3 This file is part of Ext JS 4
5 Copyright (c) 2011 Sencha Inc
7 Contact: http://www.sencha.com/contact
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
18 * @author Jacky Nguyen <jacky@sencha.com>
19 * @docauthor Jacky Nguyen <jacky@sencha.com>
21 * Ext.Loader is the heart of the new dynamic dependency loading capability in Ext JS 4+. It is most commonly used
22 * via the {@link Ext#require} shorthand. Ext.Loader supports both asynchronous and synchronous loading
23 * approaches, and leverage their advantages for the best development flow. We'll discuss about the pros and cons
26 * # Asynchronous Loading
30 * + No web server needed: you can run the application via the file system protocol
31 * (i.e: `file://path/to/your/index.html`)
32 * + Best possible debugging experience: error messages come with the exact file name and line number
35 * + Dependencies need to be specified before-hand
37 * ### Method 1: Explicitly include what you need:
40 * Ext.require({String/Array} expressions);
42 * // Example: Single alias
43 * Ext.require('widget.window');
45 * // Example: Single class name
46 * Ext.require('Ext.window.Window');
48 * // Example: Multiple aliases / class names mix
49 * Ext.require(['widget.window', 'layout.border', 'Ext.data.Connection']);
52 * Ext.require(['widget.*', 'layout.*', 'Ext.data.*']);
54 * ### Method 2: Explicitly exclude what you don't need:
56 * // Syntax: Note that it must be in this chaining format.
57 * Ext.exclude({String/Array} expressions)
58 * .require({String/Array} expressions);
60 * // Include everything except Ext.data.*
61 * Ext.exclude('Ext.data.*').require('*');
63 * // Include all widgets except widget.checkbox*,
64 * // which will match widget.checkbox, widget.checkboxfield, widget.checkboxgroup, etc.
65 * Ext.exclude('widget.checkbox*').require('widget.*');
67 * # Synchronous Loading on Demand
70 * + There's no need to specify dependencies before-hand, which is always the convenience of including
74 * + Not as good debugging experience since file name won't be shown (except in Firebug at the moment)
75 * + Must be from the same domain due to XHR restriction
76 * + Need a web server, same reason as above
78 * There's one simple rule to follow: Instantiate everything with Ext.create instead of the `new` keyword
80 * Ext.create('widget.window', { ... }); // Instead of new Ext.window.Window({...});
82 * Ext.create('Ext.window.Window', {}); // Same as above, using full class name instead of alias
84 * Ext.widget('window', {}); // Same as above, all you need is the traditional `xtype`
86 * Behind the scene, {@link Ext.ClassManager} will automatically check whether the given class name / alias has already
87 * existed on the page. If it's not, Ext.Loader will immediately switch itself to synchronous mode and automatic load
88 * the given class and all its dependencies.
90 * # Hybrid Loading - The Best of Both Worlds
92 * It has all the advantages combined from asynchronous and synchronous loading. The development flow is simple:
94 * ### Step 1: Start writing your application using synchronous approach.
96 * Ext.Loader will automatically fetch all dependencies on demand as they're needed during run-time. For example:
98 * Ext.onReady(function(){
99 * var window = Ext.createWidget('window', {
106 * title: 'Hello Dialog',
108 * title: 'Navigation',
123 * ### Step 2: Along the way, when you need better debugging ability, watch the console for warnings like these:
125 * [Ext.Loader] Synchronously loading 'Ext.window.Window'; consider adding Ext.require('Ext.window.Window') before your application's code ClassManager.js:432
126 * [Ext.Loader] Synchronously loading 'Ext.layout.container.Border'; consider adding Ext.require('Ext.layout.container.Border') before your application's code
128 * Simply copy and paste the suggested code above `Ext.onReady`, e.g.:
130 * Ext.require('Ext.window.Window');
131 * Ext.require('Ext.layout.container.Border');
135 * Everything should now load via asynchronous mode.
139 * It's important to note that dynamic loading should only be used during development on your local machines.
140 * During production, all dependencies should be combined into one single JavaScript file. Ext.Loader makes
141 * the whole process of transitioning from / to between development / maintenance and production as easy as
142 * possible. Internally {@link Ext.Loader#history Ext.Loader.history} maintains the list of all dependencies
143 * your application needs in the exact loading sequence. It's as simple as concatenating all files in this
144 * array into one, then include it on top of your application.
146 * This process will be automated with Sencha Command, to be released and documented towards Ext JS 4 Final.
148 (function(Manager, Class, flexSetter, alias) {
152 isNonBrowser = typeof window === 'undefined',
153 isNodeJS = isNonBrowser && (typeof require === 'function'),
154 isPhantomJS = (typeof phantom !== 'undefined' && phantom.fs),
156 dependencyProperties = ['extend', 'mixins', 'requires'],
159 Loader = Ext.Loader = {
163 documentHead: typeof document !== 'undefined' && (document.head || document.getElementsByTagName('head')[0]),
166 * Flag indicating whether there are still files being loaded
172 * Maintain the queue for all dependencies. Each item in the array is an object of the format:
174 * requires: [...], // The required classes for this queue item
175 * callback: function() { ... } // The function to execute when all classes specified in requires exist
182 * Maintain the list of files that have already been handled so that they never get double-loaded
188 * Maintain the list of listeners to execute when all required scripts are fully loaded
194 * Contains optional dependencies to be loaded last
197 optionalRequires: [],
200 * Map of fully qualified class names to an array of dependent classes.
216 hasFileLoadError: false,
221 classNameToFilePathMap: {},
224 * @property {String[]} history
225 * An array of class names to keep track of the dependency loading order.
226 * This is not guaranteed to be the same everytime due to the asynchronous nature of the Loader.
236 * @cfg {Boolean} enabled
237 * Whether or not to enable the dynamic dependency loading feature.
242 * @cfg {Boolean} disableCaching
243 * Appends current timestamp to script files to prevent caching.
245 disableCaching: true,
248 * @cfg {String} disableCachingParam
249 * The get parameter name for the cache buster's timestamp.
251 disableCachingParam: '_dc',
254 * @cfg {Object} paths
255 * The mapping from namespaces to file paths
258 * 'Ext': '.', // This is set by default, Ext.layout.container.Container will be
259 * // loaded from ./layout/Container.js
261 * 'My': './src/my_own_folder' // My.layout.Container will be loaded from
262 * // ./src/my_own_folder/layout/Container.js
265 * Note that all relative paths are relative to the current HTML document.
266 * If not being specified, for example, `Other.awesome.Class`
267 * will simply be loaded from `./Other/awesome/Class.js`
275 * Set the configuration for the loader. This should be called right after ext-core.js
276 * (or ext-core-debug.js) is included in the page, e.g.:
278 * <script type="text/javascript" src="ext-core-debug.js"></script>
279 * <script type="text/javascript">
280 * Ext.Loader.setConfig({
283 * 'My': 'my_own_path'
287 * <script type="text/javascript">
290 * Ext.onReady(function() {
291 * // application code here
295 * Refer to config options of {@link Ext.Loader} for the list of possible properties.
297 * @param {String/Object} name Name of the value to override, or a config object to override multiple values.
298 * @param {Object} value (optional) The new value to set, needed if first parameter is String.
299 * @return {Ext.Loader} this
301 setConfig: function(name, value) {
302 if (Ext.isObject(name) && arguments.length === 1) {
303 Ext.Object.merge(this.config, name);
306 this.config[name] = (Ext.isObject(value)) ? Ext.Object.merge(this.config[name], value) : value;
313 * Get the config value corresponding to the specified name.
314 * If no name is given, will return the config object.
315 * @param {String} name The config property name
318 getConfig: function(name) {
320 return this.config[name];
327 * Sets the path of a namespace. For Example:
329 * Ext.Loader.setPath('Ext', '.');
331 * @param {String/Object} name See {@link Ext.Function#flexSetter flexSetter}
332 * @param {String} path See {@link Ext.Function#flexSetter flexSetter}
333 * @return {Ext.Loader} this
336 setPath: flexSetter(function(name, path) {
340 path = require('fs').realpathSync(path);
344 this.config.paths[name] = path;
350 * Translates a className to a file path by adding the the proper prefix and converting the .'s to /'s.
353 * Ext.Loader.setPath('My', '/path/to/My');
355 * alert(Ext.Loader.getPath('My.awesome.Class')); // alerts '/path/to/My/awesome/Class.js'
357 * Note that the deeper namespace levels, if explicitly set, are always resolved first. For example:
359 * Ext.Loader.setPath({
360 * 'My': '/path/to/lib',
361 * 'My.awesome': '/other/path/for/awesome/stuff',
362 * 'My.awesome.more': '/more/awesome/path'
365 * alert(Ext.Loader.getPath('My.awesome.Class')); // alerts '/other/path/for/awesome/stuff/Class.js'
367 * alert(Ext.Loader.getPath('My.awesome.more.Class')); // alerts '/more/awesome/path/Class.js'
369 * alert(Ext.Loader.getPath('My.cool.Class')); // alerts '/path/to/lib/cool/Class.js'
371 * alert(Ext.Loader.getPath('Unknown.strange.Stuff')); // alerts 'Unknown/strange/Stuff.js'
373 * @param {String} className
374 * @return {String} path
376 getPath: function(className) {
378 paths = this.config.paths,
379 prefix = this.getPrefix(className);
381 if (prefix.length > 0) {
382 if (prefix === className) {
383 return paths[prefix];
386 path = paths[prefix];
387 className = className.substring(prefix.length + 1);
390 if (path.length > 0) {
394 return path.replace(/\/\.\//g, '/') + className.replace(/\./g, "/") + '.js';
399 * @param {String} className
401 getPrefix: function(className) {
402 var paths = this.config.paths,
403 prefix, deepestPrefix = '';
405 if (paths.hasOwnProperty(className)) {
409 for (prefix in paths) {
410 if (paths.hasOwnProperty(prefix) && prefix + '.' === className.substring(0, prefix.length + 1)) {
411 if (prefix.length > deepestPrefix.length) {
412 deepestPrefix = prefix;
417 return deepestPrefix;
421 * Refresh all items in the queue. If all dependencies for an item exist during looping,
422 * it will execute the callback and call refreshQueue again. Triggers onReady when the queue is
426 refreshQueue: function() {
427 var ln = this.queue.length,
428 i, item, j, requires;
435 for (i = 0; i < ln; i++) {
436 item = this.queue[i];
439 requires = item.requires;
441 // Don't bother checking when the number of files loaded
442 // is still less than the array length
443 if (requires.length > this.numLoadedFiles) {
450 if (Manager.isCreated(requires[j])) {
451 // Take out from the queue
452 Ext.Array.erase(requires, j, 1);
457 } while (j < requires.length);
459 if (item.requires.length === 0) {
460 Ext.Array.erase(this.queue, i, 1);
461 item.callback.call(item.scope);
472 * Inject a script element to document's head, call onLoad and onError accordingly
475 injectScriptElement: function(url, onLoad, onError, scope) {
476 var script = document.createElement('script'),
478 onLoadFn = function() {
479 me.cleanupScriptElement(script);
482 onErrorFn = function() {
483 me.cleanupScriptElement(script);
487 script.type = 'text/javascript';
489 script.onload = onLoadFn;
490 script.onerror = onErrorFn;
491 script.onreadystatechange = function() {
492 if (this.readyState === 'loaded' || this.readyState === 'complete') {
497 this.documentHead.appendChild(script);
505 cleanupScriptElement: function(script) {
506 script.onload = null;
507 script.onreadystatechange = null;
508 script.onerror = null;
514 * Load a script file, supports both asynchronous and synchronous approaches
516 * @param {String} url
517 * @param {Function} onLoad
518 * @param {Object} scope
519 * @param {Boolean} synchronous
522 loadScriptFile: function(url, onLoad, onError, scope, synchronous) {
524 noCacheUrl = url + (this.getConfig('disableCaching') ? ('?' + this.getConfig('disableCachingParam') + '=' + Ext.Date.now()) : ''),
525 fileName = url.split('/').pop(),
526 isCrossOriginRestricted = false,
527 xhr, status, onScriptError;
529 scope = scope || this;
531 this.isLoading = true;
534 onScriptError = function() {
535 onError.call(scope, "Failed loading '" + url + "', please verify that the file exists", synchronous);
538 if (!Ext.isReady && Ext.onDocumentReady) {
539 Ext.onDocumentReady(function() {
540 me.injectScriptElement(noCacheUrl, onLoad, onScriptError, scope);
544 this.injectScriptElement(noCacheUrl, onLoad, onScriptError, scope);
548 if (typeof XMLHttpRequest !== 'undefined') {
549 xhr = new XMLHttpRequest();
551 xhr = new ActiveXObject('Microsoft.XMLHTTP');
555 xhr.open('GET', noCacheUrl, false);
558 isCrossOriginRestricted = true;
561 status = (xhr.status === 1223) ? 204 : xhr.status;
563 if (!isCrossOriginRestricted) {
564 isCrossOriginRestricted = (status === 0);
567 if (isCrossOriginRestricted
572 onError.call(this, "Failed loading synchronously via XHR: '" + url + "'; It's likely that the file is either " +
573 "being loaded from a different domain or from the local file system whereby cross origin " +
574 "requests are not allowed due to security reasons. Use asynchronous loading with " +
575 "Ext.require instead.", synchronous);
577 else if (status >= 200 && status < 300
582 // Firebug friendly, file names are still shown even though they're eval'ed code
583 new Function(xhr.responseText + "\n//@ sourceURL=" + fileName)();
588 onError.call(this, "Failed loading synchronously via XHR: '" + url + "'; please " +
589 "verify that the file exists. " +
590 "XHR status code: " + status, synchronous);
593 // Prevent potential IE memory leak
599 * Explicitly exclude files from being loaded. Useful when used in conjunction with a broad include expression.
600 * Can be chained with more `require` and `exclude` methods, e.g.:
602 * Ext.exclude('Ext.data.*').require('*');
604 * Ext.exclude('widget.button*').require('widget.*');
606 * {@link Ext#exclude Ext.exclude} is alias for {@link Ext.Loader#exclude Ext.Loader.exclude} for convenience.
608 * @param {String/String[]} excludes
609 * @return {Object} object contains `require` method for chaining
611 exclude: function(excludes) {
615 require: function(expressions, fn, scope) {
616 return me.require(expressions, fn, scope, excludes);
619 syncRequire: function(expressions, fn, scope) {
620 return me.syncRequire(expressions, fn, scope, excludes);
626 * Synchronously loads all classes by the given names and all their direct dependencies;
627 * optionally executes the given callback function when finishes, within the optional scope.
629 * {@link Ext#syncRequire Ext.syncRequire} is alias for {@link Ext.Loader#syncRequire Ext.Loader.syncRequire} for convenience.
631 * @param {String/String[]} expressions Can either be a string or an array of string
632 * @param {Function} fn (Optional) The callback function
633 * @param {Object} scope (Optional) The execution scope (`this`) of the callback function
634 * @param {String/String[]} excludes (Optional) Classes to be excluded, useful when being used with expressions
636 syncRequire: function() {
637 this.syncModeEnabled = true;
638 this.require.apply(this, arguments);
640 this.syncModeEnabled = false;
644 * Loads all classes by the given names and all their direct dependencies;
645 * optionally executes the given callback function when finishes, within the optional scope.
647 * {@link Ext#require Ext.require} is alias for {@link Ext.Loader#require Ext.Loader.require} for convenience.
649 * @param {String/String[]} expressions Can either be a string or an array of string
650 * @param {Function} fn (Optional) The callback function
651 * @param {Object} scope (Optional) The execution scope (`this`) of the callback function
652 * @param {String/String[]} excludes (Optional) Classes to be excluded, useful when being used with expressions
654 require: function(expressions, fn, scope, excludes) {
655 var filePath, expression, exclude, className, excluded = {},
656 excludedClassNames = [],
657 possibleClassNames = [],
658 possibleClassName, classNames = [],
661 expressions = Ext.Array.from(expressions);
662 excludes = Ext.Array.from(excludes);
664 fn = fn || Ext.emptyFn;
666 scope = scope || Ext.global;
668 for (i = 0, ln = excludes.length; i < ln; i++) {
669 exclude = excludes[i];
671 if (typeof exclude === 'string' && exclude.length > 0) {
672 excludedClassNames = Manager.getNamesByExpression(exclude);
674 for (j = 0, subLn = excludedClassNames.length; j < subLn; j++) {
675 excluded[excludedClassNames[j]] = true;
680 for (i = 0, ln = expressions.length; i < ln; i++) {
681 expression = expressions[i];
683 if (typeof expression === 'string' && expression.length > 0) {
684 possibleClassNames = Manager.getNamesByExpression(expression);
686 for (j = 0, subLn = possibleClassNames.length; j < subLn; j++) {
687 possibleClassName = possibleClassNames[j];
689 if (!excluded.hasOwnProperty(possibleClassName) && !Manager.isCreated(possibleClassName)) {
690 Ext.Array.include(classNames, possibleClassName);
696 // If the dynamic dependency feature is not being used, throw an error
697 // if the dependencies are not defined
698 if (!this.config.enabled) {
699 if (classNames.length > 0) {
701 sourceClass: "Ext.Loader",
702 sourceMethod: "require",
703 msg: "Ext.Loader is not enabled, so dependencies cannot be resolved dynamically. " +
704 "Missing required class" + ((classNames.length > 1) ? "es" : "") + ": " + classNames.join(', ')
709 if (classNames.length === 0) {
715 requires: classNames,
720 classNames = classNames.slice();
722 for (i = 0, ln = classNames.length; i < ln; i++) {
723 className = classNames[i];
725 if (!this.isFileLoaded.hasOwnProperty(className)) {
726 this.isFileLoaded[className] = false;
728 filePath = this.getPath(className);
730 this.classNameToFilePathMap[className] = filePath;
732 this.numPendingFiles++;
739 // Temporary support for hammerjs
741 var f = fs.open(filePath),
747 if (line.length === 0) {
757 this.onFileLoaded(className, filePath);
760 return Manager.get(className);
768 Ext.Function.pass(this.onFileLoaded, [className, filePath], this),
769 Ext.Function.pass(this.onFileLoadError, [className, filePath]),
781 * @param {String} className
782 * @param {String} filePath
784 onFileLoaded: function(className, filePath) {
785 this.numLoadedFiles++;
787 this.isFileLoaded[className] = true;
789 this.numPendingFiles--;
791 if (this.numPendingFiles === 0) {
796 if (this.numPendingFiles <= 1) {
797 window.status = "Finished loading all dependencies, onReady fired!";
800 window.status = "Loading dependencies, " + this.numPendingFiles + " files left...";
805 if (!this.syncModeEnabled && this.numPendingFiles === 0 && this.isLoading && !this.hasFileLoadError) {
806 var queue = this.queue,
808 i, ln, j, subLn, missingClasses = [], missingPaths = [];
810 for (i = 0, ln = queue.length; i < ln; i++) {
811 requires = queue[i].requires;
813 for (j = 0, subLn = requires.length; j < ln; j++) {
814 if (this.isFileLoaded[requires[j]]) {
815 missingClasses.push(requires[j]);
820 if (missingClasses.length < 1) {
824 missingClasses = Ext.Array.filter(missingClasses, function(item) {
825 return !this.requiresMap.hasOwnProperty(item);
828 for (i = 0,ln = missingClasses.length; i < ln; i++) {
829 missingPaths.push(this.classNameToFilePathMap[missingClasses[i]]);
833 sourceClass: "Ext.Loader",
834 sourceMethod: "onFileLoaded",
835 msg: "The following classes are not declared even if their files have been " +
836 "loaded: '" + missingClasses.join("', '") + "'. Please check the source code of their " +
837 "corresponding files for possible typos: '" + missingPaths.join("', '") + "'"
846 onFileLoadError: function(className, filePath, errorMessage, isSynchronous) {
847 this.numPendingFiles--;
848 this.hasFileLoadError = true;
852 sourceClass: "Ext.Loader",
853 classToLoad: className,
855 loadingType: isSynchronous ? 'synchronous' : 'async',
864 addOptionalRequires: function(requires) {
865 var optionalRequires = this.optionalRequires,
868 requires = Ext.Array.from(requires);
870 for (i = 0, ln = requires.length; i < ln; i++) {
871 require = requires[i];
873 Ext.Array.include(optionalRequires, require);
882 triggerReady: function(force) {
883 var readyListeners = this.readyListeners,
884 optionalRequires, listener;
886 if (this.isLoading || force) {
887 this.isLoading = false;
889 if (this.optionalRequires.length) {
890 // Clone then empty the array to eliminate potential recursive loop issue
891 optionalRequires = Ext.Array.clone(this.optionalRequires);
893 // Empty the original array
894 this.optionalRequires.length = 0;
896 this.require(optionalRequires, Ext.Function.pass(this.triggerReady, [true], this), this);
900 while (readyListeners.length) {
901 listener = readyListeners.shift();
902 listener.fn.call(listener.scope);
904 if (this.isLoading) {
914 * Adds new listener to be executed when all required scripts are fully loaded.
916 * @param {Function} fn The function callback to be executed
917 * @param {Object} scope The execution scope (`this`) of the callback function
918 * @param {Boolean} withDomReady Whether or not to wait for document dom ready as well
920 onReady: function(fn, scope, withDomReady, options) {
923 if (withDomReady !== false && Ext.onDocumentReady) {
927 Ext.onDocumentReady(oldFn, scope, options);
931 if (!this.isLoading) {
935 this.readyListeners.push({
944 * @param {String} className
946 historyPush: function(className) {
947 if (className && this.isFileLoaded.hasOwnProperty(className)) {
948 Ext.Array.include(this.history, className);
958 * @alias Ext.Loader#require
960 Ext.require = alias(Loader, 'require');
964 * @method syncRequire
965 * @alias Ext.Loader#syncRequire
967 Ext.syncRequire = alias(Loader, 'syncRequire');
972 * @alias Ext.Loader#exclude
974 Ext.exclude = alias(Loader, 'exclude');
979 * @alias Ext.Loader#onReady
981 Ext.onReady = function(fn, scope, options) {
982 Loader.onReady(fn, scope, true, options);
986 * @cfg {String[]} requires
988 * List of classes that have to be loaded before instantiating this class.
991 * Ext.define('Mother', {
992 * requires: ['Child'],
993 * giveBirth: function() {
994 * // we can be sure that child class is available.
995 * return new Child();
999 Class.registerPreprocessor('loader', function(cls, data, continueFn) {
1002 className = Manager.getName(cls),
1003 i, j, ln, subLn, value, propertyName, propertyValue;
1006 Basically loop through the dependencyProperties, look for string class names and push
1007 them into a stack, regardless of whether the property's value is a string, array or object. For example:
1009 extend: 'Ext.MyClass',
1010 requires: ['Ext.some.OtherClass'],
1012 observable: 'Ext.util.Observable';
1015 which will later be transformed into:
1017 extend: Ext.MyClass,
1018 requires: [Ext.some.OtherClass],
1020 observable: Ext.util.Observable;
1025 for (i = 0, ln = dependencyProperties.length; i < ln; i++) {
1026 propertyName = dependencyProperties[i];
1028 if (data.hasOwnProperty(propertyName)) {
1029 propertyValue = data[propertyName];
1031 if (typeof propertyValue === 'string') {
1032 dependencies.push(propertyValue);
1034 else if (propertyValue instanceof Array) {
1035 for (j = 0, subLn = propertyValue.length; j < subLn; j++) {
1036 value = propertyValue[j];
1038 if (typeof value === 'string') {
1039 dependencies.push(value);
1043 else if (typeof propertyValue != 'function') {
1044 for (j in propertyValue) {
1045 if (propertyValue.hasOwnProperty(j)) {
1046 value = propertyValue[j];
1048 if (typeof value === 'string') {
1049 dependencies.push(value);
1057 if (dependencies.length === 0) {
1058 // Loader.historyPush(className);
1063 var deadlockPath = [],
1064 requiresMap = Loader.requiresMap,
1068 Automatically detect deadlocks before-hand,
1069 will throw an error with detailed path for ease of debugging. Examples of deadlock cases:
1071 - A extends B, then B extends A
1072 - A requires B, B requires C, then C requires A
1074 The detectDeadlock function will recursively transverse till the leaf, hence it can detect deadlocks
1075 no matter how deep the path is.
1079 requiresMap[className] = dependencies;
1081 detectDeadlock = function(cls) {
1082 deadlockPath.push(cls);
1084 if (requiresMap[cls]) {
1085 if (Ext.Array.contains(requiresMap[cls], className)) {
1087 sourceClass: "Ext.Loader",
1088 msg: "Deadlock detected while loading dependencies! '" + className + "' and '" +
1089 deadlockPath[1] + "' " + "mutually require each other. Path: " +
1090 deadlockPath.join(' -> ') + " -> " + deadlockPath[0]
1094 for (i = 0, ln = requiresMap[cls].length; i < ln; i++) {
1095 detectDeadlock(requiresMap[cls][i]);
1100 detectDeadlock(className);
1105 Loader.require(dependencies, function() {
1106 for (i = 0, ln = dependencyProperties.length; i < ln; i++) {
1107 propertyName = dependencyProperties[i];
1109 if (data.hasOwnProperty(propertyName)) {
1110 propertyValue = data[propertyName];
1112 if (typeof propertyValue === 'string') {
1113 data[propertyName] = Manager.get(propertyValue);
1115 else if (propertyValue instanceof Array) {
1116 for (j = 0, subLn = propertyValue.length; j < subLn; j++) {
1117 value = propertyValue[j];
1119 if (typeof value === 'string') {
1120 data[propertyName][j] = Manager.get(value);
1124 else if (typeof propertyValue != 'function') {
1125 for (var k in propertyValue) {
1126 if (propertyValue.hasOwnProperty(k)) {
1127 value = propertyValue[k];
1129 if (typeof value === 'string') {
1130 data[propertyName][k] = Manager.get(value);
1138 continueFn.call(me, cls, data);
1144 Class.setDefaultPreprocessorPosition('loader', 'after', 'className');
1147 * @cfg {String[]} uses
1149 * List of classes to load together with this class. These aren't neccessarily loaded before
1150 * this class is instantiated. For example:
1152 * Ext.define('Mother', {
1154 * giveBirth: function() {
1155 * // This code might, or might not work:
1156 * // return new Child();
1158 * // Instead use Ext.create() to load the class at the spot if not loaded already:
1159 * return Ext.create('Child');
1163 Manager.registerPostprocessor('uses', function(name, cls, data) {
1164 var uses = Ext.Array.from(data.uses),
1168 for (i = 0, ln = uses.length; i < ln; i++) {
1171 if (typeof item === 'string') {
1176 Loader.addOptionalRequires(items);
1179 Manager.setDefaultPostprocessorPosition('uses', 'last');
1181 })(Ext.ClassManager, Ext.Class, Ext.Function.flexSetter, Ext.Function.alias);