From 6095a979216ba3ba15b79c630b8f758903e0e471 Mon Sep 17 00:00:00 2001 From: Zixaphir Date: Thu, 16 Jan 2014 11:07:19 -0700 Subject: [PATCH] More of a proof of concept than anything. May revert? --- builds/4chan-X.user.js | 121 ++++++++++++++++++------- builds/crx/script.js | 121 ++++++++++++++++++------- src/General/Index.coffee | 60 +++++++++--- src/General/lib/randomaccesslist.class | 24 ++++- src/Monitoring/Unread.coffee | 8 +- 5 files changed, 241 insertions(+), 93 deletions(-) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index efdaf8e69..05e4a11be 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -1523,19 +1523,40 @@ this.length = 0; } - RandomAccessList.prototype.push = function(item) { - var ID, last; - ID = item.ID; + RandomAccessList.prototype.push = function(data) { + var ID, item, last; + ID = data.ID; + ID || (ID = data.id); if (this[ID]) { return; } last = this.last; + this[ID] = item = { + prev: last, + next: null, + data: data, + ID: ID + }; item.prev = last; - this[ID] = item; this.last = last ? last.next = item : this.first = item; return this.length++; }; + RandomAccessList.prototype.before = function(root, item) { + var prev; + if (item.next === root) { + return; + } + this.rmi(item); + prev = root.prev; + root.prev = item; + item.next = root; + item.prev = prev; + if (prev) { + return prev.next = item; + } + }; + RandomAccessList.prototype.after = function(root, item) { var next; if (item.prev === root) { @@ -2360,7 +2381,7 @@ }); }, scroll: $.debounce(100, function() { - var nodes, nodesPerPage, offset, pageNum; + var nodes, pageNum; if (Index.req || Conf['Index Mode'] !== 'infinite' || (doc.scrollTop <= doc.scrollHeight - (300 + window.innerHeight)) || g.VIEW === 'thread') { return; } @@ -2371,9 +2392,7 @@ if (pageNum >= Index.pagesNum) { return Index.endNotice(); } - nodesPerPage = Index.threadsNumPerPage; - offset = nodesPerPage * pageNum; - nodes = Index.sortedNodes.slice(offset, offset + nodesPerPage); + nodes = Index.buildSinglePage(pageNum); if (Conf['Show Replies']) { Index.buildReplies(nodes); } @@ -2719,7 +2738,7 @@ return Main.callbackNodes(Post, posts); }, sort: function() { - var cnd, fn, i, item, items, nodes, sortedThreadIDs, threadID, _i, _len; + var cnd, fn, i, item, items, node, nodes, sortedThreadIDs, threadID, _i, _j, _len, _len1; sortedThreadIDs = { lastreply: __slice.call(Index.liveThreadData).sort(function(a, b) { if ('last_replies' in a) { @@ -2747,14 +2766,18 @@ return data.no; }) }[Conf['Index Sort']]; - Index.sortedNodes = []; + Index.sortedNodes = new RandomAccessList; nodes = Index.nodes; for (_i = 0, _len = sortedThreadIDs.length; _i < _len; _i++) { threadID = sortedThreadIDs[_i]; Index.sortedNodes.push(nodes[Index.liveThreadIDs.indexOf(threadID)]); } if (Index.isSearching && (nodes = Index.querySearch(Index.searchInput.value))) { - Index.sortedNodes = nodes; + Index.sortedNodes = new RandomAccessList; + for (_j = 0, _len1 = nodes.length; _j < _len1; _j++) { + node = nodes[_j]; + Index.sortedNodes.push(node); + } } items = [ { @@ -2783,25 +2806,36 @@ } }, sortOnTop: function(match) { - var i, offset, threadRoot, _i, _len, _ref; + var j, offset, sortedNodes, target, threadRoot; offset = 0; - _ref = Index.sortedNodes; - for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { - threadRoot = _ref[i]; - if (match(Get.threadFromRoot(threadRoot))) { - Index.sortedNodes.splice(offset++, 0, Index.sortedNodes.splice(i, 1)[0]); + sortedNodes = Index.sortedNodes; + threadRoot = Index.sortedNodes.first; + while (threadRoot) { + if (match(Get.threadFromRoot(threadRoot.data))) { + target = Index.sortedNodes.first; + j = 0; + while (j++ < offset) { + target = target.next; + } + if (threadRoot !== target) { + offset++; + sortedNodes.before(target, threadRoot); + } } + threadRoot = threadRoot.next; } }, buildIndex: function() { - var nodes, nodesPerPage, offset, pageNum; + var nodes, target; if (Conf['Index Mode'] !== 'all pages') { - pageNum = Index.getCurrentPage(); - nodesPerPage = Index.threadsNumPerPage; - offset = nodesPerPage * pageNum; - nodes = Index.sortedNodes.slice(offset, offset + nodesPerPage); + nodes = Index.buildSinglePage(Index.getCurrentPage()); } else { - nodes = Index.sortedNodes; + nodes = []; + target = Index.sortedNodes.first; + while (target) { + nodes.push(target.data); + target = target.next; + } } $.rmAll(Index.root); $.rmAll(Header.hover); @@ -2810,6 +2844,22 @@ } return Index.buildStructure(nodes); }, + buildSinglePage: function(pageNum) { + var end, i, nodes, nodesPerPage, offset, target; + nodes = []; + nodesPerPage = Index.threadsNumPerPage; + offset = nodesPerPage * pageNum; + end = offset + nodesPerPage; + target = Index.sortedNodes.first; + i = 0; + while (i <= end) { + if (offset <= i++) { + nodes.push(target.data); + } + target = target.next; + } + return nodes; + }, buildStructure: function(nodes) { var hr, i, node, result, _i, _len, _ref; result = $.frag(); @@ -2866,16 +2916,17 @@ return Index.search(keywords); }, search: function(keywords) { - var threadRoot, _i, _len, _ref, _results; - _ref = Index.sortedNodes; - _results = []; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - threadRoot = _ref[_i]; - if (Index.searchMatch(Get.threadFromRoot(threadRoot), keywords)) { - _results.push(threadRoot); + var data, found, target; + found = []; + target = Index.sortedNodes.first; + while (target) { + data = target.data; + if (Index.searchMatch(Get.threadFromRoot(data), keywords)) { + found.push(data); } + target = target.next; } - return _results; + return found; }, searchMatch: function(thread, keywords) { var file, info, key, keyword, text, _i, _j, _len, _len1, _ref, _ref1; @@ -9904,10 +9955,10 @@ if (!(post.prev || post.next)) { Unread.posts.push(post); } - Unread.addPostQuotingYou(post); + Unread.addPostQuotingYou(post.data); } if (Conf['Unread Line']) { - Unread.setLine((_ref = Unread.posts.first, __indexOf.call(posts, _ref) >= 0)); + Unread.setLine((_ref = Unread.posts.first.data, __indexOf.call(posts, _ref) >= 0)); } Unread.read(); return Unread.update(); @@ -9989,13 +10040,13 @@ height = doc.clientHeight; posts = Unread.posts; while (post = posts.first) { - if (!(Header.getBottomOf(post.nodes.root) > -1)) { + if (!(Header.getBottomOf(post.data.nodes.root) > -1)) { break; } ID = post.ID; posts.rm(ID); if (Conf['Mark Quotes of You'] && post.info.yours) { - QuoteYou.lastRead = post.nodes.root; + QuoteYou.lastRead = post.data.nodes.root; } } if (!ID) { diff --git a/builds/crx/script.js b/builds/crx/script.js index 9fb7260a1..c223d0013 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -1529,19 +1529,40 @@ this.length = 0; } - RandomAccessList.prototype.push = function(item) { - var ID, last; - ID = item.ID; + RandomAccessList.prototype.push = function(data) { + var ID, item, last; + ID = data.ID; + ID || (ID = data.id); if (this[ID]) { return; } last = this.last; + this[ID] = item = { + prev: last, + next: null, + data: data, + ID: ID + }; item.prev = last; - this[ID] = item; this.last = last ? last.next = item : this.first = item; return this.length++; }; + RandomAccessList.prototype.before = function(root, item) { + var prev; + if (item.next === root) { + return; + } + this.rmi(item); + prev = root.prev; + root.prev = item; + item.next = root; + item.prev = prev; + if (prev) { + return prev.next = item; + } + }; + RandomAccessList.prototype.after = function(root, item) { var next; if (item.prev === root) { @@ -2370,7 +2391,7 @@ }); }, scroll: $.debounce(100, function() { - var nodes, nodesPerPage, offset, pageNum; + var nodes, pageNum; if (Index.req || Conf['Index Mode'] !== 'infinite' || (doc.scrollTop <= doc.scrollHeight - (300 + window.innerHeight)) || g.VIEW === 'thread') { return; } @@ -2381,9 +2402,7 @@ if (pageNum >= Index.pagesNum) { return Index.endNotice(); } - nodesPerPage = Index.threadsNumPerPage; - offset = nodesPerPage * pageNum; - nodes = Index.sortedNodes.slice(offset, offset + nodesPerPage); + nodes = Index.buildSinglePage(pageNum); if (Conf['Show Replies']) { Index.buildReplies(nodes); } @@ -2729,7 +2748,7 @@ return Main.callbackNodes(Post, posts); }, sort: function() { - var cnd, fn, i, item, items, nodes, sortedThreadIDs, threadID, _i, _len; + var cnd, fn, i, item, items, node, nodes, sortedThreadIDs, threadID, _i, _j, _len, _len1; sortedThreadIDs = { lastreply: __slice.call(Index.liveThreadData).sort(function(a, b) { if ('last_replies' in a) { @@ -2757,14 +2776,18 @@ return data.no; }) }[Conf['Index Sort']]; - Index.sortedNodes = []; + Index.sortedNodes = new RandomAccessList; nodes = Index.nodes; for (_i = 0, _len = sortedThreadIDs.length; _i < _len; _i++) { threadID = sortedThreadIDs[_i]; Index.sortedNodes.push(nodes[Index.liveThreadIDs.indexOf(threadID)]); } if (Index.isSearching && (nodes = Index.querySearch(Index.searchInput.value))) { - Index.sortedNodes = nodes; + Index.sortedNodes = new RandomAccessList; + for (_j = 0, _len1 = nodes.length; _j < _len1; _j++) { + node = nodes[_j]; + Index.sortedNodes.push(node); + } } items = [ { @@ -2793,25 +2816,36 @@ } }, sortOnTop: function(match) { - var i, offset, threadRoot, _i, _len, _ref; + var j, offset, sortedNodes, target, threadRoot; offset = 0; - _ref = Index.sortedNodes; - for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { - threadRoot = _ref[i]; - if (match(Get.threadFromRoot(threadRoot))) { - Index.sortedNodes.splice(offset++, 0, Index.sortedNodes.splice(i, 1)[0]); + sortedNodes = Index.sortedNodes; + threadRoot = Index.sortedNodes.first; + while (threadRoot) { + if (match(Get.threadFromRoot(threadRoot.data))) { + target = Index.sortedNodes.first; + j = 0; + while (j++ < offset) { + target = target.next; + } + if (threadRoot !== target) { + offset++; + sortedNodes.before(target, threadRoot); + } } + threadRoot = threadRoot.next; } }, buildIndex: function() { - var nodes, nodesPerPage, offset, pageNum; + var nodes, target; if (Conf['Index Mode'] !== 'all pages') { - pageNum = Index.getCurrentPage(); - nodesPerPage = Index.threadsNumPerPage; - offset = nodesPerPage * pageNum; - nodes = Index.sortedNodes.slice(offset, offset + nodesPerPage); + nodes = Index.buildSinglePage(Index.getCurrentPage()); } else { - nodes = Index.sortedNodes; + nodes = []; + target = Index.sortedNodes.first; + while (target) { + nodes.push(target.data); + target = target.next; + } } $.rmAll(Index.root); $.rmAll(Header.hover); @@ -2820,6 +2854,22 @@ } return Index.buildStructure(nodes); }, + buildSinglePage: function(pageNum) { + var end, i, nodes, nodesPerPage, offset, target; + nodes = []; + nodesPerPage = Index.threadsNumPerPage; + offset = nodesPerPage * pageNum; + end = offset + nodesPerPage; + target = Index.sortedNodes.first; + i = 0; + while (i <= end) { + if (offset <= i++) { + nodes.push(target.data); + } + target = target.next; + } + return nodes; + }, buildStructure: function(nodes) { var hr, i, node, result, _i, _len, _ref; result = $.frag(); @@ -2876,16 +2926,17 @@ return Index.search(keywords); }, search: function(keywords) { - var threadRoot, _i, _len, _ref, _results; - _ref = Index.sortedNodes; - _results = []; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - threadRoot = _ref[_i]; - if (Index.searchMatch(Get.threadFromRoot(threadRoot), keywords)) { - _results.push(threadRoot); + var data, found, target; + found = []; + target = Index.sortedNodes.first; + while (target) { + data = target.data; + if (Index.searchMatch(Get.threadFromRoot(data), keywords)) { + found.push(data); } + target = target.next; } - return _results; + return found; }, searchMatch: function(thread, keywords) { var file, info, key, keyword, text, _i, _j, _len, _len1, _ref, _ref1; @@ -9887,10 +9938,10 @@ if (!(post.prev || post.next)) { Unread.posts.push(post); } - Unread.addPostQuotingYou(post); + Unread.addPostQuotingYou(post.data); } if (Conf['Unread Line']) { - Unread.setLine((_ref = Unread.posts.first, __indexOf.call(posts, _ref) >= 0)); + Unread.setLine((_ref = Unread.posts.first.data, __indexOf.call(posts, _ref) >= 0)); } Unread.read(); return Unread.update(); @@ -9972,13 +10023,13 @@ height = doc.clientHeight; posts = Unread.posts; while (post = posts.first) { - if (!(Header.getBottomOf(post.nodes.root) > -1)) { + if (!(Header.getBottomOf(post.data.nodes.root) > -1)) { break; } ID = post.ID; posts.rm(ID); if (Conf['Mark Quotes of You'] && post.info.yours) { - QuoteYou.lastRead = post.nodes.root; + QuoteYou.lastRead = post.data.nodes.root; } } if (!ID) { diff --git a/src/General/Index.coffee b/src/General/Index.coffee index e9ca91a98..8e577e1c1 100644 --- a/src/General/Index.coffee +++ b/src/General/Index.coffee @@ -119,11 +119,8 @@ Index = pageNum = Index.pageNum++ return Index.endNotice() if pageNum >= Index.pagesNum - nodesPerPage = Index.threadsNumPerPage - offset = nodesPerPage * pageNum - nodes = Index.sortedNodes[offset ... offset + nodesPerPage] - - Index.buildReplies nodes if Conf['Show Replies'] + nodes = Index.buildSinglePage pageNum + Index.buildReplies nodes if Conf['Show Replies'] Index.buildStructure nodes Index.setPage pageNum @@ -390,12 +387,13 @@ Index = replycount: [Index.liveThreadData...].sort((a, b) -> b.replies - a.replies).map (data) -> data.no filecount: [Index.liveThreadData...].sort((a, b) -> b.images - a.images ).map (data) -> data.no }[Conf['Index Sort']] - Index.sortedNodes = [] + Index.sortedNodes = new RandomAccessList {nodes} = Index for threadID in sortedThreadIDs Index.sortedNodes.push nodes[Index.liveThreadIDs.indexOf(threadID)] if Index.isSearching and nodes = Index.querySearch(Index.searchInput.value) - Index.sortedNodes = nodes + Index.sortedNodes = new RandomAccessList + Index.sortedNodes.push node for node in nodes items = [ # Sticky threads fn: (thread) -> thread.isSticky @@ -415,23 +413,47 @@ Index = sortOnTop: (match) -> offset = 0 - for threadRoot, i in Index.sortedNodes when match Get.threadFromRoot threadRoot - Index.sortedNodes.splice offset++, 0, Index.sortedNodes.splice(i, 1)[0] + {sortedNodes} = Index + threadRoot = Index.sortedNodes.first + while threadRoot + if match Get.threadFromRoot threadRoot.data + target = Index.sortedNodes.first + j = 0 + while j++ < offset + target = target.next + unless threadRoot is target + offset++ + sortedNodes.before target, threadRoot + threadRoot = threadRoot.next return buildIndex: -> if Conf['Index Mode'] isnt 'all pages' - pageNum = Index.getCurrentPage() - nodesPerPage = Index.threadsNumPerPage - offset = nodesPerPage * pageNum - nodes = Index.sortedNodes[offset ... offset + nodesPerPage] + nodes = Index.buildSinglePage Index.getCurrentPage() else - nodes = Index.sortedNodes + nodes = [] + target = Index.sortedNodes.first + while target + nodes.push target.data + target = target.next $.rmAll Index.root $.rmAll Header.hover Index.buildReplies nodes if Conf['Show Replies'] Index.buildStructure nodes + buildSinglePage: (pageNum) -> + nodes = [] + nodesPerPage = Index.threadsNumPerPage + offset = nodesPerPage * pageNum + end = offset + nodesPerPage + target = Index.sortedNodes.first + i = 0 + while i <= end + if offset <= i++ + nodes.push target.data + target = target.next + nodes + buildStructure: (nodes) -> result = $.frag() i = 0 @@ -478,7 +500,15 @@ Index = return unless keywords = query.toLowerCase().match /\S+/g Index.search keywords - search: (keywords) -> threadRoot for threadRoot in Index.sortedNodes when Index.searchMatch Get.threadFromRoot(threadRoot), keywords + search: (keywords) -> + found = [] + target = Index.sortedNodes.first + while target + {data} = target + if Index.searchMatch Get.threadFromRoot(data), keywords + found.push data + target = target.next + found searchMatch: (thread, keywords) -> {info, file} = thread.OP diff --git a/src/General/lib/randomaccesslist.class b/src/General/lib/randomaccesslist.class index e16e57204..9ccbfeb43 100644 --- a/src/General/lib/randomaccesslist.class +++ b/src/General/lib/randomaccesslist.class @@ -2,18 +2,34 @@ class RandomAccessList constructor: -> @length = 0 - push: (item) -> - {ID} = item + push: (data) -> + {ID} = data + ID or= data.id return if @[ID] {last} = @ + @[ID] = item = + prev: last + next: null + data: data + ID: ID item.prev = last - @[ID] = item @last = if last last.next = item else @first = item @length++ + before: (root, item) -> + return if item.next is root + + @rmi item + + {prev} = root + root.prev = item + item.next = root + item.prev = prev + prev.next = item if prev + after: (root, item) -> return if item.prev is root @@ -24,7 +40,7 @@ class RandomAccessList item.prev = root item.next = next next.prev = item if next - + prepend: (item) -> {first} = @ return if item is first or not @[item.ID] diff --git a/src/Monitoring/Unread.coffee b/src/Monitoring/Unread.coffee index ba9cb5fca..ab5729e3b 100755 --- a/src/Monitoring/Unread.coffee +++ b/src/Monitoring/Unread.coffee @@ -92,10 +92,10 @@ Unread = postID: ID } Unread.posts.push post unless post.prev or post.next - Unread.addPostQuotingYou post + Unread.addPostQuotingYou post.data if Conf['Unread Line'] # Force line on visible threads if there were no unread posts previously. - Unread.setLine Unread.posts.first in posts + Unread.setLine Unread.posts.first.data in posts Unread.read() Unread.update() @@ -151,12 +151,12 @@ Unread = {posts} = Unread while post = posts.first - break unless Header.getBottomOf(post.nodes.root) > -1 # post is not completely read + break unless Header.getBottomOf(post.data.nodes.root) > -1 # post is not completely read {ID} = post posts.rm ID if Conf['Mark Quotes of You'] and post.info.yours - QuoteYou.lastRead = post.nodes.root + QuoteYou.lastRead = post.data.nodes.root return unless ID