A Simple Web Navigation Enhancer

You can quickly access the previous and next episodes, perform smooth scrolling up or down, and even enable or disable full-screen mode. This script is designed to enhance the reading experience of web content in a more convenient and customizable.

  1. // ==UserScript==
  2. // @name A Simple Web Navigation Enhancer
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.5.3
  5. // @description You can quickly access the previous and next episodes, perform smooth scrolling up or down, and even enable or disable full-screen mode. This script is designed to enhance the reading experience of web content in a more convenient and customizable.
  6. // @match https://westmanga.me/*
  7. // @match https://komikcast02.com/*
  8. // @match https://aquareader.net/*
  9. // @match https://www.webtoons.com/*
  10. // @match https://kiryuu01.com/*
  11. // @match https://mangaku.lat/*
  12. // @match https://manhwatop.com/*
  13. // @match https://komiku.id/*
  14. // @match https://www.mikoroku.com/*
  15. // @match https://mangadex.org/chapter/*
  16. // @match https://mangatoto.com/*
  17. // @grant none
  18. // @require https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js
  19. // @license MIT
  20. // ==/UserScript==
  21.  
  22. (function() {
  23. 'use strict';
  24.  
  25. var isFullscreen = false;
  26. var activeElement;
  27.  
  28. let scrollingUp = false; // Is "W" key held down?
  29. let scrollingDown = false; // Is "S" key held down?
  30. let momentumUp = 0; // Momentum for scrolling up
  31. let momentumDown = 0; // Momentum for scrolling down
  32. const maxMomentum = 80; // Maximum scroll speed
  33. const momentumDecay = 0.0; // Rate of momentum decay
  34. let scrollInterval; // Reference to the scroll loop
  35.  
  36. const HOSTS = {
  37. 'westmanga.me': {
  38. next: 'div.max-w-screen-xl:nth-child(7) > div:nth-child(1) > div:nth-child(1) > div:nth-child(2) > button:nth-child(2)',
  39. prev: 'div.max-w-screen-xl:nth-child(7) > div:nth-child(1) > div:nth-child(1) > div:nth-child(2) > button:nth-child(1)'
  40. },
  41. 'komikcast02.com': {
  42. next: '.nextprev a[rel="next"]',
  43. prev: '.nextprev a[rel="prev"]'
  44. },
  45. 'www.webtoons.com': {
  46. next: '.paginate .pg_next',
  47. prev: '.paginate .pg_prev'
  48. },
  49. 'aquareader.net': {
  50. next: 'a.btn.next_page',
  51. prev: 'a.btn.prev_page'
  52. },
  53. 'kiryuu01.com': {
  54. next: 'a.ch-next-btn',
  55. prev: 'a.ch-prev-btn'
  56. },
  57. 'mangaku.lat': {
  58. prev: 'button.glho.glkp_1:-soup-contains("PREV")',
  59. next: 'button.glho.glkn_1:-soup-contains("NEXT")'
  60. },
  61. 'manhwatop.com': {
  62. prev: '.prev_page',
  63. next: '.next_page'
  64. },
  65. 'komiku.id': {
  66. prev: 'div.nxpr > a.rl:first-of-type',
  67. next: 'div.nxpr > a.rl:last-of-type'
  68. },
  69. 'mangadex.org': {
  70. prev: '',
  71. next: ''
  72. },
  73. 'mangatoto.com': {
  74. prev: 'a.btn.btn-sm.btn-outline.btn-primary:nth-of-type(1)',
  75. next: 'a.btn.btn-sm.btn-outline.btn-primary:nth-of-type(2)'
  76. },
  77. 'www.mikoroku.com': {
  78. prev: 'a[rel="prev"][type="button"]',
  79. next: 'a[rel="next"][type="button"]'
  80. }
  81. };
  82.  
  83. let btnNext, btnPrev;
  84. let host = window.location.host;
  85. if (HOSTS[host]) {
  86. btnNext = HOSTS[host].next;
  87. btnPrev = HOSTS[host].prev;
  88. }
  89.  
  90. const zoomLevel = () => {
  91. return document.body.style.zoom || 1;
  92. }
  93.  
  94. // Function to start scrolling (up or down)
  95. function startScrolling(direction) {
  96. if ((direction === "up" && !scrollingUp) || (direction === "down" && !scrollingDown)) {
  97. if (direction === "up") scrollingUp = true;
  98. if (direction === "down") scrollingDown = true;
  99.  
  100. clearInterval(scrollInterval); // Clear any existing scroll loop
  101.  
  102. // Momentum-based scrolling loop
  103. scrollInterval = setInterval(() => {
  104. // Increase momentum if scrolling in a direction
  105. if (scrollingUp) {
  106. momentumUp = Math.min(momentumUp + 8, maxMomentum);
  107. } else {
  108. momentumUp *= momentumDecay; // Decay momentum if "W" is released
  109. }
  110.  
  111. if (scrollingDown) {
  112. momentumDown = Math.min(momentumDown + 8, maxMomentum);
  113. } else {
  114. momentumDown *= momentumDecay; // Decay momentum if "S" is released
  115. }
  116.  
  117. // Stop the interval if both momentums are negligible
  118. if (momentumUp < 1 && momentumDown < 1) {
  119. clearInterval(scrollInterval);
  120. return;
  121. }
  122.  
  123. // Perform the scroll action
  124. const netMomentum = momentumDown - momentumUp; // Net scrolling direction
  125. $('html, body').scrollTop($(window).scrollTop() + netMomentum);
  126. }, 30); // Adjust interval for smooth updates
  127. }
  128. }
  129.  
  130. // Function to stop scrolling (up or down)
  131. function stopScrolling(direction) {
  132. if (direction === "up") scrollingUp = false;
  133. if (direction === "down") scrollingDown = false;
  134. }
  135.  
  136. // Event listener for "keydown"
  137. $(document).on('keydown', function (e) {
  138. if ((e.key === "w" || e.key === "W") && !scrollingUp && !e.ctrlKey && !e.altKey && e.key !== 'Tab') {
  139. // Periksa elemen yang sedang dalam fokus
  140. activeElement = document.activeElement;
  141. if (!(activeElement && (activeElement.tagName === 'INPUT' || activeElement.tagName === 'TEXTAREA'))) {
  142. // Pengguna tidak sedang mengetik
  143. e.preventDefault(); // Prevent default behavior
  144. startScrolling("up"); // Start scrolling up
  145. }
  146. }
  147. if ((e.key === "s" || e.key === "S") && !scrollingDown && !e.ctrlKey && !e.altKey && e.key !== 'Tab') {
  148. // Periksa elemen yang sedang dalam fokus
  149. activeElement = document.activeElement;
  150. if (!(activeElement && (activeElement.tagName === 'INPUT' || activeElement.tagName === 'TEXTAREA'))) {
  151. // Pengguna tidak sedang mengetik
  152. e.preventDefault(); // Prevent default behavior
  153. startScrolling("down"); // Start scrolling down
  154. }
  155. }
  156. });
  157.  
  158. // Event listener for "keyup"
  159. $(document).on('keyup', function (e) {
  160. if ((e.key === "w" || e.key === "W") && !e.ctrlKey && !e.altKey && e.key !== 'Tab') {
  161. // Periksa elemen yang sedang dalam fokus
  162. activeElement = document.activeElement;
  163. if (!(activeElement && (activeElement.tagName === 'INPUT' || activeElement.tagName === 'TEXTAREA'))) {
  164. // Pengguna tidak sedang mengetik
  165. e.preventDefault(); // Prevent default behavior
  166. stopScrolling("up"); // Stop scrolling up
  167. }
  168. }
  169. if ((e.key === "s" || e.key === "S") && !e.ctrlKey && !e.altKey && e.key !== 'Tab') {
  170. // Periksa elemen yang sedang dalam fokus
  171. activeElement = document.activeElement;
  172. if (!(activeElement && (activeElement.tagName === 'INPUT' || activeElement.tagName === 'TEXTAREA'))) {
  173. // Pengguna tidak sedang mengetik
  174. e.preventDefault(); // Prevent default behavior
  175. stopScrolling("down"); // Stop scrolling down
  176. }
  177. }
  178. });
  179.  
  180. document.addEventListener('keydown', function(event) {
  181. if ((event.key === 'a' || event.key === 'A' || event.key === 'ArrowLeft') && !event.ctrlKey && !event.altKey && event.key !== 'Tab') {
  182. let prevButton = document.querySelector(btnPrev);
  183. if (prevButton) {
  184. // Periksa elemen yang sedang dalam fokus
  185. activeElement = document.activeElement;
  186. if (!(activeElement && (activeElement.tagName === 'INPUT' || activeElement.tagName === 'TEXTAREA'))) {
  187. // Pengguna tidak sedang mengetik
  188. prevButton.click();
  189. }
  190. }
  191. } else if ((event.key === 'd' || event.key === 'D' || event.key === 'ArrowRight') && !event.ctrlKey && !event.altKey && event.key !== 'Tab') {
  192. let nextButton = document.querySelector(btnNext);
  193. if (nextButton) {
  194. // Periksa elemen yang sedang dalam fokus
  195. activeElement = document.activeElement;
  196. if (!(activeElement && (activeElement.tagName === 'INPUT' || activeElement.tagName === 'TEXTAREA'))) {
  197. // Pengguna tidak sedang mengetik
  198. nextButton.click();
  199. }
  200. }
  201. } else if ((event.key === 'f' || event.key === 'F') && !event.ctrlKey && !event.altKey && event.key !== 'Tab') {
  202. // Periksa elemen yang sedang dalam fokus
  203. activeElement = document.activeElement;
  204. if (!(activeElement && (activeElement.tagName === 'INPUT' || activeElement.tagName === 'TEXTAREA'))) {
  205. // Pengguna tidak sedang mengetik, masuk ke mode fullscreen
  206. event.preventDefault();
  207. toggleFullscreen(); // Fungsi Anda untuk masuk/keluar dari mode fullscreen
  208. }
  209. } else if ((event.key === 'q' || event.key === 'Q') && !event.ctrlKey && !event.altKey && event.key !== 'Tab') {
  210. var allChapterButton;
  211. if (window.location.host === 'westmanga.me') {
  212. allChapterButton = document.querySelector('.text-primary');
  213. } else if (window.location.host === 'www.webtoons.com') {
  214. allChapterButton = document.querySelector('.subj_info .subj');
  215. } else if (window.location.host === 'kiryuu01.com') {
  216. allChapterButton = document.querySelector('.headpost .allc a');
  217. } else if (window.location.host === 'komikcast02.com') {
  218. allChapterButton = document.querySelector('div.allc a');
  219. } else if (window.location.host === 'manhwatop.com') {
  220. allChapterButton = document.querySelector('ol.breadcrumb li:nth-child(2) a');
  221. } else if (window.location.host === 'www.mikoroku.com') {
  222. allChapterButton = document.querySelector('a[rel="home"][type="button"]');
  223. } else if (window.location.host === 'mangatoto.com') {
  224. allChapterButton = document.querySelector('h3.text-xl.font-bold.space-x-2 > a.link-pri.link-hover');
  225. } else if (window.location.host === 'aquareader.net') {
  226. allChapterButton = document.querySelector('.breadcrumb > li:nth-child(2) > a:nth-child(1)');
  227. }
  228. if (allChapterButton) {
  229. // Periksa elemen yang sedang dalam fokus
  230. activeElement = document.activeElement;
  231. if (!(activeElement && (activeElement.tagName === 'INPUT' || activeElement.tagName === 'TEXTAREA'))) {
  232. // Pengguna tidak sedang mengetik
  233. allChapterButton.click();
  234. }
  235. }
  236. }
  237. });
  238.  
  239. function toggleFullscreen() {
  240. if (!isFullscreen) {
  241. enterFullscreen();
  242. } else {
  243. exitFullscreen();
  244. }
  245. isFullscreen = !isFullscreen;
  246. }
  247.  
  248. function enterFullscreen() {
  249. const elem = document.documentElement;
  250. if (elem.requestFullscreen) {
  251. elem.requestFullscreen();
  252. } else if (elem.mozRequestFullScreen) {
  253. elem.mozRequestFullScreen();
  254. } else if (elem.webkitRequestFullscreen) {
  255. elem.webkitRequestFullscreen();
  256. } else if (elem.msRequestFullscreen) {
  257. elem.msRequestFullscreen();
  258. }
  259. }
  260.  
  261. function exitFullscreen() {
  262. if (document.exitFullscreen) {
  263. document.exitFullscreen();
  264. } else if (document.mozCancelFullScreen) {
  265. document.mozCancelFullScreen();
  266. } else if (document.webkitExitFullscreen) {
  267. document.webkitExitFullscreen();
  268. } else if (document.msExitFullscreen) {
  269. document.msExitFullscreen();
  270. }
  271. }
  272. })();