domma-cms 0.79.1 → 0.81.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/CLAUDE.md +6 -1
- package/admin/index.html +2 -2
- package/admin/js/app.js +2 -2
- package/admin/js/lib/action-editor-arrange.js +1 -1
- package/admin/js/lib/desktop-notify.js +1 -0
- package/admin/js/lib/notifications-arrange.js +1 -1
- package/admin/js/lib/sidebar-renderer.js +1 -1
- package/admin/js/templates/action-editor.html +1 -0
- package/admin/js/templates/docs/usage-actions.html +5 -0
- package/admin/js/templates/form-editor.html +72 -0
- package/admin/js/templates/notifications.html +21 -1
- package/admin/js/views/form-editor.js +7 -7
- package/admin/js/views/notifications.js +67 -4
- package/package.json +1 -1
- package/public/css/forms.css +1 -1
- package/public/js/form-logic-engine.js +1 -1
- package/public/js/forms.js +2 -2
- package/public/js/site.js +1 -1
- package/server/routes/api/api-tokens.js +5 -2
- package/server/routes/api/auth.js +15 -1
- package/server/routes/api/effects.js +36 -0
- package/server/routes/api/forms.js +40 -9
- package/server/routes/api/layouts.js +6 -2
- package/server/routes/api/notifications.js +97 -20
- package/server/routes/api/users.js +11 -0
- package/server/server.js +29 -5
- package/server/services/actions.js +28 -0
- package/server/services/adapterRegistry.js +1 -0
- package/server/services/collections.js +10 -4
- package/server/services/connectionManager.js +1 -0
- package/server/services/email.js +3 -0
- package/server/services/notification-sources.js +361 -0
- package/server/services/notify.js +420 -0
- package/server/services/plugins.js +6 -2
- package/server/services/sidebarBadges.js +33 -0
- package/server/templates/page.html +4 -4
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{api as
|
|
1
|
+
import{api as g,apiRequest as m}from"../api.js";import{addBannerAction as U}from"../lib/plugin-chrome.js";import{helpPopovers as ee}from"../lib/help-popover.js";import{onceClosed as Ae,openMenuAt as te,sidebarCount as Ne,viewLifetime as Re}from"../lib/tool-kit.js";import{DEFAULT_SETTINGS as se,FILTERS as Le,arrangeNotifications as De,filterCounts as $e,normaliseSettings as Ie,notificationRow as ne,sourceRow as Oe}from"../lib/notifications-arrange.js";import{desktopPermission as ae,notificationSettings as oe,primeSeen as Pe,requestDesktopPermission as qe,showTestNotification as Ue}from"../lib/desktop-notify.js";const ie=/^#\/system\/notifications\/?(\?.*)?$/,_e="#/system/notifications",_="notifications.filter",Be=`
|
|
2
2
|
<div class="ntx-row" data-id="{{id}}" tabindex="0" data-bind-class="rowCls" data-bind-aria-expanded="open ? 'true' : 'false'">
|
|
3
3
|
<span class="ntx-dot" aria-hidden="true"></span>
|
|
4
4
|
<span class="ntx-main">
|
|
@@ -9,10 +9,73 @@ import{api as d,apiRequest as b}from"../api.js";import{addBannerAction as O}from
|
|
|
9
9
|
<span class="ntx-preview" data-bind-hidden="open">{{preview}}</span>
|
|
10
10
|
<span class="ntx-body" data-bind-hidden="!open">{{body}}</span>
|
|
11
11
|
<span class="ntx-actions" data-bind-hidden="!open || !link">
|
|
12
|
-
<a class="btn btn-sm btn-outline" href="{{link}}" target="
|
|
12
|
+
<a class="btn btn-sm btn-outline" href="{{link}}" target="{{linkTarget}}" rel="noopener">{{linkText}}</a>
|
|
13
13
|
</span>
|
|
14
14
|
</span>
|
|
15
15
|
<button type="button" class="ntx-kebab" aria-label="Actions for {{title}}" title="Actions">\u22EE</button>
|
|
16
|
-
</div>`,
|
|
16
|
+
</div>`,We=r=>String(r??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,"""),Fe=`
|
|
17
|
+
<div class="ntx-src" data-id="{{id}}">
|
|
18
|
+
<div class="ntx-src-head">
|
|
19
|
+
<span class="ntx-src-name"><strong>{{label}}</strong><small>{{meta}}</small></span>
|
|
20
|
+
<input type="checkbox" class="form-switch ntx-src-on" aria-label="Send: {{label}}" data-bind-checked="enabled">
|
|
21
|
+
</div>
|
|
22
|
+
<small class="ntx-src-desc">{{description}}</small>
|
|
23
|
+
<div class="ntx-src-foot" data-bind-hidden="!enabled">
|
|
24
|
+
<select class="form-input ntx-src-aud" aria-label="Who gets these: {{label}}">
|
|
25
|
+
<option value="super-admin">Super Admins</option>
|
|
26
|
+
<option value="admins">Admins and Super Admins</option>
|
|
27
|
+
<option value="everyone">Everyone who can read notifications</option>
|
|
28
|
+
</select>
|
|
29
|
+
<button type="button" class="btn btn-sm btn-ghost ntx-src-run" data-bind-hidden="!every">Run now</button>
|
|
30
|
+
</div>
|
|
31
|
+
<small class="ntx-src-err" data-bind-hidden="!lastError">{{lastError}}</small>
|
|
32
|
+
</div>`,C={granted:"Allowed in this browser.",default:"Your browser will ask for permission when you turn this on.",denied:"Blocked in this browser. Allow notifications for this site in the browser's site settings (the icon by the address), then turn this on.",unsupported:"This browser cannot show desktop notifications here - they need a current browser and an https address."};function je(r){const c=document.createElement("template");return c.innerHTML=r.trim(),c.content.firstElementChild}const p=Re(ie);async function f(){Ne(_e);try{const{count:r}=await g.system.notifications.unreadCount(),c=$("#topbar-bell-badge");r>0?c.text(r>99?"99+":String(r)).removeAttr("hidden"):c.attr("hidden","").text("")}catch{}}export const notificationsView={templateUrl:"/admin/js/templates/notifications.html",async onMount(r){p.reset();const c=r.get(0).querySelector(".ntx")||r.get(0),A=()=>ie.test(location.hash)&&c.isConnected,i=M.observable([]),k=M.observable(!0),N=M.observable("");let B="all";try{B=Le.includes(S.get(_))?S.get(_):"all"}catch{}const h=M.observable(B),W=M.observable(""),x=M.observable("");p.add(M.effect(()=>{const e=h.value;try{S.set(_,e)}catch{}}));const F=M.computed(()=>$e(i.value));p.add(M.effect(()=>{const e=F.value;!k.value&&h.peek()!=="all"&&!e[h.peek()]&&(h.value="all")}));const j=M.observable([]);p.add(M.effect(()=>{const e=Date.now(),t=x.value;j.value=De(i.value,{filter:h.value,query:W.value}).map(n=>ne(n,{now:e,expanded:t}))}));const re=M.computed(()=>i.value.length?"No notifications match.":"No notifications - you're all caught up."),d=M.observable(oe.read());p.add(M.effect(()=>oe.write(d.value)));const R=(e,t)=>{d.value=Ie({...d.peek(),[e]:t})},H=e=>i.peek().find(t=>String(t.id)===String(e)),le=(e,t)=>{i.value=i.peek().map(n=>String(n.id)===String(e)?t(n):n)},G=e=>{i.value=i.peek().filter(t=>String(t.id)!==String(e))};async function T({markRead:e=!0}={}){try{const t=await g.system.notifications.list();if(!A())return;const n=Array.isArray(t)?t:[];if(i.value=n.map(l=>({...l,isNew:!!l.unread})),N.value="",e&&n.some(l=>l.unread)){if(await m("/system/notifications/read-all",{method:"POST"}).catch(()=>{}),!A())return;i.value=i.peek().map(l=>({...l,unread:!1})),f()}}catch(t){N.value=`Could not load notifications: ${t.message||t}`}finally{k.value=!1}}async function K(e,t){try{t?await g.system.notifications.markRead(e.id):await m(`/system/notifications/${encodeURIComponent(e.id)}/unread`,{method:"POST"}),le(e.id,n=>({...n,unread:!t})),f()}catch(n){E.toast(`Not changed: ${n.message||n}`,{type:"error"})}}async function Y(e){try{await g.system.notifications.dismiss(e.id),G(e.id),f()}catch(t){E.toast(`Could not dismiss it: ${t.message||t}`,{type:"error"})}}async function ce(e){if(await E.confirm(`Delete "${We(e.data?.title||"this notification")}" for everyone? Dismissing hides it for you only.`))try{await g.system.notifications.remove(e.id),G(e.id),E.toast("Notification deleted.",{type:"success"}),f()}catch(t){E.toast(/403|forbidden|permission/i.test(String(t.message||t))?"Deleting needs the Notifications delete permission - Dismiss hides it for you.":`Could not delete it: ${t.message||t}`,{type:"error"})}}async function de(){const e=i.peek().length;if(!e){E.toast("Nothing to dismiss.",{type:"info"});return}if(await E.confirm(`Dismiss all ${e} notification${e===1?"":"s"}? They are hidden for you only.`))try{await m("/system/notifications/dismiss-all",{method:"POST"}),i.value=[],f()}catch(t){E.toast(`Could not dismiss them: ${t.message||t}`,{type:"error"})}}async function pe(e){const t=e.data||{},n=[t.title,t.body,t.link].filter(Boolean).join(`
|
|
17
33
|
|
|
18
|
-
`);try{await navigator.clipboard.writeText(n),E.toast("Copied.",{type:"success"})}catch{E.toast("Copying is blocked here - select the text instead.",{type:"warning"})}}const
|
|
34
|
+
`);try{await navigator.clipboard.writeText(n),E.toast("Copied.",{type:"success"})}catch{E.toast("Copying is blocked here - select the text instead.",{type:"warning"})}}const L=e=>{x.value=x.peek()===String(e)?"":String(e)};function ue(e){const t=H(e);if(!t)return[];const n=ne(t);return[{header:n.title},{label:n.open||x.peek()===n.id?"Close":"Read it",icon:"eye",action:()=>L(e)},...n.link?[n.linkTarget==="_self"?{label:"Go there",icon:"arrow-right",action:()=>{location.hash=n.link}}:{label:"Open link",icon:"external-link",action:()=>window.open(n.link,"_blank","noopener")}]:[],{label:"Copy text",icon:"copy",action:()=>pe(t)},t.unread?{label:"Mark as read",icon:"check",action:()=>K(t,!0)}:{label:"Mark as unread",icon:"mail",action:()=>K(t,!1)},{type:"divider"},{label:"Dismiss",icon:"x",action:()=>Y(t)},{label:"Delete for everyone\u2026",icon:"trash",danger:!0,action:()=>ce(t)}]}const b=c.querySelector(".ntx-list");b.innerHTML=Be;const y=e=>()=>{h.value=e},he=M.applyBindings({items:i,loading:k,loadError:N,filter:h,query:W,rows:j,counts:F,emptyText:re},c,{methods:{showAll:y("all"),showNew:y("new"),showCritical:y("critical"),showWarning:y("warning"),showInfo:y("info"),showSuccess:y("success")}});p.add(()=>he.dispose()),p.add(ee(c,{position:"bottom"}));const me=E.contextMenu(b,{cursor:"glyph",match:".ntx-row",inherit:!1,density:"compact",items:e=>ue(e.dataset.id)});p.add(()=>me?.destroy?.());const z=e=>{const t=e.target.closest(".ntx-row");if(!t)return;const n=e.target.closest(".ntx-kebab");if(n){te(n);return}e.target.closest("a")||L(t.dataset.id)},J=e=>{const t=e.target.closest(".ntx-row");if(t){if(e.key==="F10"&&e.shiftKey)e.preventDefault(),te(t);else if(e.key==="Enter"&&e.target===t)L(t.dataset.id);else if(e.key==="Delete"&&e.target===t){const n=H(t.dataset.id);n&&Y(n)}}};b.addEventListener("click",z),b.addEventListener("keydown",J),p.add(()=>{b.removeEventListener("click",z),b.removeEventListener("keydown",J)});async function fe(){try{const{added:e}=await m("/system/notifications/samples",{method:"POST"});await T({markRead:!1}),f(),E.toast(`${e} sample notifications added.${d.peek().desktop?" They pop up on the desktop within a minute.":""}`,{type:"success"})}catch(e){E.toast(/403|forbidden|permission/i.test(String(e.message||e))?"Adding samples needs the Notifications delete permission.":`Could not add samples: ${e.message||e}`,{type:"error"})}}function be(){const e=E.slideover({title:"Notifications settings",size:"md",position:"right"}),t=je(`
|
|
35
|
+
<div class="ntx-panel">
|
|
36
|
+
<div class="ntx-setting-row">
|
|
37
|
+
<label class="form-label" for="ntx-set-desktop" style="margin:0">Desktop notifications
|
|
38
|
+
<span data-help-title="Desktop notifications" data-help="New notifications pop up on your desktop, even when the admin is in a background tab - as long as it is open somewhere. They are checked every minute. Click one to come back here. This is for this browser only: turn it on in each browser you use."></span>
|
|
39
|
+
</label>
|
|
40
|
+
<input id="ntx-set-desktop" type="checkbox" class="form-switch" data-model="desktop.value">
|
|
41
|
+
</div>
|
|
42
|
+
<small class="ntx-panel-meta" data-bind-class="permTone.value" data-bind-text="permText.value"></small>
|
|
43
|
+
<div data-bind-hidden="!desktop.value">
|
|
44
|
+
<label class="form-label" for="ntx-set-level">Which ones
|
|
45
|
+
<span data-help="Everything still arrives here and on the bell - this only decides what is worth interrupting you for. Critical ones stay on the desktop until you close them."></span>
|
|
46
|
+
</label>
|
|
47
|
+
<select id="ntx-set-level" class="form-input" data-model="desktopLevel.value">
|
|
48
|
+
<option value="all">All of them</option>
|
|
49
|
+
<option value="warning">Warnings and critical</option>
|
|
50
|
+
<option value="critical">Critical only</option>
|
|
51
|
+
</select>
|
|
52
|
+
</div>
|
|
53
|
+
<div data-bind-hidden="!canTest.value">
|
|
54
|
+
<button type="button" class="btn btn-sm btn-outline" data-on-click="test">Send a test</button>
|
|
55
|
+
</div>
|
|
56
|
+
<div class="ntx-setting-row">
|
|
57
|
+
<label class="form-label" for="ntx-set-toasts" style="margin:0">Pop-ups when you sign in
|
|
58
|
+
<span data-help="On signing in, unread warnings and critical notifications (up to three) are shown as messages that stay until you close them."></span>
|
|
59
|
+
</label>
|
|
60
|
+
<input id="ntx-set-toasts" type="checkbox" class="form-switch" data-model="signInToasts.value">
|
|
61
|
+
</div>
|
|
62
|
+
<small class="ntx-panel-meta">Saved in this browser as you change them.</small>
|
|
63
|
+
<div class="ntx-setting-row">
|
|
64
|
+
<label class="form-label" style="margin:0">Sample notifications
|
|
65
|
+
<span data-help-title="Sample notifications" data-help="Adds four made-up notifications - one critical, warning, info and success - to see how they look, and to try the bell, the sidebar badge and desktop notifications. They are added for everyone, stay unread until you open this screen again, and go by themselves after a week. Adding them again replaces the earlier ones. Needs the Notifications delete permission."></span>
|
|
66
|
+
</label>
|
|
67
|
+
<button type="button" class="btn btn-sm btn-outline" data-on-click="samples">Add samples</button>
|
|
68
|
+
</div>
|
|
69
|
+
<div class="ntx-sources" data-bind-hidden="!sourcesShown.value">
|
|
70
|
+
<h4 class="ntx-panel-h">What this site tells you
|
|
71
|
+
<span data-help-title="Sources" data-help="Everything the site and its plugins can notify about. Switch one off and it stops sending; choose who gets each one. These settings are for the whole site and only Super Admins can see or change them. Checks that run on a timer can be run now to try them."></span>
|
|
72
|
+
</h4>
|
|
73
|
+
<small class="ntx-panel-meta">For the whole site - only Super Admins see this.</small>
|
|
74
|
+
<div class="ntx-src-list" data-each="sourceRows.value key=id"></div>
|
|
75
|
+
</div>
|
|
76
|
+
<div class="ntx-panel-actions">
|
|
77
|
+
<button type="button" class="btn btn-sm btn-ghost" data-on-click="reset">Reset to defaults</button>
|
|
78
|
+
<span class="spacer"></span>
|
|
79
|
+
<button type="button" class="btn btn-sm btn-primary" data-on-click="done">Done</button>
|
|
80
|
+
</div>
|
|
81
|
+
</div>`),n=t.querySelector("#ntx-set-desktop"),l=t.querySelector(".ntx-src-list");l.innerHTML=Fe;const w=M.observable([]),D=M.observable([]),O=M.observable(!1),ye=M.effect(()=>{D.value=w.value.map(s=>Oe(s))}),ve=M.effect(()=>{const s=D.value;setTimeout(()=>s.forEach(a=>{const o=l.querySelector(`.ntx-src[data-id="${CSS.escape(a.id)}"] .ntx-src-aud`);o&&(o.value=a.audience)}),0)}),P=s=>{w.value=w.peek().map(a=>a.id===s.id?s:a)},V=s=>`/system/notifications/sources/${encodeURIComponent(s)}`;m("/system/notifications/sources",{method:"GET"}).then(s=>{w.value=s.sources||[],O.value=!0}).catch(()=>{O.value=!1});async function X(s,a){const o=w.peek().find(v=>v.id===s);try{const{setting:v}=await m(V(s),{method:"PUT",body:JSON.stringify(a)});P({...o,...v})}catch(v){P({...o}),E.toast(`Not saved: ${v.message||v}`,{type:"error"})}}async function ge(s,a){a.disabled=!0;try{const o=await m(`${V(s)}/run`,{method:"POST"});o.source&&P(o.source),E.toast(o.ok?"Done - anything it found is in the list.":`It failed: ${o.error}`,{type:o.ok?"success":"error"}),await T({markRead:!1}),f()}catch(o){E.toast(`Could not run it: ${o.message||o}`,{type:"error"})}finally{a.disabled=!1}}const Q=s=>{const a=s.target.closest(".ntx-src");a&&(s.target.matches(".ntx-src-on")?X(a.dataset.id,{enabled:s.target.checked}):s.target.matches(".ntx-src-aud")&&X(a.dataset.id,{audience:s.target.value}))},Z=s=>{const a=s.target.closest(".ntx-src-run");a&&ge(a.closest(".ntx-src").dataset.id,a)};l.addEventListener("change",Q),l.addEventListener("click",Z);const u=M.observable(ae()),we=M.computed(()=>d.value.desktop&&u.value!=="granted"?`On, but not showing: ${C[u.value]}`:C[u.value]),ke=M.computed(()=>u.value==="denied"||u.value==="unsupported"?"is-bad":""),xe=M.computed(()=>d.value.desktop&&u.value==="granted"),q=()=>{n.checked=d.peek().desktop};function Se(){const s=ae();if(s==="unsupported"||s==="denied"){u.value=s,E.toast(C[s],{type:"warning"}),q();return}(s==="granted"?Promise.resolve("granted"):qe()).then(async o=>{u.value=o,o==="granted"?(Pe(await g.system.notifications.list().catch(()=>[])),R("desktop",!0),E.toast("Desktop notifications are on for this browser.",{type:"success"})):E.toast(o==="denied"?C.denied:"Not turned on - the browser was not given permission.",{type:"warning"}),q()})}const Ee=s=>M.computed({read:()=>d.value[s],write:a=>R(s,a)}),Te={...Object.fromEntries(Object.keys(se).map(s=>[s,Ee(s)])),desktop:M.computed({read:()=>d.value.desktop,write:s=>s?Se():R("desktop",!1)}),permText:we,permTone:ke,canTest:xe,sourceRows:D,sourcesShown:O};e.element.appendChild(t);const Me=ee(t),Ce=M.applyBindings(Te,t,{methods:{done:()=>e.close(),reset:()=>{d.value={...se},q()},samples:()=>{fe()},test:()=>{Ue()||E.toast("The browser would not show it - check its notification settings.",{type:"warning"})}}});Ae(e,()=>{Ce.dispose(),Me(),ye(),ve(),l.removeEventListener("change",Q),l.removeEventListener("click",Z)}),e.open()}U(r,{id:"notifications-settings",icon:"settings",title:"Notifications settings",onClick:be}),U(r,{id:"notifications-dismiss-all",icon:"check-circle",title:"Dismiss all",onClick:()=>de()}),U(r,{id:"notifications-refresh",icon:"refresh-cw",title:"Refresh",onClick:()=>{k.value=!0,T()}}),I.scan(c),await T(),A()&&I.scan(c)}};
|
package/package.json
CHANGED
package/public/css/forms.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.fb-form-wrapper{max-width:600px;margin:2rem auto}.fb-form-honeypot{position:absolute;left:-9999px;top:-9999px;width:0;height:0;overflow:hidden;opacity:0;z-index:-1}.fb-form-success{padding:1rem 1.25rem;border-radius:var(--dm-radius-md, 6px);background:color-mix(in srgb,var(--dm-success) 12%,transparent);border:1px solid color-mix(in srgb,var(--dm-success) 30%,transparent);color:var(--dm-success);font-size:.95rem;margin-top:.5rem}.fb-form-error{padding:.75rem 1rem;border-radius:var(--dm-radius-md, 6px);background:color-mix(in srgb,var(--dm-danger) 10%,transparent);border:1px solid color-mix(in srgb,var(--dm-danger) 25%,transparent);color:var(--dm-danger);font-size:.875rem;margin-top:.75rem}.fb-form-loading{opacity:.6;pointer-events:none}.wizard-progress{height:4px;background:var(--dm-border);border-radius:2px;margin-bottom:1rem}.wizard-progress-bar{height:100%;background:var(--dm-primary);border-radius:2px;transition:width .3s ease}.wizard-step-title{font-size:1.1rem;font-weight:600;margin-bottom:.25rem}.wizard-step-description{font-size:.875rem;color:var(--dm-text-muted);margin-bottom:1rem}.wizard-nav{display:flex;justify-content:space-between;gap:.5rem;margin-top:1rem}.fb-field-hidden{display:none!important}.fb-validation-error{color:var(--dm-danger);font-size:.8rem;margin-top:.25rem}.fb-field-trigger-hidden{display:none!important}.fb-field-disabled{opacity:.55;pointer-events:none}.fb-trigger-banner{display:flex;align-items:flex-start;gap:.75rem;margin:.75rem 0;padding:.7rem .9rem;border-radius:var(--dm-radius-md);border-left:3px solid var(--dm-info);background:var(--dm-surface-raised);color:var(--dm-text);font-size:.9rem}.fb-trigger-banner--success{border-left-color:var(--dm-success)}.fb-trigger-banner--warning{border-left-color:var(--dm-warning)}.fb-trigger-banner--error{border-left-color:var(--dm-danger)}.fb-trigger-banner-text{flex:1 1 auto}.fb-trigger-banner-close{flex:0 0 auto;background:none;border:0;color:var(--dm-text-muted);font-size:1.1rem;line-height:1;cursor:pointer;padding:0 .15rem}.fb-trigger-banner-close:hover{color:var(--dm-text)}.fb-trigger-block-note{color:var(--dm-danger);font-size:.85rem;margin:.5rem 0 0}.fb-form-wrapper .grid>.fb-trigger-banner,.cms-form-embed .grid>.fb-trigger-banner{grid-column:1 / -1}.fb-form-wrapper .domma-form-field{margin-bottom:2rem}.fb-form-wrapper .form-group:not(.form-buttons){margin-bottom:2rem}.fb-spacer{height:2rem}.fb-field-required .picker-label:after,.fb-field-required>.form-label:after{content:" *";color:var(--dm-danger)}.fb-field-required .picker-label .picker-required{display:none}.fb-field-invalid>.form-label,.fb-field-invalid .picker-label{color:var(--dm-danger)}.fb-field-invalid .form-input,.fb-field-invalid .form-select,.fb-field-invalid textarea,.fb-field-invalid .picker-options{border-color:var(--dm-danger)}@media(max-width:700px){.fb-form-wrapper form.grid,[data-form-inline] form.grid,.fb-form-wrapper .wizard form.grid,[data-form-inline] .wizard form.grid{grid-template-columns:1fr!important}.fb-form-wrapper form.grid>[class*=col-span-],[data-form-inline] form.grid>[class*=col-span-]{grid-column:span 1 / span 1!important}}.wizard-nav{gap:1rem;flex-wrap:wrap}.wizard-nav .btn{min-width:7.5rem}@media(max-width:480px){.wizard-nav{flex-direction:column-reverse}.wizard-nav .btn{width:100%}}.fb-form-wrapper .form-buttons,[data-form-inline] .form-buttons{display:flex;gap:1rem;flex-wrap:wrap;margin-top:1.25rem}
|
|
1
|
+
.fb-form-wrapper{max-width:600px;margin:2rem auto}.fb-form-honeypot{position:absolute;left:-9999px;top:-9999px;width:0;height:0;overflow:hidden;opacity:0;z-index:-1}.fb-form-success{padding:1rem 1.25rem;border-radius:var(--dm-radius-md, 6px);background:color-mix(in srgb,var(--dm-success) 12%,transparent);border:1px solid color-mix(in srgb,var(--dm-success) 30%,transparent);color:var(--dm-success);font-size:.95rem;margin-top:.5rem}.fb-form-error{padding:.75rem 1rem;border-radius:var(--dm-radius-md, 6px);background:color-mix(in srgb,var(--dm-danger) 10%,transparent);border:1px solid color-mix(in srgb,var(--dm-danger) 25%,transparent);color:var(--dm-danger);font-size:.875rem;margin-top:.75rem}.fb-form-loading{opacity:.6;pointer-events:none}.wizard-progress{height:4px;background:var(--dm-border);border-radius:2px;margin-bottom:1rem}.wizard-progress-bar{height:100%;background:var(--dm-primary);border-radius:2px;transition:width .3s ease}.wizard-step-title{font-size:1.1rem;font-weight:600;margin-bottom:.25rem}.wizard-step-description{font-size:.875rem;color:var(--dm-text-muted);margin-bottom:1rem}.wizard-nav{display:flex;justify-content:space-between;gap:.5rem;margin-top:1rem}.fb-field-hidden{display:none!important}.fb-validation-error{color:var(--dm-danger);font-size:.8rem;margin-top:.25rem}.fb-field-trigger-hidden{display:none!important}.fb-field-disabled{opacity:.55;pointer-events:none}.fb-trigger-banner{display:flex;align-items:flex-start;gap:.75rem;margin:.75rem 0;padding:.7rem .9rem;border-radius:var(--dm-radius-md);border-left:3px solid var(--dm-info);background:var(--dm-surface-raised);color:var(--dm-text);font-size:.9rem}.fb-trigger-banner--success{border-left-color:var(--dm-success)}.fb-trigger-banner--warning{border-left-color:var(--dm-warning)}.fb-trigger-banner--error{border-left-color:var(--dm-danger)}.fb-trigger-banner-text{flex:1 1 auto}.fb-trigger-banner-close{flex:0 0 auto;background:none;border:0;color:var(--dm-text-muted);font-size:1.1rem;line-height:1;cursor:pointer;padding:0 .15rem}.fb-trigger-banner-close:hover{color:var(--dm-text)}.fb-trigger-block-note{color:var(--dm-danger);font-size:.85rem;margin:.5rem 0 0}.fb-field-trigger-masked{display:none!important}.fb-trigger-end{margin:1rem 0;padding:1rem 1.1rem;border-radius:var(--dm-radius-md);border:1px solid var(--dm-border);border-left:3px solid var(--dm-warning);background:var(--dm-surface-raised);color:var(--dm-text)}.fb-trigger-end-text{margin:0}.fb-form-wrapper .grid>.fb-trigger-banner,.cms-form-embed .grid>.fb-trigger-banner,.fb-form-wrapper .grid>.fb-trigger-end,.cms-form-embed .grid>.fb-trigger-end{grid-column:1 / -1}.fb-form-wrapper .domma-form-field{margin-bottom:2rem}.fb-form-wrapper .form-group:not(.form-buttons){margin-bottom:2rem}.fb-spacer{height:2rem}.fb-field-required .picker-label:after,.fb-field-required>.form-label:after{content:" *";color:var(--dm-danger)}.fb-field-required .picker-label .picker-required{display:none}.fb-field-invalid>.form-label,.fb-field-invalid .picker-label{color:var(--dm-danger)}.fb-field-invalid .form-input,.fb-field-invalid .form-select,.fb-field-invalid textarea,.fb-field-invalid .picker-options{border-color:var(--dm-danger)}@media(max-width:700px){.fb-form-wrapper form.grid,[data-form-inline] form.grid,.fb-form-wrapper .wizard form.grid,[data-form-inline] .wizard form.grid{grid-template-columns:1fr!important}.fb-form-wrapper form.grid>[class*=col-span-],[data-form-inline] form.grid>[class*=col-span-]{grid-column:span 1 / span 1!important}}.wizard-nav{gap:1rem;flex-wrap:wrap}.wizard-nav .btn{min-width:7.5rem}@media(max-width:480px){.wizard-nav{flex-direction:column-reverse}.wizard-nav .btn{width:100%}}.fb-form-wrapper .form-buttons,[data-form-inline] .form-buttons{display:flex;gap:1rem;flex-wrap:wrap;margin-top:1.25rem}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(l,f){typeof module<"u"&&module.exports?module.exports=f():l.FormLogicEngine=f()})(typeof globalThis<"u"?globalThis:this,function(){"use strict";function l(e){return e===""||e===null||e===void 0?"":String(e)}function f(e){const n=parseFloat(e);return isNaN(n)?null:n}function d(e){return e==null?!0:typeof e=="string"?e.trim()==="":Array.isArray(e)?e.length===0:!1}const M={not_equals:"equals",not_contains:"contains",not_in:"in"};function v(e,n){const t=l(n),r=l(e.value);switch(e.operator){case"equals":return t===r;case"not_equals":return t!==r;case"contains":return t.toLowerCase().includes(r.toLowerCase());case"not_contains":return!t.toLowerCase().includes(r.toLowerCase());case"starts_with":return t.toLowerCase().startsWith(r.toLowerCase());case"ends_with":return t.toLowerCase().endsWith(r.toLowerCase());case"greater_than":{const i=f(n),s=f(e.value);return i!==null&&s!==null&&i>s}case"less_than":{const i=f(n),s=f(e.value);return i!==null&&s!==null&&i<s}case"is_empty":return d(n);case"is_not_empty":return!d(n);case"in":return(Array.isArray(e.value)?e.value.map(l):String(e.value).split(",").map(s=>s.trim())).includes(t);case"not_in":return!(Array.isArray(e.value)?e.value.map(l):String(e.value).split(",").map(s=>s.trim())).includes(t);case"matches_regex":try{const i=e.flags||"";return new RegExp(e.value,i).test(t)}catch(i){return typeof console<"u"&&console.warn&&console.warn('[FormLogicEngine] Invalid regex in condition for field "'+e.field+'": '+e.value,i&&i.message),!1}default:return!0}}function g(e,n){if(!e||!e.field||!e.operator)return!0;const t=n[e.field];if(!Array.isArray(t))return v(e,t);if(e.operator==="is_empty")return d(t);if(e.operator==="is_not_empty")return!d(t);const r=M[e.operator];if(r){const i=Object.assign({},e,{operator:r});return!t.some(s=>v(i,s))}return t.some(i=>v(e,i))}function p(e,n){return e?Array.isArray(e.all)?e.all.every(t=>t.all||t.any?p(t,n):g(t,n)):Array.isArray(e.any)?e.any.some(t=>t.all||t.any?p(t,n):g(t,n)):e.field&&e.operator?g(e,n):!0:!0}function w(e,n){const t=e.logic;if(!t||!t.visibility)return"visible";const r=t.visibility,i=r.conditions||[];for(const s of i)if(s.when&&p(s.when,n))return s.then==="hidden"?"hidden":"visible";return r.default==="hidden"?"hidden":"visible"}function y(e,n){const t=e.logic;if(!t||!t.requirement)return e.required||!1;const r=t.requirement,i=r.conditions||[];for(const s of i)if(s.when&&p(s.when,n))return s.then===!0;return r.default===!0}function q(e,n,t){const r=e.logic;if(!r||!Array.isArray(r.validation))return[];const i=[];for(const s of r.validation)switch(s.type){case"regex":{if(!d(n))try{new RegExp(s.pattern||"",s.flags||"").test(l(n))||i.push({message:s.message||"Invalid format."})}catch{}break}case"match":{const a=t[s.field];!d(n)&&l(n)!==l(a)&&i.push({message:s.message||"Fields do not match."});break}}return i}function x(e,n){const t=e.logic;if(!t||!t.cascade||!t.cascade.sourceField)return null;const r=t.cascade,i=r.mapping||{},s=n[r.sourceField];if(Array.isArray(s)){const c=new Set,u=[];return s.forEach(function(m){const _=l(m);Object.prototype.hasOwnProperty.call(i,_)&&(i[_]||[]).forEach(function(S){const I=l(S&&S.value);c.has(I)||(c.add(I),u.push(S))})}),u.length?u:r.defaultOptions||null}const a=l(s);return Object.prototype.hasOwnProperty.call(i,a)?i[a]||[]:r.defaultOptions||null}const R={fade:250,slide:300,scale:200};function k(e,n){const t=Number(n);return!isFinite(t)||t<0||t>5e3?R[e]||250:Math.round(t)}function O(e,n,t){const r=k(n,t);if(n==="fade")e.style.transition="opacity "+r+"ms ease",e.style.opacity="1",requestAnimationFrame(function(){e.style.opacity="0",setTimeout(function(){e.classList.add("fb-field-hidden"),e.style.transition="",e.style.opacity=""},r+10)});else if(n==="slide"){const i=Math.round(r*.67);e.style.overflow="hidden",e.style.maxHeight=e.scrollHeight+"px",e.style.transition="max-height "+r+"ms ease, opacity "+i+"ms ease",requestAnimationFrame(function(){e.style.maxHeight="0",e.style.opacity="0",setTimeout(function(){e.classList.add("fb-field-hidden"),e.style.transition="",e.style.maxHeight="",e.style.opacity="",e.style.overflow=""},r+20)})}else n==="scale"&&(e.style.transition="opacity "+r+"ms ease, transform "+r+"ms ease",e.style.transform="scale(1)",e.style.opacity="1",requestAnimationFrame(function(){e.style.transform="scale(0.95)",e.style.opacity="0",setTimeout(function(){e.classList.add("fb-field-hidden"),e.style.transition="",e.style.transform="",e.style.opacity=""},r+20)}))}function V(e,n,t){const r=k(n,t);if(e.classList.remove("fb-field-hidden"),n==="fade")e.style.opacity="0",e.style.transition="opacity "+r+"ms ease",requestAnimationFrame(function(){e.style.opacity="1",setTimeout(function(){e.style.transition="",e.style.opacity=""},r+10)});else if(n==="slide"){const i=Math.round(r*.67);e.style.overflow="hidden",e.style.maxHeight="0",e.style.opacity="0",e.style.transition="max-height "+r+"ms ease, opacity "+i+"ms ease",requestAnimationFrame(function(){e.style.maxHeight=e.scrollHeight+"px",e.style.opacity="1",setTimeout(function(){e.style.transition="",e.style.maxHeight="",e.style.opacity="",e.style.overflow=""},r+20)})}else n==="scale"&&(e.style.transform="scale(0.95)",e.style.opacity="0",e.style.transition="opacity "+r+"ms ease, transform "+r+"ms ease",requestAnimationFrame(function(){e.style.transform="scale(1)",e.style.opacity="1",setTimeout(function(){e.style.transition="",e.style.transform="",e.style.opacity=""},r+20)}))}const C={banner:"state","disable-fields":"state","hide-fields":"state","require-fields":"state","block-submit":"state",toast:"event",celebrate:"event","goto-step":"event",redirect:"event","run-action":"event"},B=["block-submit","hide-fields","disable-fields","require-fields","redirect","run-action"];function T(e){return!!e&&!!e.name&&e.type!=="page-break"&&e.type!=="spacer"}function b(e,n,t){const r=(t||[]).filter(T),i=e.target||(e.fields&&e.fields.length?"listed":"rest");if(i==="listed"){const a=new Set(e.fields||[]);return r.filter(c=>a.has(c.name)&&c.name!==n.name).map(c=>c.name)}if(i==="all")return r.filter(a=>a.name!==n.name).map(a=>a.name);const s=r.findIndex(a=>a.name===n.name);return s===-1?[]:r.slice(s+1).map(a=>a.name)}function A(e,n){for(const t of n)e.includes(t)||e.push(t)}function H(e,n,t,r,i){if(!n||!n.type)return;const s=C[n.type];if(s){if(s==="event"){e.events.push({key:i,type:n.type,field:t.name,action:n});return}switch(n.type){case"banner":e.banners.push({key:i,field:t.name,message:n.message||"",level:n.level||"info",position:n.position||"field-below",dismissible:n.dismissible!==!1});break;case"hide-fields":A(e.hidden,b(n,t,r));break;case"disable-fields":A(e.disabled,b(n,t,r));break;case"require-fields":A(e.required,b(n,t,r));break;case"block-submit":e.blocks.push({key:i,message:n.message||"This form cannot be submitted yet."});break;default:break}}}function F(e,n){const t={active:[],banners:[],hidden:[],disabled:[],required:[],blocks:[],events:[]};return(e||[]).forEach(r=>{!T(r)||!Array.isArray(r.triggers)||r.triggers.forEach((i,s)=>{if(!i||i.enabled===!1)return;const a=i.id||`${r.name}::${s}`,c=p(i.when,n),u=`${a}:${c?"then":"else"}`,m=c?i.then:i.otherwise;!Array.isArray(m)||!m.length||(t.active.push(u),m.forEach(_=>H(t,_,r,e,u)))})}),t.required=t.required.filter(r=>!t.hidden.includes(r)),t}function N(e){return(e||[]).some(n=>Array.isArray(n&&n.triggers)&&n.triggers.length>0)}function o(e,n,t){this._form=e,this._wrapper=n,this._fields=(e.fields||[]).filter(r=>r.type!=="page-break"),this._listeners=[],this._depMap=new Map,this._initialEval=!1,this._baseValues=t&&t.baseValues||null,this._hasTriggers=N(this._fields),this._triggerState=null,this._triggerPrev=new Set,this._triggerFired=new Set,this._triggerApplied={hidden:[],disabled:[],required:[]},this._banners=new Map,this._dismissed=new Set}function h(e,n,t){const r=t||"",i=n||"";return r+'[name="'+e+'"]'+i+", "+r+'[name="'+e+'[]"]'+i}function W(e){return String(e||"").replace(/\[\]$/,"")}o.prototype._fieldInputs=function(e){return this._wrapper.querySelectorAll(h(e))},o.prototype._fieldInput=function(e){return this._wrapper.querySelector(h(e))},o.prototype._buildDepMap=function(){this._depMap.clear();const e=this;e._fields.forEach(function(n){const t=n.logic;if(!t)return;function r(i){if(!i)return;(i.all||i.any||[]).forEach(function(a){a.field?(e._depMap.has(a.field)||e._depMap.set(a.field,new Set),e._depMap.get(a.field).add(n.name)):r(a)})}if(t.visibility&&(t.visibility.conditions||[]).forEach(function(i){r(i.when)}),t.requirement&&(t.requirement.conditions||[]).forEach(function(i){r(i.when)}),t.cascade&&t.cascade.sourceField){const i=t.cascade.sourceField;e._depMap.has(i)||e._depMap.set(i,new Set),e._depMap.get(i).add(n.name)}})};function L(e,n){const t={};return(n||[]).forEach(function(r){if(!r||!r.name)return;const i=e.querySelectorAll(h(r.name));if(!i.length)return;const s=i[0];if(s.type==="radio"){const a=e.querySelector(h(r.name,":checked"));t[r.name]=a?a.value:""}else if(s.type==="checkbox")if(i.length>1||/\[\]$/.test(s.name||"")){const a=[];i.forEach(function(c){c.checked&&a.push(c.value)}),t[r.name]=a}else t[r.name]=s.checked?s.value||"true":"";else t[r.name]=s.value}),t}function D(e,n){const t=L(e,n),r=[],i=[];return(n||[]).forEach(function(s){if(!s||!s.name||w(s,t)==="hidden")return;const a=t[s.name],c=Array.isArray(a)?a.length===0:!String(a??"").trim();if(y(s,t)&&c){r.push({name:s.name,label:s.label||s.name});return}if(c)return;const u=q(s,a,t);u.length&&i.push(u[0].message)}),{valid:r.length===0&&i.length===0,missing:r,errors:i,values:t}}return o.prototype._getValues=function(){const e=this._baseValues?Object.assign({},this._baseValues):{};return this._fields.forEach(function(n){const t=this._fieldInputs(n.name);if(!t.length)return;const r=t[0];if(r.type==="radio"){const i=this._wrapper.querySelector(h(n.name,":checked"));e[n.name]=i?i.value:""}else if(r.type==="checkbox")if(t.length>1||/\[\]$/.test(r.name||"")){const i=[];t.forEach(function(s){s.checked&&i.push(s.value)}),e[n.name]=i}else e[n.name]=r.checked?r.value||"true":"";else e[n.name]=r.value},this),e},o.prototype._applyVisibility=function(e,n){const r=w(e,n)==="hidden",i=this._findFieldWrapper(e.name);if(!i)return;i.setAttribute("aria-hidden",String(r));const s=this._initialEval?"none":e.logic?.visibility?.transition||"none";if(s==="none"){i.classList.toggle("fb-field-hidden",r);return}const a=e.logic?.visibility?.duration;i.style.transition="",r?O(i,s,a):V(i,s,a)},o.prototype._applyRequirement=function(e,n){const t=y(e,n),r=this._fieldInput(e.name);if(!r)return;if(/\[\]$/.test(r.name||"")||r.closest&&r.closest(".domma-chooser")){const s=this._findFieldWrapper(e.name);r.removeAttribute("required"),s&&(s.setAttribute("aria-required",String(t)),s.classList.toggle("fb-field-required",t));return}t?r.setAttribute("required",""):r.removeAttribute("required");const i=this._findFieldWrapper(e.name);i&&(i.setAttribute("aria-required",String(t)),i.classList.toggle("fb-field-required",t))},o.prototype._applyCascade=function(e,n){const t=x(e,n);if(t===null)return;const r=this._wrapper.querySelector(h(e.name,"","select"));if(!r)return;const i=r.value;r.textContent="",t.forEach(function(s){const a=document.createElement("option");a.value=s.value,a.textContent=s.label||s.value,s.value===i&&(a.selected=!0),r.appendChild(a)})},o.prototype._applyValidation=function(e,n){const t=this._fieldInput(e.name);if(!t)return;const r=q(e,n[e.name],n);let i=t.parentNode.querySelector(".fb-validation-error");r.length?(i||(i=document.createElement("div"),i.className="fb-validation-error",t.parentNode.appendChild(i)),i.textContent=r[0].message):i&&i.remove()},o.prototype._findFieldWrapper=function(e){const n=this._fieldInput(e);if(!n)return null;const t=n.closest("form")||this._wrapper;let r=n;for(;r.parentNode&&r.parentNode!==t;)r=r.parentNode;return r},o.prototype.evaluate=function(e){const n=this._getValues(),t=this;let r;if(e&&this._depMap.has(e)){const i=this._depMap.get(e);r=this._fields.filter(function(s){return i.has(s.name)})}else r=this._fields;r.forEach(function(i){i.logic&&(t._applyVisibility(i,n),t._applyRequirement(i,n),t._applyCascade(i,n))}),this._applyTriggers(n)},o.prototype.init=function(){this._buildDepMap();const e=this;this._wrapper.querySelectorAll("input, select, textarea").forEach(function(r){const i=W(r.getAttribute("name"));if(!i)return;const s=function(){e.evaluate(i)};r.addEventListener("input",s),r.addEventListener("change",s),e._listeners.push({el:r,event:"input",fn:s}),e._listeners.push({el:r,event:"change",fn:s});const a=e._fields.find(function(c){return c.name===i});if(a&&a.logic&&Array.isArray(a.logic.validation)&&a.logic.validation.length){const c=function(){const u=e._getValues();e._applyValidation(a,u)};r.addEventListener("blur",c),e._listeners.push({el:r,event:"blur",fn:c})}}),this._wrapper.querySelectorAll("[data-chooser-field]").forEach(function(r){const i=r.getAttribute("data-chooser-field");if(!i)return;const s=function(){setTimeout(function(){e.evaluate(i)},0)};r.addEventListener("click",s),e._listeners.push({el:r,event:"click",fn:s})}),this._initialEval=!0,this.evaluate(),this._initialEval=!1},o.prototype.destroy=function(){this._listeners.forEach(function(e){e.el.removeEventListener(e.event,e.fn)}),this._listeners=[],this._banners.forEach(function(e){e&&e.parentNode&&e.parentNode.removeChild(e)}),this._banners.clear()},o.prototype._applyTriggers=function(e){if(!this._hasTriggers)return;const n=F(this._fields,e);this._triggerState=n;const t=this._triggerApplied||{hidden:[],disabled:[],required:[]};t.hidden.forEach(r=>{n.hidden.indexOf(r)===-1&&this._setTriggerHidden(r,!1)}),n.hidden.forEach(r=>this._setTriggerHidden(r,!0)),t.disabled.forEach(r=>{n.disabled.indexOf(r)===-1&&this._setTriggerDisabled(r,!1)}),n.disabled.forEach(r=>this._setTriggerDisabled(r,!0)),t.required.forEach(r=>{n.required.indexOf(r)===-1&&this._restoreRequired(r,e)}),n.required.forEach(r=>this._setTriggerRequired(r,!0)),this._triggerApplied={hidden:n.hidden.slice(),disabled:n.disabled.slice(),required:n.required.slice()},this._syncBanners(n.banners),this._syncSubmitBlock(n.blocks),this._fireTriggerEvents(n)},o.prototype._setTriggerHidden=function(e,n){const t=this._findFieldWrapper(e);t&&(t.classList.toggle("fb-field-trigger-hidden",n),t.setAttribute("aria-hidden",String(n||t.classList.contains("fb-field-hidden"))))},o.prototype._setTriggerDisabled=function(e,n){const t=this._findFieldWrapper(e);t&&t.classList.toggle("fb-field-disabled",n),this._fieldInputs(e).forEach(function(i){i.disabled=n});const r=this._wrapper.querySelector('[data-chooser-field="'+e+'"]');r&&(r.classList.toggle("fb-field-disabled",n),r.querySelectorAll('button, [role="radio"], [role="checkbox"]').forEach(function(i){n?i.setAttribute("aria-disabled","true"):i.removeAttribute("aria-disabled")}))},o.prototype._setTriggerRequired=function(e,n){const t=this._fieldInput(e),r=this._findFieldWrapper(e);if(r&&(r.setAttribute("aria-required",String(n)),r.classList.toggle("fb-field-required",n)),!!t){if(/\[\]$/.test(t.name||"")||t.closest&&t.closest(".domma-chooser")){t.removeAttribute("required");return}n?t.setAttribute("required",""):t.removeAttribute("required")}},o.prototype._restoreRequired=function(e,n){const t=this._fields.find(function(r){return r.name===e});t&&this._setTriggerRequired(e,y(t,n))},o.prototype._syncBanners=function(e){const n=new Map;e.forEach(function(t){n.set(t.key,t)}),this._banners.forEach(function(t,r){n.has(r)||(t.parentNode&&t.parentNode.removeChild(t),this._banners.delete(r))},this),this._dismissed.forEach(function(t){n.has(t)||this._dismissed.delete(t)},this),n.forEach(function(t,r){if(this._banners.has(r)){const a=this._banners.get(r).querySelector(".fb-trigger-banner-text");a&&a.textContent!==t.message&&(a.textContent=t.message);return}if(this._dismissed.has(r))return;const i=this._buildBanner(t,r);i&&this._banners.set(r,i)},this)},o.prototype._buildBanner=function(e,n){if(typeof document>"u"||!e.message)return null;const t=document.createElement("div");t.className="fb-trigger-banner fb-trigger-banner--"+(e.level||"info"),t.setAttribute("role",e.level==="error"?"alert":"status"),t.dataset.triggerKey=n;const r=document.createElement("span");if(r.className="fb-trigger-banner-text",r.textContent=e.message,t.appendChild(r),e.dismissible){const a=document.createElement("button");a.type="button",a.className="fb-trigger-banner-close",a.setAttribute("aria-label","Dismiss"),a.textContent="\xD7";const c=this;a.addEventListener("click",function(){c._dismissed.add(n),t.parentNode&&t.parentNode.removeChild(t),c._banners.delete(n)}),t.appendChild(a)}const i=this._findFieldWrapper(e.field),s=this._wrapper.querySelector("form")||this._wrapper;return e.position==="form-top"?s.insertBefore(t,s.firstChild):i&&i.parentNode?e.position==="field-above"?i.parentNode.insertBefore(t,i):i.parentNode.insertBefore(t,i.nextSibling):s.appendChild(t),t},o.prototype._syncSubmitBlock=function(e){const n=this._wrapper.querySelector("form")||this._wrapper,t=n.querySelector('button[type="submit"], input[type="submit"], .fb-form-submit'),r=e.length>0;this._wrapper.dataset.triggerBlocked=r?"true":"false",t&&(t.disabled=r,t.setAttribute("aria-disabled",String(r)));let i=n.querySelector(".fb-trigger-block-note");if(!r){i&&i.parentNode&&i.parentNode.removeChild(i);return}i||(i=document.createElement("p"),i.className="fb-trigger-block-note",i.setAttribute("role","status"),(t&&t.parentNode?t.parentNode:n).appendChild(i)),i.textContent=e.map(function(s){return s.message}).join(" ")},o.prototype._fireTriggerEvents=function(e){const n=new Set(e.active),t=this;e.events.forEach(function(r){t._triggerPrev.has(r.key)||r.action.once&&t._triggerFired.has(r.key)||t._initialEval&&r.action.onLoad!==!0||(t._triggerFired.add(r.key),t._runTriggerEvent(r))}),this._triggerPrev=n},o.prototype._runTriggerEvent=function(e){const n=e.action;switch(n.type){case"toast":typeof E<"u"&&E&&typeof E.toast=="function"&&E.toast(n.message||"",{type:n.level||"info",duration:Number(n.duration)||4e3});break;case"celebrate":this._celebrate(n);break;default:break}typeof CustomEvent<"u"&&this._wrapper.dispatchEvent&&this._wrapper.dispatchEvent(new CustomEvent("dm-form-trigger",{bubbles:!0,detail:{type:n.type,field:e.field,action:n}}))},o.prototype._celebrate=function(e){typeof window>"u"||typeof document>"u"||window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches||import("/public/js/celebrations/index.js").then(function(n){const t=n.CelebrationsEffect;let r=e.theme||"auto";if(r==="auto"&&(r=t.getCurrentTheme(),!r))return;const i=new t({theme:r,intensity:e.intensity||"medium",enabled:!0,zIndex:999});i.init().then(function(){i.start()})}).catch(function(){})},o.prototype.submitBlock=function(){const e=this._triggerState&&this._triggerState.blocks||[];return{blocked:e.length>0,reasons:e.map(function(n){return n.message})}},{evaluateCondition:g,evaluateConditionGroup:p,evaluateFieldVisibility:w,evaluateFieldRequirement:y,validateField:q,collectValues:L,validateFields:D,getCascadeOptions:x,resolveTriggerState:F,resolveTriggerTargets:b,hasTriggers:N,TRIGGER_ACTION_KINDS:C,SERVER_TRIGGER_ACTIONS:B,FormLogicRuntime:o}});
|
|
1
|
+
(function(u,f){typeof module<"u"&&module.exports?module.exports=f():u.FormLogicEngine=f()})(typeof globalThis<"u"?globalThis:this,function(){"use strict";function u(e){return e===""||e===null||e===void 0?"":String(e)}function f(e){const i=parseFloat(e);return isNaN(i)?null:i}function p(e){return e==null?!0:typeof e=="string"?e.trim()==="":Array.isArray(e)?e.length===0:!1}const O={not_equals:"equals",not_contains:"contains",not_in:"in"};function w(e,i){const t=u(i),n=u(e.value);switch(e.operator){case"equals":return t===n;case"not_equals":return t!==n;case"contains":return t.toLowerCase().includes(n.toLowerCase());case"not_contains":return!t.toLowerCase().includes(n.toLowerCase());case"starts_with":return t.toLowerCase().startsWith(n.toLowerCase());case"ends_with":return t.toLowerCase().endsWith(n.toLowerCase());case"greater_than":{const r=f(i),s=f(e.value);return r!==null&&s!==null&&r>s}case"less_than":{const r=f(i),s=f(e.value);return r!==null&&s!==null&&r<s}case"is_empty":return p(i);case"is_not_empty":return!p(i);case"in":return(Array.isArray(e.value)?e.value.map(u):String(e.value).split(",").map(s=>s.trim())).includes(t);case"not_in":return!(Array.isArray(e.value)?e.value.map(u):String(e.value).split(",").map(s=>s.trim())).includes(t);case"matches_regex":try{const r=e.flags||"";return new RegExp(e.value,r).test(t)}catch(r){return typeof console<"u"&&console.warn&&console.warn('[FormLogicEngine] Invalid regex in condition for field "'+e.field+'": '+e.value,r&&r.message),!1}default:return!0}}function b(e,i){if(!e||!e.field||!e.operator)return!0;const t=i[e.field];if(!Array.isArray(t))return w(e,t);if(e.operator==="is_empty")return p(t);if(e.operator==="is_not_empty")return!p(t);const n=O[e.operator];if(n){const r=Object.assign({},e,{operator:n});return!t.some(s=>w(r,s))}return t.some(r=>w(e,r))}function h(e,i){return e?Array.isArray(e.all)?e.all.every(t=>t.all||t.any?h(t,i):b(t,i)):Array.isArray(e.any)?e.any.some(t=>t.all||t.any?h(t,i):b(t,i)):e.field&&e.operator?b(e,i):!0:!0}function q(e,i){const t=e.logic;if(!t||!t.visibility)return"visible";const n=t.visibility,r=n.conditions||[];for(const s of r)if(s.when&&h(s.when,i))return s.then==="hidden"?"hidden":"visible";return n.default==="hidden"?"hidden":"visible"}function _(e,i){const t=e.logic;if(!t||!t.requirement)return e.required||!1;const n=t.requirement,r=n.conditions||[];for(const s of r)if(s.when&&h(s.when,i))return s.then===!0;return n.default===!0}function A(e,i,t){const n=e.logic;if(!n||!Array.isArray(n.validation))return[];const r=[];for(const s of n.validation)switch(s.type){case"regex":{if(!p(i))try{new RegExp(s.pattern||"",s.flags||"").test(u(i))||r.push({message:s.message||"Invalid format."})}catch{}break}case"match":{const a=t[s.field];!p(i)&&u(i)!==u(a)&&r.push({message:s.message||"Fields do not match."});break}}return r}function S(e,i){const t=e.logic;if(!t||!t.cascade||!t.cascade.sourceField)return null;const n=t.cascade,r=n.mapping||{},s=i[n.sourceField];if(Array.isArray(s)){const o=new Set,l=[];return s.forEach(function(g){const d=u(g);Object.prototype.hasOwnProperty.call(r,d)&&(r[d]||[]).forEach(function(x){const B=u(x&&x.value);o.has(B)||(o.add(B),l.push(x))})}),l.length?l:n.defaultOptions||null}const a=u(s);return Object.prototype.hasOwnProperty.call(r,a)?r[a]||[]:n.defaultOptions||null}const V={fade:250,slide:300,scale:200};function C(e,i){const t=Number(i);return!isFinite(t)||t<0||t>5e3?V[e]||250:Math.round(t)}function W(e,i,t){const n=C(i,t);if(i==="fade")e.style.transition="opacity "+n+"ms ease",e.style.opacity="1",requestAnimationFrame(function(){e.style.opacity="0",setTimeout(function(){e.classList.add("fb-field-hidden"),e.style.transition="",e.style.opacity=""},n+10)});else if(i==="slide"){const r=Math.round(n*.67);e.style.overflow="hidden",e.style.maxHeight=e.scrollHeight+"px",e.style.transition="max-height "+n+"ms ease, opacity "+r+"ms ease",requestAnimationFrame(function(){e.style.maxHeight="0",e.style.opacity="0",setTimeout(function(){e.classList.add("fb-field-hidden"),e.style.transition="",e.style.maxHeight="",e.style.opacity="",e.style.overflow=""},n+20)})}else i==="scale"&&(e.style.transition="opacity "+n+"ms ease, transform "+n+"ms ease",e.style.transform="scale(1)",e.style.opacity="1",requestAnimationFrame(function(){e.style.transform="scale(0.95)",e.style.opacity="0",setTimeout(function(){e.classList.add("fb-field-hidden"),e.style.transition="",e.style.transform="",e.style.opacity=""},n+20)}))}function D(e,i,t){const n=C(i,t);if(e.classList.remove("fb-field-hidden"),i==="fade")e.style.opacity="0",e.style.transition="opacity "+n+"ms ease",requestAnimationFrame(function(){e.style.opacity="1",setTimeout(function(){e.style.transition="",e.style.opacity=""},n+10)});else if(i==="slide"){const r=Math.round(n*.67);e.style.overflow="hidden",e.style.maxHeight="0",e.style.opacity="0",e.style.transition="max-height "+n+"ms ease, opacity "+r+"ms ease",requestAnimationFrame(function(){e.style.maxHeight=e.scrollHeight+"px",e.style.opacity="1",setTimeout(function(){e.style.transition="",e.style.maxHeight="",e.style.opacity="",e.style.overflow=""},n+20)})}else i==="scale"&&(e.style.transform="scale(0.95)",e.style.opacity="0",e.style.transition="opacity "+n+"ms ease, transform "+n+"ms ease",requestAnimationFrame(function(){e.style.transform="scale(1)",e.style.opacity="1",setTimeout(function(){e.style.transition="",e.style.transform="",e.style.opacity=""},n+20)}))}const T={banner:"state","disable-fields":"state","hide-fields":"state","require-fields":"state","block-submit":"state","end-form":"state",toast:"event",celebrate:"event","goto-step":"event",redirect:"event","run-action":"event",notify:"event"},H=["block-submit","hide-fields","disable-fields","require-fields","end-form","redirect","run-action","notify"];function k(e){return!!e&&!!e.name&&e.type!=="page-break"&&e.type!=="spacer"}function y(e,i,t){const n=(t||[]).filter(k),r=e.target||(e.fields&&e.fields.length?"listed":"rest");if(r==="listed"){const a=new Set(e.fields||[]);return n.filter(o=>a.has(o.name)&&o.name!==i.name).map(o=>o.name)}if(r==="all")return n.filter(a=>a.name!==i.name).map(a=>a.name);const s=n.findIndex(a=>a.name===i.name);return s===-1?[]:n.slice(s+1).map(a=>a.name)}function v(e,i){for(const t of i)e.includes(t)||e.push(t)}function P(e,i,t,n,r){if(!i||!i.type)return;const s=T[i.type];if(s){if(s==="event"){e.events.push({key:r,type:i.type,field:t.name,action:i});return}switch(i.type){case"banner":e.banners.push({key:r,field:t.name,message:i.message||"",level:i.level||"info",position:i.position||"field-below",dismissible:i.dismissible!==!1});break;case"hide-fields":v(e.hidden,y(i,t,n));break;case"disable-fields":v(e.disabled,y(i,t,n));break;case"require-fields":v(e.required,y(i,t,n));break;case"block-submit":e.blocks.push({key:r,message:i.message||"This form cannot be submitted yet."});break;case"end-form":v(e.masked,y({target:"rest"},t,n)),e.ends.push({key:r,field:t.name,message:i.message||"",finishLabel:i.finishLabel||"Finish",doneMessage:i.doneMessage||"",record:i.record!==!1,finish:i.finish!==!1}),i.finish===!1&&e.blocks.push({key:r,quiet:!0,message:i.message||"This form cannot be submitted."});break;default:break}}}function N(e,i){const t={active:[],banners:[],hidden:[],disabled:[],required:[],blocks:[],events:[],masked:[],ends:[]};return(e||[]).forEach(n=>{!k(n)||!Array.isArray(n.triggers)||n.triggers.forEach((r,s)=>{if(!r||r.enabled===!1)return;const a=r.id||`${n.name}::${s}`,o=h(r.when,i),l=`${a}:${o?"then":"else"}`,g=o?r.then:r.otherwise;!Array.isArray(g)||!g.length||(t.active.push(l),g.forEach(d=>P(t,d,n,e,l)))})}),t.required=t.required.filter(n=>!t.hidden.includes(n)&&!t.masked.includes(n)),t}function F(e){return(e||[]).some(i=>Array.isArray(i&&i.triggers)&&i.triggers.length>0)}function c(e,i,t){this._form=e,this._wrapper=i,this._fields=(e.fields||[]).filter(n=>n.type!=="page-break"),this._listeners=[],this._depMap=new Map,this._initialEval=!1,this._baseValues=t&&t.baseValues||null,this._hasTriggers=F(this._fields),this._triggerState=null,this._triggerPrev=new Set,this._triggerFired=new Set,this._triggerApplied={hidden:[],disabled:[],required:[],masked:[]},this._banners=new Map,this._dismissed=new Set,this._end=null}function m(e,i,t){const n=t||"",r=i||"";return n+'[name="'+e+'"]'+r+", "+n+'[name="'+e+'[]"]'+r}function $(e){return String(e||"").replace(/\[\]$/,"")}c.prototype._fieldInputs=function(e){return this._wrapper.querySelectorAll(m(e))},c.prototype._fieldInput=function(e){return this._wrapper.querySelector(m(e))},c.prototype._buildDepMap=function(){this._depMap.clear();const e=this;e._fields.forEach(function(i){const t=i.logic;if(!t)return;function n(r){if(!r)return;(r.all||r.any||[]).forEach(function(a){a.field?(e._depMap.has(a.field)||e._depMap.set(a.field,new Set),e._depMap.get(a.field).add(i.name)):n(a)})}if(t.visibility&&(t.visibility.conditions||[]).forEach(function(r){n(r.when)}),t.requirement&&(t.requirement.conditions||[]).forEach(function(r){n(r.when)}),t.cascade&&t.cascade.sourceField){const r=t.cascade.sourceField;e._depMap.has(r)||e._depMap.set(r,new Set),e._depMap.get(r).add(i.name)}})};function L(e,i){const t={};return(i||[]).forEach(function(n){if(!n||!n.name)return;const r=e.querySelectorAll(m(n.name));if(!r.length)return;const s=r[0];if(s.type==="radio"){const a=e.querySelector(m(n.name,":checked"));t[n.name]=a?a.value:""}else if(s.type==="checkbox")if(r.length>1||/\[\]$/.test(s.name||"")){const a=[];r.forEach(function(o){o.checked&&a.push(o.value)}),t[n.name]=a}else t[n.name]=s.checked?s.value||"true":"";else t[n.name]=s.value}),t}function I(e,i){const t=L(e,i),n=[],r=[];return(i||[]).forEach(function(s){if(!s||!s.name||q(s,t)==="hidden")return;const a=t[s.name],o=Array.isArray(a)?a.length===0:!String(a??"").trim();if(_(s,t)&&o){n.push({name:s.name,label:s.label||s.name});return}if(o)return;const l=A(s,a,t);l.length&&r.push(l[0].message)}),{valid:n.length===0&&r.length===0,missing:n,errors:r,values:t}}c.prototype._getValues=function(){const e=this._baseValues?Object.assign({},this._baseValues):{};return this._fields.forEach(function(i){const t=this._fieldInputs(i.name);if(!t.length)return;const n=t[0];if(n.type==="radio"){const r=this._wrapper.querySelector(m(i.name,":checked"));e[i.name]=r?r.value:""}else if(n.type==="checkbox")if(t.length>1||/\[\]$/.test(n.name||"")){const r=[];t.forEach(function(s){s.checked&&r.push(s.value)}),e[i.name]=r}else e[i.name]=n.checked?n.value||"true":"";else e[i.name]=n.value},this),e},c.prototype._applyVisibility=function(e,i){const n=q(e,i)==="hidden",r=this._findFieldWrapper(e.name);if(!r)return;r.setAttribute("aria-hidden",String(n));const s=this._initialEval?"none":e.logic?.visibility?.transition||"none";if(s==="none"){r.classList.toggle("fb-field-hidden",n);return}const a=e.logic?.visibility?.duration;r.style.transition="",n?W(r,s,a):D(r,s,a)},c.prototype._applyRequirement=function(e,i){const t=_(e,i),n=this._fieldInput(e.name);if(!n)return;if(/\[\]$/.test(n.name||"")||n.closest&&n.closest(".domma-chooser")){const s=this._findFieldWrapper(e.name);n.removeAttribute("required"),s&&(s.setAttribute("aria-required",String(t)),s.classList.toggle("fb-field-required",t));return}t?n.setAttribute("required",""):n.removeAttribute("required");const r=this._findFieldWrapper(e.name);r&&(r.setAttribute("aria-required",String(t)),r.classList.toggle("fb-field-required",t))},c.prototype._applyCascade=function(e,i){const t=S(e,i);if(t===null)return;const n=this._wrapper.querySelector(m(e.name,"","select"));if(!n)return;const r=n.value;n.textContent="",t.forEach(function(s){const a=document.createElement("option");a.value=s.value,a.textContent=s.label||s.value,s.value===r&&(a.selected=!0),n.appendChild(a)})},c.prototype._applyValidation=function(e,i){const t=this._fieldInput(e.name);if(!t)return;const n=A(e,i[e.name],i);let r=t.parentNode.querySelector(".fb-validation-error");n.length?(r||(r=document.createElement("div"),r.className="fb-validation-error",t.parentNode.appendChild(r)),r.textContent=n[0].message):r&&r.remove()},c.prototype._findFieldWrapper=function(e){const i=this._fieldInput(e);if(!i)return null;const t=i.closest("form")||this._wrapper;let n=i;for(;n.parentNode&&n.parentNode!==t;)n=n.parentNode;return n},c.prototype.evaluate=function(e){const i=this._getValues(),t=this;let n;if(e&&this._depMap.has(e)){const r=this._depMap.get(e);n=this._fields.filter(function(s){return r.has(s.name)})}else n=this._fields;n.forEach(function(r){r.logic&&(t._applyVisibility(r,i),t._applyRequirement(r,i),t._applyCascade(r,i))}),this._applyTriggers(i)},c.prototype.init=function(){this._buildDepMap();const e=this;if(this._wrapper.querySelectorAll("input, select, textarea").forEach(function(n){const r=$(n.getAttribute("name"));if(!r)return;const s=function(){e.evaluate(r)};n.addEventListener("input",s),n.addEventListener("change",s),e._listeners.push({el:n,event:"input",fn:s}),e._listeners.push({el:n,event:"change",fn:s});const a=e._fields.find(function(o){return o.name===r});if(a&&a.logic&&Array.isArray(a.logic.validation)&&a.logic.validation.length){const o=function(){const l=e._getValues();e._applyValidation(a,l)};n.addEventListener("blur",o),e._listeners.push({el:n,event:"blur",fn:o})}}),this._wrapper.querySelectorAll("[data-chooser-field]").forEach(function(n){const r=n.getAttribute("data-chooser-field");if(!r)return;const s=function(){setTimeout(function(){e.evaluate(r)},0)};n.addEventListener("click",s),e._listeners.push({el:n,event:"click",fn:s})}),this._hasTriggers){const n=function(r){if(e._end&&!(r.type==="click"&&!(r.target&&r.target.closest&&r.target.closest(z)))){if(!e._end.spec.finish){r.preventDefault(),r.stopImmediatePropagation();return}e._wrapper.dataset.triggerBlocked!=="true"&&(r.preventDefault(),r.stopImmediatePropagation(),e._finishEnded())}};this._wrapper.addEventListener("click",n,!0),this._wrapper.addEventListener("submit",n,!0),this._listeners.push({el:this._wrapper,event:"click",fn:n,capture:!0}),this._listeners.push({el:this._wrapper,event:"submit",fn:n,capture:!0})}this._initialEval=!0,this.evaluate(),this._initialEval=!1},c.prototype.destroy=function(){this._listeners.forEach(function(e){e.el.removeEventListener(e.event,e.fn,!!e.capture)}),this._listeners=[],this._banners.forEach(function(e){e&&e.parentNode&&e.parentNode.removeChild(e)}),this._banners.clear(),this._syncEnd([])},c.prototype._applyTriggers=function(e){if(!this._hasTriggers)return;const i=N(this._fields,e);this._triggerState=i;const t=this._triggerApplied||{hidden:[],disabled:[],required:[]};t.hidden.forEach(n=>{i.hidden.indexOf(n)===-1&&this._setTriggerHidden(n,!1)}),i.hidden.forEach(n=>this._setTriggerHidden(n,!0)),t.disabled.forEach(n=>{i.disabled.indexOf(n)===-1&&this._setTriggerDisabled(n,!1)}),i.disabled.forEach(n=>this._setTriggerDisabled(n,!0)),t.required.forEach(n=>{i.required.indexOf(n)===-1&&this._restoreRequired(n,e)}),i.required.forEach(n=>this._setTriggerRequired(n,!0)),(t.masked||[]).forEach(n=>{i.masked.indexOf(n)===-1&&this._setTriggerMasked(n,!1)}),i.masked.forEach(n=>this._setTriggerMasked(n,!0)),this._triggerApplied={hidden:i.hidden.slice(),disabled:i.disabled.slice(),required:i.required.slice(),masked:i.masked.slice()},this._syncBanners(i.banners),this._syncSubmitBlock(i.blocks),this._syncEnd(i.ends),this._fireTriggerEvents(i)},c.prototype._setTriggerHidden=function(e,i){const t=this._findFieldWrapper(e);t&&(t.classList.toggle("fb-field-trigger-hidden",i),t.setAttribute("aria-hidden",String(i||t.classList.contains("fb-field-hidden"))))},c.prototype._setTriggerMasked=function(e,i){const t=this._findFieldWrapper(e);t&&(t.classList.toggle("fb-field-trigger-masked",i),t.setAttribute("aria-hidden",String(i||t.classList.contains("fb-field-hidden")||t.classList.contains("fb-field-trigger-hidden"))))},c.prototype._setTriggerDisabled=function(e,i){const t=this._findFieldWrapper(e);t&&t.classList.toggle("fb-field-disabled",i),this._fieldInputs(e).forEach(function(r){r.disabled=i});const n=this._wrapper.querySelector('[data-chooser-field="'+e+'"]');n&&(n.classList.toggle("fb-field-disabled",i),n.querySelectorAll('button, [role="radio"], [role="checkbox"]').forEach(function(r){i?r.setAttribute("aria-disabled","true"):r.removeAttribute("aria-disabled")}))},c.prototype._setTriggerRequired=function(e,i){const t=this._fieldInput(e),n=this._findFieldWrapper(e);if(n&&(n.setAttribute("aria-required",String(i)),n.classList.toggle("fb-field-required",i)),!!t){if(/\[\]$/.test(t.name||"")||t.closest&&t.closest(".domma-chooser")){t.removeAttribute("required");return}i?t.setAttribute("required",""):t.removeAttribute("required")}},c.prototype._restoreRequired=function(e,i){const t=this._fields.find(function(n){return n.name===e});t&&this._setTriggerRequired(e,_(t,i))},c.prototype._syncBanners=function(e){const i=new Map;e.forEach(function(t){i.set(t.key,t)}),this._banners.forEach(function(t,n){i.has(n)||(t.parentNode&&t.parentNode.removeChild(t),this._banners.delete(n))},this),this._dismissed.forEach(function(t){i.has(t)||this._dismissed.delete(t)},this),i.forEach(function(t,n){if(this._banners.has(n)){const a=this._banners.get(n).querySelector(".fb-trigger-banner-text");a&&a.textContent!==t.message&&(a.textContent=t.message);return}if(this._dismissed.has(n))return;const r=this._buildBanner(t,n);r&&this._banners.set(n,r)},this)},c.prototype._buildBanner=function(e,i){if(typeof document>"u"||!e.message)return null;const t=document.createElement("div");t.className="fb-trigger-banner fb-trigger-banner--"+(e.level||"info"),t.setAttribute("role",e.level==="error"?"alert":"status"),t.dataset.triggerKey=i;const n=document.createElement("span");if(n.className="fb-trigger-banner-text",n.textContent=e.message,t.appendChild(n),e.dismissible){const a=document.createElement("button");a.type="button",a.className="fb-trigger-banner-close",a.setAttribute("aria-label","Dismiss"),a.textContent="\xD7";const o=this;a.addEventListener("click",function(){o._dismissed.add(i),t.parentNode&&t.parentNode.removeChild(t),o._banners.delete(i)}),t.appendChild(a)}const r=this._findFieldWrapper(e.field),s=this._wrapper.querySelector("form")||this._wrapper;return e.position==="form-top"?s.insertBefore(t,s.firstChild):r&&r.parentNode?e.position==="field-above"?r.parentNode.insertBefore(t,r):r.parentNode.insertBefore(t,r.nextSibling):s.appendChild(t),t},c.prototype._syncSubmitBlock=function(e){const i=this._wrapper.querySelector("form")||this._wrapper,t=i.querySelector('button[type="submit"], input[type="submit"], .fb-form-submit'),n=e.length>0;this._wrapper.dataset.triggerBlocked=n?"true":"false",t&&(t.disabled=n,t.setAttribute("aria-disabled",String(n)));let r=i.querySelector(".fb-trigger-block-note");const s=e.filter(function(a){return!a.quiet}).map(function(a){return a.message});if(!s.length){r&&r.parentNode&&r.parentNode.removeChild(r);return}r||(r=document.createElement("p"),r.className="fb-trigger-block-note",r.setAttribute("role","status"),(t&&t.parentNode?t.parentNode:i).appendChild(r)),r.textContent=s.join(" ")};const z='button[type="submit"], input[type="submit"], .fb-form-submit, .wizard-next, .wizard-submit';c.prototype._primaryButton=function(){return this._wrapper.querySelector(".wizard-next, .wizard-submit")||this._wrapper.querySelector('button[type="submit"], input[type="submit"], .fb-form-submit')};function M(e){return!!(e.matches&&e.matches(".wizard-next, .wizard-submit"))}function R(e,i){e.tagName==="INPUT"?e.value=i:e.textContent=i}return c.prototype._syncEnd=function(e){const i=e&&e.length?e[0]:null,t=this._end;if(t&&(!i||t.spec.key!==i.key)&&(t.panel&&t.panel.parentNode&&t.panel.parentNode.removeChild(t.panel),t.button&&t.button.isConnected&&(t.spec.finish?R(t.button,t.label):M(t.button)&&(t.button.disabled=!1)),this._end=null),this._wrapper.dataset.triggerEnded=i?"true":"false",!i||typeof document>"u")return;if(this._end){const a=this._end.panel&&this._end.panel.querySelector(".fb-trigger-end-text");a&&a.textContent!==i.message&&(a.textContent=i.message),this._end.spec=i;return}let n=null;if(i.message){n=document.createElement("div"),n.className="fb-trigger-end",n.setAttribute("role","status"),n.dataset.triggerKey=i.key;const a=document.createElement("div");a.className="fb-trigger-end-text",a.textContent=i.message,n.appendChild(a);const o=this._findFieldWrapper(i.field);o&&o.parentNode?o.parentNode.insertBefore(n,o.nextSibling):(this._wrapper.querySelector("form")||this._wrapper).appendChild(n)}const r=this._primaryButton();let s="";r&&i.finish?(s=r.tagName==="INPUT"?r.value:r.textContent,R(r,i.finishLabel)):r&&M(r)&&(r.disabled=!0),this._end={spec:i,panel:n,button:r,label:s}},c.prototype._finishEnded=function(){const e=this._triggerState||{},i=new Set([].concat(e.masked||[],e.hidden||[],e.disabled||[])),t=this,n=this._fields.filter(function(a){return k(a)&&!i.has(a.name)&&t._fieldInput(a.name)}),r=this._getValues();(e.masked||[]).forEach(function(a){delete r[a]});const s=I(this._wrapper,n);(e.required||[]).forEach(function(a){const o=n.find(function(d){return d.name===a});if(!o)return;const l=r[a];(Array.isArray(l)?l.length===0:!String(l??"").trim())&&!s.missing.some(function(d){return d.name===a})&&(s.missing.push({name:a,label:o.label||a}),s.valid=!1)}),this._wrapper.dispatchEvent(new CustomEvent("dm-form-end",{bubbles:!0,detail:{values:r,validation:s,end:this._end?this._end.spec:null}}))},c.prototype._fireTriggerEvents=function(e){const i=new Set(e.active),t=this;e.events.forEach(function(n){t._triggerPrev.has(n.key)||n.action.once&&t._triggerFired.has(n.key)||t._initialEval&&n.action.onLoad!==!0||(t._triggerFired.add(n.key),t._runTriggerEvent(n))}),this._triggerPrev=i},c.prototype._runTriggerEvent=function(e){const i=e.action;switch(i.type){case"toast":typeof E<"u"&&E&&typeof E.toast=="function"&&E.toast(i.message||"",{type:i.level||"info",duration:Number(i.duration)||4e3});break;case"celebrate":this._celebrate(i);break;default:break}typeof CustomEvent<"u"&&this._wrapper.dispatchEvent&&this._wrapper.dispatchEvent(new CustomEvent("dm-form-trigger",{bubbles:!0,detail:{type:i.type,field:e.field,action:i}}))},c.prototype._celebrate=function(e){typeof window>"u"||typeof document>"u"||window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches||import("/public/js/celebrations/index.js").then(function(i){const t=i.CelebrationsEffect;let n=e.theme||"auto";if(n==="auto"&&(n=t.getCurrentTheme(),!n))return;const r=new t({theme:n,intensity:e.intensity||"medium",enabled:!0,zIndex:999});r.init().then(function(){r.start()})}).catch(function(){})},c.prototype.submitBlock=function(){const e=this._triggerState&&this._triggerState.blocks||[];return{blocked:e.length>0,reasons:e.map(function(i){return i.message})}},{evaluateCondition:b,evaluateConditionGroup:h,evaluateFieldVisibility:q,evaluateFieldRequirement:_,validateField:A,collectValues:L,validateFields:I,getCascadeOptions:S,resolveTriggerState:N,resolveTriggerTargets:y,hasTriggers:F,TRIGGER_ACTION_KINDS:T,SERVER_TRIGGER_ACTIONS:H,FormLogicRuntime:c}});
|
package/public/js/forms.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{buildWizardSteps as
|
|
2
|
-
`).replace(/<[^>]+>/g,""))}function N(n){return String(n).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}function w(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 _(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 a of i.removedNodes)if(a===n||a.nodeType===1&&a.contains&&a.contains(n)){n._formLogicRuntime&&n._formLogicRuntime.destroy(),t.disconnect();return}});t.observe(n.parentNode,{childList:!0,subtree:!1}),n._formLogicObserver=t}function W(n){const o=n&&n.logic;return!!(o&&(o.visibility||o.requirement))}function j(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)),a=e.type==="checkbox"?"boolean":e.type==="date"||e.type==="icon"||e.type==="image"?"string":i?"chooser":e.type,u={...e.formConfig||{}};u.span==="full"&&o&&(u.span=o);const r={type:a,label:e.label||e.name,required:W(e)?!1:e.required||!1,options:e.options,formConfig:{...e.placeholder&&{placeholder:e.placeholder},...e.helper&&{helperText:e.helper},...e.tooltip&&{tooltip:e.tooltip},...u}};e.minLength!==void 0&&(r.minLength=e.minLength),e.maxLength!==void 0&&(r.maxLength=e.maxLength),e.min!==void 0&&(r.min=e.min),e.max!==void 0&&(r.max=e.max),i&&(e.variant&&(r.variant=e.variant),(e.multiple||e.type==="checkbox-group")&&(r.multiple=!0),e.density&&(r.density=e.density),e.columns&&(r.columns=e.columns),e.accent&&(r.accent=e.accent),e.accentStyle&&(r.accentStyle=e.accentStyle),e.glow&&(r.glow=!0),e.glowColour&&(r.glowColour=e.glowColour),e.shadow&&(r.shadow=e.shadow),e.shadowColour&&(r.shadowColour=e.shadowColour)),t[e.name]=r}),t}function J(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 A(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 R="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 O(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 a=document.createElement("button");a.type="button",a.textContent="\xD7",a.setAttribute("aria-label","Close"),a.style.cssText=R+"font-size:1rem;padding:.15rem .55rem;",e.appendChild(i),e.appendChild(a),t.appendChild(e);const u=document.createElement("div");u.style.cssText="display:flex;flex-direction:column;gap:.6rem;overflow:hidden;min-height:0;",t.appendChild(u),o.appendChild(t);const r=l=>{l.key==="Escape"&&s()};function s(){document.removeEventListener("keydown",r),o.remove()}return a.addEventListener("click",s),o.addEventListener("click",l=>{l.target===o&&s()}),document.addEventListener("keydown",r),document.body.appendChild(o),{body:u,close:s}}function U(n){const{body:o,close:t}=O("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 a=window.I&&typeof I.list=="function"?I.list():[],u=a.map(r=>{const s=document.createElement("button");s.type="button",s.title=r,s.dataset.iconName=r,s.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 l=document.createElement("span");return l.setAttribute("data-icon",r),l.setAttribute("data-icon-size","18"),s.appendChild(l),s.addEventListener("mouseenter",()=>{s.style.borderColor="var(--dm-border)"}),s.addEventListener("mouseleave",()=>{s.style.borderColor="transparent"}),s.addEventListener("click",()=>{t(),n(r)}),i.appendChild(s),s});if(!a.length){const r=document.createElement("p");r.textContent="Icon engine unavailable - type an icon name into the field instead.",i.appendChild(r)}window.I?.scan?.(i),e.addEventListener("input",()=>{const r=e.value.trim().toLowerCase();u.forEach(s=>{s.style.display=!r||s.dataset.iconName.includes(r)?"":"none"})}),e.focus()}async function H(){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:a}=await i.json();S.set("auth_token",a),t=await o(a)}}}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 K(n){const{body:o,close:t}=O("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),H().then(({files:i,error:a})=>{if(a){e.textContent=a==="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 u=document.createElement("div");u.style.cssText="display:grid;grid-template-columns:repeat(auto-fill,minmax(104px,1fr));gap:8px;overflow-y:auto;min-height:0;",i.forEach(r=>{const s=document.createElement("button");s.type="button",s.title=r.name||r.url,s.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 l=document.createElement("img");l.src=r.url,l.alt=r.name||"",l.loading="lazy",l.style.cssText="width:100%;height:72px;object-fit:cover;border-radius:5px;";const c=document.createElement("span");c.textContent=r.name||r.url,c.style.cssText="font-size:.68em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;",s.appendChild(l),s.appendChild(c),s.addEventListener("click",()=>{t(),n(r.url)}),u.appendChild(s)}),o.appendChild(u)})}function B(n,o){n.value=o,n.dispatchEvent(new Event("input",{bubbles:!0})),n.dispatchEvent(new Event("change",{bubbles:!0}))}function z(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 P(n){const o=document.createElement("button");return o.type="button",o.textContent=n,o.style.cssText=R,o}function Y(n){if(!window.I||typeof I.list!="function")return;const o=z(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 a=n.value.trim();if(!a)return;const u=document.createElement("span");u.setAttribute("data-icon",a),u.setAttribute("data-icon-size","16"),t.appendChild(u),window.I?.scan?.(t)};n.addEventListener("input",e),e();const i=P("Browse");i.addEventListener("click",()=>U(a=>{B(n,a),e()})),o.appendChild(t),o.appendChild(i)}function $(n){const o=z(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 a=n.value.trim();t.style.display=a?"":"none",a&&(t.src=a)};n.addEventListener("input",e),e();const i=P("Browse");i.addEventListener("click",()=>K(a=>{B(n,a),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"?Y(i):$(i))})}function k(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 D(n,o){const t=n.querySelector("form");if(!t)return;const e=Array.from(t.querySelectorAll(".form-group"));let i=0;o.forEach(function(a){if(a.type==="spacer"){const u=document.createElement("div");u.className="fb-spacer";const r=e[i];if(r)t.insertBefore(u,r);else{const s=t.querySelector('[type="submit"]');s?t.insertBefore(u,s):t.appendChild(u)}}else a.type!=="page-break"&&i++})}function T(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 q(n,o,t,e,i,a){const u=i||e,r=u.querySelector(".fb-form-honeypot"),s=r?.querySelector('input[type="text"]')?.value||"",l=r?.querySelector('[name="_t"]')?.value||"",c=Object.assign({},o);delete c._t,r?.dataset.hpName&&delete c[r.dataset.hpName];const m=new Map(a||[]);T(u,m);const d=m.size>0;let b,g;if(d){const p=new FormData;for(const[y,v]of Object.entries(c))v!=null&&(m.has(y)||p.append(y,typeof v=="object"?JSON.stringify(v):String(v)));for(const[y,v]of m)for(const x of v)p.append(y,x,x.name);p.append("_hp",s),p.append("_t",l),b=p,g={}}else{const p=Object.assign({},c,{_hp:s,_t:l});b=JSON.stringify(p),g={"Content-Type":"application/json"}}return fetch("/api/forms/submit/"+n,{method:"POST",headers:g,body:b}).then(p=>p.json().then(y=>({ok:p.ok,body:y}))).then(p=>{p.ok&&p.body.ok?(e.textContent="",w(e,p.body.message||t.successMessage||"Thank you.","success")):w(e,p.body.error||"Something went wrong.","error")}).catch(()=>{w(e,"Unable to submit. Please check your connection.","error")})}function G(n,o,t,e,i){const a=document.createElement("form");a.noValidate=!0,o.forEach(function(r){const s=document.createElement("div");s.className="form-group",s.style.marginBottom="1.25rem";const l=document.createElement("label");if(l.className="form-label",l.textContent=r.label||r.name,r.required){const m=document.createElement("span");m.textContent=" *",m.style.color="var(--dm-danger)",l.appendChild(m)}let c;r.type==="textarea"?(c=document.createElement("textarea"),c.rows=r.formConfig?.rows||4,c.className="form-input"):r.type==="select"?(c=document.createElement("select"),c.className="form-input",(r.options||[]).forEach(function(m){const d=document.createElement("option");d.value=typeof m=="string"?m:m.value??"",d.textContent=typeof m=="string"?m:m.label||d.value,c.appendChild(d)})):(c=document.createElement("input"),c.type=r.type==="icon"||r.type==="image"?"text":r.type||"text",c.className="form-input",r.placeholder&&(c.placeholder=r.placeholder),r.type==="file"&&(r.file?.accept&&(c.accept=r.file.accept),r.multiple&&(c.multiple=!0))),c.name=r.name,c.required=r.required||!1,s.appendChild(l),s.appendChild(c),a.appendChild(s)}),t.honeypot&&k(a);const u=document.createElement("button");if(u.type="submit",u.className="btn btn-primary",u.textContent=t.submitText||"Submit",a.appendChild(u),a.addEventListener("submit",function(r){r.preventDefault();const s={};if(o.forEach(function(l){const c=a.querySelector('[name="'+l.name+'"]');c&&(s[l.name]=c.value)}),window.FormLogicEngine&&i){const l=window.FormLogicEngine,c=[],m=[];if(o.forEach(function(d){if(l.evaluateFieldVisibility(d,s)==="hidden"){delete s[d.name];return}const g=l.evaluateFieldRequirement(d,s),p=s[d.name];g&&(!p||!String(p).trim())&&c.push(d.label||d.name);const y=l.validateField(d,p||"",s);y.length&&m.push(y[0].message)}),c.length||m.length){const d=[];c.length&&d.push("Required: "+c.join(", ")),m.length&&d.push(m.join("; ")),w(n,d.join(". "),"error");return}}if(n.dataset.triggerBlocked==="true"){const l=n._formLogicRuntime,c=l&&l.submitBlock?l.submitBlock().reasons:[];w(n,c.join(" ")||"This form cannot be submitted yet.","error");return}n.classList.add("fb-form-loading"),u.disabled=!0,q(e,s,t,n,a).finally(function(){n.classList.remove("fb-form-loading"),u.disabled=!1})}),n.appendChild(a),enhancePickerFields(n,o),window.FormLogicEngine&&i&&L(o)){const r=new window.FormLogicEngine.FormLogicRuntime(i,n);r.init(),n._formLogicRuntime=r}}function Q(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||{},a=document.createElement("div");a.className="fb-form-wrapper",n.appendChild(a);const u=e.some(r=>r.type==="page-break");if(typeof Domma<"u"&&Domma.forms){const r=Number(i.columns)===3?3:2,s=i.layout||"grid";if(u&&Domma.forms.wizard){const l=M(e,t.title),c=l.map(f=>f.fields),m=l.map(function(f){return{title:f.title,description:f.description,fields:j(f.fields,r)}});let d=0,b=null,g=null;const p=Date.now(),y=new Map,v=function(f){const h=window.FormLogicEngine;if(!h||!h.validateFields)return;const C=h.validateFields(a,c[d]||[]);C.valid||(f.preventDefault(),f.stopImmediatePropagation(),markInvalidFields(a,C.missing),showValidationPopup(C))},x=function(){const f=c[d]||e;if(A(a,f),enhancePickerFields(a,f),f.some(h=>h.type==="spacer")&&D(a,f),i.honeypot){const h=a.querySelector("form");h&&k(h,p)}window.FormLogicEngine&&L(e)&&(b&&b.destroy(),b=new window.FormLogicEngine.FormLogicRuntime(t,a,{baseValues:g&&g.getStepData?g.getStepData():{}}),b.init(),_(a,b)),a.querySelectorAll(".wizard-next, .wizard-submit").forEach(h=>h.addEventListener("click",v,!0))};g=Domma.forms.wizard(a,{schema:{steps:m},layout:s,columns:r,finishText:i.submitText||"Finish",onStepChange:function(f){T(a,y),d=f,setTimeout(x,0)},onSubmit:function(f){return T(a,y),q(o,f,i,a,null,y)}}),a.addEventListener("dm-form-trigger",function(f){if(!f.detail||f.detail.type!=="goto-step"||!g||!g.goToStep)return;const h=Number(f.detail.action.step);!isFinite(h)||h<0||h>=m.length||h!==d&&g.goToStep(h)}),Promise.resolve(g).then(x)}else if(Domma.forms.render){const l=j(e,r),c=J(e),m=Domma.forms.render(a,l,c,{submitText:i.submitText||"Submit",layout:s,columns:r,onSubmit:function(d){return q(o,d,i,a,null)}});Promise.resolve(m).then(function(){if(A(a,e),enhancePickerFields(a,e),window.FormLogicEngine&&L(e)){const d=new window.FormLogicEngine.FormLogicRuntime(t,a);d.init(),_(a,d)}if(e.some(d=>d.type==="spacer")&&D(a,e),s==="grid"&&i.submitSpan==="full"){const d=a.querySelector(".form-buttons");d&&d.classList.add("col-span-full")}if(i.honeypot){const d=a.querySelector("form");d&&k(d)}})}}else G(a,e.filter(r=>r.type!=="page-break"&&r.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{buildWizardSteps as V}from"/public/js/form-steps.mjs";const N=document.querySelectorAll("[data-form]");N.length&&N.forEach(X);function T(n){return(n||[]).some(o=>o.logic||Array.isArray(o.triggers)&&o.triggers.length)}function W(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 r=t;for(;r.parentNode&&r.parentNode!==e;)r=r.parentNode;return r}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 r=W(n,e.name);r&&(r.classList.add("fb-field-invalid"),t||(t=r))}),t&&t.scrollIntoView&&t.scrollIntoView({behavior:"smooth",block:"center"})}export function showValidationPopup(n){const o=(n.missing||[]).map(e=>"<strong>"+_(e.label)+"</strong> is required.").concat((n.errors||[]).map(_)),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 _(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 r=e.detail&&e.detail.validation;if(r&&!r.valid){markInvalidFields(n,r.missing),showValidationPopup(r);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 r=document.createElement("div");r.className=t==="success"?"fb-form-success":"fb-form-error",r.textContent=o,n.appendChild(r)}function j(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 r of e)for(const a of r.removedNodes)if(a===n||a.nodeType===1&&a.contains&&a.contains(n)){n._formLogicRuntime&&n._formLogicRuntime.destroy(),t.disconnect();return}});t.observe(n.parentNode,{childList:!0,subtree:!1}),n._formLogicObserver=t}function J(n){const o=n&&n.logic;return!!(o&&(o.visibility||o.requirement))}function A(n,o){const t={};return n.forEach(function(e){if(e.type==="page-break"||e.type==="spacer"||!e.name)return;const r=e.type==="chooser"||!!(e.variant&&["select","radio","checkbox-group"].includes(e.type)),a=e.type==="checkbox"?"boolean":e.type==="date"||e.type==="icon"||e.type==="image"?"string":r?"chooser":e.type,u={...e.formConfig||{}};u.span==="full"&&o&&(u.span=o);const i={type:a,label:e.label||e.name,required:J(e)?!1:e.required||!1,options:e.options,formConfig:{...e.placeholder&&{placeholder:e.placeholder},...e.helper&&{helperText:e.helper},...e.tooltip&&{tooltip:e.tooltip},...u}};e.minLength!==void 0&&(i.minLength=e.minLength),e.maxLength!==void 0&&(i.maxLength=e.maxLength),e.min!==void 0&&(i.min=e.min),e.max!==void 0&&(i.max=e.max),r&&(e.variant&&(i.variant=e.variant),(e.multiple||e.type==="checkbox-group")&&(i.multiple=!0),e.density&&(i.density=e.density),e.columns&&(i.columns=e.columns),e.accent&&(i.accent=e.accent),e.accentStyle&&(i.accentStyle=e.accentStyle),e.glow&&(i.glow=!0),e.glowColour&&(i.glowColour=e.glowColour),e.shadow&&(i.shadow=e.shadow),e.shadowColour&&(i.shadowColour=e.shadowColour)),t[e.name]=i}),t}function U(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 R(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 O="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 B(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 r=document.createElement("strong");r.textContent=n;const a=document.createElement("button");a.type="button",a.textContent="\xD7",a.setAttribute("aria-label","Close"),a.style.cssText=O+"font-size:1rem;padding:.15rem .55rem;",e.appendChild(r),e.appendChild(a),t.appendChild(e);const u=document.createElement("div");u.style.cssText="display:flex;flex-direction:column;gap:.6rem;overflow:hidden;min-height:0;",t.appendChild(u),o.appendChild(t);const i=d=>{d.key==="Escape"&&s()};function s(){document.removeEventListener("keydown",i),o.remove()}return a.addEventListener("click",s),o.addEventListener("click",d=>{d.target===o&&s()}),document.addEventListener("keydown",i),document.body.appendChild(o),{body:u,close:s}}function H(n){const{body:o,close:t}=B("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 r=document.createElement("div");r.style.cssText="display:grid;grid-template-columns:repeat(auto-fill,minmax(46px,1fr));gap:4px;overflow-y:auto;min-height:0;",o.appendChild(r);const a=window.I&&typeof I.list=="function"?I.list():[],u=a.map(i=>{const s=document.createElement("button");s.type="button",s.title=i,s.dataset.iconName=i,s.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 d=document.createElement("span");return d.setAttribute("data-icon",i),d.setAttribute("data-icon-size","18"),s.appendChild(d),s.addEventListener("mouseenter",()=>{s.style.borderColor="var(--dm-border)"}),s.addEventListener("mouseleave",()=>{s.style.borderColor="transparent"}),s.addEventListener("click",()=>{t(),n(i)}),r.appendChild(s),s});if(!a.length){const i=document.createElement("p");i.textContent="Icon engine unavailable - type an icon name into the field instead.",r.appendChild(i)}window.I?.scan?.(r),e.addEventListener("input",()=>{const i=e.value.trim().toLowerCase();u.forEach(s=>{s.style.display=!i||s.dataset.iconName.includes(i)?"":"none"})}),e.focus()}async function K(){const n=typeof S<"u"&&S.get?S.get("auth_token"):null;if(!n)return{error:"auth"};const o=r=>fetch("/api/media",{headers:{Authorization:"Bearer "+r}});let t;try{if(t=await o(n),t.status===401&&S.get("auth_refresh_token")){const r=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:S.get("auth_refresh_token")})});if(r.ok){const{token:a}=await r.json();S.set("auth_token",a),t=await o(a)}}}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(r=>/\.(png|jpe?g|gif|webp|svg|avif)$/i.test(r.name||r.url||""))}}function Y(n){const{body:o,close:t}=B("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),K().then(({files:r,error:a})=>{if(a){e.textContent=a==="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(!r.length){e.textContent="No images in the media library yet.";return}e.remove();const u=document.createElement("div");u.style.cssText="display:grid;grid-template-columns:repeat(auto-fill,minmax(104px,1fr));gap:8px;overflow-y:auto;min-height:0;",r.forEach(i=>{const s=document.createElement("button");s.type="button",s.title=i.name||i.url,s.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 d=document.createElement("img");d.src=i.url,d.alt=i.name||"",d.loading="lazy",d.style.cssText="width:100%;height:72px;object-fit:cover;border-radius:5px;";const c=document.createElement("span");c.textContent=i.name||i.url,c.style.cssText="font-size:.68em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;",s.appendChild(d),s.appendChild(c),s.addEventListener("click",()=>{t(),n(i.url)}),u.appendChild(s)}),o.appendChild(u)})}function z(n,o){n.value=o,n.dispatchEvent(new Event("input",{bubbles:!0})),n.dispatchEvent(new Event("change",{bubbles:!0}))}function P(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 D(n){const o=document.createElement("button");return o.type="button",o.textContent=n,o.style.cssText=O,o}function $(n){if(!window.I||typeof I.list!="function")return;const o=P(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 a=n.value.trim();if(!a)return;const u=document.createElement("span");u.setAttribute("data-icon",a),u.setAttribute("data-icon-size","16"),t.appendChild(u),window.I?.scan?.(t)};n.addEventListener("input",e),e();const r=D("Browse");r.addEventListener("click",()=>H(a=>{z(n,a),e()})),o.appendChild(t),o.appendChild(r)}function G(n){const o=P(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 a=n.value.trim();t.style.display=a?"":"none",a&&(t.src=a)};n.addEventListener("input",e),e();const r=D("Browse");r.addEventListener("click",()=>Y(a=>{z(n,a),e()})),o.appendChild(t),o.appendChild(r)}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 r=t.querySelector('[name="'+e.name+'"]');!r||r.dataset.pickerEnhanced||(r.dataset.pickerEnhanced="1",e.type==="icon"?$(r):G(r))})}function F(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 r=document.createElement("input");r.name="_t",r.type="hidden",r.value=o||Date.now(),t.appendChild(r),n.appendChild(t)}function M(n,o){const t=n.querySelector("form");if(!t)return;const e=Array.from(t.querySelectorAll(".form-group"));let r=0;o.forEach(function(a){if(a.type==="spacer"){const u=document.createElement("div");u.className="fb-spacer";const i=e[r];if(i)t.insertBefore(u,i);else{const s=t.querySelector('[type="submit"]');s?t.insertBefore(u,s):t.appendChild(u)}}else a.type!=="page-break"&&r++})}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,r,a){const u=r||e,i=u.querySelector(".fb-form-honeypot"),s=i?.querySelector('input[type="text"]')?.value||"",d=i?.querySelector('[name="_t"]')?.value||"",c=Object.assign({},o);delete c._t,i?.dataset.hpName&&delete c[i.dataset.hpName];const m=new Map(a||[]);L(u,m);const l=m.size>0;let b,y;if(l){const f=new FormData;for(const[g,v]of Object.entries(c))v!=null&&(m.has(g)||f.append(g,typeof v=="object"?JSON.stringify(v):String(v)));for(const[g,v]of m)for(const w of v)f.append(g,w,w.name);f.append("_hp",s),f.append("_t",d),b=f,y={}}else{const f=Object.assign({},c,{_hp:s,_t:d});b=JSON.stringify(f),y={"Content-Type":"application/json"}}return fetch("/api/forms/submit/"+n,{method:"POST",headers:y,body:b}).then(f=>f.json().then(g=>({ok:f.ok,body:g}))).then(f=>{f.ok&&f.body.ok?(e.textContent="",C(e,f.body.message||t.successMessage||"Thank you.","success")):C(e,f.body.error||"Something went wrong.","error")}).catch(()=>{C(e,"Unable to submit. Please check your connection.","error")})}function Q(n,o,t,e,r){const a=document.createElement("form");a.noValidate=!0,o.forEach(function(i){const s=document.createElement("div");s.className="form-group",s.style.marginBottom="1.25rem";const d=document.createElement("label");if(d.className="form-label",d.textContent=i.label||i.name,i.required){const m=document.createElement("span");m.textContent=" *",m.style.color="var(--dm-danger)",d.appendChild(m)}let c;i.type==="textarea"?(c=document.createElement("textarea"),c.rows=i.formConfig?.rows||4,c.className="form-input"):i.type==="select"?(c=document.createElement("select"),c.className="form-input",(i.options||[]).forEach(function(m){const l=document.createElement("option");l.value=typeof m=="string"?m:m.value??"",l.textContent=typeof m=="string"?m:m.label||l.value,c.appendChild(l)})):(c=document.createElement("input"),c.type=i.type==="icon"||i.type==="image"?"text":i.type||"text",c.className="form-input",i.placeholder&&(c.placeholder=i.placeholder),i.type==="file"&&(i.file?.accept&&(c.accept=i.file.accept),i.multiple&&(c.multiple=!0))),c.name=i.name,c.required=i.required||!1,s.appendChild(d),s.appendChild(c),a.appendChild(s)}),t.honeypot&&F(a);const u=document.createElement("button");if(u.type="submit",u.className="btn btn-primary",u.textContent=t.submitText||"Submit",a.appendChild(u),a.addEventListener("submit",function(i){i.preventDefault();const s={};if(o.forEach(function(d){const c=a.querySelector('[name="'+d.name+'"]');c&&(s[d.name]=c.value)}),window.FormLogicEngine&&r){const d=window.FormLogicEngine,c=[],m=[];if(o.forEach(function(l){if(d.evaluateFieldVisibility(l,s)==="hidden"){delete s[l.name];return}const y=d.evaluateFieldRequirement(l,s),f=s[l.name];y&&(!f||!String(f).trim())&&c.push(l.label||l.name);const g=d.validateField(l,f||"",s);g.length&&m.push(g[0].message)}),c.length||m.length){const l=[];c.length&&l.push("Required: "+c.join(", ")),m.length&&l.push(m.join("; ")),C(n,l.join(". "),"error");return}}if(n.dataset.triggerBlocked==="true"){const d=n._formLogicRuntime,c=d&&d.submitBlock?d.submitBlock().reasons:[];C(n,c.join(" ")||"This form cannot be submitted yet.","error");return}n.classList.add("fb-form-loading"),u.disabled=!0,x(e,s,t,n,a).finally(function(){n.classList.remove("fb-form-loading"),u.disabled=!1})}),n.appendChild(a),enhancePickerFields(n,o),q(n,function(i){return x(e,i,t,n,a)}),window.FormLogicEngine&&r&&T(o)){const i=new window.FormLogicEngine.FormLogicRuntime(r,n);i.init(),n._formLogicRuntime=i}}function X(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||[],r=t.settings||{},a=document.createElement("div");a.className="fb-form-wrapper",n.appendChild(a);const u=e.some(i=>i.type==="page-break");if(typeof Domma<"u"&&Domma.forms){const i=Number(r.columns)===3?3:2,s=r.layout||"grid";if(u&&Domma.forms.wizard){const d=V(e,t.title),c=d.map(p=>p.fields),m=d.map(function(p){return{title:p.title,description:p.description,fields:A(p.fields,i)}});let l=0,b=null,y=null;const f=Date.now(),g=new Map,v=function(p){const h=window.FormLogicEngine;if(!h||!h.validateFields)return;const k=h.validateFields(a,c[l]||[]);k.valid||(p.preventDefault(),p.stopImmediatePropagation(),markInvalidFields(a,k.missing),showValidationPopup(k))},w=function(){const p=c[l]||e;if(R(a,p),enhancePickerFields(a,p),p.some(h=>h.type==="spacer")&&M(a,p),r.honeypot){const h=a.querySelector("form");h&&F(h,f)}window.FormLogicEngine&&T(e)&&(b&&b.destroy(),b=new window.FormLogicEngine.FormLogicRuntime(t,a,{baseValues:y&&y.getStepData?y.getStepData():{}}),b.init(),j(a,b)),a.querySelectorAll(".wizard-next, .wizard-submit").forEach(h=>h.addEventListener("click",v,!0))};y=Domma.forms.wizard(a,{schema:{steps:m},layout:s,columns:i,finishText:r.submitText||"Finish",onStepChange:function(p){L(a,g),l=p,setTimeout(w,0)},onSubmit:function(p){return L(a,g),x(o,p,r,a,null,g)}}),q(a,function(p){return L(a,g),x(o,p,r,a,null,g)}),a.addEventListener("dm-form-trigger",function(p){if(!p.detail||p.detail.type!=="goto-step"||!y||!y.goToStep)return;const h=Number(p.detail.action.step);!isFinite(h)||h<0||h>=m.length||h!==l&&y.goToStep(h)}),Promise.resolve(y).then(w)}else if(Domma.forms.render){const d=A(e,i),c=U(e);q(a,function(l){return x(o,l,r,a,null)});const m=Domma.forms.render(a,d,c,{submitText:r.submitText||"Submit",layout:s,columns:i,onSubmit:function(l){return x(o,l,r,a,null)}});Promise.resolve(m).then(function(){if(R(a,e),enhancePickerFields(a,e),window.FormLogicEngine&&T(e)){const l=new window.FormLogicEngine.FormLogicRuntime(t,a);l.init(),j(a,l)}if(e.some(l=>l.type==="spacer")&&M(a,e),s==="grid"&&r.submitSpan==="full"){const l=a.querySelector(".form-buttons");l&&l.classList.add("col-span-full")}if(r.honeypot){const l=a.querySelector("form");l&&F(l)}})}}else Q(a,e.filter(i=>i.type!=="page-break"&&i.type!=="spacer"),r,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)})}
|
package/public/js/site.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{makeBadgeEl as N,applyColour as M,applyPill as B,applyFloat as R,applyHighlight as z,applyItemHighlight as P,itemHighlightAttrs as V,isCurrentItem as O,menuAlign as J}from"/public/js/menu-decor.mjs?v=20260918-item-highlight";import{initFloatingMenus as W}from"/public/js/menu-float.js?v=20260829-float-panel";import{enhancePickerFields as j,markInvalidFields as G,showValidationPopup as X}from"/public/js/forms.js?v=20260821-wizard-grid";import{buildWizardSteps as Y}from"/public/js/form-steps.mjs";$(()=>{const b=window.__CMS_NAV__||{},g=window.__CMS_SITE__||{};if(g.autoTheme?.enabled){let t=function(e){const c=(e||"07:00").split(":");return+c[0]*60+(+c[1]||0)},o=function(){const e=new Date,c=e.getHours()*60+e.getMinutes();return c>=t(a.dayStart)&&c<t(a.nightStart)?a.dayTheme:a.nightTheme},n=function(e){const c=a.apply&&a.apply[e]||{theme:e,custom:""};Domma.theme.set(c.theme);for(const p of[...document.body.classList])p.startsWith("dm-custom-")&&document.body.classList.remove(p);c.custom&&document.body.classList.add(c.custom)};const a=g.autoTheme;n(o()),setInterval(()=>n(o()),6e4)}const k=$("#site-navbar");if(k.length&&b.brand){let o=function(i,d=0){return Array.isArray(i)?i.filter(s=>!(s&&s.type==="spacer")).filter(s=>!(s&&s.type==="separator"&&d===0)).map(s=>s&&s.type==="separator"?{divider:!0}:Array.isArray(s?.items)?{...s,items:o(s.items,d+1)}:s):i},n=function(i){return Array.isArray(i)?i.filter(d=>!(d&&(d.type==="separator"||d.type==="spacer"))):[]};const a={...b.brand},t=a.size&&a.size!=="md"?` navbar-brand-${a.size}`:"";if(a.logo||a.icon||a.tagline){let i="";a.logo?i+=`<img src="${a.logo}" class="navbar-brand-logo" alt="${a.text||""}">`:a.icon&&(i+=`<span data-icon="${a.icon}" style="width:1.1em;height:1.1em;margin-right:.35em;vertical-align:middle;"></span>`),a.text&&(i+=`<span class="navbar-brand-text${t}">${a.text}</span>`),a.tagline&&(i+=`<small class="navbar-brand-tagline">${a.tagline}</small>`),a.html=i}else t&&a.text&&(a.html=`<span class="navbar-brand-text${t}">${a.text}</span>`);const e=b.position==="floating",c=b.orientation==="vertical";Domma.elements.navbar("#site-navbar",{brand:a,items:o(b.items||[]),variant:b.variant||"dark",position:e?"static":b.position||"sticky",collapsible:!0,appearOnHover:!!b.appearOnHover}),Domma.icons.scan("#site-navbar");const p=k.get(0);p.classList.add(`dm-nav-align-${J(b)||"centre"}`),c&&k.get(0).classList.add("dm-nav-vertical",b.side==="right"?"dm-nav-vertical-right":"dm-nav-vertical-left"),z(p,b),e&&(R(k.get(0),b),W(k.get(0).parentNode||document));const y=p.querySelector(".navbar-toggle"),h=p.querySelector(".navbar-collapse");if(y&&h){let i=null;const d=()=>{h.classList.contains("show")&&y.click()},s=()=>{i&&(i.remove(),i=null),document.body.classList.remove("dm-drawer-open")},r=()=>{i||(i=document.createElement("div"),i.className="dm-navbar-backdrop",i.addEventListener("click",d),document.body.appendChild(i),document.body.classList.add("dm-drawer-open"))};y.addEventListener("click",()=>{requestAnimationFrame(()=>{h.classList.contains("show")?r():s()})}),h.addEventListener("click",l=>{l.target.closest("a")&&requestAnimationFrame(s)}),document.addEventListener("keydown",l=>{l.key==="Escape"&&h.classList.contains("show")&&d()})}const u=p.querySelector(".navbar-nav");if(u){const i=Array.from(u.children);let d=0;for(const s of b.items||[])if(!(s&&s.type==="separator")){if(s&&s.type==="spacer"){const r=document.createElement("li"),l=["xs","sm","md","lg","xl","flex"].includes(s.size)?s.size:"md";r.className=`dm-menu-spacer dm-menu-spacer--${l}`,r.setAttribute("aria-hidden","true"),u.insertBefore(r,i[d]||null);continue}d++}}const w=n(b.items||[]);p.querySelectorAll(".navbar-link[data-index], .navbar-dropdown-item[data-index]").forEach(i=>{const d=w[parseInt(i.dataset.index,10)];if(!d)return;const s=i.dataset.subindex!=null,r=s&&Array.isArray(d.items)?d.items[parseInt(i.dataset.subindex,10)]:d;if(!r)return;r.colour&&M(i,r.colour),r.pill&&!s&&B(i,r.pill),P(i,r);const l=N(r.badge);l&&i.appendChild(l),O(r.url,location.pathname)&&(i.classList.add("active"),i.setAttribute("aria-current","page"))})}const C=$("#site-footer");if(C.length){let e=function(r){return String(r??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")},c=function(r){return e(r).replace(/'/g,"'")},p=function(r){if(r&&r.type==="separator")return'<li class="footer-sep"><hr></li>';if(r&&r.type==="spacer")return`<li class="dm-menu-spacer dm-menu-spacer--${["xs","sm","md","lg","xl","flex"].includes(r.size)?r.size:"md"}" aria-hidden="true"></li>`;const l=c(r.url||"#"),m=e(r.text||""),v=Array.isArray(r.items)&&r.items.length?`<ul>${r.items.map(p).join("")}</ul>`:"",L=V(r),I=[L.hover&&` data-menu-item-hover="${c(L.hover)}"`,L.active&&` data-menu-item-active="${c(L.active)}"`].filter(Boolean).join(""),x=L.accent?` style="--dm-menu-highlight:${c(L.accent)}"`:"";return`<li><a href="${l}"${I}${x}>${m}</a>${v}</li>`},y=function(r,l){return(r||[]).forEach(m=>{m&&(m.type==="separator"||m.type==="spacer")||(l.push(m),Array.isArray(m.items)&&m.items.length&&y(m.items,l))}),l};const a=g.social||{},t={twitter:{label:"X / Twitter",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-4.714-6.231-5.401 6.231H2.742l7.73-8.835L1.254 2.25H8.08l4.259 5.629L18.244 2.25zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>'},facebook:{label:"Facebook",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>'},instagram:{label:"Instagram",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zM12 0C8.741 0 8.333.014 7.053.072 2.695.272.273 2.69.073 7.052.014 8.333 0 8.741 0 12c0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98C8.333 23.986 8.741 24 12 24c3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98C15.668.014 15.259 0 12 0zm0 5.838a6.162 6.162 0 100 12.324 6.162 6.162 0 000-12.324zM12 16a4 4 0 110-8 4 4 0 010 8zm6.406-11.845a1.44 1.44 0 100 2.881 1.44 1.44 0 000-2.881z"/></svg>'},linkedin:{label:"LinkedIn",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg>'},github:{label:"GitHub",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>'},youtube:{label:"YouTube",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M23.495 6.205a3.007 3.007 0 00-2.088-2.088c-1.87-.501-9.396-.501-9.396-.501s-7.507-.01-9.396.501A3.007 3.007 0 00.527 6.205a31.247 31.247 0 00-.522 5.805 31.247 31.247 0 00.522 5.783 3.007 3.007 0 002.088 2.088c1.868.502 9.396.502 9.396.502s7.506 0 9.396-.502a3.007 3.007 0 002.088-2.088 31.247 31.247 0 00.5-5.783 31.247 31.247 0 00-.5-5.805zM9.609 15.601V8.408l6.264 3.602z"/></svg>'}};let o='<div class="footer-inner container">';const n=window.__CMS_FOOTER__||{primary:[],legal:[],copyright:""};if(n.copyright&&(o+=`<p>${e(n.copyright)}</p>`),n.primary.length&&(o+=`<nav class="footer-nav footer-nav--primary"><ul>${n.primary.map(p).join("")}</ul></nav>`),n.legal.length&&(o+=`<nav class="footer-nav footer-nav--legal"><ul>${n.legal.map(p).join("")}</ul></nav>`),g.footer){const r=Object.keys(t).filter(l=>a[l]);r.length&&(o+='<div class="footer-social">',r.forEach(l=>{const{label:m,svg:v}=t[l];o+=`<a href="${a[l]}" target="_blank" rel="noopener noreferrer" aria-label="${m}" class="footer-social-link">${v}</a>`}),o+="</div>")}o+="</div>",C.html(o);const h=[...y(n.primary,[]),...y(n.legal,[])];C.get(0).querySelectorAll(".footer-nav a").forEach((r,l)=>{const m=h[l];if(!m)return;m.colour&&M(r,m.colour);const v=N(m.badge);v&&r.appendChild(v),O(m.url,location.pathname)&&(r.classList.add("is-active"),r.setAttribute("aria-current","page"))}),z(C.get(0).querySelector(".footer-nav--primary"),{highlight:n.primaryHighlight}),z(C.get(0).querySelector(".footer-nav--legal"),{highlight:n.legalHighlight});const w=g.footer?.showMotionToggle!==!1,i=S.get("reduced_motion"),d=i!==null?!!i:!!(window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches),s=C.get(0).querySelector(".footer-inner");if(w&&s){const r=document.createElement("input");r.type="checkbox",r.className="form-switch-input",r.id="dm-motion-switch",r.checked=d,r.addEventListener("change",function(){S.set("reduced_motion",this.checked),window.location.reload()});const l=document.createElement("span");l.className="form-switch-label",l.textContent="Reduce motion";const m=document.createElement("label");m.className="form-switch footer-motion-switch",m.title="Reduce motion",m.appendChild(r),m.appendChild(l),s.appendChild(m)}}$("#site-sidebar").length&&Domma.elements.sidebar("#site-sidebar",{autoGenerate:!0,selector:"h2, h3",collapsible:!1,push:!0,contentSelector:".site-content"}),Domma.icons.scan(),Domma.syntax?.scan&&Domma.syntax.scan();const f=$(".page-body");if(f.length){f.find(".accordion").each(function(){Domma.elements.accordion(this,{allowMultiple:this.dataset.multi==="true"})}),f.find(".tabs").each(function(){Domma.elements.tabs(this)}),f.find(".carousel").each(function(){const a={autoplay:this.dataset.autoplay==="true",interval:parseInt(this.dataset.interval,10)||5e3,loop:this.dataset.loop!=="false",animation:this.dataset.animation||"slide"};if(this.dataset.animationDuration){const t=parseInt(this.dataset.animationDuration,10);Number.isFinite(t)&&(a.animationDuration=t)}this.dataset.animationEasing&&(a.animationEasing=this.dataset.animationEasing),this.dataset.ctx&&(this._dmCtxSlides=Array.from(this.querySelectorAll(".carousel-slide")).map(t=>t.cloneNode(!0)),this._dmCarouselOpts=a),this._dmCarousel=Domma.elements.carousel(this,a)}),f.find(".dm-countdown").each(function(){const a={autoStart:!0};this.dataset.to&&(a.targetDate=new Date(this.dataset.to)),this.dataset.duration&&(a.duration=parseInt(this.dataset.duration,10)),this.dataset.format&&(a.format=this.dataset.format),Domma.elements.timer(this,a)}),f.find("[data-tooltip]").each(function(){Domma.elements.tooltip(this,{content:$(this).data("tooltip"),position:$(this).data("tooltip-position")||"top"})}),f.find(".dm-progression").each(function(){const a=Array.from(this.querySelectorAll(".dm-progression-item")).map(n=>{const e={title:n.querySelector(".dm-progression-item-title")?.textContent?.trim()||"",description:n.querySelector(".dm-progression-item-body")?.innerHTML?.trim()||""};return n.dataset.date&&(e.date=n.dataset.date),n.dataset.status&&(e.status=n.dataset.status),n.dataset.entryId&&(e._entryId=n.dataset.entryId),e}),t=this.dataset.layout==="centred"?"centered":this.dataset.layout||"vertical",o=Domma.elements.progression(this,{layout:t,theme:this.dataset.theme||"minimal",mode:this.dataset.mode||"timeline",statusIcons:!0,allowHtml:!0,...a.length?{items:a}:{}});a.length&&(this._dmCtx={instance:o,items:a})});try{Domma.effects.reveal(".page-body .hero",{animation:"slide-up",duration:480,threshold:.06,stagger:60,once:!1})}catch{}document.querySelectorAll(".page-body .row[data-reveal]").forEach(a=>{const t=a.dataset.revealMode||"stagger",o=a.dataset.revealAnimation||"slide-up",n=parseInt(a.dataset.revealDuration,10)||400,e=parseInt(a.dataset.revealStagger,10)||60,c=parseInt(a.dataset.revealDelay,10)||0,p=a.dataset.revealDirection==="rtl",y=Array.from(a.children),h={"slide-up":"translateY(30px)","slide-down":"translateY(-30px)","slide-left":"translateX(30px)","slide-right":"translateX(-30px)",zoom:"scale(0.85)",flip:"perspective(600px) rotateX(15deg)"};y.forEach((u,w)=>{u.style.opacity="0",u.style.transform=h[o]||"",u.style.transition=`opacity ${n}ms ease, transform ${n}ms ease`;const i=p?y.length-1-w:w;u.style.transitionDelay=t==="stagger"?`${c+i*e}ms`:`${c}ms`}),requestAnimationFrame(()=>requestAnimationFrame(()=>{const u=new IntersectionObserver(w=>{w.forEach(i=>{i.isIntersecting&&(i.target.offsetWidth,i.target.style.opacity="1",i.target.style.transform="none",u.unobserve(i.target))})},{threshold:.1});y.forEach(w=>u.observe(w))}))}),f.find(".card[data-collapsible]").each(function(){const a=this.querySelector(".card-header");a&&a.addEventListener("click",()=>this.classList.toggle("is-collapsed"))}),f.find(".dm-so-trigger").each(function(){this.addEventListener("click",()=>{const a=this.dataset.soTarget,t=document.getElementById(a);if(!t)return;const o=E.slideover({title:t.dataset.soTitle||"",size:t.dataset.soSize||"md",position:t.dataset.soPosition||"right"});t.style.display="",o.setContent(t),o.open()})})}if(typeof $.setup=="function"){const a=Object.assign({},window.__CMS_DCONFIG__||{});if(document.querySelectorAll(".dm-page-config[data-config]").forEach(t=>{try{const o=atob(t.dataset.config),n=JSON.parse(o);Object.assign(a,n)}catch{}}),Object.keys(a).length>0){const t={};for(const[o,n]of Object.entries(a)){const e=n?.events?.click,{confirm:c,toast:p,alert:y,prompt:h,...u}=e||{};c||p||y||h?($(o).on("click",async function(i){if(i.preventDefault(),c&&!await E.confirm(c))return;let d=null;if(!(h&&(d=await E.prompt(h,{inputPlaceholder:u.promptPlaceholder||"",inputValue:u.promptDefault||""}),d===null))){if(u.target){const s=$(u.target);u.toggleClass&&s.toggleClass(u.toggleClass),u.addClass&&s.addClass(u.addClass),u.removeClass&&s.removeClass(u.removeClass),d!==null&&(u.setText&&s.text(d),u.setVal&&s.val(d),u.setAttr&&s.attr(u.setAttr,d))}u.href&&(window.location.href=u.href),p&&E.toast(p,{type:u.toastType||"success"}),y&&E.alert(y)}}),Object.keys(u).length&&(t[o]={...n,events:{...n.events,click:u}})):t[o]=n}$.setup(t)}}f.length&&D(f.get(0))});function D(b){b.querySelectorAll(".dm-cta-trigger").forEach(g=>{g.__ctaWired||(g.__ctaWired=!0,g.addEventListener("click",async()=>{const k=g.dataset.action,C=g.dataset.entry,_=g.dataset.confirm;let f=S.get("auth_token");if(!f){E.toast("Please log in to perform this action.",{type:"warning"});return}if(_&&!await E.confirm(_))return;const a=Array.from(g.childNodes).map(o=>o.cloneNode(!0));g.disabled=!0,g.textContent="Running\u2026";const t=o=>fetch(`/api/actions/${k}/public`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${o}`},body:JSON.stringify({entryId:C})});try{let o=await t(f);if(o.status===401){const e=S.get("auth_refresh_token");if(e){const c=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:e})});if(c.ok){const{token:p}=await c.json();S.set("auth_token",p),f=p,o=await t(f)}}}const n=await o.json().catch(()=>({}));if(!o.ok)throw new Error(n.error||n.message||`Error ${o.status}`);E.toast(n.message||"Action completed.",{type:"success"})}catch(o){E.toast(o.message||"Action failed.",{type:"error"})}finally{g.disabled=!1,g.textContent="",a.forEach(o=>g.appendChild(o)),Domma.icons.scan(g)}}))})}(function(){const g=document.querySelectorAll('.dm-collection-hydrate[data-collection-scope="mine"]');if(!g.length)return;const k=S.get("auth_token");function C(f,a){const t=document.createElement("div");t.className="dm-collection-display dm-collection-empty";const o=document.createElement("p");if(a){o.appendChild(document.createTextNode("Please "));const n=document.createElement("a");n.href="/login",n.textContent="sign in",o.appendChild(n),o.appendChild(document.createTextNode(" to view your entries."))}else o.textContent=f;return t.appendChild(o),t}function _(f,a){const t=document.createElement("template");t.innerHTML=a||"",f.replaceWith(t.content)}g.forEach(async f=>{const a=f.dataset.collectionAttrs;if(!a)return;if(!k){f.replaceChildren(C("",!0));return}const t=o=>fetch("/api/collections/render-scope",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${o}`},body:JSON.stringify({attrs:a})});try{let o=k,n=await t(o);if(n.status===401){const c=S.get("auth_refresh_token");if(c){const p=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:c})});if(p.ok){const{token:y}=await p.json();S.set("auth_token",y),o=y,n=await t(o)}}}if(!n.ok){const{error:c}=await n.json().catch(()=>({}));f.replaceChildren(C(c||"Unable to load entries.",!1));return}const{html:e}=await n.json();_(f,e)}catch{f.replaceChildren(C("Unable to load entries.",!1))}}),setTimeout(()=>{typeof Domma<"u"&&Domma.icons?.scan&&Domma.icons.scan(),typeof Domma<"u"&&Domma.syntax?.scan&&Domma.syntax.scan(),D(document.body)},100)})(),(function(){const g=document.querySelectorAll("[data-collection-table]");!g.length||typeof T>"u"||!T.create||g.forEach(k=>{let C;try{C=JSON.parse(atob(k.dataset.payload))}catch{return}const{columns:_,rows:f,search:a,sortable:t,exportable:o,pageSize:n,empty:e,ctaConfig:c,slug:p,exportCfg:y}=C;if(!_?.length)return;if(c){const w=_.findIndex(i=>i.key==="_cta");w!==-1&&(_[w]={key:"_cta",title:"",render:(i,d)=>{const s=document.createElement("button");if(s.className=`btn btn-${c.style||"primary"} dm-cta-trigger`,s.dataset.action=c.action||"",s.dataset.entry=d._entryId||"",c.confirm&&(s.dataset.confirm=c.confirm),c.icon){const r=document.createElement("span");r.dataset.icon=c.icon,s.appendChild(r),s.appendChild(document.createTextNode(" "))}return s.appendChild(document.createTextNode(c.label||"Run")),s}})}const h="col-table-"+Math.random().toString(36).slice(2,7),u=document.createElement("div");u.id=h,k.replaceChildren(u),T.create("#"+h,{data:f,columns:_,search:a,sortable:t,exportable:o,pageSize:n,emptyMessage:e}),c&&D(u),k._dmTable={columns:_,rows:f,slug:p,exportCfg:y,render(w){T.create("#"+h,{data:w,columns:_,search:a,sortable:t,exportable:o,pageSize:n,emptyMessage:e}),c&&D(u)}}})})(),(function(){const g=document.querySelectorAll("[data-form-inline]");if(!g.length||typeof F>"u")return;function k(t){const o=t&&t.logic;return!!(o&&(o.visibility||o.requirement))}function C(t,o){const n={};return(t||[]).forEach(e=>{if(e.type==="page-break"||e.type==="spacer"||!e.name)return;const c=e.type==="chooser"||!!(e.variant&&["select","radio","checkbox-group"].includes(e.type)),p=e.type==="checkbox"?"boolean":e.type==="date"||e.type==="icon"||e.type==="image"?"string":c?"chooser":e.type,y={...e.formConfig||{}};y.span==="full"&&o&&(y.span=o);const h={type:p,required:k(e)?!1:e.required,label:e.label,options:e.options,formConfig:{...e.placeholder&&{placeholder:e.placeholder},...e.helper&&{hint:e.helper},...y}};c&&(e.variant&&(h.variant=e.variant),(e.multiple||e.type==="checkbox-group")&&(h.multiple=!0),e.density&&(h.density=e.density),e.columns&&(h.columns=e.columns),e.accent&&(h.accent=e.accent),e.accentStyle&&(h.accentStyle=e.accentStyle),e.glow&&(h.glow=!0),e.glowColour&&(h.glowColour=e.glowColour),e.shadow&&(h.shadow=e.shadow),e.shadowColour&&(h.shadowColour=e.shadowColour)),n[e.name]=h}),n}function _(t){const o={};return(t||[]).forEach(n=>{if(!(!n.name||n.type==="page-break"||n.type==="spacer")&&(n.type==="select"||n.type==="multiselect")&&n.required){const e=(n.options||[])[0];e&&(o[n.name]=typeof e=="object"?e.value:e)}}),o}function f(t,o){(o||[]).forEach(n=>{if(n.type!=="date"||!n.name)return;const e=t.querySelector(`[name="${n.name}"]`);e&&e.type!=="date"&&(e.type="date")})}function a(t,o,n){let e=t.querySelector(".cms-form-message");e||(e=document.createElement("p"),e.className="cms-form-message",t.appendChild(e)),e.textContent=o,e.style.cssText=n?"color:var(--dm-danger);margin-top:.75rem;":"color:var(--dm-success);margin-top:.75rem;"}g.forEach(t=>{let o;try{o=JSON.parse(atob(t.dataset.formInline))}catch{return}const n=o.fields||[],e=o.settings||{},c=Number(e.columns)===3?3:2,p=e.layout||"grid",y=n.some(d=>d.type==="page-break"),h=async d=>{if(t.dataset.triggerBlocked==="true"){const s=t._formLogicRuntime,r=s&&s.submitBlock?s.submitBlock().reasons:[];a(t,r.join(" ")||"This form cannot be submitted yet.",!0);const l=new Error("Blocked by a form trigger");throw l.formSubmitFailed=!0,l}try{const s=t.querySelector(".fb-form-honeypot"),r=s?.querySelector('input[type="text"]')||null,l=s?.querySelector('[name="_t"]')||null,m=Object.assign({},d);delete m._t,s?.dataset.hpName&&delete m[s.dataset.hpName],r!==null&&(m._hp=r.value),l!==null&&(m._t=l.value);const v=await H.post(`/api/forms/submit/${o.slug}`,m);if(v&&v.error){a(t,v.error,!0);const L=new Error(v.error);throw L.formSubmitFailed=!0,L}if(v?.redirect){window.location.href=v.redirect;return}for(;t.firstChild;)t.removeChild(t.firstChild);a(t,v?.message||e.successMessage||"Thank you for your submission.",!1)}catch(s){throw s.formSubmitFailed||a(t,s.message||"Submission failed. Please try again.",!0),s}},u=Date.now();function w(d){const s=d.querySelector("form");if(!s||s.querySelector(".fb-form-honeypot"))return;const r=document.createElement("div");r.className="fb-form-honeypot",r.setAttribute("aria-hidden","true");const l=document.createElement("input");l.name=s.querySelector('[name="website"]')?"_website":"website",r.dataset.hpName=l.name,l.type="text",l.tabIndex=-1,l.autocomplete="url",l.placeholder="https://",r.appendChild(l);const m=document.createElement("input");m.name="_t",m.type="hidden",m.value=u,r.appendChild(m),s.appendChild(r)}function i(d){if(!window.FormLogicEngine||!n.some(r=>r.logic||Array.isArray(r.triggers)&&r.triggers.length))return null;const s=new window.FormLogicEngine.FormLogicRuntime(o,t,d?{baseValues:d}:void 0);if(s.init(),t._formLogicRuntime=s,t._formLogicObserved)return s;if(t._formLogicObserved=!0,t.parentNode&&typeof MutationObserver<"u"){const r=new MutationObserver(function(l){for(const m of l)for(const v of m.removedNodes)if(v===t||v.nodeType===1&&v.contains&&v.contains(t)){t._formLogicRuntime&&t._formLogicRuntime.destroy(),r.disconnect();return}});r.observe(t.parentNode,{childList:!0,subtree:!1}),t._formLogicObserver=r}return s}if(y&&F.wizard){const d=Y(n,o.title),s=d.map(x=>({title:x.title,description:x.description,fields:C(x.fields,c)})),r=d.map(x=>x.fields);let l=0,m=null,v=null;const L=x=>{const A=window.FormLogicEngine;if(!A||!A.validateFields)return;const q=A.validateFields(t,r[l]||[]);q.valid||(x.preventDefault(),x.stopImmediatePropagation(),G(t,q.missing),X(q))},I=()=>{const x=r[l]||n;f(t,x),j(t,x),e.honeypot!==!1&&w(t),m&&m.destroy(),m=i(v?.getStepData?v.getStepData():void 0),t.querySelectorAll(".wizard-next, .wizard-submit").forEach(A=>A.addEventListener("click",L,!0))};v=F.wizard(t,{schema:{steps:s},layout:p,columns:c,finishText:e.submitText||"Finish",onSubmit:h,onStepChange:x=>{l=x,setTimeout(I,0)}}),t.addEventListener("dm-form-trigger",x=>{if(x.detail?.type!=="goto-step"||!v?.goToStep)return;const A=Number(x.detail.action.step);!Number.isFinite(A)||A<0||A>=s.length||A!==l&&v.goToStep(A)}),Promise.resolve(v).then(I)}else if(F.render){const d=F.render(t,C(n,c),_(n),{submitText:e.submitText||"Submit",layout:p,columns:c,onSubmit:h});Promise.resolve(d).then(function(){if(p==="grid"&&e.submitSpan==="full"){const s=t.querySelector(".form-buttons");s&&s.classList.add("col-span-full")}f(t,n),j(t,n),e.honeypot!==!1&&w(t),i()})}}),$(document).on("click",".dm-banner__dismiss",function(){$(this).closest(".dm-banner").remove()})})();
|
|
1
|
+
import{makeBadgeEl as M,applyColour as O,applyPill as V,applyFloat as J,applyHighlight as N,applyItemHighlight as W,itemHighlightAttrs as G,isCurrentItem as j,menuAlign as X}from"/public/js/menu-decor.mjs?v=20260918-item-highlight";import{initFloatingMenus as Y}from"/public/js/menu-float.js?v=20260829-float-panel";import{enhancePickerFields as B,markInvalidFields as R,showValidationPopup as P}from"/public/js/forms.js?v=20260926-form-end";import{buildWizardSteps as U}from"/public/js/form-steps.mjs";$(()=>{const w=window.__CMS_NAV__||{},g=window.__CMS_SITE__||{};if(g.autoTheme?.enabled){let e=function(t){const c=(t||"07:00").split(":");return+c[0]*60+(+c[1]||0)},n=function(){const t=new Date,c=t.getHours()*60+t.getMinutes();return c>=e(o.dayStart)&&c<e(o.nightStart)?o.dayTheme:o.nightTheme},r=function(t){const c=o.apply&&o.apply[t]||{theme:t,custom:""};Domma.theme.set(c.theme);for(const u of[...document.body.classList])u.startsWith("dm-custom-")&&document.body.classList.remove(u);c.custom&&document.body.classList.add(c.custom)};const o=g.autoTheme;r(n()),setInterval(()=>r(n()),6e4)}const _=$("#site-navbar");if(_.length&&w.brand){let n=function(i,h=0){return Array.isArray(i)?i.filter(s=>!(s&&s.type==="spacer")).filter(s=>!(s&&s.type==="separator"&&h===0)).map(s=>s&&s.type==="separator"?{divider:!0}:Array.isArray(s?.items)?{...s,items:n(s.items,h+1)}:s):i},r=function(i){return Array.isArray(i)?i.filter(h=>!(h&&(h.type==="separator"||h.type==="spacer"))):[]};const o={...w.brand},e=o.size&&o.size!=="md"?` navbar-brand-${o.size}`:"";if(o.logo||o.icon||o.tagline){let i="";o.logo?i+=`<img src="${o.logo}" class="navbar-brand-logo" alt="${o.text||""}">`:o.icon&&(i+=`<span data-icon="${o.icon}" style="width:1.1em;height:1.1em;margin-right:.35em;vertical-align:middle;"></span>`),o.text&&(i+=`<span class="navbar-brand-text${e}">${o.text}</span>`),o.tagline&&(i+=`<small class="navbar-brand-tagline">${o.tagline}</small>`),o.html=i}else e&&o.text&&(o.html=`<span class="navbar-brand-text${e}">${o.text}</span>`);const t=w.position==="floating",c=w.orientation==="vertical";Domma.elements.navbar("#site-navbar",{brand:o,items:n(w.items||[]),variant:w.variant||"dark",position:t?"static":w.position||"sticky",collapsible:!0,appearOnHover:!!w.appearOnHover}),Domma.icons.scan("#site-navbar");const u=_.get(0);u.classList.add(`dm-nav-align-${X(w)||"centre"}`),c&&_.get(0).classList.add("dm-nav-vertical",w.side==="right"?"dm-nav-vertical-right":"dm-nav-vertical-left"),N(u,w),t&&(J(_.get(0),w),Y(_.get(0).parentNode||document));const y=u.querySelector(".navbar-toggle"),p=u.querySelector(".navbar-collapse");if(y&&p){let i=null;const h=()=>{p.classList.contains("show")&&y.click()},s=()=>{i&&(i.remove(),i=null),document.body.classList.remove("dm-drawer-open")},a=()=>{i||(i=document.createElement("div"),i.className="dm-navbar-backdrop",i.addEventListener("click",h),document.body.appendChild(i),document.body.classList.add("dm-drawer-open"))};y.addEventListener("click",()=>{requestAnimationFrame(()=>{p.classList.contains("show")?a():s()})}),p.addEventListener("click",l=>{l.target.closest("a")&&requestAnimationFrame(s)}),document.addEventListener("keydown",l=>{l.key==="Escape"&&p.classList.contains("show")&&h()})}const m=u.querySelector(".navbar-nav");if(m){const i=Array.from(m.children);let h=0;for(const s of w.items||[])if(!(s&&s.type==="separator")){if(s&&s.type==="spacer"){const a=document.createElement("li"),l=["xs","sm","md","lg","xl","flex"].includes(s.size)?s.size:"md";a.className=`dm-menu-spacer dm-menu-spacer--${l}`,a.setAttribute("aria-hidden","true"),m.insertBefore(a,i[h]||null);continue}h++}}const C=r(w.items||[]);u.querySelectorAll(".navbar-link[data-index], .navbar-dropdown-item[data-index]").forEach(i=>{const h=C[parseInt(i.dataset.index,10)];if(!h)return;const s=i.dataset.subindex!=null,a=s&&Array.isArray(h.items)?h.items[parseInt(i.dataset.subindex,10)]:h;if(!a)return;a.colour&&O(i,a.colour),a.pill&&!s&&V(i,a.pill),W(i,a);const l=M(a.badge);l&&i.appendChild(l),j(a.url,location.pathname)&&(i.classList.add("active"),i.setAttribute("aria-current","page"))})}const x=$("#site-footer");if(x.length){let t=function(a){return String(a??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")},c=function(a){return t(a).replace(/'/g,"'")},u=function(a){if(a&&a.type==="separator")return'<li class="footer-sep"><hr></li>';if(a&&a.type==="spacer")return`<li class="dm-menu-spacer dm-menu-spacer--${["xs","sm","md","lg","xl","flex"].includes(a.size)?a.size:"md"}" aria-hidden="true"></li>`;const l=c(a.url||"#"),d=t(a.text||""),b=Array.isArray(a.items)&&a.items.length?`<ul>${a.items.map(u).join("")}</ul>`:"",v=G(a),I=[v.hover&&` data-menu-item-hover="${c(v.hover)}"`,v.active&&` data-menu-item-active="${c(v.active)}"`].filter(Boolean).join(""),D=v.accent?` style="--dm-menu-highlight:${c(v.accent)}"`:"";return`<li><a href="${l}"${I}${D}>${d}</a>${b}</li>`},y=function(a,l){return(a||[]).forEach(d=>{d&&(d.type==="separator"||d.type==="spacer")||(l.push(d),Array.isArray(d.items)&&d.items.length&&y(d.items,l))}),l};const o=g.social||{},e={twitter:{label:"X / Twitter",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-4.714-6.231-5.401 6.231H2.742l7.73-8.835L1.254 2.25H8.08l4.259 5.629L18.244 2.25zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>'},facebook:{label:"Facebook",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>'},instagram:{label:"Instagram",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zM12 0C8.741 0 8.333.014 7.053.072 2.695.272.273 2.69.073 7.052.014 8.333 0 8.741 0 12c0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98C8.333 23.986 8.741 24 12 24c3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98C15.668.014 15.259 0 12 0zm0 5.838a6.162 6.162 0 100 12.324 6.162 6.162 0 000-12.324zM12 16a4 4 0 110-8 4 4 0 010 8zm6.406-11.845a1.44 1.44 0 100 2.881 1.44 1.44 0 000-2.881z"/></svg>'},linkedin:{label:"LinkedIn",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg>'},github:{label:"GitHub",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>'},youtube:{label:"YouTube",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M23.495 6.205a3.007 3.007 0 00-2.088-2.088c-1.87-.501-9.396-.501-9.396-.501s-7.507-.01-9.396.501A3.007 3.007 0 00.527 6.205a31.247 31.247 0 00-.522 5.805 31.247 31.247 0 00.522 5.783 3.007 3.007 0 002.088 2.088c1.868.502 9.396.502 9.396.502s7.506 0 9.396-.502a3.007 3.007 0 002.088-2.088 31.247 31.247 0 00.5-5.783 31.247 31.247 0 00-.5-5.805zM9.609 15.601V8.408l6.264 3.602z"/></svg>'}};let n='<div class="footer-inner container">';const r=window.__CMS_FOOTER__||{primary:[],legal:[],copyright:""};if(r.copyright&&(n+=`<p>${t(r.copyright)}</p>`),r.primary.length&&(n+=`<nav class="footer-nav footer-nav--primary"><ul>${r.primary.map(u).join("")}</ul></nav>`),r.legal.length&&(n+=`<nav class="footer-nav footer-nav--legal"><ul>${r.legal.map(u).join("")}</ul></nav>`),g.footer){const a=Object.keys(e).filter(l=>o[l]);a.length&&(n+='<div class="footer-social">',a.forEach(l=>{const{label:d,svg:b}=e[l];n+=`<a href="${o[l]}" target="_blank" rel="noopener noreferrer" aria-label="${d}" class="footer-social-link">${b}</a>`}),n+="</div>")}n+="</div>",x.html(n);const p=[...y(r.primary,[]),...y(r.legal,[])];x.get(0).querySelectorAll(".footer-nav a").forEach((a,l)=>{const d=p[l];if(!d)return;d.colour&&O(a,d.colour);const b=M(d.badge);b&&a.appendChild(b),j(d.url,location.pathname)&&(a.classList.add("is-active"),a.setAttribute("aria-current","page"))}),N(x.get(0).querySelector(".footer-nav--primary"),{highlight:r.primaryHighlight}),N(x.get(0).querySelector(".footer-nav--legal"),{highlight:r.legalHighlight});const C=g.footer?.showMotionToggle!==!1,i=S.get("reduced_motion"),h=i!==null?!!i:!!(window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches),s=x.get(0).querySelector(".footer-inner");if(C&&s){const a=document.createElement("input");a.type="checkbox",a.className="form-switch-input",a.id="dm-motion-switch",a.checked=h,a.addEventListener("change",function(){S.set("reduced_motion",this.checked),window.location.reload()});const l=document.createElement("span");l.className="form-switch-label",l.textContent="Reduce motion";const d=document.createElement("label");d.className="form-switch footer-motion-switch",d.title="Reduce motion",d.appendChild(a),d.appendChild(l),s.appendChild(d)}}$("#site-sidebar").length&&Domma.elements.sidebar("#site-sidebar",{autoGenerate:!0,selector:"h2, h3",collapsible:!1,push:!0,contentSelector:".site-content"}),Domma.icons.scan(),Domma.syntax?.scan&&Domma.syntax.scan();const f=$(".page-body");if(f.length){f.find(".accordion").each(function(){Domma.elements.accordion(this,{allowMultiple:this.dataset.multi==="true"})}),f.find(".tabs").each(function(){Domma.elements.tabs(this)}),f.find(".carousel").each(function(){const o={autoplay:this.dataset.autoplay==="true",interval:parseInt(this.dataset.interval,10)||5e3,loop:this.dataset.loop!=="false",animation:this.dataset.animation||"slide"};if(this.dataset.animationDuration){const e=parseInt(this.dataset.animationDuration,10);Number.isFinite(e)&&(o.animationDuration=e)}this.dataset.animationEasing&&(o.animationEasing=this.dataset.animationEasing),this.dataset.ctx&&(this._dmCtxSlides=Array.from(this.querySelectorAll(".carousel-slide")).map(e=>e.cloneNode(!0)),this._dmCarouselOpts=o),this._dmCarousel=Domma.elements.carousel(this,o)}),f.find(".dm-countdown").each(function(){const o={autoStart:!0};this.dataset.to&&(o.targetDate=new Date(this.dataset.to)),this.dataset.duration&&(o.duration=parseInt(this.dataset.duration,10)),this.dataset.format&&(o.format=this.dataset.format),Domma.elements.timer(this,o)}),f.find("[data-tooltip]").each(function(){Domma.elements.tooltip(this,{content:$(this).data("tooltip"),position:$(this).data("tooltip-position")||"top"})}),f.find(".dm-progression").each(function(){const o=Array.from(this.querySelectorAll(".dm-progression-item")).map(r=>{const t={title:r.querySelector(".dm-progression-item-title")?.textContent?.trim()||"",description:r.querySelector(".dm-progression-item-body")?.innerHTML?.trim()||""};return r.dataset.date&&(t.date=r.dataset.date),r.dataset.status&&(t.status=r.dataset.status),r.dataset.entryId&&(t._entryId=r.dataset.entryId),t}),e=this.dataset.layout==="centred"?"centered":this.dataset.layout||"vertical",n=Domma.elements.progression(this,{layout:e,theme:this.dataset.theme||"minimal",mode:this.dataset.mode||"timeline",statusIcons:!0,allowHtml:!0,...o.length?{items:o}:{}});o.length&&(this._dmCtx={instance:n,items:o})});try{Domma.effects.reveal(".page-body .hero",{animation:"slide-up",duration:480,threshold:.06,stagger:60,once:!1})}catch{}document.querySelectorAll(".page-body .row[data-reveal]").forEach(o=>{const e=o.dataset.revealMode||"stagger",n=o.dataset.revealAnimation||"slide-up",r=parseInt(o.dataset.revealDuration,10)||400,t=parseInt(o.dataset.revealStagger,10)||60,c=parseInt(o.dataset.revealDelay,10)||0,u=o.dataset.revealDirection==="rtl",y=Array.from(o.children),p={"slide-up":"translateY(30px)","slide-down":"translateY(-30px)","slide-left":"translateX(30px)","slide-right":"translateX(-30px)",zoom:"scale(0.85)",flip:"perspective(600px) rotateX(15deg)"};y.forEach((m,C)=>{m.style.opacity="0",m.style.transform=p[n]||"",m.style.transition=`opacity ${r}ms ease, transform ${r}ms ease`;const i=u?y.length-1-C:C;m.style.transitionDelay=e==="stagger"?`${c+i*t}ms`:`${c}ms`}),requestAnimationFrame(()=>requestAnimationFrame(()=>{const m=new IntersectionObserver(C=>{C.forEach(i=>{i.isIntersecting&&(i.target.offsetWidth,i.target.style.opacity="1",i.target.style.transform="none",m.unobserve(i.target))})},{threshold:.1});y.forEach(C=>m.observe(C))}))}),f.find(".card[data-collapsible]").each(function(){const o=this.querySelector(".card-header");o&&o.addEventListener("click",()=>this.classList.toggle("is-collapsed"))}),f.find(".dm-so-trigger").each(function(){this.addEventListener("click",()=>{const o=this.dataset.soTarget,e=document.getElementById(o);if(!e)return;const n=E.slideover({title:e.dataset.soTitle||"",size:e.dataset.soSize||"md",position:e.dataset.soPosition||"right"});e.style.display="",n.setContent(e),n.open()})})}if(typeof $.setup=="function"){const o=Object.assign({},window.__CMS_DCONFIG__||{});if(document.querySelectorAll(".dm-page-config[data-config]").forEach(e=>{try{const n=atob(e.dataset.config),r=JSON.parse(n);Object.assign(o,r)}catch{}}),Object.keys(o).length>0){const e={};for(const[n,r]of Object.entries(o)){const t=r?.events?.click,{confirm:c,toast:u,alert:y,prompt:p,...m}=t||{};c||u||y||p?($(n).on("click",async function(i){if(i.preventDefault(),c&&!await E.confirm(c))return;let h=null;if(!(p&&(h=await E.prompt(p,{inputPlaceholder:m.promptPlaceholder||"",inputValue:m.promptDefault||""}),h===null))){if(m.target){const s=$(m.target);m.toggleClass&&s.toggleClass(m.toggleClass),m.addClass&&s.addClass(m.addClass),m.removeClass&&s.removeClass(m.removeClass),h!==null&&(m.setText&&s.text(h),m.setVal&&s.val(h),m.setAttr&&s.attr(m.setAttr,h))}m.href&&(window.location.href=m.href),u&&E.toast(u,{type:m.toastType||"success"}),y&&E.alert(y)}}),Object.keys(m).length&&(e[n]={...r,events:{...r.events,click:m}})):e[n]=r}$.setup(e)}}f.length&&q(f.get(0))});function q(w){w.querySelectorAll(".dm-cta-trigger").forEach(g=>{g.__ctaWired||(g.__ctaWired=!0,g.addEventListener("click",async()=>{const _=g.dataset.action,x=g.dataset.entry,A=g.dataset.confirm;let f=S.get("auth_token");if(!f){E.toast("Please log in to perform this action.",{type:"warning"});return}if(A&&!await E.confirm(A))return;const o=Array.from(g.childNodes).map(n=>n.cloneNode(!0));g.disabled=!0,g.textContent="Running\u2026";const e=n=>fetch(`/api/actions/${_}/public`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${n}`},body:JSON.stringify({entryId:x})});try{let n=await e(f);if(n.status===401){const t=S.get("auth_refresh_token");if(t){const c=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:t})});if(c.ok){const{token:u}=await c.json();S.set("auth_token",u),f=u,n=await e(f)}}}const r=await n.json().catch(()=>({}));if(!n.ok)throw new Error(r.error||r.message||`Error ${n.status}`);E.toast(r.message||"Action completed.",{type:"success"})}catch(n){E.toast(n.message||"Action failed.",{type:"error"})}finally{g.disabled=!1,g.textContent="",o.forEach(n=>g.appendChild(n)),Domma.icons.scan(g)}}))})}(function(){const g=document.querySelectorAll('.dm-collection-hydrate[data-collection-scope="mine"]');if(!g.length)return;const _=S.get("auth_token");function x(f,o){const e=document.createElement("div");e.className="dm-collection-display dm-collection-empty";const n=document.createElement("p");if(o){n.appendChild(document.createTextNode("Please "));const r=document.createElement("a");r.href="/login",r.textContent="sign in",n.appendChild(r),n.appendChild(document.createTextNode(" to view your entries."))}else n.textContent=f;return e.appendChild(n),e}function A(f,o){const e=document.createElement("template");e.innerHTML=o||"",f.replaceWith(e.content)}g.forEach(async f=>{const o=f.dataset.collectionAttrs;if(!o)return;if(!_){f.replaceChildren(x("",!0));return}const e=n=>fetch("/api/collections/render-scope",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${n}`},body:JSON.stringify({attrs:o})});try{let n=_,r=await e(n);if(r.status===401){const c=S.get("auth_refresh_token");if(c){const u=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:c})});if(u.ok){const{token:y}=await u.json();S.set("auth_token",y),n=y,r=await e(n)}}}if(!r.ok){const{error:c}=await r.json().catch(()=>({}));f.replaceChildren(x(c||"Unable to load entries.",!1));return}const{html:t}=await r.json();A(f,t)}catch{f.replaceChildren(x("Unable to load entries.",!1))}}),setTimeout(()=>{typeof Domma<"u"&&Domma.icons?.scan&&Domma.icons.scan(),typeof Domma<"u"&&Domma.syntax?.scan&&Domma.syntax.scan(),q(document.body)},100)})(),(function(){const g=document.querySelectorAll("[data-collection-table]");!g.length||typeof T>"u"||!T.create||g.forEach(_=>{let x;try{x=JSON.parse(atob(_.dataset.payload))}catch{return}const{columns:A,rows:f,search:o,sortable:e,exportable:n,pageSize:r,empty:t,ctaConfig:c,slug:u,exportCfg:y}=x;if(!A?.length)return;if(c){const C=A.findIndex(i=>i.key==="_cta");C!==-1&&(A[C]={key:"_cta",title:"",render:(i,h)=>{const s=document.createElement("button");if(s.className=`btn btn-${c.style||"primary"} dm-cta-trigger`,s.dataset.action=c.action||"",s.dataset.entry=h._entryId||"",c.confirm&&(s.dataset.confirm=c.confirm),c.icon){const a=document.createElement("span");a.dataset.icon=c.icon,s.appendChild(a),s.appendChild(document.createTextNode(" "))}return s.appendChild(document.createTextNode(c.label||"Run")),s}})}const p="col-table-"+Math.random().toString(36).slice(2,7),m=document.createElement("div");m.id=p,_.replaceChildren(m),T.create("#"+p,{data:f,columns:A,search:o,sortable:e,exportable:n,pageSize:r,emptyMessage:t}),c&&q(m),_._dmTable={columns:A,rows:f,slug:u,exportCfg:y,render(C){T.create("#"+p,{data:C,columns:A,search:o,sortable:e,exportable:n,pageSize:r,emptyMessage:t}),c&&q(m)}}})})(),(function(){const g=document.querySelectorAll("[data-form-inline]");if(!g.length||typeof F>"u")return;function _(e){const n=e&&e.logic;return!!(n&&(n.visibility||n.requirement))}function x(e,n){const r={};return(e||[]).forEach(t=>{if(t.type==="page-break"||t.type==="spacer"||!t.name)return;const c=t.type==="chooser"||!!(t.variant&&["select","radio","checkbox-group"].includes(t.type)),u=t.type==="checkbox"?"boolean":t.type==="date"||t.type==="icon"||t.type==="image"?"string":c?"chooser":t.type,y={...t.formConfig||{}};y.span==="full"&&n&&(y.span=n);const p={type:u,required:_(t)?!1:t.required,label:t.label,options:t.options,formConfig:{...t.placeholder&&{placeholder:t.placeholder},...t.helper&&{hint:t.helper},...y}};c&&(t.variant&&(p.variant=t.variant),(t.multiple||t.type==="checkbox-group")&&(p.multiple=!0),t.density&&(p.density=t.density),t.columns&&(p.columns=t.columns),t.accent&&(p.accent=t.accent),t.accentStyle&&(p.accentStyle=t.accentStyle),t.glow&&(p.glow=!0),t.glowColour&&(p.glowColour=t.glowColour),t.shadow&&(p.shadow=t.shadow),t.shadowColour&&(p.shadowColour=t.shadowColour)),r[t.name]=p}),r}function A(e){const n={};return(e||[]).forEach(r=>{if(!(!r.name||r.type==="page-break"||r.type==="spacer")&&(r.type==="select"||r.type==="multiselect")&&r.required){const t=(r.options||[])[0];t&&(n[r.name]=typeof t=="object"?t.value:t)}}),n}function f(e,n){(n||[]).forEach(r=>{if(r.type!=="date"||!r.name)return;const t=e.querySelector(`[name="${r.name}"]`);t&&t.type!=="date"&&(t.type="date")})}function o(e,n,r){let t=e.querySelector(".cms-form-message");t||(t=document.createElement("p"),t.className="cms-form-message",e.appendChild(t)),t.textContent=n,t.style.cssText=r?"color:var(--dm-danger);margin-top:.75rem;":"color:var(--dm-success);margin-top:.75rem;"}g.forEach(e=>{let n;try{n=JSON.parse(atob(e.dataset.formInline))}catch{return}const r=n.fields||[],t=n.settings||{},c=Number(t.columns)===3?3:2,u=t.layout||"grid",y=r.some(s=>s.type==="page-break"),p=async s=>{if(e.dataset.triggerBlocked==="true"){const a=e._formLogicRuntime,l=a&&a.submitBlock?a.submitBlock().reasons:[];o(e,l.join(" ")||"This form cannot be submitted yet.",!0);const d=new Error("Blocked by a form trigger");throw d.formSubmitFailed=!0,d}try{const a=e.querySelector(".fb-form-honeypot"),l=a?.querySelector('input[type="text"]')||null,d=a?.querySelector('[name="_t"]')||null,b=Object.assign({},s);delete b._t,a?.dataset.hpName&&delete b[a.dataset.hpName],l!==null&&(b._hp=l.value),d!==null&&(b._t=d.value);const v=await H.post(`/api/forms/submit/${n.slug}`,b);if(v&&v.error){o(e,v.error,!0);const I=new Error(v.error);throw I.formSubmitFailed=!0,I}if(v?.redirect){window.location.href=v.redirect;return}for(;e.firstChild;)e.removeChild(e.firstChild);o(e,v?.message||t.successMessage||"Thank you for your submission.",!1)}catch(a){throw a.formSubmitFailed||o(e,a.message||"Submission failed. Please try again.",!0),a}},m=Date.now();function C(s){const a=s.querySelector("form");if(!a||a.querySelector(".fb-form-honeypot"))return;const l=document.createElement("div");l.className="fb-form-honeypot",l.setAttribute("aria-hidden","true");const d=document.createElement("input");d.name=a.querySelector('[name="website"]')?"_website":"website",l.dataset.hpName=d.name,d.type="text",d.tabIndex=-1,d.autocomplete="url",d.placeholder="https://",l.appendChild(d);const b=document.createElement("input");b.name="_t",b.type="hidden",b.value=m,l.appendChild(b),a.appendChild(l)}function i(s){if(!window.FormLogicEngine||!r.some(l=>l.logic||Array.isArray(l.triggers)&&l.triggers.length))return null;const a=new window.FormLogicEngine.FormLogicRuntime(n,e,s?{baseValues:s}:void 0);if(a.init(),e._formLogicRuntime=a,e._formLogicObserved)return a;if(e._formLogicObserved=!0,e.parentNode&&typeof MutationObserver<"u"){const l=new MutationObserver(function(d){for(const b of d)for(const v of b.removedNodes)if(v===e||v.nodeType===1&&v.contains&&v.contains(e)){e._formLogicRuntime&&e._formLogicRuntime.destroy(),l.disconnect();return}});l.observe(e.parentNode,{childList:!0,subtree:!1}),e._formLogicObserver=l}return a}let h=!1;if(e.addEventListener("dm-form-end",s=>{const a=s.detail?.validation;if(a&&!a.valid){R(e,a.missing),P(a);return}h||(h=!0,p(s.detail?.values||{}).catch(()=>{}).finally(()=>{h=!1}))}),y&&F.wizard){const s=U(r,n.title),a=s.map(k=>({title:k.title,description:k.description,fields:x(k.fields,c)})),l=s.map(k=>k.fields);let d=0,b=null,v=null;const I=k=>{const L=window.FormLogicEngine;if(!L||!L.validateFields)return;const z=L.validateFields(e,l[d]||[]);z.valid||(k.preventDefault(),k.stopImmediatePropagation(),R(e,z.missing),P(z))},D=()=>{const k=l[d]||r;f(e,k),B(e,k),t.honeypot!==!1&&C(e),b&&b.destroy(),b=i(v?.getStepData?v.getStepData():void 0),e.querySelectorAll(".wizard-next, .wizard-submit").forEach(L=>L.addEventListener("click",I,!0))};v=F.wizard(e,{schema:{steps:a},layout:u,columns:c,finishText:t.submitText||"Finish",onSubmit:p,onStepChange:k=>{d=k,setTimeout(D,0)}}),e.addEventListener("dm-form-trigger",k=>{if(k.detail?.type!=="goto-step"||!v?.goToStep)return;const L=Number(k.detail.action.step);!Number.isFinite(L)||L<0||L>=a.length||L!==d&&v.goToStep(L)}),Promise.resolve(v).then(D)}else if(F.render){const s=F.render(e,x(r,c),A(r),{submitText:t.submitText||"Submit",layout:u,columns:c,onSubmit:p});Promise.resolve(s).then(function(){if(u==="grid"&&t.submitSpan==="full"){const a=e.querySelector(".form-buttons");a&&a.classList.add("col-span-full")}f(e,r),B(e,r),t.honeypot!==!1&&C(e),i()})}}),$(document).on("click",".dm-banner__dismiss",function(){$(this).closest(".dm-banner").remove()})})();
|
|
@@ -53,12 +53,15 @@ export function apiTokensBadge(tokens = [], now = Date.now()) {
|
|
|
53
53
|
const d = Math.ceil((at(t) - now) / DAY);
|
|
54
54
|
return d <= 0 ? 'today' : d === 1 ? 'tomorrow' : `in ${d} days`;
|
|
55
55
|
};
|
|
56
|
+
// A number either way (0.80): how many tokens while none needs anyone - an
|
|
57
|
+
// inventory - and the expired and expiring ones when some do.
|
|
56
58
|
return {
|
|
57
|
-
count: need,
|
|
59
|
+
count: need || tokens.length,
|
|
60
|
+
...(!need && {inventory: true}),
|
|
58
61
|
label: need
|
|
59
62
|
? [expired.length && `${plural(expired.length, 'token')} expired`, soon.length && `${soon.length} expiring soon`].filter(Boolean).join(', ')
|
|
60
63
|
: `${plural(tokens.length, 'token')}, none expiring soon`,
|
|
61
|
-
tone: expired.length ? 'danger' : 'attention',
|
|
64
|
+
tone: expired.length ? 'danger' : need ? 'attention' : 'info',
|
|
62
65
|
details: {
|
|
63
66
|
title: 'API Tokens',
|
|
64
67
|
rows: [
|
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
import {getProfile, updateProfile} from '../../services/userProfiles.js';
|
|
32
32
|
import {buildClearedSessionCookie, buildSessionCookie} from '../../services/viewerSession.js';
|
|
33
33
|
import {createTransport, sendEmail} from '../../services/email.js';
|
|
34
|
+
import {loginFailed, loginRateLimited} from '../../services/notification-sources.js';
|
|
34
35
|
|
|
35
36
|
const { accessTokenExpiry, refreshTokenExpiry } = config.auth;
|
|
36
37
|
|
|
@@ -94,7 +95,7 @@ export async function authRoutes(fastify) {
|
|
|
94
95
|
});
|
|
95
96
|
|
|
96
97
|
// POST /api/auth/login
|
|
97
|
-
fastify.post('/auth/login', { config: { rateLimit: { max: 5, timeWindow: '1 minute' } } }, async (request, reply) => {
|
|
98
|
+
fastify.post('/auth/login', { config: { rateLimit: { max: 5, timeWindow: '1 minute', onExceeded: (req) => { loginRateLimited(req.ip); } } } }, async (request, reply) => {
|
|
98
99
|
const { email, password } = request.body || {};
|
|
99
100
|
if (!email || !password) {
|
|
100
101
|
return reply.status(400).send({ error: 'email and password are required' });
|
|
@@ -102,11 +103,13 @@ export async function authRoutes(fastify) {
|
|
|
102
103
|
|
|
103
104
|
const user = await getUserByEmail(email);
|
|
104
105
|
if (!user || !user.isActive) {
|
|
106
|
+
loginFailed(email, request.ip);
|
|
105
107
|
return reply.status(401).send({ error: 'Invalid credentials' });
|
|
106
108
|
}
|
|
107
109
|
|
|
108
110
|
const valid = await validatePassword(password, user.password);
|
|
109
111
|
if (!valid) {
|
|
112
|
+
loginFailed(email, request.ip);
|
|
110
113
|
return reply.status(401).send({ error: 'Invalid credentials' });
|
|
111
114
|
}
|
|
112
115
|
|
|
@@ -156,6 +159,17 @@ export async function authRoutes(fastify) {
|
|
|
156
159
|
return {...user, profile: profileEntry?.data || {}};
|
|
157
160
|
});
|
|
158
161
|
|
|
162
|
+
// GET /api/auth/me/_count - My Profile's sidebar badge: profile details left empty
|
|
163
|
+
fastify.get('/auth/me/_count', {preHandler: [authenticate]}, async (request, reply) => {
|
|
164
|
+
const user = await getUserById(request.user.id);
|
|
165
|
+
if (!user) return reply.status(404).send({error: 'User not found'});
|
|
166
|
+
const {getCollection} = await import('../../services/collections.js');
|
|
167
|
+
const schema = await getCollection('user-profiles').catch(() => null);
|
|
168
|
+
const profileEntry = await getProfile(request.user.id);
|
|
169
|
+
const {profileBadge} = await import('../../services/sidebarBadges.js');
|
|
170
|
+
return profileBadge(user, profileEntry?.data || {}, schema?.fields || []);
|
|
171
|
+
});
|
|
172
|
+
|
|
159
173
|
// PUT /api/auth/me - self-service update (name, email, password, profile)
|
|
160
174
|
fastify.put('/auth/me', {preHandler: [authenticate]}, async (request, reply) => {
|
|
161
175
|
const id = request.user.id;
|