// ==UserScript==
// @name DM5動漫屋_閱讀輔助
// @name:en DM5 Read Helpr
// @name:zh-CN DM5动漫屋_阅读辅助
// @name:zh-TW DM5動漫屋_閱讀輔助
// @version 1.0.5
// @description DM5動漫屋_瀑布流閱讀連續載入圖片(自用)。
// @description:en DM5 read infinite scroll
// @description:zh-CN DM5动漫屋_瀑布流阅读连续载入图片(自用)。
// @description:zh-TW DM5動漫屋_瀑布流閱讀連續載入圖片(自用)。
// @author tony0809
// @match *://*.dm5.com/m*/
// @match *://*.dm5.cn/m*/
// @match *://*.1kkk.com/ch*/
// @match *://*.1kkk.com/vol*/
// @match *://*.1kkk.com/other*/
// @icon https://www.google.com/s2/favicons?sz=64&domain=dm5.com
// @grant none
// @license MIT
// @namespace https://greasyfork.org/users/20361
// ==/UserScript==
(() => {
'use strict';
const options = { //true 開啟,false 關閉
aH: true, //載入下一話時添加瀏覽器歷史紀錄。
pln: true, //PC條漫版和手機版預讀下一頁的圖片,減少等待加載圖片的時間。
remove: [true, 3] //!!!不能小於2!!!閱讀載入超過n話時刪除前面話數的圖片。
};
const ge = (selector, doc) => (doc || document).querySelector(selector);
const gae = (selector, doc) => (doc || document).querySelectorAll(selector);
const gx = (xpath, doc) => (doc || document).evaluate(xpath, (doc || document), null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
const gax = (xpath, doc) => {
let nodes = [];
let results = (doc || document).evaluate(xpath, (doc || document), null, XPathResult.ANY_TYPE, null);
let node;
while (node = results.iterateNext()) {
nodes.push(node);
}
return nodes;
};
const pcMode1 = () => {
if (ge('#chapterpager')) {
return true;
}
return false;
};
const pcMode2 = () => {
if (ge('#barChapter')) {
return true;
}
return false;
};
const mobile = () => {
if (gx("//script[contains(text(),'newImgs')]")) {
return true;
}
return false;
};
const runCode = code => new Function('return ' + code)();
const addGlobalStyle = css => {
let style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = css;
document.head.appendChild(style);
};
const pcCss = `
a[href^='j'],
.chapterpager,
#lb-win {
display: none !important;
}
.view-comment {
display: none ;
}
.chapterTitle {
width: auto;
height: 30px;
font-size: 24px;
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;
}
`;
const mCss = `
.view-fix-bottom-bar-item[onclick],
.guide {
display: none !important;
}
.view-fix-bottom-bar-item {
width: 25% !important;
}
.chapterTitle {
width: auto;
height: 30px;
font-size: 20px;
font-family: Arial,sans-serif!important;
line-height: 32px;
text-align: center;
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
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;
}
`;
const css = `
#cp_img>img,#barChapter>img {
width: auto !important;
height: auto !important;
max-width: 100% !important;
display: block !important;
margin: 0 auto !important
}
.chapterLoading {
font-size: 20px;
height: 30px;
line-height: 32px;
text-align: center;
}
`;
const showElement = () => {
let ele = gx("//a[text()='下一章']");
if (!ele) {
ge('.view-comment').style.display = 'block';
}
};
const editElement = () => {
let end = gx("//ul[@class='view-bottom-bar']//a[contains(@href,'-end')]");
if (end) {
end.href = "/manhua-list-s2/";
end.innerText = '最近更新';
}
};
const addHistory = (title, url) => {
history.pushState(null, title, url);
document.title = title;
};
const addLoad = () => {
let cl = document.createElement('div');
cl.className = 'chapterLoading';
cl.innerText = 'Loading...';
ge('#cp_img,#barChapter').appendChild(cl);
};
const removeLoad = () => {
ge('.chapterLoading').remove();
};
const addTitle = title => {
let t = document.createElement('div');
t.className = 'chapterTitle';
t.innerText = title;
let load = ge('.chapterLoading');
load.parentNode.insertBefore(t, load);
};
const parseHTML = str => {
var doc = null;
try {
doc = new DOMParser().parseFromString(str, 'text/html');
} catch (e) {}
if (!doc) {
doc = document.implementation.createHTMLDocument('');
doc.documentElement.innerHTML = str;
}
return doc;
};
const mobilePreloadNext = () => {
let next = gx("//ul[@class='view-bottom-bar']//a[text()='下一章' and not(contains(@href,'-end'))]");
if (next) {
let url = location.origin + next.href.split("'")[1];
fetch(url).then(res => res.text()).then(res => {
let doc = parseHTML(res);
let title = gx("//title", doc).innerText.match(/_([^_]+)/)[1].replace(/,$/, '');
let imgs = runCode(runCode(Array.from(doc.scripts).find(s => s.innerHTML.search(/newImgs/) > -1).innerHTML.trim().slice(4)).replace('var', ''));
let F = new DocumentFragment();
imgs.forEach(e => {
let temp = new Image();
temp.src = e;
F.appendChild(temp);
temp.onload = () => {
temp = null;
};
});
console.log(`[${title}] 圖片預讀:\n`, F);
doc = null;title = null;imgs = null;F = null;
});
}
};
const pcMode2PreloadNext = () => {
let next = gx("//a[text()='下一章']");
if (next) {
fetch(next.href).then(res => res.text()).then(res => {
let doc = parseHTML(res);
let title = gx("//span[@class='active right-arrow']", doc).innerText.trim();
let imgs = gae('#barChapter>img[data-src]', doc);
let F = new DocumentFragment();
imgs.forEach(e => {
let temp = new Image();
temp.src = e.dataset.src;
F.appendChild(temp);
temp.onload = () => {
temp = null;
};
});
console.log(`[${title}] 圖片預讀:\n`, F);
doc = null;title = null;imgs = null;F = null;
});
}
};
const fetchData = url => {
fetch(url).then(res => res.text()).then(res => {
let doc = parseHTML(res);
if (options.aH) {
addHistory(doc.title, url);
}
if (!pcMode2()) {
var code = null;
if (mobile()) {
code = runCode(Array.from(doc.scripts).find(s => s.innerHTML.search(/newImgs/) > -1).innerHTML.trim().slice(4));
} else {
code = Array.from(doc.scripts).find(s => s.innerHTML.search(/DM5_IMAGE_COUNT/) > -1).innerHTML;
}
let script = document.createElement('script');
script.type = 'text/javascript';
script.innerHTML = code;
ge('#cp_img').appendChild(script);
}
insertData(doc);
}).catch(error => {
console.error(error);
ge('.chapterLoading').innerText = '连接出错,可能下一章为付费章节。';
});
};
const insertData = doc => {
const pcData = async () => {
if (!mkey) {
var mkey = '';
}
for (let i = 1; i <= DM5_IMAGE_COUNT; i++) {
let apiUrl = location.origin + DM5_CURL + 'chapterfun.ashx' + `?cid=${DM5_CID}&page=${i}&key=${mkey}&language=1>k=6&_cid=${DM5_CID}&_mid=${DM5_MID}&_dt=${DM5_VIEWSIGN_DT}&_sign=${DM5_VIEWSIGN}`;
let res = await fetch(apiUrl);
let resText = await res.text();
let imgSrc = await runCode(resText)[0];
let img = new Image();
img.src = imgSrc;
ge('#cp_img').appendChild(img);
}
addNextObserver();
};
const pc2Data = () => {
let F = new DocumentFragment();
let imgs = gae('#barChapter>img[data-src]', doc);
imgs.forEach(e => {
let img = new Image();
img.src = e.dataset.src;
F.appendChild(img);
});
ge('#barChapter').appendChild(F);
imgs = null;
setTimeout(() => {
addNextObserver();
}, 1700);
};
const mobileData = () => {
let F = new DocumentFragment();
newImgs.forEach(e => {
let img = new Image();
img.src = e;
F.appendChild(img);
});
ge('#cp_img').appendChild(F);
setTimeout(() => {
addNextObserver();
}, 1300);
};
let load = ge('.chapterLoading');
if (load) {
let title;
if (mobile()) {
["//div[@class='view-fix-top-bar']", "//ul[@class='view-bottom-bar']", "//div[@class='view-fix-bottom-bar']"].forEach(e => {
gx(e).outerHTML = gx(e, doc).outerHTML;
});
title = gx("//title", doc).innerText.match(/_([^_]+)/)[1].replace(/,$/, '');
} else {
["//div[@class='view-paging']", "//div[@class='rightToolBar']"].forEach(x => {
gax(x).forEach(e => {
e.outerHTML = gx(x, doc).outerHTML;
});
});
title = gx("//span[@class='active right-arrow']", doc).innerText.trim();
}
addTitle(title);
if (options.remove[0] && options.remove[1] > 1) {
removeOldChapter();
}
setTimeout(() => {
removeLoad();
if (mobile()) {
mobileData();
editElement();
} else if (pcMode1()) {
pcData();
showElement();
} else {
pc2Data();
showElement();
}
}, 300);
} else {
if (mobile()) {
ge('#cp_img').innerHTML = '';
mobileData();
editElement();
} else if (pcMode1()) {
document.body.style.overflow = 'scroll';
ge('#cp_img').innerHTML = '';
pcData();
showElement();
} else {
gae('#barChapter>img[data-src]').forEach(e => {
e.outerHTML = `<img src="${e.dataset.src}">`;
});
setTimeout(() => {
addNextObserver();
}, 1300);
showElement();
}
}
};
const nextObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
observer.unobserve(entry.target);
let url, next;
if (mobile()) {
next = gx("//ul[@class='view-bottom-bar']//a[text()='下一章' and not(contains(@href,'-end'))]");
if (next) url = location.origin + next.href.split("'")[1];
} else {
next = gx("//a[text()='下一章']");
if (next) url = next.href;
}
if (next) {
console.log(`觸發載入下一話\n${url}`);
addLoad();
fetchData(url);
}
}
});
});
const addNextObserver = () => {
let lastImg = [...gae('#cp_img img,#barChapter img')].pop();
let loadImg = new Image();
loadImg.src = lastImg.src;
loadImg.onload = () => {
nextObserver.observe(lastImg);
if (mobile() && options.pln) {
mobilePreloadNext();
} else if (pcMode2() && options.pln) {
pcMode2PreloadNext();
}
loadImg = null;
};
loadImg.onerror = () => {
nextObserver.observe(lastImg);
};
};
const removeOldChapter = () => {
let titles = gae('.chapterTitle');
if (titles.length > options.remove[1]) {
titles[0].remove();
let removes = gae('#cp_img>*,#barChapter>*');
for (let i in removes) {
if (/chapterTitle/.test(removes[i].className)) {
break;
}
removes[i].remove();
}
}
};
if (pcMode1()) {
console.log('DM5_PcMode1');
addGlobalStyle(pcCss);
addGlobalStyle(css);
if (ge('#lb-win')) {
ge('#lb-win').remove();
}
let loop = setInterval(() => {
let set = ge('#cp_image');
if (set) {
clearInterval(loop);
insertData();
set.remove();
}
}, 100);
} else if (pcMode2()) {
console.log('DM5_PcMode2');
addGlobalStyle(pcCss);
addGlobalStyle(css);
if (ge('#lb-win')) {
ge('#lb-win').remove();
}
insertData();
} else if (mobile()) {
console.log('DM5_mobile');
addGlobalStyle(mCss);
addGlobalStyle(css);
if (gae('.view-bottom-bar>li').length == 4) {
addGlobalStyle(`.view-bottom-bar>li:nth-child(n+2):nth-child(-n+3){display:none !important}.view-bottom-bar li{width:50% !important}`);
}
insertData(document);
}
})();