openvibers/openvibe.shared Commits

Shared 2.13.2: the navbar never loads a second ov-mark.js (a page's own async tag that had not run yet) (#32)

OP OpenVibers committed 8 Oct 2026 · 70a0f47da9 · 5 files, +22 −5

Changes

ddiff --git a/CHANGELOG.md b/CHANGELOG.md
iindex 20c0ca1..18c15b8 100644
---- a/CHANGELOG.md
++++ b/CHANGELOG.md
@@@ -4,6 +4,12 @@ All notable changes to `openvibe-shared`. Versions follow [semver](https://semve
breaking change to any exported module, browser global or served file name is a new major.
A release is the git tag `vX.Y.Z`; consumers pin the tag's tarball (see README).
++## 2.13.2 — 2026-10-08
++
++- The navbar loads `ov-mark.js` only when no `<script src=".../ov-mark.js">` is on the page yet: a page carrying its own async
++ tag that had not run when the navbar rendered got a second copy (ovhost browser-check "script requested twice" on
++ openvibe.coupons, .reviews, .services, .vip and openre.stream, 2026-10-08).
++
## 2.13.1 — 2026-10-08
- `showcase` code samples are a focus stop with a name (`<pre tabindex="0" aria-label="…">`): a sample that scrolls
ddiff --git a/navbar.js b/navbar.js
iindex f0d1429..11aeba5 100644
---- a/navbar.js
++++ b/navbar.js
@@@ -1335,8 +1335,9 @@
const loginHref = resolveLoginHref(currentHost(), window.location.href);
const addAccountHref = `${_config.apiBase}/login?add_account=1&return=${encodeURIComponent(window.location.href)}`;
-- // The OV brand mark is a self-contained drop-in (mounts every .ov-mark it finds).
-- if (!window.__ovMark && !document.getElementById('ov-mark-loader')) {
++ // The OV brand mark is a self-contained drop-in (mounts every .ov-mark it finds). A page may already carry its own
++ // async <script src=".../ov-mark.js?v=…"> that has not run yet: never load a second copy.
++ if (!window.__ovMark && !document.getElementById('ov-mark-loader') && !(document.querySelector && document.querySelector('script[src*="/ov-mark.js"]'))) {
try { const sc = document.createElement('script'); sc.id = 'ov-mark-loader'; sc.src = sibling('ov-mark.js'); sc.async = true; document.head.appendChild(sc); } catch { /* */ }
}
nav.innerHTML = `
ddiff --git a/package-lock.json b/package-lock.json
iindex 9d2fba9..67ab6e3 100644
---- a/package-lock.json
++++ b/package-lock.json
@@@ -1,12 +1,12 @@
{
"name": "openvibe-shared",
-- "version": "2.13.1",
++ "version": "2.13.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "openvibe-shared",
-- "version": "2.13.1",
++ "version": "2.13.2",
"license": "MIT",
"dependencies": {
"jsonwebtoken": "^9.0.0"
ddiff --git a/package.json b/package.json
iindex 165c858..49512a7 100644
---- a/package.json
++++ b/package.json
@@@ -1,6 +1,6 @@
{
"name": "openvibe-shared",
-- "version": "2.13.1",
++ "version": "2.13.2",
"description": "Shared navbar, footer, themes, icons, legal pages, SEO, auth and analytics for every OpenVibe site",
"main": "index.js",
"bin": {
ddiff --git a/test/navbar-brand.test.js b/test/navbar-brand.test.js
iindex 076fdd6..3683455 100644
---- a/test/navbar-brand.test.js
++++ b/test/navbar-brand.test.js
@@@ -120,6 +120,16 @@ function load(hostname, opts = {}) {
assert.strictEqual(n3.brand().name, 'Custom Name');
assert.strictEqual(n3.brand().variant, 'games');
}
++// ── ov-mark.js is loaded once: not when the page already carries its own (async) tag ────
++{
++ const { navbar, created } = load('openvibe.coupons');
++ navbar.init({ service: 'coupons', user: null });
++ assert.strictEqual(created.filter((e) => e.tag === 'script' && e.id === 'ov-mark-loader').length, 1, 'no tag on the page: the navbar loads the mark');
++ const second = load('openvibe.coupons');
++ global.document.querySelector = (sel) => (sel === 'script[src*="/ov-mark.js"]' ? { src: '/shared/ov-mark.js?v=1' } : null);
++ second.navbar.init({ service: 'coupons', user: null });
++ assert.strictEqual(second.created.filter((e) => e.tag === 'script' && e.id === 'ov-mark-loader').length, 0, 'the page\'s own tag: no second copy');
++}
// ── rendered markup carries the segments, compact attr and the variant ────
{
const { navbar, created } = load('pastes.openvibe.tools', { pathname: '/mine' });