<% // ── Helpers used inside this template ───────────────────────────── function _e(s) { return String(s == null ? '' : s) .replace(/&/g, '&').replace(//g, '>') .replace(/"/g, '"').replace(/'/g, '''); } const safeSite = site || {}; const safeUrlBase = (typeof siteUrlBase !== 'undefined' && siteUrlBase) ? siteUrlBase : ''; const safeAccent = safeSite.accent && /^#[0-9a-fA-F]{6}$/.test(safeSite.accent) ? safeSite.accent : '#c2410c'; const lang = safeSite.language || 'nl'; const ogLocale = safeSite.og_locale || (lang === 'nl' ? 'nl_NL' : (lang.length === 2 ? lang + '_' + lang.toUpperCase() : 'en_US')); const homePath = safeUrlBase + '/'; const isPostPage = bodyClass && bodyClass.indexOf('on-post') >= 0; const isHomePage = bodyClass && bodyClass.indexOf('on-home') >= 0; const isSpecialPg = bodyClass && bodyClass.indexOf('on-special') >= 0; const isAdminPage = bodyClass && bodyClass.indexOf('on-admin') >= 0; // ── via site.title_template ────────────────────────────── // Template: '{title} — {site}'. If pageTitle equals site.title (homepage) we // just use the site title alone, otherwise apply the template. const _siteTitle = safeSite.title || 'Klonkt'; const _rawTitle = pageTitle || _siteTitle; const _tpl = safeSite.title_template || '{title} — {site}'; const _finalTitle = (_rawTitle === _siteTitle) ? _siteTitle : _tpl.replace('{title}', _rawTitle).replace('{site}', _siteTitle); // ── Robots: noindex on listing pages and on per-post override ───── let _shouldIndex = safeSite.robots_index !== 0; if (typeof post !== 'undefined' && post && post.noindex) _shouldIndex = false; // Listing pages (search/tag/type/archive) shouldn't be indexed (dupe content) if (currentPath) { if (/^\/(?:search|tag|type|archive|users|prutter|account|admin)(?:$|\/)/.test(currentPath)) { _shouldIndex = false; } } // Special-flagged views from routes opt out too if (isSpecialPg && (currentPath === '/search' || /^\/(tag|type|archive|users)\//.test(currentPath))) { _shouldIndex = false; } // ── Canonical URL (only when we have one configured) ───────────── let _canonical = null; if (safeSite.canonical) { const _base = safeSite.canonical.replace(/\/+$/, ''); let _path = '/'; if (typeof post !== 'undefined' && post && post.slug) _path = '/' + post.slug; else if (currentPath) _path = currentPath; _canonical = _base + _path; } // ── Social bits (OG/Twitter) ────────────────────────────────────── const _socialTitle = (typeof post !== 'undefined' && post && post.title) ? post.title : _siteTitle; const _socialDescr = (typeof socialDescr !== 'undefined' && socialDescr) ? socialDescr : (safeSite.default_description || safeSite.description || ''); const _socialImage = (typeof socialImage !== 'undefined' && socialImage) ? socialImage : (safeSite.og_image_default || safeSite.default_cover || ''); const _ogType = isPostPage ? 'article' : 'website'; // ── JSON-LD ─────────────────────────────────────────────────────── const _publisher = { '@type': safeSite.schema_type === 'Organization' ? 'Organization' : 'Person', name: safeSite.publisher_name || _siteTitle, url: safeSite.publisher_url || (_canonical ? _canonical.split(/(?<=^[^/]*\/\/[^/]+)\//)[0] + '/' : null), }; if (safeSite.publisher_logo) { _publisher.logo = { '@type': 'ImageObject', url: safeSite.publisher_logo }; } let _jsonLd = null; if (typeof post !== 'undefined' && post && post.slug) { _jsonLd = { '@context': 'https://schema.org', '@type': (post.type === 'foto' || post.type === 'video') ? 'CreativeWork' : 'Article', headline: post.title || _siteTitle, description: _socialDescr, datePublished: post.published_at || post.created_at || new Date().toISOString(), dateModified: post.updated_at || post.published_at || new Date().toISOString(), publisher: _publisher, }; if (_socialImage) _jsonLd.image = _socialImage; if (post.author_username) _jsonLd.author = { '@type': 'Person', name: post.author_username }; if (Array.isArray(post.tags) && post.tags.length) _jsonLd.keywords = post.tags.join(', '); } else if (isHomePage) { _jsonLd = { '@context': 'https://schema.org', '@type': 'WebSite', name: _siteTitle, description: safeSite.description || safeSite.default_description || '', publisher: _publisher, }; } %><!DOCTYPE html> <html lang="<%= _e(lang) %>" data-palette="<%= _e(safeSite.palette || 'sage') %>"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover"> <meta name="color-scheme" content="dark light"> <title><%= _finalTitle %> <% if (safeSite.author) { %><% } %> <% if (_canonical) { %><% } %> <% if (safeSite.google_verification) { %><% } %> <% if (safeSite.bing_verification) { %><% } %> <% if (safeSite.pinterest_verification) { %><% } %> <% if (safeSite.yandex_verification) { %><% } %> <% if (site) { %> <% } %> <% if (_socialImage) { %> <% } %> <% if (_canonical) { %><% } %> <% if (typeof post !== 'undefined' && post && post.published_at) { %> <% if (post.author_username) { %><% } %> <% } %> <% if (safeSite.facebook_app_id) { %><% } %> <% if (_socialImage) { %><% } %> <% if (safeSite.twitter) { %><% } %> <% if (_jsonLd) { %> <% } %> <%- include('partials/shared-styles') %> <% if (safeSite.custom_css) { %> <% } %> <% if (safeSite.custom_head_html) { %> <%- safeSite.custom_head_html %> <% } %> <% if (typeof isViewer !== 'undefined' && isViewer) { %>
Kijker-modus — je kunt alles bekijken, maar niets wijzigen.
<% } %> <%# Site-chrome (topnav + profielkop + view-switcher) in één vaste slot #pcms-chrome. Bij htmx-navigatie wordt dit slot out-of-band ververst (zie chrome.ejs + render.js), zodat de kop ALTIJD bij de nieuwe pagina/artiest hoort terwijl de audioplayer (los in document.body) blijft leven → geen verspringen. Op de hub-landing is het slot leeg: de hero is daar de header. %> <%- include('partials/chrome') %> <%# hx-history-elt scopes HTMX history snapshots to #pcms-main only, so when the user navigates back the switcher (which lives in the shell OUTSIDE this element) keeps its place rather than being replaced along with the rest of the body. %>
<%- pageContent %>
<%- include('partials/footer') %> <% if (!(typeof bodyClass === 'string' && bodyClass.indexOf('on-auth') >= 0)) { %> <%- include('partials/bottom-tab') %> <% } %> <% if (user) { %> <%- include('partials/profile-sheet') %> <% } %> <% if (site && site.enable_audio_player && audioTracks && audioTracks.length > 0) { %> <% } %> <% if (safeSite.custom_foot_html) { %> <%- safeSite.custom_foot_html %> <% } %>