Hide Web Elements Pro

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

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey, το Greasemonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

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

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Userscripts για να εγκαταστήσετε αυτόν τον κώδικα.

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

Θα χρειαστεί να εγκαταστήσετε μια επέκταση διαχείρισης κώδικα χρήστη για να εγκαταστήσετε αυτόν τον κώδικα.

(Έχω ήδη έναν διαχειριστή κώδικα χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Έχω ήδη έναν διαχειριστή στυλ χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

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

})();