VBookProcess - Weread Progress Show

在网页版微信读书右下角显示当前阅读进度

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

You will need to install an extension such as Tampermonkey to install this script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==UserScript==
// @name         VBookProcess - Weread Progress Show
// @namespace    https://github.com/chan1919/VBookProcess
// @version      1.0.0
// @description  在网页版微信读书右下角显示当前阅读进度
// @match        https://weread.qq.com/*
// @grant        GM_addStyle
// @license      MIT
// @run-at       document-idle
// @supportURL   https://github.com/chan1919/VBookProcess/issues
// @homepageURL  https://github.com/chan1919/VBookProcess
// ==/UserScript==

(function () {
    'use strict';

    let currentChapterRatio = 0;
    let nextChapterRatio = 0;
    let result = 0;
    let chapterObserver = null;
    let initAttempts = 0;
    const MAX_ATTEMPTS = 30;

    GM_addStyle(`
        #weread-progress-box {
            position: fixed !important;
            bottom: 60px !important;
            right: 5px !important;
            width: 50px !important;
            height: 30px !important;
            font-size: 12px !important;
            padding: 0 !important;
            align-items: center !important;
            text-align: center !important;
            display: flex !important;
            justify-content: center !important;
            background-color: #6b6b6b !important;
            color: #fff !important;
            border-radius: 5px !important;
            z-index: 2147483647 !important;
            pointer-events: none !important;
        }
    `);

    function getChapterItems() {
        const items = document.querySelectorAll('.readerCatalog_list_item');
        return items;
    }

    function isOnReaderPage() {
        return window.location.href.includes('/web/reader/') ||
               document.querySelector('.readerContent') !== null ||
               document.querySelector('.readerCatalog_list') !== null;
    }

    function updateChapterRatio() {
        const chapterItems = getChapterItems();
        if (chapterItems.length === 0) {
            return false;
        }

        const chapterTotal = chapterItems.length;
        let found = false;

        for (let i = 0; i < chapterTotal; i++) {
            const className = chapterItems[i].className || '';
            if (className.includes('readerCatalog_list_item_selected')) {
                currentChapterRatio = parseFloat(((i / chapterTotal) * 100).toFixed(1));
                nextChapterRatio = parseFloat((((i + 1) / chapterTotal) * 100).toFixed(1));
                result = currentChapterRatio;
                updateProgressBox();
                found = true;
                break;
            }
        }

        return found;
    }

    function watchChapterChange() {
        const chapterItems = getChapterItems();

        if (chapterItems.length === 0) {
            return false;
        }

        updateChapterRatio();

        if (chapterObserver) {
            chapterObserver.disconnect();
        }

        chapterObserver = new MutationObserver(function (mutations) {
            mutations.forEach(function (mutation) {
                if (mutation.type === 'attributes') {
                    updateChapterRatio();
                }
            });
        });

        chapterItems.forEach(item => {
            chapterObserver.observe(item, { attributes: true });
        });

        return true;
    }

    function updateScrollRatio() {
        const scrollPosition = window.scrollY || window.pageYOffset;
        const docHeight = document.documentElement.scrollHeight;
        const winHeight = window.innerHeight;
        const maxScrollPosition = docHeight - winHeight;

        if (maxScrollPosition > 0) {
            const scrollRatio = scrollPosition / maxScrollPosition;
            const totalRatio = (nextChapterRatio - currentChapterRatio) * scrollRatio + currentChapterRatio;
            result = parseFloat(totalRatio.toFixed(1));
            updateProgressBox();
        }
    }

    function updateProgressBox() {
        let progressBox = document.querySelector('#weread-progress-box');

        if (!progressBox) {
            progressBox = document.createElement('div');
            progressBox.id = 'weread-progress-box';
            document.body.appendChild(progressBox);
        }

        progressBox.textContent = result + '%';
    }

    function tryInit() {
        if (!isOnReaderPage()) {
            return;
        }

        initAttempts++;

        if (initAttempts > MAX_ATTEMPTS) {
            return;
        }

        updateProgressBox();

        const success = watchChapterChange();

        if (!success) {
            setTimeout(tryInit, 500);
        }
    }

    function init() {
        updateProgressBox();
        tryInit();

        window.addEventListener('scroll', updateScrollRatio, { passive: true });

        let lastUrl = location.href;
        new MutationObserver(() => {
            const url = location.href;
            if (url !== lastUrl) {
                lastUrl = url;
                initAttempts = 0;
                setTimeout(tryInit, 1000);
            }
        }).observe(document, { subtree: true, childList: true });

        const bodyObserver = new MutationObserver(function (mutations) {
            const chapterItems = getChapterItems();
            if (chapterItems.length > 0 && !chapterObserver) {
                watchChapterChange();
            }
        });

        bodyObserver.observe(document.body, {
            childList: true,
            subtree: true
        });
    }

    if (document.readyState === 'complete' || document.readyState === 'interactive') {
        setTimeout(init, 500);
    } else {
        window.addEventListener('load', function() {
            setTimeout(init, 500);
        });
    }

})();