您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds the word count of a fiction (taken from the information tooltip in the Pages statistic) as it's own statistic
// ==UserScript== // @name Royal Road: Add Word Count to Statistics Section // @namespace https://github.com/w4tchdoge // @version 1.1.0-20250611_121836 // @description Adds the word count of a fiction (taken from the information tooltip in the Pages statistic) as it's own statistic // @author w4tchdoge // @homepage https://github.com/w4tchdoge/MISC-UserScripts // @match *://*.royalroad.com/fiction/* // @exclude *://*.royalroad.com/fiction/*/*/chapter/* // @license AGPL-3.0-or-later // @run-at document-start // @history 1.1.0 — Add word count next to chapter count in the ToC header bar thing // @history 1.0.0 — Initial release // ==/UserScript== (async function () { `use strict`; // modified from https://stackoverflow.com/a/61511955/11750206 function waitForElm(selector, search_root = document) { return new Promise(resolve => { if (search_root.querySelector(selector)) { return resolve(search_root.querySelector(selector)); } const observer = new MutationObserver(mutations => { if (search_root.querySelector(selector)) { observer.disconnect(); resolve(search_root.querySelector(selector)); } }); // If you get "parameter 1 is not of type 'Node'" error, see https://stackoverflow.com/a/77855838/492336 observer.observe(document.documentElement, { childList: true, subtree: true }); }); } // wait for fic info section to load in const ficinfo = await waitForElm(`div.fiction-info`); // Get the element which contains the word "Pages" and which also contains the word count in an informational tooltip const statsPage_pages_elem = ficinfo.querySelector(`.fiction-stats .stats-content .list-unstyled li:has(> i)`); // Get Word Count const word_count_str = statsPage_pages_elem.querySelector(`i`).getAttribute(`data-content`).replace(/.*calculated from (.*) words./gmi, `$1`); console.log(` RR WORD COUNT SCRIPT WORD COUNT: ${word_count_str}`); // Make the "Heading" element that describes the data below it. i.e. it says "Words" const elm_wordcount_title = (() => { let outelm = Object.assign(document.createElement(`li`), { id: `userscript-words-title`, innerHTML: `Words :` }); // Add the bold and uppercase classes to make it look like the other stats page entries outelm.classList.add(`bold`, `uppercase`); return outelm; })(); // Make the "Data" element that has the actual data. i.e. it says the word count const elm_wordcount_data = (() => { let outelm = Object.assign(document.createElement(`li`), { id: `userscript-words-data`, innerHTML: `${word_count_str}` }); // Add the bold and uppercase classes to make it look like the other stats page entries outelm.classList.add(`bold`, `uppercase`, `font-red-sunglo`); return outelm; })(); // Add the word count elements before the page count elements statsPage_pages_elem.before(elm_wordcount_title, elm_wordcount_data); const tocbar_ch_cnt = (() => { const tocbar = ficinfo.querySelector(`div.portlet > .portlet-title:has(> .caption > a#toc)`); const xpath = `.//*[contains(concat(" ",normalize-space(@class)," ")," actions ")]/span[contains(normalize-space(),"Chapter")]`; const chcntpar = document.evaluate(xpath, tocbar, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; return chcntpar; })(); const tocbar_wc = (() => { let base_elm = tocbar_ch_cnt.cloneNode(true); base_elm.textContent = `${word_count_str} Words`; base_elm.style.marginRight = `0.25em`; return base_elm; })(); tocbar_ch_cnt.after(tocbar_wc); })();