Hide Web Elements Pro

Double-Tap Hide with Animated Preview, Undetectable Stealth Engine, Real-time Element Restoration

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

You will need to install an extension such as Tampermonkey to install this script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==UserScript==
// @name         Hide Web Elements Pro 
// @version      2.40
// @description  Double-Tap Hide with Animated Preview, Undetectable Stealth Engine, Real-time Element Restoration
// @author       KTZ
// @match        *://*/*
// @grant        GM_setValue
// @grant        GM_getValue
// @run-at       document-end
// @license      MIT
// @namespace    https://greasyfork.org/users/1620673
// ==/UserScript==

(function() {
    'use strict';

    const UI_HOST_ID = 'hider-ui-root';
    const isMobile = /Android|iPhone|iPad/i.test(navigator.userAgent);
    const btnSize = isMobile ? '50px' : '44px';

    let isSelecting = false;
    let currentScope = 'site'; // 'site' or 'link'
    let previewElement = null; // Tracks the currently highlighted element

    // --- 1. Cosmetic Filtering Engine ---
    function getExactSelector(el) {
        if (!el || el.nodeType !== Node.ELEMENT_NODE) return '';

        const path = [];
        let current = el;

        while (current && current.nodeType === Node.ELEMENT_NODE && current.tagName.toLowerCase() !== 'body' && current.tagName.toLowerCase() !== 'html') {
            let selector = current.tagName.toLowerCase();

            if (current.id && !/^\d+$/.test(current.id) && !/.{15,}/.test(current.id)) {
                selector = `#${CSS.escape(current.id)}`;
                path.unshift(selector);
                break;
            }

            let hasGoodClass = false;
            if (current.className && typeof current.className === 'string') {
                const classes = current.className.trim().split(/\s+/).filter(c => {
                    return c.length > 2 && !/^[a-zA-Z0-9]{10,}$/.test(c) && !/^hover:/i.test(c);
                });

                if (classes.length > 0) {
                    selector += `.${CSS.escape(classes[0])}`;
                    if (classes[1]) selector += `.${CSS.escape(classes[1])}`;
                    hasGoodClass = true;
                }
            }

            if (!hasGoodClass) {
                let index = 1, sibling = current.previousElementSibling;
                while (sibling) { index++; sibling = sibling.previousElementSibling; }
                selector += `:nth-child(${index})`;
            }

            path.unshift(selector);
            if (hasGoodClass && path.length >= 2) break;

            current = current.parentElement;
        }

        if (path.length > 0 && !path[0].startsWith('#') && !path[0].includes('.')) {
             path.unshift('body');
        }
        return path.join(' > ');
    }

    // --- 2. UI Injection ---
    function injectUI() {
        if (document.getElementById(UI_HOST_ID)) return;
        if (!document.body) return;

        const host = document.createElement('div');
        host.id = UI_HOST_ID;
        host.style.cssText = 'position:fixed; top:0; right:0; bottom:0; width:0; z-index:2147483647; pointer-events:none;';
        document.body.appendChild(host);

        const s = document.createElement('style');
        s.textContent = `
            @keyframes hider-preview-pulse {
                0% { outline: 3px dotted #ef4444; outline-offset: -3px; box-shadow: inset 0 0 0 rgba(239, 68, 68, 0.1); }
                50% { outline: 3px dotted #ef4444; outline-offset: 2px; box-shadow: inset 0 0 15px rgba(239, 68, 68, 0.4); }
                100% { outline: 3px dotted #ef4444; outline-offset: -3px; box-shadow: inset 0 0 0 rgba(239, 68, 68, 0.1); }
            }
            .hider-preview-highlight {
                animation: hider-preview-pulse 1s infinite !important;
                cursor: pointer !important;
                border-radius: 2px !important;
            }

            .hider-btn {
                position: fixed; right: 0; width: ${btnSize}; height: ${btnSize};
                background: rgba(255, 255, 255, 0.2); backdrop-filter: blur(15px);
                border: 1px solid rgba(255,255,255,0.3); border-radius: 12px 0 0 12px;
                color: white; display: flex; align-items: center; justify-content: center;
                cursor: pointer; font-size: 10px; font-weight: 700; text-transform: uppercase;
                transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); pointer-events: auto;
                transform: translateX(calc(${btnSize} - 15px));
            }
            @media (hover: hover) {
                .hider-btn:hover { transform: translateX(0); width: 70px; background: rgba(255,255,255,0.4); }
            }
            .hider-btn.active { transform: translateX(0) !important; width: 70px !important; }

            #btn-select.active { background: #3b82f6 !important; }
            #btn-scope.scope-link { background: #10b981 !important; }
            #btn-manage.active { background: rgba(239, 68, 68, 0.8) !important; transform: translateX(0) !important; width: 70px !important; }

            #btn-select { top: 20px; }
            #btn-scope { top: calc(20px + ${btnSize} + 10px); }
            #btn-manage { top: calc(20px + (${btnSize} * 2) + 20px); background: rgba(239, 68, 68, 0.4); }

            #hider-panel {
                position: fixed; right: 10px; top: 100px; width: 300px; max-height: 70vh;
                background: rgba(0,0,0,0.85); backdrop-filter: blur(20px); border-radius: 16px;
                padding: 15px; color: white; display: none; flex-direction: column; overflow-y: auto; pointer-events: auto;
            }
            .panel-header { font-weight:bold; margin-bottom:12px; border-bottom: 2px solid #555; padding-bottom: 8px; display: flex; justify-content: space-between; align-items: center; }
            .section-title { font-size: 11px; color: #aaa; text-transform: uppercase; margin: 10px 0 5px 0; letter-spacing: 1px; }
            .list-item { font-size: 11px; margin-bottom: 6px; border-bottom: 1px solid #333; padding-bottom: 4px; display: flex; justify-content: space-between; align-items: center; }
            .list-item span { word-break: break-all; padding-right: 8px; }
            .hider-btn-small { cursor: pointer; border: none; border-radius: 4px; padding: 4px 8px; color: white; font-weight: bold; background: #ff4444; }
            .hider-btn-small:hover { background: #cc0000; }
            #close-p { background: #555; padding: 6px 10px; } #close-p:hover { background: #777; }
        `;
        (document.head || document.documentElement).appendChild(s);

        host.innerHTML = `
            <div class="hider-btn" id="btn-select">Hide</div>
            <div class="hider-btn" id="btn-scope">SITE</div>
            <div class="hider-btn" id="btn-manage">List</div>

            <div id="hider-panel">
                <div class="panel-header">
                    <span>Manage Hidden Items</span>
                    <button class="hider-btn-small" id="close-p">X</button>
                </div>
                <div class="section-title">Hidden Everywhere (Site)</div>
                <div id="list-site"></div>

                <div class="section-title" style="margin-top: 15px;">Hidden Here Only (Link)</div>
                <div id="list-link"></div>
            </div>
        `;

        document.getElementById('btn-select').onclick = (e) => {
            isSelecting = !isSelecting;
            const btnScope = document.getElementById('btn-scope');
            const btnManage = document.getElementById('btn-manage');
            const panel = document.getElementById('hider-panel');

            if (isSelecting) {
                e.target.classList.add('active');
                btnScope.classList.add('active');
                panel.style.display = 'none';
                btnManage.classList.remove('active');
            } else {
                e.target.classList.remove('active');
                btnScope.classList.remove('active');

                if (previewElement) {
                    previewElement.classList.remove('hider-preview-highlight');
                    previewElement = null;
                }
            }
        };

        const btnScope = document.getElementById('btn-scope');
        btnScope.onclick = () => {
            if (!isSelecting) return;
            currentScope = currentScope === 'site' ? 'link' : 'site';
            btnScope.textContent = currentScope === 'site' ? 'SITE' : 'LINK';
            btnScope.classList.toggle('scope-link', currentScope === 'link');
        };

        document.getElementById('btn-manage').onclick = (e) => {
            const panel = document.getElementById('hider-panel');
            const btnSelect = document.getElementById('btn-select');
            const btnScope = document.getElementById('btn-scope');

            if (panel.style.display === 'flex') {
                panel.style.display = 'none';
                e.target.classList.remove('active');
            } else {
                panel.style.display = 'flex';
                e.target.classList.add('active');

                isSelecting = false;
                btnSelect.classList.remove('active');
                btnScope.classList.remove('active');
                if (previewElement) {
                    previewElement.classList.remove('hider-preview-highlight');
                    previewElement = null;
                }

                renderList();
            }
        };

        document.getElementById('close-p').onclick = () => {
            document.getElementById('hider-panel').style.display = 'none';
            document.getElementById('btn-manage').classList.remove('active');
        };
    }

    // --- 3. Style & Storage (Undetectable Stealth Engine) ---
    function updateStyles() {
        let styleEl = document.getElementById('hider-dynamic-styles');
        if (!styleEl) {
            styleEl = document.createElement('style');
            styleEl.id = 'hider-dynamic-styles';
            (document.head || document.documentElement).appendChild(styleEl);
        }

        const siteData = GM_getValue('hider_site_' + window.location.hostname, []);
        const linkData = GM_getValue('hider_link_' + window.location.href, []);
        const combined = [...new Set([...siteData, ...linkData])];

        // Undetectable stealth hiding rules bypass checks for getComputedStyle, offsetParent, and dimensions
        const hideRules = combined
            .filter(sel => sel && sel.trim() !== '')
            .map(sel => `${sel} {
                position: absolute !important;
                top: -99999px !important;
                left: -99999px !important;
                width: 1px !important;
                height: 1px !important;
                opacity: 0.0001 !important;
                pointer-events: none !important;
                clip: rect(1px, 1px, 1px, 1px) !important;
                clip-path: inset(50%) !important;
                overflow: hidden !important;
                max-width: 0px !important;
                max-height: 0px !important;
                margin: 0 !important;
                padding: 0 !important;
            }`)
            .join('\n');

        styleEl.textContent = hideRules;
    }

    function restoreSelector(selector) {
        if (!selector) return;
        try {
            const els = document.querySelectorAll(selector);
            els.forEach(el => {
                el.style.removeProperty('display');
                el.style.removeProperty('position');
                el.style.removeProperty('top');
                el.style.removeProperty('left');
                el.style.removeProperty('width');
                el.style.removeProperty('height');
                el.style.removeProperty('opacity');
                el.style.removeProperty('pointer-events');
            });
        } catch (err) {
            // Ignore invalid selector queries safely
        }
    }

    function renderList() {
        const siteKey = 'hider_site_' + window.location.hostname;
        const linkKey = 'hider_link_' + window.location.href;
        const siteData = GM_getValue(siteKey, []);
        const linkData = GM_getValue(linkKey, []);

        const cSite = document.getElementById('list-site');
        const cLink = document.getElementById('list-link');
        if(!cSite || !cLink) return;

        cSite.innerHTML = ''; cLink.innerHTML = '';

        if (siteData.length === 0) cSite.innerHTML = `<div style="font-size:10px; color:#777;">None</div>`;
        siteData.forEach((sel, i) => {
            const div = document.createElement('div'); div.className = 'list-item';
            div.innerHTML = `<span>${sel.substring(0, 25)}...</span> <button class="hider-btn-small" data-i="${i}">X</button>`;
            div.querySelector('button').onclick = (e) => {
                const removed = siteData.splice(e.target.dataset.i, 1)[0];
                GM_setValue(siteKey, siteData);
                restoreSelector(removed);
                updateStyles();
                renderList();
            };
            cSite.appendChild(div);
        });

        if (linkData.length === 0) cLink.innerHTML = `<div style="font-size:10px; color:#777;">None</div>`;
        linkData.forEach((sel, i) => {
            const div = document.createElement('div'); div.className = 'list-item';
            div.innerHTML = `<span>${sel.substring(0, 25)}...</span> <button class="hider-btn-small" data-i="${i}">X</button>`;
            div.querySelector('button').onclick = (e) => {
                const removed = linkData.splice(e.target.dataset.i, 1)[0];
                GM_setValue(linkKey, linkData);
                restoreSelector(removed);
                updateStyles();
                renderList();
            };
            cLink.appendChild(div);
        });
    }

    // --- 4. Global Capture Click (Double-Tap Preview) ---
    document.addEventListener('click', (e) => {
        if (!isSelecting) return;
        if (e.target.closest('#' + UI_HOST_ID)) return;

        e.preventDefault();
        e.stopPropagation();

        if (previewElement === e.target) {
            // TAP 2: Commit and hide stealthily
            previewElement.classList.remove('hider-preview-highlight');

            const selector = getExactSelector(e.target);
            if (!selector) {
                previewElement = null;
                return;
            }

            if (currentScope === 'site') {
                const key = 'hider_site_' + window.location.hostname;
                const s = GM_getValue(key, []);
                if (!s.includes(selector)) { s.push(selector); GM_setValue(key, s); }
            } else {
                const key = 'hider_link_' + window.location.href;
                const s = GM_getValue(key, []);
                if (!s.includes(selector)) { s.push(selector); GM_setValue(key, s); }
            }

            updateStyles(); // Applies stealth hiding rules in real-time
            previewElement = null;
        } else {
            // TAP 1: Highlight
            if (previewElement) {
                previewElement.classList.remove('hider-preview-highlight');
            }
            previewElement = e.target;
            previewElement.classList.add('hider-preview-highlight');
        }

    }, true);

    // --- 5. Permanent Long-Press & Right-Click Enabler ---
    function enableContextMenus() {
        const blockEvents = ['contextmenu', 'selectstart', 'copy', 'paste', 'dragstart'];
        blockEvents.forEach(evt => {
            document.addEventListener(evt, (e) => {
                e.stopPropagation();
            }, true);
        });

        const cssUnlock = document.createElement('style');
        cssUnlock.textContent = `* { -webkit-touch-callout: default !important; }`;
        (document.head || document.documentElement).appendChild(cssUnlock);
    }
    enableContextMenus();

    // --- 6. MutationObserver Engine ---
    let lastUrl = window.location.href;

    const observer = new MutationObserver(() => {
        if (!document.getElementById(UI_HOST_ID)) injectUI();
        if (!document.getElementById('hider-dynamic-styles')) updateStyles();

        if (window.location.href !== lastUrl) {
            lastUrl = window.location.href;
            updateStyles();
            const panel = document.getElementById('hider-panel');
            if (panel && panel.style.display === 'flex') renderList();
        }
    });

    observer.observe(document.documentElement, { childList: true, subtree: true });

    injectUI();
    updateStyles();

})();