I kinda-sorta into quote threading.

Unread count is completely broken with this, so I'm keeping it in
its own branch until I fix it.

Also code is a mess.
This commit is contained in:
Zixaphir 2013-04-18 01:15:50 -07:00
parent 5d446e53be
commit 643e20a2d8
12 changed files with 1197 additions and 97 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -277,6 +277,10 @@ Config =
true
'Highlight the previewed post.'
]
'Quote Threading': [
true
''
]
'Resurrect Quotes': [
true
'Link dead quotes to the archives.'

View File

@ -107,6 +107,7 @@ Main =
'Thread Excerpt': ThreadExcerpt
'Favicon': Favicon
'Unread': Unread
'Quote Threading': QuoteThreading
'Thread Stats': ThreadStats
'Thread Updater': ThreadUpdater
'Thread Watcher': ThreadWatcher

View File

@ -218,7 +218,6 @@ ThreadUpdater =
ThreadUpdater.thread.postLimit = !!OP.bumplimit
ThreadUpdater.thread.fileLimit = !!OP.imagelimit
nodes = [] # post container elements
posts = [] # post objects
index = [] # existing posts
files = [] # existing files
@ -232,7 +231,6 @@ ThreadUpdater =
# Insert new posts, not older ones.
count++
node = Build.postFromObject postObject, ThreadUpdater.thread.board
nodes.push node
posts.push new Post node, ThreadUpdater.thread, ThreadUpdater.thread.board
deletedPosts = []
@ -258,6 +256,7 @@ ThreadUpdater =
unless count
ThreadUpdater.set 'status', null, null
ThreadUpdater.outdateCount++
else
ThreadUpdater.set 'status', "+#{count}", 'new'
ThreadUpdater.outdateCount = 0
@ -271,7 +270,14 @@ ThreadUpdater =
scroll = Conf['Auto Scroll'] and ThreadUpdater.scrollBG() and
ThreadUpdater.root.getBoundingClientRect().bottom - doc.clientHeight < 25
$.add ThreadUpdater.root, nodes
for key, post of posts
continue unless posts.hasOwnProperty key
if post.cb
post.cb.call post
else
$.add ThreadUpdater.root, post.nodes.root
if scroll
if Conf['Bottom Scroll']
<% if (type === 'crx') { %>d.body<% } else { %>doc<% } %>.scrollTop = d.body.clientHeight

View File

@ -5,7 +5,7 @@ Unread =
@db = new DataBoard 'lastReadPosts', @sync
@hr = $.el 'hr',
id: 'unread-line'
@posts = []
@posts = new RandomAccessList
@postsQuotingYou = []
Thread::callbacks.push
@ -19,7 +19,7 @@ Unread =
for ID, post of @posts
posts.push post if post.isReply
Unread.lastReadPost = Unread.db.get
boardID: @board.ID
boardID: @board.ID
threadID: @ID
defaultValue: 0
Unread.addPosts posts
@ -62,13 +62,14 @@ Unread =
threadID: post.thread.ID
postID: post.ID
continue if QR.db.get data
Unread.posts.push post
Unread.posts.push ID, post
Unread.addPostQuotingYou post
if Conf['Unread Line']
# Force line on visible threads if there were no unread posts previously.
Unread.setLine newPosts.contains Unread.posts[0]
Unread.read()
Unread.update()
Unread.setLine newPosts.contains Unread.posts.first
unless Conf['Quote Threading']
Unread.read()
Unread.update()
addPostQuotingYou: (post) ->
return unless QR.db
@ -84,14 +85,22 @@ Unread =
Unread.addPosts e.detail.newPosts
readSinglePost: (post) ->
return if (i = Unread.posts.indexOf post) is -1
Unread.posts.splice i, 1
if i is 0
Unread.lastReadPost = post.ID
{ID} = post
return unless Unread.posts[ID]
Unread.posts.rm ID
unless Unread.posts.first
Unread.lastReadPost = ID
Unread.saveLastReadPost()
if (i = Unread.postsQuotingYou.indexOf post) isnt -1
Unread.postsQuotingYou.splice i, 1
Unread.update()
readRAL: (ral) ->
items = []
for post of ral
items.push post.ID > Unread.lastReadPost
for item in items
ral.rm item
readArray: (arr) ->
for post, i in arr
@ -100,15 +109,17 @@ Unread =
read: (e) ->
return if d.hidden or !Unread.posts.length
{posts} = Unread
height = doc.clientHeight
for post, i in Unread.posts
for key, post of posts
continue unless posts.hasOwnProperty key
{bottom} = post.nodes.root.getBoundingClientRect()
break if bottom > height # post is not completely read
return unless i
Unread.posts.rm post
return unless post
Unread.lastReadPost = Unread.posts[i - 1].ID
Unread.lastReadPost = post.ID
Unread.saveLastReadPost()
Unread.posts.splice 0, i
Unread.readArray Unread.postsQuotingYou
Unread.update() if e

View File

@ -0,0 +1,139 @@
###
<3 aeosynth
###
QuoteThreading =
init: ->
return unless Conf['Quote Threading'] and g.VIEW is 'thread'
@enabled = true
@controls = $.el 'span',
innerHTML: '<label><input id=threadingControl type=checkbox checked> Threading</label>'
input = $ 'input', @controls
$.on input, 'change', QuoteThreading.toggle
$.event 'AddMenuEntry',
type: 'header'
el: @controls
order: 115
$.on d, '4chanXInitFinished', @setup
Post::callbacks.push
name: 'Quote Threading'
cb: @node
setup: ->
$.off d, '4chanXInitFinished', QuoteThreading.setup
{posts} = g
Unread.read()
Unread.update()
for ID, post of posts
if post.cb
try
post.cb.call post
catch err
console.log err
return
QuoteThreading.hasRun = true
node: ->
# Random access list
#
# 'Array' implementation is very awkward - mid-object inserts, loop to find
# quoted post, loop to find inserted post(!), loop to find distance from
# threaded post to thread root
#
# Of course, implementing your own data structure can be awkward.
return if @isClone or not QuoteThreading.enabled or @thread.OP is @
{quotes, ID} = @
if QuoteThreading.hasRun
{posts} = Unread
return if !(post = posts[ID]) or post.isHidden # Filtered
else
{posts} = g
return if !(post = posts["#{g.BOARD}.#{ID}"]) or post.isHidden # Filtered
uniq = {}
if QuoteThreading.hasRun
for quote in quotes
qid = quote[2..]
continue unless qid < ID
if qid of posts
uniq[qid] = true
else
for quote in quotes
qid = quote
continue unless qid[2..] < ID
if qid of posts
uniq[qid[2..]] = true
keys = Object.keys uniq
return unless keys.length is 1
@threaded = keys[0]
@cb = QuoteThreading.nodeinsert
nodeinsert: ->
qid = @threaded
if QuoteThreading.hasRun
{posts} = Unread
qpost = posts[qid]
else
{posts} = g
unread = Unread.posts
qpost = posts["#{g.BOARD}.#{qid}"]
return if @thread.OP is qpost
qroot = qpost.nodes.root
threadContainer = qroot.nextSibling
if threadContainer?.className isnt 'threadContainer'
threadContainer = $.el 'div',
className: 'threadContainer'
$.after qroot, threadContainer
$.add threadContainer, @nodes.root
pEl = $.x 'preceding::div[contains(@class,"post reply")][1]/parent::div', @nodes.root
pid = pEl.id[2..]
if QuoteThreading.hasRun
ppost = posts[pid]
else
ppost = posts[pid]
return unless (post = unread["#{g.BOARD}.#{@id}"]) and (ppost = unread["#{g.BOARD}.#{pid}"])
posts.after ppost, @
toggle: ->
thread = $ '.thread'
replies = $$ '.thread > .replyContainer, .threadContainer > .replyContainer', thread
QuoteThreading.enabled = @checked
if @checked
nodes = (Get.postFromNode reply for reply in replies)
Unread.node.call node for node in nodes
QuoteThreading.node node for node in nodes
else
replies.sort (a, b) ->
aID = Number a.id[2..]
bID = Number b.id[2..]
aID - bID
$.add thread, replies
containers = $$ '.threadContainer', thread
$.rm container for container in containers
Unread.update true
# Keybind comes later.
# public:
# toggle: ->
# control = $.id 'threadingControl'
# control.checked = not control.checked
# QuoteThreading.toggle.call control

View File

@ -481,6 +481,12 @@ a.hide-announcement {
outline: 2px solid rgba(216, 94, 49, .7);
}
/* Quote Threading */
.threadContainer {
margin-left: 20px;
border-left: 1px solid black;
}
/* File */
.fileText:hover .fntrunc,
.fileText:not(:hover) .fnfull,

View File

@ -11,6 +11,7 @@ $$ = (selector, root=d.body) ->
$.extend = (object, properties) ->
for key, val of properties
continue unless properties.hasOwnProperty key
object[key] = val
return
@ -35,7 +36,7 @@ $.extend Array::,
args = arguments
for arg in args
@push.apply @, arg
return @
return
remove: (object) ->
if (index = @indexOf object) > -1
@ -116,8 +117,9 @@ $.extend $,
cb()
else
setTimeout $.asap, 25, test, cb
addStyle: (css) ->
addStyle: (css, id) ->
style = $.el 'style',
id: id
textContent: css
$.asap (-> d.head), ->
$.add d.head, style
@ -247,6 +249,16 @@ $.extend $,
# Round to an integer otherwise.
Math.round size
"#{size} #{['B', 'KB', 'MB', 'GB'][unit]}"
minmax: (value, min, max) ->
return (
if value < min
min
else
if value > max
max
else
value
)
syncing: {}
sync: do ->
<% if (type === 'crx') { %>

View File

@ -3,4 +3,5 @@
<%= grunt.file.read('src/lib/post.class') %>
<%= grunt.file.read('src/lib/clone.class') %>
<%= grunt.file.read('src/lib/databoard.class') %>
<%= grunt.file.read('src/lib/notification.class') %>
<%= grunt.file.read('src/lib/notification.class') %>
<%= grunt.file.read('src/lib/randomaccesslist.class') %>

View File

@ -0,0 +1,50 @@
class RandomAccessList
constructor: ->
@first = null
@last = null
@length = 0
push: (id, post) ->
{last} = @
@[id] = item = post
item.prev = last
item.next = null
@last = item
if last
last.next = item
else
@first = item
@length++
shift: ->
@rm @first.ID
after: (root, item) ->
return if item.prev is root
@rmi item
{next} = root
root.next = item
item.prev = root
item.next = next
next.prev = item
rm: (id) ->
item = @[id]
return unless item
delete @[id]
@length--
@rmi item
rmi: (item) ->
{prev, next} = item
if prev
prev.next = next
else
@first = next
if next
next.prev = prev
else
@last = prev