SpoilerMan

Want to blurr spoilers? Spoiler man to the rescue!

2025-06-20 يوللانغان نەشرى. ئەڭ يېڭى نەشرىنى كۆرۈش.

// ==UserScript==
// @name         SpoilerMan
// @namespace    codesidian.com
// @description  Want to blurr spoilers? Spoiler man to the rescue!
// @version      1.1
// @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 STORAGE_KEY = 'spoilerManBlurredClasses';
    let lastHoveredElement = null;
    const highlightStyle = '2px dashed red';


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

    async function addBlurredClass(className) {
        if (!className || className.trim() === '') return;
        const classes = await getBlurredClasses();
        if (!classes.includes(className)) {
            classes.push(className);
            await GM.setValue(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(STORAGE_KEY, []);
           window.location.reload();
        }
    }


    function blurElementsByClass(className) {
        const elementsToBlur = document.querySelectorAll('.' + className);
        if (elementsToBlur.length > 0) {
            elementsToBlur.forEach(element => {
                element.style.filter = 'blur(8px)';
                element.style.transition = 'filter 1s ease';
                element.addEventListener('mouseenter', () => element.style.filter = 'blur(0px)');
                element.addEventListener('mouseleave', () => element.style.filter = 'blur(8px)');
            });
        }
    }


    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());

            //const classString = Array.from(usefulTarget.classList).join(' ');
            //const chosenClass = prompt(`This element (or its parent) has these classes:\n\n${classString}\n\nEnter the one you want to blur:`, usefulTarget.classList[0]);
            //if (chosenClass) {
            //    addBlurredClass(chosenClass.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.");
        document.body.style.cursor = 'crosshair';
        document.addEventListener('mouseover', hoverListener);
        document.addEventListener('click', clickListener, true);
    }

    function registerMenuCommands() {
        GM.registerMenuCommand('🎯 Select an element to blur', activateSelectionMode);
        GM.registerMenuCommand('❌ Clear all blur rules', clearBlurredClasses);
    }

    async function main() {
        registerMenuCommands();

        const classesToBlur = await getBlurredClasses();
        if (classesToBlur.length > 0) {
            classesToBlur.forEach(className => {
                blurElementsByClass(className);
            });
        }
    }

    main();

})();