@port60/template-kit 0.18.0 → 0.19.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@port60/template-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "Build Port60 site templates locally: scaffold, live-preview, validate against the platform contract, and package for studio upload. AI-agent ready: every scaffold ships AGENTS.md and validate emits machine-readable JSON.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
|
-
"description": "The behaviour catalogue: engine-attached JavaScript a template opts into with data attributes on its own markup. Templates never ship code; they author complete static markup and declare behaviours in supports.behaviors. The engine adds the p60-js class to the root element when its script runs; any CSS that hides pre-behaviour content MUST be scoped under .p60-js so the no-JS render stays complete. All behaviours honour prefers-reduced-motion centrally. State classes are toggled by the engine; their appearance is entirely the template's CSS.",
|
|
2
|
+
"description": "The behaviour catalogue: engine-attached JavaScript a template opts into with data attributes on its own markup. Templates never ship code; they author complete static markup and declare behaviours in supports.behaviors. The engine adds the p60-js class to the root element when its script runs; any CSS that hides pre-behaviour content MUST be scoped under .p60-js so the no-JS render stays complete. All behaviours honour prefers-reduced-motion centrally. State classes are toggled by the engine; their appearance is entirely the template's CSS. Each entry's primaryAttribute is the opt-in attribute: the validator derives its declaration checks from it and the reference documents it, so the catalogue is the single source.",
|
|
3
3
|
"behaviours": [
|
|
4
4
|
{
|
|
5
5
|
"name": "reveal",
|
|
6
6
|
"status": "available",
|
|
7
7
|
"label": "Reveal on scroll",
|
|
8
|
+
"primaryAttribute": "data-p60-reveal",
|
|
8
9
|
"description": "Adds is-revealed when the element first enters the viewport; the template's CSS does the actual motion. data-p60-reveal-group on a container staggers its children by stamping --p60-reveal-index on each (use it in a transition-delay calc). Reduced motion: is-revealed applies immediately on load.",
|
|
9
10
|
"attributes": [
|
|
10
11
|
{ "attr": "data-p60-reveal", "value": "optional variant name for the CSS to key on" },
|
|
@@ -17,6 +18,7 @@
|
|
|
17
18
|
"name": "counter",
|
|
18
19
|
"status": "available",
|
|
19
20
|
"label": "Animated counter",
|
|
21
|
+
"primaryAttribute": "data-p60-count",
|
|
20
22
|
"description": "Counts the element's figure up from zero when first revealed. The static text IS the final value, typed exactly as it should read (\"£20\", \"95%\", \"1,200\", \"2.5k\"): the engine animates only the first run of digits and keeps the prefix, suffix, grouping and decimal places as typed, restoring the exact text on the last frame. A numeric attribute value (legacy) overrides the target; the text still sets the format. Reduced motion: the final figure shows immediately.",
|
|
21
23
|
"attributes": [
|
|
22
24
|
{ "attr": "data-p60-count", "value": "none: the element's own text is the target and the format; or a numeric target (legacy)" },
|
|
@@ -29,6 +31,7 @@
|
|
|
29
31
|
"name": "progress",
|
|
30
32
|
"status": "available",
|
|
31
33
|
"label": "Progress bar sweep",
|
|
34
|
+
"primaryAttribute": "data-p60-progress",
|
|
32
35
|
"description": "Animates the marked element's width from zero to its authored width when first revealed. The authored width (inline style or CSS) is the target and the no-JS render. Reduced motion: the authored width stands untouched.",
|
|
33
36
|
"attributes": [
|
|
34
37
|
{ "attr": "data-p60-progress", "value": "none; the element's own width is the target" }
|
|
@@ -40,6 +43,7 @@
|
|
|
40
43
|
"name": "countdown",
|
|
41
44
|
"status": "available",
|
|
42
45
|
"label": "Countdown",
|
|
46
|
+
"primaryAttribute": "data-p60-countdown",
|
|
43
47
|
"description": "Replaces the element's content with a live compact remainder to data-p60-until (\"3d 12h 45m\"). The static content is the authored date text (the no-JS render). On expiry the engine adds is-elapsed and swaps to data-p60-elapsed-text when provided. Countdowns keep ticking under reduced motion: they are information, not decoration. v1 unit labels are compact and language-neutral (d/h/m/s).",
|
|
44
48
|
"attributes": [
|
|
45
49
|
{ "attr": "data-p60-countdown", "value": "segments: dhms, dhm (default) or hm" },
|
|
@@ -53,6 +57,7 @@
|
|
|
53
57
|
"name": "accordion",
|
|
54
58
|
"status": "available",
|
|
55
59
|
"label": "Accordion",
|
|
60
|
+
"primaryAttribute": "data-p60-accordion",
|
|
56
61
|
"description": "On a container of native details elements: exclusive-open (opening one closes the others) plus smooth height animation. data-p60-accordion=\"multi\" keeps independent opening. Built on details/summary so the no-JS render is already functional and semantics are free. Reduced motion: no height animation, instant toggle.",
|
|
57
62
|
"attributes": [
|
|
58
63
|
{ "attr": "data-p60-accordion", "value": "none for exclusive-open; \"multi\" for independent" }
|
|
@@ -64,6 +69,7 @@
|
|
|
64
69
|
"name": "stickyHeader",
|
|
65
70
|
"status": "available",
|
|
66
71
|
"label": "Sticky header condense",
|
|
72
|
+
"primaryAttribute": "data-p60-sticky-header",
|
|
67
73
|
"description": "State classes for scroll-aware headers: past a small threshold the engine adds is-condensed; with the \"auto-hide\" value, scrolling down past 160px adds is-hidden and scrolling up removes it. The engine only ever toggles classes (rAF-throttled), what condensing or hiding looks like, and whether it animates, is entirely the template's CSS (gate transitions behind prefers-reduced-motion yourself; the classes always apply because a condensed header is layout, not decoration).",
|
|
68
74
|
"attributes": [
|
|
69
75
|
{ "attr": "data-p60-sticky-header", "value": "none for condense-only; \"auto-hide\" adds the hide-on-scroll-down pair" }
|
|
@@ -75,6 +81,7 @@
|
|
|
75
81
|
"name": "stickyCta",
|
|
76
82
|
"status": "available",
|
|
77
83
|
"label": "Sticky call-to-action bar",
|
|
84
|
+
"primaryAttribute": "data-p60-sticky-cta",
|
|
78
85
|
"description": "Adds is-stuck once the visitor scrolls past the threshold (the attribute value in px, default 480) and removes it above. The bar should DUPLICATE an action that already exists in the page (a donate button, a ticket link); because of that, this is the one behaviour whose element may be hidden unconditionally in CSS rather than .p60-js-scoped: a no-JS visitor loses nothing; the original action is still on the page.",
|
|
79
86
|
"attributes": [
|
|
80
87
|
{ "attr": "data-p60-sticky-cta", "value": "optional scroll threshold in px (default 480)" }
|
|
@@ -86,6 +93,7 @@
|
|
|
86
93
|
"name": "lightbox",
|
|
87
94
|
"status": "available",
|
|
88
95
|
"label": "Lightbox",
|
|
96
|
+
"primaryAttribute": "data-p60-lightbox",
|
|
89
97
|
"description": "On a group container: each data-p60-lightbox-item is an anchor whose href is the full image (the no-JS render simply navigates to it, already functional). The engine intercepts the click and opens an injected overlay (.p60-lightbox) with the image, a caption from data-p60-caption or the thumbnail's alt, a close button, backdrop and Escape close, arrow-key prev/next within the group, and a focus trap. Reduced motion: no transition on the overlay (the engine sets none; style the steady state).",
|
|
90
98
|
"attributes": [
|
|
91
99
|
{ "attr": "data-p60-lightbox", "value": "none; the group container" },
|
|
@@ -100,6 +108,7 @@
|
|
|
100
108
|
"name": "tabs",
|
|
101
109
|
"status": "available",
|
|
102
110
|
"label": "Tabs",
|
|
111
|
+
"primaryAttribute": "data-p60-tabs",
|
|
103
112
|
"description": "data-p60-tab=\"key\" on each tab control and data-p60-panel=\"key\" on each panel, inside a data-p60-tabs container. The engine wires the tablist/tab/tabpanel roles, aria-selected, roving tabindex, arrow/Home/End keys, toggles is-active on the active pair and hides inactive panels with the hidden attribute, so the no-JS render shows every panel stacked and complete, with no CSS scoping needed. The first tab (or the one authored with is-active) starts active.",
|
|
104
113
|
"attributes": [
|
|
105
114
|
{ "attr": "data-p60-tabs", "value": "none; the container" },
|
|
@@ -113,6 +122,7 @@
|
|
|
113
122
|
"name": "carousel",
|
|
114
123
|
"status": "available",
|
|
115
124
|
"label": "Carousel",
|
|
125
|
+
"primaryAttribute": "data-p60-carousel",
|
|
116
126
|
"description": "On a container with one data-p60-slide per child: the engine manages is-active across slides, injects a dots nav (.p60-carousel-dots with one button per slide), wires optional template-provided data-p60-carousel-prev/data-p60-carousel-next controls, adds swipe, keyboard arrows, pause on hover and focus, and hides inactive slides from assistive tech. Value \"auto\" enables auto-advance (data-p60-interval ms, min 3000, default 6000); reduced motion disables auto-advance while controls keep working. The engine stamps --p60-carousel-index on the container for track-translate designs; class-based fade on is-active is the simplest pattern. CSS hiding non-active slides MUST be .p60-js-scoped so the no-JS render shows all slides.",
|
|
117
127
|
"attributes": [
|
|
118
128
|
{ "attr": "data-p60-carousel", "value": "none for manual; \"auto\" for auto-advance" },
|
|
@@ -129,6 +139,7 @@
|
|
|
129
139
|
"name": "nav",
|
|
130
140
|
"status": "available",
|
|
131
141
|
"label": "Navigation",
|
|
142
|
+
"primaryAttribute": "data-p60-nav",
|
|
132
143
|
"description": "Your menu, made robust. You design the menu (markup, every pixel, the breakpoint); the engine supplies the interaction CSS :hover cannot: data-p60-nav on each nav root (several allowed), data-p60-nav-item on each group holding a data-p60-nav-toggle control and a data-p60-nav-menu panel (nesting allowed). Hover-capable pointers get hover intent (a short delay to open, a grace period to close) so crossing the gap between a toggle and its panel never closes it; keyboard, touch and pen toggle on the control (Enter/click, ArrowDown opens and focuses the first link; a mouse click on an already-hovered parent link navigates); Escape closes and returns focus to the toggle; a click outside or focus moving away closes; one item stays open per level and aria-expanded is kept honest. The burger: data-p60-nav-burger on a button whose aria-controls (or the attribute value) names the stacked panel; the engine toggles is-open on both, closes on Escape (returning focus), on a real navigation inside the panel, on an outside click, and when the burger disappears at a wider viewport. State is is-open on the item, the panel and the burger; under .p60-js show a panel only on is-open and keep your :hover/:focus-within rules scoped to html:not(.p60-js) for the no-JS render. Fit steps: when the root's row wraps or overflows, the engine steps is-fit-1, is-fit-2, is-fit-3 onto the root until it fits, re-measured on resize and once fonts settle, never on a stacked (column) nav; the platform's defaults shrink the member_menu pill at each step (the Port60 ID wordmark, then the text, leaving the person icon with its accessible label), and a template maps the same classes to anything else it wants to give up before the burger.",
|
|
133
144
|
"attributes": [
|
|
134
145
|
{ "attr": "data-p60-nav", "value": "none; a nav root, several allowed" },
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// GENERATED by scripts/build-behaviors-runtime.mjs — do not edit by hand.
|
|
2
|
-
"use strict";var p60Behaviors=(()=>{var x=Object.defineProperty;var H=Object.getOwnPropertyDescriptor;var P=Object.getOwnPropertyNames;var q=Object.prototype.hasOwnProperty;var I=(n,l)=>{for(var e in l)x(n,e,{get:l[e],enumerable:!0})},C=(n,l,e,o)=>{if(l&&typeof l=="object"||typeof l=="function")for(let t of P(l))!q.call(n,t)&&t!==e&&x(n,t,{get:()=>l[t],enumerable:!(o=H(l,t))||o.enumerable});return n};var F=n=>C(x({},"__esModule",{value:!0}),n);var Q={};I(Q,{initBehaviors:()=>J});var L=()=>window.matchMedia("(prefers-reduced-motion: reduce)").matches;function b(n,l){let e=`data-p60-bound-${l}`;return n.hasAttribute(e)?!0:(n.setAttribute(e,""),!1)}function T(n,l){if(n.length===0)return;if(L()||!("IntersectionObserver"in window)){n.forEach(l);return}let e=new IntersectionObserver(o=>{for(let t of o)t.isIntersecting&&(e.unobserve(t.target),l(t.target))},{rootMargin:"0px 0px -10% 0px",threshold:.15});n.forEach(o=>e.observe(o))}function O(n){let l=Array.from(n.querySelectorAll("[data-p60-reveal-group]")).filter(o=>!b(o,"reveal-group"));for(let o of l)Array.from(o.children).forEach((t,a)=>{t.style.setProperty("--p60-reveal-index",String(a)),t.classList.add("p60-reveal-child")});T(l,o=>o.classList.add("is-revealed"));let e=Array.from(n.querySelectorAll("[data-p60-reveal]")).filter(o=>!b(o,"reveal"));T(e,o=>o.classList.add("is-revealed"))}var B=/\d[\d, ]*(?:\.\d+)?/;function D(n){let l=Array.from(n.querySelectorAll("[data-p60-count]")).filter(e=>!b(e,"count"));T(l,e=>{e.classList.add("is-revealed");let o=e.textContent??"",t=B.exec(o);if(!t)return;let a=t[0].replace(/[, ]+$/,""),c=o.slice(0,t.index),r=o.slice(t.index+a.length),i=/[, ]/.test(a),d=a.includes(".")?a.length-a.indexOf(".")-1:0,s=Number.parseFloat(a.replace(/[, ]/g,"")),f=Number.parseFloat(e.getAttribute("data-p60-count")??""),g=Number.isFinite(f)?f:s;if(!Number.isFinite(g)||L())return;let u=Math.min(Number.parseInt(e.getAttribute("data-p60-count-duration")??"1200",10)||1200,4e3),p=new Intl.NumberFormat(document.documentElement.lang||void 0,{useGrouping:i,minimumFractionDigits:d,maximumFractionDigits:d}),m=performance.now(),h=v=>{let y=Math.min((v-m)/u,1),k=1-Math.pow(1-y,3);e.textContent=y<1?c+p.format(g*k)+r:o,y<1&&requestAnimationFrame(h)};requestAnimationFrame(h)})}function _(n){let l=Array.from(n.querySelectorAll("[data-p60-progress]")).filter(e=>!b(e,"progress"));if(!L()){for(let e of l){let o=e.style.width||getComputedStyle(e).width;e.style.transition="none",e.style.width="0px",e.offsetWidth,e.style.transition="width 1.1s cubic-bezier(0.22, 1, 0.36, 1)",e.dataset.p60ProgressTarget=o}T(l,e=>{e.classList.add("is-revealed"),e.style.width=e.dataset.p60ProgressTarget??""})}}function R(n){let l=Array.from(n.querySelectorAll("[data-p60-countdown]")).filter(e=>!b(e,"countdown"));for(let e of l){let o=Date.parse(e.getAttribute("data-p60-until")??"");if(!Number.isFinite(o))continue;let t=e.getAttribute("data-p60-countdown")||"dhm",a=t==="dhms",c=()=>{let i=o-Date.now();if(i<=0){e.classList.add("is-elapsed");let m=e.getAttribute("data-p60-elapsed-text");m&&(e.textContent=m),window.clearInterval(r);return}let d=Math.floor(i/1e3),s=Math.floor(d/86400),f=Math.floor(d%86400/3600),g=Math.floor(d%3600/60),u=d%60,p=[];t.includes("d")&&s>0&&p.push(`${s}d`),p.push(`${f}h`,`${g}m`),a&&p.push(`${u}s`),e.textContent=p.join(" ")},r=window.setInterval(c,a?1e3:15e3);c()}}function V(n){let l=Array.from(n.querySelectorAll("[data-p60-accordion]")).filter(e=>!b(e,"accordion"));for(let e of l){let o=e.getAttribute("data-p60-accordion")!=="multi",t=Array.from(e.querySelectorAll("details"));for(let a of t){a.addEventListener("toggle",()=>{if(a.classList.toggle("is-open",a.open),a.open&&o)for(let i of t)i!==a&&i.open&&(i.open=!1)}),a.open&&a.classList.add("is-open");let r=a.querySelector("summary")?.nextElementSibling;r&&!L()&&a.addEventListener("toggle",()=>{if(!a.open)return;let i=r.scrollHeight;r.style.overflow="hidden",r.animate([{height:"0px",opacity:"0"},{height:`${i}px`,opacity:"1"}],{duration:260,easing:"ease-out"}).addEventListener("finish",()=>{r.style.overflow=""})})}}}function $(n){let l=Array.from(n.querySelectorAll("[data-p60-carousel]")).filter(e=>!b(e,"carousel"));for(let e of l){let o=Array.from(e.querySelectorAll("[data-p60-slide]"));if(o.length<2)continue;let t=Math.max(0,o.findIndex(m=>m.classList.contains("is-active"))),a=!1,c=[],r=document.createElement("nav");r.className="p60-carousel-dots",r.setAttribute("aria-label","Slides"),o.forEach((m,h)=>{let v=document.createElement("button");v.type="button",v.className="p60-carousel-dot",v.setAttribute("aria-label",`Go to slide ${h+1}`),v.addEventListener("click",()=>i(h,!0)),c.push(v),r.appendChild(v)}),e.appendChild(r),e.setAttribute("aria-roledescription","carousel"),e.hasAttribute("tabindex")||e.setAttribute("tabindex","0");let i=(m,h=!1)=>{t=(m+o.length)%o.length,o.forEach((v,y)=>{v.classList.toggle("is-active",y===t),y===t?(v.removeAttribute("aria-hidden"),v.removeAttribute("inert")):(v.setAttribute("aria-hidden","true"),v.setAttribute("inert",""))}),c.forEach((v,y)=>{v.classList.toggle("p60-carousel-dot--active",y===t),v.setAttribute("aria-current",y===t?"true":"false")}),e.style.setProperty("--p60-carousel-index",String(t)),h&&p()};e.querySelectorAll("[data-p60-carousel-prev]").forEach(m=>m.addEventListener("click",()=>i(t-1,!0))),e.querySelectorAll("[data-p60-carousel-next]").forEach(m=>m.addEventListener("click",()=>i(t+1,!0))),e.addEventListener("keydown",m=>{m.key==="ArrowLeft"&&i(t-1,!0),m.key==="ArrowRight"&&i(t+1,!0)});let d=0,s=0;e.addEventListener("pointerdown",m=>{d=m.clientX,s=m.clientY}),e.addEventListener("pointerup",m=>{let h=m.clientX-d,v=m.clientY-s;Math.abs(h)>40&&Math.abs(h)>Math.abs(v)&&i(t+(h<0?1:-1),!0)}),e.addEventListener("mouseenter",()=>{a=!0}),e.addEventListener("mouseleave",()=>{a=!1}),e.addEventListener("focusin",()=>{a=!0}),e.addEventListener("focusout",()=>{a=!1});let f,g=e.getAttribute("data-p60-carousel")==="auto"&&!L(),u=Math.max(Number.parseInt(e.getAttribute("data-p60-interval")??"6000",10)||6e3,3e3),p=()=>{f!==void 0&&window.clearInterval(f),g&&(f=window.setInterval(()=>{!a&&!document.hidden&&i(t+1)},u))};i(t),p()}}function Y(n){let l=Array.from(n.querySelectorAll("[data-p60-sticky-header]")).filter(e=>!b(e,"sticky-header"));for(let e of l){let o=e.getAttribute("data-p60-sticky-header")==="auto-hide",t=window.scrollY,a=!1,c=()=>{let r=window.scrollY;e.classList.toggle("is-condensed",r>24),o&&(r>160&&r>t?e.classList.add("is-hidden"):e.classList.remove("is-hidden")),t=r,a=!1};window.addEventListener("scroll",()=>{a||(a=!0,requestAnimationFrame(c))},{passive:!0}),c()}}function K(n){let l=Array.from(n.querySelectorAll("[data-p60-sticky-cta]")).filter(e=>!b(e,"sticky-cta"));for(let e of l){let o=Number.parseInt(e.getAttribute("data-p60-sticky-cta")??"480",10)||480,t=!1,a=()=>{e.classList.toggle("is-stuck",window.scrollY>o),t=!1};window.addEventListener("scroll",()=>{t||(t=!0,requestAnimationFrame(a))},{passive:!0}),a()}}function X(n){let l=Array.from(n.querySelectorAll("[data-p60-lightbox]")).filter(e=>!b(e,"lightbox"));for(let e of l){let o=Array.from(e.querySelectorAll("a[data-p60-lightbox-item]"));if(o.length===0)continue;let t=null,a=0,c=null,r=()=>{t?.remove(),t=null,e.classList.remove("is-open"),document.removeEventListener("keydown",d),c?.focus()},i=f=>{a=(f+o.length)%o.length;let g=o[a];if(!t)return;let u=t.querySelector(".p60-lightbox-img"),p=t.querySelector(".p60-lightbox-caption");u&&(u.src=g.href);let m=g.getAttribute("data-p60-caption")??g.querySelector("img")?.alt??"";p&&(p.textContent=m),u&&(u.alt=m)},d=f=>{if(f.key==="Escape"&&r(),f.key==="ArrowLeft"&&i(a-1),f.key==="ArrowRight"&&i(a+1),f.key==="Tab"&&t){let g=Array.from(t.querySelectorAll("button")),u=g.indexOf(document.activeElement),p=f.shiftKey?u<=0?g.length-1:u-1:u>=g.length-1?0:u+1;g[p]?.focus(),f.preventDefault()}},s=(f,g)=>{c=g,t=document.createElement("div"),t.className="p60-lightbox",t.setAttribute("role","dialog"),t.setAttribute("aria-modal","true"),L()&&(t.style.transition="none"),t.innerHTML='<figure><img class="p60-lightbox-img" alt=""><figcaption class="p60-lightbox-caption"></figcaption></figure>';let u=(m,h,v)=>{let y=document.createElement("button");return y.type="button",y.className=m,y.setAttribute("aria-label",h),y.addEventListener("click",v),t.appendChild(y),y};o.length>1&&(u("p60-lightbox-prev","Previous image",()=>i(a-1)),u("p60-lightbox-next","Next image",()=>i(a+1)));let p=u("p60-lightbox-close","Close",r);t.addEventListener("click",m=>{m.target===t&&r()}),document.body.appendChild(t),e.classList.add("is-open"),document.addEventListener("keydown",d),i(f),p.focus()};o.forEach((f,g)=>f.addEventListener("click",u=>{u.preventDefault(),s(g,f)}))}}function z(n){let l=Array.from(n.querySelectorAll("[data-p60-tabs]")).filter(e=>!b(e,"tabs"));for(let e of l){let o=Array.from(e.querySelectorAll("[data-p60-tab]")),t=Array.from(e.querySelectorAll("[data-p60-panel]"));if(o.length===0||t.length===0)continue;let a=r=>t.find(i=>i.getAttribute("data-p60-panel")===r.getAttribute("data-p60-tab"));o[0].closest("[data-p60-tabs] > *")?.setAttribute("role","tablist");let c=(r,i=!1)=>{for(let d of o){let s=d===r;d.classList.toggle("is-active",s),d.setAttribute("aria-selected",s?"true":"false"),d.setAttribute("tabindex",s?"0":"-1")}for(let d of t){let s=d===a(r);d.classList.toggle("is-active",s),d.hidden=!s}i&&r.focus()};o.forEach((r,i)=>{r.setAttribute("role","tab"),r.addEventListener("click",d=>{d.preventDefault(),c(r)}),r.addEventListener("keydown",d=>{let s=d.key;s==="ArrowLeft"&&c(o[(i-1+o.length)%o.length],!0),s==="ArrowRight"&&c(o[(i+1)%o.length],!0),s==="Home"&&c(o[0],!0),s==="End"&&c(o[o.length-1],!0)})}),t.forEach(r=>r.setAttribute("role","tabpanel")),c(o.find(r=>r.classList.contains("is-active"))??o[0])}}var W=60,U=280,M=()=>window.matchMedia("(hover: hover)").matches,j='a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])',A=new Set,N=!1;function S(n){n.openTimer!==null&&(window.clearTimeout(n.openTimer),n.openTimer=null),n.closeTimer!==null&&(window.clearTimeout(n.closeTimer),n.closeTimer=null)}function E(n,l=!1){S(n);for(let e of Array.from(A))e!==n&&n.item.contains(e.item)&&E(e);n.item.classList.remove("is-open"),n.toggle.setAttribute("aria-expanded","false"),n.openedBy=null,A.delete(n),l&&n.toggle.focus()}function w(n,l){S(n);for(let e of Array.from(A))e!==n&&e.nav===n.nav&&!e.item.contains(n.item)&&E(e);n.item.classList.add("is-open"),n.toggle.setAttribute("aria-expanded","true"),n.openedBy=l,A.add(n)}function G(n){for(let e of Array.from(n.querySelectorAll("[data-p60-nav]"))){let o=Array.from(e.querySelectorAll("[data-p60-nav-item]")).filter(t=>t.closest("[data-p60-nav]")===e&&!b(t,"nav-item"));for(let t of o){let a=s=>s.closest("[data-p60-nav-item]")===t,c=Array.from(t.querySelectorAll("[data-p60-nav-toggle]")).find(a)??null,r=Array.from(t.querySelectorAll("[data-p60-nav-menu]")).find(a)??null;if(!c||!r)continue;let i={nav:e,item:t,toggle:c,menu:r,openedBy:null,lastPointer:"",openTimer:null,closeTimer:null},d=()=>A.has(i);c.setAttribute("aria-expanded","false"),r.id&&!c.hasAttribute("aria-controls")&&c.setAttribute("aria-controls",r.id),t.addEventListener("mouseenter",()=>{M()&&(i.closeTimer!==null&&(window.clearTimeout(i.closeTimer),i.closeTimer=null),!d()&&i.openTimer===null&&(i.openTimer=window.setTimeout(()=>{i.openTimer=null,w(i,"hover")},W)))}),t.addEventListener("mouseleave",()=>{M()&&(i.openTimer!==null&&(window.clearTimeout(i.openTimer),i.openTimer=null),d()&&i.openedBy==="hover"&&i.closeTimer===null&&(i.closeTimer=window.setTimeout(()=>{i.closeTimer=null,E(i)},U)))}),c.addEventListener("pointerdown",s=>{i.lastPointer=s.pointerType??""}),c.addEventListener("click",s=>{let f=s.detail===0||i.lastPointer==="touch"||i.lastPointer==="pen"||!M();if(!d()){s.preventDefault(),s.stopPropagation(),w(i,"toggle");return}!f&&i.openedBy==="hover"||(s.preventDefault(),s.stopPropagation(),E(i))}),c.addEventListener("keydown",s=>{s.key==="ArrowDown"&&(s.preventDefault(),d()||w(i,"toggle"),r.querySelector(j)?.focus())}),t.addEventListener("keydown",s=>{s.key==="Escape"&&d()&&(s.stopPropagation(),E(i,!0))}),t.addEventListener("focusin",s=>{s.target instanceof Node&&r.contains(s.target)&&!d()&&w(i,"focus")}),t.addEventListener("focusout",s=>{let f=s.relatedTarget;f instanceof Node&&!t.contains(f)&&d()&&E(i)})}if(!b(e,"nav-fit")){let a=u=>{for(let p=1;p<=3;p++)e.classList.toggle(`is-fit-${p}`,p<=u)},c=u=>{let p=u.getBoundingClientRect();return p.width>0||p.height>0?[u]:Array.from(u.children).flatMap(c)},r=u=>{let p=-1/0;for(let m of u.flatMap(c)){let h=m.getBoundingClientRect();if(p!==-1/0&&h.top>=p-2)return!0;p=Math.max(p,h.bottom)}return!1},i=()=>{if(e.scrollWidth>e.clientWidth+1||r(Array.from(e.children)))return!1;let u=e.parentElement;return!(u&&getComputedStyle(u).flexWrap==="wrap"&&r(Array.from(u.children)))},d=0,s=()=>{if(d=performance.now(),e.offsetParent===null||!getComputedStyle(e).flexDirection.startsWith("row")){a(0);return}a(0);let u=0;for(;!i()&&u<3;)a(++u);d=performance.now()},f=!1,g=()=>{f||(f=!0,requestAnimationFrame(()=>{f=!1,s()}))};window.addEventListener("resize",g,{passive:!0}),document.fonts?.ready.then(g).catch(()=>{}),document.fonts?.addEventListener("loadingdone",g),"ResizeObserver"in window&&new ResizeObserver(()=>{performance.now()-d>120&&g()}).observe(e),s()}}let l=Array.from(n.querySelectorAll("[data-p60-nav-burger]")).filter(e=>!b(e,"nav-burger"));for(let e of l){let o=e.getAttribute("aria-controls")||e.getAttribute("data-p60-nav-burger")||"",t=o?document.getElementById(o):null;if(!t)continue;let a=()=>t.classList.contains("is-open"),c=r=>{if(t.classList.toggle("is-open",r),e.classList.toggle("is-open",r),e.setAttribute("aria-expanded",String(r)),!r)for(let i of Array.from(A))t.contains(i.item)&&E(i)};e.setAttribute("aria-expanded","false"),e.addEventListener("click",r=>{r.stopPropagation(),c(!a())}),t.addEventListener("click",r=>{r.target instanceof Element&&r.target.closest("a")&&c(!1)}),document.addEventListener("keydown",r=>{r.key==="Escape"&&a()&&(c(!1),e.focus())}),document.addEventListener("click",r=>{a()&&r.target instanceof Node&&!t.contains(r.target)&&!e.contains(r.target)&&c(!1)}),window.addEventListener("resize",()=>{a()&&e.offsetParent===null&&c(!1)})}N||(N=!0,document.addEventListener("click",e=>{for(let o of Array.from(A))(!(e.target instanceof Node)||!o.item.contains(e.target))&&E(o)}))}function J(n=document){document.documentElement.classList.add("p60-js"),O(n),D(n),_(n),R(n),V(n),$(n),Y(n),K(n),X(n),z(n),G(n)}return F(Q);})();
|
|
2
|
+
"use strict";var p60Behaviors=(()=>{var x=Object.defineProperty;var P=Object.getOwnPropertyDescriptor;var q=Object.getOwnPropertyNames;var I=Object.prototype.hasOwnProperty;var C=(o,l)=>{for(var e in l)x(o,e,{get:l[e],enumerable:!0})},F=(o,l,e,n)=>{if(l&&typeof l=="object"||typeof l=="function")for(let t of q(l))!I.call(o,t)&&t!==e&&x(o,t,{get:()=>l[t],enumerable:!(n=P(l,t))||n.enumerable});return o};var O=o=>F(x({},"__esModule",{value:!0}),o);var ee={};C(ee,{initBehaviors:()=>Z});var L=()=>window.matchMedia("(prefers-reduced-motion: reduce)").matches;function b(o,l){let e=`data-p60-bound-${l}`;return o.hasAttribute(e)?!0:(o.setAttribute(e,""),!1)}var N=.15;function T(o,l){if(o.length===0)return;if(L()||!("IntersectionObserver"in window)){o.forEach(l);return}let e=new IntersectionObserver(n=>{for(let t of n){if(!t.isIntersecting)continue;(t.boundingClientRect.height>=window.innerHeight||t.intersectionRatio>=N-.001)&&(e.unobserve(t.target),l(t.target))}},{rootMargin:"0px 0px -10% 0px",threshold:[0,N]});o.forEach(n=>e.observe(n))}function R(o){let l=Array.from(o.querySelectorAll("[data-p60-reveal-group]")).filter(n=>!b(n,"reveal-group"));for(let n of l)Array.from(n.children).forEach((t,a)=>{t.style.setProperty("--p60-reveal-index",String(a)),t.classList.add("p60-reveal-child")});T(l,n=>n.classList.add("is-revealed"));let e=Array.from(o.querySelectorAll("[data-p60-reveal]")).filter(n=>!b(n,"reveal"));T(e,n=>n.classList.add("is-revealed"))}var B=/\d[\d, ]*(?:\.\d+)?/;function _(o){let l=Array.from(o.querySelectorAll("[data-p60-count]")).filter(e=>!b(e,"count"));T(l,e=>{e.classList.add("is-revealed");let n=e.textContent??"",t=B.exec(n);if(!t)return;let a=t[0].replace(/[, ]+$/,""),c=n.slice(0,t.index),r=n.slice(t.index+a.length),i=/[, ]/.test(a),d=a.includes(".")?a.length-a.indexOf(".")-1:0,s=Number.parseFloat(a.replace(/[, ]/g,"")),f=Number.parseFloat(e.getAttribute("data-p60-count")??""),g=Number.isFinite(f)?f:s;if(!Number.isFinite(g)||L())return;let u=Math.min(Number.parseInt(e.getAttribute("data-p60-count-duration")??"1200",10)||1200,4e3),p=new Intl.NumberFormat(document.documentElement.lang||void 0,{useGrouping:i,minimumFractionDigits:d,maximumFractionDigits:d}),m=performance.now(),h=v=>{let y=Math.min((v-m)/u,1),H=1-Math.pow(1-y,3);e.textContent=y<1?c+p.format(g*H)+r:n,y<1&&requestAnimationFrame(h)};requestAnimationFrame(h)})}function D(o){let l=Array.from(o.querySelectorAll("[data-p60-progress]")).filter(e=>!b(e,"progress"));if(!L()){for(let e of l){let n=e.style.width||getComputedStyle(e).width;e.style.transition="none",e.style.width="0px",e.offsetWidth,e.style.transition="width 1.1s cubic-bezier(0.22, 1, 0.36, 1)",e.dataset.p60ProgressTarget=n}T(l,e=>{e.classList.add("is-revealed"),e.style.width=e.dataset.p60ProgressTarget??""})}}function V(o){let l=Array.from(o.querySelectorAll("[data-p60-countdown]")).filter(e=>!b(e,"countdown"));for(let e of l){let n=Date.parse(e.getAttribute("data-p60-until")??"");if(!Number.isFinite(n))continue;let t=e.getAttribute("data-p60-countdown")||"dhm",a=t==="dhms",c=()=>{let i=n-Date.now();if(i<=0){e.classList.add("is-elapsed");let m=e.getAttribute("data-p60-elapsed-text");m&&(e.textContent=m),window.clearInterval(r);return}let d=Math.floor(i/1e3),s=Math.floor(d/86400),f=Math.floor(d%86400/3600),g=Math.floor(d%3600/60),u=d%60,p=[];t.includes("d")&&s>0&&p.push(`${s}d`),p.push(`${f}h`,`${g}m`),a&&p.push(`${u}s`),e.textContent=p.join(" ")},r=window.setInterval(c,a?1e3:15e3);c()}}function $(o){let l=Array.from(o.querySelectorAll("[data-p60-accordion]")).filter(e=>!b(e,"accordion"));for(let e of l){let n=e.getAttribute("data-p60-accordion")!=="multi",t=Array.from(e.querySelectorAll("details"));for(let a of t){a.addEventListener("toggle",()=>{if(a.classList.toggle("is-open",a.open),a.open&&n)for(let i of t)i!==a&&i.open&&(i.open=!1)}),a.open&&a.classList.add("is-open");let r=a.querySelector("summary")?.nextElementSibling;r&&!L()&&a.addEventListener("toggle",()=>{if(!a.open)return;let i=r.scrollHeight;r.style.overflow="hidden",r.animate([{height:"0px",opacity:"0"},{height:`${i}px`,opacity:"1"}],{duration:260,easing:"ease-out"}).addEventListener("finish",()=>{r.style.overflow=""})})}}}function Y(o){let l=Array.from(o.querySelectorAll("[data-p60-carousel]")).filter(e=>!b(e,"carousel"));for(let e of l){let n=Array.from(e.querySelectorAll("[data-p60-slide]"));if(n.length<2)continue;let t=Math.max(0,n.findIndex(m=>m.classList.contains("is-active"))),a=!1,c=[],r=document.createElement("nav");r.className="p60-carousel-dots",r.setAttribute("aria-label","Slides"),n.forEach((m,h)=>{let v=document.createElement("button");v.type="button",v.className="p60-carousel-dot",v.setAttribute("aria-label",`Go to slide ${h+1}`),v.addEventListener("click",()=>i(h,!0)),c.push(v),r.appendChild(v)}),e.appendChild(r),e.setAttribute("aria-roledescription","carousel"),e.hasAttribute("tabindex")||e.setAttribute("tabindex","0");let i=(m,h=!1)=>{t=(m+n.length)%n.length,n.forEach((v,y)=>{v.classList.toggle("is-active",y===t),y===t?(v.removeAttribute("aria-hidden"),v.removeAttribute("inert")):(v.setAttribute("aria-hidden","true"),v.setAttribute("inert",""))}),c.forEach((v,y)=>{v.classList.toggle("p60-carousel-dot--active",y===t),v.setAttribute("aria-current",y===t?"true":"false")}),e.style.setProperty("--p60-carousel-index",String(t)),h&&p()};e.querySelectorAll("[data-p60-carousel-prev]").forEach(m=>m.addEventListener("click",()=>i(t-1,!0))),e.querySelectorAll("[data-p60-carousel-next]").forEach(m=>m.addEventListener("click",()=>i(t+1,!0))),e.addEventListener("keydown",m=>{m.key==="ArrowLeft"&&i(t-1,!0),m.key==="ArrowRight"&&i(t+1,!0)});let d=0,s=0;e.addEventListener("pointerdown",m=>{d=m.clientX,s=m.clientY}),e.addEventListener("pointerup",m=>{let h=m.clientX-d,v=m.clientY-s;Math.abs(h)>40&&Math.abs(h)>Math.abs(v)&&i(t+(h<0?1:-1),!0)}),e.addEventListener("mouseenter",()=>{a=!0}),e.addEventListener("mouseleave",()=>{a=!1}),e.addEventListener("focusin",()=>{a=!0}),e.addEventListener("focusout",()=>{a=!1});let f,g=e.getAttribute("data-p60-carousel")==="auto"&&!L(),u=Math.max(Number.parseInt(e.getAttribute("data-p60-interval")??"6000",10)||6e3,3e3),p=()=>{f!==void 0&&window.clearInterval(f),g&&(f=window.setInterval(()=>{!a&&!document.hidden&&i(t+1)},u))};i(t),p()}}function K(o){let l=Array.from(o.querySelectorAll("[data-p60-sticky-header]")).filter(e=>!b(e,"sticky-header"));for(let e of l){let n=e.getAttribute("data-p60-sticky-header")==="auto-hide",t=window.scrollY,a=!1,c=()=>{let r=window.scrollY;e.classList.toggle("is-condensed",r>24),n&&(r>160&&r>t?e.classList.add("is-hidden"):e.classList.remove("is-hidden")),t=r,a=!1};window.addEventListener("scroll",()=>{a||(a=!0,requestAnimationFrame(c))},{passive:!0}),c()}}function X(o){let l=Array.from(o.querySelectorAll("[data-p60-sticky-cta]")).filter(e=>!b(e,"sticky-cta"));for(let e of l){let n=Number.parseInt(e.getAttribute("data-p60-sticky-cta")??"480",10)||480,t=!1,a=()=>{e.classList.toggle("is-stuck",window.scrollY>n),t=!1};window.addEventListener("scroll",()=>{t||(t=!0,requestAnimationFrame(a))},{passive:!0}),a()}}function z(o){let l=Array.from(o.querySelectorAll("[data-p60-lightbox]")).filter(e=>!b(e,"lightbox"));for(let e of l){let n=Array.from(e.querySelectorAll("a[data-p60-lightbox-item]"));if(n.length===0)continue;let t=null,a=0,c=null,r=()=>{t?.remove(),t=null,e.classList.remove("is-open"),document.removeEventListener("keydown",d),c?.focus()},i=f=>{a=(f+n.length)%n.length;let g=n[a];if(!t)return;let u=t.querySelector(".p60-lightbox-img"),p=t.querySelector(".p60-lightbox-caption");u&&(u.src=g.href);let m=g.getAttribute("data-p60-caption")??g.querySelector("img")?.alt??"";p&&(p.textContent=m),u&&(u.alt=m)},d=f=>{if(f.key==="Escape"&&r(),f.key==="ArrowLeft"&&i(a-1),f.key==="ArrowRight"&&i(a+1),f.key==="Tab"&&t){let g=Array.from(t.querySelectorAll("button")),u=g.indexOf(document.activeElement),p=f.shiftKey?u<=0?g.length-1:u-1:u>=g.length-1?0:u+1;g[p]?.focus(),f.preventDefault()}},s=(f,g)=>{c=g,t=document.createElement("div"),t.className="p60-lightbox",t.setAttribute("role","dialog"),t.setAttribute("aria-modal","true"),L()&&(t.style.transition="none"),t.innerHTML='<figure><img class="p60-lightbox-img" alt=""><figcaption class="p60-lightbox-caption"></figcaption></figure>';let u=(m,h,v)=>{let y=document.createElement("button");return y.type="button",y.className=m,y.setAttribute("aria-label",h),y.addEventListener("click",v),t.appendChild(y),y};n.length>1&&(u("p60-lightbox-prev","Previous image",()=>i(a-1)),u("p60-lightbox-next","Next image",()=>i(a+1)));let p=u("p60-lightbox-close","Close",r);t.addEventListener("click",m=>{m.target===t&&r()}),document.body.appendChild(t),e.classList.add("is-open"),document.addEventListener("keydown",d),i(f),p.focus()};n.forEach((f,g)=>f.addEventListener("click",u=>{u.preventDefault(),s(g,f)}))}}function U(o){let l=Array.from(o.querySelectorAll("[data-p60-tabs]")).filter(e=>!b(e,"tabs"));for(let e of l){let n=Array.from(e.querySelectorAll("[data-p60-tab]")),t=Array.from(e.querySelectorAll("[data-p60-panel]"));if(n.length===0||t.length===0)continue;let a=r=>t.find(i=>i.getAttribute("data-p60-panel")===r.getAttribute("data-p60-tab"));n[0].closest("[data-p60-tabs] > *")?.setAttribute("role","tablist");let c=(r,i=!1)=>{for(let d of n){let s=d===r;d.classList.toggle("is-active",s),d.setAttribute("aria-selected",s?"true":"false"),d.setAttribute("tabindex",s?"0":"-1")}for(let d of t){let s=d===a(r);d.classList.toggle("is-active",s),d.hidden=!s}i&&r.focus()};n.forEach((r,i)=>{r.setAttribute("role","tab"),r.addEventListener("click",d=>{d.preventDefault(),c(r)}),r.addEventListener("keydown",d=>{let s=d.key;s==="ArrowLeft"&&c(n[(i-1+n.length)%n.length],!0),s==="ArrowRight"&&c(n[(i+1)%n.length],!0),s==="Home"&&c(n[0],!0),s==="End"&&c(n[n.length-1],!0)})}),t.forEach(r=>r.setAttribute("role","tabpanel")),c(n.find(r=>r.classList.contains("is-active"))??n[0])}}var W=60,j=280,M=()=>window.matchMedia("(hover: hover)").matches,G='a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])',A=new Set,k=!1;function S(o){o.openTimer!==null&&(window.clearTimeout(o.openTimer),o.openTimer=null),o.closeTimer!==null&&(window.clearTimeout(o.closeTimer),o.closeTimer=null)}function E(o,l=!1){S(o);for(let e of Array.from(A))e!==o&&o.item.contains(e.item)&&E(e);o.item.classList.remove("is-open"),o.toggle.setAttribute("aria-expanded","false"),o.openedBy=null,A.delete(o),l&&o.toggle.focus()}function w(o,l){S(o);for(let e of Array.from(A))e!==o&&e.nav===o.nav&&!e.item.contains(o.item)&&E(e);o.item.classList.add("is-open"),o.toggle.setAttribute("aria-expanded","true"),o.openedBy=l,A.add(o)}function J(o){for(let e of Array.from(o.querySelectorAll("[data-p60-nav]"))){let n=Array.from(e.querySelectorAll("[data-p60-nav-item]")).filter(t=>t.closest("[data-p60-nav]")===e&&!b(t,"nav-item"));for(let t of n){let a=s=>s.closest("[data-p60-nav-item]")===t,c=Array.from(t.querySelectorAll("[data-p60-nav-toggle]")).find(a)??null,r=Array.from(t.querySelectorAll("[data-p60-nav-menu]")).find(a)??null;if(!c||!r)continue;let i={nav:e,item:t,toggle:c,menu:r,openedBy:null,lastPointer:"",openTimer:null,closeTimer:null},d=()=>A.has(i);c.setAttribute("aria-expanded","false"),r.id&&!c.hasAttribute("aria-controls")&&c.setAttribute("aria-controls",r.id),t.addEventListener("mouseenter",()=>{M()&&(i.closeTimer!==null&&(window.clearTimeout(i.closeTimer),i.closeTimer=null),!d()&&i.openTimer===null&&(i.openTimer=window.setTimeout(()=>{i.openTimer=null,w(i,"hover")},W)))}),t.addEventListener("mouseleave",()=>{M()&&(i.openTimer!==null&&(window.clearTimeout(i.openTimer),i.openTimer=null),d()&&i.openedBy==="hover"&&i.closeTimer===null&&(i.closeTimer=window.setTimeout(()=>{i.closeTimer=null,E(i)},j)))}),c.addEventListener("pointerdown",s=>{i.lastPointer=s.pointerType??""}),c.addEventListener("click",s=>{let f=s.detail===0||i.lastPointer==="touch"||i.lastPointer==="pen"||!M();if(!d()){s.preventDefault(),s.stopPropagation(),w(i,"toggle");return}!f&&i.openedBy==="hover"||(s.preventDefault(),s.stopPropagation(),E(i))}),c.addEventListener("keydown",s=>{s.key==="ArrowDown"&&(s.preventDefault(),d()||w(i,"toggle"),r.querySelector(G)?.focus())}),t.addEventListener("keydown",s=>{s.key==="Escape"&&d()&&(s.stopPropagation(),E(i,!0))}),t.addEventListener("focusin",s=>{s.target instanceof Node&&r.contains(s.target)&&!d()&&w(i,"focus")}),t.addEventListener("focusout",s=>{let f=s.relatedTarget;f instanceof Node&&!t.contains(f)&&d()&&E(i)})}if(!b(e,"nav-fit")){let a=u=>{for(let p=1;p<=3;p++)e.classList.toggle(`is-fit-${p}`,p<=u)},c=u=>{let p=u.getBoundingClientRect();return p.width>0||p.height>0?[u]:Array.from(u.children).flatMap(c)},r=u=>{let p=-1/0;for(let m of u.flatMap(c)){let h=m.getBoundingClientRect();if(p!==-1/0&&h.top>=p-2)return!0;p=Math.max(p,h.bottom)}return!1},i=()=>{if(e.scrollWidth>e.clientWidth+1||r(Array.from(e.children)))return!1;let u=e.parentElement;return!(u&&getComputedStyle(u).flexWrap==="wrap"&&r(Array.from(u.children)))},d=0,s=()=>{if(d=performance.now(),e.offsetParent===null||!getComputedStyle(e).flexDirection.startsWith("row")){a(0);return}a(0);let u=0;for(;!i()&&u<3;)a(++u);d=performance.now()},f=!1,g=()=>{f||(f=!0,requestAnimationFrame(()=>{f=!1,s()}))};window.addEventListener("resize",g,{passive:!0}),document.fonts?.ready.then(g).catch(()=>{}),document.fonts?.addEventListener("loadingdone",g),"ResizeObserver"in window&&new ResizeObserver(()=>{performance.now()-d>120&&g()}).observe(e),s()}}let l=Array.from(o.querySelectorAll("[data-p60-nav-burger]")).filter(e=>!b(e,"nav-burger"));for(let e of l){let n=e.getAttribute("aria-controls")||e.getAttribute("data-p60-nav-burger")||"",t=n?document.getElementById(n):null;if(!t)continue;let a=()=>t.classList.contains("is-open"),c=r=>{if(t.classList.toggle("is-open",r),e.classList.toggle("is-open",r),e.setAttribute("aria-expanded",String(r)),!r)for(let i of Array.from(A))t.contains(i.item)&&E(i)};e.setAttribute("aria-expanded","false"),e.addEventListener("click",r=>{r.stopPropagation(),c(!a())}),t.addEventListener("click",r=>{r.target instanceof Element&&r.target.closest("a")&&c(!1)}),document.addEventListener("keydown",r=>{r.key==="Escape"&&a()&&(c(!1),e.focus())}),document.addEventListener("click",r=>{a()&&r.target instanceof Node&&!t.contains(r.target)&&!e.contains(r.target)&&c(!1)}),window.addEventListener("resize",()=>{a()&&e.offsetParent===null&&c(!1)})}k||(k=!0,document.addEventListener("click",e=>{for(let n of Array.from(A))(!(e.target instanceof Node)||!n.item.contains(e.target))&&E(n)}))}var Q=[["reveal",R],["counter",_],["progress",D],["countdown",V],["accordion",$],["carousel",Y],["stickyHeader",K],["stickyCta",X],["lightbox",z],["tabs",U],["nav",J]];function Z(o=document){document.documentElement.classList.add("p60-js");for(let[l,e]of Q)try{e(o)}catch(n){console.warn(`[p60] behaviour "${l}" failed to initialise; the others still run`,n)}}return O(ee);})();
|
|
@@ -28,6 +28,19 @@ const Ajv = Ajv2020.default ?? Ajv2020;
|
|
|
28
28
|
|
|
29
29
|
export { dialect as contractDialect, sectionCatalogue, islandRegistry, contextContract, behaviourCatalogue };
|
|
30
30
|
|
|
31
|
+
// The behaviour to opt-in-attribute map is DERIVED from the catalogue (FR-5): `primaryAttribute` on
|
|
32
|
+
// each entry feeds the checks below, the generated reference and the kit, so adding a behaviour is
|
|
33
|
+
// one catalogue entry plus its runtime initialiser. The trailing word boundary keeps the original
|
|
34
|
+
// semantics: a companion attribute that starts with the primary one (data-p60-reveal-group,
|
|
35
|
+
// data-p60-nav-item) counts as the behaviour in use.
|
|
36
|
+
const escapeRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
37
|
+
export const BEHAVIOUR_PRIMARY_ATTR = Object.fromEntries(
|
|
38
|
+
behaviourCatalogue.behaviours.map((b) => [b.name, b.primaryAttribute])
|
|
39
|
+
);
|
|
40
|
+
const PRIMARY_ATTR = Object.fromEntries(
|
|
41
|
+
behaviourCatalogue.behaviours.map((b) => [b.name, [new RegExp(`${escapeRegExp(b.primaryAttribute)}\\b`), b.primaryAttribute]])
|
|
42
|
+
);
|
|
43
|
+
|
|
31
44
|
// Templates are markup and attributes, NEVER code (docs/template-behaviours.md). These are hard
|
|
32
45
|
// errors over the RAW liquid source — even inside comments, because there is no legitimate reason
|
|
33
46
|
// for the tokens to appear at all. The handler pattern names real DOM event families rather than
|
|
@@ -144,19 +157,6 @@ export async function validateArtifact(files) {
|
|
|
144
157
|
.filter(([path]) => path.endsWith('.liquid'))
|
|
145
158
|
.map(([, source]) => source)
|
|
146
159
|
.join('\n');
|
|
147
|
-
const PRIMARY_ATTR = {
|
|
148
|
-
reveal: [/data-p60-reveal\b/, 'data-p60-reveal'],
|
|
149
|
-
counter: [/data-p60-count\b/, 'data-p60-count'],
|
|
150
|
-
progress: [/data-p60-progress\b/, 'data-p60-progress'],
|
|
151
|
-
countdown: [/data-p60-countdown\b/, 'data-p60-countdown'],
|
|
152
|
-
accordion: [/data-p60-accordion\b/, 'data-p60-accordion'],
|
|
153
|
-
carousel: [/data-p60-carousel\b/, 'data-p60-carousel'],
|
|
154
|
-
stickyHeader: [/data-p60-sticky-header\b/, 'data-p60-sticky-header'],
|
|
155
|
-
stickyCta: [/data-p60-sticky-cta\b/, 'data-p60-sticky-cta'],
|
|
156
|
-
lightbox: [/data-p60-lightbox\b/, 'data-p60-lightbox'],
|
|
157
|
-
tabs: [/data-p60-tabs\b/, 'data-p60-tabs'],
|
|
158
|
-
nav: [/data-p60-nav\b/, 'data-p60-nav'],
|
|
159
|
-
};
|
|
160
160
|
for (const name of declaredBehaviours) {
|
|
161
161
|
const primary = PRIMARY_ATTR[name];
|
|
162
162
|
if (primary && !primary[0].test(liquidSource)) {
|