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

@@ -1182,6 +1182,55 @@ final class AdsensePlacementFormBuilder
$delayTimeout = $this->renderer->getFieldValue($cid, 'forms', 'delay_timeout', '5000');
$html .= $this->buildTextInput($cid . 'DelayTimeout', 'Timeout de delay (ms)', $delayTimeout);
// Lazy Loading settings
$html .= '<hr class="my-3">';
$html .= '<p class="small fw-semibold mb-2">';
$html .= ' <i class="bi bi-eye me-1" style="color: #198754;"></i>';
$html .= ' Lazy Loading (Intersection Observer)';
$html .= ' <span class="badge bg-success ms-1">Nuevo</span>';
$html .= '</p>';
$html .= '<div class="alert alert-info small py-2 mb-2">';
$html .= ' <i class="bi bi-lightbulb me-1"></i>';
$html .= ' Carga anuncios individualmente al entrar al viewport. Mejora fill rate y reduce CLS.';
$html .= '</div>';
$lazyEnabled = $this->renderer->getFieldValue($cid, 'behavior', 'lazy_loading_enabled', true);
$html .= $this->buildSwitch($cid . 'LazyLoadingEnabled', 'Activar Lazy Loading', $lazyEnabled, 'bi-eye');
$html .= '<div class="row g-2">';
$html .= ' <div class="col-md-6">';
$lazyRootmargin = $this->renderer->getFieldValue($cid, 'behavior', 'lazy_rootmargin', '200');
$html .= $this->buildSelect($cid . 'LazyRootmargin', 'Pre-carga (px)',
(string)$lazyRootmargin,
[
'0' => '0px (sin pre-carga)',
'100' => '100px',
'200' => '200px (recomendado)',
'300' => '300px',
'400' => '400px',
'500' => '500px'
]
);
$html .= ' </div>';
$html .= ' <div class="col-md-6">';
$lazyFillTimeout = $this->renderer->getFieldValue($cid, 'behavior', 'lazy_fill_timeout', '5000');
$html .= $this->buildSelect($cid . 'LazyFillTimeout', 'Timeout fill (ms)',
(string)$lazyFillTimeout,
[
'3000' => '3 segundos',
'5000' => '5 segundos (recomendado)',
'7000' => '7 segundos',
'10000' => '10 segundos'
]
);
$html .= ' </div>';
$html .= '</div>';
$html .= '<div class="alert alert-warning small py-2 mt-2 mb-0">';
$html .= ' <i class="bi bi-exclamation-triangle me-1"></i>';
$html .= ' <strong>Nota:</strong> Cambios requieren vaciar cache (Redis, W3TC) para aplicarse.';
$html .= '</div>';
$html .= ' </div>';
$html .= '</div>';