4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5 <title>The source code</title>
6 <link href="../prettify/prettify.css" type="text/css" rel="stylesheet" />
7 <script type="text/javascript" src="../prettify/prettify.js"></script>
8 <style type="text/css">
9 .highlight { display: block; background-color: #ddd; }
11 <script type="text/javascript">
12 function highlight() {
13 document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
17 <body onload="prettyPrint(); highlight();">
18 <pre class="prettyprint lang-js"><span id='Ext-Loader'>/**
19 </span> * @author Jacky Nguyen <jacky@sencha.com>
20 * @docauthor Jacky Nguyen <jacky@sencha.com>
24 Ext.Loader is the heart of the new dynamic dependency loading capability in Ext JS 4+. It is most commonly used
25 via the {@link Ext#require} shorthand. Ext.Loader supports both asynchronous and synchronous loading
26 approaches, and leverage their advantages for the best development flow. We'll discuss about the pros and cons of each approach:
28 # Asynchronous Loading #
32 + No web server needed: you can run the application via the file system protocol (i.e: `file://path/to/your/index
34 + Best possible debugging experience: error messages come with the exact file name and line number
37 + Dependencies need to be specified before-hand
39 ### Method 1: Explicitly include what you need: ###
42 Ext.require({String/Array} expressions);
44 // Example: Single alias
45 Ext.require('widget.window');
47 // Example: Single class name
48 Ext.require('Ext.window.Window');
50 // Example: Multiple aliases / class names mix
51 Ext.require(['widget.window', 'layout.border', 'Ext.data.Connection']);
54 Ext.require(['widget.*', 'layout.*', 'Ext.data.*']);
56 ### Method 2: Explicitly exclude what you don't need: ###
58 // Syntax: Note that it must be in this chaining format.
59 Ext.exclude({String/Array} expressions)
60 .require({String/Array} expressions);
62 // Include everything except Ext.data.*
63 Ext.exclude('Ext.data.*').require('*');
65 // Include all widgets except widget.checkbox*,
66 // which will match widget.checkbox, widget.checkboxfield, widget.checkboxgroup, etc.
67 Ext.exclude('widget.checkbox*').require('widget.*');
69 # Synchronous Loading on Demand #
72 + There's no need to specify dependencies before-hand, which is always the convenience of including ext-all.js
76 + Not as good debugging experience since file name won't be shown (except in Firebug at the moment)
77 + Must be from the same domain due to XHR restriction
78 + Need a web server, same reason as above
80 There's one simple rule to follow: Instantiate everything with Ext.create instead of the `new` keyword
82 Ext.create('widget.window', { ... }); // Instead of new Ext.window.Window({...});
84 Ext.create('Ext.window.Window', {}); // Same as above, using full class name instead of alias
86 Ext.widget('window', {}); // Same as above, all you need is the traditional `xtype`
88 Behind the scene, {@link Ext.ClassManager} will automatically check whether the given class name / alias has already
89 existed on the page. If it's not, Ext.Loader will immediately switch itself to synchronous mode and automatic load the given
90 class and all its dependencies.
92 # Hybrid Loading - The Best of Both Worlds #
94 It has all the advantages combined from asynchronous and synchronous loading. The development flow is simple:
96 ### Step 1: Start writing your application using synchronous approach. Ext.Loader will automatically fetch all
97 dependencies on demand as they're needed during run-time. For example: ###
99 Ext.onReady(function(){
100 var window = Ext.createWidget('window', {
107 title: 'Hello Dialog',
124 ### Step 2: Along the way, when you need better debugging ability, watch the console for warnings like these: ###
126 [Ext.Loader] Synchronously loading 'Ext.window.Window'; consider adding Ext.require('Ext.window.Window') before your application's code
128 [Ext.Loader] Synchronously loading 'Ext.layout.container.Border'; consider adding Ext.require('Ext.layout.container.Border') before your application's code
130 Simply copy and paste the suggested code above `Ext.onReady`, i.e:
132 Ext.require('Ext.window.Window');
133 Ext.require('Ext.layout.container.Border');
137 Everything should now load via asynchronous mode.
141 It's important to note that dynamic loading should only be used during development on your local machines.
142 During production, all dependencies should be combined into one single JavaScript file. Ext.Loader makes
143 the whole process of transitioning from / to between development / maintenance and production as easy as
144 possible. Internally {@link Ext.Loader#history Ext.Loader.history} maintains the list of all dependencies your application
145 needs in the exact loading sequence. It's as simple as concatenating all files in this array into one,
146 then include it on top of your application.
148 This process will be automated with Sencha Command, to be released and documented towards Ext JS 4 Final.
154 (function(Manager, Class, flexSetter, alias) {
157 //<if nonBrowser>
158 isNonBrowser = typeof window === 'undefined',
159 isNodeJS = isNonBrowser && (typeof require === 'function'),
160 isPhantomJS = (typeof phantom !== 'undefined' && phantom.fs),
162 dependencyProperties = ['extend', 'mixins', 'requires'],
165 Loader = Ext.Loader = {
166 <span id='Ext-Loader-property-documentHead'> /**
169 documentHead: typeof document !== 'undefined' && (document.head || document.getElementsByTagName('head')[0]),
171 <span id='Ext-Loader-property-isLoading'> /**
172 </span> * Flag indicating whether there are still files being loaded
177 <span id='Ext-Loader-property-queue'> /**
178 </span> * Maintain the queue for all dependencies. Each item in the array is an object of the format:
180 * requires: [...], // The required classes for this queue item
181 * callback: function() { ... } // The function to execute when all classes specified in requires exist
187 <span id='Ext-Loader-property-isFileLoaded'> /**
188 </span> * Maintain the list of files that have already been handled so that they never get double-loaded
193 <span id='Ext-Loader-property-readyListeners'> /**
194 </span> * Maintain the list of listeners to execute when all required scripts are fully loaded
199 <span id='Ext-Loader-property-optionalRequires'> /**
200 </span> * Contains optional dependencies to be loaded last
203 optionalRequires: [],
205 <span id='Ext-Loader-property-requiresMap'> /**
206 </span> * Map of fully qualified class names to an array of dependent classes.
211 <span id='Ext-Loader-property-numPendingFiles'> /**
216 <span id='Ext-Loader-property-numLoadedFiles'> /**
221 <span id='Ext-Loader-property-hasFileLoadError'> /** @private */
222 </span> hasFileLoadError: false,
224 <span id='Ext-Loader-property-classNameToFilePathMap'> /**
227 classNameToFilePathMap: {},
229 <span id='Ext-Loader-property-history'> /**
230 </span> * An array of class names to keep track of the dependency loading order.
231 * This is not guaranteed to be the same everytime due to the asynchronous
232 * nature of the Loader.
239 <span id='Ext-Loader-property-config'> /**
240 </span> * Configuration
244 <span id='Ext-Loader-cfg-enabled'> /**
245 </span> * Whether or not to enable the dynamic dependency loading feature
247 * @cfg {Boolean} enabled
251 <span id='Ext-Loader-cfg-disableCaching'> /**
252 </span> * @cfg {Boolean} disableCaching
253 * Appends current timestamp to script files to prevent caching
256 disableCaching: true,
258 <span id='Ext-Loader-cfg-disableCachingParam'> /**
259 </span> * @cfg {String} disableCachingParam
260 * The get parameter name for the cache buster's timestamp.
263 disableCachingParam: '_dc',
265 <span id='Ext-Loader-cfg-paths'> /**
266 </span> * @cfg {Object} paths
267 * The mapping from namespaces to file paths
269 'Ext': '.', // This is set by default, Ext.layout.container.Container will be
270 // loaded from ./layout/Container.js
272 'My': './src/my_own_folder' // My.layout.Container will be loaded from
273 // ./src/my_own_folder/layout/Container.js
275 * Note that all relative paths are relative to the current HTML document.
276 * If not being specified, for example, <code>Other.awesome.Class</code>
277 * will simply be loaded from <code>./Other/awesome/Class.js</code>
284 <span id='Ext-Loader-method-setConfig'> /**
285 </span> * Set the configuration for the loader. This should be called right after ext-core.js
286 * (or ext-core-debug.js) is included in the page, i.e:
288 <script type="text/javascript" src="ext-core-debug.js"></script>
289 <script type="text/javascript">
290 Ext.Loader.setConfig({
297 <script type="text/javascript">
300 Ext.onReady(function() {
301 // application code here
305 * Refer to {@link Ext.Loader#configs} for the list of possible properties
307 * @param {Object} config The config object to override the default values in {@link Ext.Loader#config}
308 * @return {Ext.Loader} this
311 setConfig: function(name, value) {
312 if (Ext.isObject(name) && arguments.length === 1) {
313 Ext.Object.merge(this.config, name);
316 this.config[name] = (Ext.isObject(value)) ? Ext.Object.merge(this.config[name], value) : value;
322 <span id='Ext-Loader-method-getConfig'> /**
323 </span> * Get the config value corresponding to the specified name. If no name is given, will return the config object
324 * @param {String} name The config property name
325 * @return {Object/Mixed}
327 getConfig: function(name) {
329 return this.config[name];
335 <span id='Ext-Loader-method-setPath'> /**
336 </span> * Sets the path of a namespace.
339 Ext.Loader.setPath('Ext', '.');
341 * @param {String/Object} name See {@link Ext.Function#flexSetter flexSetter}
342 * @param {String} path See {@link Ext.Function#flexSetter flexSetter}
343 * @return {Ext.Loader} this
347 setPath: flexSetter(function(name, path) {
348 //<if nonBrowser>
351 path = require('fs').realpathSync(path);
355 this.config.paths[name] = path;
360 <span id='Ext-Loader-method-getPath'> /**
361 </span> * Translates a className to a file path by adding the
362 * the proper prefix and converting the .'s to /'s. For example:
364 Ext.Loader.setPath('My', '/path/to/My');
366 alert(Ext.Loader.getPath('My.awesome.Class')); // alerts '/path/to/My/awesome/Class.js'
368 * Note that the deeper namespace levels, if explicitly set, are always resolved first. For example:
371 'My': '/path/to/lib',
372 'My.awesome': '/other/path/for/awesome/stuff',
373 'My.awesome.more': '/more/awesome/path'
376 alert(Ext.Loader.getPath('My.awesome.Class')); // alerts '/other/path/for/awesome/stuff/Class.js'
378 alert(Ext.Loader.getPath('My.awesome.more.Class')); // alerts '/more/awesome/path/Class.js'
380 alert(Ext.Loader.getPath('My.cool.Class')); // alerts '/path/to/lib/cool/Class.js'
382 alert(Ext.Loader.getPath('Unknown.strange.Stuff')); // alerts 'Unknown/strange/Stuff.js'
384 * @param {String} className
385 * @return {String} path
388 getPath: function(className) {
390 paths = this.config.paths,
391 prefix = this.getPrefix(className);
393 if (prefix.length > 0) {
394 if (prefix === className) {
395 return paths[prefix];
398 path = paths[prefix];
399 className = className.substring(prefix.length + 1);
402 if (path.length > 0) {
406 return path.replace(/\/\.\//g, '/') + className.replace(/\./g, "/") + '.js';
409 <span id='Ext-Loader-method-getPrefix'> /**
411 * @param {String} className
413 getPrefix: function(className) {
414 var paths = this.config.paths,
415 prefix, deepestPrefix = '';
417 if (paths.hasOwnProperty(className)) {
421 for (prefix in paths) {
422 if (paths.hasOwnProperty(prefix) && prefix + '.' === className.substring(0, prefix.length + 1)) {
423 if (prefix.length > deepestPrefix.length) {
424 deepestPrefix = prefix;
429 return deepestPrefix;
432 <span id='Ext-Loader-method-refreshQueue'> /**
433 </span> * Refresh all items in the queue. If all dependencies for an item exist during looping,
434 * it will execute the callback and call refreshQueue again. Triggers onReady when the queue is
438 refreshQueue: function() {
439 var ln = this.queue.length,
440 i, item, j, requires;
447 for (i = 0; i < ln; i++) {
448 item = this.queue[i];
451 requires = item.requires;
453 // Don't bother checking when the number of files loaded
454 // is still less than the array length
455 if (requires.length > this.numLoadedFiles) {
462 if (Manager.isCreated(requires[j])) {
463 // Take out from the queue
464 requires.splice(j, 1);
469 } while (j < requires.length);
471 if (item.requires.length === 0) {
472 this.queue.splice(i, 1);
473 item.callback.call(item.scope);
483 <span id='Ext-Loader-method-injectScriptElement'> /**
484 </span> * Inject a script element to document's head, call onLoad and onError accordingly
487 injectScriptElement: function(url, onLoad, onError, scope) {
488 var script = document.createElement('script'),
490 onLoadFn = function() {
491 me.cleanupScriptElement(script);
494 onErrorFn = function() {
495 me.cleanupScriptElement(script);
499 script.type = 'text/javascript';
501 script.onload = onLoadFn;
502 script.onerror = onErrorFn;
503 script.onreadystatechange = function() {
504 if (this.readyState === 'loaded' || this.readyState === 'complete') {
509 this.documentHead.appendChild(script);
514 <span id='Ext-Loader-method-cleanupScriptElement'> /**
517 cleanupScriptElement: function(script) {
518 script.onload = null;
519 script.onreadystatechange = null;
520 script.onerror = null;
525 <span id='Ext-Loader-method-loadScriptFile'> /**
526 </span> * Load a script file, supports both asynchronous and synchronous approaches
528 * @param {String} url
529 * @param {Function} onLoad
530 * @param {Scope} scope
531 * @param {Boolean} synchronous
534 loadScriptFile: function(url, onLoad, onError, scope, synchronous) {
536 noCacheUrl = url + (this.getConfig('disableCaching') ? ('?' + this.getConfig('disableCachingParam') + '=' + Ext.Date.now()) : ''),
537 fileName = url.split('/').pop(),
538 isCrossOriginRestricted = false,
539 xhr, status, onScriptError;
541 scope = scope || this;
543 this.isLoading = true;
546 onScriptError = function() {
547 onError.call(scope, "Failed loading '" + url + "', please verify that the file exists", synchronous);
550 if (!Ext.isReady && Ext.onDocumentReady) {
551 Ext.onDocumentReady(function() {
552 me.injectScriptElement(noCacheUrl, onLoad, onScriptError, scope);
556 this.injectScriptElement(noCacheUrl, onLoad, onScriptError, scope);
560 if (typeof XMLHttpRequest !== 'undefined') {
561 xhr = new XMLHttpRequest();
563 xhr = new ActiveXObject('Microsoft.XMLHTTP');
567 xhr.open('GET', noCacheUrl, false);
570 isCrossOriginRestricted = true;
573 status = (xhr.status === 1223) ? 204 : xhr.status;
575 if (!isCrossOriginRestricted) {
576 isCrossOriginRestricted = (status === 0);
579 if (isCrossOriginRestricted
580 //<if isNonBrowser>
581 && !isPhantomJS
584 onError.call(this, "Failed loading synchronously via XHR: '" + url + "'; It's likely that the file is either " +
585 "being loaded from a different domain or from the local file system whereby cross origin " +
586 "requests are not allowed due to security reasons. Use asynchronous loading with " +
587 "Ext.require instead.", synchronous);
589 else if (status >= 200 && status < 300
590 //<if isNonBrowser>
594 // Firebug friendly, file names are still shown even though they're eval'ed code
595 new Function(xhr.responseText + "\n//@ sourceURL=" + fileName)();
600 onError.call(this, "Failed loading synchronously via XHR: '" + url + "'; please " +
601 "verify that the file exists. " +
602 "XHR status code: " + status, synchronous);
605 // Prevent potential IE memory leak
610 <span id='Ext-Loader-method-exclude'> /**
611 </span> * Explicitly exclude files from being loaded. Useful when used in conjunction with a broad include expression.
612 * Can be chained with more `require` and `exclude` methods, eg:
614 Ext.exclude('Ext.data.*').require('*');
616 Ext.exclude('widget.button*').require('widget.*');
618 * @param {Array} excludes
619 * @return {Object} object contains `require` method for chaining
622 exclude: function(excludes) {
626 require: function(expressions, fn, scope) {
627 return me.require(expressions, fn, scope, excludes);
630 syncRequire: function(expressions, fn, scope) {
631 return me.syncRequire(expressions, fn, scope, excludes);
636 <span id='Ext-Loader-method-syncRequire'> /**
637 </span> * Synchronously loads all classes by the given names and all their direct dependencies; optionally executes the given callback function when finishes, within the optional scope. This method is aliased by {@link Ext#syncRequire} for convenience
638 * @param {String/Array} expressions Can either be a string or an array of string
639 * @param {Function} fn (Optional) The callback function
640 * @param {Object} scope (Optional) The execution scope (`this`) of the callback function
641 * @param {String/Array} excludes (Optional) Classes to be excluded, useful when being used with expressions
644 syncRequire: function() {
645 this.syncModeEnabled = true;
646 this.require.apply(this, arguments);
648 this.syncModeEnabled = false;
651 <span id='Ext-Loader-method-require'> /**
652 </span> * Loads all classes by the given names and all their direct dependencies; optionally executes the given callback function when
653 * finishes, within the optional scope. This method is aliased by {@link Ext#require Ext.require} for convenience
654 * @param {String/Array} expressions Can either be a string or an array of string
655 * @param {Function} fn (Optional) The callback function
656 * @param {Object} scope (Optional) The execution scope (`this`) of the callback function
657 * @param {String/Array} excludes (Optional) Classes to be excluded, useful when being used with expressions
660 require: function(expressions, fn, scope, excludes) {
661 var filePath, expression, exclude, className, excluded = {},
662 excludedClassNames = [],
663 possibleClassNames = [],
664 possibleClassName, classNames = [],
667 expressions = Ext.Array.from(expressions);
668 excludes = Ext.Array.from(excludes);
670 fn = fn || Ext.emptyFn;
672 scope = scope || Ext.global;
674 for (i = 0, ln = excludes.length; i < ln; i++) {
675 exclude = excludes[i];
677 if (typeof exclude === 'string' && exclude.length > 0) {
678 excludedClassNames = Manager.getNamesByExpression(exclude);
680 for (j = 0, subLn = excludedClassNames.length; j < subLn; j++) {
681 excluded[excludedClassNames[j]] = true;
686 for (i = 0, ln = expressions.length; i < ln; i++) {
687 expression = expressions[i];
689 if (typeof expression === 'string' && expression.length > 0) {
690 possibleClassNames = Manager.getNamesByExpression(expression);
692 for (j = 0, subLn = possibleClassNames.length; j < subLn; j++) {
693 possibleClassName = possibleClassNames[j];
695 if (!excluded.hasOwnProperty(possibleClassName) && !Manager.isCreated(possibleClassName)) {
696 Ext.Array.include(classNames, possibleClassName);
702 // If the dynamic dependency feature is not being used, throw an error
703 // if the dependencies are not defined
704 if (!this.config.enabled) {
705 if (classNames.length > 0) {
707 sourceClass: "Ext.Loader",
708 sourceMethod: "require",
709 msg: "Ext.Loader is not enabled, so dependencies cannot be resolved dynamically. " +
710 "Missing required class" + ((classNames.length > 1) ? "es" : "") + ": " + classNames.join(', ')
715 if (classNames.length === 0) {
721 requires: classNames,
726 classNames = classNames.slice();
728 for (i = 0, ln = classNames.length; i < ln; i++) {
729 className = classNames[i];
731 if (!this.isFileLoaded.hasOwnProperty(className)) {
732 this.isFileLoaded[className] = false;
734 filePath = this.getPath(className);
736 this.classNameToFilePathMap[className] = filePath;
738 this.numPendingFiles++;
740 //<if nonBrowser>
745 // Temporary support for hammerjs
747 var f = fs.open(filePath),
753 if (line.length === 0) {
763 this.onFileLoaded(className, filePath);
766 return Manager.get(className);
774 Ext.Function.pass(this.onFileLoaded, [className, filePath], this),
775 Ext.Function.pass(this.onFileLoadError, [className, filePath]),
785 <span id='Ext-Loader-method-onFileLoaded'> /**
787 * @param {String} className
788 * @param {String} filePath
790 onFileLoaded: function(className, filePath) {
791 this.numLoadedFiles++;
793 this.isFileLoaded[className] = true;
795 this.numPendingFiles--;
797 if (this.numPendingFiles === 0) {
802 if (this.numPendingFiles <= 1) {
803 window.status = "Finished loading all dependencies, onReady fired!";
806 window.status = "Loading dependencies, " + this.numPendingFiles + " files left...";
811 if (!this.syncModeEnabled && this.numPendingFiles === 0 && this.isLoading && !this.hasFileLoadError) {
812 var queue = this.queue,
814 i, ln, j, subLn, missingClasses = [], missingPaths = [];
816 for (i = 0, ln = queue.length; i < ln; i++) {
817 requires = queue[i].requires;
819 for (j = 0, subLn = requires.length; j < ln; j++) {
820 if (this.isFileLoaded[requires[j]]) {
821 missingClasses.push(requires[j]);
826 if (missingClasses.length < 1) {
830 missingClasses = Ext.Array.filter(missingClasses, function(item) {
831 return !this.requiresMap.hasOwnProperty(item);
834 for (i = 0,ln = missingClasses.length; i < ln; i++) {
835 missingPaths.push(this.classNameToFilePathMap[missingClasses[i]]);
839 sourceClass: "Ext.Loader",
840 sourceMethod: "onFileLoaded",
841 msg: "The following classes are not declared even if their files have been " +
842 "loaded: '" + missingClasses.join("', '") + "'. Please check the source code of their " +
843 "corresponding files for possible typos: '" + missingPaths.join("', '") + "'"
849 <span id='Ext-Loader-method-onFileLoadError'> /**
852 onFileLoadError: function(className, filePath, errorMessage, isSynchronous) {
853 this.numPendingFiles--;
854 this.hasFileLoadError = true;
856 //<debug error>
858 sourceClass: "Ext.Loader",
859 classToLoad: className,
861 loadingType: isSynchronous ? 'synchronous' : 'async',
867 <span id='Ext-Loader-method-addOptionalRequires'> /**
870 addOptionalRequires: function(requires) {
871 var optionalRequires = this.optionalRequires,
874 requires = Ext.Array.from(requires);
876 for (i = 0, ln = requires.length; i < ln; i++) {
877 require = requires[i];
879 Ext.Array.include(optionalRequires, require);
885 <span id='Ext-Loader-method-triggerReady'> /**
888 triggerReady: function(force) {
889 var readyListeners = this.readyListeners,
890 optionalRequires, listener;
892 if (this.isLoading || force) {
893 this.isLoading = false;
895 if (this.optionalRequires.length) {
896 // Clone then empty the array to eliminate potential recursive loop issue
897 optionalRequires = Ext.Array.clone(this.optionalRequires);
899 // Empty the original array
900 this.optionalRequires.length = 0;
902 this.require(optionalRequires, Ext.Function.pass(this.triggerReady, [true], this), this);
906 while (readyListeners.length) {
907 listener = readyListeners.shift();
908 listener.fn.call(listener.scope);
910 if (this.isLoading) {
919 <span id='Ext-Loader-method-onReady'> /**
920 </span> * Add a new listener to be executed when all required scripts are fully loaded
922 * @param {Function} fn The function callback to be executed
923 * @param {Object} scope The execution scope (<code>this</code>) of the callback function
924 * @param {Boolean} withDomReady Whether or not to wait for document dom ready as well
926 onReady: function(fn, scope, withDomReady, options) {
929 if (withDomReady !== false && Ext.onDocumentReady) {
933 Ext.onDocumentReady(oldFn, scope, options);
937 if (!this.isLoading) {
941 this.readyListeners.push({
948 <span id='Ext-Loader-method-historyPush'> /**
950 * @param {String} className
952 historyPush: function(className) {
953 if (className && this.isFileLoaded.hasOwnProperty(className)) {
954 Ext.Array.include(this.history, className);
961 <span id='Ext-method-require'> /**
962 </span> * Convenient alias of {@link Ext.Loader#require}. Please see the introduction documentation of
963 * {@link Ext.Loader} for examples.
967 Ext.require = alias(Loader, 'require');
969 <span id='Ext-method-syncRequire'> /**
970 </span> * Synchronous version of {@link Ext#require}, convenient alias of {@link Ext.Loader#syncRequire}.
973 * @method syncRequire
975 Ext.syncRequire = alias(Loader, 'syncRequire');
977 <span id='Ext-method-exclude'> /**
978 </span> * Convenient shortcut to {@link Ext.Loader#exclude}
982 Ext.exclude = alias(Loader, 'exclude');
984 <span id='Ext-method-onReady'> /**
985 </span> * @member Ext
988 Ext.onReady = function(fn, scope, options) {
989 Loader.onReady(fn, scope, true, options);
992 Class.registerPreprocessor('loader', function(cls, data, continueFn) {
995 className = Manager.getName(cls),
996 i, j, ln, subLn, value, propertyName, propertyValue;
999 Basically loop through the dependencyProperties, look for string class names and push
1000 them into a stack, regardless of whether the property's value is a string, array or object. For example:
1002 extend: 'Ext.MyClass',
1003 requires: ['Ext.some.OtherClass'],
1005 observable: 'Ext.util.Observable';
1008 which will later be transformed into:
1010 extend: Ext.MyClass,
1011 requires: [Ext.some.OtherClass],
1013 observable: Ext.util.Observable;
1018 for (i = 0, ln = dependencyProperties.length; i < ln; i++) {
1019 propertyName = dependencyProperties[i];
1021 if (data.hasOwnProperty(propertyName)) {
1022 propertyValue = data[propertyName];
1024 if (typeof propertyValue === 'string') {
1025 dependencies.push(propertyValue);
1027 else if (propertyValue instanceof Array) {
1028 for (j = 0, subLn = propertyValue.length; j < subLn; j++) {
1029 value = propertyValue[j];
1031 if (typeof value === 'string') {
1032 dependencies.push(value);
1037 for (j in propertyValue) {
1038 if (propertyValue.hasOwnProperty(j)) {
1039 value = propertyValue[j];
1041 if (typeof value === 'string') {
1042 dependencies.push(value);
1050 if (dependencies.length === 0) {
1051 // Loader.historyPush(className);
1055 //<debug error>
1056 var deadlockPath = [],
1057 requiresMap = Loader.requiresMap,
1061 Automatically detect deadlocks before-hand,
1062 will throw an error with detailed path for ease of debugging. Examples of deadlock cases:
1064 - A extends B, then B extends A
1065 - A requires B, B requires C, then C requires A
1067 The detectDeadlock function will recursively transverse till the leaf, hence it can detect deadlocks
1068 no matter how deep the path is.
1072 requiresMap[className] = dependencies;
1074 detectDeadlock = function(cls) {
1075 deadlockPath.push(cls);
1077 if (requiresMap[cls]) {
1078 if (Ext.Array.contains(requiresMap[cls], className)) {
1080 sourceClass: "Ext.Loader",
1081 msg: "Deadlock detected while loading dependencies! '" + className + "' and '" +
1082 deadlockPath[1] + "' " + "mutually require each other. Path: " +
1083 deadlockPath.join(' -> ') + " -> " + deadlockPath[0]
1087 for (i = 0, ln = requiresMap[cls].length; i < ln; i++) {
1088 detectDeadlock(requiresMap[cls][i]);
1093 detectDeadlock(className);
1098 Loader.require(dependencies, function() {
1099 for (i = 0, ln = dependencyProperties.length; i < ln; i++) {
1100 propertyName = dependencyProperties[i];
1102 if (data.hasOwnProperty(propertyName)) {
1103 propertyValue = data[propertyName];
1105 if (typeof propertyValue === 'string') {
1106 data[propertyName] = Manager.get(propertyValue);
1108 else if (propertyValue instanceof Array) {
1109 for (j = 0, subLn = propertyValue.length; j < subLn; j++) {
1110 value = propertyValue[j];
1112 if (typeof value === 'string') {
1113 data[propertyName][j] = Manager.get(value);
1118 for (var k in propertyValue) {
1119 if (propertyValue.hasOwnProperty(k)) {
1120 value = propertyValue[k];
1122 if (typeof value === 'string') {
1123 data[propertyName][k] = Manager.get(value);
1131 continueFn.call(me, cls, data);
1137 Class.setDefaultPreprocessorPosition('loader', 'after', 'className');
1139 Manager.registerPostprocessor('uses', function(name, cls, data) {
1140 var uses = Ext.Array.from(data.uses),
1144 for (i = 0, ln = uses.length; i < ln; i++) {
1147 if (typeof item === 'string') {
1152 Loader.addOptionalRequires(items);
1155 Manager.setDefaultPostprocessorPosition('uses', 'last');
1157 })(Ext.ClassManager, Ext.Class, Ext.Function.flexSetter, Ext.Function.alias);