Word Blocker with Menu & Smart Images

Block specific words and their associated images on webpages

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Word Blocker with Menu & Smart Images
// @namespace    http://tampermonkey.net/
// @version      0.5
// @description  Block specific words and their associated images on webpages
// @match        *://*/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @author       Edu Altamirano
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // --- 1. CONFIGURATION & MENU ---

    // Default list (used if no custom list is saved)
    const defaultWords = [
        'boluarte', 'milei', 'salinas', 'xochitl', 'netanyahu', 'israel',
        'ecuador', 'noboa', 'piqué', 'sunak', 'chupaprecios', 'CHUPAPRECIOS',
        'flow', 'FLOW', 'trump'
    ];

    // Load words from storage
    var blockedWords = GM_getValue('userBlockedWords', defaultWords);

    // Function to open the input box
    function editBlockedWords() {
        var currentList = blockedWords.join(', ');
        var input = prompt("Edit blocked words (comma separated):", currentList);

        if (input !== null) {
            var newList = input.split(',').map(s => s.trim()).filter(s => s !== "");
            GM_setValue('userBlockedWords', newList);
            alert("List saved! Reloading page...");
            location.reload();
        }
    }

    // Add button to Tampermonkey menu
    GM_registerMenuCommand("🚫 Edit Blocked Words", editBlockedWords);


    // --- 2. FILTERING LOGIC ---

    function containsBlockedWord(element) {
        if (!element || !element.textContent) return false;
        var text = element.textContent.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase();

        for (var i = 0; i < blockedWords.length; i++) {
            var word = blockedWords[i].normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase();
            if (word && text.includes(word)) return true;
        }
        return false;
    }

    // Helper to animate and hide an element
    function blurAndHide(el) {
        if (!el || el.dataset.blocked === "true") return; // Prevent double hiding
        el.dataset.blocked = "true"; // Mark as processed

        setTimeout(function() {
            el.style.transition = 'all 0.5s';
            el.style.filter = 'blur(10px)';
            el.style.opacity = '0.5';
            setTimeout(function() {
                el.style.display = 'none';
            }, 500);
        }, 500);
    }

    function hideBlockedElements() {
        // Target common text containers
        var elements = document.querySelectorAll('p, h1, h2, h3, h4, h5, h6, a, span, li, article');

        elements.forEach(function(element) {
            if (containsBlockedWord(element)) {

                // 1. Redact the specific text
                var regex = new RegExp(blockedWords.join("|"), "gi");
                // Only replace if it's a leaf node to avoid breaking HTML structure
                if(element.children.length === 0) {
                     element.innerHTML = element.innerHTML.replace(regex, match => 
                        '<span style="color: red; text-decoration: line-through;">' + match + '</span>'
                     );
                }

                // 2. Hide the element itself
                blurAndHide(element);

                // 3. Smart Parent Hiding (Hide the card/wrapper)
                var parent = element.parentElement;
                // Go up a few levels to find the main container (like a generic div or article)
                var attempts = 0;
                while (parent && attempts < 3) {
                    if (['div', 'article', 'li', 'figure'].includes(parent.tagName.toLowerCase())) {
                        blurAndHide(parent);

                        // 4. NEW: Check Siblings of the Parent for Images
                        // This catches: <div class="img-block">...</div> <div class="text-block">BLOCKED</div>
                        var sibling = parent.previousElementSibling;
                        while (sibling) {
                            // Hide sibling if it IS an image or CONTAINS an image
                            if (sibling.tagName.toLowerCase() === 'img' || sibling.querySelector('img')) {
                                blurAndHide(sibling);
                            }
                            sibling = sibling.previousElementSibling;
                        }
                        break; // Stop after hiding the closest container
                    }
                    parent = parent.parentElement;
                    attempts++;
                }
            }
        });

        // 5. Cleanup standalone images with bad alt text
        document.querySelectorAll('img').forEach(function(img) {
            var altText = (img.alt || "").toLowerCase();
            if (blockedWords.some(word => altText.includes(word))) {
                blurAndHide(img);
            }
        });
    }

    // --- 3. EXECUTION ---

    // Run on load
    window.addEventListener('load', hideBlockedElements, false);

    // Run periodically to catch Lazy Loaded images and Infinite Scroll
    setInterval(hideBlockedElements, 2000);

})();