// What the richeditor's TipTap schema can show (UI-SPEC RichEditorField, // "Lossy note"). Stored HTML with any other element still opens: the field // keeps the stored string until the administrator edits (the untouched // rule), and the HTML view edits it without loss. This module has no TipTap // import, so the field can decide before the editor chunk loads. /** Elements the editor shows; compared by lower-cased tag name. */ export const EDITOR_TAGS: ReadonlySet = new Set([ 'p', 'br', 'strong', 'b', 'em', 'i', 'u', 's', 'h3', 'ul', 'ol', 'li', 'blockquote', 'a', 'div', 'span', ]) /** * Parses html into an inert template: nothing in it runs or loads (no * scripts, no image requests), so stored markup can be inspected safely. */ export function inertFragment(html: string): DocumentFragment { const template = document.createElement('template') template.innerHTML = html return template.content } /** The distinct lower-cased tag names of html's elements, in document order. */ export function tagNames(html: string): string[] { if (html === '') { return [] } const out = new Set() for (const element of Array.from(inertFragment(html).querySelectorAll('*'))) { out.add(element.localName.toLowerCase()) } return [...out] } /** Whether html holds an element the editor cannot show (the lossy note). */ export function isLossy(html: string): boolean { return tagNames(html).some((name) => !EDITOR_TAGS.has(name)) }