text: // 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 Defines the goog.module.ModuleInfo class.
   */
  
  goog.provide('goog.module.ModuleInfo');
  
  goog.forwardDeclare('goog.loader.AbstractModuleManager.FailureType');
  goog.require('goog.Disposable');
  goog.require('goog.async.throwException');
  goog.require('goog.functions');
  goog.require('goog.html.TrustedResourceUrl');
  /** @suppress {extraRequire} */
  goog.require('goog.module');
  goog.require('goog.module.BaseModule');
  goog.require('goog.module.ModuleLoadCallback');
  
  
  
  /**
   * A ModuleInfo object is used by the ModuleManager to hold information about a
   * module of js code that may or may not yet be loaded into the environment.
   *
   * @param {Array<string>} deps Ids of the modules that must be loaded before
   *     this one. The ids must be in dependency order (i.e. if the ith module
   *     depends on the jth module, then i > j).
   * @param {string} id The module's ID.
   * @constructor
   * @extends {goog.Disposable}
   * @final
   */
  goog.module.ModuleInfo = function(deps, id) {
    goog.Disposable.call(this);
  
    /**
     * A list of the ids of the modules that must be loaded before this module.
     * @type {Array<string>}
     * @private
     */
    this.deps_ = deps;
  
    /**
     * The module's ID.
     * @type {string}
     * @private
     */
    this.id_ = id;
  
    /**
     * Callbacks to execute once this module is loaded.
     * @type {Array<goog.module.ModuleLoadCallback>}
     * @private
     */
    this.onloadCallbacks_ = [];
  
    /**
     * Callbacks to execute if the module load errors.
     * @type {Array<goog.module.ModuleLoadCallback>}
     * @private
     */
    this.onErrorCallbacks_ = [];
  
    /**
     * Early callbacks to execute once this module is loaded. Called after
     * module initialization but before regular onload callbacks.
     * @type {Array<goog.module.ModuleLoadCallback>}
     * @private
     */
    this.earlyOnloadCallbacks_ = [];
  };
  goog.inherits(goog.module.ModuleInfo, goog.Disposable);
  
  
  /**
   * The uris that can be used to retrieve this module's code.
   * @type {?Array<!goog.html.TrustedResourceUrl>}
   * @private
   */
  goog.module.ModuleInfo.prototype.uris_ = null;
  
  
  /**
   * The constructor to use to instantiate the module object after the module
   * code is loaded. This must be either goog.module.BaseModule or a subclass of
   * it.
   * @type {Function}
   * @private
   */
  goog.module.ModuleInfo.prototype.moduleConstructor_ = goog.module.BaseModule;
  
  
  /**
   * The module object. This will be null until the module is loaded.
   * @type {goog.module.BaseModule?}
   * @private
   */
  goog.module.ModuleInfo.prototype.module_ = null;
  
  
  /**
   * Gets the dependencies of this module.
   * @return {Array<string>} The ids of the modules that this module depends on.
   */
  goog.module.ModuleInfo.prototype.getDependencies = function() {
    return this.deps_;
  };
  
  
  /**
   * Gets the ID of this module.
   * @return {string} The ID.
   */
  goog.module.ModuleInfo.prototype.getId = function() {
    return this.id_;
  };
  
  
  /**
   * Sets the uris of this module.
   * @param {!Array<!goog.html.TrustedResourceUrl>} uris Uris for this module's
   *     code.
   */
  goog.module.ModuleInfo.prototype.setTrustedUris = function(uris) {
    this.uris_ = uris;
  };
  
  
  /**
   * Gets the uris of this module.
   * @return {!Array<!goog.html.TrustedResourceUrl>} Uris for this module's code.
   */
  goog.module.ModuleInfo.prototype.getUris = function() {
    if (!this.uris_) {
      this.uris_ = [];
    }
    return this.uris_;
  };
  
  
  /**
   * Sets the constructor to use to instantiate the module object after the
   * module code is loaded.
   * @param {Function} constructor The constructor of a goog.module.BaseModule
   *     subclass.
   */
  goog.module.ModuleInfo.prototype.setModuleConstructor = function(constructor) {
    if (this.moduleConstructor_ === goog.module.BaseModule) {
      this.moduleConstructor_ = constructor;
    } else {
      throw new Error('Cannot set module constructor more than once.');
    }
  };
  
  
  /**
   * Registers a function that should be called after the module is loaded. These
   * early callbacks are called after {@link Module#initialize} is called but
   * before the other 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.
   * @return {!goog.module.ModuleLoadCallback} Reference to the callback
   *     object.
   */
  goog.module.ModuleInfo.prototype.registerEarlyCallback = function(
      fn, opt_handler) {
    return this.registerCallback_(this.earlyOnloadCallbacks_, fn, opt_handler);
  };
  
  
  /**
   * Registers a function that should be called after the module is loaded.
   * @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.
   * @return {!goog.module.ModuleLoadCallback} Reference to the callback
   *     object.
   */
  goog.module.ModuleInfo.prototype.registerCallback = function(fn, opt_handler) {
    return this.registerCallback_(this.onloadCallbacks_, fn, opt_handler);
  };
  
  
  /**
   * Registers a function that should be called if the module load fails.
   * @param {Function} fn A callback function that takes a single argument which
   *    is the failure type.
   * @param {Object=} opt_handler Optional handler under whose scope to execute
   *     the callback.
   * @return {!goog.module.ModuleLoadCallback} Reference to the callback
   *     object.
   */
  goog.module.ModuleInfo.prototype.registerErrback = function(fn, opt_handler) {
    return this.registerCallback_(this.onErrorCallbacks_, fn, opt_handler);
  };
  
  
  /**
   * Registers a function that should be called after the module is loaded.
   * @param {Array<goog.module.ModuleLoadCallback>} callbacks The array to
   *     add the callback to.
   * @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.
   * @return {!goog.module.ModuleLoadCallback} Reference to the callback
   *     object.
   * @private
   */
  goog.module.ModuleInfo.prototype.registerCallback_ = function(
      callbacks, fn, opt_handler) {
    var callback = new goog.module.ModuleLoadCallback(fn, opt_handler);
    callbacks.push(callback);
    return callback;
  };
  
  
  /**
   * Determines whether the module has been loaded.
   * @return {boolean} Whether the module has been loaded.
   */
  goog.module.ModuleInfo.prototype.isLoaded = function() {
    return !!this.module_;
  };
  
  
  /**
   * Marks the current module as loaded. This is useful for subtractive module
   * loading, where occasionally we need to fallback to normal module loading,
   * and re-fetch the module graph. In this case, we need a way to tell the module
   * manager to mark all modules that are already loaded.
   */
  goog.module.ModuleInfo.prototype.setLoaded = function() {
    this.module_ = new goog.module.BaseModule();
  };
  
  
  /**
   * Gets the module.
   * @return {goog.module.BaseModule?} The module if it has been loaded.
   *     Otherwise, null.
   */
  goog.module.ModuleInfo.prototype.getModule = function() {
    return this.module_;
  };
  
  
  /**
   * Sets this module as loaded.
   * @param {function() : Object} contextProvider A function that provides the
   *     module context.
   * @return {boolean} Whether any errors occurred while executing the onload
   *     callbacks.
   */
  goog.module.ModuleInfo.prototype.onLoad = function(contextProvider) {
    // Instantiate and initialize the module object.
    var module = new this.moduleConstructor_;
    module.initialize(contextProvider());
  
    // Keep an internal reference to the module.
    this.module_ = module;
  
    // Fire any early callbacks that were waiting for the module to be loaded.
    var errors =
        !!this.callCallbacks_(this.earlyOnloadCallbacks_, contextProvider());
  
    // Fire any callbacks that were waiting for the module to be loaded.
    errors =
        errors || !!this.callCallbacks_(this.onloadCallbacks_, contextProvider());
  
    if (!errors) {
      // Clear the errbacks.
      this.onErrorCallbacks_.length = 0;
    }
  
    return errors;
  };
  
  
  /**
   * Calls the error callbacks for the module.
   * @param {goog.loader.AbstractModuleManager.FailureType} cause What caused the
   *     error.
   */
  goog.module.ModuleInfo.prototype.onError = function(cause) {
    var result = this.callCallbacks_(this.onErrorCallbacks_, cause);
    if (result) {
      // Throw an exception asynchronously. Do not let the exception leak
      // up to the caller, or it will blow up the module loading framework.
      window.setTimeout(
          goog.functions.error('Module errback failures: ' + result), 0);
    }
    this.earlyOnloadCallbacks_.length = 0;
    this.onloadCallbacks_.length = 0;
  };
  
  
  /**
   * Helper to call the callbacks after module load.
   * @param {Array<goog.module.ModuleLoadCallback>} callbacks The callbacks
   *     to call and then clear.
   * @param {*} context The module context.
   * @return {Array<*>} Any errors encountered while calling the callbacks,
   *     or null if there were no errors.
   * @private
   */
  goog.module.ModuleInfo.prototype.callCallbacks_ = function(callbacks, context) {
    // NOTE(nicksantos):
    // In practice, there are two error-handling scenarios:
    // 1) The callback does some mandatory initialization of the module.
    // 2) The callback is for completion of some optional UI event.
    // There's no good way to handle both scenarios.
    //
    // Our strategy here is to protect module manager from exceptions, so that
    // the failure of one module doesn't affect the loading of other modules.
    // Errors are thrown outside of the current stack frame, so they still
    // get reported but don't interrupt execution.
  
    // Call each callback in the order they were registered
    var errors = [];
    for (var i = 0; i < callbacks.length; i++) {
      try {
        callbacks[i].execute(context);
      } catch (e) {
        goog.async.throwException(e);
        errors.push(e);
      }
    }
  
    // Clear the list of callbacks.
    callbacks.length = 0;
    return errors.length ? errors : null;
  };
  
  
  /** @override */
  goog.module.ModuleInfo.prototype.disposeInternal = function() {
    goog.module.ModuleInfo.superClass_.disposeInternal.call(this);
    goog.dispose(this.module_);
  };
  