commit
d9a1b2844e
10
CHANGELOG.md
10
CHANGELOG.md
@ -1,3 +1,13 @@
|
||||
- Thread and post hiding changes:
|
||||
- The posts' menu now has a label entry listing the reasons why a post got hidden or highlighted.
|
||||
- `Thread Hiding` and `Reply Hiding` settings are merged into one: `Post Hiding`.
|
||||
- `Thread Hiding Link` and `Reply Hiding Link` settings are merged into one: `Post Hiding Link`.
|
||||
- Hiding a thread removes it from the index in `Paged` or `All threads` modes.
|
||||
<ul>
|
||||
<li> Hidden threads can be seen by clicking the `[Show]` button the the top of the index.
|
||||
<li> The `Anchor Hidden Threads` setting has been removed.
|
||||
</ul>
|
||||
|
||||
### 3.18.1 - *2014-02-20*
|
||||
|
||||
- Fix the QR breaking after a change with 4chan.
|
||||
|
||||
@ -368,7 +368,6 @@ a[href="javascript:;"] {
|
||||
:root.index-loading .navLinks,
|
||||
:root.index-loading .board,
|
||||
:root.index-loading .pagelist,
|
||||
:root:not(.catalog-mode) #hidden-toggle,
|
||||
:root:not(.catalog-mode) #index-size {
|
||||
display: none;
|
||||
}
|
||||
@ -706,14 +705,16 @@ a.hide-announcement {
|
||||
border: 2px solid rgba(255, 0, 0, .5);
|
||||
}
|
||||
|
||||
/* Thread & Reply Hiding */
|
||||
.hide-thread-button,
|
||||
.hide-reply-button {
|
||||
float: left;
|
||||
margin-right: 2px;
|
||||
/* Post Hiding */
|
||||
.hide-post-button,
|
||||
.show-post-button {
|
||||
font-size: 14px;
|
||||
line-height: 12px; /* Prevent the floating effect from affecting the thumbnail too */
|
||||
}
|
||||
.stub ~ * {
|
||||
display: none !important;
|
||||
.opContainer > .show-post-button,
|
||||
.hide-post-button {
|
||||
float: left;
|
||||
margin-right: 3px;
|
||||
}
|
||||
.stub input {
|
||||
display: inline-block;
|
||||
|
||||
@ -57,7 +57,18 @@ Filter =
|
||||
top = filter.match(/top:(yes|no)/)?[1] or 'yes'
|
||||
top = top is 'yes' # Turn it into a boolean
|
||||
|
||||
@filters[key].push @createFilter regexp, op, stub, hl, top
|
||||
@filters[key].push {
|
||||
hide: !hl
|
||||
op: op
|
||||
stub: stub
|
||||
class: hl
|
||||
top: top
|
||||
match: regexp
|
||||
test: if typeof regexp is 'string'
|
||||
Filter.stringTest # MD5 checking
|
||||
else
|
||||
Filter.regexpTest
|
||||
}
|
||||
|
||||
# Only execute filter types that contain valid filters.
|
||||
unless @filters[key].length
|
||||
@ -68,25 +79,6 @@ Filter =
|
||||
name: 'Filter'
|
||||
cb: @node
|
||||
|
||||
createFilter: (regexp, op, stub, hl, top) ->
|
||||
test =
|
||||
if typeof regexp is 'string'
|
||||
# MD5 checking
|
||||
(value) -> regexp is value
|
||||
else
|
||||
(value) -> regexp.test value
|
||||
settings =
|
||||
hide: !hl
|
||||
stub: stub
|
||||
class: hl
|
||||
top: top
|
||||
(value, isReply) ->
|
||||
if isReply and op is 'only' or !isReply and op is 'no'
|
||||
return false
|
||||
unless test value
|
||||
return false
|
||||
settings
|
||||
|
||||
node: ->
|
||||
return if @isClone
|
||||
for key of Filter.filters
|
||||
@ -94,27 +86,29 @@ Filter =
|
||||
# Continue if there's nothing to filter (no tripcode for example).
|
||||
continue if value is false
|
||||
|
||||
for filter in Filter.filters[key]
|
||||
unless result = filter value, @isReply
|
||||
for obj in Filter.filters[key]
|
||||
unless Filter.test obj, value, @isReply
|
||||
continue
|
||||
|
||||
# Hide
|
||||
if result.hide
|
||||
if @isReply
|
||||
PostHiding.hide @, result.stub
|
||||
else if g.VIEW is 'index'
|
||||
ThreadHiding.hide @thread, result.stub
|
||||
else
|
||||
continue
|
||||
if obj.hide
|
||||
continue unless @isReply or g.VIEW is 'index'
|
||||
@hide "Hidden by filtering the #{key}: #{obj.match}", obj.stub
|
||||
return
|
||||
|
||||
# Highlight
|
||||
$.addClass @nodes.root, result.class
|
||||
unless @highlights and result.class in @highlights
|
||||
(@highlights or= []).push result.class
|
||||
if !@isReply and result.top
|
||||
@thread.isOnTop = true
|
||||
@highlight "Highlighted by filtering the #{key}: #{obj.match}", obj.class, obj.top
|
||||
|
||||
stringTest: (string, value) ->
|
||||
string is value
|
||||
regexpTest: (regexp, value) ->
|
||||
regexp.test value
|
||||
test: ({test, match, op}, value, isReply) ->
|
||||
if isReply and op is 'only' or !isReply and op is 'no'
|
||||
return false
|
||||
unless test match, value
|
||||
return false
|
||||
true
|
||||
name: (post) ->
|
||||
if 'name' of post.info
|
||||
return post.info.name
|
||||
|
||||
@ -1,186 +1,184 @@
|
||||
PostHiding =
|
||||
init: ->
|
||||
return if !Conf['Reply Hiding'] and !Conf['Reply Hiding Link']
|
||||
|
||||
@db = new DataBoard 'hiddenPosts'
|
||||
@hideButton = $.el 'a',
|
||||
className: 'hide-post-button'
|
||||
innerHTML: '<i class="fa fa-minus-square-o"></i>'
|
||||
href: 'javascript:;'
|
||||
@showButton = $.el 'a',
|
||||
className: 'show-post-button'
|
||||
innerHTML: '<i class="fa fa-plus-square-o"></i>'
|
||||
href: 'javascript:;'
|
||||
|
||||
Post.callbacks.push
|
||||
name: 'Reply Hiding'
|
||||
name: 'Post Hiding'
|
||||
cb: @node
|
||||
|
||||
# XXX tmp conversion
|
||||
$.get 'hiddenThreads', null, ({hiddenThreads}) ->
|
||||
return unless hiddenThreads
|
||||
for boardID, board of hiddenThreads.boards
|
||||
for threadID, val of board
|
||||
((PostHiding.db.data.boards[boardID] or= {})[threadID] or= {})[threadID] = val
|
||||
PostHiding.db.save()
|
||||
$.delete 'hiddenThreads'
|
||||
|
||||
node: ->
|
||||
return if !@isReply or @isClone
|
||||
return if !@isReply and g.VIEW isnt 'index' or @isClone
|
||||
|
||||
if data = PostHiding.db.get {boardID: @board.ID, threadID: @thread.ID, postID: @ID}
|
||||
if data.thisPost
|
||||
PostHiding.hide @, data.makeStub, data.hideRecursively
|
||||
if data.thisPost is false
|
||||
label = "Recursively hidden for quoting No.#{@}"
|
||||
Recursive.apply 'hide', @, label, data.makeStub, true
|
||||
Recursive.add 'hide', @, label, data.makeStub, true
|
||||
else
|
||||
Recursive.apply PostHiding.hide, @, data.makeStub, true
|
||||
Recursive.add PostHiding.hide, @, data.makeStub, true
|
||||
return unless Conf['Reply Hiding']
|
||||
$.replace $('.sideArrows', @nodes.root), PostHiding.makeButton @, 'hide'
|
||||
@hide 'Manually hidden', data.makeStub, data.hideRecursively
|
||||
|
||||
menu:
|
||||
init: ->
|
||||
return if !Conf['Menu'] or !Conf['Reply Hiding Link']
|
||||
return unless Conf['Post Hiding']
|
||||
if @isReply
|
||||
a = PostHiding.makeButton true
|
||||
a.hidden = true if @isHidden
|
||||
$.replace $('.sideArrows', @nodes.root), a
|
||||
else
|
||||
$.prepend @nodes.root, PostHiding.makeButton !@isHidden
|
||||
|
||||
# Hide
|
||||
div = $.el 'div',
|
||||
className: 'hide-reply-link'
|
||||
textContent: 'Hide reply'
|
||||
|
||||
apply = $.el 'a',
|
||||
textContent: 'Apply'
|
||||
href: 'javascript:;'
|
||||
$.on apply, 'click', PostHiding.menu.hide
|
||||
|
||||
thisPost = $.el 'label',
|
||||
innerHTML: '<input type=checkbox name=thisPost checked> This post'
|
||||
replies = $.el 'label',
|
||||
innerHTML: "<input type=checkbox name=replies checked=#{Conf['Recursive Hiding']}> Hide replies"
|
||||
makeStub = $.el 'label',
|
||||
innerHTML: "<input type=checkbox name=makeStub checked=#{Conf['Stubs']}> Make stub"
|
||||
|
||||
$.event 'AddMenuEntry',
|
||||
type: 'post'
|
||||
el: div
|
||||
order: 20
|
||||
open: (post) ->
|
||||
if !post.isReply or post.isClone or post.isHidden
|
||||
return false
|
||||
PostHiding.menu.post = post
|
||||
true
|
||||
subEntries: [{el: apply}, {el: thisPost}, {el: replies}, {el: makeStub}]
|
||||
|
||||
# Show
|
||||
div = $.el 'div',
|
||||
className: 'show-reply-link'
|
||||
textContent: 'Show reply'
|
||||
|
||||
apply = $.el 'a',
|
||||
textContent: 'Apply'
|
||||
href: 'javascript:;'
|
||||
$.on apply, 'click', PostHiding.menu.show
|
||||
|
||||
thisPost = $.el 'label',
|
||||
innerHTML: '<input type=checkbox name=thisPost> This post'
|
||||
replies = $.el 'label',
|
||||
innerHTML: "<input type=checkbox name=replies> Show replies"
|
||||
|
||||
$.event 'AddMenuEntry',
|
||||
type: 'post'
|
||||
el: div
|
||||
order: 20
|
||||
open: (post) ->
|
||||
if !post.isReply or post.isClone or !post.isHidden
|
||||
return false
|
||||
unless data = PostHiding.db.get {boardID: post.board.ID, threadID: post.thread.ID, postID: post.ID}
|
||||
return false
|
||||
PostHiding.menu.post = post
|
||||
thisPost.firstChild.checked = post.isHidden
|
||||
replies.firstChild.checked = if data?.hideRecursively? then data.hideRecursively else Conf['Recursive Hiding']
|
||||
true
|
||||
subEntries: [{el: apply}, {el: thisPost}, {el: replies}]
|
||||
hide: ->
|
||||
parent = @parentNode
|
||||
thisPost = $('input[name=thisPost]', parent).checked
|
||||
replies = $('input[name=replies]', parent).checked
|
||||
makeStub = $('input[name=makeStub]', parent).checked
|
||||
{post} = PostHiding.menu
|
||||
if thisPost
|
||||
PostHiding.hide post, makeStub, replies
|
||||
else if replies
|
||||
Recursive.apply PostHiding.hide, post, makeStub, true
|
||||
Recursive.add PostHiding.hide, post, makeStub, true
|
||||
else
|
||||
return
|
||||
PostHiding.saveHiddenState post, true, thisPost, makeStub, replies
|
||||
$.event 'CloseMenu'
|
||||
show: ->
|
||||
parent = @parentNode
|
||||
thisPost = $('input[name=thisPost]', parent).checked
|
||||
replies = $('input[name=replies]', parent).checked
|
||||
{post} = PostHiding.menu
|
||||
if thisPost
|
||||
PostHiding.show post, replies
|
||||
else if replies
|
||||
Recursive.apply PostHiding.show, post, true
|
||||
Recursive.rm PostHiding.hide, post, true
|
||||
else
|
||||
return
|
||||
if data = PostHiding.db.get {boardID: post.board.ID, threadID: post.thread.ID, postID: post.ID}
|
||||
PostHiding.saveHiddenState post, !(thisPost and replies), !thisPost, data.makeStub, !replies
|
||||
$.event 'CloseMenu'
|
||||
|
||||
makeButton: (post, type) ->
|
||||
span = $.el 'span',
|
||||
textContent: "[\u00A0#{if type is 'hide' then '-' else '+'}\u00A0]"
|
||||
a = $.el 'a',
|
||||
className: "#{type}-reply-button"
|
||||
href: 'javascript:;'
|
||||
$.add a, span
|
||||
$.on a, 'click', PostHiding.toggle
|
||||
makeButton: (hide) ->
|
||||
a = (if hide then PostHiding.hideButton else PostHiding.showButton).cloneNode true
|
||||
$.on a, 'click', PostHiding.onToggleClick
|
||||
a
|
||||
|
||||
saveHiddenState: (post, isHiding, thisPost, makeStub, hideRecursively) ->
|
||||
onToggleClick: ->
|
||||
PostHiding.toggle if $.x 'ancestor::div[contains(@class,"postContainer")][1]', @
|
||||
Get.postFromNode @
|
||||
else
|
||||
Get.threadFromNode(@).OP
|
||||
toggle: (post) ->
|
||||
if post.isHidden
|
||||
post.show()
|
||||
else
|
||||
post.hide 'Manually hidden'
|
||||
PostHiding.saveHiddenState post
|
||||
return if post.isReply
|
||||
Index.updateHideLabel()
|
||||
Index.sort()
|
||||
Index.buildIndex()
|
||||
|
||||
saveHiddenState: (post, val) ->
|
||||
data =
|
||||
boardID: post.board.ID
|
||||
threadID: post.thread.ID
|
||||
postID: post.ID
|
||||
if isHiding
|
||||
data.val =
|
||||
thisPost: thisPost isnt false # undefined -> true
|
||||
makeStub: makeStub
|
||||
hideRecursively: hideRecursively
|
||||
if post.isHidden or val and !val.thisPost
|
||||
data.val = val or {}
|
||||
PostHiding.db.set data
|
||||
else
|
||||
else if PostHiding.db.get data # unhiding a filtered post f.e.
|
||||
PostHiding.db.delete data
|
||||
|
||||
toggle: ->
|
||||
post = Get.postFromNode @
|
||||
if post.isHidden
|
||||
PostHiding.show post
|
||||
else
|
||||
PostHiding.hide post
|
||||
PostHiding.saveHiddenState post, post.isHidden
|
||||
menu:
|
||||
init: ->
|
||||
return if !Conf['Menu'] or !Conf['Post Hiding Link']
|
||||
|
||||
hide: (post, makeStub=Conf['Stubs'], hideRecursively=Conf['Recursive Hiding']) ->
|
||||
return if post.isHidden
|
||||
post.isHidden = true
|
||||
# Hide
|
||||
apply =
|
||||
el: $.el 'a', textContent: 'Apply', href: 'javascript:;'
|
||||
open: (post) ->
|
||||
$.off @el, 'click', @cb if @cb
|
||||
@cb = -> PostHiding.menu.hide post
|
||||
$.on @el, 'click', @cb
|
||||
true
|
||||
thisPost =
|
||||
el: $.el 'label', innerHTML: '<input type=checkbox name=thisPost checked> This post'
|
||||
replies =
|
||||
el: $.el 'label', innerHTML: "<input type=checkbox name=replies checked=#{Conf['Recursive Hiding']}> Hide replies"
|
||||
makeStub =
|
||||
el: $.el 'label', innerHTML: "<input type=checkbox name=makeStub checked=#{Conf['Stubs']}> Make stub"
|
||||
|
||||
if hideRecursively
|
||||
Recursive.apply PostHiding.hide, post, makeStub, true
|
||||
Recursive.add PostHiding.hide, post, makeStub, true
|
||||
$.event 'AddMenuEntry',
|
||||
type: 'post'
|
||||
el: $.el 'div',
|
||||
textContent: 'Hide post'
|
||||
className: 'hide-post-link'
|
||||
order: 20
|
||||
open: (post) -> !(post.isHidden or !post.isReply or post.isClone)
|
||||
subEntries: [apply, thisPost, replies, makeStub]
|
||||
|
||||
for quotelink in Get.allQuotelinksLinkingTo post
|
||||
$.addClass quotelink, 'filtered'
|
||||
# Show
|
||||
apply =
|
||||
el: $.el 'a', textContent: 'Apply', href: 'javascript:;'
|
||||
open: (post) ->
|
||||
$.off @el, 'click', @cb if @cb
|
||||
@cb = -> PostHiding.menu.show post
|
||||
$.on @el, 'click', @cb
|
||||
true
|
||||
thisPost =
|
||||
el: $.el 'label', innerHTML: '<input type=checkbox name=thisPost> This post'
|
||||
open: (post) ->
|
||||
@el.firstChild.checked = post.isHidden
|
||||
true
|
||||
replies =
|
||||
el: $.el 'label', innerHTML: '<input type=checkbox name=replies> Unhide replies'
|
||||
open: (post) ->
|
||||
data = PostHiding.db.get {boardID: post.board.ID, threadID: post.thread.ID, postID: post.ID}
|
||||
@el.firstChild.checked = if 'hideRecursively' of data then data.hideRecursively else Conf['Recursive Hiding']
|
||||
true
|
||||
|
||||
unless makeStub
|
||||
post.nodes.root.hidden = true
|
||||
return
|
||||
$.event 'AddMenuEntry',
|
||||
type: 'post'
|
||||
el: $.el 'div',
|
||||
textContent: 'Unhide post'
|
||||
className: 'show-post-link'
|
||||
order: 20
|
||||
open: (post) ->
|
||||
if !post.isHidden or !post.isReply or post.isClone
|
||||
return false
|
||||
unless PostHiding.db.get {boardID: post.board.ID, threadID: post.thread.ID, postID: post.ID}
|
||||
return false
|
||||
true
|
||||
subEntries: [apply, thisPost, replies]
|
||||
|
||||
a = PostHiding.makeButton post, 'show'
|
||||
postInfo =
|
||||
if Conf['Anonymize']
|
||||
'Anonymous'
|
||||
return if g.VIEW isnt 'index'
|
||||
$.event 'AddMenuEntry',
|
||||
type: 'post'
|
||||
el: $.el 'a', href: 'javascript:;'
|
||||
order: 20
|
||||
open: (post) ->
|
||||
@el.textContent = if post.isHidden
|
||||
'Unhide thread'
|
||||
else
|
||||
'Hide thread'
|
||||
$.off @el, 'click', @cb if @cb
|
||||
@cb = ->
|
||||
$.event 'CloseMenu'
|
||||
PostHiding.toggle post
|
||||
$.on @el, 'click', @cb
|
||||
true
|
||||
|
||||
hide: (post) ->
|
||||
parent = @parentNode
|
||||
thisPost = $('input[name=thisPost]', parent).checked
|
||||
replies = $('input[name=replies]', parent).checked
|
||||
makeStub = $('input[name=makeStub]', parent).checked
|
||||
label = 'Manually hidden'
|
||||
if thisPost
|
||||
post.hide label, makeStub, replies
|
||||
else if replies
|
||||
Recursive.apply 'hide', post, label, makeStub, true
|
||||
Recursive.add 'hide', post, label, makeStub, true
|
||||
else
|
||||
$('.nameBlock', post.nodes.info).textContent
|
||||
$.add a, $.tn " #{postInfo}"
|
||||
post.nodes.stub = $.el 'div',
|
||||
className: 'stub'
|
||||
$.add post.nodes.stub, a
|
||||
if Conf['Menu']
|
||||
$.add post.nodes.stub, Menu.makeButton()
|
||||
$.prepend post.nodes.root, post.nodes.stub
|
||||
|
||||
show: (post, showRecursively=Conf['Recursive Hiding']) ->
|
||||
if post.nodes.stub
|
||||
$.rm post.nodes.stub
|
||||
delete post.nodes.stub
|
||||
else
|
||||
post.nodes.root.hidden = false
|
||||
post.isHidden = false
|
||||
if showRecursively
|
||||
Recursive.apply PostHiding.show, post, true
|
||||
Recursive.rm PostHiding.hide, post
|
||||
for quotelink in Get.allQuotelinksLinkingTo post
|
||||
$.rmClass quotelink, 'filtered'
|
||||
return
|
||||
return
|
||||
PostHiding.saveHiddenState post, {thisPost, hideRecursively: replies, makeStub}
|
||||
$.event 'CloseMenu'
|
||||
show: (post) ->
|
||||
parent = @parentNode
|
||||
thisPost = $('input[name=thisPost]', parent).checked
|
||||
replies = $('input[name=replies]', parent).checked
|
||||
if thisPost
|
||||
post.show replies
|
||||
else if replies
|
||||
Recursive.apply 'show', post, true
|
||||
Recursive.rm 'hide', post, true
|
||||
else
|
||||
return
|
||||
val = {thisPost: !thisPost, hideRecursively: !replies, makeStub: !!post.nodes.stub}
|
||||
PostHiding.saveHiddenState post, val
|
||||
$.event 'CloseMenu'
|
||||
|
||||
@ -7,10 +7,9 @@ Recursive =
|
||||
|
||||
node: ->
|
||||
return if @isClone
|
||||
for quote in @quotes
|
||||
if obj = Recursive.recursives[quote]
|
||||
for recursive, i in obj.recursives
|
||||
recursive @, obj.args[i]...
|
||||
for quote in @quotes when obj = Recursive.recursives[quote]
|
||||
for recursive, i in obj.recursives
|
||||
@[recursive] obj.args[i]...
|
||||
return
|
||||
|
||||
add: (recursive, post, args...) ->
|
||||
@ -22,15 +21,13 @@ Recursive =
|
||||
|
||||
rm: (recursive, post) ->
|
||||
return unless obj = Recursive.recursives[post.fullID]
|
||||
for rec, i in obj.recursives
|
||||
if rec is recursive
|
||||
obj.recursives.splice i, 1
|
||||
obj.args.splice i, 1
|
||||
for rec, i in obj.recursives when rec is recursive
|
||||
obj.recursives.splice i, 1
|
||||
obj.args.splice i, 1
|
||||
return
|
||||
|
||||
apply: (recursive, post, args...) ->
|
||||
{fullID} = post
|
||||
for ID, post of g.posts
|
||||
if fullID in post.quotes
|
||||
recursive post, args...
|
||||
for ID, post of g.posts when fullID in post.quotes
|
||||
post[recursive] args...
|
||||
return
|
||||
|
||||
@ -1,126 +0,0 @@
|
||||
ThreadHiding =
|
||||
init: ->
|
||||
return if g.VIEW isnt 'index'
|
||||
|
||||
@db = new DataBoard 'hiddenThreads'
|
||||
$.on d, 'IndexRefresh', @onIndexRefresh
|
||||
Thread.callbacks.push
|
||||
name: 'Thread Hiding'
|
||||
cb: @node
|
||||
|
||||
node: ->
|
||||
if data = ThreadHiding.db.get {boardID: @board.ID, threadID: @ID}
|
||||
ThreadHiding.hide @, data.makeStub
|
||||
return unless Conf['Thread Hiding']
|
||||
$.prepend @OP.nodes.root, ThreadHiding.makeButton @, 'hide'
|
||||
|
||||
onIndexRefresh: ->
|
||||
for root, i in Index.nodes by 2
|
||||
thread = Get.threadFromRoot root
|
||||
continue unless thread.isHidden
|
||||
unless thread.stub
|
||||
Index.nodes[i + 1].hidden = true
|
||||
else unless root.contains thread.stub
|
||||
# When we come back to a page, the stub is already there.
|
||||
ThreadHiding.makeStub thread, root
|
||||
return
|
||||
|
||||
menu:
|
||||
init: ->
|
||||
return if g.VIEW isnt 'index' or !Conf['Menu'] or !Conf['Thread Hiding Link']
|
||||
|
||||
div = $.el 'div',
|
||||
className: 'hide-thread-link'
|
||||
textContent: 'Hide thread'
|
||||
|
||||
apply = $.el 'a',
|
||||
textContent: 'Apply'
|
||||
href: 'javascript:;'
|
||||
$.on apply, 'click', ThreadHiding.menu.hide
|
||||
|
||||
makeStub = $.el 'label',
|
||||
innerHTML: "<input type=checkbox checked=#{Conf['Stubs']}> Make stub"
|
||||
|
||||
$.event 'AddMenuEntry',
|
||||
type: 'post'
|
||||
el: div
|
||||
order: 20
|
||||
open: ({thread, isReply}) ->
|
||||
if isReply or thread.isHidden or Conf['Index Mode'] is 'catalog'
|
||||
return false
|
||||
ThreadHiding.menu.thread = thread
|
||||
true
|
||||
subEntries: [el: apply; el: makeStub]
|
||||
hide: ->
|
||||
makeStub = $('input', @parentNode).checked
|
||||
{thread} = ThreadHiding.menu
|
||||
ThreadHiding.hide thread, makeStub
|
||||
ThreadHiding.saveHiddenState thread, makeStub
|
||||
$.event 'CloseMenu'
|
||||
|
||||
makeButton: (thread, type) ->
|
||||
a = $.el 'a',
|
||||
className: "#{type}-thread-button"
|
||||
innerHTML: "<span>[ #{if type is 'hide' then '-' else '+'} ]</span>"
|
||||
href: 'javascript:;'
|
||||
a.dataset.fullID = thread.fullID
|
||||
$.on a, 'click', ThreadHiding.toggle
|
||||
a
|
||||
makeStub: (thread, root) ->
|
||||
numReplies = $$('.thread > .replyContainer', root).length
|
||||
numReplies += +summary.textContent.match /\d+/ if summary = $ '.summary', root
|
||||
opInfo = if Conf['Anonymize']
|
||||
'Anonymous'
|
||||
else
|
||||
$('.nameBlock', thread.OP.nodes.info).textContent
|
||||
|
||||
a = ThreadHiding.makeButton thread, 'show'
|
||||
$.add a, $.tn " #{opInfo} (#{if numReplies is 1 then '1 reply' else "#{numReplies} replies"})"
|
||||
thread.stub = $.el 'div',
|
||||
className: 'stub'
|
||||
if Conf['Menu']
|
||||
$.add thread.stub, [a, Menu.makeButton()]
|
||||
else
|
||||
$.add thread.stub, a
|
||||
$.prepend root, thread.stub
|
||||
|
||||
saveHiddenState: (thread, makeStub) ->
|
||||
if thread.isHidden
|
||||
ThreadHiding.db.set
|
||||
boardID: thread.board.ID
|
||||
threadID: thread.ID
|
||||
val: {makeStub}
|
||||
else
|
||||
ThreadHiding.db.delete
|
||||
boardID: thread.board.ID
|
||||
threadID: thread.ID
|
||||
|
||||
toggle: (thread) ->
|
||||
unless thread instanceof Thread
|
||||
thread = g.threads[@dataset.fullID]
|
||||
if thread.isHidden
|
||||
ThreadHiding.show thread
|
||||
else
|
||||
ThreadHiding.hide thread
|
||||
ThreadHiding.saveHiddenState thread
|
||||
|
||||
hide: (thread, makeStub=Conf['Stubs']) ->
|
||||
return if thread.isHidden
|
||||
threadRoot = thread.OP.nodes.root.parentNode
|
||||
thread.isHidden = true
|
||||
Index.updateHideLabel()
|
||||
|
||||
unless makeStub
|
||||
threadRoot.hidden = threadRoot.nextElementSibling.hidden = true # <hr>
|
||||
return
|
||||
|
||||
ThreadHiding.makeStub thread, threadRoot
|
||||
|
||||
show: (thread) ->
|
||||
if thread.stub
|
||||
$.rm thread.stub
|
||||
delete thread.stub
|
||||
threadRoot = thread.OP.nodes.root.parentNode
|
||||
threadRoot.nextElementSibling.hidden =
|
||||
threadRoot.hidden = thread.isHidden = false
|
||||
Index.updateHideLabel()
|
||||
@ -244,20 +244,20 @@ Build =
|
||||
|
||||
if (OP = board.posts[data.no]) and root = OP.nodes.root.parentNode
|
||||
$.rmAll root
|
||||
$.add root, OP.nodes.root
|
||||
else
|
||||
root = $.el 'div',
|
||||
className: 'thread'
|
||||
id: "t#{data.no}"
|
||||
$.add root, Build.postFromObject data, board.ID
|
||||
|
||||
nodes = [if OP then OP.nodes.root else Build.postFromObject data, board.ID]
|
||||
if data.omitted_posts or !Conf['Show Replies'] and data.replies
|
||||
[posts, files] = if Conf['Show Replies']
|
||||
[data.omitted_posts, data.omitted_images]
|
||||
else
|
||||
[data.replies, data.images]
|
||||
nodes.push Build.summary board.ID, data.no, posts, files
|
||||
$.add root, Build.summary board.ID, data.no, posts, files
|
||||
|
||||
$.add root, nodes
|
||||
root
|
||||
catalogThread: (thread) ->
|
||||
{staticPath, gifIcon} = Build
|
||||
@ -279,7 +279,7 @@ Build =
|
||||
|
||||
root.dataset.fullID = thread.fullID
|
||||
$.addClass root, 'pinned' if thread.isPinned
|
||||
$.addClass root, thread.OP.highlights... if thread.OP.highlights
|
||||
$.addClass root, thread.OP.highlights... if thread.OP.highlights.length
|
||||
|
||||
thumb = root.firstElementChild
|
||||
if data.spoiler and !Conf['Reveal Spoilers']
|
||||
|
||||
@ -17,10 +17,9 @@ Config =
|
||||
'Filtering':
|
||||
'Anonymize': [false, 'Make everyone Anonymous.']
|
||||
'Filter': [true, 'Self-moderation placebo.']
|
||||
'Post Hiding': [true, 'Add buttons to hide threads and replies.']
|
||||
'Stubs': [true, 'Show stubs of hidden posts.']
|
||||
'Recursive Hiding': [true, 'Hide replies of hidden posts, recursively.']
|
||||
'Thread Hiding': [true, 'Add buttons to hide entire threads.']
|
||||
'Reply Hiding': [true, 'Add buttons to hide single replies.']
|
||||
'Stubs': [true, 'Show stubs of hidden threads / replies.']
|
||||
'Images':
|
||||
'Auto-GIF': [false, 'Animate GIF thumbnails (disabled on /gif/, /wsg/).']
|
||||
'Image Expansion': [true, 'Expand images inline.']
|
||||
@ -31,8 +30,7 @@ Config =
|
||||
'Menu':
|
||||
'Menu': [true, 'Add a drop-down menu to posts.']
|
||||
'Report Link': [true, 'Add a report link to the menu.']
|
||||
'Thread Hiding Link': [true, 'Add a link to hide entire threads.']
|
||||
'Reply Hiding Link': [true, 'Add a link to hide single replies.']
|
||||
'Post Hiding Link': [true, 'Add a link to hide threads and replies.']
|
||||
'Delete Link': [true, 'Add post and image deletion links to the menu.']
|
||||
<% if (type === 'crx') { %>
|
||||
'Download Link': [true, 'Add a download with original filename link to the menu.']
|
||||
@ -143,7 +141,6 @@ Config =
|
||||
'Threads per Page': 0
|
||||
'Open threads in a new tab': false
|
||||
'Show Replies': true
|
||||
'Anchor Hidden Threads': true
|
||||
'Refreshed Navigation': false
|
||||
Header:
|
||||
'Header auto-hide': false
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
class DataBoard
|
||||
@keys = ['pinnedThreads', 'hiddenThreads', 'hiddenPosts', 'lastReadPosts', 'yourPosts', 'watchedThreads']
|
||||
@keys = ['pinnedThreads', 'hiddenPosts', 'lastReadPosts', 'yourPosts', 'watchedThreads']
|
||||
|
||||
constructor: (@key, sync, dontClean) ->
|
||||
@data = Conf[key]
|
||||
|
||||
@ -3,8 +3,7 @@ Get =
|
||||
{OP} = thread
|
||||
excerpt = OP.info.subject?.trim() or
|
||||
OP.info.comment.replace(/\n+/g, ' // ') or
|
||||
Conf['Anonymize'] and 'Anonymous' or
|
||||
$('.nameBlock', OP.nodes.info).textContent.trim()
|
||||
OP.getNameBlock()
|
||||
if excerpt.length > 70
|
||||
excerpt = "#{excerpt[...67]}..."
|
||||
"/#{thread.board}/ - #{excerpt}"
|
||||
@ -233,6 +232,6 @@ Get =
|
||||
thread = g.threads["#{boardID}.#{threadID}"] or
|
||||
new Thread threadID, board
|
||||
post = new Post Build.post(o, true), thread, board, {isArchived: true}
|
||||
$('.page-num', post.nodes.info).hidden = true
|
||||
$('.page-num', post.nodes.info)?.hidden = true
|
||||
Main.callbackNodes Post, [post]
|
||||
Get.insert post, root, context
|
||||
|
||||
@ -38,15 +38,11 @@ Index =
|
||||
repliesEntry =
|
||||
el: $.el 'label',
|
||||
innerHTML: '<input type=checkbox name="Show Replies"> Show replies'
|
||||
anchorEntry =
|
||||
el: $.el 'label',
|
||||
innerHTML: '<input type=checkbox name="Anchor Hidden Threads"> Anchor hidden threads'
|
||||
title: 'Move hidden threads at the end of the index.'
|
||||
refNavEntry =
|
||||
el: $.el 'label',
|
||||
innerHTML: '<input type=checkbox name="Refreshed Navigation"> Refreshed navigation'
|
||||
title: 'Refresh index when navigating through pages.'
|
||||
for label in [targetEntry, repliesEntry, anchorEntry, refNavEntry]
|
||||
for label in [targetEntry, repliesEntry, refNavEntry]
|
||||
input = label.el.firstChild
|
||||
{name} = input
|
||||
input.checked = Conf[name]
|
||||
@ -56,15 +52,13 @@ Index =
|
||||
$.on input, 'change', @cb.target
|
||||
when 'Show Replies'
|
||||
$.on input, 'change', @cb.replies
|
||||
when 'Anchor Hidden Threads'
|
||||
$.on input, 'change', @cb.sort
|
||||
|
||||
$.event 'AddMenuEntry',
|
||||
type: 'header'
|
||||
el: $.el 'span',
|
||||
textContent: 'Index Navigation'
|
||||
order: 90
|
||||
subEntries: [threadNumEntry, targetEntry, repliesEntry, anchorEntry, refNavEntry]
|
||||
subEntries: [threadNumEntry, targetEntry, repliesEntry, refNavEntry]
|
||||
|
||||
$.addClass doc, 'index-loading'
|
||||
@update()
|
||||
@ -124,24 +118,7 @@ Index =
|
||||
$.event 'AddMenuEntry',
|
||||
type: 'post'
|
||||
el: $.el 'a', href: 'javascript:;'
|
||||
order: 5
|
||||
open: ({thread}) ->
|
||||
return false if Conf['Index Mode'] isnt 'catalog'
|
||||
@el.textContent = if thread.isHidden
|
||||
'Unhide thread'
|
||||
else
|
||||
'Hide thread'
|
||||
$.off @el, 'click', @cb if @cb
|
||||
@cb = ->
|
||||
$.event 'CloseMenu'
|
||||
Index.toggleHide thread
|
||||
$.on @el, 'click', @cb
|
||||
true
|
||||
|
||||
$.event 'AddMenuEntry',
|
||||
type: 'post'
|
||||
el: $.el 'a', href: 'javascript:;'
|
||||
order: 6
|
||||
order: 19
|
||||
open: ({thread}) ->
|
||||
return false if Conf['Index Mode'] isnt 'catalog'
|
||||
@el.textContent = if thread.isPinned
|
||||
@ -166,7 +143,7 @@ Index =
|
||||
return if e.button isnt 0
|
||||
thread = g.threads[@parentNode.dataset.fullID]
|
||||
if e.shiftKey
|
||||
Index.toggleHide thread
|
||||
PostHiding.toggle thread.OP
|
||||
else if e.altKey
|
||||
Index.togglePin thread
|
||||
else
|
||||
@ -194,15 +171,6 @@ Index =
|
||||
offsetX: 15
|
||||
offsetY: -20
|
||||
setTimeout (-> el.hidden = false if el.parentNode), .25 * $.SECOND
|
||||
toggleHide: (thread) ->
|
||||
$.rm thread.catalogView.nodes.root
|
||||
if Index.showHiddenThreads
|
||||
ThreadHiding.show thread
|
||||
return unless ThreadHiding.db.get {boardID: thread.board.ID, threadID: thread.ID}
|
||||
# Don't save when un-hiding filtered threads.
|
||||
else
|
||||
ThreadHiding.hide thread
|
||||
ThreadHiding.saveHiddenState thread
|
||||
togglePin: (thread) ->
|
||||
data =
|
||||
boardID: thread.board.ID
|
||||
@ -259,7 +227,10 @@ Index =
|
||||
else
|
||||
'Show'
|
||||
Index.sort()
|
||||
Index.buildIndex()
|
||||
if Conf['Index Mode'] is 'paged' and Index.getCurrentPage() > 0
|
||||
Index.pageNav 0
|
||||
else
|
||||
Index.buildIndex()
|
||||
mode: (e) ->
|
||||
Index.cb.toggleCatalogMode()
|
||||
Index.togglePagelist()
|
||||
@ -289,7 +260,6 @@ Index =
|
||||
Index.buildIndex() if e
|
||||
threadsNum: ->
|
||||
return unless Conf['Index Mode'] is 'paged'
|
||||
Index.buildPagelist()
|
||||
Index.buildIndex()
|
||||
target: ->
|
||||
for threadID, thread of g.BOARD.threads when thread.catalogView
|
||||
@ -367,7 +337,6 @@ Index =
|
||||
Index.currentPage = pageNum
|
||||
return if Conf['Index Mode'] isnt 'paged'
|
||||
Index.buildIndex()
|
||||
Index.setPage()
|
||||
Index.scrollToIndex()
|
||||
|
||||
getThreadsNumPerPage: ->
|
||||
@ -376,11 +345,7 @@ Index =
|
||||
else
|
||||
Index.threadsNumPerPage
|
||||
getPagesNum: ->
|
||||
numThreads = if Index.isSearching
|
||||
Index.sortedNodes.length / 2
|
||||
else
|
||||
Index.liveThreadIDs.length
|
||||
Math.ceil numThreads / Index.getThreadsNumPerPage()
|
||||
Math.ceil Index.sortedThreads.length / Index.getThreadsNumPerPage()
|
||||
getMaxPageNum: ->
|
||||
Math.max 0, Index.getPagesNum() - 1
|
||||
togglePagelist: ->
|
||||
@ -430,7 +395,7 @@ Index =
|
||||
Index.cb.toggleHiddenThreads() if Index.showHiddenThreads
|
||||
return
|
||||
Index.hideLabel.hidden = false
|
||||
$('#hidden-count', Index.navLinks).textContent = if hiddenCount is 1
|
||||
$('#hidden-count', Index.hideLabel).textContent = if hiddenCount is 1
|
||||
'1 hidden thread'
|
||||
else
|
||||
"#{hiddenCount} hidden threads"
|
||||
@ -508,12 +473,10 @@ Index =
|
||||
Index.parseThreadList pages
|
||||
Index.buildThreads()
|
||||
Index.sort()
|
||||
Index.buildPagelist()
|
||||
if pageNum?
|
||||
Index.pageNav pageNum
|
||||
return
|
||||
Index.buildIndex()
|
||||
Index.setPage()
|
||||
parseThreadList: (pages) ->
|
||||
Index.threadsNumPerPage = pages[0].threads.length
|
||||
Index.liveThreadData = pages.reduce ((arr, next) -> arr.concat next.threads), []
|
||||
@ -527,7 +490,7 @@ Index =
|
||||
posts = []
|
||||
for threadData, i in Index.liveThreadData
|
||||
threadRoot = Build.thread g.BOARD, threadData
|
||||
Index.nodes.push threadRoot, $.el 'hr'
|
||||
Index.nodes.push threadRoot
|
||||
if thread = g.BOARD.threads[threadData.no]
|
||||
thread.setPage i // Index.threadsNumPerPage
|
||||
thread.setCount 'post', threadData.replies + 1, threadData.bumplimit
|
||||
@ -548,16 +511,18 @@ Index =
|
||||
error: err
|
||||
Main.handleErrors errors if errors
|
||||
|
||||
# Add the threads and <hr>s in a container to make sure all features work.
|
||||
$.nodes Index.nodes
|
||||
Main.callbackNodes Thread, threads
|
||||
Main.callbackNodes Post, posts
|
||||
Index.updateHideLabel()
|
||||
$.event 'IndexRefresh'
|
||||
buildReplies: (threadRoots) ->
|
||||
buildHRs: (threadRoots) ->
|
||||
for i in [0...threadRoots.length] by 1
|
||||
threadRoots.splice (i * 2) + 1, 0, $.el 'hr'
|
||||
return
|
||||
buildReplies: (threads) ->
|
||||
return unless Conf['Show Replies']
|
||||
posts = []
|
||||
for threadRoot in threadRoots by 2
|
||||
thread = Get.threadFromRoot threadRoot
|
||||
for thread in threads
|
||||
i = Index.liveThreadIDs.indexOf thread.ID
|
||||
continue unless lastReplies = Index.liveThreadData[i].last_replies
|
||||
nodes = []
|
||||
@ -574,20 +539,16 @@ Index =
|
||||
errors.push
|
||||
message: "Parsing of Post No.#{data.no} failed. Post will be skipped."
|
||||
error: err
|
||||
$.add threadRoot, nodes
|
||||
$.add thread.OP.nodes.root.parentNode, nodes
|
||||
|
||||
Main.handleErrors errors if errors
|
||||
Main.callbackNodes Post, posts
|
||||
buildCatalogViews: ->
|
||||
threads = Index.sortedNodes
|
||||
.filter (n, i) -> !(i % 2)
|
||||
.map (threadRoot) -> Get.threadFromRoot threadRoot
|
||||
.filter (thread) -> !thread.isHidden isnt Index.showHiddenThreads
|
||||
catalogThreads = []
|
||||
for thread in threads when !thread.catalogView
|
||||
for thread in Index.sortedThreads when !thread.catalogView
|
||||
catalogThreads.push new CatalogThread Build.catalogThread(thread), thread
|
||||
Main.callbackNodes CatalogThread, catalogThreads
|
||||
threads.map (thread) -> thread.catalogView.nodes.root
|
||||
Index.sortedThreads.map (thread) -> thread.catalogView.nodes.root
|
||||
sizeCatalogViews: (nodes) ->
|
||||
# XXX When browsers support CSS3 attr(), use it instead.
|
||||
size = if Conf['Index Size'] is 'small' then 150 else 250
|
||||
@ -615,36 +576,43 @@ Index =
|
||||
sortedThreadIDs = [Index.liveThreadData...].sort((a, b) -> b.replies - a.replies).map (data) -> data.no
|
||||
when 'filecount'
|
||||
sortedThreadIDs = [Index.liveThreadData...].sort((a, b) -> b.images - a.images).map (data) -> data.no
|
||||
Index.sortedNodes = []
|
||||
for threadID in sortedThreadIDs
|
||||
i = Index.liveThreadIDs.indexOf(threadID) * 2
|
||||
Index.sortedNodes.push Index.nodes[i], Index.nodes[i + 1]
|
||||
Index.sortedThreads = sortedThreadIDs
|
||||
.map (threadID) -> Get.threadFromRoot Index.nodes[Index.liveThreadIDs.indexOf threadID]
|
||||
.filter (thread) -> thread.isHidden is Index.showHiddenThreads
|
||||
if Index.isSearching
|
||||
Index.sortedNodes = Index.querySearch(Index.searchInput.value) or Index.sortedNodes
|
||||
Index.sortedThreads = Index.querySearch(Index.searchInput.value) or Index.sortedThreads
|
||||
# Sticky threads
|
||||
Index.sortOnTop (thread) -> thread.isSticky
|
||||
# Highlighted threads
|
||||
Index.sortOnTop (thread) -> thread.isOnTop or thread.isPinned
|
||||
# Non-hidden threads
|
||||
Index.sortOnTop((thread) -> !thread.isHidden) if Conf['Anchor Hidden Threads']
|
||||
sortOnTop: (match) ->
|
||||
offset = 0
|
||||
for threadRoot, i in Index.sortedNodes by 2 when match Get.threadFromRoot threadRoot
|
||||
Index.sortedNodes.splice offset++ * 2, 0, Index.sortedNodes.splice(i, 2)...
|
||||
for thread, i in Index.sortedThreads when match thread
|
||||
Index.sortedThreads.splice offset++, 0, Index.sortedThreads.splice(i, 1)[0]
|
||||
return
|
||||
buildIndex: ->
|
||||
switch Conf['Index Mode']
|
||||
when 'paged'
|
||||
pageNum = Index.getCurrentPage()
|
||||
nodesPerPage = Index.getThreadsNumPerPage() * 2
|
||||
nodes = Index.sortedNodes[nodesPerPage * pageNum ... nodesPerPage * (pageNum + 1)]
|
||||
if pageNum > Index.getMaxPageNum()
|
||||
# Go to the last available page if we were past the limit.
|
||||
Index.pageNav Index.getMaxPageNum()
|
||||
return
|
||||
threadsPerPage = Index.getThreadsNumPerPage()
|
||||
threads = Index.sortedThreads[threadsPerPage * pageNum ... threadsPerPage * (pageNum + 1)]
|
||||
nodes = threads.map (thread) -> thread.OP.nodes.root.parentNode
|
||||
Index.buildReplies threads
|
||||
Index.buildHRs nodes
|
||||
Index.buildPagelist()
|
||||
Index.setPage()
|
||||
when 'catalog'
|
||||
nodes = Index.buildCatalogViews()
|
||||
Index.sizeCatalogViews nodes
|
||||
else
|
||||
nodes = Index.sortedNodes
|
||||
nodes = Index.sortedThreads.map (thread) -> thread.OP.nodes.root.parentNode
|
||||
Index.buildReplies Index.sortedThreads
|
||||
Index.buildHRs nodes
|
||||
$.rmAll Index.root
|
||||
Index.buildReplies nodes if Conf['Show Replies'] and Conf['Index Mode'] isnt 'catalog'
|
||||
$.add Index.root, nodes
|
||||
$.event 'IndexBuild', nodes
|
||||
|
||||
@ -672,23 +640,17 @@ Index =
|
||||
delete Index.searchInput.dataset.searching
|
||||
<% } %>
|
||||
Index.sort()
|
||||
# Go to the last available page if we were past the limit.
|
||||
pageNum = Math.min pageNum, Index.getMaxPageNum() if Conf['Index Mode'] is 'paged'
|
||||
Index.buildPagelist()
|
||||
if Index.currentPage is pageNum
|
||||
Index.buildIndex()
|
||||
Index.setPage()
|
||||
else
|
||||
if Conf['Index Mode'] is 'paged' and Index.currentPage isnt Math.min pageNum, Index.getMaxPageNum()
|
||||
# Go to the last available page if we were past the limit.
|
||||
Index.pageNav pageNum
|
||||
else
|
||||
Index.buildIndex()
|
||||
querySearch: (query) ->
|
||||
return unless keywords = query.toLowerCase().match /\S+/g
|
||||
Index.search keywords
|
||||
search: (keywords) ->
|
||||
found = []
|
||||
for threadRoot, i in Index.sortedNodes by 2
|
||||
if Index.searchMatch Get.threadFromRoot(threadRoot), keywords
|
||||
found.push Index.sortedNodes[i], Index.sortedNodes[i + 1]
|
||||
found
|
||||
Index.sortedThreads.filter (thread) ->
|
||||
Index.searchMatch thread, keywords
|
||||
searchMatch: (thread, keywords) ->
|
||||
{info, file} = thread.OP
|
||||
text = []
|
||||
|
||||
@ -79,19 +79,18 @@ Main =
|
||||
initFeature 'Redirect', Redirect
|
||||
initFeature 'Resurrect Quotes', Quotify
|
||||
initFeature 'Filter', Filter
|
||||
initFeature 'Thread Hiding', ThreadHiding
|
||||
initFeature 'Reply Hiding', PostHiding
|
||||
initFeature 'Post Hiding', PostHiding
|
||||
initFeature 'Recursive', Recursive
|
||||
initFeature 'Strike-through Quotes', QuoteStrikeThrough
|
||||
initFeature 'Quick Reply', QR
|
||||
initFeature 'Menu', Menu
|
||||
initFeature 'Index Generator (Menu)', Index.menu
|
||||
initFeature 'Report Link', ReportLink
|
||||
initFeature 'Thread Hiding (Menu)', ThreadHiding.menu
|
||||
initFeature 'Reply Hiding (Menu)', PostHiding.menu
|
||||
initFeature 'Post Hiding (Menu)', PostHiding.menu
|
||||
initFeature 'Delete Link', DeleteLink
|
||||
initFeature 'Filter (Menu)', Filter.menu
|
||||
initFeature 'Download Link', DownloadLink
|
||||
initFeature 'Labels list', Labels
|
||||
initFeature 'Archive Link', ArchiveLink
|
||||
initFeature 'Quote Inlining', QuoteInline
|
||||
initFeature 'Quote Previewing', QuotePreview
|
||||
|
||||
@ -52,6 +52,8 @@ class Post
|
||||
@parseQuotes()
|
||||
@parseFile that
|
||||
|
||||
@labels = []
|
||||
@highlights = []
|
||||
@isDead = false
|
||||
@isHidden = false
|
||||
|
||||
@ -106,7 +108,7 @@ class Post
|
||||
|
||||
# ES6 Set when?
|
||||
fullID = "#{match[1]}.#{match[2]}"
|
||||
@quotes.push fullID if @quotes.indexOf(fullID) is -1
|
||||
@quotes.push fullID unless fullID in @quotes
|
||||
|
||||
parseFile: (that) ->
|
||||
return unless (fileEl = $ '.file', @nodes.post) and thumb = $ 'img[data-md5]', fileEl
|
||||
@ -151,6 +153,76 @@ class Post
|
||||
$.rmClass node, 'desktop'
|
||||
return
|
||||
|
||||
getNameBlock: ->
|
||||
if Conf['Anonymize']
|
||||
'Anonymous'
|
||||
else
|
||||
$('.nameBlock', @nodes.info).textContent.trim()
|
||||
|
||||
hide: (label, makeStub=Conf['Stubs'], hideRecursively=Conf['Recursive Hiding']) ->
|
||||
@labels.push label unless label in @labels
|
||||
return if @isHidden
|
||||
@isHidden = true
|
||||
|
||||
for quotelink in Get.allQuotelinksLinkingTo @
|
||||
$.addClass quotelink, 'filtered'
|
||||
|
||||
if hideRecursively
|
||||
label = "Recursively hidden for quoting No.#{@}"
|
||||
Recursive.apply 'hide', @, label, makeStub, true
|
||||
Recursive.add 'hide', @, label, makeStub, true
|
||||
|
||||
if !@isReply
|
||||
@thread.hide()
|
||||
return
|
||||
|
||||
unless makeStub
|
||||
@nodes.root.hidden = true
|
||||
return
|
||||
|
||||
@nodes.post.hidden = true
|
||||
@nodes.post.previousElementSibling.hidden = true
|
||||
@nodes.stub = $.el 'div',
|
||||
className: 'stub'
|
||||
$.add @nodes.stub, [
|
||||
PostHiding.makeButton false
|
||||
$.tn " #{@getNameBlock()}"
|
||||
]
|
||||
$.add @nodes.stub, Menu.makeButton() if Conf['Menu']
|
||||
$.prepend @nodes.root, @nodes.stub
|
||||
show: (showRecursively=Conf['Recursive Hiding']) ->
|
||||
return if !@isHidden
|
||||
@isHidden = false
|
||||
@labels = @labels.filter (label) ->
|
||||
# This is lame.
|
||||
!/^(Manually hidden|Recursively hidden|Hidden by)/.test label
|
||||
|
||||
for quotelink in Get.allQuotelinksLinkingTo @
|
||||
$.rmClass quotelink, 'filtered'
|
||||
|
||||
if showRecursively
|
||||
Recursive.apply 'show', @, true
|
||||
Recursive.rm 'hide', @
|
||||
|
||||
if !@isReply
|
||||
@thread.show()
|
||||
return
|
||||
|
||||
unless @nodes.stub
|
||||
@nodes.root.hidden = false
|
||||
return
|
||||
@nodes.post.hidden = false
|
||||
@nodes.post.previousElementSibling.hidden = false
|
||||
$.rm @nodes.stub
|
||||
delete @nodes.stub
|
||||
highlight: (label, highlight, top) ->
|
||||
@labels.push label
|
||||
unless highlight in @highlights
|
||||
@highlights.push highlight
|
||||
$.addClass @nodes.root, highlight
|
||||
if !@isReply and top
|
||||
@thread.isOnTop = true
|
||||
|
||||
kill: (file) ->
|
||||
if file
|
||||
return if @file.isDead
|
||||
|
||||
@ -123,18 +123,16 @@ Settings =
|
||||
div = $.el 'div',
|
||||
innerHTML: "<button></button><span class=description>: Clear manually-hidden threads and posts on all boards. Reload the page to apply."
|
||||
button = $ 'button', div
|
||||
$.get {hiddenThreads: {}, hiddenPosts: {}}, ({hiddenThreads, hiddenPosts}) ->
|
||||
$.get 'hiddenPosts', {}, ({hiddenPosts}) ->
|
||||
hiddenNum = 0
|
||||
for ID, board of hiddenThreads.boards
|
||||
hiddenNum += Object.keys(board).length
|
||||
for ID, board of hiddenPosts.boards
|
||||
for ID, thread of board
|
||||
hiddenNum += Object.keys(thread).length
|
||||
button.textContent = "Hidden: #{hiddenNum}"
|
||||
$.on button, 'click', ->
|
||||
@textContent = 'Hidden: 0'
|
||||
$.delete ['hiddenThreads', 'hiddenPosts']
|
||||
$.after $('input[name="Stubs"]', section).parentNode.parentNode, div
|
||||
$.delete 'hiddenPosts'
|
||||
$.after $('input[name="Recursive Hiding"]', section).parentNode.parentNode, div
|
||||
export: ->
|
||||
# Make sure to export the most recent data.
|
||||
$.get Conf, (Conf) ->
|
||||
|
||||
@ -62,6 +62,17 @@ class Thread
|
||||
@isPinned = false
|
||||
$.rmClass @catalogView.nodes.root, 'pinned' if @catalogView
|
||||
|
||||
hide: ->
|
||||
return if @isHidden
|
||||
@isHidden = true
|
||||
if button = $ '.hide-post-button', @OP.nodes.root
|
||||
$.replace button, PostHiding.makeButton false
|
||||
show: ->
|
||||
return if !@isHidden
|
||||
@isHidden = false
|
||||
if button = $ '.show-post-button', @OP.nodes.root
|
||||
$.replace button, PostHiding.makeButton true
|
||||
|
||||
kill: ->
|
||||
@isDead = true
|
||||
|
||||
|
||||
@ -84,7 +84,9 @@ UI = do ->
|
||||
|
||||
insertEntry: (entry, parent, data) ->
|
||||
if typeof entry.open is 'function'
|
||||
return unless entry.open data
|
||||
return unless entry.open data, (subEntry) =>
|
||||
@parseEntry subEntry
|
||||
entry.subEntries.push subEntry
|
||||
$.add parent, entry.el
|
||||
|
||||
return unless entry.subEntries
|
||||
|
||||
@ -43,7 +43,7 @@ ImageExpand =
|
||||
for post in [post].concat post.clones
|
||||
{file} = post
|
||||
continue unless file and file.isImage and doc.contains post.nodes.root
|
||||
if ImageExpand.on and
|
||||
if ImageExpand.on and !post.isHidden and
|
||||
(!Conf['Expand spoilers'] and file.isSpoiler or
|
||||
Conf['Expand from here'] and Header.getTopOf(file.thumb) < 0)
|
||||
continue
|
||||
@ -76,7 +76,7 @@ ImageExpand =
|
||||
expand: (post, src) ->
|
||||
# Do not expand images of hidden/filtered replies, or already expanded pictures.
|
||||
{thumb} = post.file
|
||||
return if post.isHidden or post.file.isExpanded or $.hasClass thumb, 'expanding'
|
||||
return if post.file.isExpanded or $.hasClass thumb, 'expanding'
|
||||
$.addClass thumb, 'expanding'
|
||||
if post.file.fullImage
|
||||
# Expand already-loaded/ing picture.
|
||||
|
||||
16
src/Menu/Labels.coffee
Normal file
16
src/Menu/Labels.coffee
Normal file
@ -0,0 +1,16 @@
|
||||
Labels =
|
||||
init: ->
|
||||
return if !Conf['Menu']
|
||||
|
||||
$.event 'AddMenuEntry',
|
||||
type: 'post'
|
||||
el: $.el 'div', textContent: 'Labels'
|
||||
order: 60
|
||||
open: (post, addSubEntry) ->
|
||||
{labels} = post.origin or post
|
||||
return false unless labels.length
|
||||
@subEntries.length = 0
|
||||
for label in labels
|
||||
addSubEntry el: $.el 'div', textContent: label
|
||||
true
|
||||
subEntries: []
|
||||
@ -120,7 +120,7 @@ Keybinds =
|
||||
when Conf['Deselect reply']
|
||||
Keybinds.hl 0, threadRoot
|
||||
when Conf['Hide']
|
||||
ThreadHiding.toggle thread if ThreadHiding.db
|
||||
PostHiding.toggle thread.OP
|
||||
else
|
||||
return
|
||||
e.preventDefault()
|
||||
|
||||
@ -38,8 +38,6 @@ Nav =
|
||||
|
||||
getThread: ->
|
||||
for threadRoot in $$ '.thread'
|
||||
thread = Get.threadFromRoot threadRoot
|
||||
continue if thread.isHidden and !thread.stub
|
||||
if Header.getTopOf(threadRoot) >= -threadRoot.getBoundingClientRect().height # not scrolled past
|
||||
return threadRoot
|
||||
return $ '.board'
|
||||
|
||||
@ -92,11 +92,7 @@ Unread =
|
||||
return
|
||||
openNotification: (post) ->
|
||||
return unless Header.areNotificationsEnabled
|
||||
name = if Conf['Anonymize']
|
||||
'Anonymous'
|
||||
else
|
||||
$('.nameBlock', post.nodes.info).textContent.trim()
|
||||
notif = new Notification "#{name} replied to you",
|
||||
notif = new Notification "#{post.getNameBlock()} replied to you",
|
||||
body: post.info.comment
|
||||
icon: Favicon.logo
|
||||
notif.onclick = ->
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
QuoteStrikeThrough =
|
||||
init: ->
|
||||
return if !Conf['Reply Hiding'] and !Conf['Reply Hiding Link'] and !Conf['Filter']
|
||||
return if !Conf['Post Hiding'] and !Conf['Post Hiding Link'] and !Conf['Filter']
|
||||
|
||||
Post.callbacks.push
|
||||
name: 'Strike-through Quotes'
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user