diff --git a/js/activity.js b/js/activity.js index cc4389b303..89a5b4d771 100644 --- a/js/activity.js +++ b/js/activity.js @@ -38,7 +38,7 @@ TENOR, TITLESTRING, Toolbar, Trashcan, TREBLE, Turtles, TURTLESVG, updatePluginObj, ZERODIVIDEERRORMSG, GRAND_G, GRAND_F, SHARP, FLAT, buildScale, TREBLE_F, TREBLE_G, GIFAnimator, - MUSICALMODES + MUSICALMODES, waitForReadiness */ /* @@ -7676,7 +7676,24 @@ define(["domReady!"].concat(MYDEFINES), doc => { } else { // Race condition in Firefox: non-AMD scripts might not have // finished global assignment yet. - setTimeout(initialize, 10); + // Use readiness-based initialization for Firefox for better performance + if (typeof jQuery !== "undefined" && jQuery.browser && jQuery.browser.mozilla) { + waitForReadiness( + () => { + activity.setupDependencies(); + activity.domReady(doc); + activity.doContextMenus(); + activity.doPluginsAndPaletteCols(); + }, + { + maxWait: 10000, + minWait: 500, + checkInterval: 100 + } + ); + } else { + setTimeout(initialize, 10); + } } }; initialize(); diff --git a/js/utils/utils.js b/js/utils/utils.js index c4a4981f1a..331bf0feeb 100644 --- a/js/utils/utils.js +++ b/js/utils/utils.js @@ -39,7 +39,7 @@ oneHundredToFraction, prepareMacroExports, preparePluginExports, processMacroData, processRawPluginData, rationalSum, rgbToHex, safeSVG, toFixed2, toTitleCase, windowHeight, windowWidth, - fnBrowserDetect + fnBrowserDetect, waitForReadiness */ /** @@ -343,6 +343,68 @@ function doBrowserCheck() { jQuery.browser = browser; } +/** + * Wait for critical dependencies to be ready before calling callback. + * Uses polling with exponential backoff and maximum timeout. + * This replaces the arbitrary 5-second delay for Firefox with actual readiness checks. + * + * @param {Function} callback - The function to call when ready + * @param {Object} options - Configuration options + * @param {number} options.maxWait - Maximum wait time in ms (default: 10000) + * @param {number} options.minWait - Minimum wait time in ms (default: 500) + * @param {number} options.checkInterval - Initial check interval in ms (default: 100) + */ +function waitForReadiness(callback, options = {}) { + const { maxWait = 10000, minWait = 500, checkInterval = 100 } = options; + const startTime = Date.now(); + + /** + * Check if critical dependencies and DOM elements are ready + * @returns {boolean} True if all critical dependencies are loaded + */ + const isReady = () => { + // Check if critical JavaScript libraries are loaded + const createjsLoaded = typeof createjs !== "undefined" && createjs.Stage; + const howlerLoaded = typeof Howler !== "undefined"; + const jqueryLoaded = typeof jQuery !== "undefined"; + + // Check if critical DOM elements exist + const canvas = document.getElementById("myCanvas"); + const loader = document.getElementById("loader"); + const toolbars = document.getElementById("toolbars"); + const domReady = canvas && loader && toolbars; + + return createjsLoaded && howlerLoaded && jqueryLoaded && domReady; + }; + + /** + * Polling function that checks readiness and calls callback when ready + */ + const check = () => { + const elapsed = Date.now() - startTime; + + if (elapsed >= minWait && isReady()) { + // Ready! Initialize the app + // eslint-disable-next-line no-console + console.log(`[Firefox] Initialized in ${elapsed}ms (readiness-based)`); + callback(); + } else if (elapsed >= maxWait) { + // Timeout - initialize anyway as fallback + // eslint-disable-next-line no-console + console.warn( + `[Firefox] Initialization timed out after ${maxWait}ms, proceeding anyway` + ); + callback(); + } else { + // Not ready yet, check again on next animation frame + requestAnimationFrame(check); + } + }; + + // Start the readiness check loop + requestAnimationFrame(check); +} + // Check for Internet Explorer window.onload = () => {