@mapled/vanilla 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -5
- package/dist/mapled.js +17 -17
- package/dist/mapled.js.sha384 +1 -1
- package/dist/paths.d.ts +21 -6
- package/dist/paths.js +15 -8
- package/dist/runtime.d.ts +8 -3
- package/dist/runtime.js +111 -30
- package/dist/sinks.d.ts +9 -0
- package/dist/sinks.js +25 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
<html lang="en">
|
|
14
14
|
<head>
|
|
15
15
|
<title data-mapled-bind="home.headline">Studio Nord</title>
|
|
16
|
-
<script src="https://cdn.jsdelivr.net/npm/@mapled/vanilla@0.
|
|
16
|
+
<script src="https://cdn.jsdelivr.net/npm/@mapled/vanilla@0.3.0/dist/mapled.js" data-mapled-key="mk_live_…" defer></script>
|
|
17
17
|
</head>
|
|
18
18
|
<body>
|
|
19
19
|
<h1 data-mapled-bind="home.headline">Websites, built by hand</h1>
|
|
@@ -58,6 +58,35 @@ The key is the project's **delivery key** (Mapled: Integrations → Your site).
|
|
|
58
58
|
<li><strong>Designed</strong> by people</li>
|
|
59
59
|
</ul>
|
|
60
60
|
|
|
61
|
+
<!-- a components field: the blocks in their order, each from the template of its type -->
|
|
62
|
+
<main data-mapled-each="home.sections" data-mapled-expand="sections.picks">
|
|
63
|
+
<template data-mapled-type="hero">
|
|
64
|
+
<section class="hero">
|
|
65
|
+
<h2 data-mapled-bind="heading"></h2>
|
|
66
|
+
<a data-mapled-bind="button-label" data-mapled-bind-href="button-link"></a>
|
|
67
|
+
</section>
|
|
68
|
+
</template>
|
|
69
|
+
<template data-mapled-type="text">
|
|
70
|
+
<section data-mapled-bind="body" data-mapled-format="markdown"></section>
|
|
71
|
+
</template>
|
|
72
|
+
<!-- a block's relation: the posts it picks, read along by data-mapled-expand -->
|
|
73
|
+
<template data-mapled-type="featured">
|
|
74
|
+
<section>
|
|
75
|
+
<h2 data-mapled-bind="heading"></h2>
|
|
76
|
+
<ul data-mapled-each="picks">
|
|
77
|
+
<template>
|
|
78
|
+
<li><a data-mapled-link="/post.html?slug={slug}" data-mapled-bind="title"></a></li>
|
|
79
|
+
</template>
|
|
80
|
+
</ul>
|
|
81
|
+
</section>
|
|
82
|
+
</template>
|
|
83
|
+
<!-- every other type of block -->
|
|
84
|
+
<template>
|
|
85
|
+
<section><h2 data-mapled-bind="heading"></h2></section>
|
|
86
|
+
</template>
|
|
87
|
+
<section><h2>Studio Nord</h2></section>
|
|
88
|
+
</main>
|
|
89
|
+
|
|
61
90
|
<!-- a collection: the template once per record -->
|
|
62
91
|
<ul data-mapled-list="posts" data-mapled-sort="-date" data-mapled-limit="5" data-mapled-filter="featured=true">
|
|
63
92
|
<template>
|
|
@@ -71,11 +100,12 @@ The key is the project's **delivery key** (Mapled: Integrations → Your site).
|
|
|
71
100
|
</ul>
|
|
72
101
|
```
|
|
73
102
|
|
|
74
|
-
- **Paths.** From the document a path starts with a single's key: `home.headline`, `home.seo.title` for a field inside a group. Inside `data-mapled-list`, `data-mapled-each` and `data-mapled-record` it starts at the record: `title`, `author.name` for a linked record — name the relation in `data-mapled-expand="author"` on the list or the record element.
|
|
103
|
+
- **Paths.** From the document a path starts with a single's key: `home.headline`, `home.seo.title` for a field inside a group. Inside `data-mapled-list`, `data-mapled-each` and `data-mapled-record` it starts at the record: `title`, `author.name` for a linked record — name the relation in `data-mapled-expand="author"` on the list or the record element (on the element itself for a single). `data-mapled-each="related"` repeats the records a relation the read expanded links to, the template once per record.
|
|
75
104
|
- **`data-mapled-bind`** fills the element with text — on an `<img>`, with the picture. `data-mapled-format` says how: `markdown` for a rich text field, `date`, `datetime` and `number` for those, written the way the nearest `lang` attribute says.
|
|
76
105
|
- **`data-mapled-bind-…`** fills an attribute: `href` (on `a`, `area`), `src` (`img`, `video`, `audio`, `source`), `alt`, `title`, `aria-label`, `placeholder`, `datetime`. Nothing else — no `style`, no `class`, no `on…`.
|
|
77
106
|
- **`data-mapled-list`** reads a collection: `data-mapled-sort="-date"`, `data-mapled-limit` (1–100), `data-mapled-filter="featured=true; category=news"`. Whatever else sits inside the element is its fallback and leaves when records arrive; an element with `data-mapled-empty` shows when there are none.
|
|
78
107
|
- **`data-mapled-link`** builds an address from a template. Every `{field}` is percent-encoded before it goes in, so a value fills its place and nothing more.
|
|
108
|
+
- **Blocks.** A components field holds blocks of different types — a hero, a text, a gallery — in the order editors put them, and editors add, remove and reorder them in Mapled. `data-mapled-each` repeats them: each block takes the `<template data-mapled-type="…">` of its type (the type's key, as `MAPLED.md` lists it), or else the `<template>` without a type. A block the page has neither for is left out, and the report says `no-template`. Inside, paths start at the block: `heading`, `button-link`. A block's group — a pricing block's tiers — repeats inside the block's template with a `data-mapled-each` of its own, `data-mapled-each="tiers"`, and its paths start at the item: `name`, `price`. A block's relation — the posts a featured block picks — comes along when the read names the field and the sub-field, `data-mapled-expand="sections.picks"` on the each (on the list or the record element for a collection's records): inside the block's template `data-mapled-each="picks"` repeats the linked records, their paths starting at the record (`title`, `slug`), and `lead.title` reads a relation to one record. A JSON field's objects that carry a `_type` pick a template the same way; a list's records, a group's items and a relation's records have no type and take the `<template>` without one.
|
|
79
109
|
|
|
80
110
|
### One record per page
|
|
81
111
|
|
|
@@ -114,10 +144,11 @@ The key is the project's **delivery key** (Mapled: Integrations → Your site).
|
|
|
114
144
|
| `empty` | The record is there, a value at this path isn't: an editor left the field empty — or the key is misspelled | Nothing: the text is emptied, a picture is hidden, an attribute is removed (`alt` becomes `""`) |
|
|
115
145
|
| `failed` | The read didn't come back | The HTML you wrote |
|
|
116
146
|
| `unsafe-url` | An address that isn't `http:`, `https:` — or `mailto:`, `tel:` in a link — or that carries credentials; a value that can't go into a link template | The HTML you wrote |
|
|
117
|
-
| `type` | A value that has no text: a group bound as text, a word where a date goes | The HTML you wrote |
|
|
147
|
+
| `type` | A value that has no text: a group bound as text, a word where a date goes, a relation an each repeats that the read didn't expand | The HTML you wrote |
|
|
118
148
|
| `refused-element` | `<script>`, `<style>`, `<iframe>`, `<object>`, `<embed>`, `<base>`, `<link>`, `<meta>`, `<template>`, `<noscript>` are never written into | The HTML you wrote |
|
|
119
149
|
| `unsupported` | An attribute Mapled doesn't read — `data-mapled-bind-style`, a misspelled name — or a list without a `<template>` | The HTML you wrote |
|
|
120
150
|
| `bad-path` | A path, a key or an option that isn't one | The HTML you wrote |
|
|
151
|
+
| `no-template` | A block of a type the page has no `<template data-mapled-type>` for, and no `<template>` without a type to take it | Nothing for that block; the other blocks show |
|
|
121
152
|
|
|
122
153
|
```html
|
|
123
154
|
<!-- debug.html -->
|
|
@@ -187,11 +218,12 @@ if (report.state === "fallback") console.info("Showing the page as written.");
|
|
|
187
218
|
| `data-mapled-link` | `a`, `area` | an address template with `{field}` places |
|
|
188
219
|
| `data-mapled-list` | an element with a `<template>` | the collection to repeat the template for |
|
|
189
220
|
| `data-mapled-sort`, `data-mapled-limit`, `data-mapled-filter` | with `data-mapled-list` | the order, how many, which |
|
|
190
|
-
| `data-mapled-each` | an element with a `<template>` | the path of a group to repeat the template for |
|
|
221
|
+
| `data-mapled-each` | an element with a `<template>` | the path of a group, a components field or an expanded relation to repeat the template for |
|
|
222
|
+
| `data-mapled-type` | a `<template>` of an each | the type of block it shows |
|
|
191
223
|
| `data-mapled-empty` | inside a list or an each | shown when there is nothing to repeat |
|
|
192
224
|
| `data-mapled-record` | an element | the collection of the one record its content shows |
|
|
193
225
|
| `data-mapled-slug`, `data-mapled-slug-param` | with `data-mapled-record` | the record's slug; the query parameter that holds it |
|
|
194
|
-
| `data-mapled-expand` | a list, a record, a bound element | relation fields to read along |
|
|
226
|
+
| `data-mapled-expand` | a list, a record, a bound element, an each | relation fields to read along — `sections.picks` for a relation inside the blocks of `sections` |
|
|
195
227
|
| `data-mapled-form` | a `<form>` | the key of the Mapled form it posts to |
|
|
196
228
|
| `data-mapled-consent`, `data-mapled-form-sent`, `data-mapled-form-failed`, `data-mapled-end-preview` | inside a form; anywhere | the consent checkbox; the two outcomes; a way out of preview |
|
|
197
229
|
| `data-mapled-state`, `data-mapled-preview`, `data-mapled-form-state` | set by the script | `<html>`: how the fill went, whether drafts are shown; a form: `sending`, `sent`, `failed` |
|
package/dist/mapled.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
/*! @mapled/vanilla 0.
|
|
2
|
-
"use strict";(()=>{var $t=Object.defineProperty;var Lt=(e,t,n)=>t in e?$t(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var be=(e,t,n)=>Lt(e,typeof t!="symbol"?t+"":t,n);var we="mapled-preview",ie="mapled:preview",Ut=/^mpv_[A-Za-z0-9]{16,96}$/,Ze=/^mps_[A-Za-z0-9]{16,96}$/,Ve=new WeakMap,Nt=4e3;function ke(){if(typeof window>"u"||typeof document>"u")return null;try{let e=window.sessionStorage,t=Ve.get(window);return t||Ve.set(window,t={memory:null,exchanging:null,epoch:0}),{location:window.location,history:window.history,sessionStorage:e,page:t}}catch{return null}}function It(e){try{let t=e.getItem(ie);if(!t)return null;let n=JSON.parse(t),r=typeof n?.expiresAt=="string"?Date.parse(n.expiresAt):NaN;return typeof n?.draftToken!="string"||!Ze.test(n.draftToken)||!(r>Date.now())?(e.removeItem(ie),null):{draftToken:n.draftToken,expiresAt:n.expiresAt}}catch{return null}}function K(e){let t=It(e.sessionStorage);if(t)return t;let n=e.page.memory;return n&&Date.parse(n.expiresAt)>Date.now()?n:(e.page.memory=null,null)}function B(){let e=ke();return e?K(e):null}function Ye(e){let t;try{t=new URL(e.location.href)}catch{return null}let n=t.searchParams.get(we);if(n===null)return null;t.searchParams.delete(we);try{e.history.replaceState(e.history.state,"",`${t.pathname}${t.search}${t.hash}`)}catch{}return n}function Se(){let e=ke();if(e){e.page.memory=null,e.page.epoch++,e.page.exchanging=null,Ye(e);try{e.sessionStorage.removeItem(ie)}catch{}}}function Je(e={}){let t=ke();if(!t)return Promise.resolve(null);let n=Ye(t);if(n===null)return t.page.exchanging?t.page.exchanging.done:Promise.resolve(K(t));if(!Ut.test(n))return Promise.resolve(K(t));if(t.page.exchanging?.token===n)return t.page.exchanging.done;let r=(e.apiUrl??"https://api.mapled.io").replace(/\/+$/,""),s=(e.appUrl??"https://app.mapled.io").replace(/\/+$/,""),o=e.fetch??fetch,c=++t.page.epoch,a=(async()=>{try{let i=await o(`${r}/v1/delivery/preview/exchange`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({token:n}),cache:"no-store"});if(i.ok){let p=await i.json();if(typeof p.draftToken!="string"||typeof p.expiresAt!="string"||!Ze.test(p.draftToken)||!(Date.parse(p.expiresAt)>Date.now())||t.page.epoch!==c)return K(t);let d={draftToken:p.draftToken,expiresAt:p.expiresAt};t.page.memory=d;try{t.sessionStorage.setItem(ie,JSON.stringify(d))}catch{}return d}if(i.status===401&&t.page.epoch===c){let p=await i.json().catch(()=>null),d=p?.error?.projectId;if(t.page.epoch===c&&p?.error?.code==="PREVIEW_EXPIRED"&&typeof d=="string"&&/^[0-9a-f-]{8,64}$/i.test(d))return t.location.assign(`${s}/preview/expired?project=${encodeURIComponent(d)}`),await new Promise(l=>setTimeout(l,Nt)),K(t)}return K(t)}catch{return K(t)}})(),f={token:n,done:a};return t.page.exchanging=f,a.then(()=>{t.page.exchanging===f&&(t.page.exchanging=null)}),a}async function Qe(e){return(await Je(e))?.draftToken??null}function Ae(e){if(!e||typeof e!="object")return;let{release:t,publishedAt:n}=e;if(t===null)return n===null?null:void 0;if(!(!Number.isInteger(t)||t<1||typeof n!="string"))return{release:t,publishedAt:n}}var Ft=1e4;function Dt(e,t){let n=typeof document>"u"?void 0:globalThis.performance?.timeOrigin;if(n===void 0)return!1;let r=Date.parse(t.get("date")??""),s=Number.isNaN(r)?0:r+(Number(t.get("age"))||0)*1e3-Date.now();return Date.parse(e)>n+s-Ft}function et(e,t){return(n,r)=>{if(e!==void 0)return e;let s=t?.();return s!==void 0?s:Dt(n.publishedAt,r)?null:n.release}}function tt(e,t,n){let r=typeof e.key=="string"&&e.key.length>0;if(!r&&!e.endpoint)throw new Error("Mapled: watchRelease needs an endpoint or a delivery key.");let s=r?`${(e.apiUrl??"https://api.mapled.io").replace(/\/+$/,"")}/v1/delivery/release`:e.endpoint,o=r?{accept:"application/json","x-mapled-key":e.key}:{accept:"application/json"},c=(r?Math.max(15,e.interval??60):Math.max(2,e.interval??10))*1e3,a=e.fetch??fetch,f=typeof document>"u"?null:document,i,p=null,d=0,l=null,g=0,S=!1,E,M=null,b=()=>!f||f.visibilityState!=="hidden",$=()=>{S||!b()||(E=setTimeout(L,Math.min(c*2**g,3e5)))};async function L(){if(S||M)return;let U=new AbortController;M=U;let N=null;try{let X=await a(s,{cache:"no-cache",headers:o,signal:U.signal}),O=X.ok?Ae(await X.json()):void 0;if(S)return;O===void 0?g+=1:(g=0,i===void 0&&(i=O?t(O,X.headers):null),O&&O.release>(p?.release??0)&&(p=O),O&&O.release>(i??0)?(N={next:O,previous:i},i=O.release,d=O.release,l=null):O&&l!==null&&typeof i=="number"&&O.release>=i&&i>d&&(N={next:O,previous:l},d=i,l=null))}catch{if(S)return;g+=1}finally{M===U&&(M=null)}$(),N&&e.onPublish(N.next,N.previous)}let C=()=>{clearTimeout(E),b()&&L()};f?.addEventListener("visibilitychange",C);let z=n?.(U=>{if(S||typeof i!="number"||U>=i||i<=d||(l=l===null?U:Math.min(l,U),p===null||p.release<i))return;d=i;let N=l;l=null,e.onPublish(p,N)});return b()&&L(),()=>{S=!0,clearTimeout(E),M?.abort(),z?.(),f?.removeEventListener("visibilitychange",C)}}function nt(e){let t={};return e.forEach((n,r)=>{typeof n!="string"||r in t||(t[r]=n)}),t}async function Re(e,t,n){if(!n?.key)throw new Error("Mapled: a delivery key is required.");let r=(n.apiUrl??"https://api.mapled.io").replace(/\/+$/,""),s=typeof HTMLFormElement<"u"&&t instanceof HTMLFormElement?nt(new FormData(t)):typeof FormData<"u"&&t instanceof FormData?nt(t):{...t},o=n.page??(typeof window<"u"&&typeof document<"u"?window.location.pathname:void 0),c={...s};n.consent&&(c._consent=n.consent),o&&c._page===void 0&&(c._page=o),n.test&&(c._test=!0);let a=B()?.draftToken,f={"content-type":"application/json","x-mapled-key":n.key};a&&(f["x-mapled-draft"]=a);let i;try{i=await(n.fetch??fetch)(`${r}/v1/delivery/forms/${encodeURIComponent(e)}`,{method:"POST",headers:f,body:JSON.stringify(c)})}catch{return{ok:!1,status:0,code:"NETWORK",message:"Mapled couldn't be reached. Try again in a minute."}}if(i.ok)return{ok:!0};let p=`The submission wasn't accepted (${i.status}).`,d;try{let l=await i.json();l.error?.message&&(p=l.error.message),typeof l.error?.code=="string"&&(d=l.error.code)}catch{}return{ok:!1,status:i.status,code:d,message:p}}var ve=/^[a-z0-9][a-z0-9-]{0,63}$/,rt=new Set;function _t(e){if(typeof process<"u")return;let t=e?.aliases;if(!(!t||typeof t!="object")){for(let[n,r]of Object.entries(t))if(!(!r||typeof r!="object"||!ve.test(n)))for(let[s,o]of Object.entries(r)){if(typeof o!="string"||!ve.test(s)||!ve.test(o))continue;let c=`${n}.${s}`;rt.has(c)||(rt.add(c),console.warn(`[mapled] \u201C${s}\u201D in \u201C${n}\u201D was renamed to \u201C${o}\u201D. The old key keeps working until its alias is removed in Mapled \u2014 use \u201C${o}\u201D instead.`))}}}function it(e,t={}){let n=new URLSearchParams;t.width&&n.set("w",String(t.width)),t.height&&n.set("h",String(t.height)),t.fit&&n.set("fit",t.fit),t.format&&n.set("fmt",t.format),t.quality&&n.set("q",String(t.quality));let r=n.toString(),s=t.files?(t.files[e]??t.files[e.toLowerCase()])?.url:void 0;return typeof s=="string"&&s?r?`${s}${s.includes("?")?"&":"?"}${r}`:s:`${(t.apiUrl??"https://api.mapled.io").replace(/\/+$/,"")}/files/${encodeURIComponent(e)}${r?`?${r}`:""}`}var W=class extends Error{constructor(n,r,s){super(r);be(this,"status");be(this,"code");this.name="MapledError",this.status=n,this.code=s}},st=e=>Number.isInteger(e)&&e>=1,ot=()=>new Error("Mapled: release must be a positive whole number.");function lt(e){let t=(e.apiUrl??"https://api.mapled.io").replace(/\/+$/,"");if(!e.key)throw new Error("Mapled: a delivery key is required.");if(e.release!==void 0&&!st(e.release))throw ot();let n=(d,l)=>(e.fetch??fetch)(d,l),r,s=new Set;async function o(){return e.draft===null?null:typeof e.draft=="string"?e.draft||null:typeof e.draft=="function"?await e.draft()||null:Qe({apiUrl:t,appUrl:e.appUrl,fetch:e.fetch})}async function c(d){let l=`Mapled request failed (${d.status}).`,g;try{let S=await d.json();S.error?.message&&(l=S.error.message),typeof S.error?.code=="string"&&(g=S.error.code)}catch{}return new W(d.status,l,g)}async function a(d,l,g){let S=await o(),E=g.release??e.release;if(E!==void 0&&!st(E))throw ot();E!==void 0&&!S&&l.set("release",String(E));let M=g.locale||e.locale;M&&l.set("locale",M);let b=l.toString(),$=await n(`${t}${d}${b?`?${b}`:""}`,{headers:S?{"x-mapled-key":e.key,"x-mapled-draft":S}:{"x-mapled-key":e.key},...S?{cache:"no-store"}:{},...g.signal?{signal:g.signal}:{}});if(!$.ok)throw await c($);let L=await $.json();if(!S){let C=Number($.headers.get("x-mapled-release"));if(Number.isInteger(C)&&C>=1){r=r===void 0?C:Math.min(r,C);for(let z of s)Promise.resolve().then(()=>z(C))}}return _t(L),L}let f=(d,l)=>(l.expand?.length&&d.set("expand",l.expand.join(",")),l.fields?.length&&d.set("fields",l.fields.join(",")),d),i=async(d,l)=>{try{return(await a(d,f(new URLSearchParams,l),l)).record}catch(g){if(g instanceof W&&g.status===404&&g.code!=="RELEASE_NOT_FOUND")return null;throw g}};return{async getRecords(d,l={}){let g=new URLSearchParams;for(let[S,E]of Object.entries(l.filter??{}))if(E!==null&&typeof E=="object")for(let[M,b]of Object.entries(E))b!==void 0&&g.set(`filter[${S}][${M}]`,Array.isArray(b)?b.join(","):String(b));else g.set(`filter[${S}]`,String(E));return l.sort&&g.set("sort",l.sort),l.limit!==void 0&&g.set("limit",String(l.limit)),l.offset!==void 0&&g.set("offset",String(l.offset)),l.search&&g.set("search",l.search),f(g,l),a(`/v1/delivery/collections/${encodeURIComponent(d)}/records`,g,l)},getRecord(d,l,g={}){return i(`/v1/delivery/collections/${encodeURIComponent(d)}/records/${encodeURIComponent(l)}`,g)},getRecordBySlug(d,l,g={}){return i(`/v1/delivery/collections/${encodeURIComponent(d)}/records/by-slug/${encodeURIComponent(l)}`,g)},getSingle(d,l={}){return i(`/v1/delivery/singles/${encodeURIComponent(d)}`,l)},async isPreview(){return await o()!==null},watch(d,l={}){return tt({key:e.key,apiUrl:t,interval:l.interval,onPublish:d,fetch:e.fetch},et(l.from,()=>r),g=>(s.add(g),()=>{s.delete(g)}))},async getRelease(d={}){let l=await n(`${t}/v1/delivery/release`,{headers:{"x-mapled-key":e.key},cache:"no-cache",...d.signal?{signal:d.signal}:{}});if(!l.ok)throw await c(l);let g=Ae(await l.json());if(g===void 0)throw new Error("Mapled: the release pointer didn't come back as one \u2014 check the API origin.");return g}}}var jt=/^[a-z0-9][a-z0-9_-]{0,63}$/;function Z(e){if(typeof e!="string")return null;let t=e.trim().split(".");if(t.length>8)return null;for(let n of t)if(!jt.test(n))return null;return t}var le=(e,t)=>e!==null&&typeof e=="object"&&!Array.isArray(e)&&Object.prototype.hasOwnProperty.call(e,t)?e[t]:void 0;function Te(e,t){let n=e.data,r=e.expanded;for(let s=0;s<t.length;s++){let o=t[s],c=s<t.length-1?le(r,o):void 0;if(c!==void 0){if(c===null||Array.isArray(c))return;n=le(c,"data"),r=le(c,"expanded");continue}if(n=le(n,o),r=void 0,n===void 0)return}return n}var Y=e=>e==null||e===""||Array.isArray(e)&&e.length===0;function xe(e){return typeof e=="string"?e:typeof e=="number"&&Number.isFinite(e)?String(e):typeof e=="boolean"?e?"true":"false":Array.isArray(e)&&e.every(t=>typeof t=="string"||typeof t=="number")?e.join(", "):null}var Kt=new Set(["http:","https:","mailto:","tel:"]),Bt=new Set(["http:","https:"]),qt=2048;function q(e,t,n){if(typeof e!="string"||e.length>qt)return null;let r;try{r=new URL(e,t)}catch{return null}return!(n==="link"?Kt:Bt).has(r.protocol)||r.username!==""||r.password!==""?null:r.href}var Ht=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,at=e=>typeof e=="string"&&Ht.test(e);function ut(e,t,n){let r=[],s=e.replace(/\{([^{}]*)\}/g,(c,a)=>{let f=Z(a);if(!f)return r.push({ok:!1,reason:"bad-path",path:a.trim().slice(0,80)}),"";let i=n(f);if(typeof i!="string"&&typeof i!="number"||i==="")return r.push({ok:!1,reason:"empty",path:f.join(".")}),"";try{return encodeURIComponent(String(i))}catch{return r.push({ok:!1,reason:"unsafe-url",path:f.join(".")}),""}});if(r[0])return r[0];let o=q(s,t,"link");return o===null?{ok:!1,reason:"unsafe-url",path:e.slice(0,80)}:{ok:!0,url:o}}var zt=1e5,dt=8,ft=2048,Wt="!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~",ct={amp:"&",lt:"<",gt:">",quot:'"',apos:"'",nbsp:"\xA0",copy:"\xA9",reg:"\xAE",trade:"\u2122",mdash:"\u2014",ndash:"\u2013",hellip:"\u2026",laquo:"\xAB",raquo:"\xBB"};function pt(e,t,n){let r=t.createDocumentFragment();if(typeof e!="string"||e==="")return r;let s=e.replace(/\r\n?/g,`
|
|
3
|
-
`).replace(/\t/g," ").replace(/\u0000/g,"\uFFFD"),
|
|
4
|
-
`),r,0),r}function
|
|
5
|
-
`).trim();
|
|
1
|
+
/*! @mapled/vanilla 0.3.0 | MIT | https://mapled.io */
|
|
2
|
+
"use strict";(()=>{var It=Object.defineProperty;var Ft=(e,t,n)=>t in e?It(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var Se=(e,t,n)=>Ft(e,typeof t!="symbol"?t+"":t,n);var Re="mapled-preview",pe="mapled:preview",Dt=/^mpv_[A-Za-z0-9]{16,96}$/,Je=/^mps_[A-Za-z0-9]{16,96}$/,Ye=new WeakMap,_t=4e3;function Ae(){if(typeof window>"u"||typeof document>"u")return null;try{let e=window.sessionStorage,t=Ye.get(window);return t||Ye.set(window,t={memory:null,exchanging:null,epoch:0}),{location:window.location,history:window.history,sessionStorage:e,page:t}}catch{return null}}function jt(e){try{let t=e.getItem(pe);if(!t)return null;let n=JSON.parse(t),r=typeof n?.expiresAt=="string"?Date.parse(n.expiresAt):NaN;return typeof n?.draftToken!="string"||!Je.test(n.draftToken)||!(r>Date.now())?(e.removeItem(pe),null):{draftToken:n.draftToken,expiresAt:n.expiresAt}}catch{return null}}function K(e){let t=jt(e.sessionStorage);if(t)return t;let n=e.page.memory;return n&&Date.parse(n.expiresAt)>Date.now()?n:(e.page.memory=null,null)}function B(){let e=Ae();return e?K(e):null}function Qe(e){let t;try{t=new URL(e.location.href)}catch{return null}let n=t.searchParams.get(Re);if(n===null)return null;t.searchParams.delete(Re);try{e.history.replaceState(e.history.state,"",`${t.pathname}${t.search}${t.hash}`)}catch{}return n}function ve(){let e=Ae();if(e){e.page.memory=null,e.page.epoch++,e.page.exchanging=null,Qe(e);try{e.sessionStorage.removeItem(pe)}catch{}}}function et(e={}){let t=Ae();if(!t)return Promise.resolve(null);let n=Qe(t);if(n===null)return t.page.exchanging?t.page.exchanging.done:Promise.resolve(K(t));if(!Dt.test(n))return Promise.resolve(K(t));if(t.page.exchanging?.token===n)return t.page.exchanging.done;let r=(e.apiUrl??"https://api.mapled.io").replace(/\/+$/,""),s=(e.appUrl??"https://app.mapled.io").replace(/\/+$/,""),l=e.fetch??fetch,c=++t.page.epoch,o=(async()=>{try{let i=await l(`${r}/v1/delivery/preview/exchange`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({token:n}),cache:"no-store"});if(i.ok){let p=await i.json();if(typeof p.draftToken!="string"||typeof p.expiresAt!="string"||!Je.test(p.draftToken)||!(Date.parse(p.expiresAt)>Date.now())||t.page.epoch!==c)return K(t);let f={draftToken:p.draftToken,expiresAt:p.expiresAt};t.page.memory=f;try{t.sessionStorage.setItem(pe,JSON.stringify(f))}catch{}return f}if(i.status===401&&t.page.epoch===c){let p=await i.json().catch(()=>null),f=p?.error?.projectId;if(t.page.epoch===c&&p?.error?.code==="PREVIEW_EXPIRED"&&typeof f=="string"&&/^[0-9a-f-]{8,64}$/i.test(f))return t.location.assign(`${s}/preview/expired?project=${encodeURIComponent(f)}`),await new Promise(a=>setTimeout(a,_t)),K(t)}return K(t)}catch{return K(t)}})(),d={token:n,done:o};return t.page.exchanging=d,o.then(()=>{t.page.exchanging===d&&(t.page.exchanging=null)}),o}async function tt(e){return(await et(e))?.draftToken??null}function Te(e){if(!e||typeof e!="object")return;let{release:t,publishedAt:n}=e;if(t===null)return n===null?null:void 0;if(!(!Number.isInteger(t)||t<1||typeof n!="string"))return{release:t,publishedAt:n}}var Kt=1e4;function Bt(e,t){let n=typeof document>"u"?void 0:globalThis.performance?.timeOrigin;if(n===void 0)return!1;let r=Date.parse(t.get("date")??""),s=Number.isNaN(r)?0:r+(Number(t.get("age"))||0)*1e3-Date.now();return Date.parse(e)>n+s-Kt}function nt(e,t){return(n,r)=>{if(e!==void 0)return e;let s=t?.();return s!==void 0?s:Bt(n.publishedAt,r)?null:n.release}}function rt(e,t,n){let r=typeof e.key=="string"&&e.key.length>0;if(!r&&!e.endpoint)throw new Error("Mapled: watchRelease needs an endpoint or a delivery key.");let s=r?`${(e.apiUrl??"https://api.mapled.io").replace(/\/+$/,"")}/v1/delivery/release`:e.endpoint,l=r?{accept:"application/json","x-mapled-key":e.key}:{accept:"application/json"},c=(r?Math.max(15,e.interval??60):Math.max(2,e.interval??10))*1e3,o=e.fetch??fetch,d=typeof document>"u"?null:document,i,p=null,f=0,a=null,g=0,R=!1,O,P=null,w=()=>!d||d.visibilityState!=="hidden",U=()=>{R||!w()||(O=setTimeout(N,Math.min(c*2**g,3e5)))};async function N(){if(R||P)return;let I=new AbortController;P=I;let F=null;try{let J=await o(s,{cache:"no-cache",headers:l,signal:I.signal}),$=J.ok?Te(await J.json()):void 0;if(R)return;$===void 0?g+=1:(g=0,i===void 0&&(i=$?t($,J.headers):null),$&&$.release>(p?.release??0)&&(p=$),$&&$.release>(i??0)?(F={next:$,previous:i},i=$.release,f=$.release,a=null):$&&a!==null&&typeof i=="number"&&$.release>=i&&i>f&&(F={next:$,previous:a},f=i,a=null))}catch{if(R)return;g+=1}finally{P===I&&(P=null)}U(),F&&e.onPublish(F.next,F.previous)}let L=()=>{clearTimeout(O),w()&&N()};d?.addEventListener("visibilitychange",L);let W=n?.(I=>{if(R||typeof i!="number"||I>=i||i<=f||(a=a===null?I:Math.min(a,I),p===null||p.release<i))return;f=i;let F=a;a=null,e.onPublish(p,F)});return w()&&N(),()=>{R=!0,clearTimeout(O),P?.abort(),W?.(),d?.removeEventListener("visibilitychange",L)}}function st(e){let t={};return e.forEach((n,r)=>{typeof n!="string"||r in t||(t[r]=n)}),t}async function xe(e,t,n){if(!n?.key)throw new Error("Mapled: a delivery key is required.");let r=(n.apiUrl??"https://api.mapled.io").replace(/\/+$/,""),s=typeof HTMLFormElement<"u"&&t instanceof HTMLFormElement?st(new FormData(t)):typeof FormData<"u"&&t instanceof FormData?st(t):{...t},l=n.page??(typeof window<"u"&&typeof document<"u"?window.location.pathname:void 0),c={...s};n.consent&&(c._consent=n.consent),l&&c._page===void 0&&(c._page=l),n.test&&(c._test=!0);let o=B()?.draftToken,d={"content-type":"application/json","x-mapled-key":n.key};o&&(d["x-mapled-draft"]=o);let i;try{i=await(n.fetch??fetch)(`${r}/v1/delivery/forms/${encodeURIComponent(e)}`,{method:"POST",headers:d,body:JSON.stringify(c)})}catch{return{ok:!1,status:0,code:"NETWORK",message:"Mapled couldn't be reached. Try again in a minute."}}if(i.ok)return{ok:!0};let p=`The submission wasn't accepted (${i.status}).`,f;try{let a=await i.json();a.error?.message&&(p=a.error.message),typeof a.error?.code=="string"&&(f=a.error.code)}catch{}return{ok:!1,status:i.status,code:f,message:p}}var Ee=/^[a-z0-9][a-z0-9-]{0,63}$/,ot=new Set;function Ht(e){if(typeof process<"u")return;let t=e?.aliases;if(!(!t||typeof t!="object")){for(let[n,r]of Object.entries(t))if(!(!r||typeof r!="object"||!Ee.test(n)))for(let[s,l]of Object.entries(r)){if(typeof l!="string"||!Ee.test(s)||!Ee.test(l))continue;let c=`${n}.${s}`;ot.has(c)||(ot.add(c),console.warn(`[mapled] \u201C${s}\u201D in \u201C${n}\u201D was renamed to \u201C${l}\u201D. The old key keeps working until its alias is removed in Mapled \u2014 use \u201C${l}\u201D instead.`))}}}function at(e,t={}){let n=new URLSearchParams;t.width&&n.set("w",String(t.width)),t.height&&n.set("h",String(t.height)),t.fit&&n.set("fit",t.fit),t.format&&n.set("fmt",t.format),t.quality&&n.set("q",String(t.quality));let r=n.toString(),s=t.files?(t.files[e]??t.files[e.toLowerCase()])?.url:void 0;return typeof s=="string"&&s?r?`${s}${s.includes("?")?"&":"?"}${r}`:s:`${(t.apiUrl??"https://api.mapled.io").replace(/\/+$/,"")}/files/${encodeURIComponent(e)}${r?`?${r}`:""}`}var V=class extends Error{constructor(n,r,s){super(r);Se(this,"status");Se(this,"code");this.name="MapledError",this.status=n,this.code=s}},it=e=>Number.isInteger(e)&&e>=1,lt=()=>new Error("Mapled: release must be a positive whole number.");function ut(e){let t=(e.apiUrl??"https://api.mapled.io").replace(/\/+$/,"");if(!e.key)throw new Error("Mapled: a delivery key is required.");if(e.release!==void 0&&!it(e.release))throw lt();let n=(f,a)=>(e.fetch??fetch)(f,a),r,s=new Set;async function l(){return e.draft===null?null:typeof e.draft=="string"?e.draft||null:typeof e.draft=="function"?await e.draft()||null:tt({apiUrl:t,appUrl:e.appUrl,fetch:e.fetch})}async function c(f){let a=`Mapled request failed (${f.status}).`,g;try{let R=await f.json();R.error?.message&&(a=R.error.message),typeof R.error?.code=="string"&&(g=R.error.code)}catch{}return new V(f.status,a,g)}async function o(f,a,g){let R=await l(),O=g.release??e.release;if(O!==void 0&&!it(O))throw lt();O!==void 0&&!R&&a.set("release",String(O));let P=g.locale||e.locale;P&&a.set("locale",P);let w=a.toString(),U=await n(`${t}${f}${w?`?${w}`:""}`,{headers:R?{"x-mapled-key":e.key,"x-mapled-draft":R}:{"x-mapled-key":e.key},...R?{cache:"no-store"}:{},...g.signal?{signal:g.signal}:{}});if(!U.ok)throw await c(U);let N=await U.json();if(!R){let L=Number(U.headers.get("x-mapled-release"));if(Number.isInteger(L)&&L>=1){r=r===void 0?L:Math.min(r,L);for(let W of s)Promise.resolve().then(()=>W(L))}}return Ht(N),N}let d=(f,a)=>(a.expand?.length&&f.set("expand",a.expand.join(",")),a.fields?.length&&f.set("fields",a.fields.join(",")),f),i=async(f,a)=>{try{return(await o(f,d(new URLSearchParams,a),a)).record}catch(g){if(g instanceof V&&g.status===404&&g.code!=="RELEASE_NOT_FOUND")return null;throw g}};return{async getRecords(f,a={}){let g=new URLSearchParams;for(let[R,O]of Object.entries(a.filter??{}))if(O!==null&&typeof O=="object")for(let[P,w]of Object.entries(O))w!==void 0&&g.set(`filter[${R}][${P}]`,Array.isArray(w)?w.join(","):String(w));else g.set(`filter[${R}]`,String(O));return a.sort&&g.set("sort",a.sort),a.limit!==void 0&&g.set("limit",String(a.limit)),a.offset!==void 0&&g.set("offset",String(a.offset)),a.search&&g.set("search",a.search),d(g,a),o(`/v1/delivery/collections/${encodeURIComponent(f)}/records`,g,a)},getRecord(f,a,g={}){return i(`/v1/delivery/collections/${encodeURIComponent(f)}/records/${encodeURIComponent(a)}`,g)},getRecordBySlug(f,a,g={}){return i(`/v1/delivery/collections/${encodeURIComponent(f)}/records/by-slug/${encodeURIComponent(a)}`,g)},getSingle(f,a={}){return i(`/v1/delivery/singles/${encodeURIComponent(f)}`,a)},async isPreview(){return await l()!==null},watch(f,a={}){return rt({key:e.key,apiUrl:t,interval:a.interval,onPublish:f,fetch:e.fetch},nt(a.from,()=>r),g=>(s.add(g),()=>{s.delete(g)}))},async getRelease(f={}){let a=await n(`${t}/v1/delivery/release`,{headers:{"x-mapled-key":e.key},cache:"no-cache",...f.signal?{signal:f.signal}:{}});if(!a.ok)throw await c(a);let g=Te(await a.json());if(g===void 0)throw new Error("Mapled: the release pointer didn't come back as one \u2014 check the API origin.");return g}}}var qt=/^[a-z0-9][a-z0-9_-]{0,63}$/;function te(e){if(typeof e!="string")return null;let t=e.trim().split(".");if(t.length>8)return null;for(let n of t)if(!qt.test(n))return null;return t}var z=(e,t)=>e!==null&&typeof e=="object"&&!Array.isArray(e)&&Object.prototype.hasOwnProperty.call(e,t)?e[t]:void 0;function Me(e,t){let n=e.data,r=e.expanded,s=e.expandedBlocks;for(let l=0;l<t.length;l++){let c=t[l],o=z(r,c);if(l===t.length-1)return{value:z(n,c),linked:o,blocks:z(s,c)};if(o!==void 0){if(o===null||Array.isArray(o))return{value:void 0};n=z(o,"data"),r=z(o,"expanded"),s=z(o,"expandedBlocks");continue}if(n=z(n,c),r=void 0,s=void 0,n===void 0)return{value:void 0}}return{value:n}}var ne=e=>e==null||e===""||Array.isArray(e)&&e.length===0;function Oe(e){return typeof e=="string"?e:typeof e=="number"&&Number.isFinite(e)?String(e):typeof e=="boolean"?e?"true":"false":Array.isArray(e)&&e.every(t=>typeof t=="string"||typeof t=="number")?e.join(", "):null}var zt=new Set(["http:","https:","mailto:","tel:"]),Wt=new Set(["http:","https:"]),Gt=2048;function H(e,t,n){if(typeof e!="string"||e.length>Gt)return null;let r;try{r=new URL(e,t)}catch{return null}return!(n==="link"?zt:Wt).has(r.protocol)||r.username!==""||r.password!==""?null:r.href}var Xt=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,ct=e=>typeof e=="string"&&Xt.test(e);function dt(e,t,n){let r=[],s=e.replace(/\{([^{}]*)\}/g,(c,o)=>{let d=te(o);if(!d)return r.push({ok:!1,reason:"bad-path",path:o.trim().slice(0,80)}),"";let i=n(d);if(typeof i!="string"&&typeof i!="number"||i==="")return r.push({ok:!1,reason:"empty",path:d.join(".")}),"";try{return encodeURIComponent(String(i))}catch{return r.push({ok:!1,reason:"unsafe-url",path:d.join(".")}),""}});if(r[0])return r[0];let l=H(s,t,"link");return l===null?{ok:!1,reason:"unsafe-url",path:e.slice(0,80)}:{ok:!0,url:l}}var Vt=1e5,pt=8,mt=2048,Zt="!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~",ft={amp:"&",lt:"<",gt:">",quot:'"',apos:"'",nbsp:"\xA0",copy:"\xA9",reg:"\xAE",trade:"\u2122",mdash:"\u2014",ndash:"\u2013",hellip:"\u2026",laquo:"\xAB",raquo:"\xBB"};function gt(e,t,n){let r=t.createDocumentFragment();if(typeof e!="string"||e==="")return r;let s=e.replace(/\r\n?/g,`
|
|
3
|
+
`).replace(/\t/g," ").replace(/\u0000/g,"\uFFFD"),l={doc:t,base:n};if(s.length>Vt){for(let c of s.split(/\n{2,}/))c.trim()!==""&&(C(l,"p",r).textContent=c.trim());return r}return Ne(l,s.split(`
|
|
4
|
+
`),r,0),r}function C(e,t,n){let r=e.doc.createElement(t);return n.appendChild(r),r}var Pe=e=>e.trim()==="",se=e=>e.length-e.trimStart().length;function ht(e){let t=se(e);if(t>3)return null;let n=0;for(;n<7&&e[t+n]==="#";)n++;if(n===0||n>6)return null;let r=e[t+n];if(r!==void 0&&r!==" ")return null;let s=e.slice(t+n).trim(),l=s.length;for(;l>0&&s[l-1]==="#";)l--;return l<s.length&&(l===0||s[l-1]===" ")&&(s=s.slice(0,l).trim()),{level:n,text:s}}function Ue(e){if(se(e)>3)return!1;let t=e.replace(/ /g,""),n=t[0];if(t.length<3||n!=="-"&&n!=="*"&&n!=="_")return!1;for(let r of t)if(r!==n)return!1;return!0}function Ce(e){let t=/^ {0,3}(`{3,}|~{3,})/.exec(e);if(!t)return null;let n=t[1][0];return n==="`"&&e.slice(t[0].length).includes("`")?null:{mark:n,size:t[1].length}}function re(e){let t=/^( {0,3})([-*+]|\d{1,9}[.)])( +|$)/.exec(e);if(!t)return null;let n=t[2],r=t[3].length,s=n.length>1||/\d/.test(n);return{indent:t[1].length,width:n.length+(r>=1&&r<=4?r:1),ordered:s,start:s?parseInt(n,10):1,rest:e.slice(t[0].length)}}var $e=e=>/^ {0,3}>/.test(e);function Yt(e){if(ht(e)!==null||Ce(e)!==null||$e(e)||Ue(e))return!0;let t=re(e);return t!==null&&t.rest!==""&&(!t.ordered||t.start===1)}function Ne(e,t,n,r){if(r>=pt){let l=t.join(`
|
|
5
|
+
`).trim();l!==""&&(C(e,"p",n).textContent=l);return}let s=0;for(;s<t.length;){let l=t[s];if(Pe(l)){s++;continue}let c=Ce(l);if(c){let p=[],f=se(l);for(s++;s<t.length;){let a=t[s],g=Ce(a);if(g&&g.mark===c.mark&&g.size>=c.size&&a.trim().length===g.size)break;p.push(a.slice(Math.min(f,se(a)))),s++}s++,C(e,"code",C(e,"pre",n)).textContent=p.length>0?`${p.join(`
|
|
6
6
|
`)}
|
|
7
|
-
`:"";continue}let
|
|
8
|
-
`),
|
|
9
|
-
`,
|
|
10
|
-
&`);if(n===-1)return null;let r=e.slice(t+1,n);if(r[0]==="#"){let
|
|
11
|
-
<`);if(c===-1)return null;s=e.slice(r+1,c),r=c+1}else{let c=r,
|
|
12
|
-
`)break;if(
|
|
13
|
-
`);)r++;let
|
|
14
|
-
`);)r++}return e[r]!==")"?null:{address:
|
|
15
|
-
`)for(
|
|
16
|
-
`){let i=s.endsWith(" "),p=s.length;for(;p>0&&s[p-1]===" ";)p--;for(s=s.slice(0,p),i?(
|
|
17
|
-
`,
|
|
18
|
-
<`),p=i===-1?null:t.slice(a+1,i);if(p!==null&&Jt.test(p)){let d=q(p.includes(":")?p:`mailto:${p}`,e.base,"link");if(d!==null){o();let l=P(e,"a",n);l.setAttribute("href",d),l.textContent=p,a=i+1;continue}}}if(f==="&"){let i=Zt(t,a);if(i){s+=i.char,a=i.end;continue}}if(f==="*"||f==="_"||f==="~"){let i=ee(t,a,f),p=i-a,d=!Le(t[i])&&(f!=="_"||!gt(t[a-1])),l=f==="~"?p>=2?[["~~",["del"]]]:[]:[...p>=3?[[f.repeat(3),["em","strong"]]]:[],...p>=2?[[f.repeat(2),["strong"]]]:[],[f,["em"]]],g=!1;if(d)for(let[S,E]of l){if(c.has(S))continue;let M=a+S.length,b=Vt(t,M,S);if(b===-1){c.add(S);continue}if(b===M)continue;o();let $=n;for(let L of E)$=P(e,L,$);ue(e,t.slice(M,b),$,r+1),a=b+S.length,g=!0;break}g||(s+=t.slice(a,i),a=i);continue}s+=f,a++}o()}var Qt="http://www.w3.org/1999/xhtml",en=new Set(["script","style","iframe","frame","frameset","object","embed","applet","base","link","meta","template","noscript"]),ce={href:{on:["a","area"],holds:"link"},src:{on:["img","video","audio","source"],holds:"media"},alt:{on:["img","area"],holds:"text"},title:{on:null,holds:"text"},"aria-label":{on:null,holds:"text"},placeholder:{on:["input","textarea"],holds:"text"},datetime:{on:["time","del","ins"],holds:"text"}},de=e=>e.namespaceURI===Qt,te=e=>!en.has(e.localName.toLowerCase());function Ue(e,t){if(!te(e))return"refused-element";let n=xe(t);return n===null?"type":(e.textContent=n,null)}function wt(e,t,n){return!te(e)||!de(e)?"refused-element":typeof t!="string"?"type":(e.replaceChildren(pt(t,e.ownerDocument,n.base)),null)}function kt(e,t,n){return typeof e!="string"?null:at(e)?it(e,{...t.apiUrl?{apiUrl:t.apiUrl}:{},...n?{width:n}:{}}):q(e,t.base,"media")}function St(e,t,n,r){if(!de(e)||e.localName!=="img")return"refused-element";if(typeof t!="string")return"type";let s=kt(t,n,r);return s===null?"unsafe-url":(e.removeAttribute("srcset"),e.setAttribute("src",s),null)}function At(e,t,n,r,s){let o=t.toLowerCase(),c=Object.prototype.hasOwnProperty.call(ce,o)?ce[o]:void 0;if(!c)return"unsupported";if(!te(e)||!de(e))return"refused-element";if(c.on!==null&&!c.on.includes(e.localName))return"unsupported";if(c.holds==="text"){let f=xe(n);return f===null?"type":(e.setAttribute(o,f),null)}if(typeof n!="string")return"type";let a=c.holds==="media"?kt(n,r,e.localName==="img"?s:void 0):q(n,r.base,"link");return a===null?"unsafe-url":(e.setAttribute(o,a),null)}function Rt(e,t){return!de(e)||e.localName!=="a"&&e.localName!=="area"?"unsupported":(e.setAttribute("href",t),null)}function G(e,t){t?e.removeAttribute("hidden"):e.setAttribute("hidden","")}var vt=["text","markdown","date","datetime","number"],tn=/^\d{4}-\d{2}-\d{2}$/,nn=/^\d{4}-\d{2}-\d{2}(?:[T ]\d{2}:\d{2}(?::\d{2}(?:\.\d{1,9})?)?(?:Z|[+-]\d{2}:?\d{2})?)?$/;function Tt(e,t,n){let r=n&&/^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})*$/.test(n)?n:void 0;try{if(t==="number"){let c=typeof e=="number"?e:typeof e=="string"&&e.trim()!==""?Number(e):NaN;return Number.isFinite(c)?new Intl.NumberFormat(r).format(c):null}if(typeof e!="string"||!nn.test(e))return null;let s=new Date(e);if(Number.isNaN(s.getTime()))return null;let o=tn.test(e);return new Intl.DateTimeFormat(r,{dateStyle:"long",...t==="datetime"&&!o?{timeStyle:"short"}:{},...o?{timeZone:"UTC"}:{}}).format(s)}catch{return null}}var A="data-mapled-",rn=new Set(["bind","format","width","show","hide","link","list","each","record","slug","slug-param","sort","limit","filter","expand","empty","form","form-state","form-sent","form-failed","consent","end-preview","state","preview","key","api","live","locale"]),Ie=/^[a-z0-9][a-z0-9_-]{0,63}$/,sn=/^mk_[A-Za-z0-9_]{8,128}$/,on=4,ln=100,an=2;function re(e,t=0){if(e===null||typeof e!="object"||Array.isArray(e))return!1;let{data:n,expanded:r}=e;if(n===null||typeof n!="object"||Array.isArray(n))return!1;if(r===void 0)return!0;if(r===null||typeof r!="object"||Array.isArray(r))return!1;let s=Object.values(r);return t>=an?s.length===0:s.every(o=>o===null||(Array.isArray(o)?o.every(c=>re(c,t+1)):re(o,t+1)))}var un=e=>Array.isArray(e)&&e.every(t=>re(t));function _(e){let t=e.getAttribute("id");return`${e.localName}${t&&/^[\w-]{1,40}$/.test(t)?`#${t}`:""}`}var pe=e=>(e??"").split(",").map(t=>t.trim()).filter(t=>/^[a-z0-9][a-z0-9_.-]{0,127}$/.test(t)),xt=e=>Array.from(e.children).find(t=>t.localName==="template")??null;function Ne(e,t){let n=null,r=()=>n??(n={format:"text",attrs:[],expand:[]});for(let a of Array.from(e.attributes)){if(!a.name.startsWith(A))continue;let f=a.name.slice(A.length);if(f==="bind")r().bind=a.value;else if(f.startsWith("bind-")){let i=f.slice(5);Object.prototype.hasOwnProperty.call(ce,i)?r().attrs.push([i,a.value]):t.push({path:a.value.slice(0,80),reason:"unsupported",on:`${_(e)}[${a.name.slice(0,60)}]`})}else f==="show"?r().show=a.value:f==="hide"?r().hide=a.value:f==="link"?r().link=a.value:rn.has(f)||t.push({path:a.value.slice(0,80),reason:"unsupported",on:`${_(e)}[${a.name.slice(0,60)}]`})}if(!n)return null;let s=n,o=e.getAttribute(`${A}format`);o!==null&&(vt.includes(o)?s.format=o:t.push({path:o.slice(0,80),reason:"bad-path",on:`${_(e)}[${A}format]`}));let c=parseInt(e.getAttribute(`${A}width`)??"",10);return c>=16&&c<=4096&&(s.width=c),s.expand=pe(e.getAttribute(`${A}expand`)),s}function cn(e,t){let n={},r=e.getAttribute(`${A}sort`);r!==null&&(/^-?[a-z0-9][a-z0-9_-]{0,63}$/.test(r.trim())?n.sort=r.trim():t.push({path:r.slice(0,80),reason:"bad-path",on:`${_(e)}[${A}sort]`}));let s=parseInt(e.getAttribute(`${A}limit`)??"",10);s>=1&&s<=100&&(n.limit=s);let o=e.getAttribute(`${A}filter`);if(o!==null)for(let a of o.split(";")){if(a.trim()==="")continue;let f=a.indexOf("="),i=f===-1?"":a.slice(0,f).trim(),p=f===-1?"":a.slice(f+1).trim();Ie.test(i)&&p!==""&&p.length<=200?(n.filter??(n.filter={}))[i]=p:t.push({path:a.trim().slice(0,80),reason:"bad-path",on:`${_(e)}[${A}filter]`})}let c=pe(e.getAttribute(`${A}expand`));return c.length>0&&(n.expand=c),n}function fe(e,t){let n={bound:[],eaches:[],lists:[],records:[],issues:[]},r=s=>{if(s.hasAttributes()){let o=s.getAttribute(`${A}list`),c=s.getAttribute(`${A}record`),a=s.getAttribute(`${A}each`);if(o!==null||c!==null){let i=(o??c??"").trim();if(!t)n.issues.push({path:i.slice(0,80),reason:"unsupported",on:_(s)});else if(!Ie.test(i))n.issues.push({path:i.slice(0,80),reason:"bad-path",on:_(s)});else if(o!==null)n.lists.push({el:s,collection:i,options:cn(s,n.issues)});else{let p=s.getAttribute(`${A}slug-param`),d=s.getAttribute(`${A}slug`)??(p===null?null:new URLSearchParams(s.ownerDocument.location?.search??"").get(p)),l=fe(s,!0);for(let S of l.records)n.issues.push({path:S.collection,reason:"unsupported",on:_(S.el)});n.lists.push(...l.lists),n.issues.push(...l.issues);let g=Ne(s,n.issues);g&&l.bound.unshift({el:s,d:g}),n.records.push({el:s,collection:i,slug:d!==null&&d.trim()!==""&&d.length<=200?d.trim():null,expand:pe(s.getAttribute(`${A}expand`)),block:{...l,lists:[],records:[],issues:[]}})}return}if(a!==null){n.eaches.push({el:s,path:a});return}let f=Ne(s,n.issues);f&&n.bound.push({el:s,d:f})}for(let o of Array.from(s.children))r(o)};for(let s of Array.from(e.children))r(s);return n}function Et(e){let t=new Map,n=(r,s=[])=>{let o=Z(r)?.[0];if(!o)return;let c=t.get(o)??new Set;for(let a of s)c.add(a);t.set(o,c)};for(let{d:r}of e.bound){n(r.bind,r.expand);for(let[,s]of r.attrs)n(s,r.expand);n(r.show,r.expand),n(r.hide,r.expand);for(let s of(r.link??"").matchAll(/\{([^{}]*)\}/g))n(s[1],r.expand)}for(let r of e.eaches)n(r.path,pe(r.el.getAttribute(`${A}expand`)));return t}function ne(e,t){let n=Z(t);if(!n)return{status:"bad-path"};if(e.kind==="record")return{status:"value",value:Te(e.source,n)};if(n.length<2)return{status:"bad-path"};let r=e.singles.get(n[0]);return!r||r.status!=="ok"?{status:r?.status==="failed"?"failed":"missing"}:{status:"value",value:Te(r.value,n.slice(1))}}function Fe(e){if(typeof e?.key!="string"||!sn.test(e.key))throw new Error("Mapled: data-mapled-key takes the delivery key (mk_\u2026) \u2014 the one key that is safe in a page. A server key or a secret never goes into HTML.");if(typeof document>"u")throw new Error("Mapled: @mapled/vanilla fills a page \u2014 it runs in a browser. On a server read with @mapled/client.");let t=e.root??document.documentElement,n=t.ownerDocument,r=lt({key:e.key,apiUrl:e.apiUrl,appUrl:e.appUrl,fetch:e.fetch,...e.locale?{locale:e.locale}:{}}),s={apiUrl:e.apiUrl,base:n.baseURI},o=new Map,c=u=>{let m=o.get(u);return m||o.set(u,m={attrs:new Map}),m},a=u=>{let m=c(u);m.nodes??(m.nodes=Array.from(u.childNodes))},f=(u,m)=>{let R=c(u);R.attrs.has(m)||R.attrs.set(m,u.getAttribute(m))},i=()=>{for(let[u,m]of o){m.nodes&&u.replaceChildren(...m.nodes);for(let[R,h]of m.attrs)h===null?u.removeAttribute(R):u.setAttribute(R,h)}o.clear()},p=[],d=0,l=(u,m,R)=>p.push({path:(m??"").trim().slice(0,120),reason:R,on:_(u)});function g(u,m,R){if(m.bind!==void 0){let h=ne(R,m.bind);if(h.status!=="value")l(u,m.bind,h.status);else if(!te(u))l(u,m.bind,"refused-element");else{let w=u.localName==="img";if(w?(f(u,"src"),f(u,"srcset"),f(u,"hidden")):a(u),Y(h.value))w?G(u,!1):u.textContent="",l(u,m.bind,"empty");else{let k;if(w)k=St(u,h.value,s,m.width);else if(m.format==="markdown")k=wt(u,h.value,s);else if(m.format==="text")k=Ue(u,h.value);else{let v=Tt(h.value,m.format,u.closest("[lang]")?.getAttribute("lang")??void 0);k=v===null?"type":Ue(u,v)}k?l(u,m.bind,k):d++}}}for(let[h,w]of m.attrs){let k=ne(R,w);if(k.status!=="value"){l(u,w,k.status);continue}if(f(u,h),Y(k.value)){h==="alt"?u.setAttribute("alt",""):u.removeAttribute(h),l(u,w,"empty");continue}let v=u.getAttribute(h),T=At(u,h,k.value,s,m.width);T?(v===null&&u.removeAttribute(h),l(u,w,T)):d++}for(let[h,w]of[[m.show,!0],[m.hide,!1]]){if(h===void 0)continue;let k=ne(R,h);if(k.status!=="value"){l(u,h,k.status);continue}f(u,"hidden");let v=!Y(k.value)&&k.value!==!1;G(u,v===w),d++}if(m.link!==void 0){let h=[],w=ut(m.link,s.base,k=>{let v=ne(R,k.join("."));if(v.status==="value")return v.value;h.push(v.status)});if(h[0])l(u,m.link,h[0]);else if(!w.ok)l(u,w.path,w.reason);else{f(u,"href");let k=Rt(u,w.url);k?l(u,m.link,k):d++}}}function S(u,m,R){let h=xt(u);if(!h){l(u,"template","unsupported");return}a(u);let w=[h];for(let v of Array.from(u.children))v.hasAttribute(`${A}empty`)&&(f(v,"hidden"),G(v,m.length===0),w.push(v));let k=[];for(let v of m.slice(0,ln)){let T=h.content.cloneNode(!0);E(fe(T,!1),{kind:"record",source:v},R+1),k.push(...Array.from(T.childNodes))}u.replaceChildren(...w,...k),d++}function E(u,m,R){p.push(...u.issues);for(let{el:h,d:w}of u.bound)g(h,w,m);for(let h of u.eaches){let w=ne(m,h.path);if(w.status!=="value"){l(h.el,h.path,w.status);continue}if(R>=on){l(h.el,h.path,"unsupported");continue}let k=w.value,v=Array.isArray(k)?k:Y(k)?[]:[k];if(v.some(T=>T===null||typeof T!="object"||Array.isArray(T))){l(h.el,h.path,"type");continue}v.length===0&&l(h.el,h.path,"empty"),S(h.el,v.map(T=>({data:T})),R)}}async function M(u,m){try{let R=await u();return R===null?{status:"missing"}:m(R)?{status:"ok",value:R}:{status:"failed",answered:!0}}catch(R){return R instanceof W&&R.status===404&&R.code!=="RELEASE_NOT_FOUND"?{status:"missing"}:{status:"failed"}}}let b=null,$="",L=null,C,z=!1,U=0,N=-1,X=3,O=typeof e.live=="number"?e.live:void 0;function De(){i(),b={state:"fallback",release:null,preview:!1,bound:0,issues:[]},t.setAttribute(`${A}state`,"fallback"),t.removeAttribute(`${A}preview`)}function _e(){U++,clearTimeout(C),b?.preview&&De()}function Ot(){clearTimeout(C);let u=B();if(!u||z)return;let m=Date.parse(u.expiresAt)-Date.now();C=setTimeout(()=>{B()===null&&_e(),ge()},Math.min(Math.max(m,0)+1,2147483647))}async function je(u,m=1){let R=b===null;R&&t.setAttribute(`${A}state`,"loading");let h=U,w=await r.isPreview().catch(()=>!1),k=w?B()?.draftToken??null:null;if(b!==null&&b.preview&&!w&&De(),!w&&u!==void 0&&b!==null&&b.state==="ready"&&!b.preview&&b.release!==null&&u<=b.release)return b;let v=y=>{b=y,N=h,t.setAttribute(`${A}state`,y.state),t.toggleAttribute(`${A}preview`,y.preview);let x=y.issues.filter(D=>D.reason!=="empty"),I=x.map(D=>`${D.path} (${D.reason}) on ${D.on}`).join("; ");return I!==$&&($=I,x.length>0&&console.warn(`[mapled] ${x.length===1?"1 place":`${x.length} places`} couldn't be filled: ${I}`)),n.dispatchEvent(new CustomEvent(R?"mapled:ready":"mapled:update",{detail:y})),y.preview?Ot():(clearTimeout(C),e.live&&L===null&&(L=r.watch(D=>{ge(D.release)},{interval:O,from:y.state==="ready"?y.release:null}))),y},T=null;if(!w)if(u!==void 0)T=u;else try{let y=await r.getRelease();if(y===null)return i(),v({state:"unpublished",release:null,preview:w,bound:0,issues:[]});T=y.release}catch{return b!==null&&b.state==="ready"?b:v({state:"fallback",release:null,preview:w,bound:0,issues:[]})}let F=T===null?{}:{release:T},j=fe(t,!0),We=Ne(t,j.issues);We&&j.bound.unshift({el:t,d:We});let he=new Map,ye=new Map,se=new Map,oe=[];for(let[y,x]of Et(j))oe.push(M(()=>r.getSingle(y,{...F,...x.size>0?{expand:[...x]}:{}}),re).then(I=>he.set(y,I)));for(let y of j.lists){let x=xt(y.el),I=x?[...Et(fe(x.content,!1)).keys()]:[];oe.push(M(async()=>(await r.getRecords(y.collection,{...y.options,...F,...I.length>0?{fields:I}:{}})).records,un).then(D=>ye.set(y,D)))}for(let y of j.records){if(y.slug===null){se.set(y,{status:"missing"});continue}let x=y.slug;oe.push(M(()=>r.getRecordBySlug(y.collection,x,{...F,...y.expand.length>0?{expand:y.expand}:{}}),re).then(I=>se.set(y,I)))}await Promise.all(oe);let Ge=await r.isPreview().catch(()=>!1)?B()?.draftToken??null:null;if(h!==U||Ge!==k)return m<X?je(u,m+1):(i(),v({state:"fallback",release:null,preview:Ge!==null,bound:0,issues:[]}));let Xe=[...he.values(),...ye.values(),...se.values()];if(Xe.length>0&&Xe.every(y=>y.status==="failed"&&!y.answered))return b!==null&&b.state==="ready"?b:v({state:"fallback",release:T,preview:w,bound:0,issues:[]});i(),p=[],d=0,E(j,{kind:"page",singles:he},0);for(let y of j.lists){let x=ye.get(y);x?.status==="ok"?S(y.el,x.value,0):l(y.el,y.collection,x?.status??"failed")}for(let y of j.records){let x=se.get(y);x?.status==="ok"?E(y.block,{kind:"record",source:x.value},0):l(y.el,y.collection,x?.status??"failed")}return qe(),v({state:"ready",release:T,preview:w,bound:d,issues:p})}let Ke=new WeakSet,Be=new WeakSet;function qe(){for(let u of Array.from(t.querySelectorAll(`form[${A}form]`)))Ke.has(u)||(Ke.add(u),u.addEventListener("submit",m=>{m.preventDefault(),Ct(u)}))}async function Ct(u){if(u.getAttribute(`${A}form-state`)==="sending")return;let m=(u.getAttribute(`${A}form`)??"").trim(),R=u.querySelector(`[${A}form-sent]`),h=u.querySelector(`[${A}form-failed]`),w=Array.from(u.querySelectorAll("button, input[type=submit]")).filter(F=>!F.disabled);u.setAttribute(`${A}form-state`,"sending");for(let F of w)F.disabled=!0;let k=u.querySelector(`input[type=checkbox][${A}consent]`),v=k?.checked?(k.labels?.[0]?.textContent??k.closest("label")?.textContent??"").trim().slice(0,500):"",T=Ie.test(m)?await Re(m,u,{key:e.key,apiUrl:e.apiUrl,fetch:e.fetch,...v?{consent:v}:{}}):{ok:!1,status:0,code:"BAD_FORM",message:"This form isn't set up yet."};for(let F of w)F.disabled=!1;u.setAttribute(`${A}form-state`,T.ok?"sent":"failed"),T.ok&&u.reset(),R&&G(R,T.ok),h&&(!T.ok&&(Be.has(h)||(h.textContent??"").trim()==="")&&(Be.add(h),h.textContent=T.message),G(h,!T.ok)),n.dispatchEvent(new CustomEvent("mapled:form",{detail:T.ok?{form:m,ok:!0}:{form:m,ok:!1,status:T.status,code:T.code}}))}qe();let me=async u=>{try{return await je(u)}catch(m){return console.error(`[mapled] ${m instanceof Error?m.message:"The page couldn't be filled."}`),b??{state:"fallback",release:null,preview:!1,bound:0,issues:[]}}},V=me(),ge=u=>V=V.then(()=>me(u)),He=()=>{Se(),_e();let u=U;return V=V.then(()=>N>=u&&b!==null?b:me())},ze=u=>{u.target?.closest?.(`[${A}end-preview]`)&&(u.preventDefault(),He())};return n.addEventListener("click",ze),{ready:V,get report(){return b},refresh:()=>ge(),endPreview:He,stop:()=>{z=!0,L?.(),L=()=>{},clearTimeout(C),n.removeEventListener("click",ze)},client:r}}var Mt="0.2.3";var Pt={version:Mt,mount:Fe,page:null};window.Mapled=Pt;var H=document.currentScript;if(H instanceof HTMLScriptElement&&H.hasAttribute("data-mapled-key")){let e=H.getAttribute("data-mapled-live"),t=e===null?NaN:parseInt(e,10),n=()=>{try{Pt.page=Fe({key:H.getAttribute("data-mapled-key")??"",...H.getAttribute("data-mapled-api")?{apiUrl:H.getAttribute("data-mapled-api")}:{},...H.getAttribute("data-mapled-locale")?{locale:H.getAttribute("data-mapled-locale")}:{},...e===null?{}:{live:Number.isFinite(t)?t:!0}})}catch(r){console.error(r instanceof Error?r.message:"Mapled couldn't start.")}};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",n,{once:!0}):n()}})();
|
|
7
|
+
`:"";continue}let o=ht(l);if(o){ge(e,o.text,C(e,`h${o.level}`,n),r),s++;continue}if(Ue(l)){C(e,"hr",n),s++;continue}if($e(l)){let p=[];for(;s<t.length&&$e(t[s]);)p.push(t[s].replace(/^ {0,3}> ?/,"")),s++;Ne(e,p,C(e,"blockquote",n),r+1);continue}let d=re(l);if(d){s=Jt(e,t,s,d,n,r);continue}let i=[l.trimStart()];for(s++;s<t.length&&!Pe(t[s])&&!Yt(t[s]);)i.push(t[s].trimStart()),s++;ge(e,i.join(`
|
|
8
|
+
`),C(e,"p",n),r)}}function Jt(e,t,n,r,s,l){let c=C(e,r.ordered?"ol":"ul",s);r.ordered&&r.start!==1&&c.setAttribute("start",String(r.start));let o=[],d=!1;for(;n<t.length;){let i=re(t[n]);if(!i||i.ordered!==r.ordered||i.indent!==r.indent||Ue(t[n]))break;let p=i.indent+i.width,f=[i.rest];n++;let a=!1;for(;n<t.length;){let g=t[n];if(Pe(g)){a=!0,f.push(""),n++;continue}if(se(g)<p)break;a&&f.some(R=>R!=="")&&!re(g.slice(p))&&(d=!0),a=!1,f.push(g.slice(p)),n++}for(;f.length>0&&f[f.length-1]==="";)f.pop();if(a&&n<t.length){let g=re(t[n]);g&&g.ordered===r.ordered&&g.indent===r.indent&&(d=!0)}o.push({lines:f,el:C(e,"li",c)})}for(let i of o)if(Ne(e,i.lines,i.el,l+1),!d){for(let p of Array.from(i.el.childNodes))if(p.nodeName==="P"){for(;p.firstChild;)i.el.insertBefore(p.firstChild,p);i.el.removeChild(p)}}return n}var Ie=e=>e===void 0||e===" "||e===`
|
|
9
|
+
`,yt=e=>e!==void 0&&/[\p{L}\p{N}]/u.test(e);function me(e,t,n,r,s=""){let l=Math.min(e.length,r);for(let c=n;c<l;c++){if(e[c]===t)return c;if(s.includes(e[c]))return-1}return-1}function oe(e,t,n){let r=t;for(;e[r]===n;)r++;return r}function bt(e,t){if(e[t]==="\\")return t+2;if(e[t]==="`"){let n=oe(e,t,"`"),r=wt(e,n,n-t);return r===-1?n:r+(n-t)}return-1}function wt(e,t,n){let r=e.indexOf("`",t);for(;r!==-1;){let s=oe(e,r,"`");if(s-r===n)return r;r=e.indexOf("`",s)}return-1}function Qt(e,t,n){let r=n[0],s=t;for(;s<e.length;){let l=bt(e,s);if(l!==-1){s=l;continue}if(e[s]!==r){s++;continue}let c=oe(e,s,r),o=c-s;if((n.length===1?o===1||o>=3:o>=n.length)&&s>t&&!Ie(e[s-1])&&(r!=="_"||!yt(e[c])))return c-n.length;s=c}return-1}function en(e,t){let n=me(e,";",t+1,t+34,`
|
|
10
|
+
&`);if(n===-1)return null;let r=e.slice(t+1,n);if(r[0]==="#"){let l=r[1]==="x"||r[1]==="X",c=r.slice(l?2:1);if(!(l?/^[0-9a-fA-F]{1,6}$/:/^[0-9]{1,7}$/).test(c))return null;let o=parseInt(c,l?16:10);return{char:o>0&&o<=1114111&&!(o>=55296&&o<=57343)?String.fromCodePoint(o):"\uFFFD",end:n+1}}let s=Object.prototype.hasOwnProperty.call(ft,r)?ft[r]:void 0;return s===void 0?null:{char:s,end:n+1}}var kt=e=>e.replace(/\\([!-/:-@[-`{-~])/g,"$1");function tn(e,t){if(e[t]!=="(")return null;let n=Math.min(e.length,t+mt+1024),r=t+1;for(;r<n&&Ie(e[r])&&e[r]!==void 0;)r++;let s="";if(e[r]==="<"){let c=me(e,">",r+1,n,`
|
|
11
|
+
<`);if(c===-1)return null;s=e.slice(r+1,c),r=c+1}else{let c=r,o=0;for(;r<n;){let d=e[r];if(d==="\\"&&r+1<n){r+=2;continue}if(d===" "||d===`
|
|
12
|
+
`)break;if(d==="("&&o++,d===")"){if(o===0)break;o--}r++}s=e.slice(c,r)}for(;r<n&&(e[r]===" "||e[r]===`
|
|
13
|
+
`);)r++;let l=e[r];if(l==='"'||l==="'"||l==="("){let c=me(e,l==="("?")":l,r+1,n);if(c===-1)return null;for(r=c+1;r<n&&(e[r]===" "||e[r]===`
|
|
14
|
+
`);)r++}return e[r]!==")"?null:{address:kt(s),end:r+1}}function Le(e,t,n=!1){let r=t+1;for(;r<e.length;){let l=bt(e,r);if(l!==-1){r=l;continue}if(!n&&e[r]==="!"&&e[r+1]==="["){let c=Le(e,r+1,!0);if(!c)return null;r=c.end;continue}if(e[r]==="[")return null;if(e[r]==="]")break;r++}if(r>=e.length)return null;let s=tn(e,r+1);return s?{label:e.slice(t+1,r),address:s.address,end:s.end}:null}var nn=/^(?:[a-z][a-z0-9+.-]{1,31}:[^\s<>]*|[^\s<>@]+@[^\s<>@]+\.[^\s<>@]+)$/i;function ge(e,t,n,r){if(r>=pt){n.appendChild(e.doc.createTextNode(t));return}let s="",l=()=>{s!==""&&n.appendChild(e.doc.createTextNode(s)),s=""},c=new Set,o=0;for(;o<t.length;){let d=t[o];if(d==="\\"){let i=t[o+1];if(i===`
|
|
15
|
+
`)for(l(),C(e,"br",n),o+=2;t[o]===" ";)o++;else i!==void 0&&Zt.includes(i)?(s+=i,o+=2):(s+=d,o++);continue}if(d===`
|
|
16
|
+
`){let i=s.endsWith(" "),p=s.length;for(;p>0&&s[p-1]===" ";)p--;for(s=s.slice(0,p),i?(l(),C(e,"br",n)):s+=`
|
|
17
|
+
`,o++;t[o]===" ";)o++;continue}if(d==="`"){let i=oe(t,o,"`"),p=wt(t,i,i-o);if(p===-1){s+=t.slice(o,i),o=i;continue}let f=t.slice(i,p).replace(/\n/g," ");f.length>1&&f.startsWith(" ")&&f.endsWith(" ")&&f.trim()!==""&&(f=f.slice(1,-1)),l(),C(e,"code",n).textContent=f,o=p+(i-o);continue}if(d==="!"&&t[o+1]==="["){let i=Le(t,o+1);if(i){let p=kt(i.label),f=H(i.address.trim(),e.base,"media");if(f===null)s+=p;else{l();let a=C(e,"img",n);a.setAttribute("src",f),a.setAttribute("alt",p),a.setAttribute("loading","lazy")}o=i.end;continue}}if(d==="["){let i=Le(t,o);if(i){let p=H(i.address.trim(),e.base,"link");l();let f=p===null?n:C(e,"a",n);p!==null&&f.setAttribute("href",p),ge(e,i.label,f,r+1),o=i.end;continue}}if(d==="<"){let i=me(t,">",o+1,o+mt,`
|
|
18
|
+
<`),p=i===-1?null:t.slice(o+1,i);if(p!==null&&nn.test(p)){let f=H(p.includes(":")?p:`mailto:${p}`,e.base,"link");if(f!==null){l();let a=C(e,"a",n);a.setAttribute("href",f),a.textContent=p,o=i+1;continue}}}if(d==="&"){let i=en(t,o);if(i){s+=i.char,o=i.end;continue}}if(d==="*"||d==="_"||d==="~"){let i=oe(t,o,d),p=i-o,f=!Ie(t[i])&&(d!=="_"||!yt(t[o-1])),a=d==="~"?p>=2?[["~~",["del"]]]:[]:[...p>=3?[[d.repeat(3),["em","strong"]]]:[],...p>=2?[[d.repeat(2),["strong"]]]:[],[d,["em"]]],g=!1;if(f)for(let[R,O]of a){if(c.has(R))continue;let P=o+R.length,w=Qt(t,P,R);if(w===-1){c.add(R);continue}if(w===P)continue;l();let U=n;for(let N of O)U=C(e,N,U);ge(e,t.slice(P,w),U,r+1),o=w+R.length,g=!0;break}g||(s+=t.slice(o,i),o=i);continue}s+=d,o++}l()}var rn="http://www.w3.org/1999/xhtml",sn=new Set(["script","style","iframe","frame","frameset","object","embed","applet","base","link","meta","template","noscript"]),ie={href:{on:["a","area"],holds:"link"},src:{on:["img","video","audio","source"],holds:"media"},alt:{on:["img","area"],holds:"text"},title:{on:null,holds:"text"},"aria-label":{on:null,holds:"text"},placeholder:{on:["input","textarea"],holds:"text"},datetime:{on:["time","del","ins"],holds:"text"}},le=e=>e.namespaceURI===rn,he=e=>!sn.has(e.localName.toLowerCase());function Fe(e,t){if(!he(e))return"refused-element";let n=Oe(t);return n===null?"type":(e.textContent=n,null)}function St(e,t,n){return!he(e)||!le(e)?"refused-element":typeof t!="string"?"type":(e.replaceChildren(gt(t,e.ownerDocument,n.base)),null)}function Rt(e,t,n){return typeof e!="string"?null:ct(e)?at(e,{...t.apiUrl?{apiUrl:t.apiUrl}:{},...n?{width:n}:{}}):H(e,t.base,"media")}function At(e,t,n,r){if(!le(e)||e.localName!=="img")return"refused-element";if(typeof t!="string")return"type";let s=Rt(t,n,r);return s===null?"unsafe-url":(e.removeAttribute("srcset"),e.setAttribute("src",s),null)}function vt(e,t){return!he(e)||(e.localName==="img"||t==="markdown")&&!le(e)?"refused-element":null}function De(e,t){let n=t.toLowerCase(),r=Object.prototype.hasOwnProperty.call(ie,n)?ie[n]:void 0;return r?!he(e)||!le(e)?"refused-element":r.on!==null&&!r.on.includes(e.localName)?"unsupported":null:"unsupported"}function Tt(e,t,n,r,s){let l=De(e,t);if(l)return l;let c=t.toLowerCase(),o=ie[c];if(o.holds==="text"){let i=Oe(n);return i===null?"type":(e.setAttribute(c,i),null)}if(typeof n!="string")return"type";let d=o.holds==="media"?Rt(n,r,e.localName==="img"?s:void 0):H(n,r.base,"link");return d===null?"unsafe-url":(e.setAttribute(c,d),null)}function xt(e,t){return!le(e)||e.localName!=="a"&&e.localName!=="area"?"unsupported":(e.setAttribute("href",t),null)}function Z(e,t){t?e.removeAttribute("hidden"):e.setAttribute("hidden","")}var Et=["text","markdown","date","datetime","number"],on=/^\d{4}-\d{2}-\d{2}$/,ln=/^\d{4}-\d{2}-\d{2}(?:[T ]\d{2}:\d{2}(?::\d{2}(?:\.\d{1,9})?)?(?:Z|[+-]\d{2}:?\d{2})?)?$/;function Mt(e,t,n){let r=n&&/^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})*$/.test(n)?n:void 0;try{if(t==="number"){let c=typeof e=="number"?e:typeof e=="string"&&e.trim()!==""?Number(e):NaN;return Number.isFinite(c)?new Intl.NumberFormat(r).format(c):null}if(typeof e!="string"||!ln.test(e))return null;let s=new Date(e);if(Number.isNaN(s.getTime()))return null;let l=on.test(e);return new Intl.DateTimeFormat(r,{dateStyle:"long",...t==="datetime"&&!l?{timeStyle:"short"}:{},...l?{timeZone:"UTC"}:{}}).format(s)}catch{return null}}var S="data-mapled-",an=new Set(["bind","format","width","show","hide","link","list","each","record","slug","slug-param","sort","limit","filter","expand","empty","form","form-state","form-sent","form-failed","consent","end-preview","state","preview","key","api","live","locale","type"]),ce=/^[a-z0-9][a-z0-9_-]{0,63}$/,un=/^mk_[A-Za-z0-9_]{8,128}$/,cn=4,dn=100,Ot=2,Y=e=>e!==null&&typeof e=="object"&&!Array.isArray(e);function ue(e,t=0){if(!Y(e))return!1;let{data:n,expanded:r,expandedBlocks:s}=e;if(!Y(n))return!1;let l=o=>Y(o)&&(t>=Ot?Object.keys(o).length===0:Object.values(o).every(d=>d===null||(Array.isArray(d)?d.every(i=>ue(i,t+1)):ue(d,t+1))));if(r!==void 0&&!l(r))return!1;if(s===void 0)return!0;if(!Y(s))return!1;let c=Object.entries(s);return t>=Ot?c.length===0:c.every(([o,d])=>{let i=Object.prototype.hasOwnProperty.call(n,o)?n[o]:void 0;return Array.isArray(d)&&(!Array.isArray(i)||d.length===i.length)&&d.every(l)})}var fn=e=>Array.isArray(e)&&e.every(t=>ue(t));function j(e){let t=e.getAttribute("id");return`${e.localName}${t&&/^[\w-]{1,40}$/.test(t)?`#${t}`:""}`}var be=e=>(e??"").split(",").map(t=>t.trim()).filter(t=>/^[a-z0-9][a-z0-9_.-]{0,127}$/.test(t));function Pt(e){let t={plain:null,typed:new Map,all:[],bad:[]};for(let n of Array.from(e.children)){if(n.localName!=="template")continue;let r=n;t.all.push(r);let s=r.getAttribute(`${S}type`);s===null?t.plain??(t.plain=r):ce.test(s.trim())?t.typed.has(s.trim())||t.typed.set(s.trim(),r):t.bad.push(r)}return t}function pn(e){let t=e.data;if(t===null||typeof t!="object"||Array.isArray(t)||!Object.prototype.hasOwnProperty.call(t,"_type"))return null;let n=t._type;return typeof n=="string"&&ce.test(n)?n:null}function _e(e,t){let n=null,r=()=>n??(n={format:"text",attrs:[],expand:[]});for(let o of Array.from(e.attributes)){if(!o.name.startsWith(S))continue;let d=o.name.slice(S.length);if(d==="bind")r().bind=o.value;else if(d.startsWith("bind-")){let i=d.slice(5);Object.prototype.hasOwnProperty.call(ie,i)?r().attrs.push([i,o.value]):t.push({path:o.value.slice(0,80),reason:"unsupported",on:`${j(e)}[${o.name.slice(0,60)}]`})}else d==="show"?r().show=o.value:d==="hide"?r().hide=o.value:d==="link"?r().link=o.value:an.has(d)||t.push({path:o.value.slice(0,80),reason:"unsupported",on:`${j(e)}[${o.name.slice(0,60)}]`})}if(!n)return null;let s=n,l=e.getAttribute(`${S}format`);l!==null&&(Et.includes(l)?s.format=l:t.push({path:l.slice(0,80),reason:"bad-path",on:`${j(e)}[${S}format]`}));let c=parseInt(e.getAttribute(`${S}width`)??"",10);return c>=16&&c<=4096&&(s.width=c),s.expand=be(e.getAttribute(`${S}expand`)),s}function mn(e,t){let n={},r=e.getAttribute(`${S}sort`);r!==null&&(/^-?[a-z0-9][a-z0-9_-]{0,63}$/.test(r.trim())?n.sort=r.trim():t.push({path:r.slice(0,80),reason:"bad-path",on:`${j(e)}[${S}sort]`}));let s=parseInt(e.getAttribute(`${S}limit`)??"",10);s>=1&&s<=100&&(n.limit=s);let l=e.getAttribute(`${S}filter`);if(l!==null)for(let o of l.split(";")){if(o.trim()==="")continue;let d=o.indexOf("="),i=d===-1?"":o.slice(0,d).trim(),p=d===-1?"":o.slice(d+1).trim();ce.test(i)&&p!==""&&p.length<=200?(n.filter??(n.filter={}))[i]=p:t.push({path:o.trim().slice(0,80),reason:"bad-path",on:`${j(e)}[${S}filter]`})}let c=be(e.getAttribute(`${S}expand`));return c.length>0&&(n.expand=c),n}function ye(e,t){let n={bound:[],eaches:[],lists:[],records:[],issues:[]},r=s=>{if(s.hasAttributes()){let l=s.getAttribute(`${S}list`),c=s.getAttribute(`${S}record`),o=s.getAttribute(`${S}each`);if(l!==null||c!==null){let i=(l??c??"").trim();if(!t)n.issues.push({path:i.slice(0,80),reason:"unsupported",on:j(s)});else if(!ce.test(i))n.issues.push({path:i.slice(0,80),reason:"bad-path",on:j(s)});else if(l!==null)n.lists.push({el:s,collection:i,options:mn(s,n.issues)});else{let p=s.getAttribute(`${S}slug-param`),f=s.getAttribute(`${S}slug`)??(p===null?null:new URLSearchParams(s.ownerDocument.location?.search??"").get(p)),a=ye(s,!0);for(let R of a.records)n.issues.push({path:R.collection,reason:"unsupported",on:j(R.el)});n.lists.push(...a.lists),n.issues.push(...a.issues);let g=_e(s,n.issues);g&&a.bound.unshift({el:s,d:g}),n.records.push({el:s,collection:i,slug:f!==null&&f.trim()!==""&&f.length<=200?f.trim():null,expand:be(s.getAttribute(`${S}expand`)),block:{...a,lists:[],records:[],issues:[]}})}return}if(o!==null){n.eaches.push({el:s,path:o});return}let d=_e(s,n.issues);d&&n.bound.push({el:s,d})}for(let l of Array.from(s.children))r(l)};for(let s of Array.from(e.children))r(s);return n}function Ct(e){let t=new Map,n=(r,s=[])=>{let l=te(r)?.[0];if(!l)return;let c=t.get(l)??new Set;for(let o of s)c.add(o);t.set(l,c)};for(let{d:r}of e.bound){n(r.bind,r.expand);for(let[,s]of r.attrs)n(s,r.expand);n(r.show,r.expand),n(r.hide,r.expand);for(let s of(r.link??"").matchAll(/\{([^{}]*)\}/g))n(s[1],r.expand)}for(let r of e.eaches)n(r.path,be(r.el.getAttribute(`${S}expand`)));return t}function ae(e,t){let n=te(t);if(!n)return{status:"bad-path"};if(e.kind==="record")return{status:"value",...Me(e.source,n)};if(n.length<2)return{status:"bad-path"};let r=e.singles.get(n[0]);return!r||r.status!=="ok"?{status:r?.status==="failed"?"failed":"missing"}:{status:"value",...Me(r.value,n.slice(1))}}function je(e){if(typeof e?.key!="string"||!un.test(e.key))throw new Error("Mapled: data-mapled-key takes the delivery key (mk_\u2026) \u2014 the one key that is safe in a page. A server key or a secret never goes into HTML.");if(typeof document>"u")throw new Error("Mapled: @mapled/vanilla fills a page \u2014 it runs in a browser. On a server read with @mapled/client.");let t=e.root??document.documentElement,n=t.ownerDocument,r=ut({key:e.key,apiUrl:e.apiUrl,appUrl:e.appUrl,fetch:e.fetch,...e.locale?{locale:e.locale}:{}}),s={apiUrl:e.apiUrl,base:n.baseURI},l=new Map,c=u=>{let m=l.get(u);return m||l.set(u,m={attrs:new Map}),m},o=u=>{let m=c(u);m.nodes??(m.nodes=Array.from(u.childNodes))},d=(u,m)=>{let A=c(u);A.attrs.has(m)||A.attrs.set(m,u.getAttribute(m))},i=()=>{for(let[u,m]of l){m.nodes&&u.replaceChildren(...m.nodes);for(let[A,h]of m.attrs)h===null?u.removeAttribute(A):u.setAttribute(A,h)}l.clear()},p=[],f=0,a=(u,m,A)=>p.push({path:(m??"").trim().slice(0,120),reason:A,on:j(u)});function g(u,m,A){if(m.bind!==void 0){let h=ae(A,m.bind),y=vt(u,m.format);if(h.status!=="value")a(u,m.bind,h.status);else if(y)a(u,m.bind,y);else{let k=u.localName==="img";if(k?(d(u,"src"),d(u,"srcset"),d(u,"hidden")):o(u),ne(h.value))k?Z(u,!1):u.textContent="",a(u,m.bind,"empty");else{let v;if(k)v=At(u,h.value,s,m.width);else if(m.format==="markdown")v=St(u,h.value,s);else if(m.format==="text")v=Fe(u,h.value);else{let T=Mt(h.value,m.format,u.closest("[lang]")?.getAttribute("lang")??void 0);v=T===null?"type":Fe(u,T)}v?a(u,m.bind,v):f++}}}for(let[h,y]of m.attrs){let k=ae(A,y);if(k.status!=="value"){a(u,y,k.status);continue}let v=De(u,h);if(v){a(u,y,v);continue}if(d(u,h),ne(k.value)){h==="alt"?u.setAttribute("alt",""):u.removeAttribute(h),a(u,y,"empty");continue}let T=u.getAttribute(h),x=Tt(u,h,k.value,s,m.width);x?(T===null&&u.removeAttribute(h),a(u,y,x)):f++}for(let[h,y]of[[m.show,!0],[m.hide,!1]]){if(h===void 0)continue;let k=ae(A,h);if(k.status!=="value"){a(u,h,k.status);continue}d(u,"hidden");let v=!ne(k.value)&&k.value!==!1;Z(u,v===y),f++}if(m.link!==void 0){let h=[],y=dt(m.link,s.base,k=>{let v=ae(A,k.join("."));if(v.status==="value")return v.value;h.push(v.status)});if(h[0])a(u,m.link,h[0]);else if(!y.ok)a(u,y.path,y.reason);else{d(u,"href");let k=xt(u,y.url);k?a(u,m.link,k):f++}}}function R(u,m,A,h,y){let k=Pt(u);for(let E of k.bad)a(E,E.getAttribute(`${S}type`)??"","bad-path");if(!k.plain&&k.typed.size===0){a(u,"template","unsupported");return}o(u);let v=[...k.all];for(let E of Array.from(u.children))E.hasAttribute(`${S}empty`)&&(d(E,"hidden"),Z(E,m.length===0),v.push(E));let T=[],x=!1;for(let E of m.slice(0,dn)){let ee=y?pn(E):null,G=(ee===null?void 0:k.typed.get(ee))??k.plain;if(!G){x=!0;continue}let X=G.content.cloneNode(!0);O(ye(X,!1),{kind:"record",source:E},A+1),T.push(...Array.from(X.childNodes))}x&&a(u,h,"no-template"),u.replaceChildren(...v,...T),f++}function O(u,m,A){p.push(...u.issues);for(let{el:h,d:y}of u.bound)g(h,y,m);for(let h of u.eaches){let y=ae(m,h.path);if(y.status!=="value"){a(h.el,h.path,y.status);continue}if(A>=cn){a(h.el,h.path,"unsupported");continue}if(y.linked!==void 0){let x=Array.isArray(y.linked)?y.linked:y.linked===null?[]:[y.linked];x.length===0&&a(h.el,h.path,"empty"),R(h.el,x,A,h.path,!1);continue}let k=y.value,v=Array.isArray(k)?k:ne(k)?[]:[k];if(v.some(x=>!Y(x))){a(h.el,h.path,"type");continue}v.length===0&&a(h.el,h.path,"empty");let T=Array.isArray(k)&&Array.isArray(y.blocks)?y.blocks:[];R(h.el,v.map((x,E)=>Y(T[E])?{data:x,expanded:T[E]}:{data:x}),A,h.path,!0)}}async function P(u,m){try{let A=await u();return A===null?{status:"missing"}:m(A)?{status:"ok",value:A}:{status:"failed",answered:!0}}catch(A){return A instanceof V&&A.status===404&&A.code!=="RELEASE_NOT_FOUND"?{status:"missing"}:{status:"failed"}}}let w=null,U="",N=null,L,W=!1,I=0,F=-1,J=3,$=typeof e.live=="number"?e.live:void 0;function Ke(){i(),w={state:"fallback",release:null,preview:!1,bound:0,issues:[]},t.setAttribute(`${S}state`,"fallback"),t.removeAttribute(`${S}preview`)}function Be(){I++,clearTimeout(L),w?.preview&&Ke()}function Ut(){clearTimeout(L);let u=B();if(!u||W)return;let m=Date.parse(u.expiresAt)-Date.now();L=setTimeout(()=>{B()===null&&Be(),ke()},Math.min(Math.max(m,0)+1,2147483647))}async function He(u,m=1){let A=w===null;A&&t.setAttribute(`${S}state`,"loading");let h=I,y=await r.isPreview().catch(()=>!1),k=y?B()?.draftToken??null:null;if(w!==null&&w.preview&&!y&&Ke(),!y&&u!==void 0&&w!==null&&w.state==="ready"&&!w.preview&&w.release!==null&&u<=w.release)return w;let v=b=>{w=b,F=h,t.setAttribute(`${S}state`,b.state),t.toggleAttribute(`${S}preview`,b.preview);let M=b.issues.filter(_=>_.reason!=="empty"),D=M.map(_=>`${_.path} (${_.reason}) on ${_.on}`).join("; ");return D!==U&&(U=D,M.length>0&&console.warn(`[mapled] ${M.length===1?"1 place":`${M.length} places`} couldn't be filled: ${D}`)),n.dispatchEvent(new CustomEvent(A?"mapled:ready":"mapled:update",{detail:b})),b.preview?Ut():(clearTimeout(L),e.live&&N===null&&(N=r.watch(_=>{ke(_.release)},{interval:$,from:b.state==="ready"?b.release:null}))),b},T=null;if(!y)if(u!==void 0)T=u;else try{let b=await r.getRelease();if(b===null)return i(),v({state:"unpublished",release:null,preview:y,bound:0,issues:[]});T=b.release}catch{return w!==null&&w.state==="ready"?w:v({state:"fallback",release:null,preview:y,bound:0,issues:[]})}let x=T===null?{}:{release:T},E=ye(t,!0),ee=_e(t,E.issues);ee&&E.bound.unshift({el:t,d:ee});let G=new Map,X=new Map,de=new Map,fe=[];for(let[b,M]of Ct(E))fe.push(P(()=>r.getSingle(b,{...x,...M.size>0?{expand:[...M]}:{}}),ue).then(D=>G.set(b,D)));for(let b of E.lists){let M=Pt(b.el).plain,D=M?[...Ct(ye(M.content,!1)).keys()]:[];fe.push(P(async()=>(await r.getRecords(b.collection,{...b.options,...x,...D.length>0?{fields:D}:{}})).records,fn).then(_=>X.set(b,_)))}for(let b of E.records){if(b.slug===null){de.set(b,{status:"missing"});continue}let M=b.slug;fe.push(P(()=>r.getRecordBySlug(b.collection,M,{...x,...b.expand.length>0?{expand:b.expand}:{}}),ue).then(D=>de.set(b,D)))}await Promise.all(fe);let Ve=await r.isPreview().catch(()=>!1)?B()?.draftToken??null:null;if(h!==I||Ve!==k)return m<J?He(u,m+1):(i(),v({state:"fallback",release:null,preview:Ve!==null,bound:0,issues:[]}));let Ze=[...G.values(),...X.values(),...de.values()];if(Ze.length>0&&Ze.every(b=>b.status==="failed"&&!b.answered))return w!==null&&w.state==="ready"?w:v({state:"fallback",release:T,preview:y,bound:0,issues:[]});i(),p=[],f=0,O(E,{kind:"page",singles:G},0);for(let b of E.lists){let M=X.get(b);M?.status==="ok"?R(b.el,M.value,0,b.collection,!1):a(b.el,b.collection,M?.status??"failed")}for(let b of E.records){let M=de.get(b);M?.status==="ok"?O(b.block,{kind:"record",source:M.value},0):a(b.el,b.collection,M?.status??"failed")}return We(),v({state:"ready",release:T,preview:y,bound:f,issues:p})}let qe=new WeakSet,ze=new WeakSet;function We(){for(let u of Array.from(t.querySelectorAll(`form[${S}form]`)))qe.has(u)||(qe.add(u),u.addEventListener("submit",m=>{m.preventDefault(),Nt(u)}))}async function Nt(u){if(u.getAttribute(`${S}form-state`)==="sending")return;let m=(u.getAttribute(`${S}form`)??"").trim(),A=u.querySelector(`[${S}form-sent]`),h=u.querySelector(`[${S}form-failed]`),y=Array.from(u.querySelectorAll("button, input[type=submit]")).filter(x=>!x.disabled);u.setAttribute(`${S}form-state`,"sending");for(let x of y)x.disabled=!0;let k=u.querySelector(`input[type=checkbox][${S}consent]`),v=k?.checked?(k.labels?.[0]?.textContent??k.closest("label")?.textContent??"").trim().slice(0,500):"",T=ce.test(m)?await xe(m,u,{key:e.key,apiUrl:e.apiUrl,fetch:e.fetch,...v?{consent:v}:{}}):{ok:!1,status:0,code:"BAD_FORM",message:"This form isn't set up yet."};for(let x of y)x.disabled=!1;u.setAttribute(`${S}form-state`,T.ok?"sent":"failed"),T.ok&&u.reset(),A&&Z(A,T.ok),h&&(!T.ok&&(ze.has(h)||(h.textContent??"").trim()==="")&&(ze.add(h),h.textContent=T.message),Z(h,!T.ok)),n.dispatchEvent(new CustomEvent("mapled:form",{detail:T.ok?{form:m,ok:!0}:{form:m,ok:!1,status:T.status,code:T.code}}))}We();let we=async u=>{try{return await He(u)}catch(m){return console.error(`[mapled] ${m instanceof Error?m.message:"The page couldn't be filled."}`),w??{state:"fallback",release:null,preview:!1,bound:0,issues:[]}}},Q=we(),ke=u=>Q=Q.then(()=>we(u)),Ge=()=>{ve(),Be();let u=I;return Q=Q.then(()=>F>=u&&w!==null?w:we())},Xe=u=>{u.target?.closest?.(`[${S}end-preview]`)&&(u.preventDefault(),Ge())};return n.addEventListener("click",Xe),{ready:Q,get report(){return w},refresh:()=>ke(),endPreview:Ge,stop:()=>{W=!0,N?.(),N=()=>{},clearTimeout(L),n.removeEventListener("click",Xe)},client:r}}var $t="0.3.0";var Lt={version:$t,mount:je,page:null};window.Mapled=Lt;var q=document.currentScript;if(q instanceof HTMLScriptElement&&q.hasAttribute("data-mapled-key")){let e=q.getAttribute("data-mapled-live"),t=e===null?NaN:parseInt(e,10),n=()=>{try{Lt.page=je({key:q.getAttribute("data-mapled-key")??"",...q.getAttribute("data-mapled-api")?{apiUrl:q.getAttribute("data-mapled-api")}:{},...q.getAttribute("data-mapled-locale")?{locale:q.getAttribute("data-mapled-locale")}:{},...e===null?{}:{live:Number.isFinite(t)?t:!0}})}catch(r){console.error(r instanceof Error?r.message:"Mapled couldn't start.")}};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",n,{once:!0}):n()}})();
|
package/dist/mapled.js.sha384
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
sha384-
|
|
1
|
+
sha384-evhHWpu15FOXqD3S7ZXIrtTTDv71hVrOE6l9eHWeeo2NzJh4o9eE3VUkQOnkMj/R
|
package/dist/paths.d.ts
CHANGED
|
@@ -4,16 +4,31 @@
|
|
|
4
4
|
content, read by own properties only (DOM_SECURITY.md, rule 7). */
|
|
5
5
|
/** The segments of a path, or null when it is outside the grammar. */
|
|
6
6
|
export declare function parsePath(text: string | null | undefined): string[] | null;
|
|
7
|
-
/** What paths resolve in: a record as Mapled serves it
|
|
8
|
-
|
|
7
|
+
/** What paths resolve in: a record as Mapled serves it — the records its
|
|
8
|
+
relations link to under `expanded`, those of its blocks' relations
|
|
9
|
+
under `expandedBlocks` — or an item of a repeatable group or of a
|
|
10
|
+
components field (a plain object of the record's data; a block's
|
|
11
|
+
linked records are its `expanded`). */
|
|
9
12
|
export type Source = {
|
|
10
13
|
data: unknown;
|
|
11
14
|
expanded?: unknown;
|
|
15
|
+
expandedBlocks?: unknown;
|
|
12
16
|
};
|
|
13
|
-
/**
|
|
14
|
-
read expanded
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
/** Where a path ends: the value there; `linked` when it ends at a
|
|
18
|
+
relation the read expanded — the linked record, a list of them or
|
|
19
|
+
null; `blocks` when it ends at a components field whose blocks'
|
|
20
|
+
relations the read expanded — one entry per block, in the list's order. */
|
|
21
|
+
export type Walked = {
|
|
22
|
+
value: unknown;
|
|
23
|
+
linked?: unknown;
|
|
24
|
+
blocks?: unknown;
|
|
25
|
+
};
|
|
26
|
+
/** The path walked through a source. A relation the read expanded is
|
|
27
|
+
walked into: `author.name` reads the linked record — one linked
|
|
28
|
+
record only; a list of them has no single value. */
|
|
29
|
+
export declare function walk(source: Source, segments: readonly string[]): Walked;
|
|
30
|
+
/** The value at a path, or undefined when there is none. */
|
|
31
|
+
export declare const valueAt: (source: Source, segments: readonly string[]) => unknown;
|
|
17
32
|
/** Nothing to show: no value, or an empty one. */
|
|
18
33
|
export declare const isEmpty: (value: unknown) => boolean;
|
|
19
34
|
/** A value as the text a person reads, or null when it isn't one —
|
package/dist/paths.js
CHANGED
|
@@ -19,30 +19,37 @@ export function parsePath(text) {
|
|
|
19
19
|
const own = (value, key) => value !== null && typeof value === "object" && !Array.isArray(value) && Object.prototype.hasOwnProperty.call(value, key)
|
|
20
20
|
? value[key]
|
|
21
21
|
: undefined;
|
|
22
|
-
/** The
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
export function
|
|
22
|
+
/** The path walked through a source. A relation the read expanded is
|
|
23
|
+
walked into: `author.name` reads the linked record — one linked
|
|
24
|
+
record only; a list of them has no single value. */
|
|
25
|
+
export function walk(source, segments) {
|
|
26
26
|
let current = source.data;
|
|
27
27
|
let expanded = source.expanded;
|
|
28
|
+
let blocks = source.expandedBlocks;
|
|
28
29
|
for (let i = 0; i < segments.length; i++) {
|
|
29
30
|
const segment = segments[i];
|
|
30
|
-
const linked =
|
|
31
|
+
const linked = own(expanded, segment);
|
|
32
|
+
if (i === segments.length - 1)
|
|
33
|
+
return { value: own(current, segment), linked, blocks: own(blocks, segment) };
|
|
31
34
|
if (linked !== undefined) {
|
|
32
35
|
if (linked === null || Array.isArray(linked))
|
|
33
|
-
return undefined;
|
|
36
|
+
return { value: undefined };
|
|
34
37
|
current = own(linked, "data");
|
|
35
38
|
expanded = own(linked, "expanded");
|
|
39
|
+
blocks = own(linked, "expandedBlocks");
|
|
36
40
|
continue;
|
|
37
41
|
}
|
|
38
42
|
current = own(current, segment);
|
|
39
43
|
// below a field there are no records to walk into
|
|
40
44
|
expanded = undefined;
|
|
45
|
+
blocks = undefined;
|
|
41
46
|
if (current === undefined)
|
|
42
|
-
return undefined;
|
|
47
|
+
return { value: undefined };
|
|
43
48
|
}
|
|
44
|
-
return current;
|
|
49
|
+
return { value: current };
|
|
45
50
|
}
|
|
51
|
+
/** The value at a path, or undefined when there is none. */
|
|
52
|
+
export const valueAt = (source, segments) => walk(source, segments).value;
|
|
46
53
|
/** Nothing to show: no value, or an empty one. */
|
|
47
54
|
export const isEmpty = (value) => value === undefined || value === null || value === "" || (Array.isArray(value) && value.length === 0);
|
|
48
55
|
/** A value as the text a person reads, or null when it isn't one —
|
package/dist/runtime.d.ts
CHANGED
|
@@ -19,7 +19,10 @@
|
|
|
19
19
|
record: data-mapled-sort,
|
|
20
20
|
-limit, -filter, -expand
|
|
21
21
|
data-mapled-each="features" the same for the items of a
|
|
22
|
-
repeatable group
|
|
22
|
+
repeatable group, and for the
|
|
23
|
+
blocks of a components field:
|
|
24
|
+
<template data-mapled-type=
|
|
25
|
+
"hero"> per type of block
|
|
23
26
|
data-mapled-record="posts" one record for what is inside:
|
|
24
27
|
data-mapled-slug, or
|
|
25
28
|
data-mapled-slug-param="slug"
|
|
@@ -32,8 +35,10 @@ import { type MapledClient } from "@mapled/client";
|
|
|
32
35
|
collection in the release; `empty` — the record is there, a value at
|
|
33
36
|
this path isn't; `failed` — the read didn't come back; `bad-path` —
|
|
34
37
|
the page's attribute isn't a path, a key or an option Mapled reads;
|
|
35
|
-
|
|
36
|
-
|
|
38
|
+
`no-template` — a block whose type the page has no template for (and
|
|
39
|
+
no template without a type to take it); the rest are the sinks'
|
|
40
|
+
refusals. */
|
|
41
|
+
export declare const ISSUE_REASONS: readonly ["missing", "empty", "failed", "bad-path", "unsafe-url", "type", "refused-element", "unsupported", "no-template"];
|
|
37
42
|
export type IssueReason = (typeof ISSUE_REASONS)[number];
|
|
38
43
|
/** A place the runtime couldn't fill: the page's own path, why, and the
|
|
39
44
|
element as the page names it. Never a value. */
|
package/dist/runtime.js
CHANGED
|
@@ -19,7 +19,10 @@
|
|
|
19
19
|
record: data-mapled-sort,
|
|
20
20
|
-limit, -filter, -expand
|
|
21
21
|
data-mapled-each="features" the same for the items of a
|
|
22
|
-
repeatable group
|
|
22
|
+
repeatable group, and for the
|
|
23
|
+
blocks of a components field:
|
|
24
|
+
<template data-mapled-type=
|
|
25
|
+
"hero"> per type of block
|
|
23
26
|
data-mapled-record="posts" one record for what is inside:
|
|
24
27
|
data-mapled-slug, or
|
|
25
28
|
data-mapled-slug-param="slug"
|
|
@@ -28,20 +31,22 @@
|
|
|
28
31
|
Paths start at a single from the document (`home.headline`) and at the
|
|
29
32
|
record inside a list, an each or a record (`title`, `author.name`). */
|
|
30
33
|
import { createClient, endPreview, MapledError, previewSession, submitForm } from "@mapled/client";
|
|
31
|
-
import { isEmpty, parsePath,
|
|
32
|
-
import { ATTRIBUTES, FORMATS, formatted, setVisible,
|
|
34
|
+
import { isEmpty, parsePath, walk } from "./paths.js";
|
|
35
|
+
import { ATTRIBUTES, attributeRefusal, bindRefusal, FORMATS, formatted, setVisible, writeAttribute, writeImage, writeLink, writeMarkdown, writeText } from "./sinks.js";
|
|
33
36
|
import { fillTemplate } from "./url.js";
|
|
34
37
|
/** Why a place wasn't filled. `missing` — no such single, record or
|
|
35
38
|
collection in the release; `empty` — the record is there, a value at
|
|
36
39
|
this path isn't; `failed` — the read didn't come back; `bad-path` —
|
|
37
40
|
the page's attribute isn't a path, a key or an option Mapled reads;
|
|
38
|
-
|
|
39
|
-
|
|
41
|
+
`no-template` — a block whose type the page has no template for (and
|
|
42
|
+
no template without a type to take it); the rest are the sinks'
|
|
43
|
+
refusals. */
|
|
44
|
+
export const ISSUE_REASONS = ["missing", "empty", "failed", "bad-path", "unsafe-url", "type", "refused-element", "unsupported", "no-template"];
|
|
40
45
|
const P = "data-mapled-";
|
|
41
46
|
/** Every attribute the runtime reads or writes, besides `bind-…`. */
|
|
42
47
|
export const NAMES = new Set([
|
|
43
48
|
"bind", "format", "width", "show", "hide", "link", "list", "each", "record", "slug", "slug-param", "sort", "limit", "filter", "expand", "empty",
|
|
44
|
-
"form", "form-state", "form-sent", "form-failed", "consent", "end-preview", "state", "preview", "key", "api", "live", "locale",
|
|
49
|
+
"form", "form-state", "form-sent", "form-failed", "consent", "end-preview", "state", "preview", "key", "api", "live", "locale", "type",
|
|
45
50
|
]);
|
|
46
51
|
const KEY = /^[a-z0-9][a-z0-9_-]{0,63}$/;
|
|
47
52
|
const DELIVERY_KEY = /^mk_[A-Za-z0-9_]{8,128}$/;
|
|
@@ -51,24 +56,40 @@ const MAX_EACH_DEPTH = 4;
|
|
|
51
56
|
const MAX_ITEMS = 100;
|
|
52
57
|
/** How deep `expand` reaches: `author`, `related.author`. */
|
|
53
58
|
const MAX_EXPAND_DEPTH = 2;
|
|
59
|
+
const isObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
54
60
|
/** A record as Mapled serves one — what every path walks: `data` an
|
|
55
61
|
object; `expanded`, when there, one linked record, a list of them or
|
|
56
|
-
null under each key
|
|
57
|
-
|
|
62
|
+
null under each key; `expandedBlocks`, when there, a list under each
|
|
63
|
+
key — one object per block of a components field, as many as the
|
|
64
|
+
record's list holds (each block takes the one in its place), holding
|
|
65
|
+
the block's linked records the same way; none deeper than `expand`
|
|
66
|
+
can ask. An answer is data, and so is its shape: what isn't a record
|
|
67
|
+
is read as no answer. */
|
|
58
68
|
function isRecord(value, depth = 0) {
|
|
59
|
-
if (
|
|
69
|
+
if (!isObject(value))
|
|
60
70
|
return false;
|
|
61
|
-
const { data, expanded } = value;
|
|
62
|
-
if (
|
|
71
|
+
const { data, expanded, expandedBlocks } = value;
|
|
72
|
+
if (!isObject(data))
|
|
63
73
|
return false;
|
|
64
|
-
|
|
74
|
+
/** an object of linked records — empty past the depth `expand` reaches */
|
|
75
|
+
const linkedIn = (links) => isObject(links) &&
|
|
76
|
+
(depth >= MAX_EXPAND_DEPTH
|
|
77
|
+
? Object.keys(links).length === 0
|
|
78
|
+
: Object.values(links).every((one) => one === null || (Array.isArray(one) ? one.every((r) => isRecord(r, depth + 1)) : isRecord(one, depth + 1))));
|
|
79
|
+
if (expanded !== undefined && !linkedIn(expanded))
|
|
80
|
+
return false;
|
|
81
|
+
if (expandedBlocks === undefined)
|
|
65
82
|
return true;
|
|
66
|
-
if (
|
|
83
|
+
if (!isObject(expandedBlocks))
|
|
67
84
|
return false;
|
|
68
|
-
const
|
|
85
|
+
const lists = Object.entries(expandedBlocks);
|
|
69
86
|
if (depth >= MAX_EXPAND_DEPTH)
|
|
70
|
-
return
|
|
71
|
-
return
|
|
87
|
+
return lists.length === 0;
|
|
88
|
+
return lists.every(([key, entries]) => {
|
|
89
|
+
// a key the record doesn't hold as a list — a field the read left out — has no blocks to pair with
|
|
90
|
+
const blocks = Object.prototype.hasOwnProperty.call(data, key) ? data[key] : undefined;
|
|
91
|
+
return Array.isArray(entries) && (!Array.isArray(blocks) || entries.length === blocks.length) && entries.every(linkedIn);
|
|
92
|
+
});
|
|
72
93
|
}
|
|
73
94
|
const isRecordList = (value) => Array.isArray(value) && value.every((r) => isRecord(r));
|
|
74
95
|
/** The element as the page names it: `a#cta`. The id is the author's. */
|
|
@@ -80,7 +101,33 @@ const listOf = (text) => (text ?? "")
|
|
|
80
101
|
.split(",")
|
|
81
102
|
.map((t) => t.trim())
|
|
82
103
|
.filter((t) => /^[a-z0-9][a-z0-9_.-]{0,127}$/.test(t));
|
|
83
|
-
|
|
104
|
+
function templatesOf(el) {
|
|
105
|
+
const out = { plain: null, typed: new Map(), all: [], bad: [] };
|
|
106
|
+
for (const child of Array.from(el.children)) {
|
|
107
|
+
if (child.localName !== "template")
|
|
108
|
+
continue;
|
|
109
|
+
const template = child;
|
|
110
|
+
out.all.push(template);
|
|
111
|
+
const type = template.getAttribute(`${P}type`);
|
|
112
|
+
if (type === null)
|
|
113
|
+
out.plain ??= template;
|
|
114
|
+
else if (!KEY.test(type.trim()))
|
|
115
|
+
out.bad.push(template);
|
|
116
|
+
else if (!out.typed.has(type.trim()))
|
|
117
|
+
out.typed.set(type.trim(), template);
|
|
118
|
+
}
|
|
119
|
+
return out;
|
|
120
|
+
}
|
|
121
|
+
/** The type a block names — or a JSON field's object, the same way —
|
|
122
|
+
read as its own property and held to the grammar of Mapled's keys;
|
|
123
|
+
null for a group's item or a record, whose keys never start with `_`. */
|
|
124
|
+
function typeOf(source) {
|
|
125
|
+
const data = source.data;
|
|
126
|
+
if (data === null || typeof data !== "object" || Array.isArray(data) || !Object.prototype.hasOwnProperty.call(data, "_type"))
|
|
127
|
+
return null;
|
|
128
|
+
const type = data._type;
|
|
129
|
+
return typeof type === "string" && KEY.test(type) ? type : null;
|
|
130
|
+
}
|
|
84
131
|
function declaredOn(el, issues) {
|
|
85
132
|
let d = null;
|
|
86
133
|
const get = () => (d ??= { format: "text", attrs: [], expand: [] });
|
|
@@ -241,14 +288,14 @@ function look(scope, path) {
|
|
|
241
288
|
if (!segments)
|
|
242
289
|
return { status: "bad-path" };
|
|
243
290
|
if (scope.kind === "record")
|
|
244
|
-
return { status: "value",
|
|
291
|
+
return { status: "value", ...walk(scope.source, segments) };
|
|
245
292
|
// from the document a path names a single, then a field of it
|
|
246
293
|
if (segments.length < 2)
|
|
247
294
|
return { status: "bad-path" };
|
|
248
295
|
const single = scope.singles.get(segments[0]);
|
|
249
296
|
if (!single || single.status !== "ok")
|
|
250
297
|
return { status: single?.status === "failed" ? "failed" : "missing" };
|
|
251
|
-
return { status: "value",
|
|
298
|
+
return { status: "value", ...walk(single.value, segments.slice(1)) };
|
|
252
299
|
}
|
|
253
300
|
/* ---- mount ---- */
|
|
254
301
|
export function mount(options) {
|
|
@@ -304,10 +351,12 @@ export function mount(options) {
|
|
|
304
351
|
function fillElement(el, d, scope) {
|
|
305
352
|
if (d.bind !== undefined) {
|
|
306
353
|
const found = look(scope, d.bind);
|
|
354
|
+
// a place the sink refuses stays as written, an empty value or not
|
|
355
|
+
const refused = bindRefusal(el, d.format);
|
|
307
356
|
if (found.status !== "value")
|
|
308
357
|
note(el, d.bind, found.status);
|
|
309
|
-
else if (
|
|
310
|
-
note(el, d.bind,
|
|
358
|
+
else if (refused)
|
|
359
|
+
note(el, d.bind, refused);
|
|
311
360
|
else {
|
|
312
361
|
const image = el.localName === "img";
|
|
313
362
|
if (image) {
|
|
@@ -350,6 +399,11 @@ export function mount(options) {
|
|
|
350
399
|
note(el, path, found.status);
|
|
351
400
|
continue;
|
|
352
401
|
}
|
|
402
|
+
const refused = attributeRefusal(el, name);
|
|
403
|
+
if (refused) {
|
|
404
|
+
note(el, path, refused);
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
353
407
|
keepAttr(el, name);
|
|
354
408
|
if (isEmpty(found.value)) {
|
|
355
409
|
// an image without a description is a decorative one; any other attribute goes
|
|
@@ -406,15 +460,19 @@ export function mount(options) {
|
|
|
406
460
|
}
|
|
407
461
|
}
|
|
408
462
|
}
|
|
409
|
-
/** The element's template, once per item
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
463
|
+
/** The element's template, once per item — an each's block takes the
|
|
464
|
+
one of its type; what else it held is its fallback. `path` is the
|
|
465
|
+
page's own, for the report. */
|
|
466
|
+
function repeat(el, sources, depth, path, blocks) {
|
|
467
|
+
const templates = templatesOf(el);
|
|
468
|
+
for (const bad of templates.bad)
|
|
469
|
+
note(bad, bad.getAttribute(`${P}type`) ?? "", "bad-path");
|
|
470
|
+
if (!templates.plain && templates.typed.size === 0) {
|
|
413
471
|
note(el, "template", "unsupported");
|
|
414
472
|
return;
|
|
415
473
|
}
|
|
416
474
|
keepContent(el);
|
|
417
|
-
const kept = [
|
|
475
|
+
const kept = [...templates.all];
|
|
418
476
|
for (const child of Array.from(el.children)) {
|
|
419
477
|
if (!child.hasAttribute(`${P}empty`))
|
|
420
478
|
continue;
|
|
@@ -423,11 +481,23 @@ export function mount(options) {
|
|
|
423
481
|
kept.push(child);
|
|
424
482
|
}
|
|
425
483
|
const items = [];
|
|
484
|
+
let untemplated = false;
|
|
426
485
|
for (const source of sources.slice(0, MAX_ITEMS)) {
|
|
486
|
+
// a list's record has no type: only an each's item picks among the typed templates —
|
|
487
|
+
// a block, or a JSON field's object that names its _type (the page doesn't see the schema)
|
|
488
|
+
const type = blocks ? typeOf(source) : null;
|
|
489
|
+
const template = (type === null ? undefined : templates.typed.get(type)) ?? templates.plain;
|
|
490
|
+
// a block of a type the page doesn't show: content picks among the author's templates, never adds one
|
|
491
|
+
if (!template) {
|
|
492
|
+
untemplated = true;
|
|
493
|
+
continue;
|
|
494
|
+
}
|
|
427
495
|
const clone = template.content.cloneNode(true);
|
|
428
496
|
fill(compile(clone, false), { kind: "record", source }, depth + 1);
|
|
429
497
|
items.push(...Array.from(clone.childNodes));
|
|
430
498
|
}
|
|
499
|
+
if (untemplated)
|
|
500
|
+
note(el, path, "no-template");
|
|
431
501
|
el.replaceChildren(...kept, ...items);
|
|
432
502
|
bound++;
|
|
433
503
|
}
|
|
@@ -445,16 +515,26 @@ export function mount(options) {
|
|
|
445
515
|
note(each.el, each.path, "unsupported");
|
|
446
516
|
continue;
|
|
447
517
|
}
|
|
518
|
+
// a relation the read expanded: its linked records, one for a relation to one — records have no type
|
|
519
|
+
if (found.linked !== undefined) {
|
|
520
|
+
const records = (Array.isArray(found.linked) ? found.linked : found.linked === null ? [] : [found.linked]);
|
|
521
|
+
if (records.length === 0)
|
|
522
|
+
note(each.el, each.path, "empty");
|
|
523
|
+
repeat(each.el, records, depth, each.path, false);
|
|
524
|
+
continue;
|
|
525
|
+
}
|
|
448
526
|
// a repeatable group is a list of items; one that isn't repeatable is a single item
|
|
449
527
|
const value = found.value;
|
|
450
528
|
const items = Array.isArray(value) ? value : isEmpty(value) ? [] : [value];
|
|
451
|
-
if (items.some((item) =>
|
|
529
|
+
if (items.some((item) => !isObject(item))) {
|
|
452
530
|
note(each.el, each.path, "type");
|
|
453
531
|
continue;
|
|
454
532
|
}
|
|
455
533
|
if (items.length === 0)
|
|
456
534
|
note(each.el, each.path, "empty");
|
|
457
|
-
|
|
535
|
+
// the blocks' linked records, when the read expanded their relations: one entry a block, in the list's order
|
|
536
|
+
const links = Array.isArray(value) && Array.isArray(found.blocks) ? found.blocks : [];
|
|
537
|
+
repeat(each.el, items.map((item, i) => (isObject(links[i]) ? { data: item, expanded: links[i] } : { data: item })), depth, each.path, true);
|
|
458
538
|
}
|
|
459
539
|
}
|
|
460
540
|
async function read(load, wellFormed) {
|
|
@@ -589,7 +669,8 @@ export function mount(options) {
|
|
|
589
669
|
reads.push(read(() => client.getSingle(key, { ...pin, ...(expand.size > 0 ? { expand: [...expand] } : {}) }), isRecord).then((r) => singles.set(key, r)));
|
|
590
670
|
}
|
|
591
671
|
for (const list of page.lists) {
|
|
592
|
-
|
|
672
|
+
// a record has no type: it takes the template without one, and the read asks for what that one reads
|
|
673
|
+
const template = templatesOf(list.el).plain;
|
|
593
674
|
const fields = template ? [...heads(compile(template.content, false)).keys()] : [];
|
|
594
675
|
reads.push(read(async () => (await client.getRecords(list.collection, { ...list.options, ...pin, ...(fields.length > 0 ? { fields } : {}) })).records, isRecordList).then((r) => lists.set(list, r)));
|
|
595
676
|
}
|
|
@@ -625,7 +706,7 @@ export function mount(options) {
|
|
|
625
706
|
for (const list of page.lists) {
|
|
626
707
|
const answer = lists.get(list);
|
|
627
708
|
if (answer?.status === "ok")
|
|
628
|
-
repeat(list.el, answer.value, 0);
|
|
709
|
+
repeat(list.el, answer.value, 0, list.collection, false);
|
|
629
710
|
else
|
|
630
711
|
note(list.el, list.collection, answer?.status ?? "failed");
|
|
631
712
|
}
|
package/dist/sinks.d.ts
CHANGED
|
@@ -27,6 +27,15 @@ export declare function writeText(el: Element, value: unknown): Refusal | null;
|
|
|
27
27
|
export declare function writeMarkdown(el: Element, value: unknown, ctx: SinkContext): Refusal | null;
|
|
28
28
|
/** `<img data-mapled-bind>`: the picture. */
|
|
29
29
|
export declare function writeImage(el: Element, value: unknown, ctx: SinkContext, width?: number): Refusal | null;
|
|
30
|
+
/** Why `data-mapled-bind` never writes into this element, whatever the
|
|
31
|
+
value — or null: a refused element (rule 2), and a picture or rich
|
|
32
|
+
text outside HTML (writeImage, writeMarkdown). The runtime asks before
|
|
33
|
+
an empty value empties the place, so a place it refuses stays as the
|
|
34
|
+
author wrote it. */
|
|
35
|
+
export declare function bindRefusal(el: Element, format: Format): Refusal | null;
|
|
36
|
+
/** Why the attribute never goes onto this element, whatever the value —
|
|
37
|
+
or null (rule 3). The runtime asks before an empty value removes it. */
|
|
38
|
+
export declare function attributeRefusal(el: Element, name: string): Refusal | null;
|
|
30
39
|
/** One attribute of the table. `name` is the page's spelling: it is
|
|
31
40
|
looked up, never written as it came. */
|
|
32
41
|
export declare function writeAttribute(el: Element, name: string, value: unknown, ctx: SinkContext, width?: number): Refusal | null;
|
package/dist/sinks.js
CHANGED
|
@@ -70,9 +70,21 @@ export function writeImage(el, value, ctx, width) {
|
|
|
70
70
|
el.setAttribute("src", url);
|
|
71
71
|
return null;
|
|
72
72
|
}
|
|
73
|
-
/**
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
/** Why `data-mapled-bind` never writes into this element, whatever the
|
|
74
|
+
value — or null: a refused element (rule 2), and a picture or rich
|
|
75
|
+
text outside HTML (writeImage, writeMarkdown). The runtime asks before
|
|
76
|
+
an empty value empties the place, so a place it refuses stays as the
|
|
77
|
+
author wrote it. */
|
|
78
|
+
export function bindRefusal(el, format) {
|
|
79
|
+
if (!writable(el))
|
|
80
|
+
return "refused-element";
|
|
81
|
+
if ((el.localName === "img" || format === "markdown") && !isHtml(el))
|
|
82
|
+
return "refused-element";
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
/** Why the attribute never goes onto this element, whatever the value —
|
|
86
|
+
or null (rule 3). The runtime asks before an empty value removes it. */
|
|
87
|
+
export function attributeRefusal(el, name) {
|
|
76
88
|
const key = name.toLowerCase();
|
|
77
89
|
const rule = Object.prototype.hasOwnProperty.call(ATTRIBUTES, key) ? ATTRIBUTES[key] : undefined;
|
|
78
90
|
if (!rule)
|
|
@@ -81,6 +93,16 @@ export function writeAttribute(el, name, value, ctx, width) {
|
|
|
81
93
|
return "refused-element";
|
|
82
94
|
if (rule.on !== null && !rule.on.includes(el.localName))
|
|
83
95
|
return "unsupported";
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
/** One attribute of the table. `name` is the page's spelling: it is
|
|
99
|
+
looked up, never written as it came. */
|
|
100
|
+
export function writeAttribute(el, name, value, ctx, width) {
|
|
101
|
+
const refusal = attributeRefusal(el, name);
|
|
102
|
+
if (refusal)
|
|
103
|
+
return refusal;
|
|
104
|
+
const key = name.toLowerCase();
|
|
105
|
+
const rule = ATTRIBUTES[key];
|
|
84
106
|
if (rule.holds === "text") {
|
|
85
107
|
const text = asText(value);
|
|
86
108
|
if (text === null)
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mapled/vanilla",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "The browser runtime of Mapled, the hosted headless CMS for sites built with AI — published content in a plain HTML site: one script tag and data-mapled attributes, no framework and no build step.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://mapled.io",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"typecheck": "tsc --noEmit -p tsconfig.json"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@mapled/client": "^0.
|
|
44
|
+
"@mapled/client": "^0.5.0"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"esbuild": "^0.28.2",
|