// ==UserScript== // @name 包子漫畫閱讀輔助 // @name:zh-CN 包子漫画阅读辅助 // @version 2.0 // @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 // @downloadURL none // ==/UserScript== (() => { 'use strict'; const options = { //true 開啟,false 關閉 oint: true, //在新分頁打開漫畫鏈接。 aH: true, //載入下一話時添加瀏覽器歷史紀錄 aO: true, //目錄頁自動展開全部章節。 pln: true, //預讀下一頁的圖片,減少等待加載圖片渲染頁面的時間。 remove: [true, 4] //!!!不能小於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(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAAAXNSR0IArs4c6QAAAgpJREFUSEvtlD2IE0EYht9vJBeRQxIU5AoPCwsFGwsRrAQRzztRRFeEa6LsfiyBgJ2FrYWdSDjITH4uhYK4Ih74g4qFjSAWFoJYWCgKIigbVCSYzYxsGCXJ5ZLlDq6QTDPs9/M+H+/MDmGdFq0TB2PQqp0eW0fMfAUAKaUuAjBJvUxsneM4G7LZbBnAOSu+GIahFwRBOwksESiXy21MpVI3iehEt6gxZqnVap2t1+vNUbCRoEKhsLnZbC4R0UEr9tvuE3Z/lk6njxeLxe/DYENBrutuE0I8BLDXivwEcIqIjDHmDoBJG3+ltT5aqVS+rARbEeT7/g6t9RMAO23zNwBzSqkX8Tcz7wdwH8AWm38nhDhcKpXeD4INBPm+v0dr/RjAlG36SEQzUso33SKu6+4WQjwCsN3GPwM4opR63Q9bBvI87wAR3QOQtcVv2+32TLVa/TBoUmaeBhDDdtl8aIw5Vi6Xn3fX94CYeQ7ALQCbbNFLALNKqa/DDpqZtwJ4AGCfrfsF4IxSKra2s/6BPM+bJ6JFACmbexpF0clarfZj1NWN8/l8fjKKorsADtn6FhGdl1Je7wEx81UAF+KgMeZ2o9GYD4Lg71VOwoLjOBOZTOYGEZ3uiBNdk1J2NPutuwRgOgzDfNI/vn8C+4IsENEnKeXlZdYlGnkNRSNfhjVo97SOQat28v+z7g/utJ8bMvScRQAAAABJRU5ErkJggg==) no-repeat; background-position:center; opacity: 0.7; 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 cl = document.createElement('div'); cl.className = 'chapterLoading'; let li = new Image(); li.className = 'loadingImg'; li.src = '/_nuxt/img/loading.12fdcc4.gif'; li.style.width = '50px'; cl.appendChild(li); let lt = document.createElement('div'); lt.className = 'loadingText'; lt.innerText = 'Loading...'; cl.appendChild(lt); ge('.comic-contain').appendChild(cl); }, 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; }, preLoadNext = () => { 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); } fetch(url).then(res => res.text()).then(res => { let doc = parseHTML(res), imgs = doc.querySelectorAll('.comic-contain amp-img'); imgs.forEach(e => { let img = new Image(); img.src = e.getAttribute('src'); }); }); } }, fetchData = url => { fetch(url).then(res => res.text()).then(res => { let doc = parseHTML(res); insertData(doc, url); setTimeout(() => { addNextObserver(); }, 1200); }).catch(error => { console.log(error); 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(); if (options.pln) { console.log('包子漫畫預讀下一頁'); preLoadNext(); } }, 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 = () => { //等待最後一張圖片載入完成再添加觀察,過早可能會連續觸發下一頁,導致圖片請求過度頻繁。 console.log('包子漫畫添加下一頁觀察者'); const observer = new IntersectionObserver((e, observer) => { if (e[0].isIntersecting) { observer.unobserve(lastImg); console.log('包子漫畫觸發載入下一頁'); getNext(); } }).observe(lastImg); }; loadImg.onerror = () => { alert('可能圖片請求過於頻繁,觸發網站限制圖片403拒絕存取,也或許只是圖片404掛掉了,請自行確認。'); //圖片出錯則改用當前頁面的next元素做為觀察對象 let ele = ge('.comic-chapter>.next_chapter'); console.log('包子漫畫添加下一頁觀察者'); const observer = new IntersectionObserver((e, observer) => { if (e[0].isIntersecting) { observer.unobserve(ele); console.log('包子漫畫觸發載入下一頁'); 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.pln) { console.log('包子漫畫預讀下一頁'); preLoadNext(); } } if (options.oint && !comic && !read) { console.log('包子漫畫在新分頁打開漫畫鏈接'); openInNewTab(); new MutationObserver(() => { openInNewTab(); }).observe(document.body, { childList: true, subtree: true }); } if (options.aO && comic) { console.log('包子漫畫自動展開目錄'); window.onload = () => { let open = ge('#button_show_all_chatper:not([hidden])'); if (open) open.click(); }; } })();