Bitcointalk Post & Reply Word/Char Counter

Adds a live word, character, and estimated reading time counter to Bitcointalk reply boxes and posts.

// ==UserScript==
// @name         Bitcointalk Post & Reply Word/Char Counter
// @namespace    Royal Cap
// @version      1.1.0
// @description  Adds a live word, character, and estimated reading time counter to Bitcointalk reply boxes and posts.
// @match        https://bitcointalk.org/index.php?*
// @run-at       document-end
// @grant        none
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    // Counter for textarea (your reply)
    function createCounterBox(textarea) {
        if (textarea.dataset.counterAdded) return;
        textarea.dataset.counterAdded = "1";

        const counter = document.createElement('div');
        counter.style.fontSize = '12px';
        counter.style.marginTop = '4px';
        counter.style.color = '#333';
        counter.textContent = 'Words: 0 | Characters: 0 | Reading time: 0 min';

        textarea.parentNode.insertBefore(counter, textarea.nextSibling);

        function updateCounter() {
            const text = textarea.value.trim();
            const words = text.length ? text.split(/\s+/).length : 0;
            const chars = text.length;
            const readingTime = words ? Math.ceil(words / 200) : 0; // Avg 200 wpm
            counter.textContent = `Words: ${words} | Characters: ${chars} | Reading time: ${readingTime} min`;
        }

        textarea.addEventListener('input', updateCounter);
        updateCounter();
    }

    // Counter for forum posts (other people's posts)
    function createPostCounters() {
        document.querySelectorAll("td.post, div.post").forEach(post => {
            if (post.dataset.counterAdded) return;
            post.dataset.counterAdded = "1";

            const text = post.innerText.trim();
            const words = text.length ? text.split(/\s+/).length : 0;
            const chars = text.length;
            const readingTime = words ? Math.ceil(words / 200) : 0;

            const counter = document.createElement('div');
            counter.style.fontSize = '11px';
            counter.style.marginTop = '6px';
            counter.style.color = 'gray';
            counter.style.textAlign = 'right';
            counter.textContent = `Words: ${words} | Characters: ${chars} | Reading time: ${readingTime} min`;

            post.appendChild(counter);
        });
    }

    function init() {
        // For reply textarea
        document.querySelectorAll("textarea[name='message']").forEach(createCounterBox);

        // For existing posts
        createPostCounters();

        // Watch for dynamic changes
        const observer = new MutationObserver(() => {
            document.querySelectorAll("textarea[name='message']").forEach(createCounterBox);
            createPostCounters();
        });
        observer.observe(document.body, { childList: true, subtree: true });
    }

    init();
})();