diff --git a/4chan_x.user.js b/4chan_x.user.js index c131c3306..de1d6b562 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -73,7 +73,7 @@ */ (function() { - var $, $$, Anonymize, AutoGif, Conf, Config, DAY, ExpandComment, ExpandThread, Favicon, FileInfo, Filter, GetTitle, HOUR, ImageExpand, ImageHover, Keybinds, MINUTE, Main, NAMESPACE, Nav, Options, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, Quotify, Redirect, ReplyHiding, ReportButton, RevealSpoilers, SECOND, Sauce, StrikethroughQuotes, ThreadHiding, ThreadStats, Threading, Time, TitlePost, UI, Unread, Updater, VERSION, Watcher, d, flatten, _base; + var $, $$, Anonymize, AutoGif, Conf, Config, DAY, ExpandComment, ExpandThread, Favicon, FileInfo, Filter, GetTitle, HOUR, ImageExpand, ImageHover, Keybinds, MINUTE, Main, NAMESPACE, Nav, Options, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, Quotify, Redirect, ReplyHiding, ReportButton, RevealSpoilers, SECOND, Sauce, StrikethroughQuotes, ThreadHiding, ThreadStats, Threading, Time, TitlePost, UI, Unread, Updater, VERSION, Watcher, flatten, _base; Config = { main: { @@ -208,24 +208,10 @@ } })(null, Config); - NAMESPACE = '4chan_x.'; - - VERSION = '2.29.1'; - - SECOND = 1000; - - MINUTE = 60 * SECOND; - - HOUR = 60 * MINUTE; - - DAY = 24 * HOUR; - - d = document; - UI = { dialog: function(id, position, html) { var el, saved; - el = d.createElement('div'); + el = $.d.createElement('div'); el.className = 'reply dialog'; el.innerHTML = html; el.id = id; @@ -237,13 +223,13 @@ var el, rect; e.preventDefault(); UI.el = el = this.parentNode; - d.addEventListener('mousemove', UI.drag, false); - d.addEventListener('mouseup', UI.dragend, false); + $.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.width = $.d.body.clientWidth - el.offsetWidth; + return UI.height = $.d.body.clientHeight - el.offsetHeight; }, drag: function(e) { var bottom, left, right, style, top; @@ -263,15 +249,15 @@ 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); + $.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; style = el.style; - _ref = d.body, clientHeight = _ref.clientHeight, clientWidth = _ref.clientWidth; + _ref = $.d.body, clientHeight = _ref.clientHeight, clientWidth = _ref.clientWidth; height = el.offsetHeight; top = clientY - 120; style.top = clientHeight <= height || top <= 0 ? 0 : top + height >= clientHeight ? clientHeight - height : top; @@ -296,7 +282,7 @@ */ $ = function(selector, root) { - if (root == null) root = d.body; + if (root == null) root = $.d.body; return root.querySelector(selector); }; @@ -308,17 +294,30 @@ } }; + NAMESPACE = '4chan_x.'; + + VERSION = '2.29.1'; + + SECOND = 1000; + + MINUTE = 60 * SECOND; + + HOUR = 60 * MINUTE; + + DAY = 24 * HOUR; + $.extend($, { log: typeof (_base = console.log).bind === "function" ? _base.bind(console) : void 0, + d: document, engine: /WebKit|Presto|Gecko/.exec(navigator.userAgent)[0].toLowerCase(), ready: function(fc) { var cb; - if (/interactive|complete/.test(d.readyState)) return setTimeout(fc); + if (/interactive|complete/.test($.d.readyState)) return setTimeout(fc); cb = function() { - $.off(d, 'DOMContentLoaded', cb); + $.off($.d, 'DOMContentLoaded', cb); return fc(); }; - return $.on(d, 'DOMContentLoaded', cb); + return $.on($.d, 'DOMContentLoaded', cb); }, sync: function(key, cb) { return $.on(window, 'storage', function(e) { @@ -326,7 +325,7 @@ }); }, id: function(id) { - return d.getElementById(id); + return $.d.getElementById(id); }, ajax: function(url, callbacks, opts) { var form, headers, key, r, type, upCallbacks, val; @@ -390,12 +389,12 @@ style = $.el('style', { textContent: css }); - $.add(d.head, style); + $.add($.d.head, style); return style; }, x: function(path, root) { - if (root == null) root = d.body; - return d.evaluate(path, root, null, 8, null).singleNodeValue; + if (root == null) root = $.d.body; + return $.d.evaluate(path, root, null, 8, null).singleNodeValue; }, addClass: function(el, className) { return el.classList.add(className); @@ -407,12 +406,12 @@ return el.parentNode.removeChild(el); }, tn: function(s) { - return d.createTextNode(s); + return $.d.createTextNode(s); }, nodes: function(nodes) { var frag, node, _i, _len; if (nodes instanceof Node) return nodes; - frag = d.createDocumentFragment(); + frag = $.d.createDocumentFragment(); for (_i = 0, _len = nodes.length; _i < _len; _i++) { node = nodes[_i]; frag.appendChild(node); @@ -436,7 +435,7 @@ }, el: function(tag, properties) { var el; - el = d.createElement(tag); + el = $.d.createElement(tag); if (properties) $.extend(el, properties); return el; }, @@ -530,7 +529,7 @@ }); $$ = function(selector, root) { - if (root == null) root = d.body; + if (root == null) root = $.d.body; return Array.prototype.slice.call(root.querySelectorAll(selector)); }; @@ -658,7 +657,7 @@ comment: function(post) { var data, i, nodes, text, _ref; text = []; - nodes = d.evaluate('.//br|.//text()', post.el.lastChild, null, 7, null); + nodes = $.d.evaluate('.//br|.//text()', post.el.lastChild, null, 7, null); for (i = 0, _ref = nodes.snapshotLength; 0 <= _ref ? i < _ref : i > _ref; 0 <= _ref ? i++ : i--) { text.push((data = nodes.snapshotItem(i).data) ? data : '\n'); } @@ -736,10 +735,10 @@ a.textContent = "" + req.status + " " + req.statusText; return; } - doc = d.implementation.createHTMLDocument(null); + doc = $.d.implementation.createHTMLDocument(null); doc.documentElement.innerHTML = req.responseText; Threading.op($('body > form', doc).firstChild); - node = d.importNode(doc.getElementById(replyID)); + node = $.d.importNode(doc.getElementById(replyID)); quotes = node.getElementsByClassName('quotelink'); for (_i = 0, _len = quotes.length; _i < _len; _i++) { quote = quotes[_i]; @@ -839,13 +838,13 @@ return; } a.textContent = a.textContent.replace('\u00d7 Loading...', '-'); - doc = d.implementation.createHTMLDocument(null); + doc = $.d.implementation.createHTMLDocument(null); doc.documentElement.innerHTML = req.responseText; nodes = []; _ref = $$('.reply', doc); for (_i = 0, _len = _ref.length; _i < _len; _i++) { reply = _ref[_i]; - table = d.importNode(reply.parentNode.parentNode.parentNode); + table = $.d.importNode(reply.parentNode.parentNode.parentNode); _ref2 = $$('.quotelink', table); for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) { quote = _ref2[_j]; @@ -935,7 +934,7 @@ node = _ref[_i]; node.removeAttribute('accesskey'); } - return $.on(d, 'keydown', Keybinds.keydown); + return $.on($.d, 'keydown', Keybinds.keydown); }, keydown: function(e) { var key, o, range, selEnd, selStart, ta, thread, value, _ref, _ref2; @@ -1131,7 +1130,7 @@ td.className = 'reply'; td.removeAttribute('tabindex'); rect = td.getBoundingClientRect(); - if (rect.bottom >= 0 && rect.top <= d.body.clientHeight) { + if (rect.bottom >= 0 && rect.top <= $.d.body.clientHeight) { next = delta === +1 ? $.x('following::td[@class="reply"]', td) : $.x('preceding::td[@class="reply"]', td); if (!next) { td.className = 'replyhl'; @@ -1143,7 +1142,7 @@ return; } rect = next.getBoundingClientRect(); - if (rect.top < 0 || rect.bottom > d.body.clientHeight) { + if (rect.top < 0 || rect.bottom > $.d.body.clientHeight) { next.scrollIntoView(delta === -1); } next.className = 'replyhl'; @@ -1157,7 +1156,7 @@ for (_i = 0, _len = replies.length; _i < _len; _i++) { reply = replies[_i]; rect = reply.getBoundingClientRect(); - if (delta === +1 && rect.top >= 0 || delta === -1 && rect.bottom <= d.body.clientHeight) { + if (delta === +1 && rect.top >= 0 || delta === -1 && rect.bottom <= $.d.body.clientHeight) { reply.className = 'replyhl'; reply.tabIndex = 0; reply.focus(); @@ -1184,7 +1183,7 @@ $.on(prev, 'click', this.prev); $.on(next, 'click', this.next); $.add(span, [prev, $.tn(' '), next]); - return $.add(d.body, span); + return $.add($.d.body, span); }, prev: function() { if (Main.REPLY) { @@ -1195,7 +1194,7 @@ }, next: function() { if (Main.REPLY) { - return window.scrollTo(0, d.body.scrollHeight); + return window.scrollTo(0, $.d.body.scrollHeight); } else { return Nav.scroll(+1); } @@ -1244,7 +1243,7 @@ if (!Main.REPLY) $('select', QR.el).value = 'new'; return $('textarea', QR.el).focus(); }); - form = d.forms[0]; + form = $.d.forms[0]; $.before(form, link); } if (/chrome/i.test(navigator.userAgent)) { @@ -1272,21 +1271,21 @@ return setTimeout(loadChecking, 500, this); } }); - $.add(d.head, iframe); + $.add($.d.head, iframe); } script = $.el('script', { textContent: 'Recaptcha.focus_response_field=function(){}' }); - $.add(d.head, script); + $.add($.d.head, script); $.rm(script); 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); @@ -1305,7 +1304,7 @@ QR.message.send({ req: 'abort' }); - d.activeElement.blur(); + $.d.activeElement.blur(); $.removeClass(QR.el, 'dump'); _ref = QR.replies; for (_i = 0, _len = _ref.length; _i < _len; _i++) { @@ -1321,7 +1320,7 @@ return QR.cleanError(); }, hide: function() { - d.activeElement.blur(); + $.d.activeElement.blur(); $.addClass(QR.el, 'autohide'); return $.id('autohide').checked = true; }, @@ -1339,7 +1338,7 @@ if (node) $.replace(el.firstChild, node); QR.open(); if (/captcha|verification/i.test(err)) $('[autocomplete]', QR.el).focus(); - if (d.hidden || d.oHidden || d.mozHidden || d.webkitHidden) { + if ($.d.hidden || $.d.oHidden || $.d.mozHidden || $.d.webkitHidden) { return alert(err); } }, @@ -1423,8 +1422,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(); @@ -1830,8 +1829,8 @@ QR.status(); QR.cooldown.init(); QR.captcha.init(); - $.add(d.body, QR.el); - e = d.createEvent('CustomEvent'); + $.add($.d.body, QR.el); + e = $.d.createEvent('CustomEvent'); e.initEvent('QRDialogCreation', true, false); return QR.el.dispatchEvent(e); }, @@ -1888,7 +1887,7 @@ upfile: reply.file, spoiler: reply.spoiler, mode: 'regist', - pwd: (m = d.cookie.match(/4chan_pass=([^;]+)/)) ? decodeURIComponent(m[1]) : $('[name=pwd]').value, + pwd: (m = $.d.cookie.match(/4chan_pass=([^;]+)/)) ? decodeURIComponent(m[1]) : $('[name=pwd]').value, recaptcha_challenge_field: challenge, recaptcha_response_field: response + ' ' }; @@ -2318,8 +2317,8 @@ return e.stopPropagation(); }); $.add(overlay, dialog); - $.add(d.body, overlay); - d.body.style.setProperty('overflow', 'hidden', null); + $.add($.d.body, overlay); + $.d.body.style.setProperty('overflow', 'hidden', null); Options.backlink.call(back); Options.time.call(time); Options.fileInfo.call(fileInfoR); @@ -2328,7 +2327,7 @@ }, close: function() { $.rm(this); - return d.body.style.removeProperty('overflow'); + return $.d.body.style.removeProperty('overflow'); }, clearHidden: function() { $["delete"]("hiddenReplies/" + Main.BOARD + "/"); @@ -2524,7 +2523,7 @@ $.on(input, 'click', this.update); } } - $.add(d.body, dialog); + $.add($.d.body, dialog); this.retryCoef = 10; return this.lastModified = 0; }, @@ -2552,7 +2551,7 @@ return Updater.scrollBG = this.checked ? function() { return true; } : function() { - return !(d.hidden || d.oHidden || d.mozHidden || d.webkitHidden); + return !($.d.hidden || $.d.oHidden || $.d.mozHidden || $.d.webkitHidden); }; }, update: function() { @@ -2566,7 +2565,7 @@ if (Conf['Unread Count']) { Unread.title = Unread.title.match(/^.+-/)[0] + ' 404'; } else { - d.title = d.title.match(/^.+-/)[0] + ' 404'; + $.d.title = $.d.title.match(/^.+-/)[0] + ' 404'; } Unread.update(true); QR.message.send({ @@ -2591,7 +2590,7 @@ return; } Updater.lastModified = this.getResponseHeader('Last-Modified'); - doc = d.implementation.createHTMLDocument(null); + doc = $.d.implementation.createHTMLDocument(null); doc.documentElement.innerHTML = this.responseText; id = $('input', Updater.br.previousElementSibling).name; nodes = []; @@ -2602,7 +2601,7 @@ nodes.push(reply.parentNode.parentNode.parentNode); } newPosts = nodes.length; - scroll = Conf['Scrolling'] && Updater.scrollBG() && newPosts && Updater.br.previousElementSibling.getBoundingClientRect().bottom - d.body.clientHeight < 25; + scroll = Conf['Scrolling'] && Updater.scrollBG() && newPosts && Updater.br.previousElementSibling.getBoundingClientRect().bottom - $.d.body.clientHeight < 25; if (Conf['Verbose']) { Updater.count.textContent = "+" + newPosts; Updater.count.className = newPosts ? 'new' : null; @@ -2649,7 +2648,7 @@ var favicon, html, input, inputs, _i, _len; html = '
Thread Watcher
'; this.dialog = UI.dialog('watcher', 'top: 50px; left: 0px;', html); - $.add(d.body, this.dialog); + $.add($.d.body, this.dialog); inputs = $$('.op > input'); for (_i = 0, _len = inputs.length; _i < _len; _i++) { input = inputs[_i]; @@ -3051,7 +3050,7 @@ TitlePost = { init: function() { - return d.title = GetTitle(); + return $.d.title = GetTitle(); } }; @@ -3191,7 +3190,7 @@ inline.textContent = "" + req.status + " " + req.statusText; return; } - doc = d.implementation.createHTMLDocument(null); + doc = $.d.implementation.createHTMLDocument(null); doc.documentElement.innerHTML = req.responseText; node = id === threadID ? Threading.op($('body > form', doc).firstChild) : doc.getElementById(id); newInline = QuoteInline.table(id, node.innerHTML); @@ -3243,7 +3242,7 @@ id: 'qp', className: 'reply dialog' }); - $.add(d.body, qp); + $.add($.d.body, qp); id = this.hash.slice(1); if (el = $.id(id)) { qp.innerHTML = el.innerHTML; @@ -3283,7 +3282,7 @@ qp.textContent = "" + req.status + " " + req.statusText; return; } - doc = d.implementation.createHTMLDocument(null); + doc = $.d.implementation.createHTMLDocument(null); doc.documentElement.innerHTML = req.responseText; node = id === threadID ? Threading.op($('body > form', doc).firstChild) : doc.getElementById(id); qp.innerHTML = node.innerHTML; @@ -3341,7 +3340,7 @@ node: function(post) { var a, board, data, i, id, index, m, node, nodes, quote, quotes, snapshot, text, _i, _len, _ref; if (post["class"] === 'inline') return; - snapshot = d.evaluate('.//text()[not(parent::a)]', post.el.lastChild, null, 6, null); + snapshot = $.d.evaluate('.//text()[not(parent::a)]', post.el.lastChild, null, 6, null); for (i = 0, _ref = snapshot.snapshotLength; 0 <= _ref ? i < _ref : i > _ref; 0 <= _ref ? i++ : i--) { node = snapshot.snapshotItem(i); data = node.data; @@ -3404,7 +3403,7 @@ var dialog; dialog = UI.dialog('stats', 'bottom: 0; left: 0;', '
0 / 0
'); dialog.className = 'dialog'; - $.add(d.body, dialog); + $.add($.d.body, dialog); this.posts = this.images = 0; this.imgLimit = (function() { switch (Main.BOARD) { @@ -3435,7 +3434,7 @@ Unread = { init: function() { - this.title = d.title; + this.title = $.d.title; this.update(); $.on(window, 'scroll', Unread.scroll); return Main.callbacks.push(this.node); @@ -3454,7 +3453,7 @@ }, scroll: function() { var bottom, height, i, reply, _len, _ref; - height = d.body.clientHeight; + height = $.d.body.clientHeight; _ref = Unread.replies; for (i = 0, _len = _ref.length; i < _len; i++) { reply = _ref[i]; @@ -3473,7 +3472,7 @@ return; } return this.scheduled = setTimeout((function() { - return d.title = "(" + count + ") " + Unread.title; + return $.d.title = "(" + count + ") " + Unread.title; }), 5); }, update: function(forceUpdate) { @@ -3483,14 +3482,14 @@ if (Conf['Unread Count']) this.setTitle(count); if (!(Conf['Unread Favicon'] && (count < 2 || forceUpdate))) return; Favicon.el.href = Main.dead ? count ? Favicon.unreadDead : Favicon.dead : count ? Favicon.unread : Favicon["default"]; - return $.add(d.head, Favicon.el); + return $.add($.d.head, Favicon.el); } }; Favicon = { init: function() { var href; - this.el = $('link[rel="shortcut icon"]', d.head); + this.el = $('link[rel="shortcut icon"]', $.d.head); this.el.type = 'image/x-icon'; href = this.el.href; this.SFW = /ws.ico$/.test(href); @@ -3611,7 +3610,7 @@ id: 'ihover', src: this.parentNode.href }); - $.add(d.body, UI.el); + $.add($.d.body, UI.el); $.on(UI.el, 'load', ImageHover.load); $.on(this, 'mousemove', UI.hover); return $.on(this, 'mouseout', ImageHover.mouseout); @@ -3726,8 +3725,8 @@ thumb = a.firstChild; if (thumb.hidden) { rect = a.getBoundingClientRect(); - if (rect.top < 0) d.body.scrollTop += rect.top - 42; - if (rect.left < 0) d.body.scrollLeft += rect.left; + if (rect.top < 0) $.d.body.scrollTop += rect.top - 42; + if (rect.left < 0) $.d.body.scrollLeft += rect.left; return ImageExpand.contract(thumb); } else { return ImageExpand.expand(thumb); @@ -3791,7 +3790,7 @@ return $.prepend(form, controls); }, resize: function() { - return ImageExpand.style.textContent = ".fitheight img[md5] + img {max-height:" + d.body.clientHeight + "px;}"; + return ImageExpand.style.textContent = ".fitheight img[md5] + img {max-height:" + $.d.body.clientHeight + "px;}"; } }; @@ -3840,7 +3839,7 @@ return; case 'images.4chan.org': $.ready(function() { - if (d.title === '4chan - 404') return Redirect.init(); + if ($.d.title === '4chan - 404') return Redirect.init(); }); return; } @@ -3852,7 +3851,7 @@ now = Date.now(); if (Conf['Check for Updates'] && $.get('lastUpdate', 0) < now - 6 * HOUR) { $.ready(function() { - return $.add(d.head, $.el('script', { + return $.add($.d.head, $.el('script', { src: 'https://raw.github.com/mayhemydg/4chan-x/master/latest.js' })); }); @@ -3896,13 +3895,13 @@ }, ready: function() { var MutationObserver, form, nav, node, nodes, observer, _i, _j, _len, _len2, _ref, _ref2; - if (d.title === '4chan - 404') { + if ($.d.title === '4chan - 404') { Redirect.init(); return; } if (!$.id('navtopr')) return; - $.addClass(d.body, "chanx_" + (VERSION.split('.')[1])); - $.addClass(d.body, $.engine); + $.addClass($.d.body, "chanx_" + (VERSION.split('.')[1])); + $.addClass($.d.body, $.engine); _ref = ['navtop', 'navbot']; for (_i = 0, _len = _ref.length; _i < _len; _i++) { nav = _ref[_i]; @@ -3977,11 +3976,11 @@ } }, addStyle: function() { - $.off(d, 'DOMNodeInserted', Main.addStyle); - if (d.head) { + $.off($.d, 'DOMNodeInserted', Main.addStyle); + if ($.d.head) { return $.addStyle(Main.css); } else { - return $.on(d, 'DOMNodeInserted', Main.addStyle); + return $.on($.d, 'DOMNodeInserted', Main.addStyle); } }, message: function(e) { diff --git a/script.coffee b/script.coffee index 8ec689fb5..55fe92d5c 100644 --- a/script.coffee +++ b/script.coffee @@ -167,17 +167,9 @@ Conf = {} return ) null, Config -NAMESPACE = '4chan_x.' -VERSION = '2.29.1' -SECOND = 1000 -MINUTE = 60*SECOND -HOUR = 60*MINUTE -DAY = 24*HOUR -d = document - UI = dialog: (id, position, html) -> - el = d.createElement 'div' + el = $.d.createElement 'div' el.className = 'reply dialog' el.innerHTML = html el.id = id @@ -188,16 +180,16 @@ UI = #prevent text selection e.preventDefault() UI.el = el = @parentNode - d.addEventListener 'mousemove', UI.drag, false - d.addEventListener 'mouseup', UI.dragend, false + $.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 #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 @@ -224,13 +216,13 @@ UI = #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 + $.d.removeEventListener 'mousemove', UI.drag, false + $.d.removeEventListener 'mouseup', UI.dragend, false hover: (e) -> {clientX, clientY} = e {el} = UI {style} = el - {clientHeight, clientWidth} = d.body + {clientHeight, clientWidth} = $.d.body height = el.offsetHeight top = clientY - 120 @@ -258,7 +250,7 @@ loosely follows the jquery api: http://api.jquery.com/ not chainable ### -$ = (selector, root=d.body) -> +$ = (selector, root=$.d.body) -> root.querySelector selector $.extend = (object, properties) -> @@ -266,24 +258,33 @@ $.extend = (object, properties) -> object[key] = val return +NAMESPACE = '4chan_x.' +VERSION = '2.29.1' +SECOND = 1000 +MINUTE = 60*SECOND +HOUR = 60*MINUTE +DAY = 24*HOUR + $.extend $, - # XXX GreaseMonkey can't into console.log.bind - log: console.log.bind? console + log: + # XXX GreaseMonkey can't into console.log.bind + console.log.bind? console + d: document engine: /WebKit|Presto|Gecko/.exec(navigator.userAgent)[0].toLowerCase() ready: (fc) -> - if /interactive|complete/.test d.readyState + if /interactive|complete/.test $.d.readyState # Execute the functions in parallel. # If one fails, do not stop the others. return setTimeout fc cb = -> - $.off d, 'DOMContentLoaded', cb + $.off $.d, 'DOMContentLoaded', cb fc() - $.on d, 'DOMContentLoaded', cb + $.on $.d, 'DOMContentLoaded', cb sync: (key, cb) -> $.on window, 'storage', (e) -> cb JSON.parse e.newValue if e.key is "#{NAMESPACE}#{key}" id: (id) -> - d.getElementById id + $.d.getElementById id ajax: (url, callbacks, opts={}) -> {type, headers, upCallbacks, form} = opts r = new XMLHttpRequest() @@ -316,11 +317,11 @@ $.extend $, addStyle: (css) -> style = $.el 'style', textContent: css - $.add d.head, style + $.add $.d.head, style style - x: (path, root=d.body) -> + x: (path, root=$.d.body) -> # XPathResult.ANY_UNORDERED_NODE_TYPE is 8 - d.evaluate(path, root, null, 8, null). + $.d.evaluate(path, root, null, 8, null). singleNodeValue addClass: (el, className) -> el.classList.add className @@ -329,11 +330,11 @@ $.extend $, rm: (el) -> el.parentNode.removeChild el tn: (s) -> - d.createTextNode s + $.d.createTextNode s nodes: (nodes) -> if nodes instanceof Node return nodes - frag = d.createDocumentFragment() + frag = $.d.createDocumentFragment() for node in nodes frag.appendChild node frag @@ -348,7 +349,7 @@ $.extend $, replace: (root, el) -> root.parentNode.replaceChild $.nodes(el), root el: (tag, properties) -> - el = d.createElement tag + el = $.d.createElement tag $.extend el, properties if properties el on: (el, eventType, handler) -> @@ -447,7 +448,7 @@ $.extend $, name = NAMESPACE + name localStorage[name] = JSON.stringify value -$$ = (selector, root=d.body) -> +$$ = (selector, root=$.d.body) -> Array::slice.call root.querySelectorAll selector Filter = @@ -582,7 +583,7 @@ Filter = comment: (post) -> text = [] # XPathResult.ORDERED_NODE_SNAPSHOT_TYPE is 7 - nodes = d.evaluate './/br|.//text()', post.el.lastChild, null, 7, null + nodes = $.d.evaluate './/br|.//text()', post.el.lastChild, null, 7, null for i in [0...nodes.snapshotLength] text.push if data = nodes.snapshotItem(i).data then data else '\n' text.join '' @@ -635,13 +636,13 @@ ExpandComment = a.textContent = "#{req.status} #{req.statusText}" return - doc = d.implementation.createHTMLDocument null + doc = $.d.implementation.createHTMLDocument null doc.documentElement.innerHTML = req.responseText Threading.op $('body > form', doc).firstChild # Import the node to fix quote.hashes # as they're empty when in a different document. - node = d.importNode doc.getElementById replyID + node = $.d.importNode doc.getElementById replyID quotes = node.getElementsByClassName 'quotelink' for quote in quotes @@ -718,12 +719,12 @@ ExpandThread = a.textContent = a.textContent.replace '\u00d7 Loading...', '-' - doc = d.implementation.createHTMLDocument null + doc = $.d.implementation.createHTMLDocument null doc.documentElement.innerHTML = req.responseText nodes = [] for reply in $$ '.reply', doc - table = d.importNode reply.parentNode.parentNode.parentNode + table = $.d.importNode reply.parentNode.parentNode.parentNode for quote in $$ '.quotelink', table if (href = quote.getAttribute 'href') is quote.hash #add pathname to normal quotes quote.pathname = pathname @@ -794,7 +795,7 @@ Keybinds = init: -> for node in $$ '[accesskey]' node.removeAttribute 'accesskey' - $.on d, 'keydown', Keybinds.keydown + $.on $.d, 'keydown', Keybinds.keydown keydown: (e) -> if not (key = Keybinds.keyCode(e)) or /TEXTAREA|INPUT/.test(e.target.nodeName) and not (e.altKey or e.ctrlKey or e.keyCode is 27) @@ -927,7 +928,7 @@ Keybinds = td.className = 'reply' td.removeAttribute 'tabindex' rect = td.getBoundingClientRect() - if rect.bottom >= 0 and rect.top <= d.body.clientHeight # We're at least partially visible + if rect.bottom >= 0 and rect.top <= $.d.body.clientHeight # We're at least partially visible next = if delta is +1 $.x 'following::td[@class="reply"]', td @@ -940,7 +941,7 @@ Keybinds = return return unless Main.REPLY or $.x('ancestor::div[@class="thread"]', next) is thread rect = next.getBoundingClientRect() - if rect.top < 0 or rect.bottom > d.body.clientHeight + if rect.top < 0 or rect.bottom > $.d.body.clientHeight next.scrollIntoView delta is -1 next.className = 'replyhl' next.tabIndex = 0 @@ -951,7 +952,7 @@ Keybinds = replies.reverse() if delta is -1 for reply in replies rect = reply.getBoundingClientRect() - if delta is +1 and rect.top >= 0 or delta is -1 and rect.bottom <= d.body.clientHeight + if delta is +1 and rect.top >= 0 or delta is -1 and rect.bottom <= $.d.body.clientHeight reply.className = 'replyhl' reply.tabIndex = 0 reply.focus() @@ -973,7 +974,7 @@ Nav = $.on next, 'click', @next $.add span, [prev, $.tn(' '), next] - $.add d.body, span + $.add $.d.body, span prev: -> if Main.REPLY @@ -983,7 +984,7 @@ Nav = next: -> if Main.REPLY - window.scrollTo 0, d.body.scrollHeight + window.scrollTo 0, $.d.body.scrollHeight else Nav.scroll +1 @@ -1024,7 +1025,7 @@ QR = QR.open() $('select', QR.el).value = 'new' unless Main.REPLY $('textarea', QR.el).focus() - form = d.forms[0] + form = $.d.forms[0] $.before form, link # CORS is ignored for content script on Chrome, but not Safari/Oprah/Firefox. @@ -1041,20 +1042,20 @@ QR = 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, @ - $.add d.head, iframe + $.add $.d.head, iframe # Prevent original captcha input from being focused on reload. script = $.el 'script', textContent: 'Recaptcha.focus_response_field=function(){}' - $.add d.head, script + $.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 + $.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 @@ -1068,7 +1069,7 @@ QR = close: -> QR.el.hidden = true QR.message.send req: 'abort' - d.activeElement.blur() + $.d.activeElement.blur() $.removeClass QR.el, 'dump' for i in QR.replies QR.replies[0].rm() @@ -1079,7 +1080,7 @@ QR = spoiler.click() QR.cleanError() hide: -> - d.activeElement.blur() + $.d.activeElement.blur() $.addClass QR.el, 'autohide' $.id('autohide').checked = true unhide: -> @@ -1096,7 +1097,7 @@ QR = if /captcha|verification/i.test err # Focus the captcha input on captcha error. $('[autocomplete]', QR.el).focus() - alert err if d.hidden or d.oHidden or d.mozHidden or d.webkitHidden + alert err if $.d.hidden or $.d.oHidden or $.d.mozHidden or $.d.webkitHidden cleanError: -> $('.warning', QR.el).textContent = null @@ -1179,8 +1180,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 @@ -1505,11 +1506,11 @@ QR = QR.status() QR.cooldown.init() QR.captcha.init() - $.add d.body, QR.el + $.add $.d.body, QR.el # Create a custom event when the QR dialog is first initialized. # Use it to extend the QR's functionalities, or for XTRM RICE. - e = d.createEvent 'CustomEvent' + e = $.d.createEvent 'CustomEvent' e.initEvent 'QRDialogCreation', true, false QR.el.dispatchEvent e @@ -1569,7 +1570,7 @@ QR = upfile: reply.file spoiler: reply.spoiler mode: 'regist' - pwd: if m = d.cookie.match(/4chan_pass=([^;]+)/) then decodeURIComponent m[1] else $('[name=pwd]').value + pwd: if m = $.d.cookie.match(/4chan_pass=([^;]+)/) then decodeURIComponent m[1] else $('[name=pwd]').value recaptcha_challenge_field: challenge recaptcha_response_field: response + ' ' @@ -1953,8 +1954,8 @@ Options = $.on overlay, 'click', Options.close $.on dialog, 'click', (e) -> e.stopPropagation() $.add overlay, dialog - $.add d.body, overlay - d.body.style.setProperty 'overflow', 'hidden', null + $.add $.d.body, overlay + $.d.body.style.setProperty 'overflow', 'hidden', null Options.backlink.call back Options.time.call time @@ -1964,7 +1965,7 @@ Options = close: -> $.rm this - d.body.style.removeProperty 'overflow' + $.d.body.style.removeProperty 'overflow' clearHidden: -> #'hidden' might be misleading; it's the number of IDs we're *looking* for, @@ -2140,7 +2141,7 @@ Updater = else if input.type is 'button' $.on input, 'click', @update - $.add d.body, dialog + $.add $.d.body, dialog @retryCoef = 10 @lastModified = 0 @@ -2165,7 +2166,7 @@ Updater = if @checked -> true else - -> !(d.hidden or d.oHidden or d.mozHidden or d.webkitHidden) + -> !($.d.hidden or $.d.oHidden or $.d.mozHidden or $.d.webkitHidden) update: -> if @status is 404 Updater.timer.textContent = '' @@ -2176,7 +2177,7 @@ Updater = if Conf['Unread Count'] Unread.title = Unread.title.match(/^.+-/)[0] + ' 404' else - d.title = d.title.match(/^.+-/)[0] + ' 404' + $.d.title = $.d.title.match(/^.+-/)[0] + ' 404' Unread.update true QR.message.send req: 'abort' QR.status() @@ -2198,7 +2199,7 @@ Updater = return Updater.lastModified = @getResponseHeader 'Last-Modified' - doc = d.implementation.createHTMLDocument null + doc = $.d.implementation.createHTMLDocument null doc.documentElement.innerHTML = @responseText id = $('input', Updater.br.previousElementSibling).name @@ -2209,7 +2210,7 @@ Updater = newPosts = nodes.length scroll = Conf['Scrolling'] && Updater.scrollBG() && newPosts && - Updater.br.previousElementSibling.getBoundingClientRect().bottom - d.body.clientHeight < 25 + Updater.br.previousElementSibling.getBoundingClientRect().bottom - $.d.body.clientHeight < 25 if Conf['Verbose'] Updater.count.textContent = "+#{newPosts}" Updater.count.className = if newPosts then 'new' else null @@ -2247,7 +2248,7 @@ Watcher = init: -> html = '
Thread Watcher
' @dialog = UI.dialog 'watcher', 'top: 50px; left: 0px;', html - $.add d.body, @dialog + $.add $.d.body, @dialog #add watch buttons inputs = $$ '.op > input' @@ -2546,7 +2547,7 @@ GetTitle = (thread) -> TitlePost = init: -> - d.title = GetTitle() + $.d.title = GetTitle() QuoteBacklink = init: -> @@ -2651,7 +2652,7 @@ QuoteInline = inline.textContent = "#{req.status} #{req.statusText}" return - doc = d.implementation.createHTMLDocument null + doc = $.d.implementation.createHTMLDocument null doc.documentElement.innerHTML = req.responseText node = @@ -2690,7 +2691,7 @@ QuotePreview = qp = UI.el = $.el 'div', id: 'qp' className: 'reply dialog' - $.add d.body, qp + $.add $.d.body, qp id = @hash[1..] if el = $.id id @@ -2723,7 +2724,7 @@ QuotePreview = qp.textContent = "#{req.status} #{req.statusText}" return - doc = d.implementation.createHTMLDocument null + doc = $.d.implementation.createHTMLDocument null doc.documentElement.innerHTML = req.responseText node = @@ -2778,7 +2779,7 @@ Quotify = # XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE is 6 # Get all the text nodes that are not inside an anchor. - snapshot = d.evaluate './/text()[not(parent::a)]', post.el.lastChild, null, 6, null + snapshot = $.d.evaluate './/text()[not(parent::a)]', post.el.lastChild, null, 6, null for i in [0...snapshot.snapshotLength] node = snapshot.snapshotItem i @@ -2848,7 +2849,7 @@ ThreadStats = init: -> dialog = UI.dialog 'stats', 'bottom: 0; left: 0;', '
0 / 0
' dialog.className = 'dialog' - $.add d.body, dialog + $.add $.d.body, dialog @posts = @images = 0 @imgLimit = switch Main.BOARD @@ -2870,7 +2871,7 @@ ThreadStats = Unread = init: -> - @title = d.title + @title = $.d.title @update() $.on window, 'scroll', Unread.scroll Main.callbacks.push @node @@ -2887,7 +2888,7 @@ Unread = Unread.update() scroll: -> - height = d.body.clientHeight + height = $.d.body.clientHeight for reply, i in Unread.replies {bottom} = reply.getBoundingClientRect() if bottom > height #post is not completely read @@ -2904,7 +2905,7 @@ Unread = @setTitle count return @scheduled = setTimeout (-> - d.title = "(#{count}) #{Unread.title}" + $.d.title = "(#{count}) #{Unread.title}" ), 5 update: (forceUpdate) -> @@ -2933,11 +2934,11 @@ Unread = #`favicon.href = href` doesn't work on Firefox #`favicon.href = href` isn't enough on Opera #Opera won't always update the favicon if the href didn't not change - $.add d.head, Favicon.el + $.add $.d.head, Favicon.el Favicon = init: -> - @el = $ 'link[rel="shortcut icon"]', d.head + @el = $ 'link[rel="shortcut icon"]', $.d.head @el.type = 'image/x-icon' {href} = @el @SFW = /ws.ico$/.test href @@ -3009,7 +3010,7 @@ ImageHover = UI.el = $.el 'img' id: 'ihover' src: @parentNode.href - $.add d.body, UI.el + $.add $.d.body, UI.el $.on UI.el, 'load', ImageHover.load $.on @, 'mousemove', UI.hover $.on @, 'mouseout', ImageHover.mouseout @@ -3092,8 +3093,8 @@ ImageExpand = thumb = a.firstChild if thumb.hidden rect = a.getBoundingClientRect() - d.body.scrollTop += rect.top - 42 if rect.top < 0 - d.body.scrollLeft += rect.left if rect.left < 0 + $.d.body.scrollTop += rect.top - 42 if rect.top < 0 + $.d.body.scrollLeft += rect.left if rect.left < 0 ImageExpand.contract thumb else ImageExpand.expand thumb @@ -3151,7 +3152,7 @@ ImageExpand = $.prepend form, controls resize: -> - ImageExpand.style.textContent = ".fitheight img[md5] + img {max-height:#{d.body.clientHeight}px;}" + ImageExpand.style.textContent = ".fitheight img[md5] + img {max-height:#{$.d.body.clientHeight}px;}" Main = init: -> @@ -3184,7 +3185,7 @@ Main = QR.message.send req: 'status', ready: true, banned: true return when 'images.4chan.org' - $.ready -> Redirect.init() if d.title is '4chan - 404' + $.ready -> Redirect.init() if $.d.title is '4chan - 404' return $.ready Options.init @@ -3196,7 +3197,7 @@ Main = now = Date.now() if Conf['Check for Updates'] and $.get('lastUpdate', 0) < now - 6*HOUR - $.ready -> $.add d.head, $.el 'script', src: 'https://raw.github.com/mayhemydg/4chan-x/master/latest.js' + $.ready -> $.add $.d.head, $.el 'script', src: 'https://raw.github.com/mayhemydg/4chan-x/master/latest.js' $.set 'lastUpdate', now Main.hiddenReplies = $.get "hiddenReplies/#{Main.BOARD}/", {} @@ -3273,13 +3274,13 @@ Main = $.ready Main.ready ready: -> - if d.title is '4chan - 404' + if $.d.title is '4chan - 404' Redirect.init() return unless $.id 'navtopr' return - $.addClass d.body, "chanx_#{VERSION.split('.')[1]}" - $.addClass d.body, $.engine + $.addClass $.d.body, "chanx_#{VERSION.split('.')[1]}" + $.addClass $.d.body, $.engine for nav in ['navtop', 'navbot'] $.addClass $("a[href$='/#{Main.BOARD}/']", $.id nav), 'current' form = $ 'form[name=delform]' @@ -3342,11 +3343,11 @@ Main = $.on form, 'DOMNodeInserted', Main.listener addStyle: -> - $.off d, 'DOMNodeInserted', Main.addStyle - if d.head + $.off $.d, 'DOMNodeInserted', Main.addStyle + if $.d.head $.addStyle Main.css else # XXX fox - $.on d, 'DOMNodeInserted', Main.addStyle + $.on $.d, 'DOMNodeInserted', Main.addStyle message: (e) -> {data} = e