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:
FrankZamora
2025-12-10 15:48:20 -06:00
parent 555541b2a0
commit 179a83e9cd
14 changed files with 3303 additions and 201 deletions

View File

@@ -0,0 +1,111 @@
# Cambios al Schema: adsense-placement.json
## Resumen
Agregar 3 campos nuevos al grupo `behavior` para configurar el lazy loading de anuncios.
**Nota:** Los campos van en grupo `behavior` (priority 70) porque configuran el comportamiento del componente, no formularios de exclusion.
## Campos a Agregar
Ubicacion: `groups.behavior.fields`
### Campo 1: lazy_loading_enabled
```json
"lazy_loading_enabled": {
"type": "boolean",
"label": "Lazy Loading de Anuncios",
"default": true,
"editable": true,
"description": "Cargar anuncios individualmente al entrar al viewport (mejora fill rate)"
}
```
### Campo 2: lazy_rootmargin
```json
"lazy_rootmargin": {
"type": "select",
"label": "Pre-carga (px antes del viewport)",
"default": "200",
"editable": true,
"options": {
"0": "0px (sin pre-carga)",
"100": "100px",
"200": "200px (recomendado)",
"300": "300px",
"400": "400px",
"500": "500px"
},
"description": "Pixeles de anticipacion para iniciar carga de anuncio"
}
```
**Nota:** Tipo `select` en lugar de `number` porque el schema solo soporta: boolean, text, textarea, url, select, color.
### Campo 3: lazy_fill_timeout
```json
"lazy_fill_timeout": {
"type": "select",
"label": "Timeout de llenado (ms)",
"default": "5000",
"editable": true,
"options": {
"3000": "3 segundos",
"5000": "5 segundos (recomendado)",
"7000": "7 segundos",
"10000": "10 segundos"
},
"description": "Tiempo maximo para esperar contenido de Google antes de ocultar slot"
}
```
## Comando de Sincronizacion
Despues de actualizar el JSON:
```bash
wp roi-theme sync-component adsense-placement
```
## Version del Schema
Incrementar version de `1.4.0` a `1.5.0` para reflejar nueva funcionalidad.
## Relacion con delay_enabled
El campo `delay_enabled` (en grupo `forms`) controla si la **biblioteca** `adsbygoogle.js` se carga con retraso.
El campo `lazy_loading_enabled` (en grupo `behavior`) controla si los **slots individuales** se activan por visibilidad.
**Ambos pueden estar activos simultaneamente** - son complementarios:
- `delay_enabled: true` = biblioteca no se carga hasta interaccion/timeout
- `lazy_loading_enabled: true` = slots se activan individualmente por viewport
Si `lazy_loading_enabled: false`, el sistema usa el comportamiento actual (cargar todos los ads de una vez despues de que la biblioteca cargue).
## Interaccion con Cache
**Importante:** El CSS dinamico generado por `CSSGeneratorService` incluye `display: none` para `.roi-ad-slot` cuando lazy loading esta habilitado.
Si se cambia `lazy_loading_enabled` de true a false:
1. El CSS dinamico cambiara en el siguiente render
2. **Se DEBE vaciar cache** (Redis, W3TC, OPcache) para que el cambio surta efecto
3. Usuarios con HTML cacheado veran slots ocultos hasta que su cache expire
**Recomendacion:** Agregar nota en FormBuilder indicando que cambios requieren vaciar cache.
## Parseo de Valores en PHP
Como los campos son tipo `select` con valores string, el `AdsenseAssetEnqueuer` debe parsear:
```php
wp_localize_script('adsense-loader', 'roiAdsenseConfig', [
'lazyEnabled' => (bool) $settings['lazy_loading_enabled'],
'rootMargin' => (int) $settings['lazy_rootmargin'] . 'px 0px',
'fillTimeout' => (int) $settings['lazy_fill_timeout'],
'debug' => WP_DEBUG,
]);
```