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