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: ->