Skip to content

Instantly share code, notes, and snippets.

@yhojann-cl
Created October 7, 2025 18:42
Show Gist options
  • Select an option

  • Save yhojann-cl/4a3ab68aa64610ffdbd5955e75dce8e2 to your computer and use it in GitHub Desktop.

Select an option

Save yhojann-cl/4a3ab68aa64610ffdbd5955e75dce8e2 to your computer and use it in GitHub Desktop.
Animeflv ads blocker
// 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