SpoilerMan

Want to blurr spoilers? Spoiler man to the rescue!

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да инсталирате разширение, като например Tampermonkey .

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

Advertisement:

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

Advertisement:

// ==UserScript==
// @name         SpoilerMan
// @namespace    codesidian.com
// @description  Want to blurr spoilers? Spoiler man to the rescue!
// @version      1.2
// @match        *://*/*
// @grant        GM.setValue
// @grant        GM.getValue
// @grant        GM.registerMenuCommand
// @author       Joshua Latham (codesidian.com) & Gemini 2.5 Pro
// @license      MIT 
// ==/UserScript==

(function() {
    'use strict';

    const CLASS_STORAGE_KEY = 'spoilerManBlurredClasses';
    const MODE_STORAGE_KEY = 'spoilerManMode'; // Stores 'blur' or 'hide'
    const BLUR_STRENGTH_KEY = 'spoilerManBlurStrength'; // Stores the blur strength
    let lastHoveredElement = null;
    const highlightStyle = '2px dashed red';

    function getBlurredClasses() {
        return GM.getValue(CLASS_STORAGE_KEY, []);
    }

    function getDisplayMode() {
        return GM.getValue(MODE_STORAGE_KEY, 'blur');
    }

    function getBlurStrength() {
        return GM.getValue(BLUR_STRENGTH_KEY, 40);
    }

    async function addBlurredClass(className) {
        if (!className || className.trim() === '') return;
        const classes = await getBlurredClasses();
        if (!classes.includes(className)) {
            classes.push(className);
            await GM.setValue(CLASS_STORAGE_KEY, classes);
            window.location.reload();
        } else {
            alert(`'${className}' is already in the blur list.`);
        }
    }

    async function clearBlurredClasses() {
        if (confirm("Are you sure you want to clear all spoiler rules?")) {
            await GM.setValue(CLASS_STORAGE_KEY, []);
            window.location.reload();
        }
    }

    async function toggleDisplayMode() {
        const currentMode = await getDisplayMode();
        const newMode = currentMode === 'blur' ? 'hide' : 'blur';
        await GM.setValue(MODE_STORAGE_KEY, newMode);
        window.location.reload();
    }

    async function setBlurStrength() {
        const currentStrength = await getBlurStrength();
        const newStrengthStr = prompt("Enter new blur strength in pixels (e.g., 20):", currentStrength);
        if (newStrengthStr) {
            const newStrength = parseInt(newStrengthStr, 10);
            if (!isNaN(newStrength) && newStrength >= 0) {
                await GM.setValue(BLUR_STRENGTH_KEY, newStrength);
                window.location.reload();
            } else {
                alert("Invalid input. Please enter a valid non-negative number.");
            }
        }
    }


    function applySpoilerEffect(className, mode, blurStrength) {
        const elementsToModify = document.querySelectorAll('.' + className);
        elementsToModify.forEach(element => {
            if (mode === 'blur') {
                const blurValue = `${blurStrength}px`;
                element.style.display = ''; // Ensure element is visible
                element.style.filter = `blur(${blurValue})`;
                element.style.transition = 'filter 1s ease';
                element.addEventListener('mouseenter', () => element.style.filter = 'blur(0px)');
                element.addEventListener('mouseleave', () => element.style.filter = `blur(${blurValue})`);
            } else {
                element.style.display = 'none';
            }
        });
    }


    function findTargetWithClasses(element) {
        let current = element;
        while (current) {
            if (current.classList && current.classList.length > 0) {
                return current;
            }
            current = current.parentElement;
        }
        return null;
    }

    const clickListener = (event) => {
        event.preventDefault();
        event.stopPropagation();
        const usefulTarget = findTargetWithClasses(event.target);
        deactivateSelectionMode();

        if (usefulTarget) {
            addBlurredClass(usefulTarget.classList[0].trim());
        } else {
            alert("Could not find any usable classes for the clicked element or its parents.");
        }
    };

    const hoverListener = (event) => {
        const target = event.target;
        if (lastHoveredElement) {
            lastHoveredElement.style.outline = '';
        }
        target.style.outline = highlightStyle;
        lastHoveredElement = target;
    };

    function deactivateSelectionMode() {
        if (lastHoveredElement) {
            lastHoveredElement.style.outline = '';
        }
        document.body.style.cursor = 'default';
        document.removeEventListener('mouseover', hoverListener);
        document.removeEventListener('click', clickListener, true);
    }

    function activateSelectionMode() {
        alert("Selection mode activated. Hover over elements to highlight them, then click the one you want to blur/hide.");
        document.body.style.cursor = 'crosshair';
        document.addEventListener('mouseover', hoverListener);
        document.addEventListener('click', clickListener, true);
    }

    async function registerMenuCommands() {
        GM.registerMenuCommand('🎯 Select an element to spoil', activateSelectionMode);
        GM.registerMenuCommand('❌ Clear all spoiler rules', clearBlurredClasses);
        GM.registerMenuCommand('⚙️ Set Blur Strength', setBlurStrength);

        const currentMode = await getDisplayMode();
        const nextModeText = currentMode === 'blur' ? 'Hide' : 'Blur';
        GM.registerMenuCommand(`🔄 Switch to ${nextModeText} Mode`, toggleDisplayMode);
    }


    async function main() {
        await registerMenuCommands();

        const classesToAffect = await getBlurredClasses();
        const mode = await getDisplayMode();
        const blurStrength = await getBlurStrength();

        console.log(`SpoilerMan active in '${mode}' mode with blur strength ${blurStrength}px.`);

        if (classesToAffect.length > 0) {
            classesToAffect.forEach(className => {
                applySpoilerEffect(className, mode, blurStrength);
            });
        }
    }

    main();

})();