diff --git a/4chan_x.user.js b/4chan_x.user.js index 5f4fb14c5..9e2680ae7 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -72,7 +72,7 @@ */ (function() { - var $, $$, DAY, Favicon, FileInfo, 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, updater, val, watcher, _base; + var $, $$, DAY, Favicon, FileInfo, 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, quoteDR, quoteInline, quoteOP, quotePreview, redirect, replyHiding, reportButton, revealSpoilers, sauce, strikethroughQuotes, threadHiding, threadStats, threading, titlePost, ui, unread, updater, val, watcher, _base; config = { main: { @@ -575,8 +575,9 @@ if (Object.keys(this.filters).length) return g.callbacks.push(this.node); }, createFilter: function(regexp, op, hl, top) { - return function(root, value, isOP) { - var firstThread, thisThread; + return function(post, value) { + var el, firstThread, isOP, thisThread; + el = post.el, isOP = post.isOP; if (isOP && op === 'no' || !isOP && op === 'only') return false; if (typeof regexp === 'string') { if (regexp !== value) return false; @@ -585,12 +586,12 @@ } if (hl) { if (isOP) { - $.addClass(root, hl); + $.addClass(el, hl); } else { - $.addClass(root.parentNode, hl); + $.addClass(el.parentNode, hl); } if (isOP && top && !g.REPLY) { - thisThread = root.parentNode; + thisThread = el.parentNode; if (firstThread = $('div[class=op]')) { $.before(firstThread.parentNode, [thisThread, thisThread.nextElementSibling]); } @@ -598,86 +599,85 @@ return false; } if (isOP) { - if (!g.REPLY) threadHiding.hideHide(root.parentNode); + if (!g.REPLY) threadHiding.hideHide(el.parentNode); } else { - replyHiding.hideHide(root); + replyHiding.hideHide(el); } return true; }; }, - node: function(root) { - var Filter, isOP, key, klass, value, _i, _len, _ref; - klass = root.className; - if (/\binlined\b/.test(klass)) return; - if (!(isOP = klass === 'op')) root = $('td[id]', root); + node: function(post) { + var Filter, key, value, _i, _len, _ref; + if (post.isInlined) return; for (key in filter.filters) { - value = filter[key](root, isOP); + value = filter[key](post); if (value === false) continue; _ref = filter.filters[key]; for (_i = 0, _len = _ref.length; _i < _len; _i++) { Filter = _ref[_i]; - if (Filter(root, value, isOP)) return; + if (Filter(post, value)) return; } } }, - name: function(root, isOP) { + name: function(post) { var name; - name = isOP ? $('.postername', root) : $('.commentpostername', root); + name = post.isOP ? $('.postername', post.el) : $('.commentpostername', post.el); return name.textContent; }, - tripcode: function(root) { + tripcode: function(post) { var trip; - if (trip = $('.postertrip', root)) return trip.textContent; + if (trip = $('.postertrip', post.el)) return trip.textContent; return false; }, - mod: function(root, isOP) { + mod: function(post) { var mod; - if (mod = (isOP ? $('.commentpostername', root) : $('.commentpostername ~ .commentpostername', root))) { + if (mod = (post.isOP ? $('.commentpostername', post.el) : $('.commentpostername ~ .commentpostername', post.el))) { return mod.textContent; } return false; }, - email: function(root) { + email: function(post) { var mail; - if (mail = $('.linkmail', root)) return mail.href; + if (mail = $('.linkmail', post.el)) return mail.href; return false; }, - subject: function(root, isOP) { + subject: function(post) { var sub; - sub = isOP ? $('.filetitle', root) : $('.replytitle', root); + sub = post.isOP ? $('.filetitle', post.el) : $('.replytitle', post.el); return sub.textContent; }, - comment: function(root) { - var data, i, len, nodes, text; + comment: function(post) { + var data, i, nodes, text, _ref; text = []; - nodes = d.evaluate('.//br|.//text()', root.lastChild, null, 7, null); - i = 0; - len = nodes.snapshotLength; - while (i < len) { - text.push((data = nodes.snapshotItem(i++).data) ? data : '\n'); + nodes = d.evaluate('.//br|.//text()', post.bq, 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'); } return text.join(''); }, - filename: function(root) { + filename: function(post) { var file; - if (file = $('.filesize > span', root)) return file.title; + if (file = $('span', post.filesize)) return file.title; return false; }, - dimensions: function(root) { - var match, span; - if ((span = $('.filesize', root)) && (match = span.textContent.match(/\d+x\d+/))) { + dimensions: function(post) { + var filesize, match; + filesize = post.filesize; + if (filesize && (match = filesize.textContent.match(/\d+x\d+/))) { return match[0]; } return false; }, - filesize: function(root) { + filesize: function(post) { var img; - if (img = $('img[md5]', root)) return img.alt; + img = post.img; + if (img) return img.alt; return false; }, - md5: function(root) { + md5: function(post) { var img; - if (img = $('img[md5]', root)) return img.getAttribute('md5'); + img = post.img; + if (img) return img.getAttribute('md5'); return false; } }; @@ -686,15 +686,15 @@ init: function() { return g.callbacks.push(this.node); }, - node: function(root) { + node: function(post) { var el, quote, _i, _len, _ref; - if (root.className === 'inline') return; - _ref = $$('.quotelink', root); + if (post.isInlined) return; + _ref = post.quotes; for (_i = 0, _len = _ref.length; _i < _len; _i++) { quote = _ref[_i]; if ((el = $.id(quote.hash.slice(1))) && el.parentNode.parentNode.parentNode.hidden) { $.addClass(quote, 'filtered'); - if (conf['Recursive Filtering']) root.hidden = true; + if (conf['Recursive Filtering']) post.root.hidden = true; } } } @@ -866,9 +866,10 @@ init: function() { return g.callbacks.push(this.node); }, - node: function(root) { + node: function(post) { var a, dd, id, reply; - if (!(dd = $('.doubledash', root))) return; + if (post["class"]) return; + dd = $('.doubledash', post.root); dd.className = 'replyhider'; a = $.el('a', { textContent: '[ - ]', @@ -1312,8 +1313,8 @@ $.on(d, 'dragstart', qr.drag); return $.on(d, 'dragend', qr.drag); }, - node: function(root) { - return $.on($('.quotejs + .quotejs', root), 'click', qr.quote); + node: function(post) { + return $.on($('.quotejs + .quotejs', post.el), 'click', qr.quote); }, open: function() { if (qr.el) { @@ -2745,11 +2746,11 @@ init: function() { return g.callbacks.push(this.node); }, - node: function(root) { + node: function(post) { var name, trip; - name = $('.commentpostername, .postername', root); + name = $('.commentpostername, .postername', post.el); name.textContent = 'Anonymous'; - if (trip = $('.postertrip', root)) { + if (trip = $('.postertrip', post.el)) { if (trip.parentNode.nodeName === 'A') { return $.rm(trip.parentNode); } else { @@ -2797,17 +2798,18 @@ }); }; }, - node: function(root) { - var img, link, nodes, span, _i, _len, _ref; - if (root.className === 'inline' || !(span = $('.filesize', root))) return; - img = span.nextElementSibling.nextElementSibling; + node: function(post) { + var img, link, nodes, _i, _len, _ref; + img = post.img; + if (post["class"] === 'inline' || !img) return; + img = img.parentNode; nodes = []; _ref = sauce.links; for (_i = 0, _len = _ref.length; _i < _len; _i++) { link = _ref[_i]; nodes.push($.tn(' '), link(img)); } - return $.add(span, nodes); + return $.add(post.filesize, nodes); } }; @@ -2815,11 +2817,12 @@ init: function() { return g.callbacks.push(this.node); }, - node: function(root) { + node: function(post) { var img; - if (!(img = $('img[alt^=Spoil]', root)) || root.className === 'inline') { - return; - } + img = { + post: post + }; + if (!(img && /^Spoil/.test(img.alt)) || post["class"] === 'inline') return; img.removeAttribute('height'); img.removeAttribute('width'); return img.src = "http://thumbs.4chan.org" + (img.parentNode.pathname.replace(/src(\/\d+).+$/, 'thumb$1s.jpg')); @@ -2844,10 +2847,10 @@ }; return g.callbacks.push(this.node); }, - node: function(root) { + node: function(post) { var node, time; - if (root.className === 'inline') return; - node = $('.posttime', root) || $('span[id]', root).previousSibling; + if (post["class"] === 'inline') return; + node = $('.posttime', post.el) || $('span[id]', post.el).previousSibling; Time.date = Time.parse(node); time = $.el('time', { textContent: ' ' + Time.funk(Time) + ' ' @@ -2937,9 +2940,9 @@ this.setFormats(); return g.callbacks.push(this.node); }, - node: function(root) { + node: function(post) { var fullname, link, node, regexp, resolution, shortname, size, type, unit, _, _ref; - if (root.className === 'inline' || !(node = $('.filesize', root))) return; + if (post["class"] === 'inline' || !(node = post.filesize)) return; type = node.childElementCount === 2 ? 0 : 1; regexp = type ? /^File: (<.+>)-\((?:Spoiler Image, )?([\d\.]+) (\w+), (\d+x\d+|PDF)/ : /^File: (<.+>)-\((?:Spoiler Image, )?([\d\.]+) (\w+), (\d+x\d+|PDF), ([^<]+)/; _ref = node.innerHTML.match(regexp), _ = _ref[0], link = _ref[1], size = _ref[2], unit = _ref[3], resolution = _ref[4], fullname = _ref[5], shortname = _ref[6]; @@ -3059,20 +3062,19 @@ quoteBacklink.funk = Function('id', "return '" + format + "'"); return g.callbacks.push(this.node); }, - node: function(root) { - var a, container, el, id, link, qid, quote, quotes, _i, _len, _ref; - if (/\binline\b/.test(root.className)) return; + node: function(post) { + var a, container, el, link, qid, quote, quotes, root, _i, _len, _ref; + if (post.isInlined) return; quotes = {}; - _ref = $$('.quotelink', root); + _ref = post.quotes; for (_i = 0, _len = _ref.length; _i < _len; _i++) { quote = _ref[_i]; if (qid = quote.hash.slice(1)) quotes[qid] = true; } - id = $('input', root).name; a = $.el('a', { - href: "#" + id, - className: root.hidden ? 'filtered backlink' : 'backlink', - textContent: quoteBacklink.funk(id) + href: "#" + post.id, + className: post.root.hidden ? 'filtered backlink' : 'backlink', + textContent: quoteBacklink.funk(post.id) }); for (qid in quotes) { if (!(el = $.id(qid)) || el.className === 'op' && !conf['OP Backlinks']) { @@ -3097,9 +3099,9 @@ init: function() { return g.callbacks.push(this.node); }, - node: function(root) { + node: function(post) { var quote, _i, _len, _ref; - _ref = $$('.quotelink, .backlink', root); + _ref = post.quotes.concat(post.backlinks); for (_i = 0, _len = _ref.length; _i < _len; _i++) { quote = _ref[_i]; if (!quote.hash) continue; @@ -3223,9 +3225,9 @@ init: function() { return g.callbacks.push(this.node); }, - node: function(root) { + node: function(post) { var quote, _i, _len, _ref; - _ref = $$('.quotelink, .backlink', root); + _ref = post.quotes.concat(post.backlinks); for (_i = 0, _len = _ref.length; _i < _len; _i++) { quote = _ref[_i]; if (quote.hash) $.on(quote, 'mouseover', quotePreview.mouseover); @@ -3301,24 +3303,36 @@ } }; - quoteIndicators = { + quoteOP = { init: function() { return g.callbacks.push(this.node); }, - node: function(root) { - var hash, path, quote, tid, _i, _len, _ref; - if (root.className === 'inline') return; - tid = g.THREAD_ID || $.x('ancestor::div[contains(@class,"thread")]', root).firstChild.id; - _ref = $$('.quotelink', root); + node: function(post) { + var quote, _i, _len, _ref; + if (post["class"] === 'inline') return; + _ref = post.quotes; for (_i = 0, _len = _ref.length; _i < _len; _i++) { quote = _ref[_i]; - if (!(hash = quote.hash.slice(1))) continue; - if (conf['Indicate OP quote'] && hash === tid) { + if (quote.hash.slice(1) === post.threadId) { $.add(quote, $.tn('\u00A0(OP)')); - continue; } - path = quote.pathname; - if (conf['Indicate Cross-thread Quotes'] && path.lastIndexOf("/" + tid) === -1 && path.indexOf("/" + g.BOARD + "/") === 0) { + } + } + }; + + quoteDR = { + init: function() { + return g.callbacks.push(this.node); + }, + node: function(post) { + var path, quote, _i, _len, _ref; + if (post["class"] === 'inline') return; + _ref = post.quotes; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + quote = _ref[_i]; + if (!quote.hash) continue; + path = quote.pathname.split('/'); + if (path[1] === g.BOARD && path[3] !== post.threadId) { $.add(quote, $.tn('\u00A0(Cross-thread)')); } } @@ -3329,16 +3343,15 @@ init: function() { return g.callbacks.push(this.node); }, - node: function(root) { - var a, span; - if (!(a = $('.reportbutton', root))) { - span = $('span[id]', root); + node: function(post) { + var a; + if (!(a = $('.reportbutton', post.el))) { a = $.el('a', { className: 'reportbutton', innerHTML: '[ ! ]', href: 'javascript:;' }); - $.after(span, [$.tn(' '), a]); + $.after($('span[id]', post.el), [$.tn(' '), a]); } return $.on(a, 'click', reportButton.report); }, @@ -3372,11 +3385,11 @@ })(); return g.callbacks.push(this.node); }, - node: function(root) { + node: function(post) { var imgcount; - if (/\binline\b/.test(root.className)) return; + if (post.isInlined) return; $.id('postcount').textContent = ++threadStats.posts; - if (!$('img[md5]', root)) return; + if (!post.img) return; imgcount = $.id('imagecount'); imgcount.textContent = ++threadStats.images; if (threadStats.images > threadStats.imgLimit) { @@ -3393,9 +3406,9 @@ return g.callbacks.push(this.node); }, replies: [], - node: function(root) { - if (root.hidden || root.className) return; - unread.replies.push(root); + node: function(post) { + if (post.root.hidden || post["class"]) return; + unread.replies.push(post.root); return unread.update(); }, scroll: function() { @@ -3530,10 +3543,9 @@ init: function() { return g.callbacks.push(this.node); }, - node: function(root) { - var thumb; - if (!(thumb = $('img[md5]', root))) return; - return $.on(thumb, 'mouseover', imgHover.mouseover); + node: function(post) { + if (!post.img) return; + return $.on(post.img, 'mouseover', imgHover.mouseover); }, mouseover: function() { ui.el = $.el('img', { @@ -3565,14 +3577,14 @@ init: function() { return g.callbacks.push(this.node); }, - node: function(root) { - var img, src, thumb; - if (root.hidden || !(thumb = $('img[md5]', root))) return; - src = thumb.parentNode.href; + node: function(post) { + var img, src; + if (post.root.hidden || post.img) return; + src = post.img.parentNode.href; if (/gif$/.test(src) && !/spoiler/.test(src)) { img = $.el('img'); $.on(img, 'load', function() { - return thumb.src = src; + return post.img.src = src; }); return img.src = src; } @@ -3584,12 +3596,12 @@ g.callbacks.push(this.node); return imgExpand.dialog(); }, - node: function(root) { - var a, thumb; - if (!(thumb = $('img[md5]', root))) return; - a = thumb.parentNode; + node: function(post) { + var a; + if (!post.img) return; + a = post.img.parentNode; $.on(a, 'click', imgExpand.cb.toggle); - if (imgExpand.on && !root.hidden && root.className !== 'inline') { + if (imgExpand.on && !post.root.hidden && post["class"] !== 'inline') { return imgExpand.expand(a.firstChild); } }, @@ -3814,9 +3826,8 @@ if (conf['Quote Inline']) quoteInline.init(); if (conf['Quote Preview']) quotePreview.init(); if (conf['Quote Backlinks']) quoteBacklink.init(); - if (conf['Indicate OP quote'] || conf['Indicate Cross-thread Quotes']) { - quoteIndicators.init(); - } + if (conf['Indicate OP quote']) quoteOP.init(); + if (conf['Indicate Cross-thread Quotes']) quoteDR.init(); return $.ready(Main.ready); }, ready: function() { @@ -3880,14 +3891,33 @@ } }, node: function(nodes, notify) { - var callback, node, _i, _j, _len, _len2, _ref; + var callback, klass, node, post, posts, _i, _j, _k, _len, _len2, _len3, _ref; + posts = []; + for (_i = 0, _len = nodes.length; _i < _len; _i++) { + node = nodes[_i]; + klass = node.className; + posts.push({ + root: node, + el: klass === 'op' ? node : $('td[id]', node), + "class": klass, + id: $('input', node).name, + threadId: g.THREAD_ID || $.x('ancestor::div[contains(@class,"thread")]', node).firstChild.id, + isOP: klass === 'op', + isInlined: /\binlined\b/.test(klass), + filesize: $('.filesize', node), + img: $('img[md5]', node), + quotes: $$('.quotelink', node), + backlinks: $$('.backlink', node), + bq: node.lastChild + }); + } _ref = g.callbacks; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - callback = _ref[_i]; + for (_j = 0, _len2 = _ref.length; _j < _len2; _j++) { + callback = _ref[_j]; try { - for (_j = 0, _len2 = nodes.length; _j < _len2; _j++) { - node = nodes[_j]; - callback(node); + for (_k = 0, _len3 = posts.length; _k < _len3; _k++) { + post = posts[_k]; + callback(post); } } catch (err) { if (notify) { diff --git a/script.coffee b/script.coffee index da72d9f53..f42c16664 100644 --- a/script.coffee +++ b/script.coffee @@ -499,7 +499,8 @@ filter = g.callbacks.push @node createFilter: (regexp, op, hl, top) -> - (root, value, isOP) -> + (post, value) -> + {el, isOP} = post if isOP and op is 'no' or !isOP and op is 'only' return false if typeof regexp is 'string' @@ -510,12 +511,12 @@ filter = return false if hl if isOP - $.addClass root, hl + $.addClass el, hl else - $.addClass root.parentNode, hl + $.addClass el.parentNode, hl if isOP and top and not g.REPLY # Put the highlighted OPs' threads on top of the board pages... - thisThread = root.parentNode + thisThread = el.parentNode # ...before the first non highlighted thread. if firstThread = $ 'div[class=op]' $.before firstThread.parentNode, [thisThread, thisThread.nextElementSibling] @@ -523,78 +524,76 @@ filter = return false if isOP unless g.REPLY - threadHiding.hideHide root.parentNode + threadHiding.hideHide el.parentNode else - replyHiding.hideHide root + replyHiding.hideHide el true - node: (root) -> - klass = root.className - return if /\binlined\b/.test klass - unless isOP = klass is 'op' - root = $ 'td[id]', root + node: (post) -> + return if post.isInlined for key of filter.filters - value = filter[key] root, isOP + value = filter[key] post if value is false # Continue if there's nothing to filter (no tripcode for example). continue for Filter in filter.filters[key] - if Filter root, value, isOP + if Filter post, value return - name: (root, isOP) -> - name = if isOP then $ '.postername', root else $ '.commentpostername', root + name: (post) -> + name = if post.isOP then $ '.postername', post.el else $ '.commentpostername', post.el name.textContent - tripcode: (root) -> - if trip = $ '.postertrip', root + tripcode: (post) -> + if trip = $ '.postertrip', post.el return trip.textContent false - mod: (root, isOP) -> - if mod = (if isOP then $ '.commentpostername', root else $ '.commentpostername ~ .commentpostername', root) + mod: (post) -> + if mod = (if post.isOP then $ '.commentpostername', post.el else $ '.commentpostername ~ .commentpostername', post.el) return mod.textContent false - email: (root) -> - if mail = $ '.linkmail', root + email: (post) -> + if mail = $ '.linkmail', post.el return mail.href false - subject: (root, isOP) -> - sub = if isOP then $ '.filetitle', root else $ '.replytitle', root + subject: (post) -> + sub = if post.isOP then $ '.filetitle', post.el else $ '.replytitle', post.el sub.textContent - comment: (root) -> + comment: (post) -> text = [] # XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE is 7 - nodes = d.evaluate './/br|.//text()', root.lastChild, null, 7, null - i = 0 - len = nodes.snapshotLength - while i < len - text.push if data = nodes.snapshotItem(i++).data then data else '\n' + nodes = d.evaluate './/br|.//text()', post.bq, null, 7, null + for i in [0...nodes.snapshotLength] + text.push if data = nodes.snapshotItem(i).data then data else '\n' text.join '' - filename: (root) -> - if file = $ '.filesize > span', root + filename: (post) -> + if file = $ 'span', post.filesize return file.title false - dimensions: (root) -> - if (span = $ '.filesize', root) and match = span.textContent.match /\d+x\d+/ + dimensions: (post) -> + {filesize} = post + if filesize and match = filesize.textContent.match /\d+x\d+/ return match[0] return false - filesize: (root) -> - if img = $ 'img[md5]', root + filesize: (post) -> + {img} = post + if img return img.alt false - md5: (root) -> - if img = $ 'img[md5]', root + md5: (post) -> + {img} = post + if img return img.getAttribute 'md5' false strikethroughQuotes = init: -> g.callbacks.push @node - node: (root) -> - return if root.className is 'inline' - for quote in $$ '.quotelink', root + 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' - root.hidden = true if conf['Recursive Filtering'] + post.root.hidden = true if conf['Recursive Filtering'] return expandComment = @@ -711,8 +710,9 @@ replyHiding = init: -> g.callbacks.push @node - node: (root) -> - return unless dd = $ '.doubledash', root + node: (post) -> + return if post.class + dd = $ '.doubledash', post.root dd.className = 'replyhider' a = $.el 'a', textContent: '[ - ]' @@ -755,8 +755,8 @@ replyHiding = if conf['Show Stubs'] name = $('.commentpostername', reply).textContent - uid = $('.posteruid', reply)?.textContent or '' - trip = $('.postertrip', reply)?.textContent or '' + uid = $('.posteruid', reply)?.textContent or '' + trip = $('.postertrip', reply)?.textContent or '' div = $.el 'div', className: 'stub' @@ -1037,8 +1037,8 @@ qr = $.on d, 'dragstart', qr.drag $.on d, 'dragend', qr.drag - node: (root) -> - $.on $('.quotejs + .quotejs', root), 'click', qr.quote + node: (post) -> + $.on $('.quotejs + .quotejs', post.el), 'click', qr.quote open: -> if qr.el @@ -2264,10 +2264,10 @@ watcher = anonymize = init: -> g.callbacks.push @node - node: (root) -> - name = $ '.commentpostername, .postername', root + node: (post) -> + name = $ '.commentpostername, .postername', post.el name.textContent = 'Anonymous' - if trip = $ '.postertrip', root + if trip = $ '.postertrip', post.el if trip.parentNode.nodeName is 'A' $.rm trip.parentNode else @@ -2302,19 +2302,22 @@ sauce = target: '_blank' textContent: domain - node: (root) -> - return if root.className is 'inline' or not span = $ '.filesize', root - img = span.nextElementSibling.nextElementSibling + node: (post) -> + {img} = post + return if post.class is 'inline' or not img + img = img.parentNode nodes = [] for link in sauce.links nodes.push $.tn(' '), link img - $.add span, nodes + $.add post.filesize, nodes revealSpoilers = init: -> g.callbacks.push @node - node: (root) -> - return if not (img = $ 'img[alt^=Spoil]', root) or root.className is 'inline' + node: (post) -> + img = {post} + if not (img and /^Spoil/.test img.alt) or post.class is 'inline' + return img.removeAttribute 'height' img.removeAttribute 'width' img.src = "http://thumbs.4chan.org#{img.parentNode.pathname.replace(/src(\/\d+).+$/, 'thumb$1s.jpg')}" @@ -2341,10 +2344,10 @@ Time = new Date year, month, day, hour, min g.callbacks.push @node - node: (root) -> - return if root.className is 'inline' + node: (post) -> + return if post.class is 'inline' # .posttime exists on every board except /f/ - node = $('.posttime', root) or $('span[id]', root).previousSibling + node = $('.posttime', post.el) or $('span[id]', post.el).previousSibling Time.date = Time.parse node time = $.el 'time', textContent: ' ' + Time.funk(Time) + ' ' @@ -2402,8 +2405,8 @@ FileInfo = return if g.BOARD is 'f' @setFormats() g.callbacks.push @node - node: (root) -> - return if root.className is 'inline' or not node = $ '.filesize', root + node: (post) -> + return if post.class is 'inline' or not node = post.filesize type = if node.childElementCount is 2 then 0 else 1 regexp = if type @@ -2484,20 +2487,18 @@ quoteBacklink = format = conf['backlink'].replace /%id/g, "' + id + '" quoteBacklink.funk = Function 'id', "return '#{format}'" g.callbacks.push @node - node: (root) -> - return if /\binline\b/.test root.className + node: (post) -> + return if post.isInlined quotes = {} - for quote in $$ '.quotelink', root + for quote in post.quotes # Don't process >>>/b/. if qid = quote.hash[1..] # Duplicate quotes get overwritten. quotes[qid] = true - # OP or reply id. - id = $('input', root).name a = $.el 'a', - href: "##{id}" - className: if root.hidden then 'filtered backlink' else 'backlink' - textContent: quoteBacklink.funk id + href: "##{post.id}" + className: if post.root.hidden then 'filtered backlink' else 'backlink' + 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'] @@ -2516,8 +2517,8 @@ quoteBacklink = quoteInline = init: -> g.callbacks.push @node - node: (root) -> - for quote in $$ '.quotelink, .backlink', root + node: (post) -> + for quote in post.quotes.concat post.backlinks continue unless quote.hash quote.removeAttribute 'onclick' $.on quote, 'click', quoteInline.toggle @@ -2608,8 +2609,8 @@ quoteInline = quotePreview = init: -> g.callbacks.push @node - node: (root) -> - for quote in $$ '.quotelink, .backlink', root + node: (post) -> + for quote in post.quotes.concat post.backlinks $.on quote, 'mouseover', quotePreview.mouseover if quote.hash return mouseover: (e) -> @@ -2668,23 +2669,29 @@ quotePreview = if conf['File Info Formatting'] FileInfo.node qp -quoteIndicators = +quoteOP = init: -> g.callbacks.push @node - node: (root) -> - return if root.className is 'inline' - # We use contains() so that it works with hidden threads - tid = g.THREAD_ID or $.x('ancestor::div[contains(@class,"thread")]', root).firstChild.id - for quote in $$ '.quotelink', root - unless hash = quote.hash[1..] - continue - if conf['Indicate OP quote'] and hash is tid + node: (post) -> + return if post.class is 'inline' + for quote in post.quotes + if quote.hash[1..] is post.threadId # \u00A0 is nbsp $.add quote, $.tn '\u00A0(OP)' + return + +quoteDR = + init: -> + g.callbacks.push @node + node: (post) -> + return if post.class is 'inline' + for quote in post.quotes + unless quote.hash + # Make sure this isn't a link to the board we're on. continue - 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 + path = quote.pathname.split '/' + # If quote leads to a different thread id and is located on the same board. + if path[1] is g.BOARD and path[3] isnt post.threadId # \u00A0 is nbsp $.add quote, $.tn '\u00A0(Cross-thread)' return @@ -2692,14 +2699,13 @@ quoteIndicators = reportButton = init: -> g.callbacks.push @node - node: (root) -> - if not a = $ '.reportbutton', root - span = $ 'span[id]', root + node: (post) -> + if not a = $ '.reportbutton', post.el a = $.el 'a', className: 'reportbutton' innerHTML: '[ ! ]' href: 'javascript:;' - $.after span, [$.tn(' '), a] + $.after $('span[id]', post.el), [$.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}" @@ -2722,10 +2728,10 @@ threadStats = else 151 g.callbacks.push @node - node: (root) -> - return if /\binline\b/.test root.className + node: (post) -> + return if post.isInlined $.id('postcount').textContent = ++threadStats.posts - return unless $ 'img[md5]', root + return unless post.img imgcount = $.id 'imagecount' imgcount.textContent = ++threadStats.images if threadStats.images > threadStats.imgLimit @@ -2740,9 +2746,9 @@ unread = replies: [] - node: (root) -> - return if root.hidden or root.className - unread.replies.push root + node: (post) -> + return if post.root.hidden or post.class + unread.replies.push post.root unread.update() scroll: -> @@ -2848,9 +2854,9 @@ redirect = imgHover = init: -> g.callbacks.push @node - node: (root) -> - return unless thumb = $ 'img[md5]', root - $.on thumb, 'mouseover', imgHover.mouseover + node: (post) -> + return unless post.img + $.on post.img, 'mouseover', imgHover.mouseover mouseover: -> ui.el = $.el 'img' id: 'ihover' @@ -2874,14 +2880,14 @@ imgHover = imgGif = init: -> g.callbacks.push @node - node: (root) -> - return if root.hidden or !thumb = $ 'img[md5]', root - src = thumb.parentNode.href + node: (post) -> + return if post.root.hidden or post.img + src = post.img.parentNode.href if /gif$/.test(src) and !/spoiler/.test src img = $.el 'img' $.on img, 'load', -> # Replace the thumbnail once the GIF has finished loading. - thumb.src = src + post.img.src = src img.src = src imgExpand = @@ -2889,11 +2895,11 @@ imgExpand = g.callbacks.push @node imgExpand.dialog() - node: (root) -> - return unless thumb = $ 'img[md5]', root - a = thumb.parentNode + node: (post) -> + return unless post.img + a = post.img.parentNode $.on a, 'click', imgExpand.cb.toggle - if imgExpand.on and !root.hidden and root.className isnt 'inline' + if imgExpand.on and !post.root.hidden and post.class isnt 'inline' imgExpand.expand a.firstChild cb: toggle: (e) -> @@ -3103,8 +3109,11 @@ Main = if conf['Quote Backlinks'] quoteBacklink.init() - if conf['Indicate OP quote'] or conf['Indicate Cross-thread Quotes'] - quoteIndicators.init() + if conf['Indicate OP quote'] + quoteOP.init() + + if conf['Indicate Cross-thread Quotes'] + quoteDR.init() $.ready Main.ready @@ -3162,7 +3171,6 @@ Main = if conf['Index Navigation'] nav.init() - nodes = $$ '.op, a + table', form Main.node nodes, true @@ -3191,9 +3199,25 @@ Main = window.location = "https://raw.github.com/mayhemydg/4chan-x/#{version}/4chan_x.user.js" node: (nodes, notify) -> + posts = [] + for node in nodes + klass = node.className + posts.push + root: node + el: if klass is 'op' then node else $ 'td[id]', node + class: klass + id: $('input', node).name + threadId: g.THREAD_ID or $.x('ancestor::div[contains(@class,"thread")]', node).firstChild.id + isOP: klass is 'op' + isInlined: /\binlined\b/.test klass + filesize: $ '.filesize', node + img: $ 'img[md5]', node + quotes: $$ '.quotelink', node + backlinks: $$ '.backlink', node + bq: node.lastChild for callback in g.callbacks try - callback node for node in nodes + callback post for post in posts catch err alert "4chan X error: #{err.message}\nhttp://mayhemydg.github.com/4chan-x/#bug-report\n\n#{err.stack}" if notify return