Infinite scrolling for galleries in e-hentai and exhentai
Verze ze dne
// ==UserScript==
// @name Panda Infinite Scroll
// @description Infinite scrolling for galleries in e-hentai and exhentai
// @namespace https://gist.github.com/ytjchan
// @author ytjchan
// @version 1.0.0
// @include *://e-hentai.org/*
// @include *://exhentai.org/*
// @exclude *://e-hentai.org/g/*
// @exclude *://e-hentai.org/s/*
// @exclude *://exhentai.org/g/*
// @exclude *://exhentai.org/s/*
// ==/UserScript==
// Current page (0-indexed)
let url = new URL(window.location);
if (url.pathname.includes("tag") && isNaN(url.pathname.split("/").slice(-1)[0]))
url.href += "/0";
url.setPage = (url.pathname.includes("tag"))?
page => url.href = url.href.replace(/\/[^\/]*$/, "/"+page) :
page => url.searchParams.set("page", page);
let currentPage = (url.pathname.includes("tag"))?
(parseInt(url.pathname.split("/").slice(-1)[0]) || 0) :
(parseInt(url.searchParams.get("page")) || 0);
// Restore current page in case of page back
if (document.getElementsByClassName("page-anchor").length !== 0)
currentPage = Math.max(...Array.from(document.getElementsByClassName("page-anchor")).map(el => parseInt(el.getAttribute("data-page"))));
// Max page
let pageBtns = document.querySelectorAll(".ptt td");
let maxPage = parseInt(pageBtns[pageBtns.length-2].textContent) - 1;
// Detect if bottom of page reached
let observer = new IntersectionObserver(fetchNextPage, { threshold: 1.0 });
observer.observe(document.querySelector(".ptb"));
// Alert area
let flex = document.createElement("div");
flex.setAttribute("style", "display: flex;flex-direction: column;align-items: flex-end;position: fixed;top: 0;right: 0;z-index: 10;");
document.getElementsByTagName("body")[0].appendChild(flex);
createScrollAlert(currentPage);
let inProcess = false;
function fetchNextPage() {
if (currentPage >= maxPage) {
observer.disconnect();
createAlert("Last page reached", 5000, "#0FF");
return;
}
if (inProcess) return;
inProcess = true;
url.setPage(++currentPage);
fetch(url.href)
.then(res => res.text())
.then(html => {
let parent = document.querySelector(".gl1t, .itg tr").parentNode;
let dummy = document.createElement("html");
dummy.innerHTML = html;
parent.innerHTML += Array.from(dummy.querySelectorAll(".gl1t, .itg tr")).reduce((prev, el, i) => {
if (i === 0) {
el.classList.add("page-anchor");
el.setAttribute("data-page", currentPage);
}
return prev += el.outerHTML;
}, "");
createScrollAlert(currentPage);
inProcess = false;
})
.catch(e => createAlert(e, 5000, "#F00"));
}
function createAlert(msg, timeout, bgColor = "#EEE") {
let div = document.createElement("div");
div.textContent = msg;
div.setAttribute("style", "padding: 0.5em;margin-bottom: 0.1em;font-size: 1.25em;background-color: "+bgColor+";color: #000");
flex.appendChild(div);
setTimeout(()=>div.remove(), timeout);
return div;
}
function scrollToPage(page) {
if (page > currentPage) {
alert("Page not loaded yet");
return;
}
try {
document.querySelector(".page-anchor[data-page='"+page+"']").scrollIntoView();
} catch (e) { // page anchor does not exist
document.documentElement.scrollTop = 0;
}
}
// display clickable page alert as 1-indexed
function createScrollAlert(page) {
createAlert((page+1)+" / "+(maxPage+1), 5000, "#0F0")
.addEventListener("click", scrollToPage.bind(null, page)); // display as 1-indexed
}