Greasy Fork

Greasy Fork is available in English.

包子漫画阅读辅助

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

当前为 2023-07-10 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name               包子漫畫閱讀輔助
// @name:en            Baozi Manga Read Helpr
// @name:zh-CN         包子漫画阅读辅助
// @name:zh-TW         包子漫畫閱讀輔助
// @version            2.5.3
// @description        包子漫畫閱讀輔助,瀑布流閱讀連續載入圖片,在新分頁打開漫畫鏈接(自用)。
// @description:en     read infinite scroll,manga link open in newtab
// @description:zh-CN  包子漫画阅读辅助,瀑布流阅读连续载入图片,在新分页打开漫画链接(自用)。
// @description:zh-TW  包子漫畫閱讀輔助,瀑布流閱讀連續載入圖片,在新分頁打開漫畫鏈接(自用)。
// @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/*
// @match              *://tw.czmanga.com/*
// @match              *://cn.czmanga.com/*
// @match              *://www.czmanga.com/*
// @icon               https://www.baozimh.com/favicon.ico
// @require            https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js
// @license            MIT
// @namespace          http://greasyfork.icu/users/20361
// ==/UserScript==

(() => {
    'use strict';
    const options = { //true 開啟,false 關閉
        oint: true, //在新分頁打開漫畫鏈接。
        aH: true, //載入下一話時添加瀏覽器歷史紀錄
        aO: true, //目錄頁自動展開全部章節。
        pln: true, //1頁1線程逐張預讀的圖片,可減少等待加載圖片的時間,如果滾動、滑動的閱讀速度大於預讀速度還是需要待圖片載入。
        topBtn: false, //添加返回頂部按鈕
        remove: [true, 4] //!!!不能小於2!!!閱讀載入超過n話時刪除前面話數的圖片。
    },
          ge = (selector, doc) => (doc || document).querySelector(selector),
          gae = (selector, doc) => (doc || document).querySelectorAll(selector),
          gx = (xpath, doc) => (doc || document).evaluate(xpath, (doc || document), null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue,
          lp = location.pathname,
          home = /^\/$/.test(lp),
          classify = /^\/classify/.test(lp),
          list = /^\/list\/new/.test(lp),
          search = /^\/search\?/.test(lp),
          comic = /^\/comic\/[^.]+$/.test(lp),
          read = /^\/comic\/chapter\/[^.]+\.html$/.test(lp),
          loading_bak = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAT4AAAGqBAMAAABg4TVWAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAASUExURefn5+Dg4NjY2M7OzsPDw7i4uMuujGEAAAXgSURBVHja7drLdqJKGAXgXaBzojJHDXMUmKtQ80Rqv/+rnEFxMzE53YNTctba35B0r7Wt618FgIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI/F+YJWXJvj/aLyhfFJ2+PNmekC0nX4GonLWXObYnoFhMvFUCmMq15flwOJRVyyYBcF5MvlvfahVJuvacjKkXYVc8nbXxZSGTt/nhD83ymu9BuogZEnV//5eQ6p9bqV7ACNzdf2la9/IpHD+PYNoTAOxe3cORfb6LrcgrALxfXxpv0/4w+Cz7gNXphekq97z1TEWSPAHANvg2ZzaHsizLyjb739KRLADAHAJXWjGAt7fD2w/pjxy54WEWOt+PbXu0XFq+aGrJh3Tk/Q9+0X+Q78uKl7N7ks61TTn8w23YajT7sgT6pWQ7S9ecH37D+YX53v1SspnSufLrlvLxsnzbxwFH0p2/bXhR2DJh3eerEFXf0vnt4nibL8yrsHVg6htox5YkeUtJZ+fpTEUWIG/DDwqbL08AwPQNliEl77sp3cafkjbDDgykYU/CtV9lfOP53ezDWHYJAGyHLu/GDQ552HztmM9l/QS59HXgbAVs/HMAqMPm6wDzlqAmO1ORru7bacf5ZH6f2q8OWkWbu2lJ3mKSljwjJZOh5vNj0pJuNe2/Nmi++MOPvaL2PRzVJABEw8DbIyU/VyQ/XpFvfQF2JO8r0iU4kn7/XZFkW/EO5ORlPXYvbNDxl2e+LzvTD0DyHt+ANckT4j5fsSaZvSJf2zdWh5rOks2mPFh2iEkWiNhhU5NFyn5YBp6/0R0AjOUda5JuDxxJMoElPxDRz+Ispx+WofOt/JoWtxli8jYcN+4JcrIbF8Asn8rnugg8/MyxLDPA0CVTBdPs5oVCVnMoW5EHrF9MNyx1BWALX/45kuR1LE4tmczypZfQ3Rv3i6+vSm9+6fuo/dqcoM83HD9WAevTfq+qSWa+/KtY+HyfKXlFzgyWTOzsePQZbvZ+jvcrrEmyy2IWEVmT95j8QOrzwZKfD1M+YO3nOzgnne0Qs7F0K9IZ8o4VT0eSmLa3fswGmR3DSI/Jz5guqdnPkIgkLDv0s3ier6/IAjiORQK7BNYhpSXZ2g7Wr3nDamPG6g9AHmr0jRvBqgGQ81CTbPbbG1CTl9V0Torm+daBNpBZnWRd5isWd/LHoTX5GT3mG7eNuAidz5AuiUie/RrospjsQNI1h2RHF8/zmUAL9Gaej3fYLjHvfVVqSKLuj2yb024qr8JdcEwXAYYksc2mA1FWk1nezW/ZpnyrQBPkfejhiOQ97aO1JHlJyYvxL1f757PhEGiCxNdZvkvty4EkJ/3kvWNTtlMNM5tOoSrAYaVdkSwsySbzB6MO0XjNMZj9v1BX0LvLlC+xbPbVDTVJlwBf74qmfCbYS4ZhK01JB3va2r74K/qahs9un00ZsL7KhvrqE4f44aI5nV/uzvO9Bzx/9MePL02WjQdguua8T4B8Kp/jkO+4fAdHfesMr2LcqX84fsWxm8rTNuj1Qd03lW/H7dCjtwRmqkgBvA/5dmFvd9Ni9kohsvObXcv5QDsWgYu/8XoIsI2/Q32Yss5ef1vSg12vAWa4uCDJ0+yS3DU/ngiCGSP41rvOXzI8ed96CxwP1TAB7PiOF5vKknTN97aKg39jMnRYZOmaqb3M29OO3IWO95dvC8J/JPYHb4M2Zdv2rwiL5eWLLNuyLMu2SYIVpvMR/y/7wZbjNDmeN1havpjzYu8tfL7o1/419tUfrZlfA+RXvFqfLzq2dvY1Z/9wAZ/UZQAQVaRrq7b1Z6Sx+RbwUXECmIrN+W1Y7CxvyeP15YtFtpm3kznavjDYLeKT3fh7HXC0JwC4LqL1no0x83774S9LEZ0RYdH2CUREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREZJH+Af50vOqowef/AAAAAElFTkSuQmCC',
          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 => {
              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;
          },
          getNextLink = () => {
              let nextlink = null;
              let next = ge('#next-chapter');
              if (next) {
                  nextlink = next.href;
                  //可能會遇到當前域名和下一頁鏈接的域名不同,導致發生跨域請求出錯的情況,需替換為當前域名。
                  const nh = next.host,
                        lh = location.host;
                  if (nh !== lh) {
                      nextlink = nextlink.replace(nh, lh);
                  }
              }
              return nextlink;
          },
          picPreload = async (imgsArray, index, str) => {
              const loadImg = src => {
                  return new Promise(resolve => {
                      let num = src.match(/(\d+)\.[a-z]{3,5}$/i)[1];
                      let temp = new Image();
                      temp.src = src;
                      temp.onload = () => {
                          resolve(`${(str || '')}[Pic(${num})][Preload OK]\n${src}`);
                          temp = null;
                      };
                      temp.onerror = (e) => {
                          resolve(`${(str || '')}[Pic(${num})][Preload ERROR]\n${src}`);
                          setTimeout(() => {
                              console.log(`Preload重新載入圖片:\n${src}\n`, loadImg(src));
                          }, 500);
                          temp = null;
                      };
                  });
              };
              for (let i = index; i < imgsArray.length; i++) {
                  let msg = await loadImg(imgsArray[i].getAttribute('src'));
                  console.log(msg);
                  msg = null;
              }
          },
          preloadNext = () => {
              let url = getNextLink();
              if (url !== null) {
                  fetch(url).then(res => res.text()).then(res => {
                      var doc = null;
                      doc = parseHTML(res);
                      let imgs = gae('.comic-contain amp-img', doc);
                      let title = ge('span.title', doc).innerText;
                      let firstImgSrcNum = imgs[0].getAttribute('src').match(/(\d+)\.[a-z]{3,5}$/i)[1];
                      const num = (n) => Math.round(n / 50 + 1);
                      picPreload(imgs, 0, `[${title} part${num(firstImgSrcNum)}]`);
                  });
              }
          },
          fetchData = url => {
              fetch(url).then(res => res.text()).then(res => {
                  let doc = parseHTML(res);
                  insertData(doc, url);
              }).catch(error => {
                  console.error(error);
                  ge('.loadingImg').style.display = 'none';
                  ge('.loadingText').innerText = '連線出錯,請返回頂部重新載入。';
              });
          },
          imagesObserver = new IntersectionObserver((entries, observer) => {
              entries.forEach(entry => {
                  if (entry.isIntersecting) {
                      observer.unobserve(entry.target);
                      let realSrc = entry.target.dataset.src,
                          nE = entry.target.nextElementSibling;
                      if (realSrc) {
                          entry.target.src = realSrc;
                          entry.target.onerror = (error) => {
                              error.target.src = loading_bak;
                              setTimeout(() => {
                                  console.log(`Observer重新載入圖片:\n${realSrc}`);
                                  error.target.src = realSrc;
                                  console.log(error.target);
                              }, 500);
                          };
                      }
                      if (nE && nE.tagName == 'IMG' && nE.dataset.src) {
                          nE.src = nE.dataset.src;
                      }
                  }
              });
          }),
          nextObserver = new IntersectionObserver((entries, observer) => {
              entries.forEach(entry => {
                  if (entry.isIntersecting) {
                      observer.unobserve(entry.target);
                      let url = getNextLink();
                      if (url !== null) {
                          console.log(`觸發載入下一頁\n${url}`);
                          addLoad();
                          fetchData(url);
                      }
                  }
              });
          }),
          insertData = (doc, url) => {
              let imgs = gae('.comic-contain amp-img', doc);
              let F = new DocumentFragment();
              let n = 0;
              if (ge('.comic-contain>img')) {
                  let currentLastImgSrc = [...gae('.comic-contain>img')].pop().src;
                  let nextFirstImgSrc = imgs[0].getAttribute('src');
                  //當目前最後一張圖片檔名是50的倍數和下一頁第一張圖片檔名尾數是7且尾數不是1時,則不插入下一頁的前4張圖,讓條漫整體圖片按正確順序銜接。
                  if (/\/(50|100|150|200|250|300)\.[a-z]{3,5}$/i.test(currentLastImgSrc) && /\/(\d+)?7\.[a-z]{3,5}$/i.test(nextFirstImgSrc) && !/\/(\d+)?1\.[a-z]{3,5}$/i.test(nextFirstImgSrc)) {
                      n = 4;
                  }
              }
              for (let i = n; i < imgs.length; i++) {
                  let img = new Image();
                  img.className = 'comic-contain__item';
                  img.src = loading_bak;
                  img.dataset.src = imgs[i].getAttribute('src');
                  imagesObserver.observe(img);
                  F.appendChild(img);
              }
              let load = ge('.chapterLoading');
              if (load) {
                  ['.comic-chapter>.next_chapter', '.bottom-bar', 'span.title'].forEach(e => {
                      ge(e).outerHTML = ge(e, doc).outerHTML; //替換元素
                  });
                  showElement();
                  let title = ge('span.title', doc).innerText;
                  if (!/\/\d+_\d+_\d+\.html$/.test(url)) { //是下一話才添加標題分隔條,下一頁則不添加。
                      let docTitle = doc.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) {
                          preloadNext();
                      }
                      addNextObserver();
                  }, 300);
              } else {
                  showElement();
                  let E = ge('.comic-contain');
                  E.innerHTML = '';
                  E.appendChild(F);
              }
          },
          addNextObserver = () => {
              let lastImg = [...gae('.comic-contain img')].pop();
              nextObserver.observe(lastImg);
          },
          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();
                  }
              }
          },
          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:bottom 6px right 5px;
    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;
    margin-bottom: 20px;
}
.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;
}
.next_chapter + .l-content {
    display: none;
}
          `;

    if (home) {
        addGlobalStyle(`amp-addthis[data-widget-type=floating]{display:none !important}`);
        ge('amp-addthis[data-widget-type=floating]').remove();
    }

    if (read) {
        document['onkeydown'] = null;
        removeAd();
        addGlobalStyle(readCss);
        if (options.topBtn) addGoBack();
        let imgs = [...gae('.comic-contain amp-img')];
        let title = ge('span.title').innerText;
        if (imgs.length > 3 && options.pln) picPreload(imgs, 3, `[${title} part1]`);
        insertData(document);
        addNextObserver();
        if (options.pln) preloadNext();

        const hidetoolbar = () => {
            var e = e || window.event;
            if (e.wheelDelta < 0 || e.detail > 0) {
                $('div.header').attr('style', 'top: -44px;');
                $('div.bottom-bar').attr('style', 'bottom: -50px;')
            } else {
                $('div.header').attr('style', 'transform: translateY(0%);');
                $('div.bottom-bar').attr('style', 'transform: translateY(0%);')
            }
        };
        $('body').on('wheel', hidetoolbar);
        $('body').on('DOMMouseScroll', hidetoolbar);

        const keyhidetoolbar = (e) => {
            let key = window.event ? e.keyCode : e.which;
            if (key == '34' || key == '32' || key == '40') {
                $('div.header').attr('style', 'top: -44px;');
                $('div.bottom-bar').attr('style', 'bottom: -50px;')
            } else {
                $('div.header').attr('style', 'transform: translateY(0%);');
                $('div.bottom-bar').attr('style', 'transform: translateY(0%);')
            }
        };
        $('body').on('keydown', keyhidetoolbar);

        if (('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0)) {
            let startY, moveY, Y;
            $('body').on('touchstart', (e) => {
                startY = e.originalEvent.changedTouches[0].pageY;
            });
            $('body').on('touchmove', (e) => {
                moveY = e.originalEvent.changedTouches[0].pageY;
                Y = moveY - startY;
                if (Y < 0) {
                    $('div.header').attr('style', 'top: -44px;');
                    $('div.bottom-bar').attr('style', 'bottom: -50px;')
                } else if (Y > 0) {
                    $('div.header').attr('style', 'transform: translateY(0%);');
                    $('div.bottom-bar').attr('style', 'transform: translateY(0%);')
                }
            });
        }
    }

    if (options.oint && !comic && !read) {
        openInNewTab();
        new MutationObserver(() => {
            openInNewTab();
        }).observe(document.body, {
            childList: true,
            subtree: true
        });
    }

    if (options.aO && comic) {
        let button = ge('#button_show_all_chatper');
        new IntersectionObserver(entries => {
            if (entries[0].isIntersecting) {
                button.click();
            }
        }).observe(button);
    }

})();