// Delegated image-fallback handling for strict CSP. // // Cover images used to carry inline `onerror` attributes; under the // hash-allow-listed script-src (no 'unsafe-inline') the browser refuses to // execute them. This module restores the same behavior with one // capture-phase listener on , so htmx-swapped containers are covered // without any per-element re-binding. // // Opt-ins via data attributes on the element: // data-fallback="hide" → hide the element instead of swapping the src // (audiobook covers: placeholder would overflow) // default → swap a broken cover to the placeholder SVG const PLACEHOLDER = "/static/placeholder-book.svg"; let armed = false; function onResourceError(event: Event) { const target = event.target; // Only interested in resource elements (img/audio/video/source), not // generic events bubbling up (error does not bubble, but capture sees it). if (!(target instanceof HTMLImageElement)) return; if (target.dataset.fallback === "hide") { target.style.display = "none"; return; } // Never chase our own placeholder (if it 404s, stop). if (target.src.endsWith(PLACEHOLDER)) return; target.src = PLACEHOLDER; } export function armImageFallbacks() { if (armed) return; armed = true; // Capture is required: 'error' doesn't bubble. document.documentElement.addEventListener("error", onResourceError, true); }