Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / docs / source / Loader.html
1 <!DOCTYPE html>
2 <html>
3 <head>
4   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5   <title>The source code</title>
6   <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
7   <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
8   <style type="text/css">
9     .highlight { display: block; background-color: #ddd; }
10   </style>
11   <script type="text/javascript">
12     function highlight() {
13       document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
14     }
15   </script>
16 </head>
17 <body onload="prettyPrint(); highlight();">
18   <pre class="prettyprint lang-js"><span id='Ext-Loader'>/**
19 </span> * @class Ext.Loader
20  * @singleton
21  * @author Jacky Nguyen &lt;jacky@sencha.com&gt;
22  * @docauthor Jacky Nguyen &lt;jacky@sencha.com&gt;
23  *
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
27  * of each approach:
28  *
29  * # Asynchronous Loading
30  *
31  * - Advantages:
32  *       + Cross-domain
33  *       + No web server needed: you can run the application via the file system protocol
34  *     (i.e: `file://path/to/your/index.html`)
35  *       + Best possible debugging experience: error messages come with the exact file name and line number
36  *
37  * - Disadvantages:
38  *       + Dependencies need to be specified before-hand
39  *
40  * ### Method 1: Explicitly include what you need:
41  *
42  *     // Syntax
43  *     Ext.require({String/Array} expressions);
44  *
45  *     // Example: Single alias
46  *     Ext.require('widget.window');
47  *
48  *     // Example: Single class name
49  *     Ext.require('Ext.window.Window');
50  *
51  *     // Example: Multiple aliases / class names mix
52  *     Ext.require(['widget.window', 'layout.border', 'Ext.data.Connection']);
53  *
54  *     // Wildcards
55  *     Ext.require(['widget.*', 'layout.*', 'Ext.data.*']);
56  *
57  * ### Method 2: Explicitly exclude what you don't need:
58  *
59  *     // Syntax: Note that it must be in this chaining format.
60  *     Ext.exclude({String/Array} expressions)
61  *        .require({String/Array} expressions);
62  *
63  *     // Include everything except Ext.data.*
64  *     Ext.exclude('Ext.data.*').require('*'); 
65  *
66  *     // Include all widgets except widget.checkbox*,
67  *     // which will match widget.checkbox, widget.checkboxfield, widget.checkboxgroup, etc.
68  *     Ext.exclude('widget.checkbox*').require('widget.*');
69  *
70  * # Synchronous Loading on Demand
71  *
72  * - Advantages:
73  *       + There's no need to specify dependencies before-hand, which is always the convenience of including
74  *     ext-all.js before
75  *
76  * - Disadvantages:
77  *       + Not as good debugging experience since file name won't be shown (except in Firebug at the moment)
78  *       + Must be from the same domain due to XHR restriction
79  *       + Need a web server, same reason as above
80  *
81  * There's one simple rule to follow: Instantiate everything with Ext.create instead of the `new` keyword
82  *
83  *     Ext.create('widget.window', { ... }); // Instead of new Ext.window.Window({...});
84  *
85  *     Ext.create('Ext.window.Window', {}); // Same as above, using full class name instead of alias
86  *
87  *     Ext.widget('window', {}); // Same as above, all you need is the traditional `xtype`
88  *
89  * Behind the scene, {@link Ext.ClassManager} will automatically check whether the given class name / alias has already
90  * existed on the page. If it's not, Ext.Loader will immediately switch itself to synchronous mode and automatic load
91  * the given class and all its dependencies.
92  *
93  * # Hybrid Loading - The Best of Both Worlds
94  *
95  * It has all the advantages combined from asynchronous and synchronous loading. The development flow is simple:
96  *
97  * ### Step 1: Start writing your application using synchronous approach.
98  *
99  * Ext.Loader will automatically fetch all dependencies on demand as they're needed during run-time. For example:
100  *
101  *     Ext.onReady(function(){
102  *         var window = Ext.createWidget('window', {
103  *             width: 500,
104  *             height: 300,
105  *             layout: {
106  *                 type: 'border',
107  *                 padding: 5
108  *             },
109  *             title: 'Hello Dialog',
110  *             items: [{
111  *                 title: 'Navigation',
112  *                 collapsible: true,
113  *                 region: 'west',
114  *                 width: 200,
115  *                 html: 'Hello',
116  *                 split: true
117  *             }, {
118  *                 title: 'TabPanel',
119  *                 region: 'center'
120  *             }]
121  *         });
122  *
123  *         window.show();
124  *     })
125  *
126  * ### Step 2: Along the way, when you need better debugging ability, watch the console for warnings like these:
127  *
128  *     [Ext.Loader] Synchronously loading 'Ext.window.Window'; consider adding Ext.require('Ext.window.Window') before your application's code ClassManager.js:432
129  *     [Ext.Loader] Synchronously loading 'Ext.layout.container.Border'; consider adding Ext.require('Ext.layout.container.Border') before your application's code
130  *
131  * Simply copy and paste the suggested code above `Ext.onReady`, e.g.:
132  *
133  *     Ext.require('Ext.window.Window');
134  *     Ext.require('Ext.layout.container.Border');
135  *
136  *     Ext.onReady(...);
137  *
138  * Everything should now load via asynchronous mode.
139  *
140  * # Deployment
141  *
142  * It's important to note that dynamic loading should only be used during development on your local machines.
143  * During production, all dependencies should be combined into one single JavaScript file. Ext.Loader makes
144  * the whole process of transitioning from / to between development / maintenance and production as easy as
145  * possible. Internally {@link Ext.Loader#history Ext.Loader.history} maintains the list of all dependencies
146  * your application needs in the exact loading sequence. It's as simple as concatenating all files in this
147  * array into one, then include it on top of your application.
148  *
149  * This process will be automated with Sencha Command, to be released and documented towards Ext JS 4 Final.
150  */
151 (function(Manager, Class, flexSetter, alias) {
152
153     var
154         //&lt;if nonBrowser&gt;
155         isNonBrowser = typeof window === 'undefined',
156         isNodeJS = isNonBrowser &amp;&amp; (typeof require === 'function'),
157         isPhantomJS = (typeof phantom !== 'undefined' &amp;&amp; phantom.fs),
158         //&lt;/if&gt;
159         dependencyProperties = ['extend', 'mixins', 'requires'],
160         Loader;
161
162     Loader = Ext.Loader = {
163 <span id='Ext-Loader-property-documentHead'>        /**
164 </span>         * @private
165          */
166         documentHead: typeof document !== 'undefined' &amp;&amp; (document.head || document.getElementsByTagName('head')[0]),
167
168 <span id='Ext-Loader-property-isLoading'>        /**
169 </span>         * Flag indicating whether there are still files being loaded
170          * @private
171          */
172         isLoading: false,
173
174 <span id='Ext-Loader-property-queue'>        /**
175 </span>         * Maintain the queue for all dependencies. Each item in the array is an object of the format:
176          * {
177          *      requires: [...], // The required classes for this queue item
178          *      callback: function() { ... } // The function to execute when all classes specified in requires exist
179          * }
180          * @private
181          */
182         queue: [],
183
184 <span id='Ext-Loader-property-isFileLoaded'>        /**
185 </span>         * Maintain the list of files that have already been handled so that they never get double-loaded
186          * @private
187          */
188         isFileLoaded: {},
189
190 <span id='Ext-Loader-property-readyListeners'>        /**
191 </span>         * Maintain the list of listeners to execute when all required scripts are fully loaded
192          * @private
193          */
194         readyListeners: [],
195
196 <span id='Ext-Loader-property-optionalRequires'>        /**
197 </span>         * Contains optional dependencies to be loaded last
198          * @private
199          */
200         optionalRequires: [],
201
202 <span id='Ext-Loader-property-requiresMap'>        /**
203 </span>         * Map of fully qualified class names to an array of dependent classes.
204          * @private
205          */
206         requiresMap: {},
207
208 <span id='Ext-Loader-property-numPendingFiles'>        /**
209 </span>         * @private
210          */
211         numPendingFiles: 0,
212
213 <span id='Ext-Loader-property-numLoadedFiles'>        /**
214 </span>         * @private
215          */
216         numLoadedFiles: 0,
217
218 <span id='Ext-Loader-property-hasFileLoadError'>        /** @private */
219 </span>        hasFileLoadError: false,
220
221 <span id='Ext-Loader-property-classNameToFilePathMap'>        /**
222 </span>         * @private
223          */
224         classNameToFilePathMap: {},
225
226 <span id='Ext-Loader-property-history'>        /**
227 </span>         * @property {String[]} history
228          * An array of class names to keep track of the dependency loading order.
229          * This is not guaranteed to be the same everytime due to the asynchronous nature of the Loader.
230          */
231         history: [],
232
233 <span id='Ext-Loader-property-config'>        /**
234 </span>         * Configuration
235          * @private
236          */
237         config: {
238 <span id='Ext-Loader-cfg-enabled'>            /**
239 </span>             * @cfg {Boolean} enabled
240              * Whether or not to enable the dynamic dependency loading feature.
241              */
242             enabled: false,
243
244 <span id='Ext-Loader-cfg-disableCaching'>            /**
245 </span>             * @cfg {Boolean} disableCaching
246              * Appends current timestamp to script files to prevent caching.
247              */
248             disableCaching: true,
249
250 <span id='Ext-Loader-cfg-disableCachingParam'>            /**
251 </span>             * @cfg {String} disableCachingParam
252              * The get parameter name for the cache buster's timestamp.
253              */
254             disableCachingParam: '_dc',
255
256 <span id='Ext-Loader-cfg-paths'>            /**
257 </span>             * @cfg {Object} paths
258              * The mapping from namespaces to file paths
259              *
260              *     {
261              *         'Ext': '.', // This is set by default, Ext.layout.container.Container will be
262              *                     // loaded from ./layout/Container.js
263              *
264              *         'My': './src/my_own_folder' // My.layout.Container will be loaded from
265              *                                     // ./src/my_own_folder/layout/Container.js
266              *     }
267              *
268              * Note that all relative paths are relative to the current HTML document.
269              * If not being specified, for example, `Other.awesome.Class`
270              * will simply be loaded from `./Other/awesome/Class.js`
271              */
272             paths: {
273                 'Ext': '.'
274             }
275         },
276
277 <span id='Ext-Loader-method-setConfig'>        /**
278 </span>         * Set the configuration for the loader. This should be called right after ext-core.js
279          * (or ext-core-debug.js) is included in the page, e.g.:
280          *
281          *     &lt;script type=&quot;text/javascript&quot; src=&quot;ext-core-debug.js&quot;&gt;&lt;/script&gt;
282          *     &lt;script type=&quot;text/javascript&quot;&gt;
283          *       Ext.Loader.setConfig({
284          *           enabled: true,
285          *           paths: {
286          *               'My': 'my_own_path'
287          *           }
288          *       });
289          *     &lt;script&gt;
290          *     &lt;script type=&quot;text/javascript&quot;&gt;
291          *       Ext.require(...);
292          *
293          *       Ext.onReady(function() {
294          *           // application code here
295          *       });
296          *     &lt;/script&gt;
297          *
298          * Refer to config options of {@link Ext.Loader} for the list of possible properties.
299          *
300          * @param {String/Object} name  Name of the value to override, or a config object to override multiple values.
301          * @param {Object} value  (optional) The new value to set, needed if first parameter is String.
302          * @return {Ext.Loader} this
303          */
304         setConfig: function(name, value) {
305             if (Ext.isObject(name) &amp;&amp; arguments.length === 1) {
306                 Ext.Object.merge(this.config, name);
307             }
308             else {
309                 this.config[name] = (Ext.isObject(value)) ? Ext.Object.merge(this.config[name], value) : value;
310             }
311
312             return this;
313         },
314
315 <span id='Ext-Loader-method-getConfig'>        /**
316 </span>         * Get the config value corresponding to the specified name.
317          * If no name is given, will return the config object.
318          * @param {String} name The config property name
319          * @return {Object}
320          */
321         getConfig: function(name) {
322             if (name) {
323                 return this.config[name];
324             }
325
326             return this.config;
327         },
328
329 <span id='Ext-Loader-method-setPath'>        /**
330 </span>         * Sets the path of a namespace. For Example:
331          *
332          *     Ext.Loader.setPath('Ext', '.');
333          *
334          * @param {String/Object} name See {@link Ext.Function#flexSetter flexSetter}
335          * @param {String} path See {@link Ext.Function#flexSetter flexSetter}
336          * @return {Ext.Loader} this
337          * @method
338          */
339         setPath: flexSetter(function(name, path) {
340             //&lt;if nonBrowser&gt;
341             if (isNonBrowser) {
342                 if (isNodeJS) {
343                     path = require('fs').realpathSync(path);
344                 }
345             }
346             //&lt;/if&gt;
347             this.config.paths[name] = path;
348
349             return this;
350         }),
351
352 <span id='Ext-Loader-method-getPath'>        /**
353 </span>         * Translates a className to a file path by adding the the proper prefix and converting the .'s to /'s.
354          * For example:
355          *
356          *     Ext.Loader.setPath('My', '/path/to/My');
357          *
358          *     alert(Ext.Loader.getPath('My.awesome.Class')); // alerts '/path/to/My/awesome/Class.js'
359          *
360          * Note that the deeper namespace levels, if explicitly set, are always resolved first. For example:
361          *
362          *     Ext.Loader.setPath({
363          *         'My': '/path/to/lib',
364          *         'My.awesome': '/other/path/for/awesome/stuff',
365          *         'My.awesome.more': '/more/awesome/path'
366          *     });
367          *
368          *     alert(Ext.Loader.getPath('My.awesome.Class')); // alerts '/other/path/for/awesome/stuff/Class.js'
369          *
370          *     alert(Ext.Loader.getPath('My.awesome.more.Class')); // alerts '/more/awesome/path/Class.js'
371          *
372          *     alert(Ext.Loader.getPath('My.cool.Class')); // alerts '/path/to/lib/cool/Class.js'
373          *
374          *     alert(Ext.Loader.getPath('Unknown.strange.Stuff')); // alerts 'Unknown/strange/Stuff.js'
375          *
376          * @param {String} className
377          * @return {String} path
378          */
379         getPath: function(className) {
380             var path = '',
381                 paths = this.config.paths,
382                 prefix = this.getPrefix(className);
383
384             if (prefix.length &gt; 0) {
385                 if (prefix === className) {
386                     return paths[prefix];
387                 }
388
389                 path = paths[prefix];
390                 className = className.substring(prefix.length + 1);
391             }
392
393             if (path.length &gt; 0) {
394                 path += '/';
395             }
396
397             return path.replace(/\/\.\//g, '/') + className.replace(/\./g, &quot;/&quot;) + '.js';
398         },
399
400 <span id='Ext-Loader-method-getPrefix'>        /**
401 </span>         * @private
402          * @param {String} className
403          */
404         getPrefix: function(className) {
405             var paths = this.config.paths,
406                 prefix, deepestPrefix = '';
407
408             if (paths.hasOwnProperty(className)) {
409                 return className;
410             }
411
412             for (prefix in paths) {
413                 if (paths.hasOwnProperty(prefix) &amp;&amp; prefix + '.' === className.substring(0, prefix.length + 1)) {
414                     if (prefix.length &gt; deepestPrefix.length) {
415                         deepestPrefix = prefix;
416                     }
417                 }
418             }
419
420             return deepestPrefix;
421         },
422
423 <span id='Ext-Loader-method-refreshQueue'>        /**
424 </span>         * Refresh all items in the queue. If all dependencies for an item exist during looping,
425          * it will execute the callback and call refreshQueue again. Triggers onReady when the queue is
426          * empty
427          * @private
428          */
429         refreshQueue: function() {
430             var ln = this.queue.length,
431                 i, item, j, requires;
432
433             if (ln === 0) {
434                 this.triggerReady();
435                 return;
436             }
437
438             for (i = 0; i &lt; ln; i++) {
439                 item = this.queue[i];
440
441                 if (item) {
442                     requires = item.requires;
443
444                     // Don't bother checking when the number of files loaded
445                     // is still less than the array length
446                     if (requires.length &gt; this.numLoadedFiles) {
447                         continue;
448                     }
449
450                     j = 0;
451
452                     do {
453                         if (Manager.isCreated(requires[j])) {
454                             // Take out from the queue
455                             Ext.Array.erase(requires, j, 1);
456                         }
457                         else {
458                             j++;
459                         }
460                     } while (j &lt; requires.length);
461
462                     if (item.requires.length === 0) {
463                         Ext.Array.erase(this.queue, i, 1);
464                         item.callback.call(item.scope);
465                         this.refreshQueue();
466                         break;
467                     }
468                 }
469             }
470
471             return this;
472         },
473
474 <span id='Ext-Loader-method-injectScriptElement'>        /**
475 </span>         * Inject a script element to document's head, call onLoad and onError accordingly
476          * @private
477          */
478         injectScriptElement: function(url, onLoad, onError, scope) {
479             var script = document.createElement('script'),
480                 me = this,
481                 onLoadFn = function() {
482                     me.cleanupScriptElement(script);
483                     onLoad.call(scope);
484                 },
485                 onErrorFn = function() {
486                     me.cleanupScriptElement(script);
487                     onError.call(scope);
488                 };
489
490             script.type = 'text/javascript';
491             script.src = url;
492             script.onload = onLoadFn;
493             script.onerror = onErrorFn;
494             script.onreadystatechange = function() {
495                 if (this.readyState === 'loaded' || this.readyState === 'complete') {
496                     onLoadFn();
497                 }
498             };
499
500             this.documentHead.appendChild(script);
501
502             return script;
503         },
504
505 <span id='Ext-Loader-method-cleanupScriptElement'>        /**
506 </span>         * @private
507          */
508         cleanupScriptElement: function(script) {
509             script.onload = null;
510             script.onreadystatechange = null;
511             script.onerror = null;
512
513             return this;
514         },
515
516 <span id='Ext-Loader-method-loadScriptFile'>        /**
517 </span>         * Load a script file, supports both asynchronous and synchronous approaches
518          *
519          * @param {String} url
520          * @param {Function} onLoad
521          * @param {Object} scope
522          * @param {Boolean} synchronous
523          * @private
524          */
525         loadScriptFile: function(url, onLoad, onError, scope, synchronous) {
526             var me = this,
527                 noCacheUrl = url + (this.getConfig('disableCaching') ? ('?' + this.getConfig('disableCachingParam') + '=' + Ext.Date.now()) : ''),
528                 fileName = url.split('/').pop(),
529                 isCrossOriginRestricted = false,
530                 xhr, status, onScriptError;
531
532             scope = scope || this;
533
534             this.isLoading = true;
535
536             if (!synchronous) {
537                 onScriptError = function() {
538                     onError.call(scope, &quot;Failed loading '&quot; + url + &quot;', please verify that the file exists&quot;, synchronous);
539                 };
540
541                 if (!Ext.isReady &amp;&amp; Ext.onDocumentReady) {
542                     Ext.onDocumentReady(function() {
543                         me.injectScriptElement(noCacheUrl, onLoad, onScriptError, scope);
544                     });
545                 }
546                 else {
547                     this.injectScriptElement(noCacheUrl, onLoad, onScriptError, scope);
548                 }
549             }
550             else {
551                 if (typeof XMLHttpRequest !== 'undefined') {
552                     xhr = new XMLHttpRequest();
553                 } else {
554                     xhr = new ActiveXObject('Microsoft.XMLHTTP');
555                 }
556
557                 try {
558                     xhr.open('GET', noCacheUrl, false);
559                     xhr.send(null);
560                 } catch (e) {
561                     isCrossOriginRestricted = true;
562                 }
563
564                 status = (xhr.status === 1223) ? 204 : xhr.status;
565
566                 if (!isCrossOriginRestricted) {
567                     isCrossOriginRestricted = (status === 0);
568                 }
569
570                 if (isCrossOriginRestricted
571                 //&lt;if isNonBrowser&gt;
572                 &amp;&amp; !isPhantomJS
573                 //&lt;/if&gt;
574                 ) {
575                     onError.call(this, &quot;Failed loading synchronously via XHR: '&quot; + url + &quot;'; It's likely that the file is either &quot; +
576                                        &quot;being loaded from a different domain or from the local file system whereby cross origin &quot; +
577                                        &quot;requests are not allowed due to security reasons. Use asynchronous loading with &quot; +
578                                        &quot;Ext.require instead.&quot;, synchronous);
579                 }
580                 else if (status &gt;= 200 &amp;&amp; status &lt; 300
581                 //&lt;if isNonBrowser&gt;
582                 || isPhantomJS
583                 //&lt;/if&gt;
584                 ) {
585                     // Firebug friendly, file names are still shown even though they're eval'ed code
586                     new Function(xhr.responseText + &quot;\n//@ sourceURL=&quot; + fileName)();
587
588                     onLoad.call(scope);
589                 }
590                 else {
591                     onError.call(this, &quot;Failed loading synchronously via XHR: '&quot; + url + &quot;'; please &quot; +
592                                        &quot;verify that the file exists. &quot; +
593                                        &quot;XHR status code: &quot; + status, synchronous);
594                 }
595
596                 // Prevent potential IE memory leak
597                 xhr = null;
598             }
599         },
600
601 <span id='Ext-Loader-method-exclude'>        /**
602 </span>         * Explicitly exclude files from being loaded. Useful when used in conjunction with a broad include expression.
603          * Can be chained with more `require` and `exclude` methods, e.g.:
604          *
605          *     Ext.exclude('Ext.data.*').require('*');
606          *
607          *     Ext.exclude('widget.button*').require('widget.*');
608          *
609          * {@link Ext#exclude Ext.exclude} is alias for {@link Ext.Loader#exclude Ext.Loader.exclude} for convenience.
610          *
611          * @param {String/String[]} excludes
612          * @return {Object} object contains `require` method for chaining
613          */
614         exclude: function(excludes) {
615             var me = this;
616
617             return {
618                 require: function(expressions, fn, scope) {
619                     return me.require(expressions, fn, scope, excludes);
620                 },
621
622                 syncRequire: function(expressions, fn, scope) {
623                     return me.syncRequire(expressions, fn, scope, excludes);
624                 }
625             };
626         },
627
628 <span id='Ext-Loader-method-syncRequire'>        /**
629 </span>         * Synchronously loads all classes by the given names and all their direct dependencies;
630          * optionally executes the given callback function when finishes, within the optional scope.
631          *
632          * {@link Ext#syncRequire Ext.syncRequire} is alias for {@link Ext.Loader#syncRequire Ext.Loader.syncRequire} for convenience.
633          *
634          * @param {String/String[]} expressions Can either be a string or an array of string
635          * @param {Function} fn (Optional) The callback function
636          * @param {Object} scope (Optional) The execution scope (`this`) of the callback function
637          * @param {String/String[]} excludes (Optional) Classes to be excluded, useful when being used with expressions
638          */
639         syncRequire: function() {
640             this.syncModeEnabled = true;
641             this.require.apply(this, arguments);
642             this.refreshQueue();
643             this.syncModeEnabled = false;
644         },
645
646 <span id='Ext-Loader-method-require'>        /**
647 </span>         * Loads all classes by the given names and all their direct dependencies;
648          * optionally executes the given callback function when finishes, within the optional scope.
649          *
650          * {@link Ext#require Ext.require} is alias for {@link Ext.Loader#require Ext.Loader.require} for convenience.
651          *
652          * @param {String/String[]} expressions Can either be a string or an array of string
653          * @param {Function} fn (Optional) The callback function
654          * @param {Object} scope (Optional) The execution scope (`this`) of the callback function
655          * @param {String/String[]} excludes (Optional) Classes to be excluded, useful when being used with expressions
656          */
657         require: function(expressions, fn, scope, excludes) {
658             var filePath, expression, exclude, className, excluded = {},
659                 excludedClassNames = [],
660                 possibleClassNames = [],
661                 possibleClassName, classNames = [],
662                 i, j, ln, subLn;
663
664             expressions = Ext.Array.from(expressions);
665             excludes = Ext.Array.from(excludes);
666
667             fn = fn || Ext.emptyFn;
668
669             scope = scope || Ext.global;
670
671             for (i = 0, ln = excludes.length; i &lt; ln; i++) {
672                 exclude = excludes[i];
673
674                 if (typeof exclude === 'string' &amp;&amp; exclude.length &gt; 0) {
675                     excludedClassNames = Manager.getNamesByExpression(exclude);
676
677                     for (j = 0, subLn = excludedClassNames.length; j &lt; subLn; j++) {
678                         excluded[excludedClassNames[j]] = true;
679                     }
680                 }
681             }
682
683             for (i = 0, ln = expressions.length; i &lt; ln; i++) {
684                 expression = expressions[i];
685
686                 if (typeof expression === 'string' &amp;&amp; expression.length &gt; 0) {
687                     possibleClassNames = Manager.getNamesByExpression(expression);
688
689                     for (j = 0, subLn = possibleClassNames.length; j &lt; subLn; j++) {
690                         possibleClassName = possibleClassNames[j];
691
692                         if (!excluded.hasOwnProperty(possibleClassName) &amp;&amp; !Manager.isCreated(possibleClassName)) {
693                             Ext.Array.include(classNames, possibleClassName);
694                         }
695                     }
696                 }
697             }
698
699             // If the dynamic dependency feature is not being used, throw an error
700             // if the dependencies are not defined
701             if (!this.config.enabled) {
702                 if (classNames.length &gt; 0) {
703                     Ext.Error.raise({
704                         sourceClass: &quot;Ext.Loader&quot;,
705                         sourceMethod: &quot;require&quot;,
706                         msg: &quot;Ext.Loader is not enabled, so dependencies cannot be resolved dynamically. &quot; +
707                              &quot;Missing required class&quot; + ((classNames.length &gt; 1) ? &quot;es&quot; : &quot;&quot;) + &quot;: &quot; + classNames.join(', ')
708                     });
709                 }
710             }
711
712             if (classNames.length === 0) {
713                 fn.call(scope);
714                 return this;
715             }
716
717             this.queue.push({
718                 requires: classNames,
719                 callback: fn,
720                 scope: scope
721             });
722
723             classNames = classNames.slice();
724
725             for (i = 0, ln = classNames.length; i &lt; ln; i++) {
726                 className = classNames[i];
727
728                 if (!this.isFileLoaded.hasOwnProperty(className)) {
729                     this.isFileLoaded[className] = false;
730
731                     filePath = this.getPath(className);
732
733                     this.classNameToFilePathMap[className] = filePath;
734
735                     this.numPendingFiles++;
736
737                     //&lt;if nonBrowser&gt;
738                     if (isNonBrowser) {
739                         if (isNodeJS) {
740                             require(filePath);
741                         }
742                         // Temporary support for hammerjs
743                         else {
744                             var f = fs.open(filePath),
745                                 content = '',
746                                 line;
747
748                             while (true) {
749                                 line = f.readLine();
750                                 if (line.length === 0) {
751                                     break;
752                                 }
753                                 content += line;
754                             }
755
756                             f.close();
757                             eval(content);
758                         }
759
760                         this.onFileLoaded(className, filePath);
761
762                         if (ln === 1) {
763                             return Manager.get(className);
764                         }
765
766                         continue;
767                     }
768                     //&lt;/if&gt;
769                     this.loadScriptFile(
770                         filePath,
771                         Ext.Function.pass(this.onFileLoaded, [className, filePath], this),
772                         Ext.Function.pass(this.onFileLoadError, [className, filePath]),
773                         this,
774                         this.syncModeEnabled
775                     );
776                 }
777             }
778
779             return this;
780         },
781
782 <span id='Ext-Loader-method-onFileLoaded'>        /**
783 </span>         * @private
784          * @param {String} className
785          * @param {String} filePath
786          */
787         onFileLoaded: function(className, filePath) {
788             this.numLoadedFiles++;
789
790             this.isFileLoaded[className] = true;
791
792             this.numPendingFiles--;
793
794             if (this.numPendingFiles === 0) {
795                 this.refreshQueue();
796             }
797
798             //&lt;debug&gt;
799             if (this.numPendingFiles &lt;= 1) {
800                 window.status = &quot;Finished loading all dependencies, onReady fired!&quot;;
801             }
802             else {
803                 window.status = &quot;Loading dependencies, &quot; + this.numPendingFiles + &quot; files left...&quot;;
804             }
805             //&lt;/debug&gt;
806
807             //&lt;debug&gt;
808             if (!this.syncModeEnabled &amp;&amp; this.numPendingFiles === 0 &amp;&amp; this.isLoading &amp;&amp; !this.hasFileLoadError) {
809                 var queue = this.queue,
810                     requires,
811                     i, ln, j, subLn, missingClasses = [], missingPaths = [];
812
813                 for (i = 0, ln = queue.length; i &lt; ln; i++) {
814                     requires = queue[i].requires;
815
816                     for (j = 0, subLn = requires.length; j &lt; ln; j++) {
817                         if (this.isFileLoaded[requires[j]]) {
818                             missingClasses.push(requires[j]);
819                         }
820                     }
821                 }
822
823                 if (missingClasses.length &lt; 1) {
824                     return;
825                 }
826
827                 missingClasses = Ext.Array.filter(missingClasses, function(item) {
828                     return !this.requiresMap.hasOwnProperty(item);
829                 }, this);
830
831                 for (i = 0,ln = missingClasses.length; i &lt; ln; i++) {
832                     missingPaths.push(this.classNameToFilePathMap[missingClasses[i]]);
833                 }
834
835                 Ext.Error.raise({
836                     sourceClass: &quot;Ext.Loader&quot;,
837                     sourceMethod: &quot;onFileLoaded&quot;,
838                     msg: &quot;The following classes are not declared even if their files have been &quot; +
839                             &quot;loaded: '&quot; + missingClasses.join(&quot;', '&quot;) + &quot;'. Please check the source code of their &quot; +
840                             &quot;corresponding files for possible typos: '&quot; + missingPaths.join(&quot;', '&quot;) + &quot;'&quot;
841                 });
842             }
843             //&lt;/debug&gt;
844         },
845
846 <span id='Ext-Loader-method-onFileLoadError'>        /**
847 </span>         * @private
848          */
849         onFileLoadError: function(className, filePath, errorMessage, isSynchronous) {
850             this.numPendingFiles--;
851             this.hasFileLoadError = true;
852
853             //&lt;debug error&gt;
854             Ext.Error.raise({
855                 sourceClass: &quot;Ext.Loader&quot;,
856                 classToLoad: className,
857                 loadPath: filePath,
858                 loadingType: isSynchronous ? 'synchronous' : 'async',
859                 msg: errorMessage
860             });
861             //&lt;/debug&gt;
862         },
863
864 <span id='Ext-Loader-method-addOptionalRequires'>        /**
865 </span>         * @private
866          */
867         addOptionalRequires: function(requires) {
868             var optionalRequires = this.optionalRequires,
869                 i, ln, require;
870
871             requires = Ext.Array.from(requires);
872
873             for (i = 0, ln = requires.length; i &lt; ln; i++) {
874                 require = requires[i];
875
876                 Ext.Array.include(optionalRequires, require);
877             }
878
879             return this;
880         },
881
882 <span id='Ext-Loader-method-triggerReady'>        /**
883 </span>         * @private
884          */
885         triggerReady: function(force) {
886             var readyListeners = this.readyListeners,
887                 optionalRequires, listener;
888
889             if (this.isLoading || force) {
890                 this.isLoading = false;
891
892                 if (this.optionalRequires.length) {
893                     // Clone then empty the array to eliminate potential recursive loop issue
894                     optionalRequires = Ext.Array.clone(this.optionalRequires);
895
896                     // Empty the original array
897                     this.optionalRequires.length = 0;
898
899                     this.require(optionalRequires, Ext.Function.pass(this.triggerReady, [true], this), this);
900                     return this;
901                 }
902
903                 while (readyListeners.length) {
904                     listener = readyListeners.shift();
905                     listener.fn.call(listener.scope);
906
907                     if (this.isLoading) {
908                         return this;
909                     }
910                 }
911             }
912
913             return this;
914         },
915
916 <span id='Ext-Loader-method-onReady'>        /**
917 </span>         * Adds new listener to be executed when all required scripts are fully loaded.
918          *
919          * @param {Function} fn The function callback to be executed
920          * @param {Object} scope The execution scope (`this`) of the callback function
921          * @param {Boolean} withDomReady Whether or not to wait for document dom ready as well
922          */
923         onReady: function(fn, scope, withDomReady, options) {
924             var oldFn;
925
926             if (withDomReady !== false &amp;&amp; Ext.onDocumentReady) {
927                 oldFn = fn;
928
929                 fn = function() {
930                     Ext.onDocumentReady(oldFn, scope, options);
931                 };
932             }
933
934             if (!this.isLoading) {
935                 fn.call(scope);
936             }
937             else {
938                 this.readyListeners.push({
939                     fn: fn,
940                     scope: scope
941                 });
942             }
943         },
944
945 <span id='Ext-Loader-method-historyPush'>        /**
946 </span>         * @private
947          * @param {String} className
948          */
949         historyPush: function(className) {
950             if (className &amp;&amp; this.isFileLoaded.hasOwnProperty(className)) {
951                 Ext.Array.include(this.history, className);
952             }
953
954             return this;
955         }
956     };
957
958 <span id='Ext-method-require'>    /**
959 </span>     * @member Ext
960      * @method require
961      * @alias Ext.Loader#require
962      */
963     Ext.require = alias(Loader, 'require');
964
965 <span id='Ext-method-syncRequire'>    /**
966 </span>     * @member Ext
967      * @method syncRequire
968      * @alias Ext.Loader#syncRequire
969      */
970     Ext.syncRequire = alias(Loader, 'syncRequire');
971
972 <span id='Ext-method-exclude'>    /**
973 </span>     * @member Ext
974      * @method exclude
975      * @alias Ext.Loader#exclude
976      */
977     Ext.exclude = alias(Loader, 'exclude');
978
979 <span id='Ext-method-onReady'>    /**
980 </span>     * @member Ext
981      * @method onReady
982      * @alias Ext.Loader#onReady
983      */
984     Ext.onReady = function(fn, scope, options) {
985         Loader.onReady(fn, scope, true, options);
986     };
987
988 <span id='Ext-Class-cfg-requires'>    /**
989 </span>     * @cfg {String[]} requires
990      * @member Ext.Class
991      * List of classes that have to be loaded before instantiating this class.
992      * For example:
993      *
994      *     Ext.define('Mother', {
995      *         requires: ['Child'],
996      *         giveBirth: function() {
997      *             // we can be sure that child class is available.
998      *             return new Child();
999      *         }
1000      *     });
1001      */
1002     Class.registerPreprocessor('loader', function(cls, data, continueFn) {
1003         var me = this,
1004             dependencies = [],
1005             className = Manager.getName(cls),
1006             i, j, ln, subLn, value, propertyName, propertyValue;
1007
1008         /*
1009         Basically loop through the dependencyProperties, look for string class names and push
1010         them into a stack, regardless of whether the property's value is a string, array or object. For example:
1011         {
1012               extend: 'Ext.MyClass',
1013               requires: ['Ext.some.OtherClass'],
1014               mixins: {
1015                   observable: 'Ext.util.Observable';
1016               }
1017         }
1018         which will later be transformed into:
1019         {
1020               extend: Ext.MyClass,
1021               requires: [Ext.some.OtherClass],
1022               mixins: {
1023                   observable: Ext.util.Observable;
1024               }
1025         }
1026         */
1027
1028         for (i = 0, ln = dependencyProperties.length; i &lt; ln; i++) {
1029             propertyName = dependencyProperties[i];
1030
1031             if (data.hasOwnProperty(propertyName)) {
1032                 propertyValue = data[propertyName];
1033
1034                 if (typeof propertyValue === 'string') {
1035                     dependencies.push(propertyValue);
1036                 }
1037                 else if (propertyValue instanceof Array) {
1038                     for (j = 0, subLn = propertyValue.length; j &lt; subLn; j++) {
1039                         value = propertyValue[j];
1040
1041                         if (typeof value === 'string') {
1042                             dependencies.push(value);
1043                         }
1044                     }
1045                 }
1046                 else if (typeof propertyValue != 'function') {
1047                     for (j in propertyValue) {
1048                         if (propertyValue.hasOwnProperty(j)) {
1049                             value = propertyValue[j];
1050
1051                             if (typeof value === 'string') {
1052                                 dependencies.push(value);
1053                             }
1054                         }
1055                     }
1056                 }
1057             }
1058         }
1059
1060         if (dependencies.length === 0) {
1061 //            Loader.historyPush(className);
1062             return;
1063         }
1064
1065         //&lt;debug error&gt;
1066         var deadlockPath = [],
1067             requiresMap = Loader.requiresMap,
1068             detectDeadlock;
1069
1070         /*
1071         Automatically detect deadlocks before-hand,
1072         will throw an error with detailed path for ease of debugging. Examples of deadlock cases:
1073
1074         - A extends B, then B extends A
1075         - A requires B, B requires C, then C requires A
1076
1077         The detectDeadlock function will recursively transverse till the leaf, hence it can detect deadlocks
1078         no matter how deep the path is.
1079         */
1080
1081         if (className) {
1082             requiresMap[className] = dependencies;
1083
1084             detectDeadlock = function(cls) {
1085                 deadlockPath.push(cls);
1086
1087                 if (requiresMap[cls]) {
1088                     if (Ext.Array.contains(requiresMap[cls], className)) {
1089                         Ext.Error.raise({
1090                             sourceClass: &quot;Ext.Loader&quot;,
1091                             msg: &quot;Deadlock detected while loading dependencies! '&quot; + className + &quot;' and '&quot; +
1092                                 deadlockPath[1] + &quot;' &quot; + &quot;mutually require each other. Path: &quot; +
1093                                 deadlockPath.join(' -&gt; ') + &quot; -&gt; &quot; + deadlockPath[0]
1094                         });
1095                     }
1096
1097                     for (i = 0, ln = requiresMap[cls].length; i &lt; ln; i++) {
1098                         detectDeadlock(requiresMap[cls][i]);
1099                     }
1100                 }
1101             };
1102
1103             detectDeadlock(className);
1104         }
1105
1106         //&lt;/debug&gt;
1107
1108         Loader.require(dependencies, function() {
1109             for (i = 0, ln = dependencyProperties.length; i &lt; ln; i++) {
1110                 propertyName = dependencyProperties[i];
1111
1112                 if (data.hasOwnProperty(propertyName)) {
1113                     propertyValue = data[propertyName];
1114
1115                     if (typeof propertyValue === 'string') {
1116                         data[propertyName] = Manager.get(propertyValue);
1117                     }
1118                     else if (propertyValue instanceof Array) {
1119                         for (j = 0, subLn = propertyValue.length; j &lt; subLn; j++) {
1120                             value = propertyValue[j];
1121
1122                             if (typeof value === 'string') {
1123                                 data[propertyName][j] = Manager.get(value);
1124                             }
1125                         }
1126                     }
1127                     else if (typeof propertyValue != 'function') {
1128                         for (var k in propertyValue) {
1129                             if (propertyValue.hasOwnProperty(k)) {
1130                                 value = propertyValue[k];
1131
1132                                 if (typeof value === 'string') {
1133                                     data[propertyName][k] = Manager.get(value);
1134                                 }
1135                             }
1136                         }
1137                     }
1138                 }
1139             }
1140
1141             continueFn.call(me, cls, data);
1142         });
1143
1144         return false;
1145     }, true);
1146
1147     Class.setDefaultPreprocessorPosition('loader', 'after', 'className');
1148
1149 <span id='Ext-Class-cfg-uses'>    /**
1150 </span>     * @cfg {String[]} uses
1151      * @member Ext.Class
1152      * List of classes to load together with this class.  These aren't neccessarily loaded before
1153      * this class is instantiated. For example:
1154      *
1155      *     Ext.define('Mother', {
1156      *         uses: ['Child'],
1157      *         giveBirth: function() {
1158      *             // This code might, or might not work:
1159      *             // return new Child();
1160      *
1161      *             // Instead use Ext.create() to load the class at the spot if not loaded already:
1162      *             return Ext.create('Child');
1163      *         }
1164      *     });
1165      */
1166     Manager.registerPostprocessor('uses', function(name, cls, data) {
1167         var uses = Ext.Array.from(data.uses),
1168             items = [],
1169             i, ln, item;
1170
1171         for (i = 0, ln = uses.length; i &lt; ln; i++) {
1172             item = uses[i];
1173
1174             if (typeof item === 'string') {
1175                 items.push(item);
1176             }
1177         }
1178
1179         Loader.addOptionalRequires(items);
1180     });
1181
1182     Manager.setDefaultPostprocessorPosition('uses', 'last');
1183
1184 })(Ext.ClassManager, Ext.Class, Ext.Function.flexSetter, Ext.Function.alias);
1185 </pre>
1186 </body>
1187 </html>