diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index e688b10c3..dfa6a4dc6 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -1480,9 +1480,27 @@ } this.rmi(item); next = root.next; - next.prev = root.next = item; - item.prev = root; - return item.next = next; + next.prev = item; + item.next = next; + root.next = item; + return item.prev = root; + }; + + RandomAccessList.prototype.prepend = function(item) { + var ID, first; + ID = item.ID; + if (!this[ID]) { + this.push(item); + } + first = this.first; + if (this !== first) { + item.next = first; + } + if (first) { + first.prev = item; + } + this.first = item; + return delete item.prev; }; RandomAccessList.prototype.shift = function() { @@ -1533,6 +1551,19 @@ } }; + RandomAccessList.prototype.closest = function(ID) { + var item, prev; + item = this.first; + while (item) { + if (item.ID > ID) { + prev = item.prev.prev; + break; + } + item = item.next; + } + return (prev ? prev.ID : -1); + }; + return RandomAccessList; })(); @@ -4928,7 +4959,9 @@ el: this.controls, order: 98 }); - $.on(d, '4chanXInitFinished', this.setup); + if (!Conf['Unread Count']) { + $.on(d, '4chanXInitFinished', QuoteThreading.setup); + } return Post.callbacks.push({ name: 'Quote Threading', cb: this.node @@ -4977,17 +5010,18 @@ return this.cb = QuoteThreading.nodeinsert; }, nodeinsert: function() { - var bottom, height, post, root, threadContainer, top, _ref; + var ID, bottom, height, post, posts, root, threadContainer, top, _ref; post = g.posts[this.threaded]; - delete this.threaded; - delete this.cb; + posts = Unread.posts; + this.threaded; + this.cb; if (this.thread.OP === post) { return false; } if (QuoteThreading.hasRun) { height = doc.clientHeight; _ref = post.nodes.root.getBoundingClientRect(), bottom = _ref.bottom, top = _ref.top; - if (!(Unread.posts[post.ID] || ((bottom < height) && (top > 0)))) { + if (!(posts[post.ID] || ((bottom < height) && (top > 0)))) { return false; } } @@ -5002,12 +5036,24 @@ threadContainer = root.nextSibling; } $.add(threadContainer, this.nodes.root); - Unread.posts.after(post.ID, this); + if (!posts[this.ID]) { + posts.push(this); + } + if (posts[post.ID]) { + posts.after(post, this); + } else { + if ((ID = posts.closest(ID)) !== -1) { + posts.after(posts[ID], this); + } else { + posts.prepend(this); + } + } return true; }, toggle: function() { var container, containers, node, post, replies, reply, thread, _i, _j, _k, _len, _len1, _len2, _ref; - Unread.replies = new RandomAccessList; + Unread.posts = new RandomAccessList; + Unread.ready(); thread = $('.thread'); replies = $$('.thread > .replyContainer, .threadContainer > .replyContainer', thread); QuoteThreading.enabled = this.checked; @@ -5015,9 +5061,14 @@ QuoteThreading.hasRun = false; for (_i = 0, _len = replies.length; _i < _len; _i++) { reply = replies[_i]; - QuoteThreading.node.call(node = Get.postFromRoot(reply)); + node = Get.postFromRoot(reply); if (node.cb) { - node.cb(); + node.cb.call(node); + } else { + QuoteThreading.node.call(node); + if (node.cb) { + node.cb.call(node); + } } } QuoteThreading.hasRun = true; @@ -5045,7 +5096,8 @@ kb: function() { var control; control = $.id('threadingControl'); - return control.click(); + control.checked = !control.checked; + return QuoteThreading.toggle.call(control); } }; @@ -9477,6 +9529,22 @@ }); this.posts = new RandomAccessList; this.postsQuotingYou = []; + this.qr = QR.db ? function(_arg) { + var ID, board, data, thread; + board = _arg.board, thread = _arg.thread, ID = _arg.ID; + data = { + boardID: board.ID, + threadID: thread.ID, + postID: ID + }; + if (QR.db.get(data)) { + return true; + } else { + return false; + } + } : function() { + return false; + }; return Thread.callbacks.push({ name: 'Unread', cb: this.node @@ -9509,6 +9577,9 @@ } } Unread.addPosts(posts); + if (Conf['Quote Threading']) { + QuoteThreading.setup(); + } if (Conf['Scroll to Last Read Post']) { return Unread.scroll(); } @@ -9562,30 +9633,16 @@ return Unread.update(); }, addPosts: function(posts) { - var ID, db, post, _i, _len, _ref; - db = QR.db ? function(_arg) { - var ID, board, data, thread; - board = _arg.board, thread = _arg.thread, ID = _arg.ID; - data = { - boardID: board.ID, - threadID: thread.ID, - postID: ID - }; - if (QR.db.get(data)) { - return true; - } else { - return false; - } - } : function() { - return false; - }; + var ID, post, _i, _len, _ref; for (_i = 0, _len = posts.length; _i < _len; _i++) { post = posts[_i]; ID = post.ID; - if (ID <= Unread.lastReadPost || post.isHidden || db(post)) { + if (ID <= Unread.lastReadPost || post.isHidden || Unread.qr(post)) { continue; } - Unread.posts.push(post); + if (!(post.prev || post.next)) { + Unread.posts.push(post); + } Unread.addPostQuotingYou(post); } if (Conf['Unread Line']) { @@ -9663,25 +9720,24 @@ } return arr.splice(0, i); }, - read: $.debounce(50, function(e) { + read: function(e) { var ID, height, post, posts; if (d.hidden || !Unread.posts.length) { return; } height = doc.clientHeight; posts = Unread.posts; - post = posts.first; - while (post) { - if (Header.getBottomOf(post.nodes.root) > -1) { - ID = post.ID; - if (Conf['Mark Quotes of You']) { - if (post.info.yours) { - QuoteYou.lastRead = post.nodes.root; - } - } - post = post.next; - posts.rm(ID); - } else { + while (post = posts.first) { + if (!(Header.getBottomOf(post.nodes.root) > -1)) { + break; + } + ID = post.ID; + if (Conf['Mark Quotes of You'] && post.info.yours) { + QuoteYou.lastRead = post.nodes.root; + } + posts.rm(ID); + if (post === posts.first) { + c.log(posts); break; } } @@ -9696,7 +9752,7 @@ if (e) { return Unread.update(); } - }), + }, saveLastReadPost: $.debounce(2 * $.SECOND, function() { if (Unread.thread.isDead) { return; diff --git a/builds/crx/script.js b/builds/crx/script.js index f2f569043..e314fce6a 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -1486,9 +1486,27 @@ } this.rmi(item); next = root.next; - next.prev = root.next = item; - item.prev = root; - return item.next = next; + next.prev = item; + item.next = next; + root.next = item; + return item.prev = root; + }; + + RandomAccessList.prototype.prepend = function(item) { + var ID, first; + ID = item.ID; + if (!this[ID]) { + this.push(item); + } + first = this.first; + if (this !== first) { + item.next = first; + } + if (first) { + first.prev = item; + } + this.first = item; + return delete item.prev; }; RandomAccessList.prototype.shift = function() { @@ -1539,6 +1557,19 @@ } }; + RandomAccessList.prototype.closest = function(ID) { + var item, prev; + item = this.first; + while (item) { + if (item.ID > ID) { + prev = item.prev.prev; + break; + } + item = item.next; + } + return (prev ? prev.ID : -1); + }; + return RandomAccessList; })(); @@ -4931,7 +4962,9 @@ el: this.controls, order: 98 }); - $.on(d, '4chanXInitFinished', this.setup); + if (!Conf['Unread Count']) { + $.on(d, '4chanXInitFinished', QuoteThreading.setup); + } return Post.callbacks.push({ name: 'Quote Threading', cb: this.node @@ -4980,17 +5013,18 @@ return this.cb = QuoteThreading.nodeinsert; }, nodeinsert: function() { - var bottom, height, post, root, threadContainer, top, _ref; + var ID, bottom, height, post, posts, root, threadContainer, top, _ref; post = g.posts[this.threaded]; - delete this.threaded; - delete this.cb; + posts = Unread.posts; + this.threaded; + this.cb; if (this.thread.OP === post) { return false; } if (QuoteThreading.hasRun) { height = doc.clientHeight; _ref = post.nodes.root.getBoundingClientRect(), bottom = _ref.bottom, top = _ref.top; - if (!(Unread.posts[post.ID] || ((bottom < height) && (top > 0)))) { + if (!(posts[post.ID] || ((bottom < height) && (top > 0)))) { return false; } } @@ -5005,12 +5039,24 @@ threadContainer = root.nextSibling; } $.add(threadContainer, this.nodes.root); - Unread.posts.after(post.ID, this); + if (!posts[this.ID]) { + posts.push(this); + } + if (posts[post.ID]) { + posts.after(post, this); + } else { + if ((ID = posts.closest(ID)) !== -1) { + posts.after(posts[ID], this); + } else { + posts.prepend(this); + } + } return true; }, toggle: function() { var container, containers, node, post, replies, reply, thread, _i, _j, _k, _len, _len1, _len2, _ref; - Unread.replies = new RandomAccessList; + Unread.posts = new RandomAccessList; + Unread.ready(); thread = $('.thread'); replies = $$('.thread > .replyContainer, .threadContainer > .replyContainer', thread); QuoteThreading.enabled = this.checked; @@ -5018,9 +5064,14 @@ QuoteThreading.hasRun = false; for (_i = 0, _len = replies.length; _i < _len; _i++) { reply = replies[_i]; - QuoteThreading.node.call(node = Get.postFromRoot(reply)); + node = Get.postFromRoot(reply); if (node.cb) { - node.cb(); + node.cb.call(node); + } else { + QuoteThreading.node.call(node); + if (node.cb) { + node.cb.call(node); + } } } QuoteThreading.hasRun = true; @@ -5048,7 +5099,8 @@ kb: function() { var control; control = $.id('threadingControl'); - return control.click(); + control.checked = !control.checked; + return QuoteThreading.toggle.call(control); } }; @@ -9460,6 +9512,22 @@ }); this.posts = new RandomAccessList; this.postsQuotingYou = []; + this.qr = QR.db ? function(_arg) { + var ID, board, data, thread; + board = _arg.board, thread = _arg.thread, ID = _arg.ID; + data = { + boardID: board.ID, + threadID: thread.ID, + postID: ID + }; + if (QR.db.get(data)) { + return true; + } else { + return false; + } + } : function() { + return false; + }; return Thread.callbacks.push({ name: 'Unread', cb: this.node @@ -9492,6 +9560,9 @@ } } Unread.addPosts(posts); + if (Conf['Quote Threading']) { + QuoteThreading.setup(); + } if (Conf['Scroll to Last Read Post']) { return Unread.scroll(); } @@ -9545,30 +9616,16 @@ return Unread.update(); }, addPosts: function(posts) { - var ID, db, post, _i, _len, _ref; - db = QR.db ? function(_arg) { - var ID, board, data, thread; - board = _arg.board, thread = _arg.thread, ID = _arg.ID; - data = { - boardID: board.ID, - threadID: thread.ID, - postID: ID - }; - if (QR.db.get(data)) { - return true; - } else { - return false; - } - } : function() { - return false; - }; + var ID, post, _i, _len, _ref; for (_i = 0, _len = posts.length; _i < _len; _i++) { post = posts[_i]; ID = post.ID; - if (ID <= Unread.lastReadPost || post.isHidden || db(post)) { + if (ID <= Unread.lastReadPost || post.isHidden || Unread.qr(post)) { continue; } - Unread.posts.push(post); + if (!(post.prev || post.next)) { + Unread.posts.push(post); + } Unread.addPostQuotingYou(post); } if (Conf['Unread Line']) { @@ -9646,25 +9703,24 @@ } return arr.splice(0, i); }, - read: $.debounce(50, function(e) { + read: function(e) { var ID, height, post, posts; if (d.hidden || !Unread.posts.length) { return; } height = doc.clientHeight; posts = Unread.posts; - post = posts.first; - while (post) { - if (Header.getBottomOf(post.nodes.root) > -1) { - ID = post.ID; - if (Conf['Mark Quotes of You']) { - if (post.info.yours) { - QuoteYou.lastRead = post.nodes.root; - } - } - post = post.next; - posts.rm(ID); - } else { + while (post = posts.first) { + if (!(Header.getBottomOf(post.nodes.root) > -1)) { + break; + } + ID = post.ID; + if (Conf['Mark Quotes of You'] && post.info.yours) { + QuoteYou.lastRead = post.nodes.root; + } + posts.rm(ID); + if (post === posts.first) { + c.log(posts); break; } } @@ -9679,7 +9735,7 @@ if (e) { return Unread.update(); } - }), + }, saveLastReadPost: $.debounce(2 * $.SECOND, function() { if (Unread.thread.isDead) { return; diff --git a/src/General/Main.coffee b/src/General/Main.coffee index 14b4c8dd9..61b083ecf 100755 --- a/src/General/Main.coffee +++ b/src/General/Main.coffee @@ -1,6 +1,5 @@ Main = init: -> - # flatten Config into Conf # and get saved or default values flatten = (parent, obj) -> diff --git a/src/General/lib/randomaccesslist.class b/src/General/lib/randomaccesslist.class index aa736e33f..40598be24 100644 --- a/src/General/lib/randomaccesslist.class +++ b/src/General/lib/randomaccesslist.class @@ -19,9 +19,19 @@ class RandomAccessList @rmi item {next} = root - next.prev = root.next = item - item.prev = root + next.prev = item item.next = next + root.next = item + item.prev = root + + prepend: (item) -> + {ID} = item + @push item unless @[ID] + {first} = @ + item.next = first unless @ is first + first.prev = item if first + @first = item + delete item.prev shift: -> @rm @first.ID @@ -53,4 +63,13 @@ class RandomAccessList if next next.prev = prev else - @last = prev \ No newline at end of file + @last = prev + + closest: (ID) -> + item = @first + while item + if item.ID > ID + {prev} = item.prev + break + item = item.next + return (if prev then prev.ID else -1) \ No newline at end of file diff --git a/src/Monitoring/Unread.coffee b/src/Monitoring/Unread.coffee index c6fcf81f9..90e78b9d4 100755 --- a/src/Monitoring/Unread.coffee +++ b/src/Monitoring/Unread.coffee @@ -7,6 +7,16 @@ Unread = id: 'unread-line' @posts = new RandomAccessList @postsQuotingYou = [] + + @qr = if QR.db + ({board, thread, ID}) -> + data = + boardID: board.ID + threadID: thread.ID + postID: ID + return if QR.db.get data then true else false + else -> + return false Thread.callbacks.push name: 'Unread' @@ -30,6 +40,7 @@ Unread = for ID, post of Unread.thread.posts posts.push post if post.isReply Unread.addPosts posts + QuoteThreading.setup() if Conf['Quote Threading'] Unread.scroll() if Conf['Scroll to Last Read Post'] scroll: -> @@ -68,20 +79,10 @@ Unread = Unread.update() addPosts: (posts) -> - db = if QR.db - ({board, thread, ID}) -> - data = - boardID: board.ID - threadID: thread.ID - postID: ID - return if QR.db.get data then true else false - else -> - return false - for post in posts {ID} = post - continue if ID <= Unread.lastReadPost or post.isHidden or db post - Unread.posts.push post + continue if ID <= Unread.lastReadPost or post.isHidden or Unread.qr post + Unread.posts.push post unless post.prev or post.next Unread.addPostQuotingYou post if Conf['Unread Line'] # Force line on visible threads if there were no unread posts previously. @@ -135,22 +136,21 @@ Unread = break if post.ID > Unread.lastReadPost arr.splice 0, i - read: $.debounce 50, (e) -> + read: (e) -> return if d.hidden or !Unread.posts.length height = doc.clientHeight + {posts} = Unread + while post = posts.first + break unless Header.getBottomOf(post.nodes.root) > -1 # post is not completely read - post = posts.first - - while post - if Header.getBottomOf(post.nodes.root) > -1 # post is not completely read - {ID} = post - if Conf['Mark Quotes of You'] - if post.info.yours - QuoteYou.lastRead = post.nodes.root - post = post.next - posts.rm ID - else + {ID} = post + if Conf['Mark Quotes of You'] and post.info.yours + QuoteYou.lastRead = post.nodes.root + posts.rm ID + + if post is posts.first + c.log posts break return unless ID diff --git a/src/Quotelinks/QuoteThreading.coffee b/src/Quotelinks/QuoteThreading.coffee index 5da0476e5..f4930b6be 100755 --- a/src/Quotelinks/QuoteThreading.coffee +++ b/src/Quotelinks/QuoteThreading.coffee @@ -18,7 +18,7 @@ QuoteThreading = el: @controls order: 98 - $.on d, '4chanXInitFinished', @setup + $.on d, '4chanXInitFinished', QuoteThreading.setup unless Conf['Unread Count'] Post.callbacks.push name: 'Quote Threading' @@ -28,9 +28,7 @@ QuoteThreading = $.off d, '4chanXInitFinished', QuoteThreading.setup {posts} = g - for ID, post of posts - if post.cb - post.cb.call post + post.cb.call post for ID, post of posts when post.cb QuoteThreading.hasRun = true @@ -58,10 +56,11 @@ QuoteThreading = @cb = QuoteThreading.nodeinsert nodeinsert: -> - post = g.posts[@threaded] + post = g.posts[@threaded] + {posts} = Unread - delete @threaded - delete @cb + @threaded + @cb return false if @thread.OP is post @@ -70,7 +69,7 @@ QuoteThreading = {bottom, top} = post.nodes.root.getBoundingClientRect() # Post is unread or is fully visible. - return false unless Unread.posts[post.ID] or ((bottom < height) and (top > 0)) + return false unless posts[post.ID] or ((bottom < height) and (top > 0)) root = post.nodes.root unless $.hasClass root, 'threadOP' @@ -83,20 +82,34 @@ QuoteThreading = $.add threadContainer, @nodes.root - Unread.posts.after post.ID, @ + posts.push @ unless posts[@ID] + if posts[post.ID] + posts.after post, @ + else + if (ID = posts.closest ID) isnt -1 + posts.after posts[ID], @ + else + posts.prepend @ + return true toggle: -> - Unread.replies = new RandomAccessList + Unread.posts = new RandomAccessList + Unread.ready() + thread = $ '.thread' replies = $$ '.thread > .replyContainer, .threadContainer > .replyContainer', thread QuoteThreading.enabled = @checked if @checked QuoteThreading.hasRun = false for reply in replies - QuoteThreading.node.call node = Get.postFromRoot reply - node.cb() if node.cb + node = Get.postFromRoot reply + if node.cb + node.cb.call node + else + QuoteThreading.node.call node + node.cb.call node if node.cb QuoteThreading.hasRun = true else replies.sort (a, b) -> @@ -111,4 +124,5 @@ QuoteThreading = kb: -> control = $.id 'threadingControl' - control.click() \ No newline at end of file + control.checked = not control.checked + QuoteThreading.toggle.call control \ No newline at end of file