Adding float-no-zero branch hosted build

This commit is contained in:
ahocevar
2014-03-07 10:55:12 +01:00
parent 84cad42f6d
commit bd9092199b
1664 changed files with 731463 additions and 0 deletions
@@ -0,0 +1,56 @@
// Copyright 2009 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 An interface for module loading.
*
*/
goog.provide('goog.module.AbstractModuleLoader');
/**
* An interface that loads JavaScript modules.
* @interface
*/
goog.module.AbstractModuleLoader = function() {};
/**
* Loads a list of JavaScript modules.
*
* @param {Array.<string>} ids The module ids in dependency order.
* @param {Object} moduleInfoMap A mapping from module id to ModuleInfo object.
* @param {function()?=} opt_successFn The callback if module loading is a
* success.
* @param {function(?number)?=} opt_errorFn The callback if module loading is an
* error.
* @param {function()?=} opt_timeoutFn The callback if module loading times out.
* @param {boolean=} opt_forceReload Whether to bypass cache while loading the
* module.
*/
goog.module.AbstractModuleLoader.prototype.loadModules = function(
ids, moduleInfoMap, opt_successFn, opt_errorFn, opt_timeoutFn,
opt_forceReload) {};
/**
* Pre-fetches a JavaScript module.
*
* @param {string} id The module id.
* @param {!goog.module.ModuleInfo} moduleInfo The module info.
*/
goog.module.AbstractModuleLoader.prototype.prefetchModule = function(
id, moduleInfo) {};
@@ -0,0 +1,45 @@
// 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 base class for a module. This is used to allow the
* code to be modularized, giving the benefits of lazy loading and loading on
* demand.
*
*/
goog.provide('goog.module.BaseModule');
goog.require('goog.Disposable');
/**
* A basic module object that represents a module of Javascript code that can
* be dynamically loaded.
*
* @constructor
* @extends {goog.Disposable}
*/
goog.module.BaseModule = function() {
goog.Disposable.call(this);
};
goog.inherits(goog.module.BaseModule, goog.Disposable);
/**
* Performs any load-time initialization that the module requires.
* @param {Object} context The module context.
*/
goog.module.BaseModule.prototype.initialize = function(context) {};
@@ -0,0 +1,269 @@
// Copyright 2006 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 This class supports the dynamic loading of compiled
* javascript modules at runtime, as descibed in the designdoc.
*
* <http://go/js_modules_design>
*
*/
goog.provide('goog.module.Loader');
goog.require('goog.Timer');
goog.require('goog.array');
goog.require('goog.dom');
goog.require('goog.object');
/**
* The dynamic loading functionality is defined as a class. The class
* will be used as singleton. There is, however, a two step
* initialization procedure because parameters need to be passed to
* the goog.module.Loader instance.
*
* @constructor
*/
goog.module.Loader = function() {
/**
* Map of module name/array of {symbol name, callback} pairs that are pending
* to be loaded.
* @type {Object}
* @private
*/
this.pending_ = {};
/**
* Provides associative access to each module and the symbols of each module
* that have aready been loaded (one lookup for the module, another lookup
* on the module for the symbol).
* @type {Object}
* @private
*/
this.modules_ = {};
/**
* Map of module name to module url. Used to avoid fetching the same URL
* twice by keeping track of in-flight URLs.
* Note: this allows two modules to be bundled into the same file.
* @type {Object}
* @private
*/
this.pendingModuleUrls_ = {};
/**
* The base url to load modules from. This property will be set in init().
* @type {?string}
* @private
*/
this.urlBase_ = null;
/**
* Array of modules that have been requested before init() was called.
* If require() is called before init() was called, the required
* modules can obviously not yet be loaded, because their URL is
* unknown. The modules that are requested before init() are
* therefore stored in this array, and they are loaded at init()
* time.
* @type {Array.<string>}
* @private
*/
this.pendingBeforeInit_ = [];
};
goog.addSingletonGetter(goog.module.Loader);
/**
* Creates a full URL to the compiled module code given a base URL and a
* module name. By default it's urlBase + '_' + module + '.js'.
* @param {string} urlBase URL to the module files.
* @param {string} module Module name.
* @return {string} The full url to the module binary.
* @private
*/
goog.module.Loader.prototype.getModuleUrl_ = function(urlBase, module) {
return urlBase + '_' + module + '.js';
};
/**
* The globally exported name of the load callback. Matches the
* definition in the js_modular_binary() BUILD rule.
* @type {string}
*/
goog.module.Loader.LOAD_CALLBACK = '__gjsload__';
/**
* Loads the module by evaluating the javascript text in the current
* scope. Uncompiled, base identifiers are visible in the global scope;
* when compiled they are visible in the closure of the anonymous
* namespace. Notice that this cannot be replaced by the global eval,
* because the global eval isn't in the scope of the anonymous
* namespace function that the jscompiled code lives in.
*
* @param {string} t_ The javascript text to evaluate. IMPORTANT: The
* name of the identifier is chosen so that it isn't compiled and
* hence cannot shadow compiled identifiers in the surrounding scope.
* @private
*/
goog.module.Loader.loaderEval_ = function(t_) {
eval(t_);
};
/**
* Initializes the Loader to be fully functional. Also executes load
* requests that were received before initialization. Must be called
* exactly once, with the URL of the base library. Module URLs are
* derived from the URL of the base library by inserting the module
* name, preceded by a period, before the .js prefix of the base URL.
*
* @param {string} baseUrl The URL of the base library.
* @param {Function=} opt_urlFunction Function that creates the URL for the
* module file. It will be passed the base URL for module files and the
* module name and should return the fully-formed URL to the module file to
* load.
*/
goog.module.Loader.prototype.init = function(baseUrl, opt_urlFunction) {
// For the use by the module wrappers, loaderEval_ is exported to
// the page. Note that, despite the name, this is not part of the
// API, so it is here and not in api_app.js. Cf. BUILD. Note this is
// done before the first load requests are sent.
goog.exportSymbol(goog.module.Loader.LOAD_CALLBACK,
goog.module.Loader.loaderEval_);
this.urlBase_ = baseUrl.replace(/\.js$/, '');
if (opt_urlFunction) {
this.getModuleUrl_ = opt_urlFunction;
}
goog.array.forEach(this.pendingBeforeInit_, function(module) {
this.load_(module);
}, this);
goog.array.clear(this.pendingBeforeInit_);
};
/**
* Requests the loading of a symbol from a module. When the module is
* loaded, the requested symbol will be passed as argument to the
* function callback.
*
* @param {string} module The name of the module. Usually, the value
* is defined as a constant whose name starts with MOD_.
* @param {number|string} symbol The ID of the symbol. Usually, the value is
* defined as a constant whose name starts with SYM_.
* @param {Function} callback This function will be called with the
* resolved symbol as the argument once the module is loaded.
*/
goog.module.Loader.prototype.require = function(module, symbol, callback) {
var pending = this.pending_;
var modules = this.modules_;
if (modules[module]) {
// already loaded
callback(modules[module][symbol]);
} else if (pending[module]) {
// loading is pending from another require of the same module
pending[module].push([symbol, callback]);
} else {
// not loaded, and not requested
pending[module] = [[symbol, callback]]; // Yes, really [[ ]].
// Defer loading to initialization if Loader is not yet
// initialized, otherwise load the module.
if (goog.isString(this.urlBase_)) {
this.load_(module);
} else {
this.pendingBeforeInit_.push(module);
}
}
};
/**
* Registers a symbol in a loaded module. When called without symbol,
* registers the module to be fully loaded and executes all callbacks
* from pending require() callbacks for this module.
*
* @param {string} module The name of the module. Cf. parameter module
* of method require().
* @param {number|string=} opt_symbol The symbol being defined, or nothing when
* all symbols of the module are defined. Cf. parameter symbol of method
* require().
* @param {Object=} opt_object The object bound to the symbol, or nothing when
* all symbols of the module are defined.
*/
goog.module.Loader.prototype.provide = function(
module, opt_symbol, opt_object) {
var modules = this.modules_;
var pending = this.pending_;
if (!modules[module]) {
modules[module] = {};
}
if (opt_object) {
// When an object is provided, just register it.
modules[module][opt_symbol] = opt_object;
} else if (pending[module]) {
// When no object is provided, and there are pending require()
// callbacks for this module, execute them.
for (var i = 0; i < pending[module].length; ++i) {
var symbol = pending[module][i][0];
var callback = pending[module][i][1];
callback(modules[module][symbol]);
}
delete pending[module];
delete this.pendingModuleUrls_[module];
}
};
/**
* Starts to load a module. Assumes that init() was called.
*
* @param {string} module The name of the module.
* @private
*/
goog.module.Loader.prototype.load_ = function(module) {
// NOTE(user): If the module request happens inside a click handler
// (presumably inside any user event handler, but the onload event
// handler is fine), IE will load the script but not execute
// it. Thus we break out of the current flow of control before we do
// the load. For the record, for IE it would have been enough to
// just defer the assignment to src. Safari doesn't execute the
// script if the assignment to src happens *after* the script
// element is inserted into the DOM.
goog.Timer.callOnce(function() {
// The module might have been registered in the interim (if fetched as part
// of another module fetch because they share the same url)
if (this.modules_[module]) {
return;
}
var url = this.getModuleUrl_(this.urlBase_, module);
// Check if specified URL is already in flight
var urlInFlight = goog.object.containsValue(this.pendingModuleUrls_, url);
this.pendingModuleUrls_[module] = url;
if (urlInFlight) {
return;
}
var s = goog.dom.createDom('script',
{'type': 'text/javascript', 'src': url});
document.body.appendChild(s);
}, 0, this);
};
@@ -0,0 +1,166 @@
// Copyright 2006 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 This class supports the dynamic loading of compiled
* javascript modules at runtime, as descibed in the designdoc.
*
* <http://go/js_modules_design>
*
*/
goog.provide('goog.module');
goog.require('goog.array');
goog.require('goog.module.Loader');
/**
* Wrapper of goog.module.Loader.require() for use in modules.
* See method goog.module.Loader.require() for
* explanation of params.
*
* @param {string} module The name of the module. Usually, the value
* is defined as a constant whose name starts with MOD_.
* @param {number|string} symbol The ID of the symbol. Usually, the value is
* defined as a constant whose name starts with SYM_.
* @param {Function} callback This function will be called with the
* resolved symbol as the argument once the module is loaded.
*/
goog.module.require = function(module, symbol, callback) {
goog.module.Loader.getInstance().require(module, symbol, callback);
};
/**
* Wrapper of goog.module.Loader.provide() for use in modules
* See method goog.module.Loader.provide() for explanation of params.
*
* @param {string} module The name of the module. Cf. parameter module
* of method require().
* @param {number|string=} opt_symbol The symbol being defined, or nothing
* when all symbols of the module are defined. Cf. parameter symbol of
* method require().
* @param {Object=} opt_object The object bound to the symbol, or nothing when
* all symbols of the module are defined.
*/
goog.module.provide = function(module, opt_symbol, opt_object) {
goog.module.Loader.getInstance().provide(
module, opt_symbol, opt_object);
};
/**
* Wrapper of init() so that we only need to export this single
* identifier instead of three. See method goog.module.Loader.init() for
* explanation of param.
*
* @param {string} urlBase The URL of the base library.
* @param {Function=} opt_urlFunction Function that creates the URL for the
* module file. It will be passed the base URL for module files and the
* module name and should return the fully-formed URL to the module file to
* load.
*/
goog.module.initLoader = function(urlBase, opt_urlFunction) {
goog.module.Loader.getInstance().init(urlBase, opt_urlFunction);
};
/**
* Produces a function that delegates all its arguments to a
* dynamically loaded function. This is used to export dynamically
* loaded functions.
*
* @param {string} module The module to load from.
* @param {number|string} symbol The ID of the symbol to load from the module.
* This symbol must resolve to a function.
* @return {!Function} A function that forwards all its arguments to
* the dynamically loaded function specified by module and symbol.
*/
goog.module.loaderCall = function(module, symbol) {
return function() {
var args = arguments;
goog.module.require(module, symbol, function(f) {
f.apply(null, args);
});
};
};
/**
* Requires symbols for multiple modules, and invokes a final callback
* on the condition that all of them are loaded. I.e. a barrier for
* loading of multiple symbols. If no symbols are required, the
* final callback is called immediately.
*
* @param {Array.<Object>} symbolRequests A
* list of tuples of module, symbol, callback (analog to the arguments
* to require(), above). These will each be require()d
* individually. NOTE: This argument will be modified during execution
* of the function.
* @param {Function} finalCb A function that is called when all
* required symbols are loaded.
*/
goog.module.requireMultipleSymbols = function(symbolRequests, finalCb) {
var I = symbolRequests.length;
if (I == 0) {
finalCb();
} else {
for (var i = 0; i < I; ++i) {
goog.module.requireMultipleSymbolsHelper_(symbolRequests, i, finalCb);
}
}
};
/**
* Used by requireMultipleSymbols() to load each required symbol and
* keep track how many are loaded, and finally invoke the barrier
* callback when they are all done.
*
* @param {Array.<Object>} symbolRequests Same as in
* requireMultipleSymbols().
* @param {number} i The single module that is required in this invocation.
* @param {Function} finalCb Same as in requireMultipleSymbols().
* @private
*/
goog.module.requireMultipleSymbolsHelper_ = function(symbolRequests, i,
finalCb) {
var r = symbolRequests[i];
var module = r[0];
var symbol = r[1];
var symbolCb = r[2];
goog.module.require(module, symbol, function() {
symbolCb.apply(this, arguments);
symbolRequests[i] = null;
if (goog.array.every(symbolRequests, goog.module.isNull_)) {
finalCb();
}
});
};
/**
* Checks if the given element is null.
*
* @param {Object} el The element to check if null.
* @param {number} i The index of the element.
* @param {Array.<Object>} arr The array that contains the element.
* @return {boolean} TRUE iff the element is null.
* @private
*/
goog.module.isNull_ = function(el, i, arr) {
return el == null;
};
@@ -0,0 +1,335 @@
// 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.require('goog.Disposable');
goog.require('goog.functions');
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}
*/
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.<string>?}
* @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.<string>} uris Uris for this module's code.
*/
goog.module.ModuleInfo.prototype.setUris = function(uris) {
this.uris_ = uris;
};
/**
* Gets the uris of this module.
* @return {Array.<string>?} Uris for this module's code.
*/
goog.module.ModuleInfo.prototype.getUris = function() {
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 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_;
};
/**
* 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.module.ModuleManager.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.
// Then, we try to report the exception as best we can.
// 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) {
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_);
};
@@ -0,0 +1,84 @@
// 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 simple callback mechanism for notification about module
* loads. Should be considered package-private to goog.module.
*
*/
goog.provide('goog.module.ModuleLoadCallback');
goog.require('goog.debug.entryPointRegistry');
goog.require('goog.debug.errorHandlerWeakDep');
/**
* Class used to encapsulate the callbacks to be called when a module loads.
* @param {Function} fn Callback function.
* @param {Object=} opt_handler Optional handler under whose scope to execute
* the callback.
* @constructor
*/
goog.module.ModuleLoadCallback = function(fn, opt_handler) {
/**
* Callback function.
* @type {Function}
* @private
*/
this.fn_ = fn;
/**
* Optional handler under whose scope to execute the callback.
* @type {Object|undefined}
* @private
*/
this.handler_ = opt_handler;
};
/**
* Completes the operation and calls the callback function if appropriate.
* @param {*} context The module context.
*/
goog.module.ModuleLoadCallback.prototype.execute = function(context) {
if (this.fn_) {
this.fn_.call(this.handler_ || null, context);
this.handler_ = null;
this.fn_ = null;
}
};
/**
* Abort the callback, but not the actual module load.
*/
goog.module.ModuleLoadCallback.prototype.abort = function() {
this.fn_ = null;
this.handler_ = null;
};
// Register the browser event handler as an entry point, so that
// it can be monitored for exception handling, etc.
goog.debug.entryPointRegistry.register(
/**
* @param {function(!Function): !Function} transformer The transforming
* function.
*/
function(transformer) {
goog.module.ModuleLoadCallback.prototype.execute =
transformer(goog.module.ModuleLoadCallback.prototype.execute);
});
@@ -0,0 +1,462 @@
// 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 The module loader for loading modules across the network.
*
* Browsers do not guarantee that scripts appended to the document
* are executed in the order they are added. For production mode, we use
* XHRs to load scripts, because they do not have this problem and they
* have superior mechanisms for handling failure. However, XHR-evaled
* scripts are harder to debug.
*
* In debugging mode, we use normal script tags. In order to make this work,
* we load the scripts in serial: we do not execute script B to the document
* until we are certain that script A is finished loading.
*
*/
goog.provide('goog.module.ModuleLoader');
goog.require('goog.Timer');
goog.require('goog.array');
goog.require('goog.events');
goog.require('goog.events.Event');
goog.require('goog.events.EventHandler');
goog.require('goog.events.EventTarget');
goog.require('goog.log');
goog.require('goog.module.AbstractModuleLoader');
goog.require('goog.net.BulkLoader');
goog.require('goog.net.EventType');
goog.require('goog.net.jsloader');
goog.require('goog.userAgent.product');
/**
* A class that loads Javascript modules.
* @constructor
* @extends {goog.events.EventTarget}
* @implements {goog.module.AbstractModuleLoader}
*/
goog.module.ModuleLoader = function() {
goog.base(this);
/**
* Event handler for managing handling events.
* @type {goog.events.EventHandler}
* @private
*/
this.eventHandler_ = new goog.events.EventHandler(this);
/**
* A map from module IDs to goog.module.ModuleLoader.LoadStatus.
* @type {!Object.<Array.<string>, goog.module.ModuleLoader.LoadStatus>}
* @private
*/
this.loadingModulesStatus_ = {};
};
goog.inherits(goog.module.ModuleLoader, goog.events.EventTarget);
/**
* A logger.
* @type {goog.log.Logger}
* @protected
*/
goog.module.ModuleLoader.prototype.logger = goog.log.getLogger(
'goog.module.ModuleLoader');
/**
* Whether debug mode is enabled.
* @type {boolean}
* @private
*/
goog.module.ModuleLoader.prototype.debugMode_ = false;
/**
* Whether source url injection is enabled.
* @type {boolean}
* @private
*/
goog.module.ModuleLoader.prototype.sourceUrlInjection_ = false;
/**
* @return {boolean} Whether sourceURL affects stack traces.
* Chrome is currently the only browser that does this, but
* we believe other browsers are working on this.
* @see http://bugzilla.mozilla.org/show_bug.cgi?id=583083
*/
goog.module.ModuleLoader.supportsSourceUrlStackTraces = function() {
return goog.userAgent.product.CHROME;
};
/**
* @return {boolean} Whether sourceURL affects the debugger.
*/
goog.module.ModuleLoader.supportsSourceUrlDebugger = function() {
return goog.userAgent.product.CHROME || goog.userAgent.GECKO;
};
/**
* Gets the debug mode for the loader.
* @return {boolean} Whether the debug mode is enabled.
*/
goog.module.ModuleLoader.prototype.getDebugMode = function() {
return this.debugMode_;
};
/**
* Sets the debug mode for the loader.
* @param {boolean} debugMode Whether the debug mode is enabled.
*/
goog.module.ModuleLoader.prototype.setDebugMode = function(debugMode) {
this.debugMode_ = debugMode;
};
/**
* When enabled, we will add a sourceURL comment to the end of all scripts
* to mark their origin.
*
* On WebKit, stack traces will refect the sourceURL comment, so this is
* useful for debugging webkit stack traces in production.
*
* Notice that in debug mode, we will use source url injection + eval rather
* then appending script nodes to the DOM, because the scripts will load far
* faster. (Appending script nodes is very slow, because we can't parallelize
* the downloading and evaling of the script).
*
* The cost of appending sourceURL information is negligible when compared to
* the cost of evaling the script. Almost all clients will want this on.
*
* TODO(nicksantos): Turn this on by default. We may want to turn this off
* for clients that inject their own sourceURL.
*
* @param {boolean} enabled Whether source url injection is enabled.
*/
goog.module.ModuleLoader.prototype.setSourceUrlInjection = function(enabled) {
this.sourceUrlInjection_ = enabled;
};
/**
* @return {boolean} Whether we're using source url injection.
* @private
*/
goog.module.ModuleLoader.prototype.usingSourceUrlInjection_ = function() {
return this.sourceUrlInjection_ ||
(this.getDebugMode() &&
goog.module.ModuleLoader.supportsSourceUrlStackTraces());
};
/** @override */
goog.module.ModuleLoader.prototype.loadModules = function(
ids, moduleInfoMap, opt_successFn, opt_errorFn, opt_timeoutFn,
opt_forceReload) {
var loadStatus = this.loadingModulesStatus_[ids] ||
new goog.module.ModuleLoader.LoadStatus();
loadStatus.loadRequested = true;
loadStatus.successFn = opt_successFn || null;
loadStatus.errorFn = opt_errorFn || null;
if (!this.loadingModulesStatus_[ids]) {
// Modules were not prefetched.
this.loadingModulesStatus_[ids] = loadStatus;
this.downloadModules_(ids, moduleInfoMap);
// TODO(user): Need to handle timeouts in the module loading code.
} else if (goog.isDefAndNotNull(loadStatus.responseTexts)) {
// Modules prefetch is complete.
this.evaluateCode_(ids);
}
// Otherwise modules prefetch is in progress, and these modules will be
// executed after the prefetch is complete.
};
/**
* Evaluate the JS code.
* @param {Array.<string>} moduleIds The module ids.
* @private
*/
goog.module.ModuleLoader.prototype.evaluateCode_ = function(moduleIds) {
this.dispatchEvent(new goog.module.ModuleLoader.Event(
goog.module.ModuleLoader.EventType.REQUEST_SUCCESS, moduleIds));
goog.log.info(this.logger, 'evaluateCode ids:' + moduleIds);
var success = true;
var loadStatus = this.loadingModulesStatus_[moduleIds];
var uris = loadStatus.requestUris;
var texts = loadStatus.responseTexts;
try {
if (this.usingSourceUrlInjection_()) {
for (var i = 0; i < uris.length; i++) {
var uri = uris[i];
goog.globalEval(texts[i] + ' //@ sourceURL=' + uri);
}
} else {
goog.globalEval(texts.join('\n'));
}
} catch (e) {
success = false;
// TODO(user): Consider throwing an exception here.
goog.log.warning(this.logger, 'Loaded incomplete code for module(s): ' +
moduleIds, e);
}
this.dispatchEvent(
new goog.module.ModuleLoader.Event(
goog.module.ModuleLoader.EventType.EVALUATE_CODE, moduleIds));
if (!success) {
this.handleErrorHelper_(moduleIds, loadStatus.errorFn, null /* status */);
} else if (loadStatus.successFn) {
loadStatus.successFn();
}
delete this.loadingModulesStatus_[moduleIds];
};
/**
* Handles a successful response to a request for prefetch or load one or more
* modules.
*
* @param {goog.net.BulkLoader} bulkLoader The bulk loader.
* @param {Array.<string>} moduleIds The ids of the modules requested.
* @private
*/
goog.module.ModuleLoader.prototype.handleSuccess_ = function(
bulkLoader, moduleIds) {
goog.log.info(this.logger, 'Code loaded for module(s): ' + moduleIds);
var loadStatus = this.loadingModulesStatus_[moduleIds];
loadStatus.responseTexts = bulkLoader.getResponseTexts();
if (loadStatus.loadRequested) {
this.evaluateCode_(moduleIds);
}
// NOTE: A bulk loader instance is used for loading a set of module ids.
// Once these modules have been loaded successfully or in error the bulk
// loader should be disposed as it is not needed anymore. A new bulk loader
// is instantiated for any new modules to be loaded. The dispose is called
// on a timer so that the bulkloader has a chance to release its
// objects.
goog.Timer.callOnce(bulkLoader.dispose, 5, bulkLoader);
};
/** @override */
goog.module.ModuleLoader.prototype.prefetchModule = function(
id, moduleInfo) {
// Do not prefetch in debug mode.
if (this.getDebugMode()) {
return;
}
var loadStatus = this.loadingModulesStatus_[[id]];
if (loadStatus) {
return;
}
var moduleInfoMap = {};
moduleInfoMap[id] = moduleInfo;
this.loadingModulesStatus_[[id]] = new goog.module.ModuleLoader.LoadStatus();
this.downloadModules_([id], moduleInfoMap);
};
/**
* Downloads a list of JavaScript modules.
*
* @param {Array.<string>} ids The module ids in dependency order.
* @param {Object} moduleInfoMap A mapping from module id to ModuleInfo object.
* @private
*/
goog.module.ModuleLoader.prototype.downloadModules_ = function(
ids, moduleInfoMap) {
var uris = [];
for (var i = 0; i < ids.length; i++) {
goog.array.extend(uris, moduleInfoMap[ids[i]].getUris());
}
goog.log.info(this.logger, 'downloadModules ids:' + ids + ' uris:' + uris);
if (this.getDebugMode() &&
!this.usingSourceUrlInjection_()) {
// In debug mode use <script> tags rather than XHRs to load the files.
// This makes it possible to debug and inspect stack traces more easily.
// It's also possible to use it to load JavaScript files that are hosted on
// another domain.
// The scripts need to load serially, so this is much slower than parallel
// script loads with source url injection.
goog.net.jsloader.loadMany(uris);
} else {
var loadStatus = this.loadingModulesStatus_[ids];
loadStatus.requestUris = uris;
var bulkLoader = new goog.net.BulkLoader(uris);
var eventHandler = this.eventHandler_;
eventHandler.listen(
bulkLoader,
goog.net.EventType.SUCCESS,
goog.bind(this.handleSuccess_, this, bulkLoader, ids),
false,
null);
eventHandler.listen(
bulkLoader,
goog.net.EventType.ERROR,
goog.bind(this.handleError_, this, bulkLoader, ids),
false,
null);
bulkLoader.load();
}
};
/**
* Handles an error during a request for one or more modules.
* @param {goog.net.BulkLoader} bulkLoader The bulk loader.
* @param {Array.<string>} moduleIds The ids of the modules requested.
* @param {number} status The response status.
* @private
*/
goog.module.ModuleLoader.prototype.handleError_ = function(
bulkLoader, moduleIds, status) {
var loadStatus = this.loadingModulesStatus_[moduleIds];
// The bulk loader doesn't cancel other requests when a request fails. We will
// delete the loadStatus in the first failure, so it will be undefined in
// subsequent errors.
if (loadStatus) {
delete this.loadingModulesStatus_[moduleIds];
this.handleErrorHelper_(moduleIds, loadStatus.errorFn, status);
}
// NOTE: A bulk loader instance is used for loading a set of module ids. Once
// these modules have been loaded successfully or in error the bulk loader
// should be disposed as it is not needed anymore. A new bulk loader is
// instantiated for any new modules to be loaded. The dispose is called
// on another thread so that the bulkloader has a chance to release its
// objects.
goog.Timer.callOnce(bulkLoader.dispose, 5, bulkLoader);
};
/**
* Handles an error during a request for one or more modules.
* @param {Array.<string>} moduleIds The ids of the modules requested.
* @param {?function(?number)} errorFn The function to call on failure.
* @param {?number} status The response status.
* @private
*/
goog.module.ModuleLoader.prototype.handleErrorHelper_ = function(
moduleIds, errorFn, status) {
this.dispatchEvent(
new goog.module.ModuleLoader.Event(
goog.module.ModuleLoader.EventType.REQUEST_ERROR, moduleIds));
goog.log.warning(this.logger, 'Request failed for module(s): ' + moduleIds);
if (errorFn) {
errorFn(status);
}
};
/** @override */
goog.module.ModuleLoader.prototype.disposeInternal = function() {
goog.module.ModuleLoader.superClass_.disposeInternal.call(this);
this.eventHandler_.dispose();
this.eventHandler_ = null;
};
/**
* @enum {string}
*/
goog.module.ModuleLoader.EventType = {
/** Called after the code for a module is evaluated. */
EVALUATE_CODE: goog.events.getUniqueId('evaluateCode'),
/** Called when the BulkLoader finishes successfully. */
REQUEST_SUCCESS: goog.events.getUniqueId('requestSuccess'),
/** Called when the BulkLoader fails, or code loading fails. */
REQUEST_ERROR: goog.events.getUniqueId('requestError')
};
/**
* @param {goog.module.ModuleLoader.EventType} type The type.
* @param {Array.<string>} moduleIds The ids of the modules being evaluated.
* @constructor
* @extends {goog.events.Event}
*/
goog.module.ModuleLoader.Event = function(type, moduleIds) {
goog.base(this, type);
/**
* @type {Array.<string>}
*/
this.moduleIds = moduleIds;
};
goog.inherits(goog.module.ModuleLoader.Event, goog.events.Event);
/**
* A class that keeps the state of the module during the loading process. It is
* used to save loading information between modules download and evaluation.
* @constructor
*/
goog.module.ModuleLoader.LoadStatus = function() {
/**
* The request uris.
* @type {Array.<string>}
*/
this.requestUris = null;
/**
* The response texts.
* @type {Array.<string>}
*/
this.responseTexts = null;
/**
* Whether loadModules was called for the set of modules referred by this
* status.
* @type {boolean}
*/
this.loadRequested = false;
/**
* Success callback.
* @type {?function()}
*/
this.successFn = null;
/**
* Error callback.
* @type {?function(?number)}
*/
this.errorFn = null;
};
@@ -0,0 +1,438 @@
// Copyright 2009 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 Tests for goog.module.ModuleLoader.
* @author nicksantos@google.com (Nick Santos)
*/
goog.provide('goog.module.ModuleLoaderTest');
goog.require('goog.array');
goog.require('goog.dom');
goog.require('goog.functions');
goog.require('goog.module.ModuleLoader');
goog.require('goog.module.ModuleManager');
goog.require('goog.module.ModuleManager.CallbackType');
goog.require('goog.object');
goog.require('goog.testing.AsyncTestCase');
goog.require('goog.testing.PropertyReplacer');
goog.require('goog.testing.events.EventObserver');
goog.require('goog.testing.jsunit');
goog.require('goog.testing.recordFunction');
goog.require('goog.userAgent.product');
goog.setTestOnly('goog.module.ModuleLoaderTest');
var modA1Loaded = false;
var modA2Loaded = false;
var modB1Loaded = false;
var moduleLoader = null;
var moduleManager = null;
var stubs = new goog.testing.PropertyReplacer();
var testCase = goog.testing.AsyncTestCase.createAndInstall(document.title);
testCase.stepTimeout = 5 * 1000; // 5 seconds
var EventType = goog.module.ModuleLoader.EventType;
var observer;
testCase.setUp = function() {
modA1Loaded = false;
modA2Loaded = false;
modB1Loaded = false;
goog.provide = goog.nullFunction;
moduleManager = goog.module.ModuleManager.getInstance();
stubs.replace(moduleManager, 'getBackOff_', goog.functions.constant(0));
moduleLoader = new goog.module.ModuleLoader();
observer = new goog.testing.events.EventObserver();
goog.events.listen(
moduleLoader, goog.object.getValues(EventType), observer);
moduleManager.setLoader(moduleLoader);
moduleManager.setAllModuleInfo({
'modA': [],
'modB': ['modA']
});
moduleManager.setModuleUris({
'modA': ['testdata/modA_1.js', 'testdata/modA_2.js'],
'modB': ['testdata/modB_1.js']
});
assertNotLoaded('modA');
assertNotLoaded('modB');
assertFalse(modA1Loaded);
};
testCase.tearDown = function() {
stubs.reset();
// Ensure that the module manager was created.
assertNotNull(goog.module.ModuleManager.getInstance());
moduleManager = goog.module.ModuleManager.instance_ = null;
// tear down the module loaded flag.
modA1Loaded = false;
// Remove all the fake scripts.
var scripts = goog.array.clone(
document.getElementsByTagName('SCRIPT'));
for (var i = 0; i < scripts.length; i++) {
if (scripts[i].src.indexOf('testdata') != -1) {
goog.dom.removeNode(scripts[i]);
}
}
};
function testLoadModuleA() {
testCase.waitForAsync('wait for module A load');
moduleManager.execOnLoad('modA', function() {
testCase.continueTesting();
assertLoaded('modA');
assertNotLoaded('modB');
assertTrue(modA1Loaded);
assertEquals('EVALUATE_CODE',
0, observer.getEvents(EventType.EVALUATE_CODE).length);
assertEquals('REQUEST_SUCCESS',
1, observer.getEvents(EventType.REQUEST_SUCCESS).length);
assertArrayEquals(
['modA'], observer.getEvents(EventType.REQUEST_SUCCESS)[0].moduleIds);
assertEquals('REQUEST_ERROR',
0, observer.getEvents(EventType.REQUEST_ERROR).length);
});
}
function testLoadModuleB() {
testCase.waitForAsync('wait for module B load');
moduleManager.execOnLoad('modB', function() {
testCase.continueTesting();
assertLoaded('modA');
assertLoaded('modB');
assertTrue(modA1Loaded);
});
}
function testLoadDebugModuleA() {
testCase.waitForAsync('wait for module A load');
moduleLoader.setDebugMode(true);
moduleManager.execOnLoad('modA', function() {
testCase.continueTesting();
assertLoaded('modA');
assertNotLoaded('modB');
assertTrue(modA1Loaded);
});
}
function testLoadDebugModuleB() {
testCase.waitForAsync('wait for module B load');
moduleLoader.setDebugMode(true);
moduleManager.execOnLoad('modB', function() {
testCase.continueTesting();
assertLoaded('modA');
assertLoaded('modB');
assertTrue(modA1Loaded);
});
}
function testLoadDebugModuleAThenB() {
// Swap the script tags of module A, to introduce a race condition.
// See the comments on this in ModuleLoader's debug loader.
moduleManager.setModuleUris({
'modA': ['testdata/modA_2.js', 'testdata/modA_1.js'],
'modB': ['testdata/modB_1.js']
});
testCase.waitForAsync('wait for module B load');
moduleLoader.setDebugMode(true);
moduleManager.execOnLoad('modB', function() {
testCase.continueTesting();
assertLoaded('modA');
assertLoaded('modB');
var scripts = goog.array.clone(
document.getElementsByTagName('SCRIPT'));
var seenLastScriptOfModuleA = false;
for (var i = 0; i < scripts.length; i++) {
var uri = scripts[i].src;
if (uri.indexOf('modA_1.js') >= 0) {
seenLastScriptOfModuleA = true;
} else if (uri.indexOf('modB') >= 0) {
assertTrue(seenLastScriptOfModuleA);
}
}
});
}
function testSourceInjection() {
moduleLoader.setSourceUrlInjection(true);
assertSourceInjection();
}
function testSourceInjectionViaDebugMode() {
moduleLoader.setDebugMode(true);
assertSourceInjection();
}
function assertSourceInjection() {
testCase.waitForAsync('wait for module B load');
moduleManager.execOnLoad('modB', function() {
testCase.continueTesting();
assertTrue(!!throwErrorInModuleB);
var ex = assertThrows(function() {
throwErrorInModuleB();
});
var stackTrace = ex.stack.toString();
var expectedString = 'testdata/modB_1.js';
if (goog.module.ModuleLoader.supportsSourceUrlStackTraces()) {
// Source URL should be added in eval or in jsloader.
assertContains(expectedString, stackTrace);
} else if (moduleLoader.isDebugMode()) {
// Browsers used jsloader, thus URLs are present.
assertContains(expectedString, stackTrace);
} else {
// Browser used eval, does not support source URL.
assertNotContains(expectedString, stackTrace);
}
});
}
function testModuleLoaderRecursesTooDeep(opt_numModules) {
// There was a bug in the module loader where it would retry recursively
// whenever there was a synchronous failure in the module load. When you
// asked for modB, it would try to load its dependency modA. When modA
// failed, it would move onto modB, and then start over, repeating until it
// ran out of stack.
var numModules = opt_numModules || 1;
var uris = {};
var deps = {};
var mods = [];
for (var num = 0; num < numModules; num++) {
var modName = 'mod' + num;
mods.unshift(modName);
uris[modName] = [];
deps[modName] = num ? ['mod' + (num - 1)] : [];
for (var i = 0; i < 5; i++) {
uris[modName].push(
'http://www.google.com/crossdomain' + num + 'x' + i + '.js');
}
}
moduleManager.setAllModuleInfo(deps);
moduleManager.setModuleUris(uris);
// Make all XHRs throw an error, so that we test the error-handling
// functionality.
var oldXmlHttp = goog.net.XmlHttp;
stubs.set(goog.net, 'XmlHttp', function() {
return {
open: goog.functions.error('mock error'),
abort: goog.nullFunction
};
});
goog.object.extend(goog.net.XmlHttp, oldXmlHttp);
var errorCount = 0;
var errorIds = [];
var errorHandler = function(ignored, modId) {
errorCount++;
errorIds.push(modId);
};
moduleManager.registerCallback(
goog.module.ModuleManager.CallbackType.ERROR,
errorHandler);
moduleManager.execOnLoad(mods[0], function() {
fail('modB should not load successfully');
});
assertEquals(mods.length, errorCount);
goog.array.sort(mods);
goog.array.sort(errorIds);
assertArrayEquals(mods, errorIds);
assertArrayEquals([], moduleManager.requestedModuleIdsQueue_);
assertArrayEquals([], moduleManager.userInitiatedLoadingModuleIds_);
}
function testModuleLoaderRecursesTooDeep2modules() {
testModuleLoaderRecursesTooDeep(2);
}
function testModuleLoaderRecursesTooDeep3modules() {
testModuleLoaderRecursesTooDeep(3);
}
function testModuleLoaderRecursesTooDeep4modules() {
testModuleLoaderRecursesTooDeep(3);
}
function testErrback() {
// Don't run this test on IE, because the way the test runner catches
// errors on IE plays badly with the simulated errors in the test.
if (goog.userAgent.IE) return;
// Modules will throw an exception if this boolean is set to true.
modA1Loaded = true;
var errorHandler = function() {
testCase.continueTesting();
assertNotLoaded('modA');
};
moduleManager.registerCallback(
goog.module.ModuleManager.CallbackType.ERROR,
errorHandler);
moduleManager.execOnLoad('modA', function() {
fail('modA should not load successfully');
});
testCase.waitForAsync('wait for the error callback');
}
function testPrefetchThenLoadModuleA() {
moduleManager.prefetchModule('modA');
stubs.set(goog.net.BulkLoader.prototype, 'load', function() {
fail('modA should not be reloaded')
});
testCase.waitForAsync('wait for module A load');
moduleManager.execOnLoad('modA', function() {
testCase.continueTesting();
assertLoaded('modA');
assertEquals('REQUEST_SUCCESS',
1, observer.getEvents(EventType.REQUEST_SUCCESS).length);
assertArrayEquals(
['modA'], observer.getEvents(EventType.REQUEST_SUCCESS)[0].moduleIds);
assertEquals('REQUEST_ERROR',
0, observer.getEvents(EventType.REQUEST_ERROR).length);
});
}
function testPrefetchThenLoadModuleB() {
moduleManager.prefetchModule('modB');
stubs.set(goog.net.BulkLoader.prototype, 'load', function() {
fail('modA and modB should not be reloaded')
});
testCase.waitForAsync('wait for module B load');
moduleManager.execOnLoad('modB', function() {
testCase.continueTesting();
assertLoaded('modA');
assertLoaded('modB');
assertEquals('REQUEST_SUCCESS',
2, observer.getEvents(EventType.REQUEST_SUCCESS).length);
assertArrayEquals(
['modA'], observer.getEvents(EventType.REQUEST_SUCCESS)[0].moduleIds);
assertArrayEquals(
['modB'], observer.getEvents(EventType.REQUEST_SUCCESS)[1].moduleIds);
assertEquals('REQUEST_ERROR',
0, observer.getEvents(EventType.REQUEST_ERROR).length);
});
}
function testPrefetchModuleAThenLoadModuleB() {
moduleManager.prefetchModule('modA');
testCase.waitForAsync('wait for module A load');
moduleManager.execOnLoad('modB', function() {
testCase.continueTesting();
assertLoaded('modA');
assertLoaded('modB');
assertEquals('REQUEST_SUCCESS',
2, observer.getEvents(EventType.REQUEST_SUCCESS).length);
assertArrayEquals(
['modA'], observer.getEvents(EventType.REQUEST_SUCCESS)[0].moduleIds);
assertArrayEquals(
['modB'], observer.getEvents(EventType.REQUEST_SUCCESS)[1].moduleIds);
assertEquals('REQUEST_ERROR',
0, observer.getEvents(EventType.REQUEST_ERROR).length);
});
}
function testLoadModuleBThenPrefetchModuleA() {
testCase.waitForAsync('wait for module A load');
moduleManager.execOnLoad('modB', function() {
testCase.continueTesting();
assertLoaded('modA');
assertLoaded('modB');
assertEquals('REQUEST_SUCCESS',
2, observer.getEvents(EventType.REQUEST_SUCCESS).length);
assertArrayEquals(
['modA'], observer.getEvents(EventType.REQUEST_SUCCESS)[0].moduleIds);
assertArrayEquals(
['modB'], observer.getEvents(EventType.REQUEST_SUCCESS)[1].moduleIds);
assertEquals('REQUEST_ERROR',
0, observer.getEvents(EventType.REQUEST_ERROR).length);
assertThrows('Module load already requested: modB',
function() {
moduleManager.prefetchModule('modA')
});
});
}
function testPrefetchModuleWithBatchModeEnabled() {
moduleManager.setBatchModeEnabled(true);
assertThrows('Modules prefetching is not supported in batch mode',
function() {
moduleManager.prefetchModule('modA');
});
}
function testLoadErrorCallbackExecutedWhenPrefetchFails() {
// Make all XHRs throw an error, so that we test the error-handling
// functionality.
var oldXmlHttp = goog.net.XmlHttp;
stubs.set(goog.net, 'XmlHttp', function() {
return {
open: goog.functions.error('mock error'),
abort: goog.nullFunction
};
});
goog.object.extend(goog.net.XmlHttp, oldXmlHttp);
var errorCount = 0;
var errorHandler = function() {
errorCount++;
};
moduleManager.registerCallback(
goog.module.ModuleManager.CallbackType.ERROR,
errorHandler);
moduleLoader.prefetchModule('modA', moduleManager.moduleInfoMap_['modA']);
moduleLoader.loadModules(['modA'], moduleManager.moduleInfoMap_,
function() {
fail('modA should not load successfully')
}, errorHandler);
assertEquals(1, errorCount);
}
function assertLoaded(id) {
assertTrue(moduleManager.getModuleInfo(id).isLoaded());
}
function assertNotLoaded(id) {
assertFalse(moduleManager.getModuleInfo(id).isLoaded());
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,23 @@
// Copyright 2009 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.
// All Rights Reserved
/**
* @fileoverview File #1 of module A.
*/
goog.provide('goog.module.testdata.modA_1');
if (window.modA1Loaded) throw Error('modA_1 loaded twice');
window.modA1Loaded = true;
@@ -0,0 +1,27 @@
// Copyright 2009 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.
// All Rights Reserved
/**
* @fileoverview File #2 of module A.
*/
goog.provide('goog.module.testdata.modA_2');
goog.require('goog.module.ModuleManager');
if (window.modA2Loaded) throw Error('modA_2 loaded twice');
window.modA2Loaded = true;
goog.module.ModuleManager.getInstance().setLoaded('modA');
@@ -0,0 +1,31 @@
// Copyright 2009 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.
// All Rights Reserved
/**
* @fileoverview File #1 of module B.
*/
goog.provide('goog.module.testdata.modB_1');
goog.require('goog.module.ModuleManager');
function throwErrorInModuleB() {
throw Error();
}
if (window.modB1Loaded) throw Error('modB_1 loaded twice');
window.modB1Loaded = true;
goog.module.ModuleManager.getInstance().setLoaded('modB');