1/* 2 * Stage 32 shared-nav PREVIEW loader â drop-in for the legacy (Drupal) and Next.js sites. 3 * 4 * Adds the new shared nav/footer to a page ONLY when the viewer has opted in with the `?nav=new` 5 * preview flag. Dark by default: with no flag it does NOTHING, so the site keeps its current live 6 * menu untouched. This is the client-side, cache-safe preview from 7 * _docs/20260807-unified-nav/README.md §4a â every cached page stays byte-identical for all users. 8 * 9 * ONE line to add (keep the existing site menu exactly as is): 10 * 11 * <script src="https://backend.stage32.com/nav/v3/s32-nav-preview.js" 12 * data-hide="#site-navbar, #site-footer" defer></script> 13 * 14 * data-hide : CSS selector(s) for the site's CURRENT nav + footer, hidden only while previewing. 15 * data-api : (optional) override the menu API base. Defaults to the production endpoint. 16 * 17 * Turn on: add ?nav=new to any URL (sets a cookie on .stage32.com so it sticks across pages/subdomains). 18 * Turn off: add ?nav=off (or clear cookies). 19 * 20 * Nothing here replaces the live menu. The real cutover (server-rendered, old menu removed) is a 21 * separate, deliberate step â not this preview. 22 */ 23(function () { 24 'use strict' 25 var COOKIE = 's32_nav_preview' 26 var BUNDLE = 'https://backend.stage32.com/nav/v3/s32-nav.js' 27 var DEFAULT_API = 'https://backend.stage32.com/api/v2/nav' 28 var script = document.currentScript 29 30 function qp(name) { 31 var m = location.search.match(new RegExp('[?&]' + name + '=([^&]*)')) 32 return m ? decodeURIComponent(m[1]) : null 33 } 34 function getCookie(name) { 35 var m = document.cookie.match(new RegExp('(?:^|; )' + name + '=([^;]*)')) 36 return m ? m[1] : null 37 } 38 function setCookie(name, val) { 39 var base = name + '=' + val + ';path=/;max-age=' + (60 * 60 * 24 * 30) + ';SameSite=Lax' 40 // Scope to .stage32.com so the preview survives www -> subdomain hops. 41 document.cookie = /(^|\.)stage32\.com$/.test(location.hostname) 42 ? base + ';domain=.stage32.com' : base 43 } 44 45 // Flag -> cookie. ?nav=new opts in, ?nav=off opts out. 46 var flag = qp('nav') 47 if (flag === 'new') setCookie(COOKIE, '1') 48 if (flag === 'off') setCookie(COOKIE, '0') 49 50 // Dark by default: not opted in -> do nothing, leave the live menu alone. 51 if (getCookie(COOKIE) !== '1') return 52 53 var api = (script && script.getAttribute('data-api')) || DEFAULT_API 54 var hide = (script && script.getAttribute('data-hide')) || '' 55 // Optional site-specific preview-only CSS (applied ONLY while previewing). Use it to fix layout 56 // the hidden nav leaves behind, e.g. Legacy: data-css="#wrap{padding-top:0 !important}". 57 var css = (script && script.getAttribute('data-css')) || '' 58 59 function activate() { 60 // Hide the site's current nav/footer + any layout fix (reversible: it's just a <style>). 61 if (hide || css) { 62 var st = document.createElement('style') 63 st.id = 's32-preview-style' 64 st.textContent = (hide ? hide + '{display:none !important}' : '') + css 65 document.head.appendChild(st) 66 } 67 // Load the shared component bundle (classic script: loads cross-origin, no CORS needed). 68 if (!document.getElementById('s32-nav-bundle')) { 69 var b = document.createElement('script') 70 b.id = 's32-nav-bundle'; b.src = BUNDLE; b.async = true 71 document.head.appendChild(b) 72 } 73 function mount() { 74 if (document.getElementById('s32-nav-preview-el')) return 75 var nav = document.createElement('s32-nav') 76 nav.id = 's32-nav-preview-el'; nav.setAttribute('api', api) 77 document.body.insertBefore(nav, document.body.firstChild) 78 var footer = document.createElement('s32-footer') 79 footer.setAttribute('api', api) 80 document.body.appendChild(footer) 81 } 82 if (document.readyState !== 'loading') mount() 83 else document.addEventListener('DOMContentLoaded', mount) 84 } 85 86 // Respect the global kill switch BEFORE touching the page: if the shared component is disabled 87 // (config/nav.php component_enabled=false), do NOTHING â never hide the site's own nav, or a 88 // previewer would be left with no menu at all. Same credentialed fetch the component uses. 89 fetch(api, { credentials: 'include', headers: { Accept: 'application/json' } }) 90 .then(function (r) { return r.ok ? r.json() : null }) 91 .then(function (j) { 92 if (j && j.data && j.data.component_enabled === false) return // killed -> leave live menu alone 93 activate() 94 }) 95 .catch(function () { /* API unreachable: leave the live menu untouched */ }) 96})()
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.