domma-cms 1.7.1 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/CLAUDE.md +30 -0
- package/admin/dist/domma/domma-tools.css +3 -3
- package/admin/dist/domma/domma-tools.min.js +3 -3
- package/admin/js/app.js +2 -2
- package/admin/js/lib/editor-folding.js +6 -6
- package/admin/js/lib/simple-editor.js +8 -8
- package/admin/js/lib/smooth-caret.js +1 -0
- package/admin/js/lib/wordpress-import-arrange.js +1 -0
- package/admin/js/templates/docs/usage-tools.html +13 -1
- package/admin/js/templates/wordpress-import.html +223 -0
- package/admin/js/views/index.js +1 -1
- package/admin/js/views/page-editor.js +15 -8
- package/admin/js/views/wordpress-import.js +38 -0
- package/docs/screenshots/editor-caret-settings.png +0 -0
- package/package.json +2 -2
- package/plugins/_lib/admin/mail/font-controls.js +4 -0
- package/public/js/forms.js +2 -2
- package/public/js/site.js +1 -1
- package/public/js/smooth-caret.js +1 -0
- package/server/routes/api/wordpress-import.js +220 -0
- package/server/server.js +12 -0
- package/server/services/hooks.js +1 -1
- package/server/services/permissionRegistry.js +10 -0
- package/server/services/roles.js +1 -1
- package/server/services/tools.js +2 -1
- package/server/services/wpImport/convert.js +491 -0
- package/server/services/wpImport/fetch.js +264 -0
- package/server/services/wpImport/index.js +832 -0
- package/server/services/wpImport/plan.js +549 -0
- package/server/services/wpImport/store.js +242 -0
- package/server/services/wpImport/wxr.js +167 -0
- package/server/services/wpImport/xml.js +261 -0
- package/server/services/wpImport/zip.js +142 -0
- package/server/templates/page.html +2 -2
package/admin/js/views/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{dashboardView as o}from"./dashboard.js";import{dashboardsView as i}from"./dashboards.js";import{pagesView as r}from"./pages.js";import{pageEditorView as t}from"./page-editor.js";import{settingsView as e}from"./settings.js";import{themeView as m}from"./theme.js";import{notificationsView as p}from"./notifications.js";import{layoutsView as
|
|
1
|
+
import{dashboardView as o}from"./dashboard.js";import{dashboardsView as i}from"./dashboards.js";import{pagesView as r}from"./pages.js";import{pageEditorView as t}from"./page-editor.js";import{settingsView as e}from"./settings.js";import{themeView as m}from"./theme.js";import{notificationsView as p}from"./notifications.js";import{layoutsView as s}from"./layouts.js";import{mediaView as n}from"./media.js";import{loginView as f}from"./login.js";import{usersView as w}from"./users.js";import{userEditorView as c}from"./user-editor.js";import{pluginsView as V}from"./plugins.js";import{pluginCodeView as a}from"./plugin-code.js";import{documentationView as d}from"./documentation.js";import{tutorialsView as l}from"./tutorials.js";import{docViews as u}from"./doc-pages.js";import{pluginGuidesView as E,pluginGuideView as g}from"./plugin-guides.js";import{apiReferenceView as b}from"./api-reference.js";import{collectionsView as h}from"./collections.js";import{collectionEditorView as v}from"./collection-editor.js";import{collectionEntriesView as y}from"./collection-entries.js";import{formsView as j}from"./forms.js";import{formEditorView as k}from"./form-editor.js";import{formSubmissionsView as L}from"./form-submissions.js";import{viewsListView as x}from"./views-list.js";import{viewEditorView as M}from"./view-editor.js";import{viewPreviewView as D}from"./view-preview.js";import{actionsListView as G}from"./actions-list.js";import{actionEditorView as P}from"./action-editor.js";import{proDocsView as R}from"./pro-docs.js";import{blocksView as S}from"./blocks.js";import{blockEditorView as A}from"./block-editor.js";import{componentsView as C}from"./components.js";import{componentEditorView as I}from"./component-editor.js";import{myProfileView as N}from"./my-profile.js";import{rolesView as T}from"./roles.js";import{roleEditorView as U}from"./role-editor.js";import{effectsView as q}from"./effects.js";import{searchView as z}from"./search.js";import{contentApiView as B}from"./content-api.js";import{notesView as F}from"./notes.js";import{todoView as H}from"./todo.js";import{calculatorView as J}from"../lib/calculator.js";import{analyticsView as K}from"./analytics.js";import{seoView as O}from"./seo.js";import{wordpressImportView as Q}from"./wordpress-import.js";import{contactsView as W}from"./contacts.js";import{callNewView as X}from"./call-new.js";import{menusView as Y}from"./menus.js";import{contextMenusView as Z}from"./context-menus.js";import{contextMenuEditorView as _}from"./context-menu-editor.js";import{menuEditorView as $}from"./menu-editor.js";import{menuLocationsView as oo}from"./menu-locations.js";import{projectsView as io}from"./projects.js";import{projectDetailView as ro}from"./project-detail.js";import{projectSettingsView as to}from"./project-settings.js";import{apiTokensView as eo}from"./api-tokens.js";import{apiEndpointsView as mo}from"./api-endpoints.js";import{apiEndpointEditorView as po}from"./api-endpoint-editor.js";const so={templateUrl:"",async onMount(){location.hash="#/menus"}};export const views={projects:io,projectDetail:ro,projectSettings:to,apiTokens:eo,apiEndpoints:mo,apiEndpointEditor:po,dashboard:o,dashboards:i,pages:r,pageEditor:t,settings:e,theme:m,layouts:s,media:n,login:f,users:w,userEditor:c,plugins:V,pluginCode:a,documentation:d,tutorials:l,apiReference:b,collections:h,collectionEditor:v,collectionEntries:y,forms:j,formEditor:k,formSubmissions:L,viewsList:x,viewEditor:M,viewPreview:D,actionsList:G,actionEditor:P,proDocs:R,blocks:S,blockEditor:A,components:C,componentEditor:I,myProfile:N,roles:T,roleEditor:U,effects:q,search:z,contentApi:B,notes:F,todo:H,calculator:J,analytics:K,seo:O,wordpressImport:Q,contacts:W,callNew:X,notifications:p,menus:Y,contextMenus:Z,contextMenuEditor:_,menuEditor:$,menuLocations:oo,menusRedirect:so,pluginGuides:E,pluginGuide:g,...u};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{api as
|
|
1
|
+
import{api as g}from"../api.js";import{CardBuilder as gt}from"../lib/card-builder.js";import{TimelineBuilder as bt}from"../lib/timeline-builder.js";import{attachEditorKeybindings as ht,createToolbar as yt,insertAtCursor as wt}from"../lib/markdown-toolbar.js";import{EffectsBuilder as kt}from"../lib/effects-builder.js";import{attachContextMenu as Et}from"../lib/shortcode-context-menu.js";import{registerCustomInserters as Mt}from"../lib/shortcode-insert.js";import{populateThemeSelect as St}from"../lib/themes.js";import{createFoldingManager as Tt}from"../lib/editor-folding.js";import{openScribeComposer as Ct}from"../lib/scribe-composer.js";import{openCrudTutorial as xt}from"../lib/crud-tutorial.js";import{addBannerAction as j}from"../lib/plugin-chrome.js";import{helpPopovers as Ke}from"../lib/help-popover.js";import{onceClosed as ye,openMenuAt as Ue}from"../lib/tool-kit.js";import{pickMedia as Pt}from"../lib/media-picker.js";import{openPageClone as At}from"../lib/page-clone.js";import{openEditorReference as _e}from"../lib/page-editor-reference.js";import{cleanType as Z,DEFAULT_TYPE as Q,EDITOR_FONTS as Lt,fontHref as Ot,isDefaultType as Dt,LINE_HEIGHTS as Rt,SIZES as Nt,typeVars as Je}from"../lib/editor-type.js";import{getProjectFromHash as Ft}from"../lib/project-context.js";import{attachSmoothCaret as We,getCaretPreference as It,setCaretPreference as Bt}from"../lib/smooth-caret.js";import{SCREEN as $t,bannerSection as zt,draftIsStale as jt,draftKey as Vt,initialProject as qt,makeDraft as Ht,problems as we,readDraft as Kt,snapshot as V}from"../lib/page-editor-arrange.js";import{fmtDateTime as ee}from"../lib/dates.js";function te(o){const i=document.createElement("template");return i.innerHTML=o.trim(),i.content.firstElementChild}const Ut=ee,Ge=o=>ee(o.createdAt)+(o.label?` - ${o.label}`:""),_t=`
|
|
2
2
|
<div class="pe-share">
|
|
3
3
|
<p class="form-hint" style="margin-top:0">Anyone with the link can view this page even while it is unpublished, without
|
|
4
4
|
signing in. The link works for this page only, expires on its own, and can be revoked at any time.</p>
|
|
@@ -30,7 +30,7 @@ import{api as b}from"../api.js";import{CardBuilder as vt}from"../lib/card-builde
|
|
|
30
30
|
.pe-share [hidden] { display: none !important; }
|
|
31
31
|
.pe-share-row { display: flex; align-items: center; gap: .6rem; padding: .5rem 0; border-bottom: 1px solid var(--dm-border-light); }
|
|
32
32
|
.pe-share-text { flex: 1 1 auto; min-width: 0; font-size: .82rem; }
|
|
33
|
-
</style>`;function
|
|
33
|
+
</style>`;function Ye(o){const i=Ot(o);if(!i||document.querySelector(`link[data-pe-font="${CSS.escape(o)}"]`))return;const l=Object.assign(document.createElement("link"),{rel:"stylesheet",href:i});l.dataset.peFont=o,document.head.appendChild(l)}const Jt=`
|
|
34
34
|
<div class="pe-settings">
|
|
35
35
|
<p class="pe-settings-note">How the editor looks to you - kept in this browser, for every page.</p>
|
|
36
36
|
<label class="form-label" for="pe-set-font">Font <span data-help="The lettering in the editor (not on the site). Monospace keeps every character the same width, so shortcodes and Markdown line up."></span></label>
|
|
@@ -43,6 +43,13 @@ import{api as b}from"../api.js";import{CardBuilder as vt}from"../lib/card-builde
|
|
|
43
43
|
</div>
|
|
44
44
|
<label class="pe-settings-switch"><input type="checkbox" class="form-switch" data-model="wrap.value"> Wrap long lines
|
|
45
45
|
<span data-help="Off: a long line runs on and the editor scrolls sideways - each line number is then exactly one line."></span></label>
|
|
46
|
+
<label class="form-label" for="pe-set-caret">Caret movement</label>
|
|
47
|
+
<select id="pe-set-caret" class="form-select" data-model="caret.value">
|
|
48
|
+
<option value="auto">Automatic - follow system motion preference</option>
|
|
49
|
+
<option value="smooth">Smooth</option>
|
|
50
|
+
<option value="native">Native</option>
|
|
51
|
+
</select>
|
|
52
|
+
<p class="pe-settings-note">Smooth enables caret animation across the admin and public site forms in this browser. Automatic follows your system's reduced-motion preference.</p>
|
|
46
53
|
<div class="pe-settings-sample" aria-label="Sample"># A heading
|
|
47
54
|
[card title="Shortcodes line up"]
|
|
48
55
|
The quick brown fox - 0O 1lI {} [] () => ==
|
|
@@ -61,17 +68,17 @@ The quick brown fox - 0O 1lI {} [] () => ==
|
|
|
61
68
|
.pe-settings-sample { white-space: pre-wrap; padding: .75rem 1rem; border-radius: var(--dm-radius-md); border: 1px solid var(--dm-border);
|
|
62
69
|
background: var(--dm-background-alt); font-family: var(--pe-font); font-size: var(--pe-size); line-height: var(--pe-line); }
|
|
63
70
|
.pe-settings-actions { display: flex; gap: .5rem; border-top: 1px solid var(--dm-border); padding-top: .75rem; }
|
|
64
|
-
</style>`;function
|
|
71
|
+
</style>`;function Wt(o){const i=E.slideover({title:"Editor settings",size:"sm",position:"right"}),l=te(`<div>${Jt}</div>`),s=Z(o.peek()),r={font:M.observable(s.font),size:M.observable(String(s.size)),line:M.observable(String(s.line)),wrap:M.observable(s.wrap),caret:M.observable(It())},v=M.computed(()=>Z({font:r.font.value,size:r.size.value,line:r.line.value,wrap:r.wrap.value}));r.isDefault=M.computed(()=>Dt(v.value)&&r.caret.value==="auto");const u=M.effect(()=>Bt(r.caret.value)),p=l.querySelector("#pe-set-font");for(const[b,L]of[["mono","Monospace"],["sans","Other"],["serif",null]]){const $=Lt.filter(P=>P.kind===b),m=L?Object.assign(document.createElement("optgroup"),{label:L}):p.querySelector("optgroup:last-of-type");m.append(...$.map(P=>new Option(P.label,P.id))),L&&p.appendChild(m)}l.querySelector("#pe-set-size").append(...Nt.map(b=>new Option(`${b}px`,String(b)))),l.querySelector("#pe-set-line").append(...Rt.map(b=>new Option({1.3:"Tight",1.45:"Snug",1.6:"Normal",1.8:"Relaxed",2:"Double"}[b]||String(b),String(b))));const c=l.querySelector(".pe-settings-sample"),C=M.effect(()=>{const b=v.value;Ye(b.font);for(const[L,$]of Object.entries(Je(b)))c.style.setProperty(L,$);c.style.whiteSpace=b.wrap?"pre-wrap":"pre",c.style.overflowX=b.wrap?"":"auto",JSON.stringify(b)!==JSON.stringify(Z(o.peek()))&&(o.value=b,ae("type",b))});r.reset=()=>{r.font.value=Q.font,r.size.value=String(Q.size),r.line.value=String(Q.line),r.wrap.value=Q.wrap,r.caret.value="auto"},r.done=()=>i.close(),i.element.appendChild(l);const d=M.applyBindings(r,l),h=Ke(l);return ye(i,()=>{d.dispose(),h(),C?.(),u?.()}),Domma.icons?.scan?.(l),i.open(),i}async function Gt(o){const i=E.slideover({title:"Share preview link",size:"md",position:"right"}),l=te(`<div>${_t}</div>`),s={expiry:M.observable("7d"),note:M.observable(""),creating:M.observable(!1),url:M.observable(""),links:M.observable([]),loaded:M.observable(!1),listError:M.observable("")},r=async()=>{try{const u=await g.pages.previewLinks.list(o);s.links.value=u.filter(p=>!p.revokedAt&&!p.expired).map(p=>({id:p.id,title:p.label||"Untitled link",meta:`Expires ${Ut(p.expiresAt)} - ${p.useCount===1?"1 view":`${p.useCount||0} views`}`})),s.listError.value=""}catch(u){s.listError.value="Could not load existing links: "+(u.message||"unknown error")}finally{s.loaded.value=!0}};s.create=async()=>{s.creating.value=!0;try{const u=await g.pages.previewLinks.create(o,s.expiry.peek(),s.note.peek().trim());s.url.value=u.url,s.note.value="",E.toast("Share link created.",{type:"success"}),await r(),l.querySelector(".pe-share-url")?.select()}catch(u){E.toast("Could not create link: "+(u.message||"unknown error"),{type:"error"})}finally{s.creating.value=!1}},s.copy=async()=>{try{await navigator.clipboard.writeText(s.url.peek()),E.toast("Link copied to clipboard.",{type:"success"})}catch{l.querySelector(".pe-share-url")?.select(),E.toast("Press Ctrl+C to copy the selected link.",{type:"info"})}},s.revoke=async u=>{if(await E.confirm("Revoke this link? Anyone holding it loses access immediately."))try{await g.pages.previewLinks.revoke(u),E.toast("Link revoked.",{type:"success"}),await r()}catch(p){E.toast("Could not revoke: "+(p.message||"unknown error"),{type:"error"})}};const v=M.applyBindings(s,l);ye(i,()=>v.dispose()),i.element.appendChild(l),i.open(),await r()}function Yt(o){return new Promise(i=>{const l=E.slideover({title:"Save Named Version",size:"sm",position:"right"}),s=te(`
|
|
65
72
|
<div style="padding:1rem">
|
|
66
73
|
<label class="form-label" for="pe-version-label">Version Label</label>
|
|
67
74
|
<input id="pe-version-label" type="text" class="form-input" style="margin-bottom:.75rem"
|
|
68
75
|
placeholder="e.g. Initial design, Before restructure" data-model="label.value" data-on-keydown="key">
|
|
69
76
|
<button type="button" class="btn btn-primary" data-on-click="save" data-bind-disabled="saving.value">Save Version</button>
|
|
70
|
-
</div>`);let
|
|
71
|
-
`).map(
|
|
77
|
+
</div>`);let r=!1;const v={label:M.observable(""),saving:M.observable(!1)};v.save=async()=>{v.saving.value=!0;try{await g.versions.create(o,v.label.peek().trim()||null),E.toast("Named version saved.",{type:"success"}),r=!0,l.close()}catch(p){E.toast("Failed to save version: "+p.message,{type:"error"})}finally{v.saving.value=!1}},v.key=p=>{p.key==="Enter"&&(p.preventDefault(),v.save())};const u=M.applyBindings(v,s);ye(l,()=>{u.dispose(),i(r)}),l.element.appendChild(s),l.open(),setTimeout(()=>s.querySelector("input")?.focus(),100)})}let T=null,Xe=!1;function Xt(){Xe||!window.R?.use||(Xe=!0,R.use((o,i,l)=>{const s=i?.path||"",r=o?.path||"",v=$t.test(s)&&r!==s;return v&&T?.path===s&&T.cloneTarget===r?(T.cloneTarget=null,T.keepDraft(),l()):!v||!T||T.path!==s||!T.dirty()?l():E.confirm("You have unsaved changes to this page. Leave anyway? They are kept as a draft for your next visit.").then(u=>{u?(T?.keepDraft(),l()):history.replaceState(null,"","#"+s)})}),M.subscribe("router:afterChange",({to:o}={})=>{if(T&&(o?.path||"")!==T.path){const i=T;T=null,i.dispose()}}))}function Zt(o){return o.split(`
|
|
78
|
+
`).map(i=>i.trimEnd()).join(`
|
|
72
79
|
`).replace(/\n{3,}/g,`
|
|
73
80
|
|
|
74
|
-
`)}const
|
|
81
|
+
`)}const Ze="editor_prefs";function q(){return S.get(Ze)||{viewMode:"split",fullscreen:!1}}function ae(o,i){const l=q();l[o]=i,S.set(Ze,l)}const Qt=["edit","details","seo","dconfig","live","history"],Qe=["split","write","preview"],ea=o=>o.replace(/<\/head>/i,"<style>body.dm-cloaked{opacity:1 !important;transition:none !important}</style></head>");function et(o,i,l=()=>{}){let s=0;try{s=o.contentWindow.scrollY||0}catch{}o.addEventListener("load",()=>{try{o.contentWindow.scrollTo(0,s)}catch{}l()},{once:!0}),o.srcdoc=i}export const pageEditorView={templateUrl:"/admin/js/templates/page-editor.html",async onMount(o){if(Xt(),T){const e=T;T=null,e.dispose()}const i=window.location.hash.replace(/^#/,""),l=[],s=e=>(l.push(e),e);let r=()=>!1,v=()=>{};const u={path:i,dirty:()=>r(),keepDraft:()=>v(),dispose:()=>{l.splice(0).forEach(e=>{try{e()}catch{}})}};T=u;const p=o.get(0),c=()=>T===u&&p.isConnected,C=window.location.hash.match(/#\/pages\/edit(\/[^?]*)/),d=C?C[1]:null,h=!!d,b=E.loader(p,{type:"dots"}),[L,$,m,P,ke,tt,at]=await Promise.all([g.layouts.get().catch(()=>({})),g.settings.get().catch(()=>({})),h?g.pages.get(d).catch(()=>null):null,h?g.pages.list().catch(()=>[]):[],g.projects.list().catch(()=>[]),g.pages.tags().catch(()=>[]),g.listTags.list("pages").then(e=>e.map(t=>t.name)).catch(()=>[])]);if(!c()){b.destroy();return}if(b.destroy(),h&&!m){E.toast("Page not found.",{type:"error"}),R.navigate("/pages");return}const w=e=>p.querySelector(e);if(St(w("#field-theme"),{includeDefault:!0}),await na(w("#field-visibility"),m?.visibility),!c())return;const st=oa(w("#field-layout"),L,m?.layout||"default");for(const e of ke)w("#field-project").append(new Option(e.name||e.slug,e.slug));const Ee=[...Array.isArray(P)?P:[]].sort((e,t)=>(e.title||e.urlPath).localeCompare(t.title||t.urlPath));for(const e of Ee)w("#quick-switch").append(new Option(`${e.title||"Untitled"} (${e.urlPath})`,e.urlPath,!1,e.urlPath===d));const H=q(),a={urlPath:M.observable(d||""),title:M.observable(m?.title||""),description:M.observable(m?.description||""),layout:M.observable(st),status:M.observable(m?.status||"draft"),sortOrder:M.observable(String(m?.sortOrder??99)),showInNav:M.observable(!!m?.showInNav),sidebar:M.observable(!!m?.sidebar),breadcrumbs:M.observable(m?m.breadcrumbs!==!1:!0),bundled:M.observable(!!m?.bundled),tags:M.observable(Array.isArray(m?.tags)?m.tags:[]),adminTags:M.observable(Array.isArray(m?.adminTags)?m.adminTags:[]),category:M.observable(m?.category||""),visibility:M.observable(typeof m?.visibility=="string"&&m.visibility?m.visibility:"public"),project:M.observable(qt({isEdit:h,page:m,projects:ke,fromHash:Ft()||"",urlPath:d||""})),theme:M.observable(m?.theme||""),seoTitle:M.observable(m?.seo?.title||""),seoDesc:M.observable(m?.seo?.description||""),dconfig:M.observable(m?.dconfig?JSON.stringify(m.dconfig,null,2):""),css:M.observable(""),cssOpened:M.observable(!1)},n={bodyRev:M.observable(0),base:M.observable(""),saving:M.observable(!1),tab:M.observable("edit"),viewMode:M.observable(Qe.includes(H.viewMode)?H.viewMode:"split"),fullscreen:M.observable(!!H.fullscreen),cssMode:M.observable(!1),folded:M.observable(!1),liveStatus:M.observable("Showing unsaved changes"),liveWidth:M.observable("full"),versions:M.observable([]),histLoading:M.observable(!1),histError:M.observable(""),compare:M.observable(null)},k=w("#markdown-editor"),F=w("#css-editor");k.value=m?.content||"";const ot=We(k),nt=We(F);s(()=>ot.destroy()),s(()=>nt.destroy());const O=document.createElement("div");O.className="editor-line-numbers editor-line-numbers--foldable",k.parentElement.insertBefore(O,k);const x=Tt(k,O,{onFoldChange:()=>{}});let I=!1;x.setGuard(e=>{I=e});const z=()=>x.getUnfoldedContent(),Me=M.observable(Z(H.type));s(M.effect(()=>{const e=Me.value,t=w("#editor-body");if(t){Ye(e.font);for(const[f,y]of Object.entries(Je(e)))t.style.setProperty(f,y);t.classList.toggle("pe-nowrap",!e.wrap),k.wrap=e.wrap?"soft":"off",F.wrap=e.wrap?"soft":"off"}}));let Se=null;s(()=>{try{Se?.close()}catch{}});const D=()=>(n.bodyRev.value,{urlPath:a.urlPath.value.trim(),title:a.title.value,description:a.description.value,layout:a.layout.value,status:a.status.value,sortOrder:a.sortOrder.value,showInNav:a.showInNav.value,sidebar:a.sidebar.value,breadcrumbs:a.breadcrumbs.value,bundled:a.bundled.value,tags:a.tags.value,adminTags:a.adminTags.value,category:a.category.value,visibility:a.visibility.value,project:a.project.value,theme:a.theme.value,seoTitle:a.seoTitle.value,seoDesc:a.seoDesc.value,dconfig:a.dconfig.value,body:z(),css:a.cssOpened.value?a.css.value:null}),rt=M.computed(()=>V(D())!==n.base.value),it=M.computed(()=>we({urlPath:a.urlPath.value,dconfig:a.dconfig.value})[0]?.text||""),lt=M.computed(()=>n.versions.value.some(e=>e.checked.value)),ct=M.computed(()=>n.versions.value.length>0&&n.versions.value.every(e=>e.checked.value)),dt=M.computed(()=>{const e=n.versions.value.filter(t=>t.checked.value).length;return e>0&&e<n.versions.value.length});r=()=>V(D())!==n.base.peek();let Te=JSON.stringify(a.adminTags.peek());const K=e=>({title:a.title.peek().trim()||"Untitled",description:a.description.peek().trim(),layout:a.layout.peek(),status:a.status.peek(),sortOrder:parseInt(String(a.sortOrder.peek()),10)||99,showInNav:a.showInNav.peek(),sidebar:a.sidebar.peek(),...a.breadcrumbs.peek()?{}:{breadcrumbs:!1},...a.bundled.peek()?{bundled:!0}:{},tags:a.tags.peek(),...h&&JSON.stringify(a.adminTags.peek())===Te?{}:{adminTags:a.adminTags.peek()},category:a.category.peek().trim()||null,visibility:a.visibility.peek()||"public",project:a.project.peek()||"",seo:{title:a.seoTitle.peek().trim(),description:a.seoDesc.peek().trim()},dconfig:e,...a.theme.peek()?{theme:a.theme.peek()}:{}}),se=()=>{const e=a.dconfig.peek().trim();if(!e)return null;try{return JSON.parse(e)}catch{return null}},Ce=()=>a.urlPath.peek().trim()||d||"/",U=w("#markdown-preview");let xe="";g.settings.getCustomCss().then(({css:e})=>{xe=e||""}).catch(()=>{});const Pe=()=>{const e=U?.contentDocument;if(!e?.head)return;const t=a.cssOpened.peek()?a.css.peek():"";if(!t)return;let f=e.getElementById("preview-custom-css");f||(f=e.createElement("style"),f.id="preview-custom-css",e.head.appendChild(f)),f.textContent=t};let oe=null;const _=()=>{clearTimeout(oe),oe=setTimeout(async()=>{if(!(!U||n.viewMode.peek()==="write"))try{const{html:e}=await g.pages.previewFull(Ce(),K(se()),z());c()&&et(U,ea(e),Pe)}catch(e){U.srcdoc='<!doctype html><meta charset="utf-8"><body style="font:14px system-ui;padding:1rem;color:#b91c1c;">Preview failed: '+String(e?.message||"unknown error").replace(/[<&]/g,t=>t==="<"?"<":"&")+"</body>"}},500)},Ae=w("#live-preview-frame");let ne=!0,re=null,ie=!1;const le=async()=>{if(!(ie||!Ae)){ie=!0,n.liveStatus.value="Rendering\u2026";try{const{html:e}=await g.pages.previewFull(Ce(),K(se()),z());if(!c())return;et(Ae,e),ne=!1,n.liveStatus.value="Showing unsaved changes"}catch(e){n.liveStatus.value="Preview failed - "+(e?.message||"unknown error")}finally{ie=!1}}},Le=()=>{ne=!0,n.tab.peek()==="live"&&(clearTimeout(re),re=setTimeout(le,600))};s(()=>{clearTimeout(oe),clearTimeout(re)});const ce=yt(o.find("#markdown-editor"),o.find("#editor-toolbar"),{spacerDefault:$?.layoutOptions?.spacerSize??40});Et(k);const de=p.querySelector(".editor-toolbar-right");de.querySelectorAll(".editor-view-btn[data-mode]").forEach(e=>{const t=e.getAttribute("data-mode");e.classList.remove("active"),e.setAttribute("data-bind-class",`viewMode.value === '${t}' && 'active'`),e.setAttribute("data-on-click",`setMode('${t}')`)}),w("#fullscreen-btn")?.setAttribute("data-on-click","toggleFullscreen");const pt=te(`
|
|
75
82
|
<span class="pe-toolbar-extras" style="display:contents">
|
|
76
83
|
<button type="button" class="editor-view-btn" data-tip="Edit Custom CSS" data-on-click="toggleCss"
|
|
77
84
|
data-bind-class="cssMode.value && 'active'"><span>CSS</span></button>
|
|
@@ -83,5 +90,5 @@ The quick brown fox - 0O 1lI {} [] () => ==
|
|
|
83
90
|
<span class="pe-fold-icon" hidden data-bind-hidden="!folded.value"><span data-icon="chevrons-right" data-icon-size="10"></span></span>
|
|
84
91
|
</button>
|
|
85
92
|
<span class="editor-toolbar-sep"></span>
|
|
86
|
-
</span>`);de.prepend(
|
|
87
|
-
`).map((
|
|
93
|
+
</span>`);de.prepend(pt);const Oe=e=>(Array.isArray(e)?e:[]).map(t=>({value:t,label:t})),De=w("#field-tags");let J=null;J=De?E.pillbox(De,{data:Oe(tt),value:a.tags.peek(),creatable:!0,searchable:!0,placeholder:"Add tag\u2026",onChange:()=>{J&&(a.tags.value=[...J.getValue()])}}):null;const Re=w("#field-admin-tags");let W=null;W=Re?E.pillbox(Re,{data:Oe(at),value:a.adminTags.peek(),creatable:!0,searchable:!0,placeholder:"Add admin tag\u2026",onChange:()=>{W&&(a.adminTags.value=[...W.getValue()])}}):null;let pe=!1,ue=null;const G=async()=>{pe=!0,n.histLoading.value=!0,n.histError.value="";try{const e=(await g.versions.list(d)).versions||[];if(!c())return;n.versions.value=e.map(t=>({...t,when:ee(t.createdAt),author:t.author||"",label:t.label||"",checked:M.observable(!1)}))}catch(e){n.histError.value="Failed to load versions: "+e.message}finally{n.histLoading.value=!1}},ve=e=>n.versions.peek().find(t=>t.filename===e),fe=async e=>{try{const t=await g.versions.get(d,e.filename);ue=e.filename,n.compare.value={label:Ge(e),old:t.content,current:z()}}catch(t){E.toast("Failed to load version: "+t.message,{type:"error"})}},Ne=async e=>{if(!(!e||!await E.confirm("Restore this version? The current page content will be overwritten (a pre-restore snapshot will be saved automatically).")))try{await g.versions.restore(d,e),E.toast("Page restored. Reloading editor\u2026",{type:"success"}),R.navigate("/pages"),setTimeout(()=>R.navigate(`/pages/edit${d}`),300)}catch(t){E.toast("Restore failed: "+t.message,{type:"error"})}},ut=async e=>{if(await E.confirm("Delete this version? This cannot be undone."))try{await g.versions.delete(d,e.filename),E.toast("Version deleted.",{type:"success"}),await G()}catch(t){E.toast("Failed to delete: "+t.message,{type:"error"})}},Fe=()=>{n.bodyRev.value=n.bodyRev.peek()+1},Ie=()=>{a.cssOpened.peek()||(a.css.value=xe,a.cssOpened.value=!0,ht(F))},Be=e=>{for(const t of["urlPath","title","description","layout","status","sortOrder","category","visibility","project","theme","seoTitle","seoDesc","dconfig"])a[t].value=e[t]==null?"":String(e[t]);for(const t of["showInNav","sidebar","breadcrumbs","bundled"])a[t].value=!!e[t];a.tags.value=Array.isArray(e.tags)?e.tags:[],J?.setValue(a.tags.peek()),Array.isArray(e.adminTags)&&(a.adminTags.value=e.adminTags),W?.setValue(a.adminTags.peek()),x.unfoldAll(),I=!0,k.value=e.body==null?"":String(e.body),I=!1,x.refresh(),e.css!=null&&(Ie(),a.css.value=String(e.css)),Fe()};n.base.value=V(D());const Y=Vt(d||"");v=()=>{if(r())try{S.set(Y,Ht(D(),n.base.peek()))}catch{}};const me=()=>{try{S.remove?S.remove(Y):S.set(Y,null)}catch{}},$e=e=>{r()&&(v(),e.preventDefault(),e.returnValue="")};window.addEventListener("beforeunload",$e),s(()=>window.removeEventListener("beforeunload",$e));const ze=async()=>{if(n.saving.peek())return;const e=D(),t=we(e);if(t.length){E.toast(t[0].text,{type:"warning"}),p.querySelectorAll("#editor-meta-tabs .tab-item")[Qt.indexOf(t[0].field==="dconfig"?"dconfig":"details")]?.click();return}const f=(e.dconfig||"").trim(),y=K(f?JSON.parse(f):null),A=a.cssOpened.peek();n.saving.value=!0;try{if(h){const N={frontmatter:y,body:e.body};e.urlPath!==d&&(N.newUrlPath=e.urlPath),await g.pages.update(d,N)}else await g.pages.create({urlPath:e.urlPath,frontmatter:y,body:e.body});A&&await g.settings.saveCustomCss(a.css.peek()),E.toast(A?"Page and CSS saved.":"Page saved.",{type:"success"}),me(),Te=JSON.stringify(a.adminTags.peek()),n.base.value=V(D()),h?e.urlPath!==d&&R.navigate(`/pages/edit${e.urlPath}`):R.navigate("/pages")}catch(N){E.toast(`Save failed: ${N.message||"Unknown error"}`,{type:"error"})}finally{n.saving.value=!1}},je=p.querySelector(".dm-plugin-body")||p,vt={...a,...n,dirty:rt,problem:it,anySelected:lt,allSelected:ct,someSelected:dt,isEdit:h,savedPath:d||"",hasPages:Ee.length>0},ft={save:()=>ze(),discard:async()=>{!r()||!await E.confirm("Discard the changes you have not saved?")||(Be(JSON.parse(n.base.peek())),me())},switchPage:e=>{const t=e?.target?.value;t&&R.navigate("/pages/edit"+t)},showTab:e=>{n.tab.value=e,e==="live"&&ne&&le(),e==="history"&&h&&!pe&&G()},setMode:e=>{n.cssMode.peek()&&(n.cssMode.value=!1),n.viewMode.value=e,ae("viewMode",e),e!=="write"&&_()},toggleFullscreen:()=>{n.fullscreen.value=!n.fullscreen.peek(),ae("fullscreen",n.fullscreen.peek())},toggleCss:()=>{n.cssMode.peek()?(n.cssMode.value=!1,n.viewMode.value=Qe.includes(q().viewMode)?q().viewMode:"split",x.refresh()):(Ie(),n.cssMode.value=!0,n.viewMode.value="split")},formatMarkdown:()=>{const e=k.selectionStart,t=Zt(z());x.unfoldAll(),I=!0,k.value=t,I=!1,k.selectionStart=k.selectionEnd=Math.min(e,t.length),k.dispatchEvent(new Event("input")),x.refresh(),E.toast("Markdown formatted.",{type:"success"})},toggleFold:()=>{n.folded.peek()?x.unfoldAll():x.foldAll(),n.folded.value=!n.folded.peek()},setWidth:e=>{n.liveWidth.value=e;const t=w("#live-preview-stage");t&&(t.scrollTop=0)},refreshLive:()=>le(),share:async()=>{if(!h){E.toast("Save the page first - a share link needs a page to point at.",{type:"warning"});return}await Gt(d)},toggleAll:e=>{const t=!!e?.target?.checked;n.versions.peek().forEach(f=>{f.checked.value=t})},deleteSelected:async()=>{const e=n.versions.peek().filter(f=>f.checked.peek()).map(f=>f.filename),t=e.length;if(!(!t||!await E.confirm(`Delete ${t} version${t>1?"s":""}? This cannot be undone.`)))try{await g.versions.bulkDelete(d,e),E.toast(`${t} version${t>1?"s":""} deleted.`,{type:"success"}),await G()}catch(f){E.toast("Delete failed: "+f.message,{type:"error"})}},saveNamed:async()=>{await Yt(d)&&c()&&(n.tab.peek()==="history"?await G():pe=!1)},closeCompare:()=>{n.compare.value=null,ue=null},restoreCompared:()=>Ne(ue)};de.querySelectorAll("[data-tip]").forEach(e=>E.tooltip(e,{content:e.getAttribute("data-tip"),position:"top"}));const mt=M.applyBindings(vt,je,{methods:ft});s(()=>mt.dispose()),E.tabs(w("#editor-meta-tabs"));let ge=null;s(()=>clearTimeout(ge)),k.addEventListener("input",()=>{x.refresh(),O.scrollTop=k.scrollTop,!I&&(clearTimeout(ge),ge=setTimeout(Fe,120),_(),Le())}),k.addEventListener("scroll",()=>{O.scrollTop=k.scrollTop}),F.addEventListener("scroll",()=>{O.scrollTop=F.scrollTop});let Ve=!0;s(M.effect(()=>{for(const e of Object.keys(a))e!=="css"&&e!=="cssOpened"&&a[e].value;if(n.bodyRev.value,Ve){Ve=!1,_();return}_(),Le()}));let be=null;if(s(()=>clearTimeout(be)),s(M.effect(()=>{const e=a.css.value;n.cssMode.value&&(ta(O,e,F),clearTimeout(be),be=setTimeout(Pe,400))})),h){const e=w(".pe-version-list"),t=E.contextMenu(e,{cursor:"glyph",match:"tr[data-file]",inherit:!1,density:"compact",items:f=>{const y=ve(f.dataset.file);return y?[{header:Ge(y)},{label:"Compare with the editor",icon:"columns",action:()=>fe(y)},{label:"Restore this version\u2026",icon:"rotate-ccw",action:()=>Ne(y.filename)},{type:"divider"},{label:"Delete\u2026",icon:"trash",danger:!0,action:()=>ut(y)}]:[]}});s(()=>t?.destroy?.()),e.addEventListener("click",f=>{const y=f.target.closest("tr[data-file]");if(!y||f.target.matches("input"))return;const A=f.target.closest(".pe-kebab");if(A){Ue(A);return}const N=ve(y.dataset.file);N&&fe(N)}),e.addEventListener("keydown",f=>{const y=f.target.closest("tr[data-file]");if(y){if(f.key==="F10"&&f.shiftKey)f.preventDefault(),Ue(y);else if(f.key==="Enter"&&f.target===y){const A=ve(y.dataset.file);A&&fe(A)}}})}aa(w("#editor-body"),w("#editor-divider"),s),Mt({image:e=>(n.cssMode.value=!1,sa(e)),scribe:e=>Ct(e),card:e=>gt.open(e,{onMediaBrowse:()=>g.media.list().catch(()=>[])}),timeline:e=>bt.open(e,{onPreview:async t=>{try{return await g.pages.preview(t)}catch{return null}},onListCollections:()=>g.collections.list().catch(()=>[]),onGetCollection:t=>g.collections.get(t).catch(()=>null)})}),ce.onEffects(e=>kt.open(e)),ce.onHelp(()=>_e()),ce.onTutorial(e=>xt(e));const qe=e=>{(e.ctrlKey||e.metaKey)&&!e.shiftKey&&!e.altKey&&e.key.toLowerCase()==="s"&&c()&&(e.preventDefault(),ze())};document.addEventListener("keydown",qe),s(()=>document.removeEventListener("keydown",qe));const X=p.querySelector(".dm-plugin-banner-name");s(M.effect(()=>{const e=zt(h,a.title.value,d);if(!c()||(document.title=`${e} - Domma CMS`,!X))return;const t=X.lastChild;t&&t.nodeType===3&&X.querySelector(".dm-plugin-banner-sep")?t.textContent=e:X.textContent=`Pages \u203A ${e}`})),j(o,{id:"page-editor-back",icon:"arrow-left",title:"Back to Pages",onClick:()=>R.navigate("/pages")}),j(o,{id:"page-editor-help",icon:"help-circle",title:"Editor reference",onClick:()=>_e()}),h&&j(o,{id:"page-editor-view",icon:"external-link",title:"View on the site",onClick:()=>window.open(a.urlPath.peek()||d,"_blank","noopener")});const he=j(o,{id:"page-editor-clone",icon:"copy",title:"Clone page",onClick:()=>{if(!h||n.saving.peek()||!c())return;const e=D(),t=we({...e,urlPath:d});if(t.length){E.toast(t[0].text,{type:"warning"});return}const f=At({page:m,frontmatter:{...K(se()),adminTags:a.adminTags.peek()},body:e.body,source:d,pages:P,onCloned:y=>{c()&&(u.cloneTarget=`/pages/edit${y}`,R.navigate(u.cloneTarget))}});s(()=>f.close())}});he&&(he.disabled=!h,h||(he.title="Save the page before cloning it")),j(o,{id:"page-editor-settings",icon:"settings",title:"Editor settings - font, size, spacing",onClick:()=>{Se=Wt(Me)}}),s(Ke(je,{position:"bottom"})),Domma.icons.scan();let He=null;try{He=S.get(Y)}catch{}const B=Kt(He);if(B&&(me(),V(B.state)!==n.base.peek())){const e=B.at?` (${ee(B.at)})`:"",t=jt(B,n.base.peek())?" The saved page has changed since - restoring puts your version back over it (the saved one stays in History).":"";await E.confirm(`You left unsaved changes to this page${e}. Restore them?${t}`)&&c()&&Be(B.state)}}};function ta(o,i,l){o.replaceChildren(...String(i).split(`
|
|
94
|
+
`).map((s,r)=>{const v=document.createElement("span");v.className="editor-line-number-row";const u=document.createElement("span");u.className="fold-toggle fold-toggle--empty";const p=document.createElement("span");return p.className="editor-line-num",p.textContent=String(r+1),v.append(u,p),v})),o.scrollTop=l.scrollTop}function aa(o,i,l){if(!o||!i)return;const s=15,r=85,v=d=>Math.min(r,Math.max(s,d));let u=v(parseFloat(q().splitRatio)||50);const p=d=>{o.style.setProperty("--dm-editor-split",u+"%"),i.setAttribute("aria-valuenow",String(Math.round(u))),d&&ae("splitRatio",u)};p(!1);const c=d=>{const h=o.getBoundingClientRect();h.width&&(u=v((d.clientX-h.left)/h.width*100)),p(!1)},C=()=>{document.removeEventListener("pointermove",c),document.removeEventListener("pointerup",C),o.classList.remove("is-resizing"),i.classList.remove("is-dragging"),p(!0)};l(C),i.addEventListener("pointerdown",d=>{window.matchMedia("(max-width: 768px)").matches||(d.preventDefault(),o.classList.add("is-resizing"),i.classList.add("is-dragging"),document.addEventListener("pointermove",c),document.addEventListener("pointerup",C))}),i.addEventListener("dblclick",()=>{u=50,p(!0)}),i.addEventListener("keydown",d=>{const h=d.shiftKey?10:2;let b=u;if(d.key==="ArrowLeft")b=u-h;else if(d.key==="ArrowRight")b=u+h;else if(d.key==="Home")b=s;else if(d.key==="End")b=r;else if(d.key==="Enter"||d.key===" ")b=50;else return;d.preventDefault(),u=v(b),p(!0)})}async function sa(o){const i=o.selectionStart,l=o.selectionEnd,s=await Pt({title:"Upload or insert an image"});if(!s||!o.isConnected)return;let r=s.split("/").pop().split("?")[0];try{r=decodeURIComponent(r)}catch{}const v=r.replace(/[\\[\]]/g,"\\$&"),u=s.replace(/\(/g,"%28").replace(/\)/g,"%29").replace(/ /g,"%20");o.setSelectionRange(i,l),wt(o,``),o.dispatchEvent(new Event("input",{bubbles:!0}))}function oa(o,i,l){const s=Object.entries(i||{});return o?(o.replaceChildren(...s.map(([r,v])=>new Option(v?.label||r,r))),s.some(([r])=>r===l)?l:s[0]?.[0]||l):l}async function na(o,i){if(!o)return;let l=[];try{const c=await g.get("/collections/roles/entries?limit=100");l=Array.isArray(c)?c:c?.entries||[]}catch{}const s=l.map(c=>({name:c.data?.name,label:c.data?.label||c.data?.name,level:c.data?.level??99})).filter(c=>c.name&&c.level!==0).sort((c,C)=>C.level-c.level),r=(c,C)=>Object.assign(document.createElement("option"),{value:c,textContent:C}),v=()=>{typeof i=="string"&&i&&![...o.options].some(c=>c.value===i)&&o.append(r(i,`${i} (custom)`))};if(!s.length)return v();const u=document.createElement("optgroup");u.label="Role and above",s.forEach(c=>u.append(r(c.name,`${c.label} and above`)));const p=document.createElement("optgroup");p.label="Role only",s.forEach(c=>p.append(r(`=${c.name}`,`${c.label} only`))),o.replaceChildren(r("public","Public - everyone"),u,p,r("private","Private - super-admin only")),v()}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import{apiRequest as q,getToken as Wt}from"../api.js";import{addBannerAction as Zt}from"../lib/plugin-chrome.js";import{helpPopovers as Fe}from"../lib/help-popover.js";import{onceClosed as Jt,openMenuAt as J,sidebarCount as C,viewLifetime as Vt}from"../lib/tool-kit.js";import{SHAPES as Ke,whileLoading as We}from"../lib/loading.js";import{fmtDateTime as Gt}from"../lib/dates.js";import{applyChoice as Xt,choiceItems as Yt,cleanOptions as pe,counts as Ze,defaultOptions as Se,draftKey as Qt,draftQuestion as ea,historyRow as ta,isDirty as aa,kindTabs as oa,liveDrafts as Je,optionsSnapshot as Ve,PLAN_FILTERS as Ge,planMatches as Xe,planRow as sa,planTiles as Ye,progressPercent as na,progressText as ra,REPORT_FILTERS as Qe,reportMatches as et,reportRow as ia,stagedRow as la,withDraft as tt}from"../lib/wordpress-import-arrange.js";const at=/^#\/wordpress-import\/?(\?.*)?$/,V="#/wordpress-import",T="/import/wordpress",de=400,ca=`
|
|
2
|
+
<div class="wpx-row" data-id="{{id}}" tabindex="0">
|
|
3
|
+
<span class="wpx-main"><strong>{{title}}</strong><small>{{meta}}</small></span>
|
|
4
|
+
<span class="wpx-chips"><span class="wpx-chip" data-bind-class="pillClass">{{pill}}</span></span>
|
|
5
|
+
<button type="button" class="wpx-kebab" aria-label="Actions for {{title}}" title="Actions">\u22EE</button>
|
|
6
|
+
</div>`,ot=`
|
|
7
|
+
<div class="wpx-row" data-key="{{key}}" tabindex="0">
|
|
8
|
+
<span class="wpx-main">
|
|
9
|
+
<strong>{{title}}</strong>
|
|
10
|
+
<small>{{meta}}</small>
|
|
11
|
+
<span class="wpx-where" data-bind-hidden="!where">{{where}}</span>
|
|
12
|
+
<span class="wpx-reason" data-bind-hidden="!reason">{{reason}}</span>
|
|
13
|
+
<span class="wpx-rebuild" data-bind-hidden="!rebuild" title="Needs rebuilding">{{rebuild}}</span>
|
|
14
|
+
</span>
|
|
15
|
+
<span class="wpx-chips"><span class="wpx-chip" data-bind-class="pillCls">{{pill}}</span></span>
|
|
16
|
+
<button type="button" class="wpx-kebab" aria-label="Actions for {{title}}" title="Actions">\u22EE</button>
|
|
17
|
+
</div>`,st=`
|
|
18
|
+
<div class="wpx-row" data-from="{{from}}" tabindex="0">
|
|
19
|
+
<span class="wpx-main"><span class="wpx-where">{{from}} \u2192 {{to}}</span><span class="wpx-reason" data-bind-hidden="!reason">{{reason}}</span></span>
|
|
20
|
+
<span class="wpx-chips"><span class="wpx-chip" data-bind-class="pillClass">{{pill}}</span></span>
|
|
21
|
+
<button type="button" class="wpx-kebab" aria-label="Actions for {{from}}" title="Actions">\u22EE</button>
|
|
22
|
+
</div>`,pa=`
|
|
23
|
+
<div class="wpx-row" data-id="{{id}}" tabindex="0">
|
|
24
|
+
<span class="wpx-main"><strong>{{title}}</strong><small>{{meta}}</small><span class="wpx-reason">{{summary}}</span></span>
|
|
25
|
+
<span class="wpx-chips"><span class="wpx-chip" data-bind-class="pillClass">{{pill}}</span></span>
|
|
26
|
+
<button type="button" class="wpx-kebab" aria-label="Actions for {{title}}" title="Actions">\u22EE</button>
|
|
27
|
+
</div>`,nt='<div class="wpx-tile"><strong>{{value}}</strong><small>{{label}} <span data-bind-hidden="!sub">{{sub}}</span></small></div>',rt='<p class="wpx-alert"><span>{{text}}</span></p>',Le='<button type="button" data-on-click="$root.pickSeg($data)" data-bind-class="cls">{{label}} <b>{{count}}</b></button>',y=Vt(at);function it(l,n,r){return new Promise((w,g)=>{const c=new XMLHttpRequest,L=new FormData;L.append("file",n),c.open("POST",`/api${l}`);const v=Wt();v&&c.setRequestHeader("Authorization",`Bearer ${v}`),c.upload.onprogress=p=>{p.lengthComputable&&r(p.loaded/p.total)},c.onload=()=>{let p=null;try{p=JSON.parse(c.responseText)}catch{}c.status>=200&&c.status<300?w(p):g(new Error(p?.error||`The upload failed (${c.status}).`))},c.onerror=()=>g(new Error("The upload failed - the connection dropped.")),c.send(L),y.add(()=>{c.readyState!==4&&c.abort()})})}const ue=l=>l>=1048576?`${Math.round(l/1048576)} MB`:`${Math.max(1,Math.round(l/1024))} KB`,H=l=>String(l??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""");function da(l){const n=document.createElement("template");return n.innerHTML=l.trim(),n.content.firstElementChild}async function me(l,n){const r=E.confirm(l,n);let w=null;try{w=[...document.querySelectorAll(".dm-dialog-overlay")].at(-1)??null}catch{}const g=y.add(()=>{if(!w)return;const c=w;w=null;try{c.querySelector(".dm-dialog-cancel")?.click(),c.remove()}catch{}});try{return await r}finally{w=null,g()}}function lt(l){navigator.clipboard?.writeText(l).then(()=>E.toast("Copied.",{type:"success"}),()=>E.toast(l,{type:"info",duration:8e3}))}function fe(l,n,r,w){const g=E.contextMenu(l,{cursor:"glyph",match:n,inherit:!1,density:"compact",items:r});y.add(()=>g?.destroy?.());const c=v=>{const p=v.target.closest(n);if(!p||!l.contains(p))return;const d=v.target.closest(".wpx-kebab");if(d){J(d);return}w?.(p)},L=v=>{const p=v.target.closest(n);p&&(v.key==="F10"&&v.shiftKey?(v.preventDefault(),J(p)):v.key==="Enter"&&v.target===p&&w&&(v.preventDefault(),v.stopPropagation(),w(p)))};l.addEventListener("click",c),l.addEventListener("keydown",L),y.add(()=>{l.removeEventListener("click",c),l.removeEventListener("keydown",L)})}export const wordpressImportView={templateUrl:"/admin/js/templates/wordpress-import.html",async onMount(l){y.reset();const n=l.get(0).querySelector(".wpx")||l.get(0),r=()=>at.test(location.hash)&&n.isConnected;let w="";try{w=S.get("auth_user")?.id||""}catch{}const g=Qt(w),c=()=>{try{return g?S.get(g):null}catch{return null}},L=e=>{try{g&&S.set(g,e)}catch{}},v=n.querySelector(".wpx-loading");let p;try{p=await We(v,q(`${T}/status`),Ke.rows)}catch(e){if(!n.isConnected)return;v&&(v.hidden=!0);const t=n.querySelector(".wpx-failed");t&&(t.textContent=`Could not load the importer: ${e.message||e}. Reload the page to try again.`,t.hidden=!1);return}if(!r())return;v&&(v.hidden=!0),n.querySelector(".wpx-body").hidden=!1;const d=M.observable(p.staged||[]),P=M.observable(p.history||[]),N=M.observable(p.blog||{available:!1}),z=p.limits||{},b=M.observable(p.running||null),u=M.observable(""),k=M.observable(null),G=M.observable(!1),ve=M.observable(!1),he=M.observable(""),h=M.observable(Se(N.peek().available)),X=M.observable("all"),D=M.observable(""),we=M.observable(""),Y=M.observable(""),j=M.observable(""),Q=M.observable(""),ee=M.observable(!1);let te=!0;const ua=()=>d.peek().find(e=>e.id===u.peek())||null,be=M.computed(()=>d.value.find(e=>e.id===u.value)||null),ct=M.computed(()=>u.value?"preview":"start"),R=M.computed(()=>!!N.value.available),qe=M.computed(()=>!!u.value&&aa(h.value,R.value)),ae=e=>M.computed({read:()=>h.value[e]!==!1,write:t=>{h.value={...h.peek(),[e]:!!t}}}),A=e=>{h.value={...h.peek(),...e}},_=e=>{try{return Gt(e)}catch{return e}},pt=M.computed(()=>d.value.map(e=>la(e,_))),dt=M.computed(()=>P.value.map(e=>ta(e,_))),B=M.computed(()=>(k.value?.items||[]).map(e=>({...e,choice:h.value.choices?.[e.key]}))),ut=M.computed(()=>oa(B.value).map(e=>({...e,cls:D.value===e.id?"is-on":"",seg:"kind"}))),mt=M.computed(()=>Ze(B.value,Ge,Xe,D.value)),ft=M.computed(()=>Ge.map(e=>({...e,count:mt.value[e.id],cls:X.value===e.id?"is-on":"",seg:"filter"}))),ge=M.computed(()=>{const e=we.value.trim().toLowerCase();return B.value.filter(t=>(!D.value||t.kind===D.value)&&Xe(t,X.value)&&(!e||`${t.title} ${t.target} ${t.oldUrl}`.toLowerCase().includes(e)))}),vt=M.computed(()=>ge.value.slice(0,de).map(e=>{const t=sa(e);return{...t,pillCls:`${t.pillClass}${e.choice?" is-chosen":""}`}})),ht=M.computed(()=>ge.value.length>de?`Showing the first ${de} of ${ge.value.length} - search or filter to find the rest.`:""),wt=M.computed(()=>B.value.length?"Nothing matches.":"This export has nothing to import."),bt=M.computed(()=>Ye(k.value)),gt=M.computed(()=>(k.value?.notes||[]).map(e=>({text:e}))),yt=M.computed(()=>(k.value?.redirects||[]).slice(0,de).map(e=>({...e,pill:e.action==="skip"?"Skipped":e.action==="update"?"Update":"New",pillClass:e.action==="skip"?"is-muted":"is-ok"}))),kt=M.computed(()=>String((k.value?.redirects||[]).filter(e=>e.action!=="skip").length)),xt=M.computed(()=>k.value?.site?.title||be.value?.siteTitle||"WordPress export"),Mt=M.computed(()=>{const e=be.value,t=k.value?.site;return[t?.url||e?.siteUrl,e?.fileName,t?.wxrVersion&&`WXR ${t.wxrVersion}`,e?.at&&`uploaded ${_(e.at)}`].filter(Boolean).join(" \xB7 ")}),Tt=M.computed(()=>R.value?`Posts go to ${N.value.basePath||"/blog"}.`:N.value.reason||"No Blog is switched on here."),oe=M.computed(()=>be.value?.uploads||null),Et=M.computed(()=>{const e=oe.value;return e?`${e.fileName} - ${e.files} file${e.files===1?"":"s"}, ${ue(e.size)}${e.unsafe?`. ${e.unsafe} with unsafe names (like ../) were ignored`:""}.`:`Zip the wp-content/uploads folder and upload it here (up to ${ue(z.uploads||0)}).`}),$t=M.computed(()=>oe.value?"Replace the zip":"Upload the uploads zip"),Re=M.computed(()=>!!k.value&&!b.value&&!G.value&&!(h.value.media==="zip"&&!oe.value)),St=M.computed(()=>b.value?`Importing${b.value.siteTitle?` ${b.value.siteTitle}`:""}\u2026`:""),Lt=M.computed(()=>ra(b.value)),Ce=M.computed(()=>na(b.value)),qt=M.observable(ue(z.wxr||0)),Pe=n.querySelector(".wpx-running .wpx-bar-fill");y.add(M.effect(()=>{Pe&&(Pe.style.width=`${Ce.value}%`)}));async function ye(){const e=await q(`${T}/status`);return r()?(d.value=e.staged||[],P.value=e.history||[],N.value=e.blog||{available:!1},e.running&&!b.peek()&&(b.value=e.running,K()),e):null}let se=0,ke=null;async function ne({first:e=!1}={}){const t=u.peek();if(!t)return;const a=++se;he.value="";const s=q(`${T}/staged/${t}/preview`,{method:"POST",body:JSON.stringify({options:pe(h.peek(),R.peek())})});try{let f;if(e?(G.value=!0,f=await We(n.querySelector(".wpx-plan-loading"),s,Ke.table)):(ve.value=!0,f=await s),a!==se||!r()||u.peek()!==t)return;k.value=f.plan}catch(f){if(a!==se||!r())return;if(f.status===404){E.toast("That upload has gone - upload the export again.",{type:"warning"}),F(),ye().catch(()=>{});return}he.value=`Could not read the export: ${f.message||f}`}finally{a===se&&(G.value=!1,ve.value=!1)}}let re="";y.add(M.effect(()=>{const e=`${u.value}|${Ve(h.value)}`;if(e===re)return;const t=re.split("|")[0];re=e,!(!u.value||t!==u.value)&&(clearTimeout(ke),ke=setTimeout(()=>ne(),250))})),y.add(()=>clearTimeout(ke)),y.add(M.effect(()=>{const e=u.value,t=h.value,a=qe.value;if(!e||!te||!g)return;const s=c();L(tt(s,w,e,a?{options:pe(t,!0),at:new Date().toISOString(),was:e}:null))}));const xe=e=>{g&&L(tt(c(),w,e,null))};async function ie(e){if(!d.peek().some(m=>m.id===e))return;const t=d.peek().find(m=>m.id===e),a=Je(c(),d.peek().map(m=>m.id),w).items[e];if(te=!a,k.value=null,X.value="all",D.value="",we.value="",h.value=Se(R.peek()),re=`${e}|${Ve(h.peek())}`,u.value=e,await ne({first:!0}),!r()||u.peek()!==e||!a)return;await new Promise(m=>setTimeout(m,0));const s=ea({when:a.at?_(a.at):"",file:t?.fileName||""}),f=await me(H(s.text),{title:s.title,confirmText:s.confirmText,cancelText:s.cancelText});!r()||u.peek()!==e||(te=!0,f?xe(e):h.value=pe(a.options,R.peek()))}function F(){u.value="",k.value=null,te=!0}async function De(e=u.peek()){const t=d.peek().find(a=>a.id===e);if(t&&await me(`Discard the upload of <strong>${H(t.siteTitle||t.fileName)}</strong>? Nothing has been imported from it.`,{title:"Discard upload",confirmText:"Discard",danger:!0}))try{if(await q(`${T}/staged/${e}`,{method:"DELETE"}),xe(e),!r())return;u.peek()===e&&F(),d.value=d.peek().filter(a=>a.id!==e),C(V)}catch(a){E.toast(`Not discarded: ${a.message||a}`,{type:"error"})}}async function Ae(e){if(!e)return;if(j.value="",!/\.(xml|zip)$/i.test(e.name)){j.value="Upload the .xml file WordPress exports, or a .zip holding it.";return}if(z.wxr&&e.size>z.wxr*1.2&&/\.xml$/i.test(e.name)){j.value=`That file is over ${ue(z.wxr)}.`;return}const t=n.querySelector(".wpx-start .wpx-upload .wpx-bar-fill");Y.value=`Uploading ${e.name}\u2026`;try{const a=await it(`${T}/upload`,e,s=>{t&&(t.style.width=`${Math.round(s*100)}%`),Y.value=s<1?`Uploading ${e.name} - ${Math.round(s*100)}%`:`Reading ${e.name}\u2026`});if(!r())return;d.value=[a,...d.peek().filter(s=>s.id!==a.id)],C(V),await ie(a.id)}catch(a){r()&&(j.value=a.message||String(a))}finally{r()&&(Y.value="",t&&(t.style.width="0"))}}async function Rt(e){const t=u.peek();if(!e||!t)return;if(!/\.zip$/i.test(e.name)){E.toast("Zip the wp-content/uploads folder and upload the .zip.",{type:"warning"});return}const a=n.querySelector(".wpx-zip-fill");Q.value=`Uploading ${e.name}\u2026`;try{const s=await it(`${T}/staged/${t}/uploads`,e,f=>{a&&(a.style.width=`${Math.round(f*100)}%`),Q.value=f<1?`Uploading ${e.name} - ${Math.round(f*100)}%`:`Checking ${e.name}\u2026`});if(!r())return;d.value=d.peek().map(f=>f.id===s.id?s:f),u.peek()===t&&ne()}catch(s){r()&&E.toast(s.message||String(s),{type:"error"})}finally{r()&&(Q.value="",a&&(a.style.width="0"))}}async function Ct(){const e=u.peek();try{const t=await q(`${T}/staged/${e}/uploads`,{method:"DELETE"});if(!r())return;d.value=d.peek().map(a=>a.id===t.id?t:a),ne()}catch(t){E.toast(`Not removed: ${t.message||t}`,{type:"error"})}}async function Pt(){const e=u.peek(),t=k.peek();if(!e||!t||!Re.peek())return;const s=Ye(t).filter(m=>m.value&&m.id!=="comments").map(m=>`${m.value} ${m.label.toLowerCase()}`);if(!(!await me(`Import <strong>${H(t.site?.title||"this export")}</strong>: ${H(s.join(", ")||"nothing new")}? Pages edited here since an earlier import are left alone unless you chose Overwrite.`,{title:"Run the import",confirmText:"Run import"})||!r()||u.peek()!==e))try{const m=await q(`${T}/staged/${e}/run`,{method:"POST",body:JSON.stringify({options:pe(h.peek(),R.peek())})});if(xe(e),!r())return;b.value={...m.job,siteTitle:t.site?.title||""},F(),C(V),K()}catch(m){E.toast(`The import did not start: ${m.message||m}`,{type:"error"})}}let Me=null;function K(){clearTimeout(Me);const e=b.peek();e&&(Me=setTimeout(async()=>{try{const t=await q(`${T}/jobs/${e.id}`);if(!r())return;if(b.value={...t.job,siteTitle:e.siteTitle},t.job.status==="running")return K();if(b.value=null,await ye().catch(()=>{}),C(V),C("#/pages"),C("#/media"),!r())return;t.job.status==="done"?(E.toast("The import has finished - here is its report.",{type:"success"}),le(t.job.reportId)):(E.toast(`The import stopped: ${t.job.error||"an error"}`,{type:"error"}),t.job.reportId&&le(t.job.reportId))}catch(t){if(!r())return;if(t.status===404){b.value=null,ye().catch(()=>{});return}K()}},700))}y.add(()=>clearTimeout(Me)),b.peek()&&K();let O=!1;async function le(e){if(O||!e)return;O=!0;let t;try{t=await q(`${T}/history/${e}`)}catch(o){O=!1,E.toast(`Could not open the report: ${o.message||o}`,{type:"error"});return}if(!r()){O=!1;return}let a;try{a=E.slideover({title:H(`Report - ${t.run?.siteTitle||"WordPress import"}`),size:"lg",position:"right"})}catch(o){throw O=!1,o}const s=da(`
|
|
28
|
+
<div class="wpx wpx-report">
|
|
29
|
+
<p class="wpx-note" data-bind-text="meta.value"></p>
|
|
30
|
+
<div class="wpx-tiles" data-each="tiles.value key=id"></div>
|
|
31
|
+
<div class="wpx-note-list" data-each="notes.value key=text"></div>
|
|
32
|
+
<div class="wpx-bar-row"><div class="wpx-seg" role="group" aria-label="Show" data-each="filters.value key=id"></div>
|
|
33
|
+
<span data-help-title="The report" data-help="Every item of the export and what happened to it. Kept as HTML: the page had tables, columns or other layout Markdown cannot say - it shows as it did, and edits as HTML. Needs rebuilding: shortcodes and page builders this site has no equivalent for. Right-click (or \u22EE, or Shift+F10) an item to open it."></span></div>
|
|
34
|
+
<p class="wpx-note" data-bind-hidden="rows.value.length">Nothing matches.</p>
|
|
35
|
+
<div class="wpx-list wpx-report-items" data-each="rows.value key=key"></div>
|
|
36
|
+
<div class="wpx-card-title" data-bind-hidden="!redirects.value.length">Redirects</div>
|
|
37
|
+
<div class="wpx-list wpx-report-redirects" data-each="redirects.value key=from"></div>
|
|
38
|
+
</div>`);s.querySelector(".wpx-tiles").innerHTML=nt,s.querySelector(".wpx-note-list").innerHTML=rt,s.querySelector(".wpx-seg").innerHTML=Le,s.querySelector(".wpx-report-items").innerHTML=ot,s.querySelector(".wpx-report-redirects").innerHTML=st;const f=M.observable(t.run?.summary?.failed?"failed":"all"),m=t.items||[],je=Ze(m,Qe,et),Z=t.run?.summary||{},Ee=o=>Object.values(Z.kinds||{}).reduce((i,x)=>i+(x[o]||0),0),Ut={meta:M.observable([_(t.run?.at),t.run?.by,t.run?.file,t.run?.durationMs?`${Math.max(1,Math.round(t.run.durationMs/1e3))} s`:""].filter(Boolean).join(" \xB7 ")),tiles:M.observable([{id:"made",label:"Made",value:Ee("created"),sub:""},{id:"updated",label:"Updated",value:Ee("updated"),sub:""},{id:"kept",label:"Left as is",value:Ee("kept"),sub:""},{id:"redirects",label:"Redirects",value:(Z.redirects?.created||0)+(Z.redirects?.updated||0),sub:""},{id:"comments",label:"Comments",value:Z.comments||0,sub:""},{id:"failed",label:"Failed",value:Z.failed||0,sub:""}]),notes:M.observable((t.notes||[]).map(o=>({text:o}))),filters:M.computed(()=>Qe.filter(o=>o.id==="all"||je[o.id]).map(o=>({...o,count:je[o.id],cls:f.value===o.id?"is-on":""}))),rows:M.computed(()=>m.filter(o=>et(o,f.value)).slice(0,1e3).map(o=>{const i=ia(o);return{...i,pillCls:i.pillClass}})),redirects:M.observable((t.redirects||[]).filter(o=>o.result!=="skipped").slice(0,1e3).map(o=>({...o,reason:o.error||"",pill:o.result==="failed"?"Failed":o.result==="updated"?"Updated":"Added",pillClass:o.result==="failed"?"is-bad":"is-ok"})))};a.element.appendChild(s);const Ht=M.applyBindings(Ut,s,{methods:{pickSeg:o=>{f.value=o.id}}}),Nt=Fe(s),_e=new Map(m.map(o=>[o.key,o])),zt=s.querySelector(".wpx-report-items"),jt=o=>{const i=_e.get(o.dataset.key);if(!i)return[];const x=typeof i.target=="string"&&i.target.startsWith("/")?i.target:"",$=[{header:i.title}];return x&&i.result!=="failed"&&i.result!=="skipped"&&$.push({label:"Open on the site",icon:"external-link",action:()=>window.open(x,"_blank","noopener")}),x&&i.kind==="page"&&(i.result==="created"||i.result==="updated"||i.result==="kept")&&$.push({label:"Edit the page",icon:"edit",action:()=>{a.close(),location.hash=`#/pages/edit${x}`}}),/^https?:\/\//i.test(i.oldUrl)&&$.push({label:"Open the old address",icon:"link",action:()=>window.open(i.oldUrl,"_blank","noopener,noreferrer")}),x&&$.push({label:"Copy the new address",icon:"copy",action:()=>lt(new URL(x,location.origin).href)}),$},_t=E.contextMenu(zt,{cursor:"glyph",match:".wpx-row",inherit:!1,density:"compact",items:jt}),Bt=s.querySelector(".wpx-report-redirects"),Ft=E.contextMenu(Bt,{cursor:"glyph",match:".wpx-row",inherit:!1,density:"compact",items:o=>[{header:o.dataset.from},...o.dataset.from.includes("*")?[]:[{label:"Try it on the site",icon:"external-link",action:()=>window.open(o.dataset.from,"_blank","noopener")}],{label:"Open SEO \u203A Redirects",icon:"corner-up-right",action:()=>{a.close(),location.hash="#/seo/redirects"}}]}),$e=o=>{const i=o.target.closest(".wpx-kebab");if(i){J(i);return}const x=o.target.closest(".wpx-report-items .wpx-row");if(!x)return;const $=_e.get(x.dataset.key);$?.target?.startsWith("/")&&$.result!=="failed"&&$.result!=="skipped"&&window.open($.target,"_blank","noopener")},Be=o=>{const i=o.target.closest(".wpx-row");i&&(o.key==="F10"&&o.shiftKey?(o.preventDefault(),J(i)):o.key==="Enter"&&o.target===i&&(o.preventDefault(),o.stopPropagation(),$e({target:i})))};s.addEventListener("click",$e),s.addEventListener("keydown",Be);const Kt=y.add(()=>a.close());Jt(a,()=>{O=!1,Ht.dispose(),Nt(),_t?.destroy?.(),Ft?.destroy?.(),s.removeEventListener("click",$e),s.removeEventListener("keydown",Be),Kt()}),a.open(),I.scan(s)}async function Dt(e){const t=P.peek().find(a=>a.id===e);if(t&&await me(`Remove the import of <strong>${H(t.siteTitle||t.source)}</strong> from this list? Everything it imported stays on the site.`,{title:"Remove from the history",confirmText:"Remove"}))try{if(await q(`${T}/history/${e}`,{method:"DELETE"}),!r())return;P.value=P.peek().filter(a=>a.id!==e),C(V)}catch(a){E.toast(`Not removed: ${a.message||a}`,{type:"error"})}}n.querySelector(".wpx-staged").innerHTML=ca,n.querySelector(".wpx-items").innerHTML=ot,n.querySelector(".wpx-redirects").innerHTML=st,n.querySelector(".wpx-history").innerHTML=pa,n.querySelector(".wpx-tiles").innerHTML=nt,n.querySelector(".wpx-note-list").innerHTML=rt,n.querySelector(".wpx-kinds").innerHTML=Le,n.querySelector(".wpx-filters").innerHTML=Le;const At={mode:ct,job:b,jobTitle:St,jobText:Lt,jobPct:Ce,staged:d,stagedRows:pt,historyRows:dt,plan:k,planLoading:G,planError:he,refreshing:ve,opt:h,blogAvailable:R,blogNote:Tt,dirty:qe,canRun:Re,siteTitle:xt,siteMeta:Mt,tiles:bt,notes:gt,kinds:ut,filters:ft,rows:vt,moreText:ht,emptyText:wt,redirectRows:yt,redirectCount:kt,query:we,uploadText:Y,uploadError:j,dropping:ee,wxrLimit:qt,zipInfo:oe,zipText:Et,zipButton:$t,zipUploadText:Q,optComments:ae("comments"),optMenus:ae("menus"),optRedirects:ae("redirects"),optDrafts:ae("drafts")},It=M.applyBindings(At,n,{methods:{back:()=>F(),discard:()=>De(),run:()=>Pt(),resetChoices:()=>{h.value=Se(R.peek())},postsBlog:()=>A({posts:"blog"}),postsPages:()=>A({posts:"pages"}),postsSkip:()=>A({posts:"skip"}),mediaDownload:()=>A({media:"download"}),mediaZip:()=>A({media:"zip"}),mediaSkip:()=>A({media:"skip"}),removeZip:()=>Ct(),pickSeg:e=>{e.seg==="kind"?D.value=e.id:X.value=e.id}}});y.add(()=>It.dispose()),y.add(Fe(n,{position:"bottom"}));const W=n.querySelector(".wpx-file"),Ie=()=>{const e=W.files?.[0];W.value="",Ae(e)};W.addEventListener("change",Ie);const U=n.querySelector(".wpx-drop"),Oe=e=>{e.preventDefault(),ee.value=!0},Ue=()=>{ee.value=!1},He=e=>{e.preventDefault(),ee.value=!1,Ae(e.dataTransfer?.files?.[0])};U.addEventListener("dragover",Oe),U.addEventListener("dragleave",Ue),U.addEventListener("drop",He);const ce=n.querySelector(".wpx-zip-file"),Ne=()=>{const e=ce.files?.[0];ce.value="",Rt(e)};ce.addEventListener("change",Ne),y.add(()=>{W.removeEventListener("change",Ie),U.removeEventListener("dragover",Oe),U.removeEventListener("dragleave",Ue),U.removeEventListener("drop",He),ce.removeEventListener("change",Ne)}),fe(n.querySelector(".wpx-staged"),".wpx-row",e=>[{header:d.peek().find(t=>t.id===e.dataset.id)?.siteTitle||"Upload"},{label:"Preview the import",icon:"eye",action:()=>ie(e.dataset.id)},{label:"Discard the upload",icon:"trash-2",action:()=>De(e.dataset.id)}],e=>ie(e.dataset.id));const Ot=(e,t)=>{h.value={...h.peek(),choices:Xt(h.peek().choices,e,t)}};fe(n.querySelector(".wpx-items"),".wpx-row",e=>{const t=B.peek().find(s=>s.key===e.dataset.key);if(!t)return[];const a=[{header:t.title}];for(const s of Yt(t,t.choice))a.push({label:s.label,icon:s.icon,action:()=>Ot(t.key,s.id)});return t.oldUrl&&/^https?:\/\//i.test(t.oldUrl)&&a.push({label:"Open the old address",icon:"link",action:()=>window.open(t.oldUrl,"_blank","noopener,noreferrer")}),t.target?.startsWith("/")&&(t.action==="update"||t.action==="keep")&&a.push({label:"Open it on this site",icon:"external-link",action:()=>window.open(t.target,"_blank","noopener")}),a.length===1&&a.push({label:"Nothing to change here",icon:"info",disabled:!0,action:()=>{}}),a},e=>J(e.querySelector(".wpx-kebab")||e)),fe(n.querySelector(".wpx-redirects"),".wpx-row",e=>[{header:e.dataset.from},{label:"Copy the old address",icon:"copy",action:()=>lt(e.dataset.from)}],null),fe(n.querySelector(".wpx-history"),".wpx-row",e=>[{header:P.peek().find(t=>t.id===e.dataset.id)?.siteTitle||"Import"},{label:"Open the report",icon:"file-text",action:()=>le(e.dataset.id)},{label:"Remove from the history",icon:"trash-2",action:()=>Dt(e.dataset.id)}],e=>le(e.dataset.id)),Zt(l,{id:"wpx-upload",icon:"upload",title:"Upload an export",onClick:()=>{u.peek()&&F(),setTimeout(()=>W.click(),0)}}),I.scan(n);const Te=Je(c(),d.peek().map(e=>e.id),w);Te.pruned&&g&&L({owner:w,items:Te.items});const ze=Object.entries(Te.items).sort((e,t)=>String(t[1].at).localeCompare(String(e[1].at)))[0];ze&&!b.peek()&&await ie(ze[0])}};
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "domma-cms",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"description": "File-based CMS powered by Domma and Fastify. Run npx domma-cms my-site to create a new project.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "server/server.js",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"@fastify/rate-limit": "^10.3.0",
|
|
85
85
|
"@fastify/static": "9.1.1",
|
|
86
86
|
"bcryptjs": "^3.0.3",
|
|
87
|
-
"domma-js": "0.
|
|
87
|
+
"domma-js": "0.53.0",
|
|
88
88
|
"dotenv": "^17.2.3",
|
|
89
89
|
"fastify": "5.8.5",
|
|
90
90
|
"gray-matter": "^4.0.3",
|
|
@@ -28,6 +28,8 @@ const CARET_ATTR = 'data-dm-font-caret';
|
|
|
28
28
|
* @param {object} editor - an `E.editor(..., {mode: 'rich'})` instance
|
|
29
29
|
* @returns {{destroy: Function}|null} null where the editor or E.dropdown is absent
|
|
30
30
|
*/
|
|
31
|
+
const attachSmoothCaret = (await import('/admin/js/lib/smooth-caret.js').catch(() => ({}))).attachSmoothCaret;
|
|
32
|
+
|
|
31
33
|
export function installFontControls(editor) {
|
|
32
34
|
const content = editor?._editorEl;
|
|
33
35
|
const toolbar = editor?._toolbarEl;
|
|
@@ -320,8 +322,10 @@ export function installFontControls(editor) {
|
|
|
320
322
|
};
|
|
321
323
|
window.addEventListener('keydown', onEscape, true);
|
|
322
324
|
|
|
325
|
+
const smoothCaret = attachSmoothCaret?.(content);
|
|
323
326
|
return {
|
|
324
327
|
destroy() {
|
|
328
|
+
smoothCaret?.destroy();
|
|
325
329
|
window.removeEventListener('keydown', onEscape, true);
|
|
326
330
|
scope.removeEventListener('input', tidy, true);
|
|
327
331
|
for (const menu of menus) {
|
package/public/js/forms.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
2
|
-
`).replace(/<[^>]+>/g,""))}function A(n){return String(n).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}function q(n,o){let t=!1;n.addEventListener("dm-form-end",function(e){const i=e.detail&&e.detail.validation;if(i&&!i.valid){markInvalidFields(n,i.missing),showValidationPopup(i);return}t||(t=!0,n.classList.add("fb-form-loading"),Promise.resolve(o(e.detail&&e.detail.values||{})).catch(function(){}).finally(function(){t=!1,n.classList.remove("fb-form-loading")}))})}function C(n,o,t){const e=n.querySelector(".fb-form-success, .fb-form-error");e&&e.remove();const i=document.createElement("div");i.className=t==="success"?"fb-form-success":"fb-form-error",i.textContent=o,n.appendChild(i)}function B(n,o){if(n._formLogicRuntime=o,n._formLogicObserver&&(n._formLogicObserver.disconnect(),n._formLogicObserver=null),!n.parentNode||typeof MutationObserver>"u"||n._formLogicObserved)return;n._formLogicObserved=!0;const t=new MutationObserver(function(e){for(const i of e)for(const r of i.removedNodes)if(r===n||r.nodeType===1&&r.contains&&r.contains(n)){n._formLogicRuntime&&n._formLogicRuntime.destroy(),t.disconnect();return}});t.observe(n.parentNode,{childList:!0,subtree:!1}),n._formLogicObserver=t}function $(n){const o=n&&n.logic;return!!(o&&(o.visibility||o.requirement))}function R(n,o){const t={};return n.forEach(function(e){if(e.type==="page-break"||e.type==="spacer"||!e.name)return;const i=e.type==="chooser"||!!(e.variant&&["select","radio","checkbox-group"].includes(e.type)),r=e.type==="checkbox"?"boolean":e.type==="date"||e.type==="icon"||e.type==="image"?"string":i?"chooser":e.type,l={...e.formConfig||{}};l.span==="full"&&o&&(l.span=o);const s={type:r,label:e.label||e.name,required:$(e)?!1:e.required||!1,options:e.options,formConfig:{...e.placeholder&&{placeholder:e.placeholder},...e.helper&&{helperText:e.helper},...e.tooltip&&{tooltip:e.tooltip},...l}};e.minLength!==void 0&&(s.minLength=e.minLength),e.maxLength!==void 0&&(s.maxLength=e.maxLength),e.min!==void 0&&(s.min=e.min),e.max!==void 0&&(s.max=e.max),i&&(e.variant&&(s.variant=e.variant),(e.multiple||e.type==="checkbox-group")&&(s.multiple=!0),e.density&&(s.density=e.density),e.columns&&(s.columns=e.columns),e.accent&&(s.accent=e.accent),e.accentStyle&&(s.accentStyle=e.accentStyle),e.glow&&(s.glow=!0),e.glowColour&&(s.glowColour=e.glowColour),e.shadow&&(s.shadow=e.shadow),e.shadowColour&&(s.shadowColour=e.shadowColour)),t[e.name]=s}),t}function G(n){const o={};return n.forEach(function(t){if(!(!t.name||t.type==="page-break"||t.type==="spacer")&&(t.type==="select"||t.type==="multiselect")&&t.required){const e=(t.options||[])[0];e&&(o[t.name]=typeof e=="object"?e.value:e)}}),o}function O(n,o){(o||[]).forEach(function(t){if(t.type!=="date"||!t.name)return;const e=n.querySelector('[name="'+t.name+'"]');e&&e.type!=="date"&&(e.type="date")})}const z="padding:.45rem .7rem;border:1px solid var(--dm-border);border-radius:var(--dm-radius-md,6px);background:transparent;color:inherit;cursor:pointer;font:inherit;font-size:.85em;flex-shrink:0;line-height:1.2;";function P(n){const o=document.createElement("div");o.style.cssText="position:fixed;inset:0;z-index:9999;background:rgba(0,0,0,.55);display:flex;align-items:center;justify-content:center;padding:20px;";const t=document.createElement("div");t.style.cssText="background:var(--dm-surface-overlay,var(--dm-surface));color:var(--dm-text);border:1px solid var(--dm-border);border-radius:10px;width:100%;max-width:560px;max-height:75vh;display:flex;flex-direction:column;gap:.6rem;padding:1rem;box-shadow:var(--dm-shadow-lg,0 12px 40px rgba(0,0,0,.35));";const e=document.createElement("div");e.style.cssText="display:flex;align-items:center;justify-content:space-between;gap:.5rem;";const i=document.createElement("strong");i.textContent=n;const r=document.createElement("button");r.type="button",r.textContent="\xD7",r.setAttribute("aria-label","Close"),r.style.cssText=z+"font-size:1rem;padding:.15rem .55rem;",e.appendChild(i),e.appendChild(r),t.appendChild(e);const l=document.createElement("div");l.style.cssText="display:flex;flex-direction:column;gap:.6rem;overflow:hidden;min-height:0;",t.appendChild(l),o.appendChild(t);const s=c=>{c.key==="Escape"&&a()};function a(){document.removeEventListener("keydown",s),o.remove()}return r.addEventListener("click",a),o.addEventListener("click",c=>{c.target===o&&a()}),document.addEventListener("keydown",s),document.body.appendChild(o),{body:l,close:a}}function Q(n){const{body:o,close:t}=P("Choose an icon"),e=document.createElement("input");e.type="text",e.placeholder="Search icons\u2026",e.className="form-input",e.style.cssText="padding:.45rem .6rem;border:1px solid var(--dm-border);border-radius:var(--dm-radius-md,6px);background:var(--dm-input-bg,transparent);color:var(--dm-text);",o.appendChild(e);const i=document.createElement("div");i.style.cssText="display:grid;grid-template-columns:repeat(auto-fill,minmax(46px,1fr));gap:4px;overflow-y:auto;min-height:0;",o.appendChild(i);const r=window.I&&typeof I.list=="function"?I.list():[],l=r.map(s=>{const a=document.createElement("button");a.type="button",a.title=s,a.dataset.iconName=s,a.style.cssText="display:flex;align-items:center;justify-content:center;padding:.55rem 0;border:1px solid transparent;border-radius:6px;background:transparent;color:inherit;cursor:pointer;";const c=document.createElement("span");return c.setAttribute("data-icon",s),c.setAttribute("data-icon-size","18"),a.appendChild(c),a.addEventListener("mouseenter",()=>{a.style.borderColor="var(--dm-border)"}),a.addEventListener("mouseleave",()=>{a.style.borderColor="transparent"}),a.addEventListener("click",()=>{t(),n(s)}),i.appendChild(a),a});if(!r.length){const s=document.createElement("p");s.textContent="Icon engine unavailable - type an icon name into the field instead.",i.appendChild(s)}window.I?.scan?.(i),e.addEventListener("input",()=>{const s=e.value.trim().toLowerCase();l.forEach(a=>{a.style.display=!s||a.dataset.iconName.includes(s)?"":"none"})}),e.focus()}async function X(){const n=typeof S<"u"&&S.get?S.get("auth_token"):null;if(!n)return{error:"auth"};const o=i=>fetch("/api/media",{headers:{Authorization:"Bearer "+i}});let t;try{if(t=await o(n),t.status===401&&S.get("auth_refresh_token")){const i=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:S.get("auth_refresh_token")})});if(i.ok){const{token:r}=await i.json();S.set("auth_token",r),t=await o(r)}}}catch{return{error:"load"}}if(!t.ok)return{error:t.status===401||t.status===403?"auth":"load"};const e=await t.json().catch(()=>[]);return{files:(Array.isArray(e)?e:[]).filter(i=>/\.(png|jpe?g|gif|webp|svg|avif)$/i.test(i.name||i.url||""))}}function Z(n){const{body:o,close:t}=P("Choose an image"),e=document.createElement("p");e.textContent="Loading media library\u2026",e.style.cssText="margin:0;font-size:.85em;color:var(--dm-text-muted);",o.appendChild(e),X().then(({files:i,error:r})=>{if(r){e.textContent=r==="auth"?"Sign in with media access to browse the library - or paste an image URL into the field.":"Could not load the media library - paste an image URL into the field instead.";return}if(!i.length){e.textContent="No images in the media library yet.";return}e.remove();const l=document.createElement("div");l.style.cssText="display:grid;grid-template-columns:repeat(auto-fill,minmax(104px,1fr));gap:8px;overflow-y:auto;min-height:0;",i.forEach(s=>{const a=document.createElement("button");a.type="button",a.title=s.name||s.url,a.style.cssText="display:flex;flex-direction:column;gap:.25rem;padding:.35rem;border:1px solid var(--dm-border);border-radius:8px;background:transparent;color:inherit;cursor:pointer;min-width:0;";const c=document.createElement("img");c.src=s.url,c.alt=s.name||"",c.loading="lazy",c.style.cssText="width:100%;height:72px;object-fit:cover;border-radius:5px;";const m=document.createElement("span");m.textContent=s.name||s.url,m.style.cssText="font-size:.68em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;",a.appendChild(c),a.appendChild(m),a.addEventListener("click",()=>{t(),n(s.url)}),l.appendChild(a)}),o.appendChild(l)})}function D(n,o){n.value=o,n.dispatchEvent(new Event("input",{bubbles:!0})),n.dispatchEvent(new Event("change",{bubbles:!0}))}function M(n){const o=document.createElement("div");return o.style.cssText="display:flex;align-items:center;gap:.5rem;",n.parentNode.insertBefore(o,n),o.appendChild(n),n.style.flex="1",n.style.minWidth="0",o}function V(n){const o=document.createElement("button");return o.type="button",o.textContent=n,o.style.cssText=z,o}function ee(n){if(!window.I||typeof I.list!="function")return;const o=M(n),t=document.createElement("span");t.style.cssText="display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;flex-shrink:0;border:1px solid var(--dm-border);border-radius:var(--dm-radius-md,6px);";const e=()=>{t.textContent="";const r=n.value.trim();if(!r)return;const l=document.createElement("span");l.setAttribute("data-icon",r),l.setAttribute("data-icon-size","16"),t.appendChild(l),window.I?.scan?.(t)};n.addEventListener("input",e),e();const i=V("Browse");i.addEventListener("click",()=>Q(r=>{D(n,r),e()})),o.appendChild(t),o.appendChild(i)}function te(n){const o=M(n),t=document.createElement("img");t.alt="",t.style.cssText="width:2rem;height:2rem;flex-shrink:0;object-fit:cover;border:1px solid var(--dm-border);border-radius:var(--dm-radius-md,6px);display:none;";const e=()=>{const r=n.value.trim();t.style.display=r?"":"none",r&&(t.src=r)};n.addEventListener("input",e),e();const i=V("Browse");i.addEventListener("click",()=>Z(r=>{D(n,r),e()})),o.appendChild(t),o.appendChild(i)}export function enhancePickerFields(n,o){const t=typeof n=="string"?document.querySelector(n):n;t&&(o||[]).forEach(function(e){if(!e.name||e.type!=="icon"&&e.type!=="image")return;const i=t.querySelector('[name="'+e.name+'"]');!i||i.dataset.pickerEnhanced||(i.dataset.pickerEnhanced="1",e.type==="icon"?ee(i):te(i))})}function N(n,o){const t=document.createElement("div");t.className="fb-form-honeypot",t.setAttribute("aria-hidden","true");const e=document.createElement("input");e.name=n.querySelector('[name="website"]')?"_website":"website",t.dataset.hpName=e.name,e.type="text",e.tabIndex=-1,e.autocomplete="url",e.placeholder="https://",t.appendChild(e);const i=document.createElement("input");i.name="_t",i.type="hidden",i.value=o||Date.now(),t.appendChild(i),n.appendChild(t)}function W(n,o){const t=n.querySelector("form");if(!t)return;const e=Array.from(t.querySelectorAll(".form-group"));let i=0;o.forEach(function(r){if(r.type==="spacer"){const l=document.createElement("div");l.className="fb-spacer";const s=e[i];if(s)t.insertBefore(l,s);else{const a=t.querySelector('[type="submit"]');a?t.insertBefore(l,a):t.appendChild(l)}}else r.type!=="page-break"&&i++})}function L(n,o){!n||!o||n.querySelectorAll('input[type="file"]').forEach(function(t){t.name&&t.files&&t.files.length&&o.set(t.name,Array.from(t.files))})}function x(n,o,t,e,i,r){const l=i||e,s=l.querySelector(".fb-form-honeypot"),a=s?.querySelector('input[type="text"]')?.value||"",c=s?.querySelector('[name="_t"]')?.value||"",m=Object.assign({},o);delete m._t,s?.dataset.hpName&&delete m[s.dataset.hpName];const d=new Map(r||[]);L(l,d);const u=d.size>0;let p,b;if(u){const h=new FormData;for(const[g,v]of Object.entries(m))v!=null&&(d.has(g)||h.append(g,typeof v=="object"?JSON.stringify(v):String(v)));for(const[g,v]of d)for(const w of v)h.append(g,w,w.name);h.append("_hp",a),h.append("_t",c),p=h,b={}}else{const h=Object.assign({},m,{_hp:a,_t:c});p=JSON.stringify(h),b={"Content-Type":"application/json"}}return fetch("/api/forms/submit/"+n,{method:"POST",headers:b,body:p}).then(h=>h.json().then(g=>({ok:h.ok,body:g}))).then(h=>{h.ok&&h.body.ok?(e.textContent="",C(e,h.body.message||t.successMessage||"Thank you.","success")):C(e,h.body.error||"Something went wrong.","error")}).catch(()=>{C(e,"Unable to submit. Please check your connection.","error")})}function ne(n,o,t,e,i){const r=document.createElement("form");r.noValidate=!0,o.forEach(function(a){const c=document.createElement("div");c.className="form-group",c.style.marginBottom="1.25rem",a.name&&(c.dataset.field=a.name);const m=document.createElement("label");if(m.className="form-label",m.textContent=a.label||a.name,a.required){const u=document.createElement("span");u.textContent=" *",u.style.color="var(--dm-danger)",m.appendChild(u)}let d;a.type==="textarea"?(d=document.createElement("textarea"),d.rows=a.formConfig?.rows||4,d.className="form-input"):a.type==="select"?(d=document.createElement("select"),d.className="form-input",(a.options||[]).forEach(function(u){const p=document.createElement("option");p.value=typeof u=="string"?u:u.value??"",p.textContent=typeof u=="string"?u:u.label||p.value,d.appendChild(p)})):(d=document.createElement("input"),d.type=a.type==="icon"||a.type==="image"?"text":a.type||"text",d.className="form-input",a.placeholder&&(d.placeholder=a.placeholder),a.type==="file"&&(a.file?.accept&&(d.accept=a.file.accept),a.multiple&&(d.multiple=!0))),d.name=a.name,d.required=a.required||!1,c.appendChild(m),c.appendChild(d),r.appendChild(c)}),t.honeypot&&N(r);const l=document.createElement("button");if(l.type="submit",l.className="btn btn-primary",l.textContent=t.submitText||"Submit",K(t,"stacked").length||H(t)){const a=document.createElement("div");a.className="form-group form-buttons",a.appendChild(l),r.appendChild(a)}else r.appendChild(l);if(r.addEventListener("submit",function(a){a.preventDefault();const c={};if(o.forEach(function(m){const d=r.querySelector('[name="'+m.name+'"]');d&&(c[m.name]=d.value)}),window.FormLogicEngine&&i){const m=window.FormLogicEngine,d=[],u=[];if(o.forEach(function(p){if(m.evaluateFieldVisibility(p,c)==="hidden"){delete c[p.name];return}const h=m.evaluateFieldRequirement(p,c),g=c[p.name];h&&(!g||!String(g).trim())&&d.push(p.label||p.name);const v=m.validateField(p,g||"",c);v.length&&u.push(v[0].message)}),d.length||u.length){const p=[];d.length&&p.push("Required: "+d.join(", ")),u.length&&p.push(u.join("; ")),C(n,p.join(". "),"error");return}}if(n.dataset.triggerBlocked==="true"){const m=n._formLogicRuntime,d=m&&m.submitBlock?m.submitBlock().reasons:[];C(n,d.join(" ")||"This form cannot be submitted yet.","error");return}n.classList.add("fb-form-loading"),l.disabled=!0,x(e,c,t,n,r).finally(function(){n.classList.remove("fb-form-loading"),l.disabled=!1})}),n.appendChild(r),_(r,t,"stacked"),T(r,t,o),enhancePickerFields(n,o),q(n,function(a){return x(e,a,t,n,r)}),window.FormLogicEngine&&i&&F(o)){const a=new window.FormLogicEngine.FormLogicRuntime(i,n);a.init(),n._formLogicRuntime=a}}function oe(n){const o=n.getAttribute("data-form");o&&fetch("/api/forms/"+o+"/public").then(t=>{if(!t.ok)throw new Error("Form not found: "+o);return t.json()}).then(t=>{const e=t.fields||[],i=t.settings||{},r=document.createElement("div");r.className="fb-form-wrapper",n.appendChild(r);const l=e.some(s=>s.type==="page-break");if(typeof Domma<"u"&&Domma.forms){const s=Number(i.columns)===3?3:2,a=i.layout||"grid";if(l&&Domma.forms.wizard){const c=J(e,t.title),m=c.map(f=>f.fields),d=c.map(function(f){return{title:f.title,description:f.description,fields:R(f.fields,s)}});let u=0,p=null,b=null;const h=Date.now(),g=new Map,v=function(f){const y=window.FormLogicEngine;if(!y||!y.validateFields)return;const k=y.validateFields(r,m[u]||[]);k.valid||(f.preventDefault(),f.stopImmediatePropagation(),markInvalidFields(r,k.missing),showValidationPopup(k))},w=function(){const f=m[u]||e;if(U(r,i),T(r,i,f),O(r,f),enhancePickerFields(r,f),f.some(y=>y.type==="spacer")&&W(r,f),i.honeypot){const y=r.querySelector("form");y&&N(y,h)}window.FormLogicEngine&&F(e)&&(p&&p.destroy(),p=new window.FormLogicEngine.FormLogicRuntime(t,r,{baseValues:b&&b.getStepData?b.getStepData():{}}),p.init(),B(r,p)),r.querySelectorAll(".wizard-next, .wizard-submit").forEach(y=>y.addEventListener("click",v,!0))};b=Domma.forms.wizard(r,{schema:{steps:d},layout:a,columns:s,finishText:i.submitText||"Finish",onStepChange:function(f){L(r,g),u=f,setTimeout(w,0)},onSubmit:function(f){return L(r,g),x(o,f,i,r,null,g)}}),q(r,function(f){return L(r,g),x(o,f,i,r,null,g)}),r.addEventListener("dm-form-trigger",function(f){if(!f.detail||f.detail.type!=="goto-step"||!b||!b.goToStep)return;const y=Number(f.detail.action.step);!isFinite(y)||y<0||y>=d.length||y!==u&&b.goToStep(y)}),Promise.resolve(b).then(w)}else if(Domma.forms.render){const c=R(e,s),m=G(e);q(r,function(u){return x(o,u,i,r,null)});const d=Domma.forms.render(r,c,m,{submitText:i.submitText||"Submit",layout:a,columns:s,onSubmit:function(u){return x(o,u,i,r,null)}});Promise.resolve(d).then(function(){if(_(r,i,a),T(r,i,e),O(r,e),enhancePickerFields(r,e),window.FormLogicEngine&&F(e)){const u=new window.FormLogicEngine.FormLogicRuntime(t,r);u.init(),B(r,u)}if(e.some(u=>u.type==="spacer")&&W(r,e),i.honeypot){const u=r.querySelector("form");u&&N(u)}})}}else ne(r,e.filter(s=>s.type!=="page-break"&&s.type!=="spacer"),i,o,t)}).catch(t=>{const e=document.createElement("p");e.textContent="Form unavailable.",e.style.cssText="color:var(--dm-danger);font-style:italic;",n.appendChild(e),console.warn("[forms]",t.message)})}
|
|
1
|
+
import{installSmoothCarets as J}from"/public/js/smooth-caret.js";import{buildWizardSteps as U}from"/public/js/form-steps.mjs";import{applyFormClasses as T,applySubmitButtons as _,applyWizardButtons as H,resolveSecondaryButton as K,submitWrapperClasses as Y}from"/public/js/form-submit.mjs";J(document);const j=document.querySelectorAll("[data-form]");j.length&&j.forEach(re);function F(n){return(n||[]).some(o=>o.logic||Array.isArray(o.triggers)&&o.triggers.length)}function $(n,o){const t=n.querySelector('[name="'+o+'"], [name="'+o+'[]"]')||n.querySelector('[data-chooser-field="'+o+'"]');if(!t)return null;const e=t.closest("form")||n;let i=t;for(;i.parentNode&&i.parentNode!==e;)i=i.parentNode;return i}export function markInvalidFields(n,o){n.querySelectorAll(".fb-field-invalid").forEach(e=>e.classList.remove("fb-field-invalid"));let t=null;(o||[]).forEach(function(e){const i=$(n,e.name);i&&(i.classList.add("fb-field-invalid"),t||(t=i))}),t&&t.scrollIntoView&&t.scrollIntoView({behavior:"smooth",block:"center"})}export function showValidationPopup(n){const o=(n.missing||[]).map(e=>"<strong>"+A(e.label)+"</strong> is required.").concat((n.errors||[]).map(A)),t=o.join("<br>");if(typeof E<"u"&&E.alert){E.alert(t,{title:"Please complete this step"});return}window.alert(o.join(`
|
|
2
|
+
`).replace(/<[^>]+>/g,""))}function A(n){return String(n).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}function q(n,o){let t=!1;n.addEventListener("dm-form-end",function(e){const i=e.detail&&e.detail.validation;if(i&&!i.valid){markInvalidFields(n,i.missing),showValidationPopup(i);return}t||(t=!0,n.classList.add("fb-form-loading"),Promise.resolve(o(e.detail&&e.detail.values||{})).catch(function(){}).finally(function(){t=!1,n.classList.remove("fb-form-loading")}))})}function C(n,o,t){const e=n.querySelector(".fb-form-success, .fb-form-error");e&&e.remove();const i=document.createElement("div");i.className=t==="success"?"fb-form-success":"fb-form-error",i.textContent=o,n.appendChild(i)}function B(n,o){if(n._formLogicRuntime=o,n._formLogicObserver&&(n._formLogicObserver.disconnect(),n._formLogicObserver=null),!n.parentNode||typeof MutationObserver>"u"||n._formLogicObserved)return;n._formLogicObserved=!0;const t=new MutationObserver(function(e){for(const i of e)for(const r of i.removedNodes)if(r===n||r.nodeType===1&&r.contains&&r.contains(n)){n._formLogicRuntime&&n._formLogicRuntime.destroy(),t.disconnect();return}});t.observe(n.parentNode,{childList:!0,subtree:!1}),n._formLogicObserver=t}function G(n){const o=n&&n.logic;return!!(o&&(o.visibility||o.requirement))}function R(n,o){const t={};return n.forEach(function(e){if(e.type==="page-break"||e.type==="spacer"||!e.name)return;const i=e.type==="chooser"||!!(e.variant&&["select","radio","checkbox-group"].includes(e.type)),r=e.type==="checkbox"?"boolean":e.type==="date"||e.type==="icon"||e.type==="image"?"string":i?"chooser":e.type,l={...e.formConfig||{}};l.span==="full"&&o&&(l.span=o);const s={type:r,label:e.label||e.name,required:G(e)?!1:e.required||!1,options:e.options,formConfig:{...e.placeholder&&{placeholder:e.placeholder},...e.helper&&{helperText:e.helper},...e.tooltip&&{tooltip:e.tooltip},...l}};e.minLength!==void 0&&(s.minLength=e.minLength),e.maxLength!==void 0&&(s.maxLength=e.maxLength),e.min!==void 0&&(s.min=e.min),e.max!==void 0&&(s.max=e.max),i&&(e.variant&&(s.variant=e.variant),(e.multiple||e.type==="checkbox-group")&&(s.multiple=!0),e.density&&(s.density=e.density),e.columns&&(s.columns=e.columns),e.accent&&(s.accent=e.accent),e.accentStyle&&(s.accentStyle=e.accentStyle),e.glow&&(s.glow=!0),e.glowColour&&(s.glowColour=e.glowColour),e.shadow&&(s.shadow=e.shadow),e.shadowColour&&(s.shadowColour=e.shadowColour)),t[e.name]=s}),t}function Q(n){const o={};return n.forEach(function(t){if(!(!t.name||t.type==="page-break"||t.type==="spacer")&&(t.type==="select"||t.type==="multiselect")&&t.required){const e=(t.options||[])[0];e&&(o[t.name]=typeof e=="object"?e.value:e)}}),o}function O(n,o){(o||[]).forEach(function(t){if(t.type!=="date"||!t.name)return;const e=n.querySelector('[name="'+t.name+'"]');e&&e.type!=="date"&&(e.type="date")})}const z="padding:.45rem .7rem;border:1px solid var(--dm-border);border-radius:var(--dm-radius-md,6px);background:transparent;color:inherit;cursor:pointer;font:inherit;font-size:.85em;flex-shrink:0;line-height:1.2;";function P(n){const o=document.createElement("div");o.style.cssText="position:fixed;inset:0;z-index:9999;background:rgba(0,0,0,.55);display:flex;align-items:center;justify-content:center;padding:20px;";const t=document.createElement("div");t.style.cssText="background:var(--dm-surface-overlay,var(--dm-surface));color:var(--dm-text);border:1px solid var(--dm-border);border-radius:10px;width:100%;max-width:560px;max-height:75vh;display:flex;flex-direction:column;gap:.6rem;padding:1rem;box-shadow:var(--dm-shadow-lg,0 12px 40px rgba(0,0,0,.35));";const e=document.createElement("div");e.style.cssText="display:flex;align-items:center;justify-content:space-between;gap:.5rem;";const i=document.createElement("strong");i.textContent=n;const r=document.createElement("button");r.type="button",r.textContent="\xD7",r.setAttribute("aria-label","Close"),r.style.cssText=z+"font-size:1rem;padding:.15rem .55rem;",e.appendChild(i),e.appendChild(r),t.appendChild(e);const l=document.createElement("div");l.style.cssText="display:flex;flex-direction:column;gap:.6rem;overflow:hidden;min-height:0;",t.appendChild(l),o.appendChild(t);const s=c=>{c.key==="Escape"&&a()};function a(){document.removeEventListener("keydown",s),o.remove()}return r.addEventListener("click",a),o.addEventListener("click",c=>{c.target===o&&a()}),document.addEventListener("keydown",s),document.body.appendChild(o),{body:l,close:a}}function X(n){const{body:o,close:t}=P("Choose an icon"),e=document.createElement("input");e.type="text",e.placeholder="Search icons\u2026",e.className="form-input",e.style.cssText="padding:.45rem .6rem;border:1px solid var(--dm-border);border-radius:var(--dm-radius-md,6px);background:var(--dm-input-bg,transparent);color:var(--dm-text);",o.appendChild(e);const i=document.createElement("div");i.style.cssText="display:grid;grid-template-columns:repeat(auto-fill,minmax(46px,1fr));gap:4px;overflow-y:auto;min-height:0;",o.appendChild(i);const r=window.I&&typeof I.list=="function"?I.list():[],l=r.map(s=>{const a=document.createElement("button");a.type="button",a.title=s,a.dataset.iconName=s,a.style.cssText="display:flex;align-items:center;justify-content:center;padding:.55rem 0;border:1px solid transparent;border-radius:6px;background:transparent;color:inherit;cursor:pointer;";const c=document.createElement("span");return c.setAttribute("data-icon",s),c.setAttribute("data-icon-size","18"),a.appendChild(c),a.addEventListener("mouseenter",()=>{a.style.borderColor="var(--dm-border)"}),a.addEventListener("mouseleave",()=>{a.style.borderColor="transparent"}),a.addEventListener("click",()=>{t(),n(s)}),i.appendChild(a),a});if(!r.length){const s=document.createElement("p");s.textContent="Icon engine unavailable - type an icon name into the field instead.",i.appendChild(s)}window.I?.scan?.(i),e.addEventListener("input",()=>{const s=e.value.trim().toLowerCase();l.forEach(a=>{a.style.display=!s||a.dataset.iconName.includes(s)?"":"none"})}),e.focus()}async function Z(){const n=typeof S<"u"&&S.get?S.get("auth_token"):null;if(!n)return{error:"auth"};const o=i=>fetch("/api/media",{headers:{Authorization:"Bearer "+i}});let t;try{if(t=await o(n),t.status===401&&S.get("auth_refresh_token")){const i=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:S.get("auth_refresh_token")})});if(i.ok){const{token:r}=await i.json();S.set("auth_token",r),t=await o(r)}}}catch{return{error:"load"}}if(!t.ok)return{error:t.status===401||t.status===403?"auth":"load"};const e=await t.json().catch(()=>[]);return{files:(Array.isArray(e)?e:[]).filter(i=>/\.(png|jpe?g|gif|webp|svg|avif)$/i.test(i.name||i.url||""))}}function ee(n){const{body:o,close:t}=P("Choose an image"),e=document.createElement("p");e.textContent="Loading media library\u2026",e.style.cssText="margin:0;font-size:.85em;color:var(--dm-text-muted);",o.appendChild(e),Z().then(({files:i,error:r})=>{if(r){e.textContent=r==="auth"?"Sign in with media access to browse the library - or paste an image URL into the field.":"Could not load the media library - paste an image URL into the field instead.";return}if(!i.length){e.textContent="No images in the media library yet.";return}e.remove();const l=document.createElement("div");l.style.cssText="display:grid;grid-template-columns:repeat(auto-fill,minmax(104px,1fr));gap:8px;overflow-y:auto;min-height:0;",i.forEach(s=>{const a=document.createElement("button");a.type="button",a.title=s.name||s.url,a.style.cssText="display:flex;flex-direction:column;gap:.25rem;padding:.35rem;border:1px solid var(--dm-border);border-radius:8px;background:transparent;color:inherit;cursor:pointer;min-width:0;";const c=document.createElement("img");c.src=s.url,c.alt=s.name||"",c.loading="lazy",c.style.cssText="width:100%;height:72px;object-fit:cover;border-radius:5px;";const m=document.createElement("span");m.textContent=s.name||s.url,m.style.cssText="font-size:.68em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;",a.appendChild(c),a.appendChild(m),a.addEventListener("click",()=>{t(),n(s.url)}),l.appendChild(a)}),o.appendChild(l)})}function D(n,o){n.value=o,n.dispatchEvent(new Event("input",{bubbles:!0})),n.dispatchEvent(new Event("change",{bubbles:!0}))}function M(n){const o=document.createElement("div");return o.style.cssText="display:flex;align-items:center;gap:.5rem;",n.parentNode.insertBefore(o,n),o.appendChild(n),n.style.flex="1",n.style.minWidth="0",o}function V(n){const o=document.createElement("button");return o.type="button",o.textContent=n,o.style.cssText=z,o}function te(n){if(!window.I||typeof I.list!="function")return;const o=M(n),t=document.createElement("span");t.style.cssText="display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;flex-shrink:0;border:1px solid var(--dm-border);border-radius:var(--dm-radius-md,6px);";const e=()=>{t.textContent="";const r=n.value.trim();if(!r)return;const l=document.createElement("span");l.setAttribute("data-icon",r),l.setAttribute("data-icon-size","16"),t.appendChild(l),window.I?.scan?.(t)};n.addEventListener("input",e),e();const i=V("Browse");i.addEventListener("click",()=>X(r=>{D(n,r),e()})),o.appendChild(t),o.appendChild(i)}function ne(n){const o=M(n),t=document.createElement("img");t.alt="",t.style.cssText="width:2rem;height:2rem;flex-shrink:0;object-fit:cover;border:1px solid var(--dm-border);border-radius:var(--dm-radius-md,6px);display:none;";const e=()=>{const r=n.value.trim();t.style.display=r?"":"none",r&&(t.src=r)};n.addEventListener("input",e),e();const i=V("Browse");i.addEventListener("click",()=>ee(r=>{D(n,r),e()})),o.appendChild(t),o.appendChild(i)}export function enhancePickerFields(n,o){const t=typeof n=="string"?document.querySelector(n):n;t&&(o||[]).forEach(function(e){if(!e.name||e.type!=="icon"&&e.type!=="image")return;const i=t.querySelector('[name="'+e.name+'"]');!i||i.dataset.pickerEnhanced||(i.dataset.pickerEnhanced="1",e.type==="icon"?te(i):ne(i))})}function N(n,o){const t=document.createElement("div");t.className="fb-form-honeypot",t.setAttribute("aria-hidden","true");const e=document.createElement("input");e.name=n.querySelector('[name="website"]')?"_website":"website",t.dataset.hpName=e.name,e.type="text",e.tabIndex=-1,e.autocomplete="url",e.placeholder="https://",t.appendChild(e);const i=document.createElement("input");i.name="_t",i.type="hidden",i.value=o||Date.now(),t.appendChild(i),n.appendChild(t)}function W(n,o){const t=n.querySelector("form");if(!t)return;const e=Array.from(t.querySelectorAll(".form-group"));let i=0;o.forEach(function(r){if(r.type==="spacer"){const l=document.createElement("div");l.className="fb-spacer";const s=e[i];if(s)t.insertBefore(l,s);else{const a=t.querySelector('[type="submit"]');a?t.insertBefore(l,a):t.appendChild(l)}}else r.type!=="page-break"&&i++})}function L(n,o){!n||!o||n.querySelectorAll('input[type="file"]').forEach(function(t){t.name&&t.files&&t.files.length&&o.set(t.name,Array.from(t.files))})}function x(n,o,t,e,i,r){const l=i||e,s=l.querySelector(".fb-form-honeypot"),a=s?.querySelector('input[type="text"]')?.value||"",c=s?.querySelector('[name="_t"]')?.value||"",m=Object.assign({},o);delete m._t,s?.dataset.hpName&&delete m[s.dataset.hpName];const d=new Map(r||[]);L(l,d);const u=d.size>0;let p,b;if(u){const h=new FormData;for(const[g,v]of Object.entries(m))v!=null&&(d.has(g)||h.append(g,typeof v=="object"?JSON.stringify(v):String(v)));for(const[g,v]of d)for(const w of v)h.append(g,w,w.name);h.append("_hp",a),h.append("_t",c),p=h,b={}}else{const h=Object.assign({},m,{_hp:a,_t:c});p=JSON.stringify(h),b={"Content-Type":"application/json"}}return fetch("/api/forms/submit/"+n,{method:"POST",headers:b,body:p}).then(h=>h.json().then(g=>({ok:h.ok,body:g}))).then(h=>{h.ok&&h.body.ok?(e.textContent="",C(e,h.body.message||t.successMessage||"Thank you.","success")):C(e,h.body.error||"Something went wrong.","error")}).catch(()=>{C(e,"Unable to submit. Please check your connection.","error")})}function oe(n,o,t,e,i){const r=document.createElement("form");r.noValidate=!0,o.forEach(function(a){const c=document.createElement("div");c.className="form-group",c.style.marginBottom="1.25rem",a.name&&(c.dataset.field=a.name);const m=document.createElement("label");if(m.className="form-label",m.textContent=a.label||a.name,a.required){const u=document.createElement("span");u.textContent=" *",u.style.color="var(--dm-danger)",m.appendChild(u)}let d;a.type==="textarea"?(d=document.createElement("textarea"),d.rows=a.formConfig?.rows||4,d.className="form-input"):a.type==="select"?(d=document.createElement("select"),d.className="form-input",(a.options||[]).forEach(function(u){const p=document.createElement("option");p.value=typeof u=="string"?u:u.value??"",p.textContent=typeof u=="string"?u:u.label||p.value,d.appendChild(p)})):(d=document.createElement("input"),d.type=a.type==="icon"||a.type==="image"?"text":a.type||"text",d.className="form-input",a.placeholder&&(d.placeholder=a.placeholder),a.type==="file"&&(a.file?.accept&&(d.accept=a.file.accept),a.multiple&&(d.multiple=!0))),d.name=a.name,d.required=a.required||!1,c.appendChild(m),c.appendChild(d),r.appendChild(c)}),t.honeypot&&N(r);const l=document.createElement("button");if(l.type="submit",l.className="btn btn-primary",l.textContent=t.submitText||"Submit",Y(t,"stacked").length||K(t)){const a=document.createElement("div");a.className="form-group form-buttons",a.appendChild(l),r.appendChild(a)}else r.appendChild(l);if(r.addEventListener("submit",function(a){a.preventDefault();const c={};if(o.forEach(function(m){const d=r.querySelector('[name="'+m.name+'"]');d&&(c[m.name]=d.value)}),window.FormLogicEngine&&i){const m=window.FormLogicEngine,d=[],u=[];if(o.forEach(function(p){if(m.evaluateFieldVisibility(p,c)==="hidden"){delete c[p.name];return}const h=m.evaluateFieldRequirement(p,c),g=c[p.name];h&&(!g||!String(g).trim())&&d.push(p.label||p.name);const v=m.validateField(p,g||"",c);v.length&&u.push(v[0].message)}),d.length||u.length){const p=[];d.length&&p.push("Required: "+d.join(", ")),u.length&&p.push(u.join("; ")),C(n,p.join(". "),"error");return}}if(n.dataset.triggerBlocked==="true"){const m=n._formLogicRuntime,d=m&&m.submitBlock?m.submitBlock().reasons:[];C(n,d.join(" ")||"This form cannot be submitted yet.","error");return}n.classList.add("fb-form-loading"),l.disabled=!0,x(e,c,t,n,r).finally(function(){n.classList.remove("fb-form-loading"),l.disabled=!1})}),n.appendChild(r),_(r,t,"stacked"),T(r,t,o),enhancePickerFields(n,o),q(n,function(a){return x(e,a,t,n,r)}),window.FormLogicEngine&&i&&F(o)){const a=new window.FormLogicEngine.FormLogicRuntime(i,n);a.init(),n._formLogicRuntime=a}}function re(n){const o=n.getAttribute("data-form");o&&fetch("/api/forms/"+o+"/public").then(t=>{if(!t.ok)throw new Error("Form not found: "+o);return t.json()}).then(t=>{const e=t.fields||[],i=t.settings||{},r=document.createElement("div");r.className="fb-form-wrapper",n.appendChild(r);const l=e.some(s=>s.type==="page-break");if(typeof Domma<"u"&&Domma.forms){const s=Number(i.columns)===3?3:2,a=i.layout||"grid";if(l&&Domma.forms.wizard){const c=U(e,t.title),m=c.map(f=>f.fields),d=c.map(function(f){return{title:f.title,description:f.description,fields:R(f.fields,s)}});let u=0,p=null,b=null;const h=Date.now(),g=new Map,v=function(f){const y=window.FormLogicEngine;if(!y||!y.validateFields)return;const k=y.validateFields(r,m[u]||[]);k.valid||(f.preventDefault(),f.stopImmediatePropagation(),markInvalidFields(r,k.missing),showValidationPopup(k))},w=function(){const f=m[u]||e;if(H(r,i),T(r,i,f),O(r,f),enhancePickerFields(r,f),f.some(y=>y.type==="spacer")&&W(r,f),i.honeypot){const y=r.querySelector("form");y&&N(y,h)}window.FormLogicEngine&&F(e)&&(p&&p.destroy(),p=new window.FormLogicEngine.FormLogicRuntime(t,r,{baseValues:b&&b.getStepData?b.getStepData():{}}),p.init(),B(r,p)),r.querySelectorAll(".wizard-next, .wizard-submit").forEach(y=>y.addEventListener("click",v,!0))};b=Domma.forms.wizard(r,{schema:{steps:d},layout:a,columns:s,finishText:i.submitText||"Finish",onStepChange:function(f){L(r,g),u=f,setTimeout(w,0)},onSubmit:function(f){return L(r,g),x(o,f,i,r,null,g)}}),q(r,function(f){return L(r,g),x(o,f,i,r,null,g)}),r.addEventListener("dm-form-trigger",function(f){if(!f.detail||f.detail.type!=="goto-step"||!b||!b.goToStep)return;const y=Number(f.detail.action.step);!isFinite(y)||y<0||y>=d.length||y!==u&&b.goToStep(y)}),Promise.resolve(b).then(w)}else if(Domma.forms.render){const c=R(e,s),m=Q(e);q(r,function(u){return x(o,u,i,r,null)});const d=Domma.forms.render(r,c,m,{submitText:i.submitText||"Submit",layout:a,columns:s,onSubmit:function(u){return x(o,u,i,r,null)}});Promise.resolve(d).then(function(){if(_(r,i,a),T(r,i,e),O(r,e),enhancePickerFields(r,e),window.FormLogicEngine&&F(e)){const u=new window.FormLogicEngine.FormLogicRuntime(t,r);u.init(),B(r,u)}if(e.some(u=>u.type==="spacer")&&W(r,e),i.honeypot){const u=r.querySelector("form");u&&N(u)}})}}else oe(r,e.filter(s=>s.type!=="page-break"&&s.type!=="spacer"),i,o,t)}).catch(t=>{const e=document.createElement("p");e.textContent="Form unavailable.",e.style.cssText="color:var(--dm-danger);font-style:italic;",n.appendChild(e),console.warn("[forms]",t.message)})}
|