From 52b6d7982f2777e53e02b146a4a353b41453cf49 Mon Sep 17 00:00:00 2001 From: James Campos Date: Tue, 9 Nov 2010 03:17:24 -0800 Subject: [PATCH] formatting alphabetize funks, put main in one place --- 4chan_x.coffee | 915 +++++++++++++++++++---------------------- 4chan_x.js | 1069 ++++++++++++++++++++++++------------------------ 2 files changed, 966 insertions(+), 1018 deletions(-) diff --git a/4chan_x.coffee b/4chan_x.coffee index c4e52e513..fc2942d8d 100644 --- a/4chan_x.coffee +++ b/4chan_x.coffee @@ -4,6 +4,7 @@ # #TODO - 4chan time #addClass, removeClass; remove hide / show; makeDialog el, 'center' +#TODO - expose 'hidden' configs config = 'Thread Hiding': [true, 'Hide entire threads'] @@ -23,8 +24,7 @@ config = 'Post in Title': [true, 'Show the op\'s post in the tab title'] 'Sauce': [true, 'Add sauce to images'] -#TODO - expose 'hidden' configs - +#utility AEOS = init: -> #x-browser @@ -49,7 +49,6 @@ AEOS = style.type = 'text/css' style.textContent = css document.getElementsByTagName('head')[0].appendChild style - #dialog styling GM_addStyle ' div.dialog { @@ -63,13 +62,11 @@ AEOS = cursor: pointer; } ' - #dialog creation makeDialog: (id, position) -> dialog = document.createElement 'div' dialog.className = 'reply dialog' dialog.id = id - switch position when 'topleft' left = '0px' @@ -83,14 +80,11 @@ AEOS = when 'bottomright' left = null top = null - left = GM_getValue "#{id}Left", left top = GM_getValue "#{id}Top", top if left then dialog.style.left = left else dialog.style.right = '0px' if top then dialog.style.top = top else dialog.style.bottom = '0px' - dialog - #movement move: (e) -> div = @parentNode @@ -101,39 +95,31 @@ AEOS = #factor out div from document dimensions AEOS.width = document.body.clientWidth - div.offsetWidth AEOS.height = document.body.clientHeight - div.offsetHeight - document.addEventListener 'mousemove', AEOS.moveMove, true document.addEventListener 'mouseup', AEOS.moveEnd, true - moveMove: (e) -> div = AEOS.div - left = e.clientX - AEOS.dx if left < 20 then left = '0px' else if AEOS.width - left < 20 then left = '' right = if left then '' else '0px' div.style.left = left div.style.right = right - top = e.clientY - AEOS.dy if top < 20 then top = '0px' else if AEOS.height - top < 20 then top = '' bottom = if top then '' else '0px' div.style.top = top div.style.bottom = bottom - moveEnd: -> document.removeEventListener 'mousemove', AEOS.moveMove, true document.removeEventListener 'mouseup', AEOS.moveEnd, true - div = AEOS.div id = div.id GM_setValue "#{id}Left", div.style.left GM_setValue "#{id}Top", div.style.top -AEOS.init() d = document -#utility funks $ = (selector, root) -> root or= d.body root.querySelector(selector) @@ -190,6 +176,434 @@ x = (path, root) -> d.evaluate(path, root, null, XPathResult.ANY_UNORDERED_NODE_TYPE, null). singleNodeValue +#funks +autohide = -> + qr = $ '#qr' + klass = qr.className + if klass.indexOf('auto') is -1 + klass += ' auto' + else + klass = klass.replace(' auto', '') + qr.className = klass + +autoWatch = -> + #TODO look for subject + autoText = $('textarea', this).value.slice(0, 25) + GM_setValue('autoText', "/#{BOARD}/ - #{autoText}") + +close = -> + div = this.parentNode.parentNode + remove div + +clearHidden = -> + #'hidden' might be misleading; it's the number of IDs we're *looking* for, + # not the number of posts actually hidden on the page. + GM_deleteValue("hiddenReplies/#{BOARD}/") + GM_deleteValue("hiddenThreads/#{BOARD}/") + @value = "hidden: 0" + hiddenReplies = [] + hiddenThreads = [] + +cooldown = -> + submit = $ '#qr input[type=submit]' + seconds = parseInt submit.value + if seconds == 0 + submit.disabled = false + submit.value = 'Submit' + auto = submit.previousSibling.lastChild + if auto.checked + $('#qr form').submit() + #submit.click() doesn't work + else + submit.value = seconds - 1 + window.setTimeout cooldown, 1000 + +editSauce = -> + ta = $ '#options textarea' + if ta.style.display then show ta else hide ta + +expandComment = (e) -> + e.preventDefault() + a = this + href = a.getAttribute('href') + r = new XMLHttpRequest() + r.onload = -> + onloadComment(this.responseText, a, href) + r.open('GET', href, true) + r.send() + xhrs.push({ + r: r, + id: href.match(/\d+/)[0] + }) + +expandThread = -> + id = x('preceding-sibling::input[1]', this).name + span = this + #close expanded thread + if span.textContent[0] is '-' + #goddamit moot + num = if board is 'b' then 3 else 5 + table = x("following::br[@clear][1]/preceding::table[#{num}]", span) + while (prev = table.previousSibling) and (prev.nodeName is 'TABLE') + remove(prev) + span.textContent = span.textContent.replace('-', '+') + return + span.textContent = span.textContent.replace('+', 'X Loading...') + #load cache + for xhr in xhrs + if xhr.id == id + #why can't we just xhr.r.onload()? + onloadThread(xhr.r.responseText, span) + return + #create new request + r = new XMLHttpRequest() + r.onload = -> + onloadThread(this.responseText, span) + r.open('GET', "res/#{id}", true) + r.send() + xhrs.push({ + r: r, + id: id + }) + +hideReply = (reply) -> + if p = this.parentNode + reply = p.nextSibling + hiddenReplies.push({ + id: reply.id + timestamp: getTime() + }) + GM_setValue("hiddenReplies/#{BOARD}/", JSON.stringify(hiddenReplies)) + name = $('span.commentpostername', reply).textContent + trip = $('span.postertrip', reply)?.textContent || '' + table = x('ancestor::table', reply) + hide(table) + if getConfig 'Show Stubs' + a = n 'a', + textContent: "[ + ] #{name} #{trip}" + className: 'pointer' + listener: ['click', showReply] + div = n 'div' + addTo div, a + inBefore table, div + +hideThread = (div) -> + if p = @parentNode + div = p + hiddenThreads.push { + id: div.id + timestamp: getTime() + } + GM_setValue("hiddenThreads/#{BOARD}/", JSON.stringify(hiddenThreads)) + hide div + if getConfig 'Show Stubs' + if span = $ '.omittedposts', div + num = Number(span.textContent.match(/\d+/)[0]) + else + num = 0 + num += $$('table', div).length + text = if num is 1 then "1 reply" else "#{num} replies" + name = $('span.postername', div).textContent + trip = $('span.postername + span.postertrip', div)?.textContent || '' + a = n 'a', + textContent: "[ + ] #{name}#{trip} (#{text})" + className: 'pointer' + listener: ['click', showThread] + inBefore div, a + +iframeLoad = -> + if iframeLoop = !iframeLoop + return + $('iframe').src = 'about:blank' + qr = $('#qr') + if error = GM_getValue('error') + span = n 'span', + textContent: error + className: 'error' + addTo qr, span + $('input[title=autohide]:checked', qr)?.click() + else if REPLY and getConfig 'Persistent QR' + $('textarea', qr).value = '' + $('input[name=recaptcha_response_field]', qr).value = '' + submit = $('input[type=submit]', qr) + submit.value = 30 + submit.disabled = true + window.setTimeout cooldown, 1000 + auto = submit.previousSibling.lastChild + if auto.checked + #unhide the qr so you know it's ready for the next item + $('input[title=autohide]:checked', qr)?.click() + else + remove qr + recaptchaReload() + +nodeInserted = (e) -> + target = e.target + if target.nodeName is 'TABLE' + for callback in callbacks + callback(target) + else if target.id is 'recaptcha_challenge_field' and qr = $ '#qr' + $('#recaptcha_image img', qr).src = "http://www.google.com/recaptcha/api/image?c=" + target.value + $('#recaptcha_challenge_field', qr).value = target.value + +onloadComment = (responseText, a, href) -> + [_, op, id] = href.match(/(\d+)#(\d+)/) + [replies, opbq] = parseResponse(responseText) + if id is op + html = opbq.innerHTML + else + #css selectors don't like ids starting with numbers, + # getElementById only works for root document. + for reply in replies + if reply.id == id + html = $('blockquote', reply).innerHTML + bq = x('ancestor::blockquote', a) + bq.innerHTML = html + +onloadThread = (responseText, span) -> + [replies, opbq] = parseResponse(responseText) + span.textContent = span.textContent.replace('X Loading...', '- ') + #make sure all comments are fully expanded + span.previousSibling.innerHTML = opbq.innerHTML + while (next = span.nextSibling) and not next.clear#
+ remove(next) + if next + for reply in replies + inBefore next, x('ancestor::table', reply) + else#threading + div = span.parentNode + for reply in replies + addTo div, x('ancestor::table', reply) + +options = -> + if div = $ '#options' + remove div + else + div = AEOS.makeDialog 'options', 'center' + hiddenNum = hiddenReplies.length + hiddenThreads.length + html = '
Options X
' + for option, value of config + description = value[1] + checked = if getConfig option then "checked" else "" + html += "
" + html += "
Edit Sauce
" + html += "
" + html += "
" + div.innerHTML = html + $('div.move', div).addEventListener 'mousedown', AEOS.move, true + $('a.pointer', div).addEventListener 'click', optionsClose, true + $('a.sauce', div).addEventListener 'click', editSauce, true + $('textarea', div).value = GM_getValue 'saucePrefix', defaultSaucePrefix + $('input[type="button"]', div).addEventListener 'click', clearHidden, true + addTo d.body, div + +optionsClose = -> + div = this.parentNode.parentNode + inputs = $$('input', div) + for input in inputs + GM_setValue(input.name, input.checked) + GM_setValue 'saucePrefix', $('textarea', div).value + remove div + +parseResponse = (responseText) -> + body = n 'body', + innerHTML: responseText + replies = $$('td.reply', body) + opbq = $('blockquote', body) + return [replies, opbq] + +quickReply = (e) -> + unless qr = $('#qr') + #make quick reply dialog + qr = AEOS.makeDialog 'qr', 'topleft' + titlebar = n 'div', + innerHTML: 'Quick Reply ' + className: 'move' + listener: ['mousedown', AEOS.move] + addTo qr, titlebar + autohideB = n 'input', + type: 'checkbox' + className: 'pointer' + title: 'autohide' + listener: ['click', autohide] + closeB = n 'a', + textContent: 'X' + className: 'pointer' + title: 'close' + listener: ['click', close] + addTo titlebar, autohideB, tn(' '), closeB + form = $ 'form[name=post]' + clone = form.cloneNode(true) + #remove recaptcha scripts + for script in $$ 'script', clone + remove script + $('input[name=recaptcha_response_field]', clone). + addEventListener('keydown', recaptchaListener, true) + clone.addEventListener('submit', submit, true) + clone.target = 'iframe' + if not REPLY + #figure out which thread we're replying to + xpath = 'preceding::span[@class="postername"][1]/preceding::input[1]' + input = n 'input', + type: 'hidden' + name: 'resto' + value: x(xpath, this).name + addTo clone, input + else if getConfig 'Persistent QR' + submit = $ 'input[type=submit]', clone + auto = n 'label', + textContent: 'Auto' + autoBox = n 'input', + type: 'checkbox' + addTo auto, autoBox + inBefore submit, auto + addTo qr, clone + addTo d.body, qr + if e + e.preventDefault() + $('input[title=autohide]:checked', qr)?.click() + selection = window.getSelection() + id = x('preceding::span[@id][1]', selection.anchorNode)?.id + text = selection.toString() + textarea = $('textarea', qr) + textarea.focus() + #we can't just use @textContent b/c of the xxxs. goddamit moot. + textarea.value += '>>' + @parentNode.id.match(/\d+$/)[0] + '\n' + if text and id is this.parentNode.id + textarea.value += ">#{text}\n" + +recaptchaListener = (e) -> + if e.keyCode is 8 and this.value is '' + recaptchaReload() + +recaptchaReload = -> + window.location = 'javascript:Recaptcha.reload()' + +redirect = -> + switch BOARD + when 'a', 'g', 'lit', 'sci', 'tv' + url = "http://green-oval.net/cgi-board.pl/#{BOARD}/thread/#{THREAD_ID}#p" + when 'cgl', 'jp', 'm', 'tg' + url = "http://archive.easymodo.net/cgi-board.pl/#{BOARD}/thread/#{THREAD_ID}#p" + else + url = "http://boards.4chan.org/#{BOARD}" + location.href = url + +replyNav = -> + if REPLY + window.location = if @textContent is '▲' then '#navtop' else '#navbot' + else + direction = if @textContent is '▲' then 'preceding' else 'following' + op = x("#{direction}::span[starts-with(@id, 'nothread')][1]", this).id + window.location = "##{op}" + +report = -> + input = x('preceding-sibling::input[1]', this) + input.click() + $('input[value="Report"]').click() + input.click() + +showReply = -> + div = this.parentNode + table = div.nextSibling + show(table) + remove(div) + id = $('td.reply, td.replyhl', table).id + slice(hiddenReplies, id) + GM_setValue("hiddenReplies/#{BOARD}/", JSON.stringify(hiddenReplies)) + +showThread = -> + div = @nextSibling + show div + hide this + id = div.id + slice hiddenThreads, id + GM_setValue("hiddenThreads/#{BOARD}/", JSON.stringify(hiddenThreads)) + +submit = (e) -> + if span = @nextSibling + remove(span) + recaptcha = $('input[name=recaptcha_response_field]', this) + if recaptcha.value + $('#qr input[title=autohide]:not(:checked)')?.click() + else + e.preventDefault() + span = n 'span', + className: 'error' + textContent: 'You forgot to type in the verification.' + addTo @parentNode, span + alert 'You forgot to type in the verification.' + recaptcha.focus() + +stopPropagation = (e) -> + e.stopPropagation() + +threadF = (current) -> + div = n 'div', + className: 'thread' + a = n 'a', + textContent: '[ - ]' + className: 'pointer' + listener: ['click', hideThread] + addTo div, a + inBefore current, div + while (!current.clear)#
+ addTo div, current + current = div.nextSibling + addTo div, current + current = div.nextSibling + id = $('input[value="delete"]', div).name + div.id = id + #check if we should hide the thread + for hidden in hiddenThreads + if id == hidden.id + hideThread(div) + current = current.nextSibling.nextSibling + if current.nodeName isnt 'CENTER' + threadF(current) + +watch = -> + id = this.nextSibling.name + if this.src[0] is 'd'#data:png + this.src = favNormal + text = "/#{BOARD}/ - " + + x('following-sibling::blockquote', this).textContent.slice(0,25) + watched[BOARD] or= [] + watched[BOARD].push({ + id: id, + text: text + }) + else + this.src = favEmpty + watched[BOARD] = slice(watched[BOARD], id) + GM_setValue('watched', JSON.stringify(watched)) + watcherUpdate() + +watcherUpdate = -> + div = n 'div' + for board of watched + for thread in watched[board] + a = n 'a', + textContent: 'X' + className: 'pointer' + listener: ['click', watchX] + link = n 'a', + textContent: thread.text + href: "/#{board}/res/#{thread.id}" + addTo div, a, tn(' '), link, n('br') + old = $('#watcher div:last-child') + replace(old, div) + +watchX = -> + [board, _, id] = @nextElementSibling. + getAttribute('href').substring(1).split('/') + watched[board] = slice(watched[board], id) + GM_setValue('watched', JSON.stringify(watched)) + watcherUpdate() + if input = $("input[name=\"#{id}\"]") + favicon = input.previousSibling + favicon.src = favEmpty + #let's get this party started. watched = JSON.parse(GM_getValue('watched', '{}')) if location.hostname.split('.')[0] is 'sys' @@ -311,475 +725,8 @@ GM_addStyle(' ') -clearHidden = -> - #'hidden' might be misleading; it's the number of IDs we're *looking* for, - # not the number of posts actually hidden on the page. - GM_deleteValue("hiddenReplies/#{BOARD}/") - GM_deleteValue("hiddenThreads/#{BOARD}/") - @value = "hidden: 0" - hiddenReplies = [] - hiddenThreads = [] - - -editSauce = -> - ta = $ '#options textarea' - if ta.style.display then show ta else hide ta - -options = -> - if div = $ '#options' - remove div - else - div = AEOS.makeDialog 'options', 'center' - hiddenNum = hiddenReplies.length + hiddenThreads.length - html = '
Options X
' - for option, value of config - description = value[1] - checked = if getConfig option then "checked" else "" - html += "
" - html += "
Edit Sauce
" - html += "
" - html += "
" - div.innerHTML = html - $('div.move', div).addEventListener 'mousedown', AEOS.move, true - $('a.pointer', div).addEventListener 'click', optionsClose, true - $('a.sauce', div).addEventListener 'click', editSauce, true - $('textarea', div).value = GM_getValue 'saucePrefix', defaultSaucePrefix - $('input[type="button"]', div).addEventListener 'click', clearHidden, true - addTo d.body, div - - -showThread = -> - div = @nextSibling - show div - hide this - id = div.id - slice hiddenThreads, id - GM_setValue("hiddenThreads/#{BOARD}/", JSON.stringify(hiddenThreads)) - - -hideThread = (div) -> - if p = @parentNode - div = p - hiddenThreads.push { - id: div.id - timestamp: getTime() - } - GM_setValue("hiddenThreads/#{BOARD}/", JSON.stringify(hiddenThreads)) - hide div - if getConfig 'Show Stubs' - if span = $ '.omittedposts', div - num = Number(span.textContent.match(/\d+/)[0]) - else - num = 0 - num += $$('table', div).length - text = if num is 1 then "1 reply" else "#{num} replies" - name = $('span.postername', div).textContent - trip = $('span.postername + span.postertrip', div)?.textContent || '' - a = n 'a', - textContent: "[ + ] #{name}#{trip} (#{text})" - className: 'pointer' - listener: ['click', showThread] - inBefore div, a - - -threadF = (current) -> - div = n 'div', - className: 'thread' - a = n 'a', - textContent: '[ - ]' - className: 'pointer' - listener: ['click', hideThread] - addTo div, a - - inBefore current, div - while (!current.clear)#
- addTo div, current - current = div.nextSibling - addTo div, current - current = div.nextSibling - - id = $('input[value="delete"]', div).name - div.id = id - #check if we should hide the thread - for hidden in hiddenThreads - if id == hidden.id - hideThread(div) - - current = current.nextSibling.nextSibling - if current.nodeName isnt 'CENTER' - threadF(current) - - -showReply = -> - div = this.parentNode - table = div.nextSibling - show(table) - remove(div) - id = $('td.reply, td.replyhl', table).id - slice(hiddenReplies, id) - GM_setValue("hiddenReplies/#{BOARD}/", JSON.stringify(hiddenReplies)) - - -hideReply = (reply) -> - if p = this.parentNode - reply = p.nextSibling - hiddenReplies.push({ - id: reply.id - timestamp: getTime() - }) - GM_setValue("hiddenReplies/#{BOARD}/", JSON.stringify(hiddenReplies)) - - name = $('span.commentpostername', reply).textContent - trip = $('span.postertrip', reply)?.textContent || '' - table = x('ancestor::table', reply) - hide(table) - if getConfig 'Show Stubs' - a = n 'a', - textContent: "[ + ] #{name} #{trip}" - className: 'pointer' - listener: ['click', showReply] - div = n 'div' - addTo div, a - inBefore table, div - - -optionsClose = -> - div = this.parentNode.parentNode - inputs = $$('input', div) - for input in inputs - GM_setValue(input.name, input.checked) - GM_setValue 'saucePrefix', $('textarea', div).value - remove div - - -close = -> - div = this.parentNode.parentNode - remove div - -cooldown = -> - submit = $ '#qr input[type=submit]' - seconds = parseInt submit.value - if seconds == 0 - submit.disabled = false - submit.value = 'Submit' - auto = submit.previousSibling.lastChild - if auto.checked - $('#qr form').submit() - #submit.click() doesn't work - else - submit.value = seconds - 1 - window.setTimeout cooldown, 1000 - -iframeLoad = -> - if iframeLoop = !iframeLoop - return - $('iframe').src = 'about:blank' - - qr = $('#qr') - if error = GM_getValue('error') - span = n 'span', - textContent: error - className: 'error' - addTo qr, span - $('input[title=autohide]:checked', qr)?.click() - else if REPLY and getConfig 'Persistent QR' - $('textarea', qr).value = '' - $('input[name=recaptcha_response_field]', qr).value = '' - submit = $('input[type=submit]', qr) - submit.value = 30 - submit.disabled = true - window.setTimeout cooldown, 1000 - auto = submit.previousSibling.lastChild - if auto.checked - #unhide the qr so you know it's ready for the next item - $('input[title=autohide]:checked', qr)?.click() - else - remove qr - - recaptchaReload() - - -submit = (e) -> - if span = @nextSibling - remove(span) - recaptcha = $('input[name=recaptcha_response_field]', this) - if recaptcha.value - $('#qr input[title=autohide]:not(:checked)')?.click() - else - e.preventDefault() - span = n 'span', - className: 'error' - textContent: 'You forgot to type in the verification.' - addTo @parentNode, span - alert 'You forgot to type in the verification.' - recaptcha.focus() - - -autohide = -> - qr = $ '#qr' - klass = qr.className - if klass.indexOf('auto') is -1 - klass += ' auto' - else - klass = klass.replace(' auto', '') - qr.className = klass - - -quickReply = (e) -> - unless qr = $('#qr') - #make quick reply dialog - qr = AEOS.makeDialog 'qr', 'topleft' - - titlebar = n 'div', - innerHTML: 'Quick Reply ' - className: 'move' - listener: ['mousedown', AEOS.move] - addTo qr, titlebar - - autohideB = n 'input', - type: 'checkbox' - className: 'pointer' - title: 'autohide' - listener: ['click', autohide] - closeB = n 'a', - textContent: 'X' - className: 'pointer' - title: 'close' - listener: ['click', close] - addTo titlebar, autohideB, tn(' '), closeB - - form = $ 'form[name=post]' - clone = form.cloneNode(true) - #remove recaptcha scripts - for script in $$ 'script', clone - remove script - $('input[name=recaptcha_response_field]', clone). - addEventListener('keydown', recaptchaListener, true) - clone.addEventListener('submit', submit, true) - clone.target = 'iframe' - if not REPLY - #figure out which thread we're replying to - xpath = 'preceding::span[@class="postername"][1]/preceding::input[1]' - input = n 'input', - type: 'hidden' - name: 'resto' - value: x(xpath, this).name - addTo clone, input - else if getConfig 'Persistent QR' - submit = $ 'input[type=submit]', clone - auto = n 'label', - textContent: 'Auto' - autoBox = n 'input', - type: 'checkbox' - addTo auto, autoBox - inBefore submit, auto - addTo qr, clone - addTo d.body, qr - - if e - e.preventDefault() - - $('input[title=autohide]:checked', qr)?.click() - - selection = window.getSelection() - id = x('preceding::span[@id][1]', selection.anchorNode)?.id - text = selection.toString() - - textarea = $('textarea', qr) - textarea.focus() - #we can't just use @textContent b/c of the xxxs. goddamit moot. - textarea.value += '>>' + @parentNode.id.match(/\d+$/)[0] + '\n' - if text and id is this.parentNode.id - textarea.value += ">#{text}\n" - -watch = -> - id = this.nextSibling.name - if this.src[0] is 'd'#data:png - this.src = favNormal - text = "/#{BOARD}/ - " + - x('following-sibling::blockquote', this).textContent.slice(0,25) - watched[BOARD] or= [] - watched[BOARD].push({ - id: id, - text: text - }) - else - this.src = favEmpty - watched[BOARD] = slice(watched[BOARD], id) - - GM_setValue('watched', JSON.stringify(watched)) - watcherUpdate() - - -watchX = -> - [board, _, id] = @nextElementSibling. - getAttribute('href').substring(1).split('/') - watched[board] = slice(watched[board], id) - GM_setValue('watched', JSON.stringify(watched)) - watcherUpdate() - if input = $("input[name=\"#{id}\"]") - favicon = input.previousSibling - favicon.src = favEmpty - - -watcherUpdate = -> - div = n 'div' - for board of watched - for thread in watched[board] - a = n 'a', - textContent: 'X' - className: 'pointer' - listener: ['click', watchX] - link = n 'a', - textContent: thread.text - href: "/#{board}/res/#{thread.id}" - addTo div, a, tn(' '), link, n('br') - old = $('#watcher div:last-child') - replace(old, div) - - -parseResponse = (responseText) -> - body = n 'body', - innerHTML: responseText - replies = $$('td.reply', body) - opbq = $('blockquote', body) - return [replies, opbq] - - -onloadThread = (responseText, span) -> - [replies, opbq] = parseResponse(responseText) - span.textContent = span.textContent.replace('X Loading...', '- ') - - #make sure all comments are fully expanded - span.previousSibling.innerHTML = opbq.innerHTML - while (next = span.nextSibling) and not next.clear#
- remove(next) - if next - for reply in replies - inBefore next, x('ancestor::table', reply) - else#threading - div = span.parentNode - for reply in replies - addTo div, x('ancestor::table', reply) - - -expandThread = -> - id = x('preceding-sibling::input[1]', this).name - span = this - - #close expanded thread - if span.textContent[0] is '-' - #goddamit moot - num = if board is 'b' then 3 else 5 - table = x("following::br[@clear][1]/preceding::table[#{num}]", span) - while (prev = table.previousSibling) and (prev.nodeName is 'TABLE') - remove(prev) - span.textContent = span.textContent.replace('-', '+') - return - - span.textContent = span.textContent.replace('+', 'X Loading...') - #load cache - for xhr in xhrs - if xhr.id == id - #why can't we just xhr.r.onload()? - onloadThread(xhr.r.responseText, span) - return - - #create new request - r = new XMLHttpRequest() - r.onload = -> - onloadThread(this.responseText, span) - r.open('GET', "res/#{id}", true) - r.send() - xhrs.push({ - r: r, - id: id - }) - - -onloadComment = (responseText, a, href) -> - [_, op, id] = href.match(/(\d+)#(\d+)/) - [replies, opbq] = parseResponse(responseText) - if id is op - html = opbq.innerHTML - else - #css selectors don't like ids starting with numbers, - # getElementById only works for root document. - for reply in replies - if reply.id == id - html = $('blockquote', reply).innerHTML - bq = x('ancestor::blockquote', a) - bq.innerHTML = html - - -expandComment = (e) -> - e.preventDefault() - a = this - href = a.getAttribute('href') - r = new XMLHttpRequest() - r.onload = -> - onloadComment(this.responseText, a, href) - r.open('GET', href, true) - r.send() - xhrs.push({ - r: r, - id: href.match(/\d+/)[0] - }) - - -report = -> - input = x('preceding-sibling::input[1]', this) - input.click() - $('input[value="Report"]').click() - input.click() - - -nodeInserted = (e) -> - target = e.target - if target.nodeName is 'TABLE' - for callback in callbacks - callback(target) - else if target.id is 'recaptcha_challenge_field' and qr = $ '#qr' - $('#recaptcha_image img', qr).src = "http://www.google.com/recaptcha/api/image?c=" + target.value - $('#recaptcha_challenge_field', qr).value = target.value - - -autoWatch = -> - #TODO look for subject - autoText = $('textarea', this).value.slice(0, 25) - GM_setValue('autoText', "/#{BOARD}/ - #{autoText}") - - -stopPropagation = (e) -> - e.stopPropagation() - - -replyNav = -> - if REPLY - window.location = if @textContent is '▲' then '#navtop' else '#navbot' - else - direction = if @textContent is '▲' then 'preceding' else 'following' - op = x("#{direction}::span[starts-with(@id, 'nothread')][1]", this).id - window.location = "##{op}" - -recaptchaReload = -> - window.location = 'javascript:Recaptcha.reload()' - -recaptchaListener = (e) -> - if e.keyCode is 8 and this.value is '' - recaptchaReload() - -redirect = -> - switch BOARD - when 'a', 'g', 'lit', 'sci', 'tv' - url = "http://green-oval.net/cgi-board.pl/#{BOARD}/thread/#{THREAD_ID}#p" - when 'cgl', 'jp', 'm', 'tg' - url = "http://archive.easymodo.net/cgi-board.pl/#{BOARD}/thread/#{THREAD_ID}#p" - else - url = "http://boards.4chan.org/#{BOARD}" - location.href = url - #main part 2... +AEOS.init() if navtopr = $ '#navtopr a' text = navtopr.nextSibling #css doesn't see text nodes a = n 'a', diff --git a/4chan_x.js b/4chan_x.js index 63e2dd51e..bb5ce2445 100644 --- a/4chan_x.js +++ b/4chan_x.js @@ -143,7 +143,6 @@ return GM_setValue("" + (id) + "Top", div.style.top); } }; - AEOS.init(); d = document; $ = function(selector, root) { root || (root = d.body); @@ -240,6 +239,540 @@ root || (root = d.body); return d.evaluate(path, root, null, XPathResult.ANY_UNORDERED_NODE_TYPE, null).singleNodeValue; }; + autohide = function() { + var klass, qr; + qr = $('#qr'); + klass = qr.className; + if (klass.indexOf('auto') === -1) { + klass += ' auto'; + } else { + klass = klass.replace(' auto', ''); + } + return (qr.className = klass); + }; + autoWatch = function() { + var autoText; + autoText = $('textarea', this).value.slice(0, 25); + return GM_setValue('autoText', "/" + (BOARD) + "/ - " + (autoText)); + }; + close = function() { + var div; + div = this.parentNode.parentNode; + return remove(div); + }; + clearHidden = function() { + var hiddenReplies, hiddenThreads; + GM_deleteValue("hiddenReplies/" + (BOARD) + "/"); + GM_deleteValue("hiddenThreads/" + (BOARD) + "/"); + this.value = "hidden: 0"; + hiddenReplies = []; + return (hiddenThreads = []); + }; + cooldown = function() { + var auto, seconds, submit; + submit = $('#qr input[type=submit]'); + seconds = parseInt(submit.value); + if (seconds === 0) { + submit.disabled = false; + submit.value = 'Submit'; + auto = submit.previousSibling.lastChild; + return auto.checked ? $('#qr form').submit() : null; + } else { + submit.value = seconds - 1; + return window.setTimeout(cooldown, 1000); + } + }; + editSauce = function() { + var ta; + ta = $('#options textarea'); + return ta.style.display ? show(ta) : hide(ta); + }; + expandComment = function(e) { + var a, href, r; + e.preventDefault(); + a = this; + href = a.getAttribute('href'); + r = new XMLHttpRequest(); + r.onload = function() { + return onloadComment(this.responseText, a, href); + }; + r.open('GET', href, true); + r.send(); + return xhrs.push({ + r: r, + id: href.match(/\d+/)[0] + }); + }; + expandThread = function() { + var _i, _len, _ref, id, num, prev, r, span, table, xhr; + id = x('preceding-sibling::input[1]', this).name; + span = this; + if (span.textContent[0] === '-') { + num = board === 'b' ? 3 : 5; + table = x("following::br[@clear][1]/preceding::table[" + (num) + "]", span); + while ((prev = table.previousSibling) && (prev.nodeName === 'TABLE')) { + remove(prev); + } + span.textContent = span.textContent.replace('-', '+'); + return null; + } + span.textContent = span.textContent.replace('+', 'X Loading...'); + _ref = xhrs; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + xhr = _ref[_i]; + if (xhr.id === id) { + onloadThread(xhr.r.responseText, span); + return null; + } + } + r = new XMLHttpRequest(); + r.onload = function() { + return onloadThread(this.responseText, span); + }; + r.open('GET', "res/" + (id), true); + r.send(); + return xhrs.push({ + r: r, + id: id + }); + }; + hideReply = function(reply) { + var _ref, _ref2, a, div, name, p, table, trip; + if (p = this.parentNode) { + reply = p.nextSibling; + hiddenReplies.push({ + id: reply.id, + timestamp: getTime() + }); + GM_setValue("hiddenReplies/" + (BOARD) + "/", JSON.stringify(hiddenReplies)); + } + name = $('span.commentpostername', reply).textContent; + trip = ((typeof (_ref2 = ((_ref = $('span.postertrip', reply)))) === "undefined" || _ref2 === null) ? undefined : _ref2.textContent) || ''; + table = x('ancestor::table', reply); + hide(table); + if (getConfig('Show Stubs')) { + a = n('a', { + textContent: ("[ + ] " + (name) + " " + (trip)), + className: 'pointer', + listener: ['click', showReply] + }); + div = n('div'); + addTo(div, a); + return inBefore(table, div); + } + }; + hideThread = function(div) { + var _ref, _ref2, a, name, num, p, span, text, trip; + if (p = this.parentNode) { + div = p; + hiddenThreads.push({ + id: div.id, + timestamp: getTime() + }); + GM_setValue("hiddenThreads/" + (BOARD) + "/", JSON.stringify(hiddenThreads)); + } + hide(div); + if (getConfig('Show Stubs')) { + if (span = $('.omittedposts', div)) { + num = Number(span.textContent.match(/\d+/)[0]); + } else { + num = 0; + } + num += $$('table', div).length; + text = num === 1 ? "1 reply" : ("" + (num) + " replies"); + name = $('span.postername', div).textContent; + trip = ((typeof (_ref2 = ((_ref = $('span.postername + span.postertrip', div)))) === "undefined" || _ref2 === null) ? undefined : _ref2.textContent) || ''; + a = n('a', { + textContent: ("[ + ] " + (name) + (trip) + " (" + (text) + ")"), + className: 'pointer', + listener: ['click', showThread] + }); + return inBefore(div, a); + } + }; + iframeLoad = function() { + var _ref, _ref2, auto, error, iframeLoop, qr, span, submit; + if (iframeLoop = !iframeLoop) { + return null; + } + $('iframe').src = 'about:blank'; + qr = $('#qr'); + if (error = GM_getValue('error')) { + span = n('span', { + textContent: error, + className: 'error' + }); + addTo(qr, span); + (typeof (_ref2 = ((_ref = $('input[title=autohide]:checked', qr)))) === "undefined" || _ref2 === null) ? undefined : _ref2.click(); + } else if (REPLY && getConfig('Persistent QR')) { + $('textarea', qr).value = ''; + $('input[name=recaptcha_response_field]', qr).value = ''; + submit = $('input[type=submit]', qr); + submit.value = 30; + submit.disabled = true; + window.setTimeout(cooldown, 1000); + auto = submit.previousSibling.lastChild; + if (auto.checked) { + (typeof (_ref2 = ((_ref = $('input[title=autohide]:checked', qr)))) === "undefined" || _ref2 === null) ? undefined : _ref2.click(); + } + } else { + remove(qr); + } + return recaptchaReload(); + }; + nodeInserted = function(e) { + var _i, _len, _ref, _result, callback, qr, target; + target = e.target; + if (target.nodeName === 'TABLE') { + _result = []; _ref = callbacks; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + callback = _ref[_i]; + _result.push(callback(target)); + } + return _result; + } else if (target.id === 'recaptcha_challenge_field' && (qr = $('#qr'))) { + $('#recaptcha_image img', qr).src = "http://www.google.com/recaptcha/api/image?c=" + target.value; + return ($('#recaptcha_challenge_field', qr).value = target.value); + } + }; + onloadComment = function(responseText, a, href) { + var _, _i, _len, _ref, bq, html, id, op, opbq, replies, reply; + _ref = href.match(/(\d+)#(\d+)/); + _ = _ref[0]; + op = _ref[1]; + id = _ref[2]; + _ref = parseResponse(responseText); + replies = _ref[0]; + opbq = _ref[1]; + if (id === op) { + html = opbq.innerHTML; + } else { + _ref = replies; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + reply = _ref[_i]; + if (reply.id === id) { + html = $('blockquote', reply).innerHTML; + } + } + } + bq = x('ancestor::blockquote', a); + return (bq.innerHTML = html); + }; + onloadThread = function(responseText, span) { + var _i, _len, _ref, _result, div, next, opbq, replies, reply; + _ref = parseResponse(responseText); + replies = _ref[0]; + opbq = _ref[1]; + span.textContent = span.textContent.replace('X Loading...', '- '); + span.previousSibling.innerHTML = opbq.innerHTML; + while ((next = span.nextSibling) && !next.clear) { + remove(next); + } + if (next) { + _result = []; _ref = replies; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + reply = _ref[_i]; + _result.push(inBefore(next, x('ancestor::table', reply))); + } + return _result; + } else { + div = span.parentNode; + _result = []; _ref = replies; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + reply = _ref[_i]; + _result.push(addTo(div, x('ancestor::table', reply))); + } + return _result; + } + }; + options = function() { + var _ref, checked, description, div, hiddenNum, html, option, value; + if (div = $('#options')) { + return remove(div); + } else { + div = AEOS.makeDialog('options', 'center'); + hiddenNum = hiddenReplies.length + hiddenThreads.length; + html = '
Options X
'; + _ref = config; + for (option in _ref) { + if (!__hasProp.call(_ref, option)) continue; + value = _ref[option]; + description = value[1]; + checked = getConfig(option) ? "checked" : ""; + html += ("
"); + } + html += "
Edit Sauce
"; + html += "
"; + html += ("
"); + div.innerHTML = html; + $('div.move', div).addEventListener('mousedown', AEOS.move, true); + $('a.pointer', div).addEventListener('click', optionsClose, true); + $('a.sauce', div).addEventListener('click', editSauce, true); + $('textarea', div).value = GM_getValue('saucePrefix', defaultSaucePrefix); + $('input[type="button"]', div).addEventListener('click', clearHidden, true); + return addTo(d.body, div); + } + }; + optionsClose = function() { + var _i, _len, _ref, div, input, inputs; + div = this.parentNode.parentNode; + inputs = $$('input', div); + _ref = inputs; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + input = _ref[_i]; + GM_setValue(input.name, input.checked); + } + GM_setValue('saucePrefix', $('textarea', div).value); + return remove(div); + }; + parseResponse = function(responseText) { + var body, opbq, replies; + body = n('body', { + innerHTML: responseText + }); + replies = $$('td.reply', body); + opbq = $('blockquote', body); + return [replies, opbq]; + }; + quickReply = function(e) { + var _i, _len, _ref, _ref2, auto, autoBox, autohideB, clone, closeB, form, id, input, qr, script, selection, submit, text, textarea, titlebar, xpath; + if (!(qr = $('#qr'))) { + qr = AEOS.makeDialog('qr', 'topleft'); + titlebar = n('div', { + innerHTML: 'Quick Reply ', + className: 'move', + listener: ['mousedown', AEOS.move] + }); + addTo(qr, titlebar); + autohideB = n('input', { + type: 'checkbox', + className: 'pointer', + title: 'autohide', + listener: ['click', autohide] + }); + closeB = n('a', { + textContent: 'X', + className: 'pointer', + title: 'close', + listener: ['click', close] + }); + addTo(titlebar, autohideB, tn(' '), closeB); + form = $('form[name=post]'); + clone = form.cloneNode(true); + _ref = $$('script', clone); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + script = _ref[_i]; + remove(script); + } + $('input[name=recaptcha_response_field]', clone).addEventListener('keydown', recaptchaListener, true); + clone.addEventListener('submit', submit, true); + clone.target = 'iframe'; + if (!REPLY) { + xpath = 'preceding::span[@class="postername"][1]/preceding::input[1]'; + input = n('input', { + type: 'hidden', + name: 'resto', + value: x(xpath, this).name + }); + addTo(clone, input); + } else if (getConfig('Persistent QR')) { + submit = $('input[type=submit]', clone); + auto = n('label', { + textContent: 'Auto' + }); + autoBox = n('input', { + type: 'checkbox' + }); + addTo(auto, autoBox); + inBefore(submit, auto); + } + addTo(qr, clone); + addTo(d.body, qr); + } + if (e) { + e.preventDefault(); + (typeof (_ref2 = ((_ref = $('input[title=autohide]:checked', qr)))) === "undefined" || _ref2 === null) ? undefined : _ref2.click(); + selection = window.getSelection(); + id = (typeof (_ref2 = ((_ref = x('preceding::span[@id][1]', selection.anchorNode)))) === "undefined" || _ref2 === null) ? undefined : _ref2.id; + text = selection.toString(); + textarea = $('textarea', qr); + textarea.focus(); + textarea.value += '>>' + this.parentNode.id.match(/\d+$/)[0] + '\n'; + return text && id === this.parentNode.id ? textarea.value += (">" + (text) + "\n") : null; + } + }; + recaptchaListener = function(e) { + return e.keyCode === 8 && this.value === '' ? recaptchaReload() : null; + }; + recaptchaReload = function() { + return (window.location = 'javascript:Recaptcha.reload()'); + }; + redirect = function() { + var url; + switch (BOARD) { + case 'a': + case 'g': + case 'lit': + case 'sci': + case 'tv': + url = ("http://green-oval.net/cgi-board.pl/" + (BOARD) + "/thread/" + (THREAD_ID) + "#p"); + break; + case 'cgl': + case 'jp': + case 'm': + case 'tg': + url = ("http://archive.easymodo.net/cgi-board.pl/" + (BOARD) + "/thread/" + (THREAD_ID) + "#p"); + break; + default: + url = ("http://boards.4chan.org/" + (BOARD)); + } + return (location.href = url); + }; + replyNav = function() { + var direction, op; + if (REPLY) { + return (window.location = this.textContent === '▲' ? '#navtop' : '#navbot'); + } else { + direction = this.textContent === '▲' ? 'preceding' : 'following'; + op = x("" + (direction) + "::span[starts-with(@id, 'nothread')][1]", this).id; + return (window.location = ("#" + (op))); + } + }; + report = function() { + var input; + input = x('preceding-sibling::input[1]', this); + input.click(); + $('input[value="Report"]').click(); + return input.click(); + }; + showReply = function() { + var div, id, table; + div = this.parentNode; + table = div.nextSibling; + show(table); + remove(div); + id = $('td.reply, td.replyhl', table).id; + slice(hiddenReplies, id); + return GM_setValue("hiddenReplies/" + (BOARD) + "/", JSON.stringify(hiddenReplies)); + }; + showThread = function() { + var div, id; + div = this.nextSibling; + show(div); + hide(this); + id = div.id; + slice(hiddenThreads, id); + return GM_setValue("hiddenThreads/" + (BOARD) + "/", JSON.stringify(hiddenThreads)); + }; + submit = function(e) { + var _ref, _ref2, recaptcha, span; + if (span = this.nextSibling) { + remove(span); + } + recaptcha = $('input[name=recaptcha_response_field]', this); + if (recaptcha.value) { + return (typeof (_ref2 = ((_ref = $('#qr input[title=autohide]:not(:checked)')))) === "undefined" || _ref2 === null) ? undefined : _ref2.click(); + } else { + e.preventDefault(); + span = n('span', { + className: 'error', + textContent: 'You forgot to type in the verification.' + }); + addTo(this.parentNode, span); + alert('You forgot to type in the verification.'); + return recaptcha.focus(); + } + }; + stopPropagation = function(e) { + return e.stopPropagation(); + }; + threadF = function(current) { + var _i, _len, _ref, a, div, hidden, id; + div = n('div', { + className: 'thread' + }); + a = n('a', { + textContent: '[ - ]', + className: 'pointer', + listener: ['click', hideThread] + }); + addTo(div, a); + inBefore(current, div); + while ((!current.clear)) { + addTo(div, current); + current = div.nextSibling; + } + addTo(div, current); + current = div.nextSibling; + id = $('input[value="delete"]', div).name; + div.id = id; + _ref = hiddenThreads; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + hidden = _ref[_i]; + if (id === hidden.id) { + hideThread(div); + } + } + current = current.nextSibling.nextSibling; + return current.nodeName !== 'CENTER' ? threadF(current) : null; + }; + watch = function() { + var id, text; + id = this.nextSibling.name; + if (this.src[0] === 'd') { + this.src = favNormal; + text = ("/" + (BOARD) + "/ - ") + x('following-sibling::blockquote', this).textContent.slice(0, 25); + watched[BOARD] || (watched[BOARD] = []); + watched[BOARD].push({ + id: id, + text: text + }); + } else { + this.src = favEmpty; + watched[BOARD] = slice(watched[BOARD], id); + } + GM_setValue('watched', JSON.stringify(watched)); + return watcherUpdate(); + }; + watcherUpdate = function() { + var _i, _j, _len, _ref, _ref2, a, board, div, link, old, thread; + div = n('div'); + _ref = watched; + for (board in _ref) { + if (!__hasProp.call(_ref, board)) continue; + _i = _ref[board]; + _ref2 = watched[board]; + for (_j = 0, _len = _ref2.length; _j < _len; _j++) { + thread = _ref2[_j]; + a = n('a', { + textContent: 'X', + className: 'pointer', + listener: ['click', watchX] + }); + link = n('a', { + textContent: thread.text, + href: ("/" + (board) + "/res/" + (thread.id)) + }); + addTo(div, a, tn(' '), link, n('br')); + } + } + old = $('#watcher div:last-child'); + return replace(old, div); + }; + watchX = function() { + var _, _ref, board, favicon, id, input; + _ref = this.nextElementSibling.getAttribute('href').substring(1).split('/'); + board = _ref[0]; + _ = _ref[1]; + id = _ref[2]; + watched[board] = slice(watched[board], id); + GM_setValue('watched', JSON.stringify(watched)); + watcherUpdate(); + if (input = $("input[name=\"" + (id) + "\"]")) { + favicon = input.previousSibling; + return (favicon.src = favEmpty); + } + }; watched = JSON.parse(GM_getValue('watched', '{}')); if (location.hostname.split('.')[0] === 'sys') { if (b = $('table font b')) { @@ -362,539 +895,7 @@ cursor: pointer;\ }\ '); - clearHidden = function() { - GM_deleteValue("hiddenReplies/" + (BOARD) + "/"); - GM_deleteValue("hiddenThreads/" + (BOARD) + "/"); - this.value = "hidden: 0"; - hiddenReplies = []; - return (hiddenThreads = []); - }; - editSauce = function() { - var ta; - ta = $('#options textarea'); - return ta.style.display ? show(ta) : hide(ta); - }; - options = function() { - var _ref2, checked, description, div, hiddenNum, option, value; - if (div = $('#options')) { - return remove(div); - } else { - div = AEOS.makeDialog('options', 'center'); - hiddenNum = hiddenReplies.length + hiddenThreads.length; - html = '
Options X
'; - _ref2 = config; - for (option in _ref2) { - if (!__hasProp.call(_ref2, option)) continue; - value = _ref2[option]; - description = value[1]; - checked = getConfig(option) ? "checked" : ""; - html += ("
"); - } - html += ""; - html += "
"; - html += ("
"); - div.innerHTML = html; - $('div.move', div).addEventListener('mousedown', AEOS.move, true); - $('a.pointer', div).addEventListener('click', optionsClose, true); - $('a.sauce', div).addEventListener('click', editSauce, true); - $('textarea', div).value = GM_getValue('saucePrefix', defaultSaucePrefix); - $('input[type="button"]', div).addEventListener('click', clearHidden, true); - return addTo(d.body, div); - } - }; - showThread = function() { - var div; - div = this.nextSibling; - show(div); - hide(this); - id = div.id; - slice(hiddenThreads, id); - return GM_setValue("hiddenThreads/" + (BOARD) + "/", JSON.stringify(hiddenThreads)); - }; - hideThread = function(div) { - var _ref2, _ref3, a, name, num, p, span, text, trip; - if (p = this.parentNode) { - div = p; - hiddenThreads.push({ - id: div.id, - timestamp: getTime() - }); - GM_setValue("hiddenThreads/" + (BOARD) + "/", JSON.stringify(hiddenThreads)); - } - hide(div); - if (getConfig('Show Stubs')) { - if (span = $('.omittedposts', div)) { - num = Number(span.textContent.match(/\d+/)[0]); - } else { - num = 0; - } - num += $$('table', div).length; - text = num === 1 ? "1 reply" : ("" + (num) + " replies"); - name = $('span.postername', div).textContent; - trip = ((typeof (_ref3 = ((_ref2 = $('span.postername + span.postertrip', div)))) === "undefined" || _ref3 === null) ? undefined : _ref3.textContent) || ''; - a = n('a', { - textContent: ("[ + ] " + (name) + (trip) + " (" + (text) + ")"), - className: 'pointer', - listener: ['click', showThread] - }); - return inBefore(div, a); - } - }; - threadF = function(current) { - var _i, _len, _ref2, a, div, hidden; - div = n('div', { - className: 'thread' - }); - a = n('a', { - textContent: '[ - ]', - className: 'pointer', - listener: ['click', hideThread] - }); - addTo(div, a); - inBefore(current, div); - while ((!current.clear)) { - addTo(div, current); - current = div.nextSibling; - } - addTo(div, current); - current = div.nextSibling; - id = $('input[value="delete"]', div).name; - div.id = id; - _ref2 = hiddenThreads; - for (_i = 0, _len = _ref2.length; _i < _len; _i++) { - hidden = _ref2[_i]; - if (id === hidden.id) { - hideThread(div); - } - } - current = current.nextSibling.nextSibling; - return current.nodeName !== 'CENTER' ? threadF(current) : null; - }; - showReply = function() { - var div, table; - div = this.parentNode; - table = div.nextSibling; - show(table); - remove(div); - id = $('td.reply, td.replyhl', table).id; - slice(hiddenReplies, id); - return GM_setValue("hiddenReplies/" + (BOARD) + "/", JSON.stringify(hiddenReplies)); - }; - hideReply = function(reply) { - var _ref2, _ref3, a, div, name, p, table, trip; - if (p = this.parentNode) { - reply = p.nextSibling; - hiddenReplies.push({ - id: reply.id, - timestamp: getTime() - }); - GM_setValue("hiddenReplies/" + (BOARD) + "/", JSON.stringify(hiddenReplies)); - } - name = $('span.commentpostername', reply).textContent; - trip = ((typeof (_ref3 = ((_ref2 = $('span.postertrip', reply)))) === "undefined" || _ref3 === null) ? undefined : _ref3.textContent) || ''; - table = x('ancestor::table', reply); - hide(table); - if (getConfig('Show Stubs')) { - a = n('a', { - textContent: ("[ + ] " + (name) + " " + (trip)), - className: 'pointer', - listener: ['click', showReply] - }); - div = n('div'); - addTo(div, a); - return inBefore(table, div); - } - }; - optionsClose = function() { - var _i, _len, _ref2, div, input, inputs; - div = this.parentNode.parentNode; - inputs = $$('input', div); - _ref2 = inputs; - for (_i = 0, _len = _ref2.length; _i < _len; _i++) { - input = _ref2[_i]; - GM_setValue(input.name, input.checked); - } - GM_setValue('saucePrefix', $('textarea', div).value); - return remove(div); - }; - close = function() { - var div; - div = this.parentNode.parentNode; - return remove(div); - }; - cooldown = function() { - var auto, seconds, submit; - submit = $('#qr input[type=submit]'); - seconds = parseInt(submit.value); - if (seconds === 0) { - submit.disabled = false; - submit.value = 'Submit'; - auto = submit.previousSibling.lastChild; - return auto.checked ? $('#qr form').submit() : null; - } else { - submit.value = seconds - 1; - return window.setTimeout(cooldown, 1000); - } - }; - iframeLoad = function() { - var _ref2, _ref3, auto, error, qr, span, submit; - if (iframeLoop = !iframeLoop) { - return null; - } - $('iframe').src = 'about:blank'; - qr = $('#qr'); - if (error = GM_getValue('error')) { - span = n('span', { - textContent: error, - className: 'error' - }); - addTo(qr, span); - (typeof (_ref3 = ((_ref2 = $('input[title=autohide]:checked', qr)))) === "undefined" || _ref3 === null) ? undefined : _ref3.click(); - } else if (REPLY && getConfig('Persistent QR')) { - $('textarea', qr).value = ''; - $('input[name=recaptcha_response_field]', qr).value = ''; - submit = $('input[type=submit]', qr); - submit.value = 30; - submit.disabled = true; - window.setTimeout(cooldown, 1000); - auto = submit.previousSibling.lastChild; - if (auto.checked) { - (typeof (_ref3 = ((_ref2 = $('input[title=autohide]:checked', qr)))) === "undefined" || _ref3 === null) ? undefined : _ref3.click(); - } - } else { - remove(qr); - } - return recaptchaReload(); - }; - submit = function(e) { - var _ref2, _ref3, recaptcha, span; - if (span = this.nextSibling) { - remove(span); - } - recaptcha = $('input[name=recaptcha_response_field]', this); - if (recaptcha.value) { - return (typeof (_ref3 = ((_ref2 = $('#qr input[title=autohide]:not(:checked)')))) === "undefined" || _ref3 === null) ? undefined : _ref3.click(); - } else { - e.preventDefault(); - span = n('span', { - className: 'error', - textContent: 'You forgot to type in the verification.' - }); - addTo(this.parentNode, span); - alert('You forgot to type in the verification.'); - return recaptcha.focus(); - } - }; - autohide = function() { - var klass, qr; - qr = $('#qr'); - klass = qr.className; - if (klass.indexOf('auto') === -1) { - klass += ' auto'; - } else { - klass = klass.replace(' auto', ''); - } - return (qr.className = klass); - }; - quickReply = function(e) { - var _i, _len, _ref2, _ref3, auto, autoBox, autohideB, clone, closeB, form, input, qr, script, selection, text, textarea, titlebar, xpath; - if (!(qr = $('#qr'))) { - qr = AEOS.makeDialog('qr', 'topleft'); - titlebar = n('div', { - innerHTML: 'Quick Reply ', - className: 'move', - listener: ['mousedown', AEOS.move] - }); - addTo(qr, titlebar); - autohideB = n('input', { - type: 'checkbox', - className: 'pointer', - title: 'autohide', - listener: ['click', autohide] - }); - closeB = n('a', { - textContent: 'X', - className: 'pointer', - title: 'close', - listener: ['click', close] - }); - addTo(titlebar, autohideB, tn(' '), closeB); - form = $('form[name=post]'); - clone = form.cloneNode(true); - _ref2 = $$('script', clone); - for (_i = 0, _len = _ref2.length; _i < _len; _i++) { - script = _ref2[_i]; - remove(script); - } - $('input[name=recaptcha_response_field]', clone).addEventListener('keydown', recaptchaListener, true); - clone.addEventListener('submit', submit, true); - clone.target = 'iframe'; - if (!REPLY) { - xpath = 'preceding::span[@class="postername"][1]/preceding::input[1]'; - input = n('input', { - type: 'hidden', - name: 'resto', - value: x(xpath, this).name - }); - addTo(clone, input); - } else if (getConfig('Persistent QR')) { - submit = $('input[type=submit]', clone); - auto = n('label', { - textContent: 'Auto' - }); - autoBox = n('input', { - type: 'checkbox' - }); - addTo(auto, autoBox); - inBefore(submit, auto); - } - addTo(qr, clone); - addTo(d.body, qr); - } - if (e) { - e.preventDefault(); - (typeof (_ref3 = ((_ref2 = $('input[title=autohide]:checked', qr)))) === "undefined" || _ref3 === null) ? undefined : _ref3.click(); - selection = window.getSelection(); - id = (typeof (_ref3 = ((_ref2 = x('preceding::span[@id][1]', selection.anchorNode)))) === "undefined" || _ref3 === null) ? undefined : _ref3.id; - text = selection.toString(); - textarea = $('textarea', qr); - textarea.focus(); - textarea.value += '>>' + this.parentNode.id.match(/\d+$/)[0] + '\n'; - return text && id === this.parentNode.id ? textarea.value += (">" + (text) + "\n") : null; - } - }; - watch = function() { - var text; - id = this.nextSibling.name; - if (this.src[0] === 'd') { - this.src = favNormal; - text = ("/" + (BOARD) + "/ - ") + x('following-sibling::blockquote', this).textContent.slice(0, 25); - watched[BOARD] || (watched[BOARD] = []); - watched[BOARD].push({ - id: id, - text: text - }); - } else { - this.src = favEmpty; - watched[BOARD] = slice(watched[BOARD], id); - } - GM_setValue('watched', JSON.stringify(watched)); - return watcherUpdate(); - }; - watchX = function() { - var _ref2, input; - _ref2 = this.nextElementSibling.getAttribute('href').substring(1).split('/'); - board = _ref2[0]; - _ = _ref2[1]; - id = _ref2[2]; - watched[board] = slice(watched[board], id); - GM_setValue('watched', JSON.stringify(watched)); - watcherUpdate(); - if (input = $("input[name=\"" + (id) + "\"]")) { - favicon = input.previousSibling; - return (favicon.src = favEmpty); - } - }; - watcherUpdate = function() { - var _i, _j, _len, _ref2, _ref3, a, board, div, link, old, thread; - div = n('div'); - _ref2 = watched; - for (board in _ref2) { - if (!__hasProp.call(_ref2, board)) continue; - _i = _ref2[board]; - _ref3 = watched[board]; - for (_j = 0, _len = _ref3.length; _j < _len; _j++) { - thread = _ref3[_j]; - a = n('a', { - textContent: 'X', - className: 'pointer', - listener: ['click', watchX] - }); - link = n('a', { - textContent: thread.text, - href: ("/" + (board) + "/res/" + (thread.id)) - }); - addTo(div, a, tn(' '), link, n('br')); - } - } - old = $('#watcher div:last-child'); - return replace(old, div); - }; - parseResponse = function(responseText) { - var body, opbq, replies; - body = n('body', { - innerHTML: responseText - }); - replies = $$('td.reply', body); - opbq = $('blockquote', body); - return [replies, opbq]; - }; - onloadThread = function(responseText, span) { - var _i, _len, _ref2, _result, div, next, opbq, replies, reply; - _ref2 = parseResponse(responseText); - replies = _ref2[0]; - opbq = _ref2[1]; - span.textContent = span.textContent.replace('X Loading...', '- '); - span.previousSibling.innerHTML = opbq.innerHTML; - while ((next = span.nextSibling) && !next.clear) { - remove(next); - } - if (next) { - _result = []; _ref2 = replies; - for (_i = 0, _len = _ref2.length; _i < _len; _i++) { - reply = _ref2[_i]; - _result.push(inBefore(next, x('ancestor::table', reply))); - } - return _result; - } else { - div = span.parentNode; - _result = []; _ref2 = replies; - for (_i = 0, _len = _ref2.length; _i < _len; _i++) { - reply = _ref2[_i]; - _result.push(addTo(div, x('ancestor::table', reply))); - } - return _result; - } - }; - expandThread = function() { - var _i, _len, _ref2, num, prev, span, table, xhr; - id = x('preceding-sibling::input[1]', this).name; - span = this; - if (span.textContent[0] === '-') { - num = board === 'b' ? 3 : 5; - table = x("following::br[@clear][1]/preceding::table[" + (num) + "]", span); - while ((prev = table.previousSibling) && (prev.nodeName === 'TABLE')) { - remove(prev); - } - span.textContent = span.textContent.replace('-', '+'); - return null; - } - span.textContent = span.textContent.replace('+', 'X Loading...'); - _ref2 = xhrs; - for (_i = 0, _len = _ref2.length; _i < _len; _i++) { - xhr = _ref2[_i]; - if (xhr.id === id) { - onloadThread(xhr.r.responseText, span); - return null; - } - } - r = new XMLHttpRequest(); - r.onload = function() { - return onloadThread(this.responseText, span); - }; - r.open('GET', "res/" + (id), true); - r.send(); - return xhrs.push({ - r: r, - id: id - }); - }; - onloadComment = function(responseText, a, href) { - var _i, _len, _ref2, bq, op, opbq, replies, reply; - _ref2 = href.match(/(\d+)#(\d+)/); - _ = _ref2[0]; - op = _ref2[1]; - id = _ref2[2]; - _ref2 = parseResponse(responseText); - replies = _ref2[0]; - opbq = _ref2[1]; - if (id === op) { - html = opbq.innerHTML; - } else { - _ref2 = replies; - for (_i = 0, _len = _ref2.length; _i < _len; _i++) { - reply = _ref2[_i]; - if (reply.id === id) { - html = $('blockquote', reply).innerHTML; - } - } - } - bq = x('ancestor::blockquote', a); - return (bq.innerHTML = html); - }; - expandComment = function(e) { - var a, href; - e.preventDefault(); - a = this; - href = a.getAttribute('href'); - r = new XMLHttpRequest(); - r.onload = function() { - return onloadComment(this.responseText, a, href); - }; - r.open('GET', href, true); - r.send(); - return xhrs.push({ - r: r, - id: href.match(/\d+/)[0] - }); - }; - report = function() { - var input; - input = x('preceding-sibling::input[1]', this); - input.click(); - $('input[value="Report"]').click(); - return input.click(); - }; - nodeInserted = function(e) { - var _i, _len, _ref2, _result, callback, qr, target; - target = e.target; - if (target.nodeName === 'TABLE') { - _result = []; _ref2 = callbacks; - for (_i = 0, _len = _ref2.length; _i < _len; _i++) { - callback = _ref2[_i]; - _result.push(callback(target)); - } - return _result; - } else if (target.id === 'recaptcha_challenge_field' && (qr = $('#qr'))) { - $('#recaptcha_image img', qr).src = "http://www.google.com/recaptcha/api/image?c=" + target.value; - return ($('#recaptcha_challenge_field', qr).value = target.value); - } - }; - autoWatch = function() { - var autoText; - autoText = $('textarea', this).value.slice(0, 25); - return GM_setValue('autoText', "/" + (BOARD) + "/ - " + (autoText)); - }; - stopPropagation = function(e) { - return e.stopPropagation(); - }; - replyNav = function() { - var direction, op; - if (REPLY) { - return (window.location = this.textContent === '▲' ? '#navtop' : '#navbot'); - } else { - direction = this.textContent === '▲' ? 'preceding' : 'following'; - op = x("" + (direction) + "::span[starts-with(@id, 'nothread')][1]", this).id; - return (window.location = ("#" + (op))); - } - }; - recaptchaReload = function() { - return (window.location = 'javascript:Recaptcha.reload()'); - }; - recaptchaListener = function(e) { - return e.keyCode === 8 && this.value === '' ? recaptchaReload() : null; - }; - redirect = function() { - var url; - switch (BOARD) { - case 'a': - case 'g': - case 'lit': - case 'sci': - case 'tv': - url = ("http://green-oval.net/cgi-board.pl/" + (BOARD) + "/thread/" + (THREAD_ID) + "#p"); - break; - case 'cgl': - case 'jp': - case 'm': - case 'tg': - url = ("http://archive.easymodo.net/cgi-board.pl/" + (BOARD) + "/thread/" + (THREAD_ID) + "#p"); - break; - default: - url = ("http://boards.4chan.org/" + (BOARD)); - } - return (location.href = url); - }; + AEOS.init(); if (navtopr = $('#navtopr a')) { text = navtopr.nextSibling; a = n('a', {