包子漫畫閱讀輔助

包子漫畫閱讀輔助,瀑布流閱讀連續載入圖片,在新分頁打開漫畫鏈接(自用)。

Tính đến 04-03-2023. Xem phiên bản mới nhất.

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

})();