diff --git a/4chan_x.user.js b/4chan_x.user.js index 10c7c4c31..274e9b66a 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -71,8 +71,7 @@ */ (function() { - var $, $$, DAY, Favicon, HOUR, MINUTE, Main, NAMESPACE, SECOND, Time, VERSION, anonymize, conf, config, d, engine, expandComment, expandThread, filter, flatten, g, getTitle, imgExpand, imgGif, imgHover, key, keybinds, log, nav, options, qr, quoteBacklink, quoteIndicators, quoteInline, quotePreview, redirect, replyHiding, reportButton, revealSpoilers, sauce, strikethroughQuotes, threadHiding, threadStats, threading, titlePost, ui, unread, unxify, updater, val, watcher, _base, - __slice = Array.prototype.slice; + var $, $$, DAY, Favicon, HOUR, MINUTE, Main, NAMESPACE, SECOND, Time, VERSION, anonymize, conf, config, d, engine, expandComment, expandThread, filter, flatten, g, getTitle, imgExpand, imgGif, imgHover, key, keybinds, log, nav, options, qr, quoteBacklink, quoteIndicators, quoteInline, quotePreview, redirect, replyHiding, reportButton, revealSpoilers, sauce, strikethroughQuotes, threadHiding, threadStats, threading, titlePost, ui, unread, unxify, updater, val, watcher, _base; config = { main: { @@ -189,18 +188,16 @@ conf = {}; (flatten = function(parent, obj) { - var key, val, _results; + var key, val; if (obj instanceof Array) { - return conf[parent] = obj[0]; + conf[parent] = obj[0]; } else if (typeof obj === 'object') { - _results = []; for (key in obj) { val = obj[key]; - _results.push(flatten(key, val)); + flatten(key, val); } - return _results; } else { - return conf[parent] = obj; + conf[parent] = obj; } })(null, config); @@ -393,9 +390,6 @@ if (root == null) root = d.body; return d.evaluate(path, root, null, XPathResult.ANY_UNORDERED_NODE_TYPE, null).singleNodeValue; }, - tn: function(s) { - return d.createTextNode(s); - }, replace: function(root, el) { return root.parentNode.replaceChild(el, root); }, @@ -408,22 +402,30 @@ rm: function(el) { return el.parentNode.removeChild(el); }, - add: function() { - var child, children, parent, _i, _len; - parent = arguments[0], children = 2 <= arguments.length ? __slice.call(arguments, 1) : []; - for (_i = 0, _len = children.length; _i < _len; _i++) { - child = children[_i]; - parent.appendChild(child); - } + tn: function(s) { + return d.createTextNode(s); }, - prepend: function(parent, child) { - return parent.insertBefore(child, parent.firstChild); + nodes: function(nodes) { + var frag, node, _i, _len; + if (!(nodes instanceof Array)) return nodes; + frag = d.createDocumentFragment(); + for (_i = 0, _len = nodes.length; _i < _len; _i++) { + node = nodes[_i]; + frag.appendChild(node); + } + return frag; + }, + add: function(parent, children) { + return parent.appendChild($.nodes(children)); + }, + prepend: function(parent, children) { + return parent.insertBefore($.nodes(children), parent.firstChild); }, after: function(root, el) { - return root.parentNode.insertBefore(el, root.nextSibling); + return root.parentNode.insertBefore($.nodes(el), root.nextSibling); }, before: function(root, el) { - return root.parentNode.insertBefore(el, root); + return root.parentNode.insertBefore($.nodes(el), root); }, el: function(tag, properties) { var el; @@ -678,14 +680,10 @@ if (quote.getAttribute('href') === quote.hash) { quote.pathname = "/" + g.BOARD + "/res/" + threadID; } - if (quote.hash.slice(1) === threadID) quote.innerHTML += ' (OP)'; - if (conf['Quote Preview']) { - $.on(quote, 'mouseover', quotePreview.mouseover); - $.on(quote, 'mousemove', ui.hover); - $.on(quote, 'mouseout', quotePreview.mouseout); - } - if (conf['Quote Inline']) $.on(quote, 'click', quoteInline.toggle); } + quoteIndicators.node(bq); + if (conf['Quote Preview']) quotePreview.node(bq); + if (conf['Quote Inline']) quoteInline.node(bq); return $.replace(a.parentNode.parentNode, bq); } }; @@ -742,10 +740,10 @@ } })(); table = $.x("following::br[@clear]/preceding::table[" + num + "]", a); - while ((prev = table.previousSibling) && (prev.nodeName === 'TABLE')) { + while ((prev = table.previousSibling) && (prev.nodeName !== 'A')) { $.rm(prev); } - _ref2 = $$('.op a.backlink'); + _ref2 = $$('.backlink', $('.op', thread)); _results = []; for (_i = 0, _len = _ref2.length; _i < _len; _i++) { backlink = _ref2[_i]; @@ -759,7 +757,7 @@ } }, parse: function(req, pathname, thread, a) { - var body, frag, href, link, next, quote, reply, _i, _j, _len, _len2, _ref, _ref2; + var body, href, link, next, nodes, quote, reply, _i, _j, _len, _len2, _ref, _ref2; if (req.status !== 200) { a.textContent = "" + req.status + " " + req.statusText; $.off(a, 'click', expandThread.cb.toggle); @@ -769,7 +767,7 @@ body = $.el('body', { innerHTML: req.responseText }); - frag = d.createDocumentFragment(); + nodes = []; _ref = $$('.reply', body); for (_i = 0, _len = _ref.length; _i < _len; _i++) { reply = _ref[_i]; @@ -785,12 +783,12 @@ link = $('.quotejs', reply); link.href = "res/" + thread.firstChild.id + "#" + reply.id; link.nextSibling.href = "res/" + thread.firstChild.id + "#q" + reply.id; - $.add(frag, reply.parentNode.parentNode.parentNode); + nodes.push(reply.parentNode.parentNode.parentNode); } while ((next = a.nextSibling) && !next.clear) { $.rm(next); } - return $.before(next, frag); + return $.before(next, nodes); } }; @@ -1145,7 +1143,7 @@ }); $.on(prev, 'click', nav.prev); $.on(next, 'click', nav.next); - $.add(span, prev, $.tn(' '), next); + $.add(span, [prev, $.tn(' '), next]); return $.add(d.body, span); }, prev: function() { @@ -2439,7 +2437,7 @@ }; }, update: function() { - var body, frag, id, newPosts, reply, scroll, _i, _len, _ref, _ref2; + var body, id, newPosts, nodes, reply, scroll, _i, _len, _ref, _ref2; if (this.status === 404) { updater.timer.textContent = ''; updater.count.textContent = 404; @@ -2484,14 +2482,14 @@ return; } id = ((_ref = $('td[id]', updater.br.previousElementSibling)) != null ? _ref.id : void 0) || 0; - frag = d.createDocumentFragment(); + nodes = []; _ref2 = $$('.reply', body).reverse(); for (_i = 0, _len = _ref2.length; _i < _len; _i++) { reply = _ref2[_i]; if (reply.id <= id) break; - $.prepend(frag, reply.parentNode.parentNode.parentNode); + nodes.push(reply.parentNode.parentNode.parentNode); } - newPosts = frag.childNodes.length; + newPosts = nodes.length; scroll = conf['Scrolling'] && updater.scrollBG() && newPosts && updater.br.previousElementSibling.getBoundingClientRect().bottom - d.body.clientHeight < 25; if (conf['Verbose']) { updater.count.textContent = '+' + newPosts; @@ -2501,7 +2499,7 @@ updater.count.className = 'new'; } } - $.before(updater.br, frag); + $.before(updater.br, nodes.reverse()); if (scroll) return updater.br.previousSibling.scrollIntoView(); } }, @@ -2562,9 +2560,9 @@ return $.sync('watched', watcher.refresh); }, refresh: function(watched) { - var board, div, favicon, frag, id, link, props, watchedBoard, x, _i, _j, _len, _len2, _ref, _ref2, _ref3, _results; + var board, div, favicon, id, link, nodes, props, watchedBoard, x, _i, _j, _len, _len2, _ref, _ref2, _ref3; watched || (watched = $.get('watched', {})); - frag = d.createDocumentFragment(); + nodes = []; for (board in watched) { _ref = watched[board]; for (id in _ref) { @@ -2577,8 +2575,8 @@ link = $.el('a', props); link.title = link.textContent; div = $.el('div'); - $.add(div, x, $.tn(' '), link); - $.add(frag, div); + $.add(div, [x, $.tn(' '), link]); + nodes.push(div); } } _ref2 = $$('div:not(.move)', watcher.dialog); @@ -2586,20 +2584,18 @@ div = _ref2[_i]; $.rm(div); } - $.add(watcher.dialog, frag); + $.add(watcher.dialog, nodes); watchedBoard = watched[g.BOARD] || {}; _ref3 = $$('img.favicon'); - _results = []; for (_j = 0, _len2 = _ref3.length; _j < _len2; _j++) { favicon = _ref3[_j]; id = favicon.nextSibling.name; if (id in watchedBoard) { - _results.push(favicon.src = Favicon["default"]); + favicon.src = Favicon["default"]; } else { - _results.push(favicon.src = Favicon.empty); + favicon.src = Favicon.empty; } } - return _results; }, cb: { toggle: function() { @@ -2692,14 +2688,16 @@ }; }, node: function(root) { - var img, link, span, _i, _len, _ref; + var img, link, nodes, span, _i, _len, _ref; if (root.className === 'inline' || !(span = $('.filesize', root))) return; img = span.nextElementSibling.nextElementSibling; + nodes = []; _ref = sauce.links; for (_i = 0, _len = _ref.length; _i < _len; _i++) { link = _ref[_i]; - $.add(span, $.tn(' '), link(img)); + nodes.push($.tn(' '), link(img)); } + return $.add(span, nodes); } }; @@ -2850,7 +2848,7 @@ return g.callbacks.push(this.node); }, node: function(root) { - var a, container, el, id, link, qid, quote, quotes, _i, _len, _ref, _results; + var a, container, el, id, link, qid, quote, quotes, _i, _len, _ref; if (/\binline\b/.test(root.className)) return; quotes = {}; _ref = $$('.quotelink', root); @@ -2864,7 +2862,6 @@ className: root.hidden ? 'filtered backlink' : 'backlink', textContent: quoteBacklink.funk(id) }); - _results = []; for (qid in quotes) { if (!(el = $.id(qid)) || el.className === 'op' && !conf['OP Backlinks']) { continue; @@ -2879,9 +2876,8 @@ root = $('.reportbutton', el) || $('span[id]', el); $.after(root, container); } - _results.push($.add(container, $.tn(' '), link)); + $.add(container, [$.tn(' '), link]); } - return _results; } }; @@ -2890,16 +2886,14 @@ return g.callbacks.push(this.node); }, node: function(root) { - var quote, _i, _len, _ref, _results; + var quote, _i, _len, _ref; _ref = $$('.quotelink, .backlink', root); - _results = []; for (_i = 0, _len = _ref.length; _i < _len; _i++) { quote = _ref[_i]; if (!quote.hash) continue; quote.removeAttribute('onclick'); - _results.push($.on(quote, 'click', quoteInline.toggle)); + $.on(quote, 'click', quoteInline.toggle); } - return _results; }, toggle: function(e) { var id; @@ -3106,12 +3100,12 @@ for (_i = 0, _len = _ref.length; _i < _len; _i++) { quote = _ref[_i]; if (conf['Indicate OP quote'] && quote.hash.slice(1) === tid) { - quote.innerHTML += ' (OP)'; + $.add(quote, $.tn('\u00A0(OP)')); return; } path = quote.pathname; if (conf['Indicate Cross-thread Quotes'] && path.lastIndexOf("/" + tid) === -1 && path.indexOf("/" + g.BOARD + "/") === 0) { - quote.innerHTML += ' (Cross-thread)'; + $.add(quote, $.tn('\u00A0(Cross-thread)')); } } } @@ -3130,8 +3124,7 @@ innerHTML: '[ ! ]', href: 'javascript:;' }); - $.after(span, a); - $.after(span, $.tn(' ')); + $.after(span, [$.tn(' '), a]); } return $.on(a, 'click', reportButton.report); }, @@ -3372,24 +3365,20 @@ return imgExpand.toggle(this); }, all: function() { - var thumb, _i, _j, _len, _len2, _ref, _ref2, _results, _results2; + var thumb, _i, _j, _len, _len2, _ref, _ref2; imgExpand.on = this.checked; if (imgExpand.on) { _ref = $$('img[md5]'); - _results = []; for (_i = 0, _len = _ref.length; _i < _len; _i++) { thumb = _ref[_i]; - _results.push(imgExpand.expand(thumb)); + imgExpand.expand(thumb); } - return _results; } else { _ref2 = $$('img[md5][hidden]'); - _results2 = []; for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) { thumb = _ref2[_j]; - _results2.push(imgExpand.contract(thumb)); + imgExpand.contract(thumb); } - return _results2; } }, typeChange: function() { diff --git a/script.coffee b/script.coffee index 1dfa12f0f..686fb6959 100644 --- a/script.coffee +++ b/script.coffee @@ -123,6 +123,7 @@ conf = {} flatten key, val else # string or number conf[parent] = obj + return ) null, config NAMESPACE = '4chan_x.' @@ -275,8 +276,6 @@ $.extend $, x: (path, root=d.body) -> d.evaluate(path, root, null, XPathResult.ANY_UNORDERED_NODE_TYPE, null). singleNodeValue - tn: (s) -> - d.createTextNode s replace: (root, el) -> root.parentNode.replaceChild el, root addClass: (el, className) -> @@ -285,16 +284,23 @@ $.extend $, el.classList.remove className rm: (el) -> el.parentNode.removeChild el - add: (parent, children...) -> - for child in children - parent.appendChild child - return - prepend: (parent, child) -> - parent.insertBefore child, parent.firstChild + tn: (s) -> + d.createTextNode s + nodes: (nodes) -> + unless nodes instanceof Array + return nodes + frag = d.createDocumentFragment() + for node in nodes + frag.appendChild node + frag + add: (parent, children) -> + parent.appendChild $.nodes children + prepend: (parent, children) -> + parent.insertBefore $.nodes(children), parent.firstChild after: (root, el) -> - root.parentNode.insertBefore el, root.nextSibling + root.parentNode.insertBefore $.nodes(el), root.nextSibling before: (root, el) -> - root.parentNode.insertBefore el, root + root.parentNode.insertBefore $.nodes(el), root el: (tag, properties) -> el = d.createElement tag $.extend el, properties if properties @@ -496,14 +502,11 @@ expandComment = for quote in $$ '.quotelink', bq if quote.getAttribute('href') is quote.hash quote.pathname = "/#{g.BOARD}/res/#{threadID}" - if quote.hash[1..] is threadID - quote.innerHTML += ' (OP)' - if conf['Quote Preview'] - $.on quote, 'mouseover', quotePreview.mouseover - $.on quote, 'mousemove', ui.hover - $.on quote, 'mouseout', quotePreview.mouseout - if conf['Quote Inline'] - $.on quote, 'click', quoteInline.toggle + quoteIndicators.node bq + if conf['Quote Preview'] + quotePreview.node bq + if conf['Quote Inline'] + quoteInline.node bq $.replace a.parentNode.parentNode, bq expandThread = @@ -545,9 +548,9 @@ expandThread = when 't' then 1 else 5 table = $.x "following::br[@clear]/preceding::table[#{num}]", a - while (prev = table.previousSibling) and (prev.nodeName is 'TABLE') + while (prev = table.previousSibling) and (prev.nodeName isnt 'A') $.rm prev - for backlink in $$ '.op a.backlink' + for backlink in $$ '.backlink', $ '.op', thread $.rm backlink if !$.id backlink.hash[1..] @@ -562,7 +565,7 @@ expandThread = body = $.el 'body', innerHTML: req.responseText - frag = d.createDocumentFragment() + nodes = [] for reply in $$ '.reply', body for quote in $$ '.quotelink', reply if (href = quote.getAttribute('href')) is quote.hash #add pathname to normal quotes @@ -572,11 +575,11 @@ expandThread = link = $ '.quotejs', reply link.href = "res/#{thread.firstChild.id}##{reply.id}" link.nextSibling.href = "res/#{thread.firstChild.id}#q#{reply.id}" - $.add frag, reply.parentNode.parentNode.parentNode + nodes.push reply.parentNode.parentNode.parentNode # eat everything, then replace with fresh full posts while (next = a.nextSibling) and not next.clear #br[clear] $.rm next - $.before next, frag + $.before next, nodes replyHiding = init: -> @@ -828,7 +831,7 @@ nav = $.on prev, 'click', nav.prev $.on next, 'click', nav.next - $.add span, prev, $.tn(' '), next + $.add span, [prev, $.tn(' '), next] $.add d.body, span prev: -> @@ -1973,13 +1976,13 @@ updater = return id = $('td[id]', updater.br.previousElementSibling)?.id or 0 - frag = d.createDocumentFragment() + nodes = [] for reply in $$('.reply', body).reverse() if reply.id <= id #make sure to not insert older posts break - $.prepend frag, reply.parentNode.parentNode.parentNode #table + nodes.push reply.parentNode.parentNode.parentNode #table - newPosts = frag.childNodes.length + newPosts = nodes.length scroll = conf['Scrolling'] && updater.scrollBG() && newPosts && updater.br.previousElementSibling.getBoundingClientRect().bottom - d.body.clientHeight < 25 if conf['Verbose'] @@ -1989,7 +1992,7 @@ updater = else updater.count.className = 'new' - $.before updater.br, frag + $.before updater.br, nodes.reverse() if scroll updater.br.previousSibling.scrollIntoView() @@ -2043,7 +2046,7 @@ watcher = refresh: (watched) -> watched or= $.get 'watched', {} - frag = d.createDocumentFragment() + nodes = [] for board of watched for id, props of watched[board] x = $.el 'a', @@ -2054,12 +2057,12 @@ watcher = link.title = link.textContent div = $.el 'div' - $.add div, x, $.tn(' '), link - $.add frag, div + $.add div, [x, $.tn(' '), link] + nodes.push div for div in $$ 'div:not(.move)', watcher.dialog $.rm div - $.add watcher.dialog, frag + $.add watcher.dialog, nodes watchedBoard = watched[g.BOARD] or {} for favicon in $$ 'img.favicon' @@ -2068,6 +2071,7 @@ watcher = favicon.src = Favicon.default else favicon.src = Favicon.empty + return cb: toggle: -> @@ -2139,9 +2143,10 @@ sauce = node: (root) -> return if root.className is 'inline' or not span = $ '.filesize', root img = span.nextElementSibling.nextElementSibling + nodes = [] for link in sauce.links - $.add span, $.tn(' '), link img - return + nodes.push $.tn(' '), link img + $.add span, nodes revealSpoilers = init: -> @@ -2273,7 +2278,8 @@ quoteBacklink = container = $.el 'span', className: 'container' root = $('.reportbutton', el) or $('span[id]', el) $.after root, container - $.add container, $.tn(' '), link + $.add container, [$.tn(' '), link] + return quoteInline = init: -> @@ -2283,6 +2289,7 @@ quoteInline = continue unless quote.hash quote.removeAttribute 'onclick' $.on quote, 'click', quoteInline.toggle + return toggle: (e) -> return if e.shiftKey or e.altKey or e.ctrlKey or e.metaKey or e.button isnt 0 e.preventDefault() @@ -2435,12 +2442,14 @@ quoteIndicators = tid = g.THREAD_ID or $.x('ancestor::div', root).firstChild.id for quote in $$ '.quotelink', root if conf['Indicate OP quote'] and quote.hash[1..] is tid - quote.innerHTML += ' (OP)' + # \u00A0 is nbsp + $.add quote, $.tn '\u00A0(OP)' return path = quote.pathname #if quote leads to a different thread id and is located on the same board (index 0) if conf['Indicate Cross-thread Quotes'] and path.lastIndexOf("/#{tid}") is -1 and path.indexOf("/#{g.BOARD}/") is 0 - quote.innerHTML += ' (Cross-thread)' + # \u00A0 is nbsp + $.add quote, $.tn '\u00A0(Cross-thread)' return reportButton = @@ -2453,8 +2462,7 @@ reportButton = className: 'reportbutton' innerHTML: '[ ! ]' href: 'javascript:;' - $.after span, a - $.after span, $.tn(' ') + $.after span, [$.tn(' '), a] $.on a, 'click', reportButton.report report: -> url = "http://sys.4chan.org/#{g.BOARD}/imgboard.php?mode=report&no=#{$.x('preceding-sibling::input', @).name}" @@ -2647,6 +2655,7 @@ imgExpand = else #contract for thumb in $$ 'img[md5][hidden]' imgExpand.contract thumb + return typeChange: -> switch @value when 'full'