Gelbooru Endless Scroll

Adds endless scroll function to Gelbooru

As of 2016-12-19. See the latest version.

// ==UserScript==
// @id             gelbooru-endless-scroll
// @name           Gelbooru Endless Scroll
// @version        1.4
// @namespace      intermission
// @author         intermission
// @license        WTFPL; http://www.wtfpl.net/about/
// @description    Adds endless scroll function to Gelbooru
// @include        http://gelbooru.com/index.php?*
// @include        https://gelbooru.com/index.php?*
// @run-at         document-end
// @grant          none
// ==/UserScript==

(function(){
	"use strict";
	var d = document, _on = false, url,
	v = ["span.thumb[id^='s']", "div.pagination"],
	vis = function(el) {
		var rect = el.getBoundingClientRect();
		return (
			rect.top + rect.height >= 0 &&
			( d.documentElement.clientHeight - rect.bottom ) + rect.height >= 0
		);
	}, target = d.querySelector(v[1]), total,
	page = function(doc) {
		var images, pageNo = d.createElement`span`, frag = d.createDocumentFragment(), container;
		images = [].slice.call(doc.querySelectorAll('post'));
		if (images.length === 0)
			throw Error("Broken API?");
		pageNo.innerHTML = "Page " + url.index + "~<span>out of " + total + "</span>";
		pageNo.className = "thumb";
		pageNo.style = "display:flex;flex-direction:column;";
		frag.appendChild(pageNo);
		pageNo.firstElementChild.style = "margin:auto 0 30px";
		images.map(a => {
			let fn = e => {
				e.target.onload = null;
				thumb.style.overflow = "visible";
			}, thumb = d.createElement("span"),
			r = a.getAttribute("rating");
			r = r == "s" ? "safe" : r == "q" ? "questionable" : r == "e" ? "explicit" : "unknown";
			thumb.id = "s" + a.getAttribute("id");
			thumb.className = "thumb";
			thumb.insertAdjacentHTML("beforeend", '<a id="p'+a.getAttribute("id")+'" href="index.php?page=post&amp;s=view&amp;id='+a.getAttribute("id")+'"><img src="'+a.getAttribute("preview_url")+'?'+a.getAttribute("id")+'" alt="'+a.getAttribute("tags")+'" title="'+a.getAttribute("tags")+' score:'+a.getAttribute("score")+' rating:'+r+'" class="preview" border="0"></a>');
			thumb.style.overflow = "hidden";
			thumb.querySelector("img").onload = fn;
			frag.appendChild(thumb);
		});
		container = d.querySelector(v[0]).parentNode;
		if (container.lastElementChild.matches`span`) container.appendChild(frag);
		else container.insertBefore(frag, container.querySelector`span.thumb:last-of-type + *`);
		if (list.length > 0) {
			events();
			process();
		} return;
	}, req = _ => fetch(url.href).then(
		x => x.text().then(
			text => page((new DOMParser()).parseFromString(text, "text/xml").documentElement)
		)
	).catch(err => {
		if (typeof err != "undefined") {
			console.error("Something unexpected happened\n", err);
			setTimeout(() => req(), 5000);
		}
	}), process = function() {
		if (vis(target)) {
			events();
			url = list.shift();
			return req();
		}
	}, events = function() {
		var val = (_on = !_on) ? "addEventListener" : "removeEventListener";
		window[val]("scroll", process, false);
		window[val]("resize", process, false);
		window[val]("visibilitychange", process, false);
	}, list = [];
	if (!target || !target.querySelector`a`) return;
	{
		let p = target.lastElementChild.href,
		n = d.querySelectorAll(v[0]).length,
		path = window.location.search,
		pid = path.match(/pid=([0-9]+)/),
		start_index = pid ? pid[1] / n + 1 : 1,
		tags = path.match(/(?:\?|&)tags=([^&#]+)/i)[1],
		end_index;
		if (!p) return;
		else {
			end_index = p.match(/pid=([0-9]+)/)[1] / n + 1;
			do {
				list.push({
					href: 'http://gelbooru.com/index.php?page=dapi&s=post&q=index&tags=' + tags + '&pid=' + start_index + '&limit=' + n,
					index: ++start_index
				});
			} while(start_index < end_index);
			total = end_index;
		}
	}
	d.addEventListener("gelbooru-slide", _ => {
		if (list.length > 0)
			events();
	}, false);
	events();
	process();
}())