Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/third_party/closure/goog/module/loader.js
2868 views
1
// Copyright 2006 The Closure Library Authors. All Rights Reserved.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS-IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
/**
16
*
17
* @fileoverview This class supports the dynamic loading of compiled
18
* javascript modules at runtime, as described in the designdoc.
19
*
20
* <http://go/js_modules_design>
21
*
22
*/
23
24
goog.provide('goog.module.Loader');
25
26
goog.require('goog.Timer');
27
goog.require('goog.array');
28
goog.require('goog.asserts');
29
goog.require('goog.dom');
30
goog.require('goog.dom.TagName');
31
/** @suppress {extraRequire} */
32
goog.require('goog.module');
33
goog.require('goog.object');
34
35
36
37
/**
38
* The dynamic loading functionality is defined as a class. The class
39
* will be used as singleton. There is, however, a two step
40
* initialization procedure because parameters need to be passed to
41
* the goog.module.Loader instance.
42
*
43
* @constructor
44
* @final
45
*/
46
goog.module.Loader = function() {
47
/**
48
* Map of module name/array of {symbol name, callback} pairs that are pending
49
* to be loaded.
50
* @type {Object}
51
* @private
52
*/
53
this.pending_ = {};
54
55
/**
56
* Provides associative access to each module and the symbols of each module
57
* that have already been loaded (one lookup for the module, another lookup
58
* on the module for the symbol).
59
* @type {Object}
60
* @private
61
*/
62
this.modules_ = {};
63
64
/**
65
* Map of module name to module url. Used to avoid fetching the same URL
66
* twice by keeping track of in-flight URLs.
67
* Note: this allows two modules to be bundled into the same file.
68
* @type {Object}
69
* @private
70
*/
71
this.pendingModuleUrls_ = {};
72
73
/**
74
* The base url to load modules from. This property will be set in init().
75
* @type {?string}
76
* @private
77
*/
78
this.urlBase_ = null;
79
80
/**
81
* Array of modules that have been requested before init() was called.
82
* If require() is called before init() was called, the required
83
* modules can obviously not yet be loaded, because their URL is
84
* unknown. The modules that are requested before init() are
85
* therefore stored in this array, and they are loaded at init()
86
* time.
87
* @type {Array<string>}
88
* @private
89
*/
90
this.pendingBeforeInit_ = [];
91
};
92
goog.addSingletonGetter(goog.module.Loader);
93
94
95
/**
96
* Wrapper of goog.module.Loader.require() for use in modules.
97
* See method goog.module.Loader.require() for
98
* explanation of params.
99
*
100
* @param {string} module The name of the module. Usually, the value
101
* is defined as a constant whose name starts with MOD_.
102
* @param {number|string} symbol The ID of the symbol. Usually, the value is
103
* defined as a constant whose name starts with SYM_.
104
* @param {Function} callback This function will be called with the
105
* resolved symbol as the argument once the module is loaded.
106
*/
107
goog.module.Loader.require = function(module, symbol, callback) {
108
goog.module.Loader.getInstance().require(module, symbol, callback);
109
};
110
111
112
/**
113
* Wrapper of goog.module.Loader.provide() for use in modules
114
* See method goog.module.Loader.provide() for explanation of params.
115
*
116
* @param {string} module The name of the module. Cf. parameter module
117
* of method require().
118
* @param {number|string=} opt_symbol The symbol being defined, or nothing
119
* when all symbols of the module are defined. Cf. parameter symbol of
120
* method require().
121
* @param {Object=} opt_object The object bound to the symbol, or nothing when
122
* all symbols of the module are defined.
123
*/
124
goog.module.Loader.provide = function(module, opt_symbol, opt_object) {
125
goog.module.Loader.getInstance().provide(module, opt_symbol, opt_object);
126
};
127
128
129
/**
130
* Wrapper of init() so that we only need to export this single
131
* identifier instead of three. See method goog.module.Loader.init() for
132
* explanation of param.
133
*
134
* @param {string} urlBase The URL of the base library.
135
* @param {Function=} opt_urlFunction Function that creates the URL for the
136
* module file. It will be passed the base URL for module files and the
137
* module name and should return the fully-formed URL to the module file to
138
* load.
139
*/
140
goog.module.Loader.init = function(urlBase, opt_urlFunction) {
141
goog.module.Loader.getInstance().init(urlBase, opt_urlFunction);
142
};
143
144
145
/**
146
* Produces a function that delegates all its arguments to a
147
* dynamically loaded function. This is used to export dynamically
148
* loaded functions.
149
*
150
* @param {string} module The module to load from.
151
* @param {number|string} symbol The ID of the symbol to load from the module.
152
* This symbol must resolve to a function.
153
* @return {!Function} A function that forwards all its arguments to
154
* the dynamically loaded function specified by module and symbol.
155
*/
156
goog.module.Loader.loaderCall = function(module, symbol) {
157
return function() {
158
var args = arguments;
159
goog.module.Loader.require(
160
module, symbol, function(f) { f.apply(null, args); });
161
};
162
};
163
164
165
/**
166
* Creates a full URL to the compiled module code given a base URL and a
167
* module name. By default it's urlBase + '_' + module + '.js'.
168
* @param {string} urlBase URL to the module files.
169
* @param {string} module Module name.
170
* @return {string} The full url to the module binary.
171
* @private
172
*/
173
goog.module.Loader.prototype.getModuleUrl_ = function(urlBase, module) {
174
return urlBase + '_' + module + '.js';
175
};
176
177
178
/**
179
* The globally exported name of the load callback. Matches the
180
* definition in the js_module_binary() BUILD rule.
181
* @type {string}
182
*/
183
goog.module.Loader.LOAD_CALLBACK = '__gjsload__';
184
185
186
/**
187
* Loads the module by evaluating the javascript text in the current
188
* scope. Uncompiled, base identifiers are visible in the global scope;
189
* when compiled they are visible in the closure of the anonymous
190
* namespace. Notice that this cannot be replaced by the global eval,
191
* because the global eval isn't in the scope of the anonymous
192
* namespace function that the jscompiled code lives in.
193
*
194
* @param {string} t_ The javascript text to evaluate. IMPORTANT: The
195
* name of the identifier is chosen so that it isn't compiled and
196
* hence cannot shadow compiled identifiers in the surrounding scope.
197
* @private
198
*/
199
goog.module.Loader.loaderEval_ = function(t_) {
200
eval(t_);
201
};
202
203
204
/**
205
* Initializes the Loader to be fully functional. Also executes load
206
* requests that were received before initialization. Must be called
207
* exactly once, with the URL of the base library. Module URLs are
208
* derived from the URL of the base library by inserting the module
209
* name, preceded by a period, before the .js prefix of the base URL.
210
*
211
* @param {string} baseUrl The URL of the base library.
212
* @param {Function=} opt_urlFunction Function that creates the URL for the
213
* module file. It will be passed the base URL for module files and the
214
* module name and should return the fully-formed URL to the module file to
215
* load.
216
*/
217
goog.module.Loader.prototype.init = function(baseUrl, opt_urlFunction) {
218
// For the use by the module wrappers, loaderEval_ is exported to
219
// the page. Note that, despite the name, this is not part of the
220
// API, so it is here and not in api_app.js. Cf. BUILD. Note this is
221
// done before the first load requests are sent.
222
goog.exportSymbol(
223
goog.module.Loader.LOAD_CALLBACK, goog.module.Loader.loaderEval_);
224
225
this.urlBase_ = baseUrl.replace(/\.js$/, '');
226
if (opt_urlFunction) {
227
this.getModuleUrl_ = opt_urlFunction;
228
}
229
230
goog.array.forEach(
231
this.pendingBeforeInit_, function(module) { this.load_(module); }, this);
232
goog.array.clear(this.pendingBeforeInit_);
233
};
234
235
236
/**
237
* Requests the loading of a symbol from a module. When the module is
238
* loaded, the requested symbol will be passed as argument to the
239
* function callback.
240
*
241
* @param {string} module The name of the module. Usually, the value
242
* is defined as a constant whose name starts with MOD_.
243
* @param {number|string} symbol The ID of the symbol. Usually, the value is
244
* defined as a constant whose name starts with SYM_.
245
* @param {Function} callback This function will be called with the
246
* resolved symbol as the argument once the module is loaded.
247
*/
248
goog.module.Loader.prototype.require = function(module, symbol, callback) {
249
var pending = this.pending_;
250
var modules = this.modules_;
251
if (modules[module]) {
252
// already loaded
253
callback(modules[module][symbol]);
254
} else if (pending[module]) {
255
// loading is pending from another require of the same module
256
pending[module].push([symbol, callback]);
257
} else {
258
// not loaded, and not requested
259
pending[module] = [[symbol, callback]]; // Yes, really [[ ]].
260
// Defer loading to initialization if Loader is not yet
261
// initialized, otherwise load the module.
262
if (goog.isString(this.urlBase_)) {
263
this.load_(module);
264
} else {
265
this.pendingBeforeInit_.push(module);
266
}
267
}
268
};
269
270
271
/**
272
* Registers a symbol in a loaded module. When called without symbol,
273
* registers the module to be fully loaded and executes all callbacks
274
* from pending require() callbacks for this module.
275
*
276
* @param {string} module The name of the module. Cf. parameter module
277
* of method require().
278
* @param {number|string=} opt_symbol The symbol being defined, or nothing when
279
* all symbols of the module are defined. Cf. parameter symbol of method
280
* require().
281
* @param {Object=} opt_object The object bound to the symbol, or nothing when
282
* all symbols of the module are defined.
283
*/
284
goog.module.Loader.prototype.provide = function(
285
module, opt_symbol, opt_object) {
286
var modules = this.modules_;
287
var pending = this.pending_;
288
if (!modules[module]) {
289
modules[module] = {};
290
}
291
if (opt_object) {
292
// When an object is provided, just register it.
293
modules[module][opt_symbol] = opt_object;
294
} else if (pending[module]) {
295
// When no object is provided, and there are pending require()
296
// callbacks for this module, execute them.
297
for (var i = 0; i < pending[module].length; ++i) {
298
var symbol = pending[module][i][0];
299
var callback = pending[module][i][1];
300
callback(modules[module][symbol]);
301
}
302
delete pending[module];
303
delete this.pendingModuleUrls_[module];
304
}
305
};
306
307
308
/**
309
* Starts to load a module. Assumes that init() was called.
310
*
311
* @param {string} module The name of the module.
312
* @private
313
*/
314
goog.module.Loader.prototype.load_ = function(module) {
315
// NOTE(user): If the module request happens inside a click handler
316
// (presumably inside any user event handler, but the onload event
317
// handler is fine), IE will load the script but not execute
318
// it. Thus we break out of the current flow of control before we do
319
// the load. For the record, for IE it would have been enough to
320
// just defer the assignment to src. Safari doesn't execute the
321
// script if the assignment to src happens *after* the script
322
// element is inserted into the DOM.
323
goog.Timer.callOnce(function() {
324
// The module might have been registered in the interim (if fetched as part
325
// of another module fetch because they share the same url)
326
if (this.modules_[module]) {
327
return;
328
}
329
330
goog.asserts.assertString(this.urlBase_);
331
var url = this.getModuleUrl_(this.urlBase_, module);
332
333
// Check if specified URL is already in flight
334
var urlInFlight = goog.object.containsValue(this.pendingModuleUrls_, url);
335
this.pendingModuleUrls_[module] = url;
336
if (urlInFlight) {
337
return;
338
}
339
340
var s = goog.dom.createDom(
341
goog.dom.TagName.SCRIPT, {'type': 'text/javascript', 'src': url});
342
document.body.appendChild(s);
343
}, 0, this);
344
};
345
346