/b/ackwash fork'd

By aeosynth Last update Sep 16, 2009 — Installed 5,294 times. Daily Installs: 16, 10, 9, 11, 16, 25, 15, 6, 14, 11, 26, 19, 16, 19, 10, 16, 21, 13, 16, 19, 17, 15, 21, 47, 21, 22, 24, 22, 57, 13, 14, 19

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'
	}
}