🏠 Home 

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.


安装此脚本?
// ==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
}