Stealth element hider, Shadow DOM UI, persistent freeze, link extractor, time skipper, enhanced reveal, dock button visibility, stable Reveal & Unblock Engine, plus media downloader with preview. Automatic Protection.
// ==UserScript==
// @name Hide Web Elements Pro
// @version 12.0
// @description Stealth element hider, Shadow DOM UI, persistent freeze, link extractor, time skipper, enhanced reveal, dock button visibility, stable Reveal & Unblock Engine, plus media downloader with preview. Automatic Protection.
// @author KTZ
// @match *://*/*
// @icon https://cdn.corenexis.com/f/v9leABxGUbJ.png
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_addValueChangeListener
// @grant GM_xmlhttpRequest
// @grant GM_download
// @grant unsafeWindow
// @connect *
// @run-at document-start
// @license MIT
// @namespace https://greasyfork.org/users/1620673
// ==/UserScript==
(function() {
'use strict';
// ---------- Safe Storage ----------
const gv = (key, def) => {
try {
if (typeof GM_getValue !== 'undefined') return GM_getValue(key, def);
const item = localStorage.getItem(key);
return item !== null ? JSON.parse(item) : def;
} catch {
return def;
}
};
let isSyncingStorage = false;
const sv = (key, val) => {
try {
if (typeof GM_setValue !== 'undefined') {
GM_setValue(key, val);
} else {
localStorage.setItem(key, JSON.stringify(val));
}
} catch {}
if (!isSyncingStorage) {
isSyncingStorage = true;
if (typeof syncCache === 'function') syncCache();
if (typeof updateStyles === 'function') requestUpdateStyles();
if (isTop && typeof broadcastState === 'function') broadcastState();
isSyncingStorage = false;
}
};
const doc = document, win = typeof unsafeWindow !== 'undefined' ? unsafeWindow : window;
const UI_HOST_ID = 'hider-ui-root', STEPPER_BAR_ID = 'hider-stepper-bar';
let isTop = false;
try { isTop = (window.self === window.top); } catch { isTop = false; }
let shadowRoot = null;
const shadowBy = id => shadowRoot ? shadowRoot.getElementById(id) : null;
// ---------- Persistent Settings ----------
let initialMem = gv('hider_freeze_memory', null) || (gv('hider_autono_global', false) ? 'block_all' : 'ask');
const CACHE = {
blockedDomainsList: [], blockedDomainsSet: new Set(),
allowedDomainsList: [], allowedDomainsSet: new Set(),
customRules: [], isFrozen: gv('hider_freeze_global', false),
freezeMemory: initialMem, logs: null,
autoTimeSkipper: gv('hider_auto_time_skipper', false),
autoScroll: gv('hider_auto_scroll', true),
enableContextMenu: gv('hider_enable_contextmenu', true),
autoRemoveBlur: gv('hider_auto_remove_blur', false),
// Protection is always ON – no user toggle
universalProtect: true
};
// ---------- Dock Button Visibility ----------
const DOCK_BUTTONS = [
{ id: 'btn-select', label: '🎯 Hide' },
{ id: 'btn-scope', label: '🌐 Scope' },
{ id: 'btn-reveal-quick', label: '👁️ Reveal' },
{ id: 'btn-links', label: '🔗 Links' },
{ id: 'btn-skip-30', label: '⏩ +30s' },
{ id: 'btn-freeze', label: '❄️ Freeze' }
];
function getHiddenDockButtons() {
return gv('hider_hidden_dock_buttons', []);
}
function setHiddenDockButtons(arr) {
sv('hider_hidden_dock_buttons', arr);
}
function applyDockButtonVisibility() {
if (!shadowRoot) return;
const hidden = new Set(getHiddenDockButtons());
DOCK_BUTTONS.forEach(btn => {
const el = shadowRoot.getElementById(btn.id);
if (el) {
el.style.setProperty('display', hidden.has(btn.id) ? 'none' : 'flex', 'important');
}
});
const menuBtn = shadowRoot.getElementById('btn-manage');
if (menuBtn) menuBtn.style.setProperty('display', 'flex', 'important');
}
function renderDockButtonOptions() {
if (!shadowRoot) return;
const container = shadowRoot.getElementById('dock-buttons-list');
if (!container) return;
const hidden = new Set(getHiddenDockButtons());
container.innerHTML = '';
DOCK_BUTTONS.forEach(btn => {
const label = document.createElement('label');
label.style.cssText = 'font-size:10px!important;color:#cbd5e1!important;display:flex!important;align-items:center!important;gap:6px;cursor:pointer;user-select:none;';
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.dataset.btnId = btn.id;
checkbox.checked = !hidden.has(btn.id);
checkbox.style.cssText = 'accent-color:#38bdf8;cursor:pointer;';
checkbox.addEventListener('change', function() {
const id = this.dataset.btnId;
let hiddenArr = getHiddenDockButtons();
if (this.checked) {
hiddenArr = hiddenArr.filter(h => h !== id);
} else {
if (!hiddenArr.includes(id)) hiddenArr.push(id);
}
setHiddenDockButtons(hiddenArr);
applyDockButtonVisibility();
});
label.appendChild(checkbox);
const span = document.createElement('span');
span.textContent = btn.label;
label.appendChild(span);
container.appendChild(label);
});
}
// ---------- Core Variables ----------
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, scrollAnimationFrame = null, styleUpdateRAF = null;
let linkPanelEl = null;
let linkDisplayMode = 'text';
let isProtected = false;
let featuresEnabled = true;
let CURRENT_DOMAIN = '', CURRENT_URL = '';
// Global preview outside listener
let previewOutsideListener = null;
const FREEZE_LABELS = {
'ask': '❓ Ask Every Time', 'block_all': '⛔ Auto-Block All Navigations',
'allow_same': '🔗 Allow Same Domain Only', 'allow_all': '🟢 Allow All Navigations'
};
const resolveUrl = u => {
if (!u || typeof u !== 'string') return '';
const trimmed = u.trim();
if (trimmed === '*') return '*';
if (!/^[a-zA-Z][a-zA-Z0-9+-.]*:\/\//.test(trimmed)) {
if (trimmed.startsWith('//')) return 'http:' + trimmed;
if (!trimmed.startsWith('/') && !trimmed.startsWith('./') && !trimmed.startsWith('../')) {
return 'http://' + trimmed;
}
}
try { return new URL(trimmed, win.location.href).href; } catch { return trimmed; }
};
const cleanUrl = () => CURRENT_URL || (CURRENT_URL = location.origin + location.pathname);
const cleanDomain = url => {
try {
if (!url) return '';
if (url === '*') return '*';
const abs = resolveUrl(url);
return new URL(abs).hostname.replace(/^www\./, '').toLowerCase();
} catch {
return String(url).trim().toLowerCase();
}
};
const updateCurrentLocCache = () => {
CURRENT_URL = location.origin + location.pathname;
CURRENT_DOMAIN = cleanDomain(location.href);
updateProtectionFlag();
applyAllSettings();
};
function getParentDomain(url) {
if (!url) return '';
try {
const host = cleanDomain(url);
if (!host || host === '*') return host;
const parts = host.split('.');
if (parts.length <= 2) return host;
const multiPartTlds = ['co.uk', 'com.au', 'org.uk', 'gov.uk', 'co.jp', 'com.br', 'co.id', 'or.id', 'ac.uk', 'net.au', 'com.tw', 'co.nz', 'com.sg', 'com.mx', 'co.kr', 'com.tr'];
const lastTwo = parts.slice(-2).join('.');
return (multiPartTlds.includes(lastTwo) && parts.length > 2) ? parts.slice(-3).join('.') : lastTwo;
} catch {
return cleanDomain(url) || '';
}
}
const FEATURE_BLACKLIST = ['facebook.com', 'fb.com', 'facebook'];
let blacklistToastShown = false;
function isFeatureBlacklisted() {
if (!CURRENT_DOMAIN) return false;
return FEATURE_BLACKLIST.some(domain =>
CURRENT_DOMAIN.includes(domain) || CURRENT_DOMAIN.endsWith('.' + domain)
);
}
// ============ REVEAL & UNBLOCK ENGINE ============
function isProtectedPage() {
try {
if (win.__cf_chl_opt || win.__cfRLUnblockHandlers || win._cf) return true;
if (win.turnstile) {
if (doc.querySelector('.cf-turnstile, #turnstile-wrapper, .turnstile-container, #cf-challenge')) return true;
}
const cfSelectors = [
'#challenge-running', '#cf-please-wait', '.cf-browser-verification',
'#cf-content', '#cf-stage', '#cf-challenge', '.cf-challenge',
'#cf-error-details', '#cf-waiting', '.cf-please-wait',
'#cf-content-wrapper', '#challenge-form', '.challenge-form', '#cf-intercept'
];
for (const sel of cfSelectors) {
if (doc.querySelector(sel)) return true;
}
if (doc.querySelector('.ray-id, [data-ray-id], [data-cf-ray]')) return true;
const title = doc.title || '';
if (title.includes('Just a moment...') || title.includes('Attention Required!') ||
title.includes('Security Check') || title.includes('Verify you are human') ||
title.includes('Checking your browser')) {
return true;
}
const forms = doc.querySelectorAll('form[action]');
for (const form of forms) {
const action = form.action || '';
if (action.includes('__cf_chl') || action.includes('cf-challenge')) return true;
}
const scripts = doc.querySelectorAll('script[src]');
for (const script of scripts) {
const src = script.src || '';
if (src.includes('challenges.cloudflare.com') ||
src.includes('/cdn-cgi/challenge-platform/')) {
return true;
}
}
if (doc.querySelector('meta[name="cf-options"]')) return true;
const html = doc.documentElement.innerHTML || '';
if (html.includes('cf-browser-verification') ||
html.includes('cf-challenge') ||
html.includes('challenge-form') ||
html.includes('ray-id') ||
html.includes('cdn-cgi/challenge-platform')) {
return true;
}
} catch (e) { /* ignore */ }
return false;
}
function updateProtectionFlag() {
const was = isProtected;
isProtected = isProtectedPage();
featuresEnabled = isProtected ? !CACHE.universalProtect : true;
if (was !== isProtected) {
applyAllSettings();
requestUpdateStyles();
}
}
let protectionObserver = null;
function setupProtectionObserver() {
if (protectionObserver) return;
protectionObserver = new MutationObserver(() => {
const was = isProtected;
updateProtectionFlag();
if (was !== isProtected) {
applyAllSettings();
requestUpdateStyles();
}
});
protectionObserver.observe(doc.documentElement, {
childList: true,
subtree: true,
attributes: true,
attributeOldValue: false
});
let checks = 0;
const rapidInterval = setInterval(() => {
checks++;
const was = isProtected;
updateProtectionFlag();
if (was !== isProtected) {
applyAllSettings();
requestUpdateStyles();
}
if (checks >= 10) clearInterval(rapidInterval);
}, 1000);
if (!window.__hider_protection_poller) {
window.__hider_protection_poller = setInterval(() => {
const was = isProtected;
updateProtectionFlag();
if (was !== isProtected) {
applyAllSettings();
requestUpdateStyles();
}
}, 5000);
}
}
let scrollStyleEl = null;
let scrollInterval = null;
function forceEnableScroll() {
if (!CACHE.autoScroll) return;
if (isFeatureBlacklisted()) {
stopScrollDefeater();
return;
}
if (!featuresEnabled) return;
const html = doc.documentElement, body = doc.body;
if (!html && !body) return;
if (!scrollStyleEl) {
scrollStyleEl = doc.createElement('style');
scrollStyleEl.id = 'hider-force-scroll-style';
(doc.head || doc.documentElement)?.appendChild(scrollStyleEl);
}
scrollStyleEl.textContent = `
html, body {
overflow: auto !important;
overflow-x: auto !important;
overflow-y: auto !important;
position: static !important;
height: auto !important;
max-height: none !important;
touch-action: auto !important;
-webkit-overflow-scrolling: touch !important;
}
`;
}
function checkAndAutoUnblockScroll() {
if (!CACHE.autoScroll) return;
if (isFeatureBlacklisted()) {
stopScrollDefeater();
return;
}
if (!featuresEnabled) {
stopScrollDefeater();
return;
}
const html = doc.documentElement, body = doc.body;
if (!html || !body) return;
try {
const hStyle = win.getComputedStyle(html), bStyle = win.getComputedStyle(body);
if (
hStyle.overflow === 'hidden' || hStyle.overflowY === 'hidden' ||
bStyle.overflow === 'hidden' || bStyle.overflowY === 'hidden' ||
bStyle.position === 'fixed' || hStyle.position === 'fixed'
) {
forceEnableScroll();
}
} catch {}
}
function startScrollDefeater() {
if (isFeatureBlacklisted()) {
stopScrollDefeater();
return;
}
if (!featuresEnabled) {
stopScrollDefeater();
return;
}
if (scrollInterval) clearInterval(scrollInterval);
if (CACHE.autoScroll) {
forceEnableScroll();
scrollInterval = setInterval(checkAndAutoUnblockScroll, 2000);
}
}
function stopScrollDefeater() {
if (scrollInterval) {
clearInterval(scrollInterval);
scrollInterval = null;
}
if (scrollStyleEl) {
scrollStyleEl.remove();
scrollStyleEl = null;
}
}
let contextMenuStyleEl = null;
let contextMenuOverrideInstalled = false;
let origPreventDefault = null;
function updateContextMenuStyles() {
if (isFeatureBlacklisted()) {
if (contextMenuStyleEl) {
contextMenuStyleEl.remove();
contextMenuStyleEl = null;
}
if (contextMenuOverrideInstalled && origPreventDefault) {
Event.prototype.preventDefault = origPreventDefault;
contextMenuOverrideInstalled = false;
}
return;
}
if (!featuresEnabled) {
if (contextMenuStyleEl) {
contextMenuStyleEl.remove();
contextMenuStyleEl = null;
}
if (contextMenuOverrideInstalled && origPreventDefault) {
Event.prototype.preventDefault = origPreventDefault;
contextMenuOverrideInstalled = false;
}
return;
}
if (!CACHE.enableContextMenu) {
if (contextMenuStyleEl) {
contextMenuStyleEl.remove();
contextMenuStyleEl = null;
}
if (contextMenuOverrideInstalled && origPreventDefault) {
Event.prototype.preventDefault = origPreventDefault;
contextMenuOverrideInstalled = false;
}
return;
}
if (!contextMenuStyleEl) {
contextMenuStyleEl = doc.createElement('style');
contextMenuStyleEl.id = 'hider-contextmenu-style';
(doc.head || doc.documentElement)?.appendChild(contextMenuStyleEl);
}
contextMenuStyleEl.textContent = `
* {
-webkit-touch-callout: default !important;
-webkit-user-select: text !important;
user-select: text !important;
}
`;
if (!contextMenuOverrideInstalled) {
origPreventDefault = Event.prototype.preventDefault;
Event.prototype.preventDefault = function() {
if (CACHE.enableContextMenu && this.type === 'contextmenu') {
return;
}
return origPreventDefault.apply(this, arguments);
};
contextMenuOverrideInstalled = true;
}
}
const unblockEvents = ['contextmenu', 'selectstart', 'copy', 'paste', 'dragstart'];
const unblockHandler = e => {
if (!CACHE.enableContextMenu) return;
if (isFeatureBlacklisted()) return;
if (!featuresEnabled) return;
const path = e.composedPath ? e.composedPath() : [];
if (path.some(el => el.id === UI_HOST_ID || el.id === STEPPER_BAR_ID || el.closest && el.closest('#' + UI_HOST_ID))) {
return;
}
e.stopPropagation();
};
unblockEvents.forEach(evt => {
win.addEventListener(evt, unblockHandler, true);
});
let blurInterval = null;
let blurObserver = null;
let blurGlobalStyle = null;
function removeBlurFromElements() {
if (!CACHE.autoRemoveBlur) return;
if (isFeatureBlacklisted()) {
stopBlurRemoval();
return;
}
if (!featuresEnabled) {
stopBlurRemoval();
return;
}
const all = doc.querySelectorAll('*');
const uiHost = doc.getElementById(UI_HOST_ID);
for (const el of all) {
if (el === uiHost || el.closest && el.closest('#' + UI_HOST_ID)) continue;
if (el.classList && el.classList.contains('hider-stealth-target')) continue;
try {
const style = win.getComputedStyle(el);
if (style.filter && style.filter.includes('blur')) {
el.style.setProperty('filter', 'none', 'important');
el.style.setProperty('backdrop-filter', 'none', 'important');
el.style.setProperty('-webkit-backdrop-filter', 'none', 'important');
}
if (el.style.filter && el.style.filter.includes('blur')) {
el.style.setProperty('filter', 'none', 'important');
}
if (el.style.backdropFilter && el.style.backdropFilter.includes('blur')) {
el.style.setProperty('backdrop-filter', 'none', 'important');
el.style.setProperty('-webkit-backdrop-filter', 'none', 'important');
}
} catch {}
}
}
function scheduleBlurRemoval() {
if (isFeatureBlacklisted()) {
stopBlurRemoval();
return;
}
if (!featuresEnabled) {
stopBlurRemoval();
return;
}
if (CACHE.autoRemoveBlur && !blurGlobalStyle) {
blurGlobalStyle = doc.createElement('style');
blurGlobalStyle.id = 'hider-blur-global-style';
blurGlobalStyle.textContent = `
*:not(#hider-ui-root):not(#hider-ui-root *) {
filter: none !important;
backdrop-filter: none !important;
-webkit-backdrop-filter: none !important;
}
`;
(doc.head || doc.documentElement)?.appendChild(blurGlobalStyle);
} else if (!CACHE.autoRemoveBlur && blurGlobalStyle) {
blurGlobalStyle.remove();
blurGlobalStyle = null;
}
if (blurInterval) clearInterval(blurInterval);
if (CACHE.autoRemoveBlur) {
removeBlurFromElements();
blurInterval = setInterval(removeBlurFromElements, 2000);
if (!blurObserver) {
blurObserver = new MutationObserver(() => {
if (CACHE.autoRemoveBlur) {
removeBlurFromElements();
}
});
blurObserver.observe(doc.documentElement, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });
}
} else {
if (blurObserver) {
blurObserver.disconnect();
blurObserver = null;
}
}
}
function stopBlurRemoval() {
if (blurInterval) {
clearInterval(blurInterval);
blurInterval = null;
}
if (blurObserver) {
blurObserver.disconnect();
blurObserver = null;
}
if (blurGlobalStyle) {
blurGlobalStyle.remove();
blurGlobalStyle = null;
}
}
function applyAllSettings() {
if (isFeatureBlacklisted() && !blacklistToastShown) {
showToast('⚠️ Some Reveal features disabled on this domain');
blacklistToastShown = true;
}
if (CACHE.autoScroll) startScrollDefeater();
else stopScrollDefeater();
updateContextMenuStyles();
if (CACHE.autoRemoveBlur) scheduleBlurRemoval();
else stopBlurRemoval();
updateProtectionFlag();
}
// ---------- Sync Cache ----------
function syncCache() {
CACHE.blockedDomainsList = gv('hider_blocked_domains', []);
CACHE.blockedDomainsSet = new Set(CACHE.blockedDomainsList.map(cleanDomain).filter(Boolean));
CACHE.allowedDomainsList = gv('hider_allowed_domains', []);
CACHE.allowedDomainsSet = new Set(CACHE.allowedDomainsList.map(cleanDomain).filter(Boolean));
CACHE.customRules = gv('hider_custom_rules_v4', []);
CACHE.isFrozen = gv('hider_freeze_global', false);
isFrozen = CACHE.isFrozen;
CACHE.freezeMemory = gv('hider_freeze_memory', CACHE.freezeMemory);
CACHE.autoTimeSkipper = gv('hider_auto_time_skipper', false);
CACHE.autoScroll = gv('hider_auto_scroll', true);
CACHE.enableContextMenu = gv('hider_enable_contextmenu', true);
CACHE.autoRemoveBlur = gv('hider_auto_remove_blur', false);
// Protection is always ON – ignore any stored value
CACHE.universalProtect = true;
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());
}
applyAllSettings();
}
syncCache();
function disableFreezeMode() {
isFrozen = false; CACHE.isFrozen = false;
sv('hider_freeze_global', false);
shadowBy('btn-freeze')?.classList.remove('is-frozen');
broadcastState();
}
// ---------- Stealth Engine ----------
const styleProxyCache = new WeakMap();
try {
const origGetComputedStyle = win.getComputedStyle;
win.getComputedStyle = function(el, pseudo) {
const style = origGetComputedStyle.apply(this, arguments);
if (el && el instanceof Element && el.classList?.contains('hider-stealth-target')) {
let cachedProxy = styleProxyCache.get(style);
if (!cachedProxy) {
cachedProxy = new Proxy(style, {
get(target, prop) {
switch(prop) {
case 'display': return 'block';
case 'visibility': return 'visible';
case 'opacity': return '1';
case 'pointerEvents': return 'auto';
default:
const val = target[prop];
return typeof val === 'function' ? val.bind(target) : val;
}
}
});
styleProxyCache.set(style, cachedProxy);
}
return cachedProxy;
}
return style;
};
} catch {}
const isSameDomain = u => cleanDomain(u) && cleanDomain(u) === CURRENT_DOMAIN;
const isDomainBlocked = u => {
if (!u || !CACHE.blockedDomainsSet.size) return false;
const d = cleanDomain(u), lower = String(u).toLowerCase();
return d && (CACHE.blockedDomainsSet.has(d) || [...CACHE.blockedDomainsSet].some(b => b !== '*' && (d.endsWith('.' + b) || lower.includes(b))));
};
const isDomainAllowed = u => {
if (!u || !CACHE.allowedDomainsSet.size) return false;
const d = cleanDomain(u), lower = String(u).toLowerCase();
return d && (CACHE.allowedDomainsSet.has('*') || CACHE.allowedDomainsSet.has(d) || [...CACHE.allowedDomainsSet].some(a => a !== '*' && (d.endsWith('.' + a) || lower.includes(a))));
};
function convertToWildcardSelector(sel) {
if (!sel || typeof sel !== 'string') return '';
return sel.replace(/(#|\.)([a-zA-Z0-9_-]+)/g, (m, p, name) => {
if (name.startsWith('hider-')) return '';
const base = (name.match(/^([a-zA-Z_-]+?)[0-9a-fA-F_-]{3,}$/)?.[1] || name.replace(/[0-9a-fA-F]{4,}$/, '')).replace(/[-_]+$/, '');
return base ? (p === '#' ? `[id*="${base}"]` : `[class*="${base}"]`) : '';
}).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);
}
// ---------- Freeze FX ----------
function triggerFreezeFx() {
const fx = doc.createElement('div');
fx.className = 'hider-freeze-snow-overlay';
const frag = doc.createDocumentFragment();
const flakeChars = ['❄', '❅', '❆', '✨', '⚡'];
for (let i = 0; i < 22; i++) {
const flake = doc.createElement('div');
flake.className = 'hider-snow-flake';
flake.textContent = flakeChars[Math.floor(Math.random() * flakeChars.length)];
flake.style.cssText = `left:${Math.random() * 98}vw; top:${Math.random() * 60}vh; animation-delay:${Math.random() * 0.35}s; font-size:${14 + Math.random() * 18}px;`;
frag.appendChild(flake);
}
fx.appendChild(frag);
(doc.body || doc.documentElement)?.appendChild(fx);
setTimeout(() => fx.remove(), 1450);
}
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) {
if (isDomainBlocked(url)) {
logBlockedAttempt(url, triggerType + ' (Auto-Block Domain)');
onDeny?.(); simulateAdWindowSuccess(); return;
}
if (isDomainAllowed(url)) {
userApprovedNavigation = true; onConfirm?.();
setTimeout(() => userApprovedNavigation = false, 300); return;
}
const mem = CACHE.freezeMemory || 'ask';
if (mem === 'allow_all') {
disableFreezeMode(); userApprovedNavigation = true; onConfirm?.();
setTimeout(() => userApprovedNavigation = false, 300); return;
}
if (mem === 'block_all') {
logBlockedAttempt(url, triggerType + ' (Auto-Block)');
onDeny?.(); simulateAdWindowSuccess(); return;
}
if (mem === 'allow_same' && isSameDomain(url)) {
const domain = getParentDomain(url) || cleanDomain(url);
if (domain) executeAddAllowedDomain(domain);
userApprovedNavigation = true; onConfirm?.();
setTimeout(() => userApprovedNavigation = false, 300); return;
}
showFreezePrompt(url, triggerType, onConfirm, onDeny);
}
// ---------- Interceptors ----------
let interceptorsInstalled = false;
function installInterceptors() {
if (interceptorsInstalled) return;
interceptorsInstalled = true;
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');
const checkAndInterceptNav = (u, origFn, contextThis, triggerName) => {
if (!featuresEnabled) {
return origFn.call(contextThis, u);
}
if (isDomainBlocked(u)) {
logBlockedAttempt(u, `Blocked ${triggerName}`);
showToast(`⛔ Blocked ${triggerName}`); simulateAdWindowSuccess(); return;
}
if (isFrozen && !userApprovedNavigation) {
if (isDomainAllowed(u)) {
userApprovedNavigation = true; const res = origFn.call(contextThis, u);
setTimeout(() => userApprovedNavigation = false, 300); return res;
}
handleFreezeNavigation(u, triggerName, () => {
userApprovedNavigation = true; origFn.call(contextThis, u);
setTimeout(() => userApprovedNavigation = false, 300);
}, simulateAdWindowSuccess);
return;
}
return origFn.call(contextThis, u);
};
if (origA) lp.assign = function(u) { return checkAndInterceptNav(u, origA, this, 'Redirect (assign)'); };
if (origR) lp.replace = function(u) { return checkAndInterceptNav(u, origR, this, 'Redirect (replace)'); };
if (hrefDesc?.set) {
try {
Object.defineProperty(lp, 'href', {
set(u) { checkAndInterceptNav(u, hrefDesc.set, this, 'Redirect (href)'); },
get() { return hrefDesc.get.call(this); }
});
} catch {}
}
}
const origClick = HTMLAnchorElement.prototype.click;
HTMLAnchorElement.prototype.click = function() {
if (!featuresEnabled) return origClick.apply(this, arguments);
if (isDomainBlocked(this.href)) { logBlockedAttempt(this.href, 'Blocked Click'); showToast('⛔ Blocked link click'); simulateAdWindowSuccess(); return; }
if (isFrozen && !userApprovedNavigation) {
if (isDomainAllowed(this.href)) {
userApprovedNavigation = true; const res = origClick.apply(this, arguments);
setTimeout(() => userApprovedNavigation = false, 300); return res;
}
handleFreezeNavigation(this.href, 'Anchor Click', () => {
userApprovedNavigation = true; origClick.apply(this, arguments);
setTimeout(() => userApprovedNavigation = false, 300);
}, simulateAdWindowSuccess);
return;
}
return origClick.apply(this, arguments);
};
const origOpen = win.open;
win.open = function(url, target, features) {
if (!featuresEnabled) return origOpen.apply(win, arguments);
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) {
if (isDomainAllowed(url)) {
userApprovedNavigation = true; const res = origOpen.apply(win, arguments);
setTimeout(() => userApprovedNavigation = false, 300); return res;
}
handleFreezeNavigation(url, 'win.open()', () => {
userApprovedNavigation = true; origOpen.call(win, url, target, features);
setTimeout(() => userApprovedNavigation = false, 300);
}, () => {});
return createDummyWindow();
}
return origOpen.apply(win, arguments);
};
} catch {}
}
// ---------- Broadcast / Sync ----------
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);
CACHE.isFrozen = isFrozen;
syncCache(); requestUpdateStyles();
shadowBy('btn-select')?.classList.toggle('active', isSelecting);
shadowBy('btn-scope')?.classList.toggle('active', currentScope === 'link');
shadowBy('btn-freeze')?.classList.toggle('is-frozen', isFrozen);
if (!isSelecting) clearSelectionState();
applyDockButtonVisibility();
}
if (e.data.type === 'HIDER_RESUME_VIDEOS') triggerVideoResumeChain();
if (e.data.type === 'HIDER_SKIP_30') skip30Seconds();
});
window.addEventListener('storage', e => {
if (e.key && e.key.startsWith('hider_')) {
syncCache(); requestUpdateStyles();
if (isTop) broadcastState();
applyDockButtonVisibility();
}
});
if (typeof GM_addValueChangeListener !== 'undefined') {
['hider_freeze_global', 'hider_freeze_memory', 'hider_blocked_domains', 'hider_allowed_domains', 'hider_custom_rules_v4', 'hider_auto_time_skipper',
'hider_auto_scroll', 'hider_enable_contextmenu', 'hider_auto_remove_blur', 'hider_hidden_dock_buttons'
].forEach(key => {
try { GM_addValueChangeListener(key, () => { syncCache(); requestUpdateStyles(); if (isTop) broadcastState(); applyDockButtonVisibility(); }); } catch {}
});
}
if (!isTop) try { window.top.postMessage({ type: 'HIDER_REQUEST_STATE' }, '*'); } catch {}
// ---------- Style Update ----------
function requestUpdateStyles() {
if (styleUpdateRAF) return;
styleUpdateRAF = requestAnimationFrame(() => {
styleUpdateRAF = null;
updateStyles();
});
}
function updateStyles() {
let el = doc.getElementById('hider-dynamic-styles');
if (!el) { el = doc.createElement('style'); el.id = 'hider-dynamic-styles'; (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?.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);
const hideCss = (featuresEnabled && combined.length) ? `${combined.join(',')}{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}` : '';
const baseAnimationCss = `
.hider-preview-highlight { outline: none !important; border: 2px solid #38bdf8 !important; background: rgba(56, 189, 248, 0.15) !important; box-shadow: 0 0 24px rgba(56, 189, 248, 0.6), inset 0 0 12px rgba(56, 189, 248, 0.3) !important; transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1) !important; position: relative !important; z-index: 2147483640 !important; cursor: crosshair !important; border-radius: 6px !important; }
.hider-freeze-snow-overlay { position: fixed !important; top: 0 !important; left: 0 !important; width: 100vw !important; height: 100vh !important; pointer-events: none !important; z-index: 2147483647 !important; overflow: hidden !important; background: radial-gradient(circle at center, rgba(56, 189, 248, 0.18) 0%, transparent 70%) !important; animation: hiderSnowOverlayFade 1.4s cubic-bezier(0.16, 1, 0.3, 1) forwards !important; }
@keyframes hiderSnowOverlayFade { 0% { opacity: 0; backdrop-filter: blur(0px); } 30% { opacity: 1; backdrop-filter: blur(3px); } 80% { opacity: 1; backdrop-filter: blur(3px); } 100% { opacity: 0; backdrop-filter: blur(0px); } }
.hider-snow-flake { position: absolute !important; color: #e0f2fe !important; user-select: none !important; pointer-events: none !important; opacity: 0.95 !important; animation: hiderSnowFall 1.3s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards !important; filter: drop-shadow(0 0 8px rgba(56, 189, 248, 0.9)) !important; }
@keyframes hiderSnowFall { 0% { transform: translateY(-25px) scale(0.5) rotate(0deg); opacity: 0; } 20% { opacity: 1; } 100% { transform: translateY(140px) scale(1.15) rotate(180deg); opacity: 0; } }`;
const css = baseAnimationCss + '\n' + hideCss;
if (cachedCssString !== css) {
cachedCssString = css; el.textContent = css;
if (featuresEnabled) {
combined.forEach(s => {
try { doc.querySelectorAll(s).forEach(targetEl => targetEl.classList.add('hider-stealth-target')); } catch {}
});
}
}
if (CACHE.autoScroll && featuresEnabled) checkAndAutoUnblockScroll();
}
updateStyles();
// ---------- Selector Helpers ----------
function safeCSSEscape(str) { return (typeof CSS !== 'undefined' && typeof CSS.escape === 'function') ? CSS.escape(str) : str.replace(/([!"#$%&'()*+,.\/:;<=>?@[\\\]^`{|}~])/g, '\\$1'); }
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 < 18) { path.unshift(`#${safeCSSEscape(curr.id)}`); break; }
const rawC = typeof curr.className === 'string' ? curr.className : curr.getAttribute('class') || '';
const 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 += `.${safeCSSEscape(classes[0])}` + (classes[1] ? `.${safeCSSEscape(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(' > ');
}
// ---------- Drag Listeners ----------
function attachDragListeners(barEl, handleEl) {
if (!handleEl) return;
let startX, startY, initialX, initialY;
const onMove = e => {
if (!isDraggingStepper) return;
if (e.cancelable) e.preventDefault();
const p = e.touches ? e.touches[0] : e, maxX = win.innerWidth - barEl.offsetWidth, maxY = win.innerHeight - barEl.offsetHeight;
stepperPos = { x: Math.max(0, Math.min(maxX, initialX + (p.clientX - startX))), y: Math.max(0, Math.min(maxY, initialY + (p.clientY - startY))) };
barEl.style.setProperty('left', `${stepperPos.x}px`, 'important'); barEl.style.setProperty('top', `${stepperPos.y}px`, 'important');
barEl.style.setProperty('right', 'auto', 'important'); barEl.style.setProperty('bottom', 'auto', 'important'); barEl.style.setProperty('transform', 'none', 'important');
};
const onEnd = () => {
if (isDraggingStepper) { isDraggingStepper = false; }
win.removeEventListener('mousemove', onMove); win.removeEventListener('mouseup', onEnd);
win.removeEventListener('touchmove', onMove); win.removeEventListener('touchend', onEnd); win.removeEventListener('touchcancel', onEnd);
};
const onStart = e => {
if (e.target.tagName === 'BUTTON') return;
if (e.cancelable) e.preventDefault();
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.setProperty('right', 'auto', 'important');
barEl.style.setProperty('bottom', 'auto', 'important');
barEl.style.setProperty('transform', 'none', 'important');
barEl.style.setProperty('margin', '0', 'important');
barEl.style.setProperty('left', `${initialX}px`, 'important');
barEl.style.setProperty('top', `${initialY}px`, 'important');
win.addEventListener('mousemove', onMove, { passive: false }); win.addEventListener('mouseup', onEnd);
win.addEventListener('touchmove', onMove, { passive: false }); win.addEventListener('touchend', onEnd); win.addEventListener('touchcancel', onEnd);
};
handleEl.addEventListener('mousedown', onStart, { passive: false });
handleEl.addEventListener('touchstart', onStart, { passive: false });
}
function clearSelectionState() {
previewElement?.classList.remove('hider-preview-highlight'); previewElement = null; stepperStack = [];
const stepper = shadowBy(STEPPER_BAR_ID);
if (stepper) {
stepper.remove();
}
}
function turnOffHideMode() {
if (isSelecting) {
isSelecting = false; shadowBy('btn-select')?.classList.remove('active');
clearSelectionState(); broadcastState();
showToast('🎯 Selection mode OFF');
}
}
function closeAllMenus(e, force = false) {
if (!shadowRoot) return false;
if (!force) {
if (isSelecting || isDraggingDock || isDraggingStepper) return false;
const active = shadowRoot.activeElement || doc.activeElement;
if (active && (active.tagName === 'INPUT' || active.tagName === 'TEXTAREA')) return false;
const path = e?.composedPath?.() || [];
if (path.length > 0 && path.some(el => el.id === UI_HOST_ID || el.id === STEPPER_BAR_ID)) return false;
if (shadowBy('hider-panel')?.querySelector('.is-editing')) return false;
}
const panel = shadowBy('hider-panel'), menu = shadowBy('hider-dock-menu'), mainBtn = shadowBy('btn-toggle-dock'), dockEl = shadowBy('hider-main-dock');
let closed = false;
shadowRoot.querySelectorAll('.h-custom-select').forEach(c => c.classList.remove('is-open'));
if (linkPanelEl && linkPanelEl.classList.contains('is-visible')) {
linkPanelEl.classList.remove('is-visible');
setTimeout(() => linkPanelEl.style.display = 'none', 300);
closed = true;
}
if (panel && panel.classList.contains('is-visible')) {
panel.classList.remove('is-visible');
setTimeout(()=> { if(!panel.classList.contains('is-visible')) panel.style.display='none'; }, 300);
shadowBy('btn-manage')?.classList.remove('active');
closed = true;
}
if (menu && menu.classList.contains('is-open')) {
menu.classList.remove('is-open'); 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);
closed = true;
}
return closed;
}
// ---------- Stepper UI ----------
function renderTouchStepperUI() {
if (!shadowRoot) return;
let stepper = shadowBy(STEPPER_BAR_ID);
if (!previewElement) { stepper?.remove(); return; }
if (!stepper) {
stepper = doc.createElement('div'); stepper.id = STEPPER_BAR_ID;
shadowRoot.appendChild(stepper);
}
stepper.className = 'h-glass h-stepper-pill';
if (stepperPos.x !== null && stepperPos.y !== null) {
stepper.style.setProperty('left', `${stepperPos.x}px`, 'important');
stepper.style.setProperty('top', `${stepperPos.y}px`, 'important');
stepper.style.setProperty('bottom', 'auto', 'important');
stepper.style.setProperty('right', 'auto', 'important');
stepper.style.setProperty('transform', 'none', 'important');
stepper.style.setProperty('margin', '0', 'important');
} else {
stepper.style.setProperty('left', '50%', 'important');
stepper.style.setProperty('top', 'auto', 'important');
stepper.style.setProperty('bottom', '24px', 'important');
stepper.style.setProperty('right', 'auto', 'important');
stepper.style.setProperty('transform', 'translateX(-50%)', 'important');
stepper.style.setProperty('margin', '0', 'important');
}
const tag = previewElement.tagName.toLowerCase(), c = typeof previewElement.className === 'string' ? previewElement.className.trim().split(/\s+/)[0] : '';
const classStr = c && !c.startsWith('hider-') ? `.${c}` : '', idStr = previewElement.id && !previewElement.id.startsWith('hider-') ? `#${previewElement.id}` : '';
const fullText = `${tag}${idStr}${classStr}`;
const isLongText = fullText.length > 14;
stepper.innerHTML = `
<div id="hider-drag-handle" class="h-drag-dots" title="Drag Selector Bar">⠿</div>
<div class="h-stepper-row">
<button id="hider-step-up" class="h-btn-icon" title="Select Parent Element">▲</button>
<button id="hider-step-down" class="h-btn-icon" title="Select Child Element">▼</button>
</div>
<div class="h-tag-badge-box" title="${fullText}">
<span class="h-tag-badge-text ${isLongText ? 'is-animating' : ''}">${fullText}</span>
</div>
<button id="hider-step-confirm" class="h-btn-pill btn-blue">🙈 Hide</button>
<button id="hider-step-cancel" class="h-btn-pill btn-gray">✖ Cancel</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();
showToast('❌ Selection cancelled');
};
}
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); }
}
requestUpdateStyles(); showToast('🙈 Element Hidden!'); clearSelectionState();
}
// ---------- Toast ----------
function showToast(msg) {
let targetRoot = shadowRoot;
if (!targetRoot) targetRoot = doc.body;
if (!targetRoot) return;
const existing = targetRoot.querySelector('#hider-toast');
if (existing) existing.remove();
const toast = doc.createElement('div');
toast.id = 'hider-toast';
toast.className = 'h-glass h-toast';
toast.textContent = msg;
Object.assign(toast.style, {
position: 'fixed',
bottom: '70px',
left: '50%',
transform: 'translateX(-50%) translateY(16px)',
opacity: '0',
padding: '6px 16px',
fontSize: '11px',
fontWeight: '700',
zIndex: '2147483647',
pointerEvents: 'none',
transition: 'all .3s ease',
textAlign: 'center',
borderRadius: '20px',
border: '1px solid rgba(56,189,248,.5)',
color: '#e0f2fe',
background: 'rgba(13,18,30,0.95)',
backdropFilter: 'blur(12px)',
WebkitBackdropFilter: 'blur(12px)',
boxShadow: '0 12px 30px rgba(0,0,0,0.6)',
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Inter, sans-serif',
lineHeight: '1.3',
maxWidth: '90vw',
whiteSpace: 'nowrap'
});
targetRoot.appendChild(toast);
requestAnimationFrame(() => {
toast.style.opacity = '1';
toast.style.transform = 'translateX(-50%) translateY(0)';
});
setTimeout(() => {
toast.style.opacity = '0';
toast.style.transform = 'translateX(-50%) translateY(12px)';
setTimeout(() => {
if (toast.parentNode) toast.remove();
}, 400);
}, 2500);
}
// ---------- Clipboard ----------
function copyToClipboard(text) {
if (!text) return;
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(text).then(() => {
showToast('📋 Copied!');
}).catch(() => {
fallbackCopy(text);
});
} else {
fallbackCopy(text);
}
}
function fallbackCopy(text) {
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.style.position = 'fixed';
textarea.style.opacity = '0';
textarea.style.left = '-9999px';
textarea.style.top = '-9999px';
textarea.style.width = '1px';
textarea.style.height = '1px';
document.body.appendChild(textarea);
textarea.select();
try {
document.execCommand('copy');
showToast('📋 Copied!');
} catch (err) {
showToast('❌ Copy failed');
}
document.body.removeChild(textarea);
}
// ---------- Custom Dropdown ----------
function setupCustomDropdown(container, initialValue, onChangeCallback) {
if (!container) return;
const trigger = container.querySelector('.h-custom-trigger'), textSpan = container.querySelector('.h-custom-value-text'), options = container.querySelectorAll('.h-custom-opt');
let currentVal = initialValue || 'ask'; textSpan.textContent = FREEZE_LABELS[currentVal] || FREEZE_LABELS['ask'];
options.forEach(opt => {
opt.classList.toggle('is-selected', opt.getAttribute('data-val') === currentVal);
opt.onclick = e => {
e.stopPropagation(); currentVal = opt.getAttribute('data-val');
textSpan.textContent = FREEZE_LABELS[currentVal] || opt.textContent.trim();
options.forEach(o => o.classList.toggle('is-selected', o === opt)); container.classList.remove('is-open'); onChangeCallback?.(currentVal);
};
});
trigger.onclick = e => {
e.stopPropagation(); const isOpen = container.classList.contains('is-open');
shadowRoot.querySelectorAll('.h-custom-select').forEach(c => c.classList.remove('is-open'));
if (!isOpen) container.classList.add('is-open');
};
}
// ---------- Domain Management ----------
function executeAddBlockDomain(parentDomain, triggerType, onDeny, promptEl) {
if (parentDomain && !CACHE.blockedDomainsSet.has(parentDomain)) {
CACHE.blockedDomainsList.push(parentDomain); CACHE.blockedDomainsSet.add(parentDomain);
sv('hider_blocked_domains', CACHE.blockedDomainsList);
}
logBlockedAttempt(parentDomain, triggerType + ' (User Domain Block)'); promptEl?.remove();
showToast(`🚫 Blocked ${parentDomain}! Managed in Control Panel.`); onDeny?.(); simulateAdWindowSuccess();
}
function executeAddAllowedDomain(parentDomain) {
if (parentDomain && !CACHE.allowedDomainsSet.has(parentDomain)) {
CACHE.allowedDomainsList.push(parentDomain); CACHE.allowedDomainsSet.add(parentDomain);
sv('hider_allowed_domains', CACHE.allowedDomainsList);
if (shadowBy('hider-panel')?.classList.contains('is-visible')) renderList();
}
}
function showFreezePrompt(url, triggerType, onConfirm, onDeny) {
if (!shadowRoot) return;
shadowBy('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';
const parentDomain = getParentDomain(url) || 'unknown domain';
promptEl.innerHTML = `
<div style="font-weight:800;color:#38bdf8;font-size:11px!important;text-transform:uppercase!important;letter-spacing:0.5px">❄️ Navigation Intercepted</div>
<div style="font-size:11px!important;color:#f8fafc!important;font-weight:600">Proceed to page?</div>
<div class="h-url-box">${displayUrl}</div>
<div class="h-custom-select" id="hider-modal-custom-dropdown">
<div class="h-custom-trigger"><span class="h-custom-value-text">${FREEZE_LABELS[CACHE.freezeMemory] || FREEZE_LABELS['ask']}</span><span class="h-custom-arrow">▼</span></div>
<div class="h-custom-options"><div class="h-custom-opt" data-val="ask">❓ Ask Every Time</div><div class="h-custom-opt" data-val="block_all">⛔ Auto-Block All Navigations</div><div class="h-custom-opt" data-val="allow_same">🔗 Allow Same Domain Only</div><div class="h-custom-opt" data-val="allow_all">🟢 Allow All Navigations</div></div>
</div>
<div style="display:flex;gap:6px;width:100%;margin-top:4px"><button id="hider-freeze-yes" class="h-btn-pill btn-green" style="flex:1">Allow Once</button><button id="hider-freeze-no" class="h-btn-pill btn-red" style="flex:1">Deny</button></div>
<button id="hider-freeze-allow-btn" class="h-btn-pill" style="width:100%;margin-top:4px;background:rgba(16,185,129,0.15)!important;border:1px solid rgba(16,185,129,0.4)!important;color:#34d399!important;">🟢 Always Allow (${parentDomain})</button>
<button id="hider-freeze-block-btn" class="h-btn-pill" style="width:100%;margin-top:4px;background:rgba(239,68,68,0.15)!important;border:1px solid rgba(239,68,68,0.4)!important;color:#fca5a5!important;">🚫 Always Block (${parentDomain})</button>
`;
shadowRoot.appendChild(promptEl); let selectedMem = CACHE.freezeMemory || 'ask';
setupCustomDropdown(promptEl.querySelector('#hider-modal-custom-dropdown'), selectedMem, v => { selectedMem = v; });
setTimeout(() => { promptEl.style.opacity = '1'; promptEl.style.transform = 'translateX(-50%) translateY(0)'; }, 10);
const saveMem = () => { if (selectedMem !== CACHE.freezeMemory) { CACHE.freezeMemory = selectedMem; sv('hider_freeze_memory', selectedMem); } };
promptEl.querySelector('#hider-freeze-yes').onclick = () => {
saveMem(); if (selectedMem === 'allow_all') disableFreezeMode(); else if (selectedMem === 'allow_same' && parentDomain !== 'unknown domain') executeAddAllowedDomain(parentDomain);
promptEl.remove(); onConfirm?.();
};
promptEl.querySelector('#hider-freeze-no').onclick = () => {
saveMem(); if (selectedMem === 'allow_all') disableFreezeMode();
logBlockedAttempt(url, triggerType + ' (User)'); promptEl.remove(); onDeny?.(); simulateAdWindowSuccess();
};
promptEl.querySelector('#hider-freeze-allow-btn').onclick = () => {
saveMem(); if (parentDomain !== 'unknown domain') executeAddAllowedDomain(parentDomain);
if (selectedMem === 'allow_all') disableFreezeMode();
promptEl.remove(); showToast(`🟢 Allowed ${parentDomain}!`); onConfirm?.();
};
promptEl.querySelector('#hider-freeze-block-btn').onclick = () => {
saveMem(); const skipConfirm = gv('hider_skip_block_confirm', false);
if (skipConfirm) { executeAddBlockDomain(parentDomain, triggerType, onDeny, promptEl); return; }
promptEl.innerHTML = `
<div style="font-weight:800;color:#ef4444;font-size:11px!important;text-transform:uppercase!important;">🚫 Confirm Domain Block</div>
<div style="font-size:11px!important;color:#f8fafc!important;text-align:center;margin:2px 0">Block all future requests to:<br><strong style="color:#38bdf8;font-size:12px!important">${parentDomain}</strong>?</div>
<label style="font-size:10px!important;color:#cbd5e1!important;display:flex;align-items:center;gap:4px;cursor:pointer;margin:4px 0;user-select:none"><input type="checkbox" id="hider-dont-ask-block" style="cursor:pointer;accent-color:#38bdf8;width:12px!important;height:12px!important"> Don't ask confirmation again</label>
<div style="display:flex;gap:6px;width:100%;margin-top:4px"><button id="hider-confirm-block-yes" class="h-btn-pill btn-red" style="flex:1">Yes, Block</button><button id="hider-confirm-block-no" class="h-btn-pill" style="flex:1;background:rgba(255,255,255,0.1)!important;color:#f1f5f9!important;border:1px solid rgba(255,255,255,0.15)!important;">Cancel</button></div>
`;
promptEl.querySelector('#hider-confirm-block-yes').onclick = () => {
if (promptEl.querySelector('#hider-dont-ask-block')?.checked) sv('hider_skip_block_confirm', true);
executeAddBlockDomain(parentDomain, triggerType, onDeny, promptEl);
};
promptEl.querySelector('#hider-confirm-block-no').onclick = () => { showFreezePrompt(url, triggerType, onConfirm, onDeny); };
};
}
// ---------- Enhanced Reveal (Quick) ----------
function revealHiddenElements() {
if (!doc.body) return;
const isHiddenByUs = el =>
el.id === UI_HOST_ID ||
el.closest('#' + UI_HOST_ID) ||
el.classList.contains('hider-stealth-target');
const targets = new Set();
const inlineHidden = doc.querySelectorAll([
'[style*="display:none"]',
'[style*="display: none"]',
'[style*="visibility:hidden"]',
'[style*="visibility: hidden"]',
'[style*="opacity:0"]',
'[style*="opacity: 0"]',
'[style*="filter:blur"]',
'[style*="filter: blur"]',
'[style*="backdrop-filter:blur"]',
'[style*="backdrop-filter: blur"]'
].join(','));
inlineHidden.forEach(el => targets.add(el));
const overlaySelector = [
'div[style*="position:fixed"]',
'div[style*="position: fixed"]',
'div[style*="position:absolute"]',
'div[style*="position: absolute"]',
'div[style*="z-index: 999"]',
'div[style*="z-index:999"]',
'div[style*="z-index: 9999"]',
'div[style*="z-index:9999"]',
'div[style*="z-index: 99999"]',
'div[style*="z-index:99999"]'
].join(',');
const overlayCandidates = doc.querySelectorAll(overlaySelector);
overlayCandidates.forEach(el => {
const rect = el.getBoundingClientRect();
if (rect.width < 100 || rect.height < 100) return;
if (el.innerText.length > 200) return;
if (el.querySelector('article, main, p, h1, h2, h3, h4, h5, h6')) return;
targets.add(el);
});
doc.querySelectorAll([
'.modal', '.overlay', '.popup', '.lightbox', '.blocker',
'.paywall', '.gate', '.wall', '.restricted', '.locked',
'[data-overlay]', '[data-modal]', '[data-popup]', '[data-paywall]'
].join(',')).forEach(el => {
if (!isHiddenByUs(el)) targets.add(el);
});
doc.querySelectorAll('*').forEach(el => {
if (isHiddenByUs(el)) return;
const style = win.getComputedStyle(el);
if (style.filter && style.filter.includes('blur')) {
targets.add(el);
}
if (style.backdropFilter && style.backdropFilter.includes('blur')) {
targets.add(el);
}
if (style.display === 'none' || style.visibility === 'hidden' || style.opacity === '0') {
targets.add(el);
}
});
let count = 0;
let blurCount = 0, hiddenCount = 0, overlayCount = 0;
const processBatch = (arr, start) => {
const end = Math.min(start + 50, arr.length);
for (let i = start; i < end; i++) {
const el = arr[i];
if (isHiddenByUs(el)) continue;
const style = win.getComputedStyle(el);
let changed = false;
if (style.filter && style.filter.includes('blur')) {
el.style.setProperty('filter', 'none', 'important');
el.style.setProperty('backdrop-filter', 'none', 'important');
el.style.setProperty('-webkit-backdrop-filter', 'none', 'important');
blurCount++;
changed = true;
}
if (style.display === 'none') {
el.style.setProperty('display', 'block', 'important');
hiddenCount++;
changed = true;
}
if (style.visibility === 'hidden') {
el.style.setProperty('visibility', 'visible', 'important');
hiddenCount++;
changed = true;
}
if (style.opacity === '0') {
el.style.setProperty('opacity', '1', 'important');
hiddenCount++;
changed = true;
}
if (style.pointerEvents === 'none') {
el.style.setProperty('pointer-events', 'auto', 'important');
}
if (style.userSelect === 'none') {
el.style.setProperty('user-select', 'text', 'important');
el.style.setProperty('-webkit-user-select', 'text', 'important');
}
if (style.position === 'fixed' || style.position === 'absolute') {
const z = parseInt(style.zIndex, 10);
if (z > 900) {
el.style.setProperty('position', 'static', 'important');
el.style.setProperty('z-index', 'auto', 'important');
overlayCount++;
changed = true;
}
}
if (changed) count++;
}
if (end < arr.length) {
requestAnimationFrame(() => processBatch(arr, end));
} else {
const parts = [];
if (hiddenCount) parts.push(`${hiddenCount} hidden`);
if (blurCount) parts.push(`${blurCount} blurred`);
if (overlayCount) parts.push(`${overlayCount} overlays`);
const summary = parts.length ? `👁️ Revealed: ${parts.join(', ')}` : '👁️ No hidden/blurred elements found.';
showToast(summary);
if (CACHE.autoScroll && featuresEnabled) forceEnableScroll();
}
};
const targetArray = Array.from(targets);
if (targetArray.length === 0) {
showToast('👁️ No hidden/blurred elements found.');
return;
}
requestAnimationFrame(() => processBatch(targetArray, 0));
}
// ---------- Time Skipper ----------
function skip30Seconds() {
let mediaCount = 0;
doc.querySelectorAll('video, audio').forEach(el => {
try {
if (!isNaN(el.duration) && isFinite(el.duration)) {
el.currentTime = Math.min(el.duration, el.currentTime + 30);
} else {
el.currentTime += 30;
}
mediaCount++;
} catch {}
});
let timerCount = 0;
const timerElements = doc.querySelectorAll(
'[class*="timer"], [class*="countdown"], [id*="timer"], [id*="countdown"], ' +
'[class*="time"], [id*="time"], [class*="remaining"], [id*="remaining"]'
);
timerElements.forEach(el => {
const text = el.textContent.trim();
if (/^\d{1,2}:\d{2}$/.test(text) || /^\d+\s*(s|sec|seconds?)$/i.test(text) || /^\d{1,2}:\d{2}\s*$/.test(text)) {
el.textContent = '0';
el.dispatchEvent(new Event('input', { bubbles: true }));
timerCount++;
}
});
doc.querySelectorAll('*:not([class*="timer"]):not([id*="timer"]):not([class*="countdown"]):not([id*="countdown"])')
.forEach(el => {
const text = el.textContent.trim();
if (/^(?:[0-9]{1,2}:[0-5][0-9]|[1-9][0-9]?s?)$/i.test(text)) {
el.textContent = '0';
el.dispatchEvent(new Event('input', { bubbles: true }));
timerCount++;
}
});
let clickCount = 0;
const skipSelectors = [
'[class*="skip"]', '[id*="skip"]',
'[class*="close"]', '[id*="close"]',
'[class*="dismiss"]', '[id*="dismiss"]',
'button:contains("Skip")', 'button:contains("Close")',
'a:contains("Skip")', 'a:contains("Close")'
];
skipSelectors.forEach(sel => {
try {
doc.querySelectorAll(sel).forEach(btn => {
if (btn.offsetParent !== null && !btn.closest('#' + UI_HOST_ID)) {
btn.click();
clickCount++;
}
});
} catch {}
});
broadcastToFrames(window, { type: 'HIDER_SKIP_30' });
const msgParts = [];
if (mediaCount) msgParts.push(`${mediaCount} media`);
if (timerCount) msgParts.push(`${timerCount} timers`);
if (clickCount) msgParts.push(`${clickCount} buttons`);
const summary = msgParts.length ? `⏩ Skipped: ${msgParts.join(', ')}` : '⏩ Skipped +30s';
showToast(summary);
}
function autoSkipTimers() {
if (!CACHE.autoTimeSkipper) return;
if (!featuresEnabled) return;
let timerCount = 0, clickCount = 0;
const timerSelectors = [
'[class*="timer"]', '[class*="countdown"]', '[id*="timer"]', '[id*="countdown"]',
'[class*="time"]', '[id*="time"]', '[class*="remaining"]', '[id*="remaining"]'
];
timerSelectors.forEach(sel => {
doc.querySelectorAll(sel).forEach(el => {
const text = el.textContent.trim();
if (/^\d{1,2}:\d{2}$/.test(text) || /^\d+\s*(s|sec|seconds?)$/i.test(text) || /^\d{1,2}:\d{2}\s*$/.test(text)) {
el.textContent = '0';
el.dispatchEvent(new Event('input', { bubbles: true }));
timerCount++;
}
});
});
doc.querySelectorAll('*:not([class*="timer"]):not([id*="timer"]):not([class*="countdown"]):not([id*="countdown"])')
.forEach(el => {
const text = el.textContent.trim();
if (/^(?:[0-9]{1,2}:[0-5][0-9]|[1-9][0-9]?s?)$/i.test(text)) {
el.textContent = '0';
el.dispatchEvent(new Event('input', { bubbles: true }));
timerCount++;
}
});
const skipSelectors = [
'[class*="skip"]', '[id*="skip"]',
'[class*="close"]', '[id*="close"]',
'[class*="dismiss"]', '[id*="dismiss"]',
'button:contains("Skip")', 'button:contains("Close")',
'a:contains("Skip")', 'a:contains("Close")'
];
skipSelectors.forEach(sel => {
try {
doc.querySelectorAll(sel).forEach(btn => {
if (btn.offsetParent !== null && !btn.closest('#' + UI_HOST_ID)) {
btn.click();
clickCount++;
}
});
} catch {}
});
if (timerCount || clickCount) {
showToast(`⏩ Auto-skipped: ${timerCount} timers, ${clickCount} buttons`);
}
}
// ========== AGGRESSIVE PAUSE ==========
function pauseAllVideos() {
function pauseMedia(el) {
try {
if (el.tagName === 'VIDEO' || el.tagName === 'AUDIO') {
el.pause();
el.currentTime = 0;
el.loop = false;
}
} catch(e) {}
}
function traverse(node) {
if (!node) return;
if (node.nodeType === Node.ELEMENT_NODE) {
pauseMedia(node);
if (node.shadowRoot) {
traverse(node.shadowRoot);
}
if (node.tagName === 'IFRAME' || node.tagName === 'FRAME') {
try {
if (node.contentDocument) {
traverse(node.contentDocument);
}
} catch(e) {}
}
if (node.childNodes) {
node.childNodes.forEach(child => traverse(child));
}
} else if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE || node.nodeType === Node.DOCUMENT_NODE) {
if (node.childNodes) {
node.childNodes.forEach(child => traverse(child));
}
}
}
traverse(document);
document.querySelectorAll('video, audio').forEach(pauseMedia);
}
// =====================================================================
// ========== LINK EXTRACTOR (SIMPLIFIED MEDIA DETECTION) ==========
// =====================================================================
function isDirectMedia(url) {
if (!url) return false;
const lower = url.toLowerCase();
if (lower.startsWith('blob:')) return true;
const mediaExts = [
'.jpg', '.jpeg', '.png', '.gif', '.webp', '.svg', '.bmp', '.ico', '.tiff', '.tif', '.avif', '.heic', '.heif',
'.mp4', '.webm', '.ogg', '.ogv', '.mov', '.avi', '.mkv', '.ts', '.m4v', '.wmv', '.flv', '.m3u8',
'.mp3', '.wav', '.flac', '.m4a', '.aac', '.ogg', '.wma'
];
return mediaExts.some(ext => lower.includes(ext));
}
function getMediaType(url) {
if (!url) return null;
const lower = url.toLowerCase();
const imgExts = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.svg', '.bmp', '.ico', '.tiff', '.tif', '.avif', '.heic', '.heif'];
if (imgExts.some(ext => lower.includes(ext))) return 'image';
const vidExts = ['.mp4', '.webm', '.ogg', '.ogv', '.mov', '.avi', '.mkv', '.ts', '.m4v', '.wmv', '.flv', '.m3u8'];
if (vidExts.some(ext => lower.includes(ext))) return 'video';
const audExts = ['.mp3', '.wav', '.flac', '.m4a', '.aac', '.ogg', '.wma'];
if (audExts.some(ext => lower.includes(ext))) return 'audio';
if (lower.startsWith('blob:')) return 'image';
return null;
}
function isMediaUrl(url) {
return getMediaType(url) !== null;
}
function getAllLinks() {
const linkMap = new Map();
function addLink(url, text, type = 'link') {
if (!url) return;
try {
const abs = new URL(url, location.href).href;
if (abs.startsWith('javascript:') || abs.startsWith('mailto:') || abs.startsWith('tel:') || abs === '#') return;
let finalType = type;
const mediaType = getMediaType(abs);
if (mediaType) finalType = 'media';
if (!linkMap.has(abs)) {
linkMap.set(abs, { url: abs, text: text || abs, type: finalType, mediaSubtype: mediaType });
} else {
const existing = linkMap.get(abs);
if (finalType === 'media' && existing.type === 'link') {
existing.type = 'media';
existing.mediaSubtype = mediaType;
}
if (text && text !== abs && (existing.text === existing.url || !existing.text)) {
existing.text = text;
}
}
} catch (e) { /* ignore */ }
}
function traverse(node) {
if (!node) return;
if (node.nodeType === Node.ELEMENT_NODE) {
const el = node;
if (el.tagName === 'VIDEO' || el.tagName === 'AUDIO') {
const src = el.getAttribute('src') || el.currentSrc;
if (src) {
const text = el.getAttribute('title') || el.getAttribute('aria-label') || el.getAttribute('alt') || el.textContent.trim() || src;
addLink(src, text, 'media');
}
el.querySelectorAll('source').forEach(source => {
const s = source.getAttribute('src');
if (s) {
const label = source.getAttribute('label') || source.getAttribute('title') || s;
addLink(s, label, 'media');
}
});
const poster = el.getAttribute('poster');
if (poster && isMediaUrl(poster)) {
addLink(poster, 'Poster image', 'media');
}
}
if (el.tagName === 'IMG') {
const src = el.getAttribute('src') || el.currentSrc;
if (src) {
const text = el.alt || el.title || el.getAttribute('aria-label') || src;
addLink(src, text, 'media');
}
const srcset = el.getAttribute('srcset');
if (srcset) {
const urls = srcset.split(',').map(s => s.trim().split(/\s+/)[0]).filter(Boolean);
urls.forEach(u => {
if (u) addLink(u, el.alt || el.title || u, 'media');
});
}
const lazyAttrs = ['data-src', 'data-original', 'data-lazy-src', 'data-srcset'];
lazyAttrs.forEach(attr => {
const val = el.getAttribute(attr);
if (val) {
if (attr === 'data-srcset') {
const urls = val.split(',').map(s => s.trim().split(/\s+/)[0]).filter(Boolean);
urls.forEach(u => addLink(u, el.alt || el.title || u, 'media'));
} else {
addLink(val, el.alt || el.title || val, 'media');
}
}
});
}
if (el.tagName === 'SOURCE' && el.closest('picture')) {
const s = el.getAttribute('src');
if (s) {
const label = el.getAttribute('label') || el.getAttribute('title') || s;
addLink(s, label, 'media');
}
const srcset = el.getAttribute('srcset');
if (srcset) {
const urls = srcset.split(',').map(s => s.trim().split(/\s+/)[0]).filter(Boolean);
urls.forEach(u => addLink(u, el.getAttribute('label') || u, 'media'));
}
}
const dataAttrs = ['src', 'url', 'link', 'video-src', 'audio-src', 'media-url', 'file', 'image', 'img', 'photo'];
for (const attr of dataAttrs) {
const val = el.getAttribute('data-' + attr);
if (val && isMediaUrl(val)) {
const text = el.textContent.trim() || el.title || el.getAttribute('aria-label') || val;
addLink(val, text, 'media');
}
}
if (el.matches('a[href], area[href]')) {
const href = el.getAttribute('href');
if (href && isMediaUrl(href)) {
const text = el.textContent.trim() || el.title || el.getAttribute('aria-label') || href;
addLink(href, text, 'media');
} else if (href) {
const text = el.textContent.trim() || el.title || el.getAttribute('aria-label') || href;
addLink(href, text, 'link');
}
}
const onclick = el.getAttribute('onclick');
if (onclick) {
const matches = onclick.match(/(?:location\.href|window\.open)\s*\(\s*['"]([^'"]+)['"]/gi);
if (matches) {
matches.forEach(m => {
const urlMatch = m.match(/['"]([^'"]+)['"]/);
if (urlMatch) {
const url = urlMatch[1];
if (isMediaUrl(url)) {
const text = el.textContent.trim() || el.title || el.getAttribute('aria-label') || url;
addLink(url, text, 'media');
}
}
});
}
}
if (el.matches('form[action]')) {
const action = el.getAttribute('action');
if (action && isMediaUrl(action)) {
const text = el.getAttribute('name') || el.id || 'Form action';
addLink(action, text, 'media');
}
}
if (el.tagName === 'IFRAME') {
const iframeSrc = el.getAttribute('src');
if (iframeSrc) {
const text = el.getAttribute('title') || el.getAttribute('aria-label') || 'iframe';
addLink(iframeSrc, text, 'link');
}
}
if (el.shadowRoot) {
traverseShadow(el.shadowRoot);
}
}
if (node.childNodes) {
for (const child of node.childNodes) {
traverse(child);
}
}
}
function traverseShadow(root) {
if (root.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
for (const child of root.children) {
traverse(child);
}
}
}
traverse(document);
return Array.from(linkMap.values());
}
// ---------- Preview Modal ----------
function closePreviewModal(modalEl) {
const modal = modalEl || shadowBy('hider-link-preview-modal');
if (modal && modal.parentNode) {
modal.remove();
}
if (previewOutsideListener) {
document.removeEventListener('click', previewOutsideListener);
previewOutsideListener = null;
}
}
function showLinkPreview(url) {
const existing = shadowBy('hider-link-preview-modal');
if (existing) {
closePreviewModal(existing);
}
const mediaType = getMediaType(url);
const isDirect = isDirectMedia(url);
const modal = doc.createElement('div');
modal.id = 'hider-link-preview-modal';
modal.className = 'h-glass';
modal.style.cssText = `
position: fixed !important;
top: 50% !important;
left: 50% !important;
transform: translate(-50%, -50%) !important;
z-index: 2147483647 !important;
padding: 16px !important;
border-radius: 16px !important;
background: rgba(13, 18, 30, 0.96) !important;
backdrop-filter: blur(16px) !important;
border: 1px solid rgba(255,255,255,0.15) !important;
box-shadow: 0 24px 48px rgba(0,0,0,0.8) !important;
min-width: 280px !important;
max-width: 90vw !important;
max-height: 90vh !important;
pointer-events: auto !important;
display: flex !important;
flex-direction: column !important;
gap: 10px !important;
color: #f8fafc !important;
overflow-y: auto !important;
`;
let typeIcon = '🔗';
if (mediaType === 'image') typeIcon = '🖼️';
else if (mediaType === 'video') typeIcon = '🎬';
else if (mediaType === 'audio') typeIcon = '🔊';
const title = doc.createElement('div');
title.style.cssText = 'font-weight:800; font-size:12px; color:#38bdf8; text-transform:uppercase; letter-spacing:0.5px;';
title.textContent = `${typeIcon} ${mediaType ? mediaType.toUpperCase() : 'Link'} Preview`;
modal.appendChild(title);
if (mediaType) {
const mediaWrapper = doc.createElement('div');
mediaWrapper.style.cssText = `
display:flex; justify-content:center; align-items:center;
background: rgba(0,0,0,0.4); border-radius:8px; padding:4px;
min-height: 80px; max-height: 55vh; overflow:hidden;
`;
if (mediaType === 'image') {
const img = doc.createElement('img');
img.src = url;
img.style.cssText = 'max-width:100%; max-height:55vh; object-fit:contain; border-radius:4px;';
img.onerror = () => {
img.alt = '❌ Failed to load image';
img.style.cssText += 'height:60px; width:auto;';
};
mediaWrapper.appendChild(img);
} else if (mediaType === 'video') {
if (isDirect) {
const video = doc.createElement('video');
video.src = url;
video.controls = true;
video.preload = 'metadata';
video.style.cssText = 'max-width:100%; max-height:55vh; border-radius:4px; background:#000;';
video.onerror = () => {
video.innerHTML = '<div style="padding:20px;color:#f87171;font-size:12px;">❌ Failed to load video</div>';
};
mediaWrapper.appendChild(video);
} else {
mediaWrapper.textContent = '🎬 Video – click "Open" to watch in new tab.';
mediaWrapper.style.cssText += 'padding:20px;color:#94a3b8;font-size:12px;';
}
} else if (mediaType === 'audio') {
const audio = doc.createElement('audio');
audio.src = url;
audio.controls = true;
audio.preload = 'metadata';
audio.style.cssText = 'width:100%;';
audio.onerror = () => {
audio.innerHTML = '<div style="padding:20px;color:#f87171;font-size:12px;">❌ Failed to load audio</div>';
};
mediaWrapper.appendChild(audio);
} else {
mediaWrapper.textContent = 'Media preview not available';
mediaWrapper.style.cssText += 'padding:20px;color:#94a3b8;font-size:12px;';
}
modal.appendChild(mediaWrapper);
} else {
const linkPreview = doc.createElement('div');
linkPreview.textContent = '🔗 Link Preview';
linkPreview.style.cssText = 'font-size:12px;color:#94a3b8;padding:10px;';
modal.appendChild(linkPreview);
}
const urlDisplay = doc.createElement('div');
urlDisplay.style.cssText = 'font-size:10px; word-break:break-all; background:rgba(0,0,0,0.3); padding:6px 8px; border-radius:6px; border:1px solid rgba(255,255,255,0.1); font-family:monospace; max-height:80px; overflow-y:auto;';
urlDisplay.textContent = url;
modal.appendChild(urlDisplay);
const btnGroup = doc.createElement('div');
btnGroup.style.cssText = 'display:flex; gap:6px; justify-content:flex-end; flex-wrap:wrap;';
const copyBtn = doc.createElement('button');
copyBtn.className = 'hider-btn-small btn-blue';
copyBtn.textContent = '📋 Copy';
copyBtn.onclick = () => {
copyToClipboard(url);
showToast('📋 URL copied!');
};
btnGroup.appendChild(copyBtn);
if (isDirect && (mediaType === 'image' || mediaType === 'video' || mediaType === 'audio')) {
const downloadBtn = doc.createElement('button');
downloadBtn.className = 'hider-btn-small btn-green';
downloadBtn.textContent = '⬇️ Download';
downloadBtn.onclick = function(e) {
e.stopPropagation();
let filename = url.split('/').pop().split(/[?#]/)[0] || 'media_file';
if (!filename.includes('.')) {
if (mediaType === 'image') filename += '.jpg';
else if (mediaType === 'video') filename += '.mp4';
else if (mediaType === 'audio') filename += '.mp3';
else filename += '.bin';
}
try {
GM_download({
url: url,
name: filename,
onerror: function(err) {
console.error('Download failed:', err);
showToast('❌ Download failed – opening in new tab');
window.open(url, '_blank');
}
});
showToast(`⬇️ Downloading: ${filename}`);
} catch (err) {
console.error('GM_download error:', err);
showToast('❌ Download error – opening in new tab');
window.open(url, '_blank');
}
};
btnGroup.appendChild(downloadBtn);
}
const openBtn = doc.createElement('button');
openBtn.className = 'hider-btn-small btn-purple';
openBtn.textContent = '↗ Open';
openBtn.onclick = () => {
win.open(url, '_blank');
};
btnGroup.appendChild(openBtn);
const closeBtn = doc.createElement('button');
closeBtn.className = 'hider-btn-small btn-gray';
closeBtn.textContent = '✖ Close';
closeBtn.onclick = () => closePreviewModal(modal);
btnGroup.appendChild(closeBtn);
modal.appendChild(btnGroup);
shadowRoot.appendChild(modal);
const closeModalHandler = (e) => {
if (!modal.parentNode) {
document.removeEventListener('click', closeModalHandler);
return;
}
const path = e.composedPath ? e.composedPath() : [];
if (path.some(el => el === modal || (el && el.contains && el.contains(modal)))) {
return;
}
if (path.some(el => el.id === UI_HOST_ID || el.id === STEPPER_BAR_ID)) {
return;
}
closePreviewModal(modal);
};
if (previewOutsideListener) {
document.removeEventListener('click', previewOutsideListener);
}
previewOutsideListener = closeModalHandler;
setTimeout(() => {
document.addEventListener('click', previewOutsideListener);
}, 10);
}
// ----- Grid item for Media Mode -----
function buildGridItem(link) {
const div = doc.createElement('div');
div.className = 'hider-grid-item';
div.style.cssText = `
position: relative;
background: rgba(255,255,255,0.04);
border-radius: 8px;
overflow: hidden;
aspect-ratio: 1 / 1;
cursor: pointer;
border: 1px solid rgba(255,255,255,0.08);
transition: transform 0.2s ease, box-shadow 0.2s ease;
`;
div.onmouseover = () => { div.style.transform = 'scale(1.03)'; div.style.boxShadow = '0 4px 12px rgba(0,0,0,0.6)'; };
div.onmouseout = () => { div.style.transform = 'scale(1)'; div.style.boxShadow = 'none'; };
const mediaType = link.mediaSubtype || getMediaType(link.url);
const isDirect = isDirectMedia(link.url);
let icon = '📎';
if (mediaType === 'image') icon = '🖼️';
else if (mediaType === 'video') icon = '🎬';
else if (mediaType === 'audio') icon = '🔊';
let thumbnail = '';
if (mediaType === 'image') {
thumbnail = `<img src="${link.url}" style="width:100%;height:100%;object-fit:cover;" onerror="this.style.display='none';">`;
} else if (mediaType === 'video' && isDirect) {
thumbnail = `<video src="${link.url}" muted preload="metadata" style="width:100%;height:100%;object-fit:cover;" onerror="this.style.display='none';"></video>`;
} else {
thumbnail = `<div style="display:flex;align-items:center;justify-content:center;width:100%;height:100%;font-size:48px;color:#94a3b8;background:rgba(0,0,0,0.3);">${icon}</div>`;
}
div.innerHTML = thumbnail;
const overlay = doc.createElement('div');
overlay.style.cssText = `
position: absolute;
bottom: 4px;
left: 4px;
background: rgba(0,0,0,0.7);
padding: 2px 6px;
border-radius: 4px;
font-size: 8px;
color: #e0f2fe;
backdrop-filter: blur(4px);
pointer-events: none;
font-weight: 600;
letter-spacing: 0.3px;
text-transform: uppercase;
`;
overlay.textContent = mediaType || 'media';
div.appendChild(overlay);
if (isDirect && (mediaType === 'image' || mediaType === 'video' || mediaType === 'audio')) {
const downloadBtn = doc.createElement('button');
downloadBtn.className = 'hider-btn-small';
downloadBtn.textContent = '⬇️';
downloadBtn.title = 'Download this media';
downloadBtn.style.cssText = `
position: absolute;
bottom: 4px;
right: 4px;
background: rgba(0,0,0,0.7) !important;
backdrop-filter: blur(4px);
border: none;
border-radius: 4px;
color: #fff;
padding: 2px 6px;
font-size: 10px;
cursor: pointer;
z-index: 2;
pointer-events: auto;
transition: background 0.2s;
`;
downloadBtn.onmouseover = () => { downloadBtn.style.background = 'rgba(56,189,248,0.8) !important'; };
downloadBtn.onmouseout = () => { downloadBtn.style.background = 'rgba(0,0,0,0.7) !important'; };
downloadBtn.addEventListener('click', function(e) {
e.stopPropagation();
const url = link.url;
if (!url) {
showToast('❌ No media URL');
return;
}
let filename = url.split('/').pop().split(/[?#]/)[0] || 'media_file';
if (!filename.includes('.')) {
if (mediaType === 'image') filename += '.jpg';
else if (mediaType === 'video') filename += '.mp4';
else if (mediaType === 'audio') filename += '.mp3';
else filename += '.bin';
}
try {
GM_download({
url: url,
name: filename,
onerror: function(err) {
console.error('Download failed:', err);
showToast('❌ Download failed – opening in new tab');
window.open(url, '_blank');
}
});
showToast(`⬇️ Downloading: ${filename}`);
} catch (err) {
console.error('GM_download error:', err);
showToast('❌ Download error – opening in new tab');
window.open(url, '_blank');
}
});
div.appendChild(downloadBtn);
}
div.addEventListener('click', (e) => {
if (e.target.closest('button')) return;
showLinkPreview(link.url);
});
return div;
}
// ----- List item for normal mode -----
function buildListItem(link, filterText, hideInternal, mediaOnly) {
const currentHost = location.hostname;
if (hideInternal) {
try {
const urlObj = new URL(link.url);
if (urlObj.hostname === currentHost) return null;
} catch { /* ignore */ }
}
if (mediaOnly && link.type !== 'media') return null;
if (filterText) {
const lower = filterText.toLowerCase();
if (!link.text.toLowerCase().includes(lower) && !link.url.toLowerCase().includes(lower)) {
return null;
}
}
const div = doc.createElement('div');
div.className = 'list-item';
div.style.cssText = 'display:flex; justify-content:space-between; align-items:center; gap:4px;';
let icon = '🔗';
if (link.type === 'media') {
const subtype = link.mediaSubtype || getMediaType(link.url);
if (subtype === 'image') icon = '🖼️';
else if (subtype === 'video') icon = '🎬';
else if (subtype === 'audio') icon = '🔊';
else icon = '🎬';
}
let displayText = link.text;
if (link.type === 'media') {
displayText = icon + ' ' + displayText;
}
if (linkDisplayMode === 'url') {
displayText = link.url + (link.type === 'media' ? ' ' + icon : '');
}
const textSpan = doc.createElement('span');
textSpan.className = 'rule-text';
textSpan.textContent = displayText;
textSpan.title = link.url;
textSpan.style.cssText = 'flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;';
const btnGroup = doc.createElement('div');
btnGroup.style.cssText = 'display:flex; gap:2px; flex-shrink:0;';
const previewBtn = doc.createElement('button');
previewBtn.className = 'hider-btn-small btn-purple';
previewBtn.textContent = '🔍';
previewBtn.title = 'Preview full link (media preview if applicable)';
previewBtn.onclick = (e) => {
e.stopPropagation();
showLinkPreview(link.url);
};
btnGroup.appendChild(previewBtn);
const copyBtn = doc.createElement('button');
copyBtn.className = 'hider-btn-small btn-blue';
copyBtn.textContent = '📋';
copyBtn.title = 'Copy URL';
copyBtn.onclick = (e) => {
e.stopPropagation();
copyToClipboard(link.url);
showToast('📋 Copied!');
};
btnGroup.appendChild(copyBtn);
const openBtn = doc.createElement('button');
openBtn.className = 'hider-btn-small btn-green';
openBtn.textContent = '↗';
openBtn.title = 'Open in new tab';
openBtn.onclick = (e) => {
e.stopPropagation();
win.open(link.url, '_blank');
};
btnGroup.appendChild(openBtn);
div.appendChild(textSpan);
div.appendChild(btnGroup);
return div;
}
function populateLinkList() {
const listContainer = shadowBy('hider-link-list');
if (!listContainer) return;
const searchInput = shadowBy('hider-link-search');
const hideInternalCheck = shadowBy('hider-link-hide-internal');
const mediaModeCheck = shadowBy('hider-link-media-mode');
const filterText = searchInput ? searchInput.value.toLowerCase() : '';
const hideInternal = hideInternalCheck ? hideInternalCheck.checked : false;
const mediaMode = mediaModeCheck ? mediaModeCheck.checked : false;
const links = getAllLinks();
const title = shadowBy('link-panel-title');
if (title) title.textContent = `🔗 Extracted Links (${links.length})`;
listContainer.innerHTML = '';
let displayed = 0;
const frag = doc.createDocumentFragment();
if (mediaMode) {
const grid = doc.createElement('div');
grid.style.cssText = 'display:grid;grid-template-columns:repeat(3,1fr);gap:6px;';
links.forEach(link => {
if (link.type !== 'media') return;
if (filterText) {
const lower = filterText;
if (!link.text.toLowerCase().includes(lower) && !link.url.toLowerCase().includes(lower)) return;
}
if (hideInternal) {
try {
const urlObj = new URL(link.url);
if (urlObj.hostname === location.hostname) return;
} catch {}
}
const item = buildGridItem(link);
grid.appendChild(item);
displayed++;
});
if (displayed === 0) {
const empty = doc.createElement('div');
empty.style.cssText = 'font-size:9px!important;color:#94a3b8!important;padding:8px!important;text-align:center!important;border:1px dashed rgba(255,255,255,0.12)!important;border-radius:6px!important;grid-column:1/4;';
empty.textContent = 'No media items.';
grid.appendChild(empty);
}
frag.appendChild(grid);
} else {
links.forEach(link => {
const item = buildListItem(link, filterText, hideInternal, false);
if (item) {
frag.appendChild(item);
displayed++;
}
});
if (displayed === 0) {
const empty = doc.createElement('div');
empty.style.cssText = 'font-size:9px!important;color:#94a3b8!important;padding:8px!important;text-align:center!important;border:1px dashed rgba(255,255,255,0.12)!important;border-radius:6px!important;';
empty.textContent = 'No matching links.';
frag.appendChild(empty);
}
}
listContainer.appendChild(frag);
if (title) title.textContent = `🔗 Extracted Links (${displayed} shown)`;
}
// --- Link panel with Refresh button ---
function createLinkPanel() {
if (linkPanelEl) return linkPanelEl;
const panel = doc.createElement('div');
panel.id = 'hider-link-panel';
panel.className = 'h-glass';
panel.style.cssText = `
position: fixed !important;
right: 14px !important;
top: 55px !important;
z-index: 2147483645 !important;
width: min(310px, 90vw) !important;
max-height: min(580px, 82vh) !important;
border-radius: 16px !important;
padding: 10px !important;
display: none;
flex-direction: column !important;
overflow-y: auto !important;
overscroll-behavior: contain !important;
touch-action: pan-y !important;
pointer-events: auto !important;
gap: 6px !important;
opacity: 0;
transform: scale(0.96) translateY(-8px);
transition: opacity 0.2s ease, transform 0.2s ease !important;
`;
const header = doc.createElement('div');
header.className = 'panel-header';
header.style.cssText = 'display:flex; justify-content:space-between; align-items:center;';
const title = doc.createElement('span');
title.id = 'link-panel-title';
title.textContent = '🔗 Extracted Links';
const headerRight = doc.createElement('div');
headerRight.style.cssText = 'display:flex; gap:4px;';
const modeBtn = doc.createElement('button');
modeBtn.className = 'hider-btn-small btn-gray';
modeBtn.textContent = '📝';
modeBtn.title = 'Toggle display: text / URL';
modeBtn.onclick = (e) => {
e.stopPropagation();
linkDisplayMode = linkDisplayMode === 'text' ? 'url' : 'text';
modeBtn.textContent = linkDisplayMode === 'text' ? '📝' : '🔗';
populateLinkList();
};
headerRight.appendChild(modeBtn);
const refreshBtn = doc.createElement('button');
refreshBtn.className = 'hider-btn-small btn-purple';
refreshBtn.textContent = '🔄';
refreshBtn.title = 'Refresh links & pause media';
refreshBtn.onclick = (e) => {
e.stopPropagation();
pauseAllVideos();
populateLinkList();
showToast('🔄 Links refreshed');
};
headerRight.appendChild(refreshBtn);
const closeBtn = doc.createElement('button');
closeBtn.className = 'hider-btn-small';
closeBtn.style.cssText = 'background:rgba(255,255,255,0.12)!important; padding:2px 6px!important;';
closeBtn.textContent = '✖';
closeBtn.onclick = () => { toggleLinkPanel(); };
headerRight.appendChild(closeBtn);
header.appendChild(title);
header.appendChild(headerRight);
panel.appendChild(header);
const searchWrapper = doc.createElement('div');
searchWrapper.style.cssText = 'display:flex; gap:4px; align-items:center; margin-bottom:2px; flex-wrap:wrap;';
const searchInput = doc.createElement('input');
searchInput.id = 'hider-link-search';
searchInput.className = 'h-select';
searchInput.placeholder = '🔍 Filter links...';
searchInput.style.cssText = 'flex:1; height:26px; font-size:10px; min-width:80px;';
searchInput.oninput = () => populateLinkList();
searchWrapper.appendChild(searchInput);
const hideCheck = doc.createElement('label');
hideCheck.style.cssText = 'font-size:9px; color:#cbd5e1; display:flex; align-items:center; gap:4px; cursor:pointer; user-select:none; white-space:nowrap;';
const checkBox = doc.createElement('input');
checkBox.id = 'hider-link-hide-internal';
checkBox.type = 'checkbox';
checkBox.style.cssText = 'accent-color:#38bdf8; width:12px; height:12px; cursor:pointer;';
checkBox.onchange = () => populateLinkList();
hideCheck.appendChild(checkBox);
hideCheck.appendChild(doc.createTextNode('Hide internal'));
searchWrapper.appendChild(hideCheck);
const mediaCheck = doc.createElement('label');
mediaCheck.style.cssText = 'font-size:9px; color:#cbd5e1; display:flex; align-items:center; gap:4px; cursor:pointer; user-select:none; white-space:nowrap;';
const mediaBox = doc.createElement('input');
mediaBox.id = 'hider-link-media-mode';
mediaBox.type = 'checkbox';
mediaBox.style.cssText = 'accent-color:#f59e0b; width:12px; height:12px; cursor:pointer;';
mediaBox.onchange = () => populateLinkList();
mediaCheck.appendChild(mediaBox);
mediaCheck.appendChild(doc.createTextNode('📺 Media mode'));
searchWrapper.appendChild(mediaCheck);
panel.appendChild(searchWrapper);
const listContainer = doc.createElement('div');
listContainer.id = 'hider-link-list';
listContainer.style.cssText = 'display:flex; flex-direction:column; gap:4px; overflow-y:auto; flex:1; overscroll-behavior:contain;';
panel.appendChild(listContainer);
shadowRoot.appendChild(panel);
linkPanelEl = panel;
return panel;
}
function toggleLinkPanel() {
if (!shadowRoot) return;
const panel = shadowBy('hider-panel');
if (panel && panel.classList.contains('is-visible')) {
panel.classList.remove('is-visible');
setTimeout(() => panel.style.display = 'none', 300);
shadowBy('btn-manage')?.classList.remove('active');
}
if (!linkPanelEl || !linkPanelEl.parentNode) {
linkPanelEl = createLinkPanel();
}
const isVisible = linkPanelEl.classList.contains('is-visible');
if (isVisible) {
linkPanelEl.classList.remove('is-visible');
setTimeout(() => linkPanelEl.style.display = 'none', 300);
} else {
pauseAllVideos();
populateLinkList();
linkPanelEl.style.display = 'flex';
void linkPanelEl.offsetWidth;
linkPanelEl.classList.add('is-visible');
}
}
// ---------- UI Creation ----------
function createShadowUI() {
if (!isTop) return;
const parentEl = doc.documentElement || doc.body;
if (!parentEl) return;
let hostEl = doc.getElementById(UI_HOST_ID);
if (!hostEl) {
hostEl = doc.createElement('div'); hostEl.id = UI_HOST_ID;
hostEl.style.cssText = 'position:fixed!important;top:0!important;left:0!important;width:0!important;height:0!important;z-index:2147483647!important;pointer-events:none!important;display:block!important;visibility:visible!important;opacity:1!important;overflow:visible!important;';
parentEl.appendChild(hostEl);
}
shadowRoot = hostEl.shadowRoot || hostEl.attachShadow({ mode: 'open' });
const style = doc.createElement('style');
style.textContent = `
* { box-sizing: border-box !important; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Inter, sans-serif !important; line-height: 1.3 !important; }
.h-glass {
background: rgba(13, 18, 30, 0.92) !important;
backdrop-filter: blur(12px) !important;
-webkit-backdrop-filter: blur(12px) !important;
border: 1px solid rgba(255, 255, 255, 0.12) !important;
color: #f8fafc !important;
border-radius: 14px !important;
box-shadow: 0 12px 30px rgba(0, 0, 0, 0.6) !important;
}
::-webkit-scrollbar { width: 4px !important; height: 4px !important; }
::-webkit-scrollbar-track { background: transparent !important; }
::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.2) !important; border-radius: 8px !important; }
#hider-panel {
position: fixed !important;
right: 14px !important;
top: 55px !important;
z-index: 2147483645 !important;
width: min(480px, 92vw) !important;
max-height: min(700px, 85vh) !important;
border-radius: 16px !important;
padding: 0 !important;
display: none;
flex-direction: column !important;
overflow: hidden !important;
pointer-events: auto !important;
opacity: 0;
transform: scale(0.96) translateY(-8px);
transition: opacity 0.2s ease, transform 0.2s ease !important;
}
#hider-panel.is-visible {
opacity: 1 !important;
transform: scale(1) translateY(0) !important;
display: flex !important;
}
#hider-link-panel.is-visible {
opacity: 1 !important;
transform: scale(1) translateY(0) !important;
display: flex !important;
}
.panel-header {
padding: 8px 12px;
border-bottom: 1px solid rgba(255,255,255,0.08);
display: flex;
justify-content: space-between;
align-items: center;
flex-shrink: 0;
}
.panel-header .title {
font-weight: 800;
font-size: 12px;
color: #f8fafc;
}
.panel-header .close-btn {
background: rgba(255,255,255,0.08);
border: none;
border-radius: 6px;
color: #cbd5e1;
padding: 2px 8px;
cursor: pointer;
font-size: 11px;
}
.panel-header .close-btn:hover { background: rgba(255,255,255,0.16); }
.panel-body {
display: flex;
flex: 1;
overflow: hidden;
}
.panel-sidebar {
flex: 0 0 110px;
background: rgba(0,0,0,0.2);
padding: 6px 4px;
overflow-y: auto;
border-right: 1px solid rgba(255,255,255,0.06);
}
.panel-sidebar .tab-btn {
display: block;
width: 100%;
text-align: left;
background: transparent;
border: none;
border-radius: 6px;
padding: 5px 8px;
color: #94a3b8;
font-size: 10px;
font-weight: 600;
cursor: pointer;
transition: all 0.15s ease;
margin-bottom: 2px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.panel-sidebar .tab-btn:hover {
background: rgba(255,255,255,0.08);
color: #e2e8f0;
}
.panel-sidebar .tab-btn.active {
background: rgba(56,189,248,0.15);
color: #38bdf8;
border-left: 2px solid #38bdf8;
}
.panel-content {
flex: 1;
padding: 8px 10px;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 6px;
}
.panel-content .tab-content {
display: none;
flex-direction: column;
gap: 6px;
}
.panel-content .tab-content.active {
display: flex;
}
.h-dock {
position: fixed !important; right: 10px !important; top: 35%; z-index: 2147483643 !important;
display: flex !important; flex-direction: column !important; align-items: center !important; gap: 5px !important;
padding: 5px !important; pointer-events: auto !important; transition: transform 0.3s ease, opacity 0.3s ease !important;
user-select: none !important; border-radius: 20px !important;
}
.h-dock.is-collapsed { transform: translateX(65%) !important; opacity: 0.5 !important; }
.h-dock.manual-hidden { transform: translateX(65%) !important; opacity: 0.5 !important; }
.h-dock.is-collapsed:not(.manual-hidden):hover, .h-dock.expanded { transform: translateX(0) !important; opacity: 1 !important; }
.h-dock-main {
width: 34px !important; height: 34px !important; font-size: 16px !important;
background: linear-gradient(135deg, rgba(56, 189, 248, 0.3), rgba(37, 99, 235, 0.4)) !important;
border: 1px solid rgba(56, 189, 248, 0.4) !important; cursor: grab !important; border-radius: 50% !important;
flex-shrink: 0 !important; min-height: 34px !important; min-width: 34px !important; display: flex !important; align-items: center !important; justify-content: center !important;
}
.h-dock-main.expanded {
background: linear-gradient(135deg, #0ea5e9, #2563eb) !important; color: #fff !important;
box-shadow: 0 0 12px rgba(14, 165, 233, 0.5) !important; border-color: transparent !important;
}
.h-dock-menu { display: flex; flex-direction: column !important; gap: 5px !important; overflow: hidden !important; max-height: 0; opacity: 0; transition: max-height 0.3s ease, opacity 0.2s ease !important; }
.h-dock-menu.is-open { max-height: 450px; opacity: 1; }
.h-dock-btn {
width: 32px !important; height: 32px !important; border-radius: 10px !important;
border: 1px solid rgba(255, 255, 255, 0.1) !important; background: rgba(255, 255, 255, 0.06) !important;
color: #cbd5e1 !important; font-size: 11px !important; font-weight: 700 !important; display: flex !important;
flex-direction: column !important; align-items: center !important; justify-content: center !important;
cursor: pointer !important; transition: all 0.2s ease !important; margin: 0 !important; padding: 0 !important;
flex-shrink: 0 !important; min-height: 32px !important; min-width: 32px !important;
}
.h-dock-btn:hover { background: rgba(255, 255, 255, 0.18) !important; color: #fff !important; transform: scale(1.05) !important; }
.h-dock-btn.active { background: linear-gradient(135deg, #0ea5e9, #2563eb) !important; color: #fff !important; border-color: transparent !important; }
.h-dock-btn.scope-link { background: linear-gradient(135deg, #10b981, #059669) !important; color: #fff !important; border-color: transparent !important; }
.h-dock-btn.is-frozen { background: linear-gradient(135deg, #8b5cf6, #6d28d9) !important; color: #fff !important; border-color: transparent !important; }
.h-dock-btn span { font-size: 7px !important; font-weight: 800 !important; margin-top: 1px !important; letter-spacing: 0.3px !important; text-transform: uppercase !important; }
.btn-blue { background: linear-gradient(135deg, #0ea5e9, #0284c7) !important; color: #fff !important; border: 1px solid rgba(255,255,255,0.2) !important; }
.btn-green { background: linear-gradient(135deg, #10b981, #059669) !important; color: #fff !important; border: 1px solid rgba(255,255,255,0.2) !important; }
.btn-red { background: linear-gradient(135deg, #f43f5e, #e11d48) !important; color: #fff !important; border: 1px solid rgba(255,255,255,0.2) !important; }
.btn-purple { background: linear-gradient(135deg, #a855f7, #7e22ce) !important; color: #fff !important; border: 1px solid rgba(255,255,255,0.2) !important; }
.btn-gray { background: linear-gradient(135deg, #475569, #334155) !important; color: #fff !important; border: 1px solid rgba(255,255,255,0.15) !important; }
.h-stepper-pill {
position: fixed !important; bottom: 20px !important; left: 50% !important; transform: translateX(-50%) !important;
z-index: 2147483644 !important; padding: 6px 8px !important; display: flex !important; flex-direction: column !important;
align-items: center !important; gap: 4px !important; pointer-events: auto !important; width: 130px !important;
user-select: none !important; border-radius: 12px !important;
}
.h-stepper-row { display: flex !important; gap: 4px !important; width: 100% !important; justify-content: center !important; }
.h-stepper-row .h-btn-icon { flex: 1 !important; }
.h-drag-dots { cursor: grab !important; color: #64748b !important; font-size: 11px !important; width: 100% !important; text-align: center !important; line-height: 1 !important; letter-spacing: 2px !important; }
.h-btn-icon { width: 100% !important; height: 22px !important; border-radius: 6px !important; border: 1px solid rgba(255,255,255,.12) !important; background: rgba(255,255,255,.08) !important; color: #f1f5f9 !important; font-size: 9px !important; font-weight: 700 !important; cursor: pointer !important; display: flex !important; align-items: center !important; justify-content: center !important; }
.h-btn-icon:hover { background: rgba(255,255,255,.2) !important; }
.h-tag-badge-box { width: 100% !important; height: 22px !important; overflow: hidden !important; position: relative !important; background: rgba(0, 0, 0, 0.5) !important; border: 1px solid rgba(56, 189, 248, 0.3) !important; border-radius: 6px !important; display: flex !important; align-items: center !important; flex-shrink: 0 !important; }
.h-tag-badge-text { color: #38bdf8 !important; font-size: 9px !important; font-family: monospace !important; font-weight: 700 !important; display: block !important; width: 100% !important; text-align: center !important; overflow: hidden !important; text-overflow: ellipsis !important; white-space: nowrap !important; line-height: 22px !important; }
.h-tag-badge-text.is-animating { position: absolute !important; left: 0 !important; width: auto !important; padding-left: 100% !important; text-align: left !important; animation: hiderMarqueeR2L 8s linear infinite !important; }
@keyframes hiderMarqueeR2L { 0% { transform: translate3d(0,0,0); } 100% { transform: translate3d(-100%,0,0); } }
.h-btn-pill { height: 22px !important; min-height: 22px !important; width: 100% !important; padding: 0 6px !important; border-radius: 6px !important; font-size: 9px !important; font-weight: 800 !important; border: none !important; cursor: pointer !important; display: flex !important; align-items: center !important; justify-content: center !important; }
.h-btn-pill:hover { filter: brightness(1.15) !important; }
.h-toast { /* overridden by inline styles */ }
.h-prompt { position: fixed !important; top: 25px !important; left: 50% !important; transform: translateX(-50%) translateY(-15px) !important; opacity: 0; padding: 12px 14px !important; z-index: 2147483646 !important; display: flex !important; flex-direction: column !important; align-items: center !important; gap: 6px !important; pointer-events: auto !important; width: min(290px, 88vw) !important; max-height: 85vh !important; transition: all 0.3s ease !important; }
.h-url-box { font-size: 10px !important; color: #94a3b8 !important; word-break: break-all !important; max-height: 32px !important; overflow: hidden !important; background: rgba(0,0,0,.4) !important; padding: 4px 8px !important; border-radius: 6px !important; width: 100% !important; border: 1px solid rgba(255,255,255,.1) !important; font-family: monospace !important; }
.h-select { width: 100% !important; background: rgba(255,255,255,.06) !important; border: 1px solid rgba(255,255,255,.14) !important; border-radius: 6px !important; color: #fff !important; padding: 4px 8px !important; font-size: 10px !important; outline: 0 !important; height: 28px !important; }
.h-custom-select { position: relative !important; width: 100% !important; user-select: none !important; margin-top: 2px !important; z-index: 10 !important; }
.h-custom-trigger { background: rgba(30,41,59,.85) !important; border: 1px solid rgba(96,165,250,.4) !important; border-radius: 8px !important; color: #f1f5f9 !important; padding: 6px 10px !important; font-size: 10px !important; font-weight: 700 !important; cursor: pointer !important; display: flex !important; justify-content: space-between !important; align-items: center !important; height: 30px !important; }
.h-custom-arrow { font-size: 9px !important; color: #38bdf8 !important; transition: transform .2s ease !important; }
.h-custom-select.is-open .h-custom-arrow { transform: rotate(-180deg) !important; }
.h-custom-options { display: none; position: relative !important; z-index: 100 !important; flex-direction: column !important; background: rgba(15,23,42,0.98) !important; border: 1px solid rgba(255,255,255,.2) !important; border-radius: 8px !important; max-height: 160px !important; overflow-y: auto !important; margin-top: 4px !important; }
.h-custom-select.is-open .h-custom-options { display: flex !important; }
.h-custom-opt { padding: 6px 10px !important; color: #cbd5e1 !important; font-size: 10px !important; cursor: pointer !important; border-bottom: 1px solid rgba(255,255,255,.05) !important; }
.h-custom-opt:hover, .h-custom-opt.is-selected { background: rgba(56,189,248,.2) !important; color: #38bdf8 !important; font-weight: 700 !important; }
.panel-header { font-weight: 800 !important; font-size: 11px !important; border-bottom: 1px solid rgba(255,255,255,.12) !important; padding-bottom: 6px !important; display: flex !important; justify-content: space-between !important; align-items: center !important; margin-bottom: 2px !important; color: #f8fafc !important; }
.list-item { font-size: 10px !important; background: rgba(255,255,255,.04) !important; border: 1px solid rgba(255,255,255,.08) !important; border-radius: 6px !important; padding: 4px 6px !important; display: flex !important; justify-content: space-between !important; align-items: center !important; gap: 6px !important; }
.list-item span.rule-text { word-break: break-all !important; color: #e2e8f0 !important; font-family: monospace !important; font-size: 9px !important; flex: 1 !important; overflow: hidden !important; text-overflow: ellipsis !important; white-space: nowrap !important; }
.hider-btn-small { cursor: pointer !important; border: none !important; border-radius: 4px !important; padding: 2px 6px !important; color: #fff !important; font-weight: 700 !important; font-size: 9px !important; height: 20px !important; display: flex; align-items: center; justify-content: center; }
`;
const container = doc.createElement('div');
container.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 Control Dock">🛡️</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" id="btn-reveal-quick" title="Reveal hidden/blurred elements">👁️<span>Reveal</span></button>
<button class="h-dock-btn" id="btn-links" title="Extract all links from the page">🔗<span>Links</span></button>
<button class="h-dock-btn" id="btn-skip-30" title="Skip 30 seconds forward">⏩<span>+30s</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>
<!-- Control Panel (two-column layout) -->
<div id="hider-panel" class="h-glass">
<div class="panel-header">
<span class="title">🛡️ Hide Web Elements Pro</span>
<button class="close-btn" id="close-p">✖</button>
</div>
<div class="panel-body">
<div class="panel-sidebar" id="panel-sidebar"></div>
<div class="panel-content" id="panel-content"></div>
</div>
</div>`;
shadowRoot.innerHTML = ''; shadowRoot.appendChild(style); shadowRoot.appendChild(container);
buildPanelTabs();
setupShadowUIEvents();
applyDockButtonVisibility();
renderDockButtonOptions();
// ---- ISOLATE PANEL EVENTS FROM MAIN PAGE ----
const panel = shadowRoot.getElementById('hider-panel');
if (panel) {
const stopProp = (e) => e.stopPropagation();
panel.addEventListener('wheel', stopProp, { passive: true, capture: true });
panel.addEventListener('touchmove', stopProp, { passive: true, capture: true });
panel.addEventListener('scroll', stopProp, { passive: true, capture: true });
panel.addEventListener('pointerdown', stopProp, { capture: true });
panel.addEventListener('mousedown', stopProp, { capture: true });
}
const linkPanel = shadowRoot.getElementById('hider-link-panel');
if (linkPanel) {
const stopProp = (e) => e.stopPropagation();
linkPanel.addEventListener('wheel', stopProp, { passive: true, capture: true });
linkPanel.addEventListener('touchmove', stopProp, { passive: true, capture: true });
linkPanel.addEventListener('scroll', stopProp, { passive: true, capture: true });
linkPanel.addEventListener('pointerdown', stopProp, { capture: true });
linkPanel.addEventListener('mousedown', stopProp, { capture: true });
}
const stepper = shadowRoot.getElementById(STEPPER_BAR_ID);
if (stepper) {
const stopProp = (e) => e.stopPropagation();
stepper.addEventListener('wheel', stopProp, { passive: true, capture: true });
stepper.addEventListener('touchmove', stopProp, { passive: true, capture: true });
stepper.addEventListener('pointerdown', stopProp, { capture: true });
}
}
// ---------- Build Panel Tabs (Redesigned) ----------
function buildPanelTabs() {
const sidebar = shadowBy('panel-sidebar');
const content = shadowBy('panel-content');
if (!sidebar || !content) return;
const tabs = [
{ id: 'tab-tools', label: '🛠️ Tools', html: `
<div style="display:flex;flex-direction:column;gap:4px;">
<label style="font-size:10px;color:#cbd5e1;display:flex;align-items:center;gap:6px;cursor:pointer;user-select:none;">
<input type="checkbox" id="chk-auto-scroll" ${CACHE.autoScroll?'checked':''} style="accent-color:#38bdf8;"> Auto Anti‑Scroll Lock
</label>
<label style="font-size:10px;color:#cbd5e1;display:flex;align-items:center;gap:6px;cursor:pointer;user-select:none;">
<input type="checkbox" id="chk-enable-contextmenu" ${CACHE.enableContextMenu?'checked':''} style="accent-color:#38bdf8;"> Auto Right‑Click / Long‑Press
</label>
<label style="font-size:10px;color:#cbd5e1;display:flex;align-items:center;gap:6px;cursor:pointer;user-select:none;">
<input type="checkbox" id="chk-auto-remove-blur" ${CACHE.autoRemoveBlur?'checked':''} style="accent-color:#38bdf8;"> Auto Remove Blur
</label>
<label style="font-size:10px;color:#cbd5e1;display:flex;align-items:center;gap:6px;cursor:pointer;user-select:none;margin-top:4px;">
<input type="checkbox" id="chk-auto-time-skipper" ${CACHE.autoTimeSkipper?'checked':''} style="accent-color:#f59e0b;"> Auto Time Skipper
</label>
<div style="margin-top:4px;border-top:1px solid rgba(255,255,255,0.06);padding-top:4px;">
<div style="font-size:10px;color:#94a3b8;margin-bottom:2px;">❄️ Freeze Behaviour</div>
<div class="h-custom-select" id="freeze-custom-panel-dropdown">
<div class="h-custom-trigger"><span class="h-custom-value-text">${FREEZE_LABELS[CACHE.freezeMemory] || FREEZE_LABELS['ask']}</span><span class="h-custom-arrow">▼</span></div>
<div class="h-custom-options">
<div class="h-custom-opt" data-val="ask">❓ Ask Every Time</div>
<div class="h-custom-opt" data-val="block_all">⛔ Auto‑Block All</div>
<div class="h-custom-opt" data-val="allow_same">🔗 Allow Same Domain Only</div>
<div class="h-custom-opt" data-val="allow_all">🟢 Allow All</div>
</div>
</div>
<button id="btn-reset-block-confirm" class="hider-btn-small" style="background:rgba(255,255,255,0.06);color:#cbd5e1;margin-top:4px;width:100%;padding:4px;height:auto;font-size:9px;">Reset "Don't Ask Block" Prompt</button>
</div>
<div style="margin-top:4px;border-top:1px solid rgba(255,255,255,0.06);padding-top:4px;">
<div style="font-size:10px;color:#94a3b8;margin-bottom:2px;">🔘 Dock Button Visibility</div>
<div id="dock-buttons-list" style="display:flex;flex-direction:column;gap:4px;"></div>
</div>
</div>
` },
{ id: 'tab-rules', label: '✏️ Rules', html: `
<div style="display:flex;flex-direction:column;gap:4px;">
<div style="display:flex;gap:4px;">
<input type="text" id="manual-rule-input" placeholder="CSS Selector (e.g. .ad-banner)" class="h-select" style="flex:1;">
<input type="text" id="manual-target-input" placeholder="Target Domain (* Global)" class="h-select" style="flex:1;">
<button class="hider-btn-small btn-blue" id="save-rule-btn" style="height:28px;padding:0 8px;">Save</button>
</div>
<div style="display:flex;flex-direction:column;gap:4px;">
<div style="display:flex;justify-content:space-between;font-size:10px;color:#94a3b8;">
<span>Custom Rules (<span id="cnt-custom">0</span>)</span>
</div>
<div id="list-custom-rules" style="display:flex;flex-direction:column;gap:3px;"></div>
</div>
<div style="border-top:1px solid rgba(255,255,255,0.06);padding-top:4px;">
<div style="display:flex;justify-content:space-between;font-size:10px;color:#94a3b8;">
<span>🌐 Site‑Wide (<span id="cnt-site">0</span>)</span>
</div>
<div id="list-site" style="display:flex;flex-direction:column;gap:3px;"></div>
</div>
<div style="border-top:1px solid rgba(255,255,255,0.06);padding-top:4px;">
<div style="display:flex;justify-content:space-between;font-size:10px;color:#94a3b8;">
<span>📄 Page‑Only (<span id="cnt-link">0</span>)</span>
</div>
<div id="list-link" style="display:flex;flex-direction:column;gap:3px;"></div>
</div>
</div>
` },
{ id: 'tab-domains', label: '🌐 Domains', html: `
<div style="display:flex;flex-direction:column;gap:6px;">
<div style="display:flex;gap:4px;">
<input type="text" id="manual-domain-input" placeholder="Block domain (e.g. bad.com)" class="h-select" style="flex:1;">
<button class="hider-btn-small btn-red" id="add-domain-btn" style="height:28px;padding:0 8px;">Block</button>
</div>
<div>
<div style="font-size:10px;color:#f87171;font-weight:700;margin-bottom:2px;">🚫 Blocked Domains</div>
<div id="list-blocked-domains" style="display:flex;flex-direction:column;gap:3px;"></div>
</div>
<div style="border-top:1px solid rgba(255,255,255,0.06);padding-top:6px;">
<div style="display:flex;gap:4px;">
<input type="text" id="manual-allowed-domain-input" placeholder="Allow domain (e.g. trusted.com)" class="h-select" style="flex:1;">
<button class="hider-btn-small btn-green" id="add-allowed-domain-btn" style="height:28px;padding:0 8px;">Allow</button>
</div>
<div>
<div style="font-size:10px;color:#34d399;font-weight:700;margin-top:4px;margin-bottom:2px;">🟢 Allowed Domains</div>
<div id="list-allowed-domains" style="display:flex;flex-direction:column;gap:3px;"></div>
</div>
</div>
</div>
` },
{ id: 'tab-logs', label: '📋 Logs', html: `
<div style="display:flex;justify-content:space-between;align-items:center;">
<span style="font-size:10px;color:#94a3b8;">Blocked (<span id="cnt-logs">0</span>)</span>
<button class="hider-btn-small btn-gray" id="clear-log-btn" style="font-size:8px;padding:1px 5px;height:18px;">Clear</button>
</div>
<div id="list-blocked-log" style="display:flex;flex-direction:column;gap:3px;margin-top:4px;"></div>
` },
{ id: 'tab-export', label: '💾 Export', html: `
<div style="display:flex;gap:4px;flex-wrap:wrap;align-items:center;">
<button class="hider-btn-small btn-blue" id="btn-export-settings" style="flex:1;min-width:70px;height:26px;">📤 Export All</button>
<button class="hider-btn-small btn-green" id="btn-import-settings" style="flex:1;min-width:70px;height:26px;">📥 Import All</button>
<input type="file" id="import-file-input" accept=".json" style="display:none;">
</div>
<div style="font-size:9px;color:#94a3b8;margin-top:4px;">Export/Import all settings (rules, domains, toggles, logs).</div>
<div style="margin-top:8px;border-top:1px solid rgba(255,255,255,0.06);padding-top:6px;">
<div style="font-size:10px;color:#94a3b8;margin-bottom:4px;">🍪 Cookie Manager</div>
<div style="display:flex;gap:4px;margin-bottom:4px;">
<button class="hider-btn-small btn-blue" id="cookie-refresh" style="flex:1;height:24px;">🔄 Refresh</button>
<button class="hider-btn-small btn-green" id="cookie-export" style="flex:1;height:24px;">📤 Export</button>
<button class="hider-btn-small btn-purple" id="cookie-import" style="flex:1;height:24px;">📥 Import</button>
<input type="file" id="cookie-import-file" accept=".json" style="display:none;">
</div>
<div style="display:flex;gap:4px;margin-bottom:4px;">
<input type="text" id="cookie-new-name" placeholder="name" style="flex:1;background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.14);border-radius:6px;color:#fff;padding:2px 6px;font-size:9px;height:22px;">
<input type="text" id="cookie-new-value" placeholder="value" style="flex:1;background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.14);border-radius:6px;color:#fff;padding:2px 6px;font-size:9px;height:22px;">
<button class="hider-btn-small btn-green" id="cookie-add" style="height:22px;padding:0 8px;">Add</button>
</div>
<div id="cookie-list" style="display:flex;flex-direction:column;gap:3px;max-height:180px;overflow-y:auto;"></div>
</div>
` },
{ id: 'tab-about', label: 'ℹ️ About', html: `
<div style="font-size:10px;color:#cbd5e1;line-height:1.6;padding:4px 0;">
<h3 style="font-size:11px;font-weight:800;color:#f8fafc;margin:0 0 6px 0;">🛡️ Hide Web Elements Pro</h3>
<p> A powerful element hider with advanced navigation control, media extraction, and automatic challenge protection.</p>
<h4 style="font-size:10px;font-weight:700;color:#94a3b8;margin:8px 0 4px 0;">✨ Core Features</h4>
<ul style="margin:0;padding-left:16px;">
<li><strong>🎯 Hide</strong> – Click any element to hide it (site‑wide or page‑only).</li>
<li><strong>❄️ Freeze</strong> – Block or allow navigation popups/redirects with fine‑grained rules.</li>
<li><strong>🔗 Link Extractor</strong> – Scan the page for all links, with media preview and download.</li>
<li><strong>⏩ Time Skipper</strong> – Jump forward 30 seconds on videos/audio and skip timers.</li>
<li><strong>👁️ Reveal</strong> – Unhide hidden/blurred elements (overlays, modals, etc.).</li>
<li><strong>📥 Media Downloader</strong> – Direct download of images, videos, and audio from extracted links.</li>
<li><strong>🛡️ Automatic Protection</strong> – Detects Cloudflare/challenge pages and temporarily disables features to avoid interference.</li>
</ul>
<h4 style="font-size:10px;font-weight:700;color:#94a3b8;margin:8px 0 4px 0;">🔒 Protection Logic</h4>
<p>The script automatically pauses all hiding/freezing/scroll‑override features when it detects a Cloudflare challenge, "Just a moment..." page, or other anti‑bot screens. This ensures that the script never interferes with the challenge process. Once the page loads normally, features resume instantly.</p>
<h4 style="font-size:10px;font-weight:700;color:#94a3b8;margin:8px 0 4px 0;">💾 Persistent Storage</h4>
<p>All rules, domains, freeze settings, and logs are saved locally (via GM_setValue or localStorage). Your customisations persist across browser restarts and site visits.</p>
<h4 style="font-size:10px;font-weight:700;color:#94a3b8;margin:8px 0 4px 0;">⌨️ Keyboard Shortcuts</h4>
<ul style="margin:0;padding-left:16px;">
<li><kbd>Esc</kbd> – Cancel selection mode / close panels.</li>
<li><kbd>Ctrl+Click</kbd> / <kbd>Middle‑click</kbd> – Bypass freeze for that link.</li>
</ul>
<p style="margin-top:8px;font-size:9px;color:#64748b;">Made with ❤️ by KTZ • Open Source under MIT</p>
</div>
` }
];
tabs.forEach((tab, index) => {
const btn = document.createElement('button');
btn.className = 'tab-btn' + (index === 0 ? ' active' : '');
btn.dataset.tab = tab.id;
btn.textContent = tab.label;
btn.title = tab.label;
sidebar.appendChild(btn);
const contentDiv = document.createElement('div');
contentDiv.className = 'tab-content' + (index === 0 ? ' active' : '');
contentDiv.id = tab.id;
contentDiv.innerHTML = tab.html;
content.appendChild(contentDiv);
});
sidebar.addEventListener('click', (e) => {
const btn = e.target.closest('.tab-btn');
if (!btn) return;
const tabId = btn.dataset.tab;
sidebar.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
content.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
btn.classList.add('active');
const contentDiv = content.querySelector('#' + tabId);
if (contentDiv) contentDiv.classList.add('active');
});
}
// ---------- Shadow UI Events ----------
function setupShadowUIEvents() {
shadowBy('close-p').onclick = e => { if (e) { e.stopPropagation(); e.preventDefault(); } turnOffHideMode(); closeAllMenus(e, true); };
setupCustomDropdown(shadowBy('freeze-custom-panel-dropdown'), CACHE.freezeMemory, val => {
CACHE.freezeMemory = val; sv('hider_freeze_memory', val);
const label = FREEZE_LABELS[val] || val;
if (val === 'allow_all') { disableFreezeMode(); showToast(`🟢 Freeze Mode Disabled (Allow All)`); }
else showToast(`❄️ Memory updated: ${label}`);
});
shadowBy('btn-reset-block-confirm').onclick = () => { sv('hider_skip_block_confirm', false); showToast('🔄 Reset Block Domain confirmation!'); };
const chkScroll = shadowBy('chk-auto-scroll');
if (chkScroll) {
chkScroll.onchange = e => {
CACHE.autoScroll = e.target.checked;
sv('hider_auto_scroll', CACHE.autoScroll);
if (CACHE.autoScroll) {
startScrollDefeater();
showToast('🔓 Auto Anti-Scroll Lock enabled');
} else {
stopScrollDefeater();
showToast('🔒 Auto Anti-Scroll Lock disabled');
}
};
}
const chkContext = shadowBy('chk-enable-contextmenu');
if (chkContext) {
chkContext.onchange = e => {
CACHE.enableContextMenu = e.target.checked;
sv('hider_enable_contextmenu', CACHE.enableContextMenu);
updateContextMenuStyles();
showToast(`🖱️ Auto Right-Click/Long-Press: ${CACHE.enableContextMenu ? 'ON' : 'OFF'}`);
};
}
const chkBlur = shadowBy('chk-auto-remove-blur');
if (chkBlur) {
chkBlur.onchange = e => {
CACHE.autoRemoveBlur = e.target.checked;
sv('hider_auto_remove_blur', CACHE.autoRemoveBlur);
if (CACHE.autoRemoveBlur) {
scheduleBlurRemoval();
showToast('👁️ Auto Remove Blur enabled (aggressive)');
} else {
stopBlurRemoval();
showToast('👁️ Auto Remove Blur disabled');
}
};
}
const autoSkipChk = shadowBy('chk-auto-time-skipper');
if (autoSkipChk) {
autoSkipChk.onchange = e => {
CACHE.autoTimeSkipper = e.target.checked;
sv('hider_auto_time_skipper', CACHE.autoTimeSkipper);
if (CACHE.autoTimeSkipper) {
autoSkipTimers();
showToast('⏩ Auto Time Skipper enabled');
} else {
showToast('⏩ Auto Time Skipper disabled');
}
};
}
const dockEl = shadowBy('hider-main-dock'), mainBtn = shadowBy('btn-toggle-dock');
const savedY = gv('hider_dock_y', null); if (savedY) dockEl.style.top = savedY;
dockEl.addEventListener('mouseleave', () => dockEl.classList.remove('manual-hidden'));
let dockStartX, dockStartY, dockInitY, dockMoved = false;
const onDockMove = e => {
if (!isDraggingDock) return;
const p = e.touches ? e.touches[0] : e, dy = p.clientY - dockStartY;
if (Math.hypot(p.clientX - dockStartX, dy) > 8) {
dockMoved = true; if (e.cancelable) e.preventDefault();
dockEl.style.top = `${Math.max(0, Math.min(dockInitY + dy, win.innerHeight - dockEl.offsetHeight))}px`;
}
};
const onDockEnd = () => {
if (isDraggingDock) { isDraggingDock = false; dockEl.style.transition = ''; if (dockMoved) sv('hider_dock_y', dockEl.style.top); }
win.removeEventListener('mousemove', onDockMove); win.removeEventListener('mouseup', onDockEnd);
win.removeEventListener('touchmove', onDockMove); win.removeEventListener('touchend', onDockEnd); win.removeEventListener('touchcancel', onDockEnd);
};
const onDockStart = e => {
if (e.target.tagName === 'BUTTON' && e.target.id !== 'btn-toggle-dock') return;
const p = e.touches ? e.touches[0] : e;
dockStartX = p.clientX; dockStartY = p.clientY; dockInitY = dockEl.getBoundingClientRect().top;
isDraggingDock = true; dockMoved = false; dockEl.style.transition = 'none';
win.addEventListener('mousemove', onDockMove, { passive: false }); win.addEventListener('mouseup', onDockEnd);
win.addEventListener('touchmove', onDockMove, { passive: false }); win.addEventListener('touchend', onDockEnd); win.addEventListener('touchcancel', onDockEnd);
};
mainBtn.addEventListener('mousedown', onDockStart, { passive: false });
mainBtn.addEventListener('touchstart', onDockStart, { passive: false });
mainBtn.onclick = e => {
e.stopPropagation(); if (dockMoved) { dockMoved = false; return; }
if (collapseTimer) { clearTimeout(collapseTimer); collapseTimer = null; }
const menu = shadowBy('hider-dock-menu'), isOpening = !menu.classList.contains('is-open');
menu.classList.toggle('is-open', isOpening);
mainBtn.classList.toggle('expanded', isOpening);
dockEl.classList.toggle('expanded', isOpening);
dockEl.classList.toggle('is-collapsed', !isOpening);
if (!isOpening) {
turnOffHideMode();
const panel = shadowBy('hider-panel');
if (panel.classList.contains('is-visible')) { panel.classList.remove('is-visible'); setTimeout(()=> panel.style.display='none', 300); }
shadowBy('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');
};
shadowBy('btn-reveal-quick').onclick = (e) => {
e.stopPropagation();
revealHiddenElements();
};
shadowBy('btn-links').onclick = (e) => {
e.stopPropagation();
toggleLinkPanel();
};
shadowBy('btn-skip-30').onclick = (e) => {
e.stopPropagation();
skip30Seconds();
};
shadowBy('save-rule-btn').onclick = () => {
const ruleInput = shadowBy('manual-rule-input'), targetInput = shadowBy('manual-target-input'), sel = (ruleInput.value || '').trim(), 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: target }; showToast('✏️ Rule updated'); }
editingRuleId = null; shadowBy('save-rule-btn').textContent = 'Save';
} else { CACHE.customRules.push({ id: 'rule_' + Date.now(), selector: sel, target: target }); showToast('✨ Custom Rule Added!'); }
sv('hider_custom_rules_v4', CACHE.customRules); ruleInput.value = ''; targetInput.value = ''; requestUpdateStyles(); renderList();
};
const addAllowedDomain = () => {
const input = shadowBy('manual-allowed-domain-input'), d = cleanDomain(input.value);
if (d && !CACHE.allowedDomainsSet.has(d)) { CACHE.allowedDomainsList.push(d); CACHE.allowedDomainsSet.add(d); sv('hider_allowed_domains', CACHE.allowedDomainsList); showToast(`🟢 Allowed: ${d}`); }
input.value = ''; renderList();
};
shadowBy('add-allowed-domain-btn').onclick = addAllowedDomain; shadowBy('manual-allowed-domain-input').onkeypress = e => e.key === 'Enter' && addAllowedDomain();
const addDomain = () => {
const input = shadowBy('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();
};
shadowBy('add-domain-btn').onclick = addDomain; shadowBy('manual-domain-input').onkeypress = e => e.key === 'Enter' && addDomain();
shadowBy('btn-select').onclick = e => {
e.stopPropagation(); isSelecting = !isSelecting; e.currentTarget.classList.toggle('active', isSelecting);
const p = shadowBy('hider-panel');
if (isSelecting) {
if(p.classList.contains('is-visible')) { p.classList.remove('is-visible'); setTimeout(()=> p.style.display='none', 300); } shadowBy('btn-manage').classList.remove('active');
showToast('🎯 Selection mode ON – Click element to hide');
} else {
clearSelectionState();
showToast('🎯 Selection mode OFF');
}
broadcastState();
};
const btnScope = shadowBy('btn-scope');
btnScope.onclick = e => {
e.stopPropagation(); currentScope = currentScope === 'site' ? 'link' : 'site'; btnScope.querySelector('span').textContent = currentScope.toUpperCase();
btnScope.classList.toggle('scope-link', currentScope === 'link');
const label = currentScope === 'site' ? '🌐 Site-wide' : '📄 Page-only';
showToast(`Scope: ${label}`); broadcastState();
};
const btnFreeze = shadowBy('btn-freeze');
btnFreeze.onclick = e => {
e.stopPropagation(); isFrozen = !isFrozen; CACHE.isFrozen = isFrozen; sv('hider_freeze_global', isFrozen);
if (isFrozen && CACHE.freezeMemory === 'allow_all') { CACHE.freezeMemory = 'ask'; sv('hider_freeze_memory', 'ask'); }
btnFreeze.classList.toggle('is-frozen', isFrozen); if (isFrozen) triggerFreezeFx();
showToast(isFrozen ? '🥶 Freeze ON' : '🔥 Freeze OFF'); broadcastState();
};
shadowBy('btn-manage').onclick = e => {
e.stopPropagation(); const p = shadowBy('hider-panel'), isHidden = p.style.display === 'none' || p.style.display === '';
if (isHidden) {
p.style.display = 'flex';
void p.offsetWidth;
p.classList.add('is-visible');
e.currentTarget.classList.add('active');
turnOffHideMode(); renderList();
} else {
p.classList.remove('is-visible');
setTimeout(() => p.style.display = 'none', 300);
e.currentTarget.classList.remove('active');
}
};
shadowBy('clear-log-btn').onclick = e => { if (e) e.stopPropagation(); CACHE.logs = []; sv('hider_global_logs', []); renderList(); showToast('🧹 Logs cleared'); };
// Export/Import
const exportBtn = shadowBy('btn-export-settings');
const importBtn = shadowBy('btn-import-settings');
const fileInput = shadowBy('import-file-input');
if (exportBtn) exportBtn.onclick = () => {
const data = getAllSettings();
const json = JSON.stringify(data, null, 2);
const blob = new Blob([json], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `hider_backup_${new Date().toISOString().slice(0,10)}.json`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
showToast('📤 Settings exported');
};
if (importBtn) importBtn.onclick = () => fileInput?.click();
if (fileInput) {
fileInput.onchange = function() {
if (this.files && this.files[0]) {
const reader = new FileReader();
reader.onload = function(e) {
try {
const data = JSON.parse(e.target.result);
let count = 0;
for (const [key, val] of Object.entries(data)) {
if (key.startsWith('hider_')) {
sv(key, val);
count++;
}
}
syncCache();
requestUpdateStyles();
renderList();
applyDockButtonVisibility();
showToast(`📥 Imported ${count} settings`);
} catch (err) {
showToast('❌ Invalid JSON file');
}
};
reader.readAsText(this.files[0]);
this.value = '';
}
};
}
// Cookie Manager
const cookieRefresh = shadowBy('cookie-refresh');
const cookieExport = shadowBy('cookie-export');
const cookieImport = shadowBy('cookie-import');
const cookieFileInput = shadowBy('cookie-import-file');
const cookieAdd = shadowBy('cookie-add');
const cookieName = shadowBy('cookie-new-name');
const cookieValue = shadowBy('cookie-new-value');
function getCookies() {
return document.cookie.split(';').map(c => c.trim()).filter(Boolean).map(c => {
const eq = c.indexOf('=');
return { name: c.slice(0, eq), value: c.slice(eq + 1) };
});
}
function renderCookies() {
const list = shadowBy('cookie-list');
if (!list) return;
const cookies = getCookies();
if (cookies.length === 0) {
list.innerHTML = '<div style="font-size:9px;color:#94a3b8;text-align:center;padding:4px;">No cookies for this domain.</div>';
return;
}
list.innerHTML = '';
cookies.forEach((c, idx) => {
const row = document.createElement('div');
row.style.cssText = 'display:flex;align-items:center;gap:4px;font-size:9px;padding:2px 4px;background:rgba(255,255,255,.04);border-radius:4px;';
const nameSpan = document.createElement('span');
nameSpan.textContent = c.name;
nameSpan.style.cssText = 'font-weight:700;color:#38bdf8;min-width:60px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;';
const valSpan = document.createElement('span');
valSpan.textContent = c.value;
valSpan.style.cssText = 'flex:1;color:#e2e8f0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;';
const btnGroup = document.createElement('div');
btnGroup.style.cssText = 'display:flex;gap:2px;flex-shrink:0;';
const editBtn = document.createElement('button');
editBtn.className = 'hider-btn-small btn-blue';
editBtn.textContent = '✏️';
editBtn.style.cssText = 'padding:0 4px;height:16px;font-size:7px;';
editBtn.onclick = function(e) {
e.stopPropagation();
const newVal = prompt(`Edit value for "${c.name}":`, c.value);
if (newVal !== null) {
document.cookie = `${c.name}=${newVal}; path=/; domain=${location.hostname}`;
renderCookies();
showToast(`🍪 Updated cookie: ${c.name}`);
}
};
const delBtn = document.createElement('button');
delBtn.className = 'hider-btn-small btn-red';
delBtn.textContent = '✖';
delBtn.style.cssText = 'padding:0 4px;height:16px;font-size:7px;';
delBtn.onclick = function(e) {
e.stopPropagation();
if (confirm(`Delete cookie "${c.name}"?`)) {
document.cookie = `${c.name}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain=${location.hostname}`;
renderCookies();
showToast(`🍪 Deleted cookie: ${c.name}`);
}
};
btnGroup.appendChild(editBtn);
btnGroup.appendChild(delBtn);
row.appendChild(nameSpan);
row.appendChild(valSpan);
row.appendChild(btnGroup);
list.appendChild(row);
});
}
if (cookieAdd) {
cookieAdd.onclick = function() {
const name = cookieName ? cookieName.value.trim() : '';
const value = cookieValue ? cookieValue.value.trim() : '';
if (!name) { showToast('⚠️ Enter cookie name'); return; }
document.cookie = `${name}=${value}; path=/; domain=${location.hostname}`;
renderCookies();
if (cookieName) cookieName.value = '';
if (cookieValue) cookieValue.value = '';
showToast(`🍪 Added cookie: ${name}`);
};
}
if (cookieName) cookieName.onkeypress = e => e.key === 'Enter' && cookieAdd?.click();
if (cookieValue) cookieValue.onkeypress = e => e.key === 'Enter' && cookieAdd?.click();
if (cookieRefresh) cookieRefresh.onclick = renderCookies;
if (cookieExport) {
cookieExport.onclick = function() {
const cookies = getCookies();
const json = JSON.stringify(cookies, null, 2);
const blob = new Blob([json], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `cookies_${location.hostname}_${new Date().toISOString().slice(0,10)}.json`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
showToast('🍪 Cookies exported');
};
}
if (cookieImport) cookieImport.onclick = () => cookieFileInput?.click();
if (cookieFileInput) {
cookieFileInput.onchange = function() {
if (this.files && this.files[0]) {
const reader = new FileReader();
reader.onload = function(e) {
try {
const cookies = JSON.parse(e.target.result);
if (!Array.isArray(cookies)) throw new Error('Not an array');
cookies.forEach(c => {
if (c.name && c.value !== undefined) {
document.cookie = `${c.name}=${c.value}; path=/; domain=${location.hostname}`;
}
});
renderCookies();
showToast(`🍪 Imported ${cookies.length} cookies`);
} catch (err) {
showToast('❌ Invalid cookie JSON');
}
};
reader.readAsText(this.files[0]);
this.value = '';
}
};
}
function getAllSettings() {
const settings = {};
const knownKeys = [
'hider_freeze_memory', 'hider_freeze_global', 'hider_blocked_domains',
'hider_allowed_domains', 'hider_custom_rules_v4', 'hider_auto_time_skipper',
'hider_auto_scroll', 'hider_enable_contextmenu', 'hider_auto_remove_blur',
'hider_hidden_dock_buttons', 'hider_global_logs',
'hider_last_log_clear_day', 'hider_skip_block_confirm', 'hider_dock_y'
];
knownKeys.forEach(k => {
const val = gv(k, undefined);
if (val !== undefined) settings[k] = val;
});
try {
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (key && (key.startsWith('hider_site_') || key.startsWith('hider_link_'))) {
const val = localStorage.getItem(key);
try { settings[key] = JSON.parse(val); } catch { settings[key] = val; }
}
}
} catch {}
return settings;
}
}
// ---------- Render List ----------
function renderList() {
if (!shadowRoot) return;
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 = shadowBy('list-custom-rules'), cAllowed = shadowBy('list-allowed-domains'), cDomains = shadowBy('list-blocked-domains'), cSite = shadowBy('list-site'), cLink = shadowBy('list-link'), cLog = shadowBy('list-blocked-log');
if (!cSite || !cLink) return;
['cnt-custom', 'cnt-allowed', 'cnt-domains', 'cnt-logs', 'cnt-site', 'cnt-link'].forEach((id, i) => {
const el = shadowBy(id);
if(el) el.textContent = [CACHE.customRules.length, CACHE.allowedDomainsList.length, CACHE.blockedDomainsList.length, CACHE.logs.length, siteData.length, linkData.length][i];
});
const buildFrag = (arr, emptyMsg, renderItem) => {
const frag = doc.createDocumentFragment();
if (!arr.length) { const d = doc.createElement('div'); d.style.cssText = 'font-size:9px!important;color:#94a3b8!important;padding:4px!important;text-align:center!important;border:1px dashed rgba(255,255,255,0.12)!important;border-radius:6px!important;'; d.textContent = emptyMsg; frag.appendChild(d); return frag; }
arr.forEach((item, i) => frag.appendChild(renderItem(item, i))); return frag;
};
if (cCustom) {
cCustom.innerHTML = '';
cCustom.appendChild(buildFrag(CACHE.customRules, 'No custom rules', rule => {
const div = doc.createElement('div'); div.className = 'list-item';
div.innerHTML = `<span class="rule-text" title="${rule.selector}">${rule.selector} <span style="color:${rule.target === '*' ? '#38bdf8' : '#34d399'}!important; font-weight:bold!important;">[${rule.target}]</span></span><div style="display:flex;gap:2px"><button class="hider-btn-small btn-blue btn-edit">✏️</button><button class="hider-btn-small btn-red btn-del">✖</button></div>`;
div.querySelector('.btn-edit').onclick = () => {
editingRuleId = rule.id; shadowBy('manual-rule-input').value = rule.selector; shadowBy('manual-target-input').value = rule.target; shadowBy('save-rule-btn').textContent = 'Update';
const sidebar = shadowBy('panel-sidebar');
const content = shadowBy('panel-content');
if (sidebar && content) {
const tabBtn = sidebar.querySelector('[data-tab="tab-rules"]');
if (tabBtn) tabBtn.click();
}
};
div.querySelector('.btn-del').onclick = () => {
CACHE.customRules = CACHE.customRules.filter(r => r.id !== rule.id);
sv('hider_custom_rules_v4', CACHE.customRules);
requestUpdateStyles(); renderList();
showToast('🗑️ Rule removed');
};
return div;
}));
}
if (cAllowed) {
cAllowed.innerHTML = '';
cAllowed.appendChild(buildFrag(CACHE.allowedDomainsList, 'No allowed domains', (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 btn-red">✖</button>`;
div.querySelector('button').onclick = () => {
CACHE.allowedDomainsList.splice(i, 1);
CACHE.allowedDomainsSet.delete(cleanDomain(d));
sv('hider_allowed_domains', CACHE.allowedDomainsList);
renderList();
showToast('🟢 Allowed domain removed');
};
return div;
}));
}
if (cDomains) {
cDomains.innerHTML = '';
cDomains.appendChild(buildFrag(CACHE.blockedDomainsList, 'No blocked domains', (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 btn-red">✖</button>`;
div.querySelector('button').onclick = () => {
CACHE.blockedDomainsList.splice(i, 1);
CACHE.blockedDomainsSet.delete(cleanDomain(d));
sv('hider_blocked_domains', CACHE.blockedDomainsList);
renderList();
showToast('🚫 Blocked domain removed');
};
return div;
}));
}
if (cLog) {
cLog.innerHTML = '';
cLog.appendChild(buildFrag(CACHE.logs, 'No logs today', item => {
const row = doc.createElement('div'); row.style.cssText = 'font-size:9px!important;border-bottom:1px solid rgba(255,255,255,0.08)!important;padding:2px 0!important;';
row.innerHTML = `<div style="display:flex;justify-content:space-between;color:#6ee7b7!important;font-weight:700!important;"><span>${item.type}</span><span style="color:#94a3b8!important;font-weight:normal!important;">${item.time}</span></div><div style="color:#cbd5e1!important;word-break:break-all!important;font-family:monospace!important;opacity:0.8;">${item.url}</div>`; return row;
}));
}
const renderEditableHiddenItems = (data, key, container) => {
container.innerHTML = '';
container.appendChild(buildFrag(data, 'None', (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-blue btn-edit" title="Edit Rule Inline">✏️</button><button class="hider-btn-small btn-purple btn-wild" title="Convert Wildcard">🪄</button><button class="hider-btn-small btn-red btn-del" title="Delete Rule">✖</button></div>`;
const textSpan = div.querySelector('.rule-text'), 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!important;padding:2px 4px!important;height:20px!important;flex:1!important;margin-right:4px!important;';
const saveBtn = doc.createElement('button'); saveBtn.className = 'hider-btn-small btn-green'; 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); requestUpdateStyles(); 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); requestUpdateStyles(); 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 {}
requestUpdateStyles(); renderList();
showToast('🗑️ Hidden rule removed');
};
return div;
}));
};
renderEditableHiddenItems(siteData, siteKey, cSite); renderEditableHiddenItems(linkData, linkKey, cLink);
renderDockButtonOptions();
applyDockButtonVisibility();
}
// ---------- Navigation Click Handler ----------
function handleNavigationClick(e) {
const path = e.composedPath?.() || [];
if (isSelecting || path.some(el => el.id === UI_HOST_ID || el.id === STEPPER_BAR_ID)) return;
const link = e.target.closest('a[href], area[href]');
if (link?.href) {
const rawHref = link.getAttribute('href') || '';
if (rawHref.startsWith('#') || rawHref.toLowerCase().startsWith('javascript:')) return;
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 || isDomainAllowed(targetUrl)) 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);
}
}
// ---------- Scroll auto-close (also closes preview) ----------
function setupScrollAutoClose() {
win.addEventListener('scroll', () => {
if (scrollAnimationFrame) return;
scrollAnimationFrame = win.requestAnimationFrame(() => {
scrollAnimationFrame = null;
closeAllMenus(null);
closePreviewModal();
});
}, { passive: true, capture: true });
}
// ---------- Keyboard shortcuts ----------
window.addEventListener('keydown', e => { if (e.key === 'Escape') { if (isSelecting) { turnOffHideMode(); showToast('🎯 Selection mode cancelled'); } else { closeAllMenus(null, true); closePreviewModal(); } } }, true);
// ---------- Selection mode blockers ----------
['mousedown', 'pointerdown', 'mouseup'].forEach(evtType => {
window.addEventListener(evtType, e => {
if (!isSelecting) return;
if (e.composedPath?.().some(el => el.id === UI_HOST_ID || el.id === STEPPER_BAR_ID)) return;
e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation();
}, true);
});
// ---------- Click handling for selection ----------
window.addEventListener('click', e => {
const path = e.composedPath?.() || [];
const isUI = path.some(el => el.id === UI_HOST_ID || el.id === STEPPER_BAR_ID);
if (!isUI) { closeAllMenus(e); shadowRoot?.querySelectorAll('.h-custom-select').forEach(c => c.classList.remove('is-open')); }
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 => {
const path = e.composedPath?.() || [];
if (path.some(el => el.id === UI_HOST_ID || el.id === 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 || isDomainAllowed(action)) return;
e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation();
handleFreezeNavigation(action, 'Form Submit', () => { userApprovedNavigation = true; form.submit(); setTimeout(() => userApprovedNavigation = false, 300); }, simulateAdWindowSuccess);
}, true);
window.addEventListener('touchstart', e => {
if (!e.touches?.[0]) return;
const x = e.touches[0].clientX;
if (x <= 40 || x >= (win.innerWidth - 40)) { userApprovedNavigation = true; setTimeout(() => { userApprovedNavigation = false; }, 1500); }
}, { passive: true, capture: true });
// ---------- History navigation ----------
['back', 'forward', 'go'].forEach(method => {
const orig = history[method];
if (orig) { history[method] = function() { userApprovedNavigation = true; setTimeout(() => { userApprovedNavigation = false; }, 1000); return orig.apply(this, arguments); }; }
});
try { win.navigation?.addEventListener('navigate', e => { if (e.navigationType === 'traverse' || e.navigationType === 'reload') { userApprovedNavigation = true; setTimeout(() => { userApprovedNavigation = false; }, 1000); } }); } catch {}
win.addEventListener('beforeunload', e => {
if (userApprovedNavigation) return;
if (isFrozen && !isDomainAllowed(location.href) && (CACHE.freezeMemory === 'ask' || CACHE.freezeMemory === 'block_all')) { e.preventDefault(); return (e.returnValue = 'Page navigation is currently frozen site-wide.'); }
}, true);
// ---------- URL change detection ----------
const checkUrlChange = () => {
if (location.href !== lastUrl) {
lastUrl = location.href;
updateCurrentLocCache();
requestUpdateStyles();
if (shadowBy('hider-panel')?.classList.contains('is-visible')) renderList();
}
};
['pushState', 'replaceState'].forEach(fn => { const orig = history[fn]; if (orig) history[fn] = function() { orig.apply(this, arguments); checkUrlChange(); }; });
window.addEventListener('popstate', () => { userApprovedNavigation = true; checkUrlChange(); setTimeout(() => { userApprovedNavigation = false; }, 500); }, { passive: true });
window.addEventListener('hashchange', checkUrlChange, { passive: true });
window.addEventListener('pageshow', () => { userApprovedNavigation = false; checkUrlChange(); }, { passive: true });
// ---------- Low-power heartbeat ----------
function setupLowPowerHeartbeat() {
const scheduleIdle = fn => (win.requestIdleCallback ? win.requestIdleCallback(fn, { timeout: 2000 }) : setTimeout(fn, 1000));
const performHealthCheck = () => {
if (isTop && !doc.getElementById(UI_HOST_ID)) createShadowUI();
if (!doc.getElementById('hider-dynamic-styles')) requestUpdateStyles();
if (!isTop) try { window.top.postMessage({ type: 'HIDER_REQUEST_STATE' }, '*'); } catch {}
scheduleIdle(performHealthCheck);
};
scheduleIdle(performHealthCheck);
}
// ---------- Init ----------
function init() {
updateCurrentLocCache();
installInterceptors();
createShadowUI();
requestUpdateStyles();
setupScrollAutoClose();
setupLowPowerHeartbeat();
setupProtectionObserver();
applyAllSettings();
if (CACHE.autoTimeSkipper && featuresEnabled) {
setTimeout(autoSkipTimers, 500);
}
}
if (doc.readyState === 'loading') doc.addEventListener('DOMContentLoaded', init); else init();
})();