4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5 <title>The source code</title>
6 <link href="../prettify/prettify.css" type="text/css" rel="stylesheet" />
7 <script type="text/javascript" src="../prettify/prettify.js"></script>
8 <style type="text/css">
9 .highlight { display: block; background-color: #ddd; }
11 <script type="text/javascript">
12 function highlight() {
13 document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
17 <body onload="prettyPrint(); highlight();">
18 <pre class="prettyprint lang-js"><span id='Ext-Loader'>/**
19 </span> * @class Ext.Loader
21 * @author Jacky Nguyen <jacky@sencha.com>
22 * @docauthor Jacky Nguyen <jacky@sencha.com>
24 * Ext.Loader is the heart of the new dynamic dependency loading capability in Ext JS 4+. It is most commonly used
25 * via the {@link Ext#require} shorthand. Ext.Loader supports both asynchronous and synchronous loading
26 * approaches, and leverage their advantages for the best development flow. We'll discuss about the pros and cons
29 * # Asynchronous Loading
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
38 * + Dependencies need to be specified before-hand
40 * ### Method 1: Explicitly include what you need:
43 * Ext.require({String/Array} expressions);
45 * // Example: Single alias
46 * Ext.require('widget.window');
48 * // Example: Single class name
49 * Ext.require('Ext.window.Window');
51 * // Example: Multiple aliases / class names mix
52 * Ext.require(['widget.window', 'layout.border', 'Ext.data.Connection']);
55 * Ext.require(['widget.*', 'layout.*', 'Ext.data.*']);
57 * ### Method 2: Explicitly exclude what you don't need:
59 * // Syntax: Note that it must be in this chaining format.
60 * Ext.exclude({String/Array} expressions)
61 * .require({String/Array} expressions);
63 * // Include everything except Ext.data.*
64 * Ext.exclude('Ext.data.*').require('*');
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.*');
70 * # Synchronous Loading on Demand
73 * + There's no need to specify dependencies before-hand, which is always the convenience of including
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
81 * There's one simple rule to follow: Instantiate everything with Ext.create instead of the `new` keyword
83 * Ext.create('widget.window', { ... }); // Instead of new Ext.window.Window({...});
85 * Ext.create('Ext.window.Window', {}); // Same as above, using full class name instead of alias
87 * Ext.widget('window', {}); // Same as above, all you need is the traditional `xtype`
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.
93 * # Hybrid Loading - The Best of Both Worlds
95 * It has all the advantages combined from asynchronous and synchronous loading. The development flow is simple:
97 * ### Step 1: Start writing your application using synchronous approach.
99 * Ext.Loader will automatically fetch all dependencies on demand as they're needed during run-time. For example:
101 * Ext.onReady(function(){
102 * var window = Ext.createWidget('window', {
109 * title: 'Hello Dialog',
111 * title: 'Navigation',
126 * ### Step 2: Along the way, when you need better debugging ability, watch the console for warnings like these:
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
131 * Simply copy and paste the suggested code above `Ext.onReady`, e.g.:
133 * Ext.require('Ext.window.Window');
134 * Ext.require('Ext.layout.container.Border');
138 * Everything should now load via asynchronous mode.
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.
149 * This process will be automated with Sencha Command, to be released and documented towards Ext JS 4 Final.
151 (function(Manager, Class, flexSetter, alias) {
154 //<if nonBrowser>
155 isNonBrowser = typeof window === 'undefined',
156 isNodeJS = isNonBrowser && (typeof require === 'function'),
157 isPhantomJS = (typeof phantom !== 'undefined' && phantom.fs),
159 dependencyProperties = ['extend', 'mixins', 'requires'],
162 Loader = Ext.Loader = {
163 <span id='Ext-Loader-property-documentHead'> /**
166 documentHead: typeof document !== 'undefined' && (document.head || document.getElementsByTagName('head')[0]),
168 <span id='Ext-Loader-property-isLoading'> /**
169 </span> * Flag indicating whether there are still files being loaded
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:
177 * requires: [...], // The required classes for this queue item
178 * callback: function() { ... } // The function to execute when all classes specified in requires exist
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
190 <span id='Ext-Loader-property-readyListeners'> /**
191 </span> * Maintain the list of listeners to execute when all required scripts are fully loaded
196 <span id='Ext-Loader-property-optionalRequires'> /**
197 </span> * Contains optional dependencies to be loaded last
200 optionalRequires: [],
202 <span id='Ext-Loader-property-requiresMap'> /**
203 </span> * Map of fully qualified class names to an array of dependent classes.
208 <span id='Ext-Loader-property-numPendingFiles'> /**
213 <span id='Ext-Loader-property-numLoadedFiles'> /**
218 <span id='Ext-Loader-property-hasFileLoadError'> /** @private */
219 </span> hasFileLoadError: false,
221 <span id='Ext-Loader-property-classNameToFilePathMap'> /**
224 classNameToFilePathMap: {},
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.
233 <span id='Ext-Loader-property-config'> /**
234 </span> * Configuration
238 <span id='Ext-Loader-cfg-enabled'> /**
239 </span> * @cfg {Boolean} enabled
240 * Whether or not to enable the dynamic dependency loading feature Defaults to false
244 <span id='Ext-Loader-cfg-disableCaching'> /**
245 </span> * @cfg {Boolean} disableCaching
246 * Appends current timestamp to script files to prevent caching Defaults to true
248 disableCaching: true,
250 <span id='Ext-Loader-cfg-disableCachingParam'> /**
251 </span> * @cfg {String} disableCachingParam
252 * The get parameter name for the cache buster's timestamp. Defaults to '_dc'
254 disableCachingParam: '_dc',
256 <span id='Ext-Loader-cfg-paths'> /**
257 </span> * @cfg {Object} paths
258 * The mapping from namespaces to file paths
261 * 'Ext': '.', // This is set by default, Ext.layout.container.Container will be
262 * // loaded from ./layout/Container.js
264 * 'My': './src/my_own_folder' // My.layout.Container will be loaded from
265 * // ./src/my_own_folder/layout/Container.js
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`
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.:
281 * <script type="text/javascript" src="ext-core-debug.js"></script>
282 * <script type="text/javascript">
283 * Ext.Loader.setConfig({
286 * 'My': 'my_own_path'
290 * <script type="text/javascript">
293 * Ext.onReady(function() {
294 * // application code here
298 * Refer to config options of {@link Ext.Loader} for the list of possible properties.
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
304 setConfig: function(name, value) {
305 if (Ext.isObject(name) && arguments.length === 1) {
306 Ext.Object.merge(this.config, name);
309 this.config[name] = (Ext.isObject(value)) ? Ext.Object.merge(this.config[name], value) : value;
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/Mixed}
321 getConfig: function(name) {
323 return this.config[name];
329 <span id='Ext-Loader-method-setPath'> /**
330 </span> * Sets the path of a namespace. For Example:
332 * Ext.Loader.setPath('Ext', '.');
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
339 setPath: flexSetter(function(name, path) {
340 //<if nonBrowser>
343 path = require('fs').realpathSync(path);
347 this.config.paths[name] = path;
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.
356 * Ext.Loader.setPath('My', '/path/to/My');
358 * alert(Ext.Loader.getPath('My.awesome.Class')); // alerts '/path/to/My/awesome/Class.js'
360 * Note that the deeper namespace levels, if explicitly set, are always resolved first. For example:
362 * Ext.Loader.setPath({
363 * 'My': '/path/to/lib',
364 * 'My.awesome': '/other/path/for/awesome/stuff',
365 * 'My.awesome.more': '/more/awesome/path'
368 * alert(Ext.Loader.getPath('My.awesome.Class')); // alerts '/other/path/for/awesome/stuff/Class.js'
370 * alert(Ext.Loader.getPath('My.awesome.more.Class')); // alerts '/more/awesome/path/Class.js'
372 * alert(Ext.Loader.getPath('My.cool.Class')); // alerts '/path/to/lib/cool/Class.js'
374 * alert(Ext.Loader.getPath('Unknown.strange.Stuff')); // alerts 'Unknown/strange/Stuff.js'
376 * @param {String} className
377 * @return {String} path
379 getPath: function(className) {
381 paths = this.config.paths,
382 prefix = this.getPrefix(className);
384 if (prefix.length > 0) {
385 if (prefix === className) {
386 return paths[prefix];
389 path = paths[prefix];
390 className = className.substring(prefix.length + 1);
393 if (path.length > 0) {
397 return path.replace(/\/\.\//g, '/') + className.replace(/\./g, "/") + '.js';
400 <span id='Ext-Loader-method-getPrefix'> /**
402 * @param {String} className
404 getPrefix: function(className) {
405 var paths = this.config.paths,
406 prefix, deepestPrefix = '';
408 if (paths.hasOwnProperty(className)) {
412 for (prefix in paths) {
413 if (paths.hasOwnProperty(prefix) && prefix + '.' === className.substring(0, prefix.length + 1)) {
414 if (prefix.length > deepestPrefix.length) {
415 deepestPrefix = prefix;
420 return deepestPrefix;
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
429 refreshQueue: function() {
430 var ln = this.queue.length,
431 i, item, j, requires;
438 for (i = 0; i < ln; i++) {
439 item = this.queue[i];
442 requires = item.requires;
444 // Don't bother checking when the number of files loaded
445 // is still less than the array length
446 if (requires.length > this.numLoadedFiles) {
453 if (Manager.isCreated(requires[j])) {
454 // Take out from the queue
455 Ext.Array.erase(requires, j, 1);
460 } while (j < requires.length);
462 if (item.requires.length === 0) {
463 Ext.Array.erase(this.queue, i, 1);
464 item.callback.call(item.scope);
474 <span id='Ext-Loader-method-injectScriptElement'> /**
475 </span> * Inject a script element to document's head, call onLoad and onError accordingly
478 injectScriptElement: function(url, onLoad, onError, scope) {
479 var script = document.createElement('script'),
481 onLoadFn = function() {
482 me.cleanupScriptElement(script);
485 onErrorFn = function() {
486 me.cleanupScriptElement(script);
490 script.type = 'text/javascript';
492 script.onload = onLoadFn;
493 script.onerror = onErrorFn;
494 script.onreadystatechange = function() {
495 if (this.readyState === 'loaded' || this.readyState === 'complete') {
500 this.documentHead.appendChild(script);
505 <span id='Ext-Loader-method-cleanupScriptElement'> /**
508 cleanupScriptElement: function(script) {
509 script.onload = null;
510 script.onreadystatechange = null;
511 script.onerror = null;
516 <span id='Ext-Loader-method-loadScriptFile'> /**
517 </span> * Load a script file, supports both asynchronous and synchronous approaches
519 * @param {String} url
520 * @param {Function} onLoad
521 * @param {Scope} scope
522 * @param {Boolean} synchronous
525 loadScriptFile: function(url, onLoad, onError, scope, synchronous) {
527 noCacheUrl = url + (this.getConfig('disableCaching') ? ('?' + this.getConfig('disableCachingParam') + '=' + Ext.Date.now()) : ''),
528 fileName = url.split('/').pop(),
529 isCrossOriginRestricted = false,
530 xhr, status, onScriptError;
532 scope = scope || this;
534 this.isLoading = true;
537 onScriptError = function() {
538 onError.call(scope, "Failed loading '" + url + "', please verify that the file exists", synchronous);
541 if (!Ext.isReady && Ext.onDocumentReady) {
542 Ext.onDocumentReady(function() {
543 me.injectScriptElement(noCacheUrl, onLoad, onScriptError, scope);
547 this.injectScriptElement(noCacheUrl, onLoad, onScriptError, scope);
551 if (typeof XMLHttpRequest !== 'undefined') {
552 xhr = new XMLHttpRequest();
554 xhr = new ActiveXObject('Microsoft.XMLHTTP');
558 xhr.open('GET', noCacheUrl, false);
561 isCrossOriginRestricted = true;
564 status = (xhr.status === 1223) ? 204 : xhr.status;
566 if (!isCrossOriginRestricted) {
567 isCrossOriginRestricted = (status === 0);
570 if (isCrossOriginRestricted
571 //<if isNonBrowser>
572 && !isPhantomJS
575 onError.call(this, "Failed loading synchronously via XHR: '" + url + "'; It's likely that the file is either " +
576 "being loaded from a different domain or from the local file system whereby cross origin " +
577 "requests are not allowed due to security reasons. Use asynchronous loading with " +
578 "Ext.require instead.", synchronous);
580 else if (status >= 200 && status < 300
581 //<if isNonBrowser>
585 // Firebug friendly, file names are still shown even though they're eval'ed code
586 new Function(xhr.responseText + "\n//@ sourceURL=" + fileName)();
591 onError.call(this, "Failed loading synchronously via XHR: '" + url + "'; please " +
592 "verify that the file exists. " +
593 "XHR status code: " + status, synchronous);
596 // Prevent potential IE memory leak
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.:
605 * Ext.exclude('Ext.data.*').require('*');
607 * Ext.exclude('widget.button*').require('widget.*');
609 * {@link Ext#exclude Ext.exclude} is alias for {@link Ext.Loader#exclude Ext.Loader.exclude} for convenience.
611 * @param {String/[String]} excludes
612 * @return {Object} object contains `require` method for chaining
614 exclude: function(excludes) {
618 require: function(expressions, fn, scope) {
619 return me.require(expressions, fn, scope, excludes);
622 syncRequire: function(expressions, fn, scope) {
623 return me.syncRequire(expressions, fn, scope, excludes);
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.
632 * {@link Ext#syncRequire Ext.syncRequire} is alias for {@link Ext.Loader#syncRequire Ext.Loader.syncRequire} for convenience.
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
639 syncRequire: function() {
640 this.syncModeEnabled = true;
641 this.require.apply(this, arguments);
643 this.syncModeEnabled = false;
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.
650 * {@link Ext#require Ext.require} is alias for {@link Ext.Loader#require Ext.Loader.require} for convenience.
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
657 require: function(expressions, fn, scope, excludes) {
658 var filePath, expression, exclude, className, excluded = {},
659 excludedClassNames = [],
660 possibleClassNames = [],
661 possibleClassName, classNames = [],
664 expressions = Ext.Array.from(expressions);
665 excludes = Ext.Array.from(excludes);
667 fn = fn || Ext.emptyFn;
669 scope = scope || Ext.global;
671 for (i = 0, ln = excludes.length; i < ln; i++) {
672 exclude = excludes[i];
674 if (typeof exclude === 'string' && exclude.length > 0) {
675 excludedClassNames = Manager.getNamesByExpression(exclude);
677 for (j = 0, subLn = excludedClassNames.length; j < subLn; j++) {
678 excluded[excludedClassNames[j]] = true;
683 for (i = 0, ln = expressions.length; i < ln; i++) {
684 expression = expressions[i];
686 if (typeof expression === 'string' && expression.length > 0) {
687 possibleClassNames = Manager.getNamesByExpression(expression);
689 for (j = 0, subLn = possibleClassNames.length; j < subLn; j++) {
690 possibleClassName = possibleClassNames[j];
692 if (!excluded.hasOwnProperty(possibleClassName) && !Manager.isCreated(possibleClassName)) {
693 Ext.Array.include(classNames, possibleClassName);
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 > 0) {
704 sourceClass: "Ext.Loader",
705 sourceMethod: "require",
706 msg: "Ext.Loader is not enabled, so dependencies cannot be resolved dynamically. " +
707 "Missing required class" + ((classNames.length > 1) ? "es" : "") + ": " + classNames.join(', ')
712 if (classNames.length === 0) {
718 requires: classNames,
723 classNames = classNames.slice();
725 for (i = 0, ln = classNames.length; i < ln; i++) {
726 className = classNames[i];
728 if (!this.isFileLoaded.hasOwnProperty(className)) {
729 this.isFileLoaded[className] = false;
731 filePath = this.getPath(className);
733 this.classNameToFilePathMap[className] = filePath;
735 this.numPendingFiles++;
737 //<if nonBrowser>
742 // Temporary support for hammerjs
744 var f = fs.open(filePath),
750 if (line.length === 0) {
760 this.onFileLoaded(className, filePath);
763 return Manager.get(className);
771 Ext.Function.pass(this.onFileLoaded, [className, filePath], this),
772 Ext.Function.pass(this.onFileLoadError, [className, filePath]),
782 <span id='Ext-Loader-method-onFileLoaded'> /**
784 * @param {String} className
785 * @param {String} filePath
787 onFileLoaded: function(className, filePath) {
788 this.numLoadedFiles++;
790 this.isFileLoaded[className] = true;
792 this.numPendingFiles--;
794 if (this.numPendingFiles === 0) {
799 if (this.numPendingFiles <= 1) {
800 window.status = "Finished loading all dependencies, onReady fired!";
803 window.status = "Loading dependencies, " + this.numPendingFiles + " files left...";
808 if (!this.syncModeEnabled && this.numPendingFiles === 0 && this.isLoading && !this.hasFileLoadError) {
809 var queue = this.queue,
811 i, ln, j, subLn, missingClasses = [], missingPaths = [];
813 for (i = 0, ln = queue.length; i < ln; i++) {
814 requires = queue[i].requires;
816 for (j = 0, subLn = requires.length; j < ln; j++) {
817 if (this.isFileLoaded[requires[j]]) {
818 missingClasses.push(requires[j]);
823 if (missingClasses.length < 1) {
827 missingClasses = Ext.Array.filter(missingClasses, function(item) {
828 return !this.requiresMap.hasOwnProperty(item);
831 for (i = 0,ln = missingClasses.length; i < ln; i++) {
832 missingPaths.push(this.classNameToFilePathMap[missingClasses[i]]);
836 sourceClass: "Ext.Loader",
837 sourceMethod: "onFileLoaded",
838 msg: "The following classes are not declared even if their files have been " +
839 "loaded: '" + missingClasses.join("', '") + "'. Please check the source code of their " +
840 "corresponding files for possible typos: '" + missingPaths.join("', '") + "'"
846 <span id='Ext-Loader-method-onFileLoadError'> /**
849 onFileLoadError: function(className, filePath, errorMessage, isSynchronous) {
850 this.numPendingFiles--;
851 this.hasFileLoadError = true;
853 //<debug error>
855 sourceClass: "Ext.Loader",
856 classToLoad: className,
858 loadingType: isSynchronous ? 'synchronous' : 'async',
864 <span id='Ext-Loader-method-addOptionalRequires'> /**
867 addOptionalRequires: function(requires) {
868 var optionalRequires = this.optionalRequires,
871 requires = Ext.Array.from(requires);
873 for (i = 0, ln = requires.length; i < ln; i++) {
874 require = requires[i];
876 Ext.Array.include(optionalRequires, require);
882 <span id='Ext-Loader-method-triggerReady'> /**
885 triggerReady: function(force) {
886 var readyListeners = this.readyListeners,
887 optionalRequires, listener;
889 if (this.isLoading || force) {
890 this.isLoading = false;
892 if (this.optionalRequires.length) {
893 // Clone then empty the array to eliminate potential recursive loop issue
894 optionalRequires = Ext.Array.clone(this.optionalRequires);
896 // Empty the original array
897 this.optionalRequires.length = 0;
899 this.require(optionalRequires, Ext.Function.pass(this.triggerReady, [true], this), this);
903 while (readyListeners.length) {
904 listener = readyListeners.shift();
905 listener.fn.call(listener.scope);
907 if (this.isLoading) {
916 <span id='Ext-Loader-method-onReady'> /**
917 </span> * Adds new listener to be executed when all required scripts are fully loaded.
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
923 onReady: function(fn, scope, withDomReady, options) {
926 if (withDomReady !== false && Ext.onDocumentReady) {
930 Ext.onDocumentReady(oldFn, scope, options);
934 if (!this.isLoading) {
938 this.readyListeners.push({
945 <span id='Ext-Loader-method-historyPush'> /**
947 * @param {String} className
949 historyPush: function(className) {
950 if (className && this.isFileLoaded.hasOwnProperty(className)) {
951 Ext.Array.include(this.history, className);
958 <span id='Ext-method-require'> /**
959 </span> * @member Ext
961 * @alias Ext.Loader#require
963 Ext.require = alias(Loader, 'require');
965 <span id='Ext-method-syncRequire'> /**
966 </span> * @member Ext
967 * @method syncRequire
968 * @alias Ext.Loader#syncRequire
970 Ext.syncRequire = alias(Loader, 'syncRequire');
972 <span id='Ext-method-exclude'> /**
973 </span> * @member Ext
975 * @alias Ext.Loader#exclude
977 Ext.exclude = alias(Loader, 'exclude');
979 <span id='Ext-method-onReady'> /**
980 </span> * @member Ext
982 * @alias Ext.Loader#onReady
984 Ext.onReady = function(fn, scope, options) {
985 Loader.onReady(fn, scope, true, options);
988 <span id='Ext-Class-cfg-requires'> /**
989 </span> * @cfg {[String]} requires
991 * List of classes that have to be loaded before instanciating this class.
994 * Ext.define('Mother', {
995 * requires: ['Child'],
996 * giveBirth: function() {
997 * // we can be sure that child class is available.
998 * return new Child();
1002 Class.registerPreprocessor('loader', function(cls, data, continueFn) {
1005 className = Manager.getName(cls),
1006 i, j, ln, subLn, value, propertyName, propertyValue;
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:
1012 extend: 'Ext.MyClass',
1013 requires: ['Ext.some.OtherClass'],
1015 observable: 'Ext.util.Observable';
1018 which will later be transformed into:
1020 extend: Ext.MyClass,
1021 requires: [Ext.some.OtherClass],
1023 observable: Ext.util.Observable;
1028 for (i = 0, ln = dependencyProperties.length; i < ln; i++) {
1029 propertyName = dependencyProperties[i];
1031 if (data.hasOwnProperty(propertyName)) {
1032 propertyValue = data[propertyName];
1034 if (typeof propertyValue === 'string') {
1035 dependencies.push(propertyValue);
1037 else if (propertyValue instanceof Array) {
1038 for (j = 0, subLn = propertyValue.length; j < subLn; j++) {
1039 value = propertyValue[j];
1041 if (typeof value === 'string') {
1042 dependencies.push(value);
1047 for (j in propertyValue) {
1048 if (propertyValue.hasOwnProperty(j)) {
1049 value = propertyValue[j];
1051 if (typeof value === 'string') {
1052 dependencies.push(value);
1060 if (dependencies.length === 0) {
1061 // Loader.historyPush(className);
1065 //<debug error>
1066 var deadlockPath = [],
1067 requiresMap = Loader.requiresMap,
1071 Automatically detect deadlocks before-hand,
1072 will throw an error with detailed path for ease of debugging. Examples of deadlock cases:
1074 - A extends B, then B extends A
1075 - A requires B, B requires C, then C requires A
1077 The detectDeadlock function will recursively transverse till the leaf, hence it can detect deadlocks
1078 no matter how deep the path is.
1082 requiresMap[className] = dependencies;
1084 detectDeadlock = function(cls) {
1085 deadlockPath.push(cls);
1087 if (requiresMap[cls]) {
1088 if (Ext.Array.contains(requiresMap[cls], className)) {
1090 sourceClass: "Ext.Loader",
1091 msg: "Deadlock detected while loading dependencies! '" + className + "' and '" +
1092 deadlockPath[1] + "' " + "mutually require each other. Path: " +
1093 deadlockPath.join(' -> ') + " -> " + deadlockPath[0]
1097 for (i = 0, ln = requiresMap[cls].length; i < ln; i++) {
1098 detectDeadlock(requiresMap[cls][i]);
1103 detectDeadlock(className);
1108 Loader.require(dependencies, function() {
1109 for (i = 0, ln = dependencyProperties.length; i < ln; i++) {
1110 propertyName = dependencyProperties[i];
1112 if (data.hasOwnProperty(propertyName)) {
1113 propertyValue = data[propertyName];
1115 if (typeof propertyValue === 'string') {
1116 data[propertyName] = Manager.get(propertyValue);
1118 else if (propertyValue instanceof Array) {
1119 for (j = 0, subLn = propertyValue.length; j < subLn; j++) {
1120 value = propertyValue[j];
1122 if (typeof value === 'string') {
1123 data[propertyName][j] = Manager.get(value);
1128 for (var k in propertyValue) {
1129 if (propertyValue.hasOwnProperty(k)) {
1130 value = propertyValue[k];
1132 if (typeof value === 'string') {
1133 data[propertyName][k] = Manager.get(value);
1141 continueFn.call(me, cls, data);
1147 Class.setDefaultPreprocessorPosition('loader', 'after', 'className');
1149 <span id='Ext-Class-cfg-uses'> /**
1150 </span> * @cfg {[String]} uses
1152 * List of classes to load together with this class. These aren't neccessarily loaded before
1153 * this class is instanciated. For example:
1155 * Ext.define('Mother', {
1157 * giveBirth: function() {
1158 * // This code might, or might not work:
1159 * // return new Child();
1161 * // Instead use Ext.create() to load the class at the spot if not loaded already:
1162 * return Ext.create('Child');
1166 Manager.registerPostprocessor('uses', function(name, cls, data) {
1167 var uses = Ext.Array.from(data.uses),
1171 for (i = 0, ln = uses.length; i < ln; i++) {
1174 if (typeof item === 'string') {
1179 Loader.addOptionalRequires(items);
1182 Manager.setDefaultPostprocessorPosition('uses', 'last');
1184 })(Ext.ClassManager, Ext.Class, Ext.Function.flexSetter, Ext.Function.alias);