Created
October 7, 2025 18:42
-
-
Save yhojann-cl/4a3ab68aa64610ffdbd5955e75dce8e2 to your computer and use it in GitHub Desktop.
Animeflv ads blocker
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // bloqueo-enlaces-externos.js | |
| (function () { | |
| 'use strict'; | |
| const ORIGIN = location.origin; | |
| // ------------------------- | |
| // Utilidades | |
| // ------------------------- | |
| function isExternalHref(href) { | |
| if (!href) return false; | |
| // href puede ser: "#", "javascript:;", "mailto:", "/path", "http://..." | |
| // Normalizamos y consideramos externo si su origin difiere del origin actual | |
| try { | |
| // new URL resolver usa location.href como base para rutas relativas | |
| const u = new URL(href, location.href); | |
| return u.origin !== ORIGIN; | |
| } catch (e) { | |
| // si no es parseable (por ejemplo "javascript:;") lo consideramos NO externo | |
| return false; | |
| } | |
| } | |
| function neutralizeAnchor(a) { | |
| try { | |
| if (!a || a.dataset.__external_blocked) return; | |
| const href = a.getAttribute('href') || a.href || ''; | |
| if (!isExternalHref(href)) return; | |
| // Guardamos original por si más tarde queremos revisar | |
| a.dataset.__originalHref = href; | |
| a.dataset.__external_blocked = '1'; | |
| // Cambiamos href a inofensivo y ajustamos atributos | |
| a.setAttribute('href', '#'); | |
| a.setAttribute('rel', 'noopener noreferrer'); | |
| a.setAttribute('data-blocked', 'external'); | |
| a.setAttribute('title', (a.getAttribute('title') || '') + ' — Enlace bloqueado (origen externo)'); | |
| // opcional: evitar que el enlace capture el pointer (si quieres) | |
| // a.style.pointerEvents = 'none'; | |
| } catch (err) { | |
| // no fallar la página por esto | |
| console.error('neutralizeAnchor error', err); | |
| } | |
| } | |
| function scanAndNeutralize(root = document) { | |
| // Buscamos anchors con href, incluidas las añadidas dinámicamente | |
| const anchors = root.querySelectorAll ? root.querySelectorAll('a[href]') : []; | |
| for (const a of anchors) neutralizeAnchor(a); | |
| } | |
| // ------------------------- | |
| // Observador de DOM | |
| // ------------------------- | |
| const mo = new MutationObserver((mutations) => { | |
| for (const m of mutations) { | |
| // nodos añadidos | |
| if (m.addedNodes && m.addedNodes.length) { | |
| for (const node of m.addedNodes) { | |
| if (node.nodeType !== Node.ELEMENT_NODE) continue; | |
| // si es <a> directo | |
| if (node.matches && node.matches('a[href]')) { | |
| neutralizeAnchor(node); | |
| } | |
| // si tiene descendientes con <a> | |
| scanAndNeutralize(node); | |
| } | |
| } | |
| // cambios de atributos (por ejemplo cambio de href) | |
| if (m.type === 'attributes' && m.target && m.attributeName === 'href') { | |
| const target = m.target; | |
| if (target.matches && target.matches('a')) { | |
| neutralizeAnchor(target); | |
| } | |
| } | |
| } | |
| }); | |
| mo.observe(document.documentElement || document, { | |
| childList: true, | |
| subtree: true, | |
| attributes: true, | |
| attributeFilter: ['href'] | |
| }); | |
| // ------------------------- | |
| // Interceptar clicks (fase captura) | |
| // ------------------------- | |
| document.addEventListener('click', function (ev) { | |
| try { | |
| const el = ev.target && ev.target.closest ? ev.target.closest('a') : null; | |
| if (!el) return; | |
| // si es un anchor bloqueado, o apunta a externo, impedimos navegación | |
| if (el.dataset && el.dataset.__external_blocked) { | |
| ev.preventDefault(); | |
| ev.stopImmediatePropagation(); | |
| // opcional: emitir evento custom para logging | |
| el.dispatchEvent(new CustomEvent('external-link-blocked', { bubbles: true })); | |
| return; | |
| } | |
| // si no estaba bloqueado pero su href actual apunta a origin externo, | |
| // lo neutralizamos en caliente y evitamos el click | |
| const href = el.getAttribute('href') || el.href || ''; | |
| if (isExternalHref(href)) { | |
| neutralizeAnchor(el); | |
| ev.preventDefault(); | |
| ev.stopImmediatePropagation(); | |
| el.dispatchEvent(new CustomEvent('external-link-blocked', { bubbles: true })); | |
| return; | |
| } | |
| } catch (err) { | |
| console.error('click interception error', err); | |
| } | |
| }, true); // <<--- capture = true (muy importante) | |
| // ------------------------- | |
| // Wrappers protectores (mejorar cobertura) | |
| // ------------------------- | |
| // override window.open si no lo hicieron ya (seguro) | |
| try { | |
| const _open = window.open; | |
| window.open = function (...args) { | |
| // bloqueamos apertura a dominios externos si el primer arg es URL | |
| const url = args && args[0]; | |
| if (url && isExternalHref(url)) { | |
| console.warn('window.open bloqueado hacia:', url); | |
| return null; | |
| } | |
| return _open.apply(this, args); | |
| }; | |
| } catch (e) { /* ignore */ } | |
| // wrap location.assign/replace para prevenir asignaciones via JS | |
| try { | |
| const origAssign = location.assign.bind(location); | |
| location.assign = function (url) { | |
| if (url && isExternalHref(url)) { | |
| console.warn('location.assign bloqueada a', url); | |
| return; | |
| } | |
| return origAssign(url); | |
| }; | |
| } catch (e) { /* algunos navegadores no permiten reconfigurar */ } | |
| try { | |
| const origReplace = location.replace.bind(location); | |
| location.replace = function (url) { | |
| if (url && isExternalHref(url)) { | |
| console.warn('location.replace bloqueada a', url); | |
| return; | |
| } | |
| return origReplace(url); | |
| }; | |
| } catch (e) { /* ignore */ } | |
| // ------------------------- | |
| // Escaneo inicial | |
| // ------------------------- | |
| if (document.readyState === 'loading') { | |
| document.addEventListener('DOMContentLoaded', () => scanAndNeutralize(document)); | |
| } else { | |
| scanAndNeutralize(document); | |
| } | |
| // ------------------------- | |
| // API interna opcional (para debug) | |
| // ------------------------- | |
| window.__externalLinkProtector = { | |
| neutralizeAnchor, | |
| scanAndNeutralize, | |
| isExternalHref, | |
| disconnectObserver: () => mo.disconnect() | |
| }; | |
| console.info('External Link Protector activo. Origen protegido:', ORIGIN); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment