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