// ==UserScript==
// @name 嗨皮漫畫閱讀輔助(自用)
// @version 1.1
// @description 增加一些輔助閱讀功能。
// @author tony0809
// @match *://m.happymh.com/*
// @icon https://m.happymh.com/favicon.ico
// @grant none
// @run-at document-end
// @license GPL
// @namespace https://greasyfork.org/users/20361
// ==/UserScript==
(function() {
'use strict';
const options = { //true 開啟,false 關閉
kn: true, //按鍵盤右方向鍵前往下一話。
kp: true, //按鍵盤左方向鍵前往上一話。
dn: true, //雙擊前往下一話,方便手機使用。
kdn: [false, 300], //按住空白鍵超過幾ms下一話。
nE: true, //閱讀頁底部增加更新頁和收藏頁的按鈕。
sV: [true, 1000], //閱讀前先花幾ms時間捲動至每張圖片的位置,使其觸發所有圖片請求載入全部圖片,捲完後返回頂部,在手機上使用如果圖片太多會效果不佳不一定會全部觸發。
hE: true, //隱藏閱讀頁頂部的公告。
ion: [false, 100], //下一話按鈕完全進入視窗可視範圍內時經過幾ms後自動下一話。
ac: true, //更新頁自動點擊載入更多。
list: true, //目錄頁自動展開全部章節。
oint: true //漫畫在新分頁打開。
}
const ge = e => document.querySelector(e);
const gx = x => document.evaluate(x, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
const read = /^\/reads\/\w+\/\d+$/.test(location.pathname);
const latest = /^\/latest$/.test(location.pathname);
const list = /^\/manga\/\w+$/.test(location.pathname);
const book = /^\/bookcase$/.test(location.pathname);
const addGlobalStyle = css => {
let style = document.createElement('style');
style.innerText = css;
document.head.appendChild(style);
}
const hasTouchEvents = () => {
if (('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0)) {
return true
}
return false
}
const autoClick = e => {
let btn = ge(e);
if (hasTouchEvents()) {
let dispatchTouchEvent = (ele, type) => {
let touchEvent = document.createEvent('UIEvent');
touchEvent.initUIEvent(type, true, true);
touchEvent.touches = [{
clientX: 1,
clientY: 1
}];
ele.dispatchEvent(touchEvent);
}
dispatchTouchEvent(btn, "touchstart");
dispatchTouchEvent(btn, "touchend");
} else {
btn.click();
}
}
const openInNewTab = () => document.querySelectorAll('.manga-cover a:not([target=_blank])').forEach(a => {
a.setAttribute('target', '_blank');
});
if (options.oint) {
openInNewTab();
new MutationObserver(() => {
openInNewTab();
}).observe(document.body, {
childList: true,
subtree: true
});
}
if (options.ac && latest) {
new IntersectionObserver((e) => {
if (e[0].isIntersecting) {
autoClick('.more-div-btn');
}
}).observe(ge('.more-div-btn'));
}
if (options.list && list) {
autoClick('#expandButton');
}
if (options.hE && read) {
addGlobalStyle('#root>div>header+div{display:none!important}');
}
if (options.kn && read) {
document.addEventListener('keydown', (e) => {
let key = window.event ? e.keyCode : e.which;
if (key == '39') {
let n = gx("//a[span[text()='下一话' or text()='下一話'] and contains(@href,'reads')]");
if (n) {
location.href = n.href;
} else {
alert('沒有下一话了!');
}
}
})
}
if (options.kp && read) {
document.addEventListener('keydown', (e) => {
let key = window.event ? e.keyCode : e.which;
if (key == '37') {
let p = gx("//a[span[text()='上一话' or text()='上一話'] and contains(@href,'reads')]");
if (p) {
location.href = p.href;
} else {
alert('沒有上一话了!');
}
}
});
}
if (options.dn && read) {
document.addEventListener('dblclick', () => {
let n = ge('footer a');
location.href = n.href;
})
}
if (options.kdn[0] && read) {
let timeId;
document.addEventListener('keypress', (e) => {
let key = window.event ? e.keyCode : e.which;
if (key = 32) {
let n = gx("//a[span[text()='下一话' or text()='下一話'] and contains(@href,'reads')]");
if (n) {
timeId = setTimeout(() => {
location.href = n.href;
}, options.kdn[1])
} else {
timeId = setTimeout(() => {
alert('沒有下一話了!');
}, options.kdn[1])
}
}
});
document.addEventListener('keyup', (e) => {
let key = window.event ? e.keyCode : e.which;
if (key = 32) {
clearTimeout(timeId);
}
});
}
if (options.nE && read) {
setTimeout(() => {
let f = ge('footer>article');
let c1 = f.firstChild.cloneNode(true);
c1.firstChild.href = '/latest';
c1.firstChild.firstChild.innerText = '更新';
f.appendChild(c1);
let c2 = f.firstChild.cloneNode(true);
c2.firstChild.href = '/bookcase';
c2.firstChild.firstChild.innerText = '收藏';
f.appendChild(c2);
let p = ge('footer p:nth-child(2)>a[href*=reads]');
if (p) {
p.classList.add('MuiButton-containedPrimary');
}
let n = ge('footer a[href*=readMore]');
if (n) {
n.classList.remove('MuiButton-containedPrimary');
n.firstChild.innerText = '^_^感谢您的阅读~已经没有下一话了哦~';
}
}, 4000)
}
if (options.sV[0] && read) {
let loop = setInterval(() => {
let iL = ge('[id^=imageLoader]');
if (iL) {
clearInterval(loop);
let imgs = iL.parentNode.children;
let n = imgs.length - 1;
let ms = Math.ceil(options.sV[1] / n);
for (let i = 1; i <= n; i++) {
setTimeout(() => {
imgs[i].scrollIntoView();
if (i == n) {
window.scrollTo(0, 0);
}
}, i * ms)
}
}
}, 100)
}
if (options.ion[0] && read) {
setTimeout(() => {
new IntersectionObserver((e) => {
if (e[0].isIntersecting) {
setTimeout(() => {
let n = gx("//a[span[text()='下一话' or text()='下一話'] and contains(@href,'reads')]");
if (n) location.href = n.href;
}, options.ion[1])
}
}, {
threshold: 1,
}).observe(ge('footer a'));
}, 4000)
}
})();