1/** 2 * CitrusOS shell services â settings, routes, global cursor (once, not per window). 3 */ 4(function (global) { 5 'use strict'; 6 7 function bootReport(track, fraction, label) { 8 if (global.CitrusBoot && typeof global.CitrusBoot.reportBoot === 'function') { 9 global.CitrusBoot.reportBoot(track, fraction, label); 10 } 11 } 12 13 function bootFinalize(track, label) { 14 if (global.CitrusBoot && typeof global.CitrusBoot.finalizeBootTrack === 'function') { 15 global.CitrusBoot.finalizeBootTrack(track, label); 16 } 17 } 18 19 function shellScriptLabel(src) { 20 if (!src || typeof src !== 'string') return 'Loading shell modules...'; 21 if (src.indexOf('firebase-app') !== -1 || src.indexOf('firebase-database') !== -1) { 22 return 'Connecting Firebase...'; 23 } 24 if (src.indexOf('firebaseConfig') !== -1) return 'Connecting Firebase... OK'; 25 if (src.indexOf('sharedUsername') !== -1) return 'Loading account services...'; 26 if (src.indexOf('settingsMenu') !== -1) return 'Loading settings menu...'; 27 if (src.indexOf('spaRouteManifest') !== -1) return 'Registering app routes...'; 28 if (src.indexOf('spaNav') !== -1) return 'Loading navigation...'; 29 if (src.indexOf('citrusWindowLoader') !== -1) return 'Starting window manager...'; 30 if (src.indexOf('ensureSiteFontAwesome') !== -1) return 'Loading icons...'; 31 if (src.indexOf('scriptLoadOnce') !== -1) return 'Loading core modules...'; 32 if (src.indexOf('resolveSiteAssetUrl') !== -1) return 'Resolving asset paths...'; 33 if (src.indexOf('siteThemePalettes') !== -1) return 'Loading theme palettes...'; 34 if (src.indexOf('citrusDirectMountRoutes') !== -1) return 'Mounting desktop apps...'; 35 if (src.indexOf('chatroomRouteAssets') !== -1) return 'Loading chatroom assets...'; 36 return null; 37 } 38 39 function reportShellStep(stepIndex, totalSteps, src) { 40 if (!totalSteps) return; 41 var fraction = stepIndex / totalSteps; 42 var label = shellScriptLabel(src) || ('Loading shell modules (' + stepIndex + '/' + totalSteps + ')...'); 43 bootReport('shell', fraction, label); 44 } 45 46 function markScriptLoaded(src) { 47 var el = document.querySelector('script[src="' + src + '"]'); 48 if (el) el.dataset.spaLoaded = '1'; 49 } 50 51 function isShellScriptAlreadyReady(src) { 52 if (src.indexOf('siteThemePalettes.js') !== -1 && global.SiteThemePalettes) { 53 return true; 54 } 55 if (src.indexOf('firebase-app-compat') !== -1 && global.firebase) { 56 return true; 57 } 58 if (src.indexOf('firebase-database-compat') !== -1 && 59 global.firebase && typeof global.firebase.database === 'function') { 60 return true; 61 } 62 if (src.indexOf('firebaseConfig.js') !== -1 && 63 typeof global.ensureFirebaseApp === 'function' && 64 global.firebase && global.firebase.apps && global.firebase.apps.length) { 65 return true; 66 } 67 return false; 68 } 69 70 function loadScript(src) { 71 if (isShellScriptAlreadyReady(src)) { 72 markScriptLoaded(src); 73 return Promise.resolve(); 74 } 75 if (global.PupwifeScriptLoad && typeof global.PupwifeScriptLoad.once === 'function') { 76 return global.PupwifeScriptLoad.once(src); 77 } 78 return new Promise(function (resolve, reject) { 79 if (document.querySelector('script[src="' + src + '"]')) { 80 resolve(); 81 return; 82 } 83 var s = document.createElement('script'); 84 s.src = src; 85 s.async = false; 86 s.onload = function () { resolve(); }; 87 s.onerror = function () { reject(new Error('Failed to load ' + src)); }; 88 document.body.appendChild(s); 89 }); 90 } 91 92 function ensureStylesheet(href) { 93 if (document.querySelector('link[href="' + href + '"]')) return; 94 var link = document.createElement('link'); 95 link.rel = 'stylesheet'; 96 link.href = href; 97 document.head.appendChild(link); 98 } 99 100 var SHELL_CTX_MENU_V = 'citrus-ctx-20260927a'; 101 102 function boot() { 103 if (!document.body || !document.body.classList.contains('page-citrusos')) return; 104 105 var shellScripts = [ 106 '/scripts/effects/spaGlobalServices.js',
107 'https://www.gstatic.com/firebasejs/9.22.0/firebase-app-compat.js', 108 'https://www.gstatic.com/firebasejs/9.22.0/firebase-database-compat.js', 109 '/scripts/dependencies/firebaseConfig.js', 110 '/scripts/dependencies/sharedUsername.js', 111 '/scripts/dependencies/accountIndicator.js', 112 '/scripts/dependencies/accountPage.js', 113 '/scripts/dependencies/perfModeToggle.js', 114 '/scripts/effects/siteThemePalettes.js', 115 '/scripts/effects/settingsMenu.js', 116 '/scripts/effects/settingsTabs.js', 117 '/scripts/desktop/citrusSettingsWindow.js', 118 '/scripts/effects/siteAudio.js', 119 '/scripts/desktop/citrusDirectMountRoutes.js?v=citrus-daw-20260907', 120 '/projects/chatroom/scripts/chatroomRouteAssets.js', 121 '/scripts/effects/spaRouteManifest.js?v=charsheet-vitals-20260929', 122 '/scripts/desktop/citrusStylesPolicy.js', 123 '/scripts/desktop/citrusToolContract.js', 124 '/scripts/desktop/citrusToolUi.js', 125 '/scripts/effects/spaNav.js', 126 '/scripts/desktop/citrusWindowLoader.js', 127 '/scripts/desktop/citrusAccountWindow.js' 128 ]; 129 var preludeScripts = [ 130 '/scripts/dependencies/resolveSiteAssetUrl.js', 131 '/scripts/effects/scriptLoadOnce.js', 132 '/scripts/effects/ensureSiteFontAwesome.js' 133 ]; 134 var totalShellSteps = preludeScripts.length + shellScripts.length; 135 var shellStep = 0; 136 137 function advanceShell(src) { 138 shellStep += 1; 139 reportShellStep(shellStep, totalShellSteps, src); 140 } 141 142 var chain = loadScript(preludeScripts[0]).then(function () { 143 advanceShell(preludeScripts[0]); 144 return loadScript(preludeScripts[1]); 145 }).then(function () { 146 advanceShell(preludeScripts[1]); 147 return loadScript(preludeScripts[2]); 148 }).then(function () { 149 advanceShell(preludeScripts[2]); 150 if (typeof global.ensureSiteFontAwesome === 'function') { 151 return global.ensureSiteFontAwesome(); 152 } 153 }); 154 155 shellScripts.forEach(function (src) { 156 chain = chain.then(function () { 157 return loadScript(src).then(function () { 158 advanceShell(src); 159 }); 160 }); 161 }); 162 163 chain.then(function () { 164 bootFinalize('shell', 'Shell modules... OK'); 165 bootReport('services', 0, 'Starting global services...'); 166 if (global.PupwifeSpa && typeof global.PupwifeSpa.ensureGlobalDashboardServices === 'function') { 167 return global.PupwifeSpa.ensureGlobalDashboardServices(); 168 } 169 }).then(function () { 170 bootFinalize('services', 'Global services... OK'); 171 bootReport('ambient', 0, 'Loading WebGL wallpaper...'); 172 if (global.SiteAmbientBoot && typeof global.SiteAmbientBoot.ensure === 'function') { 173 return global.SiteAmbientBoot.ensure({ routeId: 'index.html' }); 174 } 175 }).then(function () { 176 bootFinalize('ambient', 'Ambient effects... OK'); 177 bootReport('finalize', 0, 'Loading context menus...'); 178 ensureStylesheet('/css/siteContextMenu.css'); 179 return loadScript('/scripts/effects/siteContextMenu.js?v=' + SHELL_CTX_MENU_V); 180 }).then(function () { 181 bootReport('finalize', 0.45, 'Registering context menus...'); 182 return loadScript('/scripts/effects/siteContextMenuRegistry.js?v=' + SHELL_CTX_MENU_V); 183 }).then(function () { 184 bootReport('finalize', 0.75, 'Applying settings...'); 185 if (global.CitrusStylesPolicy && typeof global.CitrusStylesPolicy.pruneDisallowedHostStyles === 'function') { 186 global.CitrusStylesPolicy.pruneDisallowedHostStyles(); 187 } 188 if (global.SettingsMenu && typeof global.SettingsMenu.init === 'function') { 189 global.SettingsMenu.init(); 190 } 191 if (global.SiteContextMenu && typeof global.SiteContextMenu.init === 'function') { 192 global.SiteContextMenu.init(); 193 } 194 if (global.AccountIndicator && typeof global.AccountIndicator.update === 'function') {
195 global.AccountIndicator.update(); 196 } 197 if (global.CitrusDirectMountRoutes && typeof global.CitrusDirectMountRoutes.ensure === 'function') { 198 global.CitrusDirectMountRoutes.ensure(); 199 } 200 bootFinalize('finalize', 'Shell ready... OK'); 201 try { 202 global.__citrusShellReady = true; 203 global.dispatchEvent(new CustomEvent('pupwife:citrus-shell-ready')); 204 } catch (e) { /* ignore */ } 205 }).catch(function (err) { 206 console.warn('[citrusShellBoot]', err); 207 bootFinalize('shell', 'Shell modules... OK'); 208 bootFinalize('services', 'Global services... OK'); 209 bootFinalize('ambient', 'Ambient effects... OK'); 210 bootFinalize('finalize', 'Shell ready... OK'); 211 try { 212 global.__citrusShellReady = true; 213 global.dispatchEvent(new CustomEvent('pupwife:citrus-shell-ready')); 214 } catch (e2) { /* ignore */ } 215 }); 216 } 217 218 if (document.readyState === 'loading') { 219 document.addEventListener('DOMContentLoaded', boot); 220 } else { 221 boot(); 222 } 223})(typeof window !== 'undefined' ? window : this);
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.