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