diff --git a/builds/appchan-x.user.js b/builds/appchan-x.user.js index a5c766317..2acb380aa 100644 --- a/builds/appchan-x.user.js +++ b/builds/appchan-x.user.js @@ -2696,7 +2696,7 @@ (function() { var reqs; reqs = {}; - return $.cache = function(url, cb, options) { + $.cache = function(url, cb, options) { var err, req, rm; if (req = reqs[url]) { if (req.readyState === 4) { @@ -2734,6 +2734,14 @@ req.callbacks = [cb]; return reqs[url] = req; }; + return $.cleanCache = function(testf) { + var url; + for (url in reqs) { + if (testf(url)) { + delete reqs[url]; + } + } + }; })(); $.cb = { @@ -4762,7 +4770,7 @@ Index = { showHiddenThreads: false, init: function() { - var input, label, modeEntry, name, refNavEntry, repliesEntry, returnLink, select, sortEntry, targetEntry, threadNumEntry, threadsNumInput, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2; + var anchorEntry, input, label, modeEntry, name, pinEntry, refNavEntry, repliesEntry, returnLink, select, sortEntry, targetEntry, threadNumEntry, threadsNumInput, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2; if (g.BOARD.ID === 'f' || !Conf['JSON Navigation']) { return; } @@ -4835,23 +4843,25 @@ $.on(threadsNumInput, 'change', $.cb.value); $.on(threadsNumInput, 'change', this.cb.threadsNum); targetEntry = { - el: $.el('label', { - innerHTML: ' Open threads in a new tab', - title: 'Catalog-only setting.' - }) + el: UI.checkbox('Open threads in a new tab', 'Open threads in a new tab') }; repliesEntry = { - el: $.el('label', { - innerHTML: ' Show replies' - }) + el: UI.checkbox('Show Replies', 'Show replies') + }; + pinEntry = { + el: UI.checkbox('Pin Watched Threads', 'Pin watched threads') + }; + anchorEntry = { + el: UI.checkbox('Anchor Hidden Threads', 'Anchor hidden threads') }; refNavEntry = { - el: $.el('label', { - innerHTML: ' Refreshed navigation', - title: 'Refresh index when navigating through pages.' - }) + el: UI.checkbox('Refreshed Navigation', 'Refreshed navigation') }; - _ref1 = [targetEntry, repliesEntry, refNavEntry]; + targetEntry.el.title = 'Catalog-only setting.'; + pinEntry.el.title = 'Move watched threads to the start of the index.'; + anchorEntry.el.title = 'Move hidden threads to the end of the index.'; + refNavEntry.el.title = 'Refresh index when navigating through pages.'; + _ref1 = [targetEntry, repliesEntry, pinEntry, anchorEntry, refNavEntry]; for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { label = _ref1[_j]; input = label.el.firstChild; @@ -4864,6 +4874,10 @@ break; case 'Show Replies': $.on(input, 'change', this.cb.replies); + break; + case 'Pin Watched Threads': + case 'Anchor Hidden Threads': + $.on(input, 'change', this.cb.sort); } } Header.menu.addEntry({ @@ -4871,7 +4885,7 @@ textContent: 'Index Navigation' }), order: 98, - subEntries: [threadNumEntry, targetEntry, repliesEntry, refNavEntry] + subEntries: [threadNumEntry, targetEntry, repliesEntry, pinEntry, anchorEntry, refNavEntry] }); $.addClass(doc, 'index-loading'); this.root = $.el('div', { @@ -4960,6 +4974,7 @@ } board = $('.board'); $.replace(board, Index.root); + $.event('PostsInserted'); return d.implementation.createDocument(null, null, null).appendChild(board); }); return $.asap((function() { @@ -4978,7 +4993,7 @@ if (Index.req || Conf['Index Mode'] !== 'infinite' || (window.scrollY <= doc.scrollHeight - (300 + window.innerHeight)) || g.VIEW === 'thread') { return; } - Index.currentPage = (Index.currentPage || Index.getCurrentPage()) + 1; + Index.currentPage = Index.getCurrentPage() + 1; if (Index.currentPage >= Index.pagesNum) { return Index.endNotice(); } @@ -5023,8 +5038,7 @@ $.event('CloseMenu'); return Index.togglePin(thread); }; - $.on(this.el, 'click', this.cb); - return true; + return $.on(this.el, 'click', this.cb); } }); } @@ -5056,13 +5070,12 @@ thread = g.threads[this.parentNode.dataset.fullID]; if (e.shiftKey) { PostHiding.toggle(thread.OP); - return e.preventDefault(); } else if (e.altKey) { Index.togglePin(thread); - return e.preventDefault(); } else { return Navigate.navigate.call(this, e); } + return e.preventDefault(); }, onOver: function(e) { var el, nodes; @@ -5111,13 +5124,18 @@ }, cycleSortType: function() { var i, option, type, types, _i, _len; - types = []; - i = 0; - while (option = Index.selectSort.options[i++]) { - if (!option.disabled) { - types.push(option); + types = (function() { + var _i, _len, _ref, _results; + _ref = Index.selectSort.options; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + option = _ref[_i]; + if (!option.disabled) { + _results.push(option); + } } - } + return _results; + })(); for (i = _i = 0, _len = types.length; _i < _len; i = ++_i) { type = types[i]; if (type.selected) { @@ -5256,6 +5274,7 @@ } switch (e.target.nodeName) { case 'BUTTON': + e.target.blur(); a = e.target.parentNode; break; case 'A': @@ -5318,6 +5337,10 @@ return Header.scrollToIfNeeded(Index.navLinks); }, getCurrentPage: function() { + var _ref; + if ((_ref = Conf['Index Mode']) === 'all pages' || _ref === 'catalog') { + return 1; + } if (Conf['Index Mode'] === 'infinite' && Index.currentPage) { return Index.currentPage; } @@ -5550,6 +5573,9 @@ return Index.scrollToIndex(); }, parse: function(pages, pageNum) { + $.cleanCache(function(url) { + return /^\/\/a\.4cdn\.org\//.test(url); + }); Index.parseThreadList(pages); Index.buildThreads(); Index.sort(); @@ -5761,20 +5787,28 @@ Index.sortOnTop(function(thread) { return thread.isSticky; }); - return Index.sortOnTop(function(thread) { - return thread.isOnTop || thread.isPinned; + Index.sortOnTop(function(thread) { + return thread.isOnTop || Conf['Pin Watched Threads'] && ThreadWatcher.isWatched(thread); }); + if (Conf['Anchor Hidden Threads']) { + return Index.sortOnTop(function(thread) { + return !thread.isHidden; + }); + } }, sortOnTop: function(match) { - var i, offset, thread, _i, _len, _ref; + var bottomThreads, i, offset, thread, topThreads, _i, _len, _ref; offset = 0; + topThreads = []; + bottomThreads = []; _ref = Index.sortedThreads; for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { thread = _ref[i]; if (match(thread)) { - Index.sortedThreads.splice(offset++, 0, Index.sortedThreads.splice(i, 1)[0]); + (match(thread) ? topThreads : bottomThreads).push(thread); } } + return Index.sortedThreads = topThreads.push.apply(topThreads, bottomThreads); }, buildIndex: function(infinite) { var i, max, nodes, pageNum, sortedThreads, thread, threads, threadsPerPage; diff --git a/builds/crx/script.js b/builds/crx/script.js index 81b43b7ea..486a6de9e 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -2668,7 +2668,7 @@ (function() { var reqs; reqs = {}; - return $.cache = function(url, cb, options) { + $.cache = function(url, cb, options) { var err, req, rm; if (req = reqs[url]) { if (req.readyState === 4) { @@ -2706,6 +2706,14 @@ req.callbacks = [cb]; return reqs[url] = req; }; + return $.cleanCache = function(testf) { + var url; + for (url in reqs) { + if (testf(url)) { + delete reqs[url]; + } + } + }; })(); $.cb = { @@ -4791,7 +4799,7 @@ Index = { showHiddenThreads: false, init: function() { - var input, label, modeEntry, name, refNavEntry, repliesEntry, returnLink, select, sortEntry, targetEntry, threadNumEntry, threadsNumInput, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2; + var anchorEntry, input, label, modeEntry, name, pinEntry, refNavEntry, repliesEntry, returnLink, select, sortEntry, targetEntry, threadNumEntry, threadsNumInput, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2; if (g.BOARD.ID === 'f' || !Conf['JSON Navigation']) { return; } @@ -4864,23 +4872,25 @@ $.on(threadsNumInput, 'change', $.cb.value); $.on(threadsNumInput, 'change', this.cb.threadsNum); targetEntry = { - el: $.el('label', { - innerHTML: ' Open threads in a new tab', - title: 'Catalog-only setting.' - }) + el: UI.checkbox('Open threads in a new tab', 'Open threads in a new tab') }; repliesEntry = { - el: $.el('label', { - innerHTML: ' Show replies' - }) + el: UI.checkbox('Show Replies', 'Show replies') + }; + pinEntry = { + el: UI.checkbox('Pin Watched Threads', 'Pin watched threads') + }; + anchorEntry = { + el: UI.checkbox('Anchor Hidden Threads', 'Anchor hidden threads') }; refNavEntry = { - el: $.el('label', { - innerHTML: ' Refreshed navigation', - title: 'Refresh index when navigating through pages.' - }) + el: UI.checkbox('Refreshed Navigation', 'Refreshed navigation') }; - _ref1 = [targetEntry, repliesEntry, refNavEntry]; + targetEntry.el.title = 'Catalog-only setting.'; + pinEntry.el.title = 'Move watched threads to the start of the index.'; + anchorEntry.el.title = 'Move hidden threads to the end of the index.'; + refNavEntry.el.title = 'Refresh index when navigating through pages.'; + _ref1 = [targetEntry, repliesEntry, pinEntry, anchorEntry, refNavEntry]; for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { label = _ref1[_j]; input = label.el.firstChild; @@ -4893,6 +4903,10 @@ break; case 'Show Replies': $.on(input, 'change', this.cb.replies); + break; + case 'Pin Watched Threads': + case 'Anchor Hidden Threads': + $.on(input, 'change', this.cb.sort); } } Header.menu.addEntry({ @@ -4900,7 +4914,7 @@ textContent: 'Index Navigation' }), order: 98, - subEntries: [threadNumEntry, targetEntry, repliesEntry, refNavEntry] + subEntries: [threadNumEntry, targetEntry, repliesEntry, pinEntry, anchorEntry, refNavEntry] }); $.addClass(doc, 'index-loading'); this.root = $.el('div', { @@ -4989,6 +5003,7 @@ } board = $('.board'); $.replace(board, Index.root); + $.event('PostsInserted'); return d.implementation.createDocument(null, null, null).appendChild(board); }); return $.asap((function() { @@ -5007,7 +5022,7 @@ if (Index.req || Conf['Index Mode'] !== 'infinite' || (window.scrollY <= doc.scrollHeight - (300 + window.innerHeight)) || g.VIEW === 'thread') { return; } - Index.currentPage = (Index.currentPage || Index.getCurrentPage()) + 1; + Index.currentPage = Index.getCurrentPage() + 1; if (Index.currentPage >= Index.pagesNum) { return Index.endNotice(); } @@ -5052,8 +5067,7 @@ $.event('CloseMenu'); return Index.togglePin(thread); }; - $.on(this.el, 'click', this.cb); - return true; + return $.on(this.el, 'click', this.cb); } }); } @@ -5085,13 +5099,12 @@ thread = g.threads[this.parentNode.dataset.fullID]; if (e.shiftKey) { PostHiding.toggle(thread.OP); - return e.preventDefault(); } else if (e.altKey) { Index.togglePin(thread); - return e.preventDefault(); } else { return Navigate.navigate.call(this, e); } + return e.preventDefault(); }, onOver: function(e) { var el, nodes; @@ -5140,13 +5153,18 @@ }, cycleSortType: function() { var i, option, type, types, _i, _len; - types = []; - i = 0; - while (option = Index.selectSort.options[i++]) { - if (!option.disabled) { - types.push(option); + types = (function() { + var _i, _len, _ref, _results; + _ref = Index.selectSort.options; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + option = _ref[_i]; + if (!option.disabled) { + _results.push(option); + } } - } + return _results; + })(); for (i = _i = 0, _len = types.length; _i < _len; i = ++_i) { type = types[i]; if (type.selected) { @@ -5285,6 +5303,7 @@ } switch (e.target.nodeName) { case 'BUTTON': + e.target.blur(); a = e.target.parentNode; break; case 'A': @@ -5347,6 +5366,10 @@ return Header.scrollToIfNeeded(Index.navLinks); }, getCurrentPage: function() { + var _ref; + if ((_ref = Conf['Index Mode']) === 'all pages' || _ref === 'catalog') { + return 1; + } if (Conf['Index Mode'] === 'infinite' && Index.currentPage) { return Index.currentPage; } @@ -5579,6 +5602,9 @@ return Index.scrollToIndex(); }, parse: function(pages, pageNum) { + $.cleanCache(function(url) { + return /^\/\/a\.4cdn\.org\//.test(url); + }); Index.parseThreadList(pages); Index.buildThreads(); Index.sort(); @@ -5790,20 +5816,28 @@ Index.sortOnTop(function(thread) { return thread.isSticky; }); - return Index.sortOnTop(function(thread) { - return thread.isOnTop || thread.isPinned; + Index.sortOnTop(function(thread) { + return thread.isOnTop || Conf['Pin Watched Threads'] && ThreadWatcher.isWatched(thread); }); + if (Conf['Anchor Hidden Threads']) { + return Index.sortOnTop(function(thread) { + return !thread.isHidden; + }); + } }, sortOnTop: function(match) { - var i, offset, thread, _i, _len, _ref; + var bottomThreads, i, offset, thread, topThreads, _i, _len, _ref; offset = 0; + topThreads = []; + bottomThreads = []; _ref = Index.sortedThreads; for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { thread = _ref[i]; if (match(thread)) { - Index.sortedThreads.splice(offset++, 0, Index.sortedThreads.splice(i, 1)[0]); + (match(thread) ? topThreads : bottomThreads).push(thread); } } + return Index.sortedThreads = topThreads.push.apply(topThreads, bottomThreads); }, buildIndex: function(infinite) { var i, max, nodes, pageNum, sortedThreads, thread, threads, threadsPerPage; diff --git a/src/General/Index.coffee b/src/General/Index.coffee index 39e75880d..b0a1b2b7d 100644 --- a/src/General/Index.coffee +++ b/src/General/Index.coffee @@ -48,21 +48,18 @@ Index = $.on threadsNumInput, 'change', $.cb.value $.on threadsNumInput, 'change', @cb.threadsNum - targetEntry = - el: $.el 'label', - innerHTML: ' Open threads in a new tab' - title: 'Catalog-only setting.' + targetEntry = el: UI.checkbox 'Open threads in a new tab', 'Open threads in a new tab' + repliesEntry = el: UI.checkbox 'Show Replies', 'Show replies' + pinEntry = el: UI.checkbox 'Pin Watched Threads', 'Pin watched threads' + anchorEntry = el: UI.checkbox 'Anchor Hidden Threads', 'Anchor hidden threads' + refNavEntry = el: UI.checkbox 'Refreshed Navigation', 'Refreshed navigation' - repliesEntry = - el: $.el 'label', - innerHTML: ' Show replies' + targetEntry.el.title = 'Catalog-only setting.' + pinEntry.el.title = 'Move watched threads to the start of the index.' + anchorEntry.el.title = 'Move hidden threads to the end of the index.' + refNavEntry.el.title = 'Refresh index when navigating through pages.' - refNavEntry = - el: $.el 'label', - innerHTML: ' Refreshed navigation' - title: 'Refresh index when navigating through pages.' - - for label in [targetEntry, repliesEntry, refNavEntry] + for label in [targetEntry, repliesEntry, pinEntry, anchorEntry, refNavEntry] input = label.el.firstChild {name} = input input.checked = Conf[name] @@ -72,12 +69,14 @@ Index = $.on input, 'change', @cb.target when 'Show Replies' $.on input, 'change', @cb.replies + when 'Pin Watched Threads', 'Anchor Hidden Threads' + $.on input, 'change', @cb.sort Header.menu.addEntry el: $.el 'span', textContent: 'Index Navigation' order: 98 - subEntries: [threadNumEntry, targetEntry, repliesEntry, refNavEntry] + subEntries: [threadNumEntry, targetEntry, repliesEntry, pinEntry, anchorEntry, refNavEntry] $.addClass doc, 'index-loading' @@ -156,6 +155,7 @@ Index = board = $ '.board' $.replace board, Index.root + $.event 'PostsInserted' # Hacks: # - When removing an element from the document during page load, # its ancestors will still be correctly created inside of it. @@ -174,7 +174,7 @@ Index = scroll: -> return if Index.req or Conf['Index Mode'] isnt 'infinite' or (window.scrollY <= doc.scrollHeight - (300 + window.innerHeight)) or g.VIEW is 'thread' - Index.currentPage = (Index.currentPage or Index.getCurrentPage()) + 1 # Avoid having to pushState to keep track of the current page + Index.currentPage = Index.getCurrentPage() + 1 # Avoid having to pushState to keep track of the current page return Index.endNotice() if Index.currentPage >= Index.pagesNum Index.buildIndex true @@ -205,7 +205,6 @@ Index = $.event 'CloseMenu' Index.togglePin thread $.on @el, 'click', @cb - true threadNode: -> return if g.VIEW isnt 'index' @@ -222,12 +221,11 @@ Index = thread = g.threads[@parentNode.dataset.fullID] if e.shiftKey PostHiding.toggle thread.OP - e.preventDefault() else if e.altKey Index.togglePin thread - e.preventDefault() else - Navigate.navigate.call @, e + return Navigate.navigate.call @, e + e.preventDefault() onOver: (e) -> # 4chan's less than stellar CSS forces us to include a .post and .postInfo @@ -271,10 +269,7 @@ Index = $.event 'change', null, Index.selectMode cycleSortType: -> - types = [] - i = 0 - while option = Index.selectSort.options[i++] - types.push option if !option.disabled + types = (option for option in Index.selectSort.options when not option.disabled) for type, i in types break if type.selected types[(i + 1) % types.length].selected = true @@ -375,6 +370,7 @@ Index = return if e.shiftKey or e.altKey or e.ctrlKey or e.metaKey or e.button isnt 0 switch e.target.nodeName when 'BUTTON' + e.target.blur() a = e.target.parentNode when 'A' a = e.target @@ -420,6 +416,8 @@ Index = Header.scrollToIfNeeded Index.navLinks getCurrentPage: -> + if Conf['Index Mode'] in ['all pages', 'catalog'] + return 1 if Conf['Index Mode'] is 'infinite' and Index.currentPage return Index.currentPage +window.location.pathname.split('/')[2] or 1 @@ -607,6 +605,7 @@ Index = Index.scrollToIndex() parse: (pages, pageNum) -> + $.cleanCache (url) -> /^\/\/a\.4cdn\.org\//.test url Index.parseThreadList pages Index.buildThreads() Index.sort() @@ -644,7 +643,6 @@ Index = thread.setCount 'file', threadData.images + !!threadData.ext, threadData.imagelimit thread.setStatus 'Sticky', !!threadData.sticky thread.setStatus 'Closed', !!threadData.closed - else thread = new Thread threadData.no, g.BOARD threads.push thread @@ -764,13 +762,17 @@ Index = # Sticky threads Index.sortOnTop (thread) -> thread.isSticky # Highlighted threads - Index.sortOnTop (thread) -> thread.isOnTop or thread.isPinned + Index.sortOnTop (thread) -> thread.isOnTop or Conf['Pin Watched Threads'] and ThreadWatcher.isWatched thread + # Non-hidden threads + Index.sortOnTop((thread) -> !thread.isHidden) if Conf['Anchor Hidden Threads'] sortOnTop: (match) -> offset = 0 + topThreads = [] + bottomThreads = [] for thread, i in Index.sortedThreads when match thread - Index.sortedThreads.splice offset++, 0, Index.sortedThreads.splice(i, 1)[0] - return + (if match thread then topThreads else bottomThreads).push thread + Index.sortedThreads = topThreads.push bottomThreads... buildIndex: (infinite) -> {sortedThreads} = Index diff --git a/src/General/lib/$.coffee b/src/General/lib/$.coffee index 18bc3070f..aa99fb9a3 100755 --- a/src/General/lib/$.coffee +++ b/src/General/lib/$.coffee @@ -83,6 +83,10 @@ do -> $.on req, 'abort error', rm req.callbacks = [cb] reqs[url] = req + $.cleanCache = (testf) -> + for url of reqs when testf url + delete reqs[url] + return $.cb = checked: ->