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 true
'Highlight the previewed post.' 'Highlight the previewed post.'
] ]
'Quote Threading': [
true
''
]
'Resurrect Quotes': [ 'Resurrect Quotes': [
true true
'Link dead quotes to the archives.' 'Link dead quotes to the archives.'

View File

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

View File

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

View File

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

View File

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

View File

@ -3,4 +3,5 @@
<%= grunt.file.read('src/lib/post.class') %> <%= grunt.file.read('src/lib/post.class') %>
<%= grunt.file.read('src/lib/clone.class') %> <%= grunt.file.read('src/lib/clone.class') %>
<%= grunt.file.read('src/lib/databoard.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