Hide Web Elements Pro

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

Você precisará instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Você precisará instalar uma extensão como o Tampermonkey para instalar este script.

Você precisará instalar um gerenciador de scripts de usuário para instalar este script.

(Eu já tenho um gerenciador de scripts de usuário, me deixe instalá-lo!)

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

(Eu já possuo um gerenciador de estilos de usuário, me deixar fazer a instalação!)

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

})();