Merge branch 'v3'

Conflicts:
	CHANGELOG.md
	LICENSE
	builds/4chan-X.meta.js
	builds/appchan-x.user.js
	builds/crx/manifest.json
	builds/crx/script.js
	latest.js
	package.json
	src/Menu/Menu.coffee
This commit is contained in:
Zixaphir 2014-01-13 13:25:43 -07:00
commit 94dd0e555f
12 changed files with 206 additions and 203 deletions

View File

@ -1,5 +1,6 @@
**seaweeedchan**: **seaweeedchan**:
- Convert infinite scrolling into an Index Mode, rather than being forced - Convert infinite scrolling into an Index Mode, rather than being forced
- Fix Menu errors on older Firefox versions, such as the ESR
**Zixaphir**: **Zixaphir**:
- Fix an issue where changing the current archive would crash the redirect features. - Fix an issue where changing the current archive would crash the redirect features.

View File

@ -1,5 +1,5 @@
/* /*
* appchan x - Version 2.8.0 - 2014-01-12 * appchan x - Version 2.8.0 - 2014-01-13
* *
* Licensed under the MIT license. * Licensed under the MIT license.
* https://github.com/zixaphir/appchan-x/blob/master/LICENSE * https://github.com/zixaphir/appchan-x/blob/master/LICENSE

21
builds/4chan-X.meta.js Executable file
View File

@ -0,0 +1,21 @@
// ==UserScript==
// @name 4chan X
// @version 1.3.2
// @minGMVer 1.13
// @minFFVer 26
// @namespace 4chan-X
// @description Cross-browser userscript for maximum lurking on 4chan.
// @license MIT; https://github.com/seaweedchan/4chan-x/blob/master/LICENSE
// @match *://boards.4chan.org/*
// @match *://sys.4chan.org/*
// @match *://a.4cdn.org/*
// @match *://i.4cdn.org/*
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_deleteValue
// @grant GM_openInTab
// @run-at document-start
// @updateURL https://github.com/seaweedchan/4chan-x/raw/stable/builds/4chan-X.meta.js
// @downloadURL https://github.com/seaweedchan/4chan-x/raw/stable/builds/4chan-X.user.js
// @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwAgMAAAAqbBEUAAAACVBMVEUAAGcAAABmzDNZt9VtAAAAAXRSTlMAQObYZgAAAHFJREFUKFOt0LENACEIBdBv4Qju4wgWanEj3D6OcIVMKaitYHEU/jwTCQj8W75kiVCSBvdQ5/AvfVHBin11BgdRq3ysBgfwBDRrj3MCIA+oAQaku/Q1cNctrAmyDl577tOThYt/Y1RBM4DgOHzM0HFTAyLukH/cmRnqAAAAAElFTkSuQmCC
// ==/UserScript==

View File

@ -22,7 +22,7 @@
// ==/UserScript== // ==/UserScript==
/* /*
* appchan x - Version 2.8.0 - 2014-01-12 * appchan x - Version 2.8.0 - 2014-01-13
* *
* Licensed under the MIT license. * Licensed under the MIT license.
* https://github.com/zixaphir/appchan-x/blob/master/LICENSE * https://github.com/zixaphir/appchan-x/blob/master/LICENSE
@ -117,15 +117,17 @@
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
Array.prototype.indexOf = function(val) { Array.prototype.indexOf = function(val, i) {
var i; var len;
i = this.length; i || (i = 0);
while (i--) { len = this.length;
while (i < len) {
if (this[i] === val) { if (this[i] === val) {
return i; return i;
} }
i++;
} }
return i; return -1;
}; };
__indexOf = [].indexOf; __indexOf = [].indexOf;
@ -4576,7 +4578,7 @@
}); });
this.navLinks = $.el('div', { this.navLinks = $.el('div', {
className: 'navLinks', className: 'navLinks',
innerHTML: "<a href=.././ id=returnlink>Return</a> [<a href=javascript:; id=cataloglink>Catalog</a>] <a href=\"#bottom\" id=bottomlink>Bottom</a> <time id=\"index-last-refresh\" title=\"Last index refresh\">...</time> <input type=\"search\" id=\"index-search\" class=\"field\" placeholder=\"Search\"><a id=\"index-search-clear\" class=\"fa\" href=\"javascript:;\">\uf057</a>" innerHTML: "<span class=brackets-wrap id=returnlink><a href=.././>Return</a></span> <span class=brackets-wrap id=cataloglink><a href=javascript:;>Catalog</a></span> <span class=brackets-wrap id=bottomlink><a href=\"#bottom\">Bottom</a></span> <span class=brackets-wrap id=\"index-last-refresh\"><time title=\"Last index refresh\">...</time></span> <input type=\"search\" id=\"index-search\" class=\"field\" placeholder=\"Search\"><a id=\"index-search-clear\" class=\"fa\" href=\"javascript:;\">\uf057</a>"
}); });
this.searchInput = $('#index-search', this.navLinks); this.searchInput = $('#index-search', this.navLinks);
this.currentPage = this.getCurrentPage(); this.currentPage = this.getCurrentPage();
@ -4584,8 +4586,8 @@
$.on(this.pagelist, 'click', this.cb.pageNav); $.on(this.pagelist, 'click', this.cb.pageNav);
$.on(this.searchInput, 'input', this.onSearchInput); $.on(this.searchInput, 'input', this.onSearchInput);
$.on($('#index-search-clear', this.navLinks), 'click', this.clearSearch); $.on($('#index-search-clear', this.navLinks), 'click', this.clearSearch);
$.on($('#returnlink', this.navLinks), 'click', Navigate.navigate); $.on($('#returnlink a', this.navLinks), 'click', Navigate.navigate);
$.on($('#cataloglink', this.navLinks), 'click', function() { $.on($('#cataloglink a', this.navLinks), 'click', function() {
return window.location = "//boards.4chan.org/" + g.BOARD + "/catalog"; return window.location = "//boards.4chan.org/" + g.BOARD + "/catalog";
}); });
if (g.VIEW === 'index') { if (g.VIEW === 'index') {
@ -4861,7 +4863,7 @@
} }
return; return;
} }
timeEl = $('#index-last-refresh', Index.navLinks); timeEl = $('#index-last-refresh time', Index.navLinks);
timeEl.dataset.utc = Date.parse(req.getResponseHeader('Last-Modified')); timeEl.dataset.utc = Date.parse(req.getResponseHeader('Last-Modified'));
RelativeDates.update(timeEl); RelativeDates.update(timeEl);
return Index.scrollToIndex(); return Index.scrollToIndex();
@ -5397,36 +5399,36 @@
}; };
}, },
allQuotelinksLinkingTo: function(post) { allQuotelinksLinkingTo: function(post) {
var ID, quote, quotedPost, quotelinks, quoterPost, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3, _ref4; var ID, handleQuotes, quote, quotedPost, quotelinks, quoterPost, _i, _len, _ref, _ref1, _ref2;
quotelinks = []; quotelinks = [];
handleQuotes = function(post, type) {
var clone, _i, _len, _ref;
quotelinks.push.apply(quotelinks, post.nodes[type]);
_ref = post.clones;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
clone = _ref[_i];
quotelinks.push.apply(quotelinks, clone.nodes[type]);
}
};
_ref = g.posts; _ref = g.posts;
for (ID in _ref) { for (ID in _ref) {
quoterPost = _ref[ID]; quoterPost = _ref[ID];
if (_ref1 = post.fullID, __indexOf.call(quoterPost.quotes, _ref1) >= 0) { if (_ref1 = post.fullID, __indexOf.call(quoterPost.quotes, _ref1) >= 0) {
_ref2 = [quoterPost].concat(quoterPost.clones); handleQuotes(quoterPost, 'quotelinks');
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
quoterPost = _ref2[_i];
quotelinks.push.apply(quotelinks, quoterPost.nodes.quotelinks);
}
} }
} }
if (Conf['Quote Backlinks']) { if (Conf['Quote Backlinks']) {
_ref3 = post.quotes; _ref2 = post.quotes;
for (_j = 0, _len1 = _ref3.length; _j < _len1; _j++) { for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
quote = _ref3[_j]; quote = _ref2[_i];
if (!(quotedPost = g.posts[quote])) { if (quotedPost = g.posts[quote]) {
continue; handleQuotes(quotedPost, 'backlinks');
}
_ref4 = [quotedPost].concat(quotedPost.clones);
for (_k = 0, _len2 = _ref4.length; _k < _len2; _k++) {
quotedPost = _ref4[_k];
quotelinks.push.apply(quotelinks, __slice.call(quotedPost.nodes.backlinks));
} }
} }
} }
return quotelinks.filter(function(quotelink) { return quotelinks.filter(function(quotelink) {
var boardID, postID, _ref5; var boardID, postID, _ref3;
_ref5 = Get.postDataFromLink(quotelink), boardID = _ref5.boardID, postID = _ref5.postID; _ref3 = Get.postDataFromLink(quotelink), boardID = _ref3.boardID, postID = _ref3.postID;
return boardID === post.board.ID && postID === post.ID; return boardID === post.board.ID && postID === post.ID;
}); });
}, },
@ -7100,28 +7102,14 @@
if (g.VIEW === 'catalog' || !Conf['Quote Inlining']) { if (g.VIEW === 'catalog' || !Conf['Quote Inlining']) {
return; return;
} }
if (Conf['Quote Hash Navigation']) { this.process = Conf['Quote Hash Navigation'] ? function(link, clone) {
this.node = function() { if (!clone) {
var link, _i, _len, _ref; $.after(link, QuoteInline.qiQuote(link, $.hasClass(link, 'filtered')));
_ref = this.nodes.quotelinks.concat(__slice.call(this.nodes.backlinks)); }
for (_i = 0, _len = _ref.length; _i < _len; _i++) { return $.on(link, 'click', QuoteInline.toggle);
link = _ref[_i]; } : function(link) {
if (!this.isClone) { return $.on(link, 'click', QuoteInline.toggle);
$.after(link, QuoteInline.qiQuote(link, $.hasClass(link, 'filtered'))); };
}
$.on(link, 'click', QuoteInline.toggle);
}
};
} else {
this.node = function() {
var link, _i, _len, _ref;
_ref = this.nodes.quotelinks.concat(__slice.call(this.nodes.backlinks));
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
link = _ref[_i];
$.on(link, 'click', QuoteInline.toggle);
}
};
}
if (Conf['Comment Expansion']) { if (Conf['Comment Expansion']) {
ExpandComment.callbacks.push(this.node); ExpandComment.callbacks.push(this.node);
} }
@ -7130,14 +7118,27 @@
cb: this.node cb: this.node
}); });
}, },
node: function() {
var isClone, link, process, _i, _j, _len, _len1, _ref, _ref1;
process = QuoteInline.process;
isClone = this.isClone;
_ref = this.nodes.quotelinks;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
link = _ref[_i];
process(link, isClone);
}
_ref1 = this.nodes.backlinks;
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
link = _ref1[_j];
process(link, isClone);
}
},
qiQuote: function(link, hidden) { qiQuote: function(link, hidden) {
return [ return $.el('a', {
$.tn(' '), $.el('a', { className: "hashlink" + (hidden ? ' filtered' : ''),
className: hidden ? 'hashlink filtered' : 'hashlink', textContent: '#',
textContent: '#', href: link.href
href: link.href });
})
];
}, },
toggle: function(e) { toggle: function(e) {
var boardID, context, postID, threadID, _ref; var boardID, context, postID, threadID, _ref;
@ -10905,21 +10906,16 @@
return $.add(this.nodes.info, Menu.makeButton()); return $.add(this.nodes.info, Menu.makeButton());
}, },
makeButton: (function() { makeButton: (function() {
var frag; var a;
frag = null; a = $.el('a', {
className: 'menu-button',
innerHTML: '<i class=fa>\uf107</i>',
href: 'javascript:;'
});
return function() { return function() {
var clone; var clone;
if (frag == null) { clone = a.cloneNode(true);
frag = $.nodes([ $.on(clone, 'click', Menu.toggle);
$.tn(' '), $.el('a', {
className: 'menu-button',
innerHTML: '<i class=fa>\uf107</i>',
href: 'javascript:;'
})
]);
}
clone = frag.cloneNode(true);
$.on(clone.lastElementChild, 'click', Menu.toggle);
return clone; return clone;
}; };
})(), })(),

View File

@ -1,6 +1,6 @@
// Generated by CoffeeScript // Generated by CoffeeScript
/* /*
* appchan x - Version 2.8.0 - 2014-01-12 * appchan x - Version 2.8.0 - 2014-01-13
* *
* Licensed under the MIT license. * Licensed under the MIT license.
* https://github.com/zixaphir/appchan-x/blob/master/LICENSE * https://github.com/zixaphir/appchan-x/blob/master/LICENSE
@ -95,15 +95,17 @@
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
Array.prototype.indexOf = function(val) { Array.prototype.indexOf = function(val, i) {
var i; var len;
i = this.length; i || (i = 0);
while (i--) { len = this.length;
while (i < len) {
if (this[i] === val) { if (this[i] === val) {
return i; return i;
} }
i++;
} }
return i; return -1;
}; };
__indexOf = [].indexOf; __indexOf = [].indexOf;
@ -4587,7 +4589,7 @@
}); });
this.navLinks = $.el('div', { this.navLinks = $.el('div', {
className: 'navLinks', className: 'navLinks',
innerHTML: "<a href=.././ id=returnlink>Return</a> [<a href=javascript:; id=cataloglink>Catalog</a>] <a href=\"#bottom\" id=bottomlink>Bottom</a> <time id=\"index-last-refresh\" title=\"Last index refresh\">...</time> <input type=\"search\" id=\"index-search\" class=\"field\" placeholder=\"Search\"><a id=\"index-search-clear\" class=\"fa\" href=\"javascript:;\">\uf057</a>" innerHTML: "<span class=brackets-wrap id=returnlink><a href=.././>Return</a></span> <span class=brackets-wrap id=cataloglink><a href=javascript:;>Catalog</a></span> <span class=brackets-wrap id=bottomlink><a href=\"#bottom\">Bottom</a></span> <span class=brackets-wrap id=\"index-last-refresh\"><time title=\"Last index refresh\">...</time></span> <input type=\"search\" id=\"index-search\" class=\"field\" placeholder=\"Search\"><a id=\"index-search-clear\" class=\"fa\" href=\"javascript:;\">\uf057</a>"
}); });
this.searchInput = $('#index-search', this.navLinks); this.searchInput = $('#index-search', this.navLinks);
this.currentPage = this.getCurrentPage(); this.currentPage = this.getCurrentPage();
@ -4595,8 +4597,8 @@
$.on(this.pagelist, 'click', this.cb.pageNav); $.on(this.pagelist, 'click', this.cb.pageNav);
$.on(this.searchInput, 'input', this.onSearchInput); $.on(this.searchInput, 'input', this.onSearchInput);
$.on($('#index-search-clear', this.navLinks), 'click', this.clearSearch); $.on($('#index-search-clear', this.navLinks), 'click', this.clearSearch);
$.on($('#returnlink', this.navLinks), 'click', Navigate.navigate); $.on($('#returnlink a', this.navLinks), 'click', Navigate.navigate);
$.on($('#cataloglink', this.navLinks), 'click', function() { $.on($('#cataloglink a', this.navLinks), 'click', function() {
return window.location = "//boards.4chan.org/" + g.BOARD + "/catalog"; return window.location = "//boards.4chan.org/" + g.BOARD + "/catalog";
}); });
if (g.VIEW === 'index') { if (g.VIEW === 'index') {
@ -4872,7 +4874,7 @@
} }
return; return;
} }
timeEl = $('#index-last-refresh', Index.navLinks); timeEl = $('#index-last-refresh time', Index.navLinks);
timeEl.dataset.utc = Date.parse(req.getResponseHeader('Last-Modified')); timeEl.dataset.utc = Date.parse(req.getResponseHeader('Last-Modified'));
RelativeDates.update(timeEl); RelativeDates.update(timeEl);
return Index.scrollToIndex(); return Index.scrollToIndex();
@ -5408,36 +5410,36 @@
}; };
}, },
allQuotelinksLinkingTo: function(post) { allQuotelinksLinkingTo: function(post) {
var ID, quote, quotedPost, quotelinks, quoterPost, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3, _ref4; var ID, handleQuotes, quote, quotedPost, quotelinks, quoterPost, _i, _len, _ref, _ref1, _ref2;
quotelinks = []; quotelinks = [];
handleQuotes = function(post, type) {
var clone, _i, _len, _ref;
quotelinks.push.apply(quotelinks, post.nodes[type]);
_ref = post.clones;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
clone = _ref[_i];
quotelinks.push.apply(quotelinks, clone.nodes[type]);
}
};
_ref = g.posts; _ref = g.posts;
for (ID in _ref) { for (ID in _ref) {
quoterPost = _ref[ID]; quoterPost = _ref[ID];
if (_ref1 = post.fullID, __indexOf.call(quoterPost.quotes, _ref1) >= 0) { if (_ref1 = post.fullID, __indexOf.call(quoterPost.quotes, _ref1) >= 0) {
_ref2 = [quoterPost].concat(quoterPost.clones); handleQuotes(quoterPost, 'quotelinks');
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
quoterPost = _ref2[_i];
quotelinks.push.apply(quotelinks, quoterPost.nodes.quotelinks);
}
} }
} }
if (Conf['Quote Backlinks']) { if (Conf['Quote Backlinks']) {
_ref3 = post.quotes; _ref2 = post.quotes;
for (_j = 0, _len1 = _ref3.length; _j < _len1; _j++) { for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
quote = _ref3[_j]; quote = _ref2[_i];
if (!(quotedPost = g.posts[quote])) { if (quotedPost = g.posts[quote]) {
continue; handleQuotes(quotedPost, 'backlinks');
}
_ref4 = [quotedPost].concat(quotedPost.clones);
for (_k = 0, _len2 = _ref4.length; _k < _len2; _k++) {
quotedPost = _ref4[_k];
quotelinks.push.apply(quotelinks, __slice.call(quotedPost.nodes.backlinks));
} }
} }
} }
return quotelinks.filter(function(quotelink) { return quotelinks.filter(function(quotelink) {
var boardID, postID, _ref5; var boardID, postID, _ref3;
_ref5 = Get.postDataFromLink(quotelink), boardID = _ref5.boardID, postID = _ref5.postID; _ref3 = Get.postDataFromLink(quotelink), boardID = _ref3.boardID, postID = _ref3.postID;
return boardID === post.board.ID && postID === post.ID; return boardID === post.board.ID && postID === post.ID;
}); });
}, },
@ -7104,28 +7106,14 @@
if (g.VIEW === 'catalog' || !Conf['Quote Inlining']) { if (g.VIEW === 'catalog' || !Conf['Quote Inlining']) {
return; return;
} }
if (Conf['Quote Hash Navigation']) { this.process = Conf['Quote Hash Navigation'] ? function(link, clone) {
this.node = function() { if (!clone) {
var link, _i, _len, _ref; $.after(link, QuoteInline.qiQuote(link, $.hasClass(link, 'filtered')));
_ref = this.nodes.quotelinks.concat(__slice.call(this.nodes.backlinks)); }
for (_i = 0, _len = _ref.length; _i < _len; _i++) { return $.on(link, 'click', QuoteInline.toggle);
link = _ref[_i]; } : function(link) {
if (!this.isClone) { return $.on(link, 'click', QuoteInline.toggle);
$.after(link, QuoteInline.qiQuote(link, $.hasClass(link, 'filtered'))); };
}
$.on(link, 'click', QuoteInline.toggle);
}
};
} else {
this.node = function() {
var link, _i, _len, _ref;
_ref = this.nodes.quotelinks.concat(__slice.call(this.nodes.backlinks));
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
link = _ref[_i];
$.on(link, 'click', QuoteInline.toggle);
}
};
}
if (Conf['Comment Expansion']) { if (Conf['Comment Expansion']) {
ExpandComment.callbacks.push(this.node); ExpandComment.callbacks.push(this.node);
} }
@ -7134,14 +7122,27 @@
cb: this.node cb: this.node
}); });
}, },
node: function() {
var isClone, link, process, _i, _j, _len, _len1, _ref, _ref1;
process = QuoteInline.process;
isClone = this.isClone;
_ref = this.nodes.quotelinks;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
link = _ref[_i];
process(link, isClone);
}
_ref1 = this.nodes.backlinks;
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
link = _ref1[_j];
process(link, isClone);
}
},
qiQuote: function(link, hidden) { qiQuote: function(link, hidden) {
return [ return $.el('a', {
$.tn(' '), $.el('a', { className: "hashlink" + (hidden ? ' filtered' : ''),
className: hidden ? 'hashlink filtered' : 'hashlink', textContent: '#',
textContent: '#', href: link.href
href: link.href });
})
];
}, },
toggle: function(e) { toggle: function(e) {
var boardID, context, postID, threadID, _ref; var boardID, context, postID, threadID, _ref;
@ -10889,21 +10890,16 @@
return $.add(this.nodes.info, Menu.makeButton()); return $.add(this.nodes.info, Menu.makeButton());
}, },
makeButton: (function() { makeButton: (function() {
var frag; var a;
frag = null; a = $.el('a', {
className: 'menu-button',
innerHTML: '<i class=fa>\uf107</i>',
href: 'javascript:;'
});
return function() { return function() {
var clone; var clone;
if (frag == null) { clone = a.cloneNode(true);
frag = $.nodes([ $.on(clone, 'click', Menu.toggle);
$.tn(' '), $.el('a', {
className: 'menu-button',
innerHTML: '<i class=fa>\uf107</i>',
href: 'javascript:;'
})
]);
}
clone = frag.cloneNode(true);
$.on(clone.lastElementChild, 'click', Menu.toggle);
return clone; return clone;
}; };
})(), })(),

View File

@ -1,9 +1,11 @@
# I am bad at JavaScript and if you reuse this, so are you. # I am bad at JavaScript and if you reuse this, so are you.
Array::indexOf = (val) -> Array::indexOf = (val, i) ->
i = @length i or= 0
while i-- len = @length
while i < len
return i if @[i] is val return i if @[i] is val
return i i++
return -1
# Update CoffeeScript's reference to [].indexOf # Update CoffeeScript's reference to [].indexOf
# Reserved keywords are ignored in embedded javascript. # Reserved keywords are ignored in embedded javascript.

View File

@ -40,24 +40,22 @@ Get =
allQuotelinksLinkingTo: (post) -> allQuotelinksLinkingTo: (post) ->
# Get quotelinks & backlinks linking to the given post. # Get quotelinks & backlinks linking to the given post.
quotelinks = [] quotelinks = []
handleQuotes = (post, type) ->
quotelinks.push post.nodes[type]...
quotelinks.push clone.nodes[type]... for clone in post.clones
return
# First: # First:
# In every posts, # In every posts,
# if it did quote this post, # if it did quote this post,
# get all their backlinks. # get all their backlinks.
for ID, quoterPost of g.posts handleQuotes quoterPost, 'quotelinks' for ID, quoterPost of g.posts when post.fullID in quoterPost.quotes
if post.fullID in quoterPost.quotes
for quoterPost in [quoterPost].concat quoterPost.clones
quotelinks.push.apply quotelinks, quoterPost.nodes.quotelinks
# Second: # Second:
# If we have quote backlinks: # If we have quote backlinks:
# in all posts this post quoted # in all posts this post quoted
# and their clones, # and their clones,
# get all of their backlinks. # get all of their backlinks.
if Conf['Quote Backlinks'] if Conf['Quote Backlinks']
for quote in post.quotes handleQuotes quotedPost, 'backlinks' for quote in post.quotes when quotedPost = g.posts[quote]
continue unless quotedPost = g.posts[quote]
for quotedPost in [quotedPost].concat quotedPost.clones
quotelinks.push.apply quotelinks, [quotedPost.nodes.backlinks...]
# Third: # Third:
# Filter out irrelevant quotelinks. # Filter out irrelevant quotelinks.
quotelinks.filter (quotelink) -> quotelinks.filter (quotelink) ->

View File

@ -83,9 +83,8 @@ Index =
$.on @pagelist, 'click', @cb.pageNav $.on @pagelist, 'click', @cb.pageNav
$.on @searchInput, 'input', @onSearchInput $.on @searchInput, 'input', @onSearchInput
$.on $('#index-search-clear', @navLinks), 'click', @clearSearch $.on $('#index-search-clear', @navLinks), 'click', @clearSearch
$.on $('#returnlink', @navLinks), 'click', Navigate.navigate $.on $('#returnlink a', @navLinks), 'click', Navigate.navigate
$.on $('#cataloglink', @navLinks), 'click', -> window.location = "//boards.4chan.org/#{g.BOARD}/catalog" $.on $('#cataloglink a', @navLinks), 'click', -> window.location = "//boards.4chan.org/#{g.BOARD}/catalog"
@update() if g.VIEW is 'index' @update() if g.VIEW is 'index'
$.asap (-> $('.board', doc) or d.readyState isnt 'loading'), -> $.asap (-> $('.board', doc) or d.readyState isnt 'loading'), ->
@ -287,7 +286,7 @@ Index =
new Notice 'error', 'Index refresh failed.', 1 new Notice 'error', 'Index refresh failed.', 1
return return
timeEl = $ '#index-last-refresh', Index.navLinks timeEl = $ '#index-last-refresh time', Index.navLinks
timeEl.dataset.utc = Date.parse req.getResponseHeader 'Last-Modified' timeEl.dataset.utc = Date.parse req.getResponseHeader 'Last-Modified'
RelativeDates.update timeEl RelativeDates.update timeEl
Index.scrollToIndex() Index.scrollToIndex()

View File

@ -90,6 +90,11 @@ div.navLinks {
.reply > .file > .fileText { .reply > .file > .fileText {
margin: 0 20px; margin: 0 20px;
} }
.hashlink::before {
content: ' ';
visibility: hidden;
}
.inline + .hashlink,
[hidden] { [hidden] {
display: none !important; display: none !important;
} }
@ -273,12 +278,12 @@ div.center:not(.ad-cnt) {
font-weight: bold; font-weight: bold;
} }
/* 4chan X link brackets */ /* 4chan X link brackets */
.brackets-wrap::after {
content: "]";
}
.brackets-wrap::before { .brackets-wrap::before {
content: "["; content: "[";
} }
.brackets-wrap::after {
content: "]";
}
/* Notifications */ /* Notifications */
#notifications { #notifications {
position: fixed; position: fixed;
@ -508,16 +513,6 @@ div.center:not(.ad-cnt) {
.thread #index-search { .thread #index-search {
display: none; display: none;
} }
#returnlink::before,
#bottomlink::before,
#index-last-refresh::before {
content: '[';
}
#returnlink::after,
#bottomlink::after,
#index-last-refresh::after {
content: ']';
}
/* Announcement Hiding */ /* Announcement Hiding */
:root.hide-announcement #globalMessage { :root.hide-announcement #globalMessage {
@ -1115,7 +1110,7 @@ a:only-of-type > .remove {
.reply .menu-button, .reply .menu-button,
.op .menu-button, .op .menu-button,
#thread-watcher .menu-button { #thread-watcher .menu-button {
margin-left: -5px !important; margin-left: -1px !important;
position: relative; position: relative;
} }
.op .menu-button, .op .menu-button,

View File

@ -1,6 +1,6 @@
<a href=.././ id=returnlink>Return</a> <span class=brackets-wrap id=returnlink><a href=.././>Return</a></span>
[<a href=javascript:; id=cataloglink>Catalog</a>] <span class=brackets-wrap id=cataloglink><a href=javascript:;>Catalog</a></span>
<a href="#bottom" id=bottomlink>Bottom</a> <span class=brackets-wrap id=bottomlink><a href="#bottom">Bottom</a></span>
<time id="index-last-refresh" title="Last index refresh">...</time> <span class=brackets-wrap id="index-last-refresh"><time title="Last index refresh">...</time></span>
<input type="search" id="index-search" class="field" placeholder="Search"> <input type="search" id="index-search" class="field" placeholder="Search">
<a id="index-search-clear" class="fa" href="javascript:;">\uf057</a> <a id="index-search-clear" class="fa" href="javascript:;">\uf057</a>

View File

@ -14,18 +14,13 @@ Menu =
$.add @nodes.info, Menu.makeButton() $.add @nodes.info, Menu.makeButton()
makeButton: do -> makeButton: do ->
frag = null a = $.el 'a',
className: 'menu-button'
innerHTML: '<i class=fa>\uf107</i>'
href: 'javascript:;'
-> ->
unless frag? clone = a.cloneNode true
frag = $.nodes [ $.on clone, 'click', Menu.toggle
$.tn(' ')
$.el 'a',
className: 'menu-button'
innerHTML: '<i class=fa>\uf107</i>'
href: 'javascript:;'
]
clone = frag.cloneNode true
$.on clone.lastElementChild, 'click', Menu.toggle
clone clone
toggle: (e) -> toggle: (e) ->

View File

@ -2,18 +2,14 @@ QuoteInline =
init: -> init: ->
return if g.VIEW is 'catalog' or !Conf['Quote Inlining'] return if g.VIEW is 'catalog' or !Conf['Quote Inlining']
if Conf['Quote Hash Navigation'] @process = if Conf['Quote Hash Navigation']
@node = -> (link, clone) ->
for link in @nodes.quotelinks.concat [@nodes.backlinks...] $.after link, QuoteInline.qiQuote link, $.hasClass link, 'filtered' unless clone
$.after link, QuoteInline.qiQuote link, $.hasClass link, 'filtered' unless @isClone $.on link, 'click', QuoteInline.toggle
$.on link, 'click', QuoteInline.toggle
return
else else
@node = -> (link) ->
for link in @nodes.quotelinks.concat [@nodes.backlinks...] $.on link, 'click', QuoteInline.toggle
$.on link, 'click', QuoteInline.toggle
return
if Conf['Comment Expansion'] if Conf['Comment Expansion']
ExpandComment.callbacks.push @node ExpandComment.callbacks.push @node
@ -22,14 +18,18 @@ QuoteInline =
name: 'Quote Inlining' name: 'Quote Inlining'
cb: @node cb: @node
node: ->
{process} = QuoteInline
{isClone} = @
process link, isClone for link in @nodes.quotelinks
process link, isClone for link in @nodes.backlinks
return
qiQuote: (link, hidden) -> qiQuote: (link, hidden) ->
[ $.el 'a',
$.tn(' ') className: "hashlink#{if hidden then ' filtered' else ''}"
$.el 'a', textContent: '#'
className: if hidden then 'hashlink filtered' else 'hashlink' href: link.href
textContent: '#'
href: link.href
]
toggle: (e) -> toggle: (e) ->
return if e.shiftKey or e.altKey or e.ctrlKey or e.metaKey or e.button isnt 0 return if e.shiftKey or e.altKey or e.ctrlKey or e.metaKey or e.button isnt 0