domma-cms 1.6.13 → 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/lib/site-settings-arrange.js +1 -1
- package/admin/js/templates/layouts.html +6 -0
- package/admin/js/templates/settings.html +17 -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 +22 -2
- package/server/templates/page.html +21 -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 D,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 ae}from"/public/js/form-submit.mjs";$(()=>{const h=window.__CMS_NAV__||{},g=window.__CMS_SITE__||{};if(g.autoTheme?.enabled){let t=function(e){const n=(e||"07:00").split(":");return+n[0]*60+(+n[1]||0)},s=function(){const e=new Date,n=e.getHours()*60+e.getMinutes();return n>=t(o.dayStart)&&n<t(o.nightStart)?o.dayTheme:o.nightTheme},i=function(e){const n=o.apply&&o.apply[e]||{theme:e,custom:""};Domma.theme.set(n.theme);for(const r of[...document.body.classList])r.startsWith("dm-custom-")&&document.body.classList.remove(r);n.custom&&document.body.classList.add(n.custom)};const o=g.autoTheme;i(s()),setInterval(()=>i(s()),6e4)}const C=$("#site-navbar");if(C.length&&h.brand){let s=function(m,l=0){return Array.isArray(m)?m.filter(a=>!(a&&(a.type==="spacer"||a.type==="theme-switcher"))).filter(a=>!(a&&a.type==="separator"&&l===0)).map(a=>a&&a.type==="separator"?{divider:!0}:Array.isArray(a?.items)?{...a,items:s(a.items,l+1)}:a):m},i=function(m){return Array.isArray(m)?m.filter(l=>!(l&&(l.type==="separator"||l.type==="spacer"||l.type==="theme-switcher"))):[]};const o={...h.brand},t=o.size&&o.size!=="md"?` navbar-brand-${o.size}`:"";if(o.logo||o.icon||o.tagline){let m="";o.logo?m+=`<img src="${o.logo}" class="navbar-brand-logo" alt="${o.text||""}">`:o.icon&&(m+=`<span data-icon="${o.icon}" style="width:1.1em;height:1.1em;margin-right:.35em;vertical-align:middle;"></span>`),o.text&&(m+=`<span class="navbar-brand-text${t}">${o.text}</span>`),o.tagline&&(m+=`<small class="navbar-brand-tagline">${o.tagline}</small>`),o.html=m}else t&&o.text&&(o.html=`<span class="navbar-brand-text${t}">${o.text}</span>`);const e=h.position==="floating",n=h.orientation==="vertical";Domma.elements.navbar("#site-navbar",{brand:o,items:s(h.items||[]),variant:h.variant||"dark",position:e?"static":h.position||"sticky",collapsible:!0,appearOnHover:!!h.appearOnHover}),Domma.icons.scan("#site-navbar");const r=C.get(0);r.classList.add(`dm-nav-align-${Q(h)||"centre"}`),n&&C.get(0).classList.add("dm-nav-vertical",h.side==="right"?"dm-nav-vertical-right":"dm-nav-vertical-left");const y=h.dock||{};n&&!e&&oe(r,h.side==="right"),!n&&y.hideOnScroll&&ne(r),B(r,h),D(r,h.class),e&&(X(C.get(0),h),Z(C.get(0).parentNode||document));const p=r.querySelector(".navbar-toggle"),u=r.querySelector(".navbar-collapse");if(p&&u){let m=null;const l=()=>{u.classList.contains("show")&&p.click()},a=()=>{m&&(m.remove(),m=null),document.body.classList.remove("dm-drawer-open")},d=()=>{m||(m=document.createElement("div"),m.className="dm-navbar-backdrop",m.addEventListener("click",l),document.body.appendChild(m),document.body.classList.add("dm-drawer-open"))};p.addEventListener("click",()=>{requestAnimationFrame(()=>{u.classList.contains("show")?d():a()})}),u.addEventListener("click",c=>{c.target.closest("a")&&requestAnimationFrame(a)}),document.addEventListener("keydown",c=>{c.key==="Escape"&&u.classList.contains("show")&&l()})}const b=r.querySelector(".navbar-nav");if(b){const m=Array.from(b.children);let l=0;for(const a of h.items||[])if(!(a&&a.type==="separator")){if(a&&a.type==="spacer"){const d=document.createElement("li"),c=["xs","sm","md","lg","xl","flex"].includes(a.size)?a.size:"md";d.className=`dm-menu-spacer dm-menu-spacer--${c}`,d.setAttribute("aria-hidden","true"),b.insertBefore(d,m[l]||null);continue}if(a&&a.type==="theme-switcher"){const d=document.createElement("li");d.className="dm-menu-theme-switcher";const c=document.createElement("span");c.dataset.themeSwitcher="menu",c.dataset.position="inline",d.appendChild(c),b.insertBefore(d,m[l]||null);continue}l++}window.CMSThemeSwitcher?.scan(b)}const k=i(h.items||[]);r.querySelectorAll(".navbar-link[data-index], .navbar-dropdown-item[data-index]").forEach(m=>{const l=k[parseInt(m.dataset.index,10)];if(!l)return;const a=m.dataset.subindex!=null,d=a&&Array.isArray(l.items)?l.items[parseInt(m.dataset.subindex,10)]:l;if(!d)return;d.colour&&j(m,d.colour),d.pill&&!a&&U(m,d.pill),G(m,d),D(m.closest("li"),d.class);const c=O(d.badge);c&&m.appendChild(c),R(d.url,location.pathname)&&(m.classList.add("active"),m.setAttribute("aria-current","page"))})}const f=$("#site-footer");if(f.length){let e=function(a){return String(a??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")},n=function(a){return e(a).replace(/'/g,"'")},r=function(a){if(a&&a.type==="separator")return'<li class="footer-sep"><hr></li>';if(a&&a.type==="spacer")return`<li class="dm-menu-spacer dm-menu-spacer--${["xs","sm","md","lg","xl","flex"].includes(a.size)?a.size:"md"}" aria-hidden="true"></li>`;if(a&&a.type==="theme-switcher")return'<li class="dm-menu-theme-switcher"><span data-theme-switcher="menu" data-position="inline"></span></li>';const d=n(a.url||"#"),c=e(a.text||""),A=Array.isArray(a.items)&&a.items.length?`<ul>${a.items.map(r).join("")}</ul>`:"",w=K(a),q=[w.hover&&` data-menu-item-hover="${n(w.hover)}"`,w.active&&` data-menu-item-active="${n(w.active)}"`].filter(Boolean).join(""),I=w.accent?` style="--dm-menu-highlight:${n(w.accent)}"`:"",L=P(a.class).join(" ");return`<li${L?` class="${n(L)}"`:""}><a href="${d}"${q}${I}>${c}</a>${A}</li>`},y=function(a,d){return(a||[]).forEach(c=>{c&&(c.type==="separator"||c.type==="spacer"||c.type==="theme-switcher")||(d.push(c),Array.isArray(c.items)&&c.items.length&&y(c.items,d))}),d};const o=g.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 i=window.__CMS_FOOTER__||{primary:[],legal:[],copyright:""};if(i.copyright&&(s+=`<p>${e(i.copyright)}</p>`),i.primary.length&&(s+=`<nav class="footer-nav footer-nav--primary"><ul>${i.primary.map(r).join("")}</ul></nav>`),i.legal.length&&(s+=`<nav class="footer-nav footer-nav--legal"><ul>${i.legal.map(r).join("")}</ul></nav>`),g.footer){const a=Object.keys(t).filter(c=>o[c]),d=P(g.footer.socialClass).join(" ");a.length&&(s+='<div class="footer-social">',a.forEach(c=>{const{label:A,svg:w}=t[c];s+=`<a href="${o[c]}" target="_blank" rel="noopener noreferrer" aria-label="${A}" class="footer-social-link${d?` ${n(d)}`:""}">${w}</a>`}),s+="</div>")}s+="</div>",f.html(s),window.CMSThemeSwitcher?.scan(f.get(0)),D(f.get(0),g.footer?.class);const p=[...y(i.primary,[]),...y(i.legal,[])];f.get(0).querySelectorAll(".footer-nav a").forEach((a,d)=>{const c=p[d];if(!c)return;c.colour&&j(a,c.colour);const A=O(c.badge);A&&a.appendChild(A),R(c.url,location.pathname)&&(a.classList.add("is-active"),a.setAttribute("aria-current","page"))}),B(f.get(0).querySelector(".footer-nav--primary"),{highlight:i.primaryHighlight}),B(f.get(0).querySelector(".footer-nav--legal"),{highlight:i.legalHighlight}),D(f.get(0).querySelector(".footer-nav--primary"),i.primaryClass),D(f.get(0).querySelector(".footer-nav--legal"),i.legalClass);const b=g.footer?.showMotionToggle!==!1,k=S.get("reduced_motion"),m=k!==null?!!k:!!(window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches),l=f.get(0).querySelector(".footer-inner");if(b&&l){const a=document.createElement("input");a.type="checkbox",a.className="form-switch-input",a.id="dm-motion-switch",a.checked=m,a.addEventListener("change",function(){S.set("reduced_motion",this.checked),window.location.reload()});const d=document.createElement("span");d.className="form-switch-label",d.textContent="Reduce motion";const c=document.createElement("label");c.className="form-switch footer-motion-switch",c.title="Reduce motion",c.appendChild(a),c.appendChild(d),l.appendChild(c)}}if($("#site-sidebar").length){const o=document.querySelector(".site-content"),t=r=>!r.closest(".hero, .card, .accordion, .tabs, figure, #site-sidebar"),s=r=>o?[...o.querySelectorAll(r)].filter(t):[],i=s("h2").length?s("h2"):s("h3"),e=new Set([...document.querySelectorAll("[id]")].map(r=>r.id)),n=[];for(const r of i){if(!r.id){const y=(r.textContent||"").toLowerCase().trim().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")||"section";let p=y;for(let u=2;e.has(p);u++)p=`${y}-${u}`;e.add(p),r.id=p}n.push({text:r.textContent.trim(),url:`#${r.id}`,section:r.id})}n.length?Domma.elements.sidebar("#site-sidebar",{items:n,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 v=$(".page-body");if(v.length){v.find(".accordion").each(function(){Domma.elements.accordion(this,{allowMultiple:this.dataset.multi==="true"})}),v.find(".tabs").each(function(){Domma.elements.tabs(this)}),v.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)}),v.find(".dm-countdown").each(function(){const o={autoStart:!0},t=String(this.dataset.format||"").trim(),s=String(this.dataset.to||"").trim(),i=/^(\d{4})-(\d{2})-(\d{2})$/.exec(s),e=i?new Date(+i[1],+i[2]-1,+i[3]).getTime():s?new Date(s).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 n=/^dd:/i.test(t);o.format=n?"hh:mm:ss":t||(o.duration>=36e5?"hh:mm:ss":"mm:ss");const r=this,y=u=>String(u).padStart(2,"0"),p=u=>{const b=Math.max(0,Math.floor(u/1e3)),k=r.querySelector(".dm-timer-time");k&&(k.textContent=`${y(Math.floor(b/86400))}:${y(Math.floor(b%86400/3600))}:${y(Math.floor(b%3600/60))}:${y(b%60)}`)};n&&(o.onTick=p),Domma.elements.timer(this,o),n&&p(o.duration||0)}),v.find("[data-tooltip]").each(function(){Domma.elements.tooltip(this,{content:$(this).data("tooltip"),position:$(this).data("tooltip-position")||"top"})}),v.find(".dm-progression").each(function(){const o=Array.from(this.querySelectorAll(".dm-progression-item")).map(i=>{const e={title:i.querySelector(".dm-progression-item-title")?.textContent?.trim()||"",description:i.querySelector(".dm-progression-item-body")?.innerHTML?.trim()||""};return i.dataset.date&&(e.date=i.dataset.date),i.dataset.status&&(e.status=i.dataset.status),i.dataset.entryId&&(e._entryId=i.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,...o.length?{items:o}:{}});o.length&&(this._dmCtx={instance:s,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",s=o.dataset.revealAnimation||"slide-up",i=parseInt(o.dataset.revealDuration,10)||400,e=parseInt(o.dataset.revealStagger,10)||60,n=parseInt(o.dataset.revealDelay,10)||0,r=o.dataset.revealDirection==="rtl",y=Array.from(o.children),p={"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)"};y.forEach((u,b)=>{u.style.opacity="0",u.style.transform=p[s]||"",u.style.transition=`opacity ${i}ms ease, transform ${i}ms ease`;const k=r?y.length-1-b:b;u.style.transitionDelay=t==="stagger"?`${n+k*e}ms`:`${n}ms`}),requestAnimationFrame(()=>requestAnimationFrame(()=>{const u=new IntersectionObserver(b=>{b.forEach(k=>{k.isIntersecting&&(k.target.offsetWidth,k.target.style.opacity="1",k.target.style.transform="none",u.unobserve(k.target))})},{threshold:.1});y.forEach(b=>u.observe(b))}))}),v.find(".card[data-collapsible]").each(function(){const o=this.querySelector(".card-header");o&&o.addEventListener("click",()=>this.classList.toggle("is-collapsed"))}),v.find(".dm-so-trigger").each(function(){this.addEventListener("click",()=>{const o=this.dataset.soTarget,t=document.getElementById(o);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 o=Object.assign({},window.__CMS_DCONFIG__||{});if(document.querySelectorAll(".dm-page-config[data-config]").forEach(t=>{try{const s=N(t.dataset.config);Object.assign(o,s)}catch{}}),Object.keys(o).length>0){const t={};for(const[s,i]of Object.entries(o)){const e=i?.events?.click,{confirm:n,toast:r,alert:y,prompt:p,...u}=e||{};n||r||y||p?($(s).on("click",async function(k){if(k.preventDefault(),n&&!await E.confirm(n))return;let m=null;if(!(p&&(m=await E.prompt(p,{inputPlaceholder:u.promptPlaceholder||"",inputValue:u.promptDefault||""}),m===null))){if(u.target){const l=$(u.target);u.toggleClass&&l.toggleClass(u.toggleClass),u.addClass&&l.addClass(u.addClass),u.removeClass&&l.removeClass(u.removeClass),m!==null&&(u.setText&&l.text(m),u.setVal&&l.val(m),u.setAttr&&l.attr(u.setAttr,m))}u.href&&(window.location.href=u.href),r&&E.toast(r,{type:u.toastType||"success"}),y&&E.alert(y)}}),Object.keys(u).length&&(t[s]={...i,events:{...i.events,click:u}})):t[s]=i}$.setup(t)}}v.length&&M(v.get(0))}),window.wireCTAButtons=M;function M(h){h.querySelectorAll(".dm-cta-trigger").forEach(g=>{g.__ctaWired||(g.__ctaWired=!0,g.addEventListener("click",async()=>{const C=g.dataset.action,f=g.dataset.entry,x=g.dataset.confirm;let v=S.get("auth_token");if(!v){E.toast("Please log in to perform this action.",{type:"warning"});return}if(x&&!await E.confirm(x))return;const o=Array.from(g.childNodes).map(s=>s.cloneNode(!0));g.disabled=!0,g.textContent="Running\u2026";const t=s=>fetch(`/api/actions/${C}/public`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify(g.dataset.scope?{entryId:f,scope:g.dataset.scope}:{entryId:f})});try{let s=await t(v);if(s.status===401){const e=S.get("auth_refresh_token");if(e){const n=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:e})});if(n.ok){const{token:r}=await n.json();S.set("auth_token",r),v=r,s=await t(v)}}}const i=await s.json().catch(()=>({}));if(!s.ok)throw new Error(i.error||i.message||`Error ${s.status}`);E.toast(i.message||"Action completed.",{type:"success"})}catch(s){E.toast(s.message||"Action failed.",{type:"error"})}finally{g.disabled=!1,g.textContent="",o.forEach(s=>g.appendChild(s)),Domma.icons.scan(g)}}))})}(function(){const g=document.querySelectorAll('.dm-collection-hydrate[data-collection-scope="mine"]');if(!g.length)return;const C=S.get("auth_token");function f(v,o){const t=document.createElement("div");t.className="dm-collection-display dm-collection-empty";const s=document.createElement("p");if(o){s.appendChild(document.createTextNode("Please "));const i=document.createElement("a");i.href="/login",i.textContent="sign in",s.appendChild(i),s.appendChild(document.createTextNode(" to view your entries."))}else s.textContent=v;return t.appendChild(s),t}function x(v,o){const t=document.createElement("template");t.innerHTML=o||"",v.replaceWith(t.content)}g.forEach(async v=>{const o=v.dataset.collectionAttrs;if(!o)return;if(!C){v.replaceChildren(f("",!0));return}const t=s=>fetch("/api/collections/render-scope",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify({attrs:o})});try{let s=C,i=await t(s);if(i.status===401){const n=S.get("auth_refresh_token");if(n){const r=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:n})});if(r.ok){const{token:y}=await r.json();S.set("auth_token",y),s=y,i=await t(s)}}}if(!i.ok){const{error:n}=await i.json().catch(()=>({}));v.replaceChildren(f(n||"Unable to load entries.",!1));return}const{html:e}=await i.json();x(v,e),window.DommaCollectionBrowser?.hydrate&&window.DommaCollectionBrowser.hydrate(document)}catch{v.replaceChildren(f("Unable to load entries.",!1))}}),setTimeout(()=>{typeof Domma<"u"&&Domma.icons?.scan&&Domma.icons.scan(),typeof Domma<"u"&&Domma.syntax?.scan&&Domma.syntax.scan(),M(document.body)},100)})(),(function(){const g=document.querySelectorAll("[data-collection-table]");!g.length||typeof T>"u"||!T.create||g.forEach(C=>{let f;try{f=N(C.dataset.payload)}catch{return}const{columns:x,rows:v,search:o,sortable:t,exportable:s,pageSize:i,empty:e,ctaConfig:n,slug:r,exportCfg:y}=f;if(!x?.length)return;if(n){const b=x.findIndex(k=>k.key==="_cta");b!==-1&&(x[b]={key:"_cta",title:"",render:(k,m)=>{const l=document.createElement("button");if(l.className=`btn btn-${n.style||"primary"} dm-cta-trigger`,l.dataset.action=n.action||"",n.scope&&(l.dataset.scope=n.scope),l.dataset.entry=m._entryId||"",n.confirm&&(l.dataset.confirm=n.confirm),n.icon){const a=document.createElement("span");a.dataset.icon=n.icon,l.appendChild(a),l.appendChild(document.createTextNode(" "))}return l.appendChild(document.createTextNode(n.label||"Run")),l}})}const p="col-table-"+Math.random().toString(36).slice(2,7),u=document.createElement("div");u.id=p,C.replaceChildren(u),T.create("#"+p,{data:v,columns:x,search:o,sortable:t,exportable:s,pageSize:i,emptyMessage:e}),n&&M(u),C._dmTable={columns:x,rows:v,slug:r,exportCfg:y,render(b){T.create("#"+p,{data:b,columns:x,search:o,sortable:t,exportable:s,pageSize:i,emptyMessage:e}),n&&M(u)}}})})(),(function(){const g=document.querySelectorAll("[data-form-inline]");if(!g.length||typeof F>"u")return;function C(t){const s=t&&t.logic;return!!(s&&(s.visibility||s.requirement))}function f(t,s){const i={};return(t||[]).forEach(e=>{if(e.type==="page-break"||e.type==="spacer"||!e.name)return;const n=e.type==="chooser"||!!(e.variant&&["select","radio","checkbox-group"].includes(e.type)),r=e.type==="checkbox"?"boolean":e.type==="date"||e.type==="icon"||e.type==="image"?"string":n?"chooser":e.type,y={...e.formConfig||{}};y.span==="full"&&s&&(y.span=s);const p={type:r,required:C(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},...y}};e.minLength!==void 0&&(p.minLength=e.minLength),e.maxLength!==void 0&&(p.maxLength=e.maxLength),e.min!==void 0&&(p.min=e.min),e.max!==void 0&&(p.max=e.max),n&&(e.variant&&(p.variant=e.variant),(e.multiple||e.type==="checkbox-group")&&(p.multiple=!0),e.density&&(p.density=e.density),e.columns&&(p.columns=e.columns),e.accent&&(p.accent=e.accent),e.accentStyle&&(p.accentStyle=e.accentStyle),e.glow&&(p.glow=!0),e.glowColour&&(p.glowColour=e.glowColour),e.shadow&&(p.shadow=e.shadow),e.shadowColour&&(p.shadowColour=e.shadowColour)),i[e.name]=p}),i}function x(t){const s={};return(t||[]).forEach(i=>{if(!(!i.name||i.type==="page-break"||i.type==="spacer")&&(i.type==="select"||i.type==="multiselect")&&i.required){const e=(i.options||[])[0];e&&(s[i.name]=typeof e=="object"?e.value:e)}}),s}function v(t,s){(s||[]).forEach(i=>{if(i.type!=="date"||!i.name)return;const e=t.querySelector(`[name="${i.name}"]`);e&&e.type!=="date"&&(e.type="date")})}function o(t,s,i){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=i?"color:var(--dm-danger);margin-top:.75rem;":"color:var(--dm-success);margin-top:.75rem;"}g.forEach(t=>{let s;try{s=N(t.dataset.formInline)}catch{return}const i=s.fields||[],e=s.settings||{},n=Number(e.columns)===3?3:2,r=e.layout||"grid",y=i.some(l=>l.type==="page-break"),p=async l=>{if(t.dataset.triggerBlocked==="true"){const a=t._formLogicRuntime,d=a&&a.submitBlock?a.submitBlock().reasons:[];o(t,d.join(" ")||"This form cannot be submitted yet.",!0);const c=new Error("Blocked by a form trigger");throw c.formSubmitFailed=!0,c}try{const a=t.querySelector(".fb-form-honeypot"),d=a?.querySelector('input[type="text"]')||null,c=a?.querySelector('[name="_t"]')||null,A=Object.assign({},l);delete A._t,a?.dataset.hpName&&delete A[a.dataset.hpName],d!==null&&(A._hp=d.value),c!==null&&(A._t=c.value);const w=await H.post(`/api/forms/submit/${s.slug}`,A);if(w&&w.error){o(t,w.error,!0);const q=new Error(w.error);throw q.formSubmitFailed=!0,q}if(w?.redirect){window.location.href=w.redirect;return}for(;t.firstChild;)t.removeChild(t.firstChild);o(t,w?.message||e.successMessage||"Thank you for your submission.",!1)}catch(a){throw a.formSubmitFailed||o(t,a.message||"Submission failed. Please try again.",!0),a}},u=Date.now();function b(l){const a=l.querySelector("form");if(!a||a.querySelector(".fb-form-honeypot"))return;const d=document.createElement("div");d.className="fb-form-honeypot",d.setAttribute("aria-hidden","true");const c=document.createElement("input");c.name=a.querySelector('[name="website"]')?"_website":"website",d.dataset.hpName=c.name,c.type="text",c.tabIndex=-1,c.autocomplete="url",c.placeholder="https://",d.appendChild(c);const A=document.createElement("input");A.name="_t",A.type="hidden",A.value=u,d.appendChild(A),a.appendChild(d)}function k(l){if(!window.FormLogicEngine||!i.some(d=>d.logic||Array.isArray(d.triggers)&&d.triggers.length))return null;const a=new window.FormLogicEngine.FormLogicRuntime(s,t,l?{baseValues:l}:void 0);if(a.init(),t._formLogicRuntime=a,t._formLogicObserved)return a;if(t._formLogicObserved=!0,t.parentNode&&typeof MutationObserver<"u"){const d=new MutationObserver(function(c){for(const A of c)for(const w of A.removedNodes)if(w===t||w.nodeType===1&&w.contains&&w.contains(t)){t._formLogicRuntime&&t._formLogicRuntime.destroy(),d.disconnect();return}});d.observe(t.parentNode,{childList:!0,subtree:!1}),t._formLogicObserver=d}return a}let m=!1;if(t.addEventListener("dm-form-end",l=>{const a=l.detail?.validation;if(a&&!a.valid){W(t,a.missing),J(a);return}m||(m=!0,p(l.detail?.values||{}).catch(()=>{}).finally(()=>{m=!1}))}),y&&F.wizard){const l=ee(i,s.title),a=l.map(L=>({title:L.title,description:L.description,fields:f(L.fields,n)})),d=l.map(L=>L.fields);let c=0,A=null,w=null;const q=L=>{const _=window.FormLogicEngine;if(!_||!_.validateFields)return;const z=_.validateFields(t,d[c]||[]);z.valid||(L.preventDefault(),L.stopImmediatePropagation(),W(t,z.missing),J(z))},I=()=>{const L=d[c]||i;ae(t,e),Y(t,e,L),v(t,L),V(t,L),e.honeypot!==!1&&b(t),A&&A.destroy(),A=k(w?.getStepData?w.getStepData():void 0),t.querySelectorAll(".wizard-next, .wizard-submit").forEach(_=>_.addEventListener("click",q,!0))};w=F.wizard(t,{schema:{steps:a},layout:r,columns:n,finishText:e.submitText||"Finish",onSubmit:p,onStepChange:L=>{c=L,setTimeout(I,0)}}),t.addEventListener("dm-form-trigger",L=>{if(L.detail?.type!=="goto-step"||!w?.goToStep)return;const _=Number(L.detail.action.step);!Number.isFinite(_)||_<0||_>=a.length||_!==c&&w.goToStep(_)}),Promise.resolve(w).then(I)}else if(F.render){const l=F.render(t,f(i,n),x(i),{submitText:e.submitText||"Submit",layout:r,columns:n,onSubmit:p});Promise.resolve(l).then(function(){te(t,e,r),Y(t,e,i),v(t,i),V(t,i),e.honeypot!==!1&&b(t),k()})}}),$(document).on("click",".dm-banner__dismiss",function(){$(this).closest(".dm-banner").remove()})})();function oe(h,g){const C=document.body,f=C.classList,x=new Set((C.dataset.railSite||"").split(" ").filter(Boolean)),v=()=>window.matchMedia("(min-width: 993px)").matches;h.querySelectorAll(".navbar-link, .navbar-dropdown-toggle").forEach(n=>{const r=n.textContent.trim();r&&(n.setAttribute("title",r),n.querySelector("svg, [data-icon]")||(n.dataset.initial=r.charAt(0).toUpperCase()))});const o=document.createElement("button");o.type="button",o.className="dm-nav-collapse-toggle";const t=()=>{const n=f.contains("dm-vnav-collapsed");o.setAttribute("aria-pressed",String(n)),o.setAttribute("aria-label",n?"Expand menu":"Collapse menu"),o.title=o.getAttribute("aria-label");const r=g?"chevrons-left":"chevrons-right",y=g?"chevrons-right":"chevrons-left";o.innerHTML=`<span data-icon="${n?r:y}"></span>`,window.Domma&&Domma.icons&&Domma.icons.scan(o)},s=n=>{f.toggle("dm-vnav-collapsed",n),S.set("nav_collapsed",n),t()};o.addEventListener("click",()=>s(!f.contains("dm-vnav-collapsed"))),t();const i=h.querySelector(".navbar-container")||h;i.insertBefore(o,i.firstChild?.nextSibling||null);const e=n=>{f.toggle("dm-vnav-autohide",n),f.toggle("dm-vnav-collapsible",!n&&x.has("collapsible")),n?f.remove("dm-vnav-collapsed"):x.has("collapsible")&&S.get("nav_collapsed")===!0&&f.add("dm-vnav-collapsed"),h.classList.remove("dm-nav-revealed"),t()};h.addEventListener("pointerdown",n=>{f.contains("dm-vnav-autohide")&&n.pointerType!=="mouse"&&!h.classList.contains("dm-nav-revealed")&&(h.classList.add("dm-nav-revealed"),n.preventDefault())}),document.addEventListener("pointerdown",n=>{h.contains(n.target)||h.classList.remove("dm-nav-revealed")}),window.E&&typeof E.contextMenu=="function"&&E.contextMenu(h,{items:()=>{const n=f.contains("dm-vnav-autohide"),r=f.contains("dm-vnav-collapsed"),y=S.has("nav_autohide")||S.has("nav_collapsed");return[{header:"Menu"},{label:r?"Expand menu":"Collapse to icons",icon:r?"maximize":"minimize",disabled:!v()||n||!x.has("collapsible"),action:()=>s(!r)},{label:n?"Keep on screen":"Hide when not in use",icon:n?"eye":"eye-off",disabled:!v(),action:()=>{S.set("nav_autohide",!n),e(!n)}},{type:"divider"},{label:"Reset to the site's setting",icon:"refresh",disabled:!y,action:()=>{S.remove("nav_autohide"),S.remove("nav_collapsed"),e(x.has("autohide")),f.toggle("dm-vnav-collapsed",!x.has("autohide")&&x.has("start-collapsed")),t()}}]}})}function ne(h){h.classList.add("dm-nav-scroll-hide");let g=window.scrollY,C=!1;window.addEventListener("scroll",()=>{C||(C=!0,requestAnimationFrame(()=>{C=!1;const f=window.scrollY,x=h.matches(":focus-within")||h.querySelector(".navbar-dropdown.open, .navbar-collapse.show");f>g&&f>h.offsetHeight&&!x?h.classList.add("dm-nav-scrolled-away"):f<g&&h.classList.remove("dm-nav-scrolled-away"),g=f}))},{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,17 @@ 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
|
+
|
|
131
|
+
/** The site's reduced-motion default for visitors who have not chosen: 'system', 'off' or 'on'. */
|
|
132
|
+
function motionDefaultOf(site) {
|
|
133
|
+
const d = site?.footer?.motionDefault;
|
|
134
|
+
return d === 'off' || d === 'on' ? d : 'system';
|
|
135
|
+
}
|
|
136
|
+
|
|
125
137
|
/** The override a page's [navbar] tag left in its html (the first one), or null. */
|
|
126
138
|
function readNavbarOverride(html) {
|
|
127
139
|
const m = /data-navbar-override="([^"]*)"/.exec(html || '');
|
|
@@ -303,6 +315,7 @@ export async function renderPage(page, opts = {}) {
|
|
|
303
315
|
: (await readLegacyNavBackup()) || {brand: site.brand || {}, items: []};
|
|
304
316
|
|
|
305
317
|
const preset = presets[page.layout] || presets['default'] || {};
|
|
318
|
+
const pageContents = resolveContents(preset, page, readContentsOverride(page.html));
|
|
306
319
|
|
|
307
320
|
// Per-page `width:` frontmatter overrides the preset's width when valid.
|
|
308
321
|
const pageWidth = VALID_LAYOUT_WIDTHS.has(page.width) ? page.width : null;
|
|
@@ -418,11 +431,17 @@ export async function renderPage(page, opts = {}) {
|
|
|
418
431
|
layoutBodyClass,
|
|
419
432
|
pageBodyStyle,
|
|
420
433
|
navbarClass,
|
|
434
|
+
motionDefault: motionDefaultOf(site),
|
|
421
435
|
railDocked: /\bdm-vnav\b/.test(verticalNavClass),
|
|
422
436
|
showNavbar: preset.navbar !== false,
|
|
423
437
|
showFooter: preset.footer !== false,
|
|
424
|
-
|
|
425
|
-
|
|
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,
|
|
426
445
|
navJson: JSON.stringify(filterHiddenNavItems(navigation)).replace(/<\/script>/gi, '<\\/script>'),
|
|
427
446
|
footerScript: footerJson ? `window.__CMS_FOOTER__ = ${footerJson};` : '',
|
|
428
447
|
ctxMenusScript: ctxMenusScript,
|
|
@@ -1161,6 +1180,7 @@ export async function renderBlogPage(templatePath, data = {}, seoMeta = {}) {
|
|
|
1161
1180
|
? railBodyClass(navigation)
|
|
1162
1181
|
: '', ...cssClasses(site.bodyClass)].filter(Boolean).join(' '),
|
|
1163
1182
|
pageBodyStyle: '',
|
|
1183
|
+
motionDefault: motionDefaultOf(site),
|
|
1164
1184
|
railDocked: navigation.orientation === 'vertical' && /\bdm-vnav\b/.test(railBodyClass(navigation)),
|
|
1165
1185
|
showNavbar: true,
|
|
1166
1186
|
showFooter: true,
|
|
@@ -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">
|
|
@@ -98,6 +108,13 @@
|
|
|
98
108
|
_stored = JSON.parse(localStorage.getItem('domma:reduced_motion'));
|
|
99
109
|
} catch (e) {
|
|
100
110
|
}
|
|
111
|
+
// No choice of the visitor's yet: the site's default (Site Settings > Footer) -
|
|
112
|
+
// 'on' or 'off' decide it; 'system' (or nothing) leaves it to the device.
|
|
113
|
+
if (_stored === null || _stored === undefined) {
|
|
114
|
+
var _def = '{{motionDefault}}';
|
|
115
|
+
if (_def === 'on') _stored = true;
|
|
116
|
+
else if (_def === 'off') _stored = false;
|
|
117
|
+
}
|
|
101
118
|
if (_stored === true) {
|
|
102
119
|
document.documentElement.classList.add('dm-reduced-motion');
|
|
103
120
|
}
|
|
@@ -156,7 +173,7 @@
|
|
|
156
173
|
</script>
|
|
157
174
|
|
|
158
175
|
<!-- Site initialisation -->
|
|
159
|
-
<script src="/public/js/site.js?v=20261011-
|
|
176
|
+
<script src="/public/js/site.js?v=20261011-page-contents" type="module"></script>
|
|
160
177
|
<script src="/public/js/collection-context.js?v=20261010-utf8-payloads" type="module"></script>
|
|
161
178
|
{{ctxMenusModule}}
|
|
162
179
|
|