diff --git a/docs/architecture.md b/docs/architecture.md index 2604b84..4044398 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -156,7 +156,11 @@ Primary flow (minimal blank create launcher page): 4. `src/embed-create-main.js` reads `name` and `accessMode` from the launcher URL, validates them client-side, and calls `POST /api/v1/pads/create-by-parent` same-origin with CSRF token from the template. - the token is injected manually for the same reason as embed-open: blank layout has no automatic `OC.requestToken` bootstrap 5. `PadCreateController::createByParent` performs server-side validation of `name`, `accessMode`, and the writable target folder before creating the `.pad` file and binding. -6. On success the launcher redirects itself to the returned `embed_url`, after which the normal embed-open flow takes over. +6. Before redirecting, `src/embed-create-main.js` posts the host page one of two structured events so the surrounding UI can react without scraping the iframe DOM: + - `epnc:create-succeeded` — payload `{embed_url, file_id, pad_id, access_mode}`. Fires once on the success path, immediately before the iframe self-redirects to the embed-open URL. + - `epnc:create-failed` — payload `{reason, status, message}`. Fires on any error. `reason` is one of `'invalid'` (client-side validation), `'conflict'` (HTTP 409 — e.g. duplicate filename), `'server'` (any other 4xx/5xx), or `'network'` (fetch itself failed). + The inline error rendering inside the iframe is unchanged — `postMessage` is purely additive for hosts that want to act on the outcome. Target-origin is `*` because the page doesn't know the host's origin up-front; the `frame-ancestors` allowlist already constrains who can be the parent. +7. On success the launcher redirects itself to the returned `embed_url`, after which the normal embed-open flow takes over. ### 3) Open (public share) diff --git a/js/api-client-BXEMiUh7.chunk.mjs b/js/api-client-BXEMiUh7.chunk.mjs new file mode 100644 index 0000000..d81ec30 --- /dev/null +++ b/js/api-client-BXEMiUh7.chunk.mjs @@ -0,0 +1,2 @@ +import{f as i,A as c,o as u}from"./oc-compat-hVqZy-MX.chunk.mjs";const w=(t,e)=>{const n=!t||t==="/"?"":String(t),r=String(e||"").replace(/^\/+/,"").replace(/\s+\.pad$/i,".pad");return n===""?"/"+r:n+"/"+r},g=t=>typeof t=="string"&&t.toLowerCase().endsWith(".pad"),y=t=>{if(!t||typeof t!="string")return"/";const e=t.lastIndexOf("/");return e<=0?"/":t.slice(0,e)||"/"},U=()=>{const t=(new URLSearchParams(window.location.search||"").get("dir")||"/").trim();return t===""?"/":t},R=t=>{const e=y(t);if(e!=="/")return e;const n=U();return n===""?"/":n},I=t=>i("/apps/"+c+"/?file="+encodeURIComponent(t)),C=(t,e)=>{const n=i("/apps/files/files/"+encodeURIComponent(String(t))),r=new URLSearchParams;return r.set("dir",R(e)),r.set("editing","false"),r.set("openfile","true"),n+"?"+r.toString()},P=(t,e)=>{const n=i("/apps/"+c+"/public/"+encodeURIComponent(t));return e?n+"?file="+encodeURIComponent(e):n},L=()=>{const t=(window.location.pathname||"").match(/(?:\/index\.php)?\/s\/([^/]+)(?:\/.*)?$/);return t&&t[1]||null},x=t=>{if(!t||typeof t!="string")return null;let e;try{e=new URL(t,window.location.origin)}catch{return null}const n=e.pathname||"",r=n.match(/\/apps\/files\/files\/(\d+)\/?$/);if(!r){const s=n.match(/\/f\/(\d+)\/?$/);if(!s){const d=Number(e.searchParams.get("fileid")||"");return Number.isFinite(d)&&d>0?d:null}const l=parseInt(s[1],10);return Number.isFinite(l)&&l>0?l:null}const a=parseInt(r[1],10);return Number.isFinite(a)&&a>0?a:null},A=()=>{const t=(window.location.pathname||"").match(/\/apps\/files\/files\/(\d+)\/?$/);if(!t)return null;const e=parseInt(t[1],10);return Number.isFinite(e)&&e>0?e:null},T=t=>{if(!t||typeof t!="string")return null;let e;try{e=new URL(t,window.location.origin)}catch{return null}const n=(e.pathname||"").match(/(?:\/index\.php)?\/s\/([^/]+)\/download\/?$/);if(!n)return null;const r=(e.searchParams.get("files")||"").trim();if(!g(r))return null;const a=(e.searchParams.get("path")||"/").trim()||"/";return{token:n[1],path:w(a,r)}},j=t=>{if(!t||typeof t!="string")return null;let e;try{e=new URL(t,window.location.origin)}catch{return null}let n;try{n=decodeURIComponent(e.pathname||"")}catch{return null}if(!n.endsWith(".pad"))return null;const r=["/remote.php/dav/files/","/public.php/dav/files/"].find(d=>n.includes(d));if(!r)return null;const a=n.indexOf(r),s=n.substring(a+r.length),l=s.indexOf("/");return l===-1?null:"/"+s.substring(l+1)},o=new Map,S=50,v=300*1e3,k=async t=>{const e=String(t),n=f(e);if(n!==null)return n;const r=i("/apps/"+c+"/api/v1/pads/resolve")+"?fileId="+encodeURIComponent(e),a=p(r,{method:"GET",headers:{Accept:"application/json"}},"Pad resolve failed.").catch(s=>{throw o.delete(e),s});return m(e,a),a},O=async t=>{const e="path:"+String(t),n=f(e);if(n!==null)return n;const r=i("/apps/"+c+"/api/v1/pads/resolve")+"?file="+encodeURIComponent(t),a=p(r,{method:"GET",headers:{Accept:"application/json"}},"Pad resolve by path failed.").catch(s=>{throw o.delete(e),s});return m(e,a),a},F=async(t,e)=>{const n=i("/apps/"+c+"/api/v1/pads/from-url"),r=new URLSearchParams;return r.set("file",t),r.set("padUrl",e),p(n,{method:"POST",headers:h(),body:r.toString()},"Could not import public pad URL.")},N=async t=>{const e=i("/apps/"+c+"/api/v1/pads/find-original/"+encodeURIComponent(String(t)));return p(e,{method:"GET",headers:{Accept:"application/json"}},"Lookup failed.")},$=async t=>{const e=i("/apps/"+c+"/api/v1/pads/recover-from-snapshot/"+encodeURIComponent(String(t))),n=await p(e,{method:"POST",headers:{Accept:"application/json",requesttoken:u()}},"Recovery failed.");return o.delete(String(t)),n},E=async t=>{const e=i("/apps/"+c+"/api/v1/pads"),n=new URLSearchParams;return n.set("file",t),n.set("accessMode","public"),p(e,{method:"POST",headers:h(),body:n.toString()},"Could not create public pad.")},h=()=>({Accept:"application/json","Content-Type":"application/x-www-form-urlencoded;charset=UTF-8",requesttoken:u()}),f=t=>{const e=o.get(t);return e?Date.now()-e.createdAt>v?(o.delete(t),null):e.request:null},m=(t,e)=>{if(!o.has(t)&&o.size>=S){const n=o.keys().next().value;n!==void 0&&o.delete(n)}o.set(t,{createdAt:Date.now(),request:e})},p=async(t,e,n)=>{const r=await fetch(t,{...e,credentials:"same-origin"}),a=await r.json().catch(()=>({}));if(!r.ok){const s=new Error(a&&a.message||n);throw a&&typeof a.code=="string"&&(s.code=a.code),s.status=r.status,s}return a};export{L as a,k as b,O as c,A as d,I as e,C as f,U as g,E as h,F as i,g as j,T as k,j as l,$ as m,w as n,N as o,x as p,R as r,P as v}; +//# sourceMappingURL=api-client-BXEMiUh7.chunk.mjs.map diff --git a/js/api-client-CT0tqdIR.chunk.mjs.license b/js/api-client-BXEMiUh7.chunk.mjs.license similarity index 100% rename from js/api-client-CT0tqdIR.chunk.mjs.license rename to js/api-client-BXEMiUh7.chunk.mjs.license diff --git a/js/api-client-CT0tqdIR.chunk.mjs.map b/js/api-client-BXEMiUh7.chunk.mjs.map similarity index 99% rename from js/api-client-CT0tqdIR.chunk.mjs.map rename to js/api-client-BXEMiUh7.chunk.mjs.map index 1ef66b8..403151d 100644 --- a/js/api-client-CT0tqdIR.chunk.mjs.map +++ b/js/api-client-BXEMiUh7.chunk.mjs.map @@ -1 +1 @@ -{"version":3,"file":"api-client-CT0tqdIR.chunk.mjs","sources":["../src/lib/urls.js","../src/lib/api-client.js"],"sourcesContent":["/**\n * SPDX-License-Identifier: AGPL-3.0-or-later\n * Copyright (c) 2026 Jacob Bühler\n */\n\nimport { APP_ID } from './constants.js'\nimport { ocGenerateUrl } from './oc-compat.js'\n\nexport const normalizeFilePath = (dir, filename) => {\n\tconst cleanDir = !dir || dir === '/' ? '' : String(dir)\n\tconst cleanName = String(filename || '').replace(/^\\/+/, '').replace(/\\s+\\.pad$/i, '.pad')\n\tif (cleanDir === '') {\n\t\treturn '/' + cleanName\n\t}\n\treturn cleanDir + '/' + cleanName\n}\n\nexport const isPadName = (name) => typeof name === 'string' && name.toLowerCase().endsWith('.pad')\n\nexport const getDirFromPath = (path) => {\n\tif (!path || typeof path !== 'string') {\n\t\treturn '/'\n\t}\n\tconst idx = path.lastIndexOf('/')\n\tif (idx <= 0) {\n\t\treturn '/'\n\t}\n\treturn path.slice(0, idx) || '/'\n}\n\nexport const getCurrentDir = () => {\n\tconst params = new URLSearchParams(window.location.search || '')\n\tconst dir = (params.get('dir') || '/').trim()\n\treturn dir === '' ? '/' : dir\n}\n\nexport const resolveOpenDir = (path) => {\n\tconst dirFromPath = getDirFromPath(path)\n\tif (dirFromPath !== '/') {\n\t\treturn dirFromPath\n\t}\n\t// When a file path has no directory context (or is already root), keep the\n\t// currently active Files dir in URL so close/back navigation returns to the\n\t// same folder instead of jumping to an unrelated default view.\n\tconst currentDir = getCurrentDir()\n\treturn currentDir === '' ? '/' : currentDir\n}\n\nexport const viewerUrlForPath = (path) => ocGenerateUrl('/apps/' + APP_ID + '/?file=' + encodeURIComponent(path))\n\nexport const filesUrlForFileId = (fileId, path) => {\n\tconst base = ocGenerateUrl('/apps/files/files/' + encodeURIComponent(String(fileId)))\n\tconst params = new URLSearchParams()\n\tparams.set('dir', resolveOpenDir(path))\n\tparams.set('editing', 'false')\n\tparams.set('openfile', 'true')\n\treturn base + '?' + params.toString()\n}\n\nexport const viewerUrlForPublicShare = (token, path) => {\n\tconst base = ocGenerateUrl('/apps/' + APP_ID + '/public/' + encodeURIComponent(token))\n\tif (!path) {\n\t\treturn base\n\t}\n\treturn base + '?file=' + encodeURIComponent(path)\n}\n\nexport const parsePublicShareTokenFromLocation = () => {\n\tconst match = (window.location.pathname || '').match(/(?:\\/index\\.php)?\\/s\\/([^/]+)(?:\\/.*)?$/)\n\tif (!match) {\n\t\treturn null\n\t}\n\treturn match[1] || null\n}\n\nexport const parseFileIdFromFilesHref = (href) => {\n\tif (!href || typeof href !== 'string') {\n\t\treturn null\n\t}\n\tlet url\n\ttry {\n\t\turl = new URL(href, window.location.origin)\n\t} catch (error) {\n\t\treturn null\n\t}\n\tconst pathValue = url.pathname || ''\n\tconst match = pathValue.match(/\\/apps\\/files\\/files\\/(\\d+)\\/?$/)\n\tif (!match) {\n\t\tconst shareMatch = pathValue.match(/\\/f\\/(\\d+)\\/?$/)\n\t\tif (!shareMatch) {\n\t\t\tconst byQuery = Number(url.searchParams.get('fileid') || '')\n\t\t\treturn Number.isFinite(byQuery) && byQuery > 0 ? byQuery : null\n\t\t}\n\t\tconst shareId = parseInt(shareMatch[1], 10)\n\t\treturn Number.isFinite(shareId) && shareId > 0 ? shareId : null\n\t}\n\tconst id = parseInt(match[1], 10)\n\treturn Number.isFinite(id) && id > 0 ? id : null\n}\n\nexport const parseFileIdFromCurrentLocation = () => {\n\tconst match = (window.location.pathname || '').match(/\\/apps\\/files\\/files\\/(\\d+)\\/?$/)\n\tif (!match) {\n\t\treturn null\n\t}\n\tconst id = parseInt(match[1], 10)\n\treturn Number.isFinite(id) && id > 0 ? id : null\n}\n\nexport const parsePublicSharePadFromHref = (href) => {\n\tif (!href || typeof href !== 'string') {\n\t\treturn null\n\t}\n\tlet url\n\ttry {\n\t\turl = new URL(href, window.location.origin)\n\t} catch (error) {\n\t\treturn null\n\t}\n\tconst pathMatch = (url.pathname || '').match(/(?:\\/index\\.php)?\\/s\\/([^/]+)\\/download\\/?$/)\n\tif (!pathMatch) {\n\t\treturn null\n\t}\n\tconst files = (url.searchParams.get('files') || '').trim()\n\tif (!isPadName(files)) {\n\t\treturn null\n\t}\n\tconst dir = (url.searchParams.get('path') || '/').trim() || '/'\n\treturn {\n\t\ttoken: pathMatch[1],\n\t\tpath: normalizeFilePath(dir, files),\n\t}\n}\n\nexport const parsePadPathFromDavHref = (href) => {\n\tif (!href || typeof href !== 'string') {\n\t\treturn null\n\t}\n\tlet url\n\ttry {\n\t\turl = new URL(href, window.location.origin)\n\t} catch (error) {\n\t\treturn null\n\t}\n\tlet pathname\n\ttry {\n\t\tpathname = decodeURIComponent(url.pathname || '')\n\t} catch (error) {\n\t\treturn null\n\t}\n\tif (!pathname.endsWith('.pad')) {\n\t\treturn null\n\t}\n\tconst markers = ['/remote.php/dav/files/', '/public.php/dav/files/']\n\tconst marker = markers.find((candidate) => pathname.includes(candidate))\n\tif (!marker) return null\n\tconst markerIndex = pathname.indexOf(marker)\n\tconst rest = pathname.substring(markerIndex + marker.length)\n\tconst firstSlash = rest.indexOf('/')\n\tif (firstSlash === -1) {\n\t\treturn null\n\t}\n\treturn '/' + rest.substring(firstSlash + 1)\n}\n","/**\n * SPDX-License-Identifier: AGPL-3.0-or-later\n * Copyright (c) 2026 Jacob Bühler\n */\n\nimport { APP_ID } from './constants.js'\nimport { ocGenerateUrl, ocRequestToken } from './oc-compat.js'\n\nconst RESOLVE_CACHE = new Map()\nconst RESOLVE_CACHE_MAX_ENTRIES = 50\nconst RESOLVE_CACHE_TTL_MS = 5 * 60 * 1000\n\nexport const apiResolvePadByFileId = async (fileId) => {\n\tconst cacheKey = String(fileId)\n\tconst cached = getResolveCache(cacheKey)\n\tif (cached !== null) {\n\t\treturn cached\n\t}\n\tconst url = ocGenerateUrl('/apps/' + APP_ID + '/api/v1/pads/resolve') + '?fileId=' + encodeURIComponent(cacheKey)\n\tconst request = fetchJson(url, {\n\t\tmethod: 'GET',\n\t\theaders: { Accept: 'application/json' },\n\t}, 'Pad resolve failed.')\n\t\t.catch((error) => {\n\t\t\tRESOLVE_CACHE.delete(cacheKey)\n\t\t\tthrow error\n\t\t})\n\tsetResolveCache(cacheKey, request)\n\treturn request\n}\n\nexport const apiResolvePadByPath = async (path) => {\n\tconst cacheKey = 'path:' + String(path)\n\tconst cached = getResolveCache(cacheKey)\n\tif (cached !== null) {\n\t\treturn cached\n\t}\n\tconst url = ocGenerateUrl('/apps/' + APP_ID + '/api/v1/pads/resolve') + '?file=' + encodeURIComponent(path)\n\tconst request = fetchJson(url, {\n\t\tmethod: 'GET',\n\t\theaders: { Accept: 'application/json' },\n\t}, 'Pad resolve by path failed.')\n\t\t.catch((error) => {\n\t\t\tRESOLVE_CACHE.delete(cacheKey)\n\t\t\tthrow error\n\t\t})\n\tsetResolveCache(cacheKey, request)\n\treturn request\n}\n\nexport const apiCreatePadFromUrl = async (filePath, padUrl) => {\n\tconst endpoint = ocGenerateUrl('/apps/' + APP_ID + '/api/v1/pads/from-url')\n\tconst body = new URLSearchParams()\n\tbody.set('file', filePath)\n\tbody.set('padUrl', padUrl)\n\n\treturn fetchJson(endpoint, {\n\t\tmethod: 'POST',\n\t\theaders: formHeaders(),\n\t\tbody: body.toString(),\n\t}, 'Could not import public pad URL.')\n}\n\nexport const apiFindOriginalPad = async (fileId) => {\n\tconst endpoint = ocGenerateUrl('/apps/' + APP_ID + '/api/v1/pads/find-original/' + encodeURIComponent(String(fileId)))\n\treturn fetchJson(endpoint, {\n\t\tmethod: 'GET',\n\t\theaders: { Accept: 'application/json' },\n\t}, 'Lookup failed.')\n}\n\nexport const apiRecoverFromSnapshot = async (fileId) => {\n\tconst endpoint = ocGenerateUrl('/apps/' + APP_ID + '/api/v1/pads/recover-from-snapshot/' + encodeURIComponent(String(fileId)))\n\tconst result = await fetchJson(endpoint, {\n\t\tmethod: 'POST',\n\t\theaders: {\n\t\t\tAccept: 'application/json',\n\t\t\trequesttoken: ocRequestToken(),\n\t\t},\n\t}, 'Recovery failed.')\n\t// A freshly recovered pad invalidates any cached resolve response: the\n\t// old one carried a missing-binding marker that no longer applies.\n\tRESOLVE_CACHE.delete(String(fileId))\n\treturn result\n}\n\nexport const apiCreatePublicPad = async (filePath) => {\n\tconst endpoint = ocGenerateUrl('/apps/' + APP_ID + '/api/v1/pads')\n\tconst body = new URLSearchParams()\n\tbody.set('file', filePath)\n\tbody.set('accessMode', 'public')\n\n\treturn fetchJson(endpoint, {\n\t\tmethod: 'POST',\n\t\theaders: formHeaders(),\n\t\tbody: body.toString(),\n\t}, 'Could not create public pad.')\n}\n\nconst formHeaders = () => ({\n\tAccept: 'application/json',\n\t'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',\n\trequesttoken: ocRequestToken(),\n})\n\nconst getResolveCache = (cacheKey) => {\n\tconst cached = RESOLVE_CACHE.get(cacheKey)\n\tif (!cached) {\n\t\treturn null\n\t}\n\tif ((Date.now() - cached.createdAt) > RESOLVE_CACHE_TTL_MS) {\n\t\tRESOLVE_CACHE.delete(cacheKey)\n\t\treturn null\n\t}\n\treturn cached.request\n}\n\nconst setResolveCache = (cacheKey, request) => {\n\tif (!RESOLVE_CACHE.has(cacheKey) && RESOLVE_CACHE.size >= RESOLVE_CACHE_MAX_ENTRIES) {\n\t\tconst oldestKey = RESOLVE_CACHE.keys().next().value\n\t\tif (oldestKey !== undefined) {\n\t\t\tRESOLVE_CACHE.delete(oldestKey)\n\t\t}\n\t}\n\tRESOLVE_CACHE.set(cacheKey, {\n\t\tcreatedAt: Date.now(),\n\t\trequest,\n\t})\n}\n\nconst fetchJson = async (url, options, fallbackMessage) => {\n\tconst response = await fetch(url, {\n\t\t...options,\n\t\tcredentials: 'same-origin',\n\t})\n\tconst data = await response.json().catch(() => ({}))\n\tif (!response.ok) {\n\t\tconst error = new Error((data && data.message) || fallbackMessage)\n\t\tif (data && typeof data.code === 'string') {\n\t\t\terror.code = data.code\n\t\t}\n\t\terror.status = response.status\n\t\tthrow error\n\t}\n\treturn data\n}\n"],"names":["normalizeFilePath","dir","filename","cleanDir","cleanName","isPadName","name","getDirFromPath","path","idx","getCurrentDir","resolveOpenDir","dirFromPath","currentDir","viewerUrlForPath","ocGenerateUrl","APP_ID","filesUrlForFileId","fileId","base","params","viewerUrlForPublicShare","token","parsePublicShareTokenFromLocation","match","parseFileIdFromFilesHref","href","url","pathValue","shareMatch","byQuery","shareId","id","parseFileIdFromCurrentLocation","parsePublicSharePadFromHref","pathMatch","files","parsePadPathFromDavHref","pathname","marker","candidate","markerIndex","rest","firstSlash","RESOLVE_CACHE","RESOLVE_CACHE_MAX_ENTRIES","RESOLVE_CACHE_TTL_MS","apiResolvePadByFileId","cacheKey","cached","getResolveCache","request","fetchJson","error","setResolveCache","apiResolvePadByPath","apiCreatePadFromUrl","filePath","padUrl","endpoint","body","formHeaders","apiFindOriginalPad","apiRecoverFromSnapshot","result","ocRequestToken","apiCreatePublicPad","oldestKey","options","fallbackMessage","response","data"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"api-client-BXEMiUh7.chunk.mjs","sources":["../src/lib/urls.js","../src/lib/api-client.js"],"sourcesContent":["/**\n * SPDX-License-Identifier: AGPL-3.0-or-later\n * Copyright (c) 2026 Jacob Bühler\n */\n\nimport { APP_ID } from './constants.js'\nimport { ocGenerateUrl } from './oc-compat.js'\n\nexport const normalizeFilePath = (dir, filename) => {\n\tconst cleanDir = !dir || dir === '/' ? '' : String(dir)\n\tconst cleanName = String(filename || '').replace(/^\\/+/, '').replace(/\\s+\\.pad$/i, '.pad')\n\tif (cleanDir === '') {\n\t\treturn '/' + cleanName\n\t}\n\treturn cleanDir + '/' + cleanName\n}\n\nexport const isPadName = (name) => typeof name === 'string' && name.toLowerCase().endsWith('.pad')\n\nexport const getDirFromPath = (path) => {\n\tif (!path || typeof path !== 'string') {\n\t\treturn '/'\n\t}\n\tconst idx = path.lastIndexOf('/')\n\tif (idx <= 0) {\n\t\treturn '/'\n\t}\n\treturn path.slice(0, idx) || '/'\n}\n\nexport const getCurrentDir = () => {\n\tconst params = new URLSearchParams(window.location.search || '')\n\tconst dir = (params.get('dir') || '/').trim()\n\treturn dir === '' ? '/' : dir\n}\n\nexport const resolveOpenDir = (path) => {\n\tconst dirFromPath = getDirFromPath(path)\n\tif (dirFromPath !== '/') {\n\t\treturn dirFromPath\n\t}\n\t// When a file path has no directory context (or is already root), keep the\n\t// currently active Files dir in URL so close/back navigation returns to the\n\t// same folder instead of jumping to an unrelated default view.\n\tconst currentDir = getCurrentDir()\n\treturn currentDir === '' ? '/' : currentDir\n}\n\nexport const viewerUrlForPath = (path) => ocGenerateUrl('/apps/' + APP_ID + '/?file=' + encodeURIComponent(path))\n\nexport const filesUrlForFileId = (fileId, path) => {\n\tconst base = ocGenerateUrl('/apps/files/files/' + encodeURIComponent(String(fileId)))\n\tconst params = new URLSearchParams()\n\tparams.set('dir', resolveOpenDir(path))\n\tparams.set('editing', 'false')\n\tparams.set('openfile', 'true')\n\treturn base + '?' + params.toString()\n}\n\nexport const viewerUrlForPublicShare = (token, path) => {\n\tconst base = ocGenerateUrl('/apps/' + APP_ID + '/public/' + encodeURIComponent(token))\n\tif (!path) {\n\t\treturn base\n\t}\n\treturn base + '?file=' + encodeURIComponent(path)\n}\n\nexport const parsePublicShareTokenFromLocation = () => {\n\tconst match = (window.location.pathname || '').match(/(?:\\/index\\.php)?\\/s\\/([^/]+)(?:\\/.*)?$/)\n\tif (!match) {\n\t\treturn null\n\t}\n\treturn match[1] || null\n}\n\nexport const parseFileIdFromFilesHref = (href) => {\n\tif (!href || typeof href !== 'string') {\n\t\treturn null\n\t}\n\tlet url\n\ttry {\n\t\turl = new URL(href, window.location.origin)\n\t} catch (error) {\n\t\treturn null\n\t}\n\tconst pathValue = url.pathname || ''\n\tconst match = pathValue.match(/\\/apps\\/files\\/files\\/(\\d+)\\/?$/)\n\tif (!match) {\n\t\tconst shareMatch = pathValue.match(/\\/f\\/(\\d+)\\/?$/)\n\t\tif (!shareMatch) {\n\t\t\tconst byQuery = Number(url.searchParams.get('fileid') || '')\n\t\t\treturn Number.isFinite(byQuery) && byQuery > 0 ? byQuery : null\n\t\t}\n\t\tconst shareId = parseInt(shareMatch[1], 10)\n\t\treturn Number.isFinite(shareId) && shareId > 0 ? shareId : null\n\t}\n\tconst id = parseInt(match[1], 10)\n\treturn Number.isFinite(id) && id > 0 ? id : null\n}\n\nexport const parseFileIdFromCurrentLocation = () => {\n\tconst match = (window.location.pathname || '').match(/\\/apps\\/files\\/files\\/(\\d+)\\/?$/)\n\tif (!match) {\n\t\treturn null\n\t}\n\tconst id = parseInt(match[1], 10)\n\treturn Number.isFinite(id) && id > 0 ? id : null\n}\n\nexport const parsePublicSharePadFromHref = (href) => {\n\tif (!href || typeof href !== 'string') {\n\t\treturn null\n\t}\n\tlet url\n\ttry {\n\t\turl = new URL(href, window.location.origin)\n\t} catch (error) {\n\t\treturn null\n\t}\n\tconst pathMatch = (url.pathname || '').match(/(?:\\/index\\.php)?\\/s\\/([^/]+)\\/download\\/?$/)\n\tif (!pathMatch) {\n\t\treturn null\n\t}\n\tconst files = (url.searchParams.get('files') || '').trim()\n\tif (!isPadName(files)) {\n\t\treturn null\n\t}\n\tconst dir = (url.searchParams.get('path') || '/').trim() || '/'\n\treturn {\n\t\ttoken: pathMatch[1],\n\t\tpath: normalizeFilePath(dir, files),\n\t}\n}\n\nexport const parsePadPathFromDavHref = (href) => {\n\tif (!href || typeof href !== 'string') {\n\t\treturn null\n\t}\n\tlet url\n\ttry {\n\t\turl = new URL(href, window.location.origin)\n\t} catch (error) {\n\t\treturn null\n\t}\n\tlet pathname\n\ttry {\n\t\tpathname = decodeURIComponent(url.pathname || '')\n\t} catch (error) {\n\t\treturn null\n\t}\n\tif (!pathname.endsWith('.pad')) {\n\t\treturn null\n\t}\n\tconst markers = ['/remote.php/dav/files/', '/public.php/dav/files/']\n\tconst marker = markers.find((candidate) => pathname.includes(candidate))\n\tif (!marker) return null\n\tconst markerIndex = pathname.indexOf(marker)\n\tconst rest = pathname.substring(markerIndex + marker.length)\n\tconst firstSlash = rest.indexOf('/')\n\tif (firstSlash === -1) {\n\t\treturn null\n\t}\n\treturn '/' + rest.substring(firstSlash + 1)\n}\n","/**\n * SPDX-License-Identifier: AGPL-3.0-or-later\n * Copyright (c) 2026 Jacob Bühler\n */\n\nimport { APP_ID } from './constants.js'\nimport { ocGenerateUrl, ocRequestToken } from './oc-compat.js'\n\nconst RESOLVE_CACHE = new Map()\nconst RESOLVE_CACHE_MAX_ENTRIES = 50\nconst RESOLVE_CACHE_TTL_MS = 5 * 60 * 1000\n\nexport const apiResolvePadByFileId = async (fileId) => {\n\tconst cacheKey = String(fileId)\n\tconst cached = getResolveCache(cacheKey)\n\tif (cached !== null) {\n\t\treturn cached\n\t}\n\tconst url = ocGenerateUrl('/apps/' + APP_ID + '/api/v1/pads/resolve') + '?fileId=' + encodeURIComponent(cacheKey)\n\tconst request = fetchJson(url, {\n\t\tmethod: 'GET',\n\t\theaders: { Accept: 'application/json' },\n\t}, 'Pad resolve failed.')\n\t\t.catch((error) => {\n\t\t\tRESOLVE_CACHE.delete(cacheKey)\n\t\t\tthrow error\n\t\t})\n\tsetResolveCache(cacheKey, request)\n\treturn request\n}\n\nexport const apiResolvePadByPath = async (path) => {\n\tconst cacheKey = 'path:' + String(path)\n\tconst cached = getResolveCache(cacheKey)\n\tif (cached !== null) {\n\t\treturn cached\n\t}\n\tconst url = ocGenerateUrl('/apps/' + APP_ID + '/api/v1/pads/resolve') + '?file=' + encodeURIComponent(path)\n\tconst request = fetchJson(url, {\n\t\tmethod: 'GET',\n\t\theaders: { Accept: 'application/json' },\n\t}, 'Pad resolve by path failed.')\n\t\t.catch((error) => {\n\t\t\tRESOLVE_CACHE.delete(cacheKey)\n\t\t\tthrow error\n\t\t})\n\tsetResolveCache(cacheKey, request)\n\treturn request\n}\n\nexport const apiCreatePadFromUrl = async (filePath, padUrl) => {\n\tconst endpoint = ocGenerateUrl('/apps/' + APP_ID + '/api/v1/pads/from-url')\n\tconst body = new URLSearchParams()\n\tbody.set('file', filePath)\n\tbody.set('padUrl', padUrl)\n\n\treturn fetchJson(endpoint, {\n\t\tmethod: 'POST',\n\t\theaders: formHeaders(),\n\t\tbody: body.toString(),\n\t}, 'Could not import public pad URL.')\n}\n\nexport const apiFindOriginalPad = async (fileId) => {\n\tconst endpoint = ocGenerateUrl('/apps/' + APP_ID + '/api/v1/pads/find-original/' + encodeURIComponent(String(fileId)))\n\treturn fetchJson(endpoint, {\n\t\tmethod: 'GET',\n\t\theaders: { Accept: 'application/json' },\n\t}, 'Lookup failed.')\n}\n\nexport const apiRecoverFromSnapshot = async (fileId) => {\n\tconst endpoint = ocGenerateUrl('/apps/' + APP_ID + '/api/v1/pads/recover-from-snapshot/' + encodeURIComponent(String(fileId)))\n\tconst result = await fetchJson(endpoint, {\n\t\tmethod: 'POST',\n\t\theaders: {\n\t\t\tAccept: 'application/json',\n\t\t\trequesttoken: ocRequestToken(),\n\t\t},\n\t}, 'Recovery failed.')\n\t// A freshly recovered pad invalidates any cached resolve response: the\n\t// old one carried a missing-binding marker that no longer applies.\n\tRESOLVE_CACHE.delete(String(fileId))\n\treturn result\n}\n\nexport const apiCreatePublicPad = async (filePath) => {\n\tconst endpoint = ocGenerateUrl('/apps/' + APP_ID + '/api/v1/pads')\n\tconst body = new URLSearchParams()\n\tbody.set('file', filePath)\n\tbody.set('accessMode', 'public')\n\n\treturn fetchJson(endpoint, {\n\t\tmethod: 'POST',\n\t\theaders: formHeaders(),\n\t\tbody: body.toString(),\n\t}, 'Could not create public pad.')\n}\n\nconst formHeaders = () => ({\n\tAccept: 'application/json',\n\t'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',\n\trequesttoken: ocRequestToken(),\n})\n\nconst getResolveCache = (cacheKey) => {\n\tconst cached = RESOLVE_CACHE.get(cacheKey)\n\tif (!cached) {\n\t\treturn null\n\t}\n\tif ((Date.now() - cached.createdAt) > RESOLVE_CACHE_TTL_MS) {\n\t\tRESOLVE_CACHE.delete(cacheKey)\n\t\treturn null\n\t}\n\treturn cached.request\n}\n\nconst setResolveCache = (cacheKey, request) => {\n\tif (!RESOLVE_CACHE.has(cacheKey) && RESOLVE_CACHE.size >= RESOLVE_CACHE_MAX_ENTRIES) {\n\t\tconst oldestKey = RESOLVE_CACHE.keys().next().value\n\t\tif (oldestKey !== undefined) {\n\t\t\tRESOLVE_CACHE.delete(oldestKey)\n\t\t}\n\t}\n\tRESOLVE_CACHE.set(cacheKey, {\n\t\tcreatedAt: Date.now(),\n\t\trequest,\n\t})\n}\n\nconst fetchJson = async (url, options, fallbackMessage) => {\n\tconst response = await fetch(url, {\n\t\t...options,\n\t\tcredentials: 'same-origin',\n\t})\n\tconst data = await response.json().catch(() => ({}))\n\tif (!response.ok) {\n\t\tconst error = new Error((data && data.message) || fallbackMessage)\n\t\tif (data && typeof data.code === 'string') {\n\t\t\terror.code = data.code\n\t\t}\n\t\terror.status = response.status\n\t\tthrow error\n\t}\n\treturn data\n}\n"],"names":["normalizeFilePath","dir","filename","cleanDir","cleanName","isPadName","name","getDirFromPath","path","idx","getCurrentDir","resolveOpenDir","dirFromPath","currentDir","viewerUrlForPath","ocGenerateUrl","APP_ID","filesUrlForFileId","fileId","base","params","viewerUrlForPublicShare","token","parsePublicShareTokenFromLocation","match","parseFileIdFromFilesHref","href","url","pathValue","shareMatch","byQuery","shareId","id","parseFileIdFromCurrentLocation","parsePublicSharePadFromHref","pathMatch","files","parsePadPathFromDavHref","pathname","marker","candidate","markerIndex","rest","firstSlash","RESOLVE_CACHE","RESOLVE_CACHE_MAX_ENTRIES","RESOLVE_CACHE_TTL_MS","apiResolvePadByFileId","cacheKey","cached","getResolveCache","request","fetchJson","error","setResolveCache","apiResolvePadByPath","apiCreatePadFromUrl","filePath","padUrl","endpoint","body","formHeaders","apiFindOriginalPad","apiRecoverFromSnapshot","result","ocRequestToken","apiCreatePublicPad","oldestKey","options","fallbackMessage","response","data"],"mappings":"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"} \ No newline at end of file diff --git a/js/api-client-CT0tqdIR.chunk.mjs b/js/api-client-CT0tqdIR.chunk.mjs deleted file mode 100644 index 87f94a0..0000000 --- a/js/api-client-CT0tqdIR.chunk.mjs +++ /dev/null @@ -1,2 +0,0 @@ -import{f as i,A as c,o as u}from"./oc-compat-DlZkDG_8.chunk.mjs";const w=(e,t)=>{const n=!e||e==="/"?"":String(e),r=String(t||"").replace(/^\/+/,"").replace(/\s+\.pad$/i,".pad");return n===""?"/"+r:n+"/"+r},g=e=>typeof e=="string"&&e.toLowerCase().endsWith(".pad"),y=e=>{if(!e||typeof e!="string")return"/";const t=e.lastIndexOf("/");return t<=0?"/":e.slice(0,t)||"/"},U=()=>{const e=(new URLSearchParams(window.location.search||"").get("dir")||"/").trim();return e===""?"/":e},v=e=>{const t=y(e);if(t!=="/")return t;const n=U();return n===""?"/":n},I=e=>i("/apps/"+c+"/?file="+encodeURIComponent(e)),C=(e,t)=>{const n=i("/apps/files/files/"+encodeURIComponent(String(e))),r=new URLSearchParams;return r.set("dir",v(t)),r.set("editing","false"),r.set("openfile","true"),n+"?"+r.toString()},P=(e,t)=>{const n=i("/apps/"+c+"/public/"+encodeURIComponent(e));return t?n+"?file="+encodeURIComponent(t):n},L=()=>{const e=(window.location.pathname||"").match(/(?:\/index\.php)?\/s\/([^/]+)(?:\/.*)?$/);return e&&e[1]||null},x=e=>{if(!e||typeof e!="string")return null;let t;try{t=new URL(e,window.location.origin)}catch{return null}const n=t.pathname||"",r=n.match(/\/apps\/files\/files\/(\d+)\/?$/);if(!r){const a=n.match(/\/f\/(\d+)\/?$/);if(!a){const d=Number(t.searchParams.get("fileid")||"");return Number.isFinite(d)&&d>0?d:null}const l=parseInt(a[1],10);return Number.isFinite(l)&&l>0?l:null}const s=parseInt(r[1],10);return Number.isFinite(s)&&s>0?s:null},A=()=>{const e=(window.location.pathname||"").match(/\/apps\/files\/files\/(\d+)\/?$/);if(!e)return null;const t=parseInt(e[1],10);return Number.isFinite(t)&&t>0?t:null},T=e=>{if(!e||typeof e!="string")return null;let t;try{t=new URL(e,window.location.origin)}catch{return null}const n=(t.pathname||"").match(/(?:\/index\.php)?\/s\/([^/]+)\/download\/?$/);if(!n)return null;const r=(t.searchParams.get("files")||"").trim();if(!g(r))return null;const s=(t.searchParams.get("path")||"/").trim()||"/";return{token:n[1],path:w(s,r)}},j=e=>{if(!e||typeof e!="string")return null;let t;try{t=new URL(e,window.location.origin)}catch{return null}let n;try{n=decodeURIComponent(t.pathname||"")}catch{return null}if(!n.endsWith(".pad"))return null;const r=["/remote.php/dav/files/","/public.php/dav/files/"].find(d=>n.includes(d));if(!r)return null;const s=n.indexOf(r),a=n.substring(s+r.length),l=a.indexOf("/");return l===-1?null:"/"+a.substring(l+1)},o=new Map,R=50,S=300*1e3,k=async e=>{const t=String(e),n=h(t);if(n!==null)return n;const r=i("/apps/"+c+"/api/v1/pads/resolve")+"?fileId="+encodeURIComponent(t),s=p(r,{method:"GET",headers:{Accept:"application/json"}},"Pad resolve failed.").catch(a=>{throw o.delete(t),a});return m(t,s),s},O=async e=>{const t="path:"+String(e),n=h(t);if(n!==null)return n;const r=i("/apps/"+c+"/api/v1/pads/resolve")+"?file="+encodeURIComponent(e),s=p(r,{method:"GET",headers:{Accept:"application/json"}},"Pad resolve by path failed.").catch(a=>{throw o.delete(t),a});return m(t,s),s},F=async(e,t)=>{const n=i("/apps/"+c+"/api/v1/pads/from-url"),r=new URLSearchParams;return r.set("file",e),r.set("padUrl",t),p(n,{method:"POST",headers:f(),body:r.toString()},"Could not import public pad URL.")},N=async e=>{const t=i("/apps/"+c+"/api/v1/pads/find-original/"+encodeURIComponent(String(e)));return p(t,{method:"GET",headers:{Accept:"application/json"}},"Lookup failed.")},$=async e=>{const t=i("/apps/"+c+"/api/v1/pads/recover-from-snapshot/"+encodeURIComponent(String(e))),n=await p(t,{method:"POST",headers:{Accept:"application/json",requesttoken:u()}},"Recovery failed.");return o.delete(String(e)),n},E=async e=>{const t=i("/apps/"+c+"/api/v1/pads"),n=new URLSearchParams;return n.set("file",e),n.set("accessMode","public"),p(t,{method:"POST",headers:f(),body:n.toString()},"Could not create public pad.")},f=()=>({Accept:"application/json","Content-Type":"application/x-www-form-urlencoded;charset=UTF-8",requesttoken:u()}),h=e=>{const t=o.get(e);return t?Date.now()-t.createdAt>S?(o.delete(e),null):t.request:null},m=(e,t)=>{if(!o.has(e)&&o.size>=R){const n=o.keys().next().value;n!==void 0&&o.delete(n)}o.set(e,{createdAt:Date.now(),request:t})},p=async(e,t,n)=>{const r=await fetch(e,{...t,credentials:"same-origin"}),s=await r.json().catch(()=>({}));if(!r.ok){const a=new Error(s&&s.message||n);throw s&&typeof s.code=="string"&&(a.code=s.code),a.status=r.status,a}return s};export{L as a,k as b,O as c,A as d,I as e,C as f,U as g,E as h,F as i,g as j,T as k,j as l,$ as m,w as n,N as o,x as p,v as r,P as v}; -//# sourceMappingURL=api-client-CT0tqdIR.chunk.mjs.map diff --git a/js/etherpad_nextcloud-embed-create-main.mjs b/js/etherpad_nextcloud-embed-create-main.mjs index 1f72d57..b6b1818 100644 --- a/js/etherpad_nextcloud-embed-create-main.mjs +++ b/js/etherpad_nextcloud-embed-create-main.mjs @@ -1,2 +1,2 @@ -import{o as h}from"./oc-compat-DlZkDG_8.chunk.mjs";import{f as S}from"./fetch-helpers-26-A0xtP.chunk.mjs";(function(){const e=document.getElementById("etherpad-nextcloud-embed-create");if(!(e instanceof HTMLElement))return;const o=Number(e.getAttribute("data-parent-folder-id")||""),c=String(e.getAttribute("data-create-by-parent-url")||"").trim(),u=String(e.getAttribute("data-request-token")||"").trim(),g=String(e.getAttribute("data-l10n-missing-name")||"Pad name is required."),f=String(e.getAttribute("data-l10n-invalid-access-mode")||"Invalid access mode."),b=String(e.getAttribute("data-l10n-incomplete-config")||"Embed configuration is incomplete."),d=e.querySelector("[data-epnc-embed-create-loading]"),s=e.querySelector("[data-epnc-embed-create-error]"),m=e.querySelector("[data-epnc-embed-create-error-message]"),l=()=>h(u),i=t=>{d instanceof HTMLElement&&(d.hidden=!0),m instanceof HTMLElement&&(m.textContent=String(t||"Unknown error.")),s instanceof HTMLElement&&(s.hidden=!1)},p=()=>{const t=new URL(window.location.href).searchParams;return{name:String(t.get("name")||"").trim(),accessMode:String(t.get("accessMode")||"protected").trim()}},w=t=>{const r=new URL(String(t||"").trim(),window.location.origin);if(r.origin!==window.location.origin)throw new Error("Invalid embed URL origin.");return r.pathname+r.search+r.hash};(async()=>{if(!Number.isFinite(o)||o<=0||c===""){i(b);return}if(l()===""){i("CSRF request token is missing.");return}const{name:t,accessMode:r}=p();if(t===""){i(g);return}if(r!=="protected"&&r!=="public"){i(f);return}const a=new URLSearchParams;a.set("parentFolderId",String(o)),a.set("name",t),a.set("accessMode",r);try{const n=await S(c,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded;charset=UTF-8",requesttoken:l()},body:a.toString()});if(!n||typeof n.embed_url!="string"||n.embed_url.trim()==="")throw new Error("Pad creation API did not return a valid embed URL.");window.location.replace(w(n.embed_url))}catch(n){i(n instanceof Error?n.message:"Pad creation failed.")}})()})(); +import{o as U}from"./oc-compat-hVqZy-MX.chunk.mjs";import{f as R}from"./fetch-helpers-C4MxuNvt.chunk.mjs";(function(){const r=document.getElementById("etherpad-nextcloud-embed-create");if(!(r instanceof HTMLElement))return;const s=Number(r.getAttribute("data-parent-folder-id")||""),c=String(r.getAttribute("data-create-by-parent-url")||"").trim(),b=String(r.getAttribute("data-request-token")||"").trim(),w=String(r.getAttribute("data-l10n-missing-name")||"Pad name is required."),h=String(r.getAttribute("data-l10n-invalid-access-mode")||"Invalid access mode."),y=String(r.getAttribute("data-l10n-incomplete-config")||"Embed configuration is incomplete."),l=r.querySelector("[data-epnc-embed-create-loading]"),m=r.querySelector("[data-epnc-embed-create-error]"),u=r.querySelector("[data-epnc-embed-create-error-message]"),g=()=>U(b),p=(e,n)=>{if(window.parent!==window)try{window.parent.postMessage(Object.assign({type:e},n||{}),"*")}catch{}},S=e=>{l instanceof HTMLElement&&(l.hidden=!0),u instanceof HTMLElement&&(u.textContent=String(e||"Unknown error.")),m instanceof HTMLElement&&(m.hidden=!1)},a=(e,n,o)=>{const t=String(n||"Unknown error.");S(t),p("epnc:create-failed",{reason:e,status:typeof o=="number"?o:null,message:t})},_=()=>{const e=new URL(window.location.href).searchParams;return{name:String(e.get("name")||"").trim(),accessMode:String(e.get("accessMode")||"protected").trim()}},v=e=>{const n=new URL(String(e||"").trim(),window.location.origin);if(n.origin!==window.location.origin)throw new Error("Invalid embed URL origin.");return n.pathname+n.search+n.hash},L=e=>e===409?"conflict":"server";(async()=>{if(!Number.isFinite(s)||s<=0||c===""){a("invalid",y);return}if(g()===""){a("invalid","CSRF request token is missing.");return}const{name:e,accessMode:n}=_();if(e===""){a("invalid",w);return}if(n!=="protected"&&n!=="public"){a("invalid",h);return}const o=new URLSearchParams;o.set("parentFolderId",String(s)),o.set("name",e),o.set("accessMode",n);let t;try{t=await R(c,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded;charset=UTF-8",requesttoken:g()},body:o.toString()})}catch(i){const d=i&&typeof i.status=="number"?i.status:null,E=i instanceof Error?i.message:"Pad creation failed.",M=d===null?"network":L(d);a(M,E,d);return}if(!t||typeof t.embed_url!="string"||t.embed_url.trim()===""){a("server","Pad creation API did not return a valid embed URL.");return}let f;try{f=v(t.embed_url)}catch(i){const d=i instanceof Error?i.message:"Invalid embed URL.";a("server",d);return}p("epnc:create-succeeded",{embed_url:t.embed_url,file_id:typeof t.file_id=="number"?t.file_id:null,pad_id:typeof t.pad_id=="string"?t.pad_id:"",access_mode:typeof t.access_mode=="string"?t.access_mode:""}),window.location.replace(f)})()})(); //# sourceMappingURL=etherpad_nextcloud-embed-create-main.mjs.map diff --git a/js/etherpad_nextcloud-embed-create-main.mjs.map b/js/etherpad_nextcloud-embed-create-main.mjs.map index 99b40c3..4a289ef 100644 --- a/js/etherpad_nextcloud-embed-create-main.mjs.map +++ b/js/etherpad_nextcloud-embed-create-main.mjs.map @@ -1 +1 @@ -{"version":3,"file":"etherpad_nextcloud-embed-create-main.mjs","sources":["../src/embed-create-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 { fetchJsonWithTimeout as fetchJson } from './lib/fetch-helpers.js'\n\n(function () {\n\tconst root = document.getElementById('etherpad-nextcloud-embed-create')\n\tif (!(root instanceof HTMLElement)) {\n\t\treturn\n\t}\n\n\tconst parentFolderId = Number(root.getAttribute('data-parent-folder-id') || '')\n\tconst createByParentUrl = String(root.getAttribute('data-create-by-parent-url') || '').trim()\n\tconst templateRequestToken = String(root.getAttribute('data-request-token') || '').trim()\n\tconst missingNameMessage = String(root.getAttribute('data-l10n-missing-name') || 'Pad name is required.')\n\tconst invalidAccessModeMessage = String(root.getAttribute('data-l10n-invalid-access-mode') || 'Invalid access mode.')\n\tconst incompleteConfigMessage = String(root.getAttribute('data-l10n-incomplete-config') || 'Embed configuration is incomplete.')\n\tconst loadingNode = root.querySelector('[data-epnc-embed-create-loading]')\n\tconst errorNode = root.querySelector('[data-epnc-embed-create-error]')\n\tconst errorMessageNode = root.querySelector('[data-epnc-embed-create-error-message]')\n\n\tconst requestToken = () => ocRequestToken(templateRequestToken)\n\n\tconst showError = (message) => {\n\t\tif (loadingNode instanceof HTMLElement) {\n\t\t\tloadingNode.hidden = true\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 readLauncherParams = () => {\n\t\tconst params = new URL(window.location.href).searchParams\n\t\treturn {\n\t\t\tname: String(params.get('name') || '').trim(),\n\t\t\taccessMode: String(params.get('accessMode') || 'protected').trim(),\n\t\t}\n\t}\n\n\tconst normalizeEmbedRedirectUrl = (value) => {\n\t\tconst url = new URL(String(value || '').trim(), window.location.origin)\n\t\tif (url.origin !== window.location.origin) {\n\t\t\tthrow new Error('Invalid embed URL origin.')\n\t\t}\n\t\treturn url.pathname + url.search + url.hash\n\t}\n\n\tconst run = async () => {\n\t\tif (!Number.isFinite(parentFolderId) || parentFolderId <= 0 || createByParentUrl === '') {\n\t\t\tshowError(incompleteConfigMessage)\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\n\t\tconst { name, accessMode } = readLauncherParams()\n\t\tif (name === '') {\n\t\t\tshowError(missingNameMessage)\n\t\t\treturn\n\t\t}\n\t\tif (accessMode !== 'protected' && accessMode !== 'public') {\n\t\t\tshowError(invalidAccessModeMessage)\n\t\t\treturn\n\t\t}\n\n\t\tconst body = new URLSearchParams()\n\t\tbody.set('parentFolderId', String(parentFolderId))\n\t\tbody.set('name', name)\n\t\tbody.set('accessMode', accessMode)\n\n\t\ttry {\n\t\t\tconst data = await fetchJson(createByParentUrl, {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: {\n\t\t\t\t\t'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',\n\t\t\t\t\trequesttoken: requestToken(),\n\t\t\t\t},\n\t\t\t\tbody: body.toString(),\n\t\t\t})\n\t\t\tif (!data || typeof data.embed_url !== 'string' || data.embed_url.trim() === '') {\n\t\t\t\tthrow new Error('Pad creation API did not return a valid embed URL.')\n\t\t\t}\n\t\t\twindow.location.replace(normalizeEmbedRedirectUrl(data.embed_url))\n\t\t} catch (error) {\n\t\t\tshowError(error instanceof Error ? error.message : 'Pad creation failed.')\n\t\t}\n\t}\n\n\tvoid run()\n})()\n"],"names":["root","parentFolderId","createByParentUrl","templateRequestToken","missingNameMessage","invalidAccessModeMessage","incompleteConfigMessage","loadingNode","errorNode","errorMessageNode","requestToken","ocRequestToken","showError","message","readLauncherParams","params","normalizeEmbedRedirectUrl","value","url","name","accessMode","body","data","fetchJson","error"],"mappings":"2GAOC,UAAY,CACZ,MAAMA,EAAO,SAAS,eAAe,iCAAiC,EACtE,GAAI,EAAEA,aAAgB,aACrB,OAGD,MAAMC,EAAiB,OAAOD,EAAK,aAAa,uBAAuB,GAAK,EAAE,EACxEE,EAAoB,OAAOF,EAAK,aAAa,2BAA2B,GAAK,EAAE,EAAE,KAAI,EACrFG,EAAuB,OAAOH,EAAK,aAAa,oBAAoB,GAAK,EAAE,EAAE,KAAI,EACjFI,EAAqB,OAAOJ,EAAK,aAAa,wBAAwB,GAAK,uBAAuB,EAClGK,EAA2B,OAAOL,EAAK,aAAa,+BAA+B,GAAK,sBAAsB,EAC9GM,EAA0B,OAAON,EAAK,aAAa,6BAA6B,GAAK,oCAAoC,EACzHO,EAAcP,EAAK,cAAc,kCAAkC,EACnEQ,EAAYR,EAAK,cAAc,gCAAgC,EAC/DS,EAAmBT,EAAK,cAAc,wCAAwC,EAE9EU,EAAe,IAAMC,EAAeR,CAAoB,EAExDS,EAAaC,GAAY,CAC1BN,aAAuB,cAC1BA,EAAY,OAAS,IAElBE,aAA4B,cAC/BA,EAAiB,YAAc,OAAOI,GAAW,gBAAgB,GAE9DL,aAAqB,cACxBA,EAAU,OAAS,GAErB,EAEMM,EAAqB,IAAM,CAChC,MAAMC,EAAS,IAAI,IAAI,OAAO,SAAS,IAAI,EAAE,aAC7C,MAAO,CACN,KAAM,OAAOA,EAAO,IAAI,MAAM,GAAK,EAAE,EAAE,KAAI,EAC3C,WAAY,OAAOA,EAAO,IAAI,YAAY,GAAK,WAAW,EAAE,KAAI,CACnE,CACC,EAEMC,EAA6BC,GAAU,CAC5C,MAAMC,EAAM,IAAI,IAAI,OAAOD,GAAS,EAAE,EAAE,KAAI,EAAI,OAAO,SAAS,MAAM,EACtE,GAAIC,EAAI,SAAW,OAAO,SAAS,OAClC,MAAM,IAAI,MAAM,2BAA2B,EAE5C,OAAOA,EAAI,SAAWA,EAAI,OAASA,EAAI,IACxC,GAEY,SAAY,CACvB,GAAI,CAAC,OAAO,SAASjB,CAAc,GAAKA,GAAkB,GAAKC,IAAsB,GAAI,CACxFU,EAAUN,CAAuB,EACjC,MACD,CACA,GAAII,EAAY,IAAO,GAAI,CAC1BE,EAAU,gCAAgC,EAC1C,MACD,CAEA,KAAM,CAAE,KAAAO,EAAM,WAAAC,CAAU,EAAKN,EAAkB,EAC/C,GAAIK,IAAS,GAAI,CAChBP,EAAUR,CAAkB,EAC5B,MACD,CACA,GAAIgB,IAAe,aAAeA,IAAe,SAAU,CAC1DR,EAAUP,CAAwB,EAClC,MACD,CAEA,MAAMgB,EAAO,IAAI,gBACjBA,EAAK,IAAI,iBAAkB,OAAOpB,CAAc,CAAC,EACjDoB,EAAK,IAAI,OAAQF,CAAI,EACrBE,EAAK,IAAI,aAAcD,CAAU,EAEjC,GAAI,CACH,MAAME,EAAO,MAAMC,EAAUrB,EAAmB,CAC/C,OAAQ,OACR,QAAS,CACR,eAAgB,kDAChB,aAAcQ,EAAY,CAC/B,EACI,KAAMW,EAAK,SAAQ,CACvB,CAAI,EACD,GAAI,CAACC,GAAQ,OAAOA,EAAK,WAAc,UAAYA,EAAK,UAAU,KAAI,IAAO,GAC5E,MAAM,IAAI,MAAM,oDAAoD,EAErE,OAAO,SAAS,QAAQN,EAA0BM,EAAK,SAAS,CAAC,CAClE,OAASE,EAAO,CACfZ,EAAUY,aAAiB,MAAQA,EAAM,QAAU,sBAAsB,CAC1E,CACD,GAEQ,CACT,GAAC"} \ No newline at end of file +{"version":3,"file":"etherpad_nextcloud-embed-create-main.mjs","sources":["../src/embed-create-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 { fetchJsonWithTimeout as fetchJson } from './lib/fetch-helpers.js'\n\n(function () {\n\tconst root = document.getElementById('etherpad-nextcloud-embed-create')\n\tif (!(root instanceof HTMLElement)) {\n\t\treturn\n\t}\n\n\tconst parentFolderId = Number(root.getAttribute('data-parent-folder-id') || '')\n\tconst createByParentUrl = String(root.getAttribute('data-create-by-parent-url') || '').trim()\n\tconst templateRequestToken = String(root.getAttribute('data-request-token') || '').trim()\n\tconst missingNameMessage = String(root.getAttribute('data-l10n-missing-name') || 'Pad name is required.')\n\tconst invalidAccessModeMessage = String(root.getAttribute('data-l10n-invalid-access-mode') || 'Invalid access mode.')\n\tconst incompleteConfigMessage = String(root.getAttribute('data-l10n-incomplete-config') || 'Embed configuration is incomplete.')\n\tconst loadingNode = root.querySelector('[data-epnc-embed-create-loading]')\n\tconst errorNode = root.querySelector('[data-epnc-embed-create-error]')\n\tconst errorMessageNode = root.querySelector('[data-epnc-embed-create-error-message]')\n\n\tconst requestToken = () => ocRequestToken(templateRequestToken)\n\n\t/**\n\t * Post an `epnc:*` event to the host page that's embedding this iframe.\n\t *\n\t * Target-origin is `*` rather than a specific origin because the create\n\t * page doesn't know the host's origin up-front (the host hasn't talked to\n\t * us yet). The actual access control happens at iframe-load time via the\n\t * route's CSP `frame-ancestors` header, which only lists the admin-\n\t * configured `trusted_embed_origins`. Anyone receiving these messages is\n\t * by construction already in that allowlist.\n\t *\n\t * No-ops if we're not actually embedded (window.parent === window).\n\t */\n\tconst postHostMessage = (type, payload) => {\n\t\tif (window.parent === window) {\n\t\t\treturn\n\t\t}\n\t\ttry {\n\t\t\twindow.parent.postMessage(Object.assign({ type }, payload || {}), '*')\n\t\t} catch (e) {\n\t\t\t// Posting can throw on certain cross-origin / cross-process boundaries;\n\t\t\t// inline error rendering is the user-visible fallback, so the message\n\t\t\t// is purely advisory for the host.\n\t\t}\n\t}\n\n\tconst showError = (message) => {\n\t\tif (loadingNode instanceof HTMLElement) {\n\t\t\tloadingNode.hidden = true\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\t/**\n\t * Emit a structured `epnc:create-failed` event AND render the inline error.\n\t * The message is normalised once so the host's payload and the user-facing\n\t * inline message never drift (an empty/undefined `message` would otherwise\n\t * land in the iframe as \"Unknown error.\" but in the postMessage payload as\n\t * the empty string).\n\t *\n\t * `reason` is a coarse bucket so hosts can branch without parsing the\n\t * HTTP status:\n\t * - 'invalid' — client-side validation failed (missing name, etc.)\n\t * - 'conflict' — backend returned 409 (e.g. duplicate filename)\n\t * - 'server' — any other 4xx / 5xx\n\t * - 'network' — fetch itself failed (offline, CORS, timeout)\n\t */\n\tconst failCreate = (reason, message, status) => {\n\t\tconst normalizedMessage = String(message || 'Unknown error.')\n\t\tshowError(normalizedMessage)\n\t\tpostHostMessage('epnc:create-failed', {\n\t\t\treason,\n\t\t\tstatus: typeof status === 'number' ? status : null,\n\t\t\tmessage: normalizedMessage,\n\t\t})\n\t}\n\n\tconst readLauncherParams = () => {\n\t\tconst params = new URL(window.location.href).searchParams\n\t\treturn {\n\t\t\tname: String(params.get('name') || '').trim(),\n\t\t\taccessMode: String(params.get('accessMode') || 'protected').trim(),\n\t\t}\n\t}\n\n\tconst normalizeEmbedRedirectUrl = (value) => {\n\t\tconst url = new URL(String(value || '').trim(), window.location.origin)\n\t\tif (url.origin !== window.location.origin) {\n\t\t\tthrow new Error('Invalid embed URL origin.')\n\t\t}\n\t\treturn url.pathname + url.search + url.hash\n\t}\n\n\tconst classifyHttpStatus = (status) => {\n\t\tif (status === 409) {\n\t\t\treturn 'conflict'\n\t\t}\n\t\treturn 'server'\n\t}\n\n\tconst run = async () => {\n\t\tif (!Number.isFinite(parentFolderId) || parentFolderId <= 0 || createByParentUrl === '') {\n\t\t\tfailCreate('invalid', incompleteConfigMessage)\n\t\t\treturn\n\t\t}\n\t\tif (requestToken() === '') {\n\t\t\tfailCreate('invalid', 'CSRF request token is missing.')\n\t\t\treturn\n\t\t}\n\n\t\tconst { name, accessMode } = readLauncherParams()\n\t\tif (name === '') {\n\t\t\tfailCreate('invalid', missingNameMessage)\n\t\t\treturn\n\t\t}\n\t\tif (accessMode !== 'protected' && accessMode !== 'public') {\n\t\t\tfailCreate('invalid', invalidAccessModeMessage)\n\t\t\treturn\n\t\t}\n\n\t\tconst body = new URLSearchParams()\n\t\tbody.set('parentFolderId', String(parentFolderId))\n\t\tbody.set('name', name)\n\t\tbody.set('accessMode', accessMode)\n\n\t\t// Step 1: server-side create. Failures here are either network\n\t\t// (fetch threw — no HTTP status reached us) or server (we got a\n\t\t// status code back, including the 409 on duplicate filename).\n\t\tlet data\n\t\ttry {\n\t\t\tdata = await fetchJson(createByParentUrl, {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: {\n\t\t\t\t\t'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',\n\t\t\t\t\trequesttoken: requestToken(),\n\t\t\t\t},\n\t\t\t\tbody: body.toString(),\n\t\t\t})\n\t\t} catch (error) {\n\t\t\tconst status = (error && typeof error.status === 'number') ? error.status : null\n\t\t\tconst message = error instanceof Error ? error.message : 'Pad creation failed.'\n\t\t\tconst reason = status === null ? 'network' : classifyHttpStatus(status)\n\t\t\tfailCreate(reason, message, status)\n\t\t\treturn\n\t\t}\n\n\t\t// Step 2: validate the server's response shape *and* the redirect\n\t\t// target before emitting success. A malformed or cross-origin\n\t\t// embed_url is a server-side bug, not a network failure — and we\n\t\t// must not announce success only to then announce failure to the\n\t\t// same host listener, which would leave them with contradictory\n\t\t// signals.\n\t\tif (!data || typeof data.embed_url !== 'string' || data.embed_url.trim() === '') {\n\t\t\tfailCreate('server', 'Pad creation API did not return a valid embed URL.')\n\t\t\treturn\n\t\t}\n\t\tlet redirectTarget\n\t\ttry {\n\t\t\tredirectTarget = normalizeEmbedRedirectUrl(data.embed_url)\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : 'Invalid embed URL.'\n\t\t\tfailCreate('server', message)\n\t\t\treturn\n\t\t}\n\n\t\t// Step 3: announce success once everything is definitively OK, then\n\t\t// navigate. Notify *before* the redirect: once we replace the iframe\n\t\t// location the host loses its handle on this script.\n\t\tpostHostMessage('epnc:create-succeeded', {\n\t\t\tembed_url: data.embed_url,\n\t\t\tfile_id: typeof data.file_id === 'number' ? data.file_id : null,\n\t\t\tpad_id: typeof data.pad_id === 'string' ? data.pad_id : '',\n\t\t\taccess_mode: typeof data.access_mode === 'string' ? data.access_mode : '',\n\t\t})\n\t\twindow.location.replace(redirectTarget)\n\t}\n\n\tvoid run()\n})()\n"],"names":["root","parentFolderId","createByParentUrl","templateRequestToken","missingNameMessage","invalidAccessModeMessage","incompleteConfigMessage","loadingNode","errorNode","errorMessageNode","requestToken","ocRequestToken","postHostMessage","type","payload","showError","message","failCreate","reason","status","normalizedMessage","readLauncherParams","params","normalizeEmbedRedirectUrl","value","url","classifyHttpStatus","name","accessMode","body","data","fetchJson","error","redirectTarget"],"mappings":"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"} \ No newline at end of file diff --git a/js/etherpad_nextcloud-embed-main.mjs b/js/etherpad_nextcloud-embed-main.mjs index b1ecbe1..3b7623f 100644 --- a/js/etherpad_nextcloud-embed-main.mjs +++ b/js/etherpad_nextcloud-embed-main.mjs @@ -1,2 +1,2 @@ -import{o as _e}from"./oc-compat-DlZkDG_8.chunk.mjs";import{f as q}from"./fetch-helpers-26-A0xtP.chunk.mjs";(function(){const r=document.getElementById("etherpad-nextcloud-embed");if(!(r instanceof HTMLElement))return;const h=Number(r.getAttribute("data-file-id")||""),j=String(r.getAttribute("data-open-by-id-url")||"").trim(),D=String(r.getAttribute("data-initialize-by-id-url-template")||"").trim(),B=String(r.getAttribute("data-recover-url-template")||"").trim(),z=String(r.getAttribute("data-find-original-url-template")||"").trim(),$=String(r.getAttribute("data-request-token")||"").trim(),ee=String(r.getAttribute("data-trusted-origins")||"").split(/\s+/).map(e=>e.trim()).filter(Boolean),i=r.querySelector("[data-epnc-embed-loading]"),m=r.querySelector("[data-epnc-embed-error]"),G=r.querySelector("[data-epnc-embed-error-message]"),p=r.querySelector("[data-epnc-embed-recovery]"),u=r.querySelector("[data-epnc-embed-recovery-message]"),b=r.querySelector("[data-epnc-embed-recovery-body]"),f=r.querySelector("[data-epnc-embed-recovery-actions]"),o=r.querySelector("[data-epnc-embed-iframe]"),te=String(r.getAttribute("data-l10n-external-title")||"Pad from another server").trim(),ne=String(r.getAttribute("data-l10n-external-message")||"Read-only snapshot from the .pad file.").trim(),re=String(r.getAttribute("data-l10n-external-empty")||"No synced snapshot is stored in this .pad file yet.").trim(),ae=String(r.getAttribute("data-l10n-external-link")||"Open original pad").trim(),ie=String(r.getAttribute("data-l10n-recovery-checking")||"Checking for the original pad...").trim(),oe=String(r.getAttribute("data-l10n-recovery-copy-body")||"").trim(),se=String(r.getAttribute("data-l10n-recovery-orphan-body")||"").trim(),ce=String(r.getAttribute("data-l10n-recovery-open-original")||"Open the original .pad file").trim(),Y=String(r.getAttribute("data-l10n-recovery-create-new")||"Create new pad from this file").trim(),de=String(r.getAttribute("data-l10n-recovery-creating")||"Creating new pad...").trim();let y="",J=12e4,E=null,N=!1,k=!1,L=!1,v=null,F=null,P=null,R=null;const S=()=>_e($),T=e=>{i instanceof HTMLElement&&(i.hidden=!0,i.classList.remove("epnc-embed__loading--snapshot")),o instanceof HTMLIFrameElement&&(o.hidden=!0,o.removeAttribute("src")),G instanceof HTMLElement&&(G.textContent=String(e||"Unknown error.")),m instanceof HTMLElement&&(m.hidden=!1)},le=(e,t,n)=>{if(m instanceof HTMLElement&&(m.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 c=document.createElement("div");c.className="epnc-embed__snapshot-inner";const l=document.createElement("h2");l.className="epnc-embed__snapshot-title",l.textContent=te;const s=document.createElement("p");s.className="epnc-embed__snapshot-message",s.textContent=ne;const d=document.createElement("a");d.className="epnc-embed__snapshot-link",d.href=e,d.target="_blank",d.rel="noopener noreferrer",d.textContent=ae;const _=document.createElement("div");_.className="epnc-embed__snapshot-actions",_.appendChild(d);const g=String(n||"").trim()!=="",x=document.createElement(g?"div":"pre");x.className=g?"epnc-embed__snapshot-text epnc-embed__snapshot-text--html":"epnc-embed__snapshot-text",g?x.innerHTML=String(n):x.textContent=String(t||"").trim()!==""?String(t):re;const A=document.createElement("div");A.className="epnc-embed__snapshot-heading",A.appendChild(l),A.appendChild(s);const I=document.createElement("div");I.className="epnc-embed__snapshot-header",I.appendChild(A),I.appendChild(_),c.appendChild(I),c.appendChild(x),a.appendChild(c),i.appendChild(a)},me=e=>{if(!(o instanceof HTMLIFrameElement)){T("Embed iframe is not available.");return}m instanceof HTMLElement&&(m.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},w=()=>{v!==null&&(window.clearInterval(v),v=null)},C=(e,t)=>{M(e,t).catch(()=>{})},O=(e,t,n,a={})=>{!e||typeof e.postMessage!="function"||e.postMessage(Object.assign({type:n,fileId:h},a),t)},M=async(e,t)=>{if(!y)return{status:"disabled"};if(E)return e&&!N?(k=!0,L=L||!!t,E.catch(()=>{}).then(()=>M(!0,L))):E;N=!!e;const n=(async()=>{const d=e?y+(y.includes("?")?"&":"?")+"force=1":y,_=await fetch(d,{method:"POST",credentials:"same-origin",headers:{Accept:"application/json",requesttoken:S()},keepalive:!!t}),g=await _.json().catch(()=>({}));if(!_.ok)throw new Error(g&&g.message||"Sync request failed.");return g})();E=n;let a,c=null;try{a=await n}catch(d){c=d}finally{E===n&&(E=null),N=!1}const l=k,s=L;if(k=!1,L=!1,l)return M(!0,s);if(c instanceof Error)throw c;return a},U=()=>{!y||v!==null||(v=window.setInterval(()=>{document.visibilityState==="visible"&&C(!1,!1)},J))},pe=()=>{F||P||(F=()=>{if(document.visibilityState==="hidden"){C(!0,!0),w();return}U()},P=()=>{C(!0,!0),w()},document.addEventListener("visibilitychange",F),window.addEventListener("pagehide",P))},ue=e=>!e||e==="null"?!1:e===window.location.origin?!0:ee.includes(e),fe=()=>{R||(R=e=>{const t=String(e.origin||"");if(!ue(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"){U();return}if(a==="epnc:host-hidden"){C(!0,!0),w();return}if(a==="epnc:host-before-close"||a==="epnc:host-sync-now"){const c=a!=="epnc:host-sync-now",l=a==="epnc:host-before-close"?"before-close":"sync-now";O(e.source,t,"epnc:sync-flush-started",{reason:l}),M(!0,c).then(s=>{O(e.source,t,"epnc:sync-flush-finished",{reason:l,result:s&&typeof s=="object"?s:{}})}).catch(s=>{O(e.source,t,"epnc:sync-flush-failed",{reason:l,message:s instanceof Error?s.message:"Sync failed."})}),c&&w()}}},window.addEventListener("message",R))},he=e=>e instanceof Error?String(e.message||"").includes("Missing YAML frontmatter"):!1,K=async()=>{const e=new URLSearchParams;e.set("fileId",String(h));const t=await q(j,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded;charset=UTF-8",requesttoken:S()},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},ge=async()=>{const e=D.replace("__FILE_ID__",encodeURIComponent(String(h)));await q(e,{method:"POST",headers:{requesttoken:S()}})},H=()=>{i instanceof HTMLElement&&(i.hidden=!0),m instanceof HTMLElement&&(m.hidden=!0),p instanceof HTMLElement&&(p.hidden=!0),o instanceof HTMLIFrameElement&&(o.hidden=!0,o.removeAttribute("src"))},be=()=>{p instanceof HTMLElement&&(H(),p.hidden=!1,u instanceof HTMLElement&&(u.textContent=ie),b instanceof HTMLElement&&(b.textContent=""),f instanceof HTMLElement&&f.replaceChildren())},Q=(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},ye=(e,t)=>{if(p instanceof HTMLElement&&(H(),p.hidden=!1,u instanceof HTMLElement&&(u.textContent=t),b instanceof HTMLElement&&(b.textContent=oe),f instanceof HTMLElement)){const n=document.createElement("a");n.className="epnc-embed__recovery-button epnc-embed__recovery-button--primary",n.href=e,n.textContent=ce,f.replaceChildren(n,Q(Y,()=>{X()}))}},V=e=>{if(p instanceof HTMLElement&&(H(),p.hidden=!1,u instanceof HTMLElement&&(u.textContent=e),b instanceof HTMLElement&&(b.textContent=se),f instanceof HTMLElement)){const t=Q(Y,()=>{X()});t.classList.add("epnc-embed__recovery-button--primary"),f.replaceChildren(t)}},W=e=>{f instanceof HTMLElement&&f.querySelectorAll("button").forEach(t=>{t.disabled=e,e?(t.dataset.originalLabel=t.dataset.originalLabel||t.textContent||"",t.textContent=de):t.dataset.originalLabel&&(t.textContent=t.dataset.originalLabel,delete t.dataset.originalLabel)})},X=async()=>{if(B===""){T("Recovery is not available in this embed.");return}W(!0);const e=B.replace("__FILE_ID__",encodeURIComponent(String(h)));try{await q(e,{method:"POST",headers:{requesttoken:S()}}),H(),i instanceof HTMLElement&&(i.hidden=!1),Z()}catch(t){W(!1),u instanceof HTMLElement&&(u.textContent=t instanceof Error&&t.message?t.message:"Recovery failed.")}},Ee=async e=>{const t=e instanceof Error&&e.message?e.message:"Pad open failed.";if(be(),z===""){V(t);return}const n=z.replace("__FILE_ID__",encodeURIComponent(String(h)));try{const a=await q(n,{method:"GET"});if(a&&a.found===!0&&typeof a.embed_url=="string"&&a.embed_url!==""){ye(a.embed_url,t);return}}catch{}V(t)},Z=async()=>{if(!Number.isFinite(h)||h<=0||j===""||D===""){T("Embed configuration is incomplete.");return}if(S()===""){T("CSRF request token is missing.");return}try{let e;try{e=await K()}catch(n){if(!he(n))throw n;await ge(),e=await K()}y=typeof e.sync_url=="string"?e.sync_url.trim():"";const t=Number(e.sync_interval_seconds??0);if(J=Number.isFinite(t)&&t>0?t*1e3:12e4,pe(),fe(),U(),e.is_external===!0){le(e.url,typeof e.snapshot_text=="string"?e.snapshot_text:"",typeof e.snapshot_html=="string"?e.snapshot_html:"");return}me(e.url)}catch(e){if(e&&e.code==="missing_binding"){Ee(e);return}T(e instanceof Error?e.message:"Pad open failed.")}};Z()})(); +import{o as Ee}from"./oc-compat-hVqZy-MX.chunk.mjs";import{f as q}from"./fetch-helpers-C4MxuNvt.chunk.mjs";(function(){const r=document.getElementById("etherpad-nextcloud-embed");if(!(r instanceof HTMLElement))return;const h=Number(r.getAttribute("data-file-id")||""),j=String(r.getAttribute("data-open-by-id-url")||"").trim(),D=String(r.getAttribute("data-initialize-by-id-url-template")||"").trim(),B=String(r.getAttribute("data-recover-url-template")||"").trim(),z=String(r.getAttribute("data-find-original-url-template")||"").trim(),$=String(r.getAttribute("data-request-token")||"").trim(),ee=String(r.getAttribute("data-trusted-origins")||"").split(/\s+/).map(e=>e.trim()).filter(Boolean),i=r.querySelector("[data-epnc-embed-loading]"),m=r.querySelector("[data-epnc-embed-error]"),G=r.querySelector("[data-epnc-embed-error-message]"),p=r.querySelector("[data-epnc-embed-recovery]"),u=r.querySelector("[data-epnc-embed-recovery-message]"),b=r.querySelector("[data-epnc-embed-recovery-body]"),f=r.querySelector("[data-epnc-embed-recovery-actions]"),o=r.querySelector("[data-epnc-embed-iframe]"),te=String(r.getAttribute("data-l10n-external-title")||"Pad from another server").trim(),ne=String(r.getAttribute("data-l10n-external-message")||"Read-only snapshot from the .pad file.").trim(),re=String(r.getAttribute("data-l10n-external-empty")||"No synced snapshot is stored in this .pad file yet.").trim(),ae=String(r.getAttribute("data-l10n-external-link")||"Open original pad").trim(),ie=String(r.getAttribute("data-l10n-recovery-checking")||"Checking for the original pad...").trim(),oe=String(r.getAttribute("data-l10n-recovery-copy-body")||"").trim(),se=String(r.getAttribute("data-l10n-recovery-orphan-body")||"").trim(),ce=String(r.getAttribute("data-l10n-recovery-open-original")||"Open the original .pad file").trim(),Y=String(r.getAttribute("data-l10n-recovery-create-new")||"Create new pad from this file").trim(),de=String(r.getAttribute("data-l10n-recovery-creating")||"Creating new pad...").trim();let y="",J=12e4,_=null,N=!1,k=!1,L=!1,v=null,F=null,P=null,R=null;const S=()=>Ee($),w=e=>{i instanceof HTMLElement&&(i.hidden=!0,i.classList.remove("epnc-embed__loading--snapshot")),o instanceof HTMLIFrameElement&&(o.hidden=!0,o.removeAttribute("src")),G instanceof HTMLElement&&(G.textContent=String(e||"Unknown error.")),m instanceof HTMLElement&&(m.hidden=!1)},le=(e,t,n)=>{if(m instanceof HTMLElement&&(m.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 c=document.createElement("div");c.className="epnc-embed__snapshot-inner";const l=document.createElement("h2");l.className="epnc-embed__snapshot-title",l.textContent=te;const s=document.createElement("p");s.className="epnc-embed__snapshot-message",s.textContent=ne;const d=document.createElement("a");d.className="epnc-embed__snapshot-link",d.href=e,d.target="_blank",d.rel="noopener noreferrer",d.textContent=ae;const E=document.createElement("div");E.className="epnc-embed__snapshot-actions",E.appendChild(d);const g=String(n||"").trim()!=="",x=document.createElement(g?"div":"pre");x.className=g?"epnc-embed__snapshot-text epnc-embed__snapshot-text--html":"epnc-embed__snapshot-text",g?x.innerHTML=String(n):x.textContent=String(t||"").trim()!==""?String(t):re;const A=document.createElement("div");A.className="epnc-embed__snapshot-heading",A.appendChild(l),A.appendChild(s);const I=document.createElement("div");I.className="epnc-embed__snapshot-header",I.appendChild(A),I.appendChild(E),c.appendChild(I),c.appendChild(x),a.appendChild(c),i.appendChild(a)},me=e=>{if(!(o instanceof HTMLIFrameElement)){w("Embed iframe is not available.");return}m instanceof HTMLElement&&(m.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},T=()=>{v!==null&&(window.clearInterval(v),v=null)},C=(e,t)=>{M(e,t).catch(()=>{})},O=(e,t,n,a={})=>{!e||typeof e.postMessage!="function"||e.postMessage(Object.assign({type:n,fileId:h},a),t)},M=async(e,t)=>{if(!y)return{status:"disabled"};if(_)return e&&!N?(k=!0,L=L||!!t,_.catch(()=>{}).then(()=>M(!0,L))):_;N=!!e;const n=(async()=>{const d=e?y+(y.includes("?")?"&":"?")+"force=1":y,E=await fetch(d,{method:"POST",credentials:"same-origin",headers:{Accept:"application/json",requesttoken:S()},keepalive:!!t}),g=await E.json().catch(()=>({}));if(!E.ok)throw new Error(g&&g.message||"Sync request failed.");return g})();_=n;let a,c=null;try{a=await n}catch(d){c=d}finally{_===n&&(_=null),N=!1}const l=k,s=L;if(k=!1,L=!1,l)return M(!0,s);if(c instanceof Error)throw c;return a},U=()=>{!y||v!==null||(v=window.setInterval(()=>{document.visibilityState==="visible"&&C(!1,!1)},J))},pe=()=>{F||P||(F=()=>{if(document.visibilityState==="hidden"){C(!0,!0),T();return}U()},P=()=>{C(!0,!0),T()},document.addEventListener("visibilitychange",F),window.addEventListener("pagehide",P))},ue=e=>!e||e==="null"?!1:e===window.location.origin?!0:ee.includes(e),fe=()=>{R||(R=e=>{const t=String(e.origin||"");if(!ue(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"){U();return}if(a==="epnc:host-hidden"){C(!0,!0),T();return}if(a==="epnc:host-before-close"||a==="epnc:host-sync-now"){const c=a!=="epnc:host-sync-now",l=a==="epnc:host-before-close"?"before-close":"sync-now";O(e.source,t,"epnc:sync-flush-started",{reason:l}),M(!0,c).then(s=>{O(e.source,t,"epnc:sync-flush-finished",{reason:l,result:s&&typeof s=="object"?s:{}})}).catch(s=>{O(e.source,t,"epnc:sync-flush-failed",{reason:l,message:s instanceof Error?s.message:"Sync failed."})}),c&&T()}}},window.addEventListener("message",R))},he=e=>e instanceof Error?String(e.message||"").includes("Missing YAML frontmatter"):!1,K=async()=>{const e=new URLSearchParams;e.set("fileId",String(h));const t=await q(j,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded;charset=UTF-8",requesttoken:S()},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},ge=async()=>{const e=D.replace("__FILE_ID__",encodeURIComponent(String(h))),t=await q(e,{method:"POST",headers:{requesttoken:S()}});t&&t.status==="migrated_from_legacy"&&console.info("Legacy Ownpad .pad migrated to managed format on first open.")},H=()=>{i instanceof HTMLElement&&(i.hidden=!0),m instanceof HTMLElement&&(m.hidden=!0),p instanceof HTMLElement&&(p.hidden=!0),o instanceof HTMLIFrameElement&&(o.hidden=!0,o.removeAttribute("src"))},be=()=>{p instanceof HTMLElement&&(H(),p.hidden=!1,u instanceof HTMLElement&&(u.textContent=ie),b instanceof HTMLElement&&(b.textContent=""),f instanceof HTMLElement&&f.replaceChildren())},Q=(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},ye=(e,t)=>{if(p instanceof HTMLElement&&(H(),p.hidden=!1,u instanceof HTMLElement&&(u.textContent=t),b instanceof HTMLElement&&(b.textContent=oe),f instanceof HTMLElement)){const n=document.createElement("a");n.className="epnc-embed__recovery-button epnc-embed__recovery-button--primary",n.href=e,n.textContent=ce,f.replaceChildren(n,Q(Y,()=>{X()}))}},V=e=>{if(p instanceof HTMLElement&&(H(),p.hidden=!1,u instanceof HTMLElement&&(u.textContent=e),b instanceof HTMLElement&&(b.textContent=se),f instanceof HTMLElement)){const t=Q(Y,()=>{X()});t.classList.add("epnc-embed__recovery-button--primary"),f.replaceChildren(t)}},W=e=>{f instanceof HTMLElement&&f.querySelectorAll("button").forEach(t=>{t.disabled=e,e?(t.dataset.originalLabel=t.dataset.originalLabel||t.textContent||"",t.textContent=de):t.dataset.originalLabel&&(t.textContent=t.dataset.originalLabel,delete t.dataset.originalLabel)})},X=async()=>{if(B===""){w("Recovery is not available in this embed.");return}W(!0);const e=B.replace("__FILE_ID__",encodeURIComponent(String(h)));try{await q(e,{method:"POST",headers:{requesttoken:S()}}),H(),i instanceof HTMLElement&&(i.hidden=!1),Z()}catch(t){W(!1),u instanceof HTMLElement&&(u.textContent=t instanceof Error&&t.message?t.message:"Recovery failed.")}},_e=async e=>{const t=e instanceof Error&&e.message?e.message:"Pad open failed.";if(be(),z===""){V(t);return}const n=z.replace("__FILE_ID__",encodeURIComponent(String(h)));try{const a=await q(n,{method:"GET"});if(a&&a.found===!0&&typeof a.embed_url=="string"&&a.embed_url!==""){ye(a.embed_url,t);return}}catch{}V(t)},Z=async()=>{if(!Number.isFinite(h)||h<=0||j===""||D===""){w("Embed configuration is incomplete.");return}if(S()===""){w("CSRF request token is missing.");return}try{let e;try{e=await K()}catch(n){if(!he(n))throw n;await ge(),e=await K()}y=typeof e.sync_url=="string"?e.sync_url.trim():"";const t=Number(e.sync_interval_seconds??0);if(J=Number.isFinite(t)&&t>0?t*1e3:12e4,pe(),fe(),U(),e.is_external===!0){le(e.url,typeof e.snapshot_text=="string"?e.snapshot_text:"",typeof e.snapshot_html=="string"?e.snapshot_html:"");return}me(e.url)}catch(e){if(e&&e.code==="missing_binding"){_e(e);return}w(e instanceof Error?e.message:"Pad open failed.")}};Z()})(); //# 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 cb54445..10e0352 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 { 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 syncUrl = ''\n\tlet syncIntervalMs = 120000\n\tlet syncPromise = null\n\tlet activeSyncForce = false\n\tlet pendingForcedSync = false\n\tlet pendingForcedKeepalive = false\n\tlet syncTimerId = null\n\tlet visibilityHandler = null\n\tlet pageHideHandler = null\n\tlet messageHandler = null\n\n\tconst requestToken = () => ocRequestToken(templateRequestToken)\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 stopSyncLoop = () => {\n\t\tif (syncTimerId !== null) {\n\t\t\twindow.clearInterval(syncTimerId)\n\t\t\tsyncTimerId = null\n\t\t}\n\t}\n\n\tconst fireAndForgetSync = (force, keepalive) => {\n\t\tvoid runSync(force, keepalive).catch(() => {})\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 runSync = async (force, keepalive) => {\n\t\tif (!syncUrl) {\n\t\t\treturn { status: 'disabled' }\n\t\t}\n\t\tif (syncPromise) {\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(() => runSync(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 fetch(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: requestToken(),\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 runSync(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 startSyncLoop = () => {\n\t\tif (!syncUrl || syncTimerId !== null) {\n\t\t\treturn\n\t\t}\n\t\tsyncTimerId = window.setInterval(() => {\n\t\t\tif (document.visibilityState === 'visible') {\n\t\t\t\tfireAndForgetSync(false, false)\n\t\t\t}\n\t\t}, syncIntervalMs)\n\t}\n\n\tconst installSyncLifecycleHandlers = () => {\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\tfireAndForgetSync(true, true)\n\t\t\t\tstopSyncLoop()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tstartSyncLoop()\n\t\t}\n\t\tpageHideHandler = () => {\n\t\t\tfireAndForgetSync(true, true)\n\t\t\tstopSyncLoop()\n\t\t}\n\t\tdocument.addEventListener('visibilitychange', visibilityHandler)\n\t\twindow.addEventListener('pagehide', pageHideHandler)\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\tstartSyncLoop()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (type === 'epnc:host-hidden') {\n\t\t\t\tfireAndForgetSync(true, true)\n\t\t\t\tstopSyncLoop()\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 runSync(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\tstopSyncLoop()\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\tawait 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}\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\tsyncUrl = typeof data.sync_url === 'string' ? data.sync_url.trim() : ''\n\t\t\tconst intervalSeconds = Number(data.sync_interval_seconds ?? 0)\n\t\t\tsyncIntervalMs = Number.isFinite(intervalSeconds) && intervalSeconds > 0 ? intervalSeconds * 1000 : 120000\n\t\t\tinstallSyncLifecycleHandlers()\n\t\t\tinstallHostMessageHandler()\n\t\t\tstartSyncLoop()\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","syncUrl","syncIntervalMs","syncPromise","activeSyncForce","pendingForcedSync","pendingForcedKeepalive","syncTimerId","visibilityHandler","pageHideHandler","messageHandler","requestToken","ocRequestToken","showError","message","showExternalPadPreview","url","snapshotText","snapshotHtml","snapshot","inner","title","link","actions","hasSnapshotHtml","preview","heading","header","showIframe","revealIframe","stopSyncLoop","fireAndForgetSync","force","keepalive","runSync","postHostMessage","source","origin","type","payload","currentPromise","response","data","result","syncError","error","rerunForcedSync","rerunKeepalive","startSyncLoop","installSyncLifecycleHandlers","isAllowedMessageOrigin","installHostMessageHandler","event","reason","isMissingFrontmatterError","openPad","body","fetchJson","initializePad","hideAllPanels","showRecoveryChecking","buildRecoveryButton","label","onClick","button","showRecoveryWithOriginal","originalEmbedUrl","errorMessage","openLink","triggerRecovery","showRecoveryWithoutOriginal","setRecoveryActionsBusy","busy","node","run","enterRecoveryFlow","initialError","lookupUrl","hint","intervalSeconds"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"etherpad_nextcloud-embed-main.mjs","sources":["../src/embed-main.js"],"sourcesContent":["/**\n * SPDX-License-Identifier: AGPL-3.0-or-later\n * Copyright (c) 2026 Jacob Bühler\n */\nimport { ocRequestToken } from './lib/oc-compat.js'\nimport { 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 syncUrl = ''\n\tlet syncIntervalMs = 120000\n\tlet syncPromise = null\n\tlet activeSyncForce = false\n\tlet pendingForcedSync = false\n\tlet pendingForcedKeepalive = false\n\tlet syncTimerId = null\n\tlet visibilityHandler = null\n\tlet pageHideHandler = null\n\tlet messageHandler = null\n\n\tconst requestToken = () => ocRequestToken(templateRequestToken)\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 stopSyncLoop = () => {\n\t\tif (syncTimerId !== null) {\n\t\t\twindow.clearInterval(syncTimerId)\n\t\t\tsyncTimerId = null\n\t\t}\n\t}\n\n\tconst fireAndForgetSync = (force, keepalive) => {\n\t\tvoid runSync(force, keepalive).catch(() => {})\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 runSync = async (force, keepalive) => {\n\t\tif (!syncUrl) {\n\t\t\treturn { status: 'disabled' }\n\t\t}\n\t\tif (syncPromise) {\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(() => runSync(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 fetch(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: requestToken(),\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 runSync(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 startSyncLoop = () => {\n\t\tif (!syncUrl || syncTimerId !== null) {\n\t\t\treturn\n\t\t}\n\t\tsyncTimerId = window.setInterval(() => {\n\t\t\tif (document.visibilityState === 'visible') {\n\t\t\t\tfireAndForgetSync(false, false)\n\t\t\t}\n\t\t}, syncIntervalMs)\n\t}\n\n\tconst installSyncLifecycleHandlers = () => {\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\tfireAndForgetSync(true, true)\n\t\t\t\tstopSyncLoop()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tstartSyncLoop()\n\t\t}\n\t\tpageHideHandler = () => {\n\t\t\tfireAndForgetSync(true, true)\n\t\t\tstopSyncLoop()\n\t\t}\n\t\tdocument.addEventListener('visibilitychange', visibilityHandler)\n\t\twindow.addEventListener('pagehide', pageHideHandler)\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\tstartSyncLoop()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (type === 'epnc:host-hidden') {\n\t\t\t\tfireAndForgetSync(true, true)\n\t\t\t\tstopSyncLoop()\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 runSync(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\tstopSyncLoop()\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\tsyncUrl = typeof data.sync_url === 'string' ? data.sync_url.trim() : ''\n\t\t\tconst intervalSeconds = Number(data.sync_interval_seconds ?? 0)\n\t\t\tsyncIntervalMs = Number.isFinite(intervalSeconds) && intervalSeconds > 0 ? intervalSeconds * 1000 : 120000\n\t\t\tinstallSyncLifecycleHandlers()\n\t\t\tinstallHostMessageHandler()\n\t\t\tstartSyncLoop()\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","syncUrl","syncIntervalMs","syncPromise","activeSyncForce","pendingForcedSync","pendingForcedKeepalive","syncTimerId","visibilityHandler","pageHideHandler","messageHandler","requestToken","ocRequestToken","showError","message","showExternalPadPreview","url","snapshotText","snapshotHtml","snapshot","inner","title","link","actions","hasSnapshotHtml","preview","heading","header","showIframe","revealIframe","stopSyncLoop","fireAndForgetSync","force","keepalive","runSync","postHostMessage","source","origin","type","payload","currentPromise","response","data","result","syncError","error","rerunForcedSync","rerunKeepalive","startSyncLoop","installSyncLifecycleHandlers","isAllowedMessageOrigin","installHostMessageHandler","event","reason","isMissingFrontmatterError","openPad","body","fetchJson","initializePad","hideAllPanels","showRecoveryChecking","buildRecoveryButton","label","onClick","button","showRecoveryWithOriginal","originalEmbedUrl","errorMessage","openLink","triggerRecovery","showRecoveryWithoutOriginal","setRecoveryActionsBusy","busy","node","run","enterRecoveryFlow","initialError","lookupUrl","hint","intervalSeconds"],"mappings":"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"} \ No newline at end of file diff --git a/js/etherpad_nextcloud-files-main.mjs b/js/etherpad_nextcloud-files-main.mjs index 217662f..fb81b19 100644 --- a/js/etherpad_nextcloud-files-main.mjs +++ b/js/etherpad_nextcloud-files-main.mjs @@ -1,2 +1,2 @@ -import{M as j,a as ne,b as ie,A as f,c as re,d as oe,n as ae,e as le,f as se}from"./oc-compat-DlZkDG_8.chunk.mjs";import{g as V,n as O,p as ce,a as T,b as B,c as de,d as M,v as _,f as k,e as ue,r as X,h as fe,i as pe,j as F,k as we,l as me}from"./api-client-CT0tqdIR.chunk.mjs";const N=()=>!!(window.OCA&&window.OCA.Viewer&&typeof window.OCA.Viewer.open=="function"),v=()=>(window.location.pathname||"").includes("/apps/files"),U=()=>{const e=window.OCP&&window.OCP.Files&&window.OCP.Files.Router;return e&&typeof e.goToRoute=="function"?e:null},ye=e=>{const i=e instanceof Error?e.message:String(e||"");return i.includes("Redirected when going")&&i.includes("navigation guard")},E=e=>{!e||typeof e.then!="function"||Promise.resolve(e).catch(i=>{ye(i)||window.console&&typeof window.console.debug=="function"&&window.console.debug("[etherpad_nextcloud] Unexpected navigation rejection",i)})},D=e=>{const i=Number(e);return Number.isFinite(i)&&i>0?i:null},z="etherpad_nextcloud_open",he=e=>{if(!e)return null;const i=[e.fileId,e.fileid,e.id,e.nodeId,e.nodeid,e.file_id];for(const n of i){const r=D(n);if(r!==null)return r}if(typeof e.get=="function")for(const n of["fileid","fileId","id"])try{const r=D(e.get(n));if(r!==null)return r}catch{}return null},ge=e=>{if(!(e instanceof Element))return null;const i=[e.getAttribute("data-fileid"),e.getAttribute("data-file-id"),e.getAttribute("data-id"),e.getAttribute("data-node-id")];for(const r of i){const o=D(r);if(o!==null)return o}const n=e.matches("a[href]")?e:e.querySelector("a[href]");return n instanceof HTMLAnchorElement?ce(n.getAttribute("href")):null},be=e=>{if(!e||typeof e!="object")return null;const i=[e,e.fileInfoModel,e.fileInfo,e.model,e.node,e.file,e.attributes];for(const r of i){const o=he(r);if(o!==null)return o}const n=[e.fileElement,e.el,e.element,e.$file&&e.$file[0],e.$el&&e.$el[0],e.target,e.currentTarget];for(const r of n){const o=ge(r);if(o!==null)return o}return null},Ce=({openPadInNativeViewer:e})=>{window.OCA&&window.OCA.Files&&window.OCA.Files.fileActions&&(window.OCA.Files.fileActions.register(j,z,ne(),ie(f,"etherpad-icon-color"),(i,n)=>{const r=n&&n.dir||V(),o=O(r,i),d=be(n);e({path:o,fileId:d})},t(f,"Open in Etherpad")),window.OCA.Files.fileActions.setDefault(j,z))},ve=800,xe=180,Ee=120,Pe=(e,i)=>{const n=U();if(!n||!e||!Number.isFinite(e))return!1;let r=!1;return E(n.goToRoute(null,{view:"files",fileid:String(e)},{dir:X(i)})),window.setTimeout(()=>{try{const o=window.OCA.Viewer.open({path:i,onClose:Ne});r=!0,E(o)}catch{}},Ee),window.setTimeout(()=>{if(r)return;const o=k(e,i);window.location.assign(o)},xe),!0},Ne=()=>{const e=U();if(!e)return;const i={...e.query||{}};delete i.openfile,delete i.editing,E(e.goToRoute(null,e.params||{},i))},Ae=()=>{let e=null,i=0;return async n=>{const r=String(n.fileId??"")+"|"+String(n.path??""),o=Date.now();if(e===r&&o-i{if(s){window.location.assign(_(d,n.path||""));return}if(v()&&n.fileId!==null&&n.fileId!==void 0&&Number.isFinite(Number(n.fileId))){const m=n.path||"/";window.location.assign(k(Number(n.fileId),m));return}const l=v()?M():null;if((n.fileId===null||n.fileId===void 0)&&l){const m=n.path||"/";window.location.assign(k(l,m));return}if(n.fileId!==null&&n.fileId!==void 0&&Number.isFinite(Number(n.fileId))){const m=n.path||"/";window.location.assign(k(Number(n.fileId),m));return}n.path&&window.location.assign(ue(n.path))};if(!N()){u();return}let a=n.path||"",c=n.fileId??null;if(!a&&n.fileId!==null&&n.fileId!==void 0)try{const l=await B(n.fileId);a=l&&typeof l.path=="string"?l.path:"",c=l&&Number.isFinite(Number(l.file_id))?Number(l.file_id):c}catch{a=""}if(!a){u();return}if((!c||!Number.isFinite(Number(c)))&&a&&!s)try{const l=await de(a);c=l&&Number.isFinite(Number(l.file_id))?Number(l.file_id):c}catch{}if((!c||!Number.isFinite(Number(c)))&&v()){const l=M();l&&(c=l)}if(v()){if(c&&Number.isFinite(Number(c))&&Pe(Number(c),a))return;u();return}try{const l=window.OCA.Viewer.open({path:a});E(l)}catch{u()}}},Ie=120,Oe=900,Fe=async(e,i={})=>{if(N()&&e.path)try{const n=Number(e.fileId);v()&&Number.isFinite(n)&&n>0&&(qe(n,e.path,i.resolveOpenDir),await Se()),await Re(e.path);const r=window.OCA.Viewer.open({path:e.path,onClose:Me});E(r);return}catch{}typeof i.fallbackOpen=="function"&&await i.fallbackOpen(e)},Se=()=>new Promise(e=>{window.setTimeout(e,Ie)}),Re=async e=>{const i=re(e);i!==""&&(oe("editor:file:created",i),await Le())},Le=()=>new Promise(e=>{window.setTimeout(e,Oe)}),qe=(e,i,n)=>{const r=U();if(!r)return;const o={...r.query||{}};delete o.openfile,delete o.editing,o.dir=ke(i,n),E(r.goToRoute(null,{...r.params||{},view:r.params&&r.params.view?r.params.view:"files",fileid:String(e)},o,!0))},ke=(e,i)=>{if(typeof i=="function")return i(e);const n=String(e||""),r=n.lastIndexOf("/");return r>0?n.substring(0,r):"/"},Me=()=>{const e=U();if(!e)return;const i={...e.query||{}};delete i.openfile,delete i.editing,E(e.goToRoute(null,e.params||{},i))},_e=e=>{try{const i=new URL(e,window.location.origin),n=decodeURIComponent(i.pathname||"").match(/\/p\/([^/]+)$/);if(n&&n[1]){const r=n[1].replace(/[^a-zA-Z0-9._-]+/g,"-").replace(/^-+|-+$/g,"");if(r!=="")return r+".pad"}}catch{}return"Imported pad.pad"},Y=e=>{const i=document.createElement("div");i.style.position="fixed",i.style.inset="0",i.style.background="rgba(0, 0, 0, 0.45)",i.style.display="flex",i.style.alignItems="center",i.style.justifyContent="center",i.style.zIndex="20000";const n=document.createElement("div");n.style.position="relative",n.style.background="var(--color-main-background, #fff)",n.style.borderRadius="10px",n.style.boxShadow="0 10px 30px rgba(0, 0, 0, 0.25)",n.style.padding="18px",n.style.width="min(460px, calc(100vw - 24px))";const r=document.createElement("button");r.type="button",r.setAttribute("aria-label",t(f,"Close")),r.textContent="×",r.style.position="absolute",r.style.top="8px",r.style.right="10px",r.style.border="none",r.style.background="transparent",r.style.fontSize="22px",r.style.cursor="pointer",r.style.lineHeight="1";const o=document.createElement("h3");return o.textContent=e,o.style.margin="0 26px 10px 0",o.style.fontSize="18px",n.appendChild(r),n.appendChild(o),i.appendChild(n),document.body.appendChild(i),{overlay:i,dialog:n,closeButton:r}},Te=({onSubmit:e})=>new Promise(i=>{const{overlay:n,dialog:r,closeButton:o}=Y(t(f,"Public pad")),d=document.createElement("label");d.textContent=t(f,"File name"),d.style.display="block",d.style.marginBottom="6px";const s=document.createElement("input");s.type="text",s.value=t(f,"Public pad")+".pad",s.style.width="100%",s.style.boxSizing="border-box",s.style.marginBottom="12px";const u=document.createElement("p");u.style.color="var(--color-error, #c62828)",u.style.margin="0 0 12px 0",u.style.minHeight="20px";const a=document.createElement("button");a.type="button",a.className="primary",a.textContent=t(f,"Create");let c=!1;const l=p=>{c||(n.remove(),i(p))};o.addEventListener("click",()=>l(null)),n.addEventListener("click",p=>{p.target===n&&l(null)});const m=p=>{c=p,a.disabled=p,s.disabled=p,o.disabled=p,a.textContent=p?t(f,"Creating..."):t(f,"Create")},h=async()=>{const p=s.value.trim();if(p===""){u.textContent=t(f,"File name is required."),s.focus();return}u.textContent="",m(!0);try{const b=await e(p);m(!1),l(b)}catch(b){m(!1),u.textContent=b instanceof Error&&b.message!==""?b.message:t(f,"Could not create public pad."),s.focus(),s.select()}};s.addEventListener("keydown",p=>{p.key==="Enter"&&(p.preventDefault(),h())}),a.addEventListener("click",()=>{h()}),r.appendChild(d),r.appendChild(s),r.appendChild(u),r.appendChild(a),s.focus(),s.select()}),Ue=({onSubmit:e})=>new Promise(i=>{const{overlay:n,dialog:r,closeButton:o}=Y(t(f,"Public pad from URL")),d=document.createElement("label");d.textContent=t(f,"External pad URL"),d.style.display="block",d.style.marginBottom="6px";const s=document.createElement("input");s.type="url",s.value="https://",s.placeholder="https://",s.style.width="100%",s.style.boxSizing="border-box",s.style.marginBottom="12px";const u=document.createElement("label");u.textContent=t(f,"File name"),u.style.display="block",u.style.marginBottom="6px";const a=document.createElement("input");a.type="text",a.value="Imported pad.pad",a.style.width="100%",a.style.boxSizing="border-box",a.style.marginBottom="12px";const c=document.createElement("p");c.style.color="var(--color-error, #c62828)",c.style.margin="0 0 12px 0",c.style.minHeight="20px";const l=document.createElement("button");l.type="button",l.className="primary",l.textContent=t(f,"Create");let m=!1;const h=w=>{m||(n.remove(),i(w))};o.addEventListener("click",()=>h(null)),n.addEventListener("click",w=>{w.target===n&&h(null)}),s.addEventListener("blur",()=>{const w=s.value.trim();w.startsWith("http")&&(a.value=_e(w))});const p=w=>{m=w,l.disabled=w,s.disabled=w,a.disabled=w,o.disabled=w,l.textContent=w?t(f,"Creating..."):t(f,"Create")},b=async()=>{const w=s.value.trim(),S=a.value.trim();if(w===""){c.textContent=t(f,"External pad URL is required."),s.focus();return}if(S===""){c.textContent=t(f,"File name is required."),a.focus();return}c.textContent="",p(!0);try{const y=await e({padUrl:w,name:S});p(!1),h(y)}catch(y){p(!1),c.textContent=y instanceof Error&&y.message!==""?y.message:t(f,"Could not import public pad URL."),a.focus(),a.select()}};s.addEventListener("keydown",w=>{w.key==="Enter"&&(w.preventDefault(),b())}),a.addEventListener("keydown",w=>{w.key==="Enter"&&(w.preventDefault(),b())}),l.addEventListener("click",()=>{b()}),r.appendChild(d),r.appendChild(s),r.appendChild(u),r.appendChild(a),r.appendChild(c),r.appendChild(l),s.focus(),s.select()}),W=e=>F(e)?e:e+".pad",He=(e,i)=>({path:e&&typeof e.file=="string"?e.file:i,fileId:e&&Number.isFinite(Number(e.file_id))?Number(e.file_id):null}),Ve=({openPadInNativeViewer:e})=>{const i=(d,s)=>Fe(He(d,s),{fallbackOpen:e,resolveOpenDir:X}),n=async()=>{await Te({onSubmit:async d=>{const s=O(V(),W(d.trim())),u=await fe(s);return await i(u,s),u}})},r=async()=>{await Ue({onSubmit:async({padUrl:d,name:s})=>{const u=d.trim(),a=O(V(),W(s.trim())),c=await pe(a,u);return o(c),await i(c,a),c}})},o=d=>{if(!d||d.snapshot_warning_code!=="remote_export_unavailable")return;const s=t(f,"Could not fetch the pad content from the remote server. The new file is empty for now — open the pad in the viewer to populate it."),u=window.OC;u&&u.Notification&&typeof u.Notification.showTemporary=="function"?u.Notification.showTemporary(s,{type:"warning"}):window.console.warn("[etherpad_nextcloud] "+s)};return{createInternalPublicPad:n,createExternalPublicPad:r}},$=f+"_public_pad",K=f+"_public_pad_external",R="icon-filetype-etherpad-nextcloud-pad",L='',J=98,Z=99,Be=120,De=500,q=()=>{const e=ae();return e!==null&&e>=33},I=e=>(e&&e.message?String(e.message):"").toLowerCase().includes("duplicate"),je=e=>{if(!e||typeof e!="object")return null;if(e.hasCreatePermission===!1)return!1;if(e.hasCreatePermission===!0)return!0;if(typeof e.canCreate=="boolean")return e.canCreate;const i=le(),n=[e.permissions,e.permission,e.attributes&&e.attributes.permissions,typeof e.get=="function"?e.get("permissions"):null];for(const r of n){if(r==null)continue;const o=Number(r);if(Number.isFinite(o))return(o&i)===i}return null},ze=(...e)=>{for(const i of e){const n=je(i);if(n!==null)return n}return!0},We=()=>{const e=[window.OCP&&window.OCP.Files,window.OCA&&window.OCA.Files];for(const n of e)if(!(!n||typeof n!="object")){if(typeof n.addNewFileMenuEntry=="function")return{register:n.addNewFileMenuEntry.bind(n),unregister:typeof n.removeNewFileMenuEntry=="function"?n.removeNewFileMenuEntry.bind(n):null};if(typeof n.getNewFileMenu=="function"){let r=null;try{r=n.getNewFileMenu()}catch{r=null}if(r&&typeof r.registerEntry=="function")return{register:r.registerEntry.bind(r),unregister:typeof r.unregisterEntry=="function"?r.unregisterEntry.bind(r):null}}}const i=window._nc_newfilemenu;return i&&typeof i=="object"&&typeof i.registerEntry=="function"?{register:i.registerEntry.bind(i),unregister:typeof i.unregisterEntry=="function"?i.unregisterEntry.bind(i):null}:null},$e=({isFilesAppRoute:e,onCreateInternalPublicPad:i,onCreateExternalPublicPad:n})=>{let r=!1,o=!1,d=!1,s=0;const u=()=>{i()},a=()=>{n()},c=(...y)=>ze(...y),l=()=>({id:$,displayName:t(f,"Public pad"),...q()?{iconSvgInline:L}:{iconClass:R},order:J,enabled:c,handler:u}),m=()=>({id:K,displayName:t(f,"Public pad from URL"),...q()?{iconSvgInline:L}:{iconClass:R},order:Z,enabled:c,handler:a}),h=()=>({id:$,displayName:t(f,"Public pad"),iconClass:R,...q()?{iconSvgInline:L}:{},fileType:"file",order:J,actionHandler:u}),p=()=>({id:K,displayName:t(f,"Public pad from URL"),iconClass:R,...q()?{iconSvgInline:L}:{},fileType:"file",order:Z,actionHandler:a}),b=async()=>{const y=We();if(!y)return!1;for(const C of[l(),m()])try{y.register(C)}catch(g){if(!I(g))return!1}return r=!0,!0},w=()=>{const y=[window.OCA&&window.OCA.Files&&window.OCA.Files.NewFileMenu,window.OCA&&window.OCA.Files&&window.OCA.Files.newFileMenu],C=[{legacy:h(),modern:l()},{legacy:p(),modern:m()}];for(const g of y)if(!(!g||typeof g!="object")){if(typeof g.addMenuEntry=="function"){let x=!0;for(const{legacy:A}of C)try{g.addMenuEntry(A)}catch(H){I(H)||(x=!1)}if(x)return!0}if(typeof g.registerEntry=="function"){let x=!0;for(const{modern:A}of C)try{g.registerEntry(A)}catch(H){I(H)||(x=!1)}if(x)return!0}}return!1},S=()=>{if(o)return!0;try{if(w())return o=!0,!0;if(!d&&window.OC&&window.OC.Plugins&&typeof window.OC.Plugins.register=="function"){const y=h(),C=p();window.OC.Plugins.register("OCA.Files.NewFileMenu",{attach(g){if(!(!g||typeof g.addMenuEntry!="function")){for(const x of[y,C])try{g.addMenuEntry(x)}catch(A){if(!I(A))return}o=!0}}}),d=!0}return o}catch(y){return I(y)?(o=!0,!0):!1}};return()=>{if(!e()||r||o)return;const y=++s,C=async g=>{y!==s||r||o||await b()||S()||g{C(g+1)},De)};C(0)}},Ke=(e,i)=>{if(!(e instanceof HTMLAnchorElement)||!i)return null;const n=e.getAttribute("href")||"",r=we(n);if(r&&r.token===i&&F(r.path))return r.path;const o=me(n);return F(o)?o:null},Je=({openPadInNativeViewer:e})=>{if(window.OCA&&window.OCA.EtherpadNextcloudClickInterceptorRegistered===!0)return;window.OCA&&(window.OCA.EtherpadNextcloudClickInterceptorRegistered=!0);const i=async n=>{if(!n||n.defaultPrevented)return;const r=T();if(!r||n.type==="click"&&(n.button!==0||n.metaKey||n.ctrlKey||n.shiftKey||n.altKey)||!(n.target instanceof Element))return;const o=n.target.closest("a[href]");if(!(o instanceof HTMLAnchorElement))return;const d=Ke(o,r);if(d){if(n.preventDefault(),n.stopPropagation(),typeof n.stopImmediatePropagation=="function"&&n.stopImmediatePropagation(),N()){await e({path:d,fileId:null});return}window.location.assign(_(r,d))}};document.addEventListener("click",i,!0)},P="data-epnc-single-file-share";let G=0;const ee=e=>{if(!e)return null;const i='a[href*="/s/'+e+'/download"],a[href*="/index.php/s/'+e+'/download"]',n=document.querySelector(i);return n instanceof HTMLAnchorElement?n:null},Ze=()=>{const e=document.getElementById("initial-state-files_sharing-view");if(!(e instanceof HTMLInputElement))return"";const i=String(e.value||"").trim();if(i==="")return"";try{const n=window.atob(i);if(n.startsWith('"')||n.startsWith("{")||n.startsWith("[")){const r=JSON.parse(n);return typeof r=="string"?r:""}return n}catch{return""}},te=e=>{if(!e)return!1;const i=ee(e);if(!i)return!1;let n;try{n=new URL(i.getAttribute("href")||"",window.location.origin)}catch{return!1}const r=(n.pathname||"").match(/(?:\/index\.php)?\/s\/([^/]+)\/download\/?$/);return!r||r[1]!==e?!1:!n.searchParams.has("files")},Ge=()=>{const e=document.body;if(!(e instanceof HTMLElement))return{retry:!0};const i=T();if(!i)return e.removeAttribute(P),{retry:!1};const n=Ze();return n==="public-file-share"?(e.setAttribute(P,"1"),{retry:!1}):n==="public-share"?(e.removeAttribute(P),{retry:!1}):ee(i)?te(i)?(e.setAttribute(P,"1"),{retry:!1}):(e.removeAttribute(P),{retry:!1}):(e.removeAttribute(P),{retry:!0})},Qe=()=>{const e=++G,i=n=>{e===G&&Ge().retry&&n<30&&window.setTimeout(()=>i(n+1),180)};i(0)},Q="EtherpadNextcloudRouteWatchInstalled",Xe=({ensurePublicPadMenuRegistration:e,openPadInNativeViewer:i,schedulePublicSingleShareUiStateRefresh:n})=>{let r="";const o=async()=>{if(N())return;const a=M();if(!a)return;const c=new URLSearchParams(window.location.search||"");if(!(c.get("epNoRedirect")==="1"||c.get("details")==="1"||c.get("opendetails")==="true")&&!(window.location.pathname||"").includes("/apps/"+f+"/"))try{const l=await B(a);l&&l.is_pad&&l.viewer_url&&window.location.assign(l.viewer_url)}catch{}},d=async()=>{if(N()||!v())return;const a=M();if(!a)return;const c=new URLSearchParams(window.location.search||"");if(!(c.get("openfile")==="true"||c.get("opendetails")==="true"||c.get("details")==="1"))try{const l=await B(a);if(!l||!l.is_pad)return;const m=c.get("dir")||"/",h=se("/apps/files/files")+"?dir="+encodeURIComponent(m);window.location.href!==h&&window.location.assign(h)}catch{}},s=()=>{const a=T();if(!a)return;const c=new URLSearchParams(window.location.search||""),l=c.get("files")||"";if(N()){if(!F(l))return;const p=c.get("path")||"/",b=O(p,l);i({path:b,fileId:null});return}if(!F(l)){if((window.location.pathname||"").includes("/apps/"+f+"/public/"))return;te(a)&&window.location.assign(_(a,""));return}const m=c.get("path")||"/",h=O(m,l);window.location.assign(_(a,h))},u=()=>{const a=(window.location.pathname||"")+"?"+(window.location.search||"");a!==r&&(r=a,v()&&e(),n(),d(),o(),s())};return{evaluateCurrentRoute:u,installRouteWatchers:()=>{if(window.OCA&&window.OCA[Q]===!0)return;window.OCA&&(window.OCA[Q]=!0);const a=()=>window.setTimeout(u,0);window.addEventListener("popstate",a),window.addEventListener("hashchange",a);const c=l=>{const m=window.history&&window.history[l];typeof m=="function"&&(window.history[l]=function(...h){const p=m.apply(this,h);return a(),p})};c("pushState"),c("replaceState")}}};(function(){let e=!1;const i=Ae(),n=Ve({openPadInNativeViewer:i}),r=$e({isFilesAppRoute:v,onCreateInternalPublicPad:n.createInternalPublicPad,onCreateExternalPublicPad:n.createExternalPublicPad}),o=Xe({ensurePublicPadMenuRegistration:r,openPadInNativeViewer:i,schedulePublicSingleShareUiStateRefresh:Qe}),d=()=>{e||(e=!0,o.installRouteWatchers(),o.evaluateCurrentRoute(),Ce({openPadInNativeViewer:i}),Je({openPadInNativeViewer:i}))};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",d,{once:!0}):d()})(); +import{M as j,a as ne,b as ie,A as f,c as re,d as oe,n as ae,e as le,f as se}from"./oc-compat-hVqZy-MX.chunk.mjs";import{g as V,n as O,p as ce,a as T,b as B,c as de,d as M,v as _,f as k,e as ue,r as X,h as fe,i as pe,j as F,k as we,l as me}from"./api-client-BXEMiUh7.chunk.mjs";const N=()=>!!(window.OCA&&window.OCA.Viewer&&typeof window.OCA.Viewer.open=="function"),v=()=>(window.location.pathname||"").includes("/apps/files"),U=()=>{const e=window.OCP&&window.OCP.Files&&window.OCP.Files.Router;return e&&typeof e.goToRoute=="function"?e:null},ye=e=>{const i=e instanceof Error?e.message:String(e||"");return i.includes("Redirected when going")&&i.includes("navigation guard")},E=e=>{!e||typeof e.then!="function"||Promise.resolve(e).catch(i=>{ye(i)||window.console&&typeof window.console.debug=="function"&&window.console.debug("[etherpad_nextcloud] Unexpected navigation rejection",i)})},D=e=>{const i=Number(e);return Number.isFinite(i)&&i>0?i:null},z="etherpad_nextcloud_open",he=e=>{if(!e)return null;const i=[e.fileId,e.fileid,e.id,e.nodeId,e.nodeid,e.file_id];for(const n of i){const r=D(n);if(r!==null)return r}if(typeof e.get=="function")for(const n of["fileid","fileId","id"])try{const r=D(e.get(n));if(r!==null)return r}catch{}return null},ge=e=>{if(!(e instanceof Element))return null;const i=[e.getAttribute("data-fileid"),e.getAttribute("data-file-id"),e.getAttribute("data-id"),e.getAttribute("data-node-id")];for(const r of i){const o=D(r);if(o!==null)return o}const n=e.matches("a[href]")?e:e.querySelector("a[href]");return n instanceof HTMLAnchorElement?ce(n.getAttribute("href")):null},be=e=>{if(!e||typeof e!="object")return null;const i=[e,e.fileInfoModel,e.fileInfo,e.model,e.node,e.file,e.attributes];for(const r of i){const o=he(r);if(o!==null)return o}const n=[e.fileElement,e.el,e.element,e.$file&&e.$file[0],e.$el&&e.$el[0],e.target,e.currentTarget];for(const r of n){const o=ge(r);if(o!==null)return o}return null},Ce=({openPadInNativeViewer:e})=>{window.OCA&&window.OCA.Files&&window.OCA.Files.fileActions&&(window.OCA.Files.fileActions.register(j,z,ne(),ie(f,"etherpad-icon-color"),(i,n)=>{const r=n&&n.dir||V(),o=O(r,i),d=be(n);e({path:o,fileId:d})},t(f,"Open in Etherpad")),window.OCA.Files.fileActions.setDefault(j,z))},ve=800,xe=180,Ee=120,Pe=(e,i)=>{const n=U();if(!n||!e||!Number.isFinite(e))return!1;let r=!1;return E(n.goToRoute(null,{view:"files",fileid:String(e)},{dir:X(i)})),window.setTimeout(()=>{try{const o=window.OCA.Viewer.open({path:i,onClose:Ne});r=!0,E(o)}catch{}},Ee),window.setTimeout(()=>{if(r)return;const o=k(e,i);window.location.assign(o)},xe),!0},Ne=()=>{const e=U();if(!e)return;const i={...e.query||{}};delete i.openfile,delete i.editing,E(e.goToRoute(null,e.params||{},i))},Ae=()=>{let e=null,i=0;return async n=>{const r=String(n.fileId??"")+"|"+String(n.path??""),o=Date.now();if(e===r&&o-i{if(s){window.location.assign(_(d,n.path||""));return}if(v()&&n.fileId!==null&&n.fileId!==void 0&&Number.isFinite(Number(n.fileId))){const m=n.path||"/";window.location.assign(k(Number(n.fileId),m));return}const l=v()?M():null;if((n.fileId===null||n.fileId===void 0)&&l){const m=n.path||"/";window.location.assign(k(l,m));return}if(n.fileId!==null&&n.fileId!==void 0&&Number.isFinite(Number(n.fileId))){const m=n.path||"/";window.location.assign(k(Number(n.fileId),m));return}n.path&&window.location.assign(ue(n.path))};if(!N()){u();return}let a=n.path||"",c=n.fileId??null;if(!a&&n.fileId!==null&&n.fileId!==void 0)try{const l=await B(n.fileId);a=l&&typeof l.path=="string"?l.path:"",c=l&&Number.isFinite(Number(l.file_id))?Number(l.file_id):c}catch{a=""}if(!a){u();return}if((!c||!Number.isFinite(Number(c)))&&a&&!s)try{const l=await de(a);c=l&&Number.isFinite(Number(l.file_id))?Number(l.file_id):c}catch{}if((!c||!Number.isFinite(Number(c)))&&v()){const l=M();l&&(c=l)}if(v()){if(c&&Number.isFinite(Number(c))&&Pe(Number(c),a))return;u();return}try{const l=window.OCA.Viewer.open({path:a});E(l)}catch{u()}}},Ie=120,Oe=900,Fe=async(e,i={})=>{if(N()&&e.path)try{const n=Number(e.fileId);v()&&Number.isFinite(n)&&n>0&&(qe(n,e.path,i.resolveOpenDir),await Se()),await Re(e.path);const r=window.OCA.Viewer.open({path:e.path,onClose:Me});E(r);return}catch{}typeof i.fallbackOpen=="function"&&await i.fallbackOpen(e)},Se=()=>new Promise(e=>{window.setTimeout(e,Ie)}),Re=async e=>{const i=re(e);i!==""&&(oe("editor:file:created",i),await Le())},Le=()=>new Promise(e=>{window.setTimeout(e,Oe)}),qe=(e,i,n)=>{const r=U();if(!r)return;const o={...r.query||{}};delete o.openfile,delete o.editing,o.dir=ke(i,n),E(r.goToRoute(null,{...r.params||{},view:r.params&&r.params.view?r.params.view:"files",fileid:String(e)},o,!0))},ke=(e,i)=>{if(typeof i=="function")return i(e);const n=String(e||""),r=n.lastIndexOf("/");return r>0?n.substring(0,r):"/"},Me=()=>{const e=U();if(!e)return;const i={...e.query||{}};delete i.openfile,delete i.editing,E(e.goToRoute(null,e.params||{},i))},_e=e=>{try{const i=new URL(e,window.location.origin),n=decodeURIComponent(i.pathname||"").match(/\/p\/([^/]+)$/);if(n&&n[1]){const r=n[1].replace(/[^a-zA-Z0-9._-]+/g,"-").replace(/^-+|-+$/g,"");if(r!=="")return r+".pad"}}catch{}return"Imported pad.pad"},Y=e=>{const i=document.createElement("div");i.style.position="fixed",i.style.inset="0",i.style.background="rgba(0, 0, 0, 0.45)",i.style.display="flex",i.style.alignItems="center",i.style.justifyContent="center",i.style.zIndex="20000";const n=document.createElement("div");n.style.position="relative",n.style.background="var(--color-main-background, #fff)",n.style.borderRadius="10px",n.style.boxShadow="0 10px 30px rgba(0, 0, 0, 0.25)",n.style.padding="18px",n.style.width="min(460px, calc(100vw - 24px))";const r=document.createElement("button");r.type="button",r.setAttribute("aria-label",t(f,"Close")),r.textContent="×",r.style.position="absolute",r.style.top="8px",r.style.right="10px",r.style.border="none",r.style.background="transparent",r.style.fontSize="22px",r.style.cursor="pointer",r.style.lineHeight="1";const o=document.createElement("h3");return o.textContent=e,o.style.margin="0 26px 10px 0",o.style.fontSize="18px",n.appendChild(r),n.appendChild(o),i.appendChild(n),document.body.appendChild(i),{overlay:i,dialog:n,closeButton:r}},Te=({onSubmit:e})=>new Promise(i=>{const{overlay:n,dialog:r,closeButton:o}=Y(t(f,"Public pad")),d=document.createElement("label");d.textContent=t(f,"File name"),d.style.display="block",d.style.marginBottom="6px";const s=document.createElement("input");s.type="text",s.value=t(f,"Public pad")+".pad",s.style.width="100%",s.style.boxSizing="border-box",s.style.marginBottom="12px";const u=document.createElement("p");u.style.color="var(--color-error, #c62828)",u.style.margin="0 0 12px 0",u.style.minHeight="20px";const a=document.createElement("button");a.type="button",a.className="primary",a.textContent=t(f,"Create");let c=!1;const l=p=>{c||(n.remove(),i(p))};o.addEventListener("click",()=>l(null)),n.addEventListener("click",p=>{p.target===n&&l(null)});const m=p=>{c=p,a.disabled=p,s.disabled=p,o.disabled=p,a.textContent=p?t(f,"Creating..."):t(f,"Create")},h=async()=>{const p=s.value.trim();if(p===""){u.textContent=t(f,"File name is required."),s.focus();return}u.textContent="",m(!0);try{const b=await e(p);m(!1),l(b)}catch(b){m(!1),u.textContent=b instanceof Error&&b.message!==""?b.message:t(f,"Could not create public pad."),s.focus(),s.select()}};s.addEventListener("keydown",p=>{p.key==="Enter"&&(p.preventDefault(),h())}),a.addEventListener("click",()=>{h()}),r.appendChild(d),r.appendChild(s),r.appendChild(u),r.appendChild(a),s.focus(),s.select()}),Ue=({onSubmit:e})=>new Promise(i=>{const{overlay:n,dialog:r,closeButton:o}=Y(t(f,"Public pad from URL")),d=document.createElement("label");d.textContent=t(f,"External pad URL"),d.style.display="block",d.style.marginBottom="6px";const s=document.createElement("input");s.type="url",s.value="https://",s.placeholder="https://",s.style.width="100%",s.style.boxSizing="border-box",s.style.marginBottom="12px";const u=document.createElement("label");u.textContent=t(f,"File name"),u.style.display="block",u.style.marginBottom="6px";const a=document.createElement("input");a.type="text",a.value="Imported pad.pad",a.style.width="100%",a.style.boxSizing="border-box",a.style.marginBottom="12px";const c=document.createElement("p");c.style.color="var(--color-error, #c62828)",c.style.margin="0 0 12px 0",c.style.minHeight="20px";const l=document.createElement("button");l.type="button",l.className="primary",l.textContent=t(f,"Create");let m=!1;const h=w=>{m||(n.remove(),i(w))};o.addEventListener("click",()=>h(null)),n.addEventListener("click",w=>{w.target===n&&h(null)}),s.addEventListener("blur",()=>{const w=s.value.trim();w.startsWith("http")&&(a.value=_e(w))});const p=w=>{m=w,l.disabled=w,s.disabled=w,a.disabled=w,o.disabled=w,l.textContent=w?t(f,"Creating..."):t(f,"Create")},b=async()=>{const w=s.value.trim(),S=a.value.trim();if(w===""){c.textContent=t(f,"External pad URL is required."),s.focus();return}if(S===""){c.textContent=t(f,"File name is required."),a.focus();return}c.textContent="",p(!0);try{const y=await e({padUrl:w,name:S});p(!1),h(y)}catch(y){p(!1),c.textContent=y instanceof Error&&y.message!==""?y.message:t(f,"Could not import public pad URL."),a.focus(),a.select()}};s.addEventListener("keydown",w=>{w.key==="Enter"&&(w.preventDefault(),b())}),a.addEventListener("keydown",w=>{w.key==="Enter"&&(w.preventDefault(),b())}),l.addEventListener("click",()=>{b()}),r.appendChild(d),r.appendChild(s),r.appendChild(u),r.appendChild(a),r.appendChild(c),r.appendChild(l),s.focus(),s.select()}),W=e=>F(e)?e:e+".pad",He=(e,i)=>({path:e&&typeof e.file=="string"?e.file:i,fileId:e&&Number.isFinite(Number(e.file_id))?Number(e.file_id):null}),Ve=({openPadInNativeViewer:e})=>{const i=(d,s)=>Fe(He(d,s),{fallbackOpen:e,resolveOpenDir:X}),n=async()=>{await Te({onSubmit:async d=>{const s=O(V(),W(d.trim())),u=await fe(s);return await i(u,s),u}})},r=async()=>{await Ue({onSubmit:async({padUrl:d,name:s})=>{const u=d.trim(),a=O(V(),W(s.trim())),c=await pe(a,u);return o(c),await i(c,a),c}})},o=d=>{if(!d||d.snapshot_warning_code!=="remote_export_unavailable")return;const s=t(f,"Could not fetch the pad content from the remote server. The new file is empty for now — open the pad in the viewer to populate it."),u=window.OC;u&&u.Notification&&typeof u.Notification.showTemporary=="function"?u.Notification.showTemporary(s,{type:"warning"}):window.console.warn("[etherpad_nextcloud] "+s)};return{createInternalPublicPad:n,createExternalPublicPad:r}},$=f+"_public_pad",K=f+"_public_pad_external",R="icon-filetype-etherpad-nextcloud-pad",L='',J=98,Z=99,Be=120,De=500,q=()=>{const e=ae();return e!==null&&e>=33},I=e=>(e&&e.message?String(e.message):"").toLowerCase().includes("duplicate"),je=e=>{if(!e||typeof e!="object")return null;if(e.hasCreatePermission===!1)return!1;if(e.hasCreatePermission===!0)return!0;if(typeof e.canCreate=="boolean")return e.canCreate;const i=le(),n=[e.permissions,e.permission,e.attributes&&e.attributes.permissions,typeof e.get=="function"?e.get("permissions"):null];for(const r of n){if(r==null)continue;const o=Number(r);if(Number.isFinite(o))return(o&i)===i}return null},ze=(...e)=>{for(const i of e){const n=je(i);if(n!==null)return n}return!0},We=()=>{const e=[window.OCP&&window.OCP.Files,window.OCA&&window.OCA.Files];for(const n of e)if(!(!n||typeof n!="object")){if(typeof n.addNewFileMenuEntry=="function")return{register:n.addNewFileMenuEntry.bind(n),unregister:typeof n.removeNewFileMenuEntry=="function"?n.removeNewFileMenuEntry.bind(n):null};if(typeof n.getNewFileMenu=="function"){let r=null;try{r=n.getNewFileMenu()}catch{r=null}if(r&&typeof r.registerEntry=="function")return{register:r.registerEntry.bind(r),unregister:typeof r.unregisterEntry=="function"?r.unregisterEntry.bind(r):null}}}const i=window._nc_newfilemenu;return i&&typeof i=="object"&&typeof i.registerEntry=="function"?{register:i.registerEntry.bind(i),unregister:typeof i.unregisterEntry=="function"?i.unregisterEntry.bind(i):null}:null},$e=({isFilesAppRoute:e,onCreateInternalPublicPad:i,onCreateExternalPublicPad:n})=>{let r=!1,o=!1,d=!1,s=0;const u=()=>{i()},a=()=>{n()},c=(...y)=>ze(...y),l=()=>({id:$,displayName:t(f,"Public pad"),...q()?{iconSvgInline:L}:{iconClass:R},order:J,enabled:c,handler:u}),m=()=>({id:K,displayName:t(f,"Public pad from URL"),...q()?{iconSvgInline:L}:{iconClass:R},order:Z,enabled:c,handler:a}),h=()=>({id:$,displayName:t(f,"Public pad"),iconClass:R,...q()?{iconSvgInline:L}:{},fileType:"file",order:J,actionHandler:u}),p=()=>({id:K,displayName:t(f,"Public pad from URL"),iconClass:R,...q()?{iconSvgInline:L}:{},fileType:"file",order:Z,actionHandler:a}),b=async()=>{const y=We();if(!y)return!1;for(const C of[l(),m()])try{y.register(C)}catch(g){if(!I(g))return!1}return r=!0,!0},w=()=>{const y=[window.OCA&&window.OCA.Files&&window.OCA.Files.NewFileMenu,window.OCA&&window.OCA.Files&&window.OCA.Files.newFileMenu],C=[{legacy:h(),modern:l()},{legacy:p(),modern:m()}];for(const g of y)if(!(!g||typeof g!="object")){if(typeof g.addMenuEntry=="function"){let x=!0;for(const{legacy:A}of C)try{g.addMenuEntry(A)}catch(H){I(H)||(x=!1)}if(x)return!0}if(typeof g.registerEntry=="function"){let x=!0;for(const{modern:A}of C)try{g.registerEntry(A)}catch(H){I(H)||(x=!1)}if(x)return!0}}return!1},S=()=>{if(o)return!0;try{if(w())return o=!0,!0;if(!d&&window.OC&&window.OC.Plugins&&typeof window.OC.Plugins.register=="function"){const y=h(),C=p();window.OC.Plugins.register("OCA.Files.NewFileMenu",{attach(g){if(!(!g||typeof g.addMenuEntry!="function")){for(const x of[y,C])try{g.addMenuEntry(x)}catch(A){if(!I(A))return}o=!0}}}),d=!0}return o}catch(y){return I(y)?(o=!0,!0):!1}};return()=>{if(!e()||r||o)return;const y=++s,C=async g=>{y!==s||r||o||await b()||S()||g{C(g+1)},De)};C(0)}},Ke=(e,i)=>{if(!(e instanceof HTMLAnchorElement)||!i)return null;const n=e.getAttribute("href")||"",r=we(n);if(r&&r.token===i&&F(r.path))return r.path;const o=me(n);return F(o)?o:null},Je=({openPadInNativeViewer:e})=>{if(window.OCA&&window.OCA.EtherpadNextcloudClickInterceptorRegistered===!0)return;window.OCA&&(window.OCA.EtherpadNextcloudClickInterceptorRegistered=!0);const i=async n=>{if(!n||n.defaultPrevented)return;const r=T();if(!r||n.type==="click"&&(n.button!==0||n.metaKey||n.ctrlKey||n.shiftKey||n.altKey)||!(n.target instanceof Element))return;const o=n.target.closest("a[href]");if(!(o instanceof HTMLAnchorElement))return;const d=Ke(o,r);if(d){if(n.preventDefault(),n.stopPropagation(),typeof n.stopImmediatePropagation=="function"&&n.stopImmediatePropagation(),N()){await e({path:d,fileId:null});return}window.location.assign(_(r,d))}};document.addEventListener("click",i,!0)},P="data-epnc-single-file-share";let G=0;const ee=e=>{if(!e)return null;const i='a[href*="/s/'+e+'/download"],a[href*="/index.php/s/'+e+'/download"]',n=document.querySelector(i);return n instanceof HTMLAnchorElement?n:null},Ze=()=>{const e=document.getElementById("initial-state-files_sharing-view");if(!(e instanceof HTMLInputElement))return"";const i=String(e.value||"").trim();if(i==="")return"";try{const n=window.atob(i);if(n.startsWith('"')||n.startsWith("{")||n.startsWith("[")){const r=JSON.parse(n);return typeof r=="string"?r:""}return n}catch{return""}},te=e=>{if(!e)return!1;const i=ee(e);if(!i)return!1;let n;try{n=new URL(i.getAttribute("href")||"",window.location.origin)}catch{return!1}const r=(n.pathname||"").match(/(?:\/index\.php)?\/s\/([^/]+)\/download\/?$/);return!r||r[1]!==e?!1:!n.searchParams.has("files")},Ge=()=>{const e=document.body;if(!(e instanceof HTMLElement))return{retry:!0};const i=T();if(!i)return e.removeAttribute(P),{retry:!1};const n=Ze();return n==="public-file-share"?(e.setAttribute(P,"1"),{retry:!1}):n==="public-share"?(e.removeAttribute(P),{retry:!1}):ee(i)?te(i)?(e.setAttribute(P,"1"),{retry:!1}):(e.removeAttribute(P),{retry:!1}):(e.removeAttribute(P),{retry:!0})},Qe=()=>{const e=++G,i=n=>{e===G&&Ge().retry&&n<30&&window.setTimeout(()=>i(n+1),180)};i(0)},Q="EtherpadNextcloudRouteWatchInstalled",Xe=({ensurePublicPadMenuRegistration:e,openPadInNativeViewer:i,schedulePublicSingleShareUiStateRefresh:n})=>{let r="";const o=async()=>{if(N())return;const a=M();if(!a)return;const c=new URLSearchParams(window.location.search||"");if(!(c.get("epNoRedirect")==="1"||c.get("details")==="1"||c.get("opendetails")==="true")&&!(window.location.pathname||"").includes("/apps/"+f+"/"))try{const l=await B(a);l&&l.is_pad&&l.viewer_url&&window.location.assign(l.viewer_url)}catch{}},d=async()=>{if(N()||!v())return;const a=M();if(!a)return;const c=new URLSearchParams(window.location.search||"");if(!(c.get("openfile")==="true"||c.get("opendetails")==="true"||c.get("details")==="1"))try{const l=await B(a);if(!l||!l.is_pad)return;const m=c.get("dir")||"/",h=se("/apps/files/files")+"?dir="+encodeURIComponent(m);window.location.href!==h&&window.location.assign(h)}catch{}},s=()=>{const a=T();if(!a)return;const c=new URLSearchParams(window.location.search||""),l=c.get("files")||"";if(N()){if(!F(l))return;const p=c.get("path")||"/",b=O(p,l);i({path:b,fileId:null});return}if(!F(l)){if((window.location.pathname||"").includes("/apps/"+f+"/public/"))return;te(a)&&window.location.assign(_(a,""));return}const m=c.get("path")||"/",h=O(m,l);window.location.assign(_(a,h))},u=()=>{const a=(window.location.pathname||"")+"?"+(window.location.search||"");a!==r&&(r=a,v()&&e(),n(),d(),o(),s())};return{evaluateCurrentRoute:u,installRouteWatchers:()=>{if(window.OCA&&window.OCA[Q]===!0)return;window.OCA&&(window.OCA[Q]=!0);const a=()=>window.setTimeout(u,0);window.addEventListener("popstate",a),window.addEventListener("hashchange",a);const c=l=>{const m=window.history&&window.history[l];typeof m=="function"&&(window.history[l]=function(...h){const p=m.apply(this,h);return a(),p})};c("pushState"),c("replaceState")}}};(function(){let e=!1;const i=Ae(),n=Ve({openPadInNativeViewer:i}),r=$e({isFilesAppRoute:v,onCreateInternalPublicPad:n.createInternalPublicPad,onCreateExternalPublicPad:n.createExternalPublicPad}),o=Xe({ensurePublicPadMenuRegistration:r,openPadInNativeViewer:i,schedulePublicSingleShareUiStateRefresh:Qe}),d=()=>{e||(e=!0,o.installRouteWatchers(),o.evaluateCurrentRoute(),Ce({openPadInNativeViewer:i}),Je({openPadInNativeViewer:i}))};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",d,{once:!0}):d()})(); //# sourceMappingURL=etherpad_nextcloud-files-main.mjs.map diff --git a/js/etherpad_nextcloud-viewer-main.mjs b/js/etherpad_nextcloud-viewer-main.mjs index 23606d3..c9e7cac 100644 --- a/js/etherpad_nextcloud-viewer-main.mjs +++ b/js/etherpad_nextcloud-viewer-main.mjs @@ -1,2 +1,2 @@ -import{V as S,M as L,t as a,f as u,A as y,o as v}from"./oc-compat-DlZkDG_8.chunk.mjs";import{m as O,o as P,a as b,l as _}from"./api-client-CT0tqdIR.chunk.mjs";const I=p=>String(p||"").replace(/&/g,"&").replace(/"/g,""").replace(//g,">"),x=["http:","https:"],H=p=>{try{const m=new URL(String(p||""));return x.includes(m.protocol)&&m.username===""&&m.password===""}catch{return!1}},U="default-src 'none'; frame-src http: https:; style-src 'unsafe-inline'",k=p=>{const m=H(p)?I(p):"";return''};(function(){let p=0;const m={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,syncUrl:"",syncIntervalMs:12e4,syncInFlight:!1,syncTimerId:null,visibilityHandler:null,pageHideHandler:null}},computed:{sourcePath(){const e=typeof this.source=="string"?this.source.trim():"";return e&&_(e)||""},filePath(){const e=t=>String(t||"").trim().replace(/\s+\.pad$/i,".pad"),i=t=>{const o=String(t||"").trim();return!o||o==="/"?"/":o.startsWith("/")?o:"/"+o},n=(t,o)=>{if(!o)return"";if(o.startsWith("/"))return o;const h=i(t);return h==="/"?"/"+o:h+"/"+o},r=t=>typeof t=="string"&&t.toLowerCase().endsWith(".pad");if(r(this.sourcePath))return this.sourcePath;const s=this.fileInfo&&typeof this.fileInfo=="object"?this.fileInfo:null,c=s&&typeof s.path=="string"?e(s.path):"";if(r(c))return c.startsWith("/")?c:"/"+c;const d=e(this.filename||this.basename||s&&(s.name||s.basename)||"");if(!d)return"";const f=s&&typeof s.dirname=="string"?s.dirname:"";if(f){const t=n(f,d);if(r(t))return t}const g=new URLSearchParams(window.location.search||"").get("dir")||"/",l=n(g,d);return r(l)?l:"/"+d},resolvedFileId(){const e=[this.fileid,this.fileId,this.fileInfo&&(this.fileInfo.fileid||this.fileInfo.fileId||this.fileInfo.id)];for(const r of e){const s=Number(r);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||{}),r=await fetch(e,Object.assign({method:"GET",credentials:"same-origin",headers:n},i)),s=await r.json().catch(()=>({}));if(!r.ok){const c=new Error(s&&s.message||"Pad open failed.");throw s&&typeof s.code=="string"&&(c.code=s.code),c}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:v()};if(this.resolvedFileId!==null){const c=u("/apps/"+y+"/api/v1/pads/initialize-by-id/"+encodeURIComponent(String(this.resolvedFileId))),d=await fetch(c,{method:"POST",credentials:"same-origin",headers:e}),f=await d.json().catch(()=>({}));if(!d.ok)throw new Error(f&&f.message||"Pad initialization failed.");return f}if(!this.filePath)throw new Error("Pad initialization failed: missing file path.");const i=new URLSearchParams;i.set("file",this.filePath);const n=u("/apps/"+y+"/api/v1/pads/initialize"),r=await fetch(n,{method:"POST",credentials:"same-origin",headers:Object.assign({},e,{"Content-Type":"application/x-www-form-urlencoded;charset=UTF-8"}),body:i.toString()}),s=await r.json().catch(()=>({}));if(!r.ok)throw new Error(s&&s.message||"Pad initialization failed.");return s},markLoaded(){this.$emit("update:loaded",!0)},stopSyncLoop(){this.syncTimerId!==null&&(window.clearInterval(this.syncTimerId),this.syncTimerId=null)},startSyncLoop(){!this.syncUrl||this.syncTimerId!==null||(this.syncTimerId=window.setInterval(()=>{document.visibilityState==="visible"&&this.runSync(!1,!1)},this.syncIntervalMs))},async runSync(e,i){if(this.syncUrl&&!(this.syncInFlight&&!e)){this.syncInFlight=!0;try{const n=e?this.syncUrl+(this.syncUrl.includes("?")?"&":"?")+"force=1":this.syncUrl;await fetch(n,{method:"POST",credentials:"same-origin",headers:{Accept:"application/json",requesttoken:v()},keepalive:!!i})}finally{this.syncInFlight=!1}}},installSyncLifecycleHandlers(){this.visibilityHandler||this.pageHideHandler||(this.visibilityHandler=()=>{if(document.visibilityState==="hidden"){this.runSync(!0,!0),this.stopSyncLoop();return}this.startSyncLoop()},this.pageHideHandler=()=>{this.runSync(!0,!0),this.stopSyncLoop()},document.addEventListener("visibilitychange",this.visibilityHandler),window.addEventListener("pagehide",this.pageHideHandler))},removeSyncLifecycleHandlers(){this.visibilityHandler&&(document.removeEventListener("visibilitychange",this.visibilityHandler),this.visibilityHandler=null),this.pageHideHandler&&(window.removeEventListener("pagehide",this.pageHideHandler),this.pageHideHandler=null)},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.syncUrl="",this.syncInFlight=!1,this.stopSyncLoop(),!this.filePath){if(!i())return;this.loadError="No .pad file selected.",this.isLoading=!1;return}const n=b(),r=u("/apps/"+y+"/api/v1/pads/open"),s=(()=>{if(!n)return"";const l=new URL(u("/apps/"+y+"/api/v1/public/open/"+encodeURIComponent(n)),window.location.origin);return l.searchParams.set("file",this.filePath),l.toString()})(),c=this.resolvedFileId!==null?u("/apps/"+y+"/api/v1/pads/open-by-id"):"",d=new URLSearchParams;d.set("file",this.filePath);const f=new URLSearchParams;this.resolvedFileId!==null&&f.set("fileId",String(this.resolvedFileId));const g={"Content-Type":"application/x-www-form-urlencoded;charset=UTF-8",requesttoken:v()};try{const l=async()=>{let h=null;if(s)h=await this.fetchOpenPayload(s);else if(c)try{h=await this.fetchOpenPayload(c,{method:"POST",headers:g,body:f.toString()})}catch{}return h||(h=await this.fetchOpenPayload(r,{method:"POST",headers:g,body:d.toString()})),h};let t;try{if(t=await l(),!i())return}catch(h){if(!this.isMissingFrontmatterError(h))throw h;if(await this.initializeMissingFrontmatter(),!i()||(t=await l(),!i()))return}this.syncUrl=t&&typeof t.sync_url=="string"?t.sync_url:"";const o=Number(t&&t.sync_interval_seconds);if(this.syncIntervalMs=Number.isFinite(o)&&o>0?Math.max(5e3,Math.min(36e5,o*1e3)):12e4,this.installSyncLifecycleHandlers(),this.syncUrl&&this.startSyncLoop(),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 h=t.url.trim();this.externalOpenUrl=h,this.externalOpenMessage=a("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(l){if(!i())return;this.loadError=l instanceof Error?l.message:"Could not load pad.",this.canRecover=!!(l&&l.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 P(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 O(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||""),r=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"},r.trim()!==""?r:i.emptyMessage)])])}},beforeDestroy(){this.resolveGeneration+=1,this.runSync(!0,!0),this.stopSyncLoop(),this.removeSyncLifecycleHandlers()},beforeUnmount(){this.resolveGeneration+=1,this.runSync(!0,!0),this.stopSyncLoop(),this.removeSyncLifecycleHandlers()},render(e){if(this.loadError){const i=[e("div",{class:"epnc-native-error-title"},a("Unable to 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"},a("Checking for the original pad..."))):this.originalPad?i.push(e("div",{class:"epnc-native-error-message"},a("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}},a("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?a("Creating new pad..."):a("Create new pad from this file"))):i.push(e("div",{class:"epnc-native-error-message"},a("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?a("Creating new pad..."):a("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:a("Pad from another server"),message:this.externalOpenMessage,actions:[e("a",{class:"button primary",attrs:{href:this.externalOpenUrl,target:"_blank",rel:"noopener noreferrer"}},a("Open original pad"))],html:this.snapshot.html,text:this.snapshot.text,emptyMessage:a("No synced snapshot is stored in this .pad file yet.")}):this.snapshotMode==="readonly"?this.renderSnapshotView(e,{title:a("Read-only snapshot"),message:a("Read-only snapshot from the .pad file."),html:this.snapshot.html,text:this.snapshot.text,emptyMessage:a("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:k(this.iframeSrc),title:"Etherpad"},on:{load:()=>this.markLoaded(),error:()=>this.markLoaded()},class:"epnc-native-iframe"})])}},w=()=>{if(p+=1,!(window.OCA&&window.OCA.Viewer&&typeof window.OCA.Viewer.registerHandler=="function")){p<20&&window.setTimeout(w,500);return}Array.isArray(window.OCA.Viewer.availableHandlers)&&window.OCA.Viewer.availableHandlers.some(e=>e&&e.id===S)||window.OCA.Viewer.registerHandler({id:S,mimes:[L],component:m})};w()})(); +import{V as S,M as L,t as l,f as y,A as g,o as v}from"./oc-compat-hVqZy-MX.chunk.mjs";import{m as _,o as O,a as b,l as P}from"./api-client-BXEMiUh7.chunk.mjs";const I=f=>String(f||"").replace(/&/g,"&").replace(/"/g,""").replace(//g,">"),x=["http:","https:"],H=f=>{try{const u=new URL(String(f||""));return x.includes(u.protocol)&&u.username===""&&u.password===""}catch{return!1}},U="default-src 'none'; frame-src http: https:; style-src 'unsafe-inline'",k=f=>{const u=H(f)?I(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,syncUrl:"",syncIntervalMs:12e4,syncInFlight:!1,syncTimerId:null,visibilityHandler:null,pageHideHandler:null}},computed:{sourcePath(){const e=typeof this.source=="string"?this.source.trim():"";return e&&P(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 d=i(t);return d==="/"?"/"+h:d+"/"+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,c=s&&typeof s.path=="string"?e(s.path):"";if(a(c))return c.startsWith("/")?c:"/"+c;const p=e(this.filename||this.basename||s&&(s.name||s.basename)||"");if(!p)return"";const o=s&&typeof s.dirname=="string"?s.dirname:"";if(o){const t=n(o,p);if(a(t))return t}const m=new URLSearchParams(window.location.search||"").get("dir")||"/",r=n(m,p);return a(r)?r:"/"+p},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 c=new Error(s&&s.message||"Pad open failed.");throw s&&typeof s.code=="string"&&(c.code=s.code),c}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:v()},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/"+g+"/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/"+g+"/api/v1/pads/initialize"),c=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()}),p=await c.json().catch(()=>({}));if(!c.ok)throw i(p,"Pad initialization failed.");return n(p),p},markLoaded(){this.$emit("update:loaded",!0)},stopSyncLoop(){this.syncTimerId!==null&&(window.clearInterval(this.syncTimerId),this.syncTimerId=null)},startSyncLoop(){!this.syncUrl||this.syncTimerId!==null||(this.syncTimerId=window.setInterval(()=>{document.visibilityState==="visible"&&this.runSync(!1,!1)},this.syncIntervalMs))},async runSync(e,i){if(this.syncUrl&&!(this.syncInFlight&&!e)){this.syncInFlight=!0;try{const n=e?this.syncUrl+(this.syncUrl.includes("?")?"&":"?")+"force=1":this.syncUrl;await fetch(n,{method:"POST",credentials:"same-origin",headers:{Accept:"application/json",requesttoken:v()},keepalive:!!i})}finally{this.syncInFlight=!1}}},installSyncLifecycleHandlers(){this.visibilityHandler||this.pageHideHandler||(this.visibilityHandler=()=>{if(document.visibilityState==="hidden"){this.runSync(!0,!0),this.stopSyncLoop();return}this.startSyncLoop()},this.pageHideHandler=()=>{this.runSync(!0,!0),this.stopSyncLoop()},document.addEventListener("visibilitychange",this.visibilityHandler),window.addEventListener("pagehide",this.pageHideHandler))},removeSyncLifecycleHandlers(){this.visibilityHandler&&(document.removeEventListener("visibilitychange",this.visibilityHandler),this.visibilityHandler=null),this.pageHideHandler&&(window.removeEventListener("pagehide",this.pageHideHandler),this.pageHideHandler=null)},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.syncUrl="",this.syncInFlight=!1,this.stopSyncLoop(),!this.filePath){if(!i())return;this.loadError="No .pad file selected.",this.isLoading=!1;return}const n=b(),a=y("/apps/"+g+"/api/v1/pads/open"),s=(()=>{if(!n)return"";const r=new URL(y("/apps/"+g+"/api/v1/public/open/"+encodeURIComponent(n)),window.location.origin);return r.searchParams.set("file",this.filePath),r.toString()})(),c=this.resolvedFileId!==null?y("/apps/"+g+"/api/v1/pads/open-by-id"):"",p=new URLSearchParams;p.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:v()};try{const r=async()=>{let d=null;if(s)d=await this.fetchOpenPayload(s);else if(c)try{d=await this.fetchOpenPayload(c,{method:"POST",headers:m,body:o.toString()})}catch{}return d||(d=await this.fetchOpenPayload(a,{method:"POST",headers:m,body:p.toString()})),d};let t;try{if(t=await r(),!i())return}catch(d){if(!this.isMissingFrontmatterError(d))throw d;if(await this.initializeMissingFrontmatter(),!i()||(t=await r(),!i()))return}this.syncUrl=t&&typeof t.sync_url=="string"?t.sync_url:"";const h=Number(t&&t.sync_interval_seconds);if(this.syncIntervalMs=Number.isFinite(h)&&h>0?Math.max(5e3,Math.min(36e5,h*1e3)):12e4,this.installSyncLifecycleHandlers(),this.syncUrl&&this.startSyncLoop(),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 d=t.url.trim();this.externalOpenUrl=d,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 O(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 _(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.runSync(!0,!0),this.stopSyncLoop(),this.removeSyncLifecycleHandlers()},beforeUnmount(){this.resolveGeneration+=1,this.runSync(!0,!0),this.stopSyncLoop(),this.removeSyncLifecycleHandlers()},render(e){if(this.loadError){const i=[e("div",{class:"epnc-native-error-title"},l("Unable to 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:k(this.iframeSrc),title:"Etherpad"},on:{load:()=>this.markLoaded(),error:()=>this.markLoaded()},class:"epnc-native-iframe"})])}},w=()=>{if(f+=1,!(window.OCA&&window.OCA.Viewer&&typeof window.OCA.Viewer.registerHandler=="function")){f<20&&window.setTimeout(w,500);return}Array.isArray(window.OCA.Viewer.availableHandlers)&&window.OCA.Viewer.availableHandlers.some(e=>e&&e.id===S)||window.OCA.Viewer.registerHandler({id:S,mimes:[L],component:u})};w()})(); //# 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 7a3af92..44ea833 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 { 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\tsyncUrl: '',\n\t\t\t\tsyncIntervalMs: 120000,\n\t\t\t\tsyncInFlight: false,\n\t\t\t\tsyncTimerId: null,\n\t\t\t\tvisibilityHandler: null,\n\t\t\t\tpageHideHandler: null,\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\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 new Error((data && data.message) || 'Pad initialization failed.')\n\t\t\t\t\t}\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 new Error((data && data.message) || 'Pad initialization failed.')\n\t\t\t\t}\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\tstopSyncLoop() {\n\t\t\t\tif (this.syncTimerId !== null) {\n\t\t\t\t\twindow.clearInterval(this.syncTimerId)\n\t\t\t\t\tthis.syncTimerId = null\n\t\t\t\t}\n\t\t\t},\n\t\t\tstartSyncLoop() {\n\t\t\t\tif (!this.syncUrl || this.syncTimerId !== null) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tthis.syncTimerId = window.setInterval(() => {\n\t\t\t\t\tif (document.visibilityState === 'visible') {\n\t\t\t\t\t\tvoid this.runSync(false, false)\n\t\t\t\t\t}\n\t\t\t\t}, this.syncIntervalMs)\n\t\t\t},\n\t\t\tasync runSync(force, keepalive) {\n\t\t\t\tif (!this.syncUrl) return\n\t\t\t\tif (this.syncInFlight && !force) return\n\t\t\t\tthis.syncInFlight = true\n\t\t\t\ttry {\n\t\t\t\t\tconst url = force ? (this.syncUrl + (this.syncUrl.includes('?') ? '&' : '?') + 'force=1') : this.syncUrl\n\t\t\t\t\tawait fetch(url, {\n\t\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\t\tcredentials: 'same-origin',\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\tAccept: 'application/json',\n\t\t\t\t\t\t\trequesttoken: ocRequestToken(),\n\t\t\t\t\t\t},\n\t\t\t\t\t\tkeepalive: Boolean(keepalive),\n\t\t\t\t\t})\n\t\t\t\t} finally {\n\t\t\t\t\tthis.syncInFlight = false\n\t\t\t\t}\n\t\t\t},\n\t\t\tinstallSyncLifecycleHandlers() {\n\t\t\t\tif (this.visibilityHandler || this.pageHideHandler) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tthis.visibilityHandler = () => {\n\t\t\t\t\tif (document.visibilityState === 'hidden') {\n\t\t\t\t\t\tvoid this.runSync(true, true)\n\t\t\t\t\t\tthis.stopSyncLoop()\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tthis.startSyncLoop()\n\t\t\t\t}\n\t\t\t\tthis.pageHideHandler = () => {\n\t\t\t\t\tvoid this.runSync(true, true)\n\t\t\t\t\tthis.stopSyncLoop()\n\t\t\t\t}\n\t\t\t\tdocument.addEventListener('visibilitychange', this.visibilityHandler)\n\t\t\t\twindow.addEventListener('pagehide', this.pageHideHandler)\n\t\t\t},\n\t\t\tremoveSyncLifecycleHandlers() {\n\t\t\t\tif (this.visibilityHandler) {\n\t\t\t\t\tdocument.removeEventListener('visibilitychange', this.visibilityHandler)\n\t\t\t\t\tthis.visibilityHandler = null\n\t\t\t\t}\n\t\t\t\tif (this.pageHideHandler) {\n\t\t\t\t\twindow.removeEventListener('pagehide', this.pageHideHandler)\n\t\t\t\t\tthis.pageHideHandler = null\n\t\t\t\t}\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\tthis.syncUrl = ''\n\t\t\t\tthis.syncInFlight = false\n\t\t\t\tthis.stopSyncLoop()\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\tthis.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\tthis.syncIntervalMs = (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.installSyncLifecycleHandlers()\n\t\t\t\t\tif (this.syncUrl) {\n\t\t\t\t\t\tthis.startSyncLoop()\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\tvoid this.runSync(true, true)\n\t\t\tthis.stopSyncLoop()\n\t\t\tthis.removeSyncLifecycleHandlers()\n\t\t},\n\t\tbeforeUnmount() {\n\t\t\tthis.resolveGeneration += 1\n\t\t\tvoid this.runSync(true, true)\n\t\t\tthis.stopSyncLoop()\n\t\t\tthis.removeSyncLifecycleHandlers()\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('Unable to 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","ocGenerateUrl","APP_ID","body","force","keepalive","generation","isCurrent","publicToken","parsePublicShareTokenFromLocation","byPathUrl","byPublicUrl","byIdUrl","byPathBody","byIdBody","openPostHeaders","fetchOpenData","intervalSeconds","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 { 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\tsyncUrl: '',\n\t\t\t\tsyncIntervalMs: 120000,\n\t\t\t\tsyncInFlight: false,\n\t\t\t\tsyncTimerId: null,\n\t\t\t\tvisibilityHandler: null,\n\t\t\t\tpageHideHandler: null,\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\tstopSyncLoop() {\n\t\t\t\tif (this.syncTimerId !== null) {\n\t\t\t\t\twindow.clearInterval(this.syncTimerId)\n\t\t\t\t\tthis.syncTimerId = null\n\t\t\t\t}\n\t\t\t},\n\t\t\tstartSyncLoop() {\n\t\t\t\tif (!this.syncUrl || this.syncTimerId !== null) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tthis.syncTimerId = window.setInterval(() => {\n\t\t\t\t\tif (document.visibilityState === 'visible') {\n\t\t\t\t\t\tvoid this.runSync(false, false)\n\t\t\t\t\t}\n\t\t\t\t}, this.syncIntervalMs)\n\t\t\t},\n\t\t\tasync runSync(force, keepalive) {\n\t\t\t\tif (!this.syncUrl) return\n\t\t\t\tif (this.syncInFlight && !force) return\n\t\t\t\tthis.syncInFlight = true\n\t\t\t\ttry {\n\t\t\t\t\tconst url = force ? (this.syncUrl + (this.syncUrl.includes('?') ? '&' : '?') + 'force=1') : this.syncUrl\n\t\t\t\t\tawait fetch(url, {\n\t\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\t\tcredentials: 'same-origin',\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\tAccept: 'application/json',\n\t\t\t\t\t\t\trequesttoken: ocRequestToken(),\n\t\t\t\t\t\t},\n\t\t\t\t\t\tkeepalive: Boolean(keepalive),\n\t\t\t\t\t})\n\t\t\t\t} finally {\n\t\t\t\t\tthis.syncInFlight = false\n\t\t\t\t}\n\t\t\t},\n\t\t\tinstallSyncLifecycleHandlers() {\n\t\t\t\tif (this.visibilityHandler || this.pageHideHandler) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tthis.visibilityHandler = () => {\n\t\t\t\t\tif (document.visibilityState === 'hidden') {\n\t\t\t\t\t\tvoid this.runSync(true, true)\n\t\t\t\t\t\tthis.stopSyncLoop()\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tthis.startSyncLoop()\n\t\t\t\t}\n\t\t\t\tthis.pageHideHandler = () => {\n\t\t\t\t\tvoid this.runSync(true, true)\n\t\t\t\t\tthis.stopSyncLoop()\n\t\t\t\t}\n\t\t\t\tdocument.addEventListener('visibilitychange', this.visibilityHandler)\n\t\t\t\twindow.addEventListener('pagehide', this.pageHideHandler)\n\t\t\t},\n\t\t\tremoveSyncLifecycleHandlers() {\n\t\t\t\tif (this.visibilityHandler) {\n\t\t\t\t\tdocument.removeEventListener('visibilitychange', this.visibilityHandler)\n\t\t\t\t\tthis.visibilityHandler = null\n\t\t\t\t}\n\t\t\t\tif (this.pageHideHandler) {\n\t\t\t\t\twindow.removeEventListener('pagehide', this.pageHideHandler)\n\t\t\t\t\tthis.pageHideHandler = null\n\t\t\t\t}\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\tthis.syncUrl = ''\n\t\t\t\tthis.syncInFlight = false\n\t\t\t\tthis.stopSyncLoop()\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\tthis.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\tthis.syncIntervalMs = (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.installSyncLifecycleHandlers()\n\t\t\t\t\tif (this.syncUrl) {\n\t\t\t\t\t\tthis.startSyncLoop()\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\tvoid this.runSync(true, true)\n\t\t\tthis.stopSyncLoop()\n\t\t\tthis.removeSyncLifecycleHandlers()\n\t\t},\n\t\tbeforeUnmount() {\n\t\t\tthis.resolveGeneration += 1\n\t\t\tvoid this.runSync(true, true)\n\t\t\tthis.stopSyncLoop()\n\t\t\tthis.removeSyncLifecycleHandlers()\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('Unable to 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","force","keepalive","generation","isCurrent","publicToken","parsePublicShareTokenFromLocation","byPathUrl","byPublicUrl","byIdUrl","byPathBody","byIdBody","openPostHeaders","fetchOpenData","intervalSeconds","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 diff --git a/js/fetch-helpers-26-A0xtP.chunk.mjs b/js/fetch-helpers-C4MxuNvt.chunk.mjs similarity index 51% rename from js/fetch-helpers-26-A0xtP.chunk.mjs rename to js/fetch-helpers-C4MxuNvt.chunk.mjs index 9877b9c..69e90e7 100644 --- a/js/fetch-helpers-26-A0xtP.chunk.mjs +++ b/js/fetch-helpers-C4MxuNvt.chunk.mjs @@ -1,2 +1,2 @@ -const d=async(n,s={},r=1e4)=>{const a=new AbortController,c=window.setTimeout(()=>a.abort(),r),i=Object.assign({Accept:"application/json"},s.headers||{});try{const e=await fetch(n,Object.assign({},s,{credentials:"same-origin",headers:i,signal:a.signal})),t=await e.json().catch(()=>({}));if(!e.ok){const o=new Error(t&&t.message||"Request failed.");throw t&&typeof t.code=="string"&&(o.code=t.code),o.status=e.status,o}return t}catch(e){throw e&&typeof e=="object"&&"name"in e&&e.name==="AbortError"?new Error("Request timed out."):e}finally{window.clearTimeout(c)}};export{d as f}; -//# sourceMappingURL=fetch-helpers-26-A0xtP.chunk.mjs.map +const d=async(n,a={},r=1e4)=>{const s=new AbortController,c=window.setTimeout(()=>s.abort(),r),i=Object.assign({Accept:"application/json"},a.headers||{});try{const e=await fetch(n,Object.assign({},a,{credentials:"same-origin",headers:i,signal:s.signal})),t=await e.json().catch(()=>({}));if(!e.ok){const o=new Error(t&&t.message||"Request failed.");throw t&&typeof t.code=="string"&&(o.code=t.code),o.status=e.status,o}return t}catch(e){throw e&&typeof e=="object"&&"name"in e&&e.name==="AbortError"?new Error("Request timed out."):e}finally{window.clearTimeout(c)}};export{d as f}; +//# sourceMappingURL=fetch-helpers-C4MxuNvt.chunk.mjs.map diff --git a/js/fetch-helpers-26-A0xtP.chunk.mjs.license b/js/fetch-helpers-C4MxuNvt.chunk.mjs.license similarity index 100% rename from js/fetch-helpers-26-A0xtP.chunk.mjs.license rename to js/fetch-helpers-C4MxuNvt.chunk.mjs.license diff --git a/js/fetch-helpers-26-A0xtP.chunk.mjs.map b/js/fetch-helpers-C4MxuNvt.chunk.mjs.map similarity index 96% rename from js/fetch-helpers-26-A0xtP.chunk.mjs.map rename to js/fetch-helpers-C4MxuNvt.chunk.mjs.map index e1e1201..e41b164 100644 --- a/js/fetch-helpers-26-A0xtP.chunk.mjs.map +++ b/js/fetch-helpers-C4MxuNvt.chunk.mjs.map @@ -1 +1 @@ -{"version":3,"file":"fetch-helpers-26-A0xtP.chunk.mjs","sources":["../src/lib/fetch-helpers.js"],"sourcesContent":["/**\n * SPDX-License-Identifier: AGPL-3.0-or-later\n * Copyright (c) 2026 Jacob Bühler\n */\n\nconst DEFAULT_REQUEST_TIMEOUT_MS = 10000\n\nexport const fetchJsonWithTimeout = async (url, init = {}, timeoutMs = DEFAULT_REQUEST_TIMEOUT_MS) => {\n\tconst controller = new AbortController()\n\tconst timeoutId = window.setTimeout(() => controller.abort(), timeoutMs)\n\tconst headers = Object.assign({ Accept: 'application/json' }, init.headers || {})\n\ttry {\n\t\tconst response = await fetch(url, Object.assign({}, init, {\n\t\t\tcredentials: 'same-origin',\n\t\t\theaders,\n\t\t\tsignal: controller.signal,\n\t\t}))\n\t\tconst data = await response.json().catch(() => ({}))\n\t\tif (!response.ok) {\n\t\t\tconst error = new Error((data && data.message) || 'Request failed.')\n\t\t\tif (data && typeof data.code === 'string') {\n\t\t\t\terror.code = data.code\n\t\t\t}\n\t\t\terror.status = response.status\n\t\t\tthrow error\n\t\t}\n\t\treturn data\n\t} catch (error) {\n\t\tif (error && typeof error === 'object' && 'name' in error && error.name === 'AbortError') {\n\t\t\tthrow new Error('Request timed out.')\n\t\t}\n\t\tthrow error\n\t} finally {\n\t\twindow.clearTimeout(timeoutId)\n\t}\n}\n"],"names":["fetchJsonWithTimeout","url","init","timeoutMs","controller","timeoutId","headers","response","data","error"],"mappings":"MAOaA,EAAuB,MAAOC,EAAKC,EAAO,CAAA,EAAIC,EAAY,MAA+B,CACrG,MAAMC,EAAa,IAAI,gBACjBC,EAAY,OAAO,WAAW,IAAMD,EAAW,MAAK,EAAID,CAAS,EACjEG,EAAU,OAAO,OAAO,CAAE,OAAQ,kBAAkB,EAAIJ,EAAK,SAAW,CAAA,CAAE,EAChF,GAAI,CACH,MAAMK,EAAW,MAAM,MAAMN,EAAK,OAAO,OAAO,CAAA,EAAIC,EAAM,CACzD,YAAa,cACb,QAAAI,EACA,OAAQF,EAAW,MACtB,CAAG,CAAC,EACII,EAAO,MAAMD,EAAS,KAAI,EAAG,MAAM,KAAO,GAAG,EACnD,GAAI,CAACA,EAAS,GAAI,CACjB,MAAME,EAAQ,IAAI,MAAOD,GAAQA,EAAK,SAAY,iBAAiB,EACnE,MAAIA,GAAQ,OAAOA,EAAK,MAAS,WAChCC,EAAM,KAAOD,EAAK,MAEnBC,EAAM,OAASF,EAAS,OAClBE,CACP,CACA,OAAOD,CACR,OAASC,EAAO,CACf,MAAIA,GAAS,OAAOA,GAAU,UAAY,SAAUA,GAASA,EAAM,OAAS,aACrE,IAAI,MAAM,oBAAoB,EAE/BA,CACP,QAAA,CACC,OAAO,aAAaJ,CAAS,CAC9B,CACD"} \ No newline at end of file +{"version":3,"file":"fetch-helpers-C4MxuNvt.chunk.mjs","sources":["../src/lib/fetch-helpers.js"],"sourcesContent":["/**\n * SPDX-License-Identifier: AGPL-3.0-or-later\n * Copyright (c) 2026 Jacob Bühler\n */\n\nconst DEFAULT_REQUEST_TIMEOUT_MS = 10000\n\nexport const fetchJsonWithTimeout = async (url, init = {}, timeoutMs = DEFAULT_REQUEST_TIMEOUT_MS) => {\n\tconst controller = new AbortController()\n\tconst timeoutId = window.setTimeout(() => controller.abort(), timeoutMs)\n\tconst headers = Object.assign({ Accept: 'application/json' }, init.headers || {})\n\ttry {\n\t\tconst response = await fetch(url, Object.assign({}, init, {\n\t\t\tcredentials: 'same-origin',\n\t\t\theaders,\n\t\t\tsignal: controller.signal,\n\t\t}))\n\t\tconst data = await response.json().catch(() => ({}))\n\t\tif (!response.ok) {\n\t\t\tconst error = new Error((data && data.message) || 'Request failed.')\n\t\t\tif (data && typeof data.code === 'string') {\n\t\t\t\terror.code = data.code\n\t\t\t}\n\t\t\terror.status = response.status\n\t\t\tthrow error\n\t\t}\n\t\treturn data\n\t} catch (error) {\n\t\tif (error && typeof error === 'object' && 'name' in error && error.name === 'AbortError') {\n\t\t\tthrow new Error('Request timed out.')\n\t\t}\n\t\tthrow error\n\t} finally {\n\t\twindow.clearTimeout(timeoutId)\n\t}\n}\n"],"names":["fetchJsonWithTimeout","url","init","timeoutMs","controller","timeoutId","headers","response","data","error"],"mappings":"MAOaA,EAAuB,MAAOC,EAAKC,EAAO,CAAA,EAAIC,EAAY,MAA+B,CACrG,MAAMC,EAAa,IAAI,gBACjBC,EAAY,OAAO,WAAW,IAAMD,EAAW,MAAK,EAAID,CAAS,EACjEG,EAAU,OAAO,OAAO,CAAE,OAAQ,kBAAkB,EAAIJ,EAAK,SAAW,CAAA,CAAE,EAChF,GAAI,CACH,MAAMK,EAAW,MAAM,MAAMN,EAAK,OAAO,OAAO,CAAA,EAAIC,EAAM,CACzD,YAAa,cACb,QAAAI,EACA,OAAQF,EAAW,MACtB,CAAG,CAAC,EACII,EAAO,MAAMD,EAAS,KAAI,EAAG,MAAM,KAAO,GAAG,EACnD,GAAI,CAACA,EAAS,GAAI,CACjB,MAAME,EAAQ,IAAI,MAAOD,GAAQA,EAAK,SAAY,iBAAiB,EACnE,MAAIA,GAAQ,OAAOA,EAAK,MAAS,WAChCC,EAAM,KAAOD,EAAK,MAEnBC,EAAM,OAASF,EAAS,OAClBE,CACP,CACA,OAAOD,CACR,OAASC,EAAO,CACf,MAAIA,GAAS,OAAOA,GAAU,UAAY,SAAUA,GAASA,EAAM,OAAS,aACrE,IAAI,MAAM,oBAAoB,EAE/BA,CACP,QAAA,CACC,OAAO,aAAaJ,CAAS,CAC9B,CACD"} \ No newline at end of file diff --git a/js/oc-compat-DlZkDG_8.chunk.mjs b/js/oc-compat-hVqZy-MX.chunk.mjs similarity index 71% rename from js/oc-compat-DlZkDG_8.chunk.mjs rename to js/oc-compat-hVqZy-MX.chunk.mjs index 4c7396b..ef9f99a 100644 --- a/js/oc-compat-DlZkDG_8.chunk.mjs +++ b/js/oc-compat-hVqZy-MX.chunk.mjs @@ -1,2 +1,2 @@ -const c="etherpad_nextcloud",u="application/x-etherpad-nextcloud",f="etherpad_nextcloud",C=n=>window.OC&&typeof window.OC.generateUrl=="function"?window.OC.generateUrl(n):"/index.php"+n,p=(n,o)=>window.OC&&typeof window.OC.imagePath=="function"?window.OC.imagePath(n,o):"",O=(n="")=>{const o=String(n||"").trim();return o!==""?o:String(window.OC&&window.OC.requestToken||"")},s=()=>{if(window.OC&&typeof window.OC.getCurrentUser=="function"){const n=window.OC.getCurrentUser();return String(n&&n.uid||"").trim()}return""},a=(n,o)=>{const e=s(),i=String(n||"").trim();if(e===""||i==="")return"";const t=window.OC&&typeof window.OC.linkToRemoteBase=="function"?window.OC.linkToRemoteBase("dav"):"/remote.php/dav",r=new URL(t,window.location.origin),d=i.split("/").filter(w=>w!=="").map(w=>w).join("/");return r.origin+r.pathname.replace(/\/+$/,"")+"/files/"+e+"/"+d},m=n=>a(n),g=(n,o)=>{const e=window._nc_event_bus||window.OC&&window.OC._eventBus;return!e||typeof e.emit!="function"?!1:(e.emit(n,o),!0)},l=()=>{const n=window.OC&&window.OC.PERMISSION_READ,o=Number(n);return Number.isFinite(o)&&o>0?o:1},_=()=>{const n=window.OC&&window.OC.PERMISSION_CREATE,o=Number(n);return Number.isFinite(o)&&o>0?o:4},h=()=>{const n=[window.OC&&window.OC.config&&window.OC.config.version,window.OC&&window.OC.config&&window.OC.config.versionstring,window.oc_appconfig&&window.oc_appconfig.core&&window.oc_appconfig.core.version,document.documentElement&&document.documentElement.getAttribute("data-version")];for(const o of n){const e=String(o||"").trim();if(e==="")continue;const i=e.match(/^(\d+)/);if(!i)continue;const t=parseInt(i[1],10);if(Number.isFinite(t)&&t>0)return t}return null},v=n=>typeof window.t=="function"?window.t(c,n):n;export{c as A,u as M,f as V,l as a,p as b,m as c,g as d,_ as e,C as f,h as n,O as o,v as t}; -//# sourceMappingURL=oc-compat-DlZkDG_8.chunk.mjs.map +const c="etherpad_nextcloud",u="application/x-etherpad-nextcloud",f="etherpad_nextcloud",p=n=>window.OC&&typeof window.OC.generateUrl=="function"?window.OC.generateUrl(n):"/index.php"+n,C=(n,o)=>window.OC&&typeof window.OC.imagePath=="function"?window.OC.imagePath(n,o):"",O=(n="")=>{const o=String(n||"").trim();return o!==""?o:String(window.OC&&window.OC.requestToken||"")},a=()=>{if(window.OC&&typeof window.OC.getCurrentUser=="function"){const n=window.OC.getCurrentUser();return String(n&&n.uid||"").trim()}return""},s=(n,o)=>{const e=a(),i=String(n||"").trim();if(e===""||i==="")return"";const t=window.OC&&typeof window.OC.linkToRemoteBase=="function"?window.OC.linkToRemoteBase("dav"):"/remote.php/dav",r=new URL(t,window.location.origin),d=i.split("/").filter(w=>w!=="").map(w=>w).join("/");return r.origin+r.pathname.replace(/\/+$/,"")+"/files/"+e+"/"+d},m=n=>s(n),g=(n,o)=>{const e=window._nc_event_bus||window.OC&&window.OC._eventBus;return!e||typeof e.emit!="function"?!1:(e.emit(n,o),!0)},l=()=>{const n=window.OC&&window.OC.PERMISSION_READ,o=Number(n);return Number.isFinite(o)&&o>0?o:1},_=()=>{const n=window.OC&&window.OC.PERMISSION_CREATE,o=Number(n);return Number.isFinite(o)&&o>0?o:4},h=()=>{const n=[window.OC&&window.OC.config&&window.OC.config.version,window.OC&&window.OC.config&&window.OC.config.versionstring,window.oc_appconfig&&window.oc_appconfig.core&&window.oc_appconfig.core.version,document.documentElement&&document.documentElement.getAttribute("data-version")];for(const o of n){const e=String(o||"").trim();if(e==="")continue;const i=e.match(/^(\d+)/);if(!i)continue;const t=parseInt(i[1],10);if(Number.isFinite(t)&&t>0)return t}return null},b=n=>typeof window.t=="function"?window.t(c,n):n;export{c as A,u as M,f as V,l as a,C as b,m as c,g as d,_ as e,p as f,h as n,O as o,b as t}; +//# sourceMappingURL=oc-compat-hVqZy-MX.chunk.mjs.map diff --git a/js/oc-compat-DlZkDG_8.chunk.mjs.license b/js/oc-compat-hVqZy-MX.chunk.mjs.license similarity index 100% rename from js/oc-compat-DlZkDG_8.chunk.mjs.license rename to js/oc-compat-hVqZy-MX.chunk.mjs.license diff --git a/js/oc-compat-DlZkDG_8.chunk.mjs.map b/js/oc-compat-hVqZy-MX.chunk.mjs.map similarity index 98% rename from js/oc-compat-DlZkDG_8.chunk.mjs.map rename to js/oc-compat-hVqZy-MX.chunk.mjs.map index 4157e8b..c21ae86 100644 --- a/js/oc-compat-DlZkDG_8.chunk.mjs.map +++ b/js/oc-compat-hVqZy-MX.chunk.mjs.map @@ -1 +1 @@ -{"version":3,"file":"oc-compat-DlZkDG_8.chunk.mjs","sources":["../src/lib/constants.js","../src/lib/oc-compat.js"],"sourcesContent":["/**\n * SPDX-License-Identifier: AGPL-3.0-or-later\n * Copyright (c) 2026 Jacob Bühler\n */\n\nexport const APP_ID = 'etherpad_nextcloud'\nexport const MIME = 'application/x-etherpad-nextcloud'\nexport const VIEWER_HANDLER_ID = 'etherpad_nextcloud'\n","/**\n * SPDX-License-Identifier: AGPL-3.0-or-later\n * Copyright (c) 2026 Jacob Bühler\n */\n\nimport { APP_ID } from './constants.js'\n\nexport const ocGenerateUrl = (path) => {\n\tif (window.OC && typeof window.OC.generateUrl === 'function') {\n\t\treturn window.OC.generateUrl(path)\n\t}\n\treturn '/index.php' + path\n}\n\nexport const ocImagePath = (app, asset) => {\n\tif (window.OC && typeof window.OC.imagePath === 'function') {\n\t\treturn window.OC.imagePath(app, asset)\n\t}\n\treturn ''\n}\n\nexport const ocRequestToken = (fallback = '') => {\n\tconst configuredFallback = String(fallback || '').trim()\n\tif (configuredFallback !== '') {\n\t\treturn configuredFallback\n\t}\n\treturn String((window.OC && window.OC.requestToken) || '')\n}\n\nconst ocCurrentUserId = () => {\n\tif (window.OC && typeof window.OC.getCurrentUser === 'function') {\n\t\tconst user = window.OC.getCurrentUser()\n\t\treturn String((user && user.uid) || '').trim()\n\t}\n\treturn ''\n}\n\nconst buildDavFileUrl = (path, encodePath) => {\n\tconst uid = ocCurrentUserId()\n\tconst normalizedPath = String(path || '').trim()\n\tif (uid === '' || normalizedPath === '') {\n\t\treturn ''\n\t}\n\n\tconst remoteBase = (window.OC && typeof window.OC.linkToRemoteBase === 'function')\n\t\t? window.OC.linkToRemoteBase('dav')\n\t\t: '/remote.php/dav'\n\tconst baseUrl = new URL(remoteBase, window.location.origin)\n\tconst pathSuffix = normalizedPath\n\t\t.split('/')\n\t\t.filter((part) => part !== '')\n\t\t.map((part) => encodePath ? encodeURIComponent(part) : part)\n\t\t.join('/')\n\treturn baseUrl.origin\n\t\t+ baseUrl.pathname.replace(/\\/+$/, '')\n\t\t+ '/files/'\n\t\t+ (encodePath ? encodeURIComponent(uid) : uid)\n\t\t+ '/'\n\t\t+ pathSuffix\n}\n\nexport const ocDavFileSource = (path) => buildDavFileUrl(path, false)\n\nexport const ocEmitEvent = (name, payload) => {\n\tconst bus = window._nc_event_bus || (window.OC && window.OC._eventBus)\n\tif (!bus || typeof bus.emit !== 'function') {\n\t\treturn false\n\t}\n\n\tbus.emit(name, payload)\n\treturn true\n}\n\nexport const ocPermissionRead = () => {\n\tconst value = window.OC && window.OC.PERMISSION_READ\n\tconst numeric = Number(value)\n\treturn Number.isFinite(numeric) && numeric > 0 ? numeric : 1\n}\n\nexport const ocPermissionCreate = () => {\n\tconst value = window.OC && window.OC.PERMISSION_CREATE\n\tconst numeric = Number(value)\n\treturn Number.isFinite(numeric) && numeric > 0 ? numeric : 4\n}\n\nexport const nextcloudMajorVersion = () => {\n\tconst candidates = [\n\t\twindow.OC && window.OC.config && window.OC.config.version,\n\t\twindow.OC && window.OC.config && window.OC.config.versionstring,\n\t\twindow.oc_appconfig && window.oc_appconfig.core && window.oc_appconfig.core.version,\n\t\tdocument.documentElement && document.documentElement.getAttribute('data-version'),\n\t]\n\tfor (const candidate of candidates) {\n\t\tconst value = String(candidate || '').trim()\n\t\tif (value === '') {\n\t\t\tcontinue\n\t\t}\n\t\tconst match = value.match(/^(\\d+)/)\n\t\tif (!match) {\n\t\t\tcontinue\n\t\t}\n\t\tconst parsed = parseInt(match[1], 10)\n\t\tif (Number.isFinite(parsed) && parsed > 0) {\n\t\t\treturn parsed\n\t\t}\n\t}\n\treturn null\n}\n\nexport const translate = (text) => (typeof window.t === 'function' ? window.t(APP_ID, text) : text)\n"],"names":["APP_ID","MIME","VIEWER_HANDLER_ID","ocGenerateUrl","path","ocImagePath","app","asset","ocRequestToken","fallback","configuredFallback","ocCurrentUserId","user","buildDavFileUrl","encodePath","uid","normalizedPath","remoteBase","baseUrl","pathSuffix","part","ocDavFileSource","ocEmitEvent","name","payload","bus","ocPermissionRead","value","numeric","ocPermissionCreate","nextcloudMajorVersion","candidates","candidate","match","parsed","translate","text"],"mappings":"MAKaA,EAAS,qBACTC,EAAO,mCACPC,EAAoB,qBCApBC,EAAiBC,GACzB,OAAO,IAAM,OAAO,OAAO,GAAG,aAAgB,WAC1C,OAAO,GAAG,YAAYA,CAAI,EAE3B,aAAeA,EAGVC,EAAc,CAACC,EAAKC,IAC5B,OAAO,IAAM,OAAO,OAAO,GAAG,WAAc,WACxC,OAAO,GAAG,UAAUD,EAAKC,CAAK,EAE/B,GAGKC,EAAiB,CAACC,EAAW,KAAO,CAChD,MAAMC,EAAqB,OAAOD,GAAY,EAAE,EAAE,KAAI,EACtD,OAAIC,IAAuB,GACnBA,EAED,OAAQ,OAAO,IAAM,OAAO,GAAG,cAAiB,EAAE,CAC1D,EAEMC,EAAkB,IAAM,CAC7B,GAAI,OAAO,IAAM,OAAO,OAAO,GAAG,gBAAmB,WAAY,CAChE,MAAMC,EAAO,OAAO,GAAG,eAAc,EACrC,OAAO,OAAQA,GAAQA,EAAK,KAAQ,EAAE,EAAE,KAAI,CAC7C,CACA,MAAO,EACR,EAEMC,EAAkB,CAACT,EAAMU,IAAe,CAC7C,MAAMC,EAAMJ,EAAe,EACrBK,EAAiB,OAAOZ,GAAQ,EAAE,EAAE,KAAI,EAC9C,GAAIW,IAAQ,IAAMC,IAAmB,GACpC,MAAO,GAGR,MAAMC,EAAc,OAAO,IAAM,OAAO,OAAO,GAAG,kBAAqB,WACpE,OAAO,GAAG,iBAAiB,KAAK,EAChC,kBACGC,EAAU,IAAI,IAAID,EAAY,OAAO,SAAS,MAAM,EACpDE,EAAaH,EACjB,MAAM,GAAG,EACT,OAAQI,GAASA,IAAS,EAAE,EAC5B,IAAKA,GAAiDA,CAAI,EAC1D,KAAK,GAAG,EACV,OAAOF,EAAQ,OACZA,EAAQ,SAAS,QAAQ,OAAQ,EAAE,EACnC,UACwCH,EACxC,IACAI,CACJ,EAEaE,EAAmBjB,GAASS,EAAgBT,CAAW,EAEvDkB,EAAc,CAACC,EAAMC,IAAY,CAC7C,MAAMC,EAAM,OAAO,eAAkB,OAAO,IAAM,OAAO,GAAG,UAC5D,MAAI,CAACA,GAAO,OAAOA,EAAI,MAAS,WACxB,IAGRA,EAAI,KAAKF,EAAMC,CAAO,EACf,GACR,EAEaE,EAAmB,IAAM,CACrC,MAAMC,EAAQ,OAAO,IAAM,OAAO,GAAG,gBAC/BC,EAAU,OAAOD,CAAK,EAC5B,OAAO,OAAO,SAASC,CAAO,GAAKA,EAAU,EAAIA,EAAU,CAC5D,EAEaC,EAAqB,IAAM,CACvC,MAAMF,EAAQ,OAAO,IAAM,OAAO,GAAG,kBAC/BC,EAAU,OAAOD,CAAK,EAC5B,OAAO,OAAO,SAASC,CAAO,GAAKA,EAAU,EAAIA,EAAU,CAC5D,EAEaE,EAAwB,IAAM,CAC1C,MAAMC,EAAa,CAClB,OAAO,IAAM,OAAO,GAAG,QAAU,OAAO,GAAG,OAAO,QAClD,OAAO,IAAM,OAAO,GAAG,QAAU,OAAO,GAAG,OAAO,cAClD,OAAO,cAAgB,OAAO,aAAa,MAAQ,OAAO,aAAa,KAAK,QAC5E,SAAS,iBAAmB,SAAS,gBAAgB,aAAa,cAAc,CAClF,EACC,UAAWC,KAAaD,EAAY,CACnC,MAAMJ,EAAQ,OAAOK,GAAa,EAAE,EAAE,KAAI,EAC1C,GAAIL,IAAU,GACb,SAED,MAAMM,EAAQN,EAAM,MAAM,QAAQ,EAClC,GAAI,CAACM,EACJ,SAED,MAAMC,EAAS,SAASD,EAAM,CAAC,EAAG,EAAE,EACpC,GAAI,OAAO,SAASC,CAAM,GAAKA,EAAS,EACvC,OAAOA,CAET,CACA,OAAO,IACR,EAEaC,EAAaC,GAAU,OAAO,OAAO,GAAM,WAAa,OAAO,EAAEpC,EAAQoC,CAAI,EAAIA"} \ No newline at end of file +{"version":3,"file":"oc-compat-hVqZy-MX.chunk.mjs","sources":["../src/lib/constants.js","../src/lib/oc-compat.js"],"sourcesContent":["/**\n * SPDX-License-Identifier: AGPL-3.0-or-later\n * Copyright (c) 2026 Jacob Bühler\n */\n\nexport const APP_ID = 'etherpad_nextcloud'\nexport const MIME = 'application/x-etherpad-nextcloud'\nexport const VIEWER_HANDLER_ID = 'etherpad_nextcloud'\n","/**\n * SPDX-License-Identifier: AGPL-3.0-or-later\n * Copyright (c) 2026 Jacob Bühler\n */\n\nimport { APP_ID } from './constants.js'\n\nexport const ocGenerateUrl = (path) => {\n\tif (window.OC && typeof window.OC.generateUrl === 'function') {\n\t\treturn window.OC.generateUrl(path)\n\t}\n\treturn '/index.php' + path\n}\n\nexport const ocImagePath = (app, asset) => {\n\tif (window.OC && typeof window.OC.imagePath === 'function') {\n\t\treturn window.OC.imagePath(app, asset)\n\t}\n\treturn ''\n}\n\nexport const ocRequestToken = (fallback = '') => {\n\tconst configuredFallback = String(fallback || '').trim()\n\tif (configuredFallback !== '') {\n\t\treturn configuredFallback\n\t}\n\treturn String((window.OC && window.OC.requestToken) || '')\n}\n\nconst ocCurrentUserId = () => {\n\tif (window.OC && typeof window.OC.getCurrentUser === 'function') {\n\t\tconst user = window.OC.getCurrentUser()\n\t\treturn String((user && user.uid) || '').trim()\n\t}\n\treturn ''\n}\n\nconst buildDavFileUrl = (path, encodePath) => {\n\tconst uid = ocCurrentUserId()\n\tconst normalizedPath = String(path || '').trim()\n\tif (uid === '' || normalizedPath === '') {\n\t\treturn ''\n\t}\n\n\tconst remoteBase = (window.OC && typeof window.OC.linkToRemoteBase === 'function')\n\t\t? window.OC.linkToRemoteBase('dav')\n\t\t: '/remote.php/dav'\n\tconst baseUrl = new URL(remoteBase, window.location.origin)\n\tconst pathSuffix = normalizedPath\n\t\t.split('/')\n\t\t.filter((part) => part !== '')\n\t\t.map((part) => encodePath ? encodeURIComponent(part) : part)\n\t\t.join('/')\n\treturn baseUrl.origin\n\t\t+ baseUrl.pathname.replace(/\\/+$/, '')\n\t\t+ '/files/'\n\t\t+ (encodePath ? encodeURIComponent(uid) : uid)\n\t\t+ '/'\n\t\t+ pathSuffix\n}\n\nexport const ocDavFileSource = (path) => buildDavFileUrl(path, false)\n\nexport const ocEmitEvent = (name, payload) => {\n\tconst bus = window._nc_event_bus || (window.OC && window.OC._eventBus)\n\tif (!bus || typeof bus.emit !== 'function') {\n\t\treturn false\n\t}\n\n\tbus.emit(name, payload)\n\treturn true\n}\n\nexport const ocPermissionRead = () => {\n\tconst value = window.OC && window.OC.PERMISSION_READ\n\tconst numeric = Number(value)\n\treturn Number.isFinite(numeric) && numeric > 0 ? numeric : 1\n}\n\nexport const ocPermissionCreate = () => {\n\tconst value = window.OC && window.OC.PERMISSION_CREATE\n\tconst numeric = Number(value)\n\treturn Number.isFinite(numeric) && numeric > 0 ? numeric : 4\n}\n\nexport const nextcloudMajorVersion = () => {\n\tconst candidates = [\n\t\twindow.OC && window.OC.config && window.OC.config.version,\n\t\twindow.OC && window.OC.config && window.OC.config.versionstring,\n\t\twindow.oc_appconfig && window.oc_appconfig.core && window.oc_appconfig.core.version,\n\t\tdocument.documentElement && document.documentElement.getAttribute('data-version'),\n\t]\n\tfor (const candidate of candidates) {\n\t\tconst value = String(candidate || '').trim()\n\t\tif (value === '') {\n\t\t\tcontinue\n\t\t}\n\t\tconst match = value.match(/^(\\d+)/)\n\t\tif (!match) {\n\t\t\tcontinue\n\t\t}\n\t\tconst parsed = parseInt(match[1], 10)\n\t\tif (Number.isFinite(parsed) && parsed > 0) {\n\t\t\treturn parsed\n\t\t}\n\t}\n\treturn null\n}\n\nexport const translate = (text) => (typeof window.t === 'function' ? window.t(APP_ID, text) : text)\n"],"names":["APP_ID","MIME","VIEWER_HANDLER_ID","ocGenerateUrl","path","ocImagePath","app","asset","ocRequestToken","fallback","configuredFallback","ocCurrentUserId","user","buildDavFileUrl","encodePath","uid","normalizedPath","remoteBase","baseUrl","pathSuffix","part","ocDavFileSource","ocEmitEvent","name","payload","bus","ocPermissionRead","value","numeric","ocPermissionCreate","nextcloudMajorVersion","candidates","candidate","match","parsed","translate","text"],"mappings":"MAKaA,EAAS,qBACTC,EAAO,mCACPC,EAAoB,qBCApBC,EAAiBC,GACzB,OAAO,IAAM,OAAO,OAAO,GAAG,aAAgB,WAC1C,OAAO,GAAG,YAAYA,CAAI,EAE3B,aAAeA,EAGVC,EAAc,CAACC,EAAKC,IAC5B,OAAO,IAAM,OAAO,OAAO,GAAG,WAAc,WACxC,OAAO,GAAG,UAAUD,EAAKC,CAAK,EAE/B,GAGKC,EAAiB,CAACC,EAAW,KAAO,CAChD,MAAMC,EAAqB,OAAOD,GAAY,EAAE,EAAE,KAAI,EACtD,OAAIC,IAAuB,GACnBA,EAED,OAAQ,OAAO,IAAM,OAAO,GAAG,cAAiB,EAAE,CAC1D,EAEMC,EAAkB,IAAM,CAC7B,GAAI,OAAO,IAAM,OAAO,OAAO,GAAG,gBAAmB,WAAY,CAChE,MAAMC,EAAO,OAAO,GAAG,eAAc,EACrC,OAAO,OAAQA,GAAQA,EAAK,KAAQ,EAAE,EAAE,KAAI,CAC7C,CACA,MAAO,EACR,EAEMC,EAAkB,CAACT,EAAMU,IAAe,CAC7C,MAAMC,EAAMJ,EAAe,EACrBK,EAAiB,OAAOZ,GAAQ,EAAE,EAAE,KAAI,EAC9C,GAAIW,IAAQ,IAAMC,IAAmB,GACpC,MAAO,GAGR,MAAMC,EAAc,OAAO,IAAM,OAAO,OAAO,GAAG,kBAAqB,WACpE,OAAO,GAAG,iBAAiB,KAAK,EAChC,kBACGC,EAAU,IAAI,IAAID,EAAY,OAAO,SAAS,MAAM,EACpDE,EAAaH,EACjB,MAAM,GAAG,EACT,OAAQI,GAASA,IAAS,EAAE,EAC5B,IAAKA,GAAiDA,CAAI,EAC1D,KAAK,GAAG,EACV,OAAOF,EAAQ,OACZA,EAAQ,SAAS,QAAQ,OAAQ,EAAE,EACnC,UACwCH,EACxC,IACAI,CACJ,EAEaE,EAAmBjB,GAASS,EAAgBT,CAAW,EAEvDkB,EAAc,CAACC,EAAMC,IAAY,CAC7C,MAAMC,EAAM,OAAO,eAAkB,OAAO,IAAM,OAAO,GAAG,UAC5D,MAAI,CAACA,GAAO,OAAOA,EAAI,MAAS,WACxB,IAGRA,EAAI,KAAKF,EAAMC,CAAO,EACf,GACR,EAEaE,EAAmB,IAAM,CACrC,MAAMC,EAAQ,OAAO,IAAM,OAAO,GAAG,gBAC/BC,EAAU,OAAOD,CAAK,EAC5B,OAAO,OAAO,SAASC,CAAO,GAAKA,EAAU,EAAIA,EAAU,CAC5D,EAEaC,EAAqB,IAAM,CACvC,MAAMF,EAAQ,OAAO,IAAM,OAAO,GAAG,kBAC/BC,EAAU,OAAOD,CAAK,EAC5B,OAAO,OAAO,SAASC,CAAO,GAAKA,EAAU,EAAIA,EAAU,CAC5D,EAEaE,EAAwB,IAAM,CAC1C,MAAMC,EAAa,CAClB,OAAO,IAAM,OAAO,GAAG,QAAU,OAAO,GAAG,OAAO,QAClD,OAAO,IAAM,OAAO,GAAG,QAAU,OAAO,GAAG,OAAO,cAClD,OAAO,cAAgB,OAAO,aAAa,MAAQ,OAAO,aAAa,KAAK,QAC5E,SAAS,iBAAmB,SAAS,gBAAgB,aAAa,cAAc,CAClF,EACC,UAAWC,KAAaD,EAAY,CACnC,MAAMJ,EAAQ,OAAOK,GAAa,EAAE,EAAE,KAAI,EAC1C,GAAIL,IAAU,GACb,SAED,MAAMM,EAAQN,EAAM,MAAM,QAAQ,EAClC,GAAI,CAACM,EACJ,SAED,MAAMC,EAAS,SAASD,EAAM,CAAC,EAAG,EAAE,EACpC,GAAI,OAAO,SAASC,CAAM,GAAKA,EAAS,EACvC,OAAOA,CAET,CACA,OAAO,IACR,EAEaC,EAAaC,GAAU,OAAO,OAAO,GAAM,WAAa,OAAO,EAAEpC,EAAQoC,CAAI,EAAIA"} \ No newline at end of file diff --git a/src/embed-create-main.js b/src/embed-create-main.js index 4da95cf..d8e5a1a 100644 --- a/src/embed-create-main.js +++ b/src/embed-create-main.js @@ -23,6 +23,31 @@ import { fetchJsonWithTimeout as fetchJson } from './lib/fetch-helpers.js' const requestToken = () => ocRequestToken(templateRequestToken) + /** + * Post an `epnc:*` event to the host page that's embedding this iframe. + * + * Target-origin is `*` rather than a specific origin because the create + * page doesn't know the host's origin up-front (the host hasn't talked to + * us yet). The actual access control happens at iframe-load time via the + * route's CSP `frame-ancestors` header, which only lists the admin- + * configured `trusted_embed_origins`. Anyone receiving these messages is + * by construction already in that allowlist. + * + * No-ops if we're not actually embedded (window.parent === window). + */ + const postHostMessage = (type, payload) => { + if (window.parent === window) { + return + } + try { + window.parent.postMessage(Object.assign({ type }, payload || {}), '*') + } catch (e) { + // Posting can throw on certain cross-origin / cross-process boundaries; + // inline error rendering is the user-visible fallback, so the message + // is purely advisory for the host. + } + } + const showError = (message) => { if (loadingNode instanceof HTMLElement) { loadingNode.hidden = true @@ -35,6 +60,30 @@ import { fetchJsonWithTimeout as fetchJson } from './lib/fetch-helpers.js' } } + /** + * Emit a structured `epnc:create-failed` event AND render the inline error. + * The message is normalised once so the host's payload and the user-facing + * inline message never drift (an empty/undefined `message` would otherwise + * land in the iframe as "Unknown error." but in the postMessage payload as + * the empty string). + * + * `reason` is a coarse bucket so hosts can branch without parsing the + * HTTP status: + * - 'invalid' — client-side validation failed (missing name, etc.) + * - 'conflict' — backend returned 409 (e.g. duplicate filename) + * - 'server' — any other 4xx / 5xx + * - 'network' — fetch itself failed (offline, CORS, timeout) + */ + const failCreate = (reason, message, status) => { + const normalizedMessage = String(message || 'Unknown error.') + showError(normalizedMessage) + postHostMessage('epnc:create-failed', { + reason, + status: typeof status === 'number' ? status : null, + message: normalizedMessage, + }) + } + const readLauncherParams = () => { const params = new URL(window.location.href).searchParams return { @@ -51,23 +100,30 @@ import { fetchJsonWithTimeout as fetchJson } from './lib/fetch-helpers.js' return url.pathname + url.search + url.hash } + const classifyHttpStatus = (status) => { + if (status === 409) { + return 'conflict' + } + return 'server' + } + const run = async () => { if (!Number.isFinite(parentFolderId) || parentFolderId <= 0 || createByParentUrl === '') { - showError(incompleteConfigMessage) + failCreate('invalid', incompleteConfigMessage) return } if (requestToken() === '') { - showError('CSRF request token is missing.') + failCreate('invalid', 'CSRF request token is missing.') return } const { name, accessMode } = readLauncherParams() if (name === '') { - showError(missingNameMessage) + failCreate('invalid', missingNameMessage) return } if (accessMode !== 'protected' && accessMode !== 'public') { - showError(invalidAccessModeMessage) + failCreate('invalid', invalidAccessModeMessage) return } @@ -76,8 +132,12 @@ import { fetchJsonWithTimeout as fetchJson } from './lib/fetch-helpers.js' body.set('name', name) body.set('accessMode', accessMode) + // Step 1: server-side create. Failures here are either network + // (fetch threw — no HTTP status reached us) or server (we got a + // status code back, including the 409 on duplicate filename). + let data try { - const data = await fetchJson(createByParentUrl, { + data = await fetchJson(createByParentUrl, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8', @@ -85,13 +145,43 @@ import { fetchJsonWithTimeout as fetchJson } from './lib/fetch-helpers.js' }, body: body.toString(), }) - if (!data || typeof data.embed_url !== 'string' || data.embed_url.trim() === '') { - throw new Error('Pad creation API did not return a valid embed URL.') - } - window.location.replace(normalizeEmbedRedirectUrl(data.embed_url)) } catch (error) { - showError(error instanceof Error ? error.message : 'Pad creation failed.') + const status = (error && typeof error.status === 'number') ? error.status : null + const message = error instanceof Error ? error.message : 'Pad creation failed.' + const reason = status === null ? 'network' : classifyHttpStatus(status) + failCreate(reason, message, status) + return + } + + // Step 2: validate the server's response shape *and* the redirect + // target before emitting success. A malformed or cross-origin + // embed_url is a server-side bug, not a network failure — and we + // must not announce success only to then announce failure to the + // same host listener, which would leave them with contradictory + // signals. + if (!data || typeof data.embed_url !== 'string' || data.embed_url.trim() === '') { + failCreate('server', 'Pad creation API did not return a valid embed URL.') + return + } + let redirectTarget + try { + redirectTarget = normalizeEmbedRedirectUrl(data.embed_url) + } catch (error) { + const message = error instanceof Error ? error.message : 'Invalid embed URL.' + failCreate('server', message) + return } + + // Step 3: announce success once everything is definitively OK, then + // navigate. Notify *before* the redirect: once we replace the iframe + // location the host loses its handle on this script. + postHostMessage('epnc:create-succeeded', { + embed_url: data.embed_url, + file_id: typeof data.file_id === 'number' ? data.file_id : null, + pad_id: typeof data.pad_id === 'string' ? data.pad_id : '', + access_mode: typeof data.access_mode === 'string' ? data.access_mode : '', + }) + window.location.replace(redirectTarget) } void run() diff --git a/tests/js/embed-create-main.test.js b/tests/js/embed-create-main.test.js new file mode 100644 index 0000000..5a16eec --- /dev/null +++ b/tests/js/embed-create-main.test.js @@ -0,0 +1,296 @@ +/** + * SPDX-License-Identifier: AGPL-3.0-or-later + * Copyright (c) 2026 Jacob Bühler + */ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' + +const flushMicrotasks = async () => { + for (let i = 0; i < 8; i += 1) { + await Promise.resolve() + } +} + +const setupEmbedCreateDom = () => { + document.body.innerHTML = ` +
+
loading
+ +
+ ` +} + +const jsonResponse = (body, ok = true, status = 200) => ({ + ok, + status, + json: () => Promise.resolve(body), +}) + +const errorResponse = (body, status = 400) => jsonResponse(body, false, status) + +const errorMessageText = () => document.querySelector('[data-epnc-embed-create-error-message]').textContent +const errorPanelHidden = () => document.querySelector('[data-epnc-embed-create-error]').hidden + +let parentPostSpy +let locationReplaceSpy +let originalLocationDescriptor + +const importEmbedCreate = async (search) => { + const url = `http://localhost/embed/create-by-parent/42${search ?? '?name=My%20Pad&accessMode=protected'}` + // happy-dom doesn't let us simply reassign window.location; redefine just + // the bits we touch. `configurable: true` is required so afterEach can + // restore the original descriptor and the next test can redefine again. + Object.defineProperty(window, 'location', { + configurable: true, + writable: true, + value: { + href: url, + origin: 'http://localhost', + pathname: '/embed/create-by-parent/42', + search: search ?? '?name=My%20Pad&accessMode=protected', + replace: locationReplaceSpy, + }, + }) + vi.resetModules() + await import('../../src/embed-create-main.js') +} + +beforeEach(() => { + setupEmbedCreateDom() + window.OC = { requestToken: 'csrf' } + globalThis.fetch = vi.fn() + // Pretend we're embedded inside another window so window.parent !== window. + parentPostSpy = vi.fn() + Object.defineProperty(window, 'parent', { + configurable: true, + value: { postMessage: parentPostSpy }, + }) + locationReplaceSpy = vi.fn() + // Cache the original `location` descriptor so afterEach can restore it + // instead of leaving the test's mock around for the next file. + originalLocationDescriptor = Object.getOwnPropertyDescriptor(window, 'location') +}) + +afterEach(() => { + document.body.innerHTML = '' + delete window.OC + delete globalThis.fetch + // Restore parent (point back at window so happy-dom's defaults hold). + Object.defineProperty(window, 'parent', { configurable: true, value: window }) + // Restore the location descriptor we cached in beforeEach (if any). + if (originalLocationDescriptor) { + Object.defineProperty(window, 'location', originalLocationDescriptor) + } +}) + +describe('embed-create-main', () => { + it('posts epnc:create-succeeded and redirects on a clean create', async () => { + fetch.mockResolvedValueOnce(jsonResponse({ + embed_url: '/embed/by-id/777', + file_id: 777, + pad_id: 'g.abc$mypad', + access_mode: 'protected', + })) + + await importEmbedCreate() + await flushMicrotasks() + + expect(fetch).toHaveBeenCalledOnce() + expect(fetch.mock.calls[0][0]).toBe('/api/create-by-parent') + + // Host gets a structured success event with the new pad's identity. + expect(parentPostSpy).toHaveBeenCalledOnce() + const [payload, targetOrigin] = parentPostSpy.mock.calls[0] + expect(payload).toEqual({ + type: 'epnc:create-succeeded', + embed_url: '/embed/by-id/777', + file_id: 777, + pad_id: 'g.abc$mypad', + access_mode: 'protected', + }) + expect(targetOrigin).toBe('*') + + // And the iframe redirects to the embed open URL. + expect(locationReplaceSpy).toHaveBeenCalledWith('/embed/by-id/777') + }) + + it('posts epnc:create-failed with reason=conflict on a 409 from the API', async () => { + fetch.mockResolvedValueOnce(errorResponse( + { message: 'A file with this name already exists.' }, + 409, + )) + + await importEmbedCreate() + await flushMicrotasks() + + expect(parentPostSpy).toHaveBeenCalledOnce() + const payload = parentPostSpy.mock.calls[0][0] + expect(payload.type).toBe('epnc:create-failed') + expect(payload.reason).toBe('conflict') + expect(payload.status).toBe(409) + expect(payload.message).toBe('A file with this name already exists.') + + // Inline error stays visible for users who can actually see the iframe. + expect(errorPanelHidden()).toBe(false) + expect(errorMessageText()).toBe('A file with this name already exists.') + + // No redirect on failure. + expect(locationReplaceSpy).not.toHaveBeenCalled() + }) + + it('posts epnc:create-failed with reason=server on a 5xx response', async () => { + fetch.mockResolvedValueOnce(errorResponse( + { message: 'Could not create pad.' }, + 500, + )) + + await importEmbedCreate() + await flushMicrotasks() + + const payload = parentPostSpy.mock.calls[0][0] + expect(payload.type).toBe('epnc:create-failed') + expect(payload.reason).toBe('server') + expect(payload.status).toBe(500) + }) + + it('posts epnc:create-failed with reason=network when fetch itself throws', async () => { + fetch.mockRejectedValueOnce(new Error('Network unreachable')) + + await importEmbedCreate() + await flushMicrotasks() + + const payload = parentPostSpy.mock.calls[0][0] + expect(payload.type).toBe('epnc:create-failed') + expect(payload.reason).toBe('network') + expect(payload.status).toBe(null) + expect(payload.message).toBe('Network unreachable') + }) + + it('posts epnc:create-failed with reason=invalid when launcher params are missing', async () => { + await importEmbedCreate('?accessMode=protected') // no name param + + await flushMicrotasks() + + expect(fetch).not.toHaveBeenCalled() + const payload = parentPostSpy.mock.calls[0][0] + expect(payload.type).toBe('epnc:create-failed') + expect(payload.reason).toBe('invalid') + expect(payload.message).toBe('Pad name is required.') + }) + + it('emits epnc:create-failed with reason=invalid when embed config is incomplete', async () => { + // Simulates the host page mounting the iframe without the + // data-create-by-parent-url attribute (or with a non-numeric + // parent-folder-id). The flow should bail before doing any fetch + // AND still send the host a structured signal. + document.body.innerHTML = ` +
+
loading
+ +
+ ` + await importEmbedCreate() + await flushMicrotasks() + + expect(fetch).not.toHaveBeenCalled() + expect(locationReplaceSpy).not.toHaveBeenCalled() + + const payload = parentPostSpy.mock.calls[0][0] + expect(payload.type).toBe('epnc:create-failed') + expect(payload.reason).toBe('invalid') + expect(payload.status).toBe(null) + expect(payload.message).toBe('Embed configuration is incomplete.') + }) + + it('emits epnc:create-failed with reason=invalid when the CSRF token is missing', async () => { + // data-request-token empty AND no window.OC fallback. The script + // should refuse to call fetch and surface a structured invalid + // signal to the host. + document.body.innerHTML = ` +
+
loading
+ +
+ ` + delete window.OC + await importEmbedCreate() + await flushMicrotasks() + + expect(fetch).not.toHaveBeenCalled() + expect(locationReplaceSpy).not.toHaveBeenCalled() + + const payload = parentPostSpy.mock.calls[0][0] + expect(payload.type).toBe('epnc:create-failed') + expect(payload.reason).toBe('invalid') + expect(payload.message).toBe('CSRF request token is missing.') + }) + + it('does not emit succeeded then failed when the server returns a cross-origin embed_url', async () => { + // Regression: an earlier version emitted `epnc:create-succeeded` + // before validating the redirect target. A bad embed_url would then + // throw on `normalizeEmbedRedirectUrl()`, fall into the catch, and + // emit a contradictory `epnc:create-failed` — leaving the host with + // both signals for the same operation. We now validate first. + fetch.mockResolvedValueOnce(jsonResponse({ + embed_url: 'https://evil.example/whatever', + file_id: 777, + pad_id: 'p', + access_mode: 'protected', + })) + + await importEmbedCreate() + await flushMicrotasks() + + // Exactly one event, classified as a server-side response problem + // (not a network error — fetch itself succeeded). + expect(parentPostSpy).toHaveBeenCalledOnce() + const payload = parentPostSpy.mock.calls[0][0] + expect(payload.type).toBe('epnc:create-failed') + expect(payload.reason).toBe('server') + // And no redirect happened. + expect(locationReplaceSpy).not.toHaveBeenCalled() + }) + + it('does not postMessage when not embedded (window.parent === window)', async () => { + // Restore parent === window for this test only. + Object.defineProperty(window, 'parent', { configurable: true, value: window }) + fetch.mockResolvedValueOnce(jsonResponse({ + embed_url: '/embed/by-id/777', + file_id: 777, + pad_id: 'p', + access_mode: 'protected', + })) + + await importEmbedCreate() + await flushMicrotasks() + + expect(parentPostSpy).not.toHaveBeenCalled() + expect(locationReplaceSpy).toHaveBeenCalled() + }) +})