diff --git a/js/etherpad_nextcloud-embed-main.mjs b/js/etherpad_nextcloud-embed-main.mjs index 13702f3..39de9e2 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 ce}from"./oc-compat-hVqZy-MX.chunk.mjs";import{c as de,s as le}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(),B=String(r.getAttribute("data-request-token")||"").trim(),z=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]"),G=String(r.getAttribute("data-l10n-external-title")||"Pad from another server").trim(),Y=String(r.getAttribute("data-l10n-external-message")||"Read-only snapshot from the .pad file.").trim(),J=String(r.getAttribute("data-l10n-external-empty")||"No synced snapshot is stored in this .pad file yet.").trim(),K=String(r.getAttribute("data-l10n-external-link")||"Open original pad").trim(),Q=String(r.getAttribute("data-l10n-recovery-checking")||"Checking for the original pad...").trim(),V=String(r.getAttribute("data-l10n-recovery-copy-body")||"").trim(),W=String(r.getAttribute("data-l10n-recovery-orphan-body")||"").trim(),X=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(),Z=String(r.getAttribute("data-l10n-recovery-creating")||"Creating new pad...").trim();let C=null;const y=()=>ce(B),f=de({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)},$=(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=G;const c=document.createElement("p");c.className="epnc-embed__snapshot-message",c.textContent=Y;const b=document.createElement("a");b.className="epnc-embed__snapshot-link",b.href=e,b.target="_blank",b.rel="noopener noreferrer",b.textContent=K;const H=document.createElement("div");H.className="epnc-embed__snapshot-actions",H.appendChild(b);const D=le(n),x=D.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=D:L.textContent=String(t||"").trim()!==""?String(t):J;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)},ee=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)},te=e=>!e||e==="null"?!1:e===window.location.origin?!0:z.includes(e),ne=()=>{C||(C=e=>{const t=String(e.origin||"");if(!te(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))},re=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},ae=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"))},ie=()=>{l instanceof HTMLElement&&(E(),l.hidden=!1,m instanceof HTMLElement&&(m.textContent=Q),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},oe=(e,t)=>{if(l instanceof HTMLElement&&(E(),l.hidden=!1,m instanceof HTMLElement&&(m.textContent=t),h instanceof HTMLElement&&(h.textContent=V),p instanceof HTMLElement)){const n=document.createElement("a");n.className="epnc-embed__recovery-button epnc-embed__recovery-button--primary",n.href=e,n.textContent=X,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=W),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=Z):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.")}},se=async e=>{const t=e instanceof Error&&e.message?e.message:"Pad open failed.";if(ie(),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!==""){oe(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(!re(s))throw s;await ae(),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(),ne(),f.start(),e.is_external===!0){$(e.url,typeof e.snapshot_text=="string"?e.snapshot_text:"",typeof e.snapshot_html=="string"?e.snapshot_html:"");return}ee(e.url)}catch(e){if(e&&e.code==="missing_binding"){se(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..0afc2f0 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":"+JAQC,UAAY,CAGZ,MAAMA,EAAO,SAAS,eAAe,0BAA0B,EAC/D,GAAI,EAAEA,aAAgB,aACrB,OAGD,MAAMC,EAAS,OAAOD,EAAK,aAAa,cAAc,GAAK,EAAE,EACvDE,EAAc,OAAOF,EAAK,aAAa,qBAAqB,GAAK,EAAE,EAAE,KAAI,EACzEG,EAA4B,OAAOH,EAAK,aAAa,oCAAoC,GAAK,EAAE,EAAE,KAAI,EACtGI,EAAqB,OAAOJ,EAAK,aAAa,2BAA2B,GAAK,EAAE,EAAE,KAAI,EACtFK,EAA0B,OAAOL,EAAK,aAAa,iCAAiC,GAAK,EAAE,EAAE,KAAI,EACjGM,EAAuB,OAAON,EAAK,aAAa,oBAAoB,GAAK,EAAE,EAAE,KAAI,EACjFO,EAAiB,OAAOP,EAAK,aAAa,sBAAsB,GAAK,EAAE,EAC3E,MAAM,KAAK,EACX,IAAKQ,GAAUA,EAAM,KAAI,CAAE,EAC3B,OAAO,OAAO,EACVC,EAAcT,EAAK,cAAc,2BAA2B,EAC5DU,EAAYV,EAAK,cAAc,yBAAyB,EACxDW,EAAmBX,EAAK,cAAc,iCAAiC,EACvEY,EAAeZ,EAAK,cAAc,4BAA4B,EAC9Da,EAAsBb,EAAK,cAAc,oCAAoC,EAC7Ec,EAAmBd,EAAK,cAAc,iCAAiC,EACvEe,EAAsBf,EAAK,cAAc,oCAAoC,EAC7EgB,EAAShB,EAAK,cAAc,0BAA0B,EACtDiB,EAAoB,OAAOjB,EAAK,aAAa,0BAA0B,GAAK,yBAAyB,EAAE,KAAI,EAC3GkB,EAAsB,OAAOlB,EAAK,aAAa,4BAA4B,GAAK,wCAAwC,EAAE,KAAI,EAC9HmB,EAAoB,OAAOnB,EAAK,aAAa,0BAA0B,GAAK,qDAAqD,EAAE,KAAI,EACvIoB,EAAmB,OAAOpB,EAAK,aAAa,yBAAyB,GAAK,mBAAmB,EAAE,KAAI,EACnGqB,EAAuB,OAAOrB,EAAK,aAAa,6BAA6B,GAAK,kCAAkC,EAAE,KAAI,EAC1HsB,EAAuB,OAAOtB,EAAK,aAAa,8BAA8B,GAAK,EAAE,EAAE,KAAI,EAC3FuB,EAAyB,OAAOvB,EAAK,aAAa,gCAAgC,GAAK,EAAE,EAAE,KAAI,EAC/FwB,EAA2B,OAAOxB,EAAK,aAAa,kCAAkC,GAAK,6BAA6B,EAAE,KAAI,EAC9HyB,EAAwB,OAAOzB,EAAK,aAAa,+BAA+B,GAAK,+BAA+B,EAAE,KAAI,EAC1H0B,EAAuB,OAAO1B,EAAK,aAAa,6BAA6B,GAAK,qBAAqB,EAAE,KAAI,EACnH,IAAI2B,EAAiB,KAErB,MAAMC,EAAe,IAAMC,GAAevB,CAAoB,EACxDwB,EAAUC,GAAc,CAAE,aAAAH,CAAY,CAAE,EAExCI,EAAaC,GAAY,CAC1BxB,aAAuB,cAC1BA,EAAY,OAAS,GACrBA,EAAY,UAAU,OAAO,+BAA+B,GAEzDO,aAAkB,oBACrBA,EAAO,OAAS,GAChBA,EAAO,gBAAgB,KAAK,GAEzBL,aAA4B,cAC/BA,EAAiB,YAAc,OAAOsB,GAAW,gBAAgB,GAE9DvB,aAAqB,cACxBA,EAAU,OAAS,GAErB,EAEMwB,EAAyB,CAACC,EAAKC,EAAcC,IAAiB,CAQnE,GAPI3B,aAAqB,cACxBA,EAAU,OAAS,IAEhBM,aAAkB,oBACrBA,EAAO,OAAS,GAChBA,EAAO,gBAAgB,KAAK,GAEzB,EAAEP,aAAuB,aAC5B,OAEDA,EAAY,OAAS,GACrBA,EAAY,UAAU,IAAI,+BAA+B,EACzDA,EAAY,YAAc,GAE1B,MAAM6B,EAAW,SAAS,cAAc,KAAK,EAC7CA,EAAS,UAAY,uBAErB,MAAMC,EAAQ,SAAS,cAAc,KAAK,EAC1CA,EAAM,UAAY,6BAElB,MAAMC,EAAQ,SAAS,cAAc,IAAI,EACzCA,EAAM,UAAY,6BAClBA,EAAM,YAAcvB,EAEpB,MAAMgB,EAAU,SAAS,cAAc,GAAG,EAC1CA,EAAQ,UAAY,+BACpBA,EAAQ,YAAcf,EAEtB,MAAMuB,EAAO,SAAS,cAAc,GAAG,EACvCA,EAAK,UAAY,4BACjBA,EAAK,KAAON,EACZM,EAAK,OAAS,SACdA,EAAK,IAAM,sBACXA,EAAK,YAAcrB,EAEnB,MAAMsB,EAAU,SAAS,cAAc,KAAK,EAC5CA,EAAQ,UAAY,+BACpBA,EAAQ,YAAYD,CAAI,EAExB,MAAME,EAAkB,OAAON,GAAgB,EAAE,EAAE,KAAI,IAAO,GACxDO,EAAU,SAAS,cAAcD,EAAkB,MAAQ,KAAK,EACtEC,EAAQ,UAAYD,EACjB,4DACA,4BACCA,EACHC,EAAQ,UAAY,OAAOP,CAAY,EAEvCO,EAAQ,YAAc,OAAOR,GAAgB,EAAE,EAAE,KAAI,IAAO,GAAK,OAAOA,CAAY,EAAIjB,EAGzF,MAAM0B,EAAU,SAAS,cAAc,KAAK,EAC5CA,EAAQ,UAAY,+BACpBA,EAAQ,YAAYL,CAAK,EACzBK,EAAQ,YAAYZ,CAAO,EAE3B,MAAMa,EAAS,SAAS,cAAc,KAAK,EAC3CA,EAAO,UAAY,8BACnBA,EAAO,YAAYD,CAAO,EAC1BC,EAAO,YAAYJ,CAAO,EAE1BH,EAAM,YAAYO,CAAM,EACxBP,EAAM,YAAYK,CAAO,EACzBN,EAAS,YAAYC,CAAK,EAC1B9B,EAAY,YAAY6B,CAAQ,CACjC,EAEMS,EAAcZ,GAAQ,CAC3B,GAAI,EAAEnB,aAAkB,mBAAoB,CAC3CgB,EAAU,gCAAgC,EAC1C,MACD,CACItB,aAAqB,cACxBA,EAAU,OAAS,IAEhBD,aAAuB,aAC1BA,EAAY,UAAU,OAAO,+BAA+B,EAE7DO,EAAO,OAAS,GAChB,MAAMgC,EAAe,IAAM,CAC1BhC,EAAO,oBAAoB,OAAQgC,CAAY,EAC/C,OAAO,WAAW,IAAM,CACnBvC,aAAuB,cAC1BA,EAAY,OAAS,IAEtBO,EAAO,OAAS,EACjB,EAAG,GAAsB,CAC1B,EACAA,EAAO,iBAAiB,OAAQgC,EAAc,CAAE,KAAM,EAAI,CAAE,EAC5DhC,EAAO,IAAMmB,CACd,EAEMc,EAAkB,CAACC,EAAQC,EAAQC,EAAMC,EAAU,KAAO,CAE3D,CAACH,GAAU,OAAOA,EAAO,aAAgB,YAG7CA,EAAO,YAAY,OAAO,OAAO,CAChC,KAAAE,EACA,OAAAnD,CACH,EAAKoD,CAAO,EAAGF,CAAM,CACpB,EAEMG,GAA0BH,GAC3B,CAACA,GAAUA,IAAW,OAClB,GAEJA,IAAW,OAAO,SAAS,OACvB,GAED5C,EAAe,SAAS4C,CAAM,EAGhCI,GAA4B,IAAM,CACnC5B,IAGJA,EAAkB6B,GAAU,CAC3B,MAAML,EAAS,OAAOK,EAAM,QAAU,EAAE,EACxC,GAAI,CAACF,GAAuBH,CAAM,EACjC,OAED,MAAME,EAAUG,EAAM,KAChBJ,EAAO,OAAOC,GAAY,SAC7BA,EACCA,GAAW,OAAOA,GAAY,UAAY,OAAOA,EAAQ,MAAS,SAAWA,EAAQ,KAAO,GAChG,GAAKD,EAGL,CAAA,GAAIA,IAAS,oBAAqB,CACjCtB,EAAQ,MAAK,EACb,MACD,CACA,GAAIsB,IAAS,mBAAoB,CAChCtB,EAAQ,cAAc,GAAM,EAAI,EAChCA,EAAQ,KAAI,EACZ,MACD,CACA,GAAIsB,IAAS,0BAA4BA,IAAS,qBAAsB,CACvE,MAAMK,EAAYL,IAAS,qBACrBM,EAASN,IAAS,yBAA2B,eAAiB,WACpEH,EAAgBO,EAAM,OAAQL,EAAQ,0BAA2B,CAChE,OAAAO,CACL,CAAK,EACI5B,EAAQ,KAAK,GAAM2B,CAAS,EAC/B,KAAME,GAAW,CACjBV,EAAgBO,EAAM,OAAQL,EAAQ,2BAA4B,CACjE,OAAAO,EACA,OAAQC,GAAU,OAAOA,GAAW,SAAWA,EAAS,CAAA,CAC/D,CAAO,CACF,CAAC,EACA,MAAOC,GAAU,CACjBX,EAAgBO,EAAM,OAAQL,EAAQ,yBAA0B,CAC/D,OAAAO,EACA,QAASE,aAAiB,MAAQA,EAAM,QAAU,cACzD,CAAO,CACF,CAAC,EACEH,GACH3B,EAAQ,KAAI,CAEd,CAAA,CACD,EACA,OAAO,iBAAiB,UAAWH,CAAc,EAClD,EAEMkC,GAA6BD,GAC5BA,aAAiB,MAGhB,OAAOA,EAAM,SAAW,EAAE,EAAE,SAAS,0BAA0B,EAF9D,GAKHE,EAAU,SAAY,CAC3B,MAAMC,EAAO,IAAI,gBACjBA,EAAK,IAAI,SAAU,OAAO9D,CAAM,CAAC,EACjC,MAAM+D,EAAO,MAAMC,EAAU/D,EAAa,CACzC,OAAQ,OACR,QAAS,CACR,eAAgB,kDAChB,aAAc0B,EAAY,CAC9B,EACG,KAAMmC,EAAK,SAAQ,CACtB,CAAG,EACD,GAAI,CAACC,GAAQ,OAAOA,EAAK,KAAQ,UAAYA,EAAK,IAAI,KAAI,IAAO,GAChE,MAAM,IAAI,MAAM,0CAA0C,EAE3D,OAAOA,CACR,EAEME,GAAgB,SAAY,CACjC,MAAM/B,EAAMhC,EAA0B,QAAQ,cAAe,mBAAmB,OAAOF,CAAM,CAAC,CAAC,EACzF+D,EAAO,MAAMC,EAAU9B,EAAK,CACjC,OAAQ,OACR,QAAS,CACR,aAAcP,EAAY,CAC9B,CACA,CAAG,EACGoC,GAAQA,EAAK,SAAW,wBAG3B,QAAQ,KAAK,8DAA8D,CAE7E,EAEMG,EAAgB,IAAM,CACvB1D,aAAuB,cAAaA,EAAY,OAAS,IACzDC,aAAqB,cAAaA,EAAU,OAAS,IACrDE,aAAwB,cAAaA,EAAa,OAAS,IAC3DI,aAAkB,oBACrBA,EAAO,OAAS,GAChBA,EAAO,gBAAgB,KAAK,EAE9B,EAEMoD,GAAuB,IAAM,CAC5BxD,aAAwB,cAC9BuD,EAAa,EACbvD,EAAa,OAAS,GAClBC,aAA+B,cAAaA,EAAoB,YAAcQ,GAC9EP,aAA4B,cAAaA,EAAiB,YAAc,IACxEC,aAA+B,aAAaA,EAAoB,gBAAe,EACpF,EAEMsD,EAAsB,CAACC,EAAOC,IAAY,CAC/C,MAAMC,EAAS,SAAS,cAAc,QAAQ,EAC9C,OAAAA,EAAO,KAAO,SACdA,EAAO,UAAY,8BACnBA,EAAO,YAAcF,EACrBE,EAAO,iBAAiB,QAASD,CAAO,EACjCC,CACR,EAEMC,GAA2B,CAACC,EAAkBC,IAAiB,CACpE,GAAM/D,aAAwB,cAC9BuD,EAAa,EACbvD,EAAa,OAAS,GAClBC,aAA+B,cAAaA,EAAoB,YAAc8D,GAC9E7D,aAA4B,cAAaA,EAAiB,YAAcQ,GACxEP,aAA+B,aAAa,CAC/C,MAAM6D,EAAW,SAAS,cAAc,GAAG,EAC3CA,EAAS,UAAY,mEAGrBA,EAAS,KAAOF,EAChBE,EAAS,YAAcpD,EACvBT,EAAoB,gBACnB6D,EACAP,EAAoB5C,EAAuB,IAAM,CAAOoD,EAAe,CAAG,CAAC,CAC/E,CACE,CACD,EAEMC,EAA+BH,GAAiB,CACrD,GAAM/D,aAAwB,cAC9BuD,EAAa,EACbvD,EAAa,OAAS,GAClBC,aAA+B,cAAaA,EAAoB,YAAc8D,GAC9E7D,aAA4B,cAAaA,EAAiB,YAAcS,GACxER,aAA+B,aAAa,CAC/C,MAAMyD,EAASH,EAAoB5C,EAAuB,IAAM,CAAOoD,EAAe,CAAG,CAAC,EAC1FL,EAAO,UAAU,IAAI,sCAAsC,EAC3DzD,EAAoB,gBAAgByD,CAAM,CAC3C,CACD,EAEMO,EAA0BC,GAAS,CAClCjE,aAA+B,aACrBA,EAAoB,iBAAiB,QAAQ,EACrD,QAASkE,GAAS,CACzBA,EAAK,SAAWD,EACZA,GACHC,EAAK,QAAQ,cAAgBA,EAAK,QAAQ,eAAiBA,EAAK,aAAe,GAC/EA,EAAK,YAAcvD,GACTuD,EAAK,QAAQ,gBACvBA,EAAK,YAAcA,EAAK,QAAQ,cAChC,OAAOA,EAAK,QAAQ,cAEtB,CAAC,CACF,EAEMJ,EAAkB,SAAY,CACnC,GAAIzE,IAAuB,GAAI,CAC9B4B,EAAU,0CAA0C,EACpD,MACD,CACA+C,EAAuB,EAAI,EAC3B,MAAM5C,EAAM/B,EAAmB,QAAQ,cAAe,mBAAmB,OAAOH,CAAM,CAAC,CAAC,EACxF,GAAI,CACH,MAAMgE,EAAU9B,EAAK,CACpB,OAAQ,OACR,QAAS,CAAE,aAAcP,GAAc,CAC3C,CAAI,EAEDuC,EAAa,EACT1D,aAAuB,cAAaA,EAAY,OAAS,IACxDyE,EAAG,CACT,OAAStB,EAAO,CACfmB,EAAuB,EAAK,EACxBlE,aAA+B,cAClCA,EAAoB,YAAc+C,aAAiB,OAASA,EAAM,QAC/DA,EAAM,QACN,mBAEL,CACD,EAEMuB,GAAoB,MAAOC,GAAiB,CACjD,MAAMT,EAAeS,aAAwB,OAASA,EAAa,QAChEA,EAAa,QACb,mBAEH,GADAhB,GAAoB,EAChB/D,IAA4B,GAAI,CACnCyE,EAA4BH,CAAY,EACxC,MACD,CACA,MAAMU,EAAYhF,EAAwB,QAAQ,cAAe,mBAAmB,OAAOJ,CAAM,CAAC,CAAC,EACnG,GAAI,CACH,MAAMqF,EAAO,MAAMrB,EAAUoB,EAAW,CAAE,OAAQ,KAAK,CAAE,EACzD,GAAIC,GAAQA,EAAK,QAAU,IAAQ,OAAOA,EAAK,WAAc,UAAYA,EAAK,YAAc,GAAI,CAC/Fb,GAAyBa,EAAK,UAAWX,CAAY,EACrD,MACD,CACD,MAAQ,CAER,CACAG,EAA4BH,CAAY,CACzC,EAEMO,EAAM,SAAY,CACvB,GAAI,CAAC,OAAO,SAASjF,CAAM,GAAKA,GAAU,GAAKC,IAAgB,IAAMC,IAA8B,GAAI,CACtG6B,EAAU,oCAAoC,EAC9C,MACD,CACA,GAAIJ,EAAY,IAAO,GAAI,CAC1BI,EAAU,gCAAgC,EAC1C,MACD,CACA,GAAI,CACH,IAAIgC,EACJ,GAAI,CACHA,EAAO,MAAMF,EAAO,CACrB,OAASF,EAAO,CACf,GAAI,CAACC,GAA0BD,CAAK,EACnC,MAAMA,EAEP,MAAMM,GAAa,EACnBF,EAAO,MAAMF,EAAO,CACrB,CACA,MAAMyB,EAAU,OAAOvB,EAAK,UAAa,SAAWA,EAAK,SAAS,OAAS,GACrEwB,EAAkB,OAAOxB,EAAK,uBAAyB,CAAC,EACxDyB,EAAa,OAAO,SAASD,CAAe,GAAKA,EAAkB,EAAIA,EAAkB,IAAO,KAKtG,GAJA1D,EAAQ,UAAU,CAAE,QAAAyD,EAAS,WAAAE,CAAU,CAAE,EACzC3D,EAAQ,yBAAwB,EAChCyB,GAAyB,EACzBzB,EAAQ,MAAK,EACTkC,EAAK,cAAgB,GAAM,CAC9B9B,EACC8B,EAAK,IACL,OAAOA,EAAK,eAAkB,SAAWA,EAAK,cAAgB,GAC9D,OAAOA,EAAK,eAAkB,SAAWA,EAAK,cAAgB,EACnE,EACI,MACD,CACAjB,EAAWiB,EAAK,GAAG,CACpB,OAASJ,EAAO,CACf,GAAIA,GAASA,EAAM,OAAS,kBAAmB,CACzCuB,GAAkBvB,CAAK,EAC5B,MACD,CACA5B,EAAU4B,aAAiB,MAAQA,EAAM,QAAU,kBAAkB,CACtE,CACD,EAEKsB,EAAG,CACT,GAAC"} \ 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\t// Sanitize first, then decide on the HTML path from the *sanitized*\n\t\t// result: if DOMPurify empties it (e.g. all-dangerous markup) we fall\n\t\t// back to the text / empty-message path instead of rendering a blank\n\t\t// HTML block (mirrors viewer-main's renderSnapshotView).\n\t\tconst sanitizedSnapshotHtml = sanitizeSnapshotHtml(snapshotHtml)\n\t\tconst hasSnapshotHtml = sanitizedSnapshotHtml.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 = sanitizedSnapshotHtml\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","sanitizedSnapshotHtml","sanitizeSnapshotHtml","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 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":"AAqBO,SAASA,EAAc,CAAE,aAAAC,EAAc,UAAAC,CAAS,EAAK,CAAA,EAAI,CAC/D,MAAMC,EAAU,OAAOD,GAAc,WAAaA,EAAY,IAAIE,IAAS,OAAO,MAAM,GAAGA,CAAI,EACzFC,EAAW,OAAOJ,GAAiB,WAAaA,EAAe,IAAM,GAE3E,IAAIK,EAAU,GACVC,EAAa,KACbC,EAAc,KACdC,EAAkB,GAClBC,EAAoB,GACpBC,EAAyB,GACzBC,EAAU,KACVC,EAAoB,KACpBC,EAAkB,KAEtB,MAAMC,EAAY,CAAC,CAAE,QAASC,EAAK,WAAYC,CAAE,EAAK,KAAO,CACxD,OAAOD,GAAQ,WAClBV,EAAUU,GAEP,OAAO,SAASC,CAAE,GAAKA,EAAK,IAC/BV,EAAaU,EAEf,EAEMC,EAAO,IAAM,CACdN,IAAY,OACf,OAAO,cAAcA,CAAO,EAC5BA,EAAU,KAEZ,EAEMO,EAAQ,IAAM,CACf,CAACb,GAAWM,IAAY,OAG5BA,EAAU,OAAO,YAAY,IAAM,CAC9B,SAAS,kBAAoB,WAChCQ,EAAc,GAAO,EAAK,CAE5B,EAAGb,CAAU,EACd,EAEMa,EAAgB,CAACC,EAAOC,IAAc,CACtCC,EAAKF,EAAOC,CAAS,EAAE,MAAM,IAAM,CAAC,CAAC,CAC3C,EAEMC,EAAO,MAAOF,EAAOC,IAAc,CACxC,GAAI,CAAChB,EACJ,MAAO,CAAE,OAAQ,UAAU,EAE5B,GAAIE,EAGH,OAAIa,GAAS,CAACZ,GACbC,EAAoB,GACpBC,EAAyBA,GAA0B,CAAA,CAAQW,EACpDd,EAAY,MAAM,MAAe,EAAE,KAAK,IAAMe,EAAK,GAAMZ,CAAsB,CAAC,GAEjFH,EAERC,EAAkB,EAAQY,EAC1B,MAAMG,GAAkB,SAAY,CACnC,MAAMR,EAAMK,EAASf,GAAWA,EAAQ,SAAS,GAAG,EAAI,IAAM,KAAO,UAAaA,EAC5EmB,EAAW,MAAMtB,EAAQa,EAAK,CACnC,OAAQ,OACR,YAAa,cACb,QAAS,CACR,OAAQ,mBACR,aAAcX,EAAQ,CAC3B,EACI,UAAW,CAAA,CAAQiB,CACvB,CAAI,EACKI,EAAO,MAAMD,EAAS,KAAI,EAAG,MAAM,KAAO,GAAG,EACnD,GAAI,CAACA,EAAS,GACb,MAAM,IAAI,MAAOC,GAAQA,EAAK,SAAY,sBAAsB,EAEjE,OAAOA,CACR,GAAC,EACDlB,EAAcgB,EACd,IAAIG,EACAC,EAAY,KAChB,GAAI,CACHD,EAAS,MAAMH,CAChB,OAASK,EAAO,CACfD,EAAYC,CACb,QAAA,CACKrB,IAAgBgB,IACnBhB,EAAc,MAEfC,EAAkB,EACnB,CACA,MAAMqB,EAAkBpB,EAClBqB,EAAiBpB,EAGvB,GAFAD,EAAoB,GACpBC,EAAyB,GACrBmB,EACH,OAAOP,EAAK,GAAMQ,CAAc,EAEjC,GAAIH,aAAqB,MACxB,MAAMA,EAEP,OAAOD,CACR,EAiCA,MAAO,CACN,UAAAZ,EACA,MAAAI,EACA,KAAAD,EACA,KAAAK,EACA,cAAAH,EACA,yBArCgC,IAAM,CAClCP,GAAqBC,IAGzBD,EAAoB,IAAM,CACzB,GAAI,SAAS,kBAAoB,SAAU,CAC1CO,EAAc,GAAM,EAAI,EACxBF,EAAI,EACJ,MACD,CACAC,EAAK,CACN,EACAL,EAAkB,IAAM,CACvBM,EAAc,GAAM,EAAI,EACxBF,EAAI,CACL,EACA,SAAS,iBAAiB,mBAAoBL,CAAiB,EAC/D,OAAO,iBAAiB,WAAYC,CAAe,EACpD,EAoBC,wBAlB+B,IAAM,CACjCD,IACH,SAAS,oBAAoB,mBAAoBA,CAAiB,EAClEA,EAAoB,MAEjBC,IACH,OAAO,oBAAoB,WAAYA,CAAe,EACtDA,EAAkB,KAEpB,CAUD,CACA"} \ 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);i