Trump Text Styler

Makes instances of "trump" more prominent on webpages

// ==UserScript==
// @name         Trump Text Styler
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  Makes instances of "trump" more prominent on webpages
// @author       Ethan
// @match        *://*/*
// @grant        none
// @run-at       document-idle
// @license     MIT
// ==/UserScript==

(function() {
    'use strict';

    // Flag to prevent processing our own mutations
    let isProcessing = false;
    
    // Custom attribute to mark elements we've already processed
    const PROCESSED_ATTR = 'data-trump-processed';

    function isEditable(node) {
        const editableTags = ['INPUT', 'TEXTAREA'];
        
        // Check if node itself is editable
        if (editableTags.includes(node?.tagName) || node?.isContentEditable) {
            return true;
        }
        
        // Check if node is inside an editable element
        let parent = node?.parentNode;
        while (parent) {
            if (editableTags.includes(parent.tagName) || parent.isContentEditable) {
                return true;
            }
            parent = parent.parentNode;
        }
        
        return false;
    }

    function styleTrump(node) {
        // Skip if not a valid node
        if (!node) return;
        
        // Skip if already processed
        if (node.nodeType === Node.ELEMENT_NODE && 
            node.getAttribute && 
            node.getAttribute(PROCESSED_ATTR) === 'true') {
            return;
        }
        
        // First check if node is inside an editable element
        if (isEditable(node)) {
            return;
        }
        
        if (node.nodeType === Node.TEXT_NODE) {
            const text = node.textContent;
            const regex = /\b(trump)\b/gi;
            
            if (regex.test(text)) {
                // Temporarily disable observer
                isProcessing = true;
                
                const span = document.createElement('span');
                span.innerHTML = text.replace(regex, '<b style="font-size: 1.2em;">$1</b>');
                span.setAttribute(PROCESSED_ATTR, 'true');
                
                if (node.parentNode) {
                    node.parentNode.replaceChild(span, node);
                }
                
                // Re-enable observer after a small delay
                setTimeout(() => {
                    isProcessing = false;
                }, 0);
            }
        } else if (node.nodeType === Node.ELEMENT_NODE) {
            // Mark this element as processed
            if (node.setAttribute) {
                node.setAttribute(PROCESSED_ATTR, 'true');
            }
            
            // Process children
            if (node.childNodes && node.childNodes.length > 0) {
                // Safe copy of childNodes to avoid modification during iteration
                Array.from(node.childNodes).forEach(child => styleTrump(child));
            }
        }
    }

    // Wait for the DOM to be fully loaded
    window.addEventListener('DOMContentLoaded', () => {
        // Process initial page after a small delay
        setTimeout(() => {
            styleTrump(document.body);
        }, 500);
    });
    
    // For pages where DOMContentLoaded might have already fired
    if (document.readyState === 'complete' || document.readyState === 'interactive') {
        setTimeout(() => {
            styleTrump(document.body);
        }, 500);
    }

    const observer = new MutationObserver(mutations => {
        // Skip processing if we're already handling mutations
        if (isProcessing) return;
        
        mutations.forEach(mutation => {
            if (mutation.addedNodes && mutation.addedNodes.length > 0) {
                mutation.addedNodes.forEach(node => {
                    // Only process if it's a DOM node and not already processed
                    if (node.nodeType && 
                        !(node.nodeType === Node.ELEMENT_NODE && 
                          node.getAttribute && 
                          node.getAttribute(PROCESSED_ATTR) === 'true') &&
                        !isEditable(node)) {
                        styleTrump(node);
                    }
                });
            }
        });
    });

    // Start observing with a delay to let the page load first
    setTimeout(() => {
        observer.observe(document.body, { 
            childList: true, 
            subtree: true 
        });
    }, 1000);
})();