diff --git a/allterrain-forms.php b/allterrain-forms.php index 67d5d6d..7589766 100644 --- a/allterrain-forms.php +++ b/allterrain-forms.php @@ -201,6 +201,7 @@ require_once ATF_DIR . 'includes/notifications.php'; require_once ATF_DIR . 'includes/confirmations.php'; require_once ATF_DIR . 'includes/actions.php'; +require_once ATF_DIR . 'includes/mailpoet.php'; require_once ATF_DIR . 'includes/entries.php'; require_once ATF_DIR . 'includes/archive.php'; require_once ATF_DIR . 'includes/analytics.php'; diff --git a/assets/css/builder.css b/assets/css/builder.css index 3e7de1d..9dec862 100644 --- a/assets/css/builder.css +++ b/assets/css/builder.css @@ -3488,3 +3488,183 @@ os-checkbox-label.atfb-check { animation: none; } } + +/* -------------------------------------------------------------------------- */ +/* The MailPoet window */ +/* -------------------------------------------------------------------------- */ + +/* The hero wears MailPoet's orange — their brand, on the one surface that is + about them — and nothing else does: the working parts below are shell-token + furniture like every other window, so the integration reads as native to + both sides. The logo is MailPoet's own asset served from MailPoet's own + install, on a white chip because their wordmark is drawn for a light + ground and must survive every desktop theme. */ +.atfm { + --atfm-brand: #fe5301; +} + +.atfm__body { + overflow-y: auto; + padding: 20px; + display: grid; + gap: 18px; + align-content: start; +} + +.atfm-hero { + display: flex; + align-items: center; + gap: 18px; + padding: 22px; + border: 1px solid color-mix( in srgb, var( --atfm-brand ) 35%, transparent ); + border-radius: var( --atfb-radius ); + background: + radial-gradient( circle at 0% 0%, color-mix( in srgb, var( --atfm-brand ) 14%, transparent ), transparent 55% ), + var( --atfb-surface ); +} + +.atfm-hero__logo { + flex: 0 0 auto; + display: grid; + place-items: center; + padding: 12px 14px; + background: #fff; + border-radius: calc( var( --atfb-radius ) - 2px ); + box-shadow: 0 2px 10px rgba( 0, 0, 0, 0.12 ); +} + +.atfm-hero__logo img { + display: block; + max-inline-size: 108px; + block-size: auto; +} + +.atfm-hero__logo--glyph { + font-size: 34px; +} + +.atfm-hero__words h1 { + margin: 0 0 6px; + font-size: 18px; + line-height: 1.3; +} + +.atfm-hero__words p { + margin: 0; + max-inline-size: 52ch; + color: var( --atfb-muted ); +} + +.atfm-hero__status { + margin-block-start: 8px !important; + font-size: 12px; + font-weight: 600; + color: var( --atfb-muted ); +} + +.atfm-hero__status.is-on { + color: var( --atfb-success ); +} + +.atfm-pitch { + display: grid; + gap: 12px; + justify-items: start; + padding: 4px 2px; +} + +.atfm-pitch p { + margin: 0; + max-inline-size: 62ch; +} + +/* The one control allowed to wear the brand: the step that brings MailPoet in. */ +.atfm-pitch__cta { + display: inline-block; + padding: 8px 18px; + border-radius: var( --atfb-radius-ctl ); + background: var( --atfm-brand ); + color: #fff; + font-weight: 600; + text-decoration: none; +} + +.atfm-pitch__cta:hover { + filter: brightness( 1.08 ); + color: #fff; +} + +.atfm-forms { + display: grid; + gap: 10px; +} + +.atfm-forms h2 { + margin: 0; + font-size: 14px; +} + +.atfm-hint { + margin: 0; + font-size: 12px; + color: var( --atfb-muted ); +} + +.atfm-form { + border: 1px solid var( --atfb-card-border ); + border-radius: var( --atfb-radius ); + background: var( --atfb-bg ); +} + +.atfm-form__head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; + padding: 12px 14px; +} + +.atfm-form__title { + display: grid; + gap: 2px; + min-inline-size: 0; +} + +.atfm-form__status { + font-size: 11px; + color: var( --atfb-muted ); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.atfm-form__status.is-on { + color: var( --atfb-success ); + font-weight: 600; +} + +.atfm-form__body { + display: grid; + gap: 12px; + padding: 0 14px 14px; + border-block-start: 1px solid var( --atfb-border ); + padding-block-start: 12px; +} + +.atfm-lists { + display: grid; + gap: 6px; +} + +.atfm-list { + display: flex; + align-items: center; + gap: 8px; + font-size: 13px; + cursor: pointer; +} + +.atfm-form__actions { + display: flex; + gap: 8px; +} diff --git a/assets/js/analytics.js b/assets/js/analytics.js index 5b65430..eb1f6c0 100644 --- a/assets/js/analytics.js +++ b/assets/js/analytics.js @@ -95,6 +95,8 @@ var allTerrainFormsAnalytics = function(exports) { } const api = { config: () => get("/config"), + /** MailPoet's presence, lists and logo — what the MailPoet window boots from. */ + mailpoet: () => get("/mailpoet"), listForms: () => get("/forms"), /** The other side of the archive: the retired forms, same shape. */ listArchivedForms: () => get("/forms?archived=1"), diff --git a/assets/js/analytics.min.js b/assets/js/analytics.min.js index 4c5e373..2ef3b9a 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 d=await r.json();l=d.message??l,c=d.code??""}catch{}throw new y(l,r.status,c)}if(r.status!==204)return await r.json()}const h=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:()=>h("/config"),listForms:()=>h("/forms"),listArchivedForms:()=>h("/forms?archived=1"),archiveForm:s=>u(`/forms/${s}/archive`,{}),unarchiveForm:s=>u(`/forms/${s}/unarchive`,{}),getForm:s=>h(`/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=>h(`/forms/${s}/merge-tags`).then(e=>e.groups),analytics:(s,e="")=>h(`/forms/${s}/analytics${e?`?dimension=${encodeURIComponent(e)}`:""}`),demoStatus:()=>h("/demo"),demoSeed:s=>u("/demo",s?{count:s}:{}),demoRemove:()=>w("/demo"),listEntries:s=>h(`/entries${P(s)}`),getEntry:s=>h(`/entries/${s}`),updateEntry:(s,e)=>u(`/entries/${s}`,e),deleteEntry:s=>w(`/entries/${s}`),exportEntries:s=>h(`/entries/export${P(s)}`),listThemes:()=>h("/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((d-e)/i))),l=[];for(let d=0;dt.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,d)=>Math.max(c,d.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("atf-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("atf-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(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("atf-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("atf-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}({}); diff --git a/assets/js/builder.js b/assets/js/builder.js index 0d2ff43..872076c 100644 --- a/assets/js/builder.js +++ b/assets/js/builder.js @@ -349,6 +349,8 @@ var allTerrainFormsBuilder = function(exports) { } const api = { config: () => get("/config"), + /** MailPoet's presence, lists and logo — what the MailPoet window boots from. */ + mailpoet: () => get("/mailpoet"), listForms: () => get("/forms"), /** The other side of the archive: the retired forms, same shape. */ listArchivedForms: () => get("/forms?archived=1"), @@ -2534,6 +2536,342 @@ var allTerrainFormsBuilder = function(exports) { } return "#888888"; } + const ACTION_ID = "mailpoet"; + const SENTINEL_EVERYONE = ""; + const CONSENT_TYPES = ["checkbox", "checkboxes", "toggle", "consent", "gdpr"]; + const EMAIL_TYPES = ["email"]; + function findAction(form) { + return form.schema.actions.find((action) => "mailpoet" === action.type); + } + function settingsOf(action) { + const raw = action?.settings ?? {}; + return { + lists: Array.isArray(raw.lists) ? raw.lists.map(Number).filter(Boolean) : [], + email_field: typeof raw.email_field === "string" ? raw.email_field : "", + first_name_field: typeof raw.first_name_field === "string" ? raw.first_name_field : "", + last_name_field: typeof raw.last_name_field === "string" ? raw.last_name_field : "" + }; + } + function consentOf(action) { + if (!action?.logic?.enabled) { + return SENTINEL_EVERYONE; + } + return action.logic.rules[0]?.field ?? SENTINEL_EVERYONE; + } + function guessField(fields, types, namePattern) { + const byType = fields.find((field) => types.includes(field.type)); + if (byType) { + return byType.id; + } + if (namePattern) { + return fields.find((field) => namePattern.test(field.label))?.id ?? ""; + } + return ""; + } + function formCard(summary, info, host) { + const card = el("section", { class: "atfm-form" }); + const paintClosed = (subscribed) => { + clear(card); + card.classList.remove("is-open"); + card.append( + el("div", { + class: "atfm-form__head", + children: [ + el("div", { + class: "atfm-form__title", + children: [ + el("strong", { text: summary.title || `Form ${summary.id}` }), + subscribed.length ? el("span", { + class: "atfm-form__status is-on", + text: `Subscribing → ${subscribed.join(", ")}` + }) : el("span", { class: "atfm-form__status", text: "Not connected" }) + ] + }), + button(subscribed.length ? "Edit" : "Connect", () => void openEditor(), "secondary") + ] + }) + ); + }; + const openEditor = async () => { + card.classList.add("is-open"); + clear(card); + card.append(el("p", { class: "atfm-hint", text: "Loading the form…" })); + let form; + try { + form = await api.getForm(summary.id); + } catch (error) { + clear(card); + card.append(el("p", { class: "atfm-hint", text: "Could not load this form." })); + return; + } + const fields = form.schema.fields.filter((field) => "page_break" !== field.type); + const action = findAction(form); + const settings = settingsOf(action); + const chosen = new Set(settings.lists.filter((id) => info.lists.some((list) => list.id === id))); + let consent = consentOf(action); + let email = settings.email_field || guessField(fields, EMAIL_TYPES, /mail/i); + let firstName = settings.first_name_field; + let lastName = settings.last_name_field; + if (!fields.some((field) => field.id === email)) { + email = guessField(fields, EMAIL_TYPES, /mail/i); + } + const consentable = fields.filter((field) => CONSENT_TYPES.includes(field.type)); + if (!action && consentable.length) { + consent = consentable[0].id; + } + const fieldOptions = (included, none) => [ + { value: "", label: none }, + ...included.map((field) => ({ value: field.id, label: field.label || field.id })) + ]; + const listsBox = el("div", { class: "atfm-lists" }); + const paintLists = () => { + clear(listsBox); + for (const list of info.lists) { + const tick = el("input", { + attrs: { type: "checkbox" } + }); + tick.checked = chosen.has(list.id); + tick.addEventListener("change", () => { + if (tick.checked) { + chosen.add(list.id); + } else { + chosen.delete(list.id); + } + }); + listsBox.append( + el("label", { + class: "atfm-list", + children: [tick, el("span", { text: list.name })] + }) + ); + } + }; + paintLists(); + const save = async (remove = false) => { + const actions = form.schema.actions.filter((candidate) => "mailpoet" !== candidate.type); + if (!remove) { + if (!chosen.size) { + notify("Pick a list", "A subscription needs at least one MailPoet list.", "error"); + return; + } + if (!email) { + notify("Pick the email field", "MailPoet needs to know which answer is the address.", "error"); + return; + } + actions.push({ + id: ACTION_ID, + type: "mailpoet", + enabled: true, + logic: consent === SENTINEL_EVERYONE ? { enabled: false, action: "show", match: "all", rules: [] } : { + enabled: true, + action: "show", + match: "all", + rules: [{ field: consent, operator: "not_empty", value: "" }] + }, + settings: { + lists: [...chosen], + email_field: email, + first_name_field: firstName, + last_name_field: lastName + } + }); + } + form.schema.actions = actions; + try { + await api.updateForm(form.id, { schema: form.schema }); + } catch (error) { + notify("Could not save", error instanceof Error ? error.message : "", "error"); + return; + } + notify( + remove ? "Disconnected" : "Connected to MailPoet", + remove ? `${summary.title} no longer subscribes anyone.` : `${summary.title} now subscribes opted-in visitors.` + ); + paintClosed(remove ? [] : info.lists.filter((list) => chosen.has(list.id)).map((list) => list.name)); + }; + clear(card); + card.append( + el("div", { + class: "atfm-form__head", + children: [ + el("div", { + class: "atfm-form__title", + children: [el("strong", { text: summary.title || `Form ${summary.id}` })] + }), + button("Close", () => paintClosed(action ? info.lists.filter((l) => settingsOf(action).lists.includes(l.id)).map((l) => l.name) : []), "ghost") + ] + }), + el("div", { + class: "atfm-form__body", + children: [ + row("Lists", listsBox, "Where the subscriber lands. MailPoet sends its own confirmation email first."), + row( + "Email address", + select(email, fieldOptions(fields, "— pick a field —"), (value) => { + email = value; + }), + "The answer that carries their address." + ), + row( + "First name", + select(firstName, fieldOptions(fields, "— none —"), (value) => { + firstName = value; + }) + ), + row( + "Last name", + select(lastName, fieldOptions(fields, "— none —"), (value) => { + lastName = value; + }) + ), + row( + "Subscribe", + select( + consent, + [ + ...consentable.map((field) => ({ + value: field.id, + label: `Only when “${field.label || field.id}” is ticked` + })), + { value: SENTINEL_EVERYONE, label: "Everyone who submits" } + ], + (value) => { + consent = value; + } + ), + consentable.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." + ), + el("div", { + class: "atfm-form__actions", + children: action ? [button("Save connection", () => void save(), "primary"), button("Disconnect", () => void save(true), "danger")] : [button("Save connection", () => void save(), "primary")] + }) + ] + }) + ); + }; + paintClosed([]); + void api.getForm(summary.id).then((form) => { + const action = findAction(form); + if (action && !card.classList.contains("is-open")) { + const names = info.lists.filter((list) => settingsOf(action).lists.includes(list.id)).map((list) => list.name); + paintClosed(names); + } + }).catch(() => void 0); + host.append(card); + return card; + } + function hero(info) { + const media = info.logo ? el("span", { + class: "atfm-hero__logo", + children: [ + el("img", { + attrs: { src: info.logo, alt: "MailPoet", width: "108", height: "65" } + }) + ] + }) : el("span", { class: "atfm-hero__logo atfm-hero__logo--glyph", text: "📬" }); + return el("header", { + class: "atfm-hero", + children: [ + media, + el("div", { + class: "atfm-hero__words", + children: [ + el("h1", { text: "Grow your audience where it begins" }), + el("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." + }), + info.active ? el("p", { + class: "atfm-hero__status is-on", + text: 1 === info.lists.length ? "Connected — 1 list available" : `Connected — ${info.lists.length} lists available` + }) : el("p", { + class: "atfm-hero__status", + text: "MailPoet is not installed on this site yet." + }) + ] + }) + ] + }); + } + async function mountMailPoetHub() { + const root = document.querySelector("[data-atfm-root]"); + if (!root || root.dataset.atfmMounted) { + return; + } + root.dataset.atfmMounted = "1"; + pinWindowBodyScroll(root); + const bar = root.querySelector("[data-atfm-bar]"); + const body = root.querySelector("[data-atfm-body]") ?? root; + try { + const [info, forms] = await Promise.all([api.mailpoet(), api.listForms()]); + bar?.remove(); + clear(body); + body.append(hero(info)); + if (!info.active) { + body.append( + el("div", { + class: "atfm-pitch", + children: [ + el("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." + }), + el("a", { + class: "atfm-pitch__cta", + text: "Install MailPoet", + attrs: { href: info.adminUrl, target: "_blank", rel: "noreferrer" } + }) + ] + }) + ); + return; + } + if (!info.lists.length) { + body.append( + el("div", { + class: "atfm-pitch", + children: [ + el("p", { + text: "MailPoet is here, but it has no list yet. Make one, and your forms will have somewhere to send subscribers." + }), + el("a", { + class: "atfm-pitch__cta", + text: "Open MailPoet lists", + attrs: { href: info.adminUrl, target: "_blank", rel: "noreferrer" } + }) + ] + }) + ); + return; + } + const section = el("section", { + class: "atfm-forms", + children: [ + el("h2", { text: "Your forms" }), + el("p", { + class: "atfm-hint", + text: "Pick a form, choose its lists, and bind the subscription to an opt-in the visitor actually ticks." + }) + ] + }); + body.append(section); + if (!forms.length) { + section.append(el("p", { class: "atfm-hint", text: "No forms yet — make one, then come back to connect it." })); + return; + } + for (const summary of forms) { + formCard(summary, info, section); + } + } catch (error) { + if (bar) { + bar.textContent = "Could not reach the site — reload the window to try again."; + } + } + } + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", () => void mountMailPoetHub()); + } else { + void mountMailPoetHub(); + } + document.addEventListener("os-window-content-loaded", () => void mountMailPoetHub()); const SUCCESS_STYLE_ICONS = { plain: "", simple: "✓", diff --git a/assets/js/builder.min.js b/assets/js/builder.min.js index 6f0bcb8..2670d53 100644 --- a/assets/js/builder.min.js +++ b/assets/js/builder.min.js @@ -1,3 +1,3 @@ -var allTerrainFormsBuilder=function(ee){"use strict";class Wt{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,n=s.clientX,i=s.clientY;let r=!1,l=!1,c=null,d=null,h=0,w=0;const v=()=>{document.removeEventListener("pointermove",f),document.removeEventListener("pointerup",E),document.removeEventListener("pointercancel",R),document.removeEventListener("keydown",k),window.removeEventListener("blur",R),c?.remove(),c=null,t.source.classList.remove("atf-is-dragging"),document.body.classList.remove("atf-drag-active"),d?.onLeave?.(u),d=null,this.active=null,this.lastEndMs=Date.now()},u={payload:t,isFinished:()=>l,cancel:(T="caller")=>{l||(l=!0,v(),e.onCancel?.(T))}},x=T=>{r=!0,t.source.classList.add("atf-is-dragging"),document.body.classList.add("atf-drag-active");const M=t.source.getBoundingClientRect();h=t.ghost?.offsetX??n-M.left,w=t.ghost?.offsetY??i-M.top,c=t.ghost?.element??t.source.cloneNode(!0),c.classList.add("atf-drag-ghost"),c.style.width=`${M.width}px`,document.body.appendChild(c),g(T)},g=T=>{c&&(c.style.transform=`translate3d(${T.clientX-h}px, ${T.clientY-w}px, 0)`)},f=T=>{if(l)return;if(!r){if(Math.hypot(T.clientX-n,T.clientY-i)<4)return;x(T)}g(T);const M=this.hitTest(T.clientX,T.clientY);M!==d&&(d?.onLeave?.(u),d=M,d?.onEnter?.(u))},E=T=>{if(l)return;if(!r){l=!0,v(),e.onClickOnly?.();return}const M=d;if(l=!0,v(),M&&M.accept(t)){e.onCommit?.(M),M.onDrop(u,{clientX:T.clientX,clientY:T.clientY});return}e.onCancel?.(M?"rejected":"no-target")},R=()=>u.cancel("pointercancel"),k=T=>{T.key==="Escape"&&u.cancel("escape")};return document.addEventListener("pointermove",f),document.addEventListener("pointerup",E),document.addEventListener("pointercancel",R),document.addEventListener("keydown",k),window.addEventListener("blur",R),this.active=u,u}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.lastEndMsr.right||tr.bottom)continue;const l=Ut(i.element);if(l>n){s=i,n=l;continue}l===n&&s&&zt(i.element,s.element)&&(s=i)}return s}}function Ut(a){let e=0,t=a;for(;t;)e++,t=t.parentElement;return e}function zt(a,e){return(e.compareDocumentPosition(a)&Node.DOCUMENT_POSITION_FOLLOWING)!==0}function ke(){return window.wp?.os??null}let Se=null;function K(){const a=ke();return a?.dragManager?a.dragManager:(Se||(Se=new Wt),Se)}function Yt(a){const e=n=>{const i=n.detail?.payload;return i&&a.includes(i.type)?i.source:null},t=n=>{const i=e(n);i&&(i.classList.add("atf-is-dragging"),document.body.classList.add("atf-drag-active"))},s=n=>{const i=e(n);i&&(i.classList.remove("atf-is-dragging"),document.body.classList.remove("atf-drag-active"))};return document.addEventListener("os.drag.start",t),document.addEventListener("os.drag.end",s),()=>{document.removeEventListener("os.drag.start",t),document.removeEventListener("os.drag.end",s)}}function Le(a,e,t,s,n){const i=e.getBoundingClientRect();return n&&(n.style.width=`${Math.round(i.width)}px`,n.style.maxWidth=`${Math.round(i.width)}px`,n.style.boxSizing="border-box"),{type:a,source:e,data:t,ghost:{element:n,offsetX:s.clientX-i.left,offsetY:s.clientY-i.top,hint:{neutral:"",accept:"",reject:"",hidden:!0}}}}function Ee(a,e,t,s){const n=Array.from(a.querySelectorAll(e)).filter(i=>i!==s&&!i.classList.contains("atf-drag-ghost"));for(let i=0;in.fetch(l,c,{source:"allterrain-forms"}):(l,c)=>fetch(l,c))(t,{credentials:"same-origin",...e,headers:s});if(!r.ok){let l=`Request failed with status ${r.status}.`,c="";try{const d=await r.json();l=d.message??l,c=d.code??""}catch{}throw new ce(l,r.status,c)}if(r.status!==204)return await r.json()}const z=a=>$e(a);async function Gt(a){if(!W?.wpRestUrl)throw new ce("AllTerrain Forms is not configured on this page.",0);const e=W.nonce?{"X-WP-Nonce":W.nonce}:{},t=ke(),s=Ke(W.wpRestUrl,a),n=t?.fetch?await t.fetch(s,{credentials:"same-origin",headers:e},{source:"allterrain-forms"}):await fetch(s,{credentials:"same-origin",headers:e});if(!n.ok)throw new ce(`Request failed with status ${n.status}.`,n.status);return await n.json()}function Zt(a){if(!a||!a.includes("&"))return a;const e=document.createElement("textarea");return e.innerHTML=a,e.value}const X=(a,e)=>$e(a,{method:"POST",body:JSON.stringify(e)}),de=a=>$e(a,{method:"DELETE"});function Xe(a){const e=new URLSearchParams;for(const[s,n]of Object.entries(a))n===void 0||n===""||n===!1||e.set(s,String(n));const t=e.toString();return t?`?${t}`:""}function he(a){const e=a?.schema?.settings;return e&&(!e.themeOverrides||Array.isArray(e.themeOverrides))&&(e.themeOverrides={...e.themeOverrides}),a}const D={config:()=>z("/config"),listForms:()=>z("/forms"),listArchivedForms:()=>z("/forms?archived=1"),archiveForm:a=>X(`/forms/${a}/archive`,{}),unarchiveForm:a=>X(`/forms/${a}/unarchive`,{}),getForm:a=>z(`/forms/${a}`).then(he),createForm:a=>X("/forms",a).then(he),updateForm:(a,e)=>X(`/forms/${a}`,e).then(he),duplicateForm:a=>X(`/forms/${a}/duplicate`,{}).then(he),deleteForm:a=>de(`/forms/${a}`),preview:(a,e)=>X(`/forms/${a}/preview`,e),pages:()=>Gt("wp/v2/pages?per_page=100&status=publish&orderby=title&order=asc&_fields=id,title").then(a=>a.map(e=>({id:e.id,title:Zt(e.title?.rendered??"")||`#${e.id}`}))),mergeTags:a=>z(`/forms/${a}/merge-tags`).then(e=>e.groups),analytics:(a,e="")=>z(`/forms/${a}/analytics${e?`?dimension=${encodeURIComponent(e)}`:""}`),demoStatus:()=>z("/demo"),demoSeed:a=>X("/demo",a?{count:a}:{}),demoRemove:()=>de("/demo"),listEntries:a=>z(`/entries${Xe(a)}`),getEntry:a=>z(`/entries/${a}`),updateEntry:(a,e)=>X(`/entries/${a}`,e),deleteEntry:a=>de(`/entries/${a}`),exportEntries:a=>z(`/entries/export${Xe(a)}`),listThemes:()=>z("/themes"),saveTheme:a=>X("/themes",a),deleteTheme:a=>de(`/themes/${a}`)};function o(a,e={}){const t=document.createElement(a);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,n]of Object.entries(e.attrs??{}))n===void 0||n===!1||t.setAttribute(s,n===!0?"":String(n));Object.assign(t.style,e.style??{});for(const[s,n]of Object.entries(e.on??{}))t.addEventListener(s,n);for(const s of e.children??[])s==null||s===!1||t.append(s);return t}function H(a){a.replaceChildren()}function U(a){return o("span",{class:`dashicons ${a.startsWith("dashicons-")?a:`dashicons-${a}`}`,attrs:{"aria-hidden":"true"}})}function b(a,e,t){const s=e.matches("input, select, textarea, button")?e:e.querySelector("input, select, textarea");if(s){const n=s.id||`atf-c-${Math.random().toString(36).slice(2,9)}`;return s.id=n,o("div",{class:"atfb-row",children:[o("label",{class:"atfb-row__label",text:a,attrs:{for:n}}),e,t?o("p",{class:"atfb-row__hint",text:t}):null]})}return o("div",{class:"atfb-row",children:[o("span",{class:"atfb-row__label",text:a}),e,t?o("p",{class:"atfb-row__hint",text:t}):null]})}function C(a,e,t=""){return o("input",{class:"atfb-input",type:"text",value:a,placeholder:t,on:{input:s=>e(s.target.value)}})}function G(a,e){if(Y("os-number-field")){const t=document.createElement("os-number-field");return t.setAttribute("value",a),t.classList.add("atfb-field"),t.addEventListener("os-input-change",s=>{e(String(s.detail?.value??""))}),t}return o("input",{class:"atfb-input",type:"number",value:a,on:{input:t=>e(t.target.value)}})}function ue(a,e,t=4){const s=o("textarea",{class:"atfb-input atfb-input--area",attrs:{rows:t},on:{input:n=>e(n.target.value)}});return s.value=a,s}function q(a,e,t){if(Y("os-select")&&Y("os-option")){const s=document.createElement("os-select");s.setAttribute("value",a),s.classList.add("atfb-field");for(const n of e){const i=document.createElement("os-option");i.setAttribute("value",n.value),i.textContent=n.label,s.append(i)}return s.addEventListener("os-pick",n=>{t(String(n.detail?.value??""))}),s}return o("select",{class:"atfb-input atfb-select",on:{change:s=>t(s.target.value)},children:e.map(s=>o("option",{value:s.value,text:s.label,attrs:{selected:s.value===a}}))})}function I(a,e,t){if(Y("os-checkbox-label")){const n=document.createElement("os-checkbox-label");return n.setAttribute("label",a),n.classList.add("atfb-check"),e&&n.setAttribute("checked",""),n.addEventListener("os-checkbox-change",i=>{t(!!i.detail?.checked)}),n}const s=o("input",{type:"checkbox",on:{change:n=>t(n.target.checked)}});return s.checked=e,o("label",{class:"atfb-check",children:[s,o("span",{text:a})]})}function L(a,e,t="secondary",s){const n=[s?U(s):null,o("span",{text:a})];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 r of n)r&&i.append(r);return i}return o("button",{class:`atfb-button atfb-button--${t}`,type:"button",on:{click:e},children:n})}function Y(a){return typeof customElements<"u"&&!!customElements.get(a)}const Kt=["os-button","os-checkbox-label","os-number-field","os-select","os-option","os-segmented","os-segment","os-color-field","os-empty-state"];let pe=null;function Xt(){if(pe)return pe;const a=window.wp?.os;return pe=a?.loadComponents?a.loadComponents(Kt).catch(()=>{}):Promise.resolve(),pe}async function fe(a,e=""){const t=window.wp?.os;return t?.confirm?t.confirm({title:e,message:a,danger:!0}):window.confirm(a)}function B(a,e="",t="info"){const s=window.wp?.os;if(s?.notify){s.notify({title:a,body:e,type:t});return}console.info(`[AllTerrain Forms] ${a}${e?`: ${e}`:""}`)}function Vt(a){let e=0,t;return(...s)=>{t=s,!e&&(e=window.requestAnimationFrame(()=>{e=0,a(...t)}))}}function Ce(a,e){let t=0;return(...s)=>{window.clearTimeout(t),t=window.setTimeout(()=>a(...s),e)}}function Jt(a){try{return window.localStorage.getItem(a)??""}catch{return""}}function Qt(a,e){try{window.localStorage.setItem(a,e)}catch{}}function Ve(a){const e=a.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 Je(){const a=[window];try{window.parent&&window.parent!==window&&a.push(window.parent)}catch{}return a}function es(a){const e=a.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 ts=4e3,ss=100;function ns(a,e){const t=Date.now()+ts,s=()=>{const n=es(a);if(n&&n.id!==e){typeof n.close=="function"?n.close():a.wp?.os?.windowManager?.remove?.(n.id);return}!n&&Date.now()0?{form:t}:void 0;t>0&&os(t);for(const n of Je())if(n.wp?.os?.openWindow?.(e,{source:"handoff",params:s})){window.setTimeout(()=>ns(n,e),0);return}}function is(){document.addEventListener("click",a=>{const e=a.target?.closest?.("[data-atf-open-window]");if(!e)return;a.preventDefault();const t=e.getAttribute("data-atf-open-window")??"";for(const s of Je())if(s.wp?.os?.openWindow?.(t,{source:"handoff"}))return})}const rs="allterrain-forms/requested-form";function os(a){try{window.sessionStorage.setItem(rs,String(a))}catch{}}function Qe(a){return`allterrain-forms/requested-form-${a}`}function ls(a){try{const e=window.sessionStorage.getItem(Qe(a));return window.sessionStorage.removeItem(Qe(a)),Number(e)||0}catch{return 0}}const Ae={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"},Te=["empty","not_empty"];function et(a,e){const t=a.find(s=>s.id===e);return t?t.label||"an untitled question":null}function tt(a,e,t){return a.find(i=>i.id===e)?.choices?.find(i=>i.value===t)?.label||t}function cs(a,e){const t=Ae[a.operator]??String(a.operator);if(Te.includes(a.operator))return t;const s=tt(e,a.field,a.value);return`${a.operator==="is"?"":`${t} `}${s!==""?s:"(nothing)"}`}function st(a,e,t=0){const s=et(e,a.field),n=Ae[a.operator]??String(a.operator);if(s===null)return[{kind:"field",text:"a question that no longer exists",fieldId:a.field,missing:!0}];const i=[{kind:"field",text:s,fieldId:a.field,missing:!1},{kind:"operator",text:n,operator:a.operator,ruleIndex:t}];if(Te.includes(a.operator))return i;const r=tt(e,a.field,a.value);return i.push({kind:"value",text:r!==""?r:"(nothing)",raw:a.value,sourceId:a.field,ruleIndex:t}),i}function ds(a,e){const t=a.logic;if(!t?.enabled||!t.rules.length)return[];const s=[{kind:"verb",text:t.action==="hide"?"Hidden when":"Shown when"}];return t.rules.forEach((n,i)=>{i>0&&s.push({kind:"join",text:t.match==="all"?"and":"or"}),s.push(...st(n,e,i))}),s}function nt(a){return a.map(e=>e.text).join(" ")}function hs(a,e){return nt(st(a,e))}function me(a){const e=[];for(const t of a){const s=t.logic;if(s?.enabled)for(const n of s.rules)!n.field||n.field===t.id||e.push({from:n.field,to:t.id,label:hs(n,a),short:cs(n,a),action:s.action,broken:et(a,n.field)===null})}return e}function us(a){const e=new Map,t=new Set;for(const s of me(a)){const n=`${s.from}->${s.to}`;s.broken||t.has(n)||(t.add(n),e.set(s.from,(e.get(s.from)??0)+1))}return e}const at=10,it=22,ne="http://www.w3.org/2000/svg";class ps{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 n=new ResizeObserver(t);n.observe(e),this.teardowns.push(()=>n.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 n=this.cardRect(s.from),i=this.cardRect(s.to);if(!n||!i)continue;const r=t.get(s.from)??0;t.set(s.from,r+1),this.drawEdge(s,n,i,e,r)}}cardRect(e){const t=this.host.querySelector(`[data-atfb-card="${CSS.escape(e)}"]`);return t?t.getBoundingClientRect():null}drawEdge(e,t,s,n,i){const r=t.right-n.left,l=t.top+t.height/2-n.top,c=s.right-n.left,d=s.top+s.height/2-n.top,h=Math.max(0,n.width-at-Math.max(r,c)),w=22+i*11+Math.min(22,Math.abs(d-l)/8),v=Math.max(12,Math.min(w,h*.4)),u=document.createElementNS(ne,"path");u.setAttribute("d",`M ${r} ${l} C ${r+v} ${l}, ${c+v} ${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 x=document.createElementNS(ne,"title");x.textContent=e.label,u.append(x);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 f=document.createElementNS(ne,"text");f.setAttribute("x",String(n.width-at)),f.setAttribute("y",String(d+3)),f.setAttribute("text-anchor","end"),f.setAttribute("class","atfb-logicmap__label"),f.dataset.from=e.from,f.dataset.to=e.to,f.textContent=e.short.length>it?`${e.short.slice(0,it-1)}…`:e.short;const E=document.createElementNS(ne,"title");E.textContent=e.label,f.append(E),this.svg.append(f)}}const fs={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(a){return fs[a]??"text"}function rt(a,e){const t=/^choice:(\d+):(label|value)$/.exec(e);return String(t?a.choices?.[Number(t[1])]?.[t[2]]??"":a[e]??"")}function V(a){const{value:e,onInput:t,onCommit:s}=a,n=o("span",{class:`${a.class} atfb-editable`,text:e,attrs:{contenteditable:"plaintext-only",role:"textbox",spellcheck:"false","data-placeholder":a.placeholder}});return a.bind&&(n.dataset.atfbBind=a.bind),n.addEventListener("pointerdown",i=>i.stopPropagation()),n.addEventListener("input",()=>t(n.textContent??"")),n.addEventListener("keydown",i=>{i.key==="Enter"&&(i.preventDefault(),n.blur()),i.key==="Escape"&&(n.textContent=e,n.blur()),i.stopPropagation()}),n.addEventListener("blur",()=>s?.()),n}function bs(a){const e=o("input",{class:"atf-choice__input",type:a==="checkboxes"||a==="multiselect"?"checkbox":"radio"});return e.disabled=!0,e}function ot(a,e,t){const s=ms(a.type),n=V({value:a.label,placeholder:"Write the question…",class:"atf-label",bind:"label",onInput:r=>t.edit(l=>{l.label=r}),onCommit:()=>t.restructure(()=>{})}),i=[s==="static"||s==="toggle"?null:n,vs(a,e,s,t),gs(a,e,t)];return o("div",{class:"atfb-preview atf-form",children:[o("div",{class:`atf-field atf-field--${a.type}`,children:i})]})}function gs(a,e,t){if(!e?.supports.includes("hint"))return null;const s=V({value:a.hint??"",placeholder:"Add a hint…",class:"atf-hint",bind:"hint",onInput:n=>t.edit(i=>{i.hint=n})});return s.setAttribute("aria-label","Hint"),o("p",{class:"atfb-preview__hint",children:[s]})}function vs(a,e,t,s){switch(t){case"text":return Me(a,"atf-input",s);case"textarea":return Me(a,"atf-input atf-textarea",s,!0);case"select":return o("div",{class:"atfb-preview__select",children:[Me(a,"atf-input atf-select",s)]});case"options":return _s(a,s);case"toggle":return o("div",{class:a.type==="switch"?"atf-toggle atf-toggle--switch":"atf-toggle",children:[(()=>{const n=o("input",{class:"atf-toggle__input",type:"checkbox"});return n.disabled=!0,n})(),V({value:a.label||"",placeholder:a.type==="consent"?"What are they agreeing to?…":"What does this turn on?…",class:"atf-toggle__label",bind:"label",onInput:n=>s.edit(i=>{i.label=n}),onCommit:()=>s.restructure(()=>{})})]});case"file":return o("div",{class:"atf-file",children:[o("input",{class:"atf-file__input",type:"file"})]});case"range":return o("input",{class:"atf-range__input",type:"range"});case"composite":return o("div",{class:"atf-composite__parts",children:[o("span",{class:"atf-input atfb-preview__ghost",text:""}),o("span",{class:"atf-input atfb-preview__ghost",text:""})]});case"static":return ks(a,e,s);case"repeater":return ys(a,s);default:return o("div",{class:"atfb-preview__stack",children:[o("p",{class:"atfb-preview__summary",text:`${e?.label??a.type} — set this up in the panel on the right.`})]})}}function ys(a,e){const t=a.fields??[],s=o("div",{class:"atfb-repeater__zone",attrs:{"data-atfb-repeater-zone":a.id}});return t.forEach(n=>s.append(ws(a,n,e))),t.length||s.append(o("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."})),o("div",{class:"atfb-repeater",attrs:{"data-atfb-repeater":a.id},children:[o("div",{class:"atf-repeater__row atfb-repeater__frame",children:[o("div",{class:"atf-repeater__row-head",children:[o("span",{class:"atf-repeater__title atfb-repeater__title",children:[V({value:String(a.itemLabel??""),placeholder:"Row",class:"atfb-repeater__item-label",bind:"itemLabel",onInput:n=>e.edit(i=>{i.itemLabel=n})}),o("span",{class:"atfb-repeater__ordinal",text:"1",attrs:{"aria-hidden":"true"}})]})]}),s]}),xs(a,e)]})}function ws(a,e,t){const s=e.id,n=l=>c=>{const d=(c.fields??[]).find(h=>h.id===s);d&&l(d)},i={edit:l=>t.edit(n(l)),restructure:l=>t.restructure(n(l)),types:t.types,selectedId:t.selectedId},r=t.types?.(e.type);return o("div",{class:`atfb-subcard${t.selectedId===s?" is-selected":""}`,attrs:{"data-atfb-subfield":s,"data-atfb-parent":a.id,tabindex:"0",role:"button","aria-label":`${e.label||r?.label||e.type}, inside ${a.label||"the repeater"}`},children:[o("div",{class:"atfb-subcard__head",children:[o("span",{class:"atfb-subcard__type",text:r?.label??e.type}),e.required?o("span",{class:"atfb-subcard__required",text:"Required"}):null,o("button",{class:"atfb-preview__remove",type:"button",title:"Remove this field from the repeater",attrs:{"aria-label":`Remove ${e.label||r?.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:[o("span",{text:"×"})]})]}),ot(e,r,i)]})}function Me(a,e,t,s=!1){const n=V({value:a.placeholder??"",placeholder:a.type==="select"||a.type==="country"?"Choose…":"Placeholder…",class:`${e} atfb-preview__box${s?" atfb-preview__box--tall":""}`,bind:"placeholder",onInput:i=>t.edit(r=>{r.placeholder=i})});return n.setAttribute("aria-label","Placeholder"),n}function Fe(a,e,t,s,n,i){return o("span",{class:`atf-button ${t} atfb-preview__button`,children:[V({value:a,placeholder:e,class:"atfb-preview__button-text",bind:s,onInput:r=>n.edit(l=>i(l,r))})]})}function xs(a,e){return Fe(String(a.addLabel??""),"Add another","atf-button--ghost atfb-repeater__add","addLabel",e,(t,s)=>{t.addLabel=s})}function _s(a,e){const t=o("div",{class:"atf-choices__list"});return(a.choices??[]).forEach((s,n)=>{t.append(o("div",{class:"atf-choice atfb-preview__option",children:[bs(a.type),V({value:s.label,placeholder:"Option…",class:"atf-choice__label",bind:`choice:${n}:label`,onInput:i=>{e.edit(r=>{const l=r.choices?.[n];if(!l)return;const c=!l.value||l.value===l.label;l.label=i,c&&(l.value=i)})}}),o("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(r=>{r.choices.splice(n,1)})}},children:[o("span",{text:"×"})]})]}))}),t.append(o("button",{class:"atfb-preview__add",type:"button",on:{pointerdown:s=>s.stopPropagation(),click:s=>{s.stopPropagation(),e.restructure(n=>{const i=n.choices.length+1;n.choices.push({label:`Option ${i}`,value:`Option ${i}`})})}},children:[o("span",{text:"+ Add option"})]})),o("fieldset",{class:"atf-choices",children:[t]})}function ks(a,e,t){return a.type==="heading"?V({value:a.label,placeholder:"Section heading…",class:"atf-heading",bind:"label",onInput:s=>t.edit(n=>{n.label=s}),onCommit:()=>t.restructure(()=>{})}):a.type==="divider"?o("hr",{class:"atf-divider"}):a.type==="page_break"?o("div",{class:"atfb-preview__stack",children:[o("p",{class:"atfb-progress-name",children:[V({value:a.label,placeholder:"Name this step…",class:"atf-progress__label",bind:"label",onInput:s=>t.edit(n=>{n.label=s}),onCommit:()=>t.restructure(()=>{})})]}),o("p",{class:"atfb-preview__summary",text:"Everything after this is a new page."}),o("div",{class:"atf-nav atfb-preview__nav",children:[Fe(String(a.prevLabel??""),"Back","atf-button--secondary","prevLabel",t,(s,n)=>{s.prevLabel=n}),Fe(String(a.nextLabel??""),"Next","","nextLabel",t,(s,n)=>{s.nextLabel=n})]})]}):o("p",{class:"atfb-preview__summary",text:`${e?.label??a.type} — nothing is shown to the visitor here.`})}const Ie=new Map;function De(a){let e=Ie.get(a);return e||(e=D.mergeTags(a).catch(()=>[]),Ie.set(a,e)),e}function Ss(a){Ie.delete(a)}function Ls(a){const e=new Map;for(const t of a)for(const s of t.items)e.set(s.tag,s);return e}function Es(a,e){const t=Ls(e);return a.replace(/\{[a-z_]+(?::[^}]*)?\}/gi,s=>{const n=t.get(s.toLowerCase());return n?n.sample:s})}function $s(a){return/\{[a-z_]+(?::[^}]*)?\}/i.test(a)}let te=null;function ie(){te?.remove(),te=null}typeof document<"u"&&(document.addEventListener("pointerdown",a=>{const e=a.target;e?.closest(".atfb-tagpick__open")||te&&!te.contains(e)&&ie()}),document.addEventListener("keydown",a=>{a.key==="Escape"&&te&&(ie(),a.stopPropagation())}));function lt(a,e){const t=a.selectionStart??a.value.length,s=a.selectionEnd??a.value.length;a.value=a.value.slice(0,t)+e+a.value.slice(s);const n=t+e.length;a.setSelectionRange(n,n),a.focus(),a.dispatchEvent(new Event("input",{bubbles:!0}))}function Cs(a){let e=a.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 As(a,e){const t=o("input",{class:"atfb-input atfb-tagpick__search",type:"search",placeholder:"Search values…",attrs:{"aria-label":"Search values"}}),s=o("div",{class:"atfb-tagpick__list"}),n=i=>{s.replaceChildren();const r=i.trim().toLowerCase();let l=0;for(const c of a){const d=c.items.filter(h=>!r||h.label.toLowerCase().includes(r)||h.tag.toLowerCase().includes(r));if(!d.length){c.empty&&!r&&!c.items.length&&s.append(o("p",{class:"atfb-tagpick__group",text:c.label}),o("p",{class:"atfb-tagpick__empty",text:c.empty}));continue}s.append(o("p",{class:"atfb-tagpick__group",text:c.label}));for(const h of d)l+=1,s.append(o("button",{class:"atfb-tagpick__item",type:"button",on:{click:()=>{e(h.tag),ie()}},children:[o("span",{class:"atfb-tagpick__item-main",children:[o("span",{class:"atfb-tagpick__label",text:h.label}),o("code",{class:"atfb-tagpick__tag",text:h.tag})]}),h.hint||h.sample?o("span",{class:"atfb-tagpick__meta",text:h.sample?`e.g. ${h.sample}`:h.hint}):null]}))}!l&&r&&s.append(o("p",{class:"atfb-tagpick__empty",text:`Nothing matches “${i}”.`}))};return n(""),t.addEventListener("input",()=>n(t.value)),o("div",{class:"atfb-tagpick",attrs:{role:"dialog","aria-label":"Insert a value"},children:[o("p",{class:"atfb-tagpick__intro",text:"Pick something to drop in. It is filled in when the form is submitted."}),t,s]})}function ct(a,e){const t=o("button",{class:"atfb-button atfb-button--ghost atfb-tagpick__open",type:"button",title:"Insert a value from the submission",children:[U("shortcode"),o("span",{text:"Insert a value"})]}),s=o("div",{class:"atfb-taggable",children:[a,o("div",{class:"atfb-taggable__tools",children:[t]})]}),n=e.preview===!1?null:o("p",{class:"atfb-taggable__preview"});n&&s.append(n);const i=()=>{if(n){if(!$s(a.value)){n.textContent="",n.hidden=!0;return}De(e.formId).then(r=>{n.hidden=!1,n.replaceChildren(o("span",{class:"atfb-taggable__preview-label",text:"Reads as"}),o("span",{text:Es(a.value,r)}))})}};return a.addEventListener("input",i),i(),t.addEventListener("click",r=>{if(r.stopPropagation(),te&&s.contains(te)){ie();return}ie(),De(e.formId).then(l=>{const c=As(l,d=>{lt(a,d),i()});s.append(c),te=c,(window.innerHeight-c.getBoundingClientRect().topCs(s))&&c.classList.add("atfb-tagpick--above"),c.querySelector(".atfb-tagpick__search")?.focus()})}),s}function dt(a,e){const t=parseFloat(String(a??""));return Number.isFinite(t)?t:e}function ht(a,e){let t=e[0];for(const s of e)Math.abs(s.at-a)a.accent??"#2271b1",apply:a=>({accent:a,"accent-soft":`color-mix( in srgb, ${a} 12%, transparent )`,"border-focus":a,"focus-ring-color":a,"button-bg":a,"button-bg-hover":`color-mix( in srgb, ${a} 88%, #000 )`})},{id:"roundness",label:"Roundness",hint:"Corners, everywhere at once.",kind:"scale",steps:qe.map(({value:a,label:e})=>({value:a,label:e})),owns:["radius-field","radius-button","radius-card","radius-check"],read:a=>ht(dt(a["radius-field"],4),qe),apply:a=>{const e=qe.find(t=>t.value===a)?.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:Oe.map(({value:a,label:e})=>({value:a,label:e})),owns:["pad-field-x","pad-field-y","gap-fields","gap-label","button-pad-x","button-pad-y"],read:a=>ht(dt(a["pad-field-y"],9),Oe),apply:a=>{const e=Oe.find(t=>t.value===a)?.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:Ts,owns:["shadow-field","shadow-field-focus","shadow-button","shadow-button-hover","shadow-card"],read:a=>{const e=a["shadow-card"]??"none";return e==="none"||e.trim()===""?a["shadow-field"]&&a["shadow-field"]!=="none"?"hard":"none":e.includes("0 1px")||e.includes("2px")?"subtle":"lifted"},apply:a=>{switch(a){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:Ms,owns:["field-style"],read:a=>a["field-style"]??"outline",apply:a=>({"field-style":a})},{id:"labels",label:"Labels",hint:"Where the question sits relative to the answer.",kind:"choice",steps:Fs,owns:["label-position"],read:a=>a["label-position"]??"top",apply:a=>({"label-position":a})}]}function Is(a){return Re().find(e=>e.owns.includes(a))??null}function ut(a){let e=a.activeSlug,t={...a.overrides},s=a.themes;const n=p=>{t=p,a.onOverridesReplaced?.({...p})},i=o("div",{class:"atfs-preview__frame"}),r=o("div",{class:"atfs-controls__body"}),l=o("div",{class:"atfs-quick"}),c=o("div",{class:"atfs-themes"}),d=Ce(async()=>{try{const p=await a.previewFor(e,t),m=i.closest(".atf-studio__preview"),y=m?.scrollTop??0;i.innerHTML=p,m&&(m.scrollTop=y),document.dispatchEvent(new CustomEvent("atf-refresh"))}catch(p){H(i),i.append(o("p",{class:"atfb-error",text:p instanceof Error?p.message:"Preview failed."}))}},180),h={"label-position":"atf-labels-","field-style":"atf-fields-"},w=(p,m,y)=>{const _=y.replace(/[^a-z0-9_-]/gi,"");for(const S of[...p.classList])S.startsWith(m)&&p.classList.remove(S);_&&p.classList.add(m+_)},v=p=>{const m=i.querySelector(".atf-form-wrap"),y=m?.querySelector(".atf-form");if(m)for(const[_,S]of Object.entries(p))S===""?m.style.removeProperty(`--atf-${_}`):m.style.setProperty(`--atf-${_}`,S),y&&h[_]&&w(y,h[_],S||(s.find(O=>O.slug===e)?.resolved[_]??""))},u=p=>{v(p.resolved);const m=i.querySelector(".atf-form");m&&(w(m,"atf-theme-",p.slug),m.classList.toggle("atf-is-dark",!!p.dark))},x=p=>t[p.token]!==void 0?t[p.token]:s.find(y=>y.slug===e)?.resolved?.[p.token]??p.default,g=()=>{H(c);for(const p of s){const m=o("button",{class:`atfs-theme${p.slug===e?" is-active":""}`,type:"button",attrs:{"aria-pressed":p.slug===e,title:p.description},children:[o("span",{class:"atfs-theme__chip",style:{background:p.resolved.surface??"#fff",borderColor:p.resolved.border??"#ccc",borderRadius:p.resolved["radius-field"]??"4px",boxShadow:p.resolved["shadow-card"]??"none"},children:[o("span",{class:"atfs-theme__accent",style:{background:p.resolved.accent??"#2271b1",borderRadius:p.resolved["radius-button"]??"4px"}})]}),o("span",{class:"atfs-theme__name",text:p.label}),p.custom?o("span",{class:"atfb-badge",text:"yours"}):null],on:{click:()=>{e=p.slug,n({}),a.onTheme(e),u(p),g(),k(),R(),Bt()}}});c.append(m)}},f=()=>{const m={...s.find(y=>y.slug===e)?.resolved??{}};for(const[y,_]of Object.entries(t))m[y]=_;return m},E=p=>{const m=l.closest(".atf-studio__controls"),y=m?.scrollTop??0;p(),m&&(m.scrollTop=y)},R=Ce(()=>E($),150),k=()=>{const p=f(),m=l.querySelectorAll(".atfs-dial");Re().forEach((y,_)=>{const S=m[_];if(!S)return;const O=y.read(p);if(y.kind==="colour"){const F=S.querySelector('input[type="color"]'),Z=S.querySelector("input.atfb-input");F&&document.activeElement!==F&&(F.value=be(O)),Z&&document.activeElement!==Z&&(Z.value=O);return}S.querySelectorAll(".atfs-segment").forEach((F,Z)=>{const Q=y.steps?.[Z]?.value===O;F.classList.toggle("is-on",Q),F.setAttribute("aria-pressed",String(Q))})})},T=(p,m)=>{const y=p.apply(m,f());for(const[_,S]of Object.entries(y))t[_]=S,a.onOverride(_,S);v(y),k(),R()},M=()=>{H(l);const p=f();for(const m of Re()){const y=m.read(p);let _;if(m.kind==="colour"){const S=o("input",{class:"atfs-color",type:"color",value:be(y),attrs:{"aria-label":m.label},on:{input:O=>T(m,O.target.value)}});_=o("div",{class:"atfs-color-row",children:[S,C(y,O=>T(m,O))]})}else _=o("div",{class:`atfs-segmented atfs-segmented--${m.kind}`,attrs:{role:"group","aria-label":m.label},children:(m.steps??[]).map(S=>o("button",{class:`atfs-segment${S.value===y?" is-on":""}`,type:"button",text:S.label,attrs:{"aria-pressed":S.value===y},on:{click:()=>T(m,S.value)}}))});l.append(o("div",{class:"atfs-dial",children:[o("div",{class:"atfs-dial__head",children:[o("span",{class:"atfs-dial__label",text:m.label}),o("span",{class:"atfs-dial__hint",text:m.hint})]}),_]}))}},$=()=>{H(r);const p=new Map;for(const y of a.tokens){const _=p.get(y.group)??[];_.push(y),p.set(y.group,_)}const m={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[y,_]of p)r.append(o("details",{class:"atfs-group",attrs:{open:y==="colour"},children:[o("summary",{text:m[y]??y}),..._.map(S=>N(S))]}))},N=p=>{const m=x(p),y=F=>{F===""?delete t[p.token]:t[p.token]=F,a.onOverride(p.token,F),v({[p.token]:F})};let _;if(p.control==="color"){const F=o("input",{class:"atfs-color",type:"color",value:be(m),attrs:{"aria-label":`${p.label} colour`},on:{input:Q=>{const le=Q.target.value;Z.value=le,y(le)}}}),Z=C(m,Q=>{F.value=be(Q),y(Q)});_=o("div",{class:"atfs-color-row",children:[F,Z]})}else if(p.control==="select")_=q(m,(p.options??[]).map(F=>({value:F,label:F})),y);else if(p.control==="length"){const F=parseFloat(m)||0,Z=p.unit??"px",Q=p.token.includes("radius")?60:80,le=o("input",{class:"atfs-range",type:"range",value:String(F),attrs:{min:"0",max:String(Q),step:"1","aria-label":p.label},on:{input:_e=>{const jt=`${_e.target.value}${Z}`;Ht.value=jt,y(jt)}}}),Ht=C(m,_e=>{le.value=String(parseFloat(_e)||0),y(_e)});_=o("div",{class:"atfs-length-row",children:[le,Ht]})}else _=C(m,y);const S=b(p.label,_),O=Is(p.token);return O&&(S.classList.add("is-dialled"),S.append(o("span",{class:"atfs-owned",text:O.label}))),t[p.token]!==void 0&&(S.classList.add("is-overridden"),S.append(o("button",{class:"atfs-reset",type:"button",text:"Reset",attrs:{"aria-label":`Reset ${p.label} to the theme's value`},on:{click:()=>{y(""),E($)}}}))),S},A=async()=>{const p=s.find(_=>_.slug===e),m=p?`${p.label} (mine)`:"My theme",y=window.prompt("Name this theme",m);if(y)try{const _={};for(const O of a.tokens){const F=x(O);F!==O.default&&(_[O.token]=F)}const S=await D.saveTheme({label:y,tokens:_});s=[...s.filter(O=>O.slug!==S.slug),S],a.onThemesChanged(s),e=S.slug,n({}),a.onTheme(e),g(),k(),E($),B("Theme saved",S.label)}catch(_){B("Could not save the theme",_ instanceof Error?_.message:"","error")}},j=async()=>{const p=s.find(m=>m.slug===e);if(p?.custom&&await fe(`Delete “${p.label}”? Forms using it fall back to Clean.`,"Delete theme"))try{await D.deleteTheme(p.id),s=s.filter(y=>y.slug!==p.slug),a.onThemesChanged(s),e="clean",n({}),a.onTheme(e),g(),k(),E($);const m=s.find(y=>y.slug===e);m&&u(m)}catch(m){B("Could not delete the theme",m instanceof Error?m.message:"","error")}},P=async()=>{const p=s.find(_=>_.slug===e),m={label:p?.label??e,tokens:{...p?.tokens??{},...t}},y=JSON.stringify(m,null," ");try{await navigator.clipboard.writeText(y),B("Theme copied","Paste it into another site to import it.")}catch{window.prompt("Copy this theme",y)}},oe=()=>{const p=window.prompt("Paste a theme");if(p)try{const m=JSON.parse(p);if(!m.tokens||typeof m.tokens!="object")throw new Error("That does not look like a theme.");for(const[y,_]of Object.entries(m.tokens))t[y]=String(_),a.onOverride(y,String(_));v({...t}),k(),E($)}catch(m){B("Could not read that theme",m instanceof Error?m.message:"","error")}},Pt=L("Delete",()=>void j(),"danger"),Bt=()=>{const p=s.find(m=>m.slug===e);Pt.hidden=!p?.custom};return g(),M(),$(),d(),Bt(),o("div",{class:"atf-studio",children:[o("div",{class:"atf-studio__top",children:[o("div",{class:"atf-studio__topbar",children:[o("h2",{class:"atf-studio__heading",text:"Theme"}),o("div",{class:"atfs__actions",children:[L("Save as a theme",()=>void A(),"primary"),L("Export",()=>void P()),L("Import",oe),Pt]})]}),c]}),o("div",{class:"atf-studio__panes",children:[o("aside",{class:"atf-studio__controls",children:[o("p",{class:"atfb-hint",text:"Changes apply to this form only, until you save them as a theme."}),l,o("details",{class:"atfs-advanced",children:[o("summary",{text:"Every setting"}),o("p",{class:"atfb-hint",text:"The full token list. The dials above write into it."}),r]})]}),o("main",{class:"atf-studio__preview",children:[o("h2",{class:"screen-reader-text",text:"Live preview"}),i]})]})]})}async function Ne(){const a=document.querySelector("[data-atfs-root]");if(!a||a.dataset.atfsMounted)return;a.dataset.atfsMounted="1",Ve(a);const e=a.querySelector("[data-atfs-bar]"),t=a.querySelector(".atfs__body")??a;try{const[s,n,i]=await Promise.all([D.config(),D.listThemes(),D.listForms()]);if(!i.length){H(t),t.append(o("div",{class:"atfb-empty",children:[o("h2",{text:"No forms to preview"}),o("p",{text:"Make a form first — a theme needs something to dress."})]}));return}let r=i[0],l={},c=r.theme;const d=()=>{H(t),t.append(ut({themes:n,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 v=await D.getForm(r.id);v.schema.settings.theme=h,v.schema.settings.themeOverrides=w;const{html:u}=await D.preview(r.id,{schema:v.schema,theme:h});return u},onThemesChanged:h=>{n.length=0,n.push(...h)}}))};e&&(H(e),e.append(o("span",{class:"atfs__label",text:"Preview against"}),q(String(r.id),i.map(h=>({value:String(h.id),label:h.title||"(untitled)"})),h=>{r=i.find(w=>String(w.id)===h)??i[0],l={},d()}))),d()}catch(s){H(t),t.append(o("p",{class:"atfb-error",text:s instanceof Error?s.message:"Could not load themes."}))}}typeof document<"u"&&(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>void Ne()):Ne(),document.addEventListener("os-window-content-loaded",()=>void Ne()));function be(a){const e=a.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 ge={plain:"",simple:"✓",minimal:"",card:"🎉",check:"",confetti:"🎉",fireworks:"🎆",sparkles:"✨",typewriter:""};function pt(){return{style:"simple",title:"",icon:"",accent:"",intensity:"medium",showButton:!1,buttonLabel:""}}function Pe(a){const e={...pt(),...a??{}};return e.style in ge||(e.style="simple"),e}function Ds(){return typeof window.matchMedia=="function"&&window.matchMedia("(prefers-reduced-motion: reduce)").matches}function qs(a,e,t){const s=Pe(e),n=document.createElement("div");if(n.className=`atf-confirmation atf-success atf-success--${s.style}`,n.setAttribute("role","status"),n.setAttribute("tabindex","-1"),s.style==="plain")return n.className="atf-confirmation",n.innerHTML=a,n;s.accent&&n.style.setProperty("--atf-accent",s.accent);const i=document.createElement("div");i.className="atf-success__inner",n.append(i);const r=s.icon||ge[s.style];if(s.style==="check")i.insertAdjacentHTML("beforeend",'');else if(r){const c=document.createElement("span");c.className="atf-success__icon",c.setAttribute("aria-hidden","true"),c.textContent=r,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=a,i.append(l),s.style==="typewriter"&&n.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 n}function Os(a,e){const t=Pe(e);if(Ds())return()=>{};switch(t.style){case"confetti":return Rs(a,t);case"fireworks":return Ns(t);case"sparkles":return Ps(a,t);case"typewriter":return Bs(a);default:return()=>{}}}function re(a){return{low:.5,medium:1,high:1.8}[a.intensity]}function ft(){const a=document.createElement("canvas");a.className="atf-success-canvas",a.setAttribute("aria-hidden","true");const e=Math.min(window.devicePixelRatio||1,2);a.width=Math.floor(window.innerWidth*e),a.height=Math.floor(window.innerHeight*e),document.body.append(a);const t=a.getContext("2d");return t?.scale(e,e),{canvas:a,ctx:t,stop:()=>a.remove()}}const mt=["#f43f5e","#f59e0b","#10b981","#3b82f6","#8b5cf6","#ec4899","#eab308"];function Rs(a,e){const{ctx:t,stop:s}=ft();if(!t)return s(),()=>{};const n=e.accent?[e.accent,...mt]:mt,i=[],r=a.getBoundingClientRect(),l=r.left+r.width/2,c=Math.min(r.top+40,window.innerHeight-20),d=(E,R,k)=>{const T=k?Math.PI*(1.15+.7*Math.random()):0,M=k?9+Math.random()*8:0;return{x:E,y:R,vx:k?Math.cos(T)*M*(Math.random()<.5?1:-1):(Math.random()-.5)*1.5,vy:k?Math.sin(T)*M: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:n[Math.floor(Math.random()*n.length)],wobble:Math.random()*Math.PI*2}},h=Math.round(90*re(e));for(let E=0;E{if(v>=w){window.clearInterval(u);return}i.push(d(Math.random()*window.innerWidth,-20,!1)),v++},2e3/w);let x=0;const g=performance.now(),f=E=>{t.clearRect(0,0,window.innerWidth,window.innerHeight);let R=!1;for(const k of i)k.vy+=.16,k.vx*=.99,k.vy*=.985,k.wobble+=.1,k.x+=k.vx+Math.sin(k.wobble)*.8,k.y+=k.vy,k.angle+=k.spin,k.y{window.clearInterval(u),window.cancelAnimationFrame(x),s()}}function Ns(a){const{ctx:e,stop:t}=ft();if(!e)return t(),()=>{};const s=[],n=[],i=Math.round(6*re(a))+2;let r=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)}),r++};l();const c=window.setInterval(()=>{if(r>=i){window.clearInterval(c);return}l()},3500/i),d=u=>{const x=Math.round(70*re(a));for(let g=0;g{e.clearRect(0,0,window.innerWidth,window.innerHeight);for(let g=s.length-1;g>=0;g--){const f=s[g];f.y+=f.vy,f.vy+=.08,e.fillStyle=`hsl(${f.hue} 90% 65%)`,e.fillRect(f.x-1.5,f.y,3,10),(f.y<=f.targetY||f.vy>=-1)&&(d(f),s.splice(g,1))}for(let g=n.length-1;g>=0;g--){const f=n[g];if(f.x+=f.vx,f.y+=f.vy,f.vy+=.045,f.vx*=.985,f.vy*=.985,f.life-=f.decay,f.life<=0){n.splice(g,1);continue}const E=f.life*(.7+.3*Math.random());e.globalAlpha=Math.max(0,E),e.fillStyle=`hsl(${f.hue} 95% ${55+25*f.life}%)`,e.beginPath(),e.arc(f.x,f.y,1.1+1.6*f.life,0,Math.PI*2),e.fill()}e.globalAlpha=1,!(r>=i&&s.length===0&&n.length===0)&&u-w<9e3?h=window.requestAnimationFrame(v):t()};return h=window.requestAnimationFrame(v),()=>{window.clearInterval(c),window.cancelAnimationFrame(h),t()}}function Ps(a,e){const t=e.icon||ge.sparkles,s=Math.round(22*re(e)),n=[];let i=0;const r=()=>{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()),a.append(c),n.push(c),i++};r();const l=window.setInterval(()=>{if(i>=s){window.clearInterval(l);return}r()},2800/s);return()=>{window.clearInterval(l),n.forEach(c=>c.remove())}}function Bs(a){const e=a.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 n=Math.min(45,3800/s.length);let i=0;const r=window.setInterval(()=>{i++,e.textContent=s.slice(0,i),i>=s.length&&(window.clearInterval(r),e.classList.remove("is-typing"),e.innerHTML=t)},n);return()=>{window.clearInterval(r),e.classList.remove("is-typing"),e.innerHTML=t}}const Hs={min:-1,max:-1,sum:-1,avg:-1,round:-1,ceil:1,floor:1,abs:1,sqrt:1,pow:2},bt={"+":{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 js(a,e,t=[]){if(!a||!a.trim()||a.length>2e3)return null;const s=Ws(a,e,t),n=Gs(s);if(!n)return null;const i=Ks(n);if(!i)return null;const r=Xs(i);return r===null||!Number.isFinite(r)?null:r}function Ws(a,e,t){return a.replace(/\{([a-zA-Z0-9_]+)(?:\.([a-zA-Z0-9_]+))?\}/g,(s,n,i,r)=>Us(a,r,s.length,n,i??"",e,t))}function Us(a,e,t,s,n,i,r){const l=zs(r,s),c=Object.prototype.hasOwnProperty.call(i,s)?i[s]:null;if(n===""){const u=l?.type==="repeater"?gt(c).length:Be(c,l);return vt(u)}const h=(l?.fields??[]).find(u=>u.id===n)??null,w=gt(c).map(u=>Be(u[n]??"",h));if(!w.length)return"0";const v=w.map(vt);return Ys(a,e,t)?v.join(", "):v.length===1?v[0]:`( ${v.join(" + ")} )`}function zs(a,e){for(const t of a){if(t.id===e)return t;for(const s of t.fields??[])if(s.id===e)return s}return null}function Ys(a,e,t){const s=a.slice(e+t).replace(/^\s+/,"");if(s===""||s[0]!==")")return!1;const n=a.slice(0,e).replace(/\s+$/,""),i=/([a-zA-Z_][a-zA-Z0-9_]*)\s*\($/.exec(n);return!!i&&["sum","avg","min","max"].includes(i[1].toLowerCase())}function gt(a){if(!Array.isArray(a))return[];const e=[];for(const t of a){if(!t||typeof t!="object"||Array.isArray(t))continue;Object.values(t).some(n=>n!==""&&n!==null&&n!==void 0&&n!==!1&&!(Array.isArray(n)&&!n.length))&&e.push(t)}return e}function vt(a){const e=a.toFixed(10).replace(/0+$/,"").replace(/\.$/,"");return e===""||e==="-"?"0":e}function Be(a,e){if(typeof a=="boolean")return a?1:0;if(Array.isArray(a))return a.reduce((n,i)=>n+Be(i,e),0);if(a==null||a==="")return 0;const t=e?.choices??[];for(const n of t)if(String(n.value)===String(a)){if(typeof n.price=="number")return n.price;if(typeof n.points=="number")return n.points;break}const s=Number(a);return Number.isFinite(s)?s:0}function Gs(a){const e=[];let t=0;for(;ti.precedence||l.precedence===i.precedence&&!i.right){e.push(t.pop());continue}break}t.push(n);break}case"(":t.push(n);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 n=t.pop();if(n.type==="(")return null;e.push(n)}return e}function Xs(a){const e=[];for(const t of a)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(),n=e.pop();switch(t.value){case"+":e.push(n+s);break;case"-":e.push(n-s);break;case"*":e.push(n*s);break;case"/":e.push(s===0?0:n/s);break;case"%":e.push(s===0?0:n%s);break;case"^":e.push(Math.pow(n,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 Js=["sum","min","max","avg","round","ceil","floor","abs","sqrt","pow"],yt=["number","range","scale","rating","total","select","multiselect","radio","checkboxes","switch","quiz"];function He(a,e){return a.filter(t=>t.id!==e&&yt.includes(t.type))}function Qs(a){const e=[];for(const t of a){if(t.type!=="repeater")continue;const s=t.label||t.id;e.push({label:`${s} (how many)`,insert:`{${t.id}}`});for(const n of t.fields??[])yt.includes(n.type)&&e.push({label:`${s} · ${n.label||n.id}`,insert:`{${t.id}.${n.id}}`})}return e}function en(a,e){const t={};He(a,e).forEach((s,n)=>{t[s.id]=n+1});for(const s of a){if(s.type!=="repeater"||s.id===e)continue;const n=s.fields??[],i=r=>Object.fromEntries(n.map((l,c)=>[l.id,c+1+r]));t[s.id]=[i(0),i(1)]}return t}function tn(a){const e=o("div",{class:"atfb-overlay"}),t=()=>{e.remove(),document.removeEventListener("keydown",s)},s=u=>{u.key==="Escape"&&t()},n=o("textarea",{class:"atfb-input atfb-formula__input",attrs:{rows:"3","aria-label":"Formula"}});n.value=String(a.field.formula??"");const i=o("p",{class:"atfb-formula__result",attrs:{"aria-live":"polite"}}),r=en(a.fields,a.field.id),l=()=>{const u=n.value.trim();if(u===""){i.textContent="Empty. Reference a question below to start.",i.classList.remove("is-error");return}const x=js(u,r,a.fields);if(x===null){i.textContent="This does not compute yet — check the braces and parentheses.",i.classList.add("is-error");return}const g=He(a.fields,a.field.id).map((f,E)=>`${f.label||f.id} = ${E+1}`).concat(a.fields.filter(f=>f.type==="repeater"&&f.id!==a.field.id).map(f=>`${f.label||f.id} = 2 sample rows`)).join(", ");i.textContent=`With sample answers (${g}): ${x}`,i.classList.remove("is-error")};n.addEventListener("input",l);const c=(u,x,g=0)=>o("button",{class:"atfb-formula__chip",type:"button",text:u,on:{click:()=>{if(lt(n,x),g>0){const f=(n.selectionStart??n.value.length)-g;n.setSelectionRange(f,f)}}}}),d=He(a.fields,a.field.id),h=Qs(a.fields),w=o("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))]:[o("p",{class:"atfb-hint",text:"No number-shaped questions yet — add a number, scale or priced choice field and it appears here."})]}),v=o("div",{class:"atfb-formula__chips",children:Js.map(u=>c(`${u}()`,`${u}()`,1))});e.append(o("div",{class:"atfb-modal atfb-formula",attrs:{role:"dialog","aria-label":"Formula editor"},children:[o("h2",{text:"Formula"}),n,i,b("Your questions",w,"Click one to reference its answer."),b("Functions",v),o("div",{class:"atfb-modal__actions",children:[L("Cancel",t),L("Save formula",()=>{a.onSave(n.value.trim()),t()},"primary")]})]})),e.addEventListener("click",u=>{u.target===e&&t()}),document.addEventListener("keydown",s),a.root.append(e),l(),n.focus(),n.setSelectionRange(n.value.length,n.value.length)}const sn="allterrain-forms/form";function nn(){return window.wp?.os?.relations??null}function wt(a){const e=a.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 an=6e3,xt=120;function _t(a,e){const t=nn();if(je.set(a,e),!t?.set)return;const s=i=>{if(ve.get(a)!==n)return;const r=wt(a);if(!r){Date.now()s(i),xt);return}try{t.set(r,e)}catch(c){kt||(kt=!0,console.error("[AllTerrain Forms] The shell refused a window identity.",c,e)),ve.delete(a);return}if(!e||t.get?.(r)?.id===e.id||Date.now()>=i){ve.delete(a);return}window.setTimeout(()=>s(i),xt)},n=Symbol("atf-identity");ve.set(a,n),s(Date.now()+an)}let kt=!1;const ve=new WeakMap,je=new Map;function rn(){for(const[a,e]of je){if(!a.isConnected){je.delete(a);continue}_t(a,e)}}if(typeof document<"u")for(const a of["os-window-content-loaded","os-window-opened"])document.addEventListener(a,()=>rn());function on(a,e){return{type:sn,id:a.id,label:a.title||"Untitled form",related:[{id:`allterrain-forms/entries-${a.id}`,label:"Entries for this form",url:`${e}admin.php?page=allterrain-forms-entries&form=${a.id}`,group:"allterrain-forms",groupLabel:"Forms",icon:"dashicons-list-view"}]}}const ye={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 ln(){return{mode:"blocks",starts:"",ends:"",contains:"",notContains:"",chars:[],minLen:"",maxLen:"",regex:"",message:"",tests:[]}}function St(a){const e=ln();let t;try{t=JSON.parse(a)}catch{return e}if(!t||typeof t!="object")return e;const s=t;e.mode=s.mode==="regex"?"regex":"blocks";for(const n of["starts","ends","contains","notContains","minLen","maxLen","regex","message"])typeof s[n]=="string"&&(e[n]=s[n]);return Array.isArray(s.chars)&&(e.chars=s.chars.filter(n=>typeof n=="string"&&n in ye)),Array.isArray(s.tests)&&(e.tests=s.tests.filter(n=>typeof n=="string").slice(0,10)),e}function we(a){return a.replace(/[.*+?^${}()|[\]\\/]/g,"\\$&")}function xe(a){if(a.mode==="regex")return a.regex.trim();const e=[],t=a.minLen.trim(),s=a.maxLen.trim();(t!==""||s!=="")&&e.push(`(?=.{${t===""?"0":parseInt(t,10)||0},${s===""?"":parseInt(s,10)||""}}$)`),a.contains!==""&&e.push(`(?=.*${we(a.contains)})`),a.notContains!==""&&e.push(`(?!.*${we(a.notContains)})`),a.starts!==""&&e.push(`(?=${we(a.starts)})`),a.ends!==""&&e.push(`(?=.*${we(a.ends)}$)`);const n=a.chars.map(r=>ye[r]?.chars??"").join(""),i=n?`[${n}]*$`:".*$";return!e.length&&!n?"":`^${e.join("")}${i}`}function Lt(a){if(a.mode==="regex")return a.regex.trim()?`Matches the expression ${a.regex.trim()}`:"";const e=[];if(a.starts&&e.push(`starts with “${a.starts}”`),a.ends&&e.push(`ends with “${a.ends}”`),a.contains&&e.push(`contains “${a.contains}”`),a.notContains&&e.push(`never contains “${a.notContains}”`),a.chars.length){const i=a.chars.map(r=>(ye[r]?.label??r).split(" (")[0].toLowerCase());e.push(`uses only ${i.join(" and ")}`)}const t=a.minLen.trim(),s=a.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 cn(a,e){const t=xe(a);if(t==="")return null;try{return new RegExp(t).test(e)}catch{return null}}function dn(){return window.wp?.os?.windowManager??null}const Et="allterrain-forms-validation";function hn(a){const e=dn(),t=wt(a.root);if(e?.openChild&&t){un(e,t,a);return}pn(a)}function un(a,e,t){const s=`${Et}-${t.field.id}`;a.getById?.(s)&&a.remove?.(s);const n={saved:!1},i=()=>{const r=a.getById?.(s);r?.close?r.close():a.remove?.(s)};a.openChild?.(e,{id:s,baseId:Et,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:r=>{const l=o("div",{class:"atfa atfb-valwin atfb-valwin--window"}),c=o("div",{class:"atfb-valwin__scroll"});l.append(c),$t(c,t,n,i,"window");const d=c.querySelector(".atfb-modal__actions");d&&l.append(d),r.append(l)},onClose:()=>{n.saved||t.onCancel?.()}})}function pn(a){const e=o("div",{class:"atfb-overlay"}),t={saved:!1},s=()=>{e.remove(),document.removeEventListener("keydown",n),t.saved||a.onCancel?.()},n=r=>{r.key==="Escape"&&s()},i=o("div",{class:"atfb-modal atfb-valwin",attrs:{role:"dialog","aria-label":"Custom validation rule"},children:[o("h2",{text:"Custom rule"})]});$t(i,a,t,s,"overlay"),e.append(i),e.addEventListener("click",r=>{r.target===e&&s()}),document.addEventListener("keydown",n),a.root.append(e),e.querySelector(".atfb-valwin__pane:not([hidden]) input, .atfb-valwin__pane:not([hidden]) textarea")?.focus()}function $t(a,e,t,s,n){const i=St(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 r=($,N)=>{const A=o("input",{class:"atfb-input",value:i[$],placeholder:N,attrs:{type:"text"}});return A.addEventListener("input",()=>{i[$]=A.value,M()}),A},l=($,N)=>{const A=o("input",{class:"atfb-input atfb-valwin__len",value:i[$],attrs:{type:"number",min:"0","aria-label":N}});return A.addEventListener("input",()=>{i[$]=A.value,M()}),A},c=o("div",{class:"atfb-valwin__chars",children:Object.entries(ye).map(([$,N])=>I(N.label,i.chars.includes($),A=>{i.chars=A?[...i.chars,$]:i.chars.filter(j=>j!==$),M()}))}),d=o("div",{class:"atfb-valwin__pane",children:[b("Starts with",r("starts","e.g. AT-")),b("Ends with",r("ends","e.g. -2026")),b("Must contain",r("contains","e.g. @")),b("Must not contain",r("notContains","e.g. spaces? type one")),b("Only these characters",c,"Leave every box unticked to allow anything."),b("Length",o("div",{class:"atfb-valwin__lengths",children:[o("span",{text:"between"}),l("minLen","Minimum length"),o("span",{text:"and"}),l("maxLen","Maximum length"),o("span",{text:"characters"})]}),"Leave a box empty for no limit.")]}),h=o("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,M()});const w=o("div",{class:"atfb-valwin__pane",children:[b("Expression",h,"A regular expression, without slashes. Checked against the whole answer only if you anchor it with ^ and $.")]}),v=[["blocks","Easy blocks"],["regex","Expression (advanced)"]],u=$=>{i.mode=$,d.hidden=$!=="blocks",w.hidden=$!=="regex",M()},g=(()=>{if(Y("os-segmented")&&Y("os-segment")){const A=document.createElement("os-segmented");A.setAttribute("value",i.mode),A.setAttribute("label","How to write the rule"),A.classList.add("atfb-valwin__tabs");for(const[j,P]of v){const oe=document.createElement("os-segment");oe.setAttribute("value",j),oe.textContent=P,A.append(oe)}return A.addEventListener("os-pick",j=>{const P=j.detail?.value;(P==="blocks"||P==="regex")&&(A.setAttribute("value",P),u(P))}),A}const $=o("div",{class:"atfb-valwin__tabs",attrs:{role:"tablist"}}),N=()=>{$.replaceChildren(...v.map(([A,j])=>{const P=i.mode===A;return o("button",{class:`atfb-valwin__tab${P?" is-active":""}`,type:"button",text:j,attrs:{role:"tab","aria-selected":P?"true":"false"},on:{click:()=>{u(A),N()}}})}))};return N(),$})();d.hidden=i.mode!=="blocks",w.hidden=i.mode!=="regex";const f=o("input",{class:"atfb-input",value:i.message,placeholder:"That is not in the expected format.",attrs:{type:"text"}});f.addEventListener("input",()=>{i.message=f.value});const E=o("p",{class:"atfb-valwin__summary",attrs:{"aria-live":"polite"}}),R=o("div",{class:"atfb-valwin__samples"}),k=$=>{const N=o("span",{class:"atfb-valwin__verdict",attrs:{"aria-live":"polite"}}),A=o("input",{class:"atfb-input",value:$,placeholder:"Type a sample answer…",attrs:{type:"text"}});A.addEventListener("input",()=>M()),R.append(o("div",{class:"atfb-valwin__sample",children:[A,N]}))};for(const $ of i.tests.length?i.tests:["","",""])k($);const T=()=>Array.from(R.querySelectorAll("input")).map($=>$.value),M=()=>{const $=Lt(i);E.textContent=$||"Nothing yet — fill in a block above and the rule appears here in plain words.";for(const N of Array.from(R.querySelectorAll(".atfb-valwin__sample"))){const A=N.querySelector("input"),j=N.querySelector(".atfb-valwin__verdict");if(!A||!j)continue;const P=A.value===""?null:cn(i,A.value);j.textContent=P===null?"·":P?"✓ passes":"✗ fails",j.classList.toggle("is-pass",P===!0),j.classList.toggle("is-fail",P===!1)}};a.append(o("p",{class:"atfb-hint",text:`Describe what a good answer to “${e.field.label||"this question"}” looks like — no code needed.`}),g,d,w,o("div",{class:"atfb-valwin__try",children:[o("h3",{text:"Try it out"}),E,R,L("Add another sample",()=>{k(""),M()},"ghost","plus-alt2")]}),b("When it fails, say",f,"Shown to the visitor when their answer breaks the rule. Leave empty for the default wording."),o("div",{class:"atfb-modal__actions",children:[L(n==="window"?"Close without saving":"Cancel",s),L("Save rule",()=>{const $=xe(i);i.tests=T().filter(N=>N!=="").slice(0,10),t.saved=!0,e.onSave({pattern:$,recipe:i,message:i.message.trim()}),s()},"primary")]})),M()}const fn=["Contact","Numbers & codes","Text shape","Web"],Ct=[{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 mn(a){return Ct.find(e=>e.slug===a)??null}function We(){return window.wp?.os??null}const At="allterrain-forms/preview",Ue="allterrain-forms-preview";function bn(a){const e=We();if(!e?.registerTitleBarButton)return()=>{};const t=()=>{try{e.registerTitleBarButton({id:At,label:"Preview this form",icon:"dashicons-visibility",placement:"right",order:90,match:s=>{const n=s?.id??s?.config?.id??"";return n==="allterrain-forms"||n.startsWith("allterrain-forms#")},onClick:()=>void Tt(a),owner:"allterrain-forms-builder"})}catch{}};return e.ready?e.ready(t):t(),()=>{try{e.unregisterTitleBarButton?.(At)}catch{}}}async function Tt(a){a.isDirty()&&await a.save();const e=a.current();e&&Mt(e.id,e.title,e.previewUrl)}function Mt(a,e,t){const s=We();if(!s?.windowManager?.open){window.open(t,"_blank","noopener");return}s.windowManager.open({id:`${Ue}-${a}`,baseId:Ue,url:t,title:`Preview: ${e}`,icon:"dashicons-visibility"})}function gn(a,e,t){if(!We()?.windowManager?.open||!document.querySelector(`[data-window-id="${Ue}-${a}"]`))return;const i=t.includes("?")?"&":"?";Mt(a,e,`${t}${i}atf_r=${Date.now()}`)}const se="allterrain-forms/field",vn=["openstation/file","desktop-mode/file","openstation/attachment"],Ft="allterrain-forms/logic-map-v2";function J(a,e){return a.dataset.atfbBind=e,a}const It={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"}}},yn={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 Dt(a,e,t){const s=a[e.key],n=i=>t(e.key,i);if(e.control==="checkbox")return I(e.label,!!s,n);if(e.control==="commas"){const i=Array.isArray(s)?s:[];return b(e.label,C(i.join(", "),r=>{n(r.split(",").map(l=>l.trim().replace(/^\./,"").toLowerCase()).filter(Boolean))}),e.hint)}return e.control==="select"?b(e.label,q(String(s??""),e.options??[],n),e.hint):e.control==="textarea"?b(e.label,ue(String(s??""),n),e.hint):e.control==="number"?b(e.label,G(String(s??""),n),e.hint):b(e.label,J(C(String(s??""),n),e.key),e.hint)}function qt(a,e){const t=new Set(a.map(n=>n.key).filter(Boolean));let s=a.length+1;return e.split(` -`).map(n=>n.trim()).filter(Boolean).map((n,i)=>{const r=a[i]?.key;if(r)return{key:r,label:n};for(;t.has(`r${s}`);)s+=1;return t.add(`r${s}`),{key:`r${s}`,label:n}})}function Ot(a,e,t){const s=a.findIndex(i=>i.id===e);if(s<0)return null;const n=Math.max(0,Math.min(t,a.length-1));return n===s?null:{from:s,to:n}}const ze=[{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}"}],wn=["About the person filling it in","The date and time","About this site"],Ye=(a,e)=>W?.i18n?.[a]??e;class Rt{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=o("style"),this.canvasThemeSignature="",this.openSections=new Map,this.logicMapOn=Jt(Ft)!=="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=Ce(()=>{this.save(!0)},2500),this.renderInspector=Vt(()=>{if(H(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,n=t?.parent??null;if(this.root.classList.toggle("atfb--has-selection",!!s),!s){this.inspector.append(o("div",{class:"atfb-placeholder",children:[o("p",{text:"Select a field to change it."}),o("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),r=i?.supports??[],l=(c,d)=>{const h=this.liveField(s.id);h&&(h[c]=d,this.markDirty(),this.renderCanvas())};this.inspector.append(o("h3",{class:"atfb-inspector__title",text:i?.label??s.type})),n?this.inspector.append(o("p",{class:"atfb-hint atfb-inspector__crumb",text:`Inside ${n.label||"a repeater"} — the visitor answers this once per ${String(n.itemLabel??"")||"row"}.`}),o("p",{class:"atfb-hint",text:`Formulas aggregate it as {${n.id}.${s.id}} — e.g. sum( {${n.id}.${s.id}} ).`})):this.inspector.append(o("p",{class:"atfb-hint",text:`Reference this field as {field:${s.id}}`})),r.includes("label")&&this.inspector.append(b("Label",J(C(s.label,c=>l("label",c)),"label"))),r.includes("placeholder")&&this.inspector.append(b("Placeholder",J(C(s.placeholder,c=>l("placeholder",c)),"placeholder"))),r.includes("hint")&&this.inspector.append(b("Hint",J(C(s.hint,c=>l("hint",c)),"hint"),"Shown under the field, and read out with it.")),r.includes("nextlabel")&&this.inspector.append(b("Next button",J(C(String(s.nextLabel??""),c=>l("nextLabel",c)),"nextLabel"),"Leave empty for “Next”.")),r.includes("prevlabel")&&this.inspector.append(b("Back button",J(C(String(s.prevLabel??""),c=>l("prevLabel",c)),"prevLabel"),"Leave empty for “Back”.")),r.includes("addlabel")&&this.inspector.append(b("Add button",J(C(String(s.addLabel??""),c=>l("addLabel",c)),"addLabel"),"Leave empty for “Add another”.")),r.includes("required")&&this.inspector.append(I("Required",s.required,c=>l("required",c))),r.includes("width")&&this.inspector.append(b("Width",q(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,r,l),(s.type==="total"||r.includes("formula"))&&this.inspector.append(b("Formula",o("div",{class:"atfb-formula__row",children:[C(String(s.formula??""),c=>l("formula",c)),L("Formula editor",()=>tn({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."),b("Currency symbol",C(String(s.currency??""),c=>l("currency",c)))),this.inspector.append(this.renderValidationSection(s,r,l)),n||(this.inspector.append(this.renderLogicSection(s)),r.includes("prefill")&&this.inspector.append(this.prefillControl(s,l))),r.includes("css")&&this.inspector.append(b("CSS class",C(s.cssClass,c=>l("cssClass",c))))}),this.root=e,this.bar=e.querySelector("[data-atfb-bar]")??o("div"),this.palette=e.querySelector("[data-atfb-palette]")??o("div"),this.canvas=e.querySelector("[data-atfb-canvas]")??o("div"),this.inspector=e.querySelector("[data-atfb-inspector]")??o("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 n of this.canvas.querySelectorAll(".atfb-card.is-selected"))n.classList.remove("is-selected");this.renderInspector()}})}async start(){try{const[s,n,i]=await Promise.all([D.config(),D.listThemes(),D.listForms()]);this.config=s,this.themes=n,this.forms=i}catch(s){this.fail(s);return}this.teardowns.push(Yt([se])),this.teardowns.push(bn({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=ls("builder");await this.open(s&&this.forms.some(n=>n.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){H(this.bar),this.bar.append(o("p",{class:"atfb-error",text:e instanceof Error?e.message:"Something went wrong loading your forms."}))}renderBar(){H(this.bar);const e=q(String(this.form?.id??""),[...this.forms.map(n=>({value:String(n.id),label:n.title||"(untitled)"}))],n=>void this.open(Number(n)));e.setAttribute("aria-label","Choose a form");const t=o("input",{class:"atfb-title",type:"text",value:this.form?.title??"",placeholder:"Untitled form",attrs:{"aria-label":"Form title"},on:{input:n=>{this.form&&(this.form.title=n.target.value,this.markDirty())}}}),s=o("div",{class:"atfb-tabs",attrs:{role:"tablist"},children:[["build","Build"],["theme","Theme"],["settings","Settings"],["notify","Notifications"],["confirm","Confirmations"]].map(([n,i])=>o("button",{class:`atfb-tab${this.tab===n?" is-active":""}`,type:"button",text:i,attrs:{role:"tab","aria-selected":this.tab===n},on:{click:()=>{this.tab=n,this.renderBar(),this.renderInspector(),this.renderCanvas()}}}))});this.bar.append(o("div",{class:"atfb-bar__left",children:[this.forms.length>1?e:null,t]}),s,o("div",{class:"atfb-bar__right",children:[this.historyButton("undo","Undo",-1),this.historyButton("redo","Redo",1),L("New",()=>void this.showTemplates(),"secondary","plus-alt2"),L("Export",()=>void this.exportForm(),"secondary","download"),L("Import",()=>void this.importForm(),"secondary","upload"),L("Preview",()=>void this.preview(),"secondary","visibility"),L("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 n=L(t,()=>this.travel(s),"secondary",e);return n.setAttribute("data-atfb-history",String(s)),n.disabled=!this.canTravel(s),n}canTravel(e){const t=this.historyAt+e;return t>=0&&tURL.revokeObjectURL(s),1e3)}async importForm(){const e=o("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 n=await D.createForm({title:s.title??t.name.replace(/\.json$/i,""),schema:s.schema});this.forms.unshift({id:n.id,title:n.title,status:n.status,modified:n.modified,fields:n.schema.fields.length,theme:n.schema.settings.theme,entries:0,unread:0,views:0,submissions:0,shortcode:n.shortcode}),this.form=n,this.schema=n.schema,this.selected=null,this.dirty=!1,this.history=[],this.historyAt=-1,this.snapshot(),this.renderBar(),this.renderCanvas(),this.renderInspector(),B("Form imported",n.title)}catch(s){B("Could not import that file",s instanceof Error?s.message:"","error")}}),e.click()}saveButton(){const e=L("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 D.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)}Ss(s.id);const n=this.forms.find(i=>i.id===s.id);n&&(n.title=s.title),gn(s.id,s.title,s.previewUrl),e||B("Form saved",s.title)}catch(s){B(Ye("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 fe("You have unsaved changes. Discard them?"))){try{this.form=await D.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,on(this.form,W?.adminUrl??""))}async showTemplates(){if(!this.config)return;const e=o("div",{class:"atfb-overlay"}),t=i=>{i.key==="Escape"&&s()},s=()=>{e.remove(),document.removeEventListener("keydown",t)},n=o("div",{class:"atfb-templates",children:this.config.templates.map(i=>o("button",{class:"atfb-template",type:"button",on:{click:async()=>{s();try{const r=await D.createForm({template:i.slug});this.forms.unshift({id:r.id,title:r.title,status:r.status,modified:r.modified,fields:r.schema.fields.length,theme:r.schema.settings.theme,entries:0,unread:0,views:0,submissions:0,shortcode:r.shortcode}),this.form=r,this.schema=r.schema,this.selected=null,this.dirty=!1,this.history=[],this.historyAt=-1,this.snapshot(),this.renderBar(),this.renderCanvas(),this.renderInspector(),this.announceIdentity()}catch(r){B("Could not create the form",r instanceof Error?r.message:"","error")}}},children:[U(i.icon),o("strong",{text:i.label}),o("span",{text:i.description})]}))});e.append(o("div",{class:"atfb-modal",attrs:{role:"dialog","aria-label":"Start a new form"},children:[o("h2",{text:"Start a new form"}),n,this.archivedFormsSection(s),o("div",{class:"atfb-modal__actions",children:[L("Cancel",s)]})]})),e.addEventListener("click",i=>{i.target===e&&s()}),document.addEventListener("keydown",t),this.root.append(e),n.querySelector("button")?.focus()}archivedFormsSection(e){const t=o("div",{class:"atfb-archived"});return D.listArchivedForms().then(s=>{s.length&&t.append(o("h3",{class:"atfb-archived__title",text:"Or bring one back from the archive"}),...s.map(n=>o("div",{class:"atfb-archived__row",children:[o("div",{class:"atfb-archived__meta",children:[o("strong",{text:n.title||"(untitled)"}),o("span",{class:"atfb-hint",text:`${n.entries} ${n.entries===1?"entry":"entries"} · ${n.submissions} submissions · ${n.views} views`})]}),L("Restore",async()=>{try{const i=await D.unarchiveForm(n.id);e(),this.forms.unshift(i),B("Form restored",`${i.title||"(untitled)"} is back, with its entries and stats.`),await this.open(i.id)}catch(i){B("Could not restore the form",i instanceof Error?i.message:"","error")}},"secondary","undo")]})))}).catch(()=>{}),t}renderFormsList(){H(this.canvas),this.canvas.append(o("div",{class:"atfb-empty",children:[o("h2",{text:"No forms yet"}),o("p",{text:"Start from a template, or build one from nothing."}),L("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(`${W?.adminUrl??""}admin.php?page=allterrain-forms-entries`)}renderPalette(){if(!this.config)return;H(this.palette);const e=new Map;for(const s of this.config.fieldTypes){const n=e.get(s.group)??[];n.push(s),e.set(s.group,n)}const t=o("input",{class:"atfb-input atfb-palette__search",type:"search",placeholder:"Search fields",attrs:{"aria-label":"Search field types"},on:{input:s=>{const n=s.target.value.toLowerCase().trim();this.palette.querySelectorAll(".atfb-chip").forEach(i=>{const r=(i.textContent??"").toLowerCase();i.hidden=n!==""&&!r.includes(n)}),this.palette.querySelectorAll(".atfb-group").forEach(i=>{const r=Array.from(i.querySelectorAll(".atfb-chip")).some(l=>!l.hidden);i.hidden=!r})}}});this.palette.append(t);for(const[s,n]of Object.entries(this.config.groups)){const i=e.get(s);i?.length&&this.palette.append(o("div",{class:"atfb-group",children:[o("h3",{class:"atfb-group__title",text:n}),o("div",{class:"atfb-group__items",children:i.map(r=>this.paletteChip(r))})]}))}}paletteChip(e){const t=o("button",{class:"atfb-chip",type:"button",title:e.description,attrs:{"data-atf-type":e.type},children:[U(e.icon),o("span",{text:e.label})]});return t.addEventListener("pointerdown",s=>{const n=o("div",{class:"atfb-chip atfb-chip--ghost",children:[U(e.icon),o("span",{text:e.label})]});K().start({payload:Le(se,t,{fieldType:e.type,isNew:!0},s,n),origin:s,onClickOnly:()=>this.addField(e.type)})}),t.addEventListener("click",s=>{K().recentlyEndedDrag()&&s.preventDefault()}),t}renderCanvas(){if(H(this.canvas),!this.schema||!this.form){this.renderFormsList();return}if(this.tab!=="build"){this.canvas.append(this.renderTabCanvas());return}const e=o("div",{class:"atfb-canvas__list",attrs:{"data-atfb-list":""}});this.schema.fields.length||e.append(o("div",{class:"atfb-placeholder",text:Ye("emptyCanvas","Drag a field from the left to begin.")})),this.schema.fields.forEach((s,n)=>{e.append(this.renderFieldCard(s,n))});const t=o("div",{class:"atfb-canvas__inner",children:[o("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:"),n=ze.some(v=>v.value===e.prefill),i=s?"query":n&&e.prefill||(e.prefill?"custom":""),r=o("div",{class:"atfb-prefill__detail"}),l=o("p",{class:"atfb-row__hint atfb-prefill__preview"}),c=v=>{if(r.replaceChildren(),l.replaceChildren(),v==="query"){const u=e.prefill.startsWith("query:")?e.prefill.slice(6):"";r.append(C(u,x=>{const g=x.trim();t("prefill",g?`query:${g}`:""),d(`query:${g}`)},"utm_source"),o("p",{class:"atfb-row__hint",text:"The name of the parameter in the link people arrive on."}))}v==="custom"&&r.append(C(e.prefill,u=>{t("prefill",u),d(u)},"myplugin:something"),o("p",{class:"atfb-row__hint",text:"For a source another plugin has added through atf_resolve_prefill."})),d(e.prefill)},d=v=>{if(l.replaceChildren(),!v)return;if(v.startsWith("query:")){const x=v.slice(6);x&&(l.textContent=`A visit to …/your-page/?${x}=abc opens the form with “abc” in it.`);return}const u=ze.find(x=>x.value===v)?.tag;u&&De(this.form?.id??0).then(x=>{for(const g of x)for(const f of g.items)if(f.tag===u){const E=v.startsWith("user:");if(!f.sample){l.textContent="Empty unless the visitor is signed in.";return}l.textContent=E?`Opens with “${f.sample}” for you — empty for a visitor who is not signed in.`:`Opens with “${f.sample}”.`;return}})};c(i);const h=o("select",{class:"atfb-input atfb-select",on:{change:v=>{const u=v.target.value;t("prefill",u==="query"||u==="custom"?"":u),c(u)}}});h.append(o("option",{value:"",text:"Nothing — leave it empty",attrs:{selected:i===""}}));for(const v of wn){const u=document.createElement("optgroup");u.label=v;for(const x of ze.filter(g=>g.group===v))u.append(o("option",{value:x.value,text:x.label,attrs:{selected:x.value===i}}));h.append(u)}const w=document.createElement("optgroup");return w.label="From the link they arrived on",w.append(o("option",{value:"query",text:"A parameter in the web address",attrs:{selected:i==="query"}})),h.append(w,o("option",{value:"custom",text:"Something else (advanced)",attrs:{selected:i==="custom"}})),b("Pre-fill this with",o("div",{class:"atfb-prefill",children:[h,r,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),n=o("span",{class:`atfb-cond${s?" is-broken":""}`,attrs:{"aria-label":nt(t)},children:[U("randomize"),...t.map(i=>this.renderConditionToken(e,i))]});return n.addEventListener("pointerdown",i=>i.stopPropagation()),n.addEventListener("click",i=>i.stopPropagation()),n.addEventListener("keydown",i=>i.stopPropagation()),n}editCondition(e,t,s=!0,n=""){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(),n&&window.requestAnimationFrame(()=>{this.canvas.querySelector(`[data-cond="${CSS.escape(n)}"]`)?.focus()})}}condSelect(e,t,s,n,i){if(Y("os-select")&&Y("os-option")){const r=document.createElement("os-select");r.setAttribute("value",e),r.setAttribute("aria-label",n),r.setAttribute("data-cond",s),r.className="atfb-cond__control",r.title=n;for(const l of t){const c=document.createElement("os-option");c.setAttribute("value",l.value),c.textContent=l.label,r.append(c)}return r.addEventListener("os-pick",l=>{i(String(l.detail?.value??""))}),r}return o("select",{class:"atfb-cond__control atfb-cond__control--native",title:n,attrs:{"aria-label":n,"data-cond":s},on:{change:r=>i(r.target.value)},children:t.map(r=>o("option",{value:r.value,text:r.label,attrs:{selected:r.value===e}}))})}renderConditionToken(e,t){if(t.kind==="field"&&!t.missing)return o("button",{class:"atfb-cond__chip atfb-cond__chip--field",type:"button",text:t.text,title:"Go to this question",on:{click:n=>{n.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 o("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 o("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(Ae).map(([n,i])=>({value:n,label:i})),s,"How the answer is compared",n=>this.editCondition(e.id,i=>{const r=i.rules[t.ruleIndex];r&&(r.operator=n)},!0,s))}return t.kind==="value"?this.renderConditionValue(e,t):o("span",{class:"atfb-cond__chip atfb-cond__chip--missing",text:t.text})}renderConditionValue(e,t){const s=`${e.id}:value:${t.ruleIndex}`,n=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(n?.choices?.length){const c=n.choices.map(d=>({value:d.value,label:d.label||d.value}));return t.raw!==""&&!n.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 r=["number","range","scale","rating","total"].includes(n?.type??""),l=o("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:r?"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,n=!1){const i=o("details",{class:"atfb-section",attrs:{open:this.openSections.get(e)??n},children:[o("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=L(this.logicMapOn?"Hide logic":"Show logic",()=>{this.logicMapOn=!this.logicMapOn,Qt(Ft,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 n=new ps(e);n.setEdges(s),n.highlight(this.selected??""),this.logicMap=n,e.addEventListener("pointerover",i=>{const r=i.target.closest("[data-atfb-card]");n.highlight(r?.dataset.atfbCard??this.selected??"")}),e.addEventListener("pointerleave",()=>n.highlight(this.selected??""))}renderFieldCard(e,t){const s=this.config?.fieldTypes.find(d=>d.type===e.type),n=this.selected===e.id,i=this.schema?.fields??[],r=ds(e,i),l=us(i).get(e.id)??0,c=o("div",{class:`atfb-card${n?" is-selected":""}`,attrs:{"data-atfb-card":e.id,"data-index":t,tabindex:"0",role:"button","aria-pressed":n,"aria-label":`${e.label||s?.label||e.type}, ${t+1} of ${this.schema?.fields.length??0}`},children:[o("div",{class:"atfb-card__bar",children:[o("div",{class:"atfb-card__grip",attrs:{"aria-hidden":"true"},children:[U("menu")]}),o("div",{class:"atfb-card__head",children:[U(s?.icon??"dashicons-forms"),o("span",{class:"atfb-card__type",text:s?.label??e.type}),this.requiredToggle(e),l?o("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]}),o("div",{class:"atfb-card__actions",children:[this.cardAction("admin-page","Duplicate",()=>this.duplicateField(e.id)),this.cardAction("trash","Delete",()=>void this.deleteField(e.id))]})]}),o("div",{class:"atfb-card__body",children:[ot(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}),r.length?this.renderCondition(e,r):null]})]});return c.addEventListener("click",d=>{d.target.closest(".atfb-card__actions")||K().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")||K().start({payload:Le(se,c,{fieldId:e.id,field:e,isNew:!1},d),origin:d})}),c}requiredToggle(e){return o("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 o("button",{class:"atfb-card__action",type:"button",title:t,attrs:{"aria-label":t},on:{click:n=>{n.stopPropagation(),s()}},children:[U(e)]})}registerCanvasTarget(e){this.canvasTarget?.(),this.canvasTarget=null;const t=o("div",{class:"atfb-marker",attrs:{"aria-hidden":"true"}}),s=K().registerDropTarget({id:`atfb-canvas-${this.form?.id??0}`,element:e,accept:r=>r.type===se,onEnter:()=>e.classList.add("is-dropping"),onLeave:()=>{e.classList.remove("is-dropping"),t.remove()},onDrop:(r,l)=>{e.classList.remove("is-dropping"),t.remove();const c=r.payload.data,d=c.fieldId?this.canvas.querySelector(`[data-atfb-card="${CSS.escape(c.fieldId)}"]`):null,h=Ee(e,".atfb-card",l.clientY,d??void 0);if(c.isNew&&c.fieldType){this.addField(c.fieldType,h);return}if(c.fieldId&&this.locateField(c.fieldId)){this.relocateField(c.fieldId,null,h);return}c.field&&this.insertField({...c.field,id:""},h)}}),n=this.wireRepeaterZones(e),i=r=>{const l=r.detail;if(l?.payload?.type!==se||!e.classList.contains("is-dropping"))return;const c=l.payload.data?.fieldId?this.canvas.querySelector(`[data-atfb-card="${CSS.escape(l.payload.data.fieldId)}"]`):null,d=l.clientY??0,h=Ee(e,".atfb-card",d,c??void 0),w=Array.from(e.querySelectorAll(".atfb-card")).filter(v=>v!==c);h>=w.length?e.append(t):w[h].before(t)};document.addEventListener("os.drag.move",i),this.canvasTarget=()=>{s(),n.forEach(r=>r()),document.removeEventListener("os.drag.move",i)}}wireRepeaterZones(e){const t=[];return e.querySelectorAll("[data-atfb-repeater-zone]").forEach(s=>{const n=s.dataset.atfbRepeaterZone??"";t.push(K().registerDropTarget({id:`atfb-repzone-${this.form?.id??0}-${n}`,element:s,accept:i=>{if(i.type!==se)return!1;const r=i.data;if(r.fieldId===n)return!1;const l=r.isNew?r.fieldType:this.locateField(r.fieldId??"")?.field.type??r.field?.type;return!!l&&this.allowedInRepeater(l)},onEnter:()=>s.classList.add("is-dropping"),onLeave:()=>s.classList.remove("is-dropping"),onDrop:(i,r)=>{s.classList.remove("is-dropping");const l=i.payload.data,c=l.fieldId?e.querySelector(`[data-atfb-subfield="${CSS.escape(l.fieldId)}"]`):null,d=Ee(s,".atfb-subcard",r.clientY,c??void 0);if(l.isNew&&l.fieldType){this.addFieldToRepeater(l.fieldType,n,d);return}if(l.fieldId&&this.locateField(l.fieldId)){this.relocateField(l.fieldId,n,d);return}l.field&&this.insertFieldIntoRepeater({...l.field,id:""},n,d)}})),s.querySelectorAll(".atfb-subcard").forEach(i=>{const r=i.dataset.atfbSubfield??"";i.addEventListener("click",l=>{l.target.closest(".atfb-preview__remove")||(l.stopPropagation(),!K().recentlyEndedDrag()&&this.selectField(r))}),i.addEventListener("keydown",l=>{if(l.stopPropagation(),l.key==="Enter"||l.key===" "){l.preventDefault(),this.selectField(r);return}if(l.altKey&&(l.key==="ArrowUp"||l.key==="ArrowDown")){l.preventDefault();const c=this.locateField(r);c&&(this.relocateField(r,n,l.key==="ArrowUp"?c.index-1:c.index+1),window.requestAnimationFrame(()=>{this.canvas.querySelector(`[data-atfb-subfield="${CSS.escape(r)}"]`)?.focus()}));return}(l.key==="Delete"||l.key==="Backspace")&&(l.preventDefault(),this.deleteField(r))}),i.addEventListener("pointerdown",l=>{if(l.stopPropagation(),l.target.closest(".atfb-preview__remove"))return;const c=this.locateField(r);c&&K().start({payload:Le(se,i,{fieldId:r,parentId:n,field:c.field,isNew:!1},l),origin:l})})})}),t}addField(e,t){const s=this.buildField(e);s&&this.insertField(s,t)}buildField(e){const t=this.config?.fieldTypes.find(n=>n.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 n=this.buildField(e);n&&this.insertFieldIntoRepeater(n,t,s)}insertFieldIntoRepeater(e,t,s){const n=this.liveField(t);if(!this.schema||!n||n.type!=="repeater")return;e.id||(e.id=this.nextFieldId()),this.snapshot(),Array.isArray(n.fields)||(n.fields=[]);const i=n.fields,r=s===void 0?i.length:Math.max(0,Math.min(s,i.length));i.splice(r,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 n=this.locateField(e);if(!n)return;if(!n.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 r=i?i.fields:this.schema.fields;this.snapshot(),n.list.splice(n.index,1);const l=Math.max(0,Math.min(s,r.length));r.splice(l,0,n.field),this.selected=n.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=Ot(this.schema.fields,e,t);if(!s)return;this.snapshot();const[n]=this.schema.fields.splice(s.from,1);this.schema.fields.splice(s.to,0,n),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)),n=this.usedFieldIds(),i=()=>{let r=n.size+1;for(;n.has(`f${r}`);)r++;const l=`f${r}`;return n.add(l),l};s.id=i();for(const r of s.fields??[])r.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(r=>r.field===e)),n=s.length?`Delete this field? ${s.length} other field${s.length===1?"":"s"} use it in a condition, and those conditions will stop working.`:Ye("confirmDelete","Delete this? It cannot be undone.");await fe(n,"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 n=t.length+1;for(;s.has(`${e}${n}`);)n++;return`${e}${n}`}renderTypeSettings(e,t,s,n){for(const i of s){const r=It[i];r&&(this.inspector.append(Dt(e,r,n)),r.also&&this.inspector.append(Dt(e,{...r,key:r.also.key,label:r.also.label,hint:r.also.hint},n)))}s.includes("rows")&&e.type!=="likert"&&this.inspector.append(b("Lines tall",G(String(e.rows??""),i=>n("rows",i)))),s.includes("rows")&&e.type==="likert"&&this.inspector.append(this.renderStatementList(e,n)),s.includes("parts")&&this.inspector.append(this.renderPartsEditor(e,t,n)),s.includes("default")&&!["files","object"].includes(t?.value??"")&&this.inspector.append(this.renderDefaultControl(e,t,n))}renderStatementList(e,t){const s=Array.isArray(e.rows)?e.rows:[];return b("Statements",ue(s.map(n=>n.label??"").join(` -`),n=>t("rows",qt(s,n)),5),"One per line. Each becomes a row of the matrix.")}renderPartsEditor(e,t,s){const n=t?.parts??[],i=Array.isArray(e.parts)?e.parts:n.map(l=>l.key),r=n.map(l=>I(l.label,i.includes(l.key),c=>{const d=n.map(h=>h.key).filter(h=>h===l.key?c:i.includes(h));s("parts",d.length?d:[l.key])}));return o("div",{class:"atfb-section",children:[o("h4",{class:"atfb-section__title",text:"Parts to ask for"}),...r]})}renderDefaultControl(e,t,s){const n="Filled in before they start. They can change it.";return e.type==="switch"||e.type==="consent"?I("On by default",!!e.default,i=>s("default",i)):t?.choices&&(e.choices??[]).length?b("Default answer",q(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)),n):b("Default answer",C(String(e.default??""),i=>s("default",i)),n)}renderChoicesEditor(e,t){const s=e.choices??[],n=r=>this.liveField(e.id)?.choices?.[r],i=o("div",{class:"atfb-choices"});return s.forEach((r,l)=>{const c=o("div",{class:"atfb-choice-row",children:[e.type==="image_choice"?this.choiceImageWell(r,l,e):null,J(C(r.label,d=>{const h=n(l);if(!h)return;const w=!h.value||h.value===h.label;h.label=d,w&&(h.value=d),this.markDirty();const v=this.liveField(e.id);v&&this.syncCanvas(v)}),`choice:${l}:label`),J(C(r.value,d=>{const h=n(l);h&&(h.value=d,this.markDirty())},"value"),`choice:${l}:value`),e.type==="quiz"||r.points!==void 0?G(String(r.points??""),d=>{const h=n(l);h&&(h.points=d===""?void 0:Number(d),this.markDirty())}):G(String(r.price??""),d=>{const h=n(l);h&&(h.price=d===""?void 0:Number(d),this.markDirty())}),o("button",{class:"atfb-card__action",type:"button",attrs:{"aria-label":`Remove ${r.label}`},on:{click:()=>{const d=this.liveField(e.id);d&&((d.choices??[]).splice(l,1),t("choices",d.choices),this.renderInspector())}},children:[U("trash")]})]});i.append(c)}),o("div",{class:"atfb-section",children:[o("h4",{text:"Choices"}),o("p",{class:"atfb-hint",text:e.type==="quiz"?"Label, value, points.":"Label, value, and a price for calculations."}),i,L("Add choice",()=>{const r=this.liveField(e.id);if(!r)return;const l=r.choices??[];l.push({label:"",value:""}),t("choices",l),this.renderInspector()},"ghost","plus-alt2"),e.type==="quiz"?b("Correct answer",q(String(e.correct??""),[{value:"",label:"—"},...s.map(r=>({value:r.value,label:r.label}))],r=>t("correct",r))):null]})}choiceImageWell(e,t,s){const n=o("div",{class:`atfb-well${e.image?" has-image":""}`,attrs:{"data-choice":t,"aria-label":`Image for ${e.label||`choice ${t+1}`}`},children:[e.image?o("span",{class:"atfb-well__id",text:`#${e.image}`}):U("format-image")]}),i=K().registerDropTarget({id:`atfb-well-${s.id}-${t}`,element:n,accept:r=>vn.includes(r.type),onEnter:()=>n.classList.add("is-dropping"),onLeave:()=>n.classList.remove("is-dropping"),onDrop:r=>{n.classList.remove("is-dropping");const l=r.payload.data,c=Number(l.attachmentId??l.id??l.file?.id??0);if(!c){B("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),n.addEventListener("click",()=>{const r=this.liveField(s.id)?.choices?.[t];r?.image&&(r.image=void 0,this.markDirty(),this.renderInspector())}),n}renderValidationSection(e,t,s){const n=[],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)n.push(b(c,C(String(e[l]??""),d=>s(l,d))));t.includes("pattern")&&n.push(...this.renderAnswerShapeRows(e,s)),t.includes("unique")&&n.push(I("No two people may submit the same value",!!e.unique,l=>s("unique",l)));const r=e.messages??{};return t.includes("required")&&n.push(b("Message when required",C(r.required??"",l=>{r.required=l,s("messages",r)}),"Leave empty for the default wording.")),n.length?this.section(`validation:${e.id}`,"Validation",n):o("div",{class:"atfb-section is-empty"})}buildShapePicker(e,t){const s=[{heading:null,options:[{value:"",label:"Anything at all"}]},...fn.map(i=>({heading:i,options:Ct.filter(r=>r.group===i).map(r=>({value:r.slug,label:r.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 r of s){if(r.heading){const l=document.createElement("os-option");l.setAttribute("value",`__heading:${r.heading}`),l.setAttribute("disabled",""),l.textContent=r.heading,i.append(l)}for(const l of r.options){const c=document.createElement("os-option");c.setAttribute("value",l.value),c.textContent=l.label,i.append(c)}}return i.addEventListener("os-pick",r=>{t(String(r.detail?.value??""))}),i}const n=o("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 r=i.heading?(()=>{const l=document.createElement("optgroup");return l.label=i.heading,n.append(l),l})():n;for(const l of i.options)r.append(o("option",{value:l.value,text:l.label,attrs:{selected:l.value===e}}))}return n}renderAnswerShapeRows(e,t){const n=(typeof e.validation=="string"?e.validation:"")||(e.pattern?"custom":""),i=()=>hn({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()}),r=h=>{if(h==="custom"){i();return}t("validation",h),t("pattern",""),t("validationRecipe",""),this.renderInspector()},l=this.buildShapePicker(n,r),c=mn(n),d=[b("The answer should be",l,c?`e.g. ${c.example}`:"Checked as they type, and again on the server.")];if(n==="custom"){const h=St(String(e.validationRecipe??"")),w=Lt(h)||(e.pattern?`Matches the expression ${String(e.pattern)}`:"No rule yet — open the builder.");d.push(b("Your rule",o("div",{class:"atfb-valrule",children:[o("p",{class:"atfb-valrule__words",text:w}),L("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 n=(this.schema?.fields??[]).filter(d=>d.id!==s&&d.type!=="page_break"),i=()=>o("div",{class:"atfb-rule-join",children:[o("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)}})]}),r=(d,h)=>{const w=o("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:[U("trash")]}),v=[o("div",{class:"atfb-rule__top",children:[q(d.field,n.map(u=>({value:u.id,label:u.label||u.id})),u=>t(x=>{const g=x.rules[h];g&&(g.field=u,g.value="")},!0)),w]}),q(d.operator,Object.entries(this.config?.operators??{}).map(([u,x])=>({value:u,label:x})),u=>t(x=>{const g=x.rules[h];g&&(g.operator=u)},!0))];if(!Te.includes(d.operator)){const u=this.schema?.fields.find(x=>x.id===d.field);if(u?.choices?.length){const x=u.choices.map(g=>({value:g.value,label:g.label||g.value}));d.value!==""&&!u.choices.some(g=>g.value===d.value)&&x.unshift({value:d.value,label:d.value}),v.push(q(d.value,x,g=>t(f=>{const E=f.rules[h];E&&(E.value=g)})))}else v.push(C(d.value,x=>t(g=>{const f=g.rules[h];f&&(f.value=x)}),"The answer to compare against"))}return o("div",{class:"atfb-rule",children:v})},l=o("div",{class:"atfb-rules"});e.rules.forEach((d,h)=>{h>0&&l.append(i()),l.append(r(d,h))});const c=L("Add rule",()=>{t(d=>{d.rules.push({field:n[0]?.id??"",operator:"is",value:""})},!0)},"ghost","plus-alt2");return[l,c]}conditionsSection(e,t,s){const n=(r,l=!1)=>{r(s),this.markDirty(),l&&this.renderCanvas()},i=t==="confirmation"?"Use":"Send";return this.section(`conditions:${e}`,"Conditions",[I(`Only ${i.toLowerCase()} this ${t} sometimes`,s.enabled,r=>n(l=>{l.enabled=r},!0)),s.enabled?o("div",{children:[o("div",{class:"atfb-rule-head",children:[o("span",{text:`${i} it when`}),q(s.match,[{value:"all",label:"all"},{value:"any",label:"any"}],r=>n(l=>{l.match=r},!0)),o("span",{text:"of these match:"})]}),...this.logicRulesEditor(s,n)]}):null],s.enabled)}renderLogicSection(e){const t=e.logic,s=()=>this.liveField(e.id)?.logic,n=(r,l=!1)=>{const c=s();c&&(r(c),this.markDirty(),this.renderCanvas(),l&&this.renderInspector())},i=this.logicRulesEditor(t,n,e.id);return this.section(`logic:${e.id}`,"Conditional logic",[I("Only show this field sometimes",t.enabled,r=>{n(l=>{l.enabled=r},!0)}),t.enabled?o("div",{children:[o("div",{class:"atfb-rule-head",children:[q(t.action,[{value:"show",label:"Show"},{value:"hide",label:"Hide"}],r=>n(l=>{l.action=r},!0)),o("span",{text:"this field when"}),q(t.match,[{value:"all",label:"all"},{value:"any",label:"any"}],r=>n(l=>{l.match=r},!0)),o("span",{text:"of these match:"})]}),...i]}):null],t.enabled)}renderTabCanvas(){return this.schema?this.tab==="theme"?ut({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():o("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 n=/