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