{"version":3,"sources":["mod::ww/js/feedback/widget/styles.ts","mod::ww/js/feedback/widget/selection-geometry.ts","mod::ww/js/feedback/widget/selection.ts","mod::ww/js/feedback/widget/widget.ts","@webhare/env/src/envbackend.ts","@webhare/env/src/init-browser.ts","@webhare/dompack/src/tree.ts","@webhare/std/src/collections.ts","@webhare/std/src/localmutex.ts","@webhare/frontend/src/init.ts","@webhare/tsrun/src/polyfills.ts","mod::publisher/js/internal/polyfills/iterator-helpers.ts","@webhare/env/src/whglobal.ts","mod::system/js/dompack/src/events.ts","@webhare/dompack/src/browser.ts","mod::publisher/js/internal/polyfills/publisher-integration.ts","/entrypoint.js"],"sourcesContent":["// Every rule is scoped under .wwfb-root and starts from `all: revert` so host page styling cannot leak in\nexport const css = `\n.wwfb-root, .wwfb-root * { all: revert; box-sizing: border-box; }\n.wwfb-root[hidden], .wwfb-root [hidden] { display: none !important; }\n/* Host-settable through data-color, data-color-text, data-radius and data-font on the script tag. The page font is not inherited:\n   many sites set it on their own containers, not on body, which leaves a browser-default serif */\n.wwfb-root { --wwfb-ink: #111827; --wwfb-muted: #6b7280; --wwfb-line: #d1d5db; --wwfb-bg: #fff; --wwfb-soft: #f3f4f6; --wwfb-error: #b91c1c;\n  --wwfb-accent: #111827; --wwfb-on-accent: #fff; --wwfb-radius: 12px; --wwfb-radius-sm: min(var(--wwfb-radius), 8px);\n  --wwfb-font: system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif;\n  font-family: var(--wwfb-font); font-size: 14px; line-height: 1.45; color: var(--wwfb-ink); color-scheme: light; }\n.wwfb-root button, .wwfb-root select { font-family: inherit; }\n.wwfb-root button { cursor: pointer; font: inherit; }\n.wwfb-root button:disabled { cursor: default; opacity: .6; }\n.wwfb-root :focus-visible { outline: 2px solid #2563eb; outline-offset: 2px; }\n.wwfb-launcher { position: fixed; right: 16px; bottom: 16px; z-index: 2147483000; display: inline-flex; align-items: center; gap: 8px;\n  padding: 10px 14px; border: 0; border-radius: max(var(--wwfb-radius-sm), 4px); background: var(--wwfb-accent); color: var(--wwfb-on-accent); font-weight: 600; box-shadow: 0 4px 14px rgba(0,0,0,.2); }\n.wwfb-count { min-width: 20px; padding: 0 6px; border-radius: 999px; background: var(--wwfb-on-accent); color: var(--wwfb-accent); font-size: 12px; text-align: center; }\n.wwfb-dialog { width: min(760px, calc(100vw - 32px)); max-height: calc(100dvh - 32px); padding: 0; border: 0; border-radius: var(--wwfb-radius);\n  background: var(--wwfb-bg); color: var(--wwfb-ink); box-shadow: 0 20px 50px rgba(0,0,0,.3); }\n.wwfb-dialog::backdrop { background: rgba(17,24,39,.4); }\n.wwfb-head { display: flex; align-items: center; justify-content: space-between; padding: 14px 18px; border-bottom: 1px solid var(--wwfb-line); }\n.wwfb-head h2 { margin: 0; font-size: 16px; }\n.wwfb-close { border: 0; background: none; font-size: 22px; line-height: 1; padding: 2px 6px; color: var(--wwfb-muted); }\n.wwfb-body { display: grid; gap: 14px; padding: 16px 18px 18px; }\n.wwfb-field { display: grid; gap: 6px; }\n.wwfb-label { font-weight: 600; }\n.wwfb-rte { display: flex; flex-direction: column; border: 1px solid var(--wwfb-line); border-radius: var(--wwfb-radius-sm); background: #fff; color: var(--wwfb-ink); }\n.wwfb-rte:focus-within { border-color: var(--wwfb-accent); box-shadow: 0 0 0 1px var(--wwfb-accent); }\n.wwfb-toolbar { display: flex; align-items: center; gap: 2px; padding: 4px; border-bottom: 1px solid var(--wwfb-line); }\n.wwfb-root .wwfb-tool { min-width: 28px; height: 28px; padding: 0 6px; border: 0; border-radius: 6px; background: none; color: var(--wwfb-muted); font-size: 13px; line-height: 1; }\n.wwfb-tool:hover { background: var(--wwfb-soft); color: var(--wwfb-ink); }\n.wwfb-tool[aria-pressed=\"true\"] { background: var(--wwfb-soft); color: var(--wwfb-ink); }\n.wwfb-root .wwfb-tool--bold { font-weight: 700; }\n.wwfb-root .wwfb-tool--italic { font-style: italic; font-family: Georgia, serif; }\n.wwfb-root .wwfb-tool--end { margin-left: auto; font-size: 16px; }\n.wwfb-rte-body { display: flex; flex-direction: column; flex: 1; min-height: 0; }\n/* The field border shows focus (.wwfb-rte:focus-within); a second ring on the editor itself is noise.\n   !important because hosts force rings that way too (Kiek: :focus-visible { outline: ... !important }) */\n.wwfb-root .wwfb-editor:focus-visible, .wwfb-root .wwfb-dialog:focus-visible { outline: none !important; }\n.wwfb-root .wwfb-editor { flex: 1; min-height: 140px; max-height: 40vh; overflow: auto; padding: 8px 10px; outline: none; white-space: pre-wrap; overflow-wrap: anywhere; }\n.wwfb-root .wwfb-editor[contenteditable=\"false\"] { background: var(--wwfb-soft); border-radius: var(--wwfb-radius-sm); }\n.wwfb-root .wwfb-editor > * + * { margin-top: .5em; }\n.wwfb-root .wwfb-editor p { margin: 0; }\n.wwfb-root .wwfb-editor ul, .wwfb-root .wwfb-editor ol { margin: 0; padding-left: 1.4em; }\n.wwfb-root .wwfb-editor code { padding: 0 3px; border-radius: 4px; background: var(--wwfb-soft); font-family: ui-monospace, Menlo, monospace; font-size: .92em; }\n.wwfb-root .wwfb-editor pre { padding: 8px; border-radius: 6px; background: var(--wwfb-soft); white-space: pre-wrap; }\n.wwfb-root .wwfb-editor blockquote { margin: 0; padding-left: 10px; border-left: 3px solid var(--wwfb-line); color: var(--wwfb-muted); }\n.wwfb-root .wwfb-editor a { color: #1d4ed8; }\n.wwfb-root .wwfb-editor img { display: block; max-width: 100%; max-height: 200px; margin: 6px 0; border-radius: 4px; }\n.wwfb-root .wwfb-editor img.ProseMirror-selectednode { outline: 2px solid var(--wwfb-accent); }\n/* Full screen: the field covers the dialog's viewport area; Esc or the toolbar button returns */\n.wwfb-rte--full { position: fixed; inset: 16px; z-index: 2; box-shadow: 0 20px 50px rgba(0,0,0,.35); }\n.wwfb-rte--full .wwfb-editor { flex: 1; max-height: none; padding: 16px 20px; font-size: 15px; }\n.wwfb-rte--full .wwfb-editor img { max-height: 60vh; }\n.wwfb-hint { margin: 0; color: var(--wwfb-muted); font-size: 12px; }\n.wwfb-select { padding: 6px 8px; border: 1px solid var(--wwfb-line); border-radius: var(--wwfb-radius-sm); background: #fff; color: var(--wwfb-ink); font: inherit; max-width: 220px; }\n.wwfb-shot-actions { display: flex; flex-wrap: wrap; gap: 10px 18px; }\n.wwfb-root .wwfb-selection { position: fixed; inset: 0; width: 100vw; height: 100dvh; max-width: none; max-height: none; margin: 0; padding: 16px;\n  border: 0; background: #111827; color: #fff; overflow: hidden; }\n.wwfb-selection[open] { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; }\n.wwfb-selection::backdrop { background: #111827; }\n.wwfb-selection-surface { position: relative; flex-shrink: 0; overflow: hidden; cursor: crosshair; touch-action: none; user-select: none; }\n.wwfb-selection-image { display: block; width: 100%; height: 100%; pointer-events: none; user-select: none; }\n.wwfb-selection-box { position: absolute; border: 2px solid var(--wwfb-accent); outline: 1px solid #fff; outline-offset: -1px; box-shadow: 0 0 0 9999px rgba(0,0,0,.45); pointer-events: none; }\n.wwfb-selection-toolbar { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; flex-shrink: 0; }\n.wwfb-selection-hint { margin: 0; max-width: 700px; text-align: center; font-size: 13px; flex-shrink: 0; }\n.wwfb-shot summary { cursor: pointer; font-weight: 600; }\n.wwfb-shot summary span { font-weight: 400; color: var(--wwfb-muted); margin-left: 6px; }\n.wwfb-shot[data-state=\"error\"] summary span { color: var(--wwfb-error); }\n.wwfb-shot img { display: block; width: 100%; margin: 8px 0; border: 1px solid var(--wwfb-line); border-radius: 6px; }\n.wwfb-link { justify-self: start; border: 0; background: none; padding: 0; color: #1d4ed8; text-decoration: underline; }\n.wwfb-error { margin: 0; color: var(--wwfb-error); }\n.wwfb-success { margin: 0; padding: 10px 12px; border-radius: 8px; background: #ecfdf5; color: #065f46; }\n.wwfb-actions { display: flex; justify-content: flex-end; gap: 8px; }\n.wwfb-primary, .wwfb-secondary { padding: 8px 14px; border-radius: var(--wwfb-radius-sm); font-weight: 600; }\n.wwfb-primary { border: 0; background: var(--wwfb-accent); color: var(--wwfb-on-accent); }\n.wwfb-secondary { border: 1px solid var(--wwfb-line); background: #fff; color: var(--wwfb-ink); }\n.wwfb-history { display: grid; gap: 8px; padding-top: 14px; border-top: 1px solid var(--wwfb-line); }\n.wwfb-history h3 { margin: 0; font-size: 14px; }\n.wwfb-history ul { display: grid; gap: 8px; margin: 0; padding: 0; list-style: none; }\n.wwfb-history li { display: grid; gap: 4px; padding: 8px 10px; border-radius: 8px; background: var(--wwfb-soft); }\n.wwfb-history p { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; }\n.wwfb-meta { display: flex; gap: 8px; color: var(--wwfb-muted); font-size: 12px; }\n.wwfb-status { padding: 0 6px; border-radius: 999px; background: #e5e7eb; color: var(--wwfb-ink); }\n.wwfb-status[data-type=\"completed\"] { background: #d1fae5; }\n.wwfb-status[data-type=\"canceled\"] { background: #fee2e2; }\n.wwfb-status[data-type=\"started\"] { background: #dbeafe; }\n`;\n","export type SelectionPoint = { x: number; y: number };\nexport type SelectionSize = { width: number; height: number };\nexport type SelectionRect = SelectionPoint & SelectionSize;\n\nexport const MIN_SELECTION_SIZE = 8;\n\nexport function selectionRect({ start, end, bounds }: { start: SelectionPoint; end: SelectionPoint; bounds: SelectionSize }): SelectionRect | null {\n  if (![start.x, start.y, end.x, end.y, bounds.width, bounds.height].every(Number.isFinite) || bounds.width <= 0 || bounds.height <= 0)\n    return null;\n  const x = Math.max(0, Math.min(bounds.width, Math.min(start.x, end.x)));\n  const y = Math.max(0, Math.min(bounds.height, Math.min(start.y, end.y)));\n  const right = Math.max(0, Math.min(bounds.width, Math.max(start.x, end.x)));\n  const bottom = Math.max(0, Math.min(bounds.height, Math.max(start.y, end.y)));\n  return right - x >= MIN_SELECTION_SIZE && bottom - y >= MIN_SELECTION_SIZE ? { x, y, width: right - x, height: bottom - y } : null;\n}\n\n/** Round outward so selecting between display pixels never clips a bitmap edge. */\nexport function bitmapRect({ rect, display, bitmap }: { rect: SelectionRect; display: SelectionSize; bitmap: SelectionSize }): SelectionRect | null {\n  if (![bitmap.width, bitmap.height].every(Number.isFinite) || bitmap.width <= 0 || bitmap.height <= 0)\n    return null;\n  const selection = selectionRect({ start: rect, end: { x: rect.x + rect.width, y: rect.y + rect.height }, bounds: display });\n  if (!selection)\n    return null;\n  const x = Math.floor(selection.x * bitmap.width / display.width);\n  const y = Math.floor(selection.y * bitmap.height / display.height);\n  const right = Math.min(bitmap.width, Math.ceil((selection.x + selection.width) * bitmap.width / display.width));\n  const bottom = Math.min(bitmap.height, Math.ceil((selection.y + selection.height) * bitmap.height / display.height));\n  return { x, y, width: right - x, height: bottom - y };\n}\n","import { isJpegDataUrl } from \"./capture\";\nimport { bitmapRect, MIN_SELECTION_SIZE, selectionRect, type SelectionPoint, type SelectionRect } from \"./selection-geometry\";\n\nlet nextSelectionId = 0;\n\n/** Select from the existing capture; aborting never replaces the caller's current screenshot. */\nexport async function selectScreenshot({ screenshot, root, signal }: { screenshot: string; root: HTMLElement; signal: AbortSignal }): Promise<string | null> {\n  if (signal.aborted)\n    return null;\n  if (!isJpegDataUrl(screenshot))\n    throw new Error(\"Invalid screenshot\");\n  return new Promise((resolve, reject) => {\n    const dialog = document.createElement(\"dialog\");\n    dialog.className = \"wwfb-selection\";\n    dialog.setAttribute(\"aria-label\", \"Gebied selecteren\");\n    const surface = document.createElement(\"div\");\n    surface.className = \"wwfb-selection-surface\";\n    surface.tabIndex = 0;\n    surface.inert = true;\n    surface.setAttribute(\"role\", \"group\");\n    surface.setAttribute(\"aria-label\", \"Selecteer een gebied op de screenshot\");\n    surface.style.touchAction = \"none\";\n    const image = document.createElement(\"img\");\n    image.className = \"wwfb-selection-image\";\n    image.alt = \"\";\n    image.draggable = false;\n    const box = document.createElement(\"div\");\n    box.className = \"wwfb-selection-box\";\n    box.setAttribute(\"aria-hidden\", \"true\");\n    box.hidden = true;\n    surface.append(image, box);\n    const hint = document.createElement(\"p\");\n    hint.className = \"wwfb-selection-hint\";\n    hint.id = `wwfb-selection-hint-${++nextSelectionId}`;\n    hint.textContent = \"Sleep om een gebied te kiezen. Pijltoetsen verplaatsen het kader, Shift + pijltoetsen wijzigen de grootte. Escape annuleert.\";\n    surface.setAttribute(\"aria-describedby\", hint.id);\n    const toolbar = document.createElement(\"div\");\n    toolbar.className = \"wwfb-selection-toolbar\";\n    const cancel = document.createElement(\"button\");\n    cancel.type = \"button\";\n    cancel.className = \"wwfb-secondary\";\n    cancel.textContent = \"Annuleren\";\n    const use = document.createElement(\"button\");\n    use.type = \"button\";\n    use.className = \"wwfb-primary\";\n    use.textContent = \"Gebied gebruiken\";\n    use.disabled = true;\n    toolbar.append(cancel, use);\n    dialog.append(hint, surface, toolbar);\n\n    const events = new AbortController();\n    let settled = false;\n    let selection: SelectionRect | null = null;\n    let start: SelectionPoint | null = null;\n    let pointerId: number | null = null;\n    const finish = (result: string | null, error?: Error) => {\n      if (settled)\n        return;\n      settled = true;\n      events.abort();\n      if (pointerId !== null && surface.hasPointerCapture(pointerId))\n        surface.releasePointerCapture(pointerId);\n      if (dialog.open)\n        dialog.close();\n      dialog.remove();\n      if (error)\n        reject(error);\n      else\n        resolve(result);\n    };\n    const listen = { signal: events.signal };\n    const render = () => {\n      box.hidden = !selection;\n      use.disabled = !selection;\n      if (selection)\n        Object.assign(box.style, { left: `${selection.x}px`, top: `${selection.y}px`, width: `${selection.width}px`, height: `${selection.height}px` });\n    };\n    const point = (event: PointerEvent): SelectionPoint => {\n      const bounds = image.getBoundingClientRect();\n      return { x: event.clientX - bounds.left, y: event.clientY - bounds.top };\n    };\n    const update = (event: PointerEvent) => {\n      if (event.pointerId !== pointerId || !start)\n        return;\n      selection = selectionRect({ start, end: point(event), bounds: image.getBoundingClientRect() });\n      render();\n    };\n    surface.addEventListener(\"pointerdown\", event => {\n      if (!event.isPrimary || event.button !== 0 || pointerId !== null)\n        return;\n      event.preventDefault();\n      surface.focus();\n      start = point(event);\n      pointerId = event.pointerId;\n      selection = null;\n      render();\n      surface.setPointerCapture(pointerId);\n    }, listen);\n    surface.addEventListener(\"pointermove\", update, listen);\n    surface.addEventListener(\"pointerup\", event => {\n      if (event.pointerId !== pointerId)\n        return;\n      update(event);\n      const released = event.pointerId;\n      pointerId = null;\n      start = null;\n      if (surface.hasPointerCapture(released))\n        surface.releasePointerCapture(released);\n    }, listen);\n    surface.addEventListener(\"pointercancel\", event => {\n      if (event.pointerId === pointerId)\n        finish(null);\n    }, listen);\n    surface.addEventListener(\"lostpointercapture\", event => {\n      if (event.pointerId === pointerId)\n        finish(null);\n    }, listen);\n    surface.addEventListener(\"keydown\", event => {\n      const directions: Record<string, [number, number]> = { ArrowLeft: [-1, 0], ArrowRight: [1, 0], ArrowUp: [0, -1], ArrowDown: [0, 1] };\n      const direction = directions[event.key];\n      if (!direction || event.altKey || event.ctrlKey || event.metaKey)\n        return;\n      event.preventDefault();\n      const bounds = image.getBoundingClientRect();\n      const rect = selection ?? { x: bounds.width / 4, y: bounds.height / 4, width: bounds.width / 2, height: bounds.height / 2 };\n      const [dx, dy] = direction.map(value => value * 10);\n      selection = event.shiftKey\n        ? { ...rect, width: Math.max(MIN_SELECTION_SIZE, Math.min(bounds.width - rect.x, rect.width + dx)), height: Math.max(MIN_SELECTION_SIZE, Math.min(bounds.height - rect.y, rect.height + dy)) }\n        : { ...rect, x: Math.max(0, Math.min(bounds.width - rect.width, rect.x + dx)), y: Math.max(0, Math.min(bounds.height - rect.height, rect.y + dy)) };\n      render();\n    }, listen);\n    cancel.addEventListener(\"click\", () => finish(null), listen);\n    use.addEventListener(\"click\", () => {\n      if (!selection)\n        return;\n      try {\n        const crop = bitmapRect({ rect: selection, display: image.getBoundingClientRect(), bitmap: { width: image.naturalWidth, height: image.naturalHeight } });\n        if (!crop)\n          return;\n        const canvas = document.createElement(\"canvas\");\n        canvas.width = crop.width;\n        canvas.height = crop.height;\n        const context = canvas.getContext(\"2d\");\n        if (!context)\n          throw new Error(\"Screenshot canvas unavailable\");\n        context.drawImage(image, crop.x, crop.y, crop.width, crop.height, 0, 0, crop.width, crop.height);\n        const result = canvas.toDataURL(\"image/jpeg\", 0.8);\n        if (!isJpegDataUrl(result))\n          throw new Error(\"Invalid selected screenshot\");\n        finish(result);\n      } catch {\n        finish(null, new Error(\"Het geselecteerde gebied kon niet worden vastgelegd. Probeer het opnieuw.\"));\n      }\n    }, listen);\n    dialog.addEventListener(\"cancel\", event => {\n      event.preventDefault();\n      finish(null);\n    }, listen);\n    dialog.addEventListener(\"close\", () => finish(null), listen);\n    signal.addEventListener(\"abort\", () => finish(null), { ...listen, once: true });\n    window.addEventListener(\"resize\", () => finish(null), listen);\n    image.addEventListener(\"error\", () => finish(null, new Error(\"De screenshot kon niet worden geopend. Maak opnieuw een screenshot.\")), listen);\n    image.addEventListener(\"load\", () => {\n      const style = getComputedStyle(dialog);\n      const paddingX = (parseFloat(style.paddingLeft) || 0) + (parseFloat(style.paddingRight) || 0);\n      const paddingY = (parseFloat(style.paddingTop) || 0) + (parseFloat(style.paddingBottom) || 0);\n      const width = dialog.clientWidth - paddingX;\n      const height = dialog.clientHeight - paddingY - hint.offsetHeight - toolbar.offsetHeight - 2 * (parseFloat(style.rowGap) || 0);\n      const scale = Math.min(1, width / image.naturalWidth, height / image.naturalHeight);\n      if (!Number.isFinite(scale) || scale <= 0) {\n        finish(null);\n        return;\n      }\n      // Surface and image share exact dimensions, so pointer coordinates never include letterboxing.\n      Object.assign(surface.style, { width: `${image.naturalWidth * scale}px`, height: `${image.naturalHeight * scale}px` });\n      surface.inert = false;\n      surface.focus();\n    }, { ...listen, once: true });\n    root.appendChild(dialog);\n    try {\n      dialog.showModal();\n      image.src = screenshot;\n    } catch {\n      finish(null, new Error(\"Gebied selecteren kon niet worden geopend. Probeer het opnieuw.\"));\n    }\n  });\n}\n","import { css } from \"./styles\";\nimport type { JSONContent } from \"@tiptap/core\";\nimport { captureScreenshot, isJpegDataUrl, MAX_IMAGES, type Viewport } from \"./capture\";\nimport type { EditorField } from \"./editor\";\nimport { selectScreenshot } from \"./selection\";\n\n/* Embeddable feedback widget, see js/feedback/router.ts for the API. Embed:\n     <script type=\"module\" src=\"https://<server>/.wh/ea/ap/ww.feedback/ap.mjs\" data-ww-feedback=\"<sitekey>\"\n             data-user-name=\"...\" data-user-email=\"...\" data-color=\"#0a7d4f\" data-color-text=\"#fff\" data-radius=\"4px\"\n             data-font='\"Golos Text\", sans-serif' data-history=\"off\"></script>\n   All data- attributes except data-ww-feedback are optional.\n   or set the reporter later with window.wwFeedback.setUser({ name, email }). */\n\ntype SiteConfig = { allowPriority: boolean; showHistory: boolean };\ntype HistoryItem = { identifier: string; description: string; expected: string; createdAt: string; status: string; statusType: string };\ntype Reporter = { name?: string; email?: string };\ntype Payload = {\n  siteKey: string; requestId: string; description: string; expected: string; images: string[]; screenshot: string;\n  priority?: number; reporter?: Reporter; pageUrl: string; userAgent: string; viewport: Viewport;\n};\ntype Session = {\n  key: string; requestId: string; descriptionDoc: JSONContent | null; expectedDoc: JSONContent | null; priority: number;\n  screenshot: string; fullScreenshot: string; captureError: string; context: { pageUrl: string; viewport: Viewport; userAgent: string };\n  payload: Payload | null; pending: boolean; error: string; result: { identifier: string } | null;\n};\n\n// Well below the server limit (js/feedback/router.ts) and typical proxy body limits\nconst MAX_PAYLOAD_CHARS = 6_000_000;\nconst PRIORITIES: Array<[number, string]> = [[0, \"Geen\"], [4, \"Laag\"], [3, \"Normaal\"], [2, \"Hoog\"], [1, \"Urgent\"]];\n\nfunction el<K extends keyof HTMLElementTagNameMap>(tag: K, props: Partial<HTMLElementTagNameMap[K]> = {}, ...children: Array<Node | string>): HTMLElementTagNameMap[K] {\n  const node = Object.assign(document.createElement(tag), props);\n  node.append(...children);\n  return node;\n}\n\nfunction nextPaint(): Promise<void> {\n  return new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(() => resolve())));\n}\n\n/** Drafts belong to the exact page; queries are left out because a SPA may rewrite them while you type */\nfunction currentPageKey(): string {\n  return location.origin + location.pathname + location.hash.split(\"?\")[0];\n}\n\nasync function start(script: HTMLScriptElement) {\n  const siteKey = script.dataset.wwFeedback ?? \"\";\n  const apiBase = new URL(\"/.ww-feedback/v1/\", script.src).href;\n  const api = (endpoint: string, params: Record<string, string> = {}) => `${apiBase}${endpoint}?${new URLSearchParams({ key: siteKey, ...params })}`;\n  let reporterOverride: Reporter | null = null;\n  let enabled = script.dataset.enabled !== \"off\";\n  let pageUrlOverride: string | null = null;\n  let enableGeneration = 0;\n\n  async function call<T>(response: Promise<Response>, fallback: string): Promise<T> {\n    let result: Response;\n    try {\n      result = await response;\n    } catch {\n      throw new Error(fallback);\n    }\n    const body = await result.json().catch(() => null) as (T & { error?: string }) | null;\n    if (!result.ok || !body)\n      throw new Error(body?.error || fallback);\n    return body;\n  }\n\n  let config: SiteConfig;\n  try {\n    config = await call<SiteConfig>(fetch(api(\"site\")), \"Feedback is niet beschikbaar.\");\n  } catch (e) {\n    console.warn(`[ww-feedback] ${(e as Error).message}`);\n    return;\n  }\n  // The page can only switch the history off: whether it may be shown at all is the site's setting on the server\n  if (script.dataset.history === \"off\")\n    config.showHistory = false;\n\n  const style = el(\"style\", { textContent: css });\n  const root = el(\"div\", { className: \"wwfb-root\" });\n  for (const [attribute, property, kind] of [[\"color\", \"--wwfb-accent\", \"color\"], [\"colorText\", \"--wwfb-on-accent\", \"color\"], [\"radius\", \"--wwfb-radius\", \"border-radius\"], [\"font\", \"--wwfb-font\", \"font-family\"]] as const) {\n    const value = script.dataset[attribute]?.trim();\n    if (value && CSS.supports(kind, value))\n      root.style.setProperty(property, value);\n  }\n  root.dataset.wwFeedbackRoot = \"\";\n  root.setAttribute(\"data-html2canvas-ignore\", \"\");\n\n  const launcherRoot = el(\"div\", { className: \"wwfb-root\", hidden: !enabled });\n  launcherRoot.style.cssText = root.style.cssText;\n  launcherRoot.dataset.wwFeedbackRoot = \"\";\n  launcherRoot.setAttribute(\"data-html2canvas-ignore\", \"\");\n  root.hidden = !enabled;\n\n  // Launcher\n  const count = el(\"span\", { className: \"wwfb-count\", hidden: true });\n  const launcher = el(\"button\", { type: \"button\", className: \"wwfb-launcher\" }, \"Feedback\", count);\n\n  // Dialog\n  const dialog = el(\"dialog\", { className: \"wwfb-dialog\" });\n  dialog.setAttribute(\"aria-labelledby\", \"wwfb-title\");\n  const closeButton = el(\"button\", { type: \"button\", className: \"wwfb-close\", title: \"Sluiten\" }, \"×\");\n  closeButton.setAttribute(\"aria-label\", \"Sluiten\");\n  const formError = el(\"p\", { className: \"wwfb-error\", hidden: true });\n  formError.setAttribute(\"role\", \"alert\");\n  // The editors (Tiptap) are a separate chunk, only loaded when someone opens the dialog\n  const descriptionSlot = el(\"div\");\n  const expectedSlot = el(\"div\");\n  let description: EditorField | null = null;\n  let expected: EditorField | null = null;\n  let editorsReady: Promise<void> | null = null;\n  const descriptionError = el(\"p\", { className: \"wwfb-error\", id: \"wwfb-description-error\", hidden: true }, \"Beschrijf wat er misgaat.\");\n  const showDescriptionError = (show: boolean) => {\n    descriptionError.hidden = !show;\n    description?.setInvalid(show);\n  };\n  function ensureEditors(): Promise<void> {\n    editorsReady ??= import(\"./editor\").then(({ createEditorField }) => {\n      const totalImages = () => (description?.imageCount() ?? 0) + (expected?.imageCount() ?? 0);\n      const onImageError = (message: string) => { formError.textContent = message; formError.hidden = false; };\n      description = createEditorField({ labelId: \"wwfb-description-label\", describedBy: \"wwfb-description-error\", onImageError, totalImages });\n      expected = createEditorField({ labelId: \"wwfb-expected-label\", onImageError, totalImages });\n      description.onInput(() => showDescriptionError(false));\n      descriptionSlot.replaceWith(description.element);\n      expectedSlot.replaceWith(expected.element);\n    }).catch(error => {\n      editorsReady = null; // a later open may succeed, for example after a network hiccup\n      throw error;\n    });\n    return editorsReady;\n  }\n  const priority = el(\"select\", { className: \"wwfb-select\", id: \"wwfb-priority\" }, ...PRIORITIES.map(([value, label]) => el(\"option\", { value: String(value) }, label)));\n  const priorityField = el(\"div\", { className: \"wwfb-field\", hidden: !config.allowPriority },\n    el(\"label\", { className: \"wwfb-label\", htmlFor: \"wwfb-priority\" }, \"Prioriteit\"), priority);\n  const shotState = el(\"span\");\n  const shotImage = el(\"img\", { alt: \"Screenshot van de pagina\" });\n  const selectArea = el(\"button\", { type: \"button\", className: \"wwfb-link\" }, \"Gebied selecteren\");\n  const recapture = el(\"button\", { type: \"button\", className: \"wwfb-link\" }, \"Opnieuw vastleggen\");\n  const shot = el(\"details\", { className: \"wwfb-shot\" }, el(\"summary\", {}, \"Screenshot\", shotState), shotImage);\n  const shotField = el(\"div\", { className: \"wwfb-field\" }, shot, el(\"div\", { className: \"wwfb-shot-actions\" }, selectArea, recapture));\n  const success = el(\"p\", { className: \"wwfb-success\", hidden: true });\n  success.setAttribute(\"role\", \"status\");\n  const submitButton = el(\"button\", { type: \"submit\", className: \"wwfb-primary\" }, \"Versturen\");\n  const cancelButton = el(\"button\", { type: \"button\", className: \"wwfb-secondary\" }, \"Annuleren\");\n  const fields = el(\"div\", { className: \"wwfb-body\" },\n    el(\"div\", { className: \"wwfb-field\" }, el(\"span\", { className: \"wwfb-label\", id: \"wwfb-description-label\" }, \"Wat gaat er mis?\"), descriptionSlot, descriptionError,\n      el(\"p\", { className: \"wwfb-hint\" }, `Plak een afbeelding met ${/Mac|iPhone|iPad/.test(navigator.userAgent) ? \"⌘V\" : \"Ctrl+V\"}.`)),\n    el(\"div\", { className: \"wwfb-field\" }, el(\"span\", { className: \"wwfb-label\", id: \"wwfb-expected-label\" }, \"Wat had het moeten zijn?\"), expectedSlot),\n    priorityField, shotField, formError, success,\n    el(\"div\", { className: \"wwfb-actions\" }, cancelButton, submitButton));\n  const form = el(\"form\", { method: \"dialog\", noValidate: true }, fields);\n\n  const historyList = el(\"ul\");\n  const historyStatus = el(\"p\", { className: \"wwfb-hint\" });\n  const historyMore = el(\"button\", { type: \"button\", className: \"wwfb-link\", hidden: true }, \"Meer laden\");\n  const historyRetry = el(\"button\", { type: \"button\", className: \"wwfb-link\", hidden: true }, \"Opnieuw proberen\");\n  const historySection = el(\"section\", { className: \"wwfb-history\", hidden: !config.showHistory },\n    el(\"h3\", {}, \"Eerdere feedback op deze pagina\"), historyStatus, historyList, historyMore, historyRetry);\n\n  dialog.append(el(\"div\", { className: \"wwfb-head\" }, el(\"h2\", { id: \"wwfb-title\" }, \"Feedback\"), closeButton),\n    form, el(\"div\", { className: \"wwfb-body\", hidden: !config.showHistory }, historySection));\n  launcherRoot.append(launcher);\n  root.append(dialog);\n  document.head.append(style);\n  document.body.append(launcherRoot, root);\n\n  function moveLauncher() {\n    const appDialog = document.activeElement?.closest(\"dialog:modal, [role=dialog][aria-modal=true]\");\n    if (appDialog?.closest(\"[data-ww-feedback-root]\"))\n      return;\n    // The launcher belongs inside the app modal; the feedback dialog stays outside its form and key handlers.\n    const parent = appDialog ?? document.body;\n    if (launcherRoot.parentElement !== parent)\n      parent.append(launcherRoot);\n  }\n  document.addEventListener(\"focusin\", moveLauncher, true);\n\n  // State\n  const sessions = new Map<string, Session>();\n  let session: Session | null = null;\n  // The session the fields currently show; a new session must not save the previous one's text as its own\n  let shown: Session | null = null;\n  let capturing = false;\n  let selectionAbort: AbortController | null = null;\n\n  function saveEditors() {\n    if (session && session === shown && !session.payload && description && expected) {\n      session.descriptionDoc = description.getDoc();\n      session.expectedDoc = expected.getDoc();\n      session.priority = Number(priority.value);\n    }\n  }\n\n  function render() {\n    if (!session)\n      return;\n    shown = session;\n    const locked = Boolean(session.payload) || session.pending;\n    for (const [editor, doc] of [[description, session.descriptionDoc], [expected, session.expectedDoc]] as const) {\n      editor?.setDoc(doc);\n      editor?.setEditable(!locked && !session.result);\n    }\n    priority.value = String(session.priority);\n    priority.disabled = locked || Boolean(session.result);\n\n    shot.dataset.state = session.screenshot ? \"ok\" : session.captureError ? \"error\" : \"busy\";\n    shotState.textContent = session.screenshot ? session.screenshot === session.fullScreenshot ? \"vastgelegd\" : \"gebied geselecteerd\" : session.captureError ? \"mislukt\" : \"bezig...\";\n    shotImage.hidden = !session.screenshot;\n    shotImage.src = session.screenshot;\n    recapture.hidden = locked || Boolean(session.result);\n    selectArea.hidden = recapture.hidden || !session.fullScreenshot;\n\n    const error = session.error || session.captureError || (description ? \"\" : \"De invoervelden konden niet worden geladen. Vernieuw de pagina en probeer het opnieuw.\");\n    formError.hidden = !error;\n    formError.textContent = error;\n    success.hidden = !session.result;\n    success.textContent = session.result ? \"Bedankt, je feedback is verstuurd.\" : \"\";\n    submitButton.hidden = Boolean(session.result);\n    submitButton.disabled = session.pending || !session.screenshot || !description;\n    submitButton.textContent = session.pending ? \"Versturen...\" : session.payload ? \"Opnieuw versturen\" : \"Versturen\";\n    cancelButton.textContent = session.result ? \"Sluiten\" : \"Annuleren\";\n  }\n\n  async function capture() {\n    const captured = session;\n    if (!enabled || !captured || capturing || captured.pending || captured.payload)\n      return;\n    saveEditors();\n    capturing = true;\n    const generation = enableGeneration;\n    launcher.disabled = true;\n    dialog.close();\n    captured.screenshot = \"\";\n    captured.fullScreenshot = \"\";\n    captured.captureError = \"\";\n    try {\n      await nextPaint();\n      if (!enabled || generation !== enableGeneration || currentPageKey() !== captured.key)\n        return;\n      captured.context = { pageUrl: pageUrlOverride ?? location.href, viewport: { width: window.innerWidth, height: window.innerHeight }, userAgent: navigator.userAgent };\n      captured.screenshot = await captureScreenshot(captured.context.viewport);\n      captured.fullScreenshot = captured.screenshot;\n    } catch {\n      // Renderer errors contain browser internals rather than actionable copy\n      captured.captureError = \"Het screenshot kon niet worden gemaakt. Probeer het opnieuw.\";\n    } finally {\n      await ensureEditors().catch(error => console.warn(\"[ww-feedback] Editor could not be loaded\", error));\n      capturing = false;\n      launcher.disabled = false;\n      // Never open a draft over a page the user navigated to in the meantime\n      if (enabled && generation === enableGeneration && session === captured && currentPageKey() === captured.key) {\n        render();\n        dialog.showModal();\n        description?.focus();\n      }\n    }\n  }\n\n  async function selectRegion() {\n    const captured = session;\n    if (!enabled || !captured?.fullScreenshot || capturing || captured.pending || captured.payload || captured.result)\n      return;\n    saveEditors();\n    capturing = true;\n    const generation = enableGeneration;\n    const controller = new AbortController();\n    selectionAbort = controller;\n    launcher.disabled = true;\n    dialog.close();\n    captured.error = \"\";\n    try {\n      const screenshot = await selectScreenshot({ screenshot: captured.fullScreenshot, root, signal: controller.signal });\n      if (screenshot && !controller.signal.aborted && enabled && generation === enableGeneration && session === captured && currentPageKey() === captured.key)\n        captured.screenshot = screenshot;\n    } catch {\n      captured.error = \"Het gebied kon niet worden vastgelegd. Probeer het opnieuw.\";\n    } finally {\n      selectionAbort = null;\n      capturing = false;\n      launcher.disabled = false;\n      if (enabled && generation === enableGeneration && session === captured && currentPageKey() === captured.key) {\n        render();\n        shot.open = true;\n        dialog.showModal();\n        selectArea.focus();\n      }\n    }\n  }\n\n  async function open() {\n    if (!enabled || capturing)\n      return;\n    showDescriptionError(false);\n    void ensureEditors().catch(() => {}); // loads while the screenshot is taken; capture() waits for it\n    const key = currentPageKey();\n    const existing = sessions.get(key);\n    if (existing && !existing.result) {\n      session = existing;\n      render();\n      dialog.showModal();\n      return;\n    }\n    session = {\n      key, requestId: crypto.randomUUID(), descriptionDoc: null, expectedDoc: null, priority: 0, screenshot: \"\", fullScreenshot: \"\", captureError: \"\",\n      context: { pageUrl: \"\", viewport: { width: 0, height: 0 }, userAgent: \"\" }, payload: null, pending: false, error: \"\", result: null,\n    };\n    sessions.set(key, session);\n    await capture();\n  }\n\n  function reporter(): Reporter | undefined {\n    const found = reporterOverride ?? { name: script.dataset.userName, email: script.dataset.userEmail };\n    const clean = Object.fromEntries(Object.entries(found).filter(([, value]) => typeof value === \"string\" && value.trim()).map(([k, v]) => [k, v!.trim().slice(0, 200)]));\n    return Object.keys(clean).length ? clean : undefined;\n  }\n\n  async function submit() {\n    const submitted = session;\n    if (!enabled || !submitted || submitted.pending || submitted.result || !description || !expected)\n      return;\n    if (!submitted.payload) {\n      saveEditors();\n      const images: string[] = [];\n      const descriptionText = description.toMarkdown(images);\n      const expectedText = expected.toMarkdown(images);\n      if (!descriptionText) {\n        showDescriptionError(true);\n        description.focus();\n        return;\n      }\n      if (!isJpegDataUrl(submitted.screenshot) || images.length > MAX_IMAGES) {\n        submitted.error = \"Maak opnieuw een screenshot voordat je de feedback verstuurt.\";\n        render();\n        return;\n      }\n      const payload: Payload = {\n        siteKey, requestId: submitted.requestId, description: descriptionText, expected: expectedText, images, screenshot: submitted.screenshot,\n        ...(config.allowPriority ? { priority: submitted.priority } : {}), reporter: reporter(), ...submitted.context,\n      };\n      if (JSON.stringify(payload).length > MAX_PAYLOAD_CHARS) {\n        submitted.error = \"De melding is te groot. Gebruik minder of kleinere afbeeldingen.\";\n        render();\n        return;\n      }\n      // Fixed after the first attempt: the response can be lost after Linear created the issue\n      submitted.payload = payload;\n    }\n    submitted.pending = true;\n    submitted.error = \"\";\n    render();\n    try {\n      submitted.result = await call<{ identifier: string }>(fetch(api(\"submit\"), {\n        method: \"POST\", headers: { \"content-type\": \"application/json\" }, body: JSON.stringify(submitted.payload),\n      }), \"Verzenden kon niet worden bevestigd. Probeer het opnieuw; dezelfde melding wordt maar één keer aangemaakt.\");\n      if (session === submitted && currentPageKey() === submitted.key)\n        void loadHistory({ reset: true });\n    } catch (e) {\n      submitted.error = (e as Error).message;\n    } finally {\n      submitted.pending = false;\n      if (session === submitted)\n        render();\n    }\n  }\n\n  // History: only the latest request generation may update the list\n  let historyGeneration = 0;\n  let historyItems: HistoryItem[] = [];\n  let historyCursor: string | null = null;\n\n  function renderHistoryItem(item: HistoryItem): HTMLLIElement {\n    const status = el(\"span\", { className: \"wwfb-status\" }, item.status);\n    status.dataset.type = item.statusType;\n    return el(\"li\", {},\n      el(\"p\", {}, item.description),\n      ...(item.expected ? [el(\"p\", { className: \"wwfb-hint\" }, `Verwacht: ${item.expected}`)] : []),\n      el(\"div\", { className: \"wwfb-meta\" }, el(\"span\", {}, new Date(item.createdAt).toLocaleDateString(\"nl-NL\", { day: \"numeric\", month: \"short\", year: \"numeric\" })), status));\n  }\n\n  async function loadHistory({ reset }: { reset: boolean }) {\n    if (!enabled || !config.showHistory)\n      return;\n    const generation = ++historyGeneration;\n    historyRetry.hidden = true;\n    historyMore.disabled = true;\n    if (reset) {\n      historyStatus.textContent = \"Laden...\";\n      historyStatus.hidden = false;\n    }\n    try {\n      const result = await call<{ items: HistoryItem[]; nextCursor: string | null }>(\n        fetch(api(\"history\", { page: pageUrlOverride ?? location.href, ...(!reset && historyCursor ? { after: historyCursor } : {}) })),\n        \"Eerdere feedback kon niet worden geladen.\");\n      if (generation !== historyGeneration)\n        return;\n      historyItems = reset ? result.items : [...historyItems, ...result.items];\n      historyCursor = result.nextCursor;\n      historyList.replaceChildren(...historyItems.map(renderHistoryItem));\n      historyStatus.hidden = historyItems.length > 0;\n      historyStatus.textContent = \"Nog geen feedback op deze pagina.\";\n      historyMore.hidden = !historyCursor;\n      count.hidden = historyItems.length === 0;\n      count.textContent = `${historyItems.length}${historyCursor ? \"+\" : \"\"}`;\n    } catch (e) {\n      if (generation !== historyGeneration)\n        return;\n      // A failed load is not \"no feedback\": keep what is shown and say so\n      historyStatus.textContent = (e as Error).message;\n      historyStatus.hidden = false;\n      historyRetry.hidden = false;\n      if (reset) {\n        historyItems = [];\n        historyList.replaceChildren();\n        count.hidden = true;\n      }\n    } finally {\n      if (generation === historyGeneration)\n        historyMore.disabled = false;\n    }\n  }\n\n  // Events\n  launcher.addEventListener(\"click\", () => void open());\n  closeButton.addEventListener(\"click\", () => { if (!session?.pending) dialog.close(); });\n  cancelButton.addEventListener(\"click\", () => { if (!session?.pending) dialog.close(); });\n  dialog.addEventListener(\"cancel\", event => {\n    // Esc first leaves a full-screen field, then closes the dialog\n    if (session?.pending || description?.exitFullscreen() || expected?.exitFullscreen())\n      event.preventDefault();\n  });\n  dialog.addEventListener(\"close\", saveEditors);\n  form.addEventListener(\"submit\", event => { event.preventDefault(); void submit(); });\n  recapture.addEventListener(\"click\", () => void capture());\n  selectArea.addEventListener(\"click\", () => void selectRegion());\n  historyMore.addEventListener(\"click\", () => void loadHistory({ reset: false }));\n  historyRetry.addEventListener(\"click\", () => void loadHistory({ reset: historyItems.length === 0 }));\n\n  // SPA navigation: there is no portable navigation event, so compare the page on a short interval\n  let lastPage = currentPageKey();\n  setInterval(() => {\n    const page = currentPageKey();\n    if (page === lastPage)\n      return;\n    lastPage = page;\n    selectionAbort?.abort();\n    if (dialog.open && !session?.pending)\n      dialog.close();\n    session = sessions.get(page) ?? null;\n    void loadHistory({ reset: true });\n  }, 500);\n\n  void loadHistory({ reset: true });\n\n  (window as Window & { wwFeedback?: unknown }).wwFeedback = {\n    setUser(user: Reporter | null) {\n      reporterOverride = user;\n    },\n    setPageUrl(url: string | null) {\n      pageUrlOverride = url;\n    },\n    clearDrafts() {\n      // Scope changes must discard even a pending draft; its late response stays attached to the old session.\n      ++enableGeneration;\n      ++historyGeneration;\n      selectionAbort?.abort();\n      sessions.clear();\n      session = null;\n      shown = null;\n      dialog.close();\n      description?.exitFullscreen();\n      expected?.exitFullscreen();\n      description?.setDoc(null);\n      expected?.setDoc(null);\n      shotImage.removeAttribute(\"src\");\n    },\n    setEnabled(value: boolean) {\n      if (enabled === value)\n        return;\n      enabled = value;\n      ++enableGeneration;\n      ++historyGeneration;\n      root.hidden = !enabled;\n      launcherRoot.hidden = !enabled;\n      if (!enabled) {\n        selectionAbort?.abort();\n        saveEditors();\n        dialog.close();\n      } else {\n        moveLauncher();\n        void loadHistory({ reset: true });\n      }\n    },\n  };\n  window.dispatchEvent(new Event(\"ww-feedback-ready\"));\n}\n\nconst script = document.querySelector<HTMLScriptElement>(\"script[data-ww-feedback]\");\nif (script && !document.querySelector(\"[data-ww-feedback-root]\")) {\n  if (document.body)\n    void start(script);\n  else\n    document.addEventListener(\"DOMContentLoaded\", () => void start(script), { once: true });\n}\n","//We implement the backend version of getWHDebugFlags so bridge can access us without going through a recursive dep\n\nimport type { DTAPStage } from \"./concepts\";\n\n/// List global polyfills currently active. This may be needed to align TypeScript hosts configuration\nexport const globalPolyfills: string[] = [];\n\n/// An object with string keys and typed values\nexport interface WellKnownFlags {\n  /** Log RPcs */\n  rpc?: boolean;\n  /** Log web traffic */\n  wrq?: boolean;\n  /** Autoprofile */\n  apr?: boolean;\n  /** IPC */\n  ipc?: boolean;\n  /** async */\n  async?: boolean;\n}\nexport type DebugFlags = WellKnownFlags & { [key: string]: boolean | undefined };\n\nexport type DebugConfig = {\n  tags: readonly string[];\n  outputsession: string;\n  context: string;\n};\n\n/// Empty or no parameter lang is get, otherwise it's a set\nexport type GetSetAssetBase = (assetBase?: string | null) => string | null;\n\nexport type EnvHooks = {\n  currentAssetBase: GetSetAssetBase;\n};\n\n\nconst settingschangedcallbacks = new Array<() => void>;\n\n/* Global debug flags are set by `wh debug`, local flags are prefilled by environment variables.\n   Edits to the debug flags will be applied to the local flags (unless setDebugFlagsOverrideCB returns\n   override records).\n*/\nconst globalDebugFlags: DebugFlags = {}, localDebugFlags: DebugFlags = {};\n\n/** Returns the flag override records. The first record is examined first, then the second, etc. */\nlet debugFlagsOverridesCB: undefined | (() => DebugFlags[]);\n\n/** Override the debug flag override getter. */\nexport function setDebugFlagsOverrideCB(cb: undefined | (() => DebugFlags[])) {\n  debugFlagsOverridesCB = cb;\n}\n\n/* Proxy handler for the published debug flags. Uses the override records (with the first record\n   examined first, then the second, etc.) to override the flags from baseDebugFlags. Setting to `true` sets\n   the flag, `false` disables it and setting to `undefined` has the same effect as deleting the property in the\n   current top record (exposing any value from a lower record, like the debug settings from `wh debug`).\n*/\nclass DebugFlagsProxyHandler implements ProxyHandler<DebugFlags> {\n  private getRecordList(): DebugFlags[] {\n    return [...debugFlagsOverridesCB?.() ?? [], localDebugFlags, globalDebugFlags];\n  }\n\n  get(target: DebugFlags, p: string) {\n    for (const record of this.getRecordList())\n      if (p in record && typeof record[p] !== \"undefined\")\n        return record[p];\n    return undefined;\n  }\n  has(target: DebugFlags, p: string): boolean {\n    return this.get(target, p) !== undefined;\n  }\n  ownKeys(target: DebugFlags): Array<string | symbol> {\n    const keys = new Array<string | symbol>;\n    for (const record of this.getRecordList())\n      for (const key of Reflect.ownKeys(record))\n        if (typeof key === \"string\" && record[key] !== undefined && !keys.includes(key))\n          keys.push(key);\n    return keys;\n  }\n  set(target: DebugFlags, p: string, newValue: boolean | undefined): boolean {\n    const toModify = this.getRecordList()[0];\n    if (typeof newValue === \"boolean\")\n      toModify[p] = newValue;\n    else\n      delete toModify[p];\n\n    runSettingsCallbacks();\n    return true;\n  }\n  deleteProperty(target: DebugFlags, p: string): boolean {\n    const toModify = this.getRecordList()[0];\n    delete toModify[p];\n\n    runSettingsCallbacks();\n    return true;\n  }\n  getOwnPropertyDescriptor(target: DebugFlags, p: string): PropertyDescriptor | undefined {\n    const value = this.get(target, p);\n    return value !== undefined ? { enumerable: true, value, configurable: true } : undefined;\n  }\n}\n\nexport const debugFlags = new Proxy<DebugFlags>({\n  [Symbol.for('nodejs.util.inspect.custom')]: formatForConsoleLogs\n}, new DebugFlagsProxyHandler());\n\nfunction formatForConsoleLogs() {\n  return `DebugFlags [${[...Object.keys(debugFlags)].filter(key => debugFlags[key]).join(\", \")}]`;\n}\n\nfunction runSettingsCallbacks() {\n  for (const cb of [...settingschangedcallbacks]) {\n    try {\n      cb();\n    } catch (e) {\n      console.error(\"Error invoking settings change callback\", cb?.name, e);\n      //ignore, debugFlags can change due to external updates so they're basically signal handlers and there's no real point in crashing whatever was really running\n    }\n  }\n}\n\n/** Update the debugconfig as present in the system configuration record\n    @param settings - debugconfig cell of the system configuration record\n*/\nexport function updateDebugConfig(settings: DebugConfig | null) {\n  const oldenabledflags = Object.keys(globalDebugFlags).sort();\n  const newenabledflags = settings?.tags ? [...settings.tags].sort() : [];\n\n  if (oldenabledflags.join(\",\") !== newenabledflags.join(\",\")) {\n    for (const flag of newenabledflags)\n      globalDebugFlags[flag] = true;\n    for (const flag of oldenabledflags)\n      if (!newenabledflags.includes(flag))\n        delete globalDebugFlags[flag];\n\n    runSettingsCallbacks();\n  }\n  if (debugFlags.async && Error.stackTraceLimit < 100)\n    Error.stackTraceLimit = 100;\n}\n\nexport function registerDebugConfigChangedCallback(cb: () => void) {\n  settingschangedcallbacks.push(cb);\n}\n\nexport function updateLocalDebugFlags(mode: \"enable\" | \"disable\" | \"clear\", flags: string[]): DebugFlags {\n  switch (mode) {\n    case \"enable\":\n    case \"disable\":\n      for (const flag of flags)\n        localDebugFlags[flag] = mode === \"enable\";\n      break;\n    case \"clear\":\n      for (const flag of Object.keys(localDebugFlags))\n        delete localDebugFlags[flag];\n  }\n  runSettingsCallbacks();\n  return { ...globalDebugFlags, ...localDebugFlags };\n}\n\n/** DTAP stage set for this WebHare */\nlet dtapStage: DTAPStage = \"production\";\n\n/** @deprecated We now recommend checking dtapStage against production/acceptance Explicitly */\nlet isLive: boolean = dtapStage === \"production\" || dtapStage === \"acceptance\";\n\n/** The backend base URL. Used for eg. autoconfiguring JSON/RPC */\nlet backendBase = \"\";\n\n/** The global assetbase */\nlet assetBase: string | null = null;\n\n//deprecated variants\n/** @deprecated For WH5.4 and up use 'dtapStage' */\nlet dtapstage: DTAPStage = dtapStage;\n/** @deprecated For WH5.4 and up compare 'dtapStage' for production or acceptance */\nlet islive: boolean = isLive;\n\nlet hooks: EnvHooks | undefined;\nlet hookFactory: (() => EnvHooks) | undefined;\nlet gotHooks = false;\n\nexport function initEnv(setDtapStage: DTAPStage, setBackendBase: string) {\n  dtapStage = setDtapStage;\n  isLive = dtapStage === \"production\" || dtapStage === \"acceptance\";\n  backendBase = setBackendBase;\n\n  dtapstage = dtapStage;\n  islive = isLive;\n}\n\nexport function setEnvHooksFactory(newHookFactory: () => EnvHooks) {\n  if (gotHooks)\n    throw new Error(`The call to setEnvHooksFactory was too late - hooks have already been retrieved once`);\n  hookFactory = newHookFactory;\n}\n\nexport function getEnvHooks() {\n  gotHooks = true;\n  return hookFactory?.();\n}\n\nfunction ensureHooks(): EnvHooks {\n  return hooks ??= getEnvHooks?.() ?? {\n    currentAssetBase: (newAssetBase) => {\n      if (newAssetBase !== undefined)\n        assetBase = newAssetBase;\n      return assetBase;\n    }\n  };\n}\n\n/** Get the base url for assets. This may point towards eg. a CDN\n * @returns The base url for assets, ending with a '/'. null if not set\n */\nexport function getAssetBase(): string | null {\n  return ensureHooks().currentAssetBase();\n}\n\n/** Get the base url for a module's public assets (those located in the module's `public` folder)\n */\nexport function getModulePublicAssetBase(module: string) {\n  return `${getAssetBase() || \"/\"}.wh/mod/${encodeURIComponent(module)}/public/`;\n}\n\n/** Set the base url for assets */\nexport function setAssetBase(base: string | null): void {\n  if (base && !base.endsWith(\"/\"))\n    throw new Error(`Asset base must end with a '/'`);\n\n  ensureHooks().currentAssetBase(base);\n}\n\nexport { dtapStage, isLive, backendBase, localDebugFlags };\nexport { dtapstage, islive }; //deprecated variants\n","\n//Note that @webhare/env does not invoke this API itself. In WebHare @webhare/frontend/src/init uses this to initialize\nexport function getBrowserDebugFlags(varname: string): string[] {\n  const flags = [];\n  const urldebugvar = new URL(location.href).searchParams.get(varname);\n  if (urldebugvar)\n    flags.push(...urldebugvar.split(','));\n\n  //not importing getCookie to solve some import ordering issues\n  if (typeof document !== \"undefined\") { //'document' is undefined in a worker but assetpacks compiled for a ServiceWorker will also load us\n    const debugcookie = document.cookie.match(`(?:^|;)\\\\s*${varname}=([^;]*)`); //TODO escape varname\n    if (debugcookie) {\n      const debugcookievalue = decodeURIComponent(debugcookie[1]);\n      if (debugcookievalue)\n        flags.push(... (debugcookievalue.split('.').filter(flag => !flag.startsWith('sig='))));\n    }\n  }\n\n  if (typeof sessionStorage !== \"undefined\") {\n    const sessionDebug = sessionStorage.getItem(varname);\n    if (sessionDebug)\n      flags.push(...sessionDebug.split(','));\n  }\n\n  return flags;\n}\n","/** Rect describing an elements position */\nexport type Rect = {\n  top: number;\n  bottom: number;\n  left: number;\n  right: number;\n  width: number;\n  height: number;\n};\n\n/** Elements you can set a value on and would have to trigger change and/or input events */\nexport type FormControlElement = HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;\n\n/** Types that support querySelector(All) */\ntype QuerySelectorAble = ParentNode | ShadowRoot;\n\nexport function qS<E extends Element = HTMLElement>(startnode: QuerySelectorAble, selector: string): E | null;\nexport function qS<E extends Element = HTMLElement>(selector: string): E | null;\n\n/** Match the first element using a CSS selector\n * @param node_or_selector - The starting node or selector\n * @param selector - The selector to match if the starting node was specified\n * @returns The first matching element or null\n */\nexport function qS<E extends Element>(node_or_selector: QuerySelectorAble | string, selector?: string): E | null {\n  if (typeof node_or_selector === 'string')\n    return document.querySelector<E>(node_or_selector);\n  else if (selector)\n    return node_or_selector.querySelector<E>(selector);\n  return null;\n}\n\n\nexport function qR<E extends Element = HTMLElement>(startnode: QuerySelectorAble, selector: string): E;\nexport function qR<E extends Element = HTMLElement>(selector: string): E;\n\n/** Match a specific element using a CSS selector, requiring it to exist and be unique\n * @param node_or_selector - The starting node or selector\n * @param selector - The selector to match if the starting node was specified\n * @returns The requested element. Throw it the selector doesn't match exactly one element\n */\nexport function qR<E extends Element>(node_or_selector: QuerySelectorAble | string, selector?: string): E {\n  const matches = qSA<E>(node_or_selector as QuerySelectorAble, selector as string);\n  if (matches.length === 1)\n    return matches[0];\n\n  if (typeof node_or_selector !== 'string') {\n    console.error(`${matches.length} elements match selector \\`${selector}\\` with startingpoint`, node_or_selector, matches);\n    throw new Error(`${matches.length} elements match selector \\`${selector}\\` given startingpoint '${node_or_selector.nodeName}'`);\n  } else {\n    console.error(`${matches.length} elements match selector \\`${node_or_selector}\\` in the document`, matches);\n    throw new Error(`${matches.length} elements match selector \\`${node_or_selector}\\` in the document`);\n  }\n}\n\nexport function qSA<E extends Element = HTMLElement>(startnode: QuerySelectorAble, selector: string): E[];\nexport function qSA<E extends Element = HTMLElement>(selector: string): E[];\n\n/** Find multiple elements using a CSS selector\n * @param node_or_selector - The starting node or selector\n * @param selector - The selector to match if the starting node was specified\n * @returns The requested elements.\n */\nexport function qSA<E extends Element>(node_or_selector: QuerySelectorAble | string, selector?: string): E[] {\n  if (typeof node_or_selector === 'string')\n    return Array.from(document.querySelectorAll(node_or_selector));\n  else if (selector)\n    return Array.from(node_or_selector.querySelectorAll(selector));\n\n  return [];\n}\n\n/** Return whether the passed element is a FormControlElement */\nexport function isFormControl(field: Element): field is FormControlElement {\n  return isHTMLElement(field) && [\"INPUT\", \"SELECT\", \"TEXTAREA\"].includes(field.tagName);\n}\n\n/** Return whether the passed element is an editable text field */\nexport function isEditControl(field: Element): field is HTMLElement {\n  return isHTMLElement(field) && ([\"INPUT\", \"TEXTAREA\"].includes(field.tagName) || field.isContentEditable);\n}\n\n/** Test whether node is an Element, even if it's in a different iframe */\nexport function isElement(node: unknown): node is Element {\n  //TODO What is actually going on if defaultView is missing?\n  if (!node || typeof node !== \"object\")\n    return false;\n\n  /* Getting the proto doesn't always work:\n  const proto = (node as Element).ownerDocument.defaultView?.Element;\n  (because our iframes derive off about? not sure) so until someone finds the real answer, we'll do a heuristic\n  */\n\n  /** Analyzing node.constructor.name doesn't work because custom elements don't necessarily have their constructor's name ending in Element\n  return Boolean(\"ownerDocument\" in node && node.constructor.name.match(/Element$/));\n  */\n\n  return Boolean(\"ownerDocument\" in node && (node as Element).nodeType === 1);\n\n}\n\n/** Test whether node is a HTMLElement, even if it's in a different iframe */\nexport function isHTMLElement(node: unknown): node is HTMLElement {\n  return isElement(node) && \"accessKey\" in node;\n}\n\n/**\n * get the relative bound difference between two elements, and return a writable copy\n *\n * @param node - The node for which you need coordinates\n * @param relativeto - Optional reference point. If not set, you just get a 'normal' coordinate object\n */\nexport function getRelativeBounds(node: Element, relativeto?: Element): Rect {\n  if (!relativeto)\n    relativeto = node.ownerDocument.documentElement;\n\n  const nodecoords = node.getBoundingClientRect();\n  const relcoords = relativeto.getBoundingClientRect();\n  return {\n    top: nodecoords.top - relcoords.top,\n    left: nodecoords.left - relcoords.left,\n    right: nodecoords.right - relcoords.left,\n    bottom: nodecoords.bottom - relcoords.top,\n    width: nodecoords.width,\n    height: nodecoords.height\n  };\n}\n\nexport type ParsedLanguageTag = {\n  /** The full tag, eg en-US */\n  tag: string;\n  /** The language code, eg 'en' */\n  language: string;\n  region?: string;\n  /** The script, eg 'Hant' */\n  script?: string;\n};\n\n/** Parse a BCP47 language tag */\nexport function parseLanguageTag(languageTag: string): ParsedLanguageTag {\n  /* https://datatracker.ietf.org/doc/html/rfc5646#section-2.1\n     langtag       = language\n                 [\"-\" script]\n                 [\"-\" region]\n                 *(\"-\" variant)\n                 *(\"-\" extension)\n                 [\"-\" privateuse]\n     BUT: we're limiting ourselves to language, script and region for now\n\n     language      = 2*3ALPHA           ; shortest ISO 639 code\n                    [\"-\" extlang]       ; sometimes followed by\n                                          extended language subtags\n\n                                           extlang       = 3ALPHA              ; selected ISO 639 codes\n                 *2(\"-\" 3ALPHA)      ; permanently reserved\n\n      script        = 4ALPHA              ; ISO 15924 code\n\n      region        = 2ALPHA              ; ISO 3166-1 code\n                    / 3DIGIT              ; UN M.49 code\n\n     Note that we're not yet fully implementing the spec, just the parts we expect to encounter in practice\n  */\n  const match = languageTag.match(/^([a-z]{2,}(-[a-z]{3})?)(-[A-Za-z]{4,})?(-[A-Z]{2}|-\\d{3})?$/);\n  if (!match)\n    throw new Error(`Invalid language tag: ${languageTag}`);\n\n  return {\n    tag: languageTag,\n    language: match[1],\n    ...(match[3] ? { script: match[3].substring(1) } : {}),\n    ...(match[4] ? { region: match[4].substring(1) } : {})\n  };\n}\n\n/** Get the effective language for an element (or the document)\n * @param node - The element for which to get the effective language. If not specified, the documentElement and finally the browser is used\n * @returns The language code, eg. \"nl\" or \"en-GB\"\n */\nexport function getLang(node?: Element): ParsedLanguageTag {\n  if (!node && typeof document !== \"undefined\")\n    node = document.documentElement;\n  const tag = (node?.closest('[lang]')?.getAttribute(\"lang\") || navigator.language || \"en\");\n  return parseLanguageTag(tag);\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any -- Cannot specify this without using any */\n\nexport interface EmplaceHandler<ValueType> {\n  insert?: () => ValueType;\n  update?: (current: ValueType) => ValueType;\n}\n\nfunction binarySearchImpl<A, K>(compareFn: (element: A, key: K) => number, contents: A[], searchfor: K, upper_bound: boolean): { present: boolean; index: number } {\n  let first = 0;\n  let len = contents.length;\n  let present = false;\n\n  const cmpbound = upper_bound ? 1 : 0;\n  let unsorted_cmp = 0; // if this is non-0 and cmp is this value, we have an unsorted list\n  while (len > 0) {\n    const half = Math.floor(len / 2);\n    const middle = first + half;\n    const cmp = compareFn(contents[middle], searchfor);\n    if (cmp === 0) {\n      present = true;\n      unsorted_cmp = upper_bound ? -1 : 1;\n    } else if (cmp === unsorted_cmp)\n      throw new Error(`The provided array was not properly sorted!`);\n\n    if (cmp < cmpbound) {\n      first = middle + 1;\n      len -= half;\n      --len;\n    } else {\n      len = half;\n    }\n  }\n  return { present, index: first };\n}\n\n//emplace is based on https://github.com/tc39/proposal-upsert (a Map.prototype.emplace)\n/** Place a value into a Map\n * @param map - The map to place the value into\n * @param key - The key to add/replace\n * @param handler - Callbacks for inserting or updating the value\n * @returns The value that was placed into the map.\n * @throws If the key is not found and no insert handler is provided\n */\nexport function emplace<T extends Map<any, any> | WeakMap<any, any>>(\n  map: T,\n  key: T extends Map<infer K, any> ? K : T extends WeakMap<infer K, any> ? K : never,\n  handler?: T extends Map<any, infer V> ? EmplaceHandler<V> : T extends WeakMap<any, infer V> ? EmplaceHandler<V> : never):\n  T extends Map<any, infer V> ? V : T extends WeakMap<any, infer V> ? V : never {\n  let current = map.get(key);\n  if (current !== undefined) {\n    if (handler?.update) {\n      current = handler.update(current);\n      map.set(key, current);\n    }\n    return current;\n  }\n\n  if (!handler?.insert)\n    throw new Error(\"Key not found and no insert handler provided\");\n\n  const setvalue = handler.insert();\n  map.set(key, setvalue);\n  return setvalue;\n}\n\n// the `| keyof X` is needed to be able to use a `keyof X` type as key parameter in DistributedPick or DistributedOmit in generics\nexport type DistributedKeys<X extends object> = (X extends object ? keyof X : never) | keyof X;\n\n/** Applies Pick to all types in a union. Allows all keys that are present in any object in the union. Warning: You might not be\n    able to use all keys of the union if TypeScript has narrowed the union to a specific type. Eg:\n```typescript\ntype A = { x: number; t: \"a\"; a: number } | { x: number; t: \"b\"; b: number };\nconst a: A = { t: \"a\", a: 1 };\nconst b = pick(a, [\"t\", \"a\", \"b\"]); // No overload matches this call. <snip> Type '\"b\"' is not assignable to type '\"a\" | \"t\" | \"d\"'.\n```\n    @typeParam T - Type of the supplied object\n    @typeParam K - Type of the property keys to return\n    @returns Type with only the specified keys (distributed over the union if present)\n*/\nexport type DistributedPick<X extends object, Y extends DistributedKeys<X>> = X extends object ? Pick<X, keyof X & Y> : never;\n\n/** Applies Omit to all types in a union. Allows all keys that are not present in any object in the union. Warning: You might not be\n    able to use all keys of the union if TypeScript has narrowed the union to a specific type. Eg:\n```typescript\ntype A = { x: number; t: \"a\"; a: number } | { x: number; t: \"b\"; b: number };\nconst a: A = { t: \"a\", a: 1 };\nconst b = omit(a, [\"b\"]); // No overload matches this call. <snip> Type '\"b\"' is not assignable to type '\"a\" | \"t\" | \"d\"'.\n```\n    @typeParam T - Type of the supplied object\n    @typeParam K - Type of the property keys to leave out\n    @returns Type with only the specified keys left out (distributed over the union if present)\n*/\nexport type DistributedOmit<X extends object, Y extends DistributedKeys<X>> = X extends object ? Omit<X, keyof X & Y> : never;\n\n/** Returns an object with a selection of properties\n    @typeParam T - Type of the supplied object\n    @typeParam K - Type of the property keys to return\n    @param obj - Object to pick properties out of\n    @param keys - Names of the properties to pick\n    @returns Resulting object\n*/\nexport function pick<T extends object, K extends string & NoInfer<DistributedKeys<T>>>(obj: T, keys: readonly K[]): DistributedPick<T, K>;\n\n/** Returns an array with a selection of properties\n    @typeParam T - Type of the supplied array\n    @typeParam K - Type of the property keys to return\n    @param arr - Array to pick properties out of\n    @param keys - Names of the properties to pick\n    @returns Resulting array\n*/\nexport function pick<T extends object, K extends string & NoInfer<DistributedKeys<T>>>(arr: T[], keys: readonly K[]): Array<DistributedPick<T, K>>;\n\nexport function pick<T extends object, K extends string & NoInfer<DistributedKeys<T>>>(value: T | T[], keys: readonly K[]): DistributedPick<T, K> | Array<DistributedPick<T, K>> {\n  if (Array.isArray(value))\n    return value.map((elt: T) => pick(elt, keys));\n  const ret = {} as T;\n  keys.forEach((key: K) => {\n    if (key in value)\n      ret[key] = value[key];\n  });\n  return ret as object as DistributedPick<T, K>;\n}\n\n/** Returns an object with a selection of properties left out\n    @typeParam T - Type of the supplied object\n    @typeParam K - Type of the property keys to leave out\n    @param obj - Object to leave properties out of\n    @param keys - Names of the properties to remove\n    @returns Resulting object\n*/\nexport function omit<T extends object, K extends string & NoInfer<DistributedKeys<T>>>(obj: T, keys: readonly K[]): DistributedOmit<T, K>;\n\n/** Returns an array with a selection of properties left out\n    @typeParam T - Type of the supplied array\n    @typeParam K - Type of the property keys to leave out\n    @param arr - Array to leave properties out of\n    @param keys - Names of the properties to leave out\n    @returns Resulting array\n*/\nexport function omit<T extends object, K extends string & NoInfer<DistributedKeys<T>>>(arr: T[], keys: readonly K[]): Array<DistributedOmit<T, K>>;\n\nexport function omit<T extends object, K extends string & NoInfer<DistributedKeys<T>>>(value: T | T[], keys: readonly K[]): DistributedOmit<T, K> | Array<DistributedOmit<T, K>> {\n  if (Array.isArray(value))\n    return value.map((elt: T) => omit(elt, keys));\n  const ret = {} as T;\n  for (const [key, val] of Object.entries(value)) {\n    if (!keys.includes(key as K))\n      ret[key as K] = val;\n  }\n  return ret as object as DistributedOmit<T, K>;\n}\n\n/** Shuffle an array in-place\n * @param array - Array to shuffle\n * @returns The shuffled array\n*/\nexport function shuffle<T>(array: T[]): T[] {\n  for (let i = array.length - 1; i > 0; i--) {\n    const j = Math.floor(Math.random() * (i + 1));\n    [array[i], array[j]] = [array[j], array[i]];\n  }\n  return array;\n}\n\n/** Returns (and TypeScript-asserts) that the specified function is truthy. Handy for adjusting the type of an array when\n * filtering out falsy values\n * @example\n * const myarray: Array&lt;number | null&gt; = [0, null];\n * const filtered: number[] = myarray.filter(isTruthy);\n **/\nexport function isTruthy<T>(a: T): a is (T & {}) {\n  return Boolean(a);\n}\n\n/** Append to array, without overflowing the stack (eg V8 overflows at more than 32K entries)\n * @param array - Array to append to\n * @param values - Values to append\n*/\nexport function appendToArray<T>(array: T[], values: ReadonlyArray<NoInfer<T>>): void {\n  if (values.length < 1000)\n    array.push(...values); //push should be safe enough\n  else for (const value of values) //performance wise this appears just as fast as tricks with pushing blocks of slices\n    array.push(value);\n\n  //not returning the original array to make it clear we're not creating a new one\n}\n\ntype TypedEntriesInternal<T extends object, K extends keyof T> = K extends keyof T ? [K, T[K]] : never;\n\n/** Returns a union of [ key, value ] types for an object */\nexport type TypedEntries<T extends object> = T extends object ? TypedEntriesInternal<T, keyof T & string> : never;\n\n/** Returns an array of key/values of the enumerable own properties of an object, with exact types. Use when\n * no extra properties are expected on the object. Exists because Object.entries returns always string as key type.\n * @param obj - Object that contains the properties and methods\n */\nexport function typedEntries<T extends object>(obj: T): Array<TypedEntries<T>> {\n  return Object.entries(obj) as Array<TypedEntries<T>>;\n}\n\n/** Returns an array of keys of the enumerable own properties of an object, with exact types. Use when\n * no extra properties are expected on the object. Exists because Object.keys returns always string as key type.\n * @param obj - Object that contains the properties and methods\n */\nexport function typedKeys<T extends object>(obj: T): Array<keyof T & string> {\n  return Object.keys(obj) as Array<keyof T & string>;\n}\n\n/** Given a union of [key, value] types, returns the object constructed from those pairs. Exists because\n *  Object.fromEntries does not take the type of the keys and values into account.\n * @typeParam T - Union of [key, value] pairs\n */\nexport type TypedFromEntries<T extends [string, unknown]> = { [C in T as C[0]]: C[1] };\n\n/** Returns an object from an iterable of [key, value] pairs, with exact types. Exists because Object.fromEntries\n * doesn't take the type of the keys and values into account.\n * @param entries - Iterable of [key, value] pairs\n * @returns An object with the same keys and values as the input\n */\nexport function typedFromEntries<T extends [string, unknown]>(entries: Iterable<T>): TypedFromEntries<T> {\n  return Object.fromEntries(entries) as TypedFromEntries<T>;\n}\n\n\nexport class SortedMultiSet<V> {\n  private contents: V[] = [];\n  private compareFn;\n\n  constructor(compareFn: (lhs: V, rhs: V) => number, contents?: V[]) {\n    this.compareFn = compareFn;\n    if (contents)\n      this.addMultiple(contents);\n  }\n\n  get size() {\n    return this.contents.length;\n  }\n\n  at(index: number): V | undefined {\n    return this.contents.at(index);\n  }\n\n  add(value: V): number {\n    const pos = this.upperBound(value);\n    this.contents.splice(pos, 0, value);\n    return pos;\n  }\n\n  addMultiple(values: V[]): void {\n    for (const value of values) {\n      this.add(value);\n    }\n  }\n\n  delete(key: V): void {\n    const [start, limit] = this.range(key);\n    this.contents.splice(start, limit - start);\n  }\n\n  clear() {\n    this.contents.splice(0, this.contents.length);\n  }\n\n  lowerBound(key: Readonly<V>): { present: boolean; index: number } {\n    return binarySearchImpl(this.compareFn, this.contents, key, false);\n  }\n\n  upperBound(key: Readonly<V>): number {\n    return binarySearchImpl(this.compareFn, this.contents, key, true).index;\n  }\n\n  range(key: Readonly<V>): [number, number] {\n    return [this.lowerBound(key).index, this.upperBound(key)];\n  }\n\n  slice(start: number, limit: number): V[] {\n    return this.contents.slice(start, limit);\n  }\n\n  *#sliceIterator(start: number, limit: number): Generator<V, void> {\n    for (let idx = start; idx < limit; ++idx)\n      yield this.contents[idx];\n  }\n\n  sliceRange(key: Readonly<V>): V[] {\n    const [start, limit] = this.range(key);\n    return this.slice(start, limit);\n  }\n\n  rangeIterator(key: Readonly<V>): Iterable<V> {\n    const [start, limit] = this.range(key);\n    return this.#sliceIterator(start, limit);\n  }\n}\n\nexport class SortedMultiMap<K, V> {\n  private compareFn;\n  private contents: Array<[K, V]> = [];\n\n  constructor(compareFn: (lhs: K, rhs: K) => number, contents?: Array<[K, V]>) {\n    this.compareFn = (element: [K, V], key: K) => compareFn(element[0], key);\n    if (contents)\n      this.addMultiple(contents);\n  }\n\n  get size() {\n    return this.contents.length;\n  }\n\n  at(index: number): [K, V] | undefined {\n    return this.contents.at(index);\n  }\n\n  add(key: K, value: V): number {\n    const pos = this.upperBound(key);\n    this.contents.splice(pos, 0, [key, value]);\n    return pos;\n  }\n\n  addMultiple(values: Array<[K, V]>) {\n    for (const [key, value] of values) {\n      this.add(key, value);\n    }\n  }\n\n  delete(key: K): void {\n    const [start, limit] = this.range(key);\n    this.contents.splice(start, limit - start);\n  }\n\n  clear() {\n    this.contents.splice(0, this.contents.length);\n  }\n\n  lowerBound(key: Readonly<K>): { present: boolean; index: number } {\n    return binarySearchImpl(this.compareFn, this.contents, key, false);\n  }\n\n  upperBound(key: Readonly<K>): number {\n    return binarySearchImpl(this.compareFn, this.contents, key, true).index;\n  }\n\n  range(key: Readonly<K>): [number, number] {\n    return [this.lowerBound(key).index, this.upperBound(key)];\n  }\n\n  slice(start: number, limit: number): Array<[K, V]> {\n    return this.contents.slice(start, limit);\n  }\n\n  *#sliceIterator(start: number, limit: number): Generator<[K, V], void> {\n    for (let idx = start; idx < limit; ++idx)\n      yield this.contents[idx];\n  }\n\n  sliceRange(key: Readonly<K>): Array<[K, V]> {\n    const [start, limit] = this.range(key);\n    return this.slice(start, limit);\n  }\n\n  rangeIterator(key: Readonly<K>): Iterable<[K, V]> {\n    const [start, limit] = this.range(key);\n    return this.#sliceIterator(start, limit);\n  }\n}\n","const unlock = Symbol(\"unlock\");\n\n/** A mutex which is only visible in the current JavaScript VM\n\n    @example\n```ts\nconst mutex = new LocalMutex;\n\nusing lock = await mutex.lock();\nlock.release();\n\n// If you'll be relying on automatic release\nusing lock = await mutex.lock();\nvoid(lock);\n```\n\n*/\nexport class LocalMutex {\n  private locked = false;\n  private unlockList = new Array<() => void>;\n\n  /** Lock this mutex\n   * @returns Lock object. Use with `using` to ensure automatic unlock\n   */\n  async lock() {\n    if (this.locked) {\n      const defer = Promise.withResolvers<void>();\n      this.unlockList.push(() => defer.resolve());\n      await defer.promise;\n    } else {\n      this.locked = true;\n    }\n    return new LocalLock(this);\n  }\n\n  [unlock](): void {\n    if (this.unlockList.length > 0)\n      (this.unlockList.shift()!)(); //we're fair... unlock longest waiter\n    else\n      this.locked = false;\n  }\n}\n\n/** Instance of a LocalMutex lock */\nclass LocalLock implements Disposable {\n  private mutex: LocalMutex | null;\n\n  constructor(mutex: LocalMutex) {\n    this.mutex = mutex;\n  }\n  /** Explicitly release the lock\n   * @throws Error if the lock is already released\n  */\n  release() {\n    if (!this.mutex)\n      throw new Error(\"Lock already released\");\n\n    this.mutex[unlock]();\n    this.mutex = null;\n  }\n  [Symbol.dispose]() {\n    this.mutex?.[unlock]();\n  }\n}\n\nexport type { LocalLock };\n","/* frontend-config parses the wh-config object in the browser and mocks any missing data. @webhare/env does not actually expose this config, @webhare/frontend does\n   The frontend configuration is built in PageBuildRequest's 'frontendConfig' member */\n\nimport type { DTAPStage } from \"@webhare/env/src/concepts\";\nimport { debugFlags, initEnv } from \"@webhare/env/src/envbackend\";\nimport { getBrowserDebugFlags } from \"@webhare/env/src/init-browser\";\nimport { getLang } from \"@webhare/dompack/src/tree\";\nimport type { FrontendDataTypes } from \"@webhare/frontend\";\nimport { omit } from \"@webhare/std\";\n\n\n/** The format of the <script id=\"wh-config\"> object  */\nexport type WHConfigScriptData = {\n  //NOTE: existing frontend code doesn't expect site/obj to ever be null. not sure if 'object' provides the best interface or whether we need some sort of 'unknown but an existing object'\n  /** Page (targetobject) specific settings\n   *  @deprecated Use setFrontendData for type-safe settings\n  */\n  obj: { [key: string]: unknown };\n  /** Site specific settings\n   *  @deprecated Use setFrontendData for type-safe settings\n   */\n  site: { [key: string]: unknown };\n\n  /** Numeric server version number (eg 5.02.24 = 50224)\n   *  @deprecated Interpreting numbers (or version strings) is dangerous. Feature flags/testing or limiting your entire module to compatible versions is safer\n  */\n  server: number;\n  /** Root URL of this site */\n  siteRoot: string;\n} & {\n  ///Plugins may add keys at this level\n  [key in keyof FrontendDataTypes]?: FrontendDataTypes[key];\n};\n\nexport type WHConfigScriptData_FromServer = WHConfigScriptData & { dtapStage: DTAPStage };\n\n//names fields can still have when not yet republished\nexport interface WHConfigScriptData_OldPublishFields {\n  islive: boolean;\n  dtapstage: DTAPStage;\n  siteroot: string;\n}\n\n//fallback names with deprecation warnings\nexport interface WHConfigScriptData_LegacyFields {\n  /** @deprecated Use dtapStage in WH5.4+ */\n  islive: boolean;\n  /** @deprecated Use `dtapStage` from \"\\@webhare/env\"; in WH5.4+ */\n  dtapstage: DTAPStage;\n  /** @deprecated Use `getSiteRoot` from \"\\@webhare/frontend\"; in WH5.7+ */\n  siteroot: string;\n}\n\nlet config: WHConfigScriptData_FromServer | undefined;\nlet siteroot;\nlet dtapStage: DTAPStage = \"production\";\n\n//if document is undefined, we're serverside or in a worker\nconst whconfigel = typeof document !== \"undefined\" ? document.querySelector('script#wh-config') : null;\nif (whconfigel?.textContent) {\n  const parsedConfig: WHConfigScriptData_FromServer & WHConfigScriptData_OldPublishFields = JSON.parse(whconfigel.textContent);\n  config = omit(parsedConfig, [\"siteroot\", \"dtapstage\", \"islive\"]);\n\n  //Fallbacks for pages last published with WH5.3 *and* pages published from HareScript which still emit lowercase props\n  siteroot = parsedConfig.siteRoot ?? parsedConfig.siteroot!;\n  dtapStage = parsedConfig.dtapStage ?? parsedConfig.dtapstage;\n}\n\ninitEnv(dtapStage, '/');\n\nif (typeof location !== \"undefined\")\n  for (const flag of getBrowserDebugFlags('wh-debug'))\n    debugFlags[flag] = true;\n\n\n/** @deprecated frontendConfig has been deprecated. Switch to the getFrontendData system */\n// Make sure we have obj/site as some sort of object, to prevent crashes on naive 'if ($wh.config.obj.x)' tests'\nexport const frontendConfig: WHConfigScriptData & WHConfigScriptData_LegacyFields = {\n  server: 0,\n  ...config,\n  obj: config?.obj || {},\n  site: config?.site || {},\n  siteRoot: siteroot || \"\",\n  dtapstage: dtapStage,\n  islive: dtapStage === \"production\" || dtapStage === \"acceptance\",\n  siteroot: siteroot || \"\",\n};\n\nif (typeof document !== \"undefined\") {\n  //@ts-expect-error -- locale isn't supposed to exist - but some old code is still referring to it\n  frontendConfig.locale = getLang().tag;\n}\n\nif (dtapStage === \"development\") { //WH6.0: it's time to hard phase-out the old fields\n  for (const prop of [\"islive\", \"dtapstage\", \"siteroot\", \"locale\"] as const)\n    Object.defineProperty(frontendConfig, prop, {\n      get() { throw new Error(`frontendConfig.${prop} will be removed`); }\n    });\n}\n\n//NOTE: These APIs need to live in init.ts so eg gtm.ts can access us without triggering a CSS reset through frontend.ts. When frontend.ts stops auto-resetting we might move it back\n\nexport function getFrontendData<Type extends keyof FrontendDataTypes>(type: Type, options: { allowMissing: true }): FrontendDataTypes[Type] | null;\nexport function getFrontendData<Type extends keyof FrontendDataTypes>(type: Type, options?: { allowMissing: boolean }): FrontendDataTypes[Type];\n\n/** Get data exported by the response\n * @typeParam Type - The type of data to get\n * @param dataObject - The data object of data to get\n * @param allowMissing - If true, return null if the data object is missing. Otherwise throw an error\n * @example\n```\n  declare module \"@webhare/frontend\" {\n    interface FrontendDataTypes {\n      \"mymodule:type\": {\n        test: number;\n      };\n    }\n  }\n\n  const data = getFrontendData(\"mymodule:type\");\n```\n*/\nexport function getFrontendData<Type extends keyof FrontendDataTypes>(dataObject: Type, { allowMissing = false } = {}): FrontendDataTypes[Type] | null {\n  const retval = config?.[dataObject] as FrontendDataTypes[Type];\n  if (!retval)\n    if (allowMissing)\n      return null;\n    else\n      throw new Error(`Missing frontend data object: ${dataObject}`);\n\n  return retval;\n}\n\n/** Get the current site root\n *\n * @returns The site root URL (ending with a slash)\n*/\nexport function getSiteRoot() { //now an API to improve treeshaking\n  return frontendConfig.siteRoot;\n}\n","// Set up dispose symbols - https://github.com/evanw/esbuild/pull/3192 \"you'll need to polyfill Symbol.dispose if it's not present before you use it.\n\n//@ts-expect-error -- It's marked readonly\nSymbol.dispose ||= Symbol.for('Symbol.dispose');\n//@ts-expect-error -- It's marked readonly\nSymbol.asyncDispose ||= Symbol.for('Symbol.asyncDispose');\n\n// Set up promise resolvers (ES2024)\nPromise.withResolvers ||= function <T>(): PromiseWithResolvers<T> {\n  let resolve: (value: T | PromiseLike<T>) => void;\n  let reject: (reason: Error) => void;\n  const promise = new Promise<T>((_resolve, _reject) => { resolve = _resolve; reject = _reject; });\n  // @ts-expect-error `resolve` and `reject` are assigned synchronously, which isn't picked up by the TypeScript compiler (see\n  // https://github.com/Microsoft/TypeScript/issues/30053)\n  return { promise, resolve, reject };\n};\n\n// Polyfills for https://caniuse.com/?search=groupby - needed until we can assume safari 17.4 as global baseline\nif (!Map.groupBy)\n  Map.groupBy = function <Item, Key>(items: Iterable<Item>, callbackfn: (item: Item, idx: number) => Key): Map<Key, Item[]> {\n    const retval = new Map<Key, Item[]>;\n    let idx = 0;\n    for (const item of items) {\n      const key = callbackfn(item, idx++);\n      let list = retval.get(key);\n      if (!list)\n        retval.set(key, list = []);\n      list.push(item);\n    }\n    return retval;\n  };\n\nif (!Object.groupBy)\n  Object.groupBy = function <Item, Key extends string | number | symbol>(items: Iterable<Item>, callbackfn: (item: Item, idx: number) => Key): Partial<Record<Key, Item[]>> {\n    return Object.fromEntries(Map.groupBy(items, callbackfn).entries()) as Partial<Record<Key, Item[]>>;\n  };\n","/* eslint-disable @typescript-eslint/no-loop-func */\n/* eslint-disable @typescript-eslint/no-explicit-any */\n/* eslint-disable @typescript-eslint/no-this-alias */\n\n/* Iterator polyfills (https://caniuse.com/?search=iterator) - stage 4 ES but not in all browsers yet\n   Based on https://github.com/alkihis/iterator-polyfill/tree/master but we removed all the AsyncIterator\n   stuff as that's still stage 2 AND not implemented in TypeScript (as of 5.6)\n\n   We're ignoring a few linting/any issues as we really don't care to maintain this much\n   Fixed an issue with .some - reported as https://github.com/alkihis/iterator-polyfill/issues/6\n*/\n\n(function () {\n  // polyfill already applied / proposal implemented\n  if ('Iterator' in globalThis) {\n    return;\n  }\n\n  // Polyfill for Iterator\n  const IteratorPrototype = {};\n\n  const ArrayIteratorPrototype = Object.getPrototypeOf([][Symbol.iterator]());\n  const OriginalIteratorPrototype = Object.getPrototypeOf(ArrayIteratorPrototype);\n\n  Object.setPrototypeOf(OriginalIteratorPrototype, IteratorPrototype);\n\n  Object.defineProperties(IteratorPrototype, {\n    [Symbol.iterator]: {\n      value() {\n        return this;\n      }\n    },\n    map: {\n      *value<T, R>(callback: (value: T) => R): Generator<any, any, any> {\n        const it = this;\n        let value = it.next();\n\n        while (!value.done) {\n          const real_value = callback(value.value);\n          const next_value = yield real_value;\n          value = it.next(next_value);\n        }\n\n        return value.value;\n      },\n    },\n    filter: {\n      *value<T>(callback: (value: T) => boolean): Generator<any, any, any> {\n        const it = this;\n        let value = it.next();\n        let next_value;\n\n        while (!value.done) {\n          const real_value = value.value;\n          if (callback(real_value)) {\n            next_value = yield real_value;\n            value = it.next(next_value);\n          } else {\n            value = it.next(next_value);\n          }\n        }\n\n        return value.value;\n      },\n    },\n    find: {\n      value<T>(callback: (value: boolean) => T) {\n        const it = this;\n        let value = it.next();\n\n        while (!value.done) {\n          const real_value = value.value;\n\n          if (callback(real_value))\n            return real_value;\n\n          value = it.next();\n        }\n      }\n    },\n    every: {\n      value<T>(callback: (value: T) => boolean) {\n        const it = this;\n        let value = it.next();\n\n        while (!value.done) {\n          const real_value = value.value;\n\n          if (!callback(real_value))\n            return false;\n\n          value = it.next();\n        }\n\n        return true;\n      }\n    },\n    some: {\n      value<T>(callback: (value: T) => boolean) {\n        const it = this;\n        let value = it.next();\n\n        while (!value.done) {\n          const real_value = value.value;\n\n          if (callback(real_value)) {\n            it.return();\n            return true;\n          }\n\n          value = it.next();\n        }\n\n        return false;\n      }\n    },\n    toArray: {\n      value(max_count = Infinity) {\n        const values = [];\n\n        const it = this;\n        let value = it.next();\n\n        while (!value.done) {\n          const real_value = value.value;\n\n          if (max_count <= 0)\n            return values;\n\n          values.push(real_value);\n\n          if (max_count !== Infinity)\n            max_count--;\n\n          value = it.next();\n        }\n\n        return values;\n      }\n    },\n    take: {\n      *value(limit: number): Generator<any, any, any> {\n        limit = Number(limit);\n        if (limit < 0)\n          throw new RangeError('Invalid limit.');\n\n        const it = this;\n        let value = it.next();\n        let remaining = limit;\n        let next_value;\n\n        while (!value.done) {\n          const real_value = value.value;\n\n          if (remaining <= 0)\n            return;\n\n          next_value = yield real_value;\n          value = it.next(next_value);\n          remaining--;\n        }\n\n        return value.value;\n      },\n    },\n    drop: {\n      *value(limit: number): Generator<any, any, any> {\n        limit = Number(limit);\n        if (limit < 0)\n          throw new RangeError('Invalid limit.');\n\n        const it = this;\n        let value = it.next();\n        let remaining = limit;\n        let next_value;\n\n        while (!value.done) {\n          const real_value = value.value;\n\n          if (remaining > 0) {\n            value = it.next(next_value);\n            remaining--;\n            continue;\n          }\n\n          next_value = yield real_value;\n          value = it.next(next_value);\n        }\n\n        return value.value;\n      },\n    },\n    asIndexedPairs: {\n      *value(): Generator<any, any, any> {\n        const it = this;\n        let value = it.next();\n        let index = 0;\n\n        while (!value.done) {\n          const real_value = value.value;\n          const next_value = yield [index, real_value];\n          value = it.next(next_value);\n          index++;\n        }\n\n        return value.value;\n      }\n    },\n    flatMap: {\n      *value<T, R extends object>(mapper: (value: T) => IterableIterator<R> | R): Generator<any, any, any> {\n        if (typeof mapper !== 'function') {\n          throw new TypeError('Mapper must be a function.');\n        }\n\n        const it = this;\n        let value = it.next();\n        let next_value;\n\n        while (!value.done) {\n          const real_value = value.value;\n          const mapped = mapper(real_value);\n\n          if (Symbol.iterator in mapped) {\n            next_value = yield* mapped[Symbol.iterator]();\n          } else {\n            next_value = yield mapped;\n          }\n\n          value = it.next(next_value);\n        }\n\n        return value.value;\n      },\n    },\n    reduce: {\n      value<T, V>(reducer: (acc: V, value: T) => V, initial_value?: V) {\n        let acc = initial_value;\n\n        const it = this;\n        if (acc === undefined) {\n          acc = it.next().value;\n        }\n\n        let value = it.next();\n        while (!value.done) {\n          const real_value = value.value;\n\n          acc = reducer(acc!, real_value);\n\n          value = it.next();\n        }\n\n        return acc;\n      }\n    },\n    forEach: {\n      value<T>(callback: (value: T) => any) {\n        const it = this;\n        let value = it.next();\n\n        while (!value.done) {\n          const real_value = value.value;\n\n          callback(real_value);\n\n          value = it.next();\n        }\n      }\n    },\n    [Symbol.toStringTag]: {\n      value: 'IteratorPrototype'\n    },\n\n    /* OUTSIDE PROPOSAL */\n    count: {\n      value() {\n        let count = 0;\n\n        const it = this;\n        let value = it.next();\n\n        while (!value.done) {\n          count++;\n          value = it.next();\n        }\n\n        return count;\n      },\n    },\n    join: {\n      value(string: string) {\n        let final = '';\n        let first = true;\n\n        const it = this;\n        let value = it.next();\n\n        while (!value.done) {\n          const real_value = value.value;\n\n          if (first) {\n            first = false;\n            final += real_value;\n          } else {\n            final += string + real_value;\n          }\n\n          value = it.next();\n        }\n\n        return final;\n      }\n    },\n    chain: {\n      *value<I>(...iterables: IterableIterator<I>[]) {\n        yield* this;\n\n        for (const it of iterables) {\n          yield* it;\n        }\n      }\n    },\n    zip: {\n      *value<T, O>(...others: IterableIterator<O>[]): Iterator<(T | O)[]> {\n        const it_array = [this, ...others].map((e: any) => Symbol.iterator in e ? e[Symbol.iterator]() : e as Iterator<T | O>);\n        let values = it_array.map(e => e.next());\n        let next_value: any;\n\n        while (values.every(e => !e.done)) {\n          next_value = yield values.map(e => e.value);\n          values = it_array.map(e => e.next(next_value));\n        }\n      },\n    },\n    takeWhile: {\n      *value<T>(callback: (value: T) => boolean): Generator<any, any, any> {\n        const it = this;\n        let value = it.next();\n        let next_value;\n\n        while (!value.done) {\n          const real_value = value.value;\n\n          if (callback(real_value))\n            next_value = yield real_value;\n          else\n            return;\n\n          value = it.next(next_value);\n        }\n\n        return value.value;\n      }\n    },\n    dropWhile: {\n      *value<T>(callback: (value: T) => boolean): Generator<any, any, any> {\n        const it = this;\n        let value = it.next();\n        let next_value;\n        let finished = false;\n\n        while (!value.done) {\n          const real_value = value.value;\n\n          if (!finished && callback(real_value)) {\n            value = it.next(next_value);\n            continue;\n          }\n\n          finished = true;\n          next_value = yield real_value;\n\n          value = it.next(next_value);\n        }\n\n        return value.value;\n      }\n    },\n    fuse: {\n      *value(): Generator<any, any, any> {\n        const it = this;\n        let value = it.next();\n        let next_value;\n\n        while (!value.done) {\n          const real_value = value.value;\n\n          if (real_value !== undefined && real_value !== null)\n            next_value = yield real_value;\n          else\n            return;\n\n          value = it.next(next_value);\n        }\n\n        return value.value;\n      }\n    },\n    partition: {\n      value<T>(callback: (value: T) => boolean) {\n        const partition1 = [], partition2 = [];\n\n        const it = this;\n        let value = it.next();\n\n        while (!value.done) {\n          const real_value = value.value;\n\n          if (callback(real_value))\n            partition1.push(real_value);\n          else\n            partition2.push(real_value);\n\n          value = it.next();\n        }\n\n        return [partition1, partition2];\n      },\n    },\n    findIndex: {\n      value<T>(callback: (value: T) => boolean) {\n        const it = this;\n        let i = 0;\n        let value = it.next();\n\n        while (!value.done) {\n          const real_value = value.value;\n\n          if (callback(real_value))\n            return i;\n\n          value = it.next();\n          i++;\n        }\n\n        return -1;\n      }\n    },\n    max: {\n      value() {\n        let max = -Infinity;\n\n        const it = this;\n        let value = it.next();\n\n        while (!value.done) {\n          const real_value = value.value;\n\n          if (max < real_value)\n            max = real_value;\n\n          value = it.next();\n        }\n\n        return max;\n      },\n    },\n    min: {\n      value() {\n        let min = Infinity;\n\n        const it = this;\n        let value = it.next();\n\n        while (!value.done) {\n          const real_value = value.value;\n\n          if (min > real_value)\n            min = real_value;\n\n          value = it.next();\n        }\n\n        return min;\n      },\n    },\n    cycle: {\n      *value(): Generator<any, any, any> {\n        const values = [];\n\n        const it = this;\n        let value = it.next();\n\n        while (!value.done) {\n          const real_value = value.value;\n          values.push(real_value);\n\n          const next_value = yield real_value;\n          value = it.next(next_value);\n        }\n\n        while (true) {\n          yield* values;\n        }\n      },\n    },\n  });\n\n  //iterator.from from https://github.com/rauschma/iterator-helpers-polyfill/\n\n  function isObject(value: unknown) {\n    if (value === null) return false;\n    const t = typeof value;\n    return t === 'object' || t === 'function';\n  }\n\n  function GetIteratorFlattenable<T>(obj: Record<symbol, any>): T {\n    if (!isObject(obj)) {\n      throw new TypeError();\n    }\n    const method = obj[Symbol.iterator];\n    let iterator = undefined;\n    if (typeof method !== 'function') {\n      iterator = obj;\n    } else {\n      iterator = method.call(obj);\n    }\n    if (!isObject(iterator)) {\n      throw new TypeError();\n    }\n    return iterator;\n  }\n\n  if (!('Iterator' in globalThis)) {\n    const Iterator = function Iterator() { };\n\n    Iterator.prototype = IteratorPrototype;\n\n    // @ts-expect-error We're still missing From in this poyfill\n    (globalThis).Iterator = Iterator;\n  }\n\n\n  //----- Static method -----\n  // Must be done after Iterator.prototype was set up,\n  // so that `extends Iterator` works below\n\n  class WrappedIterator<T, TReturn = any, TNext = undefined> extends Iterator<T, TReturn, TNext> {\n    #iterator;\n    constructor(iterator: Iterator<T, TReturn, TNext>) {\n      super();\n      this.#iterator = iterator;\n    }\n    override next(...args: [] | [TNext]): any {\n      return this.#iterator.next(...args);\n    }\n    // `async` helps with line (*)\n    override return(value?: TReturn | PromiseLike<TReturn>): any {\n      const returnMethod = this.#iterator.return;\n      if (returnMethod === undefined) {\n        return { done: true, value: value as any }; // (*)\n      }\n      return returnMethod.call(this.#iterator);\n    }\n  }\n\n  function Iterator_from<T>(value: any) {\n    const iterator = GetIteratorFlattenable<Iterator<T>>(value);\n    if (iterator instanceof Iterator) {\n      return iterator;\n    }\n    // `iterator´ does not support the new API – wrap it so that it does\n    return new WrappedIterator(iterator);\n  }\n\n  Object.defineProperty(\n    Iterator, 'from',\n    {\n      writable: true,\n      enumerable: false,\n      configurable: true,\n      value: Iterator_from,\n    }\n  );\n})();\n","/* Manages the $wh global, an API for just-in-time debugging */\nimport type { DebugRegistry } from \"@webhare/env\";\nimport { debugFlags } from \"./envbackend\";\n\nlet debugRegFinalizer: FinalizationRegistry<{ type: keyof DebugRegistry; key: string }> | undefined;\n\nexport type WHGlobal = DebugRegistry & {\n  debugFlags: typeof debugFlags;\n  /** Global counter to avoid HMRs from duplicating IDs */\n  nextId: number;\n};\n\ndeclare global {\n  var $wh: WHGlobal;\n}\n\nglobalThis.$wh = {\n  debugFlags,\n  nextId: 1\n} satisfies WHGlobal;\n\nexport function addToDebugRegistry<T extends keyof DebugRegistry>(type: T, key: string, value: NonNullable<ReturnType<NonNullable<DebugRegistry[T]>[string][\"deref\"]>>) {\n  const debugRegistry = globalThis.$wh as unknown as DebugRegistry;\n  //@ts-ignore prevents '>   Type '{}' is not assignable to type 'never'. in jssdk builds, probably because noone yet extends DebugRegistry there\n  debugRegistry[type] ||= {};\n  debugRegFinalizer ||= new FinalizationRegistry(({ type: t, key: k }) => {\n    delete debugRegistry![t]?.[k];\n  });\n\n  if (debugRegistry[type][key])\n    console.error(`Debug registry key ${key} already exists, overwriting it`);\n\n  (debugRegistry[type] as Record<string, unknown>)[key] = new WeakRef(value);\n  debugRegFinalizer.register(value, { type, key });\n}\n","import type { FormControlElement } from \"@webhare/dompack\";\n\nexport const CustomEvent = globalThis.CustomEvent;\n/** Wrap an event to ensure it's target is a HTMLElement\n * @typeParam EventType - The expected event type\n * @typeParam CurrentTargetType - The type of the elemnt you're binding the event to. Optional, defaults to HTMLElement\n * @typeParam RelatedTargetType - The type of the elemnt you're binding the event to. Optional, defaults to HTMLElement\n*/\nexport type DocEvent<EventType extends Event, CurrentTargetType extends HTMLElement = HTMLElement, RelatedTargetType extends HTMLElement = HTMLElement> = EventType & {\n  target: HTMLElement;\n  currentTarget: CurrentTargetType;\n  relatedTarget: RelatedTargetType | null;\n};\n\nexport type DomEventOptions =\n  {\n    bubbles?: boolean;\n    cancelable?: boolean;\n    relatedTarget?: EventTarget;\n    detail?: object;\n  };\n\nexport function dispatchDomEvent(element: EventTarget, eventtype: string, options?: DomEventOptions) {\n  //see here https://developer.mozilla.org/en-US/docs/Web/Events whether an event is bubbles/cancelabel\n  options = {\n    bubbles: [\n      \"input\", \"change\", \"click\", \"contextmenu\", \"dblclick\",\n      \"reset\", \"submit\"\n    ].includes(eventtype),\n    cancelable: [\n      \"animationstart\", \"animationcancel\", \"animationend\", \"animationiteration\",\n      \"beforeunload\",\n      \"click\", \"contextmenu\", \"dblclick\",\n      \"reset\", \"submit\",\n      \"transitionstart\", \"transitioncancel\", \"transitionend\", \"transitionrun\"\n    ].includes(eventtype),\n    ...options\n  };\n\n  if (!element || !(element as Node).ownerDocument)\n    return true; //the element has left the dom... so there's no more bubbling. just drop it\n\n  //FIXME the load/scroll is buggy and we should be probably be using new Event (but an earlier attempt at that triggered quite a few test failures)\n  const createtype = /*[\"load\",\"scroll\"].includes(eventtype) === \"load\" ? \"UIEvents\" :*/[\"focus\", \"blur\", \"focusin\", \"focusout\"].includes(eventtype) ? \"FocusEvent\" : eventtype === \"click\" ? \"MouseEvents\" : \"HTMLEvents\";\n  //we verified its non-null ness above but TS doesn't really understand that\n  const evt = (element as Node).ownerDocument!.createEvent(createtype);\n  evt.initEvent(eventtype, options.bubbles, options.cancelable);\n  if (options.detail)\n    // eslint-disable-next-line @typescript-eslint/no-explicit-any -- we should just rewrite to new Event\n    (evt as any).detail = options.detail;\n  if (options.relatedTarget) //its a readonly prop, so redefine it\n    Object.defineProperty(evt, 'relatedTarget', { value: options.relatedTarget, writable: false });\n\n  //TODO: Should we keep this code?\n  // @ts-expect-error IScroll is a custom Window property set by IScroll\n  if (eventtype === 'click' && window.IScroll)\n    // @ts-expect-error _constructed is a custom Event property used by IScroll\n    evt._constructed = true; //ensure IScroll doesn't blindly cancel our synthetic clicks\n\n  return element.dispatchEvent(evt);\n}\n\n/** Fire the proper modified events (input and/or change) on the element after changing its value\n * @param element - Element to receive event\n * @param options - Event options\n * @deprecated Use changeValue so we can figure out the proper events to fire\n */\nexport function fireModifiedEvents(element: FormControlElement, options?: DomEventOptions) {\n  dispatchDomEvent(element, 'input', options);\n  dispatchDomEvent(element, 'change', options);\n}\n\n//manually fire 'onchange' events. needed for event simulation - DEPRECATED\n/**\n * @param element - Element to receive event\n * @param type - Event type\n * @param options - Event options\n * @deprecated Use dispatchDomEvent instead\n */\nexport function fireHTMLEvent(element: EventTarget, type: string, options?: DomEventOptions) {\n  return dispatchDomEvent(element, type, options);\n}\n\ntype CustomEventParams =\n  {\n    /** Whether this event should bubble up in the DOM */\n    bubbles: boolean;\n    /** Whether this event can be cancelled */\n    cancelable: boolean;\n    /** Custom event information */\n    detail?: unknown;\n    /** Handler to execute if the default isn't prevented by a event listener */\n    defaulthandler?: (evt: CustomEvent) => void;\n  };\n\n/** Fire a custom event\n    @param node - node to fire the event on\n    @param event - event type. You should add this event to the GlobalEventHandlersEventMap for validation in dispatchCustomEvent calls and addEventListener callbacks.\n    @param params - Event options\n    @returns true if the default wasn't prevented\n */\n\nexport function dispatchCustomEvent<K extends string>(\n  node: EventTarget,\n  event: K,\n  params: CustomEventParams & (K extends keyof GlobalEventHandlersEventMap ?\n    GlobalEventHandlersEventMap[K] extends CustomEvent ?\n    { detail: GlobalEventHandlersEventMap[K][\"detail\"] } : unknown : unknown)) {\n\n  if (!params)\n    throw new Error(`Missing dispatchCustomEvent params`);\n  ['bubbles', 'cancelable'].forEach(prop => {\n    if (!(prop in params))\n      throw new Error(`Missing '${prop}' in dispatchCustomEvent params`);\n  });\n\n  const evt = new CustomEvent(event, {\n    bubbles: params.bubbles,\n    cancelable: params.cancelable,\n    detail: params.detail\n  });\n  let defaultaction = true;\n  try {\n    if (!node.dispatchEvent(evt))\n      defaultaction = false; //defaultPrevented is unreliable on IE11, so double check\n  } finally {\n    if (!evt.defaultPrevented && params.defaulthandler && defaultaction) {\n      params.defaulthandler(evt);\n    }\n  }\n  return defaultaction && !evt.defaultPrevented;\n}\n\n/**\n     Change the value of a form element, and fire the correct events as if it were a user change\n *\n    @param element - Element to change\n    @param newvalue - New value\n */\nexport function changeValue(element: FormControlElement, newvalue: string | number | boolean) {\n  if (element.matches(`input[type=radio], input[type=checkbox]`)) {\n    if (Boolean((element as HTMLInputElement).checked) === Boolean(newvalue))\n      return;\n    (element as HTMLInputElement).checked = Boolean(newvalue);\n  } else {\n    //FIXME it's not really clean to assume that this element is changeable - throw for non input/select..\n    const asString = String(newvalue);\n    if ((element as HTMLInputElement).value === asString)\n      return;\n\n    (element as HTMLInputElement).value = String(newvalue);\n  }\n  dispatchDomEvent(element as EventTarget, 'input');\n  dispatchDomEvent(element as EventTarget, 'change');\n}\n\nlet keydata: { mapping: { [key: string]: string } };\n\nfunction initKeyMapping() {\n  keydata =\n  {\n    // Mapping from keyIdentifier/key to key. If not found, translate U+XXXX to the unicode char and return that.\n\n    // List of all current key mappings (and current inconsistencies\n    // at https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values\n    mapping:\n    {\n      \"Del\": \"Delete\", // IE/edge use 'Del' instead of 'Delete'\n      \"Esc\": \"Escape\", // IE/edge use 'Esc' instead of 'Escape'\n      \"OS\": \"Meta\", // Meta key is called OS on IE 9 and Firefox (tested v50)\n      \"Win\": \"Meta\", // Meta key is called Win on IE/Edge\n      \"Scroll\": \"ScrollLock\", // More IE/Edge, from tests\n      \"PrintScreen\": \"Print\", // More IE/Edge, from tests\n      \"MozHomeScreen\": \"GoHome\", // Prior to Firefox 37, the Home button generated a key code of \"Exit\". Starting in Firefox 37, the button generates the key code \"MozHomeScreen\".\n\n      // Internet Explorer 9 and Firefox 36 and earlier return \"Left\", \"Right\", \"Up\", and \"Down\" for the arrow keys, instead of \"ArrowLeft\", \"ArrowRight\", \"ArrowUp\", and \"ArrowDown\".\n      \"Left\": \"ArrowLeft\",\n      \"Right\": \"ArrowRight\",\n      \"Up\": \"ArrowUp\",\n      \"Down\": \"ArrowDown\",\n      // More IE and old Firefox stuff\n      \"Crsel\": \"CrSel\",\n      \"Exsel\": \"ExSel\",\n      \"Nonconvert\": \"NonConvert\",\n      // Internet Explorer 9 and Firefox 36 and earlier report \"Apps\" instead of \"ContextMenu\" for the context menu key.\n      \"Apps\": \"ContextMenu\",\n      // Internet Explorer 9 and Firefox 36 and earlier use \"MediaNextTrack\" and \"MediaPreviousTrack\" instead of \"MediaTrackNext\" and \"MediaTrackPrevious\".\n      \"MediaNextTrack\": \"MediaTrackNext\",\n      \"MediaPreviousTrack\": \"MediaTrackPrevious\",\n      // In Internet Explorer 9, and prior to Firefox 49, \"AudioVolumeUp\", \"AudioVolumeDown\", and \"AudioVolumeMute\" were \"VolumeUp\", \"VolumeDown\", and \"VolumeMute\".\n      // In Firefox 49 they were updated to match the latest specification. The old names are still used on Boot to Gecko.\n      \"VolumeUp\": \"AudioVolumeUp\",\n      \"VolumeDown\": \"AudioVolumeDown\",\n      \"VolumeMute\": \"AudioVolumeMute\",\n      // Firefox added proper support for the \"TV\" key in Firefox 37; before that, this key generated the key code \"Live\".\n      \"Live\": \"TV\",\n      // Internet Explorer 9 and Firefox 36 and earlier identify the zoom toggle button as \"Zoom\". Firefox 37 corrects this to \"ZoomToggle\".\n      \"Zoom\": \"ZoomToggle\",\n      // Internet Explorer 9 and Firefox 36 and earlier use \"SelectMedia\" instead of \"LaunchMediaPlayer\". Firefox 37 through Firefox 48 use \"MediaSelect\". Firefox 49 has been updated to match the latest specification, and to return \"LaunchMediaPlayer\".\n      \"SelectMedia\": \"LaunchMediaPlayer\",\n      \"MediaSelect\": \"LaunchMediaPlayer\",\n      // Google Chrome returns \"LaunchCalculator\" instead of \"LaunchApplication1\". See Chromium bug 612743 for more information.\n      // Google Chrome returns \"LaunchMyComputer\" instead of \"LaunchApplication2\". See Chromium bug 612743 for more information.\n      // (LaunchCalculator and LaunchMyComputer are valid too, so no translation)\n\n      // While older browsers used words like \"Add\", \"Decimal\", \"Multiply\", and so forth modern browsers identify these using the actual character (\"+\", \".\", \"*\", and so forth).\n      \"Multiply\": \"*\",\n      \"Add\": \"+\",\n      \"Divide\": \"/\",\n      \"Subtract\": \"-\",\n      \"Decimal\": \".\", // (mozilla Key_Values doc says depends on region)\n      \"Separator\": \".\", // (mozilla Key_Values doc says depends on region)\n\n      // keyIdenfier spec mapping of non-printable keys: https://www.w3.org/TR/2009/WD-DOM-Level-3-Events-20090908/#keyset-keyidentifiers\n      // Used in safari\n      \"U+0008\": \"Backspace\",\n      \"U+0009\": \"Tab\",\n      \"U+000D\": \"Enter\",\n      \"U+0018\": \"Cancel\",\n      \"U+001B\": \"Escape\",\n      \"U+007F\": \"Delete\",\n      \"U+0300\": \"DeadGrave\",\n      \"U+0301\": \"DeadEacute\",\n      \"U+0302\": \"DeadCircumflex\",\n      \"U+0303\": \"DeadTilde\",\n      \"U+0304\": \"DeadMacron\",\n      \"U+0306\": \"DeadBreve\",\n      \"U+0307\": \"DeadAboveDot\",\n      \"U+0308\": \"DeadUmlaut\",\n      \"U+030A\": \"DeadAboveRing\",\n      \"U+030B\": \"DeadDoubleacute\",\n      \"U+030C\": \"DeadCaron\",\n      \"U+0327\": \"DeadCedilla\",\n      \"U+0328\": \"DeadOgonek\",\n      \"U+0345\": \"DeadIota\",\n      \"U+3099\": \"DeadVoicedSound\",\n      \"U+309A\": \"DeadSemivoicedSound\",\n\n      // keyIdenfier spec mapping to characters: https://www.w3.org/TR/2009/WD-DOM-Level-3-Events-20090908/#keyset-keyidentifiers\n      \"Exclamation\": \"!\",\n      \"DoubleQuote\": \"\\\"\",\n      \"Hash\": \"#\",\n      \"Dollar\": \"$\",\n      \"Ampersand\": \"&\",\n      \"LeftParen\": \"(\",\n      \"RightParen\": \")\",\n      \"Asterisk\": \"*\",\n      \"Plus\": \"+\",\n      \"Percent\": \"%\",\n      \"Comma\": \",\",\n      \"HyphenMinus\": \"-\",\n      \"Period\": \".\",\n      \"Solidus\": \"/\",\n      \"Colon\": \":\",\n      \"Semicolon\": \";\",\n      \"LessThan\": \"<\",\n      \"Equals\": \"=\",\n      \"GreaterThan\": \">\",\n      \"QuestionMark\": \"?\",\n      \"At\": \"@\",\n      \"LeftSquareBracket\": \"[\",\n      \"Backslash\": \"\\\\\",\n      \"RightSquareBracket\": \"]\",\n      \"Circumflex\": \"^\",\n      \"Underscore\": \"_\",\n      \"Grave\": \"`\",\n      \"LeftCurlyBracket\": \"{\",\n      \"Pipe\": \"|\",\n      \"RightCurlyBracket\": \"}\",\n      \"Euro\": \"€\",\n      \"InvertedExclamation\": \"¡\",\n\n      // Safari fixes: viewed in local tests\n      \"U+0010\": \"ContextMenu\"\n    }\n  };\n}\n\nexport type NormalizedKeyboardEvent =\n  {\n    type: string;\n    target: EventTarget | null;\n    key: string;\n    code: string;\n    ctrlKey: boolean;\n    altKey: boolean;\n    location: number;\n    shiftKey: boolean;\n    metaKey: boolean;\n    repeat: boolean;\n    isComposing: boolean;\n  };\n\n/**\n     Returns normalized keyboard event properties, following the current W3C UI Events spec\n *\n    @param evt - Keyboard event\n    @returns Normalized keyboard event data\n */\nexport function normalizeKeyboardEventData(evt: KeyboardEvent): NormalizedKeyboardEvent {\n  // event.key is supported from chrome:51, edge, ff: 29, ie: 9, not in safari\n  // event.keyIdentifier in chrome 26-54, opera 15-41, safara: 5.1\n  // safari doesn't provide either in keypress events, use U+evt.keyCode (uppercase 4-byte hex)\n\n  let key = evt.key;\n  if (!keydata)\n    initKeyMapping();\n  if (keydata.mapping[key])\n    key = keydata.mapping[key];\n  else if (key.startsWith(\"U+\")) // U+xxxx code\n    key = String.fromCodePoint(parseInt(key.substring(2), 16));\n\n  // Seen in chrome 56.0.2924.76 on linux, numpad '.' without numlock returns key \"\\u0000\"\n  if (evt.key === \"\\u0000\" && evt.code === \"NumpadDecimal\")\n    key = \".\";\n\n  return (\n    {\n      type: evt.type,\n      target: evt.target,\n      key: key,\n      code: evt.code || \"Unidentified\",\n      ctrlKey: evt.ctrlKey,\n      altKey: evt.altKey,\n      location: evt.location,\n      shiftKey: evt.shiftKey,\n      metaKey: evt.metaKey,\n      repeat: evt.repeat,\n      isComposing: evt.isComposing\n    });\n}\n\n/**\n * Stop, fully, an event\n *\n * @param event - Event to stop\n */\nexport function stop(event: Event) {\n  event.preventDefault();\n  event.stopImmediatePropagation();\n}\n\n/** Add an event listener to HTMLElements inside a document (which allows us to ensure that 'target' is a HTMLElement for easier typings) */\nexport function addDocEventListener<CurrentTargetType extends HTMLElement, Type extends keyof HTMLElementEventMap>(node: CurrentTargetType, type: Type, listener: (this: CurrentTargetType, ev: DocEvent<HTMLElementEventMap[Type], CurrentTargetType>) => void | Promise<void>, options?: AddEventListenerOptions): void;\nexport function addDocEventListener<CurrentTargetType extends HTMLElement>(node: CurrentTargetType, type: string, listener: (this: CurrentTargetType, evt: DocEvent<Event, CurrentTargetType>) => void | Promise<void>, options?: AddEventListenerOptions): void;\n\nexport function addDocEventListener<CurrentTargetType extends HTMLElement>(node: CurrentTargetType, type: string, listener: (this: CurrentTargetType, evt: DocEvent<Event, CurrentTargetType>) => void | Promise<void>, options?: AddEventListenerOptions): void {\n  node.addEventListener(type, listener as EventListener, options);\n}\n","/* Identify devices for the purpose of analytics/tracing\n   NOT a library for feature detection!\n   Originally based on Mootools.Browser\n*/\n\nexport type Platform = \"windows\" | \"ios\" | \"webos\" | \"android\" | \"linux\" | \"mac\" | \"other\";\nexport type Device = \"desktop\" | \"mobile\" | \"tablet\" | \"\";\n\n/** Valid KeyboardEvent.key values (next to 'plain' keys)\n    This list was built from https://www.w3.org/TR/uievents-key/\n    using `console.log([...new Set([...document.querySelectorAll(\".key-table-key .key\")].map(_ => _.textContent))].toSorted().join(' | '))`\n*/\nexport type KeyAttributeValue = \"AVRInput\" | \"AVRPower\" | \"Accept\" | \"Again\" | \"AllCandidates\" | \"Alphanumeric\" | \"Alt\" | \"AltGraph\" | \"AppSwitch\" | \"ArrowDown\" | \"ArrowLeft\" | \"ArrowRight\" | \"ArrowUp\" | \"Attn\" | \"AudioBalanceLeft\" | \"AudioBalanceRight\" | \"AudioBassBoostDown\" | \"AudioBassBoostToggle\" | \"AudioBassBoostUp\" | \"AudioFaderFront\" | \"AudioFaderRear\" | \"AudioSurroundModeNext\" | \"AudioTrebleDown\" | \"AudioTrebleUp\" | \"AudioVolumeDown\" | \"AudioVolumeMute\" | \"AudioVolumeUp\" | \"Backspace\" | \"BrightnessDown\" | \"BrightnessUp\" | \"BrowserBack\" | \"BrowserFavorites\" | \"BrowserForward\" | \"BrowserHome\" | \"BrowserRefresh\" | \"BrowserSearch\" | \"BrowserStop\" | \"Call\" | \"Camera\" | \"CameraFocus\" | \"Cancel\" | \"CapsLock\" | \"ChannelDown\" | \"ChannelUp\" | \"Clear\" | \"Close\" | \"ClosedCaptionToggle\" | \"CodeInput\" | \"ColorF0Red\" | \"ColorF1Green\" | \"ColorF2Yellow\" | \"ColorF3Blue\" | \"ColorF4Grey\" | \"ColorF5Brown\" | \"Compose\" | \"ContextMenu\" | \"Control\" | \"Convert\" | \"Copy\" | \"CrSel\" | \"Cut\" | \"DVR\" | \"Dead\" | \"Delete\" | \"Dimmer\" | \"DisplaySwap\" | \"Eisu\" | \"Eject\" | \"End\" | \"EndCall\" | \"Enter\" | \"EraseEof\" | \"Escape\" | \"ExSel\" | \"Execute\" | \"Exit\" | \"F1\" | \"F10\" | \"F11\" | \"F12\" | \"F2\" | \"F3\" | \"F4\" | \"F5\" | \"F6\" | \"F7\" | \"F8\" | \"F9\" | \"FavoriteClear0\" | \"FavoriteClear1\" | \"FavoriteClear2\" | \"FavoriteClear3\" | \"FavoriteRecall0\" | \"FavoriteRecall1\" | \"FavoriteRecall2\" | \"FavoriteRecall3\" | \"FavoriteStore0\" | \"FavoriteStore1\" | \"FavoriteStore2\" | \"FavoriteStore3\" | \"FinalMode\" | \"Find\" | \"Fn\" | \"FnLock\" | \"GoBack\" | \"GoHome\" | \"GroupFirst\" | \"GroupLast\" | \"GroupNext\" | \"GroupPrevious\" | \"Guide\" | \"GuideNextDay\" | \"GuidePreviousDay\" | \"HangulMode\" | \"HanjaMode\" | \"Hankaku\" | \"HeadsetHook\" | \"Help\" | \"Hibernate\" | \"Hiragana\" | \"HiraganaKatakana\" | \"Home\" | \"Hyper\" | \"Info\" | \"Insert\" | \"InstantReplay\" | \"JunjaMode\" | \"KanaMode\" | \"KanjiMode\" | \"Katakana\" | \"Key11\" | \"Key12\" | \"LastNumberRedial\" | \"LaunchApplication1\" | \"LaunchApplication2\" | \"LaunchCalendar\" | \"LaunchContacts\" | \"LaunchMail\" | \"LaunchMediaPlayer\" | \"LaunchMusicPlayer\" | \"LaunchPhone\" | \"LaunchScreenSaver\" | \"LaunchSpreadsheet\" | \"LaunchWebBrowser\" | \"LaunchWebCam\" | \"LaunchWordProcessor\" | \"Link\" | \"ListProgram\" | \"LiveContent\" | \"Lock\" | \"LogOff\" | \"MailForward\" | \"MailReply\" | \"MailSend\" | \"MannerMode\" | \"MediaApps\" | \"MediaAudioTrack\" | \"MediaClose\" | \"MediaFastForward\" | \"MediaLast\" | \"MediaNextTrack\" | \"MediaPause\" | \"MediaPlay\" | \"MediaPlayPause\" | \"MediaPreviousTrack\" | \"MediaRecord\" | \"MediaRewind\" | \"MediaSkipBackward\" | \"MediaSkipForward\" | \"MediaStepBackward\" | \"MediaStepForward\" | \"MediaStop\" | \"MediaTopMenu\" | \"MediaTrackNext\" | \"MediaTrackPrevious\" | \"Meta\" | \"MicrophoneToggle\" | \"MicrophoneVolumeDown\" | \"MicrophoneVolumeMute\" | \"MicrophoneVolumeUp\" | \"ModeChange\" | \"NavigateIn\" | \"NavigateNext\" | \"NavigateOut\" | \"NavigatePrevious\" | \"New\" | \"NextCandidate\" | \"NextFavoriteChannel\" | \"NextUserProfile\" | \"NonConvert\" | \"Notification\" | \"NumLock\" | \"OnDemand\" | \"Open\" | \"PageDown\" | \"PageUp\" | \"Pairing\" | \"Paste\" | \"Pause\" | \"PinPDown\" | \"PinPMove\" | \"PinPToggle\" | \"PinPUp\" | \"Play\" | \"PlaySpeedDown\" | \"PlaySpeedReset\" | \"PlaySpeedUp\" | \"Power\" | \"PowerOff\" | \"PreviousCandidate\" | \"Print\" | \"PrintScreen\" | \"Process\" | \"Props\" | \"RandomToggle\" | \"RcLowBattery\" | \"RecordSpeedNext\" | \"Redo\" | \"RfBypass\" | \"Romaji\" | \"STBInput\" | \"STBPower\" | \"Save\" | \"ScanChannelsToggle\" | \"ScreenModeNext\" | \"ScrollLock\" | \"Select\" | \"Settings\" | \"Shift\" | \"SingleCandidate\" | \"Soft1\" | \"Soft2\" | \"Soft3\" | \"Soft4\" | \"SpeechCorrectionList\" | \"SpeechInputToggle\" | \"SpellCheck\" | \"SplitScreenToggle\" | \"Standby\" | \"Subtitle\" | \"Super\" | \"Symbol\" | \"SymbolLock\" | \"TV\" | \"TV3DMode\" | \"TVAntennaCable\" | \"TVAudioDescription\" | \"TVAudioDescriptionMixDown\" | \"TVAudioDescriptionMixUp\" | \"TVContentsMenu\" | \"TVDataService\" | \"TVInput\" | \"TVInputComponent1\" | \"TVInputComponent2\" | \"TVInputComposite1\" | \"TVInputComposite2\" | \"TVInputHDMI1\" | \"TVInputHDMI2\" | \"TVInputHDMI3\" | \"TVInputHDMI4\" | \"TVInputVGA1\" | \"TVMediaContext\" | \"TVNetwork\" | \"TVNumberEntry\" | \"TVPower\" | \"TVRadioService\" | \"TVSatellite\" | \"TVSatelliteBS\" | \"TVSatelliteCS\" | \"TVSatelliteToggle\" | \"TVTerrestrialAnalog\" | \"TVTerrestrialDigital\" | \"TVTimer\" | \"Tab\" | \"Teletext\" | \"Undo\" | \"Unidentified\" | \"VideoModeNext\" | \"VoiceDial\" | \"WakeUp\" | \"Wink\" | \"Zenkaku\" | \"ZenkakuHankaku\" | \"ZoomIn\" | \"ZoomOut\" | \"ZoomToggle\";\n\nexport type UserAgentInfo =\n  {\n    /** Browser name, eg 'chrome' or 'firefox' */\n    name: string;\n    /** Browser numeric version (eg 97) */\n    version: number;\n    /** Platform the browser is running on (eg 'windows') */\n    platform: Platform;\n    /** Type of device (eg 'desktop' or 'tablet' for an iPad) */\n    device: Device;\n    /** platform-browsername-version eg ios-safari-11 */\n    triplet: string;\n  };\n\nexport function parseUserAgent(ua: string): UserAgentInfo {\n  ua = ua.toLowerCase();\n\n  // chrome is included in the edge UA, so need to check for edge first, before checking if it's chrome.\n  // safari is included in the miuibrowser UA, so need to check for miuibrowser first, before checking if it's safari.\n  let UA: RegExpMatchArray | null = ua.match(/(edge|miuibrowser)[\\s/:]([\\w\\d.]+)/);\n  if (!UA)\n    UA = ua.match(/(opera|ie|firefox|chrome|trident|crios|version)[\\s/:]([\\w\\d.]+)?.*?(safari|(?:rv[\\s/:]|version[\\s/:])([\\w\\d.]+)|$)/);\n  if (!UA) { //try ios 11.4.1\n    UA = ua.match(/; cpu os ([\\d]+)/);\n    if (UA)\n      UA = ['', 'safari', UA[1]];\n  }\n  if (!UA)\n    UA = ['', 'unknown', \"0\"];\n\n  if (UA[1] === 'trident') {\n    UA[1] = 'ie';\n    if (UA[4]) UA[2] = UA[4];\n  } else if (UA[1] === 'crios') {\n    UA[1] = 'chrome';\n  }\n\n  let platform = ua.match(/ip(?:ad|od|hone)/) ? 'ios' : (ua.match(/(?:webos|android)/) || ua.match(/mac|win|linux/) || ['other'])[0];\n  if (platform === 'win')\n    platform = 'windows';\n\n  const name = (UA[1] === 'version') ? UA[3] : UA[1];\n  const version = parseInt((UA[1] === 'opera' && UA[4]) ? UA[4] : UA[2]);\n  const device = ua.match(/ipad/) ? 'tablet' : ['ios', 'webos', 'android'].includes(platform) ? 'mobile' : ['mac', 'windows', 'linux'].includes(platform) ? 'desktop' : '';\n\n  return {\n    name,\n    version,\n    platform: platform as Platform,\n    device,\n    triplet: platform + '-' + name + '-' + version\n  };\n}\n\n/**\n * Is the native 'multiselect' modifier for this platform pressed? (Cmd for Mac, Ctrl for Windows/Linux)\n *\n * @param event - Event to check\n */\nexport function isMultiSelectKey(event: KeyboardEvent | MouseEvent): boolean {\n  return browser.platform === 'mac' ? event.metaKey : event.ctrlKey;\n}\n\n/**\n * Is the native 'copy' modifier for this platform pressed?\n *\n * @param event - Event to check\n */\nexport function isCopyKey(event: KeyboardEvent | MouseEvent): boolean {\n  return browser.platform === 'mac' ? event.altKey : event.ctrlKey;\n}\n\n/** @deprecated Use getBrowser() instead, available since WH5.6.3. We will remove `browser` in the future to improve tree shaking */\nexport const browser: Readonly<UserAgentInfo> = Object.freeze(parseUserAgent(globalThis.navigator?.userAgent || \"\"));\n\n/** Get browser information */\nexport function getBrowser(): Readonly<UserAgentInfo> {\n  //Offer this as a function to improve treeshakability\n  return browser;\n}\n\n//CSS Query used to find valid submittors\nexport const submitselector = 'input[type=submit],input[type=image],button[type=submit],button:not([type])';\n//Must match possible types returned by the submitselector\nexport type SubmitSelectorType = HTMLInputElement | HTMLButtonElement;\n","import { isHTMLElement } from \"@webhare/dompack\";\nimport type { CMSConfig } from \"@mod-system/js/internal/generation/gen_misc_ts\";\n\nlet config: CMSConfig | undefined;\n\nfunction forwardPublisherNavigation(event: Event) {\n  /* we watch for clicks.. as we only want to update the Publisher's selection for user triggered actions (otherwise we'd\n     start navigation in response to JS or internal link autoredirects) */\n  if (!isHTMLElement(event.target) || !config?.interfaceServers)\n    return;\n\n  const navaction = event.target.closest<HTMLAnchorElement>('a[href]');\n  if (!navaction)\n    return;\n\n  const desturl = navaction.href;\n  if (desturl.split('#')[0] !== location.href.split('#')[0]) { //it's actual navigation...\n    for (const origin of config.interfaceServers) {\n      try {\n        window.top?.postMessage({ type: \"webhare-navigation\", location: desturl }, origin);\n      } catch (ignore) {\n        //ignore crossdomain errors\n      }\n    }\n  }\n}\n\nasync function configureForCMS() {\n  try {\n    const res = await fetch(\"/.wh/ea/config/cms.json\");\n    config = await res.json();\n  } catch (e) {\n    console.log(\"Failed to get CMS configuration\", e);\n    return;\n  }\n  window.addEventListener(\"click\", forwardPublisherNavigation);\n}\n\nif (typeof window !== \"undefined\" && window !== window.top) { //if we're in an iframe, we may be running in WebHare and need to forward navigation events\n  //we have no way to detect whether we're in a WebHare iframe or a 3rd party iframe. assume it's webhare and grab the interface servers\n  void configureForCMS();\n}\n","import \"@webhare/frontend/src/init\";import \"/opt/wh/whtree/modules/publisher/js/internal/polyfills/all\";\nimport \"/opt/whdata/installedmodules/ww.20261101T193307.399Z/js/feedback/widget/widget.ts\";const aps = globalThis.whAssetPacks ||= {}; aps[\"ww:feedback\"]=\"2026-11-01T19:33:26.814Z\";"],"mappings":"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","names":["css","init_styles","__esmMin","selectionRect","start","end","bounds","x","y","right","bottom","bitmapRect","rect","display","bitmap","selection","init_selection_geometry","__esmMin","selectScreenshot","screenshot","root","signal","isJpegDataUrl","resolve","reject","dialog","surface","image","box","hint","nextSelectionId","toolbar","cancel","use","events","settled","selection","start","pointerId","finish","result","error","listen","render","point","event","bounds","update","selectionRect","released","direction","rect","dx","dy","value","crop","bitmapRect","canvas","context","style","paddingX","paddingY","width","height","scale","init_selection","__esmMin","init_capture","init_selection_geometry","require_widget","__commonJSMin","init_styles","init_capture","init_selection","MAX_PAYLOAD_CHARS","PRIORITIES","el","tag","props","children","node","nextPaint","resolve","currentPageKey","start","script","siteKey","apiBase","api","endpoint","params","reporterOverride","enabled","pageUrlOverride","enableGeneration","call","response","fallback","result","body","config","e","style","css","root","attribute","property","kind","value","launcherRoot","count","launcher","dialog","closeButton","formError","descriptionSlot","expectedSlot","description","expected","editorsReady","descriptionError","showDescriptionError","show","ensureEditors","createEditorField","totalImages","onImageError","message","error","priority","label","priorityField","shotState","shotImage","selectArea","recapture","shot","shotField","success","submitButton","cancelButton","fields","form","historyList","historyStatus","historyMore","historyRetry","historySection","moveLauncher","appDialog","parent","sessions","session","shown","capturing","selectionAbort","saveEditors","render","locked","editor","doc","capture","captured","generation","captureScreenshot","selectRegion","controller","screenshot","selectScreenshot","open","key","existing","reporter","found","clean","k","v","submit","submitted","images","descriptionText","expectedText","isJpegDataUrl","MAX_IMAGES","payload","loadHistory","historyGeneration","historyItems","historyCursor","renderHistoryItem","item","status","reset","event","lastPage","page","user","url","settingschangedcallbacks","globalDebugFlags","localDebugFlags","debugFlagsOverridesCB","DebugFlagsProxyHandler","debugFlagsOverridesCB","localDebugFlags","globalDebugFlags","target","p","record","keys","key","newValue","toModify","runSettingsCallbacks","value","debugFlags","formatForConsoleLogs","cb","settingschangedcallbacks","e","dtapStage","isLive","backendBase","dtapstage","dtapStage","islive","isLive","initEnv","setDtapStage","setBackendBase","dtapStage","isLive","backendBase","dtapstage","islive","getBrowserDebugFlags","varname","flags","urldebugvar","debugcookie","debugcookievalue","flag","sessionDebug","isElement","node","isHTMLElement","parseLanguageTag","languageTag","match","getLang","node","tag","omit","value","keys","elt","ret","key","val","unlock","LocalMutex","defer","LocalLock","mutex","config","siteroot","dtapStage","whconfigel","parsedConfig","omit","initEnv","flag","getBrowserDebugFlags","debugFlags","frontendConfig","getLang","prop","resolve","reject","_resolve","_reject","items","callbackfn","retval","idx","item","key","list","IteratorPrototype","ArrayIteratorPrototype","OriginalIteratorPrototype","callback","it","value","next_value","real_value","max_count","values","limit","remaining","index","mapper","mapped","reducer","initial_value","acc","count","string","final","first","iterables","others","it_array","e","finished","partition1","partition2","i","max","min","isObject","t","GetIteratorFlattenable","obj","method","iterator","Iterator","WrappedIterator","#iterator","args","returnMethod","Iterator_from","debugFlags","CustomEvent","parseUserAgent","ua","UA","platform","name","version","device","browser","parseUserAgent","config","forwardPublisherNavigation","event","isHTMLElement","navaction","desturl","origin","configureForCMS","e","import_widget","aps"]}