1import { Controller } from '@hotwired/stimulus'; 2 3/** 4 * CSP-safe replacement for inline `onerror=` on <img> (Stage-3 CSP dropped 'unsafe-inline' 5 * from script-src, so inline handlers no longer fire â broken-image icons). 6 * 7 * On a failed load, swaps in a neutral book placeholder (inline data-URI, no network) so the 8 * layout stays intact instead of showing the browser's broken-image glyph. Guards against a 9 * fallback loop. Handles the case where the image already errored before Stimulus connected. 10 */ 11const PLACEHOLDER = 12 "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='1.5'%3E%3Crect x='4' y='3' width='16' height='18' rx='1.5' fill='%23f1f5f9'/%3E%3Cpath d='M8 3v18' stroke='%23cbd5e1'/%3E%3C/svg%3E"; 13 14export default class extends Controller { 15 connect() { 16 this.onError = this.onError.bind(this); 17 this.element.addEventListener('error', this.onError); 18 // Image may have already failed before this controller connected. 19 if (this.element.complete && this.element.naturalWidth === 0 && this.element.src) { 20 this.onError(); 21 } 22 } 23 24 disconnect() { 25 this.element.removeEventListener('error', this.onError); 26 } 27 28 onError() { 29 if (this.element.dataset.fallbackApplied) return; 30 this.element.dataset.fallbackApplied = '1'; 31 this.element.src = PLACEHOLDER; 32 } 33}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.