attempt to add options to #navtopr at start, erroring out on failure. do this so we don't add the thread watcher and waste time in general.
835 lines
23 KiB
CoffeeScript
835 lines
23 KiB
CoffeeScript
#todo: remove close()?, make hiddenReplies/hiddenThreads local, comments, gc
|
|
#todo: remove stupid 'obj', arr el, make hidden an object, smarter xhr, text(), @this, images, clear hidden
|
|
#todo: watch - add board in updateWatcher?, redundant move divs?, redo css / hiding, manual clear
|
|
#todo: hotkeys? navlink at top?
|
|
#thread watching doesn't work in opera?
|
|
|
|
config: {
|
|
'Thread Hiding': true,
|
|
'Reply Hiding': true,
|
|
'Show Stubs': true,
|
|
'Thread Navigation': true,
|
|
'Reply Navigation': true,
|
|
'Thread Watcher': true,
|
|
'Thread Expansion': true,
|
|
'Comment Expansion': true,
|
|
'Quick Reply': true,
|
|
'Quick Report': true,
|
|
'Auto Watch': true,
|
|
'Anonymize': false,
|
|
}
|
|
getValue: (name) ->
|
|
GM_getValue(name, config[name])
|
|
x: (path, root) ->
|
|
root ||= document.body
|
|
document.
|
|
evaluate(path, root, null, XPathResult.ANY_UNORDERED_NODE_TYPE, null).
|
|
singleNodeValue
|
|
$: (selector, root) ->
|
|
root ||= document.body
|
|
root.querySelector(selector)
|
|
$$: (selector, root) ->
|
|
root ||= document.body
|
|
result: root.querySelectorAll(selector)
|
|
#magic that turns the results object into an array:
|
|
node for node in result
|
|
inBefore: (root, el) ->
|
|
root.parentNode.insertBefore(el, root)
|
|
inAfter: (root, el) ->
|
|
root.parentNode.insertBefore(el, root.nextSibling)
|
|
tag: (el) ->
|
|
document.createElement(el)
|
|
hide: (el) ->
|
|
el.style.display = 'none'
|
|
show: (el) ->
|
|
el.style.display = ''
|
|
remove: (el) ->
|
|
el.parentNode.removeChild(el)
|
|
replace: (root, el) ->
|
|
root.parentNode.replaceChild(el, root)
|
|
getTime: ->
|
|
Math.floor(new Date().getTime() / 1000)
|
|
slice: (arr, id) ->
|
|
# the while loop is the only low-level loop left in coffeescript.
|
|
# we need to use it to see the index.
|
|
i: 0
|
|
l: arr.length
|
|
while (i < l)
|
|
if id == arr[i].id
|
|
arr.splice(i, 1)
|
|
return arr
|
|
i++
|
|
position: (el) ->
|
|
id: el.id
|
|
if left: GM_getValue("${id}Left", '0px')
|
|
el.style.left: left
|
|
else
|
|
el.style.right: '0px'
|
|
if top: GM_getValue("${id}Top", '0px')
|
|
el.style.top: top
|
|
else
|
|
el.style.bottom: '0px'
|
|
|
|
|
|
if typeof GM_deleteValue == 'undefined'
|
|
this.GM_setValue: (name, value) ->
|
|
value: (typeof value)[0] + value
|
|
localStorage.setItem(name, value)
|
|
|
|
this.GM_getValue: (name, defaultValue) ->
|
|
if not value: localStorage.getItem(name)
|
|
return defaultValue
|
|
type: value[0]
|
|
value: value.substring(1)
|
|
switch type
|
|
when 'b'
|
|
return value == 'true'
|
|
when 'n'
|
|
return Number(value)
|
|
else
|
|
return value
|
|
|
|
this.GM_addStyle: (css) ->
|
|
style: tag('style')
|
|
style.type: 'text/css'
|
|
style.textContent: css
|
|
$('head', document).appendChild(style)
|
|
|
|
watched: JSON.parse(GM_getValue('watched', '{}'))
|
|
if location.hostname.split('.')[0] is 'sys'
|
|
if b: $('table font b')
|
|
GM_setValue('error', b.firstChild.textContent)
|
|
else
|
|
GM_setValue('error', '')
|
|
if GM_getValue('Auto Watch')
|
|
html: $('b').innerHTML
|
|
[nop, thread, id]: html.match(/<!-- thread:(\d+),no:(\d+) -->/)
|
|
if thread is '0'
|
|
board: $('meta', document).content.match(/4chan.org\/(\w+)\//)[1]
|
|
watched[board] ||= []
|
|
watched[board].push({
|
|
id: id,
|
|
text: GM_getValue('autoText')
|
|
})
|
|
GM_setValue('watched', JSON.stringify(watched))
|
|
return
|
|
|
|
[nop, BOARD, magic]: location.pathname.split('/')
|
|
if magic is 'res'
|
|
REPLY: magic
|
|
else
|
|
PAGENUM: parseInt(magic) || 0
|
|
xhrs: []
|
|
r: null
|
|
head: $('head', document)
|
|
iframeLoop: false
|
|
move: { }
|
|
callbacks: []
|
|
#godammit moot
|
|
head: $('head', document)
|
|
if not favicon: $('link[rel="shortcut icon"]', head)#/f/
|
|
favicon: tag('link')
|
|
favicon.rel: 'shortcut icon'
|
|
favicon.href: 'http://static.4chan.org/image/favicon.ico'
|
|
head.appendChild(favicon)
|
|
favNormal: favicon.href
|
|
favEmpty: 'data:image/gif;base64,R0lGODlhEAAQAJEAAAAAAP///9vb2////yH5BAEAAAMALAAAAAAQABAAAAIvnI+pq+D9DBAUoFkPFnbs7lFZKIJOJJ3MyraoB14jFpOcVMpzrnF3OKlZYsMWowAAOw=='
|
|
|
|
hiddenThreads: JSON.parse(GM_getValue("hiddenThreads/$BOARD/", '[]'))
|
|
hiddenReplies: JSON.parse(GM_getValue("hiddenReplies/$BOARD/", '[]'))
|
|
|
|
lastChecked: GM_getValue('lastChecked', 0)
|
|
now: getTime()
|
|
day: 24 * 60 * 60
|
|
if lastChecked < now - day
|
|
cutoff: now - 7*day
|
|
while hiddenThreads.length
|
|
if hiddenThreads[0].timestamp > cutoff
|
|
break
|
|
hiddenThreads.shift()
|
|
|
|
while hiddenReplies.length
|
|
if hiddenReplies[0].timestamp > cutoff
|
|
break
|
|
hiddenReplies.shift()
|
|
|
|
GM_setValue("hiddenThreads/$BOARD/", JSON.stringify(hiddenThreads))
|
|
GM_setValue("hiddenReplies/$BOARD/", JSON.stringify(hiddenReplies))
|
|
GM_setValue('lastChecked', now)
|
|
|
|
GM_addStyle('
|
|
#watcher {
|
|
position: absolute;
|
|
border: 1px solid;
|
|
}
|
|
#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 span.error {
|
|
position: absolute;
|
|
bottom: 0;
|
|
left: 0;
|
|
}
|
|
#qr {
|
|
position: fixed;
|
|
border: 1px solid;
|
|
}
|
|
#qr > div {
|
|
text-align: right;
|
|
}
|
|
#qr > form > div {/* ad */
|
|
display: none;
|
|
}
|
|
#qr tr:last-child {
|
|
display: none;
|
|
}
|
|
#options {
|
|
position: fixed;
|
|
border: 1px solid;
|
|
padding: 5px;
|
|
text-align: right;
|
|
}
|
|
span.navlinks {
|
|
position: absolute;
|
|
right: 5px;
|
|
}
|
|
span.navlinks > a {
|
|
font-size: 16px;
|
|
text-decoration: none;
|
|
}
|
|
.move {
|
|
cursor: move;
|
|
}
|
|
.pointer, #options label, #options a {
|
|
cursor: pointer;
|
|
}
|
|
')
|
|
|
|
|
|
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: []
|
|
|
|
|
|
options: ->
|
|
if div: $('#options')
|
|
remove(div)
|
|
else
|
|
hiddenNum: hiddenReplies.length + hiddenThreads.length
|
|
div: tag('div')
|
|
div.id: 'options'
|
|
div.className: 'reply'
|
|
position(div)
|
|
html: '<div class="move">4chan X</div><div>'
|
|
for option of config
|
|
checked: if getValue(option) then "checked" else ""
|
|
html += "<label>$option<input $checked name=\"$option\" type=\"checkbox\"></label><br>"
|
|
html += "<input type=\"button\" value=\"hidden: $hiddenNum\"><br>"
|
|
html += '<a name="save">save</a> <a name="cancel">cancel</a></div>'
|
|
div.innerHTML: html
|
|
$('div', div).addEventListener('mousedown', mousedown, true)
|
|
$('input[type="button"]', div).addEventListener('click', clearHidden, true)
|
|
$('a[name="save"]', div).addEventListener('click', optionsSave, true)
|
|
$('a[name="cancel"]', div).addEventListener('click', close, true)
|
|
document.body.appendChild(div)
|
|
|
|
|
|
mousedown: (e) ->
|
|
div: this.parentNode
|
|
move.div: div
|
|
move.clientX: e.clientX
|
|
move.clientY: e.clientY
|
|
move.bodyX: document.body.clientWidth
|
|
move.bodyY: document.body.clientHeight
|
|
|
|
# check if the string exists. parseInt('0px') is falsey.
|
|
l = div.style.left
|
|
move.divX: if l then parseInt(l) else move.bodyX - div.offsetWidth
|
|
t = div.style.top
|
|
move.divY: if t then parseInt(t) else move.bodyY - div.offsetHeight
|
|
window.addEventListener('mousemove', mousemove, true)
|
|
window.addEventListener('mouseup', mouseup, true)
|
|
|
|
|
|
mousemove: (e) ->
|
|
div: move.div
|
|
realX: move.divX + (e.clientX - move.clientX)# x + dx
|
|
left: if realX < 20 then 0 else realX
|
|
|
|
if move.bodyX - div.offsetWidth - realX < 20
|
|
div.style.left: ''
|
|
div.style.right: '0px'
|
|
else
|
|
div.style.left: left + 'px'
|
|
div.style.right: ''
|
|
|
|
realY: move.divY + (e.clientY - move.clientY)# y + dy
|
|
top: if realY < 20 then 0 else realY
|
|
|
|
if move.bodyY - div.offsetHeight - realY < 20
|
|
div.style.top: ''
|
|
div.style.bottom: '0px'
|
|
else
|
|
div.style.top: top + 'px'
|
|
div.style.bottom: ''
|
|
|
|
|
|
mouseup: ->
|
|
id: move.div.id
|
|
GM_setValue("${id}Left", move.div.style.left)
|
|
GM_setValue("${id}Top", move.div.style.top)
|
|
window.removeEventListener('mousemove', mousemove, true)
|
|
window.removeEventListener('mouseup', mouseup, true)
|
|
|
|
|
|
showThread: ->
|
|
div: this.nextSibling
|
|
show(div)
|
|
hide(this)
|
|
id: div.id
|
|
slice(hiddenThreads, id)
|
|
GM_setValue("hiddenThreads/$BOARD/", JSON.stringify(hiddenThreads))
|
|
|
|
|
|
hideThread: (div) ->
|
|
if p: this.parentNode
|
|
div: p
|
|
hiddenThreads.push({
|
|
id: div.id
|
|
timestamp: getTime()
|
|
})
|
|
GM_setValue("hiddenThreads/$BOARD/", JSON.stringify(hiddenThreads))
|
|
hide(div)
|
|
if getValue('Show Stubs')
|
|
a: tag('a')
|
|
if span: $('.omittedposts', div)
|
|
n: Number(span.textContent.match(/\d+/)[0])
|
|
else
|
|
n: 0
|
|
n += $$('table', div).length
|
|
text: if n is 1 then "1 reply" else "$n replies"
|
|
name: $('span.postername', div).textContent
|
|
trip: $('span.postername + span.postertrip', div)?.textContent || ''
|
|
a.textContent: "[ + ] $name$trip ($text)"
|
|
a.className: 'pointer'
|
|
a.addEventListener('click', showThread, true)
|
|
inBefore(div, a)
|
|
|
|
|
|
threadF: (current) ->
|
|
div: tag('div')
|
|
a: tag('a')
|
|
a.textContent: '[ - ]'
|
|
a.className: 'pointer'
|
|
a.addEventListener('click', hideThread, true)
|
|
div.appendChild(a)
|
|
|
|
inBefore(current, div)
|
|
while (!current.clear)#<br clear>
|
|
div.appendChild(current)
|
|
current: div.nextSibling
|
|
div.appendChild(current)
|
|
current: div.nextSibling
|
|
|
|
id: $('input', 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 getValue('Show Stubs')
|
|
a: tag('a')
|
|
a.textContent: "[ + ] $name $trip"
|
|
a.className: 'pointer'
|
|
a.addEventListener('click', showReply, true)
|
|
div: tag('div')
|
|
div.appendChild(a)
|
|
inBefore(table, div)
|
|
|
|
|
|
optionsSave: ->
|
|
div: this.parentNode.parentNode
|
|
inputs: $$('input', div)
|
|
for input in inputs
|
|
GM_setValue(input.name, input.checked)
|
|
remove(div)
|
|
|
|
|
|
close: ->
|
|
div: this.parentNode.parentNode
|
|
remove(div)
|
|
|
|
|
|
iframeLoad: ->
|
|
if iframeLoop: !iframeLoop
|
|
return
|
|
$('iframe').src: 'about:blank'
|
|
|
|
qr: $('#qr')
|
|
if error: GM_getValue('error')
|
|
$('form', qr).style.visibility: ''
|
|
span: tag('span')
|
|
span.textContent: error
|
|
span.className: 'error'
|
|
qr.appendChild(span)
|
|
else
|
|
remove(qr)
|
|
|
|
|
|
submit: ->
|
|
this.style.visibility: 'collapse'
|
|
if span: this.nextSibling
|
|
remove(span)
|
|
|
|
|
|
minimize: ->
|
|
form: this.parentNode.nextSibling
|
|
if form.style.visibility
|
|
form.style.visibility: ''
|
|
else
|
|
form.style.visibility: 'collapse'
|
|
|
|
|
|
quickReply: (e) ->
|
|
e.preventDefault()
|
|
if !qr: $('#qr')
|
|
qr: tag('div')
|
|
qr.id: 'qr'
|
|
qr.className: 'reply'
|
|
position(qr)
|
|
|
|
div: tag('div')
|
|
div.innerHTML: 'Quick Reply '
|
|
div.className: 'move'
|
|
div.addEventListener('mousedown', mousedown, true)
|
|
qr.appendChild(div)
|
|
|
|
a: tag('a')
|
|
a.textContent: '_'
|
|
a.className: 'pointer'
|
|
a.title: 'minimize'
|
|
a.addEventListener('click', minimize, true)
|
|
div.appendChild(a)
|
|
div.appendChild(document.createTextNode(' '))
|
|
a: tag('a')
|
|
a.textContent: 'X'
|
|
a.className: 'pointer'
|
|
a.title: 'close'
|
|
a.addEventListener('click', close, true)
|
|
div.appendChild(a)
|
|
|
|
clone: $('form[name="post"]').cloneNode(true)
|
|
clone.addEventListener('submit', submit, true)
|
|
clone.target: 'iframe'
|
|
if not REPLY
|
|
input: tag('input')
|
|
input.type: 'hidden'
|
|
input.name: 'resto'
|
|
xpath: 'preceding::span[@class="postername"][1]/preceding::input[1]'
|
|
input.value: x(xpath, this).name
|
|
clone.appendChild(input)
|
|
qr.appendChild(clone)
|
|
document.body.appendChild(qr)
|
|
|
|
textarea: $('textarea', qr)
|
|
#goddamit moot
|
|
#xx
|
|
textarea.value += '>>' + this.parentNode.id.match(/\d+$/)[0] + '\n'
|
|
selection: window.getSelection()
|
|
id: x('preceding::span[@id][1]', selection.anchorNode)?.id
|
|
if id is this.parentNode.id
|
|
if selText: selection.toString()
|
|
textarea.value += ">$selText\n"
|
|
textarea.focus()
|
|
|
|
|
|
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] ||= []
|
|
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: ->
|
|
[nop, board, nop, id]:
|
|
this.nextElementSibling.getAttribute('href').split('/')
|
|
watched[board]: slice(watched[board], id)
|
|
GM_setValue('watched', JSON.stringify(watched))
|
|
watcherUpdate()
|
|
if input: $("input[name=\"$id\"]")
|
|
img: input.previousSibling
|
|
img.src: favEmpty
|
|
|
|
|
|
watcherUpdate: ->
|
|
div: tag('div')
|
|
for board of watched
|
|
for thread in watched[board]
|
|
a: tag('a')
|
|
a.textContent: 'X'
|
|
a.className: 'pointer'
|
|
a.addEventListener('click', watchX, true)
|
|
div.appendChild(a)
|
|
div.appendChild(document.createTextNode(' '))
|
|
a: tag('a')
|
|
a.textContent: thread.text
|
|
a.href: "/$board/res/${thread.id}"
|
|
div.appendChild(a)
|
|
div.appendChild(tag('br'))
|
|
old: $('#watcher div:last-child')
|
|
replace(old, div)
|
|
|
|
|
|
parseResponse: (responseText) ->
|
|
body: tag('body')
|
|
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#<br clear>
|
|
remove(next)
|
|
if next
|
|
for reply in replies
|
|
inBefore(next, x('ancestor::table', reply))
|
|
else#threading
|
|
div: span.parentNode
|
|
for reply in replies
|
|
div.appendChild(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) ->
|
|
[nop, 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)
|
|
|
|
|
|
autoWatch: ->
|
|
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"
|
|
|
|
|
|
#error out if there's no #navtopr.
|
|
text: $('#navtopr a').nextSibling
|
|
a: tag('a')
|
|
a.textContent: 'X'
|
|
a.className: 'pointer'
|
|
a.addEventListener('click', options, true)
|
|
inBefore(text, document.createTextNode(' / '))
|
|
inBefore(text, a)
|
|
|
|
if getValue('Reply Hiding')
|
|
callbacks.push((root) ->
|
|
tds: $$('td.doubledash', root)
|
|
for td in tds
|
|
a: tag('a')
|
|
a.textContent: '[ - ]'
|
|
a.className: 'pointer'
|
|
a.addEventListener('click', hideReply, true)
|
|
replace(td.firstChild, a)
|
|
|
|
next: td.nextSibling
|
|
id: next.id
|
|
for obj in hiddenReplies
|
|
if obj.id is id
|
|
hideReply(next)
|
|
)
|
|
|
|
if getValue('Quick Reply')
|
|
iframe: tag('iframe')
|
|
hide(iframe)
|
|
iframe.name: 'iframe'
|
|
iframe.addEventListener('load', iframeLoad, true)
|
|
document.body.appendChild(iframe)
|
|
|
|
callbacks.push((root) ->
|
|
quotes: $$('a.quotejs:not(:first-child)', root)
|
|
for quote in quotes
|
|
quote.addEventListener('click', quickReply, true)
|
|
)
|
|
|
|
|
|
if getValue('Quick Report')
|
|
callbacks.push((root) ->
|
|
arr: $$('span[id^=no]', root)
|
|
for el in arr
|
|
a: tag('a')
|
|
a.textContent: '[ ! ]'
|
|
a.className: 'pointer'
|
|
a.addEventListener('click', report, true)
|
|
inAfter(el, a)
|
|
inAfter(el, document.createTextNode(' '))
|
|
)
|
|
|
|
if getValue('Thread Watcher')
|
|
#create watcher
|
|
watcher: tag('div')
|
|
watcher.innerHTML: '<div class="move">Thread Watcher</div><div></div>'
|
|
watcher.className: 'reply'
|
|
watcher.id: 'watcher'
|
|
position(watcher)
|
|
$('div', watcher).addEventListener('mousedown', mousedown, true)
|
|
document.body.appendChild(watcher)
|
|
watcherUpdate()
|
|
|
|
#add buttons
|
|
threads: watched[BOARD] || []
|
|
#normal, threading
|
|
inputs: $$('form > input[value="delete"], div > input[value="delete"]')
|
|
for input in inputs
|
|
img: tag('img')
|
|
id: input.name
|
|
for thread in threads
|
|
if id == thread.id
|
|
img.src: favNormal
|
|
break
|
|
img.src ||= favEmpty
|
|
img.className: 'pointer'
|
|
img.addEventListener('click', watch, true)
|
|
inBefore(input, img)
|
|
|
|
if getValue('Anonymize')
|
|
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'
|
|
remove(trip.parentNode)
|
|
else
|
|
remove(trip)
|
|
)
|
|
|
|
if getValue('Reply Navigation')
|
|
callbacks.push((root) ->
|
|
arr: $$('span[id^=norep]', root)
|
|
for el in arr
|
|
span: tag('span')
|
|
up: tag('a')
|
|
up.textContent: '▲'
|
|
up.className: 'pointer'
|
|
up.addEventListener('click', replyNav, true)
|
|
down: tag('a')
|
|
down.textContent: '▼'
|
|
down.className: 'pointer'
|
|
down.addEventListener('click', replyNav, true)
|
|
span.appendChild(document.createTextNode(' '))
|
|
span.appendChild(up)
|
|
span.appendChild(document.createTextNode(' '))
|
|
span.appendChild(down)
|
|
inAfter(el, span)
|
|
)
|
|
|
|
|
|
if not REPLY
|
|
if getValue('Thread Hiding')
|
|
delform = $('form[name=delform]')
|
|
document.addEventListener('DOMNodeInserted', stopPropagation, true)
|
|
threadF(delform.firstChild)
|
|
document.removeEventListener('DOMNodeInserted', stopPropagation, true)
|
|
|
|
if getValue('Auto Watch')
|
|
$('form[name="post"]').addEventListener('submit', autoWatch, true)
|
|
|
|
if getValue('Thread Navigation')
|
|
arr: $$('div > span.filesize, form > span.filesize')
|
|
i: 0
|
|
l: arr.length
|
|
l1: l + 1
|
|
#should this be a while loop?
|
|
for el in arr
|
|
up: tag('a')
|
|
up.className: 'pointer'
|
|
if i isnt 0
|
|
up.textContent: '▲'
|
|
up.href: "#$i"
|
|
else if PAGENUM isnt 0
|
|
up.textContent: '◀'
|
|
up.href: "${PAGENUM - 1}"
|
|
else
|
|
up.textContent: '▲'
|
|
up.href: "#navtop"
|
|
|
|
span: tag('span')
|
|
span.className: 'navlinks'
|
|
span.id: ++i
|
|
i1: i + 1
|
|
down: tag('a')
|
|
down.className: 'pointer'
|
|
span.appendChild(up)
|
|
span.appendChild(document.createTextNode(' '))
|
|
span.appendChild(down)
|
|
if i1 == l1
|
|
down.textContent: '▶'
|
|
down.href: "${PAGENUM + 1}#1"
|
|
else
|
|
down.textContent: '▼'
|
|
down.href: "#$i1"
|
|
inBefore(el, span)
|
|
if location.hash is '#1'
|
|
window.location: window.location
|
|
|
|
if getValue('Thread Expansion')
|
|
omitted: $$('span.omittedposts')
|
|
for span in omitted
|
|
a: tag('a')
|
|
a.className: 'pointer omittedposts'
|
|
a.textContent: "+ ${span.textContent}"
|
|
a.addEventListener('click', expandThread, true)
|
|
replace(span, a)
|
|
|
|
if getValue('Comment Expansion')
|
|
as: $$('span.abbr a')
|
|
for a in as
|
|
a.addEventListener('click', expandComment, true)
|
|
|
|
for callback in callbacks
|
|
callback()
|
|
document.body.addEventListener('DOMNodeInserted', nodeInserted, true)
|