AO3 Word Count Script

Adds word counts to chapter links on AO3 Chapter Index pages.

Version au 18/03/2023. Voir la dernière version.

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

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

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

// ==UserScript==
// @name            AO3 Word Count Script
// @name:ru         Скрипт подсчета слов на AO3
// @version         2
// @description     Adds word counts to chapter links on AO3 Chapter Index pages.
// @description:ru  Добавляет количество слов к ссылкам на главы на страницах индекса глав AO3.
// @author          Anton Dumov
// @license         MIT
// @match           https://archiveofourown.org/*/navigate
// @grant           none
// @namespace http://tampermonkey.net/
// ==/UserScript==

(function() {
    'use strict';

    const wordCountRegex = /\s+/g;
    const cacheKeyPrefix = "ao3-word-count-cache-";
    const cacheDurationMs = 24 * 60 * 60 * 1000; // 24 hours in milliseconds

    const getCachedWordCount = link => {
        const cacheKey = cacheKeyPrefix + link.href;
        const cachedValue = localStorage.getItem(cacheKey);
        if (cachedValue) {
            const { timestamp, wordCount } = JSON.parse(cachedValue);
            if (Date.now() - timestamp < cacheDurationMs) {
                return wordCount;
            } else {
                localStorage.removeItem(cacheKey);
            }
        }
        return null;
    };

    const setCachedWordCount = (link, wordCount) => {
        const cacheKey = cacheKeyPrefix + link.href;
        const cacheValue = JSON.stringify({ timestamp: Date.now(), wordCount });
        localStorage.setItem(cacheKey, cacheValue);
    };

    const chapterLinks = document.querySelectorAll("ol.chapter.index.group li a");

    chapterLinks.forEach(link => {
        const cachedWordCount = getCachedWordCount(link);
        if (cachedWordCount) {
            const spanElement = link.parentElement.querySelector('span.datetime');
            const wordCountElement = document.createElement("span");
            wordCountElement.textContent = ` (${cachedWordCount} words)`;
            spanElement.parentNode.insertBefore(wordCountElement, spanElement.nextSibling);
        } else {
            fetch(link.href).then(response => response.text())
                .then(text => {
                    const parser = new DOMParser();
                    const doc = parser.parseFromString(text, "text/html");
                    const article = doc.querySelector("div[role=article]");
                    const wordCount = article ? article.textContent.trim().split(wordCountRegex).length : 0;
                    setCachedWordCount(link, wordCount);

                    const spanElement = link.parentElement.querySelector('span.datetime');
                    const wordCountElement = document.createElement("span");
                    wordCountElement.textContent = ` (${wordCount} words)`;
                    spanElement.parentNode.insertBefore(wordCountElement, spanElement.nextSibling);
                })
                .catch(error => console.log(error));
        }
    });

})();