Loader.html 55 KB

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