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 <jacky@sencha.com>
3 * @docauthor Jacky Nguyen <jacky@sencha.com>
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:
11 # Asynchronous Loading #
15 + No web server needed: you can run the application via the file system protocol (i.e: `file://path/to/your/index
17 + Best possible debugging experience: error messages come with the exact file name and line number
20 + Dependencies need to be specified before-hand
22 ### Method 1: Explicitly include what you need: ###
25 Ext.require({String/Array} expressions);
27 // Example: Single alias
28 Ext.require('widget.window');
30 // Example: Single class name
31 Ext.require('Ext.window.Window');
33 // Example: Multiple aliases / class names mix
34 Ext.require(['widget.window', 'layout.border', 'Ext.data.Connection']);
37 Ext.require(['widget.*', 'layout.*', 'Ext.data.*']);
39 ### Method 2: Explicitly exclude what you don't need: ###
41 // Syntax: Note that it must be in this chaining format.
42 Ext.exclude({String/Array} expressions)
43 .require({String/Array} expressions);
45 // Include everything except Ext.data.*
46 Ext.exclude('Ext.data.*').require('*');
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.*');
52 # Synchronous Loading on Demand #
55 + There's no need to specify dependencies before-hand, which is always the convenience of including ext-all.js
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
63 There's one simple rule to follow: Instantiate everything with Ext.create instead of the `new` keyword
65 Ext.create('widget.window', { ... }); // Instead of new Ext.window.Window({...});
67 Ext.create('Ext.window.Window', {}); // Same as above, using full class name instead of alias
69 Ext.widget('window', {}); // Same as above, all you need is the traditional `xtype`
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.
75 # Hybrid Loading - The Best of Both Worlds #
77 It has all the advantages combined from asynchronous and synchronous loading. The development flow is simple:
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: ###
82 Ext.onReady(function(){
83 var window = Ext.createWidget('window', {
90 title: 'Hello Dialog',
107 ### Step 2: Along the way, when you need better debugging ability, watch the console for warnings like these: ###
109 [Ext.Loader] Synchronously loading 'Ext.window.Window'; consider adding Ext.require('Ext.window.Window') before your application's code
111 [Ext.Loader] Synchronously loading 'Ext.layout.container.Border'; consider adding Ext.require('Ext.layout.container.Border') before your application's code
113 Simply copy and paste the suggested code above `Ext.onReady`, i.e:
115 Ext.require('Ext.window.Window');
116 Ext.require('Ext.layout.container.Border');
120 Everything should now load via asynchronous mode.
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.
131 This process will be automated with Sencha Command, to be released and documented towards Ext JS 4 Final.
137 (function(Manager, Class, flexSetter, alias) {
140 //<if nonBrowser>
141 isNonBrowser = typeof window === 'undefined',
142 isNodeJS = isNonBrowser && (typeof require === 'function'),
143 isPhantomJS = (typeof phantom !== 'undefined' && phantom.fs),
145 dependencyProperties = ['extend', 'mixins', 'requires'],
148 Loader = Ext.Loader = {
149 <span id='Ext-Loader-property-documentHead'> /**
152 documentHead: typeof document !== 'undefined' && (document.head || document.getElementsByTagName('head')[0]),
154 <span id='Ext-Loader-property-isLoading'> /**
155 </span> * Flag indicating whether there are still files being loaded
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:
163 * requires: [...], // The required classes for this queue item
164 * callback: function() { ... } // The function to execute when all classes specified in requires exist
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
176 <span id='Ext-Loader-property-readyListeners'> /**
177 </span> * Maintain the list of listeners to execute when all required scripts are fully loaded
182 <span id='Ext-Loader-property-optionalRequires'> /**
183 </span> * Contains optional dependencies to be loaded last
186 optionalRequires: [],
188 <span id='Ext-Loader-property-requiresMap'> /**
189 </span> * Map of fully qualified class names to an array of dependent classes.
194 <span id='Ext-Loader-property-numPendingFiles'> /**
199 <span id='Ext-Loader-property-numLoadedFiles'> /**
204 <span id='Ext-Loader-property-hasFileLoadError'> /** @private */
205 </span> hasFileLoadError: false,
207 <span id='Ext-Loader-property-classNameToFilePathMap'> /**
210 classNameToFilePathMap: {},
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.
222 <span id='Ext-Loader-property-config'> /**
223 </span> * Configuration
227 <span id='Ext-Loader-cfg-enabled'> /**
228 </span> * Whether or not to enable the dynamic dependency loading feature
230 * @cfg {Boolean} enabled
234 <span id='Ext-Loader-cfg-disableCaching'> /**
235 </span> * @cfg {Boolean} disableCaching
236 * Appends current timestamp to script files to prevent caching
239 disableCaching: true,
241 <span id='Ext-Loader-cfg-disableCachingParam'> /**
242 </span> * @cfg {String} disableCachingParam
243 * The get parameter name for the cache buster's timestamp.
246 disableCachingParam: '_dc',
248 <span id='Ext-Loader-cfg-paths'> /**
249 </span> * @cfg {Object} paths
250 * The mapping from namespaces to file paths
252 'Ext': '.', // This is set by default, Ext.layout.container.Container will be
253 // loaded from ./layout/Container.js
255 'My': './src/my_own_folder' // My.layout.Container will be loaded from
256 // ./src/my_own_folder/layout/Container.js
258 * Note that all relative paths are relative to the current HTML document.
259 * If not being specified, for example, <code>Other.awesome.Class</code>
260 * will simply be loaded from <code>./Other/awesome/Class.js</code>
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:
271 <script type="text/javascript" src="ext-core-debug.js"></script>
272 <script type="text/javascript">
273 Ext.Loader.setConfig({
280 <script type="text/javascript">
283 Ext.onReady(function() {
284 // application code here
288 * Refer to {@link Ext.Loader#configs} for the list of possible properties
290 * @param {Object} config The config object to override the default values in {@link Ext.Loader#config}
291 * @return {Ext.Loader} this
294 setConfig: function(name, value) {
295 if (Ext.isObject(name) && arguments.length === 1) {
296 Ext.Object.merge(this.config, name);
299 this.config[name] = (Ext.isObject(value)) ? Ext.Object.merge(this.config[name], value) : value;
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}
310 getConfig: function(name) {
312 return this.config[name];
318 <span id='Ext-Loader-property-setPath'> /**
319 </span> * Sets the path of a namespace.
322 Ext.Loader.setPath('Ext', '.');
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
329 setPath: flexSetter(function(name, path) {
330 //<if nonBrowser>
333 path = require('fs').realpathSync(path);
337 this.config.paths[name] = path;
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:
346 Ext.Loader.setPath('My', '/path/to/My');
348 alert(Ext.Loader.getPath('My.awesome.Class')); // alerts '/path/to/My/awesome/Class.js'
350 * Note that the deeper namespace levels, if explicitly set, are always resolved first. For example:
353 'My': '/path/to/lib',
354 'My.awesome': '/other/path/for/awesome/stuff',
355 'My.awesome.more': '/more/awesome/path'
358 alert(Ext.Loader.getPath('My.awesome.Class')); // alerts '/other/path/for/awesome/stuff/Class.js'
360 alert(Ext.Loader.getPath('My.awesome.more.Class')); // alerts '/more/awesome/path/Class.js'
362 alert(Ext.Loader.getPath('My.cool.Class')); // alerts '/path/to/lib/cool/Class.js'
364 alert(Ext.Loader.getPath('Unknown.strange.Stuff')); // alerts 'Unknown/strange/Stuff.js'
366 * @param {String} className
367 * @return {String} path
370 getPath: function(className) {
372 paths = this.config.paths,
373 prefix = this.getPrefix(className);
375 if (prefix.length > 0) {
376 if (prefix === className) {
377 return paths[prefix];
380 path = paths[prefix];
381 className = className.substring(prefix.length + 1);
384 if (path.length > 0) {
388 return path.replace(/\/\.\//g, '/') + className.replace(/\./g, "/") + '.js';
391 <span id='Ext-Loader-method-getPrefix'> /**
393 * @param {String} className
395 getPrefix: function(className) {
396 var paths = this.config.paths,
397 prefix, deepestPrefix = '';
399 if (paths.hasOwnProperty(className)) {
403 for (prefix in paths) {
404 if (paths.hasOwnProperty(prefix) && prefix + '.' === className.substring(0, prefix.length + 1)) {
405 if (prefix.length > deepestPrefix.length) {
406 deepestPrefix = prefix;
411 return deepestPrefix;
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
420 refreshQueue: function() {
421 var ln = this.queue.length,
422 i, item, j, requires;
429 for (i = 0; i < ln; i++) {
430 item = this.queue[i];
433 requires = item.requires;
435 // Don't bother checking when the number of files loaded
436 // is still less than the array length
437 if (requires.length > this.numLoadedFiles) {
444 if (Manager.isCreated(requires[j])) {
445 // Take out from the queue
446 requires.splice(j, 1);
451 } while (j < requires.length);
453 if (item.requires.length === 0) {
454 this.queue.splice(i, 1);
455 item.callback.call(item.scope);
465 <span id='Ext-Loader-method-injectScriptElement'> /**
466 </span> * Inject a script element to document's head, call onLoad and onError accordingly
469 injectScriptElement: function(url, onLoad, onError, scope) {
470 var script = document.createElement('script'),
472 onLoadFn = function() {
473 me.cleanupScriptElement(script);
476 onErrorFn = function() {
477 me.cleanupScriptElement(script);
481 script.type = 'text/javascript';
483 script.onload = onLoadFn;
484 script.onerror = onErrorFn;
485 script.onreadystatechange = function() {
486 if (this.readyState === 'loaded' || this.readyState === 'complete') {
491 this.documentHead.appendChild(script);
496 <span id='Ext-Loader-method-cleanupScriptElement'> /**
499 cleanupScriptElement: function(script) {
500 script.onload = null;
501 script.onreadystatechange = null;
502 script.onerror = null;
507 <span id='Ext-Loader-method-loadScriptFile'> /**
508 </span> * Load a script file, supports both asynchronous and synchronous approaches
510 * @param {String} url
511 * @param {Function} onLoad
512 * @param {Scope} scope
513 * @param {Boolean} synchronous
516 loadScriptFile: function(url, onLoad, onError, scope, synchronous) {
518 noCacheUrl = url + (this.getConfig('disableCaching') ? ('?' + this.getConfig('disableCachingParam') + '=' + Ext.Date.now()) : ''),
519 fileName = url.split('/').pop(),
520 isCrossOriginRestricted = false,
521 xhr, status, onScriptError;
523 scope = scope || this;
525 this.isLoading = true;
528 onScriptError = function() {
529 onError.call(scope, "Failed loading '" + url + "', please verify that the file exists", synchronous);
532 if (!Ext.isReady && Ext.onDocumentReady) {
533 Ext.onDocumentReady(function() {
534 me.injectScriptElement(noCacheUrl, onLoad, onScriptError, scope);
538 this.injectScriptElement(noCacheUrl, onLoad, onScriptError, scope);
542 if (typeof XMLHttpRequest !== 'undefined') {
543 xhr = new XMLHttpRequest();
545 xhr = new ActiveXObject('Microsoft.XMLHTTP');
549 xhr.open('GET', noCacheUrl, false);
552 isCrossOriginRestricted = true;
555 status = (xhr.status === 1223) ? 204 : xhr.status;
557 if (!isCrossOriginRestricted) {
558 isCrossOriginRestricted = (status === 0);
561 if (isCrossOriginRestricted
562 //<if isNonBrowser>
563 && !isPhantomJS
566 onError.call(this, "Failed loading synchronously via XHR: '" + url + "'; It's likely that the file is either " +
567 "being loaded from a different domain or from the local file system whereby cross origin " +
568 "requests are not allowed due to security reasons. Use asynchronous loading with " +
569 "Ext.require instead.", synchronous);
571 else if (status >= 200 && status < 300
572 //<if isNonBrowser>
576 // Firebug friendly, file names are still shown even though they're eval'ed code
577 new Function(xhr.responseText + "\n//@ sourceURL=" + fileName)();
582 onError.call(this, "Failed loading synchronously via XHR: '" + url + "'; please " +
583 "verify that the file exists. " +
584 "XHR status code: " + status, synchronous);
587 // Prevent potential IE memory leak
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:
596 Ext.exclude('Ext.data.*').require('*');
598 Ext.exclude('widget.button*').require('widget.*');
600 * @param {Array} excludes
601 * @return {Object} object contains `require` method for chaining
604 exclude: function(excludes) {
608 require: function(expressions, fn, scope) {
609 return me.require(expressions, fn, scope, excludes);
612 syncRequire: function(expressions, fn, scope) {
613 return me.syncRequire(expressions, fn, scope, excludes);
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
626 syncRequire: function() {
627 this.syncModeEnabled = true;
628 this.require.apply(this, arguments);
630 this.syncModeEnabled = false;
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
642 require: function(expressions, fn, scope, excludes) {
643 var filePath, expression, exclude, className, excluded = {},
644 excludedClassNames = [],
645 possibleClassNames = [],
646 possibleClassName, classNames = [],
649 expressions = Ext.Array.from(expressions);
650 excludes = Ext.Array.from(excludes);
652 fn = fn || Ext.emptyFn;
654 scope = scope || Ext.global;
656 for (i = 0, ln = excludes.length; i < ln; i++) {
657 exclude = excludes[i];
659 if (typeof exclude === 'string' && exclude.length > 0) {
660 excludedClassNames = Manager.getNamesByExpression(exclude);
662 for (j = 0, subLn = excludedClassNames.length; j < subLn; j++) {
663 excluded[excludedClassNames[j]] = true;
668 for (i = 0, ln = expressions.length; i < ln; i++) {
669 expression = expressions[i];
671 if (typeof expression === 'string' && expression.length > 0) {
672 possibleClassNames = Manager.getNamesByExpression(expression);
674 for (j = 0, subLn = possibleClassNames.length; j < subLn; j++) {
675 possibleClassName = possibleClassNames[j];
677 if (!excluded.hasOwnProperty(possibleClassName) && !Manager.isCreated(possibleClassName)) {
678 Ext.Array.include(classNames, possibleClassName);
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 > 0) {
689 sourceClass: "Ext.Loader",
690 sourceMethod: "require",
691 msg: "Ext.Loader is not enabled, so dependencies cannot be resolved dynamically. " +
692 "Missing required class" + ((classNames.length > 1) ? "es" : "") + ": " + classNames.join(', ')
697 if (classNames.length === 0) {
703 requires: classNames,
708 classNames = classNames.slice();
710 for (i = 0, ln = classNames.length; i < ln; i++) {
711 className = classNames[i];
713 if (!this.isFileLoaded.hasOwnProperty(className)) {
714 this.isFileLoaded[className] = false;
716 filePath = this.getPath(className);
718 this.classNameToFilePathMap[className] = filePath;
720 this.numPendingFiles++;
722 //<if nonBrowser>
727 // Temporary support for hammerjs
729 var f = fs.open(filePath),
735 if (line.length === 0) {
745 this.onFileLoaded(className, filePath);
748 return Manager.get(className);
756 Ext.Function.pass(this.onFileLoaded, [className, filePath], this),
757 Ext.Function.pass(this.onFileLoadError, [className, filePath]),
767 <span id='Ext-Loader-method-onFileLoaded'> /**
769 * @param {String} className
770 * @param {String} filePath
772 onFileLoaded: function(className, filePath) {
773 this.numLoadedFiles++;
775 this.isFileLoaded[className] = true;
777 this.numPendingFiles--;
779 if (this.numPendingFiles === 0) {
784 if (this.numPendingFiles <= 1) {
785 window.status = "Finished loading all dependencies, onReady fired!";
788 window.status = "Loading dependencies, " + this.numPendingFiles + " files left...";
793 if (!this.syncModeEnabled && this.numPendingFiles === 0 && this.isLoading && !this.hasFileLoadError) {
794 var queue = this.queue,
796 i, ln, j, subLn, missingClasses = [], missingPaths = [];
798 for (i = 0, ln = queue.length; i < ln; i++) {
799 requires = queue[i].requires;
801 for (j = 0, subLn = requires.length; j < ln; j++) {
802 if (this.isFileLoaded[requires[j]]) {
803 missingClasses.push(requires[j]);
808 if (missingClasses.length < 1) {
812 missingClasses = Ext.Array.filter(missingClasses, function(item) {
813 return !this.requiresMap.hasOwnProperty(item);
816 for (i = 0,ln = missingClasses.length; i < ln; i++) {
817 missingPaths.push(this.classNameToFilePathMap[missingClasses[i]]);
821 sourceClass: "Ext.Loader",
822 sourceMethod: "onFileLoaded",
823 msg: "The following classes are not declared even if their files have been " +
824 "loaded: '" + missingClasses.join("', '") + "'. Please check the source code of their " +
825 "corresponding files for possible typos: '" + missingPaths.join("', '") + "'"
831 <span id='Ext-Loader-method-onFileLoadError'> /**
834 onFileLoadError: function(className, filePath, errorMessage, isSynchronous) {
835 this.numPendingFiles--;
836 this.hasFileLoadError = true;
838 //<debug error>
840 sourceClass: "Ext.Loader",
841 classToLoad: className,
843 loadingType: isSynchronous ? 'synchronous' : 'async',
849 <span id='Ext-Loader-method-addOptionalRequires'> /**
852 addOptionalRequires: function(requires) {
853 var optionalRequires = this.optionalRequires,
856 requires = Ext.Array.from(requires);
858 for (i = 0, ln = requires.length; i < ln; i++) {
859 require = requires[i];
861 Ext.Array.include(optionalRequires, require);
867 <span id='Ext-Loader-method-triggerReady'> /**
870 triggerReady: function(force) {
871 var readyListeners = this.readyListeners,
872 optionalRequires, listener;
874 if (this.isLoading || force) {
875 this.isLoading = false;
877 if (this.optionalRequires.length) {
878 // Clone then empty the array to eliminate potential recursive loop issue
879 optionalRequires = Ext.Array.clone(this.optionalRequires);
881 // Empty the original array
882 this.optionalRequires.length = 0;
884 this.require(optionalRequires, Ext.Function.pass(this.triggerReady, [true], this), this);
888 while (readyListeners.length) {
889 listener = readyListeners.shift();
890 listener.fn.call(listener.scope);
892 if (this.isLoading) {
901 <span id='Ext-Loader-method-onReady'> /**
902 </span> * Add a new listener to be executed when all required scripts are fully loaded
904 * @param {Function} fn The function callback to be executed
905 * @param {Object} scope The execution scope (<code>this</code>) of the callback function
906 * @param {Boolean} withDomReady Whether or not to wait for document dom ready as well
908 onReady: function(fn, scope, withDomReady, options) {
911 if (withDomReady !== false && Ext.onDocumentReady) {
915 Ext.onDocumentReady(oldFn, scope, options);
919 if (!this.isLoading) {
923 this.readyListeners.push({
930 <span id='Ext-Loader-method-historyPush'> /**
932 * @param {String} className
934 historyPush: function(className) {
935 if (className && this.isFileLoaded.hasOwnProperty(className)) {
936 Ext.Array.include(this.history, className);
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.
949 Ext.require = alias(Loader, 'require');
951 <span id='Ext-method-syncRequire'> /**
952 </span> * Synchronous version of {@link Ext#require}, convenient alias of {@link Ext.Loader#syncRequire}.
955 * @method syncRequire
957 Ext.syncRequire = alias(Loader, 'syncRequire');
959 <span id='Ext-method-exclude'> /**
960 </span> * Convenient shortcut to {@link Ext.Loader#exclude}
964 Ext.exclude = alias(Loader, 'exclude');
966 <span id='Ext-method-onReady'> /**
967 </span> * @member Ext
970 Ext.onReady = function(fn, scope, options) {
971 Loader.onReady(fn, scope, true, options);
974 Class.registerPreprocessor('loader', function(cls, data, continueFn) {
977 className = Manager.getName(cls),
978 i, j, ln, subLn, value, propertyName, propertyValue;
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:
984 extend: 'Ext.MyClass',
985 requires: ['Ext.some.OtherClass'],
987 observable: 'Ext.util.Observable';
990 which will later be transformed into:
993 requires: [Ext.some.OtherClass],
995 observable: Ext.util.Observable;
1000 for (i = 0, ln = dependencyProperties.length; i < ln; i++) {
1001 propertyName = dependencyProperties[i];
1003 if (data.hasOwnProperty(propertyName)) {
1004 propertyValue = data[propertyName];
1006 if (typeof propertyValue === 'string') {
1007 dependencies.push(propertyValue);
1009 else if (propertyValue instanceof Array) {
1010 for (j = 0, subLn = propertyValue.length; j < subLn; j++) {
1011 value = propertyValue[j];
1013 if (typeof value === 'string') {
1014 dependencies.push(value);
1019 for (j in propertyValue) {
1020 if (propertyValue.hasOwnProperty(j)) {
1021 value = propertyValue[j];
1023 if (typeof value === 'string') {
1024 dependencies.push(value);
1032 if (dependencies.length === 0) {
1033 // Loader.historyPush(className);
1037 //<debug error>
1038 var deadlockPath = [],
1039 requiresMap = Loader.requiresMap,
1043 Automatically detect deadlocks before-hand,
1044 will throw an error with detailed path for ease of debugging. Examples of deadlock cases:
1046 - A extends B, then B extends A
1047 - A requires B, B requires C, then C requires A
1049 The detectDeadlock function will recursively transverse till the leaf, hence it can detect deadlocks
1050 no matter how deep the path is.
1054 requiresMap[className] = dependencies;
1056 detectDeadlock = function(cls) {
1057 deadlockPath.push(cls);
1059 if (requiresMap[cls]) {
1060 if (Ext.Array.contains(requiresMap[cls], className)) {
1062 sourceClass: "Ext.Loader",
1063 msg: "Deadlock detected while loading dependencies! '" + className + "' and '" +
1064 deadlockPath[1] + "' " + "mutually require each other. Path: " +
1065 deadlockPath.join(' -> ') + " -> " + deadlockPath[0]
1069 for (i = 0, ln = requiresMap[cls].length; i < ln; i++) {
1070 detectDeadlock(requiresMap[cls][i]);
1075 detectDeadlock(className);
1080 Loader.require(dependencies, function() {
1081 for (i = 0, ln = dependencyProperties.length; i < ln; i++) {
1082 propertyName = dependencyProperties[i];
1084 if (data.hasOwnProperty(propertyName)) {
1085 propertyValue = data[propertyName];
1087 if (typeof propertyValue === 'string') {
1088 data[propertyName] = Manager.get(propertyValue);
1090 else if (propertyValue instanceof Array) {
1091 for (j = 0, subLn = propertyValue.length; j < subLn; j++) {
1092 value = propertyValue[j];
1094 if (typeof value === 'string') {
1095 data[propertyName][j] = Manager.get(value);
1100 for (var k in propertyValue) {
1101 if (propertyValue.hasOwnProperty(k)) {
1102 value = propertyValue[k];
1104 if (typeof value === 'string') {
1105 data[propertyName][k] = Manager.get(value);
1113 continueFn.call(me, cls, data);
1119 Class.setDefaultPreprocessorPosition('loader', 'after', 'className');
1121 Manager.registerPostprocessor('uses', function(name, cls, data) {
1122 var uses = Ext.Array.from(data.uses),
1126 for (i = 0, ln = uses.length; i < ln; i++) {
1129 if (typeof item === 'string') {
1134 Loader.addOptionalRequires(items);
1137 Manager.setDefaultPostprocessorPosition('uses', 'last');
1139 })(Ext.ClassManager, Ext.Class, Ext.Function.flexSetter, Ext.Function.alias);
1140 </pre></pre></body></html>