domma-cms 1.6.12 → 1.6.14
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/menu-editor-arrange.js +2 -2
- package/admin/js/lib/shortcode-context-menu.js +2 -2
- package/admin/js/lib/shortcode-registry.js +1 -1
- package/admin/js/lib/site-settings-arrange.js +1 -1
- package/admin/js/templates/menu-editor.html +17 -0
- package/admin/js/templates/settings.html +17 -0
- package/admin/js/views/menu-editor.js +16 -16
- package/package.json +1 -1
- package/public/css/site.css +1 -1
- package/public/js/site.js +1 -1
- package/server/services/markdown.js +26 -2
- package/server/services/menus.js +118 -0
- package/server/services/renderer.js +55 -7
- package/server/templates/page.html +28 -2
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 Y,applyFloat as G,applyHighlight as B,applyItemHighlight as U,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 X,applySubmitButtons as te,applyWizardButtons as ae}from"/public/js/form-submit.mjs";$(()=>{const x=window.__CMS_NAV__||{},g=window.__CMS_SITE__||{};if(g.autoTheme?.enabled){let t=function(e){const r=(e||"07:00").split(":");return+r[0]*60+(+r[1]||0)},n=function(){const e=new Date,r=e.getHours()*60+e.getMinutes();return r>=t(o.dayStart)&&r<t(o.nightStart)?o.dayTheme:o.nightTheme},s=function(e){const r=o.apply&&o.apply[e]||{theme:e,custom:""};Domma.theme.set(r.theme);for(const d of[...document.body.classList])d.startsWith("dm-custom-")&&document.body.classList.remove(d);r.custom&&document.body.classList.add(r.custom)};const o=g.autoTheme;s(n()),setInterval(()=>s(n()),6e4)}const A=$("#site-navbar");if(A.length&&x.brand){let n=function(c,f=0){return Array.isArray(c)?c.filter(i=>!(i&&(i.type==="spacer"||i.type==="theme-switcher"))).filter(i=>!(i&&i.type==="separator"&&f===0)).map(i=>i&&i.type==="separator"?{divider:!0}:Array.isArray(i?.items)?{...i,items:n(i.items,f+1)}:i):c},s=function(c){return Array.isArray(c)?c.filter(f=>!(f&&(f.type==="separator"||f.type==="spacer"||f.type==="theme-switcher"))):[]};const o={...x.brand},t=o.size&&o.size!=="md"?` navbar-brand-${o.size}`:"";if(o.logo||o.icon||o.tagline){let c="";o.logo?c+=`<img src="${o.logo}" class="navbar-brand-logo" alt="${o.text||""}">`:o.icon&&(c+=`<span data-icon="${o.icon}" style="width:1.1em;height:1.1em;margin-right:.35em;vertical-align:middle;"></span>`),o.text&&(c+=`<span class="navbar-brand-text${t}">${o.text}</span>`),o.tagline&&(c+=`<small class="navbar-brand-tagline">${o.tagline}</small>`),o.html=c}else t&&o.text&&(o.html=`<span class="navbar-brand-text${t}">${o.text}</span>`);const e=x.position==="floating",r=x.orientation==="vertical";Domma.elements.navbar("#site-navbar",{brand:o,items:n(x.items||[]),variant:x.variant||"dark",position:e?"static":x.position||"sticky",collapsible:!0,appearOnHover:!!x.appearOnHover}),Domma.icons.scan("#site-navbar");const d=A.get(0);d.classList.add(`dm-nav-align-${Q(x)||"centre"}`),r&&A.get(0).classList.add("dm-nav-vertical",x.side==="right"?"dm-nav-vertical-right":"dm-nav-vertical-left"),B(d,x),D(d,x.class),e&&(G(A.get(0),x),Z(A.get(0).parentNode||document));const h=d.querySelector(".navbar-toggle"),p=d.querySelector(".navbar-collapse");if(h&&p){let c=null;const f=()=>{p.classList.contains("show")&&h.click()},i=()=>{c&&(c.remove(),c=null),document.body.classList.remove("dm-drawer-open")},a=()=>{c||(c=document.createElement("div"),c.className="dm-navbar-backdrop",c.addEventListener("click",f),document.body.appendChild(c),document.body.classList.add("dm-drawer-open"))};h.addEventListener("click",()=>{requestAnimationFrame(()=>{p.classList.contains("show")?a():i()})}),p.addEventListener("click",l=>{l.target.closest("a")&&requestAnimationFrame(i)}),document.addEventListener("keydown",l=>{l.key==="Escape"&&p.classList.contains("show")&&f()})}const m=d.querySelector(".navbar-nav");if(m){const c=Array.from(m.children);let f=0;for(const i of x.items||[])if(!(i&&i.type==="separator")){if(i&&i.type==="spacer"){const a=document.createElement("li"),l=["xs","sm","md","lg","xl","flex"].includes(i.size)?i.size:"md";a.className=`dm-menu-spacer dm-menu-spacer--${l}`,a.setAttribute("aria-hidden","true"),m.insertBefore(a,c[f]||null);continue}if(i&&i.type==="theme-switcher"){const a=document.createElement("li");a.className="dm-menu-theme-switcher";const l=document.createElement("span");l.dataset.themeSwitcher="menu",l.dataset.position="inline",a.appendChild(l),m.insertBefore(a,c[f]||null);continue}f++}window.CMSThemeSwitcher?.scan(m)}const b=s(x.items||[]);d.querySelectorAll(".navbar-link[data-index], .navbar-dropdown-item[data-index]").forEach(c=>{const f=b[parseInt(c.dataset.index,10)];if(!f)return;const i=c.dataset.subindex!=null,a=i&&Array.isArray(f.items)?f.items[parseInt(c.dataset.subindex,10)]:f;if(!a)return;a.colour&&j(c,a.colour),a.pill&&!i&&Y(c,a.pill),U(c,a),D(c.closest("li"),a.class);const l=O(a.badge);l&&c.appendChild(l),R(a.url,location.pathname)&&(c.classList.add("active"),c.setAttribute("aria-current","page"))})}const w=$("#site-footer");if(w.length){let e=function(a){return String(a??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")},r=function(a){return e(a).replace(/'/g,"'")},d=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 l=r(a.url||"#"),u=e(a.text||""),C=Array.isArray(a.items)&&a.items.length?`<ul>${a.items.map(d).join("")}</ul>`:"",v=K(a),q=[v.hover&&` data-menu-item-hover="${r(v.hover)}"`,v.active&&` data-menu-item-active="${r(v.active)}"`].filter(Boolean).join(""),M=v.accent?` style="--dm-menu-highlight:${r(v.accent)}"`:"",k=P(a.class).join(" ");return`<li${k?` class="${r(k)}"`:""}><a href="${l}"${q}${M}>${u}</a>${C}</li>`},h=function(a,l){return(a||[]).forEach(u=>{u&&(u.type==="separator"||u.type==="spacer"||u.type==="theme-switcher")||(l.push(u),Array.isArray(u.items)&&u.items.length&&h(u.items,l))}),l};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 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(d).join("")}</ul></nav>`),s.legal.length&&(n+=`<nav class="footer-nav footer-nav--legal"><ul>${s.legal.map(d).join("")}</ul></nav>`),g.footer){const a=Object.keys(t).filter(u=>o[u]),l=P(g.footer.socialClass).join(" ");a.length&&(n+='<div class="footer-social">',a.forEach(u=>{const{label:C,svg:v}=t[u];n+=`<a href="${o[u]}" target="_blank" rel="noopener noreferrer" aria-label="${C}" class="footer-social-link${l?` ${r(l)}`:""}">${v}</a>`}),n+="</div>")}n+="</div>",w.html(n),window.CMSThemeSwitcher?.scan(w.get(0)),D(w.get(0),g.footer?.class);const p=[...h(s.primary,[]),...h(s.legal,[])];w.get(0).querySelectorAll(".footer-nav a").forEach((a,l)=>{const u=p[l];if(!u)return;u.colour&&j(a,u.colour);const C=O(u.badge);C&&a.appendChild(C),R(u.url,location.pathname)&&(a.classList.add("is-active"),a.setAttribute("aria-current","page"))}),B(w.get(0).querySelector(".footer-nav--primary"),{highlight:s.primaryHighlight}),B(w.get(0).querySelector(".footer-nav--legal"),{highlight:s.legalHighlight}),D(w.get(0).querySelector(".footer-nav--primary"),s.primaryClass),D(w.get(0).querySelector(".footer-nav--legal"),s.legalClass);const b=g.footer?.showMotionToggle!==!1,c=S.get("reduced_motion"),f=c!==null?!!c:!!(window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches),i=w.get(0).querySelector(".footer-inner");if(b&&i){const a=document.createElement("input");a.type="checkbox",a.className="form-switch-input",a.id="dm-motion-switch",a.checked=f,a.addEventListener("change",function(){S.set("reduced_motion",this.checked),window.location.reload()});const l=document.createElement("span");l.className="form-switch-label",l.textContent="Reduce motion";const u=document.createElement("label");u.className="form-switch footer-motion-switch",u.title="Reduce motion",u.appendChild(a),u.appendChild(l),i.appendChild(u)}}if($("#site-sidebar").length){const o=document.querySelector(".site-content"),t=d=>!d.closest(".hero, .card, .accordion, .tabs, figure, #site-sidebar"),n=d=>o?[...o.querySelectorAll(d)].filter(t):[],s=n("h2").length?n("h2"):n("h3"),e=new Set([...document.querySelectorAll("[id]")].map(d=>d.id)),r=[];for(const d of s){if(!d.id){const h=(d.textContent||"").toLowerCase().trim().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")||"section";let p=h;for(let m=2;e.has(p);m++)p=`${h}-${m}`;e.add(p),d.id=p}r.push({text:d.textContent.trim(),url:`#${d.id}`,section:d.id})}r.length?Domma.elements.sidebar("#site-sidebar",{items:r,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 y=$(".page-body");if(y.length){y.find(".accordion").each(function(){Domma.elements.accordion(this,{allowMultiple:this.dataset.multi==="true"})}),y.find(".tabs").each(function(){Domma.elements.tabs(this)}),y.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)}),y.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 r=/^dd:/i.test(t);o.format=r?"hh:mm:ss":t||(o.duration>=36e5?"hh:mm:ss":"mm:ss");const d=this,h=m=>String(m).padStart(2,"0"),p=m=>{const b=Math.max(0,Math.floor(m/1e3)),c=d.querySelector(".dm-timer-time");c&&(c.textContent=`${h(Math.floor(b/86400))}:${h(Math.floor(b%86400/3600))}:${h(Math.floor(b%3600/60))}:${h(b%60)}`)};r&&(o.onTick=p),Domma.elements.timer(this,o),r&&p(o.duration||0)}),y.find("[data-tooltip]").each(function(){Domma.elements.tooltip(this,{content:$(this).data("tooltip"),position:$(this).data("tooltip-position")||"top"})}),y.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,r=parseInt(o.dataset.revealDelay,10)||0,d=o.dataset.revealDirection==="rtl",h=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)"};h.forEach((m,b)=>{m.style.opacity="0",m.style.transform=p[n]||"",m.style.transition=`opacity ${s}ms ease, transform ${s}ms ease`;const c=d?h.length-1-b:b;m.style.transitionDelay=t==="stagger"?`${r+c*e}ms`:`${r}ms`}),requestAnimationFrame(()=>requestAnimationFrame(()=>{const m=new IntersectionObserver(b=>{b.forEach(c=>{c.isIntersecting&&(c.target.offsetWidth,c.target.style.opacity="1",c.target.style.transform="none",m.unobserve(c.target))})},{threshold:.1});h.forEach(b=>m.observe(b))}))}),y.find(".card[data-collapsible]").each(function(){const o=this.querySelector(".card-header");o&&o.addEventListener("click",()=>this.classList.toggle("is-collapsed"))}),y.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:r,toast:d,alert:h,prompt:p,...m}=e||{};r||d||h||p?($(n).on("click",async function(c){if(c.preventDefault(),r&&!await E.confirm(r))return;let f=null;if(!(p&&(f=await E.prompt(p,{inputPlaceholder:m.promptPlaceholder||"",inputValue:m.promptDefault||""}),f===null))){if(m.target){const i=$(m.target);m.toggleClass&&i.toggleClass(m.toggleClass),m.addClass&&i.addClass(m.addClass),m.removeClass&&i.removeClass(m.removeClass),f!==null&&(m.setText&&i.text(f),m.setVal&&i.val(f),m.setAttr&&i.attr(m.setAttr,f))}m.href&&(window.location.href=m.href),d&&E.toast(d,{type:m.toastType||"success"}),h&&E.alert(h)}}),Object.keys(m).length&&(t[n]={...s,events:{...s.events,click:m}})):t[n]=s}$.setup(t)}}y.length&&I(y.get(0))}),window.wireCTAButtons=I;function I(x){x.querySelectorAll(".dm-cta-trigger").forEach(g=>{g.__ctaWired||(g.__ctaWired=!0,g.addEventListener("click",async()=>{const A=g.dataset.action,w=g.dataset.entry,_=g.dataset.confirm;let y=S.get("auth_token");if(!y){E.toast("Please log in to perform this action.",{type:"warning"});return}if(_&&!await E.confirm(_))return;const o=Array.from(g.childNodes).map(n=>n.cloneNode(!0));g.disabled=!0,g.textContent="Running\u2026";const t=n=>fetch(`/api/actions/${A}/public`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${n}`},body:JSON.stringify(g.dataset.scope?{entryId:w,scope:g.dataset.scope}:{entryId:w})});try{let n=await t(y);if(n.status===401){const e=S.get("auth_refresh_token");if(e){const r=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:e})});if(r.ok){const{token:d}=await r.json();S.set("auth_token",d),y=d,n=await t(y)}}}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{g.disabled=!1,g.textContent="",o.forEach(n=>g.appendChild(n)),Domma.icons.scan(g)}}))})}(function(){const g=document.querySelectorAll('.dm-collection-hydrate[data-collection-scope="mine"]');if(!g.length)return;const A=S.get("auth_token");function w(y,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=y;return t.appendChild(n),t}function _(y,o){const t=document.createElement("template");t.innerHTML=o||"",y.replaceWith(t.content)}g.forEach(async y=>{const o=y.dataset.collectionAttrs;if(!o)return;if(!A){y.replaceChildren(w("",!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=A,s=await t(n);if(s.status===401){const r=S.get("auth_refresh_token");if(r){const d=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:r})});if(d.ok){const{token:h}=await d.json();S.set("auth_token",h),n=h,s=await t(n)}}}if(!s.ok){const{error:r}=await s.json().catch(()=>({}));y.replaceChildren(w(r||"Unable to load entries.",!1));return}const{html:e}=await s.json();_(y,e),window.DommaCollectionBrowser?.hydrate&&window.DommaCollectionBrowser.hydrate(document)}catch{y.replaceChildren(w("Unable to load entries.",!1))}}),setTimeout(()=>{typeof Domma<"u"&&Domma.icons?.scan&&Domma.icons.scan(),typeof Domma<"u"&&Domma.syntax?.scan&&Domma.syntax.scan(),I(document.body)},100)})(),(function(){const g=document.querySelectorAll("[data-collection-table]");!g.length||typeof T>"u"||!T.create||g.forEach(A=>{let w;try{w=N(A.dataset.payload)}catch{return}const{columns:_,rows:y,search:o,sortable:t,exportable:n,pageSize:s,empty:e,ctaConfig:r,slug:d,exportCfg:h}=w;if(!_?.length)return;if(r){const b=_.findIndex(c=>c.key==="_cta");b!==-1&&(_[b]={key:"_cta",title:"",render:(c,f)=>{const i=document.createElement("button");if(i.className=`btn btn-${r.style||"primary"} dm-cta-trigger`,i.dataset.action=r.action||"",r.scope&&(i.dataset.scope=r.scope),i.dataset.entry=f._entryId||"",r.confirm&&(i.dataset.confirm=r.confirm),r.icon){const a=document.createElement("span");a.dataset.icon=r.icon,i.appendChild(a),i.appendChild(document.createTextNode(" "))}return i.appendChild(document.createTextNode(r.label||"Run")),i}})}const p="col-table-"+Math.random().toString(36).slice(2,7),m=document.createElement("div");m.id=p,A.replaceChildren(m),T.create("#"+p,{data:y,columns:_,search:o,sortable:t,exportable:n,pageSize:s,emptyMessage:e}),r&&I(m),A._dmTable={columns:_,rows:y,slug:d,exportCfg:h,render(b){T.create("#"+p,{data:b,columns:_,search:o,sortable:t,exportable:n,pageSize:s,emptyMessage:e}),r&&I(m)}}})})(),(function(){const g=document.querySelectorAll("[data-form-inline]");if(!g.length||typeof F>"u")return;function A(t){const n=t&&t.logic;return!!(n&&(n.visibility||n.requirement))}function w(t,n){const s={};return(t||[]).forEach(e=>{if(e.type==="page-break"||e.type==="spacer"||!e.name)return;const r=e.type==="chooser"||!!(e.variant&&["select","radio","checkbox-group"].includes(e.type)),d=e.type==="checkbox"?"boolean":e.type==="date"||e.type==="icon"||e.type==="image"?"string":r?"chooser":e.type,h={...e.formConfig||{}};h.span==="full"&&n&&(h.span=n);const p={type:d,required:A(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},...h}};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),r&&(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)),s[e.name]=p}),s}function _(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 y(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;"}g.forEach(t=>{let n;try{n=N(t.dataset.formInline)}catch{return}const s=n.fields||[],e=n.settings||{},r=Number(e.columns)===3?3:2,d=e.layout||"grid",h=s.some(i=>i.type==="page-break"),p=async i=>{if(t.dataset.triggerBlocked==="true"){const a=t._formLogicRuntime,l=a&&a.submitBlock?a.submitBlock().reasons:[];o(t,l.join(" ")||"This form cannot be submitted yet.",!0);const u=new Error("Blocked by a form trigger");throw u.formSubmitFailed=!0,u}try{const a=t.querySelector(".fb-form-honeypot"),l=a?.querySelector('input[type="text"]')||null,u=a?.querySelector('[name="_t"]')||null,C=Object.assign({},i);delete C._t,a?.dataset.hpName&&delete C[a.dataset.hpName],l!==null&&(C._hp=l.value),u!==null&&(C._t=u.value);const v=await H.post(`/api/forms/submit/${n.slug}`,C);if(v&&v.error){o(t,v.error,!0);const q=new Error(v.error);throw q.formSubmitFailed=!0,q}if(v?.redirect){window.location.href=v.redirect;return}for(;t.firstChild;)t.removeChild(t.firstChild);o(t,v?.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}},m=Date.now();function b(i){const a=i.querySelector("form");if(!a||a.querySelector(".fb-form-honeypot"))return;const l=document.createElement("div");l.className="fb-form-honeypot",l.setAttribute("aria-hidden","true");const u=document.createElement("input");u.name=a.querySelector('[name="website"]')?"_website":"website",l.dataset.hpName=u.name,u.type="text",u.tabIndex=-1,u.autocomplete="url",u.placeholder="https://",l.appendChild(u);const C=document.createElement("input");C.name="_t",C.type="hidden",C.value=m,l.appendChild(C),a.appendChild(l)}function c(i){if(!window.FormLogicEngine||!s.some(l=>l.logic||Array.isArray(l.triggers)&&l.triggers.length))return null;const a=new window.FormLogicEngine.FormLogicRuntime(n,t,i?{baseValues:i}:void 0);if(a.init(),t._formLogicRuntime=a,t._formLogicObserved)return a;if(t._formLogicObserved=!0,t.parentNode&&typeof MutationObserver<"u"){const l=new MutationObserver(function(u){for(const C of u)for(const v of C.removedNodes)if(v===t||v.nodeType===1&&v.contains&&v.contains(t)){t._formLogicRuntime&&t._formLogicRuntime.destroy(),l.disconnect();return}});l.observe(t.parentNode,{childList:!0,subtree:!1}),t._formLogicObserver=l}return a}let f=!1;if(t.addEventListener("dm-form-end",i=>{const a=i.detail?.validation;if(a&&!a.valid){W(t,a.missing),J(a);return}f||(f=!0,p(i.detail?.values||{}).catch(()=>{}).finally(()=>{f=!1}))}),h&&F.wizard){const i=ee(s,n.title),a=i.map(k=>({title:k.title,description:k.description,fields:w(k.fields,r)})),l=i.map(k=>k.fields);let u=0,C=null,v=null;const q=k=>{const L=window.FormLogicEngine;if(!L||!L.validateFields)return;const z=L.validateFields(t,l[u]||[]);z.valid||(k.preventDefault(),k.stopImmediatePropagation(),W(t,z.missing),J(z))},M=()=>{const k=l[u]||s;ae(t,e),X(t,e,k),y(t,k),V(t,k),e.honeypot!==!1&&b(t),C&&C.destroy(),C=c(v?.getStepData?v.getStepData():void 0),t.querySelectorAll(".wizard-next, .wizard-submit").forEach(L=>L.addEventListener("click",q,!0))};v=F.wizard(t,{schema:{steps:a},layout:d,columns:r,finishText:e.submitText||"Finish",onSubmit:p,onStepChange:k=>{u=k,setTimeout(M,0)}}),t.addEventListener("dm-form-trigger",k=>{if(k.detail?.type!=="goto-step"||!v?.goToStep)return;const L=Number(k.detail.action.step);!Number.isFinite(L)||L<0||L>=a.length||L!==u&&v.goToStep(L)}),Promise.resolve(v).then(M)}else if(F.render){const i=F.render(t,w(s,r),_(s),{submitText:e.submitText||"Submit",layout:d,columns:r,onSubmit:p});Promise.resolve(i).then(function(){te(t,e,d),X(t,e,s),y(t,s),V(t,s),e.honeypot!==!1&&b(t),c()})}}),$(document).on("click",".dm-banner__dismiss",function(){$(this).closest(".dm-banner").remove()})})();
|
|
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})}
|
|
@@ -12,7 +12,7 @@ import {fileURLToPath} from 'url';
|
|
|
12
12
|
import {applyTransforms, getSanitizeExtensions, getShortcodeProcessors} from './hooks.js';
|
|
13
13
|
import {getConfig} from '../config.js';
|
|
14
14
|
import {getCollection, listEntries} from './collections.js';
|
|
15
|
-
import {getMenu, resolveLocation, resolveMenuDecorations} from './menus.js';
|
|
15
|
+
import {getMenu, navbarOverrideFromAttrs, resolveLocation, resolveMenuDecorations} from './menus.js';
|
|
16
16
|
import {buildMenuNav} from './menuRender.js';
|
|
17
17
|
import {checkVisibility} from '../middleware/auth.js';
|
|
18
18
|
import {getRoleHierarchy, getRoleLevel} from './roles.js';
|
|
@@ -3618,6 +3618,30 @@ function processSpacerBlocks(markdown) {
|
|
|
3618
3618
|
* @param {string} markdown
|
|
3619
3619
|
* @returns {string}
|
|
3620
3620
|
*/
|
|
3621
|
+
/**
|
|
3622
|
+
* [navbar ... /] - this page's own navbar settings, over the navbar menu's.
|
|
3623
|
+
*
|
|
3624
|
+
* Any Behaviour setting of the menu, as attributes (see NAVBAR_OVERRIDE_ATTRS in
|
|
3625
|
+
* menus.js): position, orientation, side, align, variant, hover-open, hover,
|
|
3626
|
+
* selected, accent, class, font, font-size, font-weight, letter-spacing,
|
|
3627
|
+
* icon-size, width, collapsible, collapsed, autohide, hide-on-scroll and the
|
|
3628
|
+
* float-* panel options. `true` / `false` switch a setting on or off; an empty
|
|
3629
|
+
* value clears it. Leaves a hidden marker the renderer reads (the first tag on
|
|
3630
|
+
* a page counts) - it draws nothing itself.
|
|
3631
|
+
*
|
|
3632
|
+
* [navbar collapsed="true" /]
|
|
3633
|
+
* [navbar autohide="true" width="200" /]
|
|
3634
|
+
* [navbar position="floating" float-anchor="TR" float-collapsible="true" /]
|
|
3635
|
+
*/
|
|
3636
|
+
function processNavbarBlocks(markdown) {
|
|
3637
|
+
const {scrubbed, restore} = scrubCodeRegions(markdown);
|
|
3638
|
+
return restore(scrubbed.replace(/\[navbar(\s[^\]]*?)?\/?\]/gi, (_, attrStr) => {
|
|
3639
|
+
const override = navbarOverrideFromAttrs(parseShortcodeAttrs(attrStr || ''));
|
|
3640
|
+
if (!Object.keys(override).length) return '';
|
|
3641
|
+
return `<span class="dm-navbar-override" hidden data-navbar-override="${escapeAttr(JSON.stringify(override))}"></span>`;
|
|
3642
|
+
}));
|
|
3643
|
+
}
|
|
3644
|
+
|
|
3621
3645
|
export const THEME_SWITCHER_POSITIONS = ['inline', 'top-left', 'top-right', 'left-center', 'right-center', 'bottom-left', 'bottom-right'];
|
|
3622
3646
|
|
|
3623
3647
|
function processThemeSwitcherBlocks(markdown) {
|
|
@@ -4470,7 +4494,7 @@ export async function parseMarkdown(raw, opts = {}) {
|
|
|
4470
4494
|
const withTimeline = processTimelineBlocks(withCountdown);
|
|
4471
4495
|
const withListGroup = processListGroupBlocks(withTimeline);
|
|
4472
4496
|
const withSpacer = processSpacerBlocks(withListGroup);
|
|
4473
|
-
const withSwitcher = processThemeSwitcherBlocks(withSpacer);
|
|
4497
|
+
const withSwitcher = processNavbarBlocks(processThemeSwitcherBlocks(withSpacer));
|
|
4474
4498
|
const withCenter = processCenterBlocks(withSwitcher);
|
|
4475
4499
|
const withIcon = processIconBlocks(withCenter);
|
|
4476
4500
|
const withDemo = processDemoBlocks(withIcon);
|
package/server/services/menus.js
CHANGED
|
@@ -255,6 +255,112 @@ export function spacerSize(item) {
|
|
|
255
255
|
// floating switcher itself is on.
|
|
256
256
|
// ---------------------------------------------------------------------------
|
|
257
257
|
|
|
258
|
+
// ---------------------------------------------------------------------------
|
|
259
|
+
// Docked menu behaviour - `dock: {width, collapsible, collapsed, autohide, hideOnScroll}`.
|
|
260
|
+
//
|
|
261
|
+
// For the menu in the navbar slot when it is docked (not floating):
|
|
262
|
+
// width a vertical rail's width in px (160-480; 240 when unset)
|
|
263
|
+
// collapsible a vertical rail gets a toggle that shrinks it to its icons;
|
|
264
|
+
// the visitor's choice is remembered in their browser
|
|
265
|
+
// collapsed ...and starts that way until they choose
|
|
266
|
+
// autohide a vertical rail slides off-screen, leaving its edge, and comes
|
|
267
|
+
// back when the pointer reaches it (or focus moves into it)
|
|
268
|
+
// hideOnScroll a horizontal bar slides away while scrolling down and comes
|
|
269
|
+
// back on the way up
|
|
270
|
+
// ---------------------------------------------------------------------------
|
|
271
|
+
export const DOCK_WIDTH_MIN = 160;
|
|
272
|
+
export const DOCK_WIDTH_MAX = 480;
|
|
273
|
+
|
|
274
|
+
/** A dock block with only known keys and sane values; null when nothing is set. */
|
|
275
|
+
export function normaliseDock(dock) {
|
|
276
|
+
if (!dock || typeof dock !== 'object' || Array.isArray(dock)) return null;
|
|
277
|
+
const out = {};
|
|
278
|
+
const width = parseInt(dock.width, 10);
|
|
279
|
+
if (Number.isFinite(width)) out.width = Math.min(DOCK_WIDTH_MAX, Math.max(DOCK_WIDTH_MIN, width));
|
|
280
|
+
for (const key of ['collapsible', 'collapsed', 'autohide', 'hideOnScroll']) {
|
|
281
|
+
if (dock[key] === true) out[key] = true;
|
|
282
|
+
}
|
|
283
|
+
if (out.collapsed && !out.collapsible) out.collapsible = true; // starting collapsed needs the toggle back
|
|
284
|
+
return Object.keys(out).length ? out : null;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// ---------------------------------------------------------------------------
|
|
288
|
+
// Per-page overrides - the `[navbar ... /]` tag.
|
|
289
|
+
//
|
|
290
|
+
// A page can change any of its navbar menu's Behaviour settings for itself
|
|
291
|
+
// alone. The tag's attributes map onto the menu's keys below; `true`/`false`
|
|
292
|
+
// switch a setting on or off (so a page can turn off what the menu turns on),
|
|
293
|
+
// and an empty value clears it back to the default. The renderer merges the
|
|
294
|
+
// override over the menu, then validates the result - an override that would
|
|
295
|
+
// not pass validateMenu is dropped whole rather than half-applied.
|
|
296
|
+
// ---------------------------------------------------------------------------
|
|
297
|
+
export const NAVBAR_OVERRIDE_ATTRS = {
|
|
298
|
+
'variant': ['variant'], 'position': ['position'], 'orientation': ['orientation'],
|
|
299
|
+
'align': ['align'], 'side': ['side'], 'hover-open': ['appearOnHover', 'bool'], 'class': ['class'],
|
|
300
|
+
'hover': ['highlight', 'hover'], 'selected': ['highlight', 'active'], 'accent': ['highlight', 'accent'],
|
|
301
|
+
'font': ['style', 'fontFamily'], 'font-size': ['style', 'fontSize'], 'font-weight': ['style', 'fontWeight'],
|
|
302
|
+
'letter-spacing': ['style', 'letterSpacing'], 'icon-size': ['style', 'iconSize'],
|
|
303
|
+
'width': ['dock', 'width', 'num'], 'collapsible': ['dock', 'collapsible', 'bool'],
|
|
304
|
+
'collapsed': ['dock', 'collapsed', 'bool'], 'autohide': ['dock', 'autohide', 'bool'],
|
|
305
|
+
'hide-on-scroll': ['dock', 'hideOnScroll', 'bool'],
|
|
306
|
+
'float-anchor': ['float', 'anchor'], 'float-x': ['float', 'offsetX', 'num'], 'float-y': ['float', 'offsetY', 'num'],
|
|
307
|
+
'float-width': ['float', 'width', 'num'], 'float-radius': ['float', 'radius', 'num'],
|
|
308
|
+
'float-shadow': ['float', 'shadow'], 'float-accent': ['float', 'accent'], 'float-opacity': ['float', 'opacity', 'num'],
|
|
309
|
+
'float-title': ['float', 'title'], 'float-collapsible': ['float', 'collapsible', 'bool'],
|
|
310
|
+
'float-collapsed': ['float', 'collapsed', 'bool'], 'float-movable': ['float', 'movable', 'bool'],
|
|
311
|
+
'float-resizable': ['float', 'resizable', 'bool']
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* The override a tag's attributes describe: `{key: value}` for top-level settings and
|
|
316
|
+
* `{group: {key: value}}` for float / highlight / style / dock. `null` means "clear".
|
|
317
|
+
*/
|
|
318
|
+
export function navbarOverrideFromAttrs(attrs = {}) {
|
|
319
|
+
const out = {};
|
|
320
|
+
for (const [name, raw] of Object.entries(attrs)) {
|
|
321
|
+
const spec = NAVBAR_OVERRIDE_ATTRS[name];
|
|
322
|
+
if (!spec) continue;
|
|
323
|
+
const [group, keyOrKind, kind] = spec;
|
|
324
|
+
const nested = spec.length === 3 || (spec.length === 2 && !['bool', 'num'].includes(keyOrKind));
|
|
325
|
+
const type = nested ? kind : keyOrKind;
|
|
326
|
+
let value = raw === true ? 'true' : String(raw).trim();
|
|
327
|
+
if (value === '') value = null;
|
|
328
|
+
else if (type === 'bool') value = value !== 'false' && value !== '0' && value !== 'no';
|
|
329
|
+
else if (type === 'num') value = Number.isFinite(Number(value)) ? Number(value) : value;
|
|
330
|
+
if (nested) (out[group] ||= {})[keyOrKind] = value;
|
|
331
|
+
else out[group] = value;
|
|
332
|
+
}
|
|
333
|
+
return out;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* A menu with a page's override applied, or the menu as it was when the result would
|
|
338
|
+
* not validate. Groups merge key by key; `null` removes a key, `false` turns a switch off.
|
|
339
|
+
*/
|
|
340
|
+
export function applyMenuOverride(menu, override) {
|
|
341
|
+
if (!menu || !override || !Object.keys(override).length) return menu;
|
|
342
|
+
const next = {...menu};
|
|
343
|
+
for (const [key, value] of Object.entries(override)) {
|
|
344
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
345
|
+
const group = {...(menu[key] || {})};
|
|
346
|
+
for (const [k, v] of Object.entries(value)) {
|
|
347
|
+
if (v === null || v === false) delete group[k]; else group[k] = v;
|
|
348
|
+
}
|
|
349
|
+
if (Object.keys(group).length) next[key] = group; else delete next[key];
|
|
350
|
+
} else if (value === null || value === false) {
|
|
351
|
+
delete next[key];
|
|
352
|
+
} else {
|
|
353
|
+
next[key] = value;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
if (validateMenu(next).length) return menu;
|
|
357
|
+
if (next.dock) {
|
|
358
|
+
const dock = normaliseDock(next.dock);
|
|
359
|
+
if (dock) next.dock = dock; else delete next.dock;
|
|
360
|
+
}
|
|
361
|
+
return next;
|
|
362
|
+
}
|
|
363
|
+
|
|
258
364
|
/** Items that hold no link: they are drawn, not navigated. */
|
|
259
365
|
export const isStructuralItem = (it) => !!it && (it.type === 'separator' || it.type === 'spacer' || it.type === 'theme-switcher');
|
|
260
366
|
|
|
@@ -501,6 +607,7 @@ export async function createMenu(input) {
|
|
|
501
607
|
...(input.float != null && {float: input.float}),
|
|
502
608
|
...(input.highlight != null && {highlight: input.highlight}),
|
|
503
609
|
...(input.style != null && {style: input.style}),
|
|
610
|
+
...(normaliseDock(input.dock) && {dock: normaliseDock(input.dock)}),
|
|
504
611
|
...(binding != null && {binding}),
|
|
505
612
|
items: Array.isArray(input.items) ? input.items : [],
|
|
506
613
|
meta: {
|
|
@@ -546,6 +653,7 @@ export async function updateMenu(slug, input) {
|
|
|
546
653
|
...(input.float != null && {float: input.float}),
|
|
547
654
|
...(input.highlight != null && {highlight: input.highlight}),
|
|
548
655
|
...(input.style != null && {style: input.style}),
|
|
656
|
+
...(normaliseDock(input.dock) && {dock: normaliseDock(input.dock)}),
|
|
549
657
|
...(binding != null && {binding}),
|
|
550
658
|
items: Array.isArray(input.items) ? input.items : [],
|
|
551
659
|
meta: {
|
|
@@ -662,6 +770,16 @@ export function validateMenu(menu) {
|
|
|
662
770
|
if (menu.side != null && !SIDES.has(menu.side)) {
|
|
663
771
|
errors.push(`Invalid side "${menu.side}" - expected one of: ${[...SIDES].join(', ')}`);
|
|
664
772
|
}
|
|
773
|
+
if (menu.dock != null) {
|
|
774
|
+
if (typeof menu.dock !== 'object' || Array.isArray(menu.dock)) {
|
|
775
|
+
errors.push('dock must be an object');
|
|
776
|
+
} else if (menu.dock.width != null && menu.dock.width !== '') {
|
|
777
|
+
const w = Number(menu.dock.width);
|
|
778
|
+
if (!Number.isFinite(w) || w < DOCK_WIDTH_MIN || w > DOCK_WIDTH_MAX) {
|
|
779
|
+
errors.push(`dock.width must be a number of px from ${DOCK_WIDTH_MIN} to ${DOCK_WIDTH_MAX}`);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
}
|
|
665
783
|
if (menu.style != null) {
|
|
666
784
|
if (typeof menu.style !== 'object' || Array.isArray(menu.style)) {
|
|
667
785
|
errors.push('style must be an object');
|
|
@@ -8,7 +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 {hasThemeSwitcherItem, resolveLocation, resolveMenuDecorations, resolveOverlaysForPage} from './menus.js';
|
|
11
|
+
import {applyMenuOverride, hasThemeSwitcherItem, resolveLocation, resolveMenuDecorations, resolveOverlaysForPage} from './menus.js';
|
|
12
12
|
import {buildMenuNav} from './menuRender.js';
|
|
13
13
|
import {getProjectForPage} from './projects.js';
|
|
14
14
|
import {resolveContextMenusForPage} from './contextMenus.js';
|
|
@@ -122,6 +122,23 @@ function buildAnalyticsTag() {
|
|
|
122
122
|
* html (body and overlay menus) and the item lists of its navbar and footers
|
|
123
123
|
* @returns {{headTag: string, bodyTag: string}} Empty strings when nothing needs it
|
|
124
124
|
*/
|
|
125
|
+
/** The site's reduced-motion default for visitors who have not chosen: 'system', 'off' or 'on'. */
|
|
126
|
+
function motionDefaultOf(site) {
|
|
127
|
+
const d = site?.footer?.motionDefault;
|
|
128
|
+
return d === 'off' || d === 'on' ? d : 'system';
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** The override a page's [navbar] tag left in its html (the first one), or null. */
|
|
132
|
+
function readNavbarOverride(html) {
|
|
133
|
+
const m = /data-navbar-override="([^"]*)"/.exec(html || '');
|
|
134
|
+
if (!m) return null;
|
|
135
|
+
try {
|
|
136
|
+
return JSON.parse(m[1].replace(/"/g, '"').replace(/'/g, "'").replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&'));
|
|
137
|
+
} catch {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
125
142
|
function buildThemeSwitcherAssets(themeCfg, page = {}) {
|
|
126
143
|
const sw = themeCfg?.switcher || {};
|
|
127
144
|
const html = page.html || '';
|
|
@@ -235,7 +252,7 @@ export async function renderPage(page, opts = {}) {
|
|
|
235
252
|
project: page.resolvedProject
|
|
236
253
|
|| await getProjectForPage(page.urlPath || page.url || '/', page.project)
|
|
237
254
|
};
|
|
238
|
-
const [template, site, presets, injection, customCss,
|
|
255
|
+
const [template, site, presets, injection, customCss, navMenuOwn, footerPrimary, footerLegal] = await Promise.all([
|
|
239
256
|
getTemplate(),
|
|
240
257
|
Promise.resolve(getConfig('site')),
|
|
241
258
|
Promise.resolve(getConfig('presets')),
|
|
@@ -246,6 +263,8 @@ export async function renderPage(page, opts = {}) {
|
|
|
246
263
|
resolveLocation('footer-legal', opts.user || null, menuCtx)
|
|
247
264
|
]);
|
|
248
265
|
const baseUrl = opts.baseUrl || site.baseUrl || '';
|
|
266
|
+
// The page's own navbar settings ([navbar ... /]) over the menu's, for this page only.
|
|
267
|
+
const navMenu = applyMenuOverride(navMenuOwn, readNavbarOverride(page.html));
|
|
249
268
|
|
|
250
269
|
// Resolve live badge counts (countFrom) before the menus are injected as
|
|
251
270
|
// globals. Static badges/colours/pills pass through unchanged.
|
|
@@ -277,6 +296,9 @@ export async function renderPage(page, opts = {}) {
|
|
|
277
296
|
...(navMenu.align && {align: navMenu.align}),
|
|
278
297
|
...(navMenu.float && {float: navMenu.float}),
|
|
279
298
|
...(navMenu.appearOnHover && {appearOnHover: true}),
|
|
299
|
+
// Docked behaviour (rail width, collapse, autohide, hide on scroll) - site.js
|
|
300
|
+
// wires it; width and autohide also shape the body below, from the first paint.
|
|
301
|
+
...(navMenu.dock && {dock: navMenu.dock}),
|
|
280
302
|
// Item highlighting: the navbar is built in the browser, so the
|
|
281
303
|
// config has to travel to site.js rather than being rendered here.
|
|
282
304
|
...(navMenu.highlight && {highlight: navMenu.highlight}),
|
|
@@ -293,9 +315,7 @@ export async function renderPage(page, opts = {}) {
|
|
|
293
315
|
const layoutWidth = pageWidth || (VALID_LAYOUT_WIDTHS.has(preset.width) ? preset.width : 'normal');
|
|
294
316
|
// A vertical navbar docks as a side rail, so the body needs its gutter from
|
|
295
317
|
// the first paint - stamping the class client-side would shift the layout.
|
|
296
|
-
const verticalNavClass = (navigation
|
|
297
|
-
? `dm-vnav dm-vnav-${navigation.side === 'right' ? 'right' : 'left'}`
|
|
298
|
-
: '';
|
|
318
|
+
const verticalNavClass = railBodyClass(navigation);
|
|
299
319
|
const navbarClass = (navigation.orientation === 'vertical' && navigation.position !== 'floating')
|
|
300
320
|
? `dm-nav-vertical dm-nav-vertical-${navigation.side === 'right' ? 'right' : 'left'}`
|
|
301
321
|
: '';
|
|
@@ -404,6 +424,8 @@ export async function renderPage(page, opts = {}) {
|
|
|
404
424
|
layoutBodyClass,
|
|
405
425
|
pageBodyStyle,
|
|
406
426
|
navbarClass,
|
|
427
|
+
motionDefault: motionDefaultOf(site),
|
|
428
|
+
railDocked: /\bdm-vnav\b/.test(verticalNavClass),
|
|
407
429
|
showNavbar: preset.navbar !== false,
|
|
408
430
|
showFooter: preset.footer !== false,
|
|
409
431
|
showSidebar: preset.sidebar === true || page.sidebar === true,
|
|
@@ -819,10 +841,30 @@ function escapeHtml(str) {
|
|
|
819
841
|
.replace(/"/g, '"');
|
|
820
842
|
}
|
|
821
843
|
|
|
844
|
+
/**
|
|
845
|
+
* The body classes a docked vertical rail needs from the first paint: the gutter
|
|
846
|
+
* on its side, none when it autohides (it slides over the page), and a marker
|
|
847
|
+
* for a collapsible one (page.html's boot script adds the visitor's collapsed
|
|
848
|
+
* state before paint, so the gutter is right straight away).
|
|
849
|
+
*/
|
|
850
|
+
function railBodyClass(navigation) {
|
|
851
|
+
if (navigation.orientation !== 'vertical' || navigation.position === 'floating') return '';
|
|
852
|
+
const dock = navigation.dock || {};
|
|
853
|
+
return [
|
|
854
|
+
'dm-vnav', `dm-vnav-${navigation.side === 'right' ? 'right' : 'left'}`,
|
|
855
|
+
dock.autohide ? 'dm-vnav-autohide' : '',
|
|
856
|
+
dock.collapsible && !dock.autohide ? 'dm-vnav-collapsible' : '',
|
|
857
|
+
dock.collapsed && !dock.autohide ? 'dm-vnav-start-collapsed' : ''
|
|
858
|
+
].filter(Boolean).join(' ');
|
|
859
|
+
}
|
|
860
|
+
|
|
822
861
|
function buildNavbarStyleTag(nav) {
|
|
823
862
|
const s = nav?.style || {};
|
|
863
|
+
// A docked rail's width is a token the rail and the body's gutter both read.
|
|
864
|
+
const railWidth = nav?.orientation === 'vertical' && Number.isFinite(nav?.dock?.width)
|
|
865
|
+
? `:root { --dm-vnav-width: ${Math.round(nav.dock.width)}px; }` : '';
|
|
824
866
|
if (!s.fontFamily && !s.fontSize && !s.fontWeight && !s.letterSpacing && !s.iconSize) {
|
|
825
|
-
return {navbarFontLink: '', navbarStyleTag: ''};
|
|
867
|
+
return {navbarFontLink: '', navbarStyleTag: railWidth ? `<style>${railWidth}</style>` : ''};
|
|
826
868
|
}
|
|
827
869
|
let navbarFontLink = '';
|
|
828
870
|
if (s.fontFamily && s.fontFamily !== 'system-ui' && s.fontFamily !== 'inherit') {
|
|
@@ -843,6 +885,7 @@ function buildNavbarStyleTag(nav) {
|
|
|
843
885
|
const iconSel = '#site-navbar .navbar-link [data-icon], #site-navbar .navbar-link svg, #site-navbar .navbar-dropdown-toggle [data-icon], #site-navbar .navbar-dropdown-toggle svg';
|
|
844
886
|
rules.push(`${iconSel} { width: ${s.iconSize} !important; height: ${s.iconSize} !important; }`);
|
|
845
887
|
}
|
|
888
|
+
if (railWidth) rules.push(railWidth);
|
|
846
889
|
const navbarStyleTag = `<style>${rules.join(' ')}</style>`;
|
|
847
890
|
return {navbarFontLink, navbarStyleTag};
|
|
848
891
|
}
|
|
@@ -1014,6 +1057,9 @@ export async function renderBlogPage(templatePath, data = {}, seoMeta = {}) {
|
|
|
1014
1057
|
...(navMenu.orientation === 'vertical' && {orientation: 'vertical', side: navMenu.side === 'right' ? 'right' : 'left'}),
|
|
1015
1058
|
...(navMenu.align && {align: navMenu.align}),
|
|
1016
1059
|
...(navMenu.appearOnHover && {appearOnHover: true}),
|
|
1060
|
+
// Docked behaviour (rail width, collapse, autohide, hide on scroll) - site.js
|
|
1061
|
+
// wires it; width and autohide also shape the body below, from the first paint.
|
|
1062
|
+
...(navMenu.dock && {dock: navMenu.dock}),
|
|
1017
1063
|
// Item highlighting: the navbar is built in the browser, so the
|
|
1018
1064
|
// config has to travel to site.js rather than being rendered here.
|
|
1019
1065
|
...(navMenu.highlight && {highlight: navMenu.highlight}),
|
|
@@ -1119,9 +1165,11 @@ export async function renderBlogPage(templatePath, data = {}, seoMeta = {}) {
|
|
|
1119
1165
|
fontStyleTag,
|
|
1120
1166
|
layout: 'default',
|
|
1121
1167
|
layoutBodyClass: ['dm-layout-normal', navigation.orientation === 'vertical'
|
|
1122
|
-
?
|
|
1168
|
+
? railBodyClass(navigation)
|
|
1123
1169
|
: '', ...cssClasses(site.bodyClass)].filter(Boolean).join(' '),
|
|
1124
1170
|
pageBodyStyle: '',
|
|
1171
|
+
motionDefault: motionDefaultOf(site),
|
|
1172
|
+
railDocked: navigation.orientation === 'vertical' && /\bdm-vnav\b/.test(railBodyClass(navigation)),
|
|
1125
1173
|
showNavbar: true,
|
|
1126
1174
|
showFooter: true,
|
|
1127
1175
|
showSidebar: false,
|
|
@@ -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-motion-settings">
|
|
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. -->
|
|
@@ -41,6 +41,25 @@
|
|
|
41
41
|
{{headInjectLate}}
|
|
42
42
|
</head>
|
|
43
43
|
<body class="dm-cloaked dm-theme-{{dommaTheme}} {{customThemeClass}} {{layoutBodyClass}}" data-layout="{{layout}}">
|
|
44
|
+
{{#if railDocked}}
|
|
45
|
+
<script>
|
|
46
|
+
// A docked rail: the visitor's own choices - collapsed, or hidden when not in use
|
|
47
|
+
// (the rail's right-click menu sets them) - over the menu's, before the first paint,
|
|
48
|
+
// so the page's gutter is right straight away. site.js owns the controls.
|
|
49
|
+
(function () {
|
|
50
|
+
var b = document.body, cl = b.classList;
|
|
51
|
+
var get = function (k) { try { return JSON.parse(localStorage.getItem('domma:' + k)); } catch (e) { return null; } };
|
|
52
|
+
b.dataset.railSite = ['autohide', 'collapsible', 'start-collapsed']
|
|
53
|
+
.filter(function (k) { return cl.contains('dm-vnav-' + k); }).join(' ');
|
|
54
|
+
var hide = get('nav_autohide');
|
|
55
|
+
if (hide === true) { cl.add('dm-vnav-autohide'); cl.remove('dm-vnav-collapsible'); }
|
|
56
|
+
if (hide === false) { cl.remove('dm-vnav-autohide'); if (b.dataset.railSite.indexOf('collapsible') !== -1) cl.add('dm-vnav-collapsible'); }
|
|
57
|
+
if (!cl.contains('dm-vnav-collapsible')) return;
|
|
58
|
+
var c = get('nav_collapsed');
|
|
59
|
+
if (c === true || (c === null && cl.contains('dm-vnav-start-collapsed'))) cl.add('dm-vnav-collapsed');
|
|
60
|
+
})();
|
|
61
|
+
</script>
|
|
62
|
+
{{/if}}
|
|
44
63
|
|
|
45
64
|
{{#if showNavbar}}
|
|
46
65
|
<nav id="site-navbar" class="{{navbarClass}}"></nav>
|
|
@@ -79,6 +98,13 @@
|
|
|
79
98
|
_stored = JSON.parse(localStorage.getItem('domma:reduced_motion'));
|
|
80
99
|
} catch (e) {
|
|
81
100
|
}
|
|
101
|
+
// No choice of the visitor's yet: the site's default (Site Settings > Footer) -
|
|
102
|
+
// 'on' or 'off' decide it; 'system' (or nothing) leaves it to the device.
|
|
103
|
+
if (_stored === null || _stored === undefined) {
|
|
104
|
+
var _def = '{{motionDefault}}';
|
|
105
|
+
if (_def === 'on') _stored = true;
|
|
106
|
+
else if (_def === 'off') _stored = false;
|
|
107
|
+
}
|
|
82
108
|
if (_stored === true) {
|
|
83
109
|
document.documentElement.classList.add('dm-reduced-motion');
|
|
84
110
|
}
|
|
@@ -137,7 +163,7 @@
|
|
|
137
163
|
</script>
|
|
138
164
|
|
|
139
165
|
<!-- Site initialisation -->
|
|
140
|
-
<script src="/public/js/site.js?v=20261011-
|
|
166
|
+
<script src="/public/js/site.js?v=20261011-motion-settings" type="module"></script>
|
|
141
167
|
<script src="/public/js/collection-context.js?v=20261010-utf8-payloads" type="module"></script>
|
|
142
168
|
{{ctxMenusModule}}
|
|
143
169
|
|