openvibers/openvibe.live Commits
Load pastes.js and dashboard.js on demand; one closeModal and one escapeHtml for the site
Changes
@@@ -5114,7 +5114,10 @@ curl -s https://openvibe.media/live/@Goosely/chat-insight.json | jq ".chat_insig(function () {var BUNDLES = {broadcast: ['/js/broadcast-workspace.js?v=16', '/js/broadcast.js?v=31'],-- arena: ['/js/arena.js?v=18']++ arena: ['/js/arena.js?v=18'],++ pastes: ['/js/pastes.js?v=17'],++ // The dashboard renders paste cards with pastes.js helpers, synchronously.++ dashboard: ['/js/pastes.js?v=17', '/js/dashboard.js?v=17']};var started = Object.create(null);function one(src) {@@@ -5137,9 +5140,29 @@ curl -s https://openvibe.media/live/@Goosely/chat-insight.json | jq ".chat_insigwindow.ovPrefetchRoutes = function () {for (var k in BUNDLES) window.ovLoadRoute(k);};++ // A few handlers in those files are reachable from pages that do not load them: "Screenshot" in++ // the account menu, the stream-key button on the broadcast desk, the control-profile modal++ // buttons. Each gets a stand-in that loads its bundle and then calls the real function, which++ // replaces the stand-in when the script runs (a top-level function declaration overwrites the++ // window property). If the real function never arrives, the stand-in gives up instead of++ // calling itself forever.++ function stub(name, bundle) {++ var fake = function () {++ var args = arguments;++ return window.ovLoadRoute(bundle).then(function () {++ if (window[name] !== fake && typeof window[name] === 'function') return window[name].apply(window, args);++ console.warn('[routes] ' + name + ' is unavailable');++ });++ };++ window[name] = fake;++ }++ stub('captureScreenshot', 'pastes');++ stub('regenerateDashStreamKey', 'dashboard');++ stub('doCreateConfig', 'dashboard');++ stub('doAddConfigButton', 'dashboard');})();</script>--<script src="/js/app.js?v=183" defer></script>++<script src="/js/app.js?v=184" defer></script><script src="/js/stream-player.js?v=22" defer></script><script src="/js/stream-pip.js?v=1" defer></script><script src="/js/chat-shared.js?v=3" defer></script>@@@ -5150,7 +5173,6 @@ curl -s https://openvibe.media/live/@Goosely/chat-insight.json | jq ".chat_insig<script src="/js/channel-uploads.js?v=12" defer></script><script src="/js/controls.js?v=8" defer></script><script src="/js/onvif-dashboard.js?v=6" defer></script>--<script src="/js/dashboard.js?v=16" defer></script><!-- (js/admin.js removed — 130KB of unreachable code; /admin redirects off-site.) --><!-- ── Floating Global Chat Widget (visible on non-chat pages) ── --><div id="floating-chat-widget" class="floating-chat-widget" style="display:none">@@@ -5211,7 +5233,6 @@ curl -s https://openvibe.media/live/@Goosely/chat-insight.json | jq ".chat_insig<script src="/js/recap.js?v=1" defer></script><script src="/js/context-cards.js?v=4" defer></script><script src="/js/powerchat.js?v=10" defer></script>--<script src="/js/pastes.js?v=16" defer></script><script src="/js/draggable-fab.js?v=2" defer></script><script src="/js/messenger.js?v=4" defer></script><script src="/js/ov-mark.js?v=2" defer></script>@@@ -727,10 +727,24 @@ function showModal(id) {if (id === 'donate' && typeof _initDonateModal === 'function') _initDonateModal();}++// Defined here once. dashboard.js used to carry a second, fuller copy that silently replaced this one++// because it loaded later — so the site's modals were closing through the dashboard's version.function closeModal() {-- document.getElementById('modal-overlay').classList.remove('show');++ const overlay = document.getElementById('modal-overlay');++ if (overlay) overlay.classList.remove('show');++ // The clone-preset modal toggles style.display rather than the .show class.++ const cloneModal = document.getElementById('clone-preset-config-modal');++ if (cloneModal) cloneModal.style.display = 'none';++}++++// Escapes quotes as well as < > &. The previous version used the textContent/innerHTML trick,++// which leaves " and ' untouched — safe in text, but these helpers are also interpolated into++// attribute values, where an unescaped quote ends the attribute and starts a new one.++function escapeHtml(str) {++ return String(str == null ? '' : str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');}++/* ── Auth ──────────────────────────────────────────────────────── */// Local login/register removed — all auth goes through OpenVibe SSOfunction doLogin() { window.location.href = '/api/auth/sso/login'; }@@@ -1213,7 +1227,7 @@ function routeFromURL() {loadClipPlayer(segments[1]);} else if (segments[0] === 'dashboard') {showPage('dashboard');-- loadDashboard();++ ovLoadRoute('dashboard').then(() => { if (typeof loadDashboard === 'function') loadDashboard(); });} else if (segments[0] === 'settings') {// /settings was merged into the dashboard — redirect there.navigate('/dashboard', true);@@@ -1240,12 +1254,14 @@ function routeFromURL() {return;} else if (segments[0] === 'pastes') {showPage('pastes');-- loadPastesPage();-- // Handle ?edit=slugconst editSlug = new URLSearchParams(window.location.search).get('edit');++ ovLoadRoute('pastes').then(() => {++ if (typeof loadPastesPage === 'function') loadPastesPage();++ });++ // Handle ?edit=slugif (editSlug) {-- api(`/pastes/${editSlug}`).then(data => {-- if (data.paste) openNewPasteModal({++ Promise.all([api(`/pastes/${editSlug}`), ovLoadRoute('pastes')]).then(([data]) => {++ if (data.paste && typeof openNewPasteModal === 'function') openNewPasteModal({title: data.paste.title,content: data.paste.content,language: data.paste.language,@@@ -1275,7 +1291,7 @@ function routeFromURL() {return;} else if (segments[0] === 'p' && segments[1]) {showPage('paste-viewer');-- loadPasteViewer(segments[1]);++ ovLoadRoute('pastes').then(() => { if (typeof loadPasteViewer === 'function') loadPasteViewer(segments[1]); });} else if (segments[0] === 'documentation') {showPage('documentation');initDocsTabScroller();@@@ -380,14 +380,7 @@ function openClonePresetModal() {};}--function closeModal() {-- // Hide main modal (app.js uses classList 'show')-- const overlay = document.getElementById('modal-overlay');-- if (overlay) overlay.classList.remove('show');-- // Also hide clone preset modal (uses style.display)-- const cloneModal = document.getElementById('clone-preset-config-modal');-- if (cloneModal) cloneModal.style.display = 'none';--}++// closeModal() lives in app.js — every modal on the site uses it, not only the dashboard's.async function doClonePresetConfig() {const select = document.getElementById('clone-preset-select');@@@ -1597,9 +1597,4 @@ function formatTimeAgo(dateStr) {return date.toLocaleDateString();}--// Escapes quotes as well as < > &. The previous version used the textContent/innerHTML trick,--// which leaves " and ' untouched — safe in text, but these helpers are also interpolated into--// attribute values, where an unescaped quote ends the attribute and starts a new one.--function escapeHtml(str) {-- return String(str == null ? '' : str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');--}++// escapeHtml() lives in app.js: settings.js and others use it on pages that never load this file.@@@ -265,7 +265,7 @@ const ok = (name) => { pass++; console.log(' ok -', name); };// straight from the uploader's request body) in href="…" on a public page, and a group DM name// in value="…". Each helper is evaluated here against a breakout payload.const cases = [-- ['public/js/pastes.js', /function escapeHtml\(str\) \{[\s\S]*?\n\}/, 'escapeHtml'],++ ['public/js/app.js', /function escapeHtml\(str\) \{[\s\S]*?\n\}/, 'escapeHtml'],['public/js/messenger.js', /function esc\(s\) \{[\s\S]*?\n \}/, 'esc'],['public/js/vibes.js', /function escHb\(s\) \{[^\n]*\}/, 'escHb'],['public/js/call.js', /function _esc\(str\) \{[\s\S]*?\n\}/, '_esc'],
@@ -5114,7 +5114,10 @@ curl -s https://openvibe.media/live/@Goosely/chat-insight.json | jq ".chat_insig
@@ -5114,7 +5114,10 @@ curl -s https://openvibe.media/live/@Goosely/chat-insight.json | jq ".chat_insig
(function () {
(function () {
var BUNDLES = {
var BUNDLES = {
broadcast: ['/js/broadcast-workspace.js?v=16', '/js/broadcast.js?v=31'],
broadcast: ['/js/broadcast-workspace.js?v=16', '/js/broadcast.js?v=31'],
- arena: ['/js/arena.js?v=18']
+ arena: ['/js/arena.js?v=18'],
+ pastes: ['/js/pastes.js?v=17'],
+ // The dashboard renders paste cards with pastes.js helpers, synchronously.
+ dashboard: ['/js/pastes.js?v=17', '/js/dashboard.js?v=17']
};
};
var started = Object.create(null);
var started = Object.create(null);
function one(src) {
function one(src) {
@@ -5137,9 +5140,29 @@ curl -s https://openvibe.media/live/@Goosely/chat-insight.json | jq ".chat_insig
@@ -5137,9 +5140,29 @@ curl -s https://openvibe.media/live/@Goosely/chat-insight.json | jq ".chat_insig
window.ovPrefetchRoutes = function () {
window.ovPrefetchRoutes = function () {
for (var k in BUNDLES) window.ovLoadRoute(k);
for (var k in BUNDLES) window.ovLoadRoute(k);
};
};
+ // A few handlers in those files are reachable from pages that do not load them: "Screenshot" in
+ // the account menu, the stream-key button on the broadcast desk, the control-profile modal
+ // buttons. Each gets a stand-in that loads its bundle and then calls the real function, which
+ // replaces the stand-in when the script runs (a top-level function declaration overwrites the
+ // window property). If the real function never arrives, the stand-in gives up instead of
+ // calling itself forever.
+ function stub(name, bundle) {
+ var fake = function () {
+ var args = arguments;
+ return window.ovLoadRoute(bundle).then(function () {
+ if (window[name] !== fake && typeof window[name] === 'function') return window[name].apply(window, args);
+ console.warn('[routes] ' + name + ' is unavailable');
+ });
+ };
+ window[name] = fake;
+ }
+ stub('captureScreenshot', 'pastes');
+ stub('regenerateDashStreamKey', 'dashboard');
+ stub('doCreateConfig', 'dashboard');
+ stub('doAddConfigButton', 'dashboard');
})();
})();
</script>
</script>
-<script src="/js/app.js?v=183" defer></script>
+<script src="/js/app.js?v=184" defer></script>
<script src="/js/stream-player.js?v=22" defer></script>
<script src="/js/stream-player.js?v=22" defer></script>
<script src="/js/stream-pip.js?v=1" defer></script>
<script src="/js/stream-pip.js?v=1" defer></script>
<script src="/js/chat-shared.js?v=3" defer></script>
<script src="/js/chat-shared.js?v=3" defer></script>
@@ -5150,7 +5173,6 @@ curl -s https://openvibe.media/live/@Goosely/chat-insight.json | jq ".chat_insig
@@ -5150,7 +5173,6 @@ curl -s https://openvibe.media/live/@Goosely/chat-insight.json | jq ".chat_insig
<script src="/js/channel-uploads.js?v=12" defer></script>
<script src="/js/channel-uploads.js?v=12" defer></script>
<script src="/js/controls.js?v=8" defer></script>
<script src="/js/controls.js?v=8" defer></script>
<script src="/js/onvif-dashboard.js?v=6" defer></script>
<script src="/js/onvif-dashboard.js?v=6" defer></script>
-<script src="/js/dashboard.js?v=16" defer></script>
<!-- (js/admin.js removed — 130KB of unreachable code; /admin redirects off-site.) -->
<!-- (js/admin.js removed — 130KB of unreachable code; /admin redirects off-site.) -->
<!-- ── Floating Global Chat Widget (visible on non-chat pages) ── -->
<!-- ── Floating Global Chat Widget (visible on non-chat pages) ── -->
<div id="floating-chat-widget" class="floating-chat-widget" style="display:none">
<div id="floating-chat-widget" class="floating-chat-widget" style="display:none">
@@ -5211,7 +5233,6 @@ curl -s https://openvibe.media/live/@Goosely/chat-insight.json | jq ".chat_insig
@@ -5211,7 +5233,6 @@ curl -s https://openvibe.media/live/@Goosely/chat-insight.json | jq ".chat_insig
<script src="/js/recap.js?v=1" defer></script>
<script src="/js/recap.js?v=1" defer></script>
<script src="/js/context-cards.js?v=4" defer></script>
<script src="/js/context-cards.js?v=4" defer></script>
<script src="/js/powerchat.js?v=10" defer></script>
<script src="/js/powerchat.js?v=10" defer></script>
-<script src="/js/pastes.js?v=16" defer></script>
<script src="/js/draggable-fab.js?v=2" defer></script>
<script src="/js/draggable-fab.js?v=2" defer></script>
<script src="/js/messenger.js?v=4" defer></script>
<script src="/js/messenger.js?v=4" defer></script>
<script src="/js/ov-mark.js?v=2" defer></script>
<script src="/js/ov-mark.js?v=2" defer></script>
@@ -727,10 +727,24 @@ function showModal(id) {
@@ -727,10 +727,24 @@ function showModal(id) {
if (id === 'donate' && typeof _initDonateModal === 'function') _initDonateModal();
if (id === 'donate' && typeof _initDonateModal === 'function') _initDonateModal();
}
}
+// Defined here once. dashboard.js used to carry a second, fuller copy that silently replaced this one
+// because it loaded later — so the site's modals were closing through the dashboard's version.
function closeModal() {
function closeModal() {
- document.getElementById('modal-overlay').classList.remove('show');
+ const overlay = document.getElementById('modal-overlay');
+ if (overlay) overlay.classList.remove('show');
+ // The clone-preset modal toggles style.display rather than the .show class.
+ const cloneModal = document.getElementById('clone-preset-config-modal');
+ if (cloneModal) cloneModal.style.display = 'none';
+}
+
+// Escapes quotes as well as < > &. The previous version used the textContent/innerHTML trick,
+// which leaves " and ' untouched — safe in text, but these helpers are also interpolated into
+// attribute values, where an unescaped quote ends the attribute and starts a new one.
+function escapeHtml(str) {
+ return String(str == null ? '' : str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');
}
}
+
/* ── Auth ──────────────────────────────────────────────────────── */
/* ── Auth ──────────────────────────────────────────────────────── */
// Local login/register removed — all auth goes through OpenVibe SSO
// Local login/register removed — all auth goes through OpenVibe SSO
function doLogin() { window.location.href = '/api/auth/sso/login'; }
function doLogin() { window.location.href = '/api/auth/sso/login'; }
@@ -1213,7 +1227,7 @@ function routeFromURL() {
@@ -1213,7 +1227,7 @@ function routeFromURL() {
loadClipPlayer(segments[1]);
loadClipPlayer(segments[1]);
} else if (segments[0] === 'dashboard') {
} else if (segments[0] === 'dashboard') {
showPage('dashboard');
showPage('dashboard');
- loadDashboard();
+ ovLoadRoute('dashboard').then(() => { if (typeof loadDashboard === 'function') loadDashboard(); });
} else if (segments[0] === 'settings') {
} else if (segments[0] === 'settings') {
// /settings was merged into the dashboard — redirect there.
// /settings was merged into the dashboard — redirect there.
navigate('/dashboard', true);
navigate('/dashboard', true);
@@ -1240,12 +1254,14 @@ function routeFromURL() {
@@ -1240,12 +1254,14 @@ function routeFromURL() {
return;
return;
} else if (segments[0] === 'pastes') {
} else if (segments[0] === 'pastes') {
showPage('pastes');
showPage('pastes');
- loadPastesPage();
- // Handle ?edit=slug
const editSlug = new URLSearchParams(window.location.search).get('edit');
const editSlug = new URLSearchParams(window.location.search).get('edit');
+ ovLoadRoute('pastes').then(() => {
+ if (typeof loadPastesPage === 'function') loadPastesPage();
+ });
+ // Handle ?edit=slug
if (editSlug) {
if (editSlug) {
- api(`/pastes/${editSlug}`).then(data => {
- if (data.paste) openNewPasteModal({
+ Promise.all([api(`/pastes/${editSlug}`), ovLoadRoute('pastes')]).then(([data]) => {
+ if (data.paste && typeof openNewPasteModal === 'function') openNewPasteModal({
title: data.paste.title,
title: data.paste.title,
content: data.paste.content,
content: data.paste.content,
language: data.paste.language,
language: data.paste.language,
@@ -1275,7 +1291,7 @@ function routeFromURL() {
@@ -1275,7 +1291,7 @@ function routeFromURL() {
return;
return;
} else if (segments[0] === 'p' && segments[1]) {
} else if (segments[0] === 'p' && segments[1]) {
showPage('paste-viewer');
showPage('paste-viewer');
- loadPasteViewer(segments[1]);
+ ovLoadRoute('pastes').then(() => { if (typeof loadPasteViewer === 'function') loadPasteViewer(segments[1]); });
} else if (segments[0] === 'documentation') {
} else if (segments[0] === 'documentation') {
showPage('documentation');
showPage('documentation');
initDocsTabScroller();
initDocsTabScroller();
@@ -380,14 +380,7 @@ function openClonePresetModal() {
@@ -380,14 +380,7 @@ function openClonePresetModal() {
};
};
}
}
-function closeModal() {
- // Hide main modal (app.js uses classList 'show')
- const overlay = document.getElementById('modal-overlay');
- if (overlay) overlay.classList.remove('show');
- // Also hide clone preset modal (uses style.display)
- const cloneModal = document.getElementById('clone-preset-config-modal');
- if (cloneModal) cloneModal.style.display = 'none';
-}
+// closeModal() lives in app.js — every modal on the site uses it, not only the dashboard's.
async function doClonePresetConfig() {
async function doClonePresetConfig() {
const select = document.getElementById('clone-preset-select');
const select = document.getElementById('clone-preset-select');
@@ -1597,9 +1597,4 @@ function formatTimeAgo(dateStr) {
@@ -1597,9 +1597,4 @@ function formatTimeAgo(dateStr) {
return date.toLocaleDateString();
return date.toLocaleDateString();
}
}
-// Escapes quotes as well as < > &. The previous version used the textContent/innerHTML trick,
-// which leaves " and ' untouched — safe in text, but these helpers are also interpolated into
-// attribute values, where an unescaped quote ends the attribute and starts a new one.
-function escapeHtml(str) {
- return String(str == null ? '' : str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');
-}
+// escapeHtml() lives in app.js: settings.js and others use it on pages that never load this file.
@@ -265,7 +265,7 @@ const ok = (name) => { pass++; console.log(' ok -', name); };
@@ -265,7 +265,7 @@ const ok = (name) => { pass++; console.log(' ok -', name); };
// straight from the uploader's request body) in href="…" on a public page, and a group DM name
// straight from the uploader's request body) in href="…" on a public page, and a group DM name
// in value="…". Each helper is evaluated here against a breakout payload.
// in value="…". Each helper is evaluated here against a breakout payload.
const cases = [
const cases = [
- ['public/js/pastes.js', /function escapeHtml\(str\) \{[\s\S]*?\n\}/, 'escapeHtml'],
+ ['public/js/app.js', /function escapeHtml\(str\) \{[\s\S]*?\n\}/, 'escapeHtml'],
['public/js/messenger.js', /function esc\(s\) \{[\s\S]*?\n \}/, 'esc'],
['public/js/messenger.js', /function esc\(s\) \{[\s\S]*?\n \}/, 'esc'],
['public/js/vibes.js', /function escHb\(s\) \{[^\n]*\}/, 'escHb'],
['public/js/vibes.js', /function escHb\(s\) \{[^\n]*\}/, 'escHb'],
['public/js/call.js', /function _esc\(str\) \{[\s\S]*?\n\}/, '_esc'],
['public/js/call.js', /function _esc\(str\) \{[\s\S]*?\n\}/, '_esc'],