Greasy Fork

来自缓存

Greasy Fork is available in English.

Fiction.live Word Counter

Display word count for chapters on fiction.live, counting only story posts and automatically updating.

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Fiction.live Word Counter
// @namespace    https://github.com/erasels
// @version      2.1
// @description  Display word count for chapters on fiction.live, counting only story posts and automatically updating.
// @author       erasels
// @match        https://fiction.live/*
// @grant        none
// @icon         https://www.google.com/s2/favicons?sz=64&domain=fiction.live
// @license      MIT
// ==/UserScript==

// Due to AJAX the site doesn't trigger script loads when navigating to /stories/ from the main page, so we catch changes to the URL
(function(history) {
    var pushState = history.pushState;
    history.pushState = function(state) {
        if (typeof history.onpushstate == "function") {
            history.onpushstate({state: state});
        }
        window.dispatchEvent(new CustomEvent('statepushed', {detail: state}));
        return pushState.apply(history, arguments);
    };
})(window.history);

window.addEventListener('statepushed', function(e) {
    setTimeout(() => {
        console.log('Detected navigation:', e.detail);
        checkAndActivate(); // Check URL and activate main logic if appropriate
    }, 1000);
});

function main() {
    // Helper function to count words in the specified element
    function countWords(element) {
        var text = element.innerText || element.textContent;
        return text.split(/\s+/).filter(n => n != '').length;
    }

    // Function to count words only in chapter elements
    function countChapterWords() {
        var chapters = document.querySelectorAll('#storyPosts .chapter:not(.choice, .readerPost) .chapterContent');
        return Array.from(chapters).reduce((acc, chapter) => acc + countWords(chapter), 0);
    }


    // Function to update or insert the word count display
    function updateWordCountDisplay(wordCount) {
        // Define a base function for creating the word count display
        function createWordCountDiv(count) {
            var div = document.createElement('div');
            div.setAttribute('class', 'word-count-display');
            div.setAttribute('style', 'margin-top: 5px; text-align: center; font-weight: 400; font-family: Helvetica Neue,HelveticaNeue,Helvetica,Arial,sans-serif; font-size: 2em;');
            div.innerText = 'Chapter Word Count: ' + count;
            return div;
        }

        // Remove existing word count displays if they exist
        document.querySelectorAll('.word-count-display').forEach(function(div) {
            div.remove();
        });

        var wordCountTop = createWordCountDiv(wordCount);
        var wordCountBottom = createWordCountDiv(wordCount);

        // Insert the top word count display
        var pageBody = document.querySelector('.page-body');
        if (pageBody) {
            pageBody.insertBefore(wordCountTop, pageBody.firstChild);
        }

        // Insert the bottom word count display above the Next Chapter button, if present
        var nextChapterBtn = document.querySelector('a.nextChapter');
        var insertPointForBottom = nextChapterBtn ? nextChapterBtn.parentNode : document.querySelector('.page-body');
        insertPointForBottom.insertBefore(wordCountBottom, nextChapterBtn);
    }

    // Setup MutationObserver to dynamically check for loaded content
    function setupObserver() {
        var targetNode = document.getElementById('storyPosts');

        if (targetNode) {
            console.log('Target node for story posts found. Setting up observer.');

            // Observer for updates from currently active QM
            var observer = new MutationObserver((mutationsList, observer) => {
                // Check if any mutation added nodes and update word count accordingly
                if (mutationsList.some(mutation => mutation.addedNodes.length > 0)) {
                    console.log('New chapter content detected.');
                    var wordCount = countChapterWords();
                    console.log('Updated Word Count:', wordCount);
                    updateWordCountDisplay(wordCount);
                }
            });

            observer.observe(targetNode, { childList: true, subtree: true });

            // Initial word count update
            var initialWordCount = countChapterWords();
            updateWordCountDisplay(initialWordCount);
        } else {
            console.log('Target node not found yet. Retrying...');
            setTimeout(setupObserver, 1000); // Retry after 1 second
        }
    }

    console.log('Word Counter script is active on this page.');

    setupObserver();
}

function checkAndActivate() {
    if (window.location.href.includes('/stories/')) {
        main();
    } else {
        console.log('Not a /stories/ page, script waiting...');
    }
}

// Delay execution until DOM is fully loaded
if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", checkAndActivate);
} else {
    checkAndActivate(); // Immediate execution if the document is already ready
}