Merge Mayhem X

This commit is contained in:
seaweedchan 2013-06-14 03:14:54 -07:00
commit af21ad07bc
22 changed files with 478 additions and 473 deletions

View File

@ -1,5 +1,5 @@
/* /*
* 4chan X - Version 1.2.13 - 2013-05-29 * 4chan X - Version 1.2.13 - 2013-06-14
* *
* Licensed under the MIT license. * Licensed under the MIT license.
* https://github.com/seaweedchan/4chan-x/blob/master/LICENSE * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE

View File

@ -19,7 +19,7 @@
// @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwAgMAAAAqbBEUAAAACVBMVEUAAGcAAABmzDNZt9VtAAAAAXRSTlMAQObYZgAAAHFJREFUKFOt0LENACEIBdBv4Qju4wgWanEj3D6OcIVMKaitYHEU/jwTCQj8W75kiVCSBvdQ5/AvfVHBin11BgdRq3ysBgfwBDRrj3MCIA+oAQaku/Q1cNctrAmyDl577tOThYt/Y1RBM4DgOHzM0HFTAyLukH/cmRnqAAAAAElFTkSuQmCC // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwAgMAAAAqbBEUAAAACVBMVEUAAGcAAABmzDNZt9VtAAAAAXRSTlMAQObYZgAAAHFJREFUKFOt0LENACEIBdBv4Qju4wgWanEj3D6OcIVMKaitYHEU/jwTCQj8W75kiVCSBvdQ5/AvfVHBin11BgdRq3ysBgfwBDRrj3MCIA+oAQaku/Q1cNctrAmyDl577tOThYt/Y1RBM4DgOHzM0HFTAyLukH/cmRnqAAAAAElFTkSuQmCC
// ==/UserScript== // ==/UserScript==
/* /*
* 4chan X - Version 1.2.13 - 2013-05-29 * 4chan X - Version 1.2.13 - 2013-06-14
* *
* Licensed under the MIT license. * Licensed under the MIT license.
* https://github.com/seaweedchan/4chan-x/blob/master/LICENSE * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE
@ -113,7 +113,8 @@
__slice = [].slice, __slice = [].slice,
__hasProp = {}.hasOwnProperty, __hasProp = {}.hasOwnProperty,
__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); }; },
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
Config = { Config = {
main: { main: {
@ -292,6 +293,7 @@
'Open thread tab': ['Shift+o', 'Open thread in new tab.'], 'Open thread tab': ['Shift+o', 'Open thread in new tab.'],
'Next reply': ['j', 'Select next reply.'], 'Next reply': ['j', 'Select next reply.'],
'Previous reply': ['k', 'Select previous reply.'], 'Previous reply': ['k', 'Select previous reply.'],
'Deselect reply': ['Shift+d', 'Deselect reply.'],
'Hide': ['x', 'Hide thread.'] 'Hide': ['x', 'Hide thread.']
}, },
updater: { updater: {
@ -1286,9 +1288,9 @@
var boardID, postID, threadID, val, _base, _base1, _base2; var boardID, postID, threadID, val, _base, _base1, _base2;
boardID = _arg.boardID, threadID = _arg.threadID, postID = _arg.postID, val = _arg.val; boardID = _arg.boardID, threadID = _arg.threadID, postID = _arg.postID, val = _arg.val;
if (postID) { if (postID !== void 0) {
((_base = ((_base1 = this.data.boards)[boardID] || (_base1[boardID] = {})))[threadID] || (_base[threadID] = {}))[postID] = val; ((_base = ((_base1 = this.data.boards)[boardID] || (_base1[boardID] = {})))[threadID] || (_base[threadID] = {}))[postID] = val;
} else if (threadID) { } else if (threadID !== void 0) {
((_base2 = this.data.boards)[boardID] || (_base2[boardID] = {}))[threadID] = val; ((_base2 = this.data.boards)[boardID] || (_base2[boardID] = {}))[threadID] = val;
} else { } else {
this.data.boards[boardID] = val; this.data.boards[boardID] = val;
@ -1326,13 +1328,9 @@
_ref = this.data.boards; _ref = this.data.boards;
for (boardID in _ref) { for (boardID in _ref) {
val = _ref[boardID]; val = _ref[boardID];
if (typeof this.data.boards[boardID] !== 'object') { this.deleteIfEmpty({
delete this.data.boards[boardID]; boardID: boardID
} else { });
this.deleteIfEmpty({
boardID: boardID
});
}
} }
now = Date.now(); now = Date.now();
if ((this.data.lastChecked || 0) < now - 2 * $.HOUR) { if ((this.data.lastChecked || 0) < now - 2 * $.HOUR) {
@ -1779,7 +1777,7 @@
hashScroll: function() { hashScroll: function() {
var hash, post; var hash, post;
if (!((hash = this.location.hash) && (post = $.id(hash.slice(1))))) { if (!((hash = this.location.hash.slice(1)) && (post = $.id(hash)))) {
return; return;
} }
if ((Get.postFromRoot(post)).isHidden) { if ((Get.postFromRoot(post)).isHidden) {
@ -1877,7 +1875,7 @@
@license: https://github.com/4chan/4chan-JS/blob/master/LICENSE @license: https://github.com/4chan/4chan-JS/blob/master/LICENSE
*/ */
var a, boardID, capcode, capcodeClass, capcodeStart, closed, comment, container, date, dateUTC, email, emailEnd, emailStart, ext, file, fileDims, fileHTML, fileHtml, fileInfo, fileSize, fileThumb, filename, flag, flagCode, flagName, href, imgSrc, isClosed, isOP, isSticky, name, postID, quote, shortFilename, spoilerRange, staticPath, sticky, subject, threadID, tripcode, uniqueID, userID, _i, _len, _ref; var a, boardID, capcode, capcodeClass, capcodeStart, closed, comment, container, date, dateUTC, email, emailEnd, emailStart, ext, file, fileDims, fileHTML, fileInfo, fileSize, fileThumb, filename, flag, flagCode, flagName, href, imgSrc, isClosed, isOP, isSticky, name, postID, quote, shortFilename, spoilerRange, staticPath, sticky, subject, threadID, tripcode, uniqueID, userID, _i, _len, _ref;
postID = o.postID, threadID = o.threadID, boardID = o.boardID, name = o.name, capcode = o.capcode, tripcode = o.tripcode, uniqueID = o.uniqueID, email = o.email, subject = o.subject, flagCode = o.flagCode, flagName = o.flagName, date = o.date, dateUTC = o.dateUTC, isSticky = o.isSticky, isClosed = o.isClosed, comment = o.comment, file = o.file; postID = o.postID, threadID = o.threadID, boardID = o.boardID, name = o.name, capcode = o.capcode, tripcode = o.tripcode, uniqueID = o.uniqueID, email = o.email, subject = o.subject, flagCode = o.flagCode, flagName = o.flagName, date = o.date, dateUTC = o.dateUTC, isSticky = o.isSticky, isClosed = o.isClosed, comment = o.comment, file = o.file;
isOP = postID === threadID; isOP = postID === threadID;
@ -1915,7 +1913,7 @@
} }
flag = flagCode ? (" <img src='" + staticPath + "country/" + (boardID === 'pol' ? 'troll/' : '')) + flagCode.toLowerCase() + (".gif' alt=" + flagCode + " title='" + flagName + "' class=countryFlag>") : ''; flag = flagCode ? (" <img src='" + staticPath + "country/" + (boardID === 'pol' ? 'troll/' : '')) + flagCode.toLowerCase() + (".gif' alt=" + flagCode + " title='" + flagName + "' class=countryFlag>") : '';
if (file != null ? file.isDeleted : void 0) { if (file != null ? file.isDeleted : void 0) {
fileHtml = isOP ? ("<div class=file id=f" + postID + "><div class=fileInfo></div><span class=fileThumb>") + ("<img src='" + staticPath + "filedeleted.gif' alt='File deleted.' class=fileDeletedRes>") + "</span></div>" : ("<div class=file id=f" + postID + "><span class=fileThumb>") + ("<img src='" + staticPath + "filedeleted-res.gif' alt='File deleted.' class=fileDeletedRes>") + "</span></div>"; fileHTML = isOP ? ("<div class=file id=f" + postID + "><div class=fileInfo></div><span class=fileThumb>") + ("<img src='" + staticPath + "filedeleted.gif' alt='File deleted.' class=fileDeletedRes>") + "</span></div>" : ("<div class=file id=f" + postID + "><span class=fileThumb>") + ("<img src='" + staticPath + "filedeleted-res.gif' alt='File deleted.' class=fileDeletedRes>") + "</span></div>";
} else if (file) { } else if (file) {
ext = file.name.slice(-3); ext = file.name.slice(-3);
if (!file.twidth && !file.theight && ext === 'gif') { if (!file.twidth && !file.theight && ext === 'gif') {
@ -2170,9 +2168,9 @@
case '[/b]': case '[/b]':
return '</b>'; return '</b>';
case '[spoiler]': case '[spoiler]':
return '<span class=spoiler>'; return '<s>';
case '[/spoiler]': case '[/spoiler]':
return '</span>'; return '</s>';
case '[code]': case '[code]':
return '<pre class=prettyprint>'; return '<pre class=prettyprint>';
case '[/code]': case '[/code]':
@ -2704,10 +2702,6 @@
} }
for (key in Config.filter) { for (key in Config.filter) {
this.filters[key] = []; this.filters[key] = [];
if (Conf[key] === void 0) {
$["delete"](key);
continue;
}
_ref = Conf[key].split('\n'); _ref = Conf[key].split('\n');
for (_i = 0, _len = _ref.length; _i < _len; _i++) { for (_i = 0, _len = _ref.length; _i < _len; _i++) {
filter = _ref[_i]; filter = _ref[_i];
@ -5602,7 +5596,7 @@
}), this.ready.bind(this)); }), this.ready.bind(this));
}, },
ready: function() { ready: function() {
var MutationObserver, imgContainer, input, observer, setLifetime, var imgContainer, input, observer, setLifetime,
_this = this; _this = this;
setLifetime = function(e) { setLifetime = function(e) {
@ -5628,7 +5622,7 @@
img: imgContainer.firstChild, img: imgContainer.firstChild,
input: input input: input
}; };
if (MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.OMutationObserver) { if (window.MutationObserver) {
observer = new MutationObserver(this.load.bind(this)); observer = new MutationObserver(this.load.bind(this));
observer.observe(this.nodes.challenge, { observer.observe(this.nodes.challenge, {
childList: true childList: true
@ -7054,7 +7048,12 @@
return (thread.fileLimit && !thread.isSticky ? $.addClass : $.rmClass)(fileCountEl, 'warning'); return (thread.fileLimit && !thread.isSticky ? $.addClass : $.rmClass)(fileCountEl, 'warning');
}, },
fetchPage: function() { fetchPage: function() {
if (ThreadStats.thread.isDead || !Conf["Page Count in Stats"]) { if (!Conf["Page Count in Stats"]) {
return;
}
if (ThreadStats.thread.isDead) {
ThreadStats.pageCountEl.textContent = 'Dead';
$.addClass(ThreadStats.pageCountEl, 'warning');
return; return;
} }
setTimeout(ThreadStats.fetchPage, 2 * $.MINUTE); setTimeout(ThreadStats.fetchPage, 2 * $.MINUTE);
@ -7395,7 +7394,7 @@
return $.after(root, [$.tn(' '), icon]); return $.after(root, [$.tn(' '), icon]);
}, },
parse: function(postObjects) { parse: function(postObjects) {
var ID, OP, count, deletedFiles, deletedPosts, files, index, key, node, num, post, postObject, posts, root, scroll, _i, _len, _ref; var ID, OP, count, deletedFiles, deletedPosts, files, index, length, node, num, post, postObject, posts, scroll, sendEvent, threadID, _i, _len, _ref;
OP = postObjects[0]; OP = postObjects[0];
Build.spoilerRange[ThreadUpdater.thread.board] = OP.custom_spoiler; Build.spoilerRange[ThreadUpdater.thread.board] = OP.custom_spoiler;
@ -7436,67 +7435,53 @@
post.kill(true); post.kill(true);
deletedFiles.push(post); deletedFiles.push(post);
} }
if (ThreadUpdater.postID && ThreadUpdater.postID === ID) {
ThreadUpdater.foundPost = true;
}
} }
sendEvent = function() {
return $.event('ThreadUpdate', {
404: false,
thread: ThreadUpdater.thread,
newPosts: posts,
deletedPosts: deletedPosts,
deletedFiles: deletedFiles,
postCount: OP.replies + 1,
fileCount: OP.images + (!!ThreadUpdater.thread.OP.file && !ThreadUpdater.thread.OP.file.isDead)
});
};
if (!count) { if (!count) {
ThreadUpdater.set('status', null, null); ThreadUpdater.set('status', null, null);
ThreadUpdater.outdateCount++; ThreadUpdater.outdateCount++;
} else { sendEvent();
ThreadUpdater.set('status', "+" + count, 'new'); return;
ThreadUpdater.outdateCount = 0; }
if (Conf['Beep'] && d.hidden && Unread.posts && !Unread.posts.length) { ThreadUpdater.set('status', "+" + count, 'new');
if (!ThreadUpdater.audio) { ThreadUpdater.outdateCount = 0;
ThreadUpdater.audio = $.el('audio', { if (Conf['Beep'] && d.hidden && Unread.posts && !Unread.posts.length) {
src: ThreadUpdater.beep if (!ThreadUpdater.audio) {
}); ThreadUpdater.audio = $.el('audio', {
} src: ThreadUpdater.beep
ThreadUpdater.audio.play(); });
} }
ThreadUpdater.lastPost = posts[count - 1].ID; ThreadUpdater.audio.play();
Main.callbackNodes(Post, posts); }
scroll = Conf['Auto Scroll'] && ThreadUpdater.scrollBG() && ThreadUpdater.root.getBoundingClientRect().bottom - doc.clientHeight < 25; ThreadUpdater.lastPost = posts[count - 1].ID;
for (key in posts) { Main.callbackNodes(Post, posts);
post = posts[key]; scroll = Conf['Auto Scroll'] && ThreadUpdater.scrollBG() && ThreadUpdater.root.getBoundingClientRect().bottom - doc.clientHeight < 25;
if (!posts.hasOwnProperty(key)) { $.add(ThreadUpdater.root, nodes);
continue; sendEvent();
} if (scroll) {
root = post.nodes.root; if (Conf['Bottom Scroll']) {
if (post.cb) { doc.scrollTop = d.body.clientHeight;
if (!post.cb.call(post)) { } else {
$.add(ThreadUpdater.root, root); Header.scrollToPost(nodes[0]);
} }
} else { }
$.add(ThreadUpdater.root, root); threadID = ThreadUpdater.thread.ID;
} length = $$('.thread > .postContainer', ThreadUpdater.root).length;
} if (Conf['Enable 4chan\'s Extension']) {
if (scroll) { return $.globalEval("Parser.parseThread(" + threadID + ", " + (-count) + ")");
if (Conf['Bottom Scroll']) { } else {
doc.scrollTop = d.body.clientHeight; return Fourchan.parseThread(threadID, length - count, length);
} else {
if (root) {
Header.scrollToPost(root);
}
}
}
$.queueTask(function() {
var length, threadID;
threadID = ThreadUpdater.thread.ID;
length = $$('.thread > .postContainer', ThreadUpdater.root).length;
return Fourchan.parseThread(threadID, length - count, length);
});
} }
return $.event('ThreadUpdate', {
404: false,
thread: ThreadUpdater.thread,
newPosts: posts,
deletedPosts: deletedPosts,
deletedFiles: deletedFiles,
postCount: OP.replies + 1,
fileCount: OP.images + (!!ThreadUpdater.thread.OP.file && !ThreadUpdater.thread.OP.file.isDead)
});
} }
}; };
@ -7708,7 +7693,7 @@
} }
}, },
scroll: function() { scroll: function() {
var hash, post, posts, prevID, root; var checkPosition, hash, onload, post, posts, prevID, root;
if ((hash = location.hash.match(/\d+/)) && hash[0] in Unread.thread.posts) { if ((hash = location.hash.match(/\d+/)) && hash[0] in Unread.thread.posts) {
return; return;
@ -7725,11 +7710,27 @@
break; break;
} }
} }
root.scrollIntoView(false); onload = function() {
return; if (checkPosition(root)) {
return root.scrollIntoView(false);
}
};
} else {
posts = Object.keys(Unread.thread.posts);
root = Unread.thread.posts[posts[posts.length - 1]].nodes.root;
onload = function() {
if (checkPosition(root)) {
return Header.scrollToPost(root);
}
};
} }
posts = Object.keys(Unread.thread.posts); checkPosition = function(target) {
return Header.scrollToPost(Unread.thread.posts[posts[posts.length - 1]].nodes.root); var height, top, _ref;
_ref = target.getBoundingClientRect(), top = _ref.top, height = _ref.height;
return top + height - doc.clientHeight > 0;
};
return $.on(window, 'load', onload);
}, },
sync: function() { sync: function() {
var lastReadPost; var lastReadPost;
@ -7748,11 +7749,11 @@
Unread.setLine(); Unread.setLine();
return Unread.update(); return Unread.update();
}, },
addPosts: function(newPosts) { addPosts: function(posts) {
var ID, data, post, _i, _len; var ID, data, post, _i, _len, _ref;
for (_i = 0, _len = newPosts.length; _i < _len; _i++) { for (_i = 0, _len = posts.length; _i < _len; _i++) {
post = newPosts[_i]; post = posts[_i];
ID = post.ID; ID = post.ID;
if (ID <= Unread.lastReadPost || post.isHidden) { if (ID <= Unread.lastReadPost || post.isHidden) {
continue; continue;
@ -7771,7 +7772,7 @@
Unread.addPostQuotingYou(post); Unread.addPostQuotingYou(post);
} }
if (Conf['Unread Line']) { if (Conf['Unread Line']) {
Unread.setLine(newPosts.contains(Unread.posts[0])); Unread.setLine((_ref = Unread.posts[0], __indexOf.call(posts, _ref) >= 0));
} }
Unread.read(); Unread.read();
return Unread.update(); return Unread.update();
@ -8022,8 +8023,8 @@
'http': true, 'http': true,
'https': true, 'https': true,
'software': 'fuuka', 'software': 'fuuka',
'boards': ['3', 'cgl', 'ck', 'fa', 'ic', 'jp', 'lit', 'q', 's4s', 'tg', 'vr'], 'boards': ['3', 'cgl', 'ck', 'fa', 'ic', 'jp', 'lit', 'q', 'tg', 'vr'],
'files': ['3', 'cgl', 'ck', 'fa', 'ic', 'jp', 'lit', 'q', 's4s', 'vr'] 'files': ['3', 'cgl', 'ck', 'fa', 'ic', 'jp', 'lit', 'q', 'vr']
} }
}, },
to: function(dest, data) { to: function(dest, data) {
@ -8093,7 +8094,7 @@
return $.on(d, '4chanXInitFinished', this.setup); return $.on(d, '4chanXInitFinished', this.setup);
}, },
setup: function() { setup: function() {
var btn, entry, items, psa; var btn, entry, psa;
$.off(d, '4chanXInitFinished', PSAHiding.setup); $.off(d, '4chanXInitFinished', PSAHiding.setup);
if (!(psa = $.id('globalMessage'))) { if (!(psa = $.id('globalMessage'))) {
@ -8121,21 +8122,10 @@
href: 'javascript:;' href: 'javascript:;'
}); });
$.on(btn, 'click', PSAHiding.toggle); $.on(btn, 'click', PSAHiding.toggle);
items = { $.get('hiddenPSA', 0, function(_arg) {
hiddenPSA: 0, var hiddenPSA;
hiddenPSAs: null
};
$.get(items, function(_arg) {
var hiddenPSA, hiddenPSAs;
hiddenPSA = _arg.hiddenPSA, hiddenPSAs = _arg.hiddenPSAs; hiddenPSA = _arg.hiddenPSA;
if (hiddenPSAs) {
$["delete"]('hiddenPSAs');
if (hiddenPSAs.contains(psa.textContent.replace(/\W+/g, '').toLowerCase())) {
hiddenPSA = +$.id('globalMessage').dataset.utc;
$.set('hiddenPSA', hiddenPSA);
}
}
PSAHiding.sync(hiddenPSA); PSAHiding.sync(hiddenPSA);
$.before(psa, btn); $.before(psa, btn);
return $.rmClass(doc, 'hide-announcement'); return $.rmClass(doc, 'hide-announcement');
@ -8256,7 +8246,7 @@
var rgb; var rgb;
rgb = IDColor.ids[this] || IDColor.compute(this); rgb = IDColor.ids[this] || IDColor.compute(this);
return ("background-color: rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + "); color: ") + (rgb[3] ? "black;" : "white; border-radius: 3px; padding: 0px 2px;"); return ("background-color: rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + "); color: ") + (rgb[3] ? "black; border-radius: 3px; padding: 0px 2px;" : "white; border-radius: 3px; padding: 0px 2px;");
}, },
hash: function(str) { hash: function(str) {
var i, j, msg; var i, j, msg;
@ -8708,7 +8698,7 @@
} }
board = g.BOARD.ID; board = g.BOARD.ID;
if (board === 'g') { if (board === 'g') {
$.globalEval("window.addEventListener('prettyprint', function(e) {\n var pre = e.detail;\n pre.innerHTML = prettyPrintOne(pre.innerHTML.replace(/\\s/g, '&nbsp;'));\n}, false);"); $.globalEval("window.addEventListener('prettyprint', function(e) {\n var pre = e.detail;\n pre.innerHTML = prettyPrintOne(pre.innerHTML);\n}, false);");
Post.prototype.callbacks.push({ Post.prototype.callbacks.push({
name: 'Parse /g/ code', name: 'Parse /g/ code',
cb: this.code cb: this.code
@ -8932,6 +8922,9 @@
case Conf['Previous reply']: case Conf['Previous reply']:
Keybinds.hl(-1, threadRoot); Keybinds.hl(-1, threadRoot);
break; break;
case Conf['Deselect reply']:
Keybinds.hl(0, threadRoot);
break;
case Conf['Hide']: case Conf['Hide']:
if (g.VIEW === 'index') { if (g.VIEW === 'index') {
ThreadHiding.toggle(thread); ThreadHiding.toggle(thread);
@ -9042,6 +9035,12 @@
hl: function(delta, thread) { hl: function(delta, thread) {
var axe, headRect, next, postEl, rect, replies, reply, root, topMargin, _i, _len; var axe, headRect, next, postEl, rect, replies, reply, root, topMargin, _i, _len;
if (!delta) {
if (postEl = $('.reply.highlight', thread)) {
$.rmClass(postEl, 'highlight');
}
return;
}
if (Conf['Fixed Header'] && Conf['Bottom header']) { if (Conf['Fixed Header'] && Conf['Bottom header']) {
topMargin = 0; topMargin = 0;
} else { } else {
@ -9293,22 +9292,23 @@
Report = { Report = {
init: function() { init: function() {
if (!/report/.test(location.search)) { if (!(/report/.test(location.search) && d.cookie.indexOf('pass_enabled=1') === -1)) {
return; return;
} }
return $.ready(this.ready); return $.asap((function() {
return $.id('recaptcha_response_field');
}), Report.ready);
}, },
ready: function() { ready: function() {
var field, form; var field;
form = $('form');
field = $.id('recaptcha_response_field'); field = $.id('recaptcha_response_field');
$.on(field, 'keydown', function(e) { $.on(field, 'keydown', function(e) {
if (e.keyCode === 8 && !field.value) { if (e.keyCode === 8 && !field.value) {
return $.globalEval('Recaptcha.reload("t")'); return $.globalEval('Recaptcha.reload("t")');
} }
}); });
return $.on(form, 'submit', function(e) { return $.on($('form'), 'submit', function(e) {
var response; var response;
e.preventDefault(); e.preventDefault();
@ -9316,7 +9316,7 @@
if (!/\s/.test(response)) { if (!/\s/.test(response)) {
field.value = "" + response + " " + response; field.value = "" + response + " " + response;
} }
return form.submit(); return this.submit();
}); });
} }
}; };
@ -10217,16 +10217,17 @@
return; return;
case 'images.4chan.org': case 'images.4chan.org':
$.ready(function() { $.ready(function() {
var url; var URL;
if (Conf['404 Redirect'] && d.title === '4chan - 404 Not Found') { if (Conf['404 Redirect'] && d.title === '4chan - 404 Not Found') {
Redirect.init(); Redirect.init();
url = Redirect.to('file', { pathname = location.pathname.split('/');
boardID: pathname[1], URL = Redirect.to('file', {
filename: pathname[3] boardID: g.BOARD.ID,
filename: pathname[pathname.length - 1]
}); });
if (url) { if (URL) {
return location.href = url; return location.replace(URL);
} }
} }
}); });
@ -10309,7 +10310,7 @@
return $.ready(Main.initReady); return $.ready(Main.initReady);
}, },
initStyle: function() { initStyle: function() {
var MutationObserver, mainStyleSheet, observer, setStyle, style, styleSheets, _ref; var mainStyleSheet, observer, setStyle, style, styleSheets, _ref;
$.off(d, '4chanMainInit', Main.initStyle); $.off(d, '4chanMainInit', Main.initStyle);
if (!Main.isThisPageLegit() || $.hasClass(doc, 'fourchan-x')) { if (!Main.isThisPageLegit() || $.hasClass(doc, 'fourchan-x')) {
@ -10346,7 +10347,7 @@
if (!mainStyleSheet) { if (!mainStyleSheet) {
return; return;
} }
if (MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.OMutationObserver) { if (window.MutationObserver) {
observer = new MutationObserver(setStyle); observer = new MutationObserver(setStyle);
return observer.observe(mainStyleSheet, { return observer.observe(mainStyleSheet, {
attributes: true, attributes: true,
@ -10366,7 +10367,7 @@
threadID: g.THREADID, threadID: g.THREADID,
postID: +location.hash.match(/\d+/) postID: +location.hash.match(/\d+/)
}); });
location.href = href || ("/" + g.BOARD + "/"); location.replace(href || ("/" + g.BOARD + "/"));
} }
return; return;
} }

View File

@ -19,7 +19,7 @@
// @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwAgMAAAAqbBEUAAAACVBMVEUAAGcAAABmzDNZt9VtAAAAAXRSTlMAQObYZgAAAHFJREFUKFOt0LENACEIBdBv4Qju4wgWanEj3D6OcIVMKaitYHEU/jwTCQj8W75kiVCSBvdQ5/AvfVHBin11BgdRq3ysBgfwBDRrj3MCIA+oAQaku/Q1cNctrAmyDl577tOThYt/Y1RBM4DgOHzM0HFTAyLukH/cmRnqAAAAAElFTkSuQmCC // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwAgMAAAAqbBEUAAAACVBMVEUAAGcAAABmzDNZt9VtAAAAAXRSTlMAQObYZgAAAHFJREFUKFOt0LENACEIBdBv4Qju4wgWanEj3D6OcIVMKaitYHEU/jwTCQj8W75kiVCSBvdQ5/AvfVHBin11BgdRq3ysBgfwBDRrj3MCIA+oAQaku/Q1cNctrAmyDl577tOThYt/Y1RBM4DgOHzM0HFTAyLukH/cmRnqAAAAAElFTkSuQmCC
// ==/UserScript== // ==/UserScript==
/* /*
* 4chan X - Version 1.2.13 - 2013-05-29 * 4chan X - Version 1.2.13 - 2013-06-14
* *
* Licensed under the MIT license. * Licensed under the MIT license.
* https://github.com/seaweedchan/4chan-x/blob/master/LICENSE * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE
@ -113,7 +113,8 @@
__slice = [].slice, __slice = [].slice,
__hasProp = {}.hasOwnProperty, __hasProp = {}.hasOwnProperty,
__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); }; },
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
Config = { Config = {
main: { main: {
@ -293,6 +294,7 @@
'Open thread tab': ['Shift+o', 'Open thread in new tab.'], 'Open thread tab': ['Shift+o', 'Open thread in new tab.'],
'Next reply': ['j', 'Select next reply.'], 'Next reply': ['j', 'Select next reply.'],
'Previous reply': ['k', 'Select previous reply.'], 'Previous reply': ['k', 'Select previous reply.'],
'Deselect reply': ['Shift+d', 'Deselect reply.'],
'Hide': ['x', 'Hide thread.'] 'Hide': ['x', 'Hide thread.']
}, },
updater: { updater: {
@ -1282,9 +1284,9 @@
var boardID, postID, threadID, val, _base, _base1, _base2; var boardID, postID, threadID, val, _base, _base1, _base2;
boardID = _arg.boardID, threadID = _arg.threadID, postID = _arg.postID, val = _arg.val; boardID = _arg.boardID, threadID = _arg.threadID, postID = _arg.postID, val = _arg.val;
if (postID) { if (postID !== void 0) {
((_base = ((_base1 = this.data.boards)[boardID] || (_base1[boardID] = {})))[threadID] || (_base[threadID] = {}))[postID] = val; ((_base = ((_base1 = this.data.boards)[boardID] || (_base1[boardID] = {})))[threadID] || (_base[threadID] = {}))[postID] = val;
} else if (threadID) { } else if (threadID !== void 0) {
((_base2 = this.data.boards)[boardID] || (_base2[boardID] = {}))[threadID] = val; ((_base2 = this.data.boards)[boardID] || (_base2[boardID] = {}))[threadID] = val;
} else { } else {
this.data.boards[boardID] = val; this.data.boards[boardID] = val;
@ -1322,13 +1324,9 @@
_ref = this.data.boards; _ref = this.data.boards;
for (boardID in _ref) { for (boardID in _ref) {
val = _ref[boardID]; val = _ref[boardID];
if (typeof this.data.boards[boardID] !== 'object') { this.deleteIfEmpty({
delete this.data.boards[boardID]; boardID: boardID
} else { });
this.deleteIfEmpty({
boardID: boardID
});
}
} }
now = Date.now(); now = Date.now();
if ((this.data.lastChecked || 0) < now - 2 * $.HOUR) { if ((this.data.lastChecked || 0) < now - 2 * $.HOUR) {
@ -1775,7 +1773,7 @@
hashScroll: function() { hashScroll: function() {
var hash, post; var hash, post;
if (!((hash = this.location.hash) && (post = $.id(hash.slice(1))))) { if (!((hash = this.location.hash.slice(1)) && (post = $.id(hash)))) {
return; return;
} }
if ((Get.postFromRoot(post)).isHidden) { if ((Get.postFromRoot(post)).isHidden) {
@ -1873,7 +1871,7 @@
@license: https://github.com/4chan/4chan-JS/blob/master/LICENSE @license: https://github.com/4chan/4chan-JS/blob/master/LICENSE
*/ */
var a, boardID, capcode, capcodeClass, capcodeStart, closed, comment, container, date, dateUTC, email, emailEnd, emailStart, ext, file, fileDims, fileHTML, fileHtml, fileInfo, fileSize, fileThumb, filename, flag, flagCode, flagName, href, imgSrc, isClosed, isOP, isSticky, name, postID, quote, shortFilename, spoilerRange, staticPath, sticky, subject, threadID, tripcode, uniqueID, userID, _i, _len, _ref; var a, boardID, capcode, capcodeClass, capcodeStart, closed, comment, container, date, dateUTC, email, emailEnd, emailStart, ext, file, fileDims, fileHTML, fileInfo, fileSize, fileThumb, filename, flag, flagCode, flagName, href, imgSrc, isClosed, isOP, isSticky, name, postID, quote, shortFilename, spoilerRange, staticPath, sticky, subject, threadID, tripcode, uniqueID, userID, _i, _len, _ref;
postID = o.postID, threadID = o.threadID, boardID = o.boardID, name = o.name, capcode = o.capcode, tripcode = o.tripcode, uniqueID = o.uniqueID, email = o.email, subject = o.subject, flagCode = o.flagCode, flagName = o.flagName, date = o.date, dateUTC = o.dateUTC, isSticky = o.isSticky, isClosed = o.isClosed, comment = o.comment, file = o.file; postID = o.postID, threadID = o.threadID, boardID = o.boardID, name = o.name, capcode = o.capcode, tripcode = o.tripcode, uniqueID = o.uniqueID, email = o.email, subject = o.subject, flagCode = o.flagCode, flagName = o.flagName, date = o.date, dateUTC = o.dateUTC, isSticky = o.isSticky, isClosed = o.isClosed, comment = o.comment, file = o.file;
isOP = postID === threadID; isOP = postID === threadID;
@ -1911,7 +1909,7 @@
} }
flag = flagCode ? (" <img src='" + staticPath + "country/" + (boardID === 'pol' ? 'troll/' : '')) + flagCode.toLowerCase() + (".gif' alt=" + flagCode + " title='" + flagName + "' class=countryFlag>") : ''; flag = flagCode ? (" <img src='" + staticPath + "country/" + (boardID === 'pol' ? 'troll/' : '')) + flagCode.toLowerCase() + (".gif' alt=" + flagCode + " title='" + flagName + "' class=countryFlag>") : '';
if (file != null ? file.isDeleted : void 0) { if (file != null ? file.isDeleted : void 0) {
fileHtml = isOP ? ("<div class=file id=f" + postID + "><div class=fileInfo></div><span class=fileThumb>") + ("<img src='" + staticPath + "filedeleted.gif' alt='File deleted.' class=fileDeletedRes>") + "</span></div>" : ("<div class=file id=f" + postID + "><span class=fileThumb>") + ("<img src='" + staticPath + "filedeleted-res.gif' alt='File deleted.' class=fileDeletedRes>") + "</span></div>"; fileHTML = isOP ? ("<div class=file id=f" + postID + "><div class=fileInfo></div><span class=fileThumb>") + ("<img src='" + staticPath + "filedeleted.gif' alt='File deleted.' class=fileDeletedRes>") + "</span></div>" : ("<div class=file id=f" + postID + "><span class=fileThumb>") + ("<img src='" + staticPath + "filedeleted-res.gif' alt='File deleted.' class=fileDeletedRes>") + "</span></div>";
} else if (file) { } else if (file) {
ext = file.name.slice(-3); ext = file.name.slice(-3);
if (!file.twidth && !file.theight && ext === 'gif') { if (!file.twidth && !file.theight && ext === 'gif') {
@ -2166,9 +2164,9 @@
case '[/b]': case '[/b]':
return '</b>'; return '</b>';
case '[spoiler]': case '[spoiler]':
return '<span class=spoiler>'; return '<s>';
case '[/spoiler]': case '[/spoiler]':
return '</span>'; return '</s>';
case '[code]': case '[code]':
return '<pre class=prettyprint>'; return '<pre class=prettyprint>';
case '[/code]': case '[/code]':
@ -2700,10 +2698,6 @@
} }
for (key in Config.filter) { for (key in Config.filter) {
this.filters[key] = []; this.filters[key] = [];
if (Conf[key] === void 0) {
$["delete"](key);
continue;
}
_ref = Conf[key].split('\n'); _ref = Conf[key].split('\n');
for (_i = 0, _len = _ref.length; _i < _len; _i++) { for (_i = 0, _len = _ref.length; _i < _len; _i++) {
filter = _ref[_i]; filter = _ref[_i];
@ -5592,7 +5586,7 @@
}), this.ready.bind(this)); }), this.ready.bind(this));
}, },
ready: function() { ready: function() {
var MutationObserver, imgContainer, input, observer, setLifetime, var imgContainer, input, observer, setLifetime,
_this = this; _this = this;
setLifetime = function(e) { setLifetime = function(e) {
@ -5618,7 +5612,7 @@
img: imgContainer.firstChild, img: imgContainer.firstChild,
input: input input: input
}; };
if (MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.OMutationObserver) { if (window.MutationObserver) {
observer = new MutationObserver(this.load.bind(this)); observer = new MutationObserver(this.load.bind(this));
observer.observe(this.nodes.challenge, { observer.observe(this.nodes.challenge, {
childList: true childList: true
@ -7048,7 +7042,12 @@
return (thread.fileLimit && !thread.isSticky ? $.addClass : $.rmClass)(fileCountEl, 'warning'); return (thread.fileLimit && !thread.isSticky ? $.addClass : $.rmClass)(fileCountEl, 'warning');
}, },
fetchPage: function() { fetchPage: function() {
if (ThreadStats.thread.isDead || !Conf["Page Count in Stats"]) { if (!Conf["Page Count in Stats"]) {
return;
}
if (ThreadStats.thread.isDead) {
ThreadStats.pageCountEl.textContent = 'Dead';
$.addClass(ThreadStats.pageCountEl, 'warning');
return; return;
} }
setTimeout(ThreadStats.fetchPage, 2 * $.MINUTE); setTimeout(ThreadStats.fetchPage, 2 * $.MINUTE);
@ -7389,7 +7388,7 @@
return $.after(root, [$.tn(' '), icon]); return $.after(root, [$.tn(' '), icon]);
}, },
parse: function(postObjects) { parse: function(postObjects) {
var ID, OP, count, deletedFiles, deletedPosts, files, index, key, node, num, post, postObject, posts, root, scroll, _i, _len, _ref; var ID, OP, count, deletedFiles, deletedPosts, files, index, length, node, num, post, postObject, posts, scroll, sendEvent, threadID, _i, _len, _ref;
OP = postObjects[0]; OP = postObjects[0];
Build.spoilerRange[ThreadUpdater.thread.board] = OP.custom_spoiler; Build.spoilerRange[ThreadUpdater.thread.board] = OP.custom_spoiler;
@ -7430,67 +7429,53 @@
post.kill(true); post.kill(true);
deletedFiles.push(post); deletedFiles.push(post);
} }
if (ThreadUpdater.postID && ThreadUpdater.postID === ID) {
ThreadUpdater.foundPost = true;
}
} }
sendEvent = function() {
return $.event('ThreadUpdate', {
404: false,
thread: ThreadUpdater.thread,
newPosts: posts,
deletedPosts: deletedPosts,
deletedFiles: deletedFiles,
postCount: OP.replies + 1,
fileCount: OP.images + (!!ThreadUpdater.thread.OP.file && !ThreadUpdater.thread.OP.file.isDead)
});
};
if (!count) { if (!count) {
ThreadUpdater.set('status', null, null); ThreadUpdater.set('status', null, null);
ThreadUpdater.outdateCount++; ThreadUpdater.outdateCount++;
} else { sendEvent();
ThreadUpdater.set('status', "+" + count, 'new'); return;
ThreadUpdater.outdateCount = 0; }
if (Conf['Beep'] && d.hidden && Unread.posts && !Unread.posts.length) { ThreadUpdater.set('status', "+" + count, 'new');
if (!ThreadUpdater.audio) { ThreadUpdater.outdateCount = 0;
ThreadUpdater.audio = $.el('audio', { if (Conf['Beep'] && d.hidden && Unread.posts && !Unread.posts.length) {
src: ThreadUpdater.beep if (!ThreadUpdater.audio) {
}); ThreadUpdater.audio = $.el('audio', {
} src: ThreadUpdater.beep
ThreadUpdater.audio.play(); });
} }
ThreadUpdater.lastPost = posts[count - 1].ID; ThreadUpdater.audio.play();
Main.callbackNodes(Post, posts); }
scroll = Conf['Auto Scroll'] && ThreadUpdater.scrollBG() && ThreadUpdater.root.getBoundingClientRect().bottom - doc.clientHeight < 25; ThreadUpdater.lastPost = posts[count - 1].ID;
for (key in posts) { Main.callbackNodes(Post, posts);
post = posts[key]; scroll = Conf['Auto Scroll'] && ThreadUpdater.scrollBG() && ThreadUpdater.root.getBoundingClientRect().bottom - doc.clientHeight < 25;
if (!posts.hasOwnProperty(key)) { $.add(ThreadUpdater.root, nodes);
continue; sendEvent();
} if (scroll) {
root = post.nodes.root; if (Conf['Bottom Scroll']) {
if (post.cb) { doc.scrollTop = d.body.clientHeight;
if (!post.cb.call(post)) { } else {
$.add(ThreadUpdater.root, root); Header.scrollToPost(nodes[0]);
} }
} else { }
$.add(ThreadUpdater.root, root); threadID = ThreadUpdater.thread.ID;
} length = $$('.thread > .postContainer', ThreadUpdater.root).length;
} if (Conf['Enable 4chan\'s Extension']) {
if (scroll) { return $.globalEval("Parser.parseThread(" + threadID + ", " + (-count) + ")");
if (Conf['Bottom Scroll']) { } else {
doc.scrollTop = d.body.clientHeight; return Fourchan.parseThread(threadID, length - count, length);
} else {
if (root) {
Header.scrollToPost(root);
}
}
}
$.queueTask(function() {
var length, threadID;
threadID = ThreadUpdater.thread.ID;
length = $$('.thread > .postContainer', ThreadUpdater.root).length;
return Fourchan.parseThread(threadID, length - count, length);
});
} }
return $.event('ThreadUpdate', {
404: false,
thread: ThreadUpdater.thread,
newPosts: posts,
deletedPosts: deletedPosts,
deletedFiles: deletedFiles,
postCount: OP.replies + 1,
fileCount: OP.images + (!!ThreadUpdater.thread.OP.file && !ThreadUpdater.thread.OP.file.isDead)
});
} }
}; };
@ -7702,7 +7687,7 @@
} }
}, },
scroll: function() { scroll: function() {
var hash, post, posts, prevID, root; var checkPosition, hash, onload, post, posts, prevID, root;
if ((hash = location.hash.match(/\d+/)) && hash[0] in Unread.thread.posts) { if ((hash = location.hash.match(/\d+/)) && hash[0] in Unread.thread.posts) {
return; return;
@ -7719,11 +7704,27 @@
break; break;
} }
} }
root.scrollIntoView(false); onload = function() {
return; if (checkPosition(root)) {
return root.scrollIntoView(false);
}
};
} else {
posts = Object.keys(Unread.thread.posts);
root = Unread.thread.posts[posts[posts.length - 1]].nodes.root;
onload = function() {
if (checkPosition(root)) {
return Header.scrollToPost(root);
}
};
} }
posts = Object.keys(Unread.thread.posts); checkPosition = function(target) {
return Header.scrollToPost(Unread.thread.posts[posts[posts.length - 1]].nodes.root); var height, top, _ref;
_ref = target.getBoundingClientRect(), top = _ref.top, height = _ref.height;
return top + height - doc.clientHeight > 0;
};
return $.on(window, 'load', onload);
}, },
sync: function() { sync: function() {
var lastReadPost; var lastReadPost;
@ -7742,11 +7743,11 @@
Unread.setLine(); Unread.setLine();
return Unread.update(); return Unread.update();
}, },
addPosts: function(newPosts) { addPosts: function(posts) {
var ID, data, post, _i, _len; var ID, data, post, _i, _len, _ref;
for (_i = 0, _len = newPosts.length; _i < _len; _i++) { for (_i = 0, _len = posts.length; _i < _len; _i++) {
post = newPosts[_i]; post = posts[_i];
ID = post.ID; ID = post.ID;
if (ID <= Unread.lastReadPost || post.isHidden) { if (ID <= Unread.lastReadPost || post.isHidden) {
continue; continue;
@ -7765,7 +7766,7 @@
Unread.addPostQuotingYou(post); Unread.addPostQuotingYou(post);
} }
if (Conf['Unread Line']) { if (Conf['Unread Line']) {
Unread.setLine(newPosts.contains(Unread.posts[0])); Unread.setLine((_ref = Unread.posts[0], __indexOf.call(posts, _ref) >= 0));
} }
Unread.read(); Unread.read();
return Unread.update(); return Unread.update();
@ -8016,8 +8017,8 @@
'http': true, 'http': true,
'https': true, 'https': true,
'software': 'fuuka', 'software': 'fuuka',
'boards': ['3', 'cgl', 'ck', 'fa', 'ic', 'jp', 'lit', 'q', 's4s', 'tg', 'vr'], 'boards': ['3', 'cgl', 'ck', 'fa', 'ic', 'jp', 'lit', 'q', 'tg', 'vr'],
'files': ['3', 'cgl', 'ck', 'fa', 'ic', 'jp', 'lit', 'q', 's4s', 'vr'] 'files': ['3', 'cgl', 'ck', 'fa', 'ic', 'jp', 'lit', 'q', 'vr']
} }
}, },
to: function(dest, data) { to: function(dest, data) {
@ -8087,7 +8088,7 @@
return $.on(d, '4chanXInitFinished', this.setup); return $.on(d, '4chanXInitFinished', this.setup);
}, },
setup: function() { setup: function() {
var btn, entry, items, psa; var btn, entry, psa;
$.off(d, '4chanXInitFinished', PSAHiding.setup); $.off(d, '4chanXInitFinished', PSAHiding.setup);
if (!(psa = $.id('globalMessage'))) { if (!(psa = $.id('globalMessage'))) {
@ -8115,21 +8116,10 @@
href: 'javascript:;' href: 'javascript:;'
}); });
$.on(btn, 'click', PSAHiding.toggle); $.on(btn, 'click', PSAHiding.toggle);
items = { $.get('hiddenPSA', 0, function(_arg) {
hiddenPSA: 0, var hiddenPSA;
hiddenPSAs: null
};
$.get(items, function(_arg) {
var hiddenPSA, hiddenPSAs;
hiddenPSA = _arg.hiddenPSA, hiddenPSAs = _arg.hiddenPSAs; hiddenPSA = _arg.hiddenPSA;
if (hiddenPSAs) {
$["delete"]('hiddenPSAs');
if (hiddenPSAs.contains(psa.textContent.replace(/\W+/g, '').toLowerCase())) {
hiddenPSA = +$.id('globalMessage').dataset.utc;
$.set('hiddenPSA', hiddenPSA);
}
}
PSAHiding.sync(hiddenPSA); PSAHiding.sync(hiddenPSA);
$.before(psa, btn); $.before(psa, btn);
return $.rmClass(doc, 'hide-announcement'); return $.rmClass(doc, 'hide-announcement');
@ -8250,7 +8240,7 @@
var rgb; var rgb;
rgb = IDColor.ids[this] || IDColor.compute(this); rgb = IDColor.ids[this] || IDColor.compute(this);
return ("background-color: rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + "); color: ") + (rgb[3] ? "black;" : "white; border-radius: 3px; padding: 0px 2px;"); return ("background-color: rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + "); color: ") + (rgb[3] ? "black; border-radius: 3px; padding: 0px 2px;" : "white; border-radius: 3px; padding: 0px 2px;");
}, },
hash: function(str) { hash: function(str) {
var i, j, msg; var i, j, msg;
@ -8702,7 +8692,7 @@
} }
board = g.BOARD.ID; board = g.BOARD.ID;
if (board === 'g') { if (board === 'g') {
$.globalEval("window.addEventListener('prettyprint', function(e) {\n var pre = e.detail;\n pre.innerHTML = prettyPrintOne(pre.innerHTML.replace(/\\s/g, '&nbsp;'));\n}, false);"); $.globalEval("window.addEventListener('prettyprint', function(e) {\n var pre = e.detail;\n pre.innerHTML = prettyPrintOne(pre.innerHTML);\n}, false);");
Post.prototype.callbacks.push({ Post.prototype.callbacks.push({
name: 'Parse /g/ code', name: 'Parse /g/ code',
cb: this.code cb: this.code
@ -8926,6 +8916,9 @@
case Conf['Previous reply']: case Conf['Previous reply']:
Keybinds.hl(-1, threadRoot); Keybinds.hl(-1, threadRoot);
break; break;
case Conf['Deselect reply']:
Keybinds.hl(0, threadRoot);
break;
case Conf['Hide']: case Conf['Hide']:
if (g.VIEW === 'index') { if (g.VIEW === 'index') {
ThreadHiding.toggle(thread); ThreadHiding.toggle(thread);
@ -9036,6 +9029,12 @@
hl: function(delta, thread) { hl: function(delta, thread) {
var axe, headRect, next, postEl, rect, replies, reply, root, topMargin, _i, _len; var axe, headRect, next, postEl, rect, replies, reply, root, topMargin, _i, _len;
if (!delta) {
if (postEl = $('.reply.highlight', thread)) {
$.rmClass(postEl, 'highlight');
}
return;
}
if (Conf['Fixed Header'] && Conf['Bottom header']) { if (Conf['Fixed Header'] && Conf['Bottom header']) {
topMargin = 0; topMargin = 0;
} else { } else {
@ -9287,22 +9286,23 @@
Report = { Report = {
init: function() { init: function() {
if (!/report/.test(location.search)) { if (!(/report/.test(location.search) && d.cookie.indexOf('pass_enabled=1') === -1)) {
return; return;
} }
return $.ready(this.ready); return $.asap((function() {
return $.id('recaptcha_response_field');
}), Report.ready);
}, },
ready: function() { ready: function() {
var field, form; var field;
form = $('form');
field = $.id('recaptcha_response_field'); field = $.id('recaptcha_response_field');
$.on(field, 'keydown', function(e) { $.on(field, 'keydown', function(e) {
if (e.keyCode === 8 && !field.value) { if (e.keyCode === 8 && !field.value) {
return $.globalEval('Recaptcha.reload("t")'); return $.globalEval('Recaptcha.reload("t")');
} }
}); });
return $.on(form, 'submit', function(e) { return $.on($('form'), 'submit', function(e) {
var response; var response;
e.preventDefault(); e.preventDefault();
@ -9310,7 +9310,7 @@
if (!/\s/.test(response)) { if (!/\s/.test(response)) {
field.value = "" + response + " " + response; field.value = "" + response + " " + response;
} }
return form.submit(); return this.submit();
}); });
} }
}; };
@ -10213,16 +10213,17 @@
return; return;
case 'images.4chan.org': case 'images.4chan.org':
$.ready(function() { $.ready(function() {
var url; var URL;
if (Conf['404 Redirect'] && d.title === '4chan - 404 Not Found') { if (Conf['404 Redirect'] && d.title === '4chan - 404 Not Found') {
Redirect.init(); Redirect.init();
url = Redirect.to('file', { pathname = location.pathname.split('/');
boardID: pathname[1], URL = Redirect.to('file', {
filename: pathname[3] boardID: g.BOARD.ID,
filename: pathname[pathname.length - 1]
}); });
if (url) { if (URL) {
return location.href = url; return location.replace(URL);
} }
} }
}); });
@ -10305,7 +10306,7 @@
return $.ready(Main.initReady); return $.ready(Main.initReady);
}, },
initStyle: function() { initStyle: function() {
var MutationObserver, mainStyleSheet, observer, setStyle, style, styleSheets, _ref; var mainStyleSheet, observer, setStyle, style, styleSheets, _ref;
$.off(d, '4chanMainInit', Main.initStyle); $.off(d, '4chanMainInit', Main.initStyle);
if (!Main.isThisPageLegit() || $.hasClass(doc, 'fourchan-x')) { if (!Main.isThisPageLegit() || $.hasClass(doc, 'fourchan-x')) {
@ -10342,7 +10343,7 @@
if (!mainStyleSheet) { if (!mainStyleSheet) {
return; return;
} }
if (MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.OMutationObserver) { if (window.MutationObserver) {
observer = new MutationObserver(setStyle); observer = new MutationObserver(setStyle);
return observer.observe(mainStyleSheet, { return observer.observe(mainStyleSheet, {
attributes: true, attributes: true,
@ -10362,7 +10363,7 @@
threadID: g.THREADID, threadID: g.THREADID,
postID: +location.hash.match(/\d+/) postID: +location.hash.match(/\d+/)
}); });
location.href = href || ("/" + g.BOARD + "/"); location.replace(href || ("/" + g.BOARD + "/"));
} }
return; return;
} }

View File

@ -1,6 +1,6 @@
// Generated by CoffeeScript // Generated by CoffeeScript
/* /*
* 4chan X - Version 1.2.13 - 2013-05-29 * 4chan X - Version 1.2.13 - 2013-06-14
* *
* Licensed under the MIT license. * Licensed under the MIT license.
* https://github.com/seaweedchan/4chan-x/blob/master/LICENSE * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE
@ -274,6 +274,7 @@
'Open thread tab': ['Shift+o', 'Open thread in new tab.'], 'Open thread tab': ['Shift+o', 'Open thread in new tab.'],
'Next reply': ['j', 'Select next reply.'], 'Next reply': ['j', 'Select next reply.'],
'Previous reply': ['k', 'Select previous reply.'], 'Previous reply': ['k', 'Select previous reply.'],
'Deselect reply': ['Shift+d', 'Deselect reply.'],
'Hide': ['x', 'Hide thread.'] 'Hide': ['x', 'Hide thread.']
}, },
updater: { updater: {
@ -781,9 +782,14 @@
(syncItems || (syncItems = {}))[key] = val; (syncItems || (syncItems = {}))[key] = val;
} }
} }
items = {};
count = 0; count = 0;
done = function(item) { done = function(item) {
var lastError;
lastError = chrome.runtime.lastError;
if (lastError) {
c.error(lastError, lastError.message || 'No message.');
}
$.extend(items, item); $.extend(items, item);
if (!--count) { if (!--count) {
return cb(items); return cb(items);
@ -1284,9 +1290,9 @@
var boardID, postID, threadID, val, _base, _base1, _base2; var boardID, postID, threadID, val, _base, _base1, _base2;
boardID = _arg.boardID, threadID = _arg.threadID, postID = _arg.postID, val = _arg.val; boardID = _arg.boardID, threadID = _arg.threadID, postID = _arg.postID, val = _arg.val;
if (postID) { if (postID !== void 0) {
((_base = ((_base1 = this.data.boards)[boardID] || (_base1[boardID] = {})))[threadID] || (_base[threadID] = {}))[postID] = val; ((_base = ((_base1 = this.data.boards)[boardID] || (_base1[boardID] = {})))[threadID] || (_base[threadID] = {}))[postID] = val;
} else if (threadID) { } else if (threadID !== void 0) {
((_base2 = this.data.boards)[boardID] || (_base2[boardID] = {}))[threadID] = val; ((_base2 = this.data.boards)[boardID] || (_base2[boardID] = {}))[threadID] = val;
} else { } else {
this.data.boards[boardID] = val; this.data.boards[boardID] = val;
@ -1324,13 +1330,9 @@
_ref = this.data.boards; _ref = this.data.boards;
for (boardID in _ref) { for (boardID in _ref) {
val = _ref[boardID]; val = _ref[boardID];
if (typeof this.data.boards[boardID] !== 'object') { this.deleteIfEmpty({
delete this.data.boards[boardID]; boardID: boardID
} else { });
this.deleteIfEmpty({
boardID: boardID
});
}
} }
now = Date.now(); now = Date.now();
if ((this.data.lastChecked || 0) < now - 2 * $.HOUR) { if ((this.data.lastChecked || 0) < now - 2 * $.HOUR) {
@ -1777,7 +1779,7 @@
hashScroll: function() { hashScroll: function() {
var hash, post; var hash, post;
if (!((hash = this.location.hash) && (post = $.id(hash.slice(1))))) { if (!((hash = this.location.hash.slice(1)) && (post = $.id(hash)))) {
return; return;
} }
if ((Get.postFromRoot(post)).isHidden) { if ((Get.postFromRoot(post)).isHidden) {
@ -1875,7 +1877,7 @@
@license: https://github.com/4chan/4chan-JS/blob/master/LICENSE @license: https://github.com/4chan/4chan-JS/blob/master/LICENSE
*/ */
var a, boardID, capcode, capcodeClass, capcodeStart, closed, comment, container, date, dateUTC, email, emailEnd, emailStart, ext, file, fileDims, fileHTML, fileHtml, fileInfo, fileSize, fileThumb, filename, flag, flagCode, flagName, href, imgSrc, isClosed, isOP, isSticky, name, postID, quote, shortFilename, spoilerRange, staticPath, sticky, subject, threadID, tripcode, uniqueID, userID, _i, _len, _ref; var a, boardID, capcode, capcodeClass, capcodeStart, closed, comment, container, date, dateUTC, email, emailEnd, emailStart, ext, file, fileDims, fileHTML, fileInfo, fileSize, fileThumb, filename, flag, flagCode, flagName, href, imgSrc, isClosed, isOP, isSticky, name, postID, quote, shortFilename, spoilerRange, staticPath, sticky, subject, threadID, tripcode, uniqueID, userID, _i, _len, _ref;
postID = o.postID, threadID = o.threadID, boardID = o.boardID, name = o.name, capcode = o.capcode, tripcode = o.tripcode, uniqueID = o.uniqueID, email = o.email, subject = o.subject, flagCode = o.flagCode, flagName = o.flagName, date = o.date, dateUTC = o.dateUTC, isSticky = o.isSticky, isClosed = o.isClosed, comment = o.comment, file = o.file; postID = o.postID, threadID = o.threadID, boardID = o.boardID, name = o.name, capcode = o.capcode, tripcode = o.tripcode, uniqueID = o.uniqueID, email = o.email, subject = o.subject, flagCode = o.flagCode, flagName = o.flagName, date = o.date, dateUTC = o.dateUTC, isSticky = o.isSticky, isClosed = o.isClosed, comment = o.comment, file = o.file;
isOP = postID === threadID; isOP = postID === threadID;
@ -1913,7 +1915,7 @@
} }
flag = flagCode ? (" <img src='" + staticPath + "country/" + (boardID === 'pol' ? 'troll/' : '')) + flagCode.toLowerCase() + (".gif' alt=" + flagCode + " title='" + flagName + "' class=countryFlag>") : ''; flag = flagCode ? (" <img src='" + staticPath + "country/" + (boardID === 'pol' ? 'troll/' : '')) + flagCode.toLowerCase() + (".gif' alt=" + flagCode + " title='" + flagName + "' class=countryFlag>") : '';
if (file != null ? file.isDeleted : void 0) { if (file != null ? file.isDeleted : void 0) {
fileHtml = isOP ? ("<div class=file id=f" + postID + "><div class=fileInfo></div><span class=fileThumb>") + ("<img src='" + staticPath + "filedeleted.gif' alt='File deleted.' class=fileDeletedRes>") + "</span></div>" : ("<div class=file id=f" + postID + "><span class=fileThumb>") + ("<img src='" + staticPath + "filedeleted-res.gif' alt='File deleted.' class=fileDeletedRes>") + "</span></div>"; fileHTML = isOP ? ("<div class=file id=f" + postID + "><div class=fileInfo></div><span class=fileThumb>") + ("<img src='" + staticPath + "filedeleted.gif' alt='File deleted.' class=fileDeletedRes>") + "</span></div>" : ("<div class=file id=f" + postID + "><span class=fileThumb>") + ("<img src='" + staticPath + "filedeleted-res.gif' alt='File deleted.' class=fileDeletedRes>") + "</span></div>";
} else if (file) { } else if (file) {
ext = file.name.slice(-3); ext = file.name.slice(-3);
if (!file.twidth && !file.theight && ext === 'gif') { if (!file.twidth && !file.theight && ext === 'gif') {
@ -2168,9 +2170,9 @@
case '[/b]': case '[/b]':
return '</b>'; return '</b>';
case '[spoiler]': case '[spoiler]':
return '<span class=spoiler>'; return '<s>';
case '[/spoiler]': case '[/spoiler]':
return '</span>'; return '</s>';
case '[code]': case '[code]':
return '<pre class=prettyprint>'; return '<pre class=prettyprint>';
case '[/code]': case '[/code]':
@ -2702,10 +2704,6 @@
} }
for (key in Config.filter) { for (key in Config.filter) {
this.filters[key] = []; this.filters[key] = [];
if (Conf[key] === void 0) {
$["delete"](key);
continue;
}
_ref = Conf[key].split('\n'); _ref = Conf[key].split('\n');
for (_i = 0, _len = _ref.length; _i < _len; _i++) { for (_i = 0, _len = _ref.length; _i < _len; _i++) {
filter = _ref[_i]; filter = _ref[_i];
@ -5589,7 +5587,7 @@
}), this.ready.bind(this)); }), this.ready.bind(this));
}, },
ready: function() { ready: function() {
var MutationObserver, imgContainer, input, observer, setLifetime, var imgContainer, input, observer, setLifetime,
_this = this; _this = this;
setLifetime = function(e) { setLifetime = function(e) {
@ -5615,7 +5613,7 @@
img: imgContainer.firstChild, img: imgContainer.firstChild,
input: input input: input
}; };
if (MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.OMutationObserver) { if (window.MutationObserver) {
observer = new MutationObserver(this.load.bind(this)); observer = new MutationObserver(this.load.bind(this));
observer.observe(this.nodes.challenge, { observer.observe(this.nodes.challenge, {
childList: true childList: true
@ -7026,7 +7024,12 @@
return (thread.fileLimit && !thread.isSticky ? $.addClass : $.rmClass)(fileCountEl, 'warning'); return (thread.fileLimit && !thread.isSticky ? $.addClass : $.rmClass)(fileCountEl, 'warning');
}, },
fetchPage: function() { fetchPage: function() {
if (ThreadStats.thread.isDead || !Conf["Page Count in Stats"]) { if (!Conf["Page Count in Stats"]) {
return;
}
if (ThreadStats.thread.isDead) {
ThreadStats.pageCountEl.textContent = 'Dead';
$.addClass(ThreadStats.pageCountEl, 'warning');
return; return;
} }
setTimeout(ThreadStats.fetchPage, 2 * $.MINUTE); setTimeout(ThreadStats.fetchPage, 2 * $.MINUTE);
@ -7367,7 +7370,7 @@
return $.after(root, [$.tn(' '), icon]); return $.after(root, [$.tn(' '), icon]);
}, },
parse: function(postObjects) { parse: function(postObjects) {
var ID, OP, count, deletedFiles, deletedPosts, files, index, key, node, num, post, postObject, posts, root, scroll, _i, _len, _ref; var ID, OP, count, deletedFiles, deletedPosts, files, index, length, node, num, post, postObject, posts, scroll, sendEvent, threadID, _i, _len, _ref;
OP = postObjects[0]; OP = postObjects[0];
Build.spoilerRange[ThreadUpdater.thread.board] = OP.custom_spoiler; Build.spoilerRange[ThreadUpdater.thread.board] = OP.custom_spoiler;
@ -7408,67 +7411,53 @@
post.kill(true); post.kill(true);
deletedFiles.push(post); deletedFiles.push(post);
} }
if (ThreadUpdater.postID && ThreadUpdater.postID === ID) {
ThreadUpdater.foundPost = true;
}
} }
sendEvent = function() {
return $.event('ThreadUpdate', {
404: false,
thread: ThreadUpdater.thread,
newPosts: posts,
deletedPosts: deletedPosts,
deletedFiles: deletedFiles,
postCount: OP.replies + 1,
fileCount: OP.images + (!!ThreadUpdater.thread.OP.file && !ThreadUpdater.thread.OP.file.isDead)
});
};
if (!count) { if (!count) {
ThreadUpdater.set('status', null, null); ThreadUpdater.set('status', null, null);
ThreadUpdater.outdateCount++; ThreadUpdater.outdateCount++;
} else { sendEvent();
ThreadUpdater.set('status', "+" + count, 'new'); return;
ThreadUpdater.outdateCount = 0; }
if (Conf['Beep'] && d.hidden && Unread.posts && !Unread.posts.length) { ThreadUpdater.set('status', "+" + count, 'new');
if (!ThreadUpdater.audio) { ThreadUpdater.outdateCount = 0;
ThreadUpdater.audio = $.el('audio', { if (Conf['Beep'] && d.hidden && Unread.posts && !Unread.posts.length) {
src: ThreadUpdater.beep if (!ThreadUpdater.audio) {
}); ThreadUpdater.audio = $.el('audio', {
} src: ThreadUpdater.beep
ThreadUpdater.audio.play(); });
} }
ThreadUpdater.lastPost = posts[count - 1].ID; ThreadUpdater.audio.play();
Main.callbackNodes(Post, posts); }
scroll = Conf['Auto Scroll'] && ThreadUpdater.scrollBG() && ThreadUpdater.root.getBoundingClientRect().bottom - doc.clientHeight < 25; ThreadUpdater.lastPost = posts[count - 1].ID;
for (key in posts) { Main.callbackNodes(Post, posts);
post = posts[key]; scroll = Conf['Auto Scroll'] && ThreadUpdater.scrollBG() && ThreadUpdater.root.getBoundingClientRect().bottom - doc.clientHeight < 25;
if (!posts.hasOwnProperty(key)) { $.add(ThreadUpdater.root, nodes);
continue; sendEvent();
} if (scroll) {
root = post.nodes.root; if (Conf['Bottom Scroll']) {
if (post.cb) { d.body.scrollTop = d.body.clientHeight;
if (!post.cb.call(post)) { } else {
$.add(ThreadUpdater.root, root); Header.scrollToPost(nodes[0]);
} }
} else { }
$.add(ThreadUpdater.root, root); threadID = ThreadUpdater.thread.ID;
} length = $$('.thread > .postContainer', ThreadUpdater.root).length;
} if (Conf['Enable 4chan\'s Extension']) {
if (scroll) { return $.globalEval("Parser.parseThread(" + threadID + ", " + (-count) + ")");
if (Conf['Bottom Scroll']) { } else {
d.body.scrollTop = d.body.clientHeight; return Fourchan.parseThread(threadID, length - count, length);
} else {
if (root) {
Header.scrollToPost(root);
}
}
}
$.queueTask(function() {
var length, threadID;
threadID = ThreadUpdater.thread.ID;
length = $$('.thread > .postContainer', ThreadUpdater.root).length;
return Fourchan.parseThread(threadID, length - count, length);
});
} }
return $.event('ThreadUpdate', {
404: false,
thread: ThreadUpdater.thread,
newPosts: posts,
deletedPosts: deletedPosts,
deletedFiles: deletedFiles,
postCount: OP.replies + 1,
fileCount: OP.images + (!!ThreadUpdater.thread.OP.file && !ThreadUpdater.thread.OP.file.isDead)
});
} }
}; };
@ -7680,7 +7669,7 @@
} }
}, },
scroll: function() { scroll: function() {
var hash, post, posts, prevID, root; var checkPosition, hash, onload, post, posts, prevID, root;
if ((hash = location.hash.match(/\d+/)) && hash[0] in Unread.thread.posts) { if ((hash = location.hash.match(/\d+/)) && hash[0] in Unread.thread.posts) {
return; return;
@ -7697,11 +7686,27 @@
break; break;
} }
} }
root.scrollIntoView(false); onload = function() {
return; if (checkPosition(root)) {
return root.scrollIntoView(false);
}
};
} else {
posts = Object.keys(Unread.thread.posts);
root = Unread.thread.posts[posts[posts.length - 1]].nodes.root;
onload = function() {
if (checkPosition(root)) {
return Header.scrollToPost(root);
}
};
} }
posts = Object.keys(Unread.thread.posts); checkPosition = function(target) {
return Header.scrollToPost(Unread.thread.posts[posts[posts.length - 1]].nodes.root); var height, top, _ref;
_ref = target.getBoundingClientRect(), top = _ref.top, height = _ref.height;
return top + height - doc.clientHeight > 0;
};
return $.on(window, 'load', onload);
}, },
sync: function() { sync: function() {
var lastReadPost; var lastReadPost;
@ -7720,11 +7725,11 @@
Unread.setLine(); Unread.setLine();
return Unread.update(); return Unread.update();
}, },
addPosts: function(newPosts) { addPosts: function(posts) {
var ID, data, post, _i, _len; var ID, data, post, _i, _len, _ref;
for (_i = 0, _len = newPosts.length; _i < _len; _i++) { for (_i = 0, _len = posts.length; _i < _len; _i++) {
post = newPosts[_i]; post = posts[_i];
ID = post.ID; ID = post.ID;
if (ID <= Unread.lastReadPost || post.isHidden) { if (ID <= Unread.lastReadPost || post.isHidden) {
continue; continue;
@ -7743,7 +7748,7 @@
Unread.addPostQuotingYou(post); Unread.addPostQuotingYou(post);
} }
if (Conf['Unread Line']) { if (Conf['Unread Line']) {
Unread.setLine(newPosts.contains(Unread.posts[0])); Unread.setLine((_ref = Unread.posts[0], __indexOf.call(posts, _ref) >= 0));
} }
Unread.read(); Unread.read();
return Unread.update(); return Unread.update();
@ -7999,8 +8004,8 @@
'http': true, 'http': true,
'https': true, 'https': true,
'software': 'fuuka', 'software': 'fuuka',
'boards': ['3', 'cgl', 'ck', 'fa', 'ic', 'jp', 'lit', 'q', 's4s', 'tg', 'vr'], 'boards': ['3', 'cgl', 'ck', 'fa', 'ic', 'jp', 'lit', 'q', 'tg', 'vr'],
'files': ['3', 'cgl', 'ck', 'fa', 'ic', 'jp', 'lit', 'q', 's4s', 'vr'] 'files': ['3', 'cgl', 'ck', 'fa', 'ic', 'jp', 'lit', 'q', 'vr']
} }
}, },
to: function(dest, data) { to: function(dest, data) {
@ -8070,7 +8075,7 @@
return $.on(d, '4chanXInitFinished', this.setup); return $.on(d, '4chanXInitFinished', this.setup);
}, },
setup: function() { setup: function() {
var btn, entry, items, psa; var btn, entry, psa;
$.off(d, '4chanXInitFinished', PSAHiding.setup); $.off(d, '4chanXInitFinished', PSAHiding.setup);
if (!(psa = $.id('globalMessage'))) { if (!(psa = $.id('globalMessage'))) {
@ -8098,21 +8103,10 @@
href: 'javascript:;' href: 'javascript:;'
}); });
$.on(btn, 'click', PSAHiding.toggle); $.on(btn, 'click', PSAHiding.toggle);
items = { $.get('hiddenPSA', 0, function(_arg) {
hiddenPSA: 0, var hiddenPSA;
hiddenPSAs: null
};
$.get(items, function(_arg) {
var hiddenPSA, hiddenPSAs;
hiddenPSA = _arg.hiddenPSA, hiddenPSAs = _arg.hiddenPSAs; hiddenPSA = _arg.hiddenPSA;
if (hiddenPSAs) {
$["delete"]('hiddenPSAs');
if (hiddenPSAs.contains(psa.textContent.replace(/\W+/g, '').toLowerCase())) {
hiddenPSA = +$.id('globalMessage').dataset.utc;
$.set('hiddenPSA', hiddenPSA);
}
}
PSAHiding.sync(hiddenPSA); PSAHiding.sync(hiddenPSA);
$.before(psa, btn); $.before(psa, btn);
return $.rmClass(doc, 'hide-announcement'); return $.rmClass(doc, 'hide-announcement');
@ -8233,7 +8227,7 @@
var rgb; var rgb;
rgb = IDColor.ids[this] || IDColor.compute(this); rgb = IDColor.ids[this] || IDColor.compute(this);
return ("background-color: rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + "); color: ") + (rgb[3] ? "black;" : "white; border-radius: 3px; padding: 0px 2px;"); return ("background-color: rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + "); color: ") + (rgb[3] ? "black; border-radius: 3px; padding: 0px 2px;" : "white; border-radius: 3px; padding: 0px 2px;");
}, },
hash: function(str) { hash: function(str) {
var i, j, msg; var i, j, msg;
@ -8685,7 +8679,7 @@
} }
board = g.BOARD.ID; board = g.BOARD.ID;
if (board === 'g') { if (board === 'g') {
$.globalEval("window.addEventListener('prettyprint', function(e) {\n var pre = e.detail;\n pre.innerHTML = prettyPrintOne(pre.innerHTML.replace(/\\s/g, '&nbsp;'));\n}, false);"); $.globalEval("window.addEventListener('prettyprint', function(e) {\n var pre = e.detail;\n pre.innerHTML = prettyPrintOne(pre.innerHTML);\n}, false);");
Post.prototype.callbacks.push({ Post.prototype.callbacks.push({
name: 'Parse /g/ code', name: 'Parse /g/ code',
cb: this.code cb: this.code
@ -8909,6 +8903,9 @@
case Conf['Previous reply']: case Conf['Previous reply']:
Keybinds.hl(-1, threadRoot); Keybinds.hl(-1, threadRoot);
break; break;
case Conf['Deselect reply']:
Keybinds.hl(0, threadRoot);
break;
case Conf['Hide']: case Conf['Hide']:
if (g.VIEW === 'index') { if (g.VIEW === 'index') {
ThreadHiding.toggle(thread); ThreadHiding.toggle(thread);
@ -9019,6 +9016,12 @@
hl: function(delta, thread) { hl: function(delta, thread) {
var axe, headRect, next, postEl, rect, replies, reply, root, topMargin, _i, _len; var axe, headRect, next, postEl, rect, replies, reply, root, topMargin, _i, _len;
if (!delta) {
if (postEl = $('.reply.highlight', thread)) {
$.rmClass(postEl, 'highlight');
}
return;
}
if (Conf['Fixed Header'] && Conf['Bottom header']) { if (Conf['Fixed Header'] && Conf['Bottom header']) {
topMargin = 0; topMargin = 0;
} else { } else {
@ -9270,22 +9273,23 @@
Report = { Report = {
init: function() { init: function() {
if (!/report/.test(location.search)) { if (!(/report/.test(location.search) && d.cookie.indexOf('pass_enabled=1') === -1)) {
return; return;
} }
return $.ready(this.ready); return $.asap((function() {
return $.id('recaptcha_response_field');
}), Report.ready);
}, },
ready: function() { ready: function() {
var field, form; var field;
form = $('form');
field = $.id('recaptcha_response_field'); field = $.id('recaptcha_response_field');
$.on(field, 'keydown', function(e) { $.on(field, 'keydown', function(e) {
if (e.keyCode === 8 && !field.value) { if (e.keyCode === 8 && !field.value) {
return $.globalEval('Recaptcha.reload("t")'); return $.globalEval('Recaptcha.reload("t")');
} }
}); });
return $.on(form, 'submit', function(e) { return $.on($('form'), 'submit', function(e) {
var response; var response;
e.preventDefault(); e.preventDefault();
@ -9293,7 +9297,7 @@
if (!/\s/.test(response)) { if (!/\s/.test(response)) {
field.value = "" + response + " " + response; field.value = "" + response + " " + response;
} }
return form.submit(); return this.submit();
}); });
} }
}; };
@ -10194,16 +10198,17 @@
return; return;
case 'images.4chan.org': case 'images.4chan.org':
$.ready(function() { $.ready(function() {
var url; var URL;
if (Conf['404 Redirect'] && d.title === '4chan - 404 Not Found') { if (Conf['404 Redirect'] && d.title === '4chan - 404 Not Found') {
Redirect.init(); Redirect.init();
url = Redirect.to('file', { pathname = location.pathname.split('/');
boardID: pathname[1], URL = Redirect.to('file', {
filename: pathname[3] boardID: g.BOARD.ID,
filename: pathname[pathname.length - 1]
}); });
if (url) { if (URL) {
return location.href = url; return location.replace(URL);
} }
} }
}); });
@ -10286,7 +10291,7 @@
return $.ready(Main.initReady); return $.ready(Main.initReady);
}, },
initStyle: function() { initStyle: function() {
var MutationObserver, mainStyleSheet, observer, setStyle, style, styleSheets, _ref; var mainStyleSheet, observer, setStyle, style, styleSheets, _ref;
$.off(d, '4chanMainInit', Main.initStyle); $.off(d, '4chanMainInit', Main.initStyle);
if (!Main.isThisPageLegit() || $.hasClass(doc, 'fourchan-x')) { if (!Main.isThisPageLegit() || $.hasClass(doc, 'fourchan-x')) {
@ -10324,7 +10329,7 @@
if (!mainStyleSheet) { if (!mainStyleSheet) {
return; return;
} }
if (MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.OMutationObserver) { if (window.MutationObserver) {
observer = new MutationObserver(setStyle); observer = new MutationObserver(setStyle);
return observer.observe(mainStyleSheet, { return observer.observe(mainStyleSheet, {
attributes: true, attributes: true,
@ -10344,7 +10349,7 @@
threadID: g.THREADID, threadID: g.THREADID,
postID: +location.hash.match(/\d+/) postID: +location.hash.match(/\d+/)
}); });
location.href = href || ("/" + g.BOARD + "/"); location.replace(href || ("/" + g.BOARD + "/"));
} }
return; return;
} }

View File

@ -25,10 +25,10 @@
"grunt-concurrent": "~0.2.0", "grunt-concurrent": "~0.2.0",
"grunt-contrib-clean": "~0.4.1", "grunt-contrib-clean": "~0.4.1",
"grunt-contrib-coffee": "~0.7.0", "grunt-contrib-coffee": "~0.7.0",
"grunt-contrib-compress": "~0.5.0", "grunt-contrib-compress": "~0.5.1",
"grunt-contrib-concat": "~0.3.0", "grunt-contrib-concat": "~0.3.0",
"grunt-contrib-copy": "~0.4.1", "grunt-contrib-copy": "~0.4.1",
"grunt-contrib-watch": "~0.4.3", "grunt-contrib-watch": "~0.4.4",
"grunt-shell": "~0.2.2" "grunt-shell": "~0.2.2"
}, },
"repository": { "repository": {

View File

@ -114,8 +114,8 @@ Redirect =
'http': true 'http': true
'https': true 'https': true
'software': 'fuuka' 'software': 'fuuka'
'boards': ['3', 'cgl', 'ck', 'fa', 'ic', 'jp', 'lit', 'q', 's4s', 'tg', 'vr'] 'boards': ['3', 'cgl', 'ck', 'fa', 'ic', 'jp', 'lit', 'q', 'tg', 'vr']
'files': ['3', 'cgl', 'ck', 'fa', 'ic', 'jp', 'lit', 'q', 's4s', 'vr'] 'files': ['3', 'cgl', 'ck', 'fa', 'ic', 'jp', 'lit', 'q', 'vr']
to: (dest, data) -> to: (dest, data) ->
archive = (if dest is 'search' then Redirect.thread else Redirect[dest])[data.boardID] archive = (if dest is 'search' then Redirect.thread else Redirect[dest])[data.boardID]

View File

@ -8,11 +8,6 @@ Filter =
for key of Config.filter for key of Config.filter
@filters[key] = [] @filters[key] = []
if Conf[key] is undefined
# XXX hopefully tmp fix for the rare people getting this mysterious error:
# "Filter" initialization crashed. TypeError: Cannot call method 'split' of undefined
$.delete key
continue
for filter in Conf[key].split '\n' for filter in Conf[key].split '\n'
continue if filter[0] is '#' continue if filter[0] is '#'

View File

@ -116,7 +116,7 @@ Build =
'' ''
if file?.isDeleted if file?.isDeleted
fileHtml = if isOP fileHTML = if isOP
"<div class=file id=f#{postID}><div class=fileInfo></div><span class=fileThumb>" + "<div class=file id=f#{postID}><div class=fileInfo></div><span class=fileThumb>" +
"<img src='#{staticPath}filedeleted.gif' alt='File deleted.' class=fileDeletedRes>" + "<img src='#{staticPath}filedeleted.gif' alt='File deleted.' class=fileDeletedRes>" +
"</span></div>" "</span></div>"

View File

@ -614,6 +614,10 @@ http://iqdb.org/?url=%TURL
'k' 'k'
'Select previous reply.' 'Select previous reply.'
] ]
'Deselect reply': [
'Shift+d'
'Deselect reply.'
]
'Hide': [ 'Hide': [
'x' 'x'
'Hide thread.' 'Hide thread.'

View File

@ -163,9 +163,9 @@ Get =
when '[/b]' when '[/b]'
'</b>' '</b>'
when '[spoiler]' when '[spoiler]'
'<span class=spoiler>' '<s>'
when '[/spoiler]' when '[/spoiler]'
'</span>' '</s>'
when '[code]' when '[code]'
'<pre class=prettyprint>' '<pre class=prettyprint>'
when '[/code]' when '[/code]'

View File

@ -294,7 +294,7 @@ Header =
$('input[name=boardnav]', settings).focus() $('input[name=boardnav]', settings).focus()
hashScroll: -> hashScroll: ->
return unless (hash = @location.hash) and post = $.id hash[1..] return unless (hash = @location.hash[1..]) and post = $.id hash
return if (Get.postFromRoot post).isHidden return if (Get.postFromRoot post).isHidden
Header.scrollToPost post Header.scrollToPost post

View File

@ -49,10 +49,11 @@ Main =
$.ready -> $.ready ->
if Conf['404 Redirect'] and d.title is '4chan - 404 Not Found' if Conf['404 Redirect'] and d.title is '4chan - 404 Not Found'
Redirect.init() Redirect.init()
url = Redirect.to 'file', pathname = location.pathname.split '/'
boardID: pathname[1] URL = Redirect.to 'file',
filename: pathname[3] boardID: g.BOARD.ID
location.href = url if url filename: pathname[pathname.length - 1]
location.replace URL if URL
return return
init = (features) -> init = (features) ->
@ -165,13 +166,12 @@ Main =
$.addClass doc, style $.addClass doc, style
setStyle() setStyle()
return unless mainStyleSheet return unless mainStyleSheet
if MutationObserver = window.MutationObserver or window.WebKitMutationObserver or window.OMutationObserver if window.MutationObserver
observer = new MutationObserver setStyle observer = new MutationObserver setStyle
observer.observe mainStyleSheet, observer.observe mainStyleSheet,
attributes: true attributes: true
attributeFilter: ['href'] attributeFilter: ['href']
else else
# XXX this doesn't seem to work?
$.on mainStyleSheet, 'DOMAttrModified', setStyle $.on mainStyleSheet, 'DOMAttrModified', setStyle
initReady: -> initReady: ->
@ -181,7 +181,7 @@ Main =
boardID: g.BOARD.ID boardID: g.BOARD.ID
threadID: g.THREADID threadID: g.THREADID
postID: +location.hash.match /\d+/ # post number or 0 postID: +location.hash.match /\d+/ # post number or 0
location.href = href or "/#{g.BOARD}/" location.replace href or "/#{g.BOARD}/"
return return
unless $.hasClass doc, 'fourchan-x' unless $.hasClass doc, 'fourchan-x'

View File

@ -358,9 +358,11 @@ $.get = (key, val, cb) ->
else else
(syncItems or= {})[key] = val (syncItems or= {})[key] = val
items = {}
count = 0 count = 0
done = (item) -> done = (item) ->
{lastError} = chrome.runtime
if lastError
c.error lastError, lastError.message or 'No message.'
$.extend items, item $.extend items, item
cb items unless --count cb items unless --count

View File

@ -33,9 +33,9 @@ class DataBoard
delete @data.boards[boardID] delete @data.boards[boardID]
set: ({boardID, threadID, postID, val}) -> set: ({boardID, threadID, postID, val}) ->
if postID if postID isnt undefined
((@data.boards[boardID] or= {})[threadID] or= {})[postID] = val ((@data.boards[boardID] or= {})[threadID] or= {})[postID] = val
else if threadID else if threadID isnt undefined
(@data.boards[boardID] or= {})[threadID] = val (@data.boards[boardID] or= {})[threadID] = val
else else
@data.boards[boardID] = val @data.boards[boardID] = val
@ -60,12 +60,7 @@ class DataBoard
clean: -> clean: ->
for boardID, val of @data.boards for boardID, val of @data.boards
# XXX tmp fix for users that had the `null` @deleteIfEmpty {boardID}
# value for a board with the Unread features:
if typeof @data.boards[boardID] isnt 'object'
delete @data.boards[boardID]
else
@deleteIfEmpty {boardID}
now = Date.now() now = Date.now()
if (@data.lastChecked or 0) < now - 2 * $.HOUR if (@data.lastChecked or 0) < now - 2 * $.HOUR

View File

@ -30,17 +30,7 @@ PSAHiding =
href: 'javascript:;' href: 'javascript:;'
$.on btn, 'click', PSAHiding.toggle $.on btn, 'click', PSAHiding.toggle
# XXX remove hiddenPSAs workaround in the future. $.get 'hiddenPSA', 0, ({hiddenPSA}) ->
items =
hiddenPSA: 0
hiddenPSAs: null
$.get items, ({hiddenPSA, hiddenPSAs}) ->
if hiddenPSAs
$.delete 'hiddenPSAs'
if hiddenPSAs.contains psa.textContent.replace(/\W+/g, '').toLowerCase()
hiddenPSA = +$.id('globalMessage').dataset.utc
$.set 'hiddenPSA', hiddenPSA
PSAHiding.sync hiddenPSA PSAHiding.sync hiddenPSA
$.before psa, btn $.before psa, btn
$.rmClass doc, 'hide-announcement' $.rmClass doc, 'hide-announcement'

View File

@ -7,7 +7,7 @@ Fourchan =
$.globalEval """ $.globalEval """
window.addEventListener('prettyprint', function(e) { window.addEventListener('prettyprint', function(e) {
var pre = e.detail; var pre = e.detail;
pre.innerHTML = prettyPrintOne(pre.innerHTML.replace(/\\s/g, '&nbsp;')); pre.innerHTML = prettyPrintOne(pre.innerHTML);
}, false); }, false);
""" """
Post::callbacks.push Post::callbacks.push

View File

@ -111,6 +111,8 @@ Keybinds =
Keybinds.hl +1, threadRoot Keybinds.hl +1, threadRoot
when Conf['Previous reply'] when Conf['Previous reply']
Keybinds.hl -1, threadRoot Keybinds.hl -1, threadRoot
when Conf['Deselect reply']
Keybinds.hl 0, threadRoot
when Conf['Hide'] when Conf['Hide']
ThreadHiding.toggle thread if g.VIEW is 'index' ThreadHiding.toggle thread if g.VIEW is 'index'
else else
@ -194,6 +196,10 @@ Keybinds =
location.href = url location.href = url
hl: (delta, thread) -> hl: (delta, thread) ->
unless delta
if postEl = $ '.reply.highlight', thread
$.rmClass postEl, 'highlight'
return
if Conf['Fixed Header'] and Conf['Bottom header'] if Conf['Fixed Header'] and Conf['Bottom header']
topMargin = 0 topMargin = 0
else else

View File

@ -1,14 +1,13 @@
Report = Report =
init: -> init: ->
return unless /report/.test location.search return unless /report/.test(location.search) and d.cookie.indexOf('pass_enabled=1') is -1
$.ready @ready $.asap (-> $.id 'recaptcha_response_field'), Report.ready
ready: -> ready: ->
form = $ 'form'
field = $.id 'recaptcha_response_field' field = $.id 'recaptcha_response_field'
$.on field, 'keydown', (e) -> $.on field, 'keydown', (e) ->
$.globalEval 'Recaptcha.reload("t")' if e.keyCode is 8 and not field.value $.globalEval 'Recaptcha.reload("t")' if e.keyCode is 8 and not field.value
$.on form, 'submit', (e) -> $.on $('form'), 'submit', (e) ->
e.preventDefault() e.preventDefault()
response = field.value.trim() response = field.value.trim()
field.value = "#{response} #{response}" unless /\s/.test response field.value = "#{response} #{response}" unless /\s/.test response
form.submit() @submit()

View File

@ -48,7 +48,11 @@ ThreadStats =
(if thread.fileLimit and !thread.isSticky then $.addClass else $.rmClass) fileCountEl, 'warning' (if thread.fileLimit and !thread.isSticky then $.addClass else $.rmClass) fileCountEl, 'warning'
fetchPage: -> fetchPage: ->
return if ThreadStats.thread.isDead or !Conf["Page Count in Stats"] return if !Conf["Page Count in Stats"]
if ThreadStats.thread.isDead
ThreadStats.pageCountEl.textContent = 'Dead'
$.addClass ThreadStats.pageCountEl, 'warning'
return
setTimeout ThreadStats.fetchPage, 2 * $.MINUTE setTimeout ThreadStats.fetchPage, 2 * $.MINUTE
$.ajax "//api.4chan.org/#{ThreadStats.thread.board}/threads.json", onload: ThreadStats.onThreadsLoad, $.ajax "//api.4chan.org/#{ThreadStats.thread.board}/threads.json", onload: ThreadStats.onThreadsLoad,
headers: 'If-Modified-Since': ThreadStats.lastModified headers: 'If-Modified-Since': ThreadStats.lastModified

View File

@ -288,53 +288,46 @@ ThreadUpdater =
post.kill true post.kill true
deletedFiles.push post deletedFiles.push post
if ThreadUpdater.postID and ThreadUpdater.postID is ID sendEvent = ->
ThreadUpdater.foundPost = true $.event 'ThreadUpdate',
404: false
thread: ThreadUpdater.thread
newPosts: posts
deletedPosts: deletedPosts
deletedFiles: deletedFiles
postCount: OP.replies + 1
fileCount: OP.images + (!!ThreadUpdater.thread.OP.file and !ThreadUpdater.thread.OP.file.isDead)
unless count unless count
ThreadUpdater.set 'status', null, null ThreadUpdater.set 'status', null, null
ThreadUpdater.outdateCount++ ThreadUpdater.outdateCount++
sendEvent()
return
ThreadUpdater.set 'status', "+#{count}", 'new'
ThreadUpdater.outdateCount = 0
if Conf['Beep'] and d.hidden and Unread.posts and !Unread.posts.length
unless ThreadUpdater.audio
ThreadUpdater.audio = $.el 'audio', src: ThreadUpdater.beep
ThreadUpdater.audio.play()
ThreadUpdater.lastPost = posts[count - 1].ID
Main.callbackNodes Post, posts
scroll = Conf['Auto Scroll'] and ThreadUpdater.scrollBG() and
ThreadUpdater.root.getBoundingClientRect().bottom - doc.clientHeight < 25
$.add ThreadUpdater.root, nodes
sendEvent()
if scroll
if Conf['Bottom Scroll']
<% if (type === 'crx') { %>d.body<% } else { %>doc<% } %>.scrollTop = d.body.clientHeight
else
Header.scrollToPost nodes[0]
# Enable 4chan features.
threadID = ThreadUpdater.thread.ID
{length} = $$ '.thread > .postContainer', ThreadUpdater.root
if Conf['Enable 4chan\'s Extension']
$.globalEval "Parser.parseThread(#{threadID}, #{-count})"
else else
ThreadUpdater.set 'status', "+#{count}", 'new' Fourchan.parseThread threadID, length - count, length
ThreadUpdater.outdateCount = 0
if Conf['Beep'] and d.hidden and Unread.posts and !Unread.posts.length
unless ThreadUpdater.audio
ThreadUpdater.audio = $.el 'audio', src: ThreadUpdater.beep
ThreadUpdater.audio.play()
ThreadUpdater.lastPost = posts[count - 1].ID
Main.callbackNodes Post, posts
scroll = Conf['Auto Scroll'] and ThreadUpdater.scrollBG() and
ThreadUpdater.root.getBoundingClientRect().bottom - doc.clientHeight < 25
for key, post of posts
continue unless posts.hasOwnProperty key
root = post.nodes.root
if post.cb
unless post.cb.call post
$.add ThreadUpdater.root, root
else
$.add ThreadUpdater.root, root
if scroll
if Conf['Bottom Scroll']
<% if (type === 'crx') { %>d.body<% } else { %>doc<% } %>.scrollTop = d.body.clientHeight
else
Header.scrollToPost root if root
$.queueTask ->
# Enable 4chan features.
threadID = ThreadUpdater.thread.ID
{length} = $$ '.thread > .postContainer', ThreadUpdater.root
Fourchan.parseThread threadID, length - count, length
$.event 'ThreadUpdate',
404: false
thread: ThreadUpdater.thread
newPosts: posts
deletedPosts: deletedPosts
deletedFiles: deletedFiles
postCount: OP.replies + 1
fileCount: OP.images + (!!ThreadUpdater.thread.OP.file and !ThreadUpdater.thread.OP.file.isDead)

View File

@ -43,11 +43,21 @@ Unread =
break if prevID is post.ID break if prevID is post.ID
prevID = post.ID prevID = post.ID
break unless post.isHidden break unless post.isHidden
root.scrollIntoView false onload = -> root.scrollIntoView false if checkPosition root
return else
# Scroll to the last read post. # Scroll to the last read post.
posts = Object.keys Unread.thread.posts posts = Object.keys Unread.thread.posts
Header.scrollToPost Unread.thread.posts[posts[posts.length - 1]].nodes.root {root} = Unread.thread.posts[posts[posts.length - 1]].nodes
onload = -> Header.scrollToPost root if checkPosition root
checkPosition = (target) ->
# Don't scroll to the target if
# - it's visible.
# - we've scrolled past it.
{top, height} = target.getBoundingClientRect()
top + height - doc.clientHeight > 0
# Prevent the browser to scroll back to
# the previous scroll location on page load.
$.on window, 'load', onload
sync: -> sync: ->
lastReadPost = Unread.db.get lastReadPost = Unread.db.get
@ -61,8 +71,8 @@ Unread =
Unread.setLine() Unread.setLine()
Unread.update() Unread.update()
addPosts: (newPosts) -> addPosts: (posts) ->
for post in newPosts for post in posts
{ID} = post {ID} = post
if ID <= Unread.lastReadPost or post.isHidden if ID <= Unread.lastReadPost or post.isHidden
continue continue
@ -76,7 +86,7 @@ Unread =
Unread.addPostQuotingYou post Unread.addPostQuotingYou post
if Conf['Unread Line'] if Conf['Unread Line']
# Force line on visible threads if there were no unread posts previously. # Force line on visible threads if there were no unread posts previously.
Unread.setLine newPosts.contains Unread.posts[0] Unread.setLine Unread.posts[0] in posts
Unread.read() Unread.read()
Unread.update() Unread.update()

View File

@ -779,7 +779,7 @@ QR =
img: imgContainer.firstChild img: imgContainer.firstChild
input: input input: input
if MutationObserver = window.MutationObserver or window.WebKitMutationObserver or window.OMutationObserver if window.MutationObserver
observer = new MutationObserver @load.bind @ observer = new MutationObserver @load.bind @
observer.observe @nodes.challenge, observer.observe @nodes.challenge,
childList: true childList: true