您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds word counts to chapter links on AO3 Chapter Index pages and in Stats on each chapter page.
当前为
- // ==UserScript==
- // @name AO3 Word Count Script
- // @namespace ao3chapterwordcounter
- // @version 4.0
- // @description Adds word counts to chapter links on AO3 Chapter Index pages and in Stats on each chapter page.
- // @author Anton Dumov
- // @license MIT
- // @match https://archiveofourown.org/*/navigate
- // @match https://archiveofourown.org/*/chapters/*
- // @grant none
- // ==/UserScript==
- (function() {
- 'use strict';
- const uri = location.protocol+'//'+
- location.hostname+
- (location.port?":"+location.port:"")+
- location.pathname+
- (location.search?location.search:"");
- const wordCountRegex = /\s+/g;
- const chapterUrlRegex = new RegExp("https://archiveofourown\\.org/works/\\d+/chapters/\\d+/?");
- const cacheKeyPrefix = "ao3-word-count-cache-";
- const cacheDurationMs = 30 * 24 * 60 * 60 * 1000;
- 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 && wordCount !== 0) {
- return wordCount;
- } else {
- localStorage.removeItem(cacheKey);
- }
- }
- return null;
- };
- const setCachedWordCount = (url, wordCount) => {
- const cacheKey = cacheKeyPrefix + url;
- const cacheValue = JSON.stringify({ timestamp: Date.now(), wordCount });
- localStorage.setItem(cacheKey, cacheValue);
- };
- let fetchInProgress = false;
- const countWords = (doc) => {
- const article = doc.querySelector("div[role=article]");
- return article ? article.textContent.trim().split(wordCountRegex).length : 0;
- };
- const fetchWordCount = async (url) => {
- try {
- if (fetchInProgress) {
- // Wait for the previous request to complete
- await new Promise(resolve => {
- const interval = setInterval(() => {
- if (!fetchInProgress) {
- clearInterval(interval);
- resolve();
- }
- }, 2000);
- });
- }
- fetchInProgress = true;
- const response = await fetch(url);
- const text = await response.text();
- const parser = new DOMParser();
- const doc = parser.parseFromString(text, "text/html");
- const wordCount = countWords(doc);
- setCachedWordCount(url, wordCount);
- fetchInProgress = false;
- return wordCount;
- } catch (error) {
- console.log(error);
- fetchInProgress = false;
- }
- };
- const getWordCount = async (link, maxWidth) => {
- const cachedWordCount = getCachedWordCount(link);
- let wordCount;
- if (cachedWordCount) {
- wordCount = cachedWordCount;
- } else {
- wordCount = await fetchWordCount(link.href);
- }
- const spanElement = link.parentElement.querySelector('span.datetime');
- const wordCountElement = document.createElement("span");
- wordCountElement.textContent = `(${wordCount} words)`;
- const margin = maxWidth - link.getBoundingClientRect().width + 7;
- wordCountElement.style.marginLeft = `${margin}px`;
- spanElement.parentNode.insertBefore(wordCountElement, spanElement.nextSibling);
- };
- if (uri.endsWith("navigate")){
- const chapterLinks = document.querySelectorAll("ol.chapter.index.group li a");
- const parentWidth = chapterLinks[0].parentElement.getBoundingClientRect().width;
- let maxWidth = 0;
- chapterLinks.forEach(link => {
- const width = link.getBoundingClientRect().width;
- if (width > maxWidth && width <= parentWidth) {
- maxWidth = width;
- }
- });
- chapterLinks.forEach(link => {
- getWordCount(link, maxWidth);
- });
- } else if (chapterUrlRegex.test(uri)) {
- const wordsCount = countWords(document);
- const statsElement = document.querySelector('dl.stats');
- const ddElement = document.createElement('dd');
- ddElement.classList.add('chapter-words');
- ddElement.textContent = wordsCount;
- const dtElement = document.createElement('dt');
- dtElement.classList.add('chapter-words');
- dtElement.textContent = 'Chapter Words:';
- statsElement.appendChild(dtElement);
- statsElement.appendChild(ddElement);
- setCachedWordCount(uri, wordsCount);
- }
- })();