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