There are 17 previous versions of this script.
// ==UserScript==
// @name ya4cie
// @namespace aeosynth
// @description Yet another 4chan image expander.
// @include http://*.4chan.org/*
// @include http://suptg.thisisnotatrueending.com/archive/*
// @include http://4chanarchive.org/brchive/*
// @version 1.0.1
// @copyright 2009, James Campos
// @license GPL version 3 or any later version; http://www.gnu.org/copyleft/gpl.html
// ==/UserScript==
//TODO
// instant feedback
// sanity checking
// enter to save
// wtf opera
(function () {
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
}
function tag (el) {
return document.createElement(el)
}
var images = X(".//img[@md5]/parent::*|.//span[@class='tn_reply' or @class='tn_thread']/parent::*")
if (!images.length) return
var inline = GM_getValue('Inline', true)
var auto_gif = GM_getValue('Load Gifs')
var click_full = GM_getValue('Click Full')
var click_fit = GM_getValue('Click Fit')
var in_filter = GM_getValue('In Filter')
var auto_expand = Number(GM_getValue('Auto'))//we're gonna have to Number() somewhere, and doing it here requires less user interaction for the bugfix.
var reduce = GM_getValue('Reduce', 0)
var maxWidth = GM_getValue('Max Width', 600)
const reply = /res|archive\/|dspl_thread/.test(window.location.pathname)
var controls = tag('span')
controls.innerHTML =
' <select><option>full</option><option>fit width</option><option>fit screen</option></select>\
<label style="cursor: pointer;">Expand Images<input type="checkbox"></label>'
var expandSize = controls.getElementsByTagName('select')[0]
if (GM_getValue('expandSize') == 'fit width')
expandSize.childNodes[1].selected = true
else if (GM_getValue('expandSize') == 'fit screen')
expandSize.childNodes[2].selected = true
var expandImages = controls.getElementsByTagName('input')[0]
if (auto_expand == 2 || auto_expand && reply) {
expandImages.checked = true
expandImagesF()
}
expandImages.addEventListener('click', expandImagesF, true)
function expandImagesF() {
if (expandImages.checked) {
for(i in images)
if (images[i].firstChild.style.display != 'none')
expandSingle(images[i])
} else
for(i in images)
if (images[i].firstChild.style.display == 'none')
expandSingle(images[i])
}
function expandSingle (anchor, click) {
var thumb = anchor.firstChild
var clientHeight = document.body.clientHeight
if (thumb.style.display) {
var expanded = anchor.lastChild
var width = expanded.width
var height = expanded.height
if (click && click_full && !expanded.getAttribute('Full') && expanded.getAttribute('width')) {
expanded.removeAttribute('width')
expanded.setAttribute('Full', true)
} else if (click && click_fit && !expanded.getAttribute('Fit') && height > clientHeight) {
if (!expanded.getAttribute('width'))
expanded.setAttribute('Full', true)
expanded.setAttribute('width', Math.floor(width * clientHeight / height))
expanded.setAttribute('Fit', true)
} else {//thumb down
thumb.style.display = ''
anchor.removeChild(expanded)
if (anchor.parentNode.nodeName != 'TD') {
anchor.parentNode.removeChild(anchor.previousSibling)
var checkbox = x("preceding-sibling::input[1]", anchor)
anchor.parentNode.insertBefore(anchor, checkbox)
}
if (thumb.nodeName == 'SPAN')
anchor.parentNode.removeChild(anchor.previousSibling)
}
} else {//Expand
thumb.style.display = 'none'
expanded = tag('img')
expanded.src = anchor
expanded.border = 0
var size = x("preceding-sibling::span[@class='filesize'][1]/text()[2]", anchor).textContent.match(/(\d+)x(\d+)/)
var width = size[1]
var clientWidth = document.body.clientWidth - reduce
if (expandSize.value == 'fit width') {
if (width > clientWidth)
expanded.setAttribute('width', clientWidth)
} else if (expandSize.value == 'fit screen') {
var height = size[2]
if (width > clientWidth || height > clientHeight) {
if (width/height < clientWidth/clientHeight)
expanded.setAttribute('width', Math.floor(width*clientHeight/height))
else
expanded.setAttribute('width', clientWidth)
}
}
anchor.appendChild(expanded)
if (anchor.parentNode.nodeName != 'TD') {
if (Number(width) <= maxWidth)
expanded.align = 'left'
var nextBQ = x("following-sibling::blockquote", anchor)
anchor.parentNode.insertBefore(tag('br'), nextBQ)
anchor.parentNode.insertBefore(anchor, nextBQ)
}
if (thumb.nodeName == 'SPAN')
anchor.parentNode.insertBefore(tag('br'), anchor)
}
}
expandSize.addEventListener('change',
function () {
GM_setValue('expandSize', expandSize.value)
const clientWidth = document.body.clientWidth - reduce
const clientHeight = document.body.clientHeight
const k = clientWidth/clientHeight
for (var i in images)
if (images[i].firstChild.style.display == 'none') {
var size = x("preceding-sibling::span[@class='filesize'][1]/text()[2]", images[i]).textContent.match(/(\d+)x(\d+)/)
var width = size[1]
var height = size[2]
var expanded = images[i].lastChild
if (expandSize.value == 'fit width') {
if (width > clientWidth)
expanded.setAttribute ('width', clientWidth)
} else if (expandSize.value == 'fit screen') {
if (width > clientWidth || height > clientHeight) {
if (width/height < k)
expanded.setAttribute('width', width * clientHeight/height)
else
expanded.setAttribute('width', clientWidth)
}
} else
images[i].lastChild.removeAttribute('width')
}
},
true)
document.body.addEventListener('DOMNodeInserted', function(e) {if (e.target.nodeName=='TABLE') newPosts(e.target)}, true)
function newPosts(el) {
var newImage = x("descendant::img[@md5]/parent::*|descendant::span[@class='tn_reply']/parent::*", el)
if (newImage) {
images.push(newImage)
if (inline)
newImage.addEventListener('click', function(e) {if (e.ctrlKey) return; expandSingle(this, true); e.preventDefault()}, true)
if (auto_gif && /\.gif$/.test(newImage.href))
newImage.firstChild.src = newImage.href
if (expandImages.checked)
expandSingle(newImage)
}
}
if (auto_gif)
for (var i in images)
if (/\.gif$/.test(images[i].href))
images[i].firstChild.src = images[i].href
if (inline)
for (var i in images)
images[i].addEventListener('click', function(e) {if (e.ctrlKey) return; expandSingle(this, true); e.preventDefault()}, true)
if (in_filter) {
controls.appendChild(tag('br'))
var filter = document.getElementById('thread_filter')
filter.childNodes[1].insertBefore(controls, filter.childNodes[1].firstChild)
filter.childNodes[2].insertBefore(tag('br'), filter.childNodes[2].firstChild)
} else {
var filesize = x("//span[@class='filesize']")
var parent = filesize.parentNode
if (parent.nodeName == 'DIV') {//threading
var prev = parent.previousSibling
if (prev)//4chan X
prev.parentNode.insertBefore(controls, prev)
else//4chan extension
parent.parentNode.insertBefore(controls, parent)
} else
filesize.appendChild(controls)
}
GM_registerMenuCommand('ya4cie Options', options)
function options () {
var div = tag('div')
div.id = 'ya4cie'
div.className = 'reply'
div.style.top = document.body.clientHeight / 2
div.style.left = document.body.clientWidth / 2
div.innerHTML = '\
<div>ya4cie</div>\
<div>\
<label>Inline<input type = "checkbox"></label><br>\
<label>Click Full<input type = "checkbox"></label><br>\
<label>Click Fit<input type = "checkbox"></label><br>\
<label>In Filter<input type = "checkbox"></label><br>\
<label>Load Gifs<input type = "checkbox"></label><br>\
<label>Reduce <input size = 3 maxlength = 5></label><br>\
<label>Max Width <input size = 3 maxlength = 5></label><br>\
Auto Expand:<br>\
<label>Nothing<input type = "radio" name = "Auto" value = 0></label><br>\
<label>Replies<input type = "radio" name = "Auto" value = 1></label><br>\
<label>Everything<input type = "radio" name = "Auto" value = 2></label><br>\
<a>save</a> <a>cancel</a>\
</div>'
var temp = X('.//input[@type="checkbox"]', div)
temp[0].checked = GM_getValue('Inline', true)
for (var i = 1, l = temp.length; i < l; i++)//Only Inline is on by default
temp[i].checked = GM_getValue(temp[i].previousSibling.nodeValue)
temp = X('.//input[@size]', div)
temp[0].value = GM_getValue('Reduce', 0)
temp[1].value = GM_getValue('Max Width', 600)
x('.//input[@value="' + GM_getValue('Auto', 0) + '"]', div).checked = true
temp = X('.//a', div)
temp[0].addEventListener('click',
function () {//save
var boxen = X(".//input[@type='checkbox']", div)
for (var i in boxen)
GM_setValue(boxen[i].previousSibling.nodeValue, boxen[i].checked)
var numbs = X(".//input[@size]", div)
for (var i in numbs)
GM_setValue(numbs[i].previousSibling.nodeValue.replace(/ $/, ''), numbs[i].value)
var radio = X(".//input[@type='radio']", div)
for (var i in radio)
if (radio[i].checked)
GM_setValue('Auto', radio[i].value)
div.parentNode.removeChild(div)
},
true)
temp[1].addEventListener('click',
function () {//cancel
div.parentNode.removeChild(div)
},
true)
document.body.appendChild(div)
}
GM_addStyle("\
#ya4cie {\
position: fixed;\
color: inherit;\
border: 1px ridge;\
padding: 5px;\
text-align: right;\
}\
#ya4cie label,\
#ya4cie a { cursor: pointer; }\
#ya4cie div:first-child { text-align: center; }\
")
}) ()
