토끼 뷰어

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

As of 30.01.2024. See ბოლო ვერსია.

  1. // ==UserScript==
  2. // @name 토끼 뷰어
  3. // @name:ko 토끼 뷰어
  4. // @name:en toki viewer
  5. // @description i,j,k 키를 눌러보세요
  6. // @description:ko i,j,k 키를 눌러보세요
  7. // @description:en press i to open
  8. // @version 240130121920
  9. // @match https://*.net/bbs/*
  10. // @match https://*.net/comic/*
  11. // @match https://*.com/webtoon/*
  12. // @author nanikit
  13. // @namespace https://greasyfork.org/ko/users/713014-nanikit
  14. // @connect *
  15. // @grant GM_getResourceText
  16. // @grant GM_getValue
  17. // @grant GM_setValue
  18. // @grant GM_xmlhttpRequest
  19. // @grant unsafeWindow
  20. // @require https://cdn.jsdelivr.net/npm/requirejs@2.3.6/require.js
  21. // @resource link:@headlessui/react https://cdn.jsdelivr.net/npm/@headlessui/react@1.7.17/dist/headlessui.prod.cjs
  22. // @resource link:@stitches/react https://cdn.jsdelivr.net/npm/@stitches/react@1.3.1-1/dist/index.cjs
  23. // @resource link:clsx https://cdn.jsdelivr.net/npm/clsx@2.0.0/dist/clsx.js
  24. // @resource link:fflate https://cdn.jsdelivr.net/npm/fflate@0.8.1/lib/browser.cjs
  25. // @resource link:jotai https://cdn.jsdelivr.net/npm/jotai@2.4.2/index.js
  26. // @resource link:jotai/react https://cdn.jsdelivr.net/npm/jotai@2.4.2/react.js
  27. // @resource link:jotai/react/utils https://cdn.jsdelivr.net/npm/jotai@2.4.2/react/utils.js
  28. // @resource link:jotai/utils https://cdn.jsdelivr.net/npm/jotai@2.4.2/utils.js
  29. // @resource link:jotai/vanilla https://cdn.jsdelivr.net/npm/jotai@2.4.2/vanilla.js
  30. // @resource link:jotai/vanilla/utils https://cdn.jsdelivr.net/npm/jotai@2.4.2/vanilla/utils.js
  31. // @resource link:react https://cdn.jsdelivr.net/npm/react@18.2.0/cjs/react.production.min.js
  32. // @resource link:react-dom https://cdn.jsdelivr.net/npm/react-dom@18.2.0/cjs/react-dom.production.min.js
  33. // @resource link:react-toastify https://cdn.jsdelivr.net/npm/react-toastify@9.1.3/dist/react-toastify.js
  34. // @resource link:scheduler https://cdn.jsdelivr.net/npm/scheduler@0.23.0/cjs/scheduler.production.min.js
  35. // @resource link:vcv-inject-node-env data:,unsafeWindow.process=%7Benv:%7BNODE_ENV:%22production%22%7D%7D
  36. // @resource link:vim_comic_viewer https://update.greasyfork.org/scripts/417893/1280465/vim%20comic%20viewer.js
  37. // @resource react-toastify-css https://cdn.jsdelivr.net/npm/react-toastify@9.1.3/dist/ReactToastify.css
  38. // ==/UserScript==
  39. "use strict";
  40.  
  41. define("main", (require, exports, module) => {
  42. var import_vim_comic_viewer = require("vim_comic_viewer");
  43. async function main() {
  44. const isToki = location.origin.match(/manatoki|newtoki/);
  45. if (!isToki) {
  46. return;
  47. }
  48. markVisitedLinks();
  49. const buttons = duplicateViewerButton();
  50. const controller = await (0, import_vim_comic_viewer.initialize)({ source: comicSource });
  51. for (const button of buttons) {
  52. button.addEventListener("click", async () => {
  53. await controller.setImmersive(true);
  54. });
  55. }
  56. }
  57. function duplicateViewerButton() {
  58. const template = document.createElement("template");
  59. template.innerHTML = `<a class="show_viewer" alt="뷰어로 보기">
  60. <i class="ion-ios-book at-tip" aria-hidden="true" style="color: blue;"></i>
  61. </a>`;
  62. const templateButton = template.content.firstElementChild;
  63. const buttons = [];
  64. const divs = document.querySelectorAll(".toon-nav");
  65. for (const div of divs) {
  66. const button = templateButton.cloneNode(true);
  67. div.prepend(button);
  68. buttons.push(button);
  69. }
  70. return buttons;
  71. }
  72. function comicSource() {
  73. registerEpisodeNavigator();
  74. return getUrls();
  75. }
  76. function registerEpisodeNavigator() {
  77. addEventListener("keydown", (event) => {
  78. const { ctrlKey, shiftKey, altKey } = event;
  79. if (ctrlKey || shiftKey || altKey || import_vim_comic_viewer.utils.isTyping(event)) {
  80. return;
  81. }
  82. switch (event.key) {
  83. case "h":
  84. case "ArrowLeft":
  85. document.getElementById("goPrevBtn")?.click?.();
  86. break;
  87. case "l":
  88. case "ArrowRight":
  89. document.getElementById("goNextBtn")?.click?.();
  90. break;
  91. case "t":
  92. document.getElementById("sticky-wrapper")?.scrollIntoView({
  93. block: "center"
  94. });
  95. break;
  96. case "m":
  97. document.querySelector(".view-good")?.scrollIntoView({
  98. block: "center"
  99. });
  100. break;
  101. }
  102. });
  103. }
  104. function getUrls() {
  105. const imgs = document.querySelectorAll(
  106. "div.view-padding img"
  107. );
  108. const urls = [...imgs].flatMap(getUrl);
  109. return urls;
  110. }
  111. function getUrl(image) {
  112. if (image.offsetParent === null) {
  113. return [];
  114. }
  115. const data = Object.values(image.dataset);
  116. return data.length ? data : [image.src];
  117. }
  118. function markVisitedLinks() {
  119. const links = document.querySelectorAll(".post-row a");
  120. const visitedLinks = new Set(GM_getValue("visitedPaths", []));
  121. for (const link of links) {
  122. const url = link.getAttribute("href");
  123. if (!url)
  124. return;
  125. const path = new URL(url).pathname;
  126. if (visitedLinks.has(path)) {
  127. link.style.color = "#e2e2e2";
  128. }
  129. link.addEventListener("click", () => {
  130. visitedLinks.add(path);
  131. GM_setValue("visitedPaths", [...visitedLinks]);
  132. });
  133. }
  134. }
  135. main();
  136.  
  137. });
  138.  
  139. define("tampermonkey_grants", function() { Object.assign(this.window, { GM, GM_getResourceText, GM_getValue, GM_setValue, GM_xmlhttpRequest, unsafeWindow }); });
  140. requirejs.config({ deps: ["tampermonkey_grants"] });
  141. for (const { name } of GM.info.script.resources.filter(x => x.name.startsWith("link:"))) {
  142. define(name.replace("link:", ""), Function("require", "exports", "module", GM_getResourceText(name)));
  143. }
  144.  
  145. require(["main"], () => {}, console.error);