HotKey Next Page

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

// ==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);