嗨皮漫画阅读辅助

增加一些辅助阅读功能(自用)。

目前为 2023-03-07 提交的版本。查看 最新版本

// ==UserScript==
// @name         嗨皮漫畫閱讀輔助
// @name:zh-CN   嗨皮漫画阅读辅助
// @version      2.4
// @description  增加一些輔助閱讀功能(自用)。
// @description:zh-CN  增加一些辅助阅读功能(自用)。
// @author       tony0809
// @match        *://m.happymh.com/*
// @icon         https://m.happymh.com/favicon.ico
// @grant        none
// @run-at       document-end
// @license GPL
// @namespace https://greasyfork.org/users/20361
// ==/UserScript==

(() => {
    'use strict';
    const options = { //true 開啟,false 關閉
        kn: true, //按鍵盤右方向鍵前往下一話。
        kp: true, //按鍵盤左方向鍵前往上一話。
        dn: true, //雙擊前往下一話,方便手機使用。
        kdn: [false, 300], //按住空白鍵超過幾毫秒下一話。
        nE: true, //閱讀頁底部增加更新頁和收藏頁的按鈕。
        pl: true, //閱讀頁預讀全部圖片,並且嘗試預讀下一話圖片。
        hE: true, //隱藏閱讀頁頂部的公告。
        ion: [false, 200], //下一話按鈕完全進入視窗可視範圍內時經過幾ms後自動下一話。
        lM: true, //更新頁自動點擊載入更多。
        list: true, //目錄頁自動展開全部章節。
        oint: true //在新分頁打開漫畫鏈接。
    },
          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,
          read = /^\/reads\/\w+\/\d+$/.test(lp),
          latest = /^\/latest$/.test(lp),
          list = /^\/manga\/\w+$/.test(lp),
          book = /^\/bookcase$/.test(lp),
          rank = /^\/rank/.test(lp),
          user = /^\/user/.test(lp),
          addGlobalStyle = css => {
              let style = document.createElement('style');
              style.type = 'text/css';
              style.innerHTML = css;
              document.head.appendChild(style);
          },
          readCss = `
article img {
    width: auto !important;
    height: auto !important;
    max-width: 100% !important;
    display: block !important;
    margin: 0 auto !important
}
          `,
          hasTouchEvents = () => {
              if (('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0)) {
                  return true;
              }
              return false;
          },
          loadMore = e => {
              let LoadMore = ge(e);
              if (hasTouchEvents()) {
                  let dispatchTouchEvent = (ele, type) => {
                      let touchEvent = document.createEvent('UIEvent');
                      touchEvent.initUIEvent(type, true, true);
                      touchEvent.touches = [{
                          clientX: 1,
                          clientY: 1
                      }];
                      ele.dispatchEvent(touchEvent);
                  };
                  dispatchTouchEvent(LoadMore, "touchstart");
                  dispatchTouchEvent(LoadMore, "touchend");
                  console.log('嗨皮漫畫模擬觸控點擊');
              } else {
                  LoadMore.click();
                  console.log('嗨皮漫畫模擬點擊');
              }
          },
          openInNewTab = () => document.querySelectorAll('.home-banner a:not([target=_blank]),.manga-rank a:not([target=_blank]),.manga-cover a:not([target=_blank])').forEach(a => {
              a.setAttribute('target', '_blank');
          }),
          preLoad = (pn, text) => {
              let lps = pn.split('/'),
                  mangaCode = lps[2],
                  id = lps[3],
                  apiUrl = `https://m.happymh.com/v2.0/apis/manga/read?code=${mangaCode}&cid=${id}`;
              fetch(apiUrl).then(res => res.text()).then((res) => {
                  try {
                      let jsonData = JSON.parse(res);
                      if (jsonData.status == 0) {
                          let imgs = jsonData.data.scans;
                          let F = new DocumentFragment();
                          for (let i in imgs) {
                              let img = new Image();
                              img.src = imgs[i].url;
                              F.appendChild(img);
                          }
                          console.log(text + '漫畫名稱:' + jsonData.data.manga_name + '\n章節名稱:' + jsonData.data.chapter_name + '\n', jsonData, '\n', '圖片預讀\n', F);
                      } else if (jsonData.status == 403) {
                          console.log(text + '獲取數據失敗\n', jsonData);
                      }
                  } catch (error) {
                      console.error(error);
                  }
              }).catch(error => console.error(error));
          };

    if (options.oint && !read && !list && !user) {
        openInNewTab();
        console.log('嗨皮漫畫在新分頁打開漫畫鏈接');
        new MutationObserver(() => {
            openInNewTab();
        }).observe(document.body, {
            childList: true,
            subtree: true
        });
    }

    if (options.lM && latest) {
        new IntersectionObserver((e) => {
            if (e[0].isIntersecting) {
                loadMore('.more-div-btn');
                console.log('載入更多');
            }
        }).observe(ge('.more-div-btn'));
    }

    if (options.list && list) {
        ge('#expandButton').click();
        console.log('嗨皮漫畫自動展開目錄');
    }

    if (options.hE && read) {
        addGlobalStyle('#root>div>header+div{display:none!important}');
    }

    if (options.kn && read) {
        document.addEventListener('keydown', (e) => {
            let key = window.event ? e.keyCode : e.which;
            if (key == 39) {
                let n = gx("//a[span[text()='下一话' or text()='下一話'] and contains(@href,'reads')]");
                if (n) {
                    location.href = n.href;
                } else {
                    alert('沒有下一话了!');
                }
            }
        });
    }

    if (options.kp && read) {
        document.addEventListener('keydown', (e) => {
            let key = window.event ? e.keyCode : e.which;
            if (key == 37) {
                let p = gx("//a[span[text()='上一话' or text()='上一話'] and contains(@href,'reads')]");
                if (p) {
                    location.href = p.href;
                } else {
                    alert('沒有上一话了!');
                }
            }
        });
    }

    if (options.dn && read) {
        document.addEventListener('dblclick', () => {
            let n = ge('footer a');
            location.href = n.href;
        });
    }

    if (options.kdn[0] && read) {
        let timeId;
        document.addEventListener('keypress', (e) => {
            let key = window.event ? e.keyCode : e.which;
            if (key == 32) {
                let n = gx("//a[span[text()='下一话' or text()='下一話'] and contains(@href,'reads')]");
                if (n) {
                    timeId = setTimeout(() => {
                        location.href = n.href;
                    }, options.kdn[1]);
                } else {
                    timeId = setTimeout(() => {
                        alert('沒有下一話了!');
                    }, options.kdn[1]);
                }
            }
        });
        document.addEventListener('keyup', (e) => {
            let key = window.event ? e.keyCode : e.which;
            if (key == 32) {
                clearTimeout(timeId);
            }
        });
    }

    if (options.nE && read) {
        setTimeout(() => {
            let f = ge('footer>article');
            let c1 = f.firstChild.cloneNode(true);
            c1.firstChild.href = '/latest';
            c1.firstChild.firstChild.innerText = '更新';
            f.appendChild(c1);
            let c2 = f.firstChild.cloneNode(true);
            c2.firstChild.href = '/bookcase';
            c2.firstChild.firstChild.innerText = '收藏';
            f.appendChild(c2);
            let p = ge('footer p:nth-child(2)>a[href*=reads]');
            if (p) {
                p.classList.add('MuiButton-containedPrimary');
            }
            let n = ge('footer a[href*=readMore]');
            if (n) {
                n.classList.remove('MuiButton-containedPrimary');
                n.firstChild.innerText = '^_^感谢您的阅读~已经没有下一话了哦~';
            }
        }, 5000);
    }

    if (options.pl && read) {
        addGlobalStyle(readCss);
        let loop = setInterval(() => {
            let iL = ge('[id^=imageLoader]');
            if (iL) {
                clearInterval(loop);
                console.log('嗨皮漫畫預讀全部圖片');
                preLoad(lp, '嗨皮漫畫本話數據\n');
                setTimeout(() => {
                    let next = gx("//a[span[text()='下一话' or text()='下一話'] and contains(@href,'reads')]");
                    if (next) {
                        preLoad(next.pathname, '嗨皮漫畫下一話數據\n');
                    }
                }, 5000);
            }
        }, 100);
    }

    if (options.ion[0] && read) {
        setTimeout(() => {
            console.log('嗨皮漫畫下一話按鈕完全進入可視範圍內時自動下一話');
            new IntersectionObserver((e) => {
                if (e[0].isIntersecting) {
                    setTimeout(() => {
                        let n = gx("//a[span[text()='下一话' or text()='下一話'] and contains(@href,'reads')]");
                        if (n) location.href = n.href;
                    }, options.ion[1]);
                }
            }, {
                threshold: 1,
            }).observe(ge('footer a'));
        }, 5000);
    }

})();