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>
|