HotKey Next Page

按左右键快速翻页,也可点击浮动按钮快速翻页

  1. // ==UserScript==
  2. // @name HotKey Next Page
  3. // @namespace hzhbest
  4. // @author hzhbest
  5. // @version 1.5
  6. // @description 按左右键快速翻页,也可点击浮动按钮快速翻页
  7. // @include http://*
  8. // @include https://*
  9. // @license MIT
  10. // @run-at document-end
  11. // @grant unsafeWindow
  12. // ==/UserScript==
  13.  
  14. // original author : scottxp@126.com (https://userscripts-mirror.org/scripts/show/87895)
  15. // mod by hzhbest : add hilite and float buttons
  16.  
  17. const Strs = {
  18. next: [
  19. "下一页",
  20. "下壹頁",
  21. "下页",
  22. "下一页 »",
  23. "下一页 >",
  24. "下一节",
  25. "下一章",
  26. "下一篇",
  27. "后一章",
  28. "后一篇",
  29. "后页>",
  30. "»",
  31. ">",
  32. "next",
  33. "next page",
  34. "Next →",
  35. "old",
  36. "older",
  37. "earlier",
  38. "下頁",
  39. "下一頁",
  40. "后一页",
  41. "后一頁",
  42. "翻下页",
  43. "翻下頁",
  44. "后页",
  45. "后頁",
  46. "下翻",
  47. "下一个",
  48. "下一张",
  49. "下一幅",
  50. ],
  51. last: [
  52. "上一页",
  53. "上壹頁",
  54. "上页",
  55. "« 上一页",
  56. "< 上一页",
  57. "上一节",
  58. "上一章",
  59. "上一篇",
  60. "前一章",
  61. "前一篇",
  62. "<前页",
  63. "«",
  64. "<",
  65. "previous",
  66. "prev",
  67. "previous page",
  68. "← Previous",
  69. "new",
  70. "newer",
  71. "later",
  72. "上頁",
  73. "上一頁",
  74. "前一页",
  75. "前一頁",
  76. "翻上页",
  77. "翻上頁",
  78. "前页",
  79. "前頁",
  80. "上翻",
  81. "上一个",
  82. "上一张",
  83. "上一幅",
  84. ]
  85. }
  86.  
  87. const GeneralXpaths = [
  88. ["//a[(normalize-space(text())='", "')]"],
  89. ["//a[@class='", "']"],
  90. ["//a[@id='", "']"],
  91. ["//a[starts-with(@class,'", " ')]"],
  92. ["//a[starts-with(@id,'", " ')]"],
  93. ["//input[@type='button' and @value='", "']"],
  94. ];
  95.  
  96. //编辑下面的数组来自定义规则
  97. const SpecialXpaths = [
  98. {
  99. urls: ["http://www.google.com"], //匹配的url
  100. last: "//a[@id='pnprev']", //上一页节点的xpath
  101. next: "//a[@id='pnnext']", //下一页节点的xpath
  102. },
  103. {
  104. urls: ["bilibili.com"],
  105. last: '//li[@class="be-pager-prev"]',
  106. next: '//li[@class="be-pager-next"]',
  107. },
  108. {
  109. urls: ["taobao.com"],
  110. last: '//button[contains(@aria-label,"上一页")]',
  111. next: '//button[contains(@aria-label,"下一页")]',
  112. },
  113. {
  114. urls: ["gov.cn"],
  115. last: '//button[@class="btn-prev"]',
  116. next: '//button[@class="btn-next"]',
  117. },
  118. /*{
  119. urls: ["anime1.me"],
  120. last: '//a[@id="table-list_previous"]',
  121. next: '//a[@id="table-list_next"]',
  122. },
  123. {
  124. urls: ["e-hentai.org"],
  125. last: '//a[@id="dprev"]',
  126. next: '//a[@id="dnext"]',
  127. },*/
  128. ];
  129. const LastKEY = [
  130. 37, // 左方向键
  131. 65 // 字母键A
  132. ];
  133. const NextKEY = [
  134. 39, // 右方向键
  135. 68 // 字母键D
  136. ];
  137. const css = `.__hkbtn {outline: 3px solid #bb1a6a; font-size: larger;}
  138. .__hkbse {position: fixed; z-index: 1000; right: 2em; bottom: 5em; background: #fff9;}
  139. .__hkbse>div:first-of-type {display: none;}
  140. .__hkbse:hover>div:first-of-type {display: inline-block;}
  141. .__hkfbtn {width: 4em; height: 3em; display: inline-block; border: 2px solid #8a8a8a8a;
  142. cursor: pointer; text-align: center; font-size: 1.5em; line-height: 3em; opacity: 0.7;}
  143. .__hkfbtn.disabled {opacity: 0.3;}
  144. .__hkfbtn:hover {opacity: 1; background: #fbcf84dd;}
  145. `;
  146. addCSS(css, 'hknp_css');
  147. var lnode,rnode,bnode,href;
  148. setTimeout(init, 2000);
  149.  
  150. function init(){
  151. lnode = getNode('last');
  152. rnode = getNode('next');
  153. if (!!lnode || !!rnode) {
  154. if (!!bnode) bnode.parentNode.removeChild(bnode);
  155. bnode = creaElemIn('div', document.body);
  156. bnode.className = '__hkbse';
  157. var lbutton = creaElemIn('div', bnode);
  158. var rbutton = creaElemIn('div', bnode);
  159. lbutton.className = rbutton.className = '__hkfbtn disabled';
  160. lbutton.innerHTML = "<";
  161. rbutton.innerHTML = ">";
  162. bnode.addEventListener('mouseover', ()=>{
  163. // console.log("href?",href == document.location.href);
  164. if(href !== document.location.href) init();
  165. },false);
  166. if (!!lnode && lnode.offsetHeight !== 0) {
  167. lnode.classList.add('__hkbtn') ;
  168. lbutton.classList.remove('disabled');
  169. if (lnode.textContent.length > 0) lbutton.innerHTML = lnode.textContent.substring(0,3);
  170. lbutton.title = lnode.textContent + ((!!lnode.href)? '\n' + lnode.href : '');
  171. lbutton.addEventListener('click', ()=>{
  172. click(lnode);
  173. setTimeout(init, 1000);
  174. }, false);
  175. }
  176. if (!!rnode && rnode.offsetHeight !== 0) {
  177. rnode.classList.add('__hkbtn') ;
  178. rbutton.classList.remove('disabled');
  179. if (rnode.textContent.length > 0) rbutton.innerHTML = rnode.textContent.substring(0,3);
  180. rbutton.title = rnode.textContent + ((!!rnode.href)? '\n' + rnode.href : '');
  181. rbutton.addEventListener('click', ()=>{
  182. click(rnode);
  183. setTimeout(init, 1000);
  184. }, false);
  185. }
  186. }
  187. href = document.location.href;
  188. }
  189.  
  190. function creaElemIn(tagname, destin) {
  191. let theElem = destin.appendChild(document.createElement(tagname));
  192. return theElem;
  193. }
  194.  
  195. function addCSS(css, cssid) {
  196. let stylenode = creaElemIn('style',document.getElementsByTagName('head')[0]);
  197. stylenode.textContent = css;
  198. stylenode.type = 'text/css';
  199. stylenode.id = cssid || '';
  200. }
  201.  
  202. function checkKey(e) {
  203. if (e.ctrlKey || e.shiftKey || e.altKey || e.metaKey) return;
  204. if (checkTextArea(e.target)) return;
  205. //console.log("hknp_keydown:",e.keyCode);
  206. if (LastKEY.includes(e.keyCode) && !!lnode) {
  207. click(lnode);
  208. setTimeout(init, 1000);
  209. }
  210. if (NextKEY.includes(e.keyCode) && !!rnode) {
  211. click(rnode);
  212. setTimeout(init, 1000);
  213. }
  214. }
  215.  
  216. function checkTextArea(node) {
  217. var name = node.localName.toLowerCase();
  218. if (name == "textarea" || name == "input" || name == "select") {
  219. return true;
  220. }
  221. if (name == "div" && (node.id.toLowerCase().indexOf("textarea") != -1 || node.contentEditable !== false)) {
  222. return true;
  223. }
  224. return false;
  225. }
  226.  
  227. function click(node) {
  228. if (node.onclick) node.onclick();
  229. if (href !== document.location.href) return;
  230. if (node.click) node.click();
  231. if (href !== document.location.href) return;
  232. if (node.href) location.href = node.href;
  233. }
  234. function xpath(query) {
  235. return unsafeWindow.document.evaluate(
  236. query,
  237. document,
  238. null,
  239. XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
  240. null
  241. );
  242. }
  243. function getNode(lnstr) {
  244. var node = getNodeByGeneralXpath(lnstr);
  245. // console.log("n1",lnstr,node);
  246. if (!node) node = getNodeBySpecialXpath(lnstr);
  247. // console.log("n2",lnstr,node);
  248. return node;
  249. }
  250. function getNodeByGeneralXpath(lnstr) { // lnstr 只支持输入 【last】 或者 【next】
  251. var strs;
  252. strs = Strs[lnstr];
  253. var x = GeneralXpaths;
  254. for (var i in x) {
  255. for (var j in strs) {
  256. var query = x[i][0] + strs[j] + x[i][1];
  257. var nodes = xpath(query);
  258. if (nodes.snapshotLength > 0) return nodes.snapshotItem(0);
  259. }
  260. }
  261. return null;
  262. }
  263. function getNodeBySpecialXpath(lnstr) { // lnstr 只支持输入 【last】 或者 【next】
  264. var s = SpecialXpaths;
  265. for (var i in s) {
  266. if (checkXpathUrl(s[i].urls)) {
  267. return xpath(s[i][lnstr]).snapshotItem(0);
  268. }
  269. }
  270. return null;
  271. }
  272. function checkXpathUrl(urls) {
  273. for (var i in urls) if (location.href.indexOf(urls[i]) >= 0) return true;
  274. return false;
  275. }
  276. if (top.location != self.location) return;
  277. unsafeWindow.document.addEventListener("keydown", checkKey, false);