openvibers/openvibe.shared frame.js
frame.js
| 1 | ; |
| 2 | // openvibe-shared/frame — the OpenVibe Frame (the navbar, footer and "shipped" views every OpenVibe site sits |
| 3 | // in): the parts that can be baked into HTML on the server. (Formerly openvibe-shared/chrome-ssr; the alias was removed in 2.0.0.) |
| 4 | // footer(cfg) the full shared footer (HTML + CSS); the browser's footer.js renders into the same element |
| 5 | // noscriptNav(site) a plain navigation bar inside <noscript>: the JavaScript navbar cannot exist without |
| 6 | // scripts, so visitors who block them (and text browsers) still get the brand and the network |
| 7 | // shipped(opts) a home page's "shipped" block: the "🚀 shipped X ago" pill and the recent changes, |
| 8 | // filled by shipped.js; without JavaScript, a link to the update log |
| 9 | // updatesBody(opts) the body of a site's /updates page (the full log; openvibe.network's server-rendered |
| 10 | // log is the no-JavaScript fallback) |
| 11 | // shippedScript() the <script> tag for shipped.js (the footer loads it lazily too; a page that shows the |
| 12 | // log should load it itself) |
| 13 | const footerModule = require('./footer'); |
| 14 | const navbarModule = require('./navbar'); |
| 15 | |
| 16 | const SITES = [['Live', 'https://openvibe.live/'], ['Tools', 'https://openvibe.tools/'], ['Community', 'https://openvibe.community/'], ['Games', 'https://openvibe.games/'], ['Media', 'https://openvibe.media/'], ['Network', 'https://openvibe.network/']]; |
| 17 | const esc = (s) => String(s == null ? '' : s).replace(/[&<>"']/g, c => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c])); |
| 18 | |
| 19 | function noscriptNav({ name, home = '/', links = [] } = {}) { |
| 20 | const own = links.map(l => `<a href="${esc(l.href)}">${esc(l.label)}</a>`).join(''); |
| 21 | const net = SITES.filter(([n]) => !String(name || '').endsWith('.' + n)).map(([n, u]) => `<a href="${u}">${n}</a>`).join(''); |
| 22 | return `<noscript><nav aria-label="Site" style="display:flex;flex-wrap:wrap;align-items:center;gap:6px 16px;padding:12px 20px;border-bottom:1px solid rgba(255,255,255,.1);font:500 14px/1.4 system-ui,sans-serif"><a href="${esc(home)}" style="font-weight:700;color:inherit;text-decoration:none">${esc(name || 'OpenVibe')}</a>${own}<span style="flex:1"></span>${net}</nav></noscript>`; |
| 23 | } |
| 24 | |
| 25 | // The client navbar replaces this node on its first render. Keeping configured links in the same |
| 26 | // mount makes the page useful to crawlers before any JavaScript runs. |
| 27 | function serverNav({ name, service, home = '/', links } = {}) { |
| 28 | const source = Array.isArray(links) ? links : navbarModule._defaults.linksFor(service); |
| 29 | const items = source.filter(l => l && (l.href || l.url) && (l.label || l.name)); |
| 30 | const own = items.map(l => `<a data-ov-nav-link="true" href="${esc(l.href || l.url)}">${esc(l.label || l.name)}</a>`).join(''); |
| 31 | const brand = `<a href="${esc(home)}" style="font-weight:700;color:inherit;text-decoration:none">${esc(name || 'OpenVibe')}</a>`; |
| 32 | return `<div id="navbar-mount"><nav class="openvibe-navbar" aria-label="Site" data-ov-server-nav="true" style="box-sizing:border-box;display:flex;align-items:center;gap:8px;padding:0 16px;min-height:52px;font:500 14px/1.4 system-ui,sans-serif">${brand}${own}</nav></div>`; |
| 33 | } |
| 34 | |
| 35 | const NETWORK = 'https://openvibe.network'; |
| 36 | const SERVICE_RE = /^[a-z][a-z0-9-]{1,39}$/; |
| 37 | const svc = (s) => (SERVICE_RE.test(String(s || '')) ? String(s) : ''); |
| 38 | |
| 39 | function shipped({ service, updates = '/updates', title = null, limit = 5 } = {}) { |
| 40 | const id = svc(service); |
| 41 | const n = Math.min(Math.max(parseInt(limit, 10) || 5, 1), 20); |
| 42 | return `<section class="ov-shipped-home" aria-label="What shipped">` |
| 43 | + `<a data-ov-shipped="latest" data-service="${esc(id)}" href="${esc(updates)}" hidden></a>` |
| 44 | + `<div data-ov-shipped="list" data-service="${esc(id)}" data-limit="${n}" data-more="${esc(updates)}"${title ? ` data-title="${esc(title)}"` : ''} hidden></div>` |
| 45 | + `<noscript><p><a href="${esc(updates)}">What shipped recently</a></p></noscript>` |
| 46 | + `</section>`; |
| 47 | } |
| 48 | |
| 49 | function updatesBody({ service, siteName = null, base = NETWORK } = {}) { |
| 50 | const id = svc(service); |
| 51 | const name = siteName || (id ? `OpenVibe.${id.charAt(0).toUpperCase()}${id.slice(1)}` : 'OpenVibe'); |
| 52 | const network = `${String(base).replace(/\/+$/, '')}/updates${id ? `?site=${encodeURIComponent(id)}` : ''}`; |
| 53 | return `<section class="ov-updates">` |
| 54 | + `<h1>What shipped on ${esc(name)}</h1>` |
| 55 | + `<p class="ov-updates-lede">Every change deployed to ${esc(name)}, newest first. Each line is a commit from the OpenVibers repositories, linked to the change itself; batches become <a href="https://openvibe.blog/@openvibe">Patch notes on openvibe.blog</a>. Switch to <a href="${esc(String(base).replace(/\/+$/, ''))}/updates">the whole network</a>.</p>` |
| 56 | + `<div data-ov-shipped="log" data-service="${esc(id)}" data-limit="50"><noscript><p>The update log needs JavaScript here; <a href="${esc(network)}">the same log on openvibe.network</a> works without it.</p></noscript></div>` |
| 57 | + `</section>`; |
| 58 | } |
| 59 | |
| 60 | // shipped.js by its content hash (4.138.0), so an updates page never runs a copy an edge kept from before a release. |
| 61 | let _shippedRev = null; |
| 62 | function shippedRev() { |
| 63 | if (_shippedRev === null) { |
| 64 | try { _shippedRev = require('./assets').hashAsset(require('fs').readFileSync(require('path').join(__dirname, 'shipped.js'))); } catch { _shippedRev = ''; } |
| 65 | } |
| 66 | return _shippedRev; |
| 67 | } |
| 68 | function shippedScript({ base = NETWORK } = {}) { |
| 69 | const v = shippedRev(); |
| 70 | return `<script src="${esc(String(base).replace(/\/+$/, ''))}/shared/shipped.js${v ? `?v=${v}` : ''}" defer></script>`; |
| 71 | } |
| 72 | |
| 73 | module.exports = { footer: (cfg) => footerModule.ssr(cfg), noscriptNav, serverNav, shipped, updatesBody, shippedScript }; |
| 74 |