Upgrade to ExtJS 4.0.0 - Released 04/26/2011
[extjs.git] / pkgs / foundation.js
1 /*
2 Ext JS - JavaScript Library
3 Copyright (c) 2006-2011, Sencha Inc.
4 All rights reserved.
5 licensing@sencha.com
6 */
7 /**
8  * @class Ext
9  * @singleton
10  */
11 (function() {
12     var global = this,
13         objectPrototype = Object.prototype,
14         toString = Object.prototype.toString,
15         enumerables = true,
16         enumerablesTest = { toString: 1 },
17         i;
18
19     if (typeof Ext === 'undefined') {
20         global.Ext = {};
21     }
22
23     Ext.global = global;
24
25     for (i in enumerablesTest) {
26         enumerables = null;
27     }
28
29     if (enumerables) {
30         enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable',
31                        'toLocaleString', 'toString', 'constructor'];
32     }
33
34     /**
35      * An array containing extra enumerables for old browsers
36      * @type Array
37      */
38     Ext.enumerables = enumerables;
39
40     /**
41      * Copies all the properties of config to the specified object.
42      * Note that if recursive merging and cloning without referencing the original objects / arrays is needed, use
43      * {@link Ext.Object#merge} instead.
44      * @param {Object} object The receiver of the properties
45      * @param {Object} config The source of the properties
46      * @param {Object} defaults A different object that will also be applied for default values
47      * @return {Object} returns obj
48      */
49     Ext.apply = function(object, config, defaults) {
50         if (defaults) {
51             Ext.apply(object, defaults);
52         }
53
54         if (object && config && typeof config === 'object') {
55             var i, j, k;
56
57             for (i in config) {
58                 object[i] = config[i];
59             }
60
61             if (enumerables) {
62                 for (j = enumerables.length; j--;) {
63                     k = enumerables[j];
64                     if (config.hasOwnProperty(k)) {
65                         object[k] = config[k];
66                     }
67                 }
68             }
69         }
70
71         return object;
72     };
73
74     Ext.buildSettings = Ext.apply({
75         baseCSSPrefix: 'x-',
76         scopeResetCSS: false
77     }, Ext.buildSettings || {});
78
79     Ext.apply(Ext, {
80         /**
81          * A reusable empty function
82          */
83         emptyFn: function() {},
84
85         baseCSSPrefix: Ext.buildSettings.baseCSSPrefix,
86
87         /**
88          * Copies all the properties of config to object if they don't already exist.
89          * @function
90          * @param {Object} object The receiver of the properties
91          * @param {Object} config The source of the properties
92          * @return {Object} returns obj
93          */
94         applyIf: function(object, config) {
95             var property;
96
97             if (object) {
98                 for (property in config) {
99                     if (object[property] === undefined) {
100                         object[property] = config[property];
101                     }
102                 }
103             }
104
105             return object;
106         },
107
108         /**
109          * Iterates either an array or an object. This method delegates to
110          * {@link Ext.Array#each Ext.Array.each} if the given value is iterable, and {@link Ext.Object#each Ext.Object.each} otherwise.
111          *
112          * @param {Object/Array} object The object or array to be iterated.
113          * @param {Function} fn The function to be called for each iteration. See and {@link Ext.Array#each Ext.Array.each} and
114          * {@link Ext.Object#each Ext.Object.each} for detailed lists of arguments passed to this function depending on the given object
115          * type that is being iterated.
116          * @param {Object} scope (Optional) The scope (`this` reference) in which the specified function is executed.
117          * Defaults to the object being iterated itself.
118          * @markdown
119          */
120         iterate: function(object, fn, scope) {
121             if (Ext.isEmpty(object)) {
122                 return;
123             }
124
125             if (scope === undefined) {
126                 scope = object;
127             }
128
129             if (Ext.isIterable(object)) {
130                 Ext.Array.each.call(Ext.Array, object, fn, scope);
131             }
132             else {
133                 Ext.Object.each.call(Ext.Object, object, fn, scope);
134             }
135         }
136     });
137
138     Ext.apply(Ext, {
139
140         /**
141          * This method deprecated. Use {@link Ext#define Ext.define} instead.
142          * @function
143          * @param {Function} superclass
144          * @param {Object} overrides
145          * @return {Function} The subclass constructor from the <tt>overrides</tt> parameter, or a generated one if not provided.
146          * @deprecated 4.0.0 Use {@link Ext#define Ext.define} instead
147          */
148         extend: function() {
149             // inline overrides
150             var objectConstructor = objectPrototype.constructor,
151                 inlineOverrides = function(o) {
152                 for (var m in o) {
153                     if (!o.hasOwnProperty(m)) {
154                         continue;
155                     }
156                     this[m] = o[m];
157                 }
158             };
159
160             return function(subclass, superclass, overrides) {
161                 // First we check if the user passed in just the superClass with overrides
162                 if (Ext.isObject(superclass)) {
163                     overrides = superclass;
164                     superclass = subclass;
165                     subclass = overrides.constructor !== objectConstructor ? overrides.constructor : function() {
166                         superclass.apply(this, arguments);
167                     };
168                 }
169
170                 //<debug>
171                 if (!superclass) {
172                     Ext.Error.raise({
173                         sourceClass: 'Ext',
174                         sourceMethod: 'extend',
175                         msg: 'Attempting to extend from a class which has not been loaded on the page.'
176                     });
177                 }
178                 //</debug>
179
180                 // We create a new temporary class
181                 var F = function() {},
182                     subclassProto, superclassProto = superclass.prototype;
183
184                 F.prototype = superclassProto;
185                 subclassProto = subclass.prototype = new F();
186                 subclassProto.constructor = subclass;
187                 subclass.superclass = superclassProto;
188
189                 if (superclassProto.constructor === objectConstructor) {
190                     superclassProto.constructor = superclass;
191                 }
192
193                 subclass.override = function(overrides) {
194                     Ext.override(subclass, overrides);
195                 };
196
197                 subclassProto.override = inlineOverrides;
198                 subclassProto.proto = subclassProto;
199
200                 subclass.override(overrides);
201                 subclass.extend = function(o) {
202                     return Ext.extend(subclass, o);
203                 };
204
205                 return subclass;
206             };
207         }(),
208
209         /**
210          * Proxy to {@link Ext.Base#override}. Please refer {@link Ext.Base#override} for further details.
211
212     Ext.define('My.cool.Class', {
213         sayHi: function() {
214             alert('Hi!');
215         }
216     }
217
218     Ext.override(My.cool.Class, {
219         sayHi: function() {
220             alert('About to say...');
221
222             this.callOverridden();
223         }
224     });
225
226     var cool = new My.cool.Class();
227     cool.sayHi(); // alerts 'About to say...'
228                   // alerts 'Hi!'
229
230          * Please note that `this.callOverridden()` only works if the class was previously
231          * created with {@link Ext#define)
232          *
233          * @param {Object} cls The class to override
234          * @param {Object} overrides The list of functions to add to origClass. This should be specified as an object literal
235          * containing one or more methods.
236          * @method override
237          * @markdown
238          */
239         override: function(cls, overrides) {
240             if (cls.prototype.$className) {
241                 return cls.override(overrides);
242             }
243             else {
244                 Ext.apply(cls.prototype, overrides);
245             }
246         }
247     });
248
249     // A full set of static methods to do type checking
250     Ext.apply(Ext, {
251
252         /**
253          * Returns the given value itself if it's not empty, as described in {@link Ext#isEmpty}; returns the default
254          * value (second argument) otherwise.
255          *
256          * @param {Mixed} value The value to test
257          * @param {Mixed} defaultValue The value to return if the original value is empty
258          * @param {Boolean} allowBlank (optional) true to allow zero length strings to qualify as non-empty (defaults to false)
259          * @return {Mixed} value, if non-empty, else defaultValue
260          */
261         valueFrom: function(value, defaultValue, allowBlank){
262             return Ext.isEmpty(value, allowBlank) ? defaultValue : value;
263         },
264
265         /**
266          * Returns the type of the given variable in string format. List of possible values are:
267          *
268          * - `undefined`: If the given value is `undefined`
269          * - `null`: If the given value is `null`
270          * - `string`: If the given value is a string
271          * - `number`: If the given value is a number
272          * - `boolean`: If the given value is a boolean value
273          * - `date`: If the given value is a `Date` object
274          * - `function`: If the given value is a function reference
275          * - `object`: If the given value is an object
276          * - `array`: If the given value is an array
277          * - `regexp`: If the given value is a regular expression
278          * - `element`: If the given value is a DOM Element
279          * - `textnode`: If the given value is a DOM text node and contains something other than whitespace
280          * - `whitespace`: If the given value is a DOM text node and contains only whitespace
281          *
282          * @param {Mixed} value
283          * @return {String}
284          * @markdown
285          */
286         typeOf: function(value) {
287             if (value === null) {
288                 return 'null';
289             }
290
291             var type = typeof value;
292
293             if (type === 'undefined' || type === 'string' || type === 'number' || type === 'boolean') {
294                 return type;
295             }
296
297             var typeToString = toString.call(value);
298
299             switch(typeToString) {
300                 case '[object Array]':
301                     return 'array';
302                 case '[object Date]':
303                     return 'date';
304                 case '[object Boolean]':
305                     return 'boolean';
306                 case '[object Number]':
307                     return 'number';
308                 case '[object RegExp]':
309                     return 'regexp';
310             }
311
312             if (type === 'function') {
313                 return 'function';
314             }
315
316             if (type === 'object') {
317                 if (value.nodeType !== undefined) {
318                     if (value.nodeType === 3) {
319                         return (/\S/).test(value.nodeValue) ? 'textnode' : 'whitespace';
320                     }
321                     else {
322                         return 'element';
323                     }
324                 }
325
326                 return 'object';
327             }
328
329             //<debug error>
330             Ext.Error.raise({
331                 sourceClass: 'Ext',
332                 sourceMethod: 'typeOf',
333                 msg: 'Failed to determine the type of the specified value "' + value + '". This is most likely a bug.'
334             });
335             //</debug>
336         },
337
338         /**
339          * Returns true if the passed value is empty, false otherwise. The value is deemed to be empty if it is either:
340          *
341          * - `null`
342          * - `undefined`
343          * - a zero-length array
344          * - a zero-length string (Unless the `allowEmptyString` parameter is set to `true`)
345          *
346          * @param {Mixed} value The value to test
347          * @param {Boolean} allowEmptyString (optional) true to allow empty strings (defaults to false)
348          * @return {Boolean}
349          * @markdown
350          */
351         isEmpty: function(value, allowEmptyString) {
352             return (value === null) || (value === undefined) || (!allowEmptyString ? value === '' : false) || (Ext.isArray(value) && value.length === 0);
353         },
354
355         /**
356          * Returns true if the passed value is a JavaScript Array, false otherwise.
357          *
358          * @param {Mixed} target The target to test
359          * @return {Boolean}
360          */
361         isArray: ('isArray' in Array) ? Array.isArray : function(value) {
362             return toString.call(value) === '[object Array]';
363         },
364
365         /**
366          * Returns true if the passed value is a JavaScript Date object, false otherwise.
367          * @param {Object} object The object to test
368          * @return {Boolean}
369          */
370         isDate: function(value) {
371             return toString.call(value) === '[object Date]';
372         },
373
374         /**
375          * Returns true if the passed value is a JavaScript Object, false otherwise.
376          * @param {Mixed} value The value to test
377          * @return {Boolean}
378          */
379         isObject: (toString.call(null) === '[object Object]') ?
380         function(value) {
381             return value !== null && value !== undefined && toString.call(value) === '[object Object]' && value.nodeType === undefined;
382         } :
383         function(value) {
384             return toString.call(value) === '[object Object]';
385         },
386
387         /**
388          * Returns true if the passed value is a JavaScript 'primitive', a string, number or boolean.
389          * @param {Mixed} value The value to test
390          * @return {Boolean}
391          */
392         isPrimitive: function(value) {
393             var type = typeof value;
394
395             return type === 'string' || type === 'number' || type === 'boolean';
396         },
397
398         /**
399          * Returns true if the passed value is a JavaScript Function, false otherwise.
400          * @param {Mixed} value The value to test
401          * @return {Boolean}
402          */
403         isFunction:
404         // Safari 3.x and 4.x returns 'function' for typeof <NodeList>, hence we need to fall back to using
405         // Object.prorotype.toString (slower)
406         (typeof document !== 'undefined' && typeof document.getElementsByTagName('body') === 'function') ? function(value) {
407             return toString.call(value) === '[object Function]';
408         } : function(value) {
409             return typeof value === 'function';
410         },
411
412         /**
413          * Returns true if the passed value is a number. Returns false for non-finite numbers.
414          * @param {Mixed} value The value to test
415          * @return {Boolean}
416          */
417         isNumber: function(value) {
418             return typeof value === 'number' && isFinite(value);
419         },
420
421         /**
422          * Validates that a value is numeric.
423          * @param {Mixed} value Examples: 1, '1', '2.34'
424          * @return {Boolean} True if numeric, false otherwise
425          */
426         isNumeric: function(value) {
427             return !isNaN(parseFloat(value)) && isFinite(value);
428         },
429
430         /**
431          * Returns true if the passed value is a string.
432          * @param {Mixed} value The value to test
433          * @return {Boolean}
434          */
435         isString: function(value) {
436             return typeof value === 'string';
437         },
438
439         /**
440          * Returns true if the passed value is a boolean.
441          *
442          * @param {Mixed} value The value to test
443          * @return {Boolean}
444          */
445         isBoolean: function(value) {
446             return typeof value === 'boolean';
447         },
448
449         /**
450          * Returns true if the passed value is an HTMLElement
451          * @param {Mixed} value The value to test
452          * @return {Boolean}
453          */
454         isElement: function(value) {
455             return value ? value.nodeType !== undefined : false;
456         },
457
458         /**
459          * Returns true if the passed value is a TextNode
460          * @param {Mixed} value The value to test
461          * @return {Boolean}
462          */
463         isTextNode: function(value) {
464             return value ? value.nodeName === "#text" : false;
465         },
466
467         /**
468          * Returns true if the passed value is defined.
469          * @param {Mixed} value The value to test
470          * @return {Boolean}
471          */
472         isDefined: function(value) {
473             return typeof value !== 'undefined';
474         },
475
476         /**
477          * Returns true if the passed value is iterable, false otherwise
478          * @param {Mixed} value The value to test
479          * @return {Boolean}
480          */
481         isIterable: function(value) {
482             return (value && typeof value !== 'string') ? value.length !== undefined : false;
483         }
484     });
485
486     Ext.apply(Ext, {
487
488         /**
489          * Clone almost any type of variable including array, object, DOM nodes and Date without keeping the old reference
490          * @param {Mixed} item The variable to clone
491          * @return {Mixed} clone
492          */
493         clone: function(item) {
494             if (item === null || item === undefined) {
495                 return item;
496             }
497
498             // DOM nodes
499             // TODO proxy this to Ext.Element.clone to handle automatic id attribute changing
500             // recursively
501             if (item.nodeType && item.cloneNode) {
502                 return item.cloneNode(true);
503             }
504
505             var type = toString.call(item);
506
507             // Date
508             if (type === '[object Date]') {
509                 return new Date(item.getTime());
510             }
511
512             var i, j, k, clone, key;
513
514             // Array
515             if (type === '[object Array]') {
516                 i = item.length;
517
518                 clone = [];
519
520                 while (i--) {
521                     clone[i] = Ext.clone(item[i]);
522                 }
523             }
524             // Object
525             else if (type === '[object Object]' && item.constructor === Object) {
526                 clone = {};
527
528                 for (key in item) {
529                     clone[key] = Ext.clone(item[key]);
530                 }
531
532                 if (enumerables) {
533                     for (j = enumerables.length; j--;) {
534                         k = enumerables[j];
535                         clone[k] = item[k];
536                     }
537                 }
538             }
539
540             return clone || item;
541         },
542
543         /**
544          * @private
545          * Generate a unique reference of Ext in the global scope, useful for sandboxing
546          */
547         getUniqueGlobalNamespace: function() {
548             var uniqueGlobalNamespace = this.uniqueGlobalNamespace;
549
550             if (uniqueGlobalNamespace === undefined) {
551                 var i = 0;
552
553                 do {
554                     uniqueGlobalNamespace = 'ExtSandbox' + (++i);
555                 } while (Ext.global[uniqueGlobalNamespace] !== undefined);
556
557                 Ext.global[uniqueGlobalNamespace] = Ext;
558                 this.uniqueGlobalNamespace = uniqueGlobalNamespace;
559             }
560
561             return uniqueGlobalNamespace;
562         },
563
564         /**
565          * @private
566          */
567         functionFactory: function() {
568             var args = Array.prototype.slice.call(arguments);
569
570             if (args.length > 0) {
571                 args[args.length - 1] = 'var Ext=window.' + this.getUniqueGlobalNamespace() + ';' +
572                     args[args.length - 1];
573             }
574
575             return Function.prototype.constructor.apply(Function.prototype, args);
576         }
577     });
578
579     /**
580      * Old alias to {@link Ext#typeOf}
581      * @deprecated 4.0.0 Use {@link Ext#typeOf} instead
582      */
583     Ext.type = Ext.typeOf;
584
585 })();
586
587 /**
588  * @author Jacky Nguyen <jacky@sencha.com>
589  * @docauthor Jacky Nguyen <jacky@sencha.com>
590  * @class Ext.Version
591  *
592  * A utility class that wrap around a string version number and provide convenient
593  * method to perform comparison. See also: {@link Ext.Version#compare compare}. Example:
594
595     var version = new Ext.Version('1.0.2beta');
596     console.log("Version is " + version); // Version is 1.0.2beta
597
598     console.log(version.getMajor()); // 1
599     console.log(version.getMinor()); // 0
600     console.log(version.getPatch()); // 2
601     console.log(version.getBuild()); // 0
602     console.log(version.getRelease()); // beta
603
604     console.log(version.isGreaterThan('1.0.1')); // True
605     console.log(version.isGreaterThan('1.0.2alpha')); // True
606     console.log(version.isGreaterThan('1.0.2RC')); // False
607     console.log(version.isGreaterThan('1.0.2')); // False
608     console.log(version.isLessThan('1.0.2')); // True
609
610     console.log(version.match(1.0)); // True
611     console.log(version.match('1.0.2')); // True
612
613  * @markdown
614  */
615 (function() {
616
617 // Current core version
618 var version = '4.0.0', Version;
619     Ext.Version = Version = Ext.extend(Object, {
620
621         /**
622          * @constructor
623          * @param {String/Number} version The version number in the follow standard format: major[.minor[.patch[.build[release]]]]
624          * Examples: 1.0 or 1.2.3beta or 1.2.3.4RC
625          * @return {Ext.Version} this
626          * @param version
627          */
628         constructor: function(version) {
629             var parts, releaseStartIndex;
630
631             if (version instanceof Version) {
632                 return version;
633             }
634
635             this.version = this.shortVersion = String(version).toLowerCase().replace(/_/g, '.').replace(/[\-+]/g, '');
636
637             releaseStartIndex = this.version.search(/([^\d\.])/);
638
639             if (releaseStartIndex !== -1) {
640                 this.release = this.version.substr(releaseStartIndex, version.length);
641                 this.shortVersion = this.version.substr(0, releaseStartIndex);
642             }
643
644             this.shortVersion = this.shortVersion.replace(/[^\d]/g, '');
645
646             parts = this.version.split('.');
647
648             this.major = parseInt(parts.shift() || 0, 10);
649             this.minor = parseInt(parts.shift() || 0, 10);
650             this.patch = parseInt(parts.shift() || 0, 10);
651             this.build = parseInt(parts.shift() || 0, 10);
652
653             return this;
654         },
655
656         /**
657          * Override the native toString method
658          * @private
659          * @return {String} version
660          */
661         toString: function() {
662             return this.version;
663         },
664
665         /**
666          * Override the native valueOf method
667          * @private
668          * @return {String} version
669          */
670         valueOf: function() {
671             return this.version;
672         },
673
674         /**
675          * Returns the major component value
676          * @return {Number} major
677          */
678         getMajor: function() {
679             return this.major || 0;
680         },
681
682         /**
683          * Returns the minor component value
684          * @return {Number} minor
685          */
686         getMinor: function() {
687             return this.minor || 0;
688         },
689
690         /**
691          * Returns the patch component value
692          * @return {Number} patch
693          */
694         getPatch: function() {
695             return this.patch || 0;
696         },
697
698         /**
699          * Returns the build component value
700          * @return {Number} build
701          */
702         getBuild: function() {
703             return this.build || 0;
704         },
705
706         /**
707          * Returns the release component value
708          * @return {Number} release
709          */
710         getRelease: function() {
711             return this.release || '';
712         },
713
714         /**
715          * Returns whether this version if greater than the supplied argument
716          * @param {String/Number} target The version to compare with
717          * @return {Boolean} True if this version if greater than the target, false otherwise
718          */
719         isGreaterThan: function(target) {
720             return Version.compare(this.version, target) === 1;
721         },
722
723         /**
724          * Returns whether this version if smaller than the supplied argument
725          * @param {String/Number} target The version to compare with
726          * @return {Boolean} True if this version if smaller than the target, false otherwise
727          */
728         isLessThan: function(target) {
729             return Version.compare(this.version, target) === -1;
730         },
731
732         /**
733          * Returns whether this version equals to the supplied argument
734          * @param {String/Number} target The version to compare with
735          * @return {Boolean} True if this version equals to the target, false otherwise
736          */
737         equals: function(target) {
738             return Version.compare(this.version, target) === 0;
739         },
740
741         /**
742          * Returns whether this version matches the supplied argument. Example:
743          * <pre><code>
744          * var version = new Ext.Version('1.0.2beta');
745          * console.log(version.match(1)); // True
746          * console.log(version.match(1.0)); // True
747          * console.log(version.match('1.0.2')); // True
748          * console.log(version.match('1.0.2RC')); // False
749          * </code></pre>
750          * @param {String/Number} target The version to compare with
751          * @return {Boolean} True if this version matches the target, false otherwise
752          */
753         match: function(target) {
754             target = String(target);
755             return this.version.substr(0, target.length) === target;
756         },
757
758         /**
759          * Returns this format: [major, minor, patch, build, release]. Useful for comparison
760          * @return {Array}
761          */
762         toArray: function() {
763             return [this.getMajor(), this.getMinor(), this.getPatch(), this.getBuild(), this.getRelease()];
764         },
765
766         /**
767          * Returns shortVersion version without dots and release
768          * @return {String}
769          */
770         getShortVersion: function() {
771             return this.shortVersion;
772         }
773     });
774
775     Ext.apply(Version, {
776         // @private
777         releaseValueMap: {
778             'dev': -6,
779             'alpha': -5,
780             'a': -5,
781             'beta': -4,
782             'b': -4,
783             'rc': -3,
784             '#': -2,
785             'p': -1,
786             'pl': -1
787         },
788
789         /**
790          * Converts a version component to a comparable value
791          *
792          * @static
793          * @param {Mixed} value The value to convert
794          * @return {Mixed}
795          */
796         getComponentValue: function(value) {
797             return !value ? 0 : (isNaN(value) ? this.releaseValueMap[value] || value : parseInt(value, 10));
798         },
799
800         /**
801          * Compare 2 specified versions, starting from left to right. If a part contains special version strings,
802          * they are handled in the following order:
803          * 'dev' < 'alpha' = 'a' < 'beta' = 'b' < 'RC' = 'rc' < '#' < 'pl' = 'p' < 'anything else'
804          *
805          * @static
806          * @param {String} current The current version to compare to
807          * @param {String} target The target version to compare to
808          * @return {Number} Returns -1 if the current version is smaller than the target version, 1 if greater, and 0 if they're equivalent
809          */
810         compare: function(current, target) {
811             var currentValue, targetValue, i;
812
813             current = new Version(current).toArray();
814             target = new Version(target).toArray();
815
816             for (i = 0; i < Math.max(current.length, target.length); i++) {
817                 currentValue = this.getComponentValue(current[i]);
818                 targetValue = this.getComponentValue(target[i]);
819
820                 if (currentValue < targetValue) {
821                     return -1;
822                 } else if (currentValue > targetValue) {
823                     return 1;
824                 }
825             }
826
827             return 0;
828         }
829     });
830
831     Ext.apply(Ext, {
832         /**
833          * @private
834          */
835         versions: {},
836
837         /**
838          * @private
839          */
840         lastRegisteredVersion: null,
841
842         /**
843          * Set version number for the given package name.
844          *
845          * @param {String} packageName The package name, for example: 'core', 'touch', 'extjs'
846          * @param {String/Ext.Version} version The version, for example: '1.2.3alpha', '2.4.0-dev'
847          * @return {Ext}
848          */
849         setVersion: function(packageName, version) {
850             Ext.versions[packageName] = new Version(version);
851             Ext.lastRegisteredVersion = Ext.versions[packageName];
852
853             return this;
854         },
855
856         /**
857          * Get the version number of the supplied package name; will return the last registered version
858          * (last Ext.setVersion call) if there's no package name given.
859          *
860          * @param {String} packageName (Optional) The package name, for example: 'core', 'touch', 'extjs'
861          * @return {Ext.Version} The version
862          */
863         getVersion: function(packageName) {
864             if (packageName === undefined) {
865                 return Ext.lastRegisteredVersion;
866             }
867
868             return Ext.versions[packageName];
869         },
870
871         /**
872          * Create a closure for deprecated code.
873          *
874     // This means Ext.oldMethod is only supported in 4.0.0beta and older.
875     // If Ext.getVersion('extjs') returns a version that is later than '4.0.0beta', for example '4.0.0RC',
876     // the closure will not be invoked
877     Ext.deprecate('extjs', '4.0.0beta', function() {
878         Ext.oldMethod = Ext.newMethod;
879
880         ...
881     });
882
883          * @param {String} packageName The package name
884          * @param {String} since The last version before it's deprecated
885          * @param {Function} closure The callback function to be executed with the specified version is less than the current version
886          * @param {Object} scope The execution scope (<tt>this</tt>) if the closure
887          * @markdown
888          */
889         deprecate: function(packageName, since, closure, scope) {
890             if (Version.compare(Ext.getVersion(packageName), since) < 1) {
891                 closure.call(scope);
892             }
893         }
894     }); // End Versioning
895
896     Ext.setVersion('core', version);
897
898 })();
899
900 /**
901  * @class Ext.String
902  *
903  * A collection of useful static methods to deal with strings
904  * @singleton
905  */
906
907 Ext.String = {
908     trimRegex: /^[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000]+|[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000]+$/g,
909     escapeRe: /('|\\)/g,
910     formatRe: /\{(\d+)\}/g,
911     escapeRegexRe: /([-.*+?^${}()|[\]\/\\])/g,
912
913     /**
914      * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
915      * @param {String} value The string to encode
916      * @return {String} The encoded text
917      */
918     htmlEncode: (function() {
919         var entities = {
920             '&': '&amp;',
921             '>': '&gt;',
922             '<': '&lt;',
923             '"': '&quot;'
924         }, keys = [], p, regex;
925         
926         for (p in entities) {
927             keys.push(p);
928         }
929         
930         regex = new RegExp('(' + keys.join('|') + ')', 'g');
931         
932         return function(value) {
933             return (!value) ? value : String(value).replace(regex, function(match, capture) {
934                 return entities[capture];    
935             });
936         };
937     })(),
938
939     /**
940      * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
941      * @param {String} value The string to decode
942      * @return {String} The decoded text
943      */
944     htmlDecode: (function() {
945         var entities = {
946             '&amp;': '&',
947             '&gt;': '>',
948             '&lt;': '<',
949             '&quot;': '"'
950         }, keys = [], p, regex;
951         
952         for (p in entities) {
953             keys.push(p);
954         }
955         
956         regex = new RegExp('(' + keys.join('|') + '|&#[0-9]{1,5};' + ')', 'g');
957         
958         return function(value) {
959             return (!value) ? value : String(value).replace(regex, function(match, capture) {
960                 if (capture in entities) {
961                     return entities[capture];
962                 } else {
963                     return String.fromCharCode(parseInt(capture.substr(2), 10));
964                 }
965             });
966         };
967     })(),
968
969     /**
970      * Appends content to the query string of a URL, handling logic for whether to place
971      * a question mark or ampersand.
972      * @param {String} url The URL to append to.
973      * @param {String} string The content to append to the URL.
974      * @return (String) The resulting URL
975      */
976     urlAppend : function(url, string) {
977         if (!Ext.isEmpty(string)) {
978             return url + (url.indexOf('?') === -1 ? '?' : '&') + string;
979         }
980
981         return url;
982     },
983
984     /**
985      * Trims whitespace from either end of a string, leaving spaces within the string intact.  Example:
986      * @example
987 var s = '  foo bar  ';
988 alert('-' + s + '-');         //alerts "- foo bar -"
989 alert('-' + Ext.String.trim(s) + '-');  //alerts "-foo bar-"
990
991      * @param {String} string The string to escape
992      * @return {String} The trimmed string
993      */
994     trim: function(string) {
995         return string.replace(Ext.String.trimRegex, "");
996     },
997
998     /**
999      * Capitalize the given string
1000      * @param {String} string
1001      * @return {String}
1002      */
1003     capitalize: function(string) {
1004         return string.charAt(0).toUpperCase() + string.substr(1);
1005     },
1006
1007     /**
1008      * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
1009      * @param {String} value The string to truncate
1010      * @param {Number} length The maximum length to allow before truncating
1011      * @param {Boolean} word True to try to find a common word break
1012      * @return {String} The converted text
1013      */
1014     ellipsis: function(value, len, word) {
1015         if (value && value.length > len) {
1016             if (word) {
1017                 var vs = value.substr(0, len - 2),
1018                 index = Math.max(vs.lastIndexOf(' '), vs.lastIndexOf('.'), vs.lastIndexOf('!'), vs.lastIndexOf('?'));
1019                 if (index !== -1 && index >= (len - 15)) {
1020                     return vs.substr(0, index) + "...";
1021                 }
1022             }
1023             return value.substr(0, len - 3) + "...";
1024         }
1025         return value;
1026     },
1027
1028     /**
1029      * Escapes the passed string for use in a regular expression
1030      * @param {String} string
1031      * @return {String}
1032      */
1033     escapeRegex: function(string) {
1034         return string.replace(Ext.String.escapeRegexRe, "\\$1");
1035     },
1036
1037     /**
1038      * Escapes the passed string for ' and \
1039      * @param {String} string The string to escape
1040      * @return {String} The escaped string
1041      */
1042     escape: function(string) {
1043         return string.replace(Ext.String.escapeRe, "\\$1");
1044     },
1045
1046     /**
1047      * Utility function that allows you to easily switch a string between two alternating values.  The passed value
1048      * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
1049      * they are already different, the first value passed in is returned.  Note that this method returns the new value
1050      * but does not change the current string.
1051      * <pre><code>
1052     // alternate sort directions
1053     sort = Ext.String.toggle(sort, 'ASC', 'DESC');
1054
1055     // instead of conditional logic:
1056     sort = (sort == 'ASC' ? 'DESC' : 'ASC');
1057        </code></pre>
1058      * @param {String} string The current string
1059      * @param {String} value The value to compare to the current string
1060      * @param {String} other The new value to use if the string already equals the first value passed in
1061      * @return {String} The new value
1062      */
1063     toggle: function(string, value, other) {
1064         return string === value ? other : value;
1065     },
1066
1067     /**
1068      * Pads the left side of a string with a specified character.  This is especially useful
1069      * for normalizing number and date strings.  Example usage:
1070      *
1071      * <pre><code>
1072 var s = Ext.String.leftPad('123', 5, '0');
1073 // s now contains the string: '00123'
1074        </code></pre>
1075      * @param {String} string The original string
1076      * @param {Number} size The total length of the output string
1077      * @param {String} character (optional) The character with which to pad the original string (defaults to empty string " ")
1078      * @return {String} The padded string
1079      */
1080     leftPad: function(string, size, character) {
1081         var result = String(string);
1082         character = character || " ";
1083         while (result.length < size) {
1084             result = character + result;
1085         }
1086         return result;
1087     },
1088
1089     /**
1090      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
1091      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
1092      * <pre><code>
1093 var cls = 'my-class', text = 'Some text';
1094 var s = Ext.String.format('&lt;div class="{0}">{1}&lt;/div>', cls, text);
1095 // s now contains the string: '&lt;div class="my-class">Some text&lt;/div>'
1096        </code></pre>
1097      * @param {String} string The tokenized string to be formatted
1098      * @param {String} value1 The value to replace token {0}
1099      * @param {String} value2 Etc...
1100      * @return {String} The formatted string
1101      */
1102     format: function(format) {
1103         var args = Ext.Array.toArray(arguments, 1);
1104         return format.replace(Ext.String.formatRe, function(m, i) {
1105             return args[i];
1106         });
1107     }
1108 };
1109
1110 /**
1111  * @class Ext.Number
1112  *
1113  * A collection of useful static methods to deal with numbers
1114  * @singleton
1115  */
1116
1117 (function() {
1118
1119 var isToFixedBroken = (0.9).toFixed() !== '1';
1120
1121 Ext.Number = {
1122     /**
1123      * Checks whether or not the current number is within a desired range.  If the number is already within the
1124      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
1125      * exceeded. Note that this method returns the constrained value but does not change the current number.
1126      * @param {Number} number The number to check
1127      * @param {Number} min The minimum number in the range
1128      * @param {Number} max The maximum number in the range
1129      * @return {Number} The constrained value if outside the range, otherwise the current value
1130      */
1131     constrain: function(number, min, max) {
1132         number = parseFloat(number);
1133
1134         if (!isNaN(min)) {
1135             number = Math.max(number, min);
1136         }
1137         if (!isNaN(max)) {
1138             number = Math.min(number, max);
1139         }
1140         return number;
1141     },
1142
1143     /**
1144      * Formats a number using fixed-point notation
1145      * @param {Number} value The number to format
1146      * @param {Number} precision The number of digits to show after the decimal point
1147      */
1148     toFixed: function(value, precision) {
1149         if (isToFixedBroken) {
1150             precision = precision || 0;
1151             var pow = Math.pow(10, precision);
1152             return (Math.round(value * pow) / pow).toFixed(precision);
1153         }
1154
1155         return value.toFixed(precision);
1156     },
1157
1158     /**
1159      * Validate that a value is numeric and convert it to a number if necessary. Returns the specified default value if
1160      * it is not.
1161
1162 Ext.Number.from('1.23', 1); // returns 1.23
1163 Ext.Number.from('abc', 1); // returns 1
1164
1165      * @param {Mixed} value
1166      * @param {Number} defaultValue The value to return if the original value is non-numeric
1167      * @return {Number} value, if numeric, defaultValue otherwise
1168      */
1169     from: function(value, defaultValue) {
1170         if (isFinite(value)) {
1171             value = parseFloat(value);
1172         }
1173
1174         return !isNaN(value) ? value : defaultValue;
1175     }
1176 };
1177
1178 })();
1179
1180 /**
1181  * This method is deprecated, please use {@link Ext.Number#from Ext.Number.from} instead
1182  *
1183  * @deprecated 4.0.0 Replaced by Ext.Number.from
1184  * @member Ext
1185  * @method num
1186  */
1187 Ext.num = function() {
1188     return Ext.Number.from.apply(this, arguments);
1189 };
1190 /**
1191  * @author Jacky Nguyen <jacky@sencha.com>
1192  * @docauthor Jacky Nguyen <jacky@sencha.com>
1193  * @class Ext.Array
1194  *
1195  * A set of useful static methods to deal with arrays; provide missing methods for older browsers.
1196
1197  * @singleton
1198  * @markdown
1199  */
1200 (function() {
1201
1202     var arrayPrototype = Array.prototype,
1203         slice = arrayPrototype.slice,
1204         supportsForEach = 'forEach' in arrayPrototype,
1205         supportsMap = 'map' in arrayPrototype,
1206         supportsIndexOf = 'indexOf' in arrayPrototype,
1207         supportsEvery = 'every' in arrayPrototype,
1208         supportsSome = 'some' in arrayPrototype,
1209         supportsFilter = 'filter' in arrayPrototype,
1210         supportsSort = function() {
1211             var a = [1,2,3,4,5].sort(function(){ return 0; });
1212             return a[0] === 1 && a[1] === 2 && a[2] === 3 && a[3] === 4 && a[4] === 5;
1213         }(),
1214         supportsSliceOnNodeList = true,
1215         ExtArray;
1216     try {
1217         // IE 6 - 8 will throw an error when using Array.prototype.slice on NodeList
1218         if (typeof document !== 'undefined') {
1219             slice.call(document.getElementsByTagName('body'));
1220         }
1221     } catch (e) {
1222         supportsSliceOnNodeList = false;
1223     }
1224
1225     ExtArray = Ext.Array = {
1226         /*
1227          * Iterates an array or an iterable value and invoke the given callback function for each item.
1228
1229     var countries = ['Vietnam', 'Singapore', 'United States', 'Russia'];
1230
1231     Ext.Array.each(countries, function(name, index, countriesItSelf) {
1232         console.log(name);
1233     });
1234
1235     var sum = function() {
1236         var sum = 0;
1237
1238         Ext.Array.each(arguments, function(value) {
1239             sum += value;
1240         });
1241
1242         return sum;
1243     };
1244
1245     sum(1, 2, 3); // returns 6
1246
1247          * The iteration can be stopped by returning false in the function callback.
1248
1249     Ext.Array.each(countries, function(name, index, countriesItSelf) {
1250         if (name === 'Singapore') {
1251             return false; // break here
1252         }
1253     });
1254
1255          * @param {Array/NodeList/Mixed} iterable The value to be iterated. If this
1256          * argument is not iterable, the callback function is called once.
1257          * @param {Function} fn The callback function. If it returns false, the iteration stops and this method returns
1258          * the current `index`. Arguments passed to this callback function are:
1259
1260 - `item`: {Mixed} The item at the current `index` in the passed `array`
1261 - `index`: {Number} The current `index` within the `array`
1262 - `allItems`: {Array/NodeList/Mixed} The `array` passed as the first argument to `Ext.Array.each`
1263
1264          * @param {Object} scope (Optional) The scope (`this` reference) in which the specified function is executed.
1265          * @param {Boolean} reverse (Optional) Reverse the iteration order (loop from the end to the beginning)
1266          * Defaults false
1267          * @return {Boolean} See description for the `fn` parameter.
1268          * @markdown
1269          */
1270         each: function(array, fn, scope, reverse) {
1271             array = ExtArray.from(array);
1272
1273             var i,
1274                 ln = array.length;
1275
1276             if (reverse !== true) {
1277                 for (i = 0; i < ln; i++) {
1278                     if (fn.call(scope || array[i], array[i], i, array) === false) {
1279                         return i;
1280                     }
1281                 }
1282             }
1283             else {
1284                 for (i = ln - 1; i > -1; i--) {
1285                     if (fn.call(scope || array[i], array[i], i, array) === false) {
1286                         return i;
1287                     }
1288                 }
1289             }
1290
1291             return true;
1292         },
1293
1294         /**
1295          * Iterates an array and invoke the given callback function for each item. Note that this will simply
1296          * delegate to the native Array.prototype.forEach method if supported.
1297          * It doesn't support stopping the iteration by returning false in the callback function like
1298          * {@link Ext.Array#each}. However, performance could be much better in modern browsers comparing with
1299          * {@link Ext.Array#each}
1300          *
1301          * @param {Array} array The array to iterate
1302          * @param {Function} fn The function callback, to be invoked these arguments:
1303          *
1304 - `item`: {Mixed} The item at the current `index` in the passed `array`
1305 - `index`: {Number} The current `index` within the `array`
1306 - `allItems`: {Array} The `array` itself which was passed as the first argument
1307
1308          * @param {Object} scope (Optional) The execution scope (`this`) in which the specified function is executed.
1309          * @markdown
1310          */
1311         forEach: function(array, fn, scope) {
1312             if (supportsForEach) {
1313                 return array.forEach(fn, scope);
1314             }
1315
1316             var i = 0,
1317                 ln = array.length;
1318
1319             for (; i < ln; i++) {
1320                 fn.call(scope, array[i], i, array);
1321             }
1322         },
1323
1324         /**
1325          * Get the index of the provided `item` in the given `array`, a supplement for the
1326          * missing arrayPrototype.indexOf in Internet Explorer.
1327          *
1328          * @param {Array} array The array to check
1329          * @param {Mixed} item The item to look for
1330          * @param {Number} from (Optional) The index at which to begin the search
1331          * @return {Number} The index of item in the array (or -1 if it is not found)
1332          * @markdown
1333          */
1334         indexOf: function(array, item, from) {
1335             if (supportsIndexOf) {
1336                 return array.indexOf(item, from);
1337             }
1338
1339             var i, length = array.length;
1340
1341             for (i = (from < 0) ? Math.max(0, length + from) : from || 0; i < length; i++) {
1342                 if (array[i] === item) {
1343                     return i;
1344                 }
1345             }
1346
1347             return -1;
1348         },
1349
1350         /**
1351          * Checks whether or not the given `array` contains the specified `item`
1352          *
1353          * @param {Array} array The array to check
1354          * @param {Mixed} item The item to look for
1355          * @return {Boolean} True if the array contains the item, false otherwise
1356          * @markdown
1357          */
1358         contains: function(array, item) {
1359             if (supportsIndexOf) {
1360                 return array.indexOf(item) !== -1;
1361             }
1362
1363             var i, ln;
1364
1365             for (i = 0, ln = array.length; i < ln; i++) {
1366                 if (array[i] === item) {
1367                     return true;
1368                 }
1369             }
1370
1371             return false;
1372         },
1373
1374         /**
1375          * Converts any iterable (numeric indices and a length property) into a true array.
1376
1377 function test() {
1378     var args = Ext.Array.toArray(arguments),
1379         fromSecondToLastArgs = Ext.Array.toArray(arguments, 1);
1380
1381     alert(args.join(' '));
1382     alert(fromSecondToLastArgs.join(' '));
1383 }
1384
1385 test('just', 'testing', 'here'); // alerts 'just testing here';
1386                                  // alerts 'testing here';
1387
1388 Ext.Array.toArray(document.getElementsByTagName('div')); // will convert the NodeList into an array
1389 Ext.Array.toArray('splitted'); // returns ['s', 'p', 'l', 'i', 't', 't', 'e', 'd']
1390 Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i']
1391
1392          * @param {Mixed} iterable the iterable object to be turned into a true Array.
1393          * @param {Number} start (Optional) a zero-based index that specifies the start of extraction. Defaults to 0
1394          * @param {Number} end (Optional) a zero-based index that specifies the end of extraction. Defaults to the last
1395          * index of the iterable value
1396          * @return {Array} array
1397          * @markdown
1398          */
1399         toArray: function(iterable, start, end){
1400             if (!iterable || !iterable.length) {
1401                 return [];
1402             }
1403
1404             if (typeof iterable === 'string') {
1405                 iterable = iterable.split('');
1406             }
1407
1408             if (supportsSliceOnNodeList) {
1409                 return slice.call(iterable, start || 0, end || iterable.length);
1410             }
1411
1412             var array = [],
1413                 i;
1414
1415             start = start || 0;
1416             end = end ? ((end < 0) ? iterable.length + end : end) : iterable.length;
1417
1418             for (i = start; i < end; i++) {
1419                 array.push(iterable[i]);
1420             }
1421
1422             return array;
1423         },
1424
1425         /**
1426          * Plucks the value of a property from each item in the Array. Example:
1427          *
1428     Ext.Array.pluck(Ext.query("p"), "className"); // [el1.className, el2.className, ..., elN.className]
1429
1430          * @param {Array|NodeList} array The Array of items to pluck the value from.
1431          * @param {String} propertyName The property name to pluck from each element.
1432          * @return {Array} The value from each item in the Array.
1433          */
1434         pluck: function(array, propertyName) {
1435             var ret = [],
1436                 i, ln, item;
1437
1438             for (i = 0, ln = array.length; i < ln; i++) {
1439                 item = array[i];
1440
1441                 ret.push(item[propertyName]);
1442             }
1443
1444             return ret;
1445         },
1446
1447         /**
1448          * Creates a new array with the results of calling a provided function on every element in this array.
1449          * @param {Array} array
1450          * @param {Function} fn Callback function for each item
1451          * @param {Object} scope Callback function scope
1452          * @return {Array} results
1453          */
1454         map: function(array, fn, scope) {
1455             if (supportsMap) {
1456                 return array.map(fn, scope);
1457             }
1458
1459             var results = [],
1460                 i = 0,
1461                 len = array.length;
1462
1463             for (; i < len; i++) {
1464                 results[i] = fn.call(scope, array[i], i, array);
1465             }
1466
1467             return results;
1468         },
1469
1470         /**
1471          * Executes the specified function for each array element until the function returns a falsy value.
1472          * If such an item is found, the function will return false immediately.
1473          * Otherwise, it will return true.
1474          *
1475          * @param {Array} array
1476          * @param {Function} fn Callback function for each item
1477          * @param {Object} scope Callback function scope
1478          * @return {Boolean} True if no false value is returned by the callback function.
1479          */
1480         every: function(array, fn, scope) {
1481             //<debug>
1482             if (!fn) {
1483                 Ext.Error.raise('Ext.Array.every must have a callback function passed as second argument.');
1484             }
1485             //</debug>
1486             if (supportsEvery) {
1487                 return array.every(fn, scope);
1488             }
1489
1490             var i = 0,
1491                 ln = array.length;
1492
1493             for (; i < ln; ++i) {
1494                 if (!fn.call(scope, array[i], i, array)) {
1495                     return false;
1496                 }
1497             }
1498
1499             return true;
1500         },
1501
1502         /**
1503          * Executes the specified function for each array element until the function returns a truthy value.
1504          * If such an item is found, the function will return true immediately. Otherwise, it will return false.
1505          *
1506          * @param {Array} array
1507          * @param {Function} fn Callback function for each item
1508          * @param {Object} scope Callback function scope
1509          * @return {Boolean} True if the callback function returns a truthy value.
1510          */
1511         some: function(array, fn, scope) {
1512             //<debug>
1513             if (!fn) {
1514                 Ext.Error.raise('Ext.Array.some must have a callback function passed as second argument.');
1515             }
1516             //</debug>
1517             if (supportsSome) {
1518                 return array.some(fn, scope);
1519             }
1520
1521             var i = 0,
1522                 ln = array.length;
1523
1524             for (; i < ln; ++i) {
1525                 if (fn.call(scope, array[i], i, array)) {
1526                     return true;
1527                 }
1528             }
1529
1530             return false;
1531         },
1532
1533         /**
1534          * Filter through an array and remove empty item as defined in {@link Ext#isEmpty Ext.isEmpty}
1535          *
1536          * @see Ext.Array.filter
1537          * @param {Array} array
1538          * @return {Array} results
1539          */
1540         clean: function(array) {
1541             var results = [],
1542                 i = 0,
1543                 ln = array.length,
1544                 item;
1545
1546             for (; i < ln; i++) {
1547                 item = array[i];
1548
1549                 if (!Ext.isEmpty(item)) {
1550                     results.push(item);
1551                 }
1552             }
1553
1554             return results;
1555         },
1556
1557         /**
1558          * Returns a new array with unique items
1559          *
1560          * @param {Array} array
1561          * @return {Array} results
1562          */
1563         unique: function(array) {
1564             var clone = [],
1565                 i = 0,
1566                 ln = array.length,
1567                 item;
1568
1569             for (; i < ln; i++) {
1570                 item = array[i];
1571
1572                 if (ExtArray.indexOf(clone, item) === -1) {
1573                     clone.push(item);
1574                 }
1575             }
1576
1577             return clone;
1578         },
1579
1580         /**
1581          * Creates a new array with all of the elements of this array for which
1582          * the provided filtering function returns true.
1583          * @param {Array} array
1584          * @param {Function} fn Callback function for each item
1585          * @param {Object} scope Callback function scope
1586          * @return {Array} results
1587          */
1588         filter: function(array, fn, scope) {
1589             if (supportsFilter) {
1590                 return array.filter(fn, scope);
1591             }
1592
1593             var results = [],
1594                 i = 0,
1595                 ln = array.length;
1596
1597             for (; i < ln; i++) {
1598                 if (fn.call(scope, array[i], i, array)) {
1599                     results.push(array[i]);
1600                 }
1601             }
1602
1603             return results;
1604         },
1605
1606         /**
1607          * Converts a value to an array if it's not already an array; returns:
1608          *
1609          * - An empty array if given value is `undefined` or `null`
1610          * - Itself if given value is already an array
1611          * - An array copy if given value is {@link Ext#isIterable iterable} (arguments, NodeList and alike)
1612          * - An array with one item which is the given value, otherwise
1613          *
1614          * @param {Array/Mixed} value The value to convert to an array if it's not already is an array
1615          * @param {Boolean} (Optional) newReference True to clone the given array and return a new reference if necessary,
1616          * defaults to false
1617          * @return {Array} array
1618          * @markdown
1619          */
1620         from: function(value, newReference) {
1621             if (value === undefined || value === null) {
1622                 return [];
1623             }
1624
1625             if (Ext.isArray(value)) {
1626                 return (newReference) ? slice.call(value) : value;
1627             }
1628
1629             if (value && value.length !== undefined && typeof value !== 'string') {
1630                 return Ext.toArray(value);
1631             }
1632
1633             return [value];
1634         },
1635
1636         /**
1637          * Removes the specified item from the array if it exists
1638          *
1639          * @param {Array} array The array
1640          * @param {Mixed} item The item to remove
1641          * @return {Array} The passed array itself
1642          */
1643         remove: function(array, item) {
1644             var index = ExtArray.indexOf(array, item);
1645
1646             if (index !== -1) {
1647                 array.splice(index, 1);
1648             }
1649
1650             return array;
1651         },
1652
1653         /**
1654          * Push an item into the array only if the array doesn't contain it yet
1655          *
1656          * @param {Array} array The array
1657          * @param {Mixed} item The item to include
1658          * @return {Array} The passed array itself
1659          */
1660         include: function(array, item) {
1661             if (!ExtArray.contains(array, item)) {
1662                 array.push(item);
1663             }
1664         },
1665
1666         /**
1667          * Clone a flat array without referencing the previous one. Note that this is different
1668          * from Ext.clone since it doesn't handle recursive cloning. It's simply a convenient, easy-to-remember method
1669          * for Array.prototype.slice.call(array)
1670          *
1671          * @param {Array} array The array
1672          * @return {Array} The clone array
1673          */
1674         clone: function(array) {
1675             return slice.call(array);
1676         },
1677
1678         /**
1679          * Merge multiple arrays into one with unique items. Alias to {@link Ext.Array#union}.
1680          *
1681          * @param {Array} array,...
1682          * @return {Array} merged
1683          */
1684         merge: function() {
1685             var args = slice.call(arguments),
1686                 array = [],
1687                 i, ln;
1688
1689             for (i = 0, ln = args.length; i < ln; i++) {
1690                 array = array.concat(args[i]);
1691             }
1692
1693             return ExtArray.unique(array);
1694         },
1695
1696         /**
1697          * Merge multiple arrays into one with unique items that exist in all of the arrays.
1698          *
1699          * @param {Array} array,...
1700          * @return {Array} intersect
1701          */
1702         intersect: function() {
1703             var intersect = [],
1704                 arrays = slice.call(arguments),
1705                 i, j, k, minArray, array, x, y, ln, arraysLn, arrayLn;
1706
1707             if (!arrays.length) {
1708                 return intersect;
1709             }
1710
1711             // Find the smallest array
1712             for (i = x = 0,ln = arrays.length; i < ln,array = arrays[i]; i++) {
1713                 if (!minArray || array.length < minArray.length) {
1714                     minArray = array;
1715                     x = i;
1716                 }
1717             }
1718
1719             minArray = Ext.Array.unique(minArray);
1720             arrays.splice(x, 1);
1721
1722             // Use the smallest unique'd array as the anchor loop. If the other array(s) do contain
1723             // an item in the small array, we're likely to find it before reaching the end
1724             // of the inner loop and can terminate the search early.
1725             for (i = 0,ln = minArray.length; i < ln,x = minArray[i]; i++) {
1726                 var count = 0;
1727
1728                 for (j = 0,arraysLn = arrays.length; j < arraysLn,array = arrays[j]; j++) {
1729                     for (k = 0,arrayLn = array.length; k < arrayLn,y = array[k]; k++) {
1730                         if (x === y) {
1731                             count++;
1732                             break;
1733                         }
1734                     }
1735                 }
1736
1737                 if (count === arraysLn) {
1738                     intersect.push(x);
1739                 }
1740             }
1741
1742             return intersect;
1743         },
1744
1745         /**
1746          * Perform a set difference A-B by subtracting all items in array B from array A.
1747          *
1748          * @param {Array} array A
1749          * @param {Array} array B
1750          * @return {Array} difference
1751          */
1752         difference: function(arrayA, arrayB) {
1753             var clone = slice.call(arrayA),
1754                 ln = clone.length,
1755                 i, j, lnB;
1756
1757             for (i = 0,lnB = arrayB.length; i < lnB; i++) {
1758                 for (j = 0; j < ln; j++) {
1759                     if (clone[j] === arrayB[i]) {
1760                         clone.splice(j, 1);
1761                         j--;
1762                         ln--;
1763                     }
1764                 }
1765             }
1766
1767             return clone;
1768         },
1769
1770         /**
1771          * Sorts the elements of an Array.
1772          * By default, this method sorts the elements alphabetically and ascending.
1773          *
1774          * @param {Array} array The array to sort.
1775          * @param {Function} sortFn (optional) The comparison function.
1776          * @return {Array} The sorted array.
1777          */
1778         sort: function(array, sortFn) {
1779             if (supportsSort) {
1780                 if (sortFn) {
1781                     return array.sort(sortFn);
1782                 } else {
1783                     return array.sort();
1784                 }
1785             }
1786
1787             var length = array.length,
1788                 i = 0,
1789                 comparison,
1790                 j, min, tmp;
1791
1792             for (; i < length; i++) {
1793                 min = i;
1794                 for (j = i + 1; j < length; j++) {
1795                     if (sortFn) {
1796                         comparison = sortFn(array[j], array[min]);
1797                         if (comparison < 0) {
1798                             min = j;
1799                         }
1800                     } else if (array[j] < array[min]) {
1801                         min = j;
1802                     }
1803                 }
1804                 if (min !== i) {
1805                     tmp = array[i];
1806                     array[i] = array[min];
1807                     array[min] = tmp;
1808                 }
1809             }
1810
1811             return array;
1812         },
1813
1814         /**
1815          * Recursively flattens into 1-d Array. Injects Arrays inline.
1816          * @param {Array} array The array to flatten
1817          * @return {Array} The new, flattened array.
1818          */
1819         flatten: function(array) {
1820             var worker = [];
1821
1822             function rFlatten(a) {
1823                 var i, ln, v;
1824
1825                 for (i = 0, ln = a.length; i < ln; i++) {
1826                     v = a[i];
1827
1828                     if (Ext.isArray(v)) {
1829                         rFlatten(v);
1830                     } else {
1831                         worker.push(v);
1832                     }
1833                 }
1834
1835                 return worker;
1836             }
1837
1838             return rFlatten(array);
1839         },
1840
1841         /**
1842          * Returns the minimum value in the Array.
1843          * @param {Array|NodeList} array The Array from which to select the minimum value.
1844          * @param {Function} comparisonFn (optional) a function to perform the comparision which determines minimization.
1845          *                   If omitted the "<" operator will be used. Note: gt = 1; eq = 0; lt = -1
1846          * @return {Mixed} minValue The minimum value
1847          */
1848         min: function(array, comparisonFn) {
1849             var min = array[0],
1850                 i, ln, item;
1851
1852             for (i = 0, ln = array.length; i < ln; i++) {
1853                 item = array[i];
1854
1855                 if (comparisonFn) {
1856                     if (comparisonFn(min, item) === 1) {
1857                         min = item;
1858                     }
1859                 }
1860                 else {
1861                     if (item < min) {
1862                         min = item;
1863                     }
1864                 }
1865             }
1866
1867             return min;
1868         },
1869
1870         /**
1871          * Returns the maximum value in the Array
1872          * @param {Array|NodeList} array The Array from which to select the maximum value.
1873          * @param {Function} comparisonFn (optional) a function to perform the comparision which determines maximization.
1874          *                   If omitted the ">" operator will be used. Note: gt = 1; eq = 0; lt = -1
1875          * @return {Mixed} maxValue The maximum value
1876          */
1877         max: function(array, comparisonFn) {
1878             var max = array[0],
1879                 i, ln, item;
1880
1881             for (i = 0, ln = array.length; i < ln; i++) {
1882                 item = array[i];
1883
1884                 if (comparisonFn) {
1885                     if (comparisonFn(max, item) === -1) {
1886                         max = item;
1887                     }
1888                 }
1889                 else {
1890                     if (item > max) {
1891                         max = item;
1892                     }
1893                 }
1894             }
1895
1896             return max;
1897         },
1898
1899         /**
1900          * Calculates the mean of all items in the array
1901          * @param {Array} array The Array to calculate the mean value of.
1902          * @return {Number} The mean.
1903          */
1904         mean: function(array) {
1905             return array.length > 0 ? ExtArray.sum(array) / array.length : undefined;
1906         },
1907
1908         /**
1909          * Calculates the sum of all items in the given array
1910          * @param {Array} array The Array to calculate the sum value of.
1911          * @return {Number} The sum.
1912          */
1913         sum: function(array) {
1914             var sum = 0,
1915                 i, ln, item;
1916
1917             for (i = 0,ln = array.length; i < ln; i++) {
1918                 item = array[i];
1919
1920                 sum += item;
1921             }
1922
1923             return sum;
1924         }
1925
1926     };
1927
1928     /**
1929      * Convenient alias to {@link Ext.Array#each}
1930      * @member Ext
1931      * @method each
1932      */
1933     Ext.each = Ext.Array.each;
1934
1935     /**
1936      * Alias to {@link Ext.Array#merge}.
1937      * @member Ext.Array
1938      * @method union
1939      */
1940     Ext.Array.union = Ext.Array.merge;
1941
1942     /**
1943      * Old alias to {@link Ext.Array#min}
1944      * @deprecated 4.0.0 Use {@link Ext.Array#min} instead
1945      * @member Ext
1946      * @method min
1947      */
1948     Ext.min = Ext.Array.min;
1949
1950     /**
1951      * Old alias to {@link Ext.Array#max}
1952      * @deprecated 4.0.0 Use {@link Ext.Array#max} instead
1953      * @member Ext
1954      * @method max
1955      */
1956     Ext.max = Ext.Array.max;
1957
1958     /**
1959      * Old alias to {@link Ext.Array#sum}
1960      * @deprecated 4.0.0 Use {@link Ext.Array#sum} instead
1961      * @member Ext
1962      * @method sum
1963      */
1964     Ext.sum = Ext.Array.sum;
1965
1966     /**
1967      * Old alias to {@link Ext.Array#mean}
1968      * @deprecated 4.0.0 Use {@link Ext.Array#mean} instead
1969      * @member Ext
1970      * @method mean
1971      */
1972     Ext.mean = Ext.Array.mean;
1973
1974     /**
1975      * Old alias to {@link Ext.Array#flatten}
1976      * @deprecated 4.0.0 Use {@link Ext.Array#flatten} instead
1977      * @member Ext
1978      * @method flatten
1979      */
1980     Ext.flatten = Ext.Array.flatten;
1981
1982     /**
1983      * Old alias to {@link Ext.Array#clean Ext.Array.clean}
1984      * @deprecated 4.0.0 Use {@link Ext.Array.clean} instead
1985      * @member Ext
1986      * @method clean
1987      */
1988     Ext.clean = Ext.Array.clean;
1989
1990     /**
1991      * Old alias to {@link Ext.Array#unique Ext.Array.unique}
1992      * @deprecated 4.0.0 Use {@link Ext.Array.unique} instead
1993      * @member Ext
1994      * @method unique
1995      */
1996     Ext.unique = Ext.Array.unique;
1997
1998     /**
1999      * Old alias to {@link Ext.Array#pluck Ext.Array.pluck}
2000      * @deprecated 4.0.0 Use {@link Ext.Array#pluck Ext.Array.pluck} instead
2001      * @member Ext
2002      * @method pluck
2003      */
2004     Ext.pluck = Ext.Array.pluck;
2005
2006     /**
2007      * Convenient alias to {@link Ext.Array#toArray Ext.Array.toArray}
2008      * @param {Iterable} the iterable object to be turned into a true Array.
2009      * @member Ext
2010      * @method toArray
2011      * @return {Array} array
2012      */
2013     Ext.toArray = function() {
2014         return ExtArray.toArray.apply(ExtArray, arguments);
2015     }
2016 })();
2017
2018 /**
2019  * @class Ext.Function
2020  *
2021  * A collection of useful static methods to deal with function callbacks
2022  * @singleton
2023  */
2024
2025 Ext.Function = {
2026
2027     /**
2028      * A very commonly used method throughout the framework. It acts as a wrapper around another method
2029      * which originally accepts 2 arguments for <code>name</code> and <code>value</code>.
2030      * The wrapped function then allows "flexible" value setting of either:
2031      *
2032      * <ul>
2033      *      <li><code>name</code> and <code>value</code> as 2 arguments</li>
2034      *      <li>one single object argument with multiple key - value pairs</li>
2035      * </ul>
2036      *
2037      * For example:
2038      * <pre><code>
2039 var setValue = Ext.Function.flexSetter(function(name, value) {
2040     this[name] = value;
2041 });
2042
2043 // Afterwards
2044 // Setting a single name - value
2045 setValue('name1', 'value1');
2046
2047 // Settings multiple name - value pairs
2048 setValue({
2049     name1: 'value1',
2050     name2: 'value2',
2051     name3: 'value3'
2052 });
2053      * </code></pre>
2054      * @param {Function} setter
2055      * @returns {Function} flexSetter
2056      */
2057     flexSetter: function(fn) {
2058         return function(a, b) {
2059             var k, i;
2060
2061             if (a === null) {
2062                 return this;
2063             }
2064
2065             if (typeof a !== 'string') {
2066                 for (k in a) {
2067                     if (a.hasOwnProperty(k)) {
2068                         fn.call(this, k, a[k]);
2069                     }
2070                 }
2071
2072                 if (Ext.enumerables) {
2073                     for (i = Ext.enumerables.length; i--;) {
2074                         k = Ext.enumerables[i];
2075                         if (a.hasOwnProperty(k)) {
2076                             fn.call(this, k, a[k]);
2077                         }
2078                     }
2079                 }
2080             } else {
2081                 fn.call(this, a, b);
2082             }
2083
2084             return this;
2085         };
2086     },
2087
2088    /**
2089      * Create a new function from the provided <code>fn</code>, change <code>this</code> to the provided scope, optionally
2090      * overrides arguments for the call. (Defaults to the arguments passed by the caller)
2091      *
2092      * @param {Function} fn The function to delegate.
2093      * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the function is executed.
2094      * <b>If omitted, defaults to the browser window.</b>
2095      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
2096      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
2097      * if a number the args are inserted at the specified position
2098      * @return {Function} The new function
2099      */
2100     bind: function(fn, scope, args, appendArgs) {
2101         var method = fn,
2102             applyArgs;
2103
2104         return function() {
2105             var callArgs = args || arguments;
2106
2107             if (appendArgs === true) {
2108                 callArgs = Array.prototype.slice.call(arguments, 0);
2109                 callArgs = callArgs.concat(args);
2110             }
2111             else if (Ext.isNumber(appendArgs)) {
2112                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
2113                 applyArgs = [appendArgs, 0].concat(args); // create method call params
2114                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
2115             }
2116
2117             return method.apply(scope || window, callArgs);
2118         };
2119     },
2120
2121     /**
2122      * Create a new function from the provided <code>fn</code>, the arguments of which are pre-set to `args`.
2123      * New arguments passed to the newly created callback when it's invoked are appended after the pre-set ones.
2124      * This is especially useful when creating callbacks.
2125      * For example:
2126      *
2127     var originalFunction = function(){
2128         alert(Ext.Array.from(arguments).join(' '));
2129     };
2130
2131     var callback = Ext.Function.pass(originalFunction, ['Hello', 'World']);
2132
2133     callback(); // alerts 'Hello World'
2134     callback('by Me'); // alerts 'Hello World by Me'
2135
2136      * @param {Function} fn The original function
2137      * @param {Array} args The arguments to pass to new callback
2138      * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the function is executed.
2139      * @return {Function} The new callback function
2140      */
2141     pass: function(fn, args, scope) {
2142         if (args) {
2143             args = Ext.Array.from(args);
2144         }
2145
2146         return function() {
2147             return fn.apply(scope, args.concat(Ext.Array.toArray(arguments)));
2148         };
2149     },
2150
2151     /**
2152      * Create an alias to the provided method property with name <code>methodName</code> of <code>object</code>.
2153      * Note that the execution scope will still be bound to the provided <code>object</code> itself.
2154      *
2155      * @param {Object/Function} object
2156      * @param {String} methodName
2157      * @return {Function} aliasFn
2158      */
2159     alias: function(object, methodName) {
2160         return function() {
2161             return object[methodName].apply(object, arguments);
2162         };
2163     },
2164
2165     /**
2166      * Creates an interceptor function. The passed function is called before the original one. If it returns false,
2167      * the original one is not called. The resulting function returns the results of the original function.
2168      * The passed function is called with the parameters of the original function. Example usage:
2169      * <pre><code>
2170 var sayHi = function(name){
2171     alert('Hi, ' + name);
2172 }
2173
2174 sayHi('Fred'); // alerts "Hi, Fred"
2175
2176 // create a new function that validates input without
2177 // directly modifying the original function:
2178 var sayHiToFriend = Ext.Function.createInterceptor(sayHi, function(name){
2179     return name == 'Brian';
2180 });
2181
2182 sayHiToFriend('Fred');  // no alert
2183 sayHiToFriend('Brian'); // alerts "Hi, Brian"
2184      </code></pre>
2185      * @param {Function} origFn The original function.
2186      * @param {Function} newFn The function to call before the original
2187      * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the passed function is executed.
2188      * <b>If omitted, defaults to the scope in which the original function is called or the browser window.</b>
2189      * @param {Mixed} returnValue (optional) The value to return if the passed function return false (defaults to null).
2190      * @return {Function} The new function
2191      */
2192     createInterceptor: function(origFn, newFn, scope, returnValue) {
2193         var method = origFn;
2194         if (!Ext.isFunction(newFn)) {
2195             return origFn;
2196         }
2197         else {
2198             return function() {
2199                 var me = this,
2200                     args = arguments;
2201                 newFn.target = me;
2202                 newFn.method = origFn;
2203                 return (newFn.apply(scope || me || window, args) !== false) ? origFn.apply(me || window, args) : returnValue || null;
2204             };
2205         }
2206     },
2207
2208     /**
2209     * Creates a delegate (callback) which, when called, executes after a specific delay.
2210     * @param {Function} fn The function which will be called on a delay when the returned function is called.
2211     * Optionally, a replacement (or additional) argument list may be specified.
2212     * @param {Number} delay The number of milliseconds to defer execution by whenever called.
2213     * @param {Object} scope (optional) The scope (<code>this</code> reference) used by the function at execution time.
2214     * @param {Array} args (optional) Override arguments for the call. (Defaults to the arguments passed by the caller)
2215     * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
2216     * if a number the args are inserted at the specified position.
2217     * @return {Function} A function which, when called, executes the original function after the specified delay.
2218     */
2219     createDelayed: function(fn, delay, scope, args, appendArgs) {
2220         if (scope || args) {
2221             fn = Ext.Function.bind(fn, scope, args, appendArgs);
2222         }
2223         return function() {
2224             var me = this;
2225             setTimeout(function() {
2226                 fn.apply(me, arguments);
2227             }, delay);
2228         };
2229     },
2230
2231     /**
2232      * Calls this function after the number of millseconds specified, optionally in a specific scope. Example usage:
2233      * <pre><code>
2234 var sayHi = function(name){
2235     alert('Hi, ' + name);
2236 }
2237
2238 // executes immediately:
2239 sayHi('Fred');
2240
2241 // executes after 2 seconds:
2242 Ext.Function.defer(sayHi, 2000, this, ['Fred']);
2243
2244 // this syntax is sometimes useful for deferring
2245 // execution of an anonymous function:
2246 Ext.Function.defer(function(){
2247     alert('Anonymous');
2248 }, 100);
2249      </code></pre>
2250      * @param {Function} fn The function to defer.
2251      * @param {Number} millis The number of milliseconds for the setTimeout call (if less than or equal to 0 the function is executed immediately)
2252      * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the function is executed.
2253      * <b>If omitted, defaults to the browser window.</b>
2254      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
2255      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
2256      * if a number the args are inserted at the specified position
2257      * @return {Number} The timeout id that can be used with clearTimeout
2258      */
2259     defer: function(fn, millis, obj, args, appendArgs) {
2260         fn = Ext.Function.bind(fn, obj, args, appendArgs);
2261         if (millis > 0) {
2262             return setTimeout(fn, millis);
2263         }
2264         fn();
2265         return 0;
2266     },
2267
2268     /**
2269      * Create a combined function call sequence of the original function + the passed function.
2270      * The resulting function returns the results of the original function.
2271      * The passed function is called with the parameters of the original function. Example usage:
2272      *
2273      * <pre><code>
2274 var sayHi = function(name){
2275     alert('Hi, ' + name);
2276 }
2277
2278 sayHi('Fred'); // alerts "Hi, Fred"
2279
2280 var sayGoodbye = Ext.Function.createSequence(sayHi, function(name){
2281     alert('Bye, ' + name);
2282 });
2283
2284 sayGoodbye('Fred'); // both alerts show
2285      * </code></pre>
2286      *
2287      * @param {Function} origFn The original function.
2288      * @param {Function} newFn The function to sequence
2289      * @param {Object} scope (optional) The scope (this reference) in which the passed function is executed.
2290      * If omitted, defaults to the scope in which the original function is called or the browser window.
2291      * @return {Function} The new function
2292      */
2293     createSequence: function(origFn, newFn, scope) {
2294         if (!Ext.isFunction(newFn)) {
2295             return origFn;
2296         }
2297         else {
2298             return function() {
2299                 var retval = origFn.apply(this || window, arguments);
2300                 newFn.apply(scope || this || window, arguments);
2301                 return retval;
2302             };
2303         }
2304     },
2305
2306     /**
2307      * <p>Creates a delegate function, optionally with a bound scope which, when called, buffers
2308      * the execution of the passed function for the configured number of milliseconds.
2309      * If called again within that period, the impending invocation will be canceled, and the
2310      * timeout period will begin again.</p>
2311      *
2312      * @param {Function} fn The function to invoke on a buffered timer.
2313      * @param {Number} buffer The number of milliseconds by which to buffer the invocation of the
2314      * function.
2315      * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which
2316      * the passed function is executed. If omitted, defaults to the scope specified by the caller.
2317      * @param {Array} args (optional) Override arguments for the call. Defaults to the arguments
2318      * passed by the caller.
2319      * @return {Function} A function which invokes the passed function after buffering for the specified time.
2320      */
2321     createBuffered: function(fn, buffer, scope, args) {
2322         return function(){
2323             var timerId;
2324             return function() {
2325                 var me = this;
2326                 if (timerId) {
2327                     clearInterval(timerId);
2328                     timerId = null;
2329                 }
2330                 timerId = setTimeout(function(){
2331                     fn.apply(scope || me, args || arguments);
2332                 }, buffer);
2333             };
2334         }();
2335     },
2336
2337     /**
2338      * <p>Creates a throttled version of the passed function which, when called repeatedly and
2339      * rapidly, invokes the passed function only after a certain interval has elapsed since the
2340      * previous invocation.</p>
2341      *
2342      * <p>This is useful for wrapping functions which may be called repeatedly, such as
2343      * a handler of a mouse move event when the processing is expensive.</p>
2344      *
2345      * @param fn {Function} The function to execute at a regular time interval.
2346      * @param interval {Number} The interval <b>in milliseconds</b> on which the passed function is executed.
2347      * @param scope (optional) The scope (<code><b>this</b></code> reference) in which
2348      * the passed function is executed. If omitted, defaults to the scope specified by the caller.
2349      * @returns {Function} A function which invokes the passed function at the specified interval.
2350      */
2351     createThrottled: function(fn, interval, scope) {
2352         var lastCallTime, elapsed, lastArgs, timer, execute = function() {
2353             fn.apply(scope || this, lastArgs);
2354             lastCallTime = new Date().getTime();
2355         };
2356
2357         return function() {
2358             elapsed = new Date().getTime() - lastCallTime;
2359             lastArgs = arguments;
2360
2361             clearTimeout(timer);
2362             if (!lastCallTime || (elapsed >= interval)) {
2363                 execute();
2364             } else {
2365                 timer = setTimeout(execute, interval - elapsed);
2366             }
2367         };
2368     }
2369 };
2370
2371 /**
2372  * Shorthand for {@link Ext.Function#defer}
2373  * @member Ext
2374  * @method defer
2375  */
2376 Ext.defer = Ext.Function.alias(Ext.Function, 'defer');
2377
2378 /**
2379  * Shorthand for {@link Ext.Function#pass}
2380  * @member Ext
2381  * @method pass
2382  */
2383 Ext.pass = Ext.Function.alias(Ext.Function, 'pass');
2384
2385 /**
2386  * Shorthand for {@link Ext.Function#bind}
2387  * @member Ext
2388  * @method bind
2389  */
2390 Ext.bind = Ext.Function.alias(Ext.Function, 'bind');
2391
2392 /**
2393  * @author Jacky Nguyen <jacky@sencha.com>
2394  * @docauthor Jacky Nguyen <jacky@sencha.com>
2395  * @class Ext.Object
2396  *
2397  * A collection of useful static methods to deal with objects
2398  *
2399  * @singleton
2400  */
2401
2402 (function() {
2403
2404 var ExtObject = Ext.Object = {
2405
2406     /**
2407      * Convert a `name` - `value` pair to an array of objects with support for nested structures; useful to construct
2408      * query strings. For example:
2409
2410     var objects = Ext.Object.toQueryObjects('hobbies', ['reading', 'cooking', 'swimming']);
2411
2412     // objects then equals:
2413     [
2414         { name: 'hobbies', value: 'reading' },
2415         { name: 'hobbies', value: 'cooking' },
2416         { name: 'hobbies', value: 'swimming' },
2417     ];
2418
2419     var objects = Ext.Object.toQueryObjects('dateOfBirth', {
2420         day: 3,
2421         month: 8,
2422         year: 1987,
2423         extra: {
2424             hour: 4
2425             minute: 30
2426         }
2427     }, true); // Recursive
2428
2429     // objects then equals:
2430     [
2431         { name: 'dateOfBirth[day]', value: 3 },
2432         { name: 'dateOfBirth[month]', value: 8 },
2433         { name: 'dateOfBirth[year]', value: 1987 },
2434         { name: 'dateOfBirth[extra][hour]', value: 4 },
2435         { name: 'dateOfBirth[extra][minute]', value: 30 },
2436     ];
2437
2438      * @param {String} name
2439      * @param {Mixed} value
2440      * @param {Boolean} recursive
2441      * @markdown
2442      */
2443     toQueryObjects: function(name, value, recursive) {
2444         var self = ExtObject.toQueryObjects,
2445             objects = [],
2446             i, ln;
2447
2448         if (Ext.isArray(value)) {
2449             for (i = 0, ln = value.length; i < ln; i++) {
2450                 if (recursive) {
2451                     objects = objects.concat(self(name + '[' + i + ']', value[i], true));
2452                 }
2453                 else {
2454                     objects.push({
2455                         name: name,
2456                         value: value[i]
2457                     });
2458                 }
2459             }
2460         }
2461         else if (Ext.isObject(value)) {
2462             for (i in value) {
2463                 if (value.hasOwnProperty(i)) {
2464                     if (recursive) {
2465                         objects = objects.concat(self(name + '[' + i + ']', value[i], true));
2466                     }
2467                     else {
2468                         objects.push({
2469                             name: name,
2470                             value: value[i]
2471                         });
2472                     }
2473                 }
2474             }
2475         }
2476         else {
2477             objects.push({
2478                 name: name,
2479                 value: value
2480             });
2481         }
2482
2483         return objects;
2484     },
2485
2486     /**
2487      * Takes an object and converts it to an encoded query string
2488
2489 - Non-recursive:
2490
2491     Ext.Object.toQueryString({foo: 1, bar: 2}); // returns "foo=1&bar=2"
2492     Ext.Object.toQueryString({foo: null, bar: 2}); // returns "foo=&bar=2"
2493     Ext.Object.toQueryString({'some price': '$300'}); // returns "some%20price=%24300"
2494     Ext.Object.toQueryString({date: new Date(2011, 0, 1)}); // returns "date=%222011-01-01T00%3A00%3A00%22"
2495     Ext.Object.toQueryString({colors: ['red', 'green', 'blue']}); // returns "colors=red&colors=green&colors=blue"
2496
2497 - Recursive:
2498
2499     Ext.Object.toQueryString({
2500         username: 'Jacky',
2501         dateOfBirth: {
2502             day: 1,
2503             month: 2,
2504             year: 1911
2505         },
2506         hobbies: ['coding', 'eating', 'sleeping', ['nested', 'stuff']]
2507     }, true); // returns the following string (broken down and url-decoded for ease of reading purpose):
2508               // username=Jacky
2509               //    &dateOfBirth[day]=1&dateOfBirth[month]=2&dateOfBirth[year]=1911
2510               //    &hobbies[0]=coding&hobbies[1]=eating&hobbies[2]=sleeping&hobbies[3][0]=nested&hobbies[3][1]=stuff
2511
2512      *
2513      * @param {Object} object The object to encode
2514      * @param {Boolean} recursive (optional) Whether or not to interpret the object in recursive format.
2515      * (PHP / Ruby on Rails servers and similar). Defaults to false
2516      * @return {String} queryString
2517      * @markdown
2518      */
2519     toQueryString: function(object, recursive) {
2520         var paramObjects = [],
2521             params = [],
2522             i, j, ln, paramObject, value;
2523
2524         for (i in object) {
2525             if (object.hasOwnProperty(i)) {
2526                 paramObjects = paramObjects.concat(ExtObject.toQueryObjects(i, object[i], recursive));
2527             }
2528         }
2529
2530         for (j = 0, ln = paramObjects.length; j < ln; j++) {
2531             paramObject = paramObjects[j];
2532             value = paramObject.value;
2533
2534             if (Ext.isEmpty(value)) {
2535                 value = '';
2536             }
2537             else if (Ext.isDate(value)) {
2538                 value = Ext.Date.toString(value);
2539             }
2540
2541             params.push(encodeURIComponent(paramObject.name) + '=' + encodeURIComponent(String(value)));
2542         }
2543
2544         return params.join('&');
2545     },
2546
2547     /**
2548      * Converts a query string back into an object.
2549      *
2550 - Non-recursive:
2551
2552     Ext.Object.fromQueryString(foo=1&bar=2); // returns {foo: 1, bar: 2}
2553     Ext.Object.fromQueryString(foo=&bar=2); // returns {foo: null, bar: 2}
2554     Ext.Object.fromQueryString(some%20price=%24300); // returns {'some price': '$300'}
2555     Ext.Object.fromQueryString(colors=red&colors=green&colors=blue); // returns {colors: ['red', 'green', 'blue']}
2556
2557 - Recursive:
2558
2559     Ext.Object.fromQueryString("username=Jacky&dateOfBirth[day]=1&dateOfBirth[month]=2&dateOfBirth[year]=1911&hobbies[0]=coding&hobbies[1]=eating&hobbies[2]=sleeping&hobbies[3][0]=nested&hobbies[3][1]=stuff", true);
2560
2561     // returns
2562     {
2563         username: 'Jacky',
2564         dateOfBirth: {
2565             day: '1',
2566             month: '2',
2567             year: '1911'
2568         },
2569         hobbies: ['coding', 'eating', 'sleeping', ['nested', 'stuff']]
2570     }
2571
2572      * @param {String} queryString The query string to decode
2573      * @param {Boolean} recursive (Optional) Whether or not to recursively decode the string. This format is supported by
2574      * PHP / Ruby on Rails servers and similar. Defaults to false
2575      * @return {Object}
2576      */
2577     fromQueryString: function(queryString, recursive) {
2578         var parts = queryString.replace(/^\?/, '').split('&'),
2579             object = {},
2580             temp, components, name, value, i, ln,
2581             part, j, subLn, matchedKeys, matchedName,
2582             keys, key, nextKey;
2583
2584         for (i = 0, ln = parts.length; i < ln; i++) {
2585             part = parts[i];
2586
2587             if (part.length > 0) {
2588                 components = part.split('=');
2589                 name = decodeURIComponent(components[0]);
2590                 value = (components[1] !== undefined) ? decodeURIComponent(components[1]) : '';
2591
2592                 if (!recursive) {
2593                     if (object.hasOwnProperty(name)) {
2594                         if (!Ext.isArray(object[name])) {
2595                             object[name] = [object[name]];
2596                         }
2597
2598                         object[name].push(value);
2599                     }
2600                     else {
2601                         object[name] = value;
2602                     }
2603                 }
2604                 else {
2605                     matchedKeys = name.match(/(\[):?([^\]]*)\]/g);
2606                     matchedName = name.match(/^([^\[]+)/);
2607
2608                     //<debug error>
2609                     if (!matchedName) {
2610                         Ext.Error.raise({
2611                             sourceClass: "Ext.Object",
2612                             sourceMethod: "fromQueryString",
2613                             queryString: queryString,
2614                             recursive: recursive,
2615                             msg: 'Malformed query string given, failed parsing name from "' + part + '"'
2616                         });
2617                     }
2618                     //</debug>
2619
2620                     name = matchedName[0];
2621                     keys = [];
2622
2623                     if (matchedKeys === null) {
2624                         object[name] = value;
2625                         continue;
2626                     }
2627
2628                     for (j = 0, subLn = matchedKeys.length; j < subLn; j++) {
2629                         key = matchedKeys[j];
2630                         key = (key.length === 2) ? '' : key.substring(1, key.length - 1);
2631                         keys.push(key);
2632                     }
2633
2634                     keys.unshift(name);
2635
2636                     temp = object;
2637
2638                     for (j = 0, subLn = keys.length; j < subLn; j++) {
2639                         key = keys[j];
2640
2641                         if (j === subLn - 1) {
2642                             if (Ext.isArray(temp) && key === '') {
2643                                 temp.push(value);
2644                             }
2645                             else {
2646                                 temp[key] = value;
2647                             }
2648                         }
2649                         else {
2650                             if (temp[key] === undefined || typeof temp[key] === 'string') {
2651                                 nextKey = keys[j+1];
2652
2653                                 temp[key] = (Ext.isNumeric(nextKey) || nextKey === '') ? [] : {};
2654                             }
2655
2656                             temp = temp[key];
2657                         }
2658                     }
2659                 }
2660             }
2661         }
2662
2663         return object;
2664     },
2665
2666     /**
2667      * Iterate through an object and invoke the given callback function for each iteration. The iteration can be stop
2668      * by returning `false` in the callback function. For example:
2669
2670     var person = {
2671         name: 'Jacky'
2672         hairColor: 'black'
2673         loves: ['food', 'sleeping', 'wife']
2674     };
2675
2676     Ext.Object.each(person, function(key, value, myself) {
2677         console.log(key + ":" + value);
2678
2679         if (key === 'hairColor') {
2680             return false; // stop the iteration
2681         }
2682     });
2683
2684      * @param {Object} object The object to iterate
2685      * @param {Function} fn The callback function. Passed arguments for each iteration are:
2686
2687 - {String} `key`
2688 - {Mixed} `value`
2689 - {Object} `object` The object itself
2690
2691      * @param {Object} scope (Optional) The execution scope (`this`) of the callback function
2692      * @markdown
2693      */
2694     each: function(object, fn, scope) {
2695         for (var property in object) {
2696             if (object.hasOwnProperty(property)) {
2697                 if (fn.call(scope || object, property, object[property], object) === false) {
2698                     return;
2699                 }
2700             }
2701         }
2702     },
2703
2704     /**
2705      * Merges any number of objects recursively without referencing them or their children.
2706
2707     var extjs = {
2708         companyName: 'Ext JS',
2709         products: ['Ext JS', 'Ext GWT', 'Ext Designer'],
2710         isSuperCool: true
2711         office: {
2712             size: 2000,
2713             location: 'Palo Alto',
2714             isFun: true
2715         }
2716     };
2717
2718     var newStuff = {
2719         companyName: 'Sencha Inc.',
2720         products: ['Ext JS', 'Ext GWT', 'Ext Designer', 'Sencha Touch', 'Sencha Animator'],
2721         office: {
2722             size: 40000,
2723             location: 'Redwood City'
2724         }
2725     };
2726
2727     var sencha = Ext.Object.merge(extjs, newStuff);
2728
2729     // extjs and sencha then equals to
2730     {
2731         companyName: 'Sencha Inc.',
2732         products: ['Ext JS', 'Ext GWT', 'Ext Designer', 'Sencha Touch', 'Sencha Animator'],
2733         isSuperCool: true
2734         office: {
2735             size: 30000,
2736             location: 'Redwood City'
2737             isFun: true
2738         }
2739     }
2740
2741      * @param {Object} object,...
2742      * @return {Object} merged The object that is created as a result of merging all the objects passed in.
2743      * @markdown
2744      */
2745     merge: function(source, key, value) {
2746         if (typeof key === 'string') {
2747             if (value && value.constructor === Object) {
2748                 if (source[key] && source[key].constructor === Object) {
2749                     ExtObject.merge(source[key], value);
2750                 }
2751                 else {
2752                     source[key] = Ext.clone(value);
2753                 }
2754             }
2755             else {
2756                 source[key] = value;
2757             }
2758
2759             return source;
2760         }
2761
2762         var i = 1,
2763             ln = arguments.length,
2764             object, property;
2765
2766         for (; i < ln; i++) {
2767             object = arguments[i];
2768
2769             for (property in object) {
2770                 if (object.hasOwnProperty(property)) {
2771                     ExtObject.merge(source, property, object[property]);
2772                 }
2773             }
2774         }
2775
2776         return source;
2777     },
2778
2779     /**
2780      * Returns the first matching key corresponding to the given value.
2781      * If no matching value is found, null is returned.
2782
2783     var person = {
2784         name: 'Jacky',
2785         loves: 'food'
2786     };
2787
2788     alert(Ext.Object.getKey(sencha, 'loves')); // alerts 'food'
2789
2790      * @param {Object} object
2791      * @param {Object} value The value to find
2792      * @markdown
2793      */
2794     getKey: function(object, value) {
2795         for (var property in object) {
2796             if (object.hasOwnProperty(property) && object[property] === value) {
2797                 return property;
2798             }
2799         }
2800
2801         return null;
2802     },
2803
2804     /**
2805      * Gets all values of the given object as an array.
2806
2807     var values = Ext.Object.getValues({
2808         name: 'Jacky',
2809         loves: 'food'
2810     }); // ['Jacky', 'food']
2811
2812      * @param {Object} object
2813      * @return {Array} An array of values from the object
2814      * @markdown
2815      */
2816     getValues: function(object) {
2817         var values = [],
2818             property;
2819
2820         for (property in object) {
2821             if (object.hasOwnProperty(property)) {
2822                 values.push(object[property]);
2823             }
2824         }
2825
2826         return values;
2827     },
2828
2829     /**
2830      * Gets all keys of the given object as an array.
2831
2832     var values = Ext.Object.getKeys({
2833         name: 'Jacky',
2834         loves: 'food'
2835     }); // ['name', 'loves']
2836
2837      * @param {Object} object
2838      * @return {Array} An array of keys from the object
2839      */
2840     getKeys: ('keys' in Object.prototype) ? Object.keys : function(object) {
2841         var keys = [],
2842             property;
2843
2844         for (property in object) {
2845             if (object.hasOwnProperty(property)) {
2846                 keys.push(property);
2847             }
2848         }
2849
2850         return keys;
2851     },
2852
2853     /**
2854      * Gets the total number of this object's own properties
2855
2856     var size = Ext.Object.getSize({
2857         name: 'Jacky',
2858         loves: 'food'
2859     }); // size equals 2
2860
2861      * @param {Object} object
2862      * @return {Number} size
2863      * @markdown
2864      */
2865     getSize: function(object) {
2866         var size = 0,
2867             property;
2868
2869         for (property in object) {
2870             if (object.hasOwnProperty(property)) {
2871                 size++;
2872             }
2873         }
2874
2875         return size;
2876     }
2877 };
2878
2879
2880 /**
2881  * A convenient alias method for {@link Ext.Object#merge}
2882  *
2883  * @member Ext
2884  * @method merge
2885  */
2886 Ext.merge = Ext.Object.merge;
2887
2888 /**
2889  * A convenient alias method for {@link Ext.Object#toQueryString}
2890  *
2891  * @member Ext
2892  * @method urlEncode
2893  * @deprecated 4.0.0 Use {@link Ext.Object#toQueryString Ext.Object.toQueryString} instead
2894  */
2895 Ext.urlEncode = function() {
2896     var args = Ext.Array.from(arguments),
2897         prefix = '';
2898
2899     // Support for the old `pre` argument
2900     if ((typeof args[1] === 'string')) {
2901         prefix = args[1] + '&';
2902         args[1] = false;
2903     }
2904
2905     return prefix + Ext.Object.toQueryString.apply(Ext.Object, args);
2906 };
2907
2908 /**
2909  * A convenient alias method for {@link Ext.Object#fromQueryString}
2910  *
2911  * @member Ext
2912  * @method urlDecode
2913  * @deprecated 4.0.0 Use {@link Ext.Object#fromQueryString Ext.Object.fromQueryString} instead
2914  */
2915 Ext.urlDecode = function() {
2916     return Ext.Object.fromQueryString.apply(Ext.Object, arguments);
2917 };
2918
2919 })();
2920
2921 /**
2922  * @class Ext.Date
2923  * A set of useful static methods to deal with date
2924  * Note that if Ext.Date is required and loaded, it will copy all methods / properties to
2925  * this object for convenience
2926  *
2927  * The date parsing and formatting syntax contains a subset of
2928  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
2929  * supported will provide results equivalent to their PHP versions.
2930  *
2931  * The following is a list of all currently supported formats:
2932  * <pre class="">
2933 Format  Description                                                               Example returned values
2934 ------  -----------------------------------------------------------------------   -----------------------
2935   d     Day of the month, 2 digits with leading zeros                             01 to 31
2936   D     A short textual representation of the day of the week                     Mon to Sun
2937   j     Day of the month without leading zeros                                    1 to 31
2938   l     A full textual representation of the day of the week                      Sunday to Saturday
2939   N     ISO-8601 numeric representation of the day of the week                    1 (for Monday) through 7 (for Sunday)
2940   S     English ordinal suffix for the day of the month, 2 characters             st, nd, rd or th. Works well with j
2941   w     Numeric representation of the day of the week                             0 (for Sunday) to 6 (for Saturday)
2942   z     The day of the year (starting from 0)                                     0 to 364 (365 in leap years)
2943   W     ISO-8601 week number of year, weeks starting on Monday                    01 to 53
2944   F     A full textual representation of a month, such as January or March        January to December
2945   m     Numeric representation of a month, with leading zeros                     01 to 12
2946   M     A short textual representation of a month                                 Jan to Dec
2947   n     Numeric representation of a month, without leading zeros                  1 to 12
2948   t     Number of days in the given month                                         28 to 31
2949   L     Whether it&#39;s a leap year                                                  1 if it is a leap year, 0 otherwise.
2950   o     ISO-8601 year number (identical to (Y), but if the ISO week number (W)    Examples: 1998 or 2004
2951         belongs to the previous or next year, that year is used instead)
2952   Y     A full numeric representation of a year, 4 digits                         Examples: 1999 or 2003
2953   y     A two digit representation of a year                                      Examples: 99 or 03
2954   a     Lowercase Ante meridiem and Post meridiem                                 am or pm
2955   A     Uppercase Ante meridiem and Post meridiem                                 AM or PM
2956   g     12-hour format of an hour without leading zeros                           1 to 12
2957   G     24-hour format of an hour without leading zeros                           0 to 23
2958   h     12-hour format of an hour with leading zeros                              01 to 12
2959   H     24-hour format of an hour with leading zeros                              00 to 23
2960   i     Minutes, with leading zeros                                               00 to 59
2961   s     Seconds, with leading zeros                                               00 to 59
2962   u     Decimal fraction of a second                                              Examples:
2963         (minimum 1 digit, arbitrary number of digits allowed)                     001 (i.e. 0.001s) or
2964                                                                                   100 (i.e. 0.100s) or
2965                                                                                   999 (i.e. 0.999s) or
2966                                                                                   999876543210 (i.e. 0.999876543210s)
2967   O     Difference to Greenwich time (GMT) in hours and minutes                   Example: +1030
2968   P     Difference to Greenwich time (GMT) with colon between hours and minutes   Example: -08:00
2969   T     Timezone abbreviation of the machine running the code                     Examples: EST, MDT, PDT ...
2970   Z     Timezone offset in seconds (negative if west of UTC, positive if east)    -43200 to 50400
2971   c     ISO 8601 date
2972         Notes:                                                                    Examples:
2973         1) If unspecified, the month / day defaults to the current month / day,   1991 or
2974            the time defaults to midnight, while the timezone defaults to the      1992-10 or
2975            browser's timezone. If a time is specified, it must include both hours 1993-09-20 or
2976            and minutes. The "T" delimiter, seconds, milliseconds and timezone     1994-08-19T16:20+01:00 or
2977            are optional.                                                          1995-07-18T17:21:28-02:00 or
2978         2) The decimal fraction of a second, if specified, must contain at        1996-06-17T18:22:29.98765+03:00 or
2979            least 1 digit (there is no limit to the maximum number                 1997-05-16T19:23:30,12345-0400 or
2980            of digits allowed), and may be delimited by either a '.' or a ','      1998-04-15T20:24:31.2468Z or
2981         Refer to the examples on the right for the various levels of              1999-03-14T20:24:32Z or
2982         date-time granularity which are supported, or see                         2000-02-13T21:25:33
2983         http://www.w3.org/TR/NOTE-datetime for more info.                         2001-01-12 22:26:34
2984   U     Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)                1193432466 or -2138434463
2985   MS    Microsoft AJAX serialized dates                                           \/Date(1238606590509)\/ (i.e. UTC milliseconds since epoch) or
2986                                                                                   \/Date(1238606590509+0800)\/
2987 </pre>
2988  *
2989  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
2990  * <pre><code>
2991 // Sample date:
2992 // 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
2993
2994 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
2995 console.log(Ext.Date.format(dt, 'Y-m-d'));                          // 2007-01-10
2996 console.log(Ext.Date.format(dt, 'F j, Y, g:i a'));                  // January 10, 2007, 3:05 pm
2997 console.log(Ext.Date.format(dt, 'l, \\t\\he jS \\of F Y h:i:s A')); // Wednesday, the 10th of January 2007 03:05:01 PM
2998 </code></pre>
2999  *
3000  * Here are some standard date/time patterns that you might find helpful.  They
3001  * are not part of the source of Ext.Date, but to use them you can simply copy this
3002  * block of code into any script that is included after Ext.Date and they will also become
3003  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
3004  * <pre><code>
3005 Ext.Date.patterns = {
3006     ISO8601Long:"Y-m-d H:i:s",
3007     ISO8601Short:"Y-m-d",
3008     ShortDate: "n/j/Y",
3009     LongDate: "l, F d, Y",
3010     FullDateTime: "l, F d, Y g:i:s A",
3011     MonthDay: "F d",
3012     ShortTime: "g:i A",
3013     LongTime: "g:i:s A",
3014     SortableDateTime: "Y-m-d\\TH:i:s",
3015     UniversalSortableDateTime: "Y-m-d H:i:sO",
3016     YearMonth: "F, Y"
3017 };
3018 </code></pre>
3019  *
3020  * Example usage:
3021  * <pre><code>
3022 var dt = new Date();
3023 console.log(Ext.Date.format(dt, Ext.Date.patterns.ShortDate));
3024 </code></pre>
3025  * <p>Developer-written, custom formats may be used by supplying both a formatting and a parsing function
3026  * which perform to specialized requirements. The functions are stored in {@link #parseFunctions} and {@link #formatFunctions}.</p>
3027  * @singleton
3028  */
3029
3030 /*
3031  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
3032  * (see http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/)
3033  * They generate precompiled functions from format patterns instead of parsing and
3034  * processing each pattern every time a date is formatted. These functions are available
3035  * on every Date object.
3036  */
3037
3038 (function() {
3039
3040 // create private copy of Ext's Ext.util.Format.format() method
3041 // - to remove unnecessary dependency
3042 // - to resolve namespace conflict with MS-Ajax's implementation
3043 function xf(format) {
3044     var args = Array.prototype.slice.call(arguments, 1);
3045     return format.replace(/\{(\d+)\}/g, function(m, i) {
3046         return args[i];
3047     });
3048 }
3049
3050 Ext.Date = {
3051     /**
3052      * Returns the current timestamp
3053      * @return {Date} The current timestamp
3054      */
3055     now: Date.now || function() {
3056         return +new Date();
3057     },
3058
3059     /**
3060      * @private
3061      * Private for now
3062      */
3063     toString: function(date) {
3064         var pad = Ext.String.leftPad;
3065
3066         return date.getFullYear() + "-"
3067             + pad(date.getMonth() + 1, 2, '0') + "-"
3068             + pad(date.getDate(), 2, '0') + "T"
3069             + pad(date.getHours(), 2, '0') + ":"
3070             + pad(date.getMinutes(), 2, '0') + ":"
3071             + pad(date.getSeconds(), 2, '0');
3072     },
3073
3074     /**
3075      * Returns the number of milliseconds between two dates
3076      * @param {Date} dateA The first date
3077      * @param {Date} dateB (optional) The second date, defaults to now
3078      * @return {Number} The difference in milliseconds
3079      */
3080     getElapsed: function(dateA, dateB) {
3081         return Math.abs(dateA - (dateB || new Date()));
3082     },
3083
3084     /**
3085      * Global flag which determines if strict date parsing should be used.
3086      * Strict date parsing will not roll-over invalid dates, which is the
3087      * default behaviour of javascript Date objects.
3088      * (see {@link #parse} for more information)
3089      * Defaults to <tt>false</tt>.
3090      * @static
3091      * @type Boolean
3092     */
3093     useStrict: false,
3094
3095     // private
3096     formatCodeToRegex: function(character, currentGroup) {
3097         // Note: currentGroup - position in regex result array (see notes for Ext.Date.parseCodes below)
3098         var p = utilDate.parseCodes[character];
3099
3100         if (p) {
3101           p = typeof p == 'function'? p() : p;
3102           utilDate.parseCodes[character] = p; // reassign function result to prevent repeated execution
3103         }
3104
3105         return p ? Ext.applyIf({
3106           c: p.c ? xf(p.c, currentGroup || "{0}") : p.c
3107         }, p) : {
3108             g: 0,
3109             c: null,
3110             s: Ext.String.escapeRegex(character) // treat unrecognised characters as literals
3111         };
3112     },
3113
3114     /**
3115      * <p>An object hash in which each property is a date parsing function. The property name is the
3116      * format string which that function parses.</p>
3117      * <p>This object is automatically populated with date parsing functions as
3118      * date formats are requested for Ext standard formatting strings.</p>
3119      * <p>Custom parsing functions may be inserted into this object, keyed by a name which from then on
3120      * may be used as a format string to {@link #parse}.<p>
3121      * <p>Example:</p><pre><code>
3122 Ext.Date.parseFunctions['x-date-format'] = myDateParser;
3123 </code></pre>
3124      * <p>A parsing function should return a Date object, and is passed the following parameters:<div class="mdetail-params"><ul>
3125      * <li><code>date</code> : String<div class="sub-desc">The date string to parse.</div></li>
3126      * <li><code>strict</code> : Boolean<div class="sub-desc">True to validate date strings while parsing
3127      * (i.e. prevent javascript Date "rollover") (The default must be false).
3128      * Invalid date strings should return null when parsed.</div></li>
3129      * </ul></div></p>
3130      * <p>To enable Dates to also be <i>formatted</i> according to that format, a corresponding
3131      * formatting function must be placed into the {@link #formatFunctions} property.
3132      * @property parseFunctions
3133      * @static
3134      * @type Object
3135      */
3136     parseFunctions: {
3137         "MS": function(input, strict) {
3138             // note: the timezone offset is ignored since the MS Ajax server sends
3139             // a UTC milliseconds-since-Unix-epoch value (negative values are allowed)
3140             var re = new RegExp('\\/Date\\(([-+])?(\\d+)(?:[+-]\\d{4})?\\)\\/');
3141             var r = (input || '').match(re);
3142             return r? new Date(((r[1] || '') + r[2]) * 1) : null;
3143         }
3144     },
3145     parseRegexes: [],
3146
3147     /**
3148      * <p>An object hash in which each property is a date formatting function. The property name is the
3149      * format string which corresponds to the produced formatted date string.</p>
3150      * <p>This object is automatically populated with date formatting functions as
3151      * date formats are requested for Ext standard formatting strings.</p>
3152      * <p>Custom formatting functions may be inserted into this object, keyed by a name which from then on
3153      * may be used as a format string to {@link #format}. Example:</p><pre><code>
3154 Ext.Date.formatFunctions['x-date-format'] = myDateFormatter;
3155 </code></pre>
3156      * <p>A formatting function should return a string representation of the passed Date object, and is passed the following parameters:<div class="mdetail-params"><ul>
3157      * <li><code>date</code> : Date<div class="sub-desc">The Date to format.</div></li>
3158      * </ul></div></p>
3159      * <p>To enable date strings to also be <i>parsed</i> according to that format, a corresponding
3160      * parsing function must be placed into the {@link #parseFunctions} property.
3161      * @property formatFunctions
3162      * @static
3163      * @type Object
3164      */
3165     formatFunctions: {
3166         "MS": function() {
3167             // UTC milliseconds since Unix epoch (MS-AJAX serialized date format (MRSF))
3168             return '\\/Date(' + this.getTime() + ')\\/';
3169         }
3170     },
3171
3172     y2kYear : 50,
3173
3174     /**
3175      * Date interval constant
3176      * @static
3177      * @type String
3178      */
3179     MILLI : "ms",
3180
3181     /**
3182      * Date interval constant
3183      * @static
3184      * @type String
3185      */
3186     SECOND : "s",
3187
3188     /**
3189      * Date interval constant
3190      * @static
3191      * @type String
3192      */
3193     MINUTE : "mi",
3194
3195     /** Date interval constant
3196      * @static
3197      * @type String
3198      */
3199     HOUR : "h",
3200
3201     /**
3202      * Date interval constant
3203      * @static
3204      * @type String
3205      */
3206     DAY : "d",
3207
3208     /**
3209      * Date interval constant
3210      * @static
3211      * @type String
3212      */
3213     MONTH : "mo",
3214
3215     /**
3216      * Date interval constant
3217      * @static
3218      * @type String
3219      */
3220     YEAR : "y",
3221
3222     /**
3223      * <p>An object hash containing default date values used during date parsing.</p>
3224      * <p>The following properties are available:<div class="mdetail-params"><ul>
3225      * <li><code>y</code> : Number<div class="sub-desc">The default year value. (defaults to undefined)</div></li>
3226      * <li><code>m</code> : Number<div class="sub-desc">The default 1-based month value. (defaults to undefined)</div></li>
3227      * <li><code>d</code> : Number<div class="sub-desc">The default day value. (defaults to undefined)</div></li>
3228      * <li><code>h</code> : Number<div class="sub-desc">The default hour value. (defaults to undefined)</div></li>
3229      * <li><code>i</code> : Number<div class="sub-desc">The default minute value. (defaults to undefined)</div></li>
3230      * <li><code>s</code> : Number<div class="sub-desc">The default second value. (defaults to undefined)</div></li>
3231      * <li><code>ms</code> : Number<div class="sub-desc">The default millisecond value. (defaults to undefined)</div></li>
3232      * </ul></div></p>
3233      * <p>Override these properties to customize the default date values used by the {@link #parse} method.</p>
3234      * <p><b>Note: In countries which experience Daylight Saving Time (i.e. DST), the <tt>h</tt>, <tt>i</tt>, <tt>s</tt>
3235      * and <tt>ms</tt> properties may coincide with the exact time in which DST takes effect.
3236      * It is the responsiblity of the developer to account for this.</b></p>
3237      * Example Usage:
3238      * <pre><code>
3239 // set default day value to the first day of the month
3240 Ext.Date.defaults.d = 1;
3241
3242 // parse a February date string containing only year and month values.
3243 // setting the default day value to 1 prevents weird date rollover issues
3244 // when attempting to parse the following date string on, for example, March 31st 2009.
3245 Ext.Date.parse('2009-02', 'Y-m'); // returns a Date object representing February 1st 2009
3246 </code></pre>
3247      * @property defaults
3248      * @static
3249      * @type Object
3250      */
3251     defaults: {},
3252
3253     /**
3254      * An array of textual day names.
3255      * Override these values for international dates.
3256      * Example:
3257      * <pre><code>
3258 Ext.Date.dayNames = [
3259     'SundayInYourLang',
3260     'MondayInYourLang',
3261     ...
3262 ];
3263 </code></pre>
3264      * @type Array
3265      * @static
3266      */
3267     dayNames : [
3268         "Sunday",
3269         "Monday",
3270         "Tuesday",
3271         "Wednesday",
3272         "Thursday",
3273         "Friday",
3274         "Saturday"
3275     ],
3276
3277     /**
3278      * An array of textual month names.
3279      * Override these values for international dates.
3280      * Example:
3281      * <pre><code>
3282 Ext.Date.monthNames = [
3283     'JanInYourLang',
3284     'FebInYourLang',
3285     ...
3286 ];
3287 </code></pre>
3288      * @type Array
3289      * @static
3290      */
3291     monthNames : [
3292         "January",
3293         "February",
3294         "March",
3295         "April",
3296         "May",
3297         "June",
3298         "July",
3299         "August",
3300         "September",
3301         "October",
3302         "November",
3303         "December"
3304     ],
3305
3306     /**
3307      * An object hash of zero-based javascript month numbers (with short month names as keys. note: keys are case-sensitive).
3308      * Override these values for international dates.
3309      * Example:
3310      * <pre><code>
3311 Ext.Date.monthNumbers = {
3312     'ShortJanNameInYourLang':0,
3313     'ShortFebNameInYourLang':1,
3314     ...
3315 };
3316 </code></pre>
3317      * @type Object
3318      * @static
3319      */
3320     monthNumbers : {
3321         Jan:0,
3322         Feb:1,
3323         Mar:2,
3324         Apr:3,
3325         May:4,
3326         Jun:5,
3327         Jul:6,
3328         Aug:7,
3329         Sep:8,
3330         Oct:9,
3331         Nov:10,
3332         Dec:11
3333     },
3334     /**
3335      * <p>The date format string that the {@link #dateRenderer} and {@link #date} functions use.
3336      * see {@link #Date} for details.</p>
3337      * <p>This defaults to <code>m/d/Y</code>, but may be overridden in a locale file.</p>
3338      * @property defaultFormat
3339      * @static
3340      * @type String
3341      */
3342     defaultFormat : "m/d/Y",
3343     /**
3344      * Get the short month name for the given month number.
3345      * Override this function for international dates.
3346      * @param {Number} month A zero-based javascript month number.
3347      * @return {String} The short month name.
3348      * @static
3349      */
3350     getShortMonthName : function(month) {
3351         return utilDate.monthNames[month].substring(0, 3);
3352     },
3353
3354     /**
3355      * Get the short day name for the given day number.
3356      * Override this function for international dates.
3357      * @param {Number} day A zero-based javascript day number.
3358      * @return {String} The short day name.
3359      * @static
3360      */
3361     getShortDayName : function(day) {
3362         return utilDate.dayNames[day].substring(0, 3);
3363     },
3364
3365     /**
3366      * Get the zero-based javascript month number for the given short/full month name.
3367      * Override this function for international dates.
3368      * @param {String} name The short/full month name.
3369      * @return {Number} The zero-based javascript month number.
3370      * @static
3371      */
3372     getMonthNumber : function(name) {
3373         // handle camel casing for english month names (since the keys for the Ext.Date.monthNumbers hash are case sensitive)
3374         return utilDate.monthNumbers[name.substring(0, 1).toUpperCase() + name.substring(1, 3).toLowerCase()];
3375     },
3376
3377     /**
3378      * Checks if the specified format contains hour information
3379      * @param {String} format The format to check
3380      * @return {Boolean} True if the format contains hour information
3381      * @static
3382      */
3383     formatContainsHourInfo : (function(){
3384         var stripEscapeRe = /(\\.)/g,
3385             hourInfoRe = /([gGhHisucUOPZ]|MS)/;
3386         return function(format){
3387             return hourInfoRe.test(format.replace(stripEscapeRe, ''));
3388         };
3389     })(),
3390
3391     /**
3392      * Checks if the specified format contains information about
3393      * anything other than the time.
3394      * @param {String} format The format to check
3395      * @return {Boolean} True if the format contains information about
3396      * date/day information.
3397      * @static
3398      */
3399     formatContainsDateInfo : (function(){
3400         var stripEscapeRe = /(\\.)/g,
3401             dateInfoRe = /([djzmnYycU]|MS)/;
3402
3403         return function(format){
3404             return dateInfoRe.test(format.replace(stripEscapeRe, ''));
3405         };
3406     })(),
3407
3408     /**
3409      * The base format-code to formatting-function hashmap used by the {@link #format} method.
3410      * Formatting functions are strings (or functions which return strings) which
3411      * will return the appropriate value when evaluated in the context of the Date object
3412      * from which the {@link #format} method is called.
3413      * Add to / override these mappings for custom date formatting.
3414      * Note: Ext.Date.format() treats characters as literals if an appropriate mapping cannot be found.
3415      * Example:
3416      * <pre><code>
3417 Ext.Date.formatCodes.x = "Ext.util.Format.leftPad(this.getDate(), 2, '0')";
3418 console.log(Ext.Date.format(new Date(), 'X'); // returns the current day of the month
3419 </code></pre>
3420      * @type Object
3421      * @static
3422      */
3423     formatCodes : {
3424         d: "Ext.String.leftPad(this.getDate(), 2, '0')",
3425         D: "Ext.Date.getShortDayName(this.getDay())", // get localised short day name
3426         j: "this.getDate()",
3427         l: "Ext.Date.dayNames[this.getDay()]",
3428         N: "(this.getDay() ? this.getDay() : 7)",
3429         S: "Ext.Date.getSuffix(this)",
3430         w: "this.getDay()",
3431         z: "Ext.Date.getDayOfYear(this)",
3432         W: "Ext.String.leftPad(Ext.Date.getWeekOfYear(this), 2, '0')",
3433         F: "Ext.Date.monthNames[this.getMonth()]",
3434         m: "Ext.String.leftPad(this.getMonth() + 1, 2, '0')",
3435         M: "Ext.Date.getShortMonthName(this.getMonth())", // get localised short month name
3436         n: "(this.getMonth() + 1)",
3437         t: "Ext.Date.getDaysInMonth(this)",
3438         L: "(Ext.Date.isLeapYear(this) ? 1 : 0)",
3439         o: "(this.getFullYear() + (Ext.Date.getWeekOfYear(this) == 1 && this.getMonth() > 0 ? +1 : (Ext.Date.getWeekOfYear(this) >= 52 && this.getMonth() < 11 ? -1 : 0)))",
3440         Y: "Ext.String.leftPad(this.getFullYear(), 4, '0')",
3441         y: "('' + this.getFullYear()).substring(2, 4)",
3442         a: "(this.getHours() < 12 ? 'am' : 'pm')",
3443         A: "(this.getHours() < 12 ? 'AM' : 'PM')",
3444         g: "((this.getHours() % 12) ? this.getHours() % 12 : 12)",
3445         G: "this.getHours()",
3446         h: "Ext.String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0')",
3447         H: "Ext.String.leftPad(this.getHours(), 2, '0')",
3448         i: "Ext.String.leftPad(this.getMinutes(), 2, '0')",
3449         s: "Ext.String.leftPad(this.getSeconds(), 2, '0')",
3450         u: "Ext.String.leftPad(this.getMilliseconds(), 3, '0')",
3451         O: "Ext.Date.getGMTOffset(this)",
3452         P: "Ext.Date.getGMTOffset(this, true)",
3453         T: "Ext.Date.getTimezone(this)",
3454         Z: "(this.getTimezoneOffset() * -60)",
3455
3456         c: function() { // ISO-8601 -- GMT format
3457             for (var c = "Y-m-dTH:i:sP", code = [], i = 0, l = c.length; i < l; ++i) {
3458                 var e = c.charAt(i);
3459                 code.push(e == "T" ? "'T'" : utilDate.getFormatCode(e)); // treat T as a character literal
3460             }
3461             return code.join(" + ");
3462         },
3463         /*
3464         c: function() { // ISO-8601 -- UTC format
3465             return [
3466               "this.getUTCFullYear()", "'-'",
3467               "Ext.util.Format.leftPad(this.getUTCMonth() + 1, 2, '0')", "'-'",
3468               "Ext.util.Format.leftPad(this.getUTCDate(), 2, '0')",
3469               "'T'",
3470               "Ext.util.Format.leftPad(this.getUTCHours(), 2, '0')", "':'",
3471               "Ext.util.Format.leftPad(this.getUTCMinutes(), 2, '0')", "':'",
3472               "Ext.util.Format.leftPad(this.getUTCSeconds(), 2, '0')",
3473               "'Z'"
3474             ].join(" + ");
3475         },
3476         */
3477
3478         U: "Math.round(this.getTime() / 1000)"
3479     },
3480
3481     /**
3482      * Checks if the passed Date parameters will cause a javascript Date "rollover".
3483      * @param {Number} year 4-digit year
3484      * @param {Number} month 1-based month-of-year
3485      * @param {Number} day Day of month
3486      * @param {Number} hour (optional) Hour
3487      * @param {Number} minute (optional) Minute
3488      * @param {Number} second (optional) Second
3489      * @param {Number} millisecond (optional) Millisecond
3490      * @return {Boolean} true if the passed parameters do not cause a Date "rollover", false otherwise.
3491      * @static
3492      */
3493     isValid : function(y, m, d, h, i, s, ms) {
3494         // setup defaults
3495         h = h || 0;
3496         i = i || 0;
3497         s = s || 0;
3498         ms = ms || 0;
3499
3500         // Special handling for year < 100
3501         var dt = utilDate.add(new Date(y < 100 ? 100 : y, m - 1, d, h, i, s, ms), utilDate.YEAR, y < 100 ? y - 100 : 0);
3502
3503         return y == dt.getFullYear() &&
3504             m == dt.getMonth() + 1 &&
3505             d == dt.getDate() &&
3506             h == dt.getHours() &&
3507             i == dt.getMinutes() &&
3508             s == dt.getSeconds() &&
3509             ms == dt.getMilliseconds();
3510     },
3511
3512     /**
3513      * Parses the passed string using the specified date format.
3514      * Note that this function expects normal calendar dates, meaning that months are 1-based (i.e. 1 = January).
3515      * The {@link #defaults} hash will be used for any date value (i.e. year, month, day, hour, minute, second or millisecond)
3516      * which cannot be found in the passed string. If a corresponding default date value has not been specified in the {@link #defaults} hash,
3517      * the current date's year, month, day or DST-adjusted zero-hour time value will be used instead.
3518      * Keep in mind that the input date string must precisely match the specified format string
3519      * in order for the parse operation to be successful (failed parse operations return a null value).
3520      * <p>Example:</p><pre><code>
3521 //dt = Fri May 25 2007 (current date)
3522 var dt = new Date();
3523
3524 //dt = Thu May 25 2006 (today&#39;s month/day in 2006)
3525 dt = Ext.Date.parse("2006", "Y");
3526
3527 //dt = Sun Jan 15 2006 (all date parts specified)
3528 dt = Ext.Date.parse("2006-01-15", "Y-m-d");
3529
3530 //dt = Sun Jan 15 2006 15:20:01
3531 dt = Ext.Date.parse("2006-01-15 3:20:01 PM", "Y-m-d g:i:s A");
3532
3533 // attempt to parse Sun Feb 29 2006 03:20:01 in strict mode
3534 dt = Ext.Date.parse("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null
3535 </code></pre>
3536      * @param {String} input The raw date string.
3537      * @param {String} format The expected date string format.
3538      * @param {Boolean} strict (optional) True to validate date strings while parsing (i.e. prevents javascript Date "rollover")
3539                         (defaults to false). Invalid date strings will return null when parsed.
3540      * @return {Date} The parsed Date.
3541      * @static
3542      */
3543     parse : function(input, format, strict) {
3544         var p = utilDate.parseFunctions;
3545         if (p[format] == null) {
3546             utilDate.createParser(format);
3547         }
3548         return p[format](input, Ext.isDefined(strict) ? strict : utilDate.useStrict);
3549     },
3550
3551     // Backwards compat
3552     parseDate: function(input, format, strict){
3553         return utilDate.parse(input, format, strict);
3554     },
3555
3556
3557     // private
3558     getFormatCode : function(character) {
3559         var f = utilDate.formatCodes[character];
3560
3561         if (f) {
3562           f = typeof f == 'function'? f() : f;
3563           utilDate.formatCodes[character] = f; // reassign function result to prevent repeated execution
3564         }
3565
3566         // note: unknown characters are treated as literals
3567         return f || ("'" + Ext.String.escape(character) + "'");
3568     },
3569
3570     // private
3571     createFormat : function(format) {
3572         var code = [],
3573             special = false,
3574             ch = '';
3575
3576         for (var i = 0; i < format.length; ++i) {
3577             ch = format.charAt(i);
3578             if (!special && ch == "\\") {
3579                 special = true;
3580             } else if (special) {
3581                 special = false;
3582                 code.push("'" + Ext.String.escape(ch) + "'");
3583             } else {
3584                 code.push(utilDate.getFormatCode(ch));
3585             }
3586         }
3587         utilDate.formatFunctions[format] = Ext.functionFactory("return " + code.join('+'));
3588     },
3589
3590     // private
3591     createParser : (function() {
3592         var code = [
3593             "var dt, y, m, d, h, i, s, ms, o, z, zz, u, v,",
3594                 "def = Ext.Date.defaults,",
3595                 "results = String(input).match(Ext.Date.parseRegexes[{0}]);", // either null, or an array of matched strings
3596
3597             "if(results){",
3598                 "{1}",
3599
3600                 "if(u != null){", // i.e. unix time is defined
3601                     "v = new Date(u * 1000);", // give top priority to UNIX time
3602                 "}else{",
3603                     // create Date object representing midnight of the current day;
3604                     // this will provide us with our date defaults
3605                     // (note: clearTime() handles Daylight Saving Time automatically)
3606                     "dt = Ext.Date.clearTime(new Date);",
3607
3608                     // date calculations (note: these calculations create a dependency on Ext.Number.from())
3609                     "y = Ext.Number.from(y, Ext.Number.from(def.y, dt.getFullYear()));",
3610                     "m = Ext.Number.from(m, Ext.Number.from(def.m - 1, dt.getMonth()));",
3611                     "d = Ext.Number.from(d, Ext.Number.from(def.d, dt.getDate()));",
3612
3613                     // time calculations (note: these calculations create a dependency on Ext.Number.from())
3614                     "h  = Ext.Number.from(h, Ext.Number.from(def.h, dt.getHours()));",
3615                     "i  = Ext.Number.from(i, Ext.Number.from(def.i, dt.getMinutes()));",
3616                     "s  = Ext.Number.from(s, Ext.Number.from(def.s, dt.getSeconds()));",
3617                     "ms = Ext.Number.from(ms, Ext.Number.from(def.ms, dt.getMilliseconds()));",
3618
3619                     "if(z >= 0 && y >= 0){",
3620                         // both the year and zero-based day of year are defined and >= 0.
3621                         // these 2 values alone provide sufficient info to create a full date object
3622
3623                         // create Date object representing January 1st for the given year
3624                         // handle years < 100 appropriately
3625                         "v = Ext.Date.add(new Date(y < 100 ? 100 : y, 0, 1, h, i, s, ms), Ext.Date.YEAR, y < 100 ? y - 100 : 0);",
3626
3627                         // then add day of year, checking for Date "rollover" if necessary
3628                         "v = !strict? v : (strict === true && (z <= 364 || (Ext.Date.isLeapYear(v) && z <= 365))? Ext.Date.add(v, Ext.Date.DAY, z) : null);",
3629                     "}else if(strict === true && !Ext.Date.isValid(y, m + 1, d, h, i, s, ms)){", // check for Date "rollover"
3630                         "v = null;", // invalid date, so return null
3631                     "}else{",
3632                         // plain old Date object
3633                         // handle years < 100 properly
3634                         "v = Ext.Date.add(new Date(y < 100 ? 100 : y, m, d, h, i, s, ms), Ext.Date.YEAR, y < 100 ? y - 100 : 0);",
3635                     "}",
3636                 "}",
3637             "}",
3638
3639             "if(v){",
3640                 // favour UTC offset over GMT offset
3641                 "if(zz != null){",
3642                     // reset to UTC, then add offset
3643                     "v = Ext.Date.add(v, Ext.Date.SECOND, -v.getTimezoneOffset() * 60 - zz);",
3644                 "}else if(o){",
3645                     // reset to GMT, then add offset
3646                     "v = Ext.Date.add(v, Ext.Date.MINUTE, -v.getTimezoneOffset() + (sn == '+'? -1 : 1) * (hr * 60 + mn));",
3647                 "}",
3648             "}",
3649
3650             "return v;"
3651         ].join('\n');
3652
3653         return function(format) {
3654             var regexNum = utilDate.parseRegexes.length,
3655                 currentGroup = 1,
3656                 calc = [],
3657                 regex = [],
3658                 special = false,
3659                 ch = "";
3660
3661             for (var i = 0; i < format.length; ++i) {
3662                 ch = format.charAt(i);
3663                 if (!special && ch == "\\") {
3664                     special = true;
3665                 } else if (special) {
3666                     special = false;
3667                     regex.push(Ext.String.escape(ch));
3668                 } else {
3669                     var obj = utilDate.formatCodeToRegex(ch, currentGroup);
3670                     currentGroup += obj.g;
3671                     regex.push(obj.s);
3672                     if (obj.g && obj.c) {
3673                         calc.push(obj.c);
3674                     }
3675                 }
3676             }
3677
3678             utilDate.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$", 'i');
3679             utilDate.parseFunctions[format] = Ext.functionFactory("input", "strict", xf(code, regexNum, calc.join('')));
3680         };
3681     })(),
3682
3683     // private
3684     parseCodes : {
3685         /*
3686          * Notes:
3687          * g = {Number} calculation group (0 or 1. only group 1 contributes to date calculations.)
3688          * c = {String} calculation method (required for group 1. null for group 0. {0} = currentGroup - position in regex result array)
3689          * s = {String} regex pattern. all matches are stored in results[], and are accessible by the calculation mapped to 'c'
3690          */
3691         d: {
3692             g:1,
3693             c:"d = parseInt(results[{0}], 10);\n",
3694             s:"(\\d{2})" // day of month with leading zeroes (01 - 31)
3695         },
3696         j: {
3697             g:1,
3698             c:"d = parseInt(results[{0}], 10);\n",
3699             s:"(\\d{1,2})" // day of month without leading zeroes (1 - 31)
3700         },
3701         D: function() {
3702             for (var a = [], i = 0; i < 7; a.push(utilDate.getShortDayName(i)), ++i); // get localised short day names
3703             return {
3704                 g:0,
3705                 c:null,
3706                 s:"(?:" + a.join("|") +")"
3707             };
3708         },
3709         l: function() {
3710             return {
3711                 g:0,
3712                 c:null,
3713                 s:"(?:" + utilDate.dayNames.join("|") + ")"
3714             };
3715         },
3716         N: {
3717             g:0,
3718             c:null,
3719             s:"[1-7]" // ISO-8601 day number (1 (monday) - 7 (sunday))
3720         },
3721         S: {
3722             g:0,
3723             c:null,
3724             s:"(?:st|nd|rd|th)"
3725         },
3726         w: {
3727             g:0,
3728             c:null,
3729             s:"[0-6]" // javascript day number (0 (sunday) - 6 (saturday))
3730         },
3731         z: {
3732             g:1,
3733             c:"z = parseInt(results[{0}], 10);\n",
3734             s:"(\\d{1,3})" // day of the year (0 - 364 (365 in leap years))
3735         },
3736         W: {
3737             g:0,
3738             c:null,
3739             s:"(?:\\d{2})" // ISO-8601 week number (with leading zero)
3740         },
3741         F: function() {
3742             return {
3743                 g:1,
3744                 c:"m = parseInt(Ext.Date.getMonthNumber(results[{0}]), 10);\n", // get localised month number
3745                 s:"(" + utilDate.monthNames.join("|") + ")"
3746             };
3747         },
3748         M: function() {
3749             for (var a = [], i = 0; i < 12; a.push(utilDate.getShortMonthName(i)), ++i); // get localised short month names
3750             return Ext.applyIf({
3751                 s:"(" + a.join("|") + ")"
3752             }, utilDate.formatCodeToRegex("F"));
3753         },
3754         m: {
3755             g:1,
3756             c:"m = parseInt(results[{0}], 10) - 1;\n",
3757             s:"(\\d{2})" // month number with leading zeros (01 - 12)
3758         },
3759         n: {
3760             g:1,
3761             c:"m = parseInt(results[{0}], 10) - 1;\n",
3762             s:"(\\d{1,2})" // month number without leading zeros (1 - 12)
3763         },
3764         t: {
3765             g:0,
3766             c:null,
3767             s:"(?:\\d{2})" // no. of days in the month (28 - 31)
3768         },
3769         L: {
3770             g:0,
3771             c:null,
3772             s:"(?:1|0)"
3773         },
3774         o: function() {
3775             return utilDate.formatCodeToRegex("Y");
3776         },
3777         Y: {
3778             g:1,
3779             c:"y = parseInt(results[{0}], 10);\n",
3780             s:"(\\d{4})" // 4-digit year
3781         },
3782         y: {
3783             g:1,
3784             c:"var ty = parseInt(results[{0}], 10);\n"
3785                 + "y = ty > Ext.Date.y2kYear ? 1900 + ty : 2000 + ty;\n", // 2-digit year
3786             s:"(\\d{1,2})"
3787         },
3788         /**
3789          * In the am/pm parsing routines, we allow both upper and lower case
3790          * even though it doesn't exactly match the spec. It gives much more flexibility
3791          * in being able to specify case insensitive regexes.
3792          */
3793         a: {
3794             g:1,
3795             c:"if (/(am)/i.test(results[{0}])) {\n"
3796                 + "if (!h || h == 12) { h = 0; }\n"
3797                 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
3798             s:"(am|pm|AM|PM)"
3799         },
3800         A: {
3801             g:1,
3802             c:"if (/(am)/i.test(results[{0}])) {\n"
3803                 + "if (!h || h == 12) { h = 0; }\n"
3804                 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
3805             s:"(AM|PM|am|pm)"
3806         },
3807         g: function() {
3808             return utilDate.formatCodeToRegex("G");
3809         },
3810         G: {
3811             g:1,
3812             c:"h = parseInt(results[{0}], 10);\n",
3813             s:"(\\d{1,2})" // 24-hr format of an hour without leading zeroes (0 - 23)
3814         },
3815         h: function() {
3816             return utilDate.formatCodeToRegex("H");
3817         },
3818         H: {
3819             g:1,
3820             c:"h = parseInt(results[{0}], 10);\n",
3821             s:"(\\d{2})" //  24-hr format of an hour with leading zeroes (00 - 23)
3822         },
3823         i: {
3824             g:1,
3825             c:"i = parseInt(results[{0}], 10);\n",
3826             s:"(\\d{2})" // minutes with leading zeros (00 - 59)
3827         },
3828         s: {
3829             g:1,
3830             c:"s = parseInt(results[{0}], 10);\n",
3831             s:"(\\d{2})" // seconds with leading zeros (00 - 59)
3832         },
3833         u: {
3834             g:1,
3835             c:"ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",
3836             s:"(\\d+)" // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
3837         },
3838         O: {
3839             g:1,
3840             c:[
3841                 "o = results[{0}];",
3842                 "var sn = o.substring(0,1),", // get + / - sign
3843                     "hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
3844                     "mn = o.substring(3,5) % 60;", // get minutes
3845                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))? (sn + Ext.String.leftPad(hr, 2, '0') + Ext.String.leftPad(mn, 2, '0')) : null;\n" // -12hrs <= GMT offset <= 14hrs
3846             ].join("\n"),
3847             s: "([+\-]\\d{4})" // GMT offset in hrs and mins
3848         },
3849         P: {
3850             g:1,
3851             c:[
3852                 "o = results[{0}];",
3853                 "var sn = o.substring(0,1),", // get + / - sign
3854                     "hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
3855                     "mn = o.substring(4,6) % 60;", // get minutes
3856                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))? (sn + Ext.String.leftPad(hr, 2, '0') + Ext.String.leftPad(mn, 2, '0')) : null;\n" // -12hrs <= GMT offset <= 14hrs
3857             ].join("\n"),
3858             s: "([+\-]\\d{2}:\\d{2})" // GMT offset in hrs and mins (with colon separator)
3859         },
3860         T: {
3861             g:0,
3862             c:null,
3863             s:"[A-Z]{1,4}" // timezone abbrev. may be between 1 - 4 chars
3864         },
3865         Z: {
3866             g:1,
3867             c:"zz = results[{0}] * 1;\n" // -43200 <= UTC offset <= 50400
3868                   + "zz = (-43200 <= zz && zz <= 50400)? zz : null;\n",
3869             s:"([+\-]?\\d{1,5})" // leading '+' sign is optional for UTC offset
3870         },
3871         c: function() {
3872             var calc = [],
3873                 arr = [
3874                     utilDate.formatCodeToRegex("Y", 1), // year
3875                     utilDate.formatCodeToRegex("m", 2), // month
3876                     utilDate.formatCodeToRegex("d", 3), // day
3877                     utilDate.formatCodeToRegex("h", 4), // hour
3878                     utilDate.formatCodeToRegex("i", 5), // minute
3879                     utilDate.formatCodeToRegex("s", 6), // second
3880                     {c:"ms = results[7] || '0'; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n"}, // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
3881                     {c:[ // allow either "Z" (i.e. UTC) or "-0530" or "+08:00" (i.e. UTC offset) timezone delimiters. assumes local timezone if no timezone is specified
3882                         "if(results[8]) {", // timezone specified
3883                             "if(results[8] == 'Z'){",
3884                                 "zz = 0;", // UTC
3885                             "}else if (results[8].indexOf(':') > -1){",
3886                                 utilDate.formatCodeToRegex("P", 8).c, // timezone offset with colon separator
3887                             "}else{",
3888                                 utilDate.formatCodeToRegex("O", 8).c, // timezone offset without colon separator
3889                             "}",
3890                         "}"
3891                     ].join('\n')}
3892                 ];
3893
3894             for (var i = 0, l = arr.length; i < l; ++i) {
3895                 calc.push(arr[i].c);
3896             }
3897
3898             return {
3899                 g:1,
3900                 c:calc.join(""),
3901                 s:[
3902                     arr[0].s, // year (required)
3903                     "(?:", "-", arr[1].s, // month (optional)
3904                         "(?:", "-", arr[2].s, // day (optional)
3905                             "(?:",
3906                                 "(?:T| )?", // time delimiter -- either a "T" or a single blank space
3907                                 arr[3].s, ":", arr[4].s,  // hour AND minute, delimited by a single colon (optional). MUST be preceded by either a "T" or a single blank space
3908                                 "(?::", arr[5].s, ")?", // seconds (optional)
3909                                 "(?:(?:\\.|,)(\\d+))?", // decimal fraction of a second (e.g. ",12345" or ".98765") (optional)
3910                                 "(Z|(?:[-+]\\d{2}(?::)?\\d{2}))?", // "Z" (UTC) or "-0530" (UTC offset without colon delimiter) or "+08:00" (UTC offset with colon delimiter) (optional)
3911                             ")?",
3912                         ")?",
3913                     ")?"
3914                 ].join("")
3915             };
3916         },
3917         U: {
3918             g:1,
3919             c:"u = parseInt(results[{0}], 10);\n",
3920             s:"(-?\\d+)" // leading minus sign indicates seconds before UNIX epoch
3921         }
3922     },
3923
3924     //Old Ext.Date prototype methods.
3925     // private
3926     dateFormat: function(date, format) {
3927         return utilDate.format(date, format);
3928     },
3929
3930     /**
3931      * Formats a date given the supplied format string.
3932      * @param {Date} date The date to format
3933      * @param {String} format The format string
3934      * @return {String} The formatted date
3935      */
3936     format: function(date, format) {
3937         if (utilDate.formatFunctions[format] == null) {
3938             utilDate.createFormat(format);
3939         }
3940         var result = utilDate.formatFunctions[format].call(date);
3941         return result + '';
3942     },
3943
3944     /**
3945      * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
3946      *
3947      * Note: The date string returned by the javascript Date object's toString() method varies
3948      * between browsers (e.g. FF vs IE) and system region settings (e.g. IE in Asia vs IE in America).
3949      * For a given date string e.g. "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)",
3950      * getTimezone() first tries to get the timezone abbreviation from between a pair of parentheses
3951      * (which may or may not be present), failing which it proceeds to get the timezone abbreviation
3952      * from the GMT offset portion of the date string.
3953      * @param {Date} date The date
3954      * @return {String} The abbreviated timezone name (e.g. 'CST', 'PDT', 'EDT', 'MPST' ...).
3955      */
3956     getTimezone : function(date) {
3957         // the following list shows the differences between date strings from different browsers on a WinXP SP2 machine from an Asian locale:
3958         //
3959         // Opera  : "Thu, 25 Oct 2007 22:53:45 GMT+0800" -- shortest (weirdest) date string of the lot
3960         // Safari : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone (same as FF)
3961         // FF     : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone
3962         // IE     : "Thu Oct 25 22:54:35 UTC+0800 2007" -- (Asian system setting) look for 3-4 letter timezone abbrev
3963         // IE     : "Thu Oct 25 17:06:37 PDT 2007" -- (American system setting) look for 3-4 letter timezone abbrev
3964         //
3965         // this crazy regex attempts to guess the correct timezone abbreviation despite these differences.
3966         // step 1: (?:\((.*)\) -- find timezone in parentheses
3967         // step 2: ([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?) -- if nothing was found in step 1, find timezone from timezone offset portion of date string
3968         // step 3: remove all non uppercase characters found in step 1 and 2
3969         return date.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, "");
3970     },
3971
3972     /**
3973      * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
3974      * @param {Date} date The date
3975      * @param {Boolean} colon (optional) true to separate the hours and minutes with a colon (defaults to false).
3976      * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600').
3977      */
3978     getGMTOffset : function(date, colon) {
3979         var offset = date.getTimezoneOffset();
3980         return (offset > 0 ? "-" : "+")
3981             + Ext.String.leftPad(Math.floor(Math.abs(offset) / 60), 2, "0")
3982             + (colon ? ":" : "")
3983             + Ext.String.leftPad(Math.abs(offset % 60), 2, "0");
3984     },
3985
3986     /**
3987      * Get the numeric day number of the year, adjusted for leap year.
3988      * @param {Date} date The date
3989      * @return {Number} 0 to 364 (365 in leap years).
3990      */
3991     getDayOfYear: function(date) {
3992         var num = 0,
3993             d = Ext.Date.clone(date),
3994             m = date.getMonth(),
3995             i;
3996
3997         for (i = 0, d.setDate(1), d.setMonth(0); i < m; d.setMonth(++i)) {
3998             num += utilDate.getDaysInMonth(d);
3999         }
4000         return num + date.getDate() - 1;
4001     },
4002
4003     /**
4004      * Get the numeric ISO-8601 week number of the year.
4005      * (equivalent to the format specifier 'W', but without a leading zero).
4006      * @param {Date} date The date
4007      * @return {Number} 1 to 53
4008      */
4009     getWeekOfYear : (function() {
4010         // adapted from http://www.merlyn.demon.co.uk/weekcalc.htm
4011         var ms1d = 864e5, // milliseconds in a day
4012             ms7d = 7 * ms1d; // milliseconds in a week
4013
4014         return function(date) { // return a closure so constants get calculated only once
4015             var DC3 = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate() + 3) / ms1d, // an Absolute Day Number
4016                 AWN = Math.floor(DC3 / 7), // an Absolute Week Number
4017                 Wyr = new Date(AWN * ms7d).getUTCFullYear();
4018
4019             return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
4020         };
4021     })(),
4022
4023     /**
4024      * Checks if the current date falls within a leap year.
4025      * @param {Date} date The date
4026      * @return {Boolean} True if the current date falls within a leap year, false otherwise.
4027      */
4028     isLeapYear : function(date) {
4029         var year = date.getFullYear();
4030         return !!((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
4031     },
4032
4033     /**
4034      * Get the first day of the current month, adjusted for leap year.  The returned value
4035      * is the numeric day index within the week (0-6) which can be used in conjunction with
4036      * the {@link #monthNames} array to retrieve the textual day name.
4037      * Example:
4038      * <pre><code>
4039 var dt = new Date('1/10/2007'),
4040     firstDay = Ext.Date.getFirstDayOfMonth(dt);
4041 console.log(Ext.Date.dayNames[firstDay]); //output: 'Monday'
4042      * </code></pre>
4043      * @param {Date} date The date
4044      * @return {Number} The day number (0-6).
4045      */
4046     getFirstDayOfMonth : function(date) {
4047         var day = (date.getDay() - (date.getDate() - 1)) % 7;
4048         return (day < 0) ? (day + 7) : day;
4049     },
4050
4051     /**
4052      * Get the last day of the current month, adjusted for leap year.  The returned value
4053      * is the numeric day index within the week (0-6) which can be used in conjunction with
4054      * the {@link #monthNames} array to retrieve the textual day name.
4055      * Example:
4056      * <pre><code>
4057 var dt = new Date('1/10/2007'),
4058     lastDay = Ext.Date.getLastDayOfMonth(dt);
4059 console.log(Ext.Date.dayNames[lastDay]); //output: 'Wednesday'
4060      * </code></pre>
4061      * @param {Date} date The date
4062      * @return {Number} The day number (0-6).
4063      */
4064     getLastDayOfMonth : function(date) {
4065         return utilDate.getLastDateOfMonth(date).getDay();
4066     },
4067
4068
4069     /**
4070      * Get the date of the first day of the month in which this date resides.
4071      * @param {Date} date The date
4072      * @return {Date}
4073      */
4074     getFirstDateOfMonth : function(date) {
4075         return new Date(date.getFullYear(), date.getMonth(), 1);
4076     },
4077
4078     /**
4079      * Get the date of the last day of the month in which this date resides.
4080      * @param {Date} date The date
4081      * @return {Date}
4082      */
4083     getLastDateOfMonth : function(date) {
4084         return new Date(date.getFullYear(), date.getMonth(), utilDate.getDaysInMonth(date));
4085     },
4086
4087     /**
4088      * Get the number of days in the current month, adjusted for leap year.
4089      * @param {Date} date The date
4090      * @return {Number} The number of days in the month.
4091      */
4092     getDaysInMonth: (function() {
4093         var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
4094
4095         return function(date) { // return a closure for efficiency
4096             var m = date.getMonth();
4097
4098             return m == 1 && utilDate.isLeapYear(date) ? 29 : daysInMonth[m];
4099         };
4100     })(),
4101
4102     /**
4103      * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
4104      * @param {Date} date The date
4105      * @return {String} 'st, 'nd', 'rd' or 'th'.
4106      */
4107     getSuffix : function(date) {
4108         switch (date.getDate()) {
4109             case 1:
4110             case 21:
4111             case 31:
4112                 return "st";
4113             case 2:
4114             case 22:
4115                 return "nd";
4116             case 3:
4117             case 23:
4118                 return "rd";
4119             default:
4120                 return "th";
4121         }
4122     },
4123
4124     /**
4125      * Creates and returns a new Date instance with the exact same date value as the called instance.
4126      * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
4127      * variable will also be changed.  When the intention is to create a new variable that will not
4128      * modify the original instance, you should create a clone.
4129      *
4130      * Example of correctly cloning a date:
4131      * <pre><code>
4132 //wrong way:
4133 var orig = new Date('10/1/2006');
4134 var copy = orig;
4135 copy.setDate(5);
4136 console.log(orig);  //returns 'Thu Oct 05 2006'!
4137
4138 //correct way:
4139 var orig = new Date('10/1/2006'),
4140     copy = Ext.Date.clone(orig);
4141 copy.setDate(5);
4142 console.log(orig);  //returns 'Thu Oct 01 2006'
4143      * </code></pre>
4144      * @param {Date} date The date
4145      * @return {Date} The new Date instance.
4146      */
4147     clone : function(date) {
4148         return new Date(date.getTime());
4149     },
4150
4151     /**
4152      * Checks if the current date is affected by Daylight Saving Time (DST).
4153      * @param {Date} date The date
4154      * @return {Boolean} True if the current date is affected by DST.
4155      */
4156     isDST : function(date) {
4157         // adapted from http://sencha.com/forum/showthread.php?p=247172#post247172
4158         // courtesy of @geoffrey.mcgill
4159         return new Date(date.getFullYear(), 0, 1).getTimezoneOffset() != date.getTimezoneOffset();
4160     },
4161
4162     /**
4163      * Attempts to clear all time information from this Date by setting the time to midnight of the same day,
4164      * automatically adjusting for Daylight Saving Time (DST) where applicable.
4165      * (note: DST timezone information for the browser's host operating system is assumed to be up-to-date)
4166      * @param {Date} date The date
4167      * @param {Boolean} clone true to create a clone of this date, clear the time and return it (defaults to false).
4168      * @return {Date} this or the clone.
4169      */
4170     clearTime : function(date, clone) {
4171         if (clone) {
4172             return Ext.Date.clearTime(Ext.Date.clone(date));
4173         }
4174
4175         // get current date before clearing time
4176         var d = date.getDate();
4177
4178         // clear time
4179         date.setHours(0);
4180         date.setMinutes(0);
4181         date.setSeconds(0);
4182         date.setMilliseconds(0);
4183
4184         if (date.getDate() != d) { // account for DST (i.e. day of month changed when setting hour = 0)
4185             // note: DST adjustments are assumed to occur in multiples of 1 hour (this is almost always the case)
4186             // refer to http://www.timeanddate.com/time/aboutdst.html for the (rare) exceptions to this rule
4187
4188             // increment hour until cloned date == current date
4189             for (var hr = 1, c = utilDate.add(date, Ext.Date.HOUR, hr); c.getDate() != d; hr++, c = utilDate.add(date, Ext.Date.HOUR, hr));
4190
4191             date.setDate(d);
4192             date.setHours(c.getHours());
4193         }
4194
4195         return date;
4196     },
4197
4198     /**
4199      * Provides a convenient method for performing basic date arithmetic. This method
4200      * does not modify the Date instance being called - it creates and returns
4201      * a new Date instance containing the resulting date value.
4202      *
4203      * Examples:
4204      * <pre><code>
4205 // Basic usage:
4206 var dt = Ext.Date.add(new Date('10/29/2006'), Ext.Date.DAY, 5);
4207 console.log(dt); //returns 'Fri Nov 03 2006 00:00:00'
4208
4209 // Negative values will be subtracted:
4210 var dt2 = Ext.Date.add(new Date('10/1/2006'), Ext.Date.DAY, -5);
4211 console.log(dt2); //returns 'Tue Sep 26 2006 00:00:00'
4212
4213      * </code></pre>
4214      *
4215      * @param {Date} date The date to modify
4216      * @param {String} interval A valid date interval enum value.
4217      * @param {Number} value The amount to add to the current date.
4218      * @return {Date} The new Date instance.
4219      */
4220     add : function(date, interval, value) {
4221         var d = Ext.Date.clone(date),
4222             Date = Ext.Date;
4223         if (!interval || value === 0) return d;
4224
4225         switch(interval.toLowerCase()) {
4226             case Ext.Date.MILLI:
4227                 d.setMilliseconds(d.getMilliseconds() + value);
4228                 break;
4229             case Ext.Date.SECOND:
4230                 d.setSeconds(d.getSeconds() + value);
4231                 break;
4232             case Ext.Date.MINUTE:
4233                 d.setMinutes(d.getMinutes() + value);
4234                 break;
4235             case Ext.Date.HOUR:
4236                 d.setHours(d.getHours() + value);
4237                 break;
4238             case Ext.Date.DAY:
4239                 d.setDate(d.getDate() + value);
4240                 break;
4241             case Ext.Date.MONTH:
4242                 var day = date.getDate();
4243                 if (day > 28) {
4244                     day = Math.min(day, Ext.Date.getLastDateOfMonth(Ext.Date.add(Ext.Date.getFirstDateOfMonth(date), 'mo', value)).getDate());
4245                 }
4246                 d.setDate(day);
4247                 d.setMonth(date.getMonth() + value);
4248                 break;
4249             case Ext.Date.YEAR:
4250                 d.setFullYear(date.getFullYear() + value);
4251                 break;
4252         }
4253         return d;
4254     },
4255
4256     /**
4257      * Checks if a date falls on or between the given start and end dates.
4258      * @param {Date} date The date to check
4259      * @param {Date} start Start date
4260      * @param {Date} end End date
4261      * @return {Boolean} true if this date falls on or between the given start and end dates.
4262      */
4263     between : function(date, start, end) {
4264         var t = date.getTime();
4265         return start.getTime() <= t && t <= end.getTime();
4266     },
4267
4268     //Maintains compatibility with old static and prototype window.Date methods.
4269     compat: function() {
4270         var nativeDate = window.Date,
4271             p, u,
4272             statics = ['useStrict', 'formatCodeToRegex', 'parseFunctions', 'parseRegexes', 'formatFunctions', 'y2kYear', 'MILLI', 'SECOND', 'MINUTE', 'HOUR', 'DAY', 'MONTH', 'YEAR', 'defaults', 'dayNames', 'monthNames', 'monthNumbers', 'getShortMonthName', 'getShortDayName', 'getMonthNumber', 'formatCodes', 'isValid', 'parseDate', 'getFormatCode', 'createFormat', 'createParser', 'parseCodes'],
4273             proto = ['dateFormat', 'format', 'getTimezone', 'getGMTOffset', 'getDayOfYear', 'getWeekOfYear', 'isLeapYear', 'getFirstDayOfMonth', 'getLastDayOfMonth', 'getDaysInMonth', 'getSuffix', 'clone', 'isDST', 'clearTime', 'add', 'between'];
4274
4275         //Append statics
4276         Ext.Array.forEach(statics, function(s) {
4277             nativeDate[s] = utilDate[s];
4278         });
4279
4280         //Append to prototype
4281         Ext.Array.forEach(proto, function(s) {
4282             nativeDate.prototype[s] = function() {
4283                 var args = Array.prototype.slice.call(arguments);
4284                 args.unshift(this);
4285                 return utilDate[s].apply(utilDate, args);
4286             };
4287         });
4288     }
4289 };
4290
4291 var utilDate = Ext.Date;
4292
4293 })();
4294
4295 /**
4296  * @author Jacky Nguyen <jacky@sencha.com>
4297  * @docauthor Jacky Nguyen <jacky@sencha.com>
4298  * @class Ext.Base
4299  *
4300  * The root of all classes created with {@link Ext#define}
4301  * All prototype and static members of this class are inherited by any other class
4302  *
4303  */
4304 (function(flexSetter) {
4305
4306 var Base = Ext.Base = function() {};
4307     Base.prototype = {
4308         $className: 'Ext.Base',
4309
4310         $class: Base,
4311
4312         /**
4313          * Get the reference to the current class from which this object was instantiated. Unlike {@link Ext.Base#statics},
4314          * `this.self` is scope-dependent and it's meant to be used for dynamic inheritance. See {@link Ext.Base#statics}
4315          * for a detailed comparison
4316
4317     Ext.define('My.Cat', {
4318         statics: {
4319             speciesName: 'Cat' // My.Cat.speciesName = 'Cat'
4320         },
4321
4322         constructor: function() {
4323             alert(this.self.speciesName); / dependent on 'this'
4324
4325             return this;
4326         },
4327
4328         clone: function() {
4329             return new this.self();
4330         }
4331     });
4332
4333
4334     Ext.define('My.SnowLeopard', {
4335         extend: 'My.Cat',
4336         statics: {
4337             speciesName: 'Snow Leopard'         // My.SnowLeopard.speciesName = 'Snow Leopard'
4338         }
4339     });
4340
4341     var cat = new My.Cat();                     // alerts 'Cat'
4342     var snowLeopard = new My.SnowLeopard();     // alerts 'Snow Leopard'
4343
4344     var clone = snowLeopard.clone();
4345     alert(Ext.getClassName(clone));             // alerts 'My.SnowLeopard'
4346
4347          * @type Class
4348          * @protected
4349          * @markdown
4350          */
4351         self: Base,
4352
4353         /**
4354          * Default constructor, simply returns `this`
4355          *
4356          * @constructor
4357          * @protected
4358          * @return {Object} this
4359          */
4360         constructor: function() {
4361             return this;
4362         },
4363
4364         /**
4365          * Initialize configuration for this class. a typical example:
4366
4367     Ext.define('My.awesome.Class', {
4368         // The default config
4369         config: {
4370             name: 'Awesome',
4371             isAwesome: true
4372         },
4373
4374         constructor: function(config) {
4375             this.initConfig(config);
4376
4377             return this;
4378         }
4379     });
4380
4381     var awesome = new My.awesome.Class({
4382         name: 'Super Awesome'
4383     });
4384
4385     alert(awesome.getName()); // 'Super Awesome'
4386
4387          * @protected
4388          * @param {Object} config
4389          * @return {Object} mixins The mixin prototypes as key - value pairs
4390          * @markdown
4391          */
4392         initConfig: function(config) {
4393             if (!this.$configInited) {
4394                 this.config = Ext.Object.merge({}, this.config || {}, config || {});
4395
4396                 this.applyConfig(this.config);
4397
4398                 this.$configInited = true;
4399             }
4400
4401             return this;
4402         },
4403
4404         /**
4405          * @private
4406          */
4407         setConfig: function(config) {
4408             this.applyConfig(config || {});
4409
4410             return this;
4411         },
4412
4413         /**
4414          * @private
4415          */
4416         applyConfig: flexSetter(function(name, value) {
4417             var setter = 'set' + Ext.String.capitalize(name);
4418
4419             if (typeof this[setter] === 'function') {
4420                 this[setter].call(this, value);
4421             }
4422
4423             return this;
4424         }),
4425
4426         /**
4427          * Call the parent's overridden method. For example:
4428
4429     Ext.define('My.own.A', {
4430         constructor: function(test) {
4431             alert(test);
4432         }
4433     });
4434
4435     Ext.define('My.own.B', {
4436         extend: 'My.own.A',
4437
4438         constructor: function(test) {
4439             alert(test);
4440
4441             this.callParent([test + 1]);
4442         }
4443     });
4444
4445     Ext.define('My.own.C', {
4446         extend: 'My.own.B',
4447
4448         constructor: function() {
4449             alert("Going to call parent's overriden constructor...");
4450
4451             this.callParent(arguments);
4452         }
4453     });
4454
4455     var a = new My.own.A(1); // alerts '1'
4456     var b = new My.own.B(1); // alerts '1', then alerts '2'
4457     var c = new My.own.C(2); // alerts "Going to call parent's overriden constructor..."
4458                              // alerts '2', then alerts '3'
4459
4460          * @protected
4461          * @param {Array/Arguments} args The arguments, either an array or the `arguments` object
4462          * from the current method, for example: `this.callParent(arguments)`
4463          * @return {Mixed} Returns the result from the superclass' method
4464          * @markdown
4465          */
4466         callParent: function(args) {
4467             var method = this.callParent.caller,
4468                 parentClass, methodName;
4469
4470             if (!method.$owner) {
4471                 //<debug error>
4472                 if (!method.caller) {
4473                     Ext.Error.raise({
4474                         sourceClass: Ext.getClassName(this),
4475                         sourceMethod: "callParent",
4476                         msg: "Attempting to call a protected method from the public scope, which is not allowed"
4477                     });
4478                 }
4479                 //</debug>
4480
4481                 method = method.caller;
4482             }
4483
4484             parentClass = method.$owner.superclass;
4485             methodName = method.$name;
4486
4487             //<debug error>
4488             if (!(methodName in parentClass)) {
4489                 Ext.Error.raise({
4490                     sourceClass: Ext.getClassName(this),
4491                     sourceMethod: methodName,
4492                     msg: "this.callParent() was called but there's no such method (" + methodName +
4493                          ") found in the parent class (" + (Ext.getClassName(parentClass) || 'Object') + ")"
4494                  });
4495             }
4496             //</debug>
4497
4498             return parentClass[methodName].apply(this, args || []);
4499         },
4500
4501
4502         /**
4503          * Get the reference to the class from which this object was instantiated. Note that unlike {@link Ext.Base#self},
4504          * `this.statics()` is scope-independent and it always returns the class from which it was called, regardless of what
4505          * `this` points to during run-time
4506
4507     Ext.define('My.Cat', {
4508         statics: {
4509             totalCreated: 0,
4510             speciesName: 'Cat' // My.Cat.speciesName = 'Cat'
4511         },
4512
4513         constructor: function() {
4514             var statics = this.statics();
4515
4516             alert(statics.speciesName);     // always equals to 'Cat' no matter what 'this' refers to
4517                                             // equivalent to: My.Cat.speciesName
4518
4519             alert(this.self.speciesName);   // dependent on 'this'
4520
4521             statics.totalCreated++;
4522
4523             return this;
4524         },
4525
4526         clone: function() {
4527             var cloned = new this.self;                      // dependent on 'this'
4528
4529             cloned.groupName = this.statics().speciesName;   // equivalent to: My.Cat.speciesName
4530
4531             return cloned;
4532         }
4533     });
4534
4535
4536     Ext.define('My.SnowLeopard', {
4537         extend: 'My.Cat',
4538
4539         statics: {
4540             speciesName: 'Snow Leopard'     // My.SnowLeopard.speciesName = 'Snow Leopard'
4541         },
4542
4543         constructor: function() {
4544             this.callParent();
4545         }
4546     });
4547
4548     var cat = new My.Cat();                 // alerts 'Cat', then alerts 'Cat'
4549
4550     var snowLeopard = new My.SnowLeopard(); // alerts 'Cat', then alerts 'Snow Leopard'
4551
4552     var clone = snowLeopard.clone();
4553     alert(Ext.getClassName(clone));         // alerts 'My.SnowLeopard'
4554     alert(clone.groupName);                 // alerts 'Cat'
4555
4556     alert(My.Cat.totalCreated);             // alerts 3
4557
4558          * @protected
4559          * @return {Class}
4560          * @markdown
4561          */
4562         statics: function() {
4563             var method = this.statics.caller,
4564                 self = this.self;
4565
4566             if (!method) {
4567                 return self;
4568             }
4569
4570             return method.$owner;
4571         },
4572
4573         /**
4574          * Call the original method that was previously overridden with {@link Ext.Base#override}
4575
4576     Ext.define('My.Cat', {
4577         constructor: function() {
4578             alert("I'm a cat!");
4579
4580             return this;
4581         }
4582     });
4583
4584     My.Cat.override({
4585         constructor: function() {
4586             alert("I'm going to be a cat!");
4587
4588             var instance = this.callOverridden();
4589
4590             alert("Meeeeoooowwww");
4591
4592             return instance;
4593         }
4594     });
4595
4596     var kitty = new My.Cat(); // alerts "I'm going to be a cat!"
4597                               // alerts "I'm a cat!"
4598                               // alerts "Meeeeoooowwww"
4599
4600          * @param {Array/Arguments} args The arguments, either an array or the `arguments` object
4601          * @return {Mixed} Returns the result after calling the overridden method
4602          * @markdown
4603          */
4604         callOverridden: function(args) {
4605             var method = this.callOverridden.caller;
4606
4607             //<debug error>
4608             if (!method.$owner) {
4609                 Ext.Error.raise({
4610                     sourceClass: Ext.getClassName(this),
4611                     sourceMethod: "callOverridden",
4612                     msg: "Attempting to call a protected method from the public scope, which is not allowed"
4613                 });
4614             }
4615
4616             if (!method.$previous) {
4617                 Ext.Error.raise({
4618                     sourceClass: Ext.getClassName(this),
4619                     sourceMethod: "callOverridden",
4620                     msg: "this.callOverridden was called in '" + method.$name +
4621                          "' but this method has never been overridden"
4622                  });
4623             }
4624             //</debug>
4625
4626             return method.$previous.apply(this, args || []);
4627         },
4628
4629         destroy: function() {}
4630     };
4631
4632     // These static properties will be copied to every newly created class with {@link Ext#define}
4633     Ext.apply(Ext.Base, {
4634         /**
4635          * Create a new instance of this Class.
4636 Ext.define('My.cool.Class', {
4637     ...
4638 });
4639
4640 My.cool.Class.create({
4641     someConfig: true
4642 });
4643          * @property create
4644          * @static
4645          * @type Function
4646          * @markdown
4647          */
4648         create: function() {
4649             return Ext.create.apply(Ext, [this].concat(Array.prototype.slice.call(arguments, 0)));
4650         },
4651
4652         /**
4653          * @private
4654          */
4655         own: flexSetter(function(name, value) {
4656             if (typeof value === 'function') {
4657                 this.ownMethod(name, value);
4658             }
4659             else {
4660                 this.prototype[name] = value;
4661             }
4662         }),
4663
4664         /**
4665          * @private
4666          */
4667         ownMethod: function(name, fn) {
4668             var originalFn;
4669
4670             if (fn.$owner !== undefined && fn !== Ext.emptyFn) {
4671                 originalFn = fn;
4672
4673                 fn = function() {
4674                     return originalFn.apply(this, arguments);
4675                 };
4676             }
4677
4678             //<debug>
4679             var className;
4680             className = Ext.getClassName(this);
4681             if (className) {
4682                 fn.displayName = className + '#' + name;
4683             }
4684             //</debug>
4685             fn.$owner = this;
4686             fn.$name = name;
4687
4688             this.prototype[name] = fn;
4689         },
4690
4691         /**
4692          * Add / override static properties of this class.
4693
4694     Ext.define('My.cool.Class', {
4695         ...
4696     });
4697
4698     My.cool.Class.addStatics({
4699         someProperty: 'someValue',      // My.cool.Class.someProperty = 'someValue'
4700         method1: function() { ... },    // My.cool.Class.method1 = function() { ... };
4701         method2: function() { ... }     // My.cool.Class.method2 = function() { ... };
4702     });
4703
4704          * @property addStatics
4705          * @static
4706          * @type Function
4707          * @param {Object} members
4708          * @markdown
4709          */
4710         addStatics: function(members) {
4711             for (var name in members) {
4712                 if (members.hasOwnProperty(name)) {
4713                     this[name] = members[name];
4714                 }
4715             }
4716
4717             return this;
4718         },
4719
4720         /**
4721          * Add methods / properties to the prototype of this class.
4722
4723     Ext.define('My.awesome.Cat', {
4724         constructor: function() {
4725             ...
4726         }
4727     });
4728
4729      My.awesome.Cat.implement({
4730          meow: function() {
4731             alert('Meowww...');
4732          }
4733      });
4734
4735      var kitty = new My.awesome.Cat;
4736      kitty.meow();
4737
4738          * @property implement
4739          * @static
4740          * @type Function
4741          * @param {Object} members
4742          * @markdown
4743          */
4744         implement: function(members) {
4745             var prototype = this.prototype,
4746                 name, i, member, previous;
4747             //<debug>
4748             var className = Ext.getClassName(this);
4749             //</debug>
4750             for (name in members) {
4751                 if (members.hasOwnProperty(name)) {
4752                     member = members[name];
4753
4754                     if (typeof member === 'function') {
4755                         member.$owner = this;
4756                         member.$name = name;
4757                         //<debug>
4758                         if (className) {
4759                             member.displayName = className + '#' + name;
4760                         }
4761                         //</debug>
4762                     }
4763
4764                     prototype[name] = member;
4765                 }
4766             }
4767
4768             if (Ext.enumerables) {
4769                 var enumerables = Ext.enumerables;
4770
4771                 for (i = enumerables.length; i--;) {
4772                     name = enumerables[i];
4773
4774                     if (members.hasOwnProperty(name)) {
4775                         member = members[name];
4776                         member.$owner = this;
4777                         member.$name = name;
4778                         prototype[name] = member;
4779                     }
4780                 }
4781             }
4782         },
4783
4784         /**
4785          * Borrow another class' members to the prototype of this class.
4786
4787 Ext.define('Bank', {
4788     money: '$$$',
4789     printMoney: function() {
4790         alert('$$$$$$$');
4791     }
4792 });
4793
4794 Ext.define('Thief', {
4795     ...
4796 });
4797
4798 Thief.borrow(Bank, ['money', 'printMoney']);
4799
4800 var steve = new Thief();
4801
4802 alert(steve.money); // alerts '$$$'
4803 steve.printMoney(); // alerts '$$$$$$$'
4804
4805          * @property borrow
4806          * @static
4807          * @type Function
4808          * @param {Ext.Base} fromClass The class to borrow members from
4809          * @param {Array/String} members The names of the members to borrow
4810          * @return {Ext.Base} this
4811          * @markdown
4812          */
4813         borrow: function(fromClass, members) {
4814             var fromPrototype = fromClass.prototype,
4815                 i, ln, member;
4816
4817             members = Ext.Array.from(members);
4818
4819             for (i = 0, ln = members.length; i < ln; i++) {
4820                 member = members[i];
4821
4822                 this.own(member, fromPrototype[member]);
4823             }
4824
4825             return this;
4826         },
4827
4828         /**
4829          * Override prototype members of this class. Overridden methods can be invoked via
4830          * {@link Ext.Base#callOverridden}
4831
4832     Ext.define('My.Cat', {
4833         constructor: function() {
4834             alert("I'm a cat!");
4835
4836             return this;
4837         }
4838     });
4839
4840     My.Cat.override({
4841         constructor: function() {
4842             alert("I'm going to be a cat!");
4843
4844             var instance = this.callOverridden();
4845
4846             alert("Meeeeoooowwww");
4847
4848             return instance;
4849         }
4850     });
4851
4852     var kitty = new My.Cat(); // alerts "I'm going to be a cat!"
4853                               // alerts "I'm a cat!"
4854                               // alerts "Meeeeoooowwww"
4855
4856          * @property override
4857          * @static
4858          * @type Function
4859          * @param {Object} members
4860          * @return {Ext.Base} this
4861          * @markdown
4862          */
4863         override: function(members) {
4864             var prototype = this.prototype,
4865                 name, i, member, previous;
4866
4867             for (name in members) {
4868                 if (members.hasOwnProperty(name)) {
4869                     member = members[name];
4870
4871                     if (typeof member === 'function') {
4872                         if (typeof prototype[name] === 'function') {
4873                             previous = prototype[name];
4874                             member.$previous = previous;
4875                         }
4876
4877                         this.ownMethod(name, member);
4878                     }
4879                     else {
4880                         prototype[name] = member;
4881                     }
4882                 }
4883             }
4884
4885             if (Ext.enumerables) {
4886                 var enumerables = Ext.enumerables;
4887
4888                 for (i = enumerables.length; i--;) {
4889                     name = enumerables[i];
4890
4891                     if (members.hasOwnProperty(name)) {
4892                         if (prototype[name] !== undefined) {
4893                             previous = prototype[name];
4894                             members[name].$previous = previous;
4895                         }
4896
4897                         this.ownMethod(name, members[name]);
4898                     }
4899                 }
4900             }
4901
4902             return this;
4903         },
4904
4905         /**
4906          * Used internally by the mixins pre-processor
4907          * @private
4908          */
4909         mixin: flexSetter(function(name, cls) {
4910             var mixin = cls.prototype,
4911                 my = this.prototype,
4912                 i, fn;
4913
4914             for (i in mixin) {
4915                 if (mixin.hasOwnProperty(i)) {
4916                     if (my[i] === undefined) {
4917                         if (typeof mixin[i] === 'function') {
4918                             fn = mixin[i];
4919
4920                             if (fn.$owner === undefined) {
4921                                 this.ownMethod(i, fn);
4922                             }
4923                             else {
4924                                 my[i] = fn;
4925                             }
4926                         }
4927                         else {
4928                             my[i] = mixin[i];
4929                         }
4930                     }
4931                     else if (i === 'config' && my.config && mixin.config) {
4932                         Ext.Object.merge(my.config, mixin.config);
4933                     }
4934                 }
4935             }
4936
4937             if (my.mixins === undefined) {
4938                 my.mixins = {};
4939             }
4940
4941             my.mixins[name] = mixin;
4942         }),
4943
4944         /**
4945          * Get the current class' name in string format.
4946
4947     Ext.define('My.cool.Class', {
4948         constructor: function() {
4949             alert(this.self.getName()); // alerts 'My.cool.Class'
4950         }
4951     });
4952
4953     My.cool.Class.getName(); // 'My.cool.Class'
4954
4955          * @return {String} className
4956          * @markdown
4957          */
4958         getName: function() {
4959             return Ext.getClassName(this);
4960         },
4961
4962         /**
4963          * Create aliases for existing prototype methods. Example:
4964
4965     Ext.define('My.cool.Class', {
4966         method1: function() { ... },
4967         method2: function() { ... }
4968     });
4969
4970     var test = new My.cool.Class();
4971
4972     My.cool.Class.createAlias({
4973         method3: 'method1',
4974         method4: 'method2'
4975     });
4976
4977     test.method3(); // test.method1()
4978
4979     My.cool.Class.createAlias('method5', 'method3');
4980
4981     test.method5(); // test.method3() -> test.method1()
4982
4983          * @property createAlias
4984          * @static
4985          * @type Function
4986          * @param {String/Object} alias The new method name, or an object to set multiple aliases. See
4987          * {@link Ext.Function#flexSetter flexSetter}
4988          * @param {String/Object} origin The original method name
4989          * @markdown
4990          */
4991         createAlias: flexSetter(function(alias, origin) {
4992             this.prototype[alias] = this.prototype[origin];
4993         })
4994     });
4995
4996 })(Ext.Function.flexSetter);
4997
4998 /**
4999  * @author Jacky Nguyen <jacky@sencha.com>
5000  * @docauthor Jacky Nguyen <jacky@sencha.com>
5001  * @class Ext.Class
5002  * 
5003  * Handles class creation throughout the whole framework. Note that most of the time {@link Ext#define Ext.define} should
5004  * be used instead, since it's a higher level wrapper that aliases to {@link Ext.ClassManager#create}
5005  * to enable namespacing and dynamic dependency resolution.
5006  * 
5007  * # Basic syntax: #
5008  * 
5009  *     Ext.define(className, properties);
5010  * 
5011  * in which `properties` is an object represent a collection of properties that apply to the class. See
5012  * {@link Ext.ClassManager#create} for more detailed instructions.
5013  * 
5014  *     Ext.define('Person', {
5015  *          name: 'Unknown',
5016  * 
5017  *          constructor: function(name) {
5018  *              if (name) {
5019  *                  this.name = name;
5020  *              }
5021  * 
5022  *              return this;
5023  *          },
5024  * 
5025  *          eat: function(foodType) {
5026  *              alert("I'm eating: " + foodType);
5027  * 
5028  *              return this;
5029  *          }
5030  *     });
5031  * 
5032  *     var aaron = new Person("Aaron");
5033  *     aaron.eat("Sandwich"); // alert("I'm eating: Sandwich");
5034  * 
5035  * Ext.Class has a powerful set of extensible {@link Ext.Class#registerPreprocessor pre-processors} which takes care of
5036  * everything related to class creation, including but not limited to inheritance, mixins, configuration, statics, etc.
5037  * 
5038  * # Inheritance: #
5039  * 
5040  *     Ext.define('Developer', {
5041  *          extend: 'Person',
5042  * 
5043  *          constructor: function(name, isGeek) {
5044  *              this.isGeek = isGeek;
5045  * 
5046  *              // Apply a method from the parent class' prototype
5047  *              this.callParent([name]);
5048  * 
5049  *              return this;
5050  * 
5051  *          },
5052  * 
5053  *          code: function(language) {
5054  *              alert("I'm coding in: " + language);
5055  * 
5056  *              this.eat("Bugs");
5057  * 
5058  *              return this;
5059  *          }
5060  *     });
5061  * 
5062  *     var jacky = new Developer("Jacky", true);
5063  *     jacky.code("JavaScript"); // alert("I'm coding in: JavaScript");
5064  *                               // alert("I'm eating: Bugs");
5065  * 
5066  * See {@link Ext.Base#callParent} for more details on calling superclass' methods
5067  * 
5068  * # Mixins: #
5069  * 
5070  *     Ext.define('CanPlayGuitar', {
5071  *          playGuitar: function() {
5072  *             alert("F#...G...D...A");
5073  *          }
5074  *     });
5075  * 
5076  *     Ext.define('CanComposeSongs', {
5077  *          composeSongs: function() { ... }
5078  *     });
5079  * 
5080  *     Ext.define('CanSing', {
5081  *          sing: function() {
5082  *              alert("I'm on the highway to hell...")
5083  *          }
5084  *     });
5085  * 
5086  *     Ext.define('Musician', {
5087  *          extend: 'Person',
5088  * 
5089  *          mixins: {
5090  *              canPlayGuitar: 'CanPlayGuitar',
5091  *              canComposeSongs: 'CanComposeSongs',
5092  *              canSing: 'CanSing'
5093  *          }
5094  *     })
5095  * 
5096  *     Ext.define('CoolPerson', {
5097  *          extend: 'Person',
5098  * 
5099  *          mixins: {
5100  *              canPlayGuitar: 'CanPlayGuitar',
5101  *              canSing: 'CanSing'
5102  *          },
5103  * 
5104  *          sing: function() {
5105  *              alert("Ahem....");
5106  * 
5107  *              this.mixins.canSing.sing.call(this);
5108  * 
5109  *              alert("[Playing guitar at the same time...]");
5110  * 
5111  *              this.playGuitar();
5112  *          }
5113  *     });
5114  * 
5115  *     var me = new CoolPerson("Jacky");
5116  * 
5117  *     me.sing(); // alert("Ahem...");
5118  *                // alert("I'm on the highway to hell...");
5119  *                // alert("[Playing guitar at the same time...]");
5120  *                // alert("F#...G...D...A");
5121  * 
5122  * # Config: #
5123  * 
5124  *     Ext.define('SmartPhone', {
5125  *          config: {
5126  *              hasTouchScreen: false,
5127  *              operatingSystem: 'Other',
5128  *              price: 500
5129  *          },
5130  * 
5131  *          isExpensive: false,
5132  * 
5133  *          constructor: function(config) {
5134  *              this.initConfig(config);
5135  * 
5136  *              return this;
5137  *          },
5138  * 
5139  *          applyPrice: function(price) {
5140  *              this.isExpensive = (price > 500);
5141  * 
5142  *              return price;
5143  *          },
5144  * 
5145  *          applyOperatingSystem: function(operatingSystem) {
5146  *              if (!(/^(iOS|Android|BlackBerry)$/i).test(operatingSystem)) {
5147  *                  return 'Other';
5148  *              }
5149  * 
5150  *              return operatingSystem;
5151  *          }
5152  *     });
5153  * 
5154  *     var iPhone = new SmartPhone({
5155  *          hasTouchScreen: true,
5156  *          operatingSystem: 'iOS'
5157  *     });
5158  * 
5159  *     iPhone.getPrice(); // 500;
5160  *     iPhone.getOperatingSystem(); // 'iOS'
5161  *     iPhone.getHasTouchScreen(); // true;
5162  *     iPhone.hasTouchScreen(); // true
5163  * 
5164  *     iPhone.isExpensive; // false;
5165  *     iPhone.setPrice(600);
5166  *     iPhone.getPrice(); // 600
5167  *     iPhone.isExpensive; // true;
5168  * 
5169  *     iPhone.setOperatingSystem('AlienOS');
5170  *     iPhone.getOperatingSystem(); // 'Other'
5171  * 
5172  * # Statics: #
5173  * 
5174  *     Ext.define('Computer', {
5175  *          statics: {
5176  *              factory: function(brand) {
5177  *                 // 'this' in static methods refer to the class itself
5178  *                  return new this(brand);
5179  *              }
5180  *          },
5181  * 
5182  *          constructor: function() { ... }
5183  *     });
5184  * 
5185  *     var dellComputer = Computer.factory('Dell');
5186  * 
5187  * Also see {@link Ext.Base#statics} and {@link Ext.Base#self} for more details on accessing
5188  * static properties within class methods
5189  *
5190  */
5191 (function() {
5192
5193     var Class,
5194         Base = Ext.Base,
5195         baseStaticProperties = [],
5196         baseStaticProperty;
5197
5198     for (baseStaticProperty in Base) {
5199         if (Base.hasOwnProperty(baseStaticProperty)) {
5200             baseStaticProperties.push(baseStaticProperty);
5201         }
5202     }
5203
5204     /**
5205      * @constructor
5206      * @param {Object} classData An object represent the properties of this class
5207      * @param {Function} createdFn Optional, the callback function to be executed when this class is fully created.
5208      * Note that the creation process can be asynchronous depending on the pre-processors used.
5209      * @return {Ext.Base} The newly created class
5210      */
5211     Ext.Class = Class = function(newClass, classData, onClassCreated) {
5212         if (typeof newClass !== 'function') {
5213             onClassCreated = classData;
5214             classData = newClass;
5215             newClass = function() {
5216                 return this.constructor.apply(this, arguments);
5217             };
5218         }
5219
5220         if (!classData) {
5221             classData = {};
5222         }
5223
5224         var preprocessorStack = classData.preprocessors || Class.getDefaultPreprocessors(),
5225             registeredPreprocessors = Class.getPreprocessors(),
5226             index = 0,
5227             preprocessors = [],
5228             preprocessor, preprocessors, staticPropertyName, process, i, j, ln;
5229
5230         for (i = 0, ln = baseStaticProperties.length; i < ln; i++) {
5231             staticPropertyName = baseStaticProperties[i];
5232             newClass[staticPropertyName] = Base[staticPropertyName];
5233         }
5234
5235         delete classData.preprocessors;
5236
5237         for (j = 0, ln = preprocessorStack.length; j < ln; j++) {
5238             preprocessor = preprocessorStack[j];
5239
5240             if (typeof preprocessor === 'string') {
5241                 preprocessor = registeredPreprocessors[preprocessor];
5242
5243                 if (!preprocessor.always) {
5244                     if (classData.hasOwnProperty(preprocessor.name)) {
5245                         preprocessors.push(preprocessor.fn);
5246                     }
5247                 }
5248                 else {
5249                     preprocessors.push(preprocessor.fn);
5250                 }
5251             }
5252             else {
5253                 preprocessors.push(preprocessor);
5254             }
5255         }
5256
5257         classData.onClassCreated = onClassCreated;
5258
5259         classData.onBeforeClassCreated = function(cls, data) {
5260             onClassCreated = data.onClassCreated;
5261
5262             delete data.onBeforeClassCreated;
5263             delete data.onClassCreated;
5264
5265             cls.implement(data);
5266
5267             if (onClassCreated) {
5268                 onClassCreated.call(cls, cls);
5269             }
5270         };
5271
5272         process = function(cls, data) {
5273             preprocessor = preprocessors[index++];
5274
5275             if (!preprocessor) {
5276                 data.onBeforeClassCreated.apply(this, arguments);
5277                 return;
5278             }
5279
5280             if (preprocessor.call(this, cls, data, process) !== false) {
5281                 process.apply(this, arguments);
5282             }
5283         };
5284
5285         process.call(Class, newClass, classData);
5286
5287         return newClass;
5288     };
5289
5290     Ext.apply(Class, {
5291
5292         /** @private */
5293         preprocessors: {},
5294
5295         /**
5296          * Register a new pre-processor to be used during the class creation process
5297          *
5298          * @member Ext.Class registerPreprocessor
5299          * @param {String} name The pre-processor's name
5300          * @param {Function} fn The callback function to be executed. Typical format:
5301
5302     function(cls, data, fn) {
5303         // Your code here
5304
5305         // Execute this when the processing is finished.
5306         // Asynchronous processing is perfectly ok
5307         if (fn) {
5308             fn.call(this, cls, data);
5309         }
5310     });
5311
5312          * Passed arguments for this function are:
5313          *
5314          * - `{Function} cls`: The created class
5315          * - `{Object} data`: The set of properties passed in {@link Ext.Class} constructor
5316          * - `{Function} fn`: The callback function that <b>must</b> to be executed when this pre-processor finishes,
5317          * regardless of whether the processing is synchronous or aynchronous
5318          *
5319          * @return {Ext.Class} this
5320          * @markdown
5321          */
5322         registerPreprocessor: function(name, fn, always) {
5323             this.preprocessors[name] = {
5324                 name: name,
5325                 always: always ||  false,
5326                 fn: fn
5327             };
5328
5329             return this;
5330         },
5331
5332         /**
5333          * Retrieve a pre-processor callback function by its name, which has been registered before
5334          *
5335          * @param {String} name
5336          * @return {Function} preprocessor
5337          */
5338         getPreprocessor: function(name) {
5339             return this.preprocessors[name];
5340         },
5341
5342         getPreprocessors: function() {
5343             return this.preprocessors;
5344         },
5345
5346         /**
5347          * Retrieve the array stack of default pre-processors
5348          *
5349          * @return {Function} defaultPreprocessors
5350          */
5351         getDefaultPreprocessors: function() {
5352             return this.defaultPreprocessors || [];
5353         },
5354
5355         /**
5356          * Set the default array stack of default pre-processors
5357          *
5358          * @param {Array} preprocessors
5359          * @return {Ext.Class} this
5360          */
5361         setDefaultPreprocessors: function(preprocessors) {
5362             this.defaultPreprocessors = Ext.Array.from(preprocessors);
5363
5364             return this;
5365         },
5366
5367         /**
5368          * Insert this pre-processor at a specific position in the stack, optionally relative to
5369          * any existing pre-processor. For example:
5370
5371     Ext.Class.registerPreprocessor('debug', function(cls, data, fn) {
5372         // Your code here
5373
5374         if (fn) {
5375             fn.call(this, cls, data);
5376         }
5377     }).insertDefaultPreprocessor('debug', 'last');
5378
5379          * @param {String} name The pre-processor name. Note that it needs to be registered with
5380          * {@link Ext#registerPreprocessor registerPreprocessor} before this
5381          * @param {String} offset The insertion position. Four possible values are:
5382          * 'first', 'last', or: 'before', 'after' (relative to the name provided in the third argument)
5383          * @param {String} relativeName
5384          * @return {Ext.Class} this
5385          * @markdown
5386          */
5387         setDefaultPreprocessorPosition: function(name, offset, relativeName) {
5388             var defaultPreprocessors = this.defaultPreprocessors,
5389                 index;
5390
5391             if (typeof offset === 'string') {
5392                 if (offset === 'first') {
5393                     defaultPreprocessors.unshift(name);
5394
5395                     return this;
5396                 }
5397                 else if (offset === 'last') {
5398                     defaultPreprocessors.push(name);
5399
5400                     return this;
5401                 }
5402
5403                 offset = (offset === 'after') ? 1 : -1;
5404             }
5405
5406             index = Ext.Array.indexOf(defaultPreprocessors, relativeName);
5407
5408             if (index !== -1) {
5409                 defaultPreprocessors.splice(Math.max(0, index + offset), 0, name);
5410             }
5411
5412             return this;
5413         }
5414     });
5415
5416     Class.registerPreprocessor('extend', function(cls, data) {
5417         var extend = data.extend,
5418             base = Ext.Base,
5419             basePrototype = base.prototype,
5420             prototype = function() {},
5421             parent, i, k, ln, staticName, parentStatics,
5422             parentPrototype, clsPrototype;
5423
5424         if (extend && extend !== Object) {
5425             parent = extend;
5426         }
5427         else {
5428             parent = base;
5429         }
5430
5431         parentPrototype = parent.prototype;
5432
5433         prototype.prototype = parentPrototype;
5434         clsPrototype = cls.prototype = new prototype();
5435
5436         if (!('$class' in parent)) {
5437             for (i in basePrototype) {
5438                 if (!parentPrototype[i]) {
5439                     parentPrototype[i] = basePrototype[i];
5440                 }
5441             }
5442         }
5443
5444         clsPrototype.self = cls;
5445
5446         cls.superclass = clsPrototype.superclass = parentPrototype;
5447
5448         delete data.extend;
5449
5450         // Statics inheritance
5451         parentStatics = parentPrototype.$inheritableStatics;
5452
5453         if (parentStatics) {
5454             for (k = 0, ln = parentStatics.length; k < ln; k++) {
5455                 staticName = parentStatics[k];
5456
5457                 if (!cls.hasOwnProperty(staticName)) {
5458                     cls[staticName] = parent[staticName];
5459                 }
5460             }
5461         }
5462
5463         // Merge the parent class' config object without referencing it
5464         if (parentPrototype.config) {
5465             clsPrototype.config = Ext.Object.merge({}, parentPrototype.config);
5466         }
5467         else {
5468             clsPrototype.config = {};
5469         }
5470
5471         if (clsPrototype.$onExtended) {
5472             clsPrototype.$onExtended.call(cls, cls, data);
5473         }
5474
5475         if (data.onClassExtended) {
5476             clsPrototype.$onExtended = data.onClassExtended;
5477             delete data.onClassExtended;
5478         }
5479
5480     }, true);
5481
5482     Class.registerPreprocessor('statics', function(cls, data) {
5483         var statics = data.statics,
5484             name;
5485
5486         for (name in statics) {
5487             if (statics.hasOwnProperty(name)) {
5488                 cls[name] = statics[name];
5489             }
5490         }
5491
5492         delete data.statics;
5493     });
5494
5495     Class.registerPreprocessor('inheritableStatics', function(cls, data) {
5496         var statics = data.inheritableStatics,
5497             inheritableStatics,
5498             prototype = cls.prototype,
5499             name;
5500
5501         inheritableStatics = prototype.$inheritableStatics;
5502
5503         if (!inheritableStatics) {
5504             inheritableStatics = prototype.$inheritableStatics = [];
5505         }
5506
5507         for (name in statics) {
5508             if (statics.hasOwnProperty(name)) {
5509                 cls[name] = statics[name];
5510                 inheritableStatics.push(name);
5511             }
5512         }
5513
5514         delete data.inheritableStatics;
5515     });
5516
5517     Class.registerPreprocessor('mixins', function(cls, data) {
5518         cls.mixin(data.mixins);
5519
5520         delete data.mixins;
5521     });
5522
5523     Class.registerPreprocessor('config', function(cls, data) {
5524         var prototype = cls.prototype;
5525
5526         Ext.Object.each(data.config, function(name) {
5527             var cName = name.charAt(0).toUpperCase() + name.substr(1),
5528                 pName = name,
5529                 apply = 'apply' + cName,
5530                 setter = 'set' + cName,
5531                 getter = 'get' + cName;
5532
5533             if (!(apply in prototype) && !data.hasOwnProperty(apply)) {
5534                 data[apply] = function(val) {
5535                     return val;
5536                 };
5537             }
5538
5539             if (!(setter in prototype) && !data.hasOwnProperty(setter)) {
5540                 data[setter] = function(val) {
5541                     var ret = this[apply].call(this, val, this[pName]);
5542
5543                     if (ret !== undefined) {
5544                         this[pName] = ret;
5545                     }
5546
5547                     return this;
5548                 };
5549             }
5550
5551             if (!(getter in prototype) && !data.hasOwnProperty(getter)) {
5552                 data[getter] = function() {
5553                     return this[pName];
5554                 };
5555             }
5556         });
5557
5558         Ext.Object.merge(prototype.config, data.config);
5559         delete data.config;
5560     });
5561
5562     Class.setDefaultPreprocessors(['extend', 'statics', 'inheritableStatics', 'mixins', 'config']);
5563
5564     // Backwards compatible
5565     Ext.extend = function(subclass, superclass, members) {
5566         if (arguments.length === 2 && Ext.isObject(superclass)) {
5567             members = superclass;
5568             superclass = subclass;
5569             subclass = null;
5570         }
5571
5572         var cls;
5573
5574         if (!superclass) {
5575             Ext.Error.raise("Attempting to extend from a class which has not been loaded on the page.");
5576         }
5577
5578         members.extend = superclass;
5579         members.preprocessors = ['extend', 'mixins', 'config', 'statics'];
5580
5581         if (subclass) {
5582             cls = new Class(subclass, members);
5583         }
5584         else {
5585             cls = new Class(members);
5586         }
5587
5588         cls.prototype.override = function(o) {
5589             for (var m in o) {
5590                 if (o.hasOwnProperty(m)) {
5591                     this[m] = o[m];
5592                 }
5593             }
5594         };
5595
5596         return cls;
5597     };
5598
5599 })();
5600
5601 /**
5602  * @author Jacky Nguyen <jacky@sencha.com>
5603  * @docauthor Jacky Nguyen <jacky@sencha.com>
5604  * @class Ext.ClassManager
5605
5606 Ext.ClassManager manages all classes and handles mapping from string class name to
5607 actual class objects throughout the whole framework. It is not generally accessed directly, rather through
5608 these convenient shorthands:
5609
5610 - {@link Ext#define Ext.define}
5611 - {@link Ext#create Ext.create}
5612 - {@link Ext#widget Ext.widget}
5613 - {@link Ext#getClass Ext.getClass}
5614 - {@link Ext#getClassName Ext.getClassName}
5615
5616  * @singleton
5617  * @markdown
5618  */
5619 (function(Class, alias) {
5620
5621     var slice = Array.prototype.slice;
5622
5623     var Manager = Ext.ClassManager = {
5624
5625         /**
5626          * @property classes
5627          * @type Object
5628          * All classes which were defined through the ClassManager. Keys are the
5629          * name of the classes and the values are references to the classes.
5630          * @private
5631          */
5632         classes: {},
5633
5634         /**
5635          * @private
5636          */
5637         existCache: {},
5638
5639         /**
5640          * @private
5641          */
5642         namespaceRewrites: [{
5643             from: 'Ext.',
5644             to: Ext
5645         }],
5646
5647         /**
5648          * @private
5649          */
5650         maps: {
5651             alternateToName: {},
5652             aliasToName: {},
5653             nameToAliases: {}
5654         },
5655
5656         /** @private */
5657         enableNamespaceParseCache: true,
5658
5659         /** @private */
5660         namespaceParseCache: {},
5661
5662         /** @private */
5663         instantiators: [],
5664
5665         //<debug>
5666         /** @private */
5667         instantiationCounts: {},
5668         //</debug>
5669
5670         /**
5671          * Checks if a class has already been created.
5672          *
5673          * @param {String} className
5674          * @return {Boolean} exist
5675          */
5676         isCreated: function(className) {
5677             var i, ln, part, root, parts;
5678
5679             //<debug error>
5680             if (typeof className !== 'string' || className.length < 1) {
5681                 Ext.Error.raise({
5682                     sourceClass: "Ext.ClassManager",
5683                     sourceMethod: "exist",
5684                     msg: "Invalid classname, must be a string and must not be empty"
5685                 });
5686             }
5687             //</debug>
5688
5689             if (this.classes.hasOwnProperty(className) || this.existCache.hasOwnProperty(className)) {
5690                 return true;
5691             }
5692
5693             root = Ext.global;
5694             parts = this.parseNamespace(className);
5695
5696             for (i = 0, ln = parts.length; i < ln; i++) {
5697                 part = parts[i];
5698
5699                 if (typeof part !== 'string') {
5700                     root = part;
5701                 } else {
5702                     if (!root || !root[part]) {
5703                         return false;
5704                     }
5705
5706                     root = root[part];
5707                 }
5708             }
5709
5710             Ext.Loader.historyPush(className);
5711
5712             this.existCache[className] = true;
5713
5714             return true;
5715         },
5716
5717         /**
5718          * Supports namespace rewriting
5719          * @private
5720          */
5721         parseNamespace: function(namespace) {
5722             //<debug error>
5723             if (typeof namespace !== 'string') {
5724                 Ext.Error.raise({
5725                     sourceClass: "Ext.ClassManager",
5726                     sourceMethod: "parseNamespace",
5727                     msg: "Invalid namespace, must be a string"
5728                 });
5729             }
5730             //</debug>
5731
5732             var cache = this.namespaceParseCache;
5733
5734             if (this.enableNamespaceParseCache) {
5735                 if (cache.hasOwnProperty(namespace)) {
5736                     return cache[namespace];
5737                 }
5738             }
5739
5740             var parts = [],
5741                 rewrites = this.namespaceRewrites,
5742                 rewrite, from, to, i, ln, root = Ext.global;
5743
5744             for (i = 0, ln = rewrites.length; i < ln; i++) {
5745                 rewrite = rewrites[i];
5746                 from = rewrite.from;
5747                 to = rewrite.to;
5748
5749                 if (namespace === from || namespace.substring(0, from.length) === from) {
5750                     namespace = namespace.substring(from.length);
5751
5752                     if (typeof to !== 'string') {
5753                         root = to;
5754                     } else {
5755                         parts = parts.concat(to.split('.'));
5756                     }
5757
5758                     break;
5759                 }
5760             }
5761
5762             parts.push(root);
5763
5764             parts = parts.concat(namespace.split('.'));
5765
5766             if (this.enableNamespaceParseCache) {
5767                 cache[namespace] = parts;
5768             }
5769
5770             return parts;
5771         },
5772
5773         /**
5774          * Creates a namespace and assign the `value` to the created object
5775
5776     Ext.ClassManager.setNamespace('MyCompany.pkg.Example', someObject);
5777
5778     alert(MyCompany.pkg.Example === someObject); // alerts true
5779
5780          * @param {String} name
5781          * @param {Mixed} value
5782          * @markdown
5783          */
5784         setNamespace: function(name, value) {
5785             var root = Ext.global,
5786                 parts = this.parseNamespace(name),
5787                 leaf = parts.pop(),
5788                 i, ln, part;
5789
5790             for (i = 0, ln = parts.length; i < ln; i++) {
5791                 part = parts[i];
5792
5793                 if (typeof part !== 'string') {
5794                     root = part;
5795                 } else {
5796                     if (!root[part]) {
5797                         root[part] = {};
5798                     }
5799
5800                     root = root[part];
5801                 }
5802             }
5803
5804             root[leaf] = value;
5805
5806             return root[leaf];
5807         },
5808
5809         /**
5810          * The new Ext.ns, supports namespace rewriting
5811          * @private
5812          */
5813         createNamespaces: function() {
5814             var root = Ext.global,
5815                 parts, part, i, j, ln, subLn;
5816
5817             for (i = 0, ln = arguments.length; i < ln; i++) {
5818                 parts = this.parseNamespace(arguments[i]);
5819
5820                 for (j = 0, subLn = parts.length; j < subLn; j++) {
5821                     part = parts[j];
5822
5823                     if (typeof part !== 'string') {
5824                         root = part;
5825                     } else {
5826                         if (!root[part]) {
5827                             root[part] = {};
5828                         }
5829
5830                         root = root[part];
5831                     }
5832                 }
5833             }
5834
5835             return root;
5836         },
5837
5838         /**
5839          * Sets a name reference to a class.
5840          *
5841          * @param {String} name
5842          * @param {Object} value
5843          * @return {Ext.ClassManager} this
5844          */
5845         set: function(name, value) {
5846             var targetName = this.getName(value);
5847
5848             this.classes[name] = this.setNamespace(name, value);
5849
5850             if (targetName && targetName !== name) {
5851                 this.maps.alternateToName[name] = targetName;
5852             }
5853
5854             return this;
5855         },
5856
5857         /**
5858          * Retrieve a class by its name.
5859          *
5860          * @param {String} name
5861          * @return {Class} class
5862          */
5863         get: function(name) {
5864             if (this.classes.hasOwnProperty(name)) {
5865                 return this.classes[name];
5866             }
5867
5868             var root = Ext.global,
5869                 parts = this.parseNamespace(name),
5870                 part, i, ln;
5871
5872             for (i = 0, ln = parts.length; i < ln; i++) {
5873                 part = parts[i];
5874
5875                 if (typeof part !== 'string') {
5876                     root = part;
5877                 } else {
5878                     if (!root || !root[part]) {
5879                         return null;
5880                     }
5881
5882                     root = root[part];
5883                 }
5884             }
5885
5886             return root;
5887         },
5888
5889         /**
5890          * Register the alias for a class.
5891          *
5892          * @param {Class/String} cls a reference to a class or a className
5893          * @param {String} alias Alias to use when referring to this class
5894          */
5895         setAlias: function(cls, alias) {
5896             var aliasToNameMap = this.maps.aliasToName,
5897                 nameToAliasesMap = this.maps.nameToAliases,
5898                 className;
5899
5900             if (typeof cls === 'string') {
5901                 className = cls;
5902             } else {
5903                 className = this.getName(cls);
5904             }
5905
5906             if (alias && aliasToNameMap[alias] !== className) {
5907                 //<debug info>
5908                 if (aliasToNameMap.hasOwnProperty(alias) && Ext.isDefined(Ext.global.console)) {
5909                     Ext.global.console.log("[Ext.ClassManager] Overriding existing alias: '" + alias + "' " +
5910                         "of: '" + aliasToNameMap[alias] + "' with: '" + className + "'. Be sure it's intentional.");
5911                 }
5912                 //</debug>
5913
5914                 aliasToNameMap[alias] = className;
5915             }
5916
5917             if (!nameToAliasesMap[className]) {
5918                 nameToAliasesMap[className] = [];
5919             }
5920
5921             if (alias) {
5922                 Ext.Array.include(nameToAliasesMap[className], alias);
5923             }
5924
5925             return this;
5926         },
5927
5928         /**
5929          * Get a reference to the class by its alias.
5930          *
5931          * @param {String} alias
5932          * @return {Class} class
5933          */
5934         getByAlias: function(alias) {
5935             return this.get(this.getNameByAlias(alias));
5936         },
5937
5938         /**
5939          * Get the name of a class by its alias.
5940          *
5941          * @param {String} alias
5942          * @return {String} className
5943          */
5944         getNameByAlias: function(alias) {
5945             return this.maps.aliasToName[alias] || '';
5946         },
5947
5948         /**
5949          * Get the name of a class by its alternate name.
5950          *
5951          * @param {String} alternate
5952          * @return {String} className
5953          */
5954         getNameByAlternate: function(alternate) {
5955             return this.maps.alternateToName[alternate] || '';
5956         },
5957
5958         /**
5959          * Get the aliases of a class by the class name
5960          *
5961          * @param {String} name
5962          * @return {Array} aliases
5963          */
5964         getAliasesByName: function(name) {
5965             return this.maps.nameToAliases[name] || [];
5966         },
5967
5968         /**
5969          * Get the name of the class by its reference or its instance;
5970          * usually invoked by the shorthand {@link Ext#getClassName Ext.getClassName}
5971
5972     Ext.ClassManager.getName(Ext.Action); // returns "Ext.Action"
5973
5974          * @param {Class/Object} object
5975          * @return {String} className
5976          * @markdown
5977          */
5978         getName: function(object) {
5979             return object && object.$className || '';
5980         },
5981
5982         /**
5983          * Get the class of the provided object; returns null if it's not an instance
5984          * of any class created with Ext.define. This is usually invoked by the shorthand {@link Ext#getClass Ext.getClass}
5985          *
5986     var component = new Ext.Component();
5987
5988     Ext.ClassManager.getClass(component); // returns Ext.Component
5989              *
5990          * @param {Object} object
5991          * @return {Class} class
5992          * @markdown
5993          */
5994         getClass: function(object) {
5995             return object && object.self || null;
5996         },
5997
5998         /**
5999          * Defines a class. This is usually invoked via the alias {@link Ext#define Ext.define}
6000
6001     Ext.ClassManager.create('My.awesome.Class', {
6002         someProperty: 'something',
6003         someMethod: function() { ... }
6004         ...
6005
6006     }, function() {
6007         alert('Created!');
6008         alert(this === My.awesome.Class); // alerts true
6009
6010         var myInstance = new this();
6011     });
6012
6013          * @param {String} className The class name to create in string dot-namespaced format, for example:
6014          * 'My.very.awesome.Class', 'FeedViewer.plugin.CoolPager'
6015          * It is highly recommended to follow this simple convention:
6016
6017 - The root and the class name are 'CamelCased'
6018 - Everything else is lower-cased
6019
6020          * @param {Object} data The key - value pairs of properties to apply to this class. Property names can be of any valid
6021          * strings, except those in the reserved listed below:
6022
6023 - `mixins`
6024 - `statics`
6025 - `config`
6026 - `alias`
6027 - `self`
6028 - `singleton`
6029 - `alternateClassName`
6030          *
6031          * @param {Function} createdFn Optional callback to execute after the class is created, the execution scope of which
6032          * (`this`) will be the newly created class itself.
6033          * @return {Ext.Base}
6034          * @markdown
6035          */
6036         create: function(className, data, createdFn) {
6037             var manager = this;
6038
6039             //<debug error>
6040             if (typeof className !== 'string') {
6041                 Ext.Error.raise({
6042                     sourceClass: "Ext",
6043                     sourceMethod: "define",
6044                     msg: "Invalid class name '" + className + "' specified, must be a non-empty string"
6045                 });
6046             }
6047             //</debug>
6048
6049             data.$className = className;
6050
6051             return new Class(data, function() {
6052                 var postprocessorStack = data.postprocessors || manager.defaultPostprocessors,
6053                     registeredPostprocessors = manager.postprocessors,
6054                     index = 0,
6055                     postprocessors = [],
6056                     postprocessor, postprocessors, process, i, ln;
6057
6058                 delete data.postprocessors;
6059
6060                 for (i = 0, ln = postprocessorStack.length; i < ln; i++) {
6061                     postprocessor = postprocessorStack[i];
6062
6063                     if (typeof postprocessor === 'string') {
6064                         postprocessor = registeredPostprocessors[postprocessor];
6065
6066                         if (!postprocessor.always) {
6067                             if (data[postprocessor.name] !== undefined) {
6068                                 postprocessors.push(postprocessor.fn);
6069                             }
6070                         }
6071                         else {
6072                             postprocessors.push(postprocessor.fn);
6073                         }
6074                     }
6075                     else {
6076                         postprocessors.push(postprocessor);
6077                     }
6078                 }
6079
6080                 process = function(clsName, cls, clsData) {
6081                     postprocessor = postprocessors[index++];
6082
6083                     if (!postprocessor) {
6084                         manager.set(className, cls);
6085
6086                         Ext.Loader.historyPush(className);
6087
6088                         if (createdFn) {
6089                             createdFn.call(cls, cls);
6090                         }
6091
6092                         return;
6093                     }
6094
6095                     if (postprocessor.call(this, clsName, cls, clsData, process) !== false) {
6096                         process.apply(this, arguments);
6097                     }
6098                 };
6099
6100                 process.call(manager, className, this, data);
6101             });
6102         },
6103
6104         /**
6105          * Instantiate a class by its alias; usually invoked by the convenient shorthand {@link Ext#createByAlias Ext.createByAlias}
6106          * If {@link Ext.Loader} is {@link Ext.Loader#setConfig enabled} and the class has not been defined yet, it will
6107          * attempt to load the class via synchronous loading.
6108
6109     var window = Ext.ClassManager.instantiateByAlias('widget.window', { width: 600, height: 800, ... });
6110
6111          * @param {String} alias
6112          * @param {Mixed} args,... Additional arguments after the alias will be passed to the
6113          * class constructor.
6114          * @return {Object} instance
6115          * @markdown
6116          */
6117         instantiateByAlias: function() {
6118             var alias = arguments[0],
6119                 args = slice.call(arguments),
6120                 className = this.getNameByAlias(alias);
6121
6122             if (!className) {
6123                 className = this.maps.aliasToName[alias];
6124
6125                 //<debug error>
6126                 if (!className) {
6127                     Ext.Error.raise({
6128                         sourceClass: "Ext",
6129                         sourceMethod: "createByAlias",
6130                         msg: "Cannot create an instance of unrecognized alias: " + alias
6131                     });
6132                 }
6133                 //</debug>
6134
6135                 //<debug warn>
6136                 if (Ext.global.console) {
6137                     Ext.global.console.warn("[Ext.Loader] Synchronously loading '" + className + "'; consider adding " +
6138                          "Ext.require('" + alias + "') above Ext.onReady");
6139                 }
6140                 //</debug>
6141
6142                 Ext.syncRequire(className);
6143             }
6144
6145             args[0] = className;
6146
6147             return this.instantiate.apply(this, args);
6148         },
6149
6150         /**
6151          * Instantiate a class by either full name, alias or alternate name; usually invoked by the convenient
6152          * shorthand {@link Ext#create Ext.create}
6153          *
6154          * If {@link Ext.Loader} is {@link Ext.Loader#setConfig enabled} and the class has not been defined yet, it will
6155          * attempt to load the class via synchronous loading.
6156          *
6157          * For example, all these three lines return the same result:
6158
6159     // alias
6160     var window = Ext.ClassManager.instantiate('widget.window', { width: 600, height: 800, ... });
6161
6162     // alternate name
6163     var window = Ext.ClassManager.instantiate('Ext.Window', { width: 600, height: 800, ... });
6164
6165     // full class name
6166     var window = Ext.ClassManager.instantiate('Ext.window.Window', { width: 600, height: 800, ... });
6167
6168          * @param {String} name
6169          * @param {Mixed} args,... Additional arguments after the name will be passed to the class' constructor.
6170          * @return {Object} instance
6171          * @markdown
6172          */
6173         instantiate: function() {
6174             var name = arguments[0],
6175                 args = slice.call(arguments, 1),
6176                 alias = name,
6177                 possibleName, cls;
6178
6179             if (typeof name !== 'function') {
6180                 //<debug error>
6181                 if ((typeof name !== 'string' || name.length < 1)) {
6182                     Ext.Error.raise({
6183                         sourceClass: "Ext",
6184                         sourceMethod: "create",
6185                         msg: "Invalid class name or alias '" + name + "' specified, must be a non-empty string"
6186                     });
6187                 }
6188                 //</debug>
6189
6190                 cls = this.get(name);
6191             }
6192             else {
6193                 cls = name;
6194             }
6195
6196             // No record of this class name, it's possibly an alias, so look it up
6197             if (!cls) {
6198                 possibleName = this.getNameByAlias(name);
6199
6200                 if (possibleName) {
6201                     name = possibleName;
6202
6203                     cls = this.get(name);
6204                 }
6205             }
6206
6207             // Still no record of this class name, it's possibly an alternate name, so look it up
6208             if (!cls) {
6209                 possibleName = this.getNameByAlternate(name);
6210
6211                 if (possibleName) {
6212                     name = possibleName;
6213
6214                     cls = this.get(name);
6215                 }
6216             }
6217
6218             // Still not existing at this point, try to load it via synchronous mode as the last resort
6219             if (!cls) {
6220                 //<debug warn>
6221                 if (Ext.global.console) {
6222                     Ext.global.console.warn("[Ext.Loader] Synchronously loading '" + name + "'; consider adding " +
6223                          "Ext.require('" + ((possibleName) ? alias : name) + "') above Ext.onReady");
6224                 }
6225                 //</debug>
6226
6227                 Ext.syncRequire(name);
6228
6229                 cls = this.get(name);
6230             }
6231
6232             //<debug error>
6233             if (!cls) {
6234                 Ext.Error.raise({
6235                     sourceClass: "Ext",
6236                     sourceMethod: "create",
6237                     msg: "Cannot create an instance of unrecognized class name / alias: " + alias
6238                 });
6239             }
6240
6241             if (typeof cls !== 'function') {
6242                 Ext.Error.raise({
6243                     sourceClass: "Ext",
6244                     sourceMethod: "create",
6245                     msg: "'" + name + "' is a singleton and cannot be instantiated"
6246                 });
6247             }
6248             //</debug>
6249
6250             //<debug>
6251             if (!this.instantiationCounts[name]) {
6252                 this.instantiationCounts[name] = 0;
6253             }
6254
6255             this.instantiationCounts[name]++;
6256             //</debug>
6257
6258             return this.getInstantiator(args.length)(cls, args);
6259         },
6260
6261         /**
6262          * @private
6263          * @param name
6264          * @param args
6265          */
6266         dynInstantiate: function(name, args) {
6267             args = Ext.Array.from(args, true);
6268             args.unshift(name);
6269
6270             return this.instantiate.apply(this, args);
6271         },
6272
6273         /**
6274          * @private
6275          * @param length
6276          */
6277         getInstantiator: function(length) {
6278             if (!this.instantiators[length]) {
6279                 var i = length,
6280                     args = [];
6281
6282                 for (i = 0; i < length; i++) {
6283                     args.push('a['+i+']');
6284                 }
6285
6286                 this.instantiators[length] = new Function('c', 'a', 'return new c('+args.join(',')+')');
6287             }
6288
6289             return this.instantiators[length];
6290         },
6291
6292         /**
6293          * @private
6294          */
6295         postprocessors: {},
6296
6297         /**
6298          * @private
6299          */
6300         defaultPostprocessors: [],
6301
6302         /**
6303          * Register a post-processor function.
6304          *
6305          * @param {String} name
6306          * @param {Function} postprocessor
6307          */
6308         registerPostprocessor: function(name, fn, always) {
6309             this.postprocessors[name] = {
6310                 name: name,
6311                 always: always ||  false,
6312                 fn: fn
6313             };
6314
6315             return this;
6316         },
6317
6318         /**
6319          * Set the default post processors array stack which are applied to every class.
6320          *
6321          * @param {String/Array} The name of a registered post processor or an array of registered names.
6322          * @return {Ext.ClassManager} this
6323          */
6324         setDefaultPostprocessors: function(postprocessors) {
6325             this.defaultPostprocessors = Ext.Array.from(postprocessors);
6326
6327             return this;
6328         },
6329
6330         /**
6331          * Insert this post-processor at a specific position in the stack, optionally relative to
6332          * any existing post-processor
6333          *
6334          * @param {String} name The post-processor name. Note that it needs to be registered with
6335          * {@link Ext.ClassManager#registerPostprocessor} before this
6336          * @param {String} offset The insertion position. Four possible values are:
6337          * 'first', 'last', or: 'before', 'after' (relative to the name provided in the third argument)
6338          * @param {String} relativeName
6339          * @return {Ext.ClassManager} this
6340          */
6341         setDefaultPostprocessorPosition: function(name, offset, relativeName) {
6342             var defaultPostprocessors = this.defaultPostprocessors,
6343                 index;
6344
6345             if (typeof offset === 'string') {
6346                 if (offset === 'first') {
6347                     defaultPostprocessors.unshift(name);
6348
6349                     return this;
6350                 }
6351                 else if (offset === 'last') {
6352                     defaultPostprocessors.push(name);
6353
6354                     return this;
6355                 }
6356
6357                 offset = (offset === 'after') ? 1 : -1;
6358             }
6359
6360             index = Ext.Array.indexOf(defaultPostprocessors, relativeName);
6361
6362             if (index !== -1) {
6363                 defaultPostprocessors.splice(Math.max(0, index + offset), 0, name);
6364             }
6365
6366             return this;
6367         },
6368
6369         /**
6370          * Converts a string expression to an array of matching class names. An expression can either refers to class aliases
6371          * or class names. Expressions support wildcards:
6372
6373      // returns ['Ext.window.Window']
6374     var window = Ext.ClassManager.getNamesByExpression('widget.window');
6375
6376     // returns ['widget.panel', 'widget.window', ...]
6377     var allWidgets = Ext.ClassManager.getNamesByExpression('widget.*');
6378
6379     // returns ['Ext.data.Store', 'Ext.data.ArrayProxy', ...]
6380     var allData = Ext.ClassManager.getNamesByExpression('Ext.data.*');
6381
6382          * @param {String} expression
6383          * @return {Array} classNames
6384          * @markdown
6385          */
6386         getNamesByExpression: function(expression) {
6387             var nameToAliasesMap = this.maps.nameToAliases,
6388                 names = [],
6389                 name, alias, aliases, possibleName, regex, i, ln;
6390
6391             //<debug error>
6392             if (typeof expression !== 'string' || expression.length < 1) {
6393                 Ext.Error.raise({
6394                     sourceClass: "Ext.ClassManager",
6395                     sourceMethod: "getNamesByExpression",
6396                     msg: "Expression " + expression + " is invalid, must be a non-empty string"
6397                 });
6398             }
6399             //</debug>
6400
6401             if (expression.indexOf('*') !== -1) {
6402                 expression = expression.replace(/\*/g, '(.*?)');
6403                 regex = new RegExp('^' + expression + '$');
6404
6405                 for (name in nameToAliasesMap) {
6406                     if (nameToAliasesMap.hasOwnProperty(name)) {
6407                         aliases = nameToAliasesMap[name];
6408
6409                         if (name.search(regex) !== -1) {
6410                             names.push(name);
6411                         }
6412                         else {
6413                             for (i = 0, ln = aliases.length; i < ln; i++) {
6414                                 alias = aliases[i];
6415
6416                                 if (alias.search(regex) !== -1) {
6417                                     names.push(name);
6418                                     break;
6419                                 }
6420                             }
6421                         }
6422                     }
6423                 }
6424
6425             } else {
6426                 possibleName = this.getNameByAlias(expression);
6427
6428                 if (possibleName) {
6429                     names.push(possibleName);
6430                 } else {
6431                     possibleName = this.getNameByAlternate(expression);
6432
6433                     if (possibleName) {
6434                         names.push(possibleName);
6435                     } else {
6436                         names.push(expression);
6437                     }
6438                 }
6439             }
6440
6441             return names;
6442         }
6443     };
6444
6445     Manager.registerPostprocessor('alias', function(name, cls, data) {
6446         var aliases = data.alias,
6447             widgetPrefix = 'widget.',
6448             i, ln, alias;
6449
6450         if (!(aliases instanceof Array)) {
6451             aliases = [aliases];
6452         }
6453
6454         for (i = 0, ln = aliases.length; i < ln; i++) {
6455             alias = aliases[i];
6456
6457             //<debug error>
6458             if (typeof alias !== 'string') {
6459                 Ext.Error.raise({
6460                     sourceClass: "Ext",
6461                     sourceMethod: "define",
6462                     msg: "Invalid alias of: '" + alias + "' for class: '" + name + "'; must be a valid string"
6463                 });
6464             }
6465             //</debug>
6466
6467             this.setAlias(cls, alias);
6468         }
6469
6470         // This is ugly, will change to make use of parseNamespace for alias later on
6471         for (i = 0, ln = aliases.length; i < ln; i++) {
6472             alias = aliases[i];
6473
6474             if (alias.substring(0, widgetPrefix.length) === widgetPrefix) {
6475                 // Only the first alias with 'widget.' prefix will be used for xtype
6476                 cls.xtype = cls.$xtype = alias.substring(widgetPrefix.length);
6477                 break;
6478             }
6479         }
6480     });
6481
6482     Manager.registerPostprocessor('singleton', function(name, cls, data, fn) {
6483         fn.call(this, name, new cls(), data);
6484         return false;
6485     });
6486
6487     Manager.registerPostprocessor('alternateClassName', function(name, cls, data) {
6488         var alternates = data.alternateClassName,
6489             i, ln, alternate;
6490
6491         if (!(alternates instanceof Array)) {
6492             alternates = [alternates];
6493         }
6494
6495         for (i = 0, ln = alternates.length; i < ln; i++) {
6496             alternate = alternates[i];
6497
6498             //<debug error>
6499             if (typeof alternate !== 'string') {
6500                 Ext.Error.raise({
6501                     sourceClass: "Ext",
6502                     sourceMethod: "define",
6503                     msg: "Invalid alternate of: '" + alternate + "' for class: '" + name + "'; must be a valid string"
6504                 });
6505             }
6506             //</debug>
6507
6508             this.set(alternate, cls);
6509         }
6510     });
6511
6512     Manager.setDefaultPostprocessors(['alias', 'singleton', 'alternateClassName']);
6513
6514     Ext.apply(Ext, {
6515         /**
6516          * Convenient shorthand, see {@link Ext.ClassManager#instantiate}
6517          * @member Ext
6518          * @method create
6519          */
6520         create: alias(Manager, 'instantiate'),
6521
6522         /**
6523          * @private
6524          * API to be stablized
6525          *
6526          * @param {Mixed} item
6527          * @param {String} namespace
6528          */
6529         factory: function(item, namespace) {
6530             if (item instanceof Array) {
6531                 var i, ln;
6532
6533                 for (i = 0, ln = item.length; i < ln; i++) {
6534                     item[i] = Ext.factory(item[i], namespace);
6535                 }
6536
6537                 return item;
6538             }
6539
6540             var isString = (typeof item === 'string');
6541
6542             if (isString || (item instanceof Object && item.constructor === Object)) {
6543                 var name, config = {};
6544
6545                 if (isString) {
6546                     name = item;
6547                 }
6548                 else {
6549                     name = item.className;
6550                     config = item;
6551                     delete config.className;
6552                 }
6553
6554                 if (namespace !== undefined && name.indexOf(namespace) === -1) {
6555                     name = namespace + '.' + Ext.String.capitalize(name);
6556                 }
6557
6558                 return Ext.create(name, config);
6559             }
6560
6561             if (typeof item === 'function') {
6562                 return Ext.create(item);
6563             }
6564
6565             return item;
6566         },
6567
6568         /**
6569          * Convenient shorthand to create a widget by its xtype, also see {@link Ext.ClassManager#instantiateByAlias}
6570
6571     var button = Ext.widget('button'); // Equivalent to Ext.create('widget.button')
6572     var panel = Ext.widget('panel'); // Equivalent to Ext.create('widget.panel')
6573
6574          * @member Ext
6575          * @method widget
6576          * @markdown
6577          */
6578         widget: function(name) {
6579             var args = slice.call(arguments);
6580             args[0] = 'widget.' + name;
6581
6582             return Manager.instantiateByAlias.apply(Manager, args);
6583         },
6584
6585         /**
6586          * Convenient shorthand, see {@link Ext.ClassManager#instantiateByAlias}
6587          * @member Ext
6588          * @method createByAlias
6589          */
6590         createByAlias: alias(Manager, 'instantiateByAlias'),
6591
6592         /**
6593          * Convenient shorthand for {@link Ext.ClassManager#create}, see detailed {@link Ext.Class explanation}
6594          * @member Ext
6595          * @method define
6596          */
6597         define: alias(Manager, 'create'),
6598
6599         /**
6600          * Convenient shorthand, see {@link Ext.ClassManager#getName}
6601          * @member Ext
6602          * @method getClassName
6603          */
6604         getClassName: alias(Manager, 'getName'),
6605
6606         /**
6607          *
6608          * @param {Mixed} object
6609          */
6610         getDisplayName: function(object) {
6611             if (object.displayName) {
6612                 return object.displayName;
6613             }
6614
6615             if (object.$name && object.$class) {
6616                 return Ext.getClassName(object.$class) + '#' + object.$name;
6617             }
6618
6619             if (object.$className) {
6620                 return object.$className;
6621             }
6622
6623             return 'Anonymous';
6624         },
6625
6626         /**
6627          * Convenient shorthand, see {@link Ext.ClassManager#getClass}
6628          * @member Ext
6629          * @method getClassName
6630          */
6631         getClass: alias(Manager, 'getClass'),
6632
6633         /**
6634          * Creates namespaces to be used for scoping variables and classes so that they are not global.
6635          * Specifying the last node of a namespace implicitly creates all other nodes. Usage:
6636
6637     Ext.namespace('Company', 'Company.data');
6638
6639      // equivalent and preferable to the above syntax
6640     Ext.namespace('Company.data');
6641
6642     Company.Widget = function() { ... };
6643
6644     Company.data.CustomStore = function(config) { ... };
6645
6646          * @param {String} namespace1
6647          * @param {String} namespace2
6648          * @param {String} etc
6649          * @return {Object} The namespace object. (If multiple arguments are passed, this will be the last namespace created)
6650          * @function
6651          * @member Ext
6652          * @method namespace
6653          * @markdown
6654          */
6655         namespace: alias(Manager, 'createNamespaces')
6656     });
6657
6658     Ext.createWidget = Ext.widget;
6659
6660     /**
6661      * Convenient alias for {@link Ext#namespace Ext.namespace}
6662      * @member Ext
6663      * @method ns
6664      */
6665     Ext.ns = Ext.namespace;
6666
6667     Class.registerPreprocessor('className', function(cls, data) {
6668         if (data.$className) {
6669             cls.$className = data.$className;
6670             //<debug>
6671             cls.displayName = cls.$className;
6672             //</debug>
6673         }
6674     }, true);
6675
6676     Class.setDefaultPreprocessorPosition('className', 'first');
6677
6678 })(Ext.Class, Ext.Function.alias);
6679
6680 /**
6681  * @author Jacky Nguyen <jacky@sencha.com>
6682  * @docauthor Jacky Nguyen <jacky@sencha.com>
6683  * @class Ext.Loader
6684  *
6685
6686 Ext.Loader is the heart of the new dynamic dependency loading capability in Ext JS 4+. It is most commonly used
6687 via the {@link Ext#require} shorthand. Ext.Loader supports both asynchronous and synchronous loading
6688 approaches, and leverage their advantages for the best development flow. We'll discuss about the pros and cons of each approach:
6689
6690 # Asynchronous Loading #
6691
6692 - Advantages:
6693         + Cross-domain
6694         + No web server needed: you can run the application via the file system protocol (i.e: `file://path/to/your/index
6695  .html`)
6696         + Best possible debugging experience: error messages come with the exact file name and line number
6697
6698 - Disadvantages:
6699         + Dependencies need to be specified before-hand
6700
6701 ### Method 1: Explicitly include what you need: ###
6702
6703     // Syntax
6704     Ext.require({String/Array} expressions);
6705
6706     // Example: Single alias
6707     Ext.require('widget.window');
6708
6709     // Example: Single class name
6710     Ext.require('Ext.window.Window');
6711
6712     // Example: Multiple aliases / class names mix
6713     Ext.require(['widget.window', 'layout.border', 'Ext.data.Connection']);
6714
6715     // Wildcards
6716     Ext.require(['widget.*', 'layout.*', 'Ext.data.*']);
6717
6718 ### Method 2: Explicitly exclude what you don't need: ###
6719
6720     // Syntax: Note that it must be in this chaining format.
6721     Ext.exclude({String/Array} expressions)
6722        .require({String/Array} expressions);
6723
6724     // Include everything except Ext.data.*
6725     Ext.exclude('Ext.data.*').require('*'); 
6726
6727     // Include all widgets except widget.checkbox*,
6728     // which will match widget.checkbox, widget.checkboxfield, widget.checkboxgroup, etc.
6729     Ext.exclude('widget.checkbox*').require('widget.*');
6730
6731 # Synchronous Loading on Demand #
6732
6733 - *Advantages:*
6734         + There's no need to specify dependencies before-hand, which is always the convenience of including ext-all.js
6735  before
6736
6737 - *Disadvantages:*
6738         + Not as good debugging experience since file name won't be shown (except in Firebug at the moment)
6739         + Must be from the same domain due to XHR restriction
6740         + Need a web server, same reason as above
6741
6742 There's one simple rule to follow: Instantiate everything with Ext.create instead of the `new` keyword
6743
6744     Ext.create('widget.window', { ... }); // Instead of new Ext.window.Window({...});
6745
6746     Ext.create('Ext.window.Window', {}); // Same as above, using full class name instead of alias
6747
6748     Ext.widget('window', {}); // Same as above, all you need is the traditional `xtype`
6749
6750 Behind the scene, {@link Ext.ClassManager} will automatically check whether the given class name / alias has already
6751  existed on the page. If it's not, Ext.Loader will immediately switch itself to synchronous mode and automatic load the given
6752  class and all its dependencies.
6753
6754 # Hybrid Loading - The Best of Both Worlds #
6755
6756 It has all the advantages combined from asynchronous and synchronous loading. The development flow is simple:
6757
6758 ### Step 1: Start writing your application using synchronous approach. Ext.Loader will automatically fetch all
6759  dependencies on demand as they're needed during run-time. For example: ###
6760
6761     Ext.onReady(function(){
6762         var window = Ext.createWidget('window', {
6763             width: 500,
6764             height: 300,
6765             layout: {
6766                 type: 'border',
6767                 padding: 5
6768             },
6769             title: 'Hello Dialog',
6770             items: [{
6771                 title: 'Navigation',
6772                 collapsible: true,
6773                 region: 'west',
6774                 width: 200,
6775                 html: 'Hello',
6776                 split: true
6777             }, {
6778                 title: 'TabPanel',
6779                 region: 'center'
6780             }]
6781         });
6782
6783         window.show();
6784     })
6785
6786 ### Step 2: Along the way, when you need better debugging ability, watch the console for warnings like these: ###
6787
6788     [Ext.Loader] Synchronously loading 'Ext.window.Window'; consider adding Ext.require('Ext.window.Window') before your application's code
6789     ClassManager.js:432
6790     [Ext.Loader] Synchronously loading 'Ext.layout.container.Border'; consider adding Ext.require('Ext.layout.container.Border') before your application's code
6791
6792 Simply copy and paste the suggested code above `Ext.onReady`, i.e:
6793
6794     Ext.require('Ext.window.Window');
6795     Ext.require('Ext.layout.container.Border');
6796
6797     Ext.onReady(...);
6798
6799 Everything should now load via asynchronous mode.
6800
6801 # Deployment #
6802
6803 It's important to note that dynamic loading should only be used during development on your local machines.
6804 During production, all dependencies should be combined into one single JavaScript file. Ext.Loader makes
6805 the whole process of transitioning from / to between development / maintenance and production as easy as
6806 possible. Internally {@link Ext.Loader#history Ext.Loader.history} maintains the list of all dependencies your application
6807 needs in the exact loading sequence. It's as simple as concatenating all files in this array into one,
6808 then include it on top of your application.
6809
6810 This process will be automated with Sencha Command, to be released and documented towards Ext JS 4 Final.
6811
6812  * @singleton
6813  * @markdown
6814  */
6815
6816 (function(Manager, Class, flexSetter, alias) {
6817
6818     var
6819         //<if nonBrowser>
6820         isNonBrowser = typeof window === 'undefined',
6821         isNodeJS = isNonBrowser && (typeof require === 'function'),
6822         isPhantomJS = (typeof phantom !== 'undefined' && phantom.fs),
6823         //</if>
6824         dependencyProperties = ['extend', 'mixins', 'requires'],
6825         Loader;
6826
6827     Loader = Ext.Loader = {
6828         /**
6829          * @private
6830          */
6831         documentHead: typeof document !== 'undefined' && (document.head || document.getElementsByTagName('head')[0]),
6832
6833         /**
6834          * Flag indicating whether there are still files being loaded
6835          * @private
6836          */
6837         isLoading: false,
6838
6839         /**
6840          * Maintain the queue for all dependencies. Each item in the array is an object of the format:
6841          * {
6842          *      requires: [...], // The required classes for this queue item
6843          *      callback: function() { ... } // The function to execute when all classes specified in requires exist
6844          * }
6845          * @private
6846          */
6847         queue: [],
6848
6849         /**
6850          * Maintain the list of files that have already been handled so that they never get double-loaded
6851          * @private
6852          */
6853         isFileLoaded: {},
6854
6855         /**
6856          * Maintain the list of listeners to execute when all required scripts are fully loaded
6857          * @private
6858          */
6859         readyListeners: [],
6860
6861         /**
6862          * Contains optional dependencies to be loaded last
6863          * @private
6864          */
6865         optionalRequires: [],
6866
6867         /**
6868          * Map of fully qualified class names to an array of dependent classes.
6869          * @private
6870          */
6871         requiresMap: {},
6872
6873         /**
6874          * @private
6875          */
6876         numPendingFiles: 0,
6877
6878         /**
6879          * @private
6880          */
6881         numLoadedFiles: 0,
6882
6883         /** @private */
6884         hasFileLoadError: false,
6885
6886         /**
6887          * @private
6888          */
6889         classNameToFilePathMap: {},
6890
6891         /**
6892          * An array of class names to keep track of the dependency loading order.
6893          * This is not guaranteed to be the same everytime due to the asynchronous
6894          * nature of the Loader.
6895          *
6896          * @property history
6897          * @type Array
6898          */
6899         history: [],
6900
6901         /**
6902          * Configuration
6903          * @private
6904          */
6905         config: {
6906             /**
6907              * Whether or not to enable the dynamic dependency loading feature
6908              * Defaults to false
6909              * @cfg {Boolean} enabled
6910              */
6911             enabled: false,
6912
6913             /**
6914              * @cfg {Boolean} disableCaching
6915              * Appends current timestamp to script files to prevent caching
6916              * Defaults to true
6917              */
6918             disableCaching: true,
6919
6920             /**
6921              * @cfg {String} disableCachingParam
6922              * The get parameter name for the cache buster's timestamp.
6923              * Defaults to '_dc'
6924              */
6925             disableCachingParam: '_dc',
6926
6927             /**
6928              * @cfg {Object} paths
6929              * The mapping from namespaces to file paths
6930     {
6931         'Ext': '.', // This is set by default, Ext.layout.container.Container will be
6932                     // loaded from ./layout/Container.js
6933
6934         'My': './src/my_own_folder' // My.layout.Container will be loaded from
6935                                     // ./src/my_own_folder/layout/Container.js
6936     }
6937              * Note that all relative paths are relative to the current HTML document.
6938              * If not being specified, for example, <code>Other.awesome.Class</code>
6939              * will simply be loaded from <code>./Other/awesome/Class.js</code>
6940              */
6941             paths: {
6942                 'Ext': '.'
6943             }
6944         },
6945
6946         /**
6947          * Set the configuration for the loader. This should be called right after ext-core.js
6948          * (or ext-core-debug.js) is included in the page, i.e:
6949
6950     <script type="text/javascript" src="ext-core-debug.js"></script>
6951     <script type="text/javascript">
6952       Ext.Loader.setConfig({
6953           enabled: true,
6954           paths: {
6955               'My': 'my_own_path'
6956           }
6957       });
6958     <script>
6959     <script type="text/javascript">
6960       Ext.require(...);
6961
6962       Ext.onReady(function() {
6963           // application code here
6964       });
6965     </script>
6966
6967          * Refer to {@link Ext.Loader#configs} for the list of possible properties
6968          *
6969          * @param {Object} config The config object to override the default values in {@link Ext.Loader#config}
6970          * @return {Ext.Loader} this
6971          * @markdown
6972          */
6973         setConfig: function(name, value) {
6974             if (Ext.isObject(name) && arguments.length === 1) {
6975                 Ext.Object.merge(this.config, name);
6976             }
6977             else {
6978                 this.config[name] = (Ext.isObject(value)) ? Ext.Object.merge(this.config[name], value) : value;
6979             }
6980
6981             return this;
6982         },
6983
6984         /**
6985          * Get the config value corresponding to the specified name. If no name is given, will return the config object
6986          * @param {String} name The config property name
6987          * @return {Object/Mixed}
6988          */
6989         getConfig: function(name) {
6990             if (name) {
6991                 return this.config[name];
6992             }
6993
6994             return this.config;
6995         },
6996
6997         /**
6998          * Sets the path of a namespace.
6999          * For Example:
7000
7001     Ext.Loader.setPath('Ext', '.');
7002
7003          * @param {String/Object} name See {@link Ext.Function#flexSetter flexSetter}
7004          * @param {String} path See {@link Ext.Function#flexSetter flexSetter}
7005          * @return {Ext.Loader} this
7006          * @markdown
7007          */
7008         setPath: flexSetter(function(name, path) {
7009             //<if nonBrowser>
7010             if (isNonBrowser) {
7011                 if (isNodeJS) {
7012                     path = require('fs').realpathSync(path);
7013                 }
7014             }
7015             //</if>
7016             this.config.paths[name] = path;
7017
7018             return this;
7019         }),
7020
7021         /**
7022          * Translates a className to a file path by adding the
7023          * the proper prefix and converting the .'s to /'s. For example:
7024
7025     Ext.Loader.setPath('My', '/path/to/My');
7026
7027     alert(Ext.Loader.getPath('My.awesome.Class')); // alerts '/path/to/My/awesome/Class.js'
7028
7029          * Note that the deeper namespace levels, if explicitly set, are always resolved first. For example:
7030
7031     Ext.Loader.setPath({
7032         'My': '/path/to/lib',
7033         'My.awesome': '/other/path/for/awesome/stuff',
7034         'My.awesome.more': '/more/awesome/path'
7035     });
7036
7037     alert(Ext.Loader.getPath('My.awesome.Class')); // alerts '/other/path/for/awesome/stuff/Class.js'
7038
7039     alert(Ext.Loader.getPath('My.awesome.more.Class')); // alerts '/more/awesome/path/Class.js'
7040
7041     alert(Ext.Loader.getPath('My.cool.Class')); // alerts '/path/to/lib/cool/Class.js'
7042
7043     alert(Ext.Loader.getPath('Unknown.strange.Stuff')); // alerts 'Unknown/strange/Stuff.js'
7044
7045          * @param {String} className
7046          * @return {String} path
7047          * @markdown
7048          */
7049         getPath: function(className) {
7050             var path = '',
7051                 paths = this.config.paths,
7052                 prefix = this.getPrefix(className);
7053
7054             if (prefix.length > 0) {
7055                 if (prefix === className) {
7056                     return paths[prefix];
7057                 }
7058
7059                 path = paths[prefix];
7060                 className = className.substring(prefix.length + 1);
7061             }
7062
7063             if (path.length > 0) {
7064                 path += '/';
7065             }
7066
7067             return path.replace(/\/\.\//g, '/') + className.replace(/\./g, "/") + '.js';
7068         },
7069
7070         /**
7071          * @private
7072          * @param {String} className
7073          */
7074         getPrefix: function(className) {
7075             var paths = this.config.paths,
7076                 prefix, deepestPrefix = '';
7077
7078             if (paths.hasOwnProperty(className)) {
7079                 return className;
7080             }
7081
7082             for (prefix in paths) {
7083                 if (paths.hasOwnProperty(prefix) && prefix + '.' === className.substring(0, prefix.length + 1)) {
7084                     if (prefix.length > deepestPrefix.length) {
7085                         deepestPrefix = prefix;
7086                     }
7087                 }
7088             }
7089
7090             return deepestPrefix;
7091         },
7092
7093         /**
7094          * Refresh all items in the queue. If all dependencies for an item exist during looping,
7095          * it will execute the callback and call refreshQueue again. Triggers onReady when the queue is
7096          * empty
7097          * @private
7098          */
7099         refreshQueue: function() {
7100             var ln = this.queue.length,
7101                 i, item, j, requires;
7102
7103             if (ln === 0) {
7104                 this.triggerReady();
7105                 return;
7106             }
7107
7108             for (i = 0; i < ln; i++) {
7109                 item = this.queue[i];
7110
7111                 if (item) {
7112                     requires = item.requires;
7113
7114                     // Don't bother checking when the number of files loaded
7115                     // is still less than the array length
7116                     if (requires.length > this.numLoadedFiles) {
7117                         continue;
7118                     }
7119
7120                     j = 0;
7121
7122                     do {
7123                         if (Manager.isCreated(requires[j])) {
7124                             // Take out from the queue
7125                             requires.splice(j, 1);
7126                         }
7127                         else {
7128                             j++;
7129                         }
7130                     } while (j < requires.length);
7131
7132                     if (item.requires.length === 0) {
7133                         this.queue.splice(i, 1);
7134                         item.callback.call(item.scope);
7135                         this.refreshQueue();
7136                         break;
7137                     }
7138                 }
7139             }
7140
7141             return this;
7142         },
7143
7144         /**
7145          * Inject a script element to document's head, call onLoad and onError accordingly
7146          * @private
7147          */
7148         injectScriptElement: function(url, onLoad, onError, scope) {
7149             var script = document.createElement('script'),
7150                 me = this,
7151                 onLoadFn = function() {
7152                     me.cleanupScriptElement(script);
7153                     onLoad.call(scope);
7154                 },
7155                 onErrorFn = function() {
7156                     me.cleanupScriptElement(script);
7157                     onError.call(scope);
7158                 };
7159
7160             script.type = 'text/javascript';
7161             script.src = url;
7162             script.onload = onLoadFn;
7163             script.onerror = onErrorFn;
7164             script.onreadystatechange = function() {
7165                 if (this.readyState === 'loaded' || this.readyState === 'complete') {
7166                     onLoadFn();
7167                 }
7168             };
7169
7170             this.documentHead.appendChild(script);
7171
7172             return script;
7173         },
7174
7175         /**
7176          * @private
7177          */
7178         cleanupScriptElement: function(script) {
7179             script.onload = null;
7180             script.onreadystatechange = null;
7181             script.onerror = null;
7182
7183             return this;
7184         },
7185
7186         /**
7187          * Load a script file, supports both asynchronous and synchronous approaches
7188          *
7189          * @param {String} url
7190          * @param {Function} onLoad
7191          * @param {Scope} scope
7192          * @param {Boolean} synchronous
7193          * @private
7194          */
7195         loadScriptFile: function(url, onLoad, onError, scope, synchronous) {
7196             var me = this,
7197                 noCacheUrl = url + (this.getConfig('disableCaching') ? ('?' + this.getConfig('disableCachingParam') + '=' + Ext.Date.now()) : ''),
7198                 fileName = url.split('/').pop(),
7199                 isCrossOriginRestricted = false,
7200                 xhr, status, onScriptError;
7201
7202             scope = scope || this;
7203
7204             this.isLoading = true;
7205
7206             if (!synchronous) {
7207                 onScriptError = function() {
7208                     onError.call(scope, "Failed loading '" + url + "', please verify that the file exists", synchronous);
7209                 };
7210
7211                 if (!Ext.isReady && Ext.onDocumentReady) {
7212                     Ext.onDocumentReady(function() {
7213                         me.injectScriptElement(noCacheUrl, onLoad, onScriptError, scope);
7214                     });
7215                 }
7216                 else {
7217                     this.injectScriptElement(noCacheUrl, onLoad, onScriptError, scope);
7218                 }
7219             }
7220             else {
7221                 if (typeof XMLHttpRequest !== 'undefined') {
7222                     xhr = new XMLHttpRequest();
7223                 } else {
7224                     xhr = new ActiveXObject('Microsoft.XMLHTTP');
7225                 }
7226
7227                 try {
7228                     xhr.open('GET', noCacheUrl, false);
7229                     xhr.send(null);
7230                 } catch (e) {
7231                     isCrossOriginRestricted = true;
7232                 }
7233
7234                 status = (xhr.status === 1223) ? 204 : xhr.status;
7235
7236                 if (!isCrossOriginRestricted) {
7237                     isCrossOriginRestricted = (status === 0);
7238                 }
7239
7240                 if (isCrossOriginRestricted
7241                 //<if isNonBrowser>
7242                 && !isPhantomJS
7243                 //</if>
7244                 ) {
7245                     onError.call(this, "Failed loading synchronously via XHR: '" + url + "'; It's likely that the file is either " +
7246                                        "being loaded from a different domain or from the local file system whereby cross origin " +
7247                                        "requests are not allowed due to security reasons. Use asynchronous loading with " +
7248                                        "Ext.require instead.", synchronous);
7249                 }
7250                 else if (status >= 200 && status < 300
7251                 //<if isNonBrowser>
7252                 || isPhantomJS
7253                 //</if>
7254                 ) {
7255                     // Firebug friendly, file names are still shown even though they're eval'ed code
7256                     new Function(xhr.responseText + "\n//@ sourceURL=" + fileName)();
7257
7258                     onLoad.call(scope);
7259                 }
7260                 else {
7261                     onError.call(this, "Failed loading synchronously via XHR: '" + url + "'; please " +
7262                                        "verify that the file exists. " +
7263                                        "XHR status code: " + status, synchronous);
7264                 }
7265
7266                 // Prevent potential IE memory leak
7267                 xhr = null;
7268             }
7269         },
7270
7271         /**
7272          * Explicitly exclude files from being loaded. Useful when used in conjunction with a broad include expression.
7273          * Can be chained with more `require` and `exclude` methods, eg:
7274
7275     Ext.exclude('Ext.data.*').require('*');
7276
7277     Ext.exclude('widget.button*').require('widget.*');
7278
7279          * @param {Array} excludes
7280          * @return {Object} object contains `require` method for chaining
7281          * @markdown
7282          */
7283         exclude: function(excludes) {
7284             var me = this;
7285
7286             return {
7287                 require: function(expressions, fn, scope) {
7288                     return me.require(expressions, fn, scope, excludes);
7289                 },
7290
7291                 syncRequire: function(expressions, fn, scope) {
7292                     return me.syncRequire(expressions, fn, scope, excludes);
7293                 }
7294             };
7295         },
7296
7297         /**
7298          * 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
7299          * @param {String/Array} expressions Can either be a string or an array of string
7300          * @param {Function} fn (Optional) The callback function
7301          * @param {Object} scope (Optional) The execution scope (`this`) of the callback function
7302          * @param {String/Array} excludes (Optional) Classes to be excluded, useful when being used with expressions
7303          * @markdown
7304          */
7305         syncRequire: function() {
7306             this.syncModeEnabled = true;
7307             this.require.apply(this, arguments);
7308             this.refreshQueue();
7309             this.syncModeEnabled = false;
7310         },
7311
7312         /**
7313          * Loads all classes by the given names and all their direct dependencies; optionally executes the given callback function when
7314          * finishes, within the optional scope. This method is aliased by {@link Ext#require Ext.require} for convenience
7315          * @param {String/Array} expressions Can either be a string or an array of string
7316          * @param {Function} fn (Optional) The callback function
7317          * @param {Object} scope (Optional) The execution scope (`this`) of the callback function
7318          * @param {String/Array} excludes (Optional) Classes to be excluded, useful when being used with expressions
7319          * @markdown
7320          */
7321         require: function(expressions, fn, scope, excludes) {
7322             var filePath, expression, exclude, className, excluded = {},
7323                 excludedClassNames = [],
7324                 possibleClassNames = [],
7325                 possibleClassName, classNames = [],
7326                 i, j, ln, subLn;
7327
7328             expressions = Ext.Array.from(expressions);
7329             excludes = Ext.Array.from(excludes);
7330
7331             fn = fn || Ext.emptyFn;
7332
7333             scope = scope || Ext.global;
7334
7335             for (i = 0, ln = excludes.length; i < ln; i++) {
7336                 exclude = excludes[i];
7337
7338                 if (typeof exclude === 'string' && exclude.length > 0) {
7339                     excludedClassNames = Manager.getNamesByExpression(exclude);
7340
7341                     for (j = 0, subLn = excludedClassNames.length; j < subLn; j++) {
7342                         excluded[excludedClassNames[j]] = true;
7343                     }
7344                 }
7345             }
7346
7347             for (i = 0, ln = expressions.length; i < ln; i++) {
7348                 expression = expressions[i];
7349
7350                 if (typeof expression === 'string' && expression.length > 0) {
7351                     possibleClassNames = Manager.getNamesByExpression(expression);
7352
7353                     for (j = 0, subLn = possibleClassNames.length; j < subLn; j++) {
7354                         possibleClassName = possibleClassNames[j];
7355
7356                         if (!excluded.hasOwnProperty(possibleClassName) && !Manager.isCreated(possibleClassName)) {
7357                             Ext.Array.include(classNames, possibleClassName);
7358                         }
7359                     }
7360                 }
7361             }
7362
7363             // If the dynamic dependency feature is not being used, throw an error
7364             // if the dependencies are not defined
7365             if (!this.config.enabled) {
7366                 if (classNames.length > 0) {
7367                     Ext.Error.raise({
7368                         sourceClass: "Ext.Loader",
7369                         sourceMethod: "require",
7370                         msg: "Ext.Loader is not enabled, so dependencies cannot be resolved dynamically. " +
7371                              "Missing required class" + ((classNames.length > 1) ? "es" : "") + ": " + classNames.join(', ')
7372                     });
7373                 }
7374             }
7375
7376             if (classNames.length === 0) {
7377                 fn.call(scope);
7378                 return this;
7379             }
7380
7381             this.queue.push({
7382                 requires: classNames,
7383                 callback: fn,
7384                 scope: scope
7385             });
7386
7387             classNames = classNames.slice();
7388
7389             for (i = 0, ln = classNames.length; i < ln; i++) {
7390                 className = classNames[i];
7391
7392                 if (!this.isFileLoaded.hasOwnProperty(className)) {
7393                     this.isFileLoaded[className] = false;
7394
7395                     filePath = this.getPath(className);
7396
7397                     this.classNameToFilePathMap[className] = filePath;
7398
7399                     this.numPendingFiles++;
7400
7401                     //<if nonBrowser>
7402                     if (isNonBrowser) {
7403                         if (isNodeJS) {
7404                             require(filePath);
7405                         }
7406                         // Temporary support for hammerjs
7407                         else {
7408                             var f = fs.open(filePath),
7409                                 content = '',
7410                                 line;
7411
7412                             while (true) {
7413                                 line = f.readLine();
7414                                 if (line.length === 0) {
7415                                     break;
7416                                 }
7417                                 content += line;
7418                             }
7419
7420                             f.close();
7421                             eval(content);
7422                         }
7423
7424                         this.onFileLoaded(className, filePath);
7425
7426                         if (ln === 1) {
7427                             return Manager.get(className);
7428                         }
7429
7430                         continue;
7431                     }
7432                     //</if>
7433                     this.loadScriptFile(
7434                         filePath,
7435                         Ext.Function.pass(this.onFileLoaded, [className, filePath], this),
7436                         Ext.Function.pass(this.onFileLoadError, [className, filePath]),
7437                         this,
7438                         this.syncModeEnabled
7439                     );
7440                 }
7441             }
7442
7443             return this;
7444         },
7445
7446         /**
7447          * @private
7448          * @param {String} className
7449          * @param {String} filePath
7450          */
7451         onFileLoaded: function(className, filePath) {
7452             this.numLoadedFiles++;
7453
7454             this.isFileLoaded[className] = true;
7455
7456             this.numPendingFiles--;
7457
7458             if (this.numPendingFiles === 0) {
7459                 this.refreshQueue();
7460             }
7461
7462             //<debug>
7463             if (this.numPendingFiles <= 1) {
7464                 window.status = "Finished loading all dependencies, onReady fired!";
7465             }
7466             else {
7467                 window.status = "Loading dependencies, " + this.numPendingFiles + " files left...";
7468             }
7469             //</debug>
7470
7471             //<debug>
7472             if (!this.syncModeEnabled && this.numPendingFiles === 0 && this.isLoading && !this.hasFileLoadError) {
7473                 var queue = this.queue,
7474                     requires,
7475                     i, ln, j, subLn, missingClasses = [], missingPaths = [];
7476
7477                 for (i = 0, ln = queue.length; i < ln; i++) {
7478                     requires = queue[i].requires;
7479
7480                     for (j = 0, subLn = requires.length; j < ln; j++) {
7481                         if (this.isFileLoaded[requires[j]]) {
7482                             missingClasses.push(requires[j]);
7483                         }
7484                     }
7485                 }
7486
7487                 if (missingClasses.length < 1) {
7488                     return;
7489                 }
7490
7491                 missingClasses = Ext.Array.filter(missingClasses, function(item) {
7492                     return !this.requiresMap.hasOwnProperty(item);
7493                 }, this);
7494
7495                 for (i = 0,ln = missingClasses.length; i < ln; i++) {
7496                     missingPaths.push(this.classNameToFilePathMap[missingClasses[i]]);
7497                 }
7498
7499                 Ext.Error.raise({
7500                     sourceClass: "Ext.Loader",
7501                     sourceMethod: "onFileLoaded",
7502                     msg: "The following classes are not declared even if their files have been " +
7503                             "loaded: '" + missingClasses.join("', '") + "'. Please check the source code of their " +
7504                             "corresponding files for possible typos: '" + missingPaths.join("', '") + "'"
7505                 });
7506             }
7507             //</debug>
7508         },
7509
7510         /**
7511          * @private
7512          */
7513         onFileLoadError: function(className, filePath, errorMessage, isSynchronous) {
7514             this.numPendingFiles--;
7515             this.hasFileLoadError = true;
7516
7517             //<debug error>
7518             Ext.Error.raise({
7519                 sourceClass: "Ext.Loader",
7520                 classToLoad: className,
7521                 loadPath: filePath,
7522                 loadingType: isSynchronous ? 'synchronous' : 'async',
7523                 msg: errorMessage
7524             });
7525             //</debug>
7526         },
7527
7528         /**
7529          * @private
7530          */
7531         addOptionalRequires: function(requires) {
7532             var optionalRequires = this.optionalRequires,
7533                 i, ln, require;
7534
7535             requires = Ext.Array.from(requires);
7536
7537             for (i = 0, ln = requires.length; i < ln; i++) {
7538                 require = requires[i];
7539
7540                 Ext.Array.include(optionalRequires, require);
7541             }
7542
7543             return this;
7544         },
7545
7546         /**
7547          * @private
7548          */
7549         triggerReady: function(force) {
7550             var readyListeners = this.readyListeners,
7551                 optionalRequires, listener;
7552
7553             if (this.isLoading || force) {
7554                 this.isLoading = false;
7555
7556                 if (this.optionalRequires.length) {
7557                     // Clone then empty the array to eliminate potential recursive loop issue
7558                     optionalRequires = Ext.Array.clone(this.optionalRequires);
7559
7560                     // Empty the original array
7561                     this.optionalRequires.length = 0;
7562
7563                     this.require(optionalRequires, Ext.Function.pass(this.triggerReady, [true], this), this);
7564                     return this;
7565                 }
7566
7567                 while (readyListeners.length) {
7568                     listener = readyListeners.shift();
7569                     listener.fn.call(listener.scope);
7570
7571                     if (this.isLoading) {
7572                         return this;
7573                     }
7574                 }
7575             }
7576
7577             return this;
7578         },
7579
7580         /**
7581          * Add a new listener to be executed when all required scripts are fully loaded
7582          *
7583          * @param {Function} fn The function callback to be executed
7584          * @param {Object} scope The execution scope (<code>this</code>) of the callback function
7585          * @param {Boolean} withDomReady Whether or not to wait for document dom ready as well
7586          */
7587         onReady: function(fn, scope, withDomReady, options) {
7588             var oldFn;
7589
7590             if (withDomReady !== false && Ext.onDocumentReady) {
7591                 oldFn = fn;
7592
7593                 fn = function() {
7594                     Ext.onDocumentReady(oldFn, scope, options);
7595                 };
7596             }
7597
7598             if (!this.isLoading) {
7599                 fn.call(scope);
7600             }
7601             else {
7602                 this.readyListeners.push({
7603                     fn: fn,
7604                     scope: scope
7605                 });
7606             }
7607         },
7608
7609         /**
7610          * @private
7611          * @param {String} className
7612          */
7613         historyPush: function(className) {
7614             if (className && this.isFileLoaded.hasOwnProperty(className)) {
7615                 Ext.Array.include(this.history, className);
7616             }
7617
7618             return this;
7619         }
7620     };
7621
7622     /**
7623      * Convenient alias of {@link Ext.Loader#require}. Please see the introduction documentation of
7624      * {@link Ext.Loader} for examples.
7625      * @member Ext
7626      * @method require
7627      */
7628     Ext.require = alias(Loader, 'require');
7629
7630     /**
7631      * Synchronous version of {@link Ext#require}, convenient alias of {@link Ext.Loader#syncRequire}.
7632      *
7633      * @member Ext
7634      * @method syncRequire
7635      */
7636     Ext.syncRequire = alias(Loader, 'syncRequire');
7637
7638     /**
7639      * Convenient shortcut to {@link Ext.Loader#exclude}
7640      * @member Ext
7641      * @method exclude
7642      */
7643     Ext.exclude = alias(Loader, 'exclude');
7644
7645     /**
7646      * @member Ext
7647      * @method onReady
7648      */
7649     Ext.onReady = function(fn, scope, options) {
7650         Loader.onReady(fn, scope, true, options);
7651     };
7652
7653     Class.registerPreprocessor('loader', function(cls, data, continueFn) {
7654         var me = this,
7655             dependencies = [],
7656             className = Manager.getName(cls),
7657             i, j, ln, subLn, value, propertyName, propertyValue;
7658
7659         /*
7660         Basically loop through the dependencyProperties, look for string class names and push
7661         them into a stack, regardless of whether the property's value is a string, array or object. For example:
7662         {
7663               extend: 'Ext.MyClass',
7664               requires: ['Ext.some.OtherClass'],
7665               mixins: {
7666                   observable: 'Ext.util.Observable';
7667               }
7668         }
7669         which will later be transformed into:
7670         {
7671               extend: Ext.MyClass,
7672               requires: [Ext.some.OtherClass],
7673               mixins: {
7674                   observable: Ext.util.Observable;
7675               }
7676         }
7677         */
7678
7679         for (i = 0, ln = dependencyProperties.length; i < ln; i++) {
7680             propertyName = dependencyProperties[i];
7681
7682             if (data.hasOwnProperty(propertyName)) {
7683                 propertyValue = data[propertyName];
7684
7685                 if (typeof propertyValue === 'string') {
7686                     dependencies.push(propertyValue);
7687                 }
7688                 else if (propertyValue instanceof Array) {
7689                     for (j = 0, subLn = propertyValue.length; j < subLn; j++) {
7690                         value = propertyValue[j];
7691
7692                         if (typeof value === 'string') {
7693                             dependencies.push(value);
7694                         }
7695                     }
7696                 }
7697                 else {
7698                     for (j in propertyValue) {
7699                         if (propertyValue.hasOwnProperty(j)) {
7700                             value = propertyValue[j];
7701
7702                             if (typeof value === 'string') {
7703                                 dependencies.push(value);
7704                             }
7705                         }
7706                     }
7707                 }
7708             }
7709         }
7710
7711         if (dependencies.length === 0) {
7712 //            Loader.historyPush(className);
7713             return;
7714         }
7715
7716         //<debug error>
7717         var deadlockPath = [],
7718             requiresMap = Loader.requiresMap,
7719             detectDeadlock;
7720
7721         /*
7722         Automatically detect deadlocks before-hand,
7723         will throw an error with detailed path for ease of debugging. Examples of deadlock cases:
7724
7725         - A extends B, then B extends A
7726         - A requires B, B requires C, then C requires A
7727
7728         The detectDeadlock function will recursively transverse till the leaf, hence it can detect deadlocks
7729         no matter how deep the path is.
7730         */
7731
7732         if (className) {
7733             requiresMap[className] = dependencies;
7734
7735             detectDeadlock = function(cls) {
7736                 deadlockPath.push(cls);
7737
7738                 if (requiresMap[cls]) {
7739                     if (Ext.Array.contains(requiresMap[cls], className)) {
7740                         Ext.Error.raise({
7741                             sourceClass: "Ext.Loader",
7742                             msg: "Deadlock detected while loading dependencies! '" + className + "' and '" +
7743                                 deadlockPath[1] + "' " + "mutually require each other. Path: " +
7744                                 deadlockPath.join(' -> ') + " -> " + deadlockPath[0]
7745                         });
7746                     }
7747
7748                     for (i = 0, ln = requiresMap[cls].length; i < ln; i++) {
7749                         detectDeadlock(requiresMap[cls][i]);
7750                     }
7751                 }
7752             };
7753
7754             detectDeadlock(className);
7755         }
7756
7757         //</debug>
7758
7759         Loader.require(dependencies, function() {
7760             for (i = 0, ln = dependencyProperties.length; i < ln; i++) {
7761                 propertyName = dependencyProperties[i];
7762
7763                 if (data.hasOwnProperty(propertyName)) {
7764                     propertyValue = data[propertyName];
7765
7766                     if (typeof propertyValue === 'string') {
7767                         data[propertyName] = Manager.get(propertyValue);
7768                     }
7769                     else if (propertyValue instanceof Array) {
7770                         for (j = 0, subLn = propertyValue.length; j < subLn; j++) {
7771                             value = propertyValue[j];
7772
7773                             if (typeof value === 'string') {
7774                                 data[propertyName][j] = Manager.get(value);
7775                             }
7776                         }
7777                     }
7778                     else {
7779                         for (var k in propertyValue) {
7780                             if (propertyValue.hasOwnProperty(k)) {
7781                                 value = propertyValue[k];
7782
7783                                 if (typeof value === 'string') {
7784                                     data[propertyName][k] = Manager.get(value);
7785                                 }
7786                             }
7787                         }
7788                     }
7789                 }
7790             }
7791
7792             continueFn.call(me, cls, data);
7793         });
7794
7795         return false;
7796     }, true);
7797
7798     Class.setDefaultPreprocessorPosition('loader', 'after', 'className');
7799
7800     Manager.registerPostprocessor('uses', function(name, cls, data) {
7801         var uses = Ext.Array.from(data.uses),
7802             items = [],
7803             i, ln, item;
7804
7805         for (i = 0, ln = uses.length; i < ln; i++) {
7806             item = uses[i];
7807
7808             if (typeof item === 'string') {
7809                 items.push(item);
7810             }
7811         }
7812
7813         Loader.addOptionalRequires(items);
7814     });
7815
7816     Manager.setDefaultPostprocessorPosition('uses', 'last');
7817
7818 })(Ext.ClassManager, Ext.Class, Ext.Function.flexSetter, Ext.Function.alias);
7819
7820 /**
7821  * @class Ext.Error
7822  * @private
7823  * @extends Error
7824
7825 A wrapper class for the native JavaScript Error object that adds a few useful capabilities for handling
7826 errors in an Ext application. When you use Ext.Error to {@link #raise} an error from within any class that
7827 uses the Ext 4 class system, the Error class can automatically add the source class and method from which
7828 the error was raised. It also includes logic to automatically log the eroor to the console, if available, 
7829 with additional metadata about the error. In all cases, the error will always be thrown at the end so that
7830 execution will halt.
7831
7832 Ext.Error also offers a global error {@link #handle handling} method that can be overridden in order to 
7833 handle application-wide errors in a single spot. You can optionally {@link #ignore} errors altogether,
7834 although in a real application it's usually a better idea to override the handling function and perform
7835 logging or some other method of reporting the errors in a way that is meaningful to the application.
7836
7837 At its simplest you can simply raise an error as a simple string from within any code:
7838
7839 #Example usage:#
7840
7841     Ext.Error.raise('Something bad happened!');
7842     
7843 If raised from plain JavaScript code, the error will be logged to the console (if available) and the message
7844 displayed. In most cases however you'll be raising errors from within a class, and it may often be useful to add
7845 additional metadata about the error being raised.  The {@link #raise} method can also take a config object.
7846 In this form the `msg` attribute becomes the error description, and any other data added to the config gets
7847 added to the error object and, if the console is available, logged to the console for inspection.
7848
7849 #Example usage:#
7850  
7851     Ext.define('Ext.Foo', {
7852         doSomething: function(option){
7853             if (someCondition === false) {
7854                 Ext.Error.raise({
7855                     msg: 'You cannot do that!',
7856                     option: option,   // whatever was passed into the method
7857                     'error code': 100 // other arbitrary info
7858                 });
7859             }
7860         }
7861     });
7862
7863 If a console is available (that supports the `console.dir` function) you'll see console output like:
7864
7865     An error was raised with the following data:
7866     option:         Object { foo: "bar"}
7867         foo:        "bar"
7868     error code:     100
7869     msg:            "You cannot do that!"
7870     sourceClass:   "Ext.Foo"
7871     sourceMethod:  "doSomething"
7872     
7873     uncaught exception: You cannot do that!
7874
7875 As you can see, the error will report exactly where it was raised and will include as much information as the 
7876 raising code can usefully provide.
7877
7878 If you want to handle all application errors globally you can simply override the static {@link handle} method
7879 and provide whatever handling logic you need. If the method returns true then the error is considered handled
7880 and will not be thrown to the browser. If anything but true is returned then the error will be thrown normally.
7881
7882 #Example usage:#
7883
7884     Ext.Error.handle = function(err) {
7885         if (err.someProperty == 'NotReallyAnError') {
7886             // maybe log something to the application here if applicable
7887             return true;
7888         }
7889         // any non-true return value (including none) will cause the error to be thrown
7890     }
7891
7892  * Create a new Error object
7893  * @param {Object} config The config object
7894  * @markdown
7895  * @author Brian Moeskau <brian@sencha.com>
7896  * @docauthor Brian Moeskau <brian@sencha.com>
7897  */
7898 Ext.Error = Ext.extend(Error, {
7899     statics: {
7900         /**
7901          * @property ignore
7902 Static flag that can be used to globally disable error reporting to the browser if set to true
7903 (defaults to false). Note that if you ignore Ext errors it's likely that some other code may fail
7904 and throw a native JavaScript error thereafter, so use with caution. In most cases it will probably
7905 be preferable to supply a custom error {@link #handle handling} function instead.
7906
7907 #Example usage:#
7908
7909     Ext.Error.ignore = true;
7910
7911          * @markdown
7912          * @static
7913          */
7914         ignore: false,
7915
7916         /**
7917 Raise an error that can include additional data and supports automatic console logging if available. 
7918 You can pass a string error message or an object with the `msg` attribute which will be used as the 
7919 error message. The object can contain any other name-value attributes (or objects) to be logged 
7920 along with the error.
7921
7922 Note that after displaying the error message a JavaScript error will ultimately be thrown so that 
7923 execution will halt.
7924
7925 #Example usage:#
7926
7927     Ext.Error.raise('A simple string error message');
7928
7929     // or...
7930
7931     Ext.define('Ext.Foo', {
7932         doSomething: function(option){
7933             if (someCondition === false) {
7934                 Ext.Error.raise({
7935                     msg: 'You cannot do that!',
7936                     option: option,   // whatever was passed into the method
7937                     'error code': 100 // other arbitrary info
7938                 });
7939             }
7940         }
7941     });
7942          * @param {String/Object} err The error message string, or an object containing the 
7943          * attribute "msg" that will be used as the error message. Any other data included in
7944          * the object will also be logged to the browser console, if available.
7945          * @static
7946          * @markdown
7947          */
7948         raise: function(err){
7949             err = err || {};
7950             if (Ext.isString(err)) {
7951                 err = { msg: err };
7952             }
7953
7954             var method = this.raise.caller;
7955
7956             if (method) {
7957                 if (method.$name) {
7958                     err.sourceMethod = method.$name;
7959                 }
7960                 if (method.$owner) {
7961                     err.sourceClass = method.$owner.$className;
7962                 }
7963             }
7964
7965             if (Ext.Error.handle(err) !== true) {
7966                 var global = Ext.global,
7967                     con = global.console,
7968                     msg = Ext.Error.prototype.toString.call(err),
7969                     noConsoleMsg = 'An uncaught error was raised: "' + msg + 
7970                         '". Use Firebug or Webkit console for additional details.';
7971
7972                 if (con) {
7973                     if (con.dir) {
7974                         con.warn('An uncaught error was raised with the following data:');
7975                         con.dir(err);
7976                     }
7977                     else {
7978                         con.warn(noConsoleMsg);
7979                     }
7980                     if (con.error) {
7981                         con.error(msg);
7982                     }
7983                 }
7984                 else if (global.alert){
7985                     global.alert(noConsoleMsg);
7986                 }
7987                 
7988                 throw new Ext.Error(err);
7989             }
7990         },
7991
7992         /**
7993 Globally handle any Ext errors that may be raised, optionally providing custom logic to
7994 handle different errors individually. Return true from the function to bypass throwing the
7995 error to the browser, otherwise the error will be thrown and execution will halt.
7996
7997 #Example usage:#
7998
7999     Ext.Error.handle = function(err) {
8000         if (err.someProperty == 'NotReallyAnError') {
8001             // maybe log something to the application here if applicable
8002             return true;
8003         }
8004         // any non-true return value (including none) will cause the error to be thrown
8005     }
8006
8007          * @param {Ext.Error} err The Ext.Error object being raised. It will contain any attributes
8008          * that were originally raised with it, plus properties about the method and class from which
8009          * the error originated (if raised from a class that uses the Ext 4 class system).
8010          * @static
8011          * @markdown
8012          */
8013         handle: function(){
8014             return Ext.Error.ignore;
8015         }
8016     },
8017
8018     /**
8019      * @constructor
8020      * @param {String/Object} config The error message string, or an object containing the 
8021      * attribute "msg" that will be used as the error message. Any other data included in
8022      * the object will be applied to the error instance and logged to the browser console, if available.
8023      */
8024     constructor: function(config){
8025         if (Ext.isString(config)) {
8026             config = { msg: config };
8027         }
8028         Ext.apply(this, config);
8029     },
8030
8031     /**
8032 Provides a custom string representation of the error object. This is an override of the base JavaScript 
8033 `Object.toString` method, which is useful so that when logged to the browser console, an error object will 
8034 be displayed with a useful message instead of `[object Object]`, the default `toString` result.
8035
8036 The default implementation will include the error message along with the raising class and method, if available,
8037 but this can be overridden with a custom implementation either at the prototype level (for all errors) or on
8038 a particular error instance, if you want to provide a custom description that will show up in the console.
8039      * @markdown
8040      * @return {String} The error message. If raised from within the Ext 4 class system, the error message
8041      * will also include the raising class and method names, if available.
8042      */
8043     toString: function(){
8044         var me = this,
8045             className = me.className ? me.className  : '',
8046             methodName = me.methodName ? '.' + me.methodName + '(): ' : '',
8047             msg = me.msg || '(No description provided)';
8048
8049         return className + methodName + msg;
8050     }
8051 });
8052