export function esc(s) { return (s == null ? '' : String(s)) .replace(/&/g, '&').replace(//g, '>') .replace(/"/g, '"').replace(/'/g, '''); } export function stripTags(html) { if (!html) return ''; var d = document.createElement('div'); d.innerHTML = html; return (d.textContent || d.innerText || '').replace(/\s+/g, ' ').trim(); } export function formatWhen(iso) { if (!iso) return ''; var d = new Date(iso); if (isNaN(d.getTime())) return ''; var now = new Date(); var sameDay = d.toDateString() === now.toDateString(); if (sameDay) return d.toLocaleTimeString(undefined, { hour: 'numeric', minute: '2-digit' }); return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: d.getFullYear() === now.getFullYear() ? undefined : 'numeric' }); } // Sanitize via DOMPurify (loaded from cdnjs in index.html, also used by // learningHub.js). If DOMPurify fails to load, render as plain text. export function sanitizeHtml(html) { if (!window.DOMPurify || typeof window.DOMPurify.sanitize !== 'function') { console.warn('[notes] DOMPurify unavailable - rendering as plain text.'); var d = document.createElement('div'); d.textContent = String(html == null ? '' : html); return d.innerHTML; } return window.DOMPurify.sanitize(html, { ALLOWED_TAGS: ['p','br','strong','em','b','i','u','s','h2','h3','h4', 'ul','ol','li','a','blockquote','code','pre','hr'], ALLOWED_ATTR: ['href','target','rel'], ADD_ATTR: ['target'], FORBID_ATTR: ['style','onerror','onload','onclick','onmouseover'], ALLOW_DATA_ATTR: false }); }