This commit is contained in:
James Campos 2012-03-16 00:01:37 -07:00
parent 3b0f041648
commit 09e19ddc65
2 changed files with 181 additions and 181 deletions

View File

@ -73,7 +73,7 @@
*/ */
(function() { (function() {
var $, $$, Anonymize, AutoGif, Conf, Config, DAY, ExpandComment, ExpandThread, Favicon, FileInfo, Filter, GetTitle, HOUR, ImageExpand, ImageHover, Keybinds, MINUTE, Main, NAMESPACE, Nav, Options, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, Quotify, Redirect, ReplyHiding, ReportButton, RevealSpoilers, SECOND, Sauce, StrikethroughQuotes, ThreadHiding, ThreadStats, Threading, Time, TitlePost, UI, Unread, Updater, VERSION, Watcher, d, flatten, _base; var $, $$, Anonymize, AutoGif, Conf, Config, DAY, ExpandComment, ExpandThread, Favicon, FileInfo, Filter, GetTitle, HOUR, ImageExpand, ImageHover, Keybinds, MINUTE, Main, NAMESPACE, Nav, Options, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, Quotify, Redirect, ReplyHiding, ReportButton, RevealSpoilers, SECOND, Sauce, StrikethroughQuotes, ThreadHiding, ThreadStats, Threading, Time, TitlePost, UI, Unread, Updater, VERSION, Watcher, flatten, _base;
Config = { Config = {
main: { main: {
@ -208,24 +208,10 @@
} }
})(null, Config); })(null, Config);
NAMESPACE = '4chan_x.';
VERSION = '2.29.1';
SECOND = 1000;
MINUTE = 60 * SECOND;
HOUR = 60 * MINUTE;
DAY = 24 * HOUR;
d = document;
UI = { UI = {
dialog: function(id, position, html) { dialog: function(id, position, html) {
var el, saved; var el, saved;
el = d.createElement('div'); el = $.d.createElement('div');
el.className = 'reply dialog'; el.className = 'reply dialog';
el.innerHTML = html; el.innerHTML = html;
el.id = id; el.id = id;
@ -237,13 +223,13 @@
var el, rect; var el, rect;
e.preventDefault(); e.preventDefault();
UI.el = el = this.parentNode; UI.el = el = this.parentNode;
d.addEventListener('mousemove', UI.drag, false); $.d.addEventListener('mousemove', UI.drag, false);
d.addEventListener('mouseup', UI.dragend, false); $.d.addEventListener('mouseup', UI.dragend, false);
rect = el.getBoundingClientRect(); rect = el.getBoundingClientRect();
UI.dx = e.clientX - rect.left; UI.dx = e.clientX - rect.left;
UI.dy = e.clientY - rect.top; UI.dy = e.clientY - rect.top;
UI.width = d.body.clientWidth - el.offsetWidth; UI.width = $.d.body.clientWidth - el.offsetWidth;
return UI.height = d.body.clientHeight - el.offsetHeight; return UI.height = $.d.body.clientHeight - el.offsetHeight;
}, },
drag: function(e) { drag: function(e) {
var bottom, left, right, style, top; var bottom, left, right, style, top;
@ -263,15 +249,15 @@
var el; var el;
el = UI.el; el = UI.el;
localStorage["" + NAMESPACE + el.id + ".position"] = el.style.cssText; localStorage["" + NAMESPACE + el.id + ".position"] = el.style.cssText;
d.removeEventListener('mousemove', UI.drag, false); $.d.removeEventListener('mousemove', UI.drag, false);
return d.removeEventListener('mouseup', UI.dragend, false); return $.d.removeEventListener('mouseup', UI.dragend, false);
}, },
hover: function(e) { hover: function(e) {
var clientHeight, clientWidth, clientX, clientY, el, height, style, top, _ref; var clientHeight, clientWidth, clientX, clientY, el, height, style, top, _ref;
clientX = e.clientX, clientY = e.clientY; clientX = e.clientX, clientY = e.clientY;
el = UI.el; el = UI.el;
style = el.style; style = el.style;
_ref = d.body, clientHeight = _ref.clientHeight, clientWidth = _ref.clientWidth; _ref = $.d.body, clientHeight = _ref.clientHeight, clientWidth = _ref.clientWidth;
height = el.offsetHeight; height = el.offsetHeight;
top = clientY - 120; top = clientY - 120;
style.top = clientHeight <= height || top <= 0 ? 0 : top + height >= clientHeight ? clientHeight - height : top; style.top = clientHeight <= height || top <= 0 ? 0 : top + height >= clientHeight ? clientHeight - height : top;
@ -296,7 +282,7 @@
*/ */
$ = function(selector, root) { $ = function(selector, root) {
if (root == null) root = d.body; if (root == null) root = $.d.body;
return root.querySelector(selector); return root.querySelector(selector);
}; };
@ -308,17 +294,30 @@
} }
}; };
NAMESPACE = '4chan_x.';
VERSION = '2.29.1';
SECOND = 1000;
MINUTE = 60 * SECOND;
HOUR = 60 * MINUTE;
DAY = 24 * HOUR;
$.extend($, { $.extend($, {
log: typeof (_base = console.log).bind === "function" ? _base.bind(console) : void 0, log: typeof (_base = console.log).bind === "function" ? _base.bind(console) : void 0,
d: document,
engine: /WebKit|Presto|Gecko/.exec(navigator.userAgent)[0].toLowerCase(), engine: /WebKit|Presto|Gecko/.exec(navigator.userAgent)[0].toLowerCase(),
ready: function(fc) { ready: function(fc) {
var cb; var cb;
if (/interactive|complete/.test(d.readyState)) return setTimeout(fc); if (/interactive|complete/.test($.d.readyState)) return setTimeout(fc);
cb = function() { cb = function() {
$.off(d, 'DOMContentLoaded', cb); $.off($.d, 'DOMContentLoaded', cb);
return fc(); return fc();
}; };
return $.on(d, 'DOMContentLoaded', cb); return $.on($.d, 'DOMContentLoaded', cb);
}, },
sync: function(key, cb) { sync: function(key, cb) {
return $.on(window, 'storage', function(e) { return $.on(window, 'storage', function(e) {
@ -326,7 +325,7 @@
}); });
}, },
id: function(id) { id: function(id) {
return d.getElementById(id); return $.d.getElementById(id);
}, },
ajax: function(url, callbacks, opts) { ajax: function(url, callbacks, opts) {
var form, headers, key, r, type, upCallbacks, val; var form, headers, key, r, type, upCallbacks, val;
@ -390,12 +389,12 @@
style = $.el('style', { style = $.el('style', {
textContent: css textContent: css
}); });
$.add(d.head, style); $.add($.d.head, style);
return style; return style;
}, },
x: function(path, root) { x: function(path, root) {
if (root == null) root = d.body; if (root == null) root = $.d.body;
return d.evaluate(path, root, null, 8, null).singleNodeValue; return $.d.evaluate(path, root, null, 8, null).singleNodeValue;
}, },
addClass: function(el, className) { addClass: function(el, className) {
return el.classList.add(className); return el.classList.add(className);
@ -407,12 +406,12 @@
return el.parentNode.removeChild(el); return el.parentNode.removeChild(el);
}, },
tn: function(s) { tn: function(s) {
return d.createTextNode(s); return $.d.createTextNode(s);
}, },
nodes: function(nodes) { nodes: function(nodes) {
var frag, node, _i, _len; var frag, node, _i, _len;
if (nodes instanceof Node) return nodes; if (nodes instanceof Node) return nodes;
frag = d.createDocumentFragment(); frag = $.d.createDocumentFragment();
for (_i = 0, _len = nodes.length; _i < _len; _i++) { for (_i = 0, _len = nodes.length; _i < _len; _i++) {
node = nodes[_i]; node = nodes[_i];
frag.appendChild(node); frag.appendChild(node);
@ -436,7 +435,7 @@
}, },
el: function(tag, properties) { el: function(tag, properties) {
var el; var el;
el = d.createElement(tag); el = $.d.createElement(tag);
if (properties) $.extend(el, properties); if (properties) $.extend(el, properties);
return el; return el;
}, },
@ -530,7 +529,7 @@
}); });
$$ = function(selector, root) { $$ = function(selector, root) {
if (root == null) root = d.body; if (root == null) root = $.d.body;
return Array.prototype.slice.call(root.querySelectorAll(selector)); return Array.prototype.slice.call(root.querySelectorAll(selector));
}; };
@ -658,7 +657,7 @@
comment: function(post) { comment: function(post) {
var data, i, nodes, text, _ref; var data, i, nodes, text, _ref;
text = []; text = [];
nodes = d.evaluate('.//br|.//text()', post.el.lastChild, null, 7, null); 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--) { 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'); text.push((data = nodes.snapshotItem(i).data) ? data : '\n');
} }
@ -736,10 +735,10 @@
a.textContent = "" + req.status + " " + req.statusText; a.textContent = "" + req.status + " " + req.statusText;
return; return;
} }
doc = d.implementation.createHTMLDocument(null); doc = $.d.implementation.createHTMLDocument(null);
doc.documentElement.innerHTML = req.responseText; doc.documentElement.innerHTML = req.responseText;
Threading.op($('body > form', doc).firstChild); Threading.op($('body > form', doc).firstChild);
node = d.importNode(doc.getElementById(replyID)); node = $.d.importNode(doc.getElementById(replyID));
quotes = node.getElementsByClassName('quotelink'); quotes = node.getElementsByClassName('quotelink');
for (_i = 0, _len = quotes.length; _i < _len; _i++) { for (_i = 0, _len = quotes.length; _i < _len; _i++) {
quote = quotes[_i]; quote = quotes[_i];
@ -839,13 +838,13 @@
return; return;
} }
a.textContent = a.textContent.replace('\u00d7 Loading...', '-'); a.textContent = a.textContent.replace('\u00d7 Loading...', '-');
doc = d.implementation.createHTMLDocument(null); doc = $.d.implementation.createHTMLDocument(null);
doc.documentElement.innerHTML = req.responseText; doc.documentElement.innerHTML = req.responseText;
nodes = []; nodes = [];
_ref = $$('.reply', doc); _ref = $$('.reply', doc);
for (_i = 0, _len = _ref.length; _i < _len; _i++) { for (_i = 0, _len = _ref.length; _i < _len; _i++) {
reply = _ref[_i]; reply = _ref[_i];
table = d.importNode(reply.parentNode.parentNode.parentNode); table = $.d.importNode(reply.parentNode.parentNode.parentNode);
_ref2 = $$('.quotelink', table); _ref2 = $$('.quotelink', table);
for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) { for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) {
quote = _ref2[_j]; quote = _ref2[_j];
@ -935,7 +934,7 @@
node = _ref[_i]; node = _ref[_i];
node.removeAttribute('accesskey'); node.removeAttribute('accesskey');
} }
return $.on(d, 'keydown', Keybinds.keydown); return $.on($.d, 'keydown', Keybinds.keydown);
}, },
keydown: function(e) { keydown: function(e) {
var key, o, range, selEnd, selStart, ta, thread, value, _ref, _ref2; var key, o, range, selEnd, selStart, ta, thread, value, _ref, _ref2;
@ -1131,7 +1130,7 @@
td.className = 'reply'; td.className = 'reply';
td.removeAttribute('tabindex'); td.removeAttribute('tabindex');
rect = td.getBoundingClientRect(); rect = td.getBoundingClientRect();
if (rect.bottom >= 0 && rect.top <= d.body.clientHeight) { if (rect.bottom >= 0 && rect.top <= $.d.body.clientHeight) {
next = delta === +1 ? $.x('following::td[@class="reply"]', td) : $.x('preceding::td[@class="reply"]', td); next = delta === +1 ? $.x('following::td[@class="reply"]', td) : $.x('preceding::td[@class="reply"]', td);
if (!next) { if (!next) {
td.className = 'replyhl'; td.className = 'replyhl';
@ -1143,7 +1142,7 @@
return; return;
} }
rect = next.getBoundingClientRect(); rect = next.getBoundingClientRect();
if (rect.top < 0 || rect.bottom > d.body.clientHeight) { if (rect.top < 0 || rect.bottom > $.d.body.clientHeight) {
next.scrollIntoView(delta === -1); next.scrollIntoView(delta === -1);
} }
next.className = 'replyhl'; next.className = 'replyhl';
@ -1157,7 +1156,7 @@
for (_i = 0, _len = replies.length; _i < _len; _i++) { for (_i = 0, _len = replies.length; _i < _len; _i++) {
reply = replies[_i]; reply = replies[_i];
rect = reply.getBoundingClientRect(); rect = reply.getBoundingClientRect();
if (delta === +1 && rect.top >= 0 || delta === -1 && rect.bottom <= d.body.clientHeight) { if (delta === +1 && rect.top >= 0 || delta === -1 && rect.bottom <= $.d.body.clientHeight) {
reply.className = 'replyhl'; reply.className = 'replyhl';
reply.tabIndex = 0; reply.tabIndex = 0;
reply.focus(); reply.focus();
@ -1184,7 +1183,7 @@
$.on(prev, 'click', this.prev); $.on(prev, 'click', this.prev);
$.on(next, 'click', this.next); $.on(next, 'click', this.next);
$.add(span, [prev, $.tn(' '), next]); $.add(span, [prev, $.tn(' '), next]);
return $.add(d.body, span); return $.add($.d.body, span);
}, },
prev: function() { prev: function() {
if (Main.REPLY) { if (Main.REPLY) {
@ -1195,7 +1194,7 @@
}, },
next: function() { next: function() {
if (Main.REPLY) { if (Main.REPLY) {
return window.scrollTo(0, d.body.scrollHeight); return window.scrollTo(0, $.d.body.scrollHeight);
} else { } else {
return Nav.scroll(+1); return Nav.scroll(+1);
} }
@ -1244,7 +1243,7 @@
if (!Main.REPLY) $('select', QR.el).value = 'new'; if (!Main.REPLY) $('select', QR.el).value = 'new';
return $('textarea', QR.el).focus(); return $('textarea', QR.el).focus();
}); });
form = d.forms[0]; form = $.d.forms[0];
$.before(form, link); $.before(form, link);
} }
if (/chrome/i.test(navigator.userAgent)) { if (/chrome/i.test(navigator.userAgent)) {
@ -1272,21 +1271,21 @@
return setTimeout(loadChecking, 500, this); return setTimeout(loadChecking, 500, this);
} }
}); });
$.add(d.head, iframe); $.add($.d.head, iframe);
} }
script = $.el('script', { script = $.el('script', {
textContent: 'Recaptcha.focus_response_field=function(){}' textContent: 'Recaptcha.focus_response_field=function(){}'
}); });
$.add(d.head, script); $.add($.d.head, script);
$.rm(script); $.rm(script);
if (Conf['Persistent QR']) { if (Conf['Persistent QR']) {
QR.dialog(); QR.dialog();
if (Conf['Auto Hide QR']) QR.hide(); if (Conf['Auto Hide QR']) QR.hide();
} }
$.on(d, 'dragover', QR.dragOver); $.on($.d, 'dragover', QR.dragOver);
$.on(d, 'drop', QR.dropFile); $.on($.d, 'drop', QR.dropFile);
$.on(d, 'dragstart', QR.drag); $.on($.d, 'dragstart', QR.drag);
return $.on(d, 'dragend', QR.drag); return $.on($.d, 'dragend', QR.drag);
}, },
node: function(post) { node: function(post) {
return $.on($('.quotejs + .quotejs', post.el), 'click', QR.quote); return $.on($('.quotejs + .quotejs', post.el), 'click', QR.quote);
@ -1305,7 +1304,7 @@
QR.message.send({ QR.message.send({
req: 'abort' req: 'abort'
}); });
d.activeElement.blur(); $.d.activeElement.blur();
$.removeClass(QR.el, 'dump'); $.removeClass(QR.el, 'dump');
_ref = QR.replies; _ref = QR.replies;
for (_i = 0, _len = _ref.length; _i < _len; _i++) { for (_i = 0, _len = _ref.length; _i < _len; _i++) {
@ -1321,7 +1320,7 @@
return QR.cleanError(); return QR.cleanError();
}, },
hide: function() { hide: function() {
d.activeElement.blur(); $.d.activeElement.blur();
$.addClass(QR.el, 'autohide'); $.addClass(QR.el, 'autohide');
return $.id('autohide').checked = true; return $.id('autohide').checked = true;
}, },
@ -1339,7 +1338,7 @@
if (node) $.replace(el.firstChild, node); if (node) $.replace(el.firstChild, node);
QR.open(); QR.open();
if (/captcha|verification/i.test(err)) $('[autocomplete]', QR.el).focus(); if (/captcha|verification/i.test(err)) $('[autocomplete]', QR.el).focus();
if (d.hidden || d.oHidden || d.mozHidden || d.webkitHidden) { if ($.d.hidden || $.d.oHidden || $.d.mozHidden || $.d.webkitHidden) {
return alert(err); return alert(err);
} }
}, },
@ -1423,8 +1422,8 @@
drag: function(e) { drag: function(e) {
var i; var i;
i = e.type === 'dragstart' ? 'off' : 'on'; i = e.type === 'dragstart' ? 'off' : 'on';
$[i](d, 'dragover', QR.dragOver); $[i]($.d, 'dragover', QR.dragOver);
return $[i](d, 'drop', QR.dropFile); return $[i]($.d, 'drop', QR.dropFile);
}, },
dragOver: function(e) { dragOver: function(e) {
e.preventDefault(); e.preventDefault();
@ -1830,8 +1829,8 @@
QR.status(); QR.status();
QR.cooldown.init(); QR.cooldown.init();
QR.captcha.init(); QR.captcha.init();
$.add(d.body, QR.el); $.add($.d.body, QR.el);
e = d.createEvent('CustomEvent'); e = $.d.createEvent('CustomEvent');
e.initEvent('QRDialogCreation', true, false); e.initEvent('QRDialogCreation', true, false);
return QR.el.dispatchEvent(e); return QR.el.dispatchEvent(e);
}, },
@ -1888,7 +1887,7 @@
upfile: reply.file, upfile: reply.file,
spoiler: reply.spoiler, spoiler: reply.spoiler,
mode: 'regist', mode: 'regist',
pwd: (m = d.cookie.match(/4chan_pass=([^;]+)/)) ? decodeURIComponent(m[1]) : $('[name=pwd]').value, pwd: (m = $.d.cookie.match(/4chan_pass=([^;]+)/)) ? decodeURIComponent(m[1]) : $('[name=pwd]').value,
recaptcha_challenge_field: challenge, recaptcha_challenge_field: challenge,
recaptcha_response_field: response + ' ' recaptcha_response_field: response + ' '
}; };
@ -2318,8 +2317,8 @@
return e.stopPropagation(); return e.stopPropagation();
}); });
$.add(overlay, dialog); $.add(overlay, dialog);
$.add(d.body, overlay); $.add($.d.body, overlay);
d.body.style.setProperty('overflow', 'hidden', null); $.d.body.style.setProperty('overflow', 'hidden', null);
Options.backlink.call(back); Options.backlink.call(back);
Options.time.call(time); Options.time.call(time);
Options.fileInfo.call(fileInfoR); Options.fileInfo.call(fileInfoR);
@ -2328,7 +2327,7 @@
}, },
close: function() { close: function() {
$.rm(this); $.rm(this);
return d.body.style.removeProperty('overflow'); return $.d.body.style.removeProperty('overflow');
}, },
clearHidden: function() { clearHidden: function() {
$["delete"]("hiddenReplies/" + Main.BOARD + "/"); $["delete"]("hiddenReplies/" + Main.BOARD + "/");
@ -2524,7 +2523,7 @@
$.on(input, 'click', this.update); $.on(input, 'click', this.update);
} }
} }
$.add(d.body, dialog); $.add($.d.body, dialog);
this.retryCoef = 10; this.retryCoef = 10;
return this.lastModified = 0; return this.lastModified = 0;
}, },
@ -2552,7 +2551,7 @@
return Updater.scrollBG = this.checked ? function() { return Updater.scrollBG = this.checked ? function() {
return true; return true;
} : function() { } : function() {
return !(d.hidden || d.oHidden || d.mozHidden || d.webkitHidden); return !($.d.hidden || $.d.oHidden || $.d.mozHidden || $.d.webkitHidden);
}; };
}, },
update: function() { update: function() {
@ -2566,7 +2565,7 @@
if (Conf['Unread Count']) { if (Conf['Unread Count']) {
Unread.title = Unread.title.match(/^.+-/)[0] + ' 404'; Unread.title = Unread.title.match(/^.+-/)[0] + ' 404';
} else { } else {
d.title = d.title.match(/^.+-/)[0] + ' 404'; $.d.title = $.d.title.match(/^.+-/)[0] + ' 404';
} }
Unread.update(true); Unread.update(true);
QR.message.send({ QR.message.send({
@ -2591,7 +2590,7 @@
return; return;
} }
Updater.lastModified = this.getResponseHeader('Last-Modified'); Updater.lastModified = this.getResponseHeader('Last-Modified');
doc = d.implementation.createHTMLDocument(null); doc = $.d.implementation.createHTMLDocument(null);
doc.documentElement.innerHTML = this.responseText; doc.documentElement.innerHTML = this.responseText;
id = $('input', Updater.br.previousElementSibling).name; id = $('input', Updater.br.previousElementSibling).name;
nodes = []; nodes = [];
@ -2602,7 +2601,7 @@
nodes.push(reply.parentNode.parentNode.parentNode); nodes.push(reply.parentNode.parentNode.parentNode);
} }
newPosts = nodes.length; newPosts = nodes.length;
scroll = Conf['Scrolling'] && Updater.scrollBG() && newPosts && Updater.br.previousElementSibling.getBoundingClientRect().bottom - d.body.clientHeight < 25; scroll = Conf['Scrolling'] && Updater.scrollBG() && newPosts && Updater.br.previousElementSibling.getBoundingClientRect().bottom - $.d.body.clientHeight < 25;
if (Conf['Verbose']) { if (Conf['Verbose']) {
Updater.count.textContent = "+" + newPosts; Updater.count.textContent = "+" + newPosts;
Updater.count.className = newPosts ? 'new' : null; Updater.count.className = newPosts ? 'new' : null;
@ -2649,7 +2648,7 @@
var favicon, html, input, inputs, _i, _len; var favicon, html, input, inputs, _i, _len;
html = '<div class=move>Thread Watcher</div>'; html = '<div class=move>Thread Watcher</div>';
this.dialog = UI.dialog('watcher', 'top: 50px; left: 0px;', html); this.dialog = UI.dialog('watcher', 'top: 50px; left: 0px;', html);
$.add(d.body, this.dialog); $.add($.d.body, this.dialog);
inputs = $$('.op > input'); inputs = $$('.op > input');
for (_i = 0, _len = inputs.length; _i < _len; _i++) { for (_i = 0, _len = inputs.length; _i < _len; _i++) {
input = inputs[_i]; input = inputs[_i];
@ -3051,7 +3050,7 @@
TitlePost = { TitlePost = {
init: function() { init: function() {
return d.title = GetTitle(); return $.d.title = GetTitle();
} }
}; };
@ -3191,7 +3190,7 @@
inline.textContent = "" + req.status + " " + req.statusText; inline.textContent = "" + req.status + " " + req.statusText;
return; return;
} }
doc = d.implementation.createHTMLDocument(null); doc = $.d.implementation.createHTMLDocument(null);
doc.documentElement.innerHTML = req.responseText; doc.documentElement.innerHTML = req.responseText;
node = id === threadID ? Threading.op($('body > form', doc).firstChild) : doc.getElementById(id); node = id === threadID ? Threading.op($('body > form', doc).firstChild) : doc.getElementById(id);
newInline = QuoteInline.table(id, node.innerHTML); newInline = QuoteInline.table(id, node.innerHTML);
@ -3243,7 +3242,7 @@
id: 'qp', id: 'qp',
className: 'reply dialog' className: 'reply dialog'
}); });
$.add(d.body, qp); $.add($.d.body, qp);
id = this.hash.slice(1); id = this.hash.slice(1);
if (el = $.id(id)) { if (el = $.id(id)) {
qp.innerHTML = el.innerHTML; qp.innerHTML = el.innerHTML;
@ -3283,7 +3282,7 @@
qp.textContent = "" + req.status + " " + req.statusText; qp.textContent = "" + req.status + " " + req.statusText;
return; return;
} }
doc = d.implementation.createHTMLDocument(null); doc = $.d.implementation.createHTMLDocument(null);
doc.documentElement.innerHTML = req.responseText; doc.documentElement.innerHTML = req.responseText;
node = id === threadID ? Threading.op($('body > form', doc).firstChild) : doc.getElementById(id); node = id === threadID ? Threading.op($('body > form', doc).firstChild) : doc.getElementById(id);
qp.innerHTML = node.innerHTML; qp.innerHTML = node.innerHTML;
@ -3341,7 +3340,7 @@
node: function(post) { node: function(post) {
var a, board, data, i, id, index, m, node, nodes, quote, quotes, snapshot, text, _i, _len, _ref; var a, board, data, i, id, index, m, node, nodes, quote, quotes, snapshot, text, _i, _len, _ref;
if (post["class"] === 'inline') return; if (post["class"] === 'inline') return;
snapshot = d.evaluate('.//text()[not(parent::a)]', post.el.lastChild, null, 6, null); snapshot = $.d.evaluate('.//text()[not(parent::a)]', post.el.lastChild, null, 6, null);
for (i = 0, _ref = snapshot.snapshotLength; 0 <= _ref ? i < _ref : i > _ref; 0 <= _ref ? i++ : i--) { for (i = 0, _ref = snapshot.snapshotLength; 0 <= _ref ? i < _ref : i > _ref; 0 <= _ref ? i++ : i--) {
node = snapshot.snapshotItem(i); node = snapshot.snapshotItem(i);
data = node.data; data = node.data;
@ -3404,7 +3403,7 @@
var dialog; var dialog;
dialog = UI.dialog('stats', 'bottom: 0; left: 0;', '<div class=move><span id=postcount>0</span> / <span id=imagecount>0</span></div>'); dialog = UI.dialog('stats', 'bottom: 0; left: 0;', '<div class=move><span id=postcount>0</span> / <span id=imagecount>0</span></div>');
dialog.className = 'dialog'; dialog.className = 'dialog';
$.add(d.body, dialog); $.add($.d.body, dialog);
this.posts = this.images = 0; this.posts = this.images = 0;
this.imgLimit = (function() { this.imgLimit = (function() {
switch (Main.BOARD) { switch (Main.BOARD) {
@ -3435,7 +3434,7 @@
Unread = { Unread = {
init: function() { init: function() {
this.title = d.title; this.title = $.d.title;
this.update(); this.update();
$.on(window, 'scroll', Unread.scroll); $.on(window, 'scroll', Unread.scroll);
return Main.callbacks.push(this.node); return Main.callbacks.push(this.node);
@ -3454,7 +3453,7 @@
}, },
scroll: function() { scroll: function() {
var bottom, height, i, reply, _len, _ref; var bottom, height, i, reply, _len, _ref;
height = d.body.clientHeight; height = $.d.body.clientHeight;
_ref = Unread.replies; _ref = Unread.replies;
for (i = 0, _len = _ref.length; i < _len; i++) { for (i = 0, _len = _ref.length; i < _len; i++) {
reply = _ref[i]; reply = _ref[i];
@ -3473,7 +3472,7 @@
return; return;
} }
return this.scheduled = setTimeout((function() { return this.scheduled = setTimeout((function() {
return d.title = "(" + count + ") " + Unread.title; return $.d.title = "(" + count + ") " + Unread.title;
}), 5); }), 5);
}, },
update: function(forceUpdate) { update: function(forceUpdate) {
@ -3483,14 +3482,14 @@
if (Conf['Unread Count']) this.setTitle(count); if (Conf['Unread Count']) this.setTitle(count);
if (!(Conf['Unread Favicon'] && (count < 2 || forceUpdate))) return; if (!(Conf['Unread Favicon'] && (count < 2 || forceUpdate))) return;
Favicon.el.href = Main.dead ? count ? Favicon.unreadDead : Favicon.dead : count ? Favicon.unread : Favicon["default"]; Favicon.el.href = Main.dead ? count ? Favicon.unreadDead : Favicon.dead : count ? Favicon.unread : Favicon["default"];
return $.add(d.head, Favicon.el); return $.add($.d.head, Favicon.el);
} }
}; };
Favicon = { Favicon = {
init: function() { init: function() {
var href; var href;
this.el = $('link[rel="shortcut icon"]', d.head); this.el = $('link[rel="shortcut icon"]', $.d.head);
this.el.type = 'image/x-icon'; this.el.type = 'image/x-icon';
href = this.el.href; href = this.el.href;
this.SFW = /ws.ico$/.test(href); this.SFW = /ws.ico$/.test(href);
@ -3611,7 +3610,7 @@
id: 'ihover', id: 'ihover',
src: this.parentNode.href src: this.parentNode.href
}); });
$.add(d.body, UI.el); $.add($.d.body, UI.el);
$.on(UI.el, 'load', ImageHover.load); $.on(UI.el, 'load', ImageHover.load);
$.on(this, 'mousemove', UI.hover); $.on(this, 'mousemove', UI.hover);
return $.on(this, 'mouseout', ImageHover.mouseout); return $.on(this, 'mouseout', ImageHover.mouseout);
@ -3726,8 +3725,8 @@
thumb = a.firstChild; thumb = a.firstChild;
if (thumb.hidden) { if (thumb.hidden) {
rect = a.getBoundingClientRect(); rect = a.getBoundingClientRect();
if (rect.top < 0) d.body.scrollTop += rect.top - 42; if (rect.top < 0) $.d.body.scrollTop += rect.top - 42;
if (rect.left < 0) d.body.scrollLeft += rect.left; if (rect.left < 0) $.d.body.scrollLeft += rect.left;
return ImageExpand.contract(thumb); return ImageExpand.contract(thumb);
} else { } else {
return ImageExpand.expand(thumb); return ImageExpand.expand(thumb);
@ -3791,7 +3790,7 @@
return $.prepend(form, controls); return $.prepend(form, controls);
}, },
resize: function() { resize: function() {
return ImageExpand.style.textContent = ".fitheight img[md5] + img {max-height:" + d.body.clientHeight + "px;}"; return ImageExpand.style.textContent = ".fitheight img[md5] + img {max-height:" + $.d.body.clientHeight + "px;}";
} }
}; };
@ -3840,7 +3839,7 @@
return; return;
case 'images.4chan.org': case 'images.4chan.org':
$.ready(function() { $.ready(function() {
if (d.title === '4chan - 404') return Redirect.init(); if ($.d.title === '4chan - 404') return Redirect.init();
}); });
return; return;
} }
@ -3852,7 +3851,7 @@
now = Date.now(); now = Date.now();
if (Conf['Check for Updates'] && $.get('lastUpdate', 0) < now - 6 * HOUR) { if (Conf['Check for Updates'] && $.get('lastUpdate', 0) < now - 6 * HOUR) {
$.ready(function() { $.ready(function() {
return $.add(d.head, $.el('script', { return $.add($.d.head, $.el('script', {
src: 'https://raw.github.com/mayhemydg/4chan-x/master/latest.js' src: 'https://raw.github.com/mayhemydg/4chan-x/master/latest.js'
})); }));
}); });
@ -3896,13 +3895,13 @@
}, },
ready: function() { ready: function() {
var MutationObserver, form, nav, node, nodes, observer, _i, _j, _len, _len2, _ref, _ref2; var MutationObserver, form, nav, node, nodes, observer, _i, _j, _len, _len2, _ref, _ref2;
if (d.title === '4chan - 404') { if ($.d.title === '4chan - 404') {
Redirect.init(); Redirect.init();
return; return;
} }
if (!$.id('navtopr')) return; if (!$.id('navtopr')) return;
$.addClass(d.body, "chanx_" + (VERSION.split('.')[1])); $.addClass($.d.body, "chanx_" + (VERSION.split('.')[1]));
$.addClass(d.body, $.engine); $.addClass($.d.body, $.engine);
_ref = ['navtop', 'navbot']; _ref = ['navtop', 'navbot'];
for (_i = 0, _len = _ref.length; _i < _len; _i++) { for (_i = 0, _len = _ref.length; _i < _len; _i++) {
nav = _ref[_i]; nav = _ref[_i];
@ -3977,11 +3976,11 @@
} }
}, },
addStyle: function() { addStyle: function() {
$.off(d, 'DOMNodeInserted', Main.addStyle); $.off($.d, 'DOMNodeInserted', Main.addStyle);
if (d.head) { if ($.d.head) {
return $.addStyle(Main.css); return $.addStyle(Main.css);
} else { } else {
return $.on(d, 'DOMNodeInserted', Main.addStyle); return $.on($.d, 'DOMNodeInserted', Main.addStyle);
} }
}, },
message: function(e) { message: function(e) {

View File

@ -167,17 +167,9 @@ Conf = {}
return return
) null, Config ) null, Config
NAMESPACE = '4chan_x.'
VERSION = '2.29.1'
SECOND = 1000
MINUTE = 60*SECOND
HOUR = 60*MINUTE
DAY = 24*HOUR
d = document
UI = UI =
dialog: (id, position, html) -> dialog: (id, position, html) ->
el = d.createElement 'div' el = $.d.createElement 'div'
el.className = 'reply dialog' el.className = 'reply dialog'
el.innerHTML = html el.innerHTML = html
el.id = id el.id = id
@ -188,16 +180,16 @@ UI =
#prevent text selection #prevent text selection
e.preventDefault() e.preventDefault()
UI.el = el = @parentNode UI.el = el = @parentNode
d.addEventListener 'mousemove', UI.drag, false $.d.addEventListener 'mousemove', UI.drag, false
d.addEventListener 'mouseup', UI.dragend, false $.d.addEventListener 'mouseup', UI.dragend, false
#distance from pointer to el edge is constant; calculate it here. #distance from pointer to el edge is constant; calculate it here.
# XXX opera reports el.offsetLeft / el.offsetTop as 0 # XXX opera reports el.offsetLeft / el.offsetTop as 0
rect = el.getBoundingClientRect() rect = el.getBoundingClientRect()
UI.dx = e.clientX - rect.left UI.dx = e.clientX - rect.left
UI.dy = e.clientY - rect.top UI.dy = e.clientY - rect.top
#factor out el from document dimensions #factor out el from document dimensions
UI.width = d.body.clientWidth - el.offsetWidth UI.width = $.d.body.clientWidth - el.offsetWidth
UI.height = d.body.clientHeight - el.offsetHeight UI.height = $.d.body.clientHeight - el.offsetHeight
drag: (e) -> drag: (e) ->
left = e.clientX - UI.dx left = e.clientX - UI.dx
top = e.clientY - UI.dy top = e.clientY - UI.dy
@ -224,13 +216,13 @@ UI =
#a = (b = c.b, c).a; #a = (b = c.b, c).a;
{el} = UI {el} = UI
localStorage["#{NAMESPACE}#{el.id}.position"] = el.style.cssText localStorage["#{NAMESPACE}#{el.id}.position"] = el.style.cssText
d.removeEventListener 'mousemove', UI.drag, false $.d.removeEventListener 'mousemove', UI.drag, false
d.removeEventListener 'mouseup', UI.dragend, false $.d.removeEventListener 'mouseup', UI.dragend, false
hover: (e) -> hover: (e) ->
{clientX, clientY} = e {clientX, clientY} = e
{el} = UI {el} = UI
{style} = el {style} = el
{clientHeight, clientWidth} = d.body {clientHeight, clientWidth} = $.d.body
height = el.offsetHeight height = el.offsetHeight
top = clientY - 120 top = clientY - 120
@ -258,7 +250,7 @@ loosely follows the jquery api:
http://api.jquery.com/ http://api.jquery.com/
not chainable not chainable
### ###
$ = (selector, root=d.body) -> $ = (selector, root=$.d.body) ->
root.querySelector selector root.querySelector selector
$.extend = (object, properties) -> $.extend = (object, properties) ->
@ -266,24 +258,33 @@ $.extend = (object, properties) ->
object[key] = val object[key] = val
return return
NAMESPACE = '4chan_x.'
VERSION = '2.29.1'
SECOND = 1000
MINUTE = 60*SECOND
HOUR = 60*MINUTE
DAY = 24*HOUR
$.extend $, $.extend $,
# XXX GreaseMonkey can't into console.log.bind log:
log: console.log.bind? console # XXX GreaseMonkey can't into console.log.bind
console.log.bind? console
d: document
engine: /WebKit|Presto|Gecko/.exec(navigator.userAgent)[0].toLowerCase() engine: /WebKit|Presto|Gecko/.exec(navigator.userAgent)[0].toLowerCase()
ready: (fc) -> ready: (fc) ->
if /interactive|complete/.test d.readyState if /interactive|complete/.test $.d.readyState
# Execute the functions in parallel. # Execute the functions in parallel.
# If one fails, do not stop the others. # If one fails, do not stop the others.
return setTimeout fc return setTimeout fc
cb = -> cb = ->
$.off d, 'DOMContentLoaded', cb $.off $.d, 'DOMContentLoaded', cb
fc() fc()
$.on d, 'DOMContentLoaded', cb $.on $.d, 'DOMContentLoaded', cb
sync: (key, cb) -> sync: (key, cb) ->
$.on window, 'storage', (e) -> $.on window, 'storage', (e) ->
cb JSON.parse e.newValue if e.key is "#{NAMESPACE}#{key}" cb JSON.parse e.newValue if e.key is "#{NAMESPACE}#{key}"
id: (id) -> id: (id) ->
d.getElementById id $.d.getElementById id
ajax: (url, callbacks, opts={}) -> ajax: (url, callbacks, opts={}) ->
{type, headers, upCallbacks, form} = opts {type, headers, upCallbacks, form} = opts
r = new XMLHttpRequest() r = new XMLHttpRequest()
@ -316,11 +317,11 @@ $.extend $,
addStyle: (css) -> addStyle: (css) ->
style = $.el 'style', style = $.el 'style',
textContent: css textContent: css
$.add d.head, style $.add $.d.head, style
style style
x: (path, root=d.body) -> x: (path, root=$.d.body) ->
# XPathResult.ANY_UNORDERED_NODE_TYPE is 8 # XPathResult.ANY_UNORDERED_NODE_TYPE is 8
d.evaluate(path, root, null, 8, null). $.d.evaluate(path, root, null, 8, null).
singleNodeValue singleNodeValue
addClass: (el, className) -> addClass: (el, className) ->
el.classList.add className el.classList.add className
@ -329,11 +330,11 @@ $.extend $,
rm: (el) -> rm: (el) ->
el.parentNode.removeChild el el.parentNode.removeChild el
tn: (s) -> tn: (s) ->
d.createTextNode s $.d.createTextNode s
nodes: (nodes) -> nodes: (nodes) ->
if nodes instanceof Node if nodes instanceof Node
return nodes return nodes
frag = d.createDocumentFragment() frag = $.d.createDocumentFragment()
for node in nodes for node in nodes
frag.appendChild node frag.appendChild node
frag frag
@ -348,7 +349,7 @@ $.extend $,
replace: (root, el) -> replace: (root, el) ->
root.parentNode.replaceChild $.nodes(el), root root.parentNode.replaceChild $.nodes(el), root
el: (tag, properties) -> el: (tag, properties) ->
el = d.createElement tag el = $.d.createElement tag
$.extend el, properties if properties $.extend el, properties if properties
el el
on: (el, eventType, handler) -> on: (el, eventType, handler) ->
@ -447,7 +448,7 @@ $.extend $,
name = NAMESPACE + name name = NAMESPACE + name
localStorage[name] = JSON.stringify value localStorage[name] = JSON.stringify value
$$ = (selector, root=d.body) -> $$ = (selector, root=$.d.body) ->
Array::slice.call root.querySelectorAll selector Array::slice.call root.querySelectorAll selector
Filter = Filter =
@ -582,7 +583,7 @@ Filter =
comment: (post) -> comment: (post) ->
text = [] text = []
# XPathResult.ORDERED_NODE_SNAPSHOT_TYPE is 7 # XPathResult.ORDERED_NODE_SNAPSHOT_TYPE is 7
nodes = d.evaluate './/br|.//text()', post.el.lastChild, null, 7, null nodes = $.d.evaluate './/br|.//text()', post.el.lastChild, null, 7, null
for i in [0...nodes.snapshotLength] for i in [0...nodes.snapshotLength]
text.push if data = nodes.snapshotItem(i).data then data else '\n' text.push if data = nodes.snapshotItem(i).data then data else '\n'
text.join '' text.join ''
@ -635,13 +636,13 @@ ExpandComment =
a.textContent = "#{req.status} #{req.statusText}" a.textContent = "#{req.status} #{req.statusText}"
return return
doc = d.implementation.createHTMLDocument null doc = $.d.implementation.createHTMLDocument null
doc.documentElement.innerHTML = req.responseText doc.documentElement.innerHTML = req.responseText
Threading.op $('body > form', doc).firstChild Threading.op $('body > form', doc).firstChild
# Import the node to fix quote.hashes # Import the node to fix quote.hashes
# as they're empty when in a different document. # as they're empty when in a different document.
node = d.importNode doc.getElementById replyID node = $.d.importNode doc.getElementById replyID
quotes = node.getElementsByClassName 'quotelink' quotes = node.getElementsByClassName 'quotelink'
for quote in quotes for quote in quotes
@ -718,12 +719,12 @@ ExpandThread =
a.textContent = a.textContent.replace '\u00d7 Loading...', '-' a.textContent = a.textContent.replace '\u00d7 Loading...', '-'
doc = d.implementation.createHTMLDocument null doc = $.d.implementation.createHTMLDocument null
doc.documentElement.innerHTML = req.responseText doc.documentElement.innerHTML = req.responseText
nodes = [] nodes = []
for reply in $$ '.reply', doc for reply in $$ '.reply', doc
table = d.importNode reply.parentNode.parentNode.parentNode table = $.d.importNode reply.parentNode.parentNode.parentNode
for quote in $$ '.quotelink', table for quote in $$ '.quotelink', table
if (href = quote.getAttribute 'href') is quote.hash #add pathname to normal quotes if (href = quote.getAttribute 'href') is quote.hash #add pathname to normal quotes
quote.pathname = pathname quote.pathname = pathname
@ -794,7 +795,7 @@ Keybinds =
init: -> init: ->
for node in $$ '[accesskey]' for node in $$ '[accesskey]'
node.removeAttribute 'accesskey' node.removeAttribute 'accesskey'
$.on d, 'keydown', Keybinds.keydown $.on $.d, 'keydown', Keybinds.keydown
keydown: (e) -> keydown: (e) ->
if not (key = Keybinds.keyCode(e)) or /TEXTAREA|INPUT/.test(e.target.nodeName) and not (e.altKey or e.ctrlKey or e.keyCode is 27) if not (key = Keybinds.keyCode(e)) or /TEXTAREA|INPUT/.test(e.target.nodeName) and not (e.altKey or e.ctrlKey or e.keyCode is 27)
@ -927,7 +928,7 @@ Keybinds =
td.className = 'reply' td.className = 'reply'
td.removeAttribute 'tabindex' td.removeAttribute 'tabindex'
rect = td.getBoundingClientRect() rect = td.getBoundingClientRect()
if rect.bottom >= 0 and rect.top <= d.body.clientHeight # We're at least partially visible if rect.bottom >= 0 and rect.top <= $.d.body.clientHeight # We're at least partially visible
next = next =
if delta is +1 if delta is +1
$.x 'following::td[@class="reply"]', td $.x 'following::td[@class="reply"]', td
@ -940,7 +941,7 @@ Keybinds =
return return
return unless Main.REPLY or $.x('ancestor::div[@class="thread"]', next) is thread return unless Main.REPLY or $.x('ancestor::div[@class="thread"]', next) is thread
rect = next.getBoundingClientRect() rect = next.getBoundingClientRect()
if rect.top < 0 or rect.bottom > d.body.clientHeight if rect.top < 0 or rect.bottom > $.d.body.clientHeight
next.scrollIntoView delta is -1 next.scrollIntoView delta is -1
next.className = 'replyhl' next.className = 'replyhl'
next.tabIndex = 0 next.tabIndex = 0
@ -951,7 +952,7 @@ Keybinds =
replies.reverse() if delta is -1 replies.reverse() if delta is -1
for reply in replies for reply in replies
rect = reply.getBoundingClientRect() rect = reply.getBoundingClientRect()
if delta is +1 and rect.top >= 0 or delta is -1 and rect.bottom <= d.body.clientHeight if delta is +1 and rect.top >= 0 or delta is -1 and rect.bottom <= $.d.body.clientHeight
reply.className = 'replyhl' reply.className = 'replyhl'
reply.tabIndex = 0 reply.tabIndex = 0
reply.focus() reply.focus()
@ -973,7 +974,7 @@ Nav =
$.on next, 'click', @next $.on next, 'click', @next
$.add span, [prev, $.tn(' '), next] $.add span, [prev, $.tn(' '), next]
$.add d.body, span $.add $.d.body, span
prev: -> prev: ->
if Main.REPLY if Main.REPLY
@ -983,7 +984,7 @@ Nav =
next: -> next: ->
if Main.REPLY if Main.REPLY
window.scrollTo 0, d.body.scrollHeight window.scrollTo 0, $.d.body.scrollHeight
else else
Nav.scroll +1 Nav.scroll +1
@ -1024,7 +1025,7 @@ QR =
QR.open() QR.open()
$('select', QR.el).value = 'new' unless Main.REPLY $('select', QR.el).value = 'new' unless Main.REPLY
$('textarea', QR.el).focus() $('textarea', QR.el).focus()
form = d.forms[0] form = $.d.forms[0]
$.before form, link $.before form, link
# CORS is ignored for content script on Chrome, but not Safari/Oprah/Firefox. # CORS is ignored for content script on Chrome, but not Safari/Oprah/Firefox.
@ -1041,20 +1042,20 @@ QR =
iframe.src = 'about:blank' iframe.src = 'about:blank'
setTimeout (-> iframe.src = 'https://sys.4chan.org/robots.txt'), 100 setTimeout (-> iframe.src = 'https://sys.4chan.org/robots.txt'), 100
$.on iframe, 'load', -> if @src isnt 'about:blank' then setTimeout loadChecking, 500, @ $.on iframe, 'load', -> if @src isnt 'about:blank' then setTimeout loadChecking, 500, @
$.add d.head, iframe $.add $.d.head, iframe
# Prevent original captcha input from being focused on reload. # Prevent original captcha input from being focused on reload.
script = $.el 'script', textContent: 'Recaptcha.focus_response_field=function(){}' script = $.el 'script', textContent: 'Recaptcha.focus_response_field=function(){}'
$.add d.head, script $.add $.d.head, script
$.rm script $.rm script
if Conf['Persistent QR'] if Conf['Persistent QR']
QR.dialog() QR.dialog()
QR.hide() if Conf['Auto Hide QR'] QR.hide() if Conf['Auto Hide QR']
$.on d, 'dragover', QR.dragOver $.on $.d, 'dragover', QR.dragOver
$.on d, 'drop', QR.dropFile $.on $.d, 'drop', QR.dropFile
$.on d, 'dragstart', QR.drag $.on $.d, 'dragstart', QR.drag
$.on d, 'dragend', QR.drag $.on $.d, 'dragend', QR.drag
node: (post) -> node: (post) ->
$.on $('.quotejs + .quotejs', post.el), 'click', QR.quote $.on $('.quotejs + .quotejs', post.el), 'click', QR.quote
@ -1068,7 +1069,7 @@ QR =
close: -> close: ->
QR.el.hidden = true QR.el.hidden = true
QR.message.send req: 'abort' QR.message.send req: 'abort'
d.activeElement.blur() $.d.activeElement.blur()
$.removeClass QR.el, 'dump' $.removeClass QR.el, 'dump'
for i in QR.replies for i in QR.replies
QR.replies[0].rm() QR.replies[0].rm()
@ -1079,7 +1080,7 @@ QR =
spoiler.click() spoiler.click()
QR.cleanError() QR.cleanError()
hide: -> hide: ->
d.activeElement.blur() $.d.activeElement.blur()
$.addClass QR.el, 'autohide' $.addClass QR.el, 'autohide'
$.id('autohide').checked = true $.id('autohide').checked = true
unhide: -> unhide: ->
@ -1096,7 +1097,7 @@ QR =
if /captcha|verification/i.test err if /captcha|verification/i.test err
# Focus the captcha input on captcha error. # Focus the captcha input on captcha error.
$('[autocomplete]', QR.el).focus() $('[autocomplete]', QR.el).focus()
alert err if d.hidden or d.oHidden or d.mozHidden or d.webkitHidden alert err if $.d.hidden or $.d.oHidden or $.d.mozHidden or $.d.webkitHidden
cleanError: -> cleanError: ->
$('.warning', QR.el).textContent = null $('.warning', QR.el).textContent = null
@ -1179,8 +1180,8 @@ QR =
drag: (e) -> drag: (e) ->
# Let it drag anything from the page. # Let it drag anything from the page.
i = if e.type is 'dragstart' then 'off' else 'on' i = if e.type is 'dragstart' then 'off' else 'on'
$[i] d, 'dragover', QR.dragOver $[i] $.d, 'dragover', QR.dragOver
$[i] d, 'drop', QR.dropFile $[i] $.d, 'drop', QR.dropFile
dragOver: (e) -> dragOver: (e) ->
e.preventDefault() e.preventDefault()
e.dataTransfer.dropEffect = 'copy' # cursor feedback e.dataTransfer.dropEffect = 'copy' # cursor feedback
@ -1505,11 +1506,11 @@ QR =
QR.status() QR.status()
QR.cooldown.init() QR.cooldown.init()
QR.captcha.init() QR.captcha.init()
$.add d.body, QR.el $.add $.d.body, QR.el
# Create a custom event when the QR dialog is first initialized. # Create a custom event when the QR dialog is first initialized.
# Use it to extend the QR's functionalities, or for XTRM RICE. # Use it to extend the QR's functionalities, or for XTRM RICE.
e = d.createEvent 'CustomEvent' e = $.d.createEvent 'CustomEvent'
e.initEvent 'QRDialogCreation', true, false e.initEvent 'QRDialogCreation', true, false
QR.el.dispatchEvent e QR.el.dispatchEvent e
@ -1569,7 +1570,7 @@ QR =
upfile: reply.file upfile: reply.file
spoiler: reply.spoiler spoiler: reply.spoiler
mode: 'regist' mode: 'regist'
pwd: if m = d.cookie.match(/4chan_pass=([^;]+)/) then decodeURIComponent m[1] else $('[name=pwd]').value pwd: if m = $.d.cookie.match(/4chan_pass=([^;]+)/) then decodeURIComponent m[1] else $('[name=pwd]').value
recaptcha_challenge_field: challenge recaptcha_challenge_field: challenge
recaptcha_response_field: response + ' ' recaptcha_response_field: response + ' '
@ -1953,8 +1954,8 @@ Options =
$.on overlay, 'click', Options.close $.on overlay, 'click', Options.close
$.on dialog, 'click', (e) -> e.stopPropagation() $.on dialog, 'click', (e) -> e.stopPropagation()
$.add overlay, dialog $.add overlay, dialog
$.add d.body, overlay $.add $.d.body, overlay
d.body.style.setProperty 'overflow', 'hidden', null $.d.body.style.setProperty 'overflow', 'hidden', null
Options.backlink.call back Options.backlink.call back
Options.time.call time Options.time.call time
@ -1964,7 +1965,7 @@ Options =
close: -> close: ->
$.rm this $.rm this
d.body.style.removeProperty 'overflow' $.d.body.style.removeProperty 'overflow'
clearHidden: -> clearHidden: ->
#'hidden' might be misleading; it's the number of IDs we're *looking* for, #'hidden' might be misleading; it's the number of IDs we're *looking* for,
@ -2140,7 +2141,7 @@ Updater =
else if input.type is 'button' else if input.type is 'button'
$.on input, 'click', @update $.on input, 'click', @update
$.add d.body, dialog $.add $.d.body, dialog
@retryCoef = 10 @retryCoef = 10
@lastModified = 0 @lastModified = 0
@ -2165,7 +2166,7 @@ Updater =
if @checked if @checked
-> true -> true
else else
-> !(d.hidden or d.oHidden or d.mozHidden or d.webkitHidden) -> !($.d.hidden or $.d.oHidden or $.d.mozHidden or $.d.webkitHidden)
update: -> update: ->
if @status is 404 if @status is 404
Updater.timer.textContent = '' Updater.timer.textContent = ''
@ -2176,7 +2177,7 @@ Updater =
if Conf['Unread Count'] if Conf['Unread Count']
Unread.title = Unread.title.match(/^.+-/)[0] + ' 404' Unread.title = Unread.title.match(/^.+-/)[0] + ' 404'
else else
d.title = d.title.match(/^.+-/)[0] + ' 404' $.d.title = $.d.title.match(/^.+-/)[0] + ' 404'
Unread.update true Unread.update true
QR.message.send req: 'abort' QR.message.send req: 'abort'
QR.status() QR.status()
@ -2198,7 +2199,7 @@ Updater =
return return
Updater.lastModified = @getResponseHeader 'Last-Modified' Updater.lastModified = @getResponseHeader 'Last-Modified'
doc = d.implementation.createHTMLDocument null doc = $.d.implementation.createHTMLDocument null
doc.documentElement.innerHTML = @responseText doc.documentElement.innerHTML = @responseText
id = $('input', Updater.br.previousElementSibling).name id = $('input', Updater.br.previousElementSibling).name
@ -2209,7 +2210,7 @@ Updater =
newPosts = nodes.length newPosts = nodes.length
scroll = Conf['Scrolling'] && Updater.scrollBG() && newPosts && scroll = Conf['Scrolling'] && Updater.scrollBG() && newPosts &&
Updater.br.previousElementSibling.getBoundingClientRect().bottom - d.body.clientHeight < 25 Updater.br.previousElementSibling.getBoundingClientRect().bottom - $.d.body.clientHeight < 25
if Conf['Verbose'] if Conf['Verbose']
Updater.count.textContent = "+#{newPosts}" Updater.count.textContent = "+#{newPosts}"
Updater.count.className = if newPosts then 'new' else null Updater.count.className = if newPosts then 'new' else null
@ -2247,7 +2248,7 @@ Watcher =
init: -> init: ->
html = '<div class=move>Thread Watcher</div>' html = '<div class=move>Thread Watcher</div>'
@dialog = UI.dialog 'watcher', 'top: 50px; left: 0px;', html @dialog = UI.dialog 'watcher', 'top: 50px; left: 0px;', html
$.add d.body, @dialog $.add $.d.body, @dialog
#add watch buttons #add watch buttons
inputs = $$ '.op > input' inputs = $$ '.op > input'
@ -2546,7 +2547,7 @@ GetTitle = (thread) ->
TitlePost = TitlePost =
init: -> init: ->
d.title = GetTitle() $.d.title = GetTitle()
QuoteBacklink = QuoteBacklink =
init: -> init: ->
@ -2651,7 +2652,7 @@ QuoteInline =
inline.textContent = "#{req.status} #{req.statusText}" inline.textContent = "#{req.status} #{req.statusText}"
return return
doc = d.implementation.createHTMLDocument null doc = $.d.implementation.createHTMLDocument null
doc.documentElement.innerHTML = req.responseText doc.documentElement.innerHTML = req.responseText
node = node =
@ -2690,7 +2691,7 @@ QuotePreview =
qp = UI.el = $.el 'div', qp = UI.el = $.el 'div',
id: 'qp' id: 'qp'
className: 'reply dialog' className: 'reply dialog'
$.add d.body, qp $.add $.d.body, qp
id = @hash[1..] id = @hash[1..]
if el = $.id id if el = $.id id
@ -2723,7 +2724,7 @@ QuotePreview =
qp.textContent = "#{req.status} #{req.statusText}" qp.textContent = "#{req.status} #{req.statusText}"
return return
doc = d.implementation.createHTMLDocument null doc = $.d.implementation.createHTMLDocument null
doc.documentElement.innerHTML = req.responseText doc.documentElement.innerHTML = req.responseText
node = node =
@ -2778,7 +2779,7 @@ Quotify =
# XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE is 6 # XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE is 6
# Get all the text nodes that are not inside an anchor. # Get all the text nodes that are not inside an anchor.
snapshot = d.evaluate './/text()[not(parent::a)]', post.el.lastChild, null, 6, null snapshot = $.d.evaluate './/text()[not(parent::a)]', post.el.lastChild, null, 6, null
for i in [0...snapshot.snapshotLength] for i in [0...snapshot.snapshotLength]
node = snapshot.snapshotItem i node = snapshot.snapshotItem i
@ -2848,7 +2849,7 @@ ThreadStats =
init: -> init: ->
dialog = UI.dialog 'stats', 'bottom: 0; left: 0;', '<div class=move><span id=postcount>0</span> / <span id=imagecount>0</span></div>' dialog = UI.dialog 'stats', 'bottom: 0; left: 0;', '<div class=move><span id=postcount>0</span> / <span id=imagecount>0</span></div>'
dialog.className = 'dialog' dialog.className = 'dialog'
$.add d.body, dialog $.add $.d.body, dialog
@posts = @images = 0 @posts = @images = 0
@imgLimit = @imgLimit =
switch Main.BOARD switch Main.BOARD
@ -2870,7 +2871,7 @@ ThreadStats =
Unread = Unread =
init: -> init: ->
@title = d.title @title = $.d.title
@update() @update()
$.on window, 'scroll', Unread.scroll $.on window, 'scroll', Unread.scroll
Main.callbacks.push @node Main.callbacks.push @node
@ -2887,7 +2888,7 @@ Unread =
Unread.update() Unread.update()
scroll: -> scroll: ->
height = d.body.clientHeight height = $.d.body.clientHeight
for reply, i in Unread.replies for reply, i in Unread.replies
{bottom} = reply.getBoundingClientRect() {bottom} = reply.getBoundingClientRect()
if bottom > height #post is not completely read if bottom > height #post is not completely read
@ -2904,7 +2905,7 @@ Unread =
@setTitle count @setTitle count
return return
@scheduled = setTimeout (-> @scheduled = setTimeout (->
d.title = "(#{count}) #{Unread.title}" $.d.title = "(#{count}) #{Unread.title}"
), 5 ), 5
update: (forceUpdate) -> update: (forceUpdate) ->
@ -2933,11 +2934,11 @@ Unread =
#`favicon.href = href` doesn't work on Firefox #`favicon.href = href` doesn't work on Firefox
#`favicon.href = href` isn't enough on Opera #`favicon.href = href` isn't enough on Opera
#Opera won't always update the favicon if the href didn't not change #Opera won't always update the favicon if the href didn't not change
$.add d.head, Favicon.el $.add $.d.head, Favicon.el
Favicon = Favicon =
init: -> init: ->
@el = $ 'link[rel="shortcut icon"]', d.head @el = $ 'link[rel="shortcut icon"]', $.d.head
@el.type = 'image/x-icon' @el.type = 'image/x-icon'
{href} = @el {href} = @el
@SFW = /ws.ico$/.test href @SFW = /ws.ico$/.test href
@ -3009,7 +3010,7 @@ ImageHover =
UI.el = $.el 'img' UI.el = $.el 'img'
id: 'ihover' id: 'ihover'
src: @parentNode.href src: @parentNode.href
$.add d.body, UI.el $.add $.d.body, UI.el
$.on UI.el, 'load', ImageHover.load $.on UI.el, 'load', ImageHover.load
$.on @, 'mousemove', UI.hover $.on @, 'mousemove', UI.hover
$.on @, 'mouseout', ImageHover.mouseout $.on @, 'mouseout', ImageHover.mouseout
@ -3092,8 +3093,8 @@ ImageExpand =
thumb = a.firstChild thumb = a.firstChild
if thumb.hidden if thumb.hidden
rect = a.getBoundingClientRect() rect = a.getBoundingClientRect()
d.body.scrollTop += rect.top - 42 if rect.top < 0 $.d.body.scrollTop += rect.top - 42 if rect.top < 0
d.body.scrollLeft += rect.left if rect.left < 0 $.d.body.scrollLeft += rect.left if rect.left < 0
ImageExpand.contract thumb ImageExpand.contract thumb
else else
ImageExpand.expand thumb ImageExpand.expand thumb
@ -3151,7 +3152,7 @@ ImageExpand =
$.prepend form, controls $.prepend form, controls
resize: -> resize: ->
ImageExpand.style.textContent = ".fitheight img[md5] + img {max-height:#{d.body.clientHeight}px;}" ImageExpand.style.textContent = ".fitheight img[md5] + img {max-height:#{$.d.body.clientHeight}px;}"
Main = Main =
init: -> init: ->
@ -3184,7 +3185,7 @@ Main =
QR.message.send req: 'status', ready: true, banned: true QR.message.send req: 'status', ready: true, banned: true
return return
when 'images.4chan.org' when 'images.4chan.org'
$.ready -> Redirect.init() if d.title is '4chan - 404' $.ready -> Redirect.init() if $.d.title is '4chan - 404'
return return
$.ready Options.init $.ready Options.init
@ -3196,7 +3197,7 @@ Main =
now = Date.now() now = Date.now()
if Conf['Check for Updates'] and $.get('lastUpdate', 0) < now - 6*HOUR if Conf['Check for Updates'] and $.get('lastUpdate', 0) < now - 6*HOUR
$.ready -> $.add d.head, $.el 'script', src: 'https://raw.github.com/mayhemydg/4chan-x/master/latest.js' $.ready -> $.add $.d.head, $.el 'script', src: 'https://raw.github.com/mayhemydg/4chan-x/master/latest.js'
$.set 'lastUpdate', now $.set 'lastUpdate', now
Main.hiddenReplies = $.get "hiddenReplies/#{Main.BOARD}/", {} Main.hiddenReplies = $.get "hiddenReplies/#{Main.BOARD}/", {}
@ -3273,13 +3274,13 @@ Main =
$.ready Main.ready $.ready Main.ready
ready: -> ready: ->
if d.title is '4chan - 404' if $.d.title is '4chan - 404'
Redirect.init() Redirect.init()
return return
unless $.id 'navtopr' unless $.id 'navtopr'
return return
$.addClass d.body, "chanx_#{VERSION.split('.')[1]}" $.addClass $.d.body, "chanx_#{VERSION.split('.')[1]}"
$.addClass d.body, $.engine $.addClass $.d.body, $.engine
for nav in ['navtop', 'navbot'] for nav in ['navtop', 'navbot']
$.addClass $("a[href$='/#{Main.BOARD}/']", $.id nav), 'current' $.addClass $("a[href$='/#{Main.BOARD}/']", $.id nav), 'current'
form = $ 'form[name=delform]' form = $ 'form[name=delform]'
@ -3342,11 +3343,11 @@ Main =
$.on form, 'DOMNodeInserted', Main.listener $.on form, 'DOMNodeInserted', Main.listener
addStyle: -> addStyle: ->
$.off d, 'DOMNodeInserted', Main.addStyle $.off $.d, 'DOMNodeInserted', Main.addStyle
if d.head if $.d.head
$.addStyle Main.css $.addStyle Main.css
else # XXX fox else # XXX fox
$.on d, 'DOMNodeInserted', Main.addStyle $.on $.d, 'DOMNodeInserted', Main.addStyle
message: (e) -> message: (e) ->
{data} = e {data} = e