(function () { const DOMPURIFY_CDN = 'https://cdn.jsdelivr.net/npm/dompurify@3.0.4/dist/purify.min.js'; let _domPurifyPromise = null; function ensureDOMPurifyLoaded() { if (typeof window === 'undefined') { return Promise.resolve(null); } if (window.DOMPurify && typeof window.DOMPurify.sanitize === 'function') { return Promise.resolve(window.DOMPurify); } if (_domPurifyPromise) return _domPurifyPromise; _domPurifyPromise = new Promise((resolve, reject) => { try { const script = document.createElement('script'); script.src = DOMPURIFY_CDN; script.async = true; script.onload = () => { if (window.DOMPurify && window.DOMPurify.sanitize) { resolve(window.DOMPurify); } else { reject(new Error('DOMPurify failed to load')); } }; script.onerror = () => reject(new Error('Failed to load DOMPurify')); document.head.appendChild(script); } catch (err) { reject(err); } }); return _domPurifyPromise; } // Basic fallback sanitizer when DOMPurify is not available yet. function fallbackSanitize(dirty) { const temp = document.createElement('div'); temp.innerHTML = dirty; // Remove script and style tags temp.querySelectorAll('script, style').forEach((el) => el.remove()); // Remove dangerous attributes temp.querySelectorAll('*').forEach((el) => { Array.from(el.attributes).forEach((attr) => { const name = attr.name; const value = attr.value; if (/^on/i.test(name)) { el.removeAttribute(name); return; } if ((name === 'href' || name === 'src') && value && value.trim().toLowerCase().startsWith('javascript:')) { el.removeAttribute(name); } }); }); return temp.innerHTML; } function sanitizeHTML(dirty) { if (typeof window !== 'undefined' && window.DOMPurify && window.DOMPurify.sanitize) { return window.DOMPurify.sanitize(dirty); } // Trigger async load so the next call benefits ensureDOMPurifyLoaded().catch(() => {}); return fallbackSanitize(dirty); } function escapeHtml(text) { const span = document.createElement('span'); span.textContent = String(text == null ? '' : text); return span.innerHTML; } function setSafeHTML(element, html) { if (!element) return; const sanitized = sanitizeHTML(String(html == null ? '' : html)); element.innerHTML = sanitized; } // Expose globally window.htmlSanitizer = { sanitize: sanitizeHTML, ensureDOMPurifyLoaded, escape: escapeHtml, setHTML: setSafeHTML }; window.setSafeHTML = setSafeHTML; })();