# Especificacion: AdSense Lazy Loading ## Purpose Define el comportamiento del sistema de carga diferida de anuncios AdSense usando Intersection Observer para cargar ads individualmente cuando entran al viewport, ocultando slots que no reciben contenido. ## ADDED Requirements ### Requirement: Carga Individual por Visibilidad The system MUST load each AdSense ad slot individually when it enters the viewport, NOT all at once. #### Scenario: Slot entra al viewport por primera vez - **WHEN** un elemento `.roi-ad-slot[data-ad-lazy="true"]` entra al viewport (considerando rootMargin) - **THEN** el sistema DEBE ejecutar `adsbygoogle.push({})` SOLO para ese slot - **AND** el sistema DEBE marcar el slot como "activado" para no procesarlo de nuevo - **AND** el sistema DEBE observar el `` interno para detectar contenido #### Scenario: Multiples slots en viewport inicial - **GIVEN** la pagina tiene 3 slots visibles en el viewport inicial - **WHEN** la pagina termina de cargar - **THEN** el sistema DEBE activar los 3 slots en orden DOM (sin delay entre ellos) - **AND** la activacion es sincrona: push() → siguiente push() inmediatamente - **AND** el sistema NO DEBE activar slots que estan fuera del viewport **Clarificacion:** "Secuencial" significa en orden DOM, uno tras otro sin delay artificial. NO hay setTimeout entre activaciones. El Intersection Observer dispara callbacks para todos los elementos visibles en el mismo frame. #### Scenario: Usuario hace scroll rapido - **GIVEN** el usuario hace scroll rapido pasando varios slots - **WHEN** los slots entran y salen del viewport rapidamente - **THEN** el sistema DEBE activar cada slot que entre al viewport - **AND** el sistema NO DEBE cancelar la activacion si el slot sale del viewport --- ### Requirement: Biblioteca Cargada Una Sola Vez The system MUST load the `adsbygoogle.js` library only once, when the first slot is activated. #### Scenario: Primer slot activado - **GIVEN** la biblioteca `adsbygoogle.js` NO ha sido cargada - **WHEN** el primer slot entra al viewport - **THEN** el sistema DEBE cargar la biblioteca - **AND** el sistema DEBE esperar a que la biblioteca cargue (onload callback) - **AND** ENTONCES ejecutar el push para ese slot #### Scenario: Slots subsecuentes - **GIVEN** la biblioteca `adsbygoogle.js` YA fue cargada - **WHEN** otro slot entra al viewport - **THEN** el sistema DEBE ejecutar el push inmediatamente - **AND** el sistema NO DEBE intentar cargar la biblioteca de nuevo --- ### Requirement: Slots Ocultos por Defecto The system MUST hide ad slots by default and show them only when they have content. #### Scenario: Slot en estado inicial - **WHEN** la pagina renderiza un `.roi-ad-slot[data-ad-lazy="true"]` - **THEN** el slot DEBE tener `display: none` via CSS dinamico - **AND** el slot NO DEBE ocupar espacio en el layout #### Scenario: Slot recibe contenido de Google - **GIVEN** un slot fue activado con push() - **WHEN** Google inyecta contenido dentro del `` - **THEN** el sistema DEBE agregar clase `roi-ad-filled` al slot - **AND** el slot DEBE hacerse visible (`display: block`) #### Scenario: Slot NO recibe contenido (timeout) - **GIVEN** un slot fue activado con push() - **WHEN** pasa el tiempo configurado en `lazy_fill_timeout` sin que Google inyecte contenido - **THEN** el sistema DEBE agregar clase `roi-ad-empty` al slot - **AND** el slot DEBE permanecer oculto - **AND** el sistema DEBE dejar de observar ese slot --- ### Requirement: Pre-carga con rootMargin The system MUST pre-load ads before they enter the visible viewport to ensure smooth UX. #### Scenario: Configuracion de rootMargin - **WHEN** se inicializa el Intersection Observer - **THEN** DEBE usar el valor de `lazy_rootmargin` desde configuracion - **AND** el formato DEBE ser `'{value}px 0px'` #### Scenario: Slot dentro del rootMargin - **GIVEN** un slot esta 150px debajo del viewport visible - **AND** `lazy_rootmargin` es 200 - **WHEN** el Intersection Observer evalua visibilidad - **THEN** el slot DEBE considerarse "visible" y activarse --- ### Requirement: Deteccion de Contenido con Criterios Concretos The system MUST use specific criteria to determine when an ad slot has been filled. #### Scenario: Google agrega atributo data-ad-status="filled" - **GIVEN** un slot fue activado - **WHEN** Google agrega `data-ad-status="filled"` al `` - **THEN** el sistema DEBE marcar inmediatamente como `roi-ad-filled` - **AND** el sistema DEBE desconectar observadores de ese slot #### Scenario: Google agrega atributo data-ad-status="unfilled" - **GIVEN** un slot fue activado - **WHEN** Google agrega `data-ad-status="unfilled"` al `` - **THEN** el sistema DEBE marcar inmediatamente como `roi-ad-empty` - **AND** el sistema DEBE desconectar observadores de ese slot #### Scenario: Fallback - Google inyecta iframe sin atributo - **GIVEN** un slot fue activado - **AND** el `` NO tiene atributo `data-ad-status` - **WHEN** Google agrega un `