Stealth element hider, editable hidden rules, domain & global rule editor, soft snow freeze mode, anti-detection engine, redirect blocker, smart auto-closing menus with mobile keyboard safety.
Versione datata
// ==UserScript==
// @name Hide Web Elements Pro
// @version 4.3
// @description Stealth element hider, editable hidden rules, domain & global rule editor, soft snow freeze mode, anti-detection engine, redirect blocker, smart auto-closing menus with mobile keyboard safety.
// @author KTZ
// @match *://*/*
// @grant GM_setValue
// @grant GM_getValue
// @grant unsafeWindow
// @run-at document-start
// @license MIT
// @namespace https://greasyfork.org/users/1620673
// ==/UserScript==
(function() {
'use strict';
const doc = document, win = typeof unsafeWindow !== 'undefined' ? unsafeWindow : window, gv = GM_getValue, sv = GM_setValue;
const UI_HOST_ID = 'hider-ui-root', STEPPER_BAR_ID = 'hider-stepper-bar', isTop = (window.self === window.top);
const by = id => doc.getElementById(id);
let initialMem = gv('hider_freeze_memory', null) || (gv('hider_autono_global', false) ? 'block_all' : 'ask');
const CACHE = {
blockedDomainsList: [],
blockedDomainsSet: new Set(),
customRules: [], // [{ id, selector, target }]
isFrozen: false,
freezeMemory: initialMem,
logs: null
};
let isSelecting = false, isFrozen = CACHE.isFrozen, currentScope = 'site', previewElement = null;
let stepperStack = [], userApprovedNavigation = false, cachedCssString = null, lastUrl = location.href;
let stepperPos = { x: null, y: null }, isDraggingStepper = false, isDraggingDock = false, logSaveTimer = null;
let collapseTimer = null, editingRuleId = null;
const cleanUrl = () => location.origin + location.pathname;
const cleanDomain = url => {
try {
if (!url) return '';
if (url === '*') return '*';
return new URL(url.includes('://') ? url : 'http://' + url).hostname.replace(/^www\./, '');
} catch {
return url.trim().toLowerCase();
}
};
function syncCache() {
CACHE.blockedDomainsList = gv('hider_blocked_domains', []);
CACHE.blockedDomainsSet = new Set(CACHE.blockedDomainsList.map(cleanDomain).filter(Boolean));
CACHE.customRules = gv('hider_custom_rules_v4', []);
CACHE.isFrozen = gv('hider_freeze_global', false);
CACHE.freezeMemory = gv('hider_freeze_memory', CACHE.freezeMemory);
if (gv('hider_last_log_clear_day', '') !== new Date().toDateString()) {
CACHE.logs = []; sv('hider_global_logs', []); sv('hider_last_log_clear_day', new Date().toDateString());
}
}
syncCache();
// Stealth Engine: Anti-Anti-Adblock & Undetectable Element Concealment
try {
const origGetComputedStyle = win.getComputedStyle;
win.getComputedStyle = function(el, pseudo) {
const style = origGetComputedStyle.apply(this, arguments);
if (el && el.nodeType === 1 && el.classList.contains('hider-stealth-target')) {
return new Proxy(style, {
get(target, prop) {
if (prop === 'display') return 'block';
if (prop === 'visibility') return 'visible';
if (prop === 'opacity') return '1';
if (prop === 'pointerEvents') return 'auto';
const val = target[prop];
return typeof val === 'function' ? val.bind(target) : val;
}
});
}
return style;
};
} catch {}
const isSameDomain = u => cleanDomain(u) && cleanDomain(u) === cleanDomain(location.href);
const isDomainBlocked = u => {
if (!u || !CACHE.blockedDomainsSet.size) return false;
const d = cleanDomain(u), lower = u.toLowerCase();
return d && (CACHE.blockedDomainsSet.has(d) || [...CACHE.blockedDomainsSet].some(b => b !== '*' && (d.endsWith('.' + b) || lower.includes(b))));
};
function convertToWildcardSelector(sel) {
return (sel || '').replace(/(#|\.)([a-zA-Z0-9_-]+)/g, (m, p, name) => {
if (name.startsWith('hider-')) return '';
let base = (name.match(/^([a-zA-Z_-]+?)[0-9a-fA-F_-]{3,}$/)?.[1] || name.replace(/[0-9a-fA-F]{4,}$/, '')).replace(/[-_]+$/, '');
return p === '#' ? `[id*="${base || name}"]` : `[class*="${base || name}"]`;
}).replace(/\s*>\s*/g, ' ');
}
function logBlockedAttempt(url, triggerType) {
if (!CACHE.logs) CACHE.logs = gv('hider_global_logs', []);
CACHE.logs.unshift({ url: url || 'about:blank', time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' }), type: triggerType || 'Popup' });
if (CACHE.logs.length > 50) CACHE.logs.pop();
if (!logSaveTimer) logSaveTimer = setTimeout(() => { logSaveTimer = null; sv('hider_global_logs', CACHE.logs); }, 1000);
}
// Soft Frosted Snow Animation
function triggerFreezeFx() {
const fx = doc.createElement('div');
fx.className = 'hider-freeze-snow-overlay';
for (let i = 0; i < 14; i++) {
const flake = doc.createElement('div');
flake.className = 'hider-snow-flake';
flake.style.left = `${Math.random() * 100}vw`;
flake.style.top = `${Math.random() * 70}vh`;
flake.style.animationDelay = `${Math.random() * 0.3}s`;
flake.style.transform = `scale(${0.4 + Math.random() * 0.8})`;
fx.appendChild(flake);
}
(doc.body || doc.documentElement).appendChild(fx);
setTimeout(() => fx.remove(), 1300);
}
function forceExposeAndPlayVideos() {
doc.querySelectorAll('video').forEach(v => {
['display', 'visibility', 'opacity', 'pointer-events'].forEach(p => v.style.setProperty(p, p === 'display' ? 'block' : p === 'pointer-events' ? 'auto' : '1', 'important'));
v.play()?.catch?.(() => {});
});
}
function triggerVideoResumeChain() {
forceExposeAndPlayVideos(); [150, 400, 800].forEach(t => setTimeout(forceExposeAndPlayVideos, t));
broadcastToFrames(window, { type: 'HIDER_RESUME_VIDEOS' });
}
function simulateAdWindowSuccess() {
try {
win.dispatchEvent(new Event('blur')); doc.dispatchEvent(new Event('visibilitychange'));
win.onblur?.();
setTimeout(() => { win.dispatchEvent(new Event('focus')); win.onfocus?.(); }, 60);
triggerVideoResumeChain();
} catch {}
}
function createDummyWindow() {
const dummy = {
closed: false, focus(){}, blur(){}, close(){ this.closed = true; }, postMessage(){},
location: { href: 'about:blank', replace(u){ if(isDomainBlocked(u)) logBlockedAttempt(u, 'Blocked Dummy Replace'); }, assign(u){ if(isDomainBlocked(u)) logBlockedAttempt(u, 'Blocked Dummy Assign'); } },
document: { write(){}, close(){}, body:{} }, opener: win
};
return new Proxy(dummy, {
get: (t, p) => p in t ? t[p] : () => {},
set: (t, p, v) => { if (p === 'location' && isDomainBlocked(v)) logBlockedAttempt(v, 'Blocked Dummy Location Set'); else t[p] = v; return true; }
});
}
function handleFreezeNavigation(url, triggerType, onConfirm, onDeny) {
const mem = CACHE.freezeMemory || 'ask';
if (mem === 'block_all') { logBlockedAttempt(url, triggerType + ' (Auto-Block)'); onDeny?.(); simulateAdWindowSuccess(); return; }
if (mem === 'allow_all' || (mem === 'allow_same' && isSameDomain(url))) {
userApprovedNavigation = true; onConfirm?.(); setTimeout(() => userApprovedNavigation = false, 300); return;
}
showFreezePrompt(url, triggerType, onConfirm, onDeny);
}
// Interceptors
try {
const lp = win.Location ? win.Location.prototype : Object.getPrototypeOf(win.location);
if (lp) {
const origA = lp.assign, origR = lp.replace, hrefDesc = Object.getOwnPropertyDescriptor(lp, 'href');
lp.assign = function(u) { if (isDomainBlocked(u)) { logBlockedAttempt(u, 'Blocked Redirect'); showToast('⛔ Blocked redirect'); simulateAdWindowSuccess(); return; } return origA.apply(this, arguments); };
lp.replace = function(u) { if (isDomainBlocked(u)) { logBlockedAttempt(u, 'Blocked Redirect'); showToast('⛔ Blocked redirect'); simulateAdWindowSuccess(); return; } return origR.apply(this, arguments); };
if (hrefDesc?.set) {
Object.defineProperty(lp, 'href', { set(u) { if (isDomainBlocked(u)) { logBlockedAttempt(u, 'Blocked Redirect'); showToast('⛔ Blocked redirect'); simulateAdWindowSuccess(); return; } hrefDesc.set.call(this, u); } });
}
}
HTMLAnchorElement.prototype.click = function() { if (isDomainBlocked(this.href)) { logBlockedAttempt(this.href, 'Blocked Click'); showToast('⛔ Blocked link click'); simulateAdWindowSuccess(); return; } return HTMLElement.prototype.click.apply(this, arguments); };
} catch {}
try {
const origOpen = win.open;
win.open = function(url, target, features) {
if (isDomainBlocked(url)) { logBlockedAttempt(url || 'about:blank', 'Blocked Popup'); simulateAdWindowSuccess(); showToast('⛔ Blocked Popup'); return createDummyWindow(); }
if (!url || url === 'about:blank') {
const realWin = origOpen.apply(win, arguments);
return realWin ? new Proxy(realWin, {
get: (t, p) => p === 'location' ? new Proxy(t.location, { set: (l, lp, lv) => (lp==='href'||lp==='assign') && isDomainBlocked(lv) ? (logBlockedAttempt(lv, 'Blocked Popup Nav'), showToast('⛔ Blocked popup nav'), t.close(), true) : (l[lp]=lv, true) }) : (typeof t[p]==='function'?t[p].bind(t):t[p]),
set: (t, p, v) => p === 'location' && isDomainBlocked(v) ? (logBlockedAttempt(v, 'Blocked Popup Location'), showToast('⛔ Blocked popup nav'), t.close(), true) : (t[p]=v, true)
}) : createDummyWindow();
}
if (isFrozen && !userApprovedNavigation) { handleFreezeNavigation(url, 'win.open()', () => origOpen.call(win, url, target, features), () => {}); return createDummyWindow(); }
return origOpen.apply(win, arguments);
};
} catch {}
function broadcastToFrames(w, msg) { try { for (let i = 0; i < w.frames.length; i++) { w.frames[i].postMessage(msg, '*'); broadcastToFrames(w.frames[i], msg); } } catch {} }
function broadcastState() { if (isTop) broadcastToFrames(window, { type: 'HIDER_SYNC_STATE', isSelecting, currentScope, isFrozen }); }
window.addEventListener('message', e => {
if (!e.data) return;
if (e.data.type === 'HIDER_REQUEST_STATE' && isTop) broadcastState();
if (e.data.type === 'HIDER_SYNC_STATE') {
({ isSelecting, currentScope, isFrozen } = e.data);
by('btn-select')?.classList.toggle('active', isSelecting);
by('btn-scope')?.classList.toggle('active', currentScope === 'link');
by('btn-freeze')?.classList.toggle('active', isFrozen);
if (!isSelecting) clearSelectionState();
}
if (e.data.type === 'HIDER_RESUME_VIDEOS') triggerVideoResumeChain();
});
if (!isTop) try { window.top.postMessage({ type: 'HIDER_REQUEST_STATE' }, '*'); } catch {}
function updateStyles() {
let el = by('hider-dynamic-styles') || Object.assign(doc.createElement('style'), { id: 'hider-dynamic-styles' });
if (!el.parentNode) (doc.head || doc.documentElement).appendChild(el);
const host = location.hostname;
const siteRules = gv('hider_site_' + host, []);
const linkRules = gv('hider_link_' + cleanUrl(), []);
const customActiveRules = (CACHE.customRules || []).filter(r => {
if (!r || !r.selector) return false;
const t = cleanDomain(r.target);
return t === '*' || t === host || (t && host.endsWith('.' + t));
}).map(r => r.selector);
const combined = [...new Set([...siteRules, ...linkRules, ...customActiveRules])].filter(Boolean);
// Stealth CSS: Layout collapsing off-screen
const css = combined.map(s => `${s}{opacity:0!important;pointer-events:none!important;position:absolute!important;top:-99999px!important;left:-99999px!important;width:0!important;height:0!important;max-width:0!important;max-height:0!important;overflow:hidden!important;visibility:hidden!important;clip:rect(0,0,0,0)!important}`).join('\n');
if (cachedCssString !== css) {
cachedCssString = css;
el.textContent = css;
combined.forEach(s => {
try {
doc.querySelectorAll(s).forEach(targetEl => targetEl.classList.add('hider-stealth-target'));
} catch {}
});
}
}
updateStyles();
function getExactSelector(el) {
if (!el || el.nodeType !== 1) return '';
const path = []; let curr = el;
while (curr && curr.nodeType === 1 && curr.tagName.toLowerCase() !== 'html') {
let tag = curr.tagName.toLowerCase();
if (tag === 'body') { path.unshift('body'); break; }
if (curr.id && !curr.id.startsWith('hider-') && !/^\d+$/.test(curr.id) && curr.id.length < 15) { path.unshift(`#${CSS.escape(curr.id)}`); break; }
let rawC = typeof curr.className === 'string' ? curr.className : curr.getAttribute('class') || '';
let classes = rawC.trim().split(/\s+/).filter(c => c.length > 1 && !c.startsWith('hider-') && !/^[a-zA-Z0-9]{10,}$/.test(c) && !/^hover:/i.test(c));
if (classes.length) { tag += `.${CSS.escape(classes[0])}` + (classes[1] ? `.${CSS.escape(classes[1])}` : ''); }
else {
let idx = 1, sib = curr.previousElementSibling;
while (sib) { if (sib.tagName === curr.tagName) idx++; sib = sib.previousElementSibling; }
tag += `:nth-of-type(${idx})`;
}
path.unshift(tag); curr = curr.parentElement;
}
return path.join(' > ');
}
function attachDragListeners(barEl, handleEl) {
let startX, startY, initialX, initialY;
const onStart = e => {
if (e.target.tagName === 'BUTTON') return;
isDraggingStepper = true;
const p = e.touches ? e.touches[0] : e, r = barEl.getBoundingClientRect();
startX = p.clientX; startY = p.clientY; initialX = r.left; initialY = r.top;
barEl.style.transition = 'none';
};
const onMove = e => {
if (!isDraggingStepper) return; e.cancelable && e.preventDefault();
const p = e.touches ? e.touches[0] : e;
stepperPos = { x: initialX + (p.clientX - startX), y: initialY + (p.clientY - startY) };
Object.assign(barEl.style, { left: `${stepperPos.x}px`, top: `${stepperPos.y}px`, bottom: 'auto', transform: 'none' });
};
const onEnd = () => { if (isDraggingStepper) { isDraggingStepper = false; barEl.style.transition = 'all 0.2s cubic-bezier(0.16, 1, 0.3, 1)'; } };
handleEl.addEventListener('mousedown', onStart); window.addEventListener('mousemove', onMove); window.addEventListener('mouseup', onEnd);
handleEl.addEventListener('touchstart', onStart, { passive: false }); window.addEventListener('touchmove', onMove, { passive: false }); window.addEventListener('touchend', onEnd);
}
function clearSelectionState() {
previewElement?.classList.remove('hider-preview-highlight'); previewElement = null; stepperStack = [];
by(STEPPER_BAR_ID)?.remove();
}
function turnOffHideMode() {
if (isSelecting) {
isSelecting = false;
by('btn-select')?.classList.remove('active');
clearSelectionState();
broadcastState();
}
}
// Smart Auto-Close menus function with Mobile Keyboard & Editing Safety Guards (v4.3)
function closeAllMenus(e) {
if (isSelecting) return; // Do not auto-close if Hide Mode is active
if (isDraggingDock || isDraggingStepper) return; // Ignore while dragging UI
// Safety 1: Mobile Soft Keyboard / Input Focus Protection
const active = doc.activeElement;
if (active && (active.tagName === 'INPUT' || active.tagName === 'TEXTAREA' || active.tagName === 'SELECT')) {
if (active.closest('#' + UI_HOST_ID + ', #' + STEPPER_BAR_ID)) return;
}
// Safety 2: Scrolling or clicking inside the script UI elements
if (e && e.target && e.target.closest) {
if (e.target.closest('#' + UI_HOST_ID + ', #' + STEPPER_BAR_ID)) return;
}
// Safety 3: Inline Rule Editing Safety Guard
if (by('hider-panel')?.querySelector('.is-editing')) return;
const panel = by('hider-panel');
const menu = by('hider-dock-menu');
const mainBtn = by('btn-toggle-dock');
const dockEl = by('hider-main-dock');
if (panel && panel.style.display === 'flex') {
panel.style.display = 'none';
by('btn-manage')?.classList.remove('active');
}
if (menu && menu.style.display === 'flex') {
menu.style.display = 'none';
mainBtn?.classList.remove('expanded');
dockEl?.classList.remove('expanded');
dockEl?.classList.add('manual-hidden');
if (collapseTimer) clearTimeout(collapseTimer);
collapseTimer = setTimeout(() => {
dockEl?.classList.add('is-collapsed');
collapseTimer = null;
}, 1000);
}
}
function renderTouchStepperUI() {
let stepper = by(STEPPER_BAR_ID);
if (!previewElement) { stepper?.remove(); return; }
if (!stepper) { stepper = doc.createElement('div'); stepper.id = STEPPER_BAR_ID; (doc.body || doc.documentElement).appendChild(stepper); }
stepper.className = 'h-glass h-stepper-pill';
if (stepperPos.x !== null) Object.assign(stepper.style, { left: `${stepperPos.x}px`, top: `${stepperPos.y}px`, bottom: 'auto', transform: 'none' });
const tag = previewElement.tagName.toLowerCase();
const c = typeof previewElement.className === 'string' ? previewElement.className.trim().split(/\s+/)[0] : '';
const classStr = c && !c.startsWith('hider-') ? `.${c}` : '';
const idStr = previewElement.id && !previewElement.id.startsWith('hider-') ? `#${previewElement.id}` : '';
stepper.innerHTML = `<div id="hider-drag-handle" class="h-drag-dots" title="Drag">⋮⋮</div><button id="hider-step-up" class="h-btn-icon" title="Parent Level">▲</button><button id="hider-step-down" class="h-btn-icon" title="Child Level">▼</button><div class="h-tag-badge" title="${tag}${idStr}${classStr}">${tag}${idStr}${classStr}</div><button id="hider-step-confirm" class="h-btn-pill h-btn-save">Hide</button><button id="hider-step-cancel" class="h-btn-icon h-btn-close">✖</button>`;
attachDragListeners(stepper, stepper.querySelector('#hider-drag-handle'));
stepper.querySelector('#hider-step-up').onclick = e => {
e.stopPropagation(); const p = previewElement.parentElement;
if (p && p !== doc.body && p !== doc.documentElement && p.id !== UI_HOST_ID) {
previewElement.classList.remove('hider-preview-highlight'); stepperStack.push(previewElement);
previewElement = p; previewElement.classList.add('hider-preview-highlight'); renderTouchStepperUI();
} else showToast('Top parent reached');
};
stepper.querySelector('#hider-step-down').onclick = e => {
e.stopPropagation();
if (stepperStack.length || previewElement.firstElementChild) {
previewElement.classList.remove('hider-preview-highlight');
previewElement = stepperStack.length ? stepperStack.pop() : previewElement.firstElementChild;
previewElement.classList.add('hider-preview-highlight'); renderTouchStepperUI();
}
};
stepper.querySelector('#hider-step-confirm').onclick = e => { e.stopPropagation(); confirmHideSelectedElement(); };
stepper.querySelector('#hider-step-cancel').onclick = e => { e.stopPropagation(); clearSelectionState(); };
}
function confirmHideSelectedElement() {
if (!previewElement) return;
const el = previewElement;
el.classList.remove('hider-preview-highlight'); el.classList.add('hider-stealth-target');
const sel = getExactSelector(el);
if (sel) {
const key = currentScope === 'site' ? 'hider_site_' + location.hostname : 'hider_link_' + cleanUrl();
const s = gv(key, []); if (!s.includes(sel)) { s.push(sel); sv(key, s); }
}
updateStyles(); showToast('🙈 Element Hidden!'); clearSelectionState();
}
function showToast(msg) {
by('hider-toast')?.remove();
const toast = Object.assign(doc.createElement('div'), { id: 'hider-toast', className: 'h-glass h-toast', textContent: msg });
(doc.body || doc.documentElement).appendChild(toast);
setTimeout(() => { toast.style.opacity = '0'; toast.style.transform = 'translateX(-50%) translateY(8px)'; setTimeout(() => toast.remove(), 250); }, 1800);
}
function showFreezePrompt(url, triggerType, onConfirm, onDeny) {
by('hider-freeze-prompt')?.remove();
const promptEl = Object.assign(doc.createElement('div'), { id: 'hider-freeze-prompt', className: 'h-glass h-prompt' });
const displayUrl = url ? (url.length > 38 ? url.substring(0, 35) + '...' : url) : 'another page';
promptEl.innerHTML = `<div style="font-weight:700;color:#38bdf8;font-size:11px;text-transform:uppercase">❄️ Soft Freeze Triggered</div><div style="font-size:12px;color:#f8fafc">Proceed to page?</div><div class="h-url-box">${displayUrl}</div><select id="hider-freeze-modal-memory" class="h-select"><option value="ask" ${CACHE.freezeMemory==='ask'?'selected':''}>Ask Every Time</option><option value="block_all" ${CACHE.freezeMemory==='block_all'?'selected':''}>Auto-Block All</option><option value="allow_same" ${CACHE.freezeMemory==='allow_same'?'selected':''}>Allow Same Domain</option><option value="allow_all" ${CACHE.freezeMemory==='allow_all'?'selected':''}>Allow All Navigations</option></select><div style="display:flex;gap:6px;width:100%"><button id="hider-freeze-yes" class="h-btn-pill h-btn-save" style="flex:1">Yes</button><button id="hider-freeze-no" class="h-btn-pill h-btn-close" style="flex:1">No</button></div>`;
(doc.body || doc.documentElement).appendChild(promptEl);
const saveMem = () => { const m = by('hider-freeze-modal-memory'); if (m && m.value !== CACHE.freezeMemory) { CACHE.freezeMemory = m.value; sv('hider_freeze_memory', m.value); } };
promptEl.querySelector('#hider-freeze-yes').onclick = () => { saveMem(); promptEl.remove(); onConfirm?.(); };
promptEl.querySelector('#hider-freeze-no').onclick = () => { saveMem(); logBlockedAttempt(url, triggerType + ' (User)'); promptEl.remove(); onDeny?.(); simulateAdWindowSuccess(); };
}
function injectStylesAndUI() {
if (by('hider-core-styles')) return;
const s = doc.createElement('style'); s.id = 'hider-core-styles';
s.textContent = `
.h-glass{background:rgba(15,23,42,.92);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);border:1px solid rgba(255,255,255,.12);color:#f8fafc;font-family:system-ui,-apple-system,sans-serif;box-sizing:border-box;border-radius:12px}
.h-dock{position:fixed;right:0;top:35%;z-index:2147483646;display:flex;flex-direction:column;align-items:flex-end;gap:5px;padding:4px;box-shadow:0 10px 25px rgba(0,0,0,.5);pointer-events:auto;transition:transform .25s ease,opacity .25s ease;will-change:transform,opacity}
.h-dock.is-collapsed{transform:translateX(50%);opacity:.4}
.h-dock.manual-hidden{transform:translateX(50%!important);opacity:.4!important}
.h-dock.is-collapsed:not(.manual-hidden):hover,.h-dock.expanded{transform:translateX(0);opacity:1}
.h-dock-main{width:36px;height:36px;font-size:16px!important;background:rgba(59,130,246,.3)!important;border-color:rgba(96,165,250,.5)!important;cursor:grab!important}
.h-dock-main:active{cursor:grabbing!important}
.h-dock-main.expanded{background:#3b82f6!important;color:#fff!important}
.h-dock-menu{display:none;flex-direction:column;gap:5px;margin-top:2px}
.h-dock-btn{width:36px;height:36px;border-radius:9px;border:1px solid rgba(255,255,255,.08);background:rgba(255,255,255,.05);color:#cbd5e1;font-size:10px;font-weight:700;display:flex;flex-direction:column;align-items:center;justify-content:center;cursor:pointer;transition:all .15s ease}
.h-dock-btn:hover{background:rgba(255,255,255,.15);color:#fff}
.h-dock-btn.active{background:#3b82f6!important;color:#fff!important;border-color:#60a5fa!important}
.h-dock-btn.scope-link{background:#10b981!important;color:#fff!important}
.h-dock-btn.is-frozen{background:#8b5cf6!important;color:#fff!important}
.h-stepper-pill{position:fixed;bottom:20px;left:50%;transform:translateX(-50%);z-index:2147483647;padding:5px 8px;display:flex;align-items:center;gap:6px;box-shadow:0 10px 30px rgba(0,0,0,.6);pointer-events:auto}
.h-drag-dots{cursor:grab;color:#64748b;font-size:12px;padding:0 3px;user-select:none}
.h-btn-icon{width:28px;height:28px;border-radius:7px;border:1px solid rgba(255,255,255,.1);background:rgba(255,255,255,.06);color:#e2e8f0;font-size:11px;font-weight:700;cursor:pointer;display:flex;align-items:center;justify-content:center}
.h-btn-icon:hover{background:rgba(255,255,255,.18)}
.h-tag-badge{max-width:130px;height:26px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;background:rgba(0,0,0,.4);border:1px solid rgba(255,255,255,.08);border-radius:6px;padding:0 6px;color:#38bdf8;font-size:10px;font-family:monospace;line-height:24px}
.h-btn-pill{height:28px;padding:0 10px;border-radius:7px;font-size:11px;font-weight:700;border:none;cursor:pointer;display:flex;align-items:center;justify-content:center}
.h-btn-save{background:#10b981;color:#fff}
.h-btn-close{background:rgba(239,68,68,.2);border:1px solid rgba(239,68,68,.4);color:#f87171}
.h-toast{position:fixed;bottom:70px;left:50%;transform:translateX(-50%);padding:6px 16px;font-size:11px;font-weight:600;z-index:2147483647;pointer-events:none;transition:all .25s ease}
.h-prompt{position:fixed;top:30px;left:50%;transform:translateX(-50%);padding:14px 16px;z-index:2147483647;display:flex;flex-direction:column;align-items:center;gap:8px;box-shadow:0 15px 35px rgba(0,0,0,.6);pointer-events:auto;width:300px;max-width:90vw}
.h-url-box{font-size:10px;color:#9ca3af;word-break:break-all;max-height:32px;overflow:hidden;background:rgba(0,0,0,.3);padding:4px 6px;border-radius:6px;width:100%;border:1px solid rgba(255,255,255,.05)}
.h-select{width:100%;background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.15);border-radius:6px;color:#fff;padding:5px 8px;font-size:10px;outline:0;box-sizing:border-box}
#hider-panel{position:fixed;right:50px;top:75px;z-index:2147483647;width:330px;max-height:80vh;border-radius:14px;padding:12px;display:none;flex-direction:column;overflow-y:auto;pointer-events:auto;box-shadow:0 15px 35px rgba(0,0,0,.6);gap:8px}
.panel-header{font-weight:700;font-size:12px;border-bottom:1px solid rgba(255,255,255,.1);padding-bottom:6px;display:flex;justify-content:space-between;align-items:center}
.section-title{font-size:9px;color:#94a3b8;font-weight:700;text-transform:uppercase;letter-spacing:.5px;display:flex;justify-content:space-between;align-items:center;margin-top:6px}
.list-item{font-size:10px;background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.06);border-radius:6px;padding:5px 7px;display:flex;justify-content:space-between;align-items:center;gap:6px}
.list-item span.rule-text{word-break:break-all;padding-right:4px;color:#cbd5e1;font-family:monospace;font-size:9px;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.hider-btn-small{cursor:pointer;border:none;border-radius:4px;padding:2px 6px;color:#fff;font-weight:600;background:#ef4444;font-size:9px}
@keyframes hider-pulse{0%,100%{outline:2px stroke #ef4444}50%{outline:2px dashed #ef4444;box-shadow:inset 0 0 10px rgba(239,68,68,.3)}}
.hider-preview-highlight{animation:hider-pulse .8s infinite!important;cursor:pointer!important}
/* Soft Frosted Snow Animation */
@keyframes hider-snow-freeze {
0% { opacity: 0; backdrop-filter: blur(0px); background: rgba(224, 242, 254, 0); }
35% { opacity: 1; backdrop-filter: blur(8px) saturate(130%); background: radial-gradient(circle at center, rgba(186, 230, 253, 0.22) 0%, rgba(56, 189, 248, 0.12) 60%, rgba(15, 23, 42, 0.35) 100%); box-shadow: inset 0 0 120px rgba(186, 230, 253, 0.35); }
100% { opacity: 0; backdrop-filter: blur(0px); background: rgba(224, 242, 254, 0); }
}
@keyframes hider-flake-drift {
0% { transform: translateY(0) scale(0.6); opacity: 0; }
40% { opacity: 0.95; }
100% { transform: translateY(40px) scale(1.1); opacity: 0; }
}
.hider-freeze-snow-overlay { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; z-index: 2147483647; pointer-events: none; animation: hider-snow-freeze 1.3s cubic-bezier(0.16, 1, 0.3, 1) forwards; }
.hider-snow-flake { position: absolute; width: 6px; height: 6px; background: #e0f2fe; border-radius: 50%; box-shadow: 0 0 8px #38bdf8, 0 0 12px #ffffff; animation: hider-flake-drift 1.2s cubic-bezier(0.16, 1, 0.3, 1) forwards; }
`;
(doc.head || doc.documentElement).appendChild(s);
if (!isTop || by(UI_HOST_ID)) return;
const host = doc.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;';
(doc.body || doc.documentElement).appendChild(host);
host.innerHTML = `
<div class="h-glass h-dock is-collapsed manual-hidden" id="hider-main-dock">
<button class="h-dock-btn h-dock-main" id="btn-toggle-dock" title="Toggle Hider Menu">🛡️</button>
<div class="h-dock-menu" id="hider-dock-menu">
<button class="h-dock-btn" id="btn-select" title="Target Element to Hide">🎯<span>Hide</span></button>
<button class="h-dock-btn" id="btn-scope" title="Toggle Scope (Site/Page)">🌐<span>SITE</span></button>
<button class="h-dock-btn ${isFrozen?'is-frozen':''}" id="btn-freeze" title="Toggle Navigation Freeze">❄️<span>Freeze</span></button>
<button class="h-dock-btn" id="btn-manage" title="Open Control Panel">⚙️<span>Menu</span></button>
</div>
</div>
<div id="hider-panel" class="h-glass">
<div class="panel-header"><span>🛡️ Hide Web Elements Pro <span style="font-size:9px;color:#60a5fa">v4.3</span></span><button class="hider-btn-small" id="close-p" style="background:rgba(255,255,255,0.1)">✖</button></div>
<!-- Manual Rule Builder -->
<div class="section-title">Manual Rule Builder</div>
<div style="display:flex;flex-direction:column;gap:4px">
<input type="text" id="manual-rule-input" placeholder="Rule CSS Selector (e.g. .ad-banner)" class="h-select">
<div style="display:flex;gap:4px">
<input type="text" id="manual-target-input" placeholder="Target Domain (* for Global)" class="h-select" style="flex:1">
<button class="hider-btn-small" id="save-rule-btn" style="background:#3b82f6;padding:4px 10px">Save</button>
</div>
</div>
<!-- Custom Editable Rules -->
<div class="section-title">Custom Rules</div>
<div id="list-custom-rules"></div>
<!-- Domain Blacklist -->
<div class="section-title">Block Domain</div>
<div style="display:flex;gap:4px">
<input type="text" id="manual-domain-input" placeholder="e.g. badsite.com" class="h-select" style="flex:1">
<button class="hider-btn-small" id="add-domain-btn" style="background:#3b82f6">Add</button>
</div>
<div class="section-title">Blocked Domains</div>
<div id="list-blocked-domains"></div>
<div class="section-title">Freeze Mode Behavior</div>
<select id="freeze-memory-select" class="h-select">
<option value="ask">Ask Every Time</option>
<option value="block_all">Auto-Block All</option>
<option value="allow_same">Allow Same Domain</option>
<option value="allow_all">Allow All Navigations</option>
</select>
<div class="section-title"><span>Blocked Log</span><button class="hider-btn-small" id="clear-log-btn" style="background:#475569">Clear</button></div>
<div id="list-blocked-log" style="max-height:90px;overflow-y:auto;background:rgba(0,0,0,.25);border-radius:6px;padding:4px"></div>
<div class="section-title">Hidden Site-Wide (${location.hostname})</div>
<div id="list-site"></div>
<div class="section-title">Hidden This Page Only</div>
<div id="list-link"></div>
</div>`;
const dockEl = by('hider-main-dock'), mainBtn = by('btn-toggle-dock');
// Restore saved FAB dock Y position
const savedY = gv('hider_dock_y', null);
if (savedY) dockEl.style.top = savedY;
dockEl.addEventListener('mouseleave', () => dockEl.classList.remove('manual-hidden'));
// Draggable FAB Dock Logic
let dockStartY, dockInitY, dockMoved = false;
const onDockStart = e => {
const p = e.touches ? e.touches[0] : e;
dockStartY = p.clientY; dockInitY = dockEl.getBoundingClientRect().top;
isDraggingDock = true; dockMoved = false;
dockEl.style.transition = 'none';
};
const onDockMove = e => {
if (!isDraggingDock) return;
const p = e.touches ? e.touches[0] : e;
const dy = p.clientY - dockStartY;
if (Math.abs(dy) > 4) dockMoved = true;
if (dockMoved) {
e.cancelable && e.preventDefault();
dockEl.style.top = `${dockInitY + dy}px`;
}
};
const onDockEnd = () => {
if (isDraggingDock) {
isDraggingDock = false;
dockEl.style.transition = '';
if (dockMoved) sv('hider_dock_y', dockEl.style.top);
}
};
mainBtn.addEventListener('mousedown', onDockStart);
window.addEventListener('mousemove', onDockMove, { passive: true });
window.addEventListener('mouseup', onDockEnd);
mainBtn.addEventListener('touchstart', onDockStart, { passive: false });
window.addEventListener('touchmove', onDockMove, { passive: false });
window.addEventListener('touchend', onDockEnd);
mainBtn.onclick = e => {
e.stopPropagation();
if (dockMoved) return;
if (collapseTimer) { clearTimeout(collapseTimer); collapseTimer = null; }
const menu = by('hider-dock-menu');
const isOpening = menu.style.display !== 'flex';
menu.style.display = isOpening ? 'flex' : 'none';
mainBtn.classList.toggle('expanded', isOpening);
dockEl.classList.toggle('expanded', isOpening);
dockEl.classList.toggle('is-collapsed', !isOpening);
if (!isOpening) {
turnOffHideMode();
by('hider-panel').style.display = 'none';
by('btn-manage')?.classList.remove('active');
dockEl.classList.add('manual-hidden');
collapseTimer = setTimeout(() => {
dockEl.classList.add('is-collapsed');
collapseTimer = null;
}, 1000);
} else {
dockEl.classList.remove('manual-hidden');
}
};
// Manual Rule Builder Handler
by('save-rule-btn').onclick = () => {
const ruleInput = by('manual-rule-input');
const targetInput = by('manual-target-input');
const sel = (ruleInput.value || '').trim();
let target = cleanDomain(targetInput.value) || '*';
if (!sel) { showToast('⚠️ Enter a selector'); return; }
if (editingRuleId) {
const idx = CACHE.customRules.findIndex(r => r.id === editingRuleId);
if (idx !== -1) {
CACHE.customRules[idx] = { id: editingRuleId, selector: sel, target };
showToast('✏️ Rule updated');
}
editingRuleId = null;
by('save-rule-btn').textContent = 'Save';
} else {
const newRule = { id: 'rule_' + Date.now(), selector: sel, target };
CACHE.customRules.push(newRule);
showToast('✨ Custom Rule Added!');
}
sv('hider_custom_rules_v4', CACHE.customRules);
ruleInput.value = ''; targetInput.value = '';
updateStyles(); renderList();
};
const addDomain = () => {
const input = by('manual-domain-input'), d = cleanDomain(input.value);
if (d && !CACHE.blockedDomainsSet.has(d)) {
CACHE.blockedDomainsList.push(d); CACHE.blockedDomainsSet.add(d);
sv('hider_blocked_domains', CACHE.blockedDomainsList); showToast(`🚫 Blocked: ${d}`);
}
input.value = ''; renderList();
};
by('add-domain-btn').onclick = addDomain;
by('manual-domain-input').onkeypress = e => e.key === 'Enter' && addDomain();
by('btn-select').onclick = e => {
isSelecting = !isSelecting;
e.currentTarget.classList.toggle('active', isSelecting);
if (isSelecting) { by('hider-panel').style.display = 'none'; by('btn-manage').classList.remove('active'); }
else clearSelectionState();
broadcastState();
};
const btnScope = by('btn-scope');
btnScope.onclick = () => {
currentScope = currentScope === 'site' ? 'link' : 'site';
btnScope.querySelector('span').textContent = currentScope.toUpperCase();
btnScope.classList.toggle('scope-link', currentScope === 'link');
showToast(`Scope: ${currentScope.toUpperCase()}`); broadcastState();
};
const btnFreeze = by('btn-freeze');
btnFreeze.onclick = () => {
isFrozen = !isFrozen; CACHE.isFrozen = isFrozen; sv('hider_freeze_global', isFrozen);
btnFreeze.classList.toggle('is-frozen', isFrozen);
if (isFrozen) triggerFreezeFx();
showToast(isFrozen ? '❄️ Freeze ON' : '🔥 Freeze OFF'); broadcastState();
};
by('btn-manage').onclick = e => {
const p = by('hider-panel'), show = p.style.display !== 'flex';
p.style.display = show ? 'flex' : 'none';
e.currentTarget.classList.toggle('active', show);
if (show) { turnOffHideMode(); renderList(); }
};
const closePanelAndTriggerCollapse = e => {
turnOffHideMode();
closeAllMenus(e);
};
by('close-p').onclick = closePanelAndTriggerCollapse;
by('clear-log-btn').onclick = () => { CACHE.logs = []; sv('hider_global_logs', []); renderList(); };
}
function renderList() {
if (!CACHE.logs) CACHE.logs = gv('hider_global_logs', []);
const siteKey = 'hider_site_' + location.hostname, linkKey = 'hider_link_' + cleanUrl();
const siteData = gv(siteKey, []), linkData = gv(linkKey, []);
const cCustom = by('list-custom-rules'), cDomains = by('list-blocked-domains'), cSite = by('list-site'), cLink = by('list-link'), cLog = by('list-blocked-log'), memSelect = by('freeze-memory-select');
if (!cSite || !cLink) return;
if (memSelect) {
memSelect.value = CACHE.freezeMemory || 'ask';
memSelect.onchange = e => { CACHE.freezeMemory = e.target.value; sv('hider_freeze_memory', e.target.value); showToast(`Memory updated`); };
}
// Render Custom Rules
if (cCustom) {
cCustom.innerHTML = CACHE.customRules.length ? '' : `<div style="font-size:9px;color:#64748b">No custom rules</div>`;
CACHE.customRules.forEach(rule => {
const div = doc.createElement('div'); div.className = 'list-item';
const targetTag = rule.target === '*' ? '<span style="color:#38bdf8">[Global]</span>' : `<span style="color:#10b981">[${rule.target}]</span>`;
div.innerHTML = `<span class="rule-text" title="${rule.selector}">${rule.selector} ${targetTag}</span><div style="display:flex;gap:2px"><button class="hider-btn-small btn-edit" style="background:#3b82f6">✏️</button><button class="hider-btn-small btn-del">✖</button></div>`;
div.querySelector('.btn-edit').onclick = () => {
editingRuleId = rule.id;
by('manual-rule-input').value = rule.selector;
by('manual-target-input').value = rule.target;
by('save-rule-btn').textContent = 'Update';
};
div.querySelector('.btn-del').onclick = () => {
CACHE.customRules = CACHE.customRules.filter(r => r.id !== rule.id);
sv('hider_custom_rules_v4', CACHE.customRules); updateStyles(); renderList();
};
cCustom.appendChild(div);
});
}
if (cDomains) {
cDomains.innerHTML = CACHE.blockedDomainsList.length ? '' : `<div style="font-size:9px;color:#64748b">None</div>`;
CACHE.blockedDomainsList.forEach((d, i) => {
const div = doc.createElement('div'); div.className = 'list-item';
div.innerHTML = `<span class="rule-text" title="${d}">${d}</span><button class="hider-btn-small">✖</button>`;
div.querySelector('button').onclick = () => { CACHE.blockedDomainsList.splice(i, 1); CACHE.blockedDomainsSet.delete(cleanDomain(d)); sv('hider_blocked_domains', CACHE.blockedDomainsList); renderList(); };
cDomains.appendChild(div);
});
}
if (cLog) {
cLog.innerHTML = CACHE.logs.length ? '' : `<div style="font-size:9px;color:#64748b;text-align:center">No logs today</div>`;
CACHE.logs.forEach(item => {
const row = doc.createElement('div'); row.style.cssText = 'font-size:9px;border-bottom:1px solid rgba(255,255,255,0.06);padding:3px 0;';
row.innerHTML = `<div style="display:flex;justify-content:space-between;color:#a7f3d0;font-weight:bold"><span>${item.type}</span><span style="color:#64748b">${item.time}</span></div><div style="color:#94a3b8;word-break:break-all;font-family:monospace">${item.url}</div>`;
cLog.appendChild(row);
});
}
// Render & Editable Hidden Elements Rules
const renderEditableHiddenItems = (data, key, container) => {
container.innerHTML = data.length ? '' : `<div style="font-size:9px;color:#64748b">None</div>`;
data.forEach((sel, i) => {
const div = doc.createElement('div'); div.className = 'list-item';
div.innerHTML = `
<span class="rule-text" title="${sel}">${sel}</span>
<div style="display:flex;gap:2px;align-items:center">
<button class="hider-btn-small btn-edit" style="background:#3b82f6" title="Edit Rule Inline">✏️</button>
<button class="hider-btn-small btn-wild" style="background:#8b5cf6" title="Convert Wildcard">🪄</button>
<button class="hider-btn-small btn-del" style="background:#ef4444" title="Delete Rule">✖</button>
</div>`;
const textSpan = div.querySelector('.rule-text');
const editBtn = div.querySelector('.btn-edit');
editBtn.onclick = () => {
if (div.classList.contains('is-editing')) return;
div.classList.add('is-editing');
const input = doc.createElement('input');
input.type = 'text';
input.value = data[i];
input.className = 'h-select';
input.style.cssText = 'font-size:9px;padding:2px 4px;height:22px;flex:1;margin-right:4px;';
const saveBtn = doc.createElement('button');
saveBtn.className = 'hider-btn-small';
saveBtn.style.cssText = 'background:#10b981;padding:2px 6px;';
saveBtn.textContent = '✓';
textSpan.replaceWith(input);
editBtn.replaceWith(saveBtn);
input.focus();
const saveHandler = () => {
const newVal = input.value.trim();
if (newVal) {
data[i] = newVal;
sv(key, data);
updateStyles();
showToast('✏️ Hidden rule updated');
}
renderList();
};
saveBtn.onclick = saveHandler;
input.onkeypress = e => e.key === 'Enter' && saveHandler();
};
div.querySelector('.btn-wild').onclick = () => {
data[i] = convertToWildcardSelector(data[i]);
sv(key, data); updateStyles(); renderList(); showToast(`🪄 Wildcard applied`);
};
div.querySelector('.btn-del').onclick = () => {
const removed = data.splice(i, 1)[0]; sv(key, data);
try { doc.querySelectorAll(removed).forEach(el => el.classList.remove('hider-stealth-target')); } catch {}
updateStyles(); renderList();
};
container.appendChild(div);
});
};
renderEditableHiddenItems(siteData, siteKey, cSite);
renderEditableHiddenItems(linkData, linkKey, cLink);
}
function handleNavigationClick(e) {
if (isSelecting || e.target.closest('#' + UI_HOST_ID + ', #hider-freeze-prompt, #' + STEPPER_BAR_ID)) return;
const link = e.target.closest('a[href], area[href]');
if (link?.href && !link.getAttribute('href').startsWith('#') && !link.getAttribute('href').startsWith('javascript:')) {
const targetUrl = link.href;
if (isDomainBlocked(targetUrl)) {
e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation();
logBlockedAttempt(targetUrl, 'Blocked Link Click'); simulateAdWindowSuccess(); showToast('⛔ Force Blocked'); return;
}
if (!isFrozen) return;
e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation();
const isNewTab = link.target === '_blank' || e.ctrlKey || e.metaKey || e.button === 1;
handleFreezeNavigation(targetUrl, 'Link Click', () => {
userApprovedNavigation = true;
if (isNewTab) win.open(targetUrl, '_blank'); else win.location.href = targetUrl;
setTimeout(() => userApprovedNavigation = false, 300);
}, simulateAdWindowSuccess);
}
}
// Auto-close safety check on scroll & tap (v4.3)
window.addEventListener('scroll', closeAllMenus, { passive: true });
window.addEventListener('click', e => {
const isUI = e.target.closest('#' + UI_HOST_ID + ', #hider-freeze-prompt, #' + STEPPER_BAR_ID);
if (!isUI) {
closeAllMenus(e); // Close menus when user taps/clicks main website
}
if (isUI) return;
handleNavigationClick(e);
if (!isSelecting) return;
e.preventDefault(); e.stopPropagation();
if (previewElement === e.target) confirmHideSelectedElement();
else {
previewElement?.classList.remove('hider-preview-highlight');
stepperStack = []; previewElement = e.target; previewElement.classList.add('hider-preview-highlight'); renderTouchStepperUI();
}
}, true);
window.addEventListener('auxclick', handleNavigationClick, true);
window.addEventListener('submit', e => {
if (e.target.closest('#' + UI_HOST_ID + ', #hider-freeze-prompt, #' + STEPPER_BAR_ID)) return;
const form = e.target, action = form.action || location.href;
if (isDomainBlocked(action)) {
e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation();
logBlockedAttempt(action, 'Blocked Form Submit'); simulateAdWindowSuccess(); showToast('⛔ Blocked Form'); return;
}
if (!isFrozen) return;
e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation();
handleFreezeNavigation(action, 'Form Submit', () => { userApprovedNavigation = true; form.submit(); setTimeout(() => userApprovedNavigation = false, 300); }, simulateAdWindowSuccess);
}, true);
win.addEventListener('beforeunload', e => {
if (isFrozen && !userApprovedNavigation && (CACHE.freezeMemory === 'ask' || CACHE.freezeMemory === 'block_all')) {
e.preventDefault(); return (e.returnValue = 'Page navigation is currently frozen site-wide.');
}
}, true);
const checkUrlChange = () => {
if (location.href !== lastUrl) {
lastUrl = location.href; updateStyles();
if (by('hider-panel')?.style.display === 'flex') renderList();
}
};
['pushState', 'replaceState'].forEach(fn => {
const orig = history[fn];
if (orig) history[fn] = function() { orig.apply(this, arguments); checkUrlChange(); };
});
window.addEventListener('popstate', checkUrlChange, { passive: true });
window.addEventListener('hashchange', checkUrlChange, { passive: true });
function init() {
['contextmenu', 'selectstart', 'copy', 'paste', 'dragstart'].forEach(evt => doc.addEventListener(evt, e => e.stopPropagation(), true));
injectStylesAndUI(); updateStyles();
let timer = null;
new MutationObserver(() => {
if (timer) return;
timer = setTimeout(() => {
timer = null;
if (isTop && !by(UI_HOST_ID)) injectStylesAndUI();
if (!by('hider-dynamic-styles')) updateStyles();
}, 1000);
}).observe(doc.documentElement || doc.body, { childList: true, subtree: true });
}
if (doc.readyState === 'loading') doc.addEventListener('DOMContentLoaded', init); else init();
})();