| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449 | <!DOCTYPE html><html><head>  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />  <title>The source code</title>  <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />  <script type="text/javascript" src="../resources/prettify/prettify.js"></script>  <style type="text/css">    .highlight { display: block; background-color: #ddd; }  </style>  <script type="text/javascript">    function highlight() {      document.getElementById(location.hash.replace(/#/, "")).className = "highlight";    }  </script></head><body onload="prettyPrint(); highlight();">  <pre class="prettyprint lang-js"><span id='Ext-Loader'>/**</span> * @author Jacky Nguyen <jacky@sencha.com> * @docauthor Jacky Nguyen <jacky@sencha.com> * @class Ext.Loader * * Ext.Loader is the heart of the new dynamic dependency loading capability in Ext JS 4+. It is most commonly used * via the {@link Ext#require} shorthand. Ext.Loader supports both asynchronous and synchronous loading * approaches, and leverage their advantages for the best development flow. We'll discuss about the pros and cons of each approach: * * # Asynchronous Loading # * * - Advantages: *     + Cross-domain *     + No web server needed: you can run the application via the file system protocol (i.e: `file://path/to/your/index *  .html`) *     + Best possible debugging experience: error messages come with the exact file name and line number * * - Disadvantages: *     + Dependencies need to be specified before-hand * * ### Method 1: Explicitly include what you need: ### * *     // Syntax *     Ext.require({String/Array} expressions); * *     // Example: Single alias *     Ext.require('widget.window'); * *     // Example: Single class name *     Ext.require('Ext.window.Window'); * *     // Example: Multiple aliases / class names mix *     Ext.require(['widget.window', 'layout.border', 'Ext.data.Connection']); * *     // Wildcards *     Ext.require(['widget.*', 'layout.*', 'Ext.data.*']); * * ### Method 2: Explicitly exclude what you don't need: ### * *     // Syntax: Note that it must be in this chaining format. *     Ext.exclude({String/Array} expressions) *        .require({String/Array} expressions); * *     // Include everything except Ext.data.* *     Ext.exclude('Ext.data.*').require('*'); * *     // Include all widgets except widget.checkbox*, *     // which will match widget.checkbox, widget.checkboxfield, widget.checkboxgroup, etc. *     Ext.exclude('widget.checkbox*').require('widget.*'); * * # Synchronous Loading on Demand # * * - Advantages: *     + There's no need to specify dependencies before-hand, which is always the convenience of including ext-all.js *  before * * - Disadvantages: *     + Not as good debugging experience since file name won't be shown (except in Firebug at the moment) *     + Must be from the same domain due to XHR restriction *     + Need a web server, same reason as above * * There's one simple rule to follow: Instantiate everything with Ext.create instead of the `new` keyword * *     Ext.create('widget.window', { ... }); // Instead of new Ext.window.Window({...}); * *     Ext.create('Ext.window.Window', {}); // Same as above, using full class name instead of alias * *     Ext.widget('window', {}); // Same as above, all you need is the traditional `xtype` * * Behind the scene, {@link Ext.ClassManager} will automatically check whether the given class name / alias has already *  existed on the page. If it's not, Ext.Loader will immediately switch itself to synchronous mode and automatic load the given *  class and all its dependencies. * * # Hybrid Loading - The Best of Both Worlds # * * It has all the advantages combined from asynchronous and synchronous loading. The development flow is simple: * * ### Step 1: Start writing your application using synchronous approach. * * Ext.Loader will automatically fetch all dependencies on demand as they're needed during run-time. For example: * *     Ext.onReady(function(){ *         var window = Ext.widget('window', { *             width: 500, *             height: 300, *             layout: { *                 type: 'border', *                 padding: 5 *             }, *             title: 'Hello Dialog', *             items: [{ *                 title: 'Navigation', *                 collapsible: true, *                 region: 'west', *                 width: 200, *                 html: 'Hello', *                 split: true *             }, { *                 title: 'TabPanel', *                 region: 'center' *             }] *         }); * *         window.show(); *     }) * * ### Step 2: Along the way, when you need better debugging ability, watch the console for warnings like these: ### * *     [Ext.Loader] Synchronously loading 'Ext.window.Window'; consider adding Ext.require('Ext.window.Window') before your application's code *     ClassManager.js:432 *     [Ext.Loader] Synchronously loading 'Ext.layout.container.Border'; consider adding Ext.require('Ext.layout.container.Border') before your application's code * * Simply copy and paste the suggested code above `Ext.onReady`, i.e: * *     Ext.require('Ext.window.Window'); *     Ext.require('Ext.layout.container.Border'); * *     Ext.onReady(...); * * Everything should now load via asynchronous mode. * * # Deployment # * * It's important to note that dynamic loading should only be used during development on your local machines. * During production, all dependencies should be combined into one single JavaScript file. Ext.Loader makes * the whole process of transitioning from / to between development / maintenance and production as easy as * possible. Internally {@link Ext.Loader#history Ext.Loader.history} maintains the list of all dependencies your application * needs in the exact loading sequence. It's as simple as concatenating all files in this array into one, * then include it on top of your application. * * This process will be automated with Sencha Command, to be released and documented towards Ext JS 4 Final. * * @singleton */Ext.Loader = new function() {    var Loader = this,        Manager = Ext.ClassManager,        Class = Ext.Class,        flexSetter = Ext.Function.flexSetter,        alias = Ext.Function.alias,        pass = Ext.Function.pass,        defer = Ext.Function.defer,        arrayErase = Ext.Array.erase,        //<if nonBrowser>        isNonBrowser = typeof window == 'undefined',        isNodeJS = isNonBrowser && (typeof require == 'function'),        isJsdb = isNonBrowser && typeof system != 'undefined' && system.program.search(/jsdb/) !== -1,        isPhantomJS = (typeof phantom != 'undefined' && phantom.fs),        //</if>        dependencyProperties = ['extend', 'mixins', 'requires'],        isInHistory = {},        history = [],        slashDotSlashRe = /\/\.\//g,        dotRe = /\./g;    Ext.apply(Loader, {<span id='Ext-Loader-property-isInHistory'>        /**</span>         * @private         */        isInHistory: isInHistory,<span id='Ext-Loader-property-history'>        /**</span>         * An array of class names to keep track of the dependency loading order.         * This is not guaranteed to be the same everytime due to the asynchronous         * nature of the Loader.         *         * @property {Array} history         */        history: history,<span id='Ext-Loader-property-config'>        /**</span>         * Configuration         * @private         */        config: {<span id='Ext-Loader-cfg-enabled'>            /**</span>             * @cfg {Boolean} enabled             * Whether or not to enable the dynamic dependency loading feature.             */            enabled: false,<span id='Ext-Loader-cfg-scriptChainDelay'>            /**</span>             * @cfg {Boolean} scriptChainDelay             * millisecond delay between asynchronous script injection (prevents stack overflow on some user agents)             * 'false' disables delay but potentially increases stack load.             */            scriptChainDelay : false,<span id='Ext-Loader-cfg-disableCaching'>            /**</span>             * @cfg {Boolean} disableCaching             * Appends current timestamp to script files to prevent caching.             */            disableCaching: true,<span id='Ext-Loader-cfg-disableCachingParam'>            /**</span>             * @cfg {String} disableCachingParam             * The get parameter name for the cache buster's timestamp.             */            disableCachingParam: '_dc',<span id='Ext-Loader-cfg-garbageCollect'>            /**</span>             * @cfg {Boolean} garbageCollect             * True to prepare an asynchronous script tag for garbage collection (effective only             * if {@link #preserveScripts preserveScripts} is false)             */            garbageCollect : false,<span id='Ext-Loader-cfg-paths'>            /**</span>             * @cfg {Object} paths             * The mapping from namespaces to file paths             *             *     {             *         'Ext': '.', // This is set by default, Ext.layout.container.Container will be             *                     // loaded from ./layout/Container.js             *             *         'My': './src/my_own_folder' // My.layout.Container will be loaded from             *                                     // ./src/my_own_folder/layout/Container.js             *     }             *             * Note that all relative paths are relative to the current HTML document.             * If not being specified, for example, <code>Other.awesome.Class</code>             * will simply be loaded from <code>./Other/awesome/Class.js</code>             */            paths: {                'Ext': '.'            },<span id='Ext-Loader-cfg-preserveScripts'>            /**</span>             * @cfg {Boolean} preserveScripts             * False to remove and optionally {@link #garbageCollect garbage-collect} asynchronously loaded scripts,             * True to retain script element for browser debugger compatibility and improved load performance.             */            preserveScripts : true,<span id='Ext-Loader-cfg-scriptCharset'>            /**</span>             * @cfg {String} scriptCharset             * Optional charset to specify encoding of dynamic script content.             */            scriptCharset : undefined        },<span id='Ext-Loader-method-setConfig'>        /**</span>         * Set the configuration for the loader. This should be called right after ext-(debug).js         * is included in the page, and before Ext.onReady. i.e:         *         *     <script type="text/javascript" src="ext-core-debug.js"></script>         *     <script type="text/javascript">         *         Ext.Loader.setConfig({         *           enabled: true,         *           paths: {         *               'My': 'my_own_path'         *           }         *         });         *     </script>         *     <script type="text/javascript">         *         Ext.require(...);         *         *         Ext.onReady(function() {         *           // application code here         *         });         *     </script>         *         * Refer to config options of {@link Ext.Loader} for the list of possible properties         *         * @param {Object} config The config object to override the default values         * @return {Ext.Loader} this         */        setConfig: function(name, value) {            if (Ext.isObject(name) && arguments.length === 1) {                Ext.merge(Loader.config, name);            }            else {                Loader.config[name] = (Ext.isObject(value)) ? Ext.merge(Loader.config[name], value) : value;            }            return Loader;        },<span id='Ext-Loader-method-getConfig'>        /**</span>         * Get the config value corresponding to the specified name. If no name is given, will return the config object         * @param {String} name The config property name         * @return {Object}         */        getConfig: function(name) {            if (name) {                return Loader.config[name];            }            return Loader.config;        },<span id='Ext-Loader-method-setPath'>        /**</span>         * Sets the path of a namespace.         * For Example:         *         *     Ext.Loader.setPath('Ext', '.');         *         * @param {String/Object} name See {@link Ext.Function#flexSetter flexSetter}         * @param {String} path See {@link Ext.Function#flexSetter flexSetter}         * @return {Ext.Loader} this         * @method         */        setPath: flexSetter(function(name, path) {            Loader.config.paths[name] = path;            return Loader;        }),<span id='Ext-Loader-method-getPath'>        /**</span>         * Translates a className to a file path by adding the         * the proper prefix and converting the .'s to /'s. For example:         *         *     Ext.Loader.setPath('My', '/path/to/My');         *         *     alert(Ext.Loader.getPath('My.awesome.Class')); // alerts '/path/to/My/awesome/Class.js'         *         * Note that the deeper namespace levels, if explicitly set, are always resolved first. For example:         *         *     Ext.Loader.setPath({         *         'My': '/path/to/lib',         *         'My.awesome': '/other/path/for/awesome/stuff',         *         'My.awesome.more': '/more/awesome/path'         *     });         *         *     alert(Ext.Loader.getPath('My.awesome.Class')); // alerts '/other/path/for/awesome/stuff/Class.js'         *         *     alert(Ext.Loader.getPath('My.awesome.more.Class')); // alerts '/more/awesome/path/Class.js'         *         *     alert(Ext.Loader.getPath('My.cool.Class')); // alerts '/path/to/lib/cool/Class.js'         *         *     alert(Ext.Loader.getPath('Unknown.strange.Stuff')); // alerts 'Unknown/strange/Stuff.js'         *         * @param {String} className         * @return {String} path         */        getPath: function(className) {            var path = '',                paths = Loader.config.paths,                prefix = Loader.getPrefix(className);            if (prefix.length > 0) {                if (prefix === className) {                    return paths[prefix];                }                path = paths[prefix];                className = className.substring(prefix.length + 1);            }            if (path.length > 0) {                path += '/';            }            return path.replace(slashDotSlashRe, '/') + className.replace(dotRe, "/") + '.js';        },<span id='Ext-Loader-method-getPrefix'>        /**</span>         * @private         * @param {String} className         */        getPrefix: function(className) {            var paths = Loader.config.paths,                prefix, deepestPrefix = '';            if (paths.hasOwnProperty(className)) {                return className;            }            for (prefix in paths) {                if (paths.hasOwnProperty(prefix) && prefix + '.' === className.substring(0, prefix.length + 1)) {                    if (prefix.length > deepestPrefix.length) {                        deepestPrefix = prefix;                    }                }            }            return deepestPrefix;        },<span id='Ext-Loader-method-isAClassNameWithAKnownPrefix'>        /**</span>         * @private         * @param {String} className         */        isAClassNameWithAKnownPrefix: function(className) {            var prefix = Loader.getPrefix(className);            // we can only say it's really a class if className is not equal to any known namespace            return prefix !== '' && prefix !== className;        },<span id='Ext-Loader-method-require'>        /**</span>         * 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#require Ext.require} for convenience         * @param {String/Array} expressions Can either be a string or an array of string         * @param {Function} fn (Optional) The callback function         * @param {Object} scope (Optional) The execution scope (`this`) of the callback function         * @param {String/Array} excludes (Optional) Classes to be excluded, useful when being used with expressions         */        require: function(expressions, fn, scope, excludes) {            if (fn) {                fn.call(scope);            }        },<span id='Ext-Loader-method-syncRequire'>        /**</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         * @param {String/Array} expressions Can either be a string or an array of string         * @param {Function} fn (Optional) The callback function         * @param {Object} scope (Optional) The execution scope (`this`) of the callback function         * @param {String/Array} excludes (Optional) Classes to be excluded, useful when being used with expressions         */        syncRequire: function() {},<span id='Ext-Loader-method-exclude'>        /**</span>         * Explicitly exclude files from being loaded. Useful when used in conjunction with a broad include expression.         * Can be chained with more `require` and `exclude` methods, eg:         *         *     Ext.exclude('Ext.data.*').require('*');         *         *     Ext.exclude('widget.button*').require('widget.*');         *         * @param {Array} excludes         * @return {Object} object contains `require` method for chaining         */        exclude: function(excludes) {            return {                require: function(expressions, fn, scope) {                    return Loader.require(expressions, fn, scope, excludes);                },                syncRequire: function(expressions, fn, scope) {                    return Loader.syncRequire(expressions, fn, scope, excludes);                }            };        },<span id='Ext-Loader-method-onReady'>        /**</span>         * Add a new listener to be executed when all required scripts are fully loaded         *         * @param {Function} fn The function callback to be executed         * @param {Object} scope The execution scope (<code>this</code>) of the callback function         * @param {Boolean} withDomReady Whether or not to wait for document dom ready as well         */        onReady: function(fn, scope, withDomReady, options) {            var oldFn;            if (withDomReady !== false && Ext.onDocumentReady) {                oldFn = fn;                fn = function() {                    Ext.onDocumentReady(oldFn, scope, options);                };            }            fn.call(scope);        }    });    //<feature classSystem.loader>    var queue = [],        isClassFileLoaded = {},        isFileLoaded = {},        classNameToFilePathMap = {},        scriptElements = {},        readyListeners = [],        usedClasses = [],        requiresMap = {};    Ext.apply(Loader, {<span id='Ext-Loader-property-documentHead'>        /**</span>         * @private         */        documentHead: typeof document != 'undefined' && (document.head || document.getElementsByTagName('head')[0]),<span id='Ext-Loader-property-isLoading'>        /**</span>         * Flag indicating whether there are still files being loaded         * @private         */        isLoading: false,<span id='Ext-Loader-property-queue'>        /**</span>         * Maintain the queue for all dependencies. Each item in the array is an object of the format:         *         *     {         *          requires: [...], // The required classes for this queue item         *          callback: function() { ... } // The function to execute when all classes specified in requires exist         *     }         *         * @private         */        queue: queue,<span id='Ext-Loader-property-isClassFileLoaded'>        /**</span>         * Maintain the list of files that have already been handled so that they never get double-loaded         * @private         */        isClassFileLoaded: isClassFileLoaded,<span id='Ext-Loader-property-isFileLoaded'>        /**</span>         * @private         */        isFileLoaded: isFileLoaded,<span id='Ext-Loader-property-readyListeners'>        /**</span>         * Maintain the list of listeners to execute when all required scripts are fully loaded         * @private         */        readyListeners: readyListeners,<span id='Ext-Loader-property-optionalRequires'>        /**</span>         * Contains classes referenced in `uses` properties.         * @private         */        optionalRequires: usedClasses,<span id='Ext-Loader-property-requiresMap'>        /**</span>         * Map of fully qualified class names to an array of dependent classes.         * @private         */        requiresMap: requiresMap,<span id='Ext-Loader-property-numPendingFiles'>        /**</span>         * @private         */        numPendingFiles: 0,<span id='Ext-Loader-property-numLoadedFiles'>        /**</span>         * @private         */        numLoadedFiles: 0,<span id='Ext-Loader-property-hasFileLoadError'>        /** @private */</span>        hasFileLoadError: false,<span id='Ext-Loader-property-classNameToFilePathMap'>        /**</span>         * @private         */        classNameToFilePathMap: classNameToFilePathMap,<span id='Ext-Loader-property-scriptsLoading'>        /**</span>         * The number of scripts loading via loadScript.         * @private         */        scriptsLoading: 0,<span id='Ext-Loader-property-syncModeEnabled'>        /**</span>         * @private         */        syncModeEnabled: false,        scriptElements: scriptElements,<span id='Ext-Loader-method-refreshQueue'>        /**</span>         * Refresh all items in the queue. If all dependencies for an item exist during looping,         * it will execute the callback and call refreshQueue again. Triggers onReady when the queue is         * empty         * @private         */        refreshQueue: function() {            var ln = queue.length,                i, item, j, requires;            // When the queue of loading classes reaches zero, trigger readiness            if (!ln && !Loader.scriptsLoading) {                return Loader.triggerReady();            }            for (i = 0; i < ln; i++) {                item = queue[i];                if (item) {                    requires = item.requires;                    // Don't bother checking when the number of files loaded                    // is still less than the array length                    if (requires.length > Loader.numLoadedFiles) {                        continue;                    }                    // Remove any required classes that are loaded                    for (j = 0; j < requires.length; ) {                        if (Manager.isCreated(requires[j])) {                            // Take out from the queue                            arrayErase(requires, j, 1);                        }                        else {                            j++;                        }                    }                    // If we've ended up with no required classes, call the callback                    if (item.requires.length === 0) {                        arrayErase(queue, i, 1);                        item.callback.call(item.scope);                        Loader.refreshQueue();                        break;                    }                }            }            return Loader;        },<span id='Ext-Loader-method-injectScriptElement'>        /**</span>         * Inject a script element to document's head, call onLoad and onError accordingly         * @private         */        injectScriptElement: function(url, onLoad, onError, scope, charset) {            var script = document.createElement('script'),                dispatched = false,                config = Loader.config,                onLoadFn = function() {                    if(!dispatched) {                        dispatched = true;                        script.onload = script.onreadystatechange = script.onerror = null;                        if (typeof config.scriptChainDelay == 'number') {                            //free the stack (and defer the next script)                            defer(onLoad, config.scriptChainDelay, scope);                        } else {                            onLoad.call(scope);                        }                        Loader.cleanupScriptElement(script, config.preserveScripts === false, config.garbageCollect);                    }                },                onErrorFn = function(arg) {                    defer(onError, 1, scope);   //free the stack                    Loader.cleanupScriptElement(script, config.preserveScripts === false, config.garbageCollect);                };            script.type = 'text/javascript';            script.onerror = onErrorFn;            charset = charset || config.scriptCharset;            if (charset) {                script.charset = charset;            }            /*             * IE9 Standards mode (and others) SHOULD follow the load event only             * (Note: IE9 supports both onload AND readystatechange events)             */            if ('addEventListener' in script ) {                script.onload = onLoadFn;            } else if ('readyState' in script) {   // for <IE9 Compatability                script.onreadystatechange = function() {                    if ( this.readyState == 'loaded' || this.readyState == 'complete' ) {                        onLoadFn();                    }                };            } else {                 script.onload = onLoadFn;            }            script.src = url;            (Loader.documentHead || document.getElementsByTagName('head')[0]).appendChild(script);            return script;        },<span id='Ext-Loader-method-removeScriptElement'>        /**</span>         * @private         */        removeScriptElement: function(url) {            if (scriptElements[url]) {                Loader.cleanupScriptElement(scriptElements[url], true, !!Loader.getConfig('garbageCollect'));                delete scriptElements[url];            }            return Loader;        },<span id='Ext-Loader-method-cleanupScriptElement'>        /**</span>         * @private         */        cleanupScriptElement: function(script, remove, collect) {            var prop;            script.onload = script.onreadystatechange = script.onerror = null;            if (remove) {                Ext.removeNode(script);       // Remove, since its useless now                if (collect) {                    for (prop in script) {                        try {                            script[prop] = null;                            delete script[prop];      // and prepare for GC                        } catch (cleanEx) {                            //ignore                        }                    }                }            }            return Loader;        },<span id='Ext-Loader-method-loadScript'>        /**</span>         * Loads the specified script URL and calls the supplied callbacks. If this method         * is called before {@link Ext#isReady}, the script's load will delay the transition         * to ready. This can be used to load arbitrary scripts that may contain further         * {@link Ext#require Ext.require} calls.         *         * @param {Object/String} options The options object or simply the URL to load.         * @param {String} options.url The URL from which to load the script.         * @param {Function} [options.onLoad] The callback to call on successful load.         * @param {Function} [options.onError] The callback to call on failure to load.         * @param {Object} [options.scope] The scope (`this`) for the supplied callbacks.         */        loadScript: function (options) {            var config = Loader.getConfig(),                isString = typeof options == 'string',                url = isString ? options : options.url,                onError = !isString && options.onError,                onLoad = !isString && options.onLoad,                scope = !isString && options.scope,                onScriptError = function() {                    Loader.numPendingFiles--;                    Loader.scriptsLoading--;                    if (onError) {                        onError.call(scope, "Failed loading '" + url + "', please verify that the file exists");                    }                    if (Loader.numPendingFiles + Loader.scriptsLoading === 0) {                        Loader.refreshQueue();                    }                },                onScriptLoad = function () {                    Loader.numPendingFiles--;                    Loader.scriptsLoading--;                    if (onLoad) {                        onLoad.call(scope);                    }                    if (Loader.numPendingFiles + Loader.scriptsLoading === 0) {                        Loader.refreshQueue();                    }                },                src;            Loader.isLoading = true;            Loader.numPendingFiles++;            Loader.scriptsLoading++;            src = config.disableCaching ?                (url + '?' + config.disableCachingParam + '=' + Ext.Date.now()) : url;            scriptElements[url] = Loader.injectScriptElement(src, onScriptLoad, onScriptError);        },<span id='Ext-Loader-method-loadScriptFile'>        /**</span>         * Load a script file, supports both asynchronous and synchronous approaches         * @private         */        loadScriptFile: function(url, onLoad, onError, scope, synchronous) {            if (isFileLoaded[url]) {                return Loader;            }            var config = Loader.getConfig(),                noCacheUrl = url + (config.disableCaching ? ('?' + config.disableCachingParam + '=' + Ext.Date.now()) : ''),                isCrossOriginRestricted = false,                xhr, status, onScriptError,                debugSourceURL = "";            scope = scope || Loader;            Loader.isLoading = true;            if (!synchronous) {                onScriptError = function() {                    //<debug error>                    onError.call(scope, "Failed loading '" + url + "', please verify that the file exists", synchronous);                    //</debug>                };                scriptElements[url] = Loader.injectScriptElement(noCacheUrl, onLoad, onScriptError, scope);            } else {                if (typeof XMLHttpRequest != 'undefined') {                    xhr = new XMLHttpRequest();                } else {                    xhr = new ActiveXObject('Microsoft.XMLHTTP');                }                try {                    xhr.open('GET', noCacheUrl, false);                    xhr.send(null);                } catch (e) {                    isCrossOriginRestricted = true;                }                status = (xhr.status === 1223) ? 204 :                    (xhr.status === 0 && (self.location || {}).protocol == 'file:') ? 200 : xhr.status;                isCrossOriginRestricted = isCrossOriginRestricted || (status === 0);                if (isCrossOriginRestricted                    //<if isNonBrowser>                    && !isPhantomJS                    //</if>                ) {                    //<debug error>                    onError.call(Loader, "Failed loading synchronously via XHR: '" + url + "'; It's likely that the file is either " +                                       "being loaded from a different domain or from the local file system whereby cross origin " +                                       "requests are not allowed due to security reasons. Use asynchronous loading with " +                                       "Ext.require instead.", synchronous);                    //</debug>                }                else if ((status >= 200 && status < 300) || (status === 304)                    //<if isNonBrowser>                    || isPhantomJS                    //</if>                ) {                    // Debugger friendly, file names are still shown even though they're eval'ed code                    // Breakpoints work on both Firebug and Chrome's Web Inspector                    if (!Ext.isIE) {                        debugSourceURL = "\n//@ sourceURL=" + url;                    }                    Ext.globalEval(xhr.responseText + debugSourceURL);                    onLoad.call(scope);                }                else {                    //<debug>                    onError.call(Loader, "Failed loading synchronously via XHR: '" + url + "'; please " +                                       "verify that the file exists. " +                                       "XHR status code: " + status, synchronous);                    //</debug>                }                // Prevent potential IE memory leak                xhr = null;            }        },        // documented above        syncRequire: function() {            var syncModeEnabled = Loader.syncModeEnabled;            if (!syncModeEnabled) {                Loader.syncModeEnabled = true;            }            Loader.require.apply(Loader, arguments);            if (!syncModeEnabled) {                Loader.syncModeEnabled = false;            }            Loader.refreshQueue();        },        // documented above        require: function(expressions, fn, scope, excludes) {            var excluded = {},                included = {},                excludedClassNames = [],                possibleClassNames = [],                classNames = [],                references = [],                callback,                syncModeEnabled,                filePath, expression, exclude, className,                possibleClassName, i, j, ln, subLn;            if (excludes) {                // Convert possible single string to an array.                excludes = (typeof excludes === 'string') ? [ excludes ] : excludes;                for (i = 0,ln = excludes.length; i < ln; i++) {                    exclude = excludes[i];                    if (typeof exclude == 'string' && exclude.length > 0) {                        excludedClassNames = Manager.getNamesByExpression(exclude);                        for (j = 0,subLn = excludedClassNames.length; j < subLn; j++) {                            excluded[excludedClassNames[j]] = true;                        }                    }                }            }            // Convert possible single string to an array.            expressions = (typeof expressions === 'string') ? [ expressions ] : (expressions ? expressions : []);            if (fn) {                if (fn.length > 0) {                    callback = function() {                        var classes = [],                            i, ln;                        for (i = 0,ln = references.length; i < ln; i++) {                            classes.push(Manager.get(references[i]));                        }                        return fn.apply(this, classes);                    };                }                else {                    callback = fn;                }            }            else {                callback = Ext.emptyFn;            }            scope = scope || Ext.global;            for (i = 0,ln = expressions.length; i < ln; i++) {                expression = expressions[i];                if (typeof expression == 'string' && expression.length > 0) {                    possibleClassNames = Manager.getNamesByExpression(expression);                    subLn = possibleClassNames.length;                    for (j = 0; j < subLn; j++) {                        possibleClassName = possibleClassNames[j];                        if (excluded[possibleClassName] !== true) {                            references.push(possibleClassName);                            if (!Manager.isCreated(possibleClassName) && !included[possibleClassName]) {                                included[possibleClassName] = true;                                classNames.push(possibleClassName);                            }                        }                    }                }            }            // If the dynamic dependency feature is not being used, throw an error            // if the dependencies are not defined            if (classNames.length > 0) {                if (!Loader.config.enabled) {                    throw new Error("Ext.Loader is not enabled, so dependencies cannot be resolved dynamically. " +                             "Missing required class" + ((classNames.length > 1) ? "es" : "") + ": " + classNames.join(', '));                }            }            else {                callback.call(scope);                return Loader;            }            syncModeEnabled = Loader.syncModeEnabled;            if (!syncModeEnabled) {                queue.push({                    requires: classNames.slice(), // this array will be modified as the queue is processed,                                                  // so we need a copy of it                    callback: callback,                    scope: scope                });            }            ln = classNames.length;            for (i = 0; i < ln; i++) {                className = classNames[i];                filePath = Loader.getPath(className);                // If we are synchronously loading a file that has already been asychronously loaded before                // we need to destroy the script tag and revert the count                // This file will then be forced loaded in synchronous                if (syncModeEnabled && isClassFileLoaded.hasOwnProperty(className)) {                    Loader.numPendingFiles--;                    Loader.removeScriptElement(filePath);                    delete isClassFileLoaded[className];                }                if (!isClassFileLoaded.hasOwnProperty(className)) {                    isClassFileLoaded[className] = false;                    classNameToFilePathMap[className] = filePath;                    Loader.numPendingFiles++;                    Loader.loadScriptFile(                        filePath,                        pass(Loader.onFileLoaded, [className, filePath], Loader),                        pass(Loader.onFileLoadError, [className, filePath], Loader),                        Loader,                        syncModeEnabled                    );                }            }            if (syncModeEnabled) {                callback.call(scope);                if (ln === 1) {                    return Manager.get(className);                }            }            return Loader;        },<span id='Ext-Loader-method-onFileLoaded'>        /**</span>         * @private         * @param {String} className         * @param {String} filePath         */        onFileLoaded: function(className, filePath) {            Loader.numLoadedFiles++;            isClassFileLoaded[className] = true;            isFileLoaded[filePath] = true;            Loader.numPendingFiles--;            if (Loader.numPendingFiles === 0) {                Loader.refreshQueue();            }            //<debug>            if (!Loader.syncModeEnabled && Loader.numPendingFiles === 0 && Loader.isLoading && !Loader.hasFileLoadError) {                var missingClasses = [],                    missingPaths = [],                    requires,                    i, ln, j, subLn;                for (i = 0,ln = queue.length; i < ln; i++) {                    requires = queue[i].requires;                    for (j = 0,subLn = requires.length; j < subLn; j++) {                        if (isClassFileLoaded[requires[j]]) {                            missingClasses.push(requires[j]);                        }                    }                }                if (missingClasses.length < 1) {                    return;                }                missingClasses = Ext.Array.filter(Ext.Array.unique(missingClasses), function(item) {                    return !requiresMap.hasOwnProperty(item);                }, Loader);                for (i = 0,ln = missingClasses.length; i < ln; i++) {                    missingPaths.push(classNameToFilePathMap[missingClasses[i]]);                }                throw new Error("The following classes are not declared even if their files have been " +                    "loaded: '" + missingClasses.join("', '") + "'. Please check the source code of their " +                    "corresponding files for possible typos: '" + missingPaths.join("', '"));            }            //</debug>        },<span id='Ext-Loader-method-onFileLoadError'>        /**</span>         * @private         */        onFileLoadError: function(className, filePath, errorMessage, isSynchronous) {            Loader.numPendingFiles--;            Loader.hasFileLoadError = true;            //<debug error>            throw new Error("[Ext.Loader] " + errorMessage);            //</debug>        },<span id='Ext-Loader-method-addUsedClasses'>        /**</span>         * @private         * Ensure that any classes referenced in the `uses` property are loaded.         */        addUsedClasses: function (classes) {            var cls, i, ln;            if (classes) {                classes = (typeof classes == 'string') ? [classes] : classes;                for (i = 0, ln = classes.length; i < ln; i++) {                    cls = classes[i];                    if (typeof cls == 'string' && !Ext.Array.contains(usedClasses, cls)) {                        usedClasses.push(cls);                    }                }            }            return Loader;        },<span id='Ext-Loader-method-triggerReady'>        /**</span>         * @private         */        triggerReady: function() {            var listener,                i, refClasses = usedClasses;            if (Loader.isLoading) {                Loader.isLoading = false;                if (refClasses.length !== 0) {                    // Clone then empty the array to eliminate potential recursive loop issue                    refClasses = refClasses.slice();                    usedClasses.length = 0;                    // this may immediately call us back if all 'uses' classes                    // have been loaded                    Loader.require(refClasses, Loader.triggerReady, Loader);                    return Loader;                }            }            // this method can be called with Loader.isLoading either true or false            // (can be called with false when all 'uses' classes are already loaded)            // this may bypass the above if condition            while (readyListeners.length && !Loader.isLoading) {                // calls to refreshQueue may re-enter triggerReady                // so we cannot necessarily iterate the readyListeners array                listener = readyListeners.shift();                listener.fn.call(listener.scope);            }            return Loader;        },        // Documented above already        onReady: function(fn, scope, withDomReady, options) {            var oldFn;            if (withDomReady !== false && Ext.onDocumentReady) {                oldFn = fn;                fn = function() {                    Ext.onDocumentReady(oldFn, scope, options);                };            }            if (!Loader.isLoading) {                fn.call(scope);            }            else {                readyListeners.push({                    fn: fn,                    scope: scope                });            }        },<span id='Ext-Loader-method-historyPush'>        /**</span>         * @private         * @param {String} className         */        historyPush: function(className) {            if (className && isClassFileLoaded.hasOwnProperty(className) && !isInHistory[className]) {                isInHistory[className] = true;                history.push(className);            }            return Loader;        }    });<span id='Ext-Loader-method-disableCacheBuster'>    /**</span>     * Turns on or off the "cache buster" applied to dynamically loaded scripts. Normally     * dynamically loaded scripts have an extra query parameter appended to avoid stale     * cached scripts. This method can be used to disable this mechanism, and is primarily     * useful for testing. This is done using a cookie.     * @param {Boolean} disable True to disable the cache buster.     * @param {String} [path="/"] An optional path to scope the cookie.     * @private     */    Ext.disableCacheBuster = function (disable, path) {        var date = new Date();        date.setTime(date.getTime() + (disable ? 10*365 : -1) * 24*60*60*1000);        date = date.toGMTString();        document.cookie = 'ext-cache=1; expires=' + date + '; path='+(path || '/');    };    //<if nonBrowser>    if (isNonBrowser) {        if (isNodeJS) {            Ext.apply(Loader, {                syncModeEnabled: true,                setPath: flexSetter(function(name, path) {                    path = require('fs').realpathSync(path);                    Loader.config.paths[name] = path;                    return Loader;                }),                loadScriptFile: function(filePath, onLoad, onError, scope, synchronous) {                    require(filePath);                    onLoad.call(scope);                }            });        }        else if (isJsdb) {            Ext.apply(Loader, {                syncModeEnabled: true,                loadScriptFile: function(filePath, onLoad, onError, scope, synchronous) {                    load(filePath);                    onLoad.call(scope);                }            });        }    }    //</if>    //</feature><span id='Ext-method-require'>    /**</span>     * Convenient alias of {@link Ext.Loader#require}. Please see the introduction documentation of     * {@link Ext.Loader} for examples.     * @member Ext     * @method require     */    Ext.require = alias(Loader, 'require');<span id='Ext-method-syncRequire'>    /**</span>     * Synchronous version of {@link Ext#require}, convenient alias of {@link Ext.Loader#syncRequire}.     *     * @member Ext     * @method syncRequire     */    Ext.syncRequire = alias(Loader, 'syncRequire');<span id='Ext-method-exclude'>    /**</span>     * Convenient shortcut to {@link Ext.Loader#exclude}     * @member Ext     * @method exclude     */    Ext.exclude = alias(Loader, 'exclude');<span id='Ext-method-onReady'>    /**</span>     * @member Ext     * @method onReady     * @ignore     */    Ext.onReady = function(fn, scope, options) {        Loader.onReady(fn, scope, true, options);    };<span id='Ext-Class-cfg-requires'>    /**</span>     * @cfg {String[]} requires     * @member Ext.Class     * List of classes that have to be loaded before instantiating this class.     * For example:     *     *     Ext.define('Mother', {     *         requires: ['Child'],     *         giveBirth: function() {     *             // we can be sure that child class is available.     *             return new Child();     *         }     *     });     */    Class.registerPreprocessor('loader', function(cls, data, hooks, continueFn) {        var me = this,            dependencies = [],            dependency,            className = Manager.getName(cls),            i, j, ln, subLn, value, propertyName, propertyValue,            requiredMap, requiredDep;        /*        Loop through the dependencyProperties, look for string class names and push        them into a stack, regardless of whether the property's value is a string, array or object. For example:        {              extend: 'Ext.MyClass',              requires: ['Ext.some.OtherClass'],              mixins: {                  observable: 'Ext.util.Observable';              }        }        which will later be transformed into:        {              extend: Ext.MyClass,              requires: [Ext.some.OtherClass],              mixins: {                  observable: Ext.util.Observable;              }        }        */        for (i = 0,ln = dependencyProperties.length; i < ln; i++) {            propertyName = dependencyProperties[i];            if (data.hasOwnProperty(propertyName)) {                propertyValue = data[propertyName];                if (typeof propertyValue == 'string') {                    dependencies.push(propertyValue);                }                else if (propertyValue instanceof Array) {                    for (j = 0, subLn = propertyValue.length; j < subLn; j++) {                        value = propertyValue[j];                        if (typeof value == 'string') {                            dependencies.push(value);                        }                    }                }                else if (typeof propertyValue != 'function') {                    for (j in propertyValue) {                        if (propertyValue.hasOwnProperty(j)) {                            value = propertyValue[j];                            if (typeof value == 'string') {                                dependencies.push(value);                            }                        }                    }                }            }        }        if (dependencies.length === 0) {            return;        }        //<feature classSystem.loader>        //<debug error>        var deadlockPath = [],            detectDeadlock;        /*        Automatically detect deadlocks before-hand,        will throw an error with detailed path for ease of debugging. Examples of deadlock cases:        - A extends B, then B extends A        - A requires B, B requires C, then C requires A        The detectDeadlock function will recursively transverse till the leaf, hence it can detect deadlocks        no matter how deep the path is.        */        if (className) {            requiresMap[className] = dependencies;            //<debug>            requiredMap = Loader.requiredByMap || (Loader.requiredByMap = {});            for (i = 0,ln = dependencies.length; i < ln; i++) {                dependency = dependencies[i];                (requiredMap[dependency] || (requiredMap[dependency] = [])).push(className);            }            //</debug>            detectDeadlock = function(cls) {                deadlockPath.push(cls);                if (requiresMap[cls]) {                    if (Ext.Array.contains(requiresMap[cls], className)) {                        throw new Error("Deadlock detected while loading dependencies! '" + className + "' and '" +                                deadlockPath[1] + "' " + "mutually require each other. Path: " +                                deadlockPath.join(' -> ') + " -> " + deadlockPath[0]);                    }                    for (i = 0,ln = requiresMap[cls].length; i < ln; i++) {                        detectDeadlock(requiresMap[cls][i]);                    }                }            };            detectDeadlock(className);        }        //</debug>        //</feature>        Loader.require(dependencies, function() {            for (i = 0,ln = dependencyProperties.length; i < ln; i++) {                propertyName = dependencyProperties[i];                if (data.hasOwnProperty(propertyName)) {                    propertyValue = data[propertyName];                    if (typeof propertyValue == 'string') {                        data[propertyName] = Manager.get(propertyValue);                    }                    else if (propertyValue instanceof Array) {                        for (j = 0, subLn = propertyValue.length; j < subLn; j++) {                            value = propertyValue[j];                            if (typeof value == 'string') {                                data[propertyName][j] = Manager.get(value);                            }                        }                    }                    else if (typeof propertyValue != 'function') {                        for (var k in propertyValue) {                            if (propertyValue.hasOwnProperty(k)) {                                value = propertyValue[k];                                if (typeof value == 'string') {                                    data[propertyName][k] = Manager.get(value);                                }                            }                        }                    }                }            }            continueFn.call(me, cls, data, hooks);        });        return false;    }, true, 'after', 'className');    //<feature classSystem.loader><span id='Ext-Class-cfg-uses'>    /**</span>     * @cfg {String[]} uses     * @member Ext.Class     * List of optional classes to load together with this class. These aren't neccessarily loaded before     * this class is created, but are guaranteed to be available before Ext.onReady listeners are     * invoked. For example:     *     *     Ext.define('Mother', {     *         uses: ['Child'],     *         giveBirth: function() {     *             // This code might, or might not work:     *             // return new Child();     *     *             // Instead use Ext.create() to load the class at the spot if not loaded already:     *             return Ext.create('Child');     *         }     *     });     */    Manager.registerPostprocessor('uses', function(name, cls, data) {        var uses = data.uses;        if (uses) {            Loader.addUsedClasses(uses);        }    });    Manager.onCreated(Loader.historyPush);    //</feature>};</pre></body></html>
 |