domma-cms 1.6.14 → 1.6.15
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/admin/js/lib/layouts-arrange.js +1 -1
- package/admin/js/lib/shortcode-registry.js +1 -1
- package/admin/js/templates/layouts.html +6 -0
- package/admin/js/views/layouts.js +54 -12
- package/package.json +1 -1
- package/public/css/site.css +1 -1
- package/public/js/site.js +1 -1
- package/server/routes/api/layouts.js +3 -0
- package/server/services/markdown.js +22 -1
- package/server/services/pageContents.js +122 -0
- package/server/services/renderer.js +14 -2
- package/server/templates/page.html +14 -4
package/public/js/site.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{decodeBase64Json as N}from"/public/js/utf8-base64.mjs";import{makeBadgeEl as O,applyColour as j,applyPill as U,applyFloat as X,applyHighlight as B,applyItemHighlight as G,itemHighlightAttrs as K,isCurrentItem as R,menuAlign as Q,applyClasses as I,cssClasses as P}from"/public/js/menu-decor.mjs?v=20261009-hover-darken";import{initFloatingMenus as Z}from"/public/js/menu-float.js?v=20260829-float-panel";import{enhancePickerFields as V,markInvalidFields as W,showValidationPopup as J}from"/public/js/forms.js?v=20261009-form-classes";import{buildWizardSteps as ee}from"/public/js/form-steps.mjs";import{applyFormClasses as Y,applySubmitButtons as te,applyWizardButtons as oe}from"/public/js/form-submit.mjs";$(()=>{const g=window.__CMS_NAV__||{},f=window.__CMS_SITE__||{};if(f.autoTheme?.enabled){let t=function(e){const a=(e||"07:00").split(":");return+a[0]*60+(+a[1]||0)},n=function(){const e=new Date,a=e.getHours()*60+e.getMinutes();return a>=t(o.dayStart)&&a<t(o.nightStart)?o.dayTheme:o.nightTheme},s=function(e){const a=o.apply&&o.apply[e]||{theme:e,custom:""};Domma.theme.set(a.theme);for(const c of[...document.body.classList])c.startsWith("dm-custom-")&&document.body.classList.remove(c);a.custom&&document.body.classList.add(a.custom)};const o=f.autoTheme;s(n()),setInterval(()=>s(n()),6e4)}const k=$("#site-navbar");if(k.length&&g.brand){let n=function(d,l=0){return Array.isArray(d)?d.filter(r=>!(r&&(r.type==="spacer"||r.type==="theme-switcher"))).filter(r=>!(r&&r.type==="separator"&&l===0)).map(r=>r&&r.type==="separator"?{divider:!0}:Array.isArray(r?.items)?{...r,items:n(r.items,l+1)}:r):d},s=function(d){return Array.isArray(d)?d.filter(l=>!(l&&(l.type==="separator"||l.type==="spacer"||l.type==="theme-switcher"))):[]};const o={...g.brand},t=o.size&&o.size!=="md"?` navbar-brand-${o.size}`:"";if(o.logo||o.icon||o.tagline){let d="";o.logo?d+=`<img src="${o.logo}" class="navbar-brand-logo" alt="${o.text||""}">`:o.icon&&(d+=`<span data-icon="${o.icon}" style="width:1.1em;height:1.1em;margin-right:.35em;vertical-align:middle;"></span>`),o.text&&(d+=`<span class="navbar-brand-text${t}">${o.text}</span>`),o.tagline&&(d+=`<small class="navbar-brand-tagline">${o.tagline}</small>`),o.html=d}else t&&o.text&&(o.html=`<span class="navbar-brand-text${t}">${o.text}</span>`);const e=g.position==="floating",a=g.orientation==="vertical";Domma.elements.navbar("#site-navbar",{brand:o,items:n(g.items||[]),variant:g.variant||"dark",position:e?"static":g.position||"sticky",collapsible:!0,appearOnHover:!!g.appearOnHover}),Domma.icons.scan("#site-navbar");const c=k.get(0);c.classList.add(`dm-nav-align-${Q(g)||"centre"}`),a&&k.get(0).classList.add("dm-nav-vertical",g.side==="right"?"dm-nav-vertical-right":"dm-nav-vertical-left");const v=g.dock||{};a&&!e&&ae(c,g.side==="right"),!a&&v.hideOnScroll&&ne(c),B(c,g),I(c,g.class),e&&(X(k.get(0),g),Z(k.get(0).parentNode||document));const h=c.querySelector(".navbar-toggle"),m=c.querySelector(".navbar-collapse");if(h&&m){let d=null;const l=()=>{m.classList.contains("show")&&h.click()},r=()=>{d&&(d.remove(),d=null),document.body.classList.remove("dm-drawer-open")},p=()=>{d||(d=document.createElement("div"),d.className="dm-navbar-backdrop",d.addEventListener("click",l),document.body.appendChild(d),document.body.classList.add("dm-drawer-open"))};h.addEventListener("click",()=>{requestAnimationFrame(()=>{m.classList.contains("show")?p():r()})}),m.addEventListener("click",i=>{i.target.closest("a")&&requestAnimationFrame(r)}),document.addEventListener("keydown",i=>{i.key==="Escape"&&m.classList.contains("show")&&l()})}const C=c.querySelector(".navbar-nav");if(C){const d=Array.from(C.children);let l=0;for(const r of g.items||[])if(!(r&&r.type==="separator")){if(r&&r.type==="spacer"){const p=document.createElement("li"),i=["xs","sm","md","lg","xl","flex"].includes(r.size)?r.size:"md";p.className=`dm-menu-spacer dm-menu-spacer--${i}`,p.setAttribute("aria-hidden","true"),C.insertBefore(p,d[l]||null);continue}if(r&&r.type==="theme-switcher"){const p=document.createElement("li");p.className="dm-menu-theme-switcher";const i=document.createElement("span");i.dataset.themeSwitcher="menu",i.dataset.position="inline",p.appendChild(i),C.insertBefore(p,d[l]||null);continue}l++}window.CMSThemeSwitcher?.scan(C)}const x=s(g.items||[]);c.querySelectorAll(".navbar-link[data-index], .navbar-dropdown-item[data-index]").forEach(d=>{const l=x[parseInt(d.dataset.index,10)];if(!l)return;const r=d.dataset.subindex!=null,p=r&&Array.isArray(l.items)?l.items[parseInt(d.dataset.subindex,10)]:l;if(!p)return;p.colour&&j(d,p.colour),p.pill&&!r&&U(d,p.pill),G(d,p),I(d.closest("li"),p.class);const i=O(p.badge);i&&d.appendChild(i),R(p.url,location.pathname)&&(d.classList.add("active"),d.setAttribute("aria-current","page"))})}const y=$("#site-footer");if(y.length){let e=function(i){return String(i??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")},a=function(i){return e(i).replace(/'/g,"'")},c=function(i){if(i&&i.type==="separator")return'<li class="footer-sep"><hr></li>';if(i&&i.type==="spacer")return`<li class="dm-menu-spacer dm-menu-spacer--${["xs","sm","md","lg","xl","flex"].includes(i.size)?i.size:"md"}" aria-hidden="true"></li>`;if(i&&i.type==="theme-switcher")return'<li class="dm-menu-theme-switcher"><span data-theme-switcher="menu" data-position="inline"></span></li>';const w=a(i.url||"#"),u=e(i.text||""),q=Array.isArray(i.items)&&i.items.length?`<ul>${i.items.map(c).join("")}</ul>`:"",D=K(i),L=[D.hover&&` data-menu-item-hover="${a(D.hover)}"`,D.active&&` data-menu-item-active="${a(D.active)}"`].filter(Boolean).join(""),_=D.accent?` style="--dm-menu-highlight:${a(D.accent)}"`:"",M=P(i.class).join(" ");return`<li${M?` class="${a(M)}"`:""}><a href="${w}"${L}${_}>${u}</a>${q}</li>`},v=function(i,w){return(i||[]).forEach(u=>{u&&(u.type==="separator"||u.type==="spacer"||u.type==="theme-switcher")||(w.push(u),Array.isArray(u.items)&&u.items.length&&v(u.items,w))}),w};const o=f.social||{},t={twitter:{label:"X / Twitter",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-4.714-6.231-5.401 6.231H2.742l7.73-8.835L1.254 2.25H8.08l4.259 5.629L18.244 2.25zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>'},facebook:{label:"Facebook",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>'},instagram:{label:"Instagram",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zM12 0C8.741 0 8.333.014 7.053.072 2.695.272.273 2.69.073 7.052.014 8.333 0 8.741 0 12c0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98C8.333 23.986 8.741 24 12 24c3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98C15.668.014 15.259 0 12 0zm0 5.838a6.162 6.162 0 100 12.324 6.162 6.162 0 000-12.324zM12 16a4 4 0 110-8 4 4 0 010 8zm6.406-11.845a1.44 1.44 0 100 2.881 1.44 1.44 0 000-2.881z"/></svg>'},linkedin:{label:"LinkedIn",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg>'},github:{label:"GitHub",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>'},youtube:{label:"YouTube",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M23.495 6.205a3.007 3.007 0 00-2.088-2.088c-1.87-.501-9.396-.501-9.396-.501s-7.507-.01-9.396.501A3.007 3.007 0 00.527 6.205a31.247 31.247 0 00-.522 5.805 31.247 31.247 0 00.522 5.783 3.007 3.007 0 002.088 2.088c1.868.502 9.396.502 9.396.502s7.506 0 9.396-.502a3.007 3.007 0 002.088-2.088 31.247 31.247 0 00.5-5.783 31.247 31.247 0 00-.5-5.805zM9.609 15.601V8.408l6.264 3.602z"/></svg>'}};let n='<div class="footer-inner container">';const s=window.__CMS_FOOTER__||{primary:[],legal:[],copyright:""};if(s.copyright&&(n+=`<p>${e(s.copyright)}</p>`),s.primary.length&&(n+=`<nav class="footer-nav footer-nav--primary"><ul>${s.primary.map(c).join("")}</ul></nav>`),s.legal.length&&(n+=`<nav class="footer-nav footer-nav--legal"><ul>${s.legal.map(c).join("")}</ul></nav>`),f.footer){const i=Object.keys(t).filter(u=>o[u]),w=P(f.footer.socialClass).join(" ");i.length&&(n+='<div class="footer-social">',i.forEach(u=>{const{label:q,svg:D}=t[u];n+=`<a href="${o[u]}" target="_blank" rel="noopener noreferrer" aria-label="${q}" class="footer-social-link${w?` ${a(w)}`:""}">${D}</a>`}),n+="</div>")}n+="</div>",y.html(n),window.CMSThemeSwitcher?.scan(y.get(0)),I(y.get(0),f.footer?.class);const h=[...v(s.primary,[]),...v(s.legal,[])];y.get(0).querySelectorAll(".footer-nav a").forEach((i,w)=>{const u=h[w];if(!u)return;u.colour&&j(i,u.colour);const q=O(u.badge);q&&i.appendChild(q),R(u.url,location.pathname)&&(i.classList.add("is-active"),i.setAttribute("aria-current","page"))}),B(y.get(0).querySelector(".footer-nav--primary"),{highlight:s.primaryHighlight}),B(y.get(0).querySelector(".footer-nav--legal"),{highlight:s.legalHighlight}),I(y.get(0).querySelector(".footer-nav--primary"),s.primaryClass),I(y.get(0).querySelector(".footer-nav--legal"),s.legalClass);const C=f.footer?.showMotionToggle!==!1,x=S.get("reduced_motion"),d=f.footer?.motionDefault,l=x!=null?!!x:d==="on"?!0:d==="off"?!1:!!(window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches),r=f.footer?.motionControl==="checkbox",p=y.get(0).querySelector(".footer-inner");if(C&&p){const i=document.createElement("input");i.type="checkbox",i.className=r?"footer-motion-checkbox":"form-switch-input",i.id="dm-motion-switch",i.checked=l,i.addEventListener("change",function(){S.set("reduced_motion",this.checked),window.location.reload()});const w=document.createElement("span");w.className=r?"footer-motion-label":"form-switch-label",w.textContent="Reduce motion";const u=document.createElement("label");u.className=r?"footer-motion-check":"form-switch footer-motion-switch",u.title="Reduce motion",u.appendChild(i),u.appendChild(w),p.appendChild(u)}}if($("#site-sidebar").length){const o=document.querySelector(".site-content"),t=c=>!c.closest(".hero, .card, .accordion, .tabs, figure, #site-sidebar"),n=c=>o?[...o.querySelectorAll(c)].filter(t):[],s=n("h2").length?n("h2"):n("h3"),e=new Set([...document.querySelectorAll("[id]")].map(c=>c.id)),a=[];for(const c of s){if(!c.id){const v=(c.textContent||"").toLowerCase().trim().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")||"section";let h=v;for(let m=2;e.has(h);m++)h=`${v}-${m}`;e.add(h),c.id=h}a.push({text:c.textContent.trim(),url:`#${c.id}`,section:c.id})}a.length?Domma.elements.sidebar("#site-sidebar",{items:a,fixed:!1,push:!1,collapsible:!1,variant:"theme",scrollSpy:!0,position:document.querySelector(".site-main.sidebar-right")?"right":"left"}):document.querySelector(".site-main.with-sidebar")?.classList.add("no-sidebar-items")}Domma.icons.scan(),Domma.syntax?.scan&&Domma.syntax.scan();const b=$(".page-body");if(b.length){b.find(".accordion").each(function(){Domma.elements.accordion(this,{allowMultiple:this.dataset.multi==="true"})}),b.find(".tabs").each(function(){Domma.elements.tabs(this)}),b.find(".carousel").each(function(){const o={autoplay:this.dataset.autoplay==="true",interval:parseInt(this.dataset.interval,10)||5e3,loop:this.dataset.loop!=="false",animation:this.dataset.animation||"slide"};if(this.dataset.animationDuration){const t=parseInt(this.dataset.animationDuration,10);Number.isFinite(t)&&(o.animationDuration=t)}this.dataset.animationEasing&&(o.animationEasing=this.dataset.animationEasing),this.dataset.ctx&&(this._dmCtxSlides=Array.from(this.querySelectorAll(".carousel-slide")).map(t=>t.cloneNode(!0)),this._dmCarouselOpts=o),this._dmCarousel=Domma.elements.carousel(this,o)}),b.find(".dm-countdown").each(function(){const o={autoStart:!0},t=String(this.dataset.format||"").trim(),n=String(this.dataset.to||"").trim(),s=/^(\d{4})-(\d{2})-(\d{2})$/.exec(n),e=s?new Date(+s[1],+s[2]-1,+s[3]).getTime():n?new Date(n).getTime():NaN;Number.isFinite(e)?o.duration=Math.max(0,e-Date.now()):this.dataset.duration&&(o.duration=(parseInt(this.dataset.duration,10)||0)*1e3);const a=/^dd:/i.test(t);o.format=a?"hh:mm:ss":t||(o.duration>=36e5?"hh:mm:ss":"mm:ss");const c=this,v=m=>String(m).padStart(2,"0"),h=m=>{const C=Math.max(0,Math.floor(m/1e3)),x=c.querySelector(".dm-timer-time");x&&(x.textContent=`${v(Math.floor(C/86400))}:${v(Math.floor(C%86400/3600))}:${v(Math.floor(C%3600/60))}:${v(C%60)}`)};a&&(o.onTick=h),Domma.elements.timer(this,o),a&&h(o.duration||0)}),b.find("[data-tooltip]").each(function(){Domma.elements.tooltip(this,{content:$(this).data("tooltip"),position:$(this).data("tooltip-position")||"top"})}),b.find(".dm-progression").each(function(){const o=Array.from(this.querySelectorAll(".dm-progression-item")).map(s=>{const e={title:s.querySelector(".dm-progression-item-title")?.textContent?.trim()||"",description:s.querySelector(".dm-progression-item-body")?.innerHTML?.trim()||""};return s.dataset.date&&(e.date=s.dataset.date),s.dataset.status&&(e.status=s.dataset.status),s.dataset.entryId&&(e._entryId=s.dataset.entryId),e}),t=this.dataset.layout==="centred"?"centered":this.dataset.layout||"vertical",n=Domma.elements.progression(this,{layout:t,theme:this.dataset.theme||"minimal",mode:this.dataset.mode||"timeline",statusIcons:!0,allowHtml:!0,...o.length?{items:o}:{}});o.length&&(this._dmCtx={instance:n,items:o})});try{Domma.effects.reveal(".page-body .hero",{animation:"slide-up",duration:480,threshold:.06,stagger:60,once:!1})}catch{}document.querySelectorAll(".page-body .row[data-reveal]").forEach(o=>{const t=o.dataset.revealMode||"stagger",n=o.dataset.revealAnimation||"slide-up",s=parseInt(o.dataset.revealDuration,10)||400,e=parseInt(o.dataset.revealStagger,10)||60,a=parseInt(o.dataset.revealDelay,10)||0,c=o.dataset.revealDirection==="rtl",v=Array.from(o.children),h={"slide-up":"translateY(30px)","slide-down":"translateY(-30px)","slide-left":"translateX(30px)","slide-right":"translateX(-30px)",zoom:"scale(0.85)",flip:"perspective(600px) rotateX(15deg)"};v.forEach((m,C)=>{m.style.opacity="0",m.style.transform=h[n]||"",m.style.transition=`opacity ${s}ms ease, transform ${s}ms ease`;const x=c?v.length-1-C:C;m.style.transitionDelay=t==="stagger"?`${a+x*e}ms`:`${a}ms`}),requestAnimationFrame(()=>requestAnimationFrame(()=>{const m=new IntersectionObserver(C=>{C.forEach(x=>{x.isIntersecting&&(x.target.offsetWidth,x.target.style.opacity="1",x.target.style.transform="none",m.unobserve(x.target))})},{threshold:.1});v.forEach(C=>m.observe(C))}))}),b.find(".card[data-collapsible]").each(function(){const o=this.querySelector(".card-header");o&&o.addEventListener("click",()=>this.classList.toggle("is-collapsed"))}),b.find(".dm-so-trigger").each(function(){this.addEventListener("click",()=>{const o=this.dataset.soTarget,t=document.getElementById(o);if(!t)return;const n=E.slideover({title:t.dataset.soTitle||"",size:t.dataset.soSize||"md",position:t.dataset.soPosition||"right"});t.style.display="",n.setContent(t),n.open()})})}if(typeof $.setup=="function"){const o=Object.assign({},window.__CMS_DCONFIG__||{});if(document.querySelectorAll(".dm-page-config[data-config]").forEach(t=>{try{const n=N(t.dataset.config);Object.assign(o,n)}catch{}}),Object.keys(o).length>0){const t={};for(const[n,s]of Object.entries(o)){const e=s?.events?.click,{confirm:a,toast:c,alert:v,prompt:h,...m}=e||{};a||c||v||h?($(n).on("click",async function(x){if(x.preventDefault(),a&&!await E.confirm(a))return;let d=null;if(!(h&&(d=await E.prompt(h,{inputPlaceholder:m.promptPlaceholder||"",inputValue:m.promptDefault||""}),d===null))){if(m.target){const l=$(m.target);m.toggleClass&&l.toggleClass(m.toggleClass),m.addClass&&l.addClass(m.addClass),m.removeClass&&l.removeClass(m.removeClass),d!==null&&(m.setText&&l.text(d),m.setVal&&l.val(d),m.setAttr&&l.attr(m.setAttr,d))}m.href&&(window.location.href=m.href),c&&E.toast(c,{type:m.toastType||"success"}),v&&E.alert(v)}}),Object.keys(m).length&&(t[n]={...s,events:{...s.events,click:m}})):t[n]=s}$.setup(t)}}b.length&&z(b.get(0))}),window.wireCTAButtons=z;function z(g){g.querySelectorAll(".dm-cta-trigger").forEach(f=>{f.__ctaWired||(f.__ctaWired=!0,f.addEventListener("click",async()=>{const k=f.dataset.action,y=f.dataset.entry,A=f.dataset.confirm;let b=S.get("auth_token");if(!b){E.toast("Please log in to perform this action.",{type:"warning"});return}if(A&&!await E.confirm(A))return;const o=Array.from(f.childNodes).map(n=>n.cloneNode(!0));f.disabled=!0,f.textContent="Running\u2026";const t=n=>fetch(`/api/actions/${k}/public`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${n}`},body:JSON.stringify(f.dataset.scope?{entryId:y,scope:f.dataset.scope}:{entryId:y})});try{let n=await t(b);if(n.status===401){const e=S.get("auth_refresh_token");if(e){const a=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:e})});if(a.ok){const{token:c}=await a.json();S.set("auth_token",c),b=c,n=await t(b)}}}const s=await n.json().catch(()=>({}));if(!n.ok)throw new Error(s.error||s.message||`Error ${n.status}`);E.toast(s.message||"Action completed.",{type:"success"})}catch(n){E.toast(n.message||"Action failed.",{type:"error"})}finally{f.disabled=!1,f.textContent="",o.forEach(n=>f.appendChild(n)),Domma.icons.scan(f)}}))})}(function(){const f=document.querySelectorAll('.dm-collection-hydrate[data-collection-scope="mine"]');if(!f.length)return;const k=S.get("auth_token");function y(b,o){const t=document.createElement("div");t.className="dm-collection-display dm-collection-empty";const n=document.createElement("p");if(o){n.appendChild(document.createTextNode("Please "));const s=document.createElement("a");s.href="/login",s.textContent="sign in",n.appendChild(s),n.appendChild(document.createTextNode(" to view your entries."))}else n.textContent=b;return t.appendChild(n),t}function A(b,o){const t=document.createElement("template");t.innerHTML=o||"",b.replaceWith(t.content)}f.forEach(async b=>{const o=b.dataset.collectionAttrs;if(!o)return;if(!k){b.replaceChildren(y("",!0));return}const t=n=>fetch("/api/collections/render-scope",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${n}`},body:JSON.stringify({attrs:o})});try{let n=k,s=await t(n);if(s.status===401){const a=S.get("auth_refresh_token");if(a){const c=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:a})});if(c.ok){const{token:v}=await c.json();S.set("auth_token",v),n=v,s=await t(n)}}}if(!s.ok){const{error:a}=await s.json().catch(()=>({}));b.replaceChildren(y(a||"Unable to load entries.",!1));return}const{html:e}=await s.json();A(b,e),window.DommaCollectionBrowser?.hydrate&&window.DommaCollectionBrowser.hydrate(document)}catch{b.replaceChildren(y("Unable to load entries.",!1))}}),setTimeout(()=>{typeof Domma<"u"&&Domma.icons?.scan&&Domma.icons.scan(),typeof Domma<"u"&&Domma.syntax?.scan&&Domma.syntax.scan(),z(document.body)},100)})(),(function(){const f=document.querySelectorAll("[data-collection-table]");!f.length||typeof T>"u"||!T.create||f.forEach(k=>{let y;try{y=N(k.dataset.payload)}catch{return}const{columns:A,rows:b,search:o,sortable:t,exportable:n,pageSize:s,empty:e,ctaConfig:a,slug:c,exportCfg:v}=y;if(!A?.length)return;if(a){const C=A.findIndex(x=>x.key==="_cta");C!==-1&&(A[C]={key:"_cta",title:"",render:(x,d)=>{const l=document.createElement("button");if(l.className=`btn btn-${a.style||"primary"} dm-cta-trigger`,l.dataset.action=a.action||"",a.scope&&(l.dataset.scope=a.scope),l.dataset.entry=d._entryId||"",a.confirm&&(l.dataset.confirm=a.confirm),a.icon){const r=document.createElement("span");r.dataset.icon=a.icon,l.appendChild(r),l.appendChild(document.createTextNode(" "))}return l.appendChild(document.createTextNode(a.label||"Run")),l}})}const h="col-table-"+Math.random().toString(36).slice(2,7),m=document.createElement("div");m.id=h,k.replaceChildren(m),T.create("#"+h,{data:b,columns:A,search:o,sortable:t,exportable:n,pageSize:s,emptyMessage:e}),a&&z(m),k._dmTable={columns:A,rows:b,slug:c,exportCfg:v,render(C){T.create("#"+h,{data:C,columns:A,search:o,sortable:t,exportable:n,pageSize:s,emptyMessage:e}),a&&z(m)}}})})(),(function(){const f=document.querySelectorAll("[data-form-inline]");if(!f.length||typeof F>"u")return;function k(t){const n=t&&t.logic;return!!(n&&(n.visibility||n.requirement))}function y(t,n){const s={};return(t||[]).forEach(e=>{if(e.type==="page-break"||e.type==="spacer"||!e.name)return;const a=e.type==="chooser"||!!(e.variant&&["select","radio","checkbox-group"].includes(e.type)),c=e.type==="checkbox"?"boolean":e.type==="date"||e.type==="icon"||e.type==="image"?"string":a?"chooser":e.type,v={...e.formConfig||{}};v.span==="full"&&n&&(v.span=n);const h={type:c,required:k(e)?!1:e.required,label:e.label,options:e.options,formConfig:{...e.placeholder&&{placeholder:e.placeholder},...e.helper&&{hint:e.helper},...e.tooltip&&{tooltip:e.tooltip},...v}};e.minLength!==void 0&&(h.minLength=e.minLength),e.maxLength!==void 0&&(h.maxLength=e.maxLength),e.min!==void 0&&(h.min=e.min),e.max!==void 0&&(h.max=e.max),a&&(e.variant&&(h.variant=e.variant),(e.multiple||e.type==="checkbox-group")&&(h.multiple=!0),e.density&&(h.density=e.density),e.columns&&(h.columns=e.columns),e.accent&&(h.accent=e.accent),e.accentStyle&&(h.accentStyle=e.accentStyle),e.glow&&(h.glow=!0),e.glowColour&&(h.glowColour=e.glowColour),e.shadow&&(h.shadow=e.shadow),e.shadowColour&&(h.shadowColour=e.shadowColour)),s[e.name]=h}),s}function A(t){const n={};return(t||[]).forEach(s=>{if(!(!s.name||s.type==="page-break"||s.type==="spacer")&&(s.type==="select"||s.type==="multiselect")&&s.required){const e=(s.options||[])[0];e&&(n[s.name]=typeof e=="object"?e.value:e)}}),n}function b(t,n){(n||[]).forEach(s=>{if(s.type!=="date"||!s.name)return;const e=t.querySelector(`[name="${s.name}"]`);e&&e.type!=="date"&&(e.type="date")})}function o(t,n,s){let e=t.querySelector(".cms-form-message");e||(e=document.createElement("p"),e.className="cms-form-message",t.appendChild(e)),e.textContent=n,e.style.cssText=s?"color:var(--dm-danger);margin-top:.75rem;":"color:var(--dm-success);margin-top:.75rem;"}f.forEach(t=>{let n;try{n=N(t.dataset.formInline)}catch{return}const s=n.fields||[],e=n.settings||{},a=Number(e.columns)===3?3:2,c=e.layout||"grid",v=s.some(l=>l.type==="page-break"),h=async l=>{if(t.dataset.triggerBlocked==="true"){const r=t._formLogicRuntime,p=r&&r.submitBlock?r.submitBlock().reasons:[];o(t,p.join(" ")||"This form cannot be submitted yet.",!0);const i=new Error("Blocked by a form trigger");throw i.formSubmitFailed=!0,i}try{const r=t.querySelector(".fb-form-honeypot"),p=r?.querySelector('input[type="text"]')||null,i=r?.querySelector('[name="_t"]')||null,w=Object.assign({},l);delete w._t,r?.dataset.hpName&&delete w[r.dataset.hpName],p!==null&&(w._hp=p.value),i!==null&&(w._t=i.value);const u=await H.post(`/api/forms/submit/${n.slug}`,w);if(u&&u.error){o(t,u.error,!0);const q=new Error(u.error);throw q.formSubmitFailed=!0,q}if(u?.redirect){window.location.href=u.redirect;return}for(;t.firstChild;)t.removeChild(t.firstChild);o(t,u?.message||e.successMessage||"Thank you for your submission.",!1)}catch(r){throw r.formSubmitFailed||o(t,r.message||"Submission failed. Please try again.",!0),r}},m=Date.now();function C(l){const r=l.querySelector("form");if(!r||r.querySelector(".fb-form-honeypot"))return;const p=document.createElement("div");p.className="fb-form-honeypot",p.setAttribute("aria-hidden","true");const i=document.createElement("input");i.name=r.querySelector('[name="website"]')?"_website":"website",p.dataset.hpName=i.name,i.type="text",i.tabIndex=-1,i.autocomplete="url",i.placeholder="https://",p.appendChild(i);const w=document.createElement("input");w.name="_t",w.type="hidden",w.value=m,p.appendChild(w),r.appendChild(p)}function x(l){if(!window.FormLogicEngine||!s.some(p=>p.logic||Array.isArray(p.triggers)&&p.triggers.length))return null;const r=new window.FormLogicEngine.FormLogicRuntime(n,t,l?{baseValues:l}:void 0);if(r.init(),t._formLogicRuntime=r,t._formLogicObserved)return r;if(t._formLogicObserved=!0,t.parentNode&&typeof MutationObserver<"u"){const p=new MutationObserver(function(i){for(const w of i)for(const u of w.removedNodes)if(u===t||u.nodeType===1&&u.contains&&u.contains(t)){t._formLogicRuntime&&t._formLogicRuntime.destroy(),p.disconnect();return}});p.observe(t.parentNode,{childList:!0,subtree:!1}),t._formLogicObserver=p}return r}let d=!1;if(t.addEventListener("dm-form-end",l=>{const r=l.detail?.validation;if(r&&!r.valid){W(t,r.missing),J(r);return}d||(d=!0,h(l.detail?.values||{}).catch(()=>{}).finally(()=>{d=!1}))}),v&&F.wizard){const l=ee(s,n.title),r=l.map(L=>({title:L.title,description:L.description,fields:y(L.fields,a)})),p=l.map(L=>L.fields);let i=0,w=null,u=null;const q=L=>{const _=window.FormLogicEngine;if(!_||!_.validateFields)return;const M=_.validateFields(t,p[i]||[]);M.valid||(L.preventDefault(),L.stopImmediatePropagation(),W(t,M.missing),J(M))},D=()=>{const L=p[i]||s;oe(t,e),Y(t,e,L),b(t,L),V(t,L),e.honeypot!==!1&&C(t),w&&w.destroy(),w=x(u?.getStepData?u.getStepData():void 0),t.querySelectorAll(".wizard-next, .wizard-submit").forEach(_=>_.addEventListener("click",q,!0))};u=F.wizard(t,{schema:{steps:r},layout:c,columns:a,finishText:e.submitText||"Finish",onSubmit:h,onStepChange:L=>{i=L,setTimeout(D,0)}}),t.addEventListener("dm-form-trigger",L=>{if(L.detail?.type!=="goto-step"||!u?.goToStep)return;const _=Number(L.detail.action.step);!Number.isFinite(_)||_<0||_>=r.length||_!==i&&u.goToStep(_)}),Promise.resolve(u).then(D)}else if(F.render){const l=F.render(t,y(s,a),A(s),{submitText:e.submitText||"Submit",layout:c,columns:a,onSubmit:h});Promise.resolve(l).then(function(){te(t,e,c),Y(t,e,s),b(t,s),V(t,s),e.honeypot!==!1&&C(t),x()})}}),$(document).on("click",".dm-banner__dismiss",function(){$(this).closest(".dm-banner").remove()})})();function ae(g,f){const k=document.body,y=k.classList,A=new Set((k.dataset.railSite||"").split(" ").filter(Boolean)),b=()=>window.matchMedia("(min-width: 993px)").matches;g.querySelectorAll(".navbar-link, .navbar-dropdown-toggle").forEach(a=>{const c=a.textContent.trim();c&&(a.setAttribute("title",c),a.querySelector("svg, [data-icon]")||(a.dataset.initial=c.charAt(0).toUpperCase()))});const o=document.createElement("button");o.type="button",o.className="dm-nav-collapse-toggle";const t=()=>{const a=y.contains("dm-vnav-collapsed");o.setAttribute("aria-pressed",String(a)),o.setAttribute("aria-label",a?"Expand menu":"Collapse menu"),o.title=o.getAttribute("aria-label");const c=f?"chevrons-left":"chevrons-right",v=f?"chevrons-right":"chevrons-left";o.innerHTML=`<span data-icon="${a?c:v}"></span>`,window.Domma&&Domma.icons&&Domma.icons.scan(o)},n=a=>{y.toggle("dm-vnav-collapsed",a),S.set("nav_collapsed",a),t()};o.addEventListener("click",()=>n(!y.contains("dm-vnav-collapsed"))),t();const s=g.querySelector(".navbar-container")||g;s.insertBefore(o,s.firstChild?.nextSibling||null);const e=a=>{y.toggle("dm-vnav-autohide",a),y.toggle("dm-vnav-collapsible",!a&&A.has("collapsible")),a?y.remove("dm-vnav-collapsed"):A.has("collapsible")&&S.get("nav_collapsed")===!0&&y.add("dm-vnav-collapsed"),g.classList.remove("dm-nav-revealed"),t()};g.addEventListener("pointerdown",a=>{y.contains("dm-vnav-autohide")&&a.pointerType!=="mouse"&&!g.classList.contains("dm-nav-revealed")&&(g.classList.add("dm-nav-revealed"),a.preventDefault())}),document.addEventListener("pointerdown",a=>{g.contains(a.target)||g.classList.remove("dm-nav-revealed")}),window.E&&typeof E.contextMenu=="function"&&E.contextMenu(g,{items:()=>{const a=y.contains("dm-vnav-autohide"),c=y.contains("dm-vnav-collapsed"),v=S.has("nav_autohide")||S.has("nav_collapsed");return[{header:"Menu"},{label:c?"Expand menu":"Collapse to icons",icon:c?"maximize":"minimize",disabled:!b()||a||!A.has("collapsible"),action:()=>n(!c)},{label:a?"Keep on screen":"Hide when not in use",icon:a?"eye":"eye-off",disabled:!b(),action:()=>{S.set("nav_autohide",!a),e(!a)}},{type:"divider"},{label:"Reset to the site's setting",icon:"refresh",disabled:!v,action:()=>{S.remove("nav_autohide"),S.remove("nav_collapsed"),e(A.has("autohide")),y.toggle("dm-vnav-collapsed",!A.has("autohide")&&A.has("start-collapsed")),t()}}]}})}function ne(g){g.classList.add("dm-nav-scroll-hide");let f=window.scrollY,k=!1;window.addEventListener("scroll",()=>{k||(k=!0,requestAnimationFrame(()=>{k=!1;const y=window.scrollY,A=g.matches(":focus-within")||g.querySelector(".navbar-dropdown.open, .navbar-collapse.show");y>f&&y>g.offsetHeight&&!A?g.classList.add("dm-nav-scrolled-away"):y<f&&g.classList.remove("dm-nav-scrolled-away"),f=y}))},{passive:!0})}
|
|
1
|
+
import{decodeBase64Json as B}from"/public/js/utf8-base64.mjs";import{makeBadgeEl as j,applyColour as P,applyPill as U,applyFloat as G,applyHighlight as O,applyItemHighlight as K,itemHighlightAttrs as Q,isCurrentItem as R,menuAlign as Z,applyClasses as N,cssClasses as W}from"/public/js/menu-decor.mjs?v=20261009-hover-darken";import{initFloatingMenus as ee}from"/public/js/menu-float.js?v=20260829-float-panel";import{enhancePickerFields as V,markInvalidFields as Y,showValidationPopup as J}from"/public/js/forms.js?v=20261009-form-classes";import{buildWizardSteps as te}from"/public/js/form-steps.mjs";import{applyFormClasses as X,applySubmitButtons as ne,applyWizardButtons as oe}from"/public/js/form-submit.mjs";$(()=>{const p=window.__CMS_NAV__||{},d=window.__CMS_SITE__||{};if(d.autoTheme?.enabled){let t=function(e){const a=(e||"07:00").split(":");return+a[0]*60+(+a[1]||0)},s=function(){const e=new Date,a=e.getHours()*60+e.getMinutes();return a>=t(n.dayStart)&&a<t(n.nightStart)?n.dayTheme:n.nightTheme},o=function(e){const a=n.apply&&n.apply[e]||{theme:e,custom:""};Domma.theme.set(a.theme);for(const h of[...document.body.classList])h.startsWith("dm-custom-")&&document.body.classList.remove(h);a.custom&&document.body.classList.add(a.custom)};const n=d.autoTheme;o(s()),setInterval(()=>o(s()),6e4)}const L=$("#site-navbar");if(L.length&&p.brand){let s=function(c,m=0){return Array.isArray(c)?c.filter(l=>!(l&&(l.type==="spacer"||l.type==="theme-switcher"))).filter(l=>!(l&&l.type==="separator"&&m===0)).map(l=>l&&l.type==="separator"?{divider:!0}:Array.isArray(l?.items)?{...l,items:s(l.items,m+1)}:l):c},o=function(c){return Array.isArray(c)?c.filter(m=>!(m&&(m.type==="separator"||m.type==="spacer"||m.type==="theme-switcher"))):[]};const n={...p.brand},t=n.size&&n.size!=="md"?` navbar-brand-${n.size}`:"";if(n.logo||n.icon||n.tagline){let c="";n.logo?c+=`<img src="${n.logo}" class="navbar-brand-logo" alt="${n.text||""}">`:n.icon&&(c+=`<span data-icon="${n.icon}" style="width:1.1em;height:1.1em;margin-right:.35em;vertical-align:middle;"></span>`),n.text&&(c+=`<span class="navbar-brand-text${t}">${n.text}</span>`),n.tagline&&(c+=`<small class="navbar-brand-tagline">${n.tagline}</small>`),n.html=c}else t&&n.text&&(n.html=`<span class="navbar-brand-text${t}">${n.text}</span>`);const e=p.position==="floating",a=p.orientation==="vertical";Domma.elements.navbar("#site-navbar",{brand:n,items:s(p.items||[]),variant:p.variant||"dark",position:e?"static":p.position||"sticky",collapsible:!0,appearOnHover:!!p.appearOnHover}),Domma.icons.scan("#site-navbar");const h=L.get(0);h.classList.add(`dm-nav-align-${Z(p)||"centre"}`),a&&L.get(0).classList.add("dm-nav-vertical",p.side==="right"?"dm-nav-vertical-right":"dm-nav-vertical-left");const w=p.dock||{};a&&!e&&ae(h,p.side==="right"),!a&&w.hideOnScroll&&se(h),O(h,p),N(h,p.class),e&&(G(L.get(0),p),ee(L.get(0).parentNode||document));const i=h.querySelector(".navbar-toggle"),u=h.querySelector(".navbar-collapse");if(i&&u){let c=null;const m=()=>{u.classList.contains("show")&&i.click()},l=()=>{c&&(c.remove(),c=null),document.body.classList.remove("dm-drawer-open")},f=()=>{c||(c=document.createElement("div"),c.className="dm-navbar-backdrop",c.addEventListener("click",m),document.body.appendChild(c),document.body.classList.add("dm-drawer-open"))};i.addEventListener("click",()=>{requestAnimationFrame(()=>{u.classList.contains("show")?f():l()})}),u.addEventListener("click",r=>{r.target.closest("a")&&requestAnimationFrame(l)}),document.addEventListener("keydown",r=>{r.key==="Escape"&&u.classList.contains("show")&&m()})}const y=h.querySelector(".navbar-nav");if(y){const c=Array.from(y.children);let m=0;for(const l of p.items||[])if(!(l&&l.type==="separator")){if(l&&l.type==="spacer"){const f=document.createElement("li"),r=["xs","sm","md","lg","xl","flex"].includes(l.size)?l.size:"md";f.className=`dm-menu-spacer dm-menu-spacer--${r}`,f.setAttribute("aria-hidden","true"),y.insertBefore(f,c[m]||null);continue}if(l&&l.type==="theme-switcher"){const f=document.createElement("li");f.className="dm-menu-theme-switcher";const r=document.createElement("span");r.dataset.themeSwitcher="menu",r.dataset.position="inline",f.appendChild(r),y.insertBefore(f,c[m]||null);continue}m++}window.CMSThemeSwitcher?.scan(y)}const b=o(p.items||[]);h.querySelectorAll(".navbar-link[data-index], .navbar-dropdown-item[data-index]").forEach(c=>{const m=b[parseInt(c.dataset.index,10)];if(!m)return;const l=c.dataset.subindex!=null,f=l&&Array.isArray(m.items)?m.items[parseInt(c.dataset.subindex,10)]:m;if(!f)return;f.colour&&P(c,f.colour),f.pill&&!l&&U(c,f.pill),K(c,f),N(c.closest("li"),f.class);const r=j(f.badge);r&&c.appendChild(r),R(f.url,location.pathname)&&(c.classList.add("active"),c.setAttribute("aria-current","page"))})}const v=$("#site-footer");if(v.length){let e=function(r){return String(r??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")},a=function(r){return e(r).replace(/'/g,"'")},h=function(r){if(r&&r.type==="separator")return'<li class="footer-sep"><hr></li>';if(r&&r.type==="spacer")return`<li class="dm-menu-spacer dm-menu-spacer--${["xs","sm","md","lg","xl","flex"].includes(r.size)?r.size:"md"}" aria-hidden="true"></li>`;if(r&&r.type==="theme-switcher")return'<li class="dm-menu-theme-switcher"><span data-theme-switcher="menu" data-position="inline"></span></li>';const x=a(r.url||"#"),g=e(r.text||""),D=Array.isArray(r.items)&&r.items.length?`<ul>${r.items.map(h).join("")}</ul>`:"",q=Q(r),A=[q.hover&&` data-menu-item-hover="${a(q.hover)}"`,q.active&&` data-menu-item-active="${a(q.active)}"`].filter(Boolean).join(""),M=q.accent?` style="--dm-menu-highlight:${a(q.accent)}"`:"",I=W(r.class).join(" ");return`<li${I?` class="${a(I)}"`:""}><a href="${x}"${A}${M}>${g}</a>${D}</li>`},w=function(r,x){return(r||[]).forEach(g=>{g&&(g.type==="separator"||g.type==="spacer"||g.type==="theme-switcher")||(x.push(g),Array.isArray(g.items)&&g.items.length&&w(g.items,x))}),x};const n=d.social||{},t={twitter:{label:"X / Twitter",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-4.714-6.231-5.401 6.231H2.742l7.73-8.835L1.254 2.25H8.08l4.259 5.629L18.244 2.25zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>'},facebook:{label:"Facebook",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>'},instagram:{label:"Instagram",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zM12 0C8.741 0 8.333.014 7.053.072 2.695.272.273 2.69.073 7.052.014 8.333 0 8.741 0 12c0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98C8.333 23.986 8.741 24 12 24c3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98C15.668.014 15.259 0 12 0zm0 5.838a6.162 6.162 0 100 12.324 6.162 6.162 0 000-12.324zM12 16a4 4 0 110-8 4 4 0 010 8zm6.406-11.845a1.44 1.44 0 100 2.881 1.44 1.44 0 000-2.881z"/></svg>'},linkedin:{label:"LinkedIn",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg>'},github:{label:"GitHub",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>'},youtube:{label:"YouTube",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M23.495 6.205a3.007 3.007 0 00-2.088-2.088c-1.87-.501-9.396-.501-9.396-.501s-7.507-.01-9.396.501A3.007 3.007 0 00.527 6.205a31.247 31.247 0 00-.522 5.805 31.247 31.247 0 00.522 5.783 3.007 3.007 0 002.088 2.088c1.868.502 9.396.502 9.396.502s7.506 0 9.396-.502a3.007 3.007 0 002.088-2.088 31.247 31.247 0 00.5-5.783 31.247 31.247 0 00-.5-5.805zM9.609 15.601V8.408l6.264 3.602z"/></svg>'}};let s='<div class="footer-inner container">';const o=window.__CMS_FOOTER__||{primary:[],legal:[],copyright:""};if(o.copyright&&(s+=`<p>${e(o.copyright)}</p>`),o.primary.length&&(s+=`<nav class="footer-nav footer-nav--primary"><ul>${o.primary.map(h).join("")}</ul></nav>`),o.legal.length&&(s+=`<nav class="footer-nav footer-nav--legal"><ul>${o.legal.map(h).join("")}</ul></nav>`),d.footer){const r=Object.keys(t).filter(g=>n[g]),x=W(d.footer.socialClass).join(" ");r.length&&(s+='<div class="footer-social">',r.forEach(g=>{const{label:D,svg:q}=t[g];s+=`<a href="${n[g]}" target="_blank" rel="noopener noreferrer" aria-label="${D}" class="footer-social-link${x?` ${a(x)}`:""}">${q}</a>`}),s+="</div>")}s+="</div>",v.html(s),window.CMSThemeSwitcher?.scan(v.get(0)),N(v.get(0),d.footer?.class);const i=[...w(o.primary,[]),...w(o.legal,[])];v.get(0).querySelectorAll(".footer-nav a").forEach((r,x)=>{const g=i[x];if(!g)return;g.colour&&P(r,g.colour);const D=j(g.badge);D&&r.appendChild(D),R(g.url,location.pathname)&&(r.classList.add("is-active"),r.setAttribute("aria-current","page"))}),O(v.get(0).querySelector(".footer-nav--primary"),{highlight:o.primaryHighlight}),O(v.get(0).querySelector(".footer-nav--legal"),{highlight:o.legalHighlight}),N(v.get(0).querySelector(".footer-nav--primary"),o.primaryClass),N(v.get(0).querySelector(".footer-nav--legal"),o.legalClass);const y=d.footer?.showMotionToggle!==!1,b=S.get("reduced_motion"),c=d.footer?.motionDefault,m=b!=null?!!b:c==="on"?!0:c==="off"?!1:!!(window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches),l=d.footer?.motionControl==="checkbox",f=v.get(0).querySelector(".footer-inner");if(y&&f){const r=document.createElement("input");r.type="checkbox",r.className=l?"footer-motion-checkbox":"form-switch-input",r.id="dm-motion-switch",r.checked=m,r.addEventListener("change",function(){S.set("reduced_motion",this.checked),window.location.reload()});const x=document.createElement("span");x.className=l?"footer-motion-label":"form-switch-label",x.textContent="Reduce motion";const g=document.createElement("label");g.className=l?"footer-motion-check":"form-switch footer-motion-switch",g.title="Reduce motion",g.appendChild(r),g.appendChild(x),f.appendChild(g)}}const k=document.getElementById("site-sidebar");k&&ie(k),Domma.icons.scan(),Domma.syntax?.scan&&Domma.syntax.scan();const C=$(".page-body");if(C.length){C.find(".accordion").each(function(){Domma.elements.accordion(this,{allowMultiple:this.dataset.multi==="true"})}),C.find(".tabs").each(function(){Domma.elements.tabs(this)}),C.find(".carousel").each(function(){const n={autoplay:this.dataset.autoplay==="true",interval:parseInt(this.dataset.interval,10)||5e3,loop:this.dataset.loop!=="false",animation:this.dataset.animation||"slide"};if(this.dataset.animationDuration){const t=parseInt(this.dataset.animationDuration,10);Number.isFinite(t)&&(n.animationDuration=t)}this.dataset.animationEasing&&(n.animationEasing=this.dataset.animationEasing),this.dataset.ctx&&(this._dmCtxSlides=Array.from(this.querySelectorAll(".carousel-slide")).map(t=>t.cloneNode(!0)),this._dmCarouselOpts=n),this._dmCarousel=Domma.elements.carousel(this,n)}),C.find(".dm-countdown").each(function(){const n={autoStart:!0},t=String(this.dataset.format||"").trim(),s=String(this.dataset.to||"").trim(),o=/^(\d{4})-(\d{2})-(\d{2})$/.exec(s),e=o?new Date(+o[1],+o[2]-1,+o[3]).getTime():s?new Date(s).getTime():NaN;Number.isFinite(e)?n.duration=Math.max(0,e-Date.now()):this.dataset.duration&&(n.duration=(parseInt(this.dataset.duration,10)||0)*1e3);const a=/^dd:/i.test(t);n.format=a?"hh:mm:ss":t||(n.duration>=36e5?"hh:mm:ss":"mm:ss");const h=this,w=u=>String(u).padStart(2,"0"),i=u=>{const y=Math.max(0,Math.floor(u/1e3)),b=h.querySelector(".dm-timer-time");b&&(b.textContent=`${w(Math.floor(y/86400))}:${w(Math.floor(y%86400/3600))}:${w(Math.floor(y%3600/60))}:${w(y%60)}`)};a&&(n.onTick=i),Domma.elements.timer(this,n),a&&i(n.duration||0)}),C.find("[data-tooltip]").each(function(){Domma.elements.tooltip(this,{content:$(this).data("tooltip"),position:$(this).data("tooltip-position")||"top"})}),C.find(".dm-progression").each(function(){const n=Array.from(this.querySelectorAll(".dm-progression-item")).map(o=>{const e={title:o.querySelector(".dm-progression-item-title")?.textContent?.trim()||"",description:o.querySelector(".dm-progression-item-body")?.innerHTML?.trim()||""};return o.dataset.date&&(e.date=o.dataset.date),o.dataset.status&&(e.status=o.dataset.status),o.dataset.entryId&&(e._entryId=o.dataset.entryId),e}),t=this.dataset.layout==="centred"?"centered":this.dataset.layout||"vertical",s=Domma.elements.progression(this,{layout:t,theme:this.dataset.theme||"minimal",mode:this.dataset.mode||"timeline",statusIcons:!0,allowHtml:!0,...n.length?{items:n}:{}});n.length&&(this._dmCtx={instance:s,items:n})});try{Domma.effects.reveal(".page-body .hero",{animation:"slide-up",duration:480,threshold:.06,stagger:60,once:!1})}catch{}document.querySelectorAll(".page-body .row[data-reveal]").forEach(n=>{const t=n.dataset.revealMode||"stagger",s=n.dataset.revealAnimation||"slide-up",o=parseInt(n.dataset.revealDuration,10)||400,e=parseInt(n.dataset.revealStagger,10)||60,a=parseInt(n.dataset.revealDelay,10)||0,h=n.dataset.revealDirection==="rtl",w=Array.from(n.children),i={"slide-up":"translateY(30px)","slide-down":"translateY(-30px)","slide-left":"translateX(30px)","slide-right":"translateX(-30px)",zoom:"scale(0.85)",flip:"perspective(600px) rotateX(15deg)"};w.forEach((u,y)=>{u.style.opacity="0",u.style.transform=i[s]||"",u.style.transition=`opacity ${o}ms ease, transform ${o}ms ease`;const b=h?w.length-1-y:y;u.style.transitionDelay=t==="stagger"?`${a+b*e}ms`:`${a}ms`}),requestAnimationFrame(()=>requestAnimationFrame(()=>{const u=new IntersectionObserver(y=>{y.forEach(b=>{b.isIntersecting&&(b.target.offsetWidth,b.target.style.opacity="1",b.target.style.transform="none",u.unobserve(b.target))})},{threshold:.1});w.forEach(y=>u.observe(y))}))}),C.find(".card[data-collapsible]").each(function(){const n=this.querySelector(".card-header");n&&n.addEventListener("click",()=>this.classList.toggle("is-collapsed"))}),C.find(".dm-so-trigger").each(function(){this.addEventListener("click",()=>{const n=this.dataset.soTarget,t=document.getElementById(n);if(!t)return;const s=E.slideover({title:t.dataset.soTitle||"",size:t.dataset.soSize||"md",position:t.dataset.soPosition||"right"});t.style.display="",s.setContent(t),s.open()})})}if(typeof $.setup=="function"){const n=Object.assign({},window.__CMS_DCONFIG__||{});if(document.querySelectorAll(".dm-page-config[data-config]").forEach(t=>{try{const s=B(t.dataset.config);Object.assign(n,s)}catch{}}),Object.keys(n).length>0){const t={};for(const[s,o]of Object.entries(n)){const e=o?.events?.click,{confirm:a,toast:h,alert:w,prompt:i,...u}=e||{};a||h||w||i?($(s).on("click",async function(b){if(b.preventDefault(),a&&!await E.confirm(a))return;let c=null;if(!(i&&(c=await E.prompt(i,{inputPlaceholder:u.promptPlaceholder||"",inputValue:u.promptDefault||""}),c===null))){if(u.target){const m=$(u.target);u.toggleClass&&m.toggleClass(u.toggleClass),u.addClass&&m.addClass(u.addClass),u.removeClass&&m.removeClass(u.removeClass),c!==null&&(u.setText&&m.text(c),u.setVal&&m.val(c),u.setAttr&&m.attr(u.setAttr,c))}u.href&&(window.location.href=u.href),h&&E.toast(h,{type:u.toastType||"success"}),w&&E.alert(w)}}),Object.keys(u).length&&(t[s]={...o,events:{...o.events,click:u}})):t[s]=o}$.setup(t)}}C.length&&z(C.get(0))}),window.wireCTAButtons=z;function z(p){p.querySelectorAll(".dm-cta-trigger").forEach(d=>{d.__ctaWired||(d.__ctaWired=!0,d.addEventListener("click",async()=>{const L=d.dataset.action,v=d.dataset.entry,k=d.dataset.confirm;let C=S.get("auth_token");if(!C){E.toast("Please log in to perform this action.",{type:"warning"});return}if(k&&!await E.confirm(k))return;const n=Array.from(d.childNodes).map(s=>s.cloneNode(!0));d.disabled=!0,d.textContent="Running\u2026";const t=s=>fetch(`/api/actions/${L}/public`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify(d.dataset.scope?{entryId:v,scope:d.dataset.scope}:{entryId:v})});try{let s=await t(C);if(s.status===401){const e=S.get("auth_refresh_token");if(e){const a=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:e})});if(a.ok){const{token:h}=await a.json();S.set("auth_token",h),C=h,s=await t(C)}}}const o=await s.json().catch(()=>({}));if(!s.ok)throw new Error(o.error||o.message||`Error ${s.status}`);E.toast(o.message||"Action completed.",{type:"success"})}catch(s){E.toast(s.message||"Action failed.",{type:"error"})}finally{d.disabled=!1,d.textContent="",n.forEach(s=>d.appendChild(s)),Domma.icons.scan(d)}}))})}(function(){const d=document.querySelectorAll('.dm-collection-hydrate[data-collection-scope="mine"]');if(!d.length)return;const L=S.get("auth_token");function v(C,n){const t=document.createElement("div");t.className="dm-collection-display dm-collection-empty";const s=document.createElement("p");if(n){s.appendChild(document.createTextNode("Please "));const o=document.createElement("a");o.href="/login",o.textContent="sign in",s.appendChild(o),s.appendChild(document.createTextNode(" to view your entries."))}else s.textContent=C;return t.appendChild(s),t}function k(C,n){const t=document.createElement("template");t.innerHTML=n||"",C.replaceWith(t.content)}d.forEach(async C=>{const n=C.dataset.collectionAttrs;if(!n)return;if(!L){C.replaceChildren(v("",!0));return}const t=s=>fetch("/api/collections/render-scope",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify({attrs:n})});try{let s=L,o=await t(s);if(o.status===401){const a=S.get("auth_refresh_token");if(a){const h=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:a})});if(h.ok){const{token:w}=await h.json();S.set("auth_token",w),s=w,o=await t(s)}}}if(!o.ok){const{error:a}=await o.json().catch(()=>({}));C.replaceChildren(v(a||"Unable to load entries.",!1));return}const{html:e}=await o.json();k(C,e),window.DommaCollectionBrowser?.hydrate&&window.DommaCollectionBrowser.hydrate(document)}catch{C.replaceChildren(v("Unable to load entries.",!1))}}),setTimeout(()=>{typeof Domma<"u"&&Domma.icons?.scan&&Domma.icons.scan(),typeof Domma<"u"&&Domma.syntax?.scan&&Domma.syntax.scan(),z(document.body)},100)})(),(function(){const d=document.querySelectorAll("[data-collection-table]");!d.length||typeof T>"u"||!T.create||d.forEach(L=>{let v;try{v=B(L.dataset.payload)}catch{return}const{columns:k,rows:C,search:n,sortable:t,exportable:s,pageSize:o,empty:e,ctaConfig:a,slug:h,exportCfg:w}=v;if(!k?.length)return;if(a){const y=k.findIndex(b=>b.key==="_cta");y!==-1&&(k[y]={key:"_cta",title:"",render:(b,c)=>{const m=document.createElement("button");if(m.className=`btn btn-${a.style||"primary"} dm-cta-trigger`,m.dataset.action=a.action||"",a.scope&&(m.dataset.scope=a.scope),m.dataset.entry=c._entryId||"",a.confirm&&(m.dataset.confirm=a.confirm),a.icon){const l=document.createElement("span");l.dataset.icon=a.icon,m.appendChild(l),m.appendChild(document.createTextNode(" "))}return m.appendChild(document.createTextNode(a.label||"Run")),m}})}const i="col-table-"+Math.random().toString(36).slice(2,7),u=document.createElement("div");u.id=i,L.replaceChildren(u),T.create("#"+i,{data:C,columns:k,search:n,sortable:t,exportable:s,pageSize:o,emptyMessage:e}),a&&z(u),L._dmTable={columns:k,rows:C,slug:h,exportCfg:w,render(y){T.create("#"+i,{data:y,columns:k,search:n,sortable:t,exportable:s,pageSize:o,emptyMessage:e}),a&&z(u)}}})})(),(function(){const d=document.querySelectorAll("[data-form-inline]");if(!d.length||typeof F>"u")return;function L(t){const s=t&&t.logic;return!!(s&&(s.visibility||s.requirement))}function v(t,s){const o={};return(t||[]).forEach(e=>{if(e.type==="page-break"||e.type==="spacer"||!e.name)return;const a=e.type==="chooser"||!!(e.variant&&["select","radio","checkbox-group"].includes(e.type)),h=e.type==="checkbox"?"boolean":e.type==="date"||e.type==="icon"||e.type==="image"?"string":a?"chooser":e.type,w={...e.formConfig||{}};w.span==="full"&&s&&(w.span=s);const i={type:h,required:L(e)?!1:e.required,label:e.label,options:e.options,formConfig:{...e.placeholder&&{placeholder:e.placeholder},...e.helper&&{hint:e.helper},...e.tooltip&&{tooltip:e.tooltip},...w}};e.minLength!==void 0&&(i.minLength=e.minLength),e.maxLength!==void 0&&(i.maxLength=e.maxLength),e.min!==void 0&&(i.min=e.min),e.max!==void 0&&(i.max=e.max),a&&(e.variant&&(i.variant=e.variant),(e.multiple||e.type==="checkbox-group")&&(i.multiple=!0),e.density&&(i.density=e.density),e.columns&&(i.columns=e.columns),e.accent&&(i.accent=e.accent),e.accentStyle&&(i.accentStyle=e.accentStyle),e.glow&&(i.glow=!0),e.glowColour&&(i.glowColour=e.glowColour),e.shadow&&(i.shadow=e.shadow),e.shadowColour&&(i.shadowColour=e.shadowColour)),o[e.name]=i}),o}function k(t){const s={};return(t||[]).forEach(o=>{if(!(!o.name||o.type==="page-break"||o.type==="spacer")&&(o.type==="select"||o.type==="multiselect")&&o.required){const e=(o.options||[])[0];e&&(s[o.name]=typeof e=="object"?e.value:e)}}),s}function C(t,s){(s||[]).forEach(o=>{if(o.type!=="date"||!o.name)return;const e=t.querySelector(`[name="${o.name}"]`);e&&e.type!=="date"&&(e.type="date")})}function n(t,s,o){let e=t.querySelector(".cms-form-message");e||(e=document.createElement("p"),e.className="cms-form-message",t.appendChild(e)),e.textContent=s,e.style.cssText=o?"color:var(--dm-danger);margin-top:.75rem;":"color:var(--dm-success);margin-top:.75rem;"}d.forEach(t=>{let s;try{s=B(t.dataset.formInline)}catch{return}const o=s.fields||[],e=s.settings||{},a=Number(e.columns)===3?3:2,h=e.layout||"grid",w=o.some(m=>m.type==="page-break"),i=async m=>{if(t.dataset.triggerBlocked==="true"){const l=t._formLogicRuntime,f=l&&l.submitBlock?l.submitBlock().reasons:[];n(t,f.join(" ")||"This form cannot be submitted yet.",!0);const r=new Error("Blocked by a form trigger");throw r.formSubmitFailed=!0,r}try{const l=t.querySelector(".fb-form-honeypot"),f=l?.querySelector('input[type="text"]')||null,r=l?.querySelector('[name="_t"]')||null,x=Object.assign({},m);delete x._t,l?.dataset.hpName&&delete x[l.dataset.hpName],f!==null&&(x._hp=f.value),r!==null&&(x._t=r.value);const g=await H.post(`/api/forms/submit/${s.slug}`,x);if(g&&g.error){n(t,g.error,!0);const D=new Error(g.error);throw D.formSubmitFailed=!0,D}if(g?.redirect){window.location.href=g.redirect;return}for(;t.firstChild;)t.removeChild(t.firstChild);n(t,g?.message||e.successMessage||"Thank you for your submission.",!1)}catch(l){throw l.formSubmitFailed||n(t,l.message||"Submission failed. Please try again.",!0),l}},u=Date.now();function y(m){const l=m.querySelector("form");if(!l||l.querySelector(".fb-form-honeypot"))return;const f=document.createElement("div");f.className="fb-form-honeypot",f.setAttribute("aria-hidden","true");const r=document.createElement("input");r.name=l.querySelector('[name="website"]')?"_website":"website",f.dataset.hpName=r.name,r.type="text",r.tabIndex=-1,r.autocomplete="url",r.placeholder="https://",f.appendChild(r);const x=document.createElement("input");x.name="_t",x.type="hidden",x.value=u,f.appendChild(x),l.appendChild(f)}function b(m){if(!window.FormLogicEngine||!o.some(f=>f.logic||Array.isArray(f.triggers)&&f.triggers.length))return null;const l=new window.FormLogicEngine.FormLogicRuntime(s,t,m?{baseValues:m}:void 0);if(l.init(),t._formLogicRuntime=l,t._formLogicObserved)return l;if(t._formLogicObserved=!0,t.parentNode&&typeof MutationObserver<"u"){const f=new MutationObserver(function(r){for(const x of r)for(const g of x.removedNodes)if(g===t||g.nodeType===1&&g.contains&&g.contains(t)){t._formLogicRuntime&&t._formLogicRuntime.destroy(),f.disconnect();return}});f.observe(t.parentNode,{childList:!0,subtree:!1}),t._formLogicObserver=f}return l}let c=!1;if(t.addEventListener("dm-form-end",m=>{const l=m.detail?.validation;if(l&&!l.valid){Y(t,l.missing),J(l);return}c||(c=!0,i(m.detail?.values||{}).catch(()=>{}).finally(()=>{c=!1}))}),w&&F.wizard){const m=te(o,s.title),l=m.map(A=>({title:A.title,description:A.description,fields:v(A.fields,a)})),f=m.map(A=>A.fields);let r=0,x=null,g=null;const D=A=>{const M=window.FormLogicEngine;if(!M||!M.validateFields)return;const I=M.validateFields(t,f[r]||[]);I.valid||(A.preventDefault(),A.stopImmediatePropagation(),Y(t,I.missing),J(I))},q=()=>{const A=f[r]||o;oe(t,e),X(t,e,A),C(t,A),V(t,A),e.honeypot!==!1&&y(t),x&&x.destroy(),x=b(g?.getStepData?g.getStepData():void 0),t.querySelectorAll(".wizard-next, .wizard-submit").forEach(M=>M.addEventListener("click",D,!0))};g=F.wizard(t,{schema:{steps:l},layout:h,columns:a,finishText:e.submitText||"Finish",onSubmit:i,onStepChange:A=>{r=A,setTimeout(q,0)}}),t.addEventListener("dm-form-trigger",A=>{if(A.detail?.type!=="goto-step"||!g?.goToStep)return;const M=Number(A.detail.action.step);!Number.isFinite(M)||M<0||M>=l.length||M!==r&&g.goToStep(M)}),Promise.resolve(g).then(q)}else if(F.render){const m=F.render(t,v(o,a),k(o),{submitText:e.submitText||"Submit",layout:h,columns:a,onSubmit:i});Promise.resolve(m).then(function(){ne(t,e,h),X(t,e,o),C(t,o),V(t,o),e.honeypot!==!1&&y(t),b()})}}),$(document).on("click",".dm-banner__dismiss",function(){$(this).closest(".dm-banner").remove()})})();function ae(p,d){const L=document.body,v=L.classList,k=new Set((L.dataset.railSite||"").split(" ").filter(Boolean)),C=()=>window.matchMedia("(min-width: 993px)").matches;p.querySelectorAll(".navbar-link, .navbar-dropdown-toggle").forEach(a=>{const h=a.textContent.trim();h&&(a.setAttribute("title",h),a.querySelector("svg, [data-icon]")||(a.dataset.initial=h.charAt(0).toUpperCase()))});const n=document.createElement("button");n.type="button",n.className="dm-nav-collapse-toggle";const t=()=>{const a=v.contains("dm-vnav-collapsed");n.setAttribute("aria-pressed",String(a)),n.setAttribute("aria-label",a?"Expand menu":"Collapse menu"),n.title=n.getAttribute("aria-label");const h=d?"chevrons-left":"chevrons-right",w=d?"chevrons-right":"chevrons-left";n.innerHTML=`<span data-icon="${a?h:w}"></span>`,window.Domma&&Domma.icons&&Domma.icons.scan(n)},s=a=>{v.toggle("dm-vnav-collapsed",a),S.set("nav_collapsed",a),t()};n.addEventListener("click",()=>s(!v.contains("dm-vnav-collapsed"))),t();const o=p.querySelector(".navbar-container")||p;o.insertBefore(n,o.firstChild?.nextSibling||null);const e=a=>{v.toggle("dm-vnav-autohide",a),v.toggle("dm-vnav-collapsible",!a&&k.has("collapsible")),a?v.remove("dm-vnav-collapsed"):k.has("collapsible")&&S.get("nav_collapsed")===!0&&v.add("dm-vnav-collapsed"),p.classList.remove("dm-nav-revealed"),t()};p.addEventListener("pointerdown",a=>{v.contains("dm-vnav-autohide")&&a.pointerType!=="mouse"&&!p.classList.contains("dm-nav-revealed")&&(p.classList.add("dm-nav-revealed"),a.preventDefault())}),document.addEventListener("pointerdown",a=>{p.contains(a.target)||p.classList.remove("dm-nav-revealed")}),window.E&&typeof E.contextMenu=="function"&&E.contextMenu(p,{items:()=>{const a=v.contains("dm-vnav-autohide"),h=v.contains("dm-vnav-collapsed"),w=S.has("nav_autohide")||S.has("nav_collapsed");return[{header:"Menu"},{label:h?"Expand menu":"Collapse to icons",icon:h?"maximize":"minimize",disabled:!C()||a||!k.has("collapsible"),action:()=>s(!h)},{label:a?"Keep on screen":"Hide when not in use",icon:a?"eye":"eye-off",disabled:!C(),action:()=>{S.set("nav_autohide",!a),e(!a)}},{type:"divider"},{label:"Reset to the site's setting",icon:"refresh",disabled:!w,action:()=>{S.remove("nav_autohide"),S.remove("nav_collapsed"),e(k.has("autohide")),v.toggle("dm-vnav-collapsed",!k.has("autohide")&&k.has("start-collapsed")),t()}}]}})}function se(p){p.classList.add("dm-nav-scroll-hide");let d=window.scrollY,L=!1;window.addEventListener("scroll",()=>{L||(L=!0,requestAnimationFrame(()=>{L=!1;const v=window.scrollY,k=p.matches(":focus-within")||p.querySelector(".navbar-dropdown.open, .navbar-collapse.show");v>d&&v>p.offsetHeight&&!k?p.classList.add("dm-nav-scrolled-away"):v<d&&p.classList.remove("dm-nav-scrolled-away"),d=v}))},{passive:!0})}function ie(p){let d={};try{d=JSON.parse(p.dataset.contents||"{}")}catch{}const L=p.closest(".site-main"),v=document.querySelector(".site-content"),k=new Set(String(d.exclude||"").split(",").map(i=>i.trim().toLowerCase()).filter(Boolean)),C=i=>!i.closest(".hero, .card, .accordion, .tabs, figure, #site-sidebar"),n=i=>(v?[...v.querySelectorAll(i)]:[]).filter(C).filter(u=>!k.has(u.textContent.trim().toLowerCase())),t=d.headings==="h2"?n("h2"):d.headings==="h3"?n("h3"):d.headings==="h2h3"?n("h2, h3"):n("h2").length?n("h2"):n("h3");if(!t.length){L?.classList.add("no-sidebar-items");return}const s=new Set([...document.querySelectorAll("[id]")].map(i=>i.id)),o=document.createElement("nav");o.className="dm-contents",o.setAttribute("aria-label",d.title||"Page contents"),d.hover&&(o.dataset.menuHover=d.hover),d.selected&&(o.dataset.menuActive=d.selected),d.accent&&o.style.setProperty("--dm-menu-highlight",d.accent.startsWith("#")?d.accent:`var(--dm-${d.accent})`),d.font&&(o.style.fontFamily=d.font),d.fontSize&&(o.style.fontSize=d.fontSize),d.fontWeight&&(o.style.fontWeight=d.fontWeight);const e=document.createElement("div");e.className="dm-contents-head";const a=document.createElement("p");a.className="dm-contents-title",a.textContent=d.title||"",e.appendChild(a),o.appendChild(e);const h=document.createElement("ol");h.className="dm-contents-list"+(d.numbered?" is-numbered":"");const w=[];for(const i of t){if(!i.id){const b=(i.textContent||"").toLowerCase().trim().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")||"section";let c=b;for(let m=2;s.has(c);m++)c=`${b}-${m}`;s.add(c),i.id=c}const u=document.createElement("li");u.className=`dm-contents-item dm-contents-${i.tagName.toLowerCase()}`;const y=document.createElement("a");y.href=`#${i.id}`,y.textContent=i.textContent.trim(),u.appendChild(y),h.appendChild(u),w.push([i,y])}if(o.appendChild(h),p.replaceChildren(o),d.scrollSpy!==!1&&"IntersectionObserver"in window){const i=new Map,u=()=>{const b=w.find(([c])=>i.get(c))||null;w.forEach(([,c])=>{const m=b&&c===b[1];c.classList.toggle("is-active",!!m),m?c.setAttribute("aria-current","location"):c.removeAttribute("aria-current")})},y=new IntersectionObserver(b=>{b.forEach(c=>i.set(c.target,c.isIntersecting)),u()},{rootMargin:"-80px 0px -60% 0px"});w.forEach(([b])=>y.observe(b))}if(L?.classList.contains("contents-collapsible")){const i=document.createElement("button");i.type="button",i.className="dm-contents-toggle";const u=()=>{const y=L.classList.contains("contents-collapsed");i.setAttribute("aria-expanded",String(!y)),i.setAttribute("aria-label",y?"Show the contents":"Hide the contents"),i.title=i.getAttribute("aria-label"),i.innerHTML=`<span data-icon="${y?"list":"x"}"></span>`,window.Domma?.icons&&Domma.icons.scan(i)};i.addEventListener("click",()=>{S.set("contents_collapsed",L.classList.toggle("contents-collapsed")),u()}),u(),e.insertBefore(i,e.firstChild)}if(L?.classList.contains("contents-floating")&&d.movable){o.classList.add("is-movable");const i=S.get("contents_position");i&&Number.isFinite(i.x)&&Object.assign(p.style,{left:`${i.x}px`,top:`${i.y}px`,right:"auto",bottom:"auto"}),e.addEventListener("pointerdown",u=>{if(u.target.closest("button"))return;const y=p.getBoundingClientRect(),b=u.clientX-y.left,c=u.clientY-y.top,m=f=>{const r=Math.max(0,Math.min(window.innerWidth-y.width,f.clientX-b)),x=Math.max(0,Math.min(window.innerHeight-40,f.clientY-c));Object.assign(p.style,{left:`${r}px`,top:`${x}px`,right:"auto",bottom:"auto"})},l=()=>{document.removeEventListener("pointermove",m),S.set("contents_position",{x:parseInt(p.style.left,10),y:parseInt(p.style.top,10)})};document.addEventListener("pointermove",m),document.addEventListener("pointerup",l,{once:!0})})}window.E&&typeof E.contextMenu=="function"&&E.contextMenu(p,{items:()=>{const i=L?.classList.contains("contents-collapsible"),u=L?.classList.contains("contents-collapsed"),y=w.find(([,b])=>b.classList.contains("is-active"));return[{header:"Contents"},...i?[{label:u?"Show the contents":"Collapse to a button",icon:u?"list":"minimize",action:()=>p.querySelector(".dm-contents-toggle")?.click()}]:[],{label:"Copy a link to this section",icon:"link",disabled:!y,action:async()=>{const b=`${location.origin}${location.pathname}#${y[0].id}`;try{await _.copyToClipboard(b),E.toast("Link copied.",{type:"success"})}catch{E.toast("Could not copy the link.",{type:"error"})}}},{label:"Back to the top",icon:"arrow-up",action:()=>window.scrollTo({top:0,behavior:"smooth"})}]}}),L?.classList.contains("contents-autohide")&&(p.addEventListener("pointerdown",i=>{i.pointerType!=="mouse"&&!p.classList.contains("is-revealed")&&(p.classList.add("is-revealed"),i.preventDefault())}),document.addEventListener("pointerdown",i=>{p.contains(i.target)||p.classList.remove("is-revealed")}))}
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
*
|
|
13
13
|
* `_` is outside the key alphabet (slugify below), so no layout can shadow the last two.
|
|
14
14
|
*/
|
|
15
|
+
import {normaliseContents} from '../../services/pageContents.js';
|
|
15
16
|
import {getConfig, saveConfig} from '../../config.js';
|
|
16
17
|
import {
|
|
17
18
|
authenticate as defaultAuthenticate,
|
|
@@ -77,6 +78,8 @@ function sanitisePreset(data) {
|
|
|
77
78
|
footer: data.footer !== false,
|
|
78
79
|
sidebar: data.sidebar === true,
|
|
79
80
|
sidebarSide: data.sidebarSide === 'right' ? 'right' : 'left',
|
|
81
|
+
// The page contents list's settings (pageContents.js); empty when none are set.
|
|
82
|
+
...(Object.keys(normaliseContents(data.contents)).length && {contents: normaliseContents(data.contents)}),
|
|
80
83
|
width: VALID_WIDTHS.has(data.width) ? data.width : 'normal',
|
|
81
84
|
bgColor: data.bgColor && BG_COLOR_RE.test(data.bgColor) ? data.bgColor : '',
|
|
82
85
|
bgImage: typeof data.bgImage === 'string' ? data.bgImage.trim() : '',
|
|
@@ -13,6 +13,7 @@ import {applyTransforms, getSanitizeExtensions, getShortcodeProcessors} from './
|
|
|
13
13
|
import {getConfig} from '../config.js';
|
|
14
14
|
import {getCollection, listEntries} from './collections.js';
|
|
15
15
|
import {getMenu, navbarOverrideFromAttrs, resolveLocation, resolveMenuDecorations} from './menus.js';
|
|
16
|
+
import {contentsOverrideFromAttrs} from './pageContents.js';
|
|
16
17
|
import {buildMenuNav} from './menuRender.js';
|
|
17
18
|
import {checkVisibility} from '../middleware/auth.js';
|
|
18
19
|
import {getRoleHierarchy, getRoleLevel} from './roles.js';
|
|
@@ -3633,6 +3634,26 @@ function processSpacerBlocks(markdown) {
|
|
|
3633
3634
|
* [navbar autohide="true" width="200" /]
|
|
3634
3635
|
* [navbar position="floating" float-anchor="TR" float-collapsible="true" /]
|
|
3635
3636
|
*/
|
|
3637
|
+
/**
|
|
3638
|
+
* [contents ... /] - this page's own settings for its contents list (the
|
|
3639
|
+
* sidebar a layout builds from the page's headings), over the layout's. See
|
|
3640
|
+
* pageContents.js for the attributes. Leaves a hidden marker the renderer reads
|
|
3641
|
+
* (the first tag on a page counts); draws nothing itself.
|
|
3642
|
+
*
|
|
3643
|
+
* [contents side="right" numbered="true" /]
|
|
3644
|
+
* [contents headings="h2h3" title="On this page" exclude="API Reference" /]
|
|
3645
|
+
* [contents floating="true" float-anchor="BR" movable="true" /]
|
|
3646
|
+
* [contents hidden="true" /]
|
|
3647
|
+
*/
|
|
3648
|
+
function processContentsBlocks(markdown) {
|
|
3649
|
+
const {scrubbed, restore} = scrubCodeRegions(markdown);
|
|
3650
|
+
return restore(scrubbed.replace(/\[contents(\s[^\]]*?)?\/?\]/gi, (_, attrStr) => {
|
|
3651
|
+
const override = contentsOverrideFromAttrs(parseShortcodeAttrs(attrStr || ''));
|
|
3652
|
+
if (!Object.keys(override).length) return '';
|
|
3653
|
+
return `<span class="dm-contents-override" hidden data-contents-override="${escapeAttr(JSON.stringify(override))}"></span>`;
|
|
3654
|
+
}));
|
|
3655
|
+
}
|
|
3656
|
+
|
|
3636
3657
|
function processNavbarBlocks(markdown) {
|
|
3637
3658
|
const {scrubbed, restore} = scrubCodeRegions(markdown);
|
|
3638
3659
|
return restore(scrubbed.replace(/\[navbar(\s[^\]]*?)?\/?\]/gi, (_, attrStr) => {
|
|
@@ -4494,7 +4515,7 @@ export async function parseMarkdown(raw, opts = {}) {
|
|
|
4494
4515
|
const withTimeline = processTimelineBlocks(withCountdown);
|
|
4495
4516
|
const withListGroup = processListGroupBlocks(withTimeline);
|
|
4496
4517
|
const withSpacer = processSpacerBlocks(withListGroup);
|
|
4497
|
-
const withSwitcher = processNavbarBlocks(processThemeSwitcherBlocks(withSpacer));
|
|
4518
|
+
const withSwitcher = processContentsBlocks(processNavbarBlocks(processThemeSwitcherBlocks(withSpacer)));
|
|
4498
4519
|
const withCenter = processCenterBlocks(withSwitcher);
|
|
4499
4520
|
const withIcon = processIconBlocks(withCenter);
|
|
4500
4521
|
const withDemo = processDemoBlocks(withIcon);
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Page contents - the list a layout with a sidebar builds from the page's own
|
|
3
|
+
* headings (the "With Sidebar" layout's left or right column).
|
|
4
|
+
*
|
|
5
|
+
* Its settings live on the layout (presets.json: `contents: {...}`, edited in
|
|
6
|
+
* Layouts) and a page can change any of them for itself with `[contents ... /]`.
|
|
7
|
+
* The renderer merges layout, page front matter (`sidebarSide`, kept for older
|
|
8
|
+
* pages) and the page's tag, and hands the result to site.js as __CMS_CONTENTS__;
|
|
9
|
+
* the parts the first paint needs (side, width, hidden, floating, autohide) also
|
|
10
|
+
* become classes and a token on the page.
|
|
11
|
+
*
|
|
12
|
+
* Layout side, width, sticky, floating, floatAnchor, movable, hidden
|
|
13
|
+
* Behaviour collapsible, collapsed, autohide, scrollSpy
|
|
14
|
+
* Content headings ('auto' | 'h2' | 'h3' | 'h2h3'), numbered, title, exclude
|
|
15
|
+
* Look hover, selected, accent, font, fontSize, fontWeight
|
|
16
|
+
*/
|
|
17
|
+
import {FLOAT_ANCHORS, isValidColour} from './menus.js';
|
|
18
|
+
import {HIGHLIGHT_HOVER, HIGHLIGHT_ACTIVE} from '../../public/js/menu-decor.mjs';
|
|
19
|
+
|
|
20
|
+
export const CONTENTS_WIDTH_MIN = 160;
|
|
21
|
+
export const CONTENTS_WIDTH_MAX = 480;
|
|
22
|
+
const HEADINGS = new Set(['auto', 'h2', 'h3', 'h2h3']);
|
|
23
|
+
const BOOL_KEYS = ['sticky', 'floating', 'movable', 'hidden', 'collapsible', 'collapsed', 'autohide', 'scrollSpy', 'numbered'];
|
|
24
|
+
const CSS_SIZE = /^\d+(\.\d+)?(px|rem|em|%)$/;
|
|
25
|
+
const FONT = /^[A-Za-z0-9 ,'"-]{1,80}$/;
|
|
26
|
+
|
|
27
|
+
/** The defaults: what the With Sidebar layout has always done. */
|
|
28
|
+
export const CONTENTS_DEFAULTS = Object.freeze({side: 'left', sticky: true, scrollSpy: true, headings: 'auto'});
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* A contents block with only known keys and sane values. Booleans keep `false`
|
|
32
|
+
* (a page turning off what its layout turns on); unknown or bad values are dropped.
|
|
33
|
+
*/
|
|
34
|
+
export function normaliseContents(input) {
|
|
35
|
+
if (!input || typeof input !== 'object' || Array.isArray(input)) return {};
|
|
36
|
+
const out = {};
|
|
37
|
+
if (input.side === 'left' || input.side === 'right') out.side = input.side;
|
|
38
|
+
const width = parseInt(input.width, 10);
|
|
39
|
+
if (Number.isFinite(width)) out.width = Math.min(CONTENTS_WIDTH_MAX, Math.max(CONTENTS_WIDTH_MIN, width));
|
|
40
|
+
for (const key of BOOL_KEYS) if (typeof input[key] === 'boolean') out[key] = input[key];
|
|
41
|
+
if (FLOAT_ANCHORS.has(input.floatAnchor)) out.floatAnchor = input.floatAnchor;
|
|
42
|
+
if (HEADINGS.has(input.headings)) out.headings = input.headings;
|
|
43
|
+
if (typeof input.title === 'string' && input.title.trim()) out.title = input.title.trim().slice(0, 80);
|
|
44
|
+
if (typeof input.exclude === 'string' && input.exclude.trim()) out.exclude = input.exclude.trim().slice(0, 500);
|
|
45
|
+
if (HIGHLIGHT_HOVER.includes(input.hover)) out.hover = input.hover;
|
|
46
|
+
if (HIGHLIGHT_ACTIVE.includes(input.selected)) out.selected = input.selected;
|
|
47
|
+
if (input.accent && isValidColour(input.accent)) out.accent = input.accent;
|
|
48
|
+
if (typeof input.font === 'string' && FONT.test(input.font)) out.font = input.font;
|
|
49
|
+
if (typeof input.fontSize === 'string' && CSS_SIZE.test(input.fontSize)) out.fontSize = input.fontSize;
|
|
50
|
+
if (/^[1-9]00$|^(normal|bold)$/.test(String(input.fontWeight || ''))) out.fontWeight = String(input.fontWeight);
|
|
51
|
+
return out;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** The tag's attributes (kebab-case) and the keys they set. */
|
|
55
|
+
export const CONTENTS_ATTRS = {
|
|
56
|
+
'side': 'side', 'width': 'width', 'sticky': 'sticky', 'floating': 'floating', 'float-anchor': 'floatAnchor',
|
|
57
|
+
'movable': 'movable', 'hidden': 'hidden', 'collapsible': 'collapsible', 'collapsed': 'collapsed',
|
|
58
|
+
'autohide': 'autohide', 'scroll-spy': 'scrollSpy', 'headings': 'headings', 'numbered': 'numbered',
|
|
59
|
+
'title': 'title', 'exclude': 'exclude', 'hover': 'hover', 'selected': 'selected', 'accent': 'accent',
|
|
60
|
+
'font': 'font', 'font-size': 'fontSize', 'font-weight': 'fontWeight'
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/** The override a `[contents]` tag's attributes describe (normalised). */
|
|
64
|
+
export function contentsOverrideFromAttrs(attrs = {}) {
|
|
65
|
+
const raw = {};
|
|
66
|
+
for (const [name, value] of Object.entries(attrs)) {
|
|
67
|
+
const key = CONTENTS_ATTRS[name];
|
|
68
|
+
if (!key) continue;
|
|
69
|
+
let v = value === true ? 'true' : String(value).trim();
|
|
70
|
+
if (BOOL_KEYS.includes(key)) v = !(v === 'false' || v === '0' || v === 'no');
|
|
71
|
+
raw[key] = v;
|
|
72
|
+
}
|
|
73
|
+
return normaliseContents(raw);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* The settings a page gets: defaults, then its layout's, then the older
|
|
78
|
+
* `sidebarSide` (layout, then page), then the page's `[contents]` tag.
|
|
79
|
+
*/
|
|
80
|
+
export function resolveContents(preset = {}, page = {}, override = null) {
|
|
81
|
+
const layout = normaliseContents(preset.contents);
|
|
82
|
+
const merged = {
|
|
83
|
+
...CONTENTS_DEFAULTS,
|
|
84
|
+
...(preset.sidebarSide === 'right' && {side: 'right'}),
|
|
85
|
+
...layout,
|
|
86
|
+
...(page.sidebarSide === 'right' || page.sidebarSide === 'left' ? {side: page.sidebarSide} : {}),
|
|
87
|
+
...normaliseContents(override || {})
|
|
88
|
+
};
|
|
89
|
+
// Autohide and floating take the list out of the column; collapse then means nothing.
|
|
90
|
+
if (merged.autohide || merged.floating) { delete merged.collapsible; delete merged.collapsed; }
|
|
91
|
+
if (merged.collapsed) merged.collapsible = true;
|
|
92
|
+
return merged;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* The classes the page's <main> needs from the first paint, and the width token.
|
|
97
|
+
* @returns {{mainClass: string, style: string}}
|
|
98
|
+
*/
|
|
99
|
+
export function contentsPaint(c) {
|
|
100
|
+
const cls = [
|
|
101
|
+
c.side === 'right' ? 'sidebar-right' : '',
|
|
102
|
+
c.hidden ? 'contents-hidden' : '',
|
|
103
|
+
c.floating ? `contents-floating contents-float-${(c.floatAnchor || 'TR').toLowerCase()}` : '',
|
|
104
|
+
c.autohide ? 'contents-autohide' : '',
|
|
105
|
+
c.sticky === false ? 'contents-static' : '',
|
|
106
|
+
c.collapsible ? 'contents-collapsible' : '',
|
|
107
|
+
c.collapsed ? 'contents-start-collapsed' : ''
|
|
108
|
+
].filter(Boolean).join(' ');
|
|
109
|
+
const style = Number.isFinite(c.width) ? `--dm-contents-width:${c.width}px` : '';
|
|
110
|
+
return {mainClass: cls, style};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** The override a page's [contents] tag left in its html (the first), or null. */
|
|
114
|
+
export function readContentsOverride(html) {
|
|
115
|
+
const m = /data-contents-override="([^"]*)"/.exec(html || '');
|
|
116
|
+
if (!m) return null;
|
|
117
|
+
try {
|
|
118
|
+
return JSON.parse(m[1].replace(/"/g, '"').replace(/'/g, "'").replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&'));
|
|
119
|
+
} catch {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -8,6 +8,7 @@ import {fileURLToPath} from 'url';
|
|
|
8
8
|
import {getConfig} from '../config.js';
|
|
9
9
|
import {getInjectionSnippets} from './plugins.js';
|
|
10
10
|
import {applyTransforms} from './hooks.js';
|
|
11
|
+
import {contentsPaint, readContentsOverride, resolveContents} from './pageContents.js';
|
|
11
12
|
import {applyMenuOverride, hasThemeSwitcherItem, resolveLocation, resolveMenuDecorations, resolveOverlaysForPage} from './menus.js';
|
|
12
13
|
import {buildMenuNav} from './menuRender.js';
|
|
13
14
|
import {getProjectForPage} from './projects.js';
|
|
@@ -122,6 +123,11 @@ function buildAnalyticsTag() {
|
|
|
122
123
|
* html (body and overlay menus) and the item lists of its navbar and footers
|
|
123
124
|
* @returns {{headTag: string, bodyTag: string}} Empty strings when nothing needs it
|
|
124
125
|
*/
|
|
126
|
+
/** A value for a double-quoted HTML attribute. */
|
|
127
|
+
function escapeAttrValue(v) {
|
|
128
|
+
return String(v).replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<').replace(/>/g, '>');
|
|
129
|
+
}
|
|
130
|
+
|
|
125
131
|
/** The site's reduced-motion default for visitors who have not chosen: 'system', 'off' or 'on'. */
|
|
126
132
|
function motionDefaultOf(site) {
|
|
127
133
|
const d = site?.footer?.motionDefault;
|
|
@@ -309,6 +315,7 @@ export async function renderPage(page, opts = {}) {
|
|
|
309
315
|
: (await readLegacyNavBackup()) || {brand: site.brand || {}, items: []};
|
|
310
316
|
|
|
311
317
|
const preset = presets[page.layout] || presets['default'] || {};
|
|
318
|
+
const pageContents = resolveContents(preset, page, readContentsOverride(page.html));
|
|
312
319
|
|
|
313
320
|
// Per-page `width:` frontmatter overrides the preset's width when valid.
|
|
314
321
|
const pageWidth = VALID_LAYOUT_WIDTHS.has(page.width) ? page.width : null;
|
|
@@ -428,8 +435,13 @@ export async function renderPage(page, opts = {}) {
|
|
|
428
435
|
railDocked: /\bdm-vnav\b/.test(verticalNavClass),
|
|
429
436
|
showNavbar: preset.navbar !== false,
|
|
430
437
|
showFooter: preset.footer !== false,
|
|
431
|
-
|
|
432
|
-
|
|
438
|
+
// The page contents list: the layout's settings, the page's sidebarSide and its
|
|
439
|
+
// [contents] tag merged (pageContents.js); `hidden` drops the column altogether.
|
|
440
|
+
showSidebar: (preset.sidebar === true || page.sidebar === true) && !pageContents.hidden,
|
|
441
|
+
contentsClass: contentsPaint(pageContents).mainClass,
|
|
442
|
+
contentsStyle: contentsPaint(pageContents).style,
|
|
443
|
+
contentsJson: escapeAttrValue(JSON.stringify(pageContents)),
|
|
444
|
+
contentsCollapsible: pageContents.collapsible === true && (preset.sidebar === true || page.sidebar === true) && !pageContents.hidden,
|
|
433
445
|
navJson: JSON.stringify(filterHiddenNavItems(navigation)).replace(/<\/script>/gi, '<\\/script>'),
|
|
434
446
|
footerScript: footerJson ? `window.__CMS_FOOTER__ = ${footerJson};` : '',
|
|
435
447
|
ctxMenusScript: ctxMenusScript,
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
<link rel="stylesheet" href="/dist/domma/themes/domma-themes.css">
|
|
26
26
|
|
|
27
27
|
<!-- Site CSS -->
|
|
28
|
-
<link rel="stylesheet" href="/public/css/site.css?v=20261011-
|
|
28
|
+
<link rel="stylesheet" href="/public/css/site.css?v=20261011-page-contents">
|
|
29
29
|
<link rel="stylesheet" href="/public/css/forms.css?v=20260927-submit-btn-css">
|
|
30
30
|
<!-- Menu item hover / current-page effects. Loaded after site.css so a
|
|
31
31
|
menu's own highlight wins over the blanket link styling there. -->
|
|
@@ -65,9 +65,19 @@
|
|
|
65
65
|
<nav id="site-navbar" class="{{navbarClass}}"></nav>
|
|
66
66
|
{{/if}}
|
|
67
67
|
|
|
68
|
-
<main class="site-main {{#if showSidebar}}with-sidebar{{/if}}
|
|
68
|
+
<main class="site-main {{#if showSidebar}}with-sidebar {{contentsClass}}{{/if}}"{{#if contentsStyle}} style="{{contentsStyle}}"{{/if}}>
|
|
69
69
|
{{#if showSidebar}}
|
|
70
|
-
<aside id="site-sidebar" class="site-sidebar"></aside>
|
|
70
|
+
<aside id="site-sidebar" class="site-sidebar" data-contents="{{contentsJson}}"></aside>
|
|
71
|
+
{{/if}}
|
|
72
|
+
{{#if contentsCollapsible}}
|
|
73
|
+
<script>
|
|
74
|
+
// The visitor's own choice (or the layout's starting state) before the first paint.
|
|
75
|
+
(function () {
|
|
76
|
+
var m = document.currentScript.parentNode, c = null;
|
|
77
|
+
try { c = JSON.parse(localStorage.getItem('domma:contents_collapsed')); } catch (e) {}
|
|
78
|
+
if (c === true || (c === null && m.classList.contains('contents-start-collapsed'))) m.classList.add('contents-collapsed');
|
|
79
|
+
})();
|
|
80
|
+
</script>
|
|
71
81
|
{{/if}}
|
|
72
82
|
|
|
73
83
|
<article class="site-content">
|
|
@@ -163,7 +173,7 @@
|
|
|
163
173
|
</script>
|
|
164
174
|
|
|
165
175
|
<!-- Site initialisation -->
|
|
166
|
-
<script src="/public/js/site.js?v=20261011-
|
|
176
|
+
<script src="/public/js/site.js?v=20261011-page-contents" type="module"></script>
|
|
167
177
|
<script src="/public/js/collection-context.js?v=20261010-utf8-payloads" type="module"></script>
|
|
168
178
|
{{ctxMenusModule}}
|
|
169
179
|
|