1 require
= (function () {
4 Copyright (c) 2011 Chad Weider
6 Permission is hereby granted, free of charge, to any person obtaining a copy
7 of this software and associated documentation files (the "Software"), to deal
8 in the Software without restriction, including without limitation the rights
9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 copies of the Software, and to permit persons to whom the Software is
11 furnished to do so, subject to the following conditions:
13 The above copyright notice and this permission notice shall be included in
14 all copies or substantial portions of the Software.
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 var main
= null; // Reference to main module in `modules`.
28 var modules
= {}; // Repository of module objects build from `definitions`.
29 var definitions
= {}; // Functions that construct `modules`.
30 var loadingModules
= {}; // Locks for detecting circular dependencies.
31 var definitionWaiters
= {}; // Locks for clearing duplicate requires.
32 var fetchRequests
= []; // Queue of pending requests.
33 var currentRequests
= 0; // Synchronization for parallel requests.
34 var maximumRequests
= 2;
36 var syncLock
= undefined;
37 var globalKeyPath
= undefined;
39 var rootURI
= undefined;
40 var libraryURI
= undefined;
42 var JSONP_TIMEOUT
= 60 * 1000;
44 function CircularDependencyError(message
) {
45 this.name
= "CircularDependencyError";
46 this.message
= message
;
48 CircularDependencyError
.prototype = Error
.prototype;
49 function ArgumentError(message
) {
50 this.name
= "ArgumentError";
51 this.message
= message
;
53 ArgumentError
.prototype = Error
.prototype;
56 function hasOwnProperty(object
, key
) {
57 // Object-independent because an object may define `hasOwnProperty`.
58 return Object
.prototype.hasOwnProperty
.call(object
, key
);
61 // See RFC 2396 Appendix B
63 /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/;
64 function parseURI(uri
) {
65 var match
= uri
.match(URI_EXPRESSION
);
66 var location
= match
&& {
76 function joinURI(location
) {
79 uri
+= location
.scheme
+ ':';
81 uri
+= "//" + location
.host
85 uri
+= "?" + location
.query
87 uri
+= "#" + location
.fragment
92 function isSameDomain(uri
) {
94 (typeof location
== "undefined") ? {} : parseURI(location
.toString());
95 var uri
= parseURI(uri
);
97 return (uri
.scheme
=== host_uri
.scheme
) && (uri
.host
=== host_uri
.host
);
100 function mirroredURIForURI(uri
) {
102 (typeof location
== "undefined") ? {} : parseURI(location
.toString());
103 var uri
= parseURI(uri
);
105 uri
.scheme
= host_uri
.scheme
;
106 uri
.host
= host_uri
.host
;
110 function normalizePath(path
) {
111 var pathComponents1
= path
.split('/');
112 var pathComponents2
= [];
115 for (var i
= 0, ii
= pathComponents1
.length
; i
< ii
; i
++) {
116 component
= pathComponents1
[i
];
120 pathComponents2
.push(component
);
125 pathComponents2
.push(component
);
129 if (pathComponents2
.length
> 1
130 || (pathComponents2
.length
== 1
131 && pathComponents2
[0] != ''
132 && pathComponents2
[0] != '.')) {
133 pathComponents2
.pop();
137 pathComponents2
.push(component
);
141 return pathComponents2
.join('/');
144 function fullyQualifyPath(path
, basePath
) {
145 var fullyQualifiedPath
= path
;
146 if (path
.charAt(0) == '.'
147 && (path
.charAt(1) == '/'
148 || (path
.charAt(1) == '.' && path
.charAt(2) == '/'))) {
151 } else if (basePath
.charAt(basePath
.length
-1) != '/') {
154 fullyQualifiedPath
= basePath
+ path
;
156 return fullyQualifiedPath
;
159 function setRootURI(URI
) {
161 throw new ArgumentError("Invalid root URI.");
163 rootURI
= (URI
.charAt(URI
.length
-1) == '/' ? URI
.slice(0,-1) : URI
);
166 function setLibraryURI(URI
) {
167 libraryURI
= (URI
.charAt(URI
.length
-1) == '/' ? URI
: URI
+ '/');
170 function URIForModulePath(path
) {
171 var components
= path
.split('/');
172 for (var i
= 0, ii
= components
.length
; i
< ii
; i
++) {
173 components
[i
] = encodeURIComponent(components
[i
]);
175 path
= components
.join('/')
177 if (path
.charAt(0) == '/') {
179 throw new Error("Attempt to retrieve the root module "
180 + "\""+ path
+ "\" but no root URI is defined.");
182 return rootURI
+ path
;
185 throw new Error("Attempt to retrieve the library module "
186 + "\""+ path
+ "\" but no libary URI is defined.");
188 return libraryURI
+ path
;
192 function _compileFunction(code
, filename
) {
193 return new Function(code
);
196 function compileFunction(code
, filename
) {
197 var compileFunction
= rootRequire
._compileFunction
|| _compileFunction
;
198 return compileFunction
.apply(this, arguments
);
202 function setRequestMaximum (value
) {
203 value
== parseInt(value
);
205 maximumRequests
= value
;
206 checkScheduledfetchDefines();
208 throw new ArgumentError("Value must be a positive integer.")
212 function setGlobalKeyPath (value
) {
213 globalKeyPath
= value
;
216 var XMLHttpFactories
= [
217 function () {return new XMLHttpRequest()},
218 function () {return new ActiveXObject("Msxml2.XMLHTTP")},
219 function () {return new ActiveXObject("Msxml3.XMLHTTP")},
220 function () {return new ActiveXObject("Microsoft.XMLHTTP")}
223 function createXMLHTTPObject() {
225 for (var i
= 0, ii
= XMLHttpFactories
.length
; i
< ii
; i
++) {
227 xmlhttp
= XMLHttpFactories
[i
]();
236 function getXHR(uri
, async
, callback
, request
) {
237 var request
= request
|| createXMLHTTPObject();
239 throw new Error("Error making remote request.")
242 function onComplete(request
) {
243 // Build module constructor.
244 if (request
.status
== 200) {
245 callback(undefined, request
.responseText
);
247 callback(true, undefined);
251 request
.open('GET', uri
, !!(async
));
253 request
.onreadystatechange = function (event
) {
254 if (request
.readyState
== 4) {
265 function getXDR(uri
, callback
) {
266 var xdr
= new XDomainRequest();
267 xdr
.open('GET', uri
);
268 xdr
.error(function () {
269 callback(true, undefined);
271 xdr
.onload(function () {
272 callback(undefined, request
.responseText
);
277 function fetchDefineXHR(path
, async
) {
278 // If cross domain and request doesn't support such requests, go straight
281 var _globalKeyPath
= globalKeyPath
;
283 var callback = function (error
, text
) {
287 if (_globalKeyPath
) {
288 compileFunction(text
, path
)();
290 var definition
= compileFunction(
291 'return (function (require, exports, module) {'
294 define(path
, definition
);
299 var uri
= URIForModulePath(path
);
300 if (_globalKeyPath
) {
301 uri
+= '?callback=' + encodeURIComponent(globalKeyPath
+ '.define');
303 if (isSameDomain(uri
)) {
304 getXHR(uri
, async
, callback
);
306 var request
= createXMLHTTPObject();
307 if (request
&& request
.withCredentials
!== undefined) {
308 getXHR(uri
, async
, callback
, request
);
309 } else if (async
&& (typeof XDomainRequest
!= "undefined")) {
310 getXDR(uri
, callback
);
312 getXHR(mirroredURIForURI(uri
), async
, callback
);
317 function fetchDefineJSONP(path
) {
318 var head
= document
.head
319 || document
.getElementsByTagName('head')[0]
320 || document
.documentElement
;
321 var script
= document
.createElement('script');
322 if (script
.async
!== undefined) {
323 script
.async
= "true";
325 script
.defer
= "true";
327 script
.type
= "application/javascript";
328 script
.src
= URIForModulePath(path
)
329 + '?callback=' + encodeURIComponent(globalKeyPath
+ '.define');
331 // Handle failure of JSONP request.
332 if (JSONP_TIMEOUT
< Infinity
) {
333 var timeoutId
= setTimeout(function () {
334 timeoutId
= undefined;
337 definitionWaiters
[path
].unshift(function () {
338 timeoutId
=== undefined && clearTimeout(timeoutId
);
342 head
.insertBefore(script
, head
.firstChild
);
346 function fetchModule(path
, continuation
) {
347 if (hasOwnProperty(definitionWaiters
, path
)) {
348 definitionWaiters
[path
].push(continuation
);
350 definitionWaiters
[path
] = [continuation
];
351 schedulefetchDefine(path
);
355 function schedulefetchDefine(path
) {
356 fetchRequests
.push(path
);
357 checkScheduledfetchDefines();
360 function checkScheduledfetchDefines() {
361 if (fetchRequests
.length
> 0 && currentRequests
< maximumRequests
) {
362 var fetchRequest
= fetchRequests
.pop();
364 definitionWaiters
[fetchRequest
].unshift(function () {
366 checkScheduledfetchDefines();
369 && ((typeof document
!= undefined)
370 && document
.readyState
&& document
.readyState
!= 'loading')) {
371 fetchDefineJSONP(fetchRequest
);
373 fetchDefineXHR(fetchRequest
, true);
378 function fetchModuleSync(path
, continuation
) {
379 fetchDefineXHR(path
, false);
383 function moduleIsLoaded(path
) {
384 return hasOwnProperty(modules
, path
);
387 function loadModule(path
, continuation
) {
388 // If it's a function then it hasn't been exported yet. Run function and
389 // then replace with exports result.
390 if (!moduleIsLoaded(path
)) {
391 if (hasOwnProperty(loadingModules
, path
)) {
393 new CircularDependencyError("Encountered circular dependency.")
394 continuation(error
, undefined);
395 } else if (!moduleIsDefined(path
)) {
396 var error
= new Error("Attempt to load undefined module.")
397 continuation(error
, undefined);
398 } else if (definitions
[path
] === null) {
399 continuation(undefined, null);
401 var definition
= definitions
[path
];
402 var _module
= {id
: path
, exports
: {}};
403 var _require
= requireRelativeTo(path
.replace(/[^\/]+$/,''));
408 loadingModules
[path
] = true;
409 definition(_require
, _module
.exports
, _module
);
410 modules
[path
] = _module
;
411 delete loadingModules
[path
];
412 continuation(undefined, _module
);
414 delete loadingModules
[path
];
415 continuation(error
, undefined);
419 var module
= modules
[path
];
420 continuation(undefined, module
);
424 function _moduleAtPath(path
, fetchFunc
, continuation
) {
425 var suffixes
= ['', '.js', '/index.js'];
426 if (path
.charAt(path
.length
- 1) == '/') {
427 suffixes
= ['index.js'];
430 var i
= 0, ii
= suffixes
.length
;
431 var _find = function (i
) {
433 var path_
= path
+ suffixes
[i
];
434 var after = function () {
435 loadModule(path_
, function (error
, module
) {
437 continuation(error
, module
);
438 } else if (module
=== null) {
441 continuation(undefined, module
);
446 if (!moduleIsDefined(path_
)) {
447 fetchFunc(path_
, after
);
453 continuation(undefined, null);
459 function moduleAtPath(path
, continuation
) {
460 var wrappedContinuation = function (error
, module
) {
462 if (error
instanceof CircularDependencyError
) {
463 // Are the conditions for deadlock satisfied or not?
464 // TODO: This and define's satisfy should use a common deferral
466 setTimeout(function () {moduleAtPath(path
, continuation
)}, 0);
471 continuation(module
);
474 _moduleAtPath(path
, fetchModule
, wrappedContinuation
);
477 function moduleAtPathSync(path
) {
479 var oldSyncLock
= syncLock
;
482 _moduleAtPath(path
, fetchModuleSync
, function (error
, _module
) {
490 syncLock
= oldSyncLock
;
496 function moduleIsDefined(path
) {
497 return hasOwnProperty(definitions
, path
);
500 function defineModule(path
, module
) {
501 if (typeof path
!= 'string'
502 || !((module
instanceof Function
) || module
=== null)) {
503 throw new ArgumentError(
504 "Definition must be a (string, function) pair.");
507 if (moduleIsDefined(path
)) {
508 // Drop import silently
510 definitions
[path
] = module
;
514 function defineModules(moduleMap
) {
515 if (typeof moduleMap
!= 'object') {
516 throw new ArgumentError("Mapping must be an object.");
518 for (var path
in moduleMap
) {
519 if (hasOwnProperty(moduleMap
, path
)) {
520 defineModule(path
, moduleMap
[path
]);
525 function define(fullyQualifiedPathOrModuleMap
, module
) {
527 if (arguments
.length
== 1) {
528 moduleMap
= fullyQualifiedPathOrModuleMap
;
529 defineModules(moduleMap
);
530 } else if (arguments
.length
== 2) {
531 var path
= fullyQualifiedPathOrModuleMap
;
532 defineModule(fullyQualifiedPathOrModuleMap
, module
);
534 moduleMap
[path
] = module
;
536 throw new ArgumentError("Expected 1 or 2 arguments, but got "
537 + arguments
.length
+ ".");
540 // With all modules installed satisfy those conditions for all waiters.
541 var continuations
= [];
542 for (var path
in moduleMap
) {
543 if (hasOwnProperty(moduleMap
, path
)
544 && hasOwnProperty(definitionWaiters
, path
)) {
545 continuations
.push
.apply(continuations
, definitionWaiters
[path
]);
546 delete definitionWaiters
[path
];
550 // Let exceptions happen, but don't allow them to break notification.
552 while (continuations
.length
) {
553 var continuation
= continuations
.shift();
557 continuations
.length
&& setTimeout(satisfy
, 0);
562 // Only asynchronous operations will wait on this condition so schedule
563 // and don't interfere with the synchronous operation in progress.
564 setTimeout(function () {satisfy(continuations
)}, 0);
566 satisfy(continuations
);
571 function _designatedRequire(path
, continuation
) {
572 if (continuation
=== undefined) {
573 var module
= moduleAtPathSync(path
);
575 throw new Error("The module at \"" + path
+ "\" does not exist.");
577 return module
.exports
;
579 if (!(continuation
instanceof Function
)) {
580 throw new ArgumentError("Continuation must be a function.");
583 moduleAtPath(path
, function (module
) {
584 continuation(module
&& module
.exports
);
589 function designatedRequire(path
, continuation
) {
590 var designatedRequire
=
591 rootRequire
._designatedRequire
|| _designatedRequire
;
592 return designatedRequire
.apply(this, arguments
);
595 function requireRelative(basePath
, qualifiedPath
, continuation
) {
596 qualifiedPath
= qualifiedPath
.toString();
597 var path
= normalizePath(fullyQualifyPath(qualifiedPath
, basePath
));
598 return designatedRequire(path
, continuation
);
601 function requireRelativeN(basePath
, qualifiedPaths
, continuation
) {
602 if (!(continuation
instanceof Function
)) {
603 throw new ArgumentError("Final argument must be a continuation.");
605 // Copy and validate parameters
606 var _qualifiedPaths
= [];
607 for (var i
= 0, ii
= qualifiedPaths
.length
; i
< ii
; i
++) {
608 _qualifiedPaths
[i
] = qualifiedPaths
[i
].toString();
611 function _require(result
) {
612 results
.push(result
);
613 if (qualifiedPaths
.length
> 0) {
614 requireRelative(basePath
, qualifiedPaths
.shift(), _require
);
616 continuation
.apply(this, results
);
619 for (var i
= 0, ii
= qualifiedPaths
.length
; i
< ii
; i
++) {
620 requireRelative(basePath
, _qualifiedPaths
[i
], _require
);
625 var requireRelativeTo = function (basePath
) {
626 function require(qualifiedPath
, continuation
) {
627 if (arguments
.length
> 2) {
628 var qualifiedPaths
= Array
.prototype.slice
.call(arguments
, 0, -1);
629 var continuation
= arguments
[arguments
.length
-1];
630 return requireRelativeN(basePath
, qualifiedPaths
, continuation
);
632 return requireRelative(basePath
, qualifiedPath
, continuation
);
640 var rootRequire
= requireRelativeTo('/');
642 /* Private internals */
643 rootRequire
._modules
= modules
;
644 rootRequire
._definitions
= definitions
;
645 rootRequire
._designatedRequire
= _designatedRequire
;
646 rootRequire
._compileFunction
= _compileFunction
;
648 /* Public interface */
649 rootRequire
.define
= define
;
650 rootRequire
.setRequestMaximum
= setRequestMaximum
;
651 rootRequire
.setGlobalKeyPath
= setGlobalKeyPath
;
652 rootRequire
.setRootURI
= setRootURI
;
653 rootRequire
.setLibraryURI
= setLibraryURI
;