feat(js): implement intersection observer lazy loading for adsense
- Add per-slot lazy loading with Intersection Observer API - Implement fill detection via MutationObserver and data-ad-status - Add configurable rootMargin and fillTimeout from database - Generate dynamic CSS based on lazy_loading_enabled setting - Add legacy mode fallback for browsers without IO support - Include backup of previous implementation (adsense-loader.legacy.js) - Add OpenSpec documentation with test plan (72 tests verified) Schema changes: - Add lazy_loading_enabled (boolean, default: true) - Add lazy_rootmargin (select: 0-500px, default: 200) - Add lazy_fill_timeout (select: 3000-10000ms, default: 5000) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,306 +1,639 @@
|
||||
/**
|
||||
* Cargador Retrasado de AdSense
|
||||
* AdSense Lazy Loader con Intersection Observer
|
||||
*
|
||||
* Este script retrasa la carga de Google AdSense hasta que haya interacción
|
||||
* del usuario o se cumpla un timeout, mejorando el rendimiento de carga inicial.
|
||||
* Carga anuncios AdSense individualmente cuando entran al viewport,
|
||||
* detecta si reciben contenido, y oculta slots vacios.
|
||||
*
|
||||
* @package ROI_Theme
|
||||
* @since 1.0.0
|
||||
* @since 1.5.0
|
||||
* @version 2.0.0 - Refactorizado con Intersection Observer
|
||||
*/
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
// Configuración
|
||||
const CONFIG = {
|
||||
timeout: 5000, // Timeout de fallback en milisegundos
|
||||
loadedClass: 'adsense-loaded',
|
||||
debug: true // TEMPORAL: Habilitado para diagnóstico
|
||||
// =========================================================================
|
||||
// CONFIGURACION
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* Configuracion por defecto, sobrescrita por window.roiAdsenseConfig
|
||||
*/
|
||||
var DEFAULT_CONFIG = {
|
||||
lazyEnabled: true,
|
||||
rootMargin: '200px 0px',
|
||||
fillTimeout: 5000,
|
||||
debug: false
|
||||
};
|
||||
|
||||
// Estado
|
||||
let adsenseLoaded = false;
|
||||
let loadTimeout = null;
|
||||
/**
|
||||
* Obtiene configuracion desde wp_localize_script o usa defaults
|
||||
*/
|
||||
function getConfig() {
|
||||
var wpConfig = window.roiAdsenseConfig || {};
|
||||
return {
|
||||
lazyEnabled: typeof wpConfig.lazyEnabled !== 'undefined' ? wpConfig.lazyEnabled : DEFAULT_CONFIG.lazyEnabled,
|
||||
rootMargin: wpConfig.rootMargin || DEFAULT_CONFIG.rootMargin,
|
||||
fillTimeout: typeof wpConfig.fillTimeout !== 'undefined' ? parseInt(wpConfig.fillTimeout, 10) : DEFAULT_CONFIG.fillTimeout,
|
||||
debug: typeof wpConfig.debug !== 'undefined' ? wpConfig.debug : DEFAULT_CONFIG.debug
|
||||
};
|
||||
}
|
||||
|
||||
var CONFIG = getConfig();
|
||||
|
||||
// =========================================================================
|
||||
// ESTADO GLOBAL
|
||||
// =========================================================================
|
||||
|
||||
var libraryLoaded = false;
|
||||
var libraryLoading = false;
|
||||
var libraryLoadFailed = false;
|
||||
var loadRetryCount = 0;
|
||||
var MAX_LOAD_RETRIES = 1;
|
||||
var RETRY_DELAY = 2000;
|
||||
|
||||
/** @type {IntersectionObserver|null} */
|
||||
var slotObserver = null;
|
||||
|
||||
/** @type {Map<Element, MutationObserver>} */
|
||||
var fillObservers = new Map();
|
||||
|
||||
/** @type {Map<Element, number>} */
|
||||
var fillTimeouts = new Map();
|
||||
|
||||
/** @type {Set<Element>} */
|
||||
var activatedSlots = new Set();
|
||||
|
||||
/** @type {Array<Function>} */
|
||||
var pendingActivations = [];
|
||||
|
||||
// =========================================================================
|
||||
// LOGGING
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* Registra mensajes de debug si el modo debug está habilitado
|
||||
* @param {string} message - El mensaje a registrar
|
||||
* Log condicional basado en CONFIG.debug
|
||||
* @param {string} message
|
||||
* @param {string} [level='log'] - 'log', 'warn', 'error'
|
||||
*/
|
||||
function debugLog(message) {
|
||||
if (CONFIG.debug && typeof console !== 'undefined') {
|
||||
console.log('[AdSense Loader] ' + message);
|
||||
function debugLog(message, level) {
|
||||
if (!CONFIG.debug || typeof console === 'undefined') {
|
||||
return;
|
||||
}
|
||||
level = level || 'log';
|
||||
var prefix = '[AdSense Lazy] ';
|
||||
if (level === 'error') {
|
||||
console.error(prefix + message);
|
||||
} else if (level === 'warn') {
|
||||
console.warn(prefix + message);
|
||||
} else {
|
||||
console.log(prefix + message);
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// DETECCION DE SOPORTE
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* Carga los scripts de AdSense e inicializa los ads
|
||||
* Verifica si el navegador soporta Intersection Observer
|
||||
*/
|
||||
function loadAdSense() {
|
||||
// Prevenir múltiples cargas
|
||||
if (adsenseLoaded) {
|
||||
debugLog('AdSense ya fue cargado, omitiendo...');
|
||||
function hasIntersectionObserverSupport() {
|
||||
return typeof window.IntersectionObserver !== 'undefined';
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifica si el navegador soporta MutationObserver
|
||||
*/
|
||||
function hasMutationObserverSupport() {
|
||||
return typeof window.MutationObserver !== 'undefined';
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// CARGA DE BIBLIOTECA ADSENSE
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* Carga la biblioteca adsbygoogle.js
|
||||
* @param {Function} onSuccess
|
||||
* @param {Function} onError
|
||||
*/
|
||||
function loadAdSenseLibrary(onSuccess, onError) {
|
||||
if (libraryLoaded) {
|
||||
debugLog('Biblioteca ya cargada');
|
||||
onSuccess();
|
||||
return;
|
||||
}
|
||||
|
||||
adsenseLoaded = true;
|
||||
debugLog('Cargando scripts de AdSense...');
|
||||
|
||||
// Limpiar el timeout si existe
|
||||
if (loadTimeout) {
|
||||
clearTimeout(loadTimeout);
|
||||
loadTimeout = null;
|
||||
if (libraryLoading) {
|
||||
debugLog('Biblioteca en proceso de carga, encolando callback');
|
||||
pendingActivations.push(onSuccess);
|
||||
return;
|
||||
}
|
||||
|
||||
// Remover event listeners para prevenir múltiples triggers
|
||||
removeEventListeners();
|
||||
libraryLoading = true;
|
||||
debugLog('Cargando biblioteca adsbygoogle.js...');
|
||||
|
||||
// Cargar etiquetas de script de AdSense y esperar a que cargue
|
||||
// IMPORTANTE: Debe esperar a que adsbygoogle.js cargue antes de ejecutar push
|
||||
loadAdSenseScripts(function() {
|
||||
debugLog('Biblioteca AdSense cargada, ejecutando push scripts...');
|
||||
var scriptTags = document.querySelectorAll('script[data-adsense-script]');
|
||||
if (scriptTags.length === 0) {
|
||||
debugLog('No se encontro script[data-adsense-script]', 'warn');
|
||||
libraryLoading = false;
|
||||
onError();
|
||||
return;
|
||||
}
|
||||
|
||||
// Ejecutar scripts de push de AdSense
|
||||
executeAdSensePushScripts();
|
||||
var oldScript = scriptTags[0];
|
||||
var newScript = document.createElement('script');
|
||||
newScript.src = oldScript.src;
|
||||
newScript.async = true;
|
||||
|
||||
// Agregar clase loaded al body
|
||||
document.body.classList.add(CONFIG.loadedClass);
|
||||
if (oldScript.getAttribute('crossorigin')) {
|
||||
newScript.crossOrigin = oldScript.getAttribute('crossorigin');
|
||||
}
|
||||
|
||||
debugLog('Carga de AdSense completada');
|
||||
});
|
||||
}
|
||||
newScript.onload = function() {
|
||||
debugLog('Biblioteca cargada exitosamente');
|
||||
libraryLoaded = true;
|
||||
libraryLoading = false;
|
||||
window.adsbygoogle = window.adsbygoogle || [];
|
||||
|
||||
/**
|
||||
* Encuentra y carga todas las etiquetas de script de AdSense retrasadas
|
||||
* @param {Function} callback - Función a ejecutar cuando la biblioteca cargue
|
||||
*/
|
||||
function loadAdSenseScripts(callback) {
|
||||
const delayedScripts = document.querySelectorAll('script[data-adsense-script]');
|
||||
|
||||
if (delayedScripts.length === 0) {
|
||||
debugLog('No se encontraron scripts retrasados de AdSense');
|
||||
// Ejecutar callback de todas formas (puede haber ads sin script principal)
|
||||
if (typeof callback === 'function') {
|
||||
// Ejecutar callbacks pendientes
|
||||
onSuccess();
|
||||
while (pendingActivations.length > 0) {
|
||||
var callback = pendingActivations.shift();
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
newScript.onerror = function() {
|
||||
debugLog('Error cargando biblioteca (intento ' + (loadRetryCount + 1) + ')', 'error');
|
||||
libraryLoading = false;
|
||||
|
||||
if (loadRetryCount < MAX_LOAD_RETRIES) {
|
||||
loadRetryCount++;
|
||||
debugLog('Reintentando en ' + RETRY_DELAY + 'ms...');
|
||||
setTimeout(function() {
|
||||
loadAdSenseLibrary(onSuccess, onError);
|
||||
}, RETRY_DELAY);
|
||||
} else {
|
||||
debugLog('Maximo de reintentos alcanzado', 'error');
|
||||
libraryLoadFailed = true;
|
||||
onError();
|
||||
}
|
||||
};
|
||||
|
||||
oldScript.parentNode.replaceChild(newScript, oldScript);
|
||||
}
|
||||
|
||||
/**
|
||||
* Marca todos los slots como error cuando la biblioteca falla
|
||||
*/
|
||||
function markAllSlotsAsError() {
|
||||
var slots = document.querySelectorAll('.roi-ad-slot[data-ad-lazy="true"]');
|
||||
slots.forEach(function(slot) {
|
||||
slot.classList.add('roi-ad-error');
|
||||
cleanupSlot(slot);
|
||||
});
|
||||
debugLog('Todos los slots marcados como error', 'error');
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// ACTIVACION DE SLOTS
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* Activa un slot individual ejecutando adsbygoogle.push()
|
||||
* @param {Element} slot
|
||||
*/
|
||||
function activateSlot(slot) {
|
||||
if (activatedSlots.has(slot)) {
|
||||
debugLog('Slot ya activado, omitiendo');
|
||||
return;
|
||||
}
|
||||
|
||||
debugLog('Se encontraron ' + delayedScripts.length + ' script(s) retrasado(s) de AdSense');
|
||||
if (libraryLoadFailed) {
|
||||
debugLog('Biblioteca fallida, marcando slot como error');
|
||||
slot.classList.add('roi-ad-error');
|
||||
return;
|
||||
}
|
||||
|
||||
var scriptsLoaded = 0;
|
||||
var totalScripts = delayedScripts.length;
|
||||
activatedSlots.add(slot);
|
||||
|
||||
delayedScripts.forEach(function(oldScript) {
|
||||
const newScript = document.createElement('script');
|
||||
|
||||
// Copiar atributos
|
||||
if (oldScript.src) {
|
||||
newScript.src = oldScript.src;
|
||||
var doActivation = function() {
|
||||
var ins = slot.querySelector('ins.adsbygoogle');
|
||||
if (!ins) {
|
||||
debugLog('No se encontro <ins> en slot', 'warn');
|
||||
slot.classList.add('roi-ad-empty');
|
||||
return;
|
||||
}
|
||||
|
||||
// Establecer atributo async
|
||||
newScript.async = true;
|
||||
debugLog('Activando slot: ' + (ins.getAttribute('data-ad-slot') || 'unknown'));
|
||||
|
||||
// Copiar crossorigin si está presente
|
||||
if (oldScript.getAttribute('crossorigin')) {
|
||||
newScript.crossorigin = oldScript.getAttribute('crossorigin');
|
||||
// Ejecutar push
|
||||
try {
|
||||
window.adsbygoogle = window.adsbygoogle || [];
|
||||
window.adsbygoogle.push({});
|
||||
} catch (e) {
|
||||
debugLog('Error en push: ' + e.message, 'error');
|
||||
slot.classList.add('roi-ad-error');
|
||||
return;
|
||||
}
|
||||
|
||||
// Esperar a que cargue antes de ejecutar callback
|
||||
newScript.onload = function() {
|
||||
scriptsLoaded++;
|
||||
debugLog('Script cargado (' + scriptsLoaded + '/' + totalScripts + '): ' + newScript.src.substring(0, 50) + '...');
|
||||
if (scriptsLoaded === totalScripts && typeof callback === 'function') {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
// Iniciar observacion de llenado
|
||||
startFillDetection(slot, ins);
|
||||
};
|
||||
|
||||
newScript.onerror = function() {
|
||||
scriptsLoaded++;
|
||||
debugLog('Error cargando script: ' + newScript.src);
|
||||
if (scriptsLoaded === totalScripts && typeof callback === 'function') {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
// Si la biblioteca ya cargo, activar inmediatamente
|
||||
if (libraryLoaded) {
|
||||
doActivation();
|
||||
} else {
|
||||
// Cargar biblioteca y luego activar
|
||||
loadAdSenseLibrary(doActivation, function() {
|
||||
markAllSlotsAsError();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Reemplazar script viejo con el nuevo
|
||||
oldScript.parentNode.replaceChild(newScript, oldScript);
|
||||
// =========================================================================
|
||||
// DETECCION DE LLENADO
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* Inicia la deteccion de llenado para un slot
|
||||
* @param {Element} slot
|
||||
* @param {Element} ins
|
||||
*/
|
||||
function startFillDetection(slot, ins) {
|
||||
// Verificar inmediatamente si ya tiene contenido
|
||||
if (checkFillStatus(slot, ins)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Configurar timeout
|
||||
var timeoutId = setTimeout(function() {
|
||||
debugLog('Timeout de llenado alcanzado');
|
||||
markSlotEmpty(slot);
|
||||
}, CONFIG.fillTimeout);
|
||||
fillTimeouts.set(slot, timeoutId);
|
||||
|
||||
// Configurar MutationObserver si hay soporte
|
||||
if (hasMutationObserverSupport()) {
|
||||
var mutationObserver = new MutationObserver(function(mutations) {
|
||||
if (checkFillStatus(slot, ins)) {
|
||||
// Ya procesado en checkFillStatus
|
||||
}
|
||||
});
|
||||
|
||||
mutationObserver.observe(ins, {
|
||||
attributes: true,
|
||||
childList: true,
|
||||
subtree: true,
|
||||
attributeFilter: ['data-ad-status']
|
||||
});
|
||||
|
||||
fillObservers.set(slot, mutationObserver);
|
||||
} else {
|
||||
// Sin MutationObserver, solo usar timeout
|
||||
debugLog('Sin soporte MutationObserver, usando solo timeout');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifica el estado de llenado de un slot
|
||||
* @param {Element} slot
|
||||
* @param {Element} ins
|
||||
* @returns {boolean} true si el estado fue determinado (filled o empty)
|
||||
*/
|
||||
function checkFillStatus(slot, ins) {
|
||||
// Criterio 1: data-ad-status attribute
|
||||
var status = ins.getAttribute('data-ad-status');
|
||||
if (status === 'filled') {
|
||||
debugLog('Slot llenado (data-ad-status=filled)');
|
||||
markSlotFilled(slot);
|
||||
return true;
|
||||
}
|
||||
if (status === 'unfilled') {
|
||||
debugLog('Slot vacio (data-ad-status=unfilled)');
|
||||
markSlotEmpty(slot);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Criterio 2 (fallback): iframe presente
|
||||
var iframe = ins.querySelector('iframe');
|
||||
if (iframe) {
|
||||
debugLog('Slot llenado (iframe detectado)');
|
||||
markSlotFilled(slot);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Criterio 3 (fallback): div con id presente
|
||||
var divWithId = ins.querySelector('div[id]');
|
||||
if (divWithId) {
|
||||
debugLog('Slot llenado (div con id detectado)');
|
||||
markSlotFilled(slot);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marca un slot como llenado
|
||||
* @param {Element} slot
|
||||
*/
|
||||
function markSlotFilled(slot) {
|
||||
slot.classList.remove('roi-ad-empty', 'roi-ad-error');
|
||||
slot.classList.add('roi-ad-filled');
|
||||
cleanupSlot(slot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Marca un slot como vacio
|
||||
* @param {Element} slot
|
||||
*/
|
||||
function markSlotEmpty(slot) {
|
||||
slot.classList.remove('roi-ad-filled', 'roi-ad-error');
|
||||
slot.classList.add('roi-ad-empty');
|
||||
cleanupSlot(slot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Limpia observadores y timeouts de un slot
|
||||
* @param {Element} slot
|
||||
*/
|
||||
function cleanupSlot(slot) {
|
||||
// Limpiar timeout
|
||||
if (fillTimeouts.has(slot)) {
|
||||
clearTimeout(fillTimeouts.get(slot));
|
||||
fillTimeouts.delete(slot);
|
||||
}
|
||||
|
||||
// Limpiar MutationObserver
|
||||
if (fillObservers.has(slot)) {
|
||||
fillObservers.get(slot).disconnect();
|
||||
fillObservers.delete(slot);
|
||||
}
|
||||
|
||||
// Dejar de observar con IntersectionObserver
|
||||
if (slotObserver) {
|
||||
slotObserver.unobserve(slot);
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// INTERSECTION OBSERVER
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* Inicializa el Intersection Observer para slots
|
||||
*/
|
||||
function initIntersectionObserver() {
|
||||
if (!hasIntersectionObserverSupport()) {
|
||||
debugLog('Sin soporte Intersection Observer, usando modo legacy', 'warn');
|
||||
return false;
|
||||
}
|
||||
|
||||
var options = {
|
||||
root: null,
|
||||
rootMargin: CONFIG.rootMargin,
|
||||
threshold: 0
|
||||
};
|
||||
|
||||
slotObserver = new IntersectionObserver(function(entries) {
|
||||
entries.forEach(function(entry) {
|
||||
if (entry.isIntersecting) {
|
||||
var slot = entry.target;
|
||||
debugLog('Slot entro al viewport');
|
||||
activateSlot(slot);
|
||||
}
|
||||
});
|
||||
}, options);
|
||||
|
||||
debugLog('Intersection Observer inicializado con rootMargin: ' + CONFIG.rootMargin);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Observa todos los slots lazy en la pagina
|
||||
*/
|
||||
function observeAllSlots() {
|
||||
var slots = document.querySelectorAll('.roi-ad-slot[data-ad-lazy="true"]');
|
||||
debugLog('Encontrados ' + slots.length + ' slots para observar');
|
||||
|
||||
slots.forEach(function(slot) {
|
||||
if (!activatedSlots.has(slot)) {
|
||||
slotObserver.observe(slot);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Ejecuta scripts de push de AdSense retrasados
|
||||
* Observa nuevos slots agregados dinamicamente
|
||||
*/
|
||||
function executeAdSensePushScripts() {
|
||||
const delayedPushScripts = document.querySelectorAll('script[data-adsense-push]');
|
||||
function observeNewSlots() {
|
||||
var slots = document.querySelectorAll('.roi-ad-slot[data-ad-lazy="true"]');
|
||||
var newCount = 0;
|
||||
|
||||
if (delayedPushScripts.length === 0) {
|
||||
debugLog('No se encontraron scripts de push retrasados de AdSense');
|
||||
slots.forEach(function(slot) {
|
||||
if (!activatedSlots.has(slot)) {
|
||||
slotObserver.observe(slot);
|
||||
newCount++;
|
||||
}
|
||||
});
|
||||
|
||||
if (newCount > 0) {
|
||||
debugLog('Agregados ' + newCount + ' nuevos slots al observer');
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// MODO LEGACY (FALLBACK)
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* Variables para modo legacy
|
||||
*/
|
||||
var legacyLoaded = false;
|
||||
var legacyTimeout = null;
|
||||
|
||||
/**
|
||||
* Carga todos los ads en modo legacy (sin Intersection Observer)
|
||||
*/
|
||||
function loadAllAdsLegacy() {
|
||||
if (legacyLoaded) {
|
||||
return;
|
||||
}
|
||||
legacyLoaded = true;
|
||||
debugLog('Modo legacy: Cargando todos los ads');
|
||||
|
||||
debugLog('Se encontraron ' + delayedPushScripts.length + ' script(s) de push retrasado(s)');
|
||||
if (legacyTimeout) {
|
||||
clearTimeout(legacyTimeout);
|
||||
}
|
||||
removeLegacyEventListeners();
|
||||
|
||||
loadAdSenseLibrary(function() {
|
||||
executeAllPushScripts();
|
||||
}, function() {
|
||||
debugLog('Error en modo legacy', 'error');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Ejecuta todos los scripts de push en modo legacy
|
||||
*/
|
||||
function executeAllPushScripts() {
|
||||
var pushScripts = document.querySelectorAll('script[data-adsense-push]');
|
||||
debugLog('Ejecutando ' + pushScripts.length + ' scripts de push');
|
||||
|
||||
// Inicializar array adsbygoogle si no existe
|
||||
window.adsbygoogle = window.adsbygoogle || [];
|
||||
|
||||
delayedPushScripts.forEach(function(oldScript) {
|
||||
const scriptContent = oldScript.innerHTML;
|
||||
|
||||
// Crear y ejecutar nuevo script
|
||||
const newScript = document.createElement('script');
|
||||
newScript.innerHTML = scriptContent;
|
||||
pushScripts.forEach(function(oldScript) {
|
||||
var newScript = document.createElement('script');
|
||||
newScript.innerHTML = oldScript.innerHTML;
|
||||
newScript.type = 'text/javascript';
|
||||
|
||||
// Reemplazar script viejo con el nuevo
|
||||
oldScript.parentNode.replaceChild(newScript, oldScript);
|
||||
});
|
||||
|
||||
document.body.classList.add('adsense-loaded');
|
||||
}
|
||||
|
||||
/**
|
||||
* Manejador de eventos para interacciones del usuario
|
||||
* Event handler para modo legacy
|
||||
*/
|
||||
function handleUserInteraction() {
|
||||
debugLog('Interacción del usuario detectada');
|
||||
loadAdSense();
|
||||
function handleLegacyInteraction() {
|
||||
debugLog('Interaccion detectada (modo legacy)');
|
||||
loadAllAdsLegacy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remueve todos los event listeners
|
||||
* Agrega listeners para modo legacy
|
||||
*/
|
||||
function removeEventListeners() {
|
||||
window.removeEventListener('scroll', handleUserInteraction, { passive: true });
|
||||
window.removeEventListener('mousemove', handleUserInteraction, { passive: true });
|
||||
window.removeEventListener('touchstart', handleUserInteraction, { passive: true });
|
||||
window.removeEventListener('click', handleUserInteraction, { passive: true });
|
||||
window.removeEventListener('keydown', handleUserInteraction, { passive: true });
|
||||
function addLegacyEventListeners() {
|
||||
window.addEventListener('scroll', handleLegacyInteraction, { passive: true, once: true });
|
||||
window.addEventListener('mousemove', handleLegacyInteraction, { passive: true, once: true });
|
||||
window.addEventListener('touchstart', handleLegacyInteraction, { passive: true, once: true });
|
||||
window.addEventListener('click', handleLegacyInteraction, { passive: true, once: true });
|
||||
window.addEventListener('keydown', handleLegacyInteraction, { passive: true, once: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* Agrega event listeners para interacciones del usuario
|
||||
* Remueve listeners de modo legacy
|
||||
*/
|
||||
function addEventListeners() {
|
||||
debugLog('Agregando event listeners para interacción del usuario');
|
||||
|
||||
// Evento de scroll - cargar en primer scroll
|
||||
window.addEventListener('scroll', handleUserInteraction, { passive: true, once: true });
|
||||
|
||||
// Movimiento de mouse - cargar cuando el usuario mueve el mouse
|
||||
window.addEventListener('mousemove', handleUserInteraction, { passive: true, once: true });
|
||||
|
||||
// Eventos táctiles - cargar en primer toque (móviles)
|
||||
window.addEventListener('touchstart', handleUserInteraction, { passive: true, once: true });
|
||||
|
||||
// Eventos de click - cargar en primer click
|
||||
window.addEventListener('click', handleUserInteraction, { passive: true, once: true });
|
||||
|
||||
// Eventos de teclado - cargar en primera pulsación de tecla
|
||||
window.addEventListener('keydown', handleUserInteraction, { passive: true, once: true });
|
||||
function removeLegacyEventListeners() {
|
||||
window.removeEventListener('scroll', handleLegacyInteraction, { passive: true });
|
||||
window.removeEventListener('mousemove', handleLegacyInteraction, { passive: true });
|
||||
window.removeEventListener('touchstart', handleLegacyInteraction, { passive: true });
|
||||
window.removeEventListener('click', handleLegacyInteraction, { passive: true });
|
||||
window.removeEventListener('keydown', handleLegacyInteraction, { passive: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* Establece timeout de fallback para cargar AdSense después del tiempo especificado
|
||||
* Inicia modo legacy con listeners de interaccion
|
||||
*/
|
||||
function setTimeoutFallback() {
|
||||
debugLog('Estableciendo timeout de fallback (' + CONFIG.timeout + 'ms)');
|
||||
function initLegacyMode() {
|
||||
debugLog('Iniciando modo legacy');
|
||||
addLegacyEventListeners();
|
||||
|
||||
loadTimeout = setTimeout(function() {
|
||||
debugLog('Timeout alcanzado, cargando AdSense');
|
||||
loadAdSense();
|
||||
}, CONFIG.timeout);
|
||||
legacyTimeout = setTimeout(function() {
|
||||
debugLog('Timeout legacy alcanzado');
|
||||
loadAllAdsLegacy();
|
||||
}, CONFIG.fillTimeout);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// EVENTO DINAMICO
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* Activa slots de AdSense insertados dinamicamente
|
||||
* Escucha el evento 'roi-adsense-activate' disparado por otros scripts
|
||||
* Configura listener para ads dinamicos
|
||||
*/
|
||||
function setupDynamicAdsListener() {
|
||||
window.addEventListener('roi-adsense-activate', function() {
|
||||
debugLog('Evento roi-adsense-activate recibido');
|
||||
|
||||
// Si AdSense aun no ha cargado, forzar carga ahora
|
||||
if (!adsenseLoaded) {
|
||||
debugLog('AdSense no cargado, forzando carga...');
|
||||
loadAdSense();
|
||||
return;
|
||||
if (CONFIG.lazyEnabled && slotObserver) {
|
||||
observeNewSlots();
|
||||
} else if (!legacyLoaded) {
|
||||
loadAllAdsLegacy();
|
||||
} else {
|
||||
// Ya cargado en legacy, ejecutar nuevos push
|
||||
activateDynamicSlotsLegacy();
|
||||
}
|
||||
|
||||
// AdSense ya cargado - activar nuevos slots
|
||||
debugLog('Activando nuevos slots dinamicos...');
|
||||
activateDynamicSlots();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Activa slots de AdSense que fueron insertados despues de la carga inicial
|
||||
* Activa slots dinamicos en modo legacy
|
||||
*/
|
||||
function activateDynamicSlots() {
|
||||
// Buscar scripts de push que aun no han sido ejecutados
|
||||
function activateDynamicSlotsLegacy() {
|
||||
var pendingPushScripts = document.querySelectorAll('script[data-adsense-push][type="text/plain"]');
|
||||
|
||||
if (pendingPushScripts.length === 0) {
|
||||
debugLog('No hay slots pendientes por activar');
|
||||
return;
|
||||
}
|
||||
|
||||
debugLog('Activando ' + pendingPushScripts.length + ' slot(s) dinamico(s)');
|
||||
|
||||
// Asegurar que adsbygoogle existe
|
||||
debugLog('Activando ' + pendingPushScripts.length + ' slots dinamicos (legacy)');
|
||||
window.adsbygoogle = window.adsbygoogle || [];
|
||||
|
||||
pendingPushScripts.forEach(function(oldScript) {
|
||||
try {
|
||||
// Crear nuevo script ejecutable
|
||||
var newScript = document.createElement('script');
|
||||
newScript.type = 'text/javascript';
|
||||
newScript.innerHTML = oldScript.innerHTML;
|
||||
|
||||
// Reemplazar el placeholder con el script real
|
||||
oldScript.parentNode.replaceChild(newScript, oldScript);
|
||||
} catch (e) {
|
||||
debugLog('Error activando slot: ' + e.message);
|
||||
debugLog('Error activando slot dinamico: ' + e.message, 'error');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// INICIALIZACION
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* Inicializa el cargador retrasado de AdSense
|
||||
* Inicializa el sistema
|
||||
*/
|
||||
function init() {
|
||||
// =========================================================================
|
||||
// NUEVO: Siempre configurar listener para ads dinamicos
|
||||
// IMPORTANTE: Esto debe ejecutarse ANTES del early return
|
||||
// porque los ads dinamicos pueden necesitar activarse aunque
|
||||
// el delay global este deshabilitado
|
||||
// =========================================================================
|
||||
// Siempre configurar listener para ads dinamicos
|
||||
setupDynamicAdsListener();
|
||||
debugLog('Listener para ads dinamicos configurado');
|
||||
debugLog('Listener dinamico configurado');
|
||||
|
||||
// Verificar si el retardo de AdSense está habilitado
|
||||
// Verificar si delay esta habilitado globalmente
|
||||
if (!window.roiAdsenseDelayed) {
|
||||
debugLog('Retardo de AdSense no habilitado');
|
||||
debugLog('Delay global no habilitado');
|
||||
return;
|
||||
}
|
||||
|
||||
debugLog('Inicializando cargador retrasado de AdSense');
|
||||
debugLog('Inicializando AdSense Lazy Loader v2.0');
|
||||
debugLog('Config: lazyEnabled=' + CONFIG.lazyEnabled + ', rootMargin=' + CONFIG.rootMargin + ', fillTimeout=' + CONFIG.fillTimeout);
|
||||
|
||||
// Verificar si la página ya está interactiva o completa
|
||||
// Decidir modo de operacion
|
||||
if (!CONFIG.lazyEnabled) {
|
||||
debugLog('Lazy loading deshabilitado, usando modo legacy');
|
||||
initLegacyMode();
|
||||
return;
|
||||
}
|
||||
|
||||
// Intentar inicializar Intersection Observer
|
||||
var observerInitialized = initIntersectionObserver();
|
||||
|
||||
if (!observerInitialized) {
|
||||
// Fallback a modo legacy
|
||||
initLegacyMode();
|
||||
return;
|
||||
}
|
||||
|
||||
// Esperar a que el DOM este listo
|
||||
if (document.readyState === 'interactive' || document.readyState === 'complete') {
|
||||
debugLog('Página ya cargada, iniciando listeners');
|
||||
addEventListeners();
|
||||
setTimeoutFallback();
|
||||
observeAllSlots();
|
||||
} else {
|
||||
// Esperar a que el DOM esté listo
|
||||
debugLog('Esperando a DOMContentLoaded');
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
debugLog('DOMContentLoaded disparado');
|
||||
addEventListeners();
|
||||
setTimeoutFallback();
|
||||
observeAllSlots();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Iniciar inicialización
|
||||
// Iniciar
|
||||
init();
|
||||
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user