From 93d36633481ee3f12699dec9d357a32887db9c17 Mon Sep 17 00:00:00 2001
From: Jaggob <37583151+Jaggob@users.noreply.github.com>
Date: Sat, 6 Jun 2026 23:49:42 +0200
Subject: [PATCH 1/2] security: client-side sanitize snapshot HTML (#110)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Snapshot HTML stored in .pad files is injected into the viewer and embed via
innerHTML. It is already sanitized server-side by SnapshotHtmlSanitizer, but
that is the sole XSS gate and .pad bodies are attacker-writable (WebDAV /
malicious share). Add a client-side defense-in-depth pass so a regression in
the server gate can't become stored XSS.
- New src/lib/sanitize-html.js: sanitizeSnapshotHtml() runs DOMPurify with the
same allowlist the server enforces (formatting tags only, no attributes),
mirroring SnapshotHtmlSanitizer::ALLOWED_TAGS.
- Apply it at both innerHTML sinks: embed-main.js snapshot preview and
viewer-main.js renderSnapshotView.
- Tests: tests/js/lib/sanitize-html.test.js (pinned to jsdom — happy-dom
mis-sanitizes with DOMPurify) covers script/onclick/img/class stripping,
disallowed-tag unwrapping, and allowed-tag passthrough.
- Deps: dompurify (runtime, shared chunk ~11 kB gzip across viewer+embed),
jsdom (dev, for the sanitizer test env).
vitest 123 green; full Playwright 23/23 on NC 33 (snapshot rendering
unaffected).
Closes #110.
---
js/etherpad_nextcloud-embed-main.mjs | 2 +-
js/etherpad_nextcloud-embed-main.mjs.map | 2 +-
js/etherpad_nextcloud-viewer-main.mjs | 2 +-
js/etherpad_nextcloud-viewer-main.mjs.map | 2 +-
js/pad-sync-B0gz-oBk.chunk.mjs | 2 -
js/pad-sync-B0gz-oBk.chunk.mjs.map | 1 -
js/sanitize-html-dv-YifbT.chunk.mjs | 4 +
... sanitize-html-dv-YifbT.chunk.mjs.license} | 5 +
js/sanitize-html-dv-YifbT.chunk.mjs.map | 1 +
package-lock.json | 552 ++++++++++++++++++
package.json | 4 +
src/embed-main.js | 3 +-
src/lib/sanitize-html.js | 39 ++
src/viewer-main.js | 3 +-
tests/js/lib/sanitize-html.test.js | 47 ++
15 files changed, 660 insertions(+), 9 deletions(-)
delete mode 100644 js/pad-sync-B0gz-oBk.chunk.mjs
delete mode 100644 js/pad-sync-B0gz-oBk.chunk.mjs.map
create mode 100644 js/sanitize-html-dv-YifbT.chunk.mjs
rename js/{pad-sync-B0gz-oBk.chunk.mjs.license => sanitize-html-dv-YifbT.chunk.mjs.license} (53%)
create mode 100644 js/sanitize-html-dv-YifbT.chunk.mjs.map
create mode 100644 src/lib/sanitize-html.js
create mode 100644 tests/js/lib/sanitize-html.test.js
diff --git a/js/etherpad_nextcloud-embed-main.mjs b/js/etherpad_nextcloud-embed-main.mjs
index 13702f3..782df57 100644
--- a/js/etherpad_nextcloud-embed-main.mjs
+++ b/js/etherpad_nextcloud-embed-main.mjs
@@ -1,2 +1,2 @@
-import{o as se}from"./oc-compat-hVqZy-MX.chunk.mjs";import{c as ce}from"./pad-sync-B0gz-oBk.chunk.mjs";import{f as T}from"./fetch-helpers-C4MxuNvt.chunk.mjs";(function(){const r=document.getElementById("etherpad-nextcloud-embed");if(!(r instanceof HTMLElement))return;const u=Number(r.getAttribute("data-file-id")||""),w=String(r.getAttribute("data-open-by-id-url")||"").trim(),A=String(r.getAttribute("data-initialize-by-id-url-template")||"").trim(),I=String(r.getAttribute("data-recover-url-template")||"").trim(),N=String(r.getAttribute("data-find-original-url-template")||"").trim(),D=String(r.getAttribute("data-request-token")||"").trim(),B=String(r.getAttribute("data-trusted-origins")||"").split(/\s+/).map(e=>e.trim()).filter(Boolean),i=r.querySelector("[data-epnc-embed-loading]"),d=r.querySelector("[data-epnc-embed-error]"),q=r.querySelector("[data-epnc-embed-error-message]"),l=r.querySelector("[data-epnc-embed-recovery]"),m=r.querySelector("[data-epnc-embed-recovery-message]"),h=r.querySelector("[data-epnc-embed-recovery-body]"),p=r.querySelector("[data-epnc-embed-recovery-actions]"),o=r.querySelector("[data-epnc-embed-iframe]"),z=String(r.getAttribute("data-l10n-external-title")||"Pad from another server").trim(),G=String(r.getAttribute("data-l10n-external-message")||"Read-only snapshot from the .pad file.").trim(),Y=String(r.getAttribute("data-l10n-external-empty")||"No synced snapshot is stored in this .pad file yet.").trim(),J=String(r.getAttribute("data-l10n-external-link")||"Open original pad").trim(),K=String(r.getAttribute("data-l10n-recovery-checking")||"Checking for the original pad...").trim(),Q=String(r.getAttribute("data-l10n-recovery-copy-body")||"").trim(),V=String(r.getAttribute("data-l10n-recovery-orphan-body")||"").trim(),W=String(r.getAttribute("data-l10n-recovery-open-original")||"Open the original .pad file").trim(),k=String(r.getAttribute("data-l10n-recovery-create-new")||"Create new pad from this file").trim(),X=String(r.getAttribute("data-l10n-recovery-creating")||"Creating new pad...").trim();let C=null;const y=()=>se(D),f=ce({requestToken:y}),_=e=>{i instanceof HTMLElement&&(i.hidden=!0,i.classList.remove("epnc-embed__loading--snapshot")),o instanceof HTMLIFrameElement&&(o.hidden=!0,o.removeAttribute("src")),q instanceof HTMLElement&&(q.textContent=String(e||"Unknown error.")),d instanceof HTMLElement&&(d.hidden=!1)},Z=(e,t,n)=>{if(d instanceof HTMLElement&&(d.hidden=!0),o instanceof HTMLIFrameElement&&(o.hidden=!0,o.removeAttribute("src")),!(i instanceof HTMLElement))return;i.hidden=!1,i.classList.add("epnc-embed__loading--snapshot"),i.textContent="";const a=document.createElement("div");a.className="epnc-embed__snapshot";const s=document.createElement("div");s.className="epnc-embed__snapshot-inner";const g=document.createElement("h2");g.className="epnc-embed__snapshot-title",g.textContent=z;const c=document.createElement("p");c.className="epnc-embed__snapshot-message",c.textContent=G;const b=document.createElement("a");b.className="epnc-embed__snapshot-link",b.href=e,b.target="_blank",b.rel="noopener noreferrer",b.textContent=J;const H=document.createElement("div");H.className="epnc-embed__snapshot-actions",H.appendChild(b);const x=String(n||"").trim()!=="",L=document.createElement(x?"div":"pre");L.className=x?"epnc-embed__snapshot-text epnc-embed__snapshot-text--html":"epnc-embed__snapshot-text",x?L.innerHTML=String(n):L.textContent=String(t||"").trim()!==""?String(t):Y;const S=document.createElement("div");S.className="epnc-embed__snapshot-heading",S.appendChild(g),S.appendChild(c);const v=document.createElement("div");v.className="epnc-embed__snapshot-header",v.appendChild(S),v.appendChild(H),s.appendChild(v),s.appendChild(L),a.appendChild(s),i.appendChild(a)},$=e=>{if(!(o instanceof HTMLIFrameElement)){_("Embed iframe is not available.");return}d instanceof HTMLElement&&(d.hidden=!0),i instanceof HTMLElement&&i.classList.remove("epnc-embed__loading--snapshot"),o.hidden=!0;const t=()=>{o.removeEventListener("load",t),window.setTimeout(()=>{i instanceof HTMLElement&&(i.hidden=!0),o.hidden=!1},100)};o.addEventListener("load",t,{once:!0}),o.src=e},M=(e,t,n,a={})=>{!e||typeof e.postMessage!="function"||e.postMessage(Object.assign({type:n,fileId:u},a),t)},ee=e=>!e||e==="null"?!1:e===window.location.origin?!0:B.includes(e),te=()=>{C||(C=e=>{const t=String(e.origin||"");if(!ee(t))return;const n=e.data,a=typeof n=="string"?n:n&&typeof n=="object"&&typeof n.type=="string"?n.type:"";if(a){if(a==="epnc:host-visible"){f.start();return}if(a==="epnc:host-hidden"){f.fireAndForget(!0,!0),f.stop();return}if(a==="epnc:host-before-close"||a==="epnc:host-sync-now"){const s=a!=="epnc:host-sync-now",g=a==="epnc:host-before-close"?"before-close":"sync-now";M(e.source,t,"epnc:sync-flush-started",{reason:g}),f.sync(!0,s).then(c=>{M(e.source,t,"epnc:sync-flush-finished",{reason:g,result:c&&typeof c=="object"?c:{}})}).catch(c=>{M(e.source,t,"epnc:sync-flush-failed",{reason:g,message:c instanceof Error?c.message:"Sync failed."})}),s&&f.stop()}}},window.addEventListener("message",C))},ne=e=>e instanceof Error?String(e.message||"").includes("Missing YAML frontmatter"):!1,F=async()=>{const e=new URLSearchParams;e.set("fileId",String(u));const t=await T(w,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded;charset=UTF-8",requesttoken:y()},body:e.toString()});if(!t||typeof t.url!="string"||t.url.trim()==="")throw new Error("Pad open API did not return a valid URL.");return t},re=async()=>{const e=A.replace("__FILE_ID__",encodeURIComponent(String(u))),t=await T(e,{method:"POST",headers:{requesttoken:y()}});t&&t.status==="migrated_from_legacy"&&console.info("Legacy Ownpad .pad migrated to managed format on first open.")},E=()=>{i instanceof HTMLElement&&(i.hidden=!0),d instanceof HTMLElement&&(d.hidden=!0),l instanceof HTMLElement&&(l.hidden=!0),o instanceof HTMLIFrameElement&&(o.hidden=!0,o.removeAttribute("src"))},ae=()=>{l instanceof HTMLElement&&(E(),l.hidden=!1,m instanceof HTMLElement&&(m.textContent=K),h instanceof HTMLElement&&(h.textContent=""),p instanceof HTMLElement&&p.replaceChildren())},P=(e,t)=>{const n=document.createElement("button");return n.type="button",n.className="epnc-embed__recovery-button",n.textContent=e,n.addEventListener("click",t),n},ie=(e,t)=>{if(l instanceof HTMLElement&&(E(),l.hidden=!1,m instanceof HTMLElement&&(m.textContent=t),h instanceof HTMLElement&&(h.textContent=Q),p instanceof HTMLElement)){const n=document.createElement("a");n.className="epnc-embed__recovery-button epnc-embed__recovery-button--primary",n.href=e,n.textContent=W,p.replaceChildren(n,P(k,()=>{O()}))}},R=e=>{if(l instanceof HTMLElement&&(E(),l.hidden=!1,m instanceof HTMLElement&&(m.textContent=e),h instanceof HTMLElement&&(h.textContent=V),p instanceof HTMLElement)){const t=P(k,()=>{O()});t.classList.add("epnc-embed__recovery-button--primary"),p.replaceChildren(t)}},U=e=>{p instanceof HTMLElement&&p.querySelectorAll("button").forEach(t=>{t.disabled=e,e?(t.dataset.originalLabel=t.dataset.originalLabel||t.textContent||"",t.textContent=X):t.dataset.originalLabel&&(t.textContent=t.dataset.originalLabel,delete t.dataset.originalLabel)})},O=async()=>{if(I===""){_("Recovery is not available in this embed.");return}U(!0);const e=I.replace("__FILE_ID__",encodeURIComponent(String(u)));try{await T(e,{method:"POST",headers:{requesttoken:y()}}),E(),i instanceof HTMLElement&&(i.hidden=!1),j()}catch(t){U(!1),m instanceof HTMLElement&&(m.textContent=t instanceof Error&&t.message?t.message:"Recovery failed.")}},oe=async e=>{const t=e instanceof Error&&e.message?e.message:"Pad open failed.";if(ae(),N===""){R(t);return}const n=N.replace("__FILE_ID__",encodeURIComponent(String(u)));try{const a=await T(n,{method:"GET"});if(a&&a.found===!0&&typeof a.embed_url=="string"&&a.embed_url!==""){ie(a.embed_url,t);return}}catch{}R(t)},j=async()=>{if(!Number.isFinite(u)||u<=0||w===""||A===""){_("Embed configuration is incomplete.");return}if(y()===""){_("CSRF request token is missing.");return}try{let e;try{e=await F()}catch(s){if(!ne(s))throw s;await re(),e=await F()}const t=typeof e.sync_url=="string"?e.sync_url.trim():"",n=Number(e.sync_interval_seconds??0),a=Number.isFinite(n)&&n>0?n*1e3:12e4;if(f.configure({syncUrl:t,intervalMs:a}),f.installLifecycleHandlers(),te(),f.start(),e.is_external===!0){Z(e.url,typeof e.snapshot_text=="string"?e.snapshot_text:"",typeof e.snapshot_html=="string"?e.snapshot_html:"");return}$(e.url)}catch(e){if(e&&e.code==="missing_binding"){oe(e);return}_(e instanceof Error?e.message:"Pad open failed.")}};j()})();
+import{o as se}from"./oc-compat-hVqZy-MX.chunk.mjs";import{c as ce,s as de}from"./sanitize-html-dv-YifbT.chunk.mjs";import{f as T}from"./fetch-helpers-C4MxuNvt.chunk.mjs";(function(){const r=document.getElementById("etherpad-nextcloud-embed");if(!(r instanceof HTMLElement))return;const u=Number(r.getAttribute("data-file-id")||""),w=String(r.getAttribute("data-open-by-id-url")||"").trim(),A=String(r.getAttribute("data-initialize-by-id-url-template")||"").trim(),I=String(r.getAttribute("data-recover-url-template")||"").trim(),N=String(r.getAttribute("data-find-original-url-template")||"").trim(),D=String(r.getAttribute("data-request-token")||"").trim(),B=String(r.getAttribute("data-trusted-origins")||"").split(/\s+/).map(e=>e.trim()).filter(Boolean),i=r.querySelector("[data-epnc-embed-loading]"),d=r.querySelector("[data-epnc-embed-error]"),q=r.querySelector("[data-epnc-embed-error-message]"),l=r.querySelector("[data-epnc-embed-recovery]"),m=r.querySelector("[data-epnc-embed-recovery-message]"),h=r.querySelector("[data-epnc-embed-recovery-body]"),p=r.querySelector("[data-epnc-embed-recovery-actions]"),o=r.querySelector("[data-epnc-embed-iframe]"),z=String(r.getAttribute("data-l10n-external-title")||"Pad from another server").trim(),G=String(r.getAttribute("data-l10n-external-message")||"Read-only snapshot from the .pad file.").trim(),Y=String(r.getAttribute("data-l10n-external-empty")||"No synced snapshot is stored in this .pad file yet.").trim(),J=String(r.getAttribute("data-l10n-external-link")||"Open original pad").trim(),K=String(r.getAttribute("data-l10n-recovery-checking")||"Checking for the original pad...").trim(),Q=String(r.getAttribute("data-l10n-recovery-copy-body")||"").trim(),V=String(r.getAttribute("data-l10n-recovery-orphan-body")||"").trim(),W=String(r.getAttribute("data-l10n-recovery-open-original")||"Open the original .pad file").trim(),k=String(r.getAttribute("data-l10n-recovery-create-new")||"Create new pad from this file").trim(),X=String(r.getAttribute("data-l10n-recovery-creating")||"Creating new pad...").trim();let C=null;const y=()=>se(D),f=ce({requestToken:y}),_=e=>{i instanceof HTMLElement&&(i.hidden=!0,i.classList.remove("epnc-embed__loading--snapshot")),o instanceof HTMLIFrameElement&&(o.hidden=!0,o.removeAttribute("src")),q instanceof HTMLElement&&(q.textContent=String(e||"Unknown error.")),d instanceof HTMLElement&&(d.hidden=!1)},Z=(e,t,n)=>{if(d instanceof HTMLElement&&(d.hidden=!0),o instanceof HTMLIFrameElement&&(o.hidden=!0,o.removeAttribute("src")),!(i instanceof HTMLElement))return;i.hidden=!1,i.classList.add("epnc-embed__loading--snapshot"),i.textContent="";const a=document.createElement("div");a.className="epnc-embed__snapshot";const s=document.createElement("div");s.className="epnc-embed__snapshot-inner";const g=document.createElement("h2");g.className="epnc-embed__snapshot-title",g.textContent=z;const c=document.createElement("p");c.className="epnc-embed__snapshot-message",c.textContent=G;const b=document.createElement("a");b.className="epnc-embed__snapshot-link",b.href=e,b.target="_blank",b.rel="noopener noreferrer",b.textContent=J;const H=document.createElement("div");H.className="epnc-embed__snapshot-actions",H.appendChild(b);const x=String(n||"").trim()!=="",L=document.createElement(x?"div":"pre");L.className=x?"epnc-embed__snapshot-text epnc-embed__snapshot-text--html":"epnc-embed__snapshot-text",x?L.innerHTML=de(n):L.textContent=String(t||"").trim()!==""?String(t):Y;const S=document.createElement("div");S.className="epnc-embed__snapshot-heading",S.appendChild(g),S.appendChild(c);const v=document.createElement("div");v.className="epnc-embed__snapshot-header",v.appendChild(S),v.appendChild(H),s.appendChild(v),s.appendChild(L),a.appendChild(s),i.appendChild(a)},$=e=>{if(!(o instanceof HTMLIFrameElement)){_("Embed iframe is not available.");return}d instanceof HTMLElement&&(d.hidden=!0),i instanceof HTMLElement&&i.classList.remove("epnc-embed__loading--snapshot"),o.hidden=!0;const t=()=>{o.removeEventListener("load",t),window.setTimeout(()=>{i instanceof HTMLElement&&(i.hidden=!0),o.hidden=!1},100)};o.addEventListener("load",t,{once:!0}),o.src=e},M=(e,t,n,a={})=>{!e||typeof e.postMessage!="function"||e.postMessage(Object.assign({type:n,fileId:u},a),t)},ee=e=>!e||e==="null"?!1:e===window.location.origin?!0:B.includes(e),te=()=>{C||(C=e=>{const t=String(e.origin||"");if(!ee(t))return;const n=e.data,a=typeof n=="string"?n:n&&typeof n=="object"&&typeof n.type=="string"?n.type:"";if(a){if(a==="epnc:host-visible"){f.start();return}if(a==="epnc:host-hidden"){f.fireAndForget(!0,!0),f.stop();return}if(a==="epnc:host-before-close"||a==="epnc:host-sync-now"){const s=a!=="epnc:host-sync-now",g=a==="epnc:host-before-close"?"before-close":"sync-now";M(e.source,t,"epnc:sync-flush-started",{reason:g}),f.sync(!0,s).then(c=>{M(e.source,t,"epnc:sync-flush-finished",{reason:g,result:c&&typeof c=="object"?c:{}})}).catch(c=>{M(e.source,t,"epnc:sync-flush-failed",{reason:g,message:c instanceof Error?c.message:"Sync failed."})}),s&&f.stop()}}},window.addEventListener("message",C))},ne=e=>e instanceof Error?String(e.message||"").includes("Missing YAML frontmatter"):!1,F=async()=>{const e=new URLSearchParams;e.set("fileId",String(u));const t=await T(w,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded;charset=UTF-8",requesttoken:y()},body:e.toString()});if(!t||typeof t.url!="string"||t.url.trim()==="")throw new Error("Pad open API did not return a valid URL.");return t},re=async()=>{const e=A.replace("__FILE_ID__",encodeURIComponent(String(u))),t=await T(e,{method:"POST",headers:{requesttoken:y()}});t&&t.status==="migrated_from_legacy"&&console.info("Legacy Ownpad .pad migrated to managed format on first open.")},E=()=>{i instanceof HTMLElement&&(i.hidden=!0),d instanceof HTMLElement&&(d.hidden=!0),l instanceof HTMLElement&&(l.hidden=!0),o instanceof HTMLIFrameElement&&(o.hidden=!0,o.removeAttribute("src"))},ae=()=>{l instanceof HTMLElement&&(E(),l.hidden=!1,m instanceof HTMLElement&&(m.textContent=K),h instanceof HTMLElement&&(h.textContent=""),p instanceof HTMLElement&&p.replaceChildren())},P=(e,t)=>{const n=document.createElement("button");return n.type="button",n.className="epnc-embed__recovery-button",n.textContent=e,n.addEventListener("click",t),n},ie=(e,t)=>{if(l instanceof HTMLElement&&(E(),l.hidden=!1,m instanceof HTMLElement&&(m.textContent=t),h instanceof HTMLElement&&(h.textContent=Q),p instanceof HTMLElement)){const n=document.createElement("a");n.className="epnc-embed__recovery-button epnc-embed__recovery-button--primary",n.href=e,n.textContent=W,p.replaceChildren(n,P(k,()=>{O()}))}},R=e=>{if(l instanceof HTMLElement&&(E(),l.hidden=!1,m instanceof HTMLElement&&(m.textContent=e),h instanceof HTMLElement&&(h.textContent=V),p instanceof HTMLElement)){const t=P(k,()=>{O()});t.classList.add("epnc-embed__recovery-button--primary"),p.replaceChildren(t)}},U=e=>{p instanceof HTMLElement&&p.querySelectorAll("button").forEach(t=>{t.disabled=e,e?(t.dataset.originalLabel=t.dataset.originalLabel||t.textContent||"",t.textContent=X):t.dataset.originalLabel&&(t.textContent=t.dataset.originalLabel,delete t.dataset.originalLabel)})},O=async()=>{if(I===""){_("Recovery is not available in this embed.");return}U(!0);const e=I.replace("__FILE_ID__",encodeURIComponent(String(u)));try{await T(e,{method:"POST",headers:{requesttoken:y()}}),E(),i instanceof HTMLElement&&(i.hidden=!1),j()}catch(t){U(!1),m instanceof HTMLElement&&(m.textContent=t instanceof Error&&t.message?t.message:"Recovery failed.")}},oe=async e=>{const t=e instanceof Error&&e.message?e.message:"Pad open failed.";if(ae(),N===""){R(t);return}const n=N.replace("__FILE_ID__",encodeURIComponent(String(u)));try{const a=await T(n,{method:"GET"});if(a&&a.found===!0&&typeof a.embed_url=="string"&&a.embed_url!==""){ie(a.embed_url,t);return}}catch{}R(t)},j=async()=>{if(!Number.isFinite(u)||u<=0||w===""||A===""){_("Embed configuration is incomplete.");return}if(y()===""){_("CSRF request token is missing.");return}try{let e;try{e=await F()}catch(s){if(!ne(s))throw s;await re(),e=await F()}const t=typeof e.sync_url=="string"?e.sync_url.trim():"",n=Number(e.sync_interval_seconds??0),a=Number.isFinite(n)&&n>0?n*1e3:12e4;if(f.configure({syncUrl:t,intervalMs:a}),f.installLifecycleHandlers(),te(),f.start(),e.is_external===!0){Z(e.url,typeof e.snapshot_text=="string"?e.snapshot_text:"",typeof e.snapshot_html=="string"?e.snapshot_html:"");return}$(e.url)}catch(e){if(e&&e.code==="missing_binding"){oe(e);return}_(e instanceof Error?e.message:"Pad open failed.")}};j()})();
//# sourceMappingURL=etherpad_nextcloud-embed-main.mjs.map
diff --git a/js/etherpad_nextcloud-embed-main.mjs.map b/js/etherpad_nextcloud-embed-main.mjs.map
index eef4075..2626a22 100644
--- a/js/etherpad_nextcloud-embed-main.mjs.map
+++ b/js/etherpad_nextcloud-embed-main.mjs.map
@@ -1 +1 @@
-{"version":3,"file":"etherpad_nextcloud-embed-main.mjs","sources":["../src/embed-main.js"],"sourcesContent":["/**\n * SPDX-License-Identifier: AGPL-3.0-or-later\n * Copyright (c) 2026 Jacob Bühler\n */\nimport { ocRequestToken } from './lib/oc-compat.js'\nimport { createPadSync } from './lib/pad-sync.js'\nimport { fetchJsonWithTimeout as fetchJson } from './lib/fetch-helpers.js'\n\n(function () {\n\tconst IFRAME_REVEAL_DELAY_MS = 100\n\n\tconst root = document.getElementById('etherpad-nextcloud-embed')\n\tif (!(root instanceof HTMLElement)) {\n\t\treturn\n\t}\n\n\tconst fileId = Number(root.getAttribute('data-file-id') || '')\n\tconst openByIdUrl = String(root.getAttribute('data-open-by-id-url') || '').trim()\n\tconst initializeByIdUrlTemplate = String(root.getAttribute('data-initialize-by-id-url-template') || '').trim()\n\tconst recoverUrlTemplate = String(root.getAttribute('data-recover-url-template') || '').trim()\n\tconst findOriginalUrlTemplate = String(root.getAttribute('data-find-original-url-template') || '').trim()\n\tconst templateRequestToken = String(root.getAttribute('data-request-token') || '').trim()\n\tconst trustedOrigins = String(root.getAttribute('data-trusted-origins') || '')\n\t\t.split(/\\s+/)\n\t\t.map((value) => value.trim())\n\t\t.filter(Boolean)\n\tconst loadingNode = root.querySelector('[data-epnc-embed-loading]')\n\tconst errorNode = root.querySelector('[data-epnc-embed-error]')\n\tconst errorMessageNode = root.querySelector('[data-epnc-embed-error-message]')\n\tconst recoveryNode = root.querySelector('[data-epnc-embed-recovery]')\n\tconst recoveryMessageNode = root.querySelector('[data-epnc-embed-recovery-message]')\n\tconst recoveryBodyNode = root.querySelector('[data-epnc-embed-recovery-body]')\n\tconst recoveryActionsNode = root.querySelector('[data-epnc-embed-recovery-actions]')\n\tconst iframe = root.querySelector('[data-epnc-embed-iframe]')\n\tconst externalTitleText = String(root.getAttribute('data-l10n-external-title') || 'Pad from another server').trim()\n\tconst externalMessageText = String(root.getAttribute('data-l10n-external-message') || 'Read-only snapshot from the .pad file.').trim()\n\tconst externalEmptyText = String(root.getAttribute('data-l10n-external-empty') || 'No synced snapshot is stored in this .pad file yet.').trim()\n\tconst externalLinkText = String(root.getAttribute('data-l10n-external-link') || 'Open original pad').trim()\n\tconst recoveryCheckingText = String(root.getAttribute('data-l10n-recovery-checking') || 'Checking for the original pad...').trim()\n\tconst recoveryCopyBodyText = String(root.getAttribute('data-l10n-recovery-copy-body') || '').trim()\n\tconst recoveryOrphanBodyText = String(root.getAttribute('data-l10n-recovery-orphan-body') || '').trim()\n\tconst recoveryOpenOriginalText = String(root.getAttribute('data-l10n-recovery-open-original') || 'Open the original .pad file').trim()\n\tconst recoveryCreateNewText = String(root.getAttribute('data-l10n-recovery-create-new') || 'Create new pad from this file').trim()\n\tconst recoveryCreatingText = String(root.getAttribute('data-l10n-recovery-creating') || 'Creating new pad...').trim()\n\tlet messageHandler = null\n\n\tconst requestToken = () => ocRequestToken(templateRequestToken)\n\tconst padSync = createPadSync({ requestToken })\n\n\tconst showError = (message) => {\n\t\tif (loadingNode instanceof HTMLElement) {\n\t\t\tloadingNode.hidden = true\n\t\t\tloadingNode.classList.remove('epnc-embed__loading--snapshot')\n\t\t}\n\t\tif (iframe instanceof HTMLIFrameElement) {\n\t\t\tiframe.hidden = true\n\t\t\tiframe.removeAttribute('src')\n\t\t}\n\t\tif (errorMessageNode instanceof HTMLElement) {\n\t\t\terrorMessageNode.textContent = String(message || 'Unknown error.')\n\t\t}\n\t\tif (errorNode instanceof HTMLElement) {\n\t\t\terrorNode.hidden = false\n\t\t}\n\t}\n\n\tconst showExternalPadPreview = (url, snapshotText, snapshotHtml) => {\n\t\tif (errorNode instanceof HTMLElement) {\n\t\t\terrorNode.hidden = true\n\t\t}\n\t\tif (iframe instanceof HTMLIFrameElement) {\n\t\t\tiframe.hidden = true\n\t\t\tiframe.removeAttribute('src')\n\t\t}\n\t\tif (!(loadingNode instanceof HTMLElement)) {\n\t\t\treturn\n\t\t}\n\t\tloadingNode.hidden = false\n\t\tloadingNode.classList.add('epnc-embed__loading--snapshot')\n\t\tloadingNode.textContent = ''\n\n\t\tconst snapshot = document.createElement('div')\n\t\tsnapshot.className = 'epnc-embed__snapshot'\n\n\t\tconst inner = document.createElement('div')\n\t\tinner.className = 'epnc-embed__snapshot-inner'\n\n\t\tconst title = document.createElement('h2')\n\t\ttitle.className = 'epnc-embed__snapshot-title'\n\t\ttitle.textContent = externalTitleText\n\n\t\tconst message = document.createElement('p')\n\t\tmessage.className = 'epnc-embed__snapshot-message'\n\t\tmessage.textContent = externalMessageText\n\n\t\tconst link = document.createElement('a')\n\t\tlink.className = 'epnc-embed__snapshot-link'\n\t\tlink.href = url\n\t\tlink.target = '_blank'\n\t\tlink.rel = 'noopener noreferrer'\n\t\tlink.textContent = externalLinkText\n\n\t\tconst actions = document.createElement('div')\n\t\tactions.className = 'epnc-embed__snapshot-actions'\n\t\tactions.appendChild(link)\n\n\t\tconst hasSnapshotHtml = String(snapshotHtml || '').trim() !== ''\n\t\tconst preview = document.createElement(hasSnapshotHtml ? 'div' : 'pre')\n\t\tpreview.className = hasSnapshotHtml\n\t\t\t? 'epnc-embed__snapshot-text epnc-embed__snapshot-text--html'\n\t\t\t: 'epnc-embed__snapshot-text'\n\t\tif (hasSnapshotHtml) {\n\t\t\tpreview.innerHTML = String(snapshotHtml)\n\t\t} else {\n\t\t\tpreview.textContent = String(snapshotText || '').trim() !== '' ? String(snapshotText) : externalEmptyText\n\t\t}\n\n\t\tconst heading = document.createElement('div')\n\t\theading.className = 'epnc-embed__snapshot-heading'\n\t\theading.appendChild(title)\n\t\theading.appendChild(message)\n\n\t\tconst header = document.createElement('div')\n\t\theader.className = 'epnc-embed__snapshot-header'\n\t\theader.appendChild(heading)\n\t\theader.appendChild(actions)\n\n\t\tinner.appendChild(header)\n\t\tinner.appendChild(preview)\n\t\tsnapshot.appendChild(inner)\n\t\tloadingNode.appendChild(snapshot)\n\t}\n\n\tconst showIframe = (url) => {\n\t\tif (!(iframe instanceof HTMLIFrameElement)) {\n\t\t\tshowError('Embed iframe is not available.')\n\t\t\treturn\n\t\t}\n\t\tif (errorNode instanceof HTMLElement) {\n\t\t\terrorNode.hidden = true\n\t\t}\n\t\tif (loadingNode instanceof HTMLElement) {\n\t\t\tloadingNode.classList.remove('epnc-embed__loading--snapshot')\n\t\t}\n\t\tiframe.hidden = true\n\t\tconst revealIframe = () => {\n\t\t\tiframe.removeEventListener('load', revealIframe)\n\t\t\twindow.setTimeout(() => {\n\t\t\t\tif (loadingNode instanceof HTMLElement) {\n\t\t\t\t\tloadingNode.hidden = true\n\t\t\t\t}\n\t\t\t\tiframe.hidden = false\n\t\t\t}, IFRAME_REVEAL_DELAY_MS)\n\t\t}\n\t\tiframe.addEventListener('load', revealIframe, { once: true })\n\t\tiframe.src = url\n\t}\n\n\tconst postHostMessage = (source, origin, type, payload = {}) => {\n\t\t// Replies are only sent from the already origin-validated message handler.\n\t\tif (!source || typeof source.postMessage !== 'function') {\n\t\t\treturn\n\t\t}\n\t\tsource.postMessage(Object.assign({\n\t\t\ttype,\n\t\t\tfileId,\n\t\t}, payload), origin)\n\t}\n\n\tconst isAllowedMessageOrigin = (origin) => {\n\t\tif (!origin || origin === 'null') {\n\t\t\treturn false\n\t\t}\n\t\tif (origin === window.location.origin) {\n\t\t\treturn true\n\t\t}\n\t\treturn trustedOrigins.includes(origin)\n\t}\n\n\tconst installHostMessageHandler = () => {\n\t\tif (messageHandler) {\n\t\t\treturn\n\t\t}\n\t\tmessageHandler = (event) => {\n\t\t\tconst origin = String(event.origin || '')\n\t\t\tif (!isAllowedMessageOrigin(origin)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst payload = event.data\n\t\t\tconst type = typeof payload === 'string'\n\t\t\t\t? payload\n\t\t\t\t: (payload && typeof payload === 'object' && typeof payload.type === 'string' ? payload.type : '')\n\t\t\tif (!type) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (type === 'epnc:host-visible') {\n\t\t\t\tpadSync.start()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (type === 'epnc:host-hidden') {\n\t\t\t\tpadSync.fireAndForget(true, true)\n\t\t\t\tpadSync.stop()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (type === 'epnc:host-before-close' || type === 'epnc:host-sync-now') {\n\t\t\t\tconst keepalive = type !== 'epnc:host-sync-now'\n\t\t\t\tconst reason = type === 'epnc:host-before-close' ? 'before-close' : 'sync-now'\n\t\t\t\tpostHostMessage(event.source, origin, 'epnc:sync-flush-started', {\n\t\t\t\t\treason,\n\t\t\t\t})\n\t\t\t\tvoid padSync.sync(true, keepalive)\n\t\t\t\t\t.then((result) => {\n\t\t\t\t\t\tpostHostMessage(event.source, origin, 'epnc:sync-flush-finished', {\n\t\t\t\t\t\t\treason,\n\t\t\t\t\t\t\tresult: result && typeof result === 'object' ? result : {},\n\t\t\t\t\t\t})\n\t\t\t\t\t})\n\t\t\t\t\t.catch((error) => {\n\t\t\t\t\t\tpostHostMessage(event.source, origin, 'epnc:sync-flush-failed', {\n\t\t\t\t\t\t\treason,\n\t\t\t\t\t\t\tmessage: error instanceof Error ? error.message : 'Sync failed.',\n\t\t\t\t\t\t})\n\t\t\t\t\t})\n\t\t\t\tif (keepalive) {\n\t\t\t\t\tpadSync.stop()\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\twindow.addEventListener('message', messageHandler)\n\t}\n\n\tconst isMissingFrontmatterError = (error) => {\n\t\tif (!(error instanceof Error)) {\n\t\t\treturn false\n\t\t}\n\t\treturn String(error.message || '').includes('Missing YAML frontmatter')\n\t}\n\n\tconst openPad = async () => {\n\t\tconst body = new URLSearchParams()\n\t\tbody.set('fileId', String(fileId))\n\t\tconst data = await fetchJson(openByIdUrl, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\t'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',\n\t\t\t\trequesttoken: requestToken(),\n\t\t\t},\n\t\t\tbody: body.toString(),\n\t\t})\n\t\tif (!data || typeof data.url !== 'string' || data.url.trim() === '') {\n\t\t\tthrow new Error('Pad open API did not return a valid URL.')\n\t\t}\n\t\treturn data\n\t}\n\n\tconst initializePad = async () => {\n\t\tconst url = initializeByIdUrlTemplate.replace('__FILE_ID__', encodeURIComponent(String(fileId)))\n\t\tconst data = await fetchJson(url, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\trequesttoken: requestToken(),\n\t\t\t},\n\t\t})\n\t\tif (data && data.status === 'migrated_from_legacy') {\n\t\t\t// Mirror the backend audit-log entry to the browser console; no\n\t\t\t// toast surface is wired up in this app yet.\n\t\t\tconsole.info('Legacy Ownpad .pad migrated to managed format on first open.')\n\t\t}\n\t}\n\n\tconst hideAllPanels = () => {\n\t\tif (loadingNode instanceof HTMLElement) loadingNode.hidden = true\n\t\tif (errorNode instanceof HTMLElement) errorNode.hidden = true\n\t\tif (recoveryNode instanceof HTMLElement) recoveryNode.hidden = true\n\t\tif (iframe instanceof HTMLIFrameElement) {\n\t\t\tiframe.hidden = true\n\t\t\tiframe.removeAttribute('src')\n\t\t}\n\t}\n\n\tconst showRecoveryChecking = () => {\n\t\tif (!(recoveryNode instanceof HTMLElement)) return\n\t\thideAllPanels()\n\t\trecoveryNode.hidden = false\n\t\tif (recoveryMessageNode instanceof HTMLElement) recoveryMessageNode.textContent = recoveryCheckingText\n\t\tif (recoveryBodyNode instanceof HTMLElement) recoveryBodyNode.textContent = ''\n\t\tif (recoveryActionsNode instanceof HTMLElement) recoveryActionsNode.replaceChildren()\n\t}\n\n\tconst buildRecoveryButton = (label, onClick) => {\n\t\tconst button = document.createElement('button')\n\t\tbutton.type = 'button'\n\t\tbutton.className = 'epnc-embed__recovery-button'\n\t\tbutton.textContent = label\n\t\tbutton.addEventListener('click', onClick)\n\t\treturn button\n\t}\n\n\tconst showRecoveryWithOriginal = (originalEmbedUrl, errorMessage) => {\n\t\tif (!(recoveryNode instanceof HTMLElement)) return\n\t\thideAllPanels()\n\t\trecoveryNode.hidden = false\n\t\tif (recoveryMessageNode instanceof HTMLElement) recoveryMessageNode.textContent = errorMessage\n\t\tif (recoveryBodyNode instanceof HTMLElement) recoveryBodyNode.textContent = recoveryCopyBodyText\n\t\tif (recoveryActionsNode instanceof HTMLElement) {\n\t\t\tconst openLink = document.createElement('a')\n\t\t\topenLink.className = 'epnc-embed__recovery-button epnc-embed__recovery-button--primary'\n\t\t\t// Stay in embed mode: load the original's embed page in the same\n\t\t\t// frame so a host iframe doesn't need to deal with a new tab.\n\t\t\topenLink.href = originalEmbedUrl\n\t\t\topenLink.textContent = recoveryOpenOriginalText\n\t\t\trecoveryActionsNode.replaceChildren(\n\t\t\t\topenLink,\n\t\t\t\tbuildRecoveryButton(recoveryCreateNewText, () => { void triggerRecovery() }),\n\t\t\t)\n\t\t}\n\t}\n\n\tconst showRecoveryWithoutOriginal = (errorMessage) => {\n\t\tif (!(recoveryNode instanceof HTMLElement)) return\n\t\thideAllPanels()\n\t\trecoveryNode.hidden = false\n\t\tif (recoveryMessageNode instanceof HTMLElement) recoveryMessageNode.textContent = errorMessage\n\t\tif (recoveryBodyNode instanceof HTMLElement) recoveryBodyNode.textContent = recoveryOrphanBodyText\n\t\tif (recoveryActionsNode instanceof HTMLElement) {\n\t\t\tconst button = buildRecoveryButton(recoveryCreateNewText, () => { void triggerRecovery() })\n\t\t\tbutton.classList.add('epnc-embed__recovery-button--primary')\n\t\t\trecoveryActionsNode.replaceChildren(button)\n\t\t}\n\t}\n\n\tconst setRecoveryActionsBusy = (busy) => {\n\t\tif (!(recoveryActionsNode instanceof HTMLElement)) return\n\t\tconst buttons = recoveryActionsNode.querySelectorAll('button')\n\t\tbuttons.forEach((node) => {\n\t\t\tnode.disabled = busy\n\t\t\tif (busy) {\n\t\t\t\tnode.dataset.originalLabel = node.dataset.originalLabel || node.textContent || ''\n\t\t\t\tnode.textContent = recoveryCreatingText\n\t\t\t} else if (node.dataset.originalLabel) {\n\t\t\t\tnode.textContent = node.dataset.originalLabel\n\t\t\t\tdelete node.dataset.originalLabel\n\t\t\t}\n\t\t})\n\t}\n\n\tconst triggerRecovery = async () => {\n\t\tif (recoverUrlTemplate === '') {\n\t\t\tshowError('Recovery is not available in this embed.')\n\t\t\treturn\n\t\t}\n\t\tsetRecoveryActionsBusy(true)\n\t\tconst url = recoverUrlTemplate.replace('__FILE_ID__', encodeURIComponent(String(fileId)))\n\t\ttry {\n\t\t\tawait fetchJson(url, {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: { requesttoken: requestToken() },\n\t\t\t})\n\t\t\t// Restart the open flow now that the binding exists.\n\t\t\thideAllPanels()\n\t\t\tif (loadingNode instanceof HTMLElement) loadingNode.hidden = false\n\t\t\tvoid run()\n\t\t} catch (error) {\n\t\t\tsetRecoveryActionsBusy(false)\n\t\t\tif (recoveryMessageNode instanceof HTMLElement) {\n\t\t\t\trecoveryMessageNode.textContent = error instanceof Error && error.message\n\t\t\t\t\t? error.message\n\t\t\t\t\t: 'Recovery failed.'\n\t\t\t}\n\t\t}\n\t}\n\n\tconst enterRecoveryFlow = async (initialError) => {\n\t\tconst errorMessage = initialError instanceof Error && initialError.message\n\t\t\t? initialError.message\n\t\t\t: 'Pad open failed.'\n\t\tshowRecoveryChecking()\n\t\tif (findOriginalUrlTemplate === '') {\n\t\t\tshowRecoveryWithoutOriginal(errorMessage)\n\t\t\treturn\n\t\t}\n\t\tconst lookupUrl = findOriginalUrlTemplate.replace('__FILE_ID__', encodeURIComponent(String(fileId)))\n\t\ttry {\n\t\t\tconst hint = await fetchJson(lookupUrl, { method: 'GET' })\n\t\t\tif (hint && hint.found === true && typeof hint.embed_url === 'string' && hint.embed_url !== '') {\n\t\t\t\tshowRecoveryWithOriginal(hint.embed_url, errorMessage)\n\t\t\t\treturn\n\t\t\t}\n\t\t} catch {\n\t\t\t// Silent: fall through to the no-match branch.\n\t\t}\n\t\tshowRecoveryWithoutOriginal(errorMessage)\n\t}\n\n\tconst run = async () => {\n\t\tif (!Number.isFinite(fileId) || fileId <= 0 || openByIdUrl === '' || initializeByIdUrlTemplate === '') {\n\t\t\tshowError('Embed configuration is incomplete.')\n\t\t\treturn\n\t\t}\n\t\tif (requestToken() === '') {\n\t\t\tshowError('CSRF request token is missing.')\n\t\t\treturn\n\t\t}\n\t\ttry {\n\t\t\tlet data\n\t\t\ttry {\n\t\t\t\tdata = await openPad()\n\t\t\t} catch (error) {\n\t\t\t\tif (!isMissingFrontmatterError(error)) {\n\t\t\t\t\tthrow error\n\t\t\t\t}\n\t\t\t\tawait initializePad()\n\t\t\t\tdata = await openPad()\n\t\t\t}\n\t\t\tconst syncUrl = typeof data.sync_url === 'string' ? data.sync_url.trim() : ''\n\t\t\tconst intervalSeconds = Number(data.sync_interval_seconds ?? 0)\n\t\t\tconst intervalMs = Number.isFinite(intervalSeconds) && intervalSeconds > 0 ? intervalSeconds * 1000 : 120000\n\t\t\tpadSync.configure({ syncUrl, intervalMs })\n\t\t\tpadSync.installLifecycleHandlers()\n\t\t\tinstallHostMessageHandler()\n\t\t\tpadSync.start()\n\t\t\tif (data.is_external === true) {\n\t\t\t\tshowExternalPadPreview(\n\t\t\t\t\tdata.url,\n\t\t\t\t\ttypeof data.snapshot_text === 'string' ? data.snapshot_text : '',\n\t\t\t\t\ttypeof data.snapshot_html === 'string' ? data.snapshot_html : '',\n\t\t\t\t)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tshowIframe(data.url)\n\t\t} catch (error) {\n\t\t\tif (error && error.code === 'missing_binding') {\n\t\t\t\tvoid enterRecoveryFlow(error)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tshowError(error instanceof Error ? error.message : 'Pad open failed.')\n\t\t}\n\t}\n\n\tvoid run()\n})()\n"],"names":["root","fileId","openByIdUrl","initializeByIdUrlTemplate","recoverUrlTemplate","findOriginalUrlTemplate","templateRequestToken","trustedOrigins","value","loadingNode","errorNode","errorMessageNode","recoveryNode","recoveryMessageNode","recoveryBodyNode","recoveryActionsNode","iframe","externalTitleText","externalMessageText","externalEmptyText","externalLinkText","recoveryCheckingText","recoveryCopyBodyText","recoveryOrphanBodyText","recoveryOpenOriginalText","recoveryCreateNewText","recoveryCreatingText","messageHandler","requestToken","ocRequestToken","padSync","createPadSync","showError","message","showExternalPadPreview","url","snapshotText","snapshotHtml","snapshot","inner","title","link","actions","hasSnapshotHtml","preview","heading","header","showIframe","revealIframe","postHostMessage","source","origin","type","payload","isAllowedMessageOrigin","installHostMessageHandler","event","keepalive","reason","result","error","isMissingFrontmatterError","openPad","body","data","fetchJson","initializePad","hideAllPanels","showRecoveryChecking","buildRecoveryButton","label","onClick","button","showRecoveryWithOriginal","originalEmbedUrl","errorMessage","openLink","triggerRecovery","showRecoveryWithoutOriginal","setRecoveryActionsBusy","busy","node","run","enterRecoveryFlow","initialError","lookupUrl","hint","syncUrl","intervalSeconds","intervalMs"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"etherpad_nextcloud-embed-main.mjs","sources":["../src/embed-main.js"],"sourcesContent":["/**\n * SPDX-License-Identifier: AGPL-3.0-or-later\n * Copyright (c) 2026 Jacob Bühler\n */\nimport { ocRequestToken } from './lib/oc-compat.js'\nimport { createPadSync } from './lib/pad-sync.js'\nimport { fetchJsonWithTimeout as fetchJson } from './lib/fetch-helpers.js'\nimport { sanitizeSnapshotHtml } from './lib/sanitize-html.js'\n\n(function () {\n\tconst IFRAME_REVEAL_DELAY_MS = 100\n\n\tconst root = document.getElementById('etherpad-nextcloud-embed')\n\tif (!(root instanceof HTMLElement)) {\n\t\treturn\n\t}\n\n\tconst fileId = Number(root.getAttribute('data-file-id') || '')\n\tconst openByIdUrl = String(root.getAttribute('data-open-by-id-url') || '').trim()\n\tconst initializeByIdUrlTemplate = String(root.getAttribute('data-initialize-by-id-url-template') || '').trim()\n\tconst recoverUrlTemplate = String(root.getAttribute('data-recover-url-template') || '').trim()\n\tconst findOriginalUrlTemplate = String(root.getAttribute('data-find-original-url-template') || '').trim()\n\tconst templateRequestToken = String(root.getAttribute('data-request-token') || '').trim()\n\tconst trustedOrigins = String(root.getAttribute('data-trusted-origins') || '')\n\t\t.split(/\\s+/)\n\t\t.map((value) => value.trim())\n\t\t.filter(Boolean)\n\tconst loadingNode = root.querySelector('[data-epnc-embed-loading]')\n\tconst errorNode = root.querySelector('[data-epnc-embed-error]')\n\tconst errorMessageNode = root.querySelector('[data-epnc-embed-error-message]')\n\tconst recoveryNode = root.querySelector('[data-epnc-embed-recovery]')\n\tconst recoveryMessageNode = root.querySelector('[data-epnc-embed-recovery-message]')\n\tconst recoveryBodyNode = root.querySelector('[data-epnc-embed-recovery-body]')\n\tconst recoveryActionsNode = root.querySelector('[data-epnc-embed-recovery-actions]')\n\tconst iframe = root.querySelector('[data-epnc-embed-iframe]')\n\tconst externalTitleText = String(root.getAttribute('data-l10n-external-title') || 'Pad from another server').trim()\n\tconst externalMessageText = String(root.getAttribute('data-l10n-external-message') || 'Read-only snapshot from the .pad file.').trim()\n\tconst externalEmptyText = String(root.getAttribute('data-l10n-external-empty') || 'No synced snapshot is stored in this .pad file yet.').trim()\n\tconst externalLinkText = String(root.getAttribute('data-l10n-external-link') || 'Open original pad').trim()\n\tconst recoveryCheckingText = String(root.getAttribute('data-l10n-recovery-checking') || 'Checking for the original pad...').trim()\n\tconst recoveryCopyBodyText = String(root.getAttribute('data-l10n-recovery-copy-body') || '').trim()\n\tconst recoveryOrphanBodyText = String(root.getAttribute('data-l10n-recovery-orphan-body') || '').trim()\n\tconst recoveryOpenOriginalText = String(root.getAttribute('data-l10n-recovery-open-original') || 'Open the original .pad file').trim()\n\tconst recoveryCreateNewText = String(root.getAttribute('data-l10n-recovery-create-new') || 'Create new pad from this file').trim()\n\tconst recoveryCreatingText = String(root.getAttribute('data-l10n-recovery-creating') || 'Creating new pad...').trim()\n\tlet messageHandler = null\n\n\tconst requestToken = () => ocRequestToken(templateRequestToken)\n\tconst padSync = createPadSync({ requestToken })\n\n\tconst showError = (message) => {\n\t\tif (loadingNode instanceof HTMLElement) {\n\t\t\tloadingNode.hidden = true\n\t\t\tloadingNode.classList.remove('epnc-embed__loading--snapshot')\n\t\t}\n\t\tif (iframe instanceof HTMLIFrameElement) {\n\t\t\tiframe.hidden = true\n\t\t\tiframe.removeAttribute('src')\n\t\t}\n\t\tif (errorMessageNode instanceof HTMLElement) {\n\t\t\terrorMessageNode.textContent = String(message || 'Unknown error.')\n\t\t}\n\t\tif (errorNode instanceof HTMLElement) {\n\t\t\terrorNode.hidden = false\n\t\t}\n\t}\n\n\tconst showExternalPadPreview = (url, snapshotText, snapshotHtml) => {\n\t\tif (errorNode instanceof HTMLElement) {\n\t\t\terrorNode.hidden = true\n\t\t}\n\t\tif (iframe instanceof HTMLIFrameElement) {\n\t\t\tiframe.hidden = true\n\t\t\tiframe.removeAttribute('src')\n\t\t}\n\t\tif (!(loadingNode instanceof HTMLElement)) {\n\t\t\treturn\n\t\t}\n\t\tloadingNode.hidden = false\n\t\tloadingNode.classList.add('epnc-embed__loading--snapshot')\n\t\tloadingNode.textContent = ''\n\n\t\tconst snapshot = document.createElement('div')\n\t\tsnapshot.className = 'epnc-embed__snapshot'\n\n\t\tconst inner = document.createElement('div')\n\t\tinner.className = 'epnc-embed__snapshot-inner'\n\n\t\tconst title = document.createElement('h2')\n\t\ttitle.className = 'epnc-embed__snapshot-title'\n\t\ttitle.textContent = externalTitleText\n\n\t\tconst message = document.createElement('p')\n\t\tmessage.className = 'epnc-embed__snapshot-message'\n\t\tmessage.textContent = externalMessageText\n\n\t\tconst link = document.createElement('a')\n\t\tlink.className = 'epnc-embed__snapshot-link'\n\t\tlink.href = url\n\t\tlink.target = '_blank'\n\t\tlink.rel = 'noopener noreferrer'\n\t\tlink.textContent = externalLinkText\n\n\t\tconst actions = document.createElement('div')\n\t\tactions.className = 'epnc-embed__snapshot-actions'\n\t\tactions.appendChild(link)\n\n\t\tconst hasSnapshotHtml = String(snapshotHtml || '').trim() !== ''\n\t\tconst preview = document.createElement(hasSnapshotHtml ? 'div' : 'pre')\n\t\tpreview.className = hasSnapshotHtml\n\t\t\t? 'epnc-embed__snapshot-text epnc-embed__snapshot-text--html'\n\t\t\t: 'epnc-embed__snapshot-text'\n\t\tif (hasSnapshotHtml) {\n\t\t\tpreview.innerHTML = sanitizeSnapshotHtml(snapshotHtml)\n\t\t} else {\n\t\t\tpreview.textContent = String(snapshotText || '').trim() !== '' ? String(snapshotText) : externalEmptyText\n\t\t}\n\n\t\tconst heading = document.createElement('div')\n\t\theading.className = 'epnc-embed__snapshot-heading'\n\t\theading.appendChild(title)\n\t\theading.appendChild(message)\n\n\t\tconst header = document.createElement('div')\n\t\theader.className = 'epnc-embed__snapshot-header'\n\t\theader.appendChild(heading)\n\t\theader.appendChild(actions)\n\n\t\tinner.appendChild(header)\n\t\tinner.appendChild(preview)\n\t\tsnapshot.appendChild(inner)\n\t\tloadingNode.appendChild(snapshot)\n\t}\n\n\tconst showIframe = (url) => {\n\t\tif (!(iframe instanceof HTMLIFrameElement)) {\n\t\t\tshowError('Embed iframe is not available.')\n\t\t\treturn\n\t\t}\n\t\tif (errorNode instanceof HTMLElement) {\n\t\t\terrorNode.hidden = true\n\t\t}\n\t\tif (loadingNode instanceof HTMLElement) {\n\t\t\tloadingNode.classList.remove('epnc-embed__loading--snapshot')\n\t\t}\n\t\tiframe.hidden = true\n\t\tconst revealIframe = () => {\n\t\t\tiframe.removeEventListener('load', revealIframe)\n\t\t\twindow.setTimeout(() => {\n\t\t\t\tif (loadingNode instanceof HTMLElement) {\n\t\t\t\t\tloadingNode.hidden = true\n\t\t\t\t}\n\t\t\t\tiframe.hidden = false\n\t\t\t}, IFRAME_REVEAL_DELAY_MS)\n\t\t}\n\t\tiframe.addEventListener('load', revealIframe, { once: true })\n\t\tiframe.src = url\n\t}\n\n\tconst postHostMessage = (source, origin, type, payload = {}) => {\n\t\t// Replies are only sent from the already origin-validated message handler.\n\t\tif (!source || typeof source.postMessage !== 'function') {\n\t\t\treturn\n\t\t}\n\t\tsource.postMessage(Object.assign({\n\t\t\ttype,\n\t\t\tfileId,\n\t\t}, payload), origin)\n\t}\n\n\tconst isAllowedMessageOrigin = (origin) => {\n\t\tif (!origin || origin === 'null') {\n\t\t\treturn false\n\t\t}\n\t\tif (origin === window.location.origin) {\n\t\t\treturn true\n\t\t}\n\t\treturn trustedOrigins.includes(origin)\n\t}\n\n\tconst installHostMessageHandler = () => {\n\t\tif (messageHandler) {\n\t\t\treturn\n\t\t}\n\t\tmessageHandler = (event) => {\n\t\t\tconst origin = String(event.origin || '')\n\t\t\tif (!isAllowedMessageOrigin(origin)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst payload = event.data\n\t\t\tconst type = typeof payload === 'string'\n\t\t\t\t? payload\n\t\t\t\t: (payload && typeof payload === 'object' && typeof payload.type === 'string' ? payload.type : '')\n\t\t\tif (!type) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (type === 'epnc:host-visible') {\n\t\t\t\tpadSync.start()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (type === 'epnc:host-hidden') {\n\t\t\t\tpadSync.fireAndForget(true, true)\n\t\t\t\tpadSync.stop()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (type === 'epnc:host-before-close' || type === 'epnc:host-sync-now') {\n\t\t\t\tconst keepalive = type !== 'epnc:host-sync-now'\n\t\t\t\tconst reason = type === 'epnc:host-before-close' ? 'before-close' : 'sync-now'\n\t\t\t\tpostHostMessage(event.source, origin, 'epnc:sync-flush-started', {\n\t\t\t\t\treason,\n\t\t\t\t})\n\t\t\t\tvoid padSync.sync(true, keepalive)\n\t\t\t\t\t.then((result) => {\n\t\t\t\t\t\tpostHostMessage(event.source, origin, 'epnc:sync-flush-finished', {\n\t\t\t\t\t\t\treason,\n\t\t\t\t\t\t\tresult: result && typeof result === 'object' ? result : {},\n\t\t\t\t\t\t})\n\t\t\t\t\t})\n\t\t\t\t\t.catch((error) => {\n\t\t\t\t\t\tpostHostMessage(event.source, origin, 'epnc:sync-flush-failed', {\n\t\t\t\t\t\t\treason,\n\t\t\t\t\t\t\tmessage: error instanceof Error ? error.message : 'Sync failed.',\n\t\t\t\t\t\t})\n\t\t\t\t\t})\n\t\t\t\tif (keepalive) {\n\t\t\t\t\tpadSync.stop()\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\twindow.addEventListener('message', messageHandler)\n\t}\n\n\tconst isMissingFrontmatterError = (error) => {\n\t\tif (!(error instanceof Error)) {\n\t\t\treturn false\n\t\t}\n\t\treturn String(error.message || '').includes('Missing YAML frontmatter')\n\t}\n\n\tconst openPad = async () => {\n\t\tconst body = new URLSearchParams()\n\t\tbody.set('fileId', String(fileId))\n\t\tconst data = await fetchJson(openByIdUrl, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\t'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',\n\t\t\t\trequesttoken: requestToken(),\n\t\t\t},\n\t\t\tbody: body.toString(),\n\t\t})\n\t\tif (!data || typeof data.url !== 'string' || data.url.trim() === '') {\n\t\t\tthrow new Error('Pad open API did not return a valid URL.')\n\t\t}\n\t\treturn data\n\t}\n\n\tconst initializePad = async () => {\n\t\tconst url = initializeByIdUrlTemplate.replace('__FILE_ID__', encodeURIComponent(String(fileId)))\n\t\tconst data = await fetchJson(url, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\trequesttoken: requestToken(),\n\t\t\t},\n\t\t})\n\t\tif (data && data.status === 'migrated_from_legacy') {\n\t\t\t// Mirror the backend audit-log entry to the browser console; no\n\t\t\t// toast surface is wired up in this app yet.\n\t\t\tconsole.info('Legacy Ownpad .pad migrated to managed format on first open.')\n\t\t}\n\t}\n\n\tconst hideAllPanels = () => {\n\t\tif (loadingNode instanceof HTMLElement) loadingNode.hidden = true\n\t\tif (errorNode instanceof HTMLElement) errorNode.hidden = true\n\t\tif (recoveryNode instanceof HTMLElement) recoveryNode.hidden = true\n\t\tif (iframe instanceof HTMLIFrameElement) {\n\t\t\tiframe.hidden = true\n\t\t\tiframe.removeAttribute('src')\n\t\t}\n\t}\n\n\tconst showRecoveryChecking = () => {\n\t\tif (!(recoveryNode instanceof HTMLElement)) return\n\t\thideAllPanels()\n\t\trecoveryNode.hidden = false\n\t\tif (recoveryMessageNode instanceof HTMLElement) recoveryMessageNode.textContent = recoveryCheckingText\n\t\tif (recoveryBodyNode instanceof HTMLElement) recoveryBodyNode.textContent = ''\n\t\tif (recoveryActionsNode instanceof HTMLElement) recoveryActionsNode.replaceChildren()\n\t}\n\n\tconst buildRecoveryButton = (label, onClick) => {\n\t\tconst button = document.createElement('button')\n\t\tbutton.type = 'button'\n\t\tbutton.className = 'epnc-embed__recovery-button'\n\t\tbutton.textContent = label\n\t\tbutton.addEventListener('click', onClick)\n\t\treturn button\n\t}\n\n\tconst showRecoveryWithOriginal = (originalEmbedUrl, errorMessage) => {\n\t\tif (!(recoveryNode instanceof HTMLElement)) return\n\t\thideAllPanels()\n\t\trecoveryNode.hidden = false\n\t\tif (recoveryMessageNode instanceof HTMLElement) recoveryMessageNode.textContent = errorMessage\n\t\tif (recoveryBodyNode instanceof HTMLElement) recoveryBodyNode.textContent = recoveryCopyBodyText\n\t\tif (recoveryActionsNode instanceof HTMLElement) {\n\t\t\tconst openLink = document.createElement('a')\n\t\t\topenLink.className = 'epnc-embed__recovery-button epnc-embed__recovery-button--primary'\n\t\t\t// Stay in embed mode: load the original's embed page in the same\n\t\t\t// frame so a host iframe doesn't need to deal with a new tab.\n\t\t\topenLink.href = originalEmbedUrl\n\t\t\topenLink.textContent = recoveryOpenOriginalText\n\t\t\trecoveryActionsNode.replaceChildren(\n\t\t\t\topenLink,\n\t\t\t\tbuildRecoveryButton(recoveryCreateNewText, () => { void triggerRecovery() }),\n\t\t\t)\n\t\t}\n\t}\n\n\tconst showRecoveryWithoutOriginal = (errorMessage) => {\n\t\tif (!(recoveryNode instanceof HTMLElement)) return\n\t\thideAllPanels()\n\t\trecoveryNode.hidden = false\n\t\tif (recoveryMessageNode instanceof HTMLElement) recoveryMessageNode.textContent = errorMessage\n\t\tif (recoveryBodyNode instanceof HTMLElement) recoveryBodyNode.textContent = recoveryOrphanBodyText\n\t\tif (recoveryActionsNode instanceof HTMLElement) {\n\t\t\tconst button = buildRecoveryButton(recoveryCreateNewText, () => { void triggerRecovery() })\n\t\t\tbutton.classList.add('epnc-embed__recovery-button--primary')\n\t\t\trecoveryActionsNode.replaceChildren(button)\n\t\t}\n\t}\n\n\tconst setRecoveryActionsBusy = (busy) => {\n\t\tif (!(recoveryActionsNode instanceof HTMLElement)) return\n\t\tconst buttons = recoveryActionsNode.querySelectorAll('button')\n\t\tbuttons.forEach((node) => {\n\t\t\tnode.disabled = busy\n\t\t\tif (busy) {\n\t\t\t\tnode.dataset.originalLabel = node.dataset.originalLabel || node.textContent || ''\n\t\t\t\tnode.textContent = recoveryCreatingText\n\t\t\t} else if (node.dataset.originalLabel) {\n\t\t\t\tnode.textContent = node.dataset.originalLabel\n\t\t\t\tdelete node.dataset.originalLabel\n\t\t\t}\n\t\t})\n\t}\n\n\tconst triggerRecovery = async () => {\n\t\tif (recoverUrlTemplate === '') {\n\t\t\tshowError('Recovery is not available in this embed.')\n\t\t\treturn\n\t\t}\n\t\tsetRecoveryActionsBusy(true)\n\t\tconst url = recoverUrlTemplate.replace('__FILE_ID__', encodeURIComponent(String(fileId)))\n\t\ttry {\n\t\t\tawait fetchJson(url, {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: { requesttoken: requestToken() },\n\t\t\t})\n\t\t\t// Restart the open flow now that the binding exists.\n\t\t\thideAllPanels()\n\t\t\tif (loadingNode instanceof HTMLElement) loadingNode.hidden = false\n\t\t\tvoid run()\n\t\t} catch (error) {\n\t\t\tsetRecoveryActionsBusy(false)\n\t\t\tif (recoveryMessageNode instanceof HTMLElement) {\n\t\t\t\trecoveryMessageNode.textContent = error instanceof Error && error.message\n\t\t\t\t\t? error.message\n\t\t\t\t\t: 'Recovery failed.'\n\t\t\t}\n\t\t}\n\t}\n\n\tconst enterRecoveryFlow = async (initialError) => {\n\t\tconst errorMessage = initialError instanceof Error && initialError.message\n\t\t\t? initialError.message\n\t\t\t: 'Pad open failed.'\n\t\tshowRecoveryChecking()\n\t\tif (findOriginalUrlTemplate === '') {\n\t\t\tshowRecoveryWithoutOriginal(errorMessage)\n\t\t\treturn\n\t\t}\n\t\tconst lookupUrl = findOriginalUrlTemplate.replace('__FILE_ID__', encodeURIComponent(String(fileId)))\n\t\ttry {\n\t\t\tconst hint = await fetchJson(lookupUrl, { method: 'GET' })\n\t\t\tif (hint && hint.found === true && typeof hint.embed_url === 'string' && hint.embed_url !== '') {\n\t\t\t\tshowRecoveryWithOriginal(hint.embed_url, errorMessage)\n\t\t\t\treturn\n\t\t\t}\n\t\t} catch {\n\t\t\t// Silent: fall through to the no-match branch.\n\t\t}\n\t\tshowRecoveryWithoutOriginal(errorMessage)\n\t}\n\n\tconst run = async () => {\n\t\tif (!Number.isFinite(fileId) || fileId <= 0 || openByIdUrl === '' || initializeByIdUrlTemplate === '') {\n\t\t\tshowError('Embed configuration is incomplete.')\n\t\t\treturn\n\t\t}\n\t\tif (requestToken() === '') {\n\t\t\tshowError('CSRF request token is missing.')\n\t\t\treturn\n\t\t}\n\t\ttry {\n\t\t\tlet data\n\t\t\ttry {\n\t\t\t\tdata = await openPad()\n\t\t\t} catch (error) {\n\t\t\t\tif (!isMissingFrontmatterError(error)) {\n\t\t\t\t\tthrow error\n\t\t\t\t}\n\t\t\t\tawait initializePad()\n\t\t\t\tdata = await openPad()\n\t\t\t}\n\t\t\tconst syncUrl = typeof data.sync_url === 'string' ? data.sync_url.trim() : ''\n\t\t\tconst intervalSeconds = Number(data.sync_interval_seconds ?? 0)\n\t\t\tconst intervalMs = Number.isFinite(intervalSeconds) && intervalSeconds > 0 ? intervalSeconds * 1000 : 120000\n\t\t\tpadSync.configure({ syncUrl, intervalMs })\n\t\t\tpadSync.installLifecycleHandlers()\n\t\t\tinstallHostMessageHandler()\n\t\t\tpadSync.start()\n\t\t\tif (data.is_external === true) {\n\t\t\t\tshowExternalPadPreview(\n\t\t\t\t\tdata.url,\n\t\t\t\t\ttypeof data.snapshot_text === 'string' ? data.snapshot_text : '',\n\t\t\t\t\ttypeof data.snapshot_html === 'string' ? data.snapshot_html : '',\n\t\t\t\t)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tshowIframe(data.url)\n\t\t} catch (error) {\n\t\t\tif (error && error.code === 'missing_binding') {\n\t\t\t\tvoid enterRecoveryFlow(error)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tshowError(error instanceof Error ? error.message : 'Pad open failed.')\n\t\t}\n\t}\n\n\tvoid run()\n})()\n"],"names":["root","fileId","openByIdUrl","initializeByIdUrlTemplate","recoverUrlTemplate","findOriginalUrlTemplate","templateRequestToken","trustedOrigins","value","loadingNode","errorNode","errorMessageNode","recoveryNode","recoveryMessageNode","recoveryBodyNode","recoveryActionsNode","iframe","externalTitleText","externalMessageText","externalEmptyText","externalLinkText","recoveryCheckingText","recoveryCopyBodyText","recoveryOrphanBodyText","recoveryOpenOriginalText","recoveryCreateNewText","recoveryCreatingText","messageHandler","requestToken","ocRequestToken","padSync","createPadSync","showError","message","showExternalPadPreview","url","snapshotText","snapshotHtml","snapshot","inner","title","link","actions","hasSnapshotHtml","preview","sanitizeSnapshotHtml","heading","header","showIframe","revealIframe","postHostMessage","source","origin","type","payload","isAllowedMessageOrigin","installHostMessageHandler","event","keepalive","reason","result","error","isMissingFrontmatterError","openPad","body","data","fetchJson","initializePad","hideAllPanels","showRecoveryChecking","buildRecoveryButton","label","onClick","button","showRecoveryWithOriginal","originalEmbedUrl","errorMessage","openLink","triggerRecovery","showRecoveryWithoutOriginal","setRecoveryActionsBusy","busy","node","run","enterRecoveryFlow","initialError","lookupUrl","hint","syncUrl","intervalSeconds","intervalMs"],"mappings":"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"}
\ No newline at end of file
diff --git a/js/etherpad_nextcloud-viewer-main.mjs b/js/etherpad_nextcloud-viewer-main.mjs
index 72c7401..21d6fc4 100644
--- a/js/etherpad_nextcloud-viewer-main.mjs
+++ b/js/etherpad_nextcloud-viewer-main.mjs
@@ -1,2 +1,2 @@
-import{V as _,M as x,t as l,f as y,A as v,o as w}from"./oc-compat-hVqZy-MX.chunk.mjs";import{m as k,o as R,a as b,l as I}from"./api-client-BXEMiUh7.chunk.mjs";import{c as L}from"./pad-sync-B0gz-oBk.chunk.mjs";const O=f=>String(f||"").replace(/&/g,"&").replace(/"/g,""").replace(//g,">"),C=["http:","https:"],F=f=>{try{const u=new URL(String(f||""));return C.includes(u.protocol)&&u.username===""&&u.password===""}catch{return!1}},U="default-src 'none'; frame-src http: https:; style-src 'unsafe-inline'",M=f=>{const u=F(f)?O(f):"";return'
'};(function(){let f=0;const u={name:"EtherpadNextcloudViewer",props:{filename:{type:String,required:!1,default:""},basename:{type:String,required:!1,default:""},source:{type:String,required:!1,default:""},fileid:{type:[String,Number],required:!1,default:null},fileId:{type:[String,Number],required:!1,default:null},fileInfo:{type:Object,required:!1,default:null}},data(){return{iframeSrc:"",isLoading:!0,loadError:"",canRecover:!1,isRecovering:!1,isCheckingOriginal:!1,originalPad:null,externalOpenUrl:"",externalOpenMessage:"",snapshotMode:"",snapshot:{text:"",html:""},resolveGeneration:0}},computed:{sourcePath(){const e=typeof this.source=="string"?this.source.trim():"";return e&&I(e)||""},filePath(){const e=t=>String(t||"").trim().replace(/\s+\.pad$/i,".pad"),i=t=>{const h=String(t||"").trim();return!h||h==="/"?"/":h.startsWith("/")?h:"/"+h},n=(t,h)=>{if(!h)return"";if(h.startsWith("/"))return h;const g=i(t);return g==="/"?"/"+h:g+"/"+h},a=t=>typeof t=="string"&&t.toLowerCase().endsWith(".pad");if(a(this.sourcePath))return this.sourcePath;const s=this.fileInfo&&typeof this.fileInfo=="object"?this.fileInfo:null,d=s&&typeof s.path=="string"?e(s.path):"";if(a(d))return d.startsWith("/")?d:"/"+d;const c=e(this.filename||this.basename||s&&(s.name||s.basename)||"");if(!c)return"";const o=s&&typeof s.dirname=="string"?s.dirname:"";if(o){const t=n(o,c);if(a(t))return t}const m=new URLSearchParams(window.location.search||"").get("dir")||"/",r=n(m,c);return a(r)?r:"/"+c},resolvedFileId(){const e=[this.fileid,this.fileId,this.fileInfo&&(this.fileInfo.fileid||this.fileInfo.fileId||this.fileInfo.id)];for(const a of e){const s=Number(a);if(Number.isFinite(s)&&s>0)return s}if(new URLSearchParams(window.location.search||"").get("openfile")!=="true")return null;const i=(window.location.pathname||"").match(/\/apps\/files\/files\/(\d+)\/?$/);if(!i)return null;const n=Number(i[1]);return Number.isFinite(n)&&n>0?n:null}},watch:{filePath:{immediate:!0,handler(){this.resolveOpenUrl()}},resolvedFileId(){this.resolveOpenUrl()}},methods:{async fetchOpenPayload(e,i={}){const n=Object.assign({Accept:"application/json"},i.headers||{}),a=await fetch(e,Object.assign({method:"GET",credentials:"same-origin",headers:n},i)),s=await a.json().catch(()=>({}));if(!a.ok){const d=new Error(s&&s.message||"Pad open failed.");throw s&&typeof s.code=="string"&&(d.code=s.code),d}if(!s||s.is_readonly_snapshot!==!0&&(typeof s.url!="string"||s.url.trim()===""))throw new Error("Pad open API did not return a valid URL.");return s},isMissingFrontmatterError(e){return e instanceof Error?String(e.message||"").includes("Missing YAML frontmatter"):!1},async initializeMissingFrontmatter(){const e={Accept:"application/json",requesttoken:w()},i=(o,m)=>{const r=new Error(o&&o.message||m);return o&&typeof o.code=="string"&&o.code!==""&&(r.code=o.code),r},n=o=>{o&&o.status==="migrated_from_legacy"&&console.info("Legacy Ownpad .pad migrated to managed format on first open.")};if(this.resolvedFileId!==null){const o=y("/apps/"+v+"/api/v1/pads/initialize-by-id/"+encodeURIComponent(String(this.resolvedFileId))),m=await fetch(o,{method:"POST",credentials:"same-origin",headers:e}),r=await m.json().catch(()=>({}));if(!m.ok)throw i(r,"Pad initialization failed.");return n(r),r}if(!this.filePath)throw new Error("Pad initialization failed: missing file path.");const a=new URLSearchParams;a.set("file",this.filePath);const s=y("/apps/"+v+"/api/v1/pads/initialize"),d=await fetch(s,{method:"POST",credentials:"same-origin",headers:Object.assign({},e,{"Content-Type":"application/x-www-form-urlencoded;charset=UTF-8"}),body:a.toString()}),c=await d.json().catch(()=>({}));if(!d.ok)throw i(c,"Pad initialization failed.");return n(c),c},markLoaded(){this.$emit("update:loaded",!0)},padSync(){return this._padSync||(this._padSync=L({requestToken:()=>w()})),this._padSync},teardownSync(){this._padSync&&(this._padSync.fireAndForget(!0,!0),this._padSync.stop(),this._padSync.removeLifecycleHandlers())},async resolveOpenUrl(){const e=++this.resolveGeneration,i=()=>e===this.resolveGeneration;if(this.isLoading=!0,this.loadError="",this.canRecover=!1,this.isCheckingOriginal=!1,this.originalPad=null,this.iframeSrc="",this.externalOpenUrl="",this.externalOpenMessage="",this.snapshotMode="",this.snapshot={text:"",html:""},this._padSync&&(this._padSync.stop(),this._padSync.configure({syncUrl:""})),!this.filePath){if(!i())return;this.loadError="No .pad file selected.",this.isLoading=!1;return}const n=b(),a=y("/apps/"+v+"/api/v1/pads/open"),s=(()=>{if(!n)return"";const r=new URL(y("/apps/"+v+"/api/v1/public/open/"+encodeURIComponent(n)),window.location.origin);return r.searchParams.set("file",this.filePath),r.toString()})(),d=this.resolvedFileId!==null?y("/apps/"+v+"/api/v1/pads/open-by-id"):"",c=new URLSearchParams;c.set("file",this.filePath);const o=new URLSearchParams;this.resolvedFileId!==null&&o.set("fileId",String(this.resolvedFileId));const m={"Content-Type":"application/x-www-form-urlencoded;charset=UTF-8",requesttoken:w()};try{const r=async()=>{let p=null;if(s)p=await this.fetchOpenPayload(s);else if(d)try{p=await this.fetchOpenPayload(d,{method:"POST",headers:m,body:o.toString()})}catch{}return p||(p=await this.fetchOpenPayload(a,{method:"POST",headers:m,body:c.toString()})),p};let t;try{if(t=await r(),!i())return}catch(p){if(!this.isMissingFrontmatterError(p))throw p;if(await this.initializeMissingFrontmatter(),!i()||(t=await r(),!i()))return}const h=t&&typeof t.sync_url=="string"?t.sync_url:"",g=Number(t&&t.sync_interval_seconds),P=Number.isFinite(g)&&g>0?Math.max(5e3,Math.min(36e5,g*1e3)):12e4;if(this.padSync().configure({syncUrl:h,intervalMs:P}),this.padSync().installLifecycleHandlers(),h&&this.padSync().start(),t&&t.is_readonly_snapshot===!0){this.snapshotMode="readonly",this.snapshot={text:typeof t.snapshot_text=="string"?t.snapshot_text:"",html:typeof t.snapshot_html=="string"?t.snapshot_html:""},this.markLoaded();return}if(t&&t.is_external===!0&&typeof t.url=="string"&&t.url.trim()!==""){const p=t.url.trim();this.externalOpenUrl=p,this.externalOpenMessage=l("Read-only snapshot from the .pad file."),this.snapshotMode="external",this.snapshot={text:t&&typeof t.snapshot_text=="string"?t.snapshot_text:"",html:t&&typeof t.snapshot_html=="string"?t.snapshot_html:""},this.markLoaded();return}this.iframeSrc=t.url,this.markLoaded()}catch(r){if(!i())return;this.loadError=r instanceof Error?r.message:"Could not load pad.",this.canRecover=!!(r&&r.code==="missing_binding")&&this.resolvedFileId!==null&&!b(),this.canRecover&&this.fetchOriginalPadHint(e,i),this.markLoaded()}finally{if(!i())return;this.isLoading=!1}},async fetchOriginalPadHint(e,i){if(this.resolvedFileId!==null){this.isCheckingOriginal=!0;try{const n=await R(this.resolvedFileId);if(!i())return;n&&n.found===!0&&typeof n.viewer_url=="string"&&n.viewer_url!==""&&(this.originalPad={viewerUrl:n.viewer_url,path:typeof n.path=="string"?n.path:""})}catch{}finally{i()&&(this.isCheckingOriginal=!1)}}},async recoverFromSnapshot(){if(!(!this.canRecover||this.isRecovering||this.resolvedFileId===null)){this.isRecovering=!0;try{await k(this.resolvedFileId),this.loadError="",this.canRecover=!1,await this.resolveOpenUrl()}catch(e){this.loadError=e instanceof Error?e.message:"Could not load pad."}finally{this.isRecovering=!1}}},renderSnapshotView(e,i){const n=String(i.html||""),a=String(i.text||""),s=Array.isArray(i.actions)?i.actions:[];return e("div",{class:"epnc-native-snapshot"},[e("div",{class:"epnc-native-snapshot__inner"},[e("div",{class:"epnc-native-snapshot__header"},[e("div",{class:"epnc-native-snapshot__heading"},[e("div",{class:"epnc-native-snapshot__title"},i.title),e("div",{class:"epnc-native-snapshot__message"},i.message)]),s.length>0?e("div",{class:"epnc-native-snapshot__actions"},s):null]),n.trim()!==""?e("div",{class:"epnc-native-snapshot__text epnc-native-snapshot__text--html",domProps:{innerHTML:n}}):e("pre",{class:"epnc-native-snapshot__text"},a.trim()!==""?a:i.emptyMessage)])])}},beforeDestroy(){this.resolveGeneration+=1,this.teardownSync()},beforeUnmount(){this.resolveGeneration+=1,this.teardownSync()},render(e){if(this.loadError){const i=[e("div",{class:"epnc-native-error-title"},l("Could not open pad")),e("div",{class:"epnc-native-error-message"},this.loadError)];return this.canRecover&&(this.isCheckingOriginal?i.push(e("div",{class:"epnc-native-error-message"},l("Checking for the original pad..."))):this.originalPad?i.push(e("div",{class:"epnc-native-error-message"},l("This file looks like a copy of an existing .pad file in your account. Open the original to keep editing the linked pad, or create a new pad to fork the content stored in this file.")),e("a",{class:"button primary epnc-native-error-action",attrs:{href:this.originalPad.viewerUrl}},l("Open the original .pad file")),e("button",{class:"button epnc-native-error-action",attrs:{type:"button",disabled:this.isRecovering},on:{click:()=>{this.recoverFromSnapshot()}}},this.isRecovering?l("Creating new pad..."):l("Create new pad from this file"))):i.push(e("div",{class:"epnc-native-error-message"},l("We couldn't find a matching pad in this Nextcloud. You can create a new pad from the text stored in this file; from then on, opening this file will load the new pad.")),e("button",{class:"button primary epnc-native-error-action",attrs:{type:"button",disabled:this.isRecovering},on:{click:()=>{this.recoverFromSnapshot()}}},this.isRecovering?l("Creating new pad..."):l("Create new pad from this file")))),e("div",{class:"epnc-native-status epnc-native-status--error"},[e("div",{class:"epnc-native-error-card"},i)])}return this.snapshotMode==="external"?this.renderSnapshotView(e,{title:l("Pad from another server"),message:this.externalOpenMessage,actions:[e("a",{class:"button primary",attrs:{href:this.externalOpenUrl,target:"_blank",rel:"noopener noreferrer"}},l("Open original pad"))],html:this.snapshot.html,text:this.snapshot.text,emptyMessage:l("No synced snapshot is stored in this .pad file yet.")}):this.snapshotMode==="readonly"?this.renderSnapshotView(e,{title:l("Read-only snapshot"),message:l("Read-only snapshot from the .pad file."),html:this.snapshot.html,text:this.snapshot.text,emptyMessage:l("No synced snapshot is stored in this .pad file yet.")}):this.isLoading||!this.iframeSrc?e("div",{class:"epnc-native-status"},"Loading pad..."):e("div",{class:"epnc-native-shell"},[e("iframe",{attrs:{srcdoc:M(this.iframeSrc),title:"Etherpad"},on:{load:()=>this.markLoaded(),error:()=>this.markLoaded()},class:"epnc-native-iframe"})])}},S=()=>{if(f+=1,!(window.OCA&&window.OCA.Viewer&&typeof window.OCA.Viewer.registerHandler=="function")){f<20&&window.setTimeout(S,500);return}Array.isArray(window.OCA.Viewer.availableHandlers)&&window.OCA.Viewer.availableHandlers.some(e=>e&&e.id===_)||window.OCA.Viewer.registerHandler({id:_,mimes:[x],component:u})};S()})();
+import{V as _,M as x,t as l,f as y,A as v,o as w}from"./oc-compat-hVqZy-MX.chunk.mjs";import{m as k,o as R,a as b,l as I}from"./api-client-BXEMiUh7.chunk.mjs";import{s as L,c as C}from"./sanitize-html-dv-YifbT.chunk.mjs";const O=f=>String(f||"").replace(/&/g,"&").replace(/"/g,""").replace(//g,">"),F=["http:","https:"],U=f=>{try{const u=new URL(String(f||""));return F.includes(u.protocol)&&u.username===""&&u.password===""}catch{return!1}},M="default-src 'none'; frame-src http: https:; style-src 'unsafe-inline'",E=f=>{const u=U(f)?O(f):"";return''};(function(){let f=0;const u={name:"EtherpadNextcloudViewer",props:{filename:{type:String,required:!1,default:""},basename:{type:String,required:!1,default:""},source:{type:String,required:!1,default:""},fileid:{type:[String,Number],required:!1,default:null},fileId:{type:[String,Number],required:!1,default:null},fileInfo:{type:Object,required:!1,default:null}},data(){return{iframeSrc:"",isLoading:!0,loadError:"",canRecover:!1,isRecovering:!1,isCheckingOriginal:!1,originalPad:null,externalOpenUrl:"",externalOpenMessage:"",snapshotMode:"",snapshot:{text:"",html:""},resolveGeneration:0}},computed:{sourcePath(){const e=typeof this.source=="string"?this.source.trim():"";return e&&I(e)||""},filePath(){const e=t=>String(t||"").trim().replace(/\s+\.pad$/i,".pad"),i=t=>{const h=String(t||"").trim();return!h||h==="/"?"/":h.startsWith("/")?h:"/"+h},n=(t,h)=>{if(!h)return"";if(h.startsWith("/"))return h;const g=i(t);return g==="/"?"/"+h:g+"/"+h},a=t=>typeof t=="string"&&t.toLowerCase().endsWith(".pad");if(a(this.sourcePath))return this.sourcePath;const s=this.fileInfo&&typeof this.fileInfo=="object"?this.fileInfo:null,d=s&&typeof s.path=="string"?e(s.path):"";if(a(d))return d.startsWith("/")?d:"/"+d;const c=e(this.filename||this.basename||s&&(s.name||s.basename)||"");if(!c)return"";const o=s&&typeof s.dirname=="string"?s.dirname:"";if(o){const t=n(o,c);if(a(t))return t}const m=new URLSearchParams(window.location.search||"").get("dir")||"/",r=n(m,c);return a(r)?r:"/"+c},resolvedFileId(){const e=[this.fileid,this.fileId,this.fileInfo&&(this.fileInfo.fileid||this.fileInfo.fileId||this.fileInfo.id)];for(const a of e){const s=Number(a);if(Number.isFinite(s)&&s>0)return s}if(new URLSearchParams(window.location.search||"").get("openfile")!=="true")return null;const i=(window.location.pathname||"").match(/\/apps\/files\/files\/(\d+)\/?$/);if(!i)return null;const n=Number(i[1]);return Number.isFinite(n)&&n>0?n:null}},watch:{filePath:{immediate:!0,handler(){this.resolveOpenUrl()}},resolvedFileId(){this.resolveOpenUrl()}},methods:{async fetchOpenPayload(e,i={}){const n=Object.assign({Accept:"application/json"},i.headers||{}),a=await fetch(e,Object.assign({method:"GET",credentials:"same-origin",headers:n},i)),s=await a.json().catch(()=>({}));if(!a.ok){const d=new Error(s&&s.message||"Pad open failed.");throw s&&typeof s.code=="string"&&(d.code=s.code),d}if(!s||s.is_readonly_snapshot!==!0&&(typeof s.url!="string"||s.url.trim()===""))throw new Error("Pad open API did not return a valid URL.");return s},isMissingFrontmatterError(e){return e instanceof Error?String(e.message||"").includes("Missing YAML frontmatter"):!1},async initializeMissingFrontmatter(){const e={Accept:"application/json",requesttoken:w()},i=(o,m)=>{const r=new Error(o&&o.message||m);return o&&typeof o.code=="string"&&o.code!==""&&(r.code=o.code),r},n=o=>{o&&o.status==="migrated_from_legacy"&&console.info("Legacy Ownpad .pad migrated to managed format on first open.")};if(this.resolvedFileId!==null){const o=y("/apps/"+v+"/api/v1/pads/initialize-by-id/"+encodeURIComponent(String(this.resolvedFileId))),m=await fetch(o,{method:"POST",credentials:"same-origin",headers:e}),r=await m.json().catch(()=>({}));if(!m.ok)throw i(r,"Pad initialization failed.");return n(r),r}if(!this.filePath)throw new Error("Pad initialization failed: missing file path.");const a=new URLSearchParams;a.set("file",this.filePath);const s=y("/apps/"+v+"/api/v1/pads/initialize"),d=await fetch(s,{method:"POST",credentials:"same-origin",headers:Object.assign({},e,{"Content-Type":"application/x-www-form-urlencoded;charset=UTF-8"}),body:a.toString()}),c=await d.json().catch(()=>({}));if(!d.ok)throw i(c,"Pad initialization failed.");return n(c),c},markLoaded(){this.$emit("update:loaded",!0)},padSync(){return this._padSync||(this._padSync=C({requestToken:()=>w()})),this._padSync},teardownSync(){this._padSync&&(this._padSync.fireAndForget(!0,!0),this._padSync.stop(),this._padSync.removeLifecycleHandlers())},async resolveOpenUrl(){const e=++this.resolveGeneration,i=()=>e===this.resolveGeneration;if(this.isLoading=!0,this.loadError="",this.canRecover=!1,this.isCheckingOriginal=!1,this.originalPad=null,this.iframeSrc="",this.externalOpenUrl="",this.externalOpenMessage="",this.snapshotMode="",this.snapshot={text:"",html:""},this._padSync&&(this._padSync.stop(),this._padSync.configure({syncUrl:""})),!this.filePath){if(!i())return;this.loadError="No .pad file selected.",this.isLoading=!1;return}const n=b(),a=y("/apps/"+v+"/api/v1/pads/open"),s=(()=>{if(!n)return"";const r=new URL(y("/apps/"+v+"/api/v1/public/open/"+encodeURIComponent(n)),window.location.origin);return r.searchParams.set("file",this.filePath),r.toString()})(),d=this.resolvedFileId!==null?y("/apps/"+v+"/api/v1/pads/open-by-id"):"",c=new URLSearchParams;c.set("file",this.filePath);const o=new URLSearchParams;this.resolvedFileId!==null&&o.set("fileId",String(this.resolvedFileId));const m={"Content-Type":"application/x-www-form-urlencoded;charset=UTF-8",requesttoken:w()};try{const r=async()=>{let p=null;if(s)p=await this.fetchOpenPayload(s);else if(d)try{p=await this.fetchOpenPayload(d,{method:"POST",headers:m,body:o.toString()})}catch{}return p||(p=await this.fetchOpenPayload(a,{method:"POST",headers:m,body:c.toString()})),p};let t;try{if(t=await r(),!i())return}catch(p){if(!this.isMissingFrontmatterError(p))throw p;if(await this.initializeMissingFrontmatter(),!i()||(t=await r(),!i()))return}const h=t&&typeof t.sync_url=="string"?t.sync_url:"",g=Number(t&&t.sync_interval_seconds),P=Number.isFinite(g)&&g>0?Math.max(5e3,Math.min(36e5,g*1e3)):12e4;if(this.padSync().configure({syncUrl:h,intervalMs:P}),this.padSync().installLifecycleHandlers(),h&&this.padSync().start(),t&&t.is_readonly_snapshot===!0){this.snapshotMode="readonly",this.snapshot={text:typeof t.snapshot_text=="string"?t.snapshot_text:"",html:typeof t.snapshot_html=="string"?t.snapshot_html:""},this.markLoaded();return}if(t&&t.is_external===!0&&typeof t.url=="string"&&t.url.trim()!==""){const p=t.url.trim();this.externalOpenUrl=p,this.externalOpenMessage=l("Read-only snapshot from the .pad file."),this.snapshotMode="external",this.snapshot={text:t&&typeof t.snapshot_text=="string"?t.snapshot_text:"",html:t&&typeof t.snapshot_html=="string"?t.snapshot_html:""},this.markLoaded();return}this.iframeSrc=t.url,this.markLoaded()}catch(r){if(!i())return;this.loadError=r instanceof Error?r.message:"Could not load pad.",this.canRecover=!!(r&&r.code==="missing_binding")&&this.resolvedFileId!==null&&!b(),this.canRecover&&this.fetchOriginalPadHint(e,i),this.markLoaded()}finally{if(!i())return;this.isLoading=!1}},async fetchOriginalPadHint(e,i){if(this.resolvedFileId!==null){this.isCheckingOriginal=!0;try{const n=await R(this.resolvedFileId);if(!i())return;n&&n.found===!0&&typeof n.viewer_url=="string"&&n.viewer_url!==""&&(this.originalPad={viewerUrl:n.viewer_url,path:typeof n.path=="string"?n.path:""})}catch{}finally{i()&&(this.isCheckingOriginal=!1)}}},async recoverFromSnapshot(){if(!(!this.canRecover||this.isRecovering||this.resolvedFileId===null)){this.isRecovering=!0;try{await k(this.resolvedFileId),this.loadError="",this.canRecover=!1,await this.resolveOpenUrl()}catch(e){this.loadError=e instanceof Error?e.message:"Could not load pad."}finally{this.isRecovering=!1}}},renderSnapshotView(e,i){const n=L(i.html),a=String(i.text||""),s=Array.isArray(i.actions)?i.actions:[];return e("div",{class:"epnc-native-snapshot"},[e("div",{class:"epnc-native-snapshot__inner"},[e("div",{class:"epnc-native-snapshot__header"},[e("div",{class:"epnc-native-snapshot__heading"},[e("div",{class:"epnc-native-snapshot__title"},i.title),e("div",{class:"epnc-native-snapshot__message"},i.message)]),s.length>0?e("div",{class:"epnc-native-snapshot__actions"},s):null]),n.trim()!==""?e("div",{class:"epnc-native-snapshot__text epnc-native-snapshot__text--html",domProps:{innerHTML:n}}):e("pre",{class:"epnc-native-snapshot__text"},a.trim()!==""?a:i.emptyMessage)])])}},beforeDestroy(){this.resolveGeneration+=1,this.teardownSync()},beforeUnmount(){this.resolveGeneration+=1,this.teardownSync()},render(e){if(this.loadError){const i=[e("div",{class:"epnc-native-error-title"},l("Could not open pad")),e("div",{class:"epnc-native-error-message"},this.loadError)];return this.canRecover&&(this.isCheckingOriginal?i.push(e("div",{class:"epnc-native-error-message"},l("Checking for the original pad..."))):this.originalPad?i.push(e("div",{class:"epnc-native-error-message"},l("This file looks like a copy of an existing .pad file in your account. Open the original to keep editing the linked pad, or create a new pad to fork the content stored in this file.")),e("a",{class:"button primary epnc-native-error-action",attrs:{href:this.originalPad.viewerUrl}},l("Open the original .pad file")),e("button",{class:"button epnc-native-error-action",attrs:{type:"button",disabled:this.isRecovering},on:{click:()=>{this.recoverFromSnapshot()}}},this.isRecovering?l("Creating new pad..."):l("Create new pad from this file"))):i.push(e("div",{class:"epnc-native-error-message"},l("We couldn't find a matching pad in this Nextcloud. You can create a new pad from the text stored in this file; from then on, opening this file will load the new pad.")),e("button",{class:"button primary epnc-native-error-action",attrs:{type:"button",disabled:this.isRecovering},on:{click:()=>{this.recoverFromSnapshot()}}},this.isRecovering?l("Creating new pad..."):l("Create new pad from this file")))),e("div",{class:"epnc-native-status epnc-native-status--error"},[e("div",{class:"epnc-native-error-card"},i)])}return this.snapshotMode==="external"?this.renderSnapshotView(e,{title:l("Pad from another server"),message:this.externalOpenMessage,actions:[e("a",{class:"button primary",attrs:{href:this.externalOpenUrl,target:"_blank",rel:"noopener noreferrer"}},l("Open original pad"))],html:this.snapshot.html,text:this.snapshot.text,emptyMessage:l("No synced snapshot is stored in this .pad file yet.")}):this.snapshotMode==="readonly"?this.renderSnapshotView(e,{title:l("Read-only snapshot"),message:l("Read-only snapshot from the .pad file."),html:this.snapshot.html,text:this.snapshot.text,emptyMessage:l("No synced snapshot is stored in this .pad file yet.")}):this.isLoading||!this.iframeSrc?e("div",{class:"epnc-native-status"},"Loading pad..."):e("div",{class:"epnc-native-shell"},[e("iframe",{attrs:{srcdoc:E(this.iframeSrc),title:"Etherpad"},on:{load:()=>this.markLoaded(),error:()=>this.markLoaded()},class:"epnc-native-iframe"})])}},S=()=>{if(f+=1,!(window.OCA&&window.OCA.Viewer&&typeof window.OCA.Viewer.registerHandler=="function")){f<20&&window.setTimeout(S,500);return}Array.isArray(window.OCA.Viewer.availableHandlers)&&window.OCA.Viewer.availableHandlers.some(e=>e&&e.id===_)||window.OCA.Viewer.registerHandler({id:_,mimes:[x],component:u})};S()})();
//# sourceMappingURL=etherpad_nextcloud-viewer-main.mjs.map
diff --git a/js/etherpad_nextcloud-viewer-main.mjs.map b/js/etherpad_nextcloud-viewer-main.mjs.map
index ce8e231..9557db4 100644
--- a/js/etherpad_nextcloud-viewer-main.mjs.map
+++ b/js/etherpad_nextcloud-viewer-main.mjs.map
@@ -1 +1 @@
-{"version":3,"file":"etherpad_nextcloud-viewer-main.mjs","sources":["../src/lib/pad-frame-srcdoc.js","../src/viewer-main.js"],"sourcesContent":["/**\n * SPDX-License-Identifier: AGPL-3.0-or-later\n * Copyright (c) 2026 Jacob Bühler\n */\n\nconst escapeAttribute = (value) => String(value || '')\n\t.replace(/&/g, '&')\n\t.replace(/\"/g, '"')\n\t.replace(//g, '>')\n\nconst ALLOWED_PAD_URL_SCHEMES = ['http:', 'https:']\n\nconst isSafePadUrl = (url) => {\n\ttry {\n\t\tconst parsed = new URL(String(url || ''))\n\t\treturn ALLOWED_PAD_URL_SCHEMES.includes(parsed.protocol)\n\t\t\t&& parsed.username === ''\n\t\t\t&& parsed.password === ''\n\t} catch {\n\t\treturn false\n\t}\n}\n\nconst SRC_DOC_CSP = \"default-src 'none'; frame-src http: https:; style-src 'unsafe-inline'\"\n\nexport const buildPadFrameSrcdoc = (url) => {\n\tconst safeUrl = isSafePadUrl(url) ? escapeAttribute(url) : ''\n\treturn ''\n\t+ ''\n\t+ ''\n\t+ ''\n}\n","/**\n * SPDX-License-Identifier: AGPL-3.0-or-later\n * Copyright (c) 2026 Jacob Bühler\n */\nimport { APP_ID, MIME, VIEWER_HANDLER_ID } from './lib/constants.js'\nimport { apiFindOriginalPad, apiRecoverFromSnapshot } from './lib/api-client.js'\nimport { ocGenerateUrl, ocRequestToken, translate } from './lib/oc-compat.js'\nimport { createPadSync } from './lib/pad-sync.js'\nimport { buildPadFrameSrcdoc } from './lib/pad-frame-srcdoc.js'\nimport { parsePadPathFromDavHref, parsePublicShareTokenFromLocation } from './lib/urls.js'\n\n(function () {\n\tlet attempts = 0\n\n\tconst component = {\n\t\tname: 'EtherpadNextcloudViewer',\n\t\tprops: {\n\t\t\tfilename: { type: String, required: false, default: '' },\n\t\t\tbasename: { type: String, required: false, default: '' },\n\t\t\tsource: { type: String, required: false, default: '' },\n\t\t\tfileid: { type: [String, Number], required: false, default: null },\n\t\t\tfileId: { type: [String, Number], required: false, default: null },\n\t\t\tfileInfo: { type: Object, required: false, default: null },\n\t\t},\n\t\tdata() {\n\t\t\treturn {\n\t\t\t\tiframeSrc: '',\n\t\t\t\tisLoading: true,\n\t\t\t\tloadError: '',\n\t\t\t\tcanRecover: false,\n\t\t\t\tisRecovering: false,\n\t\t\t\tisCheckingOriginal: false,\n\t\t\t\toriginalPad: null,\n\t\t\t\texternalOpenUrl: '',\n\t\t\t\texternalOpenMessage: '',\n\t\t\t\tsnapshotMode: '',\n\t\t\t\tsnapshot: { text: '', html: '' },\n\t\t\t\tresolveGeneration: 0,\n\t\t\t}\n\t\t},\n\t\tcomputed: {\n\t\t\tsourcePath() {\n\t\t\t\tconst value = typeof this.source === 'string' ? this.source.trim() : ''\n\t\t\t\tif (!value) return ''\n\t\t\t\treturn parsePadPathFromDavHref(value) || ''\n\t\t\t},\n\t\t\tfilePath() {\n\t\t\t\tconst normalizeName = (value) => String(value || '').trim().replace(/\\s+\\.pad$/i, '.pad')\n\t\t\t\tconst normalizeDir = (value) => {\n\t\t\t\t\tconst dir = String(value || '').trim()\n\t\t\t\t\tif (!dir || dir === '/') return '/'\n\t\t\t\t\treturn dir.startsWith('/') ? dir : ('/' + dir)\n\t\t\t\t}\n\t\t\t\tconst joinPath = (dir, name) => {\n\t\t\t\t\tif (!name) return ''\n\t\t\t\t\tif (name.startsWith('/')) return name\n\t\t\t\t\tconst normalizedDir = normalizeDir(dir)\n\t\t\t\t\treturn normalizedDir === '/' ? '/' + name : normalizedDir + '/' + name\n\t\t\t\t}\n\t\t\t\tconst isPadPath = (value) => typeof value === 'string' && value.toLowerCase().endsWith('.pad')\n\t\t\t\tif (isPadPath(this.sourcePath)) return this.sourcePath\n\n\t\t\t\tconst info = this.fileInfo && typeof this.fileInfo === 'object' ? this.fileInfo : null\n\t\t\t\tconst infoPath = info && typeof info.path === 'string' ? normalizeName(info.path) : ''\n\t\t\t\tif (isPadPath(infoPath)) return infoPath.startsWith('/') ? infoPath : ('/' + infoPath)\n\n\t\t\t\tconst baseName = normalizeName(this.filename || this.basename || (info && (info.name || info.basename)) || '')\n\t\t\t\tif (!baseName) return ''\n\n\t\t\t\tconst infoDir = info && typeof info.dirname === 'string' ? info.dirname : ''\n\t\t\t\tif (infoDir) {\n\t\t\t\t\tconst combined = joinPath(infoDir, baseName)\n\t\t\t\t\tif (isPadPath(combined)) return combined\n\t\t\t\t}\n\n\t\t\t\tconst params = new URLSearchParams(window.location.search || '')\n\t\t\t\tconst urlDir = params.get('dir') || '/'\n\t\t\t\tconst fromDir = joinPath(urlDir, baseName)\n\t\t\t\tif (isPadPath(fromDir)) return fromDir\n\t\t\t\treturn '/' + baseName\n\t\t\t},\n\t\t\tresolvedFileId() {\n\t\t\t\tconst candidates = [this.fileid, this.fileId, this.fileInfo && (this.fileInfo.fileid || this.fileInfo.fileId || this.fileInfo.id)]\n\t\t\t\tfor (const candidate of candidates) {\n\t\t\t\t\tconst numeric = Number(candidate)\n\t\t\t\t\tif (Number.isFinite(numeric) && numeric > 0) return numeric\n\t\t\t\t}\n\t\t\t\tconst params = new URLSearchParams(window.location.search || '')\n\t\t\t\tif (params.get('openfile') !== 'true') return null\n\t\t\t\tconst match = (window.location.pathname || '').match(/\\/apps\\/files\\/files\\/(\\d+)\\/?$/)\n\t\t\t\tif (!match) return null\n\t\t\t\tconst fallbackId = Number(match[1])\n\t\t\t\treturn Number.isFinite(fallbackId) && fallbackId > 0 ? fallbackId : null\n\t\t\t},\n\t\t},\n\t\twatch: {\n\t\t\tfilePath: { immediate: true, handler() { void this.resolveOpenUrl() } },\n\t\t\tresolvedFileId() { void this.resolveOpenUrl() },\n\t\t},\n\t\tmethods: {\n\t\t\tasync fetchOpenPayload(url, init = {}) {\n\t\t\t\tconst headers = Object.assign({ Accept: 'application/json' }, init.headers || {})\n\t\t\t\tconst response = await fetch(url, Object.assign({\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tcredentials: 'same-origin',\n\t\t\t\t\theaders,\n\t\t\t\t}, init))\n\t\t\t\tconst data = await response.json().catch(() => ({}))\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tconst error = new Error((data && data.message) || 'Pad open failed.')\n\t\t\t\t\tif (data && typeof data.code === 'string') {\n\t\t\t\t\t\terror.code = data.code\n\t\t\t\t\t}\n\t\t\t\t\tthrow error\n\t\t\t\t}\n\t\t\t\tif (!data || (data.is_readonly_snapshot !== true && (typeof data.url !== 'string' || data.url.trim() === ''))) {\n\t\t\t\t\tthrow new Error('Pad open API did not return a valid URL.')\n\t\t\t\t}\n\t\t\t\treturn data\n\t\t\t},\n\t\t\tisMissingFrontmatterError(error) {\n\t\t\t\tif (!(error instanceof Error)) return false\n\t\t\t\treturn String(error.message || '').includes('Missing YAML frontmatter')\n\t\t\t},\n\t\t\tasync initializeMissingFrontmatter() {\n\t\t\t\tconst headers = {\n\t\t\t\t\tAccept: 'application/json',\n\t\t\t\t\trequesttoken: ocRequestToken(),\n\t\t\t\t}\n\n\t\t\t\tconst buildInitError = (data, fallbackMessage) => {\n\t\t\t\t\tconst err = new Error((data && data.message) || fallbackMessage)\n\t\t\t\t\t// Forward a structured code so callers can branch on\n\t\t\t\t\t// `legacy_collision_no_access` without parsing the message.\n\t\t\t\t\tif (data && typeof data.code === 'string' && data.code !== '') {\n\t\t\t\t\t\terr.code = data.code\n\t\t\t\t\t}\n\t\t\t\t\treturn err\n\t\t\t\t}\n\n\t\t\t\tconst announceMigratedStatus = (data) => {\n\t\t\t\t\tif (data && data.status === 'migrated_from_legacy') {\n\t\t\t\t\t\t// Audit-visible on the backend; mirror it in the\n\t\t\t\t\t\t// browser console so dev tools makes the conversion\n\t\t\t\t\t\t// visible without surfacing a UI toast (the codebase\n\t\t\t\t\t\t// has no toast infra wired yet).\n\t\t\t\t\t\tconsole.info('Legacy Ownpad .pad migrated to managed format on first open.')\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (this.resolvedFileId !== null) {\n\t\t\t\t\tconst url = ocGenerateUrl('/apps/' + APP_ID + '/api/v1/pads/initialize-by-id/' + encodeURIComponent(String(this.resolvedFileId)))\n\t\t\t\t\tconst response = await fetch(url, { method: 'POST', credentials: 'same-origin', headers })\n\t\t\t\t\tconst data = await response.json().catch(() => ({}))\n\t\t\t\t\tif (!response.ok) {\n\t\t\t\t\t\tthrow buildInitError(data, 'Pad initialization failed.')\n\t\t\t\t\t}\n\t\t\t\t\tannounceMigratedStatus(data)\n\t\t\t\t\treturn data\n\t\t\t\t}\n\n\t\t\t\tif (!this.filePath) {\n\t\t\t\t\tthrow new Error('Pad initialization failed: missing file path.')\n\t\t\t\t}\n\n\t\t\t\tconst body = new URLSearchParams()\n\t\t\t\tbody.set('file', this.filePath)\n\t\t\t\tconst url = ocGenerateUrl('/apps/' + APP_ID + '/api/v1/pads/initialize')\n\t\t\t\tconst response = await fetch(url, {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\tcredentials: 'same-origin',\n\t\t\t\t\theaders: Object.assign({}, headers, {\n\t\t\t\t\t\t'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',\n\t\t\t\t\t}),\n\t\t\t\t\tbody: body.toString(),\n\t\t\t\t})\n\t\t\t\tconst data = await response.json().catch(() => ({}))\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthrow buildInitError(data, 'Pad initialization failed.')\n\t\t\t\t}\n\t\t\t\tannounceMigratedStatus(data)\n\t\t\t\treturn data\n\t\t\t},\n\t\t\tmarkLoaded() {\n\t\t\t\tthis.$emit('update:loaded', true)\n\t\t\t},\n\t\t\t// Lazily build the shared sync controller. Kept off `data()` on\n\t\t\t// purpose so it is not made reactive, and memoised on a plain\n\t\t\t// instance field so it survives the immediate filePath watcher\n\t\t\t// (which runs before created/mounted).\n\t\t\tpadSync() {\n\t\t\t\tif (!this._padSync) {\n\t\t\t\t\tthis._padSync = createPadSync({ requestToken: () => ocRequestToken() })\n\t\t\t\t}\n\t\t\t\treturn this._padSync\n\t\t\t},\n\t\t\t// Final flush + full teardown on destroy. Guard on the existing\n\t\t\t// controller so we never spin one up just to tear it down (a viewer\n\t\t\t// destroyed before its first open). The lazy create stays in the\n\t\t\t// resolve path, which actually needs to sync.\n\t\t\tteardownSync() {\n\t\t\t\tif (!this._padSync) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tthis._padSync.fireAndForget(true, true)\n\t\t\t\tthis._padSync.stop()\n\t\t\t\tthis._padSync.removeLifecycleHandlers()\n\t\t\t},\n\t\t\tasync resolveOpenUrl() {\n\t\t\t\tconst generation = ++this.resolveGeneration\n\t\t\t\tconst isCurrent = () => generation === this.resolveGeneration\n\n\t\t\t\tthis.isLoading = true\n\t\t\t\tthis.loadError = ''\n\t\t\t\tthis.canRecover = false\n\t\t\t\tthis.isCheckingOriginal = false\n\t\t\t\tthis.originalPad = null\n\t\t\t\tthis.iframeSrc = ''\n\t\t\t\tthis.externalOpenUrl = ''\n\t\t\t\tthis.externalOpenMessage = ''\n\t\t\t\tthis.snapshotMode = ''\n\t\t\t\tthis.snapshot = { text: '', html: '' }\n\t\t\t\t// Reset only an existing controller; don't construct one just to\n\t\t\t\t// stop/clear it (e.g. the initial immediate watcher with no pad).\n\t\t\t\t// The success path below lazily creates it when there's a pad to\n\t\t\t\t// actually sync.\n\t\t\t\tif (this._padSync) {\n\t\t\t\t\tthis._padSync.stop()\n\t\t\t\t\tthis._padSync.configure({ syncUrl: '' })\n\t\t\t\t}\n\n\t\t\t\tif (!this.filePath) {\n\t\t\t\t\tif (!isCurrent()) return\n\t\t\t\t\tthis.loadError = 'No .pad file selected.'\n\t\t\t\t\tthis.isLoading = false\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tconst publicToken = parsePublicShareTokenFromLocation()\n\t\t\t\tconst byPathUrl = ocGenerateUrl('/apps/' + APP_ID + '/api/v1/pads/open')\n\t\t\t\tconst byPublicUrl = (() => {\n\t\t\t\t\tif (!publicToken) return ''\n\t\t\t\t\tconst url = new URL(ocGenerateUrl('/apps/' + APP_ID + '/api/v1/public/open/' + encodeURIComponent(publicToken)), window.location.origin)\n\t\t\t\t\turl.searchParams.set('file', this.filePath)\n\t\t\t\t\treturn url.toString()\n\t\t\t\t})()\n\t\t\t\tconst byIdUrl = this.resolvedFileId !== null\n\t\t\t\t\t? ocGenerateUrl('/apps/' + APP_ID + '/api/v1/pads/open-by-id')\n\t\t\t\t\t: ''\n\t\t\t\tconst byPathBody = new URLSearchParams()\n\t\t\t\tbyPathBody.set('file', this.filePath)\n\t\t\t\tconst byIdBody = new URLSearchParams()\n\t\t\t\tif (this.resolvedFileId !== null) {\n\t\t\t\t\tbyIdBody.set('fileId', String(this.resolvedFileId))\n\t\t\t\t}\n\t\t\t\tconst openPostHeaders = {\n\t\t\t\t\t'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',\n\t\t\t\t\trequesttoken: ocRequestToken(),\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tconst fetchOpenData = async () => {\n\t\t\t\t\t\t\tlet data = null\n\t\t\t\t\t\t\tif (byPublicUrl) {\n\t\t\t\t\t\t\t\tdata = await this.fetchOpenPayload(byPublicUrl)\n\t\t\t\t\t\t\t} else if (byIdUrl) {\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tdata = await this.fetchOpenPayload(byIdUrl, {\n\t\t\t\t\t\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\t\t\t\t\t\theaders: openPostHeaders,\n\t\t\t\t\t\t\t\t\t\tbody: byIdBody.toString(),\n\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\t\t// Fallback for moved/renamed files where stale fileId can fail.\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (!data) {\n\t\t\t\t\t\t\t\tdata = await this.fetchOpenPayload(byPathUrl, {\n\t\t\t\t\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\t\t\t\t\theaders: openPostHeaders,\n\t\t\t\t\t\t\t\t\tbody: byPathBody.toString(),\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn data\n\t\t\t\t\t\t}\n\n\t\t\t\t\tlet data\n\t\t\t\t\ttry {\n\t\t\t\t\t\tdata = await fetchOpenData()\n\t\t\t\t\t\tif (!isCurrent()) return\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tif (!this.isMissingFrontmatterError(error)) {\n\t\t\t\t\t\t\tthrow error\n\t\t\t\t\t\t}\n\t\t\t\t\t\tawait this.initializeMissingFrontmatter()\n\t\t\t\t\t\tif (!isCurrent()) return\n\t\t\t\t\t\tdata = await fetchOpenData()\n\t\t\t\t\t\tif (!isCurrent()) return\n\t\t\t\t\t}\n\n\t\t\t\t\tconst syncUrl = (data && typeof data.sync_url === 'string') ? data.sync_url : ''\n\n\t\t\t\t\tconst intervalSeconds = Number(data && data.sync_interval_seconds)\n\t\t\t\t\tconst intervalMs = (Number.isFinite(intervalSeconds) && intervalSeconds > 0)\n\t\t\t\t\t\t? Math.max(5000, Math.min(3600000, intervalSeconds * 1000))\n\t\t\t\t\t\t: 120000\n\n\t\t\t\t\tthis.padSync().configure({ syncUrl, intervalMs })\n\t\t\t\t\tthis.padSync().installLifecycleHandlers()\n\t\t\t\t\tif (syncUrl) {\n\t\t\t\t\t\tthis.padSync().start()\n\t\t\t\t\t}\n\n\t\t\t\t\tif (data && data.is_readonly_snapshot === true) {\n\t\t\t\t\t\tthis.snapshotMode = 'readonly'\n\t\t\t\t\t\tthis.snapshot = {\n\t\t\t\t\t\t\ttext: (typeof data.snapshot_text === 'string') ? data.snapshot_text : '',\n\t\t\t\t\t\t\thtml: (typeof data.snapshot_html === 'string') ? data.snapshot_html : '',\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthis.markLoaded()\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\tif (data && data.is_external === true && typeof data.url === 'string' && data.url.trim() !== '') {\n\t\t\t\t\t\tconst targetUrl = data.url.trim()\n\t\t\t\t\t\tthis.externalOpenUrl = targetUrl\n\t\t\t\t\t\tthis.externalOpenMessage = translate('Read-only snapshot from the .pad file.')\n\t\t\t\t\t\tthis.snapshotMode = 'external'\n\t\t\t\t\t\tthis.snapshot = {\n\t\t\t\t\t\t\ttext: (data && typeof data.snapshot_text === 'string') ? data.snapshot_text : '',\n\t\t\t\t\t\t\thtml: (data && typeof data.snapshot_html === 'string') ? data.snapshot_html : '',\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthis.markLoaded()\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\tthis.iframeSrc = data.url\n\t\t\t\t\tthis.markLoaded()\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!isCurrent()) return\n\t\t\t\t\tthis.loadError = error instanceof Error ? error.message : 'Could not load pad.'\n\t\t\t\t\t// Recovery is gated on having a fileId we can address. Public-share\n\t\t\t\t\t// visitors don't get a recovery action — only the share owner.\n\t\t\t\t\tthis.canRecover = Boolean(error && error.code === 'missing_binding')\n\t\t\t\t\t\t&& this.resolvedFileId !== null\n\t\t\t\t\t\t&& !parsePublicShareTokenFromLocation()\n\t\t\t\t\tif (this.canRecover) {\n\t\t\t\t\t\t// Optional: check if this looks like a copy of a .pad we\n\t\t\t\t\t\t// can already address; if so we'll offer 'Open the\n\t\t\t\t\t\t// original' as the primary action. A miss is silent — no\n\t\t\t\t\t\t// UI element rendered, no info leaked.\n\t\t\t\t\t\tthis.fetchOriginalPadHint(generation, isCurrent)\n\t\t\t\t\t}\n\t\t\t\t\tthis.markLoaded()\n\t\t\t\t} finally {\n\t\t\t\t\tif (!isCurrent()) return\n\t\t\t\t\tthis.isLoading = false\n\t\t\t\t}\n\t\t\t},\n\t\t\tasync fetchOriginalPadHint(generation, isCurrent) {\n\t\t\t\tif (this.resolvedFileId === null) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tthis.isCheckingOriginal = true\n\t\t\t\ttry {\n\t\t\t\t\tconst hint = await apiFindOriginalPad(this.resolvedFileId)\n\t\t\t\t\tif (!isCurrent()) return\n\t\t\t\t\tif (hint && hint.found === true && typeof hint.viewer_url === 'string' && hint.viewer_url !== '') {\n\t\t\t\t\t\tthis.originalPad = {\n\t\t\t\t\t\t\tviewerUrl: hint.viewer_url,\n\t\t\t\t\t\t\tpath: typeof hint.path === 'string' ? hint.path : '',\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Silent: the recovery button stays available, we just\n\t\t\t\t\t// don't surface the \"Open the original\" affordance.\n\t\t\t\t} finally {\n\t\t\t\t\tif (isCurrent()) {\n\t\t\t\t\t\tthis.isCheckingOriginal = false\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\tasync recoverFromSnapshot() {\n\t\t\t\tif (!this.canRecover || this.isRecovering || this.resolvedFileId === null) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tthis.isRecovering = true\n\t\t\t\ttry {\n\t\t\t\t\tawait apiRecoverFromSnapshot(this.resolvedFileId)\n\t\t\t\t\tthis.loadError = ''\n\t\t\t\t\tthis.canRecover = false\n\t\t\t\t\tawait this.resolveOpenUrl()\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthis.loadError = error instanceof Error ? error.message : 'Could not load pad.'\n\t\t\t\t} finally {\n\t\t\t\t\tthis.isRecovering = false\n\t\t\t\t}\n\t\t\t},\n\t\t\trenderSnapshotView(createElement, options) {\n\t\t\t\tconst html = String(options.html || '')\n\t\t\t\tconst text = String(options.text || '')\n\t\t\t\tconst actions = Array.isArray(options.actions) ? options.actions : []\n\n\t\t\t\treturn createElement('div', { class: 'epnc-native-snapshot' }, [\n\t\t\t\t\tcreateElement('div', { class: 'epnc-native-snapshot__inner' }, [\n\t\t\t\t\t\tcreateElement('div', { class: 'epnc-native-snapshot__header' }, [\n\t\t\t\t\t\t\tcreateElement('div', { class: 'epnc-native-snapshot__heading' }, [\n\t\t\t\t\t\t\t\tcreateElement('div', { class: 'epnc-native-snapshot__title' }, options.title),\n\t\t\t\t\t\t\t\tcreateElement('div', { class: 'epnc-native-snapshot__message' }, options.message),\n\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t\tactions.length > 0\n\t\t\t\t\t\t\t\t? createElement('div', { class: 'epnc-native-snapshot__actions' }, actions)\n\t\t\t\t\t\t\t\t: null,\n\t\t\t\t\t\t]),\n\t\t\t\t\t\thtml.trim() !== ''\n\t\t\t\t\t\t\t? createElement('div', {\n\t\t\t\t\t\t\t\tclass: 'epnc-native-snapshot__text epnc-native-snapshot__text--html',\n\t\t\t\t\t\t\t\tdomProps: { innerHTML: html },\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t: createElement('pre', { class: 'epnc-native-snapshot__text' }, text.trim() !== ''\n\t\t\t\t\t\t\t\t? text\n\t\t\t\t\t\t\t\t: options.emptyMessage),\n\t\t\t\t\t]),\n\t\t\t\t])\n\t\t\t},\n\t\t},\n\t\tbeforeDestroy() {\n\t\t\tthis.resolveGeneration += 1\n\t\t\tthis.teardownSync()\n\t\t},\n\t\tbeforeUnmount() {\n\t\t\tthis.resolveGeneration += 1\n\t\t\tthis.teardownSync()\n\t\t},\n\t\trender(createElement) {\n\t\t\tif (this.loadError) {\n\t\t\t\tconst cardChildren = [\n\t\t\t\t\tcreateElement('div', { class: 'epnc-native-error-title' }, translate('Could not open pad')),\n\t\t\t\t\tcreateElement('div', { class: 'epnc-native-error-message' }, this.loadError),\n\t\t\t\t]\n\t\t\t\tif (this.canRecover) {\n\t\t\t\t\tif (this.isCheckingOriginal) {\n\t\t\t\t\t\t// Don't render any action button while the lookup is in\n\t\t\t\t\t\t// flight: a slow connection could otherwise let the user\n\t\t\t\t\t\t// click 'Create new pad' before we know that opening the\n\t\t\t\t\t\t// original is the better default.\n\t\t\t\t\t\tcardChildren.push(\n\t\t\t\t\t\t\tcreateElement('div', { class: 'epnc-native-error-message' },\n\t\t\t\t\t\t\t\ttranslate('Checking for the original pad...')),\n\t\t\t\t\t\t)\n\t\t\t\t\t} else if (this.originalPad) {\n\t\t\t\t\t\tcardChildren.push(\n\t\t\t\t\t\t\tcreateElement('div', { class: 'epnc-native-error-message' },\n\t\t\t\t\t\t\t\ttranslate('This file looks like a copy of an existing .pad file in your account. Open the original to keep editing the linked pad, or create a new pad to fork the content stored in this file.')),\n\t\t\t\t\t\t\tcreateElement('a', {\n\t\t\t\t\t\t\t\tclass: 'button primary epnc-native-error-action',\n\t\t\t\t\t\t\t\tattrs: { href: this.originalPad.viewerUrl },\n\t\t\t\t\t\t\t}, translate('Open the original .pad file')),\n\t\t\t\t\t\t\tcreateElement('button', {\n\t\t\t\t\t\t\t\tclass: 'button epnc-native-error-action',\n\t\t\t\t\t\t\t\tattrs: { type: 'button', disabled: this.isRecovering },\n\t\t\t\t\t\t\t\ton: { click: () => { void this.recoverFromSnapshot() } },\n\t\t\t\t\t\t\t}, this.isRecovering ? translate('Creating new pad...') : translate('Create new pad from this file')),\n\t\t\t\t\t\t)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcardChildren.push(\n\t\t\t\t\t\t\tcreateElement('div', { class: 'epnc-native-error-message' },\n\t\t\t\t\t\t\t\ttranslate(\"We couldn't find a matching pad in this Nextcloud. You can create a new pad from the text stored in this file; from then on, opening this file will load the new pad.\")),\n\t\t\t\t\t\t\tcreateElement('button', {\n\t\t\t\t\t\t\t\tclass: 'button primary epnc-native-error-action',\n\t\t\t\t\t\t\t\tattrs: { type: 'button', disabled: this.isRecovering },\n\t\t\t\t\t\t\t\ton: { click: () => { void this.recoverFromSnapshot() } },\n\t\t\t\t\t\t\t}, this.isRecovering ? translate('Creating new pad...') : translate('Create new pad from this file')),\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn createElement('div', { class: 'epnc-native-status epnc-native-status--error' }, [\n\t\t\t\t\tcreateElement('div', { class: 'epnc-native-error-card' }, cardChildren),\n\t\t\t\t])\n\t\t\t}\n\t\t\tif (this.snapshotMode === 'external') {\n\t\t\t\treturn this.renderSnapshotView(createElement, {\n\t\t\t\t\ttitle: translate('Pad from another server'),\n\t\t\t\t\tmessage: this.externalOpenMessage,\n\t\t\t\t\tactions: [\n\t\t\t\t\t\tcreateElement('a', {\n\t\t\t\t\t\t\tclass: 'button primary',\n\t\t\t\t\t\t\tattrs: {\n\t\t\t\t\t\t\t\thref: this.externalOpenUrl,\n\t\t\t\t\t\t\t\ttarget: '_blank',\n\t\t\t\t\t\t\t\trel: 'noopener noreferrer',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}, translate('Open original pad')),\n\t\t\t\t\t],\n\t\t\t\t\thtml: this.snapshot.html,\n\t\t\t\t\ttext: this.snapshot.text,\n\t\t\t\t\temptyMessage: translate('No synced snapshot is stored in this .pad file yet.'),\n\t\t\t\t})\n\t\t\t}\n\t\t\tif (this.snapshotMode === 'readonly') {\n\t\t\t\treturn this.renderSnapshotView(createElement, {\n\t\t\t\t\ttitle: translate('Read-only snapshot'),\n\t\t\t\t\tmessage: translate('Read-only snapshot from the .pad file.'),\n\t\t\t\t\thtml: this.snapshot.html,\n\t\t\t\t\ttext: this.snapshot.text,\n\t\t\t\t\temptyMessage: translate('No synced snapshot is stored in this .pad file yet.'),\n\t\t\t\t})\n\t\t\t}\n\t\t\tif (this.isLoading || !this.iframeSrc) {\n\t\t\t\treturn createElement('div', { class: 'epnc-native-status' }, 'Loading pad...')\n\t\t\t}\n\n\t\t\treturn createElement('div', { class: 'epnc-native-shell' }, [\n\t\t\t\t// Nextcloud Viewer tries to inspect/focus direct iframe children during\n\t\t\t\t// teardown. Keep the direct iframe same-origin via srcdoc, and put the\n\t\t\t\t// cross-origin Etherpad frame one level deeper.\n\t\t\t\tcreateElement('iframe', {\n\t\t\t\t\tattrs: { srcdoc: buildPadFrameSrcdoc(this.iframeSrc), title: 'Etherpad' },\n\t\t\t\t\t// This fires when the srcdoc wrapper is ready. Etherpad then continues\n\t\t\t\t\t// loading in the inner iframe and shows its own loading UI.\n\t\t\t\t\ton: { load: () => this.markLoaded(), error: () => this.markLoaded() },\n\t\t\t\t\tclass: 'epnc-native-iframe',\n\t\t\t\t}),\n\t\t\t])\n\t\t},\n\t}\n\n\tconst tryRegister = () => {\n\t\tattempts += 1\n\t\tif (!(window.OCA && window.OCA.Viewer && typeof window.OCA.Viewer.registerHandler === 'function')) {\n\t\t\tif (attempts < 20) window.setTimeout(tryRegister, 500)\n\t\t\treturn\n\t\t}\n\t\tif (Array.isArray(window.OCA.Viewer.availableHandlers)\n\t\t\t&& window.OCA.Viewer.availableHandlers.some((handler) => handler && handler.id === VIEWER_HANDLER_ID)) {\n\t\t\treturn\n\t\t}\n\t\twindow.OCA.Viewer.registerHandler({ id: VIEWER_HANDLER_ID, mimes: [MIME], component })\n\t}\n\n\ttryRegister()\n})()\n"],"names":["escapeAttribute","value","ALLOWED_PAD_URL_SCHEMES","isSafePadUrl","url","parsed","SRC_DOC_CSP","buildPadFrameSrcdoc","safeUrl","attempts","component","parsePadPathFromDavHref","normalizeName","normalizeDir","dir","joinPath","name","normalizedDir","isPadPath","info","infoPath","baseName","infoDir","combined","urlDir","fromDir","candidates","candidate","numeric","match","fallbackId","init","headers","response","data","error","ocRequestToken","buildInitError","fallbackMessage","err","announceMigratedStatus","ocGenerateUrl","APP_ID","body","createPadSync","generation","isCurrent","publicToken","parsePublicShareTokenFromLocation","byPathUrl","byPublicUrl","byIdUrl","byPathBody","byIdBody","openPostHeaders","fetchOpenData","syncUrl","intervalSeconds","intervalMs","targetUrl","translate","hint","apiFindOriginalPad","apiRecoverFromSnapshot","createElement","options","html","text","actions","cardChildren","tryRegister","handler","VIEWER_HANDLER_ID","MIME"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"etherpad_nextcloud-viewer-main.mjs","sources":["../src/lib/pad-frame-srcdoc.js","../src/viewer-main.js"],"sourcesContent":["/**\n * SPDX-License-Identifier: AGPL-3.0-or-later\n * Copyright (c) 2026 Jacob Bühler\n */\n\nconst escapeAttribute = (value) => String(value || '')\n\t.replace(/&/g, '&')\n\t.replace(/\"/g, '"')\n\t.replace(//g, '>')\n\nconst ALLOWED_PAD_URL_SCHEMES = ['http:', 'https:']\n\nconst isSafePadUrl = (url) => {\n\ttry {\n\t\tconst parsed = new URL(String(url || ''))\n\t\treturn ALLOWED_PAD_URL_SCHEMES.includes(parsed.protocol)\n\t\t\t&& parsed.username === ''\n\t\t\t&& parsed.password === ''\n\t} catch {\n\t\treturn false\n\t}\n}\n\nconst SRC_DOC_CSP = \"default-src 'none'; frame-src http: https:; style-src 'unsafe-inline'\"\n\nexport const buildPadFrameSrcdoc = (url) => {\n\tconst safeUrl = isSafePadUrl(url) ? escapeAttribute(url) : ''\n\treturn ''\n\t+ ''\n\t+ ''\n\t+ ''\n}\n","/**\n * SPDX-License-Identifier: AGPL-3.0-or-later\n * Copyright (c) 2026 Jacob Bühler\n */\nimport { APP_ID, MIME, VIEWER_HANDLER_ID } from './lib/constants.js'\nimport { apiFindOriginalPad, apiRecoverFromSnapshot } from './lib/api-client.js'\nimport { ocGenerateUrl, ocRequestToken, translate } from './lib/oc-compat.js'\nimport { createPadSync } from './lib/pad-sync.js'\nimport { sanitizeSnapshotHtml } from './lib/sanitize-html.js'\nimport { buildPadFrameSrcdoc } from './lib/pad-frame-srcdoc.js'\nimport { parsePadPathFromDavHref, parsePublicShareTokenFromLocation } from './lib/urls.js'\n\n(function () {\n\tlet attempts = 0\n\n\tconst component = {\n\t\tname: 'EtherpadNextcloudViewer',\n\t\tprops: {\n\t\t\tfilename: { type: String, required: false, default: '' },\n\t\t\tbasename: { type: String, required: false, default: '' },\n\t\t\tsource: { type: String, required: false, default: '' },\n\t\t\tfileid: { type: [String, Number], required: false, default: null },\n\t\t\tfileId: { type: [String, Number], required: false, default: null },\n\t\t\tfileInfo: { type: Object, required: false, default: null },\n\t\t},\n\t\tdata() {\n\t\t\treturn {\n\t\t\t\tiframeSrc: '',\n\t\t\t\tisLoading: true,\n\t\t\t\tloadError: '',\n\t\t\t\tcanRecover: false,\n\t\t\t\tisRecovering: false,\n\t\t\t\tisCheckingOriginal: false,\n\t\t\t\toriginalPad: null,\n\t\t\t\texternalOpenUrl: '',\n\t\t\t\texternalOpenMessage: '',\n\t\t\t\tsnapshotMode: '',\n\t\t\t\tsnapshot: { text: '', html: '' },\n\t\t\t\tresolveGeneration: 0,\n\t\t\t}\n\t\t},\n\t\tcomputed: {\n\t\t\tsourcePath() {\n\t\t\t\tconst value = typeof this.source === 'string' ? this.source.trim() : ''\n\t\t\t\tif (!value) return ''\n\t\t\t\treturn parsePadPathFromDavHref(value) || ''\n\t\t\t},\n\t\t\tfilePath() {\n\t\t\t\tconst normalizeName = (value) => String(value || '').trim().replace(/\\s+\\.pad$/i, '.pad')\n\t\t\t\tconst normalizeDir = (value) => {\n\t\t\t\t\tconst dir = String(value || '').trim()\n\t\t\t\t\tif (!dir || dir === '/') return '/'\n\t\t\t\t\treturn dir.startsWith('/') ? dir : ('/' + dir)\n\t\t\t\t}\n\t\t\t\tconst joinPath = (dir, name) => {\n\t\t\t\t\tif (!name) return ''\n\t\t\t\t\tif (name.startsWith('/')) return name\n\t\t\t\t\tconst normalizedDir = normalizeDir(dir)\n\t\t\t\t\treturn normalizedDir === '/' ? '/' + name : normalizedDir + '/' + name\n\t\t\t\t}\n\t\t\t\tconst isPadPath = (value) => typeof value === 'string' && value.toLowerCase().endsWith('.pad')\n\t\t\t\tif (isPadPath(this.sourcePath)) return this.sourcePath\n\n\t\t\t\tconst info = this.fileInfo && typeof this.fileInfo === 'object' ? this.fileInfo : null\n\t\t\t\tconst infoPath = info && typeof info.path === 'string' ? normalizeName(info.path) : ''\n\t\t\t\tif (isPadPath(infoPath)) return infoPath.startsWith('/') ? infoPath : ('/' + infoPath)\n\n\t\t\t\tconst baseName = normalizeName(this.filename || this.basename || (info && (info.name || info.basename)) || '')\n\t\t\t\tif (!baseName) return ''\n\n\t\t\t\tconst infoDir = info && typeof info.dirname === 'string' ? info.dirname : ''\n\t\t\t\tif (infoDir) {\n\t\t\t\t\tconst combined = joinPath(infoDir, baseName)\n\t\t\t\t\tif (isPadPath(combined)) return combined\n\t\t\t\t}\n\n\t\t\t\tconst params = new URLSearchParams(window.location.search || '')\n\t\t\t\tconst urlDir = params.get('dir') || '/'\n\t\t\t\tconst fromDir = joinPath(urlDir, baseName)\n\t\t\t\tif (isPadPath(fromDir)) return fromDir\n\t\t\t\treturn '/' + baseName\n\t\t\t},\n\t\t\tresolvedFileId() {\n\t\t\t\tconst candidates = [this.fileid, this.fileId, this.fileInfo && (this.fileInfo.fileid || this.fileInfo.fileId || this.fileInfo.id)]\n\t\t\t\tfor (const candidate of candidates) {\n\t\t\t\t\tconst numeric = Number(candidate)\n\t\t\t\t\tif (Number.isFinite(numeric) && numeric > 0) return numeric\n\t\t\t\t}\n\t\t\t\tconst params = new URLSearchParams(window.location.search || '')\n\t\t\t\tif (params.get('openfile') !== 'true') return null\n\t\t\t\tconst match = (window.location.pathname || '').match(/\\/apps\\/files\\/files\\/(\\d+)\\/?$/)\n\t\t\t\tif (!match) return null\n\t\t\t\tconst fallbackId = Number(match[1])\n\t\t\t\treturn Number.isFinite(fallbackId) && fallbackId > 0 ? fallbackId : null\n\t\t\t},\n\t\t},\n\t\twatch: {\n\t\t\tfilePath: { immediate: true, handler() { void this.resolveOpenUrl() } },\n\t\t\tresolvedFileId() { void this.resolveOpenUrl() },\n\t\t},\n\t\tmethods: {\n\t\t\tasync fetchOpenPayload(url, init = {}) {\n\t\t\t\tconst headers = Object.assign({ Accept: 'application/json' }, init.headers || {})\n\t\t\t\tconst response = await fetch(url, Object.assign({\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tcredentials: 'same-origin',\n\t\t\t\t\theaders,\n\t\t\t\t}, init))\n\t\t\t\tconst data = await response.json().catch(() => ({}))\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tconst error = new Error((data && data.message) || 'Pad open failed.')\n\t\t\t\t\tif (data && typeof data.code === 'string') {\n\t\t\t\t\t\terror.code = data.code\n\t\t\t\t\t}\n\t\t\t\t\tthrow error\n\t\t\t\t}\n\t\t\t\tif (!data || (data.is_readonly_snapshot !== true && (typeof data.url !== 'string' || data.url.trim() === ''))) {\n\t\t\t\t\tthrow new Error('Pad open API did not return a valid URL.')\n\t\t\t\t}\n\t\t\t\treturn data\n\t\t\t},\n\t\t\tisMissingFrontmatterError(error) {\n\t\t\t\tif (!(error instanceof Error)) return false\n\t\t\t\treturn String(error.message || '').includes('Missing YAML frontmatter')\n\t\t\t},\n\t\t\tasync initializeMissingFrontmatter() {\n\t\t\t\tconst headers = {\n\t\t\t\t\tAccept: 'application/json',\n\t\t\t\t\trequesttoken: ocRequestToken(),\n\t\t\t\t}\n\n\t\t\t\tconst buildInitError = (data, fallbackMessage) => {\n\t\t\t\t\tconst err = new Error((data && data.message) || fallbackMessage)\n\t\t\t\t\t// Forward a structured code so callers can branch on\n\t\t\t\t\t// `legacy_collision_no_access` without parsing the message.\n\t\t\t\t\tif (data && typeof data.code === 'string' && data.code !== '') {\n\t\t\t\t\t\terr.code = data.code\n\t\t\t\t\t}\n\t\t\t\t\treturn err\n\t\t\t\t}\n\n\t\t\t\tconst announceMigratedStatus = (data) => {\n\t\t\t\t\tif (data && data.status === 'migrated_from_legacy') {\n\t\t\t\t\t\t// Audit-visible on the backend; mirror it in the\n\t\t\t\t\t\t// browser console so dev tools makes the conversion\n\t\t\t\t\t\t// visible without surfacing a UI toast (the codebase\n\t\t\t\t\t\t// has no toast infra wired yet).\n\t\t\t\t\t\tconsole.info('Legacy Ownpad .pad migrated to managed format on first open.')\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (this.resolvedFileId !== null) {\n\t\t\t\t\tconst url = ocGenerateUrl('/apps/' + APP_ID + '/api/v1/pads/initialize-by-id/' + encodeURIComponent(String(this.resolvedFileId)))\n\t\t\t\t\tconst response = await fetch(url, { method: 'POST', credentials: 'same-origin', headers })\n\t\t\t\t\tconst data = await response.json().catch(() => ({}))\n\t\t\t\t\tif (!response.ok) {\n\t\t\t\t\t\tthrow buildInitError(data, 'Pad initialization failed.')\n\t\t\t\t\t}\n\t\t\t\t\tannounceMigratedStatus(data)\n\t\t\t\t\treturn data\n\t\t\t\t}\n\n\t\t\t\tif (!this.filePath) {\n\t\t\t\t\tthrow new Error('Pad initialization failed: missing file path.')\n\t\t\t\t}\n\n\t\t\t\tconst body = new URLSearchParams()\n\t\t\t\tbody.set('file', this.filePath)\n\t\t\t\tconst url = ocGenerateUrl('/apps/' + APP_ID + '/api/v1/pads/initialize')\n\t\t\t\tconst response = await fetch(url, {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\tcredentials: 'same-origin',\n\t\t\t\t\theaders: Object.assign({}, headers, {\n\t\t\t\t\t\t'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',\n\t\t\t\t\t}),\n\t\t\t\t\tbody: body.toString(),\n\t\t\t\t})\n\t\t\t\tconst data = await response.json().catch(() => ({}))\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthrow buildInitError(data, 'Pad initialization failed.')\n\t\t\t\t}\n\t\t\t\tannounceMigratedStatus(data)\n\t\t\t\treturn data\n\t\t\t},\n\t\t\tmarkLoaded() {\n\t\t\t\tthis.$emit('update:loaded', true)\n\t\t\t},\n\t\t\t// Lazily build the shared sync controller. Kept off `data()` on\n\t\t\t// purpose so it is not made reactive, and memoised on a plain\n\t\t\t// instance field so it survives the immediate filePath watcher\n\t\t\t// (which runs before created/mounted).\n\t\t\tpadSync() {\n\t\t\t\tif (!this._padSync) {\n\t\t\t\t\tthis._padSync = createPadSync({ requestToken: () => ocRequestToken() })\n\t\t\t\t}\n\t\t\t\treturn this._padSync\n\t\t\t},\n\t\t\t// Final flush + full teardown on destroy. Guard on the existing\n\t\t\t// controller so we never spin one up just to tear it down (a viewer\n\t\t\t// destroyed before its first open). The lazy create stays in the\n\t\t\t// resolve path, which actually needs to sync.\n\t\t\tteardownSync() {\n\t\t\t\tif (!this._padSync) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tthis._padSync.fireAndForget(true, true)\n\t\t\t\tthis._padSync.stop()\n\t\t\t\tthis._padSync.removeLifecycleHandlers()\n\t\t\t},\n\t\t\tasync resolveOpenUrl() {\n\t\t\t\tconst generation = ++this.resolveGeneration\n\t\t\t\tconst isCurrent = () => generation === this.resolveGeneration\n\n\t\t\t\tthis.isLoading = true\n\t\t\t\tthis.loadError = ''\n\t\t\t\tthis.canRecover = false\n\t\t\t\tthis.isCheckingOriginal = false\n\t\t\t\tthis.originalPad = null\n\t\t\t\tthis.iframeSrc = ''\n\t\t\t\tthis.externalOpenUrl = ''\n\t\t\t\tthis.externalOpenMessage = ''\n\t\t\t\tthis.snapshotMode = ''\n\t\t\t\tthis.snapshot = { text: '', html: '' }\n\t\t\t\t// Reset only an existing controller; don't construct one just to\n\t\t\t\t// stop/clear it (e.g. the initial immediate watcher with no pad).\n\t\t\t\t// The success path below lazily creates it when there's a pad to\n\t\t\t\t// actually sync.\n\t\t\t\tif (this._padSync) {\n\t\t\t\t\tthis._padSync.stop()\n\t\t\t\t\tthis._padSync.configure({ syncUrl: '' })\n\t\t\t\t}\n\n\t\t\t\tif (!this.filePath) {\n\t\t\t\t\tif (!isCurrent()) return\n\t\t\t\t\tthis.loadError = 'No .pad file selected.'\n\t\t\t\t\tthis.isLoading = false\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tconst publicToken = parsePublicShareTokenFromLocation()\n\t\t\t\tconst byPathUrl = ocGenerateUrl('/apps/' + APP_ID + '/api/v1/pads/open')\n\t\t\t\tconst byPublicUrl = (() => {\n\t\t\t\t\tif (!publicToken) return ''\n\t\t\t\t\tconst url = new URL(ocGenerateUrl('/apps/' + APP_ID + '/api/v1/public/open/' + encodeURIComponent(publicToken)), window.location.origin)\n\t\t\t\t\turl.searchParams.set('file', this.filePath)\n\t\t\t\t\treturn url.toString()\n\t\t\t\t})()\n\t\t\t\tconst byIdUrl = this.resolvedFileId !== null\n\t\t\t\t\t? ocGenerateUrl('/apps/' + APP_ID + '/api/v1/pads/open-by-id')\n\t\t\t\t\t: ''\n\t\t\t\tconst byPathBody = new URLSearchParams()\n\t\t\t\tbyPathBody.set('file', this.filePath)\n\t\t\t\tconst byIdBody = new URLSearchParams()\n\t\t\t\tif (this.resolvedFileId !== null) {\n\t\t\t\t\tbyIdBody.set('fileId', String(this.resolvedFileId))\n\t\t\t\t}\n\t\t\t\tconst openPostHeaders = {\n\t\t\t\t\t'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',\n\t\t\t\t\trequesttoken: ocRequestToken(),\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tconst fetchOpenData = async () => {\n\t\t\t\t\t\t\tlet data = null\n\t\t\t\t\t\t\tif (byPublicUrl) {\n\t\t\t\t\t\t\t\tdata = await this.fetchOpenPayload(byPublicUrl)\n\t\t\t\t\t\t\t} else if (byIdUrl) {\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tdata = await this.fetchOpenPayload(byIdUrl, {\n\t\t\t\t\t\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\t\t\t\t\t\theaders: openPostHeaders,\n\t\t\t\t\t\t\t\t\t\tbody: byIdBody.toString(),\n\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\t\t// Fallback for moved/renamed files where stale fileId can fail.\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (!data) {\n\t\t\t\t\t\t\t\tdata = await this.fetchOpenPayload(byPathUrl, {\n\t\t\t\t\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\t\t\t\t\theaders: openPostHeaders,\n\t\t\t\t\t\t\t\t\tbody: byPathBody.toString(),\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn data\n\t\t\t\t\t\t}\n\n\t\t\t\t\tlet data\n\t\t\t\t\ttry {\n\t\t\t\t\t\tdata = await fetchOpenData()\n\t\t\t\t\t\tif (!isCurrent()) return\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tif (!this.isMissingFrontmatterError(error)) {\n\t\t\t\t\t\t\tthrow error\n\t\t\t\t\t\t}\n\t\t\t\t\t\tawait this.initializeMissingFrontmatter()\n\t\t\t\t\t\tif (!isCurrent()) return\n\t\t\t\t\t\tdata = await fetchOpenData()\n\t\t\t\t\t\tif (!isCurrent()) return\n\t\t\t\t\t}\n\n\t\t\t\t\tconst syncUrl = (data && typeof data.sync_url === 'string') ? data.sync_url : ''\n\n\t\t\t\t\tconst intervalSeconds = Number(data && data.sync_interval_seconds)\n\t\t\t\t\tconst intervalMs = (Number.isFinite(intervalSeconds) && intervalSeconds > 0)\n\t\t\t\t\t\t? Math.max(5000, Math.min(3600000, intervalSeconds * 1000))\n\t\t\t\t\t\t: 120000\n\n\t\t\t\t\tthis.padSync().configure({ syncUrl, intervalMs })\n\t\t\t\t\tthis.padSync().installLifecycleHandlers()\n\t\t\t\t\tif (syncUrl) {\n\t\t\t\t\t\tthis.padSync().start()\n\t\t\t\t\t}\n\n\t\t\t\t\tif (data && data.is_readonly_snapshot === true) {\n\t\t\t\t\t\tthis.snapshotMode = 'readonly'\n\t\t\t\t\t\tthis.snapshot = {\n\t\t\t\t\t\t\ttext: (typeof data.snapshot_text === 'string') ? data.snapshot_text : '',\n\t\t\t\t\t\t\thtml: (typeof data.snapshot_html === 'string') ? data.snapshot_html : '',\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthis.markLoaded()\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\tif (data && data.is_external === true && typeof data.url === 'string' && data.url.trim() !== '') {\n\t\t\t\t\t\tconst targetUrl = data.url.trim()\n\t\t\t\t\t\tthis.externalOpenUrl = targetUrl\n\t\t\t\t\t\tthis.externalOpenMessage = translate('Read-only snapshot from the .pad file.')\n\t\t\t\t\t\tthis.snapshotMode = 'external'\n\t\t\t\t\t\tthis.snapshot = {\n\t\t\t\t\t\t\ttext: (data && typeof data.snapshot_text === 'string') ? data.snapshot_text : '',\n\t\t\t\t\t\t\thtml: (data && typeof data.snapshot_html === 'string') ? data.snapshot_html : '',\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthis.markLoaded()\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\tthis.iframeSrc = data.url\n\t\t\t\t\tthis.markLoaded()\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!isCurrent()) return\n\t\t\t\t\tthis.loadError = error instanceof Error ? error.message : 'Could not load pad.'\n\t\t\t\t\t// Recovery is gated on having a fileId we can address. Public-share\n\t\t\t\t\t// visitors don't get a recovery action — only the share owner.\n\t\t\t\t\tthis.canRecover = Boolean(error && error.code === 'missing_binding')\n\t\t\t\t\t\t&& this.resolvedFileId !== null\n\t\t\t\t\t\t&& !parsePublicShareTokenFromLocation()\n\t\t\t\t\tif (this.canRecover) {\n\t\t\t\t\t\t// Optional: check if this looks like a copy of a .pad we\n\t\t\t\t\t\t// can already address; if so we'll offer 'Open the\n\t\t\t\t\t\t// original' as the primary action. A miss is silent — no\n\t\t\t\t\t\t// UI element rendered, no info leaked.\n\t\t\t\t\t\tthis.fetchOriginalPadHint(generation, isCurrent)\n\t\t\t\t\t}\n\t\t\t\t\tthis.markLoaded()\n\t\t\t\t} finally {\n\t\t\t\t\tif (!isCurrent()) return\n\t\t\t\t\tthis.isLoading = false\n\t\t\t\t}\n\t\t\t},\n\t\t\tasync fetchOriginalPadHint(generation, isCurrent) {\n\t\t\t\tif (this.resolvedFileId === null) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tthis.isCheckingOriginal = true\n\t\t\t\ttry {\n\t\t\t\t\tconst hint = await apiFindOriginalPad(this.resolvedFileId)\n\t\t\t\t\tif (!isCurrent()) return\n\t\t\t\t\tif (hint && hint.found === true && typeof hint.viewer_url === 'string' && hint.viewer_url !== '') {\n\t\t\t\t\t\tthis.originalPad = {\n\t\t\t\t\t\t\tviewerUrl: hint.viewer_url,\n\t\t\t\t\t\t\tpath: typeof hint.path === 'string' ? hint.path : '',\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Silent: the recovery button stays available, we just\n\t\t\t\t\t// don't surface the \"Open the original\" affordance.\n\t\t\t\t} finally {\n\t\t\t\t\tif (isCurrent()) {\n\t\t\t\t\t\tthis.isCheckingOriginal = false\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\tasync recoverFromSnapshot() {\n\t\t\t\tif (!this.canRecover || this.isRecovering || this.resolvedFileId === null) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tthis.isRecovering = true\n\t\t\t\ttry {\n\t\t\t\t\tawait apiRecoverFromSnapshot(this.resolvedFileId)\n\t\t\t\t\tthis.loadError = ''\n\t\t\t\t\tthis.canRecover = false\n\t\t\t\t\tawait this.resolveOpenUrl()\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthis.loadError = error instanceof Error ? error.message : 'Could not load pad.'\n\t\t\t\t} finally {\n\t\t\t\t\tthis.isRecovering = false\n\t\t\t\t}\n\t\t\t},\n\t\t\trenderSnapshotView(createElement, options) {\n\t\t\t\tconst html = sanitizeSnapshotHtml(options.html)\n\t\t\t\tconst text = String(options.text || '')\n\t\t\t\tconst actions = Array.isArray(options.actions) ? options.actions : []\n\n\t\t\t\treturn createElement('div', { class: 'epnc-native-snapshot' }, [\n\t\t\t\t\tcreateElement('div', { class: 'epnc-native-snapshot__inner' }, [\n\t\t\t\t\t\tcreateElement('div', { class: 'epnc-native-snapshot__header' }, [\n\t\t\t\t\t\t\tcreateElement('div', { class: 'epnc-native-snapshot__heading' }, [\n\t\t\t\t\t\t\t\tcreateElement('div', { class: 'epnc-native-snapshot__title' }, options.title),\n\t\t\t\t\t\t\t\tcreateElement('div', { class: 'epnc-native-snapshot__message' }, options.message),\n\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t\tactions.length > 0\n\t\t\t\t\t\t\t\t? createElement('div', { class: 'epnc-native-snapshot__actions' }, actions)\n\t\t\t\t\t\t\t\t: null,\n\t\t\t\t\t\t]),\n\t\t\t\t\t\thtml.trim() !== ''\n\t\t\t\t\t\t\t? createElement('div', {\n\t\t\t\t\t\t\t\tclass: 'epnc-native-snapshot__text epnc-native-snapshot__text--html',\n\t\t\t\t\t\t\t\tdomProps: { innerHTML: html },\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t: createElement('pre', { class: 'epnc-native-snapshot__text' }, text.trim() !== ''\n\t\t\t\t\t\t\t\t? text\n\t\t\t\t\t\t\t\t: options.emptyMessage),\n\t\t\t\t\t]),\n\t\t\t\t])\n\t\t\t},\n\t\t},\n\t\tbeforeDestroy() {\n\t\t\tthis.resolveGeneration += 1\n\t\t\tthis.teardownSync()\n\t\t},\n\t\tbeforeUnmount() {\n\t\t\tthis.resolveGeneration += 1\n\t\t\tthis.teardownSync()\n\t\t},\n\t\trender(createElement) {\n\t\t\tif (this.loadError) {\n\t\t\t\tconst cardChildren = [\n\t\t\t\t\tcreateElement('div', { class: 'epnc-native-error-title' }, translate('Could not open pad')),\n\t\t\t\t\tcreateElement('div', { class: 'epnc-native-error-message' }, this.loadError),\n\t\t\t\t]\n\t\t\t\tif (this.canRecover) {\n\t\t\t\t\tif (this.isCheckingOriginal) {\n\t\t\t\t\t\t// Don't render any action button while the lookup is in\n\t\t\t\t\t\t// flight: a slow connection could otherwise let the user\n\t\t\t\t\t\t// click 'Create new pad' before we know that opening the\n\t\t\t\t\t\t// original is the better default.\n\t\t\t\t\t\tcardChildren.push(\n\t\t\t\t\t\t\tcreateElement('div', { class: 'epnc-native-error-message' },\n\t\t\t\t\t\t\t\ttranslate('Checking for the original pad...')),\n\t\t\t\t\t\t)\n\t\t\t\t\t} else if (this.originalPad) {\n\t\t\t\t\t\tcardChildren.push(\n\t\t\t\t\t\t\tcreateElement('div', { class: 'epnc-native-error-message' },\n\t\t\t\t\t\t\t\ttranslate('This file looks like a copy of an existing .pad file in your account. Open the original to keep editing the linked pad, or create a new pad to fork the content stored in this file.')),\n\t\t\t\t\t\t\tcreateElement('a', {\n\t\t\t\t\t\t\t\tclass: 'button primary epnc-native-error-action',\n\t\t\t\t\t\t\t\tattrs: { href: this.originalPad.viewerUrl },\n\t\t\t\t\t\t\t}, translate('Open the original .pad file')),\n\t\t\t\t\t\t\tcreateElement('button', {\n\t\t\t\t\t\t\t\tclass: 'button epnc-native-error-action',\n\t\t\t\t\t\t\t\tattrs: { type: 'button', disabled: this.isRecovering },\n\t\t\t\t\t\t\t\ton: { click: () => { void this.recoverFromSnapshot() } },\n\t\t\t\t\t\t\t}, this.isRecovering ? translate('Creating new pad...') : translate('Create new pad from this file')),\n\t\t\t\t\t\t)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcardChildren.push(\n\t\t\t\t\t\t\tcreateElement('div', { class: 'epnc-native-error-message' },\n\t\t\t\t\t\t\t\ttranslate(\"We couldn't find a matching pad in this Nextcloud. You can create a new pad from the text stored in this file; from then on, opening this file will load the new pad.\")),\n\t\t\t\t\t\t\tcreateElement('button', {\n\t\t\t\t\t\t\t\tclass: 'button primary epnc-native-error-action',\n\t\t\t\t\t\t\t\tattrs: { type: 'button', disabled: this.isRecovering },\n\t\t\t\t\t\t\t\ton: { click: () => { void this.recoverFromSnapshot() } },\n\t\t\t\t\t\t\t}, this.isRecovering ? translate('Creating new pad...') : translate('Create new pad from this file')),\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn createElement('div', { class: 'epnc-native-status epnc-native-status--error' }, [\n\t\t\t\t\tcreateElement('div', { class: 'epnc-native-error-card' }, cardChildren),\n\t\t\t\t])\n\t\t\t}\n\t\t\tif (this.snapshotMode === 'external') {\n\t\t\t\treturn this.renderSnapshotView(createElement, {\n\t\t\t\t\ttitle: translate('Pad from another server'),\n\t\t\t\t\tmessage: this.externalOpenMessage,\n\t\t\t\t\tactions: [\n\t\t\t\t\t\tcreateElement('a', {\n\t\t\t\t\t\t\tclass: 'button primary',\n\t\t\t\t\t\t\tattrs: {\n\t\t\t\t\t\t\t\thref: this.externalOpenUrl,\n\t\t\t\t\t\t\t\ttarget: '_blank',\n\t\t\t\t\t\t\t\trel: 'noopener noreferrer',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}, translate('Open original pad')),\n\t\t\t\t\t],\n\t\t\t\t\thtml: this.snapshot.html,\n\t\t\t\t\ttext: this.snapshot.text,\n\t\t\t\t\temptyMessage: translate('No synced snapshot is stored in this .pad file yet.'),\n\t\t\t\t})\n\t\t\t}\n\t\t\tif (this.snapshotMode === 'readonly') {\n\t\t\t\treturn this.renderSnapshotView(createElement, {\n\t\t\t\t\ttitle: translate('Read-only snapshot'),\n\t\t\t\t\tmessage: translate('Read-only snapshot from the .pad file.'),\n\t\t\t\t\thtml: this.snapshot.html,\n\t\t\t\t\ttext: this.snapshot.text,\n\t\t\t\t\temptyMessage: translate('No synced snapshot is stored in this .pad file yet.'),\n\t\t\t\t})\n\t\t\t}\n\t\t\tif (this.isLoading || !this.iframeSrc) {\n\t\t\t\treturn createElement('div', { class: 'epnc-native-status' }, 'Loading pad...')\n\t\t\t}\n\n\t\t\treturn createElement('div', { class: 'epnc-native-shell' }, [\n\t\t\t\t// Nextcloud Viewer tries to inspect/focus direct iframe children during\n\t\t\t\t// teardown. Keep the direct iframe same-origin via srcdoc, and put the\n\t\t\t\t// cross-origin Etherpad frame one level deeper.\n\t\t\t\tcreateElement('iframe', {\n\t\t\t\t\tattrs: { srcdoc: buildPadFrameSrcdoc(this.iframeSrc), title: 'Etherpad' },\n\t\t\t\t\t// This fires when the srcdoc wrapper is ready. Etherpad then continues\n\t\t\t\t\t// loading in the inner iframe and shows its own loading UI.\n\t\t\t\t\ton: { load: () => this.markLoaded(), error: () => this.markLoaded() },\n\t\t\t\t\tclass: 'epnc-native-iframe',\n\t\t\t\t}),\n\t\t\t])\n\t\t},\n\t}\n\n\tconst tryRegister = () => {\n\t\tattempts += 1\n\t\tif (!(window.OCA && window.OCA.Viewer && typeof window.OCA.Viewer.registerHandler === 'function')) {\n\t\t\tif (attempts < 20) window.setTimeout(tryRegister, 500)\n\t\t\treturn\n\t\t}\n\t\tif (Array.isArray(window.OCA.Viewer.availableHandlers)\n\t\t\t&& window.OCA.Viewer.availableHandlers.some((handler) => handler && handler.id === VIEWER_HANDLER_ID)) {\n\t\t\treturn\n\t\t}\n\t\twindow.OCA.Viewer.registerHandler({ id: VIEWER_HANDLER_ID, mimes: [MIME], component })\n\t}\n\n\ttryRegister()\n})()\n"],"names":["escapeAttribute","value","ALLOWED_PAD_URL_SCHEMES","isSafePadUrl","url","parsed","SRC_DOC_CSP","buildPadFrameSrcdoc","safeUrl","attempts","component","parsePadPathFromDavHref","normalizeName","normalizeDir","dir","joinPath","name","normalizedDir","isPadPath","info","infoPath","baseName","infoDir","combined","urlDir","fromDir","candidates","candidate","numeric","match","fallbackId","init","headers","response","data","error","ocRequestToken","buildInitError","fallbackMessage","err","announceMigratedStatus","ocGenerateUrl","APP_ID","body","createPadSync","generation","isCurrent","publicToken","parsePublicShareTokenFromLocation","byPathUrl","byPublicUrl","byIdUrl","byPathBody","byIdBody","openPostHeaders","fetchOpenData","syncUrl","intervalSeconds","intervalMs","targetUrl","translate","hint","apiFindOriginalPad","apiRecoverFromSnapshot","createElement","options","html","sanitizeSnapshotHtml","text","actions","cardChildren","tryRegister","handler","VIEWER_HANDLER_ID","MIME"],"mappings":"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"}
\ No newline at end of file
diff --git a/js/pad-sync-B0gz-oBk.chunk.mjs b/js/pad-sync-B0gz-oBk.chunk.mjs
deleted file mode 100644
index 16727fb..0000000
--- a/js/pad-sync-B0gz-oBk.chunk.mjs
+++ /dev/null
@@ -1,2 +0,0 @@
-function H({requestToken:w,fetchImpl:m}={}){const S=typeof m=="function"?m:(...e)=>window.fetch(...e),k=typeof w=="function"?w:()=>"";let n="",b=12e4,i=null,u=!1,d=!1,c=!1,a=null,r=null,l=null;const q=({syncUrl:e,intervalMs:t}={})=>{typeof e=="string"&&(n=e),Number.isFinite(t)&&t>0&&(b=t)},f=()=>{a!==null&&(window.clearInterval(a),a=null)},g=()=>{!n||a!==null||(a=window.setInterval(()=>{document.visibilityState==="visible"&&s(!1,!1)},b))},s=(e,t)=>{o(e,t).catch(()=>{})},o=async(e,t)=>{if(!n)return{status:"disabled"};if(i)return e&&!u?(d=!0,c=c||!!t,i.catch(()=>{}).then(()=>o(!0,c))):i;u=!!e;const h=(async()=>{const y=e?n+(n.includes("?")?"&":"?")+"force=1":n,L=await S(y,{method:"POST",credentials:"same-origin",headers:{Accept:"application/json",requesttoken:k()},keepalive:!!t}),p=await L.json().catch(()=>({}));if(!L.ok)throw new Error(p&&p.message||"Sync request failed.");return p})();i=h;let E,v=null;try{E=await h}catch(y){v=y}finally{i===h&&(i=null),u=!1}const A=d,F=c;if(d=!1,c=!1,A)return o(!0,F);if(v instanceof Error)throw v;return E};return{configure:q,start:g,stop:f,sync:o,fireAndForget:s,installLifecycleHandlers:()=>{r||l||(r=()=>{if(document.visibilityState==="hidden"){s(!0,!0),f();return}g()},l=()=>{s(!0,!0),f()},document.addEventListener("visibilitychange",r),window.addEventListener("pagehide",l))},removeLifecycleHandlers:()=>{r&&(document.removeEventListener("visibilitychange",r),r=null),l&&(window.removeEventListener("pagehide",l),l=null)}}}export{H as c};
-//# sourceMappingURL=pad-sync-B0gz-oBk.chunk.mjs.map
diff --git a/js/pad-sync-B0gz-oBk.chunk.mjs.map b/js/pad-sync-B0gz-oBk.chunk.mjs.map
deleted file mode 100644
index 068fed0..0000000
--- a/js/pad-sync-B0gz-oBk.chunk.mjs.map
+++ /dev/null
@@ -1 +0,0 @@
-{"version":3,"file":"pad-sync-B0gz-oBk.chunk.mjs","sources":["../src/lib/pad-sync.js"],"sourcesContent":["/**\n * SPDX-License-Identifier: AGPL-3.0-or-later\n * Copyright (c) 2026 Jacob Bühler\n */\n\n/**\n * Shared pad-sync loop for the viewer and embed entrypoints.\n *\n * Owns the periodic background sync, forced/keepalive flushes, and the\n * visibility/pagehide lifecycle wiring. Forced syncs coalesce: while one\n * request is in flight a second forced flush is not started concurrently but\n * remembered and replayed once, so two forced flushes can never overlap (this\n * is the embed behavior, now shared — the viewer previously used a plain\n * in-flight boolean that allowed overlap).\n *\n * The caller supplies the request-token getter (the viewer and embed read it\n * from different places) and may inject a `fetchImpl` for tests.\n */\n\nexport const DEFAULT_SYNC_INTERVAL_MS = 120000\n\nexport function createPadSync({ requestToken, fetchImpl } = {}) {\n\tconst doFetch = typeof fetchImpl === 'function' ? fetchImpl : (...args) => window.fetch(...args)\n\tconst getToken = typeof requestToken === 'function' ? requestToken : () => ''\n\n\tlet syncUrl = ''\n\tlet intervalMs = DEFAULT_SYNC_INTERVAL_MS\n\tlet syncPromise = null\n\tlet activeSyncForce = false\n\tlet pendingForcedSync = false\n\tlet pendingForcedKeepalive = false\n\tlet timerId = null\n\tlet visibilityHandler = null\n\tlet pageHideHandler = null\n\n\tconst configure = ({ syncUrl: url, intervalMs: ms } = {}) => {\n\t\tif (typeof url === 'string') {\n\t\t\tsyncUrl = url\n\t\t}\n\t\tif (Number.isFinite(ms) && ms > 0) {\n\t\t\tintervalMs = ms\n\t\t}\n\t}\n\n\tconst stop = () => {\n\t\tif (timerId !== null) {\n\t\t\twindow.clearInterval(timerId)\n\t\t\ttimerId = null\n\t\t}\n\t}\n\n\tconst start = () => {\n\t\tif (!syncUrl || timerId !== null) {\n\t\t\treturn\n\t\t}\n\t\ttimerId = window.setInterval(() => {\n\t\t\tif (document.visibilityState === 'visible') {\n\t\t\t\tfireAndForget(false, false)\n\t\t\t}\n\t\t}, intervalMs)\n\t}\n\n\tconst fireAndForget = (force, keepalive) => {\n\t\tvoid sync(force, keepalive).catch(() => {})\n\t}\n\n\tconst sync = async (force, keepalive) => {\n\t\tif (!syncUrl) {\n\t\t\treturn { status: 'disabled' }\n\t\t}\n\t\tif (syncPromise) {\n\t\t\t// A request is already running. A forced flush that arrives while a\n\t\t\t// non-forced sync is in flight is coalesced into a single replay.\n\t\t\tif (force && !activeSyncForce) {\n\t\t\t\tpendingForcedSync = true\n\t\t\t\tpendingForcedKeepalive = pendingForcedKeepalive || Boolean(keepalive)\n\t\t\t\treturn syncPromise.catch(() => undefined).then(() => sync(true, pendingForcedKeepalive))\n\t\t\t}\n\t\t\treturn syncPromise\n\t\t}\n\t\tactiveSyncForce = Boolean(force)\n\t\tconst currentPromise = (async () => {\n\t\t\tconst url = force ? (syncUrl + (syncUrl.includes('?') ? '&' : '?') + 'force=1') : syncUrl\n\t\t\tconst response = await doFetch(url, {\n\t\t\t\tmethod: 'POST',\n\t\t\t\tcredentials: 'same-origin',\n\t\t\t\theaders: {\n\t\t\t\t\tAccept: 'application/json',\n\t\t\t\t\trequesttoken: getToken(),\n\t\t\t\t},\n\t\t\t\tkeepalive: Boolean(keepalive),\n\t\t\t})\n\t\t\tconst data = await response.json().catch(() => ({}))\n\t\t\tif (!response.ok) {\n\t\t\t\tthrow new Error((data && data.message) || 'Sync request failed.')\n\t\t\t}\n\t\t\treturn data\n\t\t})()\n\t\tsyncPromise = currentPromise\n\t\tlet result\n\t\tlet syncError = null\n\t\ttry {\n\t\t\tresult = await currentPromise\n\t\t} catch (error) {\n\t\t\tsyncError = error\n\t\t} finally {\n\t\t\tif (syncPromise === currentPromise) {\n\t\t\t\tsyncPromise = null\n\t\t\t}\n\t\t\tactiveSyncForce = false\n\t\t}\n\t\tconst rerunForcedSync = pendingForcedSync\n\t\tconst rerunKeepalive = pendingForcedKeepalive\n\t\tpendingForcedSync = false\n\t\tpendingForcedKeepalive = false\n\t\tif (rerunForcedSync) {\n\t\t\treturn sync(true, rerunKeepalive)\n\t\t}\n\t\tif (syncError instanceof Error) {\n\t\t\tthrow syncError\n\t\t}\n\t\treturn result\n\t}\n\n\tconst installLifecycleHandlers = () => {\n\t\tif (visibilityHandler || pageHideHandler) {\n\t\t\treturn\n\t\t}\n\t\tvisibilityHandler = () => {\n\t\t\tif (document.visibilityState === 'hidden') {\n\t\t\t\tfireAndForget(true, true)\n\t\t\t\tstop()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tstart()\n\t\t}\n\t\tpageHideHandler = () => {\n\t\t\tfireAndForget(true, true)\n\t\t\tstop()\n\t\t}\n\t\tdocument.addEventListener('visibilitychange', visibilityHandler)\n\t\twindow.addEventListener('pagehide', pageHideHandler)\n\t}\n\n\tconst removeLifecycleHandlers = () => {\n\t\tif (visibilityHandler) {\n\t\t\tdocument.removeEventListener('visibilitychange', visibilityHandler)\n\t\t\tvisibilityHandler = null\n\t\t}\n\t\tif (pageHideHandler) {\n\t\t\twindow.removeEventListener('pagehide', pageHideHandler)\n\t\t\tpageHideHandler = null\n\t\t}\n\t}\n\n\treturn {\n\t\tconfigure,\n\t\tstart,\n\t\tstop,\n\t\tsync,\n\t\tfireAndForget,\n\t\tinstallLifecycleHandlers,\n\t\tremoveLifecycleHandlers,\n\t}\n}\n"],"names":["createPadSync","requestToken","fetchImpl","doFetch","args","getToken","syncUrl","intervalMs","syncPromise","activeSyncForce","pendingForcedSync","pendingForcedKeepalive","timerId","visibilityHandler","pageHideHandler","configure","url","ms","stop","start","fireAndForget","force","keepalive","sync","currentPromise","response","data","result","syncError","error","rerunForcedSync","rerunKeepalive"],"mappings":"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"}
\ No newline at end of file
diff --git a/js/sanitize-html-dv-YifbT.chunk.mjs b/js/sanitize-html-dv-YifbT.chunk.mjs
new file mode 100644
index 0000000..8e6f171
--- /dev/null
+++ b/js/sanitize-html-dv-YifbT.chunk.mjs
@@ -0,0 +1,4 @@
+function Pn({requestToken:n,fetchImpl:r}={}){const i=typeof r=="function"?r:(...v)=>window.fetch(...v),a=typeof n=="function"?n:()=>"";let s="",f=12e4,E=null,W=!1,S=!1,B=!1,Y=null,x=null,C=null;const Pt=({syncUrl:v,intervalMs:F}={})=>{typeof v=="string"&&(s=v),Number.isFinite(F)&&F>0&&(f=F)},ht=()=>{Y!==null&&(window.clearInterval(Y),Y=null)},_t=()=>{!s||Y!==null||(Y=window.setInterval(()=>{document.visibilityState==="visible"&&q(!1,!1)},f))},q=(v,F)=>{X(v,F).catch(()=>{})},X=async(v,F)=>{if(!s)return{status:"disabled"};if(E)return v&&!W?(S=!0,B=B||!!F,E.catch(()=>{}).then(()=>X(!0,B))):E;W=!!v;const D=(async()=>{const U=v?s+(s.includes("?")?"&":"?")+"force=1":s,Z=await i(U,{method:"POST",credentials:"same-origin",headers:{Accept:"application/json",requesttoken:a()},keepalive:!!F}),J=await Z.json().catch(()=>({}));if(!Z.ok)throw new Error(J&&J.message||"Sync request failed.");return J})();E=D;let M,N=null;try{M=await D}catch(U){N=U}finally{E===D&&(E=null),W=!1}const Q=S,gt=B;if(S=!1,B=!1,Q)return X(!0,gt);if(N instanceof Error)throw N;return M};return{configure:Pt,start:_t,stop:ht,sync:X,fireAndForget:q,installLifecycleHandlers:()=>{x||C||(x=()=>{if(document.visibilityState==="hidden"){q(!0,!0),ht();return}_t()},C=()=>{q(!0,!0),ht()},document.addEventListener("visibilitychange",x),window.addEventListener("pagehide",C))},removeLifecycleHandlers:()=>{x&&(document.removeEventListener("visibilitychange",x),x=null),C&&(window.removeEventListener("pagehide",C),C=null)}}}function we(n,r){(r==null||r>n.length)&&(r=n.length);for(var i=0,a=Array(r);i2?i-2:0),s=2;s1?r-1:0),a=1;a"u"?null:T(BigInt.prototype.toString),ke=typeof Symbol>"u"?null:T(Symbol.prototype.toString),p=T(Object.prototype.hasOwnProperty),Et=T(Object.prototype.toString),_=T(RegExp.prototype.test),pt=yn(TypeError);function T(n){return function(r){r instanceof RegExp&&(r.lastIndex=0);for(var i=arguments.length,a=new Array(i>1?i-1:0),s=1;s2&&arguments[2]!==void 0?arguments[2]:St;if(ve&&ve(n,null),!R(r))return n;let a=r.length;for(;a--;){let s=r[a];if(typeof s=="string"){const f=i(s);f!==s&&(sn(r)||(r[a]=f),s=f)}n[s]=!0}return n}function Tn(n){for(let r=0;r/g),On=L(/\${[\w\W]*/g),wn=L(/^data-[\-\w.\u00B7-\uFFFF]+$/),vn=L(/^aria-[\-\w]+$/),Fe=L(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),Dn=L(/^(?:\w+script|data):/i),Rn=L(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),In=L(/^html$/i),Cn=L(/^[a-z][.\w]*(-[.\w]+)+$/i),j={element:1,attribute:2,text:3,cdataSection:4,entityReference:5,entityNode:6,progressingInstruction:7,comment:8,document:9,documentType:10,documentFragment:11,notation:12},kn=function(){return typeof window>"u"?null:window},Ln=function(n,r){if(typeof n!="object"||typeof n.createPolicy!="function")return null;let i=null;const a="data-tt-policy-suffix";r&&r.hasAttribute(a)&&(i=r.getAttribute(a));const s="dompurify"+(i?"#"+i:"");try{return n.createPolicy(s,{createHTML(f){return f},createScriptURL(f){return f}})}catch{return console.warn("TrustedTypes policy "+s+" could not be created."),null}},Ue=function(){return{afterSanitizeAttributes:[],afterSanitizeElements:[],afterSanitizeShadowDOM:[],beforeSanitizeAttributes:[],beforeSanitizeElements:[],beforeSanitizeShadowDOM:[],uponSanitizeAttribute:[],uponSanitizeElement:[],uponSanitizeShadowNode:[]}};function je(){let n=arguments.length>0&&arguments[0]!==void 0?arguments[0]:kn();const r=t=>je(t);if(r.version="3.4.8",r.removed=[],!n||!n.document||n.document.nodeType!==j.document||!n.Element)return r.isSupported=!1,r;let i=n.document;const a=i,s=a.currentScript;n.DocumentFragment;const f=n.HTMLTemplateElement,E=n.Node,W=n.Element,S=n.NodeFilter;n.NamedNodeMap===void 0&&(n.NamedNodeMap||n.MozNamedAttrMap),n.HTMLFormElement;const Y=n.DOMParser,x=n.trustedTypes,C=W.prototype,Pt=G(C,"cloneNode"),ht=G(C,"remove"),_t=G(C,"nextSibling"),q=G(C,"childNodes"),X=G(C,"parentNode"),v=G(C,"shadowRoot"),F=G(C,"attributes"),D=E&&E.prototype?G(E.prototype,"nodeType"):null,M=E&&E.prototype?G(E.prototype,"nodeName"):null;if(typeof f=="function"){const t=i.createElement("template");t.content&&t.content.ownerDocument&&(i=t.content.ownerDocument)}let N,Q="",gt=0;const U=function(t){if(gt>0)throw pt('The configured TRUSTED_TYPES_POLICY.createHTML must not call DOMPurify.sanitize, as that causes infinite recursion. Do not pass a policy whose createHTML wraps DOMPurify as TRUSTED_TYPES_POLICY; see the "DOMPurify and Trusted Types" section of the README.');gt++;try{return N.createHTML(t)}finally{gt--}},Z=i,J=Z.implementation,re=Z.createNodeIterator,Ge=Z.createDocumentFragment,We=Z.getElementsByTagName,Be=a.importNode;let b=Ue();r.isSupported=typeof ze=="function"&&typeof X=="function"&&J&&J.createHTMLDocument!==void 0;const Nt=_n,Ot=Nn,wt=On,Ye=wn,qe=vn,Xe=Dn,ie=Rn,$e=Cn;let ae=Fe,d=null;const Ft=c({},[...Le,...Zt,...Jt,...te,...xe]);let y=null;const Ut=c({},[...Me,...ee,...Pe,...Mt]);let h=Object.seal(dt(null,{tagNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},allowCustomizedBuiltInElements:{writable:!0,configurable:!1,enumerable:!0,value:!1}})),yt=null,vt=null;const K=Object.seal(dt(null,{tagCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeCheck:{writable:!0,configurable:!1,enumerable:!0,value:null}}));let le=!0,zt=!0,ce=!1,se=!0,V=!1,Tt=!0,tt=!1,Ht=!1,jt=!1,rt=!1,Dt=!1,Rt=!1,ue=!0,me=!1;const fe="user-content-";let Gt=!0,bt=!1,it={},z=null;const Wt=c({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","noscript","plaintext","script","style","svg","template","thead","title","video","xmp"]);let pe=null;const de=c({},["audio","video","img","source","image","track"]);let Bt=null;const he=c({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),It="http://www.w3.org/1998/Math/MathML",Ct="http://www.w3.org/2000/svg",H="http://www.w3.org/1999/xhtml";let at=H,Yt=!1,qt=null;const Ke=c({},[It,Ct,H],Qt);let Xt=c({},["mi","mo","mn","ms","mtext"]),$t=c({},["annotation-xml"]);const Ve=c({},["title","style","font","a","script"]);let At=null;const Qe=["application/xhtml+xml","text/html"],Ze="text/html";let g=null,lt=null;const Je=i.createElement("form"),ge=function(t){return t instanceof RegExp||t instanceof Function},Kt=function(){let t=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};if(lt&<===t)return;(!t||typeof t!="object")&&(t={}),t=w(t),At=Qe.indexOf(t.PARSER_MEDIA_TYPE)===-1?Ze:t.PARSER_MEDIA_TYPE,g=At==="application/xhtml+xml"?Qt:St,d=p(t,"ALLOWED_TAGS")&&R(t.ALLOWED_TAGS)?c({},t.ALLOWED_TAGS,g):Ft,y=p(t,"ALLOWED_ATTR")&&R(t.ALLOWED_ATTR)?c({},t.ALLOWED_ATTR,g):Ut,qt=p(t,"ALLOWED_NAMESPACES")&&R(t.ALLOWED_NAMESPACES)?c({},t.ALLOWED_NAMESPACES,Qt):Ke,Bt=p(t,"ADD_URI_SAFE_ATTR")&&R(t.ADD_URI_SAFE_ATTR)?c(w(he),t.ADD_URI_SAFE_ATTR,g):he,pe=p(t,"ADD_DATA_URI_TAGS")&&R(t.ADD_DATA_URI_TAGS)?c(w(de),t.ADD_DATA_URI_TAGS,g):de,z=p(t,"FORBID_CONTENTS")&&R(t.FORBID_CONTENTS)?c({},t.FORBID_CONTENTS,g):Wt,yt=p(t,"FORBID_TAGS")&&R(t.FORBID_TAGS)?c({},t.FORBID_TAGS,g):w({}),vt=p(t,"FORBID_ATTR")&&R(t.FORBID_ATTR)?c({},t.FORBID_ATTR,g):w({}),it=p(t,"USE_PROFILES")?t.USE_PROFILES&&typeof t.USE_PROFILES=="object"?w(t.USE_PROFILES):t.USE_PROFILES:!1,le=t.ALLOW_ARIA_ATTR!==!1,zt=t.ALLOW_DATA_ATTR!==!1,ce=t.ALLOW_UNKNOWN_PROTOCOLS||!1,se=t.ALLOW_SELF_CLOSE_IN_ATTR!==!1,V=t.SAFE_FOR_TEMPLATES||!1,Tt=t.SAFE_FOR_XML!==!1,tt=t.WHOLE_DOCUMENT||!1,rt=t.RETURN_DOM||!1,Dt=t.RETURN_DOM_FRAGMENT||!1,Rt=t.RETURN_TRUSTED_TYPE||!1,jt=t.FORCE_BODY||!1,ue=t.SANITIZE_DOM!==!1,me=t.SANITIZE_NAMED_PROPS||!1,Gt=t.KEEP_CONTENT!==!1,bt=t.IN_PLACE||!1,ae=An(t.ALLOWED_URI_REGEXP)?t.ALLOWED_URI_REGEXP:Fe,at=typeof t.NAMESPACE=="string"?t.NAMESPACE:H,Xt=p(t,"MATHML_TEXT_INTEGRATION_POINTS")&&t.MATHML_TEXT_INTEGRATION_POINTS&&typeof t.MATHML_TEXT_INTEGRATION_POINTS=="object"?w(t.MATHML_TEXT_INTEGRATION_POINTS):c({},["mi","mo","mn","ms","mtext"]),$t=p(t,"HTML_INTEGRATION_POINTS")&&t.HTML_INTEGRATION_POINTS&&typeof t.HTML_INTEGRATION_POINTS=="object"?w(t.HTML_INTEGRATION_POINTS):c({},["annotation-xml"]);const e=p(t,"CUSTOM_ELEMENT_HANDLING")&&t.CUSTOM_ELEMENT_HANDLING&&typeof t.CUSTOM_ELEMENT_HANDLING=="object"?w(t.CUSTOM_ELEMENT_HANDLING):dt(null);if(h=dt(null),p(e,"tagNameCheck")&&ge(e.tagNameCheck)&&(h.tagNameCheck=e.tagNameCheck),p(e,"attributeNameCheck")&&ge(e.attributeNameCheck)&&(h.attributeNameCheck=e.attributeNameCheck),p(e,"allowCustomizedBuiltInElements")&&typeof e.allowCustomizedBuiltInElements=="boolean"&&(h.allowCustomizedBuiltInElements=e.allowCustomizedBuiltInElements),V&&(zt=!1),Dt&&(rt=!0),it&&(d=c({},xe),y=dt(null),it.html===!0&&(c(d,Le),c(y,Me)),it.svg===!0&&(c(d,Zt),c(y,ee),c(y,Mt)),it.svgFilters===!0&&(c(d,Jt),c(y,ee),c(y,Mt)),it.mathMl===!0&&(c(d,te),c(y,Pe),c(y,Mt))),K.tagCheck=null,K.attributeCheck=null,p(t,"ADD_TAGS")&&(typeof t.ADD_TAGS=="function"?K.tagCheck=t.ADD_TAGS:R(t.ADD_TAGS)&&(d===Ft&&(d=w(d)),c(d,t.ADD_TAGS,g))),p(t,"ADD_ATTR")&&(typeof t.ADD_ATTR=="function"?K.attributeCheck=t.ADD_ATTR:R(t.ADD_ATTR)&&(y===Ut&&(y=w(y)),c(y,t.ADD_ATTR,g))),p(t,"ADD_URI_SAFE_ATTR")&&R(t.ADD_URI_SAFE_ATTR)&&c(Bt,t.ADD_URI_SAFE_ATTR,g),p(t,"FORBID_CONTENTS")&&R(t.FORBID_CONTENTS)&&(z===Wt&&(z=w(z)),c(z,t.FORBID_CONTENTS,g)),p(t,"ADD_FORBID_CONTENTS")&&R(t.ADD_FORBID_CONTENTS)&&(z===Wt&&(z=w(z)),c(z,t.ADD_FORBID_CONTENTS,g)),Gt&&(d["#text"]=!0),tt&&c(d,["html","head","body"]),d.table&&(c(d,["tbody"]),delete yt.tbody),t.TRUSTED_TYPES_POLICY){if(typeof t.TRUSTED_TYPES_POLICY.createHTML!="function")throw pt('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');if(typeof t.TRUSTED_TYPES_POLICY.createScriptURL!="function")throw pt('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');const o=N;N=t.TRUSTED_TYPES_POLICY;try{Q=U("")}catch(l){throw N=o,l}}else N===void 0&&t.TRUSTED_TYPES_POLICY!==null&&(N=Ln(x,s)),N&&typeof Q=="string"&&(Q=U(""));(b.uponSanitizeElement.length>0||b.uponSanitizeAttribute.length>0)&&d===Ft&&(d=w(d)),b.uponSanitizeAttribute.length>0&&y===Ut&&(y=w(y)),I&&I(t),lt=t},ye=c({},[...Zt,...Jt,...En]),Te=c({},[...te,...Sn]),tn=function(t){let e=X(t);(!e||!e.tagName)&&(e={namespaceURI:at,tagName:"template"});const o=St(t.tagName),l=St(e.tagName);return qt[t.namespaceURI]?t.namespaceURI===Ct?e.namespaceURI===H?o==="svg":e.namespaceURI===It?o==="svg"&&(l==="annotation-xml"||Xt[l]):!!ye[o]:t.namespaceURI===It?e.namespaceURI===H?o==="math":e.namespaceURI===Ct?o==="math"&&$t[l]:!!Te[o]:t.namespaceURI===H?e.namespaceURI===Ct&&!$t[l]||e.namespaceURI===It&&!Xt[l]?!1:!Te[o]&&(Ve[o]||!ye[o]):!!(At==="application/xhtml+xml"&&qt[t.namespaceURI]):!1},et=function(t){mt(r.removed,{element:t});try{X(t).removeChild(t)}catch{ht(t)}},nt=function(t,e){try{mt(r.removed,{attribute:e.getAttributeNode(t),from:e})}catch{mt(r.removed,{attribute:null,from:e})}if(e.removeAttribute(t),t==="is")if(rt||Dt)try{et(e)}catch{}else try{e.setAttribute(t,"")}catch{}},be=function(t){let e=null,o=null;if(jt)t=""+t;else{const m=Re(t,/^[\r\n\t ]+/);o=m&&m[0]}At==="application/xhtml+xml"&&at===H&&(t=''+t+"");const l=N?U(t):t;if(at===H)try{e=new Y().parseFromString(l,At)}catch{}if(!e||!e.documentElement){e=J.createDocument(at,"template",null);try{e.documentElement.innerHTML=Yt?Q:l}catch{}}const u=e.body||e.documentElement;return t&&o&&u.insertBefore(i.createTextNode(o),u.childNodes[0]||null),at===H?We.call(e,tt?"html":"body")[0]:tt?e.documentElement:u},Ae=function(t){return re.call(t.ownerDocument||t,t,S.SHOW_ELEMENT|S.SHOW_COMMENT|S.SHOW_TEXT|S.SHOW_PROCESSING_INSTRUCTION|S.SHOW_CDATA_SECTION,null)},Vt=function(t){var e,o;t.normalize();const l=re.call(t.ownerDocument||t,t,S.SHOW_TEXT|S.SHOW_COMMENT|S.SHOW_CDATA_SECTION|S.SHOW_PROCESSING_INSTRUCTION,null);let u=l.nextNode();for(;u;){let O=u.data;ot([Nt,Ot,wt],k=>{O=ft(O,k," ")}),u.data=O,u=l.nextNode()}const m=(e=(o=t.querySelectorAll)===null||o===void 0?void 0:o.call(t,"template"))!==null&&e!==void 0?e:[];ot(Array.from(m),O=>{ct(O.content)&&Vt(O.content)})},kt=function(t){const e=M?M(t):null;return typeof e!="string"||g(e)!=="form"?!1:typeof t.nodeName!="string"||typeof t.textContent!="string"||typeof t.removeChild!="function"||t.attributes!==F(t)||typeof t.removeAttribute!="function"||typeof t.setAttribute!="function"||typeof t.namespaceURI!="string"||typeof t.insertBefore!="function"||typeof t.hasChildNodes!="function"||t.nodeType!==D(t)||t.childNodes!==q(t)},ct=function(t){if(!D||typeof t!="object"||t===null)return!1;try{return D(t)===j.documentFragment}catch{return!1}},Lt=function(t){if(!D||typeof t!="object"||t===null)return!1;try{return typeof D(t)=="number"}catch{return!1}};function $(t,e,o){ot(t,l=>{l.call(r,e,o,lt)})}const Ee=function(t){let e=null;if($(b.beforeSanitizeElements,t,null),kt(t))return et(t),!0;const o=g(M?M(t):t.nodeName);if($(b.uponSanitizeElement,t,{tagName:o,allowedTags:d}),Tt&&t.hasChildNodes()&&!Lt(t.firstElementChild)&&_(/<[/\w!]/g,t.innerHTML)&&_(/<[/\w!]/g,t.textContent)||Tt&&t.namespaceURI===H&&o==="style"&&Lt(t.firstElementChild)||t.nodeType===j.progressingInstruction||Tt&&t.nodeType===j.comment&&_(/<[/\w]/g,t.data))return et(t),!0;if(yt[o]||!(K.tagCheck instanceof Function&&K.tagCheck(o))&&!d[o]){if(!yt[o]&&_e(o)&&(h.tagNameCheck instanceof RegExp&&_(h.tagNameCheck,o)||h.tagNameCheck instanceof Function&&h.tagNameCheck(o)))return!1;if(Gt&&!z[o]){const l=X(t),u=q(t);if(u&&l){const m=u.length;for(let O=m-1;O>=0;--O){const k=Pt(u[O],!0);l.insertBefore(k,_t(t))}}}return et(t),!0}return(D?D(t):t.nodeType)===j.element&&!tn(t)||(o==="noscript"||o==="noembed"||o==="noframes")&&_(/<\/no(script|embed|frames)/i,t.innerHTML)?(et(t),!0):(V&&t.nodeType===j.text&&(e=t.textContent,ot([Nt,Ot,wt],l=>{e=ft(e,l," ")}),t.textContent!==e&&(mt(r.removed,{element:t.cloneNode()}),t.textContent=e)),$(b.afterSanitizeElements,t,null),!1)},Se=function(t,e,o){if(vt[e]||ue&&(e==="id"||e==="name")&&(o in i||o in Je))return!1;const l=y[e]||K.attributeCheck instanceof Function&&K.attributeCheck(e,t);if(!(zt&&!vt[e]&&_(Ye,e))&&!(le&&_(qe,e))){if(!l||vt[e]){if(!(_e(t)&&(h.tagNameCheck instanceof RegExp&&_(h.tagNameCheck,t)||h.tagNameCheck instanceof Function&&h.tagNameCheck(t))&&(h.attributeNameCheck instanceof RegExp&&_(h.attributeNameCheck,e)||h.attributeNameCheck instanceof Function&&h.attributeNameCheck(e,t))||e==="is"&&h.allowCustomizedBuiltInElements&&(h.tagNameCheck instanceof RegExp&&_(h.tagNameCheck,o)||h.tagNameCheck instanceof Function&&h.tagNameCheck(o))))return!1}else if(!Bt[e]&&!_(ae,ft(o,ie,""))&&!((e==="src"||e==="xlink:href"||e==="href")&&t!=="script"&&Ie(o,"data:")===0&&pe[t])&&!(ce&&!_(Xe,ft(o,ie,"")))&&o)return!1}return!0},en=c({},["annotation-xml","color-profile","font-face","font-face-format","font-face-name","font-face-src","font-face-uri","missing-glyph"]),_e=function(t){return!en[St(t)]&&_($e,t)},Ne=function(t){$(b.beforeSanitizeAttributes,t,null);const e=t.attributes;if(!e||kt(t))return;const o={attrName:"",attrValue:"",keepAttr:!0,allowedAttributes:y,forceKeepAttr:void 0};let l=e.length;for(;l--;){const u=e[l],m=u.name,O=u.namespaceURI,k=u.value,P=g(m),ut=k;let A=m==="value"?ut:dn(ut);if(o.attrName=P,o.attrValue=A,o.keepAttr=!0,o.forceKeepAttr=void 0,$(b.uponSanitizeAttribute,t,o),A=o.attrValue,me&&(P==="id"||P==="name")&&Ie(A,fe)!==0&&(nt(m,t),A=fe+A),Tt&&_(/((--!?|])>)|<\/(style|script|title|xmp|textarea|noscript|iframe|noembed|noframes)/i,A)){nt(m,t);continue}if(P==="attributename"&&Re(A,"href")){nt(m,t);continue}if(o.forceKeepAttr)continue;if(!o.keepAttr){nt(m,t);continue}if(!se&&_(/\/>/i,A)){nt(m,t);continue}V&&ot([Nt,Ot,wt],nn=>{A=ft(A,nn," ")});const Oe=g(t.nodeName);if(!Se(Oe,P,A)){nt(m,t);continue}if(N&&typeof x=="object"&&typeof x.getAttributeType=="function"&&!O)switch(x.getAttributeType(Oe,P)){case"TrustedHTML":{A=U(A);break}case"TrustedScriptURL":{A=N.createScriptURL(A);break}}if(A!==ut)try{O?t.setAttributeNS(O,m,A):t.setAttribute(m,A),kt(t)?et(t):De(r.removed)}catch{nt(m,t)}}$(b.afterSanitizeAttributes,t,null)},xt=function(t){let e=null;const o=Ae(t);for($(b.beforeSanitizeShadowDOM,t,null);e=o.nextNode();)if($(b.uponSanitizeShadowNode,e,null),Ee(e),Ne(e),ct(e.content)&&xt(e.content),(D?D(e):e.nodeType)===j.element){const l=v?v(e):e.shadowRoot;ct(l)&&(st(l),xt(l))}$(b.afterSanitizeShadowDOM,t,null)},st=function(t){const e=D?D(t):t.nodeType;if(e===j.element){const u=v?v(t):t.shadowRoot;ct(u)&&(st(u),xt(u))}const o=q?q(t):t.childNodes;if(!o)return;const l=[];ot(o,u=>{mt(l,u)});for(const u of l)st(u);if(e===j.element){const u=M?M(t):null;if(typeof u=="string"&&g(u)==="template"){const m=t.content;ct(m)&&st(m)}}};return r.sanitize=function(t){let e=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},o=null,l=null,u=null,m=null;if(Yt=!t,Yt&&(t=""),typeof t!="string"&&!Lt(t)&&(t=bn(t),typeof t!="string"))throw pt("dirty is not a string, aborting");if(!r.isSupported)return t;if(Ht||Kt(e),r.removed=[],typeof t=="string"&&(bt=!1),bt){const P=M?M(t):t.nodeName;if(typeof P=="string"){const ut=g(P);if(!d[ut]||yt[ut])throw pt("root node is forbidden and cannot be sanitized in-place")}if(kt(t))throw pt("root node is clobbered and cannot be sanitized in-place");st(t)}else if(Lt(t))o=be(""),l=o.ownerDocument.importNode(t,!0),l.nodeType===j.element&&l.nodeName==="BODY"||l.nodeName==="HTML"?o=l:o.appendChild(l),st(l);else{if(!rt&&!V&&!tt&&t.indexOf("<")===-1)return N&&Rt?U(t):t;if(o=be(t),!o)return rt?null:Rt?Q:""}o&&jt&&et(o.firstChild);const O=Ae(bt?t:o);for(;u=O.nextNode();)Ee(u),Ne(u),ct(u.content)&&xt(u.content);if(bt)return V&&Vt(t),t;if(rt){if(V&&Vt(o),Dt)for(m=Ge.call(o.ownerDocument);o.firstChild;)m.appendChild(o.firstChild);else m=o;return(y.shadowroot||y.shadowrootmode)&&(m=Be.call(a,m,!0)),m}let k=tt?o.outerHTML:o.innerHTML;return tt&&d["!doctype"]&&o.ownerDocument&&o.ownerDocument.doctype&&o.ownerDocument.doctype.name&&_(In,o.ownerDocument.doctype.name)&&(k="
+`+k),V&&ot([Nt,Ot,wt],P=>{k=ft(k,P," ")}),N&&Rt?U(k):k},r.setConfig=function(){let t=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};Kt(t),Ht=!0},r.clearConfig=function(){lt=null,Ht=!1},r.isValidAttribute=function(t,e,o){lt||Kt({});const l=g(t),u=g(e);return Se(l,u,o)},r.addHook=function(t,e){typeof e=="function"&&mt(b[t],e)},r.removeHook=function(t,e){if(e!==void 0){const o=fn(b[t],e);return o===-1?void 0:pn(b[t],o,1)[0]}return De(b[t])},r.removeHooks=function(t){b[t]=[]},r.removeAllHooks=function(){b=Ue()},r}var xn=je();const Mn=["p","br","ul","ol","li","h1","h2","h3","h4","h5","h6","strong","b","em","i","u","s","del","blockquote","pre","code"];function Fn(n){return xn.sanitize(String(n??""),{ALLOWED_TAGS:Mn,ALLOWED_ATTR:[],ALLOW_DATA_ATTR:!1})}export{Pn as c,Fn as s};
+//# sourceMappingURL=sanitize-html-dv-YifbT.chunk.mjs.map
diff --git a/js/pad-sync-B0gz-oBk.chunk.mjs.license b/js/sanitize-html-dv-YifbT.chunk.mjs.license
similarity index 53%
rename from js/pad-sync-B0gz-oBk.chunk.mjs.license
rename to js/sanitize-html-dv-YifbT.chunk.mjs.license
index 9d9b10c..99f1f90 100644
--- a/js/pad-sync-B0gz-oBk.chunk.mjs.license
+++ b/js/sanitize-html-dv-YifbT.chunk.mjs.license
@@ -1,7 +1,12 @@
+SPDX-License-Identifier: (MPL-2.0 OR Apache-2.0)
SPDX-License-Identifier: AGPL-3.0-or-later
+SPDX-FileCopyrightText: Dr.-Ing. Mario Heiderich, Cure53 (https://cure53.de/)
SPDX-FileCopyrightText: etherpad-nextcloud developers
This file is generated from multiple sources. Included packages:
+- dompurify
+ - version: 3.4.8
+ - license: (MPL-2.0 OR Apache-2.0)
- etherpad-nextcloud
- version: 1.1.0-alpha.2
- license: AGPL-3.0-or-later
diff --git a/js/sanitize-html-dv-YifbT.chunk.mjs.map b/js/sanitize-html-dv-YifbT.chunk.mjs.map
new file mode 100644
index 0000000..7350606
--- /dev/null
+++ b/js/sanitize-html-dv-YifbT.chunk.mjs.map
@@ -0,0 +1 @@
+{"version":3,"file":"sanitize-html-dv-YifbT.chunk.mjs","sources":["../src/lib/pad-sync.js","../node_modules/dompurify/dist/purify.es.mjs","../src/lib/sanitize-html.js"],"sourcesContent":["/**\n * SPDX-License-Identifier: AGPL-3.0-or-later\n * Copyright (c) 2026 Jacob Bühler\n */\n\n/**\n * Shared pad-sync loop for the viewer and embed entrypoints.\n *\n * Owns the periodic background sync, forced/keepalive flushes, and the\n * visibility/pagehide lifecycle wiring. Forced syncs coalesce: while one\n * request is in flight a second forced flush is not started concurrently but\n * remembered and replayed once, so two forced flushes can never overlap (this\n * is the embed behavior, now shared — the viewer previously used a plain\n * in-flight boolean that allowed overlap).\n *\n * The caller supplies the request-token getter (the viewer and embed read it\n * from different places) and may inject a `fetchImpl` for tests.\n */\n\nexport const DEFAULT_SYNC_INTERVAL_MS = 120000\n\nexport function createPadSync({ requestToken, fetchImpl } = {}) {\n\tconst doFetch = typeof fetchImpl === 'function' ? fetchImpl : (...args) => window.fetch(...args)\n\tconst getToken = typeof requestToken === 'function' ? requestToken : () => ''\n\n\tlet syncUrl = ''\n\tlet intervalMs = DEFAULT_SYNC_INTERVAL_MS\n\tlet syncPromise = null\n\tlet activeSyncForce = false\n\tlet pendingForcedSync = false\n\tlet pendingForcedKeepalive = false\n\tlet timerId = null\n\tlet visibilityHandler = null\n\tlet pageHideHandler = null\n\n\tconst configure = ({ syncUrl: url, intervalMs: ms } = {}) => {\n\t\tif (typeof url === 'string') {\n\t\t\tsyncUrl = url\n\t\t}\n\t\tif (Number.isFinite(ms) && ms > 0) {\n\t\t\tintervalMs = ms\n\t\t}\n\t}\n\n\tconst stop = () => {\n\t\tif (timerId !== null) {\n\t\t\twindow.clearInterval(timerId)\n\t\t\ttimerId = null\n\t\t}\n\t}\n\n\tconst start = () => {\n\t\tif (!syncUrl || timerId !== null) {\n\t\t\treturn\n\t\t}\n\t\ttimerId = window.setInterval(() => {\n\t\t\tif (document.visibilityState === 'visible') {\n\t\t\t\tfireAndForget(false, false)\n\t\t\t}\n\t\t}, intervalMs)\n\t}\n\n\tconst fireAndForget = (force, keepalive) => {\n\t\tvoid sync(force, keepalive).catch(() => {})\n\t}\n\n\tconst sync = async (force, keepalive) => {\n\t\tif (!syncUrl) {\n\t\t\treturn { status: 'disabled' }\n\t\t}\n\t\tif (syncPromise) {\n\t\t\t// A request is already running. A forced flush that arrives while a\n\t\t\t// non-forced sync is in flight is coalesced into a single replay.\n\t\t\tif (force && !activeSyncForce) {\n\t\t\t\tpendingForcedSync = true\n\t\t\t\tpendingForcedKeepalive = pendingForcedKeepalive || Boolean(keepalive)\n\t\t\t\treturn syncPromise.catch(() => undefined).then(() => sync(true, pendingForcedKeepalive))\n\t\t\t}\n\t\t\treturn syncPromise\n\t\t}\n\t\tactiveSyncForce = Boolean(force)\n\t\tconst currentPromise = (async () => {\n\t\t\tconst url = force ? (syncUrl + (syncUrl.includes('?') ? '&' : '?') + 'force=1') : syncUrl\n\t\t\tconst response = await doFetch(url, {\n\t\t\t\tmethod: 'POST',\n\t\t\t\tcredentials: 'same-origin',\n\t\t\t\theaders: {\n\t\t\t\t\tAccept: 'application/json',\n\t\t\t\t\trequesttoken: getToken(),\n\t\t\t\t},\n\t\t\t\tkeepalive: Boolean(keepalive),\n\t\t\t})\n\t\t\tconst data = await response.json().catch(() => ({}))\n\t\t\tif (!response.ok) {\n\t\t\t\tthrow new Error((data && data.message) || 'Sync request failed.')\n\t\t\t}\n\t\t\treturn data\n\t\t})()\n\t\tsyncPromise = currentPromise\n\t\tlet result\n\t\tlet syncError = null\n\t\ttry {\n\t\t\tresult = await currentPromise\n\t\t} catch (error) {\n\t\t\tsyncError = error\n\t\t} finally {\n\t\t\tif (syncPromise === currentPromise) {\n\t\t\t\tsyncPromise = null\n\t\t\t}\n\t\t\tactiveSyncForce = false\n\t\t}\n\t\tconst rerunForcedSync = pendingForcedSync\n\t\tconst rerunKeepalive = pendingForcedKeepalive\n\t\tpendingForcedSync = false\n\t\tpendingForcedKeepalive = false\n\t\tif (rerunForcedSync) {\n\t\t\treturn sync(true, rerunKeepalive)\n\t\t}\n\t\tif (syncError instanceof Error) {\n\t\t\tthrow syncError\n\t\t}\n\t\treturn result\n\t}\n\n\tconst installLifecycleHandlers = () => {\n\t\tif (visibilityHandler || pageHideHandler) {\n\t\t\treturn\n\t\t}\n\t\tvisibilityHandler = () => {\n\t\t\tif (document.visibilityState === 'hidden') {\n\t\t\t\tfireAndForget(true, true)\n\t\t\t\tstop()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tstart()\n\t\t}\n\t\tpageHideHandler = () => {\n\t\t\tfireAndForget(true, true)\n\t\t\tstop()\n\t\t}\n\t\tdocument.addEventListener('visibilitychange', visibilityHandler)\n\t\twindow.addEventListener('pagehide', pageHideHandler)\n\t}\n\n\tconst removeLifecycleHandlers = () => {\n\t\tif (visibilityHandler) {\n\t\t\tdocument.removeEventListener('visibilitychange', visibilityHandler)\n\t\t\tvisibilityHandler = null\n\t\t}\n\t\tif (pageHideHandler) {\n\t\t\twindow.removeEventListener('pagehide', pageHideHandler)\n\t\t\tpageHideHandler = null\n\t\t}\n\t}\n\n\treturn {\n\t\tconfigure,\n\t\tstart,\n\t\tstop,\n\t\tsync,\n\t\tfireAndForget,\n\t\tinstallLifecycleHandlers,\n\t\tremoveLifecycleHandlers,\n\t}\n}\n","/*! @license DOMPurify 3.4.8 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.8/LICENSE */\n\nfunction _arrayLikeToArray(r, a) {\n (null == a || a > r.length) && (a = r.length);\n for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];\n return n;\n}\nfunction _arrayWithHoles(r) {\n if (Array.isArray(r)) return r;\n}\nfunction _iterableToArrayLimit(r, l) {\n var t = null == r ? null : \"undefined\" != typeof Symbol && r[Symbol.iterator] || r[\"@@iterator\"];\n if (null != t) {\n var e,\n n,\n i,\n u,\n a = [],\n f = true,\n o = false;\n try {\n if (i = (t = t.call(r)).next, 0 === l) ; else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);\n } catch (r) {\n o = true, n = r;\n } finally {\n try {\n if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;\n } finally {\n if (o) throw n;\n }\n }\n return a;\n }\n}\nfunction _nonIterableRest() {\n throw new TypeError(\"Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\");\n}\nfunction _slicedToArray(r, e) {\n return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();\n}\nfunction _unsupportedIterableToArray(r, a) {\n if (r) {\n if (\"string\" == typeof r) return _arrayLikeToArray(r, a);\n var t = {}.toString.call(r).slice(8, -1);\n return \"Object\" === t && r.constructor && (t = r.constructor.name), \"Map\" === t || \"Set\" === t ? Array.from(r) : \"Arguments\" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;\n }\n}\n\nconst entries = Object.entries,\n setPrototypeOf = Object.setPrototypeOf,\n isFrozen = Object.isFrozen,\n getPrototypeOf = Object.getPrototypeOf,\n getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;\nlet freeze = Object.freeze,\n seal = Object.seal,\n create = Object.create; // eslint-disable-line import/no-mutable-exports\nlet _ref = typeof Reflect !== 'undefined' && Reflect,\n apply = _ref.apply,\n construct = _ref.construct;\nif (!freeze) {\n freeze = function freeze(x) {\n return x;\n };\n}\nif (!seal) {\n seal = function seal(x) {\n return x;\n };\n}\nif (!apply) {\n apply = function apply(func, thisArg) {\n for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {\n args[_key - 2] = arguments[_key];\n }\n return func.apply(thisArg, args);\n };\n}\nif (!construct) {\n construct = function construct(Func) {\n for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {\n args[_key2 - 1] = arguments[_key2];\n }\n return new Func(...args);\n };\n}\nconst arrayForEach = unapply(Array.prototype.forEach);\nconst arrayLastIndexOf = unapply(Array.prototype.lastIndexOf);\nconst arrayPop = unapply(Array.prototype.pop);\nconst arrayPush = unapply(Array.prototype.push);\nconst arraySplice = unapply(Array.prototype.splice);\nconst arrayIsArray = Array.isArray;\nconst stringToLowerCase = unapply(String.prototype.toLowerCase);\nconst stringToString = unapply(String.prototype.toString);\nconst stringMatch = unapply(String.prototype.match);\nconst stringReplace = unapply(String.prototype.replace);\nconst stringIndexOf = unapply(String.prototype.indexOf);\nconst stringTrim = unapply(String.prototype.trim);\nconst numberToString = unapply(Number.prototype.toString);\nconst booleanToString = unapply(Boolean.prototype.toString);\nconst bigintToString = typeof BigInt === 'undefined' ? null : unapply(BigInt.prototype.toString);\nconst symbolToString = typeof Symbol === 'undefined' ? null : unapply(Symbol.prototype.toString);\nconst objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);\nconst objectToString = unapply(Object.prototype.toString);\nconst regExpTest = unapply(RegExp.prototype.test);\nconst typeErrorCreate = unconstruct(TypeError);\n/**\n * Creates a new function that calls the given function with a specified thisArg and arguments.\n *\n * @param func - The function to be wrapped and called.\n * @returns A new function that calls the given function with a specified thisArg and arguments.\n */\nfunction unapply(func) {\n return function (thisArg) {\n if (thisArg instanceof RegExp) {\n thisArg.lastIndex = 0;\n }\n for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {\n args[_key3 - 1] = arguments[_key3];\n }\n return apply(func, thisArg, args);\n };\n}\n/**\n * Creates a new function that constructs an instance of the given constructor function with the provided arguments.\n *\n * @param func - The constructor function to be wrapped and called.\n * @returns A new function that constructs an instance of the given constructor function with the provided arguments.\n */\nfunction unconstruct(Func) {\n return function () {\n for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {\n args[_key4] = arguments[_key4];\n }\n return construct(Func, args);\n };\n}\n/**\n * Add properties to a lookup table\n *\n * @param set - The set to which elements will be added.\n * @param array - The array containing elements to be added to the set.\n * @param transformCaseFunc - An optional function to transform the case of each element before adding to the set.\n * @returns The modified set with added elements.\n */\nfunction addToSet(set, array) {\n let transformCaseFunc = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : stringToLowerCase;\n if (setPrototypeOf) {\n // Make 'in' and truthy checks like Boolean(set.constructor)\n // independent of any properties defined on Object.prototype.\n // Prevent prototype setters from intercepting set as a this value.\n setPrototypeOf(set, null);\n }\n if (!arrayIsArray(array)) {\n return set;\n }\n let l = array.length;\n while (l--) {\n let element = array[l];\n if (typeof element === 'string') {\n const lcElement = transformCaseFunc(element);\n if (lcElement !== element) {\n // Config presets (e.g. tags.js, attrs.js) are immutable.\n if (!isFrozen(array)) {\n array[l] = lcElement;\n }\n element = lcElement;\n }\n }\n set[element] = true;\n }\n return set;\n}\n/**\n * Clean up an array to harden against CSPP\n *\n * @param array - The array to be cleaned.\n * @returns The cleaned version of the array\n */\nfunction cleanArray(array) {\n for (let index = 0; index < array.length; index++) {\n const isPropertyExist = objectHasOwnProperty(array, index);\n if (!isPropertyExist) {\n array[index] = null;\n }\n }\n return array;\n}\n/**\n * Shallow clone an object\n *\n * @param object - The object to be cloned.\n * @returns A new object that copies the original.\n */\nfunction clone(object) {\n const newObject = create(null);\n for (const _ref2 of entries(object)) {\n var _ref3 = _slicedToArray(_ref2, 2);\n const property = _ref3[0];\n const value = _ref3[1];\n const isPropertyExist = objectHasOwnProperty(object, property);\n if (isPropertyExist) {\n if (arrayIsArray(value)) {\n newObject[property] = cleanArray(value);\n } else if (value && typeof value === 'object' && value.constructor === Object) {\n newObject[property] = clone(value);\n } else {\n newObject[property] = value;\n }\n }\n }\n return newObject;\n}\n/**\n * Convert non-node values into strings without depending on direct property access.\n *\n * @param value - The value to stringify.\n * @returns A string representation of the provided value.\n */\nfunction stringifyValue(value) {\n switch (typeof value) {\n case 'string':\n {\n return value;\n }\n case 'number':\n {\n return numberToString(value);\n }\n case 'boolean':\n {\n return booleanToString(value);\n }\n case 'bigint':\n {\n return bigintToString ? bigintToString(value) : '0';\n }\n case 'symbol':\n {\n return symbolToString ? symbolToString(value) : 'Symbol()';\n }\n case 'undefined':\n {\n return objectToString(value);\n }\n case 'function':\n case 'object':\n {\n if (value === null) {\n return objectToString(value);\n }\n const valueAsRecord = value;\n const valueToString = lookupGetter(valueAsRecord, 'toString');\n if (typeof valueToString === 'function') {\n const stringified = valueToString(valueAsRecord);\n return typeof stringified === 'string' ? stringified : objectToString(stringified);\n }\n return objectToString(value);\n }\n default:\n {\n return objectToString(value);\n }\n }\n}\n/**\n * This method automatically checks if the prop is function or getter and behaves accordingly.\n *\n * @param object - The object to look up the getter function in its prototype chain.\n * @param prop - The property name for which to find the getter function.\n * @returns The getter function found in the prototype chain or a fallback function.\n */\nfunction lookupGetter(object, prop) {\n while (object !== null) {\n const desc = getOwnPropertyDescriptor(object, prop);\n if (desc) {\n if (desc.get) {\n return unapply(desc.get);\n }\n if (typeof desc.value === 'function') {\n return unapply(desc.value);\n }\n }\n object = getPrototypeOf(object);\n }\n function fallbackValue() {\n return null;\n }\n return fallbackValue;\n}\nfunction isRegex(value) {\n try {\n regExpTest(value, '');\n return true;\n } catch (_unused) {\n return false;\n }\n}\n\nconst html$1 = freeze(['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'content', 'data', 'datalist', 'dd', 'decorator', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'main', 'map', 'mark', 'marquee', 'menu', 'menuitem', 'meter', 'nav', 'nobr', 'ol', 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'search', 'section', 'select', 'shadow', 'slot', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr']);\nconst svg$1 = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'enterkeyhint', 'exportparts', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'inputmode', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'mpath', 'part', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'view', 'vkern']);\nconst svgFilters = freeze(['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feDropShadow', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence']);\n// List of SVG elements that are disallowed by default.\n// We still need to know them so that we can do namespace\n// checks properly in case one wants to add them to\n// allow-list.\nconst svgDisallowed = freeze(['animate', 'color-profile', 'cursor', 'discard', 'font-face', 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', 'foreignobject', 'hatch', 'hatchpath', 'mesh', 'meshgradient', 'meshpatch', 'meshrow', 'missing-glyph', 'script', 'set', 'solidcolor', 'unknown', 'use']);\nconst mathMl$1 = freeze(['math', 'menclose', 'merror', 'mfenced', 'mfrac', 'mglyph', 'mi', 'mlabeledtr', 'mmultiscripts', 'mn', 'mo', 'mover', 'mpadded', 'mphantom', 'mroot', 'mrow', 'ms', 'mspace', 'msqrt', 'mstyle', 'msub', 'msup', 'msubsup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder', 'munderover', 'mprescripts']);\n// Similarly to SVG, we want to know all MathML elements,\n// even those that we disallow by default.\nconst mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);\nconst text = freeze(['#text']);\n\nconst html = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'command', 'commandfor', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'exportparts', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inert', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'nonce', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'part', 'pattern', 'placeholder', 'playsinline', 'popover', 'popovertarget', 'popovertargetaction', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'slot', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'wrap', 'xmlns']);\nconst svg = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'amplitude', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', 'clippathunits', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cx', 'cy', 'd', 'dx', 'dy', 'diffuseconstant', 'direction', 'display', 'divisor', 'dur', 'edgemode', 'elevation', 'end', 'exponent', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'filterunits', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'fx', 'fy', 'g1', 'g2', 'glyph-name', 'glyphref', 'gradientunits', 'gradienttransform', 'height', 'href', 'id', 'image-rendering', 'in', 'in2', 'intercept', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'mask-type', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'slope', 'specularconstant', 'specularexponent', 'spreadmethod', 'startoffset', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'systemlanguage', 'tabindex', 'tablevalues', 'targetx', 'targety', 'transform', 'transform-origin', 'text-anchor', 'text-decoration', 'text-rendering', 'textlength', 'type', 'u1', 'u2', 'unicode', 'values', 'viewbox', 'visibility', 'version', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'width', 'word-spacing', 'wrap', 'writing-mode', 'xchannelselector', 'ychannelselector', 'x', 'x1', 'x2', 'xmlns', 'y', 'y1', 'y2', 'z', 'zoomandpan']);\nconst mathMl = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnalign', 'columnlines', 'columnspacing', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', 'encoding', 'fence', 'frame', 'height', 'href', 'id', 'largeop', 'length', 'linethickness', 'lquote', 'lspace', 'mathbackground', 'mathcolor', 'mathsize', 'mathvariant', 'maxsize', 'minsize', 'movablelimits', 'notation', 'numalign', 'open', 'rowalign', 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'rquote', 'scriptlevel', 'scriptminsize', 'scriptsizemultiplier', 'selection', 'separator', 'separators', 'stretchy', 'subscriptshift', 'supscriptshift', 'symmetric', 'voffset', 'width', 'xmlns']);\nconst xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);\n\nconst MUSTACHE_EXPR = seal(/{{[\\w\\W]*|^[\\w\\W]*}}/g);\nconst ERB_EXPR = seal(/<%[\\w\\W]*|^[\\w\\W]*%>/g);\nconst TMPLIT_EXPR = seal(/\\${[\\w\\W]*/g);\nconst DATA_ATTR = seal(/^data-[\\-\\w.\\u00B7-\\uFFFF]+$/); // eslint-disable-line no-useless-escape\nconst ARIA_ATTR = seal(/^aria-[\\-\\w]+$/); // eslint-disable-line no-useless-escape\nconst IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\\-]+(?:[^a-z+.\\-:]|$))/i // eslint-disable-line no-useless-escape\n);\nconst IS_SCRIPT_OR_DATA = seal(/^(?:\\w+script|data):/i);\nconst ATTR_WHITESPACE = seal(/[\\u0000-\\u0020\\u00A0\\u1680\\u180E\\u2000-\\u2029\\u205F\\u3000]/g // eslint-disable-line no-control-regex\n);\nconst DOCTYPE_NAME = seal(/^html$/i);\nconst CUSTOM_ELEMENT = seal(/^[a-z][.\\w]*(-[.\\w]+)+$/i);\n\n/* eslint-disable @typescript-eslint/indent */\n// https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType\nconst NODE_TYPE = {\n element: 1,\n attribute: 2,\n text: 3,\n cdataSection: 4,\n entityReference: 5,\n // Deprecated\n entityNode: 6,\n // Deprecated\n progressingInstruction: 7,\n comment: 8,\n document: 9,\n documentType: 10,\n documentFragment: 11,\n notation: 12 // Deprecated\n};\nconst getGlobal = function getGlobal() {\n return typeof window === 'undefined' ? null : window;\n};\n/**\n * Creates a no-op policy for internal use only.\n * Don't export this function outside this module!\n * @param trustedTypes The policy factory.\n * @param purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix).\n * @return The policy created (or null, if Trusted Types\n * are not supported or creating the policy failed).\n */\nconst _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, purifyHostElement) {\n if (typeof trustedTypes !== 'object' || typeof trustedTypes.createPolicy !== 'function') {\n return null;\n }\n // Allow the callers to control the unique policy name\n // by adding a data-tt-policy-suffix to the script element with the DOMPurify.\n // Policy creation with duplicate names throws in Trusted Types.\n let suffix = null;\n const ATTR_NAME = 'data-tt-policy-suffix';\n if (purifyHostElement && purifyHostElement.hasAttribute(ATTR_NAME)) {\n suffix = purifyHostElement.getAttribute(ATTR_NAME);\n }\n const policyName = 'dompurify' + (suffix ? '#' + suffix : '');\n try {\n return trustedTypes.createPolicy(policyName, {\n createHTML(html) {\n return html;\n },\n createScriptURL(scriptUrl) {\n return scriptUrl;\n }\n });\n } catch (_) {\n // Policy creation failed (most likely another DOMPurify script has\n // already run). Skip creating the policy, as this will only cause errors\n // if TT are enforced.\n console.warn('TrustedTypes policy ' + policyName + ' could not be created.');\n return null;\n }\n};\nconst _createHooksMap = function _createHooksMap() {\n return {\n afterSanitizeAttributes: [],\n afterSanitizeElements: [],\n afterSanitizeShadowDOM: [],\n beforeSanitizeAttributes: [],\n beforeSanitizeElements: [],\n beforeSanitizeShadowDOM: [],\n uponSanitizeAttribute: [],\n uponSanitizeElement: [],\n uponSanitizeShadowNode: []\n };\n};\nfunction createDOMPurify() {\n let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();\n const DOMPurify = root => createDOMPurify(root);\n DOMPurify.version = '3.4.8';\n DOMPurify.removed = [];\n if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {\n // Not running in a browser, provide a factory function\n // so that you can pass your own Window\n DOMPurify.isSupported = false;\n return DOMPurify;\n }\n let document = window.document;\n const originalDocument = document;\n const currentScript = originalDocument.currentScript;\n window.DocumentFragment;\n const HTMLTemplateElement = window.HTMLTemplateElement,\n Node = window.Node,\n Element = window.Element,\n NodeFilter = window.NodeFilter,\n _window$NamedNodeMap = window.NamedNodeMap;\n _window$NamedNodeMap === void 0 ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap;\n window.HTMLFormElement;\n const DOMParser = window.DOMParser,\n trustedTypes = window.trustedTypes;\n const ElementPrototype = Element.prototype;\n const cloneNode = lookupGetter(ElementPrototype, 'cloneNode');\n const remove = lookupGetter(ElementPrototype, 'remove');\n const getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');\n const getChildNodes = lookupGetter(ElementPrototype, 'childNodes');\n const getParentNode = lookupGetter(ElementPrototype, 'parentNode');\n const getShadowRoot = lookupGetter(ElementPrototype, 'shadowRoot');\n const getAttributes = lookupGetter(ElementPrototype, 'attributes');\n const getNodeType = Node && Node.prototype ? lookupGetter(Node.prototype, 'nodeType') : null;\n const getNodeName = Node && Node.prototype ? lookupGetter(Node.prototype, 'nodeName') : null;\n // As per issue #47, the web-components registry is inherited by a\n // new document created via createHTMLDocument. As per the spec\n // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)\n // a new empty registry is used when creating a template contents owner\n // document, so we use that as our parent document to ensure nothing\n // is inherited.\n if (typeof HTMLTemplateElement === 'function') {\n const template = document.createElement('template');\n if (template.content && template.content.ownerDocument) {\n document = template.content.ownerDocument;\n }\n }\n let trustedTypesPolicy;\n let emptyHTML = '';\n // Tracks whether we are already inside a call to the configured Trusted Types\n // policy's `createHTML`. If the supplied `TRUSTED_TYPES_POLICY.createHTML`\n // itself calls `DOMPurify.sanitize` (the cause of #1422), `sanitize` would\n // re-enter the policy and recurse until the stack overflows. We detect that\n // re-entry and throw a clear, actionable error instead.\n let IN_POLICY_CREATE_HTML = 0;\n const _createTrustedHTML = function _createTrustedHTML(html) {\n if (IN_POLICY_CREATE_HTML > 0) {\n throw typeErrorCreate('The configured TRUSTED_TYPES_POLICY.createHTML must not call ' + 'DOMPurify.sanitize, as that causes infinite recursion. Do not pass ' + 'a policy whose createHTML wraps DOMPurify as TRUSTED_TYPES_POLICY; ' + 'see the \"DOMPurify and Trusted Types\" section of the README.');\n }\n IN_POLICY_CREATE_HTML++;\n try {\n return trustedTypesPolicy.createHTML(html);\n } finally {\n IN_POLICY_CREATE_HTML--;\n }\n };\n const _document = document,\n implementation = _document.implementation,\n createNodeIterator = _document.createNodeIterator,\n createDocumentFragment = _document.createDocumentFragment,\n getElementsByTagName = _document.getElementsByTagName;\n const importNode = originalDocument.importNode;\n let hooks = _createHooksMap();\n /**\n * Expose whether this browser supports running the full DOMPurify.\n */\n DOMPurify.isSupported = typeof entries === 'function' && typeof getParentNode === 'function' && implementation && implementation.createHTMLDocument !== undefined;\n const MUSTACHE_EXPR$1 = MUSTACHE_EXPR,\n ERB_EXPR$1 = ERB_EXPR,\n TMPLIT_EXPR$1 = TMPLIT_EXPR,\n DATA_ATTR$1 = DATA_ATTR,\n ARIA_ATTR$1 = ARIA_ATTR,\n IS_SCRIPT_OR_DATA$1 = IS_SCRIPT_OR_DATA,\n ATTR_WHITESPACE$1 = ATTR_WHITESPACE,\n CUSTOM_ELEMENT$1 = CUSTOM_ELEMENT;\n let IS_ALLOWED_URI$1 = IS_ALLOWED_URI;\n /**\n * We consider the elements and attributes below to be safe. Ideally\n * don't add any new ones but feel free to remove unwanted ones.\n */\n /* allowed element names */\n let ALLOWED_TAGS = null;\n const DEFAULT_ALLOWED_TAGS = addToSet({}, [...html$1, ...svg$1, ...svgFilters, ...mathMl$1, ...text]);\n /* Allowed attribute names */\n let ALLOWED_ATTR = null;\n const DEFAULT_ALLOWED_ATTR = addToSet({}, [...html, ...svg, ...mathMl, ...xml]);\n /*\n * Configure how DOMPurify should handle custom elements and their attributes as well as customized built-in elements.\n * @property {RegExp|Function|null} tagNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any custom elements)\n * @property {RegExp|Function|null} attributeNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any attributes not on the allow list)\n * @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`.\n */\n let CUSTOM_ELEMENT_HANDLING = Object.seal(create(null, {\n tagNameCheck: {\n writable: true,\n configurable: false,\n enumerable: true,\n value: null\n },\n attributeNameCheck: {\n writable: true,\n configurable: false,\n enumerable: true,\n value: null\n },\n allowCustomizedBuiltInElements: {\n writable: true,\n configurable: false,\n enumerable: true,\n value: false\n }\n }));\n /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */\n let FORBID_TAGS = null;\n /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */\n let FORBID_ATTR = null;\n /* Config object to store ADD_TAGS/ADD_ATTR functions (when used as functions) */\n const EXTRA_ELEMENT_HANDLING = Object.seal(create(null, {\n tagCheck: {\n writable: true,\n configurable: false,\n enumerable: true,\n value: null\n },\n attributeCheck: {\n writable: true,\n configurable: false,\n enumerable: true,\n value: null\n }\n }));\n /* Decide if ARIA attributes are okay */\n let ALLOW_ARIA_ATTR = true;\n /* Decide if custom data attributes are okay */\n let ALLOW_DATA_ATTR = true;\n /* Decide if unknown protocols are okay */\n let ALLOW_UNKNOWN_PROTOCOLS = false;\n /* Decide if self-closing tags in attributes are allowed.\n * Usually removed due to a mXSS issue in jQuery 3.0 */\n let ALLOW_SELF_CLOSE_IN_ATTR = true;\n /* Output should be safe for common template engines.\n * This means, DOMPurify removes data attributes, mustaches and ERB\n */\n let SAFE_FOR_TEMPLATES = false;\n /* Output should be safe even for XML used within HTML and alike.\n * This means, DOMPurify removes comments when containing risky content.\n */\n let SAFE_FOR_XML = true;\n /* Decide if document with ... should be returned */\n let WHOLE_DOCUMENT = false;\n /* Track whether config is already set on this instance of DOMPurify. */\n let SET_CONFIG = false;\n /* Decide if all elements (e.g. style, script) must be children of\n * document.body. By default, browsers might move them to document.head */\n let FORCE_BODY = false;\n /* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html\n * string (or a TrustedHTML object if Trusted Types are supported).\n * If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead\n */\n let RETURN_DOM = false;\n /* Decide if a DOM `DocumentFragment` should be returned, instead of a html\n * string (or a TrustedHTML object if Trusted Types are supported) */\n let RETURN_DOM_FRAGMENT = false;\n /* Try to return a Trusted Type object instead of a string, return a string in\n * case Trusted Types are not supported */\n let RETURN_TRUSTED_TYPE = false;\n /* Output should be free from DOM clobbering attacks?\n * This sanitizes markups named with colliding, clobberable built-in DOM APIs.\n */\n let SANITIZE_DOM = true;\n /* Achieve full DOM Clobbering protection by isolating the namespace of named\n * properties and JS variables, mitigating attacks that abuse the HTML/DOM spec rules.\n *\n * HTML/DOM spec rules that enable DOM Clobbering:\n * - Named Access on Window (§7.3.3)\n * - DOM Tree Accessors (§3.1.5)\n * - Form Element Parent-Child Relations (§4.10.3)\n * - Iframe srcdoc / Nested WindowProxies (§4.8.5)\n * - HTMLCollection (§4.2.10.2)\n *\n * Namespace isolation is implemented by prefixing `id` and `name` attributes\n * with a constant string, i.e., `user-content-`\n */\n let SANITIZE_NAMED_PROPS = false;\n const SANITIZE_NAMED_PROPS_PREFIX = 'user-content-';\n /* Keep element content when removing element? */\n let KEEP_CONTENT = true;\n /* If a `Node` is passed to sanitize(), then performs sanitization in-place instead\n * of importing it into a new Document and returning a sanitized copy */\n let IN_PLACE = false;\n /* Allow usage of profiles like html, svg and mathMl */\n let USE_PROFILES = {};\n /* Tags to ignore content of when KEEP_CONTENT is true */\n let FORBID_CONTENTS = null;\n const DEFAULT_FORBID_CONTENTS = addToSet({}, ['annotation-xml', 'audio', 'colgroup', 'desc', 'foreignobject', 'head', 'iframe', 'math', 'mi', 'mn', 'mo', 'ms', 'mtext', 'noembed', 'noframes', 'noscript', 'plaintext', 'script', 'style', 'svg', 'template', 'thead', 'title', 'video', 'xmp']);\n /* Tags that are safe for data: URIs */\n let DATA_URI_TAGS = null;\n const DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);\n /* Attributes safe for values like \"javascript:\" */\n let URI_SAFE_ATTRIBUTES = null;\n const DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']);\n const MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';\n const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';\n const HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';\n /* Document namespace */\n let NAMESPACE = HTML_NAMESPACE;\n let IS_EMPTY_INPUT = false;\n /* Allowed XHTML+XML namespaces */\n let ALLOWED_NAMESPACES = null;\n const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString);\n let MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);\n let HTML_INTEGRATION_POINTS = addToSet({}, ['annotation-xml']);\n // Certain elements are allowed in both SVG and HTML\n // namespace. We need to specify them explicitly\n // so that they don't get erroneously deleted from\n // HTML namespace.\n const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']);\n /* Parsing of strict XHTML documents */\n let PARSER_MEDIA_TYPE = null;\n const SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html'];\n const DEFAULT_PARSER_MEDIA_TYPE = 'text/html';\n let transformCaseFunc = null;\n /* Keep a reference to config to pass to hooks */\n let CONFIG = null;\n /* Ideally, do not touch anything below this line */\n /* ______________________________________________ */\n const formElement = document.createElement('form');\n const isRegexOrFunction = function isRegexOrFunction(testValue) {\n return testValue instanceof RegExp || testValue instanceof Function;\n };\n /**\n * _parseConfig\n *\n * @param cfg optional config literal\n */\n // eslint-disable-next-line complexity\n const _parseConfig = function _parseConfig() {\n let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n if (CONFIG && CONFIG === cfg) {\n return;\n }\n /* Shield configuration object from tampering */\n if (!cfg || typeof cfg !== 'object') {\n cfg = {};\n }\n /* Shield configuration object from prototype pollution */\n cfg = clone(cfg);\n PARSER_MEDIA_TYPE =\n // eslint-disable-next-line unicorn/prefer-includes\n SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? DEFAULT_PARSER_MEDIA_TYPE : cfg.PARSER_MEDIA_TYPE;\n // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.\n transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;\n /* Set configuration parameters */\n ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS') && arrayIsArray(cfg.ALLOWED_TAGS) ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;\n ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR') && arrayIsArray(cfg.ALLOWED_ATTR) ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;\n ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES') && arrayIsArray(cfg.ALLOWED_NAMESPACES) ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;\n URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') && arrayIsArray(cfg.ADD_URI_SAFE_ATTR) ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR, transformCaseFunc) : DEFAULT_URI_SAFE_ATTRIBUTES;\n DATA_URI_TAGS = objectHasOwnProperty(cfg, 'ADD_DATA_URI_TAGS') && arrayIsArray(cfg.ADD_DATA_URI_TAGS) ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS, transformCaseFunc) : DEFAULT_DATA_URI_TAGS;\n FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') && arrayIsArray(cfg.FORBID_CONTENTS) ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;\n FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') && arrayIsArray(cfg.FORBID_TAGS) ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : clone({});\n FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') && arrayIsArray(cfg.FORBID_ATTR) ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : clone({});\n USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES && typeof cfg.USE_PROFILES === 'object' ? clone(cfg.USE_PROFILES) : cfg.USE_PROFILES : false;\n ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true\n ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true\n ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false\n ALLOW_SELF_CLOSE_IN_ATTR = cfg.ALLOW_SELF_CLOSE_IN_ATTR !== false; // Default true\n SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false\n SAFE_FOR_XML = cfg.SAFE_FOR_XML !== false; // Default true\n WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false\n RETURN_DOM = cfg.RETURN_DOM || false; // Default false\n RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false\n RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false\n FORCE_BODY = cfg.FORCE_BODY || false; // Default false\n SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true\n SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; // Default false\n KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true\n IN_PLACE = cfg.IN_PLACE || false; // Default false\n IS_ALLOWED_URI$1 = isRegex(cfg.ALLOWED_URI_REGEXP) ? cfg.ALLOWED_URI_REGEXP : IS_ALLOWED_URI; // Default regexp\n NAMESPACE = typeof cfg.NAMESPACE === 'string' ? cfg.NAMESPACE : HTML_NAMESPACE; // Default HTML namespace\n MATHML_TEXT_INTEGRATION_POINTS = objectHasOwnProperty(cfg, 'MATHML_TEXT_INTEGRATION_POINTS') && cfg.MATHML_TEXT_INTEGRATION_POINTS && typeof cfg.MATHML_TEXT_INTEGRATION_POINTS === 'object' ? clone(cfg.MATHML_TEXT_INTEGRATION_POINTS) : addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']); // Default built-in map\n HTML_INTEGRATION_POINTS = objectHasOwnProperty(cfg, 'HTML_INTEGRATION_POINTS') && cfg.HTML_INTEGRATION_POINTS && typeof cfg.HTML_INTEGRATION_POINTS === 'object' ? clone(cfg.HTML_INTEGRATION_POINTS) : addToSet({}, ['annotation-xml']); // Default built-in map\n const customElementHandling = objectHasOwnProperty(cfg, 'CUSTOM_ELEMENT_HANDLING') && cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING === 'object' ? clone(cfg.CUSTOM_ELEMENT_HANDLING) : create(null);\n CUSTOM_ELEMENT_HANDLING = create(null);\n if (objectHasOwnProperty(customElementHandling, 'tagNameCheck') && isRegexOrFunction(customElementHandling.tagNameCheck)) {\n CUSTOM_ELEMENT_HANDLING.tagNameCheck = customElementHandling.tagNameCheck; // Default undefined\n }\n if (objectHasOwnProperty(customElementHandling, 'attributeNameCheck') && isRegexOrFunction(customElementHandling.attributeNameCheck)) {\n CUSTOM_ELEMENT_HANDLING.attributeNameCheck = customElementHandling.attributeNameCheck; // Default undefined\n }\n if (objectHasOwnProperty(customElementHandling, 'allowCustomizedBuiltInElements') && typeof customElementHandling.allowCustomizedBuiltInElements === 'boolean') {\n CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = customElementHandling.allowCustomizedBuiltInElements; // Default undefined\n }\n if (SAFE_FOR_TEMPLATES) {\n ALLOW_DATA_ATTR = false;\n }\n if (RETURN_DOM_FRAGMENT) {\n RETURN_DOM = true;\n }\n /* Parse profile info */\n if (USE_PROFILES) {\n ALLOWED_TAGS = addToSet({}, text);\n ALLOWED_ATTR = create(null);\n if (USE_PROFILES.html === true) {\n addToSet(ALLOWED_TAGS, html$1);\n addToSet(ALLOWED_ATTR, html);\n }\n if (USE_PROFILES.svg === true) {\n addToSet(ALLOWED_TAGS, svg$1);\n addToSet(ALLOWED_ATTR, svg);\n addToSet(ALLOWED_ATTR, xml);\n }\n if (USE_PROFILES.svgFilters === true) {\n addToSet(ALLOWED_TAGS, svgFilters);\n addToSet(ALLOWED_ATTR, svg);\n addToSet(ALLOWED_ATTR, xml);\n }\n if (USE_PROFILES.mathMl === true) {\n addToSet(ALLOWED_TAGS, mathMl$1);\n addToSet(ALLOWED_ATTR, mathMl);\n addToSet(ALLOWED_ATTR, xml);\n }\n }\n /* Always reset function-based ADD_TAGS / ADD_ATTR checks to prevent\n * leaking across calls when switching from function to array config */\n EXTRA_ELEMENT_HANDLING.tagCheck = null;\n EXTRA_ELEMENT_HANDLING.attributeCheck = null;\n /* Merge configuration parameters */\n if (objectHasOwnProperty(cfg, 'ADD_TAGS')) {\n if (typeof cfg.ADD_TAGS === 'function') {\n EXTRA_ELEMENT_HANDLING.tagCheck = cfg.ADD_TAGS;\n } else if (arrayIsArray(cfg.ADD_TAGS)) {\n if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {\n ALLOWED_TAGS = clone(ALLOWED_TAGS);\n }\n addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);\n }\n }\n if (objectHasOwnProperty(cfg, 'ADD_ATTR')) {\n if (typeof cfg.ADD_ATTR === 'function') {\n EXTRA_ELEMENT_HANDLING.attributeCheck = cfg.ADD_ATTR;\n } else if (arrayIsArray(cfg.ADD_ATTR)) {\n if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {\n ALLOWED_ATTR = clone(ALLOWED_ATTR);\n }\n addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);\n }\n }\n if (objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') && arrayIsArray(cfg.ADD_URI_SAFE_ATTR)) {\n addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);\n }\n if (objectHasOwnProperty(cfg, 'FORBID_CONTENTS') && arrayIsArray(cfg.FORBID_CONTENTS)) {\n if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {\n FORBID_CONTENTS = clone(FORBID_CONTENTS);\n }\n addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);\n }\n if (objectHasOwnProperty(cfg, 'ADD_FORBID_CONTENTS') && arrayIsArray(cfg.ADD_FORBID_CONTENTS)) {\n if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {\n FORBID_CONTENTS = clone(FORBID_CONTENTS);\n }\n addToSet(FORBID_CONTENTS, cfg.ADD_FORBID_CONTENTS, transformCaseFunc);\n }\n /* Add #text in case KEEP_CONTENT is set to true */\n if (KEEP_CONTENT) {\n ALLOWED_TAGS['#text'] = true;\n }\n /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */\n if (WHOLE_DOCUMENT) {\n addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);\n }\n /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */\n if (ALLOWED_TAGS.table) {\n addToSet(ALLOWED_TAGS, ['tbody']);\n delete FORBID_TAGS.tbody;\n }\n if (cfg.TRUSTED_TYPES_POLICY) {\n if (typeof cfg.TRUSTED_TYPES_POLICY.createHTML !== 'function') {\n throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a \"createHTML\" hook.');\n }\n if (typeof cfg.TRUSTED_TYPES_POLICY.createScriptURL !== 'function') {\n throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a \"createScriptURL\" hook.');\n }\n // Overwrite existing TrustedTypes policy.\n const previousTrustedTypesPolicy = trustedTypesPolicy;\n trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY;\n // Sign local variables required by `sanitize`. If the supplied policy's\n // `createHTML` is circular (i.e. it calls `DOMPurify.sanitize`), this\n // throws via the re-entrancy guard. Restore the previous policy first so\n // the instance is not left in a poisoned state. See #1422.\n try {\n emptyHTML = _createTrustedHTML('');\n } catch (error) {\n trustedTypesPolicy = previousTrustedTypesPolicy;\n throw error;\n }\n } else {\n // Uninitialized policy, attempt to initialize the internal dompurify policy.\n if (trustedTypesPolicy === undefined && cfg.TRUSTED_TYPES_POLICY !== null) {\n trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);\n }\n // If creating the internal policy succeeded sign internal variables.\n // Note: a falsy `trustedTypesPolicy` (null when policy creation failed or\n // was skipped via `TRUSTED_TYPES_POLICY: null`, or undefined when no\n // policy has been initialized yet) must be excluded here, otherwise we\n // would call `.createHTML` on a non-policy and throw. See #1422.\n if (trustedTypesPolicy && typeof emptyHTML === 'string') {\n emptyHTML = _createTrustedHTML('');\n }\n }\n /*\n * Mirror the clone-before-mutate pattern already applied above for\n * cfg.ADD_TAGS / cfg.ADD_ATTR: if any uponSanitize* hook is\n * registered AND the set still points at the default constant,\n * clone it. The hook then mutates the clone (in-call widening\n * still works exactly as documented) and the next default-cfg\n * call rebinds to the untouched original via the reassignment at\n * the top of this function.\n */\n if ((hooks.uponSanitizeElement.length > 0 || hooks.uponSanitizeAttribute.length > 0) && ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {\n ALLOWED_TAGS = clone(ALLOWED_TAGS);\n }\n if (hooks.uponSanitizeAttribute.length > 0 && ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {\n ALLOWED_ATTR = clone(ALLOWED_ATTR);\n }\n // Prevent further manipulation of configuration.\n // Not available in IE8, Safari 5, etc.\n if (freeze) {\n freeze(cfg);\n }\n CONFIG = cfg;\n };\n /* Keep track of all possible SVG and MathML tags\n * so that we can perform the namespace checks\n * correctly. */\n const ALL_SVG_TAGS = addToSet({}, [...svg$1, ...svgFilters, ...svgDisallowed]);\n const ALL_MATHML_TAGS = addToSet({}, [...mathMl$1, ...mathMlDisallowed]);\n /**\n * @param element a DOM element whose namespace is being checked\n * @returns Return false if the element has a\n * namespace that a spec-compliant parser would never\n * return. Return true otherwise.\n */\n const _checkValidNamespace = function _checkValidNamespace(element) {\n let parent = getParentNode(element);\n // In JSDOM, if we're inside shadow DOM, then parentNode\n // can be null. We just simulate parent in this case.\n if (!parent || !parent.tagName) {\n parent = {\n namespaceURI: NAMESPACE,\n tagName: 'template'\n };\n }\n const tagName = stringToLowerCase(element.tagName);\n const parentTagName = stringToLowerCase(parent.tagName);\n if (!ALLOWED_NAMESPACES[element.namespaceURI]) {\n return false;\n }\n if (element.namespaceURI === SVG_NAMESPACE) {\n // The only way to switch from HTML namespace to SVG\n // is via