您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds a fullscreen image view option when you click on images
当前为
// ==UserScript== // @id gelbooru-slide // @name Gelbooru Image Viewer // @version 1.7.1 // @namespace intermission // @author intermission // @license WTFPL; http://www.wtfpl.net/about/ // @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, w = window, array = a => [].slice.call(a), observer, request, 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, notification, pos, posEl, menu, menuEl, button, buttonEl, slideshow; if (!stor[ns]) stor[ns] = "false"; GM_registerMenuCommand("Current image mode: " + (toggle ? "Always original size" : "Sample only"), () => { stor[ns] = toggle ? "false" : "true"; return location.reload(); }); l = toggle ? "file_url" : "sample_url"; current = src => d.querySelector("img.preview[src*='" + base(src || slideEl.src) + "']").parentNode; cache = (a, b) => { let id = (a.href || a).match(/id=([0-9]+)/)[1], val = toggle ? "original" : "sample"; if (!b) { let ret; try { ret = JSON.parse(stor[ns + id])[val] } catch(e) {} return ret || "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; } }; pos = a => { if (typeof a === "boolean") { let no = posEl.firstElementChild; if (a) no.innerHTML = Number(no.innerHTML) + 1; else no.innerHTML = Number(no.innerHTML) - 1; } else { if (slideEl && !posEl) { let thumbs = array(d.querySelectorAll`span.thumb a[data-full]`); posEl = d.createElement`div`; posEl.insertAdjacentHTML("beforeend", "<span>" + (thumbs.indexOf(current()) + 1) + "</span> / " + thumbs.length); posEl.setAttribute("style", "position: fixed; bottom: 20px; left: 0; display: block; pointer-events: none;"); slideEl.insertAdjacentElement("afterend", posEl); } else if (posEl) posEl = posEl.remove(); } return; }; 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; }; menu = e => { let _l = e.clientX + 1, _t = e.clientY + 1, left = (_l > w.innerWidth - 139 ? (_l - 139) : _l) + "px", top = (_t > w.innerHeight - 48 ? (_t - 48) : _t) + "px"; if (menuEl) { menuEl.removeAttribute("class"); menuEl.style.left = left; menuEl.style.top = top; setTimeout(() => menuEl.classList.add("menuel"), 10); } else { let href = current().href; menuEl = d.createElement("div"); menuEl.id = "menuel"; menuEl.insertAdjacentHTML("beforeend", '<a href="'+href+'" style="margin-bottom: 2px">Open in This Tab</a><a href="'+href+'" target="_blank">Open in New Tab</a>'); menuEl.style.left = left; menuEl.style.top = top; d.body.appendChild(menuEl); menuEl.classList.add("menuel"); } return; }; preload = function() { var curr = current(); request(find(curr, true)); return request(find(curr, false)); }; keyDown = function(e) { var move; if (slideshow) return; switch(e.keyCode) { case 32: case 39: move = true; break; case 37: move = false; break; case 38: if (e.event) menu(e.event); else w.location = current().href; return; case 40: e.preventDefault(); return slideEl.click(); } if (typeof move != "undefined") { e = find(current(), move); if (e) { slideEl.slide(e.firstElementChild.src); pos(move); preload(); } else if (!notification) { notification = d.createElement`div`; notification.classList.add`nomoreimages`; notification.setAttribute("style", "pointer-events: none; background: linear-gradient(to " + (move ? "right" : "left") + ", transparent, rgba(255,0,0,.5));" + (move ? "right" : "left") + ": 0;"); d.body.insertBefore(notification, d.body.lastElementChild); } } return; }; button = () => { let clear = () => clearTimeout(Number(buttonEl.dataset.timer) || 0); if (buttonEl) { clear(); buttonEl = buttonEl.remove(); } else { let svg_state = true, svg_play = ` <svg width="50" height="50" version="1.1" xmlns="http://www.w3.org/2000/svg"> <rect rx="5" height="48" width="48" y="1" x="1" fill="#fff" /> <polygon fill="#000" points="16 12 16 38 36 25" /> </svg></span>`, svg_pause = ` <span><svg width="50" height="50" xmlns="http://www.w3.org/2000/svg"> <rect fill="#fff" x="1" y="1" width="48" height="48" rx="5" /> <rect fill="#000" x="12" y="12" width="10" height="26" /> <rect fill="#000" x="28" y="12" width="10" height="26" /> </svg>`, fn = function() { let options = array(buttonEl.querySelectorAll("div input")).map(a => a.type == "number" ? (a.value >= 5 ? a.value : 5) * 1E3 : a.checked ), _fnS = () => { slideEl.removeEventListener("load", _fnS); buttonEl.dataset.timer = setTimeout(_fnT, options[2]); }, _fnT = () => { let el, thumbs; if (options[1]) { thumbs = array(d.querySelectorAll("span.thumb a[data-full]")); thumbs.splice(thumbs.indexOf(current()), 1); el = thumbs[Math.random() * thumbs.length >> 0]; } else el = find(current(), true); if (!el && options[0]) el = d.querySelector("span.thumb a[data-full]"); if (!el) return fn(); slideEl.addEventListener("load", _fnS); slideEl.slide(el.firstElementChild.src); preload(); }; slideshow = !!svg_state; pos(); buttonEl.firstElementChild.innerHTML = (svg_state = !svg_state) ? svg_play : svg_pause; if (slideshow) { buttonEl.dataset.timer = setTimeout(_fnT, options[2]); buttonEl.style.opacity = ".4"; } else { clear(); buttonEl.style.opacity = ".7"; buttonEl.removeAttribute("data-timer"); } return; }; buttonEl = d.createElement("div"); buttonEl.setAttribute("style", 'opacity: .7;'); buttonEl.className = "slideshow"; buttonEl.insertAdjacentHTML('beforeend', '<span title="Slideshow">' + svg_play + "</span>" + ` <div style="display: none;padding: 10px 0">Options<hr> <label>Loop: <input type="checkbox" checked></label> <label>Shuffle: <input type="checkbox"></label> <br>Interval: <input type="number" value="5" style="width: 100px"> </div>`); buttonEl.firstElementChild.onclick = fn; d.body.appendChild(buttonEl); } }; slider = function(a) { d.dispatchEvent(new CustomEvent(ns, {bubbles:true})); if (slidin) { let center; slideshow = slidin = !(a = current()); slideEl = slideEl.remove(); button(); pos(); d.body.classList.remove`sliding`; a.classList.add`outlined`; d.removeEventListener("keydown", keyDown); center = a.offsetTop + a.offsetHeight / 2 - w.innerHeight / 2; w.scrollTo(0, center < 0 ? 0 : center); } else { let fn = function(src) { var data, load = () => { this.removeAttribute("src"); this.setAttribute("src", data); this.removeEventListener("load", load); }; if (!slideshow) this.src = src; data = current(src).dataset.full; if (data == "loading") request(current(src)); else { if (/\.gif(?:\?\d+)?$/.test(data)) this.addEventListener("load", load); this.src = data; } }; slidin = true; d.body.classList.add`sliding`; 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: fn }); slideEl.onclick = slider; slideEl.onmouseup = e => e.button === 1 && keyDown({keyCode:38, event:e}); d.body.appendChild(slideEl); slideEl.slide(a.firstElementChild.src); d.addEventListener("keydown", keyDown); pos(); button(); preload(); } return; }; request = function(node) { if (!node || node.dataset.alreadyLoading || node.dataset.full != "loading") return; node.dataset.alreadyLoading = "true"; return fetch("/index.php?page=dapi&s=post&q=index&id=" + node.id.substr(1)) .then(x => x.text()) .then(text => { let img = (new DOMParser()).parseFromString(text, "text/xml").querySelector("post").getAttribute(l); if (img) { node.dataset.full = cache(node, img); if (slideEl) { if (slideEl.src.indexOf(base(img)) > -1) slideEl.slide(img); else if (slideshow) slideEl.src = img; } return; } else { node.removeAttribute`data-already-loading`; fetch`/intermission.php`.then(() => request(node)); throw undefined; } }).catch(err => { if (typeof err == "undefined") return; console.error("Failed HTTP request\nDo you have an internet connection?\n", err); return node.removeAttribute`data-already-loading`; }); }; observer = new MutationObserver(function(mutations) { function process(node) { var 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.href); a.onclick = e => e.button === 0 && (e.preventDefault(), e.stopPropagation(), slider(e.target.parentNode)); } } catch(e) {} return; } return mutations.forEach(mutation => array(mutation.addedNodes).forEach(process)); }); observer.observe(d, { childList: true, subtree: true }); d.addEventListener("animationend", e => { if (e.animationName == "Outlined") e.target.classList.remove`outlined`; else if (e.animationName == "nomoreimages") notification = e.target.remove(); else if (e.animationName == "menuelement") menuEl = e.target.remove(); }); w.addEventListener("keypress", e => { if (e.key === "Enter" || e.keyCode === 13) { if (e.target.matches`span.thumb>a[data-full]`) { e.preventDefault(); slider(e.target); } if (slideshow) { buttonEl.firstElementChild.click(); } } }); w.addEventListener("wheel", e => slidin && keyDown({keyCode: e.deltaY > 0 ? 39 : e.deltaY < 0 ? 37 : 0})); if (stor[ns + "-firstrun"] != "1.5.3") { (function(l){ var a; for (a in l) if (/^gelbooru-slide./.test(a)) l.removeItem(a); }(stor)); stor[ns + "-firstrun"] = "1.5.3"; } { let css = d.createElement`style`; css.textContent = ` @keyframes Outlined { 0% { outline: 6px solid orange } 60% { outline: 6px solid orange } 100% { outline: 6px solid transparent } } @keyframes nomoreimages { 0% { opacity: 0 } 20% { opacity: 1 } 100% { opacity: 0 } } @keyframes menuelement { 0% { opacity: 1 } 80% { opacity: 1 } 100% { opacity: 0 } } body.sliding > *:not(#slide) { display: none } #slide { width: 100vw; height: 100vh; object-fit: contain } .outlined { outline: 6px solid transparent; animation-duration: 4s; animation-name: Outlined } .nomoreimages { display: block ! important; width: 33vw; height: 100vh; top: 0; position: fixed; animation-duration: 1s; animation-name: nomoreimages } span.thumb { max-width: 180px; max-height: 180px; } #menuel { opacity: 1; position: fixed; display: block ! important; padding: 2px; background: black; width: 139px; height: 44px; animation-duration: 1s } .menuel { animation-name: menuelement } #menuel:hover { animation-name: keepalive } #menuel a { background: #fff; display: block } .slideshow { display: block ! important; position: fixed; bottom: 20px; right: 20px } .slideshow:hover:not([data-timer]) > div { background: white; color: black; position: fixed; display: block ! important; bottom: 70px; right: 20px }`; return d.head.appendChild(css); } }())