// ==UserScript==
// @name HotKey Next Page
// @namespace hzhbest
// @author hzhbest
// @version 1.5
// @description 按左右键快速翻页,也可点击浮动按钮快速翻页
// @include http://*
// @include https://*
// @license MIT
// @run-at document-end
// @grant unsafeWindow
// ==/UserScript==
// original author : scottxp@126.com (https://userscripts-mirror.org/scripts/show/87895)
// mod by hzhbest : add hilite and float buttons
const Strs = {
next: [
"下一页",
"下壹頁",
"下页",
"下一页 »",
"下一页 >",
"下一节",
"下一章",
"下一篇",
"后一章",
"后一篇",
"后页>",
"»",
">",
"next",
"next page",
"Next →",
"old",
"older",
"earlier",
"下頁",
"下一頁",
"后一页",
"后一頁",
"翻下页",
"翻下頁",
"后页",
"后頁",
"下翻",
"下一个",
"下一张",
"下一幅",
],
last: [
"上一页",
"上壹頁",
"上页",
"« 上一页",
"< 上一页",
"上一节",
"上一章",
"上一篇",
"前一章",
"前一篇",
"<前页",
"«",
"<",
"previous",
"prev",
"previous page",
"← Previous",
"new",
"newer",
"later",
"上頁",
"上一頁",
"前一页",
"前一頁",
"翻上页",
"翻上頁",
"前页",
"前頁",
"上翻",
"上一个",
"上一张",
"上一幅",
]
}
const GeneralXpaths = [
["//a[(normalize-space(text())='", "')]"],
["//a[@class='", "']"],
["//a[@id='", "']"],
["//a[starts-with(@class,'", " ')]"],
["//a[starts-with(@id,'", " ')]"],
["//input[@type='button' and @value='", "']"],
];
//编辑下面的数组来自定义规则
const SpecialXpaths = [
{
urls: ["http://www.google.com"], //匹配的url
last: "//a[@id='pnprev']", //上一页节点的xpath
next: "//a[@id='pnnext']", //下一页节点的xpath
},
{
urls: ["bilibili.com"],
last: '//li[@class="be-pager-prev"]',
next: '//li[@class="be-pager-next"]',
},
{
urls: ["taobao.com"],
last: '//button[contains(@aria-label,"上一页")]',
next: '//button[contains(@aria-label,"下一页")]',
},
{
urls: ["gov.cn"],
last: '//button[@class="btn-prev"]',
next: '//button[@class="btn-next"]',
},
/*{
urls: ["anime1.me"],
last: '//a[@id="table-list_previous"]',
next: '//a[@id="table-list_next"]',
},
{
urls: ["e-hentai.org"],
last: '//a[@id="dprev"]',
next: '//a[@id="dnext"]',
},*/
];
const LastKEY = [
37, // 左方向键
65 // 字母键A
];
const NextKEY = [
39, // 右方向键
68 // 字母键D
];
const css = `.__hkbtn {outline: 3px solid #bb1a6a; font-size: larger;}
.__hkbse {position: fixed; z-index: 1000; right: 2em; bottom: 5em; background: #fff9;}
.__hkbse>div:first-of-type {display: none;}
.__hkbse:hover>div:first-of-type {display: inline-block;}
.__hkfbtn {width: 4em; height: 3em; display: inline-block; border: 2px solid #8a8a8a8a;
cursor: pointer; text-align: center; font-size: 1.5em; line-height: 3em; opacity: 0.7;}
.__hkfbtn.disabled {opacity: 0.3;}
.__hkfbtn:hover {opacity: 1; background: #fbcf84dd;}
`;
addCSS(css, 'hknp_css');
var lnode,rnode,bnode,href;
setTimeout(init, 2000);
function init(){
lnode = getNode('last');
rnode = getNode('next');
if (!!lnode || !!rnode) {
if (!!bnode) bnode.parentNode.removeChild(bnode);
bnode = creaElemIn('div', document.body);
bnode.className = '__hkbse';
var lbutton = creaElemIn('div', bnode);
var rbutton = creaElemIn('div', bnode);
lbutton.className = rbutton.className = '__hkfbtn disabled';
lbutton.innerHTML = "<";
rbutton.innerHTML = ">";
bnode.addEventListener('mouseover', ()=>{
// console.log("href?",href == document.location.href);
if(href !== document.location.href) init();
},false);
if (!!lnode && lnode.offsetHeight !== 0) {
lnode.classList.add('__hkbtn') ;
lbutton.classList.remove('disabled');
if (lnode.textContent.length > 0) lbutton.innerHTML = lnode.textContent.substring(0,3);
lbutton.title = lnode.textContent + ((!!lnode.href)? '\n' + lnode.href : '');
lbutton.addEventListener('click', ()=>{
click(lnode);
setTimeout(init, 1000);
}, false);
}
if (!!rnode && rnode.offsetHeight !== 0) {
rnode.classList.add('__hkbtn') ;
rbutton.classList.remove('disabled');
if (rnode.textContent.length > 0) rbutton.innerHTML = rnode.textContent.substring(0,3);
rbutton.title = rnode.textContent + ((!!rnode.href)? '\n' + rnode.href : '');
rbutton.addEventListener('click', ()=>{
click(rnode);
setTimeout(init, 1000);
}, false);
}
}
href = document.location.href;
}
function creaElemIn(tagname, destin) {
let theElem = destin.appendChild(document.createElement(tagname));
return theElem;
}
function addCSS(css, cssid) {
let stylenode = creaElemIn('style',document.getElementsByTagName('head')[0]);
stylenode.textContent = css;
stylenode.type = 'text/css';
stylenode.id = cssid || '';
}
function checkKey(e) {
if (e.ctrlKey || e.shiftKey || e.altKey || e.metaKey) return;
if (checkTextArea(e.target)) return;
//console.log("hknp_keydown:",e.keyCode);
if (LastKEY.includes(e.keyCode) && !!lnode) {
click(lnode);
setTimeout(init, 1000);
}
if (NextKEY.includes(e.keyCode) && !!rnode) {
click(rnode);
setTimeout(init, 1000);
}
}
function checkTextArea(node) {
var name = node.localName.toLowerCase();
if (name == "textarea" || name == "input" || name == "select") {
return true;
}
if (name == "div" && (node.id.toLowerCase().indexOf("textarea") != -1 || node.contentEditable !== false)) {
return true;
}
return false;
}
function click(node) {
if (node.onclick) node.onclick();
if (href !== document.location.href) return;
if (node.click) node.click();
if (href !== document.location.href) return;
if (node.href) location.href = node.href;
}
function xpath(query) {
return unsafeWindow.document.evaluate(
query,
document,
null,
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
null
);
}
function getNode(lnstr) {
var node = getNodeByGeneralXpath(lnstr);
// console.log("n1",lnstr,node);
if (!node) node = getNodeBySpecialXpath(lnstr);
// console.log("n2",lnstr,node);
return node;
}
function getNodeByGeneralXpath(lnstr) { // lnstr 只支持输入 【last】 或者 【next】
var strs;
strs = Strs[lnstr];
var x = GeneralXpaths;
for (var i in x) {
for (var j in strs) {
var query = x[i][0] + strs[j] + x[i][1];
var nodes = xpath(query);
if (nodes.snapshotLength > 0) return nodes.snapshotItem(0);
}
}
return null;
}
function getNodeBySpecialXpath(lnstr) { // lnstr 只支持输入 【last】 或者 【next】
var s = SpecialXpaths;
for (var i in s) {
if (checkXpathUrl(s[i].urls)) {
return xpath(s[i][lnstr]).snapshotItem(0);
}
}
return null;
}
function checkXpathUrl(urls) {
for (var i in urls) if (location.href.indexOf(urls[i]) >= 0) return true;
return false;
}
if (top.location != self.location) return;
unsafeWindow.document.addEventListener("keydown", checkKey, false);