There are 28 previous versions of this script.
// ==UserScript==
// @name /b/ackwash fork'd
// @description Adds tooltips to 4chan quote links; inline quoting. Forked from /b/ackwash inlined.
// @namespace http://userscripts.org/users/64431
// @attribution Todd Kirby, http://userscripts.org/scripts/show/39393
// @version 4.8.0
// @include http://*.4chan.org/*
// @include http://4chanarchive.org/brchive/*
// @include http://suptg.thisisnotatrueending.com/archive/*
// @include http://archive.easymodo.net/cgi-board.pl/*
// @copyright 2009, James Campos
// @license GPL version 3 or any later version; http://www.gnu.org/copyleft/gpl.html
// ==/UserScript==
//PREFERENCES
const fix_Pseud0ch = true//force a grey background on text-boards
const inline = 1//0 = default behavior, 1 = inline hidden quotes, 2 = inline all quotes
//use CSS to style quotes of class 'forwardlink' (visible when hovering over backlinks - see Reply Backlinker http://userscripts.org/scripts/show/53573).
//END PREFERENCES
//define
function x (xpath, root) {
if (!root) root = document.body
return document.evaluate(xpath, root, null, XPathResult.ANY_UNORDERED_NODE_TYPE, null).singleNodeValue
}
function X (xpath, root) {
if (!root) root = document.body
var result = document.evaluate(xpath, root, null, XPathResult.UNORDERED_NODE_ITERATOR_TYPE, null)
var a = [], item
while (item = result.iterateNext())
a.push (item)
return a
}
//main
var screen_height, screen_width
const TIP_X_OFFSET = 45, TIP_Y_OFFSET = 120, FLIPPER = 400
const op = window.location.search.match(/\d{4,}/) || window.location.pathname.match(/S?\d{4,}/)//{4,}: hack for non-page-0 bug. 4 b/c 4chanarchive goes to hundreds. S for easymodo.
const server = window.location.hostname.match(/\w*/)[0]
const tip = document.createElement('div')
tip.id = 'backwash_tooltip'
tip.setAttribute('style', 'display: none; position:fixed; border:1px solid #AAA;')
tip.style.backgroundColor = (server == 'dis' && fix_Pseud0ch) ? '#F0F0F0' : 'inherit'
tip.innerHTML = '<table><tr><td style="padding: 10px" id="backwash_tipcell"></td></tr></table>'
const bwtd = tip.getElementsByTagName('td')[0]
wash(document.body)
document.body.appendChild(tip)
document.body.addEventListener('DOMNodeInserted', function(e) {if (e.target.nodeName == 'TABLE') wash(e.target)}, true)//work on expanded/updated posts
//funks
function wash(el, root) {
var quotes = X(".//a[starts-with(text(), '>>')]", el)
for (var i in quotes)
washSingle(quotes[i], root)
}
function washSingle(el, root) {
var id = el.href.match(/\w*\d$/)
if (!id) return
if (root)
el.href = root + id
if (op) {//FIXME >>>/G/
if (id == op[0])
var type = 'op'
else if (op != (el.search ? el.search.match(/\d{3,}/)[0] : el.pathname.match(/\/(S?\d{3,}(?!\/\d+\.html))/)[1]) )//(?!\/\d) for suptg
if (/\d$/.test(el.innerHTML))//nested quotes. serious business.
el.innerHTML += ' (Duckroll?)'
} else {
var temp = x("preceding::span[@class='postername'][1]/following::span[@id]", el)
if (temp && (id == temp.id.match(/\d+$/)[0]) )
type = 'op'
}
if (type == 'op') {
if (/\d$/.test(el.innerHTML))
el.innerHTML+=' (OP)'
el.addEventListener('mouseover', function() {show(this, id, server == 'archive' ? null : 'op')}, true)
} else
el.addEventListener('mouseover', function() {show(this, id)}, true)
el.addEventListener('mousemove', track, true)
el.addEventListener('mouseout', function() {tip.style.display = 'none'}, true)
if (inline)
el.addEventListener('click', function(e) {e.preventDefault(); show(this, id, type, true)}, true)
else
el.addEventListener('click', function() {tip.style.display = 'none'}, true)
}
function show(el, id, type, click) {
screen_height = Math.min(document.documentElement.clientHeight, document.body.clientHeight)
screen_width = document.body.clientWidth
if (click) {
var temp = el.nextSibling
if (temp && temp.nodeName == 'TABLE')
return temp.style.display = temp.style.display ? '' : 'none'
else {
var table = document.createElement('table')
table.innerHTML = '<tr><td></td></tr>'
var td = table.getElementsByTagName('td')[0]
table.style.border = '1px dashed'
}
} else
td = bwtd
if (type == 'op') {
var tempRE = new RegExp('<a.*?name="' + id + '[^_][\\s\\S]*?<\/blockquote>', 'i')//[^_] for 4chanarchive. [\s\S] instead of [^] b/c lol opera
temp = document.body.innerHTML.match(tempRE)
td.innerHTML = temp ? temp : '(OP)'
} else if (server == 'dis') {
if (temp = x("ancestor::div[@class='thread']//span[@class='postnum']/a[text()='" + id + "']/ancestor::*[self::div or self::table][1]", el))
td.innerHTML = temp.innerHTML
else
type = 'load'
} else {
temp = document.getElementById(id)
if (temp) {
td.innerHTML = temp.innerHTML
var reply = true
} else
type = 'load'
}
td.innerHTML = td.innerHTML.replace(/<input.*?>/i, '')
if (el.className == 'backlink') {//highlight backlink generating quotes
var quotes = X(".//a[contains(text(), '>>" + el.parentNode.id + "')]", td)
for (var i in quotes)
quotes[i].className = 'forwardlink'
}
if (type == 'load') {
temp = server == 'suptg' ? el.href.replace(/(^.*archive\/)\d+\/(\w+).*$/, '$1$2') : el.href
load(id, temp, td)
td.innerHTML = 'Loading...'
if (click && inline)
return el.parentNode.insertBefore(table, el.nextSibling)
} else if (click) {
if (inline == 2)
return el.parentNode.insertBefore(table, el.nextSibling)
else
return window.location = el.href
}
td.className = reply ? 'replyhl' : ''
tip.style.display = ''
}
function load(id, url, td) {
if (!td)
td = bwtd
if (window.opera) {
var r = new XMLHttpRequest();
r.onreadystatechange = function() {
if (r.readyState == 4)
onload(r)
}
r.open('GET', url, true);
r.send(null);
} else
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: function(r) {onload(r)}
})
function onload(r) {
if (r.status == 200) {
if (server == 'suptg') url = url + '.html#' + id
if (/\?/.test(url) )//Cross-board link
load(id, r.responseText.match(/http:.*?html/)[0], td )
else if (url.indexOf(id + '.html') != -1) {//OP
var temp = new RegExp('<a.*?name="' + id + '[^_][\\s\\S]*?<\/blockquote>', 'i')
td.innerHTML = r.responseText.match(temp)
} else {
temp = server == 'dis' ? /(<span class="postnum">[\s\S]*?<\/blockquote>)/i : new RegExp('<td (?:class="reply" )?id="p?' + id + '.*?>([\\s\\S]*?)<\/td>', '')//(?:) for easymodo
try{td.innerHTML = r.responseText.match(temp)[1]}
catch(e) {td.innerHTML = 'Post does not exist'}
}
td.innerHTML = td.innerHTML.replace(/<input.*?>/, '')
wash(td, url.replace(/\w+$/, ''))//FIXME only works correctly if quotes are pointing to same thread
} else {
td.innerHTML = 'Error ' + r.status
if (r.status == 410)
td.innerHTML += '<br>Thread does not exist'
}
}
}
function track(event) {
const x = event.clientX
const y = event.clientY
const height = tip.offsetHeight
tip.style.top = y < TIP_Y_OFFSET || height > screen_height ? 0 :
y - TIP_Y_OFFSET + height > screen_height ? screen_height - height + 'px' :
y - TIP_Y_OFFSET + 'px'
if (x > screen_width - FLIPPER) {
tip.style.left = ''
tip.style.right = screen_width + TIP_X_OFFSET - x + 'px'
} else {
tip.style.right = ''
tip.style.left = x + TIP_X_OFFSET + 'px'
}
}
