]>
git.proxmox.com Git - extjs.git/blob - extjs/packages/core/src/class/Loader.js
3 * This class provides dynamic loading support for JavaScript classes. Application code
4 * does not typically need to call `Ext.Loader` except perhaps to configure path mappings
5 * when not using [Sencha Cmd](http://www.sencha.com/products/sencha-cmd/).
7 * Ext.Loader.setPath('MyApp', 'app');
9 * When using Sencha Cmd, this is handled by the "bootstrap" provided by the application
10 * build script and such configuration is not necessary.
14 * The `Ext.Loader` is most often used behind the scenes to satisfy class references in
15 * class declarations. Like so:
17 * Ext.define('MyApp.view.Main', {
18 * extend: 'Ext.panel.Panel',
33 * In all of these cases, `Ext.Loader` is used internally to resolve these class names
34 * and ensure that the necessary class files are loaded.
36 * During development, these files are loaded individually for optimal debugging. For a
37 * production use, [Sencha Cmd](http://www.sencha.com/products/sencha-cmd/) will replace
38 * all of these strings with the actual resolved class references because it ensures that
39 * the classes are all contained in the build in the correct order. In development, these
40 * files will not be loaded until the `MyApp.view.Main` class indicates they are needed
45 * You can also use `Ext.Loader` directly to load classes or files. The simplest form of
46 * use is `{@link Ext#require}`.
50 * Ext.require('MyApp.view.Main', function () {
51 * // On callback, the MyApp.view.Main class is now loaded
53 * var view = new MyApp.view.Main();
56 * You can alternatively require classes by alias or wildcard.
58 * Ext.require('widget.window');
60 * Ext.require(['widget.window', 'layout.border', 'Ext.data.Connection']);
62 * Ext.require(['widget.*', 'layout.*', 'Ext.data.*']);
64 * The callback function is optional.
66 * **Note** Using `Ext.require` at global scope will cause `{@link Ext#onReady}` and
67 * `{@link Ext.app.Application#launch}` methods to be deferred until the required classes
68 * are loaded. It is these cases where the callback function is most often unnecessary.
72 * Alternatively, you can exclude what you don't need:
74 * // Include everything except Ext.tree.*
75 * Ext.exclude('Ext.tree.*').require('*');
77 * // Include all widgets except widget.checkbox* (this will exclude
78 * // widget.checkbox, widget.checkboxfield, widget.checkboxgroup, etc.)
79 * Ext.exclude('widget.checkbox*').require('widget.*');
81 * # Dynamic Instantiation
83 * Another feature enabled by `Ext.Loader` is instantiation using class names or aliases.
87 * var win = Ext.create({
91 * // xclass: 'Ext.window.Window'
96 * This form of creation can be useful if the type to create (`window` in the above) is
97 * not known statically. Internally, `{@link Ext#create}` may need to *synchronously*
98 * load the desired class and its requirements. Doing this will generate a warning in
101 * [Ext.Loader] Synchronously loading 'Ext.window.Window'...
103 * If you see these in your debug console, you should add the indicated class(es) to the
104 * appropriate `requires` array (as above) or make an `{@link Ext#require}` call.
107 * **Note** Using `{@link Ext#create}` has some performance overhead and is best reserved
108 * for cases where the target class is not known until run-time.
113 Ext
.Loader
= (new function() { // jshint ignore:line
114 // @define Ext.Loader
116 // @require Ext.Class
117 // @require Ext.ClassManager
118 // @require Ext.Function
119 // @require Ext.Array
120 // @require Ext.env.Ready
123 Manager
= Ext
.ClassManager
, // this is an instance of Ext.Inventory
126 Ready
= Ext
.env
.Ready
,
127 alias
= Ext
.Function
.alias
,
128 dependencyProperties
= ['extend', 'mixins', 'requires'],
137 * @cfg {Boolean} [enabled=true]
138 * Whether or not to enable the dynamic dependency loading feature.
143 * @cfg {Boolean} [scriptChainDelay=false]
144 * millisecond delay between asynchronous script injection (prevents stack
145 * overflow on some user agents) 'false' disables delay but potentially
146 * increases stack load.
148 scriptChainDelay
: false,
151 * @cfg {Boolean} [disableCaching=true]
152 * Appends current timestamp to script files to prevent caching.
154 disableCaching
: true,
157 * @cfg {String} [disableCachingParam="_dc"]
158 * The get parameter name for the cache buster's timestamp.
160 disableCachingParam
: '_dc',
163 * @cfg {Object} paths
164 * The mapping from namespaces to file paths
167 * 'Ext': '.', // This is set by default, Ext.layout.container.Container will be
168 * // loaded from ./layout/Container.js
170 * 'My': './src/my_own_folder' // My.layout.Container will be loaded from
171 * // ./src/my_own_folder/layout/Container.js
174 * Note that all relative paths are relative to the current HTML document.
175 * If not being specified, for example, `Other.awesome.Class` will simply be
176 * loaded from `"./Other/awesome/Class.js"`.
178 paths
: Manager
.paths
,
181 * @cfg {Boolean} preserveScripts
182 * `false` to remove asynchronously loaded scripts, `true` to retain script
183 * element for browser debugger compatibility and improved load performance.
185 preserveScripts
: true,
188 * @cfg {String} scriptCharset
189 * Optional charset to specify encoding of dynamic script content.
191 scriptCharset
: undefined
193 // These configs are delegated to Ext.Script and may need different names:
195 disableCaching
: true,
196 disableCachingParam
: true,
197 preserveScripts
: true,
198 scriptChainDelay
: 'loadDelay'
205 isInHistory
: isInHistory
,
208 * Flag indicating whether there are still files being loaded
214 * An array of class names to keep track of the dependency loading order.
215 * This is not guaranteed to be the same everytime due to the asynchronous
216 * nature of the Loader.
218 * @property {Array} history
229 * Maintain the list of listeners to execute when all required scripts are fully loaded
232 readyListeners
: readyListeners
,
235 * Contains classes referenced in `uses` properties.
238 optionalRequires
: usedClasses
,
241 * Map of fully qualified class names to an array of dependent classes.
244 requiresMap
: _requiresMap
,
247 hasFileLoadError
: false,
250 * The number of scripts loading via loadScript.
265 syncModeEnabled
: false,
271 missingQueue
: _missingQueue
,
274 // initalize the default path of the framework
275 var scripts
= document
.getElementsByTagName('script'),
276 src
= scripts
[scripts
.length
- 1].src
,
277 path
= src
.substring(0, src
.lastIndexOf('/') + 1),
278 meta
= Ext
._classPathMetadata
,
279 microloader
= Ext
.Microloader
,
280 manifest
= Ext
.manifest
,
281 loadOrder
, baseUrl
, loadlen
, l
, loadItem
;
284 if (src
.indexOf("packages/core/src/") !== -1) {
285 path
= path
+ "../../";
286 } else if (src
.indexOf("/core/src/class/") !== -1) {
287 path
= path
+ "../../../";
292 if(!Manager
.getPath("Ext")) {
293 Manager
.setPath('Ext', path
+ 'src');
296 // Pull in Cmd generated metadata if available.
298 Ext
._classPathMetadata
= null;
299 Loader
.addClassPathMappings(meta
);
303 loadOrder
= manifest
.loadOrder
;
304 // if the manifest paths were calculated as relative to the
305 // bootstrap file, then we need to prepend Boot.baseUrl to the
306 // paths before processing
307 baseUrl
= Ext
.Boot
.baseUrl
;
308 if(loadOrder
&& manifest
.bootRelative
) {
309 for(loadlen
= loadOrder
.length
, l
= 0; l
< loadlen
; l
++) {
310 loadItem
= loadOrder
[l
];
311 loadItem
.path
= baseUrl
+ loadItem
.path
;
318 microloader
.onMicroloaderReady(function(){
325 * Set the configuration for the loader. This should be called right after ext-(debug).js
326 * is included in the page, and before Ext.onReady. i.e:
328 * <script type="text/javascript" src="ext-core-debug.js"></script>
329 * <script type="text/javascript">
330 * Ext.Loader.setConfig({
333 * 'My': 'my_own_path'
337 * <script type="text/javascript">
340 * Ext.onReady(function() {
341 * // application code here
345 * Refer to config options of {@link Ext.Loader} for the list of possible properties
347 * @param {Object} config The config object to override the default values
348 * @return {Ext.Loader} this
350 setConfig
: Ext
.Function
.flexSetter(function (name
, value
) {
351 if (name
=== 'paths') {
352 Loader
.setPath(value
);
354 _config
[name
] = value
;
356 var delegated
= delegatedConfigs
[name
];
358 Boot
.setConfig((delegated
=== true) ? name
: delegated
, value
);
366 * Get the config value corresponding to the specified name. If no name is given, will return the config object
367 * @param {String} name The config property name
370 getConfig: function(name
) {
371 return name
? _config
[name
] : _config
;
375 * Sets the path of a namespace.
378 * Ext.Loader.setPath('Ext', '.');
380 * @param {String/Object} name See {@link Ext.Function#flexSetter flexSetter}
381 * @param {String} [path] See {@link Ext.Function#flexSetter flexSetter}
382 * @return {Ext.Loader} this
385 setPath: function () {
386 // Paths are an Ext.Inventory thing and ClassManager is an instance of that:
387 Manager
.setPath
.apply(Manager
, arguments
);
392 * Sets a batch of path entries
394 * @param {Object } paths a set of className: path mappings
395 * @return {Ext.Loader} this
397 addClassPathMappings: function(paths
) {
398 // Paths are an Ext.Inventory thing and ClassManager is an instance of that:
399 Manager
.setPath(paths
);
404 * fixes up loader path configs by prepending Ext.Boot#baseUrl to the beginning
405 * of the path, then delegates to Ext.Loader#addClassPathMappings
409 addBaseUrlClassPathMappings: function(pathConfig
) {
410 for(var name
in pathConfig
) {
411 pathConfig
[name
] = Boot
.baseUrl
+ pathConfig
[name
];
413 Ext
.Loader
.addClassPathMappings(pathConfig
);
418 * Translates a className to a file path by adding the
419 * the proper prefix and converting the .'s to /'s. For example:
421 * Ext.Loader.setPath('My', '/path/to/My');
423 * alert(Ext.Loader.getPath('My.awesome.Class')); // alerts '/path/to/My/awesome/Class.js'
425 * Note that the deeper namespace levels, if explicitly set, are always resolved first. For example:
427 * Ext.Loader.setPath({
428 * 'My': '/path/to/lib',
429 * 'My.awesome': '/other/path/for/awesome/stuff',
430 * 'My.awesome.more': '/more/awesome/path'
433 * alert(Ext.Loader.getPath('My.awesome.Class')); // alerts '/other/path/for/awesome/stuff/Class.js'
435 * alert(Ext.Loader.getPath('My.awesome.more.Class')); // alerts '/more/awesome/path/Class.js'
437 * alert(Ext.Loader.getPath('My.cool.Class')); // alerts '/path/to/lib/cool/Class.js'
439 * alert(Ext.Loader.getPath('Unknown.strange.Stuff')); // alerts 'Unknown/strange/Stuff.js'
441 * @param {String} className
442 * @return {String} path
444 getPath: function(className
) {
445 // Paths are an Ext.Inventory thing and ClassManager is an instance of that:
446 return Manager
.getPath(className
);
449 require: function (expressions
, fn
, scope
, excludes
) {
451 return Loader
.exclude(excludes
).require(expressions
, fn
, scope
);
454 var classNames
= Manager
.getNamesByExpression(expressions
);
456 return Loader
.load(classNames
, fn
, scope
);
459 syncRequire: function () {
460 var wasEnabled
= Loader
.syncModeEnabled
;
462 Loader
.syncModeEnabled
= true;
464 var ret
= Loader
.require
.apply(Loader
, arguments
);
466 Loader
.syncModeEnabled
= wasEnabled
;
471 exclude: function (excludes
) {
472 var selector
= Manager
.select({
473 require: function (classNames
, fn
, scope
) {
474 return Loader
.load(classNames
, fn
, scope
);
477 syncRequire: function (classNames
, fn
, scope
) {
478 var wasEnabled
= Loader
.syncModeEnabled
;
480 Loader
.syncModeEnabled
= true;
482 var ret
= Loader
.load(classNames
, fn
, scope
);
484 Loader
.syncModeEnabled
= wasEnabled
;
490 selector
.exclude(excludes
);
494 load: function (classNames
, callback
, scope
) {
496 if (callback
.length
) {
497 // If callback expects arguments, shim it with a function that will map
498 // the requires class(es) from the names we are given.
499 callback
= Loader
.makeLoadCallback(classNames
, callback
);
501 callback
= callback
.bind(scope
|| Ext
.global
);
504 var missingClassNames
= [],
505 numClasses
= classNames
.length
,
506 className
, i
, numMissing
, urls
= [],
507 state
= Manager
.classState
;
509 for (i
= 0; i
< numClasses
; ++i
) {
510 className
= Manager
.resolveName(classNames
[i
]);
511 if (!Manager
.isCreated(className
)) {
512 missingClassNames
.push(className
);
513 _missingQueue
[className
] = Loader
.getPath(className
);
514 if(!state
[className
]) {
515 urls
.push(_missingQueue
[className
]);
520 // If the dynamic dependency feature is not being used, throw an error
521 // if the dependencies are not defined
522 numMissing
= missingClassNames
.length
;
524 Loader
.missingCount
+= numMissing
;
526 Ext
.Array
.push(Loader
.classesLoading
, missingClassNames
);
529 Manager
.onCreated(function () {
531 Ext
.Array
.remove(Loader
.classesLoading
, missingClassNames
);
532 Ext
.each(missingClassNames
, function(name
){
533 Ext
.Array
.remove(Loader
.classesLoading
, name
);
537 Ext
.callback(callback
, scope
, arguments
);
540 }, Loader
, missingClassNames
);
542 if (!_config
.enabled
) {
543 Ext
.raise("Ext.Loader is not enabled, so dependencies cannot be resolved dynamically. " +
544 "Missing required class" + ((missingClassNames
.length
> 1) ? "es" : "") +
545 ": " + missingClassNames
.join(', '));
551 // scope: this options object so we can pass these along:
552 _classNames
: missingClassNames
555 // need to call checkReady here, as the _missingCoun
556 // may have transitioned from 0 to > 0, meaning we
557 // need to block ready
562 callback
.call(scope
);
564 // need to call checkReady here, as the _missingCoun
565 // may have transitioned from 0 to > 0, meaning we
566 // need to block ready
570 if (Loader
.syncModeEnabled
) {
571 // Class may have been just loaded or was already loaded
572 if (numClasses
=== 1) {
573 return Manager
.get(classNames
[0]);
580 makeLoadCallback: function (classNames
, callback
) {
583 i
= classNames
.length
;
586 classes
[i
] = Manager
.get(classNames
[i
]);
589 return callback
.apply(this, classes
);
593 onLoadFailure: function () {
595 onError
= options
.onError
;
597 Loader
.hasFileLoadError
= true;
598 --Loader
.scriptsLoading
;
601 //TODO: need an adapter to convert to v4 onError signatures
602 onError
.call(options
.userScope
, options
);
606 Ext
.log
.error("[Ext.Loader] Some requested files failed to load.");
613 onLoadSuccess: function () {
615 onLoad
= options
.onLoad
;
617 --Loader
.scriptsLoading
;
619 //TODO: need an adapter to convert to v4 onLoad signatures
620 onLoad
.call(options
.userScope
, options
);
621 // onLoad can cause more loads to start, so it must run first
627 // TODO: this timing of this needs to be deferred until all classes have had a chance to be created
629 reportMissingClasses: function () {
630 if (!Loader
.syncModeEnabled
&& !Loader
.scriptsLoading
&& Loader
.isLoading
&&
631 !Loader
.hasFileLoadError
) {
632 var missingClasses
= [],
635 for (var missingClassName
in _missingQueue
) {
636 missingClasses
.push(missingClassName
);
637 missingPaths
.push(_missingQueue
[missingClassName
]);
640 if (missingClasses
.length
) {
641 throw new Error("The following classes are not declared even if their files have been " +
642 "loaded: '" + missingClasses
.join("', '") + "'. Please check the source code of their " +
643 "corresponding files for possible typos: '" + missingPaths
.join("', '"));
650 * Add a new listener to be executed when all required scripts are fully loaded
652 * @param {Function} fn The function callback to be executed
653 * @param {Object} scope The execution scope (`this`) of the callback function.
654 * @param {Boolean} [withDomReady=true] Pass `false` to not also wait for document
656 * @param {Object} [options] Additional callback options.
657 * @param {Number} [options.delay=0] A number of milliseconds to delay.
658 * @param {Number} [options.priority=0] Relative priority of this callback. Negative
659 * numbers are reserved.
661 onReady: function(fn
, scope
, withDomReady
, options
) {
663 Ready
.on(fn
, scope
, options
);
665 var listener
= Ready
.makeListener(fn
, scope
, options
);
667 if (Loader
.isLoading
) {
668 readyListeners
.push(listener
);
670 Ready
.invoke(listener
);
677 * Ensure that any classes referenced in the `uses` property are loaded.
679 addUsedClasses: function (classes
) {
683 classes
= (typeof classes
=== 'string') ? [classes
] : classes
;
684 for (i
= 0, ln
= classes
.length
; i
< ln
; i
++) {
686 if (typeof cls
=== 'string' && !Ext
.Array
.contains(usedClasses
, cls
)) {
687 usedClasses
.push(cls
);
698 triggerReady: function() {
700 refClasses
= usedClasses
;
702 if (Loader
.isLoading
&& refClasses
.length
) {
703 // Empty the array to eliminate potential recursive loop issue
706 // this may immediately call us back if all 'uses' classes
708 Loader
.require(refClasses
);
710 // Must clear this before calling callbacks. This will cause any new loads
711 // to call Ready.block() again. See below for more on this.
712 Loader
.isLoading
= false;
714 // These listeners are just those attached directly to Loader to wait for
715 // class loading only.
716 readyListeners
.sort(Ready
.sortFn
);
718 // this method can be called with Loader.isLoading either true or false
719 // (can be called with false when all 'uses' classes are already loaded)
720 // this may bypass the above if condition
721 while (readyListeners
.length
&& !Loader
.isLoading
) {
722 // we may re-enter triggerReady so we cannot necessarily iterate the
723 // readyListeners array
724 listener
= readyListeners
.pop();
725 Ready
.invoke(listener
);
728 // If the DOM is also ready, this will fire the normal onReady listeners.
729 // An astute observer would note that we may now be back to isLoading and
730 // so ask "Why you call unblock?". The reason is that we must match the
731 // calls to block and since we transitioned from isLoading to !isLoading
732 // here we must call unblock. If we have transitioned back to isLoading in
733 // the above loop it will have called block again so the counter will be
734 // increased and this call will not reduce the block count to 0. This is
735 // done by loadScripts.
742 * @param {String} className
744 historyPush: function(className
) {
745 if (className
&& !isInHistory
[className
] && !Manager
.overrideMap
[className
]) {
746 isInHistory
[className
] = true;
747 history
.push(className
);
753 * This is an internal method that delegate content loading to the
758 loadScripts: function(params
) {
759 var manifest
= Ext
.manifest
,
760 loadOrder
= manifest
&& manifest
.loadOrder
,
761 loadOrderMap
= manifest
&& manifest
.loadOrderMap
,
764 ++Loader
.scriptsLoading
;
766 // if the load order map hasn't been created, create it now
767 // and cache on the manifest
768 if (loadOrder
&& !loadOrderMap
) {
769 manifest
.loadOrderMap
= loadOrderMap
= Boot
.createLoadOrderMap(loadOrder
);
772 // verify the loading state, as this may have transitioned us from
773 // not loading to loading
776 options
= Ext
.apply({
777 loadOrder
: loadOrder
,
778 loadOrderMap
: loadOrderMap
,
779 charset
: _config
.scriptCharset
,
780 success
: Loader
.onLoadSuccess
,
781 failure
: Loader
.onLoadFailure
,
782 sync
: Loader
.syncModeEnabled
,
786 options
.userScope
= options
.scope
;
787 options
.scope
= options
;
793 * This method is provide for use by the bootstrap layer.
795 * @param {String[]} urls
797 loadScriptsSync: function(urls
) {
798 var syncwas
= Loader
.syncModeEnabled
;
799 Loader
.syncModeEnabled
= true;
800 Loader
.loadScripts({url
: urls
});
801 Loader
.syncModeEnabled
= syncwas
;
805 * This method is provide for use by the bootstrap layer.
807 * @param {String[]} urls
809 loadScriptsSyncBasePrefix: function(urls
) {
810 var syncwas
= Loader
.syncModeEnabled
;
811 Loader
.syncModeEnabled
= true;
812 Loader
.loadScripts({url
: urls
, prependBaseUrl
: true});
813 Loader
.syncModeEnabled
= syncwas
;
817 * Loads the specified script URL and calls the supplied callbacks. If this method
818 * is called before {@link Ext#isReady}, the script's load will delay the transition
819 * to ready. This can be used to load arbitrary scripts that may contain further
820 * {@link Ext#require Ext.require} calls.
822 * @param {Object/String/String[]} options The options object or simply the URL(s) to load.
823 * @param {String} options.url The URL from which to load the script.
824 * @param {Function} [options.onLoad] The callback to call on successful load.
825 * @param {Function} [options.onError] The callback to call on failure to load.
826 * @param {Object} [options.scope] The scope (`this`) for the supplied callbacks.
828 loadScript: function(options
) {
829 var isString
= typeof options
=== 'string',
830 isArray
= options
instanceof Array
,
831 isObject
= !isArray
&& !isString
,
832 url
= isObject
? options
.url
: options
,
833 onError
= isObject
&& options
.onError
,
834 onLoad
= isObject
&& options
.onLoad
,
835 scope
= isObject
&& options
.scope
,
844 Loader
.loadScripts(request
);
850 flushMissingQueue: function() {
851 var name
, val
, missingwas
= 0, missing
= 0;
853 for(name
in _missingQueue
) {
855 val
= _missingQueue
[name
];
856 if(Manager
.isCreated(name
)) {
857 delete _missingQueue
[name
];
858 } else if (Manager
.existCache
[name
] === 2) {
859 delete _missingQueue
[name
];
864 this.missingCount
= missing
;
870 checkReady: function() {
871 var wasLoading
= Loader
.isLoading
,
874 Loader
.flushMissingQueue();
875 isLoading
= Loader
.missingCount
+ Loader
.scriptsLoading
;
877 if (isLoading
&& !wasLoading
) {
879 Loader
.isLoading
= !!isLoading
;
880 } else if (!isLoading
&& wasLoading
) {
881 Loader
.triggerReady();
885 if (!Loader
.scriptsLoading
&& Loader
.missingCount
) {
886 // Things look bad, but since load requests may come later, defer this
887 // for a bit then check if things are still stuck.
888 Ext
.defer(function () {
889 if (!Loader
.scriptsLoading
&& Loader
.missingCount
) {
890 Ext
.log
.error('[Loader] The following classes failed to load:');
891 for (var name
in Loader
.missingQueue
) {
892 Ext
.log
.error('[Loader] ' + name
+ ' from ' +
893 Loader
.missingQueue
[name
]);
903 * Loads all classes by the given names and all their direct dependencies; optionally
904 * executes the given callback function when finishes, within the optional scope.
906 * @param {String/String[]} expressions The class, classes or wildcards to load.
907 * @param {Function} [fn] The callback function.
908 * @param {Object} [scope] The execution scope (`this`) of the callback function.
912 Ext
.require
= alias(Loader
, 'require');
915 * Synchronously loads all classes by the given names and all their direct dependencies; optionally
916 * executes the given callback function when finishes, within the optional scope.
918 * @param {String/String[]} expressions The class, classes or wildcards to load.
919 * @param {Function} [fn] The callback function.
920 * @param {Object} [scope] The execution scope (`this`) of the callback function.
922 * @method syncRequire
924 Ext
.syncRequire
= alias(Loader
, 'syncRequire');
927 * Explicitly exclude files from being loaded. Useful when used in conjunction with a
928 * broad include expression. Can be chained with more `require` and `exclude` methods,
931 * Ext.exclude('Ext.data.*').require('*');
933 * Ext.exclude('widget.button*').require('widget.*');
935 * @param {String/String[]} excludes
936 * @return {Object} Contains `exclude`, `require` and `syncRequire` methods for chaining.
940 Ext
.exclude
= alias(Loader
, 'exclude');
942 //<feature classSystem.loader>
944 * @cfg {String[]} requires
946 * List of classes that have to be loaded before instantiating this class.
949 * Ext.define('Mother', {
950 * requires: ['Child'],
951 * giveBirth: function() {
952 * // we can be sure that child class is available.
953 * return new Child();
957 Class
.registerPreprocessor('loader', function(cls
, data
, hooks
, continueFn
) {
959 Ext
.classSystemMonitor
&& Ext
.classSystemMonitor(cls
, 'Ext.Loader#loaderPreprocessor', arguments
); // jshint ignore:line
965 className
= Manager
.getName(cls
),
966 i
, j
, ln
, subLn
, value
, propertyName
, propertyValue
,
970 Loop through the dependencyProperties, look for string class names and push
971 them into a stack, regardless of whether the property's value is a string, array or object. For example:
973 extend: 'Ext.MyClass',
974 requires: ['Ext.some.OtherClass'],
976 thing: 'Foo.bar.Thing';
979 which will later be transformed into:
982 requires: [Ext.some.OtherClass],
984 thing: Foo.bar.Thing;
989 for (i
= 0,ln
= dependencyProperties
.length
; i
< ln
; i
++) {
990 propertyName
= dependencyProperties
[i
];
992 if (data
.hasOwnProperty(propertyName
)) {
993 propertyValue
= data
[propertyName
];
995 if (typeof propertyValue
=== 'string') {
996 dependencies
.push(propertyValue
);
998 else if (propertyValue
instanceof Array
) {
999 for (j
= 0, subLn
= propertyValue
.length
; j
< subLn
; j
++) {
1000 value
= propertyValue
[j
];
1002 if (typeof value
=== 'string') {
1003 dependencies
.push(value
);
1007 else if (typeof propertyValue
!== 'function') {
1008 for (j
in propertyValue
) {
1009 if (propertyValue
.hasOwnProperty(j
)) {
1010 value
= propertyValue
[j
];
1012 if (typeof value
=== 'string') {
1013 dependencies
.push(value
);
1021 if (dependencies
.length
=== 0) {
1025 _requiresMap
[className
] = dependencies
;
1029 var deadlockPath
= [],
1033 Automatically detect deadlocks before-hand,
1034 will throw an error with detailed path for ease of debugging. Examples of deadlock cases:
1036 - A extends B, then B extends A
1037 - A requires B, B requires C, then C requires A
1039 The detectDeadlock function will recursively transverse till the leaf, hence it can detect deadlocks
1040 no matter how deep the path is.
1044 requiredMap
= Loader
.requiredByMap
|| (Loader
.requiredByMap
= {});
1046 for (i
= 0,ln
= dependencies
.length
; i
< ln
; i
++) {
1047 dependency
= dependencies
[i
];
1048 (requiredMap
[dependency
] || (requiredMap
[dependency
] = [])).push(className
);
1051 detectDeadlock = function(cls
) {
1052 deadlockPath
.push(cls
);
1054 if (_requiresMap
[cls
]) {
1055 if (Ext
.Array
.contains(_requiresMap
[cls
], className
)) {
1056 Ext
.raise("Circular requirement detected! '" + className
+
1057 "' and '" + deadlockPath
[1] + "' mutually require each other. Path: " +
1058 deadlockPath
.join(' -> ') + " -> " + deadlockPath
[0]);
1061 for (i
= 0,ln
= _requiresMap
[cls
].length
; i
< ln
; i
++) {
1062 detectDeadlock(_requiresMap
[cls
][i
]);
1067 detectDeadlock(className
);
1072 (className
? Loader
.exclude(className
) : Loader
).require(dependencies
, function() {
1073 for (i
= 0,ln
= dependencyProperties
.length
; i
< ln
; i
++) {
1074 propertyName
= dependencyProperties
[i
];
1076 if (data
.hasOwnProperty(propertyName
)) {
1077 propertyValue
= data
[propertyName
];
1079 if (typeof propertyValue
=== 'string') {
1080 data
[propertyName
] = Manager
.get(propertyValue
);
1082 else if (propertyValue
instanceof Array
) {
1083 for (j
= 0, subLn
= propertyValue
.length
; j
< subLn
; j
++) {
1084 value
= propertyValue
[j
];
1086 if (typeof value
=== 'string') {
1087 data
[propertyName
][j
] = Manager
.get(value
);
1091 else if (typeof propertyValue
!== 'function') {
1092 for (var k
in propertyValue
) {
1093 if (propertyValue
.hasOwnProperty(k
)) {
1094 value
= propertyValue
[k
];
1096 if (typeof value
=== 'string') {
1097 data
[propertyName
][k
] = Manager
.get(value
);
1105 continueFn
.call(me
, cls
, data
, hooks
);
1109 }, true, 'after', 'className');
1112 * @cfg {String[]} uses
1114 * List of optional classes to load together with this class. These aren't neccessarily loaded before
1115 * this class is created, but are guaranteed to be available before Ext.onReady listeners are
1116 * invoked. For example:
1118 * Ext.define('Mother', {
1120 * giveBirth: function() {
1121 * // This code might, or might not work:
1122 * // return new Child();
1124 * // Instead use Ext.create() to load the class at the spot if not loaded already:
1125 * return Ext.create('Child');
1129 Manager
.registerPostprocessor('uses', function(name
, cls
, data
) {
1131 Ext
.classSystemMonitor
&& Ext
.classSystemMonitor(cls
, 'Ext.Loader#usesPostprocessor', arguments
); // jshint ignore:line
1134 var manifest
= Ext
.manifest
,
1135 loadOrder
= manifest
&& manifest
.loadOrder
,
1136 classes
= manifest
&& manifest
.classes
,
1137 uses
, clazz
, item
, len
, i
, indexMap
;
1140 clazz
= classes
[name
];
1141 if (clazz
&& !isNaN(i
= clazz
.idx
)) {
1142 item
= loadOrder
[i
];
1145 for (len
= uses
.length
, i
= 0; i
< len
; i
++) {
1146 indexMap
[uses
[i
]] = true;
1148 uses
= Ext
.Boot
.getPathsFromIndexes(indexMap
, loadOrder
, true);
1149 if (uses
.length
> 0) {
1150 Loader
.loadScripts({
1160 Loader
.addUsedClasses(uses
);
1164 Manager
.onCreated(Loader
.historyPush
);
1171 //-----------------------------------------------------------------------------
1173 // Use performance.now when available to keep timestamps consistent.
1174 Ext
._endTime
= Ext
.ticks();
1176 // This hook is to allow tools like DynaTrace to deterministically detect the availability
1177 // of Ext.onReady. Since Loader takes over Ext.onReady this must be done here and not in
1179 if (Ext
._beforereadyhandler
){
1180 Ext
._beforereadyhandler();