From 914a7bb21d74cb12a828017be7367a626085ff82 Mon Sep 17 00:00:00 2001 From: prismiwi2015 Date: Sun, 13 Sep 2026 11:57:18 +0200 Subject: [PATCH 1/2] Improve formula pickers and conditional logic editing --- assets/css/builder.css | 311 +++++++++++- assets/js/analytics.js | 3 +- assets/js/analytics.min.js | 2 +- assets/js/builder.js | 662 +++++++++++++++++++++---- assets/js/builder.min.js | 8 +- assets/js/entries.js | 3 +- assets/js/entries.min.js | 2 +- docs/javascript.md | 54 ++ src/builder.ts | 378 ++++++++++---- src/condition-copy.ts | 136 +++++ src/condition-values.ts | 63 +++ src/formula-editor.ts | 30 +- src/merge-tags.ts | 161 +++--- src/ui.ts | 5 +- tests/vitest/builder-picker.test.ts | 54 ++ tests/vitest/condition-copy.test.ts | 86 ++++ tests/vitest/condition-toolbar.test.ts | 136 +++++ tests/vitest/condition-values.test.ts | 85 ++++ tests/vitest/merge-tags.test.ts | 115 +++++ 19 files changed, 2005 insertions(+), 289 deletions(-) create mode 100644 src/condition-copy.ts create mode 100644 src/condition-values.ts create mode 100644 tests/vitest/builder-picker.test.ts create mode 100644 tests/vitest/condition-copy.test.ts create mode 100644 tests/vitest/condition-toolbar.test.ts create mode 100644 tests/vitest/condition-values.test.ts create mode 100644 tests/vitest/merge-tags.test.ts diff --git a/assets/css/builder.css b/assets/css/builder.css index 507fb7f..f6dacde 100644 --- a/assets/css/builder.css +++ b/assets/css/builder.css @@ -273,7 +273,7 @@ } .atfb__body { - grid-template-columns: 240px 1fr 320px; + grid-template-columns: 200px minmax( 0, 1fr ) 260px; } .atfe__body { @@ -454,7 +454,7 @@ /* -------------------------------------------------------------------------- */ .atfb-canvas__inner { - max-inline-size: 720px; + max-inline-size: 820px; margin-inline: auto; } @@ -2077,6 +2077,7 @@ html body.atf-drag-active [contenteditable="true"] { } .atfb-tagpick { + box-sizing: border-box; position: absolute; inset-inline-end: 0; inset-block-start: 100%; @@ -2103,20 +2104,19 @@ html body.atf-drag-active [contenteditable="true"] { box-shadow: 0 12px 32px rgba( 0, 0, 0, 0.22 ); } -/* Opened upward when the button is too near the bottom of the pane. */ -.atfb-tagpick--above { - inset-block-start: auto; - inset-block-end: 100%; - margin-block-end: 4px; -} - .atfb-tagpick__intro { + flex-shrink: 0; margin: 0; font-size: 11px; color: var( --atfb-muted ); } +.atfb-tagpick__search { + flex-shrink: 0; +} + .atfb-tagpick__list { + min-block-size: 0; overflow-y: auto; margin-inline: -4px; padding-inline: 4px; @@ -2340,7 +2340,7 @@ input.atfb-cond__value { line-height: 1.5; color: var( --atfb-fg ); max-inline-size: 24ch; - min-inline-size: 3ch; + min-inline-size: 100px; field-sizing: content; cursor: text; min-block-size: 0; @@ -4052,3 +4052,294 @@ os-checkbox-label.atfb-check { transition: none; } } + +/* Keep copied conditions readable even when a rule contains a long answer. */ +.atfb-condition-copy__preview { + padding: 12px; + border-radius: var( --atfb-radius-ctl ); + background: var( --atfb-accent-soft ); + line-height: 1.6; + overflow-wrap: anywhere; +} + +.atfb-cond__rule, +.atfb-cond__actions { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 6px; + inline-size: 100%; +} + +.atfb-cond__add, +.atfb-cond__delete, +.atfb-cond__clear { + display: inline-flex; + align-items: center; + justify-content: center; + min-inline-size: 28px; + min-block-size: 28px; + padding: 3px 7px; + border: 1px solid var( --atfb-border ); + border-radius: var( --atfb-radius-ctl ); + background: transparent; + color: var( --atfb-fg ); + font: inherit; + cursor: pointer; +} + +.atfb-cond__add { + font-size: 11px; +} + +.atfb-cond__delete:hover, +.atfb-cond__clear:hover:not( :disabled ) { + color: var( --atfb-danger ); + border-color: currentColor; +} + +.atfb-cond__add:hover { + background: var( --atfb-accent-soft ); +} + +.atfb-cond__clear:disabled { + opacity: 0.5; + cursor: default; +} + +.atfb-cond__add:focus-visible, +.atfb-cond__delete:focus-visible, +.atfb-cond__clear:focus-visible { + outline: 2px solid var( --atfb-accent ); + outline-offset: 2px; +} + +/* Copy is a quiet inline action: fixed geometry and no animated shell sheen. */ +.atfb-copy-condition { + display: inline-flex; + align-items: center; + gap: 5px; + padding: 5px 8px; + border: 1px solid var( --atfb-border ); + border-radius: var( --atfb-radius-ctl ); + background: var( --atfb-raised ); + color: var( --atfb-fg ); + font: inherit; + font-size: 11px; + cursor: pointer; + transition: none; +} + +.atfb-copy-condition:hover { + border-color: var( --atfb-accent ); +} + +.atfb-copy-condition:focus-visible { + outline: 2px solid var( --atfb-accent ); + outline-offset: 2px; +} + + +/* A single quiet badge opens the settings; the titlebar stays one line. */ +.atfb-condition-toggle { + display: inline-flex; + align-items: center; + gap: 5px; + white-space: nowrap; +} + +.atfb-condition-dot { + inline-size: 5px; + block-size: 5px; + border-radius: 50%; + background: currentColor; + opacity: 0.35; +} + +.atfb-condition-toggle.is-on .atfb-condition-dot { + opacity: 1; +} + +.atfb-card.is-selected .atfb-card__bar .atfb-req { + color: var( --os-titlebar-color-focused, var( --atfb-accent-ink ) ); +} + +.atfb-card__body > .atfb-cond { + margin-block-start: 0; + padding: 12px 14px; + gap: 8px; + border-block-start: 1px solid var( --atfb-border ); + background: color-mix( in srgb, var( --atfb-accent ) 3%, var( --atfb-bg ) ); +} + +.atfb-cond__heading { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + inline-size: 100%; +} + +.atfb-cond__label { + display: inline-flex; + align-items: center; + gap: 6px; + font-size: 10px; + font-weight: 600; + letter-spacing: 0.05em; + text-transform: uppercase; +} + +.atfb-cond .atfb-copy-condition, +.atfb-condition-editor__footer .atfb-copy-condition { + padding: 4px 0; + border: 0; + background: transparent; + color: var( --atfb-muted ); +} + +.atfb-cond .atfb-copy-condition:hover, +.atfb-condition-editor__footer .atfb-copy-condition:hover { + color: var( --atfb-accent ); +} + +.atfb-copy-condition .dashicons { + font-size: 14px; + inline-size: 14px; + block-size: 14px; +} + +.atfb-cond__rule { + padding: 6px 8px; + border: 1px solid var( --atfb-border ); + border-radius: 7px; + background: var( --atfb-bg ); +} + +.atfb-cond__delete { + margin-inline-start: auto; + border-color: transparent; + color: var( --atfb-muted ); +} + +.atfb-cond__actions { + justify-content: space-between; +} + +.atfb-cond__add, +.atfb-cond__clear { + padding: 3px 0; + border-color: transparent; + background: transparent; + color: var( --atfb-muted ); +} + +.atfb-cond__add { + color: var( --atfb-accent ); + font-weight: 600; +} + +.atfb-condition-editor { + inline-size: min( 580px, 100% ); + padding: 24px; +} + +.atfb-condition-editor__heading { + display: flex; + align-items: center; + gap: 12px; + margin-block-end: 24px; +} + +.atfb-condition-editor__heading h2 { + margin: 0 0 4px; + font-size: 18px; +} + +.atfb-condition-editor__heading p { + margin: 0; + color: var( --atfb-muted ); + font-size: 12px; +} + +.atfb-condition-editor__icon { + display: grid; + place-items: center; + inline-size: 40px; + block-size: 40px; + border-radius: 10px; + background: var( --atfb-accent-soft ); + color: var( --atfb-accent ); +} + +.atfb-condition-editor__switch { + padding-block-end: 18px; + border-block-end: 1px solid var( --atfb-border ); +} + +.atfb-condition-editor__switch p { + margin: 6px 0 0; + font-size: 12px; + color: var( --atfb-muted ); +} + +.atfb-condition-editor__sentence { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 6px; + margin-block: 18px 12px; + font-size: 12px; + color: var( --atfb-muted ); +} + +.atfb-condition-editor__empty { + margin-block: 20px; + font-size: 13px; + line-height: 1.6; + color: var( --atfb-muted ); +} + +.atfb-condition-editor .atfb-rule { + display: grid; + grid-template-columns: minmax( 0, 1.2fr ) minmax( 0, 1fr ) minmax( 100px, 0.6fr ) auto; + align-items: center; + gap: 8px; +} + +.atfb-condition-editor .atfb-rule__top { + display: contents; +} + +.atfb-condition-editor .atfb-rule__top > .atfb-card__action { + grid-column: 4; + grid-row: 1; +} + +.atfb-condition-editor__footer { + display: flex; + align-items: center; + justify-content: space-between; + flex-wrap: wrap; + gap: 12px; + margin-block-start: 24px; + padding-block-start: 16px; + border-block-start: 1px solid var( --atfb-border ); +} + +.atfb-condition-editor__footer .atfb-modal__actions { + margin: 0; +} + +.atfb-condition-editor .atfb-rule input { + border: 1px solid var( --atfb-border ); + border-radius: 6px; + min-inline-size: 100px; + inline-size: 100%; +} + +/* Condition values stay usable even when their content is short or empty. */ +.atfb-cond__value-select, +.atfb-rule input { + min-inline-size: 100px; +} diff --git a/assets/js/analytics.js b/assets/js/analytics.js index 79808ae..dcd2903 100644 --- a/assets/js/analytics.js +++ b/assets/js/analytics.js @@ -221,6 +221,7 @@ var allTerrainFormsAnalytics = function(exports) { for (const option of options) { const item = document.createElement("os-option"); item.setAttribute("value", option.value); + if (option.disabled) item.setAttribute("disabled", ""); item.textContent = option.label; host.append(item); } @@ -238,7 +239,7 @@ var allTerrainFormsAnalytics = function(exports) { (option) => el("option", { value: option.value, text: option.label, - attrs: { selected: option.value === value } + attrs: { selected: option.value === value, disabled: option.disabled } }) ) }); diff --git a/assets/js/analytics.min.js b/assets/js/analytics.min.js index ccb406b..4c016a9 100644 --- a/assets/js/analytics.min.js +++ b/assets/js/analytics.min.js @@ -1 +1 @@ -var allTerrainFormsAnalytics=function(p){"use strict";function F(){return window.wp?.os??null}const m=window.allTerrainForms;class y extends Error{constructor(e,t,n=""){super(e),this.name="ApiError",this.status=t,this.code=n}}function T(s,e){return s.includes("?")?`${s}${e.replace("?","&")}`:`${s}${e}`}async function M(s,e={}){if(!m?.restUrl)throw new y("AllTerrain Forms is not configured on this page.",0);const t=T(m.restUrl,s),n={"Content-Type":"application/json",...e.headers??{}};m.nonce&&(n["X-WP-Nonce"]=m.nonce);const i=F(),r=await(i?.fetch?(l,c)=>i.fetch(l,c,{source:"allterrain-forms"}):(l,c)=>fetch(l,c))(t,{credentials:"same-origin",...e,headers:n});if(!r.ok){let l=`Request failed with status ${r.status}.`,c="";try{const h=await r.json();l=h.message??l,c=h.code??""}catch{}throw new y(l,r.status,c)}if(r.status!==204)return await r.json()}const d=s=>M(s);async function D(s){if(!m?.wpRestUrl)throw new y("AllTerrain Forms is not configured on this page.",0);const e=m.nonce?{"X-WP-Nonce":m.nonce}:{},t=F(),n=T(m.wpRestUrl,s),i=t?.fetch?await t.fetch(n,{credentials:"same-origin",headers:e},{source:"allterrain-forms"}):await fetch(n,{credentials:"same-origin",headers:e});if(!i.ok)throw new y(`Request failed with status ${i.status}.`,i.status);return await i.json()}function j(s){if(!s||!s.includes("&"))return s;const e=document.createElement("textarea");return e.innerHTML=s,e.value}const u=(s,e)=>M(s,{method:"POST",body:JSON.stringify(e)}),w=s=>M(s,{method:"DELETE"});function P(s){const e=new URLSearchParams;for(const[n,i]of Object.entries(s))i===void 0||i===""||i===!1||e.set(n,String(i));const t=e.toString();return t?`?${t}`:""}function g(s){const e=s?.schema?.settings;return e&&(!e.themeOverrides||Array.isArray(e.themeOverrides))&&(e.themeOverrides={...e.themeOverrides}),s}const f={config:()=>d("/config"),mailpoet:()=>d("/mailpoet"),listForms:()=>d("/forms"),listArchivedForms:()=>d("/forms?archived=1"),archiveForm:s=>u(`/forms/${s}/archive`,{}),unarchiveForm:s=>u(`/forms/${s}/unarchive`,{}),getForm:s=>d(`/forms/${s}`).then(g),createForm:s=>u("/forms",s).then(g),updateForm:(s,e)=>u(`/forms/${s}`,e).then(g),duplicateForm:s=>u(`/forms/${s}/duplicate`,{}).then(g),deleteForm:s=>w(`/forms/${s}`),preview:(s,e)=>u(`/forms/${s}/preview`,e),pages:()=>D("wp/v2/pages?per_page=100&status=publish&orderby=title&order=asc&_fields=id,title").then(s=>s.map(e=>({id:e.id,title:j(e.title?.rendered??"")||`#${e.id}`}))),mergeTags:s=>d(`/forms/${s}/merge-tags`).then(e=>e.groups),analytics:(s,e="")=>d(`/forms/${s}/analytics${e?`?dimension=${encodeURIComponent(e)}`:""}`),demoStatus:()=>d("/demo"),demoSeed:s=>u("/demo",s?{count:s}:{}),demoRemove:()=>w("/demo"),listEntries:s=>d(`/entries${P(s)}`),getEntry:s=>d(`/entries/${s}`),updateEntry:(s,e)=>u(`/entries/${s}`,e),deleteEntry:s=>w(`/entries/${s}`),exportEntries:s=>d(`/entries/export${P(s)}`),listThemes:()=>d("/themes"),saveTheme:s=>u("/themes",s),deleteTheme:s=>w(`/themes/${s}`)};function C(s){return`allterrain-forms/requested-form-${s}`}function R(s){try{const e=window.sessionStorage.getItem(C(s));return window.sessionStorage.removeItem(C(s)),Number(e)||0}catch{return 0}}function a(s,e={}){const t=document.createElement(s);e.class&&(t.className=e.class),e.text!==void 0&&(t.textContent=e.text),e.html!==void 0&&(t.innerHTML=e.html),e.title&&(t.title=e.title),e.type&&"type"in t&&(t.type=e.type),e.value!==void 0&&"value"in t&&(t.value=e.value),e.placeholder!==void 0&&"placeholder"in t&&(t.placeholder=e.placeholder),e.href&&"href"in t&&(t.href=e.href);for(const[n,i]of Object.entries(e.attrs??{}))i===void 0||i===!1||t.setAttribute(n,i===!0?"":String(i));Object.assign(t.style,e.style??{});for(const[n,i]of Object.entries(e.on??{}))t.addEventListener(n,i);for(const n of e.children??[])n==null||n===!1||t.append(n);return t}function _(s){s.replaceChildren()}function N(s,e,t){if(E("os-select")&&E("os-option")){const n=document.createElement("os-select");n.setAttribute("value",s),n.classList.add("atfb-field");for(const i of e){const o=document.createElement("os-option");o.setAttribute("value",i.value),o.textContent=i.label,n.append(o)}return n.addEventListener("os-pick",i=>{t(String(i.detail?.value??""))}),n}return a("select",{class:"atfb-input atfb-select",on:{change:n=>t(n.target.value)},children:e.map(n=>a("option",{value:n.value,text:n.label,attrs:{selected:n.value===s}}))})}function I(s,e,t="secondary",n){const i=[null,a("span",{text:s})];if(E("os-button")){const o=document.createElement("os-button");o.setAttribute("variant",t),o.setAttribute("type","button"),o.classList.add("atfb-button",`atfb-button--${t}`),o.addEventListener("click",e);for(const r of i)r&&o.append(r);return o}return a("button",{class:`atfb-button atfb-button--${t}`,type:"button",on:{click:e},children:i})}function E(s){return typeof customElements<"u"&&!!customElements.get(s)}const B=["os-button","os-checkbox-label","os-number-field","os-select","os-option","os-segmented","os-segment","os-color-field","os-empty-state"];let $=null;function U(){if($)return $;const s=window.wp?.os;return $=s?.loadComponents?s.loadComponents(B).catch(()=>{}):Promise.resolve(),$}async function G(s,e=""){const t=window.wp?.os;return t?.confirm?t.confirm({title:e,message:s,danger:!0}):window.confirm(s)}function x(s,e="",t="info"){const n=window.wp?.os;if(n?.notify){n.notify({title:s,body:e,type:t});return}console.info(`[AllTerrain Forms] ${s}${e?`: ${e}`:""}`)}function W(s){const e=s.closest(".os-window__body");e&&(e.addEventListener("scroll",()=>{e.scrollTop&&(e.scrollTop=0),e.scrollLeft&&(e.scrollLeft=0)},{passive:!0}),e.scrollTop=0,e.scrollLeft=0)}const O=[{key:"detractors",label:"Detractors",hint:"0–6"},{key:"passives",label:"Passives",hint:"7–8"},{key:"promoters",label:"Promoters",hint:"9–10"}],q=50;function L(s){const e=Math.floor(s.min),t=Math.ceil(s.max);if(!Number.isFinite(e)||!Number.isFinite(t)||tMath.min(o-1,Math.max(0,Math.floor((h-e)/i))),l=[];for(let h=0;ht.id===e)?this.formId=e:this.formId||(this.formId=this.forms[0].id),await this.load()}async showForm(e){this.formId=e,this.dimension="",await this.load()}async load(){try{this.report=await f.analytics(this.formId,this.dimension)}catch{this.fail("Could not load that report.");return}this.dimension=this.report.breakdown?.id??"",this.renderBar(),this.render()}fail(e){_(this.bar),_(this.body),this.body.append(a("p",{class:"atfa__empty",text:e}))}renderBar(){_(this.bar),this.bar.append(N(String(this.formId),this.forms.map(t=>({value:String(t.id),label:t.title||`#${t.id}`})),t=>{this.formId=Number(t),this.dimension="",this.load()}));const e=this.report?.dimensions??[];e.length>1&&this.bar.append(a("span",{class:"atfa__bar-label",text:"Group by"}),N(this.dimension,e.map(t=>({value:t.id,label:t.label})),t=>{this.dimension=t,this.load()}))}render(){const e=this.report;if(_(this.body),!!e){if(this.body.append(this.kpis(e)),e.submissions<1&&e.sampled<1)this.body.append(a("p",{class:"atfa__empty",text:"No submissions yet. Everything below fills in as they arrive."}));else{this.body.append(this.timeline(e)),e.tech&&this.body.append(this.techPanel(e.tech));for(const t of e.fields)t.nps&&this.body.append(this.npsPanel(t));e.breakdown&&e.breakdown.groups.length&&this.body.append(this.breakdownPanel(e.breakdown)),this.body.append(this.questions(e))}this.demoAvailable&&this.body.append(this.demoPanel())}}kpis(e){const t=[{label:"Submissions",value:String(e.submissions)},{label:"Views",value:String(e.views)},{label:"Conversion",value:`${e.conversion}%`,hint:"of people who saw it"},{label:"Completion",value:`${e.completion}%`,hint:"of people who started"},{label:"Unread",value:String(e.unread)},{label:"Spam",value:String(e.spam)}];return a("div",{class:"atfa-kpis",children:t.map(n=>a("div",{class:"atfa-kpi",children:[a("span",{class:"atfa-kpi__value",text:n.value}),a("span",{class:"atfa-kpi__label",text:n.label}),n.hint?a("span",{class:"atfa-kpi__hint",text:n.hint}):null]}))})}timeline(e){const t=e.timeline,n=t.reduce((o,r)=>Math.max(o,r.count),0),i=t.reduce((o,r)=>o+r.count,0);return this.panel("When they answered",`${i} in the last ${t.length} days · busiest day ${n}`,[a("div",{class:"atfa-timeline",children:t.map(o=>a("div",{class:`atfa-timeline__day${o.count?"":" is-empty"}`,attrs:{style:`height:${n?Math.max(2,o.count/n*100):0}%`,title:`${o.date}: ${o.count}`}}))}),a("div",{class:"atfa-timeline__axis",children:[a("span",{text:t[0]?.date??""}),a("span",{text:t[t.length-1]?.date??""})]})])}npsPanel(e){const t=e.nps;if(!t)return a("div");const n=i=>t.responses?i/t.responses*100:0;return this.panel(e.label,`${t.responses} answers · Net Promoter Score`,[a("div",{class:"atfa-nps",children:[a("div",{class:`atfa-nps__score is-${t.score>=50?"great":t.score>=0?"ok":"poor"}`,children:[a("span",{class:"atfa-nps__number",text:`${t.score>0?"+":""}${t.score}`}),a("span",{class:"atfa-nps__caption",text:"NPS"})]}),a("div",{class:"atfa-nps__detail",children:[a("div",{class:"atfa-nps__bar",children:O.map(i=>a("div",{class:`atfa-nps__band is-${i.key}`,attrs:{style:`width:${n(t[i.key])}%`,title:`${i.label} (${i.hint}): ${t[i.key]}`}}))}),a("div",{class:"atfa-nps__legend",children:O.map(i=>a("span",{class:`atfa-nps__key is-${i.key}`,children:[a("strong",{text:String(t[i.key])}),a("span",{text:` ${i.label} `}),a("small",{text:i.hint})]}))})]})]})])}techPanel(e){const t=[{key:"device",label:"Device"},{key:"browser",label:"Browser"},{key:"os",label:"System"}],n=o=>{const r=e[o.key],l=r.reduce((c,h)=>Math.max(c,h.share),0);return a("div",{class:"atfa-tech__col",children:[a("h4",{class:"atfa-tech__title",text:o.label}),a("ul",{class:"atfa-bars",children:r.map(c=>a("li",{class:"atfa-bars__row",children:[a("span",{class:"atfa-bars__label",text:c.label}),a("div",{class:"atfa-meter",children:[a("div",{class:"atfa-meter__fill",attrs:{style:`width:${l?c.share/l*100:0}%`}})]}),a("span",{class:"atfa-bars__value",text:c.conversion===null?`${c.share}%`:`${c.share}% → ${c.conversion}%`,attrs:c.conversion===null?{title:`${c.submissions} submissions`}:{title:`${c.submissions} submissions from ${c.views} views — ${c.conversion}% converted`}})]}))})]})},i=t.some(o=>e[o.key].some(r=>r.conversion!==null));return this.panel("Devices and browsers",i?"Share of submissions, and → the conversion rate for that class. Aggregate tallies — nothing is stored per visitor.":"Share of submissions. Aggregate tallies — nothing is stored per visitor.",[a("div",{class:"atfa-tech",children:t.map(n)})])}breakdownPanel(e){const t=e.groups[0]?.metrics??[];if(!t.length)return a("div");const n=new Map;for(const r of e.groups)for(const l of r.metrics)n.set(l.id,Math.max(n.get(l.id)??0,l.mean));const i=a("tr",{children:[a("th",{text:e.label,attrs:{scope:"col"}}),a("th",{text:"Answers",attrs:{scope:"col"}}),...t.map(r=>a("th",{text:r.label,attrs:{scope:"col"}}))]}),o=e.groups.map(r=>a("tr",{children:[a("th",{class:"atfa-table__label",text:r.label,attrs:{scope:"row"}}),a("td",{class:"atfa-table__count",text:String(r.count)}),...r.metrics.map(l=>a("td",{children:[a("div",{class:"atfa-meter",children:[a("div",{class:"atfa-meter__fill",attrs:{style:`width:${l.mean/(n.get(l.id)||1)*100}%`}})]}),a("span",{class:"atfa-meter__value",text:l.nps!==null?`${l.mean.toFixed(1)} · NPS ${l.nps>0?"+":""}${l.nps}`:l.mean.toFixed(2)})]}))]}));return this.panel(`Broken down by ${e.label.toLowerCase()}`,"",[this.scrollable(a("table",{class:"atfa-table",children:[a("thead",{children:[i]}),a("tbody",{children:o})]}))])}questions(e){return a("div",{class:"atfa-questions",children:e.fields.map(t=>this.question(t,e.sampled))})}question(e,t){const n=[];if(e.choices.length){const i=e.choices.reduce((o,r)=>Math.max(o,r.count),0);n.push(a("ul",{class:"atfa-bars",children:e.choices.map(o=>a("li",{class:"atfa-bars__row",children:[a("span",{class:"atfa-bars__label",text:o.label}),a("div",{class:"atfa-meter",children:[a("div",{class:"atfa-meter__fill",attrs:{style:`width:${i?o.count/i*100:0}%`}})]}),a("span",{class:"atfa-bars__value",text:`${o.count} ${e.answered?Math.round(o.count/e.answered*100):0}%`})]}))}))}return e.numbers&&n.push(this.histogram(e)),n.length||n.push(a("p",{class:"atfa-question__note",text:`${e.answered} of ${e.of??t} ${e.unit??"people"} answered this.`})),this.panel(e.label,this.questionSummary(e,t),n)}questionSummary(e,t){const n=[`${e.rate}% answered`];return e.numbers&&n.push(`mean ${e.numbers.mean}`,`median ${e.numbers.median}`),n.push(`${e.answered}/${e.of??t}`),n.join(" · ")}histogram(e){const t=e.numbers;if(!t)return a("div");const n=L(t),i=n.reduce((o,r)=>Math.max(o,r.count),0);return this.scrollable(a("div",{class:"atfa-hist",children:n.map(o=>a("div",{class:`atfa-hist__col${o.holdsMean?" is-mean":""}`,attrs:{title:`${o.label}: ${o.count}`},children:[a("span",{class:"atfa-hist__count",text:o.count?String(o.count):""}),a("div",{class:"atfa-hist__bar",attrs:{style:`height:${i?Math.max(2,o.count/i*100):0}%`}}),a("span",{class:"atfa-hist__tick",text:o.label})]}))}))}scrollable(e){return a("div",{class:"atfa-scroll",children:[e]})}demoPanel(){const e=this.demo,t=a("p",{class:"atfa-demo__status",text:this.demoSummary(e)}),n=I("Fill with demo data",async()=>{await this.seedAll(t)}),i=I("Remove demo data",async()=>{if(await G("Delete the demo survey and every entry it generated?","Remove demo data")){t.textContent="Removing…";try{const o=await f.demoRemove();x("Demo data removed",`${o.entries} entries and ${o.forms} form.`)}catch{x("Could not remove the demo data","","error")}await this.refreshDemo(t)}});return a("div",{class:"atfa-demo",attrs:{"data-atfa-demo":""},children:[a("h3",{class:"atfa-demo__title",text:"Developer"}),a("p",{class:"atfa-demo__note",text:"Generates a team pulse survey and several hundred submissions through the real submission pipeline, so this report has something to report on. Everything it makes is tagged, and removing it deletes exactly that and nothing else."}),t,a("div",{class:"atfa-demo__actions",children:[n,i]})]})}demoSummary(e){return!e||!e.formId?"No demo data on this site.":`${e.title}: ${e.entries} of ${e.target} submissions.`}async seedAll(e){let t=0,n=0;for(;;){try{const i=await f.demoSeed();if(this.demo=i,n=0,e.textContent=`Generating… ${i.entries} of ${i.target}`,i.remaining<1){x("Demo data ready",`${i.entries} submissions.`);break}}catch{if(n+=1,n>=3){e.textContent="Stopped early. Press it again to carry on from here.",x("Could not finish the demo data","Some submissions were made. Try again to continue.","error");break}}if(t+=1,t>200)break}if(await this.refreshDemo(e),this.demo?.formId){try{this.forms=await f.listForms()}catch{}this.formId=this.demo.formId,this.dimension=""}await this.load()}async refreshDemo(e){try{this.demo=await f.demoStatus()}catch{this.demo=null}e.textContent=this.demoSummary(this.demo)}panel(e,t,n){return a("section",{class:"atfa-panel",children:[a("header",{class:"atfa-panel__head",children:[a("h3",{class:"atfa-panel__title",text:e}),t?a("p",{class:"atfa-panel__note",text:t}):null]}),...n]})}revealDemo(){this.body.querySelector("[data-atfa-demo]")?.scrollIntoView({behavior:"smooth",block:"start"})}}let b=null;document.addEventListener("alltfo-open-analytics-form",s=>{const e=Number(s.detail?.formId??0);e&&b?.showForm(e)});function S(){const s=document.querySelector("[data-atfa-root]");!s||s.dataset.atfaMounted||(s.dataset.atfaMounted="1",W(s),b=new H(s),b.start())}let A=!1;return document.addEventListener("alltfo-open-demo-panel",()=>{A=!0,window.setTimeout(()=>{b?.revealDemo(),A=!1},600)}),document.addEventListener("os-window-content-loaded",()=>{S(),A&&window.setTimeout(()=>b?.revealDemo(),400)}),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",S):S(),p.MAX_HISTOGRAM_BUCKETS=q,p.histogramBuckets=L,p.mountAnalytics=S,Object.defineProperty(p,Symbol.toStringTag,{value:"Module"}),p}({}); +var allTerrainFormsAnalytics=function(p){"use strict";function F(){return window.wp?.os??null}const m=window.allTerrainForms;class y extends Error{constructor(e,t,n=""){super(e),this.name="ApiError",this.status=t,this.code=n}}function T(s,e){return s.includes("?")?`${s}${e.replace("?","&")}`:`${s}${e}`}async function M(s,e={}){if(!m?.restUrl)throw new y("AllTerrain Forms is not configured on this page.",0);const t=T(m.restUrl,s),n={"Content-Type":"application/json",...e.headers??{}};m.nonce&&(n["X-WP-Nonce"]=m.nonce);const i=F(),r=await(i?.fetch?(l,c)=>i.fetch(l,c,{source:"allterrain-forms"}):(l,c)=>fetch(l,c))(t,{credentials:"same-origin",...e,headers:n});if(!r.ok){let l=`Request failed with status ${r.status}.`,c="";try{const h=await r.json();l=h.message??l,c=h.code??""}catch{}throw new y(l,r.status,c)}if(r.status!==204)return await r.json()}const d=s=>M(s);async function D(s){if(!m?.wpRestUrl)throw new y("AllTerrain Forms is not configured on this page.",0);const e=m.nonce?{"X-WP-Nonce":m.nonce}:{},t=F(),n=T(m.wpRestUrl,s),i=t?.fetch?await t.fetch(n,{credentials:"same-origin",headers:e},{source:"allterrain-forms"}):await fetch(n,{credentials:"same-origin",headers:e});if(!i.ok)throw new y(`Request failed with status ${i.status}.`,i.status);return await i.json()}function j(s){if(!s||!s.includes("&"))return s;const e=document.createElement("textarea");return e.innerHTML=s,e.value}const u=(s,e)=>M(s,{method:"POST",body:JSON.stringify(e)}),w=s=>M(s,{method:"DELETE"});function P(s){const e=new URLSearchParams;for(const[n,i]of Object.entries(s))i===void 0||i===""||i===!1||e.set(n,String(i));const t=e.toString();return t?`?${t}`:""}function g(s){const e=s?.schema?.settings;return e&&(!e.themeOverrides||Array.isArray(e.themeOverrides))&&(e.themeOverrides={...e.themeOverrides}),s}const f={config:()=>d("/config"),mailpoet:()=>d("/mailpoet"),listForms:()=>d("/forms"),listArchivedForms:()=>d("/forms?archived=1"),archiveForm:s=>u(`/forms/${s}/archive`,{}),unarchiveForm:s=>u(`/forms/${s}/unarchive`,{}),getForm:s=>d(`/forms/${s}`).then(g),createForm:s=>u("/forms",s).then(g),updateForm:(s,e)=>u(`/forms/${s}`,e).then(g),duplicateForm:s=>u(`/forms/${s}/duplicate`,{}).then(g),deleteForm:s=>w(`/forms/${s}`),preview:(s,e)=>u(`/forms/${s}/preview`,e),pages:()=>D("wp/v2/pages?per_page=100&status=publish&orderby=title&order=asc&_fields=id,title").then(s=>s.map(e=>({id:e.id,title:j(e.title?.rendered??"")||`#${e.id}`}))),mergeTags:s=>d(`/forms/${s}/merge-tags`).then(e=>e.groups),analytics:(s,e="")=>d(`/forms/${s}/analytics${e?`?dimension=${encodeURIComponent(e)}`:""}`),demoStatus:()=>d("/demo"),demoSeed:s=>u("/demo",s?{count:s}:{}),demoRemove:()=>w("/demo"),listEntries:s=>d(`/entries${P(s)}`),getEntry:s=>d(`/entries/${s}`),updateEntry:(s,e)=>u(`/entries/${s}`,e),deleteEntry:s=>w(`/entries/${s}`),exportEntries:s=>d(`/entries/export${P(s)}`),listThemes:()=>d("/themes"),saveTheme:s=>u("/themes",s),deleteTheme:s=>w(`/themes/${s}`)};function C(s){return`allterrain-forms/requested-form-${s}`}function R(s){try{const e=window.sessionStorage.getItem(C(s));return window.sessionStorage.removeItem(C(s)),Number(e)||0}catch{return 0}}function a(s,e={}){const t=document.createElement(s);e.class&&(t.className=e.class),e.text!==void 0&&(t.textContent=e.text),e.html!==void 0&&(t.innerHTML=e.html),e.title&&(t.title=e.title),e.type&&"type"in t&&(t.type=e.type),e.value!==void 0&&"value"in t&&(t.value=e.value),e.placeholder!==void 0&&"placeholder"in t&&(t.placeholder=e.placeholder),e.href&&"href"in t&&(t.href=e.href);for(const[n,i]of Object.entries(e.attrs??{}))i===void 0||i===!1||t.setAttribute(n,i===!0?"":String(i));Object.assign(t.style,e.style??{});for(const[n,i]of Object.entries(e.on??{}))t.addEventListener(n,i);for(const n of e.children??[])n==null||n===!1||t.append(n);return t}function _(s){s.replaceChildren()}function N(s,e,t){if(A("os-select")&&A("os-option")){const n=document.createElement("os-select");n.setAttribute("value",s),n.classList.add("atfb-field");for(const i of e){const o=document.createElement("os-option");o.setAttribute("value",i.value),i.disabled&&o.setAttribute("disabled",""),o.textContent=i.label,n.append(o)}return n.addEventListener("os-pick",i=>{t(String(i.detail?.value??""))}),n}return a("select",{class:"atfb-input atfb-select",on:{change:n=>t(n.target.value)},children:e.map(n=>a("option",{value:n.value,text:n.label,attrs:{selected:n.value===s,disabled:n.disabled}}))})}function I(s,e,t="secondary",n){const i=[null,a("span",{text:s})];if(A("os-button")){const o=document.createElement("os-button");o.setAttribute("variant",t),o.setAttribute("type","button"),o.classList.add("atfb-button",`atfb-button--${t}`),o.addEventListener("click",e);for(const r of i)r&&o.append(r);return o}return a("button",{class:`atfb-button atfb-button--${t}`,type:"button",on:{click:e},children:i})}function A(s){return typeof customElements<"u"&&!!customElements.get(s)}const B=["os-button","os-checkbox-label","os-number-field","os-select","os-option","os-segmented","os-segment","os-color-field","os-empty-state"];let $=null;function U(){if($)return $;const s=window.wp?.os;return $=s?.loadComponents?s.loadComponents(B).catch(()=>{}):Promise.resolve(),$}async function G(s,e=""){const t=window.wp?.os;return t?.confirm?t.confirm({title:e,message:s,danger:!0}):window.confirm(s)}function x(s,e="",t="info"){const n=window.wp?.os;if(n?.notify){n.notify({title:s,body:e,type:t});return}console.info(`[AllTerrain Forms] ${s}${e?`: ${e}`:""}`)}function W(s){const e=s.closest(".os-window__body");e&&(e.addEventListener("scroll",()=>{e.scrollTop&&(e.scrollTop=0),e.scrollLeft&&(e.scrollLeft=0)},{passive:!0}),e.scrollTop=0,e.scrollLeft=0)}const O=[{key:"detractors",label:"Detractors",hint:"0–6"},{key:"passives",label:"Passives",hint:"7–8"},{key:"promoters",label:"Promoters",hint:"9–10"}],q=50;function L(s){const e=Math.floor(s.min),t=Math.ceil(s.max);if(!Number.isFinite(e)||!Number.isFinite(t)||tMath.min(o-1,Math.max(0,Math.floor((h-e)/i))),l=[];for(let h=0;ht.id===e)?this.formId=e:this.formId||(this.formId=this.forms[0].id),await this.load()}async showForm(e){this.formId=e,this.dimension="",await this.load()}async load(){try{this.report=await f.analytics(this.formId,this.dimension)}catch{this.fail("Could not load that report.");return}this.dimension=this.report.breakdown?.id??"",this.renderBar(),this.render()}fail(e){_(this.bar),_(this.body),this.body.append(a("p",{class:"atfa__empty",text:e}))}renderBar(){_(this.bar),this.bar.append(N(String(this.formId),this.forms.map(t=>({value:String(t.id),label:t.title||`#${t.id}`})),t=>{this.formId=Number(t),this.dimension="",this.load()}));const e=this.report?.dimensions??[];e.length>1&&this.bar.append(a("span",{class:"atfa__bar-label",text:"Group by"}),N(this.dimension,e.map(t=>({value:t.id,label:t.label})),t=>{this.dimension=t,this.load()}))}render(){const e=this.report;if(_(this.body),!!e){if(this.body.append(this.kpis(e)),e.submissions<1&&e.sampled<1)this.body.append(a("p",{class:"atfa__empty",text:"No submissions yet. Everything below fills in as they arrive."}));else{this.body.append(this.timeline(e)),e.tech&&this.body.append(this.techPanel(e.tech));for(const t of e.fields)t.nps&&this.body.append(this.npsPanel(t));e.breakdown&&e.breakdown.groups.length&&this.body.append(this.breakdownPanel(e.breakdown)),this.body.append(this.questions(e))}this.demoAvailable&&this.body.append(this.demoPanel())}}kpis(e){const t=[{label:"Submissions",value:String(e.submissions)},{label:"Views",value:String(e.views)},{label:"Conversion",value:`${e.conversion}%`,hint:"of people who saw it"},{label:"Completion",value:`${e.completion}%`,hint:"of people who started"},{label:"Unread",value:String(e.unread)},{label:"Spam",value:String(e.spam)}];return a("div",{class:"atfa-kpis",children:t.map(n=>a("div",{class:"atfa-kpi",children:[a("span",{class:"atfa-kpi__value",text:n.value}),a("span",{class:"atfa-kpi__label",text:n.label}),n.hint?a("span",{class:"atfa-kpi__hint",text:n.hint}):null]}))})}timeline(e){const t=e.timeline,n=t.reduce((o,r)=>Math.max(o,r.count),0),i=t.reduce((o,r)=>o+r.count,0);return this.panel("When they answered",`${i} in the last ${t.length} days · busiest day ${n}`,[a("div",{class:"atfa-timeline",children:t.map(o=>a("div",{class:`atfa-timeline__day${o.count?"":" is-empty"}`,attrs:{style:`height:${n?Math.max(2,o.count/n*100):0}%`,title:`${o.date}: ${o.count}`}}))}),a("div",{class:"atfa-timeline__axis",children:[a("span",{text:t[0]?.date??""}),a("span",{text:t[t.length-1]?.date??""})]})])}npsPanel(e){const t=e.nps;if(!t)return a("div");const n=i=>t.responses?i/t.responses*100:0;return this.panel(e.label,`${t.responses} answers · Net Promoter Score`,[a("div",{class:"atfa-nps",children:[a("div",{class:`atfa-nps__score is-${t.score>=50?"great":t.score>=0?"ok":"poor"}`,children:[a("span",{class:"atfa-nps__number",text:`${t.score>0?"+":""}${t.score}`}),a("span",{class:"atfa-nps__caption",text:"NPS"})]}),a("div",{class:"atfa-nps__detail",children:[a("div",{class:"atfa-nps__bar",children:O.map(i=>a("div",{class:`atfa-nps__band is-${i.key}`,attrs:{style:`width:${n(t[i.key])}%`,title:`${i.label} (${i.hint}): ${t[i.key]}`}}))}),a("div",{class:"atfa-nps__legend",children:O.map(i=>a("span",{class:`atfa-nps__key is-${i.key}`,children:[a("strong",{text:String(t[i.key])}),a("span",{text:` ${i.label} `}),a("small",{text:i.hint})]}))})]})]})])}techPanel(e){const t=[{key:"device",label:"Device"},{key:"browser",label:"Browser"},{key:"os",label:"System"}],n=o=>{const r=e[o.key],l=r.reduce((c,h)=>Math.max(c,h.share),0);return a("div",{class:"atfa-tech__col",children:[a("h4",{class:"atfa-tech__title",text:o.label}),a("ul",{class:"atfa-bars",children:r.map(c=>a("li",{class:"atfa-bars__row",children:[a("span",{class:"atfa-bars__label",text:c.label}),a("div",{class:"atfa-meter",children:[a("div",{class:"atfa-meter__fill",attrs:{style:`width:${l?c.share/l*100:0}%`}})]}),a("span",{class:"atfa-bars__value",text:c.conversion===null?`${c.share}%`:`${c.share}% → ${c.conversion}%`,attrs:c.conversion===null?{title:`${c.submissions} submissions`}:{title:`${c.submissions} submissions from ${c.views} views — ${c.conversion}% converted`}})]}))})]})},i=t.some(o=>e[o.key].some(r=>r.conversion!==null));return this.panel("Devices and browsers",i?"Share of submissions, and → the conversion rate for that class. Aggregate tallies — nothing is stored per visitor.":"Share of submissions. Aggregate tallies — nothing is stored per visitor.",[a("div",{class:"atfa-tech",children:t.map(n)})])}breakdownPanel(e){const t=e.groups[0]?.metrics??[];if(!t.length)return a("div");const n=new Map;for(const r of e.groups)for(const l of r.metrics)n.set(l.id,Math.max(n.get(l.id)??0,l.mean));const i=a("tr",{children:[a("th",{text:e.label,attrs:{scope:"col"}}),a("th",{text:"Answers",attrs:{scope:"col"}}),...t.map(r=>a("th",{text:r.label,attrs:{scope:"col"}}))]}),o=e.groups.map(r=>a("tr",{children:[a("th",{class:"atfa-table__label",text:r.label,attrs:{scope:"row"}}),a("td",{class:"atfa-table__count",text:String(r.count)}),...r.metrics.map(l=>a("td",{children:[a("div",{class:"atfa-meter",children:[a("div",{class:"atfa-meter__fill",attrs:{style:`width:${l.mean/(n.get(l.id)||1)*100}%`}})]}),a("span",{class:"atfa-meter__value",text:l.nps!==null?`${l.mean.toFixed(1)} · NPS ${l.nps>0?"+":""}${l.nps}`:l.mean.toFixed(2)})]}))]}));return this.panel(`Broken down by ${e.label.toLowerCase()}`,"",[this.scrollable(a("table",{class:"atfa-table",children:[a("thead",{children:[i]}),a("tbody",{children:o})]}))])}questions(e){return a("div",{class:"atfa-questions",children:e.fields.map(t=>this.question(t,e.sampled))})}question(e,t){const n=[];if(e.choices.length){const i=e.choices.reduce((o,r)=>Math.max(o,r.count),0);n.push(a("ul",{class:"atfa-bars",children:e.choices.map(o=>a("li",{class:"atfa-bars__row",children:[a("span",{class:"atfa-bars__label",text:o.label}),a("div",{class:"atfa-meter",children:[a("div",{class:"atfa-meter__fill",attrs:{style:`width:${i?o.count/i*100:0}%`}})]}),a("span",{class:"atfa-bars__value",text:`${o.count} ${e.answered?Math.round(o.count/e.answered*100):0}%`})]}))}))}return e.numbers&&n.push(this.histogram(e)),n.length||n.push(a("p",{class:"atfa-question__note",text:`${e.answered} of ${e.of??t} ${e.unit??"people"} answered this.`})),this.panel(e.label,this.questionSummary(e,t),n)}questionSummary(e,t){const n=[`${e.rate}% answered`];return e.numbers&&n.push(`mean ${e.numbers.mean}`,`median ${e.numbers.median}`),n.push(`${e.answered}/${e.of??t}`),n.join(" · ")}histogram(e){const t=e.numbers;if(!t)return a("div");const n=L(t),i=n.reduce((o,r)=>Math.max(o,r.count),0);return this.scrollable(a("div",{class:"atfa-hist",children:n.map(o=>a("div",{class:`atfa-hist__col${o.holdsMean?" is-mean":""}`,attrs:{title:`${o.label}: ${o.count}`},children:[a("span",{class:"atfa-hist__count",text:o.count?String(o.count):""}),a("div",{class:"atfa-hist__bar",attrs:{style:`height:${i?Math.max(2,o.count/i*100):0}%`}}),a("span",{class:"atfa-hist__tick",text:o.label})]}))}))}scrollable(e){return a("div",{class:"atfa-scroll",children:[e]})}demoPanel(){const e=this.demo,t=a("p",{class:"atfa-demo__status",text:this.demoSummary(e)}),n=I("Fill with demo data",async()=>{await this.seedAll(t)}),i=I("Remove demo data",async()=>{if(await G("Delete the demo survey and every entry it generated?","Remove demo data")){t.textContent="Removing…";try{const o=await f.demoRemove();x("Demo data removed",`${o.entries} entries and ${o.forms} form.`)}catch{x("Could not remove the demo data","","error")}await this.refreshDemo(t)}});return a("div",{class:"atfa-demo",attrs:{"data-atfa-demo":""},children:[a("h3",{class:"atfa-demo__title",text:"Developer"}),a("p",{class:"atfa-demo__note",text:"Generates a team pulse survey and several hundred submissions through the real submission pipeline, so this report has something to report on. Everything it makes is tagged, and removing it deletes exactly that and nothing else."}),t,a("div",{class:"atfa-demo__actions",children:[n,i]})]})}demoSummary(e){return!e||!e.formId?"No demo data on this site.":`${e.title}: ${e.entries} of ${e.target} submissions.`}async seedAll(e){let t=0,n=0;for(;;){try{const i=await f.demoSeed();if(this.demo=i,n=0,e.textContent=`Generating… ${i.entries} of ${i.target}`,i.remaining<1){x("Demo data ready",`${i.entries} submissions.`);break}}catch{if(n+=1,n>=3){e.textContent="Stopped early. Press it again to carry on from here.",x("Could not finish the demo data","Some submissions were made. Try again to continue.","error");break}}if(t+=1,t>200)break}if(await this.refreshDemo(e),this.demo?.formId){try{this.forms=await f.listForms()}catch{}this.formId=this.demo.formId,this.dimension=""}await this.load()}async refreshDemo(e){try{this.demo=await f.demoStatus()}catch{this.demo=null}e.textContent=this.demoSummary(this.demo)}panel(e,t,n){return a("section",{class:"atfa-panel",children:[a("header",{class:"atfa-panel__head",children:[a("h3",{class:"atfa-panel__title",text:e}),t?a("p",{class:"atfa-panel__note",text:t}):null]}),...n]})}revealDemo(){this.body.querySelector("[data-atfa-demo]")?.scrollIntoView({behavior:"smooth",block:"start"})}}let b=null;document.addEventListener("alltfo-open-analytics-form",s=>{const e=Number(s.detail?.formId??0);e&&b?.showForm(e)});function S(){const s=document.querySelector("[data-atfa-root]");!s||s.dataset.atfaMounted||(s.dataset.atfaMounted="1",W(s),b=new H(s),b.start())}let E=!1;return document.addEventListener("alltfo-open-demo-panel",()=>{E=!0,window.setTimeout(()=>{b?.revealDemo(),E=!1},600)}),document.addEventListener("os-window-content-loaded",()=>{S(),E&&window.setTimeout(()=>b?.revealDemo(),400)}),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",S):S(),p.MAX_HISTOGRAM_BUCKETS=q,p.histogramBuckets=L,p.mountAnalytics=S,Object.defineProperty(p,Symbol.toStringTag,{value:"Module"}),p}({}); diff --git a/assets/js/builder.js b/assets/js/builder.js index 1f1e1c4..bd47520 100644 --- a/assets/js/builder.js +++ b/assets/js/builder.js @@ -437,6 +437,50 @@ var allTerrainFormsBuilder = function(exports) { saveTheme: (body) => post("/themes", body), deleteTheme: (id) => del(`/themes/${id}`) }; + function conditionValueOptions(field, current, countries = {}) { + if (!field) return null; + let options; + const number = (key, fallback2) => { + const value = field[key]; + return value === void 0 || value === null || !Number.isFinite(Number(value)) ? fallback2 : Number(value); + }; + const sequence = (min, max, step = 1) => Array.from( + { length: Math.floor((max - min) / step + 1e-9) + 1 }, + (_, index) => { + const value = String(Number((min + index * step).toPrecision(12))); + return { value, label: value }; + } + ); + if (field.type === "scale") { + const min = Math.trunc(number("min", 0)); + let max = Math.trunc(number("max", 10)); + if (max <= min) max = min + 10; + options = sequence(min, Math.min(max, min + 20)); + } else if (field.type === "rating") { + options = sequence(1, Math.max(2, Math.min(10, Math.abs(Math.trunc(number("max", 5)))))); + } else if (field.type === "switch" || field.type === "consent") { + options = [{ value: "1", label: "Checked" }, { value: "", label: "Unchecked" }]; + } else if (field.type === "country") { + options = Object.entries(countries).map(([value, label]) => ({ value, label })); + } else if (field.type === "range") { + const min = number("min", 0); + const max = number("max", 100); + const step = field.step === "any" ? 0 : number("step", 1); + if (step <= 0 || max < min || (max - min) / step > 999) return null; + options = sequence(min, max, step); + } else if (field.choices?.length || ["select", "multiselect", "radio", "checkboxes", "image_choice", "quiz", "likert"].includes(field.type)) { + options = (field.choices ?? []).map((choice) => ({ value: choice.value, label: choice.label || choice.value })); + } else { + return null; + } + if (!options.some((option) => option.value === current) && current !== "") { + options.unshift({ value: current, label: `${current} (stored value; unavailable)`, disabled: true }); + } + if (!options.some((option) => option.value === "")) { + options.unshift({ value: "", label: "Choose a value…", disabled: true }); + } + return options; + } function el(tag, options = {}) { const node = document.createElement(tag); if (options.class) { @@ -562,6 +606,7 @@ var allTerrainFormsBuilder = function(exports) { for (const option of options) { const item = document.createElement("os-option"); item.setAttribute("value", option.value); + if (option.disabled) item.setAttribute("disabled", ""); item.textContent = option.label; host.append(item); } @@ -579,7 +624,7 @@ var allTerrainFormsBuilder = function(exports) { (option) => el("option", { value: option.value, text: option.label, - attrs: { selected: option.value === value } + attrs: { selected: option.value === value, disabled: option.disabled } }) ) }); @@ -1582,16 +1627,23 @@ var allTerrainFormsBuilder = function(exports) { const all = flatten(groups); return text.replace(/\{[a-z_]+(?::[^}]*)?\}/gi, (match) => { const known = all.get(match.toLowerCase()); - return known ? known.sample : match; + return known ? `{the value of ${known.label}}` : match; }); } function hasTags(text) { return /\{[a-z_]+(?::[^}]*)?\}/i.test(text); } let openPicker = null; - function closePicker() { + let pickerRequest = 0; + let pickerReturnFocus = null; + function closePicker(restoreFocus = false) { + pickerRequest++; openPicker?.remove(); openPicker = null; + if (restoreFocus) { + pickerReturnFocus?.focus(); + } + pickerReturnFocus = null; } if (typeof document !== "undefined") { document.addEventListener("pointerdown", (event) => { @@ -1599,16 +1651,17 @@ var allTerrainFormsBuilder = function(exports) { if (target?.closest(".atfb-tagpick__open")) { return; } - if (openPicker && !openPicker.contains(target)) { + if (!openPicker?.contains(target)) { closePicker(); } }); document.addEventListener("keydown", (event) => { - if ("Escape" === event.key && openPicker) { - closePicker(); - event.stopPropagation(); + if ("Escape" === event.key && pickerReturnFocus) { + closePicker(true); + event.preventDefault(); + event.stopImmediatePropagation(); } - }); + }, true); } function insertAtCursor(field, text) { const start = field.selectionStart ?? field.value.length; @@ -1616,19 +1669,22 @@ var allTerrainFormsBuilder = function(exports) { field.value = field.value.slice(0, start) + text + field.value.slice(end); const caret = start + text.length; field.setSelectionRange(caret, caret); - field.focus(); field.dispatchEvent(new Event("input", { bubbles: true })); + field.focus(); } - function clipBottom(from) { + function pickerBounds(from) { + let top = 0; + let bottom = window.innerHeight; let node = from.parentElement; while (node && node !== document.body) { - const overflow = getComputedStyle(node).overflowY; - if ("auto" === overflow || "scroll" === overflow || "hidden" === overflow) { - return node.getBoundingClientRect().bottom; + if (/auto|scroll|hidden|clip/.test(getComputedStyle(node).overflowY)) { + const rect = node.getBoundingClientRect(); + top = Math.max(top, rect.top); + bottom = Math.min(bottom, rect.bottom); } node = node.parentElement; } - return window.innerHeight; + return { top, bottom }; } function buildPicker(groups, onPick) { const search = el("input", { @@ -1664,8 +1720,8 @@ var allTerrainFormsBuilder = function(exports) { type: "button", on: { click: () => { - onPick(item.tag); closePicker(); + onPick(item.tag); } }, children: [ @@ -1678,9 +1734,7 @@ var allTerrainFormsBuilder = function(exports) { }), item.hint || item.sample ? el("span", { class: "atfb-tagpick__meta", - // The sample is the part people actually read, so it - // leads; the hint explains the cases where it is empty. - text: item.sample ? `e.g. ${item.sample}` : item.hint + text: `{the value of ${item.label}}` }) : null ] }) @@ -1693,7 +1747,7 @@ var allTerrainFormsBuilder = function(exports) { }; paint(""); search.addEventListener("input", () => paint(search.value)); - return el("div", { + const picker = el("div", { class: "atfb-tagpick", attrs: { role: "dialog", "aria-label": "Insert a value" }, children: [ @@ -1705,12 +1759,28 @@ var allTerrainFormsBuilder = function(exports) { list ] }); + picker.addEventListener("keydown", (event) => { + const items = [...list.querySelectorAll("button")]; + const index = items.indexOf(document.activeElement); + if (event.key === "ArrowDown" || event.key === "ArrowUp") { + event.preventDefault(); + const next = event.key === "ArrowDown" ? index + 1 : index < 0 ? items.length - 1 : index - 1; + items[(next + items.length) % items.length]?.focus(); + } else if (event.key === "Enter" && event.target === search) { + event.preventDefault(); + items[0]?.click(); + } + event.stopPropagation(); + }); + return picker; } function taggable(field, options) { + const catalogue = () => Promise.resolve(options.groups ? options.groups() : mergeTags(options.formId ?? 0)); const insert = el("button", { class: "atfb-button atfb-button--ghost atfb-tagpick__open", type: "button", title: "Insert a value from the submission", + attrs: { "aria-haspopup": "dialog" }, children: [icon("shortcode"), el("span", { text: "Insert a value" })] }); const wrapper = el("div", { @@ -1730,7 +1800,10 @@ var allTerrainFormsBuilder = function(exports) { preview.hidden = true; return; } - void mergeTags(options.formId).then((groups) => { + void catalogue().then((groups) => { + if (!hasTags(field.value)) { + return; + } preview.hidden = false; preview.replaceChildren( el("span", { class: "atfb-taggable__preview-label", text: "Reads as" }), @@ -1740,27 +1813,45 @@ var allTerrainFormsBuilder = function(exports) { }; field.addEventListener("input", repaint); repaint(); - insert.addEventListener("click", (event) => { - event.stopPropagation(); - if (openPicker && wrapper.contains(openPicker)) { - closePicker(); - return; - } + const open = (start, end) => { closePicker(); - void mergeTags(options.formId).then((groups) => { + const request2 = pickerRequest; + const original = field.value; + pickerReturnFocus = field; + void catalogue().then((groups) => { + if (request2 !== pickerRequest || !wrapper.isConnected || field.value !== original) { + return; + } const picker = buildPicker(groups, (tag) => { + field.setSelectionRange(start, end); insertAtCursor(field, tag); - repaint(); }); wrapper.append(picker); openPicker = picker; - if (window.innerHeight - picker.getBoundingClientRect().top < picker.offsetHeight) { - picker.classList.add("atfb-tagpick--above"); - } else if (picker.getBoundingClientRect().bottom > clipBottom(wrapper)) { - picker.classList.add("atfb-tagpick--above"); - } - picker.querySelector(".atfb-tagpick__search")?.focus(); + const bounds = pickerBounds(wrapper); + picker.style.maxBlockSize = `${Math.max(0, Math.min(320, bounds.bottom - bounds.top - 8))}px`; + const anchor = wrapper.getBoundingClientRect(); + const height = picker.getBoundingClientRect().height; + const preferred = anchor.bottom + height <= bounds.bottom ? anchor.bottom : anchor.top - height; + const top = Math.max(bounds.top + 4, Math.min(preferred, bounds.bottom - height - 4)); + picker.style.insetBlockStart = `${top - anchor.top}px`; + picker.querySelector(".atfb-tagpick__search")?.focus({ preventScroll: true }); }); + }; + insert.addEventListener("click", (event) => { + event.stopPropagation(); + if (pickerReturnFocus === field) { + closePicker(true); + return; + } + open(field.selectionStart ?? field.value.length, field.selectionEnd ?? field.value.length); + }); + field.addEventListener("input", (event) => { + const typed = event; + const caret = field.selectionStart ?? 0; + if (!typed.isComposing && typed.data === "{" && field.value[caret - 1] === "{") { + open(caret - 1, caret + (field.value[caret] === "}" ? 1 : 0)); + } }); return wrapper; } @@ -3749,6 +3840,30 @@ var allTerrainFormsBuilder = function(exports) { } return values; } + function formulaInput(input, fields, except) { + return taggable(input, { + preview: false, + groups: () => [{ + id: "references", + label: "Your questions", + items: [ + ...formulaTargets(fields, except).map((field) => ({ + label: field.label || field.id, + tag: `{${field.id}}`, + sample: "", + hint: "" + })), + ...repeaterReferences(fields.filter((field) => field.id !== except)).map((ref) => ({ + label: ref.label, + tag: ref.insert, + sample: "", + hint: "" + })) + ], + empty: "Add a number, scale or priced choice question to reference it here." + }] + }); + } function openFormulaEditor(options) { const overlay = el("div", { class: "atfb-overlay" }); const close = () => { @@ -3802,7 +3917,7 @@ var allTerrainFormsBuilder = function(exports) { } }); const targets = formulaTargets(options.fields, options.field.id); - const repeaters = repeaterReferences(options.fields); + const repeaters = repeaterReferences(options.fields.filter((field) => field.id !== options.field.id)); const questions = el("div", { class: "atfb-formula__chips", children: targets.length || repeaters.length ? [ @@ -3820,7 +3935,7 @@ var allTerrainFormsBuilder = function(exports) { attrs: { role: "dialog", "aria-label": "Formula editor" }, children: [ el("h2", { text: "Formula" }), - input, + formulaInput(input, options.fields, options.field.id), result, row("Your questions", questions, "Click one to reference its answer."), row("Functions", functions), @@ -3852,6 +3967,118 @@ var allTerrainFormsBuilder = function(exports) { input.focus(); input.setSelectionRange(input.value.length, input.value.length); } + function conditionSections(schema) { + return [ + ...schema.fields.filter((field) => field.type !== "page_break").map((field) => ({ + key: `field:${field.id}`, + label: `Field: ${field.label || field.id}`, + fieldId: field.id, + logic: field.logic + })), + ...schema.notifications.map((item) => ({ + key: `notification:${item.id}`, + label: `Notification: ${item.name || item.id}`, + logic: item.logic + })), + ...schema.confirmations.map((item) => ({ + key: `confirmation:${item.id}`, + label: `Confirmation: ${item.name || item.id}`, + logic: item.logic + })) + ]; + } + function canCopyCondition(source, target, schema) { + return source.key !== target.key && source.logic.rules.length > 0 && source.logic.rules.every( + (rule) => rule.field !== target.fieldId && schema.fields.some((field) => field.id === rule.field && field.type !== "page_break") + ); + } + function copyCondition(schema, from, to, beforeCopy) { + const sections = conditionSections(schema); + const source = sections.find((item) => item.key === from); + const target = sections.find((item) => item.key === to); + if (!source || !target || !canCopyCondition(source, target, schema)) { + return false; + } + beforeCopy?.(); + Object.assign(target.logic, source.logic, { rules: source.logic.rules.map((rule) => ({ ...rule })) }); + return true; + } + function openConditionCopy(options) { + const previous = document.activeElement; + const overlay = el("div", { class: "atfb-overlay" }); + const dialog = el("div", { + class: "atfb-modal atfb-condition-copy", + attrs: { role: "dialog", "aria-modal": "true", "aria-label": "Copy condition", tabindex: "-1" } + }); + let from = ""; + const to = options.to; + const close = () => { + overlay.remove(); + previous?.focus(); + }; + const paint = () => { + const schema = options.schema(); + if (!schema) { + close(); + return; + } + const sections = conditionSections(schema); + const target = sections.find((item) => item.key === to); + const sources = sections.filter((item) => item.logic.rules.length && target && canCopyCondition(item, target, schema)); + const source = sources.find((item) => item.key === from); + const sourcePicker = select(from, [{ value: "", label: "Choose a section…" }, ...sources.map((item) => ({ value: item.key, label: item.label }))], (value) => { + from = value; + paint(); + dialog.querySelector('[aria-label="Copy from"]')?.focus(); + }); + sourcePicker.setAttribute("aria-label", "Copy from"); + const apply = button("Copy condition", () => { + const live = options.schema(); + if (live && copyCondition(live, from, to, options.beforeCopy)) { + close(); + options.onCopy(); + } + }, "primary"); + if (!source || !to) { + apply.setAttribute("disabled", ""); + } + const destination = sections.find((item) => item.key === to); + dialog.replaceChildren( + el("h2", { text: "Copy condition" }), + row("Copy from", sourcePicker), + el("p", { class: "atfb-hint", text: `Apply to ${destination?.label ?? "this section"}.` }), + el("p", { class: "atfb-condition-copy__preview", attrs: { "aria-live": "polite" }, text: source ? `${source.logic.enabled ? "Enabled" : "Disabled"} · ${source.logic.action === "hide" ? "Hide" : "Show"} · Match ${source.logic.match}: ` + source.logic.rules.map((rule) => tokensToText(ruleTokens(rule, schema.fields))).join(source.logic.match === "all" ? " and " : " or ") : sources.length ? "Choose the section whose condition you want to reuse." : "No conditions are available to copy here yet." }), + el("p", { class: "atfb-hint", text: destination?.logic.rules.length ? "This replaces the destination’s entire condition. You can edit the copy independently." : "Copies every rule, all/any matching, show/hide and enabled state. You can edit the copy independently." }), + el("div", { class: "atfb-modal__actions", children: [button("Cancel", close), apply] }) + ); + }; + overlay.append(dialog); + overlay.addEventListener("click", (event) => { + if (event.target === overlay) close(); + }); + overlay.addEventListener("keydown", (event) => { + if (event.key === "Escape") { + event.preventDefault(); + close(); + } + if (event.key === "Tab") { + const controls = [...dialog.querySelectorAll("button:not([disabled]), select, os-select, os-button:not([disabled])")]; + const first = controls[0]; + const last = controls[controls.length - 1]; + if (event.shiftKey && (document.activeElement === first || document.activeElement === dialog)) { + event.preventDefault(); + last?.focus(); + } else if (!event.shiftKey && document.activeElement === last) { + event.preventDefault(); + first?.focus(); + } + } + event.stopPropagation(); + }); + options.root.append(overlay); + paint(); + dialog.focus(); + } const FORM_TYPE = "allterrain-forms/form"; function relations() { const os = window.wp?.os; @@ -5010,7 +5237,11 @@ var allTerrainFormsBuilder = function(exports) { el("div", { class: "atfb-formula__row", children: [ - textInput(String(field.formula ?? ""), (value) => update("formula", value)), + formulaInput( + textInput(String(field.formula ?? ""), (value) => update("formula", value)), + this.schema?.fields ?? [], + field.id + ), // The editor is where the formula is meant to be // written: the questions and the functions are // buttons there, and the result computes live @@ -5021,7 +5252,7 @@ var allTerrainFormsBuilder = function(exports) { () => openFormulaEditor({ root: this.root, fields: this.schema?.fields ?? [], - field, + field: this.liveField(field.id) ?? field, onSave: (formula) => { update("formula", formula); this.renderInspector(); @@ -5336,7 +5567,11 @@ var allTerrainFormsBuilder = function(exports) { } const focused = document.activeElement; if (focused instanceof HTMLElement && this.canvas.contains(focused)) { - focused.addEventListener("blur", () => this.rebindCanvas(), { once: true }); + focused.addEventListener("blur", () => queueMicrotask(() => this.rebindCanvas()), { once: true }); + return; + } + if (this.canvas.matches(":hover")) { + this.canvas.addEventListener("pointerleave", () => this.rebindCanvas(), { once: true }); return; } this.renderCanvas(); @@ -6056,19 +6291,70 @@ var allTerrainFormsBuilder = function(exports) { * because a screen reader reading five chips as five unrelated fragments * would be worse off than before. * - * The question chip is a button that selects that field — the reference is - * the useful kind, the kind you can follow. + * Questions, comparisons and answers are editable in place. Each rule has + * its own delete control; adding or clearing rules stays on the canvas. */ renderCondition(owner, tokens) { const broken = tokens.some((token) => "field" === token.kind && token.missing); - const wrap = el("span", { + const wrap = el("div", { class: `atfb-cond${broken ? " is-broken" : ""}`, - attrs: { "aria-label": tokensToText(tokens) }, - children: [ - icon("randomize"), - ...tokens.map((token) => this.renderConditionToken(owner, token)) - ] + attrs: { "aria-label": tokens.length ? tokensToText(tokens) : "Conditional rules" } + }); + wrap.append(el("div", { class: "atfb-cond__heading", children: [ + el("span", { class: "atfb-cond__label", children: [icon("randomize"), "Conditions"] }), + this.copyConditionButton(`field:${owner.id}`) + ] })); + owner.logic.rules.forEach((rule, index) => { + const parts = ruleTokens(rule, this.schema?.fields ?? [], index); + const remove = el("button", { + class: "atfb-cond__delete", + type: "button", + title: "Delete this rule", + attrs: { "aria-label": `Delete rule ${index + 1}` }, + children: [icon("trash")], + on: { click: () => { + this.snapshot(); + this.editCondition(owner.id, (logic) => { + logic.rules.splice(index, 1); + }, true, `${owner.id}:add`); + this.snapshot(); + } } + }); + wrap.append(el("div", { + class: "atfb-cond__rule", + children: [ + index === 0 ? this.renderConditionToken(owner, { kind: "verb", text: owner.logic.action === "hide" ? "Hidden when" : "Shown when" }) : this.renderConditionToken(owner, { kind: "join", text: owner.logic.match === "all" ? "and" : "or" }), + ...parts.map((token) => this.renderConditionToken(owner, token, index)), + remove + ] + })); + }); + if (!owner.logic.rules.length) { + wrap.append(el("span", { class: "atfb-hint", text: "No rules yet. Add one or copy a condition." })); + } + const add = el("button", { + class: "atfb-cond__add", + type: "button", + text: "+ Add rule", + title: "Add rule", + attrs: { "aria-label": "Add rule", "data-cond": `${owner.id}:add` }, + on: { click: () => this.addConditionRule(owner.id) } + }); + const clear2 = el("button", { + class: "atfb-cond__clear", + type: "button", + text: "Clear", + title: "Delete all rules", + attrs: { "aria-label": "Clear all rules", disabled: !owner.logic.rules.length }, + on: { click: () => { + this.snapshot(); + this.editCondition(owner.id, (logic) => { + logic.rules = []; + }, true, `${owner.id}:add`); + this.snapshot(); + } } }); + wrap.append(el("div", { class: "atfb-cond__actions", children: [add, clear2] })); wrap.addEventListener("pointerdown", (event) => event.stopPropagation()); wrap.addEventListener("click", (event) => event.stopPropagation()); wrap.addEventListener("keydown", (event) => event.stopPropagation()); @@ -6125,10 +6411,12 @@ var allTerrainFormsBuilder = function(exports) { host.setAttribute("aria-label", label); host.setAttribute("data-cond", key); host.className = "atfb-cond__control"; + host.setAttribute("plain", ""); host.title = label; for (const option of options) { const item = document.createElement("os-option"); item.setAttribute("value", option.value); + if (option.disabled) item.setAttribute("disabled", ""); item.textContent = option.label; host.append(item); } @@ -6145,7 +6433,7 @@ var allTerrainFormsBuilder = function(exports) { change: (event) => onChange(event.target.value) }, children: options.map( - (option) => el("option", { value: option.value, text: option.label, attrs: { selected: option.value === value } }) + (option) => el("option", { value: option.value, text: option.label, attrs: { selected: option.value === value, disabled: option.disabled } }) ) }); } @@ -6159,25 +6447,20 @@ var allTerrainFormsBuilder = function(exports) { * small select, the answer is an input (or a select of the source field's * choices), and "and"/"or" toggles how rules combine. */ - renderConditionToken(owner, token) { - if ("field" === token.kind && !token.missing) { - const chip = el("button", { - class: "atfb-cond__chip atfb-cond__chip--field", - type: "button", - text: token.text, - title: "Go to this question", - // The row is inside a card that is itself a button; without this the - // click selects the card the chip is *on* rather than the question it - // names, which is the opposite of what it offers. - on: { - click: (event) => { - event.stopPropagation(); - this.selectField(token.fieldId); - this.canvas.querySelector(`[data-atfb-card="${CSS.escape(token.fieldId)}"]`)?.scrollIntoView({ block: "nearest", behavior: "smooth" }); - } + renderConditionToken(owner, token, ruleIndex = 0) { + if ("field" === token.kind) { + const key = `${owner.id}:source:${ruleIndex}`; + const choices = (this.schema?.fields ?? []).filter((field) => field.id !== owner.id && field.type !== "page_break"); + return this.condSelect(token.fieldId, [ + ...token.missing ? [{ value: token.fieldId, label: "Choose a question…" }] : [], + ...choices.map((field) => ({ value: field.id, label: field.label || field.id })) + ], key, "Question used by this rule", (value) => this.editCondition(owner.id, (logic) => { + const rule = logic.rules[ruleIndex]; + if (rule) { + rule.field = value; + rule.value = ""; } - }); - return chip; + }, true, key)); } if ("verb" === token.kind) { return el("button", { @@ -6240,14 +6523,14 @@ var allTerrainFormsBuilder = function(exports) { if ("value" === token.kind) { return this.renderConditionValue(owner, token); } - return el("span", { class: "atfb-cond__chip atfb-cond__chip--missing", text: token.text }); + return el("span"); } /** * The answer half of a condition, as the control it deserves. * * When the question being consulted has choices, the honest editor is a * select of those choices — typing free text against a radio group can only - * produce a rule that never matches. Anything else gets a text box, sized to + * produce a rule that never matches. Scales, ratings and other finite answer sets also get selectors. Open-ended answers get a text box, sized to * its content so it reads as part of the sentence rather than as a form. */ renderConditionValue(owner, token) { @@ -6264,21 +6547,12 @@ var allTerrainFormsBuilder = function(exports) { rebuild, key ); - if (source?.choices?.length) { - const options = source.choices.map((choice) => ({ - value: choice.value, - label: choice.label || choice.value - })); - if (token.raw !== "" && !source.choices.some((choice) => choice.value === token.raw)) { - options.unshift({ value: token.raw, label: token.text }); - } - return this.condSelect( - token.raw, - options, - key, - "The answer that triggers this", - (picked) => write(picked, true) - ); + const options = conditionValueOptions(source, token.raw, this.config?.countries); + if (options) { + const picker = this.condSelect(token.raw, options, key, "The answer that triggers this", (picked) => write(picked, true)); + picker.classList.add("atfb-cond__value-select"); + picker.removeAttribute("plain"); + return picker; } const numeric = ["number", "range", "scale", "rating", "total"].includes(source?.type ?? ""); const input = el("input", { @@ -6426,6 +6700,7 @@ var allTerrainFormsBuilder = function(exports) { icon(type?.icon ?? "dashicons-forms"), el("span", { class: "atfb-card__type", text: type?.label ?? field.type }), this.requiredToggle(field), + field.type !== "page_break" ? this.conditionToolbar(field) : null, controls ? el("span", { class: "atfb-badge atfb-badge--controls", text: 1 === controls ? "controls 1 field" : `controls ${controls} fields`, @@ -6482,7 +6757,7 @@ var allTerrainFormsBuilder = function(exports) { types: (name) => this.config?.fieldTypes.find((candidate) => candidate.type === name), selectedId: this.selected }), - condition.length ? this.renderCondition(field, condition) : null + field.logic.enabled ? this.renderCondition(field, condition) : null ] }) ] @@ -6525,6 +6800,149 @@ var allTerrainFormsBuilder = function(exports) { }); return card; } + /** Adds a rule without leaving the canvas. */ + addConditionRule(fieldId) { + this.snapshot(); + this.editCondition(fieldId, (logic) => { + const source = this.schema?.fields.find((field) => field.id !== fieldId && field.type !== "page_break"); + logic.rules.push({ field: source?.id ?? "", operator: "is", value: "" }); + }, true, `${fieldId}:source:${this.liveField(fieldId)?.logic.rules.length ?? 0}`); + this.snapshot(); + } + /** A compact title-bar entry point; the settings live in a dialog. */ + conditionToolbar(field) { + const trigger = el("button", { + class: `atfb-req atfb-condition-toggle${field.logic.enabled ? " is-on" : ""}`, + type: "button", + title: field.logic.enabled ? "Edit conditional logic" : "Set up conditional logic", + attrs: { "aria-haspopup": "dialog", "data-cond": `${field.id}:enabled` }, + children: [ + el("span", { class: "atfb-condition-dot", attrs: { "aria-hidden": "true" } }), + el("span", { text: "Conditional" }) + ], + on: { click: () => this.openConditionEditor(field.id) } + }); + for (const name of ["pointerdown", "click", "keydown"]) { + trigger.addEventListener(name, (event) => event.stopPropagation()); + } + return trigger; + } + /** Edits a draft, so Cancel leaves the saved and inline conditions alone. */ + openConditionEditor(fieldId) { + const field = this.liveField(fieldId); + if (!field) { + return; + } + const draft = { ...field.logic, rules: field.logic.rules.map((rule) => ({ ...rule })) }; + const overlay = el("div", { class: "atfb-overlay" }); + const dialog = el("div", { + class: "atfb-modal atfb-condition-editor", + attrs: { role: "dialog", "aria-modal": "true", "aria-label": "Conditional logic", tabindex: "-1" } + }); + const close = () => { + overlay.remove(); + this.root.querySelector(`[data-cond="${CSS.escape(fieldId)}:enabled"]`)?.focus(); + }; + const controlsSelector = "button:not([disabled]), input, select, os-select, os-checkbox-label, os-button:not([disabled])"; + const paint = () => { + const focusedIndex = [...dialog.querySelectorAll(controlsSelector)].indexOf(document.activeElement); + const write = (mutate, rebuild = false) => { + mutate(draft); + if (rebuild) { + paint(); + } + }; + const action = select(draft.action, [{ value: "show", label: "Show" }, { value: "hide", label: "Hide" }], (value) => { + draft.action = value; + }); + action.setAttribute("aria-label", "Conditional action"); + const match = select(draft.match, [{ value: "all", label: "all" }, { value: "any", label: "any" }], (value) => { + draft.match = value; + paint(); + }); + match.setAttribute("aria-label", "Match rules"); + const copy = el("button", { + class: "atfb-copy-condition", + type: "button", + children: [icon("admin-page"), el("span", { text: "Copy condition" })], + on: { click: () => openConditionCopy({ + root: overlay, + to: `field:${fieldId}`, + schema: () => this.schema ? { + ...this.schema, + fields: this.schema.fields.map((item) => item.id === fieldId ? { ...item, logic: draft } : item) + } : null, + onCopy: () => { + paint(); + dialog.focus(); + } + }) } + }); + dialog.replaceChildren( + el("div", { class: "atfb-condition-editor__heading", children: [ + el("span", { class: "atfb-condition-editor__icon", children: [icon("randomize")] }), + el("div", { children: [el("h2", { text: "Conditional logic" }), el("p", { text: field.label || "Untitled field" })] }) + ] }), + el("div", { class: "atfb-condition-editor__switch", children: [ + checkbox("Enable conditions", draft.enabled, (enabled) => { + draft.enabled = enabled; + paint(); + }), + el("p", { text: "Choose when this field appears in your form." }) + ] }), + ...draft.enabled ? [ + el("div", { class: "atfb-condition-editor__sentence", children: [action, "this field when", match, "of these rules match:"] }), + ...this.logicRulesEditor(draft, write, fieldId) + ] : [el("p", { class: "atfb-condition-editor__empty", text: "This field is always visible. Enable conditions to choose when to show or hide it." })], + el("div", { class: "atfb-condition-editor__footer", children: [ + copy, + el("div", { class: "atfb-modal__actions", children: [ + button("Cancel", close), + button("Save conditions", () => { + if (!this.liveField(fieldId)) { + close(); + return; + } + this.snapshot(); + this.editCondition(fieldId, (live) => Object.assign(live, draft, { rules: draft.rules.map((rule) => ({ ...rule })) })); + this.snapshot(); + close(); + }, "primary") + ] }) + ] }) + ); + if (focusedIndex >= 0) { + const controls = dialog.querySelectorAll(controlsSelector); + controls[Math.min(focusedIndex, controls.length - 1)]?.focus(); + } + }; + overlay.append(dialog); + overlay.addEventListener("click", (event) => { + if (event.target === overlay) close(); + }); + overlay.addEventListener("keydown", (event) => { + if (event.key === "Escape") { + event.preventDefault(); + close(); + } + if (event.key === "Tab") { + const controls = [...dialog.querySelectorAll(controlsSelector)]; + const first = controls[0]; + const last = controls[controls.length - 1]; + if (event.shiftKey && (document.activeElement === first || document.activeElement === dialog)) { + event.preventDefault(); + last?.focus(); + } else if (!event.shiftKey && document.activeElement === last) { + event.preventDefault(); + first?.focus(); + } + } + event.stopPropagation(); + }); + this.root.append(overlay); + paint(); + dialog.focus(); + } /** * The required flag, as a toggle on the card rather than a badge. * @@ -7789,14 +8207,8 @@ var allTerrainFormsBuilder = function(exports) { ]; if (!VALUELESS_OPERATORS.includes(rule.operator)) { const source = this.schema?.fields.find((candidate) => candidate.id === rule.field); - if (source?.choices?.length) { - const options = source.choices.map((choice) => ({ - value: choice.value, - label: choice.label || choice.value - })); - if ("" !== rule.value && !source.choices.some((choice) => choice.value === rule.value)) { - options.unshift({ value: rule.value, label: rule.value }); - } + const options = conditionValueOptions(source, rule.value, this.config?.countries); + if (options) { children.push( select( rule.value, @@ -7824,6 +8236,10 @@ var allTerrainFormsBuilder = function(exports) { ); } } + children[0].querySelector("select, os-select")?.setAttribute("aria-label", "Question used by this rule"); + children[1].setAttribute("aria-label", "How the answer is compared"); + children[2]?.setAttribute("aria-label", "The answer that triggers this"); + if (children[2]?.matches("select, os-select")) children[2].classList.add("atfb-cond__value-select"); return el("div", { class: "atfb-rule", children }); }; const rules = el("div", { class: "atfb-rules" }); @@ -7876,6 +8292,7 @@ var allTerrainFormsBuilder = function(exports) { `conditions:${key}`, "Conditions", [ + this.copyConditionButton(`${noun}:${key}`), checkbox( `Only ${verb.toLowerCase()} this ${noun} sometimes`, logic.enabled, @@ -7911,6 +8328,26 @@ var allTerrainFormsBuilder = function(exports) { logic.enabled ); } + /** Reuse a condition, resolving the schema when the chooser applies it. */ + copyConditionButton(key) { + return el("button", { + class: "atfb-copy-condition", + type: "button", + children: [icon("admin-page"), el("span", { text: "Copy condition" })], + on: { click: () => openConditionCopy({ + root: this.root, + schema: () => this.schema, + to: key, + beforeCopy: () => this.snapshot(), + onCopy: () => { + this.snapshot(); + this.markDirty(); + this.renderCanvas(); + this.renderInspector(); + } + }) } + }); + } /** The conditional-logic editor. */ renderLogicSection(field) { const logic = field.logic; @@ -7932,6 +8369,7 @@ var allTerrainFormsBuilder = function(exports) { `logic:${field.id}`, "Conditional logic", [ + this.copyConditionButton(`field:${field.id}`), checkbox("Only show this field sometimes", logic.enabled, (value) => { write((live) => { live.enabled = value; @@ -8383,6 +8821,19 @@ ${decls} this.renderCanvas(); this.renderInspector(); } + /** Tag inputs can remain focused across autosaves; always write to the live section. */ + writeTagValue(kind, id, key, value) { + const section = kind === "notification" ? this.schema?.notifications.find((item) => item.id === id) : this.schema?.confirmations.find((item) => item.id === id); + if (!section) { + return; + } + if (key === "successTitle" && "success" in section) { + section.success.title = value; + } else { + Object.assign(section, { [key]: value }); + } + this.markDirty(); + } /** A one-line input that understands merge tags. */ taggableInput(value, onChange, placeholder = "") { return taggable(textInput(value, onChange, placeholder), { formId: this.form.id }); @@ -8441,8 +8892,7 @@ ${decls} textInput( /\{/.test(notification.to) ? "" : notification.to, (value) => { - notification.to = value; - this.markDirty(); + this.writeTagValue("notification", notification.id, "to", value); }, "name@example.com" ) @@ -8454,8 +8904,7 @@ ${decls} this.taggableInput( notification.to, (value) => { - notification.to = value; - this.markDirty(); + this.writeTagValue("notification", notification.id, "to", value); }, "{admin_email}, sales@example.com" ), @@ -8520,8 +8969,7 @@ ${decls} this.taggableInput( notification.replyTo, (value) => { - notification.replyTo = value; - this.markDirty(); + this.writeTagValue("notification", notification.id, "replyTo", value); }, "Leave empty to reply to you" ), @@ -8530,8 +8978,7 @@ ${decls} row( "Subject", this.taggableInput(notification.subject, (value) => { - notification.subject = value; - this.markDirty(); + this.writeTagValue("notification", notification.id, "subject", value); }) ), row( @@ -8539,8 +8986,7 @@ ${decls} this.taggableArea( notification.message, (value) => { - notification.message = value; - this.markDirty(); + this.writeTagValue("notification", notification.id, "message", value); }, 8 ) @@ -8614,8 +9060,7 @@ ${decls} this.taggableArea( confirmation.message, (value) => { - confirmation.message = value; - this.markDirty(); + this.writeTagValue("confirmation", confirmation.id, "message", value); }, 5 ), @@ -8630,8 +9075,7 @@ ${decls} this.taggableInput( confirmation.query, (value) => { - confirmation.query = value; - this.markDirty(); + this.writeTagValue("confirmation", confirmation.id, "query", value); }, "ref={entry:id}&name={field:f1}" ), @@ -8645,8 +9089,7 @@ ${decls} this.taggableInput( confirmation.url, (value) => { - confirmation.url = value; - this.markDirty(); + this.writeTagValue("confirmation", confirmation.id, "url", value); }, "https://example.com/thank-you" ), @@ -8742,8 +9185,7 @@ ${decls} this.taggableInput( success.title, (value) => { - success.title = value; - this.markDirty(); + this.writeTagValue("confirmation", confirmation.id, "successTitle", value); }, "Thank you, {field:name}!" ), diff --git a/assets/js/builder.min.js b/assets/js/builder.min.js index 1f3f2cb..86616fb 100644 --- a/assets/js/builder.min.js +++ b/assets/js/builder.min.js @@ -1,5 +1,5 @@ -var allTerrainFormsBuilder=function(ee){"use strict";class Zt{constructor(){this.targets=[],this.active=null,this.lastEndMs=0}start(e){if(this.active||e.origin.button!==0)return null;const{payload:t,origin:s}=e,a=s.clientX,i=s.clientY;let o=!1,l=!1,c=null,d=null,h=0,w=0;const _=()=>{document.removeEventListener("pointermove",x),document.removeEventListener("pointerup",I),document.removeEventListener("pointercancel",S),document.removeEventListener("keydown",H),window.removeEventListener("blur",S),c?.remove(),c=null,t.source.classList.remove("atf-is-dragging"),document.body.classList.remove("atf-drag-active"),d?.onLeave?.(b),d=null,this.active=null,this.lastEndMs=Date.now()},u=(E,f)=>{document.dispatchEvent(new CustomEvent(E,{detail:{payload:t,clientX:f?.clientX,clientY:f?.clientY}}))},b={payload:t,isFinished:()=>l,cancel:(E="caller")=>{l||(l=!0,_(),o&&u("os.drag.end"),e.onCancel?.(E))}},g=E=>{o=!0,t.source.classList.add("atf-is-dragging"),document.body.classList.add("atf-drag-active");const f=t.source.getBoundingClientRect();h=t.ghost?.offsetX??a-f.left,w=t.ghost?.offsetY??i-f.top,c=t.ghost?.element??t.source.cloneNode(!0),c.classList.add("atf-drag-ghost"),c.style.width=`${f.width}px`,document.body.appendChild(c),p(E),u("os.drag.start",E)},p=E=>{c&&(c.style.transform=`translate3d(${E.clientX-h}px, ${E.clientY-w}px, 0)`)},x=E=>{if(l)return;if(!o){if(Math.hypot(E.clientX-a,E.clientY-i)<4)return;g(E)}p(E);const f=this.hitTest(E.clientX,E.clientY);f!==d&&(d?.onLeave?.(b),d=f,d?.onEnter?.(b)),u("os.drag.move",E)},I=E=>{if(l)return;if(!o){l=!0,_(),e.onClickOnly?.();return}const f=d;if(l=!0,_(),f&&f.accept(t)){e.onCommit?.(f),f.onDrop(b,{clientX:E.clientX,clientY:E.clientY}),u("os.drag.end",E);return}u("os.drag.end",E),e.onCancel?.(f?"rejected":"no-target")},S=()=>b.cancel("pointercancel"),H=E=>{E.key==="Escape"&&b.cancel("escape")};return document.addEventListener("pointermove",x),document.addEventListener("pointerup",I),document.addEventListener("pointercancel",S),document.addEventListener("keydown",H),window.addEventListener("blur",S),this.active=b,b}registerDropTarget(e){return this.targets=this.targets.filter(t=>t.id!==e.id),this.targets.push(e),()=>{this.targets=this.targets.filter(t=>t.id!==e.id)}}isDragging(){return this.active!==null}recentlyEndedDrag(e=500){return Date.now()-this.lastEndMso.right||to.bottom)continue;const l=Kt(i.element);if(l>a){s=i,a=l;continue}l===a&&s&&Vt(i.element,s.element)&&(s=i)}return s}}function Kt(n){let e=0,t=n;for(;t;)e++,t=t.parentElement;return e}function Vt(n,e){return(e.compareDocumentPosition(n)&Node.DOCUMENT_POSITION_FOLLOWING)!==0}function Se(){return window.wp?.os??null}let Le=null;function Z(){const n=Se();return n?.dragManager?n.dragManager:(Le||(Le=new Zt),Le)}function Jt(n){const e=i=>{const o=i.detail?.payload;return o&&n.includes(o.type)?o.source:null},t=i=>i.preventDefault(),s=i=>{const o=e(i);o&&(o.classList.add("atf-is-dragging"),document.body.classList.add("atf-drag-active"),window.getSelection()?.removeAllRanges(),document.addEventListener("selectstart",t))},a=i=>{const o=e(i);o&&(o.classList.remove("atf-is-dragging"),document.body.classList.remove("atf-drag-active"),document.removeEventListener("selectstart",t))};return document.addEventListener("os.drag.start",s),document.addEventListener("os.drag.end",a),()=>{document.removeEventListener("os.drag.start",s),document.removeEventListener("os.drag.end",a),document.removeEventListener("selectstart",t)}}const Qt=["atfb","atfe","atfs","atfm"];function Ee(n,e,t,s,a){const i=e.getBoundingClientRect();if(!a){const o=Qt.find(l=>e.closest(`.${l}`));if(o){const l=e.cloneNode(!0);l.style.transition="",l.style.transform="",a=document.createElement("div"),a.className=`${o} atf-ghost-scope`,a.appendChild(l)}}return a&&(a.style.width=`${Math.round(i.width)}px`,a.style.maxWidth=`${Math.round(i.width)}px`,a.style.boxSizing="border-box"),{type:n,source:e,data:t,ghost:{element:a,offsetX:s.clientX-i.left,offsetY:s.clientY-i.top,hint:{neutral:"",accept:"",reject:"",hidden:!0}}}}function Je(n,e,t,s){const a=Array.from(n.querySelectorAll(e)).filter(i=>i!==s&&!i.classList.contains("atf-drag-ghost"));for(let i=0;ia.fetch(l,c,{source:"allterrain-forms"}):(l,c)=>fetch(l,c))(t,{credentials:"same-origin",...e,headers:s});if(!o.ok){let l=`Request failed with status ${o.status}.`,c="";try{const d=await o.json();l=d.message??l,c=d.code??""}catch{}throw new de(l,o.status,c)}if(o.status!==204)return await o.json()}const U=n=>Ce(n);async function es(n){if(!j?.wpRestUrl)throw new de("AllTerrain Forms is not configured on this page.",0);const e=j.nonce?{"X-WP-Nonce":j.nonce}:{},t=Se(),s=Qe(j.wpRestUrl,n),a=t?.fetch?await t.fetch(s,{credentials:"same-origin",headers:e},{source:"allterrain-forms"}):await fetch(s,{credentials:"same-origin",headers:e});if(!a.ok)throw new de(`Request failed with status ${a.status}.`,a.status);return await a.json()}function ts(n){if(!n||!n.includes("&"))return n;const e=document.createElement("textarea");return e.innerHTML=n,e.value}const K=(n,e)=>Ce(n,{method:"POST",body:JSON.stringify(e)}),he=n=>Ce(n,{method:"DELETE"});function et(n){const e=new URLSearchParams;for(const[s,a]of Object.entries(n))a===void 0||a===""||a===!1||e.set(s,String(a));const t=e.toString();return t?`?${t}`:""}function ue(n){const e=n?.schema?.settings;return e&&(!e.themeOverrides||Array.isArray(e.themeOverrides))&&(e.themeOverrides={...e.themeOverrides}),n}const F={config:()=>U("/config"),mailpoet:()=>U("/mailpoet"),listForms:()=>U("/forms"),listArchivedForms:()=>U("/forms?archived=1"),archiveForm:n=>K(`/forms/${n}/archive`,{}),unarchiveForm:n=>K(`/forms/${n}/unarchive`,{}),getForm:n=>U(`/forms/${n}`).then(ue),createForm:n=>K("/forms",n).then(ue),updateForm:(n,e)=>K(`/forms/${n}`,e).then(ue),duplicateForm:n=>K(`/forms/${n}/duplicate`,{}).then(ue),deleteForm:n=>he(`/forms/${n}`),preview:(n,e)=>K(`/forms/${n}/preview`,e),pages:()=>es("wp/v2/pages?per_page=100&status=publish&orderby=title&order=asc&_fields=id,title").then(n=>n.map(e=>({id:e.id,title:ts(e.title?.rendered??"")||`#${e.id}`}))),mergeTags:n=>U(`/forms/${n}/merge-tags`).then(e=>e.groups),analytics:(n,e="")=>U(`/forms/${n}/analytics${e?`?dimension=${encodeURIComponent(e)}`:""}`),demoStatus:()=>U("/demo"),demoSeed:n=>K("/demo",n?{count:n}:{}),demoRemove:()=>he("/demo"),listEntries:n=>U(`/entries${et(n)}`),getEntry:n=>U(`/entries/${n}`),updateEntry:(n,e)=>K(`/entries/${n}`,e),deleteEntry:n=>he(`/entries/${n}`),exportEntries:n=>U(`/entries/export${et(n)}`),listThemes:()=>U("/themes"),saveTheme:n=>K("/themes",n),deleteTheme:n=>he(`/themes/${n}`)};function r(n,e={}){const t=document.createElement(n);e.class&&(t.className=e.class),e.text!==void 0&&(t.textContent=e.text),e.html!==void 0&&(t.innerHTML=e.html),e.title&&(t.title=e.title),e.type&&"type"in t&&(t.type=e.type),e.value!==void 0&&"value"in t&&(t.value=e.value),e.placeholder!==void 0&&"placeholder"in t&&(t.placeholder=e.placeholder),e.href&&"href"in t&&(t.href=e.href);for(const[s,a]of Object.entries(e.attrs??{}))a===void 0||a===!1||t.setAttribute(s,a===!0?"":String(a));Object.assign(t.style,e.style??{});for(const[s,a]of Object.entries(e.on??{}))t.addEventListener(s,a);for(const s of e.children??[])s==null||s===!1||t.append(s);return t}function R(n){n.replaceChildren()}function z(n){return r("span",{class:`dashicons ${n.startsWith("dashicons-")?n:`dashicons-${n}`}`,attrs:{"aria-hidden":"true"}})}function y(n,e,t){const s=e.matches("input, select, textarea, button")?e:e.querySelector("input, select, textarea");if(s){const a=s.id||`atf-c-${Math.random().toString(36).slice(2,9)}`;return s.id=a,r("div",{class:"atfb-row",children:[r("label",{class:"atfb-row__label",text:n,attrs:{for:a}}),e,t?r("p",{class:"atfb-row__hint",text:t}):null]})}return r("div",{class:"atfb-row",children:[r("span",{class:"atfb-row__label",text:n}),e,t?r("p",{class:"atfb-row__hint",text:t}):null]})}function T(n,e,t=""){return r("input",{class:"atfb-input",type:"text",value:n,placeholder:t,on:{input:s=>e(s.target.value)}})}function G(n,e){if(Y("os-number-field")){const t=document.createElement("os-number-field");return t.setAttribute("value",n),t.classList.add("atfb-field"),t.addEventListener("os-input-change",s=>{e(String(s.detail?.value??""))}),t}return r("input",{class:"atfb-input",type:"number",value:n,on:{input:t=>e(t.target.value)}})}function pe(n,e,t=4){const s=r("textarea",{class:"atfb-input atfb-input--area",attrs:{rows:t},on:{input:a=>e(a.target.value)}});return s.value=n,s}function D(n,e,t){if(Y("os-select")&&Y("os-option")){const s=document.createElement("os-select");s.setAttribute("value",n),s.classList.add("atfb-field");for(const a of e){const i=document.createElement("os-option");i.setAttribute("value",a.value),i.textContent=a.label,s.append(i)}return s.addEventListener("os-pick",a=>{t(String(a.detail?.value??""))}),s}return r("select",{class:"atfb-input atfb-select",on:{change:s=>t(s.target.value)},children:e.map(s=>r("option",{value:s.value,text:s.label,attrs:{selected:s.value===n}}))})}function O(n,e,t){if(Y("os-checkbox-label")){const a=document.createElement("os-checkbox-label");return a.setAttribute("label",n),a.classList.add("atfb-check"),e&&a.setAttribute("checked",""),a.addEventListener("os-checkbox-change",i=>{t(!!i.detail?.checked)}),a}const s=r("input",{type:"checkbox",on:{change:a=>t(a.target.checked)}});return s.checked=e,r("label",{class:"atfb-check",children:[s,r("span",{text:n})]})}function $(n,e,t="secondary",s){const a=[s?z(s):null,r("span",{text:n})];if(Y("os-button")){const i=document.createElement("os-button");i.setAttribute("variant",t),i.setAttribute("type","button"),i.classList.add("atfb-button",`atfb-button--${t}`),i.addEventListener("click",e);for(const o of a)o&&i.append(o);return i}return r("button",{class:`atfb-button atfb-button--${t}`,type:"button",on:{click:e},children:a})}function Y(n){return typeof customElements<"u"&&!!customElements.get(n)}const ss=["os-button","os-checkbox-label","os-number-field","os-select","os-option","os-segmented","os-segment","os-color-field","os-empty-state"];let fe=null;function ns(){if(fe)return fe;const n=window.wp?.os;return fe=n?.loadComponents?n.loadComponents(ss).catch(()=>{}):Promise.resolve(),fe}async function ie(n,e=""){const t=window.wp?.os;return t?.confirm?t.confirm({title:e,message:n,danger:!0}):window.confirm(n)}function N(n,e="",t="info"){const s=window.wp?.os;if(s?.notify){s.notify({title:n,body:e,type:t});return}console.info(`[AllTerrain Forms] ${n}${e?`: ${e}`:""}`)}function as(n){let e=0,t;return(...s)=>{t=s,!e&&(e=window.requestAnimationFrame(()=>{e=0,n(...t)}))}}function $e(n,e){let t=0;return(...s)=>{window.clearTimeout(t),t=window.setTimeout(()=>n(...s),e)}}function is(n){try{return window.localStorage.getItem(n)??""}catch{return""}}function rs(n,e){try{window.localStorage.setItem(n,e)}catch{}}function Ae(n){const e=n.closest(".os-window__body");e&&(e.addEventListener("scroll",()=>{e.scrollTop&&(e.scrollTop=0),e.scrollLeft&&(e.scrollLeft=0)},{passive:!0}),e.scrollTop=0,e.scrollLeft=0)}function tt(){const n=[window];try{window.parent&&window.parent!==window&&n.push(window.parent)}catch{}return n}function os(n){const e=n.wp?.os?.windowManager?.getAll?.()??[];for(const t of e){const s=t.iframe??t.element?.querySelector?.("iframe");if(s&&s.contentWindow===window)return t}return null}const ls=4e3,cs=100;function ds(n,e){const t=Date.now()+ls,s=()=>{const a=os(n);if(a&&a.id!==e){typeof a.close=="function"?a.close():n.wp?.os?.windowManager?.remove?.(a.id);return}!a&&Date.now()0?{form:t}:void 0;t>0&&fs(t);for(const a of tt())if(a.wp?.os?.openWindow?.(e,{source:"handoff",params:s})){window.setTimeout(()=>ds(a,e),0);return}}function us(){document.addEventListener("click",n=>{const e=n.target?.closest?.("[data-atf-open-window]");if(!e)return;n.preventDefault();const t=e.getAttribute("data-atf-open-window")??"";for(const s of tt())if(s.wp?.os?.openWindow?.(t,{source:"handoff"}))return})}const ps="allterrain-forms/requested-form";function fs(n){try{window.sessionStorage.setItem(ps,String(n))}catch{}}function st(n){return`allterrain-forms/requested-form-${n}`}function ms(n){try{const e=window.sessionStorage.getItem(st(n));return window.sessionStorage.removeItem(st(n)),Number(e)||0}catch{return 0}}const Te={is:"is",is_not:"is not",contains:"contains",not_contains:"does not contain",starts_with:"starts with",ends_with:"ends with",greater:"is more than",less:"is less than",greater_equal:"is at least",less_equal:"is at most",empty:"is empty",not_empty:"has any answer"},Me=["empty","not_empty"];function nt(n,e){const t=n.find(s=>s.id===e);return t?t.label||"an untitled question":null}function at(n,e,t){return n.find(i=>i.id===e)?.choices?.find(i=>i.value===t)?.label||t}function bs(n,e){const t=Te[n.operator]??String(n.operator);if(Me.includes(n.operator))return t;const s=at(e,n.field,n.value);return`${n.operator==="is"?"":`${t} `}${s!==""?s:"(nothing)"}`}function it(n,e,t=0){const s=nt(e,n.field),a=Te[n.operator]??String(n.operator);if(s===null)return[{kind:"field",text:"a question that no longer exists",fieldId:n.field,missing:!0}];const i=[{kind:"field",text:s,fieldId:n.field,missing:!1},{kind:"operator",text:a,operator:n.operator,ruleIndex:t}];if(Me.includes(n.operator))return i;const o=at(e,n.field,n.value);return i.push({kind:"value",text:o!==""?o:"(nothing)",raw:n.value,sourceId:n.field,ruleIndex:t}),i}function gs(n,e){const t=n.logic;if(!t?.enabled||!t.rules.length)return[];const s=[{kind:"verb",text:t.action==="hide"?"Hidden when":"Shown when"}];return t.rules.forEach((a,i)=>{i>0&&s.push({kind:"join",text:t.match==="all"?"and":"or"}),s.push(...it(a,e,i))}),s}function rt(n){return n.map(e=>e.text).join(" ")}function vs(n,e){return rt(it(n,e))}function me(n){const e=[];for(const t of n){const s=t.logic;if(s?.enabled)for(const a of s.rules)!a.field||a.field===t.id||e.push({from:a.field,to:t.id,label:vs(a,n),short:bs(a,n),action:s.action,broken:nt(n,a.field)===null})}return e}function ys(n){const e=new Map,t=new Set;for(const s of me(n)){const a=`${s.from}->${s.to}`;s.broken||t.has(a)||(t.add(a),e.set(s.from,(e.get(s.from)??0)+1))}return e}const ot=10,lt=22,ne="http://www.w3.org/2000/svg";class ws{constructor(e){this.edges=[],this.teardowns=[],this.frame=0,this.host=e,this.svg=document.createElementNS(ne,"svg"),this.svg.setAttribute("class","atfb-logicmap"),this.svg.setAttribute("aria-hidden","true"),this.svg.setAttribute("focusable","false"),e.append(this.svg);const t=()=>this.schedule();window.addEventListener("resize",t),this.teardowns.push(()=>window.removeEventListener("resize",t));const s=e.closest(".atfb__canvas")??e;if(s.addEventListener("scroll",t,{passive:!0}),this.teardowns.push(()=>s.removeEventListener("scroll",t)),typeof ResizeObserver<"u"){const a=new ResizeObserver(t);a.observe(e),this.teardowns.push(()=>a.disconnect())}}setEdges(e){this.edges=e,this.schedule()}highlight(e){this.svg.classList.toggle("is-focused",!!e),this.svg.querySelectorAll("[data-from]").forEach(t=>{const s=e&&(t.dataset.from===e||t.dataset.to===e);t.classList.toggle("is-lit",!!s)})}destroy(){this.teardowns.forEach(e=>e()),this.teardowns=[],this.svg.remove(),this.frame&&cancelAnimationFrame(this.frame)}schedule(){this.frame||(this.frame=requestAnimationFrame(()=>{this.frame=0,this.draw()}))}draw(){this.svg.replaceChildren();const e=this.host.getBoundingClientRect();this.svg.setAttribute("viewBox",`0 0 ${e.width} ${e.height}`),this.svg.setAttribute("width",String(e.width)),this.svg.setAttribute("height",String(e.height));const t=new Map;for(const s of this.edges){const a=this.cardRect(s.from),i=this.cardRect(s.to);if(!a||!i)continue;const o=t.get(s.from)??0;t.set(s.from,o+1),this.drawEdge(s,a,i,e,o)}}cardRect(e){const t=this.host.querySelector(`[data-atfb-card="${CSS.escape(e)}"]`);return t?t.getBoundingClientRect():null}drawEdge(e,t,s,a,i){const o=t.right-a.left,l=t.top+t.height/2-a.top,c=s.right-a.left,d=s.top+s.height/2-a.top,h=Math.max(0,a.width-ot-Math.max(o,c)),w=22+i*11+Math.min(22,Math.abs(d-l)/8),_=Math.max(12,Math.min(w,h*.4)),u=document.createElementNS(ne,"path");u.setAttribute("d",`M ${o} ${l} C ${o+_} ${l}, ${c+_} ${d}, ${c} ${d}`),u.setAttribute("class",`atfb-logicmap__path is-${e.action}${e.broken?" is-broken":""}`),u.dataset.from=e.from,u.dataset.to=e.to;const b=document.createElementNS(ne,"title");b.textContent=e.label,u.append(b);const g=document.createElementNS(ne,"circle");g.setAttribute("cx",String(c)),g.setAttribute("cy",String(d)),g.setAttribute("r","3.5"),g.setAttribute("class",`atfb-logicmap__dot is-${e.action}${e.broken?" is-broken":""}`),g.dataset.from=e.from,g.dataset.to=e.to,this.svg.append(u,g);const p=document.createElementNS(ne,"text");p.setAttribute("x",String(a.width-ot)),p.setAttribute("y",String(d+3)),p.setAttribute("text-anchor","end"),p.setAttribute("class","atfb-logicmap__label"),p.dataset.from=e.from,p.dataset.to=e.to,p.textContent=e.short.length>lt?`${e.short.slice(0,lt-1)}…`:e.short;const x=document.createElementNS(ne,"title");x.textContent=e.label,p.append(x),this.svg.append(p)}}const _s={text:"text",email:"text",url:"text",tel:"text",number:"text",password:"text",date:"text",time:"text",datetime:"text",date_range:"composite",textarea:"textarea",select:"select",country:"select",multiselect:"options",radio:"options",checkboxes:"options",image_choice:"options",quiz:"options",switch:"toggle",consent:"toggle",file:"file",range:"range",rating:"summary",scale:"summary",likert:"summary",signature:"summary",repeater:"repeater",color:"text",name:"composite",address:"composite",total:"text",hidden:"static",heading:"static",html:"static",divider:"static",spacer:"static",page_break:"static"};function xs(n){return _s[n]??"text"}function ct(n,e){const t=/^choice:(\d+):(label|value)$/.exec(e);return String(t?n.choices?.[Number(t[1])]?.[t[2]]??"":n[e]??"")}function V(n){const{value:e,onInput:t,onCommit:s}=n,a=r("span",{class:`${n.class} atfb-editable`,text:e,attrs:{contenteditable:"plaintext-only",role:"textbox",spellcheck:"false","data-placeholder":n.placeholder}});return n.bind&&(a.dataset.atfbBind=n.bind),a.addEventListener("pointerdown",i=>i.stopPropagation()),a.addEventListener("input",()=>t(a.textContent??"")),a.addEventListener("keydown",i=>{i.key==="Enter"&&(i.preventDefault(),a.blur()),i.key==="Escape"&&(a.textContent=e,a.blur()),i.stopPropagation()}),a.addEventListener("blur",()=>s?.()),a}function ks(n){const e=r("input",{class:"atf-choice__input",type:n==="checkboxes"||n==="multiselect"?"checkbox":"radio"});return e.disabled=!0,e}function dt(n,e,t){const s=xs(n.type),a=V({value:n.label,placeholder:"Write the question…",class:"atf-label",bind:"label",onInput:o=>t.edit(l=>{l.label=o}),onCommit:()=>t.restructure(()=>{})}),i=[s==="static"||s==="toggle"?null:a,Ls(n,e,s,t),Ss(n,e,t)];return r("div",{class:"atfb-preview atf-form",children:[r("div",{class:`atf-field atf-field--${n.type}`,children:i})]})}function Ss(n,e,t){if(!e?.supports.includes("hint"))return null;const s=V({value:n.hint??"",placeholder:"Add a hint…",class:"atf-hint",bind:"hint",onInput:a=>t.edit(i=>{i.hint=a})});return s.setAttribute("aria-label","Hint"),r("p",{class:"atfb-preview__hint",children:[s]})}function Ls(n,e,t,s){switch(t){case"text":return Fe(n,"atf-input",s);case"textarea":return Fe(n,"atf-input atf-textarea",s,!0);case"select":return r("div",{class:"atfb-preview__select",children:[Fe(n,"atf-input atf-select",s)]});case"options":return As(n,s);case"toggle":return r("div",{class:n.type==="switch"?"atf-toggle atf-toggle--switch":"atf-toggle",children:[(()=>{const a=r("input",{class:"atf-toggle__input",type:"checkbox"});return a.disabled=!0,a})(),V({value:n.label||"",placeholder:n.type==="consent"?"What are they agreeing to?…":"What does this turn on?…",class:"atf-toggle__label",bind:"label",onInput:a=>s.edit(i=>{i.label=a}),onCommit:()=>s.restructure(()=>{})})]});case"file":return r("div",{class:"atf-file",children:[r("input",{class:"atf-file__input",type:"file"})]});case"range":return r("input",{class:"atf-range__input",type:"range"});case"composite":return r("div",{class:"atf-composite__parts",children:[r("span",{class:"atf-input atfb-preview__ghost",text:""}),r("span",{class:"atf-input atfb-preview__ghost",text:""})]});case"static":return Ts(n,e,s);case"repeater":return Es(n,s);default:return r("div",{class:"atfb-preview__stack",children:[r("p",{class:"atfb-preview__summary",text:`${e?.label??n.type} — set this up in the panel on the right.`})]})}}function Es(n,e){const t=n.fields??[],s=r("div",{class:"atfb-repeater__zone",attrs:{"data-atfb-repeater-zone":n.id}});return t.forEach(a=>s.append(Cs(n,a,e))),t.length||s.append(r("p",{class:"atfb-repeater__empty",text:"Drag fields from the palette in here — the visitor gets a fresh copy of each with every row they add."})),r("div",{class:"atfb-repeater",attrs:{"data-atfb-repeater":n.id},children:[r("div",{class:"atf-repeater__row atfb-repeater__frame",children:[r("div",{class:"atf-repeater__row-head",children:[r("span",{class:"atf-repeater__title atfb-repeater__title",children:[V({value:String(n.itemLabel??""),placeholder:"Row",class:"atfb-repeater__item-label",bind:"itemLabel",onInput:a=>e.edit(i=>{i.itemLabel=a})}),r("span",{class:"atfb-repeater__ordinal",text:"1",attrs:{"aria-hidden":"true"}})]})]}),s]}),$s(n,e)]})}function Cs(n,e,t){const s=e.id,a=l=>c=>{const d=(c.fields??[]).find(h=>h.id===s);d&&l(d)},i={edit:l=>t.edit(a(l)),restructure:l=>t.restructure(a(l)),types:t.types,selectedId:t.selectedId},o=t.types?.(e.type);return r("div",{class:`atfb-subcard${t.selectedId===s?" is-selected":""}`,attrs:{"data-atfb-subfield":s,"data-atfb-parent":n.id,tabindex:"0",role:"button","aria-label":`${e.label||o?.label||e.type}, inside ${n.label||"the repeater"}`},children:[r("div",{class:"atfb-subcard__head",children:[r("span",{class:"atfb-subcard__type",text:o?.label??e.type}),e.required?r("span",{class:"atfb-subcard__required",text:"Required"}):null,r("button",{class:"atfb-preview__remove",type:"button",title:"Remove this field from the repeater",attrs:{"aria-label":`Remove ${e.label||o?.label||"this field"}`},on:{pointerdown:l=>l.stopPropagation(),click:l=>{l.stopPropagation(),t.restructure(c=>{const d=c.fields??[],h=d.findIndex(w=>w.id===s);h>=0&&d.splice(h,1)})}},children:[r("span",{text:"×"})]})]}),dt(e,o,i)]})}function Fe(n,e,t,s=!1){const a=V({value:n.placeholder??"",placeholder:n.type==="select"||n.type==="country"?"Choose…":"Placeholder…",class:`${e} atfb-preview__box${s?" atfb-preview__box--tall":""}`,bind:"placeholder",onInput:i=>t.edit(o=>{o.placeholder=i})});return a.setAttribute("aria-label","Placeholder"),a}function Ie(n,e,t,s,a,i){return r("span",{class:`atf-button ${t} atfb-preview__button`,children:[V({value:n,placeholder:e,class:"atfb-preview__button-text",bind:s,onInput:o=>a.edit(l=>i(l,o))})]})}function $s(n,e){return Ie(String(n.addLabel??""),"Add another","atf-button--ghost atfb-repeater__add","addLabel",e,(t,s)=>{t.addLabel=s})}function As(n,e){const t=r("div",{class:"atf-choices__list"});return(n.choices??[]).forEach((s,a)=>{t.append(r("div",{class:"atf-choice atfb-preview__option",children:[ks(n.type),V({value:s.label,placeholder:"Option…",class:"atf-choice__label",bind:`choice:${a}:label`,onInput:i=>{e.edit(o=>{const l=o.choices?.[a];if(!l)return;const c=!l.value||l.value===l.label;l.label=i,c&&(l.value=i)})}}),r("button",{class:"atfb-preview__remove",type:"button",title:"Remove this option",attrs:{"aria-label":`Remove ${s.label||"this option"}`},on:{pointerdown:i=>i.stopPropagation(),click:i=>{i.stopPropagation(),e.restructure(o=>{o.choices.splice(a,1)})}},children:[r("span",{text:"×"})]})]}))}),t.append(r("button",{class:"atfb-preview__add",type:"button",on:{pointerdown:s=>s.stopPropagation(),click:s=>{s.stopPropagation(),e.restructure(a=>{const i=a.choices.length+1;a.choices.push({label:`Option ${i}`,value:`Option ${i}`})})}},children:[r("span",{text:"+ Add option"})]})),r("fieldset",{class:"atf-choices",children:[t]})}function Ts(n,e,t){return n.type==="heading"?V({value:n.label,placeholder:"Section heading…",class:"atf-heading",bind:"label",onInput:s=>t.edit(a=>{a.label=s}),onCommit:()=>t.restructure(()=>{})}):n.type==="divider"?r("hr",{class:"atf-divider"}):n.type==="page_break"?r("div",{class:"atfb-preview__stack",children:[r("p",{class:"atfb-progress-name",children:[V({value:n.label,placeholder:"Name this step…",class:"atf-progress__label",bind:"label",onInput:s=>t.edit(a=>{a.label=s}),onCommit:()=>t.restructure(()=>{})})]}),r("p",{class:"atfb-preview__summary",text:"Everything after this is a new page."}),r("div",{class:"atf-nav atfb-preview__nav",children:[Ie(String(n.prevLabel??""),"Back","atf-button--secondary","prevLabel",t,(s,a)=>{s.prevLabel=a}),Ie(String(n.nextLabel??""),"Next","","nextLabel",t,(s,a)=>{s.nextLabel=a})]})]}):r("p",{class:"atfb-preview__summary",text:`${e?.label??n.type} — nothing is shown to the visitor here.`})}const De=new Map;function qe(n){let e=De.get(n);return e||(e=F.mergeTags(n).catch(()=>[]),De.set(n,e)),e}function Ms(n){De.delete(n)}function Fs(n){const e=new Map;for(const t of n)for(const s of t.items)e.set(s.tag,s);return e}function Is(n,e){const t=Fs(e);return n.replace(/\{[a-z_]+(?::[^}]*)?\}/gi,s=>{const a=t.get(s.toLowerCase());return a?a.sample:s})}function Ds(n){return/\{[a-z_]+(?::[^}]*)?\}/i.test(n)}let te=null;function re(){te?.remove(),te=null}typeof document<"u"&&(document.addEventListener("pointerdown",n=>{const e=n.target;e?.closest(".atfb-tagpick__open")||te&&!te.contains(e)&&re()}),document.addEventListener("keydown",n=>{n.key==="Escape"&&te&&(re(),n.stopPropagation())}));function ht(n,e){const t=n.selectionStart??n.value.length,s=n.selectionEnd??n.value.length;n.value=n.value.slice(0,t)+e+n.value.slice(s);const a=t+e.length;n.setSelectionRange(a,a),n.focus(),n.dispatchEvent(new Event("input",{bubbles:!0}))}function qs(n){let e=n.parentElement;for(;e&&e!==document.body;){const t=getComputedStyle(e).overflowY;if(t==="auto"||t==="scroll"||t==="hidden")return e.getBoundingClientRect().bottom;e=e.parentElement}return window.innerHeight}function Os(n,e){const t=r("input",{class:"atfb-input atfb-tagpick__search",type:"search",placeholder:"Search values…",attrs:{"aria-label":"Search values"}}),s=r("div",{class:"atfb-tagpick__list"}),a=i=>{s.replaceChildren();const o=i.trim().toLowerCase();let l=0;for(const c of n){const d=c.items.filter(h=>!o||h.label.toLowerCase().includes(o)||h.tag.toLowerCase().includes(o));if(!d.length){c.empty&&!o&&!c.items.length&&s.append(r("p",{class:"atfb-tagpick__group",text:c.label}),r("p",{class:"atfb-tagpick__empty",text:c.empty}));continue}s.append(r("p",{class:"atfb-tagpick__group",text:c.label}));for(const h of d)l+=1,s.append(r("button",{class:"atfb-tagpick__item",type:"button",on:{click:()=>{e(h.tag),re()}},children:[r("span",{class:"atfb-tagpick__item-main",children:[r("span",{class:"atfb-tagpick__label",text:h.label}),r("code",{class:"atfb-tagpick__tag",text:h.tag})]}),h.hint||h.sample?r("span",{class:"atfb-tagpick__meta",text:h.sample?`e.g. ${h.sample}`:h.hint}):null]}))}!l&&o&&s.append(r("p",{class:"atfb-tagpick__empty",text:`Nothing matches “${i}”.`}))};return a(""),t.addEventListener("input",()=>a(t.value)),r("div",{class:"atfb-tagpick",attrs:{role:"dialog","aria-label":"Insert a value"},children:[r("p",{class:"atfb-tagpick__intro",text:"Pick something to drop in. It is filled in when the form is submitted."}),t,s]})}function ut(n,e){const t=r("button",{class:"atfb-button atfb-button--ghost atfb-tagpick__open",type:"button",title:"Insert a value from the submission",children:[z("shortcode"),r("span",{text:"Insert a value"})]}),s=r("div",{class:"atfb-taggable",children:[n,r("div",{class:"atfb-taggable__tools",children:[t]})]}),a=e.preview===!1?null:r("p",{class:"atfb-taggable__preview"});a&&s.append(a);const i=()=>{if(a){if(!Ds(n.value)){a.textContent="",a.hidden=!0;return}qe(e.formId).then(o=>{a.hidden=!1,a.replaceChildren(r("span",{class:"atfb-taggable__preview-label",text:"Reads as"}),r("span",{text:Is(n.value,o)}))})}};return n.addEventListener("input",i),i(),t.addEventListener("click",o=>{if(o.stopPropagation(),te&&s.contains(te)){re();return}re(),qe(e.formId).then(l=>{const c=Os(l,d=>{ht(n,d),i()});s.append(c),te=c,(window.innerHeight-c.getBoundingClientRect().topqs(s))&&c.classList.add("atfb-tagpick--above"),c.querySelector(".atfb-tagpick__search")?.focus()})}),s}function pt(n,e){const t=parseFloat(String(n??""));return Number.isFinite(t)?t:e}function ft(n,e){let t=e[0];for(const s of e)Math.abs(s.at-n)n.accent??"#2271b1",apply:n=>({accent:n,"accent-soft":`color-mix( in srgb, ${n} 12%, transparent )`,"border-focus":n,"focus-ring-color":n,"button-bg":n,"button-bg-hover":`color-mix( in srgb, ${n} 88%, #000 )`})},{id:"roundness",label:"Roundness",hint:"Corners, everywhere at once.",kind:"scale",steps:Oe.map(({value:n,label:e})=>({value:n,label:e})),owns:["radius-field","radius-button","radius-card","radius-check"],read:n=>ft(pt(n["radius-field"],4),Oe),apply:n=>{const e=Oe.find(t=>t.value===n)?.at??4;return{"radius-field":`${e}px`,"radius-button":`${e}px`,"radius-card":`${Math.min(e*2,28)}px`,"radius-check":`${Math.min(e,6)}px`}}},{id:"density",label:"Density",hint:"How much air the form has.",kind:"scale",steps:Ne.map(({value:n,label:e})=>({value:n,label:e})),owns:["pad-field-x","pad-field-y","gap-fields","gap-label","button-pad-x","button-pad-y"],read:n=>ft(pt(n["pad-field-y"],9),Ne),apply:n=>{const e=Ne.find(t=>t.value===n)?.at??9;return{"pad-field-y":`${e}px`,"pad-field-x":`${Math.round(e*1.4)}px`,"gap-fields":`${Math.round(e*2.2)}px`,"gap-label":`${Math.max(4,Math.round(e*.7))}px`,"button-pad-y":`${e+2}px`,"button-pad-x":`${Math.round(e*2.2)}px`}}},{id:"shadow",label:"Depth",hint:"How far the form sits off the page.",kind:"scale",steps:Ns,owns:["shadow-field","shadow-field-focus","shadow-button","shadow-button-hover","shadow-card"],read:n=>{const e=n["shadow-card"]??"none";return e==="none"||e.trim()===""?n["shadow-field"]&&n["shadow-field"]!=="none"?"hard":"none":e.includes("0 1px")||e.includes("2px")?"subtle":"lifted"},apply:n=>{switch(n){case"subtle":return{"shadow-field":"none","shadow-field-focus":"none","shadow-button":"0 1px 2px rgba( 0, 0, 0, 0.12 )","shadow-button-hover":"0 2px 4px rgba( 0, 0, 0, 0.16 )","shadow-card":"0 1px 3px rgba( 0, 0, 0, 0.1 )"};case"lifted":return{"shadow-field":"none","shadow-field-focus":"0 4px 12px rgba( 0, 0, 0, 0.12 )","shadow-button":"0 4px 10px rgba( 0, 0, 0, 0.16 )","shadow-button-hover":"0 8px 20px rgba( 0, 0, 0, 0.2 )","shadow-card":"0 10px 30px rgba( 0, 0, 0, 0.14 )"};case"hard":return{"shadow-field":"3px 3px 0 currentColor","shadow-field-focus":"5px 5px 0 currentColor","shadow-button":"4px 4px 0 currentColor","shadow-button-hover":"2px 2px 0 currentColor","shadow-card":"none"};default:return{"shadow-field":"none","shadow-field-focus":"none","shadow-button":"none","shadow-button-hover":"none","shadow-card":"none"}}}},{id:"field-style",label:"Fields",hint:"How an input is drawn.",kind:"choice",steps:Rs,owns:["field-style"],read:n=>n["field-style"]??"outline",apply:n=>({"field-style":n})},{id:"labels",label:"Labels",hint:"Where the question sits relative to the answer.",kind:"choice",steps:Ps,owns:["label-position"],read:n=>n["label-position"]??"top",apply:n=>({"label-position":n})}]}function Bs(n){return Re().find(e=>e.owns.includes(n))??null}function mt(n){let e=n.activeSlug,t={...n.overrides},s=n.themes;const a=m=>{t=m,n.onOverridesReplaced?.({...m})},i=r("div",{class:"atfs-preview__frame"}),o=r("div",{class:"atfs-controls__body"}),l=r("div",{class:"atfs-quick"}),c=r("div",{class:"atfs-themes"}),d=$e(async()=>{try{const m=await n.previewFor(e,t),v=i.closest(".atf-studio__preview"),k=v?.scrollTop??0;i.innerHTML=m,v&&(v.scrollTop=k),document.dispatchEvent(new CustomEvent("alltfo-refresh"))}catch(m){R(i),i.append(r("p",{class:"atfb-error",text:m instanceof Error?m.message:"Preview failed."}))}},180),h={"label-position":"atf-labels-","field-style":"atf-fields-"},w=(m,v,k)=>{const L=k.replace(/[^a-z0-9_-]/gi,"");for(const A of[...m.classList])A.startsWith(v)&&m.classList.remove(A);L&&m.classList.add(v+L)},_=m=>{const v=i.querySelector(".atf-form-wrap"),k=v?.querySelector(".atf-form");if(v)for(const[L,A]of Object.entries(m))A===""?v.style.removeProperty(`--atf-${L}`):v.style.setProperty(`--atf-${L}`,A),k&&h[L]&&w(k,h[L],A||(s.find(P=>P.slug===e)?.resolved[L]??""))},u=m=>{_(m.resolved);const v=i.querySelector(".atf-form");v&&(w(v,"atf-theme-",m.slug),v.classList.toggle("atf-is-dark",!!m.dark))},b=m=>t[m.token]!==void 0?t[m.token]:s.find(k=>k.slug===e)?.resolved?.[m.token]??m.default,g=()=>{R(c);for(const m of s){const v=r("button",{class:`atfs-theme${m.slug===e?" is-active":""}`,type:"button",attrs:{"aria-pressed":m.slug===e,title:m.description},children:[r("span",{class:"atfs-theme__chip",style:{background:m.resolved.surface??"#fff",borderColor:m.resolved.border??"#ccc",borderRadius:m.resolved["radius-field"]??"4px",boxShadow:m.resolved["shadow-card"]??"none"},children:[r("span",{class:"atfs-theme__accent",style:{background:m.resolved.accent??"#2271b1",borderRadius:m.resolved["radius-button"]??"4px"}})]}),r("span",{class:"atfs-theme__name",text:m.label}),m.custom?r("span",{class:"atfb-badge",text:"yours"}):null],on:{click:()=>{e=m.slug,a({}),n.onTheme(e),u(m),g(),S(),I(),Yt()}}});c.append(v)}},p=()=>{const v={...s.find(k=>k.slug===e)?.resolved??{}};for(const[k,L]of Object.entries(t))v[k]=L;return v},x=m=>{const v=l.closest(".atf-studio__controls"),k=v?.scrollTop??0;m(),v&&(v.scrollTop=k)},I=$e(()=>x(f),150),S=()=>{const m=p(),v=l.querySelectorAll(".atfs-dial");Re().forEach((k,L)=>{const A=v[L];if(!A)return;const P=k.read(m);if(k.kind==="colour"){const q=A.querySelector('input[type="color"]'),X=A.querySelector("input.atfb-input");q&&document.activeElement!==q&&(q.value=be(P)),X&&document.activeElement!==X&&(X.value=P);return}A.querySelectorAll(".atfs-segment").forEach((q,X)=>{const Q=k.steps?.[X]?.value===P;q.classList.toggle("is-on",Q),q.setAttribute("aria-pressed",String(Q))})})},H=(m,v)=>{const k=m.apply(v,p());for(const[L,A]of Object.entries(k))t[L]=A,n.onOverride(L,A);_(k),S(),I()},E=()=>{R(l);const m=p();for(const v of Re()){const k=v.read(m);let L;if(v.kind==="colour"){const A=r("input",{class:"atfs-color",type:"color",value:be(k),attrs:{"aria-label":v.label},on:{input:P=>H(v,P.target.value)}});L=r("div",{class:"atfs-color-row",children:[A,T(k,P=>H(v,P))]})}else L=r("div",{class:`atfs-segmented atfs-segmented--${v.kind}`,attrs:{role:"group","aria-label":v.label},children:(v.steps??[]).map(A=>r("button",{class:`atfs-segment${A.value===k?" is-on":""}`,type:"button",text:A.label,attrs:{"aria-pressed":A.value===k},on:{click:()=>H(v,A.value)}}))});l.append(r("div",{class:"atfs-dial",children:[r("div",{class:"atfs-dial__head",children:[r("span",{class:"atfs-dial__label",text:v.label}),r("span",{class:"atfs-dial__hint",text:v.hint})]}),L]}))}},f=()=>{R(o);const m=new Map;for(const k of n.tokens){const L=m.get(k.group)??[];L.push(k),m.set(k.group,L)}const v={colour:"Colour",shape:"Corners and borders",shadow:"Shadows",fields:"Field style",space:"Spacing",type:"Type",labels:"Labels",button:"Buttons",focus:"Focus ring",motion:"Motion"};for(const[k,L]of m)o.append(r("details",{class:"atfs-group",attrs:{open:k==="colour"},children:[r("summary",{text:v[k]??k}),...L.map(A=>M(A))]}))},M=m=>{const v=b(m),k=q=>{q===""?delete t[m.token]:t[m.token]=q,n.onOverride(m.token,q),_({[m.token]:q})};let L;if(m.control==="color"){const q=r("input",{class:"atfs-color",type:"color",value:be(v),attrs:{"aria-label":`${m.label} colour`},on:{input:Q=>{const ce=Q.target.value;X.value=ce,k(ce)}}}),X=T(v,Q=>{q.value=be(Q),k(Q)});L=r("div",{class:"atfs-color-row",children:[q,X]})}else if(m.control==="select")L=D(v,(m.options??[]).map(q=>({value:q,label:q})),k);else if(m.control==="length"){const q=parseFloat(v)||0,X=m.unit??"px",Q=m.token.includes("radius")?60:80,ce=r("input",{class:"atfs-range",type:"range",value:String(q),attrs:{min:"0",max:String(Q),step:"1","aria-label":m.label},on:{input:ke=>{const Xt=`${ke.target.value}${X}`;Gt.value=Xt,k(Xt)}}}),Gt=T(v,ke=>{ce.value=String(parseFloat(ke)||0),k(ke)});L=r("div",{class:"atfs-length-row",children:[ce,Gt]})}else L=T(v,k);const A=y(m.label,L),P=Bs(m.token);return P&&(A.classList.add("is-dialled"),A.append(r("span",{class:"atfs-owned",text:P.label}))),t[m.token]!==void 0&&(A.classList.add("is-overridden"),A.append(r("button",{class:"atfs-reset",type:"button",text:"Reset",attrs:{"aria-label":`Reset ${m.label} to the theme's value`},on:{click:()=>{k(""),x(f)}}}))),A},C=async()=>{const m=s.find(L=>L.slug===e),v=m?`${m.label} (mine)`:"My theme",k=window.prompt("Name this theme",v);if(k)try{const L={};for(const P of n.tokens){const q=b(P);q!==P.default&&(L[P.token]=q)}const A=await F.saveTheme({label:k,tokens:L});s=[...s.filter(P=>P.slug!==A.slug),A],n.onThemesChanged(s),e=A.slug,a({}),n.onTheme(e),g(),S(),x(f),N("Theme saved",A.label)}catch(L){N("Could not save the theme",L instanceof Error?L.message:"","error")}},W=async()=>{const m=s.find(v=>v.slug===e);if(m?.custom&&await ie(`Delete “${m.label}”? Forms using it fall back to Clean.`,"Delete theme"))try{await F.deleteTheme(m.id),s=s.filter(k=>k.slug!==m.slug),n.onThemesChanged(s),e="clean",a({}),n.onTheme(e),g(),S(),x(f);const v=s.find(k=>k.slug===e);v&&u(v)}catch(v){N("Could not delete the theme",v instanceof Error?v.message:"","error")}},B=async()=>{const m=s.find(L=>L.slug===e),v={label:m?.label??e,tokens:{...m?.tokens??{},...t}},k=JSON.stringify(v,null," ");try{await navigator.clipboard.writeText(k),N("Theme copied","Paste it into another site to import it.")}catch{window.prompt("Copy this theme",k)}},le=()=>{const m=window.prompt("Paste a theme");if(m)try{const v=JSON.parse(m);if(!v.tokens||typeof v.tokens!="object")throw new Error("That does not look like a theme.");for(const[k,L]of Object.entries(v.tokens))t[k]=String(L),n.onOverride(k,String(L));_({...t}),S(),x(f)}catch(v){N("Could not read that theme",v instanceof Error?v.message:"","error")}},zt=$("Delete",()=>void W(),"danger"),Yt=()=>{const m=s.find(v=>v.slug===e);zt.hidden=!m?.custom};return g(),E(),f(),d(),Yt(),r("div",{class:"atf-studio",children:[r("div",{class:"atf-studio__top",children:[r("div",{class:"atf-studio__topbar",children:[r("h2",{class:"atf-studio__heading",text:"Theme"}),r("div",{class:"atfs__actions",children:[$("Save as a theme",()=>void C(),"primary"),$("Export",()=>void B()),$("Import",le),zt]})]}),c]}),r("div",{class:"atf-studio__panes",children:[r("aside",{class:"atf-studio__controls",children:[r("p",{class:"atfb-hint",text:"Changes apply to this form only, until you save them as a theme."}),l,r("details",{class:"atfs-advanced",children:[r("summary",{text:"Every setting"}),r("p",{class:"atfb-hint",text:"The full token list. The dials above write into it."}),o]})]}),r("main",{class:"atf-studio__preview",children:[r("h2",{class:"screen-reader-text",text:"Live preview"}),i]})]})]})}async function Pe(){const n=document.querySelector("[data-atfs-root]");if(!n||n.dataset.atfsMounted)return;n.dataset.atfsMounted="1",Ae(n);const e=n.querySelector("[data-atfs-bar]"),t=n.querySelector(".atfs__body")??n;try{const[s,a,i]=await Promise.all([F.config(),F.listThemes(),F.listForms()]);if(!i.length){R(t),t.append(r("div",{class:"atfb-empty",children:[r("h2",{text:"No forms to preview"}),r("p",{text:"Make a form first — a theme needs something to dress."})]}));return}let o=i[0],l={},c=o.theme;const d=()=>{R(t),t.append(mt({themes:a,tokens:s.tokens,activeSlug:c,overrides:l,standalone:!0,onTheme:h=>{c=h},onOverride:(h,w)=>{w===""?delete l[h]:l[h]=w},onOverridesReplaced:h=>{l={...h}},previewFor:async(h,w)=>{const _=await F.getForm(o.id);_.schema.settings.theme=h,_.schema.settings.themeOverrides=w;const{html:u}=await F.preview(o.id,{schema:_.schema,theme:h});return u},onThemesChanged:h=>{a.length=0,a.push(...h)}}))};e&&(R(e),e.append(r("span",{class:"atfs__label",text:"Preview against"}),D(String(o.id),i.map(h=>({value:String(h.id),label:h.title||"(untitled)"})),h=>{o=i.find(w=>String(w.id)===h)??i[0],l={},d()}))),d()}catch(s){R(t),t.append(r("p",{class:"atfb-error",text:s instanceof Error?s.message:"Could not load themes."}))}}typeof document<"u"&&(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>void Pe()):Pe(),document.addEventListener("os-window-content-loaded",()=>void Pe()));function be(n){const e=n.trim();return/^#[0-9a-f]{6}$/i.test(e)?e:/^#[0-9a-f]{3}$/i.test(e)?`#${e[1]}${e[1]}${e[2]}${e[2]}${e[3]}${e[3]}`:"#888888"}const Hs="mailpoet",ge="",Ws=["checkbox","checkboxes","toggle","consent","gdpr"],bt=["email"];function gt(n){return n.schema.actions.find(e=>e.type==="mailpoet")}function Be(n){const e=n?.settings??{};return{lists:Array.isArray(e.lists)?e.lists.map(Number).filter(Boolean):[],email_field:typeof e.email_field=="string"?e.email_field:"",first_name_field:typeof e.first_name_field=="string"?e.first_name_field:"",last_name_field:typeof e.last_name_field=="string"?e.last_name_field:""}}function js(n){return n?.logic?.enabled?n.logic.rules[0]?.field??ge:ge}function vt(n,e,t){const s=n.find(a=>e.includes(a.type));return s?s.id:t?n.find(a=>t.test(a.label))?.id??"":""}function Us(n,e,t){const s=r("section",{class:"atfm-form"}),a=l=>r("span",{class:`atfm-form__mark${l?"":" atfm-form__mark--off"}`,children:[r("img",{attrs:{src:e.symbol,alt:"",width:"20",height:"20"}})]}),i=l=>{R(s),s.classList.remove("is-open"),s.append(r("div",{class:"atfm-form__head",children:[a(l.length>0),r("div",{class:"atfm-form__title",children:[r("strong",{text:n.title||`Form ${n.id}`}),l.length?r("span",{class:"atfm-form__status is-on",text:`Subscribing → ${l.join(", ")}`}):r("span",{class:"atfm-form__status",text:"Not connected"})]}),$(l.length?"Edit":"Connect",()=>void o(),"secondary")]}))},o=async()=>{s.classList.add("is-open"),R(s),s.append(r("p",{class:"atfm-hint",text:"Loading the form…"}));let l;try{l=await F.getForm(n.id)}catch{R(s),s.append(r("p",{class:"atfm-hint",text:"Could not load this form."}));return}const c=l.schema.fields.filter(f=>f.type!=="page_break"),d=gt(l),h=Be(d),w=new Set(h.lists.filter(f=>e.lists.some(M=>M.id===f)));let _=js(d),u=h.email_field||vt(c,bt,/mail/i),b=h.first_name_field,g=h.last_name_field;c.some(f=>f.id===u)||(u=vt(c,bt,/mail/i));const p=c.filter(f=>Ws.includes(f.type));!d&&p.length&&(_=p[0].id);const x=(f,M)=>[{value:"",label:M},...f.map(C=>({value:C.id,label:C.label||C.id}))],I=r("div",{class:"atfm-lists"});(()=>{R(I);for(const f of e.lists){const M=r("input",{attrs:{type:"checkbox"}});M.checked=w.has(f.id),M.addEventListener("change",()=>{M.checked?w.add(f.id):w.delete(f.id)}),I.append(r("label",{class:"atfm-list",children:[M,r("span",{text:f.name})]}))}})();const H=async(f=!1)=>{const M=l.schema.actions.filter(C=>C.type!=="mailpoet");if(!f){if(!w.size){N("Pick a list","A subscription needs at least one MailPoet list.","error");return}if(!u){N("Pick the email field","MailPoet needs to know which answer is the address.","error");return}M.push({id:Hs,type:"mailpoet",enabled:!0,logic:_===ge?{enabled:!1,action:"show",match:"all",rules:[]}:{enabled:!0,action:"show",match:"all",rules:[{field:_,operator:"not_empty",value:""}]},settings:{lists:[...w],email_field:u,first_name_field:b,last_name_field:g}})}l.schema.actions=M;try{await F.updateForm(l.id,{schema:l.schema})}catch(C){N("Could not save",C instanceof Error?C.message:"","error");return}N(f?"Disconnected":"Connected to MailPoet",f?`${n.title} no longer subscribes anyone.`:`${n.title} now subscribes opted-in visitors.`),i(f?[]:e.lists.filter(C=>w.has(C.id)).map(C=>C.name))};R(s);const E=f=>r("span",{class:"atfm-form__section",text:f});s.append(r("div",{class:"atfm-form__head",children:[a(!!d),r("div",{class:"atfm-form__title",children:[r("strong",{text:n.title||`Form ${n.id}`})]}),$("Close",()=>i(d?e.lists.filter(f=>Be(d).lists.includes(f.id)).map(f=>f.name):[]),"ghost")]}),r("div",{class:"atfm-form__body",children:[E("Where they land"),y("Lists",I,"MailPoet sends its own confirmation email before anyone is truly on a list."),E("Who they are"),y("Email address",D(u,x(c,"— pick a field —"),f=>{u=f}),"The answer that carries their address."),y("First name",D(b,x(c,"— none —"),f=>{b=f})),y("Last name",D(g,x(c,"— none —"),f=>{g=f})),E("When to subscribe"),y("Subscribe",D(_,[...p.map(f=>({value:f.id,label:`Only when “${f.label||f.id}” is ticked`})),{value:ge,label:"Everyone who submits"}],f=>{_=f}),p.length?"Bind it to a box they tick. Subscribing everyone is rarely what visitors expect.":"Add a checkbox or toggle field to the form to offer a proper opt-in."),r("div",{class:"atfm-form__actions",children:d?[$("Save connection",()=>void H(),"primary"),$("Disconnect",()=>void H(!0),"danger")]:[$("Save connection",()=>void H(),"primary")]})]}))};return i([]),F.getForm(n.id).then(l=>{const c=gt(l);if(c&&!s.classList.contains("is-open")){const d=e.lists.filter(h=>Be(c).lists.includes(h.id)).map(h=>h.name);i(d)}}).catch(()=>{}),t.append(s),s}function zs(n){const e=n.logo?r("span",{class:"atfm-hero__logo",children:[r("img",{attrs:{src:n.logo,alt:"MailPoet",width:"210",height:"105"}})]}):r("span",{class:"atfm-hero__logo atfm-hero__logo--glyph",text:"📬"});return r("header",{class:"atfm-hero",children:[e,r("div",{class:"atfm-hero__words",children:[r("h1",{text:"Turn submissions into subscribers"}),r("p",{text:"Every submission is someone choosing to talk to you. Connect a form to MailPoet and the ones who opt in land on your lists by themselves — named, consented, and confirmed by MailPoet’s own double opt-in."}),n.active?r("p",{class:"atfm-hero__status is-on",text:n.lists.length===1?"Connected — 1 list ready for subscribers":`Connected — ${n.lists.length} lists ready for subscribers`}):r("p",{class:"atfm-hero__status",text:"MailPoet is not installed on this site yet"})]})]})}function Ys(){const n=(t,s,a)=>r("div",{class:"atfm-why__card",children:[r("span",{class:"atfm-why__icon",text:t}),r("strong",{text:s}),r("p",{text:a})]}),e=(t,s)=>r("a",{text:s,attrs:{href:t,target:"_blank",rel:"noreferrer"}});return[r("div",{class:"atfm-why",children:[n("✉️","Beautiful emails, made in WordPress","Design newsletters and welcome emails in a drag-and-drop editor that lives in your own admin — no external account to juggle."),n("🤝","Consent you can stand behind","Double opt-in out of the box: every address your forms send over is confirmed by the visitor before a single campaign reaches it."),n("🚀","Free to grow with","Free up to 500 subscribers, welcome automations, WooCommerce emails and open-rate stats included — a paid addon anywhere else.")]}),r("p",{class:"atfm-links",children:[e("https://www.mailpoet.com/features/","Explore MailPoet’s features ↗"),e("https://kb.mailpoet.com/","Guides & docs ↗"),e("https://www.mailpoet.com/pricing/","Plans & the free tier ↗")]})]}function Gs(){const n=(e,t)=>r("div",{class:"atfm-step",children:[r("span",{class:"atfm-step__n",text:e}),r("span",{text:t})]});return r("div",{class:"atfm-steps",children:[n("1","Pick a form below"),n("2","Choose the lists it feeds"),n("3","Bind it to an opt-in — MailPoet confirms the rest")]})}async function He(){const n=document.querySelector("[data-atfm-root]");if(!n||n.dataset.atfmMounted)return;n.dataset.atfmMounted="1",Ae(n);const e=n.querySelector("[data-atfm-bar]"),t=n.querySelector("[data-atfm-body]")??n;try{const[s,a]=await Promise.all([F.mailpoet(),F.listForms()]);if(e?.remove(),R(t),t.append(zs(s),...Ys()),!s.active){t.append(r("div",{class:"atfm-pitch",children:[r("p",{text:"MailPoet sends newsletters from right inside WordPress — no external account, free to start. Install it and every form here gets a subscribe switch: reservations feed your announcements, signups feed your schedule, enquiries feed your news."}),r("a",{class:"atfm-pitch__cta",text:"Install MailPoet — it’s free",attrs:{href:s.adminUrl,target:"_blank",rel:"noreferrer"}})]}));return}if(!s.lists.length){t.append(r("div",{class:"atfm-pitch",children:[r("p",{text:"MailPoet is here, but it has no list yet. Make one, and your forms will have somewhere to send subscribers."}),r("a",{class:"atfm-pitch__cta",text:"Open MailPoet lists",attrs:{href:s.adminUrl,target:"_blank",rel:"noreferrer"}})]}));return}t.append(Gs());const i=r("section",{class:"atfm-forms",children:[r("div",{class:"atfm-forms__head",children:[r("h2",{text:"Your forms"}),r("p",{class:"atfm-hint",text:"Connected forms wear the MailPoet mark in colour."})]})]});if(t.append(i),!a.length){i.append(r("p",{class:"atfm-hint",text:"No forms yet — make one, then come back to connect it."}));return}for(const o of a)Us(o,s,i)}catch{e&&(e.textContent="Could not reach the site — reload the window to try again.")}}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>void He()):He(),document.addEventListener("os-window-content-loaded",()=>void He());const ve={plain:"",simple:"✓",minimal:"",card:"🎉",check:"",confetti:"🎉",fireworks:"🎆",sparkles:"✨",typewriter:""};function yt(){return{style:"simple",title:"",icon:"",accent:"",intensity:"medium",showButton:!1,buttonLabel:""}}function We(n){const e={...yt(),...n??{}};return e.style in ve||(e.style="simple"),e}function Xs(){return typeof window.matchMedia=="function"&&window.matchMedia("(prefers-reduced-motion: reduce)").matches}function Zs(n,e,t){const s=We(e),a=document.createElement("div");if(a.className=`atf-confirmation atf-success atf-success--${s.style}`,a.setAttribute("role","status"),a.setAttribute("tabindex","-1"),s.style==="plain")return a.className="atf-confirmation",a.innerHTML=n,a;s.accent&&a.style.setProperty("--atf-accent",s.accent);const i=document.createElement("div");i.className="atf-success__inner",a.append(i);const o=s.icon||ve[s.style];if(s.style==="check")i.insertAdjacentHTML("beforeend",'');else if(o){const c=document.createElement("span");c.className="atf-success__icon",c.setAttribute("aria-hidden","true"),c.textContent=o,i.append(c)}if(s.title){const c=document.createElement("h2");c.className="atf-success__title",c.textContent=s.title,i.append(c)}const l=document.createElement("div");if(l.className="atf-success__message",l.innerHTML=n,i.append(l),s.style==="typewriter"&&a.setAttribute("aria-label",l.textContent??""),s.showButton){const c=document.createElement("button");c.type="button",c.className="atf-button atf-button--ghost atf-success__again",c.textContent=s.buttonLabel||"Fill it in again",c.addEventListener("click",()=>t?t():window.location.reload()),i.append(c)}return a}function Ks(n,e){const t=We(e);if(Xs())return()=>{};switch(t.style){case"confetti":return Vs(n,t);case"fireworks":return Js(t);case"sparkles":return Qs(n,t);case"typewriter":return en(n);default:return()=>{}}}function oe(n){return{low:.5,medium:1,high:1.8}[n.intensity]}function wt(){const n=document.createElement("canvas");n.className="atf-success-canvas",n.setAttribute("aria-hidden","true");const e=Math.min(window.devicePixelRatio||1,2);n.width=Math.floor(window.innerWidth*e),n.height=Math.floor(window.innerHeight*e),document.body.append(n);const t=n.getContext("2d");return t?.scale(e,e),{canvas:n,ctx:t,stop:()=>n.remove()}}const _t=["#f43f5e","#f59e0b","#10b981","#3b82f6","#8b5cf6","#ec4899","#eab308"];function Vs(n,e){const{ctx:t,stop:s}=wt();if(!t)return s(),()=>{};const a=e.accent?[e.accent,..._t]:_t,i=[],o=n.getBoundingClientRect(),l=o.left+o.width/2,c=Math.min(o.top+40,window.innerHeight-20),d=(x,I,S)=>{const H=S?Math.PI*(1.15+.7*Math.random()):0,E=S?9+Math.random()*8:0;return{x,y:I,vx:S?Math.cos(H)*E*(Math.random()<.5?1:-1):(Math.random()-.5)*1.5,vy:S?Math.sin(H)*E:1+Math.random()*2,w:6+Math.random()*5,h:8+Math.random()*7,angle:Math.random()*Math.PI,spin:(Math.random()-.5)*.3,color:a[Math.floor(Math.random()*a.length)],wobble:Math.random()*Math.PI*2}},h=Math.round(90*oe(e));for(let x=0;x{if(_>=w){window.clearInterval(u);return}i.push(d(Math.random()*window.innerWidth,-20,!1)),_++},2e3/w);let b=0;const g=performance.now(),p=x=>{t.clearRect(0,0,window.innerWidth,window.innerHeight);let I=!1;for(const S of i)S.vy+=.16,S.vx*=.99,S.vy*=.985,S.wobble+=.1,S.x+=S.vx+Math.sin(S.wobble)*.8,S.y+=S.vy,S.angle+=S.spin,S.y{window.clearInterval(u),window.cancelAnimationFrame(b),s()}}function Js(n){const{ctx:e,stop:t}=wt();if(!e)return t(),()=>{};const s=[],a=[],i=Math.round(6*oe(n))+2;let o=0;const l=()=>{s.push({x:window.innerWidth*(.15+.7*Math.random()),y:window.innerHeight,vy:-(9+Math.random()*4),targetY:window.innerHeight*(.15+.3*Math.random()),hue:Math.floor(Math.random()*360)}),o++};l();const c=window.setInterval(()=>{if(o>=i){window.clearInterval(c);return}l()},3500/i),d=u=>{const b=Math.round(70*oe(n));for(let g=0;g{e.clearRect(0,0,window.innerWidth,window.innerHeight);for(let g=s.length-1;g>=0;g--){const p=s[g];p.y+=p.vy,p.vy+=.08,e.fillStyle=`hsl(${p.hue} 90% 65%)`,e.fillRect(p.x-1.5,p.y,3,10),(p.y<=p.targetY||p.vy>=-1)&&(d(p),s.splice(g,1))}for(let g=a.length-1;g>=0;g--){const p=a[g];if(p.x+=p.vx,p.y+=p.vy,p.vy+=.045,p.vx*=.985,p.vy*=.985,p.life-=p.decay,p.life<=0){a.splice(g,1);continue}const x=p.life*(.7+.3*Math.random());e.globalAlpha=Math.max(0,x),e.fillStyle=`hsl(${p.hue} 95% ${55+25*p.life}%)`,e.beginPath(),e.arc(p.x,p.y,1.1+1.6*p.life,0,Math.PI*2),e.fill()}e.globalAlpha=1,!(o>=i&&s.length===0&&a.length===0)&&u-w<9e3?h=window.requestAnimationFrame(_):t()};return h=window.requestAnimationFrame(_),()=>{window.clearInterval(c),window.cancelAnimationFrame(h),t()}}function Qs(n,e){const t=e.icon||ve.sparkles,s=Math.round(22*oe(e)),a=[];let i=0;const o=()=>{const c=document.createElement("span");c.className="atf-success__spark",c.setAttribute("aria-hidden","true"),c.textContent=t,c.style.insetInlineStart=`${4+Math.random()*92}%`,c.style.animationDuration=`${2.6+Math.random()*1.8}s`,c.style.animationDelay=`${Math.random()*.3}s`,c.style.fontSize=`${14+Math.random()*14}px`,c.addEventListener("animationend",()=>c.remove()),n.append(c),a.push(c),i++};o();const l=window.setInterval(()=>{if(i>=s){window.clearInterval(l);return}o()},2800/s);return()=>{window.clearInterval(l),a.forEach(c=>c.remove())}}function en(n){const e=n.querySelector(".atf-success__message");if(!e)return()=>{};const t=e.innerHTML,s=e.textContent??"";if(!s)return()=>{};e.textContent="",e.classList.add("is-typing");const a=Math.min(45,3800/s.length);let i=0;const o=window.setInterval(()=>{i++,e.textContent=s.slice(0,i),i>=s.length&&(window.clearInterval(o),e.classList.remove("is-typing"),e.innerHTML=t)},a);return()=>{window.clearInterval(o),e.classList.remove("is-typing"),e.innerHTML=t}}const tn={min:-1,max:-1,sum:-1,avg:-1,round:-1,ceil:1,floor:1,abs:1,sqrt:1,pow:2},xt={"+":{precedence:1,right:!1},"-":{precedence:1,right:!1},"*":{precedence:2,right:!1},"/":{precedence:2,right:!1},"%":{precedence:2,right:!1},"^":{precedence:4,right:!0}};function sn(n,e,t=[]){if(!n||!n.trim()||n.length>2e3)return null;const s=nn(n,e,t),a=ln(s);if(!a)return null;const i=dn(a);if(!i)return null;const o=hn(i);return o===null||!Number.isFinite(o)?null:o}function nn(n,e,t){return n.replace(/\{([a-zA-Z0-9_]+)(?:\.([a-zA-Z0-9_]+))?\}/g,(s,a,i,o)=>an(n,o,s.length,a,i??"",e,t))}function an(n,e,t,s,a,i,o){const l=rn(o,s),c=Object.prototype.hasOwnProperty.call(i,s)?i[s]:null;if(a===""){const u=l?.type==="repeater"?kt(c).length:je(c,l);return St(u)}const h=(l?.fields??[]).find(u=>u.id===a)??null,w=kt(c).map(u=>je(u[a]??"",h));if(!w.length)return"0";const _=w.map(St);return on(n,e,t)?_.join(", "):_.length===1?_[0]:`( ${_.join(" + ")} )`}function rn(n,e){for(const t of n){if(t.id===e)return t;for(const s of t.fields??[])if(s.id===e)return s}return null}function on(n,e,t){const s=n.slice(e+t).replace(/^\s+/,"");if(s===""||s[0]!==")")return!1;const a=n.slice(0,e).replace(/\s+$/,""),i=/([a-zA-Z_][a-zA-Z0-9_]*)\s*\($/.exec(a);return!!i&&["sum","avg","min","max"].includes(i[1].toLowerCase())}function kt(n){if(!Array.isArray(n))return[];const e=[];for(const t of n){if(!t||typeof t!="object"||Array.isArray(t))continue;Object.values(t).some(a=>a!==""&&a!==null&&a!==void 0&&a!==!1&&!(Array.isArray(a)&&!a.length))&&e.push(t)}return e}function St(n){const e=n.toFixed(10).replace(/0+$/,"").replace(/\.$/,"");return e===""||e==="-"?"0":e}function je(n,e){if(typeof n=="boolean")return n?1:0;if(Array.isArray(n))return n.reduce((a,i)=>a+je(i,e),0);if(n==null||n==="")return 0;const t=e?.choices??[];for(const a of t)if(String(a.value)===String(n)){if(typeof a.price=="number")return a.price;if(typeof a.points=="number")return a.points;break}const s=Number(n);return Number.isFinite(s)?s:0}function ln(n){const e=[];let t=0;for(;ti.precedence||l.precedence===i.precedence&&!i.right){e.push(t.pop());continue}break}t.push(a);break}case"(":t.push(a);break;case")":{for(;t.length&&t[t.length-1].type!=="(";)e.push(t.pop());if(!t.length)return null;if(t.pop(),t.length&&t[t.length-1].type==="function"){const i=t.pop();i.arity=s.length?s.pop():1,e.push(i)}break}}for(;t.length;){const a=t.pop();if(a.type==="(")return null;e.push(a)}return e}function hn(n){const e=[];for(const t of n)switch(t.type){case"number":e.push(t.value);break;case"unary":{if(!e.length)return null;e.push(-e.pop());break}case"operator":{if(e.length<2)return null;const s=e.pop(),a=e.pop();switch(t.value){case"+":e.push(a+s);break;case"-":e.push(a-s);break;case"*":e.push(a*s);break;case"/":e.push(s===0?0:a/s);break;case"%":e.push(s===0?0:a%s);break;case"^":e.push(Math.pow(a,s));break;default:return null}break}case"function":{const s=t.arity??1;if(e.lengtht+s,0);case"avg":return e.reduce((t,s)=>t+s,0)/e.length;case"round":{const t=Math.max(-10,Math.min(10,Math.trunc(e[1]??0))),s=Math.pow(10,t);return Math.round(e[0]*s)/s}case"ceil":return Math.ceil(e[0]);case"floor":return Math.floor(e[0]);case"abs":return Math.abs(e[0]);case"sqrt":return e[0]<0?0:Math.sqrt(e[0]);case"pow":return Math.pow(e[0],e[1]??2);default:return null}}const pn=["sum","min","max","avg","round","ceil","floor","abs","sqrt","pow"],Lt=["number","range","scale","rating","total","select","multiselect","radio","checkboxes","switch","quiz"];function Ue(n,e){return n.filter(t=>t.id!==e&&Lt.includes(t.type))}function fn(n){const e=[];for(const t of n){if(t.type!=="repeater")continue;const s=t.label||t.id;e.push({label:`${s} (how many)`,insert:`{${t.id}}`});for(const a of t.fields??[])Lt.includes(a.type)&&e.push({label:`${s} · ${a.label||a.id}`,insert:`{${t.id}.${a.id}}`})}return e}function mn(n,e){const t={};Ue(n,e).forEach((s,a)=>{t[s.id]=a+1});for(const s of n){if(s.type!=="repeater"||s.id===e)continue;const a=s.fields??[],i=o=>Object.fromEntries(a.map((l,c)=>[l.id,c+1+o]));t[s.id]=[i(0),i(1)]}return t}function bn(n){const e=r("div",{class:"atfb-overlay"}),t=()=>{e.remove(),document.removeEventListener("keydown",s)},s=u=>{u.key==="Escape"&&t()},a=r("textarea",{class:"atfb-input atfb-formula__input",attrs:{rows:"3","aria-label":"Formula"}});a.value=String(n.field.formula??"");const i=r("p",{class:"atfb-formula__result",attrs:{"aria-live":"polite"}}),o=mn(n.fields,n.field.id),l=()=>{const u=a.value.trim();if(u===""){i.textContent="Empty. Reference a question below to start.",i.classList.remove("is-error");return}const b=sn(u,o,n.fields);if(b===null){i.textContent="This does not compute yet — check the braces and parentheses.",i.classList.add("is-error");return}const g=Ue(n.fields,n.field.id).map((p,x)=>`${p.label||p.id} = ${x+1}`).concat(n.fields.filter(p=>p.type==="repeater"&&p.id!==n.field.id).map(p=>`${p.label||p.id} = 2 sample rows`)).join(", ");i.textContent=`With sample answers (${g}): ${b}`,i.classList.remove("is-error")};a.addEventListener("input",l);const c=(u,b,g=0)=>r("button",{class:"atfb-formula__chip",type:"button",text:u,on:{click:()=>{if(ht(a,b),g>0){const p=(a.selectionStart??a.value.length)-g;a.setSelectionRange(p,p)}}}}),d=Ue(n.fields,n.field.id),h=fn(n.fields),w=r("div",{class:"atfb-formula__chips",children:d.length||h.length?[...d.map(u=>c(u.label||u.id,`{${u.id}}`)),...h.map(u=>c(u.label,u.insert))]:[r("p",{class:"atfb-hint",text:"No number-shaped questions yet — add a number, scale or priced choice field and it appears here."})]}),_=r("div",{class:"atfb-formula__chips",children:pn.map(u=>c(`${u}()`,`${u}()`,1))});e.append(r("div",{class:"atfb-modal atfb-formula",attrs:{role:"dialog","aria-label":"Formula editor"},children:[r("h2",{text:"Formula"}),a,i,y("Your questions",w,"Click one to reference its answer."),y("Functions",_),r("div",{class:"atfb-modal__actions",children:[$("Cancel",t),$("Save formula",()=>{n.onSave(a.value.trim()),t()},"primary")]})]})),e.addEventListener("click",u=>{u.target===e&&t()}),document.addEventListener("keydown",s),n.root.append(e),l(),a.focus(),a.setSelectionRange(a.value.length,a.value.length)}const gn="allterrain-forms/form";function vn(){return window.wp?.os?.relations??null}function Et(n){const e=n.closest("[data-window-id], .os-window");if(!e)return null;const t=e.getAttribute("data-window-id");if(t)return t;const s=e.id??"";return s?s.replace(/^wp-window-/,""):null}const yn=6e3,Ct=120;function $t(n,e){const t=vn();if(ze.set(n,e),!t?.set)return;const s=i=>{if(ye.get(n)!==a)return;const o=Et(n);if(!o){Date.now()s(i),Ct);return}try{t.set(o,e)}catch(c){At||(At=!0,console.error("[AllTerrain Forms] The shell refused a window identity.",c,e)),ye.delete(n);return}if(!e||t.get?.(o)?.id===e.id||Date.now()>=i){ye.delete(n);return}window.setTimeout(()=>s(i),Ct)},a=Symbol("atf-identity");ye.set(n,a),s(Date.now()+yn)}let At=!1;const ye=new WeakMap,ze=new Map;function wn(){for(const[n,e]of ze){if(!n.isConnected){ze.delete(n);continue}$t(n,e)}}if(typeof document<"u")for(const n of["os-window-content-loaded","os-window-opened"])document.addEventListener(n,()=>wn());function _n(n,e){return{type:gn,id:n.id,label:n.title||"Untitled form",related:[{id:`allterrain-forms/entries-${n.id}`,label:"Entries for this form",url:`${e}admin.php?page=allterrain-forms-entries&form=${n.id}`,group:"allterrain-forms",groupLabel:"Forms",icon:"dashicons-list-view"}]}}const we={letters:{label:"Letters",chars:"A-Za-zÀ-ÖØ-öø-ÿ"},numbers:{label:"Numbers",chars:"0-9"},spaces:{label:"Spaces",chars:" "},punctuation:{label:`Punctuation ( . , ! ? ' " - )`,chars:`.,!?'"()\\-:;`},symbols:{label:"Symbols ( @ # & _ / + )",chars:"@#&_/+*%="}};function xn(){return{mode:"blocks",starts:"",ends:"",contains:"",notContains:"",chars:[],minLen:"",maxLen:"",regex:"",message:"",tests:[]}}function Tt(n){const e=xn();let t;try{t=JSON.parse(n)}catch{return e}if(!t||typeof t!="object")return e;const s=t;e.mode=s.mode==="regex"?"regex":"blocks";for(const a of["starts","ends","contains","notContains","minLen","maxLen","regex","message"])typeof s[a]=="string"&&(e[a]=s[a]);return Array.isArray(s.chars)&&(e.chars=s.chars.filter(a=>typeof a=="string"&&a in we)),Array.isArray(s.tests)&&(e.tests=s.tests.filter(a=>typeof a=="string").slice(0,10)),e}function _e(n){return n.replace(/[.*+?^${}()|[\]\\/]/g,"\\$&")}function xe(n){if(n.mode==="regex")return n.regex.trim();const e=[],t=n.minLen.trim(),s=n.maxLen.trim();(t!==""||s!=="")&&e.push(`(?=.{${t===""?"0":parseInt(t,10)||0},${s===""?"":parseInt(s,10)||""}}$)`),n.contains!==""&&e.push(`(?=.*${_e(n.contains)})`),n.notContains!==""&&e.push(`(?!.*${_e(n.notContains)})`),n.starts!==""&&e.push(`(?=${_e(n.starts)})`),n.ends!==""&&e.push(`(?=.*${_e(n.ends)}$)`);const a=n.chars.map(o=>we[o]?.chars??"").join(""),i=a?`[${a}]*$`:".*$";return!e.length&&!a?"":`^${e.join("")}${i}`}function Mt(n){if(n.mode==="regex")return n.regex.trim()?`Matches the expression ${n.regex.trim()}`:"";const e=[];if(n.starts&&e.push(`starts with “${n.starts}”`),n.ends&&e.push(`ends with “${n.ends}”`),n.contains&&e.push(`contains “${n.contains}”`),n.notContains&&e.push(`never contains “${n.notContains}”`),n.chars.length){const i=n.chars.map(o=>(we[o]?.label??o).split(" (")[0].toLowerCase());e.push(`uses only ${i.join(" and ")}`)}const t=n.minLen.trim(),s=n.maxLen.trim();return t&&s?e.push(`is ${t}–${s} characters long`):t?e.push(`is at least ${t} characters long`):s&&e.push(`is at most ${s} characters long`),e.length?`The answer ${e.length>1?`${e.slice(0,-1).join(", ")}, and ${e[e.length-1]}`:e[0]}.`:""}function kn(n,e){const t=xe(n);if(t==="")return null;try{return new RegExp(t).test(e)}catch{return null}}function Sn(){return window.wp?.os?.windowManager??null}const Ft="allterrain-forms-validation";function Ln(n){const e=Sn(),t=Et(n.root);if(e?.openChild&&t){En(e,t,n);return}Cn(n)}function En(n,e,t){const s=`${Ft}-${t.field.id}`;n.getById?.(s)&&n.remove?.(s);const a={saved:!1},i=()=>{const o=n.getById?.(s);o?.close?o.close():n.remove?.(s)};n.openChild?.(e,{id:s,baseId:Ft,url:`#${s}`,title:`Custom rule — ${t.field.label||"this question"}`,icon:"dashicons-yes-alt",native:!0,width:560,height:700,minWidth:440,minHeight:480,ephemeral:!0,autofocus:".atfb-valwin__pane:not([hidden]) input, .atfb-valwin__pane:not([hidden]) textarea",render:o=>{const l=r("div",{class:"atfa atfb-valwin atfb-valwin--window"}),c=r("div",{class:"atfb-valwin__scroll"});l.append(c),It(c,t,a,i,"window");const d=c.querySelector(".atfb-modal__actions");d&&l.append(d),o.append(l)},onClose:()=>{a.saved||t.onCancel?.()}})}function Cn(n){const e=r("div",{class:"atfb-overlay"}),t={saved:!1},s=()=>{e.remove(),document.removeEventListener("keydown",a),t.saved||n.onCancel?.()},a=o=>{o.key==="Escape"&&s()},i=r("div",{class:"atfb-modal atfb-valwin",attrs:{role:"dialog","aria-label":"Custom validation rule"},children:[r("h2",{text:"Custom rule"})]});It(i,n,t,s,"overlay"),e.append(i),e.addEventListener("click",o=>{o.target===e&&s()}),document.addEventListener("keydown",a),n.root.append(e),e.querySelector(".atfb-valwin__pane:not([hidden]) input, .atfb-valwin__pane:not([hidden]) textarea")?.focus()}function It(n,e,t,s,a){const i=Tt(String(e.field.validationRecipe??""));i.mode==="blocks"&&xe(i)===""&&e.field.pattern&&(i.mode="regex",i.regex=String(e.field.pattern)),i.message||(i.message=String(e.field.messages?.invalid??""));const o=(f,M)=>{const C=r("input",{class:"atfb-input",value:i[f],placeholder:M,attrs:{type:"text"}});return C.addEventListener("input",()=>{i[f]=C.value,E()}),C},l=(f,M)=>{const C=r("input",{class:"atfb-input atfb-valwin__len",value:i[f],attrs:{type:"number",min:"0","aria-label":M}});return C.addEventListener("input",()=>{i[f]=C.value,E()}),C},c=r("div",{class:"atfb-valwin__chars",children:Object.entries(we).map(([f,M])=>O(M.label,i.chars.includes(f),C=>{i.chars=C?[...i.chars,f]:i.chars.filter(W=>W!==f),E()}))}),d=r("div",{class:"atfb-valwin__pane",children:[y("Starts with",o("starts","e.g. AT-")),y("Ends with",o("ends","e.g. -2026")),y("Must contain",o("contains","e.g. @")),y("Must not contain",o("notContains","e.g. spaces? type one")),y("Only these characters",c,"Leave every box unticked to allow anything."),y("Length",r("div",{class:"atfb-valwin__lengths",children:[r("span",{text:"between"}),l("minLen","Minimum length"),r("span",{text:"and"}),l("maxLen","Maximum length"),r("span",{text:"characters"})]}),"Leave a box empty for no limit.")]}),h=r("textarea",{class:"atfb-input atfb-valwin__regex",attrs:{rows:"2","aria-label":"Regular expression",placeholder:"^AT-[0-9]{4}$"}});h.value=i.regex,h.addEventListener("input",()=>{i.regex=h.value,E()});const w=r("div",{class:"atfb-valwin__pane",children:[y("Expression",h,"A regular expression, without slashes. Checked against the whole answer only if you anchor it with ^ and $.")]}),_=[["blocks","Easy blocks"],["regex","Expression (advanced)"]],u=f=>{i.mode=f,d.hidden=f!=="blocks",w.hidden=f!=="regex",E()},g=(()=>{if(Y("os-segmented")&&Y("os-segment")){const C=document.createElement("os-segmented");C.setAttribute("value",i.mode),C.setAttribute("label","How to write the rule"),C.classList.add("atfb-valwin__tabs");for(const[W,B]of _){const le=document.createElement("os-segment");le.setAttribute("value",W),le.textContent=B,C.append(le)}return C.addEventListener("os-pick",W=>{const B=W.detail?.value;(B==="blocks"||B==="regex")&&(C.setAttribute("value",B),u(B))}),C}const f=r("div",{class:"atfb-valwin__tabs",attrs:{role:"tablist"}}),M=()=>{f.replaceChildren(..._.map(([C,W])=>{const B=i.mode===C;return r("button",{class:`atfb-valwin__tab${B?" is-active":""}`,type:"button",text:W,attrs:{role:"tab","aria-selected":B?"true":"false"},on:{click:()=>{u(C),M()}}})}))};return M(),f})();d.hidden=i.mode!=="blocks",w.hidden=i.mode!=="regex";const p=r("input",{class:"atfb-input",value:i.message,placeholder:"That is not in the expected format.",attrs:{type:"text"}});p.addEventListener("input",()=>{i.message=p.value});const x=r("p",{class:"atfb-valwin__summary",attrs:{"aria-live":"polite"}}),I=r("div",{class:"atfb-valwin__samples"}),S=f=>{const M=r("span",{class:"atfb-valwin__verdict",attrs:{"aria-live":"polite"}}),C=r("input",{class:"atfb-input",value:f,placeholder:"Type a sample answer…",attrs:{type:"text"}});C.addEventListener("input",()=>E()),I.append(r("div",{class:"atfb-valwin__sample",children:[C,M]}))};for(const f of i.tests.length?i.tests:["","",""])S(f);const H=()=>Array.from(I.querySelectorAll("input")).map(f=>f.value),E=()=>{const f=Mt(i);x.textContent=f||"Nothing yet — fill in a block above and the rule appears here in plain words.";for(const M of Array.from(I.querySelectorAll(".atfb-valwin__sample"))){const C=M.querySelector("input"),W=M.querySelector(".atfb-valwin__verdict");if(!C||!W)continue;const B=C.value===""?null:kn(i,C.value);W.textContent=B===null?"·":B?"✓ passes":"✗ fails",W.classList.toggle("is-pass",B===!0),W.classList.toggle("is-fail",B===!1)}};n.append(r("p",{class:"atfb-hint",text:`Describe what a good answer to “${e.field.label||"this question"}” looks like — no code needed.`}),g,d,w,r("div",{class:"atfb-valwin__try",children:[r("h3",{text:"Try it out"}),x,I,$("Add another sample",()=>{S(""),E()},"ghost","plus-alt2")]}),y("When it fails, say",p,"Shown to the visitor when their answer breaks the rule. Leave empty for the default wording."),r("div",{class:"atfb-modal__actions",children:[$(a==="window"?"Close without saving":"Cancel",s),$("Save rule",()=>{const f=xe(i);i.tests=H().filter(M=>M!=="").slice(0,10),t.saved=!0,e.onSave({pattern:f,recipe:i,message:i.message.trim()}),s()},"primary")]})),E()}const $n=["Contact","Numbers & codes","Text shape","Web"],Dt=[{slug:"email",label:"An email address",group:"Contact",example:"jane@example.com",pattern:"^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$",message:"That does not look like an email address."},{slug:"phone",label:"A phone number",group:"Contact",example:"+34 612 345 678",pattern:"^(?=(?:[^0-9]*[0-9]){5,})\\+?[0-9 ().-]{5,24}$",message:"That does not look like a phone number."},{slug:"handle",label:"A username or @handle",group:"Contact",example:"@yourname",pattern:"^@?[A-Za-z0-9_]{2,30}$",message:"That does not look like a username."},{slug:"digits",label:"Numbers only",group:"Numbers & codes",example:"12345",pattern:"^[0-9]+$",message:"Numbers only, please."},{slug:"decimal",label:"A number, decimals allowed",group:"Numbers & codes",example:"3.14",pattern:"^-?[0-9]+([.,][0-9]+)?$",message:"That does not look like a number."},{slug:"price",label:"A price",group:"Numbers & codes",example:"19.99",pattern:"^[0-9]+([.,][0-9]{1,2})?$",message:"That does not look like a price."},{slug:"zip_us",label:"A ZIP code (US)",group:"Numbers & codes",example:"90210",pattern:"^[0-9]{5}(-[0-9]{4})?$",message:"That does not look like a ZIP code."},{slug:"postcode_uk",label:"A postcode (UK)",group:"Numbers & codes",example:"SW1A 1AA",pattern:"^[A-Za-z]{1,2}[0-9][A-Za-z0-9]? ?[0-9][A-Za-z]{2}$",message:"That does not look like a postcode."},{slug:"iban",label:"An IBAN",group:"Numbers & codes",example:"DE89 3704 0044 0532 0130 00",pattern:"^[A-Za-z]{2}[0-9]{2}(?: ?[A-Za-z0-9]){10,32}$",message:"That does not look like an IBAN."},{slug:"credit_card",label:"A card number",group:"Numbers & codes",example:"4242 4242 4242 4242",pattern:"^[0-9](?:[0-9 -]{9,21})?[0-9]$",message:"That does not look like a card number.",luhn:!0},{slug:"letters",label:"Letters only",group:"Text shape",example:"María López",pattern:"^[\\p{L}\\p{M} .'’-]+$",message:"Letters only, please."},{slug:"alphanumeric",label:"Letters and numbers only",group:"Text shape",example:"abc123",pattern:"^[A-Za-z0-9]+$",message:"Letters and numbers only, please."},{slug:"no_spaces",label:"One word, no spaces",group:"Text shape",example:"one-word",pattern:"^\\S+$",message:"No spaces allowed."},{slug:"url",label:"A web address",group:"Web",example:"https://example.com",pattern:"^(https?://)?([A-Za-z0-9-]+\\.)+[A-Za-z]{2,}([/?#]\\S*)?$",message:"That does not look like a web address."},{slug:"ip",label:"An IP address",group:"Web",example:"192.168.0.1",pattern:"^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])$",message:"That does not look like an IP address."},{slug:"slug",label:"A URL slug",group:"Web",example:"my-page-title",pattern:"^[a-z0-9]+(-[a-z0-9]+)*$",message:"Lowercase letters, numbers and dashes only."},{slug:"hex_color",label:"A hex colour",group:"Web",example:"#3366ff",pattern:"^#?([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$",message:"That does not look like a colour code."}];function An(n){return Dt.find(e=>e.slug===n)??null}function Ye(){return window.wp?.os??null}const qt="allterrain-forms/preview",Ge="allterrain-forms-preview";function Tn(n){const e=Ye();if(!e?.registerTitleBarButton)return()=>{};const t=()=>{try{e.registerTitleBarButton({id:qt,label:"Preview this form",icon:"dashicons-visibility",placement:"right",order:90,match:s=>{const a=s?.id??s?.config?.id??"";return a==="allterrain-forms"||a.startsWith("allterrain-forms#")},onClick:()=>void Ot(n),owner:"allterrain-forms-builder"})}catch{}};return e.ready?e.ready(t):t(),()=>{try{e.unregisterTitleBarButton?.(qt)}catch{}}}async function Ot(n){n.isDirty()&&await n.save();const e=n.current();e&&Nt(e.id,e.title,e.previewUrl)}function Nt(n,e,t){const s=Ye();if(!s?.windowManager?.open){window.open(t,"_blank","noopener");return}s.windowManager.open({id:`${Ge}-${n}`,baseId:Ge,url:t,title:`Preview: ${e}`,icon:"dashicons-visibility"})}function Mn(n,e,t){if(!Ye()?.windowManager?.open||!document.querySelector(`[data-window-id="${Ge}-${n}"]`))return;const i=t.includes("?")?"&":"?";Nt(n,e,`${t}${i}alltfo_r=${Date.now()}`)}const se="allterrain-forms/field",Fn=["openstation/file","desktop-mode/file","openstation/attachment"],Rt="allterrain-forms/logic-map-v2";function J(n,e){return n.dataset.atfbBind=e,n}const Pt={level:{key:"level",label:"Heading level",control:"select",hint:"Headings should step down one at a time.",options:[{value:"2",label:"Heading 2"},{value:"3",label:"Heading 3"},{value:"4",label:"Heading 4"},{value:"5",label:"Heading 5"},{value:"6",label:"Heading 6"}]},content:{key:"content",label:"HTML",control:"textarea",hint:"Shown as written. Scripts are stripped when the form is saved."},consenttext:{key:"consentText",label:"What they are agreeing to",control:"textarea",hint:"Shown beside the tick box. Links are allowed."},height:{key:"height",label:"Height",control:"number",hint:"In pixels."},columns:{key:"columns",label:"Columns",control:"number",hint:"How many pictures sit side by side."},multiple:{key:"multiple",label:"Let them choose more than one",control:"checkbox"},inline:{key:"inline",label:"Lay the options out in a row",control:"checkbox"},other:{key:"other",label:"Offer an “Other” box",control:"checkbox",hint:"Adds a final option with a box to type in."},filetypes:{key:"filetypes",label:"Accepted file types",control:"commas",hint:"Extensions, separated by commas. Empty accepts anything the site allows."},maxsize:{key:"maxsize",label:"Largest file",control:"number",hint:"In megabytes."},maxfiles:{key:"maxfiles",label:"How many files",control:"number"},minrows:{key:"minRows",label:"Fewest rows",control:"number",also:{key:"maxRows",label:"Most rows"}},itemlabel:{key:"itemLabel",label:"What is one row called?",control:"text",hint:"Names each card — “Attendee 1”, “Attendee 2” — and the Remove button. Also editable on the card itself."},endlabels:{key:"minLabel",label:"Label at the low end",control:"text",also:{key:"maxLabel",label:"Label at the high end"}},points:{key:"points",label:"Points if correct",control:"number"},minchoices:{key:"minChoices",label:"Fewest they may pick",control:"number",also:{key:"maxChoices",label:"Most they may pick"}}},In={label:"the canvas edits it in place, and the inspector mirrors it",placeholder:"edited by typing into the control on the canvas",hint:"edited under the control on the canvas",nextlabel:"edited on the page break’s own Next button",prevlabel:"edited on the page break’s own Back button",addlabel:"edited on the repeater’s own Add button",choices:"the choices editor, and the option list on the canvas",required:"the toggle on the card",width:"its own row",css:"its own row",prefill:"its own section",logic:"the conditional logic section",formula:"its own row, with the currency that goes with it",currency:"rendered with the formula",correct:"the choices editor marks the right answer",default:"its own row, typed to match what the field stores",rows:"a row count on a textarea; the statement list on a Likert matrix",parts:"a tick box per part, listed by the server",min:"the validation section, paired with max",max:"the validation section, paired with min",step:"the validation section",minlength:"the validation section, paired with maxlength",maxlength:"the validation section, paired with minlength",mindate:"the validation section, paired with maxdate",maxdate:"the validation section, paired with mindate",mintime:"the validation section, paired with maxtime",maxtime:"the validation section, paired with mintime",pattern:"the validation section",unique:"the validation section",maxchoices:"rendered with minchoices",maxrows:"rendered with minrows"};function Bt(n,e,t){const s=n[e.key],a=i=>t(e.key,i);if(e.control==="checkbox")return O(e.label,!!s,a);if(e.control==="commas"){const i=Array.isArray(s)?s:[];return y(e.label,T(i.join(", "),o=>{a(o.split(",").map(l=>l.trim().replace(/^\./,"").toLowerCase()).filter(Boolean))}),e.hint)}return e.control==="select"?y(e.label,D(String(s??""),e.options??[],a),e.hint):e.control==="textarea"?y(e.label,pe(String(s??""),a),e.hint):e.control==="number"?y(e.label,G(String(s??""),a),e.hint):y(e.label,J(T(String(s??""),a),e.key),e.hint)}function Ht(n,e){const t=new Set(n.map(a=>a.key).filter(Boolean));let s=n.length+1;return e.split(` -`).map(a=>a.trim()).filter(Boolean).map((a,i)=>{const o=n[i]?.key;if(o)return{key:o,label:a};for(;t.has(`r${s}`);)s+=1;return t.add(`r${s}`),{key:`r${s}`,label:a}})}function Wt(n,e,t){const s=n.findIndex(i=>i.id===e);if(s<0)return null;const a=Math.max(0,Math.min(t,n.length-1));return a===s?null:{from:s,to:a}}const Xe=[{value:"user:email",label:"Their email address",group:"About the person filling it in",tag:"{user:email}"},{value:"user:display_name",label:"Their name",group:"About the person filling it in",tag:"{user:display_name}"},{value:"user:first_name",label:"Their first name",group:"About the person filling it in"},{value:"user:last_name",label:"Their last name",group:"About the person filling it in"},{value:"user:login",label:"Their username",group:"About the person filling it in"},{value:"date:today",label:"Today’s date",group:"The date and time",tag:"{date}"},{value:"date:now",label:"The time right now",group:"The date and time",tag:"{time}"},{value:"site",label:"This site’s name",group:"About this site",tag:"{site}"},{value:"site:url",label:"This site’s address",group:"About this site",tag:"{site:url}"},{value:"site:admin_email",label:"The site administrator’s email",group:"About this site",tag:"{admin_email}"}],Dn=["About the person filling it in","The date and time","About this site"],Ze=(n,e)=>j?.i18n?.[n]??e;class jt{constructor(e){this.config=null,this.themes=[],this.forms=[],this.form=null,this.schema=null,this.selected=null,this.tab="build",this.logicMap=null,this.canvasTheme=r("style"),this.canvasThemeSignature="",this.openSections=new Map,this.logicMapOn=is(Rt)!=="off",this.dirty=!1,this.editGeneration=0,this.saveInFlight=!1,this.queuedSave=null,this.teardowns=[],this.canvasTarget=null,this.history=[],this.historyAt=-1,this.autosave=$e(()=>{this.save(!0)},2500),this.renderInspector=as(()=>{if(R(this.inspector),!this.schema||(this.root.classList.toggle("atfb--build-only-panes",this.tab!=="build"),this.tab!=="build"))return;const t=this.selected?this.locateField(this.selected):void 0,s=t?.field,a=t?.parent??null;if(this.root.classList.toggle("atfb--has-selection",!!s),!s){this.inspector.append(r("div",{class:"atfb-placeholder",children:[r("p",{text:"Select a field to change it."}),r("p",{class:"atfb-hint",text:"Drag a field from the palette, or press one to add it to the end."})]}));return}const i=this.config?.fieldTypes.find(c=>c.type===s.type),o=i?.supports??[],l=(c,d)=>{const h=this.liveField(s.id);h&&(h[c]=d,this.markDirty(),this.renderCanvas())};this.inspector.append(r("h3",{class:"atfb-inspector__title",text:i?.label??s.type})),a?this.inspector.append(r("p",{class:"atfb-hint atfb-inspector__crumb",text:`Inside ${a.label||"a repeater"} — the visitor answers this once per ${String(a.itemLabel??"")||"row"}.`}),r("p",{class:"atfb-hint",text:`Formulas aggregate it as {${a.id}.${s.id}} — e.g. sum( {${a.id}.${s.id}} ).`})):this.inspector.append(r("p",{class:"atfb-hint",text:`Reference this field as {field:${s.id}}`})),o.includes("label")&&this.inspector.append(y("Label",J(T(s.label,c=>l("label",c)),"label"))),o.includes("placeholder")&&this.inspector.append(y("Placeholder",J(T(s.placeholder,c=>l("placeholder",c)),"placeholder"))),o.includes("hint")&&this.inspector.append(y("Hint",J(T(s.hint,c=>l("hint",c)),"hint"),"Shown under the field, and read out with it.")),o.includes("nextlabel")&&this.inspector.append(y("Next button",J(T(String(s.nextLabel??""),c=>l("nextLabel",c)),"nextLabel"),"Leave empty for “Next”.")),o.includes("prevlabel")&&this.inspector.append(y("Back button",J(T(String(s.prevLabel??""),c=>l("prevLabel",c)),"prevLabel"),"Leave empty for “Back”.")),o.includes("addlabel")&&this.inspector.append(y("Add button",J(T(String(s.addLabel??""),c=>l("addLabel",c)),"addLabel"),"Leave empty for “Add another”.")),o.includes("required")&&this.inspector.append(O("Required",s.required,c=>l("required",c))),o.includes("width")&&this.inspector.append(y("Width",D(s.width,[{value:"full",label:"Full width"},{value:"two-thirds",label:"Two thirds"},{value:"half",label:"Half"},{value:"third",label:"One third"},{value:"quarter",label:"One quarter"}],c=>l("width",c)))),i?.choices&&this.inspector.append(this.renderChoicesEditor(s,l)),this.renderTypeSettings(s,i,o,l),(s.type==="total"||o.includes("formula"))&&this.inspector.append(y("Formula",r("div",{class:"atfb-formula__row",children:[T(String(s.formula??""),c=>l("formula",c)),$("Formula editor",()=>bn({root:this.root,fields:this.schema?.fields??[],field:s,onSave:c=>{l("formula",c),this.renderInspector()}}))]}),"Reference answers with braces — {f1} * {f2} + 10 — or open the editor and click them in."),y("Currency symbol",T(String(s.currency??""),c=>l("currency",c)))),this.inspector.append(this.renderValidationSection(s,o,l)),a||(this.inspector.append(this.renderLogicSection(s)),o.includes("prefill")&&this.inspector.append(this.prefillControl(s,l))),o.includes("css")&&this.inspector.append(y("CSS class",T(s.cssClass,c=>l("cssClass",c))))}),this.root=e,this.bar=e.querySelector("[data-atfb-bar]")??r("div"),this.palette=e.querySelector("[data-atfb-palette]")??r("div"),this.canvas=e.querySelector("[data-atfb-canvas]")??r("div"),this.inspector=e.querySelector("[data-atfb-inspector]")??r("div"),this.canvas.addEventListener("click",t=>{const s=t.target;if(!(this.tab!=="build"||!this.selected||s.closest("[data-atfb-card], button, os-button, a, input, textarea, select, os-select, label, [contenteditable]"))){this.selected=null;for(const a of this.canvas.querySelectorAll(".atfb-card.is-selected"))a.classList.remove("is-selected");this.renderInspector()}})}async start(){try{const[s,a,i]=await Promise.all([F.config(),F.listThemes(),F.listForms()]);this.config=s,this.themes=a,this.forms=i}catch(s){this.fail(s);return}this.teardowns.push(Jt([se])),this.teardowns.push(Tn({current:()=>this.form?{id:this.form.id,title:this.form.title,previewUrl:this.form.previewUrl}:null,isDirty:()=>this.dirty,save:()=>this.save(!0)}));const e=s=>{this.dirty&&(s.preventDefault(),s.returnValue="")};window.addEventListener("beforeunload",e),this.teardowns.push(()=>window.removeEventListener("beforeunload",e));const t=s=>{!(s.metaKey||s.ctrlKey)||s.key.toLowerCase()!=="z"||s.target?.closest("input, textarea, [contenteditable]")||(s.preventDefault(),this.travel(s.shiftKey?1:-1))};if(this.root.addEventListener("keydown",t),this.teardowns.push(()=>this.root.removeEventListener("keydown",t)),this.renderBar(),this.renderPalette(),this.forms.length){const s=ms("builder");await this.open(s&&this.forms.some(a=>a.id===s)?s:this.forms[0].id)}else this.renderFormsList()}destroy(){this.canvasTarget?.(),this.canvasTarget=null,this.teardowns.forEach(e=>e()),this.teardowns=[]}fail(e){R(this.bar),this.bar.append(r("p",{class:"atfb-error",text:e instanceof Error?e.message:"Something went wrong loading your forms."}))}renderBar(){R(this.bar);const e=D(String(this.form?.id??""),[...this.forms.map(a=>({value:String(a.id),label:a.title||"(untitled)"}))],a=>void this.open(Number(a)));e.setAttribute("aria-label","Choose a form");const t=r("input",{class:"atfb-title",type:"text",value:this.form?.title??"",placeholder:"Untitled form",attrs:{"aria-label":"Form title"},on:{input:a=>{this.form&&(this.form.title=a.target.value,this.markDirty())}}}),s=r("div",{class:"atfb-tabs",attrs:{role:"tablist"},children:[["build","Build"],["theme","Theme"],["settings","Settings"],["notify","Notifications"],["confirm","Confirmations"]].map(([a,i])=>r("button",{class:`atfb-tab${this.tab===a?" is-active":""}`,type:"button",text:i,attrs:{role:"tab","aria-selected":this.tab===a},on:{click:()=>{this.tab=a,this.renderBar(),this.renderInspector(),this.renderCanvas()}}}))});this.bar.append(r("div",{class:"atfb-bar__left",children:[this.forms.length>1?e:null,t]}),s,r("div",{class:"atfb-bar__right",children:[this.historyButton("undo","Undo",-1),this.historyButton("redo","Redo",1),$("New",()=>void this.showTemplates(),"secondary","plus-alt2"),$("Export",()=>void this.exportForm(),"secondary","download"),$("Import",()=>void this.importForm(),"secondary","upload"),$("Preview",()=>void this.preview(),"secondary","visibility"),$("Entries",()=>this.openEntries(),"secondary","list-view"),this.logicMapButton(),this.saveButton()]}))}liveField(e){return this.locateField(e)?.field}locateField(e){const t=this.schema?.fields??[];for(let s=0;sthis.rebindCanvas(),{once:!0});return}this.renderCanvas()}snapshot(){if(!this.schema)return;const e=JSON.stringify(this.schema);this.history[this.historyAt]!==e&&(this.history=this.history.slice(0,this.historyAt+1),this.history.push(e),this.history.length>60&&this.history.shift(),this.historyAt=this.history.length-1,this.syncHistoryButtons())}travel(e){const t=this.historyAt+e;!this.schema||t<0||t>=this.history.length||(this.historyAt=t,this.schema=JSON.parse(this.history[t]),this.schema.fields.some(s=>s.id===this.selected)||(this.selected=null),this.markDirty(),this.renderBar(),this.renderCanvas(),this.renderInspector())}historyButton(e,t,s){const a=$(t,()=>this.travel(s),"secondary",e);return a.setAttribute("data-atfb-history",String(s)),a.disabled=!this.canTravel(s),a}canTravel(e){const t=this.historyAt+e;return t>=0&&tURL.revokeObjectURL(s),1e3)}async importForm(){const e=r("input",{type:"file",attrs:{accept:"application/json,.json"}});e.addEventListener("change",async()=>{const t=e.files?.[0];if(t)try{const s=JSON.parse(await t.text());if(!s.schema)throw new Error("That file does not contain a form.");const a=await F.createForm({title:s.title??t.name.replace(/\.json$/i,""),schema:s.schema});this.forms.unshift({id:a.id,title:a.title,status:a.status,modified:a.modified,fields:a.schema.fields.length,theme:a.schema.settings.theme,entries:0,unread:0,views:0,submissions:0,shortcode:a.shortcode}),this.form=a,this.schema=a.schema,this.selected=null,this.dirty=!1,this.history=[],this.historyAt=-1,this.snapshot(),this.renderBar(),this.renderCanvas(),this.renderInspector(),N("Form imported",a.title)}catch(s){N("Could not import that file",s instanceof Error?s.message:"","error")}}),e.click()}saveButton(){const e=$("Save",()=>void this.save(),"primary");return e.disabled=!this.dirty,e.setAttribute("data-atfb-save",""),e}markDirty(){this.dirty=!0,this.editGeneration+=1;const e=this.bar.querySelector("[data-atfb-save]");e&&(e.disabled=!1),this.autosave()}async save(e=!1){if(!this.form||!this.schema)return;if(this.saveInFlight){this.queuedSave={silent:e&&(this.queuedSave?.silent??!0)};return}this.saveInFlight=!0;const t=this.editGeneration;try{const s=await F.updateForm(this.form.id,{title:this.form.title,schema:this.schema});if(t===this.editGeneration){this.form=s,this.schema=s.schema,this.dirty=!1,this.rebindCanvas();const i=this.bar.querySelector("[data-atfb-save]");i&&(i.disabled=!0)}Ms(s.id);const a=this.forms.find(i=>i.id===s.id);a&&(a.title=s.title),Mn(s.id,s.title,s.previewUrl),e||N("Form saved",s.title)}catch(s){N(Ze("saveFailed","Could not save"),s instanceof Error?s.message:"","error")}finally{this.saveInFlight=!1;const s=this.queuedSave;this.queuedSave=null,s&&this.save(s.silent)}}async openFormById(e){await this.open(e)}async open(e){if(!(this.dirty&&!await ie("You have unsaved changes. Discard them?"))){try{this.form=await F.getForm(e),this.schema=this.form.schema,this.selected=null,this.dirty=!1,this.history=[],this.historyAt=-1,this.snapshot()}catch(t){this.fail(t);return}this.renderBar(),this.renderCanvas(),this.renderInspector(),this.announceIdentity()}}announceIdentity(){this.form&&$t(this.root,_n(this.form,j?.adminUrl??""))}async showTemplates(){if(!this.config)return;const e=r("div",{class:"atfb-overlay"}),t=i=>{i.key==="Escape"&&s()},s=()=>{e.remove(),document.removeEventListener("keydown",t)},a=r("div",{class:"atfb-templates",children:this.config.templates.map(i=>r("button",{class:"atfb-template",type:"button",on:{click:async()=>{s();try{const o=await F.createForm({template:i.slug});this.forms.unshift({id:o.id,title:o.title,status:o.status,modified:o.modified,fields:o.schema.fields.length,theme:o.schema.settings.theme,entries:0,unread:0,views:0,submissions:0,shortcode:o.shortcode}),this.form=o,this.schema=o.schema,this.selected=null,this.dirty=!1,this.history=[],this.historyAt=-1,this.snapshot(),this.renderBar(),this.renderCanvas(),this.renderInspector(),this.announceIdentity()}catch(o){N("Could not create the form",o instanceof Error?o.message:"","error")}}},children:[z(i.icon),r("strong",{text:i.label}),r("span",{text:i.description})]}))});e.append(r("div",{class:"atfb-modal",attrs:{role:"dialog","aria-label":"Start a new form"},children:[r("h2",{text:"Start a new form"}),a,this.archivedFormsSection(s),r("div",{class:"atfb-modal__actions",children:[$("Cancel",s)]})]})),e.addEventListener("click",i=>{i.target===e&&s()}),document.addEventListener("keydown",t),this.root.append(e),a.querySelector("button")?.focus()}archivedFormsSection(e){const t=r("div",{class:"atfb-archived"});return F.listArchivedForms().then(s=>{s.length&&t.append(r("h3",{class:"atfb-archived__title",text:"Or bring one back from the archive"}),...s.map(a=>r("div",{class:"atfb-archived__row",children:[r("div",{class:"atfb-archived__meta",children:[r("strong",{text:a.title||"(untitled)"}),r("span",{class:"atfb-hint",text:`${a.entries} ${a.entries===1?"entry":"entries"} · ${a.submissions} submissions · ${a.views} views`})]}),$("Restore",async()=>{try{const i=await F.unarchiveForm(a.id);e(),this.forms.unshift(i),N("Form restored",`${i.title||"(untitled)"} is back, with its entries and stats.`),await this.open(i.id)}catch(i){N("Could not restore the form",i instanceof Error?i.message:"","error")}},"secondary","undo")]})))}).catch(()=>{}),t}renderFormsList(){R(this.canvas),this.canvas.append(r("div",{class:"atfb-empty",children:[r("h2",{text:"No forms yet"}),r("p",{text:"Start from a template, or build one from nothing."}),$("New form",()=>void this.showTemplates(),"primary","plus-alt2")]}))}openEntries(){const e=window.wp?.os;if(e?.openWindow){e.openWindow("allterrain-forms-entries");return}window.location.assign(`${j?.adminUrl??""}admin.php?page=allterrain-forms-entries`)}renderPalette(){if(!this.config)return;R(this.palette);const e=new Map;for(const s of this.config.fieldTypes){const a=e.get(s.group)??[];a.push(s),e.set(s.group,a)}const t=r("input",{class:"atfb-input atfb-palette__search",type:"search",placeholder:"Search fields",attrs:{"aria-label":"Search field types"},on:{input:s=>{const a=s.target.value.toLowerCase().trim();this.palette.querySelectorAll(".atfb-chip").forEach(i=>{const o=(i.textContent??"").toLowerCase();i.hidden=a!==""&&!o.includes(a)}),this.palette.querySelectorAll(".atfb-group").forEach(i=>{const o=Array.from(i.querySelectorAll(".atfb-chip")).some(l=>!l.hidden);i.hidden=!o})}}});this.palette.append(t);for(const[s,a]of Object.entries(this.config.groups)){const i=e.get(s);i?.length&&this.palette.append(r("div",{class:"atfb-group",children:[r("h3",{class:"atfb-group__title",text:a}),r("div",{class:"atfb-group__items",children:i.map(o=>this.paletteChip(o))})]}))}}paletteChip(e){const t=r("button",{class:"atfb-chip",type:"button",title:e.description,attrs:{"data-atf-type":e.type},children:[z(e.icon),r("span",{text:e.label})]});return t.addEventListener("pointerdown",s=>{const a=r("div",{class:"atfb-chip atfb-chip--ghost",children:[z(e.icon),r("span",{text:e.label})]});Z().start({payload:Ee(se,t,{fieldType:e.type,isNew:!0},s,a),origin:s,onClickOnly:()=>this.addField(e.type)})}),t.addEventListener("click",s=>{Z().recentlyEndedDrag()&&s.preventDefault()}),t}renderCanvas(){if(R(this.canvas),!this.schema||!this.form){this.renderFormsList();return}if(this.tab!=="build"){this.canvas.append(this.renderTabCanvas());return}const e=r("div",{class:"atfb-canvas__list",attrs:{"data-atfb-list":""}});this.schema.fields.length||e.append(r("div",{class:"atfb-placeholder",text:Ze("emptyCanvas","Drag a field from the left to begin.")})),this.schema.fields.forEach((s,a)=>{e.append(this.renderFieldCard(s,a))});const t=r("div",{class:"atfb-canvas__inner",children:[r("p",{class:"atfb-shortcode",text:this.form.shortcode,title:"Paste this anywhere to place the form"}),e]});this.canvas.append(t),this.registerCanvasTarget(e),this.paintLogicMap(t),this.paintCanvasTheme()}prefillControl(e,t){const s=e.prefill.startsWith("query:"),a=Xe.some(_=>_.value===e.prefill),i=s?"query":a&&e.prefill||(e.prefill?"custom":""),o=r("div",{class:"atfb-prefill__detail"}),l=r("p",{class:"atfb-row__hint atfb-prefill__preview"}),c=_=>{if(o.replaceChildren(),l.replaceChildren(),_==="query"){const u=e.prefill.startsWith("query:")?e.prefill.slice(6):"";o.append(T(u,b=>{const g=b.trim();t("prefill",g?`query:${g}`:""),d(`query:${g}`)},"utm_source"),r("p",{class:"atfb-row__hint",text:"The name of the parameter in the link people arrive on."}))}_==="custom"&&o.append(T(e.prefill,u=>{t("prefill",u),d(u)},"myplugin:something"),r("p",{class:"atfb-row__hint",text:"For a source another plugin has added through alltfo_resolve_prefill."})),d(e.prefill)},d=_=>{if(l.replaceChildren(),!_)return;if(_.startsWith("query:")){const b=_.slice(6);b&&(l.textContent=`A visit to …/your-page/?${b}=abc opens the form with “abc” in it.`);return}const u=Xe.find(b=>b.value===_)?.tag;u&&qe(this.form?.id??0).then(b=>{for(const g of b)for(const p of g.items)if(p.tag===u){const x=_.startsWith("user:");if(!p.sample){l.textContent="Empty unless the visitor is signed in.";return}l.textContent=x?`Opens with “${p.sample}” for you — empty for a visitor who is not signed in.`:`Opens with “${p.sample}”.`;return}})};c(i);const h=r("select",{class:"atfb-input atfb-select",on:{change:_=>{const u=_.target.value;t("prefill",u==="query"||u==="custom"?"":u),c(u)}}});h.append(r("option",{value:"",text:"Nothing — leave it empty",attrs:{selected:i===""}}));for(const _ of Dn){const u=document.createElement("optgroup");u.label=_;for(const b of Xe.filter(g=>g.group===_))u.append(r("option",{value:b.value,text:b.label,attrs:{selected:b.value===i}}));h.append(u)}const w=document.createElement("optgroup");return w.label="From the link they arrived on",w.append(r("option",{value:"query",text:"A parameter in the web address",attrs:{selected:i==="query"}})),h.append(w,r("option",{value:"custom",text:"Something else (advanced)",attrs:{selected:i==="custom"}})),y("Pre-fill this with",r("div",{class:"atfb-prefill",children:[h,o,l]}),"What the box already contains when the form opens. They can still change it.")}renderCondition(e,t){const s=t.some(i=>i.kind==="field"&&i.missing),a=r("span",{class:`atfb-cond${s?" is-broken":""}`,attrs:{"aria-label":rt(t)},children:[z("randomize"),...t.map(i=>this.renderConditionToken(e,i))]});return a.addEventListener("pointerdown",i=>i.stopPropagation()),a.addEventListener("click",i=>i.stopPropagation()),a.addEventListener("keydown",i=>i.stopPropagation()),a}editCondition(e,t,s=!0,a=""){const i=this.liveField(e)?.logic;if(i){if(t(i),this.markDirty(),!s){this.logicMap?.setEdges(me(this.schema?.fields??[]));return}this.renderCanvas(),this.renderInspector(),a&&window.requestAnimationFrame(()=>{this.canvas.querySelector(`[data-cond="${CSS.escape(a)}"]`)?.focus()})}}condSelect(e,t,s,a,i){if(Y("os-select")&&Y("os-option")){const o=document.createElement("os-select");o.setAttribute("value",e),o.setAttribute("aria-label",a),o.setAttribute("data-cond",s),o.className="atfb-cond__control",o.title=a;for(const l of t){const c=document.createElement("os-option");c.setAttribute("value",l.value),c.textContent=l.label,o.append(c)}return o.addEventListener("os-pick",l=>{i(String(l.detail?.value??""))}),o}return r("select",{class:"atfb-cond__control atfb-cond__control--native",title:a,attrs:{"aria-label":a,"data-cond":s},on:{change:o=>i(o.target.value)},children:t.map(o=>r("option",{value:o.value,text:o.label,attrs:{selected:o.value===e}}))})}renderConditionToken(e,t){if(t.kind==="field"&&!t.missing)return r("button",{class:"atfb-cond__chip atfb-cond__chip--field",type:"button",text:t.text,title:"Go to this question",on:{click:a=>{a.stopPropagation(),this.selectField(t.fieldId),this.canvas.querySelector(`[data-atfb-card="${CSS.escape(t.fieldId)}"]`)?.scrollIntoView({block:"nearest",behavior:"smooth"})}}});if(t.kind==="verb")return r("button",{class:"atfb-cond__verb",type:"button",text:t.text,title:"Switch between showing and hiding this field when the condition matches.",attrs:{"data-cond":`${e.id}:verb`},on:{click:()=>this.editCondition(e.id,s=>{s.action=s.action==="hide"?"show":"hide"},!0,`${e.id}:verb`)}});if(t.kind==="join")return r("button",{class:"atfb-cond__join",type:"button",text:t.text,title:"Switch between needing every rule (and) or any one of them (or).",attrs:{"data-cond":`${e.id}:join`},on:{click:()=>this.editCondition(e.id,s=>{s.match=s.match==="all"?"any":"all"},!0,`${e.id}:join`)}});if(t.kind==="operator"){const s=`${e.id}:op:${t.ruleIndex}`;return this.condSelect(t.operator,Object.entries(Te).map(([a,i])=>({value:a,label:i})),s,"How the answer is compared",a=>this.editCondition(e.id,i=>{const o=i.rules[t.ruleIndex];o&&(o.operator=a)},!0,s))}return t.kind==="value"?this.renderConditionValue(e,t):r("span",{class:"atfb-cond__chip atfb-cond__chip--missing",text:t.text})}renderConditionValue(e,t){const s=`${e.id}:value:${t.ruleIndex}`,a=this.schema?.fields.find(c=>c.id===t.sourceId),i=(c,d)=>this.editCondition(e.id,h=>{const w=h.rules[t.ruleIndex];w&&(w.value=c)},d,s);if(a?.choices?.length){const c=a.choices.map(d=>({value:d.value,label:d.label||d.value}));return t.raw!==""&&!a.choices.some(d=>d.value===t.raw)&&c.unshift({value:t.raw,label:t.text}),this.condSelect(t.raw,c,s,"The answer that triggers this",d=>i(d,!0))}const o=["number","range","scale","rating","total"].includes(a?.type??""),l=r("input",{class:"atfb-cond__chip atfb-cond__chip--value atfb-cond__value",value:t.raw,title:"The answer that triggers this. Edit it here.",attrs:{type:"text","aria-label":"The answer that triggers this","data-cond":s,inputmode:o?"decimal":void 0,size:String(Math.max(2,Math.min(24,t.raw.length||2)))}});return l.addEventListener("input",()=>{l.size=Math.max(2,Math.min(24,l.value.length||2)),i(l.value,!1)}),l.addEventListener("change",()=>i(l.value,!0)),l.addEventListener("keydown",c=>{c.key==="Enter"&&(c.preventDefault(),i(l.value,!0))}),l}section(e,t,s,a=!1){const i=r("details",{class:"atfb-section",attrs:{open:this.openSections.get(e)??a},children:[r("summary",{text:t}),...s]});return i.addEventListener("toggle",()=>this.openSections.set(e,i.open)),i}logicMapButton(){const e=me(this.schema?.fields??[]).length>0,t=$(this.logicMapOn?"Hide logic":"Show logic",()=>{this.logicMapOn=!this.logicMapOn,rs(Rt,this.logicMapOn?"on":"off"),this.renderBar(),this.renderCanvas()},this.logicMapOn?"primary":"secondary","randomize");return t.title="Draw a line from each question to the ones it decides.",t.hidden=!e,t}paintLogicMap(e){this.logicMap?.destroy(),this.logicMap=null;const t=this.schema?.fields??[],s=me(t);if(!s.length||!this.logicMapOn)return;e.classList.add("has-logicmap");const a=new ws(e);a.setEdges(s),a.highlight(this.selected??""),this.logicMap=a,e.addEventListener("pointerover",i=>{const o=i.target.closest("[data-atfb-card]");a.highlight(o?.dataset.atfbCard??this.selected??"")}),e.addEventListener("pointerleave",()=>a.highlight(this.selected??""))}renderFieldCard(e,t){const s=this.config?.fieldTypes.find(d=>d.type===e.type),a=this.selected===e.id,i=this.schema?.fields??[],o=gs(e,i),l=ys(i).get(e.id)??0,c=r("div",{class:`atfb-card${a?" is-selected":""}`,attrs:{"data-atfb-card":e.id,"data-index":t,tabindex:"0",role:"button","aria-pressed":a,"aria-label":`${e.label||s?.label||e.type}, ${t+1} of ${this.schema?.fields.length??0}`},children:[r("div",{class:"atfb-card__bar",children:[r("div",{class:"atfb-card__grip",attrs:{"aria-hidden":"true"},children:[z("menu")]}),r("div",{class:"atfb-card__head",children:[z(s?.icon??"dashicons-forms"),r("span",{class:"atfb-card__type",text:s?.label??e.type}),this.requiredToggle(e),l?r("span",{class:"atfb-badge atfb-badge--controls",text:l===1?"controls 1 field":`controls ${l} fields`,title:"Other questions appear or disappear based on this answer."}):null]}),r("div",{class:"atfb-card__actions",children:[this.cardAction("admin-page","Duplicate",()=>this.duplicateField(e.id)),this.cardAction("trash","Delete",()=>void this.deleteField(e.id))]})]}),r("div",{class:"atfb-card__body",children:[dt(e,s,{edit:d=>{const h=this.liveField(e.id);if(!h)return;d(h),this.markDirty(),this.syncInspector(h);const w=this.selected?this.locateField(this.selected):void 0;w?.parent&&w.parent.id===h.id&&this.syncInspector(w.field)},restructure:d=>{const h=this.liveField(e.id);h&&(this.snapshot(),d(h),this.markDirty(),this.renderCanvas(),this.renderInspector())},types:d=>this.config?.fieldTypes.find(h=>h.type===d),selectedId:this.selected}),o.length?this.renderCondition(e,o):null]})]});return c.addEventListener("click",d=>{d.target.closest(".atfb-card__actions")||Z().recentlyEndedDrag()||this.selectField(e.id)}),c.addEventListener("keydown",d=>{if(d.key==="Enter"||d.key===" "){d.preventDefault(),this.selectField(e.id);return}d.altKey&&(d.key==="ArrowUp"||d.key==="ArrowDown")&&(d.preventDefault(),this.moveField(e.id,d.key==="ArrowUp"?t-1:t+1),window.requestAnimationFrame(()=>{this.canvas.querySelector(`[data-atfb-card="${CSS.escape(e.id)}"]`)?.focus()})),(d.key==="Delete"||d.key==="Backspace")&&(d.preventDefault(),this.deleteField(e.id))}),c.addEventListener("pointerdown",d=>{d.target.closest(".atfb-card__actions")||Z().start({payload:Ee(se,c,{fieldId:e.id,field:e,isNew:!1},d),origin:d})}),c}requiredToggle(e){return r("button",{class:`atfb-req${e.required?" is-on":""}`,type:"button",text:e.required?"Required":"Optional",title:e.required?"This must be answered. Click to make it optional.":"Click to make this required.",attrs:{"aria-pressed":e.required?"true":"false"},on:{pointerdown:s=>s.stopPropagation(),click:s=>{s.stopPropagation(),e.required=!e.required,this.markDirty(),this.renderCanvas(),this.renderInspector()}}})}cardAction(e,t,s){return r("button",{class:"atfb-card__action",type:"button",title:t,attrs:{"aria-label":t},on:{click:a=>{a.stopPropagation(),s()}},children:[z(e)]})}gapAnimator(e,t,s,a){const i=r("div",{class:"atfb-marker",attrs:{"aria-hidden":"true"}});let o=null,l=null,c=0;const d=()=>{i.remove(),l?.classList.remove("atfb-lifted"),l=null,o=null},h=u=>{const b=Array.from(e.querySelectorAll(t)),g=new Map(b.filter(x=>!x.classList.contains("atfb-lifted")).map(x=>[x,x.getBoundingClientRect().top]));u();for(const x of b)x.style.transition="none",x.style.transform="";const p=b.filter(x=>{const I=g.get(x);if(I===void 0||!x.isConnected||x.classList.contains("atfb-lifted"))return!1;const S=I-x.getBoundingClientRect().top;return Math.abs(S)<.5?!1:(x.style.transform=`translateY(${S}px)`,!0)});e.offsetHeight;for(const x of b){if(!p.includes(x)){x.style.transition="";continue}x.style.transition="transform 160ms ease",x.style.transform="",x.addEventListener("transitionend",()=>{x.style.transition=""},{once:!0})}},w=u=>{const b=u.detail;if(b?.payload?.type!==se||!e.classList.contains("is-dropping")||a&&!a(b.payload.data??{}))return;const g=b.payload.data?.fieldId,p=typeof g=="string"?s(g):null,x=Je(e,t,b.clientY??0,p??void 0);x===o&&i.isConnected||(o=x,h(()=>{p&&p!==l&&(l?.classList.remove("atfb-lifted"),p.classList.add("atfb-lifted"),l=p),i.style.blockSize=`${Math.max(8,Math.round(c))}px`;const I=Array.from(e.querySelectorAll(t)).filter(S=>S!==p);x>=I.length?e.append(i):I[x].before(i)}))},_=()=>{e.classList.remove("is-dropping"),window.setTimeout(()=>h(d),0)};return document.addEventListener("os.drag.move",w),document.addEventListener("os.drag.end",_),{enter:u=>{const b=u?s(u):null;c=b&&!b.classList.contains("atfb-lifted")?b.getBoundingClientRect().height:c||44},leave:()=>h(d),drop:(u,b)=>{const g=o??Je(e,t,u,b??void 0);return d(),g},teardown:()=>{document.removeEventListener("os.drag.move",w),document.removeEventListener("os.drag.end",_),d()}}}registerCanvasTarget(e){this.canvasTarget?.(),this.canvasTarget=null;const t=this.gapAnimator(e,".atfb-card",i=>this.canvas.querySelector(`[data-atfb-card="${CSS.escape(i)}"]`)),s=Z().registerDropTarget({id:`atfb-canvas-${this.form?.id??0}`,element:e,accept:i=>i.type===se,onEnter:i=>{e.classList.add("is-dropping"),t.enter(i.payload.data.fieldId)},onLeave:()=>{e.classList.remove("is-dropping"),t.leave()},onDrop:(i,o)=>{e.classList.remove("is-dropping");const l=i.payload.data,c=l.fieldId?this.canvas.querySelector(`[data-atfb-card="${CSS.escape(l.fieldId)}"]`):null,d=t.drop(o.clientY,c);if(l.isNew&&l.fieldType){this.addField(l.fieldType,d);return}if(l.fieldId&&this.locateField(l.fieldId)){this.relocateField(l.fieldId,null,d);return}l.field&&this.insertField({...l.field,id:""},d)}}),a=this.wireRepeaterZones(e);this.canvasTarget=()=>{s(),a.forEach(i=>i()),t.teardown()}}wireRepeaterZones(e){const t=[];return e.querySelectorAll("[data-atfb-repeater-zone]").forEach(s=>{const a=s.dataset.atfbRepeaterZone??"",i=l=>{const c=l;if(c.fieldId===a)return!1;const d=c.isNew?c.fieldType:this.locateField(c.fieldId??"")?.field.type??c.field?.type;return!!d&&this.allowedInRepeater(d)},o=this.gapAnimator(s,".atfb-subcard",l=>e.querySelector(`[data-atfb-subfield="${CSS.escape(l)}"]`),i);t.push(()=>o.teardown()),t.push(Z().registerDropTarget({id:`atfb-repzone-${this.form?.id??0}-${a}`,element:s,accept:l=>l.type===se&&i(l.data),onEnter:l=>{s.classList.add("is-dropping"),o.enter(l.payload.data.fieldId)},onLeave:()=>{s.classList.remove("is-dropping"),o.leave()},onDrop:(l,c)=>{s.classList.remove("is-dropping");const d=l.payload.data,h=d.fieldId?e.querySelector(`[data-atfb-subfield="${CSS.escape(d.fieldId)}"]`):null,w=o.drop(c.clientY,h);if(d.isNew&&d.fieldType){this.addFieldToRepeater(d.fieldType,a,w);return}if(d.fieldId&&this.locateField(d.fieldId)){this.relocateField(d.fieldId,a,w);return}d.field&&this.insertFieldIntoRepeater({...d.field,id:""},a,w)}})),s.querySelectorAll(".atfb-subcard").forEach(l=>{const c=l.dataset.atfbSubfield??"";l.addEventListener("click",d=>{d.target.closest(".atfb-preview__remove")||(d.stopPropagation(),!Z().recentlyEndedDrag()&&this.selectField(c))}),l.addEventListener("keydown",d=>{if(d.stopPropagation(),d.key==="Enter"||d.key===" "){d.preventDefault(),this.selectField(c);return}if(d.altKey&&(d.key==="ArrowUp"||d.key==="ArrowDown")){d.preventDefault();const h=this.locateField(c);h&&(this.relocateField(c,a,d.key==="ArrowUp"?h.index-1:h.index+1),window.requestAnimationFrame(()=>{this.canvas.querySelector(`[data-atfb-subfield="${CSS.escape(c)}"]`)?.focus()}));return}(d.key==="Delete"||d.key==="Backspace")&&(d.preventDefault(),this.deleteField(c))}),l.addEventListener("pointerdown",d=>{if(d.stopPropagation(),d.target.closest(".atfb-preview__remove"))return;const h=this.locateField(c);h&&Z().start({payload:Ee(se,l,{fieldId:c,parentId:a,field:h.field,isNew:!1},d),origin:d})})})}),t}addField(e,t){const s=this.buildField(e);s&&this.insertField(s,t)}buildField(e){const t=this.config?.fieldTypes.find(a=>a.type===e);return!t||!this.schema?void 0:{id:this.nextFieldId(),type:e,label:t.input?t.label:"",placeholder:"",hint:"",required:!1,width:"full",cssClass:"",default:"",choices:t.choices?[{label:"First choice",value:"first"},{label:"Second choice",value:"second"}]:[],logic:{enabled:!1,action:"show",match:"all",rules:[]},messages:{},prefill:"",...t.settings}}allowedInRepeater(e){const t=this.config?.fieldTypes.find(s=>s.type===e);return!t||!t.input?!1:!["repeater","page_break","file","signature","total"].includes(e)}addFieldToRepeater(e,t,s){if(!this.allowedInRepeater(e))return;const a=this.buildField(e);a&&this.insertFieldIntoRepeater(a,t,s)}insertFieldIntoRepeater(e,t,s){const a=this.liveField(t);if(!this.schema||!a||a.type!=="repeater")return;e.id||(e.id=this.nextFieldId()),this.snapshot(),Array.isArray(a.fields)||(a.fields=[]);const i=a.fields,o=s===void 0?i.length:Math.max(0,Math.min(s,i.length));i.splice(o,0,e),this.selected=e.id,this.markDirty(),this.renderCanvas(),this.renderInspector(),window.requestAnimationFrame(()=>{this.canvas.querySelector(`[data-atfb-subfield="${CSS.escape(e.id)}"]`)?.focus()})}relocateField(e,t,s){if(!this.schema)return;const a=this.locateField(e);if(!a)return;if(!a.parent&&!t){this.moveField(e,s);return}const i=t?this.liveField(t):null;if(t&&(!i||i.type!=="repeater"||e===t))return;i&&!Array.isArray(i.fields)&&(i.fields=[]);const o=i?i.fields:this.schema.fields;this.snapshot(),a.list.splice(a.index,1);const l=Math.max(0,Math.min(s,o.length));o.splice(l,0,a.field),this.selected=a.field.id,this.markDirty(),this.renderCanvas(),this.renderInspector()}insertField(e,t){if(!this.schema)return;e.id||(e.id=this.nextFieldId()),this.snapshot();const s=t===void 0?this.schema.fields.length:Math.max(0,Math.min(t,this.schema.fields.length));this.schema.fields.splice(s,0,e),this.selected=e.id,this.markDirty(),this.renderCanvas(),this.renderInspector(),window.requestAnimationFrame(()=>{this.canvas.querySelector(`[data-atfb-card="${CSS.escape(e.id)}"]`)?.focus()})}moveField(e,t){if(!this.schema)return;const s=Wt(this.schema.fields,e,t);if(!s)return;this.snapshot();const[a]=this.schema.fields.splice(s.from,1);this.schema.fields.splice(s.to,0,a),this.markDirty(),this.renderCanvas()}duplicateField(e){if(!this.schema)return;const t=this.locateField(e);if(!t)return;const s=JSON.parse(JSON.stringify(t.field)),a=this.usedFieldIds(),i=()=>{let o=a.size+1;for(;a.has(`f${o}`);)o++;const l=`f${o}`;return a.add(l),l};s.id=i();for(const o of s.fields??[])o.id=i();if(t.parent){this.snapshot(),t.list.splice(t.index+1,0,s),this.selected=s.id,this.markDirty(),this.renderCanvas(),this.renderInspector();return}this.insertField(s,t.index+1)}async deleteField(e){if(!this.schema)return;const t=this.locateField(e);if(!t)return;const s=this.schema.fields.filter(i=>i.logic?.rules?.some(o=>o.field===e)),a=s.length?`Delete this field? ${s.length} other field${s.length===1?"":"s"} use it in a condition, and those conditions will stop working.`:Ze("confirmDelete","Delete this? It cannot be undone.");await ie(a,"Delete field")&&(this.snapshot(),t.list.splice(t.index,1),this.selected===e&&(this.selected=null),this.markDirty(),this.renderCanvas(),this.renderInspector())}selectField(e){this.selected=e;for(const t of this.canvas.querySelectorAll("[data-atfb-card]")){const s=t.dataset.atfbCard===e;t.classList.toggle("is-selected",s),t.setAttribute("aria-pressed",s?"true":"false")}for(const t of this.canvas.querySelectorAll("[data-atfb-subfield]"))t.classList.toggle("is-selected",t.dataset.atfbSubfield===e);this.logicMap?.highlight(e),this.renderInspector()}nextFieldId(){const e=this.usedFieldIds();let t=e.size+1;for(;e.has(`f${t}`);)t++;return`f${t}`}usedFieldIds(){const e=new Set;for(const t of this.schema?.fields??[]){e.add(t.id);for(const s of t.fields??[])e.add(s.id)}return e}nextEntryId(e,t){const s=new Set(t.map(i=>i.id));let a=t.length+1;for(;s.has(`${e}${a}`);)a++;return`${e}${a}`}renderTypeSettings(e,t,s,a){for(const i of s){const o=Pt[i];o&&(this.inspector.append(Bt(e,o,a)),o.also&&this.inspector.append(Bt(e,{...o,key:o.also.key,label:o.also.label,hint:o.also.hint},a)))}s.includes("rows")&&e.type!=="likert"&&this.inspector.append(y("Lines tall",G(String(e.rows??""),i=>a("rows",i)))),s.includes("rows")&&e.type==="likert"&&this.inspector.append(this.renderStatementList(e,a)),s.includes("parts")&&this.inspector.append(this.renderPartsEditor(e,t,a)),s.includes("default")&&!["files","object"].includes(t?.value??"")&&this.inspector.append(this.renderDefaultControl(e,t,a))}renderStatementList(e,t){const s=Array.isArray(e.rows)?e.rows:[];return y("Statements",pe(s.map(a=>a.label??"").join(` -`),a=>t("rows",Ht(s,a)),5),"One per line. Each becomes a row of the matrix.")}renderPartsEditor(e,t,s){const a=t?.parts??[],i=Array.isArray(e.parts)?e.parts:a.map(l=>l.key),o=a.map(l=>O(l.label,i.includes(l.key),c=>{const d=a.map(h=>h.key).filter(h=>h===l.key?c:i.includes(h));s("parts",d.length?d:[l.key])}));return r("div",{class:"atfb-section",children:[r("h4",{class:"atfb-section__title",text:"Parts to ask for"}),...o]})}renderDefaultControl(e,t,s){const a="Filled in before they start. They can change it.";return e.type==="switch"||e.type==="consent"?O("On by default",!!e.default,i=>s("default",i)):t?.choices&&(e.choices??[]).length?y("Default answer",D(String(e.default??""),[{value:"",label:"Nothing chosen"},...(e.choices??[]).map(i=>({value:String(i.value),label:i.label||String(i.value)}))],i=>s("default",i)),a):y("Default answer",T(String(e.default??""),i=>s("default",i)),a)}renderChoicesEditor(e,t){const s=e.choices??[],a=o=>this.liveField(e.id)?.choices?.[o],i=r("div",{class:"atfb-choices"});return s.forEach((o,l)=>{const c=r("div",{class:"atfb-choice-row",children:[e.type==="image_choice"?this.choiceImageWell(o,l,e):null,J(T(o.label,d=>{const h=a(l);if(!h)return;const w=!h.value||h.value===h.label;h.label=d,w&&(h.value=d),this.markDirty();const _=this.liveField(e.id);_&&this.syncCanvas(_)}),`choice:${l}:label`),J(T(o.value,d=>{const h=a(l);h&&(h.value=d,this.markDirty())},"value"),`choice:${l}:value`),e.type==="quiz"||o.points!==void 0?G(String(o.points??""),d=>{const h=a(l);h&&(h.points=d===""?void 0:Number(d),this.markDirty())}):G(String(o.price??""),d=>{const h=a(l);h&&(h.price=d===""?void 0:Number(d),this.markDirty())}),r("button",{class:"atfb-card__action",type:"button",attrs:{"aria-label":`Remove ${o.label}`},on:{click:()=>{const d=this.liveField(e.id);d&&((d.choices??[]).splice(l,1),t("choices",d.choices),this.renderInspector())}},children:[z("trash")]})]});i.append(c)}),r("div",{class:"atfb-section",children:[r("h4",{text:"Choices"}),r("p",{class:"atfb-hint",text:e.type==="quiz"?"Label, value, points.":"Label, value, and a price for calculations."}),i,$("Add choice",()=>{const o=this.liveField(e.id);if(!o)return;const l=o.choices??[];l.push({label:"",value:""}),t("choices",l),this.renderInspector()},"ghost","plus-alt2"),e.type==="quiz"?y("Correct answer",D(String(e.correct??""),[{value:"",label:"—"},...s.map(o=>({value:o.value,label:o.label}))],o=>t("correct",o))):null]})}choiceImageWell(e,t,s){const a=r("div",{class:`atfb-well${e.image?" has-image":""}`,attrs:{"data-choice":t,"aria-label":`Image for ${e.label||`choice ${t+1}`}`},children:[e.image?r("span",{class:"atfb-well__id",text:`#${e.image}`}):z("format-image")]}),i=Z().registerDropTarget({id:`atfb-well-${s.id}-${t}`,element:a,accept:o=>Fn.includes(o.type),onEnter:()=>a.classList.add("is-dropping"),onLeave:()=>a.classList.remove("is-dropping"),onDrop:o=>{a.classList.remove("is-dropping");const l=o.payload.data,c=Number(l.attachmentId??l.id??l.file?.id??0);if(!c){N("That is not an image this field can use","","error");return}const d=this.liveField(s.id)?.choices?.[t];d&&(d.image=c,this.markDirty(),this.renderInspector())}});return this.teardowns.push(i),a.addEventListener("click",()=>{const o=this.liveField(s.id)?.choices?.[t];o?.image&&(o.image=void 0,this.markDirty(),this.renderInspector())}),a}renderValidationSection(e,t,s){const a=[],i=[];t.includes("minlength")&&i.push(["minlength","Minimum characters"],["maxlength","Maximum characters"]),t.includes("min")?i.push(["min","Minimum"],["max","Maximum"]):t.includes("max")&&i.push(["max","Highest"]),t.includes("step")&&i.push(["step","Steps of"]),t.includes("mindate")&&i.push(["minDate","Earliest date"],["maxDate","Latest date"]),t.includes("mintime")&&i.push(["minTime","Earliest time"],["maxTime","Latest time"]);for(const[l,c]of i)a.push(y(c,T(String(e[l]??""),d=>s(l,d))));t.includes("pattern")&&a.push(...this.renderAnswerShapeRows(e,s)),t.includes("unique")&&a.push(O("No two people may submit the same value",!!e.unique,l=>s("unique",l)));const o=e.messages??{};return t.includes("required")&&a.push(y("Message when required",T(o.required??"",l=>{o.required=l,s("messages",o)}),"Leave empty for the default wording.")),a.length?this.section(`validation:${e.id}`,"Validation",a):r("div",{class:"atfb-section is-empty"})}buildShapePicker(e,t){const s=[{heading:null,options:[{value:"",label:"Anything at all"}]},...$n.map(i=>({heading:i,options:Dt.filter(o=>o.group===i).map(o=>({value:o.slug,label:o.label}))})),{heading:"Your own",options:[{value:"custom",label:"A custom rule…"}]}];if(Y("os-select")&&Y("os-option")){const i=document.createElement("os-select");i.setAttribute("value",e),i.setAttribute("aria-label","What the answer should look like"),i.classList.add("atfb-field");for(const o of s){if(o.heading){const l=document.createElement("os-option");l.setAttribute("value",`__heading:${o.heading}`),l.setAttribute("disabled",""),l.textContent=o.heading,i.append(l)}for(const l of o.options){const c=document.createElement("os-option");c.setAttribute("value",l.value),c.textContent=l.label,i.append(c)}}return i.addEventListener("os-pick",o=>{t(String(o.detail?.value??""))}),i}const a=r("select",{class:"atfb-input atfb-select",attrs:{"aria-label":"What the answer should look like"},on:{change:i=>t(i.target.value)}});for(const i of s){const o=i.heading?(()=>{const l=document.createElement("optgroup");return l.label=i.heading,a.append(l),l})():a;for(const l of i.options)o.append(r("option",{value:l.value,text:l.label,attrs:{selected:l.value===e}}))}return a}renderAnswerShapeRows(e,t){const a=(typeof e.validation=="string"?e.validation:"")||(e.pattern?"custom":""),i=()=>Ln({root:this.root,field:this.liveField(e.id)??e,onSave:h=>{t("validation","custom"),t("pattern",h.pattern),t("validationRecipe",JSON.stringify(h.recipe));const w={...this.liveField(e.id)?.messages??{}};w.invalid=h.message,t("messages",w),this.renderInspector()},onCancel:()=>this.renderInspector()}),o=h=>{if(h==="custom"){i();return}t("validation",h),t("pattern",""),t("validationRecipe",""),this.renderInspector()},l=this.buildShapePicker(a,o),c=An(a),d=[y("The answer should be",l,c?`e.g. ${c.example}`:"Checked as they type, and again on the server.")];if(a==="custom"){const h=Tt(String(e.validationRecipe??"")),w=Mt(h)||(e.pattern?`Matches the expression ${String(e.pattern)}`:"No rule yet — open the builder.");d.push(y("Your rule",r("div",{class:"atfb-valrule",children:[r("p",{class:"atfb-valrule__words",text:w}),$("Edit the rule…",i,"secondary","edit")]}),xe(h)||e.pattern?"Built in the rule builder, with a playground to test it.":void 0))}return d}logicRulesEditor(e,t,s=""){const a=(this.schema?.fields??[]).filter(d=>d.id!==s&&d.type!=="page_break"),i=()=>r("div",{class:"atfb-rule-join",children:[r("button",{class:"atfb-rule-join__chip",type:"button",text:e.match==="all"?"and":"or",title:"Switch between needing every rule (and) or any one of them (or).",on:{click:()=>t(d=>{d.match=d.match==="all"?"any":"all"},!0)}})]}),o=(d,h)=>{const w=r("button",{class:"atfb-card__action",type:"button",attrs:{"aria-label":"Remove this rule"},title:"Remove this rule",on:{click:()=>t(u=>{u.rules.splice(h,1)},!0)},children:[z("trash")]}),_=[r("div",{class:"atfb-rule__top",children:[D(d.field,a.map(u=>({value:u.id,label:u.label||u.id})),u=>t(b=>{const g=b.rules[h];g&&(g.field=u,g.value="")},!0)),w]}),D(d.operator,Object.entries(this.config?.operators??{}).map(([u,b])=>({value:u,label:b})),u=>t(b=>{const g=b.rules[h];g&&(g.operator=u)},!0))];if(!Me.includes(d.operator)){const u=this.schema?.fields.find(b=>b.id===d.field);if(u?.choices?.length){const b=u.choices.map(g=>({value:g.value,label:g.label||g.value}));d.value!==""&&!u.choices.some(g=>g.value===d.value)&&b.unshift({value:d.value,label:d.value}),_.push(D(d.value,b,g=>t(p=>{const x=p.rules[h];x&&(x.value=g)})))}else _.push(T(d.value,b=>t(g=>{const p=g.rules[h];p&&(p.value=b)}),"The answer to compare against"))}return r("div",{class:"atfb-rule",children:_})},l=r("div",{class:"atfb-rules"});e.rules.forEach((d,h)=>{h>0&&l.append(i()),l.append(o(d,h))});const c=$("Add rule",()=>{t(d=>{d.rules.push({field:a[0]?.id??"",operator:"is",value:""})},!0)},"ghost","plus-alt2");return[l,c]}conditionsSection(e,t,s){const a=(o,l=!1)=>{o(s),this.markDirty(),l&&this.renderCanvas()},i=t==="confirmation"?"Use":"Send";return this.section(`conditions:${e}`,"Conditions",[O(`Only ${i.toLowerCase()} this ${t} sometimes`,s.enabled,o=>a(l=>{l.enabled=o},!0)),s.enabled?r("div",{children:[r("div",{class:"atfb-rule-head",children:[r("span",{text:`${i} it when`}),D(s.match,[{value:"all",label:"all"},{value:"any",label:"any"}],o=>a(l=>{l.match=o},!0)),r("span",{text:"of these match:"})]}),...this.logicRulesEditor(s,a)]}):null],s.enabled)}renderLogicSection(e){const t=e.logic,s=()=>this.liveField(e.id)?.logic,a=(o,l=!1)=>{const c=s();c&&(o(c),this.markDirty(),this.renderCanvas(),l&&this.renderInspector())},i=this.logicRulesEditor(t,a,e.id);return this.section(`logic:${e.id}`,"Conditional logic",[O("Only show this field sometimes",t.enabled,o=>{a(l=>{l.enabled=o},!0)}),t.enabled?r("div",{children:[r("div",{class:"atfb-rule-head",children:[D(t.action,[{value:"show",label:"Show"},{value:"hide",label:"Hide"}],o=>a(l=>{l.action=o},!0)),r("span",{text:"this field when"}),D(t.match,[{value:"all",label:"all"},{value:"any",label:"any"}],o=>a(l=>{l.match=o},!0)),r("span",{text:"of these match:"})]}),...i]}):null],t.enabled)}renderTabCanvas(){return this.schema?this.tab==="theme"?mt({themes:this.themes,tokens:this.config?.tokens??[],activeSlug:this.schema.settings.theme,overrides:this.schema.settings.themeOverrides,onTheme:e=>{this.schema.settings.theme=e,this.markDirty()},onOverride:(e,t)=>{t===""?delete this.schema.settings.themeOverrides[e]:this.schema.settings.themeOverrides[e]=t,this.markDirty()},onOverridesReplaced:e=>{this.schema.settings.themeOverrides={...e},this.markDirty()},previewFor:(e,t)=>this.previewHtml(e,t),onThemesChanged:e=>{this.themes=e}}):this.tab==="settings"?this.renderSettingsPane():this.tab==="notify"?this.renderNotificationsPane():this.renderConfirmationsPane():r("div")}async paintCanvasTheme(){if(!this.form||!this.schema)return;const e=this.schema.settings.theme,t=JSON.stringify([e,this.schema.settings.themeOverrides]);if(t!==this.canvasThemeSignature){this.canvasThemeSignature=t;try{const s=await this.previewHtml(e,this.schema.settings.themeOverrides??{});this.root.classList.toggle("atfb--dark-form",/atf-is-dark/.test(s));const a=/class="atf-form-wrap"[^>]*\sstyle="([^"]*)"/.exec(s);if(!a)return;const i=a[1].replace(/"/g,'"').replace(/�?39;/g,"'").replace(/&/g,"&");this.canvasTheme.textContent=`.atfb .atfb-preview { +var allTerrainFormsBuilder=function(ee){"use strict";class as{constructor(){this.targets=[],this.active=null,this.lastEndMs=0}start(e){if(this.active||e.origin.button!==0)return null;const{payload:s,origin:t}=e,a=t.clientX,i=t.clientY;let o=!1,l=!1,c=null,d=null,h=0,m=0;const g=()=>{document.removeEventListener("pointermove",x),document.removeEventListener("pointerup",F),document.removeEventListener("pointercancel",S),document.removeEventListener("keydown",H),window.removeEventListener("blur",S),c?.remove(),c=null,s.source.classList.remove("atf-is-dragging"),document.body.classList.remove("atf-drag-active"),d?.onLeave?.(f),d=null,this.active=null,this.lastEndMs=Date.now()},u=(L,b)=>{document.dispatchEvent(new CustomEvent(L,{detail:{payload:s,clientX:b?.clientX,clientY:b?.clientY}}))},f={payload:s,isFinished:()=>l,cancel:(L="caller")=>{l||(l=!0,g(),o&&u("os.drag.end"),e.onCancel?.(L))}},y=L=>{o=!0,s.source.classList.add("atf-is-dragging"),document.body.classList.add("atf-drag-active");const b=s.source.getBoundingClientRect();h=s.ghost?.offsetX??a-b.left,m=s.ghost?.offsetY??i-b.top,c=s.ghost?.element??s.source.cloneNode(!0),c.classList.add("atf-drag-ghost"),c.style.width=`${b.width}px`,document.body.appendChild(c),p(L),u("os.drag.start",L)},p=L=>{c&&(c.style.transform=`translate3d(${L.clientX-h}px, ${L.clientY-m}px, 0)`)},x=L=>{if(l)return;if(!o){if(Math.hypot(L.clientX-a,L.clientY-i)<4)return;y(L)}p(L);const b=this.hitTest(L.clientX,L.clientY);b!==d&&(d?.onLeave?.(f),d=b,d?.onEnter?.(f)),u("os.drag.move",L)},F=L=>{if(l)return;if(!o){l=!0,g(),e.onClickOnly?.();return}const b=d;if(l=!0,g(),b&&b.accept(s)){e.onCommit?.(b),b.onDrop(f,{clientX:L.clientX,clientY:L.clientY}),u("os.drag.end",L);return}u("os.drag.end",L),e.onCancel?.(b?"rejected":"no-target")},S=()=>f.cancel("pointercancel"),H=L=>{L.key==="Escape"&&f.cancel("escape")};return document.addEventListener("pointermove",x),document.addEventListener("pointerup",F),document.addEventListener("pointercancel",S),document.addEventListener("keydown",H),window.addEventListener("blur",S),this.active=f,f}registerDropTarget(e){return this.targets=this.targets.filter(s=>s.id!==e.id),this.targets.push(e),()=>{this.targets=this.targets.filter(s=>s.id!==e.id)}}isDragging(){return this.active!==null}recentlyEndedDrag(e=500){return Date.now()-this.lastEndMso.right||so.bottom)continue;const l=is(i.element);if(l>a){t=i,a=l;continue}l===a&&t&&rs(i.element,t.element)&&(t=i)}return t}}function is(n){let e=0,s=n;for(;s;)e++,s=s.parentElement;return e}function rs(n,e){return(e.compareDocumentPosition(n)&Node.DOCUMENT_POSITION_FOLLOWING)!==0}function Le(){return window.wp?.os??null}let $e=null;function G(){const n=Le();return n?.dragManager?n.dragManager:($e||($e=new as),$e)}function os(n){const e=i=>{const o=i.detail?.payload;return o&&n.includes(o.type)?o.source:null},s=i=>i.preventDefault(),t=i=>{const o=e(i);o&&(o.classList.add("atf-is-dragging"),document.body.classList.add("atf-drag-active"),window.getSelection()?.removeAllRanges(),document.addEventListener("selectstart",s))},a=i=>{const o=e(i);o&&(o.classList.remove("atf-is-dragging"),document.body.classList.remove("atf-drag-active"),document.removeEventListener("selectstart",s))};return document.addEventListener("os.drag.start",t),document.addEventListener("os.drag.end",a),()=>{document.removeEventListener("os.drag.start",t),document.removeEventListener("os.drag.end",a),document.removeEventListener("selectstart",s)}}const ls=["atfb","atfe","atfs","atfm"];function Ae(n,e,s,t,a){const i=e.getBoundingClientRect();if(!a){const o=ls.find(l=>e.closest(`.${l}`));if(o){const l=e.cloneNode(!0);l.style.transition="",l.style.transform="",a=document.createElement("div"),a.className=`${o} atf-ghost-scope`,a.appendChild(l)}}return a&&(a.style.width=`${Math.round(i.width)}px`,a.style.maxWidth=`${Math.round(i.width)}px`,a.style.boxSizing="border-box"),{type:n,source:e,data:s,ghost:{element:a,offsetX:t.clientX-i.left,offsetY:t.clientY-i.top,hint:{neutral:"",accept:"",reject:"",hidden:!0}}}}function st(n,e,s,t){const a=Array.from(n.querySelectorAll(e)).filter(i=>i!==t&&!i.classList.contains("atf-drag-ghost"));for(let i=0;ia.fetch(l,c,{source:"allterrain-forms"}):(l,c)=>fetch(l,c))(s,{credentials:"same-origin",...e,headers:t});if(!o.ok){let l=`Request failed with status ${o.status}.`,c="";try{const d=await o.json();l=d.message??l,c=d.code??""}catch{}throw new de(l,o.status,c)}if(o.status!==204)return await o.json()}const z=n=>Te(n);async function cs(n){if(!U?.wpRestUrl)throw new de("AllTerrain Forms is not configured on this page.",0);const e=U.nonce?{"X-WP-Nonce":U.nonce}:{},s=Le(),t=nt(U.wpRestUrl,n),a=s?.fetch?await s.fetch(t,{credentials:"same-origin",headers:e},{source:"allterrain-forms"}):await fetch(t,{credentials:"same-origin",headers:e});if(!a.ok)throw new de(`Request failed with status ${a.status}.`,a.status);return await a.json()}function ds(n){if(!n||!n.includes("&"))return n;const e=document.createElement("textarea");return e.innerHTML=n,e.value}const X=(n,e)=>Te(n,{method:"POST",body:JSON.stringify(e)}),he=n=>Te(n,{method:"DELETE"});function at(n){const e=new URLSearchParams;for(const[t,a]of Object.entries(n))a===void 0||a===""||a===!1||e.set(t,String(a));const s=e.toString();return s?`?${s}`:""}function ue(n){const e=n?.schema?.settings;return e&&(!e.themeOverrides||Array.isArray(e.themeOverrides))&&(e.themeOverrides={...e.themeOverrides}),n}const I={config:()=>z("/config"),mailpoet:()=>z("/mailpoet"),listForms:()=>z("/forms"),listArchivedForms:()=>z("/forms?archived=1"),archiveForm:n=>X(`/forms/${n}/archive`,{}),unarchiveForm:n=>X(`/forms/${n}/unarchive`,{}),getForm:n=>z(`/forms/${n}`).then(ue),createForm:n=>X("/forms",n).then(ue),updateForm:(n,e)=>X(`/forms/${n}`,e).then(ue),duplicateForm:n=>X(`/forms/${n}/duplicate`,{}).then(ue),deleteForm:n=>he(`/forms/${n}`),preview:(n,e)=>X(`/forms/${n}/preview`,e),pages:()=>cs("wp/v2/pages?per_page=100&status=publish&orderby=title&order=asc&_fields=id,title").then(n=>n.map(e=>({id:e.id,title:ds(e.title?.rendered??"")||`#${e.id}`}))),mergeTags:n=>z(`/forms/${n}/merge-tags`).then(e=>e.groups),analytics:(n,e="")=>z(`/forms/${n}/analytics${e?`?dimension=${encodeURIComponent(e)}`:""}`),demoStatus:()=>z("/demo"),demoSeed:n=>X("/demo",n?{count:n}:{}),demoRemove:()=>he("/demo"),listEntries:n=>z(`/entries${at(n)}`),getEntry:n=>z(`/entries/${n}`),updateEntry:(n,e)=>X(`/entries/${n}`,e),deleteEntry:n=>he(`/entries/${n}`),exportEntries:n=>z(`/entries/export${at(n)}`),listThemes:()=>z("/themes"),saveTheme:n=>X("/themes",n),deleteTheme:n=>he(`/themes/${n}`)};function it(n,e,s={}){if(!n)return null;let t;const a=(o,l)=>{const c=n[o];return c==null||!Number.isFinite(Number(c))?l:Number(c)},i=(o,l,c=1)=>Array.from({length:Math.floor((l-o)/c+1e-9)+1},(d,h)=>{const m=String(Number((o+h*c).toPrecision(12)));return{value:m,label:m}});if(n.type==="scale"){const o=Math.trunc(a("min",0));let l=Math.trunc(a("max",10));l<=o&&(l=o+10),t=i(o,Math.min(l,o+20))}else if(n.type==="rating")t=i(1,Math.max(2,Math.min(10,Math.abs(Math.trunc(a("max",5))))));else if(n.type==="switch"||n.type==="consent")t=[{value:"1",label:"Checked"},{value:"",label:"Unchecked"}];else if(n.type==="country")t=Object.entries(s).map(([o,l])=>({value:o,label:l}));else if(n.type==="range"){const o=a("min",0),l=a("max",100),c=n.step==="any"?0:a("step",1);if(c<=0||l999)return null;t=i(o,l,c)}else if(n.choices?.length||["select","multiselect","radio","checkboxes","image_choice","quiz","likert"].includes(n.type))t=(n.choices??[]).map(o=>({value:o.value,label:o.label||o.value}));else return null;return!t.some(o=>o.value===e)&&e!==""&&t.unshift({value:e,label:`${e} (stored value; unavailable)`,disabled:!0}),t.some(o=>o.value==="")||t.unshift({value:"",label:"Choose a value…",disabled:!0}),t}function r(n,e={}){const s=document.createElement(n);e.class&&(s.className=e.class),e.text!==void 0&&(s.textContent=e.text),e.html!==void 0&&(s.innerHTML=e.html),e.title&&(s.title=e.title),e.type&&"type"in s&&(s.type=e.type),e.value!==void 0&&"value"in s&&(s.value=e.value),e.placeholder!==void 0&&"placeholder"in s&&(s.placeholder=e.placeholder),e.href&&"href"in s&&(s.href=e.href);for(const[t,a]of Object.entries(e.attrs??{}))a===void 0||a===!1||s.setAttribute(t,a===!0?"":String(a));Object.assign(s.style,e.style??{});for(const[t,a]of Object.entries(e.on??{}))s.addEventListener(t,a);for(const t of e.children??[])t==null||t===!1||s.append(t);return s}function R(n){n.replaceChildren()}function B(n){return r("span",{class:`dashicons ${n.startsWith("dashicons-")?n:`dashicons-${n}`}`,attrs:{"aria-hidden":"true"}})}function w(n,e,s){const t=e.matches("input, select, textarea, button")?e:e.querySelector("input, select, textarea");if(t){const a=t.id||`atf-c-${Math.random().toString(36).slice(2,9)}`;return t.id=a,r("div",{class:"atfb-row",children:[r("label",{class:"atfb-row__label",text:n,attrs:{for:a}}),e,s?r("p",{class:"atfb-row__hint",text:s}):null]})}return r("div",{class:"atfb-row",children:[r("span",{class:"atfb-row__label",text:n}),e,s?r("p",{class:"atfb-row__hint",text:s}):null]})}function T(n,e,s=""){return r("input",{class:"atfb-input",type:"text",value:n,placeholder:s,on:{input:t=>e(t.target.value)}})}function V(n,e){if(Y("os-number-field")){const s=document.createElement("os-number-field");return s.setAttribute("value",n),s.classList.add("atfb-field"),s.addEventListener("os-input-change",t=>{e(String(t.detail?.value??""))}),s}return r("input",{class:"atfb-input",type:"number",value:n,on:{input:s=>e(s.target.value)}})}function pe(n,e,s=4){const t=r("textarea",{class:"atfb-input atfb-input--area",attrs:{rows:s},on:{input:a=>e(a.target.value)}});return t.value=n,t}function D(n,e,s){if(Y("os-select")&&Y("os-option")){const t=document.createElement("os-select");t.setAttribute("value",n),t.classList.add("atfb-field");for(const a of e){const i=document.createElement("os-option");i.setAttribute("value",a.value),a.disabled&&i.setAttribute("disabled",""),i.textContent=a.label,t.append(i)}return t.addEventListener("os-pick",a=>{s(String(a.detail?.value??""))}),t}return r("select",{class:"atfb-input atfb-select",on:{change:t=>s(t.target.value)},children:e.map(t=>r("option",{value:t.value,text:t.label,attrs:{selected:t.value===n,disabled:t.disabled}}))})}function q(n,e,s){if(Y("os-checkbox-label")){const a=document.createElement("os-checkbox-label");return a.setAttribute("label",n),a.classList.add("atfb-check"),e&&a.setAttribute("checked",""),a.addEventListener("os-checkbox-change",i=>{s(!!i.detail?.checked)}),a}const t=r("input",{type:"checkbox",on:{change:a=>s(a.target.checked)}});return t.checked=e,r("label",{class:"atfb-check",children:[t,r("span",{text:n})]})}function C(n,e,s="secondary",t){const a=[t?B(t):null,r("span",{text:n})];if(Y("os-button")){const i=document.createElement("os-button");i.setAttribute("variant",s),i.setAttribute("type","button"),i.classList.add("atfb-button",`atfb-button--${s}`),i.addEventListener("click",e);for(const o of a)o&&i.append(o);return i}return r("button",{class:`atfb-button atfb-button--${s}`,type:"button",on:{click:e},children:a})}function Y(n){return typeof customElements<"u"&&!!customElements.get(n)}const hs=["os-button","os-checkbox-label","os-number-field","os-select","os-option","os-segmented","os-segment","os-color-field","os-empty-state"];let fe=null;function us(){if(fe)return fe;const n=window.wp?.os;return fe=n?.loadComponents?n.loadComponents(hs).catch(()=>{}):Promise.resolve(),fe}async function ae(n,e=""){const s=window.wp?.os;return s?.confirm?s.confirm({title:e,message:n,danger:!0}):window.confirm(n)}function N(n,e="",s="info"){const t=window.wp?.os;if(t?.notify){t.notify({title:n,body:e,type:s});return}console.info(`[AllTerrain Forms] ${n}${e?`: ${e}`:""}`)}function ps(n){let e=0,s;return(...t)=>{s=t,!e&&(e=window.requestAnimationFrame(()=>{e=0,n(...s)}))}}function Me(n,e){let s=0;return(...t)=>{window.clearTimeout(s),s=window.setTimeout(()=>n(...t),e)}}function fs(n){try{return window.localStorage.getItem(n)??""}catch{return""}}function ms(n,e){try{window.localStorage.setItem(n,e)}catch{}}function Fe(n){const e=n.closest(".os-window__body");e&&(e.addEventListener("scroll",()=>{e.scrollTop&&(e.scrollTop=0),e.scrollLeft&&(e.scrollLeft=0)},{passive:!0}),e.scrollTop=0,e.scrollLeft=0)}function rt(){const n=[window];try{window.parent&&window.parent!==window&&n.push(window.parent)}catch{}return n}function bs(n){const e=n.wp?.os?.windowManager?.getAll?.()??[];for(const s of e){const t=s.iframe??s.element?.querySelector?.("iframe");if(t&&t.contentWindow===window)return s}return null}const gs=4e3,vs=100;function ys(n,e){const s=Date.now()+gs,t=()=>{const a=bs(n);if(a&&a.id!==e){typeof a.close=="function"?a.close():n.wp?.os?.windowManager?.remove?.(a.id);return}!a&&Date.now()0?{form:s}:void 0;s>0&&ks(s);for(const a of rt())if(a.wp?.os?.openWindow?.(e,{source:"handoff",params:t})){window.setTimeout(()=>ys(a,e),0);return}}function _s(){document.addEventListener("click",n=>{const e=n.target?.closest?.("[data-atf-open-window]");if(!e)return;n.preventDefault();const s=e.getAttribute("data-atf-open-window")??"";for(const t of rt())if(t.wp?.os?.openWindow?.(s,{source:"handoff"}))return})}const xs="allterrain-forms/requested-form";function ks(n){try{window.sessionStorage.setItem(xs,String(n))}catch{}}function ot(n){return`allterrain-forms/requested-form-${n}`}function Ss(n){try{const e=window.sessionStorage.getItem(ot(n));return window.sessionStorage.removeItem(ot(n)),Number(e)||0}catch{return 0}}const Ie={is:"is",is_not:"is not",contains:"contains",not_contains:"does not contain",starts_with:"starts with",ends_with:"ends with",greater:"is more than",less:"is less than",greater_equal:"is at least",less_equal:"is at most",empty:"is empty",not_empty:"has any answer"},De=["empty","not_empty"];function lt(n,e){const s=n.find(t=>t.id===e);return s?s.label||"an untitled question":null}function ct(n,e,s){return n.find(i=>i.id===e)?.choices?.find(i=>i.value===s)?.label||s}function Cs(n,e){const s=Ie[n.operator]??String(n.operator);if(De.includes(n.operator))return s;const t=ct(e,n.field,n.value);return`${n.operator==="is"?"":`${s} `}${t!==""?t:"(nothing)"}`}function me(n,e,s=0){const t=lt(e,n.field),a=Ie[n.operator]??String(n.operator);if(t===null)return[{kind:"field",text:"a question that no longer exists",fieldId:n.field,missing:!0}];const i=[{kind:"field",text:t,fieldId:n.field,missing:!1},{kind:"operator",text:a,operator:n.operator,ruleIndex:s}];if(De.includes(n.operator))return i;const o=ct(e,n.field,n.value);return i.push({kind:"value",text:o!==""?o:"(nothing)",raw:n.value,sourceId:n.field,ruleIndex:s}),i}function Es(n,e){const s=n.logic;if(!s?.enabled||!s.rules.length)return[];const t=[{kind:"verb",text:s.action==="hide"?"Hidden when":"Shown when"}];return s.rules.forEach((a,i)=>{i>0&&t.push({kind:"join",text:s.match==="all"?"and":"or"}),t.push(...me(a,e,i))}),t}function qe(n){return n.map(e=>e.text).join(" ")}function Ls(n,e){return qe(me(n,e))}function be(n){const e=[];for(const s of n){const t=s.logic;if(t?.enabled)for(const a of t.rules)!a.field||a.field===s.id||e.push({from:a.field,to:s.id,label:Ls(a,n),short:Cs(a,n),action:t.action,broken:lt(n,a.field)===null})}return e}function $s(n){const e=new Map,s=new Set;for(const t of be(n)){const a=`${t.from}->${t.to}`;t.broken||s.has(a)||(s.add(a),e.set(t.from,(e.get(t.from)??0)+1))}return e}const dt=10,ht=22,se="http://www.w3.org/2000/svg";class As{constructor(e){this.edges=[],this.teardowns=[],this.frame=0,this.host=e,this.svg=document.createElementNS(se,"svg"),this.svg.setAttribute("class","atfb-logicmap"),this.svg.setAttribute("aria-hidden","true"),this.svg.setAttribute("focusable","false"),e.append(this.svg);const s=()=>this.schedule();window.addEventListener("resize",s),this.teardowns.push(()=>window.removeEventListener("resize",s));const t=e.closest(".atfb__canvas")??e;if(t.addEventListener("scroll",s,{passive:!0}),this.teardowns.push(()=>t.removeEventListener("scroll",s)),typeof ResizeObserver<"u"){const a=new ResizeObserver(s);a.observe(e),this.teardowns.push(()=>a.disconnect())}}setEdges(e){this.edges=e,this.schedule()}highlight(e){this.svg.classList.toggle("is-focused",!!e),this.svg.querySelectorAll("[data-from]").forEach(s=>{const t=e&&(s.dataset.from===e||s.dataset.to===e);s.classList.toggle("is-lit",!!t)})}destroy(){this.teardowns.forEach(e=>e()),this.teardowns=[],this.svg.remove(),this.frame&&cancelAnimationFrame(this.frame)}schedule(){this.frame||(this.frame=requestAnimationFrame(()=>{this.frame=0,this.draw()}))}draw(){this.svg.replaceChildren();const e=this.host.getBoundingClientRect();this.svg.setAttribute("viewBox",`0 0 ${e.width} ${e.height}`),this.svg.setAttribute("width",String(e.width)),this.svg.setAttribute("height",String(e.height));const s=new Map;for(const t of this.edges){const a=this.cardRect(t.from),i=this.cardRect(t.to);if(!a||!i)continue;const o=s.get(t.from)??0;s.set(t.from,o+1),this.drawEdge(t,a,i,e,o)}}cardRect(e){const s=this.host.querySelector(`[data-atfb-card="${CSS.escape(e)}"]`);return s?s.getBoundingClientRect():null}drawEdge(e,s,t,a,i){const o=s.right-a.left,l=s.top+s.height/2-a.top,c=t.right-a.left,d=t.top+t.height/2-a.top,h=Math.max(0,a.width-dt-Math.max(o,c)),m=22+i*11+Math.min(22,Math.abs(d-l)/8),g=Math.max(12,Math.min(m,h*.4)),u=document.createElementNS(se,"path");u.setAttribute("d",`M ${o} ${l} C ${o+g} ${l}, ${c+g} ${d}, ${c} ${d}`),u.setAttribute("class",`atfb-logicmap__path is-${e.action}${e.broken?" is-broken":""}`),u.dataset.from=e.from,u.dataset.to=e.to;const f=document.createElementNS(se,"title");f.textContent=e.label,u.append(f);const y=document.createElementNS(se,"circle");y.setAttribute("cx",String(c)),y.setAttribute("cy",String(d)),y.setAttribute("r","3.5"),y.setAttribute("class",`atfb-logicmap__dot is-${e.action}${e.broken?" is-broken":""}`),y.dataset.from=e.from,y.dataset.to=e.to,this.svg.append(u,y);const p=document.createElementNS(se,"text");p.setAttribute("x",String(a.width-dt)),p.setAttribute("y",String(d+3)),p.setAttribute("text-anchor","end"),p.setAttribute("class","atfb-logicmap__label"),p.dataset.from=e.from,p.dataset.to=e.to,p.textContent=e.short.length>ht?`${e.short.slice(0,ht-1)}…`:e.short;const x=document.createElementNS(se,"title");x.textContent=e.label,p.append(x),this.svg.append(p)}}const Ts={text:"text",email:"text",url:"text",tel:"text",number:"text",password:"text",date:"text",time:"text",datetime:"text",date_range:"composite",textarea:"textarea",select:"select",country:"select",multiselect:"options",radio:"options",checkboxes:"options",image_choice:"options",quiz:"options",switch:"toggle",consent:"toggle",file:"file",range:"range",rating:"summary",scale:"summary",likert:"summary",signature:"summary",repeater:"repeater",color:"text",name:"composite",address:"composite",total:"text",hidden:"static",heading:"static",html:"static",divider:"static",spacer:"static",page_break:"static"};function Ms(n){return Ts[n]??"text"}function ut(n,e){const s=/^choice:(\d+):(label|value)$/.exec(e);return String(s?n.choices?.[Number(s[1])]?.[s[2]]??"":n[e]??"")}function Z(n){const{value:e,onInput:s,onCommit:t}=n,a=r("span",{class:`${n.class} atfb-editable`,text:e,attrs:{contenteditable:"plaintext-only",role:"textbox",spellcheck:"false","data-placeholder":n.placeholder}});return n.bind&&(a.dataset.atfbBind=n.bind),a.addEventListener("pointerdown",i=>i.stopPropagation()),a.addEventListener("input",()=>s(a.textContent??"")),a.addEventListener("keydown",i=>{i.key==="Enter"&&(i.preventDefault(),a.blur()),i.key==="Escape"&&(a.textContent=e,a.blur()),i.stopPropagation()}),a.addEventListener("blur",()=>t?.()),a}function Fs(n){const e=r("input",{class:"atf-choice__input",type:n==="checkboxes"||n==="multiselect"?"checkbox":"radio"});return e.disabled=!0,e}function pt(n,e,s){const t=Ms(n.type),a=Z({value:n.label,placeholder:"Write the question…",class:"atf-label",bind:"label",onInput:o=>s.edit(l=>{l.label=o}),onCommit:()=>s.restructure(()=>{})}),i=[t==="static"||t==="toggle"?null:a,Ds(n,e,t,s),Is(n,e,s)];return r("div",{class:"atfb-preview atf-form",children:[r("div",{class:`atf-field atf-field--${n.type}`,children:i})]})}function Is(n,e,s){if(!e?.supports.includes("hint"))return null;const t=Z({value:n.hint??"",placeholder:"Add a hint…",class:"atf-hint",bind:"hint",onInput:a=>s.edit(i=>{i.hint=a})});return t.setAttribute("aria-label","Hint"),r("p",{class:"atfb-preview__hint",children:[t]})}function Ds(n,e,s,t){switch(s){case"text":return Oe(n,"atf-input",t);case"textarea":return Oe(n,"atf-input atf-textarea",t,!0);case"select":return r("div",{class:"atfb-preview__select",children:[Oe(n,"atf-input atf-select",t)]});case"options":return Rs(n,t);case"toggle":return r("div",{class:n.type==="switch"?"atf-toggle atf-toggle--switch":"atf-toggle",children:[(()=>{const a=r("input",{class:"atf-toggle__input",type:"checkbox"});return a.disabled=!0,a})(),Z({value:n.label||"",placeholder:n.type==="consent"?"What are they agreeing to?…":"What does this turn on?…",class:"atf-toggle__label",bind:"label",onInput:a=>t.edit(i=>{i.label=a}),onCommit:()=>t.restructure(()=>{})})]});case"file":return r("div",{class:"atf-file",children:[r("input",{class:"atf-file__input",type:"file"})]});case"range":return r("input",{class:"atf-range__input",type:"range"});case"composite":return r("div",{class:"atf-composite__parts",children:[r("span",{class:"atf-input atfb-preview__ghost",text:""}),r("span",{class:"atf-input atfb-preview__ghost",text:""})]});case"static":return Ps(n,e,t);case"repeater":return qs(n,t);default:return r("div",{class:"atfb-preview__stack",children:[r("p",{class:"atfb-preview__summary",text:`${e?.label??n.type} — set this up in the panel on the right.`})]})}}function qs(n,e){const s=n.fields??[],t=r("div",{class:"atfb-repeater__zone",attrs:{"data-atfb-repeater-zone":n.id}});return s.forEach(a=>t.append(Os(n,a,e))),s.length||t.append(r("p",{class:"atfb-repeater__empty",text:"Drag fields from the palette in here — the visitor gets a fresh copy of each with every row they add."})),r("div",{class:"atfb-repeater",attrs:{"data-atfb-repeater":n.id},children:[r("div",{class:"atf-repeater__row atfb-repeater__frame",children:[r("div",{class:"atf-repeater__row-head",children:[r("span",{class:"atf-repeater__title atfb-repeater__title",children:[Z({value:String(n.itemLabel??""),placeholder:"Row",class:"atfb-repeater__item-label",bind:"itemLabel",onInput:a=>e.edit(i=>{i.itemLabel=a})}),r("span",{class:"atfb-repeater__ordinal",text:"1",attrs:{"aria-hidden":"true"}})]})]}),t]}),Ns(n,e)]})}function Os(n,e,s){const t=e.id,a=l=>c=>{const d=(c.fields??[]).find(h=>h.id===t);d&&l(d)},i={edit:l=>s.edit(a(l)),restructure:l=>s.restructure(a(l)),types:s.types,selectedId:s.selectedId},o=s.types?.(e.type);return r("div",{class:`atfb-subcard${s.selectedId===t?" is-selected":""}`,attrs:{"data-atfb-subfield":t,"data-atfb-parent":n.id,tabindex:"0",role:"button","aria-label":`${e.label||o?.label||e.type}, inside ${n.label||"the repeater"}`},children:[r("div",{class:"atfb-subcard__head",children:[r("span",{class:"atfb-subcard__type",text:o?.label??e.type}),e.required?r("span",{class:"atfb-subcard__required",text:"Required"}):null,r("button",{class:"atfb-preview__remove",type:"button",title:"Remove this field from the repeater",attrs:{"aria-label":`Remove ${e.label||o?.label||"this field"}`},on:{pointerdown:l=>l.stopPropagation(),click:l=>{l.stopPropagation(),s.restructure(c=>{const d=c.fields??[],h=d.findIndex(m=>m.id===t);h>=0&&d.splice(h,1)})}},children:[r("span",{text:"×"})]})]}),pt(e,o,i)]})}function Oe(n,e,s,t=!1){const a=Z({value:n.placeholder??"",placeholder:n.type==="select"||n.type==="country"?"Choose…":"Placeholder…",class:`${e} atfb-preview__box${t?" atfb-preview__box--tall":""}`,bind:"placeholder",onInput:i=>s.edit(o=>{o.placeholder=i})});return a.setAttribute("aria-label","Placeholder"),a}function Ne(n,e,s,t,a,i){return r("span",{class:`atf-button ${s} atfb-preview__button`,children:[Z({value:n,placeholder:e,class:"atfb-preview__button-text",bind:t,onInput:o=>a.edit(l=>i(l,o))})]})}function Ns(n,e){return Ne(String(n.addLabel??""),"Add another","atf-button--ghost atfb-repeater__add","addLabel",e,(s,t)=>{s.addLabel=t})}function Rs(n,e){const s=r("div",{class:"atf-choices__list"});return(n.choices??[]).forEach((t,a)=>{s.append(r("div",{class:"atf-choice atfb-preview__option",children:[Fs(n.type),Z({value:t.label,placeholder:"Option…",class:"atf-choice__label",bind:`choice:${a}:label`,onInput:i=>{e.edit(o=>{const l=o.choices?.[a];if(!l)return;const c=!l.value||l.value===l.label;l.label=i,c&&(l.value=i)})}}),r("button",{class:"atfb-preview__remove",type:"button",title:"Remove this option",attrs:{"aria-label":`Remove ${t.label||"this option"}`},on:{pointerdown:i=>i.stopPropagation(),click:i=>{i.stopPropagation(),e.restructure(o=>{o.choices.splice(a,1)})}},children:[r("span",{text:"×"})]})]}))}),s.append(r("button",{class:"atfb-preview__add",type:"button",on:{pointerdown:t=>t.stopPropagation(),click:t=>{t.stopPropagation(),e.restructure(a=>{const i=a.choices.length+1;a.choices.push({label:`Option ${i}`,value:`Option ${i}`})})}},children:[r("span",{text:"+ Add option"})]})),r("fieldset",{class:"atf-choices",children:[s]})}function Ps(n,e,s){return n.type==="heading"?Z({value:n.label,placeholder:"Section heading…",class:"atf-heading",bind:"label",onInput:t=>s.edit(a=>{a.label=t}),onCommit:()=>s.restructure(()=>{})}):n.type==="divider"?r("hr",{class:"atf-divider"}):n.type==="page_break"?r("div",{class:"atfb-preview__stack",children:[r("p",{class:"atfb-progress-name",children:[Z({value:n.label,placeholder:"Name this step…",class:"atf-progress__label",bind:"label",onInput:t=>s.edit(a=>{a.label=t}),onCommit:()=>s.restructure(()=>{})})]}),r("p",{class:"atfb-preview__summary",text:"Everything after this is a new page."}),r("div",{class:"atf-nav atfb-preview__nav",children:[Ne(String(n.prevLabel??""),"Back","atf-button--secondary","prevLabel",s,(t,a)=>{t.prevLabel=a}),Ne(String(n.nextLabel??""),"Next","","nextLabel",s,(t,a)=>{t.nextLabel=a})]})]}):r("p",{class:"atfb-preview__summary",text:`${e?.label??n.type} — nothing is shown to the visitor here.`})}const Re=new Map;function ft(n){let e=Re.get(n);return e||(e=I.mergeTags(n).catch(()=>[]),Re.set(n,e)),e}function Bs(n){Re.delete(n)}function js(n){const e=new Map;for(const s of n)for(const t of s.items)e.set(t.tag,t);return e}function Hs(n,e){const s=js(e);return n.replace(/\{[a-z_]+(?::[^}]*)?\}/gi,t=>{const a=s.get(t.toLowerCase());return a?`{the value of ${a.label}}`:t})}function mt(n){return/\{[a-z_]+(?::[^}]*)?\}/i.test(n)}let ge=null,Pe=0,ie=null;function re(n=!1){Pe++,ge?.remove(),ge=null,n&&ie?.focus(),ie=null}typeof document<"u"&&(document.addEventListener("pointerdown",n=>{const e=n.target;e?.closest(".atfb-tagpick__open")||ge?.contains(e)||re()}),document.addEventListener("keydown",n=>{n.key==="Escape"&&ie&&(re(!0),n.preventDefault(),n.stopImmediatePropagation())},!0));function bt(n,e){const s=n.selectionStart??n.value.length,t=n.selectionEnd??n.value.length;n.value=n.value.slice(0,s)+e+n.value.slice(t);const a=s+e.length;n.setSelectionRange(a,a),n.dispatchEvent(new Event("input",{bubbles:!0})),n.focus()}function Ws(n){let e=0,s=window.innerHeight,t=n.parentElement;for(;t&&t!==document.body;){if(/auto|scroll|hidden|clip/.test(getComputedStyle(t).overflowY)){const a=t.getBoundingClientRect();e=Math.max(e,a.top),s=Math.min(s,a.bottom)}t=t.parentElement}return{top:e,bottom:s}}function Us(n,e){const s=r("input",{class:"atfb-input atfb-tagpick__search",type:"search",placeholder:"Search values…",attrs:{"aria-label":"Search values"}}),t=r("div",{class:"atfb-tagpick__list"}),a=o=>{t.replaceChildren();const l=o.trim().toLowerCase();let c=0;for(const d of n){const h=d.items.filter(m=>!l||m.label.toLowerCase().includes(l)||m.tag.toLowerCase().includes(l));if(!h.length){d.empty&&!l&&!d.items.length&&t.append(r("p",{class:"atfb-tagpick__group",text:d.label}),r("p",{class:"atfb-tagpick__empty",text:d.empty}));continue}t.append(r("p",{class:"atfb-tagpick__group",text:d.label}));for(const m of h)c+=1,t.append(r("button",{class:"atfb-tagpick__item",type:"button",on:{click:()=>{re(),e(m.tag)}},children:[r("span",{class:"atfb-tagpick__item-main",children:[r("span",{class:"atfb-tagpick__label",text:m.label}),r("code",{class:"atfb-tagpick__tag",text:m.tag})]}),m.hint||m.sample?r("span",{class:"atfb-tagpick__meta",text:`{the value of ${m.label}}`}):null]}))}!c&&l&&t.append(r("p",{class:"atfb-tagpick__empty",text:`Nothing matches “${o}”.`}))};a(""),s.addEventListener("input",()=>a(s.value));const i=r("div",{class:"atfb-tagpick",attrs:{role:"dialog","aria-label":"Insert a value"},children:[r("p",{class:"atfb-tagpick__intro",text:"Pick something to drop in. It is filled in when the form is submitted."}),s,t]});return i.addEventListener("keydown",o=>{const l=[...t.querySelectorAll("button")],c=l.indexOf(document.activeElement);if(o.key==="ArrowDown"||o.key==="ArrowUp"){o.preventDefault();const d=o.key==="ArrowDown"?c+1:c<0?l.length-1:c-1;l[(d+l.length)%l.length]?.focus()}else o.key==="Enter"&&o.target===s&&(o.preventDefault(),l[0]?.click());o.stopPropagation()}),i}function Be(n,e){const s=()=>Promise.resolve(e.groups?e.groups():ft(e.formId??0)),t=r("button",{class:"atfb-button atfb-button--ghost atfb-tagpick__open",type:"button",title:"Insert a value from the submission",attrs:{"aria-haspopup":"dialog"},children:[B("shortcode"),r("span",{text:"Insert a value"})]}),a=r("div",{class:"atfb-taggable",children:[n,r("div",{class:"atfb-taggable__tools",children:[t]})]}),i=e.preview===!1?null:r("p",{class:"atfb-taggable__preview"});i&&a.append(i);const o=()=>{if(i){if(!mt(n.value)){i.textContent="",i.hidden=!0;return}s().then(c=>{mt(n.value)&&(i.hidden=!1,i.replaceChildren(r("span",{class:"atfb-taggable__preview-label",text:"Reads as"}),r("span",{text:Hs(n.value,c)})))})}};n.addEventListener("input",o),o();const l=(c,d)=>{re();const h=Pe,m=n.value;ie=n,s().then(g=>{if(h!==Pe||!a.isConnected||n.value!==m)return;const u=Us(g,S=>{n.setSelectionRange(c,d),bt(n,S)});a.append(u),ge=u;const f=Ws(a);u.style.maxBlockSize=`${Math.max(0,Math.min(320,f.bottom-f.top-8))}px`;const y=a.getBoundingClientRect(),p=u.getBoundingClientRect().height,x=y.bottom+p<=f.bottom?y.bottom:y.top-p,F=Math.max(f.top+4,Math.min(x,f.bottom-p-4));u.style.insetBlockStart=`${F-y.top}px`,u.querySelector(".atfb-tagpick__search")?.focus({preventScroll:!0})})};return t.addEventListener("click",c=>{if(c.stopPropagation(),ie===n){re(!0);return}l(n.selectionStart??n.value.length,n.selectionEnd??n.value.length)}),n.addEventListener("input",c=>{const d=c,h=n.selectionStart??0;!d.isComposing&&d.data==="{"&&n.value[h-1]==="{"&&l(h-1,h+(n.value[h]==="}"?1:0))}),a}function gt(n,e){const s=parseFloat(String(n??""));return Number.isFinite(s)?s:e}function vt(n,e){let s=e[0];for(const t of e)Math.abs(t.at-n)n.accent??"#2271b1",apply:n=>({accent:n,"accent-soft":`color-mix( in srgb, ${n} 12%, transparent )`,"border-focus":n,"focus-ring-color":n,"button-bg":n,"button-bg-hover":`color-mix( in srgb, ${n} 88%, #000 )`})},{id:"roundness",label:"Roundness",hint:"Corners, everywhere at once.",kind:"scale",steps:je.map(({value:n,label:e})=>({value:n,label:e})),owns:["radius-field","radius-button","radius-card","radius-check"],read:n=>vt(gt(n["radius-field"],4),je),apply:n=>{const e=je.find(s=>s.value===n)?.at??4;return{"radius-field":`${e}px`,"radius-button":`${e}px`,"radius-card":`${Math.min(e*2,28)}px`,"radius-check":`${Math.min(e,6)}px`}}},{id:"density",label:"Density",hint:"How much air the form has.",kind:"scale",steps:He.map(({value:n,label:e})=>({value:n,label:e})),owns:["pad-field-x","pad-field-y","gap-fields","gap-label","button-pad-x","button-pad-y"],read:n=>vt(gt(n["pad-field-y"],9),He),apply:n=>{const e=He.find(s=>s.value===n)?.at??9;return{"pad-field-y":`${e}px`,"pad-field-x":`${Math.round(e*1.4)}px`,"gap-fields":`${Math.round(e*2.2)}px`,"gap-label":`${Math.max(4,Math.round(e*.7))}px`,"button-pad-y":`${e+2}px`,"button-pad-x":`${Math.round(e*2.2)}px`}}},{id:"shadow",label:"Depth",hint:"How far the form sits off the page.",kind:"scale",steps:zs,owns:["shadow-field","shadow-field-focus","shadow-button","shadow-button-hover","shadow-card"],read:n=>{const e=n["shadow-card"]??"none";return e==="none"||e.trim()===""?n["shadow-field"]&&n["shadow-field"]!=="none"?"hard":"none":e.includes("0 1px")||e.includes("2px")?"subtle":"lifted"},apply:n=>{switch(n){case"subtle":return{"shadow-field":"none","shadow-field-focus":"none","shadow-button":"0 1px 2px rgba( 0, 0, 0, 0.12 )","shadow-button-hover":"0 2px 4px rgba( 0, 0, 0, 0.16 )","shadow-card":"0 1px 3px rgba( 0, 0, 0, 0.1 )"};case"lifted":return{"shadow-field":"none","shadow-field-focus":"0 4px 12px rgba( 0, 0, 0, 0.12 )","shadow-button":"0 4px 10px rgba( 0, 0, 0, 0.16 )","shadow-button-hover":"0 8px 20px rgba( 0, 0, 0, 0.2 )","shadow-card":"0 10px 30px rgba( 0, 0, 0, 0.14 )"};case"hard":return{"shadow-field":"3px 3px 0 currentColor","shadow-field-focus":"5px 5px 0 currentColor","shadow-button":"4px 4px 0 currentColor","shadow-button-hover":"2px 2px 0 currentColor","shadow-card":"none"};default:return{"shadow-field":"none","shadow-field-focus":"none","shadow-button":"none","shadow-button-hover":"none","shadow-card":"none"}}}},{id:"field-style",label:"Fields",hint:"How an input is drawn.",kind:"choice",steps:Ys,owns:["field-style"],read:n=>n["field-style"]??"outline",apply:n=>({"field-style":n})},{id:"labels",label:"Labels",hint:"Where the question sits relative to the answer.",kind:"choice",steps:Vs,owns:["label-position"],read:n=>n["label-position"]??"top",apply:n=>({"label-position":n})}]}function Ks(n){return We().find(e=>e.owns.includes(n))??null}function yt(n){let e=n.activeSlug,s={...n.overrides},t=n.themes;const a=v=>{s=v,n.onOverridesReplaced?.({...v})},i=r("div",{class:"atfs-preview__frame"}),o=r("div",{class:"atfs-controls__body"}),l=r("div",{class:"atfs-quick"}),c=r("div",{class:"atfs-themes"}),d=Me(async()=>{try{const v=await n.previewFor(e,s),_=i.closest(".atf-studio__preview"),k=_?.scrollTop??0;i.innerHTML=v,_&&(_.scrollTop=k),document.dispatchEvent(new CustomEvent("alltfo-refresh"))}catch(v){R(i),i.append(r("p",{class:"atfb-error",text:v instanceof Error?v.message:"Preview failed."}))}},180),h={"label-position":"atf-labels-","field-style":"atf-fields-"},m=(v,_,k)=>{const E=k.replace(/[^a-z0-9_-]/gi,"");for(const A of[...v.classList])A.startsWith(_)&&v.classList.remove(A);E&&v.classList.add(_+E)},g=v=>{const _=i.querySelector(".atf-form-wrap"),k=_?.querySelector(".atf-form");if(_)for(const[E,A]of Object.entries(v))A===""?_.style.removeProperty(`--atf-${E}`):_.style.setProperty(`--atf-${E}`,A),k&&h[E]&&m(k,h[E],A||(t.find(P=>P.slug===e)?.resolved[E]??""))},u=v=>{g(v.resolved);const _=i.querySelector(".atf-form");_&&(m(_,"atf-theme-",v.slug),_.classList.toggle("atf-is-dark",!!v.dark))},f=v=>s[v.token]!==void 0?s[v.token]:t.find(k=>k.slug===e)?.resolved?.[v.token]??v.default,y=()=>{R(c);for(const v of t){const _=r("button",{class:`atfs-theme${v.slug===e?" is-active":""}`,type:"button",attrs:{"aria-pressed":v.slug===e,title:v.description},children:[r("span",{class:"atfs-theme__chip",style:{background:v.resolved.surface??"#fff",borderColor:v.resolved.border??"#ccc",borderRadius:v.resolved["radius-field"]??"4px",boxShadow:v.resolved["shadow-card"]??"none"},children:[r("span",{class:"atfs-theme__accent",style:{background:v.resolved.accent??"#2271b1",borderRadius:v.resolved["radius-button"]??"4px"}})]}),r("span",{class:"atfs-theme__name",text:v.label}),v.custom?r("span",{class:"atfb-badge",text:"yours"}):null],on:{click:()=>{e=v.slug,a({}),n.onTheme(e),u(v),y(),S(),F(),ts()}}});c.append(_)}},p=()=>{const _={...t.find(k=>k.slug===e)?.resolved??{}};for(const[k,E]of Object.entries(s))_[k]=E;return _},x=v=>{const _=l.closest(".atf-studio__controls"),k=_?.scrollTop??0;v(),_&&(_.scrollTop=k)},F=Me(()=>x(b),150),S=()=>{const v=p(),_=l.querySelectorAll(".atfs-dial");We().forEach((k,E)=>{const A=_[E];if(!A)return;const P=k.read(v);if(k.kind==="colour"){const O=A.querySelector('input[type="color"]'),K=A.querySelector("input.atfb-input");O&&document.activeElement!==O&&(O.value=ve(P)),K&&document.activeElement!==K&&(K.value=P);return}A.querySelectorAll(".atfs-segment").forEach((O,K)=>{const Q=k.steps?.[K]?.value===P;O.classList.toggle("is-on",Q),O.setAttribute("aria-pressed",String(Q))})})},H=(v,_)=>{const k=v.apply(_,p());for(const[E,A]of Object.entries(k))s[E]=A,n.onOverride(E,A);g(k),S(),F()},L=()=>{R(l);const v=p();for(const _ of We()){const k=_.read(v);let E;if(_.kind==="colour"){const A=r("input",{class:"atfs-color",type:"color",value:ve(k),attrs:{"aria-label":_.label},on:{input:P=>H(_,P.target.value)}});E=r("div",{class:"atfs-color-row",children:[A,T(k,P=>H(_,P))]})}else E=r("div",{class:`atfs-segmented atfs-segmented--${_.kind}`,attrs:{role:"group","aria-label":_.label},children:(_.steps??[]).map(A=>r("button",{class:`atfs-segment${A.value===k?" is-on":""}`,type:"button",text:A.label,attrs:{"aria-pressed":A.value===k},on:{click:()=>H(_,A.value)}}))});l.append(r("div",{class:"atfs-dial",children:[r("div",{class:"atfs-dial__head",children:[r("span",{class:"atfs-dial__label",text:_.label}),r("span",{class:"atfs-dial__hint",text:_.hint})]}),E]}))}},b=()=>{R(o);const v=new Map;for(const k of n.tokens){const E=v.get(k.group)??[];E.push(k),v.set(k.group,E)}const _={colour:"Colour",shape:"Corners and borders",shadow:"Shadows",fields:"Field style",space:"Spacing",type:"Type",labels:"Labels",button:"Buttons",focus:"Focus ring",motion:"Motion"};for(const[k,E]of v)o.append(r("details",{class:"atfs-group",attrs:{open:k==="colour"},children:[r("summary",{text:_[k]??k}),...E.map(A=>M(A))]}))},M=v=>{const _=f(v),k=O=>{O===""?delete s[v.token]:s[v.token]=O,n.onOverride(v.token,O),g({[v.token]:O})};let E;if(v.control==="color"){const O=r("input",{class:"atfs-color",type:"color",value:ve(_),attrs:{"aria-label":`${v.label} colour`},on:{input:Q=>{const ce=Q.target.value;K.value=ce,k(ce)}}}),K=T(_,Q=>{O.value=ve(Q),k(Q)});E=r("div",{class:"atfs-color-row",children:[O,K]})}else if(v.control==="select")E=D(_,(v.options??[]).map(O=>({value:O,label:O})),k);else if(v.control==="length"){const O=parseFloat(_)||0,K=v.unit??"px",Q=v.token.includes("radius")?60:80,ce=r("input",{class:"atfs-range",type:"range",value:String(O),attrs:{min:"0",max:String(Q),step:"1","aria-label":v.label},on:{input:Ee=>{const ns=`${Ee.target.value}${K}`;ss.value=ns,k(ns)}}}),ss=T(_,Ee=>{ce.value=String(parseFloat(Ee)||0),k(Ee)});E=r("div",{class:"atfs-length-row",children:[ce,ss]})}else E=T(_,k);const A=w(v.label,E),P=Ks(v.token);return P&&(A.classList.add("is-dialled"),A.append(r("span",{class:"atfs-owned",text:P.label}))),s[v.token]!==void 0&&(A.classList.add("is-overridden"),A.append(r("button",{class:"atfs-reset",type:"button",text:"Reset",attrs:{"aria-label":`Reset ${v.label} to the theme's value`},on:{click:()=>{k(""),x(b)}}}))),A},$=async()=>{const v=t.find(E=>E.slug===e),_=v?`${v.label} (mine)`:"My theme",k=window.prompt("Name this theme",_);if(k)try{const E={};for(const P of n.tokens){const O=f(P);O!==P.default&&(E[P.token]=O)}const A=await I.saveTheme({label:k,tokens:E});t=[...t.filter(P=>P.slug!==A.slug),A],n.onThemesChanged(t),e=A.slug,a({}),n.onTheme(e),y(),S(),x(b),N("Theme saved",A.label)}catch(E){N("Could not save the theme",E instanceof Error?E.message:"","error")}},W=async()=>{const v=t.find(_=>_.slug===e);if(v?.custom&&await ae(`Delete “${v.label}”? Forms using it fall back to Clean.`,"Delete theme"))try{await I.deleteTheme(v.id),t=t.filter(k=>k.slug!==v.slug),n.onThemesChanged(t),e="clean",a({}),n.onTheme(e),y(),S(),x(b);const _=t.find(k=>k.slug===e);_&&u(_)}catch(_){N("Could not delete the theme",_ instanceof Error?_.message:"","error")}},j=async()=>{const v=t.find(E=>E.slug===e),_={label:v?.label??e,tokens:{...v?.tokens??{},...s}},k=JSON.stringify(_,null," ");try{await navigator.clipboard.writeText(k),N("Theme copied","Paste it into another site to import it.")}catch{window.prompt("Copy this theme",k)}},le=()=>{const v=window.prompt("Paste a theme");if(v)try{const _=JSON.parse(v);if(!_.tokens||typeof _.tokens!="object")throw new Error("That does not look like a theme.");for(const[k,E]of Object.entries(_.tokens))s[k]=String(E),n.onOverride(k,String(E));g({...s}),S(),x(b)}catch(_){N("Could not read that theme",_ instanceof Error?_.message:"","error")}},es=C("Delete",()=>void W(),"danger"),ts=()=>{const v=t.find(_=>_.slug===e);es.hidden=!v?.custom};return y(),L(),b(),d(),ts(),r("div",{class:"atf-studio",children:[r("div",{class:"atf-studio__top",children:[r("div",{class:"atf-studio__topbar",children:[r("h2",{class:"atf-studio__heading",text:"Theme"}),r("div",{class:"atfs__actions",children:[C("Save as a theme",()=>void $(),"primary"),C("Export",()=>void j()),C("Import",le),es]})]}),c]}),r("div",{class:"atf-studio__panes",children:[r("aside",{class:"atf-studio__controls",children:[r("p",{class:"atfb-hint",text:"Changes apply to this form only, until you save them as a theme."}),l,r("details",{class:"atfs-advanced",children:[r("summary",{text:"Every setting"}),r("p",{class:"atfb-hint",text:"The full token list. The dials above write into it."}),o]})]}),r("main",{class:"atf-studio__preview",children:[r("h2",{class:"screen-reader-text",text:"Live preview"}),i]})]})]})}async function Ue(){const n=document.querySelector("[data-atfs-root]");if(!n||n.dataset.atfsMounted)return;n.dataset.atfsMounted="1",Fe(n);const e=n.querySelector("[data-atfs-bar]"),s=n.querySelector(".atfs__body")??n;try{const[t,a,i]=await Promise.all([I.config(),I.listThemes(),I.listForms()]);if(!i.length){R(s),s.append(r("div",{class:"atfb-empty",children:[r("h2",{text:"No forms to preview"}),r("p",{text:"Make a form first — a theme needs something to dress."})]}));return}let o=i[0],l={},c=o.theme;const d=()=>{R(s),s.append(yt({themes:a,tokens:t.tokens,activeSlug:c,overrides:l,standalone:!0,onTheme:h=>{c=h},onOverride:(h,m)=>{m===""?delete l[h]:l[h]=m},onOverridesReplaced:h=>{l={...h}},previewFor:async(h,m)=>{const g=await I.getForm(o.id);g.schema.settings.theme=h,g.schema.settings.themeOverrides=m;const{html:u}=await I.preview(o.id,{schema:g.schema,theme:h});return u},onThemesChanged:h=>{a.length=0,a.push(...h)}}))};e&&(R(e),e.append(r("span",{class:"atfs__label",text:"Preview against"}),D(String(o.id),i.map(h=>({value:String(h.id),label:h.title||"(untitled)"})),h=>{o=i.find(m=>String(m.id)===h)??i[0],l={},d()}))),d()}catch(t){R(s),s.append(r("p",{class:"atfb-error",text:t instanceof Error?t.message:"Could not load themes."}))}}typeof document<"u"&&(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>void Ue()):Ue(),document.addEventListener("os-window-content-loaded",()=>void Ue()));function ve(n){const e=n.trim();return/^#[0-9a-f]{6}$/i.test(e)?e:/^#[0-9a-f]{3}$/i.test(e)?`#${e[1]}${e[1]}${e[2]}${e[2]}${e[3]}${e[3]}`:"#888888"}const Gs="mailpoet",ye="",Xs=["checkbox","checkboxes","toggle","consent","gdpr"],wt=["email"];function _t(n){return n.schema.actions.find(e=>e.type==="mailpoet")}function ze(n){const e=n?.settings??{};return{lists:Array.isArray(e.lists)?e.lists.map(Number).filter(Boolean):[],email_field:typeof e.email_field=="string"?e.email_field:"",first_name_field:typeof e.first_name_field=="string"?e.first_name_field:"",last_name_field:typeof e.last_name_field=="string"?e.last_name_field:""}}function Zs(n){return n?.logic?.enabled?n.logic.rules[0]?.field??ye:ye}function xt(n,e,s){const t=n.find(a=>e.includes(a.type));return t?t.id:s?n.find(a=>s.test(a.label))?.id??"":""}function Js(n,e,s){const t=r("section",{class:"atfm-form"}),a=l=>r("span",{class:`atfm-form__mark${l?"":" atfm-form__mark--off"}`,children:[r("img",{attrs:{src:e.symbol,alt:"",width:"20",height:"20"}})]}),i=l=>{R(t),t.classList.remove("is-open"),t.append(r("div",{class:"atfm-form__head",children:[a(l.length>0),r("div",{class:"atfm-form__title",children:[r("strong",{text:n.title||`Form ${n.id}`}),l.length?r("span",{class:"atfm-form__status is-on",text:`Subscribing → ${l.join(", ")}`}):r("span",{class:"atfm-form__status",text:"Not connected"})]}),C(l.length?"Edit":"Connect",()=>void o(),"secondary")]}))},o=async()=>{t.classList.add("is-open"),R(t),t.append(r("p",{class:"atfm-hint",text:"Loading the form…"}));let l;try{l=await I.getForm(n.id)}catch{R(t),t.append(r("p",{class:"atfm-hint",text:"Could not load this form."}));return}const c=l.schema.fields.filter(b=>b.type!=="page_break"),d=_t(l),h=ze(d),m=new Set(h.lists.filter(b=>e.lists.some(M=>M.id===b)));let g=Zs(d),u=h.email_field||xt(c,wt,/mail/i),f=h.first_name_field,y=h.last_name_field;c.some(b=>b.id===u)||(u=xt(c,wt,/mail/i));const p=c.filter(b=>Xs.includes(b.type));!d&&p.length&&(g=p[0].id);const x=(b,M)=>[{value:"",label:M},...b.map($=>({value:$.id,label:$.label||$.id}))],F=r("div",{class:"atfm-lists"});(()=>{R(F);for(const b of e.lists){const M=r("input",{attrs:{type:"checkbox"}});M.checked=m.has(b.id),M.addEventListener("change",()=>{M.checked?m.add(b.id):m.delete(b.id)}),F.append(r("label",{class:"atfm-list",children:[M,r("span",{text:b.name})]}))}})();const H=async(b=!1)=>{const M=l.schema.actions.filter($=>$.type!=="mailpoet");if(!b){if(!m.size){N("Pick a list","A subscription needs at least one MailPoet list.","error");return}if(!u){N("Pick the email field","MailPoet needs to know which answer is the address.","error");return}M.push({id:Gs,type:"mailpoet",enabled:!0,logic:g===ye?{enabled:!1,action:"show",match:"all",rules:[]}:{enabled:!0,action:"show",match:"all",rules:[{field:g,operator:"not_empty",value:""}]},settings:{lists:[...m],email_field:u,first_name_field:f,last_name_field:y}})}l.schema.actions=M;try{await I.updateForm(l.id,{schema:l.schema})}catch($){N("Could not save",$ instanceof Error?$.message:"","error");return}N(b?"Disconnected":"Connected to MailPoet",b?`${n.title} no longer subscribes anyone.`:`${n.title} now subscribes opted-in visitors.`),i(b?[]:e.lists.filter($=>m.has($.id)).map($=>$.name))};R(t);const L=b=>r("span",{class:"atfm-form__section",text:b});t.append(r("div",{class:"atfm-form__head",children:[a(!!d),r("div",{class:"atfm-form__title",children:[r("strong",{text:n.title||`Form ${n.id}`})]}),C("Close",()=>i(d?e.lists.filter(b=>ze(d).lists.includes(b.id)).map(b=>b.name):[]),"ghost")]}),r("div",{class:"atfm-form__body",children:[L("Where they land"),w("Lists",F,"MailPoet sends its own confirmation email before anyone is truly on a list."),L("Who they are"),w("Email address",D(u,x(c,"— pick a field —"),b=>{u=b}),"The answer that carries their address."),w("First name",D(f,x(c,"— none —"),b=>{f=b})),w("Last name",D(y,x(c,"— none —"),b=>{y=b})),L("When to subscribe"),w("Subscribe",D(g,[...p.map(b=>({value:b.id,label:`Only when “${b.label||b.id}” is ticked`})),{value:ye,label:"Everyone who submits"}],b=>{g=b}),p.length?"Bind it to a box they tick. Subscribing everyone is rarely what visitors expect.":"Add a checkbox or toggle field to the form to offer a proper opt-in."),r("div",{class:"atfm-form__actions",children:d?[C("Save connection",()=>void H(),"primary"),C("Disconnect",()=>void H(!0),"danger")]:[C("Save connection",()=>void H(),"primary")]})]}))};return i([]),I.getForm(n.id).then(l=>{const c=_t(l);if(c&&!t.classList.contains("is-open")){const d=e.lists.filter(h=>ze(c).lists.includes(h.id)).map(h=>h.name);i(d)}}).catch(()=>{}),s.append(t),t}function Qs(n){const e=n.logo?r("span",{class:"atfm-hero__logo",children:[r("img",{attrs:{src:n.logo,alt:"MailPoet",width:"210",height:"105"}})]}):r("span",{class:"atfm-hero__logo atfm-hero__logo--glyph",text:"📬"});return r("header",{class:"atfm-hero",children:[e,r("div",{class:"atfm-hero__words",children:[r("h1",{text:"Turn submissions into subscribers"}),r("p",{text:"Every submission is someone choosing to talk to you. Connect a form to MailPoet and the ones who opt in land on your lists by themselves — named, consented, and confirmed by MailPoet’s own double opt-in."}),n.active?r("p",{class:"atfm-hero__status is-on",text:n.lists.length===1?"Connected — 1 list ready for subscribers":`Connected — ${n.lists.length} lists ready for subscribers`}):r("p",{class:"atfm-hero__status",text:"MailPoet is not installed on this site yet"})]})]})}function en(){const n=(s,t,a)=>r("div",{class:"atfm-why__card",children:[r("span",{class:"atfm-why__icon",text:s}),r("strong",{text:t}),r("p",{text:a})]}),e=(s,t)=>r("a",{text:t,attrs:{href:s,target:"_blank",rel:"noreferrer"}});return[r("div",{class:"atfm-why",children:[n("✉️","Beautiful emails, made in WordPress","Design newsletters and welcome emails in a drag-and-drop editor that lives in your own admin — no external account to juggle."),n("🤝","Consent you can stand behind","Double opt-in out of the box: every address your forms send over is confirmed by the visitor before a single campaign reaches it."),n("🚀","Free to grow with","Free up to 500 subscribers, welcome automations, WooCommerce emails and open-rate stats included — a paid addon anywhere else.")]}),r("p",{class:"atfm-links",children:[e("https://www.mailpoet.com/features/","Explore MailPoet’s features ↗"),e("https://kb.mailpoet.com/","Guides & docs ↗"),e("https://www.mailpoet.com/pricing/","Plans & the free tier ↗")]})]}function tn(){const n=(e,s)=>r("div",{class:"atfm-step",children:[r("span",{class:"atfm-step__n",text:e}),r("span",{text:s})]});return r("div",{class:"atfm-steps",children:[n("1","Pick a form below"),n("2","Choose the lists it feeds"),n("3","Bind it to an opt-in — MailPoet confirms the rest")]})}async function Ye(){const n=document.querySelector("[data-atfm-root]");if(!n||n.dataset.atfmMounted)return;n.dataset.atfmMounted="1",Fe(n);const e=n.querySelector("[data-atfm-bar]"),s=n.querySelector("[data-atfm-body]")??n;try{const[t,a]=await Promise.all([I.mailpoet(),I.listForms()]);if(e?.remove(),R(s),s.append(Qs(t),...en()),!t.active){s.append(r("div",{class:"atfm-pitch",children:[r("p",{text:"MailPoet sends newsletters from right inside WordPress — no external account, free to start. Install it and every form here gets a subscribe switch: reservations feed your announcements, signups feed your schedule, enquiries feed your news."}),r("a",{class:"atfm-pitch__cta",text:"Install MailPoet — it’s free",attrs:{href:t.adminUrl,target:"_blank",rel:"noreferrer"}})]}));return}if(!t.lists.length){s.append(r("div",{class:"atfm-pitch",children:[r("p",{text:"MailPoet is here, but it has no list yet. Make one, and your forms will have somewhere to send subscribers."}),r("a",{class:"atfm-pitch__cta",text:"Open MailPoet lists",attrs:{href:t.adminUrl,target:"_blank",rel:"noreferrer"}})]}));return}s.append(tn());const i=r("section",{class:"atfm-forms",children:[r("div",{class:"atfm-forms__head",children:[r("h2",{text:"Your forms"}),r("p",{class:"atfm-hint",text:"Connected forms wear the MailPoet mark in colour."})]})]});if(s.append(i),!a.length){i.append(r("p",{class:"atfm-hint",text:"No forms yet — make one, then come back to connect it."}));return}for(const o of a)Js(o,t,i)}catch{e&&(e.textContent="Could not reach the site — reload the window to try again.")}}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>void Ye()):Ye(),document.addEventListener("os-window-content-loaded",()=>void Ye());const we={plain:"",simple:"✓",minimal:"",card:"🎉",check:"",confetti:"🎉",fireworks:"🎆",sparkles:"✨",typewriter:""};function kt(){return{style:"simple",title:"",icon:"",accent:"",intensity:"medium",showButton:!1,buttonLabel:""}}function Ve(n){const e={...kt(),...n??{}};return e.style in we||(e.style="simple"),e}function sn(){return typeof window.matchMedia=="function"&&window.matchMedia("(prefers-reduced-motion: reduce)").matches}function nn(n,e,s){const t=Ve(e),a=document.createElement("div");if(a.className=`atf-confirmation atf-success atf-success--${t.style}`,a.setAttribute("role","status"),a.setAttribute("tabindex","-1"),t.style==="plain")return a.className="atf-confirmation",a.innerHTML=n,a;t.accent&&a.style.setProperty("--atf-accent",t.accent);const i=document.createElement("div");i.className="atf-success__inner",a.append(i);const o=t.icon||we[t.style];if(t.style==="check")i.insertAdjacentHTML("beforeend",'');else if(o){const c=document.createElement("span");c.className="atf-success__icon",c.setAttribute("aria-hidden","true"),c.textContent=o,i.append(c)}if(t.title){const c=document.createElement("h2");c.className="atf-success__title",c.textContent=t.title,i.append(c)}const l=document.createElement("div");if(l.className="atf-success__message",l.innerHTML=n,i.append(l),t.style==="typewriter"&&a.setAttribute("aria-label",l.textContent??""),t.showButton){const c=document.createElement("button");c.type="button",c.className="atf-button atf-button--ghost atf-success__again",c.textContent=t.buttonLabel||"Fill it in again",c.addEventListener("click",()=>s?s():window.location.reload()),i.append(c)}return a}function an(n,e){const s=Ve(e);if(sn())return()=>{};switch(s.style){case"confetti":return rn(n,s);case"fireworks":return on(s);case"sparkles":return ln(n,s);case"typewriter":return cn(n);default:return()=>{}}}function oe(n){return{low:.5,medium:1,high:1.8}[n.intensity]}function St(){const n=document.createElement("canvas");n.className="atf-success-canvas",n.setAttribute("aria-hidden","true");const e=Math.min(window.devicePixelRatio||1,2);n.width=Math.floor(window.innerWidth*e),n.height=Math.floor(window.innerHeight*e),document.body.append(n);const s=n.getContext("2d");return s?.scale(e,e),{canvas:n,ctx:s,stop:()=>n.remove()}}const Ct=["#f43f5e","#f59e0b","#10b981","#3b82f6","#8b5cf6","#ec4899","#eab308"];function rn(n,e){const{ctx:s,stop:t}=St();if(!s)return t(),()=>{};const a=e.accent?[e.accent,...Ct]:Ct,i=[],o=n.getBoundingClientRect(),l=o.left+o.width/2,c=Math.min(o.top+40,window.innerHeight-20),d=(x,F,S)=>{const H=S?Math.PI*(1.15+.7*Math.random()):0,L=S?9+Math.random()*8:0;return{x,y:F,vx:S?Math.cos(H)*L*(Math.random()<.5?1:-1):(Math.random()-.5)*1.5,vy:S?Math.sin(H)*L:1+Math.random()*2,w:6+Math.random()*5,h:8+Math.random()*7,angle:Math.random()*Math.PI,spin:(Math.random()-.5)*.3,color:a[Math.floor(Math.random()*a.length)],wobble:Math.random()*Math.PI*2}},h=Math.round(90*oe(e));for(let x=0;x{if(g>=m){window.clearInterval(u);return}i.push(d(Math.random()*window.innerWidth,-20,!1)),g++},2e3/m);let f=0;const y=performance.now(),p=x=>{s.clearRect(0,0,window.innerWidth,window.innerHeight);let F=!1;for(const S of i)S.vy+=.16,S.vx*=.99,S.vy*=.985,S.wobble+=.1,S.x+=S.vx+Math.sin(S.wobble)*.8,S.y+=S.vy,S.angle+=S.spin,S.y{window.clearInterval(u),window.cancelAnimationFrame(f),t()}}function on(n){const{ctx:e,stop:s}=St();if(!e)return s(),()=>{};const t=[],a=[],i=Math.round(6*oe(n))+2;let o=0;const l=()=>{t.push({x:window.innerWidth*(.15+.7*Math.random()),y:window.innerHeight,vy:-(9+Math.random()*4),targetY:window.innerHeight*(.15+.3*Math.random()),hue:Math.floor(Math.random()*360)}),o++};l();const c=window.setInterval(()=>{if(o>=i){window.clearInterval(c);return}l()},3500/i),d=u=>{const f=Math.round(70*oe(n));for(let y=0;y{e.clearRect(0,0,window.innerWidth,window.innerHeight);for(let y=t.length-1;y>=0;y--){const p=t[y];p.y+=p.vy,p.vy+=.08,e.fillStyle=`hsl(${p.hue} 90% 65%)`,e.fillRect(p.x-1.5,p.y,3,10),(p.y<=p.targetY||p.vy>=-1)&&(d(p),t.splice(y,1))}for(let y=a.length-1;y>=0;y--){const p=a[y];if(p.x+=p.vx,p.y+=p.vy,p.vy+=.045,p.vx*=.985,p.vy*=.985,p.life-=p.decay,p.life<=0){a.splice(y,1);continue}const x=p.life*(.7+.3*Math.random());e.globalAlpha=Math.max(0,x),e.fillStyle=`hsl(${p.hue} 95% ${55+25*p.life}%)`,e.beginPath(),e.arc(p.x,p.y,1.1+1.6*p.life,0,Math.PI*2),e.fill()}e.globalAlpha=1,!(o>=i&&t.length===0&&a.length===0)&&u-m<9e3?h=window.requestAnimationFrame(g):s()};return h=window.requestAnimationFrame(g),()=>{window.clearInterval(c),window.cancelAnimationFrame(h),s()}}function ln(n,e){const s=e.icon||we.sparkles,t=Math.round(22*oe(e)),a=[];let i=0;const o=()=>{const c=document.createElement("span");c.className="atf-success__spark",c.setAttribute("aria-hidden","true"),c.textContent=s,c.style.insetInlineStart=`${4+Math.random()*92}%`,c.style.animationDuration=`${2.6+Math.random()*1.8}s`,c.style.animationDelay=`${Math.random()*.3}s`,c.style.fontSize=`${14+Math.random()*14}px`,c.addEventListener("animationend",()=>c.remove()),n.append(c),a.push(c),i++};o();const l=window.setInterval(()=>{if(i>=t){window.clearInterval(l);return}o()},2800/t);return()=>{window.clearInterval(l),a.forEach(c=>c.remove())}}function cn(n){const e=n.querySelector(".atf-success__message");if(!e)return()=>{};const s=e.innerHTML,t=e.textContent??"";if(!t)return()=>{};e.textContent="",e.classList.add("is-typing");const a=Math.min(45,3800/t.length);let i=0;const o=window.setInterval(()=>{i++,e.textContent=t.slice(0,i),i>=t.length&&(window.clearInterval(o),e.classList.remove("is-typing"),e.innerHTML=s)},a);return()=>{window.clearInterval(o),e.classList.remove("is-typing"),e.innerHTML=s}}const dn={min:-1,max:-1,sum:-1,avg:-1,round:-1,ceil:1,floor:1,abs:1,sqrt:1,pow:2},Et={"+":{precedence:1,right:!1},"-":{precedence:1,right:!1},"*":{precedence:2,right:!1},"/":{precedence:2,right:!1},"%":{precedence:2,right:!1},"^":{precedence:4,right:!0}};function hn(n,e,s=[]){if(!n||!n.trim()||n.length>2e3)return null;const t=un(n,e,s),a=bn(t);if(!a)return null;const i=vn(a);if(!i)return null;const o=yn(i);return o===null||!Number.isFinite(o)?null:o}function un(n,e,s){return n.replace(/\{([a-zA-Z0-9_]+)(?:\.([a-zA-Z0-9_]+))?\}/g,(t,a,i,o)=>pn(n,o,t.length,a,i??"",e,s))}function pn(n,e,s,t,a,i,o){const l=fn(o,t),c=Object.prototype.hasOwnProperty.call(i,t)?i[t]:null;if(a===""){const u=l?.type==="repeater"?Lt(c).length:Ke(c,l);return $t(u)}const h=(l?.fields??[]).find(u=>u.id===a)??null,m=Lt(c).map(u=>Ke(u[a]??"",h));if(!m.length)return"0";const g=m.map($t);return mn(n,e,s)?g.join(", "):g.length===1?g[0]:`( ${g.join(" + ")} )`}function fn(n,e){for(const s of n){if(s.id===e)return s;for(const t of s.fields??[])if(t.id===e)return t}return null}function mn(n,e,s){const t=n.slice(e+s).replace(/^\s+/,"");if(t===""||t[0]!==")")return!1;const a=n.slice(0,e).replace(/\s+$/,""),i=/([a-zA-Z_][a-zA-Z0-9_]*)\s*\($/.exec(a);return!!i&&["sum","avg","min","max"].includes(i[1].toLowerCase())}function Lt(n){if(!Array.isArray(n))return[];const e=[];for(const s of n){if(!s||typeof s!="object"||Array.isArray(s))continue;Object.values(s).some(a=>a!==""&&a!==null&&a!==void 0&&a!==!1&&!(Array.isArray(a)&&!a.length))&&e.push(s)}return e}function $t(n){const e=n.toFixed(10).replace(/0+$/,"").replace(/\.$/,"");return e===""||e==="-"?"0":e}function Ke(n,e){if(typeof n=="boolean")return n?1:0;if(Array.isArray(n))return n.reduce((a,i)=>a+Ke(i,e),0);if(n==null||n==="")return 0;const s=e?.choices??[];for(const a of s)if(String(a.value)===String(n)){if(typeof a.price=="number")return a.price;if(typeof a.points=="number")return a.points;break}const t=Number(n);return Number.isFinite(t)?t:0}function bn(n){const e=[];let s=0;for(;si.precedence||l.precedence===i.precedence&&!i.right){e.push(s.pop());continue}break}s.push(a);break}case"(":s.push(a);break;case")":{for(;s.length&&s[s.length-1].type!=="(";)e.push(s.pop());if(!s.length)return null;if(s.pop(),s.length&&s[s.length-1].type==="function"){const i=s.pop();i.arity=t.length?t.pop():1,e.push(i)}break}}for(;s.length;){const a=s.pop();if(a.type==="(")return null;e.push(a)}return e}function yn(n){const e=[];for(const s of n)switch(s.type){case"number":e.push(s.value);break;case"unary":{if(!e.length)return null;e.push(-e.pop());break}case"operator":{if(e.length<2)return null;const t=e.pop(),a=e.pop();switch(s.value){case"+":e.push(a+t);break;case"-":e.push(a-t);break;case"*":e.push(a*t);break;case"/":e.push(t===0?0:a/t);break;case"%":e.push(t===0?0:a%t);break;case"^":e.push(Math.pow(a,t));break;default:return null}break}case"function":{const t=s.arity??1;if(e.lengths+t,0);case"avg":return e.reduce((s,t)=>s+t,0)/e.length;case"round":{const s=Math.max(-10,Math.min(10,Math.trunc(e[1]??0))),t=Math.pow(10,s);return Math.round(e[0]*t)/t}case"ceil":return Math.ceil(e[0]);case"floor":return Math.floor(e[0]);case"abs":return Math.abs(e[0]);case"sqrt":return e[0]<0?0:Math.sqrt(e[0]);case"pow":return Math.pow(e[0],e[1]??2);default:return null}}const _n=["sum","min","max","avg","round","ceil","floor","abs","sqrt","pow"],At=["number","range","scale","rating","total","select","multiselect","radio","checkboxes","switch","quiz"];function _e(n,e){return n.filter(s=>s.id!==e&&At.includes(s.type))}function Tt(n){const e=[];for(const s of n){if(s.type!=="repeater")continue;const t=s.label||s.id;e.push({label:`${t} (how many)`,insert:`{${s.id}}`});for(const a of s.fields??[])At.includes(a.type)&&e.push({label:`${t} · ${a.label||a.id}`,insert:`{${s.id}.${a.id}}`})}return e}function xn(n,e){const s={};_e(n,e).forEach((t,a)=>{s[t.id]=a+1});for(const t of n){if(t.type!=="repeater"||t.id===e)continue;const a=t.fields??[],i=o=>Object.fromEntries(a.map((l,c)=>[l.id,c+1+o]));s[t.id]=[i(0),i(1)]}return s}function Mt(n,e,s){return Be(n,{preview:!1,groups:()=>[{id:"references",label:"Your questions",items:[..._e(e,s).map(t=>({label:t.label||t.id,tag:`{${t.id}}`,sample:"",hint:""})),...Tt(e.filter(t=>t.id!==s)).map(t=>({label:t.label,tag:t.insert,sample:"",hint:""}))],empty:"Add a number, scale or priced choice question to reference it here."}]})}function kn(n){const e=r("div",{class:"atfb-overlay"}),s=()=>{e.remove(),document.removeEventListener("keydown",t)},t=u=>{u.key==="Escape"&&s()},a=r("textarea",{class:"atfb-input atfb-formula__input",attrs:{rows:"3","aria-label":"Formula"}});a.value=String(n.field.formula??"");const i=r("p",{class:"atfb-formula__result",attrs:{"aria-live":"polite"}}),o=xn(n.fields,n.field.id),l=()=>{const u=a.value.trim();if(u===""){i.textContent="Empty. Reference a question below to start.",i.classList.remove("is-error");return}const f=hn(u,o,n.fields);if(f===null){i.textContent="This does not compute yet — check the braces and parentheses.",i.classList.add("is-error");return}const y=_e(n.fields,n.field.id).map((p,x)=>`${p.label||p.id} = ${x+1}`).concat(n.fields.filter(p=>p.type==="repeater"&&p.id!==n.field.id).map(p=>`${p.label||p.id} = 2 sample rows`)).join(", ");i.textContent=`With sample answers (${y}): ${f}`,i.classList.remove("is-error")};a.addEventListener("input",l);const c=(u,f,y=0)=>r("button",{class:"atfb-formula__chip",type:"button",text:u,on:{click:()=>{if(bt(a,f),y>0){const p=(a.selectionStart??a.value.length)-y;a.setSelectionRange(p,p)}}}}),d=_e(n.fields,n.field.id),h=Tt(n.fields.filter(u=>u.id!==n.field.id)),m=r("div",{class:"atfb-formula__chips",children:d.length||h.length?[...d.map(u=>c(u.label||u.id,`{${u.id}}`)),...h.map(u=>c(u.label,u.insert))]:[r("p",{class:"atfb-hint",text:"No number-shaped questions yet — add a number, scale or priced choice field and it appears here."})]}),g=r("div",{class:"atfb-formula__chips",children:_n.map(u=>c(`${u}()`,`${u}()`,1))});e.append(r("div",{class:"atfb-modal atfb-formula",attrs:{role:"dialog","aria-label":"Formula editor"},children:[r("h2",{text:"Formula"}),Mt(a,n.fields,n.field.id),i,w("Your questions",m,"Click one to reference its answer."),w("Functions",g),r("div",{class:"atfb-modal__actions",children:[C("Cancel",s),C("Save formula",()=>{n.onSave(a.value.trim()),s()},"primary")]})]})),e.addEventListener("click",u=>{u.target===e&&s()}),document.addEventListener("keydown",t),n.root.append(e),l(),a.focus(),a.setSelectionRange(a.value.length,a.value.length)}function Ft(n){return[...n.fields.filter(e=>e.type!=="page_break").map(e=>({key:`field:${e.id}`,label:`Field: ${e.label||e.id}`,fieldId:e.id,logic:e.logic})),...n.notifications.map(e=>({key:`notification:${e.id}`,label:`Notification: ${e.name||e.id}`,logic:e.logic})),...n.confirmations.map(e=>({key:`confirmation:${e.id}`,label:`Confirmation: ${e.name||e.id}`,logic:e.logic}))]}function It(n,e,s){return n.key!==e.key&&n.logic.rules.length>0&&n.logic.rules.every(t=>t.field!==e.fieldId&&s.fields.some(a=>a.id===t.field&&a.type!=="page_break"))}function Sn(n,e,s,t){const a=Ft(n),i=a.find(l=>l.key===e),o=a.find(l=>l.key===s);return!i||!o||!It(i,o,n)?!1:(t?.(),Object.assign(o.logic,i.logic,{rules:i.logic.rules.map(l=>({...l}))}),!0)}function Dt(n){const e=document.activeElement,s=r("div",{class:"atfb-overlay"}),t=r("div",{class:"atfb-modal atfb-condition-copy",attrs:{role:"dialog","aria-modal":"true","aria-label":"Copy condition",tabindex:"-1"}});let a="";const i=n.to,o=()=>{s.remove(),e?.focus()},l=()=>{const c=n.schema();if(!c){o();return}const d=Ft(c),h=d.find(p=>p.key===i),m=d.filter(p=>p.logic.rules.length&&h&&It(p,h,c)),g=m.find(p=>p.key===a),u=D(a,[{value:"",label:"Choose a section…"},...m.map(p=>({value:p.key,label:p.label}))],p=>{a=p,l(),t.querySelector('[aria-label="Copy from"]')?.focus()});u.setAttribute("aria-label","Copy from");const f=C("Copy condition",()=>{const p=n.schema();p&&Sn(p,a,i,n.beforeCopy)&&(o(),n.onCopy())},"primary");(!g||!i)&&f.setAttribute("disabled","");const y=d.find(p=>p.key===i);t.replaceChildren(r("h2",{text:"Copy condition"}),w("Copy from",u),r("p",{class:"atfb-hint",text:`Apply to ${y?.label??"this section"}.`}),r("p",{class:"atfb-condition-copy__preview",attrs:{"aria-live":"polite"},text:g?`${g.logic.enabled?"Enabled":"Disabled"} · ${g.logic.action==="hide"?"Hide":"Show"} · Match ${g.logic.match}: `+g.logic.rules.map(p=>qe(me(p,c.fields))).join(g.logic.match==="all"?" and ":" or "):m.length?"Choose the section whose condition you want to reuse.":"No conditions are available to copy here yet."}),r("p",{class:"atfb-hint",text:y?.logic.rules.length?"This replaces the destination’s entire condition. You can edit the copy independently.":"Copies every rule, all/any matching, show/hide and enabled state. You can edit the copy independently."}),r("div",{class:"atfb-modal__actions",children:[C("Cancel",o),f]}))};s.append(t),s.addEventListener("click",c=>{c.target===s&&o()}),s.addEventListener("keydown",c=>{if(c.key==="Escape"&&(c.preventDefault(),o()),c.key==="Tab"){const d=[...t.querySelectorAll("button:not([disabled]), select, os-select, os-button:not([disabled])")],h=d[0],m=d[d.length-1];c.shiftKey&&(document.activeElement===h||document.activeElement===t)?(c.preventDefault(),m?.focus()):!c.shiftKey&&document.activeElement===m&&(c.preventDefault(),h?.focus())}c.stopPropagation()}),n.root.append(s),l(),t.focus()}const Cn="allterrain-forms/form";function En(){return window.wp?.os?.relations??null}function qt(n){const e=n.closest("[data-window-id], .os-window");if(!e)return null;const s=e.getAttribute("data-window-id");if(s)return s;const t=e.id??"";return t?t.replace(/^wp-window-/,""):null}const Ln=6e3,Ot=120;function Nt(n,e){const s=En();if(Ge.set(n,e),!s?.set)return;const t=i=>{if(xe.get(n)!==a)return;const o=qt(n);if(!o){Date.now()t(i),Ot);return}try{s.set(o,e)}catch(c){Rt||(Rt=!0,console.error("[AllTerrain Forms] The shell refused a window identity.",c,e)),xe.delete(n);return}if(!e||s.get?.(o)?.id===e.id||Date.now()>=i){xe.delete(n);return}window.setTimeout(()=>t(i),Ot)},a=Symbol("atf-identity");xe.set(n,a),t(Date.now()+Ln)}let Rt=!1;const xe=new WeakMap,Ge=new Map;function $n(){for(const[n,e]of Ge){if(!n.isConnected){Ge.delete(n);continue}Nt(n,e)}}if(typeof document<"u")for(const n of["os-window-content-loaded","os-window-opened"])document.addEventListener(n,()=>$n());function An(n,e){return{type:Cn,id:n.id,label:n.title||"Untitled form",related:[{id:`allterrain-forms/entries-${n.id}`,label:"Entries for this form",url:`${e}admin.php?page=allterrain-forms-entries&form=${n.id}`,group:"allterrain-forms",groupLabel:"Forms",icon:"dashicons-list-view"}]}}const ke={letters:{label:"Letters",chars:"A-Za-zÀ-ÖØ-öø-ÿ"},numbers:{label:"Numbers",chars:"0-9"},spaces:{label:"Spaces",chars:" "},punctuation:{label:`Punctuation ( . , ! ? ' " - )`,chars:`.,!?'"()\\-:;`},symbols:{label:"Symbols ( @ # & _ / + )",chars:"@#&_/+*%="}};function Tn(){return{mode:"blocks",starts:"",ends:"",contains:"",notContains:"",chars:[],minLen:"",maxLen:"",regex:"",message:"",tests:[]}}function Pt(n){const e=Tn();let s;try{s=JSON.parse(n)}catch{return e}if(!s||typeof s!="object")return e;const t=s;e.mode=t.mode==="regex"?"regex":"blocks";for(const a of["starts","ends","contains","notContains","minLen","maxLen","regex","message"])typeof t[a]=="string"&&(e[a]=t[a]);return Array.isArray(t.chars)&&(e.chars=t.chars.filter(a=>typeof a=="string"&&a in ke)),Array.isArray(t.tests)&&(e.tests=t.tests.filter(a=>typeof a=="string").slice(0,10)),e}function Se(n){return n.replace(/[.*+?^${}()|[\]\\/]/g,"\\$&")}function Ce(n){if(n.mode==="regex")return n.regex.trim();const e=[],s=n.minLen.trim(),t=n.maxLen.trim();(s!==""||t!=="")&&e.push(`(?=.{${s===""?"0":parseInt(s,10)||0},${t===""?"":parseInt(t,10)||""}}$)`),n.contains!==""&&e.push(`(?=.*${Se(n.contains)})`),n.notContains!==""&&e.push(`(?!.*${Se(n.notContains)})`),n.starts!==""&&e.push(`(?=${Se(n.starts)})`),n.ends!==""&&e.push(`(?=.*${Se(n.ends)}$)`);const a=n.chars.map(o=>ke[o]?.chars??"").join(""),i=a?`[${a}]*$`:".*$";return!e.length&&!a?"":`^${e.join("")}${i}`}function Bt(n){if(n.mode==="regex")return n.regex.trim()?`Matches the expression ${n.regex.trim()}`:"";const e=[];if(n.starts&&e.push(`starts with “${n.starts}”`),n.ends&&e.push(`ends with “${n.ends}”`),n.contains&&e.push(`contains “${n.contains}”`),n.notContains&&e.push(`never contains “${n.notContains}”`),n.chars.length){const i=n.chars.map(o=>(ke[o]?.label??o).split(" (")[0].toLowerCase());e.push(`uses only ${i.join(" and ")}`)}const s=n.minLen.trim(),t=n.maxLen.trim();return s&&t?e.push(`is ${s}–${t} characters long`):s?e.push(`is at least ${s} characters long`):t&&e.push(`is at most ${t} characters long`),e.length?`The answer ${e.length>1?`${e.slice(0,-1).join(", ")}, and ${e[e.length-1]}`:e[0]}.`:""}function Mn(n,e){const s=Ce(n);if(s==="")return null;try{return new RegExp(s).test(e)}catch{return null}}function Fn(){return window.wp?.os?.windowManager??null}const jt="allterrain-forms-validation";function In(n){const e=Fn(),s=qt(n.root);if(e?.openChild&&s){Dn(e,s,n);return}qn(n)}function Dn(n,e,s){const t=`${jt}-${s.field.id}`;n.getById?.(t)&&n.remove?.(t);const a={saved:!1},i=()=>{const o=n.getById?.(t);o?.close?o.close():n.remove?.(t)};n.openChild?.(e,{id:t,baseId:jt,url:`#${t}`,title:`Custom rule — ${s.field.label||"this question"}`,icon:"dashicons-yes-alt",native:!0,width:560,height:700,minWidth:440,minHeight:480,ephemeral:!0,autofocus:".atfb-valwin__pane:not([hidden]) input, .atfb-valwin__pane:not([hidden]) textarea",render:o=>{const l=r("div",{class:"atfa atfb-valwin atfb-valwin--window"}),c=r("div",{class:"atfb-valwin__scroll"});l.append(c),Ht(c,s,a,i,"window");const d=c.querySelector(".atfb-modal__actions");d&&l.append(d),o.append(l)},onClose:()=>{a.saved||s.onCancel?.()}})}function qn(n){const e=r("div",{class:"atfb-overlay"}),s={saved:!1},t=()=>{e.remove(),document.removeEventListener("keydown",a),s.saved||n.onCancel?.()},a=o=>{o.key==="Escape"&&t()},i=r("div",{class:"atfb-modal atfb-valwin",attrs:{role:"dialog","aria-label":"Custom validation rule"},children:[r("h2",{text:"Custom rule"})]});Ht(i,n,s,t,"overlay"),e.append(i),e.addEventListener("click",o=>{o.target===e&&t()}),document.addEventListener("keydown",a),n.root.append(e),e.querySelector(".atfb-valwin__pane:not([hidden]) input, .atfb-valwin__pane:not([hidden]) textarea")?.focus()}function Ht(n,e,s,t,a){const i=Pt(String(e.field.validationRecipe??""));i.mode==="blocks"&&Ce(i)===""&&e.field.pattern&&(i.mode="regex",i.regex=String(e.field.pattern)),i.message||(i.message=String(e.field.messages?.invalid??""));const o=(b,M)=>{const $=r("input",{class:"atfb-input",value:i[b],placeholder:M,attrs:{type:"text"}});return $.addEventListener("input",()=>{i[b]=$.value,L()}),$},l=(b,M)=>{const $=r("input",{class:"atfb-input atfb-valwin__len",value:i[b],attrs:{type:"number",min:"0","aria-label":M}});return $.addEventListener("input",()=>{i[b]=$.value,L()}),$},c=r("div",{class:"atfb-valwin__chars",children:Object.entries(ke).map(([b,M])=>q(M.label,i.chars.includes(b),$=>{i.chars=$?[...i.chars,b]:i.chars.filter(W=>W!==b),L()}))}),d=r("div",{class:"atfb-valwin__pane",children:[w("Starts with",o("starts","e.g. AT-")),w("Ends with",o("ends","e.g. -2026")),w("Must contain",o("contains","e.g. @")),w("Must not contain",o("notContains","e.g. spaces? type one")),w("Only these characters",c,"Leave every box unticked to allow anything."),w("Length",r("div",{class:"atfb-valwin__lengths",children:[r("span",{text:"between"}),l("minLen","Minimum length"),r("span",{text:"and"}),l("maxLen","Maximum length"),r("span",{text:"characters"})]}),"Leave a box empty for no limit.")]}),h=r("textarea",{class:"atfb-input atfb-valwin__regex",attrs:{rows:"2","aria-label":"Regular expression",placeholder:"^AT-[0-9]{4}$"}});h.value=i.regex,h.addEventListener("input",()=>{i.regex=h.value,L()});const m=r("div",{class:"atfb-valwin__pane",children:[w("Expression",h,"A regular expression, without slashes. Checked against the whole answer only if you anchor it with ^ and $.")]}),g=[["blocks","Easy blocks"],["regex","Expression (advanced)"]],u=b=>{i.mode=b,d.hidden=b!=="blocks",m.hidden=b!=="regex",L()},y=(()=>{if(Y("os-segmented")&&Y("os-segment")){const $=document.createElement("os-segmented");$.setAttribute("value",i.mode),$.setAttribute("label","How to write the rule"),$.classList.add("atfb-valwin__tabs");for(const[W,j]of g){const le=document.createElement("os-segment");le.setAttribute("value",W),le.textContent=j,$.append(le)}return $.addEventListener("os-pick",W=>{const j=W.detail?.value;(j==="blocks"||j==="regex")&&($.setAttribute("value",j),u(j))}),$}const b=r("div",{class:"atfb-valwin__tabs",attrs:{role:"tablist"}}),M=()=>{b.replaceChildren(...g.map(([$,W])=>{const j=i.mode===$;return r("button",{class:`atfb-valwin__tab${j?" is-active":""}`,type:"button",text:W,attrs:{role:"tab","aria-selected":j?"true":"false"},on:{click:()=>{u($),M()}}})}))};return M(),b})();d.hidden=i.mode!=="blocks",m.hidden=i.mode!=="regex";const p=r("input",{class:"atfb-input",value:i.message,placeholder:"That is not in the expected format.",attrs:{type:"text"}});p.addEventListener("input",()=>{i.message=p.value});const x=r("p",{class:"atfb-valwin__summary",attrs:{"aria-live":"polite"}}),F=r("div",{class:"atfb-valwin__samples"}),S=b=>{const M=r("span",{class:"atfb-valwin__verdict",attrs:{"aria-live":"polite"}}),$=r("input",{class:"atfb-input",value:b,placeholder:"Type a sample answer…",attrs:{type:"text"}});$.addEventListener("input",()=>L()),F.append(r("div",{class:"atfb-valwin__sample",children:[$,M]}))};for(const b of i.tests.length?i.tests:["","",""])S(b);const H=()=>Array.from(F.querySelectorAll("input")).map(b=>b.value),L=()=>{const b=Bt(i);x.textContent=b||"Nothing yet — fill in a block above and the rule appears here in plain words.";for(const M of Array.from(F.querySelectorAll(".atfb-valwin__sample"))){const $=M.querySelector("input"),W=M.querySelector(".atfb-valwin__verdict");if(!$||!W)continue;const j=$.value===""?null:Mn(i,$.value);W.textContent=j===null?"·":j?"✓ passes":"✗ fails",W.classList.toggle("is-pass",j===!0),W.classList.toggle("is-fail",j===!1)}};n.append(r("p",{class:"atfb-hint",text:`Describe what a good answer to “${e.field.label||"this question"}” looks like — no code needed.`}),y,d,m,r("div",{class:"atfb-valwin__try",children:[r("h3",{text:"Try it out"}),x,F,C("Add another sample",()=>{S(""),L()},"ghost","plus-alt2")]}),w("When it fails, say",p,"Shown to the visitor when their answer breaks the rule. Leave empty for the default wording."),r("div",{class:"atfb-modal__actions",children:[C(a==="window"?"Close without saving":"Cancel",t),C("Save rule",()=>{const b=Ce(i);i.tests=H().filter(M=>M!=="").slice(0,10),s.saved=!0,e.onSave({pattern:b,recipe:i,message:i.message.trim()}),t()},"primary")]})),L()}const On=["Contact","Numbers & codes","Text shape","Web"],Wt=[{slug:"email",label:"An email address",group:"Contact",example:"jane@example.com",pattern:"^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$",message:"That does not look like an email address."},{slug:"phone",label:"A phone number",group:"Contact",example:"+34 612 345 678",pattern:"^(?=(?:[^0-9]*[0-9]){5,})\\+?[0-9 ().-]{5,24}$",message:"That does not look like a phone number."},{slug:"handle",label:"A username or @handle",group:"Contact",example:"@yourname",pattern:"^@?[A-Za-z0-9_]{2,30}$",message:"That does not look like a username."},{slug:"digits",label:"Numbers only",group:"Numbers & codes",example:"12345",pattern:"^[0-9]+$",message:"Numbers only, please."},{slug:"decimal",label:"A number, decimals allowed",group:"Numbers & codes",example:"3.14",pattern:"^-?[0-9]+([.,][0-9]+)?$",message:"That does not look like a number."},{slug:"price",label:"A price",group:"Numbers & codes",example:"19.99",pattern:"^[0-9]+([.,][0-9]{1,2})?$",message:"That does not look like a price."},{slug:"zip_us",label:"A ZIP code (US)",group:"Numbers & codes",example:"90210",pattern:"^[0-9]{5}(-[0-9]{4})?$",message:"That does not look like a ZIP code."},{slug:"postcode_uk",label:"A postcode (UK)",group:"Numbers & codes",example:"SW1A 1AA",pattern:"^[A-Za-z]{1,2}[0-9][A-Za-z0-9]? ?[0-9][A-Za-z]{2}$",message:"That does not look like a postcode."},{slug:"iban",label:"An IBAN",group:"Numbers & codes",example:"DE89 3704 0044 0532 0130 00",pattern:"^[A-Za-z]{2}[0-9]{2}(?: ?[A-Za-z0-9]){10,32}$",message:"That does not look like an IBAN."},{slug:"credit_card",label:"A card number",group:"Numbers & codes",example:"4242 4242 4242 4242",pattern:"^[0-9](?:[0-9 -]{9,21})?[0-9]$",message:"That does not look like a card number.",luhn:!0},{slug:"letters",label:"Letters only",group:"Text shape",example:"María López",pattern:"^[\\p{L}\\p{M} .'’-]+$",message:"Letters only, please."},{slug:"alphanumeric",label:"Letters and numbers only",group:"Text shape",example:"abc123",pattern:"^[A-Za-z0-9]+$",message:"Letters and numbers only, please."},{slug:"no_spaces",label:"One word, no spaces",group:"Text shape",example:"one-word",pattern:"^\\S+$",message:"No spaces allowed."},{slug:"url",label:"A web address",group:"Web",example:"https://example.com",pattern:"^(https?://)?([A-Za-z0-9-]+\\.)+[A-Za-z]{2,}([/?#]\\S*)?$",message:"That does not look like a web address."},{slug:"ip",label:"An IP address",group:"Web",example:"192.168.0.1",pattern:"^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])$",message:"That does not look like an IP address."},{slug:"slug",label:"A URL slug",group:"Web",example:"my-page-title",pattern:"^[a-z0-9]+(-[a-z0-9]+)*$",message:"Lowercase letters, numbers and dashes only."},{slug:"hex_color",label:"A hex colour",group:"Web",example:"#3366ff",pattern:"^#?([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$",message:"That does not look like a colour code."}];function Nn(n){return Wt.find(e=>e.slug===n)??null}function Xe(){return window.wp?.os??null}const Ut="allterrain-forms/preview",Ze="allterrain-forms-preview";function Rn(n){const e=Xe();if(!e?.registerTitleBarButton)return()=>{};const s=()=>{try{e.registerTitleBarButton({id:Ut,label:"Preview this form",icon:"dashicons-visibility",placement:"right",order:90,match:t=>{const a=t?.id??t?.config?.id??"";return a==="allterrain-forms"||a.startsWith("allterrain-forms#")},onClick:()=>void zt(n),owner:"allterrain-forms-builder"})}catch{}};return e.ready?e.ready(s):s(),()=>{try{e.unregisterTitleBarButton?.(Ut)}catch{}}}async function zt(n){n.isDirty()&&await n.save();const e=n.current();e&&Yt(e.id,e.title,e.previewUrl)}function Yt(n,e,s){const t=Xe();if(!t?.windowManager?.open){window.open(s,"_blank","noopener");return}t.windowManager.open({id:`${Ze}-${n}`,baseId:Ze,url:s,title:`Preview: ${e}`,icon:"dashicons-visibility"})}function Pn(n,e,s){if(!Xe()?.windowManager?.open||!document.querySelector(`[data-window-id="${Ze}-${n}"]`))return;const i=s.includes("?")?"&":"?";Yt(n,e,`${s}${i}alltfo_r=${Date.now()}`)}const te="allterrain-forms/field",Bn=["openstation/file","desktop-mode/file","openstation/attachment"],Vt="allterrain-forms/logic-map-v2";function J(n,e){return n.dataset.atfbBind=e,n}const Kt={level:{key:"level",label:"Heading level",control:"select",hint:"Headings should step down one at a time.",options:[{value:"2",label:"Heading 2"},{value:"3",label:"Heading 3"},{value:"4",label:"Heading 4"},{value:"5",label:"Heading 5"},{value:"6",label:"Heading 6"}]},content:{key:"content",label:"HTML",control:"textarea",hint:"Shown as written. Scripts are stripped when the form is saved."},consenttext:{key:"consentText",label:"What they are agreeing to",control:"textarea",hint:"Shown beside the tick box. Links are allowed."},height:{key:"height",label:"Height",control:"number",hint:"In pixels."},columns:{key:"columns",label:"Columns",control:"number",hint:"How many pictures sit side by side."},multiple:{key:"multiple",label:"Let them choose more than one",control:"checkbox"},inline:{key:"inline",label:"Lay the options out in a row",control:"checkbox"},other:{key:"other",label:"Offer an “Other” box",control:"checkbox",hint:"Adds a final option with a box to type in."},filetypes:{key:"filetypes",label:"Accepted file types",control:"commas",hint:"Extensions, separated by commas. Empty accepts anything the site allows."},maxsize:{key:"maxsize",label:"Largest file",control:"number",hint:"In megabytes."},maxfiles:{key:"maxfiles",label:"How many files",control:"number"},minrows:{key:"minRows",label:"Fewest rows",control:"number",also:{key:"maxRows",label:"Most rows"}},itemlabel:{key:"itemLabel",label:"What is one row called?",control:"text",hint:"Names each card — “Attendee 1”, “Attendee 2” — and the Remove button. Also editable on the card itself."},endlabels:{key:"minLabel",label:"Label at the low end",control:"text",also:{key:"maxLabel",label:"Label at the high end"}},points:{key:"points",label:"Points if correct",control:"number"},minchoices:{key:"minChoices",label:"Fewest they may pick",control:"number",also:{key:"maxChoices",label:"Most they may pick"}}},jn={label:"the canvas edits it in place, and the inspector mirrors it",placeholder:"edited by typing into the control on the canvas",hint:"edited under the control on the canvas",nextlabel:"edited on the page break’s own Next button",prevlabel:"edited on the page break’s own Back button",addlabel:"edited on the repeater’s own Add button",choices:"the choices editor, and the option list on the canvas",required:"the toggle on the card",width:"its own row",css:"its own row",prefill:"its own section",logic:"the conditional logic section",formula:"its own row, with the currency that goes with it",currency:"rendered with the formula",correct:"the choices editor marks the right answer",default:"its own row, typed to match what the field stores",rows:"a row count on a textarea; the statement list on a Likert matrix",parts:"a tick box per part, listed by the server",min:"the validation section, paired with max",max:"the validation section, paired with min",step:"the validation section",minlength:"the validation section, paired with maxlength",maxlength:"the validation section, paired with minlength",mindate:"the validation section, paired with maxdate",maxdate:"the validation section, paired with mindate",mintime:"the validation section, paired with maxtime",maxtime:"the validation section, paired with mintime",pattern:"the validation section",unique:"the validation section",maxchoices:"rendered with minchoices",maxrows:"rendered with minrows"};function Gt(n,e,s){const t=n[e.key],a=i=>s(e.key,i);if(e.control==="checkbox")return q(e.label,!!t,a);if(e.control==="commas"){const i=Array.isArray(t)?t:[];return w(e.label,T(i.join(", "),o=>{a(o.split(",").map(l=>l.trim().replace(/^\./,"").toLowerCase()).filter(Boolean))}),e.hint)}return e.control==="select"?w(e.label,D(String(t??""),e.options??[],a),e.hint):e.control==="textarea"?w(e.label,pe(String(t??""),a),e.hint):e.control==="number"?w(e.label,V(String(t??""),a),e.hint):w(e.label,J(T(String(t??""),a),e.key),e.hint)}function Xt(n,e){const s=new Set(n.map(a=>a.key).filter(Boolean));let t=n.length+1;return e.split(` +`).map(a=>a.trim()).filter(Boolean).map((a,i)=>{const o=n[i]?.key;if(o)return{key:o,label:a};for(;s.has(`r${t}`);)t+=1;return s.add(`r${t}`),{key:`r${t}`,label:a}})}function Zt(n,e,s){const t=n.findIndex(i=>i.id===e);if(t<0)return null;const a=Math.max(0,Math.min(s,n.length-1));return a===t?null:{from:t,to:a}}const Je=[{value:"user:email",label:"Their email address",group:"About the person filling it in",tag:"{user:email}"},{value:"user:display_name",label:"Their name",group:"About the person filling it in",tag:"{user:display_name}"},{value:"user:first_name",label:"Their first name",group:"About the person filling it in"},{value:"user:last_name",label:"Their last name",group:"About the person filling it in"},{value:"user:login",label:"Their username",group:"About the person filling it in"},{value:"date:today",label:"Today’s date",group:"The date and time",tag:"{date}"},{value:"date:now",label:"The time right now",group:"The date and time",tag:"{time}"},{value:"site",label:"This site’s name",group:"About this site",tag:"{site}"},{value:"site:url",label:"This site’s address",group:"About this site",tag:"{site:url}"},{value:"site:admin_email",label:"The site administrator’s email",group:"About this site",tag:"{admin_email}"}],Hn=["About the person filling it in","The date and time","About this site"],Qe=(n,e)=>U?.i18n?.[n]??e;class Jt{constructor(e){this.config=null,this.themes=[],this.forms=[],this.form=null,this.schema=null,this.selected=null,this.tab="build",this.logicMap=null,this.canvasTheme=r("style"),this.canvasThemeSignature="",this.openSections=new Map,this.logicMapOn=fs(Vt)!=="off",this.dirty=!1,this.editGeneration=0,this.saveInFlight=!1,this.queuedSave=null,this.teardowns=[],this.canvasTarget=null,this.history=[],this.historyAt=-1,this.autosave=Me(()=>{this.save(!0)},2500),this.renderInspector=ps(()=>{if(R(this.inspector),!this.schema||(this.root.classList.toggle("atfb--build-only-panes",this.tab!=="build"),this.tab!=="build"))return;const s=this.selected?this.locateField(this.selected):void 0,t=s?.field,a=s?.parent??null;if(this.root.classList.toggle("atfb--has-selection",!!t),!t){this.inspector.append(r("div",{class:"atfb-placeholder",children:[r("p",{text:"Select a field to change it."}),r("p",{class:"atfb-hint",text:"Drag a field from the palette, or press one to add it to the end."})]}));return}const i=this.config?.fieldTypes.find(c=>c.type===t.type),o=i?.supports??[],l=(c,d)=>{const h=this.liveField(t.id);h&&(h[c]=d,this.markDirty(),this.renderCanvas())};this.inspector.append(r("h3",{class:"atfb-inspector__title",text:i?.label??t.type})),a?this.inspector.append(r("p",{class:"atfb-hint atfb-inspector__crumb",text:`Inside ${a.label||"a repeater"} — the visitor answers this once per ${String(a.itemLabel??"")||"row"}.`}),r("p",{class:"atfb-hint",text:`Formulas aggregate it as {${a.id}.${t.id}} — e.g. sum( {${a.id}.${t.id}} ).`})):this.inspector.append(r("p",{class:"atfb-hint",text:`Reference this field as {field:${t.id}}`})),o.includes("label")&&this.inspector.append(w("Label",J(T(t.label,c=>l("label",c)),"label"))),o.includes("placeholder")&&this.inspector.append(w("Placeholder",J(T(t.placeholder,c=>l("placeholder",c)),"placeholder"))),o.includes("hint")&&this.inspector.append(w("Hint",J(T(t.hint,c=>l("hint",c)),"hint"),"Shown under the field, and read out with it.")),o.includes("nextlabel")&&this.inspector.append(w("Next button",J(T(String(t.nextLabel??""),c=>l("nextLabel",c)),"nextLabel"),"Leave empty for “Next”.")),o.includes("prevlabel")&&this.inspector.append(w("Back button",J(T(String(t.prevLabel??""),c=>l("prevLabel",c)),"prevLabel"),"Leave empty for “Back”.")),o.includes("addlabel")&&this.inspector.append(w("Add button",J(T(String(t.addLabel??""),c=>l("addLabel",c)),"addLabel"),"Leave empty for “Add another”.")),o.includes("required")&&this.inspector.append(q("Required",t.required,c=>l("required",c))),o.includes("width")&&this.inspector.append(w("Width",D(t.width,[{value:"full",label:"Full width"},{value:"two-thirds",label:"Two thirds"},{value:"half",label:"Half"},{value:"third",label:"One third"},{value:"quarter",label:"One quarter"}],c=>l("width",c)))),i?.choices&&this.inspector.append(this.renderChoicesEditor(t,l)),this.renderTypeSettings(t,i,o,l),(t.type==="total"||o.includes("formula"))&&this.inspector.append(w("Formula",r("div",{class:"atfb-formula__row",children:[Mt(T(String(t.formula??""),c=>l("formula",c)),this.schema?.fields??[],t.id),C("Formula editor",()=>kn({root:this.root,fields:this.schema?.fields??[],field:this.liveField(t.id)??t,onSave:c=>{l("formula",c),this.renderInspector()}}))]}),"Reference answers with braces — {f1} * {f2} + 10 — or open the editor and click them in."),w("Currency symbol",T(String(t.currency??""),c=>l("currency",c)))),this.inspector.append(this.renderValidationSection(t,o,l)),a||(this.inspector.append(this.renderLogicSection(t)),o.includes("prefill")&&this.inspector.append(this.prefillControl(t,l))),o.includes("css")&&this.inspector.append(w("CSS class",T(t.cssClass,c=>l("cssClass",c))))}),this.root=e,this.bar=e.querySelector("[data-atfb-bar]")??r("div"),this.palette=e.querySelector("[data-atfb-palette]")??r("div"),this.canvas=e.querySelector("[data-atfb-canvas]")??r("div"),this.inspector=e.querySelector("[data-atfb-inspector]")??r("div"),this.canvas.addEventListener("click",s=>{const t=s.target;if(!(this.tab!=="build"||!this.selected||t.closest("[data-atfb-card], button, os-button, a, input, textarea, select, os-select, label, [contenteditable]"))){this.selected=null;for(const a of this.canvas.querySelectorAll(".atfb-card.is-selected"))a.classList.remove("is-selected");this.renderInspector()}})}async start(){try{const[t,a,i]=await Promise.all([I.config(),I.listThemes(),I.listForms()]);this.config=t,this.themes=a,this.forms=i}catch(t){this.fail(t);return}this.teardowns.push(os([te])),this.teardowns.push(Rn({current:()=>this.form?{id:this.form.id,title:this.form.title,previewUrl:this.form.previewUrl}:null,isDirty:()=>this.dirty,save:()=>this.save(!0)}));const e=t=>{this.dirty&&(t.preventDefault(),t.returnValue="")};window.addEventListener("beforeunload",e),this.teardowns.push(()=>window.removeEventListener("beforeunload",e));const s=t=>{!(t.metaKey||t.ctrlKey)||t.key.toLowerCase()!=="z"||t.target?.closest("input, textarea, [contenteditable]")||(t.preventDefault(),this.travel(t.shiftKey?1:-1))};if(this.root.addEventListener("keydown",s),this.teardowns.push(()=>this.root.removeEventListener("keydown",s)),this.renderBar(),this.renderPalette(),this.forms.length){const t=Ss("builder");await this.open(t&&this.forms.some(a=>a.id===t)?t:this.forms[0].id)}else this.renderFormsList()}destroy(){this.canvasTarget?.(),this.canvasTarget=null,this.teardowns.forEach(e=>e()),this.teardowns=[]}fail(e){R(this.bar),this.bar.append(r("p",{class:"atfb-error",text:e instanceof Error?e.message:"Something went wrong loading your forms."}))}renderBar(){R(this.bar);const e=D(String(this.form?.id??""),[...this.forms.map(a=>({value:String(a.id),label:a.title||"(untitled)"}))],a=>void this.open(Number(a)));e.setAttribute("aria-label","Choose a form");const s=r("input",{class:"atfb-title",type:"text",value:this.form?.title??"",placeholder:"Untitled form",attrs:{"aria-label":"Form title"},on:{input:a=>{this.form&&(this.form.title=a.target.value,this.markDirty())}}}),t=r("div",{class:"atfb-tabs",attrs:{role:"tablist"},children:[["build","Build"],["theme","Theme"],["settings","Settings"],["notify","Notifications"],["confirm","Confirmations"]].map(([a,i])=>r("button",{class:`atfb-tab${this.tab===a?" is-active":""}`,type:"button",text:i,attrs:{role:"tab","aria-selected":this.tab===a},on:{click:()=>{this.tab=a,this.renderBar(),this.renderInspector(),this.renderCanvas()}}}))});this.bar.append(r("div",{class:"atfb-bar__left",children:[this.forms.length>1?e:null,s]}),t,r("div",{class:"atfb-bar__right",children:[this.historyButton("undo","Undo",-1),this.historyButton("redo","Redo",1),C("New",()=>void this.showTemplates(),"secondary","plus-alt2"),C("Export",()=>void this.exportForm(),"secondary","download"),C("Import",()=>void this.importForm(),"secondary","upload"),C("Preview",()=>void this.preview(),"secondary","visibility"),C("Entries",()=>this.openEntries(),"secondary","list-view"),this.logicMapButton(),this.saveButton()]}))}liveField(e){return this.locateField(e)?.field}locateField(e){const s=this.schema?.fields??[];for(let t=0;tqueueMicrotask(()=>this.rebindCanvas()),{once:!0});return}if(this.canvas.matches(":hover")){this.canvas.addEventListener("pointerleave",()=>this.rebindCanvas(),{once:!0});return}this.renderCanvas()}snapshot(){if(!this.schema)return;const e=JSON.stringify(this.schema);this.history[this.historyAt]!==e&&(this.history=this.history.slice(0,this.historyAt+1),this.history.push(e),this.history.length>60&&this.history.shift(),this.historyAt=this.history.length-1,this.syncHistoryButtons())}travel(e){const s=this.historyAt+e;!this.schema||s<0||s>=this.history.length||(this.historyAt=s,this.schema=JSON.parse(this.history[s]),this.schema.fields.some(t=>t.id===this.selected)||(this.selected=null),this.markDirty(),this.renderBar(),this.renderCanvas(),this.renderInspector())}historyButton(e,s,t){const a=C(s,()=>this.travel(t),"secondary",e);return a.setAttribute("data-atfb-history",String(t)),a.disabled=!this.canTravel(t),a}canTravel(e){const s=this.historyAt+e;return s>=0&&sURL.revokeObjectURL(t),1e3)}async importForm(){const e=r("input",{type:"file",attrs:{accept:"application/json,.json"}});e.addEventListener("change",async()=>{const s=e.files?.[0];if(s)try{const t=JSON.parse(await s.text());if(!t.schema)throw new Error("That file does not contain a form.");const a=await I.createForm({title:t.title??s.name.replace(/\.json$/i,""),schema:t.schema});this.forms.unshift({id:a.id,title:a.title,status:a.status,modified:a.modified,fields:a.schema.fields.length,theme:a.schema.settings.theme,entries:0,unread:0,views:0,submissions:0,shortcode:a.shortcode}),this.form=a,this.schema=a.schema,this.selected=null,this.dirty=!1,this.history=[],this.historyAt=-1,this.snapshot(),this.renderBar(),this.renderCanvas(),this.renderInspector(),N("Form imported",a.title)}catch(t){N("Could not import that file",t instanceof Error?t.message:"","error")}}),e.click()}saveButton(){const e=C("Save",()=>void this.save(),"primary");return e.disabled=!this.dirty,e.setAttribute("data-atfb-save",""),e}markDirty(){this.dirty=!0,this.editGeneration+=1;const e=this.bar.querySelector("[data-atfb-save]");e&&(e.disabled=!1),this.autosave()}async save(e=!1){if(!this.form||!this.schema)return;if(this.saveInFlight){this.queuedSave={silent:e&&(this.queuedSave?.silent??!0)};return}this.saveInFlight=!0;const s=this.editGeneration;try{const t=await I.updateForm(this.form.id,{title:this.form.title,schema:this.schema});if(s===this.editGeneration){this.form=t,this.schema=t.schema,this.dirty=!1,this.rebindCanvas();const i=this.bar.querySelector("[data-atfb-save]");i&&(i.disabled=!0)}Bs(t.id);const a=this.forms.find(i=>i.id===t.id);a&&(a.title=t.title),Pn(t.id,t.title,t.previewUrl),e||N("Form saved",t.title)}catch(t){N(Qe("saveFailed","Could not save"),t instanceof Error?t.message:"","error")}finally{this.saveInFlight=!1;const t=this.queuedSave;this.queuedSave=null,t&&this.save(t.silent)}}async openFormById(e){await this.open(e)}async open(e){if(!(this.dirty&&!await ae("You have unsaved changes. Discard them?"))){try{this.form=await I.getForm(e),this.schema=this.form.schema,this.selected=null,this.dirty=!1,this.history=[],this.historyAt=-1,this.snapshot()}catch(s){this.fail(s);return}this.renderBar(),this.renderCanvas(),this.renderInspector(),this.announceIdentity()}}announceIdentity(){this.form&&Nt(this.root,An(this.form,U?.adminUrl??""))}async showTemplates(){if(!this.config)return;const e=r("div",{class:"atfb-overlay"}),s=i=>{i.key==="Escape"&&t()},t=()=>{e.remove(),document.removeEventListener("keydown",s)},a=r("div",{class:"atfb-templates",children:this.config.templates.map(i=>r("button",{class:"atfb-template",type:"button",on:{click:async()=>{t();try{const o=await I.createForm({template:i.slug});this.forms.unshift({id:o.id,title:o.title,status:o.status,modified:o.modified,fields:o.schema.fields.length,theme:o.schema.settings.theme,entries:0,unread:0,views:0,submissions:0,shortcode:o.shortcode}),this.form=o,this.schema=o.schema,this.selected=null,this.dirty=!1,this.history=[],this.historyAt=-1,this.snapshot(),this.renderBar(),this.renderCanvas(),this.renderInspector(),this.announceIdentity()}catch(o){N("Could not create the form",o instanceof Error?o.message:"","error")}}},children:[B(i.icon),r("strong",{text:i.label}),r("span",{text:i.description})]}))});e.append(r("div",{class:"atfb-modal",attrs:{role:"dialog","aria-label":"Start a new form"},children:[r("h2",{text:"Start a new form"}),a,this.archivedFormsSection(t),r("div",{class:"atfb-modal__actions",children:[C("Cancel",t)]})]})),e.addEventListener("click",i=>{i.target===e&&t()}),document.addEventListener("keydown",s),this.root.append(e),a.querySelector("button")?.focus()}archivedFormsSection(e){const s=r("div",{class:"atfb-archived"});return I.listArchivedForms().then(t=>{t.length&&s.append(r("h3",{class:"atfb-archived__title",text:"Or bring one back from the archive"}),...t.map(a=>r("div",{class:"atfb-archived__row",children:[r("div",{class:"atfb-archived__meta",children:[r("strong",{text:a.title||"(untitled)"}),r("span",{class:"atfb-hint",text:`${a.entries} ${a.entries===1?"entry":"entries"} · ${a.submissions} submissions · ${a.views} views`})]}),C("Restore",async()=>{try{const i=await I.unarchiveForm(a.id);e(),this.forms.unshift(i),N("Form restored",`${i.title||"(untitled)"} is back, with its entries and stats.`),await this.open(i.id)}catch(i){N("Could not restore the form",i instanceof Error?i.message:"","error")}},"secondary","undo")]})))}).catch(()=>{}),s}renderFormsList(){R(this.canvas),this.canvas.append(r("div",{class:"atfb-empty",children:[r("h2",{text:"No forms yet"}),r("p",{text:"Start from a template, or build one from nothing."}),C("New form",()=>void this.showTemplates(),"primary","plus-alt2")]}))}openEntries(){const e=window.wp?.os;if(e?.openWindow){e.openWindow("allterrain-forms-entries");return}window.location.assign(`${U?.adminUrl??""}admin.php?page=allterrain-forms-entries`)}renderPalette(){if(!this.config)return;R(this.palette);const e=new Map;for(const t of this.config.fieldTypes){const a=e.get(t.group)??[];a.push(t),e.set(t.group,a)}const s=r("input",{class:"atfb-input atfb-palette__search",type:"search",placeholder:"Search fields",attrs:{"aria-label":"Search field types"},on:{input:t=>{const a=t.target.value.toLowerCase().trim();this.palette.querySelectorAll(".atfb-chip").forEach(i=>{const o=(i.textContent??"").toLowerCase();i.hidden=a!==""&&!o.includes(a)}),this.palette.querySelectorAll(".atfb-group").forEach(i=>{const o=Array.from(i.querySelectorAll(".atfb-chip")).some(l=>!l.hidden);i.hidden=!o})}}});this.palette.append(s);for(const[t,a]of Object.entries(this.config.groups)){const i=e.get(t);i?.length&&this.palette.append(r("div",{class:"atfb-group",children:[r("h3",{class:"atfb-group__title",text:a}),r("div",{class:"atfb-group__items",children:i.map(o=>this.paletteChip(o))})]}))}}paletteChip(e){const s=r("button",{class:"atfb-chip",type:"button",title:e.description,attrs:{"data-atf-type":e.type},children:[B(e.icon),r("span",{text:e.label})]});return s.addEventListener("pointerdown",t=>{const a=r("div",{class:"atfb-chip atfb-chip--ghost",children:[B(e.icon),r("span",{text:e.label})]});G().start({payload:Ae(te,s,{fieldType:e.type,isNew:!0},t,a),origin:t,onClickOnly:()=>this.addField(e.type)})}),s.addEventListener("click",t=>{G().recentlyEndedDrag()&&t.preventDefault()}),s}renderCanvas(){if(R(this.canvas),!this.schema||!this.form){this.renderFormsList();return}if(this.tab!=="build"){this.canvas.append(this.renderTabCanvas());return}const e=r("div",{class:"atfb-canvas__list",attrs:{"data-atfb-list":""}});this.schema.fields.length||e.append(r("div",{class:"atfb-placeholder",text:Qe("emptyCanvas","Drag a field from the left to begin.")})),this.schema.fields.forEach((t,a)=>{e.append(this.renderFieldCard(t,a))});const s=r("div",{class:"atfb-canvas__inner",children:[r("p",{class:"atfb-shortcode",text:this.form.shortcode,title:"Paste this anywhere to place the form"}),e]});this.canvas.append(s),this.registerCanvasTarget(e),this.paintLogicMap(s),this.paintCanvasTheme()}prefillControl(e,s){const t=e.prefill.startsWith("query:"),a=Je.some(g=>g.value===e.prefill),i=t?"query":a&&e.prefill||(e.prefill?"custom":""),o=r("div",{class:"atfb-prefill__detail"}),l=r("p",{class:"atfb-row__hint atfb-prefill__preview"}),c=g=>{if(o.replaceChildren(),l.replaceChildren(),g==="query"){const u=e.prefill.startsWith("query:")?e.prefill.slice(6):"";o.append(T(u,f=>{const y=f.trim();s("prefill",y?`query:${y}`:""),d(`query:${y}`)},"utm_source"),r("p",{class:"atfb-row__hint",text:"The name of the parameter in the link people arrive on."}))}g==="custom"&&o.append(T(e.prefill,u=>{s("prefill",u),d(u)},"myplugin:something"),r("p",{class:"atfb-row__hint",text:"For a source another plugin has added through alltfo_resolve_prefill."})),d(e.prefill)},d=g=>{if(l.replaceChildren(),!g)return;if(g.startsWith("query:")){const f=g.slice(6);f&&(l.textContent=`A visit to …/your-page/?${f}=abc opens the form with “abc” in it.`);return}const u=Je.find(f=>f.value===g)?.tag;u&&ft(this.form?.id??0).then(f=>{for(const y of f)for(const p of y.items)if(p.tag===u){const x=g.startsWith("user:");if(!p.sample){l.textContent="Empty unless the visitor is signed in.";return}l.textContent=x?`Opens with “${p.sample}” for you — empty for a visitor who is not signed in.`:`Opens with “${p.sample}”.`;return}})};c(i);const h=r("select",{class:"atfb-input atfb-select",on:{change:g=>{const u=g.target.value;s("prefill",u==="query"||u==="custom"?"":u),c(u)}}});h.append(r("option",{value:"",text:"Nothing — leave it empty",attrs:{selected:i===""}}));for(const g of Hn){const u=document.createElement("optgroup");u.label=g;for(const f of Je.filter(y=>y.group===g))u.append(r("option",{value:f.value,text:f.label,attrs:{selected:f.value===i}}));h.append(u)}const m=document.createElement("optgroup");return m.label="From the link they arrived on",m.append(r("option",{value:"query",text:"A parameter in the web address",attrs:{selected:i==="query"}})),h.append(m,r("option",{value:"custom",text:"Something else (advanced)",attrs:{selected:i==="custom"}})),w("Pre-fill this with",r("div",{class:"atfb-prefill",children:[h,o,l]}),"What the box already contains when the form opens. They can still change it.")}renderCondition(e,s){const t=s.some(l=>l.kind==="field"&&l.missing),a=r("div",{class:`atfb-cond${t?" is-broken":""}`,attrs:{"aria-label":s.length?qe(s):"Conditional rules"}});a.append(r("div",{class:"atfb-cond__heading",children:[r("span",{class:"atfb-cond__label",children:[B("randomize"),"Conditions"]}),this.copyConditionButton(`field:${e.id}`)]})),e.logic.rules.forEach((l,c)=>{const d=me(l,this.schema?.fields??[],c),h=r("button",{class:"atfb-cond__delete",type:"button",title:"Delete this rule",attrs:{"aria-label":`Delete rule ${c+1}`},children:[B("trash")],on:{click:()=>{this.snapshot(),this.editCondition(e.id,m=>{m.rules.splice(c,1)},!0,`${e.id}:add`),this.snapshot()}}});a.append(r("div",{class:"atfb-cond__rule",children:[c===0?this.renderConditionToken(e,{kind:"verb",text:e.logic.action==="hide"?"Hidden when":"Shown when"}):this.renderConditionToken(e,{kind:"join",text:e.logic.match==="all"?"and":"or"}),...d.map(m=>this.renderConditionToken(e,m,c)),h]}))}),e.logic.rules.length||a.append(r("span",{class:"atfb-hint",text:"No rules yet. Add one or copy a condition."}));const i=r("button",{class:"atfb-cond__add",type:"button",text:"+ Add rule",title:"Add rule",attrs:{"aria-label":"Add rule","data-cond":`${e.id}:add`},on:{click:()=>this.addConditionRule(e.id)}}),o=r("button",{class:"atfb-cond__clear",type:"button",text:"Clear",title:"Delete all rules",attrs:{"aria-label":"Clear all rules",disabled:!e.logic.rules.length},on:{click:()=>{this.snapshot(),this.editCondition(e.id,l=>{l.rules=[]},!0,`${e.id}:add`),this.snapshot()}}});return a.append(r("div",{class:"atfb-cond__actions",children:[i,o]})),a.addEventListener("pointerdown",l=>l.stopPropagation()),a.addEventListener("click",l=>l.stopPropagation()),a.addEventListener("keydown",l=>l.stopPropagation()),a}editCondition(e,s,t=!0,a=""){const i=this.liveField(e)?.logic;if(i){if(s(i),this.markDirty(),!t){this.logicMap?.setEdges(be(this.schema?.fields??[]));return}this.renderCanvas(),this.renderInspector(),a&&window.requestAnimationFrame(()=>{this.canvas.querySelector(`[data-cond="${CSS.escape(a)}"]`)?.focus()})}}condSelect(e,s,t,a,i){if(Y("os-select")&&Y("os-option")){const o=document.createElement("os-select");o.setAttribute("value",e),o.setAttribute("aria-label",a),o.setAttribute("data-cond",t),o.className="atfb-cond__control",o.setAttribute("plain",""),o.title=a;for(const l of s){const c=document.createElement("os-option");c.setAttribute("value",l.value),l.disabled&&c.setAttribute("disabled",""),c.textContent=l.label,o.append(c)}return o.addEventListener("os-pick",l=>{i(String(l.detail?.value??""))}),o}return r("select",{class:"atfb-cond__control atfb-cond__control--native",title:a,attrs:{"aria-label":a,"data-cond":t},on:{change:o=>i(o.target.value)},children:s.map(o=>r("option",{value:o.value,text:o.label,attrs:{selected:o.value===e,disabled:o.disabled}}))})}renderConditionToken(e,s,t=0){if(s.kind==="field"){const a=`${e.id}:source:${t}`,i=(this.schema?.fields??[]).filter(o=>o.id!==e.id&&o.type!=="page_break");return this.condSelect(s.fieldId,[...s.missing?[{value:s.fieldId,label:"Choose a question…"}]:[],...i.map(o=>({value:o.id,label:o.label||o.id}))],a,"Question used by this rule",o=>this.editCondition(e.id,l=>{const c=l.rules[t];c&&(c.field=o,c.value="")},!0,a))}if(s.kind==="verb")return r("button",{class:"atfb-cond__verb",type:"button",text:s.text,title:"Switch between showing and hiding this field when the condition matches.",attrs:{"data-cond":`${e.id}:verb`},on:{click:()=>this.editCondition(e.id,a=>{a.action=a.action==="hide"?"show":"hide"},!0,`${e.id}:verb`)}});if(s.kind==="join")return r("button",{class:"atfb-cond__join",type:"button",text:s.text,title:"Switch between needing every rule (and) or any one of them (or).",attrs:{"data-cond":`${e.id}:join`},on:{click:()=>this.editCondition(e.id,a=>{a.match=a.match==="all"?"any":"all"},!0,`${e.id}:join`)}});if(s.kind==="operator"){const a=`${e.id}:op:${s.ruleIndex}`;return this.condSelect(s.operator,Object.entries(Ie).map(([i,o])=>({value:i,label:o})),a,"How the answer is compared",i=>this.editCondition(e.id,o=>{const l=o.rules[s.ruleIndex];l&&(l.operator=i)},!0,a))}return s.kind==="value"?this.renderConditionValue(e,s):r("span")}renderConditionValue(e,s){const t=`${e.id}:value:${s.ruleIndex}`,a=this.schema?.fields.find(d=>d.id===s.sourceId),i=(d,h)=>this.editCondition(e.id,m=>{const g=m.rules[s.ruleIndex];g&&(g.value=d)},h,t),o=it(a,s.raw,this.config?.countries);if(o){const d=this.condSelect(s.raw,o,t,"The answer that triggers this",h=>i(h,!0));return d.classList.add("atfb-cond__value-select"),d.removeAttribute("plain"),d}const l=["number","range","scale","rating","total"].includes(a?.type??""),c=r("input",{class:"atfb-cond__chip atfb-cond__chip--value atfb-cond__value",value:s.raw,title:"The answer that triggers this. Edit it here.",attrs:{type:"text","aria-label":"The answer that triggers this","data-cond":t,inputmode:l?"decimal":void 0,size:String(Math.max(2,Math.min(24,s.raw.length||2)))}});return c.addEventListener("input",()=>{c.size=Math.max(2,Math.min(24,c.value.length||2)),i(c.value,!1)}),c.addEventListener("change",()=>i(c.value,!0)),c.addEventListener("keydown",d=>{d.key==="Enter"&&(d.preventDefault(),i(c.value,!0))}),c}section(e,s,t,a=!1){const i=r("details",{class:"atfb-section",attrs:{open:this.openSections.get(e)??a},children:[r("summary",{text:s}),...t]});return i.addEventListener("toggle",()=>this.openSections.set(e,i.open)),i}logicMapButton(){const e=be(this.schema?.fields??[]).length>0,s=C(this.logicMapOn?"Hide logic":"Show logic",()=>{this.logicMapOn=!this.logicMapOn,ms(Vt,this.logicMapOn?"on":"off"),this.renderBar(),this.renderCanvas()},this.logicMapOn?"primary":"secondary","randomize");return s.title="Draw a line from each question to the ones it decides.",s.hidden=!e,s}paintLogicMap(e){this.logicMap?.destroy(),this.logicMap=null;const s=this.schema?.fields??[],t=be(s);if(!t.length||!this.logicMapOn)return;e.classList.add("has-logicmap");const a=new As(e);a.setEdges(t),a.highlight(this.selected??""),this.logicMap=a,e.addEventListener("pointerover",i=>{const o=i.target.closest("[data-atfb-card]");a.highlight(o?.dataset.atfbCard??this.selected??"")}),e.addEventListener("pointerleave",()=>a.highlight(this.selected??""))}renderFieldCard(e,s){const t=this.config?.fieldTypes.find(d=>d.type===e.type),a=this.selected===e.id,i=this.schema?.fields??[],o=Es(e,i),l=$s(i).get(e.id)??0,c=r("div",{class:`atfb-card${a?" is-selected":""}`,attrs:{"data-atfb-card":e.id,"data-index":s,tabindex:"0",role:"button","aria-pressed":a,"aria-label":`${e.label||t?.label||e.type}, ${s+1} of ${this.schema?.fields.length??0}`},children:[r("div",{class:"atfb-card__bar",children:[r("div",{class:"atfb-card__grip",attrs:{"aria-hidden":"true"},children:[B("menu")]}),r("div",{class:"atfb-card__head",children:[B(t?.icon??"dashicons-forms"),r("span",{class:"atfb-card__type",text:t?.label??e.type}),this.requiredToggle(e),e.type!=="page_break"?this.conditionToolbar(e):null,l?r("span",{class:"atfb-badge atfb-badge--controls",text:l===1?"controls 1 field":`controls ${l} fields`,title:"Other questions appear or disappear based on this answer."}):null]}),r("div",{class:"atfb-card__actions",children:[this.cardAction("admin-page","Duplicate",()=>this.duplicateField(e.id)),this.cardAction("trash","Delete",()=>void this.deleteField(e.id))]})]}),r("div",{class:"atfb-card__body",children:[pt(e,t,{edit:d=>{const h=this.liveField(e.id);if(!h)return;d(h),this.markDirty(),this.syncInspector(h);const m=this.selected?this.locateField(this.selected):void 0;m?.parent&&m.parent.id===h.id&&this.syncInspector(m.field)},restructure:d=>{const h=this.liveField(e.id);h&&(this.snapshot(),d(h),this.markDirty(),this.renderCanvas(),this.renderInspector())},types:d=>this.config?.fieldTypes.find(h=>h.type===d),selectedId:this.selected}),e.logic.enabled?this.renderCondition(e,o):null]})]});return c.addEventListener("click",d=>{d.target.closest(".atfb-card__actions")||G().recentlyEndedDrag()||this.selectField(e.id)}),c.addEventListener("keydown",d=>{if(d.key==="Enter"||d.key===" "){d.preventDefault(),this.selectField(e.id);return}d.altKey&&(d.key==="ArrowUp"||d.key==="ArrowDown")&&(d.preventDefault(),this.moveField(e.id,d.key==="ArrowUp"?s-1:s+1),window.requestAnimationFrame(()=>{this.canvas.querySelector(`[data-atfb-card="${CSS.escape(e.id)}"]`)?.focus()})),(d.key==="Delete"||d.key==="Backspace")&&(d.preventDefault(),this.deleteField(e.id))}),c.addEventListener("pointerdown",d=>{d.target.closest(".atfb-card__actions")||G().start({payload:Ae(te,c,{fieldId:e.id,field:e,isNew:!1},d),origin:d})}),c}addConditionRule(e){this.snapshot(),this.editCondition(e,s=>{const t=this.schema?.fields.find(a=>a.id!==e&&a.type!=="page_break");s.rules.push({field:t?.id??"",operator:"is",value:""})},!0,`${e}:source:${this.liveField(e)?.logic.rules.length??0}`),this.snapshot()}conditionToolbar(e){const s=r("button",{class:`atfb-req atfb-condition-toggle${e.logic.enabled?" is-on":""}`,type:"button",title:e.logic.enabled?"Edit conditional logic":"Set up conditional logic",attrs:{"aria-haspopup":"dialog","data-cond":`${e.id}:enabled`},children:[r("span",{class:"atfb-condition-dot",attrs:{"aria-hidden":"true"}}),r("span",{text:"Conditional"})],on:{click:()=>this.openConditionEditor(e.id)}});for(const t of["pointerdown","click","keydown"])s.addEventListener(t,a=>a.stopPropagation());return s}openConditionEditor(e){const s=this.liveField(e);if(!s)return;const t={...s.logic,rules:s.logic.rules.map(d=>({...d}))},a=r("div",{class:"atfb-overlay"}),i=r("div",{class:"atfb-modal atfb-condition-editor",attrs:{role:"dialog","aria-modal":"true","aria-label":"Conditional logic",tabindex:"-1"}}),o=()=>{a.remove(),this.root.querySelector(`[data-cond="${CSS.escape(e)}:enabled"]`)?.focus()},l="button:not([disabled]), input, select, os-select, os-checkbox-label, os-button:not([disabled])",c=()=>{const d=[...i.querySelectorAll(l)].indexOf(document.activeElement),h=(f,y=!1)=>{f(t),y&&c()},m=D(t.action,[{value:"show",label:"Show"},{value:"hide",label:"Hide"}],f=>{t.action=f});m.setAttribute("aria-label","Conditional action");const g=D(t.match,[{value:"all",label:"all"},{value:"any",label:"any"}],f=>{t.match=f,c()});g.setAttribute("aria-label","Match rules");const u=r("button",{class:"atfb-copy-condition",type:"button",children:[B("admin-page"),r("span",{text:"Copy condition"})],on:{click:()=>Dt({root:a,to:`field:${e}`,schema:()=>this.schema?{...this.schema,fields:this.schema.fields.map(f=>f.id===e?{...f,logic:t}:f)}:null,onCopy:()=>{c(),i.focus()}})}});if(i.replaceChildren(r("div",{class:"atfb-condition-editor__heading",children:[r("span",{class:"atfb-condition-editor__icon",children:[B("randomize")]}),r("div",{children:[r("h2",{text:"Conditional logic"}),r("p",{text:s.label||"Untitled field"})]})]}),r("div",{class:"atfb-condition-editor__switch",children:[q("Enable conditions",t.enabled,f=>{t.enabled=f,c()}),r("p",{text:"Choose when this field appears in your form."})]}),...t.enabled?[r("div",{class:"atfb-condition-editor__sentence",children:[m,"this field when",g,"of these rules match:"]}),...this.logicRulesEditor(t,h,e)]:[r("p",{class:"atfb-condition-editor__empty",text:"This field is always visible. Enable conditions to choose when to show or hide it."})],r("div",{class:"atfb-condition-editor__footer",children:[u,r("div",{class:"atfb-modal__actions",children:[C("Cancel",o),C("Save conditions",()=>{if(!this.liveField(e)){o();return}this.snapshot(),this.editCondition(e,f=>Object.assign(f,t,{rules:t.rules.map(y=>({...y}))})),this.snapshot(),o()},"primary")]})]})),d>=0){const f=i.querySelectorAll(l);f[Math.min(d,f.length-1)]?.focus()}};a.append(i),a.addEventListener("click",d=>{d.target===a&&o()}),a.addEventListener("keydown",d=>{if(d.key==="Escape"&&(d.preventDefault(),o()),d.key==="Tab"){const h=[...i.querySelectorAll(l)],m=h[0],g=h[h.length-1];d.shiftKey&&(document.activeElement===m||document.activeElement===i)?(d.preventDefault(),g?.focus()):!d.shiftKey&&document.activeElement===g&&(d.preventDefault(),m?.focus())}d.stopPropagation()}),this.root.append(a),c(),i.focus()}requiredToggle(e){return r("button",{class:`atfb-req${e.required?" is-on":""}`,type:"button",text:e.required?"Required":"Optional",title:e.required?"This must be answered. Click to make it optional.":"Click to make this required.",attrs:{"aria-pressed":e.required?"true":"false"},on:{pointerdown:t=>t.stopPropagation(),click:t=>{t.stopPropagation(),e.required=!e.required,this.markDirty(),this.renderCanvas(),this.renderInspector()}}})}cardAction(e,s,t){return r("button",{class:"atfb-card__action",type:"button",title:s,attrs:{"aria-label":s},on:{click:a=>{a.stopPropagation(),t()}},children:[B(e)]})}gapAnimator(e,s,t,a){const i=r("div",{class:"atfb-marker",attrs:{"aria-hidden":"true"}});let o=null,l=null,c=0;const d=()=>{i.remove(),l?.classList.remove("atfb-lifted"),l=null,o=null},h=u=>{const f=Array.from(e.querySelectorAll(s)),y=new Map(f.filter(x=>!x.classList.contains("atfb-lifted")).map(x=>[x,x.getBoundingClientRect().top]));u();for(const x of f)x.style.transition="none",x.style.transform="";const p=f.filter(x=>{const F=y.get(x);if(F===void 0||!x.isConnected||x.classList.contains("atfb-lifted"))return!1;const S=F-x.getBoundingClientRect().top;return Math.abs(S)<.5?!1:(x.style.transform=`translateY(${S}px)`,!0)});e.offsetHeight;for(const x of f){if(!p.includes(x)){x.style.transition="";continue}x.style.transition="transform 160ms ease",x.style.transform="",x.addEventListener("transitionend",()=>{x.style.transition=""},{once:!0})}},m=u=>{const f=u.detail;if(f?.payload?.type!==te||!e.classList.contains("is-dropping")||a&&!a(f.payload.data??{}))return;const y=f.payload.data?.fieldId,p=typeof y=="string"?t(y):null,x=st(e,s,f.clientY??0,p??void 0);x===o&&i.isConnected||(o=x,h(()=>{p&&p!==l&&(l?.classList.remove("atfb-lifted"),p.classList.add("atfb-lifted"),l=p),i.style.blockSize=`${Math.max(8,Math.round(c))}px`;const F=Array.from(e.querySelectorAll(s)).filter(S=>S!==p);x>=F.length?e.append(i):F[x].before(i)}))},g=()=>{e.classList.remove("is-dropping"),window.setTimeout(()=>h(d),0)};return document.addEventListener("os.drag.move",m),document.addEventListener("os.drag.end",g),{enter:u=>{const f=u?t(u):null;c=f&&!f.classList.contains("atfb-lifted")?f.getBoundingClientRect().height:c||44},leave:()=>h(d),drop:(u,f)=>{const y=o??st(e,s,u,f??void 0);return d(),y},teardown:()=>{document.removeEventListener("os.drag.move",m),document.removeEventListener("os.drag.end",g),d()}}}registerCanvasTarget(e){this.canvasTarget?.(),this.canvasTarget=null;const s=this.gapAnimator(e,".atfb-card",i=>this.canvas.querySelector(`[data-atfb-card="${CSS.escape(i)}"]`)),t=G().registerDropTarget({id:`atfb-canvas-${this.form?.id??0}`,element:e,accept:i=>i.type===te,onEnter:i=>{e.classList.add("is-dropping"),s.enter(i.payload.data.fieldId)},onLeave:()=>{e.classList.remove("is-dropping"),s.leave()},onDrop:(i,o)=>{e.classList.remove("is-dropping");const l=i.payload.data,c=l.fieldId?this.canvas.querySelector(`[data-atfb-card="${CSS.escape(l.fieldId)}"]`):null,d=s.drop(o.clientY,c);if(l.isNew&&l.fieldType){this.addField(l.fieldType,d);return}if(l.fieldId&&this.locateField(l.fieldId)){this.relocateField(l.fieldId,null,d);return}l.field&&this.insertField({...l.field,id:""},d)}}),a=this.wireRepeaterZones(e);this.canvasTarget=()=>{t(),a.forEach(i=>i()),s.teardown()}}wireRepeaterZones(e){const s=[];return e.querySelectorAll("[data-atfb-repeater-zone]").forEach(t=>{const a=t.dataset.atfbRepeaterZone??"",i=l=>{const c=l;if(c.fieldId===a)return!1;const d=c.isNew?c.fieldType:this.locateField(c.fieldId??"")?.field.type??c.field?.type;return!!d&&this.allowedInRepeater(d)},o=this.gapAnimator(t,".atfb-subcard",l=>e.querySelector(`[data-atfb-subfield="${CSS.escape(l)}"]`),i);s.push(()=>o.teardown()),s.push(G().registerDropTarget({id:`atfb-repzone-${this.form?.id??0}-${a}`,element:t,accept:l=>l.type===te&&i(l.data),onEnter:l=>{t.classList.add("is-dropping"),o.enter(l.payload.data.fieldId)},onLeave:()=>{t.classList.remove("is-dropping"),o.leave()},onDrop:(l,c)=>{t.classList.remove("is-dropping");const d=l.payload.data,h=d.fieldId?e.querySelector(`[data-atfb-subfield="${CSS.escape(d.fieldId)}"]`):null,m=o.drop(c.clientY,h);if(d.isNew&&d.fieldType){this.addFieldToRepeater(d.fieldType,a,m);return}if(d.fieldId&&this.locateField(d.fieldId)){this.relocateField(d.fieldId,a,m);return}d.field&&this.insertFieldIntoRepeater({...d.field,id:""},a,m)}})),t.querySelectorAll(".atfb-subcard").forEach(l=>{const c=l.dataset.atfbSubfield??"";l.addEventListener("click",d=>{d.target.closest(".atfb-preview__remove")||(d.stopPropagation(),!G().recentlyEndedDrag()&&this.selectField(c))}),l.addEventListener("keydown",d=>{if(d.stopPropagation(),d.key==="Enter"||d.key===" "){d.preventDefault(),this.selectField(c);return}if(d.altKey&&(d.key==="ArrowUp"||d.key==="ArrowDown")){d.preventDefault();const h=this.locateField(c);h&&(this.relocateField(c,a,d.key==="ArrowUp"?h.index-1:h.index+1),window.requestAnimationFrame(()=>{this.canvas.querySelector(`[data-atfb-subfield="${CSS.escape(c)}"]`)?.focus()}));return}(d.key==="Delete"||d.key==="Backspace")&&(d.preventDefault(),this.deleteField(c))}),l.addEventListener("pointerdown",d=>{if(d.stopPropagation(),d.target.closest(".atfb-preview__remove"))return;const h=this.locateField(c);h&&G().start({payload:Ae(te,l,{fieldId:c,parentId:a,field:h.field,isNew:!1},d),origin:d})})})}),s}addField(e,s){const t=this.buildField(e);t&&this.insertField(t,s)}buildField(e){const s=this.config?.fieldTypes.find(a=>a.type===e);return!s||!this.schema?void 0:{id:this.nextFieldId(),type:e,label:s.input?s.label:"",placeholder:"",hint:"",required:!1,width:"full",cssClass:"",default:"",choices:s.choices?[{label:"First choice",value:"first"},{label:"Second choice",value:"second"}]:[],logic:{enabled:!1,action:"show",match:"all",rules:[]},messages:{},prefill:"",...s.settings}}allowedInRepeater(e){const s=this.config?.fieldTypes.find(t=>t.type===e);return!s||!s.input?!1:!["repeater","page_break","file","signature","total"].includes(e)}addFieldToRepeater(e,s,t){if(!this.allowedInRepeater(e))return;const a=this.buildField(e);a&&this.insertFieldIntoRepeater(a,s,t)}insertFieldIntoRepeater(e,s,t){const a=this.liveField(s);if(!this.schema||!a||a.type!=="repeater")return;e.id||(e.id=this.nextFieldId()),this.snapshot(),Array.isArray(a.fields)||(a.fields=[]);const i=a.fields,o=t===void 0?i.length:Math.max(0,Math.min(t,i.length));i.splice(o,0,e),this.selected=e.id,this.markDirty(),this.renderCanvas(),this.renderInspector(),window.requestAnimationFrame(()=>{this.canvas.querySelector(`[data-atfb-subfield="${CSS.escape(e.id)}"]`)?.focus()})}relocateField(e,s,t){if(!this.schema)return;const a=this.locateField(e);if(!a)return;if(!a.parent&&!s){this.moveField(e,t);return}const i=s?this.liveField(s):null;if(s&&(!i||i.type!=="repeater"||e===s))return;i&&!Array.isArray(i.fields)&&(i.fields=[]);const o=i?i.fields:this.schema.fields;this.snapshot(),a.list.splice(a.index,1);const l=Math.max(0,Math.min(t,o.length));o.splice(l,0,a.field),this.selected=a.field.id,this.markDirty(),this.renderCanvas(),this.renderInspector()}insertField(e,s){if(!this.schema)return;e.id||(e.id=this.nextFieldId()),this.snapshot();const t=s===void 0?this.schema.fields.length:Math.max(0,Math.min(s,this.schema.fields.length));this.schema.fields.splice(t,0,e),this.selected=e.id,this.markDirty(),this.renderCanvas(),this.renderInspector(),window.requestAnimationFrame(()=>{this.canvas.querySelector(`[data-atfb-card="${CSS.escape(e.id)}"]`)?.focus()})}moveField(e,s){if(!this.schema)return;const t=Zt(this.schema.fields,e,s);if(!t)return;this.snapshot();const[a]=this.schema.fields.splice(t.from,1);this.schema.fields.splice(t.to,0,a),this.markDirty(),this.renderCanvas()}duplicateField(e){if(!this.schema)return;const s=this.locateField(e);if(!s)return;const t=JSON.parse(JSON.stringify(s.field)),a=this.usedFieldIds(),i=()=>{let o=a.size+1;for(;a.has(`f${o}`);)o++;const l=`f${o}`;return a.add(l),l};t.id=i();for(const o of t.fields??[])o.id=i();if(s.parent){this.snapshot(),s.list.splice(s.index+1,0,t),this.selected=t.id,this.markDirty(),this.renderCanvas(),this.renderInspector();return}this.insertField(t,s.index+1)}async deleteField(e){if(!this.schema)return;const s=this.locateField(e);if(!s)return;const t=this.schema.fields.filter(i=>i.logic?.rules?.some(o=>o.field===e)),a=t.length?`Delete this field? ${t.length} other field${t.length===1?"":"s"} use it in a condition, and those conditions will stop working.`:Qe("confirmDelete","Delete this? It cannot be undone.");await ae(a,"Delete field")&&(this.snapshot(),s.list.splice(s.index,1),this.selected===e&&(this.selected=null),this.markDirty(),this.renderCanvas(),this.renderInspector())}selectField(e){this.selected=e;for(const s of this.canvas.querySelectorAll("[data-atfb-card]")){const t=s.dataset.atfbCard===e;s.classList.toggle("is-selected",t),s.setAttribute("aria-pressed",t?"true":"false")}for(const s of this.canvas.querySelectorAll("[data-atfb-subfield]"))s.classList.toggle("is-selected",s.dataset.atfbSubfield===e);this.logicMap?.highlight(e),this.renderInspector()}nextFieldId(){const e=this.usedFieldIds();let s=e.size+1;for(;e.has(`f${s}`);)s++;return`f${s}`}usedFieldIds(){const e=new Set;for(const s of this.schema?.fields??[]){e.add(s.id);for(const t of s.fields??[])e.add(t.id)}return e}nextEntryId(e,s){const t=new Set(s.map(i=>i.id));let a=s.length+1;for(;t.has(`${e}${a}`);)a++;return`${e}${a}`}renderTypeSettings(e,s,t,a){for(const i of t){const o=Kt[i];o&&(this.inspector.append(Gt(e,o,a)),o.also&&this.inspector.append(Gt(e,{...o,key:o.also.key,label:o.also.label,hint:o.also.hint},a)))}t.includes("rows")&&e.type!=="likert"&&this.inspector.append(w("Lines tall",V(String(e.rows??""),i=>a("rows",i)))),t.includes("rows")&&e.type==="likert"&&this.inspector.append(this.renderStatementList(e,a)),t.includes("parts")&&this.inspector.append(this.renderPartsEditor(e,s,a)),t.includes("default")&&!["files","object"].includes(s?.value??"")&&this.inspector.append(this.renderDefaultControl(e,s,a))}renderStatementList(e,s){const t=Array.isArray(e.rows)?e.rows:[];return w("Statements",pe(t.map(a=>a.label??"").join(` +`),a=>s("rows",Xt(t,a)),5),"One per line. Each becomes a row of the matrix.")}renderPartsEditor(e,s,t){const a=s?.parts??[],i=Array.isArray(e.parts)?e.parts:a.map(l=>l.key),o=a.map(l=>q(l.label,i.includes(l.key),c=>{const d=a.map(h=>h.key).filter(h=>h===l.key?c:i.includes(h));t("parts",d.length?d:[l.key])}));return r("div",{class:"atfb-section",children:[r("h4",{class:"atfb-section__title",text:"Parts to ask for"}),...o]})}renderDefaultControl(e,s,t){const a="Filled in before they start. They can change it.";return e.type==="switch"||e.type==="consent"?q("On by default",!!e.default,i=>t("default",i)):s?.choices&&(e.choices??[]).length?w("Default answer",D(String(e.default??""),[{value:"",label:"Nothing chosen"},...(e.choices??[]).map(i=>({value:String(i.value),label:i.label||String(i.value)}))],i=>t("default",i)),a):w("Default answer",T(String(e.default??""),i=>t("default",i)),a)}renderChoicesEditor(e,s){const t=e.choices??[],a=o=>this.liveField(e.id)?.choices?.[o],i=r("div",{class:"atfb-choices"});return t.forEach((o,l)=>{const c=r("div",{class:"atfb-choice-row",children:[e.type==="image_choice"?this.choiceImageWell(o,l,e):null,J(T(o.label,d=>{const h=a(l);if(!h)return;const m=!h.value||h.value===h.label;h.label=d,m&&(h.value=d),this.markDirty();const g=this.liveField(e.id);g&&this.syncCanvas(g)}),`choice:${l}:label`),J(T(o.value,d=>{const h=a(l);h&&(h.value=d,this.markDirty())},"value"),`choice:${l}:value`),e.type==="quiz"||o.points!==void 0?V(String(o.points??""),d=>{const h=a(l);h&&(h.points=d===""?void 0:Number(d),this.markDirty())}):V(String(o.price??""),d=>{const h=a(l);h&&(h.price=d===""?void 0:Number(d),this.markDirty())}),r("button",{class:"atfb-card__action",type:"button",attrs:{"aria-label":`Remove ${o.label}`},on:{click:()=>{const d=this.liveField(e.id);d&&((d.choices??[]).splice(l,1),s("choices",d.choices),this.renderInspector())}},children:[B("trash")]})]});i.append(c)}),r("div",{class:"atfb-section",children:[r("h4",{text:"Choices"}),r("p",{class:"atfb-hint",text:e.type==="quiz"?"Label, value, points.":"Label, value, and a price for calculations."}),i,C("Add choice",()=>{const o=this.liveField(e.id);if(!o)return;const l=o.choices??[];l.push({label:"",value:""}),s("choices",l),this.renderInspector()},"ghost","plus-alt2"),e.type==="quiz"?w("Correct answer",D(String(e.correct??""),[{value:"",label:"—"},...t.map(o=>({value:o.value,label:o.label}))],o=>s("correct",o))):null]})}choiceImageWell(e,s,t){const a=r("div",{class:`atfb-well${e.image?" has-image":""}`,attrs:{"data-choice":s,"aria-label":`Image for ${e.label||`choice ${s+1}`}`},children:[e.image?r("span",{class:"atfb-well__id",text:`#${e.image}`}):B("format-image")]}),i=G().registerDropTarget({id:`atfb-well-${t.id}-${s}`,element:a,accept:o=>Bn.includes(o.type),onEnter:()=>a.classList.add("is-dropping"),onLeave:()=>a.classList.remove("is-dropping"),onDrop:o=>{a.classList.remove("is-dropping");const l=o.payload.data,c=Number(l.attachmentId??l.id??l.file?.id??0);if(!c){N("That is not an image this field can use","","error");return}const d=this.liveField(t.id)?.choices?.[s];d&&(d.image=c,this.markDirty(),this.renderInspector())}});return this.teardowns.push(i),a.addEventListener("click",()=>{const o=this.liveField(t.id)?.choices?.[s];o?.image&&(o.image=void 0,this.markDirty(),this.renderInspector())}),a}renderValidationSection(e,s,t){const a=[],i=[];s.includes("minlength")&&i.push(["minlength","Minimum characters"],["maxlength","Maximum characters"]),s.includes("min")?i.push(["min","Minimum"],["max","Maximum"]):s.includes("max")&&i.push(["max","Highest"]),s.includes("step")&&i.push(["step","Steps of"]),s.includes("mindate")&&i.push(["minDate","Earliest date"],["maxDate","Latest date"]),s.includes("mintime")&&i.push(["minTime","Earliest time"],["maxTime","Latest time"]);for(const[l,c]of i)a.push(w(c,T(String(e[l]??""),d=>t(l,d))));s.includes("pattern")&&a.push(...this.renderAnswerShapeRows(e,t)),s.includes("unique")&&a.push(q("No two people may submit the same value",!!e.unique,l=>t("unique",l)));const o=e.messages??{};return s.includes("required")&&a.push(w("Message when required",T(o.required??"",l=>{o.required=l,t("messages",o)}),"Leave empty for the default wording.")),a.length?this.section(`validation:${e.id}`,"Validation",a):r("div",{class:"atfb-section is-empty"})}buildShapePicker(e,s){const t=[{heading:null,options:[{value:"",label:"Anything at all"}]},...On.map(i=>({heading:i,options:Wt.filter(o=>o.group===i).map(o=>({value:o.slug,label:o.label}))})),{heading:"Your own",options:[{value:"custom",label:"A custom rule…"}]}];if(Y("os-select")&&Y("os-option")){const i=document.createElement("os-select");i.setAttribute("value",e),i.setAttribute("aria-label","What the answer should look like"),i.classList.add("atfb-field");for(const o of t){if(o.heading){const l=document.createElement("os-option");l.setAttribute("value",`__heading:${o.heading}`),l.setAttribute("disabled",""),l.textContent=o.heading,i.append(l)}for(const l of o.options){const c=document.createElement("os-option");c.setAttribute("value",l.value),c.textContent=l.label,i.append(c)}}return i.addEventListener("os-pick",o=>{s(String(o.detail?.value??""))}),i}const a=r("select",{class:"atfb-input atfb-select",attrs:{"aria-label":"What the answer should look like"},on:{change:i=>s(i.target.value)}});for(const i of t){const o=i.heading?(()=>{const l=document.createElement("optgroup");return l.label=i.heading,a.append(l),l})():a;for(const l of i.options)o.append(r("option",{value:l.value,text:l.label,attrs:{selected:l.value===e}}))}return a}renderAnswerShapeRows(e,s){const a=(typeof e.validation=="string"?e.validation:"")||(e.pattern?"custom":""),i=()=>In({root:this.root,field:this.liveField(e.id)??e,onSave:h=>{s("validation","custom"),s("pattern",h.pattern),s("validationRecipe",JSON.stringify(h.recipe));const m={...this.liveField(e.id)?.messages??{}};m.invalid=h.message,s("messages",m),this.renderInspector()},onCancel:()=>this.renderInspector()}),o=h=>{if(h==="custom"){i();return}s("validation",h),s("pattern",""),s("validationRecipe",""),this.renderInspector()},l=this.buildShapePicker(a,o),c=Nn(a),d=[w("The answer should be",l,c?`e.g. ${c.example}`:"Checked as they type, and again on the server.")];if(a==="custom"){const h=Pt(String(e.validationRecipe??"")),m=Bt(h)||(e.pattern?`Matches the expression ${String(e.pattern)}`:"No rule yet — open the builder.");d.push(w("Your rule",r("div",{class:"atfb-valrule",children:[r("p",{class:"atfb-valrule__words",text:m}),C("Edit the rule…",i,"secondary","edit")]}),Ce(h)||e.pattern?"Built in the rule builder, with a playground to test it.":void 0))}return d}logicRulesEditor(e,s,t=""){const a=(this.schema?.fields??[]).filter(d=>d.id!==t&&d.type!=="page_break"),i=()=>r("div",{class:"atfb-rule-join",children:[r("button",{class:"atfb-rule-join__chip",type:"button",text:e.match==="all"?"and":"or",title:"Switch between needing every rule (and) or any one of them (or).",on:{click:()=>s(d=>{d.match=d.match==="all"?"any":"all"},!0)}})]}),o=(d,h)=>{const m=r("button",{class:"atfb-card__action",type:"button",attrs:{"aria-label":"Remove this rule"},title:"Remove this rule",on:{click:()=>s(u=>{u.rules.splice(h,1)},!0)},children:[B("trash")]}),g=[r("div",{class:"atfb-rule__top",children:[D(d.field,a.map(u=>({value:u.id,label:u.label||u.id})),u=>s(f=>{const y=f.rules[h];y&&(y.field=u,y.value="")},!0)),m]}),D(d.operator,Object.entries(this.config?.operators??{}).map(([u,f])=>({value:u,label:f})),u=>s(f=>{const y=f.rules[h];y&&(y.operator=u)},!0))];if(!De.includes(d.operator)){const u=this.schema?.fields.find(y=>y.id===d.field),f=it(u,d.value,this.config?.countries);f?g.push(D(d.value,f,y=>s(p=>{const x=p.rules[h];x&&(x.value=y)}))):g.push(T(d.value,y=>s(p=>{const x=p.rules[h];x&&(x.value=y)}),"The answer to compare against"))}return g[0].querySelector("select, os-select")?.setAttribute("aria-label","Question used by this rule"),g[1].setAttribute("aria-label","How the answer is compared"),g[2]?.setAttribute("aria-label","The answer that triggers this"),g[2]?.matches("select, os-select")&&g[2].classList.add("atfb-cond__value-select"),r("div",{class:"atfb-rule",children:g})},l=r("div",{class:"atfb-rules"});e.rules.forEach((d,h)=>{h>0&&l.append(i()),l.append(o(d,h))});const c=C("Add rule",()=>{s(d=>{d.rules.push({field:a[0]?.id??"",operator:"is",value:""})},!0)},"ghost","plus-alt2");return[l,c]}conditionsSection(e,s,t){const a=(o,l=!1)=>{o(t),this.markDirty(),l&&this.renderCanvas()},i=s==="confirmation"?"Use":"Send";return this.section(`conditions:${e}`,"Conditions",[this.copyConditionButton(`${s}:${e}`),q(`Only ${i.toLowerCase()} this ${s} sometimes`,t.enabled,o=>a(l=>{l.enabled=o},!0)),t.enabled?r("div",{children:[r("div",{class:"atfb-rule-head",children:[r("span",{text:`${i} it when`}),D(t.match,[{value:"all",label:"all"},{value:"any",label:"any"}],o=>a(l=>{l.match=o},!0)),r("span",{text:"of these match:"})]}),...this.logicRulesEditor(t,a)]}):null],t.enabled)}copyConditionButton(e){return r("button",{class:"atfb-copy-condition",type:"button",children:[B("admin-page"),r("span",{text:"Copy condition"})],on:{click:()=>Dt({root:this.root,schema:()=>this.schema,to:e,beforeCopy:()=>this.snapshot(),onCopy:()=>{this.snapshot(),this.markDirty(),this.renderCanvas(),this.renderInspector()}})}})}renderLogicSection(e){const s=e.logic,t=()=>this.liveField(e.id)?.logic,a=(o,l=!1)=>{const c=t();c&&(o(c),this.markDirty(),this.renderCanvas(),l&&this.renderInspector())},i=this.logicRulesEditor(s,a,e.id);return this.section(`logic:${e.id}`,"Conditional logic",[this.copyConditionButton(`field:${e.id}`),q("Only show this field sometimes",s.enabled,o=>{a(l=>{l.enabled=o},!0)}),s.enabled?r("div",{children:[r("div",{class:"atfb-rule-head",children:[D(s.action,[{value:"show",label:"Show"},{value:"hide",label:"Hide"}],o=>a(l=>{l.action=o},!0)),r("span",{text:"this field when"}),D(s.match,[{value:"all",label:"all"},{value:"any",label:"any"}],o=>a(l=>{l.match=o},!0)),r("span",{text:"of these match:"})]}),...i]}):null],s.enabled)}renderTabCanvas(){return this.schema?this.tab==="theme"?yt({themes:this.themes,tokens:this.config?.tokens??[],activeSlug:this.schema.settings.theme,overrides:this.schema.settings.themeOverrides,onTheme:e=>{this.schema.settings.theme=e,this.markDirty()},onOverride:(e,s)=>{s===""?delete this.schema.settings.themeOverrides[e]:this.schema.settings.themeOverrides[e]=s,this.markDirty()},onOverridesReplaced:e=>{this.schema.settings.themeOverrides={...e},this.markDirty()},previewFor:(e,s)=>this.previewHtml(e,s),onThemesChanged:e=>{this.themes=e}}):this.tab==="settings"?this.renderSettingsPane():this.tab==="notify"?this.renderNotificationsPane():this.renderConfirmationsPane():r("div")}async paintCanvasTheme(){if(!this.form||!this.schema)return;const e=this.schema.settings.theme,s=JSON.stringify([e,this.schema.settings.themeOverrides]);if(s!==this.canvasThemeSignature){this.canvasThemeSignature=s;try{const t=await this.previewHtml(e,this.schema.settings.themeOverrides??{});this.root.classList.toggle("atfb--dark-form",/atf-is-dark/.test(t));const a=/class="atf-form-wrap"[^>]*\sstyle="([^"]*)"/.exec(t);if(!a)return;const i=a[1].replace(/"/g,'"').replace(/�?39;/g,"'").replace(/&/g,"&");this.canvasTheme.textContent=`.atfb .atfb-preview { ${i} -}`,this.canvasTheme.isConnected||this.root.append(this.canvasTheme)}catch{}}}async previewHtml(e,t){if(!this.form||!this.schema)return"";const s=JSON.parse(JSON.stringify(this.schema));s.settings.theme=e,s.settings.themeOverrides=t;const{html:a}=await F.preview(this.form.id,{schema:s,theme:e});return a}renderSettingsPane(){const e=this.schema.settings,t=(s,a)=>{const i=s.split(".");let o=e;for(let l=0;lt("submitLabel",s))),O("Submit without reloading the page",e.ajax,s=>t("ajax",s)),y("Progress indicator",D(e.progressBar,[{value:"steps",label:"Numbered steps"},{value:"bar",label:"A bar"},{value:"none",label:"None"}],s=>t("progressBar",s)),"Only shown on forms with a page break.")]}),r("section",{children:[r("h3",{text:"Who can fill this in"}),O("Only logged-in users",e.requireLogin,s=>t("requireLogin",s)),y("Message for everyone else",T(e.loginMessage,s=>t("loginMessage",s))),y("Open from",r("input",{class:"atfb-input",type:"datetime-local",value:e.schedule.start,on:{input:s=>t("schedule.start",s.target.value)}})),y("Closes",r("input",{class:"atfb-input",type:"datetime-local",value:e.schedule.end,on:{input:s=>t("schedule.end",s.target.value)}})),y("Message when closed",T(e.schedule.message,s=>t("schedule.message",s))),y("Stop after this many submissions",G(String(e.limit.total||""),s=>t("limit.total",Number(s)||0)),"0 means no limit."),y("Submissions per logged-in user",G(String(e.limit.perUser||""),s=>t("limit.perUser",Number(s)||0)))]}),r("section",{children:[r("h3",{text:"Spam"}),r("p",{class:"atfb-hint",text:"No captcha. Nothing here asks the visitor to prove anything."}),O("Honeypot field",e.spam.honeypot,s=>t("spam.honeypot",s)),y("Reject submissions faster than (seconds)",G(String(e.spam.timeTrap),s=>t("spam.timeTrap",Number(s)||0)),"A human cannot fill in a form in under a second. A script can."),y("Submissions allowed per hour, per address",G(String(e.spam.rateLimit),s=>t("spam.rateLimit",Number(s)||0))),y("Blocked words",pe(e.spam.blocklist,s=>t("spam.blocklist",s),4),"One per line."),O("Check submissions with Akismet",e.spam.akismet,s=>t("spam.akismet",s)),r("p",{class:"atfb-hint",text:"Off unless you switch it on. When on, each submission — the answers, plus the sender’s IP address and user agent — is sent to Akismet’s servers for a spam verdict. Needs the Akismet plugin installed and configured."}),O("Ask a simple sum before sending",e.spam.challenge,s=>t("spam.challenge",s)),r("p",{class:"atfb-hint",text:"Only for a form under sustained attack — it is the one check here that asks the visitor to do something. Still kinder than an image captcha: it is answerable by a screen reader, and it hands no data to anyone."})]}),r("section",{children:[r("h3",{text:"Storage and privacy"}),O("Keep entries",e.storage.entries,s=>t("storage.entries",s)),O("Record IP addresses",e.storage.ip,s=>t("storage.ip",s)),O("Anonymise recorded IP addresses",e.storage.anonymise,s=>t("storage.anonymise",s)),y("Delete entries after (days)",G(String(e.storage.retention||""),s=>t("storage.retention",Number(s)||0)),"0 keeps them forever. Anything else deletes automatically, every day.")]}),r("section",{children:[r("h3",{text:"Analytics"}),O("Count views and starts",e.analytics.enabled,s=>t("analytics.enabled",s)),O("Tally device, browser and system",e.analytics.tech,s=>t("analytics.tech",s)),r("p",{class:"atfb-hint",text:"Counters, never people: no cookie, no fingerprint, no per-visitor record — the tallies keep coarse classes like “phone” and “Chrome”, not the visitor’s user-agent string. Nothing here needs a consent banner. Turning the first switch off stops view and start counting entirely."})]}),r("section",{children:[r("h3",{text:"Save and continue later"}),O("Let people save a half-finished form",e.resume.enabled,s=>t("resume.enabled",s)),y("Keep a saved form for (days)",G(String(e.resume.days),s=>t("resume.days",Math.max(1,Number(s)||30)))),r("p",{class:"atfb-hint",text:"The link this creates is the only key to those answers — anyone holding it can read them. For genuinely sensitive questions, require login instead."})]}),r("section",{children:[r("h3",{text:"Archive"}),r("p",{class:"atfb-hint",text:"Retire the form when its moment has passed. It stops accepting responses and leaves every list, and its entries and stats go with it — nothing is deleted, and restoring it brings all of it back exactly as it was."}),$("Archive this form",()=>void this.archiveCurrentForm(),"secondary","archive")]}),r("section",{children:[r("h3",{text:"Delete"}),r("p",{class:"atfb-hint",text:"Deleting is for a form that should never have existed. It moves to the desktop’s Trash, disappears from every list, and any page still carrying its shortcode shows nothing at all. Its entries stay in Entries, and until the Trash is emptied you can bring it back from there. If the form simply had its day, archive it instead."}),$("Delete this form",()=>void this.deleteCurrentForm(),"danger","trash")]})]})}async archiveCurrentForm(){if(!this.form)return;const e=this.form.title||"(untitled)",t=this.forms.find(a=>a.id===this.form.id)?.entries??0;if(await ie(`Archive “${e}”? It stops accepting responses and leaves every list — its ${t} ${t===1?"entry":"entries"} and its stats go with it. Nothing is deleted: restore it any time from “Start a new form”.`,"Archive form"))try{this.dirty&&await this.save(!0),await F.archiveForm(this.form.id),N("Form archived",`${e} — restore it any time from the New form dialog.`),await this.releaseCurrentForm()}catch(a){N("Could not archive the form",a instanceof Error?a.message:"","error")}}async deleteCurrentForm(){if(!this.form)return;const e=this.form.title||"(untitled)",t=this.forms.find(a=>a.id===this.form.id)?.entries??0;if(await ie(`Delete “${e}”? Every page showing it goes blank. ${t?`Its ${t} ${t===1?"entry stays":"entries stay"} in Entries. `:""}It moves to the desktop’s Trash — restore it from there if you change your mind.`,"Delete form"))try{const a=this.form.id;await F.deleteForm(a),window.wp?.os?.announceContentChange?.("alltfo_form","trashed",a,"allterrain-forms"),N("Form deleted",`${e} moved to the desktop’s Trash.`),await this.releaseCurrentForm()}catch(a){N("Could not delete the form",a instanceof Error?a.message:"","error")}}async releaseCurrentForm(){if(this.forms=this.forms.filter(e=>e.id!==this.form.id),this.form=null,this.schema=null,this.selected=null,this.dirty=!1,this.history=[],this.historyAt=-1,this.forms.length){await this.open(this.forms[0].id);return}this.renderBar(),this.renderCanvas(),this.renderInspector()}taggableInput(e,t,s=""){return ut(T(e,t,s),{formId:this.form.id})}taggableArea(e,t,s=6){return ut(pe(e,t,s),{formId:this.form.id})}recipientControl(e){const t=this.schema.fields.filter(c=>c.type==="email"),s=c=>{if(c.trim()==="{admin_email}")return"admin";const d=c.trim().match(/^\{field:([a-z0-9_-]+)\}$/i);return d&&t.some(h=>h.id===d[1])?`field:${d[1]}`:/\{/.test(c)?"custom":"address"},a=[{value:"admin",label:"Whoever runs this site"},...t.map(c=>({value:`field:${c.id}`,label:`The person who filled it in — ${c.label||"their email answer"}`})),{value:"address",label:"A specific email address"},{value:"custom",label:"Something else (advanced)"}],i=s(e.to),o=r("div",{class:"atfb-recipient__detail"}),l=c=>{if(o.replaceChildren(),c==="address"){o.append(T(/\{/.test(e.to)?"":e.to,d=>{e.to=d,this.markDirty()},"name@example.com"));return}c==="custom"&&o.append(this.taggableInput(e.to,d=>{e.to=d,this.markDirty()},"{admin_email}, sales@example.com"),r("p",{class:"atfb-row__hint",text:"Separate several addresses with commas."}))};return l(i),y("Send it to",r("div",{class:"atfb-recipient",children:[D(i,a,c=>{c==="admin"?e.to="{admin_email}":c.startsWith("field:")?e.to=`{${c}}`:c==="address"&&(e.to=/\{/.test(e.to)?"":e.to),this.markDirty(),l(c)}),o]}),t.length?void 0:"Add an Email question on the Build tab to reply straight back to the visitor.")}renderNotificationsPane(){const e=this.schema.notifications,t=r("div",{class:"atfb-list"});return e.length||t.append(r("p",{class:"atfb-hint",text:"With none set up, one email goes to the site administrator with every answer in it."})),e.forEach((s,a)=>{t.append(this.section(`notification:${s.id}`,s.name||`Notification ${a+1}`,[y("Name",T(s.name,i=>{s.name=i,this.markDirty()})),this.recipientControl(s),y("Reply to",this.taggableInput(s.replyTo,i=>{s.replyTo=i,this.markDirty()},"Leave empty to reply to you"),"Set this to the visitor’s email address and hitting Reply answers them directly."),y("Subject",this.taggableInput(s.subject,i=>{s.subject=i,this.markDirty()})),y("Message",this.taggableArea(s.message,i=>{s.message=i,this.markDirty()},8)),O("Attach uploaded files",s.attachFiles,i=>{s.attachFiles=i,this.markDirty()}),this.conditionsSection(s.id,"notification",s.logic),$("Delete this notification",()=>{e.splice(a,1),this.markDirty(),this.renderCanvas()},"danger")]))}),r("div",{class:"atfb-pane",children:[r("h2",{text:"Notifications"}),t,$("Add a notification",()=>{e.push({id:this.nextEntryId("n",e),enabled:!0,name:"Notification",to:"{admin_email}",cc:"",bcc:"",replyTo:"",fromName:"",fromEmail:"",subject:"New submission",message:"{all_fields}",attachFiles:!1,logic:{enabled:!1,action:"show",match:"all",rules:[]}}),this.markDirty(),this.renderCanvas()},"primary","plus-alt2")]})}confirmationDetail(e){if(e.type==="message")return r("div",{children:[y("Message",this.taggableArea(e.message,i=>{e.message=i,this.markDirty()},5),"Insert an answer to greet them by name, or show back what they sent."),this.successDesigner(e)]});const t=y("Extra query parameters",this.taggableInput(e.query,i=>{e.query=i,this.markDirty()},"ref={entry:id}&name={field:f1}"),"Added to the address, so the page they land on can read them. Leave empty for none.");if(e.type==="redirect")return r("div",{children:[y("Web address",this.taggableInput(e.url,i=>{e.url=i,this.markDirty()},"https://example.com/thank-you"),"A full address, starting with https://."),t]});const s=r("div",{class:"atfb-pagepicker"}),a=i=>{s.replaceChildren(D(String(e.pageId||0),i,o=>{e.pageId=Number(o)||0,this.markDirty()}))};return a([{value:"0",label:"Loading pages…"}]),F.pages().then(i=>{a([{value:"0",label:"Choose a page…"},...i.map(o=>({value:String(o.id),label:o.title}))])}).catch(()=>{a([{value:"0",label:"Could not load the pages"}])}),y("Page",s,"They are sent to this page after submitting. Its own content is shown, not the form’s message.")}successDesigner(e){e.success=We(e.success);const t=e.success,s=r("div",{class:"atfb-success"}),a=[{key:"plain",label:"Plain",glyph:"¶",blurb:"Just the message."},{key:"simple",label:"Simple",glyph:"✓",blurb:"Check mark and a gentle fade."},{key:"minimal",label:"Minimalistic",glyph:"—",blurb:"Quiet type, generous space."},{key:"card",label:"Card",glyph:"🎫",blurb:"An elevated card that pops in."},{key:"check",label:"Check mark",glyph:"✔",blurb:"A big check draws itself."},{key:"confetti",label:"Confetti",glyph:"🎉",blurb:"Paper rains over the page."},{key:"fireworks",label:"Fireworks",glyph:"🎆",blurb:"The full night-sky show."},{key:"sparkles",label:"Sparkles",glyph:"✨",blurb:"Your emoji floats up around it."},{key:"typewriter",label:"Typewriter",glyph:"⌨",blurb:"Types itself out, letter by letter."}],i=()=>{const o=r("div",{class:"atfb-success__styles",attrs:{role:"radiogroup","aria-label":"Success screen style"},children:a.map(c=>r("button",{class:`atfb-success__style${t.style===c.key?" is-selected":""}`,type:"button",attrs:{role:"radio","aria-checked":t.style===c.key?"true":"false",title:c.blurb},children:[r("span",{class:"atfb-success__style-glyph",text:c.glyph}),r("span",{class:"atfb-success__style-label",text:c.label})],on:{click:()=>{t.style=c.key,this.markDirty(),i(),c.key!=="plain"&&this.previewSuccessScreen(e)}}}))}),l=[];t.style!=="plain"&&(l.push(y("Heading",this.taggableInput(t.title,c=>{t.title=c,this.markDirty()},"Thank you, {field:name}!"),"Shown above the message. Leave empty for none.")),["simple","card","confetti","fireworks","sparkles"].includes(t.style)&&l.push(y("Emoji",T(t.icon,c=>{t.icon=c,this.markDirty()},ve[t.style]||"🎉"),t.style==="sparkles"?"Also the particle that floats up — try 🎈 or ❤️.":"The badge above the heading.")),l.push(this.successAccentRow(t)),["confetti","fireworks","sparkles"].includes(t.style)&&l.push(y("Intensity",D(t.intensity,[{value:"low",label:"Calm"},{value:"medium",label:"Festive"},{value:"high",label:"Over the top"}],c=>{t.intensity=c,this.markDirty()}))),l.push(O("Offer to fill it in again",t.showButton,c=>{t.showButton=c,this.markDirty(),i()})),t.showButton&&l.push(y("Button label",T(t.buttonLabel,c=>{t.buttonLabel=c,this.markDirty()},"Fill it in again")))),s.replaceChildren(r("div",{class:"atfb-success__head",children:[r("h4",{text:"Success screen"}),$("Preview",()=>this.previewSuccessScreen(e),"ghost","controls-play")]}),o,...l)};return i(),s}successAccentRow(e){const t=r("input",{class:"atfb-success__accent",attrs:{type:"color","aria-label":"Accent colour"}});t.value=e.accent||"#3858e9";const s=$("Use the theme accent",()=>{e.accent="",t.value="#3858e9",s.style.display="none",this.markDirty()},"ghost");return s.style.display=e.accent?"":"none",t.addEventListener("input",()=>{e.accent=t.value,s.style.display="",this.markDirty()}),y("Accent",r("div",{class:"atfb-success__accent-row",children:[t,s]}),"Recolours the screen; the theme decides when left alone.")}previewSuccessScreen(e){const t=e.message||"Thank you. Your submission has been received.";let s=()=>{};const a=r("div",{class:"atfb-success-preview",attrs:{role:"dialog","aria-label":"Success screen preview","aria-modal":"true"}}),i=()=>{s(),a.remove(),document.removeEventListener("keydown",o)},o=h=>{h.key==="Escape"&&(h.stopPropagation(),i())},l=Zs(t,e.success,i),c=r("div",{class:"atfb-success-preview__stage atfb-preview atf-form",children:[l]}),d=()=>{s(),s=Ks(l,e.success)};a.append(c,r("div",{class:"atfb-success-preview__bar",children:[$("Replay",d,"secondary","controls-repeat"),$("Close",i,"primary")]})),a.addEventListener("click",h=>{h.target===a&&i()}),document.addEventListener("keydown",o),this.root.append(a),window.requestAnimationFrame(d)}renderConfirmationsPane(){const e=this.schema.confirmations,t=r("div",{class:"atfb-list"});return e.length||t.append(r("p",{class:"atfb-hint",text:"With none set up, the form says thank you and stops."})),e.forEach((s,a)=>{const i=r("div",{class:"atfb-confirm__detail"}),o=()=>{i.replaceChildren(this.confirmationDetail(s))};o(),t.append(this.section(`confirmation:${s.id}`,s.name||`Confirmation ${a+1}`,[y("Name",T(s.name,l=>{s.name=l,this.markDirty()})),y("What happens",D(s.type,[{value:"message",label:"Show a message"},{value:"redirect",label:"Send them to a URL"},{value:"page",label:"Send them to a page"}],l=>{s.type=l,this.markDirty(),o()})),i,this.conditionsSection(s.id,"confirmation",s.logic),$("Delete this confirmation",()=>{e.splice(a,1),this.markDirty(),this.renderCanvas()},"danger")]))}),r("div",{class:"atfb-pane",children:[r("h2",{text:"Confirmations"}),r("p",{class:"atfb-hint",text:"The first one whose conditions match is the one they see."}),t,$("Add a confirmation",()=>{e.push({id:this.nextEntryId("c",e),enabled:!0,name:"Confirmation",type:"message",message:"Thank you. Your submission has been received.",url:"",pageId:0,query:"",success:yt(),logic:{enabled:!1,action:"show",match:"all",rules:[]}}),this.markDirty(),this.renderCanvas()},"primary","plus-alt2")]})}async preview(){await Ot({current:()=>this.form?{id:this.form.id,title:this.form.title,previewUrl:this.form.previewUrl}:null,isDirty:()=>this.dirty,save:()=>this.save(!0)})}}let ae=null,Ke=null;document.addEventListener("alltfo-open-form",n=>{const e=Number(n.detail?.formId??0);e&&ae?.openFormById(e)});function Ve(){if(Ke?.isConnected)return;ae&&(ae.destroy(),ae=null,Ke=null);const n=document.querySelector("[data-atfb-root]:not([data-atfb-mounted])");n&&(n.dataset.atfbMounted="1",Ke=n,Ae(n),ns().then(()=>{n.isConnected&&(ae=new jt(n),ae.start())}))}function Ut(){Ve(),hs()}return us(),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",Ut):Ut(),document.addEventListener("os-window-content-loaded",Ve),ee.Builder=jt,ee.SETTINGS_HANDLED_ELSEWHERE=In,ee.SETTING_CONTROLS=Pt,ee.fieldMove=Wt,ee.mountBuilder=Ve,ee.restatement=Ht,Object.defineProperty(ee,Symbol.toStringTag,{value:"Module"}),ee}({}); +}`,this.canvasTheme.isConnected||this.root.append(this.canvasTheme)}catch{}}}async previewHtml(e,s){if(!this.form||!this.schema)return"";const t=JSON.parse(JSON.stringify(this.schema));t.settings.theme=e,t.settings.themeOverrides=s;const{html:a}=await I.preview(this.form.id,{schema:t,theme:e});return a}renderSettingsPane(){const e=this.schema.settings,s=(t,a)=>{const i=t.split(".");let o=e;for(let l=0;ls("submitLabel",t))),q("Submit without reloading the page",e.ajax,t=>s("ajax",t)),w("Progress indicator",D(e.progressBar,[{value:"steps",label:"Numbered steps"},{value:"bar",label:"A bar"},{value:"none",label:"None"}],t=>s("progressBar",t)),"Only shown on forms with a page break.")]}),r("section",{children:[r("h3",{text:"Who can fill this in"}),q("Only logged-in users",e.requireLogin,t=>s("requireLogin",t)),w("Message for everyone else",T(e.loginMessage,t=>s("loginMessage",t))),w("Open from",r("input",{class:"atfb-input",type:"datetime-local",value:e.schedule.start,on:{input:t=>s("schedule.start",t.target.value)}})),w("Closes",r("input",{class:"atfb-input",type:"datetime-local",value:e.schedule.end,on:{input:t=>s("schedule.end",t.target.value)}})),w("Message when closed",T(e.schedule.message,t=>s("schedule.message",t))),w("Stop after this many submissions",V(String(e.limit.total||""),t=>s("limit.total",Number(t)||0)),"0 means no limit."),w("Submissions per logged-in user",V(String(e.limit.perUser||""),t=>s("limit.perUser",Number(t)||0)))]}),r("section",{children:[r("h3",{text:"Spam"}),r("p",{class:"atfb-hint",text:"No captcha. Nothing here asks the visitor to prove anything."}),q("Honeypot field",e.spam.honeypot,t=>s("spam.honeypot",t)),w("Reject submissions faster than (seconds)",V(String(e.spam.timeTrap),t=>s("spam.timeTrap",Number(t)||0)),"A human cannot fill in a form in under a second. A script can."),w("Submissions allowed per hour, per address",V(String(e.spam.rateLimit),t=>s("spam.rateLimit",Number(t)||0))),w("Blocked words",pe(e.spam.blocklist,t=>s("spam.blocklist",t),4),"One per line."),q("Check submissions with Akismet",e.spam.akismet,t=>s("spam.akismet",t)),r("p",{class:"atfb-hint",text:"Off unless you switch it on. When on, each submission — the answers, plus the sender’s IP address and user agent — is sent to Akismet’s servers for a spam verdict. Needs the Akismet plugin installed and configured."}),q("Ask a simple sum before sending",e.spam.challenge,t=>s("spam.challenge",t)),r("p",{class:"atfb-hint",text:"Only for a form under sustained attack — it is the one check here that asks the visitor to do something. Still kinder than an image captcha: it is answerable by a screen reader, and it hands no data to anyone."})]}),r("section",{children:[r("h3",{text:"Storage and privacy"}),q("Keep entries",e.storage.entries,t=>s("storage.entries",t)),q("Record IP addresses",e.storage.ip,t=>s("storage.ip",t)),q("Anonymise recorded IP addresses",e.storage.anonymise,t=>s("storage.anonymise",t)),w("Delete entries after (days)",V(String(e.storage.retention||""),t=>s("storage.retention",Number(t)||0)),"0 keeps them forever. Anything else deletes automatically, every day.")]}),r("section",{children:[r("h3",{text:"Analytics"}),q("Count views and starts",e.analytics.enabled,t=>s("analytics.enabled",t)),q("Tally device, browser and system",e.analytics.tech,t=>s("analytics.tech",t)),r("p",{class:"atfb-hint",text:"Counters, never people: no cookie, no fingerprint, no per-visitor record — the tallies keep coarse classes like “phone” and “Chrome”, not the visitor’s user-agent string. Nothing here needs a consent banner. Turning the first switch off stops view and start counting entirely."})]}),r("section",{children:[r("h3",{text:"Save and continue later"}),q("Let people save a half-finished form",e.resume.enabled,t=>s("resume.enabled",t)),w("Keep a saved form for (days)",V(String(e.resume.days),t=>s("resume.days",Math.max(1,Number(t)||30)))),r("p",{class:"atfb-hint",text:"The link this creates is the only key to those answers — anyone holding it can read them. For genuinely sensitive questions, require login instead."})]}),r("section",{children:[r("h3",{text:"Archive"}),r("p",{class:"atfb-hint",text:"Retire the form when its moment has passed. It stops accepting responses and leaves every list, and its entries and stats go with it — nothing is deleted, and restoring it brings all of it back exactly as it was."}),C("Archive this form",()=>void this.archiveCurrentForm(),"secondary","archive")]}),r("section",{children:[r("h3",{text:"Delete"}),r("p",{class:"atfb-hint",text:"Deleting is for a form that should never have existed. It moves to the desktop’s Trash, disappears from every list, and any page still carrying its shortcode shows nothing at all. Its entries stay in Entries, and until the Trash is emptied you can bring it back from there. If the form simply had its day, archive it instead."}),C("Delete this form",()=>void this.deleteCurrentForm(),"danger","trash")]})]})}async archiveCurrentForm(){if(!this.form)return;const e=this.form.title||"(untitled)",s=this.forms.find(a=>a.id===this.form.id)?.entries??0;if(await ae(`Archive “${e}”? It stops accepting responses and leaves every list — its ${s} ${s===1?"entry":"entries"} and its stats go with it. Nothing is deleted: restore it any time from “Start a new form”.`,"Archive form"))try{this.dirty&&await this.save(!0),await I.archiveForm(this.form.id),N("Form archived",`${e} — restore it any time from the New form dialog.`),await this.releaseCurrentForm()}catch(a){N("Could not archive the form",a instanceof Error?a.message:"","error")}}async deleteCurrentForm(){if(!this.form)return;const e=this.form.title||"(untitled)",s=this.forms.find(a=>a.id===this.form.id)?.entries??0;if(await ae(`Delete “${e}”? Every page showing it goes blank. ${s?`Its ${s} ${s===1?"entry stays":"entries stay"} in Entries. `:""}It moves to the desktop’s Trash — restore it from there if you change your mind.`,"Delete form"))try{const a=this.form.id;await I.deleteForm(a),window.wp?.os?.announceContentChange?.("alltfo_form","trashed",a,"allterrain-forms"),N("Form deleted",`${e} moved to the desktop’s Trash.`),await this.releaseCurrentForm()}catch(a){N("Could not delete the form",a instanceof Error?a.message:"","error")}}async releaseCurrentForm(){if(this.forms=this.forms.filter(e=>e.id!==this.form.id),this.form=null,this.schema=null,this.selected=null,this.dirty=!1,this.history=[],this.historyAt=-1,this.forms.length){await this.open(this.forms[0].id);return}this.renderBar(),this.renderCanvas(),this.renderInspector()}writeTagValue(e,s,t,a){const i=e==="notification"?this.schema?.notifications.find(o=>o.id===s):this.schema?.confirmations.find(o=>o.id===s);i&&(t==="successTitle"&&"success"in i?i.success.title=a:Object.assign(i,{[t]:a}),this.markDirty())}taggableInput(e,s,t=""){return Be(T(e,s,t),{formId:this.form.id})}taggableArea(e,s,t=6){return Be(pe(e,s,t),{formId:this.form.id})}recipientControl(e){const s=this.schema.fields.filter(c=>c.type==="email"),t=c=>{if(c.trim()==="{admin_email}")return"admin";const d=c.trim().match(/^\{field:([a-z0-9_-]+)\}$/i);return d&&s.some(h=>h.id===d[1])?`field:${d[1]}`:/\{/.test(c)?"custom":"address"},a=[{value:"admin",label:"Whoever runs this site"},...s.map(c=>({value:`field:${c.id}`,label:`The person who filled it in — ${c.label||"their email answer"}`})),{value:"address",label:"A specific email address"},{value:"custom",label:"Something else (advanced)"}],i=t(e.to),o=r("div",{class:"atfb-recipient__detail"}),l=c=>{if(o.replaceChildren(),c==="address"){o.append(T(/\{/.test(e.to)?"":e.to,d=>{this.writeTagValue("notification",e.id,"to",d)},"name@example.com"));return}c==="custom"&&o.append(this.taggableInput(e.to,d=>{this.writeTagValue("notification",e.id,"to",d)},"{admin_email}, sales@example.com"),r("p",{class:"atfb-row__hint",text:"Separate several addresses with commas."}))};return l(i),w("Send it to",r("div",{class:"atfb-recipient",children:[D(i,a,c=>{c==="admin"?e.to="{admin_email}":c.startsWith("field:")?e.to=`{${c}}`:c==="address"&&(e.to=/\{/.test(e.to)?"":e.to),this.markDirty(),l(c)}),o]}),s.length?void 0:"Add an Email question on the Build tab to reply straight back to the visitor.")}renderNotificationsPane(){const e=this.schema.notifications,s=r("div",{class:"atfb-list"});return e.length||s.append(r("p",{class:"atfb-hint",text:"With none set up, one email goes to the site administrator with every answer in it."})),e.forEach((t,a)=>{s.append(this.section(`notification:${t.id}`,t.name||`Notification ${a+1}`,[w("Name",T(t.name,i=>{t.name=i,this.markDirty()})),this.recipientControl(t),w("Reply to",this.taggableInput(t.replyTo,i=>{this.writeTagValue("notification",t.id,"replyTo",i)},"Leave empty to reply to you"),"Set this to the visitor’s email address and hitting Reply answers them directly."),w("Subject",this.taggableInput(t.subject,i=>{this.writeTagValue("notification",t.id,"subject",i)})),w("Message",this.taggableArea(t.message,i=>{this.writeTagValue("notification",t.id,"message",i)},8)),q("Attach uploaded files",t.attachFiles,i=>{t.attachFiles=i,this.markDirty()}),this.conditionsSection(t.id,"notification",t.logic),C("Delete this notification",()=>{e.splice(a,1),this.markDirty(),this.renderCanvas()},"danger")]))}),r("div",{class:"atfb-pane",children:[r("h2",{text:"Notifications"}),s,C("Add a notification",()=>{e.push({id:this.nextEntryId("n",e),enabled:!0,name:"Notification",to:"{admin_email}",cc:"",bcc:"",replyTo:"",fromName:"",fromEmail:"",subject:"New submission",message:"{all_fields}",attachFiles:!1,logic:{enabled:!1,action:"show",match:"all",rules:[]}}),this.markDirty(),this.renderCanvas()},"primary","plus-alt2")]})}confirmationDetail(e){if(e.type==="message")return r("div",{children:[w("Message",this.taggableArea(e.message,i=>{this.writeTagValue("confirmation",e.id,"message",i)},5),"Insert an answer to greet them by name, or show back what they sent."),this.successDesigner(e)]});const s=w("Extra query parameters",this.taggableInput(e.query,i=>{this.writeTagValue("confirmation",e.id,"query",i)},"ref={entry:id}&name={field:f1}"),"Added to the address, so the page they land on can read them. Leave empty for none.");if(e.type==="redirect")return r("div",{children:[w("Web address",this.taggableInput(e.url,i=>{this.writeTagValue("confirmation",e.id,"url",i)},"https://example.com/thank-you"),"A full address, starting with https://."),s]});const t=r("div",{class:"atfb-pagepicker"}),a=i=>{t.replaceChildren(D(String(e.pageId||0),i,o=>{e.pageId=Number(o)||0,this.markDirty()}))};return a([{value:"0",label:"Loading pages…"}]),I.pages().then(i=>{a([{value:"0",label:"Choose a page…"},...i.map(o=>({value:String(o.id),label:o.title}))])}).catch(()=>{a([{value:"0",label:"Could not load the pages"}])}),w("Page",t,"They are sent to this page after submitting. Its own content is shown, not the form’s message.")}successDesigner(e){e.success=Ve(e.success);const s=e.success,t=r("div",{class:"atfb-success"}),a=[{key:"plain",label:"Plain",glyph:"¶",blurb:"Just the message."},{key:"simple",label:"Simple",glyph:"✓",blurb:"Check mark and a gentle fade."},{key:"minimal",label:"Minimalistic",glyph:"—",blurb:"Quiet type, generous space."},{key:"card",label:"Card",glyph:"🎫",blurb:"An elevated card that pops in."},{key:"check",label:"Check mark",glyph:"✔",blurb:"A big check draws itself."},{key:"confetti",label:"Confetti",glyph:"🎉",blurb:"Paper rains over the page."},{key:"fireworks",label:"Fireworks",glyph:"🎆",blurb:"The full night-sky show."},{key:"sparkles",label:"Sparkles",glyph:"✨",blurb:"Your emoji floats up around it."},{key:"typewriter",label:"Typewriter",glyph:"⌨",blurb:"Types itself out, letter by letter."}],i=()=>{const o=r("div",{class:"atfb-success__styles",attrs:{role:"radiogroup","aria-label":"Success screen style"},children:a.map(c=>r("button",{class:`atfb-success__style${s.style===c.key?" is-selected":""}`,type:"button",attrs:{role:"radio","aria-checked":s.style===c.key?"true":"false",title:c.blurb},children:[r("span",{class:"atfb-success__style-glyph",text:c.glyph}),r("span",{class:"atfb-success__style-label",text:c.label})],on:{click:()=>{s.style=c.key,this.markDirty(),i(),c.key!=="plain"&&this.previewSuccessScreen(e)}}}))}),l=[];s.style!=="plain"&&(l.push(w("Heading",this.taggableInput(s.title,c=>{this.writeTagValue("confirmation",e.id,"successTitle",c)},"Thank you, {field:name}!"),"Shown above the message. Leave empty for none.")),["simple","card","confetti","fireworks","sparkles"].includes(s.style)&&l.push(w("Emoji",T(s.icon,c=>{s.icon=c,this.markDirty()},we[s.style]||"🎉"),s.style==="sparkles"?"Also the particle that floats up — try 🎈 or ❤️.":"The badge above the heading.")),l.push(this.successAccentRow(s)),["confetti","fireworks","sparkles"].includes(s.style)&&l.push(w("Intensity",D(s.intensity,[{value:"low",label:"Calm"},{value:"medium",label:"Festive"},{value:"high",label:"Over the top"}],c=>{s.intensity=c,this.markDirty()}))),l.push(q("Offer to fill it in again",s.showButton,c=>{s.showButton=c,this.markDirty(),i()})),s.showButton&&l.push(w("Button label",T(s.buttonLabel,c=>{s.buttonLabel=c,this.markDirty()},"Fill it in again")))),t.replaceChildren(r("div",{class:"atfb-success__head",children:[r("h4",{text:"Success screen"}),C("Preview",()=>this.previewSuccessScreen(e),"ghost","controls-play")]}),o,...l)};return i(),t}successAccentRow(e){const s=r("input",{class:"atfb-success__accent",attrs:{type:"color","aria-label":"Accent colour"}});s.value=e.accent||"#3858e9";const t=C("Use the theme accent",()=>{e.accent="",s.value="#3858e9",t.style.display="none",this.markDirty()},"ghost");return t.style.display=e.accent?"":"none",s.addEventListener("input",()=>{e.accent=s.value,t.style.display="",this.markDirty()}),w("Accent",r("div",{class:"atfb-success__accent-row",children:[s,t]}),"Recolours the screen; the theme decides when left alone.")}previewSuccessScreen(e){const s=e.message||"Thank you. Your submission has been received.";let t=()=>{};const a=r("div",{class:"atfb-success-preview",attrs:{role:"dialog","aria-label":"Success screen preview","aria-modal":"true"}}),i=()=>{t(),a.remove(),document.removeEventListener("keydown",o)},o=h=>{h.key==="Escape"&&(h.stopPropagation(),i())},l=nn(s,e.success,i),c=r("div",{class:"atfb-success-preview__stage atfb-preview atf-form",children:[l]}),d=()=>{t(),t=an(l,e.success)};a.append(c,r("div",{class:"atfb-success-preview__bar",children:[C("Replay",d,"secondary","controls-repeat"),C("Close",i,"primary")]})),a.addEventListener("click",h=>{h.target===a&&i()}),document.addEventListener("keydown",o),this.root.append(a),window.requestAnimationFrame(d)}renderConfirmationsPane(){const e=this.schema.confirmations,s=r("div",{class:"atfb-list"});return e.length||s.append(r("p",{class:"atfb-hint",text:"With none set up, the form says thank you and stops."})),e.forEach((t,a)=>{const i=r("div",{class:"atfb-confirm__detail"}),o=()=>{i.replaceChildren(this.confirmationDetail(t))};o(),s.append(this.section(`confirmation:${t.id}`,t.name||`Confirmation ${a+1}`,[w("Name",T(t.name,l=>{t.name=l,this.markDirty()})),w("What happens",D(t.type,[{value:"message",label:"Show a message"},{value:"redirect",label:"Send them to a URL"},{value:"page",label:"Send them to a page"}],l=>{t.type=l,this.markDirty(),o()})),i,this.conditionsSection(t.id,"confirmation",t.logic),C("Delete this confirmation",()=>{e.splice(a,1),this.markDirty(),this.renderCanvas()},"danger")]))}),r("div",{class:"atfb-pane",children:[r("h2",{text:"Confirmations"}),r("p",{class:"atfb-hint",text:"The first one whose conditions match is the one they see."}),s,C("Add a confirmation",()=>{e.push({id:this.nextEntryId("c",e),enabled:!0,name:"Confirmation",type:"message",message:"Thank you. Your submission has been received.",url:"",pageId:0,query:"",success:kt(),logic:{enabled:!1,action:"show",match:"all",rules:[]}}),this.markDirty(),this.renderCanvas()},"primary","plus-alt2")]})}async preview(){await zt({current:()=>this.form?{id:this.form.id,title:this.form.title,previewUrl:this.form.previewUrl}:null,isDirty:()=>this.dirty,save:()=>this.save(!0)})}}let ne=null,et=null;document.addEventListener("alltfo-open-form",n=>{const e=Number(n.detail?.formId??0);e&&ne?.openFormById(e)});function tt(){if(et?.isConnected)return;ne&&(ne.destroy(),ne=null,et=null);const n=document.querySelector("[data-atfb-root]:not([data-atfb-mounted])");n&&(n.dataset.atfbMounted="1",et=n,Fe(n),us().then(()=>{n.isConnected&&(ne=new Jt(n),ne.start())}))}function Qt(){tt(),ws()}return _s(),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",Qt):Qt(),document.addEventListener("os-window-content-loaded",tt),ee.Builder=Jt,ee.SETTINGS_HANDLED_ELSEWHERE=jn,ee.SETTING_CONTROLS=Kt,ee.fieldMove=Zt,ee.mountBuilder=tt,ee.restatement=Xt,Object.defineProperty(ee,Symbol.toStringTag,{value:"Module"}),ee}({}); diff --git a/assets/js/entries.js b/assets/js/entries.js index b5e1f59..f9ad978 100644 --- a/assets/js/entries.js +++ b/assets/js/entries.js @@ -602,6 +602,7 @@ var allTerrainFormsEntries = function(exports) { for (const option of options) { const item = document.createElement("os-option"); item.setAttribute("value", option.value); + if (option.disabled) item.setAttribute("disabled", ""); item.textContent = option.label; host.append(item); } @@ -619,7 +620,7 @@ var allTerrainFormsEntries = function(exports) { (option) => el("option", { value: option.value, text: option.label, - attrs: { selected: option.value === value } + attrs: { selected: option.value === value, disabled: option.disabled } }) ) }); diff --git a/assets/js/entries.min.js b/assets/js/entries.min.js index c74d348..aa18476 100644 --- a/assets/js/entries.min.js +++ b/assets/js/entries.min.js @@ -1 +1 @@ -var allTerrainFormsEntries=function(I){"use strict";function U(){const e=[window];try{window.parent&&window.parent!==window&&e.push(window.parent)}catch{}return e}function rt(e){const t=e.wp?.os?.windowManager?.getAll?.()??[];for(const s of t){const n=s.iframe??s.element?.querySelector?.("iframe");if(n&&n.contentWindow===window)return s}return null}const it=4e3,ot=100;function at(e,t){const s=Date.now()+it,n=()=>{const r=rt(e);if(r&&r.id!==t){typeof r.close=="function"?r.close():e.wp?.os?.windowManager?.remove?.(r.id);return}!r&&Date.now()0?{form:s}:void 0;s>0&&dt(s);for(const r of U())if(r.wp?.os?.openWindow?.(t,{source:"handoff",params:n})){window.setTimeout(()=>at(r,t),0);return}}function ct(){document.addEventListener("click",e=>{const t=e.target?.closest?.("[data-atf-open-window]");if(!t)return;e.preventDefault();const s=t.getAttribute("data-atf-open-window")??"";for(const n of U())if(n.wp?.os?.openWindow?.(s,{source:"handoff"}))return})}const D="allterrain-forms/requested-form";function dt(e){try{window.sessionStorage.setItem(D,String(e))}catch{}}function Y(e){return`allterrain-forms/requested-form-${e}`}function ut(e){try{const t=window.sessionStorage.getItem(Y(e));return window.sessionStorage.removeItem(Y(e)),Number(t)||0}catch{return 0}}function ft(){try{const e=window.sessionStorage.getItem(D);return window.sessionStorage.removeItem(D),Number(e)||0}catch{return 0}}const ht=4,mt=500;class pt{constructor(){this.targets=[],this.active=null,this.lastEndMs=0}start(t){if(this.active||t.origin.button!==0)return null;const{payload:s,origin:n}=t,r=n.clientX,i=n.clientY;let a=!1,l=!1,d=null,p=null,Q=0,Z=0;const N=()=>{document.removeEventListener("pointermove",et),document.removeEventListener("pointerup",st),document.removeEventListener("pointercancel",A),document.removeEventListener("keydown",nt),window.removeEventListener("blur",A),d?.remove(),d=null,s.source.classList.remove("atf-is-dragging"),document.body.classList.remove("atf-drag-active"),p?.onLeave?.(v),p=null,this.active=null,this.lastEndMs=Date.now()},S=(c,u)=>{document.dispatchEvent(new CustomEvent(c,{detail:{payload:s,clientX:u?.clientX,clientY:u?.clientY}}))},v={payload:s,isFinished:()=>l,cancel:(c="caller")=>{l||(l=!0,N(),a&&S("os.drag.end"),t.onCancel?.(c))}},Ft=c=>{a=!0,s.source.classList.add("atf-is-dragging"),document.body.classList.add("atf-drag-active");const u=s.source.getBoundingClientRect();Q=s.ghost?.offsetX??r-u.left,Z=s.ghost?.offsetY??i-u.top,d=s.ghost?.element??s.source.cloneNode(!0),d.classList.add("atf-drag-ghost"),d.style.width=`${u.width}px`,document.body.appendChild(d),tt(c),S("os.drag.start",c)},tt=c=>{d&&(d.style.transform=`translate3d(${c.clientX-Q}px, ${c.clientY-Z}px, 0)`)},et=c=>{if(l)return;if(!a){if(Math.hypot(c.clientX-r,c.clientY-i){if(l)return;if(!a){l=!0,N(),t.onClickOnly?.();return}const u=p;if(l=!0,N(),u&&u.accept(s)){t.onCommit?.(u),u.onDrop(v,{clientX:c.clientX,clientY:c.clientY}),S("os.drag.end",c);return}S("os.drag.end",c),t.onCancel?.(u?"rejected":"no-target")},A=()=>v.cancel("pointercancel"),nt=c=>{c.key==="Escape"&&v.cancel("escape")};return document.addEventListener("pointermove",et),document.addEventListener("pointerup",st),document.addEventListener("pointercancel",A),document.addEventListener("keydown",nt),window.addEventListener("blur",A),this.active=v,v}registerDropTarget(t){return this.targets=this.targets.filter(s=>s.id!==t.id),this.targets.push(t),()=>{this.targets=this.targets.filter(s=>s.id!==t.id)}}isDragging(){return this.active!==null}recentlyEndedDrag(t=mt){return Date.now()-this.lastEndMsa.right||sa.bottom)continue;const l=wt(i.element);if(l>r){n=i,r=l;continue}l===r&&n&>(i.element,n.element)&&(n=i)}return n}}function wt(e){let t=0,s=e;for(;s;)t++,s=s.parentElement;return t}function gt(e,t){return(t.compareDocumentPosition(e)&Node.DOCUMENT_POSITION_FOLLOWING)!==0}function C(){return window.wp?.os??null}let O=null;function j(){const e=C();return e?.dragManager?e.dragManager:(O||(O=new pt),O)}function vt(e){const t=i=>{const a=i.detail?.payload;return a&&e.includes(a.type)?a.source:null},s=i=>i.preventDefault(),n=i=>{const a=t(i);a&&(a.classList.add("atf-is-dragging"),document.body.classList.add("atf-drag-active"),window.getSelection()?.removeAllRanges(),document.addEventListener("selectstart",s))},r=i=>{const a=t(i);a&&(a.classList.remove("atf-is-dragging"),document.body.classList.remove("atf-drag-active"),document.removeEventListener("selectstart",s))};return document.addEventListener("os.drag.start",n),document.addEventListener("os.drag.end",r),()=>{document.removeEventListener("os.drag.start",n),document.removeEventListener("os.drag.end",r),document.removeEventListener("selectstart",s)}}const bt=["atfb","atfe","atfs","atfm"];function yt(e,t,s,n,r){const i=t.getBoundingClientRect();if(!r){const a=bt.find(l=>t.closest(`.${l}`));if(a){const l=t.cloneNode(!0);l.style.transition="",l.style.transform="",r=document.createElement("div"),r.className=`${a} atf-ghost-scope`,r.appendChild(l)}}return r&&(r.style.width=`${Math.round(i.width)}px`,r.style.maxWidth=`${Math.round(i.width)}px`,r.style.boxSizing="border-box"),{type:e,source:t,data:s,ghost:{element:r,offsetX:n.clientX-i.left,offsetY:n.clientY-i.top,hint:{neutral:"",accept:"",reject:"",hidden:!0}}}}const h=window.allTerrainForms;class L extends Error{constructor(t,s,n=""){super(t),this.name="ApiError",this.status=s,this.code=n}}function B(e,t){return e.includes("?")?`${e}${t.replace("?","&")}`:`${e}${t}`}async function M(e,t={}){if(!h?.restUrl)throw new L("AllTerrain Forms is not configured on this page.",0);const s=B(h.restUrl,e),n={"Content-Type":"application/json",...t.headers??{}};h.nonce&&(n["X-WP-Nonce"]=h.nonce);const r=C(),a=await(r?.fetch?(l,d)=>r.fetch(l,d,{source:"allterrain-forms"}):(l,d)=>fetch(l,d))(s,{credentials:"same-origin",...t,headers:n});if(!a.ok){let l=`Request failed with status ${a.status}.`,d="";try{const p=await a.json();l=p.message??l,d=p.code??""}catch{}throw new L(l,a.status,d)}if(a.status!==204)return await a.json()}const f=e=>M(e);async function Et(e){if(!h?.wpRestUrl)throw new L("AllTerrain Forms is not configured on this page.",0);const t=h.nonce?{"X-WP-Nonce":h.nonce}:{},s=C(),n=B(h.wpRestUrl,e),r=s?.fetch?await s.fetch(n,{credentials:"same-origin",headers:t},{source:"allterrain-forms"}):await fetch(n,{credentials:"same-origin",headers:t});if(!r.ok)throw new L(`Request failed with status ${r.status}.`,r.status);return await r.json()}function St(e){if(!e||!e.includes("&"))return e;const t=document.createElement("textarea");return t.innerHTML=e,t.value}const w=(e,t)=>M(e,{method:"POST",body:JSON.stringify(t)}),$=e=>M(e,{method:"DELETE"});function W(e){const t=new URLSearchParams;for(const[n,r]of Object.entries(e))r===void 0||r===""||r===!1||t.set(n,String(r));const s=t.toString();return s?`?${s}`:""}function x(e){const t=e?.schema?.settings;return t&&(!t.themeOverrides||Array.isArray(t.themeOverrides))&&(t.themeOverrides={...t.themeOverrides}),e}const g={config:()=>f("/config"),mailpoet:()=>f("/mailpoet"),listForms:()=>f("/forms"),listArchivedForms:()=>f("/forms?archived=1"),archiveForm:e=>w(`/forms/${e}/archive`,{}),unarchiveForm:e=>w(`/forms/${e}/unarchive`,{}),getForm:e=>f(`/forms/${e}`).then(x),createForm:e=>w("/forms",e).then(x),updateForm:(e,t)=>w(`/forms/${e}`,t).then(x),duplicateForm:e=>w(`/forms/${e}/duplicate`,{}).then(x),deleteForm:e=>$(`/forms/${e}`),preview:(e,t)=>w(`/forms/${e}/preview`,t),pages:()=>Et("wp/v2/pages?per_page=100&status=publish&orderby=title&order=asc&_fields=id,title").then(e=>e.map(t=>({id:t.id,title:St(t.title?.rendered??"")||`#${t.id}`}))),mergeTags:e=>f(`/forms/${e}/merge-tags`).then(t=>t.groups),analytics:(e,t="")=>f(`/forms/${e}/analytics${t?`?dimension=${encodeURIComponent(t)}`:""}`),demoStatus:()=>f("/demo"),demoSeed:e=>w("/demo",e?{count:e}:{}),demoRemove:()=>$("/demo"),listEntries:e=>f(`/entries${W(e)}`),getEntry:e=>f(`/entries/${e}`),updateEntry:(e,t)=>w(`/entries/${e}`,t),deleteEntry:e=>$(`/entries/${e}`),exportEntries:e=>f(`/entries/export${W(e)}`),listThemes:()=>f("/themes"),saveTheme:e=>w("/themes",e),deleteTheme:e=>$(`/themes/${e}`)};function o(e,t={}){const s=document.createElement(e);t.class&&(s.className=t.class),t.text!==void 0&&(s.textContent=t.text),t.html!==void 0&&(s.innerHTML=t.html),t.title&&(s.title=t.title),t.type&&"type"in s&&(s.type=t.type),t.value!==void 0&&"value"in s&&(s.value=t.value),t.placeholder!==void 0&&"placeholder"in s&&(s.placeholder=t.placeholder),t.href&&"href"in s&&(s.href=t.href);for(const[n,r]of Object.entries(t.attrs??{}))r===void 0||r===!1||s.setAttribute(n,r===!0?"":String(r));Object.assign(s.style,t.style??{});for(const[n,r]of Object.entries(t.on??{}))s.addEventListener(n,r);for(const n of t.children??[])n==null||n===!1||s.append(n);return s}function b(e){e.replaceChildren()}function q(e){return o("span",{class:`dashicons ${e.startsWith("dashicons-")?e:`dashicons-${e}`}`,attrs:{"aria-hidden":"true"}})}function Lt(e,t,s=""){return o("input",{class:"atfb-input",type:"text",value:e,placeholder:s,on:{input:n=>t(n.target.value)}})}function X(e,t,s){if(k("os-select")&&k("os-option")){const n=document.createElement("os-select");n.setAttribute("value",e),n.classList.add("atfb-field");for(const r of t){const i=document.createElement("os-option");i.setAttribute("value",r.value),i.textContent=r.label,n.append(i)}return n.addEventListener("os-pick",r=>{s(String(r.detail?.value??""))}),n}return o("select",{class:"atfb-input atfb-select",on:{change:n=>s(n.target.value)},children:t.map(n=>o("option",{value:n.value,text:n.label,attrs:{selected:n.value===e}}))})}function $t(e,t,s){if(k("os-checkbox-label")){const r=document.createElement("os-checkbox-label");return r.setAttribute("label",e),r.classList.add("atfb-check"),t&&r.setAttribute("checked",""),r.addEventListener("os-checkbox-change",i=>{s(!!i.detail?.checked)}),r}const n=o("input",{type:"checkbox",on:{change:r=>s(r.target.checked)}});return n.checked=t,o("label",{class:"atfb-check",children:[n,o("span",{text:e})]})}function m(e,t,s="secondary",n){const r=[n?q(n):null,o("span",{text:e})];if(k("os-button")){const i=document.createElement("os-button");i.setAttribute("variant",s),i.setAttribute("type","button"),i.classList.add("atfb-button",`atfb-button--${s}`),i.addEventListener("click",t);for(const a of r)a&&i.append(a);return i}return o("button",{class:`atfb-button atfb-button--${s}`,type:"button",on:{click:t},children:r})}function k(e){return typeof customElements<"u"&&!!customElements.get(e)}const xt=["os-button","os-checkbox-label","os-number-field","os-select","os-option","os-segmented","os-segment","os-color-field","os-empty-state"];let _=null;function kt(){if(_)return _;const e=window.wp?.os;return _=e?.loadComponents?e.loadComponents(xt).catch(()=>{}):Promise.resolve(),_}async function H(e,t=""){const s=window.wp?.os;return s?.confirm?s.confirm({title:t,message:e,danger:!0}):window.confirm(e)}function E(e,t="",s="info"){const n=window.wp?.os;if(n?.notify){n.notify({title:e,body:t,type:s});return}console.info(`[AllTerrain Forms] ${e}${t?`: ${t}`:""}`)}function _t(e,t){let s=0;return(...n)=>{window.clearTimeout(s),s=window.setTimeout(()=>e(...n),t)}}function Tt(e){const t=e.closest(".os-window__body");t&&(t.addEventListener("scroll",()=>{t.scrollTop&&(t.scrollTop=0),t.scrollLeft&&(t.scrollLeft=0)},{passive:!0}),t.scrollTop=0,t.scrollLeft=0)}const At="allterrain-forms/form",It="allterrain-forms/entry";function Dt(){return window.wp?.os?.relations??null}function Ct(e){const t=e.closest("[data-window-id], .os-window");if(!t)return null;const s=t.getAttribute("data-window-id");if(s)return s;const n=t.id??"";return n?n.replace(/^wp-window-/,""):null}const Ot=6e3,z=120;function G(e,t){const s=Dt();if(R.set(e,t),!s?.set)return;const n=i=>{if(T.get(e)!==r)return;const a=Ct(e);if(!a){Date.now()n(i),z);return}try{s.set(a,t)}catch(d){K||(K=!0,console.error("[AllTerrain Forms] The shell refused a window identity.",d,t)),T.delete(e);return}if(!t||s.get?.(a)?.id===t.id||Date.now()>=i){T.delete(e);return}window.setTimeout(()=>n(i),z)},r=Symbol("atf-identity");T.set(e,r),n(Date.now()+Ot)}let K=!1;const T=new WeakMap,R=new Map;function Mt(){for(const[e,t]of R){if(!e.isConnected){R.delete(e);continue}G(e,t)}}if(typeof document<"u")for(const e of["os-window-content-loaded","os-window-opened"])document.addEventListener(e,()=>Mt());function qt(e,t){const s=[];e.userId&&s.push({type:"user",id:e.userId,rel:"references"});for(const r of e.fields)if(!(r.type!=="file"||!Array.isArray(r.value)))for(const i of r.value){const a=Number(i);a>0&&s.push({type:"media",id:a,rel:"child"})}const n=[{id:`allterrain-forms/form-${e.formId}`,label:e.formTitle||"The form",url:`${t}admin.php?page=allterrain-forms&form=${e.formId}`,group:"allterrain-forms",groupLabel:"Forms",icon:"dashicons-feedback"}];return e.userId&&n.push({id:`allterrain-forms/user-${e.userId}`,label:"Who submitted it",url:`${t}user-edit.php?user_id=${e.userId}`,group:"users",groupLabel:"People",icon:"dashicons-admin-users"}),{type:It,id:e.id,root:{type:At,id:e.formId},label:e.title||`Entry #${e.id}`,links:s.slice(0,32),related:n}}const J="allterrain-forms/entry";class Rt{constructor(t){this.forms=[],this.entries=[],this.selected=null,this.selection=new Set,this.paintedSelectionId=-1,this.loadSeq=0,this.selectSeq=0,this.formId=0,this.status="inbox",this.search="",this.starred=!1,this.page=1,this.pages=1,this.total=0,this.teardowns=[],this.root=t,this.bar=t.querySelector("[data-atfe-bar]")??o("div"),this.list=t.querySelector("[data-atfe-list]")??o("div"),this.detail=t.querySelector("[data-atfe-detail]")??o("div")}async start(){this.teardowns.push(vt([J]));try{this.forms=await g.listForms()}catch(n){b(this.bar),this.bar.append(o("p",{class:"atfb-error",text:n instanceof Error?n.message:"Could not load forms."}));return}const t=ut("entries")||ft();this.formId=(t&&this.forms.some(n=>n.id===t)?t:0)||(this.forms.find(n=>n.unread>0)??this.forms[0])?.id||0,this.renderBar(),await this.load();const s=window.wp?.os;s?.subscribe&&this.teardowns.push(s.subscribe("os.alltfo_entry.changed",()=>{if(!this.root.isConnected){this.destroy();return}this.load()}))}destroy(){this.teardowns.forEach(t=>t()),this.teardowns=[]}renderBar(){b(this.bar);const t=Lt(this.search,_t(s=>{this.search=s,this.page=1,this.load()},300),"Search answers");t.type="search",t.setAttribute("aria-label","Search entries"),this.bar.append(X(String(this.formId),this.forms.map(s=>({value:String(s.id),label:s.unread?`${s.title} (${s.unread})`:s.title})),s=>{this.formId=Number(s),this.page=1,this.selection.clear(),this.selected=null,this.load()}),X(this.status,[{value:"inbox",label:"All"},{value:"alltfo-unread",label:"Unread"},{value:"alltfo-read",label:"Read"},{value:"alltfo-spam",label:"Spam"}],s=>{this.status=s,this.page=1,this.load()}),t,$t("Starred only",this.starred,s=>{this.starred=s,this.page=1,this.load()}),o("span",{class:"atfe__count",attrs:{role:"status"},text:""}),m("Export CSV",()=>void this.exportEntries("csv"),"secondary","download"),m("JSON",()=>void this.exportEntries("json"),"secondary"))}async showForm(t){this.formId=t,this.page=1,this.selection.clear(),this.selected=null,this.renderBar(),await this.load()}async load(){if(!this.formId){b(this.list),this.list.append(o("p",{class:"atfb-hint",text:"No forms yet."}));return}const t=++this.loadSeq;try{const s=await g.listEntries({form_id:this.formId,status:this.status==="inbox"?void 0:this.status,search:this.search,page:this.page,starred:this.starred});if(t!==this.loadSeq)return;if(this.page>1&&this.page>s.pages)return this.page=Math.max(1,s.pages),this.load();this.entries=s.entries,this.total=s.total,this.pages=s.pages,this.renderList(),this.reconcileSelection();const n=this.bar.querySelector(".atfe__count");n&&(n.textContent=`${this.total} ${this.total===1?"entry":"entries"}`)}catch(s){if(t!==this.loadSeq)return;b(this.list),this.list.append(o("p",{class:"atfb-error",text:s instanceof Error?s.message:"Could not load entries."}))}}reconcileSelection(){this.selected&&!this.entries.some(t=>t.id===this.selected?.id)&&(this.selected=null),this.paintedSelectionId!==(this.selected?.id??0)&&this.renderDetail()}renderList(){if(b(this.list),!this.entries.length){this.list.append(o("div",{class:"atfb-placeholder",children:[o("p",{text:this.search?"Nothing matches that.":"No entries yet."})]}));return}const t=new Set(this.entries.map(n=>n.id));for(const n of Array.from(this.selection))t.has(n)||this.selection.delete(n);this.list.append(this.renderBulkBar());const s=o("div",{class:"atfe__rows",attrs:{role:"list"}});for(const n of this.entries)s.append(this.renderRow(n));this.list.append(s),this.pages>1&&this.list.append(o("div",{class:"atfe__pager",children:[m("Previous",()=>{this.page=Math.max(1,this.page-1),this.load()}),o("span",{text:`Page ${this.page} of ${this.pages}`}),m("Next",()=>{this.page=Math.min(this.pages,this.page+1),this.load()})]}))}renderBulkBar(){const t=this.selection.size,s=o("input",{type:"checkbox",attrs:{"aria-label":"Select every entry on this page"},on:{change:n=>{const r=n.target.checked;this.entries.forEach(i=>{r?this.selection.add(i.id):this.selection.delete(i.id)}),this.renderList()}}});return s.checked=t>0&&t===this.entries.length,s.indeterminate=t>0&&tvoid this.bulk("alltfo-read")):null,t?m("Unread",()=>void this.bulk("alltfo-unread")):null,t?m("Spam",()=>void this.bulk("alltfo-spam")):null,t&&h?.canEdit?m("Delete",()=>void this.bulkDelete(),"danger"):null]})}async bulk(t){const s=Array.from(this.selection);for(const n of s)try{await g.updateEntry(n,{status:t})}catch{}this.selection.clear(),await this.load()}async bulkDelete(){const t=Array.from(this.selection);if(await H(`Delete ${t.length} ${t.length===1?"entry":"entries"} and any files uploaded with them? It cannot be undone.`,"Delete entries")){for(const n of t)try{await g.deleteEntry(n)}catch{}this.selection.clear(),this.selected=null,await this.load(),this.renderDetail()}}renderRow(t){const s=t.status==="alltfo-unread",n=o("div",{class:`atfe__row${s?" is-unread":""}${this.selected?.id===t.id?" is-selected":""}`,attrs:{role:"listitem",tabindex:"0","data-entry":t.id},children:[o("input",{class:"atfe__select",type:"checkbox",attrs:{"aria-label":`Select ${t.title}`,checked:this.selection.has(t.id)},on:{click:r=>r.stopPropagation(),change:r=>{r.target.checked?this.selection.add(t.id):this.selection.delete(t.id),this.list.querySelector(".atfe__bulk")?.replaceWith(this.renderBulkBar())}}}),o("button",{class:`atfe__star${t.starred?" is-on":""}`,type:"button",attrs:{"aria-label":t.starred?"Unstar this entry":"Star this entry"},on:{click:r=>{r.stopPropagation(),this.toggleStar(t)}},children:[q("star-filled")]}),o("div",{class:"atfe__row-body",children:[o("strong",{text:t.title}),o("span",{class:"atfe__row-meta",text:t.dateHuman})]}),t.notes?o("span",{class:"atfb-badge",text:`💬 ${t.notes}`}):null,t.spam?o("span",{class:"atfb-badge atfb-badge--spam",text:"spam"}):null]});return n.addEventListener("click",()=>{j().recentlyEndedDrag()||this.select(t)}),n.addEventListener("keydown",r=>{(r.key==="Enter"||r.key===" ")&&(r.preventDefault(),this.select(t))}),n.addEventListener("pointerdown",r=>{if(r.target.closest("button"))return;const i=o("div",{class:"atfe__ghost",children:[q("feedback"),o("span",{text:t.title})]});j().start({payload:yt(J,n,{entry:t,formId:t.formId,formTitle:t.formTitle},r,i),origin:r})}),n}async select(t){const s=++this.selectSeq;try{const n=await g.getEntry(t.id);if(s!==this.selectSeq)return;this.selected=n;const r=this.entries.find(i=>i.id===t.id);r&&(r.status=this.selected.status),this.renderList(),this.renderDetail()}catch(n){if(s!==this.selectSeq)return;E("Could not open that entry",n instanceof Error?n.message:"","error")}}renderDetail(){this.paintedSelectionId=this.selected?.id??0,G(this.root,this.selected?qt(this.selected,h?.adminUrl??""):null),b(this.detail);const t=this.selected;if(!t){this.detail.append(o("div",{class:"atfb-placeholder",children:[o("p",{text:"Pick an entry to read it."})]}));return}const s=o("dl",{class:"atfe__answers"});for(const n of t.fields)n.formatted&&s.append(o("dt",{text:n.label||n.id}),o("dd",{text:n.formatted}));this.detail.append(o("div",{class:"atfe__detail",children:[o("div",{class:"atfe__detail-head",children:[o("h2",{text:t.title}),o("p",{class:"atfb-hint",text:`${t.formTitle} — ${t.dateHuman}`})]}),s,t.quiz?o("p",{class:"atfe__quiz",text:`Score: ${t.quiz.score} of ${t.quiz.total} (${t.quiz.percent}%) — ${t.quiz.passed?"passed":"not passed"}`}):null,o("details",{class:"atfb-section",children:[o("summary",{text:"Where it came from"}),o("p",{class:"atfb-hint",text:t.ip?`IP: ${t.ip}`:"No IP recorded."}),t.referrer?o("p",{class:"atfb-hint",text:`Referrer: ${t.referrer}`}):null,t.userAgent?o("p",{class:"atfb-hint",text:t.userAgent}):null]}),o("div",{class:"atfe__actions",children:[m(t.status==="alltfo-spam"?"Not spam":"Mark as spam",()=>void this.setStatus(t,t.status==="alltfo-spam"?"alltfo-read":"alltfo-spam")),m(t.status==="alltfo-unread"?"Mark read":"Mark unread",()=>void this.setStatus(t,t.status==="alltfo-unread"?"alltfo-read":"alltfo-unread")),t.canDelete?m("Delete",()=>void this.remove(t),"danger"):null]})]}))}async toggleStar(t){try{const s=await g.updateEntry(t.id,{starred:!t.starred});t.starred=s.starred,this.renderList()}catch(s){E("Could not update that entry",s instanceof Error?s.message:"","error")}}async setStatus(t,s){try{const n=await g.updateEntry(t.id,{status:s});this.selected=n,await this.load(),this.renderDetail()}catch(n){E("Could not update that entry",n instanceof Error?n.message:"","error")}}async remove(t){if(await H("Delete this entry and any files uploaded with it? It cannot be undone.","Delete entry"))try{await g.deleteEntry(t.id),this.selected=null,await this.load(),this.renderDetail()}catch(s){E("Could not delete that entry",s instanceof Error?s.message:"","error")}}async exportEntries(t){try{const{filename:s,csv:n}=await g.exportEntries({form_id:this.formId,search:this.search,starred:this.starred,format:t}),r=new Blob([n],{type:t==="json"?"application/json":"text/csv;charset=utf-8"}),i=URL.createObjectURL(r),a=o("a",{href:i,attrs:{download:s}});document.body.append(a),a.click(),a.remove(),window.setTimeout(()=>URL.revokeObjectURL(i),1e3)}catch(s){E("Could not export",s instanceof Error?s.message:"","error")}}}let y=null;document.addEventListener("alltfo-open-entries-form",e=>{const t=Number(e.detail?.formId??0);t&&y?.showForm(t)});let F=null;function P(){if(F?.isConnected)return;y&&(y.destroy(),y=null,F=null);const e=document.querySelector("[data-atfe-root]:not([data-atfe-mounted])");!e||!h?.canRead||(e.dataset.atfeMounted="1",F=e,Tt(e),kt().then(()=>{e.isConnected&&(y=new Rt(e),y.start())}))}function V(){P(),lt()}return ct(),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",V):V(),document.addEventListener("os-window-content-loaded",P),I.mountEntries=P,Object.defineProperty(I,Symbol.toStringTag,{value:"Module"}),I}({}); +var allTerrainFormsEntries=function(I){"use strict";function U(){const e=[window];try{window.parent&&window.parent!==window&&e.push(window.parent)}catch{}return e}function rt(e){const t=e.wp?.os?.windowManager?.getAll?.()??[];for(const s of t){const n=s.iframe??s.element?.querySelector?.("iframe");if(n&&n.contentWindow===window)return s}return null}const it=4e3,ot=100;function at(e,t){const s=Date.now()+it,n=()=>{const r=rt(e);if(r&&r.id!==t){typeof r.close=="function"?r.close():e.wp?.os?.windowManager?.remove?.(r.id);return}!r&&Date.now()0?{form:s}:void 0;s>0&&dt(s);for(const r of U())if(r.wp?.os?.openWindow?.(t,{source:"handoff",params:n})){window.setTimeout(()=>at(r,t),0);return}}function ct(){document.addEventListener("click",e=>{const t=e.target?.closest?.("[data-atf-open-window]");if(!t)return;e.preventDefault();const s=t.getAttribute("data-atf-open-window")??"";for(const n of U())if(n.wp?.os?.openWindow?.(s,{source:"handoff"}))return})}const D="allterrain-forms/requested-form";function dt(e){try{window.sessionStorage.setItem(D,String(e))}catch{}}function Y(e){return`allterrain-forms/requested-form-${e}`}function ut(e){try{const t=window.sessionStorage.getItem(Y(e));return window.sessionStorage.removeItem(Y(e)),Number(t)||0}catch{return 0}}function ft(){try{const e=window.sessionStorage.getItem(D);return window.sessionStorage.removeItem(D),Number(e)||0}catch{return 0}}const ht=4,mt=500;class pt{constructor(){this.targets=[],this.active=null,this.lastEndMs=0}start(t){if(this.active||t.origin.button!==0)return null;const{payload:s,origin:n}=t,r=n.clientX,i=n.clientY;let a=!1,l=!1,d=null,p=null,Q=0,Z=0;const N=()=>{document.removeEventListener("pointermove",et),document.removeEventListener("pointerup",st),document.removeEventListener("pointercancel",A),document.removeEventListener("keydown",nt),window.removeEventListener("blur",A),d?.remove(),d=null,s.source.classList.remove("atf-is-dragging"),document.body.classList.remove("atf-drag-active"),p?.onLeave?.(b),p=null,this.active=null,this.lastEndMs=Date.now()},S=(c,u)=>{document.dispatchEvent(new CustomEvent(c,{detail:{payload:s,clientX:u?.clientX,clientY:u?.clientY}}))},b={payload:s,isFinished:()=>l,cancel:(c="caller")=>{l||(l=!0,N(),a&&S("os.drag.end"),t.onCancel?.(c))}},Ft=c=>{a=!0,s.source.classList.add("atf-is-dragging"),document.body.classList.add("atf-drag-active");const u=s.source.getBoundingClientRect();Q=s.ghost?.offsetX??r-u.left,Z=s.ghost?.offsetY??i-u.top,d=s.ghost?.element??s.source.cloneNode(!0),d.classList.add("atf-drag-ghost"),d.style.width=`${u.width}px`,document.body.appendChild(d),tt(c),S("os.drag.start",c)},tt=c=>{d&&(d.style.transform=`translate3d(${c.clientX-Q}px, ${c.clientY-Z}px, 0)`)},et=c=>{if(l)return;if(!a){if(Math.hypot(c.clientX-r,c.clientY-i){if(l)return;if(!a){l=!0,N(),t.onClickOnly?.();return}const u=p;if(l=!0,N(),u&&u.accept(s)){t.onCommit?.(u),u.onDrop(b,{clientX:c.clientX,clientY:c.clientY}),S("os.drag.end",c);return}S("os.drag.end",c),t.onCancel?.(u?"rejected":"no-target")},A=()=>b.cancel("pointercancel"),nt=c=>{c.key==="Escape"&&b.cancel("escape")};return document.addEventListener("pointermove",et),document.addEventListener("pointerup",st),document.addEventListener("pointercancel",A),document.addEventListener("keydown",nt),window.addEventListener("blur",A),this.active=b,b}registerDropTarget(t){return this.targets=this.targets.filter(s=>s.id!==t.id),this.targets.push(t),()=>{this.targets=this.targets.filter(s=>s.id!==t.id)}}isDragging(){return this.active!==null}recentlyEndedDrag(t=mt){return Date.now()-this.lastEndMsa.right||sa.bottom)continue;const l=wt(i.element);if(l>r){n=i,r=l;continue}l===r&&n&>(i.element,n.element)&&(n=i)}return n}}function wt(e){let t=0,s=e;for(;s;)t++,s=s.parentElement;return t}function gt(e,t){return(t.compareDocumentPosition(e)&Node.DOCUMENT_POSITION_FOLLOWING)!==0}function C(){return window.wp?.os??null}let O=null;function j(){const e=C();return e?.dragManager?e.dragManager:(O||(O=new pt),O)}function bt(e){const t=i=>{const a=i.detail?.payload;return a&&e.includes(a.type)?a.source:null},s=i=>i.preventDefault(),n=i=>{const a=t(i);a&&(a.classList.add("atf-is-dragging"),document.body.classList.add("atf-drag-active"),window.getSelection()?.removeAllRanges(),document.addEventListener("selectstart",s))},r=i=>{const a=t(i);a&&(a.classList.remove("atf-is-dragging"),document.body.classList.remove("atf-drag-active"),document.removeEventListener("selectstart",s))};return document.addEventListener("os.drag.start",n),document.addEventListener("os.drag.end",r),()=>{document.removeEventListener("os.drag.start",n),document.removeEventListener("os.drag.end",r),document.removeEventListener("selectstart",s)}}const vt=["atfb","atfe","atfs","atfm"];function yt(e,t,s,n,r){const i=t.getBoundingClientRect();if(!r){const a=vt.find(l=>t.closest(`.${l}`));if(a){const l=t.cloneNode(!0);l.style.transition="",l.style.transform="",r=document.createElement("div"),r.className=`${a} atf-ghost-scope`,r.appendChild(l)}}return r&&(r.style.width=`${Math.round(i.width)}px`,r.style.maxWidth=`${Math.round(i.width)}px`,r.style.boxSizing="border-box"),{type:e,source:t,data:s,ghost:{element:r,offsetX:n.clientX-i.left,offsetY:n.clientY-i.top,hint:{neutral:"",accept:"",reject:"",hidden:!0}}}}const h=window.allTerrainForms;class L extends Error{constructor(t,s,n=""){super(t),this.name="ApiError",this.status=s,this.code=n}}function B(e,t){return e.includes("?")?`${e}${t.replace("?","&")}`:`${e}${t}`}async function M(e,t={}){if(!h?.restUrl)throw new L("AllTerrain Forms is not configured on this page.",0);const s=B(h.restUrl,e),n={"Content-Type":"application/json",...t.headers??{}};h.nonce&&(n["X-WP-Nonce"]=h.nonce);const r=C(),a=await(r?.fetch?(l,d)=>r.fetch(l,d,{source:"allterrain-forms"}):(l,d)=>fetch(l,d))(s,{credentials:"same-origin",...t,headers:n});if(!a.ok){let l=`Request failed with status ${a.status}.`,d="";try{const p=await a.json();l=p.message??l,d=p.code??""}catch{}throw new L(l,a.status,d)}if(a.status!==204)return await a.json()}const f=e=>M(e);async function Et(e){if(!h?.wpRestUrl)throw new L("AllTerrain Forms is not configured on this page.",0);const t=h.nonce?{"X-WP-Nonce":h.nonce}:{},s=C(),n=B(h.wpRestUrl,e),r=s?.fetch?await s.fetch(n,{credentials:"same-origin",headers:t},{source:"allterrain-forms"}):await fetch(n,{credentials:"same-origin",headers:t});if(!r.ok)throw new L(`Request failed with status ${r.status}.`,r.status);return await r.json()}function St(e){if(!e||!e.includes("&"))return e;const t=document.createElement("textarea");return t.innerHTML=e,t.value}const w=(e,t)=>M(e,{method:"POST",body:JSON.stringify(t)}),$=e=>M(e,{method:"DELETE"});function W(e){const t=new URLSearchParams;for(const[n,r]of Object.entries(e))r===void 0||r===""||r===!1||t.set(n,String(r));const s=t.toString();return s?`?${s}`:""}function x(e){const t=e?.schema?.settings;return t&&(!t.themeOverrides||Array.isArray(t.themeOverrides))&&(t.themeOverrides={...t.themeOverrides}),e}const g={config:()=>f("/config"),mailpoet:()=>f("/mailpoet"),listForms:()=>f("/forms"),listArchivedForms:()=>f("/forms?archived=1"),archiveForm:e=>w(`/forms/${e}/archive`,{}),unarchiveForm:e=>w(`/forms/${e}/unarchive`,{}),getForm:e=>f(`/forms/${e}`).then(x),createForm:e=>w("/forms",e).then(x),updateForm:(e,t)=>w(`/forms/${e}`,t).then(x),duplicateForm:e=>w(`/forms/${e}/duplicate`,{}).then(x),deleteForm:e=>$(`/forms/${e}`),preview:(e,t)=>w(`/forms/${e}/preview`,t),pages:()=>Et("wp/v2/pages?per_page=100&status=publish&orderby=title&order=asc&_fields=id,title").then(e=>e.map(t=>({id:t.id,title:St(t.title?.rendered??"")||`#${t.id}`}))),mergeTags:e=>f(`/forms/${e}/merge-tags`).then(t=>t.groups),analytics:(e,t="")=>f(`/forms/${e}/analytics${t?`?dimension=${encodeURIComponent(t)}`:""}`),demoStatus:()=>f("/demo"),demoSeed:e=>w("/demo",e?{count:e}:{}),demoRemove:()=>$("/demo"),listEntries:e=>f(`/entries${W(e)}`),getEntry:e=>f(`/entries/${e}`),updateEntry:(e,t)=>w(`/entries/${e}`,t),deleteEntry:e=>$(`/entries/${e}`),exportEntries:e=>f(`/entries/export${W(e)}`),listThemes:()=>f("/themes"),saveTheme:e=>w("/themes",e),deleteTheme:e=>$(`/themes/${e}`)};function o(e,t={}){const s=document.createElement(e);t.class&&(s.className=t.class),t.text!==void 0&&(s.textContent=t.text),t.html!==void 0&&(s.innerHTML=t.html),t.title&&(s.title=t.title),t.type&&"type"in s&&(s.type=t.type),t.value!==void 0&&"value"in s&&(s.value=t.value),t.placeholder!==void 0&&"placeholder"in s&&(s.placeholder=t.placeholder),t.href&&"href"in s&&(s.href=t.href);for(const[n,r]of Object.entries(t.attrs??{}))r===void 0||r===!1||s.setAttribute(n,r===!0?"":String(r));Object.assign(s.style,t.style??{});for(const[n,r]of Object.entries(t.on??{}))s.addEventListener(n,r);for(const n of t.children??[])n==null||n===!1||s.append(n);return s}function v(e){e.replaceChildren()}function q(e){return o("span",{class:`dashicons ${e.startsWith("dashicons-")?e:`dashicons-${e}`}`,attrs:{"aria-hidden":"true"}})}function Lt(e,t,s=""){return o("input",{class:"atfb-input",type:"text",value:e,placeholder:s,on:{input:n=>t(n.target.value)}})}function X(e,t,s){if(k("os-select")&&k("os-option")){const n=document.createElement("os-select");n.setAttribute("value",e),n.classList.add("atfb-field");for(const r of t){const i=document.createElement("os-option");i.setAttribute("value",r.value),r.disabled&&i.setAttribute("disabled",""),i.textContent=r.label,n.append(i)}return n.addEventListener("os-pick",r=>{s(String(r.detail?.value??""))}),n}return o("select",{class:"atfb-input atfb-select",on:{change:n=>s(n.target.value)},children:t.map(n=>o("option",{value:n.value,text:n.label,attrs:{selected:n.value===e,disabled:n.disabled}}))})}function $t(e,t,s){if(k("os-checkbox-label")){const r=document.createElement("os-checkbox-label");return r.setAttribute("label",e),r.classList.add("atfb-check"),t&&r.setAttribute("checked",""),r.addEventListener("os-checkbox-change",i=>{s(!!i.detail?.checked)}),r}const n=o("input",{type:"checkbox",on:{change:r=>s(r.target.checked)}});return n.checked=t,o("label",{class:"atfb-check",children:[n,o("span",{text:e})]})}function m(e,t,s="secondary",n){const r=[n?q(n):null,o("span",{text:e})];if(k("os-button")){const i=document.createElement("os-button");i.setAttribute("variant",s),i.setAttribute("type","button"),i.classList.add("atfb-button",`atfb-button--${s}`),i.addEventListener("click",t);for(const a of r)a&&i.append(a);return i}return o("button",{class:`atfb-button atfb-button--${s}`,type:"button",on:{click:t},children:r})}function k(e){return typeof customElements<"u"&&!!customElements.get(e)}const xt=["os-button","os-checkbox-label","os-number-field","os-select","os-option","os-segmented","os-segment","os-color-field","os-empty-state"];let _=null;function kt(){if(_)return _;const e=window.wp?.os;return _=e?.loadComponents?e.loadComponents(xt).catch(()=>{}):Promise.resolve(),_}async function H(e,t=""){const s=window.wp?.os;return s?.confirm?s.confirm({title:t,message:e,danger:!0}):window.confirm(e)}function E(e,t="",s="info"){const n=window.wp?.os;if(n?.notify){n.notify({title:e,body:t,type:s});return}console.info(`[AllTerrain Forms] ${e}${t?`: ${t}`:""}`)}function _t(e,t){let s=0;return(...n)=>{window.clearTimeout(s),s=window.setTimeout(()=>e(...n),t)}}function Tt(e){const t=e.closest(".os-window__body");t&&(t.addEventListener("scroll",()=>{t.scrollTop&&(t.scrollTop=0),t.scrollLeft&&(t.scrollLeft=0)},{passive:!0}),t.scrollTop=0,t.scrollLeft=0)}const At="allterrain-forms/form",It="allterrain-forms/entry";function Dt(){return window.wp?.os?.relations??null}function Ct(e){const t=e.closest("[data-window-id], .os-window");if(!t)return null;const s=t.getAttribute("data-window-id");if(s)return s;const n=t.id??"";return n?n.replace(/^wp-window-/,""):null}const Ot=6e3,z=120;function G(e,t){const s=Dt();if(R.set(e,t),!s?.set)return;const n=i=>{if(T.get(e)!==r)return;const a=Ct(e);if(!a){Date.now()n(i),z);return}try{s.set(a,t)}catch(d){K||(K=!0,console.error("[AllTerrain Forms] The shell refused a window identity.",d,t)),T.delete(e);return}if(!t||s.get?.(a)?.id===t.id||Date.now()>=i){T.delete(e);return}window.setTimeout(()=>n(i),z)},r=Symbol("atf-identity");T.set(e,r),n(Date.now()+Ot)}let K=!1;const T=new WeakMap,R=new Map;function Mt(){for(const[e,t]of R){if(!e.isConnected){R.delete(e);continue}G(e,t)}}if(typeof document<"u")for(const e of["os-window-content-loaded","os-window-opened"])document.addEventListener(e,()=>Mt());function qt(e,t){const s=[];e.userId&&s.push({type:"user",id:e.userId,rel:"references"});for(const r of e.fields)if(!(r.type!=="file"||!Array.isArray(r.value)))for(const i of r.value){const a=Number(i);a>0&&s.push({type:"media",id:a,rel:"child"})}const n=[{id:`allterrain-forms/form-${e.formId}`,label:e.formTitle||"The form",url:`${t}admin.php?page=allterrain-forms&form=${e.formId}`,group:"allterrain-forms",groupLabel:"Forms",icon:"dashicons-feedback"}];return e.userId&&n.push({id:`allterrain-forms/user-${e.userId}`,label:"Who submitted it",url:`${t}user-edit.php?user_id=${e.userId}`,group:"users",groupLabel:"People",icon:"dashicons-admin-users"}),{type:It,id:e.id,root:{type:At,id:e.formId},label:e.title||`Entry #${e.id}`,links:s.slice(0,32),related:n}}const J="allterrain-forms/entry";class Rt{constructor(t){this.forms=[],this.entries=[],this.selected=null,this.selection=new Set,this.paintedSelectionId=-1,this.loadSeq=0,this.selectSeq=0,this.formId=0,this.status="inbox",this.search="",this.starred=!1,this.page=1,this.pages=1,this.total=0,this.teardowns=[],this.root=t,this.bar=t.querySelector("[data-atfe-bar]")??o("div"),this.list=t.querySelector("[data-atfe-list]")??o("div"),this.detail=t.querySelector("[data-atfe-detail]")??o("div")}async start(){this.teardowns.push(bt([J]));try{this.forms=await g.listForms()}catch(n){v(this.bar),this.bar.append(o("p",{class:"atfb-error",text:n instanceof Error?n.message:"Could not load forms."}));return}const t=ut("entries")||ft();this.formId=(t&&this.forms.some(n=>n.id===t)?t:0)||(this.forms.find(n=>n.unread>0)??this.forms[0])?.id||0,this.renderBar(),await this.load();const s=window.wp?.os;s?.subscribe&&this.teardowns.push(s.subscribe("os.alltfo_entry.changed",()=>{if(!this.root.isConnected){this.destroy();return}this.load()}))}destroy(){this.teardowns.forEach(t=>t()),this.teardowns=[]}renderBar(){v(this.bar);const t=Lt(this.search,_t(s=>{this.search=s,this.page=1,this.load()},300),"Search answers");t.type="search",t.setAttribute("aria-label","Search entries"),this.bar.append(X(String(this.formId),this.forms.map(s=>({value:String(s.id),label:s.unread?`${s.title} (${s.unread})`:s.title})),s=>{this.formId=Number(s),this.page=1,this.selection.clear(),this.selected=null,this.load()}),X(this.status,[{value:"inbox",label:"All"},{value:"alltfo-unread",label:"Unread"},{value:"alltfo-read",label:"Read"},{value:"alltfo-spam",label:"Spam"}],s=>{this.status=s,this.page=1,this.load()}),t,$t("Starred only",this.starred,s=>{this.starred=s,this.page=1,this.load()}),o("span",{class:"atfe__count",attrs:{role:"status"},text:""}),m("Export CSV",()=>void this.exportEntries("csv"),"secondary","download"),m("JSON",()=>void this.exportEntries("json"),"secondary"))}async showForm(t){this.formId=t,this.page=1,this.selection.clear(),this.selected=null,this.renderBar(),await this.load()}async load(){if(!this.formId){v(this.list),this.list.append(o("p",{class:"atfb-hint",text:"No forms yet."}));return}const t=++this.loadSeq;try{const s=await g.listEntries({form_id:this.formId,status:this.status==="inbox"?void 0:this.status,search:this.search,page:this.page,starred:this.starred});if(t!==this.loadSeq)return;if(this.page>1&&this.page>s.pages)return this.page=Math.max(1,s.pages),this.load();this.entries=s.entries,this.total=s.total,this.pages=s.pages,this.renderList(),this.reconcileSelection();const n=this.bar.querySelector(".atfe__count");n&&(n.textContent=`${this.total} ${this.total===1?"entry":"entries"}`)}catch(s){if(t!==this.loadSeq)return;v(this.list),this.list.append(o("p",{class:"atfb-error",text:s instanceof Error?s.message:"Could not load entries."}))}}reconcileSelection(){this.selected&&!this.entries.some(t=>t.id===this.selected?.id)&&(this.selected=null),this.paintedSelectionId!==(this.selected?.id??0)&&this.renderDetail()}renderList(){if(v(this.list),!this.entries.length){this.list.append(o("div",{class:"atfb-placeholder",children:[o("p",{text:this.search?"Nothing matches that.":"No entries yet."})]}));return}const t=new Set(this.entries.map(n=>n.id));for(const n of Array.from(this.selection))t.has(n)||this.selection.delete(n);this.list.append(this.renderBulkBar());const s=o("div",{class:"atfe__rows",attrs:{role:"list"}});for(const n of this.entries)s.append(this.renderRow(n));this.list.append(s),this.pages>1&&this.list.append(o("div",{class:"atfe__pager",children:[m("Previous",()=>{this.page=Math.max(1,this.page-1),this.load()}),o("span",{text:`Page ${this.page} of ${this.pages}`}),m("Next",()=>{this.page=Math.min(this.pages,this.page+1),this.load()})]}))}renderBulkBar(){const t=this.selection.size,s=o("input",{type:"checkbox",attrs:{"aria-label":"Select every entry on this page"},on:{change:n=>{const r=n.target.checked;this.entries.forEach(i=>{r?this.selection.add(i.id):this.selection.delete(i.id)}),this.renderList()}}});return s.checked=t>0&&t===this.entries.length,s.indeterminate=t>0&&tvoid this.bulk("alltfo-read")):null,t?m("Unread",()=>void this.bulk("alltfo-unread")):null,t?m("Spam",()=>void this.bulk("alltfo-spam")):null,t&&h?.canEdit?m("Delete",()=>void this.bulkDelete(),"danger"):null]})}async bulk(t){const s=Array.from(this.selection);for(const n of s)try{await g.updateEntry(n,{status:t})}catch{}this.selection.clear(),await this.load()}async bulkDelete(){const t=Array.from(this.selection);if(await H(`Delete ${t.length} ${t.length===1?"entry":"entries"} and any files uploaded with them? It cannot be undone.`,"Delete entries")){for(const n of t)try{await g.deleteEntry(n)}catch{}this.selection.clear(),this.selected=null,await this.load(),this.renderDetail()}}renderRow(t){const s=t.status==="alltfo-unread",n=o("div",{class:`atfe__row${s?" is-unread":""}${this.selected?.id===t.id?" is-selected":""}`,attrs:{role:"listitem",tabindex:"0","data-entry":t.id},children:[o("input",{class:"atfe__select",type:"checkbox",attrs:{"aria-label":`Select ${t.title}`,checked:this.selection.has(t.id)},on:{click:r=>r.stopPropagation(),change:r=>{r.target.checked?this.selection.add(t.id):this.selection.delete(t.id),this.list.querySelector(".atfe__bulk")?.replaceWith(this.renderBulkBar())}}}),o("button",{class:`atfe__star${t.starred?" is-on":""}`,type:"button",attrs:{"aria-label":t.starred?"Unstar this entry":"Star this entry"},on:{click:r=>{r.stopPropagation(),this.toggleStar(t)}},children:[q("star-filled")]}),o("div",{class:"atfe__row-body",children:[o("strong",{text:t.title}),o("span",{class:"atfe__row-meta",text:t.dateHuman})]}),t.notes?o("span",{class:"atfb-badge",text:`💬 ${t.notes}`}):null,t.spam?o("span",{class:"atfb-badge atfb-badge--spam",text:"spam"}):null]});return n.addEventListener("click",()=>{j().recentlyEndedDrag()||this.select(t)}),n.addEventListener("keydown",r=>{(r.key==="Enter"||r.key===" ")&&(r.preventDefault(),this.select(t))}),n.addEventListener("pointerdown",r=>{if(r.target.closest("button"))return;const i=o("div",{class:"atfe__ghost",children:[q("feedback"),o("span",{text:t.title})]});j().start({payload:yt(J,n,{entry:t,formId:t.formId,formTitle:t.formTitle},r,i),origin:r})}),n}async select(t){const s=++this.selectSeq;try{const n=await g.getEntry(t.id);if(s!==this.selectSeq)return;this.selected=n;const r=this.entries.find(i=>i.id===t.id);r&&(r.status=this.selected.status),this.renderList(),this.renderDetail()}catch(n){if(s!==this.selectSeq)return;E("Could not open that entry",n instanceof Error?n.message:"","error")}}renderDetail(){this.paintedSelectionId=this.selected?.id??0,G(this.root,this.selected?qt(this.selected,h?.adminUrl??""):null),v(this.detail);const t=this.selected;if(!t){this.detail.append(o("div",{class:"atfb-placeholder",children:[o("p",{text:"Pick an entry to read it."})]}));return}const s=o("dl",{class:"atfe__answers"});for(const n of t.fields)n.formatted&&s.append(o("dt",{text:n.label||n.id}),o("dd",{text:n.formatted}));this.detail.append(o("div",{class:"atfe__detail",children:[o("div",{class:"atfe__detail-head",children:[o("h2",{text:t.title}),o("p",{class:"atfb-hint",text:`${t.formTitle} — ${t.dateHuman}`})]}),s,t.quiz?o("p",{class:"atfe__quiz",text:`Score: ${t.quiz.score} of ${t.quiz.total} (${t.quiz.percent}%) — ${t.quiz.passed?"passed":"not passed"}`}):null,o("details",{class:"atfb-section",children:[o("summary",{text:"Where it came from"}),o("p",{class:"atfb-hint",text:t.ip?`IP: ${t.ip}`:"No IP recorded."}),t.referrer?o("p",{class:"atfb-hint",text:`Referrer: ${t.referrer}`}):null,t.userAgent?o("p",{class:"atfb-hint",text:t.userAgent}):null]}),o("div",{class:"atfe__actions",children:[m(t.status==="alltfo-spam"?"Not spam":"Mark as spam",()=>void this.setStatus(t,t.status==="alltfo-spam"?"alltfo-read":"alltfo-spam")),m(t.status==="alltfo-unread"?"Mark read":"Mark unread",()=>void this.setStatus(t,t.status==="alltfo-unread"?"alltfo-read":"alltfo-unread")),t.canDelete?m("Delete",()=>void this.remove(t),"danger"):null]})]}))}async toggleStar(t){try{const s=await g.updateEntry(t.id,{starred:!t.starred});t.starred=s.starred,this.renderList()}catch(s){E("Could not update that entry",s instanceof Error?s.message:"","error")}}async setStatus(t,s){try{const n=await g.updateEntry(t.id,{status:s});this.selected=n,await this.load(),this.renderDetail()}catch(n){E("Could not update that entry",n instanceof Error?n.message:"","error")}}async remove(t){if(await H("Delete this entry and any files uploaded with it? It cannot be undone.","Delete entry"))try{await g.deleteEntry(t.id),this.selected=null,await this.load(),this.renderDetail()}catch(s){E("Could not delete that entry",s instanceof Error?s.message:"","error")}}async exportEntries(t){try{const{filename:s,csv:n}=await g.exportEntries({form_id:this.formId,search:this.search,starred:this.starred,format:t}),r=new Blob([n],{type:t==="json"?"application/json":"text/csv;charset=utf-8"}),i=URL.createObjectURL(r),a=o("a",{href:i,attrs:{download:s}});document.body.append(a),a.click(),a.remove(),window.setTimeout(()=>URL.revokeObjectURL(i),1e3)}catch(s){E("Could not export",s instanceof Error?s.message:"","error")}}}let y=null;document.addEventListener("alltfo-open-entries-form",e=>{const t=Number(e.detail?.formId??0);t&&y?.showForm(t)});let F=null;function P(){if(F?.isConnected)return;y&&(y.destroy(),y=null,F=null);const e=document.querySelector("[data-atfe-root]:not([data-atfe-mounted])");!e||!h?.canRead||(e.dataset.atfeMounted="1",F=e,Tt(e),kt().then(()=>{e.isConnected&&(y=new Rt(e),y.start())}))}function V(){P(),lt()}return ct(),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",V):V(),document.addEventListener("os-window-content-loaded",P),I.mountEntries=P,Object.defineProperty(I,Symbol.toStringTag,{value:"Module"}),I}({}); diff --git a/docs/javascript.md b/docs/javascript.md index a2aa457..7d3dab9 100644 --- a/docs/javascript.md +++ b/docs/javascript.md @@ -294,3 +294,57 @@ Pointer events rather than HTML5 drag-and-drop, in both: HTML5 drag has no programmatic cancel (Escape, alt-tab and system modals all strand the state), and `setPointerCapture` anywhere in the ancestry silently stops `dragstart` firing at all. + +## Builder value pickers and condition copying + +Typing `{` in any input with **Insert a value** opens its searchable picker next +to the control. Pick a value with the mouse, or search and press Enter; arrow +keys move through results and Escape returns to the input. The inserted tag +replaces the triggering brace at the cursor. Pasting an existing formula does +not open the picker. The Insert button also remains available. + +Notification and confirmation text uses the server's merge-tag catalogue and +syntax (`{field:f1}`). Its preview describes each known tag as +`{the value of Question label}` instead of displaying invented answers. Unknown +tags remain visible. This is only a builder preview change; saved tags and +submission-time resolution are unchanged. + +Calculation inputs, both in the inspector and the Formula editor, offer numeric +questions, priced choices and repeater references using calculation syntax +(`{f1}`, `{attendees.age}`). They exclude the field being edited. The Formula +editor continues to calculate its numeric result using the displayed sample +answers. + +**Conditional** is a compact button beside Required/Optional in each field +card’s title bar. It opens a dialog with an enable switch, Show/Hide, all/any, +and the full rule editor. Changes stay in a draft until **Save conditions**; +Cancel or Escape discards them. Copy condition inside the dialog also edits +only this draft. An active condition is indicated by the button’s filled dot. + +The bottom condition strip remains directly editable. The `+ Add rule` button +adds a rule, each rule has a Delete button, and Clear removes all rules while +leaving the strip open. These structural changes can be undone. + +Condition values use `os-select` for predefined choices (including checkbox +groups), opinion scales, star ratings, toggles/consent and countries. Numeric +options follow the rendered field's limits; sliders with up to 1,000 steps also +use a selector. Open-ended answers and continuous or larger slider ranges keep +an input. This applies to the dialog, inline strip, inspector, notifications and +confirmations. Value selectors and free-text condition inputs have a 100px minimum width. Unavailable values +from older rules remain visible as disabled options until replaced; opening an +editor never silently changes a saved rule. + +**Copy condition** also appears in Conditional logic / Conditions panels. The +section whose button was clicked is always the destination. The chooser only +asks for **Copy from**, listing other fields, notifications and confirmations +and previewing the full rule set. Applying replaces +the destination's condition, including enabled state, show/hide, all/any and every +rule. The new rules are independent objects, so changing `< 6` to `≥ 6` in the +copy leaves the original intact. Self-copy, missing references and copies that +would make a field depend on itself are excluded. Changes use the normal dirty, +save and undo flow; no new schema keys or public runtime hooks are introduced. + +The desktop canvas preview now allows up to 820px (100px wider than before). +The desktop palette and inspector reserve 100px less combined, giving that space +to the draggable previews while preserving the narrower-window layouts. Shell button hosts receive no extra +padding on hover; their internal components own the button dimensions. diff --git a/src/builder.ts b/src/builder.ts index a0ee001..fd2e019 100644 --- a/src/builder.ts +++ b/src/builder.ts @@ -20,6 +20,7 @@ */ import { api, runtime } from './api'; +import { conditionValueOptions } from './condition-values'; import { buildPayload, getDragManager, insertionIndex, watchShellDragVisuals } from './dnd'; import { button, @@ -43,7 +44,7 @@ import { writeSetting, } from './ui'; import { handOffToWindow, watchHandoffButton, takeFormFor } from './handoff'; -import { LogicMap, OPERATOR_LABELS, VALUELESS_OPERATORS, controlCounts, logicEdges, logicTokens, tokensToText } from './logic-map'; +import { LogicMap, OPERATOR_LABELS, VALUELESS_OPERATORS, controlCounts, logicEdges, logicTokens, ruleTokens, tokensToText } from './logic-map'; import { boundValue, renderFieldPreview } from './field-preview'; import type { LogicToken } from './logic-map'; import { forgetMergeTags, mergeTags, taggable } from './merge-tags'; @@ -52,7 +53,8 @@ import { mountThemeControls } from './theme-studio'; // standalone Theme Studio does, and mounts itself when its root is present. import './mailpoet-hub'; import { SUCCESS_STYLE_ICONS, defaultSuccessScreen, normalizeSuccessScreen, playSuccessEffects, renderSuccessScreen } from './success'; -import { openFormulaEditor } from './formula-editor'; +import { formulaInput, openFormulaEditor } from './formula-editor'; +import { openConditionCopy } from './condition-copy'; import { compileRecipe, describeRecipe, openValidationEditor, parseRecipe } from './validation-editor'; import { VALIDATION_GROUPS, VALIDATION_PRESETS, validationPreset } from './shared/validation'; import { openPreview, refreshPreview, registerPreviewButton } from './preview-button'; @@ -958,11 +960,21 @@ export class Builder { const focused = document.activeElement; if ( focused instanceof HTMLElement && this.canvas.contains( focused ) ) { - focused.addEventListener( 'blur', () => this.rebindCanvas(), { once: true } ); + // Wait for focus to reach its next control (including the value picker). + // During blur, activeElement is temporarily the body. + focused.addEventListener( 'blur', () => queueMicrotask( () => this.rebindCanvas() ), { once: true } ); return; } + // A popup option can briefly return focus to the body between pointerdown + // and click. Replacing its host then swallows the selection. Wait until + // the pointer leaves the canvas as well as its keyboard focus. + if ( this.canvas.matches( ':hover' ) ) { + this.canvas.addEventListener( 'pointerleave', () => this.rebindCanvas(), { once: true } ); + return; + } + this.renderCanvas(); } @@ -1941,20 +1953,61 @@ export class Builder { * because a screen reader reading five chips as five unrelated fragments * would be worse off than before. * - * The question chip is a button that selects that field — the reference is - * the useful kind, the kind you can follow. + * Questions, comparisons and answers are editable in place. Each rule has + * its own delete control; adding or clearing rules stays on the canvas. */ private renderCondition( owner: Field, tokens: LogicToken[] ): HTMLElement { const broken = tokens.some( ( token ) => 'field' === token.kind && token.missing ); - const wrap = el( 'span', { + const wrap = el( 'div', { class: `atfb-cond${ broken ? ' is-broken' : '' }`, - attrs: { 'aria-label': tokensToText( tokens ) }, - children: [ - icon( 'randomize' ), - ...tokens.map( ( token ) => this.renderConditionToken( owner, token ) ), - ], + attrs: { 'aria-label': tokens.length ? tokensToText( tokens ) : 'Conditional rules' }, + } ); + wrap.append( el( 'div', { class: 'atfb-cond__heading', children: [ + el( 'span', { class: 'atfb-cond__label', children: [ icon( 'randomize' ), 'Conditions' ] } ), + this.copyConditionButton( `field:${ owner.id }` ), + ] } ) ); + owner.logic.rules.forEach( ( rule, index ) => { + const parts = ruleTokens( rule, this.schema?.fields ?? [], index ); + const remove = el( 'button', { + class: 'atfb-cond__delete', type: 'button', title: 'Delete this rule', + attrs: { 'aria-label': `Delete rule ${ index + 1 }` }, + children: [ icon( 'trash' ) ], + on: { click: () => { + this.snapshot(); + this.editCondition( owner.id, ( logic ) => { logic.rules.splice( index, 1 ); }, true, `${ owner.id }:add` ); + this.snapshot(); + } }, + } ); + wrap.append( el( 'div', { + class: 'atfb-cond__rule', + children: [ + index === 0 + ? this.renderConditionToken( owner, { kind: 'verb', text: owner.logic.action === 'hide' ? 'Hidden when' : 'Shown when' } ) + : this.renderConditionToken( owner, { kind: 'join', text: owner.logic.match === 'all' ? 'and' : 'or' } ), + ...parts.map( ( token ) => this.renderConditionToken( owner, token, index ) ), + remove, + ], + } ) ); + } ); + if ( ! owner.logic.rules.length ) { + wrap.append( el( 'span', { class: 'atfb-hint', text: 'No rules yet. Add one or copy a condition.' } ) ); + } + const add = el( 'button', { + class: 'atfb-cond__add', type: 'button', text: '+ Add rule', title: 'Add rule', + attrs: { 'aria-label': 'Add rule', 'data-cond': `${ owner.id }:add` }, + on: { click: () => this.addConditionRule( owner.id ) }, + } ); + const clear = el( 'button', { + class: 'atfb-cond__clear', type: 'button', text: 'Clear', title: 'Delete all rules', + attrs: { 'aria-label': 'Clear all rules', disabled: ! owner.logic.rules.length }, + on: { click: () => { + this.snapshot(); + this.editCondition( owner.id, ( logic ) => { logic.rules = []; }, true, `${ owner.id }:add` ); + this.snapshot(); + } }, } ); + wrap.append( el( 'div', { class: 'atfb-cond__actions', children: [ add, clear ] } ) ); // The row is a live editor inside a card that is itself a draggable // button. None of the card's gestures may leak in: a pointerdown would @@ -2021,7 +2074,7 @@ export class Builder { */ private condSelect( value: string, - options: Array< { value: string; label: string } >, + options: Array< { value: string; label: string; disabled?: boolean } >, key: string, label: string, onChange: ( picked: string ) => void @@ -2033,12 +2086,14 @@ export class Builder { host.setAttribute( 'aria-label', label ); host.setAttribute( 'data-cond', key ); host.className = 'atfb-cond__control'; + host.setAttribute( 'plain', '' ); host.title = label; for ( const option of options ) { const item = document.createElement( 'os-option' ); item.setAttribute( 'value', option.value ); + if ( option.disabled ) item.setAttribute( 'disabled', '' ); item.textContent = option.label; host.append( item ); } @@ -2058,7 +2113,7 @@ export class Builder { change: ( event: Event ) => onChange( ( event.target as HTMLSelectElement ).value ), }, children: options.map( ( option ) => - el( 'option', { value: option.value, text: option.label, attrs: { selected: option.value === value } } ) + el( 'option', { value: option.value, text: option.label, attrs: { selected: option.value === value, disabled: option.disabled } } ) ), } ); } @@ -2073,29 +2128,20 @@ export class Builder { * small select, the answer is an input (or a select of the source field's * choices), and "and"/"or" toggles how rules combine. */ - private renderConditionToken( owner: Field, token: LogicToken ): HTMLElement { - if ( 'field' === token.kind && ! token.missing ) { - const chip = el( 'button', { - class: 'atfb-cond__chip atfb-cond__chip--field', - type: 'button', - text: token.text, - title: 'Go to this question', - // The row is inside a card that is itself a button; without this the - // click selects the card the chip is *on* rather than the question it - // names, which is the opposite of what it offers. - on: { - click: ( event: Event ) => { - event.stopPropagation(); - this.selectField( token.fieldId ); - - this.canvas - .querySelector< HTMLElement >( `[data-atfb-card="${ CSS.escape( token.fieldId ) }"]` ) - ?.scrollIntoView( { block: 'nearest', behavior: 'smooth' } ); - }, - }, - } ); - - return chip; + private renderConditionToken( owner: Field, token: LogicToken, ruleIndex = 0 ): HTMLElement { + if ( 'field' === token.kind ) { + const key = `${ owner.id }:source:${ ruleIndex }`; + const choices = ( this.schema?.fields ?? [] ).filter( ( field ) => field.id !== owner.id && field.type !== 'page_break' ); + return this.condSelect( token.fieldId, [ + ...( token.missing ? [ { value: token.fieldId, label: 'Choose a question…' } ] : [] ), + ...choices.map( ( field ) => ( { value: field.id, label: field.label || field.id } ) ), + ], key, 'Question used by this rule', ( value ) => this.editCondition( owner.id, ( logic ) => { + const rule = logic.rules[ ruleIndex ]; + if ( rule ) { + rule.field = value; + rule.value = ''; + } + }, true, key ) ); } if ( 'verb' === token.kind ) { @@ -2168,9 +2214,7 @@ export class Builder { return this.renderConditionValue( owner, token ); } - // Only the missing-field chip is left, and it is the one part with - // nothing to edit: the rule points at a question that is gone. - return el( 'span', { class: 'atfb-cond__chip atfb-cond__chip--missing', text: token.text } ); + return el( 'span' ); } /** @@ -2178,7 +2222,7 @@ export class Builder { * * When the question being consulted has choices, the honest editor is a * select of those choices — typing free text against a radio group can only - * produce a rule that never matches. Anything else gets a text box, sized to + * produce a rule that never matches. Scales, ratings and other finite answer sets also get selectors. Open-ended answers get a text box, sized to * its content so it reads as part of the sentence rather than as a form. */ private renderConditionValue( @@ -2202,22 +2246,12 @@ export class Builder { key ); - if ( source?.choices?.length ) { - const options = source.choices.map( ( choice ) => ( { - value: choice.value, - label: choice.label || choice.value, - } ) ); - - // A stored value no choice carries any more — the option was renamed - // or deleted — is kept visible rather than silently swapped for the - // first choice, so what the select shows is always what the rule says. - if ( token.raw !== '' && ! source.choices.some( ( choice ) => choice.value === token.raw ) ) { - options.unshift( { value: token.raw, label: token.text } ); - } - - return this.condSelect( token.raw, options, key, 'The answer that triggers this', ( picked ) => - write( picked, true ) - ); + const options = conditionValueOptions( source, token.raw, this.config?.countries ); + if ( options ) { + const picker = this.condSelect( token.raw, options, key, 'The answer that triggers this', ( picked ) => write( picked, true ) ); + picker.classList.add( 'atfb-cond__value-select' ); + picker.removeAttribute( 'plain' ); + return picker; } const numeric = [ 'number', 'range', 'scale', 'rating', 'total' ].includes( source?.type ?? '' ); @@ -2410,6 +2444,7 @@ export class Builder { icon( type?.icon ?? 'dashicons-forms' ), el( 'span', { class: 'atfb-card__type', text: type?.label ?? field.type } ), this.requiredToggle( field ), + field.type !== 'page_break' ? this.conditionToolbar( field ) : null, controls ? el( 'span', { class: 'atfb-badge atfb-badge--controls', @@ -2478,7 +2513,7 @@ export class Builder { types: ( name ) => this.config?.fieldTypes.find( ( candidate ) => candidate.type === name ), selectedId: this.selected, } ), - condition.length ? this.renderCondition( field, condition ) : null, + field.logic.enabled ? this.renderCondition( field, condition ) : null, ], } ), ], @@ -2540,6 +2575,144 @@ export class Builder { return card; } + /** Adds a rule without leaving the canvas. */ + private addConditionRule( fieldId: string ): void { + this.snapshot(); + this.editCondition( fieldId, ( logic ) => { + const source = this.schema?.fields.find( ( field ) => field.id !== fieldId && field.type !== 'page_break' ); + logic.rules.push( { field: source?.id ?? '', operator: 'is', value: '' } ); + }, true, `${ fieldId }:source:${ this.liveField( fieldId )?.logic.rules.length ?? 0 }` ); + this.snapshot(); + } + + /** A compact title-bar entry point; the settings live in a dialog. */ + private conditionToolbar( field: Field ): HTMLElement { + const trigger = el( 'button', { + class: `atfb-req atfb-condition-toggle${ field.logic.enabled ? ' is-on' : '' }`, + type: 'button', + title: field.logic.enabled ? 'Edit conditional logic' : 'Set up conditional logic', + attrs: { 'aria-haspopup': 'dialog', 'data-cond': `${ field.id }:enabled` }, + children: [ + el( 'span', { class: 'atfb-condition-dot', attrs: { 'aria-hidden': 'true' } } ), + el( 'span', { text: 'Conditional' } ), + ], + on: { click: () => this.openConditionEditor( field.id ) }, + } ); + for ( const name of [ 'pointerdown', 'click', 'keydown' ] ) { + trigger.addEventListener( name, ( event ) => event.stopPropagation() ); + } + return trigger; + } + + /** Edits a draft, so Cancel leaves the saved and inline conditions alone. */ + private openConditionEditor( fieldId: string ): void { + const field = this.liveField( fieldId ); + if ( ! field ) { + return; + } + const draft: Logic = { ...field.logic, rules: field.logic.rules.map( ( rule ) => ( { ...rule } ) ) }; + const overlay = el( 'div', { class: 'atfb-overlay' } ); + const dialog = el( 'div', { + class: 'atfb-modal atfb-condition-editor', + attrs: { role: 'dialog', 'aria-modal': 'true', 'aria-label': 'Conditional logic', tabindex: '-1' }, + } ); + const close = () => { + overlay.remove(); + this.root.querySelector< HTMLElement >( `[data-cond="${ CSS.escape( fieldId ) }:enabled"]` )?.focus(); + }; + const controlsSelector = 'button:not([disabled]), input, select, os-select, os-checkbox-label, os-button:not([disabled])'; + const paint = () => { + const focusedIndex = [ ...dialog.querySelectorAll( controlsSelector ) ].indexOf( document.activeElement! ); + const write = ( mutate: ( logic: Logic ) => void, rebuild = false ) => { + mutate( draft ); + if ( rebuild ) { + paint(); + } + }; + const action = select( draft.action, [ { value: 'show', label: 'Show' }, { value: 'hide', label: 'Hide' } ], ( value ) => { draft.action = value as Logic[ 'action' ]; } ); + action.setAttribute( 'aria-label', 'Conditional action' ); + const match = select( draft.match, [ { value: 'all', label: 'all' }, { value: 'any', label: 'any' } ], ( value ) => { + draft.match = value as Logic[ 'match' ]; + paint(); + } ); + match.setAttribute( 'aria-label', 'Match rules' ); + const copy = el( 'button', { + class: 'atfb-copy-condition', type: 'button', + children: [ icon( 'admin-page' ), el( 'span', { text: 'Copy condition' } ) ], + on: { click: () => openConditionCopy( { + root: overlay, + to: `field:${ fieldId }`, + schema: () => this.schema ? { + ...this.schema, + fields: this.schema.fields.map( ( item ) => item.id === fieldId ? { ...item, logic: draft } : item ), + } : null, + onCopy: () => { paint(); dialog.focus(); }, + } ) }, + } ); + dialog.replaceChildren( + el( 'div', { class: 'atfb-condition-editor__heading', children: [ + el( 'span', { class: 'atfb-condition-editor__icon', children: [ icon( 'randomize' ) ] } ), + el( 'div', { children: [ el( 'h2', { text: 'Conditional logic' } ), el( 'p', { text: field.label || 'Untitled field' } ) ] } ), + ] } ), + el( 'div', { class: 'atfb-condition-editor__switch', children: [ + checkbox( 'Enable conditions', draft.enabled, ( enabled ) => { + draft.enabled = enabled; + paint(); + } ), + el( 'p', { text: 'Choose when this field appears in your form.' } ), + ] } ), + ...( draft.enabled ? [ + el( 'div', { class: 'atfb-condition-editor__sentence', children: [ action, 'this field when', match, 'of these rules match:' ] } ), + ...this.logicRulesEditor( draft, write, fieldId ), + ] : [ el( 'p', { class: 'atfb-condition-editor__empty', text: 'This field is always visible. Enable conditions to choose when to show or hide it.' } ) ] ), + el( 'div', { class: 'atfb-condition-editor__footer', children: [ + copy, + el( 'div', { class: 'atfb-modal__actions', children: [ + button( 'Cancel', close ), + button( 'Save conditions', () => { + if ( ! this.liveField( fieldId ) ) { + close(); + return; + } + this.snapshot(); + this.editCondition( fieldId, ( live ) => Object.assign( live, draft, { rules: draft.rules.map( ( rule ) => ( { ...rule } ) ) } ) ); + this.snapshot(); + close(); + }, 'primary' ), + ] } ), + ] } ) + ); + if ( focusedIndex >= 0 ) { + const controls = dialog.querySelectorAll< HTMLElement >( controlsSelector ); + controls[ Math.min( focusedIndex, controls.length - 1 ) ]?.focus(); + } + }; + overlay.append( dialog ); + overlay.addEventListener( 'click', ( event ) => { if ( event.target === overlay ) close(); } ); + overlay.addEventListener( 'keydown', ( event ) => { + if ( event.key === 'Escape' ) { + event.preventDefault(); + close(); + } + if ( event.key === 'Tab' ) { + const controls = [ ...dialog.querySelectorAll< HTMLElement >( controlsSelector ) ]; + const first = controls[ 0 ]; + const last = controls[ controls.length - 1 ]; + if ( event.shiftKey && ( document.activeElement === first || document.activeElement === dialog ) ) { + event.preventDefault(); + last?.focus(); + } else if ( ! event.shiftKey && document.activeElement === last ) { + event.preventDefault(); + first?.focus(); + } + } + event.stopPropagation(); + } ); + this.root.append( overlay ); + paint(); + dialog.focus(); + } + /** * The required flag, as a toggle on the card rather than a badge. * @@ -3678,7 +3851,10 @@ export class Builder { el( 'div', { class: 'atfb-formula__row', children: [ - textInput( String( field.formula ?? '' ), ( value ) => update( 'formula', value ) ), + formulaInput( + textInput( String( field.formula ?? '' ), ( value ) => update( 'formula', value ) ), + this.schema?.fields ?? [], field.id + ), // The editor is where the formula is meant to be // written: the questions and the functions are // buttons there, and the result computes live @@ -3688,7 +3864,7 @@ export class Builder { openFormulaEditor( { root: this.root, fields: this.schema?.fields ?? [], - field, + field: this.liveField( field.id ) ?? field, onSave: ( formula ) => { update( 'formula', formula ); this.renderInspector(); @@ -4550,18 +4726,8 @@ export class Builder { if ( ! VALUELESS_OPERATORS.includes( rule.operator ) ) { const source = this.schema?.fields.find( ( candidate ) => candidate.id === rule.field ); - if ( source?.choices?.length ) { - const options = source.choices.map( ( choice ) => ( { - value: choice.value, - label: choice.label || choice.value, - } ) ); - - // A stored value no choice carries any more stays visible - // rather than being silently swapped for the first choice. - if ( '' !== rule.value && ! source.choices.some( ( choice ) => choice.value === rule.value ) ) { - options.unshift( { value: rule.value, label: rule.value } ); - } - + const options = conditionValueOptions( source, rule.value, this.config?.countries ); + if ( options ) { children.push( select( rule.value, options, ( value ) => write( ( live ) => { @@ -4591,6 +4757,11 @@ export class Builder { } } + children[ 0 ].querySelector( 'select, os-select' )?.setAttribute( 'aria-label', 'Question used by this rule' ); + children[ 1 ].setAttribute( 'aria-label', 'How the answer is compared' ); + children[ 2 ]?.setAttribute( 'aria-label', 'The answer that triggers this' ); + if ( children[ 2 ]?.matches( 'select, os-select' ) ) children[ 2 ].classList.add( 'atfb-cond__value-select' ); + return el( 'div', { class: 'atfb-rule', children } ); }; @@ -4652,6 +4823,7 @@ export class Builder { `conditions:${ key }`, 'Conditions', [ + this.copyConditionButton( `${ noun }:${ key }` ), checkbox( `Only ${ verb.toLowerCase() } this ${ noun } sometimes`, logic.enabled, @@ -4692,6 +4864,26 @@ export class Builder { ); } + /** Reuse a condition, resolving the schema when the chooser applies it. */ + private copyConditionButton( key: string ): HTMLElement { + return el( 'button', { + class: 'atfb-copy-condition', type: 'button', + children: [ icon( 'admin-page' ), el( 'span', { text: 'Copy condition' } ) ], + on: { click: () => openConditionCopy( { + root: this.root, + schema: () => this.schema, + to: key, + beforeCopy: () => this.snapshot(), + onCopy: () => { + this.snapshot(); + this.markDirty(); + this.renderCanvas(); + this.renderInspector(); + }, + } ) }, + } ); + } + /** The conditional-logic editor. */ private renderLogicSection( field: Field ): HTMLElement { const logic = field.logic; @@ -4733,6 +4925,7 @@ export class Builder { `logic:${ field.id }`, 'Conditional logic', [ + this.copyConditionButton( `field:${ field.id }` ), checkbox( 'Only show this field sometimes', logic.enabled, ( value ) => { write( ( live ) => { live.enabled = value; @@ -5286,6 +5479,22 @@ export class Builder { this.renderInspector(); } + /** Tag inputs can remain focused across autosaves; always write to the live section. */ + private writeTagValue( kind: 'notification' | 'confirmation', id: string, key: string, value: string ): void { + const section = kind === 'notification' + ? this.schema?.notifications.find( ( item ) => item.id === id ) + : this.schema?.confirmations.find( ( item ) => item.id === id ); + if ( ! section ) { + return; + } + if ( key === 'successTitle' && 'success' in section ) { + section.success.title = value; + } else { + Object.assign( section, { [ key ]: value } ); + } + this.markDirty(); + } + /** A one-line input that understands merge tags. */ private taggableInput( value: string, @@ -5360,8 +5569,7 @@ export class Builder { textInput( /\{/.test( notification.to ) ? '' : notification.to, ( value ) => { - notification.to = value; - this.markDirty(); + this.writeTagValue( 'notification', notification.id, 'to', value ); }, 'name@example.com' ) @@ -5375,8 +5583,7 @@ export class Builder { this.taggableInput( notification.to, ( value ) => { - notification.to = value; - this.markDirty(); + this.writeTagValue( 'notification', notification.id, 'to', value ); }, '{admin_email}, sales@example.com' ), @@ -5453,8 +5660,7 @@ export class Builder { this.taggableInput( notification.replyTo, ( value ) => { - notification.replyTo = value; - this.markDirty(); + this.writeTagValue( 'notification', notification.id, 'replyTo', value ); }, 'Leave empty to reply to you' ), @@ -5463,8 +5669,7 @@ export class Builder { row( 'Subject', this.taggableInput( notification.subject, ( value ) => { - notification.subject = value; - this.markDirty(); + this.writeTagValue( 'notification', notification.id, 'subject', value ); } ) ), row( @@ -5472,8 +5677,7 @@ export class Builder { this.taggableArea( notification.message, ( value ) => { - notification.message = value; - this.markDirty(); + this.writeTagValue( 'notification', notification.id, 'message', value ); }, 8 ) @@ -5550,8 +5754,7 @@ export class Builder { this.taggableArea( confirmation.message, ( value ) => { - confirmation.message = value; - this.markDirty(); + this.writeTagValue( 'confirmation', confirmation.id, 'message', value ); }, 5 ), @@ -5571,8 +5774,7 @@ export class Builder { this.taggableInput( confirmation.query, ( value ) => { - confirmation.query = value; - this.markDirty(); + this.writeTagValue( 'confirmation', confirmation.id, 'query', value ); }, 'ref={entry:id}&name={field:f1}' ), @@ -5587,8 +5789,7 @@ export class Builder { this.taggableInput( confirmation.url, ( value ) => { - confirmation.url = value; - this.markDirty(); + this.writeTagValue( 'confirmation', confirmation.id, 'url', value ); }, 'https://example.com/thank-you' ), @@ -5710,8 +5911,7 @@ export class Builder { this.taggableInput( success.title, ( value ) => { - success.title = value; - this.markDirty(); + this.writeTagValue( 'confirmation', confirmation.id, 'successTitle', value ); }, 'Thank you, {field:name}!' ), diff --git a/src/condition-copy.ts b/src/condition-copy.ts new file mode 100644 index 0000000..d8e647c --- /dev/null +++ b/src/condition-copy.ts @@ -0,0 +1,136 @@ +/** Copy a complete condition between fields, notifications and confirmations. */ +import { button, el, row, select } from './ui'; +import { ruleTokens, tokensToText } from './logic-map'; +import type { FormSchema, Logic } from './types'; + +/** A section that owns its own independent condition. */ +export interface ConditionSection { + key: string; + label: string; + fieldId?: string; + logic: Logic; +} + +/** Lists editable sections using names the form author recognises. */ +export function conditionSections( schema: FormSchema ): ConditionSection[] { + return [ + ...schema.fields.filter( ( field ) => field.type !== 'page_break' ).map( ( field ) => ( { + key: `field:${ field.id }`, label: `Field: ${ field.label || field.id }`, fieldId: field.id, logic: field.logic, + } ) ), + ...schema.notifications.map( ( item ) => ( { + key: `notification:${ item.id }`, label: `Notification: ${ item.name || item.id }`, logic: item.logic, + } ) ), + ...schema.confirmations.map( ( item ) => ( { + key: `confirmation:${ item.id }`, label: `Confirmation: ${ item.name || item.id }`, logic: item.logic, + } ) ), + ]; +} + +/** Reject self-copy, missing references and rules that would depend on themselves. */ +export function canCopyCondition( source: ConditionSection, target: ConditionSection, schema: FormSchema ): boolean { + return source.key !== target.key && source.logic.rules.length > 0 && source.logic.rules.every( + ( rule ) => rule.field !== target.fieldId && schema.fields.some( ( field ) => field.id === rule.field && field.type !== 'page_break' ) + ); +} + +/** Copies values, never rule objects; editing the copy must leave the source intact. */ +export function copyCondition( schema: FormSchema, from: string, to: string, beforeCopy?: () => void ): boolean { + const sections = conditionSections( schema ); + const source = sections.find( ( item ) => item.key === from ); + const target = sections.find( ( item ) => item.key === to ); + if ( ! source || ! target || ! canCopyCondition( source, target, schema ) ) { + return false; + } + beforeCopy?.(); + Object.assign( target.logic, source.logic, { rules: source.logic.rules.map( ( rule ) => ( { ...rule } ) ) } ); + return true; +} + +/** Opens a chooser with the whole condition visible before applying it. */ +export function openConditionCopy( options: { + root: HTMLElement; + schema: () => FormSchema | null; + to: string; + beforeCopy?: () => void; + onCopy: () => void; +} ): void { + const previous = document.activeElement as HTMLElement | null; + const overlay = el( 'div', { class: 'atfb-overlay' } ); + const dialog = el( 'div', { + class: 'atfb-modal atfb-condition-copy', + attrs: { role: 'dialog', 'aria-modal': 'true', 'aria-label': 'Copy condition', tabindex: '-1' }, + } ); + let from = ''; + const to = options.to; + const close = () => { + overlay.remove(); + previous?.focus(); + }; + + const paint = () => { + const schema = options.schema(); + if ( ! schema ) { + close(); + return; + } + const sections = conditionSections( schema ); + const target = sections.find( ( item ) => item.key === to ); + const sources = sections.filter( ( item ) => item.logic.rules.length && + target && canCopyCondition( item, target, schema ) ); + const source = sources.find( ( item ) => item.key === from ); + const sourcePicker = select( from, [ { value: '', label: 'Choose a section…' }, ...sources.map( ( item ) => ( { value: item.key, label: item.label } ) ) ], ( value ) => { + from = value; + paint(); + dialog.querySelector< HTMLElement >( '[aria-label="Copy from"]' )?.focus(); + } ); + sourcePicker.setAttribute( 'aria-label', 'Copy from' ); + const apply = button( 'Copy condition', () => { + const live = options.schema(); + if ( live && copyCondition( live, from, to, options.beforeCopy ) ) { + close(); + options.onCopy(); + } + }, 'primary' ); + if ( ! source || ! to ) { + apply.setAttribute( 'disabled', '' ); + } + const destination = sections.find( ( item ) => item.key === to ); + dialog.replaceChildren( + el( 'h2', { text: 'Copy condition' } ), + row( 'Copy from', sourcePicker ), + el( 'p', { class: 'atfb-hint', text: `Apply to ${ destination?.label ?? 'this section' }.` } ), + el( 'p', { class: 'atfb-condition-copy__preview', attrs: { 'aria-live': 'polite' }, text: source + ? `${ source.logic.enabled ? 'Enabled' : 'Disabled' } · ${ source.logic.action === 'hide' ? 'Hide' : 'Show' } · Match ${ source.logic.match }: ` + + source.logic.rules.map( ( rule ) => tokensToText( ruleTokens( rule, schema.fields ) ) ).join( source.logic.match === 'all' ? ' and ' : ' or ' ) + : ( sources.length ? 'Choose the section whose condition you want to reuse.' : 'No conditions are available to copy here yet.' ) } ), + el( 'p', { class: 'atfb-hint', text: destination?.logic.rules.length + ? 'This replaces the destination’s entire condition. You can edit the copy independently.' + : 'Copies every rule, all/any matching, show/hide and enabled state. You can edit the copy independently.' } ), + el( 'div', { class: 'atfb-modal__actions', children: [ button( 'Cancel', close ), apply ] } ) + ); + }; + overlay.append( dialog ); + overlay.addEventListener( 'click', ( event ) => { if ( event.target === overlay ) close(); } ); + overlay.addEventListener( 'keydown', ( event ) => { + if ( event.key === 'Escape' ) { + event.preventDefault(); + close(); + } + if ( event.key === 'Tab' ) { + const controls = [ ...dialog.querySelectorAll< HTMLElement >( 'button:not([disabled]), select, os-select, os-button:not([disabled])' ) ]; + const first = controls[ 0 ]; + const last = controls[ controls.length - 1 ]; + if ( event.shiftKey && ( document.activeElement === first || document.activeElement === dialog ) ) { + event.preventDefault(); + last?.focus(); + } else if ( ! event.shiftKey && document.activeElement === last ) { + event.preventDefault(); + first?.focus(); + } + } + event.stopPropagation(); + } ); + options.root.append( overlay ); + paint(); + dialog.focus(); +} diff --git a/src/condition-values.ts b/src/condition-values.ts new file mode 100644 index 0000000..5262127 --- /dev/null +++ b/src/condition-values.ts @@ -0,0 +1,63 @@ +/** Allowed answers for conditional-value selectors, shared by every editor. */ +import type { Field } from './types'; + +export interface ConditionValueOption { + value: string; + label: string; + disabled?: boolean; +} + +/** Mirrors the rendered controls; null means the answer is open-ended. */ +export function conditionValueOptions( + field: Field | undefined, + current: string, + countries: Record< string, string > = {} +): ConditionValueOption[] | null { + if ( ! field ) return null; + let options: ConditionValueOption[]; + const number = ( key: string, fallback: number ) => { + const value = field[ key ]; + return value === undefined || value === null || ! Number.isFinite( Number( value ) ) ? fallback : Number( value ); + }; + const sequence = ( min: number, max: number, step = 1 ) => Array.from( + { length: Math.floor( ( max - min ) / step + 1e-9 ) + 1 }, + ( _, index ) => { + const value = String( Number( ( min + index * step ).toPrecision( 12 ) ) ); + return { value, label: value }; + } + ); + + if ( field.type === 'scale' ) { + const min = Math.trunc( number( 'min', 0 ) ); + let max = Math.trunc( number( 'max', 10 ) ); + if ( max <= min ) max = min + 10; + options = sequence( min, Math.min( max, min + 20 ) ); + } else if ( field.type === 'rating' ) { + options = sequence( 1, Math.max( 2, Math.min( 10, Math.abs( Math.trunc( number( 'max', 5 ) ) ) ) ) ); + } else if ( field.type === 'switch' || field.type === 'consent' ) { + // Boolean answers compare as "1" and "" in both logic engines. + options = [ { value: '1', label: 'Checked' }, { value: '', label: 'Unchecked' } ]; + } else if ( field.type === 'country' ) { + options = Object.entries( countries ).map( ( [ value, label ] ) => ( { value, label } ) ); + } else if ( field.type === 'range' ) { + const min = number( 'min', 0 ); + const max = number( 'max', 100 ); + const step = field.step === 'any' ? 0 : number( 'step', 1 ); + // Continuous or enormous ranges cannot be represented by a useful menu. + if ( step <= 0 || max < min || ( max - min ) / step > 999 ) return null; + options = sequence( min, max, step ); + } else if ( field.choices?.length || [ 'select', 'multiselect', 'radio', 'checkboxes', 'image_choice', 'quiz', 'likert' ].includes( field.type ) ) { + options = ( field.choices ?? [] ).map( ( choice ) => ( { value: choice.value, label: choice.label || choice.value } ) ); + } else { + return null; + } + + if ( ! options.some( ( option ) => option.value === current ) && current !== '' ) { + // Keep old rules honest without offering an obsolete answer to new rules. + options.unshift( { value: current, label: `${ current } (stored value; unavailable)`, disabled: true } ); + } + if ( ! options.some( ( option ) => option.value === '' ) ) { + options.unshift( { value: '', label: 'Choose a value…', disabled: true } ); + } + return options; +} diff --git a/src/formula-editor.ts b/src/formula-editor.ts index 4f76585..92e197f 100644 --- a/src/formula-editor.ts +++ b/src/formula-editor.ts @@ -11,7 +11,7 @@ */ import { calculate } from './shared/calc'; -import { insertAtCursor } from './merge-tags'; +import { insertAtCursor, taggable } from './merge-tags'; import { button, el, row } from './ui'; import type { Field, Values } from './types'; @@ -136,6 +136,30 @@ export function formulaSampleValues( fields: Field[], except: string ): Values { return values; } +/** A calculation input with the same brace shortcut as notification values. */ +export function formulaInput( + input: HTMLInputElement | HTMLTextAreaElement, + fields: Field[], + except: string +): HTMLElement { + return taggable( input, { + preview: false, + groups: () => [ { + id: 'references', + label: 'Your questions', + items: [ + ...formulaTargets( fields, except ).map( ( field ) => ( { + label: field.label || field.id, tag: `{${ field.id }}`, sample: '', hint: '', + } ) ), + ...repeaterReferences( fields.filter( ( field ) => field.id !== except ) ).map( ( ref ) => ( { + label: ref.label, tag: ref.insert, sample: '', hint: '', + } ) ), + ], + empty: 'Add a number, scale or priced choice question to reference it here.', + } ], + } ); +} + /** What the editor needs from its host. */ export interface FormulaEditorOptions { /** Where the overlay mounts — the builder root, so it stays inside the window. */ @@ -232,7 +256,7 @@ export function openFormulaEditor( options: FormulaEditorOptions ): void { } ); const targets = formulaTargets( options.fields, options.field.id ); - const repeaters = repeaterReferences( options.fields ); + const repeaters = repeaterReferences( options.fields.filter( ( field ) => field.id !== options.field.id ) ); const questions = el( 'div', { class: 'atfb-formula__chips', @@ -256,7 +280,7 @@ export function openFormulaEditor( options: FormulaEditorOptions ): void { attrs: { role: 'dialog', 'aria-label': 'Formula editor' }, children: [ el( 'h2', { text: 'Formula' } ), - input, + formulaInput( input, options.fields, options.field.id ), result, row( 'Your questions', questions, 'Click one to reference its answer.' ), row( 'Functions', functions ), diff --git a/src/merge-tags.ts b/src/merge-tags.ts index 11b53ea..c918aa3 100644 --- a/src/merge-tags.ts +++ b/src/merge-tags.ts @@ -13,11 +13,11 @@ * * 1. **A picker.** Every box that understands tags gets an Insert button opening * a grouped list — the form's own questions first, by the labels the person - * wrote. Nobody has to type a brace. The tag is shown beside each label, small + * wrote. Typing `{` opens that same picker in place. The tag is shown beside each label, small * and secondary, which is how the syntax gets learned rather than taught. * - * 2. **A resolved preview.** Under each box, the same text with the tags filled - * in from sample values. The reason merge tags feel like guesswork is that you + * 2. **A descriptive preview.** Under each box, each tag becomes + * `{the value of Question label}`. The reason merge tags feel like guesswork is that you * cannot see the result until a real email reaches a real person, and by then * it is too late to be wrong. * @@ -87,7 +87,7 @@ function flatten( groups: MergeTagGroup[] ): Map< string, MergeTag > { /** * The text as it will read once the tags are resolved. * - * Uses each tag's sample value. A tag nobody recognises is left visible rather + * Names each value instead of inventing an answer. A tag nobody recognises is left visible rather * than blanked, because that is what the server does with it too — and a preview * that quietly swallowed a typo would hide the one mistake this is here to * catch. @@ -98,7 +98,7 @@ export function resolvePreview( text: string, groups: MergeTagGroup[] ): string return text.replace( /\{[a-z_]+(?::[^}]*)?\}/gi, ( match ) => { const known = all.get( match.toLowerCase() ); - return known ? known.sample : match; + return known ? `{the value of ${ known.label }}` : match; } ); } @@ -109,11 +109,18 @@ export function hasTags( text: string ): boolean { /** The one open picker, so a second Insert click does not stack two. */ let openPicker: HTMLElement | null = null; +let pickerRequest = 0; +let pickerReturnFocus: HTMLElement | null = null; /** Closes whatever picker is open. */ -function closePicker(): void { +function closePicker( restoreFocus = false ): void { + pickerRequest++; openPicker?.remove(); openPicker = null; + if ( restoreFocus ) { + pickerReturnFocus?.focus(); + } + pickerReturnFocus = null; } if ( typeof document !== 'undefined' ) { @@ -130,17 +137,18 @@ if ( typeof document !== 'undefined' ) { return; } - if ( openPicker && ! openPicker.contains( target ) ) { + if ( ! openPicker?.contains( target ) ) { closePicker(); } } ); document.addEventListener( 'keydown', ( event ) => { - if ( 'Escape' === event.key && openPicker ) { - closePicker(); - event.stopPropagation(); + if ( 'Escape' === event.key && pickerReturnFocus ) { + closePicker( true ); + event.preventDefault(); + event.stopImmediatePropagation(); } - } ); + }, true ); } /** @@ -160,35 +168,27 @@ export function insertAtCursor( field: HTMLInputElement | HTMLTextAreaElement, t const caret = start + text.length; field.setSelectionRange( caret, caret ); - field.focus(); // A programmatic value change fires nothing, so the pane's own `input` // handler — the thing that marks the form dirty — would never run. field.dispatchEvent( new Event( 'input', { bubbles: true } ) ); + field.focus(); } -/** - * The lowest point a popover can reach before something clips it. - * - * Walks up to the first ancestor that scrolls — in the builder that is - * `.atfb__canvas`, whose `overflow-y: auto` is what actually cuts the popover - * off. Falls back to the viewport on a plain admin page, where the document - * itself is the scroller. - */ -function clipBottom( from: HTMLElement ): number { - let node: HTMLElement | null = from.parentElement; - +/** The visible area shared by every scrolling ancestor of the control. */ +function pickerBounds( from: HTMLElement ): { top: number; bottom: number } { + let top = 0; + let bottom = window.innerHeight; + let node = from.parentElement; while ( node && node !== document.body ) { - const overflow = getComputedStyle( node ).overflowY; - - if ( 'auto' === overflow || 'scroll' === overflow || 'hidden' === overflow ) { - return node.getBoundingClientRect().bottom; + if ( /auto|scroll|hidden|clip/.test( getComputedStyle( node ).overflowY ) ) { + const rect = node.getBoundingClientRect(); + top = Math.max( top, rect.top ); + bottom = Math.min( bottom, rect.bottom ); } - node = node.parentElement; } - - return window.innerHeight; + return { top, bottom }; } /** Builds the popover list. */ @@ -244,8 +244,8 @@ function buildPicker( type: 'button', on: { click: () => { - onPick( item.tag ); closePicker(); + onPick( item.tag ); }, }, children: [ @@ -259,9 +259,7 @@ function buildPicker( item.hint || item.sample ? el( 'span', { class: 'atfb-tagpick__meta', - // The sample is the part people actually read, so it - // leads; the hint explains the cases where it is empty. - text: item.sample ? `e.g. ${ item.sample }` : item.hint, + text: `{the value of ${ item.label }}`, } ) : null, ], @@ -278,7 +276,7 @@ function buildPicker( paint( '' ); search.addEventListener( 'input', () => paint( search.value ) ); - return el( 'div', { + const picker = el( 'div', { class: 'atfb-tagpick', attrs: { role: 'dialog', 'aria-label': 'Insert a value' }, children: [ @@ -290,11 +288,27 @@ function buildPicker( list, ], } ); + picker.addEventListener( 'keydown', ( event ) => { + const items = [ ...list.querySelectorAll< HTMLButtonElement >( 'button' ) ]; + const index = items.indexOf( document.activeElement as HTMLButtonElement ); + if ( event.key === 'ArrowDown' || event.key === 'ArrowUp' ) { + event.preventDefault(); + const next = event.key === 'ArrowDown' ? index + 1 : ( index < 0 ? items.length - 1 : index - 1 ); + items[ ( next + items.length ) % items.length ]?.focus(); + } else if ( event.key === 'Enter' && event.target === search ) { + event.preventDefault(); + items[ 0 ]?.click(); + } + event.stopPropagation(); + } ); + return picker; } /** Options for a tag-aware control. */ interface TaggableOptions { - formId: number; + formId?: number; + /** Calculation references can supply their own grammar-specific catalogue. */ + groups?: () => MergeTagGroup[] | Promise< MergeTagGroup[] >; /** Shown under the box as “Reads as: …”. Off for one-line URLs, where it adds noise. */ preview?: boolean; /** Extra text under the control, before the preview. */ @@ -313,10 +327,12 @@ export function taggable( field: HTMLInputElement | HTMLTextAreaElement, options: TaggableOptions ): HTMLElement { + const catalogue = () => Promise.resolve( options.groups ? options.groups() : mergeTags( options.formId ?? 0 ) ); const insert = el( 'button', { class: 'atfb-button atfb-button--ghost atfb-tagpick__open', type: 'button', title: 'Insert a value from the submission', + attrs: { 'aria-haspopup': 'dialog' }, children: [ icon( 'shortcode' ), el( 'span', { text: 'Insert a value' } ) ], } ); @@ -345,7 +361,10 @@ export function taggable( return; } - void mergeTags( options.formId ).then( ( groups ) => { + void catalogue().then( ( groups ) => { + if ( ! hasTags( field.value ) ) { + return; + } preview.hidden = false; preview.replaceChildren( el( 'span', { class: 'atfb-taggable__preview-label', text: 'Reads as' } ), @@ -357,45 +376,53 @@ export function taggable( field.addEventListener( 'input', repaint ); repaint(); - insert.addEventListener( 'click', ( event ) => { - event.stopPropagation(); - - if ( openPicker && wrapper.contains( openPicker ) ) { - closePicker(); - - return; - } - + /** Preserve the replacement range while focus moves into the picker. */ + const open = ( start: number, end: number ) => { closePicker(); + const request = pickerRequest; + const original = field.value; + pickerReturnFocus = field; - void mergeTags( options.formId ).then( ( groups ) => { + void catalogue().then( ( groups ) => { + if ( request !== pickerRequest || ! wrapper.isConnected || field.value !== original ) { + return; + } const picker = buildPicker( groups, ( tag ) => { + field.setSelectionRange( start, end ); insertAtCursor( field, tag ); - repaint(); } ); - wrapper.append( picker ); openPicker = picker; + const bounds = pickerBounds( wrapper ); + picker.style.maxBlockSize = `${ Math.max( 0, Math.min( 320, bounds.bottom - bounds.top - 8 ) ) }px`; + const anchor = wrapper.getBoundingClientRect(); + const height = picker.getBoundingClientRect().height; + // Prefer below, otherwise above. Clamp within the pane even when a + // tall textarea leaves too little space on either side. + const preferred = anchor.bottom + height <= bounds.bottom ? anchor.bottom : anchor.top - height; + const top = Math.max( bounds.top + 4, Math.min( preferred, bounds.bottom - height - 4 ) ); + picker.style.insetBlockStart = `${ top - anchor.top }px`; + picker.querySelector< HTMLInputElement >( '.atfb-tagpick__search' )?.focus( { preventScroll: true } ); + } ); + }; - // Flip above the button when there is no room below it. The Message box - // is the tallest control in the pane and sits near the bottom of the - // window, so the picker opened from it is exactly the one that would - // otherwise be clipped — and a list whose last rows cannot be reached is - // indistinguishable from a list that does not contain them. - // - // Measured against the scrolling pane, not the viewport. Inside an - // OpenStation window the viewport is the whole desktop and is always - // roomy, while the pane that actually clips the popover is a few hundred - // pixels tall — so a viewport test says "plenty of room" in precisely - // the case where there is none. - if ( window.innerHeight - picker.getBoundingClientRect().top < picker.offsetHeight ) { - picker.classList.add( 'atfb-tagpick--above' ); - } else if ( picker.getBoundingClientRect().bottom > clipBottom( wrapper ) ) { - picker.classList.add( 'atfb-tagpick--above' ); - } + insert.addEventListener( 'click', ( event ) => { + event.stopPropagation(); + if ( pickerReturnFocus === field ) { + closePicker( true ); + return; + } + open( field.selectionStart ?? field.value.length, field.selectionEnd ?? field.value.length ); + } ); - picker.querySelector< HTMLInputElement >( '.atfb-tagpick__search' )?.focus(); - } ); + field.addEventListener( 'input', ( event ) => { + const typed = event as InputEvent; + const caret = field.selectionStart ?? 0; + if ( ! typed.isComposing && typed.data === '{' && field.value[ caret - 1 ] === '{' ) { + // Replace the triggering brace (and an existing closing brace), so + // picking a reference never produces {{field:f1} or {field:f1}}. + open( caret - 1, caret + ( field.value[ caret ] === '}' ? 1 : 0 ) ); + } } ); return wrapper; diff --git a/src/ui.ts b/src/ui.ts index 524c739..2e254af 100644 --- a/src/ui.ts +++ b/src/ui.ts @@ -214,7 +214,7 @@ export function textArea( value: string, onChange: ( value: string ) => void, ro */ export function select( value: string, - options: Array< { value: string; label: string } >, + options: Array< { value: string; label: string; disabled?: boolean } >, onChange: ( value: string ) => void ): HTMLElement { if ( hasComponent( 'os-select' ) && hasComponent( 'os-option' ) ) { @@ -227,6 +227,7 @@ export function select( const item = document.createElement( 'os-option' ); item.setAttribute( 'value', option.value ); + if ( option.disabled ) item.setAttribute( 'disabled', '' ); item.textContent = option.label; host.append( item ); } @@ -247,7 +248,7 @@ export function select( el( 'option', { value: option.value, text: option.label, - attrs: { selected: option.value === value }, + attrs: { selected: option.value === value, disabled: option.disabled }, } ) ), } ); diff --git a/tests/vitest/builder-picker.test.ts b/tests/vitest/builder-picker.test.ts new file mode 100644 index 0000000..ca0bb48 --- /dev/null +++ b/tests/vitest/builder-picker.test.ts @@ -0,0 +1,54 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; +import { Builder } from '../../src/builder'; +import { api } from '../../src/api'; +import type { Form, FormSchema } from '../../src/types'; + +/** A tag selection must survive an autosave that finishes while the picker is open. */ +describe( 'picker across autosave', () => { + afterEach( () => { + vi.clearAllTimers(); + vi.useRealTimers(); + vi.restoreAllMocks(); + document.body.replaceChildren(); + } ); + + it( 'writes the selected tag to the adopted schema and keeps the input mounted', async () => { + vi.useFakeTimers(); + const root = document.createElement( 'div' ); + root.innerHTML = '
'; + document.body.append( root ); + const schema = { + fields: [], confirmations: [], notifications: [ { + id: 'mail', name: 'Mail', enabled: false, to: '{admin_email}', replyTo: '', + subject: 'Score: ', message: '', attachFiles: false, + logic: { enabled: false, action: 'show', match: 'all', rules: [] }, + } ], + } as unknown as FormSchema; + const builder = new Builder( root ); + Reflect.set( builder, 'schema', schema ); + Reflect.set( builder, 'form', { id: 9876, title: 'QA', schema } ); + Reflect.set( builder, 'tab', 'notify' ); + const pane = Reflect.get( builder, 'renderNotificationsPane' ).call( builder ); + root.querySelector( '[data-atfb-canvas]' )!.append( pane ); + root.querySelector( 'details' )!.open = true; + vi.spyOn( api, 'mergeTags' ).mockResolvedValue( [ { id: 'answers', label: 'Answers', items: [ { + tag: '{field:score}', label: 'Opinion scale', hint: '', sample: 'Their answer', + } ] } ] ); + const input = [ ...root.querySelectorAll( 'input' ) ].find( ( item ) => item.value === 'Score: ' )!; + input.focus(); + input.value += '{'; + input.setSelectionRange( input.value.length, input.value.length ); + input.dispatchEvent( new InputEvent( 'input', { data: '{', bubbles: true } ) ); + await Promise.resolve(); + await Promise.resolve(); + const saved = JSON.parse( JSON.stringify( { id: 9876, title: 'QA', schema } ) ) as Form; + vi.spyOn( api, 'updateForm' ).mockResolvedValue( saved ); + await Reflect.get( builder, 'save' ).call( builder, true ); + root.querySelector< HTMLButtonElement >( '.atfb-tagpick__item' )!.click(); + await Promise.resolve(); + expect( saved.schema.notifications[ 0 ].subject ).toBe( 'Score: {field:score}' ); + expect( input.value ).toBe( 'Score: {field:score}' ); + expect( input.isConnected ).toBe( true ); + expect( document.activeElement ).toBe( input ); + } ); +} ); diff --git a/tests/vitest/condition-copy.test.ts b/tests/vitest/condition-copy.test.ts new file mode 100644 index 0000000..e84ab2c --- /dev/null +++ b/tests/vitest/condition-copy.test.ts @@ -0,0 +1,86 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; +import { conditionSections, copyCondition, openConditionCopy } from '../../src/condition-copy'; +import type { FormSchema, Logic } from '../../src/types'; + +const empty = (): Logic => ( { enabled: false, action: 'show', match: 'all', rules: [] } ); +function fixture(): FormSchema { + return { + fields: [ + { id: 'score', label: 'Opinion scale', type: 'scale', logic: empty() }, + { id: 'uninstall', label: 'Will you uninstall it?', type: 'radio', logic: { + enabled: true, action: 'hide', match: 'any', rules: [ + { field: 'score', operator: 'less', value: '6' }, + { field: 'score', operator: 'empty', value: '' }, + ], + } }, + { id: 'recommend', label: 'Will you recommend us?', type: 'radio', logic: empty() }, + ], + notifications: [ { id: 'mail', name: 'Admin email', logic: empty() } ], + confirmations: [ { id: 'thanks', name: 'Thank you', logic: empty() } ], + } as FormSchema; +} +afterEach( () => document.body.replaceChildren() ); +describe( 'copying conditions', () => { + it( 'copies the entire condition and keeps edits independent', () => { + const schema = fixture(); + expect( copyCondition( schema, 'field:uninstall', 'field:recommend' ) ).toBe( true ); + const source = schema.fields[ 1 ].logic; + const copied = schema.fields[ 2 ].logic; + expect( copied ).toEqual( source ); + copied.rules[ 0 ].operator = 'greater_equal'; + copied.rules.splice( 1, 1 ); + expect( source.rules[ 0 ].operator ).toBe( 'less' ); + expect( source.rules ).toHaveLength( 2 ); + } ); + it( 'supports fields, notifications and confirmations', () => { + const schema = fixture(); + expect( conditionSections( schema ) ).toHaveLength( 5 ); + expect( copyCondition( schema, 'field:uninstall', 'notification:mail' ) ).toBe( true ); + expect( copyCondition( schema, 'notification:mail', 'confirmation:thanks' ) ).toBe( true ); + expect( schema.confirmations[ 0 ].logic ).toEqual( schema.fields[ 1 ].logic ); + } ); + it( 'refuses missing sections, empty rules, self-copy and self-dependency', () => { + const schema = fixture(); + for ( const [ from, to ] of [ [ 'field:missing', 'field:recommend' ], [ 'field:uninstall', 'field:missing' ], + [ 'field:score', 'field:recommend' ], [ 'field:uninstall', 'field:uninstall' ], [ 'field:uninstall', 'field:score' ] ] ) { + expect( copyCondition( schema, from, to ) ).toBe( false ); + } + schema.fields[ 1 ].logic.rules[ 0 ].field = 'deleted'; + expect( copyCondition( schema, 'field:uninstall', 'field:recommend' ) ).toBe( false ); + } ); + it( 'chooses only a source and applies to the section that opened it', () => { + const schema = fixture(); + const onCopy = vi.fn(); + openConditionCopy( { root: document.body, schema: () => schema, to: 'field:recommend', onCopy } ); + expect( document.querySelector( '[aria-label="Copy to"]' ) ).toBeNull(); + const source = document.querySelector< HTMLSelectElement >( '[aria-label="Copy from"]' )!; + expect( [ ...source.options ].map( ( option ) => option.value ) ).not.toContain( 'field:recommend' ); + source.value = 'field:uninstall'; + source.dispatchEvent( new Event( 'change' ) ); + expect( document.querySelector( '.atfb-condition-copy__preview' )?.textContent ).toContain( 'is less than 6 or Opinion scale is empty' ); + expect( schema.fields[ 2 ].logic.enabled ).toBe( false ); + [ ...document.querySelectorAll( 'button' ) ].find( ( item ) => item.textContent === 'Copy condition' )!.click(); + expect( schema.fields[ 2 ].logic ).toEqual( schema.fields[ 1 ].logic ); + expect( onCopy ).toHaveBeenCalledOnce(); + expect( document.querySelector( '[role="dialog"]' ) ).toBeNull(); + } ); + it( 'Cancel leaves the destination untouched', () => { + const schema = fixture(); + openConditionCopy( { root: document.body, schema: () => schema, to: 'field:recommend', onCopy: vi.fn() } ); + [ ...document.querySelectorAll( 'button' ) ].find( ( item ) => item.textContent === 'Cancel' )!.click(); + expect( schema.fields[ 2 ].logic ).toEqual( empty() ); + } ); + it( 'uses the current schema at apply time after a save replaces it', () => { + let schema = fixture(); + openConditionCopy( { root: document.body, schema: () => schema, to: 'field:recommend', onCopy: vi.fn() } ); + const source = document.querySelector< HTMLSelectElement >( '[aria-label="Copy from"]' )!; + source.value = 'field:uninstall'; + source.dispatchEvent( new Event( 'change' ) ); + const previous = schema; + schema = fixture(); + schema.fields[ 1 ].logic.rules[ 0 ].value = '7'; + [ ...document.querySelectorAll( 'button' ) ].find( ( item ) => item.textContent === 'Copy condition' )!.click(); + expect( schema.fields[ 2 ].logic.rules[ 0 ].value ).toBe( '7' ); + expect( previous.fields[ 2 ].logic.rules ).toHaveLength( 0 ); + } ); +} ); diff --git a/tests/vitest/condition-toolbar.test.ts b/tests/vitest/condition-toolbar.test.ts new file mode 100644 index 0000000..ba644df --- /dev/null +++ b/tests/vitest/condition-toolbar.test.ts @@ -0,0 +1,136 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; +import { Builder } from '../../src/builder'; +import { logicTokens } from '../../src/logic-map'; +import type { Field, FormSchema } from '../../src/types'; + +function setup( rules = false ) { + vi.useFakeTimers(); + vi.stubGlobal( 'CSS', { escape: ( value: string ) => value } ); + const root = document.createElement( 'div' ); + root.innerHTML = '
'; + document.body.append( root ); + const field = { id: 'recommend', type: 'radio', label: 'Recommend?', logic: { + enabled: false, action: 'show', match: 'all', rules: rules ? [ { field: 'score', operator: 'greater_equal', value: '6' } ] : [], + } } as Field; + const schema = { fields: [ { id: 'score', type: 'scale', label: 'Score' }, field ], notifications: [], confirmations: [] } as unknown as FormSchema; + const builder = new Builder( root ); + Reflect.set( builder, 'schema', schema ); + Reflect.set( builder, 'config', { operators: { is: 'is', greater_equal: 'is at least', less: 'is less than' } } ); + const render = () => root.querySelector( '[data-atfb-canvas]' )!.replaceChildren( + Reflect.get( builder, 'conditionToolbar' ).call( builder, field ), + ...( field.logic.enabled ? [ Reflect.get( builder, 'renderCondition' ).call( builder, field, logicTokens( field, schema.fields ) ) ] : [] ) + ); + Reflect.set( builder, 'renderCanvas', render ); + Reflect.set( builder, 'renderInspector', vi.fn() ); + render(); + return { root, field, builder }; +} +afterEach( () => { vi.clearAllTimers(); vi.useRealTimers(); vi.unstubAllGlobals(); document.body.replaceChildren(); } ); + +function clickButton( root: HTMLElement, label: string ) { + const target = [ ...root.querySelectorAll< HTMLButtonElement >( 'button' ) ].find( ( item ) => item.textContent === label ); + expect( target ).toBeDefined(); + target!.click(); +} +function open( root: HTMLElement ) { + root.querySelector< HTMLButtonElement >( '.atfb-condition-toggle' )!.click(); + return root.querySelector< HTMLElement >( '[aria-label="Conditional logic"]' )!; +} +function enable( dialog: HTMLElement ) { + const input = dialog.querySelector< HTMLInputElement >( 'input[type="checkbox"]' )!; + input.click(); +} + +describe( 'card conditional dialog', () => { + it( 'keeps the header compact and applies changes only on Save', () => { + const { root, field } = setup(); + const dialog = open( root ); + expect( root.querySelector( '[data-atfb-canvas] select' ) ).toBeNull(); + enable( dialog ); + clickButton( dialog, 'Add rule' ); + expect( field.logic.enabled ).toBe( false ); + expect( field.logic.rules ).toHaveLength( 0 ); + clickButton( dialog, 'Save conditions' ); + expect( field.logic.enabled ).toBe( true ); + expect( field.logic.rules[ 0 ].field ).toBe( 'score' ); + expect( root.querySelector( '[aria-label="Conditional logic"]' ) ).toBeNull(); + expect( root.querySelector( '.atfb-condition-toggle.is-on' ) ).not.toBeNull(); + } ); + it( 'discards draft edits on Cancel and Escape without triggering card gestures', () => { + const { root, field } = setup( true ); + const click = vi.fn(); + root.addEventListener( 'click', click ); + let dialog = open( root ); + expect( click ).not.toHaveBeenCalled(); + enable( dialog ); + dialog.querySelector< HTMLButtonElement >( '[aria-label="Remove this rule"]' )!.click(); + clickButton( dialog, 'Cancel' ); + expect( field.logic.enabled ).toBe( false ); + expect( field.logic.rules ).toEqual( [ { field: 'score', operator: 'greater_equal', value: '6' } ] ); + dialog = open( root ); + enable( dialog ); + dialog.dispatchEvent( new KeyboardEvent( 'keydown', { key: 'Escape', bubbles: true } ) ); + expect( field.logic.enabled ).toBe( false ); + expect( document.activeElement ).toBe( root.querySelector( '.atfb-condition-toggle' ) ); + } ); + it( 'copies into a draft without changing the live condition on Cancel', () => { + const { root, field, builder } = setup( true ); + const schema = Reflect.get( builder, 'schema' ) as FormSchema; + schema.fields[ 0 ].logic = { enabled: false, action: 'show', match: 'all', rules: [] }; + schema.fields.push( { id: 'uninstall', type: 'radio', label: 'Uninstall?', logic: { + enabled: true, action: 'show', match: 'all', rules: [ { field: 'score', operator: 'less', value: '6' } ], + } } as Field ); + const dialog = open( root ); + clickButton( dialog, 'Copy condition' ); + const copy = root.querySelector< HTMLElement >( '[aria-label="Copy condition"]' )!; + const source = copy.querySelector< HTMLSelectElement >( '[aria-label="Copy from"]' )!; + source.value = 'field:uninstall'; + source.dispatchEvent( new Event( 'change' ) ); + clickButton( copy, 'Copy condition' ); + expect( dialog.querySelector< HTMLSelectElement >( '[aria-label="How the answer is compared"]' )!.value ).toBe( 'less' ); + expect( field.logic.rules[ 0 ].operator ).toBe( 'greater_equal' ); + clickButton( dialog, 'Cancel' ); + expect( field.logic.rules[ 0 ].operator ).toBe( 'greater_equal' ); + expect( field.logic.enabled ).toBe( false ); + } ); + it( 'adds, edits, deletes and clears rules directly on the card', () => { + const { root, field } = setup( true ); + const dialog = open( root ); + enable( dialog ); + clickButton( dialog, 'Save conditions' ); + const operator = root.querySelector< HTMLSelectElement >( '[aria-label="How the answer is compared"]' )!; + operator.value = 'greater_equal'; + operator.dispatchEvent( new Event( 'change' ) ); + const value = root.querySelector< HTMLSelectElement >( '[aria-label="The answer that triggers this"]' )!; + value.value = '7'; + value.dispatchEvent( new Event( 'change' ) ); + expect( field.logic.rules[ 0 ] ).toEqual( { field: 'score', operator: 'greater_equal', value: '7' } ); + root.querySelector< HTMLButtonElement >( '[aria-label="Add rule"]' )!.click(); + expect( root.querySelectorAll( '[aria-label="Question used by this rule"]' ) ).toHaveLength( 2 ); + root.querySelector< HTMLButtonElement >( '[aria-label="Delete rule 2"]' )!.click(); + expect( field.logic.rules ).toHaveLength( 1 ); + root.querySelector< HTMLButtonElement >( '[aria-label="Clear all rules"]' )!.click(); + expect( field.logic.rules ).toHaveLength( 0 ); + expect( field.logic.enabled ).toBe( true ); + expect( root.textContent ).toContain( 'No rules yet' ); + root.querySelector< HTMLButtonElement >( '[aria-label="Add rule"]' )!.click(); + expect( field.logic.rules ).toHaveLength( 1 ); + } ); + it( 'saves show/hide and all/any with an undo snapshot', () => { + const { root, field, builder } = setup( true ); + const dialog = open( root ); + enable( dialog ); + const action = dialog.querySelector< HTMLSelectElement >( '[aria-label="Conditional action"]' )!; + action.value = 'hide'; + action.dispatchEvent( new Event( 'change' ) ); + const match = dialog.querySelector< HTMLSelectElement >( '[aria-label="Match rules"]' )!; + match.value = 'any'; + match.dispatchEvent( new Event( 'change' ) ); + clickButton( dialog, 'Save conditions' ); + expect( field.logic.action ).toBe( 'hide' ); + expect( field.logic.match ).toBe( 'any' ); + const history = Reflect.get( builder, 'history' ) as string[]; + expect( JSON.parse( history[ 0 ] ).fields[ 1 ].logic.enabled ).toBe( false ); + expect( JSON.parse( history[ 1 ] ).fields[ 1 ].logic.enabled ).toBe( true ); + } ); +} ); diff --git a/tests/vitest/condition-values.test.ts b/tests/vitest/condition-values.test.ts new file mode 100644 index 0000000..87b3428 --- /dev/null +++ b/tests/vitest/condition-values.test.ts @@ -0,0 +1,85 @@ +import { afterEach, beforeAll, describe, expect, it, vi } from 'vitest'; +import { conditionValueOptions } from '../../src/condition-values'; +import { rulePasses } from '../../src/shared/logic'; +import { Builder } from '../../src/builder'; +import type { Field, FormSchema, Logic } from '../../src/types'; + +const field = ( type: string, settings = {} ) => ( { id: 'source', type, label: 'Question', ...settings } as Field ); +const available = ( source: Field ) => conditionValueOptions( source, '' )!.filter( ( option ) => ! option.disabled ).map( ( option ) => option.value ); + +describe( 'condition value domains', () => { + it( 'uses the actual opinion-scale bounds, including renderer corrections', () => { + expect( available( field( 'scale' ) ) ).toEqual( Array.from( { length: 11 }, ( _, i ) => String( i ) ) ); + expect( available( field( 'scale', { min: -2, max: 2 } ) ) ).toEqual( [ '-2', '-1', '0', '1', '2' ] ); + expect( available( field( 'scale', { min: 3, max: 3 } ) ) ).toHaveLength( 11 ); + expect( available( field( 'scale', { min: 1, max: 100 } ) ) ).toHaveLength( 21 ); + } ); + it( 'matches star-rating limits', () => { + expect( available( field( 'rating' ) ) ).toEqual( [ '1', '2', '3', '4', '5' ] ); + expect( available( field( 'rating', { max: 1 } ) ) ).toEqual( [ '1', '2' ] ); + expect( available( field( 'rating', { max: 100 } ) ) ).toHaveLength( 10 ); + } ); + it( 'uses choice labels while storing values, including checkbox membership', () => { + const source = field( 'checkboxes', { choices: [ { label: 'Send updates', value: 'updates' }, { label: 'Offers', value: 'offers' } ] } ); + expect( conditionValueOptions( source, 'updates' ) ).toContainEqual( { label: 'Send updates', value: 'updates' } ); + expect( rulePasses( { field: 'source', operator: 'is', value: available( source )[ 0 ] }, [ 'updates', 'offers' ] ) ).toBe( true ); + expect( available( field( 'checkboxes', { choices: [] } ) ) ).toEqual( [] ); + } ); + it.each( [ 'switch', 'consent' ] )( 'represents checked and unchecked %s values using engine semantics', ( type ) => { + const values = available( field( type ) ); + expect( values ).toEqual( [ '1', '' ] ); + expect( rulePasses( { field: 'source', operator: 'is', value: values[ 0 ] }, true ) ).toBe( true ); + expect( rulePasses( { field: 'source', operator: 'is', value: values[ 1 ] }, false ) ).toBe( true ); + } ); + it( 'uses country codes from the server catalogue', () => { + expect( conditionValueOptions( field( 'country' ), 'ES', { ES: 'Spain', FR: 'France' } ) ).toContainEqual( { value: 'ES', label: 'Spain' } ); + } ); + it( 'offers discrete slider steps without floating-point artifacts', () => { + expect( available( field( 'range', { min: 0.1, max: 0.5, step: 0.1 } ) ) ).toEqual( [ '0.1', '0.2', '0.3', '0.4', '0.5' ] ); + expect( conditionValueOptions( field( 'range', { step: 'any' } ), '' ) ).toBeNull(); + expect( conditionValueOptions( field( 'range', { max: 1e9 } ), '' ) ).toBeNull(); + } ); + it( 'retains obsolete values visibly but makes them unavailable for selection', () => { + expect( conditionValueOptions( field( 'scale' ), '99' ) ).toContainEqual( { value: '99', label: '99 (stored value; unavailable)', disabled: true } ); + expect( conditionValueOptions( field( 'text' ), 'anything' ) ).toBeNull(); + expect( conditionValueOptions( undefined, '' ) ).toBeNull(); + } ); +} ); + +describe( 'condition os-select controls', () => { + beforeAll( () => { + customElements.define( 'os-select', class extends HTMLElement {} ); + customElements.define( 'os-option', class extends HTMLElement {} ); + } ); + afterEach( () => { document.body.replaceChildren(); vi.restoreAllMocks(); } ); + it( 'uses os-select in the shared rule editor and writes the picked value', () => { + const root = document.createElement( 'div' ); + const builder = new Builder( root ); + const schema = { fields: [ field( 'scale' ) ] } as FormSchema; + Reflect.set( builder, 'schema', schema ); + const logic: Logic = { enabled: true, action: 'show', match: 'all', rules: [ { field: 'source', operator: 'less', value: '6' } ] }; + const write = ( mutate: ( live: Logic ) => void ) => mutate( logic ); + root.append( ...Reflect.get( builder, 'logicRulesEditor' ).call( builder, logic, write ) ); + const picker = root.querySelector( '[aria-label="The answer that triggers this"]' )!; + expect( picker.tagName ).toBe( 'OS-SELECT' ); + expect( picker.getAttribute( 'value' ) ).toBe( '6' ); + expect( picker.querySelectorAll( 'os-option:not([disabled])' ) ).toHaveLength( 11 ); + picker.dispatchEvent( new CustomEvent( 'os-pick', { detail: { value: '7' } } ) ); + expect( logic.rules[ 0 ].value ).toBe( '7' ); + } ); + it( 'uses the same allowed values in the inline editor', () => { + const builder = new Builder( document.createElement( 'div' ) ); + Reflect.set( builder, 'schema', { fields: [ field( 'scale', { min: 1, max: 5 } ) ] } ); + const edit = vi.fn(); + Reflect.set( builder, 'editCondition', edit ); + const picker = Reflect.get( builder, 'renderConditionValue' ).call( builder, { id: 'target' }, { kind: 'value', sourceId: 'source', ruleIndex: 0, raw: '3', text: '3' } ) as HTMLElement; + expect( picker.tagName ).toBe( 'OS-SELECT' ); + expect( picker.classList.contains( 'atfb-cond__value-select' ) ).toBe( true ); + expect( picker.hasAttribute( 'plain' ) ).toBe( false ); + expect( [ ...picker.querySelectorAll( 'os-option:not([disabled])' ) ].map( ( option ) => option.getAttribute( 'value' ) ) ).toEqual( [ '1', '2', '3', '4', '5' ] ); + picker.dispatchEvent( new CustomEvent( 'os-pick', { detail: { value: '4' } } ) ); + const logic = { rules: [ { value: '3' } ] }; + edit.mock.calls[ 0 ][ 1 ]( logic ); + expect( logic.rules[ 0 ].value ).toBe( '4' ); + } ); +} ); diff --git a/tests/vitest/merge-tags.test.ts b/tests/vitest/merge-tags.test.ts new file mode 100644 index 0000000..edf3d17 --- /dev/null +++ b/tests/vitest/merge-tags.test.ts @@ -0,0 +1,115 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; +import { resolvePreview, taggable } from '../../src/merge-tags'; +import { formulaInput } from '../../src/formula-editor'; +import type { Field, MergeTagGroup } from '../../src/types'; + +const groups: MergeTagGroup[] = [ { id: 'answers', label: 'Answers', items: [ + { tag: '{field:score}', label: 'Opinion scale', sample: 'Their answer', hint: '' }, + { tag: '{field:name}', label: 'Your name', sample: 'Ada', hint: '' }, +] } ]; +const settle = async () => { await Promise.resolve(); await Promise.resolve(); }; +function mount( value = '', catalogue = () => Promise.resolve( groups ) ) { + const input = document.createElement( 'textarea' ); + input.value = value; + const change = vi.fn(); + input.addEventListener( 'input', change ); + const wrapper = taggable( input, { groups: catalogue } ); + document.body.append( wrapper ); + input.focus(); + return { input, wrapper, change }; +} +function typeBrace( input: HTMLTextAreaElement ) { + const at = input.selectionStart; + input.setRangeText( '{', at, input.selectionEnd, 'end' ); + input.dispatchEvent( new InputEvent( 'input', { bubbles: true, data: '{', inputType: 'insertText' } ) ); +} +afterEach( () => { + document.dispatchEvent( new KeyboardEvent( 'keydown', { key: 'Escape', bubbles: true } ) ); + document.body.replaceChildren(); +} ); + +describe( 'value previews', () => { + it( 'explains each value by label instead of inventing an answer', () => { + expect( resolvePreview( 'Hello {field:name}: {field:score}', groups ) ).toBe( 'Hello {the value of Your name}: {the value of Opinion scale}' ); + } ); + it( 'leaves unknown tags and ordinary text visible', () => { + expect( resolvePreview( 'Hello {unknown}, plain text', groups ) ).toBe( 'Hello {unknown}, plain text' ); + } ); +} ); + +describe( 'brace picker', () => { + it( 'inserts in the middle, replaces the trigger, restores focus and notifies the host', async () => { + const { input, wrapper, change } = mount( 'Score: today' ); + input.setSelectionRange( 7, 7 ); + typeBrace( input ); + await settle(); + expect( wrapper.querySelector( '[role="dialog"]' ) ).not.toBeNull(); + wrapper.querySelector< HTMLButtonElement >( '.atfb-tagpick__item' )!.click(); + expect( input.value ).toBe( 'Score: {field:score} today' ); + expect( input.selectionStart ).toBe( 20 ); + expect( document.activeElement ).toBe( input ); + expect( change ).toHaveBeenCalledTimes( 2 ); + } ); + it( 'supports search and Enter without doubling an existing closing brace', async () => { + const { input, wrapper } = mount( '}' ); + input.setSelectionRange( 0, 0 ); + typeBrace( input ); + await settle(); + const search = wrapper.querySelector< HTMLInputElement >( '.atfb-tagpick__search' )!; + search.value = 'name'; + search.dispatchEvent( new Event( 'input' ) ); + search.dispatchEvent( new KeyboardEvent( 'keydown', { key: 'Enter', bubbles: true } ) ); + expect( input.value ).toBe( '{field:name}' ); + } ); + it( 'Escape keeps the typed brace and returns to the input', async () => { + const { input, wrapper } = mount(); + typeBrace( input ); + await settle(); + document.activeElement!.dispatchEvent( new KeyboardEvent( 'keydown', { key: 'Escape', bubbles: true } ) ); + expect( wrapper.querySelector( '.atfb-tagpick' ) ).toBeNull(); + expect( input.value ).toBe( '{' ); + expect( document.activeElement ).toBe( input ); + } ); + it( 'does not open for pasted formulas or composition', async () => { + const { input, wrapper } = mount( '{field:score}' ); + input.dispatchEvent( new InputEvent( 'input', { data: null, inputType: 'insertFromPaste' } ) ); + input.dispatchEvent( new InputEvent( 'input', { data: '{', isComposing: true } ) ); + await settle(); + expect( wrapper.querySelector( '.atfb-tagpick' ) ).toBeNull(); + } ); + it( 'does not reopen after Escape while the catalogue is loading', async () => { + let resolve!: ( value: MergeTagGroup[] ) => void; + const pending = new Promise< MergeTagGroup[] >( ( done ) => { resolve = done; } ); + const { input, wrapper } = mount( '', () => pending ); + typeBrace( input ); + document.dispatchEvent( new KeyboardEvent( 'keydown', { key: 'Escape' } ) ); + resolve( groups ); + await settle(); + expect( wrapper.querySelector( '.atfb-tagpick' ) ).toBeNull(); + } ); + it( 'the Insert button still replaces the selected text', async () => { + const { input, wrapper } = mount( 'Replace this' ); + input.setSelectionRange( 8, 12 ); + wrapper.querySelector< HTMLButtonElement >( '.atfb-tagpick__open' )!.click(); + await settle(); + wrapper.querySelector< HTMLButtonElement >( '.atfb-tagpick__item' )!.click(); + expect( input.value ).toBe( 'Replace {field:score}' ); + } ); + it( 'offers numeric and repeater references in calculation syntax, excluding self', async () => { + const input = document.createElement( 'textarea' ); + const fields = [ + { id: 'score', label: 'Opinion scale', type: 'scale' }, + { id: 'name', label: 'Name', type: 'text' }, + { id: 'total', label: 'Total', type: 'total' }, + { id: 'attendees', label: 'Attendees', type: 'repeater', fields: [ { id: 'age', label: 'Age', type: 'number' } ] }, + ] as Field[]; + const wrapper = formulaInput( input, fields, 'total' ); + document.body.append( wrapper ); + typeBrace( input ); + await settle(); + const tags = [ ...wrapper.querySelectorAll( '.atfb-tagpick__tag' ) ].map( ( item ) => item.textContent ); + expect( tags ).toEqual( [ '{score}', '{attendees}', '{attendees.age}' ] ); + wrapper.querySelector< HTMLButtonElement >( '.atfb-tagpick__item' )!.click(); + expect( input.value ).toBe( '{score}' ); + } ); +} ); From ddb52119bd4637d5ca1944f80345f12035caffad Mon Sep 17 00:00:00 2001 From: prismiwi2015 Date: Sun, 13 Sep 2026 12:02:45 +0200 Subject: [PATCH 2/2] Keep value picker navigation from triggering desktop shortcuts --- assets/js/builder.js | 36 ++++++++++++-------- assets/js/builder.min.js | 6 ++-- docs/javascript.md | 4 ++- src/merge-tags.ts | 42 ++++++++++++++++-------- tests/vitest/merge-tags.test.ts | 58 +++++++++++++++++++++++++++++++++ 5 files changed, 114 insertions(+), 32 deletions(-) diff --git a/assets/js/builder.js b/assets/js/builder.js index bd47520..4ec20f6 100644 --- a/assets/js/builder.js +++ b/assets/js/builder.js @@ -1655,11 +1655,31 @@ var allTerrainFormsBuilder = function(exports) { closePicker(); } }); - document.addEventListener("keydown", (event) => { + window.addEventListener("keydown", (event) => { if ("Escape" === event.key && pickerReturnFocus) { closePicker(true); event.preventDefault(); event.stopImmediatePropagation(); + return; + } + if (!openPicker?.contains(event.target) || !["ArrowDown", "ArrowUp", "ArrowLeft", "ArrowRight", "Home", "End", "Enter"].includes(event.key)) { + return; + } + event.stopImmediatePropagation(); + const search = openPicker.querySelector(".atfb-tagpick__search"); + const items = [...openPicker.querySelectorAll(".atfb-tagpick__item")]; + const index = items.indexOf(document.activeElement); + if (event.key === "ArrowDown" || event.key === "ArrowUp") { + event.preventDefault(); + const next = event.key === "ArrowDown" ? index + 1 : index < 0 ? items.length - 1 : index - 1; + items[(next + items.length) % items.length]?.focus(); + } else if (event.key === "Enter" && event.target === search) { + event.preventDefault(); + items[0]?.click(); + } else if (event.target !== search && event.key !== "Enter") { + event.preventDefault(); + if (event.key === "Home") items[0]?.focus(); + if (event.key === "End") items[items.length - 1]?.focus(); } }, true); } @@ -1759,19 +1779,7 @@ var allTerrainFormsBuilder = function(exports) { list ] }); - picker.addEventListener("keydown", (event) => { - const items = [...list.querySelectorAll("button")]; - const index = items.indexOf(document.activeElement); - if (event.key === "ArrowDown" || event.key === "ArrowUp") { - event.preventDefault(); - const next = event.key === "ArrowDown" ? index + 1 : index < 0 ? items.length - 1 : index - 1; - items[(next + items.length) % items.length]?.focus(); - } else if (event.key === "Enter" && event.target === search) { - event.preventDefault(); - items[0]?.click(); - } - event.stopPropagation(); - }); + picker.addEventListener("keydown", (event) => event.stopPropagation()); return picker; } function taggable(field, options) { diff --git a/assets/js/builder.min.js b/assets/js/builder.min.js index 86616fb..dfc09e3 100644 --- a/assets/js/builder.min.js +++ b/assets/js/builder.min.js @@ -1,5 +1,5 @@ -var allTerrainFormsBuilder=function(ee){"use strict";class as{constructor(){this.targets=[],this.active=null,this.lastEndMs=0}start(e){if(this.active||e.origin.button!==0)return null;const{payload:s,origin:t}=e,a=t.clientX,i=t.clientY;let o=!1,l=!1,c=null,d=null,h=0,m=0;const g=()=>{document.removeEventListener("pointermove",x),document.removeEventListener("pointerup",F),document.removeEventListener("pointercancel",S),document.removeEventListener("keydown",H),window.removeEventListener("blur",S),c?.remove(),c=null,s.source.classList.remove("atf-is-dragging"),document.body.classList.remove("atf-drag-active"),d?.onLeave?.(f),d=null,this.active=null,this.lastEndMs=Date.now()},u=(L,b)=>{document.dispatchEvent(new CustomEvent(L,{detail:{payload:s,clientX:b?.clientX,clientY:b?.clientY}}))},f={payload:s,isFinished:()=>l,cancel:(L="caller")=>{l||(l=!0,g(),o&&u("os.drag.end"),e.onCancel?.(L))}},y=L=>{o=!0,s.source.classList.add("atf-is-dragging"),document.body.classList.add("atf-drag-active");const b=s.source.getBoundingClientRect();h=s.ghost?.offsetX??a-b.left,m=s.ghost?.offsetY??i-b.top,c=s.ghost?.element??s.source.cloneNode(!0),c.classList.add("atf-drag-ghost"),c.style.width=`${b.width}px`,document.body.appendChild(c),p(L),u("os.drag.start",L)},p=L=>{c&&(c.style.transform=`translate3d(${L.clientX-h}px, ${L.clientY-m}px, 0)`)},x=L=>{if(l)return;if(!o){if(Math.hypot(L.clientX-a,L.clientY-i)<4)return;y(L)}p(L);const b=this.hitTest(L.clientX,L.clientY);b!==d&&(d?.onLeave?.(f),d=b,d?.onEnter?.(f)),u("os.drag.move",L)},F=L=>{if(l)return;if(!o){l=!0,g(),e.onClickOnly?.();return}const b=d;if(l=!0,g(),b&&b.accept(s)){e.onCommit?.(b),b.onDrop(f,{clientX:L.clientX,clientY:L.clientY}),u("os.drag.end",L);return}u("os.drag.end",L),e.onCancel?.(b?"rejected":"no-target")},S=()=>f.cancel("pointercancel"),H=L=>{L.key==="Escape"&&f.cancel("escape")};return document.addEventListener("pointermove",x),document.addEventListener("pointerup",F),document.addEventListener("pointercancel",S),document.addEventListener("keydown",H),window.addEventListener("blur",S),this.active=f,f}registerDropTarget(e){return this.targets=this.targets.filter(s=>s.id!==e.id),this.targets.push(e),()=>{this.targets=this.targets.filter(s=>s.id!==e.id)}}isDragging(){return this.active!==null}recentlyEndedDrag(e=500){return Date.now()-this.lastEndMso.right||so.bottom)continue;const l=is(i.element);if(l>a){t=i,a=l;continue}l===a&&t&&rs(i.element,t.element)&&(t=i)}return t}}function is(n){let e=0,s=n;for(;s;)e++,s=s.parentElement;return e}function rs(n,e){return(e.compareDocumentPosition(n)&Node.DOCUMENT_POSITION_FOLLOWING)!==0}function Le(){return window.wp?.os??null}let $e=null;function G(){const n=Le();return n?.dragManager?n.dragManager:($e||($e=new as),$e)}function os(n){const e=i=>{const o=i.detail?.payload;return o&&n.includes(o.type)?o.source:null},s=i=>i.preventDefault(),t=i=>{const o=e(i);o&&(o.classList.add("atf-is-dragging"),document.body.classList.add("atf-drag-active"),window.getSelection()?.removeAllRanges(),document.addEventListener("selectstart",s))},a=i=>{const o=e(i);o&&(o.classList.remove("atf-is-dragging"),document.body.classList.remove("atf-drag-active"),document.removeEventListener("selectstart",s))};return document.addEventListener("os.drag.start",t),document.addEventListener("os.drag.end",a),()=>{document.removeEventListener("os.drag.start",t),document.removeEventListener("os.drag.end",a),document.removeEventListener("selectstart",s)}}const ls=["atfb","atfe","atfs","atfm"];function Ae(n,e,s,t,a){const i=e.getBoundingClientRect();if(!a){const o=ls.find(l=>e.closest(`.${l}`));if(o){const l=e.cloneNode(!0);l.style.transition="",l.style.transform="",a=document.createElement("div"),a.className=`${o} atf-ghost-scope`,a.appendChild(l)}}return a&&(a.style.width=`${Math.round(i.width)}px`,a.style.maxWidth=`${Math.round(i.width)}px`,a.style.boxSizing="border-box"),{type:n,source:e,data:s,ghost:{element:a,offsetX:t.clientX-i.left,offsetY:t.clientY-i.top,hint:{neutral:"",accept:"",reject:"",hidden:!0}}}}function st(n,e,s,t){const a=Array.from(n.querySelectorAll(e)).filter(i=>i!==t&&!i.classList.contains("atf-drag-ghost"));for(let i=0;ia.fetch(l,c,{source:"allterrain-forms"}):(l,c)=>fetch(l,c))(s,{credentials:"same-origin",...e,headers:t});if(!o.ok){let l=`Request failed with status ${o.status}.`,c="";try{const d=await o.json();l=d.message??l,c=d.code??""}catch{}throw new de(l,o.status,c)}if(o.status!==204)return await o.json()}const z=n=>Te(n);async function cs(n){if(!U?.wpRestUrl)throw new de("AllTerrain Forms is not configured on this page.",0);const e=U.nonce?{"X-WP-Nonce":U.nonce}:{},s=Le(),t=nt(U.wpRestUrl,n),a=s?.fetch?await s.fetch(t,{credentials:"same-origin",headers:e},{source:"allterrain-forms"}):await fetch(t,{credentials:"same-origin",headers:e});if(!a.ok)throw new de(`Request failed with status ${a.status}.`,a.status);return await a.json()}function ds(n){if(!n||!n.includes("&"))return n;const e=document.createElement("textarea");return e.innerHTML=n,e.value}const X=(n,e)=>Te(n,{method:"POST",body:JSON.stringify(e)}),he=n=>Te(n,{method:"DELETE"});function at(n){const e=new URLSearchParams;for(const[t,a]of Object.entries(n))a===void 0||a===""||a===!1||e.set(t,String(a));const s=e.toString();return s?`?${s}`:""}function ue(n){const e=n?.schema?.settings;return e&&(!e.themeOverrides||Array.isArray(e.themeOverrides))&&(e.themeOverrides={...e.themeOverrides}),n}const I={config:()=>z("/config"),mailpoet:()=>z("/mailpoet"),listForms:()=>z("/forms"),listArchivedForms:()=>z("/forms?archived=1"),archiveForm:n=>X(`/forms/${n}/archive`,{}),unarchiveForm:n=>X(`/forms/${n}/unarchive`,{}),getForm:n=>z(`/forms/${n}`).then(ue),createForm:n=>X("/forms",n).then(ue),updateForm:(n,e)=>X(`/forms/${n}`,e).then(ue),duplicateForm:n=>X(`/forms/${n}/duplicate`,{}).then(ue),deleteForm:n=>he(`/forms/${n}`),preview:(n,e)=>X(`/forms/${n}/preview`,e),pages:()=>cs("wp/v2/pages?per_page=100&status=publish&orderby=title&order=asc&_fields=id,title").then(n=>n.map(e=>({id:e.id,title:ds(e.title?.rendered??"")||`#${e.id}`}))),mergeTags:n=>z(`/forms/${n}/merge-tags`).then(e=>e.groups),analytics:(n,e="")=>z(`/forms/${n}/analytics${e?`?dimension=${encodeURIComponent(e)}`:""}`),demoStatus:()=>z("/demo"),demoSeed:n=>X("/demo",n?{count:n}:{}),demoRemove:()=>he("/demo"),listEntries:n=>z(`/entries${at(n)}`),getEntry:n=>z(`/entries/${n}`),updateEntry:(n,e)=>X(`/entries/${n}`,e),deleteEntry:n=>he(`/entries/${n}`),exportEntries:n=>z(`/entries/export${at(n)}`),listThemes:()=>z("/themes"),saveTheme:n=>X("/themes",n),deleteTheme:n=>he(`/themes/${n}`)};function it(n,e,s={}){if(!n)return null;let t;const a=(o,l)=>{const c=n[o];return c==null||!Number.isFinite(Number(c))?l:Number(c)},i=(o,l,c=1)=>Array.from({length:Math.floor((l-o)/c+1e-9)+1},(d,h)=>{const m=String(Number((o+h*c).toPrecision(12)));return{value:m,label:m}});if(n.type==="scale"){const o=Math.trunc(a("min",0));let l=Math.trunc(a("max",10));l<=o&&(l=o+10),t=i(o,Math.min(l,o+20))}else if(n.type==="rating")t=i(1,Math.max(2,Math.min(10,Math.abs(Math.trunc(a("max",5))))));else if(n.type==="switch"||n.type==="consent")t=[{value:"1",label:"Checked"},{value:"",label:"Unchecked"}];else if(n.type==="country")t=Object.entries(s).map(([o,l])=>({value:o,label:l}));else if(n.type==="range"){const o=a("min",0),l=a("max",100),c=n.step==="any"?0:a("step",1);if(c<=0||l999)return null;t=i(o,l,c)}else if(n.choices?.length||["select","multiselect","radio","checkboxes","image_choice","quiz","likert"].includes(n.type))t=(n.choices??[]).map(o=>({value:o.value,label:o.label||o.value}));else return null;return!t.some(o=>o.value===e)&&e!==""&&t.unshift({value:e,label:`${e} (stored value; unavailable)`,disabled:!0}),t.some(o=>o.value==="")||t.unshift({value:"",label:"Choose a value…",disabled:!0}),t}function r(n,e={}){const s=document.createElement(n);e.class&&(s.className=e.class),e.text!==void 0&&(s.textContent=e.text),e.html!==void 0&&(s.innerHTML=e.html),e.title&&(s.title=e.title),e.type&&"type"in s&&(s.type=e.type),e.value!==void 0&&"value"in s&&(s.value=e.value),e.placeholder!==void 0&&"placeholder"in s&&(s.placeholder=e.placeholder),e.href&&"href"in s&&(s.href=e.href);for(const[t,a]of Object.entries(e.attrs??{}))a===void 0||a===!1||s.setAttribute(t,a===!0?"":String(a));Object.assign(s.style,e.style??{});for(const[t,a]of Object.entries(e.on??{}))s.addEventListener(t,a);for(const t of e.children??[])t==null||t===!1||s.append(t);return s}function R(n){n.replaceChildren()}function B(n){return r("span",{class:`dashicons ${n.startsWith("dashicons-")?n:`dashicons-${n}`}`,attrs:{"aria-hidden":"true"}})}function w(n,e,s){const t=e.matches("input, select, textarea, button")?e:e.querySelector("input, select, textarea");if(t){const a=t.id||`atf-c-${Math.random().toString(36).slice(2,9)}`;return t.id=a,r("div",{class:"atfb-row",children:[r("label",{class:"atfb-row__label",text:n,attrs:{for:a}}),e,s?r("p",{class:"atfb-row__hint",text:s}):null]})}return r("div",{class:"atfb-row",children:[r("span",{class:"atfb-row__label",text:n}),e,s?r("p",{class:"atfb-row__hint",text:s}):null]})}function T(n,e,s=""){return r("input",{class:"atfb-input",type:"text",value:n,placeholder:s,on:{input:t=>e(t.target.value)}})}function V(n,e){if(Y("os-number-field")){const s=document.createElement("os-number-field");return s.setAttribute("value",n),s.classList.add("atfb-field"),s.addEventListener("os-input-change",t=>{e(String(t.detail?.value??""))}),s}return r("input",{class:"atfb-input",type:"number",value:n,on:{input:s=>e(s.target.value)}})}function pe(n,e,s=4){const t=r("textarea",{class:"atfb-input atfb-input--area",attrs:{rows:s},on:{input:a=>e(a.target.value)}});return t.value=n,t}function D(n,e,s){if(Y("os-select")&&Y("os-option")){const t=document.createElement("os-select");t.setAttribute("value",n),t.classList.add("atfb-field");for(const a of e){const i=document.createElement("os-option");i.setAttribute("value",a.value),a.disabled&&i.setAttribute("disabled",""),i.textContent=a.label,t.append(i)}return t.addEventListener("os-pick",a=>{s(String(a.detail?.value??""))}),t}return r("select",{class:"atfb-input atfb-select",on:{change:t=>s(t.target.value)},children:e.map(t=>r("option",{value:t.value,text:t.label,attrs:{selected:t.value===n,disabled:t.disabled}}))})}function q(n,e,s){if(Y("os-checkbox-label")){const a=document.createElement("os-checkbox-label");return a.setAttribute("label",n),a.classList.add("atfb-check"),e&&a.setAttribute("checked",""),a.addEventListener("os-checkbox-change",i=>{s(!!i.detail?.checked)}),a}const t=r("input",{type:"checkbox",on:{change:a=>s(a.target.checked)}});return t.checked=e,r("label",{class:"atfb-check",children:[t,r("span",{text:n})]})}function C(n,e,s="secondary",t){const a=[t?B(t):null,r("span",{text:n})];if(Y("os-button")){const i=document.createElement("os-button");i.setAttribute("variant",s),i.setAttribute("type","button"),i.classList.add("atfb-button",`atfb-button--${s}`),i.addEventListener("click",e);for(const o of a)o&&i.append(o);return i}return r("button",{class:`atfb-button atfb-button--${s}`,type:"button",on:{click:e},children:a})}function Y(n){return typeof customElements<"u"&&!!customElements.get(n)}const hs=["os-button","os-checkbox-label","os-number-field","os-select","os-option","os-segmented","os-segment","os-color-field","os-empty-state"];let fe=null;function us(){if(fe)return fe;const n=window.wp?.os;return fe=n?.loadComponents?n.loadComponents(hs).catch(()=>{}):Promise.resolve(),fe}async function ae(n,e=""){const s=window.wp?.os;return s?.confirm?s.confirm({title:e,message:n,danger:!0}):window.confirm(n)}function N(n,e="",s="info"){const t=window.wp?.os;if(t?.notify){t.notify({title:n,body:e,type:s});return}console.info(`[AllTerrain Forms] ${n}${e?`: ${e}`:""}`)}function ps(n){let e=0,s;return(...t)=>{s=t,!e&&(e=window.requestAnimationFrame(()=>{e=0,n(...s)}))}}function Me(n,e){let s=0;return(...t)=>{window.clearTimeout(s),s=window.setTimeout(()=>n(...t),e)}}function fs(n){try{return window.localStorage.getItem(n)??""}catch{return""}}function ms(n,e){try{window.localStorage.setItem(n,e)}catch{}}function Fe(n){const e=n.closest(".os-window__body");e&&(e.addEventListener("scroll",()=>{e.scrollTop&&(e.scrollTop=0),e.scrollLeft&&(e.scrollLeft=0)},{passive:!0}),e.scrollTop=0,e.scrollLeft=0)}function rt(){const n=[window];try{window.parent&&window.parent!==window&&n.push(window.parent)}catch{}return n}function bs(n){const e=n.wp?.os?.windowManager?.getAll?.()??[];for(const s of e){const t=s.iframe??s.element?.querySelector?.("iframe");if(t&&t.contentWindow===window)return s}return null}const gs=4e3,vs=100;function ys(n,e){const s=Date.now()+gs,t=()=>{const a=bs(n);if(a&&a.id!==e){typeof a.close=="function"?a.close():n.wp?.os?.windowManager?.remove?.(a.id);return}!a&&Date.now()0?{form:s}:void 0;s>0&&ks(s);for(const a of rt())if(a.wp?.os?.openWindow?.(e,{source:"handoff",params:t})){window.setTimeout(()=>ys(a,e),0);return}}function _s(){document.addEventListener("click",n=>{const e=n.target?.closest?.("[data-atf-open-window]");if(!e)return;n.preventDefault();const s=e.getAttribute("data-atf-open-window")??"";for(const t of rt())if(t.wp?.os?.openWindow?.(s,{source:"handoff"}))return})}const xs="allterrain-forms/requested-form";function ks(n){try{window.sessionStorage.setItem(xs,String(n))}catch{}}function ot(n){return`allterrain-forms/requested-form-${n}`}function Ss(n){try{const e=window.sessionStorage.getItem(ot(n));return window.sessionStorage.removeItem(ot(n)),Number(e)||0}catch{return 0}}const Ie={is:"is",is_not:"is not",contains:"contains",not_contains:"does not contain",starts_with:"starts with",ends_with:"ends with",greater:"is more than",less:"is less than",greater_equal:"is at least",less_equal:"is at most",empty:"is empty",not_empty:"has any answer"},De=["empty","not_empty"];function lt(n,e){const s=n.find(t=>t.id===e);return s?s.label||"an untitled question":null}function ct(n,e,s){return n.find(i=>i.id===e)?.choices?.find(i=>i.value===s)?.label||s}function Cs(n,e){const s=Ie[n.operator]??String(n.operator);if(De.includes(n.operator))return s;const t=ct(e,n.field,n.value);return`${n.operator==="is"?"":`${s} `}${t!==""?t:"(nothing)"}`}function me(n,e,s=0){const t=lt(e,n.field),a=Ie[n.operator]??String(n.operator);if(t===null)return[{kind:"field",text:"a question that no longer exists",fieldId:n.field,missing:!0}];const i=[{kind:"field",text:t,fieldId:n.field,missing:!1},{kind:"operator",text:a,operator:n.operator,ruleIndex:s}];if(De.includes(n.operator))return i;const o=ct(e,n.field,n.value);return i.push({kind:"value",text:o!==""?o:"(nothing)",raw:n.value,sourceId:n.field,ruleIndex:s}),i}function Es(n,e){const s=n.logic;if(!s?.enabled||!s.rules.length)return[];const t=[{kind:"verb",text:s.action==="hide"?"Hidden when":"Shown when"}];return s.rules.forEach((a,i)=>{i>0&&t.push({kind:"join",text:s.match==="all"?"and":"or"}),t.push(...me(a,e,i))}),t}function qe(n){return n.map(e=>e.text).join(" ")}function Ls(n,e){return qe(me(n,e))}function be(n){const e=[];for(const s of n){const t=s.logic;if(t?.enabled)for(const a of t.rules)!a.field||a.field===s.id||e.push({from:a.field,to:s.id,label:Ls(a,n),short:Cs(a,n),action:t.action,broken:lt(n,a.field)===null})}return e}function $s(n){const e=new Map,s=new Set;for(const t of be(n)){const a=`${t.from}->${t.to}`;t.broken||s.has(a)||(s.add(a),e.set(t.from,(e.get(t.from)??0)+1))}return e}const dt=10,ht=22,se="http://www.w3.org/2000/svg";class As{constructor(e){this.edges=[],this.teardowns=[],this.frame=0,this.host=e,this.svg=document.createElementNS(se,"svg"),this.svg.setAttribute("class","atfb-logicmap"),this.svg.setAttribute("aria-hidden","true"),this.svg.setAttribute("focusable","false"),e.append(this.svg);const s=()=>this.schedule();window.addEventListener("resize",s),this.teardowns.push(()=>window.removeEventListener("resize",s));const t=e.closest(".atfb__canvas")??e;if(t.addEventListener("scroll",s,{passive:!0}),this.teardowns.push(()=>t.removeEventListener("scroll",s)),typeof ResizeObserver<"u"){const a=new ResizeObserver(s);a.observe(e),this.teardowns.push(()=>a.disconnect())}}setEdges(e){this.edges=e,this.schedule()}highlight(e){this.svg.classList.toggle("is-focused",!!e),this.svg.querySelectorAll("[data-from]").forEach(s=>{const t=e&&(s.dataset.from===e||s.dataset.to===e);s.classList.toggle("is-lit",!!t)})}destroy(){this.teardowns.forEach(e=>e()),this.teardowns=[],this.svg.remove(),this.frame&&cancelAnimationFrame(this.frame)}schedule(){this.frame||(this.frame=requestAnimationFrame(()=>{this.frame=0,this.draw()}))}draw(){this.svg.replaceChildren();const e=this.host.getBoundingClientRect();this.svg.setAttribute("viewBox",`0 0 ${e.width} ${e.height}`),this.svg.setAttribute("width",String(e.width)),this.svg.setAttribute("height",String(e.height));const s=new Map;for(const t of this.edges){const a=this.cardRect(t.from),i=this.cardRect(t.to);if(!a||!i)continue;const o=s.get(t.from)??0;s.set(t.from,o+1),this.drawEdge(t,a,i,e,o)}}cardRect(e){const s=this.host.querySelector(`[data-atfb-card="${CSS.escape(e)}"]`);return s?s.getBoundingClientRect():null}drawEdge(e,s,t,a,i){const o=s.right-a.left,l=s.top+s.height/2-a.top,c=t.right-a.left,d=t.top+t.height/2-a.top,h=Math.max(0,a.width-dt-Math.max(o,c)),m=22+i*11+Math.min(22,Math.abs(d-l)/8),g=Math.max(12,Math.min(m,h*.4)),u=document.createElementNS(se,"path");u.setAttribute("d",`M ${o} ${l} C ${o+g} ${l}, ${c+g} ${d}, ${c} ${d}`),u.setAttribute("class",`atfb-logicmap__path is-${e.action}${e.broken?" is-broken":""}`),u.dataset.from=e.from,u.dataset.to=e.to;const f=document.createElementNS(se,"title");f.textContent=e.label,u.append(f);const y=document.createElementNS(se,"circle");y.setAttribute("cx",String(c)),y.setAttribute("cy",String(d)),y.setAttribute("r","3.5"),y.setAttribute("class",`atfb-logicmap__dot is-${e.action}${e.broken?" is-broken":""}`),y.dataset.from=e.from,y.dataset.to=e.to,this.svg.append(u,y);const p=document.createElementNS(se,"text");p.setAttribute("x",String(a.width-dt)),p.setAttribute("y",String(d+3)),p.setAttribute("text-anchor","end"),p.setAttribute("class","atfb-logicmap__label"),p.dataset.from=e.from,p.dataset.to=e.to,p.textContent=e.short.length>ht?`${e.short.slice(0,ht-1)}…`:e.short;const x=document.createElementNS(se,"title");x.textContent=e.label,p.append(x),this.svg.append(p)}}const Ts={text:"text",email:"text",url:"text",tel:"text",number:"text",password:"text",date:"text",time:"text",datetime:"text",date_range:"composite",textarea:"textarea",select:"select",country:"select",multiselect:"options",radio:"options",checkboxes:"options",image_choice:"options",quiz:"options",switch:"toggle",consent:"toggle",file:"file",range:"range",rating:"summary",scale:"summary",likert:"summary",signature:"summary",repeater:"repeater",color:"text",name:"composite",address:"composite",total:"text",hidden:"static",heading:"static",html:"static",divider:"static",spacer:"static",page_break:"static"};function Ms(n){return Ts[n]??"text"}function ut(n,e){const s=/^choice:(\d+):(label|value)$/.exec(e);return String(s?n.choices?.[Number(s[1])]?.[s[2]]??"":n[e]??"")}function Z(n){const{value:e,onInput:s,onCommit:t}=n,a=r("span",{class:`${n.class} atfb-editable`,text:e,attrs:{contenteditable:"plaintext-only",role:"textbox",spellcheck:"false","data-placeholder":n.placeholder}});return n.bind&&(a.dataset.atfbBind=n.bind),a.addEventListener("pointerdown",i=>i.stopPropagation()),a.addEventListener("input",()=>s(a.textContent??"")),a.addEventListener("keydown",i=>{i.key==="Enter"&&(i.preventDefault(),a.blur()),i.key==="Escape"&&(a.textContent=e,a.blur()),i.stopPropagation()}),a.addEventListener("blur",()=>t?.()),a}function Fs(n){const e=r("input",{class:"atf-choice__input",type:n==="checkboxes"||n==="multiselect"?"checkbox":"radio"});return e.disabled=!0,e}function pt(n,e,s){const t=Ms(n.type),a=Z({value:n.label,placeholder:"Write the question…",class:"atf-label",bind:"label",onInput:o=>s.edit(l=>{l.label=o}),onCommit:()=>s.restructure(()=>{})}),i=[t==="static"||t==="toggle"?null:a,Ds(n,e,t,s),Is(n,e,s)];return r("div",{class:"atfb-preview atf-form",children:[r("div",{class:`atf-field atf-field--${n.type}`,children:i})]})}function Is(n,e,s){if(!e?.supports.includes("hint"))return null;const t=Z({value:n.hint??"",placeholder:"Add a hint…",class:"atf-hint",bind:"hint",onInput:a=>s.edit(i=>{i.hint=a})});return t.setAttribute("aria-label","Hint"),r("p",{class:"atfb-preview__hint",children:[t]})}function Ds(n,e,s,t){switch(s){case"text":return Oe(n,"atf-input",t);case"textarea":return Oe(n,"atf-input atf-textarea",t,!0);case"select":return r("div",{class:"atfb-preview__select",children:[Oe(n,"atf-input atf-select",t)]});case"options":return Rs(n,t);case"toggle":return r("div",{class:n.type==="switch"?"atf-toggle atf-toggle--switch":"atf-toggle",children:[(()=>{const a=r("input",{class:"atf-toggle__input",type:"checkbox"});return a.disabled=!0,a})(),Z({value:n.label||"",placeholder:n.type==="consent"?"What are they agreeing to?…":"What does this turn on?…",class:"atf-toggle__label",bind:"label",onInput:a=>t.edit(i=>{i.label=a}),onCommit:()=>t.restructure(()=>{})})]});case"file":return r("div",{class:"atf-file",children:[r("input",{class:"atf-file__input",type:"file"})]});case"range":return r("input",{class:"atf-range__input",type:"range"});case"composite":return r("div",{class:"atf-composite__parts",children:[r("span",{class:"atf-input atfb-preview__ghost",text:""}),r("span",{class:"atf-input atfb-preview__ghost",text:""})]});case"static":return Ps(n,e,t);case"repeater":return qs(n,t);default:return r("div",{class:"atfb-preview__stack",children:[r("p",{class:"atfb-preview__summary",text:`${e?.label??n.type} — set this up in the panel on the right.`})]})}}function qs(n,e){const s=n.fields??[],t=r("div",{class:"atfb-repeater__zone",attrs:{"data-atfb-repeater-zone":n.id}});return s.forEach(a=>t.append(Os(n,a,e))),s.length||t.append(r("p",{class:"atfb-repeater__empty",text:"Drag fields from the palette in here — the visitor gets a fresh copy of each with every row they add."})),r("div",{class:"atfb-repeater",attrs:{"data-atfb-repeater":n.id},children:[r("div",{class:"atf-repeater__row atfb-repeater__frame",children:[r("div",{class:"atf-repeater__row-head",children:[r("span",{class:"atf-repeater__title atfb-repeater__title",children:[Z({value:String(n.itemLabel??""),placeholder:"Row",class:"atfb-repeater__item-label",bind:"itemLabel",onInput:a=>e.edit(i=>{i.itemLabel=a})}),r("span",{class:"atfb-repeater__ordinal",text:"1",attrs:{"aria-hidden":"true"}})]})]}),t]}),Ns(n,e)]})}function Os(n,e,s){const t=e.id,a=l=>c=>{const d=(c.fields??[]).find(h=>h.id===t);d&&l(d)},i={edit:l=>s.edit(a(l)),restructure:l=>s.restructure(a(l)),types:s.types,selectedId:s.selectedId},o=s.types?.(e.type);return r("div",{class:`atfb-subcard${s.selectedId===t?" is-selected":""}`,attrs:{"data-atfb-subfield":t,"data-atfb-parent":n.id,tabindex:"0",role:"button","aria-label":`${e.label||o?.label||e.type}, inside ${n.label||"the repeater"}`},children:[r("div",{class:"atfb-subcard__head",children:[r("span",{class:"atfb-subcard__type",text:o?.label??e.type}),e.required?r("span",{class:"atfb-subcard__required",text:"Required"}):null,r("button",{class:"atfb-preview__remove",type:"button",title:"Remove this field from the repeater",attrs:{"aria-label":`Remove ${e.label||o?.label||"this field"}`},on:{pointerdown:l=>l.stopPropagation(),click:l=>{l.stopPropagation(),s.restructure(c=>{const d=c.fields??[],h=d.findIndex(m=>m.id===t);h>=0&&d.splice(h,1)})}},children:[r("span",{text:"×"})]})]}),pt(e,o,i)]})}function Oe(n,e,s,t=!1){const a=Z({value:n.placeholder??"",placeholder:n.type==="select"||n.type==="country"?"Choose…":"Placeholder…",class:`${e} atfb-preview__box${t?" atfb-preview__box--tall":""}`,bind:"placeholder",onInput:i=>s.edit(o=>{o.placeholder=i})});return a.setAttribute("aria-label","Placeholder"),a}function Ne(n,e,s,t,a,i){return r("span",{class:`atf-button ${s} atfb-preview__button`,children:[Z({value:n,placeholder:e,class:"atfb-preview__button-text",bind:t,onInput:o=>a.edit(l=>i(l,o))})]})}function Ns(n,e){return Ne(String(n.addLabel??""),"Add another","atf-button--ghost atfb-repeater__add","addLabel",e,(s,t)=>{s.addLabel=t})}function Rs(n,e){const s=r("div",{class:"atf-choices__list"});return(n.choices??[]).forEach((t,a)=>{s.append(r("div",{class:"atf-choice atfb-preview__option",children:[Fs(n.type),Z({value:t.label,placeholder:"Option…",class:"atf-choice__label",bind:`choice:${a}:label`,onInput:i=>{e.edit(o=>{const l=o.choices?.[a];if(!l)return;const c=!l.value||l.value===l.label;l.label=i,c&&(l.value=i)})}}),r("button",{class:"atfb-preview__remove",type:"button",title:"Remove this option",attrs:{"aria-label":`Remove ${t.label||"this option"}`},on:{pointerdown:i=>i.stopPropagation(),click:i=>{i.stopPropagation(),e.restructure(o=>{o.choices.splice(a,1)})}},children:[r("span",{text:"×"})]})]}))}),s.append(r("button",{class:"atfb-preview__add",type:"button",on:{pointerdown:t=>t.stopPropagation(),click:t=>{t.stopPropagation(),e.restructure(a=>{const i=a.choices.length+1;a.choices.push({label:`Option ${i}`,value:`Option ${i}`})})}},children:[r("span",{text:"+ Add option"})]})),r("fieldset",{class:"atf-choices",children:[s]})}function Ps(n,e,s){return n.type==="heading"?Z({value:n.label,placeholder:"Section heading…",class:"atf-heading",bind:"label",onInput:t=>s.edit(a=>{a.label=t}),onCommit:()=>s.restructure(()=>{})}):n.type==="divider"?r("hr",{class:"atf-divider"}):n.type==="page_break"?r("div",{class:"atfb-preview__stack",children:[r("p",{class:"atfb-progress-name",children:[Z({value:n.label,placeholder:"Name this step…",class:"atf-progress__label",bind:"label",onInput:t=>s.edit(a=>{a.label=t}),onCommit:()=>s.restructure(()=>{})})]}),r("p",{class:"atfb-preview__summary",text:"Everything after this is a new page."}),r("div",{class:"atf-nav atfb-preview__nav",children:[Ne(String(n.prevLabel??""),"Back","atf-button--secondary","prevLabel",s,(t,a)=>{t.prevLabel=a}),Ne(String(n.nextLabel??""),"Next","","nextLabel",s,(t,a)=>{t.nextLabel=a})]})]}):r("p",{class:"atfb-preview__summary",text:`${e?.label??n.type} — nothing is shown to the visitor here.`})}const Re=new Map;function ft(n){let e=Re.get(n);return e||(e=I.mergeTags(n).catch(()=>[]),Re.set(n,e)),e}function Bs(n){Re.delete(n)}function js(n){const e=new Map;for(const s of n)for(const t of s.items)e.set(t.tag,t);return e}function Hs(n,e){const s=js(e);return n.replace(/\{[a-z_]+(?::[^}]*)?\}/gi,t=>{const a=s.get(t.toLowerCase());return a?`{the value of ${a.label}}`:t})}function mt(n){return/\{[a-z_]+(?::[^}]*)?\}/i.test(n)}let ge=null,Pe=0,ie=null;function re(n=!1){Pe++,ge?.remove(),ge=null,n&&ie?.focus(),ie=null}typeof document<"u"&&(document.addEventListener("pointerdown",n=>{const e=n.target;e?.closest(".atfb-tagpick__open")||ge?.contains(e)||re()}),document.addEventListener("keydown",n=>{n.key==="Escape"&&ie&&(re(!0),n.preventDefault(),n.stopImmediatePropagation())},!0));function bt(n,e){const s=n.selectionStart??n.value.length,t=n.selectionEnd??n.value.length;n.value=n.value.slice(0,s)+e+n.value.slice(t);const a=s+e.length;n.setSelectionRange(a,a),n.dispatchEvent(new Event("input",{bubbles:!0})),n.focus()}function Ws(n){let e=0,s=window.innerHeight,t=n.parentElement;for(;t&&t!==document.body;){if(/auto|scroll|hidden|clip/.test(getComputedStyle(t).overflowY)){const a=t.getBoundingClientRect();e=Math.max(e,a.top),s=Math.min(s,a.bottom)}t=t.parentElement}return{top:e,bottom:s}}function Us(n,e){const s=r("input",{class:"atfb-input atfb-tagpick__search",type:"search",placeholder:"Search values…",attrs:{"aria-label":"Search values"}}),t=r("div",{class:"atfb-tagpick__list"}),a=o=>{t.replaceChildren();const l=o.trim().toLowerCase();let c=0;for(const d of n){const h=d.items.filter(m=>!l||m.label.toLowerCase().includes(l)||m.tag.toLowerCase().includes(l));if(!h.length){d.empty&&!l&&!d.items.length&&t.append(r("p",{class:"atfb-tagpick__group",text:d.label}),r("p",{class:"atfb-tagpick__empty",text:d.empty}));continue}t.append(r("p",{class:"atfb-tagpick__group",text:d.label}));for(const m of h)c+=1,t.append(r("button",{class:"atfb-tagpick__item",type:"button",on:{click:()=>{re(),e(m.tag)}},children:[r("span",{class:"atfb-tagpick__item-main",children:[r("span",{class:"atfb-tagpick__label",text:m.label}),r("code",{class:"atfb-tagpick__tag",text:m.tag})]}),m.hint||m.sample?r("span",{class:"atfb-tagpick__meta",text:`{the value of ${m.label}}`}):null]}))}!c&&l&&t.append(r("p",{class:"atfb-tagpick__empty",text:`Nothing matches “${o}”.`}))};a(""),s.addEventListener("input",()=>a(s.value));const i=r("div",{class:"atfb-tagpick",attrs:{role:"dialog","aria-label":"Insert a value"},children:[r("p",{class:"atfb-tagpick__intro",text:"Pick something to drop in. It is filled in when the form is submitted."}),s,t]});return i.addEventListener("keydown",o=>{const l=[...t.querySelectorAll("button")],c=l.indexOf(document.activeElement);if(o.key==="ArrowDown"||o.key==="ArrowUp"){o.preventDefault();const d=o.key==="ArrowDown"?c+1:c<0?l.length-1:c-1;l[(d+l.length)%l.length]?.focus()}else o.key==="Enter"&&o.target===s&&(o.preventDefault(),l[0]?.click());o.stopPropagation()}),i}function Be(n,e){const s=()=>Promise.resolve(e.groups?e.groups():ft(e.formId??0)),t=r("button",{class:"atfb-button atfb-button--ghost atfb-tagpick__open",type:"button",title:"Insert a value from the submission",attrs:{"aria-haspopup":"dialog"},children:[B("shortcode"),r("span",{text:"Insert a value"})]}),a=r("div",{class:"atfb-taggable",children:[n,r("div",{class:"atfb-taggable__tools",children:[t]})]}),i=e.preview===!1?null:r("p",{class:"atfb-taggable__preview"});i&&a.append(i);const o=()=>{if(i){if(!mt(n.value)){i.textContent="",i.hidden=!0;return}s().then(c=>{mt(n.value)&&(i.hidden=!1,i.replaceChildren(r("span",{class:"atfb-taggable__preview-label",text:"Reads as"}),r("span",{text:Hs(n.value,c)})))})}};n.addEventListener("input",o),o();const l=(c,d)=>{re();const h=Pe,m=n.value;ie=n,s().then(g=>{if(h!==Pe||!a.isConnected||n.value!==m)return;const u=Us(g,S=>{n.setSelectionRange(c,d),bt(n,S)});a.append(u),ge=u;const f=Ws(a);u.style.maxBlockSize=`${Math.max(0,Math.min(320,f.bottom-f.top-8))}px`;const y=a.getBoundingClientRect(),p=u.getBoundingClientRect().height,x=y.bottom+p<=f.bottom?y.bottom:y.top-p,F=Math.max(f.top+4,Math.min(x,f.bottom-p-4));u.style.insetBlockStart=`${F-y.top}px`,u.querySelector(".atfb-tagpick__search")?.focus({preventScroll:!0})})};return t.addEventListener("click",c=>{if(c.stopPropagation(),ie===n){re(!0);return}l(n.selectionStart??n.value.length,n.selectionEnd??n.value.length)}),n.addEventListener("input",c=>{const d=c,h=n.selectionStart??0;!d.isComposing&&d.data==="{"&&n.value[h-1]==="{"&&l(h-1,h+(n.value[h]==="}"?1:0))}),a}function gt(n,e){const s=parseFloat(String(n??""));return Number.isFinite(s)?s:e}function vt(n,e){let s=e[0];for(const t of e)Math.abs(t.at-n)n.accent??"#2271b1",apply:n=>({accent:n,"accent-soft":`color-mix( in srgb, ${n} 12%, transparent )`,"border-focus":n,"focus-ring-color":n,"button-bg":n,"button-bg-hover":`color-mix( in srgb, ${n} 88%, #000 )`})},{id:"roundness",label:"Roundness",hint:"Corners, everywhere at once.",kind:"scale",steps:je.map(({value:n,label:e})=>({value:n,label:e})),owns:["radius-field","radius-button","radius-card","radius-check"],read:n=>vt(gt(n["radius-field"],4),je),apply:n=>{const e=je.find(s=>s.value===n)?.at??4;return{"radius-field":`${e}px`,"radius-button":`${e}px`,"radius-card":`${Math.min(e*2,28)}px`,"radius-check":`${Math.min(e,6)}px`}}},{id:"density",label:"Density",hint:"How much air the form has.",kind:"scale",steps:He.map(({value:n,label:e})=>({value:n,label:e})),owns:["pad-field-x","pad-field-y","gap-fields","gap-label","button-pad-x","button-pad-y"],read:n=>vt(gt(n["pad-field-y"],9),He),apply:n=>{const e=He.find(s=>s.value===n)?.at??9;return{"pad-field-y":`${e}px`,"pad-field-x":`${Math.round(e*1.4)}px`,"gap-fields":`${Math.round(e*2.2)}px`,"gap-label":`${Math.max(4,Math.round(e*.7))}px`,"button-pad-y":`${e+2}px`,"button-pad-x":`${Math.round(e*2.2)}px`}}},{id:"shadow",label:"Depth",hint:"How far the form sits off the page.",kind:"scale",steps:zs,owns:["shadow-field","shadow-field-focus","shadow-button","shadow-button-hover","shadow-card"],read:n=>{const e=n["shadow-card"]??"none";return e==="none"||e.trim()===""?n["shadow-field"]&&n["shadow-field"]!=="none"?"hard":"none":e.includes("0 1px")||e.includes("2px")?"subtle":"lifted"},apply:n=>{switch(n){case"subtle":return{"shadow-field":"none","shadow-field-focus":"none","shadow-button":"0 1px 2px rgba( 0, 0, 0, 0.12 )","shadow-button-hover":"0 2px 4px rgba( 0, 0, 0, 0.16 )","shadow-card":"0 1px 3px rgba( 0, 0, 0, 0.1 )"};case"lifted":return{"shadow-field":"none","shadow-field-focus":"0 4px 12px rgba( 0, 0, 0, 0.12 )","shadow-button":"0 4px 10px rgba( 0, 0, 0, 0.16 )","shadow-button-hover":"0 8px 20px rgba( 0, 0, 0, 0.2 )","shadow-card":"0 10px 30px rgba( 0, 0, 0, 0.14 )"};case"hard":return{"shadow-field":"3px 3px 0 currentColor","shadow-field-focus":"5px 5px 0 currentColor","shadow-button":"4px 4px 0 currentColor","shadow-button-hover":"2px 2px 0 currentColor","shadow-card":"none"};default:return{"shadow-field":"none","shadow-field-focus":"none","shadow-button":"none","shadow-button-hover":"none","shadow-card":"none"}}}},{id:"field-style",label:"Fields",hint:"How an input is drawn.",kind:"choice",steps:Ys,owns:["field-style"],read:n=>n["field-style"]??"outline",apply:n=>({"field-style":n})},{id:"labels",label:"Labels",hint:"Where the question sits relative to the answer.",kind:"choice",steps:Vs,owns:["label-position"],read:n=>n["label-position"]??"top",apply:n=>({"label-position":n})}]}function Ks(n){return We().find(e=>e.owns.includes(n))??null}function yt(n){let e=n.activeSlug,s={...n.overrides},t=n.themes;const a=v=>{s=v,n.onOverridesReplaced?.({...v})},i=r("div",{class:"atfs-preview__frame"}),o=r("div",{class:"atfs-controls__body"}),l=r("div",{class:"atfs-quick"}),c=r("div",{class:"atfs-themes"}),d=Me(async()=>{try{const v=await n.previewFor(e,s),_=i.closest(".atf-studio__preview"),k=_?.scrollTop??0;i.innerHTML=v,_&&(_.scrollTop=k),document.dispatchEvent(new CustomEvent("alltfo-refresh"))}catch(v){R(i),i.append(r("p",{class:"atfb-error",text:v instanceof Error?v.message:"Preview failed."}))}},180),h={"label-position":"atf-labels-","field-style":"atf-fields-"},m=(v,_,k)=>{const E=k.replace(/[^a-z0-9_-]/gi,"");for(const A of[...v.classList])A.startsWith(_)&&v.classList.remove(A);E&&v.classList.add(_+E)},g=v=>{const _=i.querySelector(".atf-form-wrap"),k=_?.querySelector(".atf-form");if(_)for(const[E,A]of Object.entries(v))A===""?_.style.removeProperty(`--atf-${E}`):_.style.setProperty(`--atf-${E}`,A),k&&h[E]&&m(k,h[E],A||(t.find(P=>P.slug===e)?.resolved[E]??""))},u=v=>{g(v.resolved);const _=i.querySelector(".atf-form");_&&(m(_,"atf-theme-",v.slug),_.classList.toggle("atf-is-dark",!!v.dark))},f=v=>s[v.token]!==void 0?s[v.token]:t.find(k=>k.slug===e)?.resolved?.[v.token]??v.default,y=()=>{R(c);for(const v of t){const _=r("button",{class:`atfs-theme${v.slug===e?" is-active":""}`,type:"button",attrs:{"aria-pressed":v.slug===e,title:v.description},children:[r("span",{class:"atfs-theme__chip",style:{background:v.resolved.surface??"#fff",borderColor:v.resolved.border??"#ccc",borderRadius:v.resolved["radius-field"]??"4px",boxShadow:v.resolved["shadow-card"]??"none"},children:[r("span",{class:"atfs-theme__accent",style:{background:v.resolved.accent??"#2271b1",borderRadius:v.resolved["radius-button"]??"4px"}})]}),r("span",{class:"atfs-theme__name",text:v.label}),v.custom?r("span",{class:"atfb-badge",text:"yours"}):null],on:{click:()=>{e=v.slug,a({}),n.onTheme(e),u(v),y(),S(),F(),ts()}}});c.append(_)}},p=()=>{const _={...t.find(k=>k.slug===e)?.resolved??{}};for(const[k,E]of Object.entries(s))_[k]=E;return _},x=v=>{const _=l.closest(".atf-studio__controls"),k=_?.scrollTop??0;v(),_&&(_.scrollTop=k)},F=Me(()=>x(b),150),S=()=>{const v=p(),_=l.querySelectorAll(".atfs-dial");We().forEach((k,E)=>{const A=_[E];if(!A)return;const P=k.read(v);if(k.kind==="colour"){const O=A.querySelector('input[type="color"]'),K=A.querySelector("input.atfb-input");O&&document.activeElement!==O&&(O.value=ve(P)),K&&document.activeElement!==K&&(K.value=P);return}A.querySelectorAll(".atfs-segment").forEach((O,K)=>{const Q=k.steps?.[K]?.value===P;O.classList.toggle("is-on",Q),O.setAttribute("aria-pressed",String(Q))})})},H=(v,_)=>{const k=v.apply(_,p());for(const[E,A]of Object.entries(k))s[E]=A,n.onOverride(E,A);g(k),S(),F()},L=()=>{R(l);const v=p();for(const _ of We()){const k=_.read(v);let E;if(_.kind==="colour"){const A=r("input",{class:"atfs-color",type:"color",value:ve(k),attrs:{"aria-label":_.label},on:{input:P=>H(_,P.target.value)}});E=r("div",{class:"atfs-color-row",children:[A,T(k,P=>H(_,P))]})}else E=r("div",{class:`atfs-segmented atfs-segmented--${_.kind}`,attrs:{role:"group","aria-label":_.label},children:(_.steps??[]).map(A=>r("button",{class:`atfs-segment${A.value===k?" is-on":""}`,type:"button",text:A.label,attrs:{"aria-pressed":A.value===k},on:{click:()=>H(_,A.value)}}))});l.append(r("div",{class:"atfs-dial",children:[r("div",{class:"atfs-dial__head",children:[r("span",{class:"atfs-dial__label",text:_.label}),r("span",{class:"atfs-dial__hint",text:_.hint})]}),E]}))}},b=()=>{R(o);const v=new Map;for(const k of n.tokens){const E=v.get(k.group)??[];E.push(k),v.set(k.group,E)}const _={colour:"Colour",shape:"Corners and borders",shadow:"Shadows",fields:"Field style",space:"Spacing",type:"Type",labels:"Labels",button:"Buttons",focus:"Focus ring",motion:"Motion"};for(const[k,E]of v)o.append(r("details",{class:"atfs-group",attrs:{open:k==="colour"},children:[r("summary",{text:_[k]??k}),...E.map(A=>M(A))]}))},M=v=>{const _=f(v),k=O=>{O===""?delete s[v.token]:s[v.token]=O,n.onOverride(v.token,O),g({[v.token]:O})};let E;if(v.control==="color"){const O=r("input",{class:"atfs-color",type:"color",value:ve(_),attrs:{"aria-label":`${v.label} colour`},on:{input:Q=>{const ce=Q.target.value;K.value=ce,k(ce)}}}),K=T(_,Q=>{O.value=ve(Q),k(Q)});E=r("div",{class:"atfs-color-row",children:[O,K]})}else if(v.control==="select")E=D(_,(v.options??[]).map(O=>({value:O,label:O})),k);else if(v.control==="length"){const O=parseFloat(_)||0,K=v.unit??"px",Q=v.token.includes("radius")?60:80,ce=r("input",{class:"atfs-range",type:"range",value:String(O),attrs:{min:"0",max:String(Q),step:"1","aria-label":v.label},on:{input:Ee=>{const ns=`${Ee.target.value}${K}`;ss.value=ns,k(ns)}}}),ss=T(_,Ee=>{ce.value=String(parseFloat(Ee)||0),k(Ee)});E=r("div",{class:"atfs-length-row",children:[ce,ss]})}else E=T(_,k);const A=w(v.label,E),P=Ks(v.token);return P&&(A.classList.add("is-dialled"),A.append(r("span",{class:"atfs-owned",text:P.label}))),s[v.token]!==void 0&&(A.classList.add("is-overridden"),A.append(r("button",{class:"atfs-reset",type:"button",text:"Reset",attrs:{"aria-label":`Reset ${v.label} to the theme's value`},on:{click:()=>{k(""),x(b)}}}))),A},$=async()=>{const v=t.find(E=>E.slug===e),_=v?`${v.label} (mine)`:"My theme",k=window.prompt("Name this theme",_);if(k)try{const E={};for(const P of n.tokens){const O=f(P);O!==P.default&&(E[P.token]=O)}const A=await I.saveTheme({label:k,tokens:E});t=[...t.filter(P=>P.slug!==A.slug),A],n.onThemesChanged(t),e=A.slug,a({}),n.onTheme(e),y(),S(),x(b),N("Theme saved",A.label)}catch(E){N("Could not save the theme",E instanceof Error?E.message:"","error")}},W=async()=>{const v=t.find(_=>_.slug===e);if(v?.custom&&await ae(`Delete “${v.label}”? Forms using it fall back to Clean.`,"Delete theme"))try{await I.deleteTheme(v.id),t=t.filter(k=>k.slug!==v.slug),n.onThemesChanged(t),e="clean",a({}),n.onTheme(e),y(),S(),x(b);const _=t.find(k=>k.slug===e);_&&u(_)}catch(_){N("Could not delete the theme",_ instanceof Error?_.message:"","error")}},j=async()=>{const v=t.find(E=>E.slug===e),_={label:v?.label??e,tokens:{...v?.tokens??{},...s}},k=JSON.stringify(_,null," ");try{await navigator.clipboard.writeText(k),N("Theme copied","Paste it into another site to import it.")}catch{window.prompt("Copy this theme",k)}},le=()=>{const v=window.prompt("Paste a theme");if(v)try{const _=JSON.parse(v);if(!_.tokens||typeof _.tokens!="object")throw new Error("That does not look like a theme.");for(const[k,E]of Object.entries(_.tokens))s[k]=String(E),n.onOverride(k,String(E));g({...s}),S(),x(b)}catch(_){N("Could not read that theme",_ instanceof Error?_.message:"","error")}},es=C("Delete",()=>void W(),"danger"),ts=()=>{const v=t.find(_=>_.slug===e);es.hidden=!v?.custom};return y(),L(),b(),d(),ts(),r("div",{class:"atf-studio",children:[r("div",{class:"atf-studio__top",children:[r("div",{class:"atf-studio__topbar",children:[r("h2",{class:"atf-studio__heading",text:"Theme"}),r("div",{class:"atfs__actions",children:[C("Save as a theme",()=>void $(),"primary"),C("Export",()=>void j()),C("Import",le),es]})]}),c]}),r("div",{class:"atf-studio__panes",children:[r("aside",{class:"atf-studio__controls",children:[r("p",{class:"atfb-hint",text:"Changes apply to this form only, until you save them as a theme."}),l,r("details",{class:"atfs-advanced",children:[r("summary",{text:"Every setting"}),r("p",{class:"atfb-hint",text:"The full token list. The dials above write into it."}),o]})]}),r("main",{class:"atf-studio__preview",children:[r("h2",{class:"screen-reader-text",text:"Live preview"}),i]})]})]})}async function Ue(){const n=document.querySelector("[data-atfs-root]");if(!n||n.dataset.atfsMounted)return;n.dataset.atfsMounted="1",Fe(n);const e=n.querySelector("[data-atfs-bar]"),s=n.querySelector(".atfs__body")??n;try{const[t,a,i]=await Promise.all([I.config(),I.listThemes(),I.listForms()]);if(!i.length){R(s),s.append(r("div",{class:"atfb-empty",children:[r("h2",{text:"No forms to preview"}),r("p",{text:"Make a form first — a theme needs something to dress."})]}));return}let o=i[0],l={},c=o.theme;const d=()=>{R(s),s.append(yt({themes:a,tokens:t.tokens,activeSlug:c,overrides:l,standalone:!0,onTheme:h=>{c=h},onOverride:(h,m)=>{m===""?delete l[h]:l[h]=m},onOverridesReplaced:h=>{l={...h}},previewFor:async(h,m)=>{const g=await I.getForm(o.id);g.schema.settings.theme=h,g.schema.settings.themeOverrides=m;const{html:u}=await I.preview(o.id,{schema:g.schema,theme:h});return u},onThemesChanged:h=>{a.length=0,a.push(...h)}}))};e&&(R(e),e.append(r("span",{class:"atfs__label",text:"Preview against"}),D(String(o.id),i.map(h=>({value:String(h.id),label:h.title||"(untitled)"})),h=>{o=i.find(m=>String(m.id)===h)??i[0],l={},d()}))),d()}catch(t){R(s),s.append(r("p",{class:"atfb-error",text:t instanceof Error?t.message:"Could not load themes."}))}}typeof document<"u"&&(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>void Ue()):Ue(),document.addEventListener("os-window-content-loaded",()=>void Ue()));function ve(n){const e=n.trim();return/^#[0-9a-f]{6}$/i.test(e)?e:/^#[0-9a-f]{3}$/i.test(e)?`#${e[1]}${e[1]}${e[2]}${e[2]}${e[3]}${e[3]}`:"#888888"}const Gs="mailpoet",ye="",Xs=["checkbox","checkboxes","toggle","consent","gdpr"],wt=["email"];function _t(n){return n.schema.actions.find(e=>e.type==="mailpoet")}function ze(n){const e=n?.settings??{};return{lists:Array.isArray(e.lists)?e.lists.map(Number).filter(Boolean):[],email_field:typeof e.email_field=="string"?e.email_field:"",first_name_field:typeof e.first_name_field=="string"?e.first_name_field:"",last_name_field:typeof e.last_name_field=="string"?e.last_name_field:""}}function Zs(n){return n?.logic?.enabled?n.logic.rules[0]?.field??ye:ye}function xt(n,e,s){const t=n.find(a=>e.includes(a.type));return t?t.id:s?n.find(a=>s.test(a.label))?.id??"":""}function Js(n,e,s){const t=r("section",{class:"atfm-form"}),a=l=>r("span",{class:`atfm-form__mark${l?"":" atfm-form__mark--off"}`,children:[r("img",{attrs:{src:e.symbol,alt:"",width:"20",height:"20"}})]}),i=l=>{R(t),t.classList.remove("is-open"),t.append(r("div",{class:"atfm-form__head",children:[a(l.length>0),r("div",{class:"atfm-form__title",children:[r("strong",{text:n.title||`Form ${n.id}`}),l.length?r("span",{class:"atfm-form__status is-on",text:`Subscribing → ${l.join(", ")}`}):r("span",{class:"atfm-form__status",text:"Not connected"})]}),C(l.length?"Edit":"Connect",()=>void o(),"secondary")]}))},o=async()=>{t.classList.add("is-open"),R(t),t.append(r("p",{class:"atfm-hint",text:"Loading the form…"}));let l;try{l=await I.getForm(n.id)}catch{R(t),t.append(r("p",{class:"atfm-hint",text:"Could not load this form."}));return}const c=l.schema.fields.filter(b=>b.type!=="page_break"),d=_t(l),h=ze(d),m=new Set(h.lists.filter(b=>e.lists.some(M=>M.id===b)));let g=Zs(d),u=h.email_field||xt(c,wt,/mail/i),f=h.first_name_field,y=h.last_name_field;c.some(b=>b.id===u)||(u=xt(c,wt,/mail/i));const p=c.filter(b=>Xs.includes(b.type));!d&&p.length&&(g=p[0].id);const x=(b,M)=>[{value:"",label:M},...b.map($=>({value:$.id,label:$.label||$.id}))],F=r("div",{class:"atfm-lists"});(()=>{R(F);for(const b of e.lists){const M=r("input",{attrs:{type:"checkbox"}});M.checked=m.has(b.id),M.addEventListener("change",()=>{M.checked?m.add(b.id):m.delete(b.id)}),F.append(r("label",{class:"atfm-list",children:[M,r("span",{text:b.name})]}))}})();const H=async(b=!1)=>{const M=l.schema.actions.filter($=>$.type!=="mailpoet");if(!b){if(!m.size){N("Pick a list","A subscription needs at least one MailPoet list.","error");return}if(!u){N("Pick the email field","MailPoet needs to know which answer is the address.","error");return}M.push({id:Gs,type:"mailpoet",enabled:!0,logic:g===ye?{enabled:!1,action:"show",match:"all",rules:[]}:{enabled:!0,action:"show",match:"all",rules:[{field:g,operator:"not_empty",value:""}]},settings:{lists:[...m],email_field:u,first_name_field:f,last_name_field:y}})}l.schema.actions=M;try{await I.updateForm(l.id,{schema:l.schema})}catch($){N("Could not save",$ instanceof Error?$.message:"","error");return}N(b?"Disconnected":"Connected to MailPoet",b?`${n.title} no longer subscribes anyone.`:`${n.title} now subscribes opted-in visitors.`),i(b?[]:e.lists.filter($=>m.has($.id)).map($=>$.name))};R(t);const L=b=>r("span",{class:"atfm-form__section",text:b});t.append(r("div",{class:"atfm-form__head",children:[a(!!d),r("div",{class:"atfm-form__title",children:[r("strong",{text:n.title||`Form ${n.id}`})]}),C("Close",()=>i(d?e.lists.filter(b=>ze(d).lists.includes(b.id)).map(b=>b.name):[]),"ghost")]}),r("div",{class:"atfm-form__body",children:[L("Where they land"),w("Lists",F,"MailPoet sends its own confirmation email before anyone is truly on a list."),L("Who they are"),w("Email address",D(u,x(c,"— pick a field —"),b=>{u=b}),"The answer that carries their address."),w("First name",D(f,x(c,"— none —"),b=>{f=b})),w("Last name",D(y,x(c,"— none —"),b=>{y=b})),L("When to subscribe"),w("Subscribe",D(g,[...p.map(b=>({value:b.id,label:`Only when “${b.label||b.id}” is ticked`})),{value:ye,label:"Everyone who submits"}],b=>{g=b}),p.length?"Bind it to a box they tick. Subscribing everyone is rarely what visitors expect.":"Add a checkbox or toggle field to the form to offer a proper opt-in."),r("div",{class:"atfm-form__actions",children:d?[C("Save connection",()=>void H(),"primary"),C("Disconnect",()=>void H(!0),"danger")]:[C("Save connection",()=>void H(),"primary")]})]}))};return i([]),I.getForm(n.id).then(l=>{const c=_t(l);if(c&&!t.classList.contains("is-open")){const d=e.lists.filter(h=>ze(c).lists.includes(h.id)).map(h=>h.name);i(d)}}).catch(()=>{}),s.append(t),t}function Qs(n){const e=n.logo?r("span",{class:"atfm-hero__logo",children:[r("img",{attrs:{src:n.logo,alt:"MailPoet",width:"210",height:"105"}})]}):r("span",{class:"atfm-hero__logo atfm-hero__logo--glyph",text:"📬"});return r("header",{class:"atfm-hero",children:[e,r("div",{class:"atfm-hero__words",children:[r("h1",{text:"Turn submissions into subscribers"}),r("p",{text:"Every submission is someone choosing to talk to you. Connect a form to MailPoet and the ones who opt in land on your lists by themselves — named, consented, and confirmed by MailPoet’s own double opt-in."}),n.active?r("p",{class:"atfm-hero__status is-on",text:n.lists.length===1?"Connected — 1 list ready for subscribers":`Connected — ${n.lists.length} lists ready for subscribers`}):r("p",{class:"atfm-hero__status",text:"MailPoet is not installed on this site yet"})]})]})}function en(){const n=(s,t,a)=>r("div",{class:"atfm-why__card",children:[r("span",{class:"atfm-why__icon",text:s}),r("strong",{text:t}),r("p",{text:a})]}),e=(s,t)=>r("a",{text:t,attrs:{href:s,target:"_blank",rel:"noreferrer"}});return[r("div",{class:"atfm-why",children:[n("✉️","Beautiful emails, made in WordPress","Design newsletters and welcome emails in a drag-and-drop editor that lives in your own admin — no external account to juggle."),n("🤝","Consent you can stand behind","Double opt-in out of the box: every address your forms send over is confirmed by the visitor before a single campaign reaches it."),n("🚀","Free to grow with","Free up to 500 subscribers, welcome automations, WooCommerce emails and open-rate stats included — a paid addon anywhere else.")]}),r("p",{class:"atfm-links",children:[e("https://www.mailpoet.com/features/","Explore MailPoet’s features ↗"),e("https://kb.mailpoet.com/","Guides & docs ↗"),e("https://www.mailpoet.com/pricing/","Plans & the free tier ↗")]})]}function tn(){const n=(e,s)=>r("div",{class:"atfm-step",children:[r("span",{class:"atfm-step__n",text:e}),r("span",{text:s})]});return r("div",{class:"atfm-steps",children:[n("1","Pick a form below"),n("2","Choose the lists it feeds"),n("3","Bind it to an opt-in — MailPoet confirms the rest")]})}async function Ye(){const n=document.querySelector("[data-atfm-root]");if(!n||n.dataset.atfmMounted)return;n.dataset.atfmMounted="1",Fe(n);const e=n.querySelector("[data-atfm-bar]"),s=n.querySelector("[data-atfm-body]")??n;try{const[t,a]=await Promise.all([I.mailpoet(),I.listForms()]);if(e?.remove(),R(s),s.append(Qs(t),...en()),!t.active){s.append(r("div",{class:"atfm-pitch",children:[r("p",{text:"MailPoet sends newsletters from right inside WordPress — no external account, free to start. Install it and every form here gets a subscribe switch: reservations feed your announcements, signups feed your schedule, enquiries feed your news."}),r("a",{class:"atfm-pitch__cta",text:"Install MailPoet — it’s free",attrs:{href:t.adminUrl,target:"_blank",rel:"noreferrer"}})]}));return}if(!t.lists.length){s.append(r("div",{class:"atfm-pitch",children:[r("p",{text:"MailPoet is here, but it has no list yet. Make one, and your forms will have somewhere to send subscribers."}),r("a",{class:"atfm-pitch__cta",text:"Open MailPoet lists",attrs:{href:t.adminUrl,target:"_blank",rel:"noreferrer"}})]}));return}s.append(tn());const i=r("section",{class:"atfm-forms",children:[r("div",{class:"atfm-forms__head",children:[r("h2",{text:"Your forms"}),r("p",{class:"atfm-hint",text:"Connected forms wear the MailPoet mark in colour."})]})]});if(s.append(i),!a.length){i.append(r("p",{class:"atfm-hint",text:"No forms yet — make one, then come back to connect it."}));return}for(const o of a)Js(o,t,i)}catch{e&&(e.textContent="Could not reach the site — reload the window to try again.")}}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>void Ye()):Ye(),document.addEventListener("os-window-content-loaded",()=>void Ye());const we={plain:"",simple:"✓",minimal:"",card:"🎉",check:"",confetti:"🎉",fireworks:"🎆",sparkles:"✨",typewriter:""};function kt(){return{style:"simple",title:"",icon:"",accent:"",intensity:"medium",showButton:!1,buttonLabel:""}}function Ve(n){const e={...kt(),...n??{}};return e.style in we||(e.style="simple"),e}function sn(){return typeof window.matchMedia=="function"&&window.matchMedia("(prefers-reduced-motion: reduce)").matches}function nn(n,e,s){const t=Ve(e),a=document.createElement("div");if(a.className=`atf-confirmation atf-success atf-success--${t.style}`,a.setAttribute("role","status"),a.setAttribute("tabindex","-1"),t.style==="plain")return a.className="atf-confirmation",a.innerHTML=n,a;t.accent&&a.style.setProperty("--atf-accent",t.accent);const i=document.createElement("div");i.className="atf-success__inner",a.append(i);const o=t.icon||we[t.style];if(t.style==="check")i.insertAdjacentHTML("beforeend",'');else if(o){const c=document.createElement("span");c.className="atf-success__icon",c.setAttribute("aria-hidden","true"),c.textContent=o,i.append(c)}if(t.title){const c=document.createElement("h2");c.className="atf-success__title",c.textContent=t.title,i.append(c)}const l=document.createElement("div");if(l.className="atf-success__message",l.innerHTML=n,i.append(l),t.style==="typewriter"&&a.setAttribute("aria-label",l.textContent??""),t.showButton){const c=document.createElement("button");c.type="button",c.className="atf-button atf-button--ghost atf-success__again",c.textContent=t.buttonLabel||"Fill it in again",c.addEventListener("click",()=>s?s():window.location.reload()),i.append(c)}return a}function an(n,e){const s=Ve(e);if(sn())return()=>{};switch(s.style){case"confetti":return rn(n,s);case"fireworks":return on(s);case"sparkles":return ln(n,s);case"typewriter":return cn(n);default:return()=>{}}}function oe(n){return{low:.5,medium:1,high:1.8}[n.intensity]}function St(){const n=document.createElement("canvas");n.className="atf-success-canvas",n.setAttribute("aria-hidden","true");const e=Math.min(window.devicePixelRatio||1,2);n.width=Math.floor(window.innerWidth*e),n.height=Math.floor(window.innerHeight*e),document.body.append(n);const s=n.getContext("2d");return s?.scale(e,e),{canvas:n,ctx:s,stop:()=>n.remove()}}const Ct=["#f43f5e","#f59e0b","#10b981","#3b82f6","#8b5cf6","#ec4899","#eab308"];function rn(n,e){const{ctx:s,stop:t}=St();if(!s)return t(),()=>{};const a=e.accent?[e.accent,...Ct]:Ct,i=[],o=n.getBoundingClientRect(),l=o.left+o.width/2,c=Math.min(o.top+40,window.innerHeight-20),d=(x,F,S)=>{const H=S?Math.PI*(1.15+.7*Math.random()):0,L=S?9+Math.random()*8:0;return{x,y:F,vx:S?Math.cos(H)*L*(Math.random()<.5?1:-1):(Math.random()-.5)*1.5,vy:S?Math.sin(H)*L:1+Math.random()*2,w:6+Math.random()*5,h:8+Math.random()*7,angle:Math.random()*Math.PI,spin:(Math.random()-.5)*.3,color:a[Math.floor(Math.random()*a.length)],wobble:Math.random()*Math.PI*2}},h=Math.round(90*oe(e));for(let x=0;x{if(g>=m){window.clearInterval(u);return}i.push(d(Math.random()*window.innerWidth,-20,!1)),g++},2e3/m);let f=0;const y=performance.now(),p=x=>{s.clearRect(0,0,window.innerWidth,window.innerHeight);let F=!1;for(const S of i)S.vy+=.16,S.vx*=.99,S.vy*=.985,S.wobble+=.1,S.x+=S.vx+Math.sin(S.wobble)*.8,S.y+=S.vy,S.angle+=S.spin,S.y{window.clearInterval(u),window.cancelAnimationFrame(f),t()}}function on(n){const{ctx:e,stop:s}=St();if(!e)return s(),()=>{};const t=[],a=[],i=Math.round(6*oe(n))+2;let o=0;const l=()=>{t.push({x:window.innerWidth*(.15+.7*Math.random()),y:window.innerHeight,vy:-(9+Math.random()*4),targetY:window.innerHeight*(.15+.3*Math.random()),hue:Math.floor(Math.random()*360)}),o++};l();const c=window.setInterval(()=>{if(o>=i){window.clearInterval(c);return}l()},3500/i),d=u=>{const f=Math.round(70*oe(n));for(let y=0;y{e.clearRect(0,0,window.innerWidth,window.innerHeight);for(let y=t.length-1;y>=0;y--){const p=t[y];p.y+=p.vy,p.vy+=.08,e.fillStyle=`hsl(${p.hue} 90% 65%)`,e.fillRect(p.x-1.5,p.y,3,10),(p.y<=p.targetY||p.vy>=-1)&&(d(p),t.splice(y,1))}for(let y=a.length-1;y>=0;y--){const p=a[y];if(p.x+=p.vx,p.y+=p.vy,p.vy+=.045,p.vx*=.985,p.vy*=.985,p.life-=p.decay,p.life<=0){a.splice(y,1);continue}const x=p.life*(.7+.3*Math.random());e.globalAlpha=Math.max(0,x),e.fillStyle=`hsl(${p.hue} 95% ${55+25*p.life}%)`,e.beginPath(),e.arc(p.x,p.y,1.1+1.6*p.life,0,Math.PI*2),e.fill()}e.globalAlpha=1,!(o>=i&&t.length===0&&a.length===0)&&u-m<9e3?h=window.requestAnimationFrame(g):s()};return h=window.requestAnimationFrame(g),()=>{window.clearInterval(c),window.cancelAnimationFrame(h),s()}}function ln(n,e){const s=e.icon||we.sparkles,t=Math.round(22*oe(e)),a=[];let i=0;const o=()=>{const c=document.createElement("span");c.className="atf-success__spark",c.setAttribute("aria-hidden","true"),c.textContent=s,c.style.insetInlineStart=`${4+Math.random()*92}%`,c.style.animationDuration=`${2.6+Math.random()*1.8}s`,c.style.animationDelay=`${Math.random()*.3}s`,c.style.fontSize=`${14+Math.random()*14}px`,c.addEventListener("animationend",()=>c.remove()),n.append(c),a.push(c),i++};o();const l=window.setInterval(()=>{if(i>=t){window.clearInterval(l);return}o()},2800/t);return()=>{window.clearInterval(l),a.forEach(c=>c.remove())}}function cn(n){const e=n.querySelector(".atf-success__message");if(!e)return()=>{};const s=e.innerHTML,t=e.textContent??"";if(!t)return()=>{};e.textContent="",e.classList.add("is-typing");const a=Math.min(45,3800/t.length);let i=0;const o=window.setInterval(()=>{i++,e.textContent=t.slice(0,i),i>=t.length&&(window.clearInterval(o),e.classList.remove("is-typing"),e.innerHTML=s)},a);return()=>{window.clearInterval(o),e.classList.remove("is-typing"),e.innerHTML=s}}const dn={min:-1,max:-1,sum:-1,avg:-1,round:-1,ceil:1,floor:1,abs:1,sqrt:1,pow:2},Et={"+":{precedence:1,right:!1},"-":{precedence:1,right:!1},"*":{precedence:2,right:!1},"/":{precedence:2,right:!1},"%":{precedence:2,right:!1},"^":{precedence:4,right:!0}};function hn(n,e,s=[]){if(!n||!n.trim()||n.length>2e3)return null;const t=un(n,e,s),a=bn(t);if(!a)return null;const i=vn(a);if(!i)return null;const o=yn(i);return o===null||!Number.isFinite(o)?null:o}function un(n,e,s){return n.replace(/\{([a-zA-Z0-9_]+)(?:\.([a-zA-Z0-9_]+))?\}/g,(t,a,i,o)=>pn(n,o,t.length,a,i??"",e,s))}function pn(n,e,s,t,a,i,o){const l=fn(o,t),c=Object.prototype.hasOwnProperty.call(i,t)?i[t]:null;if(a===""){const u=l?.type==="repeater"?Lt(c).length:Ke(c,l);return $t(u)}const h=(l?.fields??[]).find(u=>u.id===a)??null,m=Lt(c).map(u=>Ke(u[a]??"",h));if(!m.length)return"0";const g=m.map($t);return mn(n,e,s)?g.join(", "):g.length===1?g[0]:`( ${g.join(" + ")} )`}function fn(n,e){for(const s of n){if(s.id===e)return s;for(const t of s.fields??[])if(t.id===e)return t}return null}function mn(n,e,s){const t=n.slice(e+s).replace(/^\s+/,"");if(t===""||t[0]!==")")return!1;const a=n.slice(0,e).replace(/\s+$/,""),i=/([a-zA-Z_][a-zA-Z0-9_]*)\s*\($/.exec(a);return!!i&&["sum","avg","min","max"].includes(i[1].toLowerCase())}function Lt(n){if(!Array.isArray(n))return[];const e=[];for(const s of n){if(!s||typeof s!="object"||Array.isArray(s))continue;Object.values(s).some(a=>a!==""&&a!==null&&a!==void 0&&a!==!1&&!(Array.isArray(a)&&!a.length))&&e.push(s)}return e}function $t(n){const e=n.toFixed(10).replace(/0+$/,"").replace(/\.$/,"");return e===""||e==="-"?"0":e}function Ke(n,e){if(typeof n=="boolean")return n?1:0;if(Array.isArray(n))return n.reduce((a,i)=>a+Ke(i,e),0);if(n==null||n==="")return 0;const s=e?.choices??[];for(const a of s)if(String(a.value)===String(n)){if(typeof a.price=="number")return a.price;if(typeof a.points=="number")return a.points;break}const t=Number(n);return Number.isFinite(t)?t:0}function bn(n){const e=[];let s=0;for(;si.precedence||l.precedence===i.precedence&&!i.right){e.push(s.pop());continue}break}s.push(a);break}case"(":s.push(a);break;case")":{for(;s.length&&s[s.length-1].type!=="(";)e.push(s.pop());if(!s.length)return null;if(s.pop(),s.length&&s[s.length-1].type==="function"){const i=s.pop();i.arity=t.length?t.pop():1,e.push(i)}break}}for(;s.length;){const a=s.pop();if(a.type==="(")return null;e.push(a)}return e}function yn(n){const e=[];for(const s of n)switch(s.type){case"number":e.push(s.value);break;case"unary":{if(!e.length)return null;e.push(-e.pop());break}case"operator":{if(e.length<2)return null;const t=e.pop(),a=e.pop();switch(s.value){case"+":e.push(a+t);break;case"-":e.push(a-t);break;case"*":e.push(a*t);break;case"/":e.push(t===0?0:a/t);break;case"%":e.push(t===0?0:a%t);break;case"^":e.push(Math.pow(a,t));break;default:return null}break}case"function":{const t=s.arity??1;if(e.lengths+t,0);case"avg":return e.reduce((s,t)=>s+t,0)/e.length;case"round":{const s=Math.max(-10,Math.min(10,Math.trunc(e[1]??0))),t=Math.pow(10,s);return Math.round(e[0]*t)/t}case"ceil":return Math.ceil(e[0]);case"floor":return Math.floor(e[0]);case"abs":return Math.abs(e[0]);case"sqrt":return e[0]<0?0:Math.sqrt(e[0]);case"pow":return Math.pow(e[0],e[1]??2);default:return null}}const _n=["sum","min","max","avg","round","ceil","floor","abs","sqrt","pow"],At=["number","range","scale","rating","total","select","multiselect","radio","checkboxes","switch","quiz"];function _e(n,e){return n.filter(s=>s.id!==e&&At.includes(s.type))}function Tt(n){const e=[];for(const s of n){if(s.type!=="repeater")continue;const t=s.label||s.id;e.push({label:`${t} (how many)`,insert:`{${s.id}}`});for(const a of s.fields??[])At.includes(a.type)&&e.push({label:`${t} · ${a.label||a.id}`,insert:`{${s.id}.${a.id}}`})}return e}function xn(n,e){const s={};_e(n,e).forEach((t,a)=>{s[t.id]=a+1});for(const t of n){if(t.type!=="repeater"||t.id===e)continue;const a=t.fields??[],i=o=>Object.fromEntries(a.map((l,c)=>[l.id,c+1+o]));s[t.id]=[i(0),i(1)]}return s}function Mt(n,e,s){return Be(n,{preview:!1,groups:()=>[{id:"references",label:"Your questions",items:[..._e(e,s).map(t=>({label:t.label||t.id,tag:`{${t.id}}`,sample:"",hint:""})),...Tt(e.filter(t=>t.id!==s)).map(t=>({label:t.label,tag:t.insert,sample:"",hint:""}))],empty:"Add a number, scale or priced choice question to reference it here."}]})}function kn(n){const e=r("div",{class:"atfb-overlay"}),s=()=>{e.remove(),document.removeEventListener("keydown",t)},t=u=>{u.key==="Escape"&&s()},a=r("textarea",{class:"atfb-input atfb-formula__input",attrs:{rows:"3","aria-label":"Formula"}});a.value=String(n.field.formula??"");const i=r("p",{class:"atfb-formula__result",attrs:{"aria-live":"polite"}}),o=xn(n.fields,n.field.id),l=()=>{const u=a.value.trim();if(u===""){i.textContent="Empty. Reference a question below to start.",i.classList.remove("is-error");return}const f=hn(u,o,n.fields);if(f===null){i.textContent="This does not compute yet — check the braces and parentheses.",i.classList.add("is-error");return}const y=_e(n.fields,n.field.id).map((p,x)=>`${p.label||p.id} = ${x+1}`).concat(n.fields.filter(p=>p.type==="repeater"&&p.id!==n.field.id).map(p=>`${p.label||p.id} = 2 sample rows`)).join(", ");i.textContent=`With sample answers (${y}): ${f}`,i.classList.remove("is-error")};a.addEventListener("input",l);const c=(u,f,y=0)=>r("button",{class:"atfb-formula__chip",type:"button",text:u,on:{click:()=>{if(bt(a,f),y>0){const p=(a.selectionStart??a.value.length)-y;a.setSelectionRange(p,p)}}}}),d=_e(n.fields,n.field.id),h=Tt(n.fields.filter(u=>u.id!==n.field.id)),m=r("div",{class:"atfb-formula__chips",children:d.length||h.length?[...d.map(u=>c(u.label||u.id,`{${u.id}}`)),...h.map(u=>c(u.label,u.insert))]:[r("p",{class:"atfb-hint",text:"No number-shaped questions yet — add a number, scale or priced choice field and it appears here."})]}),g=r("div",{class:"atfb-formula__chips",children:_n.map(u=>c(`${u}()`,`${u}()`,1))});e.append(r("div",{class:"atfb-modal atfb-formula",attrs:{role:"dialog","aria-label":"Formula editor"},children:[r("h2",{text:"Formula"}),Mt(a,n.fields,n.field.id),i,w("Your questions",m,"Click one to reference its answer."),w("Functions",g),r("div",{class:"atfb-modal__actions",children:[C("Cancel",s),C("Save formula",()=>{n.onSave(a.value.trim()),s()},"primary")]})]})),e.addEventListener("click",u=>{u.target===e&&s()}),document.addEventListener("keydown",t),n.root.append(e),l(),a.focus(),a.setSelectionRange(a.value.length,a.value.length)}function Ft(n){return[...n.fields.filter(e=>e.type!=="page_break").map(e=>({key:`field:${e.id}`,label:`Field: ${e.label||e.id}`,fieldId:e.id,logic:e.logic})),...n.notifications.map(e=>({key:`notification:${e.id}`,label:`Notification: ${e.name||e.id}`,logic:e.logic})),...n.confirmations.map(e=>({key:`confirmation:${e.id}`,label:`Confirmation: ${e.name||e.id}`,logic:e.logic}))]}function It(n,e,s){return n.key!==e.key&&n.logic.rules.length>0&&n.logic.rules.every(t=>t.field!==e.fieldId&&s.fields.some(a=>a.id===t.field&&a.type!=="page_break"))}function Sn(n,e,s,t){const a=Ft(n),i=a.find(l=>l.key===e),o=a.find(l=>l.key===s);return!i||!o||!It(i,o,n)?!1:(t?.(),Object.assign(o.logic,i.logic,{rules:i.logic.rules.map(l=>({...l}))}),!0)}function Dt(n){const e=document.activeElement,s=r("div",{class:"atfb-overlay"}),t=r("div",{class:"atfb-modal atfb-condition-copy",attrs:{role:"dialog","aria-modal":"true","aria-label":"Copy condition",tabindex:"-1"}});let a="";const i=n.to,o=()=>{s.remove(),e?.focus()},l=()=>{const c=n.schema();if(!c){o();return}const d=Ft(c),h=d.find(p=>p.key===i),m=d.filter(p=>p.logic.rules.length&&h&&It(p,h,c)),g=m.find(p=>p.key===a),u=D(a,[{value:"",label:"Choose a section…"},...m.map(p=>({value:p.key,label:p.label}))],p=>{a=p,l(),t.querySelector('[aria-label="Copy from"]')?.focus()});u.setAttribute("aria-label","Copy from");const f=C("Copy condition",()=>{const p=n.schema();p&&Sn(p,a,i,n.beforeCopy)&&(o(),n.onCopy())},"primary");(!g||!i)&&f.setAttribute("disabled","");const y=d.find(p=>p.key===i);t.replaceChildren(r("h2",{text:"Copy condition"}),w("Copy from",u),r("p",{class:"atfb-hint",text:`Apply to ${y?.label??"this section"}.`}),r("p",{class:"atfb-condition-copy__preview",attrs:{"aria-live":"polite"},text:g?`${g.logic.enabled?"Enabled":"Disabled"} · ${g.logic.action==="hide"?"Hide":"Show"} · Match ${g.logic.match}: `+g.logic.rules.map(p=>qe(me(p,c.fields))).join(g.logic.match==="all"?" and ":" or "):m.length?"Choose the section whose condition you want to reuse.":"No conditions are available to copy here yet."}),r("p",{class:"atfb-hint",text:y?.logic.rules.length?"This replaces the destination’s entire condition. You can edit the copy independently.":"Copies every rule, all/any matching, show/hide and enabled state. You can edit the copy independently."}),r("div",{class:"atfb-modal__actions",children:[C("Cancel",o),f]}))};s.append(t),s.addEventListener("click",c=>{c.target===s&&o()}),s.addEventListener("keydown",c=>{if(c.key==="Escape"&&(c.preventDefault(),o()),c.key==="Tab"){const d=[...t.querySelectorAll("button:not([disabled]), select, os-select, os-button:not([disabled])")],h=d[0],m=d[d.length-1];c.shiftKey&&(document.activeElement===h||document.activeElement===t)?(c.preventDefault(),m?.focus()):!c.shiftKey&&document.activeElement===m&&(c.preventDefault(),h?.focus())}c.stopPropagation()}),n.root.append(s),l(),t.focus()}const Cn="allterrain-forms/form";function En(){return window.wp?.os?.relations??null}function qt(n){const e=n.closest("[data-window-id], .os-window");if(!e)return null;const s=e.getAttribute("data-window-id");if(s)return s;const t=e.id??"";return t?t.replace(/^wp-window-/,""):null}const Ln=6e3,Ot=120;function Nt(n,e){const s=En();if(Ge.set(n,e),!s?.set)return;const t=i=>{if(xe.get(n)!==a)return;const o=qt(n);if(!o){Date.now()t(i),Ot);return}try{s.set(o,e)}catch(c){Rt||(Rt=!0,console.error("[AllTerrain Forms] The shell refused a window identity.",c,e)),xe.delete(n);return}if(!e||s.get?.(o)?.id===e.id||Date.now()>=i){xe.delete(n);return}window.setTimeout(()=>t(i),Ot)},a=Symbol("atf-identity");xe.set(n,a),t(Date.now()+Ln)}let Rt=!1;const xe=new WeakMap,Ge=new Map;function $n(){for(const[n,e]of Ge){if(!n.isConnected){Ge.delete(n);continue}Nt(n,e)}}if(typeof document<"u")for(const n of["os-window-content-loaded","os-window-opened"])document.addEventListener(n,()=>$n());function An(n,e){return{type:Cn,id:n.id,label:n.title||"Untitled form",related:[{id:`allterrain-forms/entries-${n.id}`,label:"Entries for this form",url:`${e}admin.php?page=allterrain-forms-entries&form=${n.id}`,group:"allterrain-forms",groupLabel:"Forms",icon:"dashicons-list-view"}]}}const ke={letters:{label:"Letters",chars:"A-Za-zÀ-ÖØ-öø-ÿ"},numbers:{label:"Numbers",chars:"0-9"},spaces:{label:"Spaces",chars:" "},punctuation:{label:`Punctuation ( . , ! ? ' " - )`,chars:`.,!?'"()\\-:;`},symbols:{label:"Symbols ( @ # & _ / + )",chars:"@#&_/+*%="}};function Tn(){return{mode:"blocks",starts:"",ends:"",contains:"",notContains:"",chars:[],minLen:"",maxLen:"",regex:"",message:"",tests:[]}}function Pt(n){const e=Tn();let s;try{s=JSON.parse(n)}catch{return e}if(!s||typeof s!="object")return e;const t=s;e.mode=t.mode==="regex"?"regex":"blocks";for(const a of["starts","ends","contains","notContains","minLen","maxLen","regex","message"])typeof t[a]=="string"&&(e[a]=t[a]);return Array.isArray(t.chars)&&(e.chars=t.chars.filter(a=>typeof a=="string"&&a in ke)),Array.isArray(t.tests)&&(e.tests=t.tests.filter(a=>typeof a=="string").slice(0,10)),e}function Se(n){return n.replace(/[.*+?^${}()|[\]\\/]/g,"\\$&")}function Ce(n){if(n.mode==="regex")return n.regex.trim();const e=[],s=n.minLen.trim(),t=n.maxLen.trim();(s!==""||t!=="")&&e.push(`(?=.{${s===""?"0":parseInt(s,10)||0},${t===""?"":parseInt(t,10)||""}}$)`),n.contains!==""&&e.push(`(?=.*${Se(n.contains)})`),n.notContains!==""&&e.push(`(?!.*${Se(n.notContains)})`),n.starts!==""&&e.push(`(?=${Se(n.starts)})`),n.ends!==""&&e.push(`(?=.*${Se(n.ends)}$)`);const a=n.chars.map(o=>ke[o]?.chars??"").join(""),i=a?`[${a}]*$`:".*$";return!e.length&&!a?"":`^${e.join("")}${i}`}function Bt(n){if(n.mode==="regex")return n.regex.trim()?`Matches the expression ${n.regex.trim()}`:"";const e=[];if(n.starts&&e.push(`starts with “${n.starts}”`),n.ends&&e.push(`ends with “${n.ends}”`),n.contains&&e.push(`contains “${n.contains}”`),n.notContains&&e.push(`never contains “${n.notContains}”`),n.chars.length){const i=n.chars.map(o=>(ke[o]?.label??o).split(" (")[0].toLowerCase());e.push(`uses only ${i.join(" and ")}`)}const s=n.minLen.trim(),t=n.maxLen.trim();return s&&t?e.push(`is ${s}–${t} characters long`):s?e.push(`is at least ${s} characters long`):t&&e.push(`is at most ${t} characters long`),e.length?`The answer ${e.length>1?`${e.slice(0,-1).join(", ")}, and ${e[e.length-1]}`:e[0]}.`:""}function Mn(n,e){const s=Ce(n);if(s==="")return null;try{return new RegExp(s).test(e)}catch{return null}}function Fn(){return window.wp?.os?.windowManager??null}const jt="allterrain-forms-validation";function In(n){const e=Fn(),s=qt(n.root);if(e?.openChild&&s){Dn(e,s,n);return}qn(n)}function Dn(n,e,s){const t=`${jt}-${s.field.id}`;n.getById?.(t)&&n.remove?.(t);const a={saved:!1},i=()=>{const o=n.getById?.(t);o?.close?o.close():n.remove?.(t)};n.openChild?.(e,{id:t,baseId:jt,url:`#${t}`,title:`Custom rule — ${s.field.label||"this question"}`,icon:"dashicons-yes-alt",native:!0,width:560,height:700,minWidth:440,minHeight:480,ephemeral:!0,autofocus:".atfb-valwin__pane:not([hidden]) input, .atfb-valwin__pane:not([hidden]) textarea",render:o=>{const l=r("div",{class:"atfa atfb-valwin atfb-valwin--window"}),c=r("div",{class:"atfb-valwin__scroll"});l.append(c),Ht(c,s,a,i,"window");const d=c.querySelector(".atfb-modal__actions");d&&l.append(d),o.append(l)},onClose:()=>{a.saved||s.onCancel?.()}})}function qn(n){const e=r("div",{class:"atfb-overlay"}),s={saved:!1},t=()=>{e.remove(),document.removeEventListener("keydown",a),s.saved||n.onCancel?.()},a=o=>{o.key==="Escape"&&t()},i=r("div",{class:"atfb-modal atfb-valwin",attrs:{role:"dialog","aria-label":"Custom validation rule"},children:[r("h2",{text:"Custom rule"})]});Ht(i,n,s,t,"overlay"),e.append(i),e.addEventListener("click",o=>{o.target===e&&t()}),document.addEventListener("keydown",a),n.root.append(e),e.querySelector(".atfb-valwin__pane:not([hidden]) input, .atfb-valwin__pane:not([hidden]) textarea")?.focus()}function Ht(n,e,s,t,a){const i=Pt(String(e.field.validationRecipe??""));i.mode==="blocks"&&Ce(i)===""&&e.field.pattern&&(i.mode="regex",i.regex=String(e.field.pattern)),i.message||(i.message=String(e.field.messages?.invalid??""));const o=(b,M)=>{const $=r("input",{class:"atfb-input",value:i[b],placeholder:M,attrs:{type:"text"}});return $.addEventListener("input",()=>{i[b]=$.value,L()}),$},l=(b,M)=>{const $=r("input",{class:"atfb-input atfb-valwin__len",value:i[b],attrs:{type:"number",min:"0","aria-label":M}});return $.addEventListener("input",()=>{i[b]=$.value,L()}),$},c=r("div",{class:"atfb-valwin__chars",children:Object.entries(ke).map(([b,M])=>q(M.label,i.chars.includes(b),$=>{i.chars=$?[...i.chars,b]:i.chars.filter(W=>W!==b),L()}))}),d=r("div",{class:"atfb-valwin__pane",children:[w("Starts with",o("starts","e.g. AT-")),w("Ends with",o("ends","e.g. -2026")),w("Must contain",o("contains","e.g. @")),w("Must not contain",o("notContains","e.g. spaces? type one")),w("Only these characters",c,"Leave every box unticked to allow anything."),w("Length",r("div",{class:"atfb-valwin__lengths",children:[r("span",{text:"between"}),l("minLen","Minimum length"),r("span",{text:"and"}),l("maxLen","Maximum length"),r("span",{text:"characters"})]}),"Leave a box empty for no limit.")]}),h=r("textarea",{class:"atfb-input atfb-valwin__regex",attrs:{rows:"2","aria-label":"Regular expression",placeholder:"^AT-[0-9]{4}$"}});h.value=i.regex,h.addEventListener("input",()=>{i.regex=h.value,L()});const m=r("div",{class:"atfb-valwin__pane",children:[w("Expression",h,"A regular expression, without slashes. Checked against the whole answer only if you anchor it with ^ and $.")]}),g=[["blocks","Easy blocks"],["regex","Expression (advanced)"]],u=b=>{i.mode=b,d.hidden=b!=="blocks",m.hidden=b!=="regex",L()},y=(()=>{if(Y("os-segmented")&&Y("os-segment")){const $=document.createElement("os-segmented");$.setAttribute("value",i.mode),$.setAttribute("label","How to write the rule"),$.classList.add("atfb-valwin__tabs");for(const[W,j]of g){const le=document.createElement("os-segment");le.setAttribute("value",W),le.textContent=j,$.append(le)}return $.addEventListener("os-pick",W=>{const j=W.detail?.value;(j==="blocks"||j==="regex")&&($.setAttribute("value",j),u(j))}),$}const b=r("div",{class:"atfb-valwin__tabs",attrs:{role:"tablist"}}),M=()=>{b.replaceChildren(...g.map(([$,W])=>{const j=i.mode===$;return r("button",{class:`atfb-valwin__tab${j?" is-active":""}`,type:"button",text:W,attrs:{role:"tab","aria-selected":j?"true":"false"},on:{click:()=>{u($),M()}}})}))};return M(),b})();d.hidden=i.mode!=="blocks",m.hidden=i.mode!=="regex";const p=r("input",{class:"atfb-input",value:i.message,placeholder:"That is not in the expected format.",attrs:{type:"text"}});p.addEventListener("input",()=>{i.message=p.value});const x=r("p",{class:"atfb-valwin__summary",attrs:{"aria-live":"polite"}}),F=r("div",{class:"atfb-valwin__samples"}),S=b=>{const M=r("span",{class:"atfb-valwin__verdict",attrs:{"aria-live":"polite"}}),$=r("input",{class:"atfb-input",value:b,placeholder:"Type a sample answer…",attrs:{type:"text"}});$.addEventListener("input",()=>L()),F.append(r("div",{class:"atfb-valwin__sample",children:[$,M]}))};for(const b of i.tests.length?i.tests:["","",""])S(b);const H=()=>Array.from(F.querySelectorAll("input")).map(b=>b.value),L=()=>{const b=Bt(i);x.textContent=b||"Nothing yet — fill in a block above and the rule appears here in plain words.";for(const M of Array.from(F.querySelectorAll(".atfb-valwin__sample"))){const $=M.querySelector("input"),W=M.querySelector(".atfb-valwin__verdict");if(!$||!W)continue;const j=$.value===""?null:Mn(i,$.value);W.textContent=j===null?"·":j?"✓ passes":"✗ fails",W.classList.toggle("is-pass",j===!0),W.classList.toggle("is-fail",j===!1)}};n.append(r("p",{class:"atfb-hint",text:`Describe what a good answer to “${e.field.label||"this question"}” looks like — no code needed.`}),y,d,m,r("div",{class:"atfb-valwin__try",children:[r("h3",{text:"Try it out"}),x,F,C("Add another sample",()=>{S(""),L()},"ghost","plus-alt2")]}),w("When it fails, say",p,"Shown to the visitor when their answer breaks the rule. Leave empty for the default wording."),r("div",{class:"atfb-modal__actions",children:[C(a==="window"?"Close without saving":"Cancel",t),C("Save rule",()=>{const b=Ce(i);i.tests=H().filter(M=>M!=="").slice(0,10),s.saved=!0,e.onSave({pattern:b,recipe:i,message:i.message.trim()}),t()},"primary")]})),L()}const On=["Contact","Numbers & codes","Text shape","Web"],Wt=[{slug:"email",label:"An email address",group:"Contact",example:"jane@example.com",pattern:"^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$",message:"That does not look like an email address."},{slug:"phone",label:"A phone number",group:"Contact",example:"+34 612 345 678",pattern:"^(?=(?:[^0-9]*[0-9]){5,})\\+?[0-9 ().-]{5,24}$",message:"That does not look like a phone number."},{slug:"handle",label:"A username or @handle",group:"Contact",example:"@yourname",pattern:"^@?[A-Za-z0-9_]{2,30}$",message:"That does not look like a username."},{slug:"digits",label:"Numbers only",group:"Numbers & codes",example:"12345",pattern:"^[0-9]+$",message:"Numbers only, please."},{slug:"decimal",label:"A number, decimals allowed",group:"Numbers & codes",example:"3.14",pattern:"^-?[0-9]+([.,][0-9]+)?$",message:"That does not look like a number."},{slug:"price",label:"A price",group:"Numbers & codes",example:"19.99",pattern:"^[0-9]+([.,][0-9]{1,2})?$",message:"That does not look like a price."},{slug:"zip_us",label:"A ZIP code (US)",group:"Numbers & codes",example:"90210",pattern:"^[0-9]{5}(-[0-9]{4})?$",message:"That does not look like a ZIP code."},{slug:"postcode_uk",label:"A postcode (UK)",group:"Numbers & codes",example:"SW1A 1AA",pattern:"^[A-Za-z]{1,2}[0-9][A-Za-z0-9]? ?[0-9][A-Za-z]{2}$",message:"That does not look like a postcode."},{slug:"iban",label:"An IBAN",group:"Numbers & codes",example:"DE89 3704 0044 0532 0130 00",pattern:"^[A-Za-z]{2}[0-9]{2}(?: ?[A-Za-z0-9]){10,32}$",message:"That does not look like an IBAN."},{slug:"credit_card",label:"A card number",group:"Numbers & codes",example:"4242 4242 4242 4242",pattern:"^[0-9](?:[0-9 -]{9,21})?[0-9]$",message:"That does not look like a card number.",luhn:!0},{slug:"letters",label:"Letters only",group:"Text shape",example:"María López",pattern:"^[\\p{L}\\p{M} .'’-]+$",message:"Letters only, please."},{slug:"alphanumeric",label:"Letters and numbers only",group:"Text shape",example:"abc123",pattern:"^[A-Za-z0-9]+$",message:"Letters and numbers only, please."},{slug:"no_spaces",label:"One word, no spaces",group:"Text shape",example:"one-word",pattern:"^\\S+$",message:"No spaces allowed."},{slug:"url",label:"A web address",group:"Web",example:"https://example.com",pattern:"^(https?://)?([A-Za-z0-9-]+\\.)+[A-Za-z]{2,}([/?#]\\S*)?$",message:"That does not look like a web address."},{slug:"ip",label:"An IP address",group:"Web",example:"192.168.0.1",pattern:"^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])$",message:"That does not look like an IP address."},{slug:"slug",label:"A URL slug",group:"Web",example:"my-page-title",pattern:"^[a-z0-9]+(-[a-z0-9]+)*$",message:"Lowercase letters, numbers and dashes only."},{slug:"hex_color",label:"A hex colour",group:"Web",example:"#3366ff",pattern:"^#?([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$",message:"That does not look like a colour code."}];function Nn(n){return Wt.find(e=>e.slug===n)??null}function Xe(){return window.wp?.os??null}const Ut="allterrain-forms/preview",Ze="allterrain-forms-preview";function Rn(n){const e=Xe();if(!e?.registerTitleBarButton)return()=>{};const s=()=>{try{e.registerTitleBarButton({id:Ut,label:"Preview this form",icon:"dashicons-visibility",placement:"right",order:90,match:t=>{const a=t?.id??t?.config?.id??"";return a==="allterrain-forms"||a.startsWith("allterrain-forms#")},onClick:()=>void zt(n),owner:"allterrain-forms-builder"})}catch{}};return e.ready?e.ready(s):s(),()=>{try{e.unregisterTitleBarButton?.(Ut)}catch{}}}async function zt(n){n.isDirty()&&await n.save();const e=n.current();e&&Yt(e.id,e.title,e.previewUrl)}function Yt(n,e,s){const t=Xe();if(!t?.windowManager?.open){window.open(s,"_blank","noopener");return}t.windowManager.open({id:`${Ze}-${n}`,baseId:Ze,url:s,title:`Preview: ${e}`,icon:"dashicons-visibility"})}function Pn(n,e,s){if(!Xe()?.windowManager?.open||!document.querySelector(`[data-window-id="${Ze}-${n}"]`))return;const i=s.includes("?")?"&":"?";Yt(n,e,`${s}${i}alltfo_r=${Date.now()}`)}const te="allterrain-forms/field",Bn=["openstation/file","desktop-mode/file","openstation/attachment"],Vt="allterrain-forms/logic-map-v2";function J(n,e){return n.dataset.atfbBind=e,n}const Kt={level:{key:"level",label:"Heading level",control:"select",hint:"Headings should step down one at a time.",options:[{value:"2",label:"Heading 2"},{value:"3",label:"Heading 3"},{value:"4",label:"Heading 4"},{value:"5",label:"Heading 5"},{value:"6",label:"Heading 6"}]},content:{key:"content",label:"HTML",control:"textarea",hint:"Shown as written. Scripts are stripped when the form is saved."},consenttext:{key:"consentText",label:"What they are agreeing to",control:"textarea",hint:"Shown beside the tick box. Links are allowed."},height:{key:"height",label:"Height",control:"number",hint:"In pixels."},columns:{key:"columns",label:"Columns",control:"number",hint:"How many pictures sit side by side."},multiple:{key:"multiple",label:"Let them choose more than one",control:"checkbox"},inline:{key:"inline",label:"Lay the options out in a row",control:"checkbox"},other:{key:"other",label:"Offer an “Other” box",control:"checkbox",hint:"Adds a final option with a box to type in."},filetypes:{key:"filetypes",label:"Accepted file types",control:"commas",hint:"Extensions, separated by commas. Empty accepts anything the site allows."},maxsize:{key:"maxsize",label:"Largest file",control:"number",hint:"In megabytes."},maxfiles:{key:"maxfiles",label:"How many files",control:"number"},minrows:{key:"minRows",label:"Fewest rows",control:"number",also:{key:"maxRows",label:"Most rows"}},itemlabel:{key:"itemLabel",label:"What is one row called?",control:"text",hint:"Names each card — “Attendee 1”, “Attendee 2” — and the Remove button. Also editable on the card itself."},endlabels:{key:"minLabel",label:"Label at the low end",control:"text",also:{key:"maxLabel",label:"Label at the high end"}},points:{key:"points",label:"Points if correct",control:"number"},minchoices:{key:"minChoices",label:"Fewest they may pick",control:"number",also:{key:"maxChoices",label:"Most they may pick"}}},jn={label:"the canvas edits it in place, and the inspector mirrors it",placeholder:"edited by typing into the control on the canvas",hint:"edited under the control on the canvas",nextlabel:"edited on the page break’s own Next button",prevlabel:"edited on the page break’s own Back button",addlabel:"edited on the repeater’s own Add button",choices:"the choices editor, and the option list on the canvas",required:"the toggle on the card",width:"its own row",css:"its own row",prefill:"its own section",logic:"the conditional logic section",formula:"its own row, with the currency that goes with it",currency:"rendered with the formula",correct:"the choices editor marks the right answer",default:"its own row, typed to match what the field stores",rows:"a row count on a textarea; the statement list on a Likert matrix",parts:"a tick box per part, listed by the server",min:"the validation section, paired with max",max:"the validation section, paired with min",step:"the validation section",minlength:"the validation section, paired with maxlength",maxlength:"the validation section, paired with minlength",mindate:"the validation section, paired with maxdate",maxdate:"the validation section, paired with mindate",mintime:"the validation section, paired with maxtime",maxtime:"the validation section, paired with mintime",pattern:"the validation section",unique:"the validation section",maxchoices:"rendered with minchoices",maxrows:"rendered with minrows"};function Gt(n,e,s){const t=n[e.key],a=i=>s(e.key,i);if(e.control==="checkbox")return q(e.label,!!t,a);if(e.control==="commas"){const i=Array.isArray(t)?t:[];return w(e.label,T(i.join(", "),o=>{a(o.split(",").map(l=>l.trim().replace(/^\./,"").toLowerCase()).filter(Boolean))}),e.hint)}return e.control==="select"?w(e.label,D(String(t??""),e.options??[],a),e.hint):e.control==="textarea"?w(e.label,pe(String(t??""),a),e.hint):e.control==="number"?w(e.label,V(String(t??""),a),e.hint):w(e.label,J(T(String(t??""),a),e.key),e.hint)}function Xt(n,e){const s=new Set(n.map(a=>a.key).filter(Boolean));let t=n.length+1;return e.split(` -`).map(a=>a.trim()).filter(Boolean).map((a,i)=>{const o=n[i]?.key;if(o)return{key:o,label:a};for(;s.has(`r${t}`);)t+=1;return s.add(`r${t}`),{key:`r${t}`,label:a}})}function Zt(n,e,s){const t=n.findIndex(i=>i.id===e);if(t<0)return null;const a=Math.max(0,Math.min(s,n.length-1));return a===t?null:{from:t,to:a}}const Je=[{value:"user:email",label:"Their email address",group:"About the person filling it in",tag:"{user:email}"},{value:"user:display_name",label:"Their name",group:"About the person filling it in",tag:"{user:display_name}"},{value:"user:first_name",label:"Their first name",group:"About the person filling it in"},{value:"user:last_name",label:"Their last name",group:"About the person filling it in"},{value:"user:login",label:"Their username",group:"About the person filling it in"},{value:"date:today",label:"Today’s date",group:"The date and time",tag:"{date}"},{value:"date:now",label:"The time right now",group:"The date and time",tag:"{time}"},{value:"site",label:"This site’s name",group:"About this site",tag:"{site}"},{value:"site:url",label:"This site’s address",group:"About this site",tag:"{site:url}"},{value:"site:admin_email",label:"The site administrator’s email",group:"About this site",tag:"{admin_email}"}],Hn=["About the person filling it in","The date and time","About this site"],Qe=(n,e)=>U?.i18n?.[n]??e;class Jt{constructor(e){this.config=null,this.themes=[],this.forms=[],this.form=null,this.schema=null,this.selected=null,this.tab="build",this.logicMap=null,this.canvasTheme=r("style"),this.canvasThemeSignature="",this.openSections=new Map,this.logicMapOn=fs(Vt)!=="off",this.dirty=!1,this.editGeneration=0,this.saveInFlight=!1,this.queuedSave=null,this.teardowns=[],this.canvasTarget=null,this.history=[],this.historyAt=-1,this.autosave=Me(()=>{this.save(!0)},2500),this.renderInspector=ps(()=>{if(R(this.inspector),!this.schema||(this.root.classList.toggle("atfb--build-only-panes",this.tab!=="build"),this.tab!=="build"))return;const s=this.selected?this.locateField(this.selected):void 0,t=s?.field,a=s?.parent??null;if(this.root.classList.toggle("atfb--has-selection",!!t),!t){this.inspector.append(r("div",{class:"atfb-placeholder",children:[r("p",{text:"Select a field to change it."}),r("p",{class:"atfb-hint",text:"Drag a field from the palette, or press one to add it to the end."})]}));return}const i=this.config?.fieldTypes.find(c=>c.type===t.type),o=i?.supports??[],l=(c,d)=>{const h=this.liveField(t.id);h&&(h[c]=d,this.markDirty(),this.renderCanvas())};this.inspector.append(r("h3",{class:"atfb-inspector__title",text:i?.label??t.type})),a?this.inspector.append(r("p",{class:"atfb-hint atfb-inspector__crumb",text:`Inside ${a.label||"a repeater"} — the visitor answers this once per ${String(a.itemLabel??"")||"row"}.`}),r("p",{class:"atfb-hint",text:`Formulas aggregate it as {${a.id}.${t.id}} — e.g. sum( {${a.id}.${t.id}} ).`})):this.inspector.append(r("p",{class:"atfb-hint",text:`Reference this field as {field:${t.id}}`})),o.includes("label")&&this.inspector.append(w("Label",J(T(t.label,c=>l("label",c)),"label"))),o.includes("placeholder")&&this.inspector.append(w("Placeholder",J(T(t.placeholder,c=>l("placeholder",c)),"placeholder"))),o.includes("hint")&&this.inspector.append(w("Hint",J(T(t.hint,c=>l("hint",c)),"hint"),"Shown under the field, and read out with it.")),o.includes("nextlabel")&&this.inspector.append(w("Next button",J(T(String(t.nextLabel??""),c=>l("nextLabel",c)),"nextLabel"),"Leave empty for “Next”.")),o.includes("prevlabel")&&this.inspector.append(w("Back button",J(T(String(t.prevLabel??""),c=>l("prevLabel",c)),"prevLabel"),"Leave empty for “Back”.")),o.includes("addlabel")&&this.inspector.append(w("Add button",J(T(String(t.addLabel??""),c=>l("addLabel",c)),"addLabel"),"Leave empty for “Add another”.")),o.includes("required")&&this.inspector.append(q("Required",t.required,c=>l("required",c))),o.includes("width")&&this.inspector.append(w("Width",D(t.width,[{value:"full",label:"Full width"},{value:"two-thirds",label:"Two thirds"},{value:"half",label:"Half"},{value:"third",label:"One third"},{value:"quarter",label:"One quarter"}],c=>l("width",c)))),i?.choices&&this.inspector.append(this.renderChoicesEditor(t,l)),this.renderTypeSettings(t,i,o,l),(t.type==="total"||o.includes("formula"))&&this.inspector.append(w("Formula",r("div",{class:"atfb-formula__row",children:[Mt(T(String(t.formula??""),c=>l("formula",c)),this.schema?.fields??[],t.id),C("Formula editor",()=>kn({root:this.root,fields:this.schema?.fields??[],field:this.liveField(t.id)??t,onSave:c=>{l("formula",c),this.renderInspector()}}))]}),"Reference answers with braces — {f1} * {f2} + 10 — or open the editor and click them in."),w("Currency symbol",T(String(t.currency??""),c=>l("currency",c)))),this.inspector.append(this.renderValidationSection(t,o,l)),a||(this.inspector.append(this.renderLogicSection(t)),o.includes("prefill")&&this.inspector.append(this.prefillControl(t,l))),o.includes("css")&&this.inspector.append(w("CSS class",T(t.cssClass,c=>l("cssClass",c))))}),this.root=e,this.bar=e.querySelector("[data-atfb-bar]")??r("div"),this.palette=e.querySelector("[data-atfb-palette]")??r("div"),this.canvas=e.querySelector("[data-atfb-canvas]")??r("div"),this.inspector=e.querySelector("[data-atfb-inspector]")??r("div"),this.canvas.addEventListener("click",s=>{const t=s.target;if(!(this.tab!=="build"||!this.selected||t.closest("[data-atfb-card], button, os-button, a, input, textarea, select, os-select, label, [contenteditable]"))){this.selected=null;for(const a of this.canvas.querySelectorAll(".atfb-card.is-selected"))a.classList.remove("is-selected");this.renderInspector()}})}async start(){try{const[t,a,i]=await Promise.all([I.config(),I.listThemes(),I.listForms()]);this.config=t,this.themes=a,this.forms=i}catch(t){this.fail(t);return}this.teardowns.push(os([te])),this.teardowns.push(Rn({current:()=>this.form?{id:this.form.id,title:this.form.title,previewUrl:this.form.previewUrl}:null,isDirty:()=>this.dirty,save:()=>this.save(!0)}));const e=t=>{this.dirty&&(t.preventDefault(),t.returnValue="")};window.addEventListener("beforeunload",e),this.teardowns.push(()=>window.removeEventListener("beforeunload",e));const s=t=>{!(t.metaKey||t.ctrlKey)||t.key.toLowerCase()!=="z"||t.target?.closest("input, textarea, [contenteditable]")||(t.preventDefault(),this.travel(t.shiftKey?1:-1))};if(this.root.addEventListener("keydown",s),this.teardowns.push(()=>this.root.removeEventListener("keydown",s)),this.renderBar(),this.renderPalette(),this.forms.length){const t=Ss("builder");await this.open(t&&this.forms.some(a=>a.id===t)?t:this.forms[0].id)}else this.renderFormsList()}destroy(){this.canvasTarget?.(),this.canvasTarget=null,this.teardowns.forEach(e=>e()),this.teardowns=[]}fail(e){R(this.bar),this.bar.append(r("p",{class:"atfb-error",text:e instanceof Error?e.message:"Something went wrong loading your forms."}))}renderBar(){R(this.bar);const e=D(String(this.form?.id??""),[...this.forms.map(a=>({value:String(a.id),label:a.title||"(untitled)"}))],a=>void this.open(Number(a)));e.setAttribute("aria-label","Choose a form");const s=r("input",{class:"atfb-title",type:"text",value:this.form?.title??"",placeholder:"Untitled form",attrs:{"aria-label":"Form title"},on:{input:a=>{this.form&&(this.form.title=a.target.value,this.markDirty())}}}),t=r("div",{class:"atfb-tabs",attrs:{role:"tablist"},children:[["build","Build"],["theme","Theme"],["settings","Settings"],["notify","Notifications"],["confirm","Confirmations"]].map(([a,i])=>r("button",{class:`atfb-tab${this.tab===a?" is-active":""}`,type:"button",text:i,attrs:{role:"tab","aria-selected":this.tab===a},on:{click:()=>{this.tab=a,this.renderBar(),this.renderInspector(),this.renderCanvas()}}}))});this.bar.append(r("div",{class:"atfb-bar__left",children:[this.forms.length>1?e:null,s]}),t,r("div",{class:"atfb-bar__right",children:[this.historyButton("undo","Undo",-1),this.historyButton("redo","Redo",1),C("New",()=>void this.showTemplates(),"secondary","plus-alt2"),C("Export",()=>void this.exportForm(),"secondary","download"),C("Import",()=>void this.importForm(),"secondary","upload"),C("Preview",()=>void this.preview(),"secondary","visibility"),C("Entries",()=>this.openEntries(),"secondary","list-view"),this.logicMapButton(),this.saveButton()]}))}liveField(e){return this.locateField(e)?.field}locateField(e){const s=this.schema?.fields??[];for(let t=0;tqueueMicrotask(()=>this.rebindCanvas()),{once:!0});return}if(this.canvas.matches(":hover")){this.canvas.addEventListener("pointerleave",()=>this.rebindCanvas(),{once:!0});return}this.renderCanvas()}snapshot(){if(!this.schema)return;const e=JSON.stringify(this.schema);this.history[this.historyAt]!==e&&(this.history=this.history.slice(0,this.historyAt+1),this.history.push(e),this.history.length>60&&this.history.shift(),this.historyAt=this.history.length-1,this.syncHistoryButtons())}travel(e){const s=this.historyAt+e;!this.schema||s<0||s>=this.history.length||(this.historyAt=s,this.schema=JSON.parse(this.history[s]),this.schema.fields.some(t=>t.id===this.selected)||(this.selected=null),this.markDirty(),this.renderBar(),this.renderCanvas(),this.renderInspector())}historyButton(e,s,t){const a=C(s,()=>this.travel(t),"secondary",e);return a.setAttribute("data-atfb-history",String(t)),a.disabled=!this.canTravel(t),a}canTravel(e){const s=this.historyAt+e;return s>=0&&sURL.revokeObjectURL(t),1e3)}async importForm(){const e=r("input",{type:"file",attrs:{accept:"application/json,.json"}});e.addEventListener("change",async()=>{const s=e.files?.[0];if(s)try{const t=JSON.parse(await s.text());if(!t.schema)throw new Error("That file does not contain a form.");const a=await I.createForm({title:t.title??s.name.replace(/\.json$/i,""),schema:t.schema});this.forms.unshift({id:a.id,title:a.title,status:a.status,modified:a.modified,fields:a.schema.fields.length,theme:a.schema.settings.theme,entries:0,unread:0,views:0,submissions:0,shortcode:a.shortcode}),this.form=a,this.schema=a.schema,this.selected=null,this.dirty=!1,this.history=[],this.historyAt=-1,this.snapshot(),this.renderBar(),this.renderCanvas(),this.renderInspector(),N("Form imported",a.title)}catch(t){N("Could not import that file",t instanceof Error?t.message:"","error")}}),e.click()}saveButton(){const e=C("Save",()=>void this.save(),"primary");return e.disabled=!this.dirty,e.setAttribute("data-atfb-save",""),e}markDirty(){this.dirty=!0,this.editGeneration+=1;const e=this.bar.querySelector("[data-atfb-save]");e&&(e.disabled=!1),this.autosave()}async save(e=!1){if(!this.form||!this.schema)return;if(this.saveInFlight){this.queuedSave={silent:e&&(this.queuedSave?.silent??!0)};return}this.saveInFlight=!0;const s=this.editGeneration;try{const t=await I.updateForm(this.form.id,{title:this.form.title,schema:this.schema});if(s===this.editGeneration){this.form=t,this.schema=t.schema,this.dirty=!1,this.rebindCanvas();const i=this.bar.querySelector("[data-atfb-save]");i&&(i.disabled=!0)}Bs(t.id);const a=this.forms.find(i=>i.id===t.id);a&&(a.title=t.title),Pn(t.id,t.title,t.previewUrl),e||N("Form saved",t.title)}catch(t){N(Qe("saveFailed","Could not save"),t instanceof Error?t.message:"","error")}finally{this.saveInFlight=!1;const t=this.queuedSave;this.queuedSave=null,t&&this.save(t.silent)}}async openFormById(e){await this.open(e)}async open(e){if(!(this.dirty&&!await ae("You have unsaved changes. Discard them?"))){try{this.form=await I.getForm(e),this.schema=this.form.schema,this.selected=null,this.dirty=!1,this.history=[],this.historyAt=-1,this.snapshot()}catch(s){this.fail(s);return}this.renderBar(),this.renderCanvas(),this.renderInspector(),this.announceIdentity()}}announceIdentity(){this.form&&Nt(this.root,An(this.form,U?.adminUrl??""))}async showTemplates(){if(!this.config)return;const e=r("div",{class:"atfb-overlay"}),s=i=>{i.key==="Escape"&&t()},t=()=>{e.remove(),document.removeEventListener("keydown",s)},a=r("div",{class:"atfb-templates",children:this.config.templates.map(i=>r("button",{class:"atfb-template",type:"button",on:{click:async()=>{t();try{const o=await I.createForm({template:i.slug});this.forms.unshift({id:o.id,title:o.title,status:o.status,modified:o.modified,fields:o.schema.fields.length,theme:o.schema.settings.theme,entries:0,unread:0,views:0,submissions:0,shortcode:o.shortcode}),this.form=o,this.schema=o.schema,this.selected=null,this.dirty=!1,this.history=[],this.historyAt=-1,this.snapshot(),this.renderBar(),this.renderCanvas(),this.renderInspector(),this.announceIdentity()}catch(o){N("Could not create the form",o instanceof Error?o.message:"","error")}}},children:[B(i.icon),r("strong",{text:i.label}),r("span",{text:i.description})]}))});e.append(r("div",{class:"atfb-modal",attrs:{role:"dialog","aria-label":"Start a new form"},children:[r("h2",{text:"Start a new form"}),a,this.archivedFormsSection(t),r("div",{class:"atfb-modal__actions",children:[C("Cancel",t)]})]})),e.addEventListener("click",i=>{i.target===e&&t()}),document.addEventListener("keydown",s),this.root.append(e),a.querySelector("button")?.focus()}archivedFormsSection(e){const s=r("div",{class:"atfb-archived"});return I.listArchivedForms().then(t=>{t.length&&s.append(r("h3",{class:"atfb-archived__title",text:"Or bring one back from the archive"}),...t.map(a=>r("div",{class:"atfb-archived__row",children:[r("div",{class:"atfb-archived__meta",children:[r("strong",{text:a.title||"(untitled)"}),r("span",{class:"atfb-hint",text:`${a.entries} ${a.entries===1?"entry":"entries"} · ${a.submissions} submissions · ${a.views} views`})]}),C("Restore",async()=>{try{const i=await I.unarchiveForm(a.id);e(),this.forms.unshift(i),N("Form restored",`${i.title||"(untitled)"} is back, with its entries and stats.`),await this.open(i.id)}catch(i){N("Could not restore the form",i instanceof Error?i.message:"","error")}},"secondary","undo")]})))}).catch(()=>{}),s}renderFormsList(){R(this.canvas),this.canvas.append(r("div",{class:"atfb-empty",children:[r("h2",{text:"No forms yet"}),r("p",{text:"Start from a template, or build one from nothing."}),C("New form",()=>void this.showTemplates(),"primary","plus-alt2")]}))}openEntries(){const e=window.wp?.os;if(e?.openWindow){e.openWindow("allterrain-forms-entries");return}window.location.assign(`${U?.adminUrl??""}admin.php?page=allterrain-forms-entries`)}renderPalette(){if(!this.config)return;R(this.palette);const e=new Map;for(const t of this.config.fieldTypes){const a=e.get(t.group)??[];a.push(t),e.set(t.group,a)}const s=r("input",{class:"atfb-input atfb-palette__search",type:"search",placeholder:"Search fields",attrs:{"aria-label":"Search field types"},on:{input:t=>{const a=t.target.value.toLowerCase().trim();this.palette.querySelectorAll(".atfb-chip").forEach(i=>{const o=(i.textContent??"").toLowerCase();i.hidden=a!==""&&!o.includes(a)}),this.palette.querySelectorAll(".atfb-group").forEach(i=>{const o=Array.from(i.querySelectorAll(".atfb-chip")).some(l=>!l.hidden);i.hidden=!o})}}});this.palette.append(s);for(const[t,a]of Object.entries(this.config.groups)){const i=e.get(t);i?.length&&this.palette.append(r("div",{class:"atfb-group",children:[r("h3",{class:"atfb-group__title",text:a}),r("div",{class:"atfb-group__items",children:i.map(o=>this.paletteChip(o))})]}))}}paletteChip(e){const s=r("button",{class:"atfb-chip",type:"button",title:e.description,attrs:{"data-atf-type":e.type},children:[B(e.icon),r("span",{text:e.label})]});return s.addEventListener("pointerdown",t=>{const a=r("div",{class:"atfb-chip atfb-chip--ghost",children:[B(e.icon),r("span",{text:e.label})]});G().start({payload:Ae(te,s,{fieldType:e.type,isNew:!0},t,a),origin:t,onClickOnly:()=>this.addField(e.type)})}),s.addEventListener("click",t=>{G().recentlyEndedDrag()&&t.preventDefault()}),s}renderCanvas(){if(R(this.canvas),!this.schema||!this.form){this.renderFormsList();return}if(this.tab!=="build"){this.canvas.append(this.renderTabCanvas());return}const e=r("div",{class:"atfb-canvas__list",attrs:{"data-atfb-list":""}});this.schema.fields.length||e.append(r("div",{class:"atfb-placeholder",text:Qe("emptyCanvas","Drag a field from the left to begin.")})),this.schema.fields.forEach((t,a)=>{e.append(this.renderFieldCard(t,a))});const s=r("div",{class:"atfb-canvas__inner",children:[r("p",{class:"atfb-shortcode",text:this.form.shortcode,title:"Paste this anywhere to place the form"}),e]});this.canvas.append(s),this.registerCanvasTarget(e),this.paintLogicMap(s),this.paintCanvasTheme()}prefillControl(e,s){const t=e.prefill.startsWith("query:"),a=Je.some(g=>g.value===e.prefill),i=t?"query":a&&e.prefill||(e.prefill?"custom":""),o=r("div",{class:"atfb-prefill__detail"}),l=r("p",{class:"atfb-row__hint atfb-prefill__preview"}),c=g=>{if(o.replaceChildren(),l.replaceChildren(),g==="query"){const u=e.prefill.startsWith("query:")?e.prefill.slice(6):"";o.append(T(u,f=>{const y=f.trim();s("prefill",y?`query:${y}`:""),d(`query:${y}`)},"utm_source"),r("p",{class:"atfb-row__hint",text:"The name of the parameter in the link people arrive on."}))}g==="custom"&&o.append(T(e.prefill,u=>{s("prefill",u),d(u)},"myplugin:something"),r("p",{class:"atfb-row__hint",text:"For a source another plugin has added through alltfo_resolve_prefill."})),d(e.prefill)},d=g=>{if(l.replaceChildren(),!g)return;if(g.startsWith("query:")){const f=g.slice(6);f&&(l.textContent=`A visit to …/your-page/?${f}=abc opens the form with “abc” in it.`);return}const u=Je.find(f=>f.value===g)?.tag;u&&ft(this.form?.id??0).then(f=>{for(const y of f)for(const p of y.items)if(p.tag===u){const x=g.startsWith("user:");if(!p.sample){l.textContent="Empty unless the visitor is signed in.";return}l.textContent=x?`Opens with “${p.sample}” for you — empty for a visitor who is not signed in.`:`Opens with “${p.sample}”.`;return}})};c(i);const h=r("select",{class:"atfb-input atfb-select",on:{change:g=>{const u=g.target.value;s("prefill",u==="query"||u==="custom"?"":u),c(u)}}});h.append(r("option",{value:"",text:"Nothing — leave it empty",attrs:{selected:i===""}}));for(const g of Hn){const u=document.createElement("optgroup");u.label=g;for(const f of Je.filter(y=>y.group===g))u.append(r("option",{value:f.value,text:f.label,attrs:{selected:f.value===i}}));h.append(u)}const m=document.createElement("optgroup");return m.label="From the link they arrived on",m.append(r("option",{value:"query",text:"A parameter in the web address",attrs:{selected:i==="query"}})),h.append(m,r("option",{value:"custom",text:"Something else (advanced)",attrs:{selected:i==="custom"}})),w("Pre-fill this with",r("div",{class:"atfb-prefill",children:[h,o,l]}),"What the box already contains when the form opens. They can still change it.")}renderCondition(e,s){const t=s.some(l=>l.kind==="field"&&l.missing),a=r("div",{class:`atfb-cond${t?" is-broken":""}`,attrs:{"aria-label":s.length?qe(s):"Conditional rules"}});a.append(r("div",{class:"atfb-cond__heading",children:[r("span",{class:"atfb-cond__label",children:[B("randomize"),"Conditions"]}),this.copyConditionButton(`field:${e.id}`)]})),e.logic.rules.forEach((l,c)=>{const d=me(l,this.schema?.fields??[],c),h=r("button",{class:"atfb-cond__delete",type:"button",title:"Delete this rule",attrs:{"aria-label":`Delete rule ${c+1}`},children:[B("trash")],on:{click:()=>{this.snapshot(),this.editCondition(e.id,m=>{m.rules.splice(c,1)},!0,`${e.id}:add`),this.snapshot()}}});a.append(r("div",{class:"atfb-cond__rule",children:[c===0?this.renderConditionToken(e,{kind:"verb",text:e.logic.action==="hide"?"Hidden when":"Shown when"}):this.renderConditionToken(e,{kind:"join",text:e.logic.match==="all"?"and":"or"}),...d.map(m=>this.renderConditionToken(e,m,c)),h]}))}),e.logic.rules.length||a.append(r("span",{class:"atfb-hint",text:"No rules yet. Add one or copy a condition."}));const i=r("button",{class:"atfb-cond__add",type:"button",text:"+ Add rule",title:"Add rule",attrs:{"aria-label":"Add rule","data-cond":`${e.id}:add`},on:{click:()=>this.addConditionRule(e.id)}}),o=r("button",{class:"atfb-cond__clear",type:"button",text:"Clear",title:"Delete all rules",attrs:{"aria-label":"Clear all rules",disabled:!e.logic.rules.length},on:{click:()=>{this.snapshot(),this.editCondition(e.id,l=>{l.rules=[]},!0,`${e.id}:add`),this.snapshot()}}});return a.append(r("div",{class:"atfb-cond__actions",children:[i,o]})),a.addEventListener("pointerdown",l=>l.stopPropagation()),a.addEventListener("click",l=>l.stopPropagation()),a.addEventListener("keydown",l=>l.stopPropagation()),a}editCondition(e,s,t=!0,a=""){const i=this.liveField(e)?.logic;if(i){if(s(i),this.markDirty(),!t){this.logicMap?.setEdges(be(this.schema?.fields??[]));return}this.renderCanvas(),this.renderInspector(),a&&window.requestAnimationFrame(()=>{this.canvas.querySelector(`[data-cond="${CSS.escape(a)}"]`)?.focus()})}}condSelect(e,s,t,a,i){if(Y("os-select")&&Y("os-option")){const o=document.createElement("os-select");o.setAttribute("value",e),o.setAttribute("aria-label",a),o.setAttribute("data-cond",t),o.className="atfb-cond__control",o.setAttribute("plain",""),o.title=a;for(const l of s){const c=document.createElement("os-option");c.setAttribute("value",l.value),l.disabled&&c.setAttribute("disabled",""),c.textContent=l.label,o.append(c)}return o.addEventListener("os-pick",l=>{i(String(l.detail?.value??""))}),o}return r("select",{class:"atfb-cond__control atfb-cond__control--native",title:a,attrs:{"aria-label":a,"data-cond":t},on:{change:o=>i(o.target.value)},children:s.map(o=>r("option",{value:o.value,text:o.label,attrs:{selected:o.value===e,disabled:o.disabled}}))})}renderConditionToken(e,s,t=0){if(s.kind==="field"){const a=`${e.id}:source:${t}`,i=(this.schema?.fields??[]).filter(o=>o.id!==e.id&&o.type!=="page_break");return this.condSelect(s.fieldId,[...s.missing?[{value:s.fieldId,label:"Choose a question…"}]:[],...i.map(o=>({value:o.id,label:o.label||o.id}))],a,"Question used by this rule",o=>this.editCondition(e.id,l=>{const c=l.rules[t];c&&(c.field=o,c.value="")},!0,a))}if(s.kind==="verb")return r("button",{class:"atfb-cond__verb",type:"button",text:s.text,title:"Switch between showing and hiding this field when the condition matches.",attrs:{"data-cond":`${e.id}:verb`},on:{click:()=>this.editCondition(e.id,a=>{a.action=a.action==="hide"?"show":"hide"},!0,`${e.id}:verb`)}});if(s.kind==="join")return r("button",{class:"atfb-cond__join",type:"button",text:s.text,title:"Switch between needing every rule (and) or any one of them (or).",attrs:{"data-cond":`${e.id}:join`},on:{click:()=>this.editCondition(e.id,a=>{a.match=a.match==="all"?"any":"all"},!0,`${e.id}:join`)}});if(s.kind==="operator"){const a=`${e.id}:op:${s.ruleIndex}`;return this.condSelect(s.operator,Object.entries(Ie).map(([i,o])=>({value:i,label:o})),a,"How the answer is compared",i=>this.editCondition(e.id,o=>{const l=o.rules[s.ruleIndex];l&&(l.operator=i)},!0,a))}return s.kind==="value"?this.renderConditionValue(e,s):r("span")}renderConditionValue(e,s){const t=`${e.id}:value:${s.ruleIndex}`,a=this.schema?.fields.find(d=>d.id===s.sourceId),i=(d,h)=>this.editCondition(e.id,m=>{const g=m.rules[s.ruleIndex];g&&(g.value=d)},h,t),o=it(a,s.raw,this.config?.countries);if(o){const d=this.condSelect(s.raw,o,t,"The answer that triggers this",h=>i(h,!0));return d.classList.add("atfb-cond__value-select"),d.removeAttribute("plain"),d}const l=["number","range","scale","rating","total"].includes(a?.type??""),c=r("input",{class:"atfb-cond__chip atfb-cond__chip--value atfb-cond__value",value:s.raw,title:"The answer that triggers this. Edit it here.",attrs:{type:"text","aria-label":"The answer that triggers this","data-cond":t,inputmode:l?"decimal":void 0,size:String(Math.max(2,Math.min(24,s.raw.length||2)))}});return c.addEventListener("input",()=>{c.size=Math.max(2,Math.min(24,c.value.length||2)),i(c.value,!1)}),c.addEventListener("change",()=>i(c.value,!0)),c.addEventListener("keydown",d=>{d.key==="Enter"&&(d.preventDefault(),i(c.value,!0))}),c}section(e,s,t,a=!1){const i=r("details",{class:"atfb-section",attrs:{open:this.openSections.get(e)??a},children:[r("summary",{text:s}),...t]});return i.addEventListener("toggle",()=>this.openSections.set(e,i.open)),i}logicMapButton(){const e=be(this.schema?.fields??[]).length>0,s=C(this.logicMapOn?"Hide logic":"Show logic",()=>{this.logicMapOn=!this.logicMapOn,ms(Vt,this.logicMapOn?"on":"off"),this.renderBar(),this.renderCanvas()},this.logicMapOn?"primary":"secondary","randomize");return s.title="Draw a line from each question to the ones it decides.",s.hidden=!e,s}paintLogicMap(e){this.logicMap?.destroy(),this.logicMap=null;const s=this.schema?.fields??[],t=be(s);if(!t.length||!this.logicMapOn)return;e.classList.add("has-logicmap");const a=new As(e);a.setEdges(t),a.highlight(this.selected??""),this.logicMap=a,e.addEventListener("pointerover",i=>{const o=i.target.closest("[data-atfb-card]");a.highlight(o?.dataset.atfbCard??this.selected??"")}),e.addEventListener("pointerleave",()=>a.highlight(this.selected??""))}renderFieldCard(e,s){const t=this.config?.fieldTypes.find(d=>d.type===e.type),a=this.selected===e.id,i=this.schema?.fields??[],o=Es(e,i),l=$s(i).get(e.id)??0,c=r("div",{class:`atfb-card${a?" is-selected":""}`,attrs:{"data-atfb-card":e.id,"data-index":s,tabindex:"0",role:"button","aria-pressed":a,"aria-label":`${e.label||t?.label||e.type}, ${s+1} of ${this.schema?.fields.length??0}`},children:[r("div",{class:"atfb-card__bar",children:[r("div",{class:"atfb-card__grip",attrs:{"aria-hidden":"true"},children:[B("menu")]}),r("div",{class:"atfb-card__head",children:[B(t?.icon??"dashicons-forms"),r("span",{class:"atfb-card__type",text:t?.label??e.type}),this.requiredToggle(e),e.type!=="page_break"?this.conditionToolbar(e):null,l?r("span",{class:"atfb-badge atfb-badge--controls",text:l===1?"controls 1 field":`controls ${l} fields`,title:"Other questions appear or disappear based on this answer."}):null]}),r("div",{class:"atfb-card__actions",children:[this.cardAction("admin-page","Duplicate",()=>this.duplicateField(e.id)),this.cardAction("trash","Delete",()=>void this.deleteField(e.id))]})]}),r("div",{class:"atfb-card__body",children:[pt(e,t,{edit:d=>{const h=this.liveField(e.id);if(!h)return;d(h),this.markDirty(),this.syncInspector(h);const m=this.selected?this.locateField(this.selected):void 0;m?.parent&&m.parent.id===h.id&&this.syncInspector(m.field)},restructure:d=>{const h=this.liveField(e.id);h&&(this.snapshot(),d(h),this.markDirty(),this.renderCanvas(),this.renderInspector())},types:d=>this.config?.fieldTypes.find(h=>h.type===d),selectedId:this.selected}),e.logic.enabled?this.renderCondition(e,o):null]})]});return c.addEventListener("click",d=>{d.target.closest(".atfb-card__actions")||G().recentlyEndedDrag()||this.selectField(e.id)}),c.addEventListener("keydown",d=>{if(d.key==="Enter"||d.key===" "){d.preventDefault(),this.selectField(e.id);return}d.altKey&&(d.key==="ArrowUp"||d.key==="ArrowDown")&&(d.preventDefault(),this.moveField(e.id,d.key==="ArrowUp"?s-1:s+1),window.requestAnimationFrame(()=>{this.canvas.querySelector(`[data-atfb-card="${CSS.escape(e.id)}"]`)?.focus()})),(d.key==="Delete"||d.key==="Backspace")&&(d.preventDefault(),this.deleteField(e.id))}),c.addEventListener("pointerdown",d=>{d.target.closest(".atfb-card__actions")||G().start({payload:Ae(te,c,{fieldId:e.id,field:e,isNew:!1},d),origin:d})}),c}addConditionRule(e){this.snapshot(),this.editCondition(e,s=>{const t=this.schema?.fields.find(a=>a.id!==e&&a.type!=="page_break");s.rules.push({field:t?.id??"",operator:"is",value:""})},!0,`${e}:source:${this.liveField(e)?.logic.rules.length??0}`),this.snapshot()}conditionToolbar(e){const s=r("button",{class:`atfb-req atfb-condition-toggle${e.logic.enabled?" is-on":""}`,type:"button",title:e.logic.enabled?"Edit conditional logic":"Set up conditional logic",attrs:{"aria-haspopup":"dialog","data-cond":`${e.id}:enabled`},children:[r("span",{class:"atfb-condition-dot",attrs:{"aria-hidden":"true"}}),r("span",{text:"Conditional"})],on:{click:()=>this.openConditionEditor(e.id)}});for(const t of["pointerdown","click","keydown"])s.addEventListener(t,a=>a.stopPropagation());return s}openConditionEditor(e){const s=this.liveField(e);if(!s)return;const t={...s.logic,rules:s.logic.rules.map(d=>({...d}))},a=r("div",{class:"atfb-overlay"}),i=r("div",{class:"atfb-modal atfb-condition-editor",attrs:{role:"dialog","aria-modal":"true","aria-label":"Conditional logic",tabindex:"-1"}}),o=()=>{a.remove(),this.root.querySelector(`[data-cond="${CSS.escape(e)}:enabled"]`)?.focus()},l="button:not([disabled]), input, select, os-select, os-checkbox-label, os-button:not([disabled])",c=()=>{const d=[...i.querySelectorAll(l)].indexOf(document.activeElement),h=(f,y=!1)=>{f(t),y&&c()},m=D(t.action,[{value:"show",label:"Show"},{value:"hide",label:"Hide"}],f=>{t.action=f});m.setAttribute("aria-label","Conditional action");const g=D(t.match,[{value:"all",label:"all"},{value:"any",label:"any"}],f=>{t.match=f,c()});g.setAttribute("aria-label","Match rules");const u=r("button",{class:"atfb-copy-condition",type:"button",children:[B("admin-page"),r("span",{text:"Copy condition"})],on:{click:()=>Dt({root:a,to:`field:${e}`,schema:()=>this.schema?{...this.schema,fields:this.schema.fields.map(f=>f.id===e?{...f,logic:t}:f)}:null,onCopy:()=>{c(),i.focus()}})}});if(i.replaceChildren(r("div",{class:"atfb-condition-editor__heading",children:[r("span",{class:"atfb-condition-editor__icon",children:[B("randomize")]}),r("div",{children:[r("h2",{text:"Conditional logic"}),r("p",{text:s.label||"Untitled field"})]})]}),r("div",{class:"atfb-condition-editor__switch",children:[q("Enable conditions",t.enabled,f=>{t.enabled=f,c()}),r("p",{text:"Choose when this field appears in your form."})]}),...t.enabled?[r("div",{class:"atfb-condition-editor__sentence",children:[m,"this field when",g,"of these rules match:"]}),...this.logicRulesEditor(t,h,e)]:[r("p",{class:"atfb-condition-editor__empty",text:"This field is always visible. Enable conditions to choose when to show or hide it."})],r("div",{class:"atfb-condition-editor__footer",children:[u,r("div",{class:"atfb-modal__actions",children:[C("Cancel",o),C("Save conditions",()=>{if(!this.liveField(e)){o();return}this.snapshot(),this.editCondition(e,f=>Object.assign(f,t,{rules:t.rules.map(y=>({...y}))})),this.snapshot(),o()},"primary")]})]})),d>=0){const f=i.querySelectorAll(l);f[Math.min(d,f.length-1)]?.focus()}};a.append(i),a.addEventListener("click",d=>{d.target===a&&o()}),a.addEventListener("keydown",d=>{if(d.key==="Escape"&&(d.preventDefault(),o()),d.key==="Tab"){const h=[...i.querySelectorAll(l)],m=h[0],g=h[h.length-1];d.shiftKey&&(document.activeElement===m||document.activeElement===i)?(d.preventDefault(),g?.focus()):!d.shiftKey&&document.activeElement===g&&(d.preventDefault(),m?.focus())}d.stopPropagation()}),this.root.append(a),c(),i.focus()}requiredToggle(e){return r("button",{class:`atfb-req${e.required?" is-on":""}`,type:"button",text:e.required?"Required":"Optional",title:e.required?"This must be answered. Click to make it optional.":"Click to make this required.",attrs:{"aria-pressed":e.required?"true":"false"},on:{pointerdown:t=>t.stopPropagation(),click:t=>{t.stopPropagation(),e.required=!e.required,this.markDirty(),this.renderCanvas(),this.renderInspector()}}})}cardAction(e,s,t){return r("button",{class:"atfb-card__action",type:"button",title:s,attrs:{"aria-label":s},on:{click:a=>{a.stopPropagation(),t()}},children:[B(e)]})}gapAnimator(e,s,t,a){const i=r("div",{class:"atfb-marker",attrs:{"aria-hidden":"true"}});let o=null,l=null,c=0;const d=()=>{i.remove(),l?.classList.remove("atfb-lifted"),l=null,o=null},h=u=>{const f=Array.from(e.querySelectorAll(s)),y=new Map(f.filter(x=>!x.classList.contains("atfb-lifted")).map(x=>[x,x.getBoundingClientRect().top]));u();for(const x of f)x.style.transition="none",x.style.transform="";const p=f.filter(x=>{const F=y.get(x);if(F===void 0||!x.isConnected||x.classList.contains("atfb-lifted"))return!1;const S=F-x.getBoundingClientRect().top;return Math.abs(S)<.5?!1:(x.style.transform=`translateY(${S}px)`,!0)});e.offsetHeight;for(const x of f){if(!p.includes(x)){x.style.transition="";continue}x.style.transition="transform 160ms ease",x.style.transform="",x.addEventListener("transitionend",()=>{x.style.transition=""},{once:!0})}},m=u=>{const f=u.detail;if(f?.payload?.type!==te||!e.classList.contains("is-dropping")||a&&!a(f.payload.data??{}))return;const y=f.payload.data?.fieldId,p=typeof y=="string"?t(y):null,x=st(e,s,f.clientY??0,p??void 0);x===o&&i.isConnected||(o=x,h(()=>{p&&p!==l&&(l?.classList.remove("atfb-lifted"),p.classList.add("atfb-lifted"),l=p),i.style.blockSize=`${Math.max(8,Math.round(c))}px`;const F=Array.from(e.querySelectorAll(s)).filter(S=>S!==p);x>=F.length?e.append(i):F[x].before(i)}))},g=()=>{e.classList.remove("is-dropping"),window.setTimeout(()=>h(d),0)};return document.addEventListener("os.drag.move",m),document.addEventListener("os.drag.end",g),{enter:u=>{const f=u?t(u):null;c=f&&!f.classList.contains("atfb-lifted")?f.getBoundingClientRect().height:c||44},leave:()=>h(d),drop:(u,f)=>{const y=o??st(e,s,u,f??void 0);return d(),y},teardown:()=>{document.removeEventListener("os.drag.move",m),document.removeEventListener("os.drag.end",g),d()}}}registerCanvasTarget(e){this.canvasTarget?.(),this.canvasTarget=null;const s=this.gapAnimator(e,".atfb-card",i=>this.canvas.querySelector(`[data-atfb-card="${CSS.escape(i)}"]`)),t=G().registerDropTarget({id:`atfb-canvas-${this.form?.id??0}`,element:e,accept:i=>i.type===te,onEnter:i=>{e.classList.add("is-dropping"),s.enter(i.payload.data.fieldId)},onLeave:()=>{e.classList.remove("is-dropping"),s.leave()},onDrop:(i,o)=>{e.classList.remove("is-dropping");const l=i.payload.data,c=l.fieldId?this.canvas.querySelector(`[data-atfb-card="${CSS.escape(l.fieldId)}"]`):null,d=s.drop(o.clientY,c);if(l.isNew&&l.fieldType){this.addField(l.fieldType,d);return}if(l.fieldId&&this.locateField(l.fieldId)){this.relocateField(l.fieldId,null,d);return}l.field&&this.insertField({...l.field,id:""},d)}}),a=this.wireRepeaterZones(e);this.canvasTarget=()=>{t(),a.forEach(i=>i()),s.teardown()}}wireRepeaterZones(e){const s=[];return e.querySelectorAll("[data-atfb-repeater-zone]").forEach(t=>{const a=t.dataset.atfbRepeaterZone??"",i=l=>{const c=l;if(c.fieldId===a)return!1;const d=c.isNew?c.fieldType:this.locateField(c.fieldId??"")?.field.type??c.field?.type;return!!d&&this.allowedInRepeater(d)},o=this.gapAnimator(t,".atfb-subcard",l=>e.querySelector(`[data-atfb-subfield="${CSS.escape(l)}"]`),i);s.push(()=>o.teardown()),s.push(G().registerDropTarget({id:`atfb-repzone-${this.form?.id??0}-${a}`,element:t,accept:l=>l.type===te&&i(l.data),onEnter:l=>{t.classList.add("is-dropping"),o.enter(l.payload.data.fieldId)},onLeave:()=>{t.classList.remove("is-dropping"),o.leave()},onDrop:(l,c)=>{t.classList.remove("is-dropping");const d=l.payload.data,h=d.fieldId?e.querySelector(`[data-atfb-subfield="${CSS.escape(d.fieldId)}"]`):null,m=o.drop(c.clientY,h);if(d.isNew&&d.fieldType){this.addFieldToRepeater(d.fieldType,a,m);return}if(d.fieldId&&this.locateField(d.fieldId)){this.relocateField(d.fieldId,a,m);return}d.field&&this.insertFieldIntoRepeater({...d.field,id:""},a,m)}})),t.querySelectorAll(".atfb-subcard").forEach(l=>{const c=l.dataset.atfbSubfield??"";l.addEventListener("click",d=>{d.target.closest(".atfb-preview__remove")||(d.stopPropagation(),!G().recentlyEndedDrag()&&this.selectField(c))}),l.addEventListener("keydown",d=>{if(d.stopPropagation(),d.key==="Enter"||d.key===" "){d.preventDefault(),this.selectField(c);return}if(d.altKey&&(d.key==="ArrowUp"||d.key==="ArrowDown")){d.preventDefault();const h=this.locateField(c);h&&(this.relocateField(c,a,d.key==="ArrowUp"?h.index-1:h.index+1),window.requestAnimationFrame(()=>{this.canvas.querySelector(`[data-atfb-subfield="${CSS.escape(c)}"]`)?.focus()}));return}(d.key==="Delete"||d.key==="Backspace")&&(d.preventDefault(),this.deleteField(c))}),l.addEventListener("pointerdown",d=>{if(d.stopPropagation(),d.target.closest(".atfb-preview__remove"))return;const h=this.locateField(c);h&&G().start({payload:Ae(te,l,{fieldId:c,parentId:a,field:h.field,isNew:!1},d),origin:d})})})}),s}addField(e,s){const t=this.buildField(e);t&&this.insertField(t,s)}buildField(e){const s=this.config?.fieldTypes.find(a=>a.type===e);return!s||!this.schema?void 0:{id:this.nextFieldId(),type:e,label:s.input?s.label:"",placeholder:"",hint:"",required:!1,width:"full",cssClass:"",default:"",choices:s.choices?[{label:"First choice",value:"first"},{label:"Second choice",value:"second"}]:[],logic:{enabled:!1,action:"show",match:"all",rules:[]},messages:{},prefill:"",...s.settings}}allowedInRepeater(e){const s=this.config?.fieldTypes.find(t=>t.type===e);return!s||!s.input?!1:!["repeater","page_break","file","signature","total"].includes(e)}addFieldToRepeater(e,s,t){if(!this.allowedInRepeater(e))return;const a=this.buildField(e);a&&this.insertFieldIntoRepeater(a,s,t)}insertFieldIntoRepeater(e,s,t){const a=this.liveField(s);if(!this.schema||!a||a.type!=="repeater")return;e.id||(e.id=this.nextFieldId()),this.snapshot(),Array.isArray(a.fields)||(a.fields=[]);const i=a.fields,o=t===void 0?i.length:Math.max(0,Math.min(t,i.length));i.splice(o,0,e),this.selected=e.id,this.markDirty(),this.renderCanvas(),this.renderInspector(),window.requestAnimationFrame(()=>{this.canvas.querySelector(`[data-atfb-subfield="${CSS.escape(e.id)}"]`)?.focus()})}relocateField(e,s,t){if(!this.schema)return;const a=this.locateField(e);if(!a)return;if(!a.parent&&!s){this.moveField(e,t);return}const i=s?this.liveField(s):null;if(s&&(!i||i.type!=="repeater"||e===s))return;i&&!Array.isArray(i.fields)&&(i.fields=[]);const o=i?i.fields:this.schema.fields;this.snapshot(),a.list.splice(a.index,1);const l=Math.max(0,Math.min(t,o.length));o.splice(l,0,a.field),this.selected=a.field.id,this.markDirty(),this.renderCanvas(),this.renderInspector()}insertField(e,s){if(!this.schema)return;e.id||(e.id=this.nextFieldId()),this.snapshot();const t=s===void 0?this.schema.fields.length:Math.max(0,Math.min(s,this.schema.fields.length));this.schema.fields.splice(t,0,e),this.selected=e.id,this.markDirty(),this.renderCanvas(),this.renderInspector(),window.requestAnimationFrame(()=>{this.canvas.querySelector(`[data-atfb-card="${CSS.escape(e.id)}"]`)?.focus()})}moveField(e,s){if(!this.schema)return;const t=Zt(this.schema.fields,e,s);if(!t)return;this.snapshot();const[a]=this.schema.fields.splice(t.from,1);this.schema.fields.splice(t.to,0,a),this.markDirty(),this.renderCanvas()}duplicateField(e){if(!this.schema)return;const s=this.locateField(e);if(!s)return;const t=JSON.parse(JSON.stringify(s.field)),a=this.usedFieldIds(),i=()=>{let o=a.size+1;for(;a.has(`f${o}`);)o++;const l=`f${o}`;return a.add(l),l};t.id=i();for(const o of t.fields??[])o.id=i();if(s.parent){this.snapshot(),s.list.splice(s.index+1,0,t),this.selected=t.id,this.markDirty(),this.renderCanvas(),this.renderInspector();return}this.insertField(t,s.index+1)}async deleteField(e){if(!this.schema)return;const s=this.locateField(e);if(!s)return;const t=this.schema.fields.filter(i=>i.logic?.rules?.some(o=>o.field===e)),a=t.length?`Delete this field? ${t.length} other field${t.length===1?"":"s"} use it in a condition, and those conditions will stop working.`:Qe("confirmDelete","Delete this? It cannot be undone.");await ae(a,"Delete field")&&(this.snapshot(),s.list.splice(s.index,1),this.selected===e&&(this.selected=null),this.markDirty(),this.renderCanvas(),this.renderInspector())}selectField(e){this.selected=e;for(const s of this.canvas.querySelectorAll("[data-atfb-card]")){const t=s.dataset.atfbCard===e;s.classList.toggle("is-selected",t),s.setAttribute("aria-pressed",t?"true":"false")}for(const s of this.canvas.querySelectorAll("[data-atfb-subfield]"))s.classList.toggle("is-selected",s.dataset.atfbSubfield===e);this.logicMap?.highlight(e),this.renderInspector()}nextFieldId(){const e=this.usedFieldIds();let s=e.size+1;for(;e.has(`f${s}`);)s++;return`f${s}`}usedFieldIds(){const e=new Set;for(const s of this.schema?.fields??[]){e.add(s.id);for(const t of s.fields??[])e.add(t.id)}return e}nextEntryId(e,s){const t=new Set(s.map(i=>i.id));let a=s.length+1;for(;t.has(`${e}${a}`);)a++;return`${e}${a}`}renderTypeSettings(e,s,t,a){for(const i of t){const o=Kt[i];o&&(this.inspector.append(Gt(e,o,a)),o.also&&this.inspector.append(Gt(e,{...o,key:o.also.key,label:o.also.label,hint:o.also.hint},a)))}t.includes("rows")&&e.type!=="likert"&&this.inspector.append(w("Lines tall",V(String(e.rows??""),i=>a("rows",i)))),t.includes("rows")&&e.type==="likert"&&this.inspector.append(this.renderStatementList(e,a)),t.includes("parts")&&this.inspector.append(this.renderPartsEditor(e,s,a)),t.includes("default")&&!["files","object"].includes(s?.value??"")&&this.inspector.append(this.renderDefaultControl(e,s,a))}renderStatementList(e,s){const t=Array.isArray(e.rows)?e.rows:[];return w("Statements",pe(t.map(a=>a.label??"").join(` +var allTerrainFormsBuilder=function(ee){"use strict";class as{constructor(){this.targets=[],this.active=null,this.lastEndMs=0}start(e){if(this.active||e.origin.button!==0)return null;const{payload:s,origin:t}=e,a=t.clientX,i=t.clientY;let o=!1,l=!1,c=null,d=null,h=0,m=0;const g=()=>{document.removeEventListener("pointermove",x),document.removeEventListener("pointerup",F),document.removeEventListener("pointercancel",S),document.removeEventListener("keydown",j),window.removeEventListener("blur",S),c?.remove(),c=null,s.source.classList.remove("atf-is-dragging"),document.body.classList.remove("atf-drag-active"),d?.onLeave?.(f),d=null,this.active=null,this.lastEndMs=Date.now()},u=(L,b)=>{document.dispatchEvent(new CustomEvent(L,{detail:{payload:s,clientX:b?.clientX,clientY:b?.clientY}}))},f={payload:s,isFinished:()=>l,cancel:(L="caller")=>{l||(l=!0,g(),o&&u("os.drag.end"),e.onCancel?.(L))}},y=L=>{o=!0,s.source.classList.add("atf-is-dragging"),document.body.classList.add("atf-drag-active");const b=s.source.getBoundingClientRect();h=s.ghost?.offsetX??a-b.left,m=s.ghost?.offsetY??i-b.top,c=s.ghost?.element??s.source.cloneNode(!0),c.classList.add("atf-drag-ghost"),c.style.width=`${b.width}px`,document.body.appendChild(c),p(L),u("os.drag.start",L)},p=L=>{c&&(c.style.transform=`translate3d(${L.clientX-h}px, ${L.clientY-m}px, 0)`)},x=L=>{if(l)return;if(!o){if(Math.hypot(L.clientX-a,L.clientY-i)<4)return;y(L)}p(L);const b=this.hitTest(L.clientX,L.clientY);b!==d&&(d?.onLeave?.(f),d=b,d?.onEnter?.(f)),u("os.drag.move",L)},F=L=>{if(l)return;if(!o){l=!0,g(),e.onClickOnly?.();return}const b=d;if(l=!0,g(),b&&b.accept(s)){e.onCommit?.(b),b.onDrop(f,{clientX:L.clientX,clientY:L.clientY}),u("os.drag.end",L);return}u("os.drag.end",L),e.onCancel?.(b?"rejected":"no-target")},S=()=>f.cancel("pointercancel"),j=L=>{L.key==="Escape"&&f.cancel("escape")};return document.addEventListener("pointermove",x),document.addEventListener("pointerup",F),document.addEventListener("pointercancel",S),document.addEventListener("keydown",j),window.addEventListener("blur",S),this.active=f,f}registerDropTarget(e){return this.targets=this.targets.filter(s=>s.id!==e.id),this.targets.push(e),()=>{this.targets=this.targets.filter(s=>s.id!==e.id)}}isDragging(){return this.active!==null}recentlyEndedDrag(e=500){return Date.now()-this.lastEndMso.right||so.bottom)continue;const l=is(i.element);if(l>a){t=i,a=l;continue}l===a&&t&&rs(i.element,t.element)&&(t=i)}return t}}function is(n){let e=0,s=n;for(;s;)e++,s=s.parentElement;return e}function rs(n,e){return(e.compareDocumentPosition(n)&Node.DOCUMENT_POSITION_FOLLOWING)!==0}function Le(){return window.wp?.os??null}let $e=null;function G(){const n=Le();return n?.dragManager?n.dragManager:($e||($e=new as),$e)}function os(n){const e=i=>{const o=i.detail?.payload;return o&&n.includes(o.type)?o.source:null},s=i=>i.preventDefault(),t=i=>{const o=e(i);o&&(o.classList.add("atf-is-dragging"),document.body.classList.add("atf-drag-active"),window.getSelection()?.removeAllRanges(),document.addEventListener("selectstart",s))},a=i=>{const o=e(i);o&&(o.classList.remove("atf-is-dragging"),document.body.classList.remove("atf-drag-active"),document.removeEventListener("selectstart",s))};return document.addEventListener("os.drag.start",t),document.addEventListener("os.drag.end",a),()=>{document.removeEventListener("os.drag.start",t),document.removeEventListener("os.drag.end",a),document.removeEventListener("selectstart",s)}}const ls=["atfb","atfe","atfs","atfm"];function Ae(n,e,s,t,a){const i=e.getBoundingClientRect();if(!a){const o=ls.find(l=>e.closest(`.${l}`));if(o){const l=e.cloneNode(!0);l.style.transition="",l.style.transform="",a=document.createElement("div"),a.className=`${o} atf-ghost-scope`,a.appendChild(l)}}return a&&(a.style.width=`${Math.round(i.width)}px`,a.style.maxWidth=`${Math.round(i.width)}px`,a.style.boxSizing="border-box"),{type:n,source:e,data:s,ghost:{element:a,offsetX:t.clientX-i.left,offsetY:t.clientY-i.top,hint:{neutral:"",accept:"",reject:"",hidden:!0}}}}function st(n,e,s,t){const a=Array.from(n.querySelectorAll(e)).filter(i=>i!==t&&!i.classList.contains("atf-drag-ghost"));for(let i=0;ia.fetch(l,c,{source:"allterrain-forms"}):(l,c)=>fetch(l,c))(s,{credentials:"same-origin",...e,headers:t});if(!o.ok){let l=`Request failed with status ${o.status}.`,c="";try{const d=await o.json();l=d.message??l,c=d.code??""}catch{}throw new he(l,o.status,c)}if(o.status!==204)return await o.json()}const z=n=>Te(n);async function cs(n){if(!U?.wpRestUrl)throw new he("AllTerrain Forms is not configured on this page.",0);const e=U.nonce?{"X-WP-Nonce":U.nonce}:{},s=Le(),t=nt(U.wpRestUrl,n),a=s?.fetch?await s.fetch(t,{credentials:"same-origin",headers:e},{source:"allterrain-forms"}):await fetch(t,{credentials:"same-origin",headers:e});if(!a.ok)throw new he(`Request failed with status ${a.status}.`,a.status);return await a.json()}function ds(n){if(!n||!n.includes("&"))return n;const e=document.createElement("textarea");return e.innerHTML=n,e.value}const X=(n,e)=>Te(n,{method:"POST",body:JSON.stringify(e)}),ue=n=>Te(n,{method:"DELETE"});function at(n){const e=new URLSearchParams;for(const[t,a]of Object.entries(n))a===void 0||a===""||a===!1||e.set(t,String(a));const s=e.toString();return s?`?${s}`:""}function pe(n){const e=n?.schema?.settings;return e&&(!e.themeOverrides||Array.isArray(e.themeOverrides))&&(e.themeOverrides={...e.themeOverrides}),n}const I={config:()=>z("/config"),mailpoet:()=>z("/mailpoet"),listForms:()=>z("/forms"),listArchivedForms:()=>z("/forms?archived=1"),archiveForm:n=>X(`/forms/${n}/archive`,{}),unarchiveForm:n=>X(`/forms/${n}/unarchive`,{}),getForm:n=>z(`/forms/${n}`).then(pe),createForm:n=>X("/forms",n).then(pe),updateForm:(n,e)=>X(`/forms/${n}`,e).then(pe),duplicateForm:n=>X(`/forms/${n}/duplicate`,{}).then(pe),deleteForm:n=>ue(`/forms/${n}`),preview:(n,e)=>X(`/forms/${n}/preview`,e),pages:()=>cs("wp/v2/pages?per_page=100&status=publish&orderby=title&order=asc&_fields=id,title").then(n=>n.map(e=>({id:e.id,title:ds(e.title?.rendered??"")||`#${e.id}`}))),mergeTags:n=>z(`/forms/${n}/merge-tags`).then(e=>e.groups),analytics:(n,e="")=>z(`/forms/${n}/analytics${e?`?dimension=${encodeURIComponent(e)}`:""}`),demoStatus:()=>z("/demo"),demoSeed:n=>X("/demo",n?{count:n}:{}),demoRemove:()=>ue("/demo"),listEntries:n=>z(`/entries${at(n)}`),getEntry:n=>z(`/entries/${n}`),updateEntry:(n,e)=>X(`/entries/${n}`,e),deleteEntry:n=>ue(`/entries/${n}`),exportEntries:n=>z(`/entries/export${at(n)}`),listThemes:()=>z("/themes"),saveTheme:n=>X("/themes",n),deleteTheme:n=>ue(`/themes/${n}`)};function it(n,e,s={}){if(!n)return null;let t;const a=(o,l)=>{const c=n[o];return c==null||!Number.isFinite(Number(c))?l:Number(c)},i=(o,l,c=1)=>Array.from({length:Math.floor((l-o)/c+1e-9)+1},(d,h)=>{const m=String(Number((o+h*c).toPrecision(12)));return{value:m,label:m}});if(n.type==="scale"){const o=Math.trunc(a("min",0));let l=Math.trunc(a("max",10));l<=o&&(l=o+10),t=i(o,Math.min(l,o+20))}else if(n.type==="rating")t=i(1,Math.max(2,Math.min(10,Math.abs(Math.trunc(a("max",5))))));else if(n.type==="switch"||n.type==="consent")t=[{value:"1",label:"Checked"},{value:"",label:"Unchecked"}];else if(n.type==="country")t=Object.entries(s).map(([o,l])=>({value:o,label:l}));else if(n.type==="range"){const o=a("min",0),l=a("max",100),c=n.step==="any"?0:a("step",1);if(c<=0||l999)return null;t=i(o,l,c)}else if(n.choices?.length||["select","multiselect","radio","checkboxes","image_choice","quiz","likert"].includes(n.type))t=(n.choices??[]).map(o=>({value:o.value,label:o.label||o.value}));else return null;return!t.some(o=>o.value===e)&&e!==""&&t.unshift({value:e,label:`${e} (stored value; unavailable)`,disabled:!0}),t.some(o=>o.value==="")||t.unshift({value:"",label:"Choose a value…",disabled:!0}),t}function r(n,e={}){const s=document.createElement(n);e.class&&(s.className=e.class),e.text!==void 0&&(s.textContent=e.text),e.html!==void 0&&(s.innerHTML=e.html),e.title&&(s.title=e.title),e.type&&"type"in s&&(s.type=e.type),e.value!==void 0&&"value"in s&&(s.value=e.value),e.placeholder!==void 0&&"placeholder"in s&&(s.placeholder=e.placeholder),e.href&&"href"in s&&(s.href=e.href);for(const[t,a]of Object.entries(e.attrs??{}))a===void 0||a===!1||s.setAttribute(t,a===!0?"":String(a));Object.assign(s.style,e.style??{});for(const[t,a]of Object.entries(e.on??{}))s.addEventListener(t,a);for(const t of e.children??[])t==null||t===!1||s.append(t);return s}function R(n){n.replaceChildren()}function B(n){return r("span",{class:`dashicons ${n.startsWith("dashicons-")?n:`dashicons-${n}`}`,attrs:{"aria-hidden":"true"}})}function w(n,e,s){const t=e.matches("input, select, textarea, button")?e:e.querySelector("input, select, textarea");if(t){const a=t.id||`atf-c-${Math.random().toString(36).slice(2,9)}`;return t.id=a,r("div",{class:"atfb-row",children:[r("label",{class:"atfb-row__label",text:n,attrs:{for:a}}),e,s?r("p",{class:"atfb-row__hint",text:s}):null]})}return r("div",{class:"atfb-row",children:[r("span",{class:"atfb-row__label",text:n}),e,s?r("p",{class:"atfb-row__hint",text:s}):null]})}function T(n,e,s=""){return r("input",{class:"atfb-input",type:"text",value:n,placeholder:s,on:{input:t=>e(t.target.value)}})}function V(n,e){if(Y("os-number-field")){const s=document.createElement("os-number-field");return s.setAttribute("value",n),s.classList.add("atfb-field"),s.addEventListener("os-input-change",t=>{e(String(t.detail?.value??""))}),s}return r("input",{class:"atfb-input",type:"number",value:n,on:{input:s=>e(s.target.value)}})}function fe(n,e,s=4){const t=r("textarea",{class:"atfb-input atfb-input--area",attrs:{rows:s},on:{input:a=>e(a.target.value)}});return t.value=n,t}function D(n,e,s){if(Y("os-select")&&Y("os-option")){const t=document.createElement("os-select");t.setAttribute("value",n),t.classList.add("atfb-field");for(const a of e){const i=document.createElement("os-option");i.setAttribute("value",a.value),a.disabled&&i.setAttribute("disabled",""),i.textContent=a.label,t.append(i)}return t.addEventListener("os-pick",a=>{s(String(a.detail?.value??""))}),t}return r("select",{class:"atfb-input atfb-select",on:{change:t=>s(t.target.value)},children:e.map(t=>r("option",{value:t.value,text:t.label,attrs:{selected:t.value===n,disabled:t.disabled}}))})}function q(n,e,s){if(Y("os-checkbox-label")){const a=document.createElement("os-checkbox-label");return a.setAttribute("label",n),a.classList.add("atfb-check"),e&&a.setAttribute("checked",""),a.addEventListener("os-checkbox-change",i=>{s(!!i.detail?.checked)}),a}const t=r("input",{type:"checkbox",on:{change:a=>s(a.target.checked)}});return t.checked=e,r("label",{class:"atfb-check",children:[t,r("span",{text:n})]})}function C(n,e,s="secondary",t){const a=[t?B(t):null,r("span",{text:n})];if(Y("os-button")){const i=document.createElement("os-button");i.setAttribute("variant",s),i.setAttribute("type","button"),i.classList.add("atfb-button",`atfb-button--${s}`),i.addEventListener("click",e);for(const o of a)o&&i.append(o);return i}return r("button",{class:`atfb-button atfb-button--${s}`,type:"button",on:{click:e},children:a})}function Y(n){return typeof customElements<"u"&&!!customElements.get(n)}const hs=["os-button","os-checkbox-label","os-number-field","os-select","os-option","os-segmented","os-segment","os-color-field","os-empty-state"];let me=null;function us(){if(me)return me;const n=window.wp?.os;return me=n?.loadComponents?n.loadComponents(hs).catch(()=>{}):Promise.resolve(),me}async function ie(n,e=""){const s=window.wp?.os;return s?.confirm?s.confirm({title:e,message:n,danger:!0}):window.confirm(n)}function N(n,e="",s="info"){const t=window.wp?.os;if(t?.notify){t.notify({title:n,body:e,type:s});return}console.info(`[AllTerrain Forms] ${n}${e?`: ${e}`:""}`)}function ps(n){let e=0,s;return(...t)=>{s=t,!e&&(e=window.requestAnimationFrame(()=>{e=0,n(...s)}))}}function Me(n,e){let s=0;return(...t)=>{window.clearTimeout(s),s=window.setTimeout(()=>n(...t),e)}}function fs(n){try{return window.localStorage.getItem(n)??""}catch{return""}}function ms(n,e){try{window.localStorage.setItem(n,e)}catch{}}function Fe(n){const e=n.closest(".os-window__body");e&&(e.addEventListener("scroll",()=>{e.scrollTop&&(e.scrollTop=0),e.scrollLeft&&(e.scrollLeft=0)},{passive:!0}),e.scrollTop=0,e.scrollLeft=0)}function rt(){const n=[window];try{window.parent&&window.parent!==window&&n.push(window.parent)}catch{}return n}function bs(n){const e=n.wp?.os?.windowManager?.getAll?.()??[];for(const s of e){const t=s.iframe??s.element?.querySelector?.("iframe");if(t&&t.contentWindow===window)return s}return null}const gs=4e3,vs=100;function ys(n,e){const s=Date.now()+gs,t=()=>{const a=bs(n);if(a&&a.id!==e){typeof a.close=="function"?a.close():n.wp?.os?.windowManager?.remove?.(a.id);return}!a&&Date.now()0?{form:s}:void 0;s>0&&ks(s);for(const a of rt())if(a.wp?.os?.openWindow?.(e,{source:"handoff",params:t})){window.setTimeout(()=>ys(a,e),0);return}}function _s(){document.addEventListener("click",n=>{const e=n.target?.closest?.("[data-atf-open-window]");if(!e)return;n.preventDefault();const s=e.getAttribute("data-atf-open-window")??"";for(const t of rt())if(t.wp?.os?.openWindow?.(s,{source:"handoff"}))return})}const xs="allterrain-forms/requested-form";function ks(n){try{window.sessionStorage.setItem(xs,String(n))}catch{}}function ot(n){return`allterrain-forms/requested-form-${n}`}function Ss(n){try{const e=window.sessionStorage.getItem(ot(n));return window.sessionStorage.removeItem(ot(n)),Number(e)||0}catch{return 0}}const Ie={is:"is",is_not:"is not",contains:"contains",not_contains:"does not contain",starts_with:"starts with",ends_with:"ends with",greater:"is more than",less:"is less than",greater_equal:"is at least",less_equal:"is at most",empty:"is empty",not_empty:"has any answer"},De=["empty","not_empty"];function lt(n,e){const s=n.find(t=>t.id===e);return s?s.label||"an untitled question":null}function ct(n,e,s){return n.find(i=>i.id===e)?.choices?.find(i=>i.value===s)?.label||s}function Cs(n,e){const s=Ie[n.operator]??String(n.operator);if(De.includes(n.operator))return s;const t=ct(e,n.field,n.value);return`${n.operator==="is"?"":`${s} `}${t!==""?t:"(nothing)"}`}function be(n,e,s=0){const t=lt(e,n.field),a=Ie[n.operator]??String(n.operator);if(t===null)return[{kind:"field",text:"a question that no longer exists",fieldId:n.field,missing:!0}];const i=[{kind:"field",text:t,fieldId:n.field,missing:!1},{kind:"operator",text:a,operator:n.operator,ruleIndex:s}];if(De.includes(n.operator))return i;const o=ct(e,n.field,n.value);return i.push({kind:"value",text:o!==""?o:"(nothing)",raw:n.value,sourceId:n.field,ruleIndex:s}),i}function Es(n,e){const s=n.logic;if(!s?.enabled||!s.rules.length)return[];const t=[{kind:"verb",text:s.action==="hide"?"Hidden when":"Shown when"}];return s.rules.forEach((a,i)=>{i>0&&t.push({kind:"join",text:s.match==="all"?"and":"or"}),t.push(...be(a,e,i))}),t}function qe(n){return n.map(e=>e.text).join(" ")}function Ls(n,e){return qe(be(n,e))}function ge(n){const e=[];for(const s of n){const t=s.logic;if(t?.enabled)for(const a of t.rules)!a.field||a.field===s.id||e.push({from:a.field,to:s.id,label:Ls(a,n),short:Cs(a,n),action:t.action,broken:lt(n,a.field)===null})}return e}function $s(n){const e=new Map,s=new Set;for(const t of ge(n)){const a=`${t.from}->${t.to}`;t.broken||s.has(a)||(s.add(a),e.set(t.from,(e.get(t.from)??0)+1))}return e}const dt=10,ht=22,ne="http://www.w3.org/2000/svg";class As{constructor(e){this.edges=[],this.teardowns=[],this.frame=0,this.host=e,this.svg=document.createElementNS(ne,"svg"),this.svg.setAttribute("class","atfb-logicmap"),this.svg.setAttribute("aria-hidden","true"),this.svg.setAttribute("focusable","false"),e.append(this.svg);const s=()=>this.schedule();window.addEventListener("resize",s),this.teardowns.push(()=>window.removeEventListener("resize",s));const t=e.closest(".atfb__canvas")??e;if(t.addEventListener("scroll",s,{passive:!0}),this.teardowns.push(()=>t.removeEventListener("scroll",s)),typeof ResizeObserver<"u"){const a=new ResizeObserver(s);a.observe(e),this.teardowns.push(()=>a.disconnect())}}setEdges(e){this.edges=e,this.schedule()}highlight(e){this.svg.classList.toggle("is-focused",!!e),this.svg.querySelectorAll("[data-from]").forEach(s=>{const t=e&&(s.dataset.from===e||s.dataset.to===e);s.classList.toggle("is-lit",!!t)})}destroy(){this.teardowns.forEach(e=>e()),this.teardowns=[],this.svg.remove(),this.frame&&cancelAnimationFrame(this.frame)}schedule(){this.frame||(this.frame=requestAnimationFrame(()=>{this.frame=0,this.draw()}))}draw(){this.svg.replaceChildren();const e=this.host.getBoundingClientRect();this.svg.setAttribute("viewBox",`0 0 ${e.width} ${e.height}`),this.svg.setAttribute("width",String(e.width)),this.svg.setAttribute("height",String(e.height));const s=new Map;for(const t of this.edges){const a=this.cardRect(t.from),i=this.cardRect(t.to);if(!a||!i)continue;const o=s.get(t.from)??0;s.set(t.from,o+1),this.drawEdge(t,a,i,e,o)}}cardRect(e){const s=this.host.querySelector(`[data-atfb-card="${CSS.escape(e)}"]`);return s?s.getBoundingClientRect():null}drawEdge(e,s,t,a,i){const o=s.right-a.left,l=s.top+s.height/2-a.top,c=t.right-a.left,d=t.top+t.height/2-a.top,h=Math.max(0,a.width-dt-Math.max(o,c)),m=22+i*11+Math.min(22,Math.abs(d-l)/8),g=Math.max(12,Math.min(m,h*.4)),u=document.createElementNS(ne,"path");u.setAttribute("d",`M ${o} ${l} C ${o+g} ${l}, ${c+g} ${d}, ${c} ${d}`),u.setAttribute("class",`atfb-logicmap__path is-${e.action}${e.broken?" is-broken":""}`),u.dataset.from=e.from,u.dataset.to=e.to;const f=document.createElementNS(ne,"title");f.textContent=e.label,u.append(f);const y=document.createElementNS(ne,"circle");y.setAttribute("cx",String(c)),y.setAttribute("cy",String(d)),y.setAttribute("r","3.5"),y.setAttribute("class",`atfb-logicmap__dot is-${e.action}${e.broken?" is-broken":""}`),y.dataset.from=e.from,y.dataset.to=e.to,this.svg.append(u,y);const p=document.createElementNS(ne,"text");p.setAttribute("x",String(a.width-dt)),p.setAttribute("y",String(d+3)),p.setAttribute("text-anchor","end"),p.setAttribute("class","atfb-logicmap__label"),p.dataset.from=e.from,p.dataset.to=e.to,p.textContent=e.short.length>ht?`${e.short.slice(0,ht-1)}…`:e.short;const x=document.createElementNS(ne,"title");x.textContent=e.label,p.append(x),this.svg.append(p)}}const Ts={text:"text",email:"text",url:"text",tel:"text",number:"text",password:"text",date:"text",time:"text",datetime:"text",date_range:"composite",textarea:"textarea",select:"select",country:"select",multiselect:"options",radio:"options",checkboxes:"options",image_choice:"options",quiz:"options",switch:"toggle",consent:"toggle",file:"file",range:"range",rating:"summary",scale:"summary",likert:"summary",signature:"summary",repeater:"repeater",color:"text",name:"composite",address:"composite",total:"text",hidden:"static",heading:"static",html:"static",divider:"static",spacer:"static",page_break:"static"};function Ms(n){return Ts[n]??"text"}function ut(n,e){const s=/^choice:(\d+):(label|value)$/.exec(e);return String(s?n.choices?.[Number(s[1])]?.[s[2]]??"":n[e]??"")}function Z(n){const{value:e,onInput:s,onCommit:t}=n,a=r("span",{class:`${n.class} atfb-editable`,text:e,attrs:{contenteditable:"plaintext-only",role:"textbox",spellcheck:"false","data-placeholder":n.placeholder}});return n.bind&&(a.dataset.atfbBind=n.bind),a.addEventListener("pointerdown",i=>i.stopPropagation()),a.addEventListener("input",()=>s(a.textContent??"")),a.addEventListener("keydown",i=>{i.key==="Enter"&&(i.preventDefault(),a.blur()),i.key==="Escape"&&(a.textContent=e,a.blur()),i.stopPropagation()}),a.addEventListener("blur",()=>t?.()),a}function Fs(n){const e=r("input",{class:"atf-choice__input",type:n==="checkboxes"||n==="multiselect"?"checkbox":"radio"});return e.disabled=!0,e}function pt(n,e,s){const t=Ms(n.type),a=Z({value:n.label,placeholder:"Write the question…",class:"atf-label",bind:"label",onInput:o=>s.edit(l=>{l.label=o}),onCommit:()=>s.restructure(()=>{})}),i=[t==="static"||t==="toggle"?null:a,Ds(n,e,t,s),Is(n,e,s)];return r("div",{class:"atfb-preview atf-form",children:[r("div",{class:`atf-field atf-field--${n.type}`,children:i})]})}function Is(n,e,s){if(!e?.supports.includes("hint"))return null;const t=Z({value:n.hint??"",placeholder:"Add a hint…",class:"atf-hint",bind:"hint",onInput:a=>s.edit(i=>{i.hint=a})});return t.setAttribute("aria-label","Hint"),r("p",{class:"atfb-preview__hint",children:[t]})}function Ds(n,e,s,t){switch(s){case"text":return Oe(n,"atf-input",t);case"textarea":return Oe(n,"atf-input atf-textarea",t,!0);case"select":return r("div",{class:"atfb-preview__select",children:[Oe(n,"atf-input atf-select",t)]});case"options":return Rs(n,t);case"toggle":return r("div",{class:n.type==="switch"?"atf-toggle atf-toggle--switch":"atf-toggle",children:[(()=>{const a=r("input",{class:"atf-toggle__input",type:"checkbox"});return a.disabled=!0,a})(),Z({value:n.label||"",placeholder:n.type==="consent"?"What are they agreeing to?…":"What does this turn on?…",class:"atf-toggle__label",bind:"label",onInput:a=>t.edit(i=>{i.label=a}),onCommit:()=>t.restructure(()=>{})})]});case"file":return r("div",{class:"atf-file",children:[r("input",{class:"atf-file__input",type:"file"})]});case"range":return r("input",{class:"atf-range__input",type:"range"});case"composite":return r("div",{class:"atf-composite__parts",children:[r("span",{class:"atf-input atfb-preview__ghost",text:""}),r("span",{class:"atf-input atfb-preview__ghost",text:""})]});case"static":return Ps(n,e,t);case"repeater":return qs(n,t);default:return r("div",{class:"atfb-preview__stack",children:[r("p",{class:"atfb-preview__summary",text:`${e?.label??n.type} — set this up in the panel on the right.`})]})}}function qs(n,e){const s=n.fields??[],t=r("div",{class:"atfb-repeater__zone",attrs:{"data-atfb-repeater-zone":n.id}});return s.forEach(a=>t.append(Os(n,a,e))),s.length||t.append(r("p",{class:"atfb-repeater__empty",text:"Drag fields from the palette in here — the visitor gets a fresh copy of each with every row they add."})),r("div",{class:"atfb-repeater",attrs:{"data-atfb-repeater":n.id},children:[r("div",{class:"atf-repeater__row atfb-repeater__frame",children:[r("div",{class:"atf-repeater__row-head",children:[r("span",{class:"atf-repeater__title atfb-repeater__title",children:[Z({value:String(n.itemLabel??""),placeholder:"Row",class:"atfb-repeater__item-label",bind:"itemLabel",onInput:a=>e.edit(i=>{i.itemLabel=a})}),r("span",{class:"atfb-repeater__ordinal",text:"1",attrs:{"aria-hidden":"true"}})]})]}),t]}),Ns(n,e)]})}function Os(n,e,s){const t=e.id,a=l=>c=>{const d=(c.fields??[]).find(h=>h.id===t);d&&l(d)},i={edit:l=>s.edit(a(l)),restructure:l=>s.restructure(a(l)),types:s.types,selectedId:s.selectedId},o=s.types?.(e.type);return r("div",{class:`atfb-subcard${s.selectedId===t?" is-selected":""}`,attrs:{"data-atfb-subfield":t,"data-atfb-parent":n.id,tabindex:"0",role:"button","aria-label":`${e.label||o?.label||e.type}, inside ${n.label||"the repeater"}`},children:[r("div",{class:"atfb-subcard__head",children:[r("span",{class:"atfb-subcard__type",text:o?.label??e.type}),e.required?r("span",{class:"atfb-subcard__required",text:"Required"}):null,r("button",{class:"atfb-preview__remove",type:"button",title:"Remove this field from the repeater",attrs:{"aria-label":`Remove ${e.label||o?.label||"this field"}`},on:{pointerdown:l=>l.stopPropagation(),click:l=>{l.stopPropagation(),s.restructure(c=>{const d=c.fields??[],h=d.findIndex(m=>m.id===t);h>=0&&d.splice(h,1)})}},children:[r("span",{text:"×"})]})]}),pt(e,o,i)]})}function Oe(n,e,s,t=!1){const a=Z({value:n.placeholder??"",placeholder:n.type==="select"||n.type==="country"?"Choose…":"Placeholder…",class:`${e} atfb-preview__box${t?" atfb-preview__box--tall":""}`,bind:"placeholder",onInput:i=>s.edit(o=>{o.placeholder=i})});return a.setAttribute("aria-label","Placeholder"),a}function Ne(n,e,s,t,a,i){return r("span",{class:`atf-button ${s} atfb-preview__button`,children:[Z({value:n,placeholder:e,class:"atfb-preview__button-text",bind:t,onInput:o=>a.edit(l=>i(l,o))})]})}function Ns(n,e){return Ne(String(n.addLabel??""),"Add another","atf-button--ghost atfb-repeater__add","addLabel",e,(s,t)=>{s.addLabel=t})}function Rs(n,e){const s=r("div",{class:"atf-choices__list"});return(n.choices??[]).forEach((t,a)=>{s.append(r("div",{class:"atf-choice atfb-preview__option",children:[Fs(n.type),Z({value:t.label,placeholder:"Option…",class:"atf-choice__label",bind:`choice:${a}:label`,onInput:i=>{e.edit(o=>{const l=o.choices?.[a];if(!l)return;const c=!l.value||l.value===l.label;l.label=i,c&&(l.value=i)})}}),r("button",{class:"atfb-preview__remove",type:"button",title:"Remove this option",attrs:{"aria-label":`Remove ${t.label||"this option"}`},on:{pointerdown:i=>i.stopPropagation(),click:i=>{i.stopPropagation(),e.restructure(o=>{o.choices.splice(a,1)})}},children:[r("span",{text:"×"})]})]}))}),s.append(r("button",{class:"atfb-preview__add",type:"button",on:{pointerdown:t=>t.stopPropagation(),click:t=>{t.stopPropagation(),e.restructure(a=>{const i=a.choices.length+1;a.choices.push({label:`Option ${i}`,value:`Option ${i}`})})}},children:[r("span",{text:"+ Add option"})]})),r("fieldset",{class:"atf-choices",children:[s]})}function Ps(n,e,s){return n.type==="heading"?Z({value:n.label,placeholder:"Section heading…",class:"atf-heading",bind:"label",onInput:t=>s.edit(a=>{a.label=t}),onCommit:()=>s.restructure(()=>{})}):n.type==="divider"?r("hr",{class:"atf-divider"}):n.type==="page_break"?r("div",{class:"atfb-preview__stack",children:[r("p",{class:"atfb-progress-name",children:[Z({value:n.label,placeholder:"Name this step…",class:"atf-progress__label",bind:"label",onInput:t=>s.edit(a=>{a.label=t}),onCommit:()=>s.restructure(()=>{})})]}),r("p",{class:"atfb-preview__summary",text:"Everything after this is a new page."}),r("div",{class:"atf-nav atfb-preview__nav",children:[Ne(String(n.prevLabel??""),"Back","atf-button--secondary","prevLabel",s,(t,a)=>{t.prevLabel=a}),Ne(String(n.nextLabel??""),"Next","","nextLabel",s,(t,a)=>{t.nextLabel=a})]})]}):r("p",{class:"atfb-preview__summary",text:`${e?.label??n.type} — nothing is shown to the visitor here.`})}const Re=new Map;function ft(n){let e=Re.get(n);return e||(e=I.mergeTags(n).catch(()=>[]),Re.set(n,e)),e}function Bs(n){Re.delete(n)}function Hs(n){const e=new Map;for(const s of n)for(const t of s.items)e.set(t.tag,t);return e}function js(n,e){const s=Hs(e);return n.replace(/\{[a-z_]+(?::[^}]*)?\}/gi,t=>{const a=s.get(t.toLowerCase());return a?`{the value of ${a.label}}`:t})}function mt(n){return/\{[a-z_]+(?::[^}]*)?\}/i.test(n)}let te=null,Pe=0,re=null;function oe(n=!1){Pe++,te?.remove(),te=null,n&&re?.focus(),re=null}typeof document<"u"&&(document.addEventListener("pointerdown",n=>{const e=n.target;e?.closest(".atfb-tagpick__open")||te?.contains(e)||oe()}),window.addEventListener("keydown",n=>{if(n.key==="Escape"&&re){oe(!0),n.preventDefault(),n.stopImmediatePropagation();return}if(!te?.contains(n.target)||!["ArrowDown","ArrowUp","ArrowLeft","ArrowRight","Home","End","Enter"].includes(n.key))return;n.stopImmediatePropagation();const e=te.querySelector(".atfb-tagpick__search"),s=[...te.querySelectorAll(".atfb-tagpick__item")],t=s.indexOf(document.activeElement);if(n.key==="ArrowDown"||n.key==="ArrowUp"){n.preventDefault();const a=n.key==="ArrowDown"?t+1:t<0?s.length-1:t-1;s[(a+s.length)%s.length]?.focus()}else n.key==="Enter"&&n.target===e?(n.preventDefault(),s[0]?.click()):n.target!==e&&n.key!=="Enter"&&(n.preventDefault(),n.key==="Home"&&s[0]?.focus(),n.key==="End"&&s[s.length-1]?.focus())},!0));function bt(n,e){const s=n.selectionStart??n.value.length,t=n.selectionEnd??n.value.length;n.value=n.value.slice(0,s)+e+n.value.slice(t);const a=s+e.length;n.setSelectionRange(a,a),n.dispatchEvent(new Event("input",{bubbles:!0})),n.focus()}function Ws(n){let e=0,s=window.innerHeight,t=n.parentElement;for(;t&&t!==document.body;){if(/auto|scroll|hidden|clip/.test(getComputedStyle(t).overflowY)){const a=t.getBoundingClientRect();e=Math.max(e,a.top),s=Math.min(s,a.bottom)}t=t.parentElement}return{top:e,bottom:s}}function Us(n,e){const s=r("input",{class:"atfb-input atfb-tagpick__search",type:"search",placeholder:"Search values…",attrs:{"aria-label":"Search values"}}),t=r("div",{class:"atfb-tagpick__list"}),a=o=>{t.replaceChildren();const l=o.trim().toLowerCase();let c=0;for(const d of n){const h=d.items.filter(m=>!l||m.label.toLowerCase().includes(l)||m.tag.toLowerCase().includes(l));if(!h.length){d.empty&&!l&&!d.items.length&&t.append(r("p",{class:"atfb-tagpick__group",text:d.label}),r("p",{class:"atfb-tagpick__empty",text:d.empty}));continue}t.append(r("p",{class:"atfb-tagpick__group",text:d.label}));for(const m of h)c+=1,t.append(r("button",{class:"atfb-tagpick__item",type:"button",on:{click:()=>{oe(),e(m.tag)}},children:[r("span",{class:"atfb-tagpick__item-main",children:[r("span",{class:"atfb-tagpick__label",text:m.label}),r("code",{class:"atfb-tagpick__tag",text:m.tag})]}),m.hint||m.sample?r("span",{class:"atfb-tagpick__meta",text:`{the value of ${m.label}}`}):null]}))}!c&&l&&t.append(r("p",{class:"atfb-tagpick__empty",text:`Nothing matches “${o}”.`}))};a(""),s.addEventListener("input",()=>a(s.value));const i=r("div",{class:"atfb-tagpick",attrs:{role:"dialog","aria-label":"Insert a value"},children:[r("p",{class:"atfb-tagpick__intro",text:"Pick something to drop in. It is filled in when the form is submitted."}),s,t]});return i.addEventListener("keydown",o=>o.stopPropagation()),i}function Be(n,e){const s=()=>Promise.resolve(e.groups?e.groups():ft(e.formId??0)),t=r("button",{class:"atfb-button atfb-button--ghost atfb-tagpick__open",type:"button",title:"Insert a value from the submission",attrs:{"aria-haspopup":"dialog"},children:[B("shortcode"),r("span",{text:"Insert a value"})]}),a=r("div",{class:"atfb-taggable",children:[n,r("div",{class:"atfb-taggable__tools",children:[t]})]}),i=e.preview===!1?null:r("p",{class:"atfb-taggable__preview"});i&&a.append(i);const o=()=>{if(i){if(!mt(n.value)){i.textContent="",i.hidden=!0;return}s().then(c=>{mt(n.value)&&(i.hidden=!1,i.replaceChildren(r("span",{class:"atfb-taggable__preview-label",text:"Reads as"}),r("span",{text:js(n.value,c)})))})}};n.addEventListener("input",o),o();const l=(c,d)=>{oe();const h=Pe,m=n.value;re=n,s().then(g=>{if(h!==Pe||!a.isConnected||n.value!==m)return;const u=Us(g,S=>{n.setSelectionRange(c,d),bt(n,S)});a.append(u),te=u;const f=Ws(a);u.style.maxBlockSize=`${Math.max(0,Math.min(320,f.bottom-f.top-8))}px`;const y=a.getBoundingClientRect(),p=u.getBoundingClientRect().height,x=y.bottom+p<=f.bottom?y.bottom:y.top-p,F=Math.max(f.top+4,Math.min(x,f.bottom-p-4));u.style.insetBlockStart=`${F-y.top}px`,u.querySelector(".atfb-tagpick__search")?.focus({preventScroll:!0})})};return t.addEventListener("click",c=>{if(c.stopPropagation(),re===n){oe(!0);return}l(n.selectionStart??n.value.length,n.selectionEnd??n.value.length)}),n.addEventListener("input",c=>{const d=c,h=n.selectionStart??0;!d.isComposing&&d.data==="{"&&n.value[h-1]==="{"&&l(h-1,h+(n.value[h]==="}"?1:0))}),a}function gt(n,e){const s=parseFloat(String(n??""));return Number.isFinite(s)?s:e}function vt(n,e){let s=e[0];for(const t of e)Math.abs(t.at-n)n.accent??"#2271b1",apply:n=>({accent:n,"accent-soft":`color-mix( in srgb, ${n} 12%, transparent )`,"border-focus":n,"focus-ring-color":n,"button-bg":n,"button-bg-hover":`color-mix( in srgb, ${n} 88%, #000 )`})},{id:"roundness",label:"Roundness",hint:"Corners, everywhere at once.",kind:"scale",steps:He.map(({value:n,label:e})=>({value:n,label:e})),owns:["radius-field","radius-button","radius-card","radius-check"],read:n=>vt(gt(n["radius-field"],4),He),apply:n=>{const e=He.find(s=>s.value===n)?.at??4;return{"radius-field":`${e}px`,"radius-button":`${e}px`,"radius-card":`${Math.min(e*2,28)}px`,"radius-check":`${Math.min(e,6)}px`}}},{id:"density",label:"Density",hint:"How much air the form has.",kind:"scale",steps:je.map(({value:n,label:e})=>({value:n,label:e})),owns:["pad-field-x","pad-field-y","gap-fields","gap-label","button-pad-x","button-pad-y"],read:n=>vt(gt(n["pad-field-y"],9),je),apply:n=>{const e=je.find(s=>s.value===n)?.at??9;return{"pad-field-y":`${e}px`,"pad-field-x":`${Math.round(e*1.4)}px`,"gap-fields":`${Math.round(e*2.2)}px`,"gap-label":`${Math.max(4,Math.round(e*.7))}px`,"button-pad-y":`${e+2}px`,"button-pad-x":`${Math.round(e*2.2)}px`}}},{id:"shadow",label:"Depth",hint:"How far the form sits off the page.",kind:"scale",steps:zs,owns:["shadow-field","shadow-field-focus","shadow-button","shadow-button-hover","shadow-card"],read:n=>{const e=n["shadow-card"]??"none";return e==="none"||e.trim()===""?n["shadow-field"]&&n["shadow-field"]!=="none"?"hard":"none":e.includes("0 1px")||e.includes("2px")?"subtle":"lifted"},apply:n=>{switch(n){case"subtle":return{"shadow-field":"none","shadow-field-focus":"none","shadow-button":"0 1px 2px rgba( 0, 0, 0, 0.12 )","shadow-button-hover":"0 2px 4px rgba( 0, 0, 0, 0.16 )","shadow-card":"0 1px 3px rgba( 0, 0, 0, 0.1 )"};case"lifted":return{"shadow-field":"none","shadow-field-focus":"0 4px 12px rgba( 0, 0, 0, 0.12 )","shadow-button":"0 4px 10px rgba( 0, 0, 0, 0.16 )","shadow-button-hover":"0 8px 20px rgba( 0, 0, 0, 0.2 )","shadow-card":"0 10px 30px rgba( 0, 0, 0, 0.14 )"};case"hard":return{"shadow-field":"3px 3px 0 currentColor","shadow-field-focus":"5px 5px 0 currentColor","shadow-button":"4px 4px 0 currentColor","shadow-button-hover":"2px 2px 0 currentColor","shadow-card":"none"};default:return{"shadow-field":"none","shadow-field-focus":"none","shadow-button":"none","shadow-button-hover":"none","shadow-card":"none"}}}},{id:"field-style",label:"Fields",hint:"How an input is drawn.",kind:"choice",steps:Ys,owns:["field-style"],read:n=>n["field-style"]??"outline",apply:n=>({"field-style":n})},{id:"labels",label:"Labels",hint:"Where the question sits relative to the answer.",kind:"choice",steps:Vs,owns:["label-position"],read:n=>n["label-position"]??"top",apply:n=>({"label-position":n})}]}function Ks(n){return We().find(e=>e.owns.includes(n))??null}function yt(n){let e=n.activeSlug,s={...n.overrides},t=n.themes;const a=v=>{s=v,n.onOverridesReplaced?.({...v})},i=r("div",{class:"atfs-preview__frame"}),o=r("div",{class:"atfs-controls__body"}),l=r("div",{class:"atfs-quick"}),c=r("div",{class:"atfs-themes"}),d=Me(async()=>{try{const v=await n.previewFor(e,s),_=i.closest(".atf-studio__preview"),k=_?.scrollTop??0;i.innerHTML=v,_&&(_.scrollTop=k),document.dispatchEvent(new CustomEvent("alltfo-refresh"))}catch(v){R(i),i.append(r("p",{class:"atfb-error",text:v instanceof Error?v.message:"Preview failed."}))}},180),h={"label-position":"atf-labels-","field-style":"atf-fields-"},m=(v,_,k)=>{const E=k.replace(/[^a-z0-9_-]/gi,"");for(const A of[...v.classList])A.startsWith(_)&&v.classList.remove(A);E&&v.classList.add(_+E)},g=v=>{const _=i.querySelector(".atf-form-wrap"),k=_?.querySelector(".atf-form");if(_)for(const[E,A]of Object.entries(v))A===""?_.style.removeProperty(`--atf-${E}`):_.style.setProperty(`--atf-${E}`,A),k&&h[E]&&m(k,h[E],A||(t.find(P=>P.slug===e)?.resolved[E]??""))},u=v=>{g(v.resolved);const _=i.querySelector(".atf-form");_&&(m(_,"atf-theme-",v.slug),_.classList.toggle("atf-is-dark",!!v.dark))},f=v=>s[v.token]!==void 0?s[v.token]:t.find(k=>k.slug===e)?.resolved?.[v.token]??v.default,y=()=>{R(c);for(const v of t){const _=r("button",{class:`atfs-theme${v.slug===e?" is-active":""}`,type:"button",attrs:{"aria-pressed":v.slug===e,title:v.description},children:[r("span",{class:"atfs-theme__chip",style:{background:v.resolved.surface??"#fff",borderColor:v.resolved.border??"#ccc",borderRadius:v.resolved["radius-field"]??"4px",boxShadow:v.resolved["shadow-card"]??"none"},children:[r("span",{class:"atfs-theme__accent",style:{background:v.resolved.accent??"#2271b1",borderRadius:v.resolved["radius-button"]??"4px"}})]}),r("span",{class:"atfs-theme__name",text:v.label}),v.custom?r("span",{class:"atfb-badge",text:"yours"}):null],on:{click:()=>{e=v.slug,a({}),n.onTheme(e),u(v),y(),S(),F(),ts()}}});c.append(_)}},p=()=>{const _={...t.find(k=>k.slug===e)?.resolved??{}};for(const[k,E]of Object.entries(s))_[k]=E;return _},x=v=>{const _=l.closest(".atf-studio__controls"),k=_?.scrollTop??0;v(),_&&(_.scrollTop=k)},F=Me(()=>x(b),150),S=()=>{const v=p(),_=l.querySelectorAll(".atfs-dial");We().forEach((k,E)=>{const A=_[E];if(!A)return;const P=k.read(v);if(k.kind==="colour"){const O=A.querySelector('input[type="color"]'),K=A.querySelector("input.atfb-input");O&&document.activeElement!==O&&(O.value=ve(P)),K&&document.activeElement!==K&&(K.value=P);return}A.querySelectorAll(".atfs-segment").forEach((O,K)=>{const Q=k.steps?.[K]?.value===P;O.classList.toggle("is-on",Q),O.setAttribute("aria-pressed",String(Q))})})},j=(v,_)=>{const k=v.apply(_,p());for(const[E,A]of Object.entries(k))s[E]=A,n.onOverride(E,A);g(k),S(),F()},L=()=>{R(l);const v=p();for(const _ of We()){const k=_.read(v);let E;if(_.kind==="colour"){const A=r("input",{class:"atfs-color",type:"color",value:ve(k),attrs:{"aria-label":_.label},on:{input:P=>j(_,P.target.value)}});E=r("div",{class:"atfs-color-row",children:[A,T(k,P=>j(_,P))]})}else E=r("div",{class:`atfs-segmented atfs-segmented--${_.kind}`,attrs:{role:"group","aria-label":_.label},children:(_.steps??[]).map(A=>r("button",{class:`atfs-segment${A.value===k?" is-on":""}`,type:"button",text:A.label,attrs:{"aria-pressed":A.value===k},on:{click:()=>j(_,A.value)}}))});l.append(r("div",{class:"atfs-dial",children:[r("div",{class:"atfs-dial__head",children:[r("span",{class:"atfs-dial__label",text:_.label}),r("span",{class:"atfs-dial__hint",text:_.hint})]}),E]}))}},b=()=>{R(o);const v=new Map;for(const k of n.tokens){const E=v.get(k.group)??[];E.push(k),v.set(k.group,E)}const _={colour:"Colour",shape:"Corners and borders",shadow:"Shadows",fields:"Field style",space:"Spacing",type:"Type",labels:"Labels",button:"Buttons",focus:"Focus ring",motion:"Motion"};for(const[k,E]of v)o.append(r("details",{class:"atfs-group",attrs:{open:k==="colour"},children:[r("summary",{text:_[k]??k}),...E.map(A=>M(A))]}))},M=v=>{const _=f(v),k=O=>{O===""?delete s[v.token]:s[v.token]=O,n.onOverride(v.token,O),g({[v.token]:O})};let E;if(v.control==="color"){const O=r("input",{class:"atfs-color",type:"color",value:ve(_),attrs:{"aria-label":`${v.label} colour`},on:{input:Q=>{const de=Q.target.value;K.value=de,k(de)}}}),K=T(_,Q=>{O.value=ve(Q),k(Q)});E=r("div",{class:"atfs-color-row",children:[O,K]})}else if(v.control==="select")E=D(_,(v.options??[]).map(O=>({value:O,label:O})),k);else if(v.control==="length"){const O=parseFloat(_)||0,K=v.unit??"px",Q=v.token.includes("radius")?60:80,de=r("input",{class:"atfs-range",type:"range",value:String(O),attrs:{min:"0",max:String(Q),step:"1","aria-label":v.label},on:{input:Ee=>{const ns=`${Ee.target.value}${K}`;ss.value=ns,k(ns)}}}),ss=T(_,Ee=>{de.value=String(parseFloat(Ee)||0),k(Ee)});E=r("div",{class:"atfs-length-row",children:[de,ss]})}else E=T(_,k);const A=w(v.label,E),P=Ks(v.token);return P&&(A.classList.add("is-dialled"),A.append(r("span",{class:"atfs-owned",text:P.label}))),s[v.token]!==void 0&&(A.classList.add("is-overridden"),A.append(r("button",{class:"atfs-reset",type:"button",text:"Reset",attrs:{"aria-label":`Reset ${v.label} to the theme's value`},on:{click:()=>{k(""),x(b)}}}))),A},$=async()=>{const v=t.find(E=>E.slug===e),_=v?`${v.label} (mine)`:"My theme",k=window.prompt("Name this theme",_);if(k)try{const E={};for(const P of n.tokens){const O=f(P);O!==P.default&&(E[P.token]=O)}const A=await I.saveTheme({label:k,tokens:E});t=[...t.filter(P=>P.slug!==A.slug),A],n.onThemesChanged(t),e=A.slug,a({}),n.onTheme(e),y(),S(),x(b),N("Theme saved",A.label)}catch(E){N("Could not save the theme",E instanceof Error?E.message:"","error")}},W=async()=>{const v=t.find(_=>_.slug===e);if(v?.custom&&await ie(`Delete “${v.label}”? Forms using it fall back to Clean.`,"Delete theme"))try{await I.deleteTheme(v.id),t=t.filter(k=>k.slug!==v.slug),n.onThemesChanged(t),e="clean",a({}),n.onTheme(e),y(),S(),x(b);const _=t.find(k=>k.slug===e);_&&u(_)}catch(_){N("Could not delete the theme",_ instanceof Error?_.message:"","error")}},H=async()=>{const v=t.find(E=>E.slug===e),_={label:v?.label??e,tokens:{...v?.tokens??{},...s}},k=JSON.stringify(_,null," ");try{await navigator.clipboard.writeText(k),N("Theme copied","Paste it into another site to import it.")}catch{window.prompt("Copy this theme",k)}},ce=()=>{const v=window.prompt("Paste a theme");if(v)try{const _=JSON.parse(v);if(!_.tokens||typeof _.tokens!="object")throw new Error("That does not look like a theme.");for(const[k,E]of Object.entries(_.tokens))s[k]=String(E),n.onOverride(k,String(E));g({...s}),S(),x(b)}catch(_){N("Could not read that theme",_ instanceof Error?_.message:"","error")}},es=C("Delete",()=>void W(),"danger"),ts=()=>{const v=t.find(_=>_.slug===e);es.hidden=!v?.custom};return y(),L(),b(),d(),ts(),r("div",{class:"atf-studio",children:[r("div",{class:"atf-studio__top",children:[r("div",{class:"atf-studio__topbar",children:[r("h2",{class:"atf-studio__heading",text:"Theme"}),r("div",{class:"atfs__actions",children:[C("Save as a theme",()=>void $(),"primary"),C("Export",()=>void H()),C("Import",ce),es]})]}),c]}),r("div",{class:"atf-studio__panes",children:[r("aside",{class:"atf-studio__controls",children:[r("p",{class:"atfb-hint",text:"Changes apply to this form only, until you save them as a theme."}),l,r("details",{class:"atfs-advanced",children:[r("summary",{text:"Every setting"}),r("p",{class:"atfb-hint",text:"The full token list. The dials above write into it."}),o]})]}),r("main",{class:"atf-studio__preview",children:[r("h2",{class:"screen-reader-text",text:"Live preview"}),i]})]})]})}async function Ue(){const n=document.querySelector("[data-atfs-root]");if(!n||n.dataset.atfsMounted)return;n.dataset.atfsMounted="1",Fe(n);const e=n.querySelector("[data-atfs-bar]"),s=n.querySelector(".atfs__body")??n;try{const[t,a,i]=await Promise.all([I.config(),I.listThemes(),I.listForms()]);if(!i.length){R(s),s.append(r("div",{class:"atfb-empty",children:[r("h2",{text:"No forms to preview"}),r("p",{text:"Make a form first — a theme needs something to dress."})]}));return}let o=i[0],l={},c=o.theme;const d=()=>{R(s),s.append(yt({themes:a,tokens:t.tokens,activeSlug:c,overrides:l,standalone:!0,onTheme:h=>{c=h},onOverride:(h,m)=>{m===""?delete l[h]:l[h]=m},onOverridesReplaced:h=>{l={...h}},previewFor:async(h,m)=>{const g=await I.getForm(o.id);g.schema.settings.theme=h,g.schema.settings.themeOverrides=m;const{html:u}=await I.preview(o.id,{schema:g.schema,theme:h});return u},onThemesChanged:h=>{a.length=0,a.push(...h)}}))};e&&(R(e),e.append(r("span",{class:"atfs__label",text:"Preview against"}),D(String(o.id),i.map(h=>({value:String(h.id),label:h.title||"(untitled)"})),h=>{o=i.find(m=>String(m.id)===h)??i[0],l={},d()}))),d()}catch(t){R(s),s.append(r("p",{class:"atfb-error",text:t instanceof Error?t.message:"Could not load themes."}))}}typeof document<"u"&&(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>void Ue()):Ue(),document.addEventListener("os-window-content-loaded",()=>void Ue()));function ve(n){const e=n.trim();return/^#[0-9a-f]{6}$/i.test(e)?e:/^#[0-9a-f]{3}$/i.test(e)?`#${e[1]}${e[1]}${e[2]}${e[2]}${e[3]}${e[3]}`:"#888888"}const Gs="mailpoet",ye="",Xs=["checkbox","checkboxes","toggle","consent","gdpr"],wt=["email"];function _t(n){return n.schema.actions.find(e=>e.type==="mailpoet")}function ze(n){const e=n?.settings??{};return{lists:Array.isArray(e.lists)?e.lists.map(Number).filter(Boolean):[],email_field:typeof e.email_field=="string"?e.email_field:"",first_name_field:typeof e.first_name_field=="string"?e.first_name_field:"",last_name_field:typeof e.last_name_field=="string"?e.last_name_field:""}}function Zs(n){return n?.logic?.enabled?n.logic.rules[0]?.field??ye:ye}function xt(n,e,s){const t=n.find(a=>e.includes(a.type));return t?t.id:s?n.find(a=>s.test(a.label))?.id??"":""}function Js(n,e,s){const t=r("section",{class:"atfm-form"}),a=l=>r("span",{class:`atfm-form__mark${l?"":" atfm-form__mark--off"}`,children:[r("img",{attrs:{src:e.symbol,alt:"",width:"20",height:"20"}})]}),i=l=>{R(t),t.classList.remove("is-open"),t.append(r("div",{class:"atfm-form__head",children:[a(l.length>0),r("div",{class:"atfm-form__title",children:[r("strong",{text:n.title||`Form ${n.id}`}),l.length?r("span",{class:"atfm-form__status is-on",text:`Subscribing → ${l.join(", ")}`}):r("span",{class:"atfm-form__status",text:"Not connected"})]}),C(l.length?"Edit":"Connect",()=>void o(),"secondary")]}))},o=async()=>{t.classList.add("is-open"),R(t),t.append(r("p",{class:"atfm-hint",text:"Loading the form…"}));let l;try{l=await I.getForm(n.id)}catch{R(t),t.append(r("p",{class:"atfm-hint",text:"Could not load this form."}));return}const c=l.schema.fields.filter(b=>b.type!=="page_break"),d=_t(l),h=ze(d),m=new Set(h.lists.filter(b=>e.lists.some(M=>M.id===b)));let g=Zs(d),u=h.email_field||xt(c,wt,/mail/i),f=h.first_name_field,y=h.last_name_field;c.some(b=>b.id===u)||(u=xt(c,wt,/mail/i));const p=c.filter(b=>Xs.includes(b.type));!d&&p.length&&(g=p[0].id);const x=(b,M)=>[{value:"",label:M},...b.map($=>({value:$.id,label:$.label||$.id}))],F=r("div",{class:"atfm-lists"});(()=>{R(F);for(const b of e.lists){const M=r("input",{attrs:{type:"checkbox"}});M.checked=m.has(b.id),M.addEventListener("change",()=>{M.checked?m.add(b.id):m.delete(b.id)}),F.append(r("label",{class:"atfm-list",children:[M,r("span",{text:b.name})]}))}})();const j=async(b=!1)=>{const M=l.schema.actions.filter($=>$.type!=="mailpoet");if(!b){if(!m.size){N("Pick a list","A subscription needs at least one MailPoet list.","error");return}if(!u){N("Pick the email field","MailPoet needs to know which answer is the address.","error");return}M.push({id:Gs,type:"mailpoet",enabled:!0,logic:g===ye?{enabled:!1,action:"show",match:"all",rules:[]}:{enabled:!0,action:"show",match:"all",rules:[{field:g,operator:"not_empty",value:""}]},settings:{lists:[...m],email_field:u,first_name_field:f,last_name_field:y}})}l.schema.actions=M;try{await I.updateForm(l.id,{schema:l.schema})}catch($){N("Could not save",$ instanceof Error?$.message:"","error");return}N(b?"Disconnected":"Connected to MailPoet",b?`${n.title} no longer subscribes anyone.`:`${n.title} now subscribes opted-in visitors.`),i(b?[]:e.lists.filter($=>m.has($.id)).map($=>$.name))};R(t);const L=b=>r("span",{class:"atfm-form__section",text:b});t.append(r("div",{class:"atfm-form__head",children:[a(!!d),r("div",{class:"atfm-form__title",children:[r("strong",{text:n.title||`Form ${n.id}`})]}),C("Close",()=>i(d?e.lists.filter(b=>ze(d).lists.includes(b.id)).map(b=>b.name):[]),"ghost")]}),r("div",{class:"atfm-form__body",children:[L("Where they land"),w("Lists",F,"MailPoet sends its own confirmation email before anyone is truly on a list."),L("Who they are"),w("Email address",D(u,x(c,"— pick a field —"),b=>{u=b}),"The answer that carries their address."),w("First name",D(f,x(c,"— none —"),b=>{f=b})),w("Last name",D(y,x(c,"— none —"),b=>{y=b})),L("When to subscribe"),w("Subscribe",D(g,[...p.map(b=>({value:b.id,label:`Only when “${b.label||b.id}” is ticked`})),{value:ye,label:"Everyone who submits"}],b=>{g=b}),p.length?"Bind it to a box they tick. Subscribing everyone is rarely what visitors expect.":"Add a checkbox or toggle field to the form to offer a proper opt-in."),r("div",{class:"atfm-form__actions",children:d?[C("Save connection",()=>void j(),"primary"),C("Disconnect",()=>void j(!0),"danger")]:[C("Save connection",()=>void j(),"primary")]})]}))};return i([]),I.getForm(n.id).then(l=>{const c=_t(l);if(c&&!t.classList.contains("is-open")){const d=e.lists.filter(h=>ze(c).lists.includes(h.id)).map(h=>h.name);i(d)}}).catch(()=>{}),s.append(t),t}function Qs(n){const e=n.logo?r("span",{class:"atfm-hero__logo",children:[r("img",{attrs:{src:n.logo,alt:"MailPoet",width:"210",height:"105"}})]}):r("span",{class:"atfm-hero__logo atfm-hero__logo--glyph",text:"📬"});return r("header",{class:"atfm-hero",children:[e,r("div",{class:"atfm-hero__words",children:[r("h1",{text:"Turn submissions into subscribers"}),r("p",{text:"Every submission is someone choosing to talk to you. Connect a form to MailPoet and the ones who opt in land on your lists by themselves — named, consented, and confirmed by MailPoet’s own double opt-in."}),n.active?r("p",{class:"atfm-hero__status is-on",text:n.lists.length===1?"Connected — 1 list ready for subscribers":`Connected — ${n.lists.length} lists ready for subscribers`}):r("p",{class:"atfm-hero__status",text:"MailPoet is not installed on this site yet"})]})]})}function en(){const n=(s,t,a)=>r("div",{class:"atfm-why__card",children:[r("span",{class:"atfm-why__icon",text:s}),r("strong",{text:t}),r("p",{text:a})]}),e=(s,t)=>r("a",{text:t,attrs:{href:s,target:"_blank",rel:"noreferrer"}});return[r("div",{class:"atfm-why",children:[n("✉️","Beautiful emails, made in WordPress","Design newsletters and welcome emails in a drag-and-drop editor that lives in your own admin — no external account to juggle."),n("🤝","Consent you can stand behind","Double opt-in out of the box: every address your forms send over is confirmed by the visitor before a single campaign reaches it."),n("🚀","Free to grow with","Free up to 500 subscribers, welcome automations, WooCommerce emails and open-rate stats included — a paid addon anywhere else.")]}),r("p",{class:"atfm-links",children:[e("https://www.mailpoet.com/features/","Explore MailPoet’s features ↗"),e("https://kb.mailpoet.com/","Guides & docs ↗"),e("https://www.mailpoet.com/pricing/","Plans & the free tier ↗")]})]}function tn(){const n=(e,s)=>r("div",{class:"atfm-step",children:[r("span",{class:"atfm-step__n",text:e}),r("span",{text:s})]});return r("div",{class:"atfm-steps",children:[n("1","Pick a form below"),n("2","Choose the lists it feeds"),n("3","Bind it to an opt-in — MailPoet confirms the rest")]})}async function Ye(){const n=document.querySelector("[data-atfm-root]");if(!n||n.dataset.atfmMounted)return;n.dataset.atfmMounted="1",Fe(n);const e=n.querySelector("[data-atfm-bar]"),s=n.querySelector("[data-atfm-body]")??n;try{const[t,a]=await Promise.all([I.mailpoet(),I.listForms()]);if(e?.remove(),R(s),s.append(Qs(t),...en()),!t.active){s.append(r("div",{class:"atfm-pitch",children:[r("p",{text:"MailPoet sends newsletters from right inside WordPress — no external account, free to start. Install it and every form here gets a subscribe switch: reservations feed your announcements, signups feed your schedule, enquiries feed your news."}),r("a",{class:"atfm-pitch__cta",text:"Install MailPoet — it’s free",attrs:{href:t.adminUrl,target:"_blank",rel:"noreferrer"}})]}));return}if(!t.lists.length){s.append(r("div",{class:"atfm-pitch",children:[r("p",{text:"MailPoet is here, but it has no list yet. Make one, and your forms will have somewhere to send subscribers."}),r("a",{class:"atfm-pitch__cta",text:"Open MailPoet lists",attrs:{href:t.adminUrl,target:"_blank",rel:"noreferrer"}})]}));return}s.append(tn());const i=r("section",{class:"atfm-forms",children:[r("div",{class:"atfm-forms__head",children:[r("h2",{text:"Your forms"}),r("p",{class:"atfm-hint",text:"Connected forms wear the MailPoet mark in colour."})]})]});if(s.append(i),!a.length){i.append(r("p",{class:"atfm-hint",text:"No forms yet — make one, then come back to connect it."}));return}for(const o of a)Js(o,t,i)}catch{e&&(e.textContent="Could not reach the site — reload the window to try again.")}}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>void Ye()):Ye(),document.addEventListener("os-window-content-loaded",()=>void Ye());const we={plain:"",simple:"✓",minimal:"",card:"🎉",check:"",confetti:"🎉",fireworks:"🎆",sparkles:"✨",typewriter:""};function kt(){return{style:"simple",title:"",icon:"",accent:"",intensity:"medium",showButton:!1,buttonLabel:""}}function Ve(n){const e={...kt(),...n??{}};return e.style in we||(e.style="simple"),e}function sn(){return typeof window.matchMedia=="function"&&window.matchMedia("(prefers-reduced-motion: reduce)").matches}function nn(n,e,s){const t=Ve(e),a=document.createElement("div");if(a.className=`atf-confirmation atf-success atf-success--${t.style}`,a.setAttribute("role","status"),a.setAttribute("tabindex","-1"),t.style==="plain")return a.className="atf-confirmation",a.innerHTML=n,a;t.accent&&a.style.setProperty("--atf-accent",t.accent);const i=document.createElement("div");i.className="atf-success__inner",a.append(i);const o=t.icon||we[t.style];if(t.style==="check")i.insertAdjacentHTML("beforeend",'');else if(o){const c=document.createElement("span");c.className="atf-success__icon",c.setAttribute("aria-hidden","true"),c.textContent=o,i.append(c)}if(t.title){const c=document.createElement("h2");c.className="atf-success__title",c.textContent=t.title,i.append(c)}const l=document.createElement("div");if(l.className="atf-success__message",l.innerHTML=n,i.append(l),t.style==="typewriter"&&a.setAttribute("aria-label",l.textContent??""),t.showButton){const c=document.createElement("button");c.type="button",c.className="atf-button atf-button--ghost atf-success__again",c.textContent=t.buttonLabel||"Fill it in again",c.addEventListener("click",()=>s?s():window.location.reload()),i.append(c)}return a}function an(n,e){const s=Ve(e);if(sn())return()=>{};switch(s.style){case"confetti":return rn(n,s);case"fireworks":return on(s);case"sparkles":return ln(n,s);case"typewriter":return cn(n);default:return()=>{}}}function le(n){return{low:.5,medium:1,high:1.8}[n.intensity]}function St(){const n=document.createElement("canvas");n.className="atf-success-canvas",n.setAttribute("aria-hidden","true");const e=Math.min(window.devicePixelRatio||1,2);n.width=Math.floor(window.innerWidth*e),n.height=Math.floor(window.innerHeight*e),document.body.append(n);const s=n.getContext("2d");return s?.scale(e,e),{canvas:n,ctx:s,stop:()=>n.remove()}}const Ct=["#f43f5e","#f59e0b","#10b981","#3b82f6","#8b5cf6","#ec4899","#eab308"];function rn(n,e){const{ctx:s,stop:t}=St();if(!s)return t(),()=>{};const a=e.accent?[e.accent,...Ct]:Ct,i=[],o=n.getBoundingClientRect(),l=o.left+o.width/2,c=Math.min(o.top+40,window.innerHeight-20),d=(x,F,S)=>{const j=S?Math.PI*(1.15+.7*Math.random()):0,L=S?9+Math.random()*8:0;return{x,y:F,vx:S?Math.cos(j)*L*(Math.random()<.5?1:-1):(Math.random()-.5)*1.5,vy:S?Math.sin(j)*L:1+Math.random()*2,w:6+Math.random()*5,h:8+Math.random()*7,angle:Math.random()*Math.PI,spin:(Math.random()-.5)*.3,color:a[Math.floor(Math.random()*a.length)],wobble:Math.random()*Math.PI*2}},h=Math.round(90*le(e));for(let x=0;x{if(g>=m){window.clearInterval(u);return}i.push(d(Math.random()*window.innerWidth,-20,!1)),g++},2e3/m);let f=0;const y=performance.now(),p=x=>{s.clearRect(0,0,window.innerWidth,window.innerHeight);let F=!1;for(const S of i)S.vy+=.16,S.vx*=.99,S.vy*=.985,S.wobble+=.1,S.x+=S.vx+Math.sin(S.wobble)*.8,S.y+=S.vy,S.angle+=S.spin,S.y{window.clearInterval(u),window.cancelAnimationFrame(f),t()}}function on(n){const{ctx:e,stop:s}=St();if(!e)return s(),()=>{};const t=[],a=[],i=Math.round(6*le(n))+2;let o=0;const l=()=>{t.push({x:window.innerWidth*(.15+.7*Math.random()),y:window.innerHeight,vy:-(9+Math.random()*4),targetY:window.innerHeight*(.15+.3*Math.random()),hue:Math.floor(Math.random()*360)}),o++};l();const c=window.setInterval(()=>{if(o>=i){window.clearInterval(c);return}l()},3500/i),d=u=>{const f=Math.round(70*le(n));for(let y=0;y{e.clearRect(0,0,window.innerWidth,window.innerHeight);for(let y=t.length-1;y>=0;y--){const p=t[y];p.y+=p.vy,p.vy+=.08,e.fillStyle=`hsl(${p.hue} 90% 65%)`,e.fillRect(p.x-1.5,p.y,3,10),(p.y<=p.targetY||p.vy>=-1)&&(d(p),t.splice(y,1))}for(let y=a.length-1;y>=0;y--){const p=a[y];if(p.x+=p.vx,p.y+=p.vy,p.vy+=.045,p.vx*=.985,p.vy*=.985,p.life-=p.decay,p.life<=0){a.splice(y,1);continue}const x=p.life*(.7+.3*Math.random());e.globalAlpha=Math.max(0,x),e.fillStyle=`hsl(${p.hue} 95% ${55+25*p.life}%)`,e.beginPath(),e.arc(p.x,p.y,1.1+1.6*p.life,0,Math.PI*2),e.fill()}e.globalAlpha=1,!(o>=i&&t.length===0&&a.length===0)&&u-m<9e3?h=window.requestAnimationFrame(g):s()};return h=window.requestAnimationFrame(g),()=>{window.clearInterval(c),window.cancelAnimationFrame(h),s()}}function ln(n,e){const s=e.icon||we.sparkles,t=Math.round(22*le(e)),a=[];let i=0;const o=()=>{const c=document.createElement("span");c.className="atf-success__spark",c.setAttribute("aria-hidden","true"),c.textContent=s,c.style.insetInlineStart=`${4+Math.random()*92}%`,c.style.animationDuration=`${2.6+Math.random()*1.8}s`,c.style.animationDelay=`${Math.random()*.3}s`,c.style.fontSize=`${14+Math.random()*14}px`,c.addEventListener("animationend",()=>c.remove()),n.append(c),a.push(c),i++};o();const l=window.setInterval(()=>{if(i>=t){window.clearInterval(l);return}o()},2800/t);return()=>{window.clearInterval(l),a.forEach(c=>c.remove())}}function cn(n){const e=n.querySelector(".atf-success__message");if(!e)return()=>{};const s=e.innerHTML,t=e.textContent??"";if(!t)return()=>{};e.textContent="",e.classList.add("is-typing");const a=Math.min(45,3800/t.length);let i=0;const o=window.setInterval(()=>{i++,e.textContent=t.slice(0,i),i>=t.length&&(window.clearInterval(o),e.classList.remove("is-typing"),e.innerHTML=s)},a);return()=>{window.clearInterval(o),e.classList.remove("is-typing"),e.innerHTML=s}}const dn={min:-1,max:-1,sum:-1,avg:-1,round:-1,ceil:1,floor:1,abs:1,sqrt:1,pow:2},Et={"+":{precedence:1,right:!1},"-":{precedence:1,right:!1},"*":{precedence:2,right:!1},"/":{precedence:2,right:!1},"%":{precedence:2,right:!1},"^":{precedence:4,right:!0}};function hn(n,e,s=[]){if(!n||!n.trim()||n.length>2e3)return null;const t=un(n,e,s),a=bn(t);if(!a)return null;const i=vn(a);if(!i)return null;const o=yn(i);return o===null||!Number.isFinite(o)?null:o}function un(n,e,s){return n.replace(/\{([a-zA-Z0-9_]+)(?:\.([a-zA-Z0-9_]+))?\}/g,(t,a,i,o)=>pn(n,o,t.length,a,i??"",e,s))}function pn(n,e,s,t,a,i,o){const l=fn(o,t),c=Object.prototype.hasOwnProperty.call(i,t)?i[t]:null;if(a===""){const u=l?.type==="repeater"?Lt(c).length:Ke(c,l);return $t(u)}const h=(l?.fields??[]).find(u=>u.id===a)??null,m=Lt(c).map(u=>Ke(u[a]??"",h));if(!m.length)return"0";const g=m.map($t);return mn(n,e,s)?g.join(", "):g.length===1?g[0]:`( ${g.join(" + ")} )`}function fn(n,e){for(const s of n){if(s.id===e)return s;for(const t of s.fields??[])if(t.id===e)return t}return null}function mn(n,e,s){const t=n.slice(e+s).replace(/^\s+/,"");if(t===""||t[0]!==")")return!1;const a=n.slice(0,e).replace(/\s+$/,""),i=/([a-zA-Z_][a-zA-Z0-9_]*)\s*\($/.exec(a);return!!i&&["sum","avg","min","max"].includes(i[1].toLowerCase())}function Lt(n){if(!Array.isArray(n))return[];const e=[];for(const s of n){if(!s||typeof s!="object"||Array.isArray(s))continue;Object.values(s).some(a=>a!==""&&a!==null&&a!==void 0&&a!==!1&&!(Array.isArray(a)&&!a.length))&&e.push(s)}return e}function $t(n){const e=n.toFixed(10).replace(/0+$/,"").replace(/\.$/,"");return e===""||e==="-"?"0":e}function Ke(n,e){if(typeof n=="boolean")return n?1:0;if(Array.isArray(n))return n.reduce((a,i)=>a+Ke(i,e),0);if(n==null||n==="")return 0;const s=e?.choices??[];for(const a of s)if(String(a.value)===String(n)){if(typeof a.price=="number")return a.price;if(typeof a.points=="number")return a.points;break}const t=Number(n);return Number.isFinite(t)?t:0}function bn(n){const e=[];let s=0;for(;si.precedence||l.precedence===i.precedence&&!i.right){e.push(s.pop());continue}break}s.push(a);break}case"(":s.push(a);break;case")":{for(;s.length&&s[s.length-1].type!=="(";)e.push(s.pop());if(!s.length)return null;if(s.pop(),s.length&&s[s.length-1].type==="function"){const i=s.pop();i.arity=t.length?t.pop():1,e.push(i)}break}}for(;s.length;){const a=s.pop();if(a.type==="(")return null;e.push(a)}return e}function yn(n){const e=[];for(const s of n)switch(s.type){case"number":e.push(s.value);break;case"unary":{if(!e.length)return null;e.push(-e.pop());break}case"operator":{if(e.length<2)return null;const t=e.pop(),a=e.pop();switch(s.value){case"+":e.push(a+t);break;case"-":e.push(a-t);break;case"*":e.push(a*t);break;case"/":e.push(t===0?0:a/t);break;case"%":e.push(t===0?0:a%t);break;case"^":e.push(Math.pow(a,t));break;default:return null}break}case"function":{const t=s.arity??1;if(e.lengths+t,0);case"avg":return e.reduce((s,t)=>s+t,0)/e.length;case"round":{const s=Math.max(-10,Math.min(10,Math.trunc(e[1]??0))),t=Math.pow(10,s);return Math.round(e[0]*t)/t}case"ceil":return Math.ceil(e[0]);case"floor":return Math.floor(e[0]);case"abs":return Math.abs(e[0]);case"sqrt":return e[0]<0?0:Math.sqrt(e[0]);case"pow":return Math.pow(e[0],e[1]??2);default:return null}}const _n=["sum","min","max","avg","round","ceil","floor","abs","sqrt","pow"],At=["number","range","scale","rating","total","select","multiselect","radio","checkboxes","switch","quiz"];function _e(n,e){return n.filter(s=>s.id!==e&&At.includes(s.type))}function Tt(n){const e=[];for(const s of n){if(s.type!=="repeater")continue;const t=s.label||s.id;e.push({label:`${t} (how many)`,insert:`{${s.id}}`});for(const a of s.fields??[])At.includes(a.type)&&e.push({label:`${t} · ${a.label||a.id}`,insert:`{${s.id}.${a.id}}`})}return e}function xn(n,e){const s={};_e(n,e).forEach((t,a)=>{s[t.id]=a+1});for(const t of n){if(t.type!=="repeater"||t.id===e)continue;const a=t.fields??[],i=o=>Object.fromEntries(a.map((l,c)=>[l.id,c+1+o]));s[t.id]=[i(0),i(1)]}return s}function Mt(n,e,s){return Be(n,{preview:!1,groups:()=>[{id:"references",label:"Your questions",items:[..._e(e,s).map(t=>({label:t.label||t.id,tag:`{${t.id}}`,sample:"",hint:""})),...Tt(e.filter(t=>t.id!==s)).map(t=>({label:t.label,tag:t.insert,sample:"",hint:""}))],empty:"Add a number, scale or priced choice question to reference it here."}]})}function kn(n){const e=r("div",{class:"atfb-overlay"}),s=()=>{e.remove(),document.removeEventListener("keydown",t)},t=u=>{u.key==="Escape"&&s()},a=r("textarea",{class:"atfb-input atfb-formula__input",attrs:{rows:"3","aria-label":"Formula"}});a.value=String(n.field.formula??"");const i=r("p",{class:"atfb-formula__result",attrs:{"aria-live":"polite"}}),o=xn(n.fields,n.field.id),l=()=>{const u=a.value.trim();if(u===""){i.textContent="Empty. Reference a question below to start.",i.classList.remove("is-error");return}const f=hn(u,o,n.fields);if(f===null){i.textContent="This does not compute yet — check the braces and parentheses.",i.classList.add("is-error");return}const y=_e(n.fields,n.field.id).map((p,x)=>`${p.label||p.id} = ${x+1}`).concat(n.fields.filter(p=>p.type==="repeater"&&p.id!==n.field.id).map(p=>`${p.label||p.id} = 2 sample rows`)).join(", ");i.textContent=`With sample answers (${y}): ${f}`,i.classList.remove("is-error")};a.addEventListener("input",l);const c=(u,f,y=0)=>r("button",{class:"atfb-formula__chip",type:"button",text:u,on:{click:()=>{if(bt(a,f),y>0){const p=(a.selectionStart??a.value.length)-y;a.setSelectionRange(p,p)}}}}),d=_e(n.fields,n.field.id),h=Tt(n.fields.filter(u=>u.id!==n.field.id)),m=r("div",{class:"atfb-formula__chips",children:d.length||h.length?[...d.map(u=>c(u.label||u.id,`{${u.id}}`)),...h.map(u=>c(u.label,u.insert))]:[r("p",{class:"atfb-hint",text:"No number-shaped questions yet — add a number, scale or priced choice field and it appears here."})]}),g=r("div",{class:"atfb-formula__chips",children:_n.map(u=>c(`${u}()`,`${u}()`,1))});e.append(r("div",{class:"atfb-modal atfb-formula",attrs:{role:"dialog","aria-label":"Formula editor"},children:[r("h2",{text:"Formula"}),Mt(a,n.fields,n.field.id),i,w("Your questions",m,"Click one to reference its answer."),w("Functions",g),r("div",{class:"atfb-modal__actions",children:[C("Cancel",s),C("Save formula",()=>{n.onSave(a.value.trim()),s()},"primary")]})]})),e.addEventListener("click",u=>{u.target===e&&s()}),document.addEventListener("keydown",t),n.root.append(e),l(),a.focus(),a.setSelectionRange(a.value.length,a.value.length)}function Ft(n){return[...n.fields.filter(e=>e.type!=="page_break").map(e=>({key:`field:${e.id}`,label:`Field: ${e.label||e.id}`,fieldId:e.id,logic:e.logic})),...n.notifications.map(e=>({key:`notification:${e.id}`,label:`Notification: ${e.name||e.id}`,logic:e.logic})),...n.confirmations.map(e=>({key:`confirmation:${e.id}`,label:`Confirmation: ${e.name||e.id}`,logic:e.logic}))]}function It(n,e,s){return n.key!==e.key&&n.logic.rules.length>0&&n.logic.rules.every(t=>t.field!==e.fieldId&&s.fields.some(a=>a.id===t.field&&a.type!=="page_break"))}function Sn(n,e,s,t){const a=Ft(n),i=a.find(l=>l.key===e),o=a.find(l=>l.key===s);return!i||!o||!It(i,o,n)?!1:(t?.(),Object.assign(o.logic,i.logic,{rules:i.logic.rules.map(l=>({...l}))}),!0)}function Dt(n){const e=document.activeElement,s=r("div",{class:"atfb-overlay"}),t=r("div",{class:"atfb-modal atfb-condition-copy",attrs:{role:"dialog","aria-modal":"true","aria-label":"Copy condition",tabindex:"-1"}});let a="";const i=n.to,o=()=>{s.remove(),e?.focus()},l=()=>{const c=n.schema();if(!c){o();return}const d=Ft(c),h=d.find(p=>p.key===i),m=d.filter(p=>p.logic.rules.length&&h&&It(p,h,c)),g=m.find(p=>p.key===a),u=D(a,[{value:"",label:"Choose a section…"},...m.map(p=>({value:p.key,label:p.label}))],p=>{a=p,l(),t.querySelector('[aria-label="Copy from"]')?.focus()});u.setAttribute("aria-label","Copy from");const f=C("Copy condition",()=>{const p=n.schema();p&&Sn(p,a,i,n.beforeCopy)&&(o(),n.onCopy())},"primary");(!g||!i)&&f.setAttribute("disabled","");const y=d.find(p=>p.key===i);t.replaceChildren(r("h2",{text:"Copy condition"}),w("Copy from",u),r("p",{class:"atfb-hint",text:`Apply to ${y?.label??"this section"}.`}),r("p",{class:"atfb-condition-copy__preview",attrs:{"aria-live":"polite"},text:g?`${g.logic.enabled?"Enabled":"Disabled"} · ${g.logic.action==="hide"?"Hide":"Show"} · Match ${g.logic.match}: `+g.logic.rules.map(p=>qe(be(p,c.fields))).join(g.logic.match==="all"?" and ":" or "):m.length?"Choose the section whose condition you want to reuse.":"No conditions are available to copy here yet."}),r("p",{class:"atfb-hint",text:y?.logic.rules.length?"This replaces the destination’s entire condition. You can edit the copy independently.":"Copies every rule, all/any matching, show/hide and enabled state. You can edit the copy independently."}),r("div",{class:"atfb-modal__actions",children:[C("Cancel",o),f]}))};s.append(t),s.addEventListener("click",c=>{c.target===s&&o()}),s.addEventListener("keydown",c=>{if(c.key==="Escape"&&(c.preventDefault(),o()),c.key==="Tab"){const d=[...t.querySelectorAll("button:not([disabled]), select, os-select, os-button:not([disabled])")],h=d[0],m=d[d.length-1];c.shiftKey&&(document.activeElement===h||document.activeElement===t)?(c.preventDefault(),m?.focus()):!c.shiftKey&&document.activeElement===m&&(c.preventDefault(),h?.focus())}c.stopPropagation()}),n.root.append(s),l(),t.focus()}const Cn="allterrain-forms/form";function En(){return window.wp?.os?.relations??null}function qt(n){const e=n.closest("[data-window-id], .os-window");if(!e)return null;const s=e.getAttribute("data-window-id");if(s)return s;const t=e.id??"";return t?t.replace(/^wp-window-/,""):null}const Ln=6e3,Ot=120;function Nt(n,e){const s=En();if(Ge.set(n,e),!s?.set)return;const t=i=>{if(xe.get(n)!==a)return;const o=qt(n);if(!o){Date.now()t(i),Ot);return}try{s.set(o,e)}catch(c){Rt||(Rt=!0,console.error("[AllTerrain Forms] The shell refused a window identity.",c,e)),xe.delete(n);return}if(!e||s.get?.(o)?.id===e.id||Date.now()>=i){xe.delete(n);return}window.setTimeout(()=>t(i),Ot)},a=Symbol("atf-identity");xe.set(n,a),t(Date.now()+Ln)}let Rt=!1;const xe=new WeakMap,Ge=new Map;function $n(){for(const[n,e]of Ge){if(!n.isConnected){Ge.delete(n);continue}Nt(n,e)}}if(typeof document<"u")for(const n of["os-window-content-loaded","os-window-opened"])document.addEventListener(n,()=>$n());function An(n,e){return{type:Cn,id:n.id,label:n.title||"Untitled form",related:[{id:`allterrain-forms/entries-${n.id}`,label:"Entries for this form",url:`${e}admin.php?page=allterrain-forms-entries&form=${n.id}`,group:"allterrain-forms",groupLabel:"Forms",icon:"dashicons-list-view"}]}}const ke={letters:{label:"Letters",chars:"A-Za-zÀ-ÖØ-öø-ÿ"},numbers:{label:"Numbers",chars:"0-9"},spaces:{label:"Spaces",chars:" "},punctuation:{label:`Punctuation ( . , ! ? ' " - )`,chars:`.,!?'"()\\-:;`},symbols:{label:"Symbols ( @ # & _ / + )",chars:"@#&_/+*%="}};function Tn(){return{mode:"blocks",starts:"",ends:"",contains:"",notContains:"",chars:[],minLen:"",maxLen:"",regex:"",message:"",tests:[]}}function Pt(n){const e=Tn();let s;try{s=JSON.parse(n)}catch{return e}if(!s||typeof s!="object")return e;const t=s;e.mode=t.mode==="regex"?"regex":"blocks";for(const a of["starts","ends","contains","notContains","minLen","maxLen","regex","message"])typeof t[a]=="string"&&(e[a]=t[a]);return Array.isArray(t.chars)&&(e.chars=t.chars.filter(a=>typeof a=="string"&&a in ke)),Array.isArray(t.tests)&&(e.tests=t.tests.filter(a=>typeof a=="string").slice(0,10)),e}function Se(n){return n.replace(/[.*+?^${}()|[\]\\/]/g,"\\$&")}function Ce(n){if(n.mode==="regex")return n.regex.trim();const e=[],s=n.minLen.trim(),t=n.maxLen.trim();(s!==""||t!=="")&&e.push(`(?=.{${s===""?"0":parseInt(s,10)||0},${t===""?"":parseInt(t,10)||""}}$)`),n.contains!==""&&e.push(`(?=.*${Se(n.contains)})`),n.notContains!==""&&e.push(`(?!.*${Se(n.notContains)})`),n.starts!==""&&e.push(`(?=${Se(n.starts)})`),n.ends!==""&&e.push(`(?=.*${Se(n.ends)}$)`);const a=n.chars.map(o=>ke[o]?.chars??"").join(""),i=a?`[${a}]*$`:".*$";return!e.length&&!a?"":`^${e.join("")}${i}`}function Bt(n){if(n.mode==="regex")return n.regex.trim()?`Matches the expression ${n.regex.trim()}`:"";const e=[];if(n.starts&&e.push(`starts with “${n.starts}”`),n.ends&&e.push(`ends with “${n.ends}”`),n.contains&&e.push(`contains “${n.contains}”`),n.notContains&&e.push(`never contains “${n.notContains}”`),n.chars.length){const i=n.chars.map(o=>(ke[o]?.label??o).split(" (")[0].toLowerCase());e.push(`uses only ${i.join(" and ")}`)}const s=n.minLen.trim(),t=n.maxLen.trim();return s&&t?e.push(`is ${s}–${t} characters long`):s?e.push(`is at least ${s} characters long`):t&&e.push(`is at most ${t} characters long`),e.length?`The answer ${e.length>1?`${e.slice(0,-1).join(", ")}, and ${e[e.length-1]}`:e[0]}.`:""}function Mn(n,e){const s=Ce(n);if(s==="")return null;try{return new RegExp(s).test(e)}catch{return null}}function Fn(){return window.wp?.os?.windowManager??null}const Ht="allterrain-forms-validation";function In(n){const e=Fn(),s=qt(n.root);if(e?.openChild&&s){Dn(e,s,n);return}qn(n)}function Dn(n,e,s){const t=`${Ht}-${s.field.id}`;n.getById?.(t)&&n.remove?.(t);const a={saved:!1},i=()=>{const o=n.getById?.(t);o?.close?o.close():n.remove?.(t)};n.openChild?.(e,{id:t,baseId:Ht,url:`#${t}`,title:`Custom rule — ${s.field.label||"this question"}`,icon:"dashicons-yes-alt",native:!0,width:560,height:700,minWidth:440,minHeight:480,ephemeral:!0,autofocus:".atfb-valwin__pane:not([hidden]) input, .atfb-valwin__pane:not([hidden]) textarea",render:o=>{const l=r("div",{class:"atfa atfb-valwin atfb-valwin--window"}),c=r("div",{class:"atfb-valwin__scroll"});l.append(c),jt(c,s,a,i,"window");const d=c.querySelector(".atfb-modal__actions");d&&l.append(d),o.append(l)},onClose:()=>{a.saved||s.onCancel?.()}})}function qn(n){const e=r("div",{class:"atfb-overlay"}),s={saved:!1},t=()=>{e.remove(),document.removeEventListener("keydown",a),s.saved||n.onCancel?.()},a=o=>{o.key==="Escape"&&t()},i=r("div",{class:"atfb-modal atfb-valwin",attrs:{role:"dialog","aria-label":"Custom validation rule"},children:[r("h2",{text:"Custom rule"})]});jt(i,n,s,t,"overlay"),e.append(i),e.addEventListener("click",o=>{o.target===e&&t()}),document.addEventListener("keydown",a),n.root.append(e),e.querySelector(".atfb-valwin__pane:not([hidden]) input, .atfb-valwin__pane:not([hidden]) textarea")?.focus()}function jt(n,e,s,t,a){const i=Pt(String(e.field.validationRecipe??""));i.mode==="blocks"&&Ce(i)===""&&e.field.pattern&&(i.mode="regex",i.regex=String(e.field.pattern)),i.message||(i.message=String(e.field.messages?.invalid??""));const o=(b,M)=>{const $=r("input",{class:"atfb-input",value:i[b],placeholder:M,attrs:{type:"text"}});return $.addEventListener("input",()=>{i[b]=$.value,L()}),$},l=(b,M)=>{const $=r("input",{class:"atfb-input atfb-valwin__len",value:i[b],attrs:{type:"number",min:"0","aria-label":M}});return $.addEventListener("input",()=>{i[b]=$.value,L()}),$},c=r("div",{class:"atfb-valwin__chars",children:Object.entries(ke).map(([b,M])=>q(M.label,i.chars.includes(b),$=>{i.chars=$?[...i.chars,b]:i.chars.filter(W=>W!==b),L()}))}),d=r("div",{class:"atfb-valwin__pane",children:[w("Starts with",o("starts","e.g. AT-")),w("Ends with",o("ends","e.g. -2026")),w("Must contain",o("contains","e.g. @")),w("Must not contain",o("notContains","e.g. spaces? type one")),w("Only these characters",c,"Leave every box unticked to allow anything."),w("Length",r("div",{class:"atfb-valwin__lengths",children:[r("span",{text:"between"}),l("minLen","Minimum length"),r("span",{text:"and"}),l("maxLen","Maximum length"),r("span",{text:"characters"})]}),"Leave a box empty for no limit.")]}),h=r("textarea",{class:"atfb-input atfb-valwin__regex",attrs:{rows:"2","aria-label":"Regular expression",placeholder:"^AT-[0-9]{4}$"}});h.value=i.regex,h.addEventListener("input",()=>{i.regex=h.value,L()});const m=r("div",{class:"atfb-valwin__pane",children:[w("Expression",h,"A regular expression, without slashes. Checked against the whole answer only if you anchor it with ^ and $.")]}),g=[["blocks","Easy blocks"],["regex","Expression (advanced)"]],u=b=>{i.mode=b,d.hidden=b!=="blocks",m.hidden=b!=="regex",L()},y=(()=>{if(Y("os-segmented")&&Y("os-segment")){const $=document.createElement("os-segmented");$.setAttribute("value",i.mode),$.setAttribute("label","How to write the rule"),$.classList.add("atfb-valwin__tabs");for(const[W,H]of g){const ce=document.createElement("os-segment");ce.setAttribute("value",W),ce.textContent=H,$.append(ce)}return $.addEventListener("os-pick",W=>{const H=W.detail?.value;(H==="blocks"||H==="regex")&&($.setAttribute("value",H),u(H))}),$}const b=r("div",{class:"atfb-valwin__tabs",attrs:{role:"tablist"}}),M=()=>{b.replaceChildren(...g.map(([$,W])=>{const H=i.mode===$;return r("button",{class:`atfb-valwin__tab${H?" is-active":""}`,type:"button",text:W,attrs:{role:"tab","aria-selected":H?"true":"false"},on:{click:()=>{u($),M()}}})}))};return M(),b})();d.hidden=i.mode!=="blocks",m.hidden=i.mode!=="regex";const p=r("input",{class:"atfb-input",value:i.message,placeholder:"That is not in the expected format.",attrs:{type:"text"}});p.addEventListener("input",()=>{i.message=p.value});const x=r("p",{class:"atfb-valwin__summary",attrs:{"aria-live":"polite"}}),F=r("div",{class:"atfb-valwin__samples"}),S=b=>{const M=r("span",{class:"atfb-valwin__verdict",attrs:{"aria-live":"polite"}}),$=r("input",{class:"atfb-input",value:b,placeholder:"Type a sample answer…",attrs:{type:"text"}});$.addEventListener("input",()=>L()),F.append(r("div",{class:"atfb-valwin__sample",children:[$,M]}))};for(const b of i.tests.length?i.tests:["","",""])S(b);const j=()=>Array.from(F.querySelectorAll("input")).map(b=>b.value),L=()=>{const b=Bt(i);x.textContent=b||"Nothing yet — fill in a block above and the rule appears here in plain words.";for(const M of Array.from(F.querySelectorAll(".atfb-valwin__sample"))){const $=M.querySelector("input"),W=M.querySelector(".atfb-valwin__verdict");if(!$||!W)continue;const H=$.value===""?null:Mn(i,$.value);W.textContent=H===null?"·":H?"✓ passes":"✗ fails",W.classList.toggle("is-pass",H===!0),W.classList.toggle("is-fail",H===!1)}};n.append(r("p",{class:"atfb-hint",text:`Describe what a good answer to “${e.field.label||"this question"}” looks like — no code needed.`}),y,d,m,r("div",{class:"atfb-valwin__try",children:[r("h3",{text:"Try it out"}),x,F,C("Add another sample",()=>{S(""),L()},"ghost","plus-alt2")]}),w("When it fails, say",p,"Shown to the visitor when their answer breaks the rule. Leave empty for the default wording."),r("div",{class:"atfb-modal__actions",children:[C(a==="window"?"Close without saving":"Cancel",t),C("Save rule",()=>{const b=Ce(i);i.tests=j().filter(M=>M!=="").slice(0,10),s.saved=!0,e.onSave({pattern:b,recipe:i,message:i.message.trim()}),t()},"primary")]})),L()}const On=["Contact","Numbers & codes","Text shape","Web"],Wt=[{slug:"email",label:"An email address",group:"Contact",example:"jane@example.com",pattern:"^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$",message:"That does not look like an email address."},{slug:"phone",label:"A phone number",group:"Contact",example:"+34 612 345 678",pattern:"^(?=(?:[^0-9]*[0-9]){5,})\\+?[0-9 ().-]{5,24}$",message:"That does not look like a phone number."},{slug:"handle",label:"A username or @handle",group:"Contact",example:"@yourname",pattern:"^@?[A-Za-z0-9_]{2,30}$",message:"That does not look like a username."},{slug:"digits",label:"Numbers only",group:"Numbers & codes",example:"12345",pattern:"^[0-9]+$",message:"Numbers only, please."},{slug:"decimal",label:"A number, decimals allowed",group:"Numbers & codes",example:"3.14",pattern:"^-?[0-9]+([.,][0-9]+)?$",message:"That does not look like a number."},{slug:"price",label:"A price",group:"Numbers & codes",example:"19.99",pattern:"^[0-9]+([.,][0-9]{1,2})?$",message:"That does not look like a price."},{slug:"zip_us",label:"A ZIP code (US)",group:"Numbers & codes",example:"90210",pattern:"^[0-9]{5}(-[0-9]{4})?$",message:"That does not look like a ZIP code."},{slug:"postcode_uk",label:"A postcode (UK)",group:"Numbers & codes",example:"SW1A 1AA",pattern:"^[A-Za-z]{1,2}[0-9][A-Za-z0-9]? ?[0-9][A-Za-z]{2}$",message:"That does not look like a postcode."},{slug:"iban",label:"An IBAN",group:"Numbers & codes",example:"DE89 3704 0044 0532 0130 00",pattern:"^[A-Za-z]{2}[0-9]{2}(?: ?[A-Za-z0-9]){10,32}$",message:"That does not look like an IBAN."},{slug:"credit_card",label:"A card number",group:"Numbers & codes",example:"4242 4242 4242 4242",pattern:"^[0-9](?:[0-9 -]{9,21})?[0-9]$",message:"That does not look like a card number.",luhn:!0},{slug:"letters",label:"Letters only",group:"Text shape",example:"María López",pattern:"^[\\p{L}\\p{M} .'’-]+$",message:"Letters only, please."},{slug:"alphanumeric",label:"Letters and numbers only",group:"Text shape",example:"abc123",pattern:"^[A-Za-z0-9]+$",message:"Letters and numbers only, please."},{slug:"no_spaces",label:"One word, no spaces",group:"Text shape",example:"one-word",pattern:"^\\S+$",message:"No spaces allowed."},{slug:"url",label:"A web address",group:"Web",example:"https://example.com",pattern:"^(https?://)?([A-Za-z0-9-]+\\.)+[A-Za-z]{2,}([/?#]\\S*)?$",message:"That does not look like a web address."},{slug:"ip",label:"An IP address",group:"Web",example:"192.168.0.1",pattern:"^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])$",message:"That does not look like an IP address."},{slug:"slug",label:"A URL slug",group:"Web",example:"my-page-title",pattern:"^[a-z0-9]+(-[a-z0-9]+)*$",message:"Lowercase letters, numbers and dashes only."},{slug:"hex_color",label:"A hex colour",group:"Web",example:"#3366ff",pattern:"^#?([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$",message:"That does not look like a colour code."}];function Nn(n){return Wt.find(e=>e.slug===n)??null}function Xe(){return window.wp?.os??null}const Ut="allterrain-forms/preview",Ze="allterrain-forms-preview";function Rn(n){const e=Xe();if(!e?.registerTitleBarButton)return()=>{};const s=()=>{try{e.registerTitleBarButton({id:Ut,label:"Preview this form",icon:"dashicons-visibility",placement:"right",order:90,match:t=>{const a=t?.id??t?.config?.id??"";return a==="allterrain-forms"||a.startsWith("allterrain-forms#")},onClick:()=>void zt(n),owner:"allterrain-forms-builder"})}catch{}};return e.ready?e.ready(s):s(),()=>{try{e.unregisterTitleBarButton?.(Ut)}catch{}}}async function zt(n){n.isDirty()&&await n.save();const e=n.current();e&&Yt(e.id,e.title,e.previewUrl)}function Yt(n,e,s){const t=Xe();if(!t?.windowManager?.open){window.open(s,"_blank","noopener");return}t.windowManager.open({id:`${Ze}-${n}`,baseId:Ze,url:s,title:`Preview: ${e}`,icon:"dashicons-visibility"})}function Pn(n,e,s){if(!Xe()?.windowManager?.open||!document.querySelector(`[data-window-id="${Ze}-${n}"]`))return;const i=s.includes("?")?"&":"?";Yt(n,e,`${s}${i}alltfo_r=${Date.now()}`)}const se="allterrain-forms/field",Bn=["openstation/file","desktop-mode/file","openstation/attachment"],Vt="allterrain-forms/logic-map-v2";function J(n,e){return n.dataset.atfbBind=e,n}const Kt={level:{key:"level",label:"Heading level",control:"select",hint:"Headings should step down one at a time.",options:[{value:"2",label:"Heading 2"},{value:"3",label:"Heading 3"},{value:"4",label:"Heading 4"},{value:"5",label:"Heading 5"},{value:"6",label:"Heading 6"}]},content:{key:"content",label:"HTML",control:"textarea",hint:"Shown as written. Scripts are stripped when the form is saved."},consenttext:{key:"consentText",label:"What they are agreeing to",control:"textarea",hint:"Shown beside the tick box. Links are allowed."},height:{key:"height",label:"Height",control:"number",hint:"In pixels."},columns:{key:"columns",label:"Columns",control:"number",hint:"How many pictures sit side by side."},multiple:{key:"multiple",label:"Let them choose more than one",control:"checkbox"},inline:{key:"inline",label:"Lay the options out in a row",control:"checkbox"},other:{key:"other",label:"Offer an “Other” box",control:"checkbox",hint:"Adds a final option with a box to type in."},filetypes:{key:"filetypes",label:"Accepted file types",control:"commas",hint:"Extensions, separated by commas. Empty accepts anything the site allows."},maxsize:{key:"maxsize",label:"Largest file",control:"number",hint:"In megabytes."},maxfiles:{key:"maxfiles",label:"How many files",control:"number"},minrows:{key:"minRows",label:"Fewest rows",control:"number",also:{key:"maxRows",label:"Most rows"}},itemlabel:{key:"itemLabel",label:"What is one row called?",control:"text",hint:"Names each card — “Attendee 1”, “Attendee 2” — and the Remove button. Also editable on the card itself."},endlabels:{key:"minLabel",label:"Label at the low end",control:"text",also:{key:"maxLabel",label:"Label at the high end"}},points:{key:"points",label:"Points if correct",control:"number"},minchoices:{key:"minChoices",label:"Fewest they may pick",control:"number",also:{key:"maxChoices",label:"Most they may pick"}}},Hn={label:"the canvas edits it in place, and the inspector mirrors it",placeholder:"edited by typing into the control on the canvas",hint:"edited under the control on the canvas",nextlabel:"edited on the page break’s own Next button",prevlabel:"edited on the page break’s own Back button",addlabel:"edited on the repeater’s own Add button",choices:"the choices editor, and the option list on the canvas",required:"the toggle on the card",width:"its own row",css:"its own row",prefill:"its own section",logic:"the conditional logic section",formula:"its own row, with the currency that goes with it",currency:"rendered with the formula",correct:"the choices editor marks the right answer",default:"its own row, typed to match what the field stores",rows:"a row count on a textarea; the statement list on a Likert matrix",parts:"a tick box per part, listed by the server",min:"the validation section, paired with max",max:"the validation section, paired with min",step:"the validation section",minlength:"the validation section, paired with maxlength",maxlength:"the validation section, paired with minlength",mindate:"the validation section, paired with maxdate",maxdate:"the validation section, paired with mindate",mintime:"the validation section, paired with maxtime",maxtime:"the validation section, paired with mintime",pattern:"the validation section",unique:"the validation section",maxchoices:"rendered with minchoices",maxrows:"rendered with minrows"};function Gt(n,e,s){const t=n[e.key],a=i=>s(e.key,i);if(e.control==="checkbox")return q(e.label,!!t,a);if(e.control==="commas"){const i=Array.isArray(t)?t:[];return w(e.label,T(i.join(", "),o=>{a(o.split(",").map(l=>l.trim().replace(/^\./,"").toLowerCase()).filter(Boolean))}),e.hint)}return e.control==="select"?w(e.label,D(String(t??""),e.options??[],a),e.hint):e.control==="textarea"?w(e.label,fe(String(t??""),a),e.hint):e.control==="number"?w(e.label,V(String(t??""),a),e.hint):w(e.label,J(T(String(t??""),a),e.key),e.hint)}function Xt(n,e){const s=new Set(n.map(a=>a.key).filter(Boolean));let t=n.length+1;return e.split(` +`).map(a=>a.trim()).filter(Boolean).map((a,i)=>{const o=n[i]?.key;if(o)return{key:o,label:a};for(;s.has(`r${t}`);)t+=1;return s.add(`r${t}`),{key:`r${t}`,label:a}})}function Zt(n,e,s){const t=n.findIndex(i=>i.id===e);if(t<0)return null;const a=Math.max(0,Math.min(s,n.length-1));return a===t?null:{from:t,to:a}}const Je=[{value:"user:email",label:"Their email address",group:"About the person filling it in",tag:"{user:email}"},{value:"user:display_name",label:"Their name",group:"About the person filling it in",tag:"{user:display_name}"},{value:"user:first_name",label:"Their first name",group:"About the person filling it in"},{value:"user:last_name",label:"Their last name",group:"About the person filling it in"},{value:"user:login",label:"Their username",group:"About the person filling it in"},{value:"date:today",label:"Today’s date",group:"The date and time",tag:"{date}"},{value:"date:now",label:"The time right now",group:"The date and time",tag:"{time}"},{value:"site",label:"This site’s name",group:"About this site",tag:"{site}"},{value:"site:url",label:"This site’s address",group:"About this site",tag:"{site:url}"},{value:"site:admin_email",label:"The site administrator’s email",group:"About this site",tag:"{admin_email}"}],jn=["About the person filling it in","The date and time","About this site"],Qe=(n,e)=>U?.i18n?.[n]??e;class Jt{constructor(e){this.config=null,this.themes=[],this.forms=[],this.form=null,this.schema=null,this.selected=null,this.tab="build",this.logicMap=null,this.canvasTheme=r("style"),this.canvasThemeSignature="",this.openSections=new Map,this.logicMapOn=fs(Vt)!=="off",this.dirty=!1,this.editGeneration=0,this.saveInFlight=!1,this.queuedSave=null,this.teardowns=[],this.canvasTarget=null,this.history=[],this.historyAt=-1,this.autosave=Me(()=>{this.save(!0)},2500),this.renderInspector=ps(()=>{if(R(this.inspector),!this.schema||(this.root.classList.toggle("atfb--build-only-panes",this.tab!=="build"),this.tab!=="build"))return;const s=this.selected?this.locateField(this.selected):void 0,t=s?.field,a=s?.parent??null;if(this.root.classList.toggle("atfb--has-selection",!!t),!t){this.inspector.append(r("div",{class:"atfb-placeholder",children:[r("p",{text:"Select a field to change it."}),r("p",{class:"atfb-hint",text:"Drag a field from the palette, or press one to add it to the end."})]}));return}const i=this.config?.fieldTypes.find(c=>c.type===t.type),o=i?.supports??[],l=(c,d)=>{const h=this.liveField(t.id);h&&(h[c]=d,this.markDirty(),this.renderCanvas())};this.inspector.append(r("h3",{class:"atfb-inspector__title",text:i?.label??t.type})),a?this.inspector.append(r("p",{class:"atfb-hint atfb-inspector__crumb",text:`Inside ${a.label||"a repeater"} — the visitor answers this once per ${String(a.itemLabel??"")||"row"}.`}),r("p",{class:"atfb-hint",text:`Formulas aggregate it as {${a.id}.${t.id}} — e.g. sum( {${a.id}.${t.id}} ).`})):this.inspector.append(r("p",{class:"atfb-hint",text:`Reference this field as {field:${t.id}}`})),o.includes("label")&&this.inspector.append(w("Label",J(T(t.label,c=>l("label",c)),"label"))),o.includes("placeholder")&&this.inspector.append(w("Placeholder",J(T(t.placeholder,c=>l("placeholder",c)),"placeholder"))),o.includes("hint")&&this.inspector.append(w("Hint",J(T(t.hint,c=>l("hint",c)),"hint"),"Shown under the field, and read out with it.")),o.includes("nextlabel")&&this.inspector.append(w("Next button",J(T(String(t.nextLabel??""),c=>l("nextLabel",c)),"nextLabel"),"Leave empty for “Next”.")),o.includes("prevlabel")&&this.inspector.append(w("Back button",J(T(String(t.prevLabel??""),c=>l("prevLabel",c)),"prevLabel"),"Leave empty for “Back”.")),o.includes("addlabel")&&this.inspector.append(w("Add button",J(T(String(t.addLabel??""),c=>l("addLabel",c)),"addLabel"),"Leave empty for “Add another”.")),o.includes("required")&&this.inspector.append(q("Required",t.required,c=>l("required",c))),o.includes("width")&&this.inspector.append(w("Width",D(t.width,[{value:"full",label:"Full width"},{value:"two-thirds",label:"Two thirds"},{value:"half",label:"Half"},{value:"third",label:"One third"},{value:"quarter",label:"One quarter"}],c=>l("width",c)))),i?.choices&&this.inspector.append(this.renderChoicesEditor(t,l)),this.renderTypeSettings(t,i,o,l),(t.type==="total"||o.includes("formula"))&&this.inspector.append(w("Formula",r("div",{class:"atfb-formula__row",children:[Mt(T(String(t.formula??""),c=>l("formula",c)),this.schema?.fields??[],t.id),C("Formula editor",()=>kn({root:this.root,fields:this.schema?.fields??[],field:this.liveField(t.id)??t,onSave:c=>{l("formula",c),this.renderInspector()}}))]}),"Reference answers with braces — {f1} * {f2} + 10 — or open the editor and click them in."),w("Currency symbol",T(String(t.currency??""),c=>l("currency",c)))),this.inspector.append(this.renderValidationSection(t,o,l)),a||(this.inspector.append(this.renderLogicSection(t)),o.includes("prefill")&&this.inspector.append(this.prefillControl(t,l))),o.includes("css")&&this.inspector.append(w("CSS class",T(t.cssClass,c=>l("cssClass",c))))}),this.root=e,this.bar=e.querySelector("[data-atfb-bar]")??r("div"),this.palette=e.querySelector("[data-atfb-palette]")??r("div"),this.canvas=e.querySelector("[data-atfb-canvas]")??r("div"),this.inspector=e.querySelector("[data-atfb-inspector]")??r("div"),this.canvas.addEventListener("click",s=>{const t=s.target;if(!(this.tab!=="build"||!this.selected||t.closest("[data-atfb-card], button, os-button, a, input, textarea, select, os-select, label, [contenteditable]"))){this.selected=null;for(const a of this.canvas.querySelectorAll(".atfb-card.is-selected"))a.classList.remove("is-selected");this.renderInspector()}})}async start(){try{const[t,a,i]=await Promise.all([I.config(),I.listThemes(),I.listForms()]);this.config=t,this.themes=a,this.forms=i}catch(t){this.fail(t);return}this.teardowns.push(os([se])),this.teardowns.push(Rn({current:()=>this.form?{id:this.form.id,title:this.form.title,previewUrl:this.form.previewUrl}:null,isDirty:()=>this.dirty,save:()=>this.save(!0)}));const e=t=>{this.dirty&&(t.preventDefault(),t.returnValue="")};window.addEventListener("beforeunload",e),this.teardowns.push(()=>window.removeEventListener("beforeunload",e));const s=t=>{!(t.metaKey||t.ctrlKey)||t.key.toLowerCase()!=="z"||t.target?.closest("input, textarea, [contenteditable]")||(t.preventDefault(),this.travel(t.shiftKey?1:-1))};if(this.root.addEventListener("keydown",s),this.teardowns.push(()=>this.root.removeEventListener("keydown",s)),this.renderBar(),this.renderPalette(),this.forms.length){const t=Ss("builder");await this.open(t&&this.forms.some(a=>a.id===t)?t:this.forms[0].id)}else this.renderFormsList()}destroy(){this.canvasTarget?.(),this.canvasTarget=null,this.teardowns.forEach(e=>e()),this.teardowns=[]}fail(e){R(this.bar),this.bar.append(r("p",{class:"atfb-error",text:e instanceof Error?e.message:"Something went wrong loading your forms."}))}renderBar(){R(this.bar);const e=D(String(this.form?.id??""),[...this.forms.map(a=>({value:String(a.id),label:a.title||"(untitled)"}))],a=>void this.open(Number(a)));e.setAttribute("aria-label","Choose a form");const s=r("input",{class:"atfb-title",type:"text",value:this.form?.title??"",placeholder:"Untitled form",attrs:{"aria-label":"Form title"},on:{input:a=>{this.form&&(this.form.title=a.target.value,this.markDirty())}}}),t=r("div",{class:"atfb-tabs",attrs:{role:"tablist"},children:[["build","Build"],["theme","Theme"],["settings","Settings"],["notify","Notifications"],["confirm","Confirmations"]].map(([a,i])=>r("button",{class:`atfb-tab${this.tab===a?" is-active":""}`,type:"button",text:i,attrs:{role:"tab","aria-selected":this.tab===a},on:{click:()=>{this.tab=a,this.renderBar(),this.renderInspector(),this.renderCanvas()}}}))});this.bar.append(r("div",{class:"atfb-bar__left",children:[this.forms.length>1?e:null,s]}),t,r("div",{class:"atfb-bar__right",children:[this.historyButton("undo","Undo",-1),this.historyButton("redo","Redo",1),C("New",()=>void this.showTemplates(),"secondary","plus-alt2"),C("Export",()=>void this.exportForm(),"secondary","download"),C("Import",()=>void this.importForm(),"secondary","upload"),C("Preview",()=>void this.preview(),"secondary","visibility"),C("Entries",()=>this.openEntries(),"secondary","list-view"),this.logicMapButton(),this.saveButton()]}))}liveField(e){return this.locateField(e)?.field}locateField(e){const s=this.schema?.fields??[];for(let t=0;tqueueMicrotask(()=>this.rebindCanvas()),{once:!0});return}if(this.canvas.matches(":hover")){this.canvas.addEventListener("pointerleave",()=>this.rebindCanvas(),{once:!0});return}this.renderCanvas()}snapshot(){if(!this.schema)return;const e=JSON.stringify(this.schema);this.history[this.historyAt]!==e&&(this.history=this.history.slice(0,this.historyAt+1),this.history.push(e),this.history.length>60&&this.history.shift(),this.historyAt=this.history.length-1,this.syncHistoryButtons())}travel(e){const s=this.historyAt+e;!this.schema||s<0||s>=this.history.length||(this.historyAt=s,this.schema=JSON.parse(this.history[s]),this.schema.fields.some(t=>t.id===this.selected)||(this.selected=null),this.markDirty(),this.renderBar(),this.renderCanvas(),this.renderInspector())}historyButton(e,s,t){const a=C(s,()=>this.travel(t),"secondary",e);return a.setAttribute("data-atfb-history",String(t)),a.disabled=!this.canTravel(t),a}canTravel(e){const s=this.historyAt+e;return s>=0&&sURL.revokeObjectURL(t),1e3)}async importForm(){const e=r("input",{type:"file",attrs:{accept:"application/json,.json"}});e.addEventListener("change",async()=>{const s=e.files?.[0];if(s)try{const t=JSON.parse(await s.text());if(!t.schema)throw new Error("That file does not contain a form.");const a=await I.createForm({title:t.title??s.name.replace(/\.json$/i,""),schema:t.schema});this.forms.unshift({id:a.id,title:a.title,status:a.status,modified:a.modified,fields:a.schema.fields.length,theme:a.schema.settings.theme,entries:0,unread:0,views:0,submissions:0,shortcode:a.shortcode}),this.form=a,this.schema=a.schema,this.selected=null,this.dirty=!1,this.history=[],this.historyAt=-1,this.snapshot(),this.renderBar(),this.renderCanvas(),this.renderInspector(),N("Form imported",a.title)}catch(t){N("Could not import that file",t instanceof Error?t.message:"","error")}}),e.click()}saveButton(){const e=C("Save",()=>void this.save(),"primary");return e.disabled=!this.dirty,e.setAttribute("data-atfb-save",""),e}markDirty(){this.dirty=!0,this.editGeneration+=1;const e=this.bar.querySelector("[data-atfb-save]");e&&(e.disabled=!1),this.autosave()}async save(e=!1){if(!this.form||!this.schema)return;if(this.saveInFlight){this.queuedSave={silent:e&&(this.queuedSave?.silent??!0)};return}this.saveInFlight=!0;const s=this.editGeneration;try{const t=await I.updateForm(this.form.id,{title:this.form.title,schema:this.schema});if(s===this.editGeneration){this.form=t,this.schema=t.schema,this.dirty=!1,this.rebindCanvas();const i=this.bar.querySelector("[data-atfb-save]");i&&(i.disabled=!0)}Bs(t.id);const a=this.forms.find(i=>i.id===t.id);a&&(a.title=t.title),Pn(t.id,t.title,t.previewUrl),e||N("Form saved",t.title)}catch(t){N(Qe("saveFailed","Could not save"),t instanceof Error?t.message:"","error")}finally{this.saveInFlight=!1;const t=this.queuedSave;this.queuedSave=null,t&&this.save(t.silent)}}async openFormById(e){await this.open(e)}async open(e){if(!(this.dirty&&!await ie("You have unsaved changes. Discard them?"))){try{this.form=await I.getForm(e),this.schema=this.form.schema,this.selected=null,this.dirty=!1,this.history=[],this.historyAt=-1,this.snapshot()}catch(s){this.fail(s);return}this.renderBar(),this.renderCanvas(),this.renderInspector(),this.announceIdentity()}}announceIdentity(){this.form&&Nt(this.root,An(this.form,U?.adminUrl??""))}async showTemplates(){if(!this.config)return;const e=r("div",{class:"atfb-overlay"}),s=i=>{i.key==="Escape"&&t()},t=()=>{e.remove(),document.removeEventListener("keydown",s)},a=r("div",{class:"atfb-templates",children:this.config.templates.map(i=>r("button",{class:"atfb-template",type:"button",on:{click:async()=>{t();try{const o=await I.createForm({template:i.slug});this.forms.unshift({id:o.id,title:o.title,status:o.status,modified:o.modified,fields:o.schema.fields.length,theme:o.schema.settings.theme,entries:0,unread:0,views:0,submissions:0,shortcode:o.shortcode}),this.form=o,this.schema=o.schema,this.selected=null,this.dirty=!1,this.history=[],this.historyAt=-1,this.snapshot(),this.renderBar(),this.renderCanvas(),this.renderInspector(),this.announceIdentity()}catch(o){N("Could not create the form",o instanceof Error?o.message:"","error")}}},children:[B(i.icon),r("strong",{text:i.label}),r("span",{text:i.description})]}))});e.append(r("div",{class:"atfb-modal",attrs:{role:"dialog","aria-label":"Start a new form"},children:[r("h2",{text:"Start a new form"}),a,this.archivedFormsSection(t),r("div",{class:"atfb-modal__actions",children:[C("Cancel",t)]})]})),e.addEventListener("click",i=>{i.target===e&&t()}),document.addEventListener("keydown",s),this.root.append(e),a.querySelector("button")?.focus()}archivedFormsSection(e){const s=r("div",{class:"atfb-archived"});return I.listArchivedForms().then(t=>{t.length&&s.append(r("h3",{class:"atfb-archived__title",text:"Or bring one back from the archive"}),...t.map(a=>r("div",{class:"atfb-archived__row",children:[r("div",{class:"atfb-archived__meta",children:[r("strong",{text:a.title||"(untitled)"}),r("span",{class:"atfb-hint",text:`${a.entries} ${a.entries===1?"entry":"entries"} · ${a.submissions} submissions · ${a.views} views`})]}),C("Restore",async()=>{try{const i=await I.unarchiveForm(a.id);e(),this.forms.unshift(i),N("Form restored",`${i.title||"(untitled)"} is back, with its entries and stats.`),await this.open(i.id)}catch(i){N("Could not restore the form",i instanceof Error?i.message:"","error")}},"secondary","undo")]})))}).catch(()=>{}),s}renderFormsList(){R(this.canvas),this.canvas.append(r("div",{class:"atfb-empty",children:[r("h2",{text:"No forms yet"}),r("p",{text:"Start from a template, or build one from nothing."}),C("New form",()=>void this.showTemplates(),"primary","plus-alt2")]}))}openEntries(){const e=window.wp?.os;if(e?.openWindow){e.openWindow("allterrain-forms-entries");return}window.location.assign(`${U?.adminUrl??""}admin.php?page=allterrain-forms-entries`)}renderPalette(){if(!this.config)return;R(this.palette);const e=new Map;for(const t of this.config.fieldTypes){const a=e.get(t.group)??[];a.push(t),e.set(t.group,a)}const s=r("input",{class:"atfb-input atfb-palette__search",type:"search",placeholder:"Search fields",attrs:{"aria-label":"Search field types"},on:{input:t=>{const a=t.target.value.toLowerCase().trim();this.palette.querySelectorAll(".atfb-chip").forEach(i=>{const o=(i.textContent??"").toLowerCase();i.hidden=a!==""&&!o.includes(a)}),this.palette.querySelectorAll(".atfb-group").forEach(i=>{const o=Array.from(i.querySelectorAll(".atfb-chip")).some(l=>!l.hidden);i.hidden=!o})}}});this.palette.append(s);for(const[t,a]of Object.entries(this.config.groups)){const i=e.get(t);i?.length&&this.palette.append(r("div",{class:"atfb-group",children:[r("h3",{class:"atfb-group__title",text:a}),r("div",{class:"atfb-group__items",children:i.map(o=>this.paletteChip(o))})]}))}}paletteChip(e){const s=r("button",{class:"atfb-chip",type:"button",title:e.description,attrs:{"data-atf-type":e.type},children:[B(e.icon),r("span",{text:e.label})]});return s.addEventListener("pointerdown",t=>{const a=r("div",{class:"atfb-chip atfb-chip--ghost",children:[B(e.icon),r("span",{text:e.label})]});G().start({payload:Ae(se,s,{fieldType:e.type,isNew:!0},t,a),origin:t,onClickOnly:()=>this.addField(e.type)})}),s.addEventListener("click",t=>{G().recentlyEndedDrag()&&t.preventDefault()}),s}renderCanvas(){if(R(this.canvas),!this.schema||!this.form){this.renderFormsList();return}if(this.tab!=="build"){this.canvas.append(this.renderTabCanvas());return}const e=r("div",{class:"atfb-canvas__list",attrs:{"data-atfb-list":""}});this.schema.fields.length||e.append(r("div",{class:"atfb-placeholder",text:Qe("emptyCanvas","Drag a field from the left to begin.")})),this.schema.fields.forEach((t,a)=>{e.append(this.renderFieldCard(t,a))});const s=r("div",{class:"atfb-canvas__inner",children:[r("p",{class:"atfb-shortcode",text:this.form.shortcode,title:"Paste this anywhere to place the form"}),e]});this.canvas.append(s),this.registerCanvasTarget(e),this.paintLogicMap(s),this.paintCanvasTheme()}prefillControl(e,s){const t=e.prefill.startsWith("query:"),a=Je.some(g=>g.value===e.prefill),i=t?"query":a&&e.prefill||(e.prefill?"custom":""),o=r("div",{class:"atfb-prefill__detail"}),l=r("p",{class:"atfb-row__hint atfb-prefill__preview"}),c=g=>{if(o.replaceChildren(),l.replaceChildren(),g==="query"){const u=e.prefill.startsWith("query:")?e.prefill.slice(6):"";o.append(T(u,f=>{const y=f.trim();s("prefill",y?`query:${y}`:""),d(`query:${y}`)},"utm_source"),r("p",{class:"atfb-row__hint",text:"The name of the parameter in the link people arrive on."}))}g==="custom"&&o.append(T(e.prefill,u=>{s("prefill",u),d(u)},"myplugin:something"),r("p",{class:"atfb-row__hint",text:"For a source another plugin has added through alltfo_resolve_prefill."})),d(e.prefill)},d=g=>{if(l.replaceChildren(),!g)return;if(g.startsWith("query:")){const f=g.slice(6);f&&(l.textContent=`A visit to …/your-page/?${f}=abc opens the form with “abc” in it.`);return}const u=Je.find(f=>f.value===g)?.tag;u&&ft(this.form?.id??0).then(f=>{for(const y of f)for(const p of y.items)if(p.tag===u){const x=g.startsWith("user:");if(!p.sample){l.textContent="Empty unless the visitor is signed in.";return}l.textContent=x?`Opens with “${p.sample}” for you — empty for a visitor who is not signed in.`:`Opens with “${p.sample}”.`;return}})};c(i);const h=r("select",{class:"atfb-input atfb-select",on:{change:g=>{const u=g.target.value;s("prefill",u==="query"||u==="custom"?"":u),c(u)}}});h.append(r("option",{value:"",text:"Nothing — leave it empty",attrs:{selected:i===""}}));for(const g of jn){const u=document.createElement("optgroup");u.label=g;for(const f of Je.filter(y=>y.group===g))u.append(r("option",{value:f.value,text:f.label,attrs:{selected:f.value===i}}));h.append(u)}const m=document.createElement("optgroup");return m.label="From the link they arrived on",m.append(r("option",{value:"query",text:"A parameter in the web address",attrs:{selected:i==="query"}})),h.append(m,r("option",{value:"custom",text:"Something else (advanced)",attrs:{selected:i==="custom"}})),w("Pre-fill this with",r("div",{class:"atfb-prefill",children:[h,o,l]}),"What the box already contains when the form opens. They can still change it.")}renderCondition(e,s){const t=s.some(l=>l.kind==="field"&&l.missing),a=r("div",{class:`atfb-cond${t?" is-broken":""}`,attrs:{"aria-label":s.length?qe(s):"Conditional rules"}});a.append(r("div",{class:"atfb-cond__heading",children:[r("span",{class:"atfb-cond__label",children:[B("randomize"),"Conditions"]}),this.copyConditionButton(`field:${e.id}`)]})),e.logic.rules.forEach((l,c)=>{const d=be(l,this.schema?.fields??[],c),h=r("button",{class:"atfb-cond__delete",type:"button",title:"Delete this rule",attrs:{"aria-label":`Delete rule ${c+1}`},children:[B("trash")],on:{click:()=>{this.snapshot(),this.editCondition(e.id,m=>{m.rules.splice(c,1)},!0,`${e.id}:add`),this.snapshot()}}});a.append(r("div",{class:"atfb-cond__rule",children:[c===0?this.renderConditionToken(e,{kind:"verb",text:e.logic.action==="hide"?"Hidden when":"Shown when"}):this.renderConditionToken(e,{kind:"join",text:e.logic.match==="all"?"and":"or"}),...d.map(m=>this.renderConditionToken(e,m,c)),h]}))}),e.logic.rules.length||a.append(r("span",{class:"atfb-hint",text:"No rules yet. Add one or copy a condition."}));const i=r("button",{class:"atfb-cond__add",type:"button",text:"+ Add rule",title:"Add rule",attrs:{"aria-label":"Add rule","data-cond":`${e.id}:add`},on:{click:()=>this.addConditionRule(e.id)}}),o=r("button",{class:"atfb-cond__clear",type:"button",text:"Clear",title:"Delete all rules",attrs:{"aria-label":"Clear all rules",disabled:!e.logic.rules.length},on:{click:()=>{this.snapshot(),this.editCondition(e.id,l=>{l.rules=[]},!0,`${e.id}:add`),this.snapshot()}}});return a.append(r("div",{class:"atfb-cond__actions",children:[i,o]})),a.addEventListener("pointerdown",l=>l.stopPropagation()),a.addEventListener("click",l=>l.stopPropagation()),a.addEventListener("keydown",l=>l.stopPropagation()),a}editCondition(e,s,t=!0,a=""){const i=this.liveField(e)?.logic;if(i){if(s(i),this.markDirty(),!t){this.logicMap?.setEdges(ge(this.schema?.fields??[]));return}this.renderCanvas(),this.renderInspector(),a&&window.requestAnimationFrame(()=>{this.canvas.querySelector(`[data-cond="${CSS.escape(a)}"]`)?.focus()})}}condSelect(e,s,t,a,i){if(Y("os-select")&&Y("os-option")){const o=document.createElement("os-select");o.setAttribute("value",e),o.setAttribute("aria-label",a),o.setAttribute("data-cond",t),o.className="atfb-cond__control",o.setAttribute("plain",""),o.title=a;for(const l of s){const c=document.createElement("os-option");c.setAttribute("value",l.value),l.disabled&&c.setAttribute("disabled",""),c.textContent=l.label,o.append(c)}return o.addEventListener("os-pick",l=>{i(String(l.detail?.value??""))}),o}return r("select",{class:"atfb-cond__control atfb-cond__control--native",title:a,attrs:{"aria-label":a,"data-cond":t},on:{change:o=>i(o.target.value)},children:s.map(o=>r("option",{value:o.value,text:o.label,attrs:{selected:o.value===e,disabled:o.disabled}}))})}renderConditionToken(e,s,t=0){if(s.kind==="field"){const a=`${e.id}:source:${t}`,i=(this.schema?.fields??[]).filter(o=>o.id!==e.id&&o.type!=="page_break");return this.condSelect(s.fieldId,[...s.missing?[{value:s.fieldId,label:"Choose a question…"}]:[],...i.map(o=>({value:o.id,label:o.label||o.id}))],a,"Question used by this rule",o=>this.editCondition(e.id,l=>{const c=l.rules[t];c&&(c.field=o,c.value="")},!0,a))}if(s.kind==="verb")return r("button",{class:"atfb-cond__verb",type:"button",text:s.text,title:"Switch between showing and hiding this field when the condition matches.",attrs:{"data-cond":`${e.id}:verb`},on:{click:()=>this.editCondition(e.id,a=>{a.action=a.action==="hide"?"show":"hide"},!0,`${e.id}:verb`)}});if(s.kind==="join")return r("button",{class:"atfb-cond__join",type:"button",text:s.text,title:"Switch between needing every rule (and) or any one of them (or).",attrs:{"data-cond":`${e.id}:join`},on:{click:()=>this.editCondition(e.id,a=>{a.match=a.match==="all"?"any":"all"},!0,`${e.id}:join`)}});if(s.kind==="operator"){const a=`${e.id}:op:${s.ruleIndex}`;return this.condSelect(s.operator,Object.entries(Ie).map(([i,o])=>({value:i,label:o})),a,"How the answer is compared",i=>this.editCondition(e.id,o=>{const l=o.rules[s.ruleIndex];l&&(l.operator=i)},!0,a))}return s.kind==="value"?this.renderConditionValue(e,s):r("span")}renderConditionValue(e,s){const t=`${e.id}:value:${s.ruleIndex}`,a=this.schema?.fields.find(d=>d.id===s.sourceId),i=(d,h)=>this.editCondition(e.id,m=>{const g=m.rules[s.ruleIndex];g&&(g.value=d)},h,t),o=it(a,s.raw,this.config?.countries);if(o){const d=this.condSelect(s.raw,o,t,"The answer that triggers this",h=>i(h,!0));return d.classList.add("atfb-cond__value-select"),d.removeAttribute("plain"),d}const l=["number","range","scale","rating","total"].includes(a?.type??""),c=r("input",{class:"atfb-cond__chip atfb-cond__chip--value atfb-cond__value",value:s.raw,title:"The answer that triggers this. Edit it here.",attrs:{type:"text","aria-label":"The answer that triggers this","data-cond":t,inputmode:l?"decimal":void 0,size:String(Math.max(2,Math.min(24,s.raw.length||2)))}});return c.addEventListener("input",()=>{c.size=Math.max(2,Math.min(24,c.value.length||2)),i(c.value,!1)}),c.addEventListener("change",()=>i(c.value,!0)),c.addEventListener("keydown",d=>{d.key==="Enter"&&(d.preventDefault(),i(c.value,!0))}),c}section(e,s,t,a=!1){const i=r("details",{class:"atfb-section",attrs:{open:this.openSections.get(e)??a},children:[r("summary",{text:s}),...t]});return i.addEventListener("toggle",()=>this.openSections.set(e,i.open)),i}logicMapButton(){const e=ge(this.schema?.fields??[]).length>0,s=C(this.logicMapOn?"Hide logic":"Show logic",()=>{this.logicMapOn=!this.logicMapOn,ms(Vt,this.logicMapOn?"on":"off"),this.renderBar(),this.renderCanvas()},this.logicMapOn?"primary":"secondary","randomize");return s.title="Draw a line from each question to the ones it decides.",s.hidden=!e,s}paintLogicMap(e){this.logicMap?.destroy(),this.logicMap=null;const s=this.schema?.fields??[],t=ge(s);if(!t.length||!this.logicMapOn)return;e.classList.add("has-logicmap");const a=new As(e);a.setEdges(t),a.highlight(this.selected??""),this.logicMap=a,e.addEventListener("pointerover",i=>{const o=i.target.closest("[data-atfb-card]");a.highlight(o?.dataset.atfbCard??this.selected??"")}),e.addEventListener("pointerleave",()=>a.highlight(this.selected??""))}renderFieldCard(e,s){const t=this.config?.fieldTypes.find(d=>d.type===e.type),a=this.selected===e.id,i=this.schema?.fields??[],o=Es(e,i),l=$s(i).get(e.id)??0,c=r("div",{class:`atfb-card${a?" is-selected":""}`,attrs:{"data-atfb-card":e.id,"data-index":s,tabindex:"0",role:"button","aria-pressed":a,"aria-label":`${e.label||t?.label||e.type}, ${s+1} of ${this.schema?.fields.length??0}`},children:[r("div",{class:"atfb-card__bar",children:[r("div",{class:"atfb-card__grip",attrs:{"aria-hidden":"true"},children:[B("menu")]}),r("div",{class:"atfb-card__head",children:[B(t?.icon??"dashicons-forms"),r("span",{class:"atfb-card__type",text:t?.label??e.type}),this.requiredToggle(e),e.type!=="page_break"?this.conditionToolbar(e):null,l?r("span",{class:"atfb-badge atfb-badge--controls",text:l===1?"controls 1 field":`controls ${l} fields`,title:"Other questions appear or disappear based on this answer."}):null]}),r("div",{class:"atfb-card__actions",children:[this.cardAction("admin-page","Duplicate",()=>this.duplicateField(e.id)),this.cardAction("trash","Delete",()=>void this.deleteField(e.id))]})]}),r("div",{class:"atfb-card__body",children:[pt(e,t,{edit:d=>{const h=this.liveField(e.id);if(!h)return;d(h),this.markDirty(),this.syncInspector(h);const m=this.selected?this.locateField(this.selected):void 0;m?.parent&&m.parent.id===h.id&&this.syncInspector(m.field)},restructure:d=>{const h=this.liveField(e.id);h&&(this.snapshot(),d(h),this.markDirty(),this.renderCanvas(),this.renderInspector())},types:d=>this.config?.fieldTypes.find(h=>h.type===d),selectedId:this.selected}),e.logic.enabled?this.renderCondition(e,o):null]})]});return c.addEventListener("click",d=>{d.target.closest(".atfb-card__actions")||G().recentlyEndedDrag()||this.selectField(e.id)}),c.addEventListener("keydown",d=>{if(d.key==="Enter"||d.key===" "){d.preventDefault(),this.selectField(e.id);return}d.altKey&&(d.key==="ArrowUp"||d.key==="ArrowDown")&&(d.preventDefault(),this.moveField(e.id,d.key==="ArrowUp"?s-1:s+1),window.requestAnimationFrame(()=>{this.canvas.querySelector(`[data-atfb-card="${CSS.escape(e.id)}"]`)?.focus()})),(d.key==="Delete"||d.key==="Backspace")&&(d.preventDefault(),this.deleteField(e.id))}),c.addEventListener("pointerdown",d=>{d.target.closest(".atfb-card__actions")||G().start({payload:Ae(se,c,{fieldId:e.id,field:e,isNew:!1},d),origin:d})}),c}addConditionRule(e){this.snapshot(),this.editCondition(e,s=>{const t=this.schema?.fields.find(a=>a.id!==e&&a.type!=="page_break");s.rules.push({field:t?.id??"",operator:"is",value:""})},!0,`${e}:source:${this.liveField(e)?.logic.rules.length??0}`),this.snapshot()}conditionToolbar(e){const s=r("button",{class:`atfb-req atfb-condition-toggle${e.logic.enabled?" is-on":""}`,type:"button",title:e.logic.enabled?"Edit conditional logic":"Set up conditional logic",attrs:{"aria-haspopup":"dialog","data-cond":`${e.id}:enabled`},children:[r("span",{class:"atfb-condition-dot",attrs:{"aria-hidden":"true"}}),r("span",{text:"Conditional"})],on:{click:()=>this.openConditionEditor(e.id)}});for(const t of["pointerdown","click","keydown"])s.addEventListener(t,a=>a.stopPropagation());return s}openConditionEditor(e){const s=this.liveField(e);if(!s)return;const t={...s.logic,rules:s.logic.rules.map(d=>({...d}))},a=r("div",{class:"atfb-overlay"}),i=r("div",{class:"atfb-modal atfb-condition-editor",attrs:{role:"dialog","aria-modal":"true","aria-label":"Conditional logic",tabindex:"-1"}}),o=()=>{a.remove(),this.root.querySelector(`[data-cond="${CSS.escape(e)}:enabled"]`)?.focus()},l="button:not([disabled]), input, select, os-select, os-checkbox-label, os-button:not([disabled])",c=()=>{const d=[...i.querySelectorAll(l)].indexOf(document.activeElement),h=(f,y=!1)=>{f(t),y&&c()},m=D(t.action,[{value:"show",label:"Show"},{value:"hide",label:"Hide"}],f=>{t.action=f});m.setAttribute("aria-label","Conditional action");const g=D(t.match,[{value:"all",label:"all"},{value:"any",label:"any"}],f=>{t.match=f,c()});g.setAttribute("aria-label","Match rules");const u=r("button",{class:"atfb-copy-condition",type:"button",children:[B("admin-page"),r("span",{text:"Copy condition"})],on:{click:()=>Dt({root:a,to:`field:${e}`,schema:()=>this.schema?{...this.schema,fields:this.schema.fields.map(f=>f.id===e?{...f,logic:t}:f)}:null,onCopy:()=>{c(),i.focus()}})}});if(i.replaceChildren(r("div",{class:"atfb-condition-editor__heading",children:[r("span",{class:"atfb-condition-editor__icon",children:[B("randomize")]}),r("div",{children:[r("h2",{text:"Conditional logic"}),r("p",{text:s.label||"Untitled field"})]})]}),r("div",{class:"atfb-condition-editor__switch",children:[q("Enable conditions",t.enabled,f=>{t.enabled=f,c()}),r("p",{text:"Choose when this field appears in your form."})]}),...t.enabled?[r("div",{class:"atfb-condition-editor__sentence",children:[m,"this field when",g,"of these rules match:"]}),...this.logicRulesEditor(t,h,e)]:[r("p",{class:"atfb-condition-editor__empty",text:"This field is always visible. Enable conditions to choose when to show or hide it."})],r("div",{class:"atfb-condition-editor__footer",children:[u,r("div",{class:"atfb-modal__actions",children:[C("Cancel",o),C("Save conditions",()=>{if(!this.liveField(e)){o();return}this.snapshot(),this.editCondition(e,f=>Object.assign(f,t,{rules:t.rules.map(y=>({...y}))})),this.snapshot(),o()},"primary")]})]})),d>=0){const f=i.querySelectorAll(l);f[Math.min(d,f.length-1)]?.focus()}};a.append(i),a.addEventListener("click",d=>{d.target===a&&o()}),a.addEventListener("keydown",d=>{if(d.key==="Escape"&&(d.preventDefault(),o()),d.key==="Tab"){const h=[...i.querySelectorAll(l)],m=h[0],g=h[h.length-1];d.shiftKey&&(document.activeElement===m||document.activeElement===i)?(d.preventDefault(),g?.focus()):!d.shiftKey&&document.activeElement===g&&(d.preventDefault(),m?.focus())}d.stopPropagation()}),this.root.append(a),c(),i.focus()}requiredToggle(e){return r("button",{class:`atfb-req${e.required?" is-on":""}`,type:"button",text:e.required?"Required":"Optional",title:e.required?"This must be answered. Click to make it optional.":"Click to make this required.",attrs:{"aria-pressed":e.required?"true":"false"},on:{pointerdown:t=>t.stopPropagation(),click:t=>{t.stopPropagation(),e.required=!e.required,this.markDirty(),this.renderCanvas(),this.renderInspector()}}})}cardAction(e,s,t){return r("button",{class:"atfb-card__action",type:"button",title:s,attrs:{"aria-label":s},on:{click:a=>{a.stopPropagation(),t()}},children:[B(e)]})}gapAnimator(e,s,t,a){const i=r("div",{class:"atfb-marker",attrs:{"aria-hidden":"true"}});let o=null,l=null,c=0;const d=()=>{i.remove(),l?.classList.remove("atfb-lifted"),l=null,o=null},h=u=>{const f=Array.from(e.querySelectorAll(s)),y=new Map(f.filter(x=>!x.classList.contains("atfb-lifted")).map(x=>[x,x.getBoundingClientRect().top]));u();for(const x of f)x.style.transition="none",x.style.transform="";const p=f.filter(x=>{const F=y.get(x);if(F===void 0||!x.isConnected||x.classList.contains("atfb-lifted"))return!1;const S=F-x.getBoundingClientRect().top;return Math.abs(S)<.5?!1:(x.style.transform=`translateY(${S}px)`,!0)});e.offsetHeight;for(const x of f){if(!p.includes(x)){x.style.transition="";continue}x.style.transition="transform 160ms ease",x.style.transform="",x.addEventListener("transitionend",()=>{x.style.transition=""},{once:!0})}},m=u=>{const f=u.detail;if(f?.payload?.type!==se||!e.classList.contains("is-dropping")||a&&!a(f.payload.data??{}))return;const y=f.payload.data?.fieldId,p=typeof y=="string"?t(y):null,x=st(e,s,f.clientY??0,p??void 0);x===o&&i.isConnected||(o=x,h(()=>{p&&p!==l&&(l?.classList.remove("atfb-lifted"),p.classList.add("atfb-lifted"),l=p),i.style.blockSize=`${Math.max(8,Math.round(c))}px`;const F=Array.from(e.querySelectorAll(s)).filter(S=>S!==p);x>=F.length?e.append(i):F[x].before(i)}))},g=()=>{e.classList.remove("is-dropping"),window.setTimeout(()=>h(d),0)};return document.addEventListener("os.drag.move",m),document.addEventListener("os.drag.end",g),{enter:u=>{const f=u?t(u):null;c=f&&!f.classList.contains("atfb-lifted")?f.getBoundingClientRect().height:c||44},leave:()=>h(d),drop:(u,f)=>{const y=o??st(e,s,u,f??void 0);return d(),y},teardown:()=>{document.removeEventListener("os.drag.move",m),document.removeEventListener("os.drag.end",g),d()}}}registerCanvasTarget(e){this.canvasTarget?.(),this.canvasTarget=null;const s=this.gapAnimator(e,".atfb-card",i=>this.canvas.querySelector(`[data-atfb-card="${CSS.escape(i)}"]`)),t=G().registerDropTarget({id:`atfb-canvas-${this.form?.id??0}`,element:e,accept:i=>i.type===se,onEnter:i=>{e.classList.add("is-dropping"),s.enter(i.payload.data.fieldId)},onLeave:()=>{e.classList.remove("is-dropping"),s.leave()},onDrop:(i,o)=>{e.classList.remove("is-dropping");const l=i.payload.data,c=l.fieldId?this.canvas.querySelector(`[data-atfb-card="${CSS.escape(l.fieldId)}"]`):null,d=s.drop(o.clientY,c);if(l.isNew&&l.fieldType){this.addField(l.fieldType,d);return}if(l.fieldId&&this.locateField(l.fieldId)){this.relocateField(l.fieldId,null,d);return}l.field&&this.insertField({...l.field,id:""},d)}}),a=this.wireRepeaterZones(e);this.canvasTarget=()=>{t(),a.forEach(i=>i()),s.teardown()}}wireRepeaterZones(e){const s=[];return e.querySelectorAll("[data-atfb-repeater-zone]").forEach(t=>{const a=t.dataset.atfbRepeaterZone??"",i=l=>{const c=l;if(c.fieldId===a)return!1;const d=c.isNew?c.fieldType:this.locateField(c.fieldId??"")?.field.type??c.field?.type;return!!d&&this.allowedInRepeater(d)},o=this.gapAnimator(t,".atfb-subcard",l=>e.querySelector(`[data-atfb-subfield="${CSS.escape(l)}"]`),i);s.push(()=>o.teardown()),s.push(G().registerDropTarget({id:`atfb-repzone-${this.form?.id??0}-${a}`,element:t,accept:l=>l.type===se&&i(l.data),onEnter:l=>{t.classList.add("is-dropping"),o.enter(l.payload.data.fieldId)},onLeave:()=>{t.classList.remove("is-dropping"),o.leave()},onDrop:(l,c)=>{t.classList.remove("is-dropping");const d=l.payload.data,h=d.fieldId?e.querySelector(`[data-atfb-subfield="${CSS.escape(d.fieldId)}"]`):null,m=o.drop(c.clientY,h);if(d.isNew&&d.fieldType){this.addFieldToRepeater(d.fieldType,a,m);return}if(d.fieldId&&this.locateField(d.fieldId)){this.relocateField(d.fieldId,a,m);return}d.field&&this.insertFieldIntoRepeater({...d.field,id:""},a,m)}})),t.querySelectorAll(".atfb-subcard").forEach(l=>{const c=l.dataset.atfbSubfield??"";l.addEventListener("click",d=>{d.target.closest(".atfb-preview__remove")||(d.stopPropagation(),!G().recentlyEndedDrag()&&this.selectField(c))}),l.addEventListener("keydown",d=>{if(d.stopPropagation(),d.key==="Enter"||d.key===" "){d.preventDefault(),this.selectField(c);return}if(d.altKey&&(d.key==="ArrowUp"||d.key==="ArrowDown")){d.preventDefault();const h=this.locateField(c);h&&(this.relocateField(c,a,d.key==="ArrowUp"?h.index-1:h.index+1),window.requestAnimationFrame(()=>{this.canvas.querySelector(`[data-atfb-subfield="${CSS.escape(c)}"]`)?.focus()}));return}(d.key==="Delete"||d.key==="Backspace")&&(d.preventDefault(),this.deleteField(c))}),l.addEventListener("pointerdown",d=>{if(d.stopPropagation(),d.target.closest(".atfb-preview__remove"))return;const h=this.locateField(c);h&&G().start({payload:Ae(se,l,{fieldId:c,parentId:a,field:h.field,isNew:!1},d),origin:d})})})}),s}addField(e,s){const t=this.buildField(e);t&&this.insertField(t,s)}buildField(e){const s=this.config?.fieldTypes.find(a=>a.type===e);return!s||!this.schema?void 0:{id:this.nextFieldId(),type:e,label:s.input?s.label:"",placeholder:"",hint:"",required:!1,width:"full",cssClass:"",default:"",choices:s.choices?[{label:"First choice",value:"first"},{label:"Second choice",value:"second"}]:[],logic:{enabled:!1,action:"show",match:"all",rules:[]},messages:{},prefill:"",...s.settings}}allowedInRepeater(e){const s=this.config?.fieldTypes.find(t=>t.type===e);return!s||!s.input?!1:!["repeater","page_break","file","signature","total"].includes(e)}addFieldToRepeater(e,s,t){if(!this.allowedInRepeater(e))return;const a=this.buildField(e);a&&this.insertFieldIntoRepeater(a,s,t)}insertFieldIntoRepeater(e,s,t){const a=this.liveField(s);if(!this.schema||!a||a.type!=="repeater")return;e.id||(e.id=this.nextFieldId()),this.snapshot(),Array.isArray(a.fields)||(a.fields=[]);const i=a.fields,o=t===void 0?i.length:Math.max(0,Math.min(t,i.length));i.splice(o,0,e),this.selected=e.id,this.markDirty(),this.renderCanvas(),this.renderInspector(),window.requestAnimationFrame(()=>{this.canvas.querySelector(`[data-atfb-subfield="${CSS.escape(e.id)}"]`)?.focus()})}relocateField(e,s,t){if(!this.schema)return;const a=this.locateField(e);if(!a)return;if(!a.parent&&!s){this.moveField(e,t);return}const i=s?this.liveField(s):null;if(s&&(!i||i.type!=="repeater"||e===s))return;i&&!Array.isArray(i.fields)&&(i.fields=[]);const o=i?i.fields:this.schema.fields;this.snapshot(),a.list.splice(a.index,1);const l=Math.max(0,Math.min(t,o.length));o.splice(l,0,a.field),this.selected=a.field.id,this.markDirty(),this.renderCanvas(),this.renderInspector()}insertField(e,s){if(!this.schema)return;e.id||(e.id=this.nextFieldId()),this.snapshot();const t=s===void 0?this.schema.fields.length:Math.max(0,Math.min(s,this.schema.fields.length));this.schema.fields.splice(t,0,e),this.selected=e.id,this.markDirty(),this.renderCanvas(),this.renderInspector(),window.requestAnimationFrame(()=>{this.canvas.querySelector(`[data-atfb-card="${CSS.escape(e.id)}"]`)?.focus()})}moveField(e,s){if(!this.schema)return;const t=Zt(this.schema.fields,e,s);if(!t)return;this.snapshot();const[a]=this.schema.fields.splice(t.from,1);this.schema.fields.splice(t.to,0,a),this.markDirty(),this.renderCanvas()}duplicateField(e){if(!this.schema)return;const s=this.locateField(e);if(!s)return;const t=JSON.parse(JSON.stringify(s.field)),a=this.usedFieldIds(),i=()=>{let o=a.size+1;for(;a.has(`f${o}`);)o++;const l=`f${o}`;return a.add(l),l};t.id=i();for(const o of t.fields??[])o.id=i();if(s.parent){this.snapshot(),s.list.splice(s.index+1,0,t),this.selected=t.id,this.markDirty(),this.renderCanvas(),this.renderInspector();return}this.insertField(t,s.index+1)}async deleteField(e){if(!this.schema)return;const s=this.locateField(e);if(!s)return;const t=this.schema.fields.filter(i=>i.logic?.rules?.some(o=>o.field===e)),a=t.length?`Delete this field? ${t.length} other field${t.length===1?"":"s"} use it in a condition, and those conditions will stop working.`:Qe("confirmDelete","Delete this? It cannot be undone.");await ie(a,"Delete field")&&(this.snapshot(),s.list.splice(s.index,1),this.selected===e&&(this.selected=null),this.markDirty(),this.renderCanvas(),this.renderInspector())}selectField(e){this.selected=e;for(const s of this.canvas.querySelectorAll("[data-atfb-card]")){const t=s.dataset.atfbCard===e;s.classList.toggle("is-selected",t),s.setAttribute("aria-pressed",t?"true":"false")}for(const s of this.canvas.querySelectorAll("[data-atfb-subfield]"))s.classList.toggle("is-selected",s.dataset.atfbSubfield===e);this.logicMap?.highlight(e),this.renderInspector()}nextFieldId(){const e=this.usedFieldIds();let s=e.size+1;for(;e.has(`f${s}`);)s++;return`f${s}`}usedFieldIds(){const e=new Set;for(const s of this.schema?.fields??[]){e.add(s.id);for(const t of s.fields??[])e.add(t.id)}return e}nextEntryId(e,s){const t=new Set(s.map(i=>i.id));let a=s.length+1;for(;t.has(`${e}${a}`);)a++;return`${e}${a}`}renderTypeSettings(e,s,t,a){for(const i of t){const o=Kt[i];o&&(this.inspector.append(Gt(e,o,a)),o.also&&this.inspector.append(Gt(e,{...o,key:o.also.key,label:o.also.label,hint:o.also.hint},a)))}t.includes("rows")&&e.type!=="likert"&&this.inspector.append(w("Lines tall",V(String(e.rows??""),i=>a("rows",i)))),t.includes("rows")&&e.type==="likert"&&this.inspector.append(this.renderStatementList(e,a)),t.includes("parts")&&this.inspector.append(this.renderPartsEditor(e,s,a)),t.includes("default")&&!["files","object"].includes(s?.value??"")&&this.inspector.append(this.renderDefaultControl(e,s,a))}renderStatementList(e,s){const t=Array.isArray(e.rows)?e.rows:[];return w("Statements",fe(t.map(a=>a.label??"").join(` `),a=>s("rows",Xt(t,a)),5),"One per line. Each becomes a row of the matrix.")}renderPartsEditor(e,s,t){const a=s?.parts??[],i=Array.isArray(e.parts)?e.parts:a.map(l=>l.key),o=a.map(l=>q(l.label,i.includes(l.key),c=>{const d=a.map(h=>h.key).filter(h=>h===l.key?c:i.includes(h));t("parts",d.length?d:[l.key])}));return r("div",{class:"atfb-section",children:[r("h4",{class:"atfb-section__title",text:"Parts to ask for"}),...o]})}renderDefaultControl(e,s,t){const a="Filled in before they start. They can change it.";return e.type==="switch"||e.type==="consent"?q("On by default",!!e.default,i=>t("default",i)):s?.choices&&(e.choices??[]).length?w("Default answer",D(String(e.default??""),[{value:"",label:"Nothing chosen"},...(e.choices??[]).map(i=>({value:String(i.value),label:i.label||String(i.value)}))],i=>t("default",i)),a):w("Default answer",T(String(e.default??""),i=>t("default",i)),a)}renderChoicesEditor(e,s){const t=e.choices??[],a=o=>this.liveField(e.id)?.choices?.[o],i=r("div",{class:"atfb-choices"});return t.forEach((o,l)=>{const c=r("div",{class:"atfb-choice-row",children:[e.type==="image_choice"?this.choiceImageWell(o,l,e):null,J(T(o.label,d=>{const h=a(l);if(!h)return;const m=!h.value||h.value===h.label;h.label=d,m&&(h.value=d),this.markDirty();const g=this.liveField(e.id);g&&this.syncCanvas(g)}),`choice:${l}:label`),J(T(o.value,d=>{const h=a(l);h&&(h.value=d,this.markDirty())},"value"),`choice:${l}:value`),e.type==="quiz"||o.points!==void 0?V(String(o.points??""),d=>{const h=a(l);h&&(h.points=d===""?void 0:Number(d),this.markDirty())}):V(String(o.price??""),d=>{const h=a(l);h&&(h.price=d===""?void 0:Number(d),this.markDirty())}),r("button",{class:"atfb-card__action",type:"button",attrs:{"aria-label":`Remove ${o.label}`},on:{click:()=>{const d=this.liveField(e.id);d&&((d.choices??[]).splice(l,1),s("choices",d.choices),this.renderInspector())}},children:[B("trash")]})]});i.append(c)}),r("div",{class:"atfb-section",children:[r("h4",{text:"Choices"}),r("p",{class:"atfb-hint",text:e.type==="quiz"?"Label, value, points.":"Label, value, and a price for calculations."}),i,C("Add choice",()=>{const o=this.liveField(e.id);if(!o)return;const l=o.choices??[];l.push({label:"",value:""}),s("choices",l),this.renderInspector()},"ghost","plus-alt2"),e.type==="quiz"?w("Correct answer",D(String(e.correct??""),[{value:"",label:"—"},...t.map(o=>({value:o.value,label:o.label}))],o=>s("correct",o))):null]})}choiceImageWell(e,s,t){const a=r("div",{class:`atfb-well${e.image?" has-image":""}`,attrs:{"data-choice":s,"aria-label":`Image for ${e.label||`choice ${s+1}`}`},children:[e.image?r("span",{class:"atfb-well__id",text:`#${e.image}`}):B("format-image")]}),i=G().registerDropTarget({id:`atfb-well-${t.id}-${s}`,element:a,accept:o=>Bn.includes(o.type),onEnter:()=>a.classList.add("is-dropping"),onLeave:()=>a.classList.remove("is-dropping"),onDrop:o=>{a.classList.remove("is-dropping");const l=o.payload.data,c=Number(l.attachmentId??l.id??l.file?.id??0);if(!c){N("That is not an image this field can use","","error");return}const d=this.liveField(t.id)?.choices?.[s];d&&(d.image=c,this.markDirty(),this.renderInspector())}});return this.teardowns.push(i),a.addEventListener("click",()=>{const o=this.liveField(t.id)?.choices?.[s];o?.image&&(o.image=void 0,this.markDirty(),this.renderInspector())}),a}renderValidationSection(e,s,t){const a=[],i=[];s.includes("minlength")&&i.push(["minlength","Minimum characters"],["maxlength","Maximum characters"]),s.includes("min")?i.push(["min","Minimum"],["max","Maximum"]):s.includes("max")&&i.push(["max","Highest"]),s.includes("step")&&i.push(["step","Steps of"]),s.includes("mindate")&&i.push(["minDate","Earliest date"],["maxDate","Latest date"]),s.includes("mintime")&&i.push(["minTime","Earliest time"],["maxTime","Latest time"]);for(const[l,c]of i)a.push(w(c,T(String(e[l]??""),d=>t(l,d))));s.includes("pattern")&&a.push(...this.renderAnswerShapeRows(e,t)),s.includes("unique")&&a.push(q("No two people may submit the same value",!!e.unique,l=>t("unique",l)));const o=e.messages??{};return s.includes("required")&&a.push(w("Message when required",T(o.required??"",l=>{o.required=l,t("messages",o)}),"Leave empty for the default wording.")),a.length?this.section(`validation:${e.id}`,"Validation",a):r("div",{class:"atfb-section is-empty"})}buildShapePicker(e,s){const t=[{heading:null,options:[{value:"",label:"Anything at all"}]},...On.map(i=>({heading:i,options:Wt.filter(o=>o.group===i).map(o=>({value:o.slug,label:o.label}))})),{heading:"Your own",options:[{value:"custom",label:"A custom rule…"}]}];if(Y("os-select")&&Y("os-option")){const i=document.createElement("os-select");i.setAttribute("value",e),i.setAttribute("aria-label","What the answer should look like"),i.classList.add("atfb-field");for(const o of t){if(o.heading){const l=document.createElement("os-option");l.setAttribute("value",`__heading:${o.heading}`),l.setAttribute("disabled",""),l.textContent=o.heading,i.append(l)}for(const l of o.options){const c=document.createElement("os-option");c.setAttribute("value",l.value),c.textContent=l.label,i.append(c)}}return i.addEventListener("os-pick",o=>{s(String(o.detail?.value??""))}),i}const a=r("select",{class:"atfb-input atfb-select",attrs:{"aria-label":"What the answer should look like"},on:{change:i=>s(i.target.value)}});for(const i of t){const o=i.heading?(()=>{const l=document.createElement("optgroup");return l.label=i.heading,a.append(l),l})():a;for(const l of i.options)o.append(r("option",{value:l.value,text:l.label,attrs:{selected:l.value===e}}))}return a}renderAnswerShapeRows(e,s){const a=(typeof e.validation=="string"?e.validation:"")||(e.pattern?"custom":""),i=()=>In({root:this.root,field:this.liveField(e.id)??e,onSave:h=>{s("validation","custom"),s("pattern",h.pattern),s("validationRecipe",JSON.stringify(h.recipe));const m={...this.liveField(e.id)?.messages??{}};m.invalid=h.message,s("messages",m),this.renderInspector()},onCancel:()=>this.renderInspector()}),o=h=>{if(h==="custom"){i();return}s("validation",h),s("pattern",""),s("validationRecipe",""),this.renderInspector()},l=this.buildShapePicker(a,o),c=Nn(a),d=[w("The answer should be",l,c?`e.g. ${c.example}`:"Checked as they type, and again on the server.")];if(a==="custom"){const h=Pt(String(e.validationRecipe??"")),m=Bt(h)||(e.pattern?`Matches the expression ${String(e.pattern)}`:"No rule yet — open the builder.");d.push(w("Your rule",r("div",{class:"atfb-valrule",children:[r("p",{class:"atfb-valrule__words",text:m}),C("Edit the rule…",i,"secondary","edit")]}),Ce(h)||e.pattern?"Built in the rule builder, with a playground to test it.":void 0))}return d}logicRulesEditor(e,s,t=""){const a=(this.schema?.fields??[]).filter(d=>d.id!==t&&d.type!=="page_break"),i=()=>r("div",{class:"atfb-rule-join",children:[r("button",{class:"atfb-rule-join__chip",type:"button",text:e.match==="all"?"and":"or",title:"Switch between needing every rule (and) or any one of them (or).",on:{click:()=>s(d=>{d.match=d.match==="all"?"any":"all"},!0)}})]}),o=(d,h)=>{const m=r("button",{class:"atfb-card__action",type:"button",attrs:{"aria-label":"Remove this rule"},title:"Remove this rule",on:{click:()=>s(u=>{u.rules.splice(h,1)},!0)},children:[B("trash")]}),g=[r("div",{class:"atfb-rule__top",children:[D(d.field,a.map(u=>({value:u.id,label:u.label||u.id})),u=>s(f=>{const y=f.rules[h];y&&(y.field=u,y.value="")},!0)),m]}),D(d.operator,Object.entries(this.config?.operators??{}).map(([u,f])=>({value:u,label:f})),u=>s(f=>{const y=f.rules[h];y&&(y.operator=u)},!0))];if(!De.includes(d.operator)){const u=this.schema?.fields.find(y=>y.id===d.field),f=it(u,d.value,this.config?.countries);f?g.push(D(d.value,f,y=>s(p=>{const x=p.rules[h];x&&(x.value=y)}))):g.push(T(d.value,y=>s(p=>{const x=p.rules[h];x&&(x.value=y)}),"The answer to compare against"))}return g[0].querySelector("select, os-select")?.setAttribute("aria-label","Question used by this rule"),g[1].setAttribute("aria-label","How the answer is compared"),g[2]?.setAttribute("aria-label","The answer that triggers this"),g[2]?.matches("select, os-select")&&g[2].classList.add("atfb-cond__value-select"),r("div",{class:"atfb-rule",children:g})},l=r("div",{class:"atfb-rules"});e.rules.forEach((d,h)=>{h>0&&l.append(i()),l.append(o(d,h))});const c=C("Add rule",()=>{s(d=>{d.rules.push({field:a[0]?.id??"",operator:"is",value:""})},!0)},"ghost","plus-alt2");return[l,c]}conditionsSection(e,s,t){const a=(o,l=!1)=>{o(t),this.markDirty(),l&&this.renderCanvas()},i=s==="confirmation"?"Use":"Send";return this.section(`conditions:${e}`,"Conditions",[this.copyConditionButton(`${s}:${e}`),q(`Only ${i.toLowerCase()} this ${s} sometimes`,t.enabled,o=>a(l=>{l.enabled=o},!0)),t.enabled?r("div",{children:[r("div",{class:"atfb-rule-head",children:[r("span",{text:`${i} it when`}),D(t.match,[{value:"all",label:"all"},{value:"any",label:"any"}],o=>a(l=>{l.match=o},!0)),r("span",{text:"of these match:"})]}),...this.logicRulesEditor(t,a)]}):null],t.enabled)}copyConditionButton(e){return r("button",{class:"atfb-copy-condition",type:"button",children:[B("admin-page"),r("span",{text:"Copy condition"})],on:{click:()=>Dt({root:this.root,schema:()=>this.schema,to:e,beforeCopy:()=>this.snapshot(),onCopy:()=>{this.snapshot(),this.markDirty(),this.renderCanvas(),this.renderInspector()}})}})}renderLogicSection(e){const s=e.logic,t=()=>this.liveField(e.id)?.logic,a=(o,l=!1)=>{const c=t();c&&(o(c),this.markDirty(),this.renderCanvas(),l&&this.renderInspector())},i=this.logicRulesEditor(s,a,e.id);return this.section(`logic:${e.id}`,"Conditional logic",[this.copyConditionButton(`field:${e.id}`),q("Only show this field sometimes",s.enabled,o=>{a(l=>{l.enabled=o},!0)}),s.enabled?r("div",{children:[r("div",{class:"atfb-rule-head",children:[D(s.action,[{value:"show",label:"Show"},{value:"hide",label:"Hide"}],o=>a(l=>{l.action=o},!0)),r("span",{text:"this field when"}),D(s.match,[{value:"all",label:"all"},{value:"any",label:"any"}],o=>a(l=>{l.match=o},!0)),r("span",{text:"of these match:"})]}),...i]}):null],s.enabled)}renderTabCanvas(){return this.schema?this.tab==="theme"?yt({themes:this.themes,tokens:this.config?.tokens??[],activeSlug:this.schema.settings.theme,overrides:this.schema.settings.themeOverrides,onTheme:e=>{this.schema.settings.theme=e,this.markDirty()},onOverride:(e,s)=>{s===""?delete this.schema.settings.themeOverrides[e]:this.schema.settings.themeOverrides[e]=s,this.markDirty()},onOverridesReplaced:e=>{this.schema.settings.themeOverrides={...e},this.markDirty()},previewFor:(e,s)=>this.previewHtml(e,s),onThemesChanged:e=>{this.themes=e}}):this.tab==="settings"?this.renderSettingsPane():this.tab==="notify"?this.renderNotificationsPane():this.renderConfirmationsPane():r("div")}async paintCanvasTheme(){if(!this.form||!this.schema)return;const e=this.schema.settings.theme,s=JSON.stringify([e,this.schema.settings.themeOverrides]);if(s!==this.canvasThemeSignature){this.canvasThemeSignature=s;try{const t=await this.previewHtml(e,this.schema.settings.themeOverrides??{});this.root.classList.toggle("atfb--dark-form",/atf-is-dark/.test(t));const a=/class="atf-form-wrap"[^>]*\sstyle="([^"]*)"/.exec(t);if(!a)return;const i=a[1].replace(/"/g,'"').replace(/�?39;/g,"'").replace(/&/g,"&");this.canvasTheme.textContent=`.atfb .atfb-preview { ${i} -}`,this.canvasTheme.isConnected||this.root.append(this.canvasTheme)}catch{}}}async previewHtml(e,s){if(!this.form||!this.schema)return"";const t=JSON.parse(JSON.stringify(this.schema));t.settings.theme=e,t.settings.themeOverrides=s;const{html:a}=await I.preview(this.form.id,{schema:t,theme:e});return a}renderSettingsPane(){const e=this.schema.settings,s=(t,a)=>{const i=t.split(".");let o=e;for(let l=0;ls("submitLabel",t))),q("Submit without reloading the page",e.ajax,t=>s("ajax",t)),w("Progress indicator",D(e.progressBar,[{value:"steps",label:"Numbered steps"},{value:"bar",label:"A bar"},{value:"none",label:"None"}],t=>s("progressBar",t)),"Only shown on forms with a page break.")]}),r("section",{children:[r("h3",{text:"Who can fill this in"}),q("Only logged-in users",e.requireLogin,t=>s("requireLogin",t)),w("Message for everyone else",T(e.loginMessage,t=>s("loginMessage",t))),w("Open from",r("input",{class:"atfb-input",type:"datetime-local",value:e.schedule.start,on:{input:t=>s("schedule.start",t.target.value)}})),w("Closes",r("input",{class:"atfb-input",type:"datetime-local",value:e.schedule.end,on:{input:t=>s("schedule.end",t.target.value)}})),w("Message when closed",T(e.schedule.message,t=>s("schedule.message",t))),w("Stop after this many submissions",V(String(e.limit.total||""),t=>s("limit.total",Number(t)||0)),"0 means no limit."),w("Submissions per logged-in user",V(String(e.limit.perUser||""),t=>s("limit.perUser",Number(t)||0)))]}),r("section",{children:[r("h3",{text:"Spam"}),r("p",{class:"atfb-hint",text:"No captcha. Nothing here asks the visitor to prove anything."}),q("Honeypot field",e.spam.honeypot,t=>s("spam.honeypot",t)),w("Reject submissions faster than (seconds)",V(String(e.spam.timeTrap),t=>s("spam.timeTrap",Number(t)||0)),"A human cannot fill in a form in under a second. A script can."),w("Submissions allowed per hour, per address",V(String(e.spam.rateLimit),t=>s("spam.rateLimit",Number(t)||0))),w("Blocked words",pe(e.spam.blocklist,t=>s("spam.blocklist",t),4),"One per line."),q("Check submissions with Akismet",e.spam.akismet,t=>s("spam.akismet",t)),r("p",{class:"atfb-hint",text:"Off unless you switch it on. When on, each submission — the answers, plus the sender’s IP address and user agent — is sent to Akismet’s servers for a spam verdict. Needs the Akismet plugin installed and configured."}),q("Ask a simple sum before sending",e.spam.challenge,t=>s("spam.challenge",t)),r("p",{class:"atfb-hint",text:"Only for a form under sustained attack — it is the one check here that asks the visitor to do something. Still kinder than an image captcha: it is answerable by a screen reader, and it hands no data to anyone."})]}),r("section",{children:[r("h3",{text:"Storage and privacy"}),q("Keep entries",e.storage.entries,t=>s("storage.entries",t)),q("Record IP addresses",e.storage.ip,t=>s("storage.ip",t)),q("Anonymise recorded IP addresses",e.storage.anonymise,t=>s("storage.anonymise",t)),w("Delete entries after (days)",V(String(e.storage.retention||""),t=>s("storage.retention",Number(t)||0)),"0 keeps them forever. Anything else deletes automatically, every day.")]}),r("section",{children:[r("h3",{text:"Analytics"}),q("Count views and starts",e.analytics.enabled,t=>s("analytics.enabled",t)),q("Tally device, browser and system",e.analytics.tech,t=>s("analytics.tech",t)),r("p",{class:"atfb-hint",text:"Counters, never people: no cookie, no fingerprint, no per-visitor record — the tallies keep coarse classes like “phone” and “Chrome”, not the visitor’s user-agent string. Nothing here needs a consent banner. Turning the first switch off stops view and start counting entirely."})]}),r("section",{children:[r("h3",{text:"Save and continue later"}),q("Let people save a half-finished form",e.resume.enabled,t=>s("resume.enabled",t)),w("Keep a saved form for (days)",V(String(e.resume.days),t=>s("resume.days",Math.max(1,Number(t)||30)))),r("p",{class:"atfb-hint",text:"The link this creates is the only key to those answers — anyone holding it can read them. For genuinely sensitive questions, require login instead."})]}),r("section",{children:[r("h3",{text:"Archive"}),r("p",{class:"atfb-hint",text:"Retire the form when its moment has passed. It stops accepting responses and leaves every list, and its entries and stats go with it — nothing is deleted, and restoring it brings all of it back exactly as it was."}),C("Archive this form",()=>void this.archiveCurrentForm(),"secondary","archive")]}),r("section",{children:[r("h3",{text:"Delete"}),r("p",{class:"atfb-hint",text:"Deleting is for a form that should never have existed. It moves to the desktop’s Trash, disappears from every list, and any page still carrying its shortcode shows nothing at all. Its entries stay in Entries, and until the Trash is emptied you can bring it back from there. If the form simply had its day, archive it instead."}),C("Delete this form",()=>void this.deleteCurrentForm(),"danger","trash")]})]})}async archiveCurrentForm(){if(!this.form)return;const e=this.form.title||"(untitled)",s=this.forms.find(a=>a.id===this.form.id)?.entries??0;if(await ae(`Archive “${e}”? It stops accepting responses and leaves every list — its ${s} ${s===1?"entry":"entries"} and its stats go with it. Nothing is deleted: restore it any time from “Start a new form”.`,"Archive form"))try{this.dirty&&await this.save(!0),await I.archiveForm(this.form.id),N("Form archived",`${e} — restore it any time from the New form dialog.`),await this.releaseCurrentForm()}catch(a){N("Could not archive the form",a instanceof Error?a.message:"","error")}}async deleteCurrentForm(){if(!this.form)return;const e=this.form.title||"(untitled)",s=this.forms.find(a=>a.id===this.form.id)?.entries??0;if(await ae(`Delete “${e}”? Every page showing it goes blank. ${s?`Its ${s} ${s===1?"entry stays":"entries stay"} in Entries. `:""}It moves to the desktop’s Trash — restore it from there if you change your mind.`,"Delete form"))try{const a=this.form.id;await I.deleteForm(a),window.wp?.os?.announceContentChange?.("alltfo_form","trashed",a,"allterrain-forms"),N("Form deleted",`${e} moved to the desktop’s Trash.`),await this.releaseCurrentForm()}catch(a){N("Could not delete the form",a instanceof Error?a.message:"","error")}}async releaseCurrentForm(){if(this.forms=this.forms.filter(e=>e.id!==this.form.id),this.form=null,this.schema=null,this.selected=null,this.dirty=!1,this.history=[],this.historyAt=-1,this.forms.length){await this.open(this.forms[0].id);return}this.renderBar(),this.renderCanvas(),this.renderInspector()}writeTagValue(e,s,t,a){const i=e==="notification"?this.schema?.notifications.find(o=>o.id===s):this.schema?.confirmations.find(o=>o.id===s);i&&(t==="successTitle"&&"success"in i?i.success.title=a:Object.assign(i,{[t]:a}),this.markDirty())}taggableInput(e,s,t=""){return Be(T(e,s,t),{formId:this.form.id})}taggableArea(e,s,t=6){return Be(pe(e,s,t),{formId:this.form.id})}recipientControl(e){const s=this.schema.fields.filter(c=>c.type==="email"),t=c=>{if(c.trim()==="{admin_email}")return"admin";const d=c.trim().match(/^\{field:([a-z0-9_-]+)\}$/i);return d&&s.some(h=>h.id===d[1])?`field:${d[1]}`:/\{/.test(c)?"custom":"address"},a=[{value:"admin",label:"Whoever runs this site"},...s.map(c=>({value:`field:${c.id}`,label:`The person who filled it in — ${c.label||"their email answer"}`})),{value:"address",label:"A specific email address"},{value:"custom",label:"Something else (advanced)"}],i=t(e.to),o=r("div",{class:"atfb-recipient__detail"}),l=c=>{if(o.replaceChildren(),c==="address"){o.append(T(/\{/.test(e.to)?"":e.to,d=>{this.writeTagValue("notification",e.id,"to",d)},"name@example.com"));return}c==="custom"&&o.append(this.taggableInput(e.to,d=>{this.writeTagValue("notification",e.id,"to",d)},"{admin_email}, sales@example.com"),r("p",{class:"atfb-row__hint",text:"Separate several addresses with commas."}))};return l(i),w("Send it to",r("div",{class:"atfb-recipient",children:[D(i,a,c=>{c==="admin"?e.to="{admin_email}":c.startsWith("field:")?e.to=`{${c}}`:c==="address"&&(e.to=/\{/.test(e.to)?"":e.to),this.markDirty(),l(c)}),o]}),s.length?void 0:"Add an Email question on the Build tab to reply straight back to the visitor.")}renderNotificationsPane(){const e=this.schema.notifications,s=r("div",{class:"atfb-list"});return e.length||s.append(r("p",{class:"atfb-hint",text:"With none set up, one email goes to the site administrator with every answer in it."})),e.forEach((t,a)=>{s.append(this.section(`notification:${t.id}`,t.name||`Notification ${a+1}`,[w("Name",T(t.name,i=>{t.name=i,this.markDirty()})),this.recipientControl(t),w("Reply to",this.taggableInput(t.replyTo,i=>{this.writeTagValue("notification",t.id,"replyTo",i)},"Leave empty to reply to you"),"Set this to the visitor’s email address and hitting Reply answers them directly."),w("Subject",this.taggableInput(t.subject,i=>{this.writeTagValue("notification",t.id,"subject",i)})),w("Message",this.taggableArea(t.message,i=>{this.writeTagValue("notification",t.id,"message",i)},8)),q("Attach uploaded files",t.attachFiles,i=>{t.attachFiles=i,this.markDirty()}),this.conditionsSection(t.id,"notification",t.logic),C("Delete this notification",()=>{e.splice(a,1),this.markDirty(),this.renderCanvas()},"danger")]))}),r("div",{class:"atfb-pane",children:[r("h2",{text:"Notifications"}),s,C("Add a notification",()=>{e.push({id:this.nextEntryId("n",e),enabled:!0,name:"Notification",to:"{admin_email}",cc:"",bcc:"",replyTo:"",fromName:"",fromEmail:"",subject:"New submission",message:"{all_fields}",attachFiles:!1,logic:{enabled:!1,action:"show",match:"all",rules:[]}}),this.markDirty(),this.renderCanvas()},"primary","plus-alt2")]})}confirmationDetail(e){if(e.type==="message")return r("div",{children:[w("Message",this.taggableArea(e.message,i=>{this.writeTagValue("confirmation",e.id,"message",i)},5),"Insert an answer to greet them by name, or show back what they sent."),this.successDesigner(e)]});const s=w("Extra query parameters",this.taggableInput(e.query,i=>{this.writeTagValue("confirmation",e.id,"query",i)},"ref={entry:id}&name={field:f1}"),"Added to the address, so the page they land on can read them. Leave empty for none.");if(e.type==="redirect")return r("div",{children:[w("Web address",this.taggableInput(e.url,i=>{this.writeTagValue("confirmation",e.id,"url",i)},"https://example.com/thank-you"),"A full address, starting with https://."),s]});const t=r("div",{class:"atfb-pagepicker"}),a=i=>{t.replaceChildren(D(String(e.pageId||0),i,o=>{e.pageId=Number(o)||0,this.markDirty()}))};return a([{value:"0",label:"Loading pages…"}]),I.pages().then(i=>{a([{value:"0",label:"Choose a page…"},...i.map(o=>({value:String(o.id),label:o.title}))])}).catch(()=>{a([{value:"0",label:"Could not load the pages"}])}),w("Page",t,"They are sent to this page after submitting. Its own content is shown, not the form’s message.")}successDesigner(e){e.success=Ve(e.success);const s=e.success,t=r("div",{class:"atfb-success"}),a=[{key:"plain",label:"Plain",glyph:"¶",blurb:"Just the message."},{key:"simple",label:"Simple",glyph:"✓",blurb:"Check mark and a gentle fade."},{key:"minimal",label:"Minimalistic",glyph:"—",blurb:"Quiet type, generous space."},{key:"card",label:"Card",glyph:"🎫",blurb:"An elevated card that pops in."},{key:"check",label:"Check mark",glyph:"✔",blurb:"A big check draws itself."},{key:"confetti",label:"Confetti",glyph:"🎉",blurb:"Paper rains over the page."},{key:"fireworks",label:"Fireworks",glyph:"🎆",blurb:"The full night-sky show."},{key:"sparkles",label:"Sparkles",glyph:"✨",blurb:"Your emoji floats up around it."},{key:"typewriter",label:"Typewriter",glyph:"⌨",blurb:"Types itself out, letter by letter."}],i=()=>{const o=r("div",{class:"atfb-success__styles",attrs:{role:"radiogroup","aria-label":"Success screen style"},children:a.map(c=>r("button",{class:`atfb-success__style${s.style===c.key?" is-selected":""}`,type:"button",attrs:{role:"radio","aria-checked":s.style===c.key?"true":"false",title:c.blurb},children:[r("span",{class:"atfb-success__style-glyph",text:c.glyph}),r("span",{class:"atfb-success__style-label",text:c.label})],on:{click:()=>{s.style=c.key,this.markDirty(),i(),c.key!=="plain"&&this.previewSuccessScreen(e)}}}))}),l=[];s.style!=="plain"&&(l.push(w("Heading",this.taggableInput(s.title,c=>{this.writeTagValue("confirmation",e.id,"successTitle",c)},"Thank you, {field:name}!"),"Shown above the message. Leave empty for none.")),["simple","card","confetti","fireworks","sparkles"].includes(s.style)&&l.push(w("Emoji",T(s.icon,c=>{s.icon=c,this.markDirty()},we[s.style]||"🎉"),s.style==="sparkles"?"Also the particle that floats up — try 🎈 or ❤️.":"The badge above the heading.")),l.push(this.successAccentRow(s)),["confetti","fireworks","sparkles"].includes(s.style)&&l.push(w("Intensity",D(s.intensity,[{value:"low",label:"Calm"},{value:"medium",label:"Festive"},{value:"high",label:"Over the top"}],c=>{s.intensity=c,this.markDirty()}))),l.push(q("Offer to fill it in again",s.showButton,c=>{s.showButton=c,this.markDirty(),i()})),s.showButton&&l.push(w("Button label",T(s.buttonLabel,c=>{s.buttonLabel=c,this.markDirty()},"Fill it in again")))),t.replaceChildren(r("div",{class:"atfb-success__head",children:[r("h4",{text:"Success screen"}),C("Preview",()=>this.previewSuccessScreen(e),"ghost","controls-play")]}),o,...l)};return i(),t}successAccentRow(e){const s=r("input",{class:"atfb-success__accent",attrs:{type:"color","aria-label":"Accent colour"}});s.value=e.accent||"#3858e9";const t=C("Use the theme accent",()=>{e.accent="",s.value="#3858e9",t.style.display="none",this.markDirty()},"ghost");return t.style.display=e.accent?"":"none",s.addEventListener("input",()=>{e.accent=s.value,t.style.display="",this.markDirty()}),w("Accent",r("div",{class:"atfb-success__accent-row",children:[s,t]}),"Recolours the screen; the theme decides when left alone.")}previewSuccessScreen(e){const s=e.message||"Thank you. Your submission has been received.";let t=()=>{};const a=r("div",{class:"atfb-success-preview",attrs:{role:"dialog","aria-label":"Success screen preview","aria-modal":"true"}}),i=()=>{t(),a.remove(),document.removeEventListener("keydown",o)},o=h=>{h.key==="Escape"&&(h.stopPropagation(),i())},l=nn(s,e.success,i),c=r("div",{class:"atfb-success-preview__stage atfb-preview atf-form",children:[l]}),d=()=>{t(),t=an(l,e.success)};a.append(c,r("div",{class:"atfb-success-preview__bar",children:[C("Replay",d,"secondary","controls-repeat"),C("Close",i,"primary")]})),a.addEventListener("click",h=>{h.target===a&&i()}),document.addEventListener("keydown",o),this.root.append(a),window.requestAnimationFrame(d)}renderConfirmationsPane(){const e=this.schema.confirmations,s=r("div",{class:"atfb-list"});return e.length||s.append(r("p",{class:"atfb-hint",text:"With none set up, the form says thank you and stops."})),e.forEach((t,a)=>{const i=r("div",{class:"atfb-confirm__detail"}),o=()=>{i.replaceChildren(this.confirmationDetail(t))};o(),s.append(this.section(`confirmation:${t.id}`,t.name||`Confirmation ${a+1}`,[w("Name",T(t.name,l=>{t.name=l,this.markDirty()})),w("What happens",D(t.type,[{value:"message",label:"Show a message"},{value:"redirect",label:"Send them to a URL"},{value:"page",label:"Send them to a page"}],l=>{t.type=l,this.markDirty(),o()})),i,this.conditionsSection(t.id,"confirmation",t.logic),C("Delete this confirmation",()=>{e.splice(a,1),this.markDirty(),this.renderCanvas()},"danger")]))}),r("div",{class:"atfb-pane",children:[r("h2",{text:"Confirmations"}),r("p",{class:"atfb-hint",text:"The first one whose conditions match is the one they see."}),s,C("Add a confirmation",()=>{e.push({id:this.nextEntryId("c",e),enabled:!0,name:"Confirmation",type:"message",message:"Thank you. Your submission has been received.",url:"",pageId:0,query:"",success:kt(),logic:{enabled:!1,action:"show",match:"all",rules:[]}}),this.markDirty(),this.renderCanvas()},"primary","plus-alt2")]})}async preview(){await zt({current:()=>this.form?{id:this.form.id,title:this.form.title,previewUrl:this.form.previewUrl}:null,isDirty:()=>this.dirty,save:()=>this.save(!0)})}}let ne=null,et=null;document.addEventListener("alltfo-open-form",n=>{const e=Number(n.detail?.formId??0);e&&ne?.openFormById(e)});function tt(){if(et?.isConnected)return;ne&&(ne.destroy(),ne=null,et=null);const n=document.querySelector("[data-atfb-root]:not([data-atfb-mounted])");n&&(n.dataset.atfbMounted="1",et=n,Fe(n),us().then(()=>{n.isConnected&&(ne=new Jt(n),ne.start())}))}function Qt(){tt(),ws()}return _s(),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",Qt):Qt(),document.addEventListener("os-window-content-loaded",tt),ee.Builder=Jt,ee.SETTINGS_HANDLED_ELSEWHERE=jn,ee.SETTING_CONTROLS=Kt,ee.fieldMove=Zt,ee.mountBuilder=tt,ee.restatement=Xt,Object.defineProperty(ee,Symbol.toStringTag,{value:"Module"}),ee}({}); +}`,this.canvasTheme.isConnected||this.root.append(this.canvasTheme)}catch{}}}async previewHtml(e,s){if(!this.form||!this.schema)return"";const t=JSON.parse(JSON.stringify(this.schema));t.settings.theme=e,t.settings.themeOverrides=s;const{html:a}=await I.preview(this.form.id,{schema:t,theme:e});return a}renderSettingsPane(){const e=this.schema.settings,s=(t,a)=>{const i=t.split(".");let o=e;for(let l=0;ls("submitLabel",t))),q("Submit without reloading the page",e.ajax,t=>s("ajax",t)),w("Progress indicator",D(e.progressBar,[{value:"steps",label:"Numbered steps"},{value:"bar",label:"A bar"},{value:"none",label:"None"}],t=>s("progressBar",t)),"Only shown on forms with a page break.")]}),r("section",{children:[r("h3",{text:"Who can fill this in"}),q("Only logged-in users",e.requireLogin,t=>s("requireLogin",t)),w("Message for everyone else",T(e.loginMessage,t=>s("loginMessage",t))),w("Open from",r("input",{class:"atfb-input",type:"datetime-local",value:e.schedule.start,on:{input:t=>s("schedule.start",t.target.value)}})),w("Closes",r("input",{class:"atfb-input",type:"datetime-local",value:e.schedule.end,on:{input:t=>s("schedule.end",t.target.value)}})),w("Message when closed",T(e.schedule.message,t=>s("schedule.message",t))),w("Stop after this many submissions",V(String(e.limit.total||""),t=>s("limit.total",Number(t)||0)),"0 means no limit."),w("Submissions per logged-in user",V(String(e.limit.perUser||""),t=>s("limit.perUser",Number(t)||0)))]}),r("section",{children:[r("h3",{text:"Spam"}),r("p",{class:"atfb-hint",text:"No captcha. Nothing here asks the visitor to prove anything."}),q("Honeypot field",e.spam.honeypot,t=>s("spam.honeypot",t)),w("Reject submissions faster than (seconds)",V(String(e.spam.timeTrap),t=>s("spam.timeTrap",Number(t)||0)),"A human cannot fill in a form in under a second. A script can."),w("Submissions allowed per hour, per address",V(String(e.spam.rateLimit),t=>s("spam.rateLimit",Number(t)||0))),w("Blocked words",fe(e.spam.blocklist,t=>s("spam.blocklist",t),4),"One per line."),q("Check submissions with Akismet",e.spam.akismet,t=>s("spam.akismet",t)),r("p",{class:"atfb-hint",text:"Off unless you switch it on. When on, each submission — the answers, plus the sender’s IP address and user agent — is sent to Akismet’s servers for a spam verdict. Needs the Akismet plugin installed and configured."}),q("Ask a simple sum before sending",e.spam.challenge,t=>s("spam.challenge",t)),r("p",{class:"atfb-hint",text:"Only for a form under sustained attack — it is the one check here that asks the visitor to do something. Still kinder than an image captcha: it is answerable by a screen reader, and it hands no data to anyone."})]}),r("section",{children:[r("h3",{text:"Storage and privacy"}),q("Keep entries",e.storage.entries,t=>s("storage.entries",t)),q("Record IP addresses",e.storage.ip,t=>s("storage.ip",t)),q("Anonymise recorded IP addresses",e.storage.anonymise,t=>s("storage.anonymise",t)),w("Delete entries after (days)",V(String(e.storage.retention||""),t=>s("storage.retention",Number(t)||0)),"0 keeps them forever. Anything else deletes automatically, every day.")]}),r("section",{children:[r("h3",{text:"Analytics"}),q("Count views and starts",e.analytics.enabled,t=>s("analytics.enabled",t)),q("Tally device, browser and system",e.analytics.tech,t=>s("analytics.tech",t)),r("p",{class:"atfb-hint",text:"Counters, never people: no cookie, no fingerprint, no per-visitor record — the tallies keep coarse classes like “phone” and “Chrome”, not the visitor’s user-agent string. Nothing here needs a consent banner. Turning the first switch off stops view and start counting entirely."})]}),r("section",{children:[r("h3",{text:"Save and continue later"}),q("Let people save a half-finished form",e.resume.enabled,t=>s("resume.enabled",t)),w("Keep a saved form for (days)",V(String(e.resume.days),t=>s("resume.days",Math.max(1,Number(t)||30)))),r("p",{class:"atfb-hint",text:"The link this creates is the only key to those answers — anyone holding it can read them. For genuinely sensitive questions, require login instead."})]}),r("section",{children:[r("h3",{text:"Archive"}),r("p",{class:"atfb-hint",text:"Retire the form when its moment has passed. It stops accepting responses and leaves every list, and its entries and stats go with it — nothing is deleted, and restoring it brings all of it back exactly as it was."}),C("Archive this form",()=>void this.archiveCurrentForm(),"secondary","archive")]}),r("section",{children:[r("h3",{text:"Delete"}),r("p",{class:"atfb-hint",text:"Deleting is for a form that should never have existed. It moves to the desktop’s Trash, disappears from every list, and any page still carrying its shortcode shows nothing at all. Its entries stay in Entries, and until the Trash is emptied you can bring it back from there. If the form simply had its day, archive it instead."}),C("Delete this form",()=>void this.deleteCurrentForm(),"danger","trash")]})]})}async archiveCurrentForm(){if(!this.form)return;const e=this.form.title||"(untitled)",s=this.forms.find(a=>a.id===this.form.id)?.entries??0;if(await ie(`Archive “${e}”? It stops accepting responses and leaves every list — its ${s} ${s===1?"entry":"entries"} and its stats go with it. Nothing is deleted: restore it any time from “Start a new form”.`,"Archive form"))try{this.dirty&&await this.save(!0),await I.archiveForm(this.form.id),N("Form archived",`${e} — restore it any time from the New form dialog.`),await this.releaseCurrentForm()}catch(a){N("Could not archive the form",a instanceof Error?a.message:"","error")}}async deleteCurrentForm(){if(!this.form)return;const e=this.form.title||"(untitled)",s=this.forms.find(a=>a.id===this.form.id)?.entries??0;if(await ie(`Delete “${e}”? Every page showing it goes blank. ${s?`Its ${s} ${s===1?"entry stays":"entries stay"} in Entries. `:""}It moves to the desktop’s Trash — restore it from there if you change your mind.`,"Delete form"))try{const a=this.form.id;await I.deleteForm(a),window.wp?.os?.announceContentChange?.("alltfo_form","trashed",a,"allterrain-forms"),N("Form deleted",`${e} moved to the desktop’s Trash.`),await this.releaseCurrentForm()}catch(a){N("Could not delete the form",a instanceof Error?a.message:"","error")}}async releaseCurrentForm(){if(this.forms=this.forms.filter(e=>e.id!==this.form.id),this.form=null,this.schema=null,this.selected=null,this.dirty=!1,this.history=[],this.historyAt=-1,this.forms.length){await this.open(this.forms[0].id);return}this.renderBar(),this.renderCanvas(),this.renderInspector()}writeTagValue(e,s,t,a){const i=e==="notification"?this.schema?.notifications.find(o=>o.id===s):this.schema?.confirmations.find(o=>o.id===s);i&&(t==="successTitle"&&"success"in i?i.success.title=a:Object.assign(i,{[t]:a}),this.markDirty())}taggableInput(e,s,t=""){return Be(T(e,s,t),{formId:this.form.id})}taggableArea(e,s,t=6){return Be(fe(e,s,t),{formId:this.form.id})}recipientControl(e){const s=this.schema.fields.filter(c=>c.type==="email"),t=c=>{if(c.trim()==="{admin_email}")return"admin";const d=c.trim().match(/^\{field:([a-z0-9_-]+)\}$/i);return d&&s.some(h=>h.id===d[1])?`field:${d[1]}`:/\{/.test(c)?"custom":"address"},a=[{value:"admin",label:"Whoever runs this site"},...s.map(c=>({value:`field:${c.id}`,label:`The person who filled it in — ${c.label||"their email answer"}`})),{value:"address",label:"A specific email address"},{value:"custom",label:"Something else (advanced)"}],i=t(e.to),o=r("div",{class:"atfb-recipient__detail"}),l=c=>{if(o.replaceChildren(),c==="address"){o.append(T(/\{/.test(e.to)?"":e.to,d=>{this.writeTagValue("notification",e.id,"to",d)},"name@example.com"));return}c==="custom"&&o.append(this.taggableInput(e.to,d=>{this.writeTagValue("notification",e.id,"to",d)},"{admin_email}, sales@example.com"),r("p",{class:"atfb-row__hint",text:"Separate several addresses with commas."}))};return l(i),w("Send it to",r("div",{class:"atfb-recipient",children:[D(i,a,c=>{c==="admin"?e.to="{admin_email}":c.startsWith("field:")?e.to=`{${c}}`:c==="address"&&(e.to=/\{/.test(e.to)?"":e.to),this.markDirty(),l(c)}),o]}),s.length?void 0:"Add an Email question on the Build tab to reply straight back to the visitor.")}renderNotificationsPane(){const e=this.schema.notifications,s=r("div",{class:"atfb-list"});return e.length||s.append(r("p",{class:"atfb-hint",text:"With none set up, one email goes to the site administrator with every answer in it."})),e.forEach((t,a)=>{s.append(this.section(`notification:${t.id}`,t.name||`Notification ${a+1}`,[w("Name",T(t.name,i=>{t.name=i,this.markDirty()})),this.recipientControl(t),w("Reply to",this.taggableInput(t.replyTo,i=>{this.writeTagValue("notification",t.id,"replyTo",i)},"Leave empty to reply to you"),"Set this to the visitor’s email address and hitting Reply answers them directly."),w("Subject",this.taggableInput(t.subject,i=>{this.writeTagValue("notification",t.id,"subject",i)})),w("Message",this.taggableArea(t.message,i=>{this.writeTagValue("notification",t.id,"message",i)},8)),q("Attach uploaded files",t.attachFiles,i=>{t.attachFiles=i,this.markDirty()}),this.conditionsSection(t.id,"notification",t.logic),C("Delete this notification",()=>{e.splice(a,1),this.markDirty(),this.renderCanvas()},"danger")]))}),r("div",{class:"atfb-pane",children:[r("h2",{text:"Notifications"}),s,C("Add a notification",()=>{e.push({id:this.nextEntryId("n",e),enabled:!0,name:"Notification",to:"{admin_email}",cc:"",bcc:"",replyTo:"",fromName:"",fromEmail:"",subject:"New submission",message:"{all_fields}",attachFiles:!1,logic:{enabled:!1,action:"show",match:"all",rules:[]}}),this.markDirty(),this.renderCanvas()},"primary","plus-alt2")]})}confirmationDetail(e){if(e.type==="message")return r("div",{children:[w("Message",this.taggableArea(e.message,i=>{this.writeTagValue("confirmation",e.id,"message",i)},5),"Insert an answer to greet them by name, or show back what they sent."),this.successDesigner(e)]});const s=w("Extra query parameters",this.taggableInput(e.query,i=>{this.writeTagValue("confirmation",e.id,"query",i)},"ref={entry:id}&name={field:f1}"),"Added to the address, so the page they land on can read them. Leave empty for none.");if(e.type==="redirect")return r("div",{children:[w("Web address",this.taggableInput(e.url,i=>{this.writeTagValue("confirmation",e.id,"url",i)},"https://example.com/thank-you"),"A full address, starting with https://."),s]});const t=r("div",{class:"atfb-pagepicker"}),a=i=>{t.replaceChildren(D(String(e.pageId||0),i,o=>{e.pageId=Number(o)||0,this.markDirty()}))};return a([{value:"0",label:"Loading pages…"}]),I.pages().then(i=>{a([{value:"0",label:"Choose a page…"},...i.map(o=>({value:String(o.id),label:o.title}))])}).catch(()=>{a([{value:"0",label:"Could not load the pages"}])}),w("Page",t,"They are sent to this page after submitting. Its own content is shown, not the form’s message.")}successDesigner(e){e.success=Ve(e.success);const s=e.success,t=r("div",{class:"atfb-success"}),a=[{key:"plain",label:"Plain",glyph:"¶",blurb:"Just the message."},{key:"simple",label:"Simple",glyph:"✓",blurb:"Check mark and a gentle fade."},{key:"minimal",label:"Minimalistic",glyph:"—",blurb:"Quiet type, generous space."},{key:"card",label:"Card",glyph:"🎫",blurb:"An elevated card that pops in."},{key:"check",label:"Check mark",glyph:"✔",blurb:"A big check draws itself."},{key:"confetti",label:"Confetti",glyph:"🎉",blurb:"Paper rains over the page."},{key:"fireworks",label:"Fireworks",glyph:"🎆",blurb:"The full night-sky show."},{key:"sparkles",label:"Sparkles",glyph:"✨",blurb:"Your emoji floats up around it."},{key:"typewriter",label:"Typewriter",glyph:"⌨",blurb:"Types itself out, letter by letter."}],i=()=>{const o=r("div",{class:"atfb-success__styles",attrs:{role:"radiogroup","aria-label":"Success screen style"},children:a.map(c=>r("button",{class:`atfb-success__style${s.style===c.key?" is-selected":""}`,type:"button",attrs:{role:"radio","aria-checked":s.style===c.key?"true":"false",title:c.blurb},children:[r("span",{class:"atfb-success__style-glyph",text:c.glyph}),r("span",{class:"atfb-success__style-label",text:c.label})],on:{click:()=>{s.style=c.key,this.markDirty(),i(),c.key!=="plain"&&this.previewSuccessScreen(e)}}}))}),l=[];s.style!=="plain"&&(l.push(w("Heading",this.taggableInput(s.title,c=>{this.writeTagValue("confirmation",e.id,"successTitle",c)},"Thank you, {field:name}!"),"Shown above the message. Leave empty for none.")),["simple","card","confetti","fireworks","sparkles"].includes(s.style)&&l.push(w("Emoji",T(s.icon,c=>{s.icon=c,this.markDirty()},we[s.style]||"🎉"),s.style==="sparkles"?"Also the particle that floats up — try 🎈 or ❤️.":"The badge above the heading.")),l.push(this.successAccentRow(s)),["confetti","fireworks","sparkles"].includes(s.style)&&l.push(w("Intensity",D(s.intensity,[{value:"low",label:"Calm"},{value:"medium",label:"Festive"},{value:"high",label:"Over the top"}],c=>{s.intensity=c,this.markDirty()}))),l.push(q("Offer to fill it in again",s.showButton,c=>{s.showButton=c,this.markDirty(),i()})),s.showButton&&l.push(w("Button label",T(s.buttonLabel,c=>{s.buttonLabel=c,this.markDirty()},"Fill it in again")))),t.replaceChildren(r("div",{class:"atfb-success__head",children:[r("h4",{text:"Success screen"}),C("Preview",()=>this.previewSuccessScreen(e),"ghost","controls-play")]}),o,...l)};return i(),t}successAccentRow(e){const s=r("input",{class:"atfb-success__accent",attrs:{type:"color","aria-label":"Accent colour"}});s.value=e.accent||"#3858e9";const t=C("Use the theme accent",()=>{e.accent="",s.value="#3858e9",t.style.display="none",this.markDirty()},"ghost");return t.style.display=e.accent?"":"none",s.addEventListener("input",()=>{e.accent=s.value,t.style.display="",this.markDirty()}),w("Accent",r("div",{class:"atfb-success__accent-row",children:[s,t]}),"Recolours the screen; the theme decides when left alone.")}previewSuccessScreen(e){const s=e.message||"Thank you. Your submission has been received.";let t=()=>{};const a=r("div",{class:"atfb-success-preview",attrs:{role:"dialog","aria-label":"Success screen preview","aria-modal":"true"}}),i=()=>{t(),a.remove(),document.removeEventListener("keydown",o)},o=h=>{h.key==="Escape"&&(h.stopPropagation(),i())},l=nn(s,e.success,i),c=r("div",{class:"atfb-success-preview__stage atfb-preview atf-form",children:[l]}),d=()=>{t(),t=an(l,e.success)};a.append(c,r("div",{class:"atfb-success-preview__bar",children:[C("Replay",d,"secondary","controls-repeat"),C("Close",i,"primary")]})),a.addEventListener("click",h=>{h.target===a&&i()}),document.addEventListener("keydown",o),this.root.append(a),window.requestAnimationFrame(d)}renderConfirmationsPane(){const e=this.schema.confirmations,s=r("div",{class:"atfb-list"});return e.length||s.append(r("p",{class:"atfb-hint",text:"With none set up, the form says thank you and stops."})),e.forEach((t,a)=>{const i=r("div",{class:"atfb-confirm__detail"}),o=()=>{i.replaceChildren(this.confirmationDetail(t))};o(),s.append(this.section(`confirmation:${t.id}`,t.name||`Confirmation ${a+1}`,[w("Name",T(t.name,l=>{t.name=l,this.markDirty()})),w("What happens",D(t.type,[{value:"message",label:"Show a message"},{value:"redirect",label:"Send them to a URL"},{value:"page",label:"Send them to a page"}],l=>{t.type=l,this.markDirty(),o()})),i,this.conditionsSection(t.id,"confirmation",t.logic),C("Delete this confirmation",()=>{e.splice(a,1),this.markDirty(),this.renderCanvas()},"danger")]))}),r("div",{class:"atfb-pane",children:[r("h2",{text:"Confirmations"}),r("p",{class:"atfb-hint",text:"The first one whose conditions match is the one they see."}),s,C("Add a confirmation",()=>{e.push({id:this.nextEntryId("c",e),enabled:!0,name:"Confirmation",type:"message",message:"Thank you. Your submission has been received.",url:"",pageId:0,query:"",success:kt(),logic:{enabled:!1,action:"show",match:"all",rules:[]}}),this.markDirty(),this.renderCanvas()},"primary","plus-alt2")]})}async preview(){await zt({current:()=>this.form?{id:this.form.id,title:this.form.title,previewUrl:this.form.previewUrl}:null,isDirty:()=>this.dirty,save:()=>this.save(!0)})}}let ae=null,et=null;document.addEventListener("alltfo-open-form",n=>{const e=Number(n.detail?.formId??0);e&&ae?.openFormById(e)});function tt(){if(et?.isConnected)return;ae&&(ae.destroy(),ae=null,et=null);const n=document.querySelector("[data-atfb-root]:not([data-atfb-mounted])");n&&(n.dataset.atfbMounted="1",et=n,Fe(n),us().then(()=>{n.isConnected&&(ae=new Jt(n),ae.start())}))}function Qt(){tt(),ws()}return _s(),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",Qt):Qt(),document.addEventListener("os-window-content-loaded",tt),ee.Builder=Jt,ee.SETTINGS_HANDLED_ELSEWHERE=Hn,ee.SETTING_CONTROLS=Kt,ee.fieldMove=Zt,ee.mountBuilder=tt,ee.restatement=Xt,Object.defineProperty(ee,Symbol.toStringTag,{value:"Module"}),ee}({}); diff --git a/docs/javascript.md b/docs/javascript.md index 7d3dab9..b9936ca 100644 --- a/docs/javascript.md +++ b/docs/javascript.md @@ -299,7 +299,9 @@ all. Typing `{` in any input with **Insert a value** opens its searchable picker next to the control. Pick a value with the mouse, or search and press Enter; arrow -keys move through results and Escape returns to the input. The inserted tag +keys move through results and Escape returns to the input. Picker navigation +is captured before desktop shortcuts, so arrows cannot switch desktops, open +Overview or toggle Show Desktop while selecting a reference. The inserted tag replaces the triggering brace at the cursor. Pasting an existing formula does not open the picker. The Insert button also remains available. diff --git a/src/merge-tags.ts b/src/merge-tags.ts index c918aa3..4f0d834 100644 --- a/src/merge-tags.ts +++ b/src/merge-tags.ts @@ -142,11 +142,37 @@ if ( typeof document !== 'undefined' ) { } } ); - document.addEventListener( 'keydown', ( event ) => { + // The desktop shell handles arrows during document capture. A listener on + // the picker itself runs too late once an option button has focus. Capture + // at the window boundary, scoped to this picker, before desktop shortcuts. + window.addEventListener( 'keydown', ( event ) => { if ( 'Escape' === event.key && pickerReturnFocus ) { closePicker( true ); event.preventDefault(); event.stopImmediatePropagation(); + return; + } + if ( ! openPicker?.contains( event.target as Node | null ) || + ! [ 'ArrowDown', 'ArrowUp', 'ArrowLeft', 'ArrowRight', 'Home', 'End', 'Enter' ].includes( event.key ) ) { + return; + } + event.stopImmediatePropagation(); + const search = openPicker.querySelector( '.atfb-tagpick__search' ); + const items = [ ...openPicker.querySelectorAll< HTMLButtonElement >( '.atfb-tagpick__item' ) ]; + const index = items.indexOf( document.activeElement as HTMLButtonElement ); + if ( event.key === 'ArrowDown' || event.key === 'ArrowUp' ) { + event.preventDefault(); + const next = event.key === 'ArrowDown' ? index + 1 : ( index < 0 ? items.length - 1 : index - 1 ); + items[ ( next + items.length ) % items.length ]?.focus(); + } else if ( event.key === 'Enter' && event.target === search ) { + event.preventDefault(); + items[ 0 ]?.click(); + } else if ( event.target !== search && event.key !== 'Enter' ) { + // Left/right and Home/End retain native caret movement in search. + // On result buttons, Home/End jump to the first/last result instead. + event.preventDefault(); + if ( event.key === 'Home' ) items[ 0 ]?.focus(); + if ( event.key === 'End' ) items[ items.length - 1 ]?.focus(); } }, true ); } @@ -288,19 +314,7 @@ function buildPicker( list, ], } ); - picker.addEventListener( 'keydown', ( event ) => { - const items = [ ...list.querySelectorAll< HTMLButtonElement >( 'button' ) ]; - const index = items.indexOf( document.activeElement as HTMLButtonElement ); - if ( event.key === 'ArrowDown' || event.key === 'ArrowUp' ) { - event.preventDefault(); - const next = event.key === 'ArrowDown' ? index + 1 : ( index < 0 ? items.length - 1 : index - 1 ); - items[ ( next + items.length ) % items.length ]?.focus(); - } else if ( event.key === 'Enter' && event.target === search ) { - event.preventDefault(); - items[ 0 ]?.click(); - } - event.stopPropagation(); - } ); + picker.addEventListener( 'keydown', ( event ) => event.stopPropagation() ); return picker; } diff --git a/tests/vitest/merge-tags.test.ts b/tests/vitest/merge-tags.test.ts index edf3d17..3ce66ba 100644 --- a/tests/vitest/merge-tags.test.ts +++ b/tests/vitest/merge-tags.test.ts @@ -70,6 +70,64 @@ describe( 'brace picker', () => { expect( input.value ).toBe( '{' ); expect( document.activeElement ).toBe( input ); } ); + it( 'owns navigation before document capture shortcuts, and releases it on close', async () => { + const shellShortcut = vi.fn(); + document.addEventListener( 'keydown', shellShortcut, true ); + try { + const { input, wrapper } = mount(); + typeBrace( input ); + await settle(); + const search = wrapper.querySelector< HTMLInputElement >( '.atfb-tagpick__search' )!; + const items = wrapper.querySelectorAll< HTMLButtonElement >( '.atfb-tagpick__item' ); + const press = ( key: string ) => { + const event = new KeyboardEvent( 'keydown', { key, code: key, bubbles: true, cancelable: true } ); + document.activeElement!.dispatchEvent( event ); + return event; + }; + expect( document.activeElement ).toBe( search ); + expect( press( 'ArrowDown' ).defaultPrevented ).toBe( true ); + expect( document.activeElement ).toBe( items[ 0 ] ); + press( 'ArrowDown' ); + expect( document.activeElement ).toBe( items[ 1 ] ); + press( 'ArrowUp' ); + expect( document.activeElement ).toBe( items[ 0 ] ); + press( 'ArrowLeft' ); + press( 'ArrowRight' ); + press( 'End' ); + expect( document.activeElement ).toBe( items[ 1 ] ); + press( 'Home' ); + expect( document.activeElement ).toBe( items[ 0 ] ); + search.focus(); + for ( const key of [ 'ArrowLeft', 'ArrowRight', 'Home', 'End' ] ) { + expect( press( key ).defaultPrevented ).toBe( false ); + } + expect( shellShortcut ).not.toHaveBeenCalled(); + press( 'Escape' ); + expect( document.activeElement ).toBe( input ); + expect( wrapper.querySelector( '.atfb-tagpick' ) ).toBeNull(); + expect( shellShortcut ).not.toHaveBeenCalled(); + press( 'ArrowDown' ); + expect( shellShortcut ).toHaveBeenCalledTimes( 1 ); + } finally { + document.removeEventListener( 'keydown', shellShortcut, true ); + } + } ); + it( 'does not intercept arrows outside an open picker', async () => { + const { input } = mount(); + typeBrace( input ); + await settle(); + const outside = document.createElement( 'button' ); + document.body.append( outside ); + outside.focus(); + const shellShortcut = vi.fn(); + document.addEventListener( 'keydown', shellShortcut, true ); + try { + outside.dispatchEvent( new KeyboardEvent( 'keydown', { key: 'ArrowDown', code: 'ArrowDown', bubbles: true } ) ); + expect( shellShortcut ).toHaveBeenCalledTimes( 1 ); + } finally { + document.removeEventListener( 'keydown', shellShortcut, true ); + } + } ); it( 'does not open for pasted formulas or composition', async () => { const { input, wrapper } = mount( '{field:score}' ); input.dispatchEvent( new InputEvent( 'input', { data: null, inputType: 'insertFromPaste' } ) );