5a920362 |
1 | require = (function () { |
2 | /*! |
3 | |
4 | Copyright (c) 2011 Chad Weider |
5 | |
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: |
12 | |
13 | The above copyright notice and this permission notice shall be included in |
14 | all copies or substantial portions of the Software. |
15 | |
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 |
22 | SOFTWARE. |
23 | |
24 | */ |
25 | |
26 | /* Storage */ |
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; |
35 | |
36 | var syncLock = undefined; |
37 | var globalKeyPath = undefined; |
38 | |
39 | var rootURI = undefined; |
40 | var libraryURI = undefined; |
41 | |
42 | var JSONP_TIMEOUT = 60 * 1000; |
43 | |
44 | function CircularDependencyError(message) { |
45 | this.name = "CircularDependencyError"; |
46 | this.message = message; |
47 | }; |
48 | CircularDependencyError.prototype = Error.prototype; |
49 | function ArgumentError(message) { |
50 | this.name = "ArgumentError"; |
51 | this.message = message; |
52 | }; |
53 | ArgumentError.prototype = Error.prototype; |
54 | |
55 | /* Utility */ |
56 | function hasOwnProperty(object, key) { |
57 | // Object-independent because an object may define `hasOwnProperty`. |
58 | return Object.prototype.hasOwnProperty.call(object, key); |
59 | } |
60 | |
61 | // See RFC 2396 Appendix B |
62 | var URI_EXPRESSION = |
63 | /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/; |
64 | function parseURI(uri) { |
65 | var match = uri.match(URI_EXPRESSION); |
66 | var location = match && { |
67 | scheme: match[2], |
68 | host: match[4], |
69 | path: match[5], |
70 | query: match[7], |
71 | fragment: match[9] |
72 | }; |
73 | return location; |
74 | } |
75 | |
76 | function joinURI(location) { |
77 | var uri = ""; |
78 | if (location.scheme) |
79 | uri += location.scheme + ':'; |
80 | if (location.host) |
81 | uri += "//" + location.host |
82 | if (location.path) |
83 | uri += location.path |
84 | if (location.query) |
85 | uri += "?" + location.query |
86 | if (uri.fragment) |
87 | uri += "#" + location.fragment |
88 | |
89 | return uri; |
90 | } |
91 | |
92 | function isSameDomain(uri) { |
93 | var host_uri = |
94 | (typeof location == "undefined") ? {} : parseURI(location.toString()); |
95 | var uri = parseURI(uri); |
96 | |
97 | return (uri.scheme === host_uri.scheme) && (uri.host === host_uri.host); |
98 | } |
99 | |
100 | function mirroredURIForURI(uri) { |
101 | var host_uri = |
102 | (typeof location == "undefined") ? {} : parseURI(location.toString()); |
103 | var uri = parseURI(uri); |
104 | |
105 | uri.scheme = host_uri.scheme; |
106 | uri.host = host_uri.host; |
107 | return joinURI(uri); |
108 | } |
109 | |
110 | function normalizePath(path) { |
111 | var pathComponents1 = path.split('/'); |
112 | var pathComponents2 = []; |
113 | |
114 | var component; |
115 | for (var i = 0, ii = pathComponents1.length; i < ii; i++) { |
116 | component = pathComponents1[i]; |
117 | switch (component) { |
118 | case '': |
119 | if (i == ii - 1) { |
120 | pathComponents2.push(component); |
121 | break; |
122 | } |
123 | case '.': |
124 | if (i == 0) { |
125 | pathComponents2.push(component); |
126 | } |
127 | break; |
128 | case '..': |
129 | if (pathComponents2.length > 1 |
130 | || (pathComponents2.length == 1 |
131 | && pathComponents2[0] != '' |
132 | && pathComponents2[0] != '.')) { |
133 | pathComponents2.pop(); |
134 | break; |
135 | } |
136 | default: |
137 | pathComponents2.push(component); |
138 | } |
139 | } |
140 | |
141 | return pathComponents2.join('/'); |
142 | } |
143 | |
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) == '/'))) { |
149 | if (!basePath) { |
150 | basePath = '/'; |
151 | } else if (basePath.charAt(basePath.length-1) != '/') { |
152 | basePath += '/'; |
153 | } |
154 | fullyQualifiedPath = basePath + path; |
155 | } |
156 | return fullyQualifiedPath; |
157 | } |
158 | |
159 | function setRootURI(URI) { |
160 | if (!URI) { |
161 | throw new ArgumentError("Invalid root URI."); |
162 | } |
163 | rootURI = (URI.charAt(URI.length-1) == '/' ? URI.slice(0,-1) : URI); |
164 | } |
165 | |
166 | function setLibraryURI(URI) { |
167 | libraryURI = (URI.charAt(URI.length-1) == '/' ? URI : URI + '/'); |
168 | } |
169 | |
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]); |
174 | } |
175 | path = components.join('/') |
176 | |
177 | if (path.charAt(0) == '/') { |
178 | if (!rootURI) { |
179 | throw new Error("Attempt to retrieve the root module " |
180 | + "\""+ path + "\" but no root URI is defined."); |
181 | } |
182 | return rootURI + path; |
183 | } else { |
184 | if (!libraryURI) { |
185 | throw new Error("Attempt to retrieve the library module " |
186 | + "\""+ path + "\" but no libary URI is defined."); |
187 | } |
188 | return libraryURI + path; |
189 | } |
190 | } |
191 | |
192 | function _compileFunction(code, filename) { |
193 | return new Function(code); |
194 | } |
195 | |
196 | function compileFunction(code, filename) { |
197 | var compileFunction = rootRequire._compileFunction || _compileFunction; |
198 | return compileFunction.apply(this, arguments); |
199 | } |
200 | |
201 | /* Remote */ |
202 | function setRequestMaximum (value) { |
203 | value == parseInt(value); |
204 | if (value > 0) { |
205 | maximumRequests = value; |
206 | checkScheduledfetchDefines(); |
207 | } else { |
208 | throw new ArgumentError("Value must be a positive integer.") |
209 | } |
210 | } |
211 | |
212 | function setGlobalKeyPath (value) { |
213 | globalKeyPath = value; |
214 | } |
215 | |
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")} |
221 | ]; |
222 | |
223 | function createXMLHTTPObject() { |
224 | var xmlhttp = false; |
225 | for (var i = 0, ii = XMLHttpFactories.length; i < ii; i++) { |
226 | try { |
227 | xmlhttp = XMLHttpFactories[i](); |
228 | } catch (error) { |
229 | continue; |
230 | } |
231 | break; |
232 | } |
233 | return xmlhttp; |
234 | } |
235 | |
236 | function getXHR(uri, async, callback, request) { |
237 | var request = request || createXMLHTTPObject(); |
238 | if (!request) { |
239 | throw new Error("Error making remote request.") |
240 | } |
241 | |
242 | function onComplete(request) { |
243 | // Build module constructor. |
244 | if (request.status == 200) { |
245 | callback(undefined, request.responseText); |
246 | } else { |
247 | callback(true, undefined); |
248 | } |
249 | } |
250 | |
251 | request.open('GET', uri, !!(async)); |
252 | if (async) { |
253 | request.onreadystatechange = function (event) { |
254 | if (request.readyState == 4) { |
255 | onComplete(request); |
256 | } |
257 | }; |
258 | request.send(null); |
259 | } else { |
260 | request.send(null); |
261 | onComplete(request); |
262 | } |
263 | } |
264 | |
265 | function getXDR(uri, callback) { |
266 | var xdr = new XDomainRequest(); |
267 | xdr.open('GET', uri); |
268 | xdr.error(function () { |
269 | callback(true, undefined); |
270 | }); |
271 | xdr.onload(function () { |
272 | callback(undefined, request.responseText); |
273 | }); |
274 | xdr.send(); |
275 | } |
276 | |
277 | function fetchDefineXHR(path, async) { |
278 | // If cross domain and request doesn't support such requests, go straight |
279 | // to mirroring. |
280 | |
281 | var _globalKeyPath = globalKeyPath; |
282 | |
283 | var callback = function (error, text) { |
284 | if (error) { |
285 | define(path, null); |
286 | } else { |
287 | if (_globalKeyPath) { |
288 | compileFunction(text, path)(); |
289 | } else { |
290 | var definition = compileFunction( |
291 | 'return (function (require, exports, module) {' |
292 | + text + '\n' |
293 | + '})', path)(); |
294 | define(path, definition); |
295 | } |
296 | } |
297 | } |
298 | |
299 | var uri = URIForModulePath(path); |
300 | if (_globalKeyPath) { |
301 | uri += '?callback=' + encodeURIComponent(globalKeyPath + '.define'); |
302 | } |
303 | if (isSameDomain(uri)) { |
304 | getXHR(uri, async, callback); |
305 | } else { |
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); |
311 | } else { |
312 | getXHR(mirroredURIForURI(uri), async, callback); |
313 | } |
314 | } |
315 | } |
316 | |
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"; |
324 | } else { |
325 | script.defer = "true"; |
326 | } |
327 | script.type = "application/javascript"; |
328 | script.src = URIForModulePath(path) |
329 | + '?callback=' + encodeURIComponent(globalKeyPath + '.define'); |
330 | |
331 | // Handle failure of JSONP request. |
332 | if (JSONP_TIMEOUT < Infinity) { |
333 | var timeoutId = setTimeout(function () { |
334 | timeoutId = undefined; |
335 | define(path, null); |
336 | }, JSONP_TIMEOUT); |
337 | definitionWaiters[path].unshift(function () { |
338 | timeoutId === undefined && clearTimeout(timeoutId); |
339 | }); |
340 | } |
341 | |
342 | head.insertBefore(script, head.firstChild); |
343 | } |
344 | |
345 | /* Modules */ |
346 | function fetchModule(path, continuation) { |
347 | if (hasOwnProperty(definitionWaiters, path)) { |
348 | definitionWaiters[path].push(continuation); |
349 | } else { |
350 | definitionWaiters[path] = [continuation]; |
351 | schedulefetchDefine(path); |
352 | } |
353 | } |
354 | |
355 | function schedulefetchDefine(path) { |
356 | fetchRequests.push(path); |
357 | checkScheduledfetchDefines(); |
358 | } |
359 | |
360 | function checkScheduledfetchDefines() { |
361 | if (fetchRequests.length > 0 && currentRequests < maximumRequests) { |
362 | var fetchRequest = fetchRequests.pop(); |
363 | currentRequests++; |
364 | definitionWaiters[fetchRequest].unshift(function () { |
365 | currentRequests--; |
366 | checkScheduledfetchDefines(); |
367 | }); |
368 | if (globalKeyPath |
369 | && ((typeof document != undefined) |
370 | && document.readyState && document.readyState != 'loading')) { |
371 | fetchDefineJSONP(fetchRequest); |
372 | } else { |
373 | fetchDefineXHR(fetchRequest, true); |
374 | } |
375 | } |
376 | } |
377 | |
378 | function fetchModuleSync(path, continuation) { |
379 | fetchDefineXHR(path, false); |
380 | continuation(); |
381 | } |
382 | |
383 | function moduleIsLoaded(path) { |
384 | return hasOwnProperty(modules, path); |
385 | } |
386 | |
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)) { |
392 | var error = |
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); |
400 | } else { |
401 | var definition = definitions[path]; |
402 | var _module = {id: path, exports: {}}; |
403 | var _require = requireRelativeTo(path.replace(/[^\/]+$/,'')); |
404 | if (!main) { |
405 | main = _module; |
406 | } |
407 | try { |
408 | loadingModules[path] = true; |
409 | definition(_require, _module.exports, _module); |
410 | modules[path] = _module; |
411 | delete loadingModules[path]; |
412 | continuation(undefined, _module); |
413 | } catch (error) { |
414 | delete loadingModules[path]; |
415 | continuation(error, undefined); |
416 | } |
417 | } |
418 | } else { |
419 | var module = modules[path]; |
420 | continuation(undefined, module); |
421 | } |
422 | } |
423 | |
424 | function _moduleAtPath(path, fetchFunc, continuation) { |
425 | var suffixes = ['', '.js', '/index.js']; |
426 | if (path.charAt(path.length - 1) == '/') { |
427 | suffixes = ['index.js']; |
428 | } |
429 | |
430 | var i = 0, ii = suffixes.length; |
431 | var _find = function (i) { |
432 | if (i < ii) { |
433 | var path_ = path + suffixes[i]; |
434 | var after = function () { |
435 | loadModule(path_, function (error, module) { |
436 | if (error) { |
437 | continuation(error, module); |
438 | } else if (module === null) { |
439 | _find(i + 1); |
440 | } else { |
441 | continuation(undefined, module); |
442 | } |
443 | }); |
444 | } |
445 | |
446 | if (!moduleIsDefined(path_)) { |
447 | fetchFunc(path_, after); |
448 | } else { |
449 | after(); |
450 | } |
451 | |
452 | } else { |
453 | continuation(undefined, null); |
454 | } |
455 | }; |
456 | _find(0); |
457 | } |
458 | |
459 | function moduleAtPath(path, continuation) { |
460 | var wrappedContinuation = function (error, module) { |
461 | if (error) { |
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 |
465 | // mechanism. |
466 | setTimeout(function () {moduleAtPath(path, continuation)}, 0); |
467 | } else { |
468 | continuation(null); |
469 | } |
470 | } else { |
471 | continuation(module); |
472 | } |
473 | }; |
474 | _moduleAtPath(path, fetchModule, wrappedContinuation); |
475 | } |
476 | |
477 | function moduleAtPathSync(path) { |
478 | var module; |
479 | var oldSyncLock = syncLock; |
480 | syncLock = true; |
481 | try { |
482 | _moduleAtPath(path, fetchModuleSync, function (error, _module) { |
483 | if (error) { |
484 | throw error; |
485 | } else { |
486 | module = _module |
487 | } |
488 | }); |
489 | } finally { |
490 | syncLock = oldSyncLock; |
491 | } |
492 | return module; |
493 | } |
494 | |
495 | /* Definition */ |
496 | function moduleIsDefined(path) { |
497 | return hasOwnProperty(definitions, path); |
498 | } |
499 | |
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."); |
505 | } |
506 | |
507 | if (moduleIsDefined(path)) { |
508 | // Drop import silently |
509 | } else { |
510 | definitions[path] = module; |
511 | } |
512 | } |
513 | |
514 | function defineModules(moduleMap) { |
515 | if (typeof moduleMap != 'object') { |
516 | throw new ArgumentError("Mapping must be an object."); |
517 | } |
518 | for (var path in moduleMap) { |
519 | if (hasOwnProperty(moduleMap, path)) { |
520 | defineModule(path, moduleMap[path]); |
521 | } |
522 | } |
523 | } |
524 | |
525 | function define(fullyQualifiedPathOrModuleMap, module) { |
526 | var moduleMap; |
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); |
533 | moduleMap = {}; |
534 | moduleMap[path] = module; |
535 | } else { |
536 | throw new ArgumentError("Expected 1 or 2 arguments, but got " |
537 | + arguments.length + "."); |
538 | } |
539 | |
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]; |
547 | } |
548 | } |
549 | function satisfy() { |
550 | // Let exceptions happen, but don't allow them to break notification. |
551 | try { |
552 | while (continuations.length) { |
553 | var continuation = continuations.shift(); |
554 | continuation(); |
555 | } |
556 | } finally { |
557 | continuations.length && setTimeout(satisfy, 0); |
558 | } |
559 | } |
560 | |
561 | if (syncLock) { |
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); |
565 | } else { |
566 | satisfy(continuations); |
567 | } |
568 | } |
569 | |
570 | /* Require */ |
571 | function _designatedRequire(path, continuation) { |
572 | if (continuation === undefined) { |
573 | var module = moduleAtPathSync(path); |
574 | if (!module) { |
575 | throw new Error("The module at \"" + path + "\" does not exist."); |
576 | } |
577 | return module.exports; |
578 | } else { |
579 | if (!(continuation instanceof Function)) { |
580 | throw new ArgumentError("Continuation must be a function."); |
581 | } |
582 | |
583 | moduleAtPath(path, function (module) { |
584 | continuation(module && module.exports); |
585 | }); |
586 | } |
587 | } |
588 | |
589 | function designatedRequire(path, continuation) { |
590 | var designatedRequire = |
591 | rootRequire._designatedRequire || _designatedRequire; |
592 | return designatedRequire.apply(this, arguments); |
593 | } |
594 | |
595 | function requireRelative(basePath, qualifiedPath, continuation) { |
596 | qualifiedPath = qualifiedPath.toString(); |
597 | var path = normalizePath(fullyQualifyPath(qualifiedPath, basePath)); |
598 | return designatedRequire(path, continuation); |
599 | } |
600 | |
601 | function requireRelativeN(basePath, qualifiedPaths, continuation) { |
602 | if (!(continuation instanceof Function)) { |
603 | throw new ArgumentError("Final argument must be a continuation."); |
604 | } else { |
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(); |
609 | } |
610 | var results = []; |
611 | function _require(result) { |
612 | results.push(result); |
613 | if (qualifiedPaths.length > 0) { |
614 | requireRelative(basePath, qualifiedPaths.shift(), _require); |
615 | } else { |
616 | continuation.apply(this, results); |
617 | } |
618 | } |
619 | for (var i = 0, ii = qualifiedPaths.length; i < ii; i++) { |
620 | requireRelative(basePath, _qualifiedPaths[i], _require); |
621 | } |
622 | } |
623 | } |
624 | |
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); |
631 | } else { |
632 | return requireRelative(basePath, qualifiedPath, continuation); |
633 | } |
634 | } |
635 | require.main = main; |
636 | |
637 | return require; |
638 | } |
639 | |
640 | var rootRequire = requireRelativeTo('/'); |
641 | |
642 | /* Private internals */ |
643 | rootRequire._modules = modules; |
644 | rootRequire._definitions = definitions; |
645 | rootRequire._designatedRequire = _designatedRequire; |
646 | rootRequire._compileFunction = _compileFunction; |
647 | |
648 | /* Public interface */ |
649 | rootRequire.define = define; |
650 | rootRequire.setRequestMaximum = setRequestMaximum; |
651 | rootRequire.setGlobalKeyPath = setGlobalKeyPath; |
652 | rootRequire.setRootURI = setRootURI; |
653 | rootRequire.setLibraryURI = setLibraryURI; |
654 | |
655 | return rootRequire; |
656 | }()); |