按鍵與滑鼠滾輪翻頁器

使用滑鼠滾輪或按鍵快速切換上下頁。

安装此脚本?
作者推荐脚本

您可能也喜欢隱藏youtube片尾畫面

安装此脚本
  1. // ==UserScript==
  2. // @name 按鍵與滑鼠滾輪翻頁器
  3. // @name:zh-TW 按鍵與滑鼠滾輪翻頁器
  4. // @name:ja キーとマウスホイールでのページめくり機
  5. // @name:en Keyboard and Mouse Wheel Page Turner
  6. // @name:ko 키보드 및 마우스 휠 페이지 전환기
  7. // @name:es Navegador de Páginas con Teclado y Rueda del Ratón
  8. // @namespace https://github.com/Max46656
  9. // @version 1.2.8
  10. // @description 使用滑鼠滾輪或按鍵快速切換上下頁。
  11. // @description:zh-TW 使用滑鼠滾輪或按鍵快速切換上下頁。
  12. // @description:ja マウスホイールをスクロールするか、キーを押すことで、簡単にページを上下に切り替えることができます。
  13. // @description:en Quickly navigate between pages by scrolling the mouse wheel or pressing keys.
  14. // @description:ko 마우스 휠을 스크롤하거나 키를 눌러 페이지를 쉽게 전환할 수 있습니다.
  15. // @description:es Navega rápidamente entre páginas desplazando la rueda del ratón o presionando teclas.
  16. // @author Max
  17. // @match https://*/*
  18. // @icon https://www.google.com/s2/favicons?sz=64&domain=pixiv.net
  19. // @grant GM_registerMenuCommand
  20. // @grant GM_setValue
  21. // @grant GM_getValue
  22. // @grant GM.info
  23. // @license MPL2.0
  24. // ==/UserScript==
  25.  
  26.  
  27. class PageButtonManager {
  28. constructor() {
  29. this.pageButtonsMap = {};
  30. this.domain = window.location.hostname;
  31. this.loadPageButtons();
  32. }
  33.  
  34. loadPageButtons() {
  35. this.pageButtonsMap = GM_getValue('pageButtonsMap', {});
  36. }
  37.  
  38. async savePageButtons() {
  39. await GM_setValue('pageButtonsMap', this.pageButtonsMap);
  40. }
  41.  
  42. getButtonsByCommonCases() {
  43. let buttonsByUserSetting = this.getButtonsByDomain();
  44. if (buttonsByUserSetting !== null) {
  45. return buttonsByUserSetting;
  46. }
  47. let nextSelectorList = [
  48. "a.next",
  49. "a#next",
  50. ".next>a",
  51. ".next>button",
  52. "a[alt=next]",
  53. ".page-next>a",
  54. "a.next_page",
  55. "#next_page",
  56. ".curPage+a",
  57. ".nextPage",
  58. ".pagination-next>a",
  59. ".pagination>.active+a",
  60. "a[data-pagination=next]",
  61. ".pageButtonsCurrent+a",
  62. "a[class*=nextpage]",
  63. "li.page-current+li>a",
  64. "[class^=pag] a[rel=next]",
  65. "[class^=Pag] [aria-label=next]",
  66. "[class^=Pag] [aria-label=Next]",
  67. "[aria-label='Next Page']",
  68. "[aria-label='Next page']",
  69. "[aria-label$='next page']",
  70. ".pagination-nav__item--next>a",
  71. "a.pageright",
  72. ".pager_on+a.pager",
  73. ".pager__next>a",
  74. ".page-numbers.current+a",
  75. "a.page-numbers.next",
  76. "body [class*=paginat] li.active+span+li>a",
  77. "body [class*=paginat] li.active+li>a",
  78. "body [class^=pag] .current+a",
  79. "body [class*=-pag] .current+a",
  80. ".page_current+a",
  81. "input[value='next']",
  82. "input[value='Next page']",
  83. "input[value='下一頁']",
  84. "input[value='下一页']",
  85. "a#pb_next",
  86. "a#rightFix",
  87. "a#btnPreGn",
  88. "a.page-next",
  89. "a.pages-next",
  90. "a.page.right",
  91. ".paging>.active+.item",
  92. ".pg_area>em+a",
  93. "button.next:not([disabled])",
  94. ".btn_next:not([disabled])",
  95. ".btn-next:not([disabled])",
  96. "a#linkNext",
  97. "body a[class*=page__next]",
  98. "body [class*=pager]>a.next",
  99. "body [class*=pagination-next]>a",
  100. "body [class*=pagination-next]>button",
  101. "body [class*=page--current]+li>a",
  102. "body [class*=Pages]>.curr+a",
  103. "body [class*=page]>.cur+a",
  104. "body [class*=paginat] [class*=current]+li>a",
  105. "body [class*=paginat] [class*=next-next]",
  106. "body [class*=paginat] [class*=next]",
  107. "body [class*=paginat] [class*=right]",
  108. ".page>em+a",
  109. "[name*=nextPage]",
  110. "a:has(polyline[points='1,2 5,6 9,2'])", //箭頭polyline
  111. //以下未測試
  112. "a.nav-next:not([disabled])",
  113. "button.pagination-arrow-right",
  114. "[data-page-direction='next']",
  115. ".carousel-control-next",
  116. "a.pagination-link[rel='next']",
  117. ".nav-item.next-item",
  118. "button.btn-arrow-right:not([disabled])",
  119. // ARIA 無障礙設計
  120. "[aria-label='Go to next']",
  121. "[role='button'][aria-label='Next page']:not([aria-disabled='true'])",
  122. "[aria-label='下一頁面']",
  123. "[aria-label='次のページ']",
  124. "[aria-label='Página siguiente']",
  125. // Icon
  126. ".next-btn > svg[class*='arrow-right']",
  127. "button > i[class*='chevron-right']:not([disabled])",
  128. "a > span[class*='icon-forward']",
  129. // XPath:文字與結構檢查
  130. "//button[contains(@class, 'Page')][text()='Next']",
  131. "//button[contains(@class, 'page')][text()='next']",
  132. "//a[contains(@class, 'next') and not(@aria-disabled='true')]",
  133. "//button[contains(text(), '下一頁')]",
  134. "//a[contains(text(), '次へ')]",
  135. "//div[contains(@class, 'pagination')]//a[text()='>']",
  136. "//button[contains(@class, 'btn') and text()='Suivant']", // 法文
  137. "//a[contains(@class, 'nav') and text()='Siguiente']", // 西班牙文
  138. "//li[contains(@class, 'current')]/following-sibling::li[1]/a", //可能有問題
  139. ];
  140. let prevSelectorList = [
  141. "a.previous",
  142. "a.prev",
  143. "a#prev",
  144. ".prev>a",
  145. ".prev>button",
  146. "a[alt=prev]",
  147. ".page-prev>a",
  148. "a.prev_page",
  149. "#prev_page",
  150. "//*[contains(@class, 'pag')]//*[@class='curPage']/preceding-sibling::*[1]/a", // 原 .curPage~a
  151. ".prevPage",
  152. ".pagination-prev>a",
  153. "//*[contains(@class, 'pagination')]//*[@class='active']/preceding-sibling::*[1]/a", // 原 .pagination>.active~a
  154. "a[data-pagination=prev]",
  155. "//*[contains(@class, 'pag')]//*[@class='pageButtonsCurrent']/preceding-sibling::*[1]/a", // 原 .pageButtonsCurrent~a
  156. "a[class*=prevpage]",
  157. "//li[contains(@class, 'page-current')]/preceding-sibling::li[1]/a", // 原 li.page-current~li>a
  158. "[class^=pag] a[rel=prev]",
  159. "[class^=Pag] [aria-label=prev]",
  160. "[class^=Pag] [aria-label=Prev]",
  161. "[aria-label='Previous Page']",
  162. "[aria-label='Previous page']",
  163. "[aria-label$='previous page']",
  164. ".pagination-nav__item--next>a",
  165. "a.pageleft",
  166. "//*[contains(@class, 'pager_on')]//*[@class='pager']/preceding-sibling::*[1]/a", // 原 .pager_on~a.pager
  167. ".pager__prev>a",
  168. "//*[contains(@class, 'page-numbers')]//*[@class='current']/preceding-sibling::*[1]/a", // 原 .page-numbers.current~a
  169. "a.page-numbers.prev",
  170. "//*[contains(@class, 'paginat')]//li[contains(@class, 'active')]/preceding-sibling::span[1]/preceding-sibling::li[1]/a", // 原 body [class*=paginat] li.active~span~li>a
  171. "//*[contains(@class, 'paginat')]//li[contains(@class, 'active')]/preceding-sibling::li[1]/a", // 原 body [class*=paginat] li.active~li>a
  172. "//body/*[contains(@class, 'pag')]//*[@class='current']/preceding-sibling::*[1]/a", // 原 body [class^=pag] .current~a
  173. "//body/*[contains(@class, '-pag')]//*[@class='current']/preceding-sibling::*[1]/a", // 原 body [class*=-pag] .current~a
  174. "//*[contains(@class, 'page_current')]/preceding-sibling::*[1]/a", // 原 .page_current~a
  175. "input[value='prev']",
  176. "input[value='Previous page']",
  177. "input[value='上一頁']",
  178. "a#pb_prev",
  179. "a#leftFix",
  180. "a#btnPreGp",
  181. "a.page-prev",
  182. "a.pages-prev",
  183. "a.page.left",
  184. "//*[contains(@class, 'paging')]//*[@class='active']/preceding-sibling::*[1]/*[contains(@class, 'item')]", // 原 .paging>.active~.item
  185. "//*[contains(@class, 'pg_area')]//em/preceding-sibling::*[1]/a", // 原 .pg_area>em~a
  186. "button.prev:not([disabled])",
  187. ".btn_prev:not([disabled])",
  188. ".btn-prev:not([disabled])",
  189. "a#linkPrev",
  190. "body a[class*=page__prev]",
  191. "body [class*=pager]>a.prev",
  192. "body [class*=pagination-prev]>a",
  193. "body [class*=pagination-prev]>button",
  194. "//body/*[contains(@class, 'page--current')]/preceding-sibling::li[1]/a", // 原 body [class*=page--current]~li>a
  195. "//body/*[contains(@class, 'Pages')]//*[@class='curr']/preceding-sibling::*[1]/a", // 原 body [class*=Pages]>.curr~a
  196. "//body/*[contains(@class, 'page')]//*[@class='cur']/preceding-sibling::*[1]/a", // 原 body [class*=page]>.cur~a
  197. "//body/*[contains(@class, 'paginat')]//*[@class and contains(@class, 'current')]/preceding-sibling::li[1]/a", // 原 body [class*=paginat] [class*=current]~li>a
  198. "body [class*=paginat] [class*=prev-prev]",
  199. "body [class*=paginat] [class*=prev]",
  200. "body [class*=paginat] [class*=left]",
  201. "//*[contains(@class, 'page')]//em/preceding-sibling::*[1]/a", // 原 .page>em~a
  202. "[name*=prevPage]",
  203. "a:has(polyline[points='1,2 5,6 9,2'])", //箭頭polyline
  204. //以下未測試
  205. "a.nav-prev:not([disabled])",
  206. "button.pagination-arrow-left",
  207. "[data-page-direction='prev']",
  208. ".carousel-control-prev",
  209. "a.pagination-link[rel='prev']",
  210. ".nav-item.prev-item",
  211. "button.btn-arrow-left:not([disabled])",
  212. // ARIA 無障礙設計
  213. "[aria-label='Go to previous']",
  214. "[role='button'][aria-label='Previous page']:not([aria-disabled='true'])",
  215. "[aria-label='上一頁面']",
  216. "[aria-label='前のページ']",
  217. "[aria-label='Página anterior']", // 西班牙文
  218. // Icon
  219. ".prev-btn > svg[class*='arrow-left']",
  220. "button > i[class*='chevron-left']:not([disabled])",
  221. "a > span[class*='icon-back']",
  222. // XPath:文字與結構檢查
  223. "//button[contains(@class, 'Page')][text()='Previous']",
  224. "//button[contains(@class, 'page')][text()='previous']",
  225. "//a[contains(@class, 'prev') and not(@aria-disabled='true')]",
  226. "//button[contains(text(), '上一頁')]",
  227. "//a[contains(text(), '前へ')]",
  228. "//div[contains(@class, 'pagination')]//a[text()='<']",
  229. "//button[contains(@class, 'btn') and text()='Précédent']", // 法文
  230. "//a[contains(@class, 'nav') and text()='Anterior']", // 西班牙文
  231. "//li[contains(@class, 'current')]/preceding-sibling::li[1]/a",
  232. ];
  233. let prevButton;
  234. let prevSelector;
  235. for (let selector of prevSelectorList) {
  236. if (selector.startsWith('//')) {
  237. let result = document.evaluate(selector, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  238. if (result.snapshotLength >= 1) {
  239. prevButton = result.snapshotItem(0);
  240. console.log(`${GM_info.script.name}: prev XPathSelector:`,selector);
  241. prevSelector = selector;
  242. break;
  243. }
  244. } else {
  245. let elements = document.querySelectorAll(selector);
  246. if (elements.length >= 1) {
  247. prevButton = elements[0];
  248. console.log(`${GM_info.script.name}: prev CSSSelector:`,selector);
  249. prevSelector = selector;
  250. break;
  251. }
  252. }
  253. }
  254.  
  255. let nextButton;
  256. let nextSelcetor;
  257. for (let selector of nextSelectorList) {
  258. if (selector.startsWith('//')) {
  259. let result = document.evaluate(selector, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  260. if (result.snapshotLength >= 1) {
  261. nextButton = result.snapshotItem(result.snapshotLength - 1);
  262. console.log(`${GM_info.script.name}: next XPathSelector:`,selector);
  263. nextSelcetor = selector;
  264. break;
  265. }
  266. } else {
  267. let elements = document.querySelectorAll(selector);
  268. if (elements.length >= 1) {
  269. nextButton = elements[elements.length - 1];
  270. console.log(`${GM_info.script.name}: next XPathSelector:`,selector);
  271. nextSelcetor = selector;
  272. break;
  273. }
  274. }
  275. }
  276. console.log(`${GM_info.script.name}: prevButton,nextButton`,[prevButton,nextButton]);
  277. if(prevButton == null && nextButton == null){
  278. console.error(`${GM_info.script.name} : 該網站不使用常見元素,請手動設定CSSXPath選取器以設定上下頁元素`)
  279. }
  280. return {"prevSelector":prevSelector,"nextSelcetor":nextSelcetor,"prev":prevButton,"next":nextButton};
  281. }
  282.  
  283. getButtonsByDomain() {
  284. let pageButtons = {"prev":null,"next":null};
  285. if(this.pageButtonsMap[this.domain]===undefined){
  286. return null;
  287. }
  288. const prevSelector = this.pageButtonsMap[this.domain].prevButton;
  289. const nextSelector = this.pageButtonsMap[this.domain].nextButton;
  290. if (prevSelector.startsWith('//')) {
  291. let xPathResult = document.evaluate(prevSelector, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  292. if (xPathResult.snapshotLength >= 0) {
  293. pageButtons.prev = xPathResult.snapshotItem(0);
  294. }
  295. } else {
  296. let elements = document.querySelectorAll(prevSelector);
  297. if (elements.length >= 0) {
  298. pageButtons.prev = elements[0];
  299. }
  300. }
  301.  
  302. if (nextSelector.startsWith('//')) {
  303. let xPathResult = document.evaluate(nextSelector, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  304. if (xPathResult.snapshotLength >= 0) {
  305. pageButtons.next = xPathResult.snapshotItem(xPathResult.snapshotLength - 1);
  306. }
  307. } else {
  308. let elements = document.querySelectorAll(nextSelector);
  309. if (elements.length >= 0) {
  310. pageButtons.next = elements[elements.length - 1];
  311. }
  312. }
  313.  
  314. return pageButtons;
  315. }
  316.  
  317. getSelectorByDomain(domain){
  318. return this.pageButtonsMap[domain];
  319. }
  320.  
  321. setButtonsForDomain(buttons) {
  322. this.pageButtonsMap[this.domain] = buttons;
  323. this.savePageButtons();
  324. }
  325.  
  326. getAllDomains() {
  327. return Object.keys(this.pageButtonsMap);
  328. }
  329. }
  330.  
  331. class NavigationPaginationWithInput {
  332. constructor(buttonManager) {
  333. this.buttonManager = buttonManager;
  334. this.init();
  335. }
  336.  
  337. async init() {
  338. //this.buttonManager.getButtonsByCommonCases();
  339. await this.loadSettings();
  340. this.setEventListeners();
  341. }
  342.  
  343. async loadSettings() {
  344. this.togglePaginationMode = await GM_getValue('togglePaginationMode', 'key');
  345. this.modifierKey = await GM_getValue('modifierKey', 'CapsLock');
  346. this.nextPageKey = await GM_getValue('nextPageKey', 'W');
  347. this.prevPageKey = await GM_getValue('prevPageKey', 'Q');
  348. this.saveSettings();
  349. }
  350.  
  351. async saveSettings() {
  352. await GM_setValue('togglePaginationMode', this.togglePaginationMode);
  353. await GM_setValue('modifierKey', this.modifierKey);
  354. await GM_setValue('nextPageKey', this.nextPageKey);
  355. await GM_setValue('prevPageKey', this.prevPageKey);
  356. console.group(`${GM_info.script.name} Setting: `);
  357. console.log("togglePaginationMode",this.togglePaginationMode);
  358. console.log("modifierKey",this.modifierKey);
  359. console.log("nextPageKey",this.nextPageKey);
  360. console.log("prevPageKey",this.prevPageKey);
  361. console.groupEnd();
  362. }
  363.  
  364. toNextPage() {
  365. this.pageButtons = this.buttonManager.getButtonsByCommonCases();
  366. this.pageButtons.next.click();
  367. }
  368.  
  369. toPrevPage() {
  370. this.pageButtons = this.buttonManager.getButtonsByCommonCases();
  371. this.pageButtons.prev.click();
  372. }
  373.  
  374. setEventListeners() {
  375. this.scrollHandler = () => this.handleScroll();
  376. this.keydownHandler = (event) => this.handleKeydown(event);
  377.  
  378. if (this.togglePaginationMode !== "key") {
  379. self.addEventListener("scroll", this.scrollHandler);
  380. } else {
  381. self.addEventListener("keydown", this.keydownHandler);
  382. }
  383. }
  384.  
  385. handleScroll(scrollThreshold=3) {
  386. const isBottom = document.documentElement.scrollHeight - self.innerHeight - self.pageYOffset <= scrollThreshold;
  387. if (isBottom) {
  388. this.toNextPage();
  389. //console.log("滾輪下一頁");
  390. }
  391. if (self.pageYOffset <= 0) {
  392. this.toPrevPage();
  393. //console.log("滾輪上一頁");
  394. }
  395. }
  396.  
  397. handleKeydown(event) {
  398. if (event.getModifierState(this.modifierKey)) {
  399. if (event.key.toUpperCase() === this.nextPageKey.toUpperCase()) {
  400. event.preventDefault();
  401. this.toNextPage();
  402. //console.log("快捷鍵下一頁");
  403. } else if (event.key.toUpperCase() === this.prevPageKey.toUpperCase()) {
  404. event.preventDefault();
  405. this.toPrevPage();
  406. //console.log("快捷鍵上一頁");
  407. }
  408. }
  409. }
  410. }
  411.  
  412. class MenuManager {
  413. constructor(buttonManager,navigation) {
  414. this.buttonManager = buttonManager;
  415. this.navigation = navigation;
  416. this.initMenu();
  417. }
  418.  
  419. getMenuLabels() {
  420. const userLang = navigator.language || navigator.userLanguage;
  421. const labels = {
  422. 'zh-TW': {
  423. viewAndModify: '修改上下頁的按鈕元素選取器',
  424. showAllDomains: '顯示所有網域',
  425. togglePageMode: '切換翻頁模式',
  426. customizeModifierKey: '自訂啟動快捷鍵',
  427. customizeNextPageKey: '自訂下一頁快捷鍵',
  428. customizePrevPageKey: '自訂上一頁快捷鍵',
  429. enterDomain: '輸入網域:',
  430. currentButtons: '當前按鈕:',
  431. enterNextButton: '輸入下一頁按鈕選擇器:',
  432. enterPrevButton: '輸入上一頁按鈕選擇器:',
  433. savedDomains: '已儲存的網域:',
  434. enterDomainToView: '輸入要檢視的網域:',
  435. enterModifierKey: '輸入啟動快捷鍵 (Control, Alt, Shift, CapsLock):',
  436. enterNextPageKey: '輸入下一頁快捷鍵:',
  437. enterPrevPageKey: '輸入上一頁快捷鍵:',
  438. invalidInput: '無效的輸入,請重試。'
  439. },
  440. 'en': {
  441. viewAndModify: 'Modify Page Up/Down Button Selectors',
  442. showAllDomains: 'Show All Domains',
  443. togglePageMode: 'Toggle Page Mode',
  444. customizeModifierKey: 'Customize Modifier Key',
  445. customizeNextPageKey: 'Customize Next Page Key',
  446. customizePrevPageKey: 'Customize Previous Page Key',
  447. enterDomain: 'Enter the domain:',
  448. currentButtons: 'Current buttons:',
  449. enterNextButton: 'Enter the next page button selector:',
  450. enterPrevButton: 'Enter the previous page button selector:',
  451. savedDomains: 'Saved domains:',
  452. enterDomainToView: 'Enter the domain to view:',
  453. enterModifierKey: 'Enter modifier key (Control, Alt, Shift, CapsLock):',
  454. enterNextPageKey: 'Enter next page key:',
  455. enterPrevPageKey: 'Enter previous page key:',
  456. invalidInput: 'Invalid input, please try again.'
  457. },
  458. 'ja': {
  459. viewAndModify: 'ページの上下ボタン要素セレクターの変更',
  460. showAllDomains: 'すべてのドメインを表示',
  461. togglePageMode: 'ページモードを切り替える',
  462. customizeModifierKey: '修飾キーをカスタマイズ',
  463. customizeNextPageKey: '次のページキーをカスタマイズ',
  464. customizePrevPageKey: '前のページキーをカスタマイズ',
  465. enterDomain: 'ドメインを入力してください:',
  466. currentButtons: '現在のボタン:',
  467. enterNextButton: '次のページボタンのセレクタを入力してください:',
  468. enterPrevButton: '前のページボタンのセレクタを入力してください:',
  469. savedDomains: '保存されたドメイン:',
  470. enterDomainToView: '表示するドメインを入力してください:',
  471. enterModifierKey: '修飾キーを入力してください(Control、Alt、Shift、CapsLock):',
  472. enterNextPageKey: '次のページキーを入力してください:',
  473. enterPrevPageKey: '前のページキーを入力してください:',
  474. invalidInput: '無効な入力です。もう一度お試しください。'
  475. },
  476. 'ko': {
  477. viewAndModify: '페이지 위/아래 버튼 선택기 수정',
  478. showAllDomains: '모든 도메인 보기',
  479. togglePageMode: '페이지 모드 전환',
  480. customizeModifierKey: '수정 키 사용자화',
  481. customizeNextPageKey: '다음 페이지 키 사용자화',
  482. customizePrevPageKey: '이전 페이지 키 사용자화',
  483. enterDomain: '도메인을 입력하세요:',
  484. currentButtons: '현재 버튼:',
  485. enterNextButton: '다음 페이지 버튼 선택기 입력:',
  486. enterPrevButton: '이전 페이지 버튼 선택기 입력:',
  487. savedDomains: '저장된 도메인:',
  488. enterDomainToView: '보기할 도메인을 입력하세요:',
  489. enterModifierKey: '수정 키를 입력하세요 (Control, Alt, Shift, CapsLock):',
  490. enterNextPageKey: '다음 페이지 키 입력:',
  491. enterPrevPageKey: '이전 페이지 키 입력:',
  492. invalidInput: '잘못된 입력입니다. 다시 시도하세요.'
  493. },
  494. 'es': {
  495. viewAndModify: 'Modificar Selectores de Botones de Página Arriba/Abajo',
  496. showAllDomains: 'Mostrar Todos los Dominios',
  497. togglePageMode: 'Alternar Modo de Página',
  498. customizeModifierKey: 'Personalizar Tecla Modificadora',
  499. customizeNextPageKey: 'Personalizar Tecla de Siguiente Página',
  500. customizePrevPageKey: 'Personalizar Tecla de Página Anterior',
  501. enterDomain: 'Ingrese el dominio:',
  502. currentButtons: 'Botones actuales:',
  503. enterNextButton: 'Ingrese el selector del botón de siguiente página:',
  504. enterPrevButton: 'Ingrese el selector del botón de página anterior:',
  505. savedDomains: 'Dominios guardados:',
  506. enterDomainToView: 'Ingrese el dominio a visualizar:',
  507. enterModifierKey: 'Ingrese tecla modificadora (Control, Alt, Shift, CapsLock):',
  508. enterNextPageKey: 'Ingrese tecla de siguiente página:',
  509. enterPrevPageKey: 'Ingrese tecla de página anterior:',
  510. invalidInput: 'Entrada inválida, por favor intente de nuevo.'
  511. }
  512. };
  513. return labels[userLang] || labels.en;
  514. }
  515.  
  516. initMenu() {
  517. const labels = this.getMenuLabels();
  518. GM_registerMenuCommand(labels.viewAndModify, this.viewAndModifyButtons.bind(this));
  519. GM_registerMenuCommand(labels.showAllDomains, this.showAllDomains.bind(this));
  520. GM_registerMenuCommand(labels.togglePageMode, this.inputModeSwitch.bind(this));
  521. GM_registerMenuCommand(labels.customizeModifierKey, this.customizeModifierKey.bind(this));
  522. GM_registerMenuCommand(labels.customizeNextPageKey, this.customizeNextPageKey.bind(this));
  523. GM_registerMenuCommand(labels.customizePrevPageKey, this.customizePrevPageKey.bind(this));
  524. }
  525.  
  526. async viewAndModifyButtons() {
  527. const labels = this.getMenuLabels();
  528. //const domain = prompt(labels.enterDomain, window.location.hostname);
  529. const domain = window.location.hostname;
  530. if (domain !== null) {
  531. const currentButtons = this.buttonManager.getSelectorByDomain(domain);
  532. let newPrevButton;
  533. let newNextButton;
  534. if(currentButtons !== undefined){
  535. alert(`${labels.currentButtons}\nNext: ${currentButtons.nextButton}\nPrev: ${currentButtons.prevButton}`);
  536. newNextButton = prompt(labels.enterNextButton, currentButtons.nextButton);
  537. newPrevButton = prompt(labels.enterPrevButton, currentButtons.prevButton);
  538. }else{
  539. const aotoSelcetor = this.buttonManager.getButtonsByCommonCases()
  540. newNextButton = prompt(labels.enterNextButton, aotoSelcetor.nextSelcetor);
  541. newPrevButton = prompt(labels.enterPrevButton, aotoSelcetor.prevSelector);
  542. }
  543. if (newNextButton && newPrevButton) {
  544. this.buttonManager.setButtonsForDomain({ nextButton: newNextButton, prevButton: newPrevButton });
  545. alert(`Updated buttons for ${domain}`);
  546. }
  547.  
  548. }
  549. }
  550.  
  551. async showAllDomains() {
  552. const labels = this.getMenuLabels();
  553. const allDomains = this.buttonManager.getAllDomains();
  554. const domain = prompt(`${labels.savedDomains}\n${allDomains.join('\n')}\n\n${labels.enterDomainToView}`,window.location.hostname);
  555. if (domain) {
  556. const buttons = this.buttonManager.getSelectorByDomain(domain);
  557. if(buttons){
  558. alert(`本網域元素選擇器為 ${domain}:\nNext: ${buttons.nextButton}\nPrev: ${buttons.prevButton}`);
  559. }else{
  560. alert(`本網域沒有使用者設定的元素選擇器`);
  561. }
  562. }
  563. }
  564.  
  565. async inputModeSwitch() {
  566. if (this.navigation.togglePaginationMode === 'scroll') {
  567. this.navigation.togglePaginationMode = 'key';
  568. self.removeEventListener("scroll", this.navigation.scrollHandler);
  569. self.addEventListener("keydown", this.navigation.keydownHandler);
  570. console.log(`${GM_info.script.name}: 切換為按鍵翻頁模式`);
  571. } else {
  572. this.navigation.togglePaginationMode = 'scroll';
  573. self.addEventListener("scroll", this.navigation.scrollHandler);
  574. self.removeEventListener("keydown", this.navigation.keydownHandler);
  575. console.log(`${GM_info.script.name}: 切換為滾輪翻頁模式`);
  576. }
  577. await this.navigation.saveSettings();
  578. }
  579.  
  580. async customizeModifierKey() {
  581. const labels = this.getMenuLabels();
  582. const newModifierKey = prompt(labels.enterModifierKey, this.navigation.modifierKey);
  583. if (['Control', 'Alt', 'Shift', 'CapsLock'].includes(newModifierKey)) {
  584. this.navigation.modifierKey = newModifierKey;
  585. await this.navigation.saveSettings();
  586. } else {
  587. alert(labels.invalidInput);
  588. }
  589. }
  590.  
  591. async customizeNextPageKey() {
  592. const labels = this.getMenuLabels();
  593. const newNextPageKey = prompt(labels.enterNextPageKey, this.navigation.nextPageKey);
  594. if (newNextPageKey && newNextPageKey.length === 1) {
  595. this.navigation.nextPageKey = newNextPageKey;
  596. await this.navigation.saveSettings();
  597. } else {
  598. alert(labels.invalidInput);
  599. }
  600. }
  601.  
  602. async customizePrevPageKey() {
  603. const labels = this.getMenuLabels();
  604. const newPrevPageKey = prompt(labels.enterPrevPageKey, this.navigation.prevPageKey);
  605. if (newPrevPageKey && newPrevPageKey.length === 1) {
  606. this.navigation.prevPageKey = newPrevPageKey;
  607. await this.navigation.saveSettings();
  608. } else {
  609. alert(labels.invalidInput);
  610. }
  611. }
  612. }
  613.  
  614. const buttonManager = new PageButtonManager();
  615. const Navigation = new NavigationPaginationWithInput(buttonManager);
  616. new MenuManager(buttonManager,Navigation);

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址