// ==UserScript==
// @id gelbooru-slide
// @name Gelbooru Image Viewer
// @version 1.2
// @namespace intermission
// @author intermission
// @license CC0; https://wiki.creativecommons.org/wiki/CC0
// @description Adds a fullscreen image view option when you click on images
// @include http://gelbooru.com/index.php?*
// @include https://gelbooru.com/index.php?*
// @run-at document-start
// @grant GM_registerMenuCommand
// ==/UserScript==
(function(){
"use strict";
var d = document, array = a => [].slice.call(a), observer, request, mouseUp, slideEl, slider, slidin, base = a => a.split("/").pop().split(".")[0].split("_").pop(), keyDown, find, current, preload, stor = localStorage, l, ns = "gelbooru-slide", toggle = stor[ns] == "true", cache;
if (!stor[ns]) stor[ns] = "false";
GM_registerMenuCommand("Current image mode: " + (toggle ? "Always original size" : "Sample only"), () => {
stor[ns] = toggle ? "false" : "true";
location.reload();
});
l = toggle ? ["ul>li>a[style*='font-weight:']", "href"] : ["#image", "src"];
current = a => d.querySelector("img.preview[src*='" + base(slideEl.src) + "']").parentNode;
mouseUp = e => e.button == 0 && (e.preventDefault(), e.stopPropagation(), slider(e.target.parentNode));
cache = (a, b) => {
let id = a.href.match(/id=([0-9]+)/)[1], val = toggle ? "original" : "sample";
if (!b) {
let temp;
try { temp = JSON.parse(stor[ns + id])[val] } catch(e) {}
return temp || "loading";
}
else {
let obj, temp;
try { temp = JSON.parse(stor[ns + id]) } catch(e) {}
obj = temp || {};
obj[val] = b;
stor[ns + id] = JSON.stringify(obj);
return b;
}
};
find = function(el, method) {
var a;
el = el.parentNode;
do {
try {
el = el[(method ? "next" : "previous") + "ElementSibling"];
a = el.querySelector`a[data-full]`;
} catch(err) {
return false;
}
if (a) break;
a = false;
} while(!a);
return a;
};
preload = function() {
var curr = current(), a = find(curr, true), b = find(curr, false);
a && request(a);
b && request(b);
return;
};
keyDown = function(e) {
var move;
switch(e.keyCode) {
case 32:
case 39:
move = true;
break;
case 37:
move = false;
break;
case 38:
window.location = current().href;
break;
case 40:
e.preventDefault();
slideEl.click();
break;
}
if (typeof move != "undefined") {
e = find(current(), move);
if (e) slideEl.slide(e.firstElementChild.src);
preload();
}
};
slider = function(a) {
var list;
list = array(d.querySelectorAll`body > *`);
if (slidin) {
let center;
slidin = !(a = current());
slideEl = slideEl.remove();
list.forEach(a => a.dataset.slide = "false");
a.classList.add`outlined`;
d.removeEventListener("keydown", keyDown, false);
center = a.offsetTop + a.offsetHeight / 2 - window.innerHeight / 2;
window.scrollTo(0, center < 0 ? 0 : center);
} else {
slidin = true;
list.forEach(a => a.dataset.slide = "true");
array(d.querySelectorAll`span>a.outlined`).map(a => a.classList.remove`outlined`);
slideEl = d.createElement`img`;
slideEl.id = "slide";
slideEl.alt = "Loading...";
Object.defineProperty(slideEl, "slide", {
value: function(src) {
var data;
this.src = src;
data = current().dataset.full;
if (data == "loading") request(current());
else this.src = data;
}
});
slideEl.onclick = _ => slider(a);
d.body.appendChild(slideEl);
slideEl.slide(a.firstElementChild.src);
d.addEventListener("keydown", keyDown, false);
preload();
} return;
};
request = function(node) {
if (node.dataset.alreadyLoading || node.dataset.full != "loading" ) return;
node.dataset.alreadyLoading = "true";
return fetch(node.href)
.then(x => x.text().then(text => {
var doc = (new DOMParser()).parseFromString(text, "text/html"), img, _base;
if (img = doc.querySelector(l[0])) {
_base = base(img[l[1]]);
node.dataset.full = cache(node, img[l[1]]);
if (slideEl && slideEl.src.indexOf(_base) > -1)
slideEl.slide(img[l[1]]);
} else {
node.removeAttribute`data-already-loading`;
fetch`/intermission.php`.then(x2 => request(node))
}
})).catch(err => {
console.error("Failed HTTP request\nDo you have an internet connection?\n", err);
node.removeAttribute`data-already-loading`;
});
};
observer = new MutationObserver(function(mutations) {
function process(node) {
let a;
try {
if (node.matches`span.thumb[id^='s']` && (a = node.firstElementChild) && !a.dataset.full) {
if (node.querySelector`img[alt*='webm']`) return;
a.dataset.full = cache(a);
a.onclick = mouseUp;
}
} catch(e) {}
};
mutations.forEach(function(mutation) {
array(mutation.addedNodes).forEach(process);
});
});
observer.observe(d, {
childList: true,
subtree: true
});
d.addEventListener("animationend", e => e.animationName == 'Outlined' && e.target.classList.remove`outlined`, false);
{
let css = d.createElement`style`;
css.textContent =
`@keyframes Outlined {
0% {
outline: 6px solid orange
}
60% {
outline: 6px solid orange
}
100% {
outline: 6px solid transparent
}
}
[data-slide="true"] {
display: none ! important
}
#slide {
width: 100vw;
height: 100vh;
object-fit: contain
}
.outlined {
outline: 6px solid transparent;
animation-duration: 4s;
animation-name: Outlined
}`;
d.head.appendChild(css);
}
}())