您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
i,j,k 키를 눌러보세요
当前为
// ==UserScript== // @name 히토미 뷰어 // @name:ko 히토미 뷰어 // @name:en hitomi viewer // @description i,j,k 키를 눌러보세요 // @description:ko i,j,k 키를 눌러보세요 // @description:en press i to open // @version 2203271333 // @match https://hitomi.la/* // @author nanikit // @namespace https://greasyfork.org/ko/users/713014-nanikit // @connect self // @grant GM_xmlhttpRequest // @grant GM_getResourceText // @grant GM_openInTab // @grant window.close // @grant unsafeWindow // @run-at document-start // @require https://cdn.jsdelivr.net/npm/[email protected]/require.js // @resource fflate https://cdn.jsdelivr.net/npm/[email protected]/lib/browser.cjs // @resource react https://cdn.jsdelivr.net/npm/[email protected]/umd/react.production.min.js // @resource react-dom https://cdn.jsdelivr.net/npm/[email protected]/umd/react-dom.production.min.js // @resource @stitches/react https://cdn.jsdelivr.net/npm/@stitches/[email protected]/dist/index.cjs // @resource vim_comic_viewer https://greasyfork.org/scripts/417893-vim-comic-viewer/code/vim%20comic%20viewer.js?version=1032900 // ==/UserScript== "use strict"; if (typeof define !== "function") { throw new Error("requirejs not found."); } requirejs.config({ config: { vim_comic_viewer: { GM_xmlhttpRequest }, }, enforceDefine: true, }); define("main", (require, exports, module) => { "use strict"; var vim_comic_viewer = require("vim_comic_viewer"); const timeout = (millisecond) => new Promise((resolve) => setTimeout(resolve, millisecond)); const waitDomContent = vim_comic_viewer.utils.waitDomContent; const insertCss = (css) => { const style = document.createElement("style"); style.innerHTML = css; document.head.append(style); }; const domContentLoaded = waitDomContent(window.document); const observeOnce = (element, options) => { return new Promise((resolve) => { const observer = new MutationObserver((...args) => { observer.disconnect(); resolve(args); }); observer.observe(element, options); }); }; const defaultFocusCss = ` && { background: aliceblue; }`; const selectItem = (div) => { div.classList.add("key-nav-focus"); const { left, top, width, height } = div.getBoundingClientRect(); const centerX = left + width / 2; const centerY = top + height / 2; const x = centerX - window.innerWidth / 2; const y = centerY - window.innerHeight / 2; window.scrollBy(x, y); }; const getFocusedItem = () => document.querySelector(".key-nav-focus") || undefined; const hookListPage$1 = async (configuration) => { const { navigatePage, getItems, enter, onKeyDown } = configuration; const navigateItem = (forward) => { const items = getItems(); const focus = getFocusedItem(); if (!focus) { if (items[0]) { selectItem(forward ? items[0] : items[items.length - 1]); } return; } const index = items.indexOf(focus); if (index === -1) { return; } focus.classList.remove("key-nav-focus"); let next = index + (forward ? 1 : -1); next = Math.max(0, Math.min(next, items.length - 1)); selectItem(items[next]); }; const forward1 = (event) => { if (onKeyDown) { const focus = getFocusedItem(); onKeyDown(event, focus); } }; const handlePageKeypress = (event) => { switch (event.key) { case "h": navigatePage(-1); break; case "l": navigatePage(+1); break; default: { forward1(event); break; } } }; const handleKeyPress = (event) => { if (event.target.tagName === "INPUT") { return; } switch (event.key) { case "j": navigateItem(true); break; case "k": navigateItem(false); break; case "i": { const item = getFocusedItem(); if (item) { enter(item); } break; } default: if (navigatePage) { handlePageKeypress(event); } else { forward1(event); } break; } }; const insertFocusCss = () => { const content = configuration.focusCss || defaultFocusCss; insertCss(content.replace(/&/g, ".key-nav-focus")); }; addEventListener("keypress", handleKeyPress); await domContentLoaded; insertFocusCss(); }; const getNextPageUrl = () => { const url = new URL(location.href); const search = url.searchParams; const nextPage = `${Number(search.get("page") || "1") + 1}`; search.set("page", nextPage); return url.toString(); }; const prefetchUrl = (url) => { const preloader = document.createElement("link"); preloader.rel = "prefetch"; preloader.href = url; document.head.append(preloader); }; const getShadowedIframe = (url) => { const iframe = document.createElement("iframe"); iframe.src = url; const div = document.createElement("div"); div.style.display = "none"; div.attachShadow({ mode: "open", }); div.shadowRoot?.append?.(iframe); return [ div, iframe, ]; }; const preloadUrl = async (url) => { // chrome doesn't allow link=preload so create iframe const [div, iframe] = getShadowedIframe(url); document.body.append(div); while (!iframe.contentDocument) { await timeout(100); } await waitDomContent(iframe.contentDocument); prefetchUrl(url); }; const waitPageOverHalf = () => new Promise((resolve) => { const listener = () => { if (document.body.scrollHeight / 2 < window.scrollY) { removeEventListener("scroll", listener); resolve(); } }; addEventListener("scroll", listener); }); const triggerPagePreload = async () => { const url = getNextPageUrl(); prefetchUrl(url); await waitPageOverHalf(); await preloadUrl(url); }; const navigatePage = (offset) => { const search = new URLSearchParams(location.search); const page = search.get("page") || "1"; search.set("page", Math.max(1, Number(page) + offset).toString()); location.search = search.toString(); }; const getItems = () => [ ...document.querySelectorAll(".gallery-content > div"), ]; const enter = (element) => { const anchor = element.querySelector?.("a"); const fileName = anchor?.href?.match?.(/\d+\.html/)?.[0]; if (fileName) { GM_openInTab(`${location.origin}/reader/${fileName}`); } }; const hookListPage = async () => { await hookListPage$1({ enter, getItems, navigatePage, }); triggerPagePreload(); }; const onReaderKey = (event) => { switch (event.key) { case "o": close(); break; } }; const getId = () => { return location.href.match(/([^/]+)\.html/)?.[1]; }; const findSource = (picture) => { const src = picture.getAttribute("src"); if (src) { return src; } const imgOrSource = picture.querySelector("[src], [srcset]"); return (imgOrSource?.getAttribute("src") ?? imgOrSource?.getAttribute("srcset")) ?? undefined; }; const waitUnsafeObject = async (name) => { while (true) { const target = unsafeWindow[name]; if (target) { if (typeof target == "function") { return target.bind(unsafeWindow); } return target; } await timeout(100); } }; const comicSource = async () => { const id = getId(); const info = await waitUnsafeObject("galleryinfo"); prependIdToTitle(info); const gg = await waitUnsafeObject("gg"); const guardless = `${gg.m}`.slice(14, -2).replace(/return 4;/g, ""); unsafeWindow.gg.m = Function("g", guardless); const makeImageElement = await waitUnsafeObject("make_image_element"); const urls = info.files.map((file) => findSource(makeImageElement(id, file)) ); return urls; }; const prependIdToTitle = async (info) => { const title = document.querySelector("title"); for (let i = 0; i < 2; i++) { document.title = `${info.id} ${info.title}`; await observeOnce(title, { childList: true, }); } }; const overrideCss = ` .vim_comic_viewer ::-webkit-scrollbar { width: 12px !important; } ::-webkit-scrollbar-thumb { background: #888; } `; const hookReaderPage = async () => { await vim_comic_viewer.utils.waitDomContent(document); await vim_comic_viewer.initialize({ source: comicSource, imageProps: { loading: "lazy", }, }); insertCss(overrideCss); addEventListener("keypress", onReaderKey); }; const initialize = async () => { const { pathname } = location; if (pathname.startsWith("/reader")) { await hookReaderPage(); } else if (!/^\/(manga|doujinshi|cg)\//.test(pathname)) { await hookListPage(); } }; initialize(); // }); for ( const name of [ "fflate", "react", "react-dom", "@stitches/react", "vim_comic_viewer", ] ) { const body = GM_getResourceText(name); define(name, Function("require", "exports", "module", body)); } unsafeWindow.process = { env: { NODE_ENV: "production" } }; require(["main"], () => {}, console.error);