Greasy Fork

Greasy Fork is available in English.

包子漫画阅读辅助

包子漫画阅读辅助,瀑布流阅读连续载入图片,在新分页打开漫画链接(自用)。

当前为 2023-03-15 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         包子漫畫閱讀輔助
// @name:zh-CN   包子漫画阅读辅助
// @version      2.2
// @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 http://greasyfork.icu/users/20361
// ==/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),
          gx = x => document.evaluate(x, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue,
          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: 80px;
    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;
}
.next_chapter + .l-content {
    display: none;
}
          `,
          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;
          },
          showElement = () => {
              let end = gx("//div[@class='next_chapter']/span[text()='这是本作品最后一话了' or text()='這是本作品最後一話了']");
              if (end) {
                  ge('.next_chapter+.l-content').style.display = 'block';
                  const observer = new IntersectionObserver((entries, observer) => {
                      entries.forEach(entry => {
                          if (entry.isIntersecting) {
                              observer.unobserve(entry.target);
                              let e = entry.target;
                              setTimeout(()=>{
                                  let noImg = e.querySelector('div.i-amphtml-svc');
                                  if (noImg) {
                                      noImg.remove();
                                      let img = new Image();
                                      img.setAttribute('decoding','async');
                                      img.setAttribute('alt', e.getAttribute('alt'));
                                      img.src = e.getAttribute('src');
                                      img.className = 'i-amphtml-svc i-amphtml-loading-container i-amphtml-fill-content';
                                      e.appendChild(img);
                                  }
                              },200);
                          }
                      });
                  });
                  gae('.l-box a>amp-img').forEach(amp => {
                      observer.observe(amp);
                  });
              }
          },
          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();
                  }, 1300);
              }).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);
                  ['.comic-chapter>.next_chapter', '.bottom-bar', 'span.title'].forEach(e => {
                      ge(e).outerHTML = rge(e).outerHTML; //替換元素
                  });
                  showElement();
                  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) {
                      removeOldChapter();
                  }
                  setTimeout(() => {
                      load.parentNode.insertBefore(F, load);
                      removeLoad();
                      if (options.pln) {
                          console.log('包子漫畫預讀下一頁');
                          preLoadNext();
                      }
                  }, 300);
              } else {
                  showElement();
                  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('包子漫畫添加下一頁觀察者');
                  new IntersectionObserver((entries, observer) => {
                      if (entries[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('包子漫畫添加下一頁觀察者');
                  new IntersectionObserver((entries, observer) => {
                      if (entries[0].isIntersecting) {
                          observer.unobserve(ele);
                          console.log('包子漫畫觸發載入下一頁');
                          getNext();
                      }
                  }).observe(ele);
              };
          },
          removeOldChapter = () => {
              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) {
            setTimeout(()=>{
                console.log('包子漫畫預讀下一頁');
                preLoadNext();
            },3000);
        }
    }

    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();
        };
    }

})();