This commit is contained in:
commit
92714d702c
358
4chan_x.user.js
358
4chan_x.user.js
@ -1,6 +1,6 @@
|
||||
// ==UserScript==
|
||||
// @name 4chan x
|
||||
// @version 2.27.1
|
||||
// @version 2.28.0
|
||||
// @namespace aeosynth
|
||||
// @description Adds various features.
|
||||
// @copyright 2009-2011 James Campos <james.r.campos@gmail.com>
|
||||
@ -20,7 +20,7 @@
|
||||
* Copyright (c) 2009-2011 James Campos <james.r.campos@gmail.com>
|
||||
* Copyright (c) 2012 Nicolas Stepien <stepien.nicolas@gmail.com>
|
||||
* http://mayhemydg.github.com/4chan-x/
|
||||
* 4chan X 2.27.1
|
||||
* 4chan X 2.28.0
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
@ -72,7 +72,7 @@
|
||||
*/
|
||||
|
||||
(function() {
|
||||
var $, $$, DAY, Favicon, FileInfo, HOUR, MINUTE, Main, NAMESPACE, SECOND, Time, VERSION, anonymize, conf, config, d, engine, expandComment, expandThread, filter, flatten, g, getTitle, imgExpand, imgGif, imgHover, key, keybinds, log, nav, options, qr, quoteBacklink, quoteIndicators, quoteInline, quotePreview, redirect, replyHiding, reportButton, revealSpoilers, sauce, strikethroughQuotes, threadHiding, threadStats, threading, titlePost, ui, unread, updater, val, watcher, _base;
|
||||
var $, $$, DAY, Favicon, FileInfo, HOUR, MINUTE, Main, NAMESPACE, SECOND, Time, VERSION, anonymize, conf, config, d, engine, expandComment, expandThread, filter, flatten, g, getTitle, imgExpand, imgGif, imgHover, key, keybinds, log, nav, options, qr, quoteBacklink, quoteDR, quoteInline, quoteOP, quotePreview, redirect, replyHiding, reportButton, revealSpoilers, sauce, strikethroughQuotes, threadHiding, threadStats, threading, titlePost, ui, unread, updater, val, watcher, _base;
|
||||
|
||||
config = {
|
||||
main: {
|
||||
@ -209,7 +209,7 @@
|
||||
|
||||
NAMESPACE = '4chan_x.';
|
||||
|
||||
VERSION = '2.27.1';
|
||||
VERSION = '2.28.0';
|
||||
|
||||
SECOND = 1000;
|
||||
|
||||
@ -575,109 +575,115 @@
|
||||
if (Object.keys(this.filters).length) return g.callbacks.push(this.node);
|
||||
},
|
||||
createFilter: function(regexp, op, hl, top) {
|
||||
return function(root, value, isOP) {
|
||||
var firstThread, thisThread;
|
||||
var test;
|
||||
test = typeof regexp === 'string' ? function(value) {
|
||||
return regexp === value;
|
||||
} : function(value) {
|
||||
return regexp.test(value);
|
||||
};
|
||||
return function(value, isOP) {
|
||||
if (isOP && op === 'no' || !isOP && op === 'only') return false;
|
||||
if (typeof regexp === 'string') {
|
||||
if (regexp !== value) return false;
|
||||
} else if (!regexp.test(value)) {
|
||||
return false;
|
||||
}
|
||||
if (hl) {
|
||||
if (isOP) {
|
||||
$.addClass(root, hl);
|
||||
} else {
|
||||
$.addClass(root.parentNode, hl);
|
||||
}
|
||||
if (isOP && top && !g.REPLY) {
|
||||
thisThread = root.parentNode;
|
||||
if (firstThread = $('div[class=op]')) {
|
||||
$.before(firstThread.parentNode, [thisThread, thisThread.nextElementSibling]);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (isOP) {
|
||||
if (!g.REPLY) threadHiding.hideHide(root.parentNode);
|
||||
} else {
|
||||
replyHiding.hideHide(root);
|
||||
}
|
||||
if (!test(value)) return false;
|
||||
if (hl) return [hl, top];
|
||||
return true;
|
||||
};
|
||||
},
|
||||
node: function(root) {
|
||||
var Filter, isOP, key, klass, value, _i, _len, _ref;
|
||||
klass = root.className;
|
||||
if (/\binlined\b/.test(klass)) return;
|
||||
if (!(isOP = klass === 'op')) root = $('td[id]', root);
|
||||
node: function(post) {
|
||||
var Filter, el, firstThread, isOP, key, result, thisThread, value, _i, _len, _ref;
|
||||
if (post.isInlined) return;
|
||||
isOP = post.isOP, el = post.el;
|
||||
for (key in filter.filters) {
|
||||
value = filter[key](root, isOP);
|
||||
value = filter[key](post);
|
||||
if (value === false) continue;
|
||||
_ref = filter.filters[key];
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
Filter = _ref[_i];
|
||||
if (Filter(root, value, isOP)) return;
|
||||
if (!(result = Filter(value, isOP))) continue;
|
||||
if (result === true) {
|
||||
if (isOP) {
|
||||
if (!g.REPLY) {
|
||||
threadHiding.hideHide(post.el.parentNode);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
replyHiding.hideHide(post.el);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (isOP) {
|
||||
$.addClass(el, result[0]);
|
||||
} else {
|
||||
$.addClass(el.parentNode, result[0]);
|
||||
}
|
||||
if (isOP && result[1] && !g.REPLY) {
|
||||
thisThread = el.parentNode;
|
||||
if (firstThread = $('div[class=op]')) {
|
||||
$.before(firstThread.parentNode, [thisThread, thisThread.nextElementSibling]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
name: function(root, isOP) {
|
||||
name: function(post) {
|
||||
var name;
|
||||
name = isOP ? $('.postername', root) : $('.commentpostername', root);
|
||||
name = post.isOP ? $('.postername', post.el) : $('.commentpostername', post.el);
|
||||
return name.textContent;
|
||||
},
|
||||
tripcode: function(root) {
|
||||
tripcode: function(post) {
|
||||
var trip;
|
||||
if (trip = $('.postertrip', root)) return trip.textContent;
|
||||
if (trip = $('.postertrip', post.el)) return trip.textContent;
|
||||
return false;
|
||||
},
|
||||
mod: function(root, isOP) {
|
||||
mod: function(post) {
|
||||
var mod;
|
||||
if (mod = (isOP ? $('.commentpostername', root) : $('.commentpostername ~ .commentpostername', root))) {
|
||||
if (mod = (post.isOP ? $('.commentpostername', post.el) : $('.commentpostername ~ .commentpostername', post.el))) {
|
||||
return mod.textContent;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
email: function(root) {
|
||||
email: function(post) {
|
||||
var mail;
|
||||
if (mail = $('.linkmail', root)) return mail.href;
|
||||
if (mail = $('.linkmail', post.el)) return mail.href;
|
||||
return false;
|
||||
},
|
||||
subject: function(root, isOP) {
|
||||
subject: function(post) {
|
||||
var sub;
|
||||
sub = isOP ? $('.filetitle', root) : $('.replytitle', root);
|
||||
sub = post.isOP ? $('.filetitle', post.el) : $('.replytitle', post.el);
|
||||
return sub.textContent;
|
||||
},
|
||||
comment: function(root) {
|
||||
var data, i, len, nodes, text;
|
||||
comment: function(post) {
|
||||
var data, i, nodes, text, _ref;
|
||||
text = [];
|
||||
nodes = d.evaluate('.//br|.//text()', root.lastChild, null, 7, null);
|
||||
i = 0;
|
||||
len = nodes.snapshotLength;
|
||||
while (i < len) {
|
||||
text.push((data = nodes.snapshotItem(i++).data) ? data : '\n');
|
||||
nodes = d.evaluate('.//br|.//text()', post.el.lastChild, null, 7, null);
|
||||
for (i = 0, _ref = nodes.snapshotLength; 0 <= _ref ? i < _ref : i > _ref; 0 <= _ref ? i++ : i--) {
|
||||
text.push((data = nodes.snapshotItem(i).data) ? data : '\n');
|
||||
}
|
||||
return text.join('');
|
||||
},
|
||||
filename: function(root) {
|
||||
filename: function(post) {
|
||||
var file;
|
||||
if (file = $('.filesize > span', root)) return file.title;
|
||||
if (file = $('span', post.filesize)) return file.title;
|
||||
return false;
|
||||
},
|
||||
dimensions: function(root) {
|
||||
var match, span;
|
||||
if ((span = $('.filesize', root)) && (match = span.textContent.match(/\d+x\d+/))) {
|
||||
dimensions: function(post) {
|
||||
var filesize, match;
|
||||
filesize = post.filesize;
|
||||
if (filesize && (match = filesize.textContent.match(/\d+x\d+/))) {
|
||||
return match[0];
|
||||
}
|
||||
return false;
|
||||
},
|
||||
filesize: function(root) {
|
||||
filesize: function(post) {
|
||||
var img;
|
||||
if (img = $('img[md5]', root)) return img.alt;
|
||||
img = post.img;
|
||||
if (img) return img.alt;
|
||||
return false;
|
||||
},
|
||||
md5: function(root) {
|
||||
md5: function(post) {
|
||||
var img;
|
||||
if (img = $('img[md5]', root)) return img.getAttribute('md5');
|
||||
img = post.img;
|
||||
if (img) return img.getAttribute('md5');
|
||||
return false;
|
||||
}
|
||||
};
|
||||
@ -686,15 +692,15 @@
|
||||
init: function() {
|
||||
return g.callbacks.push(this.node);
|
||||
},
|
||||
node: function(root) {
|
||||
node: function(post) {
|
||||
var el, quote, _i, _len, _ref;
|
||||
if (root.className === 'inline') return;
|
||||
_ref = $$('.quotelink', root);
|
||||
if (post.isInlined) return;
|
||||
_ref = post.quotes;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
quote = _ref[_i];
|
||||
if ((el = $.id(quote.hash.slice(1))) && el.parentNode.parentNode.parentNode.hidden) {
|
||||
$.addClass(quote, 'filtered');
|
||||
if (conf['Recursive Filtering']) root.hidden = true;
|
||||
if (conf['Recursive Filtering']) post.root.hidden = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -753,7 +759,8 @@
|
||||
$.replace(a.parentNode.parentNode, bq);
|
||||
if (conf['Quote Preview']) quotePreview.node(bq);
|
||||
if (conf['Quote Inline']) quoteInline.node(bq);
|
||||
return quoteIndicators.node(bq);
|
||||
if (conf['Indicate OP quote']) quoteOP.node(bq);
|
||||
if (conf['Indicate Cross-thread Quotes']) return quoteDR.node(bq);
|
||||
}
|
||||
};
|
||||
|
||||
@ -866,9 +873,10 @@
|
||||
init: function() {
|
||||
return g.callbacks.push(this.node);
|
||||
},
|
||||
node: function(root) {
|
||||
node: function(post) {
|
||||
var a, dd, id, reply;
|
||||
if (!(dd = $('.doubledash', root))) return;
|
||||
if (post["class"]) return;
|
||||
dd = $('.doubledash', post.root);
|
||||
dd.className = 'replyhider';
|
||||
a = $.el('a', {
|
||||
textContent: '[ - ]',
|
||||
@ -1257,7 +1265,7 @@
|
||||
|
||||
qr = {
|
||||
init: function() {
|
||||
var form, iframe, link, loadChecking, script;
|
||||
var form, iframe, link, loadChecking;
|
||||
if (!$.id('recaptcha_challenge_field_holder')) return;
|
||||
if (conf['Hide Original Post Form']) {
|
||||
link = $.el('h1', {
|
||||
@ -1298,11 +1306,14 @@
|
||||
});
|
||||
$.add(d.head, iframe);
|
||||
}
|
||||
script = $.el('script', {
|
||||
textContent: 'Recaptcha.focus_response_field=function(){}'
|
||||
setTimeout(function() {
|
||||
var script;
|
||||
script = $.el('script', {
|
||||
textContent: 'Recaptcha.focus_response_field=function(){}'
|
||||
});
|
||||
$.add(d.head, script);
|
||||
return $.rm(script);
|
||||
});
|
||||
$.add(d.head, script);
|
||||
$.rm(script);
|
||||
if (conf['Persistent QR']) {
|
||||
qr.dialog();
|
||||
if (conf['Auto Hide QR']) qr.hide();
|
||||
@ -1312,8 +1323,8 @@
|
||||
$.on(d, 'dragstart', qr.drag);
|
||||
return $.on(d, 'dragend', qr.drag);
|
||||
},
|
||||
node: function(root) {
|
||||
return $.on($('.quotejs + .quotejs', root), 'click', qr.quote);
|
||||
node: function(post) {
|
||||
return $.on($('.quotejs + .quotejs', post.el), 'click', qr.quote);
|
||||
},
|
||||
open: function() {
|
||||
if (qr.el) {
|
||||
@ -2745,11 +2756,11 @@
|
||||
init: function() {
|
||||
return g.callbacks.push(this.node);
|
||||
},
|
||||
node: function(root) {
|
||||
node: function(post) {
|
||||
var name, trip;
|
||||
name = $('.commentpostername, .postername', root);
|
||||
name = $('.commentpostername, .postername', post.el);
|
||||
name.textContent = 'Anonymous';
|
||||
if (trip = $('.postertrip', root)) {
|
||||
if (trip = $('.postertrip', post.el)) {
|
||||
if (trip.parentNode.nodeName === 'A') {
|
||||
return $.rm(trip.parentNode);
|
||||
} else {
|
||||
@ -2797,17 +2808,18 @@
|
||||
});
|
||||
};
|
||||
},
|
||||
node: function(root) {
|
||||
var img, link, nodes, span, _i, _len, _ref;
|
||||
if (root.className === 'inline' || !(span = $('.filesize', root))) return;
|
||||
img = span.nextElementSibling.nextElementSibling;
|
||||
node: function(post) {
|
||||
var img, link, nodes, _i, _len, _ref;
|
||||
img = post.img;
|
||||
if (post["class"] === 'inline' || !img) return;
|
||||
img = img.parentNode;
|
||||
nodes = [];
|
||||
_ref = sauce.links;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
link = _ref[_i];
|
||||
nodes.push($.tn(' '), link(img));
|
||||
}
|
||||
return $.add(span, nodes);
|
||||
return $.add(post.filesize, nodes);
|
||||
}
|
||||
};
|
||||
|
||||
@ -2815,11 +2827,12 @@
|
||||
init: function() {
|
||||
return g.callbacks.push(this.node);
|
||||
},
|
||||
node: function(root) {
|
||||
node: function(post) {
|
||||
var img;
|
||||
if (!(img = $('img[alt^=Spoil]', root)) || root.className === 'inline') {
|
||||
return;
|
||||
}
|
||||
img = {
|
||||
post: post
|
||||
};
|
||||
if (!(img && /^Spoil/.test(img.alt)) || post["class"] === 'inline') return;
|
||||
img.removeAttribute('height');
|
||||
img.removeAttribute('width');
|
||||
return img.src = "http://thumbs.4chan.org" + (img.parentNode.pathname.replace(/src(\/\d+).+$/, 'thumb$1s.jpg'));
|
||||
@ -2844,10 +2857,10 @@
|
||||
};
|
||||
return g.callbacks.push(this.node);
|
||||
},
|
||||
node: function(root) {
|
||||
node: function(post) {
|
||||
var node, time;
|
||||
if (root.className === 'inline') return;
|
||||
node = $('.posttime', root) || $('span[id]', root).previousSibling;
|
||||
if (post["class"] === 'inline') return;
|
||||
node = $('.posttime', post.el) || $('span[id]', post.el).previousSibling;
|
||||
Time.date = Time.parse(node);
|
||||
time = $.el('time', {
|
||||
textContent: ' ' + Time.funk(Time) + ' '
|
||||
@ -2937,9 +2950,9 @@
|
||||
this.setFormats();
|
||||
return g.callbacks.push(this.node);
|
||||
},
|
||||
node: function(root) {
|
||||
node: function(post) {
|
||||
var fullname, link, node, regexp, resolution, shortname, size, type, unit, _, _ref;
|
||||
if (root.className === 'inline' || !(node = $('.filesize', root))) return;
|
||||
if (post["class"] === 'inline' || !(node = post.filesize)) return;
|
||||
type = node.childElementCount === 2 ? 0 : 1;
|
||||
regexp = type ? /^File: (<.+>)-\((?:Spoiler Image, )?([\d\.]+) (\w+), (\d+x\d+|PDF)/ : /^File: (<.+>)-\((?:Spoiler Image, )?([\d\.]+) (\w+), (\d+x\d+|PDF), <span title="(.+)">([^<]+)/;
|
||||
_ref = node.innerHTML.match(regexp), _ = _ref[0], link = _ref[1], size = _ref[2], unit = _ref[3], resolution = _ref[4], fullname = _ref[5], shortname = _ref[6];
|
||||
@ -3059,20 +3072,19 @@
|
||||
quoteBacklink.funk = Function('id', "return '" + format + "'");
|
||||
return g.callbacks.push(this.node);
|
||||
},
|
||||
node: function(root) {
|
||||
var a, container, el, id, link, qid, quote, quotes, _i, _len, _ref;
|
||||
if (/\binline\b/.test(root.className)) return;
|
||||
node: function(post) {
|
||||
var a, container, el, link, qid, quote, quotes, root, _i, _len, _ref;
|
||||
if (post.isInlined) return;
|
||||
quotes = {};
|
||||
_ref = $$('.quotelink', root);
|
||||
_ref = post.quotes;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
quote = _ref[_i];
|
||||
if (qid = quote.hash.slice(1)) quotes[qid] = true;
|
||||
}
|
||||
id = $('input', root).name;
|
||||
a = $.el('a', {
|
||||
href: "#" + id,
|
||||
className: root.hidden ? 'filtered backlink' : 'backlink',
|
||||
textContent: quoteBacklink.funk(id)
|
||||
href: "#" + post.id,
|
||||
className: post.root.hidden ? 'filtered backlink' : 'backlink',
|
||||
textContent: quoteBacklink.funk(post.id)
|
||||
});
|
||||
for (qid in quotes) {
|
||||
if (!(el = $.id(qid)) || el.className === 'op' && !conf['OP Backlinks']) {
|
||||
@ -3085,10 +3097,12 @@
|
||||
container = $.el('span', {
|
||||
className: 'container'
|
||||
});
|
||||
$.add(container, [$.tn(' '), link]);
|
||||
root = $('.reportbutton', el) || $('span[id]', el);
|
||||
$.after(root, container);
|
||||
} else {
|
||||
$.add(container, [$.tn(' '), link]);
|
||||
}
|
||||
$.add(container, [$.tn(' '), link]);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -3097,9 +3111,9 @@
|
||||
init: function() {
|
||||
return g.callbacks.push(this.node);
|
||||
},
|
||||
node: function(root) {
|
||||
node: function(post) {
|
||||
var quote, _i, _len, _ref;
|
||||
_ref = $$('.quotelink, .backlink', root);
|
||||
_ref = post.quotes.concat(post.backlinks);
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
quote = _ref[_i];
|
||||
if (!quote.hash) continue;
|
||||
@ -3223,9 +3237,9 @@
|
||||
init: function() {
|
||||
return g.callbacks.push(this.node);
|
||||
},
|
||||
node: function(root) {
|
||||
node: function(post) {
|
||||
var quote, _i, _len, _ref;
|
||||
_ref = $$('.quotelink, .backlink', root);
|
||||
_ref = post.quotes.concat(post.backlinks);
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
quote = _ref[_i];
|
||||
if (quote.hash) $.on(quote, 'mouseover', quotePreview.mouseover);
|
||||
@ -3301,24 +3315,36 @@
|
||||
}
|
||||
};
|
||||
|
||||
quoteIndicators = {
|
||||
quoteOP = {
|
||||
init: function() {
|
||||
return g.callbacks.push(this.node);
|
||||
},
|
||||
node: function(root) {
|
||||
var hash, path, quote, tid, _i, _len, _ref;
|
||||
if (root.className === 'inline') return;
|
||||
tid = g.THREAD_ID || $.x('ancestor::div[contains(@class,"thread")]', root).firstChild.id;
|
||||
_ref = $$('.quotelink', root);
|
||||
node: function(post) {
|
||||
var quote, _i, _len, _ref;
|
||||
if (post["class"] === 'inline') return;
|
||||
_ref = post.quotes;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
quote = _ref[_i];
|
||||
if (!(hash = quote.hash.slice(1))) continue;
|
||||
if (conf['Indicate OP quote'] && hash === tid) {
|
||||
if (quote.hash.slice(1) === post.threadId) {
|
||||
$.add(quote, $.tn('\u00A0(OP)'));
|
||||
continue;
|
||||
}
|
||||
path = quote.pathname;
|
||||
if (conf['Indicate Cross-thread Quotes'] && path.lastIndexOf("/" + tid) === -1 && path.indexOf("/" + g.BOARD + "/") === 0) {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
quoteDR = {
|
||||
init: function() {
|
||||
return g.callbacks.push(this.node);
|
||||
},
|
||||
node: function(post) {
|
||||
var path, quote, _i, _len, _ref;
|
||||
if (post["class"] === 'inline') return;
|
||||
_ref = post.quotes;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
quote = _ref[_i];
|
||||
if (!quote.hash) continue;
|
||||
path = quote.pathname.split('/');
|
||||
if (path[1] === g.BOARD && path[3] !== post.threadId) {
|
||||
$.add(quote, $.tn('\u00A0(Cross-thread)'));
|
||||
}
|
||||
}
|
||||
@ -3329,16 +3355,15 @@
|
||||
init: function() {
|
||||
return g.callbacks.push(this.node);
|
||||
},
|
||||
node: function(root) {
|
||||
var a, span;
|
||||
if (!(a = $('.reportbutton', root))) {
|
||||
span = $('span[id]', root);
|
||||
node: function(post) {
|
||||
var a;
|
||||
if (!(a = $('.reportbutton', post.el))) {
|
||||
a = $.el('a', {
|
||||
className: 'reportbutton',
|
||||
innerHTML: '[ ! ]',
|
||||
href: 'javascript:;'
|
||||
});
|
||||
$.after(span, [$.tn(' '), a]);
|
||||
$.after($('span[id]', post.el), [$.tn(' '), a]);
|
||||
}
|
||||
return $.on(a, 'click', reportButton.report);
|
||||
},
|
||||
@ -3372,11 +3397,11 @@
|
||||
})();
|
||||
return g.callbacks.push(this.node);
|
||||
},
|
||||
node: function(root) {
|
||||
node: function(post) {
|
||||
var imgcount;
|
||||
if (/\binline\b/.test(root.className)) return;
|
||||
if (post.isInlined) return;
|
||||
$.id('postcount').textContent = ++threadStats.posts;
|
||||
if (!$('img[md5]', root)) return;
|
||||
if (!post.img) return;
|
||||
imgcount = $.id('imagecount');
|
||||
imgcount.textContent = ++threadStats.images;
|
||||
if (threadStats.images > threadStats.imgLimit) {
|
||||
@ -3393,9 +3418,9 @@
|
||||
return g.callbacks.push(this.node);
|
||||
},
|
||||
replies: [],
|
||||
node: function(root) {
|
||||
if (root.hidden || root.className) return;
|
||||
unread.replies.push(root);
|
||||
node: function(post) {
|
||||
if (post.root.hidden || post["class"]) return;
|
||||
unread.replies.push(post.root);
|
||||
return unread.update();
|
||||
},
|
||||
scroll: function() {
|
||||
@ -3530,10 +3555,9 @@
|
||||
init: function() {
|
||||
return g.callbacks.push(this.node);
|
||||
},
|
||||
node: function(root) {
|
||||
var thumb;
|
||||
if (!(thumb = $('img[md5]', root))) return;
|
||||
return $.on(thumb, 'mouseover', imgHover.mouseover);
|
||||
node: function(post) {
|
||||
if (!post.img) return;
|
||||
return $.on(post.img, 'mouseover', imgHover.mouseover);
|
||||
},
|
||||
mouseover: function() {
|
||||
ui.el = $.el('img', {
|
||||
@ -3565,14 +3589,14 @@
|
||||
init: function() {
|
||||
return g.callbacks.push(this.node);
|
||||
},
|
||||
node: function(root) {
|
||||
var img, src, thumb;
|
||||
if (root.hidden || !(thumb = $('img[md5]', root))) return;
|
||||
src = thumb.parentNode.href;
|
||||
node: function(post) {
|
||||
var img, src;
|
||||
if (post.root.hidden || !post.img) return;
|
||||
src = post.img.parentNode.href;
|
||||
if (/gif$/.test(src) && !/spoiler/.test(src)) {
|
||||
img = $.el('img');
|
||||
$.on(img, 'load', function() {
|
||||
return thumb.src = src;
|
||||
return post.img.src = src;
|
||||
});
|
||||
return img.src = src;
|
||||
}
|
||||
@ -3584,12 +3608,12 @@
|
||||
g.callbacks.push(this.node);
|
||||
return imgExpand.dialog();
|
||||
},
|
||||
node: function(root) {
|
||||
var a, thumb;
|
||||
if (!(thumb = $('img[md5]', root))) return;
|
||||
a = thumb.parentNode;
|
||||
node: function(post) {
|
||||
var a;
|
||||
if (!post.img) return;
|
||||
a = post.img.parentNode;
|
||||
$.on(a, 'click', imgExpand.cb.toggle);
|
||||
if (imgExpand.on && !root.hidden && root.className !== 'inline') {
|
||||
if (imgExpand.on && !post.root.hidden && post["class"] !== 'inline') {
|
||||
return imgExpand.expand(a.firstChild);
|
||||
}
|
||||
},
|
||||
@ -3814,20 +3838,24 @@
|
||||
if (conf['Quote Inline']) quoteInline.init();
|
||||
if (conf['Quote Preview']) quotePreview.init();
|
||||
if (conf['Quote Backlinks']) quoteBacklink.init();
|
||||
if (conf['Indicate OP quote'] || conf['Indicate Cross-thread Quotes']) {
|
||||
quoteIndicators.init();
|
||||
}
|
||||
if (conf['Indicate OP quote']) quoteOP.init();
|
||||
if (conf['Indicate Cross-thread Quotes']) quoteDR.init();
|
||||
return $.ready(Main.ready);
|
||||
},
|
||||
ready: function() {
|
||||
var MutationObserver, form, nodes, observer;
|
||||
var MutationObserver, form, nav, nodes, observer, _i, _len, _ref;
|
||||
if (d.title === '4chan - 404') {
|
||||
redirect.init();
|
||||
return;
|
||||
}
|
||||
if (!$.id('navtopr')) return;
|
||||
$.addClass(d.body, "chanx_" + (VERSION.match(/\.(\d+)/)[1]));
|
||||
$.addClass(d.body, "chanx_" + (VERSION.split('.')[1]));
|
||||
$.addClass(d.body, engine);
|
||||
_ref = ['navtop', 'navbot'];
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
nav = _ref[_i];
|
||||
$.addClass($("a[href$='/" + g.BOARD + "/']", $.id(nav)), 'current');
|
||||
}
|
||||
form = $('form[name=delform]');
|
||||
threading.thread(form.firstElementChild);
|
||||
Favicon.init();
|
||||
@ -3880,17 +3908,37 @@
|
||||
}
|
||||
},
|
||||
node: function(nodes, notify) {
|
||||
var callback, node, _i, _j, _len, _len2, _ref;
|
||||
var callback, klass, node, post, posts, _i, _j, _k, _len, _len2, _len3, _ref;
|
||||
posts = [];
|
||||
for (_i = 0, _len = nodes.length; _i < _len; _i++) {
|
||||
node = nodes[_i];
|
||||
klass = node.className;
|
||||
posts.push({
|
||||
root: node,
|
||||
el: klass === 'op' ? node : $('td[id]', node),
|
||||
"class": klass,
|
||||
id: $('input', node).name,
|
||||
threadId: g.THREAD_ID || $.x('ancestor::div[contains(@class,"thread")]', node).firstChild.id,
|
||||
isOP: klass === 'op',
|
||||
isInlined: /\binline\b/.test(klass),
|
||||
filesize: $('.filesize', node),
|
||||
img: $('img[md5]', node),
|
||||
quotes: $$('.quotelink', node),
|
||||
backlinks: $$('.backlink', node)
|
||||
});
|
||||
}
|
||||
_ref = g.callbacks;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
callback = _ref[_i];
|
||||
for (_j = 0, _len2 = _ref.length; _j < _len2; _j++) {
|
||||
callback = _ref[_j];
|
||||
try {
|
||||
for (_j = 0, _len2 = nodes.length; _j < _len2; _j++) {
|
||||
node = nodes[_j];
|
||||
callback(node);
|
||||
for (_k = 0, _len3 = posts.length; _k < _len3; _k++) {
|
||||
post = posts[_k];
|
||||
callback(post);
|
||||
}
|
||||
} catch (err) {
|
||||
if (notify) alert(err.message);
|
||||
if (notify) {
|
||||
alert("4chan X error: " + err.message + "\nhttp://mayhemydg.github.com/4chan-x/#bug-report\n\n" + err.stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
2
Cakefile
2
Cakefile
@ -2,7 +2,7 @@
|
||||
{exec} = require 'child_process'
|
||||
fs = require 'fs'
|
||||
|
||||
VERSION = '2.27.1'
|
||||
VERSION = '2.28.0'
|
||||
|
||||
HEADER = """
|
||||
// ==UserScript==
|
||||
|
||||
@ -1,4 +1,8 @@
|
||||
master
|
||||
- Mayhem
|
||||
General performance improvements.
|
||||
|
||||
2.28.0
|
||||
- ahodesuka
|
||||
Reply/Thread File Info Formatting:
|
||||
- Link: %l, %L (Original file names are shown inside threads).
|
||||
@ -6,7 +10,7 @@ master
|
||||
- Resolution/PDF: %r
|
||||
- Original filename: %n, %N.
|
||||
- noface
|
||||
Update imagelimit for mlp.
|
||||
Update imagelimit for /mlp/.
|
||||
Fix stubs if poster has unique ID.
|
||||
- Mayhem
|
||||
You can now filter or highlight admin/mod posts.
|
||||
|
||||
@ -1 +1 @@
|
||||
postMessage({version:'2.27.1'},'*');
|
||||
postMessage({version:'2.28.0'},'*');
|
||||
|
||||
313
script.coffee
313
script.coffee
@ -161,7 +161,7 @@ conf = {}
|
||||
) null, config
|
||||
|
||||
NAMESPACE = '4chan_x.'
|
||||
VERSION = '2.27.1'
|
||||
VERSION = '2.28.0'
|
||||
SECOND = 1000
|
||||
MINUTE = 60*SECOND
|
||||
HOUR = 60*MINUTE
|
||||
@ -499,102 +499,110 @@ filter =
|
||||
g.callbacks.push @node
|
||||
|
||||
createFilter: (regexp, op, hl, top) ->
|
||||
(root, value, isOP) ->
|
||||
if isOP and op is 'no' or !isOP and op is 'only'
|
||||
return false
|
||||
test =
|
||||
if typeof regexp is 'string'
|
||||
# MD5 checking
|
||||
if regexp isnt value
|
||||
return false
|
||||
else unless regexp.test value
|
||||
(value) -> regexp is value
|
||||
else
|
||||
(value) -> regexp.test value
|
||||
(value, isOP) ->
|
||||
if isOP and op is 'no' or !isOP and op is 'only'
|
||||
return false
|
||||
unless test value
|
||||
return false
|
||||
if hl
|
||||
if isOP
|
||||
$.addClass root, hl
|
||||
else
|
||||
$.addClass root.parentNode, hl
|
||||
if isOP and top and not g.REPLY
|
||||
# Put the highlighted OPs' threads on top of the board pages...
|
||||
thisThread = root.parentNode
|
||||
# ...before the first non highlighted thread.
|
||||
if firstThread = $ 'div[class=op]'
|
||||
$.before firstThread.parentNode, [thisThread, thisThread.nextElementSibling]
|
||||
# Continue the filter lookup to add more classes or hide it.
|
||||
return false
|
||||
if isOP
|
||||
unless g.REPLY
|
||||
threadHiding.hideHide root.parentNode
|
||||
else
|
||||
replyHiding.hideHide root
|
||||
return [hl, top]
|
||||
true
|
||||
|
||||
node: (root) ->
|
||||
klass = root.className
|
||||
return if /\binlined\b/.test klass
|
||||
unless isOP = klass is 'op'
|
||||
root = $ 'td[id]', root
|
||||
node: (post) ->
|
||||
return if post.isInlined
|
||||
{isOP, el} = post
|
||||
for key of filter.filters
|
||||
value = filter[key] root, isOP
|
||||
value = filter[key] post
|
||||
if value is false
|
||||
# Continue if there's nothing to filter (no tripcode for example).
|
||||
continue
|
||||
for Filter in filter.filters[key]
|
||||
if Filter root, value, isOP
|
||||
unless result = Filter value, isOP
|
||||
continue
|
||||
|
||||
# Hide
|
||||
if result is true
|
||||
if isOP
|
||||
unless g.REPLY
|
||||
threadHiding.hideHide post.el.parentNode
|
||||
else
|
||||
continue
|
||||
else
|
||||
replyHiding.hideHide post.el
|
||||
return
|
||||
|
||||
name: (root, isOP) ->
|
||||
name = if isOP then $ '.postername', root else $ '.commentpostername', root
|
||||
# Highlight
|
||||
if isOP
|
||||
$.addClass el, result[0]
|
||||
else
|
||||
$.addClass el.parentNode, result[0]
|
||||
if isOP and result[1] and not g.REPLY
|
||||
# Put the highlighted OPs' threads on top of the board pages...
|
||||
thisThread = el.parentNode
|
||||
# ...before the first non highlighted thread.
|
||||
if firstThread = $ 'div[class=op]'
|
||||
$.before firstThread.parentNode, [thisThread, thisThread.nextElementSibling]
|
||||
|
||||
name: (post) ->
|
||||
name = if post.isOP then $ '.postername', post.el else $ '.commentpostername', post.el
|
||||
name.textContent
|
||||
tripcode: (root) ->
|
||||
if trip = $ '.postertrip', root
|
||||
tripcode: (post) ->
|
||||
if trip = $ '.postertrip', post.el
|
||||
return trip.textContent
|
||||
false
|
||||
mod: (root, isOP) ->
|
||||
if mod = (if isOP then $ '.commentpostername', root else $ '.commentpostername ~ .commentpostername', root)
|
||||
mod: (post) ->
|
||||
if mod = (if post.isOP then $ '.commentpostername', post.el else $ '.commentpostername ~ .commentpostername', post.el)
|
||||
return mod.textContent
|
||||
false
|
||||
email: (root) ->
|
||||
if mail = $ '.linkmail', root
|
||||
email: (post) ->
|
||||
if mail = $ '.linkmail', post.el
|
||||
return mail.href
|
||||
false
|
||||
subject: (root, isOP) ->
|
||||
sub = if isOP then $ '.filetitle', root else $ '.replytitle', root
|
||||
subject: (post) ->
|
||||
sub = if post.isOP then $ '.filetitle', post.el else $ '.replytitle', post.el
|
||||
sub.textContent
|
||||
comment: (root) ->
|
||||
comment: (post) ->
|
||||
text = []
|
||||
# XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE is 7
|
||||
nodes = d.evaluate './/br|.//text()', root.lastChild, null, 7, null
|
||||
i = 0
|
||||
len = nodes.snapshotLength
|
||||
while i < len
|
||||
text.push if data = nodes.snapshotItem(i++).data then data else '\n'
|
||||
nodes = d.evaluate './/br|.//text()', post.el.lastChild, null, 7, null
|
||||
for i in [0...nodes.snapshotLength]
|
||||
text.push if data = nodes.snapshotItem(i).data then data else '\n'
|
||||
text.join ''
|
||||
filename: (root) ->
|
||||
if file = $ '.filesize > span', root
|
||||
filename: (post) ->
|
||||
if file = $ 'span', post.filesize
|
||||
return file.title
|
||||
false
|
||||
dimensions: (root) ->
|
||||
if (span = $ '.filesize', root) and match = span.textContent.match /\d+x\d+/
|
||||
dimensions: (post) ->
|
||||
{filesize} = post
|
||||
if filesize and match = filesize.textContent.match /\d+x\d+/
|
||||
return match[0]
|
||||
return false
|
||||
filesize: (root) ->
|
||||
if img = $ 'img[md5]', root
|
||||
filesize: (post) ->
|
||||
{img} = post
|
||||
if img
|
||||
return img.alt
|
||||
false
|
||||
md5: (root) ->
|
||||
if img = $ 'img[md5]', root
|
||||
md5: (post) ->
|
||||
{img} = post
|
||||
if img
|
||||
return img.getAttribute 'md5'
|
||||
false
|
||||
|
||||
strikethroughQuotes =
|
||||
init: ->
|
||||
g.callbacks.push @node
|
||||
node: (root) ->
|
||||
return if root.className is 'inline'
|
||||
for quote in $$ '.quotelink', root
|
||||
node: (post) ->
|
||||
return if post.isInlined
|
||||
for quote in post.quotes
|
||||
if (el = $.id quote.hash[1..]) and el.parentNode.parentNode.parentNode.hidden
|
||||
$.addClass quote, 'filtered'
|
||||
root.hidden = true if conf['Recursive Filtering']
|
||||
post.root.hidden = true if conf['Recursive Filtering']
|
||||
return
|
||||
|
||||
expandComment =
|
||||
@ -633,7 +641,10 @@ expandComment =
|
||||
quotePreview.node bq
|
||||
if conf['Quote Inline']
|
||||
quoteInline.node bq
|
||||
quoteIndicators.node bq
|
||||
if conf['Indicate OP quote']
|
||||
quoteOP.node bq
|
||||
if conf['Indicate Cross-thread Quotes']
|
||||
quoteDR.node bq
|
||||
|
||||
expandThread =
|
||||
init: ->
|
||||
@ -711,8 +722,9 @@ replyHiding =
|
||||
init: ->
|
||||
g.callbacks.push @node
|
||||
|
||||
node: (root) ->
|
||||
return unless dd = $ '.doubledash', root
|
||||
node: (post) ->
|
||||
return if post.class
|
||||
dd = $ '.doubledash', post.root
|
||||
dd.className = 'replyhider'
|
||||
a = $.el 'a',
|
||||
textContent: '[ - ]'
|
||||
@ -755,8 +767,8 @@ replyHiding =
|
||||
|
||||
if conf['Show Stubs']
|
||||
name = $('.commentpostername', reply).textContent
|
||||
uid = $('.posteruid', reply)?.textContent or ''
|
||||
trip = $('.postertrip', reply)?.textContent or ''
|
||||
uid = $('.posteruid', reply)?.textContent or ''
|
||||
trip = $('.postertrip', reply)?.textContent or ''
|
||||
|
||||
div = $.el 'div',
|
||||
className: 'stub'
|
||||
@ -1024,10 +1036,12 @@ qr =
|
||||
$.on iframe, 'load', -> if @src isnt 'about:blank' then setTimeout loadChecking, 500, @
|
||||
$.add d.head, iframe
|
||||
|
||||
# Prevent original captcha input from being focused on reload.
|
||||
script = $.el 'script', textContent: 'Recaptcha.focus_response_field=function(){}'
|
||||
$.add d.head, script
|
||||
$.rm script
|
||||
# This is extemely slow, execute is asynchronously.
|
||||
setTimeout ->
|
||||
# Prevent original captcha input from being focused on reload.
|
||||
script = $.el 'script', textContent: 'Recaptcha.focus_response_field=function(){}'
|
||||
$.add d.head, script
|
||||
$.rm script
|
||||
|
||||
if conf['Persistent QR']
|
||||
qr.dialog()
|
||||
@ -1037,8 +1051,8 @@ qr =
|
||||
$.on d, 'dragstart', qr.drag
|
||||
$.on d, 'dragend', qr.drag
|
||||
|
||||
node: (root) ->
|
||||
$.on $('.quotejs + .quotejs', root), 'click', qr.quote
|
||||
node: (post) ->
|
||||
$.on $('.quotejs + .quotejs', post.el), 'click', qr.quote
|
||||
|
||||
open: ->
|
||||
if qr.el
|
||||
@ -2264,10 +2278,10 @@ watcher =
|
||||
anonymize =
|
||||
init: ->
|
||||
g.callbacks.push @node
|
||||
node: (root) ->
|
||||
name = $ '.commentpostername, .postername', root
|
||||
node: (post) ->
|
||||
name = $ '.commentpostername, .postername', post.el
|
||||
name.textContent = 'Anonymous'
|
||||
if trip = $ '.postertrip', root
|
||||
if trip = $ '.postertrip', post.el
|
||||
if trip.parentNode.nodeName is 'A'
|
||||
$.rm trip.parentNode
|
||||
else
|
||||
@ -2302,19 +2316,22 @@ sauce =
|
||||
target: '_blank'
|
||||
textContent: domain
|
||||
|
||||
node: (root) ->
|
||||
return if root.className is 'inline' or not span = $ '.filesize', root
|
||||
img = span.nextElementSibling.nextElementSibling
|
||||
node: (post) ->
|
||||
{img} = post
|
||||
return if post.class is 'inline' or not img
|
||||
img = img.parentNode
|
||||
nodes = []
|
||||
for link in sauce.links
|
||||
nodes.push $.tn(' '), link img
|
||||
$.add span, nodes
|
||||
$.add post.filesize, nodes
|
||||
|
||||
revealSpoilers =
|
||||
init: ->
|
||||
g.callbacks.push @node
|
||||
node: (root) ->
|
||||
return if not (img = $ 'img[alt^=Spoil]', root) or root.className is 'inline'
|
||||
node: (post) ->
|
||||
img = {post}
|
||||
if not (img and /^Spoil/.test img.alt) or post.class is 'inline'
|
||||
return
|
||||
img.removeAttribute 'height'
|
||||
img.removeAttribute 'width'
|
||||
img.src = "http://thumbs.4chan.org#{img.parentNode.pathname.replace(/src(\/\d+).+$/, 'thumb$1s.jpg')}"
|
||||
@ -2341,10 +2358,10 @@ Time =
|
||||
new Date year, month, day, hour, min
|
||||
|
||||
g.callbacks.push @node
|
||||
node: (root) ->
|
||||
return if root.className is 'inline'
|
||||
node: (post) ->
|
||||
return if post.class is 'inline'
|
||||
# .posttime exists on every board except /f/
|
||||
node = $('.posttime', root) or $('span[id]', root).previousSibling
|
||||
node = $('.posttime', post.el) or $('span[id]', post.el).previousSibling
|
||||
Time.date = Time.parse node
|
||||
time = $.el 'time',
|
||||
textContent: ' ' + Time.funk(Time) + ' '
|
||||
@ -2402,8 +2419,8 @@ FileInfo =
|
||||
return if g.BOARD is 'f'
|
||||
@setFormats()
|
||||
g.callbacks.push @node
|
||||
node: (root) ->
|
||||
return if root.className is 'inline' or not node = $ '.filesize', root
|
||||
node: (post) ->
|
||||
return if post.class is 'inline' or not node = post.filesize
|
||||
type = if node.childElementCount is 2 then 0 else 1
|
||||
regexp =
|
||||
if type
|
||||
@ -2484,20 +2501,18 @@ quoteBacklink =
|
||||
format = conf['backlink'].replace /%id/g, "' + id + '"
|
||||
quoteBacklink.funk = Function 'id', "return '#{format}'"
|
||||
g.callbacks.push @node
|
||||
node: (root) ->
|
||||
return if /\binline\b/.test root.className
|
||||
node: (post) ->
|
||||
return if post.isInlined
|
||||
quotes = {}
|
||||
for quote in $$ '.quotelink', root
|
||||
for quote in post.quotes
|
||||
# Don't process >>>/b/.
|
||||
if qid = quote.hash[1..]
|
||||
# Duplicate quotes get overwritten.
|
||||
quotes[qid] = true
|
||||
# OP or reply id.
|
||||
id = $('input', root).name
|
||||
a = $.el 'a',
|
||||
href: "##{id}"
|
||||
className: if root.hidden then 'filtered backlink' else 'backlink'
|
||||
textContent: quoteBacklink.funk id
|
||||
href: "##{post.id}"
|
||||
className: if post.root.hidden then 'filtered backlink' else 'backlink'
|
||||
textContent: quoteBacklink.funk post.id
|
||||
for qid of quotes
|
||||
# Don't backlink the OP.
|
||||
continue if !(el = $.id qid) or el.className is 'op' and !conf['OP Backlinks']
|
||||
@ -2508,16 +2523,18 @@ quoteBacklink =
|
||||
$.on link, 'click', quoteInline.toggle
|
||||
unless (container = $ '.container', el) and container.parentNode is el
|
||||
container = $.el 'span', className: 'container'
|
||||
$.add container, [$.tn(' '), link]
|
||||
root = $('.reportbutton', el) or $('span[id]', el)
|
||||
$.after root, container
|
||||
$.add container, [$.tn(' '), link]
|
||||
else
|
||||
$.add container, [$.tn(' '), link]
|
||||
return
|
||||
|
||||
quoteInline =
|
||||
init: ->
|
||||
g.callbacks.push @node
|
||||
node: (root) ->
|
||||
for quote in $$ '.quotelink, .backlink', root
|
||||
node: (post) ->
|
||||
for quote in post.quotes.concat post.backlinks
|
||||
continue unless quote.hash
|
||||
quote.removeAttribute 'onclick'
|
||||
$.on quote, 'click', quoteInline.toggle
|
||||
@ -2608,8 +2625,8 @@ quoteInline =
|
||||
quotePreview =
|
||||
init: ->
|
||||
g.callbacks.push @node
|
||||
node: (root) ->
|
||||
for quote in $$ '.quotelink, .backlink', root
|
||||
node: (post) ->
|
||||
for quote in post.quotes.concat post.backlinks
|
||||
$.on quote, 'mouseover', quotePreview.mouseover if quote.hash
|
||||
return
|
||||
mouseover: (e) ->
|
||||
@ -2668,23 +2685,29 @@ quotePreview =
|
||||
if conf['File Info Formatting']
|
||||
FileInfo.node qp
|
||||
|
||||
quoteIndicators =
|
||||
quoteOP =
|
||||
init: ->
|
||||
g.callbacks.push @node
|
||||
node: (root) ->
|
||||
return if root.className is 'inline'
|
||||
# We use contains() so that it works with hidden threads
|
||||
tid = g.THREAD_ID or $.x('ancestor::div[contains(@class,"thread")]', root).firstChild.id
|
||||
for quote in $$ '.quotelink', root
|
||||
unless hash = quote.hash[1..]
|
||||
continue
|
||||
if conf['Indicate OP quote'] and hash is tid
|
||||
node: (post) ->
|
||||
return if post.class is 'inline'
|
||||
for quote in post.quotes
|
||||
if quote.hash[1..] is post.threadId
|
||||
# \u00A0 is nbsp
|
||||
$.add quote, $.tn '\u00A0(OP)'
|
||||
return
|
||||
|
||||
quoteDR =
|
||||
init: ->
|
||||
g.callbacks.push @node
|
||||
node: (post) ->
|
||||
return if post.class is 'inline'
|
||||
for quote in post.quotes
|
||||
unless quote.hash
|
||||
# Make sure this isn't a link to the board we're on.
|
||||
continue
|
||||
path = quote.pathname
|
||||
#if quote leads to a different thread id and is located on the same board (index 0)
|
||||
if conf['Indicate Cross-thread Quotes'] and path.lastIndexOf("/#{tid}") is -1 and path.indexOf("/#{g.BOARD}/") is 0
|
||||
path = quote.pathname.split '/'
|
||||
# If quote leads to a different thread id and is located on the same board.
|
||||
if path[1] is g.BOARD and path[3] isnt post.threadId
|
||||
# \u00A0 is nbsp
|
||||
$.add quote, $.tn '\u00A0(Cross-thread)'
|
||||
return
|
||||
@ -2692,14 +2715,13 @@ quoteIndicators =
|
||||
reportButton =
|
||||
init: ->
|
||||
g.callbacks.push @node
|
||||
node: (root) ->
|
||||
if not a = $ '.reportbutton', root
|
||||
span = $ 'span[id]', root
|
||||
node: (post) ->
|
||||
if not a = $ '.reportbutton', post.el
|
||||
a = $.el 'a',
|
||||
className: 'reportbutton'
|
||||
innerHTML: '[ ! ]'
|
||||
href: 'javascript:;'
|
||||
$.after span, [$.tn(' '), a]
|
||||
$.after $('span[id]', post.el), [$.tn(' '), a]
|
||||
$.on a, 'click', reportButton.report
|
||||
report: ->
|
||||
url = "http://sys.4chan.org/#{g.BOARD}/imgboard.php?mode=report&no=#{$.x('preceding-sibling::input', @).name}"
|
||||
@ -2722,10 +2744,10 @@ threadStats =
|
||||
else
|
||||
151
|
||||
g.callbacks.push @node
|
||||
node: (root) ->
|
||||
return if /\binline\b/.test root.className
|
||||
node: (post) ->
|
||||
return if post.isInlined
|
||||
$.id('postcount').textContent = ++threadStats.posts
|
||||
return unless $ 'img[md5]', root
|
||||
return unless post.img
|
||||
imgcount = $.id 'imagecount'
|
||||
imgcount.textContent = ++threadStats.images
|
||||
if threadStats.images > threadStats.imgLimit
|
||||
@ -2740,9 +2762,9 @@ unread =
|
||||
|
||||
replies: []
|
||||
|
||||
node: (root) ->
|
||||
return if root.hidden or root.className
|
||||
unread.replies.push root
|
||||
node: (post) ->
|
||||
return if post.root.hidden or post.class
|
||||
unread.replies.push post.root
|
||||
unread.update()
|
||||
|
||||
scroll: ->
|
||||
@ -2848,9 +2870,9 @@ redirect =
|
||||
imgHover =
|
||||
init: ->
|
||||
g.callbacks.push @node
|
||||
node: (root) ->
|
||||
return unless thumb = $ 'img[md5]', root
|
||||
$.on thumb, 'mouseover', imgHover.mouseover
|
||||
node: (post) ->
|
||||
return unless post.img
|
||||
$.on post.img, 'mouseover', imgHover.mouseover
|
||||
mouseover: ->
|
||||
ui.el = $.el 'img'
|
||||
id: 'ihover'
|
||||
@ -2874,14 +2896,14 @@ imgHover =
|
||||
imgGif =
|
||||
init: ->
|
||||
g.callbacks.push @node
|
||||
node: (root) ->
|
||||
return if root.hidden or !thumb = $ 'img[md5]', root
|
||||
src = thumb.parentNode.href
|
||||
node: (post) ->
|
||||
return if post.root.hidden or not post.img
|
||||
src = post.img.parentNode.href
|
||||
if /gif$/.test(src) and !/spoiler/.test src
|
||||
img = $.el 'img'
|
||||
$.on img, 'load', ->
|
||||
# Replace the thumbnail once the GIF has finished loading.
|
||||
thumb.src = src
|
||||
post.img.src = src
|
||||
img.src = src
|
||||
|
||||
imgExpand =
|
||||
@ -2889,11 +2911,11 @@ imgExpand =
|
||||
g.callbacks.push @node
|
||||
imgExpand.dialog()
|
||||
|
||||
node: (root) ->
|
||||
return unless thumb = $ 'img[md5]', root
|
||||
a = thumb.parentNode
|
||||
node: (post) ->
|
||||
return unless post.img
|
||||
a = post.img.parentNode
|
||||
$.on a, 'click', imgExpand.cb.toggle
|
||||
if imgExpand.on and !root.hidden and root.className isnt 'inline'
|
||||
if imgExpand.on and !post.root.hidden and post.class isnt 'inline'
|
||||
imgExpand.expand a.firstChild
|
||||
cb:
|
||||
toggle: (e) ->
|
||||
@ -3103,8 +3125,11 @@ Main =
|
||||
if conf['Quote Backlinks']
|
||||
quoteBacklink.init()
|
||||
|
||||
if conf['Indicate OP quote'] or conf['Indicate Cross-thread Quotes']
|
||||
quoteIndicators.init()
|
||||
if conf['Indicate OP quote']
|
||||
quoteOP.init()
|
||||
|
||||
if conf['Indicate Cross-thread Quotes']
|
||||
quoteDR.init()
|
||||
|
||||
$.ready Main.ready
|
||||
|
||||
@ -3114,8 +3139,10 @@ Main =
|
||||
return
|
||||
if not $.id 'navtopr'
|
||||
return
|
||||
$.addClass d.body, "chanx_#{VERSION.match(/\.(\d+)/)[1]}"
|
||||
$.addClass d.body, "chanx_#{VERSION.split('.')[1]}"
|
||||
$.addClass d.body, engine
|
||||
for nav in ['navtop', 'navbot']
|
||||
$.addClass $("a[href$='/#{g.BOARD}/']", $.id nav), 'current'
|
||||
form = $ 'form[name=delform]'
|
||||
threading.thread form.firstElementChild
|
||||
Favicon.init()
|
||||
@ -3162,7 +3189,6 @@ Main =
|
||||
if conf['Index Navigation']
|
||||
nav.init()
|
||||
|
||||
|
||||
nodes = $$ '.op, a + table', form
|
||||
Main.node nodes, true
|
||||
|
||||
@ -3191,11 +3217,26 @@ Main =
|
||||
window.location = "https://raw.github.com/mayhemydg/4chan-x/#{version}/4chan_x.user.js"
|
||||
|
||||
node: (nodes, notify) ->
|
||||
posts = []
|
||||
for node in nodes
|
||||
klass = node.className
|
||||
posts.push
|
||||
root: node
|
||||
el: if klass is 'op' then node else $ 'td[id]', node
|
||||
class: klass
|
||||
id: $('input', node).name
|
||||
threadId: g.THREAD_ID or $.x('ancestor::div[contains(@class,"thread")]', node).firstChild.id
|
||||
isOP: klass is 'op'
|
||||
isInlined: /\binline\b/.test klass
|
||||
filesize: $ '.filesize', node
|
||||
img: $ 'img[md5]', node
|
||||
quotes: $$ '.quotelink', node
|
||||
backlinks: $$ '.backlink', node
|
||||
for callback in g.callbacks
|
||||
try
|
||||
callback node for node in nodes
|
||||
callback post for post in posts
|
||||
catch err
|
||||
alert err.message if notify
|
||||
alert "4chan X error: #{err.message}\nhttp://mayhemydg.github.com/4chan-x/#bug-report\n\n#{err.stack}" if notify
|
||||
return
|
||||
observer: (mutations) ->
|
||||
nodes = []
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user