dc165015 / poco

// ==UserScript==
// @name         poco
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  show all full-scale images by Ctrl+`
// @author       You
// @match        http://www.poco.cn/works/detail*
// @grant        none
// @license      MIT
// @updateURL    https://openuserjs.org/meta/dc165015/poco.meta.js
// @downloadURL  https://openuserjs.org/install/dc165015/poco.user.js
// @copyright    2019, dc165015 (https://openuserjs.org/users/dc165015)
// ==/UserScript==

(function() {
    'use strict';
    let $ = window.jQuery;
    let srcs = [];

    let bigImgsCount = 0;
    let isBoardShowed = false;
    let slidesHandler, slidesInerval = 1000;
    let title = document.title;
    let $loadedBigImgBoxes = new Map();
    let $thumbs = $('.cc_thumbs_item > img');
    let $board = $('<div class="top-board"></div>');

    setStyle();
    setHotkey();

    function showAllBigImgs(doShow = true) {
        if (doShow) {
            $(document.body).prepend($board);
            startShow();
        } else {
            $board.detach();
            $board.children().detach();
        }
    }

    function startShow() {
        let srcs = getBigImgsSrc();
        let i = bigImgsCount = srcs.length;
        while (i-- > 0) {
            try {
                showBigImgBy(srcs[i]);
            } catch (e) {}
        }
    }

    function getBigImgsSrc() {
        let srcs=[];
        $thumbs.each((i, img)=>{
            let src = img.src.replace('W120', 'H1920');
            srcs.push(src);
        });
        return srcs;
    }

    function showBigImgBy(src, $box, retryNo = 0) {
        let $loadedImgBox, $img;

        if (!$box && ($loadedImgBox = $loadedBigImgBoxes.get(src))) {
            $board.prepend($loadedImgBox);
        } else {
            $box = $box || createBigImgWithBox(src);
            $img = $box.find('img');
            $img.length && initImg();
        }

        function initImg() {
            setImgSrc($img, src);

            return new Promise((resolve, reject) => {
                var img = $img[0];
                $board.prepend($box);

                $img.load(() => {
                    $img.css('display', 'block');
                    updateTitle();
                    resolve();
                });

                img.onerror = img.onsuspend = img.onstalled = img.onemptied = () => {
                    retryImg(src, $img, ++retryNo);
                    reject(retryNo);
                };
            });
        }
    }

    function updateTitle() {
        let loadedCount = $board.find('img[style*="display: block;"]').length;
        document.title = loadedCount != bigImgsCount ? `${loadedCount}/${bigImgsCount}|${title}` : title;
    }

    function createBigImgWithBox(src) {
        let $img = $('<img class="full-img"/>');
        let $box = $('<div class="full-img-box"></div>');
        $box.append($img);
        $loadedBigImgBoxes.set(src, $box);
        listenDblClick();
        return $box;

        function listenDblClick() {
            $box.dblclick((e) => {
                if (e.ctrlKey) {
                    $box.detach();
                    bigImgsCount--;
                } else {
                    reload$img($img);
                }
            });
        }
    }

    function reload$img($img) {
        let src = $img.attr('src');
        let index = src.indexOf('?');
        src = index == -1 ? src : src.substring(0, src.indexOf('?'));
        setImgSrc($img, src);
    }

    function setImgSrc($img, src) {
        // src = src + '?' + Date.now();
        $img.attr('src', src);
    }

    function retryImg(src, $img, retryNo) {
        if (retryNo < 10) {
            setTimeout(() => {
                console.warn(`retry to load img(${src}) the ${retryNo} time.`);
                showBigImgBy(src, $img, retryNo);
            }, 10000 * retryNo * retryNo);
        }
    }

    function setStyle() {
        let style = document.createElement('STYLE');
        style.type = 'text/css';
        style.innerHTML = `
            .top-board {
                background-color: black;
                position: absolute;
                min-height: 100%;
                width: 100%;
                z-index: 10000;
            }

            .full-img-box {
                display: block;
                position: relative;
                width: 100%;
                height: 100vh;
                margin-bottom: 1px;
            }

            .full-img {
                position: absolute;
                top: 0;
                bottom: 0;
                left: 0;
                right: 0;
                margin: auto;
                max-width: 100%;
                max-height: 100%;
            }

            .trigger-link {
                float: right;
                color: blue !important;
            }
        `;
        document.head.append(style);
    }

    function setHotkey() {
        let scrollOptions = {
            behavior: "auto",
            block: "start",
            inline: "nearest"
        };

        document.addEventListener('keydown', (e) => {
            if (e.ctrlKey) {
                if (e.key == '`') {
                    window.scrollTo(0,0);
                    showAllBigImgs(isBoardShowed = !isBoardShowed);
                } else if (e.key == 'Backspace') {
                    reset();
                }
            } else if (isBoardShowed) {
                e.stopPropagation();
                e.preventDefault();
                if (e.key == 'ArrowDown') {
                    scrollToImgBox('next');
                } else if (e.key == 'ArrowUp') {
                    scrollToImgBox('previous');
                } else if (e.key == ' ') {
                    doSlides();
                }
            }
        });

        function reset() {
            showAllBigImgs(isBoardShowed = false);
        }

        function getCurrentImgBoxInView() {
            let boxes = getBoxes();
            let current;
            for (let i = 0; i < boxes.length; i++) {
                current = boxes[i];
                if (window.scrollY >= current.offsetTop && window.scrollY <= current.offsetTop + current.scrollHeight) {
                    break;
                }
            };
            return current;
        }

        function getBoxes() {
            return $board.find('.full-img-box');
        }

        function scrollToImgBox(direction = 'next') {
            let prev, next, current = getCurrentImgBoxInView();
            switch (direction) {
                case 'previous':
                    prev = current.previousElementSibling || current;
                    prev && prev.scrollIntoView(scrollOptions);
                    break;
                case 'next':
                    next = current.nextElementSibling || current;
                    next && next.scrollIntoView(scrollOptions);
            }
        }

        function doSlides() {
            if (slidesHandler) {
                stopSlides();
            } else {
                startSlides();
            }
        }

        function stopSlides() {
            clearInterval(slidesHandler);
            slidesHandler = void 0;
        }

        function startSlides() {
            let current = getCurrentImgBoxInView();
            if (current) {
                let prev = current.previousElementSibling;
                if (!prev) {
                    prev = current.nextElementSibling;
                    while (prev && prev.nextElementSibling) {
                        prev = prev.nextElementSibling;
                    }
                }
                prev.scrollIntoView(scrollOptions);
                slidesHandler = setTimeout(() => startSlides(prev), slidesInerval);
            }
        }
    }
})();