// ==UserScript==
// @name 嗨皮漫畫閱讀輔助
// @name:zh-CN 嗨皮漫画阅读辅助
// @version 2.4
// @description 增加一些輔助閱讀功能(自用)。
// @description:zh-CN 增加一些辅助阅读功能(自用)。
// @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==
(() => {
'use strict';
const options = { //true 開啟,false 關閉
kn: true, //按鍵盤右方向鍵前往下一話。
kp: true, //按鍵盤左方向鍵前往上一話。
dn: true, //雙擊前往下一話,方便手機使用。
kdn: [false, 300], //按住空白鍵超過幾毫秒下一話。
nE: true, //閱讀頁底部增加更新頁和收藏頁的按鈕。
pl: true, //閱讀頁預讀全部圖片,並且嘗試預讀下一話圖片。
hE: true, //隱藏閱讀頁頂部的公告。
ion: [false, 200], //下一話按鈕完全進入視窗可視範圍內時經過幾ms後自動下一話。
lM: true, //更新頁自動點擊載入更多。
list: true, //目錄頁自動展開全部章節。
oint: true //在新分頁打開漫畫鏈接。
},
ge = e => document.querySelector(e),
gae = e => document.querySelectorAll(e),
gx = x => document.evaluate(x, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue,
lp = location.pathname,
read = /^\/reads\/\w+\/\d+$/.test(lp),
latest = /^\/latest$/.test(lp),
list = /^\/manga\/\w+$/.test(lp),
book = /^\/bookcase$/.test(lp),
rank = /^\/rank/.test(lp),
user = /^\/user/.test(lp),
addGlobalStyle = css => {
let style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = css;
document.head.appendChild(style);
},
readCss = `
article img {
width: auto !important;
height: auto !important;
max-width: 100% !important;
display: block !important;
margin: 0 auto !important
}
`,
hasTouchEvents = () => {
if (('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0)) {
return true;
}
return false;
},
loadMore = e => {
let LoadMore = 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(LoadMore, "touchstart");
dispatchTouchEvent(LoadMore, "touchend");
console.log('嗨皮漫畫模擬觸控點擊');
} else {
LoadMore.click();
console.log('嗨皮漫畫模擬點擊');
}
},
openInNewTab = () => document.querySelectorAll('.home-banner a:not([target=_blank]),.manga-rank a:not([target=_blank]),.manga-cover a:not([target=_blank])').forEach(a => {
a.setAttribute('target', '_blank');
}),
preLoad = (pn, text) => {
let lps = pn.split('/'),
mangaCode = lps[2],
id = lps[3],
apiUrl = `https://m.happymh.com/v2.0/apis/manga/read?code=${mangaCode}&cid=${id}`;
fetch(apiUrl).then(res => res.text()).then((res) => {
try {
let jsonData = JSON.parse(res);
if (jsonData.status == 0) {
let imgs = jsonData.data.scans;
let F = new DocumentFragment();
for (let i in imgs) {
let img = new Image();
img.src = imgs[i].url;
F.appendChild(img);
}
console.log(text + '漫畫名稱:' + jsonData.data.manga_name + '\n章節名稱:' + jsonData.data.chapter_name + '\n', jsonData, '\n', '圖片預讀\n', F);
} else if (jsonData.status == 403) {
console.log(text + '獲取數據失敗\n', jsonData);
}
} catch (error) {
console.error(error);
}
}).catch(error => console.error(error));
};
if (options.oint && !read && !list && !user) {
openInNewTab();
console.log('嗨皮漫畫在新分頁打開漫畫鏈接');
new MutationObserver(() => {
openInNewTab();
}).observe(document.body, {
childList: true,
subtree: true
});
}
if (options.lM && latest) {
new IntersectionObserver((e) => {
if (e[0].isIntersecting) {
loadMore('.more-div-btn');
console.log('載入更多');
}
}).observe(ge('.more-div-btn'));
}
if (options.list && list) {
ge('#expandButton').click();
console.log('嗨皮漫畫自動展開目錄');
}
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 = '^_^感谢您的阅读~已经没有下一话了哦~';
}
}, 5000);
}
if (options.pl && read) {
addGlobalStyle(readCss);
let loop = setInterval(() => {
let iL = ge('[id^=imageLoader]');
if (iL) {
clearInterval(loop);
console.log('嗨皮漫畫預讀全部圖片');
preLoad(lp, '嗨皮漫畫本話數據\n');
setTimeout(() => {
let next = gx("//a[span[text()='下一话' or text()='下一話'] and contains(@href,'reads')]");
if (next) {
preLoad(next.pathname, '嗨皮漫畫下一話數據\n');
}
}, 5000);
}
}, 100);
}
if (options.ion[0] && read) {
setTimeout(() => {
console.log('嗨皮漫畫下一話按鈕完全進入可視範圍內時自動下一話');
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'));
}, 5000);
}
})();