1/** 2 * Default + page-specific items for SiteContextMenu. 3 */ 4(function (global) { 5 'use strict'; 6 7 var SCM = null; 8 9 var NAV_ICONS = { 10 Home: 'fa-solid fa-house', 11 Music: 'fa-solid fa-music', 12 Friends: 'fa-solid fa-user-group', 13 'Silly Things': 'fa-solid fa-face-laugh-beam', 14 Cookbook: 'fa-solid fa-book', 15 DAW: 'fa-solid fa-sliders', 16 Shrines: 'fa-solid fa-star', 17 Arcade: 'fa-solid fa-gamepad', 18 Casino: 'fa-solid fa-dice', 19 'Study Hall': 'fa-solid fa-graduation-cap', 20 Blog: 'fa-solid fa-pen-nib', 21 Chat: 'fa-solid fa-comments', 22 Dashboard: 'fa-solid fa-house' 23 }; 24 25 var FOLLOWER_OPTIONS = [ 26 { id: 'none', label: 'None', icon: 'fa-solid fa-ban' }, 27 { id: 'marsey', label: 'Marsey', icon: 'fa-solid fa-cat' }, 28 { id: 'skeletile', label: 'Skeletile', icon: 'fa-solid fa-skull' }, 29 { id: 'orca', label: 'Orca', icon: 'fa-solid fa-fish-fins' }, 30 { id: 'dragon', label: 'Dragon', icon: 'fa-solid fa-dragon' }, 31 { id: 'fish2D', label: '2D fish', icon: 'fa-solid fa-fish' }, 32 { id: 'woimString', label: 'Woim on a string', icon: 'fa-solid fa-worm' }, 33 { id: 'jellypuff', label: 'Jellypuff', icon: 'fa-solid fa-droplet' } 34 ]; 35 36 var registryBooted = false; 37 var registryBootAttempts = 0; 38 var REGISTRY_BOOT_MAX_ATTEMPTS = 120; 39 40 function shellPrompt(label, defaultValue) { 41 if (global.CitrusShellDialogs && typeof global.CitrusShellDialogs.prompt === 'function') { 42 return global.CitrusShellDialogs.prompt({ 43 title: label, 44 label: label, 45 defaultValue: defaultValue || '' 46 }); 47 } 48 return Promise.resolve(global.prompt(label, defaultValue)); 49 } 50
51 function shellConfirm(opts) { 52 if (typeof opts === 'string') opts = { message: opts }; 53 if (global.CitrusShellDialogs && typeof global.CitrusShellDialogs.confirm === 'function') { 54 return global.CitrusShellDialogs.confirm(opts); 55 } 56 return Promise.resolve(global.confirm(opts.message || '')); 57 } 58 59 function api() { 60 return SCM || global.SiteContextMenu; 61 } 62 63 function isCitrusOsPage() { 64 if (global.document.body && global.document.body.classList.contains('page-citrusos')) { 65 return true; 66 } 67 return !!global.document.getElementById('citrusos-shell'); 68 } 69 70 function setting(key, fallback) { 71 var a = api(); 72 return a ? a.getSetting(key, fallback) : fallback; 73 } 74 75 function setSetting(key, value) { 76 var a = api(); 77 if (a) a.setSetting(key, value); 78 } 79 80 function getSidebarNavEntries() { 81 if (global.SidebarNavConfig && global.SidebarNavConfig.items) { 82 return global.SidebarNavConfig.items; 83 } 84 return [ 85 { href: '/index.html', label: 'Home' }, 86 { href: '/music.html', label: 'Music' }, 87 { href: '/friends.html', label: 'Friends' }, 88 { href: '/silly.html', label: 'Silly Things' }, 89 { href: '/cookbook.html', label: 'Cookbook' }, 90 { href: '/daw.html', label: 'Virtual Keyboard' }, 91 { href: '/strudel.html', label: 'Strudel' }, 92 { href: '/shrines.html', label: 'Shrines' }, 93 { href: '/arcade.html', label: 'Arcade' }, 94 { href: '/casino.html', label: 'Casino' }, 95 { href: '/?app=studyhall', label: 'Study Hall' }, 96 { href: '/blog.html', label: 'Blog' }, 97 { href: '/chatroom.html', label: 'Chat' } 98 ]; 99 } 100 101 function buildNavigateSubmenuChildren(ctx) { 102 return getSidebarNavEntries().map(function (entry) { 103 var routeId = api().hrefToRouteId(entry.href); 104 return { 105 id: 'nav-' + routeId, 106 label: entry.label, 107 icon: NAV_ICONS[entry.label] || 'fa-solid fa-arrow-right', 108 when: function (c) { return c.routeId !== routeId; }, 109 action: function () { api().navigate(routeId); } 110 }; 111 }); 112 } 113 114 function buildFollowerSubmenuChildren() { 115 return FOLLOWER_OPTIONS.map(function (opt) { 116 return { 117 id: 'follower-' + opt.id, 118 label: opt.label, 119 icon: opt.icon, 120 checked: function () { return setting('follower', 'none') === opt.id; }, 121 action: function () { 122 setSetting('follower', opt.id); 123 if (global.switchFollower) { 124 global.switchFollower(opt.id, true); 125 } 126 api().showToast('Follower: ' + opt.label); 127 } 128 }; 129 }); 130 } 131 132 function clickRandom(selector, emptyMsg) { 133 var nodes = global.document.querySelectorAll(selector); 134 if (!nodes.length) { 135 api().showToast(emptyMsg || 'Nothing to pick right now'); 136 return; 137 } 138 nodes[Math.floor(Math.random() * nodes.length)].click(); 139 } 140 141 function copyMusicNowPlaying() { 142 if (!global.musicPlayerApi || typeof global.musicPlayerApi.getCurrentIndex !== 'function') { 143 api().showToast('Music player not ready'); 144 return; 145 } 146 var idx = global.musicPlayerApi.getCurrentIndex(); 147 var meta = global.musicPlayerApi.getTrackMeta(idx); 148 if (!meta || !meta.title) { 149 api().showToast('No track loaded'); 150 return; 151 } 152 var line = meta.title + (meta.artist ? ' by ' + meta.artist : ''); 153 api().copyText(line, 'Now playing copied'); 154 } 155 156 function citrusContextClosest(ctx, selector) { 157 if (api() && typeof api().contextClosest === 'function') { 158 return api().contextClosest(ctx, selector); 159 } 160 if (!ctx) return null; 161 var event = ctx.event; 162 if (event && typeof event.composedPath === 'function') { 163 var path = event.composedPath(); 164 for (var i = 0; i < path.length; i++) { 165 var node = path[i]; 166 if (node && node.nodeType === 1 && node.matches && node.matches(selector)) { 167 return node; 168 } 169 } 170 } 171 var el = ctx.target; 172 if (!el) return null;
173 if (el.nodeType === 3 && el.parentElement) el = el.parentElement; 174 return el && el.closest ? el.closest(selector) : null; 175 } 176 177 function citrusIsAppPinnable(app) { 178 if (!app) return false; 179 var tier = app.launchTier || 'window'; 180 return tier === 'window' || tier === 'folder'; 181 } 182 183 function citrusRegistryApp(id, hrefHint) { 184 var registry = global.CitrusAppRegistry; 185 if (!registry || !id) return null; 186 if (registry.getAppById) return registry.getAppById(id, hrefHint); 187 return registry.getApp ? registry.getApp(id) : null; 188 } 189 190 function resolveCitrusPinnableApp(ctx, knownApp) { 191 if (knownApp && citrusIsAppPinnable(knownApp)) return knownApp; 192 var registry = global.CitrusAppRegistry; 193 if (!registry) return null; 194 195 var desktopIcon = citrusContextClosest(ctx, '.citrus-desktop-icon'); 196 if (desktopIcon) { 197 var desktopAppId = desktopIcon.getAttribute('data-app-id'); 198 var desktopApp = citrusRegistryApp(desktopAppId); 199 if (citrusIsAppPinnable(desktopApp)) return desktopApp; 200 } 201 202 var taskbarBtn = citrusContextClosest(ctx, '.citrus-taskbar-app'); 203 if (taskbarBtn) { 204 var taskbarAppId = taskbarBtn.getAttribute('data-app-id'); 205 var taskbarApp = citrusRegistryApp(taskbarAppId); 206 if (citrusIsAppPinnable(taskbarApp)) return taskbarApp; 207 } 208 209 var startAppBtn = citrusContextClosest(ctx, '.citrus-start-app'); 210 if (startAppBtn) { 211 var startAppId = startAppBtn.getAttribute('data-app-id'); 212 var startApp = citrusRegistryApp(startAppId); 213 if (citrusIsAppPinnable(startApp)) return startApp; 214 } 215 216 var folderIcon = citrusContextClosest(ctx, '.citrus-folder-icon'); 217 if (folderIcon && folderIcon.getAttribute('data-entry-type') === 'app') { 218 var href = folderIcon.getAttribute('data-entry-href'); 219 if (href && registry.getAppByHref) { 220 var folderApp = registry.getAppByHref(href); 221 if (citrusIsAppPinnable(folderApp)) return folderApp; 222 } 223 } 224 225 if (global.CitrusDesktop && typeof global.CitrusDesktop.resolvePinnableAppFromElement === 'function') { 226 var el = ctx.target; 227 if (el && el.nodeType === 3 && el.parentElement) el = el.parentElement; 228 var fromDesktop = global.CitrusDesktop.resolvePinnableAppFromElement(el); 229 if (citrusIsAppPinnable(fromDesktop)) return fromDesktop; 230 } 231 232 return null; 233 } 234 235 function readCitrusPinnedApps() { 236 try { 237 var raw = localStorage.getItem('citrusos_session_v3'); 238 var session = raw ? JSON.parse(raw) : {}; 239 return Array.isArray(session.pinnedApps) ? session.pinnedApps : []; 240 } catch (e) { 241 return []; 242 } 243 } 244 245 function isCitrusAppPinnedLocal(appId) { 246 return readCitrusPinnedApps().indexOf(appId) !== -1; 247 } 248 249 function appendCitrusPinTaskbarItems(items, ctx, knownApp) { 250 if (!isCitrusOsPage() || !ctx) return false; 251 var app; 252 try { 253 app = resolveCitrusPinnableApp(ctx, knownApp); 254 } catch (err) { 255 if (typeof global.devWarn === 'function') { 256 global.devWarn('[SiteContextMenuRegistry] Pin resolve failed', err); 257 } 258 return false; 259 } 260 if (!app) return false; 261 var appId = app.id; 262 var pinned = isCitrusAppPinnedLocal(appId); 263 items.push({ 264 id: pinned ? 'citrus-pin-unpin' : 'citrus-pin-taskbar', 265 label: pinned ? 'Unpin from taskbar' : 'Pin to taskbar', 266 icon: pinned ? 'fa-solid fa-xmark' : 'fa-solid fa-thumbtack', 267 action: function () { 268 if (!global.CitrusDesktop) return; 269 if (pinned && typeof global.CitrusDesktop.unpinApp === 'function') { 270 global.CitrusDesktop.unpinApp(appId); 271 } else if (!pinned && typeof global.CitrusDesktop.pinApp === 'function') { 272 global.CitrusDesktop.pinApp(appId); 273 } 274 } 275 }); 276 return true; 277 } 278
279 function resolveCitrusWindowIdFromCtx(ctx) { 280 if (!ctx) return ''; 281 if (ctx.citrusWindowId) return ctx.citrusWindowId; 282 if (ctx.citrusWindow) return ctx.citrusWindow.getAttribute('data-app-id') || ''; 283 if (ctx.citrusTaskbarBtn) { 284 return ctx.citrusTaskbarBtn.getAttribute('data-window-id') || 285 ctx.citrusTaskbarBtn.getAttribute('data-app-id') || ''; 286 } 287 return ''; 288 } 289 290 function citrusOnWindowChrome(ctx) { 291 if (!isCitrusOsPage() || !ctx) return false; 292 if (ctx.citrusOnTitlebarChrome) return true; 293 return !!(ctx.citrusTaskbarBtn && 294 !citrusContextClosest(ctx, '.citrus-desktop-icon') && 295 !citrusContextClosest(ctx, '.citrus-folder-icon')); 296 } 297 298 function citrusInAppWindow(ctx) { 299 if (!isCitrusOsPage() || !ctx) return false; 300 if (citrusOnWindowChrome(ctx)) return false; 301 if (citrusContextClosest(ctx, '.citrus-desktop-icon')) return false; 302 if (citrusContextClosest(ctx, '.citrus-widget')) return false; 303 if (citrusContextClosest(ctx, '.citrus-folder-window')) return false; 304 if (citrusContextClosest(ctx, '.page-chatroom')) return false; 305 if (ctx.citrusOnDesktop && !citrusContextClosest(ctx, '.citrus-window')) return false; 306 return !!citrusContextClosest(ctx, '.citrus-window__content, .citrus-fullscreen__content'); 307 } 308 309 function appendDndFloatChromeItems(items, ctx) { 310 var container = ctx && ctx.dndFloatContainer; 311 if (!container || !global.CitrusDesktop) return false; 312 var desktop = global.CitrusDesktop; 313 var pinned = typeof desktop.isLayerPinnedTop === 'function' && desktop.isLayerPinnedTop(container); 314 items.push({ 315 id: pinned ? 'dnd-float-unpin-front' : 'dnd-float-pin-front', 316 label: pinned ? 'Unpin from front' : 'Pin to front', 317 icon: 'fa-solid fa-thumbtack', 318 checked: pinned, 319 defaultFocus: true, 320 action: function () { 321 if (typeof desktop.toggleLayerPinnedTop === 'function') { 322 desktop.toggleLayerPinnedTop(container); 323 } 324 if (global.DndWorkspace && typeof global.DndWorkspace.syncFloatHostPinState === 'function') { 325 global.DndWorkspace.syncFloatHostPinState(); 326 } 327 } 328 }); 329 return true; 330 } 331 332 function appendCitrusWindowChromeItems(items, ctx) { 333 var windowId = resolveCitrusWindowIdFromCtx(ctx); 334 if (!windowId || !global.CitrusDesktop) return false; 335 var desktop = global.CitrusDesktop; 336 var state = typeof desktop.getWindowState === 'function' 337 ? desktop.getWindowState(windowId) 338 : { open: false, minimized: false }; 339 var win = typeof desktop.getWindow === 'function' ? desktop.getWindow(windowId) : null; 340 var app = win && win.app ? win.app : null; 341 342 if (state.open && win && typeof desktop.isWindowPinnedTop === 'function') { 343 var pinnedTop = desktop.isWindowPinnedTop(windowId); 344 items.push({ 345 id: pinnedTop ? 'citrus-win-unpin-front' : 'citrus-win-pin-front', 346 label: pinnedTop ? 'Unpin from front' : 'Pin to front', 347 icon: 'fa-solid fa-thumbtack', 348 checked: pinnedTop, 349 action: function () { 350 if (typeof desktop.toggleWindowPinnedTop === 'function') { 351 desktop.toggleWindowPinnedTop(windowId); 352 } 353 } 354 }); 355 } 356 357 if (state.open && state.minimized) { 358 items.push({ 359 id: 'citrus-win-restore', 360 label: 'Restore', 361 icon: 'fa-solid fa-window-restore', 362 defaultFocus: true, 363 action: function () { 364 if (typeof desktop.restoreWindow === 'function') desktop.restoreWindow(windowId); 365 } 366 }); 367 } else if (state.open) { 368 items.push({ 369 id: 'citrus-win-minimize',
370 label: 'Minimize', 371 icon: 'fa-solid fa-window-minimize', 372 defaultFocus: true, 373 action: function () { 374 if (typeof desktop.minimizeWindow === 'function') { 375 desktop.minimizeWindow(windowId); 376 } else if (typeof desktop.minimizeApp === 'function') { 377 desktop.minimizeApp(windowId); 378 } 379 } 380 }); 381 } 382 383 if (state.open && win) { 384 if (win.immersive) { 385 items.push({ 386 id: 'citrus-win-exit-immersive', 387 label: 'Exit fullscreen', 388 icon: 'fa-solid fa-compress', 389 action: function () { 390 if (typeof desktop.toggleImmersiveFocus === 'function') { 391 desktop.toggleImmersiveFocus(windowId); 392 } 393 } 394 }); 395 } else if (win.maximized) { 396 items.push({ 397 id: 'citrus-win-restore-size', 398 label: 'Restore down', 399 icon: 'fa-solid fa-window-restore', 400 action: function () { 401 if (typeof desktop.toggleMaximize === 'function') { 402 desktop.toggleMaximize(windowId); 403 } 404 } 405 }); 406 } else if (app && app.resizable !== false && app.anchored !== true) { 407 items.push({ 408 id: 'citrus-win-maximize', 409 label: app && app.immersiveFocus ? 'Fullscreen' : 'Maximize', 410 icon: app && app.immersiveFocus ? 'fa-solid fa-expand' : 'fa-solid fa-window-maximize', 411 action: function () { 412 if (app && app.immersiveFocus && 413 typeof desktop.toggleImmersiveFocus === 'function') { 414 desktop.toggleImmersiveFocus(windowId); 415 } else if (typeof desktop.toggleMaximize === 'function') { 416 desktop.toggleMaximize(windowId); 417 } 418 } 419 }); 420 } 421 } 422 423 if (state.open) { 424 items.push({ 425 id: 'citrus-win-close', 426 label: 'Close', 427 icon: 'fa-solid fa-xmark', 428 danger: true, 429 action: function () { 430 if (typeof desktop.requestCloseWindow === 'function') { 431 desktop.requestCloseWindow(windowId); 432 } else if (typeof desktop.closeWindow === 'function') { 433 desktop.closeWindow(windowId); 434 } 435 } 436 }); 437 } else if (app && typeof desktop.openApp === 'function') { 438 items.push({ 439 id: 'citrus-win-open', 440 label: 'Open', 441 icon: 'fa-solid fa-folder-open', 442 defaultFocus: true, 443 action: function () { 444 desktop.openApp(app.id); 445 } 446 }); 447 } 448 449 items.push({ type: 'divider' }); 450 appendCitrusPinTaskbarItems(items, ctx, app); 451 return items.length > 0; 452 } 453 454 function appendCitrusSharedClipboardItems(items) { 455 items.push({ 456 id: 'copy-selection', 457 label: 'Copy selected text', 458 icon: 'fa-solid fa-text-width', 459 when: function (c) { return !!c.selectionText; }, 460 action: function (c) { 461 api().copyText(c.selectionText, 'Selection copied'); 462 } 463 }); 464 465 items.push({ 466 id: 'copy-link', 467 label: 'Copy link address', 468 icon: 'fa-solid fa-link', 469 when: function (c) { return !!c.linkEl; }, 470 action: function (c) { 471 api().copyText(c.linkEl.href, 'Link copied'); 472 } 473 }); 474 475 items.push({ 476 id: 'open-link', 477 label: 'Open link in new tab', 478 icon: 'fa-solid fa-up-right-from-square', 479 when: function (c) { 480 return !!c.linkEl && !c.linkEl.target; 481 }, 482 action: function (c) { 483 global.open(c.linkEl.href, '_blank', 'noopener,noreferrer'); 484 } 485 }); 486 487 items.push({ 488 id: 'copy-image', 489 label: 'Copy image address', 490 icon: 'fa-solid fa-image', 491 when: function (c) { return !!c.imgEl && c.imgEl.src; }, 492 action: function (c) { 493 api().copyText(c.imgEl.currentSrc || c.imgEl.src, 'Image URL copied'); 494 } 495 }); 496 } 497 498 function appendCitrusTrimmedWindowItems(items, ctx) { 499 items.push({ 500 id: 'open-settings', 501 label: 'Open settings', 502 icon: 'fa-solid fa-gear', 503 defaultFocus: true, 504 action: function () { 505 if (global.SettingsMenu && global.SettingsMenu.openSettings) { 506 global.SettingsMenu.openSettings(); 507 } 508 } 509 });
510 appendCitrusSharedClipboardItems(items); 511 } 512 513 function registerGlobalItems() { 514 api().registerProvider(function (ctx) { 515 var items = []; 516 var inAppWindow = citrusInAppWindow(ctx); 517 var onWindowChrome = citrusOnWindowChrome(ctx); 518 519 var iconBtn = citrusContextClosest(ctx, '.citrus-desktop-icon'); 520 if (iconBtn && isCitrusOsPage()) { 521 var shortcutId = iconBtn.getAttribute('data-shortcut-id'); 522 var appId = iconBtn.getAttribute('data-app-id'); 523 var bulkCount = 0; 524 var bulkMode = false; 525 if (global.CitrusDesktopSelection && global.CitrusDesktopSelection.getSelectedCount && 526 global.CitrusDesktopSelection.isSelected) { 527 bulkCount = global.CitrusDesktopSelection.getSelectedCount(); 528 bulkMode = bulkCount > 1 && global.CitrusDesktopSelection.isSelected(shortcutId); 529 } 530 var citrusApp = null; 531 if (shortcutId && global.CitrusDesktopShortcuts) { 532 var sc = global.CitrusDesktopShortcuts.getShortcutById(shortcutId); 533 citrusApp = sc ? global.CitrusDesktopShortcuts.resolveShortcut(sc) : null; 534 } 535 if (!citrusApp && global.CitrusAppRegistry && global.CitrusAppRegistry.getApp) { 536 citrusApp = global.CitrusAppRegistry.getApp(appId); 537 } 538 if (bulkMode) { 539 items.push({ 540 id: 'citrus-icon-open-all', 541 label: 'Open all (' + bulkCount + ')', 542 icon: 'fa-solid fa-folder-open', 543 defaultFocus: true, 544 action: function () { 545 if (global.CitrusDesktop && global.CitrusDesktop.openSelectedShortcuts) { 546 global.CitrusDesktop.openSelectedShortcuts(); 547 } 548 } 549 }); 550 items.push({ 551 id: 'citrus-icon-remove-all', 552 label: 'Remove all from desktop (' + bulkCount + ')', 553 icon: 'fa-solid fa-trash-can', 554 action: function () { 555 if (global.CitrusDesktop && global.CitrusDesktop.removeSelectedShortcuts) { 556 global.CitrusDesktop.removeSelectedShortcuts(); 557 } 558 } 559 }); 560 items.push({ type: 'divider' }); 561 } else { 562 items.push({ 563 id: 'citrus-icon-open', 564 label: 'Open', 565 icon: 'fa-solid fa-folder-open', 566 defaultFocus: true, 567 action: function () { 568 if (global.CitrusDesktop && global.CitrusDesktop.openShortcut) { 569 global.CitrusDesktop.openShortcut(shortcutId); 570 } else if (global.CitrusDesktop && global.CitrusDesktop.openApp) { 571 global.CitrusDesktop.openApp(appId); 572 } 573 } 574 }); 575 appendCitrusPinTaskbarItems(items, ctx, citrusApp); 576 items.push({ 577 id: 'citrus-icon-rename', 578 label: 'Rename', 579 icon: 'fa-solid fa-pen', 580 action: function () { 581 var current = iconBtn.querySelector('.citrus-desktop-icon__label'); 582 shellPrompt('Shortcut name', current ? current.textContent : '').then(function (next) { 583 if (next == null) return; 584 if (global.CitrusDesktop && global.CitrusDesktop.renameDesktopShortcut) { 585 global.CitrusDesktop.renameDesktopShortcut(shortcutId, next); 586 } 587 }); 588 } 589 }); 590 items.push({ 591 id: 'citrus-icon-properties', 592 label: 'Properties', 593 icon: 'fa-solid fa-circle-info', 594 action: function () { 595 if (global.CitrusShortcutProperties && global.CitrusShortcutProperties.openForShortcut) { 596 global.CitrusShortcutProperties.openForShortcut(shortcutId); 597 } 598 } 599 }); 600 items.push({ 601 id: 'citrus-icon-remove', 602 label: 'Remove from desktop',
603 icon: 'fa-solid fa-trash-can', 604 action: function () { 605 if (global.CitrusDesktop && global.CitrusDesktop.removeDesktopShortcut) { 606 global.CitrusDesktop.removeDesktopShortcut(shortcutId); 607 } 608 } 609 }); 610 if (citrusApp && citrusApp.userFolderId) { 611 items.push({ 612 id: 'citrus-userfolder-delete', 613 label: 'Delete folder', 614 icon: 'fa-solid fa-folder-minus', 615 action: function () { 616 var ufId = citrusApp.userFolderId; 617 if (!global.CitrusUserFolders) return; 618 function doDelete() { 619 global.CitrusUserFolders.deleteFolder(ufId, true); 620 if (global.CitrusDesktop && global.CitrusDesktop.removeDesktopShortcut) { 621 global.CitrusDesktop.removeDesktopShortcut(shortcutId); 622 } 623 if (global.CitrusDesktop && global.CitrusDesktop.refreshDesktopIcons) { 624 global.CitrusDesktop.refreshDesktopIcons(); 625 } 626 } 627 if (global.CitrusUserFolders.countContents(ufId) > 0) { 628 shellConfirm({ 629 title: 'Delete folder?', 630 message: 'This folder is not empty. Delete it anyway? Contents will return to default locations.', 631 confirmLabel: 'Delete', 632 danger: true 633 }).then(function (ok) { 634 if (ok) doDelete(); 635 }); 636 return; 637 } 638 doDelete(); 639 } 640 }); 641 } 642 items.push({ type: 'divider' }); 643 } 644 } 645 646 var folderIcon = citrusContextClosest(ctx, '.citrus-folder-icon'); 647 if (folderIcon && isCitrusOsPage() && !iconBtn) { 648 var entryType = folderIcon.getAttribute('data-entry-type'); 649 var entryHref = folderIcon.getAttribute('data-entry-href'); 650 var entryId = folderIcon.getAttribute('data-entry-id'); 651 var userFolderId = folderIcon.getAttribute('data-user-folder-id'); 652 var folderApp = entryType === 'app' && entryHref && 653 global.CitrusAppRegistry && global.CitrusAppRegistry.getAppByHref 654 ? global.CitrusAppRegistry.getAppByHref(entryHref) 655 : null; 656 if (entryType === 'app' && entryHref) { 657 items.push({ 658 id: 'citrus-folder-icon-open', 659 label: 'Open', 660 icon: 'fa-solid fa-folder-open', 661 defaultFocus: true, 662 action: function () { 663 if (global.CitrusDesktop && typeof global.CitrusDesktop.openHref === 'function') { 664 global.CitrusDesktop.openHref(entryHref); 665 } 666 } 667 }); 668 appendCitrusPinTaskbarItems(items, ctx, folderApp); 669 items.push({ 670 id: 'citrus-folder-add-desktop', 671 label: 'Add to desktop', 672 icon: 'fa-solid fa-arrow-up-from-bracket', 673 action: function () { 674 if (!global.CitrusDesktopShortcuts) return; 675 var key = global.CitrusDesktopShortcuts.targetKeyFromFolderEntry({ 676 type: entryType, 677 href: entryHref, 678 id: entryId 679 }); 680 if (global.CitrusDesktop && global.CitrusDesktop.addDesktopShortcut) { 681 global.CitrusDesktop.addDesktopShortcut(key); 682 } 683 } 684 }); 685 items.push({ type: 'divider' }); 686 } else if (entryType === 'note') { 687 items.push({ 688 id: 'citrus-folder-note-desktop', 689 label: 'Add to desktop', 690 icon: 'fa-solid fa-arrow-up-from-bracket', 691 action: function () { 692 if (global.CitrusDesktop && global.CitrusDesktop.addDesktopShortcut) { 693 global.CitrusDesktop.addDesktopShortcut('note:' + entryId); 694 } 695 } 696 }); 697 items.push({ type: 'divider' }); 698 } else if (entryType === 'folder') { 699 items.push({ 700 id: 'citrus-folder-subfolder-desktop', 701 label: 'Add to desktop', 702 icon: 'fa-solid fa-arrow-up-from-bracket', 703 action: function () { 704 var key = userFolderId ? ('user:' + userFolderId) : entryId; 705 if (global.CitrusDesktop && global.CitrusDesktop.addDesktopShortcut) { 706 global.CitrusDesktop.addDesktopShortcut(key); 707 } 708 } 709 }); 710 items.push({ type: 'divider' }); 711 } 712 } 713 714 var folderShell = citrusContextClosest(ctx, '.citrus-folder-window'); 715 if (folderShell && isCitrusOsPage() && !folderIcon && !iconBtn) { 716 var shellFolderId = folderShell.getAttribute('data-folder-id'); 717 var folderPath = global.CitrusFolderWindow && global.CitrusFolderWindow.getFolderPath 718 ? global.CitrusFolderWindow.getFolderPath(shellFolderId) 719 : []; 720 items.push({ 721 id: 'citrus-folder-new-folder', 722 label: 'New folder', 723 icon: 'fa-solid fa-folder-plus', 724 action: function () { 725 shellPrompt('Folder name', 'New folder').then(function (name) { 726 if (name == null) return; 727 if (global.CitrusDesktop && global.CitrusDesktop.createNestedUserFolder) { 728 global.CitrusDesktop.createNestedUserFolder(shellFolderId, folderPath, name); 729 } 730 }); 731 } 732 }); 733 items.push({ type: 'divider' }); 734 } 735 736 var desktopSurface = citrusContextClosest(ctx, '#citrusos-desktop'); 737 if (desktopSurface && isCitrusOsPage() && !iconBtn && !folderIcon && !folderShell && !inAppWindow) { 738 items.unshift({ 739 id: 'citrus-desktop-new-folder', 740 label: 'New folder', 741 icon: 'fa-solid fa-folder-plus', 742 action: function () { 743 shellPrompt('Folder name', 'New folder').then(function (name) { 744 if (name == null) return; 745 if (global.CitrusDesktop && global.CitrusDesktop.createTopLevelUserFolder) { 746 global.CitrusDesktop.createTopLevelUserFolder(name); 747 } 748 }); 749 } 750 }); 751 } 752 753 var startAppBtn = citrusContextClosest(ctx, '.citrus-start-app'); 754 if (startAppBtn && isCitrusOsPage() && !iconBtn && !folderIcon && !onWindowChrome) { 755 if (appendCitrusPinTaskbarItems(items, ctx)) { 756 items.push({ type: 'divider' }); 757 } 758 } 759 760 var widgetEl = citrusContextClosest(ctx, '.citrus-widget'); 761 if (widgetEl && isCitrusOsPage()) { 762 var widgetId = widgetEl.getAttribute('data-widget-id'); 763 var widgetType = widgetEl.getAttribute('data-widget-type'); 764 items.push({ 765 id: 'citrus-widget-front', 766 label: 'Bring widget to front', 767 icon: 'fa-solid fa-layer-group', 768 action: function () { 769 if (global.CitrusWidgets && typeof global.CitrusWidgets.focusWidget === 'function') { 770 global.CitrusWidgets.focusWidget(widgetId); 771 } 772 } 773 }); 774 items.push({ 775 id: 'citrus-widget-remove', 776 label: widgetType === 'note' ? 'Close note' : 'Remove widget', 777 icon: widgetType === 'note' ? 'fa-solid fa-xmark' : 'fa-solid fa-tras
777h-can', 778 action: function () { 779 if (global.CitrusWidgets && typeof global.CitrusWidgets.removeWidget === 'function') { 780 global.CitrusWidgets.removeWidget(widgetId); 781 } 782 } 783 }); 784 items.push({ type: 'divider' }); 785 } 786 787 if (ctx.dndFloatContainer && isCitrusOsPage()) { 788 var floatItems = []; 789 appendDndFloatChromeItems(floatItems, ctx); 790 return floatItems; 791 } 792 793 if (onWindowChrome) { 794 var chromeItems = []; 795 appendCitrusWindowChromeItems(chromeItems, ctx); 796 return chromeItems; 797 } 798 799 if (inAppWindow) { 800 appendCitrusTrimmedWindowItems(items, ctx); 801 return items; 802 } 803 804 items.push({ 805 id: 'open-settings', 806 label: 'Open settings', 807 icon: 'fa-solid fa-gear', 808 defaultFocus: true, 809 action: function () { 810 if (global.SettingsMenu && global.SettingsMenu.openSettings) { 811 global.SettingsMenu.openSettings(); 812 } 813 } 814 }); 815 816 if (isCitrusOsPage()) { 817 items = items.concat(buildCitrusDesktopMenuItems()); 818 } 819 820 items.push({ 821 id: 'navigate', 822 type: 'submenu', 823 label: isCitrusOsPage() 824 ? 'Open app' 825 : 'Navigate', 826 icon: isCitrusOsPage() 827 ? 'fa-solid fa-window-maximize' 828 : 'fa-solid fa-compass', 829 children: isCitrusOsPage() 830 ? buildCitrusAppSubmenuChildren() 831 : buildNavigateSubmenuChildren(ctx) 832 }); 833 834 items.push({ 835 id: 'followers', 836 type: 'submenu', 837 label: 'Cursor follower', 838 icon: 'fa-solid fa-paw', 839 children: buildFollowerSubmenuChildren() 840 }); 841 842 appendCitrusSharedClipboardItems(items); 843 844 items.push({ 845 id: 'reload', 846 label: 'Refresh', 847 icon: 'fa-solid fa-arrows-rotate', 848 action: function () { global.location.reload(); } 849 }); 850 851 items.push({ 852 id: 'hard-reload', 853 label: 'Hard Refresh', 854 icon: 'fa-solid fa-rotate', 855 action: function () { 856 if (global.caches && global.caches.keys) { 857 global.caches.keys().then(function (keys) { 858 return Promise.all(keys.map(function (key) { 859 return global.caches.delete(key); 860 })); 861 }).finally(function () { 862 global.location.reload(); 863 }); 864 return; 865 } 866 global.location.reload(); 867 } 868 }); 869 870 items.push({ 871 id: 'view-about', 872 label: 'About pupwife.com', 873 icon: 'fa-solid fa-circle-info', 874 action: function () { 875 if (global.CitrusDesktop && typeof global.CitrusDesktop.openHref === 'function') { 876 if (global.CitrusDesktop.openHref('/aboutme.html')) return; 877 } 878 global.location.href = 'index.html?open=aboutme.html'; 879 } 880 }); 881 882 items.push({ type: 'divider' }); 883 884 items.push({ 885 id: 'copy-url', 886 label: 'Copy page URL', 887 icon: 'fa-solid fa-link', 888 action: function () { 889 api().copyText(global.location.href, 'Page URL copied'); 890 } 891 }); 892 893 items.push({ 894 id: 'toggle-perf', 895 label: 'Performance mode', 896 icon: 'fa-solid fa-gauge-high', 897 checked: function () { return !!setting('performanceMode', false); }, 898 action: function () { 899 var next = !setting('performanceMode', false); 900 setSetting('performanceMode', next); 901 api().showToast(next ? 'Performance mode on' : 'Performance mode off'); 902 } 903 }); 904 905 return items; 906 }); 907 } 908 909 function buildCitrusAppSubmenuChildren() { 910 try { 911 if (!global.CitrusAppRegistry || typeof global.CitrusAppRegistry.getApps !== 'function') { 912 return []; 913 } 914 var apps = global.CitrusAppRegistry.getApps(); 915 if (!apps || !apps.length) return []; 916 return apps.map(function (app) { 917 return { 918 id: 'citrus-open-' + app.id, 919 label: app.label, 920 icon: NAV_ICONS[app.label] || 'fa-solid fa-window-maximize', 921 action: function () { 922 if (global.CitrusDesktop && typeof global.CitrusDesktop.openApp === 'function') { 923 global.CitrusDesktop.openApp(app.id); 924 } 925 } 926 }; 927 }); 928 } catch (err) { 929 if (typeof global.devWarn === 'function') { 930 global.devWarn('[SiteContextMenuRegistry] Citrus app submenu failed', err); 931 } 932 return []; 933 } 934 } 935 936 function buildCitrusWidgetSubmenuChildren() { 937 if (!global.CitrusWidgetRegistry || typeof global.CitrusWidgetRegistry.list !== 'function') { 938 return []; 939 } 940 var defs = global.CitrusWidgetRegistry.listGallery 941 ? global.CitrusWidgetRegistry.listGallery() 942 : global.CitrusWidgetRegistry.list().filter(function (def) { return !def.galleryHidden; }); 943 if (!defs || !defs.length) return []; 944 return defs.map(function (def) { 945 return { 946 id: 'citrus-widget-add-' + def.type, 947 label: def.type === 'note' ? 'New note' : (def.label || def.type), 948 icon: def.icon || 'fa-solid fa-puzzle-piece', 949 action: function () { 950 if (!global.CitrusWidgets) return; 951 if (def.type === 'note' && typeof global.CitrusWidgets.createNoteAndOpen === 'function') { 952 global.CitrusWidgets.createNoteAndOpen(); 953 return; 954 } 955 if (typeof global.CitrusWidgets.addWidget === 'function') { 956 global.CitrusWidgets.addWidget(def.type); 957 } 958 } 959 }; 960 }); 961 } 962 963 function buildCitrusDesktopMenuItems() { 964 return [ 965 { 966 id: 'citrus-widget-gallery', 967 label: 'Widget gallery', 968 icon: 'fa-solid fa-puzzle-piece', 969 action: function () { 970 if (global.CitrusWidgets && typeof global.CitrusWidgets.openGallery === 'function') { 971 global.CitrusWidgets.openGallery(); 972 } 973 } 974 }, 975 { 976 id: 'citrus-add-widget', 977 type: 'submenu', 978 label: 'Add widget', 979 icon: 'fa-solid fa-plus', 980 children: buildCitrusWidgetSubmenuChildren() 981 }, 982 { 983 id: 'citrus-add-app', 984 label: 'Add appâ¦', 985 icon: 'fa-solid fa-plus', 986 action: function () { 987 if (global.CitrusAppPicker && typeof global.CitrusAppPicker.open === 'function') { 988 global.CitrusAppPicker.open(); 989 } 990 } 991 }, 992 { 993 id: 'citrus-desktop-new-folder-menu', 994 label: 'New folder', 995 icon: 'fa-solid fa-folder-plus', 996 action: function () { 997 shellPrompt('Folder name', 'New folder').then(function (name) { 998 if (name == null) return; 999 if (global.CitrusDesktop && global.CitrusDesktop.createTopLevelUserFolder) { 1000 global.CitrusDesktop.createTopLevelUserFolder(name); 1001 } 1002 }); 1003 } 1004 }, 1005 { 1006 type: 'divider' 1007 }, 1008 { 1009 id: 'citrus-start-menu', 1010 label: 'Open Start menu', 1011 icon: 'fa-solid fa-bars', 1012 action: function () { 1013 var btn = global.document.getElementById('citrusos-start-btn'); 1014 if (btn) btn.click(); 1015 } 1016 }, 1017 { 1018 id: 'citrus-music', 1019 label: 'Open Music', 1020 icon: 'fa-solid fa-music', 1021 action: function () { 1022 if (global.CitrusDesktop && typeof global.CitrusDesktop.openApp === 'function') { 1023 global.CitrusDesktop.openApp('music'); 1024 } 1025 } 1026 }, 1027 { 1028 type: 'divider' 1029 }, 1030 { 1031 id: 'citrus-reset-icon-layout',
1032 label: 'Reset icon layout', 1033 icon: 'fa-solid fa-grip', 1034 action: function () { 1035 if (global.CitrusDesktop && typeof global.CitrusDesktop.resetDesktopIconLayout === 'function') { 1036 global.CitrusDesktop.resetDesktopIconLayout(); 1037 } 1038 } 1039 }, 1040 { 1041 id: 'citrus-reset-folder-layout', 1042 label: 'Reset folder layout', 1043 icon: 'fa-solid fa-folder-tree', 1044 action: function () { 1045 if (global.CitrusDesktop && typeof global.CitrusDesktop.resetFolderLayout === 'function') { 1046 global.CitrusDesktop.resetFolderLayout(); 1047 } 1048 } 1049 }, 1050 { 1051 id: 'citrus-reset-desktop-defaults', 1052 label: 'Reset desktop to defaults', 1053 icon: 'fa-solid fa-rotate-left', 1054 action: function () { 1055 shellConfirm({ 1056 title: 'Reset desktop?', 1057 message: 'Restore factory desktop shortcuts and folder layout?', 1058 confirmLabel: 'Reset', 1059 danger: true 1060 }).then(function (ok) { 1061 if (!ok) return; 1062 if (global.CitrusDesktop && typeof global.CitrusDesktop.resetDesktopToDefaults === 'function') { 1063 global.CitrusDesktop.resetDesktopToDefaults(); 1064 } 1065 }); 1066 } 1067 }, 1068 { 1069 id: 'citrus-reset-widgets', 1070 label: 'Reset widgets', 1071 icon: 'fa-solid fa-table-cells', 1072 action: function () { 1073 if (global.CitrusWidgets && typeof global.CitrusWidgets.resetLayout === 'function') { 1074 global.CitrusWidgets.resetLayout(); 1075 } 1076 } 1077 } 1078 ]; 1079 } 1080 1081 function registerHomeItems() { 1082 api().registerPage('index.html', function () { 1083 if (isCitrusOsPage()) { 1084 return []; 1085 } 1086 return [ 1087 { 1088 id: 'skip-landing', 1089 label: 'Skip landing animation', 1090 icon: 'fa-solid fa-forward', 1091 when: function () { 1092 return global.HomeLanding && global.HomeLanding.isActive && global.HomeLanding.isActive(); 1093 }, 1094 action: function () { 1095 global.HomeLanding.destroy(); 1096 api().showToast('Landing skipped'); 1097 } 1098 } 1099 ]; 1100 }); 1101 } 1102 1103 function registerMusicItems() { 1104 api().registerPage('music.html', function () { 1105 return [ 1106 { 1107 id: 'music-play-pause', 1108 label: 'Play / pause', 1109 icon: 'fa-solid fa-play', 1110 when: function () { return typeof global.musicPlayerPlayPause === 'function'; }, 1111 action: function () { global.musicPlayerPlayPause(); } 1112 }, 1113 { 1114 id: 'music-copy-now-playing', 1115 label: 'Copy now playing', 1116 icon: 'fa-solid fa-music', 1117 when: function () { return !!global.musicPlayerApi; }, 1118 action: copyMusicNowPlaying 1119 } 1120 ]; 1121 }); 1122 } 1123 1124 function registerFriendsItems() { 1125 api().registerPage('friends.html', function () { 1126 return [ 1127 { 1128 id: 'friends-random', 1129 label: 'Visit random friend', 1130 icon: 'fa-solid fa-shuffle', 1131 when: function () { 1132 return !!global.document.querySelector('.friends-float-container .friend-link'); 1133 }, 1134 action: function () { 1135 clickRandom('.friends-float-container .friend-link', 'No friend links foun
1135d'); 1136 } 1137 } 1138 ]; 1139 }); 1140 } 1141 1142 function registerSillyItems() { 1143 api().registerPage('silly.html', function () { 1144 return [ 1145 { 1146 id: 'silly-random-tool', 1147 label: 'Open random silly tool', 1148 icon: 'fa-solid fa-dice', 1149 action: function () { 1150 clickRandom('#content .tool-button', 'No tools found'); 1151 } 1152 }, 1153 { 1154 id: 'silly-heathcliff', 1155 label: 'View today\'s Heathcliff', 1156 icon: 'fa-solid fa-newspaper', 1157 when: function () { 1158 var comic = global.document.getElementById('heathcliff-comic'); 1159 return comic && comic.style.display !== 'none' && comic.src; 1160 }, 1161 action: function () { 1162 var comic = global.document.getElementById('heathcliff-comic'); 1163 if (comic) comic.click(); 1164 } 1165 } 1166 ]; 1167 }); 1168 } 1169 1170 function registerCookbookItems() { 1171 api().registerPage('cookbook.html', function () { 1172 return [ 1173 { 1174 id: 'cookbook-metric', 1175 label: 'Use metric units', 1176 icon: 'fa-solid fa-weight-scale', 1177 when: function () { 1178 var btn = global.document.getElementById('unit-measure-toggle'); 1179 return !!btn && btn.textContent !== 'Metric'; 1180 }, 1181 action: function () { 1182 var btn = global.document.getElementById('unit-measure-toggle'); 1183 if (btn) btn.click(); 1184 api().showToast('Metric units'); 1185 } 1186 }, 1187 { 1188 id: 'cookbook-imperial', 1189 label: 'Use imperial units', 1190 icon: 'fa-solid fa-kitchen-set', 1191 when: function () { 1192 var btn = global.document.getElementById('unit-measure-toggle'); 1193 return !!btn && btn.textContent !== 'Imperial'; 1194 }, 1195 action: function () { 1196 var btn = global.document.getElementById('unit-measure-toggle'); 1197 if (btn) btn.click(); 1198 api().showToast('Imperial units'); 1199 } 1200 } 1201 ]; 1202 }); 1203 } 1204 1205 function registerShrinesItems() { 1206 api().registerPage('shrines.html', function () { 1207 return [ 1208 { 1209 id: 'shrines-random', 1210 label: 'Open random shrine', 1211 icon: 'fa-solid fa-star', 1212 when: function () { return !!global.document.querySelector('.db-shrine-card'); }, 1213 action: function () { 1214 clickRandom('.db-shrine-card', 'No shrines found'); 1215 } 1216 } 1217 ]; 1218 }); 1219 } 1220 1221 function registerArcadeItems() { 1222 api().registerPage('arcade.html', function () { 1223 return [ 1224 { 1225 id: 'arcade-random', 1226 label: 'Open random game', 1227 icon: 'fa-solid fa-dice', 1228 action: function () { 1229 clickRandom('#content a[href*=".html"], #content .db-widget a[href]', 'No game links found'); 1230 } 1231 } 1232 ]; 1233 }); 1234 } 1235 1236 function registerCasinoItems() { 1237 api().registerPage('casino.html', function () { 1238 return [ 1239 { 1240 id: 'casino-random-game', 1241 label: 'Open random casino game', 1242 icon: 'fa-solid fa-dice', 1243 action: function () { 1244 clickRandom('.casino-games .casino-game-link:not(.casino-game-link--wor
1244k)', 'No casino games found'); 1245 } 1246 }, 1247 { 1248 id: 'casino-daily-claim', 1249 label: 'Claim daily coins', 1250 icon: 'fa-solid fa-coins', 1251 when: function () { 1252 var btn = global.document.querySelector('[data-casino-daily-claim]'); 1253 return btn && !btn.hidden && !btn.disabled; 1254 }, 1255 action: function () { 1256 var btn = global.document.querySelector('[data-casino-daily-claim]'); 1257 if (btn) btn.click(); 1258 } 1259 }, 1260 { 1261 id: 'casino-sign-in', 1262 label: 'Sign in to casino', 1263 icon: 'fa-solid fa-right-to-bracket', 1264 when: function () { 1265 var banner = global.document.querySelector('[data-casino-guest-banner]'); 1266 return banner && !banner.hidden; 1267 }, 1268 action: function () { 1269 var btn = global.document.querySelector('[data-casino-sign-in]'); 1270 if (btn) btn.click(); 1271 } 1272 } 1273 ]; 1274 }); 1275 } 1276 1277 function registerStudyHallItems() { 1278 api().registerPage('studyhall.html', function () { 1279 return [ 1280 { 1281 id: 'studyhall-open', 1282 label: 'Open Study Hall', 1283 icon: 'fa-solid fa-graduation-cap', 1284 when: function () { 1285 return !!(global.CitrusDesktop && global.document.body.classList.contains('page-citrusos')); 1286 }, 1287 action: function () { 1288 global.CitrusDesktop.openApp('studyhall'); 1289 } 1290 } 1291 ]; 1292 }); 1293 } 1294 1295 function registerBlogItems() { 1296 api().registerPage('blog.html', function () { 1297 return [ 1298 { 1299 id: 'blog-random-post', 1300 label: 'Open random blog post', 1301 icon: 'fa-solid fa-shuffle', 1302 when: function () { return !!global.document.querySelector('.blog-post-card'); }, 1303 action: function () { 1304 clickRandom('.blog-post-card', 'No posts found'); 1305 } 1306 } 1307 ]; 1308 }); 1309 } 1310 1311 function registerToolItems() { 1312 api().registerPage('timezones.html', function () { 1313 return [ 1314 { 1315 id: 'tz-copy-board', 1316 label: 'Copy meeting board text', 1317 icon: 'fa-solid fa-copy', 1318 when: function () { 1319 return !!global.document.querySelector('.tz-board, #tz-board, [data-tz-board]'); 1320 }, 1321 action: function () { 1322 var el = global.document.querySelector('.tz-board-output, [data-tz-export]'); 1323 if (el && el.textContent) { 1324 api().copyText(el.textContent.trim(), 'Board copied'); 1325 } else { 1326 api().showToast('Nothing to copy yet'); 1327 } 1328 } 1329 } 1330 ]; 1331 }); 1332 } 1333 1334 function bootRegistry() { 1335 SCM = global.SiteContextMenu; 1336 if (!SCM || registryBooted) return; 1337 registryBooted = true; 1338 1339 registerGlobalItems(); 1340 registerHomeItems(); 1341 registerMusicItems(); 1342 registerFriendsItems(); 1343 registerSillyItems(); 1344 registerCookbookItems(); 1345 registerShrinesItems(); 1346 registerArcadeItems(); 1347 registerCasinoItems(); 1348 registerStudyHallItems(); 1349 registerBlogItems(); 1350 registerToolItems(); 1351 } 1352 1353 function ensureRegistryBoot() { 1354 if (global.SiteContextMenu) { 1355 bootRegistry(); 1356 if (typeof global.SiteContextMenu.init === 'function') { 1357 global.SiteContextMenu.init(); 1358 } 1359 return; 1360 } 1361 registryBootAttempts += 1; 1362 if (registryBootAttempts > REGISTRY_BOOT_MAX_ATTEMPTS) return; 1363 if (global.document.readyState === 'loading') { 1364 global.document.addEventListener('DOMContentLoaded', ensureRegistryBoot, { once: true }); 1365 return; 1366 } 1367 global.setTimeout(ensureRegistryBoot, 50); 1368 } 1369 1370 ensureRegistryBoot(); 1371})(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.