diff options
Diffstat (limited to 'contexts/data/lib/closure-library/closure/goog/module/modulemanager.js')
-rw-r--r-- | contexts/data/lib/closure-library/closure/goog/module/modulemanager.js | 1257 |
1 files changed, 0 insertions, 1257 deletions
diff --git a/contexts/data/lib/closure-library/closure/goog/module/modulemanager.js b/contexts/data/lib/closure-library/closure/goog/module/modulemanager.js deleted file mode 100644 index 6b445e1..0000000 --- a/contexts/data/lib/closure-library/closure/goog/module/modulemanager.js +++ /dev/null @@ -1,1257 +0,0 @@ -// Copyright 2008 The Closure Library Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS-IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/** - * @fileoverview A singleton object for managing Javascript code modules. - * - */ - -goog.provide('goog.module.ModuleManager'); -goog.provide('goog.module.ModuleManager.CallbackType'); -goog.provide('goog.module.ModuleManager.FailureType'); - -goog.require('goog.Disposable'); -goog.require('goog.array'); -goog.require('goog.asserts'); -goog.require('goog.async.Deferred'); -goog.require('goog.debug.Logger'); -goog.require('goog.debug.Trace'); -goog.require('goog.module.ModuleInfo'); -goog.require('goog.module.ModuleLoadCallback'); -goog.require('goog.object'); - - - -/** - * The ModuleManager keeps track of all modules in the environment. - * Since modules may not have their code loaded, we must keep track of them. - * @constructor - * @extends {goog.Disposable} - */ -goog.module.ModuleManager = function() { - goog.Disposable.call(this); - - /** - * A mapping from module id to ModuleInfo object. - * @type {Object} - * @private - */ - this.moduleInfoMap_ = {}; - - /** - * The ids of the currently loading modules. If batch mode is disabled, then - * this array will never contain more than one element at a time. - * @type {Array.<string>} - * @private - */ - this.loadingModuleIds_ = []; - - /** - * The requested ids of the currently loading modules. This does not include - * module dependencies that may also be loading. - * @type {Array.<string>} - * @private - */ - this.requestedLoadingModuleIds_ = []; - - /** - * A queue of the ids of requested but not-yet-loaded modules. The zero - * position is the front of the queue. This is a 2-D array to group modules - * together with other modules that should be batch loaded with them, if - * batch loading is enabled. - * @type {Array.<Array.<string>>} - * @private - */ - this.requestedModuleIdsQueue_ = []; - - /** - * The ids of the currently loading modules which have been initiated by user - * actions. - * @type {Array.<string>} - * @private - */ - this.userInitiatedLoadingModuleIds_ = []; - - /** - * A map of callback types to the functions to call for the specified - * callback type. - * @type {Object} - * @private - */ - this.callbackMap_ = {}; - - /** - * Module info for the base module (the one that contains the module - * manager code), which we set as the loading module so one can - * register initialization callbacks in the base module. - * - * The base module is considered loaded when #setAllModuleInfo is called or - * #setModuleContext is called, whichever comes first. - * - * @type {goog.module.ModuleInfo} - * @private - */ - this.baseModuleInfo_ = new goog.module.ModuleInfo([], ''); - - /** - * The module that is currently loading, or null if not loading anything. - * @type {goog.module.ModuleInfo} - * @private - */ - this.currentlyLoadingModule_ = this.baseModuleInfo_; -}; -goog.inherits(goog.module.ModuleManager, goog.Disposable); -goog.addSingletonGetter(goog.module.ModuleManager); - - -/** -* The type of callbacks that can be registered with the module manager,. -* @enum {string} -*/ -goog.module.ModuleManager.CallbackType = { - /** - * Fired when an error has occurred. - */ - ERROR: 'error', - - /** - * Fired when it becomes idle and has no more module loads to process. - */ - IDLE: 'idle', - - /** - * Fired when it becomes active and has module loads to process. - */ - ACTIVE: 'active', - - /** - * Fired when it becomes idle and has no more user-initiated module loads to - * process. - */ - USER_IDLE: 'userIdle', - - /** - * Fired when it becomes active and has user-initiated module loads to - * process. - */ - USER_ACTIVE: 'userActive' -}; - - -/** - * A non-HTTP status code indicating a corruption in loaded module. - * This should be used by a ModuleLoader as a replacement for the HTTP code - * given to the error handler function to indicated that the module was - * corrupted. - * This will set the forceReload flag on the loadModules method when retrying - * module loading. - * @type {number} - */ -goog.module.ModuleManager.CORRUPT_RESPONSE_STATUS_CODE = 8001; - - -/** - * A logger. - * @type {goog.debug.Logger} - * @private - */ -goog.module.ModuleManager.prototype.logger_ = goog.debug.Logger.getLogger( - 'goog.module.ModuleManager'); - - -/** - * Whether the batch mode (i.e. the loading of multiple modules with just one - * request) has been enabled. - * @type {boolean} - * @private - */ -goog.module.ModuleManager.prototype.batchModeEnabled_ = false; - - -/** - * A loader for the modules that implements loadModules(ids, moduleInfoMap, - * opt_successFn, opt_errorFn, opt_timeoutFn, opt_forceReload) method. - * @type {goog.module.AbstractModuleLoader} - * @private - */ -goog.module.ModuleManager.prototype.loader_ = null; - - -// TODO(user): Remove tracer. -/** - * Tracer that measures how long it takes to load a module. - * @type {?number} - * @private - */ -goog.module.ModuleManager.prototype.loadTracer_ = null; - - -/** - * The number of consecutive failures that have happened upon module load - * requests. - * @type {number} - * @private - */ -goog.module.ModuleManager.prototype.consecutiveFailures_ = 0; - - -/** - * Determines if the module manager was just active before the processing of - * the last data. - * @type {boolean} - * @private - */ -goog.module.ModuleManager.prototype.lastActive_ = false; - - -/** - * Determines if the module manager was just user active before the processing - * of the last data. The module manager is user active if any of the - * user-initiated modules are loading or queued up to load. - * @type {boolean} - * @private - */ -goog.module.ModuleManager.prototype.userLastActive_ = false; - - -/** - * The module context needed for module initialization. - * @type {Object} - * @private - */ -goog.module.ModuleManager.prototype.moduleContext_ = null; - - -/** - * Sets the batch mode as enabled or disabled for the module manager. - * @param {boolean} enabled Whether the batch mode is to be enabled or not. - */ -goog.module.ModuleManager.prototype.setBatchModeEnabled = function( - enabled) { - this.batchModeEnabled_ = enabled; -}; - - -/** - * Sets the module info for all modules. Should only be called once. - * - * @param {Object.<Array.<string>>} infoMap An object that contains a mapping - * from module id (String) to list of required module ids (Array). - */ -goog.module.ModuleManager.prototype.setAllModuleInfo = function(infoMap) { - for (var id in infoMap) { - this.moduleInfoMap_[id] = new goog.module.ModuleInfo(infoMap[id], id); - } - this.maybeFinishBaseLoad_(); -}; - - -/** - * Sets the module info for all modules. Should only be called once. Also - * marks modules that are currently being loaded. - * - * @param {string=} opt_info A string representation of the module dependency - * graph, in the form: module1:dep1,dep2/module2:dep1,dep2 etc. - * Where depX is the base-36 encoded position of the dep in the module list. - * @param {Array.<string>=} opt_loadingModuleIds A list of moduleIds that - * are currently being loaded. - */ -goog.module.ModuleManager.prototype.setAllModuleInfoString = function( - opt_info, opt_loadingModuleIds) { - if (!goog.isString(opt_info)) { - // The call to this method is generated in two steps, the argument is added - // after some of the compilation passes. This means that the initial code - // doesn't have any arguments and causes compiler errors. We make it - // optional to satisfy this constraint. - return; - } - - var modules = opt_info.split('/'); - var moduleIds = []; - - // Split the string into the infoMap of id->deps - for (var i = 0; i < modules.length; i++) { - var parts = modules[i].split(':'); - var id = parts[0]; - var deps; - if (parts[1]) { - deps = parts[1].split(','); - for (var j = 0; j < deps.length; j++) { - var index = parseInt(deps[j], 36); - goog.asserts.assert( - moduleIds[index], 'No module @ %s, dep of %s @ %s', index, id, i); - deps[j] = moduleIds[index]; - } - } else { - deps = []; - } - moduleIds.push(id); - this.moduleInfoMap_[id] = new goog.module.ModuleInfo(deps, id); - } - if (opt_loadingModuleIds) { - goog.array.extend(this.loadingModuleIds_, opt_loadingModuleIds); - } - this.maybeFinishBaseLoad_(); -}; - - -/** - * Gets a module info object by id. - * @param {string} id A module identifier. - * @return {goog.module.ModuleInfo} The module info. - */ -goog.module.ModuleManager.prototype.getModuleInfo = function(id) { - return this.moduleInfoMap_[id]; -}; - - -/** - * Sets the module uris. - * - * @param {Object} moduleUriMap The map of id/uris pairs for each module. - */ -goog.module.ModuleManager.prototype.setModuleUris = function(moduleUriMap) { - for (var id in moduleUriMap) { - this.moduleInfoMap_[id].setUris(moduleUriMap[id]); - } -}; - - -/** - * Gets the application-specific module loader. - * @return {goog.module.AbstractModuleLoader} An object that has a - * loadModules(ids, moduleInfoMap, opt_successFn, opt_errFn, - * opt_timeoutFn, opt_forceReload) method. - */ -goog.module.ModuleManager.prototype.getLoader = function() { - return this.loader_; -}; - - -/** - * Sets the application-specific module loader. - * @param {goog.module.AbstractModuleLoader} loader An object that has a - * loadModules(ids, moduleInfoMap, opt_successFn, opt_errFn, - * opt_timeoutFn, opt_forceReload) method. - */ -goog.module.ModuleManager.prototype.setLoader = function(loader) { - this.loader_ = loader; -}; - - -/** - * Gets the module context to use to initialize the module. - * @return {Object} The context. - */ -goog.module.ModuleManager.prototype.getModuleContext = function() { - return this.moduleContext_; -}; - - -/** - * Sets the module context to use to initialize the module. - * @param {Object} context The context. - */ -goog.module.ModuleManager.prototype.setModuleContext = function(context) { - this.moduleContext_ = context; - this.maybeFinishBaseLoad_(); -}; - - -/** - * Determines if the ModuleManager is active - * @return {boolean} TRUE iff the ModuleManager is active (i.e., not idle). - */ -goog.module.ModuleManager.prototype.isActive = function() { - return this.loadingModuleIds_.length > 0; -}; - - -/** - * Determines if the ModuleManager is user active - * @return {boolean} TRUE iff the ModuleManager is user active (i.e., not idle). - */ -goog.module.ModuleManager.prototype.isUserActive = function() { - return this.userInitiatedLoadingModuleIds_.length > 0; -}; - - -/** - * Dispatches an ACTIVE or IDLE event if necessary. - * @private - */ -goog.module.ModuleManager.prototype.dispatchActiveIdleChangeIfNeeded_ = - function() { - var lastActive = this.lastActive_; - var active = this.isActive(); - if (active != lastActive) { - this.executeCallbacks_(active ? - goog.module.ModuleManager.CallbackType.ACTIVE : - goog.module.ModuleManager.CallbackType.IDLE); - - // Flip the last active value. - this.lastActive_ = active; - } - - // Check if the module manager is user active i.e., there are user initiated - // modules being loaded or queued up to be loaded. - var userLastActive = this.userLastActive_; - var userActive = this.isUserActive(); - if (userActive != userLastActive) { - this.executeCallbacks_(userActive ? - goog.module.ModuleManager.CallbackType.USER_ACTIVE : - goog.module.ModuleManager.CallbackType.USER_IDLE); - - // Flip the last user active value. - this.userLastActive_ = userActive; - } -}; - - -/** - * Preloads a module after a short delay. - * - * @param {string} id The id of the module to preload. - * @param {number=} opt_timeout The number of ms to wait before adding the - * module id to the loading queue (defaults to 0 ms). Note that the module - * will be loaded asynchronously regardless of the value of this parameter. - * @return {goog.async.Deferred} A deferred object. - */ -goog.module.ModuleManager.prototype.preloadModule = function( - id, opt_timeout) { - var d = new goog.async.Deferred(); - window.setTimeout( - goog.bind(this.addLoadModule_, this, id, d), - opt_timeout || 0); - return d; -}; - - -/** - * Prefetches a JavaScript module and its dependencies, which means that the - * module will be downloaded, but not evaluated. To complete the module load, - * the caller should also call load or execOnLoad after prefetching the module. - * - * @param {string} id The id of the module to prefetch. - */ -goog.module.ModuleManager.prototype.prefetchModule = function(id) { - var moduleInfo = this.getModuleInfo(id); - if (moduleInfo.isLoaded() || this.isModuleLoading(id)) { - throw Error('Module load already requested: ' + id); - } else if (this.batchModeEnabled_) { - throw Error('Modules prefetching is not supported in batch mode'); - } else { - var idWithDeps = this.getNotYetLoadedTransitiveDepIds_(id); - for (var i = 0; i < idWithDeps.length; i++) { - this.loader_.prefetchModule(idWithDeps[i], - this.moduleInfoMap_[idWithDeps[i]]); - } - } -}; - - -/** - * Loads a single module for use with a given deferred. - * - * @param {string} id The id of the module to load. - * @param {goog.async.Deferred} d A deferred object. - * @private - */ -goog.module.ModuleManager.prototype.addLoadModule_ = function(id, d) { - var moduleInfo = this.getModuleInfo(id); - if (moduleInfo.isLoaded()) { - d.callback(this.moduleContext_); - return; - } - - this.registerModuleLoadCallbacks_(id, moduleInfo, false, d); - if (!this.isModuleLoading(id)) { - this.loadModulesOrEnqueue_([id]); - } -}; - - -/** - * Loads a list of modules or, if some other module is currently being loaded, - * appends the ids to the queue of requested module ids. Registers callbacks a - * module that is currently loading and returns a fired deferred for a module - * that is already loaded. - * - * @param {Array.<string>} ids The id of the module to load. - * @param {boolean=} opt_userInitiated If the load is a result of a user action. - * @return {Object.<!goog.async.Deferred>} A mapping from id (String) to - * deferred objects that will callback or errback when the load for that - * id is finished. - * @private - */ -goog.module.ModuleManager.prototype.loadModulesOrEnqueueIfNotLoadedOrLoading_ = - function(ids, opt_userInitiated) { - var uniqueIds = []; - goog.array.removeDuplicates(ids, uniqueIds); - var idsToLoad = []; - var deferredMap = {}; - for (var i = 0; i < uniqueIds.length; i++) { - var id = uniqueIds[i]; - var moduleInfo = this.getModuleInfo(id); - var d = new goog.async.Deferred(); - deferredMap[id] = d; - if (moduleInfo.isLoaded()) { - d.callback(this.moduleContext_); - } else { - this.registerModuleLoadCallbacks_(id, moduleInfo, !!opt_userInitiated, d); - if (!this.isModuleLoading(id)) { - idsToLoad.push(id); - } - } - } - - // If there are ids to load, load them, otherwise, they are all loading or - // loaded. - if (idsToLoad.length > 0) { - this.loadModulesOrEnqueue_(idsToLoad); - } - return deferredMap; -}; - - -/** - * Registers the callbacks and handles logic if it is a user initiated module - * load. - * - * @param {string} id The id of the module to possibly load. - * @param {!goog.module.ModuleInfo} moduleInfo The module identifier for the - * given id. - * @param {boolean} userInitiated If the load was user initiated. - * @param {goog.async.Deferred} d A deferred object. - * @private - */ -goog.module.ModuleManager.prototype.registerModuleLoadCallbacks_ = - function(id, moduleInfo, userInitiated, d) { - moduleInfo.registerCallback(d.callback, d); - moduleInfo.registerErrback(function(err) { d.errback(Error(err)); }); - // If it's already loading, we don't have to do anything besides handle - // if it was user initiated - if (this.isModuleLoading(id)) { - if (userInitiated) { - this.logger_.info('User initiated module already loading: ' + id); - this.addUserInitiatedLoadingModule_(id); - this.dispatchActiveIdleChangeIfNeeded_(); - } - } else { - if (userInitiated) { - this.logger_.info('User initiated module load: ' + id); - this.addUserInitiatedLoadingModule_(id); - } else { - this.logger_.info('Initiating module load: ' + id); - } - } -}; - - -/** - * Initiates loading of a list of modules or, if a module is currently being - * loaded, appends the modules to the queue of requested module ids. - * - * The caller should verify that the requested modules are not already loaded or - * loading. {@link #loadModulesOrEnqueueIfNotLoadedOrLoading_} is a more lenient - * alternative to this method. - * - * @param {Array.<string>} ids The ids of the modules to load. - * @private - */ -goog.module.ModuleManager.prototype.loadModulesOrEnqueue_ = function(ids) { - if (goog.array.isEmpty(this.loadingModuleIds_)) { - this.loadModules_(ids); - } else { - this.requestedModuleIdsQueue_.push(ids); - this.dispatchActiveIdleChangeIfNeeded_(); - } -}; - - -/** - * Gets the amount of delay to wait before sending a request for more modules. - * If a certain module request fails, we backoff a little bit and try again. - * @return {number} Delay, in ms. - * @private - */ -goog.module.ModuleManager.prototype.getBackOff_ = function() { - // 5 seconds after one error, 20 seconds after 2. - return Math.pow(this.consecutiveFailures_, 2) * 5000; -}; - - -/** - * Loads a list of modules and any of their not-yet-loaded prerequisites. - * If batch mode is enabled, the prerequisites will be loaded together with the - * requested modules and all requested modules will be loaded at the same time. - * - * The caller should verify that the requested modules are not already loaded - * and that no modules are currently loading before calling this method. - * - * @param {Array.<string>} ids The ids of the modules to load. - * @param {boolean=} opt_isRetry If the load is a retry of a previous load - * attempt. - * @param {boolean=} opt_forceReload Whether to bypass cache while loading the - * module. - * @private - */ -goog.module.ModuleManager.prototype.loadModules_ = function( - ids, opt_isRetry, opt_forceReload) { - if (!opt_isRetry) { - this.consecutiveFailures_ = 0; - } - - // Not all modules may be loaded immediately if batch mode is not enabled. - var idsToLoadImmediately = this.processModulesForLoad_(ids); - - this.logger_.info('Loading module(s): ' + idsToLoadImmediately); - this.loadingModuleIds_ = idsToLoadImmediately; - - if (this.batchModeEnabled_) { - this.requestedLoadingModuleIds_ = ids; - } else { - // If batch mode is disabled, we treat each dependency load as a separate - // load. - this.requestedLoadingModuleIds_ = goog.array.clone(idsToLoadImmediately); - } - - // Dispatch an active/idle change if needed. - this.dispatchActiveIdleChangeIfNeeded_(); - - var loadFn = goog.bind(this.loader_.loadModules, this.loader_, - goog.array.clone(idsToLoadImmediately), - this.moduleInfoMap_, - null, - goog.bind(this.handleLoadError_, this), - goog.bind(this.handleLoadTimeout_, this), - !!opt_forceReload); - - var delay = this.getBackOff_(); - if (delay) { - window.setTimeout(loadFn, delay); - } else { - loadFn(); - } -}; - - -/** - * Processes a list of module ids for loading. Checks if any of the modules are - * already loaded and then gets transitive deps. Queues any necessary modules - * if batch mode is not enabled. Returns the list of ids that should be loaded. - * - * @param {Array.<string>} ids The ids that need to be loaded. - * @return {Array.<string>} The ids to load, including dependencies. - * @throws {Error} If the module is already loaded. - * @private - */ -goog.module.ModuleManager.prototype.processModulesForLoad_ = function(ids) { - for (var i = 0; i < ids.length; i++) { - var moduleInfo = this.moduleInfoMap_[ids[i]]; - if (moduleInfo.isLoaded()) { - throw Error('Module already loaded: ' + ids[i]); - } - } - - // Build a list of the ids of this module and any of its not-yet-loaded - // prerequisite modules in dependency order. - var idsWithDeps = []; - for (var i = 0; i < ids.length; i++) { - idsWithDeps = idsWithDeps.concat( - this.getNotYetLoadedTransitiveDepIds_(ids[i])); - } - goog.array.removeDuplicates(idsWithDeps); - - if (!this.batchModeEnabled_ && idsWithDeps.length > 1) { - var idToLoad = idsWithDeps.shift(); - this.logger_.info('Must load ' + idToLoad + ' module before ' + ids); - - // Insert the requested module id and any other not-yet-loaded prereqs - // that it has at the front of the queue. - var queuedModules = goog.array.map(idsWithDeps, function(id) { - return [id]; - }); - this.requestedModuleIdsQueue_ = queuedModules.concat( - this.requestedModuleIdsQueue_); - return [idToLoad]; - } else { - return idsWithDeps; - } -}; - - -/** - * Builds a list of the ids of the not-yet-loaded modules that a particular - * module transitively depends on, including itself. - * - * @param {string} id The id of a not-yet-loaded module. - * @return {Array.<string>} An array of module ids in dependency order that's - * guaranteed to end with the provided module id. - * @private - */ -goog.module.ModuleManager.prototype.getNotYetLoadedTransitiveDepIds_ = - function(id) { - // NOTE(user): We want the earliest occurrance of a module, not the first - // dependency we find. Therefore we strip duplicates at the end rather than - // during. See the tests for concrete examples. - var ids = [id]; - var depIds = goog.array.clone(this.getModuleInfo(id).getDependencies()); - while (depIds.length) { - var depId = depIds.pop(); - if (!this.getModuleInfo(depId).isLoaded()) { - ids.unshift(depId); - // We need to process direct dependencies first. - Array.prototype.unshift.apply(depIds, - this.getModuleInfo(depId).getDependencies()); - } - } - goog.array.removeDuplicates(ids); - return ids; -}; - - -/** - * If we are still loading the base module, consider the load complete. - * @private - */ -goog.module.ModuleManager.prototype.maybeFinishBaseLoad_ = function() { - if (this.currentlyLoadingModule_ == this.baseModuleInfo_) { - this.currentlyLoadingModule_ = null; - var error = this.baseModuleInfo_.onLoad( - goog.bind(this.getModuleContext, this)); - if (error) { - this.dispatchModuleLoadFailed_( - goog.module.ModuleManager.FailureType.INIT_ERROR); - } - } -}; - - -/** - * Records that a module was loaded. Also initiates loading the next module if - * any module requests are queued. This method is called by code that is - * generated and appended to each dynamic module's code at compilation time. - * - * @param {string} id A module id. - */ -goog.module.ModuleManager.prototype.setLoaded = function(id) { - if (this.isDisposed()) { - this.logger_.warning( - 'Module loaded after module manager was disposed: ' + id); - return; - } - - this.logger_.info('Module loaded: ' + id); - - var error = this.moduleInfoMap_[id].onLoad( - goog.bind(this.getModuleContext, this)); - if (error) { - this.dispatchModuleLoadFailed_( - goog.module.ModuleManager.FailureType.INIT_ERROR); - } - - // Remove the module id from the user initiated set if it existed there. - goog.array.remove(this.userInitiatedLoadingModuleIds_, id); - - // Remove the module id from the loading modules if it exists there. - goog.array.remove(this.loadingModuleIds_, id); - - if (goog.array.isEmpty(this.loadingModuleIds_)) { - // No more modules are currently being loaded (e.g. arriving later in the - // same HTTP response), so proceed to load the next module in the queue. - this.loadNextModules_(); - } - - // Dispatch an active/idle change if needed. - this.dispatchActiveIdleChangeIfNeeded_(); -}; - - -/** - * Gets whether a module is currently loading or in the queue, waiting to be - * loaded. - * @param {string} id A module id. - * @return {boolean} TRUE iff the module is loading. - */ -goog.module.ModuleManager.prototype.isModuleLoading = function(id) { - if (goog.array.contains(this.loadingModuleIds_, id)) { - return true; - } - for (var i = 0; i < this.requestedModuleIdsQueue_.length; i++) { - if (goog.array.contains(this.requestedModuleIdsQueue_[i], id)) { - return true; - } - } - return false; -}; - - -/** - * Requests that a function be called once a particular module is loaded. - * Client code can use this method to safely call into modules that may not yet - * be loaded. For consistency, this method always calls the function - * asynchronously -- even if the module is already loaded. Initiates loading of - * the module if necessary, unless opt_noLoad is true. - * - * @param {string} moduleId A module id. - * @param {Function} fn Function to execute when the module has loaded. - * @param {Object=} opt_handler Optional handler under whose scope to execute - * the callback. - * @param {boolean=} opt_noLoad TRUE iff not to initiate loading of the module. - * @param {boolean=} opt_userInitiated TRUE iff the loading of the module was - * user initiated. - * @param {boolean=} opt_preferSynchronous TRUE iff the function should be - * executed synchronously if the module has already been loaded. - * @return {goog.module.ModuleLoadCallback} A callback wrapper that exposes - * an abort and execute method. - */ -goog.module.ModuleManager.prototype.execOnLoad = function( - moduleId, fn, opt_handler, opt_noLoad, - opt_userInitiated, opt_preferSynchronous) { - var moduleInfo = this.moduleInfoMap_[moduleId]; - var callbackWrapper; - - if (moduleInfo.isLoaded()) { - this.logger_.info(moduleId + ' module already loaded'); - // Call async so that code paths don't change between loaded and unloaded - // cases. - callbackWrapper = new goog.module.ModuleLoadCallback(fn, opt_handler); - if (opt_preferSynchronous) { - callbackWrapper.execute(this.moduleContext_); - } else { - window.setTimeout( - goog.bind(callbackWrapper.execute, callbackWrapper), 0); - } - } else if (this.isModuleLoading(moduleId)) { - this.logger_.info(moduleId + ' module already loading'); - callbackWrapper = moduleInfo.registerCallback(fn, opt_handler); - if (opt_userInitiated) { - this.logger_.info('User initiated module already loading: ' + moduleId); - this.addUserInitiatedLoadingModule_(moduleId); - this.dispatchActiveIdleChangeIfNeeded_(); - } - } else { - this.logger_.info('Registering callback for module: ' + moduleId); - callbackWrapper = moduleInfo.registerCallback(fn, opt_handler); - if (!opt_noLoad) { - if (opt_userInitiated) { - this.logger_.info('User initiated module load: ' + moduleId); - this.addUserInitiatedLoadingModule_(moduleId); - } - this.logger_.info('Initiating module load: ' + moduleId); - this.loadModulesOrEnqueue_([moduleId]); - } - } - return callbackWrapper; -}; - - -/** - * Loads a module, returning a goog.async.Deferred for keeping track of the - * result. - * - * @param {string} moduleId A module id. - * @param {boolean=} opt_userInitiated If the load is a result of a user action. - * @return {goog.async.Deferred} A deferred object. - */ -goog.module.ModuleManager.prototype.load = function( - moduleId, opt_userInitiated) { - return this.loadModulesOrEnqueueIfNotLoadedOrLoading_( - [moduleId], opt_userInitiated)[moduleId]; -}; - - -/** - * Loads a list of modules, returning a goog.async.Deferred for keeping track of - * the result. - * - * @param {Array.<string>} moduleIds A list of module ids. - * @param {boolean=} opt_userInitiated If the load is a result of a user action. - * @return {Object.<!goog.async.Deferred>} A mapping from id (String) to - * deferred objects that will callback or errback when the load for that - * id is finished. - */ -goog.module.ModuleManager.prototype.loadMultiple = function( - moduleIds, opt_userInitiated) { - return this.loadModulesOrEnqueueIfNotLoadedOrLoading_( - moduleIds, opt_userInitiated); -}; - - -/** - * Ensures that the module with the given id is listed as a user-initiated - * module that is being loaded. This method guarantees that a module will never - * get listed more than once. - * @param {string} id Identifier of the module. - * @private - */ -goog.module.ModuleManager.prototype.addUserInitiatedLoadingModule_ = function( - id) { - if (!goog.array.contains(this.userInitiatedLoadingModuleIds_, id)) { - this.userInitiatedLoadingModuleIds_.push(id); - } -}; - - -/** - * Method called just before a module code is loaded. - * @param {string} id Identifier of the module. - */ -goog.module.ModuleManager.prototype.beforeLoadModuleCode = function(id) { - this.loadTracer_ = goog.debug.Trace.startTracer('Module Load: ' + id, - 'Module Load'); - if (this.currentlyLoadingModule_) { - this.logger_.severe('beforeLoadModuleCode called with module "' + id + - '" while module "' + - this.currentlyLoadingModule_.getId() + - '" is loading'); - } - this.currentlyLoadingModule_ = this.getModuleInfo(id); -}; - - -/** - * Method called just after module code is loaded - * @param {string} id Identifier of the module. - */ -goog.module.ModuleManager.prototype.afterLoadModuleCode = function(id) { - if (!this.currentlyLoadingModule_ || - id != this.currentlyLoadingModule_.getId()) { - this.logger_.severe('afterLoadModuleCode called with module "' + id + - '" while loading module "' + - (this.currentlyLoadingModule_ && - this.currentlyLoadingModule_.getId()) + '"'); - - } - this.currentlyLoadingModule_ = null; - goog.debug.Trace.stopTracer(this.loadTracer_); -}; - - -/** - * Register an initialization callback for the currently loading module. This - * should only be called by script that is executed during the evaluation of - * a module's javascript. This is almost equivalent to calling the function - * inline, but ensures that all the code from the currently loading module - * has been loaded. This makes it cleaner and more robust than calling the - * function inline. - * - * If this function is called from the base module (the one that contains - * the module manager code), the callback is held until #setAllModuleInfo - * is called, or until #setModuleContext is called, whichever happens first. - * - * @param {Function} fn A callback function that takes a single argument - * which is the module context. - * @param {Object=} opt_handler Optional handler under whose scope to execute - * the callback. - */ -goog.module.ModuleManager.prototype.registerInitializationCallback = function( - fn, opt_handler) { - if (!this.currentlyLoadingModule_) { - this.logger_.severe('No module is currently loading'); - } else { - this.currentlyLoadingModule_.registerEarlyCallback(fn, opt_handler); - } -}; - - -/** - * Register a late initialization callback for the currently loading module. - * Callbacks registered via this function are executed similar to - * {@see registerInitializationCallback}, but they are fired after all - * initialization callbacks are called. - * - * @param {Function} fn A callback function that takes a single argument - * which is the module context. - * @param {Object=} opt_handler Optional handler under whose scope to execute - * the callback. - */ -goog.module.ModuleManager.prototype.registerLateInitializationCallback = - function(fn, opt_handler) { - if (!this.currentlyLoadingModule_) { - this.logger_.severe('No module is currently loading'); - } else { - this.currentlyLoadingModule_.registerCallback(fn, opt_handler); - } -}; - - -/** - * Sets the constructor to use for the module object for the currently - * loading module. The constructor should derive from {@see - * goog.module.BaseModule}. - * @param {Function} fn The constructor function. - */ -goog.module.ModuleManager.prototype.setModuleConstructor = function(fn) { - if (!this.currentlyLoadingModule_) { - this.logger_.severe('No module is currently loading'); - return; - } - this.currentlyLoadingModule_.setModuleConstructor(fn); -}; - - -/** - * The possible reasons for a module load failure callback being fired. - * @enum {number} - */ -goog.module.ModuleManager.FailureType = { - /** 401 Status. */ - UNAUTHORIZED: 0, - - /** Error status (not 401) returned multiple times. */ - CONSECUTIVE_FAILURES: 1, - - /** Request timeout. */ - TIMEOUT: 2, - - /** 410 status, old code gone. */ - OLD_CODE_GONE: 3, - - /** The onLoad callbacks failed. */ - INIT_ERROR: 4 -}; - - -/** - * Handles a module load failure. - * - * @param {?number} status The error status. - * @private - */ -goog.module.ModuleManager.prototype.handleLoadError_ = function(status) { - this.consecutiveFailures_++; - if (status == 401) { - // The user is not logged in. They've cleared their cookies or logged out - // from another window. - this.logger_.info('Module loading unauthorized'); - this.dispatchModuleLoadFailed_( - goog.module.ModuleManager.FailureType.UNAUTHORIZED); - // Drop any additional module requests. - this.requestedModuleIdsQueue_.length = 0; - } else if (status == 410) { - // The requested module js is old and not available. - this.requeueBatchOrDispatchFailure_( - goog.module.ModuleManager.FailureType.OLD_CODE_GONE); - this.loadNextModules_(); - } else if (this.consecutiveFailures_ >= 3) { - this.logger_.info('Aborting after failure to load: ' + - this.loadingModuleIds_); - this.requeueBatchOrDispatchFailure_( - goog.module.ModuleManager.FailureType.CONSECUTIVE_FAILURES); - this.loadNextModules_(); - } else { - this.logger_.info('Retrying after failure to load: ' + - this.loadingModuleIds_); - var forceReload = - status == goog.module.ModuleManager.CORRUPT_RESPONSE_STATUS_CODE; - this.loadModules_(this.requestedLoadingModuleIds_, true, forceReload); - } -}; - - -/** - * Handles a module load timeout. - * @private - */ -goog.module.ModuleManager.prototype.handleLoadTimeout_ = function() { - this.logger_.info('Aborting after timeout: ' + this.loadingModuleIds_); - this.requeueBatchOrDispatchFailure_( - goog.module.ModuleManager.FailureType.TIMEOUT); - this.loadNextModules_(); -}; - - -/** - * Requeues batch loads that had more than one requested module - * (i.e. modules that were not included as dependencies) as separate loads or - * if there was only one requested module, fails that module with the received - * cause. - * @param {goog.module.ModuleManager.FailureType} cause The reason for the - * failure. - * @private - */ -goog.module.ModuleManager.prototype.requeueBatchOrDispatchFailure_ = - function(cause) { - // The load failed, so if there are more than one requested modules, then we - // need to retry each one as a separate load. Otherwise, if there is only one - // requested module, remove it and its dependencies from the queue. - if (this.requestedLoadingModuleIds_.length > 1) { - var queuedModules = goog.array.map(this.requestedLoadingModuleIds_, - function(id) { - return [id]; - }); - this.requestedModuleIdsQueue_ = queuedModules.concat( - this.requestedModuleIdsQueue_); - } else { - this.dispatchModuleLoadFailed_(cause); - } -}; - - -/** - * Handles when a module load failed. - * @param {goog.module.ModuleManager.FailureType} cause The reason for the - * failure. - * @private - */ -goog.module.ModuleManager.prototype.dispatchModuleLoadFailed_ = function( - cause) { - var failedIds = this.requestedLoadingModuleIds_; - this.loadingModuleIds_.length = 0; - // If any pending modules depend on the id that failed, - // they need to be removed from the queue. - var idsToCancel = []; - for (var i = 0; i < this.requestedModuleIdsQueue_.length; i++) { - var dependentModules = goog.array.filter( - this.requestedModuleIdsQueue_[i], - /** - * Returns true if the requestedId has dependencies on the modules that - * just failed to load. - * @param {string} requestedId The module to check for dependencies. - * @return {boolean} True if the module depends on failed modules. - */ - function(requestedId) { - var requestedDeps = this.getNotYetLoadedTransitiveDepIds_( - requestedId); - return goog.array.some(failedIds, function(id) { - return goog.array.contains(requestedDeps, id); - }); - }, this); - goog.array.extend(idsToCancel, dependentModules); - } - - // Also insert the ids that failed to load as ids to cancel. - for (var i = 0; i < failedIds.length; i++) { - goog.array.insert(idsToCancel, failedIds[i]); - } - - // Remove ids to cancel from the queues. - for (var i = 0; i < idsToCancel.length; i++) { - for (var j = 0; j < this.requestedModuleIdsQueue_.length; j++) { - goog.array.remove(this.requestedModuleIdsQueue_[j], idsToCancel[i]); - } - goog.array.remove(this.userInitiatedLoadingModuleIds_, idsToCancel[i]); - } - - // Call the functions for error notification. - var errorCallbacks = this.callbackMap_[ - goog.module.ModuleManager.CallbackType.ERROR]; - if (errorCallbacks) { - for (var i = 0; i < errorCallbacks.length; i++) { - var callback = errorCallbacks[i]; - for (var j = 0; j < idsToCancel.length; j++) { - callback(goog.module.ModuleManager.CallbackType.ERROR, idsToCancel[j], - cause); - } - } - } - - // Call the errbacks on the module info. - for (var i = 0; i < failedIds.length; i++) { - if (this.moduleInfoMap_[failedIds[i]]) { - this.moduleInfoMap_[failedIds[i]].onError(cause); - } - } - - // Clear the requested loading module ids. - this.requestedLoadingModuleIds_.length = 0; - - this.dispatchActiveIdleChangeIfNeeded_(); -}; - - -/** - * Loads the next modules on the queue. - * @private - */ -goog.module.ModuleManager.prototype.loadNextModules_ = function() { - while (this.requestedModuleIdsQueue_.length) { - // Remove modules that are already loaded. - var nextIds = goog.array.filter(this.requestedModuleIdsQueue_.shift(), - /** @param {string} id The module id. */ - function(id) { - return !this.getModuleInfo(id).isLoaded(); - }, this); - if (nextIds.length > 0) { - this.loadModules_(nextIds); - return; - } - } - - // Dispatch an active/idle change if needed. - this.dispatchActiveIdleChangeIfNeeded_(); -}; - - -/** - * The function to call if the module manager is in error. - * @param {goog.module.ModuleManager.CallbackType|Array.<goog.module.ModuleManager.CallbackType>} types - * The callback type. - * @param {Function} fn The function to register as a callback. - */ -goog.module.ModuleManager.prototype.registerCallback = function( - types, fn) { - if (!goog.isArray(types)) { - types = [types]; - } - - for (var i = 0; i < types.length; i++) { - this.registerCallback_(types[i], fn); - } -}; - - -/** - * Register a callback for the specified callback type. - * @param {goog.module.ModuleManager.CallbackType} type The callback type. - * @param {Function} fn The callback function. - * @private - */ -goog.module.ModuleManager.prototype.registerCallback_ = function(type, fn) { - var callbackMap = this.callbackMap_; - if (!callbackMap[type]) { - callbackMap[type] = []; - } - callbackMap[type].push(fn); -}; - - -/** - * Call the callback functions of the specified type. - * @param {goog.module.ModuleManager.CallbackType} type The callback type. - * @private - */ -goog.module.ModuleManager.prototype.executeCallbacks_ = function(type) { - var callbacks = this.callbackMap_[type]; - for (var i = 0; callbacks && i < callbacks.length; i++) { - callbacks[i](type); - } -}; - - -/** @override */ -goog.module.ModuleManager.prototype.disposeInternal = function() { - goog.module.ModuleManager.superClass_.disposeInternal.call(this); - - // Dispose of each ModuleInfo object. - goog.array.forEach(goog.object.getValues(this.moduleInfoMap_), goog.dispose); - this.moduleInfoMap_ = null; - this.loadingModuleIds_ = null; - this.requestedLoadingModuleIds_ = null; - this.userInitiatedLoadingModuleIds_ = null; - this.requestedModuleIdsQueue_ = null; - this.callbackMap_ = null; -}; |