토끼 뷰어

i,j,k 키를 눌러보세요

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください。
// ==UserScript==
// @name           토끼 뷰어
// @name:ko        토끼 뷰어
// @name:en        toki viewer
// @description    i,j,k 키를 눌러보세요
// @description:ko i,j,k 키를 눌러보세요
// @description:en press i to open
// @version        241102165412
// @match          https://*.net/bbs/*
// @match          https://*.net/comic/*
// @match          https://*.com/webtoon/*
// @match          https://*.com/novel/*
// @author         nanikit
// @namespace      https://greasyfork.org/ko/users/713014-nanikit
// @license        MIT
// @connect        *
// @grant          GM.addValueChangeListener
// @grant          GM.getResourceText
// @grant          GM.getValue
// @grant          GM.removeValueChangeListener
// @grant          GM.setValue
// @grant          GM.xmlHttpRequest
// @grant          unsafeWindow
// @require        https://cdn.jsdelivr.net/npm/[email protected]/require.js
// @resource       link:@headlessui/react       https://cdn.jsdelivr.net/npm/@headlessui/[email protected]/dist/headlessui.prod.cjs
// @resource       link:@stitches/react         https://cdn.jsdelivr.net/npm/@stitches/[email protected]/dist/index.cjs
// @resource       link:clsx                    https://cdn.jsdelivr.net/npm/[email protected]/dist/clsx.js
// @resource       link:fflate                  https://cdn.jsdelivr.net/npm/[email protected]/lib/browser.cjs
// @resource       link:jotai                   https://cdn.jsdelivr.net/npm/[email protected]/index.js
// @resource       link:jotai-cache             https://cdn.jsdelivr.net/npm/[email protected]/dist/cjs/atomWithCache.js
// @resource       link:jotai/react             https://cdn.jsdelivr.net/npm/[email protected]/react.js
// @resource       link:jotai/react/utils       https://cdn.jsdelivr.net/npm/[email protected]/react/utils.js
// @resource       link:jotai/utils             https://cdn.jsdelivr.net/npm/[email protected]/utils.js
// @resource       link:jotai/vanilla           https://cdn.jsdelivr.net/npm/[email protected]/vanilla.js
// @resource       link:jotai/vanilla/utils     https://cdn.jsdelivr.net/npm/[email protected]/vanilla/utils.js
// @resource       link:overlayscrollbars       https://cdn.jsdelivr.net/npm/[email protected]/overlayscrollbars.cjs
// @resource       link:overlayscrollbars-react https://cdn.jsdelivr.net/npm/[email protected]/overlayscrollbars-react.cjs.js
// @resource       link:react                   https://cdn.jsdelivr.net/npm/[email protected]/cjs/react.production.min.js
// @resource       link:react-dom               https://cdn.jsdelivr.net/npm/[email protected]/cjs/react-dom.production.min.js
// @resource       link:react-toastify          https://cdn.jsdelivr.net/npm/[email protected]/dist/react-toastify.js
// @resource       link:scheduler               https://cdn.jsdelivr.net/npm/[email protected]/cjs/scheduler.production.min.js
// @resource       link:vcv-inject-node-env     data:,unsafeWindow.process=%7Benv:%7BNODE_ENV:%22production%22%7D%7D
// @resource       link:vim_comic_viewer        https://update.greasyfork.org/scripts/417893/1476400/vim%20comic%20viewer.js
// @resource       overlayscrollbars-css        https://cdn.jsdelivr.net/npm/[email protected]/styles/overlayscrollbars.min.css
// @resource       react-toastify-css           https://cdn.jsdelivr.net/npm/[email protected]/dist/ReactToastify.css
// ==/UserScript==
"use strict";

define("main", (require, exports, module) => {
var import_vim_comic_viewer = require("vim_comic_viewer");
async function main() {
  const origin = getOrigin();
  if (origin === "unknown") {
    return;
  }
  markVisitedLinks();
  registerEpisodeNavigator();
  const buttons = duplicateViewerButton();
  const source = await comicSource();
  const controller = await (0, import_vim_comic_viewer.initialize)({
    source: () => source,
    onPreviousSeries: goPreviousEpisode,
    onNextSeries: goNextEpisode
  });
  controller.setScriptPreferences({
    manualPreset: origin,
    preferences: { pageDirection: origin === "manatoki" ? "rightToLeft" : "leftToRight" }
  });
  for (const button of buttons) {
    button.addEventListener("click", async () => {
      await controller.setImmersive(true);
    });
  }
}
function getOrigin() {
  const allowedOrigins = ["manatoki", "newtoki", "booktoki"];
  return allowedOrigins.find(originIncludes) ?? "unknown";
}
function originIncludes(str) {
  return location.origin.includes(str);
}
function duplicateViewerButton() {
  const template = document.createElement("template");
  template.innerHTML = `<a class="show_viewer" alt="뷰어로 보기">
    <i class="ion-ios-book at-tip" aria-hidden="true" style="color: blue;"></i>
  </a>`;
  const templateButton = template.content.firstElementChild;
  const buttons = [];
  const divs = document.querySelectorAll(".toon-nav");
  for (const div of divs) {
    const button = templateButton.cloneNode(true);
    div.prepend(button);
    buttons.push(button);
  }
  return buttons;
}
async function comicSource() {
  while (true) {
    const urls = getUrls();
    if (urls.length) {
      return urls;
    }
    await import_vim_comic_viewer.utils.timeout(200);
  }
}
function goPreviousEpisode() {
  document.getElementById("goPrevBtn")?.click?.();
}
function goNextEpisode() {
  document.getElementById("goNextBtn")?.click?.();
}
function registerEpisodeNavigator() {
  addEventListener("keydown", (event) => {
    const { ctrlKey, shiftKey, altKey } = event;
    if (ctrlKey || shiftKey || altKey || import_vim_comic_viewer.utils.isTyping(event)) {
      return;
    }
    switch (event.key) {
      case "t":
        document.getElementById("sticky-wrapper")?.scrollIntoView({
          block: "center"
        });
        break;
      case "m":
        document.querySelector(".view-good")?.scrollIntoView({
          block: "center"
        });
        break;
    }
  });
}
function getUrls() {
  const imgs = document.querySelectorAll(
    "div.view-padding img"
  );
  return [...imgs].flatMap(getUrl);
}
function getUrl(image) {
  if (image.offsetParent === null) {
    return [];
  }
  const data = Object.values(image.dataset);
  return data.length ? data : [image.src];
}
async function markVisitedLinks() {
  const links = document.querySelectorAll(".post-row a");
  const visitedLinks = new Set(await GM.getValue("visitedPaths", []));
  for (const link of links) {
    const url = link.getAttribute("href");
    if (!url) return;
    const path = new URL(url).pathname;
    if (visitedLinks.has(path)) {
      link.style.color = "#e2e2e2";
    }
    link.addEventListener("click", async () => {
      visitedLinks.add(path);
      await GM.setValue("visitedPaths", [...visitedLinks]);
    });
  }
}
main();

});

define("tampermonkey_grants", function() { Object.assign(this.window, { GM, unsafeWindow }); });
requirejs.config({ deps: ["tampermonkey_grants"] });
load()

async function load() {
  const links = GM.info.script.resources.filter(x => x.name.startsWith("link:"));
  await Promise.all(links.map(async ({ name }) => {
    const script = await GM.getResourceText(name)
    define(name.replace("link:", ""), Function("require", "exports", "module", script))
  }));
  require(["main"], () => {}, console.error);
}