拷贝漫画简易阅读

简单的拷贝漫画阅读器,J/K 翻页,左右方向键改变章节,分号键奇偶切换,1/2 改变单双页

// ==UserScript==
// @name        拷贝漫画简易阅读
// @namespace   http://tampermonkey.net/
// @match       *://*.copymanga.com/comic/*/chapter/*
// @match       *://*.copymanga.org/comic/*/chapter/*
// @match       *://*.copymanga.site/comic/*/chapter/*
// @match       *://*.copymanga.tv/comic/*/chapter/*
// @match       *://*.mangacopy.com/comic/*/chapter/*
// @grant       none
// @version     1.25
// @author      chemPolonium
// @description 简单的拷贝漫画阅读器,J/K 翻页,左右方向键改变章节,分号键奇偶切换,1/2 改变单双页
// @run-at      document-end
// @license     GPLv3
// ==/UserScript==

/* jshint esversion: 6 */
/* jshint multistr: true */
(function() {
  'use strict';

  document.getElementsByClassName('header')[0].remove();

  let comicContainerFluid = document.getElementsByClassName('container-fluid comicContent')[0];
  comicContainerFluid.style.paddingRight = '0px';
  comicContainerFluid.style.paddingLeft = '0px';

  let comicContainer = comicContainerFluid.children[0];
  comicContainer.style.marginRight = '0px';
  comicContainer.style.marginLeft = '0px';
  comicContainer.style.setProperty('min-width', '10px', 'important');
  comicContainer.style.setProperty('max-width', '100%', 'important');

  let comicList = comicContainer.children[0];
  comicList.style.paddingTop = '0px';
  comicList.style.marginBottom = '0px';
  comicList.style.setProperty('min-width', '10px', 'important');
  comicList.style.setProperty('max-width', '100%', 'important');
  comicList.style.setProperty('width', '100%', 'important');
  comicList.style.display = 'grid';
  comicList.style.direction = 'rtl';

  let comicListChildren = comicList.children;
  let currentImageIndex = 0;

  let comicIndex = document.getElementsByClassName('comicIndex')[0];

  function refreshIndex() {
    comicIndex.textContent = currentImageIndex;
  }

  function getImage(imageIndex) {
    return comicListChildren[imageIndex].children[0];
  }

  function getCurrentImage() {
    return getImage(currentImageIndex);
  }

  function moveToCurrentImage() {
    if (currentImageIndex == 0) {
      window.scrollTo(0, 0);
    } else {
      window.scrollTo(0, getCurrentImage().offsetTop);
    }
  }

  let pageNumPerScreen = 2;

  function preloadImage() {
    // simulate the scroll for preload
    // the script is like this: total client height / 3 < window scrollY then not load
    // so first scroll Y to 0
    window.scrollTo(0, 0);
    for (let i = 0; i < pageNumPerScreen; i++) {
      // window.dispatchEvent(scrollEvent);
      window.onscroll();
      // dispatch the scroll event for preload
    }
    // this function will scroll Y to 0
  }

  function moveImageIndex(x) {
    let newImageIndex = currentImageIndex + x;
    if (newImageIndex < comicList.children.length && newImageIndex >= 0) {
      currentImageIndex = newImageIndex;
    }
  }

  function setSingleAlign(imageIndex) {
    if (pageNumPerScreen == 1)
    {
      comicListChildren[imageIndex].children[0].style.objectPosition = 'center';
      // ('style', 'text-align: center;');
    }
    if (pageNumPerScreen == 2)
    {
      comicListChildren[imageIndex].children[0].style.objectPosition = (imageIndex % 2 == 0) ? 'left' : 'right';
    }
  }

  function setAlign() {
    for (let imageIndex = 0; imageIndex < comicListChildren.length; imageIndex++) {
      setSingleAlign(imageIndex);
    }
  }

  function setPageNumPerScreen(pageNum) {
    comicList.style.gridTemplateColumns = 'repeat(' + String(pageNum) + ', 1fr)';
    moveToCurrentImage();
    pageNumPerScreen = pageNum;
    setAlign();
  }

  setPageNumPerScreen(2);

  function onePageDown() {
    preloadImage();
    moveImageIndex(pageNumPerScreen);
    moveToCurrentImage();
    refreshIndex();
  }

  function onePageUp() {
    moveImageIndex(-pageNumPerScreen);
    moveToCurrentImage();
    refreshIndex();
  }

  function createTitlePage() {
    let titlePage = document.createElement('li');
    let titlePageDiv = document.createElement('div');
    let titlePageTitle = document.createElement('p');
    titlePageTitle.appendChild(document.createTextNode(document.title));
    titlePageTitle.setAttribute('style', 'color: white;\
      font-size: xx-large;\
      max-width: 30vw;\
      margin-top: 30%;\
      margin-right: 20%;\
      white-space: normal;');
    titlePageDiv.appendChild(titlePageTitle);
    titlePage.appendChild(titlePageDiv);
    return titlePage;
  }

  function resizeImage(targetli) {
    targetli.style.height = '100vh';
    targetli.style.maxWidth = '100%';
    targetli.firstChild.style.setProperty('height', '100%', 'important');
    targetli.firstChild.style.setProperty('width', '100%', 'important');
    targetli.firstChild.style.objectFit = 'contain';
  }

  function setAllPages() {
    for (let i = 1; i < comicListChildren.length; i++) {
      if (comicListChildren[i].children[0]) {
        resizeImage(comicListChildren[i]);
      }
    }
  }

  let titlePage = createTitlePage();

  let parityChanged = false;

  function switchParity() {
    if (parityChanged) {
      comicListChildren[0].remove();
    } else {
      comicList.insertAdjacentElement('afterbegin', titlePage);
    }
    parityChanged = !parityChanged;
    setAlign();
    moveToCurrentImage();
  }

  let footer = document.getElementsByClassName('footer')[0];
  let footerChildren = footer.children;
  let prevChapterHref = footerChildren[1].children[0].href;
  let nextChapterHref = footerChildren[3].children[0].href;
  let chapterListHref = footerChildren[4].children[0].href;

  function toggleFullScreen() {
    if (!document.fullscreenElement) {
      document.documentElement.requestFullscreen();
    } else {
      if (document.exitFullscreen) {
        document.exitFullscreen();
      }
    }
  }

  document.addEventListener('keydown', (event) => {
    switch (event.code) {
      case 'ArrowRight':
        window.location = nextChapterHref;
        break;
      case 'ArrowLeft':
        window.location = prevChapterHref;
        break;
      case 'KeyK':
        onePageUp();
        break;
      case 'KeyJ':
        onePageDown();
        break;
      case 'KeyL':
        window.location = chapterListHref;
        break;
      case 'KeyR':
        setAllPages();
        break;
      case 'KeyF':
        toggleFullScreen();
        break;
      case 'Semicolon':
        switchParity();
        break;
      case 'Digit1':
        setPageNumPerScreen(1);
        break;
      case 'Digit2':
        setPageNumPerScreen(2);
        break;
      default:
        console.log('key: ' + event.key + ' code: ' + event.code);
    }
  });

  footer.remove();

  let firstLoad = true;

  const comicListObserverConfig = { childList: true };

  const firstLoadCallback = () => {
    if (firstLoad && comicListChildren.length > 0) {
      firstLoad = false;
      switchParity();
      // 有时脚本加载得慢,会导致前几页来不及修改大小,因此第一次直接全设置一遍
      setAllPages();
    }
  };

  const comicListCallback = (mutationList, observer) => {
    for (const mutation of mutationList) {
      firstLoadCallback();
      for (const targetli of mutation.addedNodes) {
        resizeImage(targetli);
        // 一般一次也就加载一页或两页,加载两页的话只设置最后一页的左右是不够的
        setSingleAlign(comicListChildren.length - 2);
        setSingleAlign(comicListChildren.length - 1);
      }
    }
  };

  const comicListObserver = new MutationObserver(comicListCallback);

  comicListObserver.observe(comicList, comicListObserverConfig);

  // 有的时候会出现列表加载完了,脚本还没加载上的情况,这时候 MutationObserver 会失效
  // 这个时候就手动加个延时当作第一次调用
  setTimeout(firstLoadCallback, 50);

  // 下面是旧版的监听方式,已经被 observer 取代
  // comicList.addEventListener('DOMNodeInserted', (event) => {
  //   if (firstLoad && comicListChildren.length > 2) {
  //     firstLoad = false;
  //     switchParity();
  //   }
  //   resizeImage(event.target);
  //   // event.target.style.height = '100vh';
  //   // event.target.style.maxWidth = '100%';
  //   // event.target.firstChild.style.setProperty('height', '100%', 'important');
  //   // event.target.firstChild.style.setProperty('width', '100%', 'important');
  //   // event.target.firstChild.style.objectFit = 'contain';
  //   setSingleAlign(comicListChildren.length - 1);
  // });

})();