// ==UserScript==
// @name 包子漫畫閱讀輔助
// @name:zh-CN 包子漫画阅读辅助
// @version 1.1
// @description 包子漫畫閱讀輔助,瀑布流閱讀連續載入圖片,在新分頁打開漫畫鏈接(自用)。
// @description:zh-CN 包子漫画阅读辅助,瀑布流阅读连续载入图片,在新分页打开漫画链接(自用)。
// @author tony0809
// @match *://cn.baozimh.com/*
// @match *://cn.webmota.com/*
// @match *://tw.baozimh.com/*
// @match *://tw.webmota.com/*
// @match *://www.baozimh.com/*
// @match *://www.webmota.com/*
// @match *://cn.kukuc.co/*
// @match *://tw.kukuc.co/*
// @match *://www.kukuc.co/*
// @icon https://www.baozimh.com/favicon.ico
// @grant none
// @run-at document-end
// @license GPL
// @namespace https://greasyfork.org/users/20361
// ==/UserScript==
(() => {
'use strict';
const options = { //true 開啟,false 關閉
oint: true, //在新分頁打開漫畫鏈接。
aH: true, //載入下一話時添加歷史紀錄
aO: true, //目錄頁自動展開全部章節。
remove: [true, 2] //!!!不能小於2!!!閱讀載入超過n話時刪除前面話數的圖片。
},
ge = e => document.querySelector(e),
gae = e => document.querySelectorAll(e),
lp = location.pathname,
classify = /^\/classify/.test(lp),
list = /^\/list\/new/.test(lp),
search = /^\/search\?/.test(lp),
comic = /^\/comic\/[^.]+$/.test(lp),
read = /^\/comic\/chapter\/[^.]+\.html$/.test(lp),
addGlobalStyle = css => {
let style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = css;
document.head.appendChild(style);
},
readCss = `
.goback {
background: #fff url(https://www.acgqd.com/assets/4155373f/images/side_ico.png) no-repeat 3px -81px;
opacity: 0.5;
border-radius: 50%;
position: fixed;
z-index:999;
bottom: 7px;
left: 50%;
margin-left: -16px;
width: 36px;
height: 36px;
}
.mobadsq {
display: none !important
}
ul {
margin-block-start: -2px !important;
margin-block-end: 2px !important
}
.chapterLoading {
font-size: 20px;
height: 30px;
line-height: 32px;
text-align: center;
}
.chapterTitle {
width: auto;
height: 30px;
font-size: 20px;
font-family: Arial,sans-serif!important;
line-height: 32px;
text-align: center;
margin: 10px 5px;
border: 1px solid #e0e0e0;
background-color: #f0f0f0;
background: -webkit-gradient(linear, 0 0, 0 100%, from(#f9f9f9), to(#f0f0f0));
background: -moz-linear-gradient(top, #f9f9f9, #f0f0f0);
box-shadow: 0 0 5px rgba(0, 0, 0, 0.6);
border-radius: 5px;
}
`,
openInNewTab = () => gae('.comics-card a:not([target=_blank]),.bookshelf-items a:not(.remove-img):not([target=_blank])').forEach(a => {
a.setAttribute('target', '_blank');
}),
addGoBack = () => {
let goback = document.createElement('div');
goback.className = 'goback';
goback.setAttribute('title', '返回頂部');
goback.addEventListener('click', () => {
window.scrollTo({
top: 0,
behavior: "smooth"
});
});
document.body.appendChild(goback);
},
removeAd = () => {
let loop = setInterval(() => {
let ad = ge('#interstitial_fade');
if (ad) {
clearInterval(loop);
ad.remove();
}
}, 100);
setTimeout(() => {
if (loop) clearInterval(loop);
}, 1e4);
gae('.mobadsq').forEach(e => {
e.remove();
});
},
addHistory = (title, url) => {
history.pushState(null, title, url);
document.title = title;
},
addLoad = () => {
let cload = document.createElement('div');
cload.className = 'chapterLoading';
let img = new Image();
img.className = 'loadingImg';
img.src = '/_nuxt/img/loading.12fdcc4.gif';
img.style.width = '50px';
cload.appendChild(img);
let load = document.createElement('div');
load.className = 'loadingText';
load.innerText = 'Loading...';
cload.appendChild(load);
ge('.comic-contain').appendChild(cload);
},
removeLoad = () => {
ge('.chapterLoading').remove();
},
addTitle = title => {
let t = document.createElement('div');
t.className = 'chapterTitle';
t.innerText = title;
let load = ge('.chapterLoading');
load.parentNode.insertBefore(t, load);
},
parseHTML = str => {
let doc;
try {
doc = new DOMParser().parseFromString(str, 'text/html');
} catch (e) {}
if (!doc) {
doc = document.implementation.createHTMLDocument('');
doc.documentElement.innerHTML = str;
}
return doc;
},
fetchData = url => {
fetch(url).then(res => res.text()).then(res => {
let doc = parseHTML(res);
insertData(doc, url);
setTimeout(() => {
addNextObserver();
}, 1200);
}).catch(() => {
ge('.loadingImg').style.display = 'none';
ge('.loadingText').innerText = '連線出錯請重新整理';
});
},
/*fetchData = url => {
let xhr = new XMLHttpRequest();
xhr.responseType = 'text';
xhr.open('GET', url);
xhr.timeout = 10000;
xhr.onload = () => {
if (xhr.status == 200) {
let doc = parseHTML(xhr.responseText),
title = doc.title;
if (options.aH) {
addHistory(title, url);
}
insertData(doc, url);
setTimeout(() => {
addNextObserver();
}, 1200);
} else if (xhr.status > 400) {
ge('.chapterLoading').innerText = 'HTTP連線狀態碼:' + xhr.status + ',獲取過程中出錯。';
}
};
xhr.onerror = (e) => {
ge('.chapterLoading').innerText = '連線出錯,請重新載入。';
}
xhr.ontimeout = (e) => {
ge('.chapterLoading').innerText = '連線逾時,請重新載入。';
};
xhr.send();
},*/
insertData = (d, url) => {
let F = new DocumentFragment(),
imgs = d.querySelectorAll('.comic-contain amp-img');
imgs.forEach(e => {
let img = new Image();
img.className = 'comic-contain__item';
img.src = e.getAttribute('src');
F.appendChild(img)
});
let load = ge('.chapterLoading');
if (load) {
const rge = e => d.querySelector(e);
let title = rge('span.title').innerText;
if (!/\/\d+_\d+_\d+\.html$/.test(url)) {//是下一話才添加標題分隔條,下一頁則不添加。
let docTitle = d.title;
if (options.aH) {
addHistory(docTitle, url);
}
addTitle(title);
}
if (options.remove[0] && options.remove[1] > 1) {
removesOldChapter();
}
setTimeout(() => {
load.parentNode.insertBefore(F, load);
['.comic-chapter>.next_chapter', '.bottom-bar', 'span.title'].forEach(e => {
ge(e).outerHTML = rge(e).outerHTML; //替換元素
});
removeLoad();
}, 200);
} else {
let E = ge('.comic-contain');
E.innerHTML = '';
E.appendChild(F);
}
},
addNextObserver = () => {
const getNext = () => {
let next = ge('#next-chapter');
if (next) {
//可能會遇到當前域名和下一頁鏈接的域名不同,導致發生跨域請求出錯的情況,需替換為當前域名。
let url = next.href;
const nh = next.host;
const lh = location.host;
if (nh !== lh) {
url = url.replace(nh, lh);
}
addLoad();
fetchData(url);
}
};
/*let imgs = ge('.comic-contain').querySelectorAll('img');
let lastImg = [].slice.call(imgs).pop();*/
let lastImg = [...ge('.comic-contain').querySelectorAll('img')].pop(), //用最後一張圖片作為觀察對象。
loadImg = new Image();
loadImg.src = lastImg.src;
loadImg.onload = () => {
//等待最後一張圖片載入完成再添加觀察,過早可能會連續觸發下一頁,導致圖片請求過度頻繁。
const observer = new IntersectionObserver((e, observer) => {
if (e[0].isIntersecting) {
observer.unobserve(lastImg);
getNext();
}
}).observe(lastImg);
};
loadImg.onerror = () => {
alert('可能圖片請求過於頻繁,觸發網站限制圖片403拒絕存取,也或許只是圖片404掛掉了,請自行確認。');
//圖片出錯則改用當前頁面的next元素做為觀察對象
let ele = ge('#next-chapter');
const observer = new IntersectionObserver((e, observer) => {
if (e[0].isIntersecting) {
observer.unobserve(ele);
getNext();
}
}).observe(ele);
};
},
removesOldChapter = () => {
let titles = gae('.chapterTitle');
if (titles.length > options.remove[1]) {
titles[0].remove();
let removes = gae('.comic-contain>*');
for (let i in removes) {
if (/chapterTitle/.test(removes[i].className)) {
break
};
removes[i].remove();
}
}
};
if (read) {
removeAd();
addGoBack();
addGlobalStyle(readCss);
insertData(document);
addNextObserver();
}
if (options.oint && !read) {
openInNewTab();
new MutationObserver(() => {
openInNewTab();
}).observe(document.body, {
childList: true,
subtree: true
});
}
if (options.aO && comic) {
window.onload = () => {
let open = ge('#button_show_all_chatper:not([hidden])');
if (open) open.click();
};
}
})();