diff --git a/4chan_x.user.js b/4chan_x.user.js
index 3934927d7..dc50b1fb6 100644
--- a/4chan_x.user.js
+++ b/4chan_x.user.js
@@ -73,9 +73,9 @@
*/
(function() {
- var $, $$, Anonymize, AutoGif, DAY, ExpandComment, ExpandThread, Favicon, FileInfo, Filter, GetTitle, HOUR, ImageExpand, ImageHover, Keybinds, MINUTE, Main, NAMESPACE, Nav, Options, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, Quotify, Redirect, ReplyHiding, ReportButton, RevealSpoilers, SECOND, Sauce, StrikethroughQuotes, ThreadHiding, ThreadStats, Threading, Time, TitlePost, Unread, Updater, VERSION, Watcher, conf, config, d, engine, flatten, g, log, qr, ui, _base;
+ var $, $$, Anonymize, AutoGif, Conf, Config, ExpandComment, ExpandThread, Favicon, FileInfo, Filter, GetTitle, ImageExpand, ImageHover, Keybinds, Main, Nav, Options, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, Quotify, Redirect, ReplyHiding, ReportButton, RevealSpoilers, Sauce, StrikethroughQuotes, ThreadHiding, ThreadStats, Threading, Time, TitlePost, UI, Unread, Updater, Watcher, d, g, _base;
- config = {
+ Config = {
main: {
Enhancing: {
'404 Redirect': [true, 'Redirect dead threads and images'],
@@ -192,76 +192,44 @@
}
};
- log = typeof (_base = console.log).bind === "function" ? _base.bind(console) : void 0;
-
- conf = {};
-
- (flatten = function(parent, obj) {
- var key, val;
- if (obj instanceof Array) {
- conf[parent] = obj[0];
- } else if (typeof obj === 'object') {
- for (key in obj) {
- val = obj[key];
- flatten(key, val);
- }
- } else {
- conf[parent] = obj;
- }
- })(null, config);
-
- NAMESPACE = '4chan_x.';
-
- VERSION = '2.29.1';
-
- SECOND = 1000;
-
- MINUTE = 60 * SECOND;
-
- HOUR = 60 * MINUTE;
-
- DAY = 24 * HOUR;
-
- engine = /WebKit|Presto|Gecko/.exec(navigator.userAgent)[0].toLowerCase();
+ Conf = {};
d = document;
- g = {
- callbacks: []
- };
+ g = {};
- ui = {
+ UI = {
dialog: function(id, position, html) {
var el, saved;
el = d.createElement('div');
el.className = 'reply dialog';
el.innerHTML = html;
el.id = id;
- el.style.cssText = (saved = localStorage["" + NAMESPACE + id + ".position"]) ? saved : position;
- el.querySelector('.move').addEventListener('mousedown', ui.dragstart, false);
+ el.style.cssText = (saved = localStorage["" + Main.namespace + id + ".position"]) ? saved : position;
+ el.querySelector('.move').addEventListener('mousedown', UI.dragstart, false);
return el;
},
dragstart: function(e) {
var el, rect;
e.preventDefault();
- ui.el = el = this.parentNode;
- d.addEventListener('mousemove', ui.drag, false);
- d.addEventListener('mouseup', ui.dragend, false);
+ UI.el = el = this.parentNode;
+ d.addEventListener('mousemove', UI.drag, false);
+ d.addEventListener('mouseup', UI.dragend, false);
rect = el.getBoundingClientRect();
- ui.dx = e.clientX - rect.left;
- ui.dy = e.clientY - rect.top;
- ui.width = d.body.clientWidth - el.offsetWidth;
- return ui.height = d.body.clientHeight - el.offsetHeight;
+ UI.dx = e.clientX - rect.left;
+ UI.dy = e.clientY - rect.top;
+ UI.width = d.body.clientWidth - el.offsetWidth;
+ return UI.height = d.body.clientHeight - el.offsetHeight;
},
drag: function(e) {
var bottom, left, right, style, top;
- left = e.clientX - ui.dx;
- top = e.clientY - ui.dy;
- left = left < 10 ? 0 : ui.width - left < 10 ? null : left;
- top = top < 10 ? 0 : ui.height - top < 10 ? null : top;
+ left = e.clientX - UI.dx;
+ top = e.clientY - UI.dy;
+ left = left < 10 ? 0 : UI.width - left < 10 ? null : left;
+ top = top < 10 ? 0 : UI.height - top < 10 ? null : top;
right = left === null ? 0 : null;
bottom = top === null ? 0 : null;
- style = ui.el.style;
+ style = UI.el.style;
style.top = top;
style.right = right;
style.bottom = bottom;
@@ -269,15 +237,15 @@
},
dragend: function() {
var el;
- el = ui.el;
- localStorage["" + NAMESPACE + el.id + ".position"] = el.style.cssText;
- d.removeEventListener('mousemove', ui.drag, false);
- return d.removeEventListener('mouseup', ui.dragend, false);
+ el = UI.el;
+ localStorage["" + Main.namespace + el.id + ".position"] = el.style.cssText;
+ d.removeEventListener('mousemove', UI.drag, false);
+ return d.removeEventListener('mouseup', UI.dragend, false);
},
hover: function(e) {
var clientHeight, clientWidth, clientX, clientY, el, height, style, top, _ref;
clientX = e.clientX, clientY = e.clientY;
- el = ui.el;
+ el = UI.el;
style = el.style;
_ref = d.body, clientHeight = _ref.clientHeight, clientWidth = _ref.clientWidth;
height = el.offsetHeight;
@@ -292,8 +260,8 @@
}
},
hoverend: function() {
- $.rm(ui.el);
- return delete ui.el;
+ $.rm(UI.el);
+ return delete UI.el;
}
};
@@ -317,6 +285,12 @@
};
$.extend($, {
+ SECOND: 1000,
+ MINUTE: 1000 * 60,
+ HOUR: 1000 * 60 * 60,
+ DAY: 1000 * 60 * 60 * 24,
+ log: typeof (_base = console.log).bind === "function" ? _base.bind(console) : void 0,
+ engine: /WebKit|Presto|Gecko/.exec(navigator.userAgent)[0].toLowerCase(),
ready: function(fc) {
var cb;
if (/interactive|complete/.test(d.readyState)) return setTimeout(fc);
@@ -328,7 +302,9 @@
},
sync: function(key, cb) {
return $.on(window, 'storage', function(e) {
- if (e.key === ("" + NAMESPACE + key)) return cb(JSON.parse(e.newValue));
+ if (e.key === ("" + Main.namespace + key)) {
+ return cb(JSON.parse(e.newValue));
+ }
});
},
id: function(id) {
@@ -384,11 +360,11 @@
cb: {
checked: function() {
$.set(this.name, this.checked);
- return conf[this.name] = this.checked;
+ return Conf[this.name] = this.checked;
},
value: function() {
$.set(this.name, this.value.trim());
- return conf[this.name] = this.value;
+ return Conf[this.name] = this.value;
}
},
addStyle: function(css) {
@@ -498,12 +474,12 @@
$.extend($, typeof GM_deleteValue !== "undefined" && GM_deleteValue !== null ? {
"delete": function(name) {
- name = NAMESPACE + name;
+ name = Main.namespace + name;
return GM_deleteValue(name);
},
get: function(name, defaultValue) {
var value;
- name = NAMESPACE + name;
+ name = Main.namespace + name;
if (value = GM_getValue(name)) {
return JSON.parse(value);
} else {
@@ -511,24 +487,24 @@
}
},
set: function(name, value) {
- name = NAMESPACE + name;
+ name = Main.namespace + name;
localStorage.setItem(name, JSON.stringify(value));
return GM_setValue(name, JSON.stringify(value));
}
} : {
"delete": function(name) {
- return localStorage.removeItem(NAMESPACE + name);
+ return localStorage.removeItem(Main.namespace + name);
},
get: function(name, defaultValue) {
var value;
- if (value = localStorage.getItem(NAMESPACE + name)) {
+ if (value = localStorage.getItem(Main.namespace + name)) {
return JSON.parse(value);
} else {
return defaultValue;
}
},
set: function(name, value) {
- return localStorage.setItem(NAMESPACE + name, JSON.stringify(value));
+ return localStorage.setItem(Main.namespace + name, JSON.stringify(value));
}
});
@@ -541,9 +517,9 @@
filters: {},
init: function() {
var boards, filter, hl, key, op, regexp, top, _i, _len, _ref, _ref2, _ref3, _ref4, _ref5;
- for (key in config.filter) {
+ for (key in Config.filter) {
this.filters[key] = [];
- _ref = conf[key].split('\n');
+ _ref = Conf[key].split('\n');
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
filter = _ref[_i];
if (filter[0] === '#') continue;
@@ -573,7 +549,7 @@
}
if (!this.filters[key].length) delete this.filters[key];
}
- if (Object.keys(this.filters).length) return g.callbacks.push(this.node);
+ if (Object.keys(this.filters).length) return Main.callbacks.push(this.node);
},
createFilter: function(regexp, op, hl, top) {
var test;
@@ -698,7 +674,7 @@
StrikethroughQuotes = {
init: function() {
- return g.callbacks.push(this.node);
+ return Main.callbacks.push(this.node);
},
node: function(post) {
var el, quote, _i, _len, _ref;
@@ -708,7 +684,7 @@
quote = _ref[_i];
if ((el = $.id(quote.hash.slice(1))) && el.parentNode.parentNode.parentNode.hidden) {
$.addClass(quote, 'filtered');
- if (conf['Recursive Filtering']) ReplyHiding.hide(post.root);
+ if (Conf['Recursive Filtering']) ReplyHiding.hide(post.root);
}
}
}
@@ -759,11 +735,11 @@
quotes: quotes,
backlinks: []
};
- if (conf['Resurrect Quotes']) Quotify.node(post);
- if (conf['Quote Preview']) QuotePreview.node(post);
- if (conf['Quote Inline']) QuoteInline.node(post);
- if (conf['Indicate OP quote']) QuoteOP.node(post);
- if (conf['Indicate Cross-thread Quotes']) QuoteCT.node(post);
+ if (Conf['Resurrect Quotes']) Quotify.node(post);
+ if (Conf['Quote Preview']) QuotePreview.node(post);
+ if (Conf['Quote Inline']) QuoteInline.node(post);
+ if (Conf['Indicate OP quote']) QuoteOP.node(post);
+ if (Conf['Indicate Cross-thread Quotes']) QuoteCT.node(post);
return $.replace(a.parentNode.parentNode, node.lastChild);
}
};
@@ -880,7 +856,7 @@
className: 'replyhider',
innerHTML: '[ - ]'
});
- return g.callbacks.push(this.node);
+ return Main.callbacks.push(this.node);
},
node: function(post) {
var td;
@@ -920,7 +896,7 @@
var div, name, trip, uid, _ref, _ref2;
if (table.hidden) return;
table.hidden = true;
- if (!conf['Show Stubs']) return;
+ if (!Conf['Show Stubs']) return;
name = $('.commentpostername', table).textContent;
uid = ((_ref = $('.posteruid', table)) != null ? _ref.textContent : void 0) || '';
trip = ((_ref2 = $('.postertrip', table)) != null ? _ref2.textContent : void 0) || '';
@@ -950,26 +926,26 @@
}
thread = Nav.getThread();
switch (key) {
- case conf.openQR:
+ case Conf.openQR:
Keybinds.qr(thread, true);
break;
- case conf.openEmptyQR:
+ case Conf.openEmptyQR:
Keybinds.qr(thread);
break;
- case conf.openOptions:
+ case Conf.openOptions:
if (!$.id('overlay')) Options.dialog();
break;
- case conf.close:
+ case Conf.close:
if (o = $.id('overlay')) {
Options.close.call(o);
- } else if (qr.el) {
- qr.close();
+ } else if (QR.el) {
+ QR.close();
}
break;
- case conf.submit:
- if (qr.el && !qr.status()) qr.submit();
+ case Conf.submit:
+ if (QR.el && !QR.status()) QR.submit();
break;
- case conf.spoiler:
+ case Conf.spoiler:
ta = e.target;
if (ta.nodeName !== 'TEXTAREA') return;
value = ta.value;
@@ -979,55 +955,55 @@
range = 9 + selEnd;
ta.setSelectionRange(range, range);
break;
- case conf.watch:
+ case Conf.watch:
Watcher.toggle(thread);
break;
- case conf.update:
+ case Conf.update:
Updater.update();
break;
- case conf.unreadCountTo0:
+ case Conf.unreadCountTo0:
Unread.replies = [];
Unread.update();
break;
- case conf.expandImage:
+ case Conf.expandImage:
Keybinds.img(thread);
break;
- case conf.expandAllImages:
+ case Conf.expandAllImages:
Keybinds.img(thread, true);
break;
- case conf.zero:
+ case Conf.zero:
window.location = "/" + g.BOARD + "/0#0";
break;
- case conf.nextPage:
+ case Conf.nextPage:
if ((_ref = $('input[value=Next]')) != null) _ref.click();
break;
- case conf.previousPage:
+ case Conf.previousPage:
if ((_ref2 = $('input[value=Previous]')) != null) _ref2.click();
break;
- case conf.nextThread:
+ case Conf.nextThread:
if (g.REPLY) return;
Nav.scroll(+1);
break;
- case conf.previousThread:
+ case Conf.previousThread:
if (g.REPLY) return;
Nav.scroll(-1);
break;
- case conf.expandThread:
+ case Conf.expandThread:
ExpandThread.toggle(thread);
break;
- case conf.openThread:
+ case Conf.openThread:
Keybinds.open(thread);
break;
- case conf.openThreadTab:
+ case Conf.openThreadTab:
Keybinds.open(thread, true);
break;
- case conf.nextReply:
+ case Conf.nextReply:
Keybinds.hl(+1, thread);
break;
- case conf.previousReply:
+ case Conf.previousReply:
Keybinds.hl(-1, thread);
break;
- case conf.hide:
+ case Conf.hide:
if (/\bthread\b/.test(thread.className)) ThreadHiding.toggle(thread);
break;
default:
@@ -1115,11 +1091,11 @@
},
qr: function(thread, quote) {
if (quote) {
- qr.quote.call($('.quotejs + .quotejs', $('.replyhl', thread) || thread));
+ QR.quote.call($('.quotejs + .quotejs', $('.replyhl', thread) || thread));
} else {
- qr.open();
+ QR.open();
}
- return $('textarea', qr.el).focus();
+ return $('textarea', QR.el).focus();
},
open: function(thread, tab) {
var id, url;
@@ -1233,28 +1209,28 @@
}
};
- qr = {
+ QR = {
init: function() {
if (!$.id('recaptcha_challenge_field_holder')) return;
- g.callbacks.push(this.node);
+ Main.callbacks.push(this.node);
return setTimeout(this.asyncInit);
},
asyncInit: function() {
var form, iframe, link, loadChecking, script;
- if (conf['Hide Original Post Form']) {
+ if (Conf['Hide Original Post Form']) {
link = $.el('h1', {
innerHTML: "" + (g.REPLY ? 'Quick Reply' : 'New Thread') + ""
});
$.on($('a', link), 'click', function() {
- qr.open();
- if (!g.REPLY) $('select', qr.el).value = 'new';
- return $('textarea', qr.el).focus();
+ QR.open();
+ if (!g.REPLY) $('select', QR.el).value = 'new';
+ return $('textarea', QR.el).focus();
});
form = d.forms[0];
$.before(form, link);
}
if (/chrome/i.test(navigator.userAgent)) {
- qr.status({
+ QR.status({
ready: true
});
} else {
@@ -1266,7 +1242,7 @@
return this.src = this.src;
});
loadChecking = function(iframe) {
- if (!qr.status.ready) {
+ if (!QR.status.ready) {
iframe.src = 'about:blank';
return setTimeout((function() {
return iframe.src = 'https://sys.4chan.org/robots.txt';
@@ -1285,131 +1261,131 @@
});
$.add(d.head, script);
$.rm(script);
- if (conf['Persistent QR']) {
- qr.dialog();
- if (conf['Auto Hide QR']) qr.hide();
+ if (Conf['Persistent QR']) {
+ QR.dialog();
+ if (Conf['Auto Hide QR']) QR.hide();
}
- $.on(d, 'dragover', qr.dragOver);
- $.on(d, 'drop', qr.dropFile);
- $.on(d, 'dragstart', qr.drag);
- return $.on(d, 'dragend', qr.drag);
+ $.on(d, 'dragover', QR.dragOver);
+ $.on(d, 'drop', QR.dropFile);
+ $.on(d, 'dragstart', QR.drag);
+ return $.on(d, 'dragend', QR.drag);
},
node: function(post) {
- return $.on($('.quotejs + .quotejs', post.el), 'click', qr.quote);
+ return $.on($('.quotejs + .quotejs', post.el), 'click', QR.quote);
},
open: function() {
- if (qr.el) {
- qr.el.hidden = false;
- return qr.unhide();
+ if (QR.el) {
+ QR.el.hidden = false;
+ return QR.unhide();
} else {
- return qr.dialog();
+ return QR.dialog();
}
},
close: function() {
var i, spoiler, _i, _len, _ref;
- qr.el.hidden = true;
- qr.message.send({
+ QR.el.hidden = true;
+ QR.message.send({
req: 'abort'
});
d.activeElement.blur();
- $.removeClass(qr.el, 'dump');
- _ref = qr.replies;
+ $.removeClass(QR.el, 'dump');
+ _ref = QR.replies;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
i = _ref[_i];
- qr.replies[0].rm();
+ QR.replies[0].rm();
}
- qr.cooldown.auto = false;
- qr.status();
- qr.resetFileInput();
- if (!conf['Remember Spoiler'] && (spoiler = $.id('spoiler')).checked) {
+ QR.cooldown.auto = false;
+ QR.status();
+ QR.resetFileInput();
+ if (!Conf['Remember Spoiler'] && (spoiler = $.id('spoiler')).checked) {
spoiler.click();
}
- return qr.cleanError();
+ return QR.cleanError();
},
hide: function() {
d.activeElement.blur();
- $.addClass(qr.el, 'autohide');
+ $.addClass(QR.el, 'autohide');
return $.id('autohide').checked = true;
},
unhide: function() {
- $.removeClass(qr.el, 'autohide');
+ $.removeClass(QR.el, 'autohide');
return $.id('autohide').checked = false;
},
toggleHide: function() {
- return this.checked && qr.hide() || qr.unhide();
+ return this.checked && QR.hide() || QR.unhide();
},
error: function(err, node) {
var el;
- el = $('.warning', qr.el);
+ el = $('.warning', QR.el);
el.textContent = err;
if (node) $.replace(el.firstChild, node);
- qr.open();
- if (/captcha|verification/i.test(err)) $('[autocomplete]', qr.el).focus();
+ QR.open();
+ if (/captcha|verification/i.test(err)) $('[autocomplete]', QR.el).focus();
if (d.hidden || d.oHidden || d.mozHidden || d.webkitHidden) {
return alert(err);
}
},
cleanError: function() {
- return $('.warning', qr.el).textContent = null;
+ return $('.warning', QR.el).textContent = null;
},
status: function(data) {
var disabled, input, value;
if (data == null) data = {};
if (data.ready) {
- qr.status.ready = true;
- qr.status.banned = data.banned;
- } else if (!qr.status.ready) {
+ QR.status.ready = true;
+ QR.status.banned = data.banned;
+ } else if (!QR.status.ready) {
value = 'Loading';
disabled = true;
}
if (g.dead) {
value = 404;
disabled = true;
- qr.cooldown.auto = false;
- } else if (qr.status.banned) {
+ QR.cooldown.auto = false;
+ } else if (QR.status.banned) {
value = 'Banned';
disabled = true;
} else {
- value = qr.cooldown.seconds || data.progress || value;
+ value = QR.cooldown.seconds || data.progress || value;
}
- if (!qr.el) return;
- input = qr.status.input;
- input.value = qr.cooldown.auto && conf['Cooldown'] ? value ? "Auto " + value : 'Auto' : value || 'Submit';
+ if (!QR.el) return;
+ input = QR.status.input;
+ input.value = QR.cooldown.auto && Conf['Cooldown'] ? value ? "Auto " + value : 'Auto' : value || 'Submit';
return input.disabled = disabled || false;
},
cooldown: {
init: function() {
- if (!conf['Cooldown']) return;
- qr.cooldown.start($.get("/" + g.BOARD + "/cooldown", 0));
- return $.sync("/" + g.BOARD + "/cooldown", qr.cooldown.start);
+ if (!Conf['Cooldown']) return;
+ QR.cooldown.start($.get("/" + g.BOARD + "/cooldown", 0));
+ return $.sync("/" + g.BOARD + "/cooldown", QR.cooldown.start);
},
start: function(timeout) {
var seconds;
seconds = Math.floor((timeout - Date.now()) / 1000);
- return qr.cooldown.count(seconds);
+ return QR.cooldown.count(seconds);
},
set: function(seconds) {
- if (!conf['Cooldown']) return;
- qr.cooldown.count(seconds);
- return $.set("/" + g.BOARD + "/cooldown", Date.now() + seconds * SECOND);
+ if (!Conf['Cooldown']) return;
+ QR.cooldown.count(seconds);
+ return $.set("/" + g.BOARD + "/cooldown", Date.now() + seconds * $.SECOND);
},
count: function(seconds) {
if (!((0 <= seconds && seconds <= 60))) return;
- setTimeout(qr.cooldown.count, 1000, seconds - 1);
- qr.cooldown.seconds = seconds;
+ setTimeout(QR.cooldown.count, 1000, seconds - 1);
+ QR.cooldown.seconds = seconds;
if (seconds === 0) {
$["delete"]("/" + g.BOARD + "/cooldown");
- if (qr.cooldown.auto) qr.submit();
+ if (QR.cooldown.auto) QR.submit();
}
- return qr.status();
+ return QR.status();
}
},
quote: function(e) {
var caretPos, id, range, s, sel, ta, text, _ref;
if (e != null) e.preventDefault();
- qr.open();
+ QR.open();
if (!g.REPLY) {
- $('select', qr.el).value = $.x('ancestor::div[@class="thread"]', this).firstChild.id;
+ $('select', QR.el).value = $.x('ancestor::div[@class="thread"]', this).firstChild.id;
}
id = this.previousElementSibling.hash.slice(1);
text = ">>" + id + "\n";
@@ -1418,9 +1394,9 @@
s = s.replace(/\n/g, '\n>');
text += ">" + s + "\n";
}
- ta = $('textarea', qr.el);
+ ta = $('textarea', QR.el);
caretPos = ta.selectionStart;
- qr.selected.el.lastChild.textContent = qr.selected.com = ta.value = ta.value.slice(0, caretPos) + text + ta.value.slice(ta.selectionEnd);
+ QR.selected.el.lastChild.textContent = QR.selected.com = ta.value = ta.value.slice(0, caretPos) + text + ta.value.slice(ta.selectionEnd);
ta.focus();
ta.selectionEnd = ta.selectionStart = caretPos + text.length;
range = caretPos + text.length;
@@ -1429,8 +1405,8 @@
drag: function(e) {
var i;
i = e.type === 'dragstart' ? 'off' : 'on';
- $[i](d, 'dragover', qr.dragOver);
- return $[i](d, 'drop', qr.dropFile);
+ $[i](d, 'dragover', QR.dragOver);
+ return $[i](d, 'drop', QR.dropFile);
},
dragOver: function(e) {
e.preventDefault();
@@ -1439,23 +1415,23 @@
dropFile: function(e) {
if (!e.dataTransfer.files.length) return;
e.preventDefault();
- qr.open();
- qr.fileInput.call(e.dataTransfer);
- return $.addClass(qr.el, 'dump');
+ QR.open();
+ QR.fileInput.call(e.dataTransfer);
+ return $.addClass(QR.el, 'dump');
},
fileInput: function() {
var file, _i, _len, _ref;
- qr.cleanError();
+ QR.cleanError();
if (this.files.length === 1) {
file = this.files[0];
if (file.size > this.max) {
- qr.error('File too large.');
- qr.resetFileInput();
- } else if (-1 === qr.mimeTypes.indexOf(file.type)) {
- qr.error('Unsupported file type.');
- qr.resetFileInput();
+ QR.error('File too large.');
+ QR.resetFileInput();
+ } else if (-1 === QR.mimeTypes.indexOf(file.type)) {
+ QR.error('Unsupported file type.');
+ QR.resetFileInput();
} else {
- qr.selected.setFile(file);
+ QR.selected.setFile(file);
}
return;
}
@@ -1463,23 +1439,23 @@
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
file = _ref[_i];
if (file.size > this.max) {
- qr.error("File " + file.name + " is too large.");
+ QR.error("File " + file.name + " is too large.");
break;
- } else if (-1 === qr.mimeTypes.indexOf(file.type)) {
- qr.error("" + file.name + ": Unsupported file type.");
+ } else if (-1 === QR.mimeTypes.indexOf(file.type)) {
+ QR.error("" + file.name + ": Unsupported file type.");
break;
}
- if (!qr.replies[qr.replies.length - 1].file) {
- qr.replies[qr.replies.length - 1].setFile(file);
+ if (!QR.replies[QR.replies.length - 1].file) {
+ QR.replies[QR.replies.length - 1].setFile(file);
} else {
- new qr.reply().setFile(file);
+ new QR.reply().setFile(file);
}
}
- $.addClass(qr.el, 'dump');
- return qr.resetFileInput();
+ $.addClass(QR.el, 'dump');
+ return QR.resetFileInput();
},
resetFileInput: function() {
- return $('[type=file]', qr.el).value = null;
+ return $('[type=file]', QR.el).value = null;
},
replies: [],
reply: (function() {
@@ -1487,12 +1463,12 @@
function _Class() {
var persona, prev,
_this = this;
- prev = qr.replies[qr.replies.length - 1];
- persona = $.get('qr.persona', {});
+ prev = QR.replies[QR.replies.length - 1];
+ persona = $.get('QR.persona', {});
this.name = prev ? prev.name : persona.name || null;
this.email = prev && !/^sage$/.test(prev.email) ? prev.email : persona.email || null;
- this.sub = prev && conf['Remember Subject'] ? prev.sub : conf['Remember Subject'] ? persona.sub : null;
- this.spoiler = prev && conf['Remember Spoiler'] ? prev.spoiler : false;
+ this.sub = prev && Conf['Remember Subject'] ? prev.sub : Conf['Remember Subject'] ? persona.sub : null;
+ this.spoiler = prev && Conf['Remember Spoiler'] ? prev.spoiler : false;
this.com = null;
this.el = $.el('a', {
className: 'preview',
@@ -1517,14 +1493,14 @@
return $.id('spoiler').checked = _this.spoiler;
}
});
- $.before($('#addReply', qr.el), this.el);
+ $.before($('#addReply', QR.el), this.el);
$.on(this.el, 'dragstart', this.dragStart);
$.on(this.el, 'dragenter', this.dragEnter);
$.on(this.el, 'dragleave', this.dragLeave);
$.on(this.el, 'dragover', this.dragOver);
$.on(this.el, 'dragend', this.dragEnd);
$.on(this.el, 'drop', this.drop);
- qr.replies.push(this);
+ QR.replies.push(this);
}
_Class.prototype.setFile = function(file) {
@@ -1532,7 +1508,7 @@
_this = this;
this.file = file;
this.el.title = file.name;
- if (qr.spoiler) $('label', this.el).hidden = false;
+ if (QR.spoiler) $('label', this.el).hidden = false;
if (file.type === 'application/pdf') {
this.el.style.backgroundImage = null;
return;
@@ -1576,18 +1552,18 @@
};
_Class.prototype.rmFile = function() {
- qr.resetFileInput();
+ QR.resetFileInput();
delete this.file;
this.el.title = null;
this.el.style.backgroundImage = null;
- if (qr.spoiler) $('label', this.el).hidden = true;
+ if (QR.spoiler) $('label', this.el).hidden = true;
return (window.URL || window.webkitURL).revokeObjectURL(this.url);
};
_Class.prototype.select = function() {
var data, rectEl, rectList, _i, _len, _ref, _ref2;
- if ((_ref = qr.selected) != null) _ref.el.id = null;
- qr.selected = this;
+ if ((_ref = QR.selected) != null) _ref.el.id = null;
+ QR.selected = this;
this.el.id = 'selected';
rectEl = this.el.getBoundingClientRect();
rectList = this.el.parentNode.getBoundingClientRect();
@@ -1595,9 +1571,9 @@
_ref2 = ['name', 'email', 'sub', 'com'];
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
data = _ref2[_i];
- $("[name=" + data + "]", qr.el).value = this[data];
+ $("[name=" + data + "]", QR.el).value = this[data];
}
- return $('#spoiler', qr.el).checked = this.spoiler;
+ return $('#spoiler', QR.el).checked = this.spoiler;
};
_Class.prototype.dragStart = function() {
@@ -1630,8 +1606,8 @@
} else {
$.before(this, el);
}
- reply = qr.replies.splice(oldIndex, 1)[0];
- return qr.replies.splice(newIndex, 0, reply);
+ reply = QR.replies.splice(oldIndex, 1)[0];
+ return QR.replies.splice(newIndex, 0, reply);
};
_Class.prototype.dragEnd = function() {
@@ -1642,15 +1618,15 @@
_Class.prototype.rm = function() {
var index;
- qr.resetFileInput();
+ QR.resetFileInput();
$.rm(this.el);
- index = qr.replies.indexOf(this);
- if (qr.replies.length === 1) {
- new qr.reply().select();
+ index = QR.replies.indexOf(this);
+ if (QR.replies.length === 1) {
+ new QR.reply().select();
} else if (this.el.id === 'selected') {
- (qr.replies[index - 1] || qr.replies[index + 1]).select();
+ (QR.replies[index - 1] || QR.replies[index + 1]).select();
}
- qr.replies.splice(index, 1);
+ QR.replies.splice(index, 1);
(window.URL || window.webkitURL).revokeObjectURL(this.url);
return delete this;
};
@@ -1661,8 +1637,8 @@
captcha: {
init: function() {
var _this = this;
- this.img = $('.captcha > img', qr.el);
- this.input = $('[autocomplete]', qr.el);
+ this.img = $('.captcha > img', QR.el);
+ this.input = $('[autocomplete]', QR.el);
this.challenge = $.id('recaptcha_challenge_field_holder');
$.on(this.img.parentNode, 'click', this.reload);
$.on(this.input, 'keydown', this.keydown);
@@ -1693,7 +1669,7 @@
},
load: function() {
var challenge;
- this.timeout = Date.now() + 26 * MINUTE;
+ this.timeout = Date.now() + 26 * $.MINUTE;
challenge = this.challenge.firstChild.value;
this.img.alt = challenge;
this.img.src = "http://www.google.com/recaptcha/api/image?c=" + challenge;
@@ -1714,11 +1690,11 @@
},
reload: function(focus) {
window.location = 'javascript:Recaptcha.reload()';
- if (focus) return qr.captcha.input.focus();
+ if (focus) return QR.captcha.input.focus();
},
keydown: function(e) {
var c;
- c = qr.captcha;
+ c = QR.captcha;
if (e.keyCode === 8 && !c.input.value) {
c.reload();
} else if (e.keyCode === 13 && e.shiftKey) {
@@ -1731,7 +1707,7 @@
},
dialog: function() {
var e, event, fileInput, input, mimeTypes, name, spoiler, ta, thread, threads, _i, _j, _k, _len, _len2, _len3, _ref, _ref2, _ref3;
- qr.el = ui.dialog('qr', 'top:0;right:0;', '\
+ QR.el = UI.dialog('qr', 'top:0;right:0;', '\
\
Quick Reply
\
×\
@@ -1746,11 +1722,11 @@
\
\
');
- if (conf['Remember QR size'] && engine === 'gecko') {
- $.on(ta = $('textarea', qr.el), 'mouseup', function() {
- return $.set('qr.size', this.style.cssText);
+ if (Conf['Remember QR size'] && $.engine === 'gecko') {
+ $.on(ta = $('textarea', QR.el), 'mouseup', function() {
+ return $.set('QR.size', this.style.cssText);
});
- ta.style.cssText = $.get('qr.size', '');
+ ta.style.cssText = $.get('QR.size', '');
}
mimeTypes = $('.rules').firstChild.textContent.match(/: (.+) /)[1].toLowerCase().replace(/\w+/g, function(type) {
switch (type) {
@@ -1762,14 +1738,14 @@
return "image/" + type;
}
});
- qr.mimeTypes = mimeTypes.split(', ');
- qr.mimeTypes.push('');
- fileInput = $('[type=file]', qr.el);
+ QR.mimeTypes = mimeTypes.split(', ');
+ QR.mimeTypes.push('');
+ fileInput = $('[type=file]', QR.el);
fileInput.max = $('[name=MAX_FILE_SIZE]').value;
fileInput.accept = mimeTypes;
- qr.spoiler = !!$('#com_submit + label');
- spoiler = $('#spoilerLabel', qr.el);
- spoiler.hidden = !qr.spoiler;
+ QR.spoiler = !!$('#com_submit + label');
+ spoiler = $('#spoilerLabel', QR.el);
+ spoiler.hidden = !QR.spoiler;
if (!g.REPLY) {
threads = '
';
_ref = $$('.op');
@@ -1777,82 +1753,82 @@
thread = _ref[_i];
threads += "
";
}
- $.prepend($('.move > span', qr.el), $.el('select', {
+ $.prepend($('.move > span', QR.el), $.el('select', {
innerHTML: threads,
title: 'Create a new thread / Reply to a thread'
}));
- $.on($('select', qr.el), 'mousedown', function(e) {
+ $.on($('select', QR.el), 'mousedown', function(e) {
return e.stopPropagation();
});
}
- $.on($('#autohide', qr.el), 'change', qr.toggleHide);
- $.on($('.close', qr.el), 'click', qr.close);
- $.on($('#dump', qr.el), 'click', function() {
- return qr.el.classList.toggle('dump');
+ $.on($('#autohide', QR.el), 'change', QR.toggleHide);
+ $.on($('.close', QR.el), 'click', QR.close);
+ $.on($('#dump', QR.el), 'click', function() {
+ return QR.el.classList.toggle('dump');
});
- $.on($('#addReply', qr.el), 'click', function() {
- return new qr.reply().select();
+ $.on($('#addReply', QR.el), 'click', function() {
+ return new QR.reply().select();
});
- $.on($('form', qr.el), 'submit', qr.submit);
- $.on($('textarea', qr.el), 'keyup', function() {
- return qr.selected.el.lastChild.textContent = this.value;
+ $.on($('form', QR.el), 'submit', QR.submit);
+ $.on($('textarea', QR.el), 'keyup', function() {
+ return QR.selected.el.lastChild.textContent = this.value;
});
- $.on(fileInput, 'change', qr.fileInput);
+ $.on(fileInput, 'change', QR.fileInput);
$.on(fileInput, 'click', function(e) {
- if (e.shiftKey) return qr.selected.rmFile() || e.preventDefault();
+ if (e.shiftKey) return QR.selected.rmFile() || e.preventDefault();
});
$.on(spoiler.firstChild, 'change', function() {
- return $('input', qr.selected.el).click();
+ return $('input', QR.selected.el).click();
});
- $.on($('.warning', qr.el), 'click', qr.cleanError);
- new qr.reply().select();
+ $.on($('.warning', QR.el), 'click', QR.cleanError);
+ new QR.reply().select();
_ref2 = ['name', 'email', 'sub', 'com'];
for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) {
name = _ref2[_j];
- input = $("[name=" + name + "]", qr.el);
+ input = $("[name=" + name + "]", QR.el);
_ref3 = ['input', 'keyup', 'change', 'paste'];
for (_k = 0, _len3 = _ref3.length; _k < _len3; _k++) {
event = _ref3[_k];
$.on(input, event, function() {
- qr.selected[this.name] = this.value;
- if (qr.cooldown.auto && qr.selected === qr.replies[0] && parseInt(qr.status.input.value.match(/\d+/)) < 6) {
- return qr.cooldown.auto = false;
+ QR.selected[this.name] = this.value;
+ if (QR.cooldown.auto && QR.selected === QR.replies[0] && parseInt(QR.status.input.value.match(/\d+/)) < 6) {
+ return QR.cooldown.auto = false;
}
});
}
}
- $.sync('qr.persona', function(persona) {
+ $.sync('QR.persona', function(persona) {
var key, val, _results;
- if (!qr.el.hidden) return;
+ if (!QR.el.hidden) return;
_results = [];
for (key in persona) {
val = persona[key];
- qr.selected[key] = val;
- _results.push($("[name=" + key + "]", qr.el).value = val);
+ QR.selected[key] = val;
+ _results.push($("[name=" + key + "]", QR.el).value = val);
}
return _results;
});
- qr.status.input = $('[type=submit]', qr.el);
- qr.status();
- qr.cooldown.init();
- qr.captcha.init();
- $.add(d.body, qr.el);
+ QR.status.input = $('[type=submit]', QR.el);
+ QR.status();
+ QR.cooldown.init();
+ QR.captcha.init();
+ $.add(d.body, QR.el);
e = d.createEvent('CustomEvent');
e.initEvent('QRDialogCreation', true, false);
- return qr.el.dispatchEvent(e);
+ return QR.el.dispatchEvent(e);
},
submit: function(e) {
var captcha, captchas, challenge, err, file, m, post, reader, reply, response, threadID;
if (e != null) e.preventDefault();
- if (qr.cooldown.seconds) {
- qr.cooldown.auto = !qr.cooldown.auto;
- qr.status();
+ if (QR.cooldown.seconds) {
+ QR.cooldown.auto = !QR.cooldown.auto;
+ QR.status();
return;
}
- qr.message.send({
+ QR.message.send({
req: 'abort'
});
- reply = qr.replies[0];
+ reply = QR.replies[0];
if (!(reply.com || reply.file)) {
err = 'No file selected.';
} else {
@@ -1864,24 +1840,24 @@
challenge = captcha.challenge;
response = captcha.response;
} else {
- challenge = qr.captcha.img.alt;
- if (response = qr.captcha.input.value) qr.captcha.reload();
+ challenge = QR.captcha.img.alt;
+ if (response = QR.captcha.input.value) QR.captcha.reload();
}
$.set('captchas', captchas);
- qr.captcha.count(captchas.length);
+ QR.captcha.count(captchas.length);
if (!response) err = 'No valid captcha.';
}
if (err) {
- qr.cooldown.auto = false;
- qr.status();
- qr.error(err);
+ QR.cooldown.auto = false;
+ QR.status();
+ QR.error(err);
return;
}
- qr.cleanError();
- threadID = g.THREAD_ID || $('select', qr.el).value;
- qr.cooldown.auto = qr.replies.length > 1;
- if (conf['Auto Hide QR'] && !qr.cooldown.auto) qr.hide();
- if (conf['Thread Watcher'] && conf['Auto Watch Reply'] && threadID !== 'new') {
+ QR.cleanError();
+ threadID = g.THREAD_ID || $('select', QR.el).value;
+ QR.cooldown.auto = QR.replies.length > 1;
+ if (Conf['Auto Hide QR'] && !QR.cooldown.auto) QR.hide();
+ if (Conf['Thread Watcher'] && Conf['Auto Watch Reply'] && threadID !== 'new') {
Watcher.watch(threadID);
}
post = {
@@ -1898,10 +1874,10 @@
recaptcha_challenge_field: challenge,
recaptcha_response_field: response + ' '
};
- qr.status({
+ QR.status({
progress: '...'
});
- if (engine === 'gecko' && reply.file) {
+ if ($.engine === 'gecko' && reply.file) {
file = {};
reader = new FileReader();
reader.onload = function() {
@@ -1909,16 +1885,16 @@
file.name = reply.file.name;
file.type = reply.file.type;
post.upfile = file;
- return qr.message.send(post);
+ return QR.message.send(post);
};
reader.readAsBinaryString(reply.file);
return;
}
if (/chrome/i.test(navigator.userAgent)) {
- qr.message.post(post);
+ QR.message.post(post);
return;
}
- return qr.message.send(post);
+ return QR.message.send(post);
},
response: function(html) {
var b, doc, err, node, persona, postNumber, reply, thread, _, _ref;
@@ -1926,7 +1902,7 @@
innerHTML: html
});
if ($('title', doc).textContent === '4chan - Banned') {
- qr.status({
+ QR.status({
ready: true,
banned: true
});
@@ -1943,58 +1919,58 @@
}
if (err) {
if (/captcha|verification/i.test(err) || err === 'Connection error with sys.4chan.org.') {
- qr.cooldown.auto = !!$.get('captchas', []).length;
- qr.cooldown.set(2);
+ QR.cooldown.auto = !!$.get('captchas', []).length;
+ QR.cooldown.set(2);
} else {
- qr.cooldown.auto = false;
+ QR.cooldown.auto = false;
}
- qr.status();
- qr.error(err, node);
+ QR.status();
+ QR.error(err, node);
return;
}
- reply = qr.replies[0];
- persona = $.get('qr.persona', {});
+ reply = QR.replies[0];
+ persona = $.get('QR.persona', {});
persona = {
name: reply.name,
email: /^sage$/.test(reply.email) ? persona.email : reply.email,
- sub: conf['Remember Subject'] ? reply.sub : null
+ sub: Conf['Remember Subject'] ? reply.sub : null
};
- $.set('qr.persona', persona);
+ $.set('QR.persona', persona);
_ref = b.lastChild.textContent.match(/thread:(\d+),no:(\d+)/), _ = _ref[0], thread = _ref[1], postNumber = _ref[2];
if (thread === '0') {
- if (conf['Thread Watcher'] && conf['Auto Watch']) {
+ if (Conf['Thread Watcher'] && Conf['Auto Watch']) {
$.set('autoWatch', postNumber);
}
location.pathname = "/" + g.BOARD + "/res/" + postNumber;
} else {
- qr.cooldown.auto = qr.replies.length > 1;
- qr.cooldown.set(/sage/i.test(reply.email) ? 60 : 30);
- if (conf['Open Reply in New Tab'] && !g.REPLY && !qr.cooldown.auto) {
+ QR.cooldown.auto = QR.replies.length > 1;
+ QR.cooldown.set(/sage/i.test(reply.email) ? 60 : 30);
+ if (Conf['Open Reply in New Tab'] && !g.REPLY && !QR.cooldown.auto) {
$.open("//boards.4chan.org/" + g.BOARD + "/res/" + thread + "#" + postNumber);
}
}
- if (conf['Persistent QR'] || qr.cooldown.auto) {
+ if (Conf['Persistent QR'] || QR.cooldown.auto) {
reply.rm();
} else {
- qr.close();
+ QR.close();
}
- if (g.REPLY && (conf['Unread Count'] || conf['Unread Favicon'])) {
+ if (g.REPLY && (Conf['Unread Count'] || Conf['Unread Favicon'])) {
Unread.foresee.push(postNumber);
}
- if (g.REPLY && conf['Thread Updater'] && conf['Auto Update This']) {
+ if (g.REPLY && Conf['Thread Updater'] && Conf['Auto Update This']) {
Updater.update();
}
- qr.status();
- return qr.resetFileInput();
+ QR.status();
+ return QR.resetFileInput();
},
message: {
send: function(data) {
var host, window;
if (/chrome/i.test(navigator.userAgent)) {
- qr.message.receive(data);
+ QR.message.receive(data);
return;
}
- data.qr = true;
+ data.QR = true;
host = location.hostname;
window = host === 'boards.4chan.org' ? $.id('iframe').contentWindow : parent;
return window.postMessage(data, '*');
@@ -2003,26 +1979,26 @@
var req, _ref;
req = data.req;
delete data.req;
- delete data.qr;
+ delete data.QR;
switch (req) {
case 'abort':
- if ((_ref = qr.ajax) != null) _ref.abort();
- return qr.message.send({
+ if ((_ref = QR.ajax) != null) _ref.abort();
+ return QR.message.send({
req: 'status'
});
case 'response':
- return qr.response(data.html);
+ return QR.response(data.html);
case 'status':
- return qr.status(data);
+ return QR.status(data);
default:
- return qr.message.post(data);
+ return QR.message.post(data);
}
},
post: function(data) {
var boundary, callbacks, form, i, name, opts, parts, toBin, url, val;
url = data.postURL;
delete data.postURL;
- if (engine === 'gecko' && data.upfile) {
+ if ($.engine === 'gecko' && data.upfile) {
if (!data.binary) {
toBin = function(data, name, val) {
var bb, r;
@@ -2031,7 +2007,7 @@
r = new FileReader();
r.onload = function() {
data[name] = r.result;
- if (!--i) return qr.message.post(data);
+ if (!--i) return QR.message.post(data);
};
return r.readAsBinaryString(bb.getBlob('text/plain'));
};
@@ -2075,13 +2051,13 @@
}
callbacks = {
onload: function() {
- return qr.message.send({
+ return QR.message.send({
req: 'response',
html: this.response
});
},
onerror: function() {
- return qr.message.send({
+ return QR.message.send({
req: 'status',
ready: true,
banned: true
@@ -2093,13 +2069,13 @@
type: 'post',
upCallbacks: {
onload: function() {
- return qr.message.send({
+ return QR.message.send({
req: 'status',
progress: '...'
});
},
onprogress: function(e) {
- return qr.message.send({
+ return QR.message.send({
req: 'status',
progress: "" + (Math.round(e.loaded / e.total * 100)) + "%"
});
@@ -2111,7 +2087,7 @@
'Content-Type': 'multipart/form-data;boundary=' + boundary
};
}
- return qr.ajax = $.ajax(url, callbacks, opts);
+ return QR.ajax = $.ajax(url, callbacks, opts);
}
}
};
@@ -2142,7 +2118,7 @@
innerHTML: '
\
\
\
@@ -2244,7 +2220,7 @@
\
'
});
- _ref = config.main;
+ _ref = Config.main;
for (key in _ref) {
obj = _ref[key];
ul = $.el('ul', {
@@ -2252,7 +2228,7 @@
});
for (key in obj) {
arr = obj[key];
- checked = conf[key] ? 'checked' : '';
+ checked = Conf[key] ? 'checked' : '';
description = arr[1];
li = $.el('li', {
innerHTML: "
: " + description + ""
@@ -2272,13 +2248,13 @@
_ref2 = $$('textarea', dialog);
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
ta = _ref2[_i];
- ta.textContent = conf[ta.name];
+ ta.textContent = Conf[ta.name];
$.on(ta, 'change', $.cb.value);
}
- (back = $('[name=backlink]', dialog)).value = conf['backlink'];
- (time = $('[name=time]', dialog)).value = conf['time'];
- (fileInfoR = $('[name=fileInfoR]', dialog)).value = conf['fileInfoR'];
- (fileInfoT = $('[name=fileInfoT]', dialog)).value = conf['fileInfoT'];
+ (back = $('[name=backlink]', dialog)).value = Conf['backlink'];
+ (time = $('[name=time]', dialog)).value = Conf['time'];
+ (fileInfoR = $('[name=fileInfoR]', dialog)).value = Conf['fileInfoR'];
+ (fileInfoT = $('[name=fileInfoT]', dialog)).value = Conf['fileInfoT'];
$.on(back, 'keyup', $.cb.value);
$.on(back, 'keyup', Options.backlink);
$.on(time, 'keyup', $.cb.value);
@@ -2288,17 +2264,17 @@
$.on(fileInfoT, 'keyup', $.cb.value);
$.on(fileInfoT, 'keyup', Options.fileInfo);
favicon = $('select', dialog);
- favicon.value = conf['favicon'];
+ favicon.value = Conf['favicon'];
$.on(favicon, 'change', $.cb.value);
$.on(favicon, 'change', Options.favicon);
- _ref3 = config.hotkeys;
+ _ref3 = Config.hotkeys;
for (key in _ref3) {
arr = _ref3[key];
tr = $.el('tr', {
innerHTML: "
" + arr[1] + " | | "
});
input = $('input', tr);
- input.value = conf[key];
+ input.value = Conf[key];
$.on(input, 'keydown', Options.keybind);
$.add($('#keybinds_tab + div tbody', dialog), tr);
}
@@ -2307,7 +2283,7 @@
for (_j = 0, _len2 = _ref4.length; _j < _len2; _j++) {
indicator = _ref4[_j];
key = indicator.firstChild.textContent;
- indicator.hidden = conf[key];
+ indicator.hidden = Conf[key];
indicators[key] = indicator;
$.on($("[name='" + key + "']", dialog), 'click', function() {
return indicators[this.name].hidden = this.checked;
@@ -2354,7 +2330,7 @@
return $.id('timePreview').textContent = Time.funk(Time);
},
backlink: function() {
- return $.id('backlinkPreview').textContent = conf['backlink'].replace(/%id/, '123456789');
+ return $.id('backlinkPreview').textContent = Conf['backlink'].replace(/%id/, '123456789');
},
fileInfo: function() {
var type;
@@ -2451,7 +2427,7 @@
},
hide: function(thread) {
var a, div, name, num, op, span, text, trip, uid, _ref, _ref2;
- if (!conf['Show Stubs']) {
+ if (!Conf['Show Stubs']) {
thread.hidden = true;
thread.nextSibling.hidden = true;
return;
@@ -2488,16 +2464,16 @@
Updater = {
init: function() {
var checkbox, checked, dialog, html, input, name, title, _i, _len, _ref;
- html = "
-" + conf['Interval'] + "
";
- checkbox = config.updater.checkbox;
+ html = "
-" + Conf['Interval'] + "
";
+ checkbox = Config.updater.checkbox;
for (name in checkbox) {
title = checkbox[name][1];
- checked = conf[name] ? 'checked' : '';
+ checked = Conf[name] ? 'checked' : '';
html += "
";
}
- checked = conf['Auto Update'] ? 'checked' : '';
- html += "
";
- dialog = ui.dialog('updater', 'bottom: 0; right: 0;', html);
+ checked = Conf['Auto Update'] ? 'checked' : '';
+ html += "
";
+ dialog = UI.dialog('updater', 'bottom: 0; right: 0;', html);
this.count = $('#count', dialog);
this.timer = $('#timer', dialog);
this.br = $('br[clear]');
@@ -2516,11 +2492,11 @@
} else if (input.name === 'Auto Update This') {
$.on(input, 'click', this.cb.autoUpdate);
this.cb.autoUpdate.call(input);
- conf[input.name] = input.checked;
+ Conf[input.name] = input.checked;
}
} else if (input.name === 'Interval') {
$.on(input, 'change', function() {
- return conf['Interval'] = this.value = parseInt(this.value, 10) || conf['Interval'];
+ return Conf['Interval'] = this.value = parseInt(this.value, 10) || Conf['Interval'];
});
$.on(input, 'change', $.cb.value);
} else if (input.type === 'button') {
@@ -2533,7 +2509,7 @@
},
cb: {
verbose: function() {
- if (conf['Verbose']) {
+ if (Conf['Verbose']) {
Updater.count.textContent = '+0';
return Updater.timer.hidden = false;
} else {
@@ -2566,20 +2542,20 @@
Updater.count.className = 'warning';
clearTimeout(Updater.timeoutID);
g.dead = true;
- if (conf['Unread Count']) {
+ if (Conf['Unread Count']) {
Unread.title = Unread.title.match(/^.+-/)[0] + ' 404';
} else {
d.title = d.title.match(/^.+-/)[0] + ' 404';
}
Unread.update(true);
- qr.message.send({
+ QR.message.send({
req: 'abort'
});
- qr.status();
+ QR.status();
return;
}
Updater.retryCoef = 10;
- Updater.timer.textContent = '-' + conf['Interval'];
+ Updater.timer.textContent = '-' + Conf['Interval'];
/*
Status Code 304: Not modified
By sending the `If-Modified-Since` header we get a proper status code, and no response.
@@ -2587,7 +2563,7 @@
and won't load images and scripts when parsing the response.
*/
if (this.status === 304) {
- if (conf['Verbose']) {
+ if (Conf['Verbose']) {
Updater.count.textContent = '+0';
Updater.count.className = null;
}
@@ -2605,8 +2581,8 @@
nodes.push(reply.parentNode.parentNode.parentNode);
}
newPosts = nodes.length;
- scroll = conf['Scrolling'] && Updater.scrollBG() && newPosts && Updater.br.previousElementSibling.getBoundingClientRect().bottom - d.body.clientHeight < 25;
- if (conf['Verbose']) {
+ scroll = Conf['Scrolling'] && Updater.scrollBG() && newPosts && Updater.br.previousElementSibling.getBoundingClientRect().bottom - d.body.clientHeight < 25;
+ if (Conf['Verbose']) {
Updater.count.textContent = "+" + newPosts;
Updater.count.className = newPosts ? 'new' : null;
}
@@ -2651,7 +2627,7 @@
init: function() {
var favicon, html, input, inputs, _i, _len;
html = '
Thread Watcher
';
- 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);
inputs = $$('.op > input');
for (_i = 0, _len = inputs.length; _i < _len; _i++) {
@@ -2748,7 +2724,7 @@
Anonymize = {
init: function() {
- return g.callbacks.push(this.node);
+ return Main.callbacks.push(this.node);
},
node: function(post) {
var name, node;
@@ -2767,14 +2743,14 @@
var link, _i, _len, _ref;
if (g.BOARD === 'f') return;
this.links = [];
- _ref = conf['sauces'].split('\n');
+ _ref = Conf['sauces'].split('\n');
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
link = _ref[_i];
if (link[0] === '#') continue;
this.links.push(this.createSauceLink(link));
}
if (!this.links.length) return;
- return g.callbacks.push(this.node);
+ return Main.callbacks.push(this.node);
},
createSauceLink: function(link) {
var domain, el, href;
@@ -2820,7 +2796,7 @@
RevealSpoilers = {
init: function() {
- return g.callbacks.push(this.node);
+ return Main.callbacks.push(this.node);
},
node: function(post) {
var img;
@@ -2839,7 +2815,7 @@
chanOffset = 5 - new Date().getTimezoneOffset() / 60;
if ($.isDST()) chanOffset--;
this.parse = Date.parse('10/11/11(Tue)18:53') === 1318351980000 ? function(node) {
- return new Date(Date.parse(node.textContent) + chanOffset * HOUR);
+ return new Date(Date.parse(node.textContent) + chanOffset * $.HOUR);
} : function(node) {
var day, hour, min, month, year, _, _ref;
_ref = node.textContent.match(/(\d+)\/(\d+)\/(\d+)\(\w+\)(\d+):(\d+)/), _ = _ref[0], month = _ref[1], day = _ref[2], year = _ref[3], hour = _ref[4], min = _ref[5];
@@ -2848,7 +2824,7 @@
hour = chanOffset + Number(hour);
return new Date(year, month, day, hour, min);
};
- return g.callbacks.push(this.node);
+ return Main.callbacks.push(this.node);
},
node: function(post) {
var node, time;
@@ -2863,7 +2839,7 @@
},
foo: function() {
var code;
- code = conf['time'].replace(/%([A-Za-z])/g, function(s, c) {
+ code = Conf['time'].replace(/%([A-Za-z])/g, function(s, c) {
if (c in Time.formatters) {
return "' + Time.formatters." + c + "() + '";
} else {
@@ -2942,7 +2918,7 @@
init: function() {
if (g.BOARD === 'f') return;
this.setFormats();
- return g.callbacks.push(this.node);
+ return Main.callbacks.push(this.node);
},
node: function(post) {
var data, link, node, regexp, resolution, size, span, unit, _, _ref;
@@ -2967,7 +2943,7 @@
var code, format, funks, i, param;
funks = [];
for (i = 0; i <= 1; i++) {
- format = i ? conf['fileInfoT'] : conf['fileInfoR'];
+ format = i ? Conf['fileInfoT'] : Conf['fileInfoR'];
param = i ? /%([BKlMrs])/g : /%([BKlLMnNrs])/g;
code = format.replace(param, function(s, c) {
if (c in FileInfo.formatters) {
@@ -3064,9 +3040,9 @@
QuoteBacklink = {
init: function() {
var format;
- format = conf['backlink'].replace(/%id/g, "' + id + '");
+ format = Conf['backlink'].replace(/%id/g, "' + id + '");
this.funk = Function('id', "return '" + format + "'");
- return g.callbacks.push(this.node);
+ return Main.callbacks.push(this.node);
},
node: function(post) {
var a, container, el, link, qid, quote, quotes, root, _i, _len, _ref;
@@ -3083,12 +3059,12 @@
textContent: QuoteBacklink.funk(post.id)
});
for (qid in quotes) {
- if (!(el = $.id(qid)) || el.className === 'op' && !conf['OP Backlinks']) {
+ if (!(el = $.id(qid)) || el.className === 'op' && !Conf['OP Backlinks']) {
continue;
}
link = a.cloneNode(true);
- if (conf['Quote Preview']) $.on(link, 'mouseover', QuotePreview.mouseover);
- if (conf['Quote Inline']) {
+ if (Conf['Quote Preview']) $.on(link, 'mouseover', QuotePreview.mouseover);
+ if (Conf['Quote Inline']) {
$.on(link, 'click', QuoteInline.toggle);
} else {
link.setAttribute('onclick', "replyhl('" + post.id + "');");
@@ -3109,7 +3085,7 @@
QuoteInline = {
init: function() {
- return g.callbacks.push(this.node);
+ return Main.callbacks.push(this.node);
},
node: function(post) {
var quote, _i, _j, _len, _len2, _ref, _ref2;
@@ -3152,7 +3128,7 @@
}
if (/\bbacklink\b/.test(q.className)) {
$.after(q.parentNode, inline);
- if (conf['Forward Hiding']) {
+ if (Conf['Forward Hiding']) {
table = $.x('ancestor::table', el);
$.addClass(table, 'forwarded');
++table.title || (table.title = 1);
@@ -3178,7 +3154,7 @@
var inlined, table, _i, _len, _ref;
table = $.x("following::*[@id='i" + id + "']", q);
$.rm(table);
- if (!conf['Forward Hiding']) return;
+ if (!Conf['Forward Hiding']) return;
_ref = $$('.backlink.inlined', table);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
inlined = _ref[_i];
@@ -3227,7 +3203,7 @@
QuotePreview = {
init: function() {
- return g.callbacks.push(this.node);
+ return Main.callbacks.push(this.node);
},
node: function(post) {
var quote, _i, _j, _len, _len2, _ref, _ref2;
@@ -3245,7 +3221,7 @@
mouseover: function(e) {
var el, id, qp, quote, replyID, threadID, _i, _len, _ref;
if (/\binlined\b/.test(this.className)) return;
- qp = ui.el = $.el('div', {
+ qp = UI.el = $.el('div', {
id: 'qp',
className: 'reply dialog'
});
@@ -3253,7 +3229,7 @@
id = this.hash.slice(1);
if (el = $.id(id)) {
qp.innerHTML = el.innerHTML;
- if (conf['Quote Highlighting']) $.addClass(el, 'qphl');
+ if (Conf['Quote Highlighting']) $.addClass(el, 'qphl');
if (/\bbacklink\b/.test(this.className)) {
replyID = $.x('preceding-sibling::input', this.parentNode).name;
_ref = $$('.quotelink', qp);
@@ -3268,23 +3244,23 @@
$.cache(this.pathname, (function() {
return QuotePreview.parse(this, id, threadID);
}));
- ui.hover(e);
+ UI.hover(e);
}
- $.on(this, 'mousemove', ui.hover);
+ $.on(this, 'mousemove', UI.hover);
$.on(this, 'mouseout', QuotePreview.mouseout);
return $.on(this, 'click', QuotePreview.mouseout);
},
mouseout: function() {
var el;
if (el = $.id(this.hash.slice(1))) $.removeClass(el, 'qphl');
- ui.hoverend();
- $.off(this, 'mousemove', ui.hover);
+ UI.hoverend();
+ $.off(this, 'mousemove', UI.hover);
$.off(this, 'mouseout', QuotePreview.mouseout);
return $.off(this, 'click', QuotePreview.mouseout);
},
parse: function(req, id, threadID) {
var doc, node, post, qp;
- if (!((qp = ui.el) && qp.textContent === ("Loading " + id + "..."))) return;
+ if (!((qp = UI.el) && qp.textContent === ("Loading " + id + "..."))) return;
if (req.status !== 200) {
qp.textContent = "" + req.status + " " + req.statusText;
return;
@@ -3298,15 +3274,15 @@
filesize: $('.filesize', qp),
img: $('img[md5]', qp)
};
- if (conf['Image Auto-Gif']) AutoGif.node(post);
- if (conf['Time Formatting']) Time.node(post);
- if (conf['File Info Formatting']) return FileInfo.node(post);
+ if (Conf['Image Auto-Gif']) AutoGif.node(post);
+ if (Conf['Time Formatting']) Time.node(post);
+ if (Conf['File Info Formatting']) return FileInfo.node(post);
}
};
QuoteOP = {
init: function() {
- return g.callbacks.push(this.node);
+ return Main.callbacks.push(this.node);
},
node: function(post) {
var quote, _i, _len, _ref;
@@ -3323,7 +3299,7 @@
QuoteCT = {
init: function() {
- return g.callbacks.push(this.node);
+ return Main.callbacks.push(this.node);
},
node: function(post) {
var path, quote, _i, _len, _ref;
@@ -3342,7 +3318,7 @@
Quotify = {
init: function() {
- return g.callbacks.push(this.node);
+ return Main.callbacks.push(this.node);
},
node: function(post) {
var a, board, data, i, id, index, m, node, nodes, quote, quotes, snapshot, text, _i, _len, _ref;
@@ -3386,7 +3362,7 @@
innerHTML: '[ ! ]',
href: 'javascript:;'
});
- return g.callbacks.push(this.node);
+ return Main.callbacks.push(this.node);
},
node: function(post) {
var a;
@@ -3408,7 +3384,7 @@
ThreadStats = {
init: function() {
var dialog;
- dialog = ui.dialog('stats', 'bottom: 0; left: 0;', '
0 / 0
');
+ dialog = UI.dialog('stats', 'bottom: 0; left: 0;', '
0 / 0
');
dialog.className = 'dialog';
$.add(d.body, dialog);
this.posts = this.images = 0;
@@ -3424,7 +3400,7 @@
return 151;
}
})();
- return g.callbacks.push(this.node);
+ return Main.callbacks.push(this.node);
},
node: function(post) {
var imgcount;
@@ -3444,7 +3420,7 @@
this.title = d.title;
this.update();
$.on(window, 'scroll', Unread.scroll);
- return g.callbacks.push(this.node);
+ return Main.callbacks.push(this.node);
},
replies: [],
foresee: [],
@@ -3486,8 +3462,8 @@
var count;
if (!g.REPLY) return;
count = this.replies.length;
- if (conf['Unread Count']) this.setTitle(count);
- if (!(conf['Unread Favicon'] && (count < 2 || forceUpdate))) return;
+ if (Conf['Unread Count']) this.setTitle(count);
+ if (!(Conf['Unread Favicon'] && (count < 2 || forceUpdate))) return;
Favicon.el.href = g.dead ? count ? Favicon.unreadDead : Favicon.dead : count ? Favicon.unread : Favicon["default"];
return $.add(d.head, Favicon.el);
}
@@ -3504,7 +3480,7 @@
return this["switch"]();
},
"switch": function() {
- switch (conf['favicon']) {
+ switch (Conf['favicon']) {
case 'ferongr':
this.unreadDead = 'data:unreadDead;base64,R0lGODlhEAAQAOMHAOgLAnMFAL8AAOgLAukMA/+AgP+rq////////////////////////////////////yH5BAEKAAcALAAAAAAQABAAAARZ8MhJ6xwDWIBv+AM1fEEIBIVRlNKYrtpIECuGzuwpCLg974EYiXUYkUItjGbC6VQ4omXFiKROA6qSy0A8nAo9GS3YCswIWnOvLAi0be23Z1QtdSUaqXcviQAAOw==';
this.unreadSFW = 'data:unreadSFW;base64,R0lGODlhEAAQAOMHAADX8QBwfgC2zADX8QDY8nnl8qLp8v///////////////////////////////////yH5BAEKAAcALAAAAAAQABAAAARZ8MhJ6xwDWIBv+AM1fEEIBIVRlNKYrtpIECuGzuwpCLg974EYiXUYkUItjGbC6VQ4omXFiKROA6qSy0A8nAo9GS3YCswIWnOvLAi0be23Z1QtdSUaqXcviQAAOw==';
@@ -3539,7 +3515,7 @@
},
image: function(href) {
href = href.split('/');
- if (!conf['404 Redirect']) return;
+ if (!Conf['404 Redirect']) return;
switch (href[3]) {
case 'a':
case 'jp':
@@ -3554,7 +3530,7 @@
if (board == null) board = g.BOARD;
if (id == null) id = g.THREAD_ID;
if (mode == null) mode = 'thread';
- if (!(conf['404 Redirect'] || mode === 'post')) return;
+ if (!(Conf['404 Redirect'] || mode === 'post')) return;
switch (g.BOARD) {
case 'a':
case 'jp':
@@ -3606,41 +3582,41 @@
ImageHover = {
init: function() {
- return g.callbacks.push(this.node);
+ return Main.callbacks.push(this.node);
},
node: function(post) {
if (!post.img) return;
return $.on(post.img, 'mouseover', ImageHover.mouseover);
},
mouseover: function() {
- ui.el = $.el('img', {
+ UI.el = $.el('img', {
id: 'ihover',
src: this.parentNode.href
});
- $.add(d.body, ui.el);
- $.on(ui.el, 'load', ImageHover.load);
- $.on(this, 'mousemove', ui.hover);
+ $.add(d.body, UI.el);
+ $.on(UI.el, 'load', ImageHover.load);
+ $.on(this, 'mousemove', UI.hover);
return $.on(this, 'mouseout', ImageHover.mouseout);
},
load: function() {
var style;
- if (this !== ui.el) return;
+ if (this !== UI.el) return;
style = this.style;
- return ui.hover({
+ return UI.hover({
clientX: -45 + parseInt(style.left),
clientY: 120 + parseInt(style.top)
});
},
mouseout: function() {
- ui.hoverend();
- $.off(this, 'mousemove', ui.hover);
+ UI.hoverend();
+ $.off(this, 'mousemove', UI.hover);
return $.off(this, 'mouseout', ImageHover.mouseout);
}
};
AutoGif = {
init: function() {
- return g.callbacks.push(this.node);
+ return Main.callbacks.push(this.node);
},
node: function(post) {
var img, src;
@@ -3658,7 +3634,7 @@
ImageExpand = {
init: function() {
- g.callbacks.push(this.node);
+ Main.callbacks.push(this.node);
return this.dialog();
},
node: function(post) {
@@ -3683,7 +3659,7 @@
ImageExpand.on = this.checked;
if (ImageExpand.on) {
thumbs = $$('img[md5]');
- if (conf['Expand From Current']) {
+ if (Conf['Expand From Current']) {
for (i = 0, _len = thumbs.length; i < _len; i++) {
thumb = thumbs[i];
if (thumb.getBoundingClientRect().top > 0) break;
@@ -3769,7 +3745,7 @@
url = href + '?' + Date.now();
}
timeoutID = setTimeout(ImageExpand.expand, 10000, thumb, url);
- if (!(engine === 'webkit' && url.split('/')[2] === 'images.4chan.org')) {
+ if (!($.engine === 'webkit' && url.split('/')[2] === 'images.4chan.org')) {
return;
}
return $.ajax(url, {
@@ -3804,24 +3780,23 @@
Main = {
init: function() {
var cutoff, hiddenThreads, id, key, now, path, pathname, temp, timestamp, val, _ref;
+ Main.flatten(null, Config);
path = location.pathname;
pathname = path.slice(1).split('/');
g.BOARD = pathname[0], temp = pathname[1];
if (temp === 'res') {
g.REPLY = true;
g.THREAD_ID = pathname[2];
- } else {
- g.PAGENUM = parseInt(temp) || 0;
}
- for (key in conf) {
- val = conf[key];
- conf[key] = $.get(key, val);
+ for (key in Conf) {
+ val = Conf[key];
+ Conf[key] = $.get(key, val);
}
$.on(window, 'message', Main.message);
switch (location.hostname) {
case 'sys.4chan.org':
if (path === '/robots.txt') {
- qr.message.send({
+ QR.message.send({
req: 'status',
ready: true
});
@@ -3837,7 +3812,7 @@
return;
case 'www.4chan.org':
if (path === '/banned') {
- qr.message.send({
+ QR.message.send({
req: 'status',
ready: true,
banned: true
@@ -3851,12 +3826,12 @@
return;
}
$.ready(Options.init);
- if (conf['Quick Reply'] && conf['Hide Original Post Form'] && g.BOARD !== 'f') {
+ if (Conf['Quick Reply'] && Conf['Hide Original Post Form'] && g.BOARD !== 'f') {
Main.css += 'form[name=post] { display: none; }';
}
Main.addStyle();
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() {
return $.add(d.head, $.el('script', {
src: 'https://raw.github.com/mayhemydg/4chan-x/master/latest.js'
@@ -3865,9 +3840,9 @@
$.set('lastUpdate', now);
}
g.hiddenReplies = $.get("hiddenReplies/" + g.BOARD + "/", {});
- if ($.get('lastChecked', 0) < now - 1 * DAY) {
+ if ($.get('lastChecked', 0) < now - 1 * $.DAY) {
$.set('lastChecked', now);
- cutoff = now - 7 * DAY;
+ cutoff = now - 7 * $.DAY;
hiddenThreads = $.get("hiddenThreads/" + g.BOARD + "/", {});
for (id in hiddenThreads) {
timestamp = hiddenThreads[id];
@@ -3881,23 +3856,23 @@
$.set("hiddenThreads/" + g.BOARD + "/", hiddenThreads);
$.set("hiddenReplies/" + g.BOARD + "/", g.hiddenReplies);
}
- if (conf['Filter']) Filter.init();
- if (conf['Reply Hiding']) ReplyHiding.init();
- if (conf['Filter'] || conf['Reply Hiding']) StrikethroughQuotes.init();
- if (conf['Anonymize']) Anonymize.init();
- if (conf['Time Formatting']) Time.init();
- if (conf['File Info Formatting']) FileInfo.init();
- if (conf['Sauce']) Sauce.init();
- if (conf['Reveal Spoilers']) RevealSpoilers.init();
- if (conf['Image Auto-Gif']) AutoGif.init();
- if (conf['Image Hover']) ImageHover.init();
- if (conf['Report Button']) ReportButton.init();
- if (conf['Resurrect Quotes']) Quotify.init();
- if (conf['Quote Inline']) QuoteInline.init();
- if (conf['Quote Preview']) QuotePreview.init();
- if (conf['Quote Backlinks']) QuoteBacklink.init();
- if (conf['Indicate OP quote']) QuoteOP.init();
- if (conf['Indicate Cross-thread Quotes']) QuoteCT.init();
+ if (Conf['Filter']) Filter.init();
+ if (Conf['Reply Hiding']) ReplyHiding.init();
+ if (Conf['Filter'] || Conf['Reply Hiding']) StrikethroughQuotes.init();
+ if (Conf['Anonymize']) Anonymize.init();
+ if (Conf['Time Formatting']) Time.init();
+ if (Conf['File Info Formatting']) FileInfo.init();
+ if (Conf['Sauce']) Sauce.init();
+ if (Conf['Reveal Spoilers']) RevealSpoilers.init();
+ if (Conf['Image Auto-Gif']) AutoGif.init();
+ if (Conf['Image Hover']) ImageHover.init();
+ if (Conf['Report Button']) ReportButton.init();
+ if (Conf['Resurrect Quotes']) Quotify.init();
+ if (Conf['Quote Inline']) QuoteInline.init();
+ if (Conf['Quote Preview']) QuotePreview.init();
+ if (Conf['Quote Backlinks']) QuoteBacklink.init();
+ if (Conf['Indicate OP quote']) QuoteOP.init();
+ if (Conf['Indicate Cross-thread Quotes']) QuoteCT.init();
return $.ready(Main.ready);
},
ready: function() {
@@ -3907,8 +3882,8 @@
return;
}
if (!$.id('navtopr')) return;
- $.addClass(d.body, "chanx_" + (VERSION.split('.')[1]));
- $.addClass(d.body, engine);
+ $.addClass(d.body, "chanx_" + (Main.version.split('.')[1]));
+ $.addClass(d.body, $.engine);
_ref = ['navtop', 'navbot'];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
nav = _ref[_i];
@@ -3917,49 +3892,49 @@
form = $('form[name=delform]');
Threading.thread(form.firstElementChild);
Favicon.init();
- if (conf['Quick Reply']) qr.init();
- if (conf['Image Expansion']) ImageExpand.init();
- if (conf['Thread Watcher']) {
+ if (Conf['Quick Reply']) QR.init();
+ if (Conf['Image Expansion']) ImageExpand.init();
+ if (Conf['Thread Watcher']) {
setTimeout(function() {
return Watcher.init();
});
}
- if (conf['Keybinds']) {
+ if (Conf['Keybinds']) {
setTimeout(function() {
return Keybinds.init();
});
}
if (g.REPLY) {
- if (conf['Thread Updater']) {
+ if (Conf['Thread Updater']) {
setTimeout(function() {
return Updater.init();
});
}
- if (conf['Thread Stats']) ThreadStats.init();
- if (conf['Reply Navigation']) {
+ if (Conf['Thread Stats']) ThreadStats.init();
+ if (Conf['Reply Navigation']) {
setTimeout(function() {
return Nav.init();
});
}
- if (conf['Post in Title']) TitlePost.init();
- if (conf['Unread Count'] || conf['Unread Favicon']) Unread.init();
+ if (Conf['Post in Title']) TitlePost.init();
+ if (Conf['Unread Count'] || Conf['Unread Favicon']) Unread.init();
} else {
- if (conf['Thread Hiding']) {
+ if (Conf['Thread Hiding']) {
setTimeout(function() {
return ThreadHiding.init();
});
}
- if (conf['Thread Expansion']) {
+ if (Conf['Thread Expansion']) {
setTimeout(function() {
return ExpandThread.init();
});
}
- if (conf['Comment Expansion']) {
+ if (Conf['Comment Expansion']) {
setTimeout(function() {
return ExpandComment.init();
});
}
- if (conf['Index Navigation']) {
+ if (Conf['Index Navigation']) {
setTimeout(function() {
return Nav.init();
});
@@ -3982,6 +3957,19 @@
return $.on(form, 'DOMNodeInserted', Main.listener);
}
},
+ flatten: function(parent, obj) {
+ var key, val;
+ if (obj instanceof Array) {
+ Conf[parent] = obj[0];
+ } else if (typeof obj === 'object') {
+ for (key in obj) {
+ val = obj[key];
+ Main.flatten(key, val);
+ }
+ } else {
+ Conf[parent] = obj;
+ }
+ },
addStyle: function() {
$.off(d, 'DOMNodeInserted', Main.addStyle);
if (d.head) {
@@ -3993,12 +3981,12 @@
message: function(e) {
var data, version;
data = e.data;
- if (data.qr) {
- qr.message.receive(data);
+ if (data.QR) {
+ QR.message.receive(data);
return;
}
version = data.version;
- if (version && version !== VERSION && confirm('An updated version of 4chan X is available, would you like to install it now?')) {
+ if (version && version !== Main.version && confirm('An updated version of 4chan X is available, would you like to install it now?')) {
return window.location = "https://raw.github.com/mayhemydg/4chan-x/" + version + "/4chan_x.user.js";
}
},
@@ -4021,7 +4009,7 @@
},
node: function(nodes, notify) {
var callback, node, _i, _j, _len, _len2, _ref;
- _ref = g.callbacks;
+ _ref = Main.callbacks;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
callback = _ref[_i];
try {
@@ -4031,7 +4019,7 @@
}
} catch (err) {
if (notify) {
- alert("4chan X (" + VERSION + ") error: " + err.message + "\nhttp://mayhemydg.github.com/4chan-x/#bug-report\n\n" + err.stack);
+ alert("4chan X (" + Main.version + ") error: " + err.message + "\nhttp://mayhemydg.github.com/4chan-x/#bug-report\n\n" + err.stack);
}
}
}
@@ -4054,6 +4042,9 @@
target = e.target;
if (target.nodeName === 'TABLE') return Main.node([Main.preParse(target)]);
},
+ namespace: '4chan_x.',
+ version: '2.29.1',
+ callbacks: [],
css: '\
/* dialog styling */\
.dialog {\
diff --git a/script.coffee b/script.coffee
index 84688eec2..82b0341fd 100644
--- a/script.coffee
+++ b/script.coffee
@@ -1,4 +1,4 @@
-config =
+Config =
main:
Enhancing:
'404 Redirect': [true, 'Redirect dead threads and images']
@@ -154,72 +154,49 @@ config =
'Auto Update': [true, 'Automatically fetch new posts']
'Interval': 30
-# XXX Chrome can't into {log} = console
-# XXX GreaseMonkey can't into console.log.bind
-log = console.log.bind? console
-
-# flatten the config
-conf = {}
-(flatten = (parent, obj) ->
- if obj instanceof Array
- conf[parent] = obj[0]
- else if typeof obj is 'object'
- for key, val of obj
- flatten key, val
- else # string or number
- conf[parent] = obj
- return
-) null, config
-
-NAMESPACE = '4chan_x.'
-VERSION = '2.29.1'
-SECOND = 1000
-MINUTE = 60*SECOND
-HOUR = 60*MINUTE
-DAY = 24*HOUR
-engine = /WebKit|Presto|Gecko/.exec(navigator.userAgent)[0].toLowerCase()
+Conf = {}
d = document
-g = callbacks: []
+g = {}
-ui =
+UI =
dialog: (id, position, html) ->
el = d.createElement 'div'
el.className = 'reply dialog'
el.innerHTML = html
el.id = id
- el.style.cssText = if saved = localStorage["#{NAMESPACE}#{id}.position"] then saved else position
- el.querySelector('.move').addEventListener 'mousedown', ui.dragstart, false
+ el.style.cssText = if saved = localStorage["#{Main.namespace}#{id}.position"] then saved else position
+ el.querySelector('.move').addEventListener 'mousedown', UI.dragstart, false
el
dragstart: (e) ->
#prevent text selection
e.preventDefault()
- ui.el = el = @parentNode
- d.addEventListener 'mousemove', ui.drag, false
- d.addEventListener 'mouseup', ui.dragend, false
+ UI.el = el = @parentNode
+ d.addEventListener 'mousemove', UI.drag, false
+ d.addEventListener 'mouseup', UI.dragend, false
#distance from pointer to el edge is constant; calculate it here.
# XXX opera reports el.offsetLeft / el.offsetTop as 0
rect = el.getBoundingClientRect()
- ui.dx = e.clientX - rect.left
- ui.dy = e.clientY - rect.top
+ UI.dx = e.clientX - rect.left
+ UI.dy = e.clientY - rect.top
#factor out el from document dimensions
- ui.width = d.body.clientWidth - el.offsetWidth
- ui.height = d.body.clientHeight - el.offsetHeight
+ UI.width = d.body.clientWidth - el.offsetWidth
+ UI.height = d.body.clientHeight - el.offsetHeight
drag: (e) ->
- left = e.clientX - ui.dx
- top = e.clientY - ui.dy
+ left = e.clientX - UI.dx
+ top = e.clientY - UI.dy
left =
if left < 10 then 0
- else if ui.width - left < 10 then null
+ else if UI.width - left < 10 then null
else left
top =
if top < 10 then 0
- else if ui.height - top < 10 then null
+ else if UI.height - top < 10 then null
else top
right = if left is null then 0 else null
bottom = if top is null then 0 else null
#using null instead of '' is 4% faster
#these 4 statements are 40% faster than 1 style.cssText
- {style} = ui.el
+ {style} = UI.el
style.top = top
style.right = right
style.bottom = bottom
@@ -228,13 +205,13 @@ ui =
#$ coffee -bpe '{a} = {b} = c'
#var a, b;
#a = (b = c.b, c).a;
- {el} = ui
- localStorage["#{NAMESPACE}#{el.id}.position"] = el.style.cssText
- d.removeEventListener 'mousemove', ui.drag, false
- d.removeEventListener 'mouseup', ui.dragend, false
+ {el} = UI
+ localStorage["#{Main.namespace}#{el.id}.position"] = el.style.cssText
+ d.removeEventListener 'mousemove', UI.drag, false
+ d.removeEventListener 'mouseup', UI.dragend, false
hover: (e) ->
{clientX, clientY} = e
- {el} = ui
+ {el} = UI
{style} = el
{clientHeight, clientWidth} = d.body
height = el.offsetHeight
@@ -256,8 +233,8 @@ ui =
style.right = clientWidth - clientX + 45
hoverend: ->
- $.rm ui.el
- delete ui.el
+ $.rm UI.el
+ delete UI.el
###
loosely follows the jquery api:
@@ -273,6 +250,14 @@ $.extend = (object, properties) ->
return
$.extend $,
+ SECOND: 1000
+ MINUTE: 1000*60
+ HOUR : 1000*60*60
+ DAY : 1000*60*60*24
+ log:
+ # XXX GreaseMonkey can't into console.log.bind
+ console.log.bind? console
+ engine: /WebKit|Presto|Gecko/.exec(navigator.userAgent)[0].toLowerCase()
ready: (fc) ->
if /interactive|complete/.test d.readyState
# Execute the functions in parallel.
@@ -284,7 +269,7 @@ $.extend $,
$.on d, 'DOMContentLoaded', cb
sync: (key, cb) ->
$.on window, 'storage', (e) ->
- cb JSON.parse e.newValue if e.key is "#{NAMESPACE}#{key}"
+ cb JSON.parse e.newValue if e.key is "#{Main.namespace}#{key}"
id: (id) ->
d.getElementById id
ajax: (url, callbacks, opts={}) ->
@@ -312,10 +297,10 @@ $.extend $,
cb:
checked: ->
$.set @name, @checked
- conf[@name] = @checked
+ Conf[@name] = @checked
value: ->
$.set @name, @value.trim()
- conf[@name] = @value
+ Conf[@name] = @value
addStyle: (css) ->
style = $.el 'style',
textContent: css
@@ -423,29 +408,29 @@ $.cache.requests = {}
$.extend $,
if GM_deleteValue?
delete: (name) ->
- name = NAMESPACE + name
+ name = Main.namespace + name
GM_deleteValue name
get: (name, defaultValue) ->
- name = NAMESPACE + name
+ name = Main.namespace + name
if value = GM_getValue name
JSON.parse value
else
defaultValue
set: (name, value) ->
- name = NAMESPACE + name
+ name = Main.namespace + name
# for `storage` events
localStorage.setItem name, JSON.stringify value
GM_setValue name, JSON.stringify value
else
delete: (name) ->
- localStorage.removeItem NAMESPACE + name
+ localStorage.removeItem Main.namespace + name
get: (name, defaultValue) ->
- if value = localStorage.getItem NAMESPACE + name
+ if value = localStorage.getItem Main.namespace + name
JSON.parse value
else
defaultValue
set: (name, value) ->
- localStorage.setItem NAMESPACE + name, JSON.stringify value
+ localStorage.setItem Main.namespace + name, JSON.stringify value
$$ = (selector, root=d.body) ->
Array::slice.call root.querySelectorAll selector
@@ -453,9 +438,9 @@ $$ = (selector, root=d.body) ->
Filter =
filters: {}
init: ->
- for key of config.filter
+ for key of Config.filter
@filters[key] = []
- for filter in conf[key].split '\n'
+ for filter in Conf[key].split '\n'
continue if filter[0] is '#'
unless regexp = filter.match /\/(.+)\/(\w*)/
@@ -504,7 +489,7 @@ Filter =
delete @filters[key]
if Object.keys(@filters).length
- g.callbacks.push @node
+ Main.callbacks.push @node
createFilter: (regexp, op, hl, top) ->
test =
@@ -610,13 +595,13 @@ Filter =
StrikethroughQuotes =
init: ->
- g.callbacks.push @node
+ Main.callbacks.push @node
node: (post) ->
return if post.isInlined
for quote in post.quotes
if (el = $.id quote.hash[1..]) and el.parentNode.parentNode.parentNode.hidden
$.addClass quote, 'filtered'
- ReplyHiding.hide post.root if conf['Recursive Filtering']
+ ReplyHiding.hide post.root if Conf['Recursive Filtering']
return
ExpandComment =
@@ -655,15 +640,15 @@ ExpandComment =
threadId: threadID
quotes: quotes
backlinks: []
- if conf['Resurrect Quotes']
+ if Conf['Resurrect Quotes']
Quotify.node post
- if conf['Quote Preview']
+ if Conf['Quote Preview']
QuotePreview.node post
- if conf['Quote Inline']
+ if Conf['Quote Inline']
QuoteInline.node post
- if conf['Indicate OP quote']
+ if Conf['Indicate OP quote']
QuoteOP.node post
- if conf['Indicate Cross-thread Quotes']
+ if Conf['Indicate Cross-thread Quotes']
QuoteCT.node post
$.replace a.parentNode.parentNode, node.lastChild
@@ -747,7 +732,7 @@ ReplyHiding =
noWrap: true
className: 'replyhider'
innerHTML: '
[ - ]'
- g.callbacks.push @node
+ Main.callbacks.push @node
node: (post) ->
return if post.class
@@ -781,7 +766,7 @@ ReplyHiding =
table.hidden = true
- return unless conf['Show Stubs']
+ return unless Conf['Show Stubs']
name = $('.commentpostername', table).textContent
uid = $('.posteruid', table)?.textContent or ''
@@ -806,20 +791,20 @@ Keybinds =
thread = Nav.getThread()
switch key
# QR & Options
- when conf.openQR
+ when Conf.openQR
Keybinds.qr thread, true
- when conf.openEmptyQR
+ when Conf.openEmptyQR
Keybinds.qr thread
- when conf.openOptions
+ when Conf.openOptions
Options.dialog() unless $.id 'overlay'
- when conf.close
+ when Conf.close
if o = $.id 'overlay'
Options.close.call o
- else if qr.el
- qr.close()
- when conf.submit
- qr.submit() if qr.el and !qr.status()
- when conf.spoiler
+ else if QR.el
+ QR.close()
+ when Conf.submit
+ QR.submit() if QR.el and !QR.status()
+ when Conf.spoiler
ta = e.target
return if ta.nodeName isnt 'TEXTAREA'
@@ -836,44 +821,44 @@ Keybinds =
# Move the caret to the end of the selection.
ta.setSelectionRange range, range
# Thread related
- when conf.watch
+ when Conf.watch
Watcher.toggle thread
- when conf.update
+ when Conf.update
Updater.update()
- when conf.unreadCountTo0
+ when Conf.unreadCountTo0
Unread.replies = []
Unread.update()
# Images
- when conf.expandImage
+ when Conf.expandImage
Keybinds.img thread
- when conf.expandAllImages
+ when Conf.expandAllImages
Keybinds.img thread, true
# Board Navigation
- when conf.zero
+ when Conf.zero
window.location = "/#{g.BOARD}/0#0"
- when conf.nextPage
+ when Conf.nextPage
$('input[value=Next]')?.click()
- when conf.previousPage
+ when Conf.previousPage
$('input[value=Previous]')?.click()
# Thread Navigation
- when conf.nextThread
+ when Conf.nextThread
return if g.REPLY
Nav.scroll +1
- when conf.previousThread
+ when Conf.previousThread
return if g.REPLY
Nav.scroll -1
- when conf.expandThread
+ when Conf.expandThread
ExpandThread.toggle thread
- when conf.openThread
+ when Conf.openThread
Keybinds.open thread
- when conf.openThreadTab
+ when Conf.openThreadTab
Keybinds.open thread, true
# Reply Navigation
- when conf.nextReply
+ when Conf.nextReply
Keybinds.hl +1, thread
- when conf.previousReply
+ when Conf.previousReply
Keybinds.hl -1, thread
- when conf.hide
+ when Conf.hide
ThreadHiding.toggle thread if /\bthread\b/.test thread.className
else
return
@@ -912,10 +897,10 @@ Keybinds =
qr: (thread, quote) ->
if quote
- qr.quote.call $ '.quotejs + .quotejs', $('.replyhl', thread) or thread
+ QR.quote.call $ '.quotejs + .quotejs', $('.replyhl', thread) or thread
else
- qr.open()
- $('textarea', qr.el).focus()
+ QR.open()
+ $('textarea', QR.el).focus()
open: (thread, tab) ->
id = thread.firstChild.id
@@ -1014,25 +999,25 @@ Nav =
{top} = Nav.threads[i]?.getBoundingClientRect()
window.scrollBy 0, top
-qr =
+QR =
init: ->
return unless $.id 'recaptcha_challenge_field_holder'
- g.callbacks.push @node
+ Main.callbacks.push @node
setTimeout @asyncInit
asyncInit: ->
- if conf['Hide Original Post Form']
+ if Conf['Hide Original Post Form']
link = $.el 'h1', innerHTML: "
#{if g.REPLY then 'Quick Reply' else 'New Thread'}"
$.on $('a', link), 'click', ->
- qr.open()
- $('select', qr.el).value = 'new' unless g.REPLY
- $('textarea', qr.el).focus()
+ QR.open()
+ $('select', QR.el).value = 'new' unless g.REPLY
+ $('textarea', QR.el).focus()
form = d.forms[0]
$.before form, link
# CORS is ignored for content script on Chrome, but not Safari/Oprah/Firefox.
if /chrome/i.test navigator.userAgent
- qr.status ready: true
+ QR.status ready: true
else
iframe = $.el 'iframe',
id: 'iframe'
@@ -1040,7 +1025,7 @@ qr =
$.on iframe, 'error', -> @src = @src
# Greasemonkey ghetto fix
loadChecking = (iframe) ->
- unless qr.status.ready
+ unless QR.status.ready
iframe.src = 'about:blank'
setTimeout (-> iframe.src = 'https://sys.4chan.org/robots.txt'), 100
$.on iframe, 'load', -> if @src isnt 'about:blank' then setTimeout loadChecking, 500, @
@@ -1051,79 +1036,79 @@ qr =
$.add d.head, script
$.rm script
- if conf['Persistent QR']
- qr.dialog()
- qr.hide() if conf['Auto Hide QR']
- $.on d, 'dragover', qr.dragOver
- $.on d, 'drop', qr.dropFile
- $.on d, 'dragstart', qr.drag
- $.on d, 'dragend', qr.drag
+ if Conf['Persistent QR']
+ QR.dialog()
+ QR.hide() if Conf['Auto Hide QR']
+ $.on d, 'dragover', QR.dragOver
+ $.on d, 'drop', QR.dropFile
+ $.on d, 'dragstart', QR.drag
+ $.on d, 'dragend', QR.drag
node: (post) ->
- $.on $('.quotejs + .quotejs', post.el), 'click', qr.quote
+ $.on $('.quotejs + .quotejs', post.el), 'click', QR.quote
open: ->
- if qr.el
- qr.el.hidden = false
- qr.unhide()
+ if QR.el
+ QR.el.hidden = false
+ QR.unhide()
else
- qr.dialog()
+ QR.dialog()
close: ->
- qr.el.hidden = true
- qr.message.send req: 'abort'
+ QR.el.hidden = true
+ QR.message.send req: 'abort'
d.activeElement.blur()
- $.removeClass qr.el, 'dump'
- for i in qr.replies
- qr.replies[0].rm()
- qr.cooldown.auto = false
- qr.status()
- qr.resetFileInput()
- if not conf['Remember Spoiler'] and (spoiler = $.id 'spoiler').checked
+ $.removeClass QR.el, 'dump'
+ for i in QR.replies
+ QR.replies[0].rm()
+ QR.cooldown.auto = false
+ QR.status()
+ QR.resetFileInput()
+ if not Conf['Remember Spoiler'] and (spoiler = $.id 'spoiler').checked
spoiler.click()
- qr.cleanError()
+ QR.cleanError()
hide: ->
d.activeElement.blur()
- $.addClass qr.el, 'autohide'
+ $.addClass QR.el, 'autohide'
$.id('autohide').checked = true
unhide: ->
- $.removeClass qr.el, 'autohide'
+ $.removeClass QR.el, 'autohide'
$.id('autohide').checked = false
toggleHide: ->
- @checked and qr.hide() or qr.unhide()
+ @checked and QR.hide() or QR.unhide()
error: (err, node) ->
- el = $ '.warning', qr.el
+ el = $ '.warning', QR.el
el.textContent = err
$.replace el.firstChild, node if node
- qr.open()
+ QR.open()
if /captcha|verification/i.test err
# 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
cleanError: ->
- $('.warning', qr.el).textContent = null
+ $('.warning', QR.el).textContent = null
status: (data={}) ->
if data.ready
- qr.status.ready = true
- qr.status.banned = data.banned
- else unless qr.status.ready
+ QR.status.ready = true
+ QR.status.banned = data.banned
+ else unless QR.status.ready
value = 'Loading'
disabled = true
if g.dead
value = 404
disabled = true
- qr.cooldown.auto = false
- else if qr.status.banned
+ QR.cooldown.auto = false
+ else if QR.status.banned
value = 'Banned'
disabled = true
else
# do not cancel `value = 'Loading'` once the cooldown is over
- value = qr.cooldown.seconds or data.progress or value
- return unless qr.el
- {input} = qr.status
+ value = QR.cooldown.seconds or data.progress or value
+ return unless QR.el
+ {input} = QR.status
input.value =
- if qr.cooldown.auto and conf['Cooldown']
+ if QR.cooldown.auto and Conf['Cooldown']
if value then "Auto #{value}" else 'Auto'
else
value or 'Submit'
@@ -1131,30 +1116,30 @@ qr =
cooldown:
init: ->
- return unless conf['Cooldown']
- qr.cooldown.start $.get "/#{g.BOARD}/cooldown", 0
- $.sync "/#{g.BOARD}/cooldown", qr.cooldown.start
+ return unless Conf['Cooldown']
+ QR.cooldown.start $.get "/#{g.BOARD}/cooldown", 0
+ $.sync "/#{g.BOARD}/cooldown", QR.cooldown.start
start: (timeout) ->
seconds = Math.floor (timeout - Date.now()) / 1000
- qr.cooldown.count seconds
+ QR.cooldown.count seconds
set: (seconds) ->
- return unless conf['Cooldown']
- qr.cooldown.count seconds
- $.set "/#{g.BOARD}/cooldown", Date.now() + seconds*SECOND
+ return unless Conf['Cooldown']
+ QR.cooldown.count seconds
+ $.set "/#{g.BOARD}/cooldown", Date.now() + seconds*$.SECOND
count: (seconds) ->
return unless 0 <= seconds <= 60
- setTimeout qr.cooldown.count, 1000, seconds-1
- qr.cooldown.seconds = seconds
+ setTimeout QR.cooldown.count, 1000, seconds-1
+ QR.cooldown.seconds = seconds
if seconds is 0
$.delete "/#{g.BOARD}/cooldown"
- qr.submit() if qr.cooldown.auto
- qr.status()
+ QR.submit() if QR.cooldown.auto
+ QR.status()
quote: (e) ->
e?.preventDefault()
- qr.open()
+ QR.open()
unless g.REPLY
- $('select', qr.el).value = $.x('ancestor::div[@class="thread"]', @).firstChild.id
+ $('select', QR.el).value = $.x('ancestor::div[@class="thread"]', @).firstChild.id
# Make sure we get the correct number, even with XXX censors
id = @previousElementSibling.hash[1..]
@@ -1165,12 +1150,12 @@ qr =
s = s.replace /\n/g, '\n>'
text += ">#{s}\n"
- ta = $ 'textarea', qr.el
+ ta = $ 'textarea', QR.el
caretPos = ta.selectionStart
# Replace selection for text.
# onchange event isn't triggered, save value.
- qr.selected.el.lastChild.textContent =
- qr.selected.com =
+ QR.selected.el.lastChild.textContent =
+ QR.selected.com =
ta.value =
ta.value[...caretPos] + text + ta.value[ta.selectionEnd..]
ta.focus()
@@ -1182,8 +1167,8 @@ qr =
drag: (e) ->
# Let it drag anything from the page.
i = if e.type is 'dragstart' then 'off' else 'on'
- $[i] d, 'dragover', qr.dragOver
- $[i] d, 'drop', qr.dropFile
+ $[i] d, 'dragover', QR.dragOver
+ $[i] d, 'drop', QR.dropFile
dragOver: (e) ->
e.preventDefault()
e.dataTransfer.dropEffect = 'copy' # cursor feedback
@@ -1191,51 +1176,51 @@ qr =
# Let it only handle files from the desktop.
return unless e.dataTransfer.files.length
e.preventDefault()
- qr.open()
- qr.fileInput.call e.dataTransfer
- $.addClass qr.el, 'dump'
+ QR.open()
+ QR.fileInput.call e.dataTransfer
+ $.addClass QR.el, 'dump'
fileInput: ->
- qr.cleanError()
+ QR.cleanError()
# Set or change current reply's file.
if @files.length is 1
file = @files[0]
if file.size > @max
- qr.error 'File too large.'
- qr.resetFileInput()
- else if -1 is qr.mimeTypes.indexOf file.type
- qr.error 'Unsupported file type.'
- qr.resetFileInput()
+ QR.error 'File too large.'
+ QR.resetFileInput()
+ else if -1 is QR.mimeTypes.indexOf file.type
+ QR.error 'Unsupported file type.'
+ QR.resetFileInput()
else
- qr.selected.setFile file
+ QR.selected.setFile file
return
# Create new replies with these files.
for file in @files
if file.size > @max
- qr.error "File #{file.name} is too large."
+ QR.error "File #{file.name} is too large."
break
- else if -1 is qr.mimeTypes.indexOf file.type
- qr.error "#{file.name}: Unsupported file type."
+ else if -1 is QR.mimeTypes.indexOf file.type
+ QR.error "#{file.name}: Unsupported file type."
break
- unless qr.replies[qr.replies.length - 1].file
+ unless QR.replies[QR.replies.length - 1].file
# set last reply's file
- qr.replies[qr.replies.length - 1].setFile file
+ QR.replies[QR.replies.length - 1].setFile file
else
- new qr.reply().setFile file
- $.addClass qr.el, 'dump'
- qr.resetFileInput() # reset input
+ new QR.reply().setFile file
+ $.addClass QR.el, 'dump'
+ QR.resetFileInput() # reset input
resetFileInput: ->
- $('[type=file]', qr.el).value = null
+ $('[type=file]', QR.el).value = null
replies: []
reply: class
constructor: ->
# set values, or null, to avoid 'undefined' values in inputs
- prev = qr.replies[qr.replies.length-1]
- persona = $.get 'qr.persona', {}
+ prev = QR.replies[QR.replies.length-1]
+ persona = $.get 'QR.persona', {}
@name = if prev then prev.name else persona.name or null
@email = if prev and !/^sage$/.test prev.email then prev.email else persona.email or null
- @sub = if prev and conf['Remember Subject'] then prev.sub else if conf['Remember Subject'] then persona.sub else null
- @spoiler = if prev and conf['Remember Spoiler'] then prev.spoiler else false
+ @sub = if prev and Conf['Remember Subject'] then prev.sub else if Conf['Remember Subject'] then persona.sub else null
+ @spoiler = if prev and Conf['Remember Spoiler'] then prev.spoiler else false
@com = null
@el = $.el 'a',
@@ -1252,7 +1237,7 @@ qr =
$.on $('input', @el), 'change', (e) =>
@spoiler = e.target.checked
$.id('spoiler').checked = @spoiler if @el.id is 'selected'
- $.before $('#addReply', qr.el), @el
+ $.before $('#addReply', QR.el), @el
$.on @el, 'dragstart', @dragStart
$.on @el, 'dragenter', @dragEnter
@@ -1261,10 +1246,10 @@ qr =
$.on @el, 'dragend', @dragEnd
$.on @el, 'drop', @drop
- qr.replies.push @
+ QR.replies.push @
setFile: (@file) ->
@el.title = file.name
- $('label', @el).hidden = false if qr.spoiler
+ $('label', @el).hidden = false if QR.spoiler
if file.type is 'application/pdf'
@el.style.backgroundImage = null
return
@@ -1312,15 +1297,15 @@ qr =
img.src = fileUrl
rmFile: ->
- qr.resetFileInput()
+ QR.resetFileInput()
delete @file
@el.title = null
@el.style.backgroundImage = null
- $('label', @el).hidden = true if qr.spoiler
+ $('label', @el).hidden = true if QR.spoiler
(window.URL or window.webkitURL).revokeObjectURL @url
select: ->
- qr.selected?.el.id = null
- qr.selected = @
+ QR.selected?.el.id = null
+ QR.selected = @
@el.id = 'selected'
# Scroll the list to center the focused reply.
rectEl = @el.getBoundingClientRect()
@@ -1328,8 +1313,8 @@ qr =
@el.parentNode.scrollLeft += rectEl.left + rectEl.width/2 - rectList.left - rectList.width/2
# Load this reply's values.
for data in ['name', 'email', 'sub', 'com']
- $("[name=#{data}]", qr.el).value = @[data]
- $('#spoiler', qr.el).checked = @spoiler
+ $("[name=#{data}]", QR.el).value = @[data]
+ $('#spoiler', QR.el).checked = @spoiler
dragStart: ->
$.addClass @, 'drag'
dragEnter: ->
@@ -1348,28 +1333,28 @@ qr =
$.after @, el
else
$.before @, el
- reply = qr.replies.splice(oldIndex, 1)[0]
- qr.replies.splice newIndex, 0, reply
+ reply = QR.replies.splice(oldIndex, 1)[0]
+ QR.replies.splice newIndex, 0, reply
dragEnd: ->
$.removeClass @, 'drag'
if el = $ '.over', @parentNode
$.removeClass el, 'over'
rm: ->
- qr.resetFileInput()
+ QR.resetFileInput()
$.rm @el
- index = qr.replies.indexOf @
- if qr.replies.length is 1
- new qr.reply().select()
+ index = QR.replies.indexOf @
+ if QR.replies.length is 1
+ new QR.reply().select()
else if @el.id is 'selected'
- (qr.replies[index-1] or qr.replies[index+1]).select()
- qr.replies.splice index, 1
+ (QR.replies[index-1] or QR.replies[index+1]).select()
+ QR.replies.splice index, 1
(window.URL or window.webkitURL).revokeObjectURL @url
delete @
captcha:
init: ->
- @img = $ '.captcha > img', qr.el
- @input = $ '[autocomplete]', qr.el
+ @img = $ '.captcha > img', QR.el
+ @input = $ '[autocomplete]', QR.el
@challenge = $.id 'recaptcha_challenge_field_holder'
$.on @img.parentNode, 'click', @reload
$.on @input, 'keydown', @keydown
@@ -1393,7 +1378,7 @@ qr =
@reload()
load: ->
# Timeout is available at RecaptchaState.timeout in seconds.
- @timeout = Date.now() + 26*MINUTE
+ @timeout = Date.now() + 26*$.MINUTE
challenge = @challenge.firstChild.value
@img.alt = challenge
@img.src = "http://www.google.com/recaptcha/api/image?c=#{challenge}"
@@ -1410,9 +1395,9 @@ qr =
reload: (focus) ->
window.location = 'javascript:Recaptcha.reload()'
# Focus if we meant to.
- qr.captcha.input.focus() if focus
+ QR.captcha.input.focus() if focus
keydown: (e) ->
- c = qr.captcha
+ c = QR.captcha
if e.keyCode is 8 and not c.input.value
c.reload()
else if e.keyCode is 13 and e.shiftKey
@@ -1422,7 +1407,7 @@ qr =
e.preventDefault()
dialog: ->
- qr.el = ui.dialog 'qr', 'top:0;right:0;', '
+ QR.el = UI.dialog 'qr', 'top:0;right:0;', '
Quick Reply
×
@@ -1438,10 +1423,10 @@ qr =
'
- if conf['Remember QR size'] and engine is 'gecko'
- $.on ta = $('textarea', qr.el), 'mouseup', ->
- $.set 'qr.size', @style.cssText
- ta.style.cssText = $.get 'qr.size', ''
+ if Conf['Remember QR size'] and $.engine is 'gecko'
+ $.on ta = $('textarea', QR.el), 'mouseup', ->
+ $.set 'QR.size', @style.cssText
+ ta.style.cssText = $.get 'QR.size', ''
# Allow only this board's supported files.
mimeTypes = $('.rules').firstChild.textContent.match(/: (.+) /)[1].toLowerCase().replace /\w+/g, (type) ->
@@ -1452,78 +1437,78 @@ qr =
'application/pdf'
else
"image/#{type}"
- qr.mimeTypes = mimeTypes.split ', '
+ QR.mimeTypes = mimeTypes.split ', '
# Add empty mimeType to avoid errors with URLs selected in Window's file dialog.
- qr.mimeTypes.push ''
- fileInput = $ '[type=file]', qr.el
+ QR.mimeTypes.push ''
+ fileInput = $ '[type=file]', QR.el
fileInput.max = $('[name=MAX_FILE_SIZE]').value
fileInput.accept = mimeTypes
- qr.spoiler = !!$ '#com_submit + label'
- spoiler = $ '#spoilerLabel', qr.el
- spoiler.hidden = !qr.spoiler
+ QR.spoiler = !!$ '#com_submit + label'
+ spoiler = $ '#spoilerLabel', QR.el
+ spoiler.hidden = !QR.spoiler
unless g.REPLY
# Make a list with visible threads and an option to create a new one.
threads = '
'
for thread in $$ '.op'
threads += "
"
- $.prepend $('.move > span', qr.el), $.el 'select'
+ $.prepend $('.move > span', QR.el), $.el 'select'
innerHTML: threads
title: 'Create a new thread / Reply to a thread'
- $.on $('select', qr.el), 'mousedown', (e) -> e.stopPropagation()
- $.on $('#autohide', qr.el), 'change', qr.toggleHide
- $.on $('.close', qr.el), 'click', qr.close
- $.on $('#dump', qr.el), 'click', -> qr.el.classList.toggle 'dump'
- $.on $('#addReply', qr.el), 'click', -> new qr.reply().select()
- $.on $('form', qr.el), 'submit', qr.submit
- $.on $('textarea', qr.el), 'keyup', -> qr.selected.el.lastChild.textContent = @value
- $.on fileInput, 'change', qr.fileInput
- $.on fileInput, 'click', (e) -> if e.shiftKey then qr.selected.rmFile() or e.preventDefault()
- $.on spoiler.firstChild, 'change', -> $('input', qr.selected.el).click()
- $.on $('.warning', qr.el), 'click', qr.cleanError
+ $.on $('select', QR.el), 'mousedown', (e) -> e.stopPropagation()
+ $.on $('#autohide', QR.el), 'change', QR.toggleHide
+ $.on $('.close', QR.el), 'click', QR.close
+ $.on $('#dump', QR.el), 'click', -> QR.el.classList.toggle 'dump'
+ $.on $('#addReply', QR.el), 'click', -> new QR.reply().select()
+ $.on $('form', QR.el), 'submit', QR.submit
+ $.on $('textarea', QR.el), 'keyup', -> QR.selected.el.lastChild.textContent = @value
+ $.on fileInput, 'change', QR.fileInput
+ $.on fileInput, 'click', (e) -> if e.shiftKey then QR.selected.rmFile() or e.preventDefault()
+ $.on spoiler.firstChild, 'change', -> $('input', QR.selected.el).click()
+ $.on $('.warning', QR.el), 'click', QR.cleanError
- new qr.reply().select()
+ new QR.reply().select()
# save selected reply's data
for name in ['name', 'email', 'sub', 'com']
- input = $ "[name=#{name}]", qr.el
+ input = $ "[name=#{name}]", QR.el
for event in ['input', 'keyup', 'change', 'paste']
# The input event replaces keyup, change and paste events.
# Firefox 12 will support the input event.
# Oprah?
$.on input, event, ->
- qr.selected[@name] = @value
+ QR.selected[@name] = @value
# Disable auto-posting if you're typing in the first reply
# during the last 5 seconds of the cooldown.
- if qr.cooldown.auto and qr.selected is qr.replies[0] and parseInt(qr.status.input.value.match /\d+/) < 6
- qr.cooldown.auto = false
+ if QR.cooldown.auto and QR.selected is QR.replies[0] and parseInt(QR.status.input.value.match /\d+/) < 6
+ QR.cooldown.auto = false
# sync between tabs
- $.sync 'qr.persona', (persona) ->
- return unless qr.el.hidden
+ $.sync 'QR.persona', (persona) ->
+ return unless QR.el.hidden
for key, val of persona
- qr.selected[key] = val
- $("[name=#{key}]", qr.el).value = val
+ QR.selected[key] = val
+ $("[name=#{key}]", QR.el).value = val
- qr.status.input = $ '[type=submit]', qr.el
- qr.status()
- qr.cooldown.init()
- qr.captcha.init()
- $.add d.body, qr.el
+ QR.status.input = $ '[type=submit]', QR.el
+ QR.status()
+ QR.cooldown.init()
+ QR.captcha.init()
+ $.add d.body, QR.el
# Create a custom event when the QR dialog is first initialized.
# Use it to extend the QR's functionalities, or for XTRM RICE.
e = d.createEvent 'CustomEvent'
e.initEvent 'QRDialogCreation', true, false
- qr.el.dispatchEvent e
+ QR.el.dispatchEvent e
submit: (e) ->
e?.preventDefault()
- if qr.cooldown.seconds
- qr.cooldown.auto = !qr.cooldown.auto
- qr.status()
+ if QR.cooldown.seconds
+ QR.cooldown.auto = !QR.cooldown.auto
+ QR.status()
return
- qr.message.send req: 'abort'
- reply = qr.replies[0]
+ QR.message.send req: 'abort'
+ reply = QR.replies[0]
# prevent errors
unless reply.com or reply.file
@@ -1538,28 +1523,28 @@ qr =
challenge = captcha.challenge
response = captcha.response
else
- challenge = qr.captcha.img.alt
- if response = qr.captcha.input.value then qr.captcha.reload()
+ challenge = QR.captcha.img.alt
+ if response = QR.captcha.input.value then QR.captcha.reload()
$.set 'captchas', captchas
- qr.captcha.count captchas.length
+ QR.captcha.count captchas.length
unless response
err = 'No valid captcha.'
if err
# stop auto-posting
- qr.cooldown.auto = false
- qr.status()
- qr.error err
+ QR.cooldown.auto = false
+ QR.status()
+ QR.error err
return
- qr.cleanError()
+ QR.cleanError()
- threadID = g.THREAD_ID or $('select', qr.el).value
+ threadID = g.THREAD_ID or $('select', QR.el).value
# Enable auto-posting if we have stuff to post, disable it otherwise.
- qr.cooldown.auto = qr.replies.length > 1
- if conf['Auto Hide QR'] and not qr.cooldown.auto
- qr.hide()
- if conf['Thread Watcher'] and conf['Auto Watch Reply'] and threadID isnt 'new'
+ QR.cooldown.auto = QR.replies.length > 1
+ if Conf['Auto Hide QR'] and not QR.cooldown.auto
+ QR.hide()
+ if Conf['Thread Watcher'] and Conf['Auto Watch Reply'] and threadID isnt 'new'
Watcher.watch threadID
post =
@@ -1578,9 +1563,9 @@ qr =
# Starting to upload might take some time.
# Provide some feedback that we're starting to submit.
- qr.status progress: '...'
+ QR.status progress: '...'
- if engine is 'gecko' and reply.file
+ if $.engine is 'gecko' and reply.file
# https://bugzilla.mozilla.org/show_bug.cgi?id=673742
# We plan to allow postMessaging Files and FileLists across origins,
# that just needs a more in depth security review.
@@ -1591,21 +1576,21 @@ qr =
file.name = reply.file.name
file.type = reply.file.type
post.upfile = file
- qr.message.send post
+ QR.message.send post
reader.readAsBinaryString reply.file
return
# CORS is ignored for content script on Chrome, but not Safari/Oprah/Firefox.
if /chrome/i.test navigator.userAgent
- qr.message.post post
+ QR.message.post post
return
- qr.message.send post
+ QR.message.send post
response: (html) ->
doc = $.el 'a', innerHTML: html
# Check for ban.
if $('title', doc).textContent is '4chan - Banned'
- qr.status ready: true, banned: true
+ QR.status ready: true, banned: true
return
unless b = $ 'td b', doc
err = 'Connection error with sys.4chan.org.'
@@ -1618,58 +1603,58 @@ qr =
if err
if /captcha|verification/i.test(err) or err is 'Connection error with sys.4chan.org.'
# Enable auto-post if we have some cached captchas.
- qr.cooldown.auto = !!$.get('captchas', []).length
+ QR.cooldown.auto = !!$.get('captchas', []).length
# Too many frequent mistyped captchas will auto-ban you!
# On connection error, the post most likely didn't go through.
- qr.cooldown.set 2
+ QR.cooldown.set 2
else # stop auto-posting
- qr.cooldown.auto = false
- qr.status()
- qr.error err, node
+ QR.cooldown.auto = false
+ QR.status()
+ QR.error err, node
return
- reply = qr.replies[0]
+ reply = QR.replies[0]
- persona = $.get 'qr.persona', {}
+ persona = $.get 'QR.persona', {}
persona =
name: reply.name
email: if /^sage$/.test reply.email then persona.email else reply.email
- sub: if conf['Remember Subject'] then reply.sub else null
- $.set 'qr.persona', persona
+ sub: if Conf['Remember Subject'] then reply.sub else null
+ $.set 'QR.persona', persona
[_, thread, postNumber] = b.lastChild.textContent.match /thread:(\d+),no:(\d+)/
if thread is '0' # new thread
- if conf['Thread Watcher'] and conf['Auto Watch']
+ if Conf['Thread Watcher'] and Conf['Auto Watch']
$.set 'autoWatch', postNumber
# auto-noko
location.pathname = "/#{g.BOARD}/res/#{postNumber}"
else
# Enable auto-posting if we have stuff to post, disable it otherwise.
- qr.cooldown.auto = qr.replies.length > 1
- qr.cooldown.set if /sage/i.test reply.email then 60 else 30
- if conf['Open Reply in New Tab'] && !g.REPLY && !qr.cooldown.auto
+ QR.cooldown.auto = QR.replies.length > 1
+ QR.cooldown.set if /sage/i.test reply.email then 60 else 30
+ if Conf['Open Reply in New Tab'] && !g.REPLY && !QR.cooldown.auto
$.open "//boards.4chan.org/#{g.BOARD}/res/#{thread}##{postNumber}"
- if conf['Persistent QR'] or qr.cooldown.auto
+ if Conf['Persistent QR'] or QR.cooldown.auto
reply.rm()
else
- qr.close()
+ QR.close()
- if g.REPLY and (conf['Unread Count'] or conf['Unread Favicon'])
+ if g.REPLY and (Conf['Unread Count'] or Conf['Unread Favicon'])
Unread.foresee.push postNumber
- if g.REPLY and conf['Thread Updater'] and conf['Auto Update This']
+ if g.REPLY and Conf['Thread Updater'] and Conf['Auto Update This']
Updater.update()
- qr.status()
- qr.resetFileInput()
+ QR.status()
+ QR.resetFileInput()
message:
send: (data) ->
# CORS is ignored for content script on Chrome, but not Safari/Oprah/Firefox.
if /chrome/i.test navigator.userAgent
- qr.message.receive data
+ QR.message.receive data
return
- data.qr = true
+ data.QR = true
host = location.hostname
window =
if host is 'boards.4chan.org'
@@ -1680,17 +1665,17 @@ qr =
receive: (data) ->
req = data.req
delete data.req
- delete data.qr
+ delete data.QR
switch req
when 'abort'
- qr.ajax?.abort()
- qr.message.send req: 'status'
+ QR.ajax?.abort()
+ QR.message.send req: 'status'
when 'response' # xhr response
- qr.response data.html
+ QR.response data.html
when 'status'
- qr.status data
+ QR.status data
else
- qr.message.post data # Reply object: we're posting
+ QR.message.post data # Reply object: we're posting
post: (data) ->
@@ -1699,7 +1684,7 @@ qr =
delete data.postURL
# File with filename upload fix from desuwa
- if engine is 'gecko' and data.upfile
+ if $.engine is 'gecko' and data.upfile
# All of this is fucking retarded.
unless data.binary
toBin = (data, name, val) ->
@@ -1709,7 +1694,7 @@ qr =
r.onload = ->
data[name] = r.result
unless --i
- qr.message.post data
+ QR.message.post data
r.readAsBinaryString bb.getBlob 'text/plain'
i = Object.keys(data).length
for name, val of data
@@ -1744,29 +1729,29 @@ qr =
callbacks =
onload: ->
- qr.message.send
+ QR.message.send
req: 'response'
html: @response
onerror: ->
# CORS disabled error: redirecting to banned page ;_;
- qr.message.send req: 'status', ready: true, banned: true
+ QR.message.send req: 'status', ready: true, banned: true
opts =
form: form
type: 'post'
upCallbacks:
onload: ->
- qr.message.send
+ QR.message.send
req: 'status'
progress: '...'
onprogress: (e) ->
- qr.message.send
+ QR.message.send
req: 'status'
progress: "#{Math.round e.loaded / e.total * 100}%"
if boundary
opts.headers =
'Content-Type': 'multipart/form-data;boundary=' + boundary
- qr.ajax = $.ajax url, callbacks, opts
+ QR.ajax = $.ajax url, callbacks, opts
Options =
init: ->
@@ -1787,7 +1772,7 @@ Options =
innerHTML: '
@@ -1890,11 +1875,11 @@ Options =
'
#main
- for key, obj of config.main
+ for key, obj of Config.main
ul = $.el 'ul',
textContent: key
for key, arr of obj
- checked = if conf[key] then 'checked' else ''
+ checked = if Conf[key] then 'checked' else ''
description = arr[1]
li = $.el 'li',
innerHTML: "
: #{description}"
@@ -1911,14 +1896,14 @@ Options =
#filter & sauce
for ta in $$ 'textarea', dialog
- ta.textContent = conf[ta.name]
+ ta.textContent = Conf[ta.name]
$.on ta, 'change', $.cb.value
#rice
- (back = $ '[name=backlink]', dialog).value = conf['backlink']
- (time = $ '[name=time]', dialog).value = conf['time']
- (fileInfoR = $ '[name=fileInfoR]', dialog).value = conf['fileInfoR']
- (fileInfoT = $ '[name=fileInfoT]', dialog).value = conf['fileInfoT']
+ (back = $ '[name=backlink]', dialog).value = Conf['backlink']
+ (time = $ '[name=time]', dialog).value = Conf['time']
+ (fileInfoR = $ '[name=fileInfoR]', dialog).value = Conf['fileInfoR']
+ (fileInfoT = $ '[name=fileInfoT]', dialog).value = Conf['fileInfoT']
$.on back, 'keyup', $.cb.value
$.on back, 'keyup', Options.backlink
$.on time, 'keyup', $.cb.value
@@ -1928,16 +1913,16 @@ Options =
$.on fileInfoT, 'keyup', $.cb.value
$.on fileInfoT, 'keyup', Options.fileInfo
favicon = $ 'select', dialog
- favicon.value = conf['favicon']
+ favicon.value = Conf['favicon']
$.on favicon, 'change', $.cb.value
$.on favicon, 'change', Options.favicon
#keybinds
- for key, arr of config.hotkeys
+ for key, arr of Config.hotkeys
tr = $.el 'tr',
innerHTML: "
#{arr[1]} | | "
input = $ 'input', tr
- input.value = conf[key]
+ input.value = Conf[key]
$.on input, 'keydown', Options.keybind
$.add $('#keybinds_tab + div tbody', dialog), tr
@@ -1945,7 +1930,7 @@ Options =
indicators = {}
for indicator in $$ '.warning', dialog
key = indicator.firstChild.textContent
- indicator.hidden = conf[key]
+ indicator.hidden = Conf[key]
indicators[key] = indicator
$.on $("[name='#{key}']", dialog), 'click', ->
indicators[@name].hidden = @checked
@@ -1986,7 +1971,7 @@ Options =
Time.date = new Date()
$.id('timePreview').textContent = Time.funk Time
backlink: ->
- $.id('backlinkPreview').textContent = conf['backlink'].replace /%id/, '123456789'
+ $.id('backlinkPreview').textContent = Conf['backlink'].replace /%id/, '123456789'
fileInfo: ->
type = if @name is 'fileInfoR' then 0 else 1
FileInfo.data =
@@ -2067,7 +2052,7 @@ ThreadHiding =
$.set "hiddenThreads/#{g.BOARD}/", hiddenThreads
hide: (thread) ->
- unless conf['Show Stubs']
+ unless Conf['Show Stubs']
thread.hidden = true
thread.nextSibling.hidden = true
return
@@ -2102,20 +2087,20 @@ ThreadHiding =
Updater =
init: ->
- html = "
-#{conf['Interval']}
"
- {checkbox} = config.updater
+ html = "
-#{Conf['Interval']}
"
+ {checkbox} = Config.updater
for name of checkbox
title = checkbox[name][1]
- checked = if conf[name] then 'checked' else ''
+ checked = if Conf[name] then 'checked' else ''
html += "
"
- checked = if conf['Auto Update'] then 'checked' else ''
+ checked = if Conf['Auto Update'] then 'checked' else ''
html += "
-
+
"
- dialog = ui.dialog 'updater', 'bottom: 0; right: 0;', html
+ dialog = UI.dialog 'updater', 'bottom: 0; right: 0;', html
@count = $ '#count', dialog
@timer = $ '#timer', dialog
@@ -2134,9 +2119,9 @@ Updater =
$.on input, 'click', @cb.autoUpdate
@cb.autoUpdate.call input
# Required for the QR's update after posting.
- conf[input.name] = input.checked
+ Conf[input.name] = input.checked
else if input.name is 'Interval'
- $.on input, 'change', -> conf['Interval'] = @value = parseInt(@value, 10) or conf['Interval']
+ $.on input, 'change', -> Conf['Interval'] = @value = parseInt(@value, 10) or Conf['Interval']
$.on input, 'change', $.cb.value
else if input.type is 'button'
$.on input, 'click', @update
@@ -2148,7 +2133,7 @@ Updater =
cb:
verbose: ->
- if conf['Verbose']
+ if Conf['Verbose']
Updater.count.textContent = '+0'
Updater.timer.hidden = false
else
@@ -2174,17 +2159,17 @@ Updater =
Updater.count.className = 'warning'
clearTimeout Updater.timeoutID
g.dead = true
- if conf['Unread Count']
+ if Conf['Unread Count']
Unread.title = Unread.title.match(/^.+-/)[0] + ' 404'
else
d.title = d.title.match(/^.+-/)[0] + ' 404'
Unread.update true
- qr.message.send req: 'abort'
- qr.status()
+ QR.message.send req: 'abort'
+ QR.status()
return
Updater.retryCoef = 10
- Updater.timer.textContent = '-' + conf['Interval']
+ Updater.timer.textContent = '-' + Conf['Interval']
###
Status Code 304: Not modified
@@ -2193,7 +2178,7 @@ Updater =
and won't load images and scripts when parsing the response.
###
if @status is 304
- if conf['Verbose']
+ if Conf['Verbose']
Updater.count.textContent = '+0'
Updater.count.className = null
return
@@ -2209,9 +2194,9 @@ Updater =
nodes.push reply.parentNode.parentNode.parentNode #table
newPosts = nodes.length
- scroll = conf['Scrolling'] && Updater.scrollBG() && newPosts &&
+ 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.className = if newPosts then 'new' else null
@@ -2247,7 +2232,7 @@ Updater =
Watcher =
init: ->
html = '
Thread Watcher
'
- @dialog = ui.dialog 'watcher', 'top: 50px; left: 0px;', html
+ @dialog = UI.dialog 'watcher', 'top: 50px; left: 0px;', html
$.add d.body, @dialog
#add watch buttons
@@ -2329,7 +2314,7 @@ Watcher =
Anonymize =
init: ->
- g.callbacks.push @node
+ Main.callbacks.push @node
node: (post) ->
return if post.class is 'inline'
name = $ '.commentpostername, .postername', post.el
@@ -2342,11 +2327,11 @@ Sauce =
init: ->
return if g.BOARD is 'f'
@links = []
- for link in conf['sauces'].split '\n'
+ for link in Conf['sauces'].split '\n'
continue if link[0] is '#'
@links.push @createSauceLink link
return unless @links.length
- g.callbacks.push @node
+ Main.callbacks.push @node
createSauceLink: (link) ->
domain = link.match(/(\w+)\.\w+\//)[1]
@@ -2380,7 +2365,7 @@ Sauce =
RevealSpoilers =
init: ->
- g.callbacks.push @node
+ Main.callbacks.push @node
node: (post) ->
{img} = post
if not (img and /^Spoil/.test img.alt) or post.class is 'inline'
@@ -2400,7 +2385,7 @@ Time =
@parse =
if Date.parse('10/11/11(Tue)18:53') is 1318351980000
- (node) -> new Date Date.parse(node.textContent) + chanOffset*HOUR
+ (node) -> new Date Date.parse(node.textContent) + chanOffset*$.HOUR
else # Firefox and Opera do not parse 4chan's time format correctly
(node) ->
[_, month, day, year, hour, min] =
@@ -2410,7 +2395,7 @@ Time =
hour = chanOffset + Number hour
new Date year, month, day, hour, min
- g.callbacks.push @node
+ Main.callbacks.push @node
node: (post) ->
return if post.class is 'inline'
# .posttime exists on every board except /f/
@@ -2422,7 +2407,7 @@ Time =
time.setAttribute 'datetime', Time.date.toISOString()
$.replace node, time
foo: ->
- code = conf['time'].replace /%([A-Za-z])/g, (s, c) ->
+ code = Conf['time'].replace /%([A-Za-z])/g, (s, c) ->
if c of Time.formatters
"' + Time.formatters.#{c}() + '"
else
@@ -2473,7 +2458,7 @@ FileInfo =
init: ->
return if g.BOARD is 'f'
@setFormats()
- g.callbacks.push @node
+ Main.callbacks.push @node
node: (post) ->
return if post.class is 'inline' or not node = post.filesize
regexp = /^File: (<.+>)-\((?:Spoiler Image, )?([\d\.]+) (\w+), (\d+x\d+|PDF)/
@@ -2493,7 +2478,7 @@ FileInfo =
setFormats: ->
funks = []
for i in [0..1]
- format = if i then conf['fileInfoT'] else conf['fileInfoR']
+ format = if i then Conf['fileInfoT'] else Conf['fileInfoR']
param = if i then /%([BKlMrs])/g else /%([BKlLMnNrs])/g
code = format.replace param, (s, c) ->
if c of FileInfo.formatters
@@ -2550,9 +2535,9 @@ TitlePost =
QuoteBacklink =
init: ->
- format = conf['backlink'].replace /%id/g, "' + id + '"
+ format = Conf['backlink'].replace /%id/g, "' + id + '"
@funk = Function 'id', "return '#{format}'"
- g.callbacks.push @node
+ Main.callbacks.push @node
node: (post) ->
return if post.isInlined
quotes = {}
@@ -2567,11 +2552,11 @@ QuoteBacklink =
textContent: QuoteBacklink.funk post.id
for qid of quotes
# Don't backlink the OP.
- continue if !(el = $.id qid) or el.className is 'op' and !conf['OP Backlinks']
+ continue if !(el = $.id qid) or el.className is 'op' and !Conf['OP Backlinks']
link = a.cloneNode true
- if conf['Quote Preview']
+ if Conf['Quote Preview']
$.on link, 'mouseover', QuotePreview.mouseover
- if conf['Quote Inline']
+ if Conf['Quote Inline']
$.on link, 'click', QuoteInline.toggle
else
link.setAttribute 'onclick', "replyhl('#{post.id}');"
@@ -2586,7 +2571,7 @@ QuoteBacklink =
QuoteInline =
init: ->
- g.callbacks.push @node
+ Main.callbacks.push @node
node: (post) ->
for quote in post.quotes
continue unless quote.hash
@@ -2615,7 +2600,7 @@ QuoteInline =
Unread.update()
if /\bbacklink\b/.test q.className
$.after q.parentNode, inline
- if conf['Forward Hiding']
+ if Conf['Forward Hiding']
table = $.x 'ancestor::table', el
$.addClass table, 'forwarded'
# Will only unhide if there's no inlined backlinks of it anymore.
@@ -2636,7 +2621,7 @@ QuoteInline =
#select the corresponding table or loading td
table = $.x "following::*[@id='i#{id}']", q
$.rm table
- return unless conf['Forward Hiding']
+ return unless Conf['Forward Hiding']
for inlined in $$ '.backlink.inlined', table
table = $.x 'ancestor::table', $.id inlined.hash[1..]
$.removeClass table, 'forwarded' unless --table.title
@@ -2678,7 +2663,7 @@ QuoteInline =
QuotePreview =
init: ->
- g.callbacks.push @node
+ Main.callbacks.push @node
node: (post) ->
for quote in post.quotes
$.on quote, 'mouseover', QuotePreview.mouseover if quote.hash
@@ -2687,7 +2672,7 @@ QuotePreview =
return
mouseover: (e) ->
return if /\binlined\b/.test @className
- qp = ui.el = $.el 'div',
+ qp = UI.el = $.el 'div',
id: 'qp'
className: 'reply dialog'
$.add d.body, qp
@@ -2695,7 +2680,7 @@ QuotePreview =
id = @hash[1..]
if el = $.id id
qp.innerHTML = el.innerHTML
- $.addClass el, 'qphl' if conf['Quote Highlighting']
+ $.addClass el, 'qphl' if Conf['Quote Highlighting']
if /\bbacklink\b/.test @className
replyID = $.x('preceding-sibling::input', @parentNode).name
for quote in $$ '.quotelink', qp
@@ -2705,19 +2690,19 @@ QuotePreview =
qp.textContent = "Loading #{id}..."
threadID = @pathname.split('/').pop() or $.x('ancestor::div[@class="thread"]', @).firstChild.id
$.cache @pathname, (-> QuotePreview.parse @, id, threadID)
- ui.hover e
- $.on @, 'mousemove', ui.hover
+ UI.hover e
+ $.on @, 'mousemove', UI.hover
$.on @, 'mouseout', QuotePreview.mouseout
$.on @, 'click', QuotePreview.mouseout
mouseout: ->
if el = $.id @hash[1..]
$.removeClass el, 'qphl'
- ui.hoverend()
- $.off @, 'mousemove', ui.hover
+ UI.hoverend()
+ $.off @, 'mousemove', UI.hover
$.off @, 'mouseout', QuotePreview.mouseout
$.off @, 'click', QuotePreview.mouseout
parse: (req, id, threadID) ->
- return unless (qp = ui.el) and qp.textContent is "Loading #{id}..."
+ return unless (qp = UI.el) and qp.textContent is "Loading #{id}..."
if req.status isnt 200
qp.textContent = "#{req.status} #{req.statusText}"
@@ -2736,16 +2721,16 @@ QuotePreview =
root: qp
filesize: $ '.filesize', qp
img: $ 'img[md5]', qp
- if conf['Image Auto-Gif']
+ if Conf['Image Auto-Gif']
AutoGif.node post
- if conf['Time Formatting']
+ if Conf['Time Formatting']
Time.node post
- if conf['File Info Formatting']
+ if Conf['File Info Formatting']
FileInfo.node post
QuoteOP =
init: ->
- g.callbacks.push @node
+ Main.callbacks.push @node
node: (post) ->
return if post.class is 'inline'
for quote in post.quotes
@@ -2756,7 +2741,7 @@ QuoteOP =
QuoteCT =
init: ->
- g.callbacks.push @node
+ Main.callbacks.push @node
node: (post) ->
return if post.class is 'inline'
for quote in post.quotes
@@ -2772,7 +2757,7 @@ QuoteCT =
Quotify =
init: ->
- g.callbacks.push @node
+ Main.callbacks.push @node
node: (post) ->
return if post.class is 'inline'
@@ -2832,7 +2817,7 @@ ReportButton =
className: 'reportbutton'
innerHTML: '[ ! ]'
href: 'javascript:;'
- g.callbacks.push @node
+ Main.callbacks.push @node
node: (post) ->
unless a = $ '.reportbutton', post.el
a = ReportButton.a.cloneNode true
@@ -2846,7 +2831,7 @@ ReportButton =
ThreadStats =
init: ->
- dialog = ui.dialog 'stats', 'bottom: 0; left: 0;', '
0 / 0
'
+ dialog = UI.dialog 'stats', 'bottom: 0; left: 0;', '
0 / 0
'
dialog.className = 'dialog'
$.add d.body, dialog
@posts = @images = 0
@@ -2858,7 +2843,7 @@ ThreadStats =
501
else
151
- g.callbacks.push @node
+ Main.callbacks.push @node
node: (post) ->
return if post.isInlined
$.id('postcount').textContent = ++ThreadStats.posts
@@ -2873,7 +2858,7 @@ Unread =
@title = d.title
@update()
$.on window, 'scroll', Unread.scroll
- g.callbacks.push @node
+ Main.callbacks.push @node
replies: []
foresee: []
@@ -2912,10 +2897,10 @@ Unread =
count = @replies.length
- if conf['Unread Count']
+ if Conf['Unread Count']
@setTitle count
- unless conf['Unread Favicon'] and (count < 2 or forceUpdate)
+ unless Conf['Unread Favicon'] and (count < 2 or forceUpdate)
return
Favicon.el.href =
@@ -2945,7 +2930,7 @@ Favicon =
@switch()
switch: ->
- switch conf['favicon']
+ switch Conf['favicon']
when 'ferongr'
@unreadDead = 'data:unreadDead;base64,R0lGODlhEAAQAOMHAOgLAnMFAL8AAOgLAukMA/+AgP+rq////////////////////////////////////yH5BAEKAAcALAAAAAAQABAAAARZ8MhJ6xwDWIBv+AM1fEEIBIVRlNKYrtpIECuGzuwpCLg974EYiXUYkUItjGbC6VQ4omXFiKROA6qSy0A8nAo9GS3YCswIWnOvLAi0be23Z1QtdSUaqXcviQAAOw=='
@unreadSFW = 'data:unreadSFW;base64,R0lGODlhEAAQAOMHAADX8QBwfgC2zADX8QDY8nnl8qLp8v///////////////////////////////////yH5BAEKAAcALAAAAAAQABAAAARZ8MhJ6xwDWIBv+AM1fEEIBIVRlNKYrtpIECuGzuwpCLg974EYiXUYkUItjGbC6VQ4omXFiKROA6qSy0A8nAo9GS3YCswIWnOvLAi0be23Z1QtdSUaqXcviQAAOw=='
@@ -2978,12 +2963,12 @@ Redirect =
image: (href) ->
href = href.split '/'
# Do not use g.BOARD, the image url can originate from a cross-quote.
- return unless conf['404 Redirect']
+ return unless Conf['404 Redirect']
switch href[3]
when 'a', 'jp', 'm', 'tg', 'u', 'vg'
"http://archive.foolz.us/#{href[3]}/full_image/#{href[5]}"
thread: (board=g.BOARD, id=g.THREAD_ID, mode='thread') ->
- return unless conf['404 Redirect'] or mode is 'post'
+ return unless Conf['404 Redirect'] or mode is 'post'
switch g.BOARD
when 'a', 'jp', 'm', 'tg', 'tv', 'u', 'v', 'vg'
"http://archive.foolz.us/#{board}/thread/#{id}/"
@@ -3001,33 +2986,33 @@ Redirect =
ImageHover =
init: ->
- g.callbacks.push @node
+ Main.callbacks.push @node
node: (post) ->
return unless post.img
$.on post.img, 'mouseover', ImageHover.mouseover
mouseover: ->
- ui.el = $.el 'img'
+ UI.el = $.el 'img'
id: 'ihover'
src: @parentNode.href
- $.add d.body, ui.el
- $.on ui.el, 'load', ImageHover.load
- $.on @, 'mousemove', ui.hover
+ $.add d.body, UI.el
+ $.on UI.el, 'load', ImageHover.load
+ $.on @, 'mousemove', UI.hover
$.on @, 'mouseout', ImageHover.mouseout
load: ->
- return if @ isnt ui.el
+ return if @ isnt UI.el
# 'Fake' mousemove event by giving required values.
{style} = @
- ui.hover
+ UI.hover
clientX: - 45 + parseInt style.left
clientY: 120 + parseInt style.top
mouseout: ->
- ui.hoverend()
- $.off @, 'mousemove', ui.hover
+ UI.hoverend()
+ $.off @, 'mousemove', UI.hover
$.off @, 'mouseout', ImageHover.mouseout
AutoGif =
init: ->
- g.callbacks.push @node
+ Main.callbacks.push @node
node: (post) ->
return if post.root.hidden or not post.img
src = post.img.parentNode.href
@@ -3040,7 +3025,7 @@ AutoGif =
ImageExpand =
init: ->
- g.callbacks.push @node
+ Main.callbacks.push @node
@dialog()
node: (post) ->
@@ -3058,7 +3043,7 @@ ImageExpand =
ImageExpand.on = @checked
if ImageExpand.on #expand
thumbs = $$ 'img[md5]'
- if conf['Expand From Current']
+ if Conf['Expand From Current']
for thumb, i in thumbs
if thumb.getBoundingClientRect().top > 0
break
@@ -3130,7 +3115,7 @@ ImageExpand =
timeoutID = setTimeout ImageExpand.expand, 10000, thumb, url
# Only Chrome let userscript break through cross domain requests.
# Don't check it 404s in the archivers.
- return unless engine is 'webkit' and url.split('/')[2] is 'images.4chan.org'
+ return unless $.engine is 'webkit' and url.split('/')[2] is 'images.4chan.org'
$.ajax url, onreadystatechange: (-> clearTimeout timeoutID if @status is 404),
type: 'head'
@@ -3155,25 +3140,25 @@ ImageExpand =
Main =
init: ->
+ Main.flatten null, Config
+
path = location.pathname
pathname = path[1..].split '/'
[g.BOARD, temp] = pathname
if temp is 'res'
g.REPLY = true
g.THREAD_ID = pathname[2]
- else
- g.PAGENUM = parseInt(temp) or 0
#load values from localStorage
- for key, val of conf
- conf[key] = $.get key, val
+ for key, val of Conf
+ Conf[key] = $.get key, val
$.on window, 'message', Main.message
switch location.hostname
when 'sys.4chan.org'
if path is '/robots.txt'
- qr.message.send req: 'status', ready: true
+ QR.message.send req: 'status', ready: true
else if /report/.test location.search
$.ready ->
$.on $.id('recaptcha_response_field'), 'keydown', (e) ->
@@ -3181,7 +3166,7 @@ Main =
return
when 'www.4chan.org'
if path is '/banned'
- qr.message.send req: 'status', ready: true, banned: true
+ QR.message.send req: 'status', ready: true, banned: true
return
when 'images.4chan.org'
$.ready -> Redirect.init() if d.title is '4chan - 404'
@@ -3189,21 +3174,21 @@ Main =
$.ready Options.init
- if conf['Quick Reply'] and conf['Hide Original Post Form'] and g.BOARD isnt 'f'
+ if Conf['Quick Reply'] and Conf['Hide Original Post Form'] and g.BOARD isnt 'f'
Main.css += 'form[name=post] { display: none; }'
Main.addStyle()
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'
$.set 'lastUpdate', now
g.hiddenReplies = $.get "hiddenReplies/#{g.BOARD}/", {}
- if $.get('lastChecked', 0) < now - 1*DAY
+ if $.get('lastChecked', 0) < now - 1*$.DAY
$.set 'lastChecked', now
- cutoff = now - 7*DAY
+ cutoff = now - 7*$.DAY
hiddenThreads = $.get "hiddenThreads/#{g.BOARD}/", {}
for id, timestamp of hiddenThreads
@@ -3219,55 +3204,55 @@ Main =
#major features
- if conf['Filter']
+ if Conf['Filter']
Filter.init()
- if conf['Reply Hiding']
+ if Conf['Reply Hiding']
ReplyHiding.init()
- if conf['Filter'] or conf['Reply Hiding']
+ if Conf['Filter'] or Conf['Reply Hiding']
StrikethroughQuotes.init()
- if conf['Anonymize']
+ if Conf['Anonymize']
Anonymize.init()
- if conf['Time Formatting']
+ if Conf['Time Formatting']
Time.init()
- if conf['File Info Formatting']
+ if Conf['File Info Formatting']
FileInfo.init()
- if conf['Sauce']
+ if Conf['Sauce']
Sauce.init()
- if conf['Reveal Spoilers']
+ if Conf['Reveal Spoilers']
RevealSpoilers.init()
- if conf['Image Auto-Gif']
+ if Conf['Image Auto-Gif']
AutoGif.init()
- if conf['Image Hover']
+ if Conf['Image Hover']
ImageHover.init()
- if conf['Report Button']
+ if Conf['Report Button']
ReportButton.init()
- if conf['Resurrect Quotes']
+ if Conf['Resurrect Quotes']
Quotify.init()
- if conf['Quote Inline']
+ if Conf['Quote Inline']
QuoteInline.init()
- if conf['Quote Preview']
+ if Conf['Quote Preview']
QuotePreview.init()
- if conf['Quote Backlinks']
+ if Conf['Quote Backlinks']
QuoteBacklink.init()
- if conf['Indicate OP quote']
+ if Conf['Indicate OP quote']
QuoteOP.init()
- if conf['Indicate Cross-thread Quotes']
+ if Conf['Indicate Cross-thread Quotes']
QuoteCT.init()
$.ready Main.ready
@@ -3278,8 +3263,8 @@ Main =
return
unless $.id 'navtopr'
return
- $.addClass d.body, "chanx_#{VERSION.split('.')[1]}"
- $.addClass d.body, engine
+ $.addClass d.body, "chanx_#{Main.version.split('.')[1]}"
+ $.addClass d.body, $.engine
for nav in ['navtop', 'navbot']
$.addClass $("a[href$='/#{g.BOARD}/']", $.id nav), 'current'
form = $ 'form[name=delform]'
@@ -3287,45 +3272,45 @@ Main =
Favicon.init()
# Major features.
- if conf['Quick Reply']
- qr.init()
+ if Conf['Quick Reply']
+ QR.init()
- if conf['Image Expansion']
+ if Conf['Image Expansion']
ImageExpand.init()
- if conf['Thread Watcher']
+ if Conf['Thread Watcher']
setTimeout -> Watcher.init()
- if conf['Keybinds']
+ if Conf['Keybinds']
setTimeout -> Keybinds.init()
if g.REPLY
- if conf['Thread Updater']
+ if Conf['Thread Updater']
setTimeout -> Updater.init()
- if conf['Thread Stats']
+ if Conf['Thread Stats']
ThreadStats.init()
- if conf['Reply Navigation']
+ if Conf['Reply Navigation']
setTimeout -> Nav.init()
- if conf['Post in Title']
+ if Conf['Post in Title']
TitlePost.init()
- if conf['Unread Count'] or conf['Unread Favicon']
+ if Conf['Unread Count'] or Conf['Unread Favicon']
Unread.init()
else #not reply
- if conf['Thread Hiding']
+ if Conf['Thread Hiding']
setTimeout -> ThreadHiding.init()
- if conf['Thread Expansion']
+ if Conf['Thread Expansion']
setTimeout -> ExpandThread.init()
- if conf['Comment Expansion']
+ if Conf['Comment Expansion']
setTimeout -> ExpandComment.init()
- if conf['Index Navigation']
+ if Conf['Index Navigation']
setTimeout -> Nav.init()
nodes = []
@@ -3341,6 +3326,16 @@ Main =
else
$.on form, 'DOMNodeInserted', Main.listener
+ flatten: (parent, obj) ->
+ if obj instanceof Array
+ Conf[parent] = obj[0]
+ else if typeof obj is 'object'
+ for key, val of obj
+ Main.flatten key, val
+ else # string or number
+ Conf[parent] = obj
+ return
+
addStyle: ->
$.off d, 'DOMNodeInserted', Main.addStyle
if d.head
@@ -3350,11 +3345,11 @@ Main =
message: (e) ->
{data} = e
- if data.qr
- qr.message.receive data
+ if data.QR
+ QR.message.receive data
return
{version} = data
- if version and version isnt VERSION and confirm 'An updated version of 4chan X is available, would you like to install it now?'
+ if version and version isnt Main.version and confirm 'An updated version of 4chan X is available, would you like to install it now?'
window.location = "https://raw.github.com/mayhemydg/4chan-x/#{version}/4chan_x.user.js"
preParse: (node) ->
@@ -3372,11 +3367,11 @@ Main =
post.img = if post.filesize then node.getElementsByTagName('img')[0] else false
post
node: (nodes, notify) ->
- for callback in g.callbacks
+ for callback in Main.callbacks
try
callback node for node in nodes
catch err
- alert "4chan X (#{VERSION}) error: #{err.message}\nhttp://mayhemydg.github.com/4chan-x/#bug-report\n\n#{err.stack}" if notify
+ alert "4chan X (#{Main.version}) error: #{err.message}\nhttp://mayhemydg.github.com/4chan-x/#bug-report\n\n#{err.stack}" if notify
return
observer: (mutations) ->
nodes = []
@@ -3388,6 +3383,9 @@ Main =
{target} = e
Main.node [Main.preParse target] if target.nodeName is 'TABLE'
+ namespace: '4chan_x.'
+ version: '2.29.1'
+ callbacks: []
css: '
/* dialog styling */
.dialog {