'
- for option, value of config
- description = value[1]
- checked = if getConfig option then "checked" else ""
- html += "
'
+ for option, value of config
+ description = value[1]
+ checked = if getConfig option then "checked" else ""
+ html += "
"
+ html += "
"
+ html += "
"
+ html += "
"
- div = new Dialog('options', 'center', html).el
+ div = new Dialog('options', 'center', html).el
- for input in $$ 'input[type="checkbox"]', div
- input.addEventListener 'change', changeCheckbox, true
- $('a.sauce', div).addEventListener 'click', editSauce, true
- $('textarea', div).addEventListener 'change', changeValue, true
- $('input[type="button"]', div).addEventListener 'click', clearHidden, true
- mv div, d.body
+ for input in $$ 'input[type="checkbox"]', div
+ input.addEventListener 'change', changeCheckbox, true
+ $('a.sauce', div).addEventListener 'click', editSauce, true
+ $('textarea', div).addEventListener 'change', changeValue, true
+ $('input[type="button"]', div).addEventListener 'click', clearHidden, true
+ mv div, d.body
parseResponse = (responseText) ->
- body = n 'body',
- innerHTML: responseText
- replies = $$ 'td.reply', body
- opbq = $ 'blockquote', body
- return [replies, opbq]
+ body = n 'body',
+ innerHTML: responseText
+ replies = $$ 'td.reply', body
+ opbq = $ 'blockquote', body
+ return [replies, opbq]
qrListener = (e) ->
- e.preventDefault()
- link = e.target
- text = qrText link
- quickReply link, text
+ e.preventDefault()
+ link = e.target
+ text = qrText link
+ quickReply link, text
qrText = (link) ->
- #we can't just use textContent b/c of the xxxs. goddamit moot.
- text = '>>' + link.parentNode.id.match(/\d+$/)[0] + '\n'
+ #we can't just use textContent b/c of the xxxs. goddamit moot.
+ text = '>>' + link.parentNode.id.match(/\d+$/)[0] + '\n'
- selection = window.getSelection()
- id = x('preceding::span[@id][1]', selection.anchorNode)?.id
- if (s = selection.toString()) and (id is link.parentNode.id)
- text += ">#{s}"
+ selection = window.getSelection()
+ id = x('preceding::span[@id][1]', selection.anchorNode)?.id
+ if (s = selection.toString()) and (id is link.parentNode.id)
+ text += ">#{s}"
- text
+ text
quickReply = (link, text) ->
- unless qr = $ '#qr'
- html = "
"
- qr = new Dialog('qr', 'topleft', html).el
+ unless qr = $ '#qr'
+ html = "
"
+ qr = new Dialog('qr', 'topleft', html).el
- form = $ 'form[name=post]'
- clone = form.cloneNode true
- #remove recaptcha scripts
- for script in $$ 'script', clone
- rm script
- m $('input[name=recaptcha_response_field]', clone),
- listener: ['keydown', recaptchaListener]
- m clone,
- listener: ['submit', formSubmit]
- target: 'iframe'
- if not g.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, link).name
- mv input, clone
- else if getConfig 'Persistent QR'
- submit = $ 'input[type=submit]', clone
- auto = n 'label',
- textContent: 'Auto'
- autoBox = n 'input',
- type: 'checkbox'
- mv autoBox, auto
- inBefore submit, auto
- mv clone, qr
- mv qr, d.body
+ form = $ 'form[name=post]'
+ clone = form.cloneNode true
+ #remove recaptcha scripts
+ for script in $$ 'script', clone
+ rm script
+ m $('input[name=recaptcha_response_field]', clone),
+ listener: ['keydown', recaptchaListener]
+ m clone,
+ listener: ['submit', formSubmit]
+ target: 'iframe'
+ if not g.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, link).name
+ mv input, clone
+ else if getConfig 'Persistent QR'
+ submit = $ 'input[type=submit]', clone
+ auto = n 'label',
+ textContent: 'Auto'
+ autoBox = n 'input',
+ type: 'checkbox'
+ mv autoBox, auto
+ inBefore submit, auto
+ mv clone, qr
+ mv qr, d.body
- $('input[title=autohide]:checked', qr)?.click()
- textarea = $('textarea', qr)
- textarea.focus()
- if text then textarea.value += text
+ $('input[title=autohide]:checked', qr)?.click()
+ textarea = $('textarea', qr)
+ textarea.focus()
+ if text then textarea.value += text
recaptchaListener = (e) ->
- if e.keyCode is 8 and @value is ''
- recaptchaReload()
+ if e.keyCode is 8 and @value is ''
+ recaptchaReload()
recaptchaReload = ->
- window.location = 'javascript:Recaptcha.reload()'
+ window.location = 'javascript:Recaptcha.reload()'
redirect = ->
- switch g.BOARD
- when 'a', 'g', 'lit', 'sci', 'tv'
- url = "http://green-oval.net/cgi-board.pl/#{g.BOARD}/thread/#{g.THREAD_ID}"
- when 'cgl', 'jp', 'm', 'tg'
- url = "http://archive.easymodo.net/cgi-board.pl/#{g.BOARD}/thread/#{g.THREAD_ID}"
- when '3', 'adv', 'an', 'c', 'ck', 'co', 'fa', 'fit', 'int', 'k', 'mu', 'n', 'o', 'p', 'po', 'soc', 'sp', 'toy', 'trv', 'v', 'vp', 'x'
- url = "http://archive.no-ip.org/#{g.BOARD}/thread/#{g.THREAD_ID}"
- else
- url = "http://boards.4chan.org/#{g.BOARD}"
- location.href = url
+ switch g.BOARD
+ when 'a', 'g', 'lit', 'sci', 'tv'
+ url = "http://green-oval.net/cgi-board.pl/#{g.BOARD}/thread/#{g.THREAD_ID}"
+ when 'cgl', 'jp', 'm', 'tg'
+ url = "http://archive.easymodo.net/cgi-board.pl/#{g.BOARD}/thread/#{g.THREAD_ID}"
+ when '3', 'adv', 'an', 'c', 'ck', 'co', 'fa', 'fit', 'int', 'k', 'mu', 'n', 'o', 'p', 'po', 'soc', 'sp', 'toy', 'trv', 'v', 'vp', 'x'
+ url = "http://archive.no-ip.org/#{g.BOARD}/thread/#{g.THREAD_ID}"
+ else
+ url = "http://boards.4chan.org/#{g.BOARD}"
+ location.href = url
replyNav = ->
- if g.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}"
+ if g.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()
+ input = x('preceding-sibling::input[1]', this)
+ input.click()
+ $('input[value="Report"]').click()
+ input.click()
scrollThread = (count) ->
- [thread, idx] = getThread()
- top = thread.getBoundingClientRect().top
- if idx is 0 and top > 1
- #we haven't scrolled to the first thread
- idx = -1
- if count < 0 and top < -1
- #we've started scrolling past this thread,
- # but now want to read from the beginning
- count++
- temp = idx + count
- if temp < 0
- hash = ''
- else if temp > 9
- hash = 'p9'
- else
- hash = "p#{temp}"
- location.hash = hash
+ [thread, idx] = getThread()
+ top = thread.getBoundingClientRect().top
+ if idx is 0 and top > 1
+ #we haven't scrolled to the first thread
+ idx = -1
+ if count < 0 and top < -1
+ #we've started scrolling past this thread,
+ # but now want to read from the beginning
+ count++
+ temp = idx + count
+ if temp < 0
+ hash = ''
+ else if temp > 9
+ hash = 'p9'
+ else
+ hash = "p#{temp}"
+ location.hash = hash
showReply = ->
- div = @parentNode
- table = div.nextSibling
- show table
- rm div
- id = $('td.reply, td.replyhl', table).id
- slice g.hiddenReplies, id
- GM_setValue "hiddenReplies/#{g.BOARD}/", JSON.stringify(g.hiddenReplies)
+ div = @parentNode
+ table = div.nextSibling
+ show table
+ rm div
+ id = $('td.reply, td.replyhl', table).id
+ slice g.hiddenReplies, id
+ GM_setValue "hiddenReplies/#{g.BOARD}/", JSON.stringify(g.hiddenReplies)
showThread = ->
- div = @nextSibling
- show div
- hide this
- id = div.id
- slice g.hiddenThreads, id
- GM_setValue("hiddenThreads/#{g.BOARD}/", JSON.stringify(g.hiddenThreads))
+ div = @nextSibling
+ show div
+ hide this
+ id = div.id
+ slice g.hiddenThreads, id
+ GM_setValue("hiddenThreads/#{g.BOARD}/", JSON.stringify(g.hiddenThreads))
stopPropagation = (e) ->
- e.stopPropagation()
+ e.stopPropagation()
threadF = (current) ->
- div = n 'div',
- className: 'thread'
- a = n 'a',
- textContent: '[ - ]'
- className: 'pointer'
- listener: ['click', hideThread]
- mv a, div
- inBefore current, div
- while (!current.clear)#
- mv current, div
- current = div.nextSibling
+ div = n 'div',
+ className: 'thread'
+ a = n 'a',
+ textContent: '[ - ]'
+ className: 'pointer'
+ listener: ['click', hideThread]
+ mv a, div
+ inBefore current, div
+ while (!current.clear)#
mv current, div
current = div.nextSibling
- id = $('input[value="delete"]', div).name
- div.id = id
- #check if we should hide the thread
- for hidden in g.hiddenThreads
- if id == hidden.id
- hideThread(div)
- current = current.nextSibling.nextSibling
- if current.nodeName isnt 'CENTER'
- threadF(current)
+ mv current, div
+ current = div.nextSibling
+ id = $('input[value="delete"]', div).name
+ div.id = id
+ #check if we should hide the thread
+ for hidden in g.hiddenThreads
+ if id == hidden.id
+ hideThread(div)
+ current = current.nextSibling.nextSibling
+ if current.nodeName isnt 'CENTER'
+ threadF(current)
request = (url, callback) ->
- r = new XMLHttpRequest()
- r.onload = callback
- r.open 'get', url, true
- r.send()
- r
+ r = new XMLHttpRequest()
+ r.onload = callback
+ r.open 'get', url, true
+ r.send()
+ r
updateCallback = ->
- count = $ '#updater #count'
- timer = $ '#updater #timer'
- if @status is 404
- count.textContent = 404
- count.className = 'error'
- timer.textContent = ''
- clearInterval g.interval
- for input in $$ 'input[type=submit]'
- input.disabled = true
- input.value = 404
- s = ''
- if getConfig 'Unread Count' then s += "(#{g.replies.length}) "
- s += "/#{g.BOARD}/ - 404"
- d.title = s
- g.dead = true
- updateFavicon()
- return
- body = n 'body', innerHTML: @responseText
- replies = $$ 'td.reply', body
+ count = $ '#updater #count'
+ timer = $ '#updater #timer'
+ if @status is 404
+ count.textContent = 404
+ count.className = 'error'
+ timer.textContent = ''
+ clearInterval g.interval
+ for input in $$ 'input[type=submit]'
+ input.disabled = true
+ input.value = 404
+ s = ''
+ if getConfig 'Unread Count' then s += "(#{g.replies.length}) "
+ s += "/#{g.BOARD}/ - 404"
+ d.title = s
+ g.dead = true
+ updateFavicon()
+ return
+ body = n 'body', innerHTML: @responseText
+ replies = $$ 'td.reply', body
- root = $('br[clear]')
- if reply = $ 'td.reply, td.replyhl', root.previousElementSibling
- id = Number reply.id
- else
- id = 0
+ root = $('br[clear]')
+ if reply = $ 'td.reply, td.replyhl', root.previousElementSibling
+ id = Number reply.id
+ else
+ id = 0
- arr = []
- while (reply = replies.pop()) and (Number reply.id > id)
- arr.push reply
+ arr = []
+ while (reply = replies.pop()) and (Number reply.id > id)
+ arr.push reply
- if g.verbose
- l = arr.length
- count.textContent = "+#{l}"
- count.className = if l > 0 then 'new' else ''
+ if g.verbose
+ l = arr.length
+ count.textContent = "+#{l}"
+ count.className = if l > 0 then 'new' else ''
- #insert replies in order, so backlinks resolve
- while reply = arr.pop()
- table = x 'ancestor::table', reply
- inBefore root, table
+ #insert replies in order, so backlinks resolve
+ while reply = arr.pop()
+ table = x 'ancestor::table', reply
+ inBefore root, table
- timer.textContent = -1 * GM_getValue 'Interval', 10
+ timer.textContent = -1 * GM_getValue 'Interval', 10
updateFavicon = ->
- len = g.replies.length
- if g.dead
- if len > 0
- href = g.favDeadHalo
- else
- href = g.favDead
+ len = g.replies.length
+ if g.dead
+ if len > 0
+ href = g.favDeadHalo
else
- if len > 0
- href = g.favHalo
- else
- href = g.favDefault
- favicon = $ 'link[rel="shortcut icon"]', d
- clone = favicon.cloneNode true
- clone.href = href
- replace favicon, clone
+ href = g.favDead
+ else
+ if len > 0
+ href = g.favHalo
+ else
+ href = g.favDefault
+ favicon = $ 'link[rel="shortcut icon"]', d
+ clone = favicon.cloneNode true
+ clone.href = href
+ replace favicon, clone
updateTime = ->
- span = $ '#updater #timer'
- time = Number span.textContent
- if ++time is 0
- updateNow()
- else if time > 10
- time = 0
- g.req.abort()
- updateNow()
- if g.verbose
- count = $ '#updater #count'
- count.textContent = 'retry'
- count.className = ''
- else
- span.textContent = time
+ span = $ '#updater #timer'
+ time = Number span.textContent
+ if ++time is 0
+ updateNow()
+ else if time > 10
+ time = 0
+ g.req.abort()
+ updateNow()
+ if g.verbose
+ count = $ '#updater #count'
+ count.textContent = 'retry'
+ count.className = ''
+ else
+ span.textContent = time
updateTitle = ->
- len = g.replies.length
- d.title = d.title.replace /\d+/, len
- updateFavicon()
+ len = g.replies.length
+ d.title = d.title.replace /\d+/, len
+ updateFavicon()
updateAuto = ->
- span = $ '#updater #timer'
- if @checked
- span.textContent = -1 * GM_getValue 'Interval', 10
- g.interval = window.setInterval updateTime, 1000
- else
- span.textContent = 'Thread Updater'
- clearInterval g.interval
+ span = $ '#updater #timer'
+ if @checked
+ span.textContent = -1 * GM_getValue 'Interval', 10
+ g.interval = window.setInterval updateTime, 1000
+ else
+ span.textContent = 'Thread Updater'
+ clearInterval g.interval
updateInterval = ->
- unless num = Number @value
- num = 10
- @value = num
- GM_setValue 'Interval', num
+ unless num = Number @value
+ num = 10
+ @value = num
+ GM_setValue 'Interval', num
- span = $ '#updater #timer'
- if 0 > Number span.textContent
- span.textContent = -1 * num
+ span = $ '#updater #timer'
+ if 0 > Number span.textContent
+ span.textContent = -1 * num
updateNow = ->
- url = location.href + '?' + new Date().getTime() # fool the cache
- g.req = request url, updateCallback
- $("#updater #timer").textContent = 0
+ url = location.href + '?' + new Date().getTime() # fool the cache
+ g.req = request url, updateCallback
+ $("#updater #timer").textContent = 0
updateVerbose = ->
- g.verbose = @checked
- timer = $ '#updater #timer'
- if @checked
- timer.hidden = false
- else
- timer.hidden = true
- $("#updater #count").textContent = 'Thread Updater'
+ g.verbose = @checked
+ timer = $ '#updater #timer'
+ if @checked
+ timer.hidden = false
+ else
+ timer.hidden = true
+ $("#updater #count").textContent = 'Thread Updater'
updaterMake = ->
- html = "
Thread Updater
"
- html += "
"
- html += "
"
- html += "
"
- html += "
"
- html += "
"
- div = new Dialog('updater', 'topright', html).el
+ html = "
Thread Updater
"
+ html += "
"
+ html += "
"
+ html += "
"
+ html += "
"
+ html += "
"
+ div = new Dialog('updater', 'topright', html).el
- for input in $$ 'input[type=checkbox]', div
- input.addEventListener 'click', changeCheckbox, true
- name = input.name
- if name is 'autoL'
- input.checked = GM_getValue 'autoG', true
- else
- input.checked = GM_getValue name, true
- switch name
- when 'autoL'
- input.addEventListener 'click', updateAuto, true
- when 'verbose'
- input.addEventListener 'click', updateVerbose, true
+ for input in $$ 'input[type=checkbox]', div
+ input.addEventListener 'click', changeCheckbox, true
+ name = input.name
+ if name is 'autoL'
+ input.checked = GM_getValue 'autoG', true
+ else
+ input.checked = GM_getValue name, true
+ switch name
+ when 'autoL'
+ input.addEventListener 'click', updateAuto, true
+ when 'verbose'
+ input.addEventListener 'click', updateVerbose, true
- unless g.verbose = GM_getValue 'verbose', true
- $("#timer", div).hidden = true
+ unless g.verbose = GM_getValue 'verbose', true
+ $("#timer", div).hidden = true
- interval = $ 'input[name=interval]', div
- interval.value = GM_getValue 'Interval', 10
- interval.addEventListener 'change', updateInterval, true
+ interval = $ 'input[name=interval]', div
+ interval.value = GM_getValue 'Interval', 10
+ interval.addEventListener 'change', updateInterval, true
- $('input[type=button]', div).addEventListener 'click', updateNow, true
+ $('input[type=button]', div).addEventListener 'click', updateNow, true
- d.body.appendChild div
+ d.body.appendChild div
- if GM_getValue 'autoG' then updateAuto.call $("input[name=autoL]", div)
+ if GM_getValue 'autoG' then updateAuto.call $("input[name=autoL]", div)
watch = ->
- id = @nextSibling.name
- if @src is g.favEmpty
- @src = g.favDefault
- text = "/#{g.BOARD}/ - " +
- x('following-sibling::blockquote', this).textContent.slice(0,25)
- g.watched[g.BOARD] or= []
- g.watched[g.BOARD].push {
- id: id,
- text: text
- }
- else
- @src = g.favEmpty
- g.watched[g.BOARD] = slice(g.watched[g.BOARD], id)
- GM_setValue('watched', JSON.stringify(g.watched))
- watcherUpdate()
+ id = @nextSibling.name
+ if @src is g.favEmpty
+ @src = g.favDefault
+ text = "/#{g.BOARD}/ - " +
+ x('following-sibling::blockquote', this).textContent.slice(0,25)
+ g.watched[g.BOARD] or= []
+ g.watched[g.BOARD].push {
+ id: id,
+ text: text
+ }
+ else
+ @src = g.favEmpty
+ g.watched[g.BOARD] = slice(g.watched[g.BOARD], id)
+ GM_setValue('watched', JSON.stringify(g.watched))
+ watcherUpdate()
watcherUpdate = ->
- div = n 'div'
- for board of g.watched
- for thread in g.watched[board]
- a = n 'a',
- textContent: 'X'
- className: 'pointer'
- listener: ['click', watchX]
- link = n 'a',
- textContent: thread.text
- href: "/#{board}/res/#{thread.id}"
- mv a, tn(' '), link, n('br'), div
- old = $('#watcher div:last-child')
- replace(old, div)
+ div = n 'div'
+ for board of g.watched
+ for thread in g.watched[board]
+ a = n 'a',
+ textContent: 'X'
+ className: 'pointer'
+ listener: ['click', watchX]
+ link = n 'a',
+ textContent: thread.text
+ href: "/#{board}/res/#{thread.id}"
+ mv a, tn(' '), link, n('br'), div
+ old = $('#watcher div:last-child')
+ replace(old, div)
watchX = ->
- [board, _, id] = @nextElementSibling.
- getAttribute('href').substring(1).split('/')
- g.watched[board] = slice(g.watched[board], id)
- GM_setValue('watched', JSON.stringify(g.watched))
- watcherUpdate()
- if input = $("input[name=\"#{id}\"]")
- favicon = input.previousSibling
- favicon.src = g.favEmpty
+ [board, _, id] = @nextElementSibling.
+ getAttribute('href').substring(1).split('/')
+ g.watched[board] = slice(g.watched[board], id)
+ GM_setValue('watched', JSON.stringify(g.watched))
+ watcherUpdate()
+ if input = $("input[name=\"#{id}\"]")
+ favicon = input.previousSibling
+ favicon.src = g.favEmpty
#main
g =
- callbacks: []
- expand: false
- favDead: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAgMAAABinRfyAAAACVBMVEUAAAAAAAD/AAA9+90tAAAAAXRSTlMAQObYZgAAADtJREFUCB0FwUERxEAIALDszMG730PNSkBEBSECoU0AEPe0mly5NWprRUcDQAdn68qtkVsj3/84z++CD5u7CsnoBJoaAAAAAElFTkSuQmCC'
- favDeadHalo: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAWUlEQVR4XrWSAQoAIAgD/f+njSApsTqjGoTQ5oGWPJMOOs60CzsWwIwz1I4PUIYh+WYEMGQ6I/txw91kP4oA9BdwhKp1My4xQq6e8Q9ANgDJjOErewFiNesV2uGSfGv1/HYAAAAASUVORK5CYII='
- favDefault: $('link[rel="shortcut icon"]', d)?.href or '' #no favicon in `post successful` page
- favEmpty: 'http://static.4chan.org/image/favicon-dis.ico'
- flavors: [
- 'http://regex.info/exif.cgi?url='
- 'http://iqdb.org/?url='
- 'http://saucenao.com/search.php?db=999&url='
- 'http://tineye.com/search?url='
- ].join '\n'
- iframe: false
- watched: JSON.parse(GM_getValue('watched', '{}'))
- xhrs: []
+ callbacks: []
+ expand: false
+ favDead: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAgMAAABinRfyAAAACVBMVEUAAAAAAAD/AAA9+90tAAAAAXRSTlMAQObYZgAAADtJREFUCB0FwUERxEAIALDszMG730PNSkBEBSECoU0AEPe0mly5NWprRUcDQAdn68qtkVsj3/84z++CD5u7CsnoBJoaAAAAAElFTkSuQmCC'
+ favDeadHalo: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAWUlEQVR4XrWSAQoAIAgD/f+njSApsTqjGoTQ5oGWPJMOOs60CzsWwIwz1I4PUIYh+WYEMGQ6I/txw91kP4oA9BdwhKp1My4xQq6e8Q9ANgDJjOErewFiNesV2uGSfGv1/HYAAAAASUVORK5CYII='
+ favDefault: $('link[rel="shortcut icon"]', d)?.href or '' #no favicon in `post successful` page
+ favEmpty: 'http://static.4chan.org/image/favicon-dis.ico'
+ flavors: [
+ 'http://regex.info/exif.cgi?url='
+ 'http://iqdb.org/?url='
+ 'http://saucenao.com/search.php?db=999&url='
+ 'http://tineye.com/search?url='
+ ].join '\n'
+ iframe: false
+ watched: JSON.parse(GM_getValue('watched', '{}'))
+ xhrs: []
g.favHalo = if /ws/.test g.favDefault then 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAZklEQVR4XrWRQQoAIQwD+6L97j7Ih9WTQQxhDqJQCk4Mranuvqod6LgwawSqSuUmWSPw/UNlJlnDAmA2ARjABLYj8ZyCzJHHqOg+GdAKZmKPIQUzuYrxicHqEgHzP9g7M0+hj45sAnRWxtPj3zSPAAAAAElFTkSuQmCC' else 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAgMAAABinRfyAAAADFBMVEUAAABmzDP///8AAABet0i+AAAAAXRSTlMAQObYZgAAAExJREFUeF4tyrENgDAMAMFXKuQswQLBG3mOlBnFS1gwDfIYLpEivvjq2MlqjmYvYg5jWEzCwtDSQlwcXKCVLrpFbvLvvSf9uZJ2HusDtJAY7Tkn1oYAAAAASUVORK5CYII='
pathname = location.pathname.substring(1).split('/')
[g.BOARD, temp] = pathname
if temp is 'res'
- g.REPLY = temp
- g.THREAD_ID = pathname[2]
+ g.REPLY = temp
+ g.THREAD_ID = pathname[2]
else
- g.PAGENUM = parseInt(temp) || 0
+ g.PAGENUM = parseInt(temp) || 0
g.hiddenThreads = JSON.parse(GM_getValue("hiddenThreads/#{g.BOARD}/", '[]'))
g.hiddenReplies = JSON.parse(GM_getValue("hiddenReplies/#{g.BOARD}/", '[]'))
tzOffset = (new Date()).getTimezoneOffset() / 60
@@ -1026,407 +1026,407 @@ tzOffset = (new Date()).getTimezoneOffset() / 60
g.chanOffset = 5 - tzOffset# 4chan = EST = GMT -5
if location.hostname.split('.')[0] is 'sys'
- if recaptcha = $ '#recaptcha_response_field'
- m recaptcha, listener: ['keydown', recaptchaListener]
- else if b = $ 'table font b'
- GM_setValue 'error', b.firstChild.textContent
- else
- GM_setValue 'error', ''
- if getConfig 'Auto Watch'
- html = $('b').innerHTML
- [_, thread, id] = html.match(//)
- if thread is '0'
- board = $('meta', d).content.match(/4chan.org\/(\w+)\//)[1]
- g.watched[board] or= []
- g.watched[board].push {
- id: id,
- text: GM_getValue 'autoText'
- }
- GM_setValue 'watched', JSON.stringify g.watched
- return
+ if recaptcha = $ '#recaptcha_response_field'
+ m recaptcha, listener: ['keydown', recaptchaListener]
+ else if b = $ 'table font b'
+ GM_setValue 'error', b.firstChild.textContent
+ else
+ GM_setValue 'error', ''
+ if getConfig 'Auto Watch'
+ html = $('b').innerHTML
+ [_, thread, id] = html.match(//)
+ if thread is '0'
+ board = $('meta', d).content.match(/4chan.org\/(\w+)\//)[1]
+ g.watched[board] or= []
+ g.watched[board].push {
+ id: id,
+ text: GM_getValue 'autoText'
+ }
+ GM_setValue 'watched', JSON.stringify g.watched
+ return
lastChecked = GM_getValue('lastChecked', 0)
now = getTime()
DAY = 24 * 60 * 60
if lastChecked < now - 1*DAY
- cutoff = now - 7*DAY
- while g.hiddenThreads.length
- if g.hiddenThreads[0].timestamp > cutoff
- break
- g.hiddenThreads.shift()
+ cutoff = now - 7*DAY
+ while g.hiddenThreads.length
+ if g.hiddenThreads[0].timestamp > cutoff
+ break
+ g.hiddenThreads.shift()
- while g.hiddenReplies.length
- if g.hiddenReplies[0].timestamp > cutoff
- break
- g.hiddenReplies.shift()
+ while g.hiddenReplies.length
+ if g.hiddenReplies[0].timestamp > cutoff
+ break
+ g.hiddenReplies.shift()
- GM_setValue("hiddenThreads/#{g.BOARD}/", JSON.stringify(g.hiddenThreads))
- GM_setValue("hiddenReplies/#{g.BOARD}/", JSON.stringify(g.hiddenReplies))
- GM_setValue('lastChecked', now)
+ GM_setValue("hiddenThreads/#{g.BOARD}/", JSON.stringify(g.hiddenThreads))
+ GM_setValue("hiddenReplies/#{g.BOARD}/", JSON.stringify(g.hiddenReplies))
+ GM_setValue('lastChecked', now)
GM_addStyle '
- #options textarea {
- height: 100px;
- width: 500px;
- }
- #updater {
- position: fixed;
- text-align: right;
- }
- #updater input[type=text] {
- width: 50px;
- }
- #updater:not(:hover) {
- border: none;
- background: transparent;
- }
- #updater:not(:hover) > div:not(.move) {
- display: none;
- }
- #watcher {
- position: absolute;
- }
- #watcher > div.move {
- text-decoration: underline;
- padding: 5px 5px 0 5px;
- }
- #watcher > div:last-child {
- padding: 0 5px 5px 5px;
- }
- span.error {
- color: red;
- }
- #qr.auto:not(:hover) form {
- visibility: collapse;
- }
- #qr span.error {
- position: absolute;
- bottom: 0;
- left: 0;
- }
- #qr {
- position: fixed;
- }
- #qr > div {
- text-align: right;
- }
- #qr > form > div, /* ad */
- #qr td.rules {
- display: none;
- }
- #options {
- position: fixed;
- padding: 5px;
- text-align: right;
- }
- form[name=delform] a img {
- border: 0px;
- float: left;
- margin: 0px 20px;
- }
- span.navlinks {
- position: absolute;
- right: 5px;
- }
- span.navlinks > a {
- font-size: 16px;
- text-decoration: none;
- }
- .hide {
- display: none;
- }
- .new {
- background: lime;
- }
+ #options textarea {
+ height: 100px;
+ width: 500px;
+ }
+ #updater {
+ position: fixed;
+ text-align: right;
+ }
+ #updater input[type=text] {
+ width: 50px;
+ }
+ #updater:not(:hover) {
+ border: none;
+ background: transparent;
+ }
+ #updater:not(:hover) > div:not(.move) {
+ display: none;
+ }
+ #watcher {
+ position: absolute;
+ }
+ #watcher > div.move {
+ text-decoration: underline;
+ padding: 5px 5px 0 5px;
+ }
+ #watcher > div:last-child {
+ padding: 0 5px 5px 5px;
+ }
+ span.error {
+ color: red;
+ }
+ #qr.auto:not(:hover) form {
+ visibility: collapse;
+ }
+ #qr span.error {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ }
+ #qr {
+ position: fixed;
+ }
+ #qr > div {
+ text-align: right;
+ }
+ #qr > form > div, /* ad */
+ #qr td.rules {
+ display: none;
+ }
+ #options {
+ position: fixed;
+ padding: 5px;
+ text-align: right;
+ }
+ form[name=delform] a img {
+ border: 0px;
+ float: left;
+ margin: 0px 20px;
+ }
+ span.navlinks {
+ position: absolute;
+ right: 5px;
+ }
+ span.navlinks > a {
+ font-size: 16px;
+ text-decoration: none;
+ }
+ .hide {
+ display: none;
+ }
+ .new {
+ background: lime;
+ }
'
if navtopr = $ '#navtopr a'
- text = navtopr.nextSibling #css doesn't see text nodes
- a = n 'a',
- textContent: 'X'
- className: 'pointer'
- listener: ['click', options]
- inBefore text, tn(' / ')
- inBefore text, a
- navbotr = $ '#navbotr a'
- text = navbotr.nextSibling
- a = n 'a',
- textContent: 'X'
- className: 'pointer'
- listener: ['click', options]
- inBefore text, tn(' / ')
- inBefore text, a
+ text = navtopr.nextSibling #css doesn't see text nodes
+ a = n 'a',
+ textContent: 'X'
+ className: 'pointer'
+ listener: ['click', options]
+ inBefore text, tn(' / ')
+ inBefore text, a
+ navbotr = $ '#navbotr a'
+ text = navbotr.nextSibling
+ a = n 'a',
+ textContent: 'X'
+ className: 'pointer'
+ listener: ['click', options]
+ inBefore text, tn(' / ')
+ inBefore text, a
else if getConfig('404 Redirect') and d.title is '4chan - 404'
- redirect()
+ redirect()
else
- return
+ return
#hack to tab from comment straight to recaptcha
for el in $$ '#recaptcha_table a'
- el.tabIndex = 1
+ el.tabIndex = 1
recaptcha = $ '#recaptcha_response_field'
recaptcha.addEventListener('keydown', recaptchaListener, true)
scroll = ->
- height = d.body.clientHeight
- for reply, i in g.replies
- bottom = reply.getBoundingClientRect().bottom
- if bottom > height #post is not completely read
- break
- if i is 0 then return
- g.replies = g.replies[i..]
- updateTitle()
+ height = d.body.clientHeight
+ for reply, i in g.replies
+ bottom = reply.getBoundingClientRect().bottom
+ if bottom > height #post is not completely read
+ break
+ if i is 0 then return
+ g.replies = g.replies[i..]
+ updateTitle()
#major features
if (getConfig 'Restore IDs') and g.BOARD in ['b', 'v']
- g.callbacks.push (root) ->
- quotes = $$ 'a.quotejs:not(:first-child)', root
- for quote in quotes
- quote.textContent = quote.parentNode.id.match(/\d+$/)[0]
+ g.callbacks.push (root) ->
+ quotes = $$ 'a.quotejs:not(:first-child)', root
+ for quote in quotes
+ quote.textContent = quote.parentNode.id.match(/\d+$/)[0]
if getConfig 'Image Expansion'
- delform = $ 'form[name=delform]'
- expand = n 'div',
- innerHTML:
- "
-
"
- imageType = GM_getValue 'imageType', 'full'
- for option in $$("option", expand)
- if option.textContent is imageType
- option.selected = true
- break
- $("select", expand).addEventListener 'change', changeValue, true
- $("select", expand).addEventListener 'change', imageTypeChange, true
- $("input", expand).addEventListener 'click', imageExpandClick, true
- inBefore delform.firstChild, expand
+ delform = $ 'form[name=delform]'
+ expand = n 'div',
+ innerHTML:
+ "
+
"
+ imageType = GM_getValue 'imageType', 'full'
+ for option in $$("option", expand)
+ if option.textContent is imageType
+ option.selected = true
+ break
+ $("select", expand).addEventListener 'change', changeValue, true
+ $("select", expand).addEventListener 'change', imageTypeChange, true
+ $("input", expand).addEventListener 'click', imageExpandClick, true
+ inBefore delform.firstChild, expand
- g.callbacks.push (root) ->
- thumbs = $$ 'img[md5]', root
- for thumb in thumbs
- thumb.parentNode.addEventListener 'click', imageClick, true
- if g.expand then imageToggle thumb.parentNode
+ g.callbacks.push (root) ->
+ thumbs = $$ 'img[md5]', root
+ for thumb in thumbs
+ thumb.parentNode.addEventListener 'click', imageClick, true
+ if g.expand then imageToggle thumb.parentNode
if getConfig 'Localize Time'
- g.callbacks.push (root) ->
- spans = $$ 'span[id^=no]', root
- for span in spans
- s = span.previousSibling
- [_, month, day, year, hour, min_sec] =
- s.textContent.match /(\d+)\/(\d+)\/(\d+)\(\w+\)(\d+):(\S+)/
- year = "20#{year}"
- month -= 1 #months start at 0
- hour = g.chanOffset + Number hour
- date = new Date year, month, day, hour
- year = date.getFullYear() - 2000
- month = zeroPad date.getMonth() + 1
- day = zeroPad date.getDate()
- hour = zeroPad date.getHours()
- dotw = [
- 'Sun'
- 'Mon'
- 'Tue'
- 'Wed'
- 'Thu'
- 'Fri'
- 'Sat'
- ][date.getDay()]
- s.textContent = " #{month}/#{day}/#{year}(#{dotw})#{hour}:#{min_sec} "
+ g.callbacks.push (root) ->
+ spans = $$ 'span[id^=no]', root
+ for span in spans
+ s = span.previousSibling
+ [_, month, day, year, hour, min_sec] =
+ s.textContent.match /(\d+)\/(\d+)\/(\d+)\(\w+\)(\d+):(\S+)/
+ year = "20#{year}"
+ month -= 1 #months start at 0
+ hour = g.chanOffset + Number hour
+ date = new Date year, month, day, hour
+ year = date.getFullYear() - 2000
+ month = zeroPad date.getMonth() + 1
+ day = zeroPad date.getDate()
+ hour = zeroPad date.getHours()
+ dotw = [
+ 'Sun'
+ 'Mon'
+ 'Tue'
+ 'Wed'
+ 'Thu'
+ 'Fri'
+ 'Sat'
+ ][date.getDay()]
+ s.textContent = " #{month}/#{day}/#{year}(#{dotw})#{hour}:#{min_sec} "
if getConfig 'Sauce'
- g.callbacks.push (root) ->
- spans = $$ 'span.filesize', root
- prefixes = GM_getValue('flavors', g.flavors).split '\n'
- names = (prefix.match(/(\w+)\./)[1] for prefix in prefixes)
- for span in spans
- suffix = $('a', span).href
- i = 0; l = names.length
- while i < l
- link = n 'a',
- textContent: names[i]
- href: prefixes[i] + suffix
- mv tn(' '), link, span
- i++
+ g.callbacks.push (root) ->
+ spans = $$ 'span.filesize', root
+ prefixes = GM_getValue('flavors', g.flavors).split '\n'
+ names = (prefix.match(/(\w+)\./)[1] for prefix in prefixes)
+ for span in spans
+ suffix = $('a', span).href
+ i = 0; l = names.length
+ while i < l
+ link = n 'a',
+ textContent: names[i]
+ href: prefixes[i] + suffix
+ mv tn(' '), link, span
+ i++
if getConfig 'Reply Hiding'
- g.callbacks.push (root) ->
- tds = $$('td.doubledash', root)
- for td in tds
- a = n 'a',
- textContent: '[ - ]'
- className: 'pointer'
- listener: ['click', hideReply]
- replace(td.firstChild, a)
+ g.callbacks.push (root) ->
+ tds = $$('td.doubledash', root)
+ for td in tds
+ a = n 'a',
+ textContent: '[ - ]'
+ className: 'pointer'
+ listener: ['click', hideReply]
+ replace(td.firstChild, a)
- next = td.nextSibling
- id = next.id
- for obj in g.hiddenReplies
- if obj.id is id
- hideReply(next)
+ next = td.nextSibling
+ id = next.id
+ for obj in g.hiddenReplies
+ if obj.id is id
+ hideReply(next)
if getConfig 'Quick Reply'
- iframe = n 'iframe',
- name: 'iframe'
- listener: ['load', iframeLoad]
- hide(iframe)
- mv iframe, d.body
+ iframe = n 'iframe',
+ name: 'iframe'
+ listener: ['load', iframeLoad]
+ hide(iframe)
+ mv iframe, d.body
- g.callbacks.push (root) ->
- quotes = $$('a.quotejs:not(:first-child)', root)
- for quote in quotes
- quote.addEventListener('click', qrListener, true)
+ g.callbacks.push (root) ->
+ quotes = $$('a.quotejs:not(:first-child)', root)
+ for quote in quotes
+ quote.addEventListener('click', qrListener, true)
- #hack - nuke id so it doesn't grab focus when reloading
- recaptcha.id = ''
+ #hack - nuke id so it doesn't grab focus when reloading
+ recaptcha.id = ''
if getConfig 'Quick Report'
- g.callbacks.push (root) ->
- arr = $$('span[id^=no]', root)
- for el in arr
- a = n 'a',
- textContent: '[ ! ]'
- className: 'pointer'
- listener: ['click', report]
- inAfter el, a
- inAfter el, tn(' ')
+ g.callbacks.push (root) ->
+ arr = $$('span[id^=no]', root)
+ for el in arr
+ a = n 'a',
+ textContent: '[ ! ]'
+ className: 'pointer'
+ listener: ['click', report]
+ inAfter el, a
+ inAfter el, tn(' ')
if getConfig 'Thread Watcher'
- #create watcher
- html = '
Thread Watcher
'
- watcher = new Dialog('watcher', 'topleft', html).el
- mv watcher, d.body
- watcherUpdate()
+ #create watcher
+ html = '
Thread Watcher
'
+ watcher = new Dialog('watcher', 'topleft', html).el
+ mv watcher, d.body
+ watcherUpdate()
- #add buttons
- threads = g.watched[g.BOARD] || []
- #normal, threading
- inputs = $$('form > input[value="delete"], div > input[value="delete"]')
- for input in inputs
- id = input.name
- src = (->
- for thread in threads
- if id is thread.id
- return g.favDefault
- g.favEmpty
- )()
- img = n 'img',
- src: src
- className: 'pointer'
- listener: ['click', watch]
- inBefore input, img
+ #add buttons
+ threads = g.watched[g.BOARD] || []
+ #normal, threading
+ inputs = $$('form > input[value="delete"], div > input[value="delete"]')
+ for input in inputs
+ id = input.name
+ src = (->
+ for thread in threads
+ if id is thread.id
+ return g.favDefault
+ g.favEmpty
+ )()
+ img = n 'img',
+ src: src
+ className: 'pointer'
+ listener: ['click', watch]
+ inBefore input, img
if getConfig 'Anonymize'
- g.callbacks.push (root) ->
- names = $$('span.postername, span.commentpostername', root)
- for name in names
- name.innerHTML = 'Anonymous'
- trips = $$('span.postertrip', root)
- for trip in trips
- if trip.parentNode.nodeName is 'A'
- rm trip.parentNode
- else
- rm trip
+ g.callbacks.push (root) ->
+ names = $$('span.postername, span.commentpostername', root)
+ for name in names
+ name.innerHTML = 'Anonymous'
+ trips = $$('span.postertrip', root)
+ for trip in trips
+ if trip.parentNode.nodeName is 'A'
+ rm trip.parentNode
+ else
+ rm trip
if getConfig 'Reply Navigation'
- g.callbacks.push (root) ->
- arr = $$('span[id^=norep]', root)
- for el in arr
- span = n 'span'
- up = n 'a',
- textContent: '▲'
- className: 'pointer'
- listener: ['click', replyNav]
- down = n 'a',
- textContent: '▼'
- className: 'pointer'
- listener: ['click', replyNav]
- mv tn(' '), up, tn(' '), down, span
- inAfter el, span
+ g.callbacks.push (root) ->
+ arr = $$('span[id^=norep]', root)
+ for el in arr
+ span = n 'span'
+ up = n 'a',
+ textContent: '▲'
+ className: 'pointer'
+ listener: ['click', replyNav]
+ down = n 'a',
+ textContent: '▼'
+ className: 'pointer'
+ listener: ['click', replyNav]
+ mv tn(' '), up, tn(' '), down, span
+ inAfter el, span
if getConfig 'Keybinds'
- d.addEventListener 'keydown', keydown, true
- d.addEventListener 'keypress', keypress, true
+ d.addEventListener 'keydown', keydown, true
+ d.addEventListener 'keypress', keypress, true
if g.REPLY
- if getConfig 'Thread Updater'
- updaterMake()
- if getConfig('Quick Reply') and getConfig 'Persistent QR'
- quickReply()
- $('#qr input[title=autohide]').click()
- if getConfig 'Post in Title'
- unless text = $('span.filetitle').textContent
- text = $('blockquote').textContent
- if text
- d.title = "/#{g.BOARD}/ - #{text}"
- if getConfig 'Unread Count'
- g.replies = []
- d.title = '(0) ' + d.title
- d.addEventListener 'scroll', scroll, true
- g.callbacks.push (root) ->
- g.replies = g.replies.concat $$ 'td.reply, td.replyhl', root
- updateTitle()
+ if getConfig 'Thread Updater'
+ updaterMake()
+ if getConfig('Quick Reply') and getConfig 'Persistent QR'
+ quickReply()
+ $('#qr input[title=autohide]').click()
+ if getConfig 'Post in Title'
+ unless text = $('span.filetitle').textContent
+ text = $('blockquote').textContent
+ if text
+ d.title = "/#{g.BOARD}/ - #{text}"
+ if getConfig 'Unread Count'
+ g.replies = []
+ d.title = '(0) ' + d.title
+ d.addEventListener 'scroll', scroll, true
+ g.callbacks.push (root) ->
+ g.replies = g.replies.concat $$ 'td.reply, td.replyhl', root
+ updateTitle()
else #not reply
- if getConfig 'Thread Hiding'
- delform = $('form[name=delform]')
- start = $ 'form[name=delform] > *'
- start = start.nextSibling if getConfig 'Image Expansion' #skip over image expansion dialog
- #don't confuse other scripts
- d.addEventListener('DOMNodeInserted', stopPropagation, true)
- threadF start
- d.removeEventListener('DOMNodeInserted', stopPropagation, true)
+ if getConfig 'Thread Hiding'
+ delform = $('form[name=delform]')
+ start = $ 'form[name=delform] > *'
+ start = start.nextSibling if getConfig 'Image Expansion' #skip over image expansion dialog
+ #don't confuse other scripts
+ d.addEventListener('DOMNodeInserted', stopPropagation, true)
+ threadF start
+ d.removeEventListener('DOMNodeInserted', stopPropagation, true)
- if getConfig 'Auto Watch'
- $('form[name="post"]').addEventListener('submit', autoWatch, true)
+ if getConfig 'Auto Watch'
+ $('form[name="post"]').addEventListener('submit', autoWatch, true)
- if getConfig 'Thread Navigation'
- arr = $$ 'div > span.filesize, form > span.filesize'
- l1 = arr.length - 1
- for el, i in arr
- span = n 'span',
- className: 'navlinks'
- id: 'p' + i
- if i
- textContent = '▲'
- href = "#p#{i - 1}"
- else if g.PAGENUM
- textContent = '◀'
- href = "#{g.PAGENUM - 1}#p0"
- else
- textContent = '▲'
- href = "#navtop"
- up = n 'a',
- className: 'pointer'
- textContent: textContent
- href: href
- if i < l1
- textContent = '▼'
- href = "#p#{i + 1}"
- else
- textContent = '▶'
- href = "#{g.PAGENUM + 1}#p0"
- down = n 'a',
- className: 'pointer'
- textContent: textContent
- href: href
- mv up, tn(' '), down, span
- inBefore el, span
- if location.hash is '#p0'
- window.location = window.location
+ if getConfig 'Thread Navigation'
+ arr = $$ 'div > span.filesize, form > span.filesize'
+ l1 = arr.length - 1
+ for el, i in arr
+ span = n 'span',
+ className: 'navlinks'
+ id: 'p' + i
+ if i
+ textContent = '▲'
+ href = "#p#{i - 1}"
+ else if g.PAGENUM
+ textContent = '◀'
+ href = "#{g.PAGENUM - 1}#p0"
+ else
+ textContent = '▲'
+ href = "#navtop"
+ up = n 'a',
+ className: 'pointer'
+ textContent: textContent
+ href: href
+ if i < l1
+ textContent = '▼'
+ href = "#p#{i + 1}"
+ else
+ textContent = '▶'
+ href = "#{g.PAGENUM + 1}#p0"
+ down = n 'a',
+ className: 'pointer'
+ textContent: textContent
+ href: href
+ mv up, tn(' '), down, span
+ inBefore el, span
+ if location.hash is '#p0'
+ window.location = window.location
- if getConfig 'Thread Expansion'
- omitted = $$('span.omittedposts')
- for span in omitted
- a = n 'a',
- className: 'pointer omittedposts'
- textContent: "+ #{span.textContent}"
- listener: ['click', expandThread]
- replace(span, a)
+ if getConfig 'Thread Expansion'
+ omitted = $$('span.omittedposts')
+ for span in omitted
+ a = n 'a',
+ className: 'pointer omittedposts'
+ textContent: "+ #{span.textContent}"
+ listener: ['click', expandThread]
+ replace(span, a)
- if getConfig 'Comment Expansion'
- as = $$('span.abbr a')
- for a in as
- a.addEventListener('click', expandComment, true)
+ if getConfig 'Comment Expansion'
+ as = $$('span.abbr a')
+ for a in as
+ a.addEventListener('click', expandComment, true)
callback() for callback in g.callbacks
d.body.addEventListener('DOMNodeInserted', nodeInserted, true)