AdBlock

Elimina TODOS los anuncios de cualquier página web (incluyendo popups, banners, videos, overlays y más) - Versión Mega Avanzada

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

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

(I already have a user script manager, let me install it!)

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

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

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

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

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

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

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         AdBlock
// @namespace    https://github.com/ondarion-t
// @version      1.1
// @description  Elimina TODOS los anuncios de cualquier página web (incluyendo popups, banners, videos, overlays y más) - Versión Mega Avanzada
// @author       KaitoNeko & Gemini
// @match        *://*/*
// @icon         https://i.ibb.co/8D277XCT/fa2e10c5aa9a.png
// @license      MPL-2.0
// @grant        GM_addStyle
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @grant        GM_xmlhttpRequest
// @grant        GM_notification
// @connect      *
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';
    /* Configuración avanzada */
    let config = { /* Cambiado a let para permitir la fusión con la configuración guardada */
        debugMode: false,
        aggressiveMode: true,
        updateInterval: 3600, /* 1 hora en segundos */
        lastUpdate: 0, /* Se actualizará desde GM_getValue o se forzará la primera vez */
        filterLists: [
            'https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/filters.txt',
            'https://easylist.to/easylist/easylist.txt',
            'https://easylist.to/easylist/easyprivacy.txt',
            'https://secure.fanboy.co.nz/fanboy-annoyance.txt',
            'https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&showintro=0&mimetype=plaintext',
            'https://raw.githubusercontent.com/AdguardTeam/AdguardFilters/master/EnglishFilter/sections/general_optimized.txt',
            'https://raw.githubusercontent.com/AdguardTeam/AdguardFilters/master/SpywareFilter/sections/spyware_optimized.txt',
            'https://raw.githubusercontent.com/AdguardTeam/AdguardFilters/master/SocialMediaFilter/sections/socialmedia_optimized.txt',
            'https://raw.githubusercontent.com/AdguardTeam/AdguardFilters/master/AnnoyancesFilter/sections/annoyances_optimized.txt'
        ],
        customFilters: [],
        whitelist: [], /* Se actualizará desde GM_getValue */
        stats: {
            adsBlocked: 0,
            elementsRemoved: 0,
            scriptsBlocked: 0,
            requestsBlocked: 0
        }
    };

    /* Estilos CSS para bloquear elementos (ampliados y más agresivos) */
    /* Esta lista es extensa y se aplica directamente. Se complementa con selectores dinámicos. */
    const blockingStyles = `
        /* Clases y IDs comunes de anuncios */
        .ad, .ads, .advert, .advertisement, .ad-banner, .ad-container, .ad-wrapper, .ad-slot, .ad-unit,
        .google-ad, .doubleclick-ad, .banner-ad, .text-ad, .video-ad, .popup-ad, .interstitial-ad,
        .sponsored-post, .promoted-content, .ad-placeholder, .ad-label, .ad-notice, .ad-choices,
        [id*="ad"], [class*="ad-"], [class*="-ad"], [id*="google_ads_"], [id*="gpt_ad_"],
        [class*="adsbygoogle"], [class*="amzn_ad"], [class*="OUTBRAIN"], [class*="taboola"],
        [class*="criteo"], [class*="revcontent"], [class*="mgid"], [class*="adthrive"],
        [class*="ezoic"], [class*="mediavine"], [class*="adroll"], [class*="carbonads"],
        [class*="buysellads"], [class*="adblade"], [class*="adsonar"], [class*="infolinks"],
        [class*="chitika"], [class*="kontera"], [class*="viglink"], [class*="skimlinks"],
        [aria-label*="ad"], [aria-label*="advertisement"], [aria-label*="sponsored"],
        [data-ad-format], [data-ad-layout-key], [data-ad-client], [data-ad-slot], [data-ad-unit],
        [data-google-query-id], [data-ad-manager], [data-ad-name], [data-ad-size],
        [data-ad-targeting], [data-ad-type], [data-ad-id], [data-ad-index], [data-ad-position],
        [data-ad-network], [data-ad-placement], [data-ad-status], [data-ad-impression-url"],
        [data-ad-click-url"], [data-ad-viewability-url"], [data-ad-error-url"],
        [data-ad-visibility-observer"], [data-ad-responsive"], [data-ad-lazy"],
        [data-ad-load"], [data-ad-refresh"], [data-ad-render"], [data-ad-state"],
        [data-ad-debug"], [data-ad-test"], [data-ad-preview"], [data-ad-config"],
        [data-ad-options"], [data-ad-settings"], [data-ad-properties"], [data-ad-attributes"],
        [data-ad-metadata"], [data-ad-info"], [data-ad-details"], [data-ad-data"],
        [data-ad-json"], [data-ad-xml"], [data-ad-html"], [data-ad-script"],
        [data-ad-iframe"], [data-ad-embed"], [data-ad-object"], [data-ad-applet"],
        [data-ad-plugin"], [data-ad-flash"], [data-ad-silverlight"], [data-ad-java"],
        [data-ad-activex"], [data-ad-vpaid"], [data-ad-vast"], [data-ad-ima"],
        [data-ad-gpt"], [data-ad-dfp"], [data-ad-header-bidding"], [data-ad-prebid"],
        [data-ad-postbid"], [data-ad-waterfall"], [data-ad-mediation"], [data-ad-exchange"],
        [data-ad-marketplace"], [data-ad-auction"], [data-ad-bid"], [data-ad-cpm"],
        [data-ad-cpc"], [data-ad-ctr"], [data-ad-ecpm"], [data-ad-fill-rate"],
        [data-ad-impressions"], [data-ad-clicks"], [data-ad-conversions"], [data-ad-revenue"],
        [data-ad-profit"], [data-ad-roi"], [data-ad-performance"], [data-ad-analytics"],
        [data-ad-tracking"], [data-ad-pixel"], [data-ad-beacon"], [data-ad-tag"],
        [data-ad-container-id"], [data-ad-unit-path"], [data-ad-size-mapping"],
        [data-ad-targeting-map"], [data-ad-category-exclusions"], [data-ad-custom-targeting"],
        [data-ad-collapse-empty-div"], [data-ad-force-collapse"], [data-ad-override-width"],
        [data-ad-override-height"], [data-ad-override-display"], [data-ad-safeframe"],
        [data-ad-sandbox"], [data-ad-allow-same-origin"], [data-ad-allow-scripts"],
        [data-ad-allow-forms"], [data-ad-allow-popups"], [data-ad-allow-pointer-lock"],
        [data-ad-allow-top-navigation"], [data-ad-allow-modals"], [data-ad-allow-orientation-lock"],
        [data-ad-allow-presentation"], [data-ad-allow-fullscreen"], [data-ad-allow-payment-request"],
        [data-ad-allow-downloads"], [data-ad-allow-storage-access-by-user-activation"],
        [data-ad-allow-top-navigation-by-user-activation"], [data-ad-allow-popups-to-escape-sandbox"],
        [data-ad-allow-downloads-without-user-activation"],
        /* Selectores de iframes de anuncios */
        iframe[src*="ads"], iframe[src*="adserver"], iframe[src*="doubleclick.net"],
        iframe[src*="googlesyndication.com"], iframe[src*="googleads.g.doubleclick.net"],
        iframe[src*="adservice.google.com"], iframe[src*="amazon-adsystem.com"],
        iframe[src*="adnxs.com"], iframe[src*="criteo.com"], iframe[src*="rubiconproject.com"],
        iframe[src*="pubmatic.com"], iframe[src*="openx.net"], iframe[src*="smartadserver.com"],
        iframe[src*="yieldmo.com"], iframe[src*="bidswitch.net"], iframe[src*="adsrvr.org"],
        iframe[src*="taboola.com"], iframe[src*="outbrain.com"], iframe[src*="zedo.com"],
        iframe[src*="scorecardresearch.com"], iframe[src*="contextweb.com"], iframe[src*="media.net"],
        iframe[src*="adtech.com"], iframe[src*="advertising.com"], iframe[src*="serving-sys.com"],
        iframe[src*="eyeblaster.com"], iframe[src*="revsci.net"], iframe[src*="yieldlab.net"],
        iframe[src*="adform.net"], iframe[src*="appnexus.com"], iframe[src*="casalemedia.com"],
        iframe[src*="quantserve.com"], iframe[src*="turn.com"], iframe[src*="mathtag.com"],
        iframe[src*="sitescout.com"], iframe[src*="simpli.fi"], iframe[src*="fout.jp"],
        iframe[src*="amoad.com"], iframe[src*="genieessp.com"], iframe[src*="adstir.com"],
        iframe[src*="soom.cz"], iframe[src*="innovid.com"], iframe[src*="flashtalking.com"],
        iframe[src*="sizmek.com"], iframe[src*="adroll.com"], iframe[src*="sharethrough.com"],
        iframe[src*="lijit.com"], iframe[src*="districtm.io"], iframe[src*="indexexchange.com"],
        iframe[src*="gumgum.com"], iframe[src*="teads.tv"], iframe[src*="vidible.tv"],
        iframe[src*="spotxchange.com"], iframe[src*="brightroll.com"], iframe[src*="tremorhub.com"],
        iframe[src*="adap.tv"], iframe[src*="lkqd.net"], iframe[src*="stickyadstv.com"],
        iframe[src*="aniview.com"], iframe[src*="playtem.com"], iframe[src*="exoclick.com"],
        iframe[src*="juicyads.com"], iframe[src*="ero-advertising.com"], iframe[src*="plugrush.com"],
        iframe[src*="trafficjunky.net"], iframe[src*="trafficforce.com"], iframe[src*="traffichaus.com"],
        iframe[src*="zeropark.com"], iframe[src*="admaven.com"], iframe[src*="propellerads.com"],
        iframe[src*="popads.net"], iframe[src*="popcash.net"], iframe[src*="onclickads.net"],
        iframe[src*="onclicktop.com"], iframe[src*="ad-maven.com"], iframe[src*="adcash.com"],
        iframe[src*="exdynsrv.com"], iframe[src*="adk2.com"], iframe[src*="adxpansion.com"],
        iframe[src*="adsupply.com"], iframe[src*="adreactor.com"], iframe[src*="adkernel.com"],
        iframe[src*="adtarget.com"], iframe[src*="adverdirect.com"], iframe[src*="adversal.com"],
        iframe[src*="adxpedia.com"], iframe[src*="adxcore.com"], iframe[src*="adxvalue.com"],
        iframe[src*="adxpremium.com"], iframe[src*="adxmarket.com"], iframe[src*="adxpartner.com"],
        iframe[src*="adxnetwork.com"], iframe[src*="adxchange.com"], iframe[src*="adxapi.com"],
        iframe[src*="adxserve.com"], iframe[src*="adxflow.com"], iframe[src*="adxpower.com"],
        iframe[src*="adxengine.com"], iframe[src*="adxplatform.com"], iframe[src*="adxsystem.com"],
        iframe[src*="adxtech.com"], iframe[src*="adxmedia.com"], iframe[src*="adxcreative.com"],
        iframe[src*="adxoptimizer.com"], iframe[src*="adxmonetize.com"], iframe[src*="adxyield.com"],
        iframe[src*="adxperformance.com"], iframe[src*="adxanalytics.com"], iframe[src*="adxtracking.com"],
        iframe[src*="adxpixel.com"], iframe[src*="adxbeacon.com"], iframe[src*="adxtag.com"],
        iframe[src*="adxcontainer.com"], iframe[src*="adxunit.com"], iframe[src*="adxslot.com"],
        iframe[src*="adxframe.com"], iframe[src*="adxbanner.com"], iframe[src*="adxcontent.com"],
        iframe[src*="adxtext.com"], iframe[src*="adximage.com"], iframe[src*="adxvideo.com"],
        iframe[src*="adxsidebar.com"], iframe[src*="adxheader.com"], iframe[src*="adxfooter.com"],
        iframe[src*="adxsticky.com"], iframe[src*="adxfloating.com"], iframe[src*="adxnative.com"],
        iframe[src*="adxsponsored.com"], iframe[src*="adxchoices.com"], iframe[src*="adxfeed.com"],
        iframe[src*="adxg.com"], iframe[src*="adxform.com"], iframe[src*="adxblade.com"],
        iframe[src*="adxcell.com"], iframe[src*="adxition.com"], iframe[src*="adxengage.com"],
        iframe[src*="adxfactor.com"], iframe[src*="adxfusion.com"], iframe[src*="adxmagnet.com"],
        iframe[src*="adxmeta.com"], iframe[src*="adxmotion.com"], iframe[src*="adxroll.com"],
        iframe[src*="adxspirit.com"], iframe[src*="adxstir.com"], iframe[src*="adxswizz.com"],
        iframe[src*="adxtrade.com"], iframe[src*="adxverster.com"], iframe[src*="adxview.com"],
        iframe[src*="adxwave.com"], iframe[src*="adxworx.com"], iframe[src*="adxzerk.com"],
        iframe[src*="adxbutler.com"], iframe[src*="adxcolt.com"], iframe[src*="adxfox.com"],
        iframe[src*="adxfront.com"], iframe[src*="adxgear.com"], iframe[src*="adxgrs.com"],
        iframe[src*="adxhese.com"], iframe[src*="adxhood.com"], iframe[src*="adxinfuse.com"],
        iframe[src*="adxjuggler.com"], iframe[src*="adxlegend.com"], iframe[src*="adxman.com"],
        iframe[src*="adxmarvel.com"], iframe[src*="adxmaster.com"], iframe[src*="adxmedo.com"],
        iframe[src*="adxmicro.com"], iframe[src*="adxmost.com"], iframe[src*="adxnegah.com"],
        iframe[src*="adxnexus.com"], iframe[src*="adxnologies.com"], iframe[src*="adxoperator.com"],
        iframe[src*="adxorika.com"], iframe[src*="adxotomi.com"], iframe[src*="adxperfect.com"],
        iframe[src*="adxplugg.com"], iframe[src*="adxscale.com"], iframe[src*="adxshost.com"],
        iframe[src*="adxshuffle.com"], iframe[src*="adxspruce.com"], iframe[src*="adxstage.com"],
        iframe[src*="adxsteam.com"], iframe[src*="adxswapper.com"], iframe[src*="adxswich.com"],
        iframe[src*="adxsynergy.com"], iframe[src*="adxtegrity.com"], iframe[src*="adxthink.com"],
        iframe[src*="adxtoma.com"], iframe[src*="adxtruth.com"], iframe[src*="adxunity.com"],
        iframe[src*="adxup.com"], iframe[src*="adxverline.com"], iframe[src*="adxverticum.com"],
        iframe[src*="adxvertserve.com"],
        /* Elementos vacíos o muy pequeños que podrían ser placeholders */
        div:empty:not([class*="spacer"]):not([class*="divider"]):not([id*="spacer"]):not([id*="divider"]),
        span:empty, a:empty, p:empty,
        div[style*="height:1px"], div[style*="height: 1px"], div[style*="width:1px"], div[style*="width: 1px"],
        div[style*="height:0px"], div[style*="height: 0px"], div[style*="width:0px"], div[style*="width: 0px"],
        /* Popups y overlays */
        .modal-overlay, .popup-overlay, .lightbox-overlay, .fancybox-overlay,
        .modal-dialog, .popup-window, .lightbox-content, .fancybox-wrap,
        [id*="modal"], [id*="popup"], [id*="lightbox"], [id*="fancybox"],
        [class*="modal"], [class*="popup"], [class*="lightbox"], [class*="fancybox"],
        [role="dialog"], [role="alertdialog"],
        /* Elementos fijos o pegajosos que suelen ser anuncios */
        div[style*="position:fixed"][style*="bottom:0"], div[style*="position:fixed"][style*="top:0"],
        div[style*="position:sticky"][style*="bottom:0"], div[style*="position:sticky"][style*="top:0"],
        /* Selectores de YouTube (ejemplos, pueden necesitar ajustes) */
        ytd-promoted-sparkles-text-search-renderer, ytd-promoted-video-renderer,
        .ytd-display-ad-renderer, .ytd-promoted-sparkles-web-renderer,
        #masthead-ad, #player-ads, .video-ads,
        /* Selectores de Facebook (ejemplos) */
        div[data-pagelet*="FeedUnit_"], div[aria-label="Patrocinado"],
        /* Selectores de Twitter/X (ejemplos) */
        article[data-testid="tweet"][aria-labelledby*="social_context"],
        div[data-testid="placementTracking"],
        /* Selectores de Twitch (ejemplos) */
        .player-ad-overlay, .persistent-player--ad-showing,
        /* Selectores genéricos de "molestias" */
        .cookie-banner, .gdpr-consent, .newsletter-popup, .push-notification-prompt,
        .social-share-buttons.sticky, .floating-chat-widget, .interstitial-message,
        .full-page-takeover, .exit-intent-popup
         {
            display: none !important;
            visibility: hidden !important;
            opacity: 0 !important;
            height: 0 !important;
            width: 0 !important;
            max-height: 0 !important;
            max-width: 0 !important;
            overflow: hidden !important;
            margin: 0 !important;
            padding: 0 !important;
            border: none !important;
            position: absolute !important;
            left: -99999px !important;
            top: -99999px !important;
            pointer-events: none !important;
            z-index: -1 !important;
        }
        /* Para elementos que intentan sobreescribir el overflow */
        body[style*="overflow: hidden !important"], html[style*="overflow: hidden !important"] {
            overflow: auto !important;
        }
        body[style*="overflow-y: hidden !important"], html[style*="overflow-y: hidden !important"] {
            overflow-y: auto !important;
        }
    `;

    /* Aplicar estilos de bloqueo inmediatamente */
    GM_addStyle(blockingStyles);

    /* Función para bloquear scripts de anuncios */
    function blockAdScripts() {
        const scripts = document.querySelectorAll('script[src], script:not([src])');
        const adScriptKeywords = [
            'ad', 'ads', 'advert', 'banner', 'sponsor', 'popup', 'popunder', 'track', 'pixel', 'beacon',
            'doubleclick', 'googleadservices', 'googlesyndication', 'googletagmanager', 'googletagservices',
            'analytics', 'metrics', 'collect', 'segment', 'optimizely', 'hotjar', 'criteo', 'taboola',
            'outbrain', 'revcontent', 'mgid', 'propellerads', 'adroll', 'rubiconproject', 'pubmatic',
            'openx', 'adnxs', 'amazon-adsystem', 'scorecardresearch', 'yieldmo', 'bidswitch', 'adsrvr.org',
            'contextweb', 'media.net', 'adtech', 'serving-sys', 'eyeblaster', 'revsci', 'yieldlab',
            'adform', 'appnexus', 'casalemedia', 'quantserve', 'turn', 'mathtag', 'sitescout', 'simpli.fi',
            'fout.jp', 'amoad.com', 'genieessp.com', 'adstir.com', 'soom.cz', 'innovid.com', 'flashtalking.com',
            'sizmek.com', 'lijit.com', 'districtm.io', 'indexexchange.com', 'gumgum.com', 'teads.tv',
            'vidible.tv', 'spotxchange.com', 'brightroll.com', 'tremorhub.com', 'adap.tv', 'lkqd.net',
            'stickyadstv.com', 'aniview.com', 'playtem.com', 'exoclick.com', 'juicyads.com', 'ero-advertising.com',
            'plugrush.com', 'trafficjunky.net', 'trafficforce.com', 'traffichaus.com', 'zeropark.com',
            'admaven.com', 'popads.net', 'popcash.net', 'onclickads.net', 'onclicktop.com', 'ad-maven.com',
            'adcash.com', 'exdynsrv.com', 'adk2.com', 'adxpansion.com', 'adsupply.com', 'adreactor.com',
            'adkernel.com', 'adtarget.com', 'adverdirect.com', 'adversal.com', 'adxpedia.com',
            'adxcore.com', 'adxvalue.com', 'adxpremium.com', 'adxmarket.com', 'adxpartner.com',
            'adxnetwork.com', 'adxchange.com', 'adxapi.com', 'adxserve.com', 'adxflow.com',
            'adxpower.com', 'adxengine.com', 'adxplatform.com', 'adxsystem.com', 'adxtech.com',
            'adxmedia.com', 'adxcreative.com', 'adxoptimizer.com', 'adxmonetize.com', 'adxyield.com',
            'adxperformance.com', 'adxanalytics.com', 'adxtracking.com', 'adxpixel.com', 'adxbeacon.com',
            'adxtag.com', 'adxcontainer.com', 'adxunit.com', 'adxslot.com', 'adxframe.com',
            'adxbanner.com', 'adxcontent.com', 'adxtext.com', 'adximage.com', 'adxvideo.com',
            'adxsidebar.com', 'adxheader.com', 'adxfooter.com', 'adxsticky.com', 'adxfloating.com',
            'adxnative.com', 'adxsponsored.com', 'adxchoices.com', 'adxfeed.com', 'adxg.com',
            'adxform.com', 'adxblade.com', 'adxcell.com', 'adxition.com', 'adxengage.com',
            'adxfactor.com', 'adxfusion.com', 'adxmagnet.com', 'adxmeta.com', 'adxmotion.com',
            'adxroll.com', 'adxspirit.com', 'adxstir.com', 'adxswizz.com', 'adxtrade.com',
            'adxverster.com', 'adxview.com', 'adxwave.com', 'adxworx.com', 'adxzerk.com',
            'adxbutler.com', 'adxcolt.com', 'adxfox.com', 'adxfront.com', 'adxgear.com',
            'adxgrs.com', 'adxhese.com', 'adxhood.com', 'adxinfuse.com', 'adxjuggler.com',
            'adxlegend.com', 'adxman.com', 'adxmarvel.com', 'adxmaster.com', 'adxmedo.com',
            'adxmicro.com', 'adxmost.com', 'adxnegah.com', 'adxnexus.com', 'adxnologies.com',
            'adxoperator.com', 'adxorika.com', 'adxotomi.com', 'adxperfect.com', 'adxplugg.com',
            'adxscale.com', 'adxshost.com', 'adxshuffle.com', 'adxspruce.com', 'adxstage.com',
            'adxsteam.com', 'adxswapper.com', 'adxswich.com', 'adxsynergy.com', 'adxtegrity.com',
            'adxthink.com', 'adxtoma.com', 'adxtruth.com', 'adxunity.com', 'adxup.com',
            'adxverline.com', 'adxverticum.com', 'adxvertserve.com'
        ];

        scripts.forEach(script => {
            const src = script.src ? script.src.toLowerCase() : '';
            const textContent = script.textContent ? script.textContent.toLowerCase() : '';

            if (adScriptKeywords.some(keyword => src.includes(keyword) || textContent.includes(keyword))) {
                try {
                    script.type = 'text/plain-blocked'; /* Cambiar tipo para evitar ejecución */
                    if (script.parentNode) {
                        script.parentNode.removeChild(script);
                    }
                    config.stats.scriptsBlocked++;
                    if (config.debugMode) {
                        console.log('AdBlocker: Script bloqueado (src/text):', src || textContent.substring(0, 100));
                    }
                } catch (e) {
                    if (config.debugMode) console.warn('AdBlocker: Error removiendo script:', e, script);
                }
            }
        });
    }

    /* Función para eliminar elementos de anuncios (complementa CSS) */
    function removeAdElements(aggressive = false) {
        let adSelectors = [
            'iframe[src*="ads"]', 'iframe[src*="adserver"]', 'iframe[name*="ad"]', 'iframe[id*="ad"]',
            'div[data-ad-status]', 'div[data-ad-unit]', 'div[data-ad-slot]', 'div[data-ad-client]',
            'div[id^="google_ads"]', 'div[id^="div-gpt-ad"]', 'div[id*="taboola"]', 'div[id*="outbrain"]',
            'ins.adsbygoogle', 'div.ad-container', 'div.ad-wrapper', 'div.ad-banner', 'div.advertisement',
            'a[href*="/ads/"]', 'a[href*="doubleclick.net"]', 'a[href*="adclick.g.doubleclick.net"]',
            'img[src*="/ads/"]', 'img[alt*="advertisement"]', 'img[alt*="sponsored"]',
            '[class*="ad-"]', '[id*="ad-"]', '[class*="-ad"]', '[id*="-ad"]',
            '[class*="sponsor"]', '[id*="sponsor"]', '[class*="banner"]', '[id*="banner"]',
            '[class*="popup"]', '[id*="popup"]', '[class*="advert"]', '[id*="advert"]',
            '[aria-label*="ad"], [aria-label*="advertisement"], [aria-label*="sponsored"]',
            '[data-ad-format], [data-ad-layout-key], [data-ad-client], [data-ad-slot], [data-ad-unit]',
            '[data-google-query-id], [data-ad-manager], [data-ad-name], [data-ad-size]',
            '[data-ad-targeting], [data-ad-type], [data-ad-id], [data-ad-index], [data-ad-position]',
            '[data-ad-network], [data-ad-placement], [data-ad-status], [data-ad-impression-url"]',
            '[data-ad-click-url"], [data-ad-viewability-url"], [data-ad-error-url"]',
            '[data-ad-visibility-observer"], [data-ad-responsive"], [data-ad-lazy"]',
            '[data-ad-load"], [data-ad-refresh"], [data-ad-render"], [data-ad-state"]',
            '[data-ad-debug"], [data-ad-test"], [data-ad-preview"], [data-ad-config"]',
            '[data-ad-options"], [data-ad-settings"], [data-ad-properties"], [data-ad-attributes"]',
            '[data-ad-metadata"], [data-ad-info"], [data-ad-details"], [data-ad-data"]',
            '[data-ad-json"], [data-ad-xml"], [data-ad-html"], [data-ad-script"]',
            '[data-ad-iframe"], [data-ad-embed"], [data-ad-object"], [data-ad-applet"]',
            '[data-ad-plugin"], [data-ad-flash"], [data-ad-silverlight"], [data-ad-java"]',
            '[data-ad-activex"], [data-ad-vpaid"], [data-ad-vast"], [data-ad-ima"]',
            '[data-ad-gpt"], [data-ad-dfp"], [data-ad-header-bidding"], [data-ad-prebid"]',
            '[data-ad-postbid"], [data-ad-waterfall"], [data-ad-mediation"], [data-ad-exchange"]',
            '[data-ad-marketplace"], [data-ad-auction"], [data-ad-bid"], [data-ad-cpm"]',
            '[data-ad-cpc"], [data-ad-ctr"], [data-ad-ecpm"], [data-ad-fill-rate"]',
            '[data-ad-impressions"], [data-ad-clicks"], [data-ad-conversions"], [data-ad-revenue"]',
            '[data-ad-profit"], [data-ad-roi"], [data-ad-performance"], [data-ad-analytics"]',
            '[data-ad-tracking"], [data-ad-pixel"], [data-ad-beacon"], [data-ad-tag"]',
            '[data-ad-container-id"], [data-ad-unit-path"], [data-ad-size-mapping"]',
            '[data-ad-targeting-map"], [data-ad-category-exclusions"], [data-ad-custom-targeting"]',
            '[data-ad-collapse-empty-div"], [data-ad-force-collapse"], [data-ad-override-width"]',
            '[data-ad-override-height"], [data-ad-override-display"], [data-ad-safeframe"]',
            '[data-ad-sandbox"], [data-ad-allow-same-origin"], [data-ad-allow-scripts"]',
            '[data-ad-allow-forms"], [data-ad-allow-popups"], [data-ad-allow-pointer-lock"]',
            '[data-ad-allow-top-navigation"], [data-ad-allow-modals"], [data-ad-allow-orientation-lock"]',
            '[data-ad-allow-presentation"], [data-ad-allow-fullscreen"], [data-ad-allow-payment-request"]',
            '[data-ad-allow-downloads"], [data-ad-allow-storage-access-by-user-activation"]',
            '[data-ad-allow-top-navigation-by-user-activation"], [data-ad-allow-popups-to-escape-sandbox"]',
            '[data-ad-allow-downloads-without-user-activation"]'
        ];

        if (aggressive) {
            adSelectors = adSelectors.concat([
                'div[class*="ad-"]:empty', 'div[id*="ad-"]:empty',
                'div:empty:not([class*="spacer"]):not([class*="divider"]):not([id*="spacer"]):not([id*="divider"])', /* Evitar falsos positivos comunes */
                'iframe:not([src])', 'iframe[src="about:blank"]',
                'img[src=""]', 'img[src^="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"]', /* Pixel transparente */
                'a[href="#"]:empty', 'a[href="javascript:void(0)"]:empty',
                'div[style*="width:1px"], div[style*="width: 1px"]', 'div[style*="height:1px"], div[style*="height: 1px"]',
                'div[style*="width:0px"], div[style*="width: 0px"]', 'div[style*="height:0px"], div[style*="height: 0px"]',
                'div[style*="visibility:hidden"][style*="position:absolute"]', /* Elementos ocultos posicionados */
                'div[style*="z-index:9999"]', 'div[style*="z-index: 9999"]', /* Popups con z-index alto */
                'div[style*="z-index:2147483647"]' /* Max z-index */
            ]);
        }

        adSelectors.forEach(selector => {
            try {
                const elements = document.querySelectorAll(selector);
                elements.forEach(element => {
                    removeElement(element, `Selector: ${selector}`);
                });
            } catch (e) {
                if (config.debugMode) console.warn(`AdBlocker: Error con selector "${selector}":`, e);
            }
        });
    }

    /* Función para manejar nuevos nodos añadidos al DOM (MutationObserver) */
    function handleNewNodes() {
        const observer = new MutationObserver(mutations => {
            mutations.forEach(mutation => {
                mutation.addedNodes.forEach(node => {
                    if (node.nodeType === Node.ELEMENT_NODE) { /* Solo elementos */
                        processNodeAndChildrenForObserver(node);
                        traverseShadowDOM(node, processNodeAndChildrenForObserver);
                    }
                });
                /* También comprobar cambios en atributos que podrían revelar un anuncio */
                if (mutation.type === 'attributes' && mutation.target && mutation.target.nodeType === Node.ELEMENT_NODE) {
                     if (isAdElement(mutation.target)) {
                        removeElement(mutation.target, 'Attribute change revealed ad');
                     }
                }
            });
        });

        function processNodeAndChildrenForObserver(node) {
            if (isAdElement(node)) {
                removeElement(node, 'MutationObserver detected ad');
                return; /* Detener el procesamiento de hijos si el padre es eliminado */
            }
            if (node.children) {
                Array.from(node.children).forEach(child => {
                    if (child.nodeType === Node.ELEMENT_NODE) {
                        processNodeAndChildrenForObserver(child);
                    }
                });
            }
        }

        observer.observe(document.documentElement, {
            childList: true,
            subtree: true,
            attributes: true, /* Observar cambios en atributos */
            attributeFilter: ['class', 'id', 'style', 'src', 'href', 'name', 'data-ad-status', 'hidden', 'aria-hidden', 'aria-label', 'role', 'data-ad', 'data-google-query-id'] /* Atributos relevantes */
        });
        if (config.debugMode) console.log('AdBlocker: Advanced MutationObserver started.');
    }

    function traverseShadowDOM(node, visitCallback) {
        if (node.shadowRoot) {
            Array.from(node.shadowRoot.children).forEach(child => {
                if (child.nodeType === Node.ELEMENT_NODE) {
                    visitCallback(child);
                    traverseShadowDOM(child, visitCallback);
                }
            });
        }
        if (node.children) {
            Array.from(node.children).forEach(childNode => {
                if (childNode.nodeType === Node.ELEMENT_NODE) {
                    traverseShadowDOM(childNode, visitCallback);
                }
            });
        }
    }

    function blockAdsRecursively(element) {
        if (element.nodeType !== Node.ELEMENT_NODE) return;

        if (isAdElement(element)) {
            removeElement(element, 'Recursive check');
            return;
        }

        if (element.shadowRoot) {
            Array.from(element.shadowRoot.children).forEach(child => blockAdsRecursively(child));
        }

        if (element.children) {
            Array.from(element.children).forEach(child => blockAdsRecursively(child));
        }
    }

    function isAdElement(element) {
        const adKeywords = [
            'ad', 'ads', 'advert', 'advertisement', 'banner', 'sponsor', 'promo', 'promotion',
            'popup', 'popunder', 'interstitial', 'preroll', 'postroll', 'midroll',
            'reklama', 'annonce', 'anuncio', 'publicidade', 'publicidad', 'werbung',
            'google_ads', 'doubleclick', 'adsense', 'adservice', 'adunit', 'adslot',
            'adcontainer', 'adwrapper', 'adplaceholder', 'adbox', 'adframe', 'adbanner',
            'adcontent', 'adtext', 'adimage', 'advideo', 'sidebarad', 'headerad', 'footerad',
            'sticky-ad', 'floating-ad', 'video-ads', 'native-ad', 'sponsored-content',
            'outbrain', 'taboola', 'criteo', 'revcontent', 'mgid', 'propellerads', 'adchoices',
            'feedads', 'gads', 'adform', 'adtech', 'adblade', 'adcell', 'adition', 'adengage',
            'adfactor', 'adfusion', 'admagnet', 'admedia', 'admeta', 'admotion', 'adreactor',
            'adroll', 'adspirit', 'adstir', 'adswizz', 'adtarget', 'adtrade', 'adversal',
            'adverster', 'adview', 'adwave', 'adworx', 'adx', 'adzerk', 'adblade', 'adbutler',
            'adcolt', 'adfox', 'adform', 'adfront', 'adgear', 'adgrs', 'adhese', 'adhood',
            'adinfuse', 'adition', 'adjuggler', 'adlegend', 'adman', 'admarvel', 'admaster',
            'admedo', 'admeta', 'admicro', 'admost', 'adnegah', 'adnexus', 'adnologies',
            'adoperator', 'adorika', 'adotomi', 'adperfect', 'adplugg', 'adscale', 'adserver',
            'adshost', 'adshuffle', 'adspirit', 'adspruce', 'adstage', 'adsteam', 'adsupply',
            'adswapper', 'adswich', 'adsynergy', 'adtegrity', 'adthink', 'adtoma', 'adtruth',
            'adunity', 'adup', 'adverline', 'adverticum', 'advertserve', 'adzerk', 'appnexus',
            'contextweb', 'conversantmedia', 'infolinks', 'intellitxt', 'kontera', 'ligatus',
            'matomymedia', 'media.net', 'meridian', 'netseer', 'pulsepoint', 'rhythmone',
            'sharethrough', 'skimlinks', 'sovrn', 'specificmedia', 'spotxchange', 'tribalfusion',
            'undertone', 'valueclick', 'vibrantmedia', 'vidora', 'viglink', 'xaxis', 'yieldads',
            'yieldbot', 'yieldbuild', 'yieldlab', 'yieldmanager', 'yieldmo', 'zdbb.net',
            'zedo', 'zergnet', 'zumobi', 'bannerflow', 'bannergrabber', 'bannersnack',
            'popads', 'popcash', 'onclickads', 'onclicktop', 'exoclick', 'juicyads', 'eroadvertising',
            'plugrush', 'trafficjunky', 'trafficforce', 'traffichaus', 'zeropark', 'admaven',
            'propelleradsmedia', 'revcontent', 'content.ad'
        ];
        const adAttributes = ['class', 'id', 'style', 'data-ad', 'data-adsbygoogle', 'aria-label', 'role', 'name', 'alt', 'title', 'data-ad-client', 'data-ad-slot', 'data-ad-format', 'data-ad-unit', 'data-ad-layout-key', 'data-ad-manager', 'data-ad-identifier', 'data-ad-name', 'data-ad-type', 'data-ad-channel', 'data-ad-targeting', 'data-ad-params', 'data-ad-size', 'data-ad-dimensions', 'data-ad-network', 'data-ad-placement', 'data-ad-position', 'data-ad-id', 'data-ad-index', 'data-ad-group', 'data-ad-campaign', 'data-ad-creative', 'data-ad-impression-url', 'data-ad-click-url', 'data-ad-viewability-url', 'data-ad-error-url', 'data-ad-visibility-observer', 'data-ad-responsive', 'data-ad-lazy', 'data-ad-load', 'data-ad-refresh', 'data-ad-render', 'data-ad-status', 'data-ad-state', 'data-ad-debug', 'data-ad-test', 'data-ad-preview', 'data-ad-config', 'data-ad-options', 'data-ad-settings', 'data-ad-properties', 'data-ad-attributes', 'data-ad-metadata', 'data-ad-info', 'data-ad-details', 'data-ad-data', 'data-ad-json', 'data-ad-xml', 'data-ad-html', 'data-ad-script', 'data-ad-iframe', 'data-ad-embed', 'data-ad-object', 'data-ad-applet', 'data-ad-plugin', 'data-ad-flash', 'data-ad-silverlight', 'data-ad-java', 'data-ad-activex', 'data-ad-vpaid', 'data-ad-vast', 'data-ad-ima', 'data-ad-gpt', 'data-ad-dfp', 'data-ad-header-bidding', 'data-ad-prebid', 'data-ad-postbid', 'data-ad-waterfall', 'data-ad-mediation', 'data-ad-exchange', 'data-ad-marketplace', 'data-ad-auction', 'data-ad-bid', 'data-ad-cpm', 'data-ad-cpc', 'data-ad-ctr', 'data-ad-ecpm', 'data-ad-fill-rate', 'data-ad-impressions', 'data-ad-clicks', 'data-ad-conversions', 'data-ad-revenue', 'data-ad-profit', 'data-ad-roi', 'data-ad-performance', 'data-ad-analytics', 'data-ad-tracking', 'data-ad-pixel', 'data-ad-beacon', 'data-ad-tag', 'data-ad-container-id', 'data-ad-unit-path', 'data-ad-size-mapping', 'data-ad-targeting-map', 'data-ad-category-exclusions', 'data-ad-custom-targeting', 'data-ad-collapse-empty-div', 'data-ad-force-collapse', 'data-ad-override-width', 'data-ad-override-height', 'data-ad-override-display', 'data-ad-safeframe', 'data-ad-sandbox', 'data-ad-allow-same-origin', 'data-ad-allow-scripts', 'data-ad-allow-forms', 'data-ad-allow-popups', 'data-ad-allow-pointer-lock', 'data-ad-allow-top-navigation', 'data-ad-allow-modals', 'data-ad-allow-orientation-lock', 'data-ad-allow-presentation', 'data-ad-allow-fullscreen', 'data-ad-allow-payment-request', 'data-ad-allow-downloads', 'data-ad-allow-storage-access-by-user-activation', 'data-ad-allow-top-navigation-by-user-activation', 'data-ad-allow-popups-to-escape-sandbox', 'data-ad-allow-downloads-without-user-activation'];

        if (!element || element.nodeType !== Node.ELEMENT_NODE || typeof element.getAttribute !== 'function') {
            return false;
        }

        /* Evitar que el script se marque a sí mismo o a sus elementos de UI como anuncios */
        if (element.id === 'adblocker-stats-div' || (element.classList && element.classList.contains('adblocker-ui-element'))) {
            return false;
        }

        try {
            const style = window.getComputedStyle(element);
            const rect = element.getBoundingClientRect();

            if (config.aggressiveMode) {
                /* Elementos muy pequeños o invisibles con palabras clave son sospechosos */
                if ((rect.width < 2 && rect.height < 2 && rect.width >= 0 && rect.height >= 0) || parseFloat(style.opacity || '1') < 0.05) {
                    const outerHTML = element.outerHTML.toLowerCase();
                    if (adKeywords.some(keyword => outerHTML.includes(keyword))) {
                        if (config.debugMode) console.log('AdBlocker: Tiny/invisible element with ad keyword', element);
                        return true;
                    }
                }

                /* Elementos posicionados fuera de la pantalla */
                if (rect.right < 0 || rect.bottom < 0 || rect.left > window.innerWidth || rect.top > window.innerHeight) {
                    const outerHTML = element.outerHTML.toLowerCase();
                     if (adKeywords.some(keyword => outerHTML.includes(keyword))) {
                        if (config.debugMode) console.log('AdBlocker: Off-screen element with ad keyword', element);
                        return true;
                    }
                }
            }

            /* Comprobación de dimensiones comunes de anuncios */
            const adSizes = [
                { w: 728, h: 90 }, { w: 300, h: 250 }, { w: 336, h: 280 }, { w: 160, h: 600 },
                { w: 120, h: 600 }, { w: 468, h: 60 }, { w: 320, h: 50 }, { w: 320, h: 100 },
                { w: 300, h: 600 }, { w: 970, h: 250 }, { w: 970, h: 90 }, { w: 250, h: 250 },
                { w: 200, h: 200 }, { w: 1, h: 1}, {w: 2, h: 2} /* Píxeles de seguimiento */
            ];
            if (adSizes.some(size => Math.abs(rect.width - size.w) < 10 && Math.abs(rect.height - size.h) < 10)) {
                const outerHTML = element.outerHTML.toLowerCase();
                if (adKeywords.some(keyword => outerHTML.includes(keyword))) {
                    if (config.debugMode) console.log('AdBlocker: Element matches common ad size and has ad keyword', element);
                    return true;
                }
            }

            /* Comprobar elementos fijos/pegajosos */
            if (style.position === 'fixed' || style.position === 'sticky') {
                if ((rect.bottom > window.innerHeight - 20 && rect.height < 250 && rect.width > 30) ||
                    (rect.top < 20 && rect.height < 250 && rect.width > 30) ||
                    (rect.right > window.innerWidth - 20 && rect.width < 250 && rect.height > 30) ||
                    (rect.left < 20 && rect.width < 250 && rect.height > 30) ||
                    (rect.width >= window.innerWidth * 0.9 && rect.height < 150) || /* Banner ancho completo */
                    (rect.height >= window.innerHeight * 0.8 && rect.width < 150)   /* Banner alto completo */
                ) {
                     const outerHTML = element.outerHTML.toLowerCase();
                     if (adKeywords.some(keyword => outerHTML.includes(keyword))) {
                        if (config.debugMode) console.log('AdBlocker: Sticky/fixed element with ad keyword and typical ad position/size', element);
                        return true;
                    }
                }
            }

        } catch (e) { /* Ignorar errores de getComputedStyle o getBoundingClientRect */ }


        for (const attribute of adAttributes) {
            const attrValue = element.getAttribute(attribute);
            if (attrValue) {
                const lowerAttrValue = attrValue.toLowerCase();
                for (const keyword of adKeywords) {
                    if (lowerAttrValue.includes(keyword)) {
                        if (config.debugMode) console.log(`AdBlocker: Keyword '${keyword}' in attribute '${attribute}' for`, element);
                        return true;
                    }
                }
            }
        }

        if (element.tagName === 'IFRAME') {
            const src = element.src ? element.src.toLowerCase() : '';
            if (src === '' || src === 'about:blank') {
                if (element.innerHTML === '' && (element.offsetWidth < 15 || element.offsetHeight < 15) && element.offsetWidth >=0 && element.offsetHeight >=0) {
                     if (config.debugMode) console.log('AdBlocker: Empty/small iframe detected', element);
                     return true;
                }
            }
            const adIframeDomains = ['doubleclick.net', 'googlesyndication.com', 'googleads.g.doubleclick.net', 'adservice.google.com', 'amazon-adsystem.com', 'adnxs.com', 'criteo.com', 'rubiconproject.com', 'pubmatic.com', 'openx.net', 'smartadserver.com', 'yieldmo.com', 'bidswitch.net', 'adsrvr.org', 'taboola.com', 'outbrain.com', 'zedo.com', 'scorecardresearch.com', 'contextweb.com', 'media.net', 'adtech.com', 'advertising.com', 'serving-sys.com', 'eyeblaster.com', 'revsci.net', 'yieldlab.net', 'adform.net', 'appnexus.com', 'casalemedia.com', 'quantserve.com', 'turn.com', 'mathtag.com', 'sitescout.com', 'simpli.fi', 'fout.jp', 'amoad.com', 'genieessp.com', 'adstir.com', 'soom.cz', 'innovid.com', 'flashtalking.com', 'sizmek.com', 'adroll.com', 'sharethrough.com', 'lijit.com', 'districtm.io', 'indexexchange.com', 'gumgum.com', 'teads.tv', 'vidible.tv', 'spotxchange.com', 'brightroll.com', 'tremorhub.com', 'adap.tv', 'lkqd.net', 'stickyadstv.com', 'aniview.com', 'playtem.com', 'exoclick.com', 'juicyads.com', 'ero-advertising.com', 'plugrush.com', 'trafficjunky.net', 'trafficforce.com', 'traffichaus.com', 'zeropark.com', 'admaven.com', 'propellerads.com', 'popads.net', 'popcash.net', 'onclickads.net', 'onclicktop.com', 'ad-maven.com', 'adcash.com', 'exdynsrv.com', 'adk2.com', 'adxpansion.com', 'adsupply.com', 'adreactor.com', 'adkernel.com', 'adtarget.com', 'adverdirect.com', 'adversal.com', 'adxpedia.com', 'adxcore.com', 'adxvalue.com', 'adxpremium.com', 'adxmarket.com', 'adxpartner.com', 'adxnetwork.com', 'adxchange.com', 'adxapi.com', 'adxserve.com', 'adxflow.com', 'adxpower.com', 'adxengine.com', 'adxplatform.com', 'adxsystem.com', 'adxtech.com', 'adxmedia.com', 'adxcreative.com', 'adxoptimizer.com', 'adxmonetize.com', 'adxyield.com', 'adxperformance.com', 'adxanalytics.com', 'adxtracking.com', 'adxpixel.com', 'adxbeacon.com', 'adxtag.com', 'adxcontainer.com', 'adxunit.com', 'adxslot.com', 'adxframe.com', 'adxbanner.com', 'adxcontent.com', 'adxtext.com', 'adximage.com', 'adxvideo.com', 'adxsidebar.com', 'adxheader.com', 'adxfooter.com', 'adxsticky.com', 'adxfloating.com', 'adxnative.com', 'adxsponsored.com', 'adxchoices.com', 'adxfeed.com', 'adxg.com', 'adxform.com', 'adxblade.com', 'adxcell.com', 'adxition.com', 'adxengage.com', 'adxfactor.com', 'adxfusion.com', 'adxmagnet.com', 'adxmeta.com', 'adxmotion.com', 'adxroll.com', 'adxspirit.com', 'adxstir.com', 'adxswizz.com', 'adxtrade.com', 'adxverster.com', 'adxview.com', 'adxwave.com', 'adxworx.com', 'adxzerk.com', 'adxbutler.com', 'adxcolt.com', 'adxfox.com', 'adxfront.com', 'adxgear.com', 'adxgrs.com', 'adxhese.com', 'adxhood.com', 'adxinfuse.com', 'adxjuggler.com', 'adxlegend.com', 'adxman.com', 'adxmarvel.com', 'adxmaster.com', 'adxmedo.com', 'adxmicro.com', 'adxmost.com', 'adxnegah.com', 'adxnexus.com', 'adxnologies.com', 'adxoperator.com', 'adxorika.com', 'adxotomi.com', 'adxperfect.com', 'adxplugg.com', 'adxscale.com', 'adxshost.com', 'adxshuffle.com', 'adxspruce.com', 'adxstage.com', 'adxsteam.com', 'adxswapper.com', 'adxswich.com', 'adxsynergy.com', 'adxtegrity.com', 'adxthink.com', 'adxtoma.com', 'adxtruth.com', 'adxunity.com', 'adxup.com', 'adxverline.com', 'adxverticum.com', 'adxvertserve.com'];
            if (adKeywords.some(keyword => src.includes(keyword)) || adIframeDomains.some(domain => src.includes(domain))) {
                if (config.debugMode) console.log('AdBlocker: Keyword or ad domain in iframe src', element);
                return true;
            }
        }

        if (element.tagName === 'SCRIPT') {
            const src = element.src ? element.src.toLowerCase() : '';
            const textContent = element.textContent ? element.textContent.toLowerCase() : '';
            const scriptKeywords = adKeywords.concat(['track', 'analytics', 'pixel', 'fingerprint', 'collect', 'telemetry', 'beacon', 'segment', 'gtag', 'gaq', 'mixpanel', 'amplitude', 'heap', 'hotjar', 'optimizely', 'crazyegg', 'fullstory', 'inspectlet', 'mouseflow', 'logrocket', 'sentry', 'bugsnag', 'newrelic', 'datadog', 'appdynamics', 'dynatrace', 'akamaihd.net', 'cloudfront.net', 'cloudflare.com', 'fastly.net', 'azureedge.net', 'akamai.net', 'edgekey.net', 'cedexis.com', 'incapsula.com', 'imperva.com', 'distilnetworks.com', 'shapesecurity.com', 'perimeterx.com', 'datadome.co', 'shieldsquare.com', 'humansecurity.com', 'arkoselabs.com', 'f5.com', 'radware.com', 'netacea.com', 'kasada.io', 'threatx.com', 'pingidentity.com', 'okta.com', 'auth0.com', 'onelogin.com', 'duo.com', 'cyberark.com', 'sailpoint.com', 'beyondtrust.com', 'centrify.com', 'thycotic.com', 'balabit.com', 'observeit.com', 'forcepoint.com', 'proofpoint.com', 'mimecast.com', 'barracuda.com', 'cisco.com', 'paloaltonetworks.com', 'fortinet.com', 'checkmk', 'checkpoint.com', 'sophos.com', 'symantec.com', 'mcafee.com', 'trendmicro.com', 'kaspersky.com', 'eset.com', 'bitdefender.com', 'avast.com', 'avg.com', 'malwarebytes.com', 'webroot.com', 'f-secure.com', 'gdatasoftware.com', 'pandasecurity.com', 'comodo.com', 'emsisoft.com', 'zillya.com', 'drweb.com', 'ahnlab.com', 'qihoo360.cn', 'tencent.com', 'baidu.com', 'kingsoft.com', 'rising.com.cn', 'jiangmin.com', 'k7computing.com', 'quickheal.co.in', 'npav.net', 'escanav.com', 'microworldsystems.com', 'protegent360.com', 'unthreat.com', 'totalav.com', 'scanguard.com', 'pcprotect.com', 'bullguard.com', 'norton.com', 'webinspector.com', 'sucuri.net', 'wordfence.com', 'ithemes.com', 'malcare.com', 'patchstack.com', 'ninjafirewall.com', 'wpcerber.com', 'allinone']);
            for (const keyword of scriptKeywords) {
                if (src.includes(keyword) || textContent.includes(keyword)) {
                    if (config.debugMode) console.log(`AdBlocker: Keyword '${keyword}' in SCRIPT tag`, element);
                    if (config.aggressiveMode || (!['analytics', 'track', 'collect', 'telemetry', 'pixel', 'beacon', 'segment', 'gtag', 'gaq'].includes(keyword) && !src.includes('jquery') && !src.includes('gsap') && !src.includes('lodash') && !src.includes('moment') && !src.includes('react') && !src.includes('vue') && !src.includes('angular'))) {
                        return true;
                    }
                }
            }
        }

        if (element.children.length === 1 && element.firstElementChild && element.firstElementChild.tagName === 'IFRAME') {
            if (isAdElement(element.firstElementChild)) {
                if (config.debugMode) console.log('AdBlocker: Element wraps an ad iframe', element);
                return true;
            }
        }

        if (element.tagName === 'DIV' && !element.children.length && (!element.textContent || !element.textContent.trim())) {
            const style = element.style;
            if (style.width && style.height && (parseInt(style.width) > 30 || parseInt(style.height) > 30)) {
                const classId = (element.className + ' ' + element.id).toLowerCase();
                if (adKeywords.some(kw => classId.includes(kw))) {
                    if (config.debugMode) console.log('AdBlocker: Empty div with ad-like class/id and dimensions', element);
                    return true;
                }
            }
        }

        /* Comprobar si el elemento es un 'a' con href a un dominio de publicidad conocido y sin contenido visible significativo */
        if (element.tagName === 'A' && element.href) {
            const adLinkDomains = ['doubleclick.net', 'adservice.google.com', 'redirect.ads', 'syndication.exoclick.com', 'trafficjunky.net', 'outbrain.com', 'taboola.com', 'revcontent.com', 'mgid.com', 'propellerads.com', 'admaven.com', 'popads.net', 'onclickads.net', 'juicyads.com', 'ero-advertising.com', 'plugrush.com', 'trafficforce.com', 'zeropark.com', 'adcash.com', 'adk2.com', 'adxpansion.com', 'adsupply.com', 'adreactor.com', 'adkernel.com', 'adtarget.com', 'adverdirect.com', 'adversal.com', 'adxpedia.com', 'adxcore.com', 'adxvalue.com', 'adxpremium.com', 'adxmarket.com', 'adxpartner.com', 'adxnetwork.com', 'adxchange.com', 'adxapi.com', 'adxserve.com', 'adxflow.com', 'adxpower.com', 'adxengine.com', 'adxplatform.com', 'adxsystem.com', 'adxtech.com', 'adxmedia.com', 'adxcreative.com', 'adxoptimizer.com', 'adxmonetize.com', 'adxyield.com', 'adxperformance.com', 'adxanalytics.com', 'adxtracking.com', 'adxpixel.com', 'adxbeacon.com', 'adxtag.com', 'adxcontainer.com', 'adxunit.com', 'adxslot.com', 'adxframe.com', 'adxbanner.com', 'adxcontent.com', 'adxtext.com', 'adximage.com', 'adxvideo.com', 'adxsidebar.com', 'adxheader.com', 'adxfooter.com', 'adxsticky.com', 'adxfloating.com', 'adxnative.com', 'adxsponsored.com', 'adxchoices.com', 'adxfeed.com', 'adxg.com', 'adxform.com', 'adxblade.com', 'adxcell.com', 'adxition.com', 'adxengage.com', 'adxfactor.com', 'adxfusion.com', 'adxmagnet.com', 'adxmeta.com', 'adxmotion.com', 'adxroll.com', 'adxspirit.com', 'adxstir.com', 'adxswizz.com', 'adxtrade.com', 'adxverster.com', 'adxview.com', 'adxwave.com', 'adxworx.com', 'adxzerk.com', 'adxbutler.com', 'adxcolt.com', 'adxfox.com', 'adxfront.com', 'adxgear.com', 'adxgrs.com', 'adxhese.com', 'adxhood.com', 'adxinfuse.com', 'adxjuggler.com', 'adxlegend.com', 'adxman.com', 'adxmarvel.com', 'adxmaster.com', 'adxmedo.com', 'adxmicro.com', 'adxmost.com', 'adxnegah.com', 'adxnexus.com', 'adxnologies.com', 'adxoperator.com', 'adxorika.com', 'adxotomi.com', 'adxperfect.com', 'adxplugg.com', 'adxscale.com', 'adxshost.com', 'adxshuffle.com', 'adxspruce.com', 'adxstage.com', 'adxsteam.com', 'adxswapper.com', 'adxswich.com', 'adxsynergy.com', 'adxtegrity.com', 'adxthink.com', 'adxtoma.com', 'adxtruth.com', 'adxunity.com', 'adxup.com', 'adxverline.com', 'adxverticum.com', 'adxvertserve.com'];
            const hrefLower = element.href.toLowerCase();
            if (adLinkDomains.some(domain => hrefLower.includes(domain))) {
                if (!element.textContent.trim() && !element.querySelector('img, svg, canvas')) { /* Sin texto visible ni imagen */
                    if (config.debugMode) console.log('AdBlocker: Empty link to ad domain', element);
                    return true;
                }
            }
        }

        return false;
    }
    /* Función para cargar listas de filtros */
    function loadFilterLists() {
        const now = Math.floor(Date.now() / 1000);
        if (now - config.lastUpdate > config.updateInterval || GM_getValue('forceFilterUpdate', false)) {
            GM_setValue('forceFilterUpdate', false); /* Reset flag */
            config.customFilters = []; /* Limpiar filtros antes de recargar */
            let listsLoaded = 0;
            const totalLists = config.filterLists.length;

            config.filterLists.forEach(url => {
                GM_xmlhttpRequest({
                    method: 'GET',
                    url: url,
                    timeout: 15000, /* Timeout de 15 segundos por lista */
                    onload: function(response) {
                        listsLoaded++;
                        if (response.status === 200) {
                            const filters = response.responseText.split('\n');
                            filters.forEach(filter => {
                                if (filter && !filter.startsWith('!') && !filter.startsWith('#') && filter.length > 2) {
                                    config.customFilters.push(filter.trim());
                                }
                            });
                            if (config.debugMode) {
                                console.log('Lista de filtros cargada:', url);
                            }
                        } else {
                            if (config.debugMode) {
                                console.warn('Error al cargar lista de filtros:', url, response.status);
                            }
                        }
                        if (listsLoaded === totalLists) {
                            GM_setValue('lastUpdate', now);
                            GM_setValue('customFiltersCache', JSON.stringify(config.customFilters));
                            if (config.debugMode) console.log('Todas las listas de filtros procesadas. Total de filtros:', config.customFilters.length);
                            applyCustomFilters(); /* Re-aplicar filtros después de cargar */
                        }
                    },
                    onerror: function(error) {
                        listsLoaded++;
                        if (config.debugMode) console.error('Error de red al cargar lista de filtros:', url, error);
                        if (listsLoaded === totalLists) {
                             /* Aún así, guardar la fecha de actualización para no reintentar inmediatamente */
                            GM_setValue('lastUpdate', now);
                            if (config.debugMode) console.log('Procesamiento de listas de filtros completado con errores.');
                        }
                    },
                    ontimeout: function() {
                        listsLoaded++;
                        if (config.debugMode) console.warn('Timeout al cargar lista de filtros:', url);
                         if (listsLoaded === totalLists) {
                            GM_setValue('lastUpdate', now);
                            if (config.debugMode) console.log('Procesamiento de listas de filtros completado con timeouts.');
                        }
                    }
                });
            });
        } else {
            /* Cargar filtros desde caché si no es tiempo de actualizar */
            const cachedFilters = GM_getValue('customFiltersCache', null);
            if (cachedFilters) {
                try {
                    config.customFilters = JSON.parse(cachedFilters);
                    if (config.debugMode) console.log('Filtros cargados desde caché. Total:', config.customFilters.length);
                } catch (e) {
                    if (config.debugMode) console.error('Error al parsear filtros cacheados:', e);
                    GM_setValue('forceFilterUpdate', true); /* Forzar actualización en la próxima carga */
                }
            } else {
                 GM_setValue('forceFilterUpdate', true); /* Forzar actualización si no hay caché */
            }
        }
    }

    /* Función para aplicar filtros personalizados */
    function applyCustomFilters() {
        if (!config.customFilters || config.customFilters.length === 0) {
            if (config.debugMode) console.log('No hay filtros personalizados para aplicar.');
            return;
        }
        config.customFilters.forEach(filter => {
            try {
                if (filter.startsWith('||')) {
                    /* Bloquear dominios (principalmente para GM_xmlhttpRequest y fetch, ya manejado) */
                    /* Esta sección podría usarse para eliminar elementos si el script se carga después de que se hayan insertado */
                    const domain = filter.substring(2).split('^')[0].split('$')[0];
                    if (window.location.hostname.includes(domain) || document.documentElement.innerHTML.includes(domain)) {
                        document.querySelectorAll(`script[src*="${domain}"], iframe[src*="${domain}"], img[src*="${domain}"], link[href*="${domain}"], object[data*="${domain}"], embed[src*="${domain}"]`).forEach(element => {
                            removeElement(element, `Custom filter domain block: ${domain}`);
                        });
                    }
                } else if (filter.includes('##') || filter.includes('#?#') || filter.includes('#@#')) {
                    /* Ocultar/eliminar elementos CSS. #?# y #@# son para selectores extendidos en uBlock/AdGuard */
                    let selector = filter.split('##')[1] || filter.split('#?#')[1] || filter.split('#@#')[1];
                    if (selector) {
                        /* Simplificar: eliminar opciones de selector extendido por ahora para compatibilidad básica */
                        selector = selector.split(':style(')[0].split(':remove(')[0].trim();
                        if (selector) {
                            document.querySelectorAll(selector).forEach(element => {
                                removeElement(element, `Custom filter CSS selector: ${selector}`);
                            });
                        }
                    }
                } else if (!filter.includes('$') && (filter.startsWith('.') || filter.startsWith('#') || filter.includes('['))) {
                    /* Asumir que es un selector CSS si no tiene modificadores de uBlock y parece un selector */
                     document.querySelectorAll(filter).forEach(element => {
                        removeElement(element, `Custom filter simple CSS: ${filter}`);
                    });
                }
            } catch (e) {
                if (config.debugMode) {
                    console.error('Error aplicando filtro personalizado:', filter, e);
                }
            }
        });
        if (config.debugMode) console.log('Filtros personalizados aplicados.');
    }

    function removeElement(element, reason = 'Unknown') {
        if (element && element.parentNode) {
            /* Marcar como oculto para el bypass de anti-adblocker antes de remover */
            element.setAttribute('data-adblock-hidden', 'true');
            element.style.setProperty('display', 'none', 'important');
            element.style.setProperty('visibility', 'hidden', 'important');
            element.style.setProperty('width', '0px', 'important');
            element.style.setProperty('height', '0px', 'important');
            element.style.setProperty('opacity', '0', 'important');
            element.style.setProperty('pointer-events', 'none', 'important');

            /* Intentar remover del DOM de forma segura */
            try {
                element.parentNode.removeChild(element);
                config.stats.elementsRemoved++;
                if (config.debugMode) {
                    console.log(`Elemento removido (${reason}):`, element.tagName, element.id, element.className.substring(0,50));
                }
            } catch (e) {
                if (config.debugMode) {
                    console.warn(`Error al remover elemento del DOM (${reason}):`, e, element);
                }
            }
        } else if (config.debugMode && element) {
            /* console.warn(`No se pudo remover el elemento (no parentNode o ya removido, razón: ${reason}):`, element.tagName, element.id, element.className); */
        }
    }

    /* Función para mostrar estadísticas */
    function showStats() {
        let statsDiv = document.getElementById('adblocker-stats-div');
        if (!statsDiv) {
            statsDiv = document.createElement('div');
            statsDiv.id = 'adblocker-stats-div';
            statsDiv.classList.add('adblocker-ui-element'); /* Para evitar que se autobloquee */
            statsDiv.style.position = 'fixed';
            statsDiv.style.bottom = '10px';
            statsDiv.style.right = '10px';
            statsDiv.style.backgroundColor = 'rgba(0,0,0,0.8)';
            statsDiv.style.color = 'white';
            statsDiv.style.padding = '12px';
            statsDiv.style.borderRadius = '8px';
            statsDiv.style.zIndex = '2147483647';
            statsDiv.style.fontFamily = 'Arial, sans-serif';
            statsDiv.style.fontSize = '13px';
            statsDiv.style.lineHeight = '1.6';
            statsDiv.style.boxShadow = '0 4px 12px rgba(0,0,0,0.3)';
            if (document.body) {
                 document.body.appendChild(statsDiv);
            } else {
                /* Fallback si el body no está listo, aunque en document-start es improbable */
                document.documentElement.appendChild(statsDiv);
            }
        }

        statsDiv.innerHTML = `
            <strong>AdBlocker Avanzado</strong><br>
            Elementos eliminados: ${config.stats.elementsRemoved}<br>
            Scripts bloqueados: ${config.stats.scriptsBlocked}<br>
            Solicitudes bloqueadas: ${config.stats.requestsBlocked}<br>
            Total bloqueado: ${config.stats.elementsRemoved + config.stats.scriptsBlocked + config.stats.requestsBlocked}
        `;
    }

    /* Menú de configuración */
    GM_registerMenuCommand('AdBlocker: Alternar Modo Agresivo', function() {
        config.aggressiveMode = !config.aggressiveMode;
        GM_setValue('aggressiveMode', config.aggressiveMode);
        alert(`Modo agresivo ${config.aggressiveMode ? 'ACTIVADO' : 'DESACTIVADO'}. La página se recargará.`);
        location.reload();
    });

    GM_registerMenuCommand('AdBlocker: Mostrar/Ocultar Estadísticas', function() {
        let statsDiv = document.getElementById('adblocker-stats-div');
        if (statsDiv && statsDiv.style.display !== 'none') {
            statsDiv.style.display = 'none';
            GM_setValue('showStats', false);
        } else {
            showStats(); /* Crea o muestra */
            if (statsDiv) statsDiv.style.display = 'block';
            GM_setValue('showStats', true);
        }
    });

    GM_registerMenuCommand('AdBlocker: Forzar Actualización de Filtros', function() {
        GM_setValue('lastUpdate', 0); /* Forzar la actualización en la próxima carga/llamada */
        GM_setValue('forceFilterUpdate', true);
        alert('Actualización de filtros forzada. Se aplicará al recargar o en la próxima comprobación.');
        loadFilterLists(); /* Intentar cargar inmediatamente */
    });

    GM_registerMenuCommand('AdBlocker: Alternar Modo Depuración', function() {
        config.debugMode = !config.debugMode;
        GM_setValue('debugMode', config.debugMode);
        alert(`Modo depuración ${config.debugMode ? 'ACTIVADO' : 'DESACTIVADO'}.`);
        if (config.debugMode && GM_getValue('showStats', true)) {
            showStats();
        }
    });

    function loadInitialConfig() {
        const savedConfig = GM_getValue('adBlockerConfig', null);
        if (savedConfig) {
            try {
                const parsedConfig = JSON.parse(savedConfig);
                /* Fusionar solo las claves que existen en la configuración por defecto para evitar errores */
                Object.keys(config).forEach(key => {
                    if (parsedConfig.hasOwnProperty(key) && typeof parsedConfig[key] === typeof config[key]) {
                        if (typeof config[key] === 'object' && !Array.isArray(config[key]) && config[key] !== null) {
                             /* Para objetos anidados como 'stats', fusionar con cuidado o reemplazar */
                             if (key !== 'stats') { /* No sobrescribir stats con valores viejos */
                                Object.assign(config[key], parsedConfig[key]);
                             }
                        } else {
                            config[key] = parsedConfig[key];
                        }
                    }
                });
                 if (config.debugMode) console.log('Configuración cargada desde GM_getValue', config);
            } catch (e) {
                if (config.debugMode) console.error('Error al parsear configuración guardada:', e);
            }
        }
        /* Aplicar valores individuales que podrían haber sido establecidos por comandos de menú */
        config.aggressiveMode = GM_getValue('aggressiveMode', config.aggressiveMode);
        config.debugMode = GM_getValue('debugMode', config.debugMode);
        config.lastUpdate = GM_getValue('lastUpdate', config.lastUpdate);
        config.whitelist = GM_getValue('whitelist', []); /* Cargar whitelist */
    }

    function saveConfig() {
        /* No guardar stats en la configuración persistente, ya que son de sesión */
        const configToSave = { ...config };
        delete configToSave.stats;
        delete configToSave.customFilters; /* Los filtros se cachean por separado */
        try {
            GM_setValue('adBlockerConfig', JSON.stringify(configToSave));
        } catch (e) {
            if (config.debugMode) console.error('Error guardando configuración:', e);
        }
    }

    /* Intercepción de Timers */
    function overrideTimers() {
        const originalSetTimeout = window.setTimeout;
        const originalSetInterval = window.setInterval;

        window.setTimeout = function(callback, delay, ...args) {
            if (typeof callback === 'string') {
                if (config.aggressiveMode && (callback.toLowerCase().includes('ad') || callback.toLowerCase().includes('banner'))) {
                    if (config.debugMode) console.log('AdBlocker: Bloqueando setTimeout basado en cadena sospechosa:', callback.substring(0,100));
                    return 0;
                }
            }
            /* Podríamos añadir más heurísticas aquí si es necesario, ej. callback.toString().includes('adKeyword') */
            return originalSetTimeout.call(window, callback, delay, ...args);
        };

        window.setInterval = function(callback, delay, ...args) {
            if (typeof callback === 'string') {
                 if (config.aggressiveMode && (callback.toLowerCase().includes('ad') || callback.toLowerCase().includes('banner'))) {
                    if (config.debugMode) console.log('AdBlocker: Bloqueando setInterval basado en cadena sospechosa:', callback.substring(0,100));
                    return 0;
                }
            }
            return originalSetInterval.call(window, callback, delay, ...args);
        };
        if (config.debugMode) console.log('AdBlocker: Timers (setTimeout, setInterval) interceptados.');
    }

    /* Bypass de Anti-AdBlockers */
    function antiAdBlockerBypass() {
        try {
            const originalGetComputedStyle = window.getComputedStyle;
            window.getComputedStyle = function(element, pseudoElt) {
                const style = originalGetComputedStyle.call(window, element, pseudoElt);
                if (element && element.hasAttribute && element.hasAttribute('data-adblock-hidden')) {
                    const newStyle = {};
                    for (let i = 0; i < style.length; i++) {
                        const prop = style[i];
                        newStyle[prop] = style.getPropertyValue(prop);
                    }
                    newStyle.display = 'block'; /* O su display original si se conociera */
                    newStyle.visibility = 'visible';
                    newStyle.width = element.getAttribute('data-og-width') || 'auto'; /* Restaurar si se guardó */
                    newStyle.height = element.getAttribute('data-og-height') || 'auto';
                    newStyle.opacity = '1';
                    newStyle.getPropertyValue = function(propName) { return this[propName]; };
                    newStyle.setProperty = function() {}; /* No permitir que anti-adblockers lo modifiquen */
                    Object.defineProperty(newStyle, 'length', { get: () => Object.keys(newStyle).filter(k => k !== 'getPropertyValue' && k !== 'setProperty' && k !== 'length').length });
                    return newStyle;
                }
                return style;
            };

            const functionsToCloak = [
                {obj: window, name: 'fetch'},
                {obj: XMLHttpRequest.prototype, name: 'open'},
                {obj: XMLHttpRequest.prototype, name: 'send'},
                {obj: document, name: 'createElement'}
            ];

            functionsToCloak.forEach(item => {
                if (item.obj && typeof item.obj[item.name] === 'function') {
                    const originalToString = item.obj[item.name].toString;
                    Object.defineProperty(item.obj[item.name], 'toString', {
                        value: function() { return `function ${item.name}() { [native code] }`; },
                        writable: false, configurable: false, enumerable: false
                    });
                }
            });

            /* Ocultar variables globales comunes de extensiones de bloqueo */
            try {
                Object.defineProperty(window, 'uBlockOrigin', { get: () => undefined, configurable: false });
                Object.defineProperty(window, 'AdGuard', { get: () => undefined, configurable: false });
                Object.defineProperty(window, '__ABPROFILE_MAST__', { get: () => undefined, configurable: false });
            } catch(e) { /* Ignorar si no se puede definir */}

            if (config.debugMode) console.log('AdBlocker: Medidas anti-adblocker aplicadas.');
        } catch (e) {
            if (config.debugMode) console.error('AdBlocker: Error en antiAdBlockerBypass', e);
        }
    }

    /* Override document.write and writeln */
    const originalDocWrite = document.write;
    const originalDocWriteln = document.writeln;
    const adPatternsForWrite = [/ad(s|d|v|_)?serv(er|ice)/i, /doubleclick/i, /googlead/i, /banner/i, /popup/i, /advertising/i, /sponsor/i, /taboola/i, /outbrain/i, /criteo/i, /adnxs/i, /pubmatic/i, /openx/i, /yieldmo/i, /bidswitch/i, /adsrvr.org/i, /revcontent/i, /mgid.com/i, /propellerads/i, /admaven.com/i, /popads.net/i, /onclickads.net/i, /juicyads.com/i, /ero-advertising.com/i, /plugrush.com/i, /trafficjunky.net/i, /trafficforce.com/i, /zeropark.com/i, /adcash.com/i, /adk2.com/i, /adxpansion.com/i, /adsupply.com/i, /adreactor.com/i, /adkernel.com/i, /adtarget.com/i, /adverdirect.com/i, /adversal.com/i, /adxpedia.com/i, /adxcore.com/i, /adxvalue.com/i, /adxpremium.com/i, /adxmarket.com/i, /adxpartner.com/i, /adxnetwork.com/i, /adxchange.com/i, /adxapi.com/i, /adxserve.com/i, /adxflow.com/i, /adxpower.com/i, /adxengine.com/i, /adxplatform.com/i, /adxsystem.com/i, /adxtech.com/i, /adxmedia.com/i, /adxcreative.com/i, /adxoptimizer.com/i, /adxmonetize.com/i, /adxyield.com/i, /adxperformance.com/i, /adxanalytics.com/i, /adxtracking.com/i, /adxpixel.com/i, /adxbeacon.com/i, /adxtag.com/i, /adxcontainer.com/i, /adxunit.com/i, /adxslot.com/i, /adxframe.com/i, /adxbanner.com/i, /adxcontent.com/i, /adxtext.com/i, /adximage.com/i, /adxvideo.com/i, /adxsidebar.com/i, /adxheader.com/i, /adxfooter.com/i, /adxsticky.com/i, /adxfloating.com/i, /adxnative.com/i, /adxsponsored.com/i, /adxchoices.com/i, /adxfeed.com/i, /adxg.com/i, /adxform.com/i, /adxblade.com/i, /adxcell.com/i, /adxition.com/i, /adxengage.com/i, /adxfactor.com/i, /adxfusion.com/i, /adxmagnet.com/i, /adxmeta.com/i, /adxmotion.com/i, /adxroll.com/i, /adxspirit.com/i, /adxstir.com/i, /adxswizz.com/i, /adxtrade.com/i, /adxverster.com/i, /adxview.com/i, /adxwave.com/i, /adxworx.com/i, /adxzerk.com/i, /adxbutler.com/i, /adxcolt.com/i, /adxfox.com/i, /adxfront.com/i, /adxgear.com/i, /adxgrs.com/i, /adxhese.com/i, /adxhood.com/i, /adxinfuse.com/i, /adxjuggler.com/i, /adxlegend.com/i, /adxman.com/i, /adxmarvel.com/i, /adxmaster.com/i, /adxmedo.com/i, /adxmicro.com/i, /adxmost.com/i, /adxnegah.com/i, /adxnexus.com/i, /adxnologies.com/i, /adxoperator.com/i, /adxorika.com/i, /adxotomi.com/i, /adxperfect.com/i, /adxplugg.com/i, /adxscale.com/i, /adxshost.com/i, /adxshuffle.com/i, /adxspruce.com/i, /adxstage.com/i, /adxsteam.com/i, /adxswapper.com/i, /adxswich.com/i, /adxsynergy.com/i, /adxtegrity.com/i, /adxthink.com/i, /adxtoma.com/i, /adxtruth.com/i, /adxunity.com/i, /adxup.com/i, /adxverline.com/i, /adxverticum.com/i, /adxvertserve.com/];

    document.write = function(...text) {
        const fullText = text.join('').toLowerCase();
        if (adPatternsForWrite.some(pattern => pattern.test(fullText))) {
            if (config.debugMode) console.log('AdBlocker: Bloqueado intento de document.write:', text.join('').substring(0,100));
            config.stats.scriptsBlocked++;
            return;
        }
        return originalDocWrite.apply(document, text);
    };
    document.writeln = function(...text) {
        const fullText = text.join('').toLowerCase();
        if (adPatternsForWrite.some(pattern => pattern.test(fullText))) {
            if (config.debugMode) console.log('AdBlocker: Bloqueado intento de document.writeln:', text.join('').substring(0,100));
            config.stats.scriptsBlocked++;
            return;
        }
        return originalDocWriteln.apply(document, text);
    };
    if (config.debugMode) console.log('AdBlocker: document.write y document.writeln interceptados.');


    /* Inicialización */
    function init() {
        loadInitialConfig(); /* Cargar configuración guardada primero */

        if (config.whitelist.includes(window.location.hostname)) {
            if (config.debugMode) console.log('AdBlocker: Sitio en lista blanca, no se bloqueará nada.');
            if (typeof GM_notification === 'function') {
                GM_notification({
                    text: `AdBlocker: ${window.location.hostname} está en la lista blanca.`,
                    title: 'AdBlocker Info',
                    timeout: 3000
                });
            }
            return;
        }

        if (config.debugMode) console.log('AdBlocker: Iniciando Ultra Bloqueador de Anuncios...');

        overrideTimers();
        antiAdBlockerBypass();

        loadFilterLists(); /* Cargar/actualizar listas de filtros */
        blockAdScripts(); /* Bloquear scripts conocidos inicialmente */
        removeAdElements(config.aggressiveMode); /* Eliminar elementos conocidos inicialmente */
        blockAdsRecursively(document.documentElement); /* Escaneo profundo inicial */
        traverseShadowDOM(document.documentElement, (node) => { /* Escanear Shadow DOMs existentes */
            if (node.nodeType === Node.ELEMENT_NODE) blockAdsRecursively(node);
        });
        applyCustomFilters(); /* Aplicar filtros personalizados después del escaneo inicial */
        blockRequestsByKeywords(); /* Configurar bloqueo de solicitudes de red */

        handleNewNodes(); /* Observar cambios dinámicos en el DOM */


        if (GM_getValue('showStats', true) || config.debugMode) {
            showStats();
        }

        /* Actualizar estadísticas periódicamente y guardar configuración */
        setInterval(() => {
            if (GM_getValue('showStats', true) || config.debugMode) {
                showStats();
            }
            saveConfig(); /* Guardar configuración periódicamente */
        }, 5000); /* Actualizar cada 5 segundos */

        if (config.debugMode) {
            console.log('Ultra Bloqueador de Anuncios iniciado completamente.');
        }
         if (typeof GM_notification === 'function' && (config.stats.elementsRemoved > 0 || config.stats.scriptsBlocked > 0 || config.stats.requestsBlocked > 0)) {
            GM_notification({
                text: `AdBlocker: ${config.stats.elementsRemoved + config.stats.scriptsBlocked + config.stats.requestsBlocked} elementos bloqueados en ${window.location.hostname}.`,
                title: 'AdBlocker Activo',
                timeout: 4000
            });
        }
    }

    /* Esperar a que el DOM esté listo, pero algunas funciones @run-at document-start necesitan ejecutarse antes */
    /* Las funciones que modifican prototipos o interceptan deben ejecutarse lo antes posible */
    overrideTimers();
    antiAdBlockerBypass();
    /* document.write override ya está definido globalmente en el IIFE */
    blockRequestsByKeywords(); /* Configurar esto temprano también */


    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }

    /* Configuration object */
    function blockRequestsByKeywords() {
    const blockedKeywordsPatterns = [
        /ad\./i, /\.ads/i, /banner/i, /sponsor/i, /reklama/i, /reklamy/i, /* /\.gif/i, /\.jpg/i, /\.jpeg/i, /\.png/i, -- Demasiado agresivo para imágenes genéricas */
        /doubleclick\.net/i, /googleadservices\.com/i, /googlesyndication\.com/i, /adsense/i,
        /adservice/i, /\/ad\//i, /\/ads\//i, /track(er|ing)?/i, /pixel/i, /beacon/i, /delivery/i, /click/i, /view/i,
        /impression/i, /redirect/i, /exchange/i, /yield/i, /popunder/i, /popup/i, /popad/i,
        /analytics/i, /collect/i, /telemetry/i, /stats/i, /metrics/i, /segment/i, /gtag/i, /gaq/i,
        /optimizely/i, /hotjar/i, /crazyegg/i, /mouseflow/i, /fullstory/i, /inspectlet/i, /logrocket/i,
        /scorecardresearch\.com/i, /crwdcntrl\.net/i, /adobedtm\.com/i, /demdex\.net/i, /everesttech\.net/i,
        /quantserve\.com/i, /rubiconproject\.com/i, /openx\.net/i, /pubmatic\.com/i, /criteo\.com/i,
        /adnxs\.com/i, /taboola\.com/i, /outbrain\.com/i, /revcontent\.com/i, /mgid\.com/i, /sharethrough\.com/i,
        /bidswitch\.net/i, /smartadserver\.com/i, /contextweb\.com/i, /media\.net/i, /advertising\.com/i,
        /serving-sys\.com/i, /eyeblaster\.com/i, /revsci\.net/i, /yieldlab\.net/i, /adform\.net/i,
        /appnexus\.com/i, /casalemedia\.com/i, /turn\.com/i, /mathtag\.com/i, /sitescout\.com/i,
        /simpli\.fi/i, /fout\.jp/i, /amoad\.com/i, /genieessp\.com/i, /adstir\.com/i, /soom\.cz/i,
        /innovid\.com/i, /flashtalking\.com/i, /sizmek\.com/i, /adroll\.com/i, /lijit\.com/i,
        /districtm\.io/i, /indexexchange\.com/i, /gumgum\.com/i, /teads\.tv/i, /vidible\.tv/i,
        /spotxchange\.com/i, /brightroll\.com/i, /tremorhub\.com/i, /adap\.tv/i, /lkqd\.net/i,
        /stickyadstv\.com/i, /aniview\.com/i, /playtem\.com/i, /exoclick\.com/i, /juicyads\.com/i,
        /ero-advertising\.com/i, /plugrush\.com/i, /trafficjunky\.net/i, /trafficforce\.com/i,
        /traffichaus\.com/i, /zeropark\.com/i, /admaven\.com/i, /propellerads\.com/i, /popads\.net/i,
        /popcash\.net/i, /onclickads\.net/i, /onclicktop\.com/i, /ad-maven\.com/i, /adcash\.com/i,
        /exdynsrv\.com/i, /adk2\.com/i, /adxpansion\.com/i, /adsupply\.com/i, /adreactor\.com/i,
        /adkernel\.com/i, /adtarget\.com/i, /adverdirect\.com/i, /adversal\.com/i, /adxpedia\.com/i,
        /adxcore\.com/i, /adxvalue\.com/i, /adxpremium\.com/i, /adxmarket\.com/i, /adxpartner\.com/i,
        /adxnetwork\.com/i, /adxchange\.com/i, /adxapi\.com/i, /adxserve\.com/i, /adxflow\.com/i,
        /adxpower\.com/i, /adxengine\.com/i, /adxplatform\.com/i, /adxsystem\.com/i, /adxtech\.com/i,
        /adxmedia\.com/i, /adxcreative\.com/i, /adxoptimizer\.com/i, /adxmonetize\.com/i, /adxyield\.com/i,
        /adxperformance\.com/i, /adxanalytics\.com/i, /adxtracking\.com/i, /adxpixel\.com/i, /adxbeacon\.com/i,
        /adxtag\.com/i, /adxcontainer\.com/i, /adxunit\.com/i, /adxslot\.com/i, /adxframe\.com/i,
        /adxbanner\.com/i, /adxcontent\.com/i, /adxtext\.com/i, /adximage\.com/i, /adxvideo\.com/i,
        /adxsidebar\.com/i, /adxheader\.com/i, /adxfooter\.com/i, /adxsticky\.com/i, /adxfloating\.com/i,
        /adxnative\.com/i, /adxsponsored\.com/i, /adxchoices\.com/i, /adxfeed\.com/i, /adxg\.com/i,
        /adxform\.com/i, /adxblade\.com/i, /adxcell\.com/i, /adxition\.com/i, /adxengage\.com/i,
        /adxfactor\.com/i, /adxfusion\.com/i, /adxmagnet\.com/i, /adxmeta\.com/i, /adxmotion\.com/i,
        /adxroll\.com/i, /adxspirit\.com/i, /adxstir\.com/i, /adxswizz\.com/i, /adxtrade\.com/i,
        /adxverster\.com/i, /adxview\.com/i, /adxwave\.com/i, /adxworx\.com/i, /adxzerk\.com/i,
        /adxbutler\.com/i, /adxcolt\.com/i, /adxfox\.com/i, /adxfront\.com/i, /adxgear\.com/i,
        /adxgrs\.com/i, /adxhese\.com/i, /adxhood\.com/i, /adxinfuse\.com/i, /adxjuggler\.com/i,
        /adxlegend\.com/i, /adxman\.com/i, /adxmarvel\.com/i, /adxmaster\.com/i, /adxmedo\.com/i,
        /adxmicro\.com/i, /adxmost\.com/i, /adxnegah\.com/i, /adxnexus\.com/i, /adxnologies\.com/i,
        /adxoperator\.com/i, /adxorika\.com/i, /adxotomi\.com/i, /adxperfect\.com/i, /adxplugg\.com/i,
        /adxscale\.com/i, /adxshost\.com/i, /adxshuffle\.com/i, /adxspruce\.com/i, /adxstage\.com/i,
        /adxsteam\.com/i, /adxswapper\.com/i, /adxswich\.com/i, /adxsynergy\.com/i, /adxtegrity\.com/i,
        /adxthink\.com/i, /adxtoma\.com/i, /adxtruth\.com/i, /adxunity\.com/i, /adxup\.com/i,
        /adxverline\.com/i, /adxverticum\.com/i, /adxvertserve\.com/i
    ];

    const originalFetch = window.fetch;
    window.fetch = function(url, options) {
        const urlString = (typeof url === 'string') ? url : (url && url.url) ? url.url : '';
        if (urlString && blockedKeywordsPatterns.some(pattern => pattern.test(urlString))) {
            config.stats.requestsBlocked++;
            if (config.debugMode) {
                console.log('AdBlocker: Fetch request bloqueado:', urlString);
            }
            return Promise.reject(new Error('Solicitud bloqueada por AdBlocker Avanzado'));
        }
        return originalFetch.call(this, url, options);
    };

    const originalXHROpen = window.XMLHttpRequest.prototype.open;
    window.XMLHttpRequest.prototype.open = function(method, url) {
        if (typeof url === 'string' && blockedKeywordsPatterns.some(pattern => pattern.test(url))) {
            config.stats.requestsBlocked++;
            if (config.debugMode) {
                console.log('AdBlocker: XMLHttpRequest bloqueado:', url);
            }
            /* Lanzar un error o abortar la solicitud de forma que no continúe */
            /* No se puede simplemente retornar; hay que evitar que la solicitud se envíe. */
            /* Esto se logra no llamando al originalXHROpen y posiblemente estableciendo un estado de error. */
            /* Sin embargo, para Tampermonkey, simplemente no llamar al original y no hacer nada más */
            /* puede ser suficiente para que no se envíe, o se puede intentar abortar si se llama a send. */
            /* La forma más segura es interceptar 'send' también. */
            this.isBlockedByAdBlocker = true; /* Marcar para interceptar en 'send' */
            return; /* No llamar a originalXHROpen */
        }
        this.isBlockedByAdBlocker = false;
        return originalXHROpen.apply(this, arguments);
    };

    const originalXHRSend = window.XMLHttpRequest.prototype.send;
    window.XMLHttpRequest.prototype.send = function() {
        if (this.isBlockedByAdBlocker) {
            if (config.debugMode) console.log('AdBlocker: XMLHttpRequest send() prevenido para URL bloqueada.');
            /* Simular un error de red o simplemente no hacer nada */
            /* Disparar un evento de error podría ser más limpio para el código que usa XHR */
            const errorEvent = new ProgressEvent('error');
            if (this.onerror) {
                this.onerror(errorEvent);
            }
            if (this.dispatchEvent) {
                this.dispatchEvent(errorEvent);
            }
            return;
        }
        return originalXHRSend.apply(this, arguments);
    };


    if (config.debugMode) {
        console.log('AdBlocker: Bloqueo de solicitudes por palabras clave activado.');
    }

     /* Bloquear URLs de imágenes de anuncios comunes */
    const adImageUrls = ['/ads/', '/adverts/', 'adbanner', 'adsquare', 'advertisement', 'sponsor', '/popups/', '/banners/', '/adimg/', '/adimage/', '/adpic/', '/adpicture/', '/adgfx/', '/adgraphics/', '/admedia/', '/adserver/', '/adcontent/', '/adtrack/', '/adpixel/', '/adbeacon/', '/adview/', '/adclick/', '/adimpression/', '/adredirect/', '/adexchange/', '/adyield/', '/adtech/', '/adsystem/', '/adplatform/', '/adnetwork/', '/adcreative/', '/adcampaign/', '/adgroup/', '/adunit/', '/adslot/', '/adframe/', '/adbox/', '/adcontainer/', '/adwrapper/', '/adplaceholder/', '/adchoices/', '/feedads/', '/gads/', '/adform/', '/adblade/', '/adcell/', '/adition/', '/adengage/', '/adfactor/', '/adfusion/', '/admagnet/', '/admeta/', '/admotion/', '/adreactor/', '/adroll/', '/adspirit/', '/adstir/', '/adswizz/', '/adtarget/', '/adtrade/', 'adversal', 'adverster', 'adwave', 'adworx', 'adx', 'adzerk', 'adbutler', 'adcolt', 'adfox', 'adfront', 'adgear', 'adgrs', 'adhese', 'adhood', 'adinfuse', 'adjuggler', 'adlegend', 'adman', 'admarvel', 'admaster', 'admedo', 'admicro', 'admost', 'adnegah', 'adnexus', 'adnologies', 'adoperator', 'adorika', 'adotomi', 'adperfect', 'adplugg', 'adscale', 'adshost', 'adshuffle', 'adspruce', 'adstage', 'adsteam', 'adsupply', 'adswapper', 'adswich', 'adsynergy', 'adtegrity', 'adthink', 'adtoma', 'adtruth', 'adunity', 'adup', 'adverline', 'adverticum', 'advertserve', 'appnexus', 'contextweb', 'conversantmedia', 'infolinks', 'intellitxt', 'kontera', 'ligatus', 'matomymedia', 'media.net', 'meridian', 'netseer', 'pulsepoint', 'rhythmone', 'sharethrough', 'skimlinks', 'sovrn', 'specificmedia', 'spotxchange', 'tribalfusion', 'undertone', 'valueclick', 'vibrantmedia', 'vidora', 'viglink', 'xaxis', 'yieldads', 'yieldbot', 'yieldbuild', 'yieldlab', 'yieldmanager', 'yieldmo', 'zdbb.net', 'zedo', 'zergnet', 'zumobi', 'bannerflow', 'bannergrabber', 'bannersnack', 'popads', 'popcash', 'onclickads', 'onclicktop', 'exoclick', 'juicyads', 'eroadvertising', 'plugrush', 'trafficjunky', 'trafficforce', 'traffichaus', 'zeropark', 'admaven', 'propelleradsmedia', 'revcontent', 'content.ad'];
    adImageUrls.forEach(urlPart => {
        try {
            document.querySelectorAll(`img[src*="${urlPart}"i], div[style*="background-image"][style*="${urlPart}"i], a[style*="background-image"][style*="${urlPart}"i]`).forEach(el => removeElement(el, `Common ad image URL/style: ${urlPart}`));
        } catch (e) {
            if (config.debugMode) console.warn(`Error con selector para adImageUrls: img[src*="${urlPart}"i]`, e);
        }
    });

    } 

})();