UNPKG

33.6 kBJavaScriptView Raw
1/**
2 * @preserve SeaJS - A Module Loader for the Web
3 * v1.2.1 | seajs.org | MIT Licensed
4 */
5
6
7/**
8 * Base namespace for the framework.
9 */
10this.seajs = { _seajs: this.seajs }
11
12
13/**
14 * The version of the framework. It will be replaced with "major.minor.patch"
15 * when building.
16 */
17seajs.version = '1.2.1'
18
19
20/**
21 * The private utilities. Internal use only.
22 */
23seajs._util = {}
24
25
26/**
27 * The private configuration data. Internal use only.
28 */
29seajs._config = {
30
31 /**
32 * Debug mode. It will be turned off automatically when compressing.
33 */
34 debug: '%DEBUG%',
35
36 /**
37 * Modules that are needed to load before all other modules.
38 */
39 preload: []
40}
41
42/**
43 * The minimal language enhancement
44 */
45;(function(util) {
46
47 var toString = Object.prototype.toString
48 var AP = Array.prototype
49
50
51 util.isString = function(val) {
52 return toString.call(val) === '[object String]'
53 }
54
55
56 util.isFunction = function(val) {
57 return toString.call(val) === '[object Function]'
58 }
59
60
61 util.isRegExp = function(val) {
62 return toString.call(val) === '[object RegExp]'
63 }
64
65
66 util.isObject = function(val) {
67 return val === Object(val)
68 }
69
70
71 util.isArray = Array.isArray || function(val) {
72 return toString.call(val) === '[object Array]'
73 }
74
75
76 util.indexOf = AP.indexOf ?
77 function(arr, item) {
78 return arr.indexOf(item)
79 } :
80 function(arr, item) {
81 for (var i = 0; i < arr.length; i++) {
82 if (arr[i] === item) {
83 return i
84 }
85 }
86 return -1
87 }
88
89
90 var forEach = util.forEach = AP.forEach ?
91 function(arr, fn) {
92 arr.forEach(fn)
93 } :
94 function(arr, fn) {
95 for (var i = 0; i < arr.length; i++) {
96 fn(arr[i], i, arr)
97 }
98 }
99
100
101 util.map = AP.map ?
102 function(arr, fn) {
103 return arr.map(fn)
104 } :
105 function(arr, fn) {
106 var ret = []
107 forEach(arr, function(item, i, arr) {
108 ret.push(fn(item, i, arr))
109 })
110 return ret
111 }
112
113
114 util.filter = AP.filter ?
115 function(arr, fn) {
116 return arr.filter(fn)
117 } :
118 function(arr, fn) {
119 var ret = []
120 forEach(arr, function(item, i, arr) {
121 if (fn(item, i, arr)) {
122 ret.push(item)
123 }
124 })
125 return ret
126 }
127
128
129 var keys = util.keys = Object.keys || function(o) {
130 var ret = []
131
132 for (var p in o) {
133 if (o.hasOwnProperty(p)) {
134 ret.push(p)
135 }
136 }
137
138 return ret
139 }
140
141
142 util.unique = function(arr) {
143 var o = {}
144
145 forEach(arr, function(item) {
146 o[item] = 1
147 })
148
149 return keys(o)
150 }
151
152
153 util.now = Date.now || function() {
154 return new Date().getTime()
155 }
156
157})(seajs._util)
158
159/**
160 * The tiny console
161 */
162;(function(util, config) {
163
164 var AP = Array.prototype
165
166
167 /**
168 * The safe wrapper of console.log/error/...
169 */
170 util.log = function() {
171 if (typeof console !== 'undefined') {
172 var args = AP.slice.call(arguments)
173
174 var type = 'log'
175 var last = args[args.length - 1]
176 console[last] && (type = args.pop())
177
178 // Only show log info in debug mode
179 if (type === 'log' && !config.debug) return
180
181 var out = type === 'dir' ? args[0] : AP.join.call(args, ' ')
182 console[type](out)
183 }
184 }
185
186})(seajs._util, seajs._config)
187
188/**
189 * Path utilities
190 */
191;(function(util, config, global) {
192
193 var DIRNAME_RE = /.*(?=\/.*$)/
194 var MULTIPLE_SLASH_RE = /([^:\/])\/\/+/g
195 var FILE_EXT_RE = /\.(?:css|js)$/
196 var ROOT_RE = /^(.*?\w)(?:\/|$)/
197
198
199 /**
200 * Extracts the directory portion of a path.
201 * dirname('a/b/c.js') ==> 'a/b/'
202 * dirname('d.js') ==> './'
203 * @see http://jsperf.com/regex-vs-split/2
204 */
205 function dirname(path) {
206 var s = path.match(DIRNAME_RE)
207 return (s ? s[0] : '.') + '/'
208 }
209
210
211 /**
212 * Canonicalizes a path.
213 * realpath('./a//b/../c') ==> 'a/c'
214 */
215 function realpath(path) {
216 MULTIPLE_SLASH_RE.lastIndex = 0
217
218 // 'file:///a//b/c' ==> 'file:///a/b/c'
219 // 'http://a//b/c' ==> 'http://a/b/c'
220 if (MULTIPLE_SLASH_RE.test(path)) {
221 path = path.replace(MULTIPLE_SLASH_RE, '$1\/')
222 }
223
224 // 'a/b/c', just return.
225 if (path.indexOf('.') === -1) {
226 return path
227 }
228
229 var original = path.split('/')
230 var ret = [], part
231
232 for (var i = 0; i < original.length; i++) {
233 part = original[i]
234
235 if (part === '..') {
236 if (ret.length === 0) {
237 throw new Error('The path is invalid: ' + path)
238 }
239 ret.pop()
240 }
241 else if (part !== '.') {
242 ret.push(part)
243 }
244 }
245
246 return ret.join('/')
247 }
248
249
250 /**
251 * Normalizes an uri.
252 */
253 function normalize(uri) {
254 uri = realpath(uri)
255 var lastChar = uri.charAt(uri.length - 1)
256
257 if (lastChar === '/') {
258 return uri
259 }
260
261 // Adds the default '.js' extension except that the uri ends with #.
262 // ref: http://jsperf.com/get-the-last-character
263 if (lastChar === '#') {
264 uri = uri.slice(0, -1)
265 }
266 else if (uri.indexOf('?') === -1 && !FILE_EXT_RE.test(uri)) {
267 uri += '.js'
268 }
269
270 // Remove ':80/' for bug in IE
271 if (uri.indexOf(':80/') > 0) {
272 uri = uri.replace(':80/', '/')
273 }
274
275 return uri
276 }
277
278
279 /**
280 * Parses alias in the module id. Only parse the first part.
281 */
282 function parseAlias(id) {
283 // #xxx means xxx is already alias-parsed.
284 if (id.charAt(0) === '#') {
285 return id.substring(1)
286 }
287
288 var alias = config.alias
289
290 // Only top-level id needs to parse alias.
291 if (alias && isTopLevel(id)) {
292 var parts = id.split('/')
293 var first = parts[0]
294
295 if (alias.hasOwnProperty(first)) {
296 parts[0] = alias[first]
297 id = parts.join('/')
298 }
299 }
300
301 return id
302 }
303
304
305 var mapCache = {}
306
307 /**
308 * Converts the uri according to the map rules.
309 */
310 function parseMap(uri) {
311 // map: [[match, replace], ...]
312 var map = config.map || []
313 if (!map.length) return uri
314
315 var ret = uri
316
317 // Apply all matched rules in sequence.
318 for (var i = 0; i < map.length; i++) {
319 var rule = map[i]
320
321 if (util.isArray(rule) && rule.length === 2) {
322 var m = rule[0]
323
324 if (util.isString(m) && ret.indexOf(m) > -1 ||
325 util.isRegExp(m) && m.test(ret)) {
326 ret = ret.replace(m, rule[1])
327 }
328 }
329 else if (util.isFunction(rule)) {
330 ret = rule(ret)
331 }
332 }
333
334 if (ret !== uri) {
335 mapCache[ret] = uri
336 }
337
338 return ret
339 }
340
341
342 /**
343 * Gets the original uri.
344 */
345 function unParseMap(uri) {
346 return mapCache[uri] || uri
347 }
348
349
350 /**
351 * Converts id to uri.
352 */
353 function id2Uri(id, refUri) {
354 if (!id) return ''
355
356 id = parseAlias(id)
357 refUri || (refUri = pageUri)
358
359 var ret
360
361 // absolute id
362 if (isAbsolute(id)) {
363 ret = id
364 }
365 // relative id
366 else if (isRelative(id)) {
367 // Converts './a' to 'a', to avoid unnecessary loop in realpath.
368 if (id.indexOf('./') === 0) {
369 id = id.substring(2)
370 }
371 ret = dirname(refUri) + id
372 }
373 // root id
374 else if (isRoot(id)) {
375 ret = refUri.match(ROOT_RE)[1] + id
376 }
377 // top-level id
378 else {
379 ret = config.base + '/' + id
380 }
381
382 return normalize(ret)
383 }
384
385
386 function isAbsolute(id) {
387 return id.indexOf('://') > 0 || id.indexOf('//') === 0
388 }
389
390
391 function isRelative(id) {
392 return id.indexOf('./') === 0 || id.indexOf('../') === 0
393 }
394
395
396 function isRoot(id) {
397 return id.charAt(0) === '/' && id.charAt(1) !== '/'
398 }
399
400
401 function isTopLevel(id) {
402 var c = id.charAt(0)
403 return id.indexOf('://') === -1 && c !== '.' && c !== '/'
404 }
405
406
407 /**
408 * Normalizes pathname to start with '/'
409 * Ref: https://groups.google.com/forum/#!topic/seajs/9R29Inqk1UU
410 */
411 function normalizePathname(pathname) {
412 if (pathname.charAt(0) !== '/') {
413 pathname = '/' + pathname
414 }
415 return pathname
416 }
417
418
419 var loc = global['location']
420 var pageUri = loc.protocol + '//' + loc.host +
421 normalizePathname(loc.pathname)
422
423 // local file in IE: C:\path\to\xx.js
424 if (pageUri.indexOf('\\') > 0) {
425 pageUri = pageUri.replace(/\\/g, '/')
426 }
427
428
429 util.dirname = dirname
430 util.realpath = realpath
431 util.normalize = normalize
432
433 util.parseAlias = parseAlias
434 util.parseMap = parseMap
435 util.unParseMap = unParseMap
436
437 util.id2Uri = id2Uri
438 util.isAbsolute = isAbsolute
439 util.isRoot = isRoot
440 util.isTopLevel = isTopLevel
441
442 util.pageUri = pageUri
443
444})(seajs._util, seajs._config, this)
445
446/**
447 * Utilities for fetching js and css files
448 */
449;(function(util, config) {
450
451 var doc = document
452 var head = doc.head ||
453 doc.getElementsByTagName('head')[0] ||
454 doc.documentElement
455
456 var baseElement = head.getElementsByTagName('base')[0]
457
458 var IS_CSS_RE = /\.css(?:\?|$)/i
459 var READY_STATE_RE = /loaded|complete|undefined/
460
461 var currentlyAddingScript
462 var interactiveScript
463
464
465 util.fetch = function(url, callback, charset) {
466 var isCSS = IS_CSS_RE.test(url)
467 var node = document.createElement(isCSS ? 'link' : 'script')
468
469 if (charset) {
470 var cs = util.isFunction(charset) ? charset(url) : charset
471 cs && (node.charset = cs)
472 }
473
474 assetOnload(node, callback || noop)
475
476 if (isCSS) {
477 node.rel = 'stylesheet'
478 node.href = url
479 } else {
480 node.async = 'async'
481 node.src = url
482 }
483
484 // For some cache cases in IE 6-9, the script executes IMMEDIATELY after
485 // the end of the insertBefore execution, so use `currentlyAddingScript`
486 // to hold current node, for deriving url in `define`.
487 currentlyAddingScript = node
488
489 // ref: #185 & http://dev.jquery.com/ticket/2709
490 baseElement ?
491 head.insertBefore(node, baseElement) :
492 head.appendChild(node)
493
494 currentlyAddingScript = null
495 }
496
497 function assetOnload(node, callback) {
498 if (node.nodeName === 'SCRIPT') {
499 scriptOnload(node, callback)
500 } else {
501 styleOnload(node, callback)
502 }
503 }
504
505 function scriptOnload(node, callback) {
506
507 node.onload = node.onerror = node.onreadystatechange = function() {
508 if (READY_STATE_RE.test(node.readyState)) {
509
510 // Ensure only run once and handle memory leak in IE
511 node.onload = node.onerror = node.onreadystatechange = null
512
513 // Remove the script to reduce memory leak
514 if (node.parentNode && !config.debug) {
515 head.removeChild(node)
516 }
517
518 // Dereference the node
519 node = undefined
520
521 callback()
522 }
523 }
524
525 }
526
527 function styleOnload(node, callback) {
528
529 // for Old WebKit and Old Firefox
530 if (isOldWebKit || isOldFirefox) {
531 util.log('Start poll to fetch css')
532
533 setTimeout(function() {
534 poll(node, callback)
535 }, 1) // Begin after node insertion
536 }
537 else {
538 node.onload = node.onerror = function() {
539 node.onload = node.onerror = null
540 node = undefined
541 callback()
542 }
543 }
544
545 }
546
547 function poll(node, callback) {
548 var isLoaded
549
550 // for WebKit < 536
551 if (isOldWebKit) {
552 if (node['sheet']) {
553 isLoaded = true
554 }
555 }
556 // for Firefox < 9.0
557 else if (node['sheet']) {
558 try {
559 if (node['sheet'].cssRules) {
560 isLoaded = true
561 }
562 } catch (ex) {
563 // The value of `ex.name` is changed from
564 // 'NS_ERROR_DOM_SECURITY_ERR' to 'SecurityError' since Firefox 13.0
565 // But Firefox is less than 9.0 in here, So it is ok to just rely on
566 // 'NS_ERROR_DOM_SECURITY_ERR'
567 if (ex.name === 'NS_ERROR_DOM_SECURITY_ERR') {
568 isLoaded = true
569 }
570 }
571 }
572
573 setTimeout(function() {
574 if (isLoaded) {
575 // Place callback in here due to giving time for style rendering.
576 callback()
577 } else {
578 poll(node, callback)
579 }
580 }, 1)
581 }
582
583 function noop() {
584 }
585
586
587 util.getCurrentScript = function() {
588 if (currentlyAddingScript) {
589 return currentlyAddingScript
590 }
591
592 // For IE6-9 browsers, the script onload event may not fire right
593 // after the the script is evaluated. Kris Zyp found that it
594 // could query the script nodes and the one that is in "interactive"
595 // mode indicates the current script.
596 // Ref: http://goo.gl/JHfFW
597 if (interactiveScript &&
598 interactiveScript.readyState === 'interactive') {
599 return interactiveScript
600 }
601
602 var scripts = head.getElementsByTagName('script')
603
604 for (var i = 0; i < scripts.length; i++) {
605 var script = scripts[i]
606 if (script.readyState === 'interactive') {
607 interactiveScript = script
608 return script
609 }
610 }
611 }
612
613 util.getScriptAbsoluteSrc = function(node) {
614 return node.hasAttribute ? // non-IE6/7
615 node.src :
616 // see http://msdn.microsoft.com/en-us/library/ms536429(VS.85).aspx
617 node.getAttribute('src', 4)
618 }
619
620
621 util.importStyle = function(cssText, id) {
622 // Don't add multi times
623 if (id && doc.getElementById(id)) return
624
625 var element = doc.createElement('style')
626 id && (element.id = id)
627
628 // Adds to DOM first to avoid the css hack invalid
629 head.appendChild(element)
630
631 // IE
632 if (element.styleSheet) {
633 element.styleSheet.cssText = cssText
634 }
635 // W3C
636 else {
637 element.appendChild(doc.createTextNode(cssText))
638 }
639 }
640
641
642 var UA = navigator.userAgent
643
644 // `onload` event is supported in WebKit since 535.23
645 // Ref:
646 // - https://bugs.webkit.org/show_activity.cgi?id=38995
647 var isOldWebKit = Number(UA.replace(/.*AppleWebKit\/(\d+)\..*/, '$1')) < 536
648
649 // `onload/onerror` event is supported since Firefox 9.0
650 // Ref:
651 // - https://bugzilla.mozilla.org/show_bug.cgi?id=185236
652 // - https://developer.mozilla.org/en/HTML/Element/link#Stylesheet_load_events
653 var isOldFirefox = UA.indexOf('Firefox') > 0 &&
654 !('onload' in document.createElement('link'))
655
656
657 /**
658 * References:
659 * - http://unixpapa.com/js/dyna.html
660 * - ../test/research/load-js-css/test.html
661 * - ../test/issues/load-css/test.html
662 * - http://www.blaze.io/technical/ies-premature-execution-problem/
663 */
664
665})(seajs._util, seajs._config, this)
666
667/**
668 * The parser for dependencies
669 */
670;(function(util) {
671
672 var REQUIRE_RE = /(?:^|[^.$])\brequire\s*\(\s*(["'])([^"'\s\)]+)\1\s*\)/g
673
674
675 util.parseDependencies = function(code) {
676 // Parse these `requires`:
677 // var a = require('a');
678 // someMethod(require('b'));
679 // require('c');
680 // ...
681 // Doesn't parse:
682 // someInstance.require(...);
683 var ret = [], match
684
685 code = removeComments(code)
686 REQUIRE_RE.lastIndex = 0
687
688 while ((match = REQUIRE_RE.exec(code))) {
689 if (match[2]) {
690 ret.push(match[2])
691 }
692 }
693
694 return util.unique(ret)
695 }
696
697 // See: research/remove-comments-safely
698 function removeComments(code) {
699 return code
700 .replace(/^\s*\/\*[\s\S]*?\*\/\s*$/mg, '') // block comments
701 .replace(/^\s*\/\/.*$/mg, '') // line comments
702 }
703
704})(seajs._util)
705
706/**
707 * The core of loader
708 */
709;(function(seajs, util, config) {
710
711 var cachedModules = {}
712 var cachedModifiers = {}
713 var compileStack = []
714
715 var STATUS = {
716 'FETCHING': 1, // The module file is fetching now.
717 'FETCHED': 2, // The module file has been fetched.
718 'SAVED': 3, // The module info has been saved.
719 'READY': 4, // All dependencies and self are ready to compile.
720 'COMPILING': 5, // The module is in compiling now.
721 'COMPILED': 6 // The module is compiled and module.exports is available.
722 }
723
724
725 function Module(uri, status) {
726 this.uri = uri
727 this.status = status || 0
728
729 // this.id is set when saving
730 // this.dependencies is set when saving
731 // this.factory is set when saving
732 // this.exports is set when compiling
733 // this.parent is set when compiling
734 // this.require is set when compiling
735 }
736
737
738 Module.prototype._use = function(ids, callback) {
739 util.isString(ids) && (ids = [ids])
740 var uris = resolve(ids, this.uri)
741
742 this._load(uris, function() {
743 var args = util.map(uris, function(uri) {
744 return uri ? cachedModules[uri]._compile() : null
745 })
746
747 if (callback) {
748 callback.apply(null, args)
749 }
750 })
751 }
752
753
754 Module.prototype._load = function(uris, callback) {
755 var unLoadedUris = util.filter(uris, function(uri) {
756 return uri && (!cachedModules[uri] ||
757 cachedModules[uri].status < STATUS.READY)
758 })
759
760 var length = unLoadedUris.length
761 if (length === 0) {
762 callback()
763 return
764 }
765
766 var remain = length
767
768 for (var i = 0; i < length; i++) {
769 (function(uri) {
770 var module = cachedModules[uri] ||
771 (cachedModules[uri] = new Module(uri, STATUS.FETCHING))
772
773 module.status >= STATUS.FETCHED ? onFetched() : fetch(uri, onFetched)
774
775 function onFetched() {
776 // cachedModules[uri] is changed in un-correspondence case
777 module = cachedModules[uri]
778
779 if (module.status >= STATUS.SAVED) {
780 var deps = getPureDependencies(module)
781
782 if (deps.length) {
783 Module.prototype._load(deps, function() {
784 cb(module)
785 })
786 }
787 else {
788 cb(module)
789 }
790 }
791 // Maybe failed to fetch successfully, such as 404 or non-module.
792 // // In these cases, module.status stay at FETCHING or FETCHED.
793 else {
794 util.log('It is not a valid CMD module: ' + uri)
795 cb()
796 }
797 }
798
799 })(unLoadedUris[i])
800 }
801
802 function cb(module) {
803 (module || {}).status < STATUS.READY && (module.status = STATUS.READY)
804 --remain === 0 && callback()
805 }
806 }
807
808
809 Module.prototype._compile = function() {
810 var module = this
811 if (module.status === STATUS.COMPILED) {
812 return module.exports
813 }
814
815 // Just return null when:
816 // 1. the module file is 404.
817 // 2. the module file is not written with valid module format.
818 // 3. other error cases.
819 if (module.status < STATUS.READY && !hasModifiers(module)) {
820 return null
821 }
822
823 module.status = STATUS.COMPILING
824
825
826 function require(id) {
827 var uri = resolve(id, module.uri)
828 var child = cachedModules[uri]
829
830 // Just return null when uri is invalid.
831 if (!child) {
832 return null
833 }
834
835 // Avoids circular calls.
836 if (child.status === STATUS.COMPILING) {
837 return child.exports
838 }
839
840 child.parent = module
841 return child._compile()
842 }
843
844 require.async = function(ids, callback) {
845 module._use(ids, callback)
846 }
847
848 require.resolve = function(id) {
849 return resolve(id, module.uri)
850 }
851
852 require.cache = cachedModules
853
854
855 module.require = require
856 module.exports = {}
857 var factory = module.factory
858
859 if (util.isFunction(factory)) {
860 compileStack.push(module)
861 runInModuleContext(factory, module)
862 compileStack.pop()
863 }
864 else if (factory !== undefined) {
865 module.exports = factory
866 }
867
868 module.status = STATUS.COMPILED
869 execModifiers(module)
870 return module.exports
871 }
872
873
874 Module._define = function(id, deps, factory) {
875 var argsLength = arguments.length
876
877 // define(factory)
878 if (argsLength === 1) {
879 factory = id
880 id = undefined
881 }
882 // define(id || deps, factory)
883 else if (argsLength === 2) {
884 factory = deps
885 deps = undefined
886
887 // define(deps, factory)
888 if (util.isArray(id)) {
889 deps = id
890 id = undefined
891 }
892 }
893
894 // Parses dependencies.
895 if (!util.isArray(deps) && util.isFunction(factory)) {
896 deps = util.parseDependencies(factory.toString())
897 }
898
899 var meta = { id: id, dependencies: deps, factory: factory }
900 var derivedUri
901
902 // Try to derive uri in IE6-9 for anonymous modules.
903 if (document.attachEvent) {
904 // Try to get the current script.
905 var script = util.getCurrentScript()
906 if (script) {
907 derivedUri = util.unParseMap(util.getScriptAbsoluteSrc(script))
908 }
909
910 if (!derivedUri) {
911 util.log('Failed to derive URI from interactive script for:',
912 factory.toString(), 'warn')
913
914 // NOTE: If the id-deriving methods above is failed, then falls back
915 // to use onload event to get the uri.
916 }
917 }
918
919 // Gets uri directly for specific module.
920 var resolvedUri = id ? resolve(id) : derivedUri
921
922 if (resolvedUri) {
923 // For IE:
924 // If the first module in a package is not the cachedModules[derivedUri]
925 // self, it should assign to the correct module when found.
926 if (resolvedUri === derivedUri) {
927 var refModule = cachedModules[derivedUri]
928 if (refModule && refModule.realUri &&
929 refModule.status === STATUS.SAVED) {
930 cachedModules[derivedUri] = null
931 }
932 }
933
934 var module = save(resolvedUri, meta)
935
936 // For IE:
937 // Assigns the first module in package to cachedModules[derivedUrl]
938 if (derivedUri) {
939 // cachedModules[derivedUri] may be undefined in combo case.
940 if ((cachedModules[derivedUri] || {}).status === STATUS.FETCHING) {
941 cachedModules[derivedUri] = module
942 module.realUri = derivedUri
943 }
944 }
945 else {
946 firstModuleInPackage || (firstModuleInPackage = module)
947 }
948 }
949 else {
950 // Saves information for "memoizing" work in the onload event.
951 anonymousModuleMeta = meta
952 }
953
954 }
955
956
957 Module._getCompilingModule = function() {
958 return compileStack[compileStack.length - 1]
959 }
960
961
962 Module._find = function(selector) {
963 var matches = []
964
965 util.forEach(util.keys(cachedModules), function(uri) {
966 if (util.isString(selector) && uri.indexOf(selector) > -1 ||
967 util.isRegExp(selector) && selector.test(uri)) {
968 var module = cachedModules[uri]
969 module.exports && matches.push(module.exports)
970 }
971 })
972
973 return matches
974 }
975
976
977 Module._modify = function(id, modifier) {
978 var uri = resolve(id)
979 var module = cachedModules[uri]
980
981 if (module && module.status === STATUS.COMPILED) {
982 runInModuleContext(modifier, module)
983 }
984 else {
985 cachedModifiers[uri] || (cachedModifiers[uri] = [])
986 cachedModifiers[uri].push(modifier)
987 }
988
989 return seajs
990 }
991
992
993 // For plugin developers
994 Module.STATUS = STATUS
995 Module._resolve = util.id2Uri
996 Module._fetch = util.fetch
997 Module.cache = cachedModules
998
999
1000 // Helpers
1001 // -------
1002
1003 var fetchingList = {}
1004 var fetchedList = {}
1005 var callbackList = {}
1006 var anonymousModuleMeta = null
1007 var firstModuleInPackage = null
1008 var circularCheckStack = []
1009
1010 function resolve(ids, refUri) {
1011 if (util.isString(ids)) {
1012 return Module._resolve(ids, refUri)
1013 }
1014
1015 return util.map(ids, function(id) {
1016 return resolve(id, refUri)
1017 })
1018 }
1019
1020 function fetch(uri, callback) {
1021 var requestUri = util.parseMap(uri)
1022
1023 if (fetchedList[requestUri]) {
1024 // See test/issues/debug-using-map
1025 cachedModules[uri] = cachedModules[requestUri]
1026 callback()
1027 return
1028 }
1029
1030 if (fetchingList[requestUri]) {
1031 callbackList[requestUri].push(callback)
1032 return
1033 }
1034
1035 fetchingList[requestUri] = true
1036 callbackList[requestUri] = [callback]
1037
1038 // Fetches it
1039 Module._fetch(
1040 requestUri,
1041
1042 function() {
1043 fetchedList[requestUri] = true
1044
1045 // Updates module status
1046 var module = cachedModules[uri]
1047 if (module.status === STATUS.FETCHING) {
1048 module.status = STATUS.FETCHED
1049 }
1050
1051 // Saves anonymous module meta data
1052 if (anonymousModuleMeta) {
1053 save(uri, anonymousModuleMeta)
1054 anonymousModuleMeta = null
1055 }
1056
1057 // Assigns the first module in package to cachedModules[uri]
1058 // See: test/issues/un-correspondence
1059 if (firstModuleInPackage && module.status === STATUS.FETCHED) {
1060 cachedModules[uri] = firstModuleInPackage
1061 firstModuleInPackage.realUri = uri
1062 }
1063 firstModuleInPackage = null
1064
1065 // Clears
1066 if (fetchingList[requestUri]) {
1067 delete fetchingList[requestUri]
1068 }
1069
1070 // Calls callbackList
1071 if (callbackList[requestUri]) {
1072 util.forEach(callbackList[requestUri], function(fn) {
1073 fn()
1074 })
1075 delete callbackList[requestUri]
1076 }
1077
1078 },
1079
1080 config.charset
1081 )
1082 }
1083
1084 function save(uri, meta) {
1085 var module = cachedModules[uri] || (cachedModules[uri] = new Module(uri))
1086
1087 // Don't override already saved module
1088 if (module.status < STATUS.SAVED) {
1089 // Lets anonymous module id equal to its uri
1090 module.id = meta.id || uri
1091
1092 module.dependencies = resolve(
1093 util.filter(meta.dependencies || [], function(dep) {
1094 return !!dep
1095 }), uri)
1096
1097 module.factory = meta.factory
1098
1099 // Updates module status
1100 module.status = STATUS.SAVED
1101 }
1102
1103 return module
1104 }
1105
1106 function runInModuleContext(fn, module) {
1107 var ret = fn(module.require, module.exports, module)
1108 if (ret !== undefined) {
1109 module.exports = ret
1110 }
1111 }
1112
1113 function hasModifiers(module) {
1114 return !!cachedModifiers[module.realUri || module.uri]
1115 }
1116
1117 function execModifiers(module) {
1118 var uri = module.realUri || module.uri
1119 var modifiers = cachedModifiers[uri]
1120
1121 if (modifiers) {
1122 util.forEach(modifiers, function(modifier) {
1123 runInModuleContext(modifier, module)
1124 })
1125
1126 delete cachedModifiers[uri]
1127 }
1128 }
1129
1130 function getPureDependencies(module) {
1131 var uri = module.uri
1132
1133 return util.filter(module.dependencies, function(dep) {
1134 circularCheckStack = [uri]
1135
1136 var isCircular = isCircularWaiting(cachedModules[dep], uri)
1137 if (isCircular) {
1138 circularCheckStack.push(uri)
1139 printCircularLog(circularCheckStack)
1140 }
1141
1142 return !isCircular
1143 })
1144 }
1145
1146 function isCircularWaiting(module, uri) {
1147 if (!module || module.status !== STATUS.SAVED) {
1148 return false
1149 }
1150
1151 circularCheckStack.push(module.uri)
1152 var deps = module.dependencies
1153
1154 if (deps.length) {
1155 if (util.indexOf(deps, uri) > -1) {
1156 return true
1157 }
1158
1159 for (var i = 0; i < deps.length; i++) {
1160 if (isCircularWaiting(cachedModules[deps[i]], uri)) {
1161 return true
1162 }
1163 }
1164
1165 return false
1166 }
1167
1168 return false
1169 }
1170
1171 function printCircularLog(stack, type) {
1172 util.log('Found circular dependencies:', stack.join(' --> '), type)
1173 }
1174
1175
1176 // Public API
1177 // ----------
1178
1179 var globalModule = new Module(util.pageUri, STATUS.COMPILED)
1180
1181 seajs.use = function(ids, callback) {
1182 var preloadMods = config.preload
1183
1184 if (preloadMods.length) {
1185 // Loads preload modules before all other modules.
1186 globalModule._use(preloadMods, function() {
1187 config.preload = []
1188 globalModule._use(ids, callback)
1189 })
1190 }
1191 else {
1192 globalModule._use(ids, callback)
1193 }
1194
1195 return seajs
1196 }
1197
1198
1199 // For normal users
1200 seajs.define = Module._define
1201 seajs.cache = Module.cache
1202 seajs.find = Module._find
1203 seajs.modify = Module._modify
1204
1205
1206 // For plugin developers
1207 seajs.pluginSDK = {
1208 Module: Module,
1209 util: util,
1210 config: config
1211 }
1212
1213})(seajs, seajs._util, seajs._config)
1214
1215/**
1216 * The configuration
1217 */
1218;(function(seajs, util, config) {
1219
1220 var noCachePrefix = 'seajs-ts='
1221 var noCacheTimeStamp = noCachePrefix + util.now()
1222
1223
1224 // Async inserted script
1225 var loaderScript = document.getElementById('seajsnode')
1226
1227 // Static script
1228 if (!loaderScript) {
1229 var scripts = document.getElementsByTagName('script')
1230 loaderScript = scripts[scripts.length - 1]
1231 }
1232
1233 var loaderSrc = util.getScriptAbsoluteSrc(loaderScript) ||
1234 util.pageUri // When sea.js is inline, set base to pageUri.
1235
1236 var base = util.dirname(getLoaderActualSrc(loaderSrc))
1237 util.loaderDir = base
1238
1239 // When src is "http://test.com/libs/seajs/1.0.0/sea.js", redirect base
1240 // to "http://test.com/libs/"
1241 var match = base.match(/^(.+\/)seajs\/[\d\.]+\/$/)
1242 if (match) {
1243 base = match[1]
1244 }
1245
1246 config.base = base
1247
1248
1249 var dataMain = loaderScript.getAttribute('data-main')
1250 if (dataMain) {
1251 config.main = dataMain
1252 }
1253
1254
1255 // The default charset of module file.
1256 config.charset = 'utf-8'
1257
1258
1259 /**
1260 * The function to configure the framework
1261 * config({
1262 * 'base': 'path/to/base',
1263 * 'alias': {
1264 * 'app': 'biz/xx',
1265 * 'jquery': 'jquery-1.5.2',
1266 * 'cart': 'cart?t=20110419'
1267 * },
1268 * 'map': [
1269 * ['test.cdn.cn', 'localhost']
1270 * ],
1271 * preload: [],
1272 * charset: 'utf-8',
1273 * debug: false
1274 * })
1275 *
1276 */
1277 seajs.config = function(o) {
1278 for (var k in o) {
1279 if (!o.hasOwnProperty(k)) continue
1280
1281 var previous = config[k]
1282 var current = o[k]
1283
1284 if (previous && k === 'alias') {
1285 for (var p in current) {
1286 if (current.hasOwnProperty(p)) {
1287
1288 var prevValue = previous[p]
1289 var currValue = current[p]
1290
1291 // Converts {jquery: '1.7.2'} to {jquery: 'jquery/1.7.2/jquery'}
1292 if (/^\d+\.\d+\.\d+$/.test(currValue)) {
1293 currValue = p + '/' + currValue + '/' + p
1294 }
1295
1296 checkAliasConflict(prevValue, currValue, p)
1297 previous[p] = currValue
1298
1299 }
1300 }
1301 }
1302 else if (previous && (k === 'map' || k === 'preload')) {
1303 // for config({ preload: 'some-module' })
1304 if (util.isString(current)) {
1305 current = [current]
1306 }
1307
1308 util.forEach(current, function(item) {
1309 if (item) {
1310 previous.push(item)
1311 }
1312 })
1313 }
1314 else {
1315 config[k] = current
1316 }
1317 }
1318
1319 // Makes sure config.base is an absolute path.
1320 var base = config.base
1321 if (base && !util.isAbsolute(base)) {
1322 config.base = util.id2Uri((util.isRoot(base) ? '' : './') + base + '/')
1323 }
1324
1325 // Uses map to implement nocache.
1326 if (config.debug === 2) {
1327 config.debug = 1
1328 seajs.config({
1329 map: [
1330 [/^.*$/, function(url) {
1331 if (url.indexOf(noCachePrefix) === -1) {
1332 url += (url.indexOf('?') === -1 ? '?' : '&') + noCacheTimeStamp
1333 }
1334 return url
1335 }]
1336 ]
1337 })
1338 }
1339
1340 debugSync()
1341
1342 return this
1343 }
1344
1345
1346 function debugSync() {
1347 if (config.debug) {
1348 // For convenient reference
1349 seajs.debug = !!config.debug
1350 }
1351 }
1352
1353 debugSync()
1354
1355
1356 function getLoaderActualSrc(src) {
1357 if (src.indexOf('??') === -1) {
1358 return src
1359 }
1360
1361 // Such as: http://cdn.com/??seajs/1.2.0/sea.js,jquery/1.7.2/jquery.js
1362 // Only support nginx combo style rule. If you use other combo rule, please
1363 // explicitly config the base path and the alias for plugins.
1364 var parts = src.split('??')
1365 var root = parts[0]
1366 var paths = util.filter(parts[1].split(','), function(str) {
1367 return str.indexOf('sea.js') !== -1
1368 })
1369
1370 return root + paths[0]
1371 }
1372
1373 function checkAliasConflict(previous, current, key) {
1374 if (previous && previous !== current) {
1375 util.log('The alias config is conflicted:',
1376 'key =', '"' + key + '"',
1377 'previous =', '"' + previous + '"',
1378 'current =', '"' + current + '"',
1379 'warn')
1380 }
1381 }
1382
1383})(seajs, seajs._util, seajs._config)
1384
1385/**
1386 * Prepare for bootstrapping
1387 */
1388;(function(seajs, util, global) {
1389
1390 // The safe and convenient version of console.log
1391 seajs.log = util.log
1392
1393
1394 // Creates a stylesheet from a text blob of rules.
1395 seajs.importStyle = util.importStyle
1396
1397
1398 // Sets a alias to `sea.js` directory for loading plugins.
1399 seajs.config({
1400 alias: { seajs: util.loaderDir }
1401 })
1402
1403
1404 // Uses `seajs-xxx` flag to load plugin-xxx.
1405 util.forEach(getStartupPlugins(), function(name) {
1406 seajs.use('seajs/plugin-' + name)
1407
1408 // Delays `seajs.use` calls to the onload of `mapfile` in debug mode.
1409 if (name === 'debug') {
1410 seajs._use = seajs.use
1411 seajs._useArgs = []
1412 seajs.use = function() { seajs._useArgs.push(arguments); return seajs }
1413 }
1414 })
1415
1416
1417 // Helpers
1418 // -------
1419
1420 function getStartupPlugins() {
1421 var ret = []
1422 var str = global.location.search
1423
1424 // Converts `seajs-xxx` to `seajs-xxx=1`
1425 str = str.replace(/(seajs-\w+)(&|$)/g, '$1=1$2')
1426
1427 // Add cookie string
1428 str += ' ' + document.cookie
1429
1430 // Excludes seajs-xxx=0
1431 str.replace(/seajs-(\w+)=[1-9]/g, function(m, name) {
1432 ret.push(name)
1433 })
1434
1435 return util.unique(ret)
1436 }
1437
1438})(seajs, seajs._util, this)
1439/**
1440 * The bootstrap and entrances
1441 */
1442;(function(seajs, config, global) {
1443
1444 var _seajs = seajs._seajs
1445
1446 // Avoids conflicting when sea.js is loaded multi times.
1447 if (_seajs && !_seajs['args']) {
1448 global.seajs = seajs._seajs
1449 return
1450 }
1451
1452
1453 // Assigns to global define.
1454 global.define = seajs.define
1455
1456
1457 // Loads the data-main module automatically.
1458 config.main && seajs.use(config.main)
1459
1460 // Parses the pre-call of seajs.config/seajs.use/define.
1461 // Ref: test/bootstrap/async-3.html
1462 ;(function(args) {
1463 if (args) {
1464 var hash = {
1465 0: 'config',
1466 1: 'use',
1467 2: 'define'
1468 }
1469 for (var i = 0; i < args.length; i += 2) {
1470 seajs[hash[args[i]]].apply(seajs, args[i + 1])
1471 }
1472 }
1473 })((_seajs || 0)['args'])
1474
1475
1476 // Add define.amd property for clear indicator.
1477 global.define.cmd = {}
1478
1479
1480 // Keeps clean!
1481 delete seajs.define
1482 delete seajs._util
1483 delete seajs._config
1484 delete seajs._seajs
1485
1486})(seajs, seajs._config, this)
1487