Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / builds / ext-core-debug.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      * @property {String[]}
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
178                 // We create a new temporary class
179                 var F = function() {},
180                     subclassProto, superclassProto = superclass.prototype;
181
182                 F.prototype = superclassProto;
183                 subclassProto = subclass.prototype = new F();
184                 subclassProto.constructor = subclass;
185                 subclass.superclass = superclassProto;
186
187                 if (superclassProto.constructor === objectConstructor) {
188                     superclassProto.constructor = superclass;
189                 }
190
191                 subclass.override = function(overrides) {
192                     Ext.override(subclass, overrides);
193                 };
194
195                 subclassProto.override = inlineOverrides;
196                 subclassProto.proto = subclassProto;
197
198                 subclass.override(overrides);
199                 subclass.extend = function(o) {
200                     return Ext.extend(subclass, o);
201                 };
202
203                 return subclass;
204             };
205         }(),
206
207         /**
208          * Proxy to {@link Ext.Base#override}. Please refer {@link Ext.Base#override} for further details.
209
210     Ext.define('My.cool.Class', {
211         sayHi: function() {
212             alert('Hi!');
213         }
214     }
215
216     Ext.override(My.cool.Class, {
217         sayHi: function() {
218             alert('About to say...');
219
220             this.callOverridden();
221         }
222     });
223
224     var cool = new My.cool.Class();
225     cool.sayHi(); // alerts 'About to say...'
226                   // alerts 'Hi!'
227
228          * Please note that `this.callOverridden()` only works if the class was previously
229          * created with {@link Ext#define)
230          *
231          * @param {Object} cls The class to override
232          * @param {Object} overrides The list of functions to add to origClass. This should be specified as an object literal
233          * containing one or more methods.
234          * @method override
235          * @markdown
236          */
237         override: function(cls, overrides) {
238             if (cls.prototype.$className) {
239                 return cls.override(overrides);
240             }
241             else {
242                 Ext.apply(cls.prototype, overrides);
243             }
244         }
245     });
246
247     // A full set of static methods to do type checking
248     Ext.apply(Ext, {
249
250         /**
251          * Returns the given value itself if it's not empty, as described in {@link Ext#isEmpty}; returns the default
252          * value (second argument) otherwise.
253          *
254          * @param {Object} value The value to test
255          * @param {Object} defaultValue The value to return if the original value is empty
256          * @param {Boolean} allowBlank (optional) true to allow zero length strings to qualify as non-empty (defaults to false)
257          * @return {Object} value, if non-empty, else defaultValue
258          */
259         valueFrom: function(value, defaultValue, allowBlank){
260             return Ext.isEmpty(value, allowBlank) ? defaultValue : value;
261         },
262
263         /**
264          * Returns the type of the given variable in string format. List of possible values are:
265          *
266          * - `undefined`: If the given value is `undefined`
267          * - `null`: If the given value is `null`
268          * - `string`: If the given value is a string
269          * - `number`: If the given value is a number
270          * - `boolean`: If the given value is a boolean value
271          * - `date`: If the given value is a `Date` object
272          * - `function`: If the given value is a function reference
273          * - `object`: If the given value is an object
274          * - `array`: If the given value is an array
275          * - `regexp`: If the given value is a regular expression
276          * - `element`: If the given value is a DOM Element
277          * - `textnode`: If the given value is a DOM text node and contains something other than whitespace
278          * - `whitespace`: If the given value is a DOM text node and contains only whitespace
279          *
280          * @param {Object} value
281          * @return {String}
282          * @markdown
283          */
284         typeOf: function(value) {
285             if (value === null) {
286                 return 'null';
287             }
288
289             var type = typeof value;
290
291             if (type === 'undefined' || type === 'string' || type === 'number' || type === 'boolean') {
292                 return type;
293             }
294
295             var typeToString = toString.call(value);
296
297             switch(typeToString) {
298                 case '[object Array]':
299                     return 'array';
300                 case '[object Date]':
301                     return 'date';
302                 case '[object Boolean]':
303                     return 'boolean';
304                 case '[object Number]':
305                     return 'number';
306                 case '[object RegExp]':
307                     return 'regexp';
308             }
309
310             if (type === 'function') {
311                 return 'function';
312             }
313
314             if (type === 'object') {
315                 if (value.nodeType !== undefined) {
316                     if (value.nodeType === 3) {
317                         return (/\S/).test(value.nodeValue) ? 'textnode' : 'whitespace';
318                     }
319                     else {
320                         return 'element';
321                     }
322                 }
323
324                 return 'object';
325             }
326
327         },
328
329         /**
330          * Returns true if the passed value is empty, false otherwise. The value is deemed to be empty if it is either:
331          *
332          * - `null`
333          * - `undefined`
334          * - a zero-length array
335          * - a zero-length string (Unless the `allowEmptyString` parameter is set to `true`)
336          *
337          * @param {Object} value The value to test
338          * @param {Boolean} allowEmptyString (optional) true to allow empty strings (defaults to false)
339          * @return {Boolean}
340          * @markdown
341          */
342         isEmpty: function(value, allowEmptyString) {
343             return (value === null) || (value === undefined) || (!allowEmptyString ? value === '' : false) || (Ext.isArray(value) && value.length === 0);
344         },
345
346         /**
347          * Returns true if the passed value is a JavaScript Array, false otherwise.
348          *
349          * @param {Object} target The target to test
350          * @return {Boolean}
351          * @method
352          */
353         isArray: ('isArray' in Array) ? Array.isArray : function(value) {
354             return toString.call(value) === '[object Array]';
355         },
356
357         /**
358          * Returns true if the passed value is a JavaScript Date object, false otherwise.
359          * @param {Object} object The object to test
360          * @return {Boolean}
361          */
362         isDate: function(value) {
363             return toString.call(value) === '[object Date]';
364         },
365
366         /**
367          * Returns true if the passed value is a JavaScript Object, false otherwise.
368          * @param {Object} value The value to test
369          * @return {Boolean}
370          * @method
371          */
372         isObject: (toString.call(null) === '[object Object]') ?
373         function(value) {
374             // check ownerDocument here as well to exclude DOM nodes
375             return value !== null && value !== undefined && toString.call(value) === '[object Object]' && value.ownerDocument === undefined;
376         } :
377         function(value) {
378             return toString.call(value) === '[object Object]';
379         },
380
381         /**
382          * Returns true if the passed value is a JavaScript 'primitive', a string, number or boolean.
383          * @param {Object} value The value to test
384          * @return {Boolean}
385          */
386         isPrimitive: function(value) {
387             var type = typeof value;
388
389             return type === 'string' || type === 'number' || type === 'boolean';
390         },
391
392         /**
393          * Returns true if the passed value is a JavaScript Function, false otherwise.
394          * @param {Object} value The value to test
395          * @return {Boolean}
396          * @method
397          */
398         isFunction:
399         // Safari 3.x and 4.x returns 'function' for typeof <NodeList>, hence we need to fall back to using
400         // Object.prorotype.toString (slower)
401         (typeof document !== 'undefined' && typeof document.getElementsByTagName('body') === 'function') ? function(value) {
402             return toString.call(value) === '[object Function]';
403         } : function(value) {
404             return typeof value === 'function';
405         },
406
407         /**
408          * Returns true if the passed value is a number. Returns false for non-finite numbers.
409          * @param {Object} value The value to test
410          * @return {Boolean}
411          */
412         isNumber: function(value) {
413             return typeof value === 'number' && isFinite(value);
414         },
415
416         /**
417          * Validates that a value is numeric.
418          * @param {Object} value Examples: 1, '1', '2.34'
419          * @return {Boolean} True if numeric, false otherwise
420          */
421         isNumeric: function(value) {
422             return !isNaN(parseFloat(value)) && isFinite(value);
423         },
424
425         /**
426          * Returns true if the passed value is a string.
427          * @param {Object} value The value to test
428          * @return {Boolean}
429          */
430         isString: function(value) {
431             return typeof value === 'string';
432         },
433
434         /**
435          * Returns true if the passed value is a boolean.
436          *
437          * @param {Object} value The value to test
438          * @return {Boolean}
439          */
440         isBoolean: function(value) {
441             return typeof value === 'boolean';
442         },
443
444         /**
445          * Returns true if the passed value is an HTMLElement
446          * @param {Object} value The value to test
447          * @return {Boolean}
448          */
449         isElement: function(value) {
450             return value ? value.nodeType === 1 : false;
451         },
452
453         /**
454          * Returns true if the passed value is a TextNode
455          * @param {Object} value The value to test
456          * @return {Boolean}
457          */
458         isTextNode: function(value) {
459             return value ? value.nodeName === "#text" : false;
460         },
461
462         /**
463          * Returns true if the passed value is defined.
464          * @param {Object} value The value to test
465          * @return {Boolean}
466          */
467         isDefined: function(value) {
468             return typeof value !== 'undefined';
469         },
470
471         /**
472          * Returns true if the passed value is iterable, false otherwise
473          * @param {Object} value The value to test
474          * @return {Boolean}
475          */
476         isIterable: function(value) {
477             return (value && typeof value !== 'string') ? value.length !== undefined : false;
478         }
479     });
480
481     Ext.apply(Ext, {
482
483         /**
484          * Clone almost any type of variable including array, object, DOM nodes and Date without keeping the old reference
485          * @param {Object} item The variable to clone
486          * @return {Object} clone
487          */
488         clone: function(item) {
489             if (item === null || item === undefined) {
490                 return item;
491             }
492
493             // DOM nodes
494             // TODO proxy this to Ext.Element.clone to handle automatic id attribute changing
495             // recursively
496             if (item.nodeType && item.cloneNode) {
497                 return item.cloneNode(true);
498             }
499
500             var type = toString.call(item);
501
502             // Date
503             if (type === '[object Date]') {
504                 return new Date(item.getTime());
505             }
506
507             var i, j, k, clone, key;
508
509             // Array
510             if (type === '[object Array]') {
511                 i = item.length;
512
513                 clone = [];
514
515                 while (i--) {
516                     clone[i] = Ext.clone(item[i]);
517                 }
518             }
519             // Object
520             else if (type === '[object Object]' && item.constructor === Object) {
521                 clone = {};
522
523                 for (key in item) {
524                     clone[key] = Ext.clone(item[key]);
525                 }
526
527                 if (enumerables) {
528                     for (j = enumerables.length; j--;) {
529                         k = enumerables[j];
530                         clone[k] = item[k];
531                     }
532                 }
533             }
534
535             return clone || item;
536         },
537
538         /**
539          * @private
540          * Generate a unique reference of Ext in the global scope, useful for sandboxing
541          */
542         getUniqueGlobalNamespace: function() {
543             var uniqueGlobalNamespace = this.uniqueGlobalNamespace;
544
545             if (uniqueGlobalNamespace === undefined) {
546                 var i = 0;
547
548                 do {
549                     uniqueGlobalNamespace = 'ExtBox' + (++i);
550                 } while (Ext.global[uniqueGlobalNamespace] !== undefined);
551
552                 Ext.global[uniqueGlobalNamespace] = Ext;
553                 this.uniqueGlobalNamespace = uniqueGlobalNamespace;
554             }
555
556             return uniqueGlobalNamespace;
557         },
558
559         /**
560          * @private
561          */
562         functionFactory: function() {
563             var args = Array.prototype.slice.call(arguments);
564
565             if (args.length > 0) {
566                 args[args.length - 1] = 'var Ext=window.' + this.getUniqueGlobalNamespace() + ';' +
567                     args[args.length - 1];
568             }
569
570             return Function.prototype.constructor.apply(Function.prototype, args);
571         }
572     });
573
574     /**
575      * Old alias to {@link Ext#typeOf}
576      * @deprecated 4.0.0 Use {@link Ext#typeOf} instead
577      * @method
578      * @alias Ext#typeOf
579      */
580     Ext.type = Ext.typeOf;
581
582 })();
583
584 /**
585  * @author Jacky Nguyen <jacky@sencha.com>
586  * @docauthor Jacky Nguyen <jacky@sencha.com>
587  * @class Ext.Version
588  *
589  * A utility class that wrap around a string version number and provide convenient
590  * method to perform comparison. See also: {@link Ext.Version#compare compare}. Example:
591
592     var version = new Ext.Version('1.0.2beta');
593     console.log("Version is " + version); // Version is 1.0.2beta
594
595     console.log(version.getMajor()); // 1
596     console.log(version.getMinor()); // 0
597     console.log(version.getPatch()); // 2
598     console.log(version.getBuild()); // 0
599     console.log(version.getRelease()); // beta
600
601     console.log(version.isGreaterThan('1.0.1')); // True
602     console.log(version.isGreaterThan('1.0.2alpha')); // True
603     console.log(version.isGreaterThan('1.0.2RC')); // False
604     console.log(version.isGreaterThan('1.0.2')); // False
605     console.log(version.isLessThan('1.0.2')); // True
606
607     console.log(version.match(1.0)); // True
608     console.log(version.match('1.0.2')); // True
609
610  * @markdown
611  */
612 (function() {
613
614 // Current core version
615 var version = '4.0.7', Version;
616     Ext.Version = Version = Ext.extend(Object, {
617
618         /**
619          * @param {String/Number} version The version number in the follow standard format: major[.minor[.patch[.build[release]]]]
620          * Examples: 1.0 or 1.2.3beta or 1.2.3.4RC
621          * @return {Ext.Version} this
622          */
623         constructor: function(version) {
624             var parts, releaseStartIndex;
625
626             if (version instanceof Version) {
627                 return version;
628             }
629
630             this.version = this.shortVersion = String(version).toLowerCase().replace(/_/g, '.').replace(/[\-+]/g, '');
631
632             releaseStartIndex = this.version.search(/([^\d\.])/);
633
634             if (releaseStartIndex !== -1) {
635                 this.release = this.version.substr(releaseStartIndex, version.length);
636                 this.shortVersion = this.version.substr(0, releaseStartIndex);
637             }
638
639             this.shortVersion = this.shortVersion.replace(/[^\d]/g, '');
640
641             parts = this.version.split('.');
642
643             this.major = parseInt(parts.shift() || 0, 10);
644             this.minor = parseInt(parts.shift() || 0, 10);
645             this.patch = parseInt(parts.shift() || 0, 10);
646             this.build = parseInt(parts.shift() || 0, 10);
647
648             return this;
649         },
650
651         /**
652          * Override the native toString method
653          * @private
654          * @return {String} version
655          */
656         toString: function() {
657             return this.version;
658         },
659
660         /**
661          * Override the native valueOf method
662          * @private
663          * @return {String} version
664          */
665         valueOf: function() {
666             return this.version;
667         },
668
669         /**
670          * Returns the major component value
671          * @return {Number} major
672          */
673         getMajor: function() {
674             return this.major || 0;
675         },
676
677         /**
678          * Returns the minor component value
679          * @return {Number} minor
680          */
681         getMinor: function() {
682             return this.minor || 0;
683         },
684
685         /**
686          * Returns the patch component value
687          * @return {Number} patch
688          */
689         getPatch: function() {
690             return this.patch || 0;
691         },
692
693         /**
694          * Returns the build component value
695          * @return {Number} build
696          */
697         getBuild: function() {
698             return this.build || 0;
699         },
700
701         /**
702          * Returns the release component value
703          * @return {Number} release
704          */
705         getRelease: function() {
706             return this.release || '';
707         },
708
709         /**
710          * Returns whether this version if greater than the supplied argument
711          * @param {String/Number} target The version to compare with
712          * @return {Boolean} True if this version if greater than the target, false otherwise
713          */
714         isGreaterThan: function(target) {
715             return Version.compare(this.version, target) === 1;
716         },
717
718         /**
719          * Returns whether this version if smaller than the supplied argument
720          * @param {String/Number} target The version to compare with
721          * @return {Boolean} True if this version if smaller than the target, false otherwise
722          */
723         isLessThan: function(target) {
724             return Version.compare(this.version, target) === -1;
725         },
726
727         /**
728          * Returns whether this version equals to the supplied argument
729          * @param {String/Number} target The version to compare with
730          * @return {Boolean} True if this version equals to the target, false otherwise
731          */
732         equals: function(target) {
733             return Version.compare(this.version, target) === 0;
734         },
735
736         /**
737          * Returns whether this version matches the supplied argument. Example:
738          * <pre><code>
739          * var version = new Ext.Version('1.0.2beta');
740          * console.log(version.match(1)); // True
741          * console.log(version.match(1.0)); // True
742          * console.log(version.match('1.0.2')); // True
743          * console.log(version.match('1.0.2RC')); // False
744          * </code></pre>
745          * @param {String/Number} target The version to compare with
746          * @return {Boolean} True if this version matches the target, false otherwise
747          */
748         match: function(target) {
749             target = String(target);
750             return this.version.substr(0, target.length) === target;
751         },
752
753         /**
754          * Returns this format: [major, minor, patch, build, release]. Useful for comparison
755          * @return {Number[]}
756          */
757         toArray: function() {
758             return [this.getMajor(), this.getMinor(), this.getPatch(), this.getBuild(), this.getRelease()];
759         },
760
761         /**
762          * Returns shortVersion version without dots and release
763          * @return {String}
764          */
765         getShortVersion: function() {
766             return this.shortVersion;
767         }
768     });
769
770     Ext.apply(Version, {
771         // @private
772         releaseValueMap: {
773             'dev': -6,
774             'alpha': -5,
775             'a': -5,
776             'beta': -4,
777             'b': -4,
778             'rc': -3,
779             '#': -2,
780             'p': -1,
781             'pl': -1
782         },
783
784         /**
785          * Converts a version component to a comparable value
786          *
787          * @static
788          * @param {Object} value The value to convert
789          * @return {Object}
790          */
791         getComponentValue: function(value) {
792             return !value ? 0 : (isNaN(value) ? this.releaseValueMap[value] || value : parseInt(value, 10));
793         },
794
795         /**
796          * Compare 2 specified versions, starting from left to right. If a part contains special version strings,
797          * they are handled in the following order:
798          * 'dev' < 'alpha' = 'a' < 'beta' = 'b' < 'RC' = 'rc' < '#' < 'pl' = 'p' < 'anything else'
799          *
800          * @static
801          * @param {String} current The current version to compare to
802          * @param {String} target The target version to compare to
803          * @return {Number} Returns -1 if the current version is smaller than the target version, 1 if greater, and 0 if they're equivalent
804          */
805         compare: function(current, target) {
806             var currentValue, targetValue, i;
807
808             current = new Version(current).toArray();
809             target = new Version(target).toArray();
810
811             for (i = 0; i < Math.max(current.length, target.length); i++) {
812                 currentValue = this.getComponentValue(current[i]);
813                 targetValue = this.getComponentValue(target[i]);
814
815                 if (currentValue < targetValue) {
816                     return -1;
817                 } else if (currentValue > targetValue) {
818                     return 1;
819                 }
820             }
821
822             return 0;
823         }
824     });
825
826     Ext.apply(Ext, {
827         /**
828          * @private
829          */
830         versions: {},
831
832         /**
833          * @private
834          */
835         lastRegisteredVersion: null,
836
837         /**
838          * Set version number for the given package name.
839          *
840          * @param {String} packageName The package name, for example: 'core', 'touch', 'extjs'
841          * @param {String/Ext.Version} version The version, for example: '1.2.3alpha', '2.4.0-dev'
842          * @return {Ext}
843          */
844         setVersion: function(packageName, version) {
845             Ext.versions[packageName] = new Version(version);
846             Ext.lastRegisteredVersion = Ext.versions[packageName];
847
848             return this;
849         },
850
851         /**
852          * Get the version number of the supplied package name; will return the last registered version
853          * (last Ext.setVersion call) if there's no package name given.
854          *
855          * @param {String} packageName (Optional) The package name, for example: 'core', 'touch', 'extjs'
856          * @return {Ext.Version} The version
857          */
858         getVersion: function(packageName) {
859             if (packageName === undefined) {
860                 return Ext.lastRegisteredVersion;
861             }
862
863             return Ext.versions[packageName];
864         },
865
866         /**
867          * Create a closure for deprecated code.
868          *
869     // This means Ext.oldMethod is only supported in 4.0.0beta and older.
870     // If Ext.getVersion('extjs') returns a version that is later than '4.0.0beta', for example '4.0.0RC',
871     // the closure will not be invoked
872     Ext.deprecate('extjs', '4.0.0beta', function() {
873         Ext.oldMethod = Ext.newMethod;
874
875         ...
876     });
877
878          * @param {String} packageName The package name
879          * @param {String} since The last version before it's deprecated
880          * @param {Function} closure The callback function to be executed with the specified version is less than the current version
881          * @param {Object} scope The execution scope (<tt>this</tt>) if the closure
882          * @markdown
883          */
884         deprecate: function(packageName, since, closure, scope) {
885             if (Version.compare(Ext.getVersion(packageName), since) < 1) {
886                 closure.call(scope);
887             }
888         }
889     }); // End Versioning
890
891     Ext.setVersion('core', version);
892
893 })();
894
895 /**
896  * @class Ext.String
897  *
898  * A collection of useful static methods to deal with strings
899  * @singleton
900  */
901
902 Ext.String = {
903     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,
904     escapeRe: /('|\\)/g,
905     formatRe: /\{(\d+)\}/g,
906     escapeRegexRe: /([-.*+?^${}()|[\]\/\\])/g,
907
908     /**
909      * Convert certain characters (&, <, >, and ") to their HTML character equivalents for literal display in web pages.
910      * @param {String} value The string to encode
911      * @return {String} The encoded text
912      * @method
913      */
914     htmlEncode: (function() {
915         var entities = {
916             '&': '&amp;',
917             '>': '&gt;',
918             '<': '&lt;',
919             '"': '&quot;'
920         }, keys = [], p, regex;
921         
922         for (p in entities) {
923             keys.push(p);
924         }
925         
926         regex = new RegExp('(' + keys.join('|') + ')', 'g');
927         
928         return function(value) {
929             return (!value) ? value : String(value).replace(regex, function(match, capture) {
930                 return entities[capture];    
931             });
932         };
933     })(),
934
935     /**
936      * Convert certain characters (&, <, >, and ") from their HTML character equivalents.
937      * @param {String} value The string to decode
938      * @return {String} The decoded text
939      * @method
940      */
941     htmlDecode: (function() {
942         var entities = {
943             '&amp;': '&',
944             '&gt;': '>',
945             '&lt;': '<',
946             '&quot;': '"'
947         }, keys = [], p, regex;
948         
949         for (p in entities) {
950             keys.push(p);
951         }
952         
953         regex = new RegExp('(' + keys.join('|') + '|&#[0-9]{1,5};' + ')', 'g');
954         
955         return function(value) {
956             return (!value) ? value : String(value).replace(regex, function(match, capture) {
957                 if (capture in entities) {
958                     return entities[capture];
959                 } else {
960                     return String.fromCharCode(parseInt(capture.substr(2), 10));
961                 }
962             });
963         };
964     })(),
965
966     /**
967      * Appends content to the query string of a URL, handling logic for whether to place
968      * a question mark or ampersand.
969      * @param {String} url The URL to append to.
970      * @param {String} string The content to append to the URL.
971      * @return (String) The resulting URL
972      */
973     urlAppend : function(url, string) {
974         if (!Ext.isEmpty(string)) {
975             return url + (url.indexOf('?') === -1 ? '?' : '&') + string;
976         }
977
978         return url;
979     },
980
981     /**
982      * Trims whitespace from either end of a string, leaving spaces within the string intact.  Example:
983      * @example
984 var s = '  foo bar  ';
985 alert('-' + s + '-');         //alerts "- foo bar -"
986 alert('-' + Ext.String.trim(s) + '-');  //alerts "-foo bar-"
987
988      * @param {String} string The string to escape
989      * @return {String} The trimmed string
990      */
991     trim: function(string) {
992         return string.replace(Ext.String.trimRegex, "");
993     },
994
995     /**
996      * Capitalize the given string
997      * @param {String} string
998      * @return {String}
999      */
1000     capitalize: function(string) {
1001         return string.charAt(0).toUpperCase() + string.substr(1);
1002     },
1003
1004     /**
1005      * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
1006      * @param {String} value The string to truncate
1007      * @param {Number} length The maximum length to allow before truncating
1008      * @param {Boolean} word True to try to find a common word break
1009      * @return {String} The converted text
1010      */
1011     ellipsis: function(value, len, word) {
1012         if (value && value.length > len) {
1013             if (word) {
1014                 var vs = value.substr(0, len - 2),
1015                 index = Math.max(vs.lastIndexOf(' '), vs.lastIndexOf('.'), vs.lastIndexOf('!'), vs.lastIndexOf('?'));
1016                 if (index !== -1 && index >= (len - 15)) {
1017                     return vs.substr(0, index) + "...";
1018                 }
1019             }
1020             return value.substr(0, len - 3) + "...";
1021         }
1022         return value;
1023     },
1024
1025     /**
1026      * Escapes the passed string for use in a regular expression
1027      * @param {String} string
1028      * @return {String}
1029      */
1030     escapeRegex: function(string) {
1031         return string.replace(Ext.String.escapeRegexRe, "\\$1");
1032     },
1033
1034     /**
1035      * Escapes the passed string for ' and \
1036      * @param {String} string The string to escape
1037      * @return {String} The escaped string
1038      */
1039     escape: function(string) {
1040         return string.replace(Ext.String.escapeRe, "\\$1");
1041     },
1042
1043     /**
1044      * Utility function that allows you to easily switch a string between two alternating values.  The passed value
1045      * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
1046      * they are already different, the first value passed in is returned.  Note that this method returns the new value
1047      * but does not change the current string.
1048      * <pre><code>
1049     // alternate sort directions
1050     sort = Ext.String.toggle(sort, 'ASC', 'DESC');
1051
1052     // instead of conditional logic:
1053     sort = (sort == 'ASC' ? 'DESC' : 'ASC');
1054        </code></pre>
1055      * @param {String} string The current string
1056      * @param {String} value The value to compare to the current string
1057      * @param {String} other The new value to use if the string already equals the first value passed in
1058      * @return {String} The new value
1059      */
1060     toggle: function(string, value, other) {
1061         return string === value ? other : value;
1062     },
1063
1064     /**
1065      * Pads the left side of a string with a specified character.  This is especially useful
1066      * for normalizing number and date strings.  Example usage:
1067      *
1068      * <pre><code>
1069 var s = Ext.String.leftPad('123', 5, '0');
1070 // s now contains the string: '00123'
1071        </code></pre>
1072      * @param {String} string The original string
1073      * @param {Number} size The total length of the output string
1074      * @param {String} character (optional) The character with which to pad the original string (defaults to empty string " ")
1075      * @return {String} The padded string
1076      */
1077     leftPad: function(string, size, character) {
1078         var result = String(string);
1079         character = character || " ";
1080         while (result.length < size) {
1081             result = character + result;
1082         }
1083         return result;
1084     },
1085
1086     /**
1087      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
1088      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
1089      * <pre><code>
1090 var cls = 'my-class', text = 'Some text';
1091 var s = Ext.String.format('&lt;div class="{0}">{1}&lt;/div>', cls, text);
1092 // s now contains the string: '&lt;div class="my-class">Some text&lt;/div>'
1093        </code></pre>
1094      * @param {String} string The tokenized string to be formatted
1095      * @param {String} value1 The value to replace token {0}
1096      * @param {String} value2 Etc...
1097      * @return {String} The formatted string
1098      */
1099     format: function(format) {
1100         var args = Ext.Array.toArray(arguments, 1);
1101         return format.replace(Ext.String.formatRe, function(m, i) {
1102             return args[i];
1103         });
1104     },
1105
1106     /**
1107      * Returns a string with a specified number of repititions a given string pattern.
1108      * The pattern be separated by a different string.
1109      *
1110      *      var s = Ext.String.repeat('---', 4); // = '------------'
1111      *      var t = Ext.String.repeat('--', 3, '/'); // = '--/--/--'
1112      *
1113      * @param {String} pattern The pattern to repeat.
1114      * @param {Number} count The number of times to repeat the pattern (may be 0).
1115      * @param {String} sep An option string to separate each pattern.
1116      */
1117     repeat: function(pattern, count, sep) {
1118         for (var buf = [], i = count; i--; ) {
1119             buf.push(pattern);
1120         }
1121         return buf.join(sep || '');
1122     }
1123 };
1124
1125 /**
1126  * @class Ext.Number
1127  *
1128  * A collection of useful static methods to deal with numbers
1129  * @singleton
1130  */
1131
1132 (function() {
1133
1134 var isToFixedBroken = (0.9).toFixed() !== '1';
1135
1136 Ext.Number = {
1137     /**
1138      * Checks whether or not the passed number is within a desired range.  If the number is already within the
1139      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
1140      * exceeded. Note that this method returns the constrained value but does not change the current number.
1141      * @param {Number} number The number to check
1142      * @param {Number} min The minimum number in the range
1143      * @param {Number} max The maximum number in the range
1144      * @return {Number} The constrained value if outside the range, otherwise the current value
1145      */
1146     constrain: function(number, min, max) {
1147         number = parseFloat(number);
1148
1149         if (!isNaN(min)) {
1150             number = Math.max(number, min);
1151         }
1152         if (!isNaN(max)) {
1153             number = Math.min(number, max);
1154         }
1155         return number;
1156     },
1157
1158     /**
1159      * Snaps the passed number between stopping points based upon a passed increment value.
1160      * @param {Number} value The unsnapped value.
1161      * @param {Number} increment The increment by which the value must move.
1162      * @param {Number} minValue The minimum value to which the returned value must be constrained. Overrides the increment..
1163      * @param {Number} maxValue The maximum value to which the returned value must be constrained. Overrides the increment..
1164      * @return {Number} The value of the nearest snap target.
1165      */
1166     snap : function(value, increment, minValue, maxValue) {
1167         var newValue = value,
1168             m;
1169
1170         if (!(increment && value)) {
1171             return value;
1172         }
1173         m = value % increment;
1174         if (m !== 0) {
1175             newValue -= m;
1176             if (m * 2 >= increment) {
1177                 newValue += increment;
1178             } else if (m * 2 < -increment) {
1179                 newValue -= increment;
1180             }
1181         }
1182         return Ext.Number.constrain(newValue, minValue,  maxValue);
1183     },
1184
1185     /**
1186      * Formats a number using fixed-point notation
1187      * @param {Number} value The number to format
1188      * @param {Number} precision The number of digits to show after the decimal point
1189      */
1190     toFixed: function(value, precision) {
1191         if (isToFixedBroken) {
1192             precision = precision || 0;
1193             var pow = Math.pow(10, precision);
1194             return (Math.round(value * pow) / pow).toFixed(precision);
1195         }
1196
1197         return value.toFixed(precision);
1198     },
1199
1200     /**
1201      * Validate that a value is numeric and convert it to a number if necessary. Returns the specified default value if
1202      * it is not.
1203
1204 Ext.Number.from('1.23', 1); // returns 1.23
1205 Ext.Number.from('abc', 1); // returns 1
1206
1207      * @param {Object} value
1208      * @param {Number} defaultValue The value to return if the original value is non-numeric
1209      * @return {Number} value, if numeric, defaultValue otherwise
1210      */
1211     from: function(value, defaultValue) {
1212         if (isFinite(value)) {
1213             value = parseFloat(value);
1214         }
1215
1216         return !isNaN(value) ? value : defaultValue;
1217     }
1218 };
1219
1220 })();
1221
1222 /**
1223  * @deprecated 4.0.0 Please use {@link Ext.Number#from} instead.
1224  * @member Ext
1225  * @method num
1226  * @alias Ext.Number#from
1227  */
1228 Ext.num = function() {
1229     return Ext.Number.from.apply(this, arguments);
1230 };
1231 /**
1232  * @class Ext.Array
1233  * @singleton
1234  * @author Jacky Nguyen <jacky@sencha.com>
1235  * @docauthor Jacky Nguyen <jacky@sencha.com>
1236  *
1237  * A set of useful static methods to deal with arrays; provide missing methods for older browsers.
1238  */
1239 (function() {
1240
1241     var arrayPrototype = Array.prototype,
1242         slice = arrayPrototype.slice,
1243         supportsSplice = function () {
1244             var array = [],
1245                 lengthBefore,
1246                 j = 20;
1247
1248             if (!array.splice) {
1249                 return false;
1250             }
1251
1252             // This detects a bug in IE8 splice method:
1253             // see http://social.msdn.microsoft.com/Forums/en-US/iewebdevelopment/thread/6e946d03-e09f-4b22-a4dd-cd5e276bf05a/
1254
1255             while (j--) {
1256                 array.push("A");
1257             }
1258
1259             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");
1260
1261             lengthBefore = array.length; //41
1262             array.splice(13, 0, "XXX"); // add one element
1263
1264             if (lengthBefore+1 != array.length) {
1265                 return false;
1266             }
1267             // end IE8 bug
1268
1269             return true;
1270         }(),
1271         supportsForEach = 'forEach' in arrayPrototype,
1272         supportsMap = 'map' in arrayPrototype,
1273         supportsIndexOf = 'indexOf' in arrayPrototype,
1274         supportsEvery = 'every' in arrayPrototype,
1275         supportsSome = 'some' in arrayPrototype,
1276         supportsFilter = 'filter' in arrayPrototype,
1277         supportsSort = function() {
1278             var a = [1,2,3,4,5].sort(function(){ return 0; });
1279             return a[0] === 1 && a[1] === 2 && a[2] === 3 && a[3] === 4 && a[4] === 5;
1280         }(),
1281         supportsSliceOnNodeList = true,
1282         ExtArray;
1283
1284     try {
1285         // IE 6 - 8 will throw an error when using Array.prototype.slice on NodeList
1286         if (typeof document !== 'undefined') {
1287             slice.call(document.getElementsByTagName('body'));
1288         }
1289     } catch (e) {
1290         supportsSliceOnNodeList = false;
1291     }
1292
1293     function fixArrayIndex (array, index) {
1294         return (index < 0) ? Math.max(0, array.length + index)
1295                            : Math.min(array.length, index);
1296     }
1297
1298     /*
1299     Does the same work as splice, but with a slightly more convenient signature. The splice
1300     method has bugs in IE8, so this is the implementation we use on that platform.
1301
1302     The rippling of items in the array can be tricky. Consider two use cases:
1303
1304                   index=2
1305                   removeCount=2
1306                  /=====\
1307         +---+---+---+---+---+---+---+---+
1308         | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
1309         +---+---+---+---+---+---+---+---+
1310                          /  \/  \/  \/  \
1311                         /   /\  /\  /\   \
1312                        /   /  \/  \/  \   +--------------------------+
1313                       /   /   /\  /\   +--------------------------+   \
1314                      /   /   /  \/  +--------------------------+   \   \
1315                     /   /   /   /+--------------------------+   \   \   \
1316                    /   /   /   /                             \   \   \   \
1317                   v   v   v   v                               v   v   v   v
1318         +---+---+---+---+---+---+       +---+---+---+---+---+---+---+---+---+
1319         | 0 | 1 | 4 | 5 | 6 | 7 |       | 0 | 1 | a | b | c | 4 | 5 | 6 | 7 |
1320         +---+---+---+---+---+---+       +---+---+---+---+---+---+---+---+---+
1321         A                               B        \=========/
1322                                                  insert=[a,b,c]
1323
1324     In case A, it is obvious that copying of [4,5,6,7] must be left-to-right so
1325     that we don't end up with [0,1,6,7,6,7]. In case B, we have the opposite; we
1326     must go right-to-left or else we would end up with [0,1,a,b,c,4,4,4,4].
1327     */
1328     function replaceSim (array, index, removeCount, insert) {
1329         var add = insert ? insert.length : 0,
1330             length = array.length,
1331             pos = fixArrayIndex(array, index);
1332
1333         // we try to use Array.push when we can for efficiency...
1334         if (pos === length) {
1335             if (add) {
1336                 array.push.apply(array, insert);
1337             }
1338         } else {
1339             var remove = Math.min(removeCount, length - pos),
1340                 tailOldPos = pos + remove,
1341                 tailNewPos = tailOldPos + add - remove,
1342                 tailCount = length - tailOldPos,
1343                 lengthAfterRemove = length - remove,
1344                 i;
1345
1346             if (tailNewPos < tailOldPos) { // case A
1347                 for (i = 0; i < tailCount; ++i) {
1348                     array[tailNewPos+i] = array[tailOldPos+i];
1349                 }
1350             } else if (tailNewPos > tailOldPos) { // case B
1351                 for (i = tailCount; i--; ) {
1352                     array[tailNewPos+i] = array[tailOldPos+i];
1353                 }
1354             } // else, add == remove (nothing to do)
1355
1356             if (add && pos === lengthAfterRemove) {
1357                 array.length = lengthAfterRemove; // truncate array
1358                 array.push.apply(array, insert);
1359             } else {
1360                 array.length = lengthAfterRemove + add; // reserves space
1361                 for (i = 0; i < add; ++i) {
1362                     array[pos+i] = insert[i];
1363                 }
1364             }
1365         }
1366
1367         return array;
1368     }
1369
1370     function replaceNative (array, index, removeCount, insert) {
1371         if (insert && insert.length) {
1372             if (index < array.length) {
1373                 array.splice.apply(array, [index, removeCount].concat(insert));
1374             } else {
1375                 array.push.apply(array, insert);
1376             }
1377         } else {
1378             array.splice(index, removeCount);
1379         }
1380         return array;
1381     }
1382
1383     function eraseSim (array, index, removeCount) {
1384         return replaceSim(array, index, removeCount);
1385     }
1386
1387     function eraseNative (array, index, removeCount) {
1388         array.splice(index, removeCount);
1389         return array;
1390     }
1391
1392     function spliceSim (array, index, removeCount) {
1393         var pos = fixArrayIndex(array, index),
1394             removed = array.slice(index, fixArrayIndex(array, pos+removeCount));
1395
1396         if (arguments.length < 4) {
1397             replaceSim(array, pos, removeCount);
1398         } else {
1399             replaceSim(array, pos, removeCount, slice.call(arguments, 3));
1400         }
1401
1402         return removed;
1403     }
1404
1405     function spliceNative (array) {
1406         return array.splice.apply(array, slice.call(arguments, 1));
1407     }
1408
1409     var erase = supportsSplice ? eraseNative : eraseSim,
1410         replace = supportsSplice ? replaceNative : replaceSim,
1411         splice = supportsSplice ? spliceNative : spliceSim;
1412
1413     // NOTE: from here on, use erase, replace or splice (not native methods)...
1414
1415     ExtArray = Ext.Array = {
1416         /**
1417          * Iterates an array or an iterable value and invoke the given callback function for each item.
1418          *
1419          *     var countries = ['Vietnam', 'Singapore', 'United States', 'Russia'];
1420          *
1421          *     Ext.Array.each(countries, function(name, index, countriesItSelf) {
1422          *         console.log(name);
1423          *     });
1424          *
1425          *     var sum = function() {
1426          *         var sum = 0;
1427          *
1428          *         Ext.Array.each(arguments, function(value) {
1429          *             sum += value;
1430          *         });
1431          *
1432          *         return sum;
1433          *     };
1434          *
1435          *     sum(1, 2, 3); // returns 6
1436          *
1437          * The iteration can be stopped by returning false in the function callback.
1438          *
1439          *     Ext.Array.each(countries, function(name, index, countriesItSelf) {
1440          *         if (name === 'Singapore') {
1441          *             return false; // break here
1442          *         }
1443          *     });
1444          *
1445          * {@link Ext#each Ext.each} is alias for {@link Ext.Array#each Ext.Array.each}
1446          *
1447          * @param {Array/NodeList/Object} iterable The value to be iterated. If this
1448          * argument is not iterable, the callback function is called once.
1449          * @param {Function} fn The callback function. If it returns false, the iteration stops and this method returns
1450          * the current `index`.
1451          * @param {Object} fn.item The item at the current `index` in the passed `array`
1452          * @param {Number} fn.index The current `index` within the `array`
1453          * @param {Array} fn.allItems The `array` itself which was passed as the first argument
1454          * @param {Boolean} fn.return Return false to stop iteration.
1455          * @param {Object} scope (Optional) The scope (`this` reference) in which the specified function is executed.
1456          * @param {Boolean} reverse (Optional) Reverse the iteration order (loop from the end to the beginning)
1457          * Defaults false
1458          * @return {Boolean} See description for the `fn` parameter.
1459          */
1460         each: function(array, fn, scope, reverse) {
1461             array = ExtArray.from(array);
1462
1463             var i,
1464                 ln = array.length;
1465
1466             if (reverse !== true) {
1467                 for (i = 0; i < ln; i++) {
1468                     if (fn.call(scope || array[i], array[i], i, array) === false) {
1469                         return i;
1470                     }
1471                 }
1472             }
1473             else {
1474                 for (i = ln - 1; i > -1; i--) {
1475                     if (fn.call(scope || array[i], array[i], i, array) === false) {
1476                         return i;
1477                     }
1478                 }
1479             }
1480
1481             return true;
1482         },
1483
1484         /**
1485          * Iterates an array and invoke the given callback function for each item. Note that this will simply
1486          * delegate to the native Array.prototype.forEach method if supported. It doesn't support stopping the
1487          * iteration by returning false in the callback function like {@link Ext.Array#each}. However, performance
1488          * could be much better in modern browsers comparing with {@link Ext.Array#each}
1489          *
1490          * @param {Array} array The array to iterate
1491          * @param {Function} fn The callback function.
1492          * @param {Object} fn.item The item at the current `index` in the passed `array`
1493          * @param {Number} fn.index The current `index` within the `array`
1494          * @param {Array}  fn.allItems The `array` itself which was passed as the first argument
1495          * @param {Object} scope (Optional) The execution scope (`this`) in which the specified function is executed.
1496          */
1497         forEach: function(array, fn, scope) {
1498             if (supportsForEach) {
1499                 return array.forEach(fn, scope);
1500             }
1501
1502             var i = 0,
1503                 ln = array.length;
1504
1505             for (; i < ln; i++) {
1506                 fn.call(scope, array[i], i, array);
1507             }
1508         },
1509
1510         /**
1511          * Get the index of the provided `item` in the given `array`, a supplement for the
1512          * missing arrayPrototype.indexOf in Internet Explorer.
1513          *
1514          * @param {Array} array The array to check
1515          * @param {Object} item The item to look for
1516          * @param {Number} from (Optional) The index at which to begin the search
1517          * @return {Number} The index of item in the array (or -1 if it is not found)
1518          */
1519         indexOf: function(array, item, from) {
1520             if (supportsIndexOf) {
1521                 return array.indexOf(item, from);
1522             }
1523
1524             var i, length = array.length;
1525
1526             for (i = (from < 0) ? Math.max(0, length + from) : from || 0; i < length; i++) {
1527                 if (array[i] === item) {
1528                     return i;
1529                 }
1530             }
1531
1532             return -1;
1533         },
1534
1535         /**
1536          * Checks whether or not the given `array` contains the specified `item`
1537          *
1538          * @param {Array} array The array to check
1539          * @param {Object} item The item to look for
1540          * @return {Boolean} True if the array contains the item, false otherwise
1541          */
1542         contains: function(array, item) {
1543             if (supportsIndexOf) {
1544                 return array.indexOf(item) !== -1;
1545             }
1546
1547             var i, ln;
1548
1549             for (i = 0, ln = array.length; i < ln; i++) {
1550                 if (array[i] === item) {
1551                     return true;
1552                 }
1553             }
1554
1555             return false;
1556         },
1557
1558         /**
1559          * Converts any iterable (numeric indices and a length property) into a true array.
1560          *
1561          *     function test() {
1562          *         var args = Ext.Array.toArray(arguments),
1563          *             fromSecondToLastArgs = Ext.Array.toArray(arguments, 1);
1564          *
1565          *         alert(args.join(' '));
1566          *         alert(fromSecondToLastArgs.join(' '));
1567          *     }
1568          *
1569          *     test('just', 'testing', 'here'); // alerts 'just testing here';
1570          *                                      // alerts 'testing here';
1571          *
1572          *     Ext.Array.toArray(document.getElementsByTagName('div')); // will convert the NodeList into an array
1573          *     Ext.Array.toArray('splitted'); // returns ['s', 'p', 'l', 'i', 't', 't', 'e', 'd']
1574          *     Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i']
1575          *
1576          * {@link Ext#toArray Ext.toArray} is alias for {@link Ext.Array#toArray Ext.Array.toArray}
1577          *
1578          * @param {Object} iterable the iterable object to be turned into a true Array.
1579          * @param {Number} start (Optional) a zero-based index that specifies the start of extraction. Defaults to 0
1580          * @param {Number} end (Optional) a zero-based index that specifies the end of extraction. Defaults to the last
1581          * index of the iterable value
1582          * @return {Array} array
1583          */
1584         toArray: function(iterable, start, end){
1585             if (!iterable || !iterable.length) {
1586                 return [];
1587             }
1588
1589             if (typeof iterable === 'string') {
1590                 iterable = iterable.split('');
1591             }
1592
1593             if (supportsSliceOnNodeList) {
1594                 return slice.call(iterable, start || 0, end || iterable.length);
1595             }
1596
1597             var array = [],
1598                 i;
1599
1600             start = start || 0;
1601             end = end ? ((end < 0) ? iterable.length + end : end) : iterable.length;
1602
1603             for (i = start; i < end; i++) {
1604                 array.push(iterable[i]);
1605             }
1606
1607             return array;
1608         },
1609
1610         /**
1611          * Plucks the value of a property from each item in the Array. Example:
1612          *
1613          *     Ext.Array.pluck(Ext.query("p"), "className"); // [el1.className, el2.className, ..., elN.className]
1614          *
1615          * @param {Array/NodeList} array The Array of items to pluck the value from.
1616          * @param {String} propertyName The property name to pluck from each element.
1617          * @return {Array} The value from each item in the Array.
1618          */
1619         pluck: function(array, propertyName) {
1620             var ret = [],
1621                 i, ln, item;
1622
1623             for (i = 0, ln = array.length; i < ln; i++) {
1624                 item = array[i];
1625
1626                 ret.push(item[propertyName]);
1627             }
1628
1629             return ret;
1630         },
1631
1632         /**
1633          * Creates a new array with the results of calling a provided function on every element in this array.
1634          *
1635          * @param {Array} array
1636          * @param {Function} fn Callback function for each item
1637          * @param {Object} scope Callback function scope
1638          * @return {Array} results
1639          */
1640         map: function(array, fn, scope) {
1641             if (supportsMap) {
1642                 return array.map(fn, scope);
1643             }
1644
1645             var results = [],
1646                 i = 0,
1647                 len = array.length;
1648
1649             for (; i < len; i++) {
1650                 results[i] = fn.call(scope, array[i], i, array);
1651             }
1652
1653             return results;
1654         },
1655
1656         /**
1657          * Executes the specified function for each array element until the function returns a falsy value.
1658          * If such an item is found, the function will return false immediately.
1659          * Otherwise, it will return true.
1660          *
1661          * @param {Array} array
1662          * @param {Function} fn Callback function for each item
1663          * @param {Object} scope Callback function scope
1664          * @return {Boolean} True if no false value is returned by the callback function.
1665          */
1666         every: function(array, fn, scope) {
1667             if (supportsEvery) {
1668                 return array.every(fn, scope);
1669             }
1670
1671             var i = 0,
1672                 ln = array.length;
1673
1674             for (; i < ln; ++i) {
1675                 if (!fn.call(scope, array[i], i, array)) {
1676                     return false;
1677                 }
1678             }
1679
1680             return true;
1681         },
1682
1683         /**
1684          * Executes the specified function for each array element until the function returns a truthy value.
1685          * If such an item is found, the function will return true immediately. Otherwise, it will return false.
1686          *
1687          * @param {Array} array
1688          * @param {Function} fn Callback function for each item
1689          * @param {Object} scope Callback function scope
1690          * @return {Boolean} True if the callback function returns a truthy value.
1691          */
1692         some: function(array, fn, scope) {
1693             if (supportsSome) {
1694                 return array.some(fn, scope);
1695             }
1696
1697             var i = 0,
1698                 ln = array.length;
1699
1700             for (; i < ln; ++i) {
1701                 if (fn.call(scope, array[i], i, array)) {
1702                     return true;
1703                 }
1704             }
1705
1706             return false;
1707         },
1708
1709         /**
1710          * Filter through an array and remove empty item as defined in {@link Ext#isEmpty Ext.isEmpty}
1711          *
1712          * See {@link Ext.Array#filter}
1713          *
1714          * @param {Array} array
1715          * @return {Array} results
1716          */
1717         clean: function(array) {
1718             var results = [],
1719                 i = 0,
1720                 ln = array.length,
1721                 item;
1722
1723             for (; i < ln; i++) {
1724                 item = array[i];
1725
1726                 if (!Ext.isEmpty(item)) {
1727                     results.push(item);
1728                 }
1729             }
1730
1731             return results;
1732         },
1733
1734         /**
1735          * Returns a new array with unique items
1736          *
1737          * @param {Array} array
1738          * @return {Array} results
1739          */
1740         unique: function(array) {
1741             var clone = [],
1742                 i = 0,
1743                 ln = array.length,
1744                 item;
1745
1746             for (; i < ln; i++) {
1747                 item = array[i];
1748
1749                 if (ExtArray.indexOf(clone, item) === -1) {
1750                     clone.push(item);
1751                 }
1752             }
1753
1754             return clone;
1755         },
1756
1757         /**
1758          * Creates a new array with all of the elements of this array for which
1759          * the provided filtering function returns true.
1760          *
1761          * @param {Array} array
1762          * @param {Function} fn Callback function for each item
1763          * @param {Object} scope Callback function scope
1764          * @return {Array} results
1765          */
1766         filter: function(array, fn, scope) {
1767             if (supportsFilter) {
1768                 return array.filter(fn, scope);
1769             }
1770
1771             var results = [],
1772                 i = 0,
1773                 ln = array.length;
1774
1775             for (; i < ln; i++) {
1776                 if (fn.call(scope, array[i], i, array)) {
1777                     results.push(array[i]);
1778                 }
1779             }
1780
1781             return results;
1782         },
1783
1784         /**
1785          * Converts a value to an array if it's not already an array; returns:
1786          *
1787          * - An empty array if given value is `undefined` or `null`
1788          * - Itself if given value is already an array
1789          * - An array copy if given value is {@link Ext#isIterable iterable} (arguments, NodeList and alike)
1790          * - An array with one item which is the given value, otherwise
1791          *
1792          * @param {Object} value The value to convert to an array if it's not already is an array
1793          * @param {Boolean} newReference (Optional) True to clone the given array and return a new reference if necessary,
1794          * defaults to false
1795          * @return {Array} array
1796          */
1797         from: function(value, newReference) {
1798             if (value === undefined || value === null) {
1799                 return [];
1800             }
1801
1802             if (Ext.isArray(value)) {
1803                 return (newReference) ? slice.call(value) : value;
1804             }
1805
1806             if (value && value.length !== undefined && typeof value !== 'string') {
1807                 return Ext.toArray(value);
1808             }
1809
1810             return [value];
1811         },
1812
1813         /**
1814          * Removes the specified item from the array if it exists
1815          *
1816          * @param {Array} array The array
1817          * @param {Object} item The item to remove
1818          * @return {Array} The passed array itself
1819          */
1820         remove: function(array, item) {
1821             var index = ExtArray.indexOf(array, item);
1822
1823             if (index !== -1) {
1824                 erase(array, index, 1);
1825             }
1826
1827             return array;
1828         },
1829
1830         /**
1831          * Push an item into the array only if the array doesn't contain it yet
1832          *
1833          * @param {Array} array The array
1834          * @param {Object} item The item to include
1835          */
1836         include: function(array, item) {
1837             if (!ExtArray.contains(array, item)) {
1838                 array.push(item);
1839             }
1840         },
1841
1842         /**
1843          * Clone a flat array without referencing the previous one. Note that this is different
1844          * from Ext.clone since it doesn't handle recursive cloning. It's simply a convenient, easy-to-remember method
1845          * for Array.prototype.slice.call(array)
1846          *
1847          * @param {Array} array The array
1848          * @return {Array} The clone array
1849          */
1850         clone: function(array) {
1851             return slice.call(array);
1852         },
1853
1854         /**
1855          * Merge multiple arrays into one with unique items.
1856          *
1857          * {@link Ext.Array#union} is alias for {@link Ext.Array#merge}
1858          *
1859          * @param {Array} array1
1860          * @param {Array} array2
1861          * @param {Array} etc
1862          * @return {Array} merged
1863          */
1864         merge: function() {
1865             var args = slice.call(arguments),
1866                 array = [],
1867                 i, ln;
1868
1869             for (i = 0, ln = args.length; i < ln; i++) {
1870                 array = array.concat(args[i]);
1871             }
1872
1873             return ExtArray.unique(array);
1874         },
1875
1876         /**
1877          * Merge multiple arrays into one with unique items that exist in all of the arrays.
1878          *
1879          * @param {Array} array1
1880          * @param {Array} array2
1881          * @param {Array} etc
1882          * @return {Array} intersect
1883          */
1884         intersect: function() {
1885             var intersect = [],
1886                 arrays = slice.call(arguments),
1887                 i, j, k, minArray, array, x, y, ln, arraysLn, arrayLn;
1888
1889             if (!arrays.length) {
1890                 return intersect;
1891             }
1892
1893             // Find the smallest array
1894             for (i = x = 0,ln = arrays.length; i < ln,array = arrays[i]; i++) {
1895                 if (!minArray || array.length < minArray.length) {
1896                     minArray = array;
1897                     x = i;
1898                 }
1899             }
1900
1901             minArray = ExtArray.unique(minArray);
1902             erase(arrays, x, 1);
1903
1904             // Use the smallest unique'd array as the anchor loop. If the other array(s) do contain
1905             // an item in the small array, we're likely to find it before reaching the end
1906             // of the inner loop and can terminate the search early.
1907             for (i = 0,ln = minArray.length; i < ln,x = minArray[i]; i++) {
1908                 var count = 0;
1909
1910                 for (j = 0,arraysLn = arrays.length; j < arraysLn,array = arrays[j]; j++) {
1911                     for (k = 0,arrayLn = array.length; k < arrayLn,y = array[k]; k++) {
1912                         if (x === y) {
1913                             count++;
1914                             break;
1915                         }
1916                     }
1917                 }
1918
1919                 if (count === arraysLn) {
1920                     intersect.push(x);
1921                 }
1922             }
1923
1924             return intersect;
1925         },
1926
1927         /**
1928          * Perform a set difference A-B by subtracting all items in array B from array A.
1929          *
1930          * @param {Array} arrayA
1931          * @param {Array} arrayB
1932          * @return {Array} difference
1933          */
1934         difference: function(arrayA, arrayB) {
1935             var clone = slice.call(arrayA),
1936                 ln = clone.length,
1937                 i, j, lnB;
1938
1939             for (i = 0,lnB = arrayB.length; i < lnB; i++) {
1940                 for (j = 0; j < ln; j++) {
1941                     if (clone[j] === arrayB[i]) {
1942                         erase(clone, j, 1);
1943                         j--;
1944                         ln--;
1945                     }
1946                 }
1947             }
1948
1949             return clone;
1950         },
1951
1952         /**
1953          * Returns a shallow copy of a part of an array. This is equivalent to the native
1954          * call "Array.prototype.slice.call(array, begin, end)". This is often used when "array"
1955          * is "arguments" since the arguments object does not supply a slice method but can
1956          * be the context object to Array.prototype.slice.
1957          *
1958          * @param {Array} array The array (or arguments object).
1959          * @param {Number} begin The index at which to begin. Negative values are offsets from
1960          * the end of the array.
1961          * @param {Number} end The index at which to end. The copied items do not include
1962          * end. Negative values are offsets from the end of the array. If end is omitted,
1963          * all items up to the end of the array are copied.
1964          * @return {Array} The copied piece of the array.
1965          */
1966         // Note: IE6 will return [] on slice.call(x, undefined).
1967         slice: ([1,2].slice(1, undefined).length ?
1968             function (array, begin, end) {
1969                 return slice.call(array, begin, end);
1970             } :
1971             // at least IE6 uses arguments.length for variadic signature
1972             function (array, begin, end) {
1973                 // After tested for IE 6, the one below is of the best performance
1974                 // see http://jsperf.com/slice-fix
1975                 if (typeof begin === 'undefined') {
1976                     return slice.call(array);
1977                 }
1978                 if (typeof end === 'undefined') {
1979                     return slice.call(array, begin);
1980                 }
1981                 return slice.call(array, begin, end);
1982             }
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          * @param {Array} array The array to flatten
2033          * @return {Array} The 1-d array.
2034          */
2035         flatten: function(array) {
2036             var worker = [];
2037
2038             function rFlatten(a) {
2039                 var i, ln, v;
2040
2041                 for (i = 0, ln = a.length; i < ln; i++) {
2042                     v = a[i];
2043
2044                     if (Ext.isArray(v)) {
2045                         rFlatten(v);
2046                     } else {
2047                         worker.push(v);
2048                     }
2049                 }
2050
2051                 return worker;
2052             }
2053
2054             return rFlatten(array);
2055         },
2056
2057         /**
2058          * Returns the minimum value in the Array.
2059          *
2060          * @param {Array/NodeList} array The Array from which to select the minimum value.
2061          * @param {Function} comparisonFn (optional) a function to perform the comparision which determines minimization.
2062          * If omitted the "<" operator will be used. Note: gt = 1; eq = 0; lt = -1
2063          * @return {Object} minValue The minimum value
2064          */
2065         min: function(array, comparisonFn) {
2066             var min = array[0],
2067                 i, ln, item;
2068
2069             for (i = 0, ln = array.length; i < ln; i++) {
2070                 item = array[i];
2071
2072                 if (comparisonFn) {
2073                     if (comparisonFn(min, item) === 1) {
2074                         min = item;
2075                     }
2076                 }
2077                 else {
2078                     if (item < min) {
2079                         min = item;
2080                     }
2081                 }
2082             }
2083
2084             return min;
2085         },
2086
2087         /**
2088          * Returns the maximum value in the Array.
2089          *
2090          * @param {Array/NodeList} array The Array from which to select the maximum value.
2091          * @param {Function} comparisonFn (optional) a function to perform the comparision which determines maximization.
2092          * If omitted the ">" operator will be used. Note: gt = 1; eq = 0; lt = -1
2093          * @return {Object} maxValue The maximum value
2094          */
2095         max: function(array, comparisonFn) {
2096             var max = array[0],
2097                 i, ln, item;
2098
2099             for (i = 0, ln = array.length; i < ln; i++) {
2100                 item = array[i];
2101
2102                 if (comparisonFn) {
2103                     if (comparisonFn(max, item) === -1) {
2104                         max = item;
2105                     }
2106                 }
2107                 else {
2108                     if (item > max) {
2109                         max = item;
2110                     }
2111                 }
2112             }
2113
2114             return max;
2115         },
2116
2117         /**
2118          * Calculates the mean of all items in the array.
2119          *
2120          * @param {Array} array The Array to calculate the mean value of.
2121          * @return {Number} The mean.
2122          */
2123         mean: function(array) {
2124             return array.length > 0 ? ExtArray.sum(array) / array.length : undefined;
2125         },
2126
2127         /**
2128          * Calculates the sum of all items in the given array.
2129          *
2130          * @param {Array} array The Array to calculate the sum value of.
2131          * @return {Number} The sum.
2132          */
2133         sum: function(array) {
2134             var sum = 0,
2135                 i, ln, item;
2136
2137             for (i = 0,ln = array.length; i < ln; i++) {
2138                 item = array[i];
2139
2140                 sum += item;
2141             }
2142
2143             return sum;
2144         },
2145
2146
2147         /**
2148          * Removes items from an array. This is functionally equivalent to the splice method
2149          * of Array, but works around bugs in IE8's splice method and does not copy the
2150          * removed elements in order to return them (because very often they are ignored).
2151          *
2152          * @param {Array} array The Array on which to replace.
2153          * @param {Number} index The index in the array at which to operate.
2154          * @param {Number} removeCount The number of items to remove at index.
2155          * @return {Array} The array passed.
2156          * @method
2157          */
2158         erase: erase,
2159
2160         /**
2161          * Inserts items in to an array.
2162          *
2163          * @param {Array} array The Array on which to replace.
2164          * @param {Number} index The index in the array at which to operate.
2165          * @param {Array} items The array of items to insert at index.
2166          * @return {Array} The array passed.
2167          */
2168         insert: function (array, index, items) {
2169             return replace(array, index, 0, items);
2170         },
2171
2172         /**
2173          * Replaces items in an array. This is functionally equivalent to the splice method
2174          * of Array, but works around bugs in IE8's splice method and is often more convenient
2175          * to call because it accepts an array of items to insert rather than use a variadic
2176          * argument list.
2177          *
2178          * @param {Array} array The Array on which to replace.
2179          * @param {Number} index The index in the array at which to operate.
2180          * @param {Number} removeCount The number of items to remove at index (can be 0).
2181          * @param {Array} insert (optional) An array of items to insert at index.
2182          * @return {Array} The array passed.
2183          * @method
2184          */
2185         replace: replace,
2186
2187         /**
2188          * Replaces items in an array. This is equivalent to the splice method of Array, but
2189          * works around bugs in IE8's splice method. The signature is exactly the same as the
2190          * splice method except that the array is the first argument. All arguments following
2191          * removeCount are inserted in the array at index.
2192          *
2193          * @param {Array} array The Array on which to replace.
2194          * @param {Number} index The index in the array at which to operate.
2195          * @param {Number} removeCount The number of items to remove at index (can be 0).
2196          * @return {Array} An array containing the removed items.
2197          * @method
2198          */
2199         splice: splice
2200     };
2201
2202     /**
2203      * @method
2204      * @member Ext
2205      * @alias Ext.Array#each
2206      */
2207     Ext.each = ExtArray.each;
2208
2209     /**
2210      * @method
2211      * @member Ext.Array
2212      * @alias Ext.Array#merge
2213      */
2214     ExtArray.union = ExtArray.merge;
2215
2216     /**
2217      * Old alias to {@link Ext.Array#min}
2218      * @deprecated 4.0.0 Use {@link Ext.Array#min} instead
2219      * @method
2220      * @member Ext
2221      * @alias Ext.Array#min
2222      */
2223     Ext.min = ExtArray.min;
2224
2225     /**
2226      * Old alias to {@link Ext.Array#max}
2227      * @deprecated 4.0.0 Use {@link Ext.Array#max} instead
2228      * @method
2229      * @member Ext
2230      * @alias Ext.Array#max
2231      */
2232     Ext.max = ExtArray.max;
2233
2234     /**
2235      * Old alias to {@link Ext.Array#sum}
2236      * @deprecated 4.0.0 Use {@link Ext.Array#sum} instead
2237      * @method
2238      * @member Ext
2239      * @alias Ext.Array#sum
2240      */
2241     Ext.sum = ExtArray.sum;
2242
2243     /**
2244      * Old alias to {@link Ext.Array#mean}
2245      * @deprecated 4.0.0 Use {@link Ext.Array#mean} instead
2246      * @method
2247      * @member Ext
2248      * @alias Ext.Array#mean
2249      */
2250     Ext.mean = ExtArray.mean;
2251
2252     /**
2253      * Old alias to {@link Ext.Array#flatten}
2254      * @deprecated 4.0.0 Use {@link Ext.Array#flatten} instead
2255      * @method
2256      * @member Ext
2257      * @alias Ext.Array#flatten
2258      */
2259     Ext.flatten = ExtArray.flatten;
2260
2261     /**
2262      * Old alias to {@link Ext.Array#clean}
2263      * @deprecated 4.0.0 Use {@link Ext.Array#clean} instead
2264      * @method
2265      * @member Ext
2266      * @alias Ext.Array#clean
2267      */
2268     Ext.clean = ExtArray.clean;
2269
2270     /**
2271      * Old alias to {@link Ext.Array#unique}
2272      * @deprecated 4.0.0 Use {@link Ext.Array#unique} instead
2273      * @method
2274      * @member Ext
2275      * @alias Ext.Array#unique
2276      */
2277     Ext.unique = ExtArray.unique;
2278
2279     /**
2280      * Old alias to {@link Ext.Array#pluck Ext.Array.pluck}
2281      * @deprecated 4.0.0 Use {@link Ext.Array#pluck Ext.Array.pluck} instead
2282      * @method
2283      * @member Ext
2284      * @alias Ext.Array#pluck
2285      */
2286     Ext.pluck = ExtArray.pluck;
2287
2288     /**
2289      * @method
2290      * @member Ext
2291      * @alias Ext.Array#toArray
2292      */
2293     Ext.toArray = function() {
2294         return ExtArray.toArray.apply(ExtArray, arguments);
2295     };
2296 })();
2297
2298 /**
2299  * @class Ext.Function
2300  *
2301  * A collection of useful static methods to deal with function callbacks
2302  * @singleton
2303  */
2304 Ext.Function = {
2305
2306     /**
2307      * A very commonly used method throughout the framework. It acts as a wrapper around another method
2308      * which originally accepts 2 arguments for `name` and `value`.
2309      * The wrapped function then allows "flexible" value setting of either:
2310      *
2311      * - `name` and `value` as 2 arguments
2312      * - one single object argument with multiple key - value pairs
2313      *
2314      * For example:
2315      *
2316      *     var setValue = Ext.Function.flexSetter(function(name, value) {
2317      *         this[name] = value;
2318      *     });
2319      *
2320      *     // Afterwards
2321      *     // Setting a single name - value
2322      *     setValue('name1', 'value1');
2323      *
2324      *     // Settings multiple name - value pairs
2325      *     setValue({
2326      *         name1: 'value1',
2327      *         name2: 'value2',
2328      *         name3: 'value3'
2329      *     });
2330      *
2331      * @param {Function} setter
2332      * @returns {Function} flexSetter
2333      */
2334     flexSetter: function(fn) {
2335         return function(a, b) {
2336             var k, i;
2337
2338             if (a === null) {
2339                 return this;
2340             }
2341
2342             if (typeof a !== 'string') {
2343                 for (k in a) {
2344                     if (a.hasOwnProperty(k)) {
2345                         fn.call(this, k, a[k]);
2346                     }
2347                 }
2348
2349                 if (Ext.enumerables) {
2350                     for (i = Ext.enumerables.length; i--;) {
2351                         k = Ext.enumerables[i];
2352                         if (a.hasOwnProperty(k)) {
2353                             fn.call(this, k, a[k]);
2354                         }
2355                     }
2356                 }
2357             } else {
2358                 fn.call(this, a, b);
2359             }
2360
2361             return this;
2362         };
2363     },
2364
2365     /**
2366      * Create a new function from the provided `fn`, change `this` to the provided scope, optionally
2367      * overrides arguments for the call. (Defaults to the arguments passed by the caller)
2368      *
2369      * {@link Ext#bind Ext.bind} is alias for {@link Ext.Function#bind Ext.Function.bind}
2370      *
2371      * @param {Function} fn The function to delegate.
2372      * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed.
2373      * **If omitted, defaults to the browser window.**
2374      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
2375      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
2376      * if a number the args are inserted at the specified position
2377      * @return {Function} The new function
2378      */
2379     bind: function(fn, scope, args, appendArgs) {
2380         if (arguments.length === 2) {
2381             return function() {
2382                 return fn.apply(scope, arguments);
2383             }
2384         }
2385
2386         var method = fn,
2387             slice = Array.prototype.slice;
2388
2389         return function() {
2390             var callArgs = args || arguments;
2391
2392             if (appendArgs === true) {
2393                 callArgs = slice.call(arguments, 0);
2394                 callArgs = callArgs.concat(args);
2395             }
2396             else if (typeof appendArgs == 'number') {
2397                 callArgs = slice.call(arguments, 0); // copy arguments first
2398                 Ext.Array.insert(callArgs, appendArgs, args);
2399             }
2400
2401             return method.apply(scope || window, callArgs);
2402         };
2403     },
2404
2405     /**
2406      * Create a new function from the provided `fn`, the arguments of which are pre-set to `args`.
2407      * New arguments passed to the newly created callback when it's invoked are appended after the pre-set ones.
2408      * This is especially useful when creating callbacks.
2409      *
2410      * For example:
2411      *
2412      *     var originalFunction = function(){
2413      *         alert(Ext.Array.from(arguments).join(' '));
2414      *     };
2415      *
2416      *     var callback = Ext.Function.pass(originalFunction, ['Hello', 'World']);
2417      *
2418      *     callback(); // alerts 'Hello World'
2419      *     callback('by Me'); // alerts 'Hello World by Me'
2420      *
2421      * {@link Ext#pass Ext.pass} is alias for {@link Ext.Function#pass Ext.Function.pass}
2422      *
2423      * @param {Function} fn The original function
2424      * @param {Array} args The arguments to pass to new callback
2425      * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed.
2426      * @return {Function} The new callback function
2427      */
2428     pass: function(fn, args, scope) {
2429         if (args) {
2430             args = Ext.Array.from(args);
2431         }
2432
2433         return function() {
2434             return fn.apply(scope, args.concat(Ext.Array.toArray(arguments)));
2435         };
2436     },
2437
2438     /**
2439      * Create an alias to the provided method property with name `methodName` of `object`.
2440      * Note that the execution scope will still be bound to the provided `object` itself.
2441      *
2442      * @param {Object/Function} object
2443      * @param {String} methodName
2444      * @return {Function} aliasFn
2445      */
2446     alias: function(object, methodName) {
2447         return function() {
2448             return object[methodName].apply(object, arguments);
2449         };
2450     },
2451
2452     /**
2453      * Creates an interceptor function. The passed function is called before the original one. If it returns false,
2454      * the original one is not called. The resulting function returns the results of the original function.
2455      * The passed function is called with the parameters of the original function. Example usage:
2456      *
2457      *     var sayHi = function(name){
2458      *         alert('Hi, ' + name);
2459      *     }
2460      *
2461      *     sayHi('Fred'); // alerts "Hi, Fred"
2462      *
2463      *     // create a new function that validates input without
2464      *     // directly modifying the original function:
2465      *     var sayHiToFriend = Ext.Function.createInterceptor(sayHi, function(name){
2466      *         return name == 'Brian';
2467      *     });
2468      *
2469      *     sayHiToFriend('Fred');  // no alert
2470      *     sayHiToFriend('Brian'); // alerts "Hi, Brian"
2471      *
2472      * @param {Function} origFn The original function.
2473      * @param {Function} newFn The function to call before the original
2474      * @param {Object} scope (optional) The scope (`this` reference) in which the passed function is executed.
2475      * **If omitted, defaults to the scope in which the original function is called or the browser window.**
2476      * @param {Object} returnValue (optional) The value to return if the passed function return false (defaults to null).
2477      * @return {Function} The new function
2478      */
2479     createInterceptor: function(origFn, newFn, scope, returnValue) {
2480         var method = origFn;
2481         if (!Ext.isFunction(newFn)) {
2482             return origFn;
2483         }
2484         else {
2485             return function() {
2486                 var me = this,
2487                     args = arguments;
2488                 newFn.target = me;
2489                 newFn.method = origFn;
2490                 return (newFn.apply(scope || me || window, args) !== false) ? origFn.apply(me || window, args) : returnValue || null;
2491             };
2492         }
2493     },
2494
2495     /**
2496      * Creates a delegate (callback) which, when called, executes after a specific delay.
2497      *
2498      * @param {Function} fn The function which will be called on a delay when the returned function is called.
2499      * Optionally, a replacement (or additional) argument list may be specified.
2500      * @param {Number} delay The number of milliseconds to defer execution by whenever called.
2501      * @param {Object} scope (optional) The scope (`this` reference) used by the function at execution time.
2502      * @param {Array} args (optional) Override arguments for the call. (Defaults to the arguments passed by the caller)
2503      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
2504      * if a number the args are inserted at the specified position.
2505      * @return {Function} A function which, when called, executes the original function after the specified delay.
2506      */
2507     createDelayed: function(fn, delay, scope, args, appendArgs) {
2508         if (scope || args) {
2509             fn = Ext.Function.bind(fn, scope, args, appendArgs);
2510         }
2511         return function() {
2512             var me = this;
2513             setTimeout(function() {
2514                 fn.apply(me, arguments);
2515             }, delay);
2516         };
2517     },
2518
2519     /**
2520      * Calls this function after the number of millseconds specified, optionally in a specific scope. Example usage:
2521      *
2522      *     var sayHi = function(name){
2523      *         alert('Hi, ' + name);
2524      *     }
2525      *
2526      *     // executes immediately:
2527      *     sayHi('Fred');
2528      *
2529      *     // executes after 2 seconds:
2530      *     Ext.Function.defer(sayHi, 2000, this, ['Fred']);
2531      *
2532      *     // this syntax is sometimes useful for deferring
2533      *     // execution of an anonymous function:
2534      *     Ext.Function.defer(function(){
2535      *         alert('Anonymous');
2536      *     }, 100);
2537      *
2538      * {@link Ext#defer Ext.defer} is alias for {@link Ext.Function#defer Ext.Function.defer}
2539      *
2540      * @param {Function} fn The function to defer.
2541      * @param {Number} millis The number of milliseconds for the setTimeout call
2542      * (if less than or equal to 0 the function is executed immediately)
2543      * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed.
2544      * **If omitted, defaults to the browser window.**
2545      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
2546      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
2547      * if a number the args are inserted at the specified position
2548      * @return {Number} The timeout id that can be used with clearTimeout
2549      */
2550     defer: function(fn, millis, obj, args, appendArgs) {
2551         fn = Ext.Function.bind(fn, obj, args, appendArgs);
2552         if (millis > 0) {
2553             return setTimeout(fn, millis);
2554         }
2555         fn();
2556         return 0;
2557     },
2558
2559     /**
2560      * Create a combined function call sequence of the original function + the passed function.
2561      * The resulting function returns the results of the original function.
2562      * The passed function is called with the parameters of the original function. Example usage:
2563      *
2564      *     var sayHi = function(name){
2565      *         alert('Hi, ' + name);
2566      *     }
2567      *
2568      *     sayHi('Fred'); // alerts "Hi, Fred"
2569      *
2570      *     var sayGoodbye = Ext.Function.createSequence(sayHi, function(name){
2571      *         alert('Bye, ' + name);
2572      *     });
2573      *
2574      *     sayGoodbye('Fred'); // both alerts show
2575      *
2576      * @param {Function} origFn The original function.
2577      * @param {Function} newFn The function to sequence
2578      * @param {Object} scope (optional) The scope (`this` reference) in which the passed function is executed.
2579      * If omitted, defaults to the scope in which the original function is called or the browser window.
2580      * @return {Function} The new function
2581      */
2582     createSequence: function(origFn, newFn, scope) {
2583         if (!Ext.isFunction(newFn)) {
2584             return origFn;
2585         }
2586         else {
2587             return function() {
2588                 var retval = origFn.apply(this || window, arguments);
2589                 newFn.apply(scope || this || window, arguments);
2590                 return retval;
2591             };
2592         }
2593     },
2594
2595     /**
2596      * Creates a delegate function, optionally with a bound scope which, when called, buffers
2597      * the execution of the passed function for the configured number of milliseconds.
2598      * If called again within that period, the impending invocation will be canceled, and the
2599      * timeout period will begin again.
2600      *
2601      * @param {Function} fn The function to invoke on a buffered timer.
2602      * @param {Number} buffer The number of milliseconds by which to buffer the invocation of the
2603      * function.
2604      * @param {Object} scope (optional) The scope (`this` reference) in which
2605      * the passed function is executed. If omitted, defaults to the scope specified by the caller.
2606      * @param {Array} args (optional) Override arguments for the call. Defaults to the arguments
2607      * passed by the caller.
2608      * @return {Function} A function which invokes the passed function after buffering for the specified time.
2609      */
2610     createBuffered: function(fn, buffer, scope, args) {
2611         return function(){
2612             var timerId;
2613             return function() {
2614                 var me = this;
2615                 if (timerId) {
2616                     clearTimeout(timerId);
2617                     timerId = null;
2618                 }
2619                 timerId = setTimeout(function(){
2620                     fn.apply(scope || me, args || arguments);
2621                 }, buffer);
2622             };
2623         }();
2624     },
2625
2626     /**
2627      * Creates a throttled version of the passed function which, when called repeatedly and
2628      * rapidly, invokes the passed function only after a certain interval has elapsed since the
2629      * previous invocation.
2630      *
2631      * This is useful for wrapping functions which may be called repeatedly, such as
2632      * a handler of a mouse move event when the processing is expensive.
2633      *
2634      * @param {Function} fn The function to execute at a regular time interval.
2635      * @param {Number} interval The interval **in milliseconds** on which the passed function is executed.
2636      * @param {Object} scope (optional) The scope (`this` reference) in which
2637      * the passed function is executed. If omitted, defaults to the scope specified by the caller.
2638      * @returns {Function} A function which invokes the passed function at the specified interval.
2639      */
2640     createThrottled: function(fn, interval, scope) {
2641         var lastCallTime, elapsed, lastArgs, timer, execute = function() {
2642             fn.apply(scope || this, lastArgs);
2643             lastCallTime = new Date().getTime();
2644         };
2645
2646         return function() {
2647             elapsed = new Date().getTime() - lastCallTime;
2648             lastArgs = arguments;
2649
2650             clearTimeout(timer);
2651             if (!lastCallTime || (elapsed >= interval)) {
2652                 execute();
2653             } else {
2654                 timer = setTimeout(execute, interval - elapsed);
2655             }
2656         };
2657     },
2658
2659     /**
2660      * Adds behavior to an existing method that is executed before the
2661      * original behavior of the function.  For example:
2662      * 
2663      *     var soup = {
2664      *         contents: [],
2665      *         add: function(ingredient) {
2666      *             this.contents.push(ingredient);
2667      *         }
2668      *     };
2669      *     Ext.Function.interceptBefore(soup, "add", function(ingredient){
2670      *         if (!this.contents.length && ingredient !== "water") {
2671      *             // Always add water to start with
2672      *             this.contents.push("water");
2673      *         }
2674      *     });
2675      *     soup.add("onions");
2676      *     soup.add("salt");
2677      *     soup.contents; // will contain: water, onions, salt
2678      * 
2679      * @param {Object} object The target object
2680      * @param {String} methodName Name of the method to override
2681      * @param {Function} fn Function with the new behavior.  It will
2682      * be called with the same arguments as the original method.  The
2683      * return value of this function will be the return value of the
2684      * new method.
2685      * @return {Function} The new function just created.
2686      */
2687     interceptBefore: function(object, methodName, fn) {
2688         var method = object[methodName] || Ext.emptyFn;
2689
2690         return object[methodName] = function() {
2691             var ret = fn.apply(this, arguments);
2692             method.apply(this, arguments);
2693
2694             return ret;
2695         };
2696     },
2697
2698     /**
2699      * Adds behavior to an existing method that is executed after the
2700      * original behavior of the function.  For example:
2701      * 
2702      *     var soup = {
2703      *         contents: [],
2704      *         add: function(ingredient) {
2705      *             this.contents.push(ingredient);
2706      *         }
2707      *     };
2708      *     Ext.Function.interceptAfter(soup, "add", function(ingredient){
2709      *         // Always add a bit of extra salt
2710      *         this.contents.push("salt");
2711      *     });
2712      *     soup.add("water");
2713      *     soup.add("onions");
2714      *     soup.contents; // will contain: water, salt, onions, salt
2715      * 
2716      * @param {Object} object The target object
2717      * @param {String} methodName Name of the method to override
2718      * @param {Function} fn Function with the new behavior.  It will
2719      * be called with the same arguments as the original method.  The
2720      * return value of this function will be the return value of the
2721      * new method.
2722      * @return {Function} The new function just created.
2723      */
2724     interceptAfter: function(object, methodName, fn) {
2725         var method = object[methodName] || Ext.emptyFn;
2726
2727         return object[methodName] = function() {
2728             method.apply(this, arguments);
2729             return fn.apply(this, arguments);
2730         };
2731     }
2732 };
2733
2734 /**
2735  * @method
2736  * @member Ext
2737  * @alias Ext.Function#defer
2738  */
2739 Ext.defer = Ext.Function.alias(Ext.Function, 'defer');
2740
2741 /**
2742  * @method
2743  * @member Ext
2744  * @alias Ext.Function#pass
2745  */
2746 Ext.pass = Ext.Function.alias(Ext.Function, 'pass');
2747
2748 /**
2749  * @method
2750  * @member Ext
2751  * @alias Ext.Function#bind
2752  */
2753 Ext.bind = Ext.Function.alias(Ext.Function, 'bind');
2754
2755 /**
2756  * @author Jacky Nguyen <jacky@sencha.com>
2757  * @docauthor Jacky Nguyen <jacky@sencha.com>
2758  * @class Ext.Object
2759  *
2760  * A collection of useful static methods to deal with objects.
2761  *
2762  * @singleton
2763  */
2764
2765 (function() {
2766
2767 var ExtObject = Ext.Object = {
2768
2769     /**
2770      * Converts a `name` - `value` pair to an array of objects with support for nested structures. Useful to construct
2771      * query strings. For example:
2772      *
2773      *     var objects = Ext.Object.toQueryObjects('hobbies', ['reading', 'cooking', 'swimming']);
2774      *
2775      *     // objects then equals:
2776      *     [
2777      *         { name: 'hobbies', value: 'reading' },
2778      *         { name: 'hobbies', value: 'cooking' },
2779      *         { name: 'hobbies', value: 'swimming' },
2780      *     ];
2781      *
2782      *     var objects = Ext.Object.toQueryObjects('dateOfBirth', {
2783      *         day: 3,
2784      *         month: 8,
2785      *         year: 1987,
2786      *         extra: {
2787      *             hour: 4
2788      *             minute: 30
2789      *         }
2790      *     }, true); // Recursive
2791      *
2792      *     // objects then equals:
2793      *     [
2794      *         { name: 'dateOfBirth[day]', value: 3 },
2795      *         { name: 'dateOfBirth[month]', value: 8 },
2796      *         { name: 'dateOfBirth[year]', value: 1987 },
2797      *         { name: 'dateOfBirth[extra][hour]', value: 4 },
2798      *         { name: 'dateOfBirth[extra][minute]', value: 30 },
2799      *     ];
2800      *
2801      * @param {String} name
2802      * @param {Object/Array} value
2803      * @param {Boolean} [recursive=false] True to traverse object recursively
2804      * @return {Array}
2805      */
2806     toQueryObjects: function(name, value, recursive) {
2807         var self = ExtObject.toQueryObjects,
2808             objects = [],
2809             i, ln;
2810
2811         if (Ext.isArray(value)) {
2812             for (i = 0, ln = value.length; i < ln; i++) {
2813                 if (recursive) {
2814                     objects = objects.concat(self(name + '[' + i + ']', value[i], true));
2815                 }
2816                 else {
2817                     objects.push({
2818                         name: name,
2819                         value: value[i]
2820                     });
2821                 }
2822             }
2823         }
2824         else if (Ext.isObject(value)) {
2825             for (i in value) {
2826                 if (value.hasOwnProperty(i)) {
2827                     if (recursive) {
2828                         objects = objects.concat(self(name + '[' + i + ']', value[i], true));
2829                     }
2830                     else {
2831                         objects.push({
2832                             name: name,
2833                             value: value[i]
2834                         });
2835                     }
2836                 }
2837             }
2838         }
2839         else {
2840             objects.push({
2841                 name: name,
2842                 value: value
2843             });
2844         }
2845
2846         return objects;
2847     },
2848
2849     /**
2850      * Takes an object and converts it to an encoded query string.
2851      *
2852      * Non-recursive:
2853      *
2854      *     Ext.Object.toQueryString({foo: 1, bar: 2}); // returns "foo=1&bar=2"
2855      *     Ext.Object.toQueryString({foo: null, bar: 2}); // returns "foo=&bar=2"
2856      *     Ext.Object.toQueryString({'some price': '$300'}); // returns "some%20price=%24300"
2857      *     Ext.Object.toQueryString({date: new Date(2011, 0, 1)}); // returns "date=%222011-01-01T00%3A00%3A00%22"
2858      *     Ext.Object.toQueryString({colors: ['red', 'green', 'blue']}); // returns "colors=red&colors=green&colors=blue"
2859      *
2860      * Recursive:
2861      *
2862      *     Ext.Object.toQueryString({
2863      *         username: 'Jacky',
2864      *         dateOfBirth: {
2865      *             day: 1,
2866      *             month: 2,
2867      *             year: 1911
2868      *         },
2869      *         hobbies: ['coding', 'eating', 'sleeping', ['nested', 'stuff']]
2870      *     }, true); // returns the following string (broken down and url-decoded for ease of reading purpose):
2871      *     // username=Jacky
2872      *     //    &dateOfBirth[day]=1&dateOfBirth[month]=2&dateOfBirth[year]=1911
2873      *     //    &hobbies[0]=coding&hobbies[1]=eating&hobbies[2]=sleeping&hobbies[3][0]=nested&hobbies[3][1]=stuff
2874      *
2875      * @param {Object} object The object to encode
2876      * @param {Boolean} [recursive=false] Whether or not to interpret the object in recursive format.
2877      * (PHP / Ruby on Rails servers and similar).
2878      * @return {String} queryString
2879      */
2880     toQueryString: function(object, recursive) {
2881         var paramObjects = [],
2882             params = [],
2883             i, j, ln, paramObject, value;
2884
2885         for (i in object) {
2886             if (object.hasOwnProperty(i)) {
2887                 paramObjects = paramObjects.concat(ExtObject.toQueryObjects(i, object[i], recursive));
2888             }
2889         }
2890
2891         for (j = 0, ln = paramObjects.length; j < ln; j++) {
2892             paramObject = paramObjects[j];
2893             value = paramObject.value;
2894
2895             if (Ext.isEmpty(value)) {
2896                 value = '';
2897             }
2898             else if (Ext.isDate(value)) {
2899                 value = Ext.Date.toString(value);
2900             }
2901
2902             params.push(encodeURIComponent(paramObject.name) + '=' + encodeURIComponent(String(value)));
2903         }
2904
2905         return params.join('&');
2906     },
2907
2908     /**
2909      * Converts a query string back into an object.
2910      *
2911      * Non-recursive:
2912      *
2913      *     Ext.Object.fromQueryString(foo=1&bar=2); // returns {foo: 1, bar: 2}
2914      *     Ext.Object.fromQueryString(foo=&bar=2); // returns {foo: null, bar: 2}
2915      *     Ext.Object.fromQueryString(some%20price=%24300); // returns {'some price': '$300'}
2916      *     Ext.Object.fromQueryString(colors=red&colors=green&colors=blue); // returns {colors: ['red', 'green', 'blue']}
2917      *
2918      * Recursive:
2919      *
2920      *       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);
2921      *     // returns
2922      *     {
2923      *         username: 'Jacky',
2924      *         dateOfBirth: {
2925      *             day: '1',
2926      *             month: '2',
2927      *             year: '1911'
2928      *         },
2929      *         hobbies: ['coding', 'eating', 'sleeping', ['nested', 'stuff']]
2930      *     }
2931      *
2932      * @param {String} queryString The query string to decode
2933      * @param {Boolean} [recursive=false] Whether or not to recursively decode the string. This format is supported by
2934      * PHP / Ruby on Rails servers and similar.
2935      * @return {Object}
2936      */
2937     fromQueryString: function(queryString, recursive) {
2938         var parts = queryString.replace(/^\?/, '').split('&'),
2939             object = {},
2940             temp, components, name, value, i, ln,
2941             part, j, subLn, matchedKeys, matchedName,
2942             keys, key, nextKey;
2943
2944         for (i = 0, ln = parts.length; i < ln; i++) {
2945             part = parts[i];
2946
2947             if (part.length > 0) {
2948                 components = part.split('=');
2949                 name = decodeURIComponent(components[0]);
2950                 value = (components[1] !== undefined) ? decodeURIComponent(components[1]) : '';
2951
2952                 if (!recursive) {
2953                     if (object.hasOwnProperty(name)) {
2954                         if (!Ext.isArray(object[name])) {
2955                             object[name] = [object[name]];
2956                         }
2957
2958                         object[name].push(value);
2959                     }
2960                     else {
2961                         object[name] = value;
2962                     }
2963                 }
2964                 else {
2965                     matchedKeys = name.match(/(\[):?([^\]]*)\]/g);
2966                     matchedName = name.match(/^([^\[]+)/);
2967
2968
2969                     name = matchedName[0];
2970                     keys = [];
2971
2972                     if (matchedKeys === null) {
2973                         object[name] = value;
2974                         continue;
2975                     }
2976
2977                     for (j = 0, subLn = matchedKeys.length; j < subLn; j++) {
2978                         key = matchedKeys[j];
2979                         key = (key.length === 2) ? '' : key.substring(1, key.length - 1);
2980                         keys.push(key);
2981                     }
2982
2983                     keys.unshift(name);
2984
2985                     temp = object;
2986
2987                     for (j = 0, subLn = keys.length; j < subLn; j++) {
2988                         key = keys[j];
2989
2990                         if (j === subLn - 1) {
2991                             if (Ext.isArray(temp) && key === '') {
2992                                 temp.push(value);
2993                             }
2994                             else {
2995                                 temp[key] = value;
2996                             }
2997                         }
2998                         else {
2999                             if (temp[key] === undefined || typeof temp[key] === 'string') {
3000                                 nextKey = keys[j+1];
3001
3002                                 temp[key] = (Ext.isNumeric(nextKey) || nextKey === '') ? [] : {};
3003                             }
3004
3005                             temp = temp[key];
3006                         }
3007                     }
3008                 }
3009             }
3010         }
3011
3012         return object;
3013     },
3014
3015     /**
3016      * Iterates through an object and invokes the given callback function for each iteration.
3017      * The iteration can be stopped by returning `false` in the callback function. For example:
3018      *
3019      *     var person = {
3020      *         name: 'Jacky'
3021      *         hairColor: 'black'
3022      *         loves: ['food', 'sleeping', 'wife']
3023      *     };
3024      *
3025      *     Ext.Object.each(person, function(key, value, myself) {
3026      *         console.log(key + ":" + value);
3027      *
3028      *         if (key === 'hairColor') {
3029      *             return false; // stop the iteration
3030      *         }
3031      *     });
3032      *
3033      * @param {Object} object The object to iterate
3034      * @param {Function} fn The callback function.
3035      * @param {String} fn.key
3036      * @param {Object} fn.value
3037      * @param {Object} fn.object The object itself
3038      * @param {Object} [scope] The execution scope (`this`) of the callback function
3039      */
3040     each: function(object, fn, scope) {
3041         for (var property in object) {
3042             if (object.hasOwnProperty(property)) {
3043                 if (fn.call(scope || object, property, object[property], object) === false) {
3044                     return;
3045                 }
3046             }
3047         }
3048     },
3049
3050     /**
3051      * Merges any number of objects recursively without referencing them or their children.
3052      *
3053      *     var extjs = {
3054      *         companyName: 'Ext JS',
3055      *         products: ['Ext JS', 'Ext GWT', 'Ext Designer'],
3056      *         isSuperCool: true
3057      *         office: {
3058      *             size: 2000,
3059      *             location: 'Palo Alto',
3060      *             isFun: true
3061      *         }
3062      *     };
3063      *
3064      *     var newStuff = {
3065      *         companyName: 'Sencha Inc.',
3066      *         products: ['Ext JS', 'Ext GWT', 'Ext Designer', 'Sencha Touch', 'Sencha Animator'],
3067      *         office: {
3068      *             size: 40000,
3069      *             location: 'Redwood City'
3070      *         }
3071      *     };
3072      *
3073      *     var sencha = Ext.Object.merge(extjs, newStuff);
3074      *
3075      *     // extjs and sencha then equals to
3076      *     {
3077      *         companyName: 'Sencha Inc.',
3078      *         products: ['Ext JS', 'Ext GWT', 'Ext Designer', 'Sencha Touch', 'Sencha Animator'],
3079      *         isSuperCool: true
3080      *         office: {
3081      *             size: 30000,
3082      *             location: 'Redwood City'
3083      *             isFun: true
3084      *         }
3085      *     }
3086      *
3087      * @param {Object...} object Any number of objects to merge.
3088      * @return {Object} merged The object that is created as a result of merging all the objects passed in.
3089      */
3090     merge: function(source, key, value) {
3091         if (typeof key === 'string') {
3092             if (value && value.constructor === Object) {
3093                 if (source[key] && source[key].constructor === Object) {
3094                     ExtObject.merge(source[key], value);
3095                 }
3096                 else {
3097                     source[key] = Ext.clone(value);
3098                 }
3099             }
3100             else {
3101                 source[key] = value;
3102             }
3103
3104             return source;
3105         }
3106
3107         var i = 1,
3108             ln = arguments.length,
3109             object, property;
3110
3111         for (; i < ln; i++) {
3112             object = arguments[i];
3113
3114             for (property in object) {
3115                 if (object.hasOwnProperty(property)) {
3116                     ExtObject.merge(source, property, object[property]);
3117                 }
3118             }
3119         }
3120
3121         return source;
3122     },
3123
3124     /**
3125      * Returns the first matching key corresponding to the given value.
3126      * If no matching value is found, null is returned.
3127      *
3128      *     var person = {
3129      *         name: 'Jacky',
3130      *         loves: 'food'
3131      *     };
3132      *
3133      *     alert(Ext.Object.getKey(person, 'food')); // alerts 'loves'
3134      *
3135      * @param {Object} object
3136      * @param {Object} value The value to find
3137      */
3138     getKey: function(object, value) {
3139         for (var property in object) {
3140             if (object.hasOwnProperty(property) && object[property] === value) {
3141                 return property;
3142             }
3143         }
3144
3145         return null;
3146     },
3147
3148     /**
3149      * Gets all values of the given object as an array.
3150      *
3151      *     var values = Ext.Object.getValues({
3152      *         name: 'Jacky',
3153      *         loves: 'food'
3154      *     }); // ['Jacky', 'food']
3155      *
3156      * @param {Object} object
3157      * @return {Array} An array of values from the object
3158      */
3159     getValues: function(object) {
3160         var values = [],
3161             property;
3162
3163         for (property in object) {
3164             if (object.hasOwnProperty(property)) {
3165                 values.push(object[property]);
3166             }
3167         }
3168
3169         return values;
3170     },
3171
3172     /**
3173      * Gets all keys of the given object as an array.
3174      *
3175      *     var values = Ext.Object.getKeys({
3176      *         name: 'Jacky',
3177      *         loves: 'food'
3178      *     }); // ['name', 'loves']
3179      *
3180      * @param {Object} object
3181      * @return {String[]} An array of keys from the object
3182      * @method
3183      */
3184     getKeys: ('keys' in Object.prototype) ? Object.keys : function(object) {
3185         var keys = [],
3186             property;
3187
3188         for (property in object) {
3189             if (object.hasOwnProperty(property)) {
3190                 keys.push(property);
3191             }
3192         }
3193
3194         return keys;
3195     },
3196
3197     /**
3198      * Gets the total number of this object's own properties
3199      *
3200      *     var size = Ext.Object.getSize({
3201      *         name: 'Jacky',
3202      *         loves: 'food'
3203      *     }); // size equals 2
3204      *
3205      * @param {Object} object
3206      * @return {Number} size
3207      */
3208     getSize: function(object) {
3209         var size = 0,
3210             property;
3211
3212         for (property in object) {
3213             if (object.hasOwnProperty(property)) {
3214                 size++;
3215             }
3216         }
3217
3218         return size;
3219     }
3220 };
3221
3222
3223 /**
3224  * A convenient alias method for {@link Ext.Object#merge}.
3225  *
3226  * @member Ext
3227  * @method merge
3228  * @alias Ext.Object#merge
3229  */
3230 Ext.merge = Ext.Object.merge;
3231
3232 /**
3233  * Alias for {@link Ext.Object#toQueryString}.
3234  *
3235  * @member Ext
3236  * @method urlEncode
3237  * @alias Ext.Object#toQueryString
3238  * @deprecated 4.0.0 Use {@link Ext.Object#toQueryString} instead
3239  */
3240 Ext.urlEncode = function() {
3241     var args = Ext.Array.from(arguments),
3242         prefix = '';
3243
3244     // Support for the old `pre` argument
3245     if ((typeof args[1] === 'string')) {
3246         prefix = args[1] + '&';
3247         args[1] = false;
3248     }
3249
3250     return prefix + Ext.Object.toQueryString.apply(Ext.Object, args);
3251 };
3252
3253 /**
3254  * Alias for {@link Ext.Object#fromQueryString}.
3255  *
3256  * @member Ext
3257  * @method urlDecode
3258  * @alias Ext.Object#fromQueryString
3259  * @deprecated 4.0.0 Use {@link Ext.Object#fromQueryString} instead
3260  */
3261 Ext.urlDecode = function() {
3262     return Ext.Object.fromQueryString.apply(Ext.Object, arguments);
3263 };
3264
3265 })();
3266
3267 /**
3268  * @class Ext.Date
3269  * A set of useful static methods to deal with date
3270  * Note that if Ext.Date is required and loaded, it will copy all methods / properties to
3271  * this object for convenience
3272  *
3273  * The date parsing and formatting syntax contains a subset of
3274  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
3275  * supported will provide results equivalent to their PHP versions.
3276  *
3277  * The following is a list of all currently supported formats:
3278  * <pre class="">
3279 Format  Description                                                               Example returned values
3280 ------  -----------------------------------------------------------------------   -----------------------
3281   d     Day of the month, 2 digits with leading zeros                             01 to 31
3282   D     A short textual representation of the day of the week                     Mon to Sun
3283   j     Day of the month without leading zeros                                    1 to 31
3284   l     A full textual representation of the day of the week                      Sunday to Saturday
3285   N     ISO-8601 numeric representation of the day of the week                    1 (for Monday) through 7 (for Sunday)
3286   S     English ordinal suffix for the day of the month, 2 characters             st, nd, rd or th. Works well with j
3287   w     Numeric representation of the day of the week                             0 (for Sunday) to 6 (for Saturday)
3288   z     The day of the year (starting from 0)                                     0 to 364 (365 in leap years)
3289   W     ISO-8601 week number of year, weeks starting on Monday                    01 to 53
3290   F     A full textual representation of a month, such as January or March        January to December
3291   m     Numeric representation of a month, with leading zeros                     01 to 12
3292   M     A short textual representation of a month                                 Jan to Dec
3293   n     Numeric representation of a month, without leading zeros                  1 to 12
3294   t     Number of days in the given month                                         28 to 31
3295   L     Whether it&#39;s a leap year                                                  1 if it is a leap year, 0 otherwise.
3296   o     ISO-8601 year number (identical to (Y), but if the ISO week number (W)    Examples: 1998 or 2004
3297         belongs to the previous or next year, that year is used instead)
3298   Y     A full numeric representation of a year, 4 digits                         Examples: 1999 or 2003
3299   y     A two digit representation of a year                                      Examples: 99 or 03
3300   a     Lowercase Ante meridiem and Post meridiem                                 am or pm
3301   A     Uppercase Ante meridiem and Post meridiem                                 AM or PM
3302   g     12-hour format of an hour without leading zeros                           1 to 12
3303   G     24-hour format of an hour without leading zeros                           0 to 23
3304   h     12-hour format of an hour with leading zeros                              01 to 12
3305   H     24-hour format of an hour with leading zeros                              00 to 23
3306   i     Minutes, with leading zeros                                               00 to 59
3307   s     Seconds, with leading zeros                                               00 to 59
3308   u     Decimal fraction of a second                                              Examples:
3309         (minimum 1 digit, arbitrary number of digits allowed)                     001 (i.e. 0.001s) or
3310                                                                                   100 (i.e. 0.100s) or
3311                                                                                   999 (i.e. 0.999s) or
3312                                                                                   999876543210 (i.e. 0.999876543210s)
3313   O     Difference to Greenwich time (GMT) in hours and minutes                   Example: +1030
3314   P     Difference to Greenwich time (GMT) with colon between hours and minutes   Example: -08:00
3315   T     Timezone abbreviation of the machine running the code                     Examples: EST, MDT, PDT ...
3316   Z     Timezone offset in seconds (negative if west of UTC, positive if east)    -43200 to 50400
3317   c     ISO 8601 date
3318         Notes:                                                                    Examples:
3319         1) If unspecified, the month / day defaults to the current month / day,   1991 or
3320            the time defaults to midnight, while the timezone defaults to the      1992-10 or
3321            browser's timezone. If a time is specified, it must include both hours 1993-09-20 or
3322            and minutes. The "T" delimiter, seconds, milliseconds and timezone     1994-08-19T16:20+01:00 or
3323            are optional.                                                          1995-07-18T17:21:28-02:00 or
3324         2) The decimal fraction of a second, if specified, must contain at        1996-06-17T18:22:29.98765+03:00 or
3325            least 1 digit (there is no limit to the maximum number                 1997-05-16T19:23:30,12345-0400 or
3326            of digits allowed), and may be delimited by either a '.' or a ','      1998-04-15T20:24:31.2468Z or
3327         Refer to the examples on the right for the various levels of              1999-03-14T20:24:32Z or
3328         date-time granularity which are supported, or see                         2000-02-13T21:25:33
3329         http://www.w3.org/TR/NOTE-datetime for more info.                         2001-01-12 22:26:34
3330   U     Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)                1193432466 or -2138434463
3331   MS    Microsoft AJAX serialized dates                                           \/Date(1238606590509)\/ (i.e. UTC milliseconds since epoch) or
3332                                                                                   \/Date(1238606590509+0800)\/
3333 </pre>
3334  *
3335  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
3336  * <pre><code>
3337 // Sample date:
3338 // 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
3339
3340 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
3341 console.log(Ext.Date.format(dt, 'Y-m-d'));                          // 2007-01-10
3342 console.log(Ext.Date.format(dt, 'F j, Y, g:i a'));                  // January 10, 2007, 3:05 pm
3343 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
3344 </code></pre>
3345  *
3346  * Here are some standard date/time patterns that you might find helpful.  They
3347  * are not part of the source of Ext.Date, but to use them you can simply copy this
3348  * block of code into any script that is included after Ext.Date and they will also become
3349  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
3350  * <pre><code>
3351 Ext.Date.patterns = {
3352     ISO8601Long:"Y-m-d H:i:s",
3353     ISO8601Short:"Y-m-d",
3354     ShortDate: "n/j/Y",
3355     LongDate: "l, F d, Y",
3356     FullDateTime: "l, F d, Y g:i:s A",
3357     MonthDay: "F d",
3358     ShortTime: "g:i A",
3359     LongTime: "g:i:s A",
3360     SortableDateTime: "Y-m-d\\TH:i:s",
3361     UniversalSortableDateTime: "Y-m-d H:i:sO",
3362     YearMonth: "F, Y"
3363 };
3364 </code></pre>
3365  *
3366  * Example usage:
3367  * <pre><code>
3368 var dt = new Date();
3369 console.log(Ext.Date.format(dt, Ext.Date.patterns.ShortDate));
3370 </code></pre>
3371  * <p>Developer-written, custom formats may be used by supplying both a formatting and a parsing function
3372  * which perform to specialized requirements. The functions are stored in {@link #parseFunctions} and {@link #formatFunctions}.</p>
3373  * @singleton
3374  */
3375
3376 /*
3377  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
3378  * (see http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/)
3379  * They generate precompiled functions from format patterns instead of parsing and
3380  * processing each pattern every time a date is formatted. These functions are available
3381  * on every Date object.
3382  */
3383
3384 (function() {
3385
3386 // create private copy of Ext's Ext.util.Format.format() method
3387 // - to remove unnecessary dependency
3388 // - to resolve namespace conflict with MS-Ajax's implementation
3389 function xf(format) {
3390     var args = Array.prototype.slice.call(arguments, 1);
3391     return format.replace(/\{(\d+)\}/g, function(m, i) {
3392         return args[i];
3393     });
3394 }
3395
3396 Ext.Date = {
3397     /**
3398      * Returns the current timestamp
3399      * @return {Date} The current timestamp
3400      * @method
3401      */
3402     now: Date.now || function() {
3403         return +new Date();
3404     },
3405
3406     /**
3407      * @private
3408      * Private for now
3409      */
3410     toString: function(date) {
3411         var pad = Ext.String.leftPad;
3412
3413         return date.getFullYear() + "-"
3414             + pad(date.getMonth() + 1, 2, '0') + "-"
3415             + pad(date.getDate(), 2, '0') + "T"
3416             + pad(date.getHours(), 2, '0') + ":"
3417             + pad(date.getMinutes(), 2, '0') + ":"
3418             + pad(date.getSeconds(), 2, '0');
3419     },
3420
3421     /**
3422      * Returns the number of milliseconds between two dates
3423      * @param {Date} dateA The first date
3424      * @param {Date} dateB (optional) The second date, defaults to now
3425      * @return {Number} The difference in milliseconds
3426      */
3427     getElapsed: function(dateA, dateB) {
3428         return Math.abs(dateA - (dateB || new Date()));
3429     },
3430
3431     /**
3432      * Global flag which determines if strict date parsing should be used.
3433      * Strict date parsing will not roll-over invalid dates, which is the
3434      * default behaviour of javascript Date objects.
3435      * (see {@link #parse} for more information)
3436      * Defaults to <tt>false</tt>.
3437      * @type Boolean
3438     */
3439     useStrict: false,
3440
3441     // private
3442     formatCodeToRegex: function(character, currentGroup) {
3443         // Note: currentGroup - position in regex result array (see notes for Ext.Date.parseCodes below)
3444         var p = utilDate.parseCodes[character];
3445
3446         if (p) {
3447           p = typeof p == 'function'? p() : p;
3448           utilDate.parseCodes[character] = p; // reassign function result to prevent repeated execution
3449         }
3450
3451         return p ? Ext.applyIf({
3452           c: p.c ? xf(p.c, currentGroup || "{0}") : p.c
3453         }, p) : {
3454             g: 0,
3455             c: null,
3456             s: Ext.String.escapeRegex(character) // treat unrecognised characters as literals
3457         };
3458     },
3459
3460     /**
3461      * <p>An object hash in which each property is a date parsing function. The property name is the
3462      * format string which that function parses.</p>
3463      * <p>This object is automatically populated with date parsing functions as
3464      * date formats are requested for Ext standard formatting strings.</p>
3465      * <p>Custom parsing functions may be inserted into this object, keyed by a name which from then on
3466      * may be used as a format string to {@link #parse}.<p>
3467      * <p>Example:</p><pre><code>
3468 Ext.Date.parseFunctions['x-date-format'] = myDateParser;
3469 </code></pre>
3470      * <p>A parsing function should return a Date object, and is passed the following parameters:<div class="mdetail-params"><ul>
3471      * <li><code>date</code> : String<div class="sub-desc">The date string to parse.</div></li>
3472      * <li><code>strict</code> : Boolean<div class="sub-desc">True to validate date strings while parsing
3473      * (i.e. prevent javascript Date "rollover") (The default must be false).
3474      * Invalid date strings should return null when parsed.</div></li>
3475      * </ul></div></p>
3476      * <p>To enable Dates to also be <i>formatted</i> according to that format, a corresponding
3477      * formatting function must be placed into the {@link #formatFunctions} property.
3478      * @property parseFunctions
3479      * @type Object
3480      */
3481     parseFunctions: {
3482         "MS": function(input, strict) {
3483             // note: the timezone offset is ignored since the MS Ajax server sends
3484             // a UTC milliseconds-since-Unix-epoch value (negative values are allowed)
3485             var re = new RegExp('\\/Date\\(([-+])?(\\d+)(?:[+-]\\d{4})?\\)\\/');
3486             var r = (input || '').match(re);
3487             return r? new Date(((r[1] || '') + r[2]) * 1) : null;
3488         }
3489     },
3490     parseRegexes: [],
3491
3492     /**
3493      * <p>An object hash in which each property is a date formatting function. The property name is the
3494      * format string which corresponds to the produced formatted date string.</p>
3495      * <p>This object is automatically populated with date formatting functions as
3496      * date formats are requested for Ext standard formatting strings.</p>
3497      * <p>Custom formatting functions may be inserted into this object, keyed by a name which from then on
3498      * may be used as a format string to {@link #format}. Example:</p><pre><code>
3499 Ext.Date.formatFunctions['x-date-format'] = myDateFormatter;
3500 </code></pre>
3501      * <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>
3502      * <li><code>date</code> : Date<div class="sub-desc">The Date to format.</div></li>
3503      * </ul></div></p>
3504      * <p>To enable date strings to also be <i>parsed</i> according to that format, a corresponding
3505      * parsing function must be placed into the {@link #parseFunctions} property.
3506      * @property formatFunctions
3507      * @type Object
3508      */
3509     formatFunctions: {
3510         "MS": function() {
3511             // UTC milliseconds since Unix epoch (MS-AJAX serialized date format (MRSF))
3512             return '\\/Date(' + this.getTime() + ')\\/';
3513         }
3514     },
3515
3516     y2kYear : 50,
3517
3518     /**
3519      * Date interval constant
3520      * @type String
3521      */
3522     MILLI : "ms",
3523
3524     /**
3525      * Date interval constant
3526      * @type String
3527      */
3528     SECOND : "s",
3529
3530     /**
3531      * Date interval constant
3532      * @type String
3533      */
3534     MINUTE : "mi",
3535
3536     /** Date interval constant
3537      * @type String
3538      */
3539     HOUR : "h",
3540
3541     /**
3542      * Date interval constant
3543      * @type String
3544      */
3545     DAY : "d",
3546
3547     /**
3548      * Date interval constant
3549      * @type String
3550      */
3551     MONTH : "mo",
3552
3553     /**
3554      * Date interval constant
3555      * @type String
3556      */
3557     YEAR : "y",
3558
3559     /**
3560      * <p>An object hash containing default date values used during date parsing.</p>
3561      * <p>The following properties are available:<div class="mdetail-params"><ul>
3562      * <li><code>y</code> : Number<div class="sub-desc">The default year value. (defaults to undefined)</div></li>
3563      * <li><code>m</code> : Number<div class="sub-desc">The default 1-based month value. (defaults to undefined)</div></li>
3564      * <li><code>d</code> : Number<div class="sub-desc">The default day value. (defaults to undefined)</div></li>
3565      * <li><code>h</code> : Number<div class="sub-desc">The default hour value. (defaults to undefined)</div></li>
3566      * <li><code>i</code> : Number<div class="sub-desc">The default minute value. (defaults to undefined)</div></li>
3567      * <li><code>s</code> : Number<div class="sub-desc">The default second value. (defaults to undefined)</div></li>
3568      * <li><code>ms</code> : Number<div class="sub-desc">The default millisecond value. (defaults to undefined)</div></li>
3569      * </ul></div></p>
3570      * <p>Override these properties to customize the default date values used by the {@link #parse} method.</p>
3571      * <p><b>Note: In countries which experience Daylight Saving Time (i.e. DST), the <tt>h</tt>, <tt>i</tt>, <tt>s</tt>
3572      * and <tt>ms</tt> properties may coincide with the exact time in which DST takes effect.
3573      * It is the responsiblity of the developer to account for this.</b></p>
3574      * Example Usage:
3575      * <pre><code>
3576 // set default day value to the first day of the month
3577 Ext.Date.defaults.d = 1;
3578
3579 // parse a February date string containing only year and month values.
3580 // setting the default day value to 1 prevents weird date rollover issues
3581 // when attempting to parse the following date string on, for example, March 31st 2009.
3582 Ext.Date.parse('2009-02', 'Y-m'); // returns a Date object representing February 1st 2009
3583 </code></pre>
3584      * @property defaults
3585      * @type Object
3586      */
3587     defaults: {},
3588
3589     /**
3590      * @property {String[]} dayNames
3591      * An array of textual day names.
3592      * Override these values for international dates.
3593      * Example:
3594      * <pre><code>
3595 Ext.Date.dayNames = [
3596     'SundayInYourLang',
3597     'MondayInYourLang',
3598     ...
3599 ];
3600 </code></pre>
3601      */
3602     dayNames : [
3603         "Sunday",
3604         "Monday",
3605         "Tuesday",
3606         "Wednesday",
3607         "Thursday",
3608         "Friday",
3609         "Saturday"
3610     ],
3611
3612     /**
3613      * @property {String[]} monthNames
3614      * An array of textual month names.
3615      * Override these values for international dates.
3616      * Example:
3617      * <pre><code>
3618 Ext.Date.monthNames = [
3619     'JanInYourLang',
3620     'FebInYourLang',
3621     ...
3622 ];
3623 </code></pre>
3624      */
3625     monthNames : [
3626         "January",
3627         "February",
3628         "March",
3629         "April",
3630         "May",
3631         "June",
3632         "July",
3633         "August",
3634         "September",
3635         "October",
3636         "November",
3637         "December"
3638     ],
3639
3640     /**
3641      * @property {Object} monthNumbers
3642      * An object hash of zero-based javascript month numbers (with short month names as keys. note: keys are case-sensitive).
3643      * Override these values for international dates.
3644      * Example:
3645      * <pre><code>
3646 Ext.Date.monthNumbers = {
3647     'ShortJanNameInYourLang':0,
3648     'ShortFebNameInYourLang':1,
3649     ...
3650 };
3651 </code></pre>
3652      */
3653     monthNumbers : {
3654         Jan:0,
3655         Feb:1,
3656         Mar:2,
3657         Apr:3,
3658         May:4,
3659         Jun:5,
3660         Jul:6,
3661         Aug:7,
3662         Sep:8,
3663         Oct:9,
3664         Nov:10,
3665         Dec:11
3666     },
3667     /**
3668      * @property {String} defaultFormat
3669      * <p>The date format string that the {@link Ext.util.Format#dateRenderer}
3670      * and {@link Ext.util.Format#date} functions use.  See {@link Ext.Date} for details.</p>
3671      * <p>This may be overridden in a locale file.</p>
3672      */
3673     defaultFormat : "m/d/Y",
3674     /**
3675      * Get the short month name for the given month number.
3676      * Override this function for international dates.
3677      * @param {Number} month A zero-based javascript month number.
3678      * @return {String} The short month name.
3679      */
3680     getShortMonthName : function(month) {
3681         return utilDate.monthNames[month].substring(0, 3);
3682     },
3683
3684     /**
3685      * Get the short day name for the given day number.
3686      * Override this function for international dates.
3687      * @param {Number} day A zero-based javascript day number.
3688      * @return {String} The short day name.
3689      */
3690     getShortDayName : function(day) {
3691         return utilDate.dayNames[day].substring(0, 3);
3692     },
3693
3694     /**
3695      * Get the zero-based javascript month number for the given short/full month name.
3696      * Override this function for international dates.
3697      * @param {String} name The short/full month name.
3698      * @return {Number} The zero-based javascript month number.
3699      */
3700     getMonthNumber : function(name) {
3701         // handle camel casing for english month names (since the keys for the Ext.Date.monthNumbers hash are case sensitive)
3702         return utilDate.monthNumbers[name.substring(0, 1).toUpperCase() + name.substring(1, 3).toLowerCase()];
3703     },
3704
3705     /**
3706      * Checks if the specified format contains hour information
3707      * @param {String} format The format to check
3708      * @return {Boolean} True if the format contains hour information
3709      * @method
3710      */
3711     formatContainsHourInfo : (function(){
3712         var stripEscapeRe = /(\\.)/g,
3713             hourInfoRe = /([gGhHisucUOPZ]|MS)/;
3714         return function(format){
3715             return hourInfoRe.test(format.replace(stripEscapeRe, ''));
3716         };
3717     })(),
3718
3719     /**
3720      * Checks if the specified format contains information about
3721      * anything other than the time.
3722      * @param {String} format The format to check
3723      * @return {Boolean} True if the format contains information about
3724      * date/day information.
3725      * @method
3726      */
3727     formatContainsDateInfo : (function(){
3728         var stripEscapeRe = /(\\.)/g,
3729             dateInfoRe = /([djzmnYycU]|MS)/;
3730
3731         return function(format){
3732             return dateInfoRe.test(format.replace(stripEscapeRe, ''));
3733         };
3734     })(),
3735
3736     /**
3737      * The base format-code to formatting-function hashmap used by the {@link #format} method.
3738      * Formatting functions are strings (or functions which return strings) which
3739      * will return the appropriate value when evaluated in the context of the Date object
3740      * from which the {@link #format} method is called.
3741      * Add to / override these mappings for custom date formatting.
3742      * Note: Ext.Date.format() treats characters as literals if an appropriate mapping cannot be found.
3743      * Example:
3744      * <pre><code>
3745 Ext.Date.formatCodes.x = "Ext.util.Format.leftPad(this.getDate(), 2, '0')";
3746 console.log(Ext.Date.format(new Date(), 'X'); // returns the current day of the month
3747 </code></pre>
3748      * @type Object
3749      */
3750     formatCodes : {
3751         d: "Ext.String.leftPad(this.getDate(), 2, '0')",
3752         D: "Ext.Date.getShortDayName(this.getDay())", // get localised short day name
3753         j: "this.getDate()",
3754         l: "Ext.Date.dayNames[this.getDay()]",
3755         N: "(this.getDay() ? this.getDay() : 7)",
3756         S: "Ext.Date.getSuffix(this)",
3757         w: "this.getDay()",
3758         z: "Ext.Date.getDayOfYear(this)",
3759         W: "Ext.String.leftPad(Ext.Date.getWeekOfYear(this), 2, '0')",
3760         F: "Ext.Date.monthNames[this.getMonth()]",
3761         m: "Ext.String.leftPad(this.getMonth() + 1, 2, '0')",
3762         M: "Ext.Date.getShortMonthName(this.getMonth())", // get localised short month name
3763         n: "(this.getMonth() + 1)",
3764         t: "Ext.Date.getDaysInMonth(this)",
3765         L: "(Ext.Date.isLeapYear(this) ? 1 : 0)",
3766         o: "(this.getFullYear() + (Ext.Date.getWeekOfYear(this) == 1 && this.getMonth() > 0 ? +1 : (Ext.Date.getWeekOfYear(this) >= 52 && this.getMonth() < 11 ? -1 : 0)))",
3767         Y: "Ext.String.leftPad(this.getFullYear(), 4, '0')",
3768         y: "('' + this.getFullYear()).substring(2, 4)",
3769         a: "(this.getHours() < 12 ? 'am' : 'pm')",
3770         A: "(this.getHours() < 12 ? 'AM' : 'PM')",
3771         g: "((this.getHours() % 12) ? this.getHours() % 12 : 12)",
3772         G: "this.getHours()",
3773         h: "Ext.String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0')",
3774         H: "Ext.String.leftPad(this.getHours(), 2, '0')",
3775         i: "Ext.String.leftPad(this.getMinutes(), 2, '0')",
3776         s: "Ext.String.leftPad(this.getSeconds(), 2, '0')",
3777         u: "Ext.String.leftPad(this.getMilliseconds(), 3, '0')",
3778         O: "Ext.Date.getGMTOffset(this)",
3779         P: "Ext.Date.getGMTOffset(this, true)",
3780         T: "Ext.Date.getTimezone(this)",
3781         Z: "(this.getTimezoneOffset() * -60)",
3782
3783         c: function() { // ISO-8601 -- GMT format
3784             for (var c = "Y-m-dTH:i:sP", code = [], i = 0, l = c.length; i < l; ++i) {
3785                 var e = c.charAt(i);
3786                 code.push(e == "T" ? "'T'" : utilDate.getFormatCode(e)); // treat T as a character literal
3787             }
3788             return code.join(" + ");
3789         },
3790         /*
3791         c: function() { // ISO-8601 -- UTC format
3792             return [
3793               "this.getUTCFullYear()", "'-'",
3794               "Ext.util.Format.leftPad(this.getUTCMonth() + 1, 2, '0')", "'-'",
3795               "Ext.util.Format.leftPad(this.getUTCDate(), 2, '0')",
3796               "'T'",
3797               "Ext.util.Format.leftPad(this.getUTCHours(), 2, '0')", "':'",
3798               "Ext.util.Format.leftPad(this.getUTCMinutes(), 2, '0')", "':'",
3799               "Ext.util.Format.leftPad(this.getUTCSeconds(), 2, '0')",
3800               "'Z'"
3801             ].join(" + ");
3802         },
3803         */
3804
3805         U: "Math.round(this.getTime() / 1000)"
3806     },
3807
3808     /**
3809      * Checks if the passed Date parameters will cause a javascript Date "rollover".
3810      * @param {Number} year 4-digit year
3811      * @param {Number} month 1-based month-of-year
3812      * @param {Number} day Day of month
3813      * @param {Number} hour (optional) Hour
3814      * @param {Number} minute (optional) Minute
3815      * @param {Number} second (optional) Second
3816      * @param {Number} millisecond (optional) Millisecond
3817      * @return {Boolean} true if the passed parameters do not cause a Date "rollover", false otherwise.
3818      */
3819     isValid : function(y, m, d, h, i, s, ms) {
3820         // setup defaults
3821         h = h || 0;
3822         i = i || 0;
3823         s = s || 0;
3824         ms = ms || 0;
3825
3826         // Special handling for year < 100
3827         var dt = utilDate.add(new Date(y < 100 ? 100 : y, m - 1, d, h, i, s, ms), utilDate.YEAR, y < 100 ? y - 100 : 0);
3828
3829         return y == dt.getFullYear() &&
3830             m == dt.getMonth() + 1 &&
3831             d == dt.getDate() &&
3832             h == dt.getHours() &&
3833             i == dt.getMinutes() &&
3834             s == dt.getSeconds() &&
3835             ms == dt.getMilliseconds();
3836     },
3837
3838     /**
3839      * Parses the passed string using the specified date format.
3840      * Note that this function expects normal calendar dates, meaning that months are 1-based (i.e. 1 = January).
3841      * The {@link #defaults} hash will be used for any date value (i.e. year, month, day, hour, minute, second or millisecond)
3842      * which cannot be found in the passed string. If a corresponding default date value has not been specified in the {@link #defaults} hash,
3843      * the current date's year, month, day or DST-adjusted zero-hour time value will be used instead.
3844      * Keep in mind that the input date string must precisely match the specified format string
3845      * in order for the parse operation to be successful (failed parse operations return a null value).
3846      * <p>Example:</p><pre><code>
3847 //dt = Fri May 25 2007 (current date)
3848 var dt = new Date();
3849
3850 //dt = Thu May 25 2006 (today&#39;s month/day in 2006)
3851 dt = Ext.Date.parse("2006", "Y");
3852
3853 //dt = Sun Jan 15 2006 (all date parts specified)
3854 dt = Ext.Date.parse("2006-01-15", "Y-m-d");
3855
3856 //dt = Sun Jan 15 2006 15:20:01
3857 dt = Ext.Date.parse("2006-01-15 3:20:01 PM", "Y-m-d g:i:s A");
3858
3859 // attempt to parse Sun Feb 29 2006 03:20:01 in strict mode
3860 dt = Ext.Date.parse("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null
3861 </code></pre>
3862      * @param {String} input The raw date string.
3863      * @param {String} format The expected date string format.
3864      * @param {Boolean} strict (optional) True to validate date strings while parsing (i.e. prevents javascript Date "rollover")
3865                         (defaults to false). Invalid date strings will return null when parsed.
3866      * @return {Date} The parsed Date.
3867      */
3868     parse : function(input, format, strict) {
3869         var p = utilDate.parseFunctions;
3870         if (p[format] == null) {
3871             utilDate.createParser(format);
3872         }
3873         return p[format](input, Ext.isDefined(strict) ? strict : utilDate.useStrict);
3874     },
3875
3876     // Backwards compat
3877     parseDate: function(input, format, strict){
3878         return utilDate.parse(input, format, strict);
3879     },
3880
3881
3882     // private
3883     getFormatCode : function(character) {
3884         var f = utilDate.formatCodes[character];
3885
3886         if (f) {
3887           f = typeof f == 'function'? f() : f;
3888           utilDate.formatCodes[character] = f; // reassign function result to prevent repeated execution
3889         }
3890
3891         // note: unknown characters are treated as literals
3892         return f || ("'" + Ext.String.escape(character) + "'");
3893     },
3894
3895     // private
3896     createFormat : function(format) {
3897         var code = [],
3898             special = false,
3899             ch = '';
3900
3901         for (var i = 0; i < format.length; ++i) {
3902             ch = format.charAt(i);
3903             if (!special && ch == "\\") {
3904                 special = true;
3905             } else if (special) {
3906                 special = false;
3907                 code.push("'" + Ext.String.escape(ch) + "'");
3908             } else {
3909                 code.push(utilDate.getFormatCode(ch));
3910             }
3911         }
3912         utilDate.formatFunctions[format] = Ext.functionFactory("return " + code.join('+'));
3913     },
3914
3915     // private
3916     createParser : (function() {
3917         var code = [
3918             "var dt, y, m, d, h, i, s, ms, o, z, zz, u, v,",
3919                 "def = Ext.Date.defaults,",
3920                 "results = String(input).match(Ext.Date.parseRegexes[{0}]);", // either null, or an array of matched strings
3921
3922             "if(results){",
3923                 "{1}",
3924
3925                 "if(u != null){", // i.e. unix time is defined
3926                     "v = new Date(u * 1000);", // give top priority to UNIX time
3927                 "}else{",
3928                     // create Date object representing midnight of the current day;
3929                     // this will provide us with our date defaults
3930                     // (note: clearTime() handles Daylight Saving Time automatically)
3931                     "dt = Ext.Date.clearTime(new Date);",
3932
3933                     // date calculations (note: these calculations create a dependency on Ext.Number.from())
3934                     "y = Ext.Number.from(y, Ext.Number.from(def.y, dt.getFullYear()));",
3935                     "m = Ext.Number.from(m, Ext.Number.from(def.m - 1, dt.getMonth()));",
3936                     "d = Ext.Number.from(d, Ext.Number.from(def.d, dt.getDate()));",
3937
3938                     // time calculations (note: these calculations create a dependency on Ext.Number.from())
3939                     "h  = Ext.Number.from(h, Ext.Number.from(def.h, dt.getHours()));",
3940                     "i  = Ext.Number.from(i, Ext.Number.from(def.i, dt.getMinutes()));",
3941                     "s  = Ext.Number.from(s, Ext.Number.from(def.s, dt.getSeconds()));",
3942                     "ms = Ext.Number.from(ms, Ext.Number.from(def.ms, dt.getMilliseconds()));",
3943
3944                     "if(z >= 0 && y >= 0){",
3945                         // both the year and zero-based day of year are defined and >= 0.
3946                         // these 2 values alone provide sufficient info to create a full date object
3947
3948                         // create Date object representing January 1st for the given year
3949                         // handle years < 100 appropriately
3950                         "v = Ext.Date.add(new Date(y < 100 ? 100 : y, 0, 1, h, i, s, ms), Ext.Date.YEAR, y < 100 ? y - 100 : 0);",
3951
3952                         // then add day of year, checking for Date "rollover" if necessary
3953                         "v = !strict? v : (strict === true && (z <= 364 || (Ext.Date.isLeapYear(v) && z <= 365))? Ext.Date.add(v, Ext.Date.DAY, z) : null);",
3954                     "}else if(strict === true && !Ext.Date.isValid(y, m + 1, d, h, i, s, ms)){", // check for Date "rollover"
3955                         "v = null;", // invalid date, so return null
3956                     "}else{",
3957                         // plain old Date object
3958                         // handle years < 100 properly
3959                         "v = Ext.Date.add(new Date(y < 100 ? 100 : y, m, d, h, i, s, ms), Ext.Date.YEAR, y < 100 ? y - 100 : 0);",
3960                     "}",
3961                 "}",
3962             "}",
3963
3964             "if(v){",
3965                 // favour UTC offset over GMT offset
3966                 "if(zz != null){",
3967                     // reset to UTC, then add offset
3968                     "v = Ext.Date.add(v, Ext.Date.SECOND, -v.getTimezoneOffset() * 60 - zz);",
3969                 "}else if(o){",
3970                     // reset to GMT, then add offset
3971                     "v = Ext.Date.add(v, Ext.Date.MINUTE, -v.getTimezoneOffset() + (sn == '+'? -1 : 1) * (hr * 60 + mn));",
3972                 "}",
3973             "}",
3974
3975             "return v;"
3976         ].join('\n');
3977
3978         return function(format) {
3979             var regexNum = utilDate.parseRegexes.length,
3980                 currentGroup = 1,
3981                 calc = [],
3982                 regex = [],
3983                 special = false,
3984                 ch = "";
3985
3986             for (var i = 0; i < format.length; ++i) {
3987                 ch = format.charAt(i);
3988                 if (!special && ch == "\\") {
3989                     special = true;
3990                 } else if (special) {
3991                     special = false;
3992                     regex.push(Ext.String.escape(ch));
3993                 } else {
3994                     var obj = utilDate.formatCodeToRegex(ch, currentGroup);
3995                     currentGroup += obj.g;
3996                     regex.push(obj.s);
3997                     if (obj.g && obj.c) {
3998                         calc.push(obj.c);
3999                     }
4000                 }
4001             }
4002
4003             utilDate.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$", 'i');
4004             utilDate.parseFunctions[format] = Ext.functionFactory("input", "strict", xf(code, regexNum, calc.join('')));
4005         };
4006     })(),
4007
4008     // private
4009     parseCodes : {
4010         /*
4011          * Notes:
4012          * g = {Number} calculation group (0 or 1. only group 1 contributes to date calculations.)
4013          * c = {String} calculation method (required for group 1. null for group 0. {0} = currentGroup - position in regex result array)
4014          * s = {String} regex pattern. all matches are stored in results[], and are accessible by the calculation mapped to 'c'
4015          */
4016         d: {
4017             g:1,
4018             c:"d = parseInt(results[{0}], 10);\n",
4019             s:"(\\d{2})" // day of month with leading zeroes (01 - 31)
4020         },
4021         j: {
4022             g:1,
4023             c:"d = parseInt(results[{0}], 10);\n",
4024             s:"(\\d{1,2})" // day of month without leading zeroes (1 - 31)
4025         },
4026         D: function() {
4027             for (var a = [], i = 0; i < 7; a.push(utilDate.getShortDayName(i)), ++i); // get localised short day names
4028             return {
4029                 g:0,
4030                 c:null,
4031                 s:"(?:" + a.join("|") +")"
4032             };
4033         },
4034         l: function() {
4035             return {
4036                 g:0,
4037                 c:null,
4038                 s:"(?:" + utilDate.dayNames.join("|") + ")"
4039             };
4040         },
4041         N: {
4042             g:0,
4043             c:null,
4044             s:"[1-7]" // ISO-8601 day number (1 (monday) - 7 (sunday))
4045         },
4046         S: {
4047             g:0,
4048             c:null,
4049             s:"(?:st|nd|rd|th)"
4050         },
4051         w: {
4052             g:0,
4053             c:null,
4054             s:"[0-6]" // javascript day number (0 (sunday) - 6 (saturday))
4055         },
4056         z: {
4057             g:1,
4058             c:"z = parseInt(results[{0}], 10);\n",
4059             s:"(\\d{1,3})" // day of the year (0 - 364 (365 in leap years))
4060         },
4061         W: {
4062             g:0,
4063             c:null,
4064             s:"(?:\\d{2})" // ISO-8601 week number (with leading zero)
4065         },
4066         F: function() {
4067             return {
4068                 g:1,
4069                 c:"m = parseInt(Ext.Date.getMonthNumber(results[{0}]), 10);\n", // get localised month number
4070                 s:"(" + utilDate.monthNames.join("|") + ")"
4071             };
4072         },
4073         M: function() {
4074             for (var a = [], i = 0; i < 12; a.push(utilDate.getShortMonthName(i)), ++i); // get localised short month names
4075             return Ext.applyIf({
4076                 s:"(" + a.join("|") + ")"
4077             }, utilDate.formatCodeToRegex("F"));
4078         },
4079         m: {
4080             g:1,
4081             c:"m = parseInt(results[{0}], 10) - 1;\n",
4082             s:"(\\d{2})" // month number with leading zeros (01 - 12)
4083         },
4084         n: {
4085             g:1,
4086             c:"m = parseInt(results[{0}], 10) - 1;\n",
4087             s:"(\\d{1,2})" // month number without leading zeros (1 - 12)
4088         },
4089         t: {
4090             g:0,
4091             c:null,
4092             s:"(?:\\d{2})" // no. of days in the month (28 - 31)
4093         },
4094         L: {
4095             g:0,
4096             c:null,
4097             s:"(?:1|0)"
4098         },
4099         o: function() {
4100             return utilDate.formatCodeToRegex("Y");
4101         },
4102         Y: {
4103             g:1,
4104             c:"y = parseInt(results[{0}], 10);\n",
4105             s:"(\\d{4})" // 4-digit year
4106         },
4107         y: {
4108             g:1,
4109             c:"var ty = parseInt(results[{0}], 10);\n"
4110                 + "y = ty > Ext.Date.y2kYear ? 1900 + ty : 2000 + ty;\n", // 2-digit year
4111             s:"(\\d{1,2})"
4112         },
4113         /*
4114          * In the am/pm parsing routines, we allow both upper and lower case
4115          * even though it doesn't exactly match the spec. It gives much more flexibility
4116          * in being able to specify case insensitive regexes.
4117          */
4118         a: {
4119             g:1,
4120             c:"if (/(am)/i.test(results[{0}])) {\n"
4121                 + "if (!h || h == 12) { h = 0; }\n"
4122                 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
4123             s:"(am|pm|AM|PM)"
4124         },
4125         A: {
4126             g:1,
4127             c:"if (/(am)/i.test(results[{0}])) {\n"
4128                 + "if (!h || h == 12) { h = 0; }\n"
4129                 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
4130             s:"(AM|PM|am|pm)"
4131         },
4132         g: function() {
4133             return utilDate.formatCodeToRegex("G");
4134         },
4135         G: {
4136             g:1,
4137             c:"h = parseInt(results[{0}], 10);\n",
4138             s:"(\\d{1,2})" // 24-hr format of an hour without leading zeroes (0 - 23)
4139         },
4140         h: function() {
4141             return utilDate.formatCodeToRegex("H");
4142         },
4143         H: {
4144             g:1,
4145             c:"h = parseInt(results[{0}], 10);\n",
4146             s:"(\\d{2})" //  24-hr format of an hour with leading zeroes (00 - 23)
4147         },
4148         i: {
4149             g:1,
4150             c:"i = parseInt(results[{0}], 10);\n",
4151             s:"(\\d{2})" // minutes with leading zeros (00 - 59)
4152         },
4153         s: {
4154             g:1,
4155             c:"s = parseInt(results[{0}], 10);\n",
4156             s:"(\\d{2})" // seconds with leading zeros (00 - 59)
4157         },
4158         u: {
4159             g:1,
4160             c:"ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",
4161             s:"(\\d+)" // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
4162         },
4163         O: {
4164             g:1,
4165             c:[
4166                 "o = results[{0}];",
4167                 "var sn = o.substring(0,1),", // get + / - sign
4168                     "hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
4169                     "mn = o.substring(3,5) % 60;", // get minutes
4170                 "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
4171             ].join("\n"),
4172             s: "([+\-]\\d{4})" // GMT offset in hrs and mins
4173         },
4174         P: {
4175             g:1,
4176             c:[
4177                 "o = results[{0}];",
4178                 "var sn = o.substring(0,1),", // get + / - sign
4179                     "hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
4180                     "mn = o.substring(4,6) % 60;", // get minutes
4181                 "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
4182             ].join("\n"),
4183             s: "([+\-]\\d{2}:\\d{2})" // GMT offset in hrs and mins (with colon separator)
4184         },
4185         T: {
4186             g:0,
4187             c:null,
4188             s:"[A-Z]{1,4}" // timezone abbrev. may be between 1 - 4 chars
4189         },
4190         Z: {
4191             g:1,
4192             c:"zz = results[{0}] * 1;\n" // -43200 <= UTC offset <= 50400
4193                   + "zz = (-43200 <= zz && zz <= 50400)? zz : null;\n",
4194             s:"([+\-]?\\d{1,5})" // leading '+' sign is optional for UTC offset
4195         },
4196         c: function() {
4197             var calc = [],
4198                 arr = [
4199                     utilDate.formatCodeToRegex("Y", 1), // year
4200                     utilDate.formatCodeToRegex("m", 2), // month
4201                     utilDate.formatCodeToRegex("d", 3), // day
4202                     utilDate.formatCodeToRegex("h", 4), // hour
4203                     utilDate.formatCodeToRegex("i", 5), // minute
4204                     utilDate.formatCodeToRegex("s", 6), // second
4205                     {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)
4206                     {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
4207                         "if(results[8]) {", // timezone specified
4208                             "if(results[8] == 'Z'){",
4209                                 "zz = 0;", // UTC
4210                             "}else if (results[8].indexOf(':') > -1){",
4211                                 utilDate.formatCodeToRegex("P", 8).c, // timezone offset with colon separator
4212                             "}else{",
4213                                 utilDate.formatCodeToRegex("O", 8).c, // timezone offset without colon separator
4214                             "}",
4215                         "}"
4216                     ].join('\n')}
4217                 ];
4218
4219             for (var i = 0, l = arr.length; i < l; ++i) {
4220                 calc.push(arr[i].c);
4221             }
4222
4223             return {
4224                 g:1,
4225                 c:calc.join(""),
4226                 s:[
4227                     arr[0].s, // year (required)
4228                     "(?:", "-", arr[1].s, // month (optional)
4229                         "(?:", "-", arr[2].s, // day (optional)
4230                             "(?:",
4231                                 "(?:T| )?", // time delimiter -- either a "T" or a single blank space
4232                                 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
4233                                 "(?::", arr[5].s, ")?", // seconds (optional)
4234                                 "(?:(?:\\.|,)(\\d+))?", // decimal fraction of a second (e.g. ",12345" or ".98765") (optional)
4235                                 "(Z|(?:[-+]\\d{2}(?::)?\\d{2}))?", // "Z" (UTC) or "-0530" (UTC offset without colon delimiter) or "+08:00" (UTC offset with colon delimiter) (optional)
4236                             ")?",
4237                         ")?",
4238                     ")?"
4239                 ].join("")
4240             };
4241         },
4242         U: {
4243             g:1,
4244             c:"u = parseInt(results[{0}], 10);\n",
4245             s:"(-?\\d+)" // leading minus sign indicates seconds before UNIX epoch
4246         }
4247     },
4248
4249     //Old Ext.Date prototype methods.
4250     // private
4251     dateFormat: function(date, format) {
4252         return utilDate.format(date, format);
4253     },
4254
4255     /**
4256      * Formats a date given the supplied format string.
4257      * @param {Date} date The date to format
4258      * @param {String} format The format string
4259      * @return {String} The formatted date
4260      */
4261     format: function(date, format) {
4262         if (utilDate.formatFunctions[format] == null) {
4263             utilDate.createFormat(format);
4264         }
4265         var result = utilDate.formatFunctions[format].call(date);
4266         return result + '';
4267     },
4268
4269     /**
4270      * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
4271      *
4272      * Note: The date string returned by the javascript Date object's toString() method varies
4273      * between browsers (e.g. FF vs IE) and system region settings (e.g. IE in Asia vs IE in America).
4274      * For a given date string e.g. "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)",
4275      * getTimezone() first tries to get the timezone abbreviation from between a pair of parentheses
4276      * (which may or may not be present), failing which it proceeds to get the timezone abbreviation
4277      * from the GMT offset portion of the date string.
4278      * @param {Date} date The date
4279      * @return {String} The abbreviated timezone name (e.g. 'CST', 'PDT', 'EDT', 'MPST' ...).
4280      */
4281     getTimezone : function(date) {
4282         // the following list shows the differences between date strings from different browsers on a WinXP SP2 machine from an Asian locale:
4283         //
4284         // Opera  : "Thu, 25 Oct 2007 22:53:45 GMT+0800" -- shortest (weirdest) date string of the lot
4285         // 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)
4286         // FF     : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone
4287         // IE     : "Thu Oct 25 22:54:35 UTC+0800 2007" -- (Asian system setting) look for 3-4 letter timezone abbrev
4288         // IE     : "Thu Oct 25 17:06:37 PDT 2007" -- (American system setting) look for 3-4 letter timezone abbrev
4289         //
4290         // this crazy regex attempts to guess the correct timezone abbreviation despite these differences.
4291         // step 1: (?:\((.*)\) -- find timezone in parentheses
4292         // 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
4293         // step 3: remove all non uppercase characters found in step 1 and 2
4294         return date.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, "");
4295     },
4296
4297     /**
4298      * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
4299      * @param {Date} date The date
4300      * @param {Boolean} colon (optional) true to separate the hours and minutes with a colon (defaults to false).
4301      * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600').
4302      */
4303     getGMTOffset : function(date, colon) {
4304         var offset = date.getTimezoneOffset();
4305         return (offset > 0 ? "-" : "+")
4306             + Ext.String.leftPad(Math.floor(Math.abs(offset) / 60), 2, "0")
4307             + (colon ? ":" : "")
4308             + Ext.String.leftPad(Math.abs(offset % 60), 2, "0");
4309     },
4310
4311     /**
4312      * Get the numeric day number of the year, adjusted for leap year.
4313      * @param {Date} date The date
4314      * @return {Number} 0 to 364 (365 in leap years).
4315      */
4316     getDayOfYear: function(date) {
4317         var num = 0,
4318             d = Ext.Date.clone(date),
4319             m = date.getMonth(),
4320             i;
4321
4322         for (i = 0, d.setDate(1), d.setMonth(0); i < m; d.setMonth(++i)) {
4323             num += utilDate.getDaysInMonth(d);
4324         }
4325         return num + date.getDate() - 1;
4326     },
4327
4328     /**
4329      * Get the numeric ISO-8601 week number of the year.
4330      * (equivalent to the format specifier 'W', but without a leading zero).
4331      * @param {Date} date The date
4332      * @return {Number} 1 to 53
4333      * @method
4334      */
4335     getWeekOfYear : (function() {
4336         // adapted from http://www.merlyn.demon.co.uk/weekcalc.htm
4337         var ms1d = 864e5, // milliseconds in a day
4338             ms7d = 7 * ms1d; // milliseconds in a week
4339
4340         return function(date) { // return a closure so constants get calculated only once
4341             var DC3 = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate() + 3) / ms1d, // an Absolute Day Number
4342                 AWN = Math.floor(DC3 / 7), // an Absolute Week Number
4343                 Wyr = new Date(AWN * ms7d).getUTCFullYear();
4344
4345             return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
4346         };
4347     })(),
4348
4349     /**
4350      * Checks if the current date falls within a leap year.
4351      * @param {Date} date The date
4352      * @return {Boolean} True if the current date falls within a leap year, false otherwise.
4353      */
4354     isLeapYear : function(date) {
4355         var year = date.getFullYear();
4356         return !!((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
4357     },
4358
4359     /**
4360      * Get the first day of the current month, adjusted for leap year.  The returned value
4361      * is the numeric day index within the week (0-6) which can be used in conjunction with
4362      * the {@link #monthNames} array to retrieve the textual day name.
4363      * Example:
4364      * <pre><code>
4365 var dt = new Date('1/10/2007'),
4366     firstDay = Ext.Date.getFirstDayOfMonth(dt);
4367 console.log(Ext.Date.dayNames[firstDay]); //output: 'Monday'
4368      * </code></pre>
4369      * @param {Date} date The date
4370      * @return {Number} The day number (0-6).
4371      */
4372     getFirstDayOfMonth : function(date) {
4373         var day = (date.getDay() - (date.getDate() - 1)) % 7;
4374         return (day < 0) ? (day + 7) : day;
4375     },
4376
4377     /**
4378      * Get the last day of the current month, adjusted for leap year.  The returned value
4379      * is the numeric day index within the week (0-6) which can be used in conjunction with
4380      * the {@link #monthNames} array to retrieve the textual day name.
4381      * Example:
4382      * <pre><code>
4383 var dt = new Date('1/10/2007'),
4384     lastDay = Ext.Date.getLastDayOfMonth(dt);
4385 console.log(Ext.Date.dayNames[lastDay]); //output: 'Wednesday'
4386      * </code></pre>
4387      * @param {Date} date The date
4388      * @return {Number} The day number (0-6).
4389      */
4390     getLastDayOfMonth : function(date) {
4391         return utilDate.getLastDateOfMonth(date).getDay();
4392     },
4393
4394
4395     /**
4396      * Get the date of the first day of the month in which this date resides.
4397      * @param {Date} date The date
4398      * @return {Date}
4399      */
4400     getFirstDateOfMonth : function(date) {
4401         return new Date(date.getFullYear(), date.getMonth(), 1);
4402     },
4403
4404     /**
4405      * Get the date of the last day of the month in which this date resides.
4406      * @param {Date} date The date
4407      * @return {Date}
4408      */
4409     getLastDateOfMonth : function(date) {
4410         return new Date(date.getFullYear(), date.getMonth(), utilDate.getDaysInMonth(date));
4411     },
4412
4413     /**
4414      * Get the number of days in the current month, adjusted for leap year.
4415      * @param {Date} date The date
4416      * @return {Number} The number of days in the month.
4417      * @method
4418      */
4419     getDaysInMonth: (function() {
4420         var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
4421
4422         return function(date) { // return a closure for efficiency
4423             var m = date.getMonth();
4424
4425             return m == 1 && utilDate.isLeapYear(date) ? 29 : daysInMonth[m];
4426         };
4427     })(),
4428
4429     /**
4430      * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
4431      * @param {Date} date The date
4432      * @return {String} 'st, 'nd', 'rd' or 'th'.
4433      */
4434     getSuffix : function(date) {
4435         switch (date.getDate()) {
4436             case 1:
4437             case 21:
4438             case 31:
4439                 return "st";
4440             case 2:
4441             case 22:
4442                 return "nd";
4443             case 3:
4444             case 23:
4445                 return "rd";
4446             default:
4447                 return "th";
4448         }
4449     },
4450
4451     /**
4452      * Creates and returns a new Date instance with the exact same date value as the called instance.
4453      * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
4454      * variable will also be changed.  When the intention is to create a new variable that will not
4455      * modify the original instance, you should create a clone.
4456      *
4457      * Example of correctly cloning a date:
4458      * <pre><code>
4459 //wrong way:
4460 var orig = new Date('10/1/2006');
4461 var copy = orig;
4462 copy.setDate(5);
4463 console.log(orig);  //returns 'Thu Oct 05 2006'!
4464
4465 //correct way:
4466 var orig = new Date('10/1/2006'),
4467     copy = Ext.Date.clone(orig);
4468 copy.setDate(5);
4469 console.log(orig);  //returns 'Thu Oct 01 2006'
4470      * </code></pre>
4471      * @param {Date} date The date
4472      * @return {Date} The new Date instance.
4473      */
4474     clone : function(date) {
4475         return new Date(date.getTime());
4476     },
4477
4478     /**
4479      * Checks if the current date is affected by Daylight Saving Time (DST).
4480      * @param {Date} date The date
4481      * @return {Boolean} True if the current date is affected by DST.
4482      */
4483     isDST : function(date) {
4484         // adapted from http://sencha.com/forum/showthread.php?p=247172#post247172
4485         // courtesy of @geoffrey.mcgill
4486         return new Date(date.getFullYear(), 0, 1).getTimezoneOffset() != date.getTimezoneOffset();
4487     },
4488
4489     /**
4490      * Attempts to clear all time information from this Date by setting the time to midnight of the same day,
4491      * automatically adjusting for Daylight Saving Time (DST) where applicable.
4492      * (note: DST timezone information for the browser's host operating system is assumed to be up-to-date)
4493      * @param {Date} date The date
4494      * @param {Boolean} clone true to create a clone of this date, clear the time and return it (defaults to false).
4495      * @return {Date} this or the clone.
4496      */
4497     clearTime : function(date, clone) {
4498         if (clone) {
4499             return Ext.Date.clearTime(Ext.Date.clone(date));
4500         }
4501
4502         // get current date before clearing time
4503         var d = date.getDate();
4504
4505         // clear time
4506         date.setHours(0);
4507         date.setMinutes(0);
4508         date.setSeconds(0);
4509         date.setMilliseconds(0);
4510
4511         if (date.getDate() != d) { // account for DST (i.e. day of month changed when setting hour = 0)
4512             // note: DST adjustments are assumed to occur in multiples of 1 hour (this is almost always the case)
4513             // refer to http://www.timeanddate.com/time/aboutdst.html for the (rare) exceptions to this rule
4514
4515             // increment hour until cloned date == current date
4516             for (var hr = 1, c = utilDate.add(date, Ext.Date.HOUR, hr); c.getDate() != d; hr++, c = utilDate.add(date, Ext.Date.HOUR, hr));
4517
4518             date.setDate(d);
4519             date.setHours(c.getHours());
4520         }
4521
4522         return date;
4523     },
4524
4525     /**
4526      * Provides a convenient method for performing basic date arithmetic. This method
4527      * does not modify the Date instance being called - it creates and returns
4528      * a new Date instance containing the resulting date value.
4529      *
4530      * Examples:
4531      * <pre><code>
4532 // Basic usage:
4533 var dt = Ext.Date.add(new Date('10/29/2006'), Ext.Date.DAY, 5);
4534 console.log(dt); //returns 'Fri Nov 03 2006 00:00:00'
4535
4536 // Negative values will be subtracted:
4537 var dt2 = Ext.Date.add(new Date('10/1/2006'), Ext.Date.DAY, -5);
4538 console.log(dt2); //returns 'Tue Sep 26 2006 00:00:00'
4539
4540      * </code></pre>
4541      *
4542      * @param {Date} date The date to modify
4543      * @param {String} interval A valid date interval enum value.
4544      * @param {Number} value The amount to add to the current date.
4545      * @return {Date} The new Date instance.
4546      */
4547     add : function(date, interval, value) {
4548         var d = Ext.Date.clone(date),
4549             Date = Ext.Date;
4550         if (!interval || value === 0) return d;
4551
4552         switch(interval.toLowerCase()) {
4553             case Ext.Date.MILLI:
4554                 d.setMilliseconds(d.getMilliseconds() + value);
4555                 break;
4556             case Ext.Date.SECOND:
4557                 d.setSeconds(d.getSeconds() + value);
4558                 break;
4559             case Ext.Date.MINUTE:
4560                 d.setMinutes(d.getMinutes() + value);
4561                 break;
4562             case Ext.Date.HOUR:
4563                 d.setHours(d.getHours() + value);
4564                 break;
4565             case Ext.Date.DAY:
4566                 d.setDate(d.getDate() + value);
4567                 break;
4568             case Ext.Date.MONTH:
4569                 var day = date.getDate();
4570                 if (day > 28) {
4571                     day = Math.min(day, Ext.Date.getLastDateOfMonth(Ext.Date.add(Ext.Date.getFirstDateOfMonth(date), 'mo', value)).getDate());
4572                 }
4573                 d.setDate(day);
4574                 d.setMonth(date.getMonth() + value);
4575                 break;
4576             case Ext.Date.YEAR:
4577                 d.setFullYear(date.getFullYear() + value);
4578                 break;
4579         }
4580         return d;
4581     },
4582
4583     /**
4584      * Checks if a date falls on or between the given start and end dates.
4585      * @param {Date} date The date to check
4586      * @param {Date} start Start date
4587      * @param {Date} end End date
4588      * @return {Boolean} true if this date falls on or between the given start and end dates.
4589      */
4590     between : function(date, start, end) {
4591         var t = date.getTime();
4592         return start.getTime() <= t && t <= end.getTime();
4593     },
4594
4595     //Maintains compatibility with old static and prototype window.Date methods.
4596     compat: function() {
4597         var nativeDate = window.Date,
4598             p, u,
4599             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'],
4600             proto = ['dateFormat', 'format', 'getTimezone', 'getGMTOffset', 'getDayOfYear', 'getWeekOfYear', 'isLeapYear', 'getFirstDayOfMonth', 'getLastDayOfMonth', 'getDaysInMonth', 'getSuffix', 'clone', 'isDST', 'clearTime', 'add', 'between'];
4601
4602         //Append statics
4603         Ext.Array.forEach(statics, function(s) {
4604             nativeDate[s] = utilDate[s];
4605         });
4606
4607         //Append to prototype
4608         Ext.Array.forEach(proto, function(s) {
4609             nativeDate.prototype[s] = function() {
4610                 var args = Array.prototype.slice.call(arguments);
4611                 args.unshift(this);
4612                 return utilDate[s].apply(utilDate, args);
4613             };
4614         });
4615     }
4616 };
4617
4618 var utilDate = Ext.Date;
4619
4620 })();
4621
4622 /**
4623  * @author Jacky Nguyen <jacky@sencha.com>
4624  * @docauthor Jacky Nguyen <jacky@sencha.com>
4625  * @class Ext.Base
4626  *
4627  * The root of all classes created with {@link Ext#define}.
4628  *
4629  * Ext.Base is the building block of all Ext classes. All classes in Ext inherit from Ext.Base.
4630  * All prototype and static members of this class are inherited by all other classes.
4631  */
4632 (function(flexSetter) {
4633
4634 var Base = Ext.Base = function() {};
4635     Base.prototype = {
4636         $className: 'Ext.Base',
4637
4638         $class: Base,
4639
4640         /**
4641          * Get the reference to the current class from which this object was instantiated. Unlike {@link Ext.Base#statics},
4642          * `this.self` is scope-dependent and it's meant to be used for dynamic inheritance. See {@link Ext.Base#statics}
4643          * for a detailed comparison
4644          *
4645          *     Ext.define('My.Cat', {
4646          *         statics: {
4647          *             speciesName: 'Cat' // My.Cat.speciesName = 'Cat'
4648          *         },
4649          *
4650          *         constructor: function() {
4651          *             alert(this.self.speciesName); / dependent on 'this'
4652          *
4653          *             return this;
4654          *         },
4655          *
4656          *         clone: function() {
4657          *             return new this.self();
4658          *         }
4659          *     });
4660          *
4661          *
4662          *     Ext.define('My.SnowLeopard', {
4663          *         extend: 'My.Cat',
4664          *         statics: {
4665          *             speciesName: 'Snow Leopard'         // My.SnowLeopard.speciesName = 'Snow Leopard'
4666          *         }
4667          *     });
4668          *
4669          *     var cat = new My.Cat();                     // alerts 'Cat'
4670          *     var snowLeopard = new My.SnowLeopard();     // alerts 'Snow Leopard'
4671          *
4672          *     var clone = snowLeopard.clone();
4673          *     alert(Ext.getClassName(clone));             // alerts 'My.SnowLeopard'
4674          *
4675          * @type Ext.Class
4676          * @protected
4677          */
4678         self: Base,
4679
4680         // Default constructor, simply returns `this`
4681         constructor: function() {
4682             return this;
4683         },
4684
4685         //<feature classSystem.config>
4686         /**
4687          * Initialize configuration for this class. a typical example:
4688          *
4689          *     Ext.define('My.awesome.Class', {
4690          *         // The default config
4691          *         config: {
4692          *             name: 'Awesome',
4693          *             isAwesome: true
4694          *         },
4695          *
4696          *         constructor: function(config) {
4697          *             this.initConfig(config);
4698          *
4699          *             return this;
4700          *         }
4701          *     });
4702          *
4703          *     var awesome = new My.awesome.Class({
4704          *         name: 'Super Awesome'
4705          *     });
4706          *
4707          *     alert(awesome.getName()); // 'Super Awesome'
4708          *
4709          * @protected
4710          * @param {Object} config
4711          * @return {Object} mixins The mixin prototypes as key - value pairs
4712          */
4713         initConfig: function(config) {
4714             if (!this.$configInited) {
4715                 this.config = Ext.Object.merge({}, this.config || {}, config || {});
4716
4717                 this.applyConfig(this.config);
4718
4719                 this.$configInited = true;
4720             }
4721
4722             return this;
4723         },
4724
4725         /**
4726          * @private
4727          */
4728         setConfig: function(config) {
4729             this.applyConfig(config || {});
4730
4731             return this;
4732         },
4733
4734         /**
4735          * @private
4736          */
4737         applyConfig: flexSetter(function(name, value) {
4738             var setter = 'set' + Ext.String.capitalize(name);
4739
4740             if (typeof this[setter] === 'function') {
4741                 this[setter].call(this, value);
4742             }
4743
4744             return this;
4745         }),
4746         //</feature>
4747
4748         /**
4749          * Call the parent's overridden method. For example:
4750          *
4751          *     Ext.define('My.own.A', {
4752          *         constructor: function(test) {
4753          *             alert(test);
4754          *         }
4755          *     });
4756          *
4757          *     Ext.define('My.own.B', {
4758          *         extend: 'My.own.A',
4759          *
4760          *         constructor: function(test) {
4761          *             alert(test);
4762          *
4763          *             this.callParent([test + 1]);
4764          *         }
4765          *     });
4766          *
4767          *     Ext.define('My.own.C', {
4768          *         extend: 'My.own.B',
4769          *
4770          *         constructor: function() {
4771          *             alert("Going to call parent's overriden constructor...");
4772          *
4773          *             this.callParent(arguments);
4774          *         }
4775          *     });
4776          *
4777          *     var a = new My.own.A(1); // alerts '1'
4778          *     var b = new My.own.B(1); // alerts '1', then alerts '2'
4779          *     var c = new My.own.C(2); // alerts "Going to call parent's overriden constructor..."
4780          *                              // alerts '2', then alerts '3'
4781          *
4782          * @protected
4783          * @param {Array/Arguments} args The arguments, either an array or the `arguments` object
4784          * from the current method, for example: `this.callParent(arguments)`
4785          * @return {Object} Returns the result from the superclass' method
4786          */
4787         callParent: function(args) {
4788             var method = this.callParent.caller,
4789                 parentClass, methodName;
4790
4791             if (!method.$owner) {
4792
4793                 method = method.caller;
4794             }
4795
4796             parentClass = method.$owner.superclass;
4797             methodName = method.$name;
4798
4799
4800             return parentClass[methodName].apply(this, args || []);
4801         },
4802
4803
4804         /**
4805          * Get the reference to the class from which this object was instantiated. Note that unlike {@link Ext.Base#self},
4806          * `this.statics()` is scope-independent and it always returns the class from which it was called, regardless of what
4807          * `this` points to during run-time
4808          *
4809          *     Ext.define('My.Cat', {
4810          *         statics: {
4811          *             totalCreated: 0,
4812          *             speciesName: 'Cat' // My.Cat.speciesName = 'Cat'
4813          *         },
4814          *
4815          *         constructor: function() {
4816          *             var statics = this.statics();
4817          *
4818          *             alert(statics.speciesName);     // always equals to 'Cat' no matter what 'this' refers to
4819          *                                             // equivalent to: My.Cat.speciesName
4820          *
4821          *             alert(this.self.speciesName);   // dependent on 'this'
4822          *
4823          *             statics.totalCreated++;
4824          *
4825          *             return this;
4826          *         },
4827          *
4828          *         clone: function() {
4829          *             var cloned = new this.self;                      // dependent on 'this'
4830          *
4831          *             cloned.groupName = this.statics().speciesName;   // equivalent to: My.Cat.speciesName
4832          *
4833          *             return cloned;
4834          *         }
4835          *     });
4836          *
4837          *
4838          *     Ext.define('My.SnowLeopard', {
4839          *         extend: 'My.Cat',
4840          *
4841          *         statics: {
4842          *             speciesName: 'Snow Leopard'     // My.SnowLeopard.speciesName = 'Snow Leopard'
4843          *         },
4844          *
4845          *         constructor: function() {
4846          *             this.callParent();
4847          *         }
4848          *     });
4849          *
4850          *     var cat = new My.Cat();                 // alerts 'Cat', then alerts 'Cat'
4851          *
4852          *     var snowLeopard = new My.SnowLeopard(); // alerts 'Cat', then alerts 'Snow Leopard'
4853          *
4854          *     var clone = snowLeopard.clone();
4855          *     alert(Ext.getClassName(clone));         // alerts 'My.SnowLeopard'
4856          *     alert(clone.groupName);                 // alerts 'Cat'
4857          *
4858          *     alert(My.Cat.totalCreated);             // alerts 3
4859          *
4860          * @protected
4861          * @return {Ext.Class}
4862          */
4863         statics: function() {
4864             var method = this.statics.caller,
4865                 self = this.self;
4866
4867             if (!method) {
4868                 return self;
4869             }
4870
4871             return method.$owner;
4872         },
4873
4874         /**
4875          * Call the original method that was previously overridden with {@link Ext.Base#override}
4876          *
4877          *     Ext.define('My.Cat', {
4878          *         constructor: function() {
4879          *             alert("I'm a cat!");
4880          *
4881          *             return this;
4882          *         }
4883          *     });
4884          *
4885          *     My.Cat.override({
4886          *         constructor: function() {
4887          *             alert("I'm going to be a cat!");
4888          *
4889          *             var instance = this.callOverridden();
4890          *
4891          *             alert("Meeeeoooowwww");
4892          *
4893          *             return instance;
4894          *         }
4895          *     });
4896          *
4897          *     var kitty = new My.Cat(); // alerts "I'm going to be a cat!"
4898          *                               // alerts "I'm a cat!"
4899          *                               // alerts "Meeeeoooowwww"
4900          *
4901          * @param {Array/Arguments} args The arguments, either an array or the `arguments` object
4902          * @return {Object} Returns the result after calling the overridden method
4903          * @protected
4904          */
4905         callOverridden: function(args) {
4906             var method = this.callOverridden.caller;
4907
4908
4909             return method.$previous.apply(this, args || []);
4910         },
4911
4912         destroy: function() {}
4913     };
4914
4915     // These static properties will be copied to every newly created class with {@link Ext#define}
4916     Ext.apply(Ext.Base, {
4917         /**
4918          * Create a new instance of this Class.
4919          *
4920          *     Ext.define('My.cool.Class', {
4921          *         ...
4922          *     });
4923          *
4924          *     My.cool.Class.create({
4925          *         someConfig: true
4926          *     });
4927          *
4928          * All parameters are passed to the constructor of the class.
4929          *
4930          * @return {Object} the created instance.
4931          * @static
4932          * @inheritable
4933          */
4934         create: function() {
4935             return Ext.create.apply(Ext, [this].concat(Array.prototype.slice.call(arguments, 0)));
4936         },
4937
4938         /**
4939          * @private
4940          * @inheritable
4941          */
4942         own: function(name, value) {
4943             if (typeof value == 'function') {
4944                 this.ownMethod(name, value);
4945             }
4946             else {
4947                 this.prototype[name] = value;
4948             }
4949         },
4950
4951         /**
4952          * @private
4953          * @inheritable
4954          */
4955         ownMethod: function(name, fn) {
4956             var originalFn;
4957
4958             if (typeof fn.$owner !== 'undefined' && fn !== Ext.emptyFn) {
4959                 originalFn = fn;
4960
4961                 fn = function() {
4962                     return originalFn.apply(this, arguments);
4963                 };
4964             }
4965
4966             fn.$owner = this;
4967             fn.$name = name;
4968
4969             this.prototype[name] = fn;
4970         },
4971
4972         /**
4973          * Add / override static properties of this class.
4974          *
4975          *     Ext.define('My.cool.Class', {
4976          *         ...
4977          *     });
4978          *
4979          *     My.cool.Class.addStatics({
4980          *         someProperty: 'someValue',      // My.cool.Class.someProperty = 'someValue'
4981          *         method1: function() { ... },    // My.cool.Class.method1 = function() { ... };
4982          *         method2: function() { ... }     // My.cool.Class.method2 = function() { ... };
4983          *     });
4984          *
4985          * @param {Object} members
4986          * @return {Ext.Base} this
4987          * @static
4988          * @inheritable
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          * @private
5002          * @param {Object} members
5003          */
5004         addInheritableStatics: function(members) {
5005             var inheritableStatics,
5006                 hasInheritableStatics,
5007                 prototype = this.prototype,
5008                 name, member;
5009
5010             inheritableStatics = prototype.$inheritableStatics;
5011             hasInheritableStatics = prototype.$hasInheritableStatics;
5012
5013             if (!inheritableStatics) {
5014                 inheritableStatics = prototype.$inheritableStatics = [];
5015                 hasInheritableStatics = prototype.$hasInheritableStatics = {};
5016             }
5017
5018
5019             for (name in members) {
5020                 if (members.hasOwnProperty(name)) {
5021                     member = members[name];
5022                     this[name] = member;
5023
5024                     if (!hasInheritableStatics[name]) {
5025                         hasInheritableStatics[name] = true;
5026                         inheritableStatics.push(name);
5027                     }
5028                 }
5029             }
5030
5031             return this;
5032         },
5033
5034         /**
5035          * Add methods / properties to the prototype of this class.
5036          *
5037          *     Ext.define('My.awesome.Cat', {
5038          *         constructor: function() {
5039          *             ...
5040          *         }
5041          *     });
5042          *
5043          *      My.awesome.Cat.implement({
5044          *          meow: function() {
5045          *             alert('Meowww...');
5046          *          }
5047          *      });
5048          *
5049          *      var kitty = new My.awesome.Cat;
5050          *      kitty.meow();
5051          *
5052          * @param {Object} members
5053          * @static
5054          * @inheritable
5055          */
5056         implement: function(members) {
5057             var prototype = this.prototype,
5058                 enumerables = Ext.enumerables,
5059                 name, i, member;
5060             for (name in members) {
5061                 if (members.hasOwnProperty(name)) {
5062                     member = members[name];
5063
5064                     if (typeof member === 'function') {
5065                         member.$owner = this;
5066                         member.$name = name;
5067                     }
5068
5069                     prototype[name] = member;
5070                 }
5071             }
5072
5073             if (enumerables) {
5074                 for (i = enumerables.length; i--;) {
5075                     name = enumerables[i];
5076
5077                     if (members.hasOwnProperty(name)) {
5078                         member = members[name];
5079                         member.$owner = this;
5080                         member.$name = name;
5081                         prototype[name] = member;
5082                     }
5083                 }
5084             }
5085         },
5086
5087         /**
5088          * Borrow another class' members to the prototype of this class.
5089          *
5090          *     Ext.define('Bank', {
5091          *         money: '$$$',
5092          *         printMoney: function() {
5093          *             alert('$$$$$$$');
5094          *         }
5095          *     });
5096          *
5097          *     Ext.define('Thief', {
5098          *         ...
5099          *     });
5100          *
5101          *     Thief.borrow(Bank, ['money', 'printMoney']);
5102          *
5103          *     var steve = new Thief();
5104          *
5105          *     alert(steve.money); // alerts '$$$'
5106          *     steve.printMoney(); // alerts '$$$$$$$'
5107          *
5108          * @param {Ext.Base} fromClass The class to borrow members from
5109          * @param {String/String[]} members The names of the members to borrow
5110          * @return {Ext.Base} this
5111          * @static
5112          * @inheritable
5113          */
5114         borrow: function(fromClass, members) {
5115             var fromPrototype = fromClass.prototype,
5116                 i, ln, member;
5117
5118             members = Ext.Array.from(members);
5119
5120             for (i = 0, ln = members.length; i < ln; i++) {
5121                 member = members[i];
5122
5123                 this.own(member, fromPrototype[member]);
5124             }
5125
5126             return this;
5127         },
5128
5129         /**
5130          * Override prototype members of this class. Overridden methods can be invoked via
5131          * {@link Ext.Base#callOverridden}
5132          *
5133          *     Ext.define('My.Cat', {
5134          *         constructor: function() {
5135          *             alert("I'm a cat!");
5136          *
5137          *             return this;
5138          *         }
5139          *     });
5140          *
5141          *     My.Cat.override({
5142          *         constructor: function() {
5143          *             alert("I'm going to be a cat!");
5144          *
5145          *             var instance = this.callOverridden();
5146          *
5147          *             alert("Meeeeoooowwww");
5148          *
5149          *             return instance;
5150          *         }
5151          *     });
5152          *
5153          *     var kitty = new My.Cat(); // alerts "I'm going to be a cat!"
5154          *                               // alerts "I'm a cat!"
5155          *                               // alerts "Meeeeoooowwww"
5156          *
5157          * @param {Object} members
5158          * @return {Ext.Base} this
5159          * @static
5160          * @inheritable
5161          */
5162         override: function(members) {
5163             var prototype = this.prototype,
5164                 enumerables = Ext.enumerables,
5165                 name, i, member, previous;
5166
5167             if (arguments.length === 2) {
5168                 name = members;
5169                 member = arguments[1];
5170
5171                 if (typeof member == 'function') {
5172                     if (typeof prototype[name] == 'function') {
5173                         previous = prototype[name];
5174                         member.$previous = previous;
5175                     }
5176
5177                     this.ownMethod(name, member);
5178                 }
5179                 else {
5180                     prototype[name] = member;
5181                 }
5182
5183                 return this;
5184             }
5185
5186             for (name in members) {
5187                 if (members.hasOwnProperty(name)) {
5188                     member = members[name];
5189
5190                     if (typeof member === 'function') {
5191                         if (typeof prototype[name] === 'function') {
5192                             previous = prototype[name];
5193                             member.$previous = previous;
5194                         }
5195
5196                         this.ownMethod(name, member);
5197                     }
5198                     else {
5199                         prototype[name] = member;
5200                     }
5201                 }
5202             }
5203
5204             if (enumerables) {
5205                 for (i = enumerables.length; i--;) {
5206                     name = enumerables[i];
5207
5208                     if (members.hasOwnProperty(name)) {
5209                         if (typeof prototype[name] !== 'undefined') {
5210                             previous = prototype[name];
5211                             members[name].$previous = previous;
5212                         }
5213
5214                         this.ownMethod(name, members[name]);
5215                     }
5216                 }
5217             }
5218
5219             return this;
5220         },
5221
5222         //<feature classSystem.mixins>
5223         /**
5224          * Used internally by the mixins pre-processor
5225          * @private
5226          * @inheritable
5227          */
5228         mixin: function(name, cls) {
5229             var mixin = cls.prototype,
5230                 my = this.prototype,
5231                 key, fn;
5232
5233             for (key in mixin) {
5234                 if (mixin.hasOwnProperty(key)) {
5235                     if (typeof my[key] === 'undefined' && key !== 'mixins' && key !== 'mixinId') {
5236                         if (typeof mixin[key] === 'function') {
5237                             fn = mixin[key];
5238
5239                             if (typeof fn.$owner === 'undefined') {
5240                                 this.ownMethod(key, fn);
5241                             }
5242                             else {
5243                                 my[key] = fn;
5244                             }
5245                         }
5246                         else {
5247                             my[key] = mixin[key];
5248                         }
5249                     }
5250                     //<feature classSystem.config>
5251                     else if (key === 'config' && my.config && mixin.config) {
5252                         Ext.Object.merge(my.config, mixin.config);
5253                     }
5254                     //</feature>
5255                 }
5256             }
5257
5258             if (typeof mixin.onClassMixedIn !== 'undefined') {
5259                 mixin.onClassMixedIn.call(cls, this);
5260             }
5261
5262             if (!my.hasOwnProperty('mixins')) {
5263                 if ('mixins' in my) {
5264                     my.mixins = Ext.Object.merge({}, my.mixins);
5265                 }
5266                 else {
5267                     my.mixins = {};
5268                 }
5269             }
5270
5271             my.mixins[name] = mixin;
5272         },
5273         //</feature>
5274
5275         /**
5276          * Get the current class' name in string format.
5277          *
5278          *     Ext.define('My.cool.Class', {
5279          *         constructor: function() {
5280          *             alert(this.self.getName()); // alerts 'My.cool.Class'
5281          *         }
5282          *     });
5283          *
5284          *     My.cool.Class.getName(); // 'My.cool.Class'
5285          *
5286          * @return {String} className
5287          * @static
5288          * @inheritable
5289          */
5290         getName: function() {
5291             return Ext.getClassName(this);
5292         },
5293
5294         /**
5295          * Create aliases for existing prototype methods. Example:
5296          *
5297          *     Ext.define('My.cool.Class', {
5298          *         method1: function() { ... },
5299          *         method2: function() { ... }
5300          *     });
5301          *
5302          *     var test = new My.cool.Class();
5303          *
5304          *     My.cool.Class.createAlias({
5305          *         method3: 'method1',
5306          *         method4: 'method2'
5307          *     });
5308          *
5309          *     test.method3(); // test.method1()
5310          *
5311          *     My.cool.Class.createAlias('method5', 'method3');
5312          *
5313          *     test.method5(); // test.method3() -> test.method1()
5314          *
5315          * @param {String/Object} alias The new method name, or an object to set multiple aliases. See
5316          * {@link Ext.Function#flexSetter flexSetter}
5317          * @param {String/Object} origin The original method name
5318          * @static
5319          * @inheritable
5320          * @method
5321          */
5322         createAlias: flexSetter(function(alias, origin) {
5323             this.prototype[alias] = function() {
5324                 return this[origin].apply(this, arguments);
5325             }
5326         })
5327     });
5328
5329 })(Ext.Function.flexSetter);
5330
5331 /**
5332  * @author Jacky Nguyen <jacky@sencha.com>
5333  * @docauthor Jacky Nguyen <jacky@sencha.com>
5334  * @class Ext.Class
5335  *
5336  * Handles class creation throughout the framework. This is a low level factory that is used by Ext.ClassManager and generally
5337  * should not be used directly. If you choose to use Ext.Class you will lose out on the namespace, aliasing and depency loading
5338  * features made available by Ext.ClassManager. The only time you would use Ext.Class directly is to create an anonymous class.
5339  *
5340  * If you wish to create a class you should use {@link Ext#define Ext.define} which aliases
5341  * {@link Ext.ClassManager#create Ext.ClassManager.create} to enable namespacing and dynamic dependency resolution.
5342  *
5343  * Ext.Class is the factory and **not** the superclass of everything. For the base class that **all** Ext classes inherit
5344  * from, see {@link Ext.Base}.
5345  */
5346 (function() {
5347
5348     var Class,
5349         Base = Ext.Base,
5350         baseStaticProperties = [],
5351         baseStaticProperty;
5352
5353     for (baseStaticProperty in Base) {
5354         if (Base.hasOwnProperty(baseStaticProperty)) {
5355             baseStaticProperties.push(baseStaticProperty);
5356         }
5357     }
5358
5359     /**
5360      * @method constructor
5361      * Creates new class.
5362      * @param {Object} classData An object represent the properties of this class
5363      * @param {Function} createdFn (Optional) The callback function to be executed when this class is fully created.
5364      * Note that the creation process can be asynchronous depending on the pre-processors used.
5365      * @return {Ext.Base} The newly created class
5366      */
5367     Ext.Class = Class = function(newClass, classData, onClassCreated) {
5368         if (typeof newClass != 'function') {
5369             onClassCreated = classData;
5370             classData = newClass;
5371             newClass = function() {
5372                 return this.constructor.apply(this, arguments);
5373             };
5374         }
5375
5376         if (!classData) {
5377             classData = {};
5378         }
5379
5380         var preprocessorStack = classData.preprocessors || Class.getDefaultPreprocessors(),
5381             registeredPreprocessors = Class.getPreprocessors(),
5382             index = 0,
5383             preprocessors = [],
5384             preprocessor, staticPropertyName, process, i, j, ln;
5385
5386         for (i = 0, ln = baseStaticProperties.length; i < ln; i++) {
5387             staticPropertyName = baseStaticProperties[i];
5388             newClass[staticPropertyName] = Base[staticPropertyName];
5389         }
5390
5391         delete classData.preprocessors;
5392
5393         for (j = 0, ln = preprocessorStack.length; j < ln; j++) {
5394             preprocessor = preprocessorStack[j];
5395
5396             if (typeof preprocessor == 'string') {
5397                 preprocessor = registeredPreprocessors[preprocessor];
5398
5399                 if (!preprocessor.always) {
5400                     if (classData.hasOwnProperty(preprocessor.name)) {
5401                         preprocessors.push(preprocessor.fn);
5402                     }
5403                 }
5404                 else {
5405                     preprocessors.push(preprocessor.fn);
5406                 }
5407             }
5408             else {
5409                 preprocessors.push(preprocessor);
5410             }
5411         }
5412
5413         classData.onClassCreated = onClassCreated || Ext.emptyFn;
5414
5415         classData.onBeforeClassCreated = function(cls, data) {
5416             onClassCreated = data.onClassCreated;
5417
5418             delete data.onBeforeClassCreated;
5419             delete data.onClassCreated;
5420
5421             cls.implement(data);
5422
5423             onClassCreated.call(cls, cls);
5424         };
5425
5426         process = function(cls, data) {
5427             preprocessor = preprocessors[index++];
5428
5429             if (!preprocessor) {
5430                 data.onBeforeClassCreated.apply(this, arguments);
5431                 return;
5432             }
5433
5434             if (preprocessor.call(this, cls, data, process) !== false) {
5435                 process.apply(this, arguments);
5436             }
5437         };
5438
5439         process.call(Class, newClass, classData);
5440
5441         return newClass;
5442     };
5443
5444     Ext.apply(Class, {
5445
5446         /** @private */
5447         preprocessors: {},
5448
5449         /**
5450          * Register a new pre-processor to be used during the class creation process
5451          *
5452          * @member Ext.Class
5453          * @param {String} name The pre-processor's name
5454          * @param {Function} fn The callback function to be executed. Typical format:
5455          *
5456          *     function(cls, data, fn) {
5457          *         // Your code here
5458          *
5459          *         // Execute this when the processing is finished.
5460          *         // Asynchronous processing is perfectly ok
5461          *         if (fn) {
5462          *             fn.call(this, cls, data);
5463          *         }
5464          *     });
5465          *
5466          * @param {Function} fn.cls The created class
5467          * @param {Object} fn.data The set of properties passed in {@link Ext.Class} constructor
5468          * @param {Function} fn.fn The callback function that **must** to be executed when this pre-processor finishes,
5469          * regardless of whether the processing is synchronous or aynchronous
5470          *
5471          * @return {Ext.Class} this
5472          * @static
5473          */
5474         registerPreprocessor: function(name, fn, always) {
5475             this.preprocessors[name] = {
5476                 name: name,
5477                 always: always ||  false,
5478                 fn: fn
5479             };
5480
5481             return this;
5482         },
5483
5484         /**
5485          * Retrieve a pre-processor callback function by its name, which has been registered before
5486          *
5487          * @param {String} name
5488          * @return {Function} preprocessor
5489          * @static
5490          */
5491         getPreprocessor: function(name) {
5492             return this.preprocessors[name];
5493         },
5494
5495         getPreprocessors: function() {
5496             return this.preprocessors;
5497         },
5498
5499         /**
5500          * Retrieve the array stack of default pre-processors
5501          *
5502          * @return {Function[]} defaultPreprocessors
5503          * @static
5504          */
5505         getDefaultPreprocessors: function() {
5506             return this.defaultPreprocessors || [];
5507         },
5508
5509         /**
5510          * Set the default array stack of default pre-processors
5511          *
5512          * @param {Function/Function[]} preprocessors
5513          * @return {Ext.Class} this
5514          * @static
5515          */
5516         setDefaultPreprocessors: function(preprocessors) {
5517             this.defaultPreprocessors = Ext.Array.from(preprocessors);
5518
5519             return this;
5520         },
5521
5522         /**
5523          * Inserts this pre-processor at a specific position in the stack, optionally relative to
5524          * any existing pre-processor. For example:
5525          *
5526          *     Ext.Class.registerPreprocessor('debug', function(cls, data, fn) {
5527          *         // Your code here
5528          *
5529          *         if (fn) {
5530          *             fn.call(this, cls, data);
5531          *         }
5532          *     }).setDefaultPreprocessorPosition('debug', 'last');
5533          *
5534          * @param {String} name The pre-processor name. Note that it needs to be registered with
5535          * {@link #registerPreprocessor registerPreprocessor} before this
5536          * @param {String} offset The insertion position. Four possible values are:
5537          * 'first', 'last', or: 'before', 'after' (relative to the name provided in the third argument)
5538          * @param {String} relativeName
5539          * @return {Ext.Class} this
5540          * @static
5541          */
5542         setDefaultPreprocessorPosition: function(name, offset, relativeName) {
5543             var defaultPreprocessors = this.defaultPreprocessors,
5544                 index;
5545
5546             if (typeof offset == 'string') {
5547                 if (offset === 'first') {
5548                     defaultPreprocessors.unshift(name);
5549
5550                     return this;
5551                 }
5552                 else if (offset === 'last') {
5553                     defaultPreprocessors.push(name);
5554
5555                     return this;
5556                 }
5557
5558                 offset = (offset === 'after') ? 1 : -1;
5559             }
5560
5561             index = Ext.Array.indexOf(defaultPreprocessors, relativeName);
5562
5563             if (index !== -1) {
5564                 Ext.Array.splice(defaultPreprocessors, Math.max(0, index + offset), 0, name);
5565             }
5566
5567             return this;
5568         }
5569     });
5570
5571     /**
5572      * @cfg {String} extend
5573      * The parent class that this class extends. For example:
5574      *
5575      *     Ext.define('Person', {
5576      *         say: function(text) { alert(text); }
5577      *     });
5578      *
5579      *     Ext.define('Developer', {
5580      *         extend: 'Person',
5581      *         say: function(text) { this.callParent(["print "+text]); }
5582      *     });
5583      */
5584     Class.registerPreprocessor('extend', function(cls, data) {
5585         var extend = data.extend,
5586             base = Ext.Base,
5587             basePrototype = base.prototype,
5588             prototype = function() {},
5589             parent, i, k, ln, staticName, parentStatics,
5590             parentPrototype, clsPrototype;
5591
5592         if (extend && extend !== Object) {
5593             parent = extend;
5594         }
5595         else {
5596             parent = base;
5597         }
5598
5599         parentPrototype = parent.prototype;
5600
5601         prototype.prototype = parentPrototype;
5602         clsPrototype = cls.prototype = new prototype();
5603
5604         if (!('$class' in parent)) {
5605             for (i in basePrototype) {
5606                 if (!parentPrototype[i]) {
5607                     parentPrototype[i] = basePrototype[i];
5608                 }
5609             }
5610         }
5611
5612         clsPrototype.self = cls;
5613
5614         cls.superclass = clsPrototype.superclass = parentPrototype;
5615
5616         delete data.extend;
5617
5618         //<feature classSystem.inheritableStatics>
5619         // Statics inheritance
5620         parentStatics = parentPrototype.$inheritableStatics;
5621
5622         if (parentStatics) {
5623             for (k = 0, ln = parentStatics.length; k < ln; k++) {
5624                 staticName = parentStatics[k];
5625
5626                 if (!cls.hasOwnProperty(staticName)) {
5627                     cls[staticName] = parent[staticName];
5628                 }
5629             }
5630         }
5631         //</feature>
5632
5633         //<feature classSystem.config>
5634         // Merge the parent class' config object without referencing it
5635         if (parentPrototype.config) {
5636             clsPrototype.config = Ext.Object.merge({}, parentPrototype.config);
5637         }
5638         else {
5639             clsPrototype.config = {};
5640         }
5641         //</feature>
5642
5643         //<feature classSystem.onClassExtended>
5644         if (clsPrototype.$onExtended) {
5645             clsPrototype.$onExtended.call(cls, cls, data);
5646         }
5647
5648         if (data.onClassExtended) {
5649             clsPrototype.$onExtended = data.onClassExtended;
5650             delete data.onClassExtended;
5651         }
5652         //</feature>
5653
5654     }, true);
5655
5656     //<feature classSystem.statics>
5657     /**
5658      * @cfg {Object} statics
5659      * List of static methods for this class. For example:
5660      *
5661      *     Ext.define('Computer', {
5662      *          statics: {
5663      *              factory: function(brand) {
5664      *                  // 'this' in static methods refer to the class itself
5665      *                  return new this(brand);
5666      *              }
5667      *          },
5668      *
5669      *          constructor: function() { ... }
5670      *     });
5671      *
5672      *     var dellComputer = Computer.factory('Dell');
5673      */
5674     Class.registerPreprocessor('statics', function(cls, data) {
5675         cls.addStatics(data.statics);
5676
5677         delete data.statics;
5678     });
5679     //</feature>
5680
5681     //<feature classSystem.inheritableStatics>
5682     /**
5683      * @cfg {Object} inheritableStatics
5684      * List of inheritable static methods for this class.
5685      * Otherwise just like {@link #statics} but subclasses inherit these methods.
5686      */
5687     Class.registerPreprocessor('inheritableStatics', function(cls, data) {
5688         cls.addInheritableStatics(data.inheritableStatics);
5689
5690         delete data.inheritableStatics;
5691     });
5692     //</feature>
5693
5694     //<feature classSystem.config>
5695     /**
5696      * @cfg {Object} config
5697      * List of configuration options with their default values, for which automatically
5698      * accessor methods are generated.  For example:
5699      *
5700      *     Ext.define('SmartPhone', {
5701      *          config: {
5702      *              hasTouchScreen: false,
5703      *              operatingSystem: 'Other',
5704      *              price: 500
5705      *          },
5706      *          constructor: function(cfg) {
5707      *              this.initConfig(cfg);
5708      *          }
5709      *     });
5710      *
5711      *     var iPhone = new SmartPhone({
5712      *          hasTouchScreen: true,
5713      *          operatingSystem: 'iOS'
5714      *     });
5715      *
5716      *     iPhone.getPrice(); // 500;
5717      *     iPhone.getOperatingSystem(); // 'iOS'
5718      *     iPhone.getHasTouchScreen(); // true;
5719      *     iPhone.hasTouchScreen(); // true
5720      */
5721     Class.registerPreprocessor('config', function(cls, data) {
5722         var prototype = cls.prototype;
5723
5724         Ext.Object.each(data.config, function(name) {
5725             var cName = name.charAt(0).toUpperCase() + name.substr(1),
5726                 pName = name,
5727                 apply = 'apply' + cName,
5728                 setter = 'set' + cName,
5729                 getter = 'get' + cName;
5730
5731             if (!(apply in prototype) && !data.hasOwnProperty(apply)) {
5732                 data[apply] = function(val) {
5733                     return val;
5734                 };
5735             }
5736
5737             if (!(setter in prototype) && !data.hasOwnProperty(setter)) {
5738                 data[setter] = function(val) {
5739                     var ret = this[apply].call(this, val, this[pName]);
5740
5741                     if (typeof ret != 'undefined') {
5742                         this[pName] = ret;
5743                     }
5744
5745                     return this;
5746                 };
5747             }
5748
5749             if (!(getter in prototype) && !data.hasOwnProperty(getter)) {
5750                 data[getter] = function() {
5751                     return this[pName];
5752                 };
5753             }
5754         });
5755
5756         Ext.Object.merge(prototype.config, data.config);
5757         delete data.config;
5758     });
5759     //</feature>
5760
5761     //<feature classSystem.mixins>
5762     /**
5763      * @cfg {Object} mixins
5764      * List of classes to mix into this class. For example:
5765      *
5766      *     Ext.define('CanSing', {
5767      *          sing: function() {
5768      *              alert("I'm on the highway to hell...")
5769      *          }
5770      *     });
5771      *
5772      *     Ext.define('Musician', {
5773      *          extend: 'Person',
5774      *
5775      *          mixins: {
5776      *              canSing: 'CanSing'
5777      *          }
5778      *     })
5779      */
5780     Class.registerPreprocessor('mixins', function(cls, data) {
5781         var mixins = data.mixins,
5782             name, mixin, i, ln;
5783
5784         delete data.mixins;
5785
5786         Ext.Function.interceptBefore(data, 'onClassCreated', function(cls) {
5787             if (mixins instanceof Array) {
5788                 for (i = 0,ln = mixins.length; i < ln; i++) {
5789                     mixin = mixins[i];
5790                     name = mixin.prototype.mixinId || mixin.$className;
5791
5792                     cls.mixin(name, mixin);
5793                 }
5794             }
5795             else {
5796                 for (name in mixins) {
5797                     if (mixins.hasOwnProperty(name)) {
5798                         cls.mixin(name, mixins[name]);
5799                     }
5800                 }
5801             }
5802         });
5803     });
5804
5805     //</feature>
5806
5807     Class.setDefaultPreprocessors([
5808         'extend'
5809         //<feature classSystem.statics>
5810         ,'statics'
5811         //</feature>
5812         //<feature classSystem.inheritableStatics>
5813         ,'inheritableStatics'
5814         //</feature>
5815         //<feature classSystem.config>
5816         ,'config'
5817         //</feature>
5818         //<feature classSystem.mixins>
5819         ,'mixins'
5820         //</feature>
5821     ]);
5822
5823     //<feature classSystem.backwardsCompatible>
5824     // Backwards compatible
5825     Ext.extend = function(subclass, superclass, members) {
5826         if (arguments.length === 2 && Ext.isObject(superclass)) {
5827             members = superclass;
5828             superclass = subclass;
5829             subclass = null;
5830         }
5831
5832         var cls;
5833
5834         if (!superclass) {
5835             Ext.Error.raise("Attempting to extend from a class which has not been loaded on the page.");
5836         }
5837
5838         members.extend = superclass;
5839         members.preprocessors = [
5840             'extend'
5841             //<feature classSystem.statics>
5842             ,'statics'
5843             //</feature>
5844             //<feature classSystem.inheritableStatics>
5845             ,'inheritableStatics'
5846             //</feature>
5847             //<feature classSystem.mixins>
5848             ,'mixins'
5849             //</feature>
5850             //<feature classSystem.config>
5851             ,'config'
5852             //</feature>
5853         ];
5854
5855         if (subclass) {
5856             cls = new Class(subclass, members);
5857         }
5858         else {
5859             cls = new Class(members);
5860         }
5861
5862         cls.prototype.override = function(o) {
5863             for (var m in o) {
5864                 if (o.hasOwnProperty(m)) {
5865                     this[m] = o[m];
5866                 }
5867             }
5868         };
5869
5870         return cls;
5871     };
5872     //</feature>
5873
5874 })();
5875
5876 /**
5877  * @author Jacky Nguyen <jacky@sencha.com>
5878  * @docauthor Jacky Nguyen <jacky@sencha.com>
5879  * @class Ext.ClassManager
5880  *
5881  * Ext.ClassManager manages all classes and handles mapping from string class name to
5882  * actual class objects throughout the whole framework. It is not generally accessed directly, rather through
5883  * these convenient shorthands:
5884  *
5885  * - {@link Ext#define Ext.define}
5886  * - {@link Ext#create Ext.create}
5887  * - {@link Ext#widget Ext.widget}
5888  * - {@link Ext#getClass Ext.getClass}
5889  * - {@link Ext#getClassName Ext.getClassName}
5890  *
5891  * # Basic syntax:
5892  *
5893  *     Ext.define(className, properties);
5894  *
5895  * in which `properties` is an object represent a collection of properties that apply to the class. See
5896  * {@link Ext.ClassManager#create} for more detailed instructions.
5897  *
5898  *     Ext.define('Person', {
5899  *          name: 'Unknown',
5900  *
5901  *          constructor: function(name) {
5902  *              if (name) {
5903  *                  this.name = name;
5904  *              }
5905  *
5906  *              return this;
5907  *          },
5908  *
5909  *          eat: function(foodType) {
5910  *              alert("I'm eating: " + foodType);
5911  *
5912  *              return this;
5913  *          }
5914  *     });
5915  *
5916  *     var aaron = new Person("Aaron");
5917  *     aaron.eat("Sandwich"); // alert("I'm eating: Sandwich");
5918  *
5919  * Ext.Class has a powerful set of extensible {@link Ext.Class#registerPreprocessor pre-processors} which takes care of
5920  * everything related to class creation, including but not limited to inheritance, mixins, configuration, statics, etc.
5921  *
5922  * # Inheritance:
5923  *
5924  *     Ext.define('Developer', {
5925  *          extend: 'Person',
5926  *
5927  *          constructor: function(name, isGeek) {
5928  *              this.isGeek = isGeek;
5929  *
5930  *              // Apply a method from the parent class' prototype
5931  *              this.callParent([name]);
5932  *
5933  *              return this;
5934  *
5935  *          },
5936  *
5937  *          code: function(language) {
5938  *              alert("I'm coding in: " + language);
5939  *
5940  *              this.eat("Bugs");
5941  *
5942  *              return this;
5943  *          }
5944  *     });
5945  *
5946  *     var jacky = new Developer("Jacky", true);
5947  *     jacky.code("JavaScript"); // alert("I'm coding in: JavaScript");
5948  *                               // alert("I'm eating: Bugs");
5949  *
5950  * See {@link Ext.Base#callParent} for more details on calling superclass' methods
5951  *
5952  * # Mixins:
5953  *
5954  *     Ext.define('CanPlayGuitar', {
5955  *          playGuitar: function() {
5956  *             alert("F#...G...D...A");
5957  *          }
5958  *     });
5959  *
5960  *     Ext.define('CanComposeSongs', {
5961  *          composeSongs: function() { ... }
5962  *     });
5963  *
5964  *     Ext.define('CanSing', {
5965  *          sing: function() {
5966  *              alert("I'm on the highway to hell...")
5967  *          }
5968  *     });
5969  *
5970  *     Ext.define('Musician', {
5971  *          extend: 'Person',
5972  *
5973  *          mixins: {
5974  *              canPlayGuitar: 'CanPlayGuitar',
5975  *              canComposeSongs: 'CanComposeSongs',
5976  *              canSing: 'CanSing'
5977  *          }
5978  *     })
5979  *
5980  *     Ext.define('CoolPerson', {
5981  *          extend: 'Person',
5982  *
5983  *          mixins: {
5984  *              canPlayGuitar: 'CanPlayGuitar',
5985  *              canSing: 'CanSing'
5986  *          },
5987  *
5988  *          sing: function() {
5989  *              alert("Ahem....");
5990  *
5991  *              this.mixins.canSing.sing.call(this);
5992  *
5993  *              alert("[Playing guitar at the same time...]");
5994  *
5995  *              this.playGuitar();
5996  *          }
5997  *     });
5998  *
5999  *     var me = new CoolPerson("Jacky");
6000  *
6001  *     me.sing(); // alert("Ahem...");
6002  *                // alert("I'm on the highway to hell...");
6003  *                // alert("[Playing guitar at the same time...]");
6004  *                // alert("F#...G...D...A");
6005  *
6006  * # Config:
6007  *
6008  *     Ext.define('SmartPhone', {
6009  *          config: {
6010  *              hasTouchScreen: false,
6011  *              operatingSystem: 'Other',
6012  *              price: 500
6013  *          },
6014  *
6015  *          isExpensive: false,
6016  *
6017  *          constructor: function(config) {
6018  *              this.initConfig(config);
6019  *
6020  *              return this;
6021  *          },
6022  *
6023  *          applyPrice: function(price) {
6024  *              this.isExpensive = (price > 500);
6025  *
6026  *              return price;
6027  *          },
6028  *
6029  *          applyOperatingSystem: function(operatingSystem) {
6030  *              if (!(/^(iOS|Android|BlackBerry)$/i).test(operatingSystem)) {
6031  *                  return 'Other';
6032  *              }
6033  *
6034  *              return operatingSystem;
6035  *          }
6036  *     });
6037  *
6038  *     var iPhone = new SmartPhone({
6039  *          hasTouchScreen: true,
6040  *          operatingSystem: 'iOS'
6041  *     });
6042  *
6043  *     iPhone.getPrice(); // 500;
6044  *     iPhone.getOperatingSystem(); // 'iOS'
6045  *     iPhone.getHasTouchScreen(); // true;
6046  *     iPhone.hasTouchScreen(); // true
6047  *
6048  *     iPhone.isExpensive; // false;
6049  *     iPhone.setPrice(600);
6050  *     iPhone.getPrice(); // 600
6051  *     iPhone.isExpensive; // true;
6052  *
6053  *     iPhone.setOperatingSystem('AlienOS');
6054  *     iPhone.getOperatingSystem(); // 'Other'
6055  *
6056  * # Statics:
6057  *
6058  *     Ext.define('Computer', {
6059  *          statics: {
6060  *              factory: function(brand) {
6061  *                 // 'this' in static methods refer to the class itself
6062  *                  return new this(brand);
6063  *              }
6064  *          },
6065  *
6066  *          constructor: function() { ... }
6067  *     });
6068  *
6069  *     var dellComputer = Computer.factory('Dell');
6070  *
6071  * Also see {@link Ext.Base#statics} and {@link Ext.Base#self} for more details on accessing
6072  * static properties within class methods
6073  *
6074  * @singleton
6075  */
6076 (function(Class, alias) {
6077
6078     var slice = Array.prototype.slice;
6079
6080     var Manager = Ext.ClassManager = {
6081
6082         /**
6083          * @property {Object} classes
6084          * All classes which were defined through the ClassManager. Keys are the
6085          * name of the classes and the values are references to the classes.
6086          * @private
6087          */
6088         classes: {},
6089
6090         /**
6091          * @private
6092          */
6093         existCache: {},
6094
6095         /**
6096          * @private
6097          */
6098         namespaceRewrites: [{
6099             from: 'Ext.',
6100             to: Ext
6101         }],
6102
6103         /**
6104          * @private
6105          */
6106         maps: {
6107             alternateToName: {},
6108             aliasToName: {},
6109             nameToAliases: {}
6110         },
6111
6112         /** @private */
6113         enableNamespaceParseCache: true,
6114
6115         /** @private */
6116         namespaceParseCache: {},
6117
6118         /** @private */
6119         instantiators: [],
6120
6121
6122         /**
6123          * Checks if a class has already been created.
6124          *
6125          * @param {String} className
6126          * @return {Boolean} exist
6127          */
6128         isCreated: function(className) {
6129             var i, ln, part, root, parts;
6130
6131
6132             if (this.classes.hasOwnProperty(className) || this.existCache.hasOwnProperty(className)) {
6133                 return true;
6134             }
6135
6136             root = Ext.global;
6137             parts = this.parseNamespace(className);
6138
6139             for (i = 0, ln = parts.length; i < ln; i++) {
6140                 part = parts[i];
6141
6142                 if (typeof part !== 'string') {
6143                     root = part;
6144                 } else {
6145                     if (!root || !root[part]) {
6146                         return false;
6147                     }
6148
6149                     root = root[part];
6150                 }
6151             }
6152
6153             Ext.Loader.historyPush(className);
6154
6155             this.existCache[className] = true;
6156
6157             return true;
6158         },
6159
6160         /**
6161          * Supports namespace rewriting
6162          * @private
6163          */
6164         parseNamespace: function(namespace) {
6165
6166             var cache = this.namespaceParseCache;
6167
6168             if (this.enableNamespaceParseCache) {
6169                 if (cache.hasOwnProperty(namespace)) {
6170                     return cache[namespace];
6171                 }
6172             }
6173
6174             var parts = [],
6175                 rewrites = this.namespaceRewrites,
6176                 rewrite, from, to, i, ln, root = Ext.global;
6177
6178             for (i = 0, ln = rewrites.length; i < ln; i++) {
6179                 rewrite = rewrites[i];
6180                 from = rewrite.from;
6181                 to = rewrite.to;
6182
6183                 if (namespace === from || namespace.substring(0, from.length) === from) {
6184                     namespace = namespace.substring(from.length);
6185
6186                     if (typeof to !== 'string') {
6187                         root = to;
6188                     } else {
6189                         parts = parts.concat(to.split('.'));
6190                     }
6191
6192                     break;
6193                 }
6194             }
6195
6196             parts.push(root);
6197
6198             parts = parts.concat(namespace.split('.'));
6199
6200             if (this.enableNamespaceParseCache) {
6201                 cache[namespace] = parts;
6202             }
6203
6204             return parts;
6205         },
6206
6207         /**
6208          * Creates a namespace and assign the `value` to the created object
6209          *
6210          *     Ext.ClassManager.setNamespace('MyCompany.pkg.Example', someObject);
6211          *
6212          *     alert(MyCompany.pkg.Example === someObject); // alerts true
6213          *
6214          * @param {String} name
6215          * @param {Object} value
6216          */
6217         setNamespace: function(name, value) {
6218             var root = Ext.global,
6219                 parts = this.parseNamespace(name),
6220                 ln = parts.length - 1,
6221                 leaf = parts[ln],
6222                 i, part;
6223
6224             for (i = 0; i < ln; i++) {
6225                 part = parts[i];
6226
6227                 if (typeof part !== 'string') {
6228                     root = part;
6229                 } else {
6230                     if (!root[part]) {
6231                         root[part] = {};
6232                     }
6233
6234                     root = root[part];
6235                 }
6236             }
6237
6238             root[leaf] = value;
6239
6240             return root[leaf];
6241         },
6242
6243         /**
6244          * The new Ext.ns, supports namespace rewriting
6245          * @private
6246          */
6247         createNamespaces: function() {
6248             var root = Ext.global,
6249                 parts, part, i, j, ln, subLn;
6250
6251             for (i = 0, ln = arguments.length; i < ln; i++) {
6252                 parts = this.parseNamespace(arguments[i]);
6253
6254                 for (j = 0, subLn = parts.length; j < subLn; j++) {
6255                     part = parts[j];
6256
6257                     if (typeof part !== 'string') {
6258                         root = part;
6259                     } else {
6260                         if (!root[part]) {
6261                             root[part] = {};
6262                         }
6263
6264                         root = root[part];
6265                     }
6266                 }
6267             }
6268
6269             return root;
6270         },
6271
6272         /**
6273          * Sets a name reference to a class.
6274          *
6275          * @param {String} name
6276          * @param {Object} value
6277          * @return {Ext.ClassManager} this
6278          */
6279         set: function(name, value) {
6280             var targetName = this.getName(value);
6281
6282             this.classes[name] = this.setNamespace(name, value);
6283
6284             if (targetName && targetName !== name) {
6285                 this.maps.alternateToName[name] = targetName;
6286             }
6287
6288             return this;
6289         },
6290
6291         /**
6292          * Retrieve a class by its name.
6293          *
6294          * @param {String} name
6295          * @return {Ext.Class} class
6296          */
6297         get: function(name) {
6298             if (this.classes.hasOwnProperty(name)) {
6299                 return this.classes[name];
6300             }
6301
6302             var root = Ext.global,
6303                 parts = this.parseNamespace(name),
6304                 part, i, ln;
6305
6306             for (i = 0, ln = parts.length; i < ln; i++) {
6307                 part = parts[i];
6308
6309                 if (typeof part !== 'string') {
6310                     root = part;
6311                 } else {
6312                     if (!root || !root[part]) {
6313                         return null;
6314                     }
6315
6316                     root = root[part];
6317                 }
6318             }
6319
6320             return root;
6321         },
6322
6323         /**
6324          * Register the alias for a class.
6325          *
6326          * @param {Ext.Class/String} cls a reference to a class or a className
6327          * @param {String} alias Alias to use when referring to this class
6328          */
6329         setAlias: function(cls, alias) {
6330             var aliasToNameMap = this.maps.aliasToName,
6331                 nameToAliasesMap = this.maps.nameToAliases,
6332                 className;
6333
6334             if (typeof cls === 'string') {
6335                 className = cls;
6336             } else {
6337                 className = this.getName(cls);
6338             }
6339
6340             if (alias && aliasToNameMap[alias] !== className) {
6341
6342                 aliasToNameMap[alias] = className;
6343             }
6344
6345             if (!nameToAliasesMap[className]) {
6346                 nameToAliasesMap[className] = [];
6347             }
6348
6349             if (alias) {
6350                 Ext.Array.include(nameToAliasesMap[className], alias);
6351             }
6352
6353             return this;
6354         },
6355
6356         /**
6357          * Get a reference to the class by its alias.
6358          *
6359          * @param {String} alias
6360          * @return {Ext.Class} class
6361          */
6362         getByAlias: function(alias) {
6363             return this.get(this.getNameByAlias(alias));
6364         },
6365
6366         /**
6367          * Get the name of a class by its alias.
6368          *
6369          * @param {String} alias
6370          * @return {String} className
6371          */
6372         getNameByAlias: function(alias) {
6373             return this.maps.aliasToName[alias] || '';
6374         },
6375
6376         /**
6377          * Get the name of a class by its alternate name.
6378          *
6379          * @param {String} alternate
6380          * @return {String} className
6381          */
6382         getNameByAlternate: function(alternate) {
6383             return this.maps.alternateToName[alternate] || '';
6384         },
6385
6386         /**
6387          * Get the aliases of a class by the class name
6388          *
6389          * @param {String} name
6390          * @return {String[]} aliases
6391          */
6392         getAliasesByName: function(name) {
6393             return this.maps.nameToAliases[name] || [];
6394         },
6395
6396         /**
6397          * Get the name of the class by its reference or its instance.
6398          *
6399          *     Ext.ClassManager.getName(Ext.Action); // returns "Ext.Action"
6400          *
6401          * {@link Ext#getClassName Ext.getClassName} is alias for {@link Ext.ClassManager#getName Ext.ClassManager.getName}.
6402          *
6403          * @param {Ext.Class/Object} object
6404          * @return {String} className
6405          */
6406         getName: function(object) {
6407             return object && object.$className || '';
6408         },
6409
6410         /**
6411          * Get the class of the provided object; returns null if it's not an instance
6412          * of any class created with Ext.define.
6413          *
6414          *     var component = new Ext.Component();
6415          *
6416          *     Ext.ClassManager.getClass(component); // returns Ext.Component
6417          *
6418          * {@link Ext#getClass Ext.getClass} is alias for {@link Ext.ClassManager#getClass Ext.ClassManager.getClass}.
6419          *
6420          * @param {Object} object
6421          * @return {Ext.Class} class
6422          */
6423         getClass: function(object) {
6424             return object && object.self || null;
6425         },
6426
6427         /**
6428          * Defines a class.
6429          *
6430          * {@link Ext#define Ext.define} and {@link Ext.ClassManager#create Ext.ClassManager.create} are almost aliases
6431          * of each other, with the only exception that Ext.define allows definition of {@link Ext.Class#override overrides}.
6432          * To avoid trouble, always use Ext.define.
6433          *
6434          *     Ext.define('My.awesome.Class', {
6435          *         someProperty: 'something',
6436          *         someMethod: function() { ... }
6437          *         ...
6438          *
6439          *     }, function() {
6440          *         alert('Created!');
6441          *         alert(this === My.awesome.Class); // alerts true
6442          *
6443          *         var myInstance = new this();
6444          *     });
6445          *
6446          * @param {String} className The class name to create in string dot-namespaced format, for example:
6447          * `My.very.awesome.Class`, `FeedViewer.plugin.CoolPager`. It is highly recommended to follow this simple convention:
6448          *
6449          * - The root and the class name are 'CamelCased'
6450          * - Everything else is lower-cased
6451          *
6452          * @param {Object} data The key-value pairs of properties to apply to this class. Property names can be of any valid
6453          * strings, except those in the reserved list below:
6454          *
6455          * - {@link Ext.Base#self self}
6456          * - {@link Ext.Class#alias alias}
6457          * - {@link Ext.Class#alternateClassName alternateClassName}
6458          * - {@link Ext.Class#config config}
6459          * - {@link Ext.Class#extend extend}
6460          * - {@link Ext.Class#inheritableStatics inheritableStatics}
6461          * - {@link Ext.Class#mixins mixins}
6462          * - {@link Ext.Class#override override} (only when using {@link Ext#define Ext.define})
6463          * - {@link Ext.Class#requires requires}
6464          * - {@link Ext.Class#singleton singleton}
6465          * - {@link Ext.Class#statics statics}
6466          * - {@link Ext.Class#uses uses}
6467          *
6468          * @param {Function} [createdFn] callback to execute after the class is created, the execution scope of which
6469          * (`this`) will be the newly created class itself.
6470          *
6471          * @return {Ext.Base}
6472          */
6473         create: function(className, data, createdFn) {
6474             var manager = this;
6475
6476
6477             data.$className = className;
6478
6479             return new Class(data, function() {
6480                 var postprocessorStack = data.postprocessors || manager.defaultPostprocessors,
6481                     registeredPostprocessors = manager.postprocessors,
6482                     index = 0,
6483                     postprocessors = [],
6484                     postprocessor, process, i, ln;
6485
6486                 delete data.postprocessors;
6487
6488                 for (i = 0, ln = postprocessorStack.length; i < ln; i++) {
6489                     postprocessor = postprocessorStack[i];
6490
6491                     if (typeof postprocessor === 'string') {
6492                         postprocessor = registeredPostprocessors[postprocessor];
6493
6494                         if (!postprocessor.always) {
6495                             if (data[postprocessor.name] !== undefined) {
6496                                 postprocessors.push(postprocessor.fn);
6497                             }
6498                         }
6499                         else {
6500                             postprocessors.push(postprocessor.fn);
6501                         }
6502                     }
6503                     else {
6504                         postprocessors.push(postprocessor);
6505                     }
6506                 }
6507
6508                 process = function(clsName, cls, clsData) {
6509                     postprocessor = postprocessors[index++];
6510
6511                     if (!postprocessor) {
6512                         manager.set(className, cls);
6513
6514                         Ext.Loader.historyPush(className);
6515
6516                         if (createdFn) {
6517                             createdFn.call(cls, cls);
6518                         }
6519
6520                         return;
6521                     }
6522
6523                     if (postprocessor.call(this, clsName, cls, clsData, process) !== false) {
6524                         process.apply(this, arguments);
6525                     }
6526                 };
6527
6528                 process.call(manager, className, this, data);
6529             });
6530         },
6531
6532         /**
6533          * Instantiate a class by its alias.
6534          *
6535          * If {@link Ext.Loader} is {@link Ext.Loader#setConfig enabled} and the class has not been defined yet, it will
6536          * attempt to load the class via synchronous loading.
6537          *
6538          *     var window = Ext.ClassManager.instantiateByAlias('widget.window', { width: 600, height: 800, ... });
6539          *
6540          * {@link Ext#createByAlias Ext.createByAlias} is alias for {@link Ext.ClassManager#instantiateByAlias Ext.ClassManager.instantiateByAlias}.
6541          *
6542          * @param {String} alias
6543          * @param {Object...} args Additional arguments after the alias will be passed to the
6544          * class constructor.
6545          * @return {Object} instance
6546          */
6547         instantiateByAlias: function() {
6548             var alias = arguments[0],
6549                 args = slice.call(arguments),
6550                 className = this.getNameByAlias(alias);
6551
6552             if (!className) {
6553                 className = this.maps.aliasToName[alias];
6554
6555
6556
6557                 Ext.syncRequire(className);
6558             }
6559
6560             args[0] = className;
6561
6562             return this.instantiate.apply(this, args);
6563         },
6564
6565         /**
6566          * Instantiate a class by either full name, alias or alternate name.
6567          *
6568          * If {@link Ext.Loader} is {@link Ext.Loader#setConfig enabled} and the class has not been defined yet, it will
6569          * attempt to load the class via synchronous loading.
6570          *
6571          * For example, all these three lines return the same result:
6572          *
6573          *     // alias
6574          *     var window = Ext.ClassManager.instantiate('widget.window', { width: 600, height: 800, ... });
6575          *
6576          *     // alternate name
6577          *     var window = Ext.ClassManager.instantiate('Ext.Window', { width: 600, height: 800, ... });
6578          *
6579          *     // full class name
6580          *     var window = Ext.ClassManager.instantiate('Ext.window.Window', { width: 600, height: 800, ... });
6581          *
6582          * {@link Ext#create Ext.create} is alias for {@link Ext.ClassManager#instantiate Ext.ClassManager.instantiate}.
6583          *
6584          * @param {String} name
6585          * @param {Object...} args Additional arguments after the name will be passed to the class' constructor.
6586          * @return {Object} instance
6587          */
6588         instantiate: function() {
6589             var name = arguments[0],
6590                 args = slice.call(arguments, 1),
6591                 alias = name,
6592                 possibleName, cls;
6593
6594             if (typeof name !== 'function') {
6595
6596                 cls = this.get(name);
6597             }
6598             else {
6599                 cls = name;
6600             }
6601
6602             // No record of this class name, it's possibly an alias, so look it up
6603             if (!cls) {
6604                 possibleName = this.getNameByAlias(name);
6605
6606                 if (possibleName) {
6607                     name = possibleName;
6608
6609                     cls = this.get(name);
6610                 }
6611             }
6612
6613             // Still no record of this class name, it's possibly an alternate name, so look it up
6614             if (!cls) {
6615                 possibleName = this.getNameByAlternate(name);
6616
6617                 if (possibleName) {
6618                     name = possibleName;
6619
6620                     cls = this.get(name);
6621                 }
6622             }
6623
6624             // Still not existing at this point, try to load it via synchronous mode as the last resort
6625             if (!cls) {
6626
6627                 Ext.syncRequire(name);
6628
6629                 cls = this.get(name);
6630             }
6631
6632
6633
6634             return this.getInstantiator(args.length)(cls, args);
6635         },
6636
6637         /**
6638          * @private
6639          * @param name
6640          * @param args
6641          */
6642         dynInstantiate: function(name, args) {
6643             args = Ext.Array.from(args, true);
6644             args.unshift(name);
6645
6646             return this.instantiate.apply(this, args);
6647         },
6648
6649         /**
6650          * @private
6651          * @param length
6652          */
6653         getInstantiator: function(length) {
6654             if (!this.instantiators[length]) {
6655                 var i = length,
6656                     args = [];
6657
6658                 for (i = 0; i < length; i++) {
6659                     args.push('a['+i+']');
6660                 }
6661
6662                 this.instantiators[length] = new Function('c', 'a', 'return new c('+args.join(',')+')');
6663             }
6664
6665             return this.instantiators[length];
6666         },
6667
6668         /**
6669          * @private
6670          */
6671         postprocessors: {},
6672
6673         /**
6674          * @private
6675          */
6676         defaultPostprocessors: [],
6677
6678         /**
6679          * Register a post-processor function.
6680          *
6681          * @param {String} name
6682          * @param {Function} postprocessor
6683          */
6684         registerPostprocessor: function(name, fn, always) {
6685             this.postprocessors[name] = {
6686                 name: name,
6687                 always: always ||  false,
6688                 fn: fn
6689             };
6690
6691             return this;
6692         },
6693
6694         /**
6695          * Set the default post processors array stack which are applied to every class.
6696          *
6697          * @param {String/String[]} The name of a registered post processor or an array of registered names.
6698          * @return {Ext.ClassManager} this
6699          */
6700         setDefaultPostprocessors: function(postprocessors) {
6701             this.defaultPostprocessors = Ext.Array.from(postprocessors);
6702
6703             return this;
6704         },
6705
6706         /**
6707          * Insert this post-processor at a specific position in the stack, optionally relative to
6708          * any existing post-processor
6709          *
6710          * @param {String} name The post-processor name. Note that it needs to be registered with
6711          * {@link Ext.ClassManager#registerPostprocessor} before this
6712          * @param {String} offset The insertion position. Four possible values are:
6713          * 'first', 'last', or: 'before', 'after' (relative to the name provided in the third argument)
6714          * @param {String} relativeName
6715          * @return {Ext.ClassManager} this
6716          */
6717         setDefaultPostprocessorPosition: function(name, offset, relativeName) {
6718             var defaultPostprocessors = this.defaultPostprocessors,
6719                 index;
6720
6721             if (typeof offset === 'string') {
6722                 if (offset === 'first') {
6723                     defaultPostprocessors.unshift(name);
6724
6725                     return this;
6726                 }
6727                 else if (offset === 'last') {
6728                     defaultPostprocessors.push(name);
6729
6730                     return this;
6731                 }
6732
6733                 offset = (offset === 'after') ? 1 : -1;
6734             }
6735
6736             index = Ext.Array.indexOf(defaultPostprocessors, relativeName);
6737
6738             if (index !== -1) {
6739                 Ext.Array.splice(defaultPostprocessors, Math.max(0, index + offset), 0, name);
6740             }
6741
6742             return this;
6743         },
6744
6745         /**
6746          * Converts a string expression to an array of matching class names. An expression can either refers to class aliases
6747          * or class names. Expressions support wildcards:
6748          *
6749          *     // returns ['Ext.window.Window']
6750          *     var window = Ext.ClassManager.getNamesByExpression('widget.window');
6751          *
6752          *     // returns ['widget.panel', 'widget.window', ...]
6753          *     var allWidgets = Ext.ClassManager.getNamesByExpression('widget.*');
6754          *
6755          *     // returns ['Ext.data.Store', 'Ext.data.ArrayProxy', ...]
6756          *     var allData = Ext.ClassManager.getNamesByExpression('Ext.data.*');
6757          *
6758          * @param {String} expression
6759          * @return {String[]} classNames
6760          */
6761         getNamesByExpression: function(expression) {
6762             var nameToAliasesMap = this.maps.nameToAliases,
6763                 names = [],
6764                 name, alias, aliases, possibleName, regex, i, ln;
6765
6766
6767             if (expression.indexOf('*') !== -1) {
6768                 expression = expression.replace(/\*/g, '(.*?)');
6769                 regex = new RegExp('^' + expression + '$');
6770
6771                 for (name in nameToAliasesMap) {
6772                     if (nameToAliasesMap.hasOwnProperty(name)) {
6773                         aliases = nameToAliasesMap[name];
6774
6775                         if (name.search(regex) !== -1) {
6776                             names.push(name);
6777                         }
6778                         else {
6779                             for (i = 0, ln = aliases.length; i < ln; i++) {
6780                                 alias = aliases[i];
6781
6782                                 if (alias.search(regex) !== -1) {
6783                                     names.push(name);
6784                                     break;
6785                                 }
6786                             }
6787                         }
6788                     }
6789                 }
6790
6791             } else {
6792                 possibleName = this.getNameByAlias(expression);
6793
6794                 if (possibleName) {
6795                     names.push(possibleName);
6796                 } else {
6797                     possibleName = this.getNameByAlternate(expression);
6798
6799                     if (possibleName) {
6800                         names.push(possibleName);
6801                     } else {
6802                         names.push(expression);
6803                     }
6804                 }
6805             }
6806
6807             return names;
6808         }
6809     };
6810
6811     var defaultPostprocessors = Manager.defaultPostprocessors;
6812     //<feature classSystem.alias>
6813
6814     /**
6815      * @cfg {String[]} alias
6816      * @member Ext.Class
6817      * List of short aliases for class names.  Most useful for defining xtypes for widgets:
6818      *
6819      *     Ext.define('MyApp.CoolPanel', {
6820      *         extend: 'Ext.panel.Panel',
6821      *         alias: ['widget.coolpanel'],
6822      *         title: 'Yeah!'
6823      *     });
6824      *
6825      *     // Using Ext.create
6826      *     Ext.widget('widget.coolpanel');
6827      *     // Using the shorthand for widgets and in xtypes
6828      *     Ext.widget('panel', {
6829      *         items: [
6830      *             {xtype: 'coolpanel', html: 'Foo'},
6831      *             {xtype: 'coolpanel', html: 'Bar'}
6832      *         ]
6833      *     });
6834      */
6835     Manager.registerPostprocessor('alias', function(name, cls, data) {
6836         var aliases = data.alias,
6837             i, ln;
6838
6839         delete data.alias;
6840
6841         for (i = 0, ln = aliases.length; i < ln; i++) {
6842             alias = aliases[i];
6843
6844             this.setAlias(cls, alias);
6845         }
6846     });
6847
6848     /**
6849      * @cfg {Boolean} singleton
6850      * @member Ext.Class
6851      * When set to true, the class will be instantiated as singleton.  For example:
6852      *
6853      *     Ext.define('Logger', {
6854      *         singleton: true,
6855      *         log: function(msg) {
6856      *             console.log(msg);
6857      *         }
6858      *     });
6859      *
6860      *     Logger.log('Hello');
6861      */
6862     Manager.registerPostprocessor('singleton', function(name, cls, data, fn) {
6863         fn.call(this, name, new cls(), data);
6864         return false;
6865     });
6866
6867     /**
6868      * @cfg {String/String[]} alternateClassName
6869      * @member Ext.Class
6870      * Defines alternate names for this class.  For example:
6871      *
6872      *     Ext.define('Developer', {
6873      *         alternateClassName: ['Coder', 'Hacker'],
6874      *         code: function(msg) {
6875      *             alert('Typing... ' + msg);
6876      *         }
6877      *     });
6878      *
6879      *     var joe = Ext.create('Developer');
6880      *     joe.code('stackoverflow');
6881      *
6882      *     var rms = Ext.create('Hacker');
6883      *     rms.code('hack hack');
6884      */
6885     Manager.registerPostprocessor('alternateClassName', function(name, cls, data) {
6886         var alternates = data.alternateClassName,
6887             i, ln, alternate;
6888
6889         if (!(alternates instanceof Array)) {
6890             alternates = [alternates];
6891         }
6892
6893         for (i = 0, ln = alternates.length; i < ln; i++) {
6894             alternate = alternates[i];
6895
6896
6897             this.set(alternate, cls);
6898         }
6899     });
6900
6901     Manager.setDefaultPostprocessors(['alias', 'singleton', 'alternateClassName']);
6902
6903     Ext.apply(Ext, {
6904         /**
6905          * @method
6906          * @member Ext
6907          * @alias Ext.ClassManager#instantiate
6908          */
6909         create: alias(Manager, 'instantiate'),
6910
6911         /**
6912          * @private
6913          * API to be stablized
6914          *
6915          * @param {Object} item
6916          * @param {String} namespace
6917          */
6918         factory: function(item, namespace) {
6919             if (item instanceof Array) {
6920                 var i, ln;
6921
6922                 for (i = 0, ln = item.length; i < ln; i++) {
6923                     item[i] = Ext.factory(item[i], namespace);
6924                 }
6925
6926                 return item;
6927             }
6928
6929             var isString = (typeof item === 'string');
6930
6931             if (isString || (item instanceof Object && item.constructor === Object)) {
6932                 var name, config = {};
6933
6934                 if (isString) {
6935                     name = item;
6936                 }
6937                 else {
6938                     name = item.className;
6939                     config = item;
6940                     delete config.className;
6941                 }
6942
6943                 if (namespace !== undefined && name.indexOf(namespace) === -1) {
6944                     name = namespace + '.' + Ext.String.capitalize(name);
6945                 }
6946
6947                 return Ext.create(name, config);
6948             }
6949
6950             if (typeof item === 'function') {
6951                 return Ext.create(item);
6952             }
6953
6954             return item;
6955         },
6956
6957         /**
6958          * Convenient shorthand to create a widget by its xtype, also see {@link Ext.ClassManager#instantiateByAlias}
6959          *
6960          *     var button = Ext.widget('button'); // Equivalent to Ext.create('widget.button')
6961          *     var panel = Ext.widget('panel'); // Equivalent to Ext.create('widget.panel')
6962          *
6963          * @method
6964          * @member Ext
6965          * @param {String} name  xtype of the widget to create.
6966          * @param {Object...} args  arguments for the widget constructor.
6967          * @return {Object} widget instance
6968          */
6969         widget: function(name) {
6970             var args = slice.call(arguments);
6971             args[0] = 'widget.' + name;
6972
6973             return Manager.instantiateByAlias.apply(Manager, args);
6974         },
6975
6976         /**
6977          * @method
6978          * @member Ext
6979          * @alias Ext.ClassManager#instantiateByAlias
6980          */
6981         createByAlias: alias(Manager, 'instantiateByAlias'),
6982
6983         /**
6984          * @cfg {String} override
6985          * @member Ext.Class
6986          * 
6987          * Defines an override applied to a class. Note that **overrides can only be created using
6988          * {@link Ext#define}.** {@link Ext.ClassManager#create} only creates classes.
6989          * 
6990          * To define an override, include the override property. The content of an override is
6991          * aggregated with the specified class in order to extend or modify that class. This can be
6992          * as simple as setting default property values or it can extend and/or replace methods.
6993          * This can also extend the statics of the class.
6994          *
6995          * One use for an override is to break a large class into manageable pieces.
6996          *
6997          *      // File: /src/app/Panel.js
6998          *
6999          *      Ext.define('My.app.Panel', {
7000          *          extend: 'Ext.panel.Panel',
7001          *          requires: [
7002          *              'My.app.PanelPart2',
7003          *              'My.app.PanelPart3'
7004          *          ]
7005          *
7006          *          constructor: function (config) {
7007          *              this.callSuper(arguments); // calls Ext.panel.Panel's constructor
7008          *              //...
7009          *          },
7010          *
7011          *          statics: {
7012          *              method: function () {
7013          *                  return 'abc';
7014          *              }
7015          *          }
7016          *      });
7017          *
7018          *      // File: /src/app/PanelPart2.js
7019          *      Ext.define('My.app.PanelPart2', {
7020          *          override: 'My.app.Panel',
7021          *
7022          *          constructor: function (config) {
7023          *              this.callSuper(arguments); // calls My.app.Panel's constructor
7024          *              //...
7025          *          }
7026          *      });
7027          *
7028          * Another use of overrides is to provide optional parts of classes that can be
7029          * independently required. In this case, the class may even be unaware of the
7030          * override altogether.
7031          *
7032          *      Ext.define('My.ux.CoolTip', {
7033          *          override: 'Ext.tip.ToolTip',
7034          *
7035          *          constructor: function (config) {
7036          *              this.callSuper(arguments); // calls Ext.tip.ToolTip's constructor
7037          *              //...
7038          *          }
7039          *      });
7040          *
7041          * The above override can now be required as normal.
7042          *
7043          *      Ext.define('My.app.App', {
7044          *          requires: [
7045          *              'My.ux.CoolTip'
7046          *          ]
7047          *      });
7048          *
7049          * Overrides can also contain statics:
7050          *
7051          *      Ext.define('My.app.BarMod', {
7052          *          override: 'Ext.foo.Bar',
7053          *
7054          *          statics: {
7055          *              method: function (x) {
7056          *                  return this.callSuper([x * 2]); // call Ext.foo.Bar.method
7057          *              }
7058          *          }
7059          *      });
7060          *
7061          * IMPORTANT: An override is only included in a build if the class it overrides is
7062          * required. Otherwise, the override, like the target class, is not included.
7063          */
7064         
7065         /**
7066          * @method
7067          *
7068          * @member Ext
7069          * @alias Ext.ClassManager#create
7070          */
7071         define: function (className, data, createdFn) {
7072             if (!data.override) {
7073                 return Manager.create.apply(Manager, arguments);
7074             }
7075
7076             var requires = data.requires,
7077                 uses = data.uses,
7078                 overrideName = className;
7079
7080             className = data.override;
7081
7082             // hoist any 'requires' or 'uses' from the body onto the faux class:
7083             data = Ext.apply({}, data);
7084             delete data.requires;
7085             delete data.uses;
7086             delete data.override;
7087
7088             // make sure className is in the requires list:
7089             if (typeof requires == 'string') {
7090                 requires = [ className, requires ];
7091             } else if (requires) {
7092                 requires = requires.slice(0);
7093                 requires.unshift(className);
7094             } else {
7095                 requires = [ className ];
7096             }
7097
7098 // TODO - we need to rework this to allow the override to not require the target class
7099 //  and rather 'wait' for it in such a way that if the target class is not in the build,
7100 //  neither are any of its overrides.
7101 //
7102 //  Also, this should process the overrides for a class ASAP (ideally before any derived
7103 //  classes) if the target class 'requires' the overrides. Without some special handling, the
7104 //  overrides so required will be processed before the class and have to be bufferred even
7105 //  in a build.
7106 //
7107 // TODO - we should probably support the "config" processor on an override (to config new
7108 //  functionaliy like Aria) and maybe inheritableStatics (although static is now supported
7109 //  by callSuper). If inheritableStatics causes those statics to be included on derived class
7110 //  constructors, that probably means "no" to this since an override can come after other
7111 //  classes extend the target.
7112             return Manager.create(overrideName, {
7113                     requires: requires,
7114                     uses: uses,
7115                     isPartial: true,
7116                     constructor: function () {
7117                     }
7118                 }, function () {
7119                     var cls = Manager.get(className);
7120                     if (cls.override) { // if (normal class)
7121                         cls.override(data);
7122                     } else { // else (singleton)
7123                         cls.self.override(data);
7124                     }
7125
7126                     if (createdFn) {
7127                         // called once the override is applied and with the context of the
7128                         // overridden class (the override itself is a meaningless, name-only
7129                         // thing).
7130                         createdFn.call(cls);
7131                     }
7132                 });
7133         },
7134
7135         /**
7136          * @method
7137          * @member Ext
7138          * @alias Ext.ClassManager#getName
7139          */
7140         getClassName: alias(Manager, 'getName'),
7141
7142         /**
7143          * Returns the displayName property or className or object.
7144          * When all else fails, returns "Anonymous".
7145          * @param {Object} object
7146          * @return {String}
7147          */
7148         getDisplayName: function(object) {
7149             if (object.displayName) {
7150                 return object.displayName;
7151             }
7152
7153             if (object.$name && object.$class) {
7154                 return Ext.getClassName(object.$class) + '#' + object.$name;
7155             }
7156
7157             if (object.$className) {
7158                 return object.$className;
7159             }
7160
7161             return 'Anonymous';
7162         },
7163
7164         /**
7165          * @method
7166          * @member Ext
7167          * @alias Ext.ClassManager#getClass
7168          */
7169         getClass: alias(Manager, 'getClass'),
7170
7171         /**
7172          * Creates namespaces to be used for scoping variables and classes so that they are not global.
7173          * Specifying the last node of a namespace implicitly creates all other nodes. Usage:
7174          *
7175          *     Ext.namespace('Company', 'Company.data');
7176          *
7177          *     // equivalent and preferable to the above syntax
7178          *     Ext.namespace('Company.data');
7179          *
7180          *     Company.Widget = function() { ... };
7181          *
7182          *     Company.data.CustomStore = function(config) { ... };
7183          *
7184          * @method
7185          * @member Ext
7186          * @param {String} namespace1
7187          * @param {String} namespace2
7188          * @param {String} etc
7189          * @return {Object} The namespace object. (If multiple arguments are passed, this will be the last namespace created)
7190          */
7191         namespace: alias(Manager, 'createNamespaces')
7192     });
7193
7194     /**
7195      * Old name for {@link Ext#widget}.
7196      * @deprecated 4.0.0 Use {@link Ext#widget} instead.
7197      * @method
7198      * @member Ext
7199      * @alias Ext#widget
7200      */
7201     Ext.createWidget = Ext.widget;
7202
7203     /**
7204      * Convenient alias for {@link Ext#namespace Ext.namespace}
7205      * @method
7206      * @member Ext
7207      * @alias Ext#namespace
7208      */
7209     Ext.ns = Ext.namespace;
7210
7211     Class.registerPreprocessor('className', function(cls, data) {
7212         if (data.$className) {
7213             cls.$className = data.$className;
7214         }
7215     }, true);
7216
7217     Class.setDefaultPreprocessorPosition('className', 'first');
7218
7219     Class.registerPreprocessor('xtype', function(cls, data) {
7220         var xtypes = Ext.Array.from(data.xtype),
7221             widgetPrefix = 'widget.',
7222             aliases = Ext.Array.from(data.alias),
7223             i, ln, xtype;
7224
7225         data.xtype = xtypes[0];
7226         data.xtypes = xtypes;
7227
7228         aliases = data.alias = Ext.Array.from(data.alias);
7229
7230         for (i = 0,ln = xtypes.length; i < ln; i++) {
7231             xtype = xtypes[i];
7232
7233
7234             aliases.push(widgetPrefix + xtype);
7235         }
7236
7237         data.alias = aliases;
7238     });
7239
7240     Class.setDefaultPreprocessorPosition('xtype', 'last');
7241
7242     Class.registerPreprocessor('alias', function(cls, data) {
7243         var aliases = Ext.Array.from(data.alias),
7244             xtypes = Ext.Array.from(data.xtypes),
7245             widgetPrefix = 'widget.',
7246             widgetPrefixLength = widgetPrefix.length,
7247             i, ln, alias, xtype;
7248
7249         for (i = 0, ln = aliases.length; i < ln; i++) {
7250             alias = aliases[i];
7251
7252
7253             if (alias.substring(0, widgetPrefixLength) === widgetPrefix) {
7254                 xtype = alias.substring(widgetPrefixLength);
7255                 Ext.Array.include(xtypes, xtype);
7256
7257                 if (!cls.xtype) {
7258                     cls.xtype = data.xtype = xtype;
7259                 }
7260             }
7261         }
7262
7263         data.alias = aliases;
7264         data.xtypes = xtypes;
7265     });
7266
7267     Class.setDefaultPreprocessorPosition('alias', 'last');
7268
7269 })(Ext.Class, Ext.Function.alias);
7270
7271 /**
7272  * @class Ext.Loader
7273  * @singleton
7274  * @author Jacky Nguyen <jacky@sencha.com>
7275  * @docauthor Jacky Nguyen <jacky@sencha.com>
7276  *
7277  * Ext.Loader is the heart of the new dynamic dependency loading capability in Ext JS 4+. It is most commonly used
7278  * via the {@link Ext#require} shorthand. Ext.Loader supports both asynchronous and synchronous loading
7279  * approaches, and leverage their advantages for the best development flow. We'll discuss about the pros and cons
7280  * of each approach:
7281  *
7282  * # Asynchronous Loading
7283  *
7284  * - Advantages:
7285  *       + Cross-domain
7286  *       + No web server needed: you can run the application via the file system protocol
7287  *     (i.e: `file://path/to/your/index.html`)
7288  *       + Best possible debugging experience: error messages come with the exact file name and line number
7289  *
7290  * - Disadvantages:
7291  *       + Dependencies need to be specified before-hand
7292  *
7293  * ### Method 1: Explicitly include what you need:
7294  *
7295  *     // Syntax
7296  *     Ext.require({String/Array} expressions);
7297  *
7298  *     // Example: Single alias
7299  *     Ext.require('widget.window');
7300  *
7301  *     // Example: Single class name
7302  *     Ext.require('Ext.window.Window');
7303  *
7304  *     // Example: Multiple aliases / class names mix
7305  *     Ext.require(['widget.window', 'layout.border', 'Ext.data.Connection']);
7306  *
7307  *     // Wildcards
7308  *     Ext.require(['widget.*', 'layout.*', 'Ext.data.*']);
7309  *
7310  * ### Method 2: Explicitly exclude what you don't need:
7311  *
7312  *     // Syntax: Note that it must be in this chaining format.
7313  *     Ext.exclude({String/Array} expressions)
7314  *        .require({String/Array} expressions);
7315  *
7316  *     // Include everything except Ext.data.*
7317  *     Ext.exclude('Ext.data.*').require('*'); 
7318  *
7319  *     // Include all widgets except widget.checkbox*,
7320  *     // which will match widget.checkbox, widget.checkboxfield, widget.checkboxgroup, etc.
7321  *     Ext.exclude('widget.checkbox*').require('widget.*');
7322  *
7323  * # Synchronous Loading on Demand
7324  *
7325  * - Advantages:
7326  *       + There's no need to specify dependencies before-hand, which is always the convenience of including
7327  *     ext-all.js before
7328  *
7329  * - Disadvantages:
7330  *       + Not as good debugging experience since file name won't be shown (except in Firebug at the moment)
7331  *       + Must be from the same domain due to XHR restriction
7332  *       + Need a web server, same reason as above
7333  *
7334  * There's one simple rule to follow: Instantiate everything with Ext.create instead of the `new` keyword
7335  *
7336  *     Ext.create('widget.window', { ... }); // Instead of new Ext.window.Window({...});
7337  *
7338  *     Ext.create('Ext.window.Window', {}); // Same as above, using full class name instead of alias
7339  *
7340  *     Ext.widget('window', {}); // Same as above, all you need is the traditional `xtype`
7341  *
7342  * Behind the scene, {@link Ext.ClassManager} will automatically check whether the given class name / alias has already
7343  * existed on the page. If it's not, Ext.Loader will immediately switch itself to synchronous mode and automatic load
7344  * the given class and all its dependencies.
7345  *
7346  * # Hybrid Loading - The Best of Both Worlds
7347  *
7348  * It has all the advantages combined from asynchronous and synchronous loading. The development flow is simple:
7349  *
7350  * ### Step 1: Start writing your application using synchronous approach.
7351  *
7352  * Ext.Loader will automatically fetch all dependencies on demand as they're needed during run-time. For example:
7353  *
7354  *     Ext.onReady(function(){
7355  *         var window = Ext.createWidget('window', {
7356  *             width: 500,
7357  *             height: 300,
7358  *             layout: {
7359  *                 type: 'border',
7360  *                 padding: 5
7361  *             },
7362  *             title: 'Hello Dialog',
7363  *             items: [{
7364  *                 title: 'Navigation',
7365  *                 collapsible: true,
7366  *                 region: 'west',
7367  *                 width: 200,
7368  *                 html: 'Hello',
7369  *                 split: true
7370  *             }, {
7371  *                 title: 'TabPanel',
7372  *                 region: 'center'
7373  *             }]
7374  *         });
7375  *
7376  *         window.show();
7377  *     })
7378  *
7379  * ### Step 2: Along the way, when you need better debugging ability, watch the console for warnings like these:
7380  *
7381  *     [Ext.Loader] Synchronously loading 'Ext.window.Window'; consider adding Ext.require('Ext.window.Window') before your application's code ClassManager.js:432
7382  *     [Ext.Loader] Synchronously loading 'Ext.layout.container.Border'; consider adding Ext.require('Ext.layout.container.Border') before your application's code
7383  *
7384  * Simply copy and paste the suggested code above `Ext.onReady`, e.g.:
7385  *
7386  *     Ext.require('Ext.window.Window');
7387  *     Ext.require('Ext.layout.container.Border');
7388  *
7389  *     Ext.onReady(...);
7390  *
7391  * Everything should now load via asynchronous mode.
7392  *
7393  * # Deployment
7394  *
7395  * It's important to note that dynamic loading should only be used during development on your local machines.
7396  * During production, all dependencies should be combined into one single JavaScript file. Ext.Loader makes
7397  * the whole process of transitioning from / to between development / maintenance and production as easy as
7398  * possible. Internally {@link Ext.Loader#history Ext.Loader.history} maintains the list of all dependencies
7399  * your application needs in the exact loading sequence. It's as simple as concatenating all files in this
7400  * array into one, then include it on top of your application.
7401  *
7402  * This process will be automated with Sencha Command, to be released and documented towards Ext JS 4 Final.
7403  */
7404 (function(Manager, Class, flexSetter, alias) {
7405
7406     var
7407         dependencyProperties = ['extend', 'mixins', 'requires'],
7408         Loader;
7409
7410     Loader = Ext.Loader = {
7411         /**
7412          * @private
7413          */
7414         documentHead: typeof document !== 'undefined' && (document.head || document.getElementsByTagName('head')[0]),
7415
7416         /**
7417          * Flag indicating whether there are still files being loaded
7418          * @private
7419          */
7420         isLoading: false,
7421
7422         /**
7423          * Maintain the queue for all dependencies. Each item in the array is an object of the format:
7424          * {
7425          *      requires: [...], // The required classes for this queue item
7426          *      callback: function() { ... } // The function to execute when all classes specified in requires exist
7427          * }
7428          * @private
7429          */
7430         queue: [],
7431
7432         /**
7433          * Maintain the list of files that have already been handled so that they never get double-loaded
7434          * @private
7435          */
7436         isFileLoaded: {},
7437
7438         /**
7439          * Maintain the list of listeners to execute when all required scripts are fully loaded
7440          * @private
7441          */
7442         readyListeners: [],
7443
7444         /**
7445          * Contains optional dependencies to be loaded last
7446          * @private
7447          */
7448         optionalRequires: [],
7449
7450         /**
7451          * Map of fully qualified class names to an array of dependent classes.
7452          * @private
7453          */
7454         requiresMap: {},
7455
7456         /**
7457          * @private
7458          */
7459         numPendingFiles: 0,
7460
7461         /**
7462          * @private
7463          */
7464         numLoadedFiles: 0,
7465
7466         /** @private */
7467         hasFileLoadError: false,
7468
7469         /**
7470          * @private
7471          */
7472         classNameToFilePathMap: {},
7473
7474         /**
7475          * @property {String[]} history
7476          * An array of class names to keep track of the dependency loading order.
7477          * This is not guaranteed to be the same everytime due to the asynchronous nature of the Loader.
7478          */
7479         history: [],
7480
7481         /**
7482          * Configuration
7483          * @private
7484          */
7485         config: {
7486             /**
7487              * @cfg {Boolean} enabled
7488              * Whether or not to enable the dynamic dependency loading feature.
7489              */
7490             enabled: false,
7491
7492             /**
7493              * @cfg {Boolean} disableCaching
7494              * Appends current timestamp to script files to prevent caching.
7495              */
7496             disableCaching: true,
7497
7498             /**
7499              * @cfg {String} disableCachingParam
7500              * The get parameter name for the cache buster's timestamp.
7501              */
7502             disableCachingParam: '_dc',
7503
7504             /**
7505              * @cfg {Object} paths
7506              * The mapping from namespaces to file paths
7507              *
7508              *     {
7509              *         'Ext': '.', // This is set by default, Ext.layout.container.Container will be
7510              *                     // loaded from ./layout/Container.js
7511              *
7512              *         'My': './src/my_own_folder' // My.layout.Container will be loaded from
7513              *                                     // ./src/my_own_folder/layout/Container.js
7514              *     }
7515              *
7516              * Note that all relative paths are relative to the current HTML document.
7517              * If not being specified, for example, `Other.awesome.Class`
7518              * will simply be loaded from `./Other/awesome/Class.js`
7519              */
7520             paths: {
7521                 'Ext': '.'
7522             }
7523         },
7524
7525         /**
7526          * Set the configuration for the loader. This should be called right after ext-core.js
7527          * (or ext-core-debug.js) is included in the page, e.g.:
7528          *
7529          *     <script type="text/javascript" src="ext-core-debug.js"></script>
7530          *     <script type="text/javascript">
7531          *       Ext.Loader.setConfig({
7532          *           enabled: true,
7533          *           paths: {
7534          *               'My': 'my_own_path'
7535          *           }
7536          *       });
7537          *     <script>
7538          *     <script type="text/javascript">
7539          *       Ext.require(...);
7540          *
7541          *       Ext.onReady(function() {
7542          *           // application code here
7543          *       });
7544          *     </script>
7545          *
7546          * Refer to config options of {@link Ext.Loader} for the list of possible properties.
7547          *
7548          * @param {String/Object} name  Name of the value to override, or a config object to override multiple values.
7549          * @param {Object} value  (optional) The new value to set, needed if first parameter is String.
7550          * @return {Ext.Loader} this
7551          */
7552         setConfig: function(name, value) {
7553             if (Ext.isObject(name) && arguments.length === 1) {
7554                 Ext.Object.merge(this.config, name);
7555             }
7556             else {
7557                 this.config[name] = (Ext.isObject(value)) ? Ext.Object.merge(this.config[name], value) : value;
7558             }
7559
7560             return this;
7561         },
7562
7563         /**
7564          * Get the config value corresponding to the specified name.
7565          * If no name is given, will return the config object.
7566          * @param {String} name The config property name
7567          * @return {Object}
7568          */
7569         getConfig: function(name) {
7570             if (name) {
7571                 return this.config[name];
7572             }
7573
7574             return this.config;
7575         },
7576
7577         /**
7578          * Sets the path of a namespace. For Example:
7579          *
7580          *     Ext.Loader.setPath('Ext', '.');
7581          *
7582          * @param {String/Object} name See {@link Ext.Function#flexSetter flexSetter}
7583          * @param {String} path See {@link Ext.Function#flexSetter flexSetter}
7584          * @return {Ext.Loader} this
7585          * @method
7586          */
7587         setPath: flexSetter(function(name, path) {
7588             this.config.paths[name] = path;
7589
7590             return this;
7591         }),
7592
7593         /**
7594          * Translates a className to a file path by adding the the proper prefix and converting the .'s to /'s.
7595          * For example:
7596          *
7597          *     Ext.Loader.setPath('My', '/path/to/My');
7598          *
7599          *     alert(Ext.Loader.getPath('My.awesome.Class')); // alerts '/path/to/My/awesome/Class.js'
7600          *
7601          * Note that the deeper namespace levels, if explicitly set, are always resolved first. For example:
7602          *
7603          *     Ext.Loader.setPath({
7604          *         'My': '/path/to/lib',
7605          *         'My.awesome': '/other/path/for/awesome/stuff',
7606          *         'My.awesome.more': '/more/awesome/path'
7607          *     });
7608          *
7609          *     alert(Ext.Loader.getPath('My.awesome.Class')); // alerts '/other/path/for/awesome/stuff/Class.js'
7610          *
7611          *     alert(Ext.Loader.getPath('My.awesome.more.Class')); // alerts '/more/awesome/path/Class.js'
7612          *
7613          *     alert(Ext.Loader.getPath('My.cool.Class')); // alerts '/path/to/lib/cool/Class.js'
7614          *
7615          *     alert(Ext.Loader.getPath('Unknown.strange.Stuff')); // alerts 'Unknown/strange/Stuff.js'
7616          *
7617          * @param {String} className
7618          * @return {String} path
7619          */
7620         getPath: function(className) {
7621             var path = '',
7622                 paths = this.config.paths,
7623                 prefix = this.getPrefix(className);
7624
7625             if (prefix.length > 0) {
7626                 if (prefix === className) {
7627                     return paths[prefix];
7628                 }
7629
7630                 path = paths[prefix];
7631                 className = className.substring(prefix.length + 1);
7632             }
7633
7634             if (path.length > 0) {
7635                 path += '/';
7636             }
7637
7638             return path.replace(/\/\.\//g, '/') + className.replace(/\./g, "/") + '.js';
7639         },
7640
7641         /**
7642          * @private
7643          * @param {String} className
7644          */
7645         getPrefix: function(className) {
7646             var paths = this.config.paths,
7647                 prefix, deepestPrefix = '';
7648
7649             if (paths.hasOwnProperty(className)) {
7650                 return className;
7651             }
7652
7653             for (prefix in paths) {
7654                 if (paths.hasOwnProperty(prefix) && prefix + '.' === className.substring(0, prefix.length + 1)) {
7655                     if (prefix.length > deepestPrefix.length) {
7656                         deepestPrefix = prefix;
7657                     }
7658                 }
7659             }
7660
7661             return deepestPrefix;
7662         },
7663
7664         /**
7665          * Refresh all items in the queue. If all dependencies for an item exist during looping,
7666          * it will execute the callback and call refreshQueue again. Triggers onReady when the queue is
7667          * empty
7668          * @private
7669          */
7670         refreshQueue: function() {
7671             var ln = this.queue.length,
7672                 i, item, j, requires;
7673
7674             if (ln === 0) {
7675                 this.triggerReady();
7676                 return;
7677             }
7678
7679             for (i = 0; i < ln; i++) {
7680                 item = this.queue[i];
7681
7682                 if (item) {
7683                     requires = item.requires;
7684
7685                     // Don't bother checking when the number of files loaded
7686                     // is still less than the array length
7687                     if (requires.length > this.numLoadedFiles) {
7688                         continue;
7689                     }
7690
7691                     j = 0;
7692
7693                     do {
7694                         if (Manager.isCreated(requires[j])) {
7695                             // Take out from the queue
7696                             Ext.Array.erase(requires, j, 1);
7697                         }
7698                         else {
7699                             j++;
7700                         }
7701                     } while (j < requires.length);
7702
7703                     if (item.requires.length === 0) {
7704                         Ext.Array.erase(this.queue, i, 1);
7705                         item.callback.call(item.scope);
7706                         this.refreshQueue();
7707                         break;
7708                     }
7709                 }
7710             }
7711
7712             return this;
7713         },
7714
7715         /**
7716          * Inject a script element to document's head, call onLoad and onError accordingly
7717          * @private
7718          */
7719         injectScriptElement: function(url, onLoad, onError, scope) {
7720             var script = document.createElement('script'),
7721                 me = this,
7722                 onLoadFn = function() {
7723                     me.cleanupScriptElement(script);
7724                     onLoad.call(scope);
7725                 },
7726                 onErrorFn = function() {
7727                     me.cleanupScriptElement(script);
7728                     onError.call(scope);
7729                 };
7730
7731             script.type = 'text/javascript';
7732             script.src = url;
7733             script.onload = onLoadFn;
7734             script.onerror = onErrorFn;
7735             script.onreadystatechange = function() {
7736                 if (this.readyState === 'loaded' || this.readyState === 'complete') {
7737                     onLoadFn();
7738                 }
7739             };
7740
7741             this.documentHead.appendChild(script);
7742
7743             return script;
7744         },
7745
7746         /**
7747          * @private
7748          */
7749         cleanupScriptElement: function(script) {
7750             script.onload = null;
7751             script.onreadystatechange = null;
7752             script.onerror = null;
7753
7754             return this;
7755         },
7756
7757         /**
7758          * Load a script file, supports both asynchronous and synchronous approaches
7759          *
7760          * @param {String} url
7761          * @param {Function} onLoad
7762          * @param {Object} scope
7763          * @param {Boolean} synchronous
7764          * @private
7765          */
7766         loadScriptFile: function(url, onLoad, onError, scope, synchronous) {
7767             var me = this,
7768                 noCacheUrl = url + (this.getConfig('disableCaching') ? ('?' + this.getConfig('disableCachingParam') + '=' + Ext.Date.now()) : ''),
7769                 fileName = url.split('/').pop(),
7770                 isCrossOriginRestricted = false,
7771                 xhr, status, onScriptError;
7772
7773             scope = scope || this;
7774
7775             this.isLoading = true;
7776
7777             if (!synchronous) {
7778                 onScriptError = function() {
7779                     onError.call(scope, "Failed loading '" + url + "', please verify that the file exists", synchronous);
7780                 };
7781
7782                 if (!Ext.isReady && Ext.onDocumentReady) {
7783                     Ext.onDocumentReady(function() {
7784                         me.injectScriptElement(noCacheUrl, onLoad, onScriptError, scope);
7785                     });
7786                 }
7787                 else {
7788                     this.injectScriptElement(noCacheUrl, onLoad, onScriptError, scope);
7789                 }
7790             }
7791             else {
7792                 if (typeof XMLHttpRequest !== 'undefined') {
7793                     xhr = new XMLHttpRequest();
7794                 } else {
7795                     xhr = new ActiveXObject('Microsoft.XMLHTTP');
7796                 }
7797
7798                 try {
7799                     xhr.open('GET', noCacheUrl, false);
7800                     xhr.send(null);
7801                 } catch (e) {
7802                     isCrossOriginRestricted = true;
7803                 }
7804
7805                 status = (xhr.status === 1223) ? 204 : xhr.status;
7806
7807                 if (!isCrossOriginRestricted) {
7808                     isCrossOriginRestricted = (status === 0);
7809                 }
7810
7811                 if (isCrossOriginRestricted
7812                 ) {
7813                     onError.call(this, "Failed loading synchronously via XHR: '" + url + "'; It's likely that the file is either " +
7814                                        "being loaded from a different domain or from the local file system whereby cross origin " +
7815                                        "requests are not allowed due to security reasons. Use asynchronous loading with " +
7816                                        "Ext.require instead.", synchronous);
7817                 }
7818                 else if (status >= 200 && status < 300
7819                 ) {
7820                     // Firebug friendly, file names are still shown even though they're eval'ed code
7821                     new Function(xhr.responseText + "\n//@ sourceURL=" + fileName)();
7822
7823                     onLoad.call(scope);
7824                 }
7825                 else {
7826                     onError.call(this, "Failed loading synchronously via XHR: '" + url + "'; please " +
7827                                        "verify that the file exists. " +
7828                                        "XHR status code: " + status, synchronous);
7829                 }
7830
7831                 // Prevent potential IE memory leak
7832                 xhr = null;
7833             }
7834         },
7835
7836         /**
7837          * Explicitly exclude files from being loaded. Useful when used in conjunction with a broad include expression.
7838          * Can be chained with more `require` and `exclude` methods, e.g.:
7839          *
7840          *     Ext.exclude('Ext.data.*').require('*');
7841          *
7842          *     Ext.exclude('widget.button*').require('widget.*');
7843          *
7844          * {@link Ext#exclude Ext.exclude} is alias for {@link Ext.Loader#exclude Ext.Loader.exclude} for convenience.
7845          *
7846          * @param {String/String[]} excludes
7847          * @return {Object} object contains `require` method for chaining
7848          */
7849         exclude: function(excludes) {
7850             var me = this;
7851
7852             return {
7853                 require: function(expressions, fn, scope) {
7854                     return me.require(expressions, fn, scope, excludes);
7855                 },
7856
7857                 syncRequire: function(expressions, fn, scope) {
7858                     return me.syncRequire(expressions, fn, scope, excludes);
7859                 }
7860             };
7861         },
7862
7863         /**
7864          * Synchronously loads all classes by the given names and all their direct dependencies;
7865          * optionally executes the given callback function when finishes, within the optional scope.
7866          *
7867          * {@link Ext#syncRequire Ext.syncRequire} is alias for {@link Ext.Loader#syncRequire Ext.Loader.syncRequire} for convenience.
7868          *
7869          * @param {String/String[]} expressions Can either be a string or an array of string
7870          * @param {Function} fn (Optional) The callback function
7871          * @param {Object} scope (Optional) The execution scope (`this`) of the callback function
7872          * @param {String/String[]} excludes (Optional) Classes to be excluded, useful when being used with expressions
7873          */
7874         syncRequire: function() {
7875             this.syncModeEnabled = true;
7876             this.require.apply(this, arguments);
7877             this.refreshQueue();
7878             this.syncModeEnabled = false;
7879         },
7880
7881         /**
7882          * Loads all classes by the given names and all their direct dependencies;
7883          * optionally executes the given callback function when finishes, within the optional scope.
7884          *
7885          * {@link Ext#require Ext.require} is alias for {@link Ext.Loader#require Ext.Loader.require} for convenience.
7886          *
7887          * @param {String/String[]} expressions Can either be a string or an array of string
7888          * @param {Function} fn (Optional) The callback function
7889          * @param {Object} scope (Optional) The execution scope (`this`) of the callback function
7890          * @param {String/String[]} excludes (Optional) Classes to be excluded, useful when being used with expressions
7891          */
7892         require: function(expressions, fn, scope, excludes) {
7893             var filePath, expression, exclude, className, excluded = {},
7894                 excludedClassNames = [],
7895                 possibleClassNames = [],
7896                 possibleClassName, classNames = [],
7897                 i, j, ln, subLn;
7898
7899             expressions = Ext.Array.from(expressions);
7900             excludes = Ext.Array.from(excludes);
7901
7902             fn = fn || Ext.emptyFn;
7903
7904             scope = scope || Ext.global;
7905
7906             for (i = 0, ln = excludes.length; i < ln; i++) {
7907                 exclude = excludes[i];
7908
7909                 if (typeof exclude === 'string' && exclude.length > 0) {
7910                     excludedClassNames = Manager.getNamesByExpression(exclude);
7911
7912                     for (j = 0, subLn = excludedClassNames.length; j < subLn; j++) {
7913                         excluded[excludedClassNames[j]] = true;
7914                     }
7915                 }
7916             }
7917
7918             for (i = 0, ln = expressions.length; i < ln; i++) {
7919                 expression = expressions[i];
7920
7921                 if (typeof expression === 'string' && expression.length > 0) {
7922                     possibleClassNames = Manager.getNamesByExpression(expression);
7923
7924                     for (j = 0, subLn = possibleClassNames.length; j < subLn; j++) {
7925                         possibleClassName = possibleClassNames[j];
7926
7927                         if (!excluded.hasOwnProperty(possibleClassName) && !Manager.isCreated(possibleClassName)) {
7928                             Ext.Array.include(classNames, possibleClassName);
7929                         }
7930                     }
7931                 }
7932             }
7933
7934             // If the dynamic dependency feature is not being used, throw an error
7935             // if the dependencies are not defined
7936             if (!this.config.enabled) {
7937                 if (classNames.length > 0) {
7938                     Ext.Error.raise({
7939                         sourceClass: "Ext.Loader",
7940                         sourceMethod: "require",
7941                         msg: "Ext.Loader is not enabled, so dependencies cannot be resolved dynamically. " +
7942                              "Missing required class" + ((classNames.length > 1) ? "es" : "") + ": " + classNames.join(', ')
7943                     });
7944                 }
7945             }
7946
7947             if (classNames.length === 0) {
7948                 fn.call(scope);
7949                 return this;
7950             }
7951
7952             this.queue.push({
7953                 requires: classNames,
7954                 callback: fn,
7955                 scope: scope
7956             });
7957
7958             classNames = classNames.slice();
7959
7960             for (i = 0, ln = classNames.length; i < ln; i++) {
7961                 className = classNames[i];
7962
7963                 if (!this.isFileLoaded.hasOwnProperty(className)) {
7964                     this.isFileLoaded[className] = false;
7965
7966                     filePath = this.getPath(className);
7967
7968                     this.classNameToFilePathMap[className] = filePath;
7969
7970                     this.numPendingFiles++;
7971
7972                     this.loadScriptFile(
7973                         filePath,
7974                         Ext.Function.pass(this.onFileLoaded, [className, filePath], this),
7975                         Ext.Function.pass(this.onFileLoadError, [className, filePath]),
7976                         this,
7977                         this.syncModeEnabled
7978                     );
7979                 }
7980             }
7981
7982             return this;
7983         },
7984
7985         /**
7986          * @private
7987          * @param {String} className
7988          * @param {String} filePath
7989          */
7990         onFileLoaded: function(className, filePath) {
7991             this.numLoadedFiles++;
7992
7993             this.isFileLoaded[className] = true;
7994
7995             this.numPendingFiles--;
7996
7997             if (this.numPendingFiles === 0) {
7998                 this.refreshQueue();
7999             }
8000
8001
8002         },
8003
8004         /**
8005          * @private
8006          */
8007         onFileLoadError: function(className, filePath, errorMessage, isSynchronous) {
8008             this.numPendingFiles--;
8009             this.hasFileLoadError = true;
8010
8011         },
8012
8013         /**
8014          * @private
8015          */
8016         addOptionalRequires: function(requires) {
8017             var optionalRequires = this.optionalRequires,
8018                 i, ln, require;
8019
8020             requires = Ext.Array.from(requires);
8021
8022             for (i = 0, ln = requires.length; i < ln; i++) {
8023                 require = requires[i];
8024
8025                 Ext.Array.include(optionalRequires, require);
8026             }
8027
8028             return this;
8029         },
8030
8031         /**
8032          * @private
8033          */
8034         triggerReady: function(force) {
8035             var readyListeners = this.readyListeners,
8036                 optionalRequires, listener;
8037
8038             if (this.isLoading || force) {
8039                 this.isLoading = false;
8040
8041                 if (this.optionalRequires.length) {
8042                     // Clone then empty the array to eliminate potential recursive loop issue
8043                     optionalRequires = Ext.Array.clone(this.optionalRequires);
8044
8045                     // Empty the original array
8046                     this.optionalRequires.length = 0;
8047
8048                     this.require(optionalRequires, Ext.Function.pass(this.triggerReady, [true], this), this);
8049                     return this;
8050                 }
8051
8052                 while (readyListeners.length) {
8053                     listener = readyListeners.shift();
8054                     listener.fn.call(listener.scope);
8055
8056                     if (this.isLoading) {
8057                         return this;
8058                     }
8059                 }
8060             }
8061
8062             return this;
8063         },
8064
8065         /**
8066          * Adds new listener to be executed when all required scripts are fully loaded.
8067          *
8068          * @param {Function} fn The function callback to be executed
8069          * @param {Object} scope The execution scope (`this`) of the callback function
8070          * @param {Boolean} withDomReady Whether or not to wait for document dom ready as well
8071          */
8072         onReady: function(fn, scope, withDomReady, options) {
8073             var oldFn;
8074
8075             if (withDomReady !== false && Ext.onDocumentReady) {
8076                 oldFn = fn;
8077
8078                 fn = function() {
8079                     Ext.onDocumentReady(oldFn, scope, options);
8080                 };
8081             }
8082
8083             if (!this.isLoading) {
8084                 fn.call(scope);
8085             }
8086             else {
8087                 this.readyListeners.push({
8088                     fn: fn,
8089                     scope: scope
8090                 });
8091             }
8092         },
8093
8094         /**
8095          * @private
8096          * @param {String} className
8097          */
8098         historyPush: function(className) {
8099             if (className && this.isFileLoaded.hasOwnProperty(className)) {
8100                 Ext.Array.include(this.history, className);
8101             }
8102
8103             return this;
8104         }
8105     };
8106
8107     /**
8108      * @member Ext
8109      * @method require
8110      * @alias Ext.Loader#require
8111      */
8112     Ext.require = alias(Loader, 'require');
8113
8114     /**
8115      * @member Ext
8116      * @method syncRequire
8117      * @alias Ext.Loader#syncRequire
8118      */
8119     Ext.syncRequire = alias(Loader, 'syncRequire');
8120
8121     /**
8122      * @member Ext
8123      * @method exclude
8124      * @alias Ext.Loader#exclude
8125      */
8126     Ext.exclude = alias(Loader, 'exclude');
8127
8128     /**
8129      * @member Ext
8130      * @method onReady
8131      * @alias Ext.Loader#onReady
8132      */
8133     Ext.onReady = function(fn, scope, options) {
8134         Loader.onReady(fn, scope, true, options);
8135     };
8136
8137     /**
8138      * @cfg {String[]} requires
8139      * @member Ext.Class
8140      * List of classes that have to be loaded before instantiating this class.
8141      * For example:
8142      *
8143      *     Ext.define('Mother', {
8144      *         requires: ['Child'],
8145      *         giveBirth: function() {
8146      *             // we can be sure that child class is available.
8147      *             return new Child();
8148      *         }
8149      *     });
8150      */
8151     Class.registerPreprocessor('loader', function(cls, data, continueFn) {
8152         var me = this,
8153             dependencies = [],
8154             className = Manager.getName(cls),
8155             i, j, ln, subLn, value, propertyName, propertyValue;
8156
8157         /*
8158         Basically loop through the dependencyProperties, look for string class names and push
8159         them into a stack, regardless of whether the property's value is a string, array or object. For example:
8160         {
8161               extend: 'Ext.MyClass',
8162               requires: ['Ext.some.OtherClass'],
8163               mixins: {
8164                   observable: 'Ext.util.Observable';
8165               }
8166         }
8167         which will later be transformed into:
8168         {
8169               extend: Ext.MyClass,
8170               requires: [Ext.some.OtherClass],
8171               mixins: {
8172                   observable: Ext.util.Observable;
8173               }
8174         }
8175         */
8176
8177         for (i = 0, ln = dependencyProperties.length; i < ln; i++) {
8178             propertyName = dependencyProperties[i];
8179
8180             if (data.hasOwnProperty(propertyName)) {
8181                 propertyValue = data[propertyName];
8182
8183                 if (typeof propertyValue === 'string') {
8184                     dependencies.push(propertyValue);
8185                 }
8186                 else if (propertyValue instanceof Array) {
8187                     for (j = 0, subLn = propertyValue.length; j < subLn; j++) {
8188                         value = propertyValue[j];
8189
8190                         if (typeof value === 'string') {
8191                             dependencies.push(value);
8192                         }
8193                     }
8194                 }
8195                 else if (typeof propertyValue != 'function') {
8196                     for (j in propertyValue) {
8197                         if (propertyValue.hasOwnProperty(j)) {
8198                             value = propertyValue[j];
8199
8200                             if (typeof value === 'string') {
8201                                 dependencies.push(value);
8202                             }
8203                         }
8204                     }
8205                 }
8206             }
8207         }
8208
8209         if (dependencies.length === 0) {
8210 //            Loader.historyPush(className);
8211             return;
8212         }
8213
8214
8215         Loader.require(dependencies, function() {
8216             for (i = 0, ln = dependencyProperties.length; i < ln; i++) {
8217                 propertyName = dependencyProperties[i];
8218
8219                 if (data.hasOwnProperty(propertyName)) {
8220                     propertyValue = data[propertyName];
8221
8222                     if (typeof propertyValue === 'string') {
8223                         data[propertyName] = Manager.get(propertyValue);
8224                     }
8225                     else if (propertyValue instanceof Array) {
8226                         for (j = 0, subLn = propertyValue.length; j < subLn; j++) {
8227                             value = propertyValue[j];
8228
8229                             if (typeof value === 'string') {
8230                                 data[propertyName][j] = Manager.get(value);
8231                             }
8232                         }
8233                     }
8234                     else if (typeof propertyValue != 'function') {
8235                         for (var k in propertyValue) {
8236                             if (propertyValue.hasOwnProperty(k)) {
8237                                 value = propertyValue[k];
8238
8239                                 if (typeof value === 'string') {
8240                                     data[propertyName][k] = Manager.get(value);
8241                                 }
8242                             }
8243                         }
8244                     }
8245                 }
8246             }
8247
8248             continueFn.call(me, cls, data);
8249         });
8250
8251         return false;
8252     }, true);
8253
8254     Class.setDefaultPreprocessorPosition('loader', 'after', 'className');
8255
8256     /**
8257      * @cfg {String[]} uses
8258      * @member Ext.Class
8259      * List of classes to load together with this class.  These aren't neccessarily loaded before
8260      * this class is instantiated. For example:
8261      *
8262      *     Ext.define('Mother', {
8263      *         uses: ['Child'],
8264      *         giveBirth: function() {
8265      *             // This code might, or might not work:
8266      *             // return new Child();
8267      *
8268      *             // Instead use Ext.create() to load the class at the spot if not loaded already:
8269      *             return Ext.create('Child');
8270      *         }
8271      *     });
8272      */
8273     Manager.registerPostprocessor('uses', function(name, cls, data) {
8274         var uses = Ext.Array.from(data.uses),
8275             items = [],
8276             i, ln, item;
8277
8278         for (i = 0, ln = uses.length; i < ln; i++) {
8279             item = uses[i];
8280
8281             if (typeof item === 'string') {
8282                 items.push(item);
8283             }
8284         }
8285
8286         Loader.addOptionalRequires(items);
8287     });
8288
8289     Manager.setDefaultPostprocessorPosition('uses', 'last');
8290
8291 })(Ext.ClassManager, Ext.Class, Ext.Function.flexSetter, Ext.Function.alias);
8292
8293 /**
8294  * @author Brian Moeskau <brian@sencha.com>
8295  * @docauthor Brian Moeskau <brian@sencha.com>
8296  *
8297  * A wrapper class for the native JavaScript Error object that adds a few useful capabilities for handling
8298  * errors in an Ext application. When you use Ext.Error to {@link #raise} an error from within any class that
8299  * uses the Ext 4 class system, the Error class can automatically add the source class and method from which
8300  * the error was raised. It also includes logic to automatically log the eroor to the console, if available,
8301  * with additional metadata about the error. In all cases, the error will always be thrown at the end so that
8302  * execution will halt.
8303  *
8304  * Ext.Error also offers a global error {@link #handle handling} method that can be overridden in order to
8305  * handle application-wide errors in a single spot. You can optionally {@link #ignore} errors altogether,
8306  * although in a real application it's usually a better idea to override the handling function and perform
8307  * logging or some other method of reporting the errors in a way that is meaningful to the application.
8308  *
8309  * At its simplest you can simply raise an error as a simple string from within any code:
8310  *
8311  * Example usage:
8312  *
8313  *     Ext.Error.raise('Something bad happened!');
8314  *
8315  * If raised from plain JavaScript code, the error will be logged to the console (if available) and the message
8316  * displayed. In most cases however you'll be raising errors from within a class, and it may often be useful to add
8317  * additional metadata about the error being raised.  The {@link #raise} method can also take a config object.
8318  * In this form the `msg` attribute becomes the error description, and any other data added to the config gets
8319  * added to the error object and, if the console is available, logged to the console for inspection.
8320  *
8321  * Example usage:
8322  *
8323  *     Ext.define('Ext.Foo', {
8324  *         doSomething: function(option){
8325  *             if (someCondition === false) {
8326  *                 Ext.Error.raise({
8327  *                     msg: 'You cannot do that!',
8328  *                     option: option,   // whatever was passed into the method
8329  *                     'error code': 100 // other arbitrary info
8330  *                 });
8331  *             }
8332  *         }
8333  *     });
8334  *
8335  * If a console is available (that supports the `console.dir` function) you'll see console output like:
8336  *
8337  *     An error was raised with the following data:
8338  *     option:         Object { foo: "bar"}
8339  *         foo:        "bar"
8340  *     error code:     100
8341  *     msg:            "You cannot do that!"
8342  *     sourceClass:   "Ext.Foo"
8343  *     sourceMethod:  "doSomething"
8344  *
8345  *     uncaught exception: You cannot do that!
8346  *
8347  * As you can see, the error will report exactly where it was raised and will include as much information as the
8348  * raising code can usefully provide.
8349  *
8350  * If you want to handle all application errors globally you can simply override the static {@link #handle} method
8351  * and provide whatever handling logic you need. If the method returns true then the error is considered handled
8352  * and will not be thrown to the browser. If anything but true is returned then the error will be thrown normally.
8353  *
8354  * Example usage:
8355  *
8356  *     Ext.Error.handle = function(err) {
8357  *         if (err.someProperty == 'NotReallyAnError') {
8358  *             // maybe log something to the application here if applicable
8359  *             return true;
8360  *         }
8361  *         // any non-true return value (including none) will cause the error to be thrown
8362  *     }
8363  *
8364  */
8365 Ext.Error = Ext.extend(Error, {
8366     statics: {
8367         /**
8368          * @property {Boolean} ignore
8369          * Static flag that can be used to globally disable error reporting to the browser if set to true
8370          * (defaults to false). Note that if you ignore Ext errors it's likely that some other code may fail
8371          * and throw a native JavaScript error thereafter, so use with caution. In most cases it will probably
8372          * be preferable to supply a custom error {@link #handle handling} function instead.
8373          *
8374          * Example usage:
8375          *
8376          *     Ext.Error.ignore = true;
8377          *
8378          * @static
8379          */
8380         ignore: false,
8381
8382         /**
8383          * @property {Boolean} notify
8384          * Static flag that can be used to globally control error notification to the user. Unlike
8385          * Ex.Error.ignore, this does not effect exceptions. They are still thrown. This value can be
8386          * set to false to disable the alert notification (default is true for IE6 and IE7).
8387          *
8388          * Only the first error will generate an alert. Internally this flag is set to false when the
8389          * first error occurs prior to displaying the alert.
8390          *
8391          * This flag is not used in a release build.
8392          *
8393          * Example usage:
8394          *
8395          *     Ext.Error.notify = false;
8396          *
8397          * @static
8398          */
8399         //notify: Ext.isIE6 || Ext.isIE7,
8400
8401         /**
8402          * Raise an error that can include additional data and supports automatic console logging if available.
8403          * You can pass a string error message or an object with the `msg` attribute which will be used as the
8404          * error message. The object can contain any other name-value attributes (or objects) to be logged
8405          * along with the error.
8406          *
8407          * Note that after displaying the error message a JavaScript error will ultimately be thrown so that
8408          * execution will halt.
8409          *
8410          * Example usage:
8411          *
8412          *     Ext.Error.raise('A simple string error message');
8413          *
8414          *     // or...
8415          *
8416          *     Ext.define('Ext.Foo', {
8417          *         doSomething: function(option){
8418          *             if (someCondition === false) {
8419          *                 Ext.Error.raise({
8420          *                     msg: 'You cannot do that!',
8421          *                     option: option,   // whatever was passed into the method
8422          *                     'error code': 100 // other arbitrary info
8423          *                 });
8424          *             }
8425          *         }
8426          *     });
8427          *
8428          * @param {String/Object} err The error message string, or an object containing the attribute "msg" that will be
8429          * used as the error message. Any other data included in the object will also be logged to the browser console,
8430          * if available.
8431          * @static
8432          */
8433         raise: function(err){
8434             err = err || {};
8435             if (Ext.isString(err)) {
8436                 err = { msg: err };
8437             }
8438
8439             var method = this.raise.caller;
8440
8441             if (method) {
8442                 if (method.$name) {
8443                     err.sourceMethod = method.$name;
8444                 }
8445                 if (method.$owner) {
8446                     err.sourceClass = method.$owner.$className;
8447                 }
8448             }
8449
8450             if (Ext.Error.handle(err) !== true) {
8451                 var msg = Ext.Error.prototype.toString.call(err);
8452
8453                 Ext.log({
8454                     msg: msg,
8455                     level: 'error',
8456                     dump: err,
8457                     stack: true
8458                 });
8459
8460                 throw new Ext.Error(err);
8461             }
8462         },
8463
8464         /**
8465          * Globally handle any Ext errors that may be raised, optionally providing custom logic to
8466          * handle different errors individually. Return true from the function to bypass throwing the
8467          * error to the browser, otherwise the error will be thrown and execution will halt.
8468          *
8469          * Example usage:
8470          *
8471          *     Ext.Error.handle = function(err) {
8472          *         if (err.someProperty == 'NotReallyAnError') {
8473          *             // maybe log something to the application here if applicable
8474          *             return true;
8475          *         }
8476          *         // any non-true return value (including none) will cause the error to be thrown
8477          *     }
8478          *
8479          * @param {Ext.Error} err The Ext.Error object being raised. It will contain any attributes that were originally
8480          * raised with it, plus properties about the method and class from which the error originated (if raised from a
8481          * class that uses the Ext 4 class system).
8482          * @static
8483          */
8484         handle: function(){
8485             return Ext.Error.ignore;
8486         }
8487     },
8488
8489     // This is the standard property that is the name of the constructor.
8490     name: 'Ext.Error',
8491
8492     /**
8493      * Creates new Error object.
8494      * @param {String/Object} config The error message string, or an object containing the
8495      * attribute "msg" that will be used as the error message. Any other data included in
8496      * the object will be applied to the error instance and logged to the browser console, if available.
8497      */
8498     constructor: function(config){
8499         if (Ext.isString(config)) {
8500             config = { msg: config };
8501         }
8502
8503         var me = this;
8504
8505         Ext.apply(me, config);
8506
8507         me.message = me.message || me.msg; // 'message' is standard ('msg' is non-standard)
8508         // note: the above does not work in old WebKit (me.message is readonly) (Safari 4)
8509     },
8510
8511     /**
8512      * Provides a custom string representation of the error object. This is an override of the base JavaScript
8513      * `Object.toString` method, which is useful so that when logged to the browser console, an error object will
8514      * be displayed with a useful message instead of `[object Object]`, the default `toString` result.
8515      *
8516      * The default implementation will include the error message along with the raising class and method, if available,
8517      * but this can be overridden with a custom implementation either at the prototype level (for all errors) or on
8518      * a particular error instance, if you want to provide a custom description that will show up in the console.
8519      * @return {String} The error message. If raised from within the Ext 4 class system, the error message will also
8520      * include the raising class and method names, if available.
8521      */
8522     toString: function(){
8523         var me = this,
8524             className = me.className ? me.className  : '',
8525             methodName = me.methodName ? '.' + me.methodName + '(): ' : '',
8526             msg = me.msg || '(No description provided)';
8527
8528         return className + methodName + msg;
8529     }
8530 });
8531
8532 /*
8533  * This mechanism is used to notify the user of the first error encountered on the page. This
8534  * was previously internal to Ext.Error.raise and is a desirable feature since errors often
8535  * slip silently under the radar. It cannot live in Ext.Error.raise since there are times
8536  * where exceptions are handled in a try/catch.
8537  */
8538
8539
8540
8541 /*
8542
8543 This file is part of Ext JS 4
8544
8545 Copyright (c) 2011 Sencha Inc
8546
8547 Contact:  http://www.sencha.com/contact
8548
8549 GNU General Public License Usage
8550 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.
8551
8552 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
8553
8554 */
8555 /**
8556  * @class Ext.JSON
8557  * Modified version of Douglas Crockford's JSON.js that doesn't
8558  * mess with the Object prototype
8559  * http://www.json.org/js.html
8560  * @singleton
8561  */
8562 Ext.JSON = new(function() {
8563     var useHasOwn = !! {}.hasOwnProperty,
8564     isNative = function() {
8565         var useNative = null;
8566
8567         return function() {
8568             if (useNative === null) {
8569                 useNative = Ext.USE_NATIVE_JSON && window.JSON && JSON.toString() == '[object JSON]';
8570             }
8571
8572             return useNative;
8573         };
8574     }(),
8575     pad = function(n) {
8576         return n < 10 ? "0" + n : n;
8577     },
8578     doDecode = function(json) {
8579         return eval("(" + json + ')');
8580     },
8581     doEncode = function(o) {
8582         if (!Ext.isDefined(o) || o === null) {
8583             return "null";
8584         } else if (Ext.isArray(o)) {
8585             return encodeArray(o);
8586         } else if (Ext.isDate(o)) {
8587             return Ext.JSON.encodeDate(o);
8588         } else if (Ext.isString(o)) {
8589             return encodeString(o);
8590         } else if (typeof o == "number") {
8591             //don't use isNumber here, since finite checks happen inside isNumber
8592             return isFinite(o) ? String(o) : "null";
8593         } else if (Ext.isBoolean(o)) {
8594             return String(o);
8595         } else if (Ext.isObject(o)) {
8596             return encodeObject(o);
8597         } else if (typeof o === "function") {
8598             return "null";
8599         }
8600         return 'undefined';
8601     },
8602     m = {
8603         "\b": '\\b',
8604         "\t": '\\t',
8605         "\n": '\\n',
8606         "\f": '\\f',
8607         "\r": '\\r',
8608         '"': '\\"',
8609         "\\": '\\\\',
8610         '\x0b': '\\u000b' //ie doesn't handle \v
8611     },
8612     charToReplace = /[\\\"\x00-\x1f\x7f-\uffff]/g,
8613     encodeString = function(s) {
8614         return '"' + s.replace(charToReplace, function(a) {
8615             var c = m[a];
8616             return typeof c === 'string' ? c : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
8617         }) + '"';
8618     },
8619     encodeArray = function(o) {
8620         var a = ["[", ""],
8621         // Note empty string in case there are no serializable members.
8622         len = o.length,
8623         i;
8624         for (i = 0; i < len; i += 1) {
8625             a.push(doEncode(o[i]), ',');
8626         }
8627         // Overwrite trailing comma (or empty string)
8628         a[a.length - 1] = ']';
8629         return a.join("");
8630     },
8631     encodeObject = function(o) {
8632         var a = ["{", ""],
8633         // Note empty string in case there are no serializable members.
8634         i;
8635         for (i in o) {
8636             if (!useHasOwn || o.hasOwnProperty(i)) {
8637                 a.push(doEncode(i), ":", doEncode(o[i]), ',');
8638             }
8639         }
8640         // Overwrite trailing comma (or empty string)
8641         a[a.length - 1] = '}';
8642         return a.join("");
8643     };
8644
8645     /**
8646      * <p>Encodes a Date. This returns the actual string which is inserted into the JSON string as the literal expression.
8647      * <b>The returned value includes enclosing double quotation marks.</b></p>
8648      * <p>The default return format is "yyyy-mm-ddThh:mm:ss".</p>
8649      * <p>To override this:</p><pre><code>
8650 Ext.JSON.encodeDate = function(d) {
8651     return Ext.Date.format(d, '"Y-m-d"');
8652 };
8653      </code></pre>
8654      * @param {Date} d The Date to encode
8655      * @return {String} The string literal to use in a JSON string.
8656      */
8657     this.encodeDate = function(o) {
8658         return '"' + o.getFullYear() + "-"
8659         + pad(o.getMonth() + 1) + "-"
8660         + pad(o.getDate()) + "T"
8661         + pad(o.getHours()) + ":"
8662         + pad(o.getMinutes()) + ":"
8663         + pad(o.getSeconds()) + '"';
8664     };
8665
8666     /**
8667      * Encodes an Object, Array or other value
8668      * @param {Object} o The variable to encode
8669      * @return {String} The JSON string
8670      */
8671     this.encode = function() {
8672         var ec;
8673         return function(o) {
8674             if (!ec) {
8675                 // setup encoding function on first access
8676                 ec = isNative() ? JSON.stringify : doEncode;
8677             }
8678             return ec(o);
8679         };
8680     }();
8681
8682
8683     /**
8684      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError unless the safe option is set.
8685      * @param {String} json The JSON string
8686      * @param {Boolean} safe (optional) Whether to return null or throw an exception if the JSON is invalid.
8687      * @return {Object} The resulting object
8688      */
8689     this.decode = function() {
8690         var dc;
8691         return function(json, safe) {
8692             if (!dc) {
8693                 // setup decoding function on first access
8694                 dc = isNative() ? JSON.parse : doDecode;
8695             }
8696             try {
8697                 return dc(json);
8698             } catch (e) {
8699                 if (safe === true) {
8700                     return null;
8701                 }
8702                 Ext.Error.raise({
8703                     sourceClass: "Ext.JSON",
8704                     sourceMethod: "decode",
8705                     msg: "You're trying to decode an invalid JSON String: " + json
8706                 });
8707             }
8708         };
8709     }();
8710
8711 })();
8712 /**
8713  * Shorthand for {@link Ext.JSON#encode}
8714  * @member Ext
8715  * @method encode
8716  * @alias Ext.JSON#encode
8717  */
8718 Ext.encode = Ext.JSON.encode;
8719 /**
8720  * Shorthand for {@link Ext.JSON#decode}
8721  * @member Ext
8722  * @method decode
8723  * @alias Ext.JSON#decode
8724  */
8725 Ext.decode = Ext.JSON.decode;
8726
8727
8728 /**
8729  * @class Ext
8730
8731  The Ext namespace (global object) encapsulates all classes, singletons, and utility methods provided by Sencha's libraries.</p>
8732  Most user interface Components are at a lower level of nesting in the namespace, but many common utility functions are provided
8733  as direct properties of the Ext namespace.
8734
8735  Also many frequently used methods from other classes are provided as shortcuts within the Ext namespace.
8736  For example {@link Ext#getCmp Ext.getCmp} aliases {@link Ext.ComponentManager#get Ext.ComponentManager.get}.
8737
8738  Many applications are initiated with {@link Ext#onReady Ext.onReady} which is called once the DOM is ready.
8739  This ensures all scripts have been loaded, preventing dependency issues. For example
8740
8741      Ext.onReady(function(){
8742          new Ext.Component({
8743              renderTo: document.body,
8744              html: 'DOM ready!'
8745          });
8746      });
8747
8748 For more information about how to use the Ext classes, see
8749
8750 - <a href="http://www.sencha.com/learn/">The Learning Center</a>
8751 - <a href="http://www.sencha.com/learn/Ext_FAQ">The FAQ</a>
8752 - <a href="http://www.sencha.com/forum/">The forums</a>
8753
8754  * @singleton
8755  * @markdown
8756  */
8757 Ext.apply(Ext, {
8758     userAgent: navigator.userAgent.toLowerCase(),
8759     cache: {},
8760     idSeed: 1000,
8761     windowId: 'ext-window',
8762     documentId: 'ext-document',
8763
8764     /**
8765      * True when the document is fully initialized and ready for action
8766      * @type Boolean
8767      */
8768     isReady: false,
8769
8770     /**
8771      * True to automatically uncache orphaned Ext.Elements periodically
8772      * @type Boolean
8773      */
8774     enableGarbageCollector: true,
8775
8776     /**
8777      * True to automatically purge event listeners during garbageCollection.
8778      * @type Boolean
8779      */
8780     enableListenerCollection: true,
8781
8782     /**
8783      * Generates unique ids. If the element already has an id, it is unchanged
8784      * @param {HTMLElement/Ext.Element} el (optional) The element to generate an id for
8785      * @param {String} prefix (optional) Id prefix (defaults "ext-gen")
8786      * @return {String} The generated Id.
8787      */
8788     id: function(el, prefix) {
8789         var me = this,
8790             sandboxPrefix = '';
8791         el = Ext.getDom(el, true) || {};
8792         if (el === document) {
8793             el.id = me.documentId;
8794         }
8795         else if (el === window) {
8796             el.id = me.windowId;
8797         }
8798         if (!el.id) {
8799             if (me.isSandboxed) {
8800                 if (!me.uniqueGlobalNamespace) {
8801                     me.getUniqueGlobalNamespace();
8802                 }
8803                 sandboxPrefix = me.uniqueGlobalNamespace + '-';
8804             }
8805             el.id = sandboxPrefix + (prefix || "ext-gen") + (++Ext.idSeed);
8806         }
8807         return el.id;
8808     },
8809
8810     /**
8811      * Returns the current document body as an {@link Ext.Element}.
8812      * @return Ext.Element The document body
8813      */
8814     getBody: function() {
8815         return Ext.get(document.body || false);
8816     },
8817
8818     /**
8819      * Returns the current document head as an {@link Ext.Element}.
8820      * @return Ext.Element The document head
8821      * @method
8822      */
8823     getHead: function() {
8824         var head;
8825
8826         return function() {
8827             if (head == undefined) {
8828                 head = Ext.get(document.getElementsByTagName("head")[0]);
8829             }
8830
8831             return head;
8832         };
8833     }(),
8834
8835     /**
8836      * Returns the current HTML document object as an {@link Ext.Element}.
8837      * @return Ext.Element The document
8838      */
8839     getDoc: function() {
8840         return Ext.get(document);
8841     },
8842
8843     /**
8844      * This is shorthand reference to {@link Ext.ComponentManager#get}.
8845      * Looks up an existing {@link Ext.Component Component} by {@link Ext.Component#id id}
8846      * @param {String} id The component {@link Ext.Component#id id}
8847      * @return Ext.Component The Component, <tt>undefined</tt> if not found, or <tt>null</tt> if a
8848      * Class was found.
8849     */
8850     getCmp: function(id) {
8851         return Ext.ComponentManager.get(id);
8852     },
8853
8854     /**
8855      * Returns the current orientation of the mobile device
8856      * @return {String} Either 'portrait' or 'landscape'
8857      */
8858     getOrientation: function() {
8859         return window.innerHeight > window.innerWidth ? 'portrait' : 'landscape';
8860     },
8861
8862     /**
8863      * Attempts to destroy any objects passed to it by removing all event listeners, removing them from the
8864      * DOM (if applicable) and calling their destroy functions (if available).  This method is primarily
8865      * intended for arguments of type {@link Ext.Element} and {@link Ext.Component}, but any subclass of
8866      * {@link Ext.util.Observable} can be passed in.  Any number of elements and/or components can be
8867      * passed into this function in a single call as separate arguments.
8868      * @param {Ext.Element/Ext.Component/Ext.Element[]/Ext.Component[]...} arg1
8869      * An {@link Ext.Element}, {@link Ext.Component}, or an Array of either of these to destroy
8870      */
8871     destroy: function() {
8872         var ln = arguments.length,
8873         i, arg;
8874
8875         for (i = 0; i < ln; i++) {
8876             arg = arguments[i];
8877             if (arg) {
8878                 if (Ext.isArray(arg)) {
8879                     this.destroy.apply(this, arg);
8880                 }
8881                 else if (Ext.isFunction(arg.destroy)) {
8882                     arg.destroy();
8883                 }
8884                 else if (arg.dom) {
8885                     arg.remove();
8886                 }
8887             }
8888         }
8889     },
8890
8891     /**
8892      * Execute a callback function in a particular scope. If no function is passed the call is ignored.
8893      *
8894      * For example, these lines are equivalent:
8895      *
8896      *     Ext.callback(myFunc, this, [arg1, arg2]);
8897      *     Ext.isFunction(myFunc) && myFunc.apply(this, [arg1, arg2]);
8898      *
8899      * @param {Function} callback The callback to execute
8900      * @param {Object} scope (optional) The scope to execute in
8901      * @param {Array} args (optional) The arguments to pass to the function
8902      * @param {Number} delay (optional) Pass a number to delay the call by a number of milliseconds.
8903      */
8904     callback: function(callback, scope, args, delay){
8905         if(Ext.isFunction(callback)){
8906             args = args || [];
8907             scope = scope || window;
8908             if (delay) {
8909                 Ext.defer(callback, delay, scope, args);
8910             } else {
8911                 callback.apply(scope, args);
8912             }
8913         }
8914     },
8915
8916     /**
8917      * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
8918      * @param {String} value The string to encode
8919      * @return {String} The encoded text
8920      */
8921     htmlEncode : function(value) {
8922         return Ext.String.htmlEncode(value);
8923     },
8924
8925     /**
8926      * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
8927      * @param {String} value The string to decode
8928      * @return {String} The decoded text
8929      */
8930     htmlDecode : function(value) {
8931          return Ext.String.htmlDecode(value);
8932     },
8933
8934     /**
8935      * Appends content to the query string of a URL, handling logic for whether to place
8936      * a question mark or ampersand.
8937      * @param {String} url The URL to append to.
8938      * @param {String} s The content to append to the URL.
8939      * @return (String) The resulting URL
8940      */
8941     urlAppend : function(url, s) {
8942         if (!Ext.isEmpty(s)) {
8943             return url + (url.indexOf('?') === -1 ? '?' : '&') + s;
8944         }
8945         return url;
8946     }
8947 });
8948
8949
8950 Ext.ns = Ext.namespace;
8951
8952 // for old browsers
8953 window.undefined = window.undefined;
8954
8955 /**
8956  * @class Ext
8957  * Ext core utilities and functions.
8958  * @singleton
8959  */
8960 (function(){
8961 /*
8962 FF 3.6      - Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.17) Gecko/20110420 Firefox/3.6.17
8963 FF 4.0.1    - Mozilla/5.0 (Windows NT 5.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1
8964 FF 5.0      - Mozilla/5.0 (Windows NT 6.1; WOW64; rv:5.0) Gecko/20100101 Firefox/5.0
8965
8966 IE6         - Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1;)
8967 IE7         - Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; SV1;)
8968 IE8         - Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)
8969 IE9         - Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)
8970
8971 Chrome 11   - Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.60 Safari/534.24
8972
8973 Safari 5    - Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1
8974
8975 Opera 11.11 - Opera/9.80 (Windows NT 6.1; U; en) Presto/2.8.131 Version/11.11
8976 */
8977     var check = function(regex){
8978             return regex.test(Ext.userAgent);
8979         },
8980         isStrict = document.compatMode == "CSS1Compat",
8981         version = function (is, regex) {
8982             var m;
8983             return (is && (m = regex.exec(Ext.userAgent))) ? parseFloat(m[1]) : 0;
8984         },
8985         docMode = document.documentMode,
8986         isOpera = check(/opera/),
8987         isOpera10_5 = isOpera && check(/version\/10\.5/),
8988         isChrome = check(/\bchrome\b/),
8989         isWebKit = check(/webkit/),
8990         isSafari = !isChrome && check(/safari/),
8991         isSafari2 = isSafari && check(/applewebkit\/4/), // unique to Safari 2
8992         isSafari3 = isSafari && check(/version\/3/),
8993         isSafari4 = isSafari && check(/version\/4/),
8994         isSafari5 = isSafari && check(/version\/5/),
8995         isIE = !isOpera && check(/msie/),
8996         isIE7 = isIE && (check(/msie 7/) || docMode == 7),
8997         isIE8 = isIE && (check(/msie 8/) && docMode != 7 && docMode != 9 || docMode == 8),
8998         isIE9 = isIE && (check(/msie 9/) && docMode != 7 && docMode != 8 || docMode == 9),
8999         isIE6 = isIE && check(/msie 6/),
9000         isGecko = !isWebKit && check(/gecko/),
9001         isGecko3 = isGecko && check(/rv:1\.9/),
9002         isGecko4 = isGecko && check(/rv:2\.0/),
9003         isGecko5 = isGecko && check(/rv:5\./),
9004         isFF3_0 = isGecko3 && check(/rv:1\.9\.0/),
9005         isFF3_5 = isGecko3 && check(/rv:1\.9\.1/),
9006         isFF3_6 = isGecko3 && check(/rv:1\.9\.2/),
9007         isWindows = check(/windows|win32/),
9008         isMac = check(/macintosh|mac os x/),
9009         isLinux = check(/linux/),
9010         scrollbarSize = null,
9011         chromeVersion = version(true, /\bchrome\/(\d+\.\d+)/),
9012         firefoxVersion = version(true, /\bfirefox\/(\d+\.\d+)/),
9013         ieVersion = version(isIE, /msie (\d+\.\d+)/),
9014         operaVersion = version(isOpera, /version\/(\d+\.\d+)/),
9015         safariVersion = version(isSafari, /version\/(\d+\.\d+)/),
9016         webKitVersion = version(isWebKit, /webkit\/(\d+\.\d+)/),
9017         isSecure = /^https/i.test(window.location.protocol);
9018
9019     // remove css image flicker
9020     try {
9021         document.execCommand("BackgroundImageCache", false, true);
9022     } catch(e) {}
9023
9024
9025     Ext.setVersion('extjs', '4.0.7');
9026     Ext.apply(Ext, {
9027         /**
9028          * URL to a blank file used by Ext when in secure mode for iframe src and onReady src to prevent
9029          * the IE insecure content warning (<tt>'about:blank'</tt>, except for IE in secure mode, which is <tt>'javascript:""'</tt>).
9030          * @type String
9031          */
9032         SSL_SECURE_URL : isSecure && isIE ? 'javascript:""' : 'about:blank',
9033
9034         /**
9035          * True if the {@link Ext.fx.Anim} Class is available
9036          * @type Boolean
9037          * @property enableFx
9038          */
9039
9040         /**
9041          * True to scope the reset CSS to be just applied to Ext components. Note that this wraps root containers
9042          * with an additional element. Also remember that when you turn on this option, you have to use ext-all-scoped {
9043          * unless you use the bootstrap.js to load your javascript, in which case it will be handled for you.
9044          * @type Boolean
9045          */
9046         scopeResetCSS : Ext.buildSettings.scopeResetCSS,
9047
9048         /**
9049          * EXPERIMENTAL - True to cascade listener removal to child elements when an element is removed.
9050          * Currently not optimized for performance.
9051          * @type Boolean
9052          */
9053         enableNestedListenerRemoval : false,
9054
9055         /**
9056          * Indicates whether to use native browser parsing for JSON methods.
9057          * This option is ignored if the browser does not support native JSON methods.
9058          * <b>Note: Native JSON methods will not work with objects that have functions.
9059          * Also, property names must be quoted, otherwise the data will not parse.</b> (Defaults to false)
9060          * @type Boolean
9061          */
9062         USE_NATIVE_JSON : false,
9063
9064         /**
9065          * Return the dom node for the passed String (id), dom node, or Ext.Element.
9066          * Optional 'strict' flag is needed for IE since it can return 'name' and
9067          * 'id' elements by using getElementById.
9068          * Here are some examples:
9069          * <pre><code>
9070 // gets dom node based on id
9071 var elDom = Ext.getDom('elId');
9072 // gets dom node based on the dom node
9073 var elDom1 = Ext.getDom(elDom);
9074
9075 // If we don&#39;t know if we are working with an
9076 // Ext.Element or a dom node use Ext.getDom
9077 function(el){
9078     var dom = Ext.getDom(el);
9079     // do something with the dom node
9080 }
9081          * </code></pre>
9082          * <b>Note</b>: the dom node to be found actually needs to exist (be rendered, etc)
9083          * when this method is called to be successful.
9084          * @param {String/HTMLElement/Ext.Element} el
9085          * @return HTMLElement
9086          */
9087         getDom : function(el, strict) {
9088             if (!el || !document) {
9089                 return null;
9090             }
9091             if (el.dom) {
9092                 return el.dom;
9093             } else {
9094                 if (typeof el == 'string') {
9095                     var e = document.getElementById(el);
9096                     // IE returns elements with the 'name' and 'id' attribute.
9097                     // we do a strict check to return the element with only the id attribute
9098                     if (e && isIE && strict) {
9099                         if (el == e.getAttribute('id')) {
9100                             return e;
9101                         } else {
9102                             return null;
9103                         }
9104                     }
9105                     return e;
9106                 } else {
9107                     return el;
9108                 }
9109             }
9110         },
9111
9112         /**
9113          * Removes a DOM node from the document.
9114          * <p>Removes this element from the document, removes all DOM event listeners, and deletes the cache reference.
9115          * All DOM event listeners are removed from this element. If {@link Ext#enableNestedListenerRemoval Ext.enableNestedListenerRemoval} is
9116          * <code>true</code>, then DOM event listeners are also removed from all child nodes. The body node
9117          * will be ignored if passed in.</p>
9118          * @param {HTMLElement} node The node to remove
9119          * @method
9120          */
9121         removeNode : isIE6 || isIE7 ? function() {
9122             var d;
9123             return function(n){
9124                 if(n && n.tagName != 'BODY'){
9125                     (Ext.enableNestedListenerRemoval) ? Ext.EventManager.purgeElement(n) : Ext.EventManager.removeAll(n);
9126                     d = d || document.createElement('div');
9127                     d.appendChild(n);
9128                     d.innerHTML = '';
9129                     delete Ext.cache[n.id];
9130                 }
9131             };
9132         }() : function(n) {
9133             if (n && n.parentNode && n.tagName != 'BODY') {
9134                 (Ext.enableNestedListenerRemoval) ? Ext.EventManager.purgeElement(n) : Ext.EventManager.removeAll(n);
9135                 n.parentNode.removeChild(n);
9136                 delete Ext.cache[n.id];
9137             }
9138         },
9139
9140         isStrict: isStrict,
9141
9142         isIEQuirks: isIE && !isStrict,
9143
9144         /**
9145          * True if the detected browser is Opera.
9146          * @type Boolean
9147          */
9148         isOpera : isOpera,
9149
9150         /**
9151          * True if the detected browser is Opera 10.5x.
9152          * @type Boolean
9153          */
9154         isOpera10_5 : isOpera10_5,
9155
9156         /**
9157          * True if the detected browser uses WebKit.
9158          * @type Boolean
9159          */
9160         isWebKit : isWebKit,
9161
9162         /**
9163          * True if the detected browser is Chrome.
9164          * @type Boolean
9165          */
9166         isChrome : isChrome,
9167
9168         /**
9169          * True if the detected browser is Safari.
9170          * @type Boolean
9171          */
9172         isSafari : isSafari,
9173
9174         /**
9175          * True if the detected browser is Safari 3.x.
9176          * @type Boolean
9177          */
9178         isSafari3 : isSafari3,
9179
9180         /**
9181          * True if the detected browser is Safari 4.x.
9182          * @type Boolean
9183          */
9184         isSafari4 : isSafari4,
9185
9186         /**
9187          * True if the detected browser is Safari 5.x.
9188          * @type Boolean
9189          */
9190         isSafari5 : isSafari5,
9191
9192         /**
9193          * True if the detected browser is Safari 2.x.
9194          * @type Boolean
9195          */
9196         isSafari2 : isSafari2,
9197
9198         /**
9199          * True if the detected browser is Internet Explorer.
9200          * @type Boolean
9201          */
9202         isIE : isIE,
9203
9204         /**
9205          * True if the detected browser is Internet Explorer 6.x.
9206          * @type Boolean
9207          */
9208         isIE6 : isIE6,
9209
9210         /**
9211          * True if the detected browser is Internet Explorer 7.x.
9212          * @type Boolean
9213          */
9214         isIE7 : isIE7,
9215
9216         /**
9217          * True if the detected browser is Internet Explorer 8.x.
9218          * @type Boolean
9219          */
9220         isIE8 : isIE8,
9221
9222         /**
9223          * True if the detected browser is Internet Explorer 9.x.
9224          * @type Boolean
9225          */
9226         isIE9 : isIE9,
9227
9228         /**
9229          * True if the detected browser uses the Gecko layout engine (e.g. Mozilla, Firefox).
9230          * @type Boolean
9231          */
9232         isGecko : isGecko,
9233
9234         /**
9235          * True if the detected browser uses a Gecko 1.9+ layout engine (e.g. Firefox 3.x).
9236          * @type Boolean
9237          */
9238         isGecko3 : isGecko3,
9239
9240         /**
9241          * True if the detected browser uses a Gecko 2.0+ layout engine (e.g. Firefox 4.x).
9242          * @type Boolean
9243          */
9244         isGecko4 : isGecko4,
9245
9246         /**
9247          * True if the detected browser uses a Gecko 5.0+ layout engine (e.g. Firefox 5.x).
9248          * @type Boolean
9249          */
9250         isGecko5 : isGecko5,
9251
9252         /**
9253          * True if the detected browser uses FireFox 3.0
9254          * @type Boolean
9255          */
9256         isFF3_0 : isFF3_0,
9257
9258         /**
9259          * True if the detected browser uses FireFox 3.5
9260          * @type Boolean
9261          */
9262         isFF3_5 : isFF3_5,
9263
9264         /**
9265          * True if the detected browser uses FireFox 3.6
9266          * @type Boolean
9267          */
9268         isFF3_6 : isFF3_6,
9269
9270         /**
9271          * True if the detected browser uses FireFox 4
9272          * @type Boolean
9273          */
9274         isFF4 : 4 <= firefoxVersion && firefoxVersion < 5,
9275
9276         /**
9277          * True if the detected browser uses FireFox 5
9278          * @type Boolean
9279          */
9280         isFF5 : 5 <= firefoxVersion && firefoxVersion < 6,
9281
9282         /**
9283          * True if the detected platform is Linux.
9284          * @type Boolean
9285          */
9286         isLinux : isLinux,
9287
9288         /**
9289          * True if the detected platform is Windows.
9290          * @type Boolean
9291          */
9292         isWindows : isWindows,
9293
9294         /**
9295          * True if the detected platform is Mac OS.
9296          * @type Boolean
9297          */
9298         isMac : isMac,
9299
9300         /**
9301          * The current version of Chrome (0 if the browser is not Chrome).
9302          * @type Number
9303          */
9304         chromeVersion: chromeVersion,
9305
9306         /**
9307          * The current version of Firefox (0 if the browser is not Firefox).
9308          * @type Number
9309          */
9310         firefoxVersion: firefoxVersion,
9311
9312         /**
9313          * The current version of IE (0 if the browser is not IE). This does not account
9314          * for the documentMode of the current page, which is factored into {@link #isIE7},
9315          * {@link #isIE8} and {@link #isIE9}. Thus this is not always true:
9316          *
9317          *      Ext.isIE8 == (Ext.ieVersion == 8)
9318          *
9319          * @type Number
9320          * @markdown
9321          */
9322         ieVersion: ieVersion,
9323
9324         /**
9325          * The current version of Opera (0 if the browser is not Opera).
9326          * @type Number
9327          */
9328         operaVersion: operaVersion,
9329
9330         /**
9331          * The current version of Safari (0 if the browser is not Safari).
9332          * @type Number
9333          */
9334         safariVersion: safariVersion,
9335
9336         /**
9337          * The current version of WebKit (0 if the browser does not use WebKit).
9338          * @type Number
9339          */
9340         webKitVersion: webKitVersion,
9341
9342         /**
9343          * True if the page is running over SSL
9344          * @type Boolean
9345          */
9346         isSecure: isSecure,
9347
9348         /**
9349          * URL to a 1x1 transparent gif image used by Ext to create inline icons with CSS background images.
9350          * In older versions of IE, this defaults to "http://sencha.com/s.gif" and you should change this to a URL on your server.
9351          * For other browsers it uses an inline data URL.
9352          * @type String
9353          */
9354         BLANK_IMAGE_URL : (isIE6 || isIE7) ? '/' + '/www.sencha.com/s.gif' : 'data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==',
9355
9356         /**
9357          * <p>Utility method for returning a default value if the passed value is empty.</p>
9358          * <p>The value is deemed to be empty if it is<div class="mdetail-params"><ul>
9359          * <li>null</li>
9360          * <li>undefined</li>
9361          * <li>an empty array</li>
9362          * <li>a zero length string (Unless the <tt>allowBlank</tt> parameter is <tt>true</tt>)</li>
9363          * </ul></div>
9364          * @param {Object} value The value to test
9365          * @param {Object} defaultValue The value to return if the original value is empty
9366          * @param {Boolean} allowBlank (optional) true to allow zero length strings to qualify as non-empty (defaults to false)
9367          * @return {Object} value, if non-empty, else defaultValue
9368          * @deprecated 4.0.0 Use {@link Ext#valueFrom} instead
9369          */
9370         value : function(v, defaultValue, allowBlank){
9371             return Ext.isEmpty(v, allowBlank) ? defaultValue : v;
9372         },
9373
9374         /**
9375          * Escapes the passed string for use in a regular expression
9376          * @param {String} str
9377          * @return {String}
9378          * @deprecated 4.0.0 Use {@link Ext.String#escapeRegex} instead
9379          */
9380         escapeRe : function(s) {
9381             return s.replace(/([-.*+?^${}()|[\]\/\\])/g, "\\$1");
9382         },
9383
9384         /**
9385          * Applies event listeners to elements by selectors when the document is ready.
9386          * The event name is specified with an <tt>&#64;</tt> suffix.
9387          * <pre><code>
9388 Ext.addBehaviors({
9389     // add a listener for click on all anchors in element with id foo
9390     '#foo a&#64;click' : function(e, t){
9391         // do something
9392     },
9393
9394     // add the same listener to multiple selectors (separated by comma BEFORE the &#64;)
9395     '#foo a, #bar span.some-class&#64;mouseover' : function(){
9396         // do something
9397     }
9398 });
9399          * </code></pre>
9400          * @param {Object} obj The list of behaviors to apply
9401          */
9402         addBehaviors : function(o){
9403             if(!Ext.isReady){
9404                 Ext.onReady(function(){
9405                     Ext.addBehaviors(o);
9406                 });
9407             } else {
9408                 var cache = {}, // simple cache for applying multiple behaviors to same selector does query multiple times
9409                     parts,
9410                     b,
9411                     s;
9412                 for (b in o) {
9413                     if ((parts = b.split('@'))[1]) { // for Object prototype breakers
9414                         s = parts[0];
9415                         if(!cache[s]){
9416                             cache[s] = Ext.select(s);
9417                         }
9418                         cache[s].on(parts[1], o[b]);
9419                     }
9420                 }
9421                 cache = null;
9422             }
9423         },
9424
9425         /**
9426          * Returns the size of the browser scrollbars. This can differ depending on
9427          * operating system settings, such as the theme or font size.
9428          * @param {Boolean} force (optional) true to force a recalculation of the value.
9429          * @return {Object} An object containing the width of a vertical scrollbar and the
9430          * height of a horizontal scrollbar.
9431          */
9432         getScrollbarSize: function (force) {
9433             if(!Ext.isReady){
9434                 return 0;
9435             }
9436
9437             if(force === true || scrollbarSize === null){
9438                 // BrowserBug: IE9
9439                 // When IE9 positions an element offscreen via offsets, the offsetWidth is
9440                 // inaccurately reported. For IE9 only, we render on screen before removing.
9441                 var cssClass = Ext.isIE9 ? '' : Ext.baseCSSPrefix + 'hide-offsets',
9442                     // Append our div, do our calculation and then remove it
9443                     div = Ext.getBody().createChild('<div class="' + cssClass + '" style="width:100px;height:50px;overflow:hidden;"><div style="height:200px;"></div></div>'),
9444                     child = div.child('div', true),
9445                     w1 = child.offsetWidth;
9446
9447                 div.setStyle('overflow', (Ext.isWebKit || Ext.isGecko) ? 'auto' : 'scroll');
9448
9449                 var w2 = child.offsetWidth, width = w1 - w2;
9450                 div.remove();
9451
9452                 // We assume width == height for now. TODO: is this always true?
9453                 scrollbarSize = { width: width, height: width };
9454             }
9455
9456             return scrollbarSize;
9457         },
9458
9459         /**
9460          * Utility method for getting the width of the browser's vertical scrollbar. This
9461          * can differ depending on operating system settings, such as the theme or font size.
9462          *
9463          * This method is deprected in favor of {@link #getScrollbarSize}.
9464          *
9465          * @param {Boolean} force (optional) true to force a recalculation of the value.
9466          * @return {Number} The width of a vertical scrollbar.
9467          * @deprecated
9468          */
9469         getScrollBarWidth: function(force){
9470             var size = Ext.getScrollbarSize(force);
9471             return size.width + 2; // legacy fudge factor
9472         },
9473
9474         /**
9475          * Copies a set of named properties fom the source object to the destination object.
9476          *
9477          * Example:
9478          *
9479          *     ImageComponent = Ext.extend(Ext.Component, {
9480          *         initComponent: function() {
9481          *             this.autoEl = { tag: 'img' };
9482          *             MyComponent.superclass.initComponent.apply(this, arguments);
9483          *             this.initialBox = Ext.copyTo({}, this.initialConfig, 'x,y,width,height');
9484          *         }
9485          *     });
9486          *
9487          * Important note: To borrow class prototype methods, use {@link Ext.Base#borrow} instead.
9488          *
9489          * @param {Object} dest The destination object.
9490          * @param {Object} source The source object.
9491          * @param {String/String[]} names Either an Array of property names, or a comma-delimited list
9492          * of property names to copy.
9493          * @param {Boolean} usePrototypeKeys (Optional) Defaults to false. Pass true to copy keys off of the prototype as well as the instance.
9494          * @return {Object} The modified object.
9495          */
9496         copyTo : function(dest, source, names, usePrototypeKeys){
9497             if(typeof names == 'string'){
9498                 names = names.split(/[,;\s]/);
9499             }
9500             Ext.each(names, function(name){
9501                 if(usePrototypeKeys || source.hasOwnProperty(name)){
9502                     dest[name] = source[name];
9503                 }
9504             }, this);
9505             return dest;
9506         },
9507
9508         /**
9509          * Attempts to destroy and then remove a set of named properties of the passed object.
9510          * @param {Object} o The object (most likely a Component) who's properties you wish to destroy.
9511          * @param {String...} args One or more names of the properties to destroy and remove from the object.
9512          */
9513         destroyMembers : function(o){
9514             for (var i = 1, a = arguments, len = a.length; i < len; i++) {
9515                 Ext.destroy(o[a[i]]);
9516                 delete o[a[i]];
9517             }
9518         },
9519
9520         /**
9521          * Logs a message. If a console is present it will be used. On Opera, the method
9522          * "opera.postError" is called. In other cases, the message is logged to an array
9523          * "Ext.log.out". An attached debugger can watch this array and view the log. The
9524          * log buffer is limited to a maximum of "Ext.log.max" entries (defaults to 250).
9525          * The `Ext.log.out` array can also be written to a popup window by entering the
9526          * following in the URL bar (a "bookmarklet"):
9527          *
9528          *    javascript:void(Ext.log.show());
9529          *
9530          * If additional parameters are passed, they are joined and appended to the message.
9531          * A technique for tracing entry and exit of a function is this:
9532          *
9533          *      function foo () {
9534          *          Ext.log({ indent: 1 }, '>> foo');
9535          *
9536          *          // log statements in here or methods called from here will be indented
9537          *          // by one step
9538          *
9539          *          Ext.log({ outdent: 1 }, '<< foo');
9540          *      }
9541          *
9542          * This method does nothing in a release build.
9543          *
9544          * @param {String/Object} message The message to log or an options object with any
9545          * of the following properties:
9546          *
9547          *  - `msg`: The message to log (required).
9548          *  - `level`: One of: "error", "warn", "info" or "log" (the default is "log").
9549          *  - `dump`: An object to dump to the log as part of the message.
9550          *  - `stack`: True to include a stack trace in the log.
9551          *  - `indent`: Cause subsequent log statements to be indented one step.
9552          *  - `outdent`: Cause this and following statements to be one step less indented.
9553          * @markdown
9554          */
9555         log :
9556             Ext.emptyFn,
9557
9558         /**
9559          * Partitions the set into two sets: a true set and a false set.
9560          * Example:
9561          * Example2:
9562          * <pre><code>
9563 // Example 1:
9564 Ext.partition([true, false, true, true, false]); // [[true, true, true], [false, false]]
9565
9566 // Example 2:
9567 Ext.partition(
9568     Ext.query("p"),
9569     function(val){
9570         return val.className == "class1"
9571     }
9572 );
9573 // true are those paragraph elements with a className of "class1",
9574 // false set are those that do not have that className.
9575          * </code></pre>
9576          * @param {Array/NodeList} arr The array to partition
9577          * @param {Function} truth (optional) a function to determine truth.  If this is omitted the element
9578          * itself must be able to be evaluated for its truthfulness.
9579          * @return {Array} [array of truish values, array of falsy values]
9580          * @deprecated 4.0.0 Will be removed in the next major version
9581          */
9582         partition : function(arr, truth){
9583             var ret = [[],[]];
9584             Ext.each(arr, function(v, i, a) {
9585                 ret[ (truth && truth(v, i, a)) || (!truth && v) ? 0 : 1].push(v);
9586             });
9587             return ret;
9588         },
9589
9590         /**
9591          * Invokes a method on each item in an Array.
9592          * <pre><code>
9593 // Example:
9594 Ext.invoke(Ext.query("p"), "getAttribute", "id");
9595 // [el1.getAttribute("id"), el2.getAttribute("id"), ..., elN.getAttribute("id")]
9596          * </code></pre>
9597          * @param {Array/NodeList} arr The Array of items to invoke the method on.
9598          * @param {String} methodName The method name to invoke.
9599          * @param {Object...} args Arguments to send into the method invocation.
9600          * @return {Array} The results of invoking the method on each item in the array.
9601          * @deprecated 4.0.0 Will be removed in the next major version
9602          */
9603         invoke : function(arr, methodName){
9604             var ret = [],
9605                 args = Array.prototype.slice.call(arguments, 2);
9606             Ext.each(arr, function(v,i) {
9607                 if (v && typeof v[methodName] == 'function') {
9608                     ret.push(v[methodName].apply(v, args));
9609                 } else {
9610                     ret.push(undefined);
9611                 }
9612             });
9613             return ret;
9614         },
9615
9616         /**
9617          * <p>Zips N sets together.</p>
9618          * <pre><code>
9619 // Example 1:
9620 Ext.zip([1,2,3],[4,5,6]); // [[1,4],[2,5],[3,6]]
9621 // Example 2:
9622 Ext.zip(
9623     [ "+", "-", "+"],
9624     [  12,  10,  22],
9625     [  43,  15,  96],
9626     function(a, b, c){
9627         return "$" + a + "" + b + "." + c
9628     }
9629 ); // ["$+12.43", "$-10.15", "$+22.96"]
9630          * </code></pre>
9631          * @param {Array/NodeList...} arr This argument may be repeated. Array(s) to contribute values.
9632          * @param {Function} zipper (optional) The last item in the argument list. This will drive how the items are zipped together.
9633          * @return {Array} The zipped set.
9634          * @deprecated 4.0.0 Will be removed in the next major version
9635          */
9636         zip : function(){
9637             var parts = Ext.partition(arguments, function( val ){ return typeof val != 'function'; }),
9638                 arrs = parts[0],
9639                 fn = parts[1][0],
9640                 len = Ext.max(Ext.pluck(arrs, "length")),
9641                 ret = [];
9642
9643             for (var i = 0; i < len; i++) {
9644                 ret[i] = [];
9645                 if(fn){
9646                     ret[i] = fn.apply(fn, Ext.pluck(arrs, i));
9647                 }else{
9648                     for (var j = 0, aLen = arrs.length; j < aLen; j++){
9649                         ret[i].push( arrs[j][i] );
9650                     }
9651                 }
9652             }
9653             return ret;
9654         },
9655
9656         /**
9657          * Turns an array into a sentence, joined by a specified connector - e.g.:
9658          * Ext.toSentence(['Adama', 'Tigh', 'Roslin']); //'Adama, Tigh and Roslin'
9659          * Ext.toSentence(['Adama', 'Tigh', 'Roslin'], 'or'); //'Adama, Tigh or Roslin'
9660          * @param {String[]} items The array to create a sentence from
9661          * @param {String} connector The string to use to connect the last two words. Usually 'and' or 'or' - defaults to 'and'.
9662          * @return {String} The sentence string
9663          * @deprecated 4.0.0 Will be removed in the next major version
9664          */
9665         toSentence: function(items, connector) {
9666             var length = items.length;
9667
9668             if (length <= 1) {
9669                 return items[0];
9670             } else {
9671                 var head = items.slice(0, length - 1),
9672                     tail = items[length - 1];
9673
9674                 return Ext.util.Format.format("{0} {1} {2}", head.join(", "), connector || 'and', tail);
9675             }
9676         },
9677
9678         /**
9679          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
9680          * you may want to set this to true.
9681          * @type Boolean
9682          */
9683         useShims: isIE6
9684     });
9685 })();
9686
9687 /**
9688  * Loads Ext.app.Application class and starts it up with given configuration after the page is ready.
9689  *
9690  * See Ext.app.Application for details.
9691  *
9692  * @param {Object} config
9693  */
9694 Ext.application = function(config) {
9695     Ext.require('Ext.app.Application');
9696
9697     Ext.onReady(function() {
9698         Ext.create('Ext.app.Application', config);
9699     });
9700 };
9701
9702 /**
9703  * @class Ext.util.Format
9704
9705 This class is a centralized place for formatting functions. It includes
9706 functions to format various different types of data, such as text, dates and numeric values.
9707
9708 __Localization__
9709 This class contains several options for localization. These can be set once the library has loaded,
9710 all calls to the functions from that point will use the locale settings that were specified.
9711 Options include:
9712 - thousandSeparator
9713 - decimalSeparator
9714 - currenyPrecision
9715 - currencySign
9716 - currencyAtEnd
9717 This class also uses the default date format defined here: {@link Ext.Date#defaultFormat}.
9718
9719 __Using with renderers__
9720 There are two helper functions that return a new function that can be used in conjunction with
9721 grid renderers:
9722
9723     columns: [{
9724         dataIndex: 'date',
9725         renderer: Ext.util.Format.dateRenderer('Y-m-d')
9726     }, {
9727         dataIndex: 'time',
9728         renderer: Ext.util.Format.numberRenderer('0.000')
9729     }]
9730
9731 Functions that only take a single argument can also be passed directly:
9732     columns: [{
9733         dataIndex: 'cost',
9734         renderer: Ext.util.Format.usMoney
9735     }, {
9736         dataIndex: 'productCode',
9737         renderer: Ext.util.Format.uppercase
9738     }]
9739
9740 __Using with XTemplates__
9741 XTemplates can also directly use Ext.util.Format functions:
9742
9743     new Ext.XTemplate([
9744         'Date: {startDate:date("Y-m-d")}',
9745         'Cost: {cost:usMoney}'
9746     ]);
9747
9748  * @markdown
9749  * @singleton
9750  */
9751 (function() {
9752     Ext.ns('Ext.util');
9753
9754     Ext.util.Format = {};
9755     var UtilFormat     = Ext.util.Format,
9756         stripTagsRE    = /<\/?[^>]+>/gi,
9757         stripScriptsRe = /(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig,
9758         nl2brRe        = /\r?\n/g,
9759
9760         // A RegExp to remove from a number format string, all characters except digits and '.'
9761         formatCleanRe  = /[^\d\.]/g,
9762
9763         // A RegExp to remove from a number format string, all characters except digits and the local decimal separator.
9764         // Created on first use. The local decimal separator character must be initialized for this to be created.
9765         I18NFormatCleanRe;
9766
9767     Ext.apply(UtilFormat, {
9768         /**
9769          * @property {String} thousandSeparator
9770          * <p>The character that the {@link #number} function uses as a thousand separator.</p>
9771          * <p>This may be overridden in a locale file.</p>
9772          */
9773         thousandSeparator: ',',
9774
9775         /**
9776          * @property {String} decimalSeparator
9777          * <p>The character that the {@link #number} function uses as a decimal point.</p>
9778          * <p>This may be overridden in a locale file.</p>
9779          */
9780         decimalSeparator: '.',
9781
9782         /**
9783          * @property {Number} currencyPrecision
9784          * <p>The number of decimal places that the {@link #currency} function displays.</p>
9785          * <p>This may be overridden in a locale file.</p>
9786          */
9787         currencyPrecision: 2,
9788
9789         /**
9790          * @property {String} currencySign
9791          * <p>The currency sign that the {@link #currency} function displays.</p>
9792          * <p>This may be overridden in a locale file.</p>
9793          */
9794         currencySign: '$',
9795
9796         /**
9797          * @property {Boolean} currencyAtEnd
9798          * <p>This may be set to <code>true</code> to make the {@link #currency} function
9799          * append the currency sign to the formatted value.</p>
9800          * <p>This may be overridden in a locale file.</p>
9801          */
9802         currencyAtEnd: false,
9803
9804         /**
9805          * Checks a reference and converts it to empty string if it is undefined
9806          * @param {Object} value Reference to check
9807          * @return {Object} Empty string if converted, otherwise the original value
9808          */
9809         undef : function(value) {
9810             return value !== undefined ? value : "";
9811         },
9812
9813         /**
9814          * Checks a reference and converts it to the default value if it's empty
9815          * @param {Object} value Reference to check
9816          * @param {String} defaultValue The value to insert of it's undefined (defaults to "")
9817          * @return {String}
9818          */
9819         defaultValue : function(value, defaultValue) {
9820             return value !== undefined && value !== '' ? value : defaultValue;
9821         },
9822
9823         /**
9824          * Returns a substring from within an original string
9825          * @param {String} value The original text
9826          * @param {Number} start The start index of the substring
9827          * @param {Number} length The length of the substring
9828          * @return {String} The substring
9829          */
9830         substr : function(value, start, length) {
9831             return String(value).substr(start, length);
9832         },
9833
9834         /**
9835          * Converts a string to all lower case letters
9836          * @param {String} value The text to convert
9837          * @return {String} The converted text
9838          */
9839         lowercase : function(value) {
9840             return String(value).toLowerCase();
9841         },
9842
9843         /**
9844          * Converts a string to all upper case letters
9845          * @param {String} value The text to convert
9846          * @return {String} The converted text
9847          */
9848         uppercase : function(value) {
9849             return String(value).toUpperCase();
9850         },
9851
9852         /**
9853          * Format a number as US currency
9854          * @param {Number/String} value The numeric value to format
9855          * @return {String} The formatted currency string
9856          */
9857         usMoney : function(v) {
9858             return UtilFormat.currency(v, '$', 2);
9859         },
9860
9861         /**
9862          * Format a number as a currency
9863          * @param {Number/String} value The numeric value to format
9864          * @param {String} sign The currency sign to use (defaults to {@link #currencySign})
9865          * @param {Number} decimals The number of decimals to use for the currency (defaults to {@link #currencyPrecision})
9866          * @param {Boolean} end True if the currency sign should be at the end of the string (defaults to {@link #currencyAtEnd})
9867          * @return {String} The formatted currency string
9868          */
9869         currency: function(v, currencySign, decimals, end) {
9870             var negativeSign = '',
9871                 format = ",0",
9872                 i = 0;
9873             v = v - 0;
9874             if (v < 0) {
9875                 v = -v;
9876                 negativeSign = '-';
9877             }
9878             decimals = decimals || UtilFormat.currencyPrecision;
9879             format += format + (decimals > 0 ? '.' : '');
9880             for (; i < decimals; i++) {
9881                 format += '0';
9882             }
9883             v = UtilFormat.number(v, format);
9884             if ((end || UtilFormat.currencyAtEnd) === true) {
9885                 return Ext.String.format("{0}{1}{2}", negativeSign, v, currencySign || UtilFormat.currencySign);
9886             } else {
9887                 return Ext.String.format("{0}{1}{2}", negativeSign, currencySign || UtilFormat.currencySign, v);
9888             }
9889         },
9890
9891         /**
9892          * Formats the passed date using the specified format pattern.
9893          * @param {String/Date} value The value to format. If a string is passed, it is converted to a Date by the Javascript
9894          * Date object's <a href="http://www.w3schools.com/jsref/jsref_parse.asp">parse()</a> method.
9895          * @param {String} format (Optional) Any valid date format string. Defaults to {@link Ext.Date#defaultFormat}.
9896          * @return {String} The formatted date string.
9897          */
9898         date: function(v, format) {
9899             if (!v) {
9900                 return "";
9901             }
9902             if (!Ext.isDate(v)) {
9903                 v = new Date(Date.parse(v));
9904             }
9905             return Ext.Date.dateFormat(v, format || Ext.Date.defaultFormat);
9906         },
9907
9908         /**
9909          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
9910          * @param {String} format Any valid date format string. Defaults to {@link Ext.Date#defaultFormat}.
9911          * @return {Function} The date formatting function
9912          */
9913         dateRenderer : function(format) {
9914             return function(v) {
9915                 return UtilFormat.date(v, format);
9916             };
9917         },
9918
9919         /**
9920          * Strips all HTML tags
9921          * @param {Object} value The text from which to strip tags
9922          * @return {String} The stripped text
9923          */
9924         stripTags : function(v) {
9925             return !v ? v : String(v).replace(stripTagsRE, "");
9926         },
9927
9928         /**
9929          * Strips all script tags
9930          * @param {Object} value The text from which to strip script tags
9931          * @return {String} The stripped text
9932          */
9933         stripScripts : function(v) {
9934             return !v ? v : String(v).replace(stripScriptsRe, "");
9935         },
9936
9937         /**
9938          * Simple format for a file size (xxx bytes, xxx KB, xxx MB)
9939          * @param {Number/String} size The numeric value to format
9940          * @return {String} The formatted file size
9941          */
9942         fileSize : function(size) {
9943             if (size < 1024) {
9944                 return size + " bytes";
9945             } else if (size < 1048576) {
9946                 return (Math.round(((size*10) / 1024))/10) + " KB";
9947             } else {
9948                 return (Math.round(((size*10) / 1048576))/10) + " MB";
9949             }
9950         },
9951
9952         /**
9953          * It does simple math for use in a template, for example:<pre><code>
9954          * var tpl = new Ext.Template('{value} * 10 = {value:math("* 10")}');
9955          * </code></pre>
9956          * @return {Function} A function that operates on the passed value.
9957          * @method
9958          */
9959         math : function(){
9960             var fns = {};
9961
9962             return function(v, a){
9963                 if (!fns[a]) {
9964                     fns[a] = Ext.functionFactory('v', 'return v ' + a + ';');
9965                 }
9966                 return fns[a](v);
9967             };
9968         }(),
9969
9970         /**
9971          * Rounds the passed number to the required decimal precision.
9972          * @param {Number/String} value The numeric value to round.
9973          * @param {Number} precision The number of decimal places to which to round the first parameter's value.
9974          * @return {Number} The rounded value.
9975          */
9976         round : function(value, precision) {
9977             var result = Number(value);
9978             if (typeof precision == 'number') {
9979                 precision = Math.pow(10, precision);
9980                 result = Math.round(value * precision) / precision;
9981             }
9982             return result;
9983         },
9984
9985         /**
9986          * <p>Formats the passed number according to the passed format string.</p>
9987          * <p>The number of digits after the decimal separator character specifies the number of
9988          * decimal places in the resulting string. The <u>local-specific</u> decimal character is used in the result.</p>
9989          * <p>The <i>presence</i> of a thousand separator character in the format string specifies that
9990          * the <u>locale-specific</u> thousand separator (if any) is inserted separating thousand groups.</p>
9991          * <p>By default, "," is expected as the thousand separator, and "." is expected as the decimal separator.</p>
9992          * <p><b>New to Ext JS 4</b></p>
9993          * <p>Locale-specific characters are always used in the formatted output when inserting
9994          * thousand and decimal separators.</p>
9995          * <p>The format string must specify separator characters according to US/UK conventions ("," as the
9996          * thousand separator, and "." as the decimal separator)</p>
9997          * <p>To allow specification of format strings according to local conventions for separator characters, add
9998          * the string <code>/i</code> to the end of the format string.</p>
9999          * <div style="margin-left:40px">examples (123456.789):
10000          * <div style="margin-left:10px">
10001          * 0 - (123456) show only digits, no precision<br>
10002          * 0.00 - (123456.78) show only digits, 2 precision<br>
10003          * 0.0000 - (123456.7890) show only digits, 4 precision<br>
10004          * 0,000 - (123,456) show comma and digits, no precision<br>
10005          * 0,000.00 - (123,456.78) show comma and digits, 2 precision<br>
10006          * 0,0.00 - (123,456.78) shortcut method, show comma and digits, 2 precision<br>
10007          * To allow specification of the formatting string using UK/US grouping characters (,) and decimal (.) for international numbers, add /i to the end.
10008          * For example: 0.000,00/i
10009          * </div></div>
10010          * @param {Number} v The number to format.
10011          * @param {String} format The way you would like to format this text.
10012          * @return {String} The formatted number.
10013          */
10014         number: function(v, formatString) {
10015             if (!formatString) {
10016                 return v;
10017             }
10018             v = Ext.Number.from(v, NaN);
10019             if (isNaN(v)) {
10020                 return '';
10021             }
10022             var comma = UtilFormat.thousandSeparator,
10023                 dec   = UtilFormat.decimalSeparator,
10024                 i18n  = false,
10025                 neg   = v < 0,
10026                 hasComma,
10027                 psplit;
10028
10029             v = Math.abs(v);
10030
10031             // The "/i" suffix allows caller to use a locale-specific formatting string.
10032             // Clean the format string by removing all but numerals and the decimal separator.
10033             // Then split the format string into pre and post decimal segments according to *what* the
10034             // decimal separator is. If they are specifying "/i", they are using the local convention in the format string.
10035             if (formatString.substr(formatString.length - 2) == '/i') {
10036                 if (!I18NFormatCleanRe) {
10037                     I18NFormatCleanRe = new RegExp('[^\\d\\' + UtilFormat.decimalSeparator + ']','g');
10038                 }
10039                 formatString = formatString.substr(0, formatString.length - 2);
10040                 i18n   = true;
10041                 hasComma = formatString.indexOf(comma) != -1;
10042                 psplit = formatString.replace(I18NFormatCleanRe, '').split(dec);
10043             } else {
10044                 hasComma = formatString.indexOf(',') != -1;
10045                 psplit = formatString.replace(formatCleanRe, '').split('.');
10046             }
10047
10048             if (1 < psplit.length) {
10049                 v = v.toFixed(psplit[1].length);
10050             } else if(2 < psplit.length) {
10051             } else {
10052                 v = v.toFixed(0);
10053             }
10054
10055             var fnum = v.toString();
10056
10057             psplit = fnum.split('.');
10058
10059             if (hasComma) {
10060                 var cnum = psplit[0],
10061                     parr = [],
10062                     j    = cnum.length,
10063                     m    = Math.floor(j / 3),
10064                     n    = cnum.length % 3 || 3,
10065                     i;
10066
10067                 for (i = 0; i < j; i += n) {
10068                     if (i !== 0) {
10069                         n = 3;
10070                     }
10071
10072                     parr[parr.length] = cnum.substr(i, n);
10073                     m -= 1;
10074                 }
10075                 fnum = parr.join(comma);
10076                 if (psplit[1]) {
10077                     fnum += dec + psplit[1];
10078                 }
10079             } else {
10080                 if (psplit[1]) {
10081                     fnum = psplit[0] + dec + psplit[1];
10082                 }
10083             }
10084
10085             if (neg) {
10086                 /*
10087                  * Edge case. If we have a very small negative number it will get rounded to 0,
10088                  * however the initial check at the top will still report as negative. Replace
10089                  * everything but 1-9 and check if the string is empty to determine a 0 value.
10090                  */
10091                 neg = fnum.replace(/[^1-9]/g, '') !== '';
10092             }
10093
10094             return (neg ? '-' : '') + formatString.replace(/[\d,?\.?]+/, fnum);
10095         },
10096
10097         /**
10098          * Returns a number rendering function that can be reused to apply a number format multiple times efficiently
10099          * @param {String} format Any valid number format string for {@link #number}
10100          * @return {Function} The number formatting function
10101          */
10102         numberRenderer : function(format) {
10103             return function(v) {
10104                 return UtilFormat.number(v, format);
10105             };
10106         },
10107
10108         /**
10109          * Selectively do a plural form of a word based on a numeric value. For example, in a template,
10110          * {commentCount:plural("Comment")}  would result in "1 Comment" if commentCount was 1 or would be "x Comments"
10111          * if the value is 0 or greater than 1.
10112          * @param {Number} value The value to compare against
10113          * @param {String} singular The singular form of the word
10114          * @param {String} plural (optional) The plural form of the word (defaults to the singular with an "s")
10115          */
10116         plural : function(v, s, p) {
10117             return v +' ' + (v == 1 ? s : (p ? p : s+'s'));
10118         },
10119
10120         /**
10121          * Converts newline characters to the HTML tag &lt;br/>
10122          * @param {String} The string value to format.
10123          * @return {String} The string with embedded &lt;br/> tags in place of newlines.
10124          */
10125         nl2br : function(v) {
10126             return Ext.isEmpty(v) ? '' : v.replace(nl2brRe, '<br/>');
10127         },
10128
10129         /**
10130          * Alias for {@link Ext.String#capitalize}.
10131          * @method
10132          * @alias Ext.String#capitalize
10133          */
10134         capitalize: Ext.String.capitalize,
10135
10136         /**
10137          * Alias for {@link Ext.String#ellipsis}.
10138          * @method
10139          * @alias Ext.String#ellipsis
10140          */
10141         ellipsis: Ext.String.ellipsis,
10142
10143         /**
10144          * Alias for {@link Ext.String#format}.
10145          * @method
10146          * @alias Ext.String#format
10147          */
10148         format: Ext.String.format,
10149
10150         /**
10151          * Alias for {@link Ext.String#htmlDecode}.
10152          * @method
10153          * @alias Ext.String#htmlDecode
10154          */
10155         htmlDecode: Ext.String.htmlDecode,
10156
10157         /**
10158          * Alias for {@link Ext.String#htmlEncode}.
10159          * @method
10160          * @alias Ext.String#htmlEncode
10161          */
10162         htmlEncode: Ext.String.htmlEncode,
10163
10164         /**
10165          * Alias for {@link Ext.String#leftPad}.
10166          * @method
10167          * @alias Ext.String#leftPad
10168          */
10169         leftPad: Ext.String.leftPad,
10170
10171         /**
10172          * Alias for {@link Ext.String#trim}.
10173          * @method
10174          * @alias Ext.String#trim
10175          */
10176         trim : Ext.String.trim,
10177
10178         /**
10179          * Parses a number or string representing margin sizes into an object. Supports CSS-style margin declarations
10180          * (e.g. 10, "10", "10 10", "10 10 10" and "10 10 10 10" are all valid options and would return the same result)
10181          * @param {Number/String} v The encoded margins
10182          * @return {Object} An object with margin sizes for top, right, bottom and left
10183          */
10184         parseBox : function(box) {
10185             if (Ext.isNumber(box)) {
10186                 box = box.toString();
10187             }
10188             var parts  = box.split(' '),
10189                 ln = parts.length;
10190
10191             if (ln == 1) {
10192                 parts[1] = parts[2] = parts[3] = parts[0];
10193             }
10194             else if (ln == 2) {
10195                 parts[2] = parts[0];
10196                 parts[3] = parts[1];
10197             }
10198             else if (ln == 3) {
10199                 parts[3] = parts[1];
10200             }
10201
10202             return {
10203                 top   :parseInt(parts[0], 10) || 0,
10204                 right :parseInt(parts[1], 10) || 0,
10205                 bottom:parseInt(parts[2], 10) || 0,
10206                 left  :parseInt(parts[3], 10) || 0
10207             };
10208         },
10209
10210         /**
10211          * Escapes the passed string for use in a regular expression
10212          * @param {String} str
10213          * @return {String}
10214          */
10215         escapeRegex : function(s) {
10216             return s.replace(/([\-.*+?\^${}()|\[\]\/\\])/g, "\\$1");
10217         }
10218     });
10219 })();
10220
10221 /**
10222  * @class Ext.util.TaskRunner
10223  * Provides the ability to execute one or more arbitrary tasks in a multithreaded
10224  * manner.  Generally, you can use the singleton {@link Ext.TaskManager} instead, but
10225  * if needed, you can create separate instances of TaskRunner.  Any number of
10226  * separate tasks can be started at any time and will run independently of each
10227  * other. Example usage:
10228  * <pre><code>
10229 // Start a simple clock task that updates a div once per second
10230 var updateClock = function(){
10231     Ext.fly('clock').update(new Date().format('g:i:s A'));
10232
10233 var task = {
10234     run: updateClock,
10235     interval: 1000 //1 second
10236 }
10237 var runner = new Ext.util.TaskRunner();
10238 runner.start(task);
10239
10240 // equivalent using TaskManager
10241 Ext.TaskManager.start({
10242     run: updateClock,
10243     interval: 1000
10244 });
10245
10246  * </code></pre>
10247  * <p>See the {@link #start} method for details about how to configure a task object.</p>
10248  * Also see {@link Ext.util.DelayedTask}. 
10249  * 
10250  * @constructor
10251  * @param {Number} [interval=10] The minimum precision in milliseconds supported by this TaskRunner instance
10252  */
10253 Ext.ns('Ext.util');
10254
10255 Ext.util.TaskRunner = function(interval) {
10256     interval = interval || 10;
10257     var tasks = [],
10258     removeQueue = [],
10259     id = 0,
10260     running = false,
10261
10262     // private
10263     stopThread = function() {
10264         running = false;
10265         clearInterval(id);
10266         id = 0;
10267     },
10268
10269     // private
10270     startThread = function() {
10271         if (!running) {
10272             running = true;
10273             id = setInterval(runTasks, interval);
10274         }
10275     },
10276
10277     // private
10278     removeTask = function(t) {
10279         removeQueue.push(t);
10280         if (t.onStop) {
10281             t.onStop.apply(t.scope || t);
10282         }
10283     },
10284
10285     // private
10286     runTasks = function() {
10287         var rqLen = removeQueue.length,
10288             now = new Date().getTime(),
10289             i;
10290
10291         if (rqLen > 0) {
10292             for (i = 0; i < rqLen; i++) {
10293                 Ext.Array.remove(tasks, removeQueue[i]);
10294             }
10295             removeQueue = [];
10296             if (tasks.length < 1) {
10297                 stopThread();
10298                 return;
10299             }
10300         }
10301         i = 0;
10302         var t,
10303             itime,
10304             rt,
10305             len = tasks.length;
10306         for (; i < len; ++i) {
10307             t = tasks[i];
10308             itime = now - t.taskRunTime;
10309             if (t.interval <= itime) {
10310                 rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
10311                 t.taskRunTime = now;
10312                 if (rt === false || t.taskRunCount === t.repeat) {
10313                     removeTask(t);
10314                     return;
10315                 }
10316             }
10317             if (t.duration && t.duration <= (now - t.taskStartTime)) {
10318                 removeTask(t);
10319             }
10320         }
10321     };
10322
10323     /**
10324      * Starts a new task.
10325      * @method start
10326      * @param {Object} task <p>A config object that supports the following properties:<ul>
10327      * <li><code>run</code> : Function<div class="sub-desc"><p>The function to execute each time the task is invoked. The
10328      * function will be called at each interval and passed the <code>args</code> argument if specified, and the
10329      * current invocation count if not.</p>
10330      * <p>If a particular scope (<code>this</code> reference) is required, be sure to specify it using the <code>scope</code> argument.</p>
10331      * <p>Return <code>false</code> from this function to terminate the task.</p></div></li>
10332      * <li><code>interval</code> : Number<div class="sub-desc">The frequency in milliseconds with which the task
10333      * should be invoked.</div></li>
10334      * <li><code>args</code> : Array<div class="sub-desc">(optional) An array of arguments to be passed to the function
10335      * specified by <code>run</code>. If not specified, the current invocation count is passed.</div></li>
10336      * <li><code>scope</code> : Object<div class="sub-desc">(optional) The scope (<tt>this</tt> reference) in which to execute the
10337      * <code>run</code> function. Defaults to the task config object.</div></li>
10338      * <li><code>duration</code> : Number<div class="sub-desc">(optional) The length of time in milliseconds to invoke
10339      * the task before stopping automatically (defaults to indefinite).</div></li>
10340      * <li><code>repeat</code> : Number<div class="sub-desc">(optional) The number of times to invoke the task before
10341      * stopping automatically (defaults to indefinite).</div></li>
10342      * </ul></p>
10343      * <p>Before each invocation, Ext injects the property <code>taskRunCount</code> into the task object so
10344      * that calculations based on the repeat count can be performed.</p>
10345      * @return {Object} The task
10346      */
10347     this.start = function(task) {
10348         tasks.push(task);
10349         task.taskStartTime = new Date().getTime();
10350         task.taskRunTime = 0;
10351         task.taskRunCount = 0;
10352         startThread();
10353         return task;
10354     };
10355
10356     /**
10357      * Stops an existing running task.
10358      * @method stop
10359      * @param {Object} task The task to stop
10360      * @return {Object} The task
10361      */
10362     this.stop = function(task) {
10363         removeTask(task);
10364         return task;
10365     };
10366
10367     /**
10368      * Stops all tasks that are currently running.
10369      * @method stopAll
10370      */
10371     this.stopAll = function() {
10372         stopThread();
10373         for (var i = 0, len = tasks.length; i < len; i++) {
10374             if (tasks[i].onStop) {
10375                 tasks[i].onStop();
10376             }
10377         }
10378         tasks = [];
10379         removeQueue = [];
10380     };
10381 };
10382
10383 /**
10384  * @class Ext.TaskManager
10385  * @extends Ext.util.TaskRunner
10386  * A static {@link Ext.util.TaskRunner} instance that can be used to start and stop arbitrary tasks.  See
10387  * {@link Ext.util.TaskRunner} for supported methods and task config properties.
10388  * <pre><code>
10389 // Start a simple clock task that updates a div once per second
10390 var task = {
10391     run: function(){
10392         Ext.fly('clock').update(new Date().format('g:i:s A'));
10393     },
10394     interval: 1000 //1 second
10395 }
10396 Ext.TaskManager.start(task);
10397 </code></pre>
10398  * <p>See the {@link #start} method for details about how to configure a task object.</p>
10399  * @singleton
10400  */
10401 Ext.TaskManager = Ext.create('Ext.util.TaskRunner');
10402 /**
10403  * @class Ext.is
10404  * 
10405  * Determines information about the current platform the application is running on.
10406  * 
10407  * @singleton
10408  */
10409 Ext.is = {
10410     init : function(navigator) {
10411         var platforms = this.platforms,
10412             ln = platforms.length,
10413             i, platform;
10414
10415         navigator = navigator || window.navigator;
10416
10417         for (i = 0; i < ln; i++) {
10418             platform = platforms[i];
10419             this[platform.identity] = platform.regex.test(navigator[platform.property]);
10420         }
10421
10422         /**
10423          * @property Desktop True if the browser is running on a desktop machine
10424          * @type {Boolean}
10425          */
10426         this.Desktop = this.Mac || this.Windows || (this.Linux && !this.Android);
10427         /**
10428          * @property Tablet True if the browser is running on a tablet (iPad)
10429          */
10430         this.Tablet = this.iPad;
10431         /**
10432          * @property Phone True if the browser is running on a phone.
10433          * @type {Boolean}
10434          */
10435         this.Phone = !this.Desktop && !this.Tablet;
10436         /**
10437          * @property iOS True if the browser is running on iOS
10438          * @type {Boolean}
10439          */
10440         this.iOS = this.iPhone || this.iPad || this.iPod;
10441         
10442         /**
10443          * @property Standalone Detects when application has been saved to homescreen.
10444          * @type {Boolean}
10445          */
10446         this.Standalone = !!window.navigator.standalone;
10447     },
10448     
10449     /**
10450      * @property iPhone True when the browser is running on a iPhone
10451      * @type {Boolean}
10452      */
10453     platforms: [{
10454         property: 'platform',
10455         regex: /iPhone/i,
10456         identity: 'iPhone'
10457     },
10458     
10459     /**
10460      * @property iPod True when the browser is running on a iPod
10461      * @type {Boolean}
10462      */
10463     {
10464         property: 'platform',
10465         regex: /iPod/i,
10466         identity: 'iPod'
10467     },
10468     
10469     /**
10470      * @property iPad True when the browser is running on a iPad
10471      * @type {Boolean}
10472      */
10473     {
10474         property: 'userAgent',
10475         regex: /iPad/i,
10476         identity: 'iPad'
10477     },
10478     
10479     /**
10480      * @property Blackberry True when the browser is running on a Blackberry
10481      * @type {Boolean}
10482      */
10483     {
10484         property: 'userAgent',
10485         regex: /Blackberry/i,
10486         identity: 'Blackberry'
10487     },
10488     
10489     /**
10490      * @property Android True when the browser is running on an Android device
10491      * @type {Boolean}
10492      */
10493     {
10494         property: 'userAgent',
10495         regex: /Android/i,
10496         identity: 'Android'
10497     },
10498     
10499     /**
10500      * @property Mac True when the browser is running on a Mac
10501      * @type {Boolean}
10502      */
10503     {
10504         property: 'platform',
10505         regex: /Mac/i,
10506         identity: 'Mac'
10507     },
10508     
10509     /**
10510      * @property Windows True when the browser is running on Windows
10511      * @type {Boolean}
10512      */
10513     {
10514         property: 'platform',
10515         regex: /Win/i,
10516         identity: 'Windows'
10517     },
10518     
10519     /**
10520      * @property Linux True when the browser is running on Linux
10521      * @type {Boolean}
10522      */
10523     {
10524         property: 'platform',
10525         regex: /Linux/i,
10526         identity: 'Linux'
10527     }]
10528 };
10529
10530 Ext.is.init();
10531
10532 /**
10533  * @class Ext.supports
10534  *
10535  * Determines information about features are supported in the current environment
10536  * 
10537  * @singleton
10538  */
10539 Ext.supports = {
10540     init : function() {
10541         var doc = document,
10542             div = doc.createElement('div'),
10543             tests = this.tests,
10544             ln = tests.length,
10545             i, test;
10546
10547         div.innerHTML = [
10548             '<div style="height:30px;width:50px;">',
10549                 '<div style="height:20px;width:20px;"></div>',
10550             '</div>',
10551             '<div style="width: 200px; height: 200px; position: relative; padding: 5px;">',
10552                 '<div style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"></div>',
10553             '</div>',
10554             '<div style="float:left; background-color:transparent;"></div>'
10555         ].join('');
10556
10557         doc.body.appendChild(div);
10558
10559         for (i = 0; i < ln; i++) {
10560             test = tests[i];
10561             this[test.identity] = test.fn.call(this, doc, div);
10562         }
10563
10564         doc.body.removeChild(div);
10565     },
10566
10567     /**
10568      * @property CSS3BoxShadow True if document environment supports the CSS3 box-shadow style.
10569      * @type {Boolean}
10570      */
10571     CSS3BoxShadow: Ext.isDefined(document.documentElement.style.boxShadow),
10572
10573     /**
10574      * @property ClassList True if document environment supports the HTML5 classList API.
10575      * @type {Boolean}
10576      */
10577     ClassList: !!document.documentElement.classList,
10578
10579     /**
10580      * @property OrientationChange True if the device supports orientation change
10581      * @type {Boolean}
10582      */
10583     OrientationChange: ((typeof window.orientation != 'undefined') && ('onorientationchange' in window)),
10584     
10585     /**
10586      * @property DeviceMotion True if the device supports device motion (acceleration and rotation rate)
10587      * @type {Boolean}
10588      */
10589     DeviceMotion: ('ondevicemotion' in window),
10590     
10591     /**
10592      * @property Touch True if the device supports touch
10593      * @type {Boolean}
10594      */
10595     // is.Desktop is needed due to the bug in Chrome 5.0.375, Safari 3.1.2
10596     // and Safari 4.0 (they all have 'ontouchstart' in the window object).
10597     Touch: ('ontouchstart' in window) && (!Ext.is.Desktop),
10598
10599     tests: [
10600         /**
10601          * @property Transitions True if the device supports CSS3 Transitions
10602          * @type {Boolean}
10603          */
10604         {
10605             identity: 'Transitions',
10606             fn: function(doc, div) {
10607                 var prefix = [
10608                         'webkit',
10609                         'Moz',
10610                         'o',
10611                         'ms',
10612                         'khtml'
10613                     ],
10614                     TE = 'TransitionEnd',
10615                     transitionEndName = [
10616                         prefix[0] + TE,
10617                         'transitionend', //Moz bucks the prefixing convention
10618                         prefix[2] + TE,
10619                         prefix[3] + TE,
10620                         prefix[4] + TE
10621                     ],
10622                     ln = prefix.length,
10623                     i = 0,
10624                     out = false;
10625                 div = Ext.get(div);
10626                 for (; i < ln; i++) {
10627                     if (div.getStyle(prefix[i] + "TransitionProperty")) {
10628                         Ext.supports.CSS3Prefix = prefix[i];
10629                         Ext.supports.CSS3TransitionEnd = transitionEndName[i];
10630                         out = true;
10631                         break;
10632                     }
10633                 }
10634                 return out;
10635             }
10636         },
10637         
10638         /**
10639          * @property RightMargin True if the device supports right margin.
10640          * See https://bugs.webkit.org/show_bug.cgi?id=13343 for why this is needed.
10641          * @type {Boolean}
10642          */
10643         {
10644             identity: 'RightMargin',
10645             fn: function(doc, div) {
10646                 var view = doc.defaultView;
10647                 return !(view && view.getComputedStyle(div.firstChild.firstChild, null).marginRight != '0px');
10648             }
10649         },
10650
10651         /**
10652          * @property DisplayChangeInputSelectionBug True if INPUT elements lose their
10653          * selection when their display style is changed. Essentially, if a text input
10654          * has focus and its display style is changed, the I-beam disappears.
10655          * 
10656          * This bug is encountered due to the work around in place for the {@link #RightMargin}
10657          * bug. This has been observed in Safari 4.0.4 and older, and appears to be fixed
10658          * in Safari 5. It's not clear if Safari 4.1 has the bug, but it has the same WebKit
10659          * version number as Safari 5 (according to http://unixpapa.com/js/gecko.html).
10660          */
10661         {
10662             identity: 'DisplayChangeInputSelectionBug',
10663             fn: function() {
10664                 var webKitVersion = Ext.webKitVersion;
10665                 // WebKit but older than Safari 5 or Chrome 6:
10666                 return 0 < webKitVersion && webKitVersion < 533;
10667             }
10668         },
10669
10670         /**
10671          * @property DisplayChangeTextAreaSelectionBug True if TEXTAREA elements lose their
10672          * selection when their display style is changed. Essentially, if a text area has
10673          * focus and its display style is changed, the I-beam disappears.
10674          *
10675          * This bug is encountered due to the work around in place for the {@link #RightMargin}
10676          * bug. This has been observed in Chrome 10 and Safari 5 and older, and appears to
10677          * be fixed in Chrome 11.
10678          */
10679         {
10680             identity: 'DisplayChangeTextAreaSelectionBug',
10681             fn: function() {
10682                 var webKitVersion = Ext.webKitVersion;
10683
10684                 /*
10685                 Has bug w/textarea:
10686
10687                 (Chrome) Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; en-US)
10688                             AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.127
10689                             Safari/534.16
10690                 (Safari) Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; en-us)
10691                             AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5
10692                             Safari/533.21.1
10693
10694                 No bug:
10695
10696                 (Chrome) Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_7)
10697                             AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.57
10698                             Safari/534.24
10699                 */
10700                 return 0 < webKitVersion && webKitVersion < 534.24;
10701             }
10702         },
10703
10704         /**
10705          * @property TransparentColor True if the device supports transparent color
10706          * @type {Boolean}
10707          */
10708         {
10709             identity: 'TransparentColor',
10710             fn: function(doc, div, view) {
10711                 view = doc.defaultView;
10712                 return !(view && view.getComputedStyle(div.lastChild, null).backgroundColor != 'transparent');
10713             }
10714         },
10715
10716         /**
10717          * @property ComputedStyle True if the browser supports document.defaultView.getComputedStyle()
10718          * @type {Boolean}
10719          */
10720         {
10721             identity: 'ComputedStyle',
10722             fn: function(doc, div, view) {
10723                 view = doc.defaultView;
10724                 return view && view.getComputedStyle;
10725             }
10726         },
10727         
10728         /**
10729          * @property SVG True if the device supports SVG
10730          * @type {Boolean}
10731          */
10732         {
10733             identity: 'Svg',
10734             fn: function(doc) {
10735                 return !!doc.createElementNS && !!doc.createElementNS( "http:/" + "/www.w3.org/2000/svg", "svg").createSVGRect;
10736             }
10737         },
10738     
10739         /**
10740          * @property Canvas True if the device supports Canvas
10741          * @type {Boolean}
10742          */
10743         {
10744             identity: 'Canvas',
10745             fn: function(doc) {
10746                 return !!doc.createElement('canvas').getContext;
10747             }
10748         },
10749         
10750         /**
10751          * @property VML True if the device supports VML
10752          * @type {Boolean}
10753          */
10754         {
10755             identity: 'Vml',
10756             fn: function(doc) {
10757                 var d = doc.createElement("div");
10758                 d.innerHTML = "<!--[if vml]><br><br><![endif]-->";
10759                 return (d.childNodes.length == 2);
10760             }
10761         },
10762         
10763         /**
10764          * @property Float True if the device supports CSS float
10765          * @type {Boolean}
10766          */
10767         {
10768             identity: 'Float',
10769             fn: function(doc, div) {
10770                 return !!div.lastChild.style.cssFloat;
10771             }
10772         },
10773         
10774         /**
10775          * @property AudioTag True if the device supports the HTML5 audio tag
10776          * @type {Boolean}
10777          */
10778         {
10779             identity: 'AudioTag',
10780             fn: function(doc) {
10781                 return !!doc.createElement('audio').canPlayType;
10782             }
10783         },
10784         
10785         /**
10786          * @property History True if the device supports HTML5 history
10787          * @type {Boolean}
10788          */
10789         {
10790             identity: 'History',
10791             fn: function() {
10792                 return !!(window.history && history.pushState);
10793             }
10794         },
10795         
10796         /**
10797          * @property CSS3DTransform True if the device supports CSS3DTransform
10798          * @type {Boolean}
10799          */
10800         {
10801             identity: 'CSS3DTransform',
10802             fn: function() {
10803                 return (typeof WebKitCSSMatrix != 'undefined' && new WebKitCSSMatrix().hasOwnProperty('m41'));
10804             }
10805         },
10806
10807                 /**
10808          * @property CSS3LinearGradient True if the device supports CSS3 linear gradients
10809          * @type {Boolean}
10810          */
10811         {
10812             identity: 'CSS3LinearGradient',
10813             fn: function(doc, div) {
10814                 var property = 'background-image:',
10815                     webkit   = '-webkit-gradient(linear, left top, right bottom, from(black), to(white))',
10816                     w3c      = 'linear-gradient(left top, black, white)',
10817                     moz      = '-moz-' + w3c,
10818                     options  = [property + webkit, property + w3c, property + moz];
10819                 
10820                 div.style.cssText = options.join(';');
10821                 
10822                 return ("" + div.style.backgroundImage).indexOf('gradient') !== -1;
10823             }
10824         },
10825         
10826         /**
10827          * @property CSS3BorderRadius True if the device supports CSS3 border radius
10828          * @type {Boolean}
10829          */
10830         {
10831             identity: 'CSS3BorderRadius',
10832             fn: function(doc, div) {
10833                 var domPrefixes = ['borderRadius', 'BorderRadius', 'MozBorderRadius', 'WebkitBorderRadius', 'OBorderRadius', 'KhtmlBorderRadius'],
10834                     pass = false,
10835                     i;
10836                 for (i = 0; i < domPrefixes.length; i++) {
10837                     if (document.body.style[domPrefixes[i]] !== undefined) {
10838                         return true;
10839                     }
10840                 }
10841                 return pass;
10842             }
10843         },
10844         
10845         /**
10846          * @property GeoLocation True if the device supports GeoLocation
10847          * @type {Boolean}
10848          */
10849         {
10850             identity: 'GeoLocation',
10851             fn: function() {
10852                 return (typeof navigator != 'undefined' && typeof navigator.geolocation != 'undefined') || (typeof google != 'undefined' && typeof google.gears != 'undefined');
10853             }
10854         },
10855         /**
10856          * @property MouseEnterLeave True if the browser supports mouseenter and mouseleave events
10857          * @type {Boolean}
10858          */
10859         {
10860             identity: 'MouseEnterLeave',
10861             fn: function(doc, div){
10862                 return ('onmouseenter' in div && 'onmouseleave' in div);
10863             }
10864         },
10865         /**
10866          * @property MouseWheel True if the browser supports the mousewheel event
10867          * @type {Boolean}
10868          */
10869         {
10870             identity: 'MouseWheel',
10871             fn: function(doc, div) {
10872                 return ('onmousewheel' in div);
10873             }
10874         },
10875         /**
10876          * @property Opacity True if the browser supports normal css opacity
10877          * @type {Boolean}
10878          */
10879         {
10880             identity: 'Opacity',
10881             fn: function(doc, div){
10882                 // Not a strict equal comparison in case opacity can be converted to a number.
10883                 if (Ext.isIE6 || Ext.isIE7 || Ext.isIE8) {
10884                     return false;
10885                 }
10886                 div.firstChild.style.cssText = 'opacity:0.73';
10887                 return div.firstChild.style.opacity == '0.73';
10888             }
10889         },
10890         /**
10891          * @property Placeholder True if the browser supports the HTML5 placeholder attribute on inputs
10892          * @type {Boolean}
10893          */
10894         {
10895             identity: 'Placeholder',
10896             fn: function(doc) {
10897                 return 'placeholder' in doc.createElement('input');
10898             }
10899         },
10900         
10901         /**
10902          * @property Direct2DBug True if when asking for an element's dimension via offsetWidth or offsetHeight, 
10903          * getBoundingClientRect, etc. the browser returns the subpixel width rounded to the nearest pixel.
10904          * @type {Boolean}
10905          */
10906         {
10907             identity: 'Direct2DBug',
10908             fn: function() {
10909                 return Ext.isString(document.body.style.msTransformOrigin);
10910             }
10911         },
10912         /**
10913          * @property BoundingClientRect True if the browser supports the getBoundingClientRect method on elements
10914          * @type {Boolean}
10915          */
10916         {
10917             identity: 'BoundingClientRect',
10918             fn: function(doc, div) {
10919                 return Ext.isFunction(div.getBoundingClientRect);
10920             }
10921         },
10922         {
10923             identity: 'IncludePaddingInWidthCalculation',
10924             fn: function(doc, div){
10925                 var el = Ext.get(div.childNodes[1].firstChild);
10926                 return el.getWidth() == 210;
10927             }
10928         },
10929         {
10930             identity: 'IncludePaddingInHeightCalculation',
10931             fn: function(doc, div){
10932                 var el = Ext.get(div.childNodes[1].firstChild);
10933                 return el.getHeight() == 210;
10934             }
10935         },
10936         
10937         /**
10938          * @property ArraySort True if the Array sort native method isn't bugged.
10939          * @type {Boolean}
10940          */
10941         {
10942             identity: 'ArraySort',
10943             fn: function() {
10944                 var a = [1,2,3,4,5].sort(function(){ return 0; });
10945                 return a[0] === 1 && a[1] === 2 && a[2] === 3 && a[3] === 4 && a[4] === 5;
10946             }
10947         },
10948         /**
10949          * @property Range True if browser support document.createRange native method.
10950          * @type {Boolean}
10951          */
10952         {
10953             identity: 'Range',
10954             fn: function() {
10955                 return !!document.createRange;
10956             }
10957         },
10958         /**
10959          * @property CreateContextualFragment True if browser support CreateContextualFragment range native methods.
10960          * @type {Boolean}
10961          */
10962         {
10963             identity: 'CreateContextualFragment',
10964             fn: function() {
10965                 var range = Ext.supports.Range ? document.createRange() : false;
10966                 
10967                 return range && !!range.createContextualFragment;
10968             }
10969         },
10970
10971         /**
10972          * @property WindowOnError True if browser supports window.onerror.
10973          * @type {Boolean}
10974          */
10975         {
10976             identity: 'WindowOnError',
10977             fn: function () {
10978                 // sadly, we cannot feature detect this...
10979                 return Ext.isIE || Ext.isGecko || Ext.webKitVersion >= 534.16; // Chrome 10+
10980             }
10981         }
10982     ]
10983 };
10984
10985
10986
10987 /*
10988
10989 This file is part of Ext JS 4
10990
10991 Copyright (c) 2011 Sencha Inc
10992
10993 Contact:  http://www.sencha.com/contact
10994
10995 GNU General Public License Usage
10996 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.
10997
10998 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
10999
11000 */
11001 /**
11002  * @class Ext.DomHelper
11003  * @alternateClassName Ext.core.DomHelper
11004  *
11005  * <p>The DomHelper class provides a layer of abstraction from DOM and transparently supports creating
11006  * elements via DOM or using HTML fragments. It also has the ability to create HTML fragment templates
11007  * from your DOM building code.</p>
11008  *
11009  * <p><b><u>DomHelper element specification object</u></b></p>
11010  * <p>A specification object is used when creating elements. Attributes of this object
11011  * are assumed to be element attributes, except for 4 special attributes:
11012  * <div class="mdetail-params"><ul>
11013  * <li><b><tt>tag</tt></b> : <div class="sub-desc">The tag name of the element</div></li>
11014  * <li><b><tt>children</tt></b> : or <tt>cn</tt><div class="sub-desc">An array of the
11015  * same kind of element definition objects to be created and appended. These can be nested
11016  * as deep as you want.</div></li>
11017  * <li><b><tt>cls</tt></b> : <div class="sub-desc">The class attribute of the element.
11018  * This will end up being either the "class" attribute on a HTML fragment or className
11019  * for a DOM node, depending on whether DomHelper is using fragments or DOM.</div></li>
11020  * <li><b><tt>html</tt></b> : <div class="sub-desc">The innerHTML for the element</div></li>
11021  * </ul></div></p>
11022  * <p><b>NOTE:</b> For other arbitrary attributes, the value will currently <b>not</b> be automatically
11023  * HTML-escaped prior to building the element's HTML string. This means that if your attribute value
11024  * contains special characters that would not normally be allowed in a double-quoted attribute value,
11025  * you <b>must</b> manually HTML-encode it beforehand (see {@link Ext.String#htmlEncode}) or risk
11026  * malformed HTML being created. This behavior may change in a future release.</p>
11027  *
11028  * <p><b><u>Insertion methods</u></b></p>
11029  * <p>Commonly used insertion methods:
11030  * <div class="mdetail-params"><ul>
11031  * <li><tt>{@link #append}</tt> : <div class="sub-desc"></div></li>
11032  * <li><tt>{@link #insertBefore}</tt> : <div class="sub-desc"></div></li>
11033  * <li><tt>{@link #insertAfter}</tt> : <div class="sub-desc"></div></li>
11034  * <li><tt>{@link #overwrite}</tt> : <div class="sub-desc"></div></li>
11035  * <li><tt>{@link #createTemplate}</tt> : <div class="sub-desc"></div></li>
11036  * <li><tt>{@link #insertHtml}</tt> : <div class="sub-desc"></div></li>
11037  * </ul></div></p>
11038  *
11039  * <p><b><u>Example</u></b></p>
11040  * <p>This is an example, where an unordered list with 3 children items is appended to an existing
11041  * element with id <tt>'my-div'</tt>:<br>
11042  <pre><code>
11043 var dh = Ext.DomHelper; // create shorthand alias
11044 // specification object
11045 var spec = {
11046     id: 'my-ul',
11047     tag: 'ul',
11048     cls: 'my-list',
11049     // append children after creating
11050     children: [     // may also specify 'cn' instead of 'children'
11051         {tag: 'li', id: 'item0', html: 'List Item 0'},
11052         {tag: 'li', id: 'item1', html: 'List Item 1'},
11053         {tag: 'li', id: 'item2', html: 'List Item 2'}
11054     ]
11055 };
11056 var list = dh.append(
11057     'my-div', // the context element 'my-div' can either be the id or the actual node
11058     spec      // the specification object
11059 );
11060  </code></pre></p>
11061  * <p>Element creation specification parameters in this class may also be passed as an Array of
11062  * specification objects. This can be used to insert multiple sibling nodes into an existing
11063  * container very efficiently. For example, to add more list items to the example above:<pre><code>
11064 dh.append('my-ul', [
11065     {tag: 'li', id: 'item3', html: 'List Item 3'},
11066     {tag: 'li', id: 'item4', html: 'List Item 4'}
11067 ]);
11068  * </code></pre></p>
11069  *
11070  * <p><b><u>Templating</u></b></p>
11071  * <p>The real power is in the built-in templating. Instead of creating or appending any elements,
11072  * <tt>{@link #createTemplate}</tt> returns a Template object which can be used over and over to
11073  * insert new elements. Revisiting the example above, we could utilize templating this time:
11074  * <pre><code>
11075 // create the node
11076 var list = dh.append('my-div', {tag: 'ul', cls: 'my-list'});
11077 // get template
11078 var tpl = dh.createTemplate({tag: 'li', id: 'item{0}', html: 'List Item {0}'});
11079
11080 for(var i = 0; i < 5, i++){
11081     tpl.append(list, [i]); // use template to append to the actual node
11082 }
11083  * </code></pre></p>
11084  * <p>An example using a template:<pre><code>
11085 var html = '<a id="{0}" href="{1}" class="nav">{2}</a>';
11086
11087 var tpl = new Ext.DomHelper.createTemplate(html);
11088 tpl.append('blog-roll', ['link1', 'http://www.edspencer.net/', "Ed&#39;s Site"]);
11089 tpl.append('blog-roll', ['link2', 'http://www.dustindiaz.com/', "Dustin&#39;s Site"]);
11090  * </code></pre></p>
11091  *
11092  * <p>The same example using named parameters:<pre><code>
11093 var html = '<a id="{id}" href="{url}" class="nav">{text}</a>';
11094
11095 var tpl = new Ext.DomHelper.createTemplate(html);
11096 tpl.append('blog-roll', {
11097     id: 'link1',
11098     url: 'http://www.edspencer.net/',
11099     text: "Ed&#39;s Site"
11100 });
11101 tpl.append('blog-roll', {
11102     id: 'link2',
11103     url: 'http://www.dustindiaz.com/',
11104     text: "Dustin&#39;s Site"
11105 });
11106  * </code></pre></p>
11107  *
11108  * <p><b><u>Compiling Templates</u></b></p>
11109  * <p>Templates are applied using regular expressions. The performance is great, but if
11110  * you are adding a bunch of DOM elements using the same template, you can increase
11111  * performance even further by {@link Ext.Template#compile "compiling"} the template.
11112  * The way "{@link Ext.Template#compile compile()}" works is the template is parsed and
11113  * broken up at the different variable points and a dynamic function is created and eval'ed.
11114  * The generated function performs string concatenation of these parts and the passed
11115  * variables instead of using regular expressions.
11116  * <pre><code>
11117 var html = '<a id="{id}" href="{url}" class="nav">{text}</a>';
11118
11119 var tpl = new Ext.DomHelper.createTemplate(html);
11120 tpl.compile();
11121
11122 //... use template like normal
11123  * </code></pre></p>
11124  *
11125  * <p><b><u>Performance Boost</u></b></p>
11126  * <p>DomHelper will transparently create HTML fragments when it can. Using HTML fragments instead
11127  * of DOM can significantly boost performance.</p>
11128  * <p>Element creation specification parameters may also be strings. If {@link #useDom} is <tt>false</tt>,
11129  * then the string is used as innerHTML. If {@link #useDom} is <tt>true</tt>, a string specification
11130  * results in the creation of a text node. Usage:</p>
11131  * <pre><code>
11132 Ext.DomHelper.useDom = true; // force it to use DOM; reduces performance
11133  * </code></pre>
11134  * @singleton
11135  */
11136 Ext.ns('Ext.core');
11137 Ext.core.DomHelper = Ext.DomHelper = function(){
11138     var tempTableEl = null,
11139         emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i,
11140         tableRe = /^table|tbody|tr|td$/i,
11141         confRe = /tag|children|cn|html$/i,
11142         tableElRe = /td|tr|tbody/i,
11143         endRe = /end/i,
11144         pub,
11145         // kill repeat to save bytes
11146         afterbegin = 'afterbegin',
11147         afterend = 'afterend',
11148         beforebegin = 'beforebegin',
11149         beforeend = 'beforeend',
11150         ts = '<table>',
11151         te = '</table>',
11152         tbs = ts+'<tbody>',
11153         tbe = '</tbody>'+te,
11154         trs = tbs + '<tr>',
11155         tre = '</tr>'+tbe;
11156
11157     // private
11158     function doInsert(el, o, returnElement, pos, sibling, append){
11159         el = Ext.getDom(el);
11160         var newNode;
11161         if (pub.useDom) {
11162             newNode = createDom(o, null);
11163             if (append) {
11164                 el.appendChild(newNode);
11165             } else {
11166                 (sibling == 'firstChild' ? el : el.parentNode).insertBefore(newNode, el[sibling] || el);
11167             }
11168         } else {
11169             newNode = Ext.DomHelper.insertHtml(pos, el, Ext.DomHelper.createHtml(o));
11170         }
11171         return returnElement ? Ext.get(newNode, true) : newNode;
11172     }
11173
11174     function createDom(o, parentNode){
11175         var el,
11176             doc = document,
11177             useSet,
11178             attr,
11179             val,
11180             cn;
11181
11182         if (Ext.isArray(o)) {                       // Allow Arrays of siblings to be inserted
11183             el = doc.createDocumentFragment(); // in one shot using a DocumentFragment
11184             for (var i = 0, l = o.length; i < l; i++) {
11185                 createDom(o[i], el);
11186             }
11187         } else if (typeof o == 'string') {         // Allow a string as a child spec.
11188             el = doc.createTextNode(o);
11189         } else {
11190             el = doc.createElement( o.tag || 'div' );
11191             useSet = !!el.setAttribute; // In IE some elements don't have setAttribute
11192             for (attr in o) {
11193                 if(!confRe.test(attr)){
11194                     val = o[attr];
11195                     if(attr == 'cls'){
11196                         el.className = val;
11197                     }else{
11198                         if(useSet){
11199                             el.setAttribute(attr, val);
11200                         }else{
11201                             el[attr] = val;
11202                         }
11203                     }
11204                 }
11205             }
11206             Ext.DomHelper.applyStyles(el, o.style);
11207
11208             if ((cn = o.children || o.cn)) {
11209                 createDom(cn, el);
11210             } else if (o.html) {
11211                 el.innerHTML = o.html;
11212             }
11213         }
11214         if(parentNode){
11215            parentNode.appendChild(el);
11216         }
11217         return el;
11218     }
11219
11220     // build as innerHTML where available
11221     function createHtml(o){
11222         var b = '',
11223             attr,
11224             val,
11225             key,
11226             cn,
11227             i;
11228
11229         if(typeof o == "string"){
11230             b = o;
11231         } else if (Ext.isArray(o)) {
11232             for (i=0; i < o.length; i++) {
11233                 if(o[i]) {
11234                     b += createHtml(o[i]);
11235                 }
11236             }
11237         } else {
11238             b += '<' + (o.tag = o.tag || 'div');
11239             for (attr in o) {
11240                 val = o[attr];
11241                 if(!confRe.test(attr)){
11242                     if (typeof val == "object") {
11243                         b += ' ' + attr + '="';
11244                         for (key in val) {
11245                             b += key + ':' + val[key] + ';';
11246                         }
11247                         b += '"';
11248                     }else{
11249                         b += ' ' + ({cls : 'class', htmlFor : 'for'}[attr] || attr) + '="' + val + '"';
11250                     }
11251                 }
11252             }
11253             // Now either just close the tag or try to add children and close the tag.
11254             if (emptyTags.test(o.tag)) {
11255                 b += '/>';
11256             } else {
11257                 b += '>';
11258                 if ((cn = o.children || o.cn)) {
11259                     b += createHtml(cn);
11260                 } else if(o.html){
11261                     b += o.html;
11262                 }
11263                 b += '</' + o.tag + '>';
11264             }
11265         }
11266         return b;
11267     }
11268
11269     function ieTable(depth, s, h, e){
11270         tempTableEl.innerHTML = [s, h, e].join('');
11271         var i = -1,
11272             el = tempTableEl,
11273             ns;
11274         while(++i < depth){
11275             el = el.firstChild;
11276         }
11277 //      If the result is multiple siblings, then encapsulate them into one fragment.
11278         ns = el.nextSibling;
11279         if (ns){
11280             var df = document.createDocumentFragment();
11281             while(el){
11282                 ns = el.nextSibling;
11283                 df.appendChild(el);
11284                 el = ns;
11285             }
11286             el = df;
11287         }
11288         return el;
11289     }
11290
11291     /**
11292      * @ignore
11293      * Nasty code for IE's broken table implementation
11294      */
11295     function insertIntoTable(tag, where, el, html) {
11296         var node,
11297             before;
11298
11299         tempTableEl = tempTableEl || document.createElement('div');
11300
11301         if(tag == 'td' && (where == afterbegin || where == beforeend) ||
11302            !tableElRe.test(tag) && (where == beforebegin || where == afterend)) {
11303             return null;
11304         }
11305         before = where == beforebegin ? el :
11306                  where == afterend ? el.nextSibling :
11307                  where == afterbegin ? el.firstChild : null;
11308
11309         if (where == beforebegin || where == afterend) {
11310             el = el.parentNode;
11311         }
11312
11313         if (tag == 'td' || (tag == 'tr' && (where == beforeend || where == afterbegin))) {
11314             node = ieTable(4, trs, html, tre);
11315         } else if ((tag == 'tbody' && (where == beforeend || where == afterbegin)) ||
11316                    (tag == 'tr' && (where == beforebegin || where == afterend))) {
11317             node = ieTable(3, tbs, html, tbe);
11318         } else {
11319             node = ieTable(2, ts, html, te);
11320         }
11321         el.insertBefore(node, before);
11322         return node;
11323     }
11324
11325     /**
11326      * @ignore
11327      * Fix for IE9 createContextualFragment missing method
11328      */
11329     function createContextualFragment(html){
11330         var div = document.createElement("div"),
11331             fragment = document.createDocumentFragment(),
11332             i = 0,
11333             length, childNodes;
11334
11335         div.innerHTML = html;
11336         childNodes = div.childNodes;
11337         length = childNodes.length;
11338
11339         for (; i < length; i++) {
11340             fragment.appendChild(childNodes[i].cloneNode(true));
11341         }
11342
11343         return fragment;
11344     }
11345
11346     pub = {
11347         /**
11348          * Returns the markup for the passed Element(s) config.
11349          * @param {Object} o The DOM object spec (and children)
11350          * @return {String}
11351          */
11352         markup : function(o){
11353             return createHtml(o);
11354         },
11355
11356         /**
11357          * Applies a style specification to an element.
11358          * @param {String/HTMLElement} el The element to apply styles to
11359          * @param {String/Object/Function} styles A style specification string e.g. 'width:100px', or object in the form {width:'100px'}, or
11360          * a function which returns such a specification.
11361          */
11362         applyStyles : function(el, styles){
11363             if (styles) {
11364                 el = Ext.fly(el);
11365                 if (typeof styles == "function") {
11366                     styles = styles.call();
11367                 }
11368                 if (typeof styles == "string") {
11369                     styles = Ext.Element.parseStyles(styles);
11370                 }
11371                 if (typeof styles == "object") {
11372                     el.setStyle(styles);
11373                 }
11374             }
11375         },
11376
11377         /**
11378          * Inserts an HTML fragment into the DOM.
11379          * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
11380          *
11381          * For example take the following HTML: `<div>Contents</div>`
11382          *
11383          * Using different `where` values inserts element to the following places:
11384          *
11385          * - beforeBegin: `<HERE><div>Contents</div>`
11386          * - afterBegin: `<div><HERE>Contents</div>`
11387          * - beforeEnd: `<div>Contents<HERE></div>`
11388          * - afterEnd: `<div>Contents</div><HERE>`
11389          *
11390          * @param {HTMLElement/TextNode} el The context element
11391          * @param {String} html The HTML fragment
11392          * @return {HTMLElement} The new node
11393          */
11394         insertHtml : function(where, el, html){
11395             var hash = {},
11396                 hashVal,
11397                 range,
11398                 rangeEl,
11399                 setStart,
11400                 frag,
11401                 rs;
11402
11403             where = where.toLowerCase();
11404             // add these here because they are used in both branches of the condition.
11405             hash[beforebegin] = ['BeforeBegin', 'previousSibling'];
11406             hash[afterend] = ['AfterEnd', 'nextSibling'];
11407
11408             // if IE and context element is an HTMLElement
11409             if (el.insertAdjacentHTML) {
11410                 if(tableRe.test(el.tagName) && (rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html))){
11411                     return rs;
11412                 }
11413
11414                 // add these two to the hash.
11415                 hash[afterbegin] = ['AfterBegin', 'firstChild'];
11416                 hash[beforeend] = ['BeforeEnd', 'lastChild'];
11417                 if ((hashVal = hash[where])) {
11418                     el.insertAdjacentHTML(hashVal[0], html);
11419                     return el[hashVal[1]];
11420                 }
11421             // if (not IE and context element is an HTMLElement) or TextNode
11422             } else {
11423                 // we cannot insert anything inside a textnode so...
11424                 if (Ext.isTextNode(el)) {
11425                     where = where === 'afterbegin' ? 'beforebegin' : where;
11426                     where = where === 'beforeend' ? 'afterend' : where;
11427                 }
11428                 range = Ext.supports.CreateContextualFragment ? el.ownerDocument.createRange() : undefined;
11429                 setStart = 'setStart' + (endRe.test(where) ? 'After' : 'Before');
11430                 if (hash[where]) {
11431                     if (range) {
11432                         range[setStart](el);
11433                         frag = range.createContextualFragment(html);
11434                     } else {
11435                         frag = createContextualFragment(html);
11436                     }
11437                     el.parentNode.insertBefore(frag, where == beforebegin ? el : el.nextSibling);
11438                     return el[(where == beforebegin ? 'previous' : 'next') + 'Sibling'];
11439                 } else {
11440                     rangeEl = (where == afterbegin ? 'first' : 'last') + 'Child';
11441                     if (el.firstChild) {
11442                         if (range) {
11443                             range[setStart](el[rangeEl]);
11444                             frag = range.createContextualFragment(html);
11445                         } else {
11446                             frag = createContextualFragment(html);
11447                         }
11448
11449                         if(where == afterbegin){
11450                             el.insertBefore(frag, el.firstChild);
11451                         }else{
11452                             el.appendChild(frag);
11453                         }
11454                     } else {
11455                         el.innerHTML = html;
11456                     }
11457                     return el[rangeEl];
11458                 }
11459             }
11460         },
11461
11462         /**
11463          * Creates new DOM element(s) and inserts them before el.
11464          * @param {String/HTMLElement/Ext.Element} el The context element
11465          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
11466          * @param {Boolean} returnElement (optional) true to return a Ext.Element
11467          * @return {HTMLElement/Ext.Element} The new node
11468          */
11469         insertBefore : function(el, o, returnElement){
11470             return doInsert(el, o, returnElement, beforebegin);
11471         },
11472
11473         /**
11474          * Creates new DOM element(s) and inserts them after el.
11475          * @param {String/HTMLElement/Ext.Element} el The context element
11476          * @param {Object} o The DOM object spec (and children)
11477          * @param {Boolean} returnElement (optional) true to return a Ext.Element
11478          * @return {HTMLElement/Ext.Element} The new node
11479          */
11480         insertAfter : function(el, o, returnElement){
11481             return doInsert(el, o, returnElement, afterend, 'nextSibling');
11482         },
11483
11484         /**
11485          * Creates new DOM element(s) and inserts them as the first child of el.
11486          * @param {String/HTMLElement/Ext.Element} el The context element
11487          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
11488          * @param {Boolean} returnElement (optional) true to return a Ext.Element
11489          * @return {HTMLElement/Ext.Element} The new node
11490          */
11491         insertFirst : function(el, o, returnElement){
11492             return doInsert(el, o, returnElement, afterbegin, 'firstChild');
11493         },
11494
11495         /**
11496          * Creates new DOM element(s) and appends them to el.
11497          * @param {String/HTMLElement/Ext.Element} el The context element
11498          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
11499          * @param {Boolean} returnElement (optional) true to return a Ext.Element
11500          * @return {HTMLElement/Ext.Element} The new node
11501          */
11502         append : function(el, o, returnElement){
11503             return doInsert(el, o, returnElement, beforeend, '', true);
11504         },
11505
11506         /**
11507          * Creates new DOM element(s) and overwrites the contents of el with them.
11508          * @param {String/HTMLElement/Ext.Element} el The context element
11509          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
11510          * @param {Boolean} returnElement (optional) true to return a Ext.Element
11511          * @return {HTMLElement/Ext.Element} The new node
11512          */
11513         overwrite : function(el, o, returnElement){
11514             el = Ext.getDom(el);
11515             el.innerHTML = createHtml(o);
11516             return returnElement ? Ext.get(el.firstChild) : el.firstChild;
11517         },
11518
11519         createHtml : createHtml,
11520
11521         /**
11522          * Creates new DOM element(s) without inserting them to the document.
11523          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
11524          * @return {HTMLElement} The new uninserted node
11525          * @method
11526          */
11527         createDom: createDom,
11528
11529         /** True to force the use of DOM instead of html fragments @type Boolean */
11530         useDom : false,
11531
11532         /**
11533          * Creates a new Ext.Template from the DOM object spec.
11534          * @param {Object} o The DOM object spec (and children)
11535          * @return {Ext.Template} The new template
11536          */
11537         createTemplate : function(o){
11538             var html = Ext.DomHelper.createHtml(o);
11539             return Ext.create('Ext.Template', html);
11540         }
11541     };
11542     return pub;
11543 }();
11544
11545 /*
11546  * This is code is also distributed under MIT license for use
11547  * with jQuery and prototype JavaScript libraries.
11548  */
11549 /**
11550  * @class Ext.DomQuery
11551 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
11552 <p>
11553 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/#selectors">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
11554
11555 <p>
11556 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
11557 </p>
11558 <h4>Element Selectors:</h4>
11559 <ul class="list">
11560     <li> <b>*</b> any element</li>
11561     <li> <b>E</b> an element with the tag E</li>
11562     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
11563     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
11564     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
11565     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
11566 </ul>
11567 <h4>Attribute Selectors:</h4>
11568 <p>The use of &#64; and quotes are optional. For example, div[&#64;foo='bar'] is also a valid attribute selector.</p>
11569 <ul class="list">
11570     <li> <b>E[foo]</b> has an attribute "foo"</li>
11571     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
11572     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
11573     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
11574     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
11575     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
11576     <li> <b>E[foo!=bar]</b> attribute "foo" does not equal "bar"</li>
11577 </ul>
11578 <h4>Pseudo Classes:</h4>
11579 <ul class="list">
11580     <li> <b>E:first-child</b> E is the first child of its parent</li>
11581     <li> <b>E:last-child</b> E is the last child of its parent</li>
11582     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
11583     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
11584     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
11585     <li> <b>E:only-child</b> E is the only child of its parent</li>
11586     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
11587     <li> <b>E:first</b> the first E in the resultset</li>
11588     <li> <b>E:last</b> the last E in the resultset</li>
11589     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
11590     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
11591     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
11592     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
11593     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
11594     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
11595     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
11596     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
11597     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
11598     <li> <b>E:any(S1|S2|S2)</b> an E element which matches any of the simple selectors S1, S2 or S3//\\</li>
11599 </ul>
11600 <h4>CSS Value Selectors:</h4>
11601 <ul class="list">
11602     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
11603     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
11604     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
11605     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
11606     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
11607     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
11608 </ul>
11609  * @singleton
11610  */
11611 Ext.ns('Ext.core');
11612
11613 Ext.core.DomQuery = Ext.DomQuery = function(){
11614     var cache = {},
11615         simpleCache = {},
11616         valueCache = {},
11617         nonSpace = /\S/,
11618         trimRe = /^\s+|\s+$/g,
11619         tplRe = /\{(\d+)\}/g,
11620         modeRe = /^(\s?[\/>+~]\s?|\s|$)/,
11621         tagTokenRe = /^(#)?([\w-\*]+)/,
11622         nthRe = /(\d*)n\+?(\d*)/,
11623         nthRe2 = /\D/,
11624         startIdRe = /^\s*\#/,
11625         // This is for IE MSXML which does not support expandos.
11626     // IE runs the same speed using setAttribute, however FF slows way down
11627     // and Safari completely fails so they need to continue to use expandos.
11628     isIE = window.ActiveXObject ? true : false,
11629     key = 30803;
11630
11631     // this eval is stop the compressor from
11632     // renaming the variable to something shorter
11633     eval("var batch = 30803;");
11634
11635     // Retrieve the child node from a particular
11636     // parent at the specified index.
11637     function child(parent, index){
11638         var i = 0,
11639             n = parent.firstChild;
11640         while(n){
11641             if(n.nodeType == 1){
11642                if(++i == index){
11643                    return n;
11644                }
11645             }
11646             n = n.nextSibling;
11647         }
11648         return null;
11649     }
11650
11651     // retrieve the next element node
11652     function next(n){
11653         while((n = n.nextSibling) && n.nodeType != 1);
11654         return n;
11655     }
11656
11657     // retrieve the previous element node
11658     function prev(n){
11659         while((n = n.previousSibling) && n.nodeType != 1);
11660         return n;
11661     }
11662
11663     // Mark each child node with a nodeIndex skipping and
11664     // removing empty text nodes.
11665     function children(parent){
11666         var n = parent.firstChild,
11667         nodeIndex = -1,
11668         nextNode;
11669         while(n){
11670             nextNode = n.nextSibling;
11671             // clean worthless empty nodes.
11672             if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
11673             parent.removeChild(n);
11674             }else{
11675             // add an expando nodeIndex
11676             n.nodeIndex = ++nodeIndex;
11677             }
11678             n = nextNode;
11679         }
11680         return this;
11681     }
11682
11683
11684     // nodeSet - array of nodes
11685     // cls - CSS Class
11686     function byClassName(nodeSet, cls){
11687         if(!cls){
11688             return nodeSet;
11689         }
11690         var result = [], ri = -1;
11691         for(var i = 0, ci; ci = nodeSet[i]; i++){
11692             if((' '+ci.className+' ').indexOf(cls) != -1){
11693                 result[++ri] = ci;
11694             }
11695         }
11696         return result;
11697     };
11698
11699     function attrValue(n, attr){
11700         // if its an array, use the first node.
11701         if(!n.tagName && typeof n.length != "undefined"){
11702             n = n[0];
11703         }
11704         if(!n){
11705             return null;
11706         }
11707
11708         if(attr == "for"){
11709             return n.htmlFor;
11710         }
11711         if(attr == "class" || attr == "className"){
11712             return n.className;
11713         }
11714         return n.getAttribute(attr) || n[attr];
11715
11716     };
11717
11718
11719     // ns - nodes
11720     // mode - false, /, >, +, ~
11721     // tagName - defaults to "*"
11722     function getNodes(ns, mode, tagName){
11723         var result = [], ri = -1, cs;
11724         if(!ns){
11725             return result;
11726         }
11727         tagName = tagName || "*";
11728         // convert to array
11729         if(typeof ns.getElementsByTagName != "undefined"){
11730             ns = [ns];
11731         }
11732
11733         // no mode specified, grab all elements by tagName
11734         // at any depth
11735         if(!mode){
11736             for(var i = 0, ni; ni = ns[i]; i++){
11737                 cs = ni.getElementsByTagName(tagName);
11738                 for(var j = 0, ci; ci = cs[j]; j++){
11739                     result[++ri] = ci;
11740                 }
11741             }
11742         // Direct Child mode (/ or >)
11743         // E > F or E/F all direct children elements of E that have the tag
11744         } else if(mode == "/" || mode == ">"){
11745             var utag = tagName.toUpperCase();
11746             for(var i = 0, ni, cn; ni = ns[i]; i++){
11747                 cn = ni.childNodes;
11748                 for(var j = 0, cj; cj = cn[j]; j++){
11749                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
11750                         result[++ri] = cj;
11751                     }
11752                 }
11753             }
11754         // Immediately Preceding mode (+)
11755         // E + F all elements with the tag F that are immediately preceded by an element with the tag E
11756         }else if(mode == "+"){
11757             var utag = tagName.toUpperCase();
11758             for(var i = 0, n; n = ns[i]; i++){
11759                 while((n = n.nextSibling) && n.nodeType != 1);
11760                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
11761                     result[++ri] = n;
11762                 }
11763             }
11764         // Sibling mode (~)
11765         // E ~ F all elements with the tag F that are preceded by a sibling element with the tag E
11766         }else if(mode == "~"){
11767             var utag = tagName.toUpperCase();
11768             for(var i = 0, n; n = ns[i]; i++){
11769                 while((n = n.nextSibling)){
11770                     if (n.nodeName == utag || n.nodeName == tagName || tagName == '*'){
11771                         result[++ri] = n;
11772                     }
11773                 }
11774             }
11775         }
11776         return result;
11777     }
11778
11779     function concat(a, b){
11780         if(b.slice){
11781             return a.concat(b);
11782         }
11783         for(var i = 0, l = b.length; i < l; i++){
11784             a[a.length] = b[i];
11785         }
11786         return a;
11787     }
11788
11789     function byTag(cs, tagName){
11790         if(cs.tagName || cs == document){
11791             cs = [cs];
11792         }
11793         if(!tagName){
11794             return cs;
11795         }
11796         var result = [], ri = -1;
11797         tagName = tagName.toLowerCase();
11798         for(var i = 0, ci; ci = cs[i]; i++){
11799             if(ci.nodeType == 1 && ci.tagName.toLowerCase() == tagName){
11800                 result[++ri] = ci;
11801             }
11802         }
11803         return result;
11804     }
11805
11806     function byId(cs, id){
11807         if(cs.tagName || cs == document){
11808             cs = [cs];
11809         }
11810         if(!id){
11811             return cs;
11812         }
11813         var result = [], ri = -1;
11814         for(var i = 0, ci; ci = cs[i]; i++){
11815             if(ci && ci.id == id){
11816                 result[++ri] = ci;
11817                 return result;
11818             }
11819         }
11820         return result;
11821     }
11822
11823     // operators are =, !=, ^=, $=, *=, %=, |= and ~=
11824     // custom can be "{"
11825     function byAttribute(cs, attr, value, op, custom){
11826         var result = [],
11827             ri = -1,
11828             useGetStyle = custom == "{",
11829             fn = Ext.DomQuery.operators[op],
11830             a,
11831             xml,
11832             hasXml;
11833
11834         for(var i = 0, ci; ci = cs[i]; i++){
11835             // skip non-element nodes.
11836             if(ci.nodeType != 1){
11837                 continue;
11838             }
11839             // only need to do this for the first node
11840             if(!hasXml){
11841                 xml = Ext.DomQuery.isXml(ci);
11842                 hasXml = true;
11843             }
11844
11845             // we only need to change the property names if we're dealing with html nodes, not XML
11846             if(!xml){
11847                 if(useGetStyle){
11848                     a = Ext.DomQuery.getStyle(ci, attr);
11849                 } else if (attr == "class" || attr == "className"){
11850                     a = ci.className;
11851                 } else if (attr == "for"){
11852                     a = ci.htmlFor;
11853                 } else if (attr == "href"){
11854                     // getAttribute href bug
11855                     // http://www.glennjones.net/Post/809/getAttributehrefbug.htm
11856                     a = ci.getAttribute("href", 2);
11857                 } else{
11858                     a = ci.getAttribute(attr);
11859                 }
11860             }else{
11861                 a = ci.getAttribute(attr);
11862             }
11863             if((fn && fn(a, value)) || (!fn && a)){
11864                 result[++ri] = ci;
11865             }
11866         }
11867         return result;
11868     }
11869
11870     function byPseudo(cs, name, value){
11871         return Ext.DomQuery.pseudos[name](cs, value);
11872     }
11873
11874     function nodupIEXml(cs){
11875         var d = ++key,
11876             r;
11877         cs[0].setAttribute("_nodup", d);
11878         r = [cs[0]];
11879         for(var i = 1, len = cs.length; i < len; i++){
11880             var c = cs[i];
11881             if(!c.getAttribute("_nodup") != d){
11882                 c.setAttribute("_nodup", d);
11883                 r[r.length] = c;
11884             }
11885         }
11886         for(var i = 0, len = cs.length; i < len; i++){
11887             cs[i].removeAttribute("_nodup");
11888         }
11889         return r;
11890     }
11891
11892     function nodup(cs){
11893         if(!cs){
11894             return [];
11895         }
11896         var len = cs.length, c, i, r = cs, cj, ri = -1;
11897         if(!len || typeof cs.nodeType != "undefined" || len == 1){
11898             return cs;
11899         }
11900         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
11901             return nodupIEXml(cs);
11902         }
11903         var d = ++key;
11904         cs[0]._nodup = d;
11905         for(i = 1; c = cs[i]; i++){
11906             if(c._nodup != d){
11907                 c._nodup = d;
11908             }else{
11909                 r = [];
11910                 for(var j = 0; j < i; j++){
11911                     r[++ri] = cs[j];
11912                 }
11913                 for(j = i+1; cj = cs[j]; j++){
11914                     if(cj._nodup != d){
11915                         cj._nodup = d;
11916                         r[++ri] = cj;
11917                     }
11918                 }
11919                 return r;
11920             }
11921         }
11922         return r;
11923     }
11924
11925     function quickDiffIEXml(c1, c2){
11926         var d = ++key,
11927             r = [];
11928         for(var i = 0, len = c1.length; i < len; i++){
11929             c1[i].setAttribute("_qdiff", d);
11930         }
11931         for(var i = 0, len = c2.length; i < len; i++){
11932             if(c2[i].getAttribute("_qdiff") != d){
11933                 r[r.length] = c2[i];
11934             }
11935         }
11936         for(var i = 0, len = c1.length; i < len; i++){
11937            c1[i].removeAttribute("_qdiff");
11938         }
11939         return r;
11940     }
11941
11942     function quickDiff(c1, c2){
11943         var len1 = c1.length,
11944             d = ++key,
11945             r = [];
11946         if(!len1){
11947             return c2;
11948         }
11949         if(isIE && typeof c1[0].selectSingleNode != "undefined"){
11950             return quickDiffIEXml(c1, c2);
11951         }
11952         for(var i = 0; i < len1; i++){
11953             c1[i]._qdiff = d;
11954         }
11955         for(var i = 0, len = c2.length; i < len; i++){
11956             if(c2[i]._qdiff != d){
11957                 r[r.length] = c2[i];
11958             }
11959         }
11960         return r;
11961     }
11962
11963     function quickId(ns, mode, root, id){
11964         if(ns == root){
11965            var d = root.ownerDocument || root;
11966            return d.getElementById(id);
11967         }
11968         ns = getNodes(ns, mode, "*");
11969         return byId(ns, id);
11970     }
11971
11972     return {
11973         getStyle : function(el, name){
11974             return Ext.fly(el).getStyle(name);
11975         },
11976         /**
11977          * Compiles a selector/xpath query into a reusable function. The returned function
11978          * takes one parameter "root" (optional), which is the context node from where the query should start.
11979          * @param {String} selector The selector/xpath query
11980          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
11981          * @return {Function}
11982          */
11983         compile : function(path, type){
11984             type = type || "select";
11985
11986             // setup fn preamble
11987             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"],
11988                 mode,
11989                 lastPath,
11990                 matchers = Ext.DomQuery.matchers,
11991                 matchersLn = matchers.length,
11992                 modeMatch,
11993                 // accept leading mode switch
11994                 lmode = path.match(modeRe);
11995
11996             if(lmode && lmode[1]){
11997                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
11998                 path = path.replace(lmode[1], "");
11999             }
12000
12001             // strip leading slashes
12002             while(path.substr(0, 1)=="/"){
12003                 path = path.substr(1);
12004             }
12005
12006             while(path && lastPath != path){
12007                 lastPath = path;
12008                 var tokenMatch = path.match(tagTokenRe);
12009                 if(type == "select"){
12010                     if(tokenMatch){
12011                         // ID Selector
12012                         if(tokenMatch[1] == "#"){
12013                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tokenMatch[2]+'");';
12014                         }else{
12015                             fn[fn.length] = 'n = getNodes(n, mode, "'+tokenMatch[2]+'");';
12016                         }
12017                         path = path.replace(tokenMatch[0], "");
12018                     }else if(path.substr(0, 1) != '@'){
12019                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
12020                     }
12021                 // type of "simple"
12022                 }else{
12023                     if(tokenMatch){
12024                         if(tokenMatch[1] == "#"){
12025                             fn[fn.length] = 'n = byId(n, "'+tokenMatch[2]+'");';
12026                         }else{
12027                             fn[fn.length] = 'n = byTag(n, "'+tokenMatch[2]+'");';
12028                         }
12029                         path = path.replace(tokenMatch[0], "");
12030                     }
12031                 }
12032                 while(!(modeMatch = path.match(modeRe))){
12033                     var matched = false;
12034                     for(var j = 0; j < matchersLn; j++){
12035                         var t = matchers[j];
12036                         var m = path.match(t.re);
12037                         if(m){
12038                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
12039                                 return m[i];
12040                             });
12041                             path = path.replace(m[0], "");
12042                             matched = true;
12043                             break;
12044                         }
12045                     }
12046                     // prevent infinite loop on bad selector
12047                     if(!matched){
12048                     }
12049                 }
12050                 if(modeMatch[1]){
12051                     fn[fn.length] = 'mode="'+modeMatch[1].replace(trimRe, "")+'";';
12052                     path = path.replace(modeMatch[1], "");
12053                 }
12054             }
12055             // close fn out
12056             fn[fn.length] = "return nodup(n);\n}";
12057
12058             // eval fn and return it
12059             eval(fn.join(""));
12060             return f;
12061         },
12062
12063         /**
12064          * Selects an array of DOM nodes using JavaScript-only implementation.
12065          *
12066          * Use {@link #select} to take advantage of browsers built-in support for CSS selectors.
12067          *
12068          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
12069          * @param {HTMLElement/String} root (optional) The start of the query (defaults to document).
12070          * @return {HTMLElement[]} An Array of DOM elements which match the selector. If there are
12071          * no matches, and empty Array is returned.
12072          */
12073         jsSelect: function(path, root, type){
12074             // set root to doc if not specified.
12075             root = root || document;
12076
12077             if(typeof root == "string"){
12078                 root = document.getElementById(root);
12079             }
12080             var paths = path.split(","),
12081                 results = [];
12082
12083             // loop over each selector
12084             for(var i = 0, len = paths.length; i < len; i++){
12085                 var subPath = paths[i].replace(trimRe, "");
12086                 // compile and place in cache
12087                 if(!cache[subPath]){
12088                     cache[subPath] = Ext.DomQuery.compile(subPath);
12089                     if(!cache[subPath]){
12090                     }
12091                 }
12092                 var result = cache[subPath](root);
12093                 if(result && result != document){
12094                     results = results.concat(result);
12095                 }
12096             }
12097
12098             // if there were multiple selectors, make sure dups
12099             // are eliminated
12100             if(paths.length > 1){
12101                 return nodup(results);
12102             }
12103             return results;
12104         },
12105
12106         isXml: function(el) {
12107             var docEl = (el ? el.ownerDocument || el : 0).documentElement;
12108             return docEl ? docEl.nodeName !== "HTML" : false;
12109         },
12110
12111         /**
12112          * Selects an array of DOM nodes by CSS/XPath selector.
12113          *
12114          * Uses [document.querySelectorAll][0] if browser supports that, otherwise falls back to
12115          * {@link Ext.DomQuery#jsSelect} to do the work.
12116          *
12117          * Aliased as {@link Ext#query}.
12118          *
12119          * [0]: https://developer.mozilla.org/en/DOM/document.querySelectorAll
12120          *
12121          * @param {String} path The selector/xpath query
12122          * @param {HTMLElement} root (optional) The start of the query (defaults to document).
12123          * @return {HTMLElement[]} An array of DOM elements (not a NodeList as returned by `querySelectorAll`).
12124          * Empty array when no matches.
12125          * @method
12126          */
12127         select : document.querySelectorAll ? function(path, root, type) {
12128             root = root || document;
12129             /* 
12130              * Safari 3.x can't handle uppercase or unicode characters when in quirks mode.
12131              */
12132             if (!Ext.DomQuery.isXml(root) && !(Ext.isSafari3 && !Ext.isStrict)) { 
12133                 try {
12134                     /*
12135                      * This checking here is to "fix" the behaviour of querySelectorAll
12136                      * for non root document queries. The way qsa works is intentional,
12137                      * however it's definitely not the expected way it should work.
12138                      * More info: http://ejohn.org/blog/thoughts-on-queryselectorall/
12139                      *
12140                      * We only modify the path for single selectors (ie, no multiples),
12141                      * without a full parser it makes it difficult to do this correctly.
12142                      */
12143                     var isDocumentRoot = root.nodeType === 9,
12144                         _path = path,
12145                         _root = root;
12146
12147                     if (!isDocumentRoot && path.indexOf(',') === -1 && !startIdRe.test(path)) {
12148                         _path = '#' + Ext.id(root) + ' ' + path;
12149                         _root = root.parentNode;
12150                     }
12151                     return Ext.Array.toArray(_root.querySelectorAll(_path));
12152                 }
12153                 catch (e) {
12154                 }
12155             }
12156             return Ext.DomQuery.jsSelect.call(this, path, root, type);
12157         } : function(path, root, type) {
12158             return Ext.DomQuery.jsSelect.call(this, path, root, type);
12159         },
12160
12161         /**
12162          * Selects a single element.
12163          * @param {String} selector The selector/xpath query
12164          * @param {HTMLElement} root (optional) The start of the query (defaults to document).
12165          * @return {HTMLElement} The DOM element which matched the selector.
12166          */
12167         selectNode : function(path, root){
12168             return Ext.DomQuery.select(path, root)[0];
12169         },
12170
12171         /**
12172          * Selects the value of a node, optionally replacing null with the defaultValue.
12173          * @param {String} selector The selector/xpath query
12174          * @param {HTMLElement} root (optional) The start of the query (defaults to document).
12175          * @param {String} defaultValue (optional) When specified, this is return as empty value.
12176          * @return {String}
12177          */
12178         selectValue : function(path, root, defaultValue){
12179             path = path.replace(trimRe, "");
12180             if(!valueCache[path]){
12181                 valueCache[path] = Ext.DomQuery.compile(path, "select");
12182             }
12183             var n = valueCache[path](root), v;
12184             n = n[0] ? n[0] : n;
12185
12186             // overcome a limitation of maximum textnode size
12187             // Rumored to potentially crash IE6 but has not been confirmed.
12188             // http://reference.sitepoint.com/javascript/Node/normalize
12189             // https://developer.mozilla.org/En/DOM/Node.normalize
12190             if (typeof n.normalize == 'function') n.normalize();
12191
12192             v = (n && n.firstChild ? n.firstChild.nodeValue : null);
12193             return ((v === null||v === undefined||v==='') ? defaultValue : v);
12194         },
12195
12196         /**
12197          * Selects the value of a node, parsing integers and floats. Returns the defaultValue, or 0 if none is specified.
12198          * @param {String} selector The selector/xpath query
12199          * @param {HTMLElement} root (optional) The start of the query (defaults to document).
12200          * @param {Number} defaultValue (optional) When specified, this is return as empty value.
12201          * @return {Number}
12202          */
12203         selectNumber : function(path, root, defaultValue){
12204             var v = Ext.DomQuery.selectValue(path, root, defaultValue || 0);
12205             return parseFloat(v);
12206         },
12207
12208         /**
12209          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
12210          * @param {String/HTMLElement/HTMLElement[]} el An element id, element or array of elements
12211          * @param {String} selector The simple selector to test
12212          * @return {Boolean}
12213          */
12214         is : function(el, ss){
12215             if(typeof el == "string"){
12216                 el = document.getElementById(el);
12217             }
12218             var isArray = Ext.isArray(el),
12219                 result = Ext.DomQuery.filter(isArray ? el : [el], ss);
12220             return isArray ? (result.length == el.length) : (result.length > 0);
12221         },
12222
12223         /**
12224          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
12225          * @param {HTMLElement[]} el An array of elements to filter
12226          * @param {String} selector The simple selector to test
12227          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
12228          * the selector instead of the ones that match
12229          * @return {HTMLElement[]} An Array of DOM elements which match the selector. If there are
12230          * no matches, and empty Array is returned.
12231          */
12232         filter : function(els, ss, nonMatches){
12233             ss = ss.replace(trimRe, "");
12234             if(!simpleCache[ss]){
12235                 simpleCache[ss] = Ext.DomQuery.compile(ss, "simple");
12236             }
12237             var result = simpleCache[ss](els);
12238             return nonMatches ? quickDiff(result, els) : result;
12239         },
12240
12241         /**
12242          * Collection of matching regular expressions and code snippets.
12243          * Each capture group within () will be replace the {} in the select
12244          * statement as specified by their index.
12245          */
12246         matchers : [{
12247                 re: /^\.([\w-]+)/,
12248                 select: 'n = byClassName(n, " {1} ");'
12249             }, {
12250                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
12251                 select: 'n = byPseudo(n, "{1}", "{2}");'
12252             },{
12253                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
12254                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
12255             }, {
12256                 re: /^#([\w-]+)/,
12257                 select: 'n = byId(n, "{1}");'
12258             },{
12259                 re: /^@([\w-]+)/,
12260                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
12261             }
12262         ],
12263
12264         /**
12265          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
12266          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
12267          */
12268         operators : {
12269             "=" : function(a, v){
12270                 return a == v;
12271             },
12272             "!=" : function(a, v){
12273                 return a != v;
12274             },
12275             "^=" : function(a, v){
12276                 return a && a.substr(0, v.length) == v;
12277             },
12278             "$=" : function(a, v){
12279                 return a && a.substr(a.length-v.length) == v;
12280             },
12281             "*=" : function(a, v){
12282                 return a && a.indexOf(v) !== -1;
12283             },
12284             "%=" : function(a, v){
12285                 return (a % v) == 0;
12286             },
12287             "|=" : function(a, v){
12288                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
12289             },
12290             "~=" : function(a, v){
12291                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
12292             }
12293         },
12294
12295         /**
12296 Object hash of "pseudo class" filter functions which are used when filtering selections.
12297 Each function is passed two parameters:
12298
12299 - **c** : Array
12300     An Array of DOM elements to filter.
12301
12302 - **v** : String
12303     The argument (if any) supplied in the selector.
12304
12305 A filter function returns an Array of DOM elements which conform to the pseudo class.
12306 In addition to the provided pseudo classes listed above such as `first-child` and `nth-child`,
12307 developers may add additional, custom psuedo class filters to select elements according to application-specific requirements.
12308
12309 For example, to filter `a` elements to only return links to __external__ resources:
12310
12311     Ext.DomQuery.pseudos.external = function(c, v){
12312         var r = [], ri = -1;
12313         for(var i = 0, ci; ci = c[i]; i++){
12314             // Include in result set only if it's a link to an external resource
12315             if(ci.hostname != location.hostname){
12316                 r[++ri] = ci;
12317             }
12318         }
12319         return r;
12320     };
12321
12322 Then external links could be gathered with the following statement:
12323
12324     var externalLinks = Ext.select("a:external");
12325
12326         * @markdown
12327         */
12328         pseudos : {
12329             "first-child" : function(c){
12330                 var r = [], ri = -1, n;
12331                 for(var i = 0, ci; ci = n = c[i]; i++){
12332                     while((n = n.previousSibling) && n.nodeType != 1);
12333                     if(!n){
12334                         r[++ri] = ci;
12335                     }
12336                 }
12337                 return r;
12338             },
12339
12340             "last-child" : function(c){
12341                 var r = [], ri = -1, n;
12342                 for(var i = 0, ci; ci = n = c[i]; i++){
12343                     while((n = n.nextSibling) && n.nodeType != 1);
12344                     if(!n){
12345                         r[++ri] = ci;
12346                     }
12347                 }
12348                 return r;
12349             },
12350
12351             "nth-child" : function(c, a) {
12352                 var r = [], ri = -1,
12353                     m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a),
12354                     f = (m[1] || 1) - 0, l = m[2] - 0;
12355                 for(var i = 0, n; n = c[i]; i++){
12356                     var pn = n.parentNode;
12357                     if (batch != pn._batch) {
12358                         var j = 0;
12359                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
12360                             if(cn.nodeType == 1){
12361                                cn.nodeIndex = ++j;
12362                             }
12363                         }
12364                         pn._batch = batch;
12365                     }
12366                     if (f == 1) {
12367                         if (l == 0 || n.nodeIndex == l){
12368                             r[++ri] = n;
12369                         }
12370                     } else if ((n.nodeIndex + l) % f == 0){
12371                         r[++ri] = n;
12372                     }
12373                 }
12374
12375                 return r;
12376             },
12377
12378             "only-child" : function(c){
12379                 var r = [], ri = -1;;
12380                 for(var i = 0, ci; ci = c[i]; i++){
12381                     if(!prev(ci) && !next(ci)){
12382                         r[++ri] = ci;
12383                     }
12384                 }
12385                 return r;
12386             },
12387
12388             "empty" : function(c){
12389                 var r = [], ri = -1;
12390                 for(var i = 0, ci; ci = c[i]; i++){
12391                     var cns = ci.childNodes, j = 0, cn, empty = true;
12392                     while(cn = cns[j]){
12393                         ++j;
12394                         if(cn.nodeType == 1 || cn.nodeType == 3){
12395                             empty = false;
12396                             break;
12397                         }
12398                     }
12399                     if(empty){
12400                         r[++ri] = ci;
12401                     }
12402                 }
12403                 return r;
12404             },
12405
12406             "contains" : function(c, v){
12407                 var r = [], ri = -1;
12408                 for(var i = 0, ci; ci = c[i]; i++){
12409                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
12410                         r[++ri] = ci;
12411                     }
12412                 }
12413                 return r;
12414             },
12415
12416             "nodeValue" : function(c, v){
12417                 var r = [], ri = -1;
12418                 for(var i = 0, ci; ci = c[i]; i++){
12419                     if(ci.firstChild && ci.firstChild.nodeValue == v){
12420                         r[++ri] = ci;
12421                     }
12422                 }
12423                 return r;
12424             },
12425
12426             "checked" : function(c){
12427                 var r = [], ri = -1;
12428                 for(var i = 0, ci; ci = c[i]; i++){
12429                     if(ci.checked == true){
12430                         r[++ri] = ci;
12431                     }
12432                 }
12433                 return r;
12434             },
12435
12436             "not" : function(c, ss){
12437                 return Ext.DomQuery.filter(c, ss, true);
12438             },
12439
12440             "any" : function(c, selectors){
12441                 var ss = selectors.split('|'),
12442                     r = [], ri = -1, s;
12443                 for(var i = 0, ci; ci = c[i]; i++){
12444                     for(var j = 0; s = ss[j]; j++){
12445                         if(Ext.DomQuery.is(ci, s)){
12446                             r[++ri] = ci;
12447                             break;
12448                         }
12449                     }
12450                 }
12451                 return r;
12452             },
12453
12454             "odd" : function(c){
12455                 return this["nth-child"](c, "odd");
12456             },
12457
12458             "even" : function(c){
12459                 return this["nth-child"](c, "even");
12460             },
12461
12462             "nth" : function(c, a){
12463                 return c[a-1] || [];
12464             },
12465
12466             "first" : function(c){
12467                 return c[0] || [];
12468             },
12469
12470             "last" : function(c){
12471                 return c[c.length-1] || [];
12472             },
12473
12474             "has" : function(c, ss){
12475                 var s = Ext.DomQuery.select,
12476                     r = [], ri = -1;
12477                 for(var i = 0, ci; ci = c[i]; i++){
12478                     if(s(ss, ci).length > 0){
12479                         r[++ri] = ci;
12480                     }
12481                 }
12482                 return r;
12483             },
12484
12485             "next" : function(c, ss){
12486                 var is = Ext.DomQuery.is,
12487                     r = [], ri = -1;
12488                 for(var i = 0, ci; ci = c[i]; i++){
12489                     var n = next(ci);
12490                     if(n && is(n, ss)){
12491                         r[++ri] = ci;
12492                     }
12493                 }
12494                 return r;
12495             },
12496
12497             "prev" : function(c, ss){
12498                 var is = Ext.DomQuery.is,
12499                     r = [], ri = -1;
12500                 for(var i = 0, ci; ci = c[i]; i++){
12501                     var n = prev(ci);
12502                     if(n && is(n, ss)){
12503                         r[++ri] = ci;
12504                     }
12505                 }
12506                 return r;
12507             }
12508         }
12509     };
12510 }();
12511
12512 /**
12513  * Shorthand of {@link Ext.DomQuery#select}
12514  * @member Ext
12515  * @method query
12516  * @alias Ext.DomQuery#select
12517  */
12518 Ext.query = Ext.DomQuery.select;
12519
12520 /**
12521  * @class Ext.Element
12522  * @alternateClassName Ext.core.Element
12523  *
12524  * Encapsulates a DOM element, adding simple DOM manipulation facilities, normalizing for browser differences.
12525  *
12526  * All instances of this class inherit the methods of {@link Ext.fx.Anim} making visual effects easily available to all
12527  * DOM elements.
12528  *
12529  * Note that the events documented in this class are not Ext events, they encapsulate browser events. Some older browsers
12530  * may not support the full range of events. Which events are supported is beyond the control of Ext JS.
12531  *
12532  * Usage:
12533  *
12534  *     // by id
12535  *     var el = Ext.get("my-div");
12536  *
12537  *     // by DOM element reference
12538  *     var el = Ext.get(myDivElement);
12539  *
12540  * # Animations
12541  *
12542  * When an element is manipulated, by default there is no animation.
12543  *
12544  *     var el = Ext.get("my-div");
12545  *
12546  *     // no animation
12547  *     el.setWidth(100);
12548  *
12549  * Many of the functions for manipulating an element have an optional "animate" parameter. This parameter can be
12550  * specified as boolean (true) for default animation effects.
12551  *
12552  *     // default animation
12553  *     el.setWidth(100, true);
12554  *
12555  * To configure the effects, an object literal with animation options to use as the Element animation configuration
12556  * object can also be specified. Note that the supported Element animation configuration options are a subset of the
12557  * {@link Ext.fx.Anim} animation options specific to Fx effects. The supported Element animation configuration options
12558  * are:
12559  *
12560  *     Option    Default   Description
12561  *     --------- --------  ---------------------------------------------
12562  *     {@link Ext.fx.Anim#duration duration}  .35       The duration of the animation in seconds
12563  *     {@link Ext.fx.Anim#easing easing}    easeOut   The easing method
12564  *     {@link Ext.fx.Anim#callback callback}  none      A function to execute when the anim completes
12565  *     {@link Ext.fx.Anim#scope scope}     this      The scope (this) of the callback function
12566  *
12567  * Usage:
12568  *
12569  *     // Element animation options object
12570  *     var opt = {
12571  *         {@link Ext.fx.Anim#duration duration}: 1,
12572  *         {@link Ext.fx.Anim#easing easing}: 'elasticIn',
12573  *         {@link Ext.fx.Anim#callback callback}: this.foo,
12574  *         {@link Ext.fx.Anim#scope scope}: this
12575  *     };
12576  *     // animation with some options set
12577  *     el.setWidth(100, opt);
12578  *
12579  * The Element animation object being used for the animation will be set on the options object as "anim", which allows
12580  * you to stop or manipulate the animation. Here is an example:
12581  *
12582  *     // using the "anim" property to get the Anim object
12583  *     if(opt.anim.isAnimated()){
12584  *         opt.anim.stop();
12585  *     }
12586  *
12587  * # Composite (Collections of) Elements
12588  *
12589  * For working with collections of Elements, see {@link Ext.CompositeElement}
12590  *
12591  * @constructor
12592  * Creates new Element directly.
12593  * @param {String/HTMLElement} element
12594  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this
12595  * element in the cache and if there is it returns the same instance. This will skip that check (useful for extending
12596  * this class).
12597  * @return {Object}
12598  */
12599  (function() {
12600     var DOC = document,
12601         EC = Ext.cache;
12602
12603     Ext.Element = Ext.core.Element = function(element, forceNew) {
12604         var dom = typeof element == "string" ? DOC.getElementById(element) : element,
12605         id;
12606
12607         if (!dom) {
12608             return null;
12609         }
12610
12611         id = dom.id;
12612
12613         if (!forceNew && id && EC[id]) {
12614             // element object already exists
12615             return EC[id].el;
12616         }
12617
12618         /**
12619          * @property {HTMLElement} dom
12620          * The DOM element
12621          */
12622         this.dom = dom;
12623
12624         /**
12625          * @property {String} id
12626          * The DOM element ID
12627          */
12628         this.id = id || Ext.id(dom);
12629     };
12630
12631     var DH = Ext.DomHelper,
12632     El = Ext.Element;
12633
12634
12635     El.prototype = {
12636         /**
12637          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
12638          * @param {Object} o The object with the attributes
12639          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
12640          * @return {Ext.Element} this
12641          */
12642         set: function(o, useSet) {
12643             var el = this.dom,
12644                 attr,
12645                 val;
12646             useSet = (useSet !== false) && !!el.setAttribute;
12647
12648             for (attr in o) {
12649                 if (o.hasOwnProperty(attr)) {
12650                     val = o[attr];
12651                     if (attr == 'style') {
12652                         DH.applyStyles(el, val);
12653                     } else if (attr == 'cls') {
12654                         el.className = val;
12655                     } else if (useSet) {
12656                         el.setAttribute(attr, val);
12657                     } else {
12658                         el[attr] = val;
12659                     }
12660                 }
12661             }
12662             return this;
12663         },
12664
12665         //  Mouse events
12666         /**
12667          * @event click
12668          * Fires when a mouse click is detected within the element.
12669          * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12670          * @param {HTMLElement} t The target of the event.
12671          */
12672         /**
12673          * @event contextmenu
12674          * Fires when a right click is detected within the element.
12675          * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12676          * @param {HTMLElement} t The target of the event.
12677          */
12678         /**
12679          * @event dblclick
12680          * Fires when a mouse double click is detected within the element.
12681          * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12682          * @param {HTMLElement} t The target of the event.
12683          */
12684         /**
12685          * @event mousedown
12686          * Fires when a mousedown is detected within the element.
12687          * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12688          * @param {HTMLElement} t The target of the event.
12689          */
12690         /**
12691          * @event mouseup
12692          * Fires when a mouseup is detected within the element.
12693          * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12694          * @param {HTMLElement} t The target of the event.
12695          */
12696         /**
12697          * @event mouseover
12698          * Fires when a mouseover is detected within the element.
12699          * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12700          * @param {HTMLElement} t The target of the event.
12701          */
12702         /**
12703          * @event mousemove
12704          * Fires when a mousemove is detected with the element.
12705          * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12706          * @param {HTMLElement} t The target of the event.
12707          */
12708         /**
12709          * @event mouseout
12710          * Fires when a mouseout is detected with the element.
12711          * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12712          * @param {HTMLElement} t The target of the event.
12713          */
12714         /**
12715          * @event mouseenter
12716          * Fires when the mouse enters the element.
12717          * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12718          * @param {HTMLElement} t The target of the event.
12719          */
12720         /**
12721          * @event mouseleave
12722          * Fires when the mouse leaves the element.
12723          * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12724          * @param {HTMLElement} t The target of the event.
12725          */
12726
12727         //  Keyboard events
12728         /**
12729          * @event keypress
12730          * Fires when a keypress is detected within the element.
12731          * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12732          * @param {HTMLElement} t The target of the event.
12733          */
12734         /**
12735          * @event keydown
12736          * Fires when a keydown is detected within the element.
12737          * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12738          * @param {HTMLElement} t The target of the event.
12739          */
12740         /**
12741          * @event keyup
12742          * Fires when a keyup is detected within the element.
12743          * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12744          * @param {HTMLElement} t The target of the event.
12745          */
12746
12747
12748         //  HTML frame/object events
12749         /**
12750          * @event load
12751          * Fires when the user agent finishes loading all content within the element. Only supported by window, frames,
12752          * objects and images.
12753          * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12754          * @param {HTMLElement} t The target of the event.
12755          */
12756         /**
12757          * @event unload
12758          * Fires when the user agent removes all content from a window or frame. For elements, it fires when the target
12759          * element or any of its content has been removed.
12760          * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12761          * @param {HTMLElement} t The target of the event.
12762          */
12763         /**
12764          * @event abort
12765          * Fires when an object/image is stopped from loading before completely loaded.
12766          * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12767          * @param {HTMLElement} t The target of the event.
12768          */
12769         /**
12770          * @event error
12771          * Fires when an object/image/frame cannot be loaded properly.
12772          * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12773          * @param {HTMLElement} t The target of the event.
12774          */
12775         /**
12776          * @event resize
12777          * Fires when a document view is resized.
12778          * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12779          * @param {HTMLElement} t The target of the event.
12780          */
12781         /**
12782          * @event scroll
12783          * Fires when a document view is scrolled.
12784          * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12785          * @param {HTMLElement} t The target of the event.
12786          */
12787
12788         //  Form events
12789         /**
12790          * @event select
12791          * Fires when a user selects some text in a text field, including input and textarea.
12792          * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12793          * @param {HTMLElement} t The target of the event.
12794          */
12795         /**
12796          * @event change
12797          * Fires when a control loses the input focus and its value has been modified since gaining focus.
12798          * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12799          * @param {HTMLElement} t The target of the event.
12800          */
12801         /**
12802          * @event submit
12803          * Fires when a form is submitted.
12804          * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12805          * @param {HTMLElement} t The target of the event.
12806          */
12807         /**
12808          * @event reset
12809          * Fires when a form is reset.
12810          * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12811          * @param {HTMLElement} t The target of the event.
12812          */
12813         /**
12814          * @event focus
12815          * Fires when an element receives focus either via the pointing device or by tab navigation.
12816          * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12817          * @param {HTMLElement} t The target of the event.
12818          */
12819         /**
12820          * @event blur
12821          * Fires when an element loses focus either via the pointing device or by tabbing navigation.
12822          * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12823          * @param {HTMLElement} t The target of the event.
12824          */
12825
12826         //  User Interface events
12827         /**
12828          * @event DOMFocusIn
12829          * Where supported. Similar to HTML focus event, but can be applied to any focusable element.
12830          * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12831          * @param {HTMLElement} t The target of the event.
12832          */
12833         /**
12834          * @event DOMFocusOut
12835          * Where supported. Similar to HTML blur event, but can be applied to any focusable element.
12836          * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12837          * @param {HTMLElement} t The target of the event.
12838          */
12839         /**
12840          * @event DOMActivate
12841          * Where supported. Fires when an element is activated, for instance, through a mouse click or a keypress.
12842          * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12843          * @param {HTMLElement} t The target of the event.
12844          */
12845
12846         //  DOM Mutation events
12847         /**
12848          * @event DOMSubtreeModified
12849          * Where supported. Fires when the subtree is modified.
12850          * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12851          * @param {HTMLElement} t The target of the event.
12852          */
12853         /**
12854          * @event DOMNodeInserted
12855          * Where supported. Fires when a node has been added as a child of another node.
12856          * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12857          * @param {HTMLElement} t The target of the event.
12858          */
12859         /**
12860          * @event DOMNodeRemoved
12861          * Where supported. Fires when a descendant node of the element is removed.
12862          * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12863          * @param {HTMLElement} t The target of the event.
12864          */
12865         /**
12866          * @event DOMNodeRemovedFromDocument
12867          * Where supported. Fires when a node is being removed from a document.
12868          * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12869          * @param {HTMLElement} t The target of the event.
12870          */
12871         /**
12872          * @event DOMNodeInsertedIntoDocument
12873          * Where supported. Fires when a node is being inserted into a document.
12874          * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12875          * @param {HTMLElement} t The target of the event.
12876          */
12877         /**
12878          * @event DOMAttrModified
12879          * Where supported. Fires when an attribute has been modified.
12880          * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12881          * @param {HTMLElement} t The target of the event.
12882          */
12883         /**
12884          * @event DOMCharacterDataModified
12885          * Where supported. Fires when the character data has been modified.
12886          * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12887          * @param {HTMLElement} t The target of the event.
12888          */
12889
12890         /**
12891          * @property {String} defaultUnit
12892          * The default unit to append to CSS values where a unit isn't provided.
12893          */
12894         defaultUnit: "px",
12895
12896         /**
12897          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
12898          * @param {String} selector The simple selector to test
12899          * @return {Boolean} True if this element matches the selector, else false
12900          */
12901         is: function(simpleSelector) {
12902             return Ext.DomQuery.is(this.dom, simpleSelector);
12903         },
12904
12905         /**
12906          * Tries to focus the element. Any exceptions are caught and ignored.
12907          * @param {Number} defer (optional) Milliseconds to defer the focus
12908          * @return {Ext.Element} this
12909          */
12910         focus: function(defer,
12911                         /* private */
12912                         dom) {
12913             var me = this;
12914             dom = dom || me.dom;
12915             try {
12916                 if (Number(defer)) {
12917                     Ext.defer(me.focus, defer, null, [null, dom]);
12918                 } else {
12919                     dom.focus();
12920                 }
12921             } catch(e) {}
12922             return me;
12923         },
12924
12925         /**
12926          * Tries to blur the element. Any exceptions are caught and ignored.
12927          * @return {Ext.Element} this
12928          */
12929         blur: function() {
12930             try {
12931                 this.dom.blur();
12932             } catch(e) {}
12933             return this;
12934         },
12935
12936         /**
12937          * Returns the value of the "value" attribute
12938          * @param {Boolean} asNumber true to parse the value as a number
12939          * @return {String/Number}
12940          */
12941         getValue: function(asNumber) {
12942             var val = this.dom.value;
12943             return asNumber ? parseInt(val, 10) : val;
12944         },
12945
12946         /**
12947          * Appends an event handler to this element.
12948          *
12949          * @param {String} eventName The name of event to handle.
12950          *
12951          * @param {Function} fn The handler function the event invokes. This function is passed the following parameters:
12952          *
12953          * - **evt** : EventObject
12954          *
12955          *   The {@link Ext.EventObject EventObject} describing the event.
12956          *
12957          * - **el** : HtmlElement
12958          *
12959          *   The DOM element which was the target of the event. Note that this may be filtered by using the delegate option.
12960          *
12961          * - **o** : Object
12962          *
12963          *   The options object from the addListener call.
12964          *
12965          * @param {Object} scope (optional) The scope (**this** reference) in which the handler function is executed. **If
12966          * omitted, defaults to this Element.**
12967          *
12968          * @param {Object} options (optional) An object containing handler configuration properties. This may contain any of
12969          * the following properties:
12970          *
12971          * - **scope** Object :
12972          *
12973          *   The scope (**this** reference) in which the handler function is executed. **If omitted, defaults to this
12974          *   Element.**
12975          *
12976          * - **delegate** String:
12977          *
12978          *   A simple selector to filter the target or look for a descendant of the target. See below for additional details.
12979          *
12980          * - **stopEvent** Boolean:
12981          *
12982          *   True to stop the event. That is stop propagation, and prevent the default action.
12983          *
12984          * - **preventDefault** Boolean:
12985          *
12986          *   True to prevent the default action
12987          *
12988          * - **stopPropagation** Boolean:
12989          *
12990          *   True to prevent event propagation
12991          *
12992          * - **normalized** Boolean:
12993          *
12994          *   False to pass a browser event to the handler function instead of an Ext.EventObject
12995          *
12996          * - **target** Ext.Element:
12997          *
12998          *   Only call the handler if the event was fired on the target Element, _not_ if the event was bubbled up from a
12999          *   child node.
13000          *
13001          * - **delay** Number:
13002          *
13003          *   The number of milliseconds to delay the invocation of the handler after the event fires.
13004          *
13005          * - **single** Boolean:
13006          *
13007          *   True to add a handler to handle just the next firing of the event, and then remove itself.
13008          *
13009          * - **buffer** Number:
13010          *
13011          *   Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed by the specified number of
13012          *   milliseconds. If the event fires again within that time, the original handler is _not_ invoked, but the new
13013          *   handler is scheduled in its place.
13014          *
13015          * **Combining Options**
13016          *
13017          * In the following examples, the shorthand form {@link #on} is used rather than the more verbose addListener. The
13018          * two are equivalent. Using the options argument, it is possible to combine different types of listeners:
13019          *
13020          * A delayed, one-time listener that auto stops the event and adds a custom argument (forumId) to the options
13021          * object. The options object is available as the third parameter in the handler function.
13022          *
13023          * Code:
13024          *
13025          *     el.on('click', this.onClick, this, {
13026          *         single: true,
13027          *         delay: 100,
13028          *         stopEvent : true,
13029          *         forumId: 4
13030          *     });
13031          *
13032          * **Attaching multiple handlers in 1 call**
13033          *
13034          * The method also allows for a single argument to be passed which is a config object containing properties which
13035          * specify multiple handlers.
13036          *
13037          * Code:
13038          *
13039          *     el.on({
13040          *         'click' : {
13041          *             fn: this.onClick,
13042          *             scope: this,
13043          *             delay: 100
13044          *         },
13045          *         'mouseover' : {
13046          *             fn: this.onMouseOver,
13047          *             scope: this
13048          *         },
13049          *         'mouseout' : {
13050          *             fn: this.onMouseOut,
13051          *             scope: this
13052          *         }
13053          *     });
13054          *
13055          * Or a shorthand syntax:
13056          *
13057          * Code:
13058          *
13059          *     el.on({
13060          *         'click' : this.onClick,
13061          *         'mouseover' : this.onMouseOver,
13062          *         'mouseout' : this.onMouseOut,
13063          *         scope: this
13064          *     });
13065          *
13066          * **delegate**
13067          *
13068          * This is a configuration option that you can pass along when registering a handler for an event to assist with
13069          * event delegation. Event delegation is a technique that is used to reduce memory consumption and prevent exposure
13070          * to memory-leaks. By registering an event for a container element as opposed to each element within a container.
13071          * By setting this configuration option to a simple selector, the target element will be filtered to look for a
13072          * descendant of the target. For example:
13073          *
13074          *     // using this markup:
13075          *     <div id='elId'>
13076          *         <p id='p1'>paragraph one</p>
13077          *         <p id='p2' class='clickable'>paragraph two</p>
13078          *         <p id='p3'>paragraph three</p>
13079          *     </div>
13080          *
13081          *     // utilize event delegation to registering just one handler on the container element:
13082          *     el = Ext.get('elId');
13083          *     el.on(
13084          *         'click',
13085          *         function(e,t) {
13086          *             // handle click
13087          *             console.info(t.id); // 'p2'
13088          *         },
13089          *         this,
13090          *         {
13091          *             // filter the target element to be a descendant with the class 'clickable'
13092          *             delegate: '.clickable'
13093          *         }
13094          *     );
13095          *
13096          * @return {Ext.Element} this
13097          */
13098         addListener: function(eventName, fn, scope, options) {
13099             Ext.EventManager.on(this.dom, eventName, fn, scope || this, options);
13100             return this;
13101         },
13102
13103         /**
13104          * Removes an event handler from this element.
13105          *
13106          * **Note**: if a *scope* was explicitly specified when {@link #addListener adding} the listener,
13107          * the same scope must be specified here.
13108          *
13109          * Example:
13110          *
13111          *     el.removeListener('click', this.handlerFn);
13112          *     // or
13113          *     el.un('click', this.handlerFn);
13114          *
13115          * @param {String} eventName The name of the event from which to remove the handler.
13116          * @param {Function} fn The handler function to remove. **This must be a reference to the function passed into the
13117          * {@link #addListener} call.**
13118          * @param {Object} scope If a scope (**this** reference) was specified when the listener was added, then this must
13119          * refer to the same object.
13120          * @return {Ext.Element} this
13121          */
13122         removeListener: function(eventName, fn, scope) {
13123             Ext.EventManager.un(this.dom, eventName, fn, scope || this);
13124             return this;
13125         },
13126
13127         /**
13128          * Removes all previous added listeners from this element
13129          * @return {Ext.Element} this
13130          */
13131         removeAllListeners: function() {
13132             Ext.EventManager.removeAll(this.dom);
13133             return this;
13134         },
13135
13136         /**
13137          * Recursively removes all previous added listeners from this element and its children
13138          * @return {Ext.Element} this
13139          */
13140         purgeAllListeners: function() {
13141             Ext.EventManager.purgeElement(this);
13142             return this;
13143         },
13144
13145         /**
13146          * Test if size has a unit, otherwise appends the passed unit string, or the default for this Element.
13147          * @param size {Mixed} The size to set
13148          * @param units {String} The units to append to a numeric size value
13149          * @private
13150          */
13151         addUnits: function(size, units) {
13152
13153             // Most common case first: Size is set to a number
13154             if (Ext.isNumber(size)) {
13155                 return size + (units || this.defaultUnit || 'px');
13156             }
13157
13158             // Size set to a value which means "auto"
13159             if (size === "" || size == "auto" || size == null) {
13160                 return size || '';
13161             }
13162
13163             // Otherwise, warn if it's not a valid CSS measurement
13164             if (!unitPattern.test(size)) {
13165                 return size || '';
13166             }
13167             return size;
13168         },
13169
13170         /**
13171          * Tests various css rules/browsers to determine if this element uses a border box
13172          * @return {Boolean}
13173          */
13174         isBorderBox: function() {
13175             return Ext.isBorderBox || noBoxAdjust[(this.dom.tagName || "").toLowerCase()];
13176         },
13177
13178         /**
13179          * Removes this element's dom reference. Note that event and cache removal is handled at {@link Ext#removeNode
13180          * Ext.removeNode}
13181          */
13182         remove: function() {
13183             var me = this,
13184             dom = me.dom;
13185
13186             if (dom) {
13187                 delete me.dom;
13188                 Ext.removeNode(dom);
13189             }
13190         },
13191
13192         /**
13193          * Sets up event handlers to call the passed functions when the mouse is moved into and out of the Element.
13194          * @param {Function} overFn The function to call when the mouse enters the Element.
13195          * @param {Function} outFn The function to call when the mouse leaves the Element.
13196          * @param {Object} scope (optional) The scope (`this` reference) in which the functions are executed. Defaults
13197          * to the Element's DOM element.
13198          * @param {Object} options (optional) Options for the listener. See {@link Ext.util.Observable#addListener the
13199          * options parameter}.
13200          * @return {Ext.Element} this
13201          */
13202         hover: function(overFn, outFn, scope, options) {
13203             var me = this;
13204             me.on('mouseenter', overFn, scope || me.dom, options);
13205             me.on('mouseleave', outFn, scope || me.dom, options);
13206             return me;
13207         },
13208
13209         /**
13210          * Returns true if this element is an ancestor of the passed element
13211          * @param {HTMLElement/String} el The element to check
13212          * @return {Boolean} True if this element is an ancestor of el, else false
13213          */
13214         contains: function(el) {
13215             return ! el ? false: Ext.Element.isAncestor(this.dom, el.dom ? el.dom: el);
13216         },
13217
13218         /**
13219          * Returns the value of a namespaced attribute from the element's underlying DOM node.
13220          * @param {String} namespace The namespace in which to look for the attribute
13221          * @param {String} name The attribute name
13222          * @return {String} The attribute value
13223          */
13224         getAttributeNS: function(ns, name) {
13225             return this.getAttribute(name, ns);
13226         },
13227
13228         /**
13229          * Returns the value of an attribute from the element's underlying DOM node.
13230          * @param {String} name The attribute name
13231          * @param {String} namespace (optional) The namespace in which to look for the attribute
13232          * @return {String} The attribute value
13233          * @method
13234          */
13235         getAttribute: (Ext.isIE && !(Ext.isIE9 && document.documentMode === 9)) ?
13236         function(name, ns) {
13237             var d = this.dom,
13238             type;
13239             if(ns) {
13240                 type = typeof d[ns + ":" + name];
13241                 if (type != 'undefined' && type != 'unknown') {
13242                     return d[ns + ":" + name] || null;
13243                 }
13244                 return null;
13245             }
13246             if (name === "for") {
13247                 name = "htmlFor";
13248             }
13249             return d[name] || null;
13250         }: function(name, ns) {
13251             var d = this.dom;
13252             if (ns) {
13253                return d.getAttributeNS(ns, name) || d.getAttribute(ns + ":" + name);
13254             }
13255             return  d.getAttribute(name) || d[name] || null;
13256         },
13257
13258         /**
13259          * Update the innerHTML of this element
13260          * @param {String} html The new HTML
13261          * @return {Ext.Element} this
13262          */
13263         update: function(html) {
13264             if (this.dom) {
13265                 this.dom.innerHTML = html;
13266             }
13267             return this;
13268         }
13269     };
13270
13271     var ep = El.prototype;
13272
13273     El.addMethods = function(o) {
13274         Ext.apply(ep, o);
13275     };
13276
13277     /**
13278      * @method
13279      * @alias Ext.Element#addListener
13280      * Shorthand for {@link #addListener}.
13281      */
13282     ep.on = ep.addListener;
13283
13284     /**
13285      * @method
13286      * @alias Ext.Element#removeListener
13287      * Shorthand for {@link #removeListener}.
13288      */
13289     ep.un = ep.removeListener;
13290
13291     /**
13292      * @method
13293      * @alias Ext.Element#removeAllListeners
13294      * Alias for {@link #removeAllListeners}.
13295      */
13296     ep.clearListeners = ep.removeAllListeners;
13297
13298     /**
13299      * @method destroy
13300      * @member Ext.Element
13301      * Removes this element's dom reference. Note that event and cache removal is handled at {@link Ext#removeNode
13302      * Ext.removeNode}. Alias to {@link #remove}.
13303      */
13304     ep.destroy = ep.remove;
13305
13306     /**
13307      * @property {Boolean} autoBoxAdjust
13308      * true to automatically adjust width and height settings for box-model issues (default to true)
13309      */
13310     ep.autoBoxAdjust = true;
13311
13312     // private
13313     var unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
13314     docEl;
13315
13316     /**
13317      * Retrieves Ext.Element objects. {@link Ext#get} is an alias for {@link Ext.Element#get}.
13318      *
13319      * **This method does not retrieve {@link Ext.Component Component}s.** This method retrieves Ext.Element
13320      * objects which encapsulate DOM elements. To retrieve a Component by its ID, use {@link Ext.ComponentManager#get}.
13321      *
13322      * Uses simple caching to consistently return the same object. Automatically fixes if an object was recreated with
13323      * the same id via AJAX or DOM.
13324      *
13325      * @param {String/HTMLElement/Ext.Element} el The id of the node, a DOM Node or an existing Element.
13326      * @return {Ext.Element} The Element object (or null if no matching element was found)
13327      * @static
13328      */
13329     El.get = function(el) {
13330         var ex,
13331         elm,
13332         id;
13333         if (!el) {
13334             return null;
13335         }
13336         if (typeof el == "string") {
13337             // element id
13338             if (! (elm = DOC.getElementById(el))) {
13339                 return null;
13340             }
13341             if (EC[el] && EC[el].el) {
13342                 ex = EC[el].el;
13343                 ex.dom = elm;
13344             } else {
13345                 ex = El.addToCache(new El(elm));
13346             }
13347             return ex;
13348         } else if (el.tagName) {
13349             // dom element
13350             if (! (id = el.id)) {
13351                 id = Ext.id(el);
13352             }
13353             if (EC[id] && EC[id].el) {
13354                 ex = EC[id].el;
13355                 ex.dom = el;
13356             } else {
13357                 ex = El.addToCache(new El(el));
13358             }
13359             return ex;
13360         } else if (el instanceof El) {
13361             if (el != docEl) {
13362                 // refresh dom element in case no longer valid,
13363                 // catch case where it hasn't been appended
13364                 // If an el instance is passed, don't pass to getElementById without some kind of id
13365                 if (Ext.isIE && (el.id == undefined || el.id == '')) {
13366                     el.dom = el.dom;
13367                 } else {
13368                     el.dom = DOC.getElementById(el.id) || el.dom;
13369                 }
13370             }
13371             return el;
13372         } else if (el.isComposite) {
13373             return el;
13374         } else if (Ext.isArray(el)) {
13375             return El.select(el);
13376         } else if (el == DOC) {
13377             // create a bogus element object representing the document object
13378             if (!docEl) {
13379                 var f = function() {};
13380                 f.prototype = El.prototype;
13381                 docEl = new f();
13382                 docEl.dom = DOC;
13383             }
13384             return docEl;
13385         }
13386         return null;
13387     };
13388
13389     /**
13390      * Retrieves Ext.Element objects like {@link Ext#get} but is optimized for sub-elements.
13391      * This is helpful for performance, because in IE (prior to IE 9), `getElementById` uses
13392      * an non-optimized search. In those browsers, starting the search for an element with a
13393      * matching ID at a parent of that element will greatly speed up the process.
13394      *
13395      * Unlike {@link Ext#get}, this method only accepts ID's. If the ID is not a child of
13396      * this element, it will still be found if it exists in the document, but will be slower
13397      * than calling {@link Ext#get} directly.
13398      *
13399      * @param {String} id The id of the element to get.
13400      * @return {Ext.Element} The Element object (or null if no matching element was found)
13401      * @member Ext.Element
13402      * @method getById
13403      * @markdown
13404      */
13405     ep.getById = (!Ext.isIE6 && !Ext.isIE7 && !Ext.isIE8) ? El.get :
13406         function (id) {
13407             var dom = this.dom,
13408                 cached, el, ret;
13409
13410             if (dom) {
13411                 el = dom.all[id];
13412                 if (el) {
13413                     // calling El.get here is a real hit (2x slower) because it has to
13414                     // redetermine that we are giving it a dom el.
13415                     cached = EC[id];
13416                     if (cached && cached.el) {
13417                         ret = cached.el;
13418                         ret.dom = el;
13419                     } else {
13420                         ret = El.addToCache(new El(el));
13421                     }
13422                     return ret;
13423                 }
13424             }
13425
13426             return El.get(id);
13427         };
13428
13429     El.addToCache = function(el, id) {
13430         if (el) {
13431             id = id || el.id;
13432             EC[id] = {
13433                 el: el,
13434                 data: {},
13435                 events: {}
13436             };
13437         }
13438         return el;
13439     };
13440
13441     // private method for getting and setting element data
13442     El.data = function(el, key, value) {
13443         el = El.get(el);
13444         if (!el) {
13445             return null;
13446         }
13447         var c = EC[el.id].data;
13448         if (arguments.length == 2) {
13449             return c[key];
13450         } else {
13451             return (c[key] = value);
13452         }
13453     };
13454
13455     // private
13456     // Garbage collection - uncache elements/purge listeners on orphaned elements
13457     // so we don't hold a reference and cause the browser to retain them
13458     function garbageCollect() {
13459         if (!Ext.enableGarbageCollector) {
13460             clearInterval(El.collectorThreadId);
13461         } else {
13462             var eid,
13463             el,
13464             d,
13465             o;
13466
13467             for (eid in EC) {
13468                 if (!EC.hasOwnProperty(eid)) {
13469                     continue;
13470                 }
13471                 o = EC[eid];
13472                 if (o.skipGarbageCollection) {
13473                     continue;
13474                 }
13475                 el = o.el;
13476                 d = el.dom;
13477                 // -------------------------------------------------------
13478                 // Determining what is garbage:
13479                 // -------------------------------------------------------
13480                 // !d
13481                 // dom node is null, definitely garbage
13482                 // -------------------------------------------------------
13483                 // !d.parentNode
13484                 // no parentNode == direct orphan, definitely garbage
13485                 // -------------------------------------------------------
13486                 // !d.offsetParent && !document.getElementById(eid)
13487                 // display none elements have no offsetParent so we will
13488                 // also try to look it up by it's id. However, check
13489                 // offsetParent first so we don't do unneeded lookups.
13490                 // This enables collection of elements that are not orphans
13491                 // directly, but somewhere up the line they have an orphan
13492                 // parent.
13493                 // -------------------------------------------------------
13494                 if (!d || !d.parentNode || (!d.offsetParent && !DOC.getElementById(eid))) {
13495                     if (d && Ext.enableListenerCollection) {
13496                         Ext.EventManager.removeAll(d);
13497                     }
13498                     delete EC[eid];
13499                 }
13500             }
13501             // Cleanup IE Object leaks
13502             if (Ext.isIE) {
13503                 var t = {};
13504                 for (eid in EC) {
13505                     if (!EC.hasOwnProperty(eid)) {
13506                         continue;
13507                     }
13508                     t[eid] = EC[eid];
13509                 }
13510                 EC = Ext.cache = t;
13511             }
13512         }
13513     }
13514     El.collectorThreadId = setInterval(garbageCollect, 30000);
13515
13516     var flyFn = function() {};
13517     flyFn.prototype = El.prototype;
13518
13519     // dom is optional
13520     El.Flyweight = function(dom) {
13521         this.dom = dom;
13522     };
13523
13524     El.Flyweight.prototype = new flyFn();
13525     El.Flyweight.prototype.isFlyweight = true;
13526     El._flyweights = {};
13527
13528     /**
13529      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference
13530      * to this element - the dom node can be overwritten by other code. {@link Ext#fly} is alias for
13531      * {@link Ext.Element#fly}.
13532      *
13533      * Use this to make one-time references to DOM elements which are not going to be accessed again either by
13534      * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link
13535      * Ext#get Ext.get} will be more appropriate to take advantage of the caching provided by the Ext.Element
13536      * class.
13537      *
13538      * @param {String/HTMLElement} el The dom node or id
13539      * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts (e.g.
13540      * internally Ext uses "_global")
13541      * @return {Ext.Element} The shared Element object (or null if no matching element was found)
13542      * @static
13543      */
13544     El.fly = function(el, named) {
13545         var ret = null;
13546         named = named || '_global';
13547         el = Ext.getDom(el);
13548         if (el) {
13549             (El._flyweights[named] = El._flyweights[named] || new El.Flyweight()).dom = el;
13550             ret = El._flyweights[named];
13551         }
13552         return ret;
13553     };
13554
13555     /**
13556      * @member Ext
13557      * @method get
13558      * @alias Ext.Element#get
13559      */
13560     Ext.get = El.get;
13561
13562     /**
13563      * @member Ext
13564      * @method fly
13565      * @alias Ext.Element#fly
13566      */
13567     Ext.fly = El.fly;
13568
13569     // speedy lookup for elements never to box adjust
13570     var noBoxAdjust = Ext.isStrict ? {
13571         select: 1
13572     }: {
13573         input: 1,
13574         select: 1,
13575         textarea: 1
13576     };
13577     if (Ext.isIE || Ext.isGecko) {
13578         noBoxAdjust['button'] = 1;
13579     }
13580 })();
13581
13582 /**
13583  * @class Ext.Element
13584  */
13585 Ext.Element.addMethods({
13586     /**
13587      * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
13588      * @param {String} selector The simple selector to test
13589      * @param {Number/String/HTMLElement/Ext.Element} maxDepth (optional)
13590      * The max depth to search as a number or element (defaults to 50 || document.body)
13591      * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
13592      * @return {HTMLElement} The matching DOM node (or null if no match was found)
13593      */
13594     findParent : function(simpleSelector, maxDepth, returnEl) {
13595         var p = this.dom,
13596             b = document.body,
13597             depth = 0,
13598             stopEl;
13599
13600         maxDepth = maxDepth || 50;
13601         if (isNaN(maxDepth)) {
13602             stopEl = Ext.getDom(maxDepth);
13603             maxDepth = Number.MAX_VALUE;
13604         }
13605         while (p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl) {
13606             if (Ext.DomQuery.is(p, simpleSelector)) {
13607                 return returnEl ? Ext.get(p) : p;
13608             }
13609             depth++;
13610             p = p.parentNode;
13611         }
13612         return null;
13613     },
13614
13615     /**
13616      * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
13617      * @param {String} selector The simple selector to test
13618      * @param {Number/String/HTMLElement/Ext.Element} maxDepth (optional)
13619      * The max depth to search as a number or element (defaults to 10 || document.body)
13620      * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
13621      * @return {HTMLElement} The matching DOM node (or null if no match was found)
13622      */
13623     findParentNode : function(simpleSelector, maxDepth, returnEl) {
13624         var p = Ext.fly(this.dom.parentNode, '_internal');
13625         return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
13626     },
13627
13628     /**
13629      * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
13630      * This is a shortcut for findParentNode() that always returns an Ext.Element.
13631      * @param {String} selector The simple selector to test
13632      * @param {Number/String/HTMLElement/Ext.Element} maxDepth (optional)
13633      * The max depth to search as a number or element (defaults to 10 || document.body)
13634      * @return {Ext.Element} The matching DOM node (or null if no match was found)
13635      */
13636     up : function(simpleSelector, maxDepth) {
13637         return this.findParentNode(simpleSelector, maxDepth, true);
13638     },
13639
13640     /**
13641      * Creates a {@link Ext.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
13642      * @param {String} selector The CSS selector
13643      * @return {Ext.CompositeElement/Ext.CompositeElement} The composite element
13644      */
13645     select : function(selector) {
13646         return Ext.Element.select(selector, false,  this.dom);
13647     },
13648
13649     /**
13650      * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
13651      * @param {String} selector The CSS selector
13652      * @return {HTMLElement[]} An array of the matched nodes
13653      */
13654     query : function(selector) {
13655         return Ext.DomQuery.select(selector, this.dom);
13656     },
13657
13658     /**
13659      * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
13660      * @param {String} selector The CSS selector
13661      * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.Element (defaults to false)
13662      * @return {HTMLElement/Ext.Element} The child Ext.Element (or DOM node if returnDom = true)
13663      */
13664     down : function(selector, returnDom) {
13665         var n = Ext.DomQuery.selectNode(selector, this.dom);
13666         return returnDom ? n : Ext.get(n);
13667     },
13668
13669     /**
13670      * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
13671      * @param {String} selector The CSS selector
13672      * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.Element (defaults to false)
13673      * @return {HTMLElement/Ext.Element} The child Ext.Element (or DOM node if returnDom = true)
13674      */
13675     child : function(selector, returnDom) {
13676         var node,
13677             me = this,
13678             id;
13679         id = Ext.get(me).id;
13680         // Escape . or :
13681         id = id.replace(/[\.:]/g, "\\$0");
13682         node = Ext.DomQuery.selectNode('#' + id + " > " + selector, me.dom);
13683         return returnDom ? node : Ext.get(node);
13684     },
13685
13686      /**
13687      * Gets the parent node for this element, optionally chaining up trying to match a selector
13688      * @param {String} selector (optional) Find a parent node that matches the passed simple selector
13689      * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
13690      * @return {Ext.Element/HTMLElement} The parent node or null
13691      */
13692     parent : function(selector, returnDom) {
13693         return this.matchNode('parentNode', 'parentNode', selector, returnDom);
13694     },
13695
13696      /**
13697      * Gets the next sibling, skipping text nodes
13698      * @param {String} selector (optional) Find the next sibling that matches the passed simple selector
13699      * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
13700      * @return {Ext.Element/HTMLElement} The next sibling or null
13701      */
13702     next : function(selector, returnDom) {
13703         return this.matchNode('nextSibling', 'nextSibling', selector, returnDom);
13704     },
13705
13706     /**
13707      * Gets the previous sibling, skipping text nodes
13708      * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector
13709      * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
13710      * @return {Ext.Element/HTMLElement} The previous sibling or null
13711      */
13712     prev : function(selector, returnDom) {
13713         return this.matchNode('previousSibling', 'previousSibling', selector, returnDom);
13714     },
13715
13716
13717     /**
13718      * Gets the first child, skipping text nodes
13719      * @param {String} selector (optional) Find the next sibling that matches the passed simple selector
13720      * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
13721      * @return {Ext.Element/HTMLElement} The first child or null
13722      */
13723     first : function(selector, returnDom) {
13724         return this.matchNode('nextSibling', 'firstChild', selector, returnDom);
13725     },
13726
13727     /**
13728      * Gets the last child, skipping text nodes
13729      * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector
13730      * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
13731      * @return {Ext.Element/HTMLElement} The last child or null
13732      */
13733     last : function(selector, returnDom) {
13734         return this.matchNode('previousSibling', 'lastChild', selector, returnDom);
13735     },
13736
13737     matchNode : function(dir, start, selector, returnDom) {
13738         if (!this.dom) {
13739             return null;
13740         }
13741
13742         var n = this.dom[start];
13743         while (n) {
13744             if (n.nodeType == 1 && (!selector || Ext.DomQuery.is(n, selector))) {
13745                 return !returnDom ? Ext.get(n) : n;
13746             }
13747             n = n[dir];
13748         }
13749         return null;
13750     }
13751 });
13752
13753 /**
13754  * @class Ext.Element
13755  */
13756 Ext.Element.addMethods({
13757     /**
13758      * Appends the passed element(s) to this element
13759      * @param {String/HTMLElement/Ext.Element} el
13760      * The id of the node, a DOM Node or an existing Element.
13761      * @return {Ext.Element} this
13762      */
13763     appendChild : function(el) {
13764         return Ext.get(el).appendTo(this);
13765     },
13766
13767     /**
13768      * Appends this element to the passed element
13769      * @param {String/HTMLElement/Ext.Element} el The new parent element.
13770      * The id of the node, a DOM Node or an existing Element.
13771      * @return {Ext.Element} this
13772      */
13773     appendTo : function(el) {
13774         Ext.getDom(el).appendChild(this.dom);
13775         return this;
13776     },
13777
13778     /**
13779      * Inserts this element before the passed element in the DOM
13780      * @param {String/HTMLElement/Ext.Element} el The element before which this element will be inserted.
13781      * The id of the node, a DOM Node or an existing Element.
13782      * @return {Ext.Element} this
13783      */
13784     insertBefore : function(el) {
13785         el = Ext.getDom(el);
13786         el.parentNode.insertBefore(this.dom, el);
13787         return this;
13788     },
13789
13790     /**
13791      * Inserts this element after the passed element in the DOM
13792      * @param {String/HTMLElement/Ext.Element} el The element to insert after.
13793      * The id of the node, a DOM Node or an existing Element.
13794      * @return {Ext.Element} this
13795      */
13796     insertAfter : function(el) {
13797         el = Ext.getDom(el);
13798         el.parentNode.insertBefore(this.dom, el.nextSibling);
13799         return this;
13800     },
13801
13802     /**
13803      * Inserts (or creates) an element (or DomHelper config) as the first child of this element
13804      * @param {String/HTMLElement/Ext.Element/Object} el The id or element to insert or a DomHelper config
13805      * to create and insert
13806      * @return {Ext.Element} The new child
13807      */
13808     insertFirst : function(el, returnDom) {
13809         el = el || {};
13810         if (el.nodeType || el.dom || typeof el == 'string') { // element
13811             el = Ext.getDom(el);
13812             this.dom.insertBefore(el, this.dom.firstChild);
13813             return !returnDom ? Ext.get(el) : el;
13814         }
13815         else { // dh config
13816             return this.createChild(el, this.dom.firstChild, returnDom);
13817         }
13818     },
13819
13820     /**
13821      * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
13822      * @param {String/HTMLElement/Ext.Element/Object/Array} el The id, element to insert or a DomHelper config
13823      * to create and insert *or* an array of any of those.
13824      * @param {String} where (optional) 'before' or 'after' defaults to before
13825      * @param {Boolean} returnDom (optional) True to return the .;ll;l,raw DOM element instead of Ext.Element
13826      * @return {Ext.Element} The inserted Element. If an array is passed, the last inserted element is returned.
13827      */
13828     insertSibling: function(el, where, returnDom){
13829         var me = this, rt,
13830         isAfter = (where || 'before').toLowerCase() == 'after',
13831         insertEl;
13832
13833         if(Ext.isArray(el)){
13834             insertEl = me;
13835             Ext.each(el, function(e) {
13836                 rt = Ext.fly(insertEl, '_internal').insertSibling(e, where, returnDom);
13837                 if(isAfter){
13838                     insertEl = rt;
13839                 }
13840             });
13841             return rt;
13842         }
13843
13844         el = el || {};
13845
13846         if(el.nodeType || el.dom){
13847             rt = me.dom.parentNode.insertBefore(Ext.getDom(el), isAfter ? me.dom.nextSibling : me.dom);
13848             if (!returnDom) {
13849                 rt = Ext.get(rt);
13850             }
13851         }else{
13852             if (isAfter && !me.dom.nextSibling) {
13853                 rt = Ext.DomHelper.append(me.dom.parentNode, el, !returnDom);
13854             } else {
13855                 rt = Ext.DomHelper[isAfter ? 'insertAfter' : 'insertBefore'](me.dom, el, !returnDom);
13856             }
13857         }
13858         return rt;
13859     },
13860
13861     /**
13862      * Replaces the passed element with this element
13863      * @param {String/HTMLElement/Ext.Element} el The element to replace.
13864      * The id of the node, a DOM Node or an existing Element.
13865      * @return {Ext.Element} this
13866      */
13867     replace : function(el) {
13868         el = Ext.get(el);
13869         this.insertBefore(el);
13870         el.remove();
13871         return this;
13872     },
13873     
13874     /**
13875      * Replaces this element with the passed element
13876      * @param {String/HTMLElement/Ext.Element/Object} el The new element (id of the node, a DOM Node
13877      * or an existing Element) or a DomHelper config of an element to create
13878      * @return {Ext.Element} this
13879      */
13880     replaceWith: function(el){
13881         var me = this;
13882             
13883         if(el.nodeType || el.dom || typeof el == 'string'){
13884             el = Ext.get(el);
13885             me.dom.parentNode.insertBefore(el, me.dom);
13886         }else{
13887             el = Ext.DomHelper.insertBefore(me.dom, el);
13888         }
13889         
13890         delete Ext.cache[me.id];
13891         Ext.removeNode(me.dom);      
13892         me.id = Ext.id(me.dom = el);
13893         Ext.Element.addToCache(me.isFlyweight ? new Ext.Element(me.dom) : me);     
13894         return me;
13895     },
13896     
13897     /**
13898      * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
13899      * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
13900      * automatically generated with the specified attributes.
13901      * @param {HTMLElement} insertBefore (optional) a child element of this element
13902      * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
13903      * @return {Ext.Element} The new child element
13904      */
13905     createChild : function(config, insertBefore, returnDom) {
13906         config = config || {tag:'div'};
13907         if (insertBefore) {
13908             return Ext.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
13909         }
13910         else {
13911             return Ext.DomHelper[!this.dom.firstChild ? 'insertFirst' : 'append'](this.dom, config,  returnDom !== true);
13912         }
13913     },
13914
13915     /**
13916      * Creates and wraps this element with another element
13917      * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
13918      * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.Element
13919      * @return {HTMLElement/Ext.Element} The newly created wrapper element
13920      */
13921     wrap : function(config, returnDom) {
13922         var newEl = Ext.DomHelper.insertBefore(this.dom, config || {tag: "div"}, !returnDom),
13923             d = newEl.dom || newEl;
13924
13925         d.appendChild(this.dom);
13926         return newEl;
13927     },
13928
13929     /**
13930      * Inserts an html fragment into this element
13931      * @param {String} where Where to insert the html in relation to this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
13932      * See {@link Ext.DomHelper#insertHtml} for details.
13933      * @param {String} html The HTML fragment
13934      * @param {Boolean} returnEl (optional) True to return an Ext.Element (defaults to false)
13935      * @return {HTMLElement/Ext.Element} The inserted node (or nearest related if more than 1 inserted)
13936      */
13937     insertHtml : function(where, html, returnEl) {
13938         var el = Ext.DomHelper.insertHtml(where, this.dom, html);
13939         return returnEl ? Ext.get(el) : el;
13940     }
13941 });
13942
13943 /**
13944  * @class Ext.Element
13945  */
13946 (function(){
13947     // local style camelizing for speed
13948     var ELEMENT = Ext.Element,
13949         supports = Ext.supports,
13950         view = document.defaultView,
13951         opacityRe = /alpha\(opacity=(.*)\)/i,
13952         trimRe = /^\s+|\s+$/g,
13953         spacesRe = /\s+/,
13954         wordsRe = /\w/g,
13955         adjustDirect2DTableRe = /table-row|table-.*-group/,
13956         INTERNAL = '_internal',
13957         PADDING = 'padding',
13958         MARGIN = 'margin',
13959         BORDER = 'border',
13960         LEFT = '-left',
13961         RIGHT = '-right',
13962         TOP = '-top',
13963         BOTTOM = '-bottom',
13964         WIDTH = '-width',
13965         MATH = Math,
13966         HIDDEN = 'hidden',
13967         ISCLIPPED = 'isClipped',
13968         OVERFLOW = 'overflow',
13969         OVERFLOWX = 'overflow-x',
13970         OVERFLOWY = 'overflow-y',
13971         ORIGINALCLIP = 'originalClip',
13972         // special markup used throughout Ext when box wrapping elements
13973         borders = {l: BORDER + LEFT + WIDTH, r: BORDER + RIGHT + WIDTH, t: BORDER + TOP + WIDTH, b: BORDER + BOTTOM + WIDTH},
13974         paddings = {l: PADDING + LEFT, r: PADDING + RIGHT, t: PADDING + TOP, b: PADDING + BOTTOM},
13975         margins = {l: MARGIN + LEFT, r: MARGIN + RIGHT, t: MARGIN + TOP, b: MARGIN + BOTTOM},
13976         data = ELEMENT.data;
13977
13978     ELEMENT.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
13979
13980     // These property values are read from the parentNode if they cannot be read
13981     // from the child:
13982     ELEMENT.inheritedProps = {
13983         fontSize: 1,
13984         fontStyle: 1,
13985         opacity: 1
13986     };
13987
13988     Ext.override(ELEMENT, {
13989
13990         /**
13991          * TODO: Look at this
13992          */
13993         // private  ==> used by Fx
13994         adjustWidth : function(width) {
13995             var me = this,
13996                 isNum = (typeof width == 'number');
13997
13998             if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
13999                width -= (me.getBorderWidth("lr") + me.getPadding("lr"));
14000             }
14001             return (isNum && width < 0) ? 0 : width;
14002         },
14003
14004         // private   ==> used by Fx
14005         adjustHeight : function(height) {
14006             var me = this,
14007                 isNum = (typeof height == "number");
14008
14009             if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
14010                height -= (me.getBorderWidth("tb") + me.getPadding("tb"));
14011             }
14012             return (isNum && height < 0) ? 0 : height;
14013         },
14014
14015
14016         /**
14017          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
14018          * @param {String/String[]} className The CSS classes to add separated by space, or an array of classes
14019          * @return {Ext.Element} this
14020          */
14021         addCls : function(className){
14022             var me = this,
14023                 cls = [],
14024                 space = ((me.dom.className.replace(trimRe, '') == '') ? "" : " "),
14025                 i, len, v;
14026             if (className === undefined) {
14027                 return me;
14028             }
14029             // Separate case is for speed
14030             if (Object.prototype.toString.call(className) !== '[object Array]') {
14031                 if (typeof className === 'string') {
14032                     className = className.replace(trimRe, '').split(spacesRe);
14033                     if (className.length === 1) {
14034                         className = className[0];
14035                         if (!me.hasCls(className)) {
14036                             me.dom.className += space + className;
14037                         }
14038                     } else {
14039                         this.addCls(className);
14040                     }
14041                 }
14042             } else {
14043                 for (i = 0, len = className.length; i < len; i++) {
14044                     v = className[i];
14045                     if (typeof v == 'string' && (' ' + me.dom.className + ' ').indexOf(' ' + v + ' ') == -1) {
14046                         cls.push(v);
14047                     }
14048                 }
14049                 if (cls.length) {
14050                     me.dom.className += space + cls.join(" ");
14051                 }
14052             }
14053             return me;
14054         },
14055
14056         /**
14057          * Removes one or more CSS classes from the element.
14058          * @param {String/String[]} className The CSS classes to remove separated by space, or an array of classes
14059          * @return {Ext.Element} this
14060          */
14061         removeCls : function(className){
14062             var me = this,
14063                 i, idx, len, cls, elClasses;
14064             if (className === undefined) {
14065                 return me;
14066             }
14067             if (Object.prototype.toString.call(className) !== '[object Array]') {
14068                 className = className.replace(trimRe, '').split(spacesRe);
14069             }
14070             if (me.dom && me.dom.className) {
14071                 elClasses = me.dom.className.replace(trimRe, '').split(spacesRe);
14072                 for (i = 0, len = className.length; i < len; i++) {
14073                     cls = className[i];
14074                     if (typeof cls == 'string') {
14075                         cls = cls.replace(trimRe, '');
14076                         idx = Ext.Array.indexOf(elClasses, cls);
14077                         if (idx != -1) {
14078                             Ext.Array.erase(elClasses, idx, 1);
14079                         }
14080                     }
14081                 }
14082                 me.dom.className = elClasses.join(" ");
14083             }
14084             return me;
14085         },
14086
14087         /**
14088          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
14089          * @param {String/String[]} className The CSS class to add, or an array of classes
14090          * @return {Ext.Element} this
14091          */
14092         radioCls : function(className){
14093             var cn = this.dom.parentNode.childNodes,
14094                 v, i, len;
14095             className = Ext.isArray(className) ? className : [className];
14096             for (i = 0, len = cn.length; i < len; i++) {
14097                 v = cn[i];
14098                 if (v && v.nodeType == 1) {
14099                     Ext.fly(v, '_internal').removeCls(className);
14100                 }
14101             }
14102             return this.addCls(className);
14103         },
14104
14105         /**
14106          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
14107          * @param {String} className The CSS class to toggle
14108          * @return {Ext.Element} this
14109          * @method
14110          */
14111         toggleCls : Ext.supports.ClassList ?
14112             function(className) {
14113                 this.dom.classList.toggle(Ext.String.trim(className));
14114                 return this;
14115             } :
14116             function(className) {
14117                 return this.hasCls(className) ? this.removeCls(className) : this.addCls(className);
14118             },
14119
14120         /**
14121          * Checks if the specified CSS class exists on this element's DOM node.
14122          * @param {String} className The CSS class to check for
14123          * @return {Boolean} True if the class exists, else false
14124          * @method
14125          */
14126         hasCls : Ext.supports.ClassList ?
14127             function(className) {
14128                 if (!className) {
14129                     return false;
14130                 }
14131                 className = className.split(spacesRe);
14132                 var ln = className.length,
14133                     i = 0;
14134                 for (; i < ln; i++) {
14135                     if (className[i] && this.dom.classList.contains(className[i])) {
14136                         return true;
14137                     }
14138                 }
14139                 return false;
14140             } :
14141             function(className){
14142                 return className && (' ' + this.dom.className + ' ').indexOf(' ' + className + ' ') != -1;
14143             },
14144
14145         /**
14146          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
14147          * @param {String} oldClassName The CSS class to replace
14148          * @param {String} newClassName The replacement CSS class
14149          * @return {Ext.Element} this
14150          */
14151         replaceCls : function(oldClassName, newClassName){
14152             return this.removeCls(oldClassName).addCls(newClassName);
14153         },
14154
14155         isStyle : function(style, val) {
14156             return this.getStyle(style) == val;
14157         },
14158
14159         /**
14160          * Normalizes currentStyle and computedStyle.
14161          * @param {String} property The style property whose value is returned.
14162          * @return {String} The current value of the style property for this element.
14163          * @method
14164          */
14165         getStyle : function() {
14166             return view && view.getComputedStyle ?
14167                 function(prop){
14168                     var el = this.dom,
14169                         v, cs, out, display, cleaner;
14170
14171                     if(el == document){
14172                         return null;
14173                     }
14174                     prop = ELEMENT.normalize(prop);
14175                     out = (v = el.style[prop]) ? v :
14176                            (cs = view.getComputedStyle(el, "")) ? cs[prop] : null;
14177
14178                     // Ignore cases when the margin is correctly reported as 0, the bug only shows
14179                     // numbers larger.
14180                     if(prop == 'marginRight' && out != '0px' && !supports.RightMargin){
14181                         cleaner = ELEMENT.getRightMarginFixCleaner(el);
14182                         display = this.getStyle('display');
14183                         el.style.display = 'inline-block';
14184                         out = view.getComputedStyle(el, '').marginRight;
14185                         el.style.display = display;
14186                         cleaner();
14187                     }
14188
14189                     if(prop == 'backgroundColor' && out == 'rgba(0, 0, 0, 0)' && !supports.TransparentColor){
14190                         out = 'transparent';
14191                     }
14192                     return out;
14193                 } :
14194                 function (prop) {
14195                     var el = this.dom,
14196                         m, cs;
14197
14198                     if (el == document) {
14199                         return null;
14200                     }
14201                     prop = ELEMENT.normalize(prop);
14202
14203                     do {
14204                         if (prop == 'opacity') {
14205                             if (el.style.filter.match) {
14206                                 m = el.style.filter.match(opacityRe);
14207                                 if(m){
14208                                     var fv = parseFloat(m[1]);
14209                                     if(!isNaN(fv)){
14210                                         return fv ? fv / 100 : 0;
14211                                     }
14212                                 }
14213                             }
14214                             return 1;
14215                         }
14216
14217                         // the try statement does have a cost, so we avoid it unless we are
14218                         // on IE6
14219                         if (!Ext.isIE6) {
14220                             return el.style[prop] || ((cs = el.currentStyle) ? cs[prop] : null);
14221                         }
14222
14223                         try {
14224                             return el.style[prop] || ((cs = el.currentStyle) ? cs[prop] : null);
14225                         } catch (e) {
14226                             // in some cases, IE6 will throw Invalid Argument for properties
14227                             // like fontSize (see in /examples/tabs/tabs.html).
14228                         }
14229
14230                         if (!ELEMENT.inheritedProps[prop]) {
14231                             break;
14232                         }
14233
14234                         el = el.parentNode;
14235                         // this is _not_ perfect, but we can only hope that the style we
14236                         // need is inherited from a parentNode. If not and since IE won't
14237                         // give us the info we need, we are never going to be 100% right.
14238                     } while (el);
14239
14240                     return null;
14241                 }
14242         }(),
14243
14244         /**
14245          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
14246          * are convert to standard 6 digit hex color.
14247          * @param {String} attr The css attribute
14248          * @param {String} defaultValue The default value to use when a valid color isn't found
14249          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
14250          * color anims.
14251          */
14252         getColor : function(attr, defaultValue, prefix){
14253             var v = this.getStyle(attr),
14254                 color = prefix || prefix === '' ? prefix : '#',
14255                 h;
14256
14257             if(!v || (/transparent|inherit/.test(v))) {
14258                 return defaultValue;
14259             }
14260             if(/^r/.test(v)){
14261                 Ext.each(v.slice(4, v.length -1).split(','), function(s){
14262                     h = parseInt(s, 10);
14263                     color += (h < 16 ? '0' : '') + h.toString(16);
14264                 });
14265             }else{
14266                 v = v.replace('#', '');
14267                 color += v.length == 3 ? v.replace(/^(\w)(\w)(\w)$/, '$1$1$2$2$3$3') : v;
14268             }
14269             return(color.length > 5 ? color.toLowerCase() : defaultValue);
14270         },
14271
14272         /**
14273          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
14274          * @param {String/Object} property The style property to be set, or an object of multiple styles.
14275          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
14276          * @return {Ext.Element} this
14277          */
14278         setStyle : function(prop, value){
14279             var me = this,
14280                 tmp, style;
14281
14282             if (!me.dom) {
14283                 return me;
14284             }
14285             if (typeof prop === 'string') {
14286                 tmp = {};
14287                 tmp[prop] = value;
14288                 prop = tmp;
14289             }
14290             for (style in prop) {
14291                 if (prop.hasOwnProperty(style)) {
14292                     value = Ext.value(prop[style], '');
14293                     if (style == 'opacity') {
14294                         me.setOpacity(value);
14295                     }
14296                     else {
14297                         me.dom.style[ELEMENT.normalize(style)] = value;
14298                     }
14299                 }
14300             }
14301             return me;
14302         },
14303
14304         /**
14305          * Set the opacity of the element
14306          * @param {Number} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
14307          * @param {Boolean/Object} animate (optional) a standard Element animation config object or <tt>true</tt> for
14308          * the default animation (<tt>{duration: .35, easing: 'easeIn'}</tt>)
14309          * @return {Ext.Element} this
14310          */
14311         setOpacity: function(opacity, animate) {
14312             var me = this,
14313                 dom = me.dom,
14314                 val,
14315                 style;
14316
14317             if (!me.dom) {
14318                 return me;
14319             }
14320
14321             style = me.dom.style;
14322
14323             if (!animate || !me.anim) {
14324                 if (!Ext.supports.Opacity) {
14325                     opacity = opacity < 1 ? 'alpha(opacity=' + opacity * 100 + ')': '';
14326                     val = style.filter.replace(opacityRe, '').replace(trimRe, '');
14327
14328                     style.zoom = 1;
14329                     style.filter = val + (val.length > 0 ? ' ': '') + opacity;
14330                 }
14331                 else {
14332                     style.opacity = opacity;
14333                 }
14334             }
14335             else {
14336                 if (!Ext.isObject(animate)) {
14337                     animate = {
14338                         duration: 350,
14339                         easing: 'ease-in'
14340                     };
14341                 }
14342                 me.animate(Ext.applyIf({
14343                     to: {
14344                         opacity: opacity
14345                     }
14346                 },
14347                 animate));
14348             }
14349             return me;
14350         },
14351
14352
14353         /**
14354          * Clears any opacity settings from this element. Required in some cases for IE.
14355          * @return {Ext.Element} this
14356          */
14357         clearOpacity : function(){
14358             var style = this.dom.style;
14359             if(!Ext.supports.Opacity){
14360                 if(!Ext.isEmpty(style.filter)){
14361                     style.filter = style.filter.replace(opacityRe, '').replace(trimRe, '');
14362                 }
14363             }else{
14364                 style.opacity = style['-moz-opacity'] = style['-khtml-opacity'] = '';
14365             }
14366             return this;
14367         },
14368
14369         /**
14370          * @private
14371          * Returns 1 if the browser returns the subpixel dimension rounded to the lowest pixel.
14372          * @return {Number} 0 or 1
14373          */
14374         adjustDirect2DDimension: function(dimension) {
14375             var me = this,
14376                 dom = me.dom,
14377                 display = me.getStyle('display'),
14378                 inlineDisplay = dom.style['display'],
14379                 inlinePosition = dom.style['position'],
14380                 originIndex = dimension === 'width' ? 0 : 1,
14381                 floating;
14382
14383             if (display === 'inline') {
14384                 dom.style['display'] = 'inline-block';
14385             }
14386
14387             dom.style['position'] = display.match(adjustDirect2DTableRe) ? 'absolute' : 'static';
14388
14389             // floating will contain digits that appears after the decimal point
14390             // if height or width are set to auto we fallback to msTransformOrigin calculation
14391             floating = (parseFloat(me.getStyle(dimension)) || parseFloat(dom.currentStyle.msTransformOrigin.split(' ')[originIndex]) * 2) % 1;
14392
14393             dom.style['position'] = inlinePosition;
14394
14395             if (display === 'inline') {
14396                 dom.style['display'] = inlineDisplay;
14397             }
14398
14399             return floating;
14400         },
14401
14402         /**
14403          * Returns the offset height of the element
14404          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
14405          * @return {Number} The element's height
14406          */
14407         getHeight: function(contentHeight, preciseHeight) {
14408             var me = this,
14409                 dom = me.dom,
14410                 hidden = Ext.isIE && me.isStyle('display', 'none'),
14411                 height, overflow, style, floating;
14412
14413             // IE Quirks mode acts more like a max-size measurement unless overflow is hidden during measurement.
14414             // We will put the overflow back to it's original value when we are done measuring.
14415             if (Ext.isIEQuirks) {
14416                 style = dom.style;
14417                 overflow = style.overflow;
14418                 me.setStyle({ overflow: 'hidden'});
14419             }
14420
14421             height = dom.offsetHeight;
14422
14423             height = MATH.max(height, hidden ? 0 : dom.clientHeight) || 0;
14424
14425             // IE9 Direct2D dimension rounding bug
14426             if (!hidden && Ext.supports.Direct2DBug) {
14427                 floating = me.adjustDirect2DDimension('height');
14428                 if (preciseHeight) {
14429                     height += floating;
14430                 }
14431                 else if (floating > 0 && floating < 0.5) {
14432                     height++;
14433                 }
14434             }
14435
14436             if (contentHeight) {
14437                 height -= (me.getBorderWidth("tb") + me.getPadding("tb"));
14438             }
14439
14440             if (Ext.isIEQuirks) {
14441                 me.setStyle({ overflow: overflow});
14442             }
14443
14444             if (height < 0) {
14445                 height = 0;
14446             }
14447             return height;
14448         },
14449
14450         /**
14451          * Returns the offset width of the element
14452          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
14453          * @return {Number} The element's width
14454          */
14455         getWidth: function(contentWidth, preciseWidth) {
14456             var me = this,
14457                 dom = me.dom,
14458                 hidden = Ext.isIE && me.isStyle('display', 'none'),
14459                 rect, width, overflow, style, floating, parentPosition;
14460
14461             // IE Quirks mode acts more like a max-size measurement unless overflow is hidden during measurement.
14462             // We will put the overflow back to it's original value when we are done measuring.
14463             if (Ext.isIEQuirks) {
14464                 style = dom.style;
14465                 overflow = style.overflow;
14466                 me.setStyle({overflow: 'hidden'});
14467             }
14468
14469             // Fix Opera 10.5x width calculation issues
14470             if (Ext.isOpera10_5) {
14471                 if (dom.parentNode.currentStyle.position === 'relative') {
14472                     parentPosition = dom.parentNode.style.position;
14473                     dom.parentNode.style.position = 'static';
14474                     width = dom.offsetWidth;
14475                     dom.parentNode.style.position = parentPosition;
14476                 }
14477                 width = Math.max(width || 0, dom.offsetWidth);
14478
14479             // Gecko will in some cases report an offsetWidth that is actually less than the width of the
14480             // text contents, because it measures fonts with sub-pixel precision but rounds the calculated
14481             // value down. Using getBoundingClientRect instead of offsetWidth allows us to get the precise
14482             // subpixel measurements so we can force them to always be rounded up. See
14483             // https://bugzilla.mozilla.org/show_bug.cgi?id=458617
14484             } else if (Ext.supports.BoundingClientRect) {
14485                 rect = dom.getBoundingClientRect();
14486                 width = rect.right - rect.left;
14487                 width = preciseWidth ? width : Math.ceil(width);
14488             } else {
14489                 width = dom.offsetWidth;
14490             }
14491
14492             width = MATH.max(width, hidden ? 0 : dom.clientWidth) || 0;
14493
14494             // IE9 Direct2D dimension rounding bug
14495             if (!hidden && Ext.supports.Direct2DBug) {
14496                 floating = me.adjustDirect2DDimension('width');
14497                 if (preciseWidth) {
14498                     width += floating;
14499                 }
14500                 else if (floating > 0 && floating < 0.5) {
14501                     width++;
14502                 }
14503             }
14504
14505             if (contentWidth) {
14506                 width -= (me.getBorderWidth("lr") + me.getPadding("lr"));
14507             }
14508
14509             if (Ext.isIEQuirks) {
14510                 me.setStyle({ overflow: overflow});
14511             }
14512
14513             if (width < 0) {
14514                 width = 0;
14515             }
14516             return width;
14517         },
14518
14519         /**
14520          * Set the width of this Element.
14521          * @param {Number/String} width The new width. This may be one of:<div class="mdetail-params"><ul>
14522          * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
14523          * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
14524          * </ul></div>
14525          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
14526          * @return {Ext.Element} this
14527          */
14528         setWidth : function(width, animate){
14529             var me = this;
14530             width = me.adjustWidth(width);
14531             if (!animate || !me.anim) {
14532                 me.dom.style.width = me.addUnits(width);
14533             }
14534             else {
14535                 if (!Ext.isObject(animate)) {
14536                     animate = {};
14537                 }
14538                 me.animate(Ext.applyIf({
14539                     to: {
14540                         width: width
14541                     }
14542                 }, animate));
14543             }
14544             return me;
14545         },
14546
14547         /**
14548          * Set the height of this Element.
14549          * <pre><code>
14550 // change the height to 200px and animate with default configuration
14551 Ext.fly('elementId').setHeight(200, true);
14552
14553 // change the height to 150px and animate with a custom configuration
14554 Ext.fly('elId').setHeight(150, {
14555     duration : .5, // animation will have a duration of .5 seconds
14556     // will change the content to "finished"
14557     callback: function(){ this.{@link #update}("finished"); }
14558 });
14559          * </code></pre>
14560          * @param {Number/String} height The new height. This may be one of:<div class="mdetail-params"><ul>
14561          * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels.)</li>
14562          * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
14563          * </ul></div>
14564          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
14565          * @return {Ext.Element} this
14566          */
14567          setHeight : function(height, animate){
14568             var me = this;
14569             height = me.adjustHeight(height);
14570             if (!animate || !me.anim) {
14571                 me.dom.style.height = me.addUnits(height);
14572             }
14573             else {
14574                 if (!Ext.isObject(animate)) {
14575                     animate = {};
14576                 }
14577                 me.animate(Ext.applyIf({
14578                     to: {
14579                         height: height
14580                     }
14581                 }, animate));
14582             }
14583             return me;
14584         },
14585
14586         /**
14587          * Gets the width of the border(s) for the specified side(s)
14588          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
14589          * passing <tt>'lr'</tt> would get the border <b><u>l</u></b>eft width + the border <b><u>r</u></b>ight width.
14590          * @return {Number} The width of the sides passed added together
14591          */
14592         getBorderWidth : function(side){
14593             return this.addStyles(side, borders);
14594         },
14595
14596         /**
14597          * Gets the width of the padding(s) for the specified side(s)
14598          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
14599          * passing <tt>'lr'</tt> would get the padding <b><u>l</u></b>eft + the padding <b><u>r</u></b>ight.
14600          * @return {Number} The padding of the sides passed added together
14601          */
14602         getPadding : function(side){
14603             return this.addStyles(side, paddings);
14604         },
14605
14606         /**
14607          *  Store the current overflow setting and clip overflow on the element - use <tt>{@link #unclip}</tt> to remove
14608          * @return {Ext.Element} this
14609          */
14610         clip : function(){
14611             var me = this,
14612                 dom = me.dom;
14613
14614             if(!data(dom, ISCLIPPED)){
14615                 data(dom, ISCLIPPED, true);
14616                 data(dom, ORIGINALCLIP, {
14617                     o: me.getStyle(OVERFLOW),
14618                     x: me.getStyle(OVERFLOWX),
14619                     y: me.getStyle(OVERFLOWY)
14620                 });
14621                 me.setStyle(OVERFLOW, HIDDEN);
14622                 me.setStyle(OVERFLOWX, HIDDEN);
14623                 me.setStyle(OVERFLOWY, HIDDEN);
14624             }
14625             return me;
14626         },
14627
14628         /**
14629          *  Return clipping (overflow) to original clipping before <tt>{@link #clip}</tt> was called
14630          * @return {Ext.Element} this
14631          */
14632         unclip : function(){
14633             var me = this,
14634                 dom = me.dom,
14635                 clip;
14636
14637             if(data(dom, ISCLIPPED)){
14638                 data(dom, ISCLIPPED, false);
14639                 clip = data(dom, ORIGINALCLIP);
14640                 if(clip.o){
14641                     me.setStyle(OVERFLOW, clip.o);
14642                 }
14643                 if(clip.x){
14644                     me.setStyle(OVERFLOWX, clip.x);
14645                 }
14646                 if(clip.y){
14647                     me.setStyle(OVERFLOWY, clip.y);
14648                 }
14649             }
14650             return me;
14651         },
14652
14653         // private
14654         addStyles : function(sides, styles){
14655             var totalSize = 0,
14656                 sidesArr = sides.match(wordsRe),
14657                 i = 0,
14658                 len = sidesArr.length,
14659                 side, size;
14660             for (; i < len; i++) {
14661                 side = sidesArr[i];
14662                 size = side && parseInt(this.getStyle(styles[side]), 10);
14663                 if (size) {
14664                     totalSize += MATH.abs(size);
14665                 }
14666             }
14667             return totalSize;
14668         },
14669
14670         margins : margins,
14671
14672         /**
14673          * More flexible version of {@link #setStyle} for setting style properties.
14674          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
14675          * a function which returns such a specification.
14676          * @return {Ext.Element} this
14677          */
14678         applyStyles : function(style){
14679             Ext.DomHelper.applyStyles(this.dom, style);
14680             return this;
14681         },
14682
14683         /**
14684          * Returns an object with properties matching the styles requested.
14685          * For example, el.getStyles('color', 'font-size', 'width') might return
14686          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
14687          * @param {String} style1 A style name
14688          * @param {String} style2 A style name
14689          * @param {String} etc.
14690          * @return {Object} The style object
14691          */
14692         getStyles : function(){
14693             var styles = {},
14694                 len = arguments.length,
14695                 i = 0, style;
14696
14697             for(; i < len; ++i) {
14698                 style = arguments[i];
14699                 styles[style] = this.getStyle(style);
14700             }
14701             return styles;
14702         },
14703
14704        /**
14705         * <p>Wraps the specified element with a special 9 element markup/CSS block that renders by default as
14706         * a gray container with a gradient background, rounded corners and a 4-way shadow.</p>
14707         * <p>This special markup is used throughout Ext when box wrapping elements ({@link Ext.button.Button},
14708         * {@link Ext.panel.Panel} when <tt>{@link Ext.panel.Panel#frame frame=true}</tt>, {@link Ext.window.Window}).  The markup
14709         * is of this form:</p>
14710         * <pre><code>
14711     Ext.Element.boxMarkup =
14712     &#39;&lt;div class="{0}-tl">&lt;div class="{0}-tr">&lt;div class="{0}-tc">&lt;/div>&lt;/div>&lt;/div>
14713      &lt;div class="{0}-ml">&lt;div class="{0}-mr">&lt;div class="{0}-mc">&lt;/div>&lt;/div>&lt;/div>
14714      &lt;div class="{0}-bl">&lt;div class="{0}-br">&lt;div class="{0}-bc">&lt;/div>&lt;/div>&lt;/div>&#39;;
14715         * </code></pre>
14716         * <p>Example usage:</p>
14717         * <pre><code>
14718     // Basic box wrap
14719     Ext.get("foo").boxWrap();
14720
14721     // You can also add a custom class and use CSS inheritance rules to customize the box look.
14722     // 'x-box-blue' is a built-in alternative -- look at the related CSS definitions as an example
14723     // for how to create a custom box wrap style.
14724     Ext.get("foo").boxWrap().addCls("x-box-blue");
14725         * </code></pre>
14726         * @param {String} class (optional) A base CSS class to apply to the containing wrapper element
14727         * (defaults to <tt>'x-box'</tt>). Note that there are a number of CSS rules that are dependent on
14728         * this name to make the overall effect work, so if you supply an alternate base class, make sure you
14729         * also supply all of the necessary rules.
14730         * @return {Ext.Element} The outermost wrapping element of the created box structure.
14731         */
14732         boxWrap : function(cls){
14733             cls = cls || Ext.baseCSSPrefix + 'box';
14734             var el = Ext.get(this.insertHtml("beforeBegin", "<div class='" + cls + "'>" + Ext.String.format(ELEMENT.boxMarkup, cls) + "</div>"));
14735             Ext.DomQuery.selectNode('.' + cls + '-mc', el.dom).appendChild(this.dom);
14736             return el;
14737         },
14738
14739         /**
14740          * Set the size of this Element. If animation is true, both width and height will be animated concurrently.
14741          * @param {Number/String} width The new width. This may be one of:<div class="mdetail-params"><ul>
14742          * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
14743          * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
14744          * <li>A size object in the format <code>{width: widthValue, height: heightValue}</code>.</li>
14745          * </ul></div>
14746          * @param {Number/String} height The new height. This may be one of:<div class="mdetail-params"><ul>
14747          * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels).</li>
14748          * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
14749          * </ul></div>
14750          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
14751          * @return {Ext.Element} this
14752          */
14753         setSize : function(width, height, animate){
14754             var me = this;
14755             if (Ext.isObject(width)) { // in case of object from getSize()
14756                 animate = height;
14757                 height = width.height;
14758                 width = width.width;
14759             }
14760             width = me.adjustWidth(width);
14761             height = me.adjustHeight(height);
14762             if(!animate || !me.anim){
14763                 // Must touch some property before setting style.width/height on non-quirk IE6,7, or the
14764                 // properties will not reflect the changes on the style immediately
14765                 if (!Ext.isIEQuirks && (Ext.isIE6 || Ext.isIE7)) {
14766                     me.dom.offsetTop;
14767                 }
14768                 me.dom.style.width = me.addUnits(width);
14769                 me.dom.style.height = me.addUnits(height);
14770             }
14771             else {
14772                 if (animate === true) {
14773                     animate = {};
14774                 }
14775                 me.animate(Ext.applyIf({
14776                     to: {
14777                         width: width,
14778                         height: height
14779                     }
14780                 }, animate));
14781             }
14782             return me;
14783         },
14784
14785         /**
14786          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
14787          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
14788          * if a height has not been set using CSS.
14789          * @return {Number}
14790          */
14791         getComputedHeight : function(){
14792             var me = this,
14793                 h = Math.max(me.dom.offsetHeight, me.dom.clientHeight);
14794             if(!h){
14795                 h = parseFloat(me.getStyle('height')) || 0;
14796                 if(!me.isBorderBox()){
14797                     h += me.getFrameWidth('tb');
14798                 }
14799             }
14800             return h;
14801         },
14802
14803         /**
14804          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
14805          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
14806          * if a width has not been set using CSS.
14807          * @return {Number}
14808          */
14809         getComputedWidth : function(){
14810             var me = this,
14811                 w = Math.max(me.dom.offsetWidth, me.dom.clientWidth);
14812
14813             if(!w){
14814                 w = parseFloat(me.getStyle('width')) || 0;
14815                 if(!me.isBorderBox()){
14816                     w += me.getFrameWidth('lr');
14817                 }
14818             }
14819             return w;
14820         },
14821
14822         /**
14823          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
14824          for more information about the sides.
14825          * @param {String} sides
14826          * @return {Number}
14827          */
14828         getFrameWidth : function(sides, onlyContentBox){
14829             return onlyContentBox && this.isBorderBox() ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
14830         },
14831
14832         /**
14833          * Sets up event handlers to add and remove a css class when the mouse is over this element
14834          * @param {String} className
14835          * @return {Ext.Element} this
14836          */
14837         addClsOnOver : function(className){
14838             var dom = this.dom;
14839             this.hover(
14840                 function(){
14841                     Ext.fly(dom, INTERNAL).addCls(className);
14842                 },
14843                 function(){
14844                     Ext.fly(dom, INTERNAL).removeCls(className);
14845                 }
14846             );
14847             return this;
14848         },
14849
14850         /**
14851          * Sets up event handlers to add and remove a css class when this element has the focus
14852          * @param {String} className
14853          * @return {Ext.Element} this
14854          */
14855         addClsOnFocus : function(className){
14856             var me = this,
14857                 dom = me.dom;
14858             me.on("focus", function(){
14859                 Ext.fly(dom, INTERNAL).addCls(className);
14860             });
14861             me.on("blur", function(){
14862                 Ext.fly(dom, INTERNAL).removeCls(className);
14863             });
14864             return me;
14865         },
14866
14867         /**
14868          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
14869          * @param {String} className
14870          * @return {Ext.Element} this
14871          */
14872         addClsOnClick : function(className){
14873             var dom = this.dom;
14874             this.on("mousedown", function(){
14875                 Ext.fly(dom, INTERNAL).addCls(className);
14876                 var d = Ext.getDoc(),
14877                     fn = function(){
14878                         Ext.fly(dom, INTERNAL).removeCls(className);
14879                         d.removeListener("mouseup", fn);
14880                     };
14881                 d.on("mouseup", fn);
14882             });
14883             return this;
14884         },
14885
14886         /**
14887          * <p>Returns the dimensions of the element available to lay content out in.<p>
14888          * <p>If the element (or any ancestor element) has CSS style <code>display : none</code>, the dimensions will be zero.</p>
14889          * example:<pre><code>
14890         var vpSize = Ext.getBody().getViewSize();
14891
14892         // all Windows created afterwards will have a default value of 90% height and 95% width
14893         Ext.Window.override({
14894             width: vpSize.width * 0.9,
14895             height: vpSize.height * 0.95
14896         });
14897         // To handle window resizing you would have to hook onto onWindowResize.
14898         * </code></pre>
14899         *
14900         * getViewSize utilizes clientHeight/clientWidth which excludes sizing of scrollbars.
14901         * To obtain the size including scrollbars, use getStyleSize
14902         *
14903         * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
14904         */
14905
14906         getViewSize : function(){
14907             var me = this,
14908                 dom = me.dom,
14909                 isDoc = (dom == Ext.getDoc().dom || dom == Ext.getBody().dom),
14910                 style, overflow, ret;
14911
14912             // If the body, use static methods
14913             if (isDoc) {
14914                 ret = {
14915                     width : ELEMENT.getViewWidth(),
14916                     height : ELEMENT.getViewHeight()
14917                 };
14918
14919             // Else use clientHeight/clientWidth
14920             }
14921             else {
14922                 // IE 6 & IE Quirks mode acts more like a max-size measurement unless overflow is hidden during measurement.
14923                 // We will put the overflow back to it's original value when we are done measuring.
14924                 if (Ext.isIE6 || Ext.isIEQuirks) {
14925                     style = dom.style;
14926                     overflow = style.overflow;
14927                     me.setStyle({ overflow: 'hidden'});
14928                 }
14929                 ret = {
14930                     width : dom.clientWidth,
14931                     height : dom.clientHeight
14932                 };
14933                 if (Ext.isIE6 || Ext.isIEQuirks) {
14934                     me.setStyle({ overflow: overflow });
14935                 }
14936             }
14937             return ret;
14938         },
14939
14940         /**
14941         * <p>Returns the dimensions of the element available to lay content out in.<p>
14942         *
14943         * getStyleSize utilizes prefers style sizing if present, otherwise it chooses the larger of offsetHeight/clientHeight and offsetWidth/clientWidth.
14944         * To obtain the size excluding scrollbars, use getViewSize
14945         *
14946         * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
14947         */
14948
14949         getStyleSize : function(){
14950             var me = this,
14951                 doc = document,
14952                 d = this.dom,
14953                 isDoc = (d == doc || d == doc.body),
14954                 s = d.style,
14955                 w, h;
14956
14957             // If the body, use static methods
14958             if (isDoc) {
14959                 return {
14960                     width : ELEMENT.getViewWidth(),
14961                     height : ELEMENT.getViewHeight()
14962                 };
14963             }
14964             // Use Styles if they are set
14965             if(s.width && s.width != 'auto'){
14966                 w = parseFloat(s.width);
14967                 if(me.isBorderBox()){
14968                    w -= me.getFrameWidth('lr');
14969                 }
14970             }
14971             // Use Styles if they are set
14972             if(s.height && s.height != 'auto'){
14973                 h = parseFloat(s.height);
14974                 if(me.isBorderBox()){
14975                    h -= me.getFrameWidth('tb');
14976                 }
14977             }
14978             // Use getWidth/getHeight if style not set.
14979             return {width: w || me.getWidth(true), height: h || me.getHeight(true)};
14980         },
14981
14982         /**
14983          * Returns the size of the element.
14984          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
14985          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
14986          */
14987         getSize : function(contentSize){
14988             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
14989         },
14990
14991         /**
14992          * Forces the browser to repaint this element
14993          * @return {Ext.Element} this
14994          */
14995         repaint : function(){
14996             var dom = this.dom;
14997             this.addCls(Ext.baseCSSPrefix + 'repaint');
14998             setTimeout(function(){
14999                 Ext.fly(dom).removeCls(Ext.baseCSSPrefix + 'repaint');
15000             }, 1);
15001             return this;
15002         },
15003
15004         /**
15005          * Enable text selection for this element (normalized across browsers)
15006          * @return {Ext.Element} this
15007          */
15008         selectable : function() {
15009             var me = this;
15010             me.dom.unselectable = "off";
15011             // Prevent it from bubles up and enables it to be selectable
15012             me.on('selectstart', function (e) {
15013                 e.stopPropagation();
15014                 return true;
15015             });
15016             me.applyStyles("-moz-user-select: text; -khtml-user-select: text;");
15017             me.removeCls(Ext.baseCSSPrefix + 'unselectable');
15018             return me;
15019         },
15020
15021         /**
15022          * Disables text selection for this element (normalized across browsers)
15023          * @return {Ext.Element} this
15024          */
15025         unselectable : function(){
15026             var me = this;
15027             me.dom.unselectable = "on";
15028
15029             me.swallowEvent("selectstart", true);
15030             me.applyStyles("-moz-user-select:-moz-none;-khtml-user-select:none;");
15031             me.addCls(Ext.baseCSSPrefix + 'unselectable');
15032
15033             return me;
15034         },
15035
15036         /**
15037          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
15038          * then it returns the calculated width of the sides (see getPadding)
15039          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
15040          * @return {Object/Number}
15041          */
15042         getMargin : function(side){
15043             var me = this,
15044                 hash = {t:"top", l:"left", r:"right", b: "bottom"},
15045                 o = {},
15046                 key;
15047
15048             if (!side) {
15049                 for (key in me.margins){
15050                     o[hash[key]] = parseFloat(me.getStyle(me.margins[key])) || 0;
15051                 }
15052                 return o;
15053             } else {
15054                 return me.addStyles.call(me, side, me.margins);
15055             }
15056         }
15057     });
15058 })();
15059 /**
15060  * @class Ext.Element
15061  */
15062 /**
15063  * Visibility mode constant for use with {@link #setVisibilityMode}. Use visibility to hide element
15064  * @static
15065  * @type Number
15066  */
15067 Ext.Element.VISIBILITY = 1;
15068 /**
15069  * Visibility mode constant for use with {@link #setVisibilityMode}. Use display to hide element
15070  * @static
15071  * @type Number
15072  */
15073 Ext.Element.DISPLAY = 2;
15074
15075 /**
15076  * Visibility mode constant for use with {@link #setVisibilityMode}. Use offsets (x and y positioning offscreen)
15077  * to hide element.
15078  * @static
15079  * @type Number
15080  */
15081 Ext.Element.OFFSETS = 3;
15082
15083
15084 Ext.Element.ASCLASS = 4;
15085
15086 /**
15087  * Defaults to 'x-hide-nosize'
15088  * @static
15089  * @type String
15090  */
15091 Ext.Element.visibilityCls = Ext.baseCSSPrefix + 'hide-nosize';
15092
15093 Ext.Element.addMethods(function(){
15094     var El = Ext.Element,
15095         OPACITY = "opacity",
15096         VISIBILITY = "visibility",
15097         DISPLAY = "display",
15098         HIDDEN = "hidden",
15099         OFFSETS = "offsets",
15100         ASCLASS = "asclass",
15101         NONE = "none",
15102         NOSIZE = 'nosize',
15103         ORIGINALDISPLAY = 'originalDisplay',
15104         VISMODE = 'visibilityMode',
15105         ISVISIBLE = 'isVisible',
15106         data = El.data,
15107         getDisplay = function(dom){
15108             var d = data(dom, ORIGINALDISPLAY);
15109             if(d === undefined){
15110                 data(dom, ORIGINALDISPLAY, d = '');
15111             }
15112             return d;
15113         },
15114         getVisMode = function(dom){
15115             var m = data(dom, VISMODE);
15116             if(m === undefined){
15117                 data(dom, VISMODE, m = 1);
15118             }
15119             return m;
15120         };
15121
15122     return {
15123         /**
15124          * @property {String} originalDisplay
15125          * The element's default display mode
15126          */
15127         originalDisplay : "",
15128         visibilityMode : 1,
15129
15130         /**
15131          * Sets the element's visibility mode. When setVisible() is called it
15132          * will use this to determine whether to set the visibility or the display property.
15133          * @param {Number} visMode Ext.Element.VISIBILITY or Ext.Element.DISPLAY
15134          * @return {Ext.Element} this
15135          */
15136         setVisibilityMode : function(visMode){
15137             data(this.dom, VISMODE, visMode);
15138             return this;
15139         },
15140
15141         /**
15142          * Checks whether the element is currently visible using both visibility and display properties.
15143          * @return {Boolean} True if the element is currently visible, else false
15144          */
15145         isVisible : function() {
15146             var me = this,
15147                 dom = me.dom,
15148                 visible = data(dom, ISVISIBLE);
15149
15150             if(typeof visible == 'boolean'){ //return the cached value if registered
15151                 return visible;
15152             }
15153             //Determine the current state based on display states
15154             visible = !me.isStyle(VISIBILITY, HIDDEN) &&
15155                       !me.isStyle(DISPLAY, NONE) &&
15156                       !((getVisMode(dom) == El.ASCLASS) && me.hasCls(me.visibilityCls || El.visibilityCls));
15157
15158             data(dom, ISVISIBLE, visible);
15159             return visible;
15160         },
15161
15162         /**
15163          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
15164          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
15165          * @param {Boolean} visible Whether the element is visible
15166          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
15167          * @return {Ext.Element} this
15168          */
15169         setVisible : function(visible, animate){
15170             var me = this, isDisplay, isVisibility, isOffsets, isNosize,
15171                 dom = me.dom,
15172                 visMode = getVisMode(dom);
15173
15174
15175             // hideMode string override
15176             if (typeof animate == 'string'){
15177                 switch (animate) {
15178                     case DISPLAY:
15179                         visMode = El.DISPLAY;
15180                         break;
15181                     case VISIBILITY:
15182                         visMode = El.VISIBILITY;
15183                         break;
15184                     case OFFSETS:
15185                         visMode = El.OFFSETS;
15186                         break;
15187                     case NOSIZE:
15188                     case ASCLASS:
15189                         visMode = El.ASCLASS;
15190                         break;
15191                 }
15192                 me.setVisibilityMode(visMode);
15193                 animate = false;
15194             }
15195
15196             if (!animate || !me.anim) {
15197                 if(visMode == El.ASCLASS ){
15198
15199                     me[visible?'removeCls':'addCls'](me.visibilityCls || El.visibilityCls);
15200
15201                 } else if (visMode == El.DISPLAY){
15202
15203                     return me.setDisplayed(visible);
15204
15205                 } else if (visMode == El.OFFSETS){
15206
15207                     if (!visible){
15208                         // Remember position for restoring, if we are not already hidden by offsets.
15209                         if (!me.hideModeStyles) {
15210                             me.hideModeStyles = {
15211                                 position: me.getStyle('position'),
15212                                 top: me.getStyle('top'),
15213                                 left: me.getStyle('left')
15214                             };
15215                         }
15216                         me.applyStyles({position: 'absolute', top: '-10000px', left: '-10000px'});
15217                     }
15218
15219                     // Only "restore" as position if we have actually been hidden using offsets.
15220                     // Calling setVisible(true) on a positioned element should not reposition it.
15221                     else if (me.hideModeStyles) {
15222                         me.applyStyles(me.hideModeStyles || {position: '', top: '', left: ''});
15223                         delete me.hideModeStyles;
15224                     }
15225
15226                 }else{
15227                     me.fixDisplay();
15228                     // Show by clearing visibility style. Explicitly setting to "visible" overrides parent visibility setting.
15229                     dom.style.visibility = visible ? '' : HIDDEN;
15230                 }
15231             }else{
15232                 // closure for composites
15233                 if(visible){
15234                     me.setOpacity(0.01);
15235                     me.setVisible(true);
15236                 }
15237                 if (!Ext.isObject(animate)) {
15238                     animate = {
15239                         duration: 350,
15240                         easing: 'ease-in'
15241                     };
15242                 }
15243                 me.animate(Ext.applyIf({
15244                     callback: function() {
15245                         visible || me.setVisible(false).setOpacity(1);
15246                     },
15247                     to: {
15248                         opacity: (visible) ? 1 : 0
15249                     }
15250                 }, animate));
15251             }
15252             data(dom, ISVISIBLE, visible);  //set logical visibility state
15253             return me;
15254         },
15255
15256
15257         /**
15258          * @private
15259          * Determine if the Element has a relevant height and width available based
15260          * upon current logical visibility state
15261          */
15262         hasMetrics  : function(){
15263             var dom = this.dom;
15264             return this.isVisible() || (getVisMode(dom) == El.OFFSETS) || (getVisMode(dom) == El.VISIBILITY);
15265         },
15266
15267         /**
15268          * Toggles the element's visibility or display, depending on visibility mode.
15269          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
15270          * @return {Ext.Element} this
15271          */
15272         toggle : function(animate){
15273             var me = this;
15274             me.setVisible(!me.isVisible(), me.anim(animate));
15275             return me;
15276         },
15277
15278         /**
15279          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
15280          * @param {Boolean/String} value Boolean value to display the element using its default display, or a string to set the display directly.
15281          * @return {Ext.Element} this
15282          */
15283         setDisplayed : function(value) {
15284             if(typeof value == "boolean"){
15285                value = value ? getDisplay(this.dom) : NONE;
15286             }
15287             this.setStyle(DISPLAY, value);
15288             return this;
15289         },
15290
15291         // private
15292         fixDisplay : function(){
15293             var me = this;
15294             if (me.isStyle(DISPLAY, NONE)) {
15295                 me.setStyle(VISIBILITY, HIDDEN);
15296                 me.setStyle(DISPLAY, getDisplay(this.dom)); // first try reverting to default
15297                 if (me.isStyle(DISPLAY, NONE)) { // if that fails, default to block
15298                     me.setStyle(DISPLAY, "block");
15299                 }
15300             }
15301         },
15302
15303         /**
15304          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
15305          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
15306          * @return {Ext.Element} this
15307          */
15308         hide : function(animate){
15309             // hideMode override
15310             if (typeof animate == 'string'){
15311                 this.setVisible(false, animate);
15312                 return this;
15313             }
15314             this.setVisible(false, this.anim(animate));
15315             return this;
15316         },
15317
15318         /**
15319         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
15320         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
15321          * @return {Ext.Element} this
15322          */
15323         show : function(animate){
15324             // hideMode override
15325             if (typeof animate == 'string'){
15326                 this.setVisible(true, animate);
15327                 return this;
15328             }
15329             this.setVisible(true, this.anim(animate));
15330             return this;
15331         }
15332     };
15333 }());
15334 /**
15335  * @class Ext.Element
15336  */
15337 Ext.applyIf(Ext.Element.prototype, {
15338     // @private override base Ext.util.Animate mixin for animate for backwards compatibility
15339     animate: function(config) {
15340         var me = this;
15341         if (!me.id) {
15342             me = Ext.get(me.dom);
15343         }
15344         if (Ext.fx.Manager.hasFxBlock(me.id)) {
15345             return me;
15346         }
15347         Ext.fx.Manager.queueFx(Ext.create('Ext.fx.Anim', me.anim(config)));
15348         return this;
15349     },
15350
15351     // @private override base Ext.util.Animate mixin for animate for backwards compatibility
15352     anim: function(config) {
15353         if (!Ext.isObject(config)) {
15354             return (config) ? {} : false;
15355         }
15356
15357         var me = this,
15358             duration = config.duration || Ext.fx.Anim.prototype.duration,
15359             easing = config.easing || 'ease',
15360             animConfig;
15361
15362         if (config.stopAnimation) {
15363             me.stopAnimation();
15364         }
15365
15366         Ext.applyIf(config, Ext.fx.Manager.getFxDefaults(me.id));
15367
15368         // Clear any 'paused' defaults.
15369         Ext.fx.Manager.setFxDefaults(me.id, {
15370             delay: 0
15371         });
15372
15373         animConfig = {
15374             target: me,
15375             remove: config.remove,
15376             alternate: config.alternate || false,
15377             duration: duration,
15378             easing: easing,
15379             callback: config.callback,
15380             listeners: config.listeners,
15381             iterations: config.iterations || 1,
15382             scope: config.scope,
15383             block: config.block,
15384             concurrent: config.concurrent,
15385             delay: config.delay || 0,
15386             paused: true,
15387             keyframes: config.keyframes,
15388             from: config.from || {},
15389             to: Ext.apply({}, config)
15390         };
15391         Ext.apply(animConfig.to, config.to);
15392
15393         // Anim API properties - backward compat
15394         delete animConfig.to.to;
15395         delete animConfig.to.from;
15396         delete animConfig.to.remove;
15397         delete animConfig.to.alternate;
15398         delete animConfig.to.keyframes;
15399         delete animConfig.to.iterations;
15400         delete animConfig.to.listeners;
15401         delete animConfig.to.target;
15402         delete animConfig.to.paused;
15403         delete animConfig.to.callback;
15404         delete animConfig.to.scope;
15405         delete animConfig.to.duration;
15406         delete animConfig.to.easing;
15407         delete animConfig.to.concurrent;
15408         delete animConfig.to.block;
15409         delete animConfig.to.stopAnimation;
15410         delete animConfig.to.delay;
15411         return animConfig;
15412     },
15413
15414     /**
15415      * Slides the element into view. An anchor point can be optionally passed to set the point of origin for the slide
15416      * effect. This function automatically handles wrapping the element with a fixed-size container if needed. See the
15417      * Fx class overview for valid anchor point options. Usage:
15418      *
15419      *     // default: slide the element in from the top
15420      *     el.slideIn();
15421      *
15422      *     // custom: slide the element in from the right with a 2-second duration
15423      *     el.slideIn('r', { duration: 2000 });
15424      *
15425      *     // common config options shown with default values
15426      *     el.slideIn('t', {
15427      *         easing: 'easeOut',
15428      *         duration: 500
15429      *     });
15430      *
15431      * @param {String} [anchor='t'] One of the valid Fx anchor positions
15432      * @param {Object} [options] Object literal with any of the Fx config options
15433      * @return {Ext.Element} The Element
15434      */
15435     slideIn: function(anchor, obj, slideOut) {
15436         var me = this,
15437             elStyle = me.dom.style,
15438             beforeAnim, wrapAnim;
15439
15440         anchor = anchor || "t";
15441         obj = obj || {};
15442
15443         beforeAnim = function() {
15444             var animScope = this,
15445                 listeners = obj.listeners,
15446                 box, position, restoreSize, wrap, anim;
15447
15448             if (!slideOut) {
15449                 me.fixDisplay();
15450             }
15451
15452             box = me.getBox();
15453             if ((anchor == 't' || anchor == 'b') && box.height === 0) {
15454                 box.height = me.dom.scrollHeight;
15455             }
15456             else if ((anchor == 'l' || anchor == 'r') && box.width === 0) {
15457                 box.width = me.dom.scrollWidth;
15458             }
15459
15460             position = me.getPositioning();
15461             me.setSize(box.width, box.height);
15462
15463             wrap = me.wrap({
15464                 style: {
15465                     visibility: slideOut ? 'visible' : 'hidden'
15466                 }
15467             });
15468             wrap.setPositioning(position);
15469             if (wrap.isStyle('position', 'static')) {
15470                 wrap.position('relative');
15471             }
15472             me.clearPositioning('auto');
15473             wrap.clip();
15474
15475             // This element is temporarily positioned absolute within its wrapper.
15476             // Restore to its default, CSS-inherited visibility setting.
15477             // We cannot explicitly poke visibility:visible into its style because that overrides the visibility of the wrap.
15478             me.setStyle({
15479                 visibility: '',
15480                 position: 'absolute'
15481             });
15482             if (slideOut) {
15483                 wrap.setSize(box.width, box.height);
15484             }
15485
15486             switch (anchor) {
15487                 case 't':
15488                     anim = {
15489                         from: {
15490                             width: box.width + 'px',
15491                             height: '0px'
15492                         },
15493                         to: {
15494                             width: box.width + 'px',
15495                             height: box.height + 'px'
15496                         }
15497                     };
15498                     elStyle.bottom = '0px';
15499                     break;
15500                 case 'l':
15501                     anim = {
15502                         from: {
15503                             width: '0px',
15504                             height: box.height + 'px'
15505                         },
15506                         to: {
15507                             width: box.width + 'px',
15508                             height: box.height + 'px'
15509                         }
15510                     };
15511                     elStyle.right = '0px';
15512                     break;
15513                 case 'r':
15514                     anim = {
15515                         from: {
15516                             x: box.x + box.width,
15517                             width: '0px',
15518                             height: box.height + 'px'
15519                         },
15520                         to: {
15521                             x: box.x,
15522                             width: box.width + 'px',
15523                             height: box.height + 'px'
15524                         }
15525                     };
15526                     break;
15527                 case 'b':
15528                     anim = {
15529                         from: {
15530                             y: box.y + box.height,
15531                             width: box.width + 'px',
15532                             height: '0px'
15533                         },
15534                         to: {
15535                             y: box.y,
15536                             width: box.width + 'px',
15537                             height: box.height + 'px'
15538                         }
15539                     };
15540                     break;
15541                 case 'tl':
15542                     anim = {
15543                         from: {
15544                             x: box.x,
15545                             y: box.y,
15546                             width: '0px',
15547                             height: '0px'
15548                         },
15549                         to: {
15550                             width: box.width + 'px',
15551                             height: box.height + 'px'
15552                         }
15553                     };
15554                     elStyle.bottom = '0px';
15555                     elStyle.right = '0px';
15556                     break;
15557                 case 'bl':
15558                     anim = {
15559                         from: {
15560                             x: box.x + box.width,
15561                             width: '0px',
15562                             height: '0px'
15563                         },
15564                         to: {
15565                             x: box.x,
15566                             width: box.width + 'px',
15567                             height: box.height + 'px'
15568                         }
15569                     };
15570                     elStyle.right = '0px';
15571                     break;
15572                 case 'br':
15573                     anim = {
15574                         from: {
15575                             x: box.x + box.width,
15576                             y: box.y + box.height,
15577                             width: '0px',
15578                             height: '0px'
15579                         },
15580                         to: {
15581                             x: box.x,
15582                             y: box.y,
15583                             width: box.width + 'px',
15584                             height: box.height + 'px'
15585                         }
15586                     };
15587                     break;
15588                 case 'tr':
15589                     anim = {
15590                         from: {
15591                             y: box.y + box.height,
15592                             width: '0px',
15593                             height: '0px'
15594                         },
15595                         to: {
15596                             y: box.y,
15597                             width: box.width + 'px',
15598                             height: box.height + 'px'
15599                         }
15600                     };
15601                     elStyle.bottom = '0px';
15602                     break;
15603             }
15604
15605             wrap.show();
15606             wrapAnim = Ext.apply({}, obj);
15607             delete wrapAnim.listeners;
15608             wrapAnim = Ext.create('Ext.fx.Anim', Ext.applyIf(wrapAnim, {
15609                 target: wrap,
15610                 duration: 500,
15611                 easing: 'ease-out',
15612                 from: slideOut ? anim.to : anim.from,
15613                 to: slideOut ? anim.from : anim.to
15614             }));
15615
15616             // In the absence of a callback, this listener MUST be added first
15617             wrapAnim.on('afteranimate', function() {
15618                 if (slideOut) {
15619                     me.setPositioning(position);
15620                     if (obj.useDisplay) {
15621                         me.setDisplayed(false);
15622                     } else {
15623                         me.hide();
15624                     }
15625                 }
15626                 else {
15627                     me.clearPositioning();
15628                     me.setPositioning(position);
15629                 }
15630                 if (wrap.dom) {
15631                     wrap.dom.parentNode.insertBefore(me.dom, wrap.dom);
15632                     wrap.remove();
15633                 }
15634                 me.setSize(box.width, box.height);
15635                 animScope.end();
15636             });
15637             // Add configured listeners after
15638             if (listeners) {
15639                 wrapAnim.on(listeners);
15640             }
15641         };
15642
15643         me.animate({
15644             duration: obj.duration ? obj.duration * 2 : 1000,
15645             listeners: {
15646                 beforeanimate: {
15647                     fn: beforeAnim
15648                 },
15649                 afteranimate: {
15650                     fn: function() {
15651                         if (wrapAnim && wrapAnim.running) {
15652                             wrapAnim.end();
15653                         }
15654                     }
15655                 }
15656             }
15657         });
15658         return me;
15659     },
15660
15661
15662     /**
15663      * Slides the element out of view. An anchor point can be optionally passed to set the end point for the slide
15664      * effect. When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will
15665      * still take up space in the document. The element must be removed from the DOM using the 'remove' config option if
15666      * desired. This function automatically handles wrapping the element with a fixed-size container if needed. See the
15667      * Fx class overview for valid anchor point options. Usage:
15668      *
15669      *     // default: slide the element out to the top
15670      *     el.slideOut();
15671      *
15672      *     // custom: slide the element out to the right with a 2-second duration
15673      *     el.slideOut('r', { duration: 2000 });
15674      *
15675      *     // common config options shown with default values
15676      *     el.slideOut('t', {
15677      *         easing: 'easeOut',
15678      *         duration: 500,
15679      *         remove: false,
15680      *         useDisplay: false
15681      *     });
15682      *
15683      * @param {String} [anchor='t'] One of the valid Fx anchor positions
15684      * @param {Object} [options] Object literal with any of the Fx config options
15685      * @return {Ext.Element} The Element
15686      */
15687     slideOut: function(anchor, o) {
15688         return this.slideIn(anchor, o, true);
15689     },
15690
15691     /**
15692      * Fades the element out while slowly expanding it in all directions. When the effect is completed, the element will
15693      * be hidden (visibility = 'hidden') but block elements will still take up space in the document. Usage:
15694      *
15695      *     // default
15696      *     el.puff();
15697      *
15698      *     // common config options shown with default values
15699      *     el.puff({
15700      *         easing: 'easeOut',
15701      *         duration: 500,
15702      *         useDisplay: false
15703      *     });
15704      *
15705      * @param {Object} options (optional) Object literal with any of the Fx config options
15706      * @return {Ext.Element} The Element
15707      */
15708     puff: function(obj) {
15709         var me = this,
15710             beforeAnim;
15711         obj = Ext.applyIf(obj || {}, {
15712             easing: 'ease-out',
15713             duration: 500,
15714             useDisplay: false
15715         });
15716
15717         beforeAnim = function() {
15718             me.clearOpacity();
15719             me.show();
15720
15721             var box = me.getBox(),
15722                 fontSize = me.getStyle('fontSize'),
15723                 position = me.getPositioning();
15724             this.to = {
15725                 width: box.width * 2,
15726                 height: box.height * 2,
15727                 x: box.x - (box.width / 2),
15728                 y: box.y - (box.height /2),
15729                 opacity: 0,
15730                 fontSize: '200%'
15731             };
15732             this.on('afteranimate',function() {
15733                 if (me.dom) {
15734                     if (obj.useDisplay) {
15735                         me.setDisplayed(false);
15736                     } else {
15737                         me.hide();
15738                     }
15739                     me.clearOpacity();
15740                     me.setPositioning(position);
15741                     me.setStyle({fontSize: fontSize});
15742                 }
15743             });
15744         };
15745
15746         me.animate({
15747             duration: obj.duration,
15748             easing: obj.easing,
15749             listeners: {
15750                 beforeanimate: {
15751                     fn: beforeAnim
15752                 }
15753             }
15754         });
15755         return me;
15756     },
15757
15758     /**
15759      * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
15760      * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still
15761      * take up space in the document. The element must be removed from the DOM using the 'remove' config option if
15762      * desired. Usage:
15763      *
15764      *     // default
15765      *     el.switchOff();
15766      *
15767      *     // all config options shown with default values
15768      *     el.switchOff({
15769      *         easing: 'easeIn',
15770      *         duration: .3,
15771      *         remove: false,
15772      *         useDisplay: false
15773      *     });
15774      *
15775      * @param {Object} options (optional) Object literal with any of the Fx config options
15776      * @return {Ext.Element} The Element
15777      */
15778     switchOff: function(obj) {
15779         var me = this,
15780             beforeAnim;
15781
15782         obj = Ext.applyIf(obj || {}, {
15783             easing: 'ease-in',
15784             duration: 500,
15785             remove: false,
15786             useDisplay: false
15787         });
15788
15789         beforeAnim = function() {
15790             var animScope = this,
15791                 size = me.getSize(),
15792                 xy = me.getXY(),
15793                 keyframe, position;
15794             me.clearOpacity();
15795             me.clip();
15796             position = me.getPositioning();
15797
15798             keyframe = Ext.create('Ext.fx.Animator', {
15799                 target: me,
15800                 duration: obj.duration,
15801                 easing: obj.easing,
15802                 keyframes: {
15803                     33: {
15804                         opacity: 0.3
15805                     },
15806                     66: {
15807                         height: 1,
15808                         y: xy[1] + size.height / 2
15809                     },
15810                     100: {
15811                         width: 1,
15812                         x: xy[0] + size.width / 2
15813                     }
15814                 }
15815             });
15816             keyframe.on('afteranimate', function() {
15817                 if (obj.useDisplay) {
15818                     me.setDisplayed(false);
15819                 } else {
15820                     me.hide();
15821                 }
15822                 me.clearOpacity();
15823                 me.setPositioning(position);
15824                 me.setSize(size);
15825                 animScope.end();
15826             });
15827         };
15828         me.animate({
15829             duration: (obj.duration * 2),
15830             listeners: {
15831                 beforeanimate: {
15832                     fn: beforeAnim
15833                 }
15834             }
15835         });
15836         return me;
15837     },
15838
15839     /**
15840      * Shows a ripple of exploding, attenuating borders to draw attention to an Element. Usage:
15841      *
15842      *     // default: a single light blue ripple
15843      *     el.frame();
15844      *
15845      *     // custom: 3 red ripples lasting 3 seconds total
15846      *     el.frame("#ff0000", 3, { duration: 3 });
15847      *
15848      *     // common config options shown with default values
15849      *     el.frame("#C3DAF9", 1, {
15850      *         duration: 1 //duration of each individual ripple.
15851      *         // Note: Easing is not configurable and will be ignored if included
15852      *     });
15853      *
15854      * @param {String} [color='C3DAF9'] The color of the border. Should be a 6 char hex color without the leading #
15855      * (defaults to light blue).
15856      * @param {Number} [count=1] The number of ripples to display
15857      * @param {Object} [options] Object literal with any of the Fx config options
15858      * @return {Ext.Element} The Element
15859      */
15860     frame : function(color, count, obj){
15861         var me = this,
15862             beforeAnim;
15863
15864         color = color || '#C3DAF9';
15865         count = count || 1;
15866         obj = obj || {};
15867
15868         beforeAnim = function() {
15869             me.show();
15870             var animScope = this,
15871                 box = me.getBox(),
15872                 proxy = Ext.getBody().createChild({
15873                     style: {
15874                         position : 'absolute',
15875                         'pointer-events': 'none',
15876                         'z-index': 35000,
15877                         border : '0px solid ' + color
15878                     }
15879                 }),
15880                 proxyAnim;
15881             proxyAnim = Ext.create('Ext.fx.Anim', {
15882                 target: proxy,
15883                 duration: obj.duration || 1000,
15884                 iterations: count,
15885                 from: {
15886                     top: box.y,
15887                     left: box.x,
15888                     borderWidth: 0,
15889                     opacity: 1,
15890                     height: box.height,
15891                     width: box.width
15892                 },
15893                 to: {
15894                     top: box.y - 20,
15895                     left: box.x - 20,
15896                     borderWidth: 10,
15897                     opacity: 0,
15898                     height: box.height + 40,
15899                     width: box.width + 40
15900                 }
15901             });
15902             proxyAnim.on('afteranimate', function() {
15903                 proxy.remove();
15904                 animScope.end();
15905             });
15906         };
15907
15908         me.animate({
15909             duration: (obj.duration * 2) || 2000,
15910             listeners: {
15911                 beforeanimate: {
15912                     fn: beforeAnim
15913                 }
15914             }
15915         });
15916         return me;
15917     },
15918
15919     /**
15920      * Slides the element while fading it out of view. An anchor point can be optionally passed to set the ending point
15921      * of the effect. Usage:
15922      *
15923      *     // default: slide the element downward while fading out
15924      *     el.ghost();
15925      *
15926      *     // custom: slide the element out to the right with a 2-second duration
15927      *     el.ghost('r', { duration: 2000 });
15928      *
15929      *     // common config options shown with default values
15930      *     el.ghost('b', {
15931      *         easing: 'easeOut',
15932      *         duration: 500
15933      *     });
15934      *
15935      * @param {String} [anchor='b'] One of the valid Fx anchor positions
15936      * @param {Object} [options] Object literal with any of the Fx config options
15937      * @return {Ext.Element} The Element
15938      */
15939     ghost: function(anchor, obj) {
15940         var me = this,
15941             beforeAnim;
15942
15943         anchor = anchor || "b";
15944         beforeAnim = function() {
15945             var width = me.getWidth(),
15946                 height = me.getHeight(),
15947                 xy = me.getXY(),
15948                 position = me.getPositioning(),
15949                 to = {
15950                     opacity: 0
15951                 };
15952             switch (anchor) {
15953                 case 't':
15954                     to.y = xy[1] - height;
15955                     break;
15956                 case 'l':
15957                     to.x = xy[0] - width;
15958                     break;
15959                 case 'r':
15960                     to.x = xy[0] + width;
15961                     break;
15962                 case 'b':
15963                     to.y = xy[1] + height;
15964                     break;
15965                 case 'tl':
15966                     to.x = xy[0] - width;
15967                     to.y = xy[1] - height;
15968                     break;
15969                 case 'bl':
15970                     to.x = xy[0] - width;
15971                     to.y = xy[1] + height;
15972                     break;
15973                 case 'br':
15974                     to.x = xy[0] + width;
15975                     to.y = xy[1] + height;
15976                     break;
15977                 case 'tr':
15978                     to.x = xy[0] + width;
15979                     to.y = xy[1] - height;
15980                     break;
15981             }
15982             this.to = to;
15983             this.on('afteranimate', function () {
15984                 if (me.dom) {
15985                     me.hide();
15986                     me.clearOpacity();
15987                     me.setPositioning(position);
15988                 }
15989             });
15990         };
15991
15992         me.animate(Ext.applyIf(obj || {}, {
15993             duration: 500,
15994             easing: 'ease-out',
15995             listeners: {
15996                 beforeanimate: {
15997                     fn: beforeAnim
15998                 }
15999             }
16000         }));
16001         return me;
16002     },
16003
16004     /**
16005      * Highlights the Element by setting a color (applies to the background-color by default, but can be changed using
16006      * the "attr" config option) and then fading back to the original color. If no original color is available, you
16007      * should provide the "endColor" config option which will be cleared after the animation. Usage:
16008      *
16009      *     // default: highlight background to yellow
16010      *     el.highlight();
16011      *
16012      *     // custom: highlight foreground text to blue for 2 seconds
16013      *     el.highlight("0000ff", { attr: 'color', duration: 2000 });
16014      *
16015      *     // common config options shown with default values
16016      *     el.highlight("ffff9c", {
16017      *         attr: "backgroundColor", //can be any valid CSS property (attribute) that supports a color value
16018      *         endColor: (current color) or "ffffff",
16019      *         easing: 'easeIn',
16020      *         duration: 1000
16021      *     });
16022      *
16023      * @param {String} [color='ffff9c'] The highlight color. Should be a 6 char hex color without the leading #
16024      * @param {Object} [options] Object literal with any of the Fx config options
16025      * @return {Ext.Element} The Element
16026      */
16027     highlight: function(color, o) {
16028         var me = this,
16029             dom = me.dom,
16030             from = {},
16031             restore, to, attr, lns, event, fn;
16032
16033         o = o || {};
16034         lns = o.listeners || {};
16035         attr = o.attr || 'backgroundColor';
16036         from[attr] = color || 'ffff9c';
16037
16038         if (!o.to) {
16039             to = {};
16040             to[attr] = o.endColor || me.getColor(attr, 'ffffff', '');
16041         }
16042         else {
16043             to = o.to;
16044         }
16045
16046         // Don't apply directly on lns, since we reference it in our own callbacks below
16047         o.listeners = Ext.apply(Ext.apply({}, lns), {
16048             beforeanimate: function() {
16049                 restore = dom.style[attr];
16050                 me.clearOpacity();
16051                 me.show();
16052
16053                 event = lns.beforeanimate;
16054                 if (event) {
16055                     fn = event.fn || event;
16056                     return fn.apply(event.scope || lns.scope || window, arguments);
16057                 }
16058             },
16059             afteranimate: function() {
16060                 if (dom) {
16061                     dom.style[attr] = restore;
16062                 }
16063
16064                 event = lns.afteranimate;
16065                 if (event) {
16066                     fn = event.fn || event;
16067                     fn.apply(event.scope || lns.scope || window, arguments);
16068                 }
16069             }
16070         });
16071
16072         me.animate(Ext.apply({}, o, {
16073             duration: 1000,
16074             easing: 'ease-in',
16075             from: from,
16076             to: to
16077         }));
16078         return me;
16079     },
16080
16081    /**
16082     * @deprecated 4.0
16083     * Creates a pause before any subsequent queued effects begin. If there are no effects queued after the pause it will
16084     * have no effect. Usage:
16085     *
16086     *     el.pause(1);
16087     *
16088     * @param {Number} seconds The length of time to pause (in seconds)
16089     * @return {Ext.Element} The Element
16090     */
16091     pause: function(ms) {
16092         var me = this;
16093         Ext.fx.Manager.setFxDefaults(me.id, {
16094             delay: ms
16095         });
16096         return me;
16097     },
16098
16099     /**
16100      * Fade an element in (from transparent to opaque). The ending opacity can be specified using the `opacity`
16101      * config option. Usage:
16102      *
16103      *     // default: fade in from opacity 0 to 100%
16104      *     el.fadeIn();
16105      *
16106      *     // custom: fade in from opacity 0 to 75% over 2 seconds
16107      *     el.fadeIn({ opacity: .75, duration: 2000});
16108      *
16109      *     // common config options shown with default values
16110      *     el.fadeIn({
16111      *         opacity: 1, //can be any value between 0 and 1 (e.g. .5)
16112      *         easing: 'easeOut',
16113      *         duration: 500
16114      *     });
16115      *
16116      * @param {Object} options (optional) Object literal with any of the Fx config options
16117      * @return {Ext.Element} The Element
16118      */
16119     fadeIn: function(o) {
16120         this.animate(Ext.apply({}, o, {
16121             opacity: 1
16122         }));
16123         return this;
16124     },
16125
16126     /**
16127      * Fade an element out (from opaque to transparent). The ending opacity can be specified using the `opacity`
16128      * config option. Note that IE may require `useDisplay:true` in order to redisplay correctly.
16129      * Usage:
16130      *
16131      *     // default: fade out from the element's current opacity to 0
16132      *     el.fadeOut();
16133      *
16134      *     // custom: fade out from the element's current opacity to 25% over 2 seconds
16135      *     el.fadeOut({ opacity: .25, duration: 2000});
16136      *
16137      *     // common config options shown with default values
16138      *     el.fadeOut({
16139      *         opacity: 0, //can be any value between 0 and 1 (e.g. .5)
16140      *         easing: 'easeOut',
16141      *         duration: 500,
16142      *         remove: false,
16143      *         useDisplay: false
16144      *     });
16145      *
16146      * @param {Object} options (optional) Object literal with any of the Fx config options
16147      * @return {Ext.Element} The Element
16148      */
16149     fadeOut: function(o) {
16150         this.animate(Ext.apply({}, o, {
16151             opacity: 0
16152         }));
16153         return this;
16154     },
16155
16156     /**
16157      * @deprecated 4.0
16158      * Animates the transition of an element's dimensions from a starting height/width to an ending height/width. This
16159      * method is a convenience implementation of {@link #shift}. Usage:
16160      *
16161      *     // change height and width to 100x100 pixels
16162      *     el.scale(100, 100);
16163      *
16164      *     // common config options shown with default values.  The height and width will default to
16165      *     // the element's existing values if passed as null.
16166      *     el.scale(
16167      *         [element's width],
16168      *         [element's height], {
16169      *             easing: 'easeOut',
16170      *             duration: .35
16171      *         }
16172      *     );
16173      *
16174      * @param {Number} width The new width (pass undefined to keep the original width)
16175      * @param {Number} height The new height (pass undefined to keep the original height)
16176      * @param {Object} options (optional) Object literal with any of the Fx config options
16177      * @return {Ext.Element} The Element
16178      */
16179     scale: function(w, h, o) {
16180         this.animate(Ext.apply({}, o, {
16181             width: w,
16182             height: h
16183         }));
16184         return this;
16185     },
16186
16187     /**
16188      * @deprecated 4.0
16189      * Animates the transition of any combination of an element's dimensions, xy position and/or opacity. Any of these
16190      * properties not specified in the config object will not be changed. This effect requires that at least one new
16191      * dimension, position or opacity setting must be passed in on the config object in order for the function to have
16192      * any effect. Usage:
16193      *
16194      *     // slide the element horizontally to x position 200 while changing the height and opacity
16195      *     el.shift({ x: 200, height: 50, opacity: .8 });
16196      *
16197      *     // common config options shown with default values.
16198      *     el.shift({
16199      *         width: [element's width],
16200      *         height: [element's height],
16201      *         x: [element's x position],
16202      *         y: [element's y position],
16203      *         opacity: [element's opacity],
16204      *         easing: 'easeOut',
16205      *         duration: .35
16206      *     });
16207      *
16208      * @param {Object} options Object literal with any of the Fx config options
16209      * @return {Ext.Element} The Element
16210      */
16211     shift: function(config) {
16212         this.animate(config);
16213         return this;
16214     }
16215 });
16216
16217 /**
16218  * @class Ext.Element
16219  */
16220 Ext.applyIf(Ext.Element, {
16221     unitRe: /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
16222     camelRe: /(-[a-z])/gi,
16223     opacityRe: /alpha\(opacity=(.*)\)/i,
16224     cssRe: /([a-z0-9-]+)\s*:\s*([^;\s]+(?:\s*[^;\s]+)*);?/gi,
16225     propertyCache: {},
16226     defaultUnit : "px",
16227     borders: {l: 'border-left-width', r: 'border-right-width', t: 'border-top-width', b: 'border-bottom-width'},
16228     paddings: {l: 'padding-left', r: 'padding-right', t: 'padding-top', b: 'padding-bottom'},
16229     margins: {l: 'margin-left', r: 'margin-right', t: 'margin-top', b: 'margin-bottom'},
16230
16231     // Reference the prototype's version of the method. Signatures are identical.
16232     addUnits : Ext.Element.prototype.addUnits,
16233
16234     /**
16235      * Parses a number or string representing margin sizes into an object. Supports CSS-style margin declarations
16236      * (e.g. 10, "10", "10 10", "10 10 10" and "10 10 10 10" are all valid options and would return the same result)
16237      * @static
16238      * @param {Number/String} box The encoded margins
16239      * @return {Object} An object with margin sizes for top, right, bottom and left
16240      */
16241     parseBox : function(box) {
16242         if (Ext.isObject(box)) {
16243             return {
16244                 top: box.top || 0,
16245                 right: box.right || 0,
16246                 bottom: box.bottom || 0,
16247                 left: box.left || 0
16248             };
16249         } else {
16250             if (typeof box != 'string') {
16251                 box = box.toString();
16252             }
16253             var parts  = box.split(' '),
16254                 ln = parts.length;
16255     
16256             if (ln == 1) {
16257                 parts[1] = parts[2] = parts[3] = parts[0];
16258             }
16259             else if (ln == 2) {
16260                 parts[2] = parts[0];
16261                 parts[3] = parts[1];
16262             }
16263             else if (ln == 3) {
16264                 parts[3] = parts[1];
16265             }
16266     
16267             return {
16268                 top   :parseFloat(parts[0]) || 0,
16269                 right :parseFloat(parts[1]) || 0,
16270                 bottom:parseFloat(parts[2]) || 0,
16271                 left  :parseFloat(parts[3]) || 0
16272             };
16273         }
16274         
16275     },
16276     
16277     /**
16278      * Parses a number or string representing margin sizes into an object. Supports CSS-style margin declarations
16279      * (e.g. 10, "10", "10 10", "10 10 10" and "10 10 10 10" are all valid options and would return the same result)
16280      * @static
16281      * @param {Number/String} box The encoded margins
16282      * @param {String} units The type of units to add
16283      * @return {String} An string with unitized (px if units is not specified) metrics for top, right, bottom and left
16284      */
16285     unitizeBox : function(box, units) {
16286         var A = this.addUnits,
16287             B = this.parseBox(box);
16288             
16289         return A(B.top, units) + ' ' +
16290                A(B.right, units) + ' ' +
16291                A(B.bottom, units) + ' ' +
16292                A(B.left, units);
16293         
16294     },
16295
16296     // private
16297     camelReplaceFn : function(m, a) {
16298         return a.charAt(1).toUpperCase();
16299     },
16300
16301     /**
16302      * Normalizes CSS property keys from dash delimited to camel case JavaScript Syntax.
16303      * For example:
16304      * <ul>
16305      *  <li>border-width -> borderWidth</li>
16306      *  <li>padding-top -> paddingTop</li>
16307      * </ul>
16308      * @static
16309      * @param {String} prop The property to normalize
16310      * @return {String} The normalized string
16311      */
16312     normalize : function(prop) {
16313         if (prop == 'float') {
16314             prop = Ext.supports.Float ? 'cssFloat' : 'styleFloat';
16315         }
16316         return this.propertyCache[prop] || (this.propertyCache[prop] = prop.replace(this.camelRe, this.camelReplaceFn));
16317     },
16318
16319     /**
16320      * Retrieves the document height
16321      * @static
16322      * @return {Number} documentHeight
16323      */
16324     getDocumentHeight: function() {
16325         return Math.max(!Ext.isStrict ? document.body.scrollHeight : document.documentElement.scrollHeight, this.getViewportHeight());
16326     },
16327
16328     /**
16329      * Retrieves the document width
16330      * @static
16331      * @return {Number} documentWidth
16332      */
16333     getDocumentWidth: function() {
16334         return Math.max(!Ext.isStrict ? document.body.scrollWidth : document.documentElement.scrollWidth, this.getViewportWidth());
16335     },
16336
16337     /**
16338      * Retrieves the viewport height of the window.
16339      * @static
16340      * @return {Number} viewportHeight
16341      */
16342     getViewportHeight: function(){
16343         return window.innerHeight;
16344     },
16345
16346     /**
16347      * Retrieves the viewport width of the window.
16348      * @static
16349      * @return {Number} viewportWidth
16350      */
16351     getViewportWidth : function() {
16352         return window.innerWidth;
16353     },
16354
16355     /**
16356      * Retrieves the viewport size of the window.
16357      * @static
16358      * @return {Object} object containing width and height properties
16359      */
16360     getViewSize : function() {
16361         return {
16362             width: window.innerWidth,
16363             height: window.innerHeight
16364         };
16365     },
16366
16367     /**
16368      * Retrieves the current orientation of the window. This is calculated by
16369      * determing if the height is greater than the width.
16370      * @static
16371      * @return {String} Orientation of window: 'portrait' or 'landscape'
16372      */
16373     getOrientation : function() {
16374         if (Ext.supports.OrientationChange) {
16375             return (window.orientation == 0) ? 'portrait' : 'landscape';
16376         }
16377         
16378         return (window.innerHeight > window.innerWidth) ? 'portrait' : 'landscape';
16379     },
16380
16381     /** 
16382      * Returns the top Element that is located at the passed coordinates
16383      * @static
16384      * @param {Number} x The x coordinate
16385      * @param {Number} y The y coordinate
16386      * @return {String} The found Element
16387      */
16388     fromPoint: function(x, y) {
16389         return Ext.get(document.elementFromPoint(x, y));
16390     },
16391     
16392     /**
16393      * Converts a CSS string into an object with a property for each style.
16394      * <p>
16395      * The sample code below would return an object with 2 properties, one
16396      * for background-color and one for color.</p>
16397      * <pre><code>
16398 var css = 'background-color: red;color: blue; ';
16399 console.log(Ext.Element.parseStyles(css));
16400      * </code></pre>
16401      * @static
16402      * @param {String} styles A CSS string
16403      * @return {Object} styles
16404      */
16405     parseStyles: function(styles){
16406         var out = {},
16407             cssRe = this.cssRe,
16408             matches;
16409             
16410         if (styles) {
16411             // Since we're using the g flag on the regex, we need to set the lastIndex.
16412             // This automatically happens on some implementations, but not others, see:
16413             // http://stackoverflow.com/questions/2645273/javascript-regular-expression-literal-persists-between-function-calls
16414             // http://blog.stevenlevithan.com/archives/fixing-javascript-regexp
16415             cssRe.lastIndex = 0;
16416             while ((matches = cssRe.exec(styles))) {
16417                 out[matches[1]] = matches[2];
16418             }
16419         }
16420         return out;
16421     }
16422 });
16423
16424 /**
16425  * @class Ext.CompositeElementLite
16426  * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter
16427  * members, or to perform collective actions upon the whole set.</p>
16428  * <p>Although they are not listed, this class supports all of the methods of {@link Ext.Element} and
16429  * {@link Ext.fx.Anim}. The methods from these classes will be performed on all the elements in this collection.</p>
16430  * Example:<pre><code>
16431 var els = Ext.select("#some-el div.some-class");
16432 // or select directly from an existing element
16433 var el = Ext.get('some-el');
16434 el.select('div.some-class');
16435
16436 els.setWidth(100); // all elements become 100 width
16437 els.hide(true); // all elements fade out and hide
16438 // or
16439 els.setWidth(100).hide(true);
16440 </code></pre>
16441  */
16442 Ext.CompositeElementLite = function(els, root){
16443     /**
16444      * <p>The Array of DOM elements which this CompositeElement encapsulates. Read-only.</p>
16445      * <p>This will not <i>usually</i> be accessed in developers' code, but developers wishing
16446      * to augment the capabilities of the CompositeElementLite class may use it when adding
16447      * methods to the class.</p>
16448      * <p>For example to add the <code>nextAll</code> method to the class to <b>add</b> all
16449      * following siblings of selected elements, the code would be</p><code><pre>
16450 Ext.override(Ext.CompositeElementLite, {
16451     nextAll: function() {
16452         var els = this.elements, i, l = els.length, n, r = [], ri = -1;
16453
16454 //      Loop through all elements in this Composite, accumulating
16455 //      an Array of all siblings.
16456         for (i = 0; i < l; i++) {
16457             for (n = els[i].nextSibling; n; n = n.nextSibling) {
16458                 r[++ri] = n;
16459             }
16460         }
16461
16462 //      Add all found siblings to this Composite
16463         return this.add(r);
16464     }
16465 });</pre></code>
16466      * @property {HTMLElement} elements
16467      */
16468     this.elements = [];
16469     this.add(els, root);
16470     this.el = new Ext.Element.Flyweight();
16471 };
16472
16473 Ext.CompositeElementLite.prototype = {
16474     isComposite: true,
16475
16476     // private
16477     getElement : function(el){
16478         // Set the shared flyweight dom property to the current element
16479         var e = this.el;
16480         e.dom = el;
16481         e.id = el.id;
16482         return e;
16483     },
16484
16485     // private
16486     transformElement : function(el){
16487         return Ext.getDom(el);
16488     },
16489
16490     /**
16491      * Returns the number of elements in this Composite.
16492      * @return Number
16493      */
16494     getCount : function(){
16495         return this.elements.length;
16496     },
16497     /**
16498      * Adds elements to this Composite object.
16499      * @param {HTMLElement[]/Ext.CompositeElement} els Either an Array of DOM elements to add, or another Composite object who's elements should be added.
16500      * @return {Ext.CompositeElement} This Composite object.
16501      */
16502     add : function(els, root){
16503         var me = this,
16504             elements = me.elements;
16505         if(!els){
16506             return this;
16507         }
16508         if(typeof els == "string"){
16509             els = Ext.Element.selectorFunction(els, root);
16510         }else if(els.isComposite){
16511             els = els.elements;
16512         }else if(!Ext.isIterable(els)){
16513             els = [els];
16514         }
16515
16516         for(var i = 0, len = els.length; i < len; ++i){
16517             elements.push(me.transformElement(els[i]));
16518         }
16519         return me;
16520     },
16521
16522     invoke : function(fn, args){
16523         var me = this,
16524             els = me.elements,
16525             len = els.length,
16526             e,
16527             i;
16528
16529         for(i = 0; i < len; i++) {
16530             e = els[i];
16531             if(e){
16532                 Ext.Element.prototype[fn].apply(me.getElement(e), args);
16533             }
16534         }
16535         return me;
16536     },
16537     /**
16538      * Returns a flyweight Element of the dom element object at the specified index
16539      * @param {Number} index
16540      * @return {Ext.Element}
16541      */
16542     item : function(index){
16543         var me = this,
16544             el = me.elements[index],
16545             out = null;
16546
16547         if(el){
16548             out = me.getElement(el);
16549         }
16550         return out;
16551     },
16552
16553     // fixes scope with flyweight
16554     addListener : function(eventName, handler, scope, opt){
16555         var els = this.elements,
16556             len = els.length,
16557             i, e;
16558
16559         for(i = 0; i<len; i++) {
16560             e = els[i];
16561             if(e) {
16562                 Ext.EventManager.on(e, eventName, handler, scope || e, opt);
16563             }
16564         }
16565         return this;
16566     },
16567     /**
16568      * <p>Calls the passed function for each element in this composite.</p>
16569      * @param {Function} fn The function to call. The function is passed the following parameters:<ul>
16570      * <li><b>el</b> : Element<div class="sub-desc">The current Element in the iteration.
16571      * <b>This is the flyweight (shared) Ext.Element instance, so if you require a
16572      * a reference to the dom node, use el.dom.</b></div></li>
16573      * <li><b>c</b> : Composite<div class="sub-desc">This Composite object.</div></li>
16574      * <li><b>idx</b> : Number<div class="sub-desc">The zero-based index in the iteration.</div></li>
16575      * </ul>
16576      * @param {Object} [scope] The scope (<i>this</i> reference) in which the function is executed. (defaults to the Element)
16577      * @return {Ext.CompositeElement} this
16578      */
16579     each : function(fn, scope){
16580         var me = this,
16581             els = me.elements,
16582             len = els.length,
16583             i, e;
16584
16585         for(i = 0; i<len; i++) {
16586             e = els[i];
16587             if(e){
16588                 e = this.getElement(e);
16589                 if(fn.call(scope || e, e, me, i) === false){
16590                     break;
16591                 }
16592             }
16593         }
16594         return me;
16595     },
16596
16597     /**
16598     * Clears this Composite and adds the elements passed.
16599     * @param {HTMLElement[]/Ext.CompositeElement} els Either an array of DOM elements, or another Composite from which to fill this Composite.
16600     * @return {Ext.CompositeElement} this
16601     */
16602     fill : function(els){
16603         var me = this;
16604         me.elements = [];
16605         me.add(els);
16606         return me;
16607     },
16608
16609     /**
16610      * Filters this composite to only elements that match the passed selector.
16611      * @param {String/Function} selector A string CSS selector or a comparison function.
16612      * The comparison function will be called with the following arguments:<ul>
16613      * <li><code>el</code> : Ext.Element<div class="sub-desc">The current DOM element.</div></li>
16614      * <li><code>index</code> : Number<div class="sub-desc">The current index within the collection.</div></li>
16615      * </ul>
16616      * @return {Ext.CompositeElement} this
16617      */
16618     filter : function(selector){
16619         var els = [],
16620             me = this,
16621             fn = Ext.isFunction(selector) ? selector
16622                 : function(el){
16623                     return el.is(selector);
16624                 };
16625
16626         me.each(function(el, self, i) {
16627             if (fn(el, i) !== false) {
16628                 els[els.length] = me.transformElement(el);
16629             }
16630         });
16631
16632         me.elements = els;
16633         return me;
16634     },
16635
16636     /**
16637      * Find the index of the passed element within the composite collection.
16638      * @param el {Mixed} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection.
16639      * @return Number The index of the passed Ext.Element in the composite collection, or -1 if not found.
16640      */
16641     indexOf : function(el){
16642         return Ext.Array.indexOf(this.elements, this.transformElement(el));
16643     },
16644
16645     /**
16646     * Replaces the specified element with the passed element.
16647     * @param {String/HTMLElement/Ext.Element/Number} el The id of an element, the Element itself, the index of the element in this composite
16648     * to replace.
16649     * @param {String/Ext.Element} replacement The id of an element or the Element itself.
16650     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
16651     * @return {Ext.CompositeElement} this
16652     */
16653     replaceElement : function(el, replacement, domReplace){
16654         var index = !isNaN(el) ? el : this.indexOf(el),
16655             d;
16656         if(index > -1){
16657             replacement = Ext.getDom(replacement);
16658             if(domReplace){
16659                 d = this.elements[index];
16660                 d.parentNode.insertBefore(replacement, d);
16661                 Ext.removeNode(d);
16662             }
16663             Ext.Array.splice(this.elements, index, 1, replacement);
16664         }
16665         return this;
16666     },
16667
16668     /**
16669      * Removes all elements.
16670      */
16671     clear : function(){
16672         this.elements = [];
16673     }
16674 };
16675
16676 Ext.CompositeElementLite.prototype.on = Ext.CompositeElementLite.prototype.addListener;
16677
16678 /**
16679  * @private
16680  * Copies all of the functions from Ext.Element's prototype onto CompositeElementLite's prototype.
16681  * This is called twice - once immediately below, and once again after additional Ext.Element
16682  * are added in Ext JS
16683  */
16684 Ext.CompositeElementLite.importElementMethods = function() {
16685     var fnName,
16686         ElProto = Ext.Element.prototype,
16687         CelProto = Ext.CompositeElementLite.prototype;
16688
16689     for (fnName in ElProto) {
16690         if (typeof ElProto[fnName] == 'function'){
16691             (function(fnName) {
16692                 CelProto[fnName] = CelProto[fnName] || function() {
16693                     return this.invoke(fnName, arguments);
16694                 };
16695             }).call(CelProto, fnName);
16696
16697         }
16698     }
16699 };
16700
16701 Ext.CompositeElementLite.importElementMethods();
16702
16703 if(Ext.DomQuery){
16704     Ext.Element.selectorFunction = Ext.DomQuery.select;
16705 }
16706
16707 /**
16708  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
16709  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
16710  * {@link Ext.CompositeElementLite CompositeElementLite} object.
16711  * @param {String/HTMLElement[]} selector The CSS selector or an array of elements
16712  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
16713  * @return {Ext.CompositeElementLite/Ext.CompositeElement}
16714  * @member Ext.Element
16715  * @method select
16716  */
16717 Ext.Element.select = function(selector, root){
16718     var els;
16719     if(typeof selector == "string"){
16720         els = Ext.Element.selectorFunction(selector, root);
16721     }else if(selector.length !== undefined){
16722         els = selector;
16723     }else{
16724     }
16725     return new Ext.CompositeElementLite(els);
16726 };
16727 /**
16728  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
16729  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
16730  * {@link Ext.CompositeElementLite CompositeElementLite} object.
16731  * @param {String/HTMLElement[]} selector The CSS selector or an array of elements
16732  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
16733  * @return {Ext.CompositeElementLite/Ext.CompositeElement}
16734  * @member Ext
16735  * @method select
16736  */
16737 Ext.select = Ext.Element.select;
16738
16739 /**
16740  * @class Ext.util.DelayedTask
16741  * 
16742  * The DelayedTask class provides a convenient way to "buffer" the execution of a method,
16743  * performing setTimeout where a new timeout cancels the old timeout. When called, the
16744  * task will wait the specified time period before executing. If durng that time period,
16745  * the task is called again, the original call will be cancelled. This continues so that
16746  * the function is only called a single time for each iteration.
16747  * 
16748  * This method is especially useful for things like detecting whether a user has finished
16749  * typing in a text field. An example would be performing validation on a keypress. You can
16750  * use this class to buffer the keypress events for a certain number of milliseconds, and
16751  * perform only if they stop for that amount of time.  
16752  * 
16753  * ## Usage
16754  * 
16755  *     var task = new Ext.util.DelayedTask(function(){
16756  *         alert(Ext.getDom('myInputField').value.length);
16757  *     });
16758  *     
16759  *     // Wait 500ms before calling our function. If the user presses another key
16760  *     // during that 500ms, it will be cancelled and we'll wait another 500ms.
16761  *     Ext.get('myInputField').on('keypress', function(){
16762  *         task.{@link #delay}(500);
16763  *     });
16764  * 
16765  * Note that we are using a DelayedTask here to illustrate a point. The configuration
16766  * option `buffer` for {@link Ext.util.Observable#addListener addListener/on} will
16767  * also setup a delayed task for you to buffer events.
16768  * 
16769  * @constructor The parameters to this constructor serve as defaults and are not required.
16770  * @param {Function} fn (optional) The default function to call. If not specified here, it must be specified during the {@link #delay} call.
16771  * @param {Object} scope (optional) The default scope (The <code><b>this</b></code> reference) in which the
16772  * function is called. If not specified, <code>this</code> will refer to the browser window.
16773  * @param {Array} args (optional) The default Array of arguments.
16774  */
16775 Ext.util.DelayedTask = function(fn, scope, args) {
16776     var me = this,
16777         id,
16778         call = function() {
16779             clearInterval(id);
16780             id = null;
16781             fn.apply(scope, args || []);
16782         };
16783
16784     /**
16785      * Cancels any pending timeout and queues a new one
16786      * @param {Number} delay The milliseconds to delay
16787      * @param {Function} newFn (optional) Overrides function passed to constructor
16788      * @param {Object} newScope (optional) Overrides scope passed to constructor. Remember that if no scope
16789      * is specified, <code>this</code> will refer to the browser window.
16790      * @param {Array} newArgs (optional) Overrides args passed to constructor
16791      */
16792     this.delay = function(delay, newFn, newScope, newArgs) {
16793         me.cancel();
16794         fn = newFn || fn;
16795         scope = newScope || scope;
16796         args = newArgs || args;
16797         id = setInterval(call, delay);
16798     };
16799
16800     /**
16801      * Cancel the last queued timeout
16802      */
16803     this.cancel = function(){
16804         if (id) {
16805             clearInterval(id);
16806             id = null;
16807         }
16808     };
16809 };
16810 Ext.require('Ext.util.DelayedTask', function() {
16811
16812     Ext.util.Event = Ext.extend(Object, (function() {
16813         function createBuffered(handler, listener, o, scope) {
16814             listener.task = new Ext.util.DelayedTask();
16815             return function() {
16816                 listener.task.delay(o.buffer, handler, scope, Ext.Array.toArray(arguments));
16817             };
16818         }
16819
16820         function createDelayed(handler, listener, o, scope) {
16821             return function() {
16822                 var task = new Ext.util.DelayedTask();
16823                 if (!listener.tasks) {
16824                     listener.tasks = [];
16825                 }
16826                 listener.tasks.push(task);
16827                 task.delay(o.delay || 10, handler, scope, Ext.Array.toArray(arguments));
16828             };
16829         }
16830
16831         function createSingle(handler, listener, o, scope) {
16832             return function() {
16833                 listener.ev.removeListener(listener.fn, scope);
16834                 return handler.apply(scope, arguments);
16835             };
16836         }
16837
16838         return {
16839             isEvent: true,
16840
16841             constructor: function(observable, name) {
16842                 this.name = name;
16843                 this.observable = observable;
16844                 this.listeners = [];
16845             },
16846
16847             addListener: function(fn, scope, options) {
16848                 var me = this,
16849                     listener;
16850                     scope = scope || me.observable;
16851
16852
16853                 if (!me.isListening(fn, scope)) {
16854                     listener = me.createListener(fn, scope, options);
16855                     if (me.firing) {
16856                         // if we are currently firing this event, don't disturb the listener loop
16857                         me.listeners = me.listeners.slice(0);
16858                     }
16859                     me.listeners.push(listener);
16860                 }
16861             },
16862
16863             createListener: function(fn, scope, o) {
16864                 o = o || {};
16865                 scope = scope || this.observable;
16866
16867                 var listener = {
16868                         fn: fn,
16869                         scope: scope,
16870                         o: o,
16871                         ev: this
16872                     },
16873                     handler = fn;
16874
16875                 // The order is important. The 'single' wrapper must be wrapped by the 'buffer' and 'delayed' wrapper
16876                 // because the event removal that the single listener does destroys the listener's DelayedTask(s)
16877                 if (o.single) {
16878                     handler = createSingle(handler, listener, o, scope);
16879                 }
16880                 if (o.delay) {
16881                     handler = createDelayed(handler, listener, o, scope);
16882                 }
16883                 if (o.buffer) {
16884                     handler = createBuffered(handler, listener, o, scope);
16885                 }
16886
16887                 listener.fireFn = handler;
16888                 return listener;
16889             },
16890
16891             findListener: function(fn, scope) {
16892                 var listeners = this.listeners,
16893                 i = listeners.length,
16894                 listener,
16895                 s;
16896
16897                 while (i--) {
16898                     listener = listeners[i];
16899                     if (listener) {
16900                         s = listener.scope;
16901                         if (listener.fn == fn && (s == scope || s == this.observable)) {
16902                             return i;
16903                         }
16904                     }
16905                 }
16906
16907                 return - 1;
16908             },
16909
16910             isListening: function(fn, scope) {
16911                 return this.findListener(fn, scope) !== -1;
16912             },
16913
16914             removeListener: function(fn, scope) {
16915                 var me = this,
16916                     index,
16917                     listener,
16918                     k;
16919                 index = me.findListener(fn, scope);
16920                 if (index != -1) {
16921                     listener = me.listeners[index];
16922
16923                     if (me.firing) {
16924                         me.listeners = me.listeners.slice(0);
16925                     }
16926
16927                     // cancel and remove a buffered handler that hasn't fired yet
16928                     if (listener.task) {
16929                         listener.task.cancel();
16930                         delete listener.task;
16931                     }
16932
16933                     // cancel and remove all delayed handlers that haven't fired yet
16934                     k = listener.tasks && listener.tasks.length;
16935                     if (k) {
16936                         while (k--) {
16937                             listener.tasks[k].cancel();
16938                         }
16939                         delete listener.tasks;
16940                     }
16941
16942                     // remove this listener from the listeners array
16943                     Ext.Array.erase(me.listeners, index, 1);
16944                     return true;
16945                 }
16946
16947                 return false;
16948             },
16949
16950             // Iterate to stop any buffered/delayed events
16951             clearListeners: function() {
16952                 var listeners = this.listeners,
16953                     i = listeners.length;
16954
16955                 while (i--) {
16956                     this.removeListener(listeners[i].fn, listeners[i].scope);
16957                 }
16958             },
16959
16960             fire: function() {
16961                 var me = this,
16962                     listeners = me.listeners,
16963                     count = listeners.length,
16964                     i,
16965                     args,
16966                     listener;
16967
16968                 if (count > 0) {
16969                     me.firing = true;
16970                     for (i = 0; i < count; i++) {
16971                         listener = listeners[i];
16972                         args = arguments.length ? Array.prototype.slice.call(arguments, 0) : [];
16973                         if (listener.o) {
16974                             args.push(listener.o);
16975                         }
16976                         if (listener && listener.fireFn.apply(listener.scope || me.observable, args) === false) {
16977                             return (me.firing = false);
16978                         }
16979                     }
16980                 }
16981                 me.firing = false;
16982                 return true;
16983             }
16984         };
16985     })());
16986 });
16987
16988 /**
16989  * @class Ext.EventManager
16990  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides
16991  * several useful events directly.
16992  * See {@link Ext.EventObject} for more details on normalized event objects.
16993  * @singleton
16994  */
16995 Ext.EventManager = {
16996
16997     // --------------------- onReady ---------------------
16998
16999     /**
17000      * Check if we have bound our global onReady listener
17001      * @private
17002      */
17003     hasBoundOnReady: false,
17004
17005     /**
17006      * Check if fireDocReady has been called
17007      * @private
17008      */
17009     hasFiredReady: false,
17010
17011     /**
17012      * Timer for the document ready event in old IE versions
17013      * @private
17014      */
17015     readyTimeout: null,
17016
17017     /**
17018      * Checks if we have bound an onreadystatechange event
17019      * @private
17020      */
17021     hasOnReadyStateChange: false,
17022
17023     /**
17024      * Holds references to any onReady functions
17025      * @private
17026      */
17027     readyEvent: new Ext.util.Event(),
17028
17029     /**
17030      * Check the ready state for old IE versions
17031      * @private
17032      * @return {Boolean} True if the document is ready
17033      */
17034     checkReadyState: function(){
17035         var me = Ext.EventManager;
17036
17037         if(window.attachEvent){
17038             // See here for reference: http://javascript.nwbox.com/IEContentLoaded/
17039             // licensed courtesy of http://developer.yahoo.com/yui/license.html
17040             if (window != top) {
17041                 return false;
17042             }
17043             try{
17044                 document.documentElement.doScroll('left');
17045             }catch(e){
17046                 return false;
17047             }
17048             me.fireDocReady();
17049             return true;
17050         }
17051         if (document.readyState == 'complete') {
17052             me.fireDocReady();
17053             return true;
17054         }
17055         me.readyTimeout = setTimeout(arguments.callee, 2);
17056         return false;
17057     },
17058
17059     /**
17060      * Binds the appropriate browser event for checking if the DOM has loaded.
17061      * @private
17062      */
17063     bindReadyEvent: function(){
17064         var me = Ext.EventManager;
17065         if (me.hasBoundOnReady) {
17066             return;
17067         }
17068
17069         if (document.addEventListener) {
17070             document.addEventListener('DOMContentLoaded', me.fireDocReady, false);
17071             // fallback, load will ~always~ fire
17072             window.addEventListener('load', me.fireDocReady, false);
17073         } else {
17074             // check if the document is ready, this will also kick off the scroll checking timer
17075             if (!me.checkReadyState()) {
17076                 document.attachEvent('onreadystatechange', me.checkReadyState);
17077                 me.hasOnReadyStateChange = true;
17078             }
17079             // fallback, onload will ~always~ fire
17080             window.attachEvent('onload', me.fireDocReady, false);
17081         }
17082         me.hasBoundOnReady = true;
17083     },
17084
17085     /**
17086      * We know the document is loaded, so trigger any onReady events.
17087      * @private
17088      */
17089     fireDocReady: function(){
17090         var me = Ext.EventManager;
17091
17092         // only unbind these events once
17093         if (!me.hasFiredReady) {
17094             me.hasFiredReady = true;
17095
17096             if (document.addEventListener) {
17097                 document.removeEventListener('DOMContentLoaded', me.fireDocReady, false);
17098                 window.removeEventListener('load', me.fireDocReady, false);
17099             } else {
17100                 if (me.readyTimeout !== null) {
17101                     clearTimeout(me.readyTimeout);
17102                 }
17103                 if (me.hasOnReadyStateChange) {
17104                     document.detachEvent('onreadystatechange', me.checkReadyState);
17105                 }
17106                 window.detachEvent('onload', me.fireDocReady);
17107             }
17108             Ext.supports.init();
17109         }
17110         if (!Ext.isReady) {
17111             Ext.isReady = true;
17112             me.onWindowUnload();
17113             me.readyEvent.fire();
17114         }
17115     },
17116
17117     /**
17118      * Adds a listener to be notified when the document is ready (before onload and before images are loaded). Can be
17119      * accessed shorthanded as Ext.onReady().
17120      * @param {Function} fn The method the event invokes.
17121      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
17122      * @param {Boolean} options (optional) Options object as passed to {@link Ext.Element#addListener}.
17123      */
17124     onDocumentReady: function(fn, scope, options){
17125         options = options || {};
17126         var me = Ext.EventManager,
17127             readyEvent = me.readyEvent;
17128
17129         // force single to be true so our event is only ever fired once.
17130         options.single = true;
17131
17132         // Document already loaded, let's just fire it
17133         if (Ext.isReady) {
17134             readyEvent.addListener(fn, scope, options);
17135             readyEvent.fire();
17136         } else {
17137             options.delay = options.delay || 1;
17138             readyEvent.addListener(fn, scope, options);
17139             me.bindReadyEvent();
17140         }
17141     },
17142
17143
17144     // --------------------- event binding ---------------------
17145
17146     /**
17147      * Contains a list of all document mouse downs, so we can ensure they fire even when stopEvent is called.
17148      * @private
17149      */
17150     stoppedMouseDownEvent: new Ext.util.Event(),
17151
17152     /**
17153      * Options to parse for the 4th argument to addListener.
17154      * @private
17155      */
17156     propRe: /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate|freezeEvent)$/,
17157
17158     /**
17159      * Get the id of the element. If one has not been assigned, automatically assign it.
17160      * @param {HTMLElement/Ext.Element} element The element to get the id for.
17161      * @return {String} id
17162      */
17163     getId : function(element) {
17164         var skipGarbageCollection = false,
17165             id;
17166
17167         element = Ext.getDom(element);
17168
17169         if (element === document || element === window) {
17170             id = element === document ? Ext.documentId : Ext.windowId;
17171         }
17172         else {
17173             id = Ext.id(element);
17174         }
17175         // skip garbage collection for special elements (window, document, iframes)
17176         if (element && (element.getElementById || element.navigator)) {
17177             skipGarbageCollection = true;
17178         }
17179
17180         if (!Ext.cache[id]){
17181             Ext.Element.addToCache(new Ext.Element(element), id);
17182             if (skipGarbageCollection) {
17183                 Ext.cache[id].skipGarbageCollection = true;
17184             }
17185         }
17186         return id;
17187     },
17188
17189     /**
17190      * Convert a "config style" listener into a set of flat arguments so they can be passed to addListener
17191      * @private
17192      * @param {Object} element The element the event is for
17193      * @param {Object} event The event configuration
17194      * @param {Object} isRemove True if a removal should be performed, otherwise an add will be done.
17195      */
17196     prepareListenerConfig: function(element, config, isRemove){
17197         var me = this,
17198             propRe = me.propRe,
17199             key, value, args;
17200
17201         // loop over all the keys in the object
17202         for (key in config) {
17203             if (config.hasOwnProperty(key)) {
17204                 // if the key is something else then an event option
17205                 if (!propRe.test(key)) {
17206                     value = config[key];
17207                     // if the value is a function it must be something like click: function(){}, scope: this
17208                     // which means that there might be multiple event listeners with shared options
17209                     if (Ext.isFunction(value)) {
17210                         // shared options
17211                         args = [element, key, value, config.scope, config];
17212                     } else {
17213                         // if its not a function, it must be an object like click: {fn: function(){}, scope: this}
17214                         args = [element, key, value.fn, value.scope, value];
17215                     }
17216
17217                     if (isRemove === true) {
17218                         me.removeListener.apply(this, args);
17219                     } else {
17220                         me.addListener.apply(me, args);
17221                     }
17222                 }
17223             }
17224         }
17225     },
17226
17227     /**
17228      * Normalize cross browser event differences
17229      * @private
17230      * @param {Object} eventName The event name
17231      * @param {Object} fn The function to execute
17232      * @return {Object} The new event name/function
17233      */
17234     normalizeEvent: function(eventName, fn){
17235         if (/mouseenter|mouseleave/.test(eventName) && !Ext.supports.MouseEnterLeave) {
17236             if (fn) {
17237                 fn = Ext.Function.createInterceptor(fn, this.contains, this);
17238             }
17239             eventName = eventName == 'mouseenter' ? 'mouseover' : 'mouseout';
17240         } else if (eventName == 'mousewheel' && !Ext.supports.MouseWheel && !Ext.isOpera){
17241             eventName = 'DOMMouseScroll';
17242         }
17243         return {
17244             eventName: eventName,
17245             fn: fn
17246         };
17247     },
17248
17249     /**
17250      * Checks whether the event's relatedTarget is contained inside (or <b>is</b>) the element.
17251      * @private
17252      * @param {Object} event
17253      */
17254     contains: function(event){
17255         var parent = event.browserEvent.currentTarget,
17256             child = this.getRelatedTarget(event);
17257
17258         if (parent && parent.firstChild) {
17259             while (child) {
17260                 if (child === parent) {
17261                     return false;
17262                 }
17263                 child = child.parentNode;
17264                 if (child && (child.nodeType != 1)) {
17265                     child = null;
17266                 }
17267             }
17268         }
17269         return true;
17270     },
17271
17272     /**
17273     * Appends an event handler to an element.  The shorthand version {@link #on} is equivalent.  Typically you will
17274     * use {@link Ext.Element#addListener} directly on an Element in favor of calling this version.
17275     * @param {String/HTMLElement} el The html element or id to assign the event handler to.
17276     * @param {String} eventName The name of the event to listen for.
17277     * @param {Function} handler The handler function the event invokes. This function is passed
17278     * the following parameters:<ul>
17279     * <li>evt : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
17280     * <li>t : Element<div class="sub-desc">The {@link Ext.Element Element} which was the target of the event.
17281     * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
17282     * <li>o : Object<div class="sub-desc">The options object from the addListener call.</div></li>
17283     * </ul>
17284     * @param {Object} scope (optional) The scope (<b><code>this</code></b> reference) in which the handler function is executed. <b>Defaults to the Element</b>.
17285     * @param {Object} options (optional) An object containing handler configuration properties.
17286     * This may contain any of the following properties:<ul>
17287     * <li>scope : Object<div class="sub-desc">The scope (<b><code>this</code></b> reference) in which the handler function is executed. <b>Defaults to the Element</b>.</div></li>
17288     * <li>delegate : String<div class="sub-desc">A simple selector to filter the target or look for a descendant of the target</div></li>
17289     * <li>stopEvent : Boolean<div class="sub-desc">True to stop the event. That is stop propagation, and prevent the default action.</div></li>
17290     * <li>preventDefault : Boolean<div class="sub-desc">True to prevent the default action</div></li>
17291     * <li>stopPropagation : Boolean<div class="sub-desc">True to prevent event propagation</div></li>
17292     * <li>normalized : Boolean<div class="sub-desc">False to pass a browser event to the handler function instead of an Ext.EventObject</div></li>
17293     * <li>delay : Number<div class="sub-desc">The number of milliseconds to delay the invocation of the handler after te event fires.</div></li>
17294     * <li>single : Boolean<div class="sub-desc">True to add a handler to handle just the next firing of the event, and then remove itself.</div></li>
17295     * <li>buffer : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
17296     * by the specified number of milliseconds. If the event fires again within that time, the original
17297     * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
17298     * <li>target : Element<div class="sub-desc">Only call the handler if the event was fired on the target Element, <i>not</i> if the event was bubbled up from a child node.</div></li>
17299     * </ul><br>
17300     * <p>See {@link Ext.Element#addListener} for examples of how to use these options.</p>
17301     */
17302     addListener: function(element, eventName, fn, scope, options){
17303         // Check if we've been passed a "config style" event.
17304         if (typeof eventName !== 'string') {
17305             this.prepareListenerConfig(element, eventName);
17306             return;
17307         }
17308
17309         var dom = Ext.getDom(element),
17310             bind,
17311             wrap;
17312
17313
17314         // create the wrapper function
17315         options = options || {};
17316
17317         bind = this.normalizeEvent(eventName, fn);
17318         wrap = this.createListenerWrap(dom, eventName, bind.fn, scope, options);
17319
17320
17321         if (dom.attachEvent) {
17322             dom.attachEvent('on' + bind.eventName, wrap);
17323         } else {
17324             dom.addEventListener(bind.eventName, wrap, options.capture || false);
17325         }
17326
17327         if (dom == document && eventName == 'mousedown') {
17328             this.stoppedMouseDownEvent.addListener(wrap);
17329         }
17330
17331         // add all required data into the event cache
17332         this.getEventListenerCache(dom, eventName).push({
17333             fn: fn,
17334             wrap: wrap,
17335             scope: scope
17336         });
17337     },
17338
17339     /**
17340     * Removes an event handler from an element.  The shorthand version {@link #un} is equivalent.  Typically
17341     * you will use {@link Ext.Element#removeListener} directly on an Element in favor of calling this version.
17342     * @param {String/HTMLElement} el The id or html element from which to remove the listener.
17343     * @param {String} eventName The name of the event.
17344     * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
17345     * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
17346     * then this must refer to the same object.
17347     */
17348     removeListener : function(element, eventName, fn, scope) {
17349         // handle our listener config object syntax
17350         if (typeof eventName !== 'string') {
17351             this.prepareListenerConfig(element, eventName, true);
17352             return;
17353         }
17354
17355         var dom = Ext.getDom(element),
17356             cache = this.getEventListenerCache(dom, eventName),
17357             bindName = this.normalizeEvent(eventName).eventName,
17358             i = cache.length, j,
17359             listener, wrap, tasks;
17360
17361
17362         while (i--) {
17363             listener = cache[i];
17364
17365             if (listener && (!fn || listener.fn == fn) && (!scope || listener.scope === scope)) {
17366                 wrap = listener.wrap;
17367
17368                 // clear buffered calls
17369                 if (wrap.task) {
17370                     clearTimeout(wrap.task);
17371                     delete wrap.task;
17372                 }
17373
17374                 // clear delayed calls
17375                 j = wrap.tasks && wrap.tasks.length;
17376                 if (j) {
17377                     while (j--) {
17378                         clearTimeout(wrap.tasks[j]);
17379                     }
17380                     delete wrap.tasks;
17381                 }
17382
17383                 if (dom.detachEvent) {
17384                     dom.detachEvent('on' + bindName, wrap);
17385                 } else {
17386                     dom.removeEventListener(bindName, wrap, false);
17387                 }
17388
17389                 if (wrap && dom == document && eventName == 'mousedown') {
17390                     this.stoppedMouseDownEvent.removeListener(wrap);
17391                 }
17392
17393                 // remove listener from cache
17394                 Ext.Array.erase(cache, i, 1);
17395             }
17396         }
17397     },
17398
17399     /**
17400     * Removes all event handers from an element.  Typically you will use {@link Ext.Element#removeAllListeners}
17401     * directly on an Element in favor of calling this version.
17402     * @param {String/HTMLElement} el The id or html element from which to remove all event handlers.
17403     */
17404     removeAll : function(element){
17405         var dom = Ext.getDom(element),
17406             cache, ev;
17407         if (!dom) {
17408             return;
17409         }
17410         cache = this.getElementEventCache(dom);
17411
17412         for (ev in cache) {
17413             if (cache.hasOwnProperty(ev)) {
17414                 this.removeListener(dom, ev);
17415             }
17416         }
17417         Ext.cache[dom.id].events = {};
17418     },
17419
17420     /**
17421      * Recursively removes all previous added listeners from an element and its children. Typically you will use {@link Ext.Element#purgeAllListeners}
17422      * directly on an Element in favor of calling this version.
17423      * @param {String/HTMLElement} el The id or html element from which to remove all event handlers.
17424      * @param {String} eventName (optional) The name of the event.
17425      */
17426     purgeElement : function(element, eventName) {
17427         var dom = Ext.getDom(element),
17428             i = 0, len;
17429
17430         if(eventName) {
17431             this.removeListener(dom, eventName);
17432         }
17433         else {
17434             this.removeAll(dom);
17435         }
17436
17437         if(dom && dom.childNodes) {
17438             for(len = element.childNodes.length; i < len; i++) {
17439                 this.purgeElement(element.childNodes[i], eventName);
17440             }
17441         }
17442     },
17443
17444     /**
17445      * Create the wrapper function for the event
17446      * @private
17447      * @param {HTMLElement} dom The dom element
17448      * @param {String} ename The event name
17449      * @param {Function} fn The function to execute
17450      * @param {Object} scope The scope to execute callback in
17451      * @param {Object} options The options
17452      * @return {Function} the wrapper function
17453      */
17454     createListenerWrap : function(dom, ename, fn, scope, options) {
17455         options = options || {};
17456
17457         var f, gen;
17458
17459         return function wrap(e, args) {
17460             // Compile the implementation upon first firing
17461             if (!gen) {
17462                 f = ['if(!Ext) {return;}'];
17463
17464                 if(options.buffer || options.delay || options.freezeEvent) {
17465                     f.push('e = new Ext.EventObjectImpl(e, ' + (options.freezeEvent ? 'true' : 'false' ) + ');');
17466                 } else {
17467                     f.push('e = Ext.EventObject.setEvent(e);');
17468                 }
17469
17470                 if (options.delegate) {
17471                     f.push('var t = e.getTarget("' + options.delegate + '", this);');
17472                     f.push('if(!t) {return;}');
17473                 } else {
17474                     f.push('var t = e.target;');
17475                 }
17476
17477                 if (options.target) {
17478                     f.push('if(e.target !== options.target) {return;}');
17479                 }
17480
17481                 if(options.stopEvent) {
17482                     f.push('e.stopEvent();');
17483                 } else {
17484                     if(options.preventDefault) {
17485                         f.push('e.preventDefault();');
17486                     }
17487                     if(options.stopPropagation) {
17488                         f.push('e.stopPropagation();');
17489                     }
17490                 }
17491
17492                 if(options.normalized === false) {
17493                     f.push('e = e.browserEvent;');
17494                 }
17495
17496                 if(options.buffer) {
17497                     f.push('(wrap.task && clearTimeout(wrap.task));');
17498                     f.push('wrap.task = setTimeout(function(){');
17499                 }
17500
17501                 if(options.delay) {
17502                     f.push('wrap.tasks = wrap.tasks || [];');
17503                     f.push('wrap.tasks.push(setTimeout(function(){');
17504                 }
17505
17506                 // finally call the actual handler fn
17507                 f.push('fn.call(scope || dom, e, t, options);');
17508
17509                 if(options.single) {
17510                     f.push('Ext.EventManager.removeListener(dom, ename, fn, scope);');
17511                 }
17512
17513                 if(options.delay) {
17514                     f.push('}, ' + options.delay + '));');
17515                 }
17516
17517                 if(options.buffer) {
17518                     f.push('}, ' + options.buffer + ');');
17519                 }
17520
17521                 gen = Ext.functionFactory('e', 'options', 'fn', 'scope', 'ename', 'dom', 'wrap', 'args', f.join('\n'));
17522             }
17523
17524             gen.call(dom, e, options, fn, scope, ename, dom, wrap, args);
17525         };
17526     },
17527
17528     /**
17529      * Get the event cache for a particular element for a particular event
17530      * @private
17531      * @param {HTMLElement} element The element
17532      * @param {Object} eventName The event name
17533      * @return {Array} The events for the element
17534      */
17535     getEventListenerCache : function(element, eventName) {
17536         if (!element) {
17537             return [];
17538         }
17539
17540         var eventCache = this.getElementEventCache(element);
17541         return eventCache[eventName] || (eventCache[eventName] = []);
17542     },
17543
17544     /**
17545      * Gets the event cache for the object
17546      * @private
17547      * @param {HTMLElement} element The element
17548      * @return {Object} The event cache for the object
17549      */
17550     getElementEventCache : function(element) {
17551         if (!element) {
17552             return {};
17553         }
17554         var elementCache = Ext.cache[this.getId(element)];
17555         return elementCache.events || (elementCache.events = {});
17556     },
17557
17558     // --------------------- utility methods ---------------------
17559     mouseLeaveRe: /(mouseout|mouseleave)/,
17560     mouseEnterRe: /(mouseover|mouseenter)/,
17561
17562     /**
17563      * Stop the event (preventDefault and stopPropagation)
17564      * @param {Event} The event to stop
17565      */
17566     stopEvent: function(event) {
17567         this.stopPropagation(event);
17568         this.preventDefault(event);
17569     },
17570
17571     /**
17572      * Cancels bubbling of the event.
17573      * @param {Event} The event to stop bubbling.
17574      */
17575     stopPropagation: function(event) {
17576         event = event.browserEvent || event;
17577         if (event.stopPropagation) {
17578             event.stopPropagation();
17579         } else {
17580             event.cancelBubble = true;
17581         }
17582     },
17583
17584     /**
17585      * Prevents the browsers default handling of the event.
17586      * @param {Event} The event to prevent the default
17587      */
17588     preventDefault: function(event) {
17589         event = event.browserEvent || event;
17590         if (event.preventDefault) {
17591             event.preventDefault();
17592         } else {
17593             event.returnValue = false;
17594             // Some keys events require setting the keyCode to -1 to be prevented
17595             try {
17596               // all ctrl + X and F1 -> F12
17597               if (event.ctrlKey || event.keyCode > 111 && event.keyCode < 124) {
17598                   event.keyCode = -1;
17599               }
17600             } catch (e) {
17601                 // see this outdated document http://support.microsoft.com/kb/934364/en-us for more info
17602             }
17603         }
17604     },
17605
17606     /**
17607      * Gets the related target from the event.
17608      * @param {Object} event The event
17609      * @return {HTMLElement} The related target.
17610      */
17611     getRelatedTarget: function(event) {
17612         event = event.browserEvent || event;
17613         var target = event.relatedTarget;
17614         if (!target) {
17615             if (this.mouseLeaveRe.test(event.type)) {
17616                 target = event.toElement;
17617             } else if (this.mouseEnterRe.test(event.type)) {
17618                 target = event.fromElement;
17619             }
17620         }
17621         return this.resolveTextNode(target);
17622     },
17623
17624     /**
17625      * Gets the x coordinate from the event
17626      * @param {Object} event The event
17627      * @return {Number} The x coordinate
17628      */
17629     getPageX: function(event) {
17630         return this.getXY(event)[0];
17631     },
17632
17633     /**
17634      * Gets the y coordinate from the event
17635      * @param {Object} event The event
17636      * @return {Number} The y coordinate
17637      */
17638     getPageY: function(event) {
17639         return this.getXY(event)[1];
17640     },
17641
17642     /**
17643      * Gets the x & y coordinate from the event
17644      * @param {Object} event The event
17645      * @return {Number[]} The x/y coordinate
17646      */
17647     getPageXY: function(event) {
17648         event = event.browserEvent || event;
17649         var x = event.pageX,
17650             y = event.pageY,
17651             doc = document.documentElement,
17652             body = document.body;
17653
17654         // pageX/pageY not available (undefined, not null), use clientX/clientY instead
17655         if (!x && x !== 0) {
17656             x = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
17657             y = event.clientY + (doc && doc.scrollTop  || body && body.scrollTop  || 0) - (doc && doc.clientTop  || body && body.clientTop  || 0);
17658         }
17659         return [x, y];
17660     },
17661
17662     /**
17663      * Gets the target of the event.
17664      * @param {Object} event The event
17665      * @return {HTMLElement} target
17666      */
17667     getTarget: function(event) {
17668         event = event.browserEvent || event;
17669         return this.resolveTextNode(event.target || event.srcElement);
17670     },
17671
17672     /**
17673      * Resolve any text nodes accounting for browser differences.
17674      * @private
17675      * @param {HTMLElement} node The node
17676      * @return {HTMLElement} The resolved node
17677      */
17678     // technically no need to browser sniff this, however it makes no sense to check this every time, for every event, whether the string is equal.
17679     resolveTextNode: Ext.isGecko ?
17680         function(node) {
17681             if (!node) {
17682                 return;
17683             }
17684             // work around firefox bug, https://bugzilla.mozilla.org/show_bug.cgi?id=101197
17685             var s = HTMLElement.prototype.toString.call(node);
17686             if (s == '[xpconnect wrapped native prototype]' || s == '[object XULElement]') {
17687                 return;
17688             }
17689                 return node.nodeType == 3 ? node.parentNode: node;
17690             }: function(node) {
17691                 return node && node.nodeType == 3 ? node.parentNode: node;
17692             },
17693
17694     // --------------------- custom event binding ---------------------
17695
17696     // Keep track of the current width/height
17697     curWidth: 0,
17698     curHeight: 0,
17699
17700     /**
17701      * Adds a listener to be notified when the browser window is resized and provides resize event buffering (100 milliseconds),
17702      * passes new viewport width and height to handlers.
17703      * @param {Function} fn      The handler function the window resize event invokes.
17704      * @param {Object}   scope   The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
17705      * @param {Boolean}  options Options object as passed to {@link Ext.Element#addListener}
17706      */
17707     onWindowResize: function(fn, scope, options){
17708         var resize = this.resizeEvent;
17709         if(!resize){
17710             this.resizeEvent = resize = new Ext.util.Event();
17711             this.on(window, 'resize', this.fireResize, this, {buffer: 100});
17712         }
17713         resize.addListener(fn, scope, options);
17714     },
17715
17716     /**
17717      * Fire the resize event.
17718      * @private
17719      */
17720     fireResize: function(){
17721         var me = this,
17722             w = Ext.Element.getViewWidth(),
17723             h = Ext.Element.getViewHeight();
17724
17725          //whacky problem in IE where the resize event will sometimes fire even though the w/h are the same.
17726          if(me.curHeight != h || me.curWidth != w){
17727              me.curHeight = h;
17728              me.curWidth = w;
17729              me.resizeEvent.fire(w, h);
17730          }
17731     },
17732
17733     /**
17734      * Removes the passed window resize listener.
17735      * @param {Function} fn        The method the event invokes
17736      * @param {Object}   scope    The scope of handler
17737      */
17738     removeResizeListener: function(fn, scope){
17739         if (this.resizeEvent) {
17740             this.resizeEvent.removeListener(fn, scope);
17741         }
17742     },
17743
17744     onWindowUnload: function() {
17745         var unload = this.unloadEvent;
17746         if (!unload) {
17747             this.unloadEvent = unload = new Ext.util.Event();
17748             this.addListener(window, 'unload', this.fireUnload, this);
17749         }
17750     },
17751
17752     /**
17753      * Fires the unload event for items bound with onWindowUnload
17754      * @private
17755      */
17756     fireUnload: function() {
17757         // wrap in a try catch, could have some problems during unload
17758         try {
17759             this.removeUnloadListener();
17760             // Work around FF3 remembering the last scroll position when refreshing the grid and then losing grid view
17761             if (Ext.isGecko3) {
17762                 var gridviews = Ext.ComponentQuery.query('gridview'),
17763                     i = 0,
17764                     ln = gridviews.length;
17765                 for (; i < ln; i++) {
17766                     gridviews[i].scrollToTop();
17767                 }
17768             }
17769             // Purge all elements in the cache
17770             var el,
17771                 cache = Ext.cache;
17772             for (el in cache) {
17773                 if (cache.hasOwnProperty(el)) {
17774                     Ext.EventManager.removeAll(el);
17775                 }
17776             }
17777         } catch(e) {
17778         }
17779     },
17780
17781     /**
17782      * Removes the passed window unload listener.
17783      * @param {Function} fn        The method the event invokes
17784      * @param {Object}   scope    The scope of handler
17785      */
17786     removeUnloadListener: function(){
17787         if (this.unloadEvent) {
17788             this.removeListener(window, 'unload', this.fireUnload);
17789         }
17790     },
17791
17792     /**
17793      * note 1: IE fires ONLY the keydown event on specialkey autorepeat
17794      * note 2: Safari < 3.1, Gecko (Mac/Linux) & Opera fire only the keypress event on specialkey autorepeat
17795      * (research done by Jan Wolter at http://unixpapa.com/js/key.html)
17796      * @private
17797      */
17798     useKeyDown: Ext.isWebKit ?
17799                    parseInt(navigator.userAgent.match(/AppleWebKit\/(\d+)/)[1], 10) >= 525 :
17800                    !((Ext.isGecko && !Ext.isWindows) || Ext.isOpera),
17801
17802     /**
17803      * Indicates which event to use for getting key presses.
17804      * @return {String} The appropriate event name.
17805      */
17806     getKeyEvent: function(){
17807         return this.useKeyDown ? 'keydown' : 'keypress';
17808     }
17809 };
17810
17811 /**
17812  * Alias for {@link Ext.Loader#onReady Ext.Loader.onReady} with withDomReady set to true
17813  * @member Ext
17814  * @method onReady
17815  */
17816 Ext.onReady = function(fn, scope, options) {
17817     Ext.Loader.onReady(fn, scope, true, options);
17818 };
17819
17820 /**
17821  * Alias for {@link Ext.EventManager#onDocumentReady Ext.EventManager.onDocumentReady}
17822  * @member Ext
17823  * @method onDocumentReady
17824  */
17825 Ext.onDocumentReady = Ext.EventManager.onDocumentReady;
17826
17827 /**
17828  * Alias for {@link Ext.EventManager#addListener Ext.EventManager.addListener}
17829  * @member Ext.EventManager
17830  * @method on
17831  */
17832 Ext.EventManager.on = Ext.EventManager.addListener;
17833
17834 /**
17835  * Alias for {@link Ext.EventManager#removeListener Ext.EventManager.removeListener}
17836  * @member Ext.EventManager
17837  * @method un
17838  */
17839 Ext.EventManager.un = Ext.EventManager.removeListener;
17840
17841 (function(){
17842     var initExtCss = function() {
17843         // find the body element
17844         var bd = document.body || document.getElementsByTagName('body')[0],
17845             baseCSSPrefix = Ext.baseCSSPrefix,
17846             cls = [baseCSSPrefix + 'body'],
17847             htmlCls = [],
17848             html;
17849
17850         if (!bd) {
17851             return false;
17852         }
17853
17854         html = bd.parentNode;
17855
17856         function add (c) {
17857             cls.push(baseCSSPrefix + c);
17858         }
17859
17860         //Let's keep this human readable!
17861         if (Ext.isIE) {
17862             add('ie');
17863
17864             // very often CSS needs to do checks like "IE7+" or "IE6 or 7". To help
17865             // reduce the clutter (since CSS/SCSS cannot do these tests), we add some
17866             // additional classes:
17867             //
17868             //      x-ie7p      : IE7+      :  7 <= ieVer
17869             //      x-ie7m      : IE7-      :  ieVer <= 7
17870             //      x-ie8p      : IE8+      :  8 <= ieVer
17871             //      x-ie8m      : IE8-      :  ieVer <= 8
17872             //      x-ie9p      : IE9+      :  9 <= ieVer
17873             //      x-ie78      : IE7 or 8  :  7 <= ieVer <= 8
17874             //
17875             if (Ext.isIE6) {
17876                 add('ie6');
17877             } else { // ignore pre-IE6 :)
17878                 add('ie7p');
17879
17880                 if (Ext.isIE7) {
17881                     add('ie7');
17882                 } else {
17883                     add('ie8p');
17884
17885                     if (Ext.isIE8) {
17886                         add('ie8');
17887                     } else {
17888                         add('ie9p');
17889
17890                         if (Ext.isIE9) {
17891                             add('ie9');
17892                         }
17893                     }
17894                 }
17895             }
17896
17897             if (Ext.isIE6 || Ext.isIE7) {
17898                 add('ie7m');
17899             }
17900             if (Ext.isIE6 || Ext.isIE7 || Ext.isIE8) {
17901                 add('ie8m');
17902             }
17903             if (Ext.isIE7 || Ext.isIE8) {
17904                 add('ie78');
17905             }
17906         }
17907         if (Ext.isGecko) {
17908             add('gecko');
17909             if (Ext.isGecko3) {
17910                 add('gecko3');
17911             }
17912             if (Ext.isGecko4) {
17913                 add('gecko4');
17914             }
17915             if (Ext.isGecko5) {
17916                 add('gecko5');
17917             }
17918         }
17919         if (Ext.isOpera) {
17920             add('opera');
17921         }
17922         if (Ext.isWebKit) {
17923             add('webkit');
17924         }
17925         if (Ext.isSafari) {
17926             add('safari');
17927             if (Ext.isSafari2) {
17928                 add('safari2');
17929             }
17930             if (Ext.isSafari3) {
17931                 add('safari3');
17932             }
17933             if (Ext.isSafari4) {
17934                 add('safari4');
17935             }
17936             if (Ext.isSafari5) {
17937                 add('safari5');
17938             }
17939         }
17940         if (Ext.isChrome) {
17941             add('chrome');
17942         }
17943         if (Ext.isMac) {
17944             add('mac');
17945         }
17946         if (Ext.isLinux) {
17947             add('linux');
17948         }
17949         if (!Ext.supports.CSS3BorderRadius) {
17950             add('nbr');
17951         }
17952         if (!Ext.supports.CSS3LinearGradient) {
17953             add('nlg');
17954         }
17955         if (!Ext.scopeResetCSS) {
17956             add('reset');
17957         }
17958
17959         // add to the parent to allow for selectors x-strict x-border-box, also set the isBorderBox property correctly
17960         if (html) {
17961             if (Ext.isStrict && (Ext.isIE6 || Ext.isIE7)) {
17962                 Ext.isBorderBox = false;
17963             }
17964             else {
17965                 Ext.isBorderBox = true;
17966             }
17967
17968             htmlCls.push(baseCSSPrefix + (Ext.isBorderBox ? 'border-box' : 'strict'));
17969             if (!Ext.isStrict) {
17970                 htmlCls.push(baseCSSPrefix + 'quirks');
17971             }
17972             Ext.fly(html, '_internal').addCls(htmlCls);
17973         }
17974
17975         Ext.fly(bd, '_internal').addCls(cls);
17976         return true;
17977     };
17978
17979     Ext.onReady(initExtCss);
17980 })();
17981
17982 /**
17983  * @class Ext.EventObject
17984
17985 Just as {@link Ext.Element} wraps around a native DOM node, Ext.EventObject
17986 wraps the browser's native event-object normalizing cross-browser differences,
17987 such as which mouse button is clicked, keys pressed, mechanisms to stop
17988 event-propagation along with a method to prevent default actions from taking place.
17989
17990 For example:
17991
17992     function handleClick(e, t){ // e is not a standard event object, it is a Ext.EventObject
17993         e.preventDefault();
17994         var target = e.getTarget(); // same as t (the target HTMLElement)
17995         ...
17996     }
17997
17998     var myDiv = {@link Ext#get Ext.get}("myDiv");  // get reference to an {@link Ext.Element}
17999     myDiv.on(         // 'on' is shorthand for addListener
18000         "click",      // perform an action on click of myDiv
18001         handleClick   // reference to the action handler
18002     );
18003
18004     // other methods to do the same:
18005     Ext.EventManager.on("myDiv", 'click', handleClick);
18006     Ext.EventManager.addListener("myDiv", 'click', handleClick);
18007
18008  * @singleton
18009  * @markdown
18010  */
18011 Ext.define('Ext.EventObjectImpl', {
18012     uses: ['Ext.util.Point'],
18013
18014     /** Key constant @type Number */
18015     BACKSPACE: 8,
18016     /** Key constant @type Number */
18017     TAB: 9,
18018     /** Key constant @type Number */
18019     NUM_CENTER: 12,
18020     /** Key constant @type Number */
18021     ENTER: 13,
18022     /** Key constant @type Number */
18023     RETURN: 13,
18024     /** Key constant @type Number */
18025     SHIFT: 16,
18026     /** Key constant @type Number */
18027     CTRL: 17,
18028     /** Key constant @type Number */
18029     ALT: 18,
18030     /** Key constant @type Number */
18031     PAUSE: 19,
18032     /** Key constant @type Number */
18033     CAPS_LOCK: 20,
18034     /** Key constant @type Number */
18035     ESC: 27,
18036     /** Key constant @type Number */
18037     SPACE: 32,
18038     /** Key constant @type Number */
18039     PAGE_UP: 33,
18040     /** Key constant @type Number */
18041     PAGE_DOWN: 34,
18042     /** Key constant @type Number */
18043     END: 35,
18044     /** Key constant @type Number */
18045     HOME: 36,
18046     /** Key constant @type Number */
18047     LEFT: 37,
18048     /** Key constant @type Number */
18049     UP: 38,
18050     /** Key constant @type Number */
18051     RIGHT: 39,
18052     /** Key constant @type Number */
18053     DOWN: 40,
18054     /** Key constant @type Number */
18055     PRINT_SCREEN: 44,
18056     /** Key constant @type Number */
18057     INSERT: 45,
18058     /** Key constant @type Number */
18059     DELETE: 46,
18060     /** Key constant @type Number */
18061     ZERO: 48,
18062     /** Key constant @type Number */
18063     ONE: 49,
18064     /** Key constant @type Number */
18065     TWO: 50,
18066     /** Key constant @type Number */
18067     THREE: 51,
18068     /** Key constant @type Number */
18069     FOUR: 52,
18070     /** Key constant @type Number */
18071     FIVE: 53,
18072     /** Key constant @type Number */
18073     SIX: 54,
18074     /** Key constant @type Number */
18075     SEVEN: 55,
18076     /** Key constant @type Number */
18077     EIGHT: 56,
18078     /** Key constant @type Number */
18079     NINE: 57,
18080     /** Key constant @type Number */
18081     A: 65,
18082     /** Key constant @type Number */
18083     B: 66,
18084     /** Key constant @type Number */
18085     C: 67,
18086     /** Key constant @type Number */
18087     D: 68,
18088     /** Key constant @type Number */
18089     E: 69,
18090     /** Key constant @type Number */
18091     F: 70,
18092     /** Key constant @type Number */
18093     G: 71,
18094     /** Key constant @type Number */
18095     H: 72,
18096     /** Key constant @type Number */
18097     I: 73,
18098     /** Key constant @type Number */
18099     J: 74,
18100     /** Key constant @type Number */
18101     K: 75,
18102     /** Key constant @type Number */
18103     L: 76,
18104     /** Key constant @type Number */
18105     M: 77,
18106     /** Key constant @type Number */
18107     N: 78,
18108     /** Key constant @type Number */
18109     O: 79,
18110     /** Key constant @type Number */
18111     P: 80,
18112     /** Key constant @type Number */
18113     Q: 81,
18114     /** Key constant @type Number */
18115     R: 82,
18116     /** Key constant @type Number */
18117     S: 83,
18118     /** Key constant @type Number */
18119     T: 84,
18120     /** Key constant @type Number */
18121     U: 85,
18122     /** Key constant @type Number */
18123     V: 86,
18124     /** Key constant @type Number */
18125     W: 87,
18126     /** Key constant @type Number */
18127     X: 88,
18128     /** Key constant @type Number */
18129     Y: 89,
18130     /** Key constant @type Number */
18131     Z: 90,
18132     /** Key constant @type Number */
18133     CONTEXT_MENU: 93,
18134     /** Key constant @type Number */
18135     NUM_ZERO: 96,
18136     /** Key constant @type Number */
18137     NUM_ONE: 97,
18138     /** Key constant @type Number */
18139     NUM_TWO: 98,
18140     /** Key constant @type Number */
18141     NUM_THREE: 99,
18142     /** Key constant @type Number */
18143     NUM_FOUR: 100,
18144     /** Key constant @type Number */
18145     NUM_FIVE: 101,
18146     /** Key constant @type Number */
18147     NUM_SIX: 102,
18148     /** Key constant @type Number */
18149     NUM_SEVEN: 103,
18150     /** Key constant @type Number */
18151     NUM_EIGHT: 104,
18152     /** Key constant @type Number */
18153     NUM_NINE: 105,
18154     /** Key constant @type Number */
18155     NUM_MULTIPLY: 106,
18156     /** Key constant @type Number */
18157     NUM_PLUS: 107,
18158     /** Key constant @type Number */
18159     NUM_MINUS: 109,
18160     /** Key constant @type Number */
18161     NUM_PERIOD: 110,
18162     /** Key constant @type Number */
18163     NUM_DIVISION: 111,
18164     /** Key constant @type Number */
18165     F1: 112,
18166     /** Key constant @type Number */
18167     F2: 113,
18168     /** Key constant @type Number */
18169     F3: 114,
18170     /** Key constant @type Number */
18171     F4: 115,
18172     /** Key constant @type Number */
18173     F5: 116,
18174     /** Key constant @type Number */
18175     F6: 117,
18176     /** Key constant @type Number */
18177     F7: 118,
18178     /** Key constant @type Number */
18179     F8: 119,
18180     /** Key constant @type Number */
18181     F9: 120,
18182     /** Key constant @type Number */
18183     F10: 121,
18184     /** Key constant @type Number */
18185     F11: 122,
18186     /** Key constant @type Number */
18187     F12: 123,
18188     /**
18189      * The mouse wheel delta scaling factor. This value depends on browser version and OS and
18190      * attempts to produce a similar scrolling experience across all platforms and browsers.
18191      *
18192      * To change this value:
18193      *
18194      *      Ext.EventObjectImpl.prototype.WHEEL_SCALE = 72;
18195      *
18196      * @type Number
18197      * @markdown
18198      */
18199     WHEEL_SCALE: (function () {
18200         var scale;
18201
18202         if (Ext.isGecko) {
18203             // Firefox uses 3 on all platforms
18204             scale = 3;
18205         } else if (Ext.isMac) {
18206             // Continuous scrolling devices have momentum and produce much more scroll than
18207             // discrete devices on the same OS and browser. To make things exciting, Safari
18208             // (and not Chrome) changed from small values to 120 (like IE).
18209
18210             if (Ext.isSafari && Ext.webKitVersion >= 532.0) {
18211                 // Safari changed the scrolling factor to match IE (for details see
18212                 // https://bugs.webkit.org/show_bug.cgi?id=24368). The WebKit version where this
18213                 // change was introduced was 532.0
18214                 //      Detailed discussion:
18215                 //      https://bugs.webkit.org/show_bug.cgi?id=29601
18216                 //      http://trac.webkit.org/browser/trunk/WebKit/chromium/src/mac/WebInputEventFactory.mm#L1063
18217                 scale = 120;
18218             } else {
18219                 // MS optical wheel mouse produces multiples of 12 which is close enough
18220                 // to help tame the speed of the continuous mice...
18221                 scale = 12;
18222             }
18223
18224             // Momentum scrolling produces very fast scrolling, so increase the scale factor
18225             // to help produce similar results cross platform. This could be even larger and
18226             // it would help those mice, but other mice would become almost unusable as a
18227             // result (since we cannot tell which device type is in use).
18228             scale *= 3;
18229         } else {
18230             // IE, Opera and other Windows browsers use 120.
18231             scale = 120;
18232         }
18233
18234         return scale;
18235     })(),
18236
18237     /**
18238      * Simple click regex
18239      * @private
18240      */
18241     clickRe: /(dbl)?click/,
18242     // safari keypress events for special keys return bad keycodes
18243     safariKeys: {
18244         3: 13, // enter
18245         63234: 37, // left
18246         63235: 39, // right
18247         63232: 38, // up
18248         63233: 40, // down
18249         63276: 33, // page up
18250         63277: 34, // page down
18251         63272: 46, // delete
18252         63273: 36, // home
18253         63275: 35 // end
18254     },
18255     // normalize button clicks, don't see any way to feature detect this.
18256     btnMap: Ext.isIE ? {
18257         1: 0,
18258         4: 1,
18259         2: 2
18260     } : {
18261         0: 0,
18262         1: 1,
18263         2: 2
18264     },
18265
18266     constructor: function(event, freezeEvent){
18267         if (event) {
18268             this.setEvent(event.browserEvent || event, freezeEvent);
18269         }
18270     },
18271
18272     setEvent: function(event, freezeEvent){
18273         var me = this, button, options;
18274
18275         if (event == me || (event && event.browserEvent)) { // already wrapped
18276             return event;
18277         }
18278         me.browserEvent = event;
18279         if (event) {
18280             // normalize buttons
18281             button = event.button ? me.btnMap[event.button] : (event.which ? event.which - 1 : -1);
18282             if (me.clickRe.test(event.type) && button == -1) {
18283                 button = 0;
18284             }
18285             options = {
18286                 type: event.type,
18287                 button: button,
18288                 shiftKey: event.shiftKey,
18289                 // mac metaKey behaves like ctrlKey
18290                 ctrlKey: event.ctrlKey || event.metaKey || false,
18291                 altKey: event.altKey,
18292                 // in getKey these will be normalized for the mac
18293                 keyCode: event.keyCode,
18294                 charCode: event.charCode,
18295                 // cache the targets for the delayed and or buffered events
18296                 target: Ext.EventManager.getTarget(event),
18297                 relatedTarget: Ext.EventManager.getRelatedTarget(event),
18298                 currentTarget: event.currentTarget,
18299                 xy: (freezeEvent ? me.getXY() : null)
18300             };
18301         } else {
18302             options = {
18303                 button: -1,
18304                 shiftKey: false,
18305                 ctrlKey: false,
18306                 altKey: false,
18307                 keyCode: 0,
18308                 charCode: 0,
18309                 target: null,
18310                 xy: [0, 0]
18311             };
18312         }
18313         Ext.apply(me, options);
18314         return me;
18315     },
18316
18317     /**
18318      * Stop the event (preventDefault and stopPropagation)
18319      */
18320     stopEvent: function(){
18321         this.stopPropagation();
18322         this.preventDefault();
18323     },
18324
18325     /**
18326      * Prevents the browsers default handling of the event.
18327      */
18328     preventDefault: function(){
18329         if (this.browserEvent) {
18330             Ext.EventManager.preventDefault(this.browserEvent);
18331         }
18332     },
18333
18334     /**
18335      * Cancels bubbling of the event.
18336      */
18337     stopPropagation: function(){
18338         var browserEvent = this.browserEvent;
18339
18340         if (browserEvent) {
18341             if (browserEvent.type == 'mousedown') {
18342                 Ext.EventManager.stoppedMouseDownEvent.fire(this);
18343             }
18344             Ext.EventManager.stopPropagation(browserEvent);
18345         }
18346     },
18347
18348     /**
18349      * Gets the character code for the event.
18350      * @return {Number}
18351      */
18352     getCharCode: function(){
18353         return this.charCode || this.keyCode;
18354     },
18355
18356     /**
18357      * Returns a normalized keyCode for the event.
18358      * @return {Number} The key code
18359      */
18360     getKey: function(){
18361         return this.normalizeKey(this.keyCode || this.charCode);
18362     },
18363
18364     /**
18365      * Normalize key codes across browsers
18366      * @private
18367      * @param {Number} key The key code
18368      * @return {Number} The normalized code
18369      */
18370     normalizeKey: function(key){
18371         // can't feature detect this
18372         return Ext.isWebKit ? (this.safariKeys[key] || key) : key;
18373     },
18374
18375     /**
18376      * Gets the x coordinate of the event.
18377      * @return {Number}
18378      * @deprecated 4.0 Replaced by {@link #getX}
18379      */
18380     getPageX: function(){
18381         return this.getX();
18382     },
18383
18384     /**
18385      * Gets the y coordinate of the event.
18386      * @return {Number}
18387      * @deprecated 4.0 Replaced by {@link #getY}
18388      */
18389     getPageY: function(){
18390         return this.getY();
18391     },
18392
18393     /**
18394      * Gets the x coordinate of the event.
18395      * @return {Number}
18396      */
18397     getX: function() {
18398         return this.getXY()[0];
18399     },
18400
18401     /**
18402      * Gets the y coordinate of the event.
18403      * @return {Number}
18404      */
18405     getY: function() {
18406         return this.getXY()[1];
18407     },
18408
18409     /**
18410      * Gets the page coordinates of the event.
18411      * @return {Number[]} The xy values like [x, y]
18412      */
18413     getXY: function() {
18414         if (!this.xy) {
18415             // same for XY
18416             this.xy = Ext.EventManager.getPageXY(this.browserEvent);
18417         }
18418         return this.xy;
18419     },
18420
18421     /**
18422      * Gets the target for the event.
18423      * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
18424      * @param {Number/HTMLElement} maxDepth (optional) The max depth to search as a number or element (defaults to 10 || document.body)
18425      * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
18426      * @return {HTMLElement}
18427      */
18428     getTarget : function(selector, maxDepth, returnEl){
18429         if (selector) {
18430             return Ext.fly(this.target).findParent(selector, maxDepth, returnEl);
18431         }
18432         return returnEl ? Ext.get(this.target) : this.target;
18433     },
18434
18435     /**
18436      * Gets the related target.
18437      * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
18438      * @param {Number/HTMLElement} maxDepth (optional) The max depth to search as a number or element (defaults to 10 || document.body)
18439      * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
18440      * @return {HTMLElement}
18441      */
18442     getRelatedTarget : function(selector, maxDepth, returnEl){
18443         if (selector) {
18444             return Ext.fly(this.relatedTarget).findParent(selector, maxDepth, returnEl);
18445         }
18446         return returnEl ? Ext.get(this.relatedTarget) : this.relatedTarget;
18447     },
18448
18449     /**
18450      * Correctly scales a given wheel delta.
18451      * @param {Number} delta The delta value.
18452      */
18453     correctWheelDelta : function (delta) {
18454         var scale = this.WHEEL_SCALE,
18455             ret = Math.round(delta / scale);
18456
18457         if (!ret && delta) {
18458             ret = (delta < 0) ? -1 : 1; // don't allow non-zero deltas to go to zero!
18459         }
18460
18461         return ret;
18462     },
18463
18464     /**
18465      * Returns the mouse wheel deltas for this event.
18466      * @return {Object} An object with "x" and "y" properties holding the mouse wheel deltas.
18467      */
18468     getWheelDeltas : function () {
18469         var me = this,
18470             event = me.browserEvent,
18471             dx = 0, dy = 0; // the deltas
18472
18473         if (Ext.isDefined(event.wheelDeltaX)) { // WebKit has both dimensions
18474             dx = event.wheelDeltaX;
18475             dy = event.wheelDeltaY;
18476         } else if (event.wheelDelta) { // old WebKit and IE
18477             dy = event.wheelDelta;
18478         } else if (event.detail) { // Gecko
18479             dy = -event.detail; // gecko is backwards
18480
18481             // Gecko sometimes returns really big values if the user changes settings to
18482             // scroll a whole page per scroll
18483             if (dy > 100) {
18484                 dy = 3;
18485             } else if (dy < -100) {
18486                 dy = -3;
18487             }
18488
18489             // Firefox 3.1 adds an axis field to the event to indicate direction of
18490             // scroll.  See https://developer.mozilla.org/en/Gecko-Specific_DOM_Events
18491             if (Ext.isDefined(event.axis) && event.axis === event.HORIZONTAL_AXIS) {
18492                 dx = dy;
18493                 dy = 0;
18494             }
18495         }
18496
18497         return {
18498             x: me.correctWheelDelta(dx),
18499             y: me.correctWheelDelta(dy)
18500         };
18501     },
18502
18503     /**
18504      * Normalizes mouse wheel y-delta across browsers. To get x-delta information, use
18505      * {@link #getWheelDeltas} instead.
18506      * @return {Number} The mouse wheel y-delta
18507      */
18508     getWheelDelta : function(){
18509         var deltas = this.getWheelDeltas();
18510
18511         return deltas.y;
18512     },
18513
18514     /**
18515      * Returns true if the target of this event is a child of el.  Unless the allowEl parameter is set, it will return false if if the target is el.
18516      * Example usage:<pre><code>
18517 // Handle click on any child of an element
18518 Ext.getBody().on('click', function(e){
18519     if(e.within('some-el')){
18520         alert('Clicked on a child of some-el!');
18521     }
18522 });
18523
18524 // Handle click directly on an element, ignoring clicks on child nodes
18525 Ext.getBody().on('click', function(e,t){
18526     if((t.id == 'some-el') && !e.within(t, true)){
18527         alert('Clicked directly on some-el!');
18528     }
18529 });
18530 </code></pre>
18531      * @param {String/HTMLElement/Ext.Element} el The id, DOM element or Ext.Element to check
18532      * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
18533      * @param {Boolean} allowEl (optional) true to also check if the passed element is the target or related target
18534      * @return {Boolean}
18535      */
18536     within : function(el, related, allowEl){
18537         if(el){
18538             var t = related ? this.getRelatedTarget() : this.getTarget(),
18539                 result;
18540
18541             if (t) {
18542                 result = Ext.fly(el).contains(t);
18543                 if (!result && allowEl) {
18544                     result = t == Ext.getDom(el);
18545                 }
18546                 return result;
18547             }
18548         }
18549         return false;
18550     },
18551
18552     /**
18553      * Checks if the key pressed was a "navigation" key
18554      * @return {Boolean} True if the press is a navigation keypress
18555      */
18556     isNavKeyPress : function(){
18557         var me = this,
18558             k = this.normalizeKey(me.keyCode);
18559
18560        return (k >= 33 && k <= 40) ||  // Page Up/Down, End, Home, Left, Up, Right, Down
18561        k == me.RETURN ||
18562        k == me.TAB ||
18563        k == me.ESC;
18564     },
18565
18566     /**
18567      * Checks if the key pressed was a "special" key
18568      * @return {Boolean} True if the press is a special keypress
18569      */
18570     isSpecialKey : function(){
18571         var k = this.normalizeKey(this.keyCode);
18572         return (this.type == 'keypress' && this.ctrlKey) ||
18573         this.isNavKeyPress() ||
18574         (k == this.BACKSPACE) || // Backspace
18575         (k >= 16 && k <= 20) || // Shift, Ctrl, Alt, Pause, Caps Lock
18576         (k >= 44 && k <= 46);   // Print Screen, Insert, Delete
18577     },
18578
18579     /**
18580      * Returns a point object that consists of the object coordinates.
18581      * @return {Ext.util.Point} point
18582      */
18583     getPoint : function(){
18584         var xy = this.getXY();
18585         return Ext.create('Ext.util.Point', xy[0], xy[1]);
18586     },
18587
18588    /**
18589     * Returns true if the control, meta, shift or alt key was pressed during this event.
18590     * @return {Boolean}
18591     */
18592     hasModifier : function(){
18593         return this.ctrlKey || this.altKey || this.shiftKey || this.metaKey;
18594     },
18595
18596     /**
18597      * Injects a DOM event using the data in this object and (optionally) a new target.
18598      * This is a low-level technique and not likely to be used by application code. The
18599      * currently supported event types are:
18600      * <p><b>HTMLEvents</b></p>
18601      * <ul>
18602      * <li>load</li>
18603      * <li>unload</li>
18604      * <li>select</li>
18605      * <li>change</li>
18606      * <li>submit</li>
18607      * <li>reset</li>
18608      * <li>resize</li>
18609      * <li>scroll</li>
18610      * </ul>
18611      * <p><b>MouseEvents</b></p>
18612      * <ul>
18613      * <li>click</li>
18614      * <li>dblclick</li>
18615      * <li>mousedown</li>
18616      * <li>mouseup</li>
18617      * <li>mouseover</li>
18618      * <li>mousemove</li>
18619      * <li>mouseout</li>
18620      * </ul>
18621      * <p><b>UIEvents</b></p>
18622      * <ul>
18623      * <li>focusin</li>
18624      * <li>focusout</li>
18625      * <li>activate</li>
18626      * <li>focus</li>
18627      * <li>blur</li>
18628      * </ul>
18629      * @param {Ext.Element/HTMLElement} target (optional) If specified, the target for the event. This
18630      * is likely to be used when relaying a DOM event. If not specified, {@link #getTarget}
18631      * is used to determine the target.
18632      */
18633     injectEvent: function () {
18634         var API,
18635             dispatchers = {}; // keyed by event type (e.g., 'mousedown')
18636
18637         // Good reference: http://developer.yahoo.com/yui/docs/UserAction.js.html
18638
18639         // IE9 has createEvent, but this code causes major problems with htmleditor (it
18640         // blocks all mouse events and maybe more). TODO
18641
18642         if (!Ext.isIE && document.createEvent) { // if (DOM compliant)
18643             API = {
18644                 createHtmlEvent: function (doc, type, bubbles, cancelable) {
18645                     var event = doc.createEvent('HTMLEvents');
18646
18647                     event.initEvent(type, bubbles, cancelable);
18648                     return event;
18649                 },
18650
18651                 createMouseEvent: function (doc, type, bubbles, cancelable, detail,
18652                                             clientX, clientY, ctrlKey, altKey, shiftKey, metaKey,
18653                                             button, relatedTarget) {
18654                     var event = doc.createEvent('MouseEvents'),
18655                         view = doc.defaultView || window;
18656
18657                     if (event.initMouseEvent) {
18658                         event.initMouseEvent(type, bubbles, cancelable, view, detail,
18659                                     clientX, clientY, clientX, clientY, ctrlKey, altKey,
18660                                     shiftKey, metaKey, button, relatedTarget);
18661                     } else { // old Safari
18662                         event = doc.createEvent('UIEvents');
18663                         event.initEvent(type, bubbles, cancelable);
18664                         event.view = view;
18665                         event.detail = detail;
18666                         event.screenX = clientX;
18667                         event.screenY = clientY;
18668                         event.clientX = clientX;
18669                         event.clientY = clientY;
18670                         event.ctrlKey = ctrlKey;
18671                         event.altKey = altKey;
18672                         event.metaKey = metaKey;
18673                         event.shiftKey = shiftKey;
18674                         event.button = button;
18675                         event.relatedTarget = relatedTarget;
18676                     }
18677
18678                     return event;
18679                 },
18680
18681                 createUIEvent: function (doc, type, bubbles, cancelable, detail) {
18682                     var event = doc.createEvent('UIEvents'),
18683                         view = doc.defaultView || window;
18684
18685                     event.initUIEvent(type, bubbles, cancelable, view, detail);
18686                     return event;
18687                 },
18688
18689                 fireEvent: function (target, type, event) {
18690                     target.dispatchEvent(event);
18691                 },
18692
18693                 fixTarget: function (target) {
18694                     // Safari3 doesn't have window.dispatchEvent()
18695                     if (target == window && !target.dispatchEvent) {
18696                         return document;
18697                     }
18698
18699                     return target;
18700                 }
18701             };
18702         } else if (document.createEventObject) { // else if (IE)
18703             var crazyIEButtons = { 0: 1, 1: 4, 2: 2 };
18704
18705             API = {
18706                 createHtmlEvent: function (doc, type, bubbles, cancelable) {
18707                     var event = doc.createEventObject();
18708                     event.bubbles = bubbles;
18709                     event.cancelable = cancelable;
18710                     return event;
18711                 },
18712
18713                 createMouseEvent: function (doc, type, bubbles, cancelable, detail,
18714                                             clientX, clientY, ctrlKey, altKey, shiftKey, metaKey,
18715                                             button, relatedTarget) {
18716                     var event = doc.createEventObject();
18717                     event.bubbles = bubbles;
18718                     event.cancelable = cancelable;
18719                     event.detail = detail;
18720                     event.screenX = clientX;
18721                     event.screenY = clientY;
18722                     event.clientX = clientX;
18723                     event.clientY = clientY;
18724                     event.ctrlKey = ctrlKey;
18725                     event.altKey = altKey;
18726                     event.shiftKey = shiftKey;
18727                     event.metaKey = metaKey;
18728                     event.button = crazyIEButtons[button] || button;
18729                     event.relatedTarget = relatedTarget; // cannot assign to/fromElement
18730                     return event;
18731                 },
18732
18733                 createUIEvent: function (doc, type, bubbles, cancelable, detail) {
18734                     var event = doc.createEventObject();
18735                     event.bubbles = bubbles;
18736                     event.cancelable = cancelable;
18737                     return event;
18738                 },
18739
18740                 fireEvent: function (target, type, event) {
18741                     target.fireEvent('on' + type, event);
18742                 },
18743
18744                 fixTarget: function (target) {
18745                     if (target == document) {
18746                         // IE6,IE7 thinks window==document and doesn't have window.fireEvent()
18747                         // IE6,IE7 cannot properly call document.fireEvent()
18748                         return document.documentElement;
18749                     }
18750
18751                     return target;
18752                 }
18753             };
18754         }
18755
18756         //----------------
18757         // HTMLEvents
18758
18759         Ext.Object.each({
18760                 load:   [false, false],
18761                 unload: [false, false],
18762                 select: [true, false],
18763                 change: [true, false],
18764                 submit: [true, true],
18765                 reset:  [true, false],
18766                 resize: [true, false],
18767                 scroll: [true, false]
18768             },
18769             function (name, value) {
18770                 var bubbles = value[0], cancelable = value[1];
18771                 dispatchers[name] = function (targetEl, srcEvent) {
18772                     var e = API.createHtmlEvent(name, bubbles, cancelable);
18773                     API.fireEvent(targetEl, name, e);
18774                 };
18775             });
18776
18777         //----------------
18778         // MouseEvents
18779
18780         function createMouseEventDispatcher (type, detail) {
18781             var cancelable = (type != 'mousemove');
18782             return function (targetEl, srcEvent) {
18783                 var xy = srcEvent.getXY(),
18784                     e = API.createMouseEvent(targetEl.ownerDocument, type, true, cancelable,
18785                                 detail, xy[0], xy[1], srcEvent.ctrlKey, srcEvent.altKey,
18786                                 srcEvent.shiftKey, srcEvent.metaKey, srcEvent.button,
18787                                 srcEvent.relatedTarget);
18788                 API.fireEvent(targetEl, type, e);
18789             };
18790         }
18791
18792         Ext.each(['click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mousemove', 'mouseout'],
18793             function (eventName) {
18794                 dispatchers[eventName] = createMouseEventDispatcher(eventName, 1);
18795             });
18796
18797         //----------------
18798         // UIEvents
18799
18800         Ext.Object.each({
18801                 focusin:  [true, false],
18802                 focusout: [true, false],
18803                 activate: [true, true],
18804                 focus:    [false, false],
18805                 blur:     [false, false]
18806             },
18807             function (name, value) {
18808                 var bubbles = value[0], cancelable = value[1];
18809                 dispatchers[name] = function (targetEl, srcEvent) {
18810                     var e = API.createUIEvent(targetEl.ownerDocument, name, bubbles, cancelable, 1);
18811                     API.fireEvent(targetEl, name, e);
18812                 };
18813             });
18814
18815         //---------
18816         if (!API) {
18817             // not even sure what ancient browsers fall into this category...
18818
18819             dispatchers = {}; // never mind all those we just built :P
18820
18821             API = {
18822                 fixTarget: function (t) {
18823                     return t;
18824                 }
18825             };
18826         }
18827
18828         function cannotInject (target, srcEvent) {
18829         }
18830
18831         return function (target) {
18832             var me = this,
18833                 dispatcher = dispatchers[me.type] || cannotInject,
18834                 t = target ? (target.dom || target) : me.getTarget();
18835
18836             t = API.fixTarget(t);
18837             dispatcher(t, me);
18838         };
18839     }() // call to produce method
18840
18841 }, function() {
18842
18843 Ext.EventObject = new Ext.EventObjectImpl();
18844
18845 });
18846
18847
18848 /**
18849  * @class Ext.Element
18850  */
18851 (function(){
18852     var doc = document,
18853         activeElement = null,
18854         isCSS1 = doc.compatMode == "CSS1Compat",
18855         ELEMENT = Ext.Element,
18856         fly = function(el){
18857             if (!_fly) {
18858                 _fly = new Ext.Element.Flyweight();
18859             }
18860             _fly.dom = el;
18861             return _fly;
18862         }, _fly;
18863
18864     // If the browser does not support document.activeElement we need some assistance.
18865     // This covers old Safari 3.2 (4.0 added activeElement along with just about all
18866     // other browsers). We need this support to handle issues with old Safari.
18867     if (!('activeElement' in doc) && doc.addEventListener) {
18868         doc.addEventListener('focus',
18869             function (ev) {
18870                 if (ev && ev.target) {
18871                     activeElement = (ev.target == doc) ? null : ev.target;
18872                 }
18873             }, true);
18874     }
18875
18876     /*
18877      * Helper function to create the function that will restore the selection.
18878      */
18879     function makeSelectionRestoreFn (activeEl, start, end) {
18880         return function () {
18881             activeEl.selectionStart = start;
18882             activeEl.selectionEnd = end;
18883         };
18884     }
18885
18886     Ext.apply(ELEMENT, {
18887         isAncestor : function(p, c) {
18888             var ret = false;
18889
18890             p = Ext.getDom(p);
18891             c = Ext.getDom(c);
18892             if (p && c) {
18893                 if (p.contains) {
18894                     return p.contains(c);
18895                 } else if (p.compareDocumentPosition) {
18896                     return !!(p.compareDocumentPosition(c) & 16);
18897                 } else {
18898                     while ((c = c.parentNode)) {
18899                         ret = c == p || ret;
18900                     }
18901                 }
18902             }
18903             return ret;
18904         },
18905
18906         /**
18907          * Returns the active element in the DOM. If the browser supports activeElement
18908          * on the document, this is returned. If not, the focus is tracked and the active
18909          * element is maintained internally.
18910          * @return {HTMLElement} The active (focused) element in the document.
18911          */
18912         getActiveElement: function () {
18913             return doc.activeElement || activeElement;
18914         },
18915
18916         /**
18917          * Creates a function to call to clean up problems with the work-around for the
18918          * WebKit RightMargin bug. The work-around is to add "display: 'inline-block'" to
18919          * the element before calling getComputedStyle and then to restore its original
18920          * display value. The problem with this is that it corrupts the selection of an
18921          * INPUT or TEXTAREA element (as in the "I-beam" goes away but ths focus remains).
18922          * To cleanup after this, we need to capture the selection of any such element and
18923          * then restore it after we have restored the display style.
18924          *
18925          * @param target {Element} The top-most element being adjusted.
18926          * @private
18927          */
18928         getRightMarginFixCleaner: function (target) {
18929             var supports = Ext.supports,
18930                 hasInputBug = supports.DisplayChangeInputSelectionBug,
18931                 hasTextAreaBug = supports.DisplayChangeTextAreaSelectionBug;
18932
18933             if (hasInputBug || hasTextAreaBug) {
18934                 var activeEl = doc.activeElement || activeElement, // save a call
18935                     tag = activeEl && activeEl.tagName,
18936                     start,
18937                     end;
18938
18939                 if ((hasTextAreaBug && tag == 'TEXTAREA') ||
18940                     (hasInputBug && tag == 'INPUT' && activeEl.type == 'text')) {
18941                     if (ELEMENT.isAncestor(target, activeEl)) {
18942                         start = activeEl.selectionStart;
18943                         end = activeEl.selectionEnd;
18944
18945                         if (Ext.isNumber(start) && Ext.isNumber(end)) { // to be safe...
18946                             // We don't create the raw closure here inline because that
18947                             // will be costly even if we don't want to return it (nested
18948                             // function decls and exprs are often instantiated on entry
18949                             // regardless of whether execution ever reaches them):
18950                             return makeSelectionRestoreFn(activeEl, start, end);
18951                         }
18952                     }
18953                 }
18954             }
18955
18956             return Ext.emptyFn; // avoid special cases, just return a nop
18957         },
18958
18959         getViewWidth : function(full) {
18960             return full ? ELEMENT.getDocumentWidth() : ELEMENT.getViewportWidth();
18961         },
18962
18963         getViewHeight : function(full) {
18964             return full ? ELEMENT.getDocumentHeight() : ELEMENT.getViewportHeight();
18965         },
18966
18967         getDocumentHeight: function() {
18968             return Math.max(!isCSS1 ? doc.body.scrollHeight : doc.documentElement.scrollHeight, ELEMENT.getViewportHeight());
18969         },
18970
18971         getDocumentWidth: function() {
18972             return Math.max(!isCSS1 ? doc.body.scrollWidth : doc.documentElement.scrollWidth, ELEMENT.getViewportWidth());
18973         },
18974
18975         getViewportHeight: function(){
18976             return Ext.isIE ?
18977                    (Ext.isStrict ? doc.documentElement.clientHeight : doc.body.clientHeight) :
18978                    self.innerHeight;
18979         },
18980
18981         getViewportWidth : function() {
18982             return (!Ext.isStrict && !Ext.isOpera) ? doc.body.clientWidth :
18983                    Ext.isIE ? doc.documentElement.clientWidth : self.innerWidth;
18984         },
18985
18986         getY : function(el) {
18987             return ELEMENT.getXY(el)[1];
18988         },
18989
18990         getX : function(el) {
18991             return ELEMENT.getXY(el)[0];
18992         },
18993
18994         getOffsetParent: function (el) {
18995             el = Ext.getDom(el);
18996             try {
18997                 // accessing offsetParent can throw "Unspecified Error" in IE6-8 (not 9)
18998                 return el.offsetParent;
18999             } catch (e) {
19000                 var body = document.body; // safe bet, unless...
19001                 return (el == body) ? null : body;
19002             }
19003         },
19004
19005         getXY : function(el) {
19006             var p,
19007                 pe,
19008                 b,
19009                 bt,
19010                 bl,
19011                 dbd,
19012                 x = 0,
19013                 y = 0,
19014                 scroll,
19015                 hasAbsolute,
19016                 bd = (doc.body || doc.documentElement),
19017                 ret;
19018
19019             el = Ext.getDom(el);
19020
19021             if(el != bd){
19022                 hasAbsolute = fly(el).isStyle("position", "absolute");
19023
19024                 if (el.getBoundingClientRect) {
19025                     try {
19026                         b = el.getBoundingClientRect();
19027                         scroll = fly(document).getScroll();
19028                         ret = [ Math.round(b.left + scroll.left), Math.round(b.top + scroll.top) ];
19029                     } catch (e) {
19030                         // IE6-8 can also throw from getBoundingClientRect...
19031                     }
19032                 }
19033
19034                 if (!ret) {
19035                     for (p = el; p; p = ELEMENT.getOffsetParent(p)) {
19036                         pe = fly(p);
19037                         x += p.offsetLeft;
19038                         y += p.offsetTop;
19039
19040                         hasAbsolute = hasAbsolute || pe.isStyle("position", "absolute");
19041
19042                         if (Ext.isGecko) {
19043                             y += bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
19044                             x += bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
19045
19046                             if (p != el && !pe.isStyle('overflow','visible')) {
19047                                 x += bl;
19048                                 y += bt;
19049                             }
19050                         }
19051                     }
19052
19053                     if (Ext.isSafari && hasAbsolute) {
19054                         x -= bd.offsetLeft;
19055                         y -= bd.offsetTop;
19056                     }
19057
19058                     if (Ext.isGecko && !hasAbsolute) {
19059                         dbd = fly(bd);
19060                         x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
19061                         y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
19062                     }
19063
19064                     p = el.parentNode;
19065                     while (p && p != bd) {
19066                         if (!Ext.isOpera || (p.tagName != 'TR' && !fly(p).isStyle("display", "inline"))) {
19067                             x -= p.scrollLeft;
19068                             y -= p.scrollTop;
19069                         }
19070                         p = p.parentNode;
19071                     }
19072                     ret = [x,y];
19073                 }
19074             }
19075             return ret || [0,0];
19076         },
19077
19078         setXY : function(el, xy) {
19079             (el = Ext.fly(el, '_setXY')).position();
19080
19081             var pts = el.translatePoints(xy),
19082                 style = el.dom.style,
19083                 pos;
19084
19085             for (pos in pts) {
19086                 if (!isNaN(pts[pos])) {
19087                     style[pos] = pts[pos] + "px";
19088                 }
19089             }
19090         },
19091
19092         setX : function(el, x) {
19093             ELEMENT.setXY(el, [x, false]);
19094         },
19095
19096         setY : function(el, y) {
19097             ELEMENT.setXY(el, [false, y]);
19098         },
19099
19100         /**
19101          * Serializes a DOM form into a url encoded string
19102          * @param {Object} form The form
19103          * @return {String} The url encoded form
19104          */
19105         serializeForm: function(form) {
19106             var fElements = form.elements || (document.forms[form] || Ext.getDom(form)).elements,
19107                 hasSubmit = false,
19108                 encoder = encodeURIComponent,
19109                 name,
19110                 data = '',
19111                 type,
19112                 hasValue;
19113
19114             Ext.each(fElements, function(element){
19115                 name = element.name;
19116                 type = element.type;
19117
19118                 if (!element.disabled && name) {
19119                     if (/select-(one|multiple)/i.test(type)) {
19120                         Ext.each(element.options, function(opt){
19121                             if (opt.selected) {
19122                                 hasValue = opt.hasAttribute ? opt.hasAttribute('value') : opt.getAttributeNode('value').specified;
19123                                 data += Ext.String.format("{0}={1}&", encoder(name), encoder(hasValue ? opt.value : opt.text));
19124                             }
19125                         });
19126                     } else if (!(/file|undefined|reset|button/i.test(type))) {
19127                         if (!(/radio|checkbox/i.test(type) && !element.checked) && !(type == 'submit' && hasSubmit)) {
19128                             data += encoder(name) + '=' + encoder(element.value) + '&';
19129                             hasSubmit = /submit/i.test(type);
19130                         }
19131                     }
19132                 }
19133             });
19134             return data.substr(0, data.length - 1);
19135         }
19136     });
19137 })();
19138
19139 /**
19140  * @class Ext.Element
19141  */
19142
19143 Ext.Element.addMethods((function(){
19144     var focusRe = /button|input|textarea|select|object/;
19145     return {
19146         /**
19147          * Monitors this Element for the mouse leaving. Calls the function after the specified delay only if
19148          * the mouse was not moved back into the Element within the delay. If the mouse <i>was</i> moved
19149          * back in, the function is not called.
19150          * @param {Number} delay The delay <b>in milliseconds</b> to wait for possible mouse re-entry before calling the handler function.
19151          * @param {Function} handler The function to call if the mouse remains outside of this Element for the specified time.
19152          * @param {Object} scope The scope (<code>this</code> reference) in which the handler function executes. Defaults to this Element.
19153          * @return {Object} The listeners object which was added to this element so that monitoring can be stopped. Example usage:<pre><code>
19154 // Hide the menu if the mouse moves out for 250ms or more
19155 this.mouseLeaveMonitor = this.menuEl.monitorMouseLeave(250, this.hideMenu, this);
19156
19157 ...
19158 // Remove mouseleave monitor on menu destroy
19159 this.menuEl.un(this.mouseLeaveMonitor);
19160     </code></pre>
19161          */
19162         monitorMouseLeave: function(delay, handler, scope) {
19163             var me = this,
19164                 timer,
19165                 listeners = {
19166                     mouseleave: function(e) {
19167                         timer = setTimeout(Ext.Function.bind(handler, scope||me, [e]), delay);
19168                     },
19169                     mouseenter: function() {
19170                         clearTimeout(timer);
19171                     },
19172                     freezeEvent: true
19173                 };
19174
19175             me.on(listeners);
19176             return listeners;
19177         },
19178
19179         /**
19180          * Stops the specified event(s) from bubbling and optionally prevents the default action
19181          * @param {String/String[]} eventName an event / array of events to stop from bubbling
19182          * @param {Boolean} preventDefault (optional) true to prevent the default action too
19183          * @return {Ext.Element} this
19184          */
19185         swallowEvent : function(eventName, preventDefault) {
19186             var me = this;
19187             function fn(e) {
19188                 e.stopPropagation();
19189                 if (preventDefault) {
19190                     e.preventDefault();
19191                 }
19192             }
19193
19194             if (Ext.isArray(eventName)) {
19195                 Ext.each(eventName, function(e) {
19196                      me.on(e, fn);
19197                 });
19198                 return me;
19199             }
19200             me.on(eventName, fn);
19201             return me;
19202         },
19203
19204         /**
19205          * Create an event handler on this element such that when the event fires and is handled by this element,
19206          * it will be relayed to another object (i.e., fired again as if it originated from that object instead).
19207          * @param {String} eventName The type of event to relay
19208          * @param {Object} object Any object that extends {@link Ext.util.Observable} that will provide the context
19209          * for firing the relayed event
19210          */
19211         relayEvent : function(eventName, observable) {
19212             this.on(eventName, function(e) {
19213                 observable.fireEvent(eventName, e);
19214             });
19215         },
19216
19217         /**
19218          * Removes Empty, or whitespace filled text nodes. Combines adjacent text nodes.
19219          * @param {Boolean} forceReclean (optional) By default the element
19220          * keeps track if it has been cleaned already so
19221          * you can call this over and over. However, if you update the element and
19222          * need to force a reclean, you can pass true.
19223          */
19224         clean : function(forceReclean) {
19225             var me  = this,
19226                 dom = me.dom,
19227                 n   = dom.firstChild,
19228                 nx,
19229                 ni  = -1;
19230     
19231             if (Ext.Element.data(dom, 'isCleaned') && forceReclean !== true) {
19232                 return me;
19233             }
19234
19235             while (n) {
19236                 nx = n.nextSibling;
19237                 if (n.nodeType == 3) {
19238                     // Remove empty/whitespace text nodes
19239                     if (!(/\S/.test(n.nodeValue))) {
19240                         dom.removeChild(n);
19241                     // Combine adjacent text nodes
19242                     } else if (nx && nx.nodeType == 3) {
19243                         n.appendData(Ext.String.trim(nx.data));
19244                         dom.removeChild(nx);
19245                         nx = n.nextSibling;
19246                         n.nodeIndex = ++ni;
19247                     }
19248                 } else {
19249                     // Recursively clean
19250                     Ext.fly(n).clean();
19251                     n.nodeIndex = ++ni;
19252                 }
19253                 n = nx;
19254             }
19255
19256             Ext.Element.data(dom, 'isCleaned', true);
19257             return me;
19258         },
19259
19260         /**
19261          * Direct access to the Ext.ElementLoader {@link Ext.ElementLoader#load} method. The method takes the same object
19262          * parameter as {@link Ext.ElementLoader#load}
19263          * @return {Ext.Element} this
19264          */
19265         load : function(options) {
19266             this.getLoader().load(options);
19267             return this;
19268         },
19269
19270         /**
19271         * Gets this element's {@link Ext.ElementLoader ElementLoader}
19272         * @return {Ext.ElementLoader} The loader
19273         */
19274         getLoader : function() {
19275             var dom = this.dom,
19276                 data = Ext.Element.data,
19277                 loader = data(dom, 'loader');
19278     
19279             if (!loader) {
19280                 loader = Ext.create('Ext.ElementLoader', {
19281                     target: this
19282                 });
19283                 data(dom, 'loader', loader);
19284             }
19285             return loader;
19286         },
19287
19288         /**
19289         * Update the innerHTML of this element, optionally searching for and processing scripts
19290         * @param {String} html The new HTML
19291         * @param {Boolean} [loadScripts=false] True to look for and process scripts
19292         * @param {Function} [callback] For async script loading you can be notified when the update completes
19293         * @return {Ext.Element} this
19294          */
19295         update : function(html, loadScripts, callback) {
19296             var me = this,
19297                 id,
19298                 dom,
19299                 interval;
19300
19301             if (!me.dom) {
19302                 return me;
19303             }
19304             html = html || '';
19305             dom = me.dom;
19306
19307             if (loadScripts !== true) {
19308                 dom.innerHTML = html;
19309                 Ext.callback(callback, me);
19310                 return me;
19311             }
19312
19313             id  = Ext.id();
19314             html += '<span id="' + id + '"></span>';
19315
19316             interval = setInterval(function(){
19317                 if (!document.getElementById(id)) {
19318                     return false;
19319                 }
19320                 clearInterval(interval);
19321                 var DOC    = document,
19322                     hd     = DOC.getElementsByTagName("head")[0],
19323                     re     = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig,
19324                     srcRe  = /\ssrc=([\'\"])(.*?)\1/i,
19325                     typeRe = /\stype=([\'\"])(.*?)\1/i,
19326                     match,
19327                     attrs,
19328                     srcMatch,
19329                     typeMatch,
19330                     el,
19331                     s;
19332
19333                 while ((match = re.exec(html))) {
19334                     attrs = match[1];
19335                     srcMatch = attrs ? attrs.match(srcRe) : false;
19336                     if (srcMatch && srcMatch[2]) {
19337                        s = DOC.createElement("script");
19338                        s.src = srcMatch[2];
19339                        typeMatch = attrs.match(typeRe);
19340                        if (typeMatch && typeMatch[2]) {
19341                            s.type = typeMatch[2];
19342                        }
19343                        hd.appendChild(s);
19344                     } else if (match[2] && match[2].length > 0) {
19345                         if (window.execScript) {
19346                            window.execScript(match[2]);
19347                         } else {
19348                            window.eval(match[2]);
19349                         }
19350                     }
19351                 }
19352
19353                 el = DOC.getElementById(id);
19354                 if (el) {
19355                     Ext.removeNode(el);
19356                 }
19357                 Ext.callback(callback, me);
19358             }, 20);
19359             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, '');
19360             return me;
19361         },
19362
19363         // inherit docs, overridden so we can add removeAnchor
19364         removeAllListeners : function() {
19365             this.removeAnchor();
19366             Ext.EventManager.removeAll(this.dom);
19367             return this;
19368         },
19369     
19370         /**
19371          * Gets the parent node of the current element taking into account Ext.scopeResetCSS
19372          * @protected
19373          * @return {HTMLElement} The parent element
19374          */
19375         getScopeParent: function(){
19376             var parent = this.dom.parentNode;
19377             return Ext.scopeResetCSS ? parent.parentNode : parent;
19378         },
19379
19380         /**
19381          * Creates a proxy element of this element
19382          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
19383          * @param {String/HTMLElement} [renderTo] The element or element id to render the proxy to (defaults to document.body)
19384          * @param {Boolean} [matchBox=false] True to align and size the proxy to this element now.
19385          * @return {Ext.Element} The new proxy element
19386          */
19387         createProxy : function(config, renderTo, matchBox) {
19388             config = (typeof config == 'object') ? config : {tag : "div", cls: config};
19389
19390             var me = this,
19391                 proxy = renderTo ? Ext.DomHelper.append(renderTo, config, true) :
19392                                    Ext.DomHelper.insertBefore(me.dom, config, true);
19393
19394             proxy.setVisibilityMode(Ext.Element.DISPLAY);
19395             proxy.hide();
19396             if (matchBox && me.setBox && me.getBox) { // check to make sure Element.position.js is loaded
19397                proxy.setBox(me.getBox());
19398             }
19399             return proxy;
19400         },
19401     
19402         /**
19403          * Checks whether this element can be focused.
19404          * @return {Boolean} True if the element is focusable
19405          */
19406         focusable: function(){
19407             var dom = this.dom,
19408                 nodeName = dom.nodeName.toLowerCase(),
19409                 canFocus = false,
19410                 hasTabIndex = !isNaN(dom.tabIndex);
19411             
19412             if (!dom.disabled) {
19413                 if (focusRe.test(nodeName)) {
19414                     canFocus = true;
19415                 } else {
19416                     canFocus = nodeName == 'a' ? dom.href || hasTabIndex : hasTabIndex;
19417                 }
19418             }
19419             return canFocus && this.isVisible(true);
19420         }    
19421     };
19422 })());
19423 Ext.Element.prototype.clearListeners = Ext.Element.prototype.removeAllListeners;
19424
19425 /**
19426  * @class Ext.Element
19427  */
19428 Ext.Element.addMethods({
19429     /**
19430      * Gets the x,y coordinates specified by the anchor position on the element.
19431      * @param {String} [anchor='c'] The specified anchor position.  See {@link #alignTo}
19432      * for details on supported anchor positions.
19433      * @param {Boolean} [local] True to get the local (element top/left-relative) anchor position instead
19434      * of page coordinates
19435      * @param {Object} [size] An object containing the size to use for calculating anchor position
19436      * {width: (target width), height: (target height)} (defaults to the element's current size)
19437      * @return {Number[]} [x, y] An array containing the element's x and y coordinates
19438      */
19439     getAnchorXY : function(anchor, local, s){
19440         //Passing a different size is useful for pre-calculating anchors,
19441         //especially for anchored animations that change the el size.
19442         anchor = (anchor || "tl").toLowerCase();
19443         s = s || {};
19444
19445         var me = this,
19446             vp = me.dom == document.body || me.dom == document,
19447             w = s.width || vp ? Ext.Element.getViewWidth() : me.getWidth(),
19448             h = s.height || vp ? Ext.Element.getViewHeight() : me.getHeight(),
19449             xy,
19450             r = Math.round,
19451             o = me.getXY(),
19452             scroll = me.getScroll(),
19453             extraX = vp ? scroll.left : !local ? o[0] : 0,
19454             extraY = vp ? scroll.top : !local ? o[1] : 0,
19455             hash = {
19456                 c  : [r(w * 0.5), r(h * 0.5)],
19457                 t  : [r(w * 0.5), 0],
19458                 l  : [0, r(h * 0.5)],
19459                 r  : [w, r(h * 0.5)],
19460                 b  : [r(w * 0.5), h],
19461                 tl : [0, 0],
19462                 bl : [0, h],
19463                 br : [w, h],
19464                 tr : [w, 0]
19465             };
19466
19467         xy = hash[anchor];
19468         return [xy[0] + extraX, xy[1] + extraY];
19469     },
19470
19471     /**
19472      * Anchors an element to another element and realigns it when the window is resized.
19473      * @param {String/HTMLElement/Ext.Element} element The element to align to.
19474      * @param {String} position The position to align to.
19475      * @param {Number[]} [offsets] Offset the positioning by [x, y]
19476      * @param {Boolean/Object} [animate] True for the default animation or a standard Element animation config object
19477      * @param {Boolean/Number} [monitorScroll] True to monitor body scroll and reposition. If this parameter
19478      * is a number, it is used as the buffer delay (defaults to 50ms).
19479      * @param {Function} [callback] The function to call after the animation finishes
19480      * @return {Ext.Element} this
19481      */
19482     anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
19483         var me = this,
19484             dom = me.dom,
19485             scroll = !Ext.isEmpty(monitorScroll),
19486             action = function(){
19487                 Ext.fly(dom).alignTo(el, alignment, offsets, animate);
19488                 Ext.callback(callback, Ext.fly(dom));
19489             },
19490             anchor = this.getAnchor();
19491
19492         // previous listener anchor, remove it
19493         this.removeAnchor();
19494         Ext.apply(anchor, {
19495             fn: action,
19496             scroll: scroll
19497         });
19498
19499         Ext.EventManager.onWindowResize(action, null);
19500
19501         if(scroll){
19502             Ext.EventManager.on(window, 'scroll', action, null,
19503                 {buffer: !isNaN(monitorScroll) ? monitorScroll : 50});
19504         }
19505         action.call(me); // align immediately
19506         return me;
19507     },
19508
19509     /**
19510      * Remove any anchor to this element. See {@link #anchorTo}.
19511      * @return {Ext.Element} this
19512      */
19513     removeAnchor : function(){
19514         var me = this,
19515             anchor = this.getAnchor();
19516
19517         if(anchor && anchor.fn){
19518             Ext.EventManager.removeResizeListener(anchor.fn);
19519             if(anchor.scroll){
19520                 Ext.EventManager.un(window, 'scroll', anchor.fn);
19521             }
19522             delete anchor.fn;
19523         }
19524         return me;
19525     },
19526
19527     // private
19528     getAnchor : function(){
19529         var data = Ext.Element.data,
19530             dom = this.dom;
19531             if (!dom) {
19532                 return;
19533             }
19534             var anchor = data(dom, '_anchor');
19535
19536         if(!anchor){
19537             anchor = data(dom, '_anchor', {});
19538         }
19539         return anchor;
19540     },
19541
19542     getAlignVector: function(el, spec, offset) {
19543         var me = this,
19544             side = {t:"top", l:"left", r:"right", b: "bottom"},
19545             thisRegion = me.getRegion(),
19546             elRegion;
19547
19548         el = Ext.get(el);
19549         if(!el || !el.dom){
19550         }
19551
19552         elRegion = el.getRegion();
19553     },
19554
19555     /**
19556      * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
19557      * supported position values.
19558      * @param {String/HTMLElement/Ext.Element} element The element to align to.
19559      * @param {String} [position="tl-bl?"] The position to align to (defaults to )
19560      * @param {Number[]} [offsets] Offset the positioning by [x, y]
19561      * @return {Number[]} [x, y]
19562      */
19563     getAlignToXY : function(el, p, o){
19564         el = Ext.get(el);
19565
19566         if(!el || !el.dom){
19567         }
19568
19569         o = o || [0,0];
19570         p = (!p || p == "?" ? "tl-bl?" : (!(/-/).test(p) && p !== "" ? "tl-" + p : p || "tl-bl")).toLowerCase();
19571
19572         var me = this,
19573             d = me.dom,
19574             a1,
19575             a2,
19576             x,
19577             y,
19578             //constrain the aligned el to viewport if necessary
19579             w,
19580             h,
19581             r,
19582             dw = Ext.Element.getViewWidth() -10, // 10px of margin for ie
19583             dh = Ext.Element.getViewHeight()-10, // 10px of margin for ie
19584             p1y,
19585             p1x,
19586             p2y,
19587             p2x,
19588             swapY,
19589             swapX,
19590             doc = document,
19591             docElement = doc.documentElement,
19592             docBody = doc.body,
19593             scrollX = (docElement.scrollLeft || docBody.scrollLeft || 0)+5,
19594             scrollY = (docElement.scrollTop || docBody.scrollTop || 0)+5,
19595             c = false, //constrain to viewport
19596             p1 = "",
19597             p2 = "",
19598             m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
19599
19600         if(!m){
19601         }
19602
19603         p1 = m[1];
19604         p2 = m[2];
19605         c = !!m[3];
19606
19607         //Subtract the aligned el's internal xy from the target's offset xy
19608         //plus custom offset to get the aligned el's new offset xy
19609         a1 = me.getAnchorXY(p1, true);
19610         a2 = el.getAnchorXY(p2, false);
19611
19612         x = a2[0] - a1[0] + o[0];
19613         y = a2[1] - a1[1] + o[1];
19614
19615         if(c){
19616            w = me.getWidth();
19617            h = me.getHeight();
19618            r = el.getRegion();
19619            //If we are at a viewport boundary and the aligned el is anchored on a target border that is
19620            //perpendicular to the vp border, allow the aligned el to slide on that border,
19621            //otherwise swap the aligned el to the opposite border of the target.
19622            p1y = p1.charAt(0);
19623            p1x = p1.charAt(p1.length-1);
19624            p2y = p2.charAt(0);
19625            p2x = p2.charAt(p2.length-1);
19626            swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
19627            swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
19628
19629
19630            if (x + w > dw + scrollX) {
19631                 x = swapX ? r.left-w : dw+scrollX-w;
19632            }
19633            if (x < scrollX) {
19634                x = swapX ? r.right : scrollX;
19635            }
19636            if (y + h > dh + scrollY) {
19637                 y = swapY ? r.top-h : dh+scrollY-h;
19638             }
19639            if (y < scrollY){
19640                y = swapY ? r.bottom : scrollY;
19641            }
19642         }
19643         return [x,y];
19644     },
19645
19646     /**
19647      * Aligns this element with another element relative to the specified anchor points. If the other element is the
19648      * document it aligns it to the viewport.
19649      * The position parameter is optional, and can be specified in any one of the following formats:
19650      * <ul>
19651      *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
19652      *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
19653      *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
19654      *       deprecated in favor of the newer two anchor syntax below</i>.</li>
19655      *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
19656      *       element's anchor point, and the second value is used as the target's anchor point.</li>
19657      * </ul>
19658      * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
19659      * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
19660      * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
19661      * that specified in order to enforce the viewport constraints.
19662      * Following are all of the supported anchor positions:
19663 <pre>
19664 Value  Description
19665 -----  -----------------------------
19666 tl     The top left corner (default)
19667 t      The center of the top edge
19668 tr     The top right corner
19669 l      The center of the left edge
19670 c      In the center of the element
19671 r      The center of the right edge
19672 bl     The bottom left corner
19673 b      The center of the bottom edge
19674 br     The bottom right corner
19675 </pre>
19676 Example Usage:
19677 <pre><code>
19678 // align el to other-el using the default positioning ("tl-bl", non-constrained)
19679 el.alignTo("other-el");
19680
19681 // align the top left corner of el with the top right corner of other-el (constrained to viewport)
19682 el.alignTo("other-el", "tr?");
19683
19684 // align the bottom right corner of el with the center left edge of other-el
19685 el.alignTo("other-el", "br-l?");
19686
19687 // align the center of el with the bottom left corner of other-el and
19688 // adjust the x position by -6 pixels (and the y position by 0)
19689 el.alignTo("other-el", "c-bl", [-6, 0]);
19690 </code></pre>
19691      * @param {String/HTMLElement/Ext.Element} element The element to align to.
19692      * @param {String} [position="tl-bl?"] The position to align to
19693      * @param {Number[]} [offsets] Offset the positioning by [x, y]
19694      * @param {Boolean/Object} [animate] true for the default animation or a standard Element animation config object
19695      * @return {Ext.Element} this
19696      */
19697     alignTo : function(element, position, offsets, animate){
19698         var me = this;
19699         return me.setXY(me.getAlignToXY(element, position, offsets),
19700                         me.anim && !!animate ? me.anim(animate) : false);
19701     },
19702
19703     // private ==>  used outside of core
19704     adjustForConstraints : function(xy, parent) {
19705         var vector = this.getConstrainVector(parent, xy);
19706         if (vector) {
19707             xy[0] += vector[0];
19708             xy[1] += vector[1];
19709         }
19710         return xy;
19711     },
19712
19713     /**
19714      * <p>Returns the <code>[X, Y]</code> vector by which this element must be translated to make a best attempt
19715      * to constrain within the passed constraint. Returns <code>false</code> is this element does not need to be moved.</p>
19716      * <p>Priority is given to constraining the top and left within the constraint.</p>
19717      * <p>The constraint may either be an existing element into which this element is to be constrained, or
19718      * an {@link Ext.util.Region Region} into which this element is to be constrained.</p>
19719      * @param constrainTo {Mixed} The Element or {@link Ext.util.Region Region} into which this element is to be constrained.
19720      * @param proposedPosition {Array} A proposed <code>[X, Y]</code> position to test for validity and to produce a vector for instead
19721      * of using this Element's current position;
19722      * @returns {Number[]/Boolean} <b>If</b> this element <i>needs</i> to be translated, an <code>[X, Y]</code>
19723      * vector by which this element must be translated. Otherwise, <code>false</code>.
19724      */
19725     getConstrainVector: function(constrainTo, proposedPosition) {
19726         if (!(constrainTo instanceof Ext.util.Region)) {
19727             constrainTo = Ext.get(constrainTo).getViewRegion();
19728         }
19729         var thisRegion = this.getRegion(),
19730             vector = [0, 0],
19731             shadowSize = this.shadow && this.shadow.offset,
19732             overflowed = false;
19733
19734         // Shift this region to occupy the proposed position
19735         if (proposedPosition) {
19736             thisRegion.translateBy(proposedPosition[0] - thisRegion.x, proposedPosition[1] - thisRegion.y);
19737         }
19738
19739         // Reduce the constrain region to allow for shadow
19740         // TODO: Rewrite the Shadow class. When that's done, get the extra for each side from the Shadow.
19741         if (shadowSize) {
19742             constrainTo.adjust(0, -shadowSize, -shadowSize, shadowSize);
19743         }
19744
19745         // Constrain the X coordinate by however much this Element overflows
19746         if (thisRegion.right > constrainTo.right) {
19747             overflowed = true;
19748             vector[0] = (constrainTo.right - thisRegion.right);    // overflowed the right
19749         }
19750         if (thisRegion.left + vector[0] < constrainTo.left) {
19751             overflowed = true;
19752             vector[0] = (constrainTo.left - thisRegion.left);      // overflowed the left
19753         }
19754
19755         // Constrain the Y coordinate by however much this Element overflows
19756         if (thisRegion.bottom > constrainTo.bottom) {
19757             overflowed = true;
19758             vector[1] = (constrainTo.bottom - thisRegion.bottom);  // overflowed the bottom
19759         }
19760         if (thisRegion.top + vector[1] < constrainTo.top) {
19761             overflowed = true;
19762             vector[1] = (constrainTo.top - thisRegion.top);        // overflowed the top
19763         }
19764         return overflowed ? vector : false;
19765     },
19766
19767     /**
19768     * Calculates the x, y to center this element on the screen
19769     * @return {Number[]} The x, y values [x, y]
19770     */
19771     getCenterXY : function(){
19772         return this.getAlignToXY(document, 'c-c');
19773     },
19774
19775     /**
19776     * Centers the Element in either the viewport, or another Element.
19777     * @param {String/HTMLElement/Ext.Element} centerIn (optional) The element in which to center the element.
19778     */
19779     center : function(centerIn){
19780         return this.alignTo(centerIn || document, 'c-c');
19781     }
19782 });
19783
19784 /**
19785  * @class Ext.Element
19786  */
19787 (function(){
19788
19789 var ELEMENT = Ext.Element,
19790     LEFT = "left",
19791     RIGHT = "right",
19792     TOP = "top",
19793     BOTTOM = "bottom",
19794     POSITION = "position",
19795     STATIC = "static",
19796     RELATIVE = "relative",
19797     AUTO = "auto",
19798     ZINDEX = "z-index";
19799
19800 Ext.override(Ext.Element, {
19801     /**
19802       * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
19803       * @return {Number} The X position of the element
19804       */
19805     getX : function(){
19806         return ELEMENT.getX(this.dom);
19807     },
19808
19809     /**
19810       * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
19811       * @return {Number} The Y position of the element
19812       */
19813     getY : function(){
19814         return ELEMENT.getY(this.dom);
19815     },
19816
19817     /**
19818       * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
19819       * @return {Number[]} The XY position of the element
19820       */
19821     getXY : function(){
19822         return ELEMENT.getXY(this.dom);
19823     },
19824
19825     /**
19826       * Returns the offsets of this element from the passed element. Both element must be part of the DOM tree and not have display:none to have page coordinates.
19827       * @param {String/HTMLElement/Ext.Element} element The element to get the offsets from.
19828       * @return {Number[]} The XY page offsets (e.g. [100, -200])
19829       */
19830     getOffsetsTo : function(el){
19831         var o = this.getXY(),
19832             e = Ext.fly(el, '_internal').getXY();
19833         return [o[0]-e[0],o[1]-e[1]];
19834     },
19835
19836     /**
19837      * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
19838      * @param {Number} The X position of the element
19839      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
19840      * @return {Ext.Element} this
19841      */
19842     setX : function(x, animate){
19843         return this.setXY([x, this.getY()], animate);
19844     },
19845
19846     /**
19847      * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
19848      * @param {Number} The Y position of the element
19849      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
19850      * @return {Ext.Element} this
19851      */
19852     setY : function(y, animate){
19853         return this.setXY([this.getX(), y], animate);
19854     },
19855
19856     /**
19857      * Sets the element's left position directly using CSS style (instead of {@link #setX}).
19858      * @param {String} left The left CSS property value
19859      * @return {Ext.Element} this
19860      */
19861     setLeft : function(left){
19862         this.setStyle(LEFT, this.addUnits(left));
19863         return this;
19864     },
19865
19866     /**
19867      * Sets the element's top position directly using CSS style (instead of {@link #setY}).
19868      * @param {String} top The top CSS property value
19869      * @return {Ext.Element} this
19870      */
19871     setTop : function(top){
19872         this.setStyle(TOP, this.addUnits(top));
19873         return this;
19874     },
19875
19876     /**
19877      * Sets the element's CSS right style.
19878      * @param {String} right The right CSS property value
19879      * @return {Ext.Element} this
19880      */
19881     setRight : function(right){
19882         this.setStyle(RIGHT, this.addUnits(right));
19883         return this;
19884     },
19885
19886     /**
19887      * Sets the element's CSS bottom style.
19888      * @param {String} bottom The bottom CSS property value
19889      * @return {Ext.Element} this
19890      */
19891     setBottom : function(bottom){
19892         this.setStyle(BOTTOM, this.addUnits(bottom));
19893         return this;
19894     },
19895
19896     /**
19897      * Sets the position of the element in page coordinates, regardless of how the element is positioned.
19898      * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
19899      * @param {Number[]} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
19900      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
19901      * @return {Ext.Element} this
19902      */
19903     setXY: function(pos, animate) {
19904         var me = this;
19905         if (!animate || !me.anim) {
19906             ELEMENT.setXY(me.dom, pos);
19907         }
19908         else {
19909             if (!Ext.isObject(animate)) {
19910                 animate = {};
19911             }
19912             me.animate(Ext.applyIf({ to: { x: pos[0], y: pos[1] } }, animate));
19913         }
19914         return me;
19915     },
19916
19917     /**
19918      * Sets the position of the element in page coordinates, regardless of how the element is positioned.
19919      * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
19920      * @param {Number} x X value for new position (coordinates are page-based)
19921      * @param {Number} y Y value for new position (coordinates are page-based)
19922      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
19923      * @return {Ext.Element} this
19924      */
19925     setLocation : function(x, y, animate){
19926         return this.setXY([x, y], animate);
19927     },
19928
19929     /**
19930      * Sets the position of the element in page coordinates, regardless of how the element is positioned.
19931      * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
19932      * @param {Number} x X value for new position (coordinates are page-based)
19933      * @param {Number} y Y value for new position (coordinates are page-based)
19934      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
19935      * @return {Ext.Element} this
19936      */
19937     moveTo : function(x, y, animate){
19938         return this.setXY([x, y], animate);
19939     },
19940
19941     /**
19942      * Gets the left X coordinate
19943      * @param {Boolean} local True to get the local css position instead of page coordinate
19944      * @return {Number}
19945      */
19946     getLeft : function(local){
19947         return !local ? this.getX() : parseInt(this.getStyle(LEFT), 10) || 0;
19948     },
19949
19950     /**
19951      * Gets the right X coordinate of the element (element X position + element width)
19952      * @param {Boolean} local True to get the local css position instead of page coordinate
19953      * @return {Number}
19954      */
19955     getRight : function(local){
19956         var me = this;
19957         return !local ? me.getX() + me.getWidth() : (me.getLeft(true) + me.getWidth()) || 0;
19958     },
19959
19960     /**
19961      * Gets the top Y coordinate
19962      * @param {Boolean} local True to get the local css position instead of page coordinate
19963      * @return {Number}
19964      */
19965     getTop : function(local) {
19966         return !local ? this.getY() : parseInt(this.getStyle(TOP), 10) || 0;
19967     },
19968
19969     /**
19970      * Gets the bottom Y coordinate of the element (element Y position + element height)
19971      * @param {Boolean} local True to get the local css position instead of page coordinate
19972      * @return {Number}
19973      */
19974     getBottom : function(local){
19975         var me = this;
19976         return !local ? me.getY() + me.getHeight() : (me.getTop(true) + me.getHeight()) || 0;
19977     },
19978
19979     /**
19980     * Initializes positioning on this element. If a desired position is not passed, it will make the
19981     * the element positioned relative IF it is not already positioned.
19982     * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
19983     * @param {Number} zIndex (optional) The zIndex to apply
19984     * @param {Number} x (optional) Set the page X position
19985     * @param {Number} y (optional) Set the page Y position
19986     */
19987     position : function(pos, zIndex, x, y) {
19988         var me = this;
19989
19990         if (!pos && me.isStyle(POSITION, STATIC)){
19991             me.setStyle(POSITION, RELATIVE);
19992         } else if(pos) {
19993             me.setStyle(POSITION, pos);
19994         }
19995         if (zIndex){
19996             me.setStyle(ZINDEX, zIndex);
19997         }
19998         if (x || y) {
19999             me.setXY([x || false, y || false]);
20000         }
20001     },
20002
20003     /**
20004     * Clear positioning back to the default when the document was loaded
20005     * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
20006     * @return {Ext.Element} this
20007      */
20008     clearPositioning : function(value){
20009         value = value || '';
20010         this.setStyle({
20011             left : value,
20012             right : value,
20013             top : value,
20014             bottom : value,
20015             "z-index" : "",
20016             position : STATIC
20017         });
20018         return this;
20019     },
20020
20021     /**
20022     * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
20023     * snapshot before performing an update and then restoring the element.
20024     * @return {Object}
20025     */
20026     getPositioning : function(){
20027         var l = this.getStyle(LEFT);
20028         var t = this.getStyle(TOP);
20029         return {
20030             "position" : this.getStyle(POSITION),
20031             "left" : l,
20032             "right" : l ? "" : this.getStyle(RIGHT),
20033             "top" : t,
20034             "bottom" : t ? "" : this.getStyle(BOTTOM),
20035             "z-index" : this.getStyle(ZINDEX)
20036         };
20037     },
20038
20039     /**
20040     * Set positioning with an object returned by getPositioning().
20041     * @param {Object} posCfg
20042     * @return {Ext.Element} this
20043      */
20044     setPositioning : function(pc){
20045         var me = this,
20046             style = me.dom.style;
20047
20048         me.setStyle(pc);
20049
20050         if(pc.right == AUTO){
20051             style.right = "";
20052         }
20053         if(pc.bottom == AUTO){
20054             style.bottom = "";
20055         }
20056
20057         return me;
20058     },
20059
20060     /**
20061      * Translates the passed page coordinates into left/top css values for this element
20062      * @param {Number/Number[]} x The page x or an array containing [x, y]
20063      * @param {Number} y (optional) The page y, required if x is not an array
20064      * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
20065      */
20066     translatePoints: function(x, y) {
20067         if (Ext.isArray(x)) {
20068              y = x[1];
20069              x = x[0];
20070         }
20071         var me = this,
20072             relative = me.isStyle(POSITION, RELATIVE),
20073             o = me.getXY(),
20074             left = parseInt(me.getStyle(LEFT), 10),
20075             top = parseInt(me.getStyle(TOP), 10);
20076
20077         if (!Ext.isNumber(left)) {
20078             left = relative ? 0 : me.dom.offsetLeft;
20079         }
20080         if (!Ext.isNumber(top)) {
20081             top = relative ? 0 : me.dom.offsetTop;
20082         }
20083         left = (Ext.isNumber(x)) ? x - o[0] + left : undefined;
20084         top = (Ext.isNumber(y)) ? y - o[1] + top : undefined;
20085         return {
20086             left: left,
20087             top: top
20088         };
20089     },
20090
20091     /**
20092      * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
20093      * @param {Object} box The box to fill {x, y, width, height}
20094      * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
20095      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
20096      * @return {Ext.Element} this
20097      */
20098     setBox: function(box, adjust, animate) {
20099         var me = this,
20100             w = box.width,
20101             h = box.height;
20102         if ((adjust && !me.autoBoxAdjust) && !me.isBorderBox()) {
20103             w -= (me.getBorderWidth("lr") + me.getPadding("lr"));
20104             h -= (me.getBorderWidth("tb") + me.getPadding("tb"));
20105         }
20106         me.setBounds(box.x, box.y, w, h, animate);
20107         return me;
20108     },
20109
20110     /**
20111      * Return an object defining the area of this Element which can be passed to {@link #setBox} to
20112      * set another Element's size/location to match this element.
20113      * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
20114      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
20115      * @return {Object} box An object in the format<pre><code>
20116 {
20117     x: &lt;Element's X position>,
20118     y: &lt;Element's Y position>,
20119     width: &lt;Element's width>,
20120     height: &lt;Element's height>,
20121     bottom: &lt;Element's lower bound>,
20122     right: &lt;Element's rightmost bound>
20123 }
20124 </code></pre>
20125      * The returned object may also be addressed as an Array where index 0 contains the X position
20126      * and index 1 contains the Y position. So the result may also be used for {@link #setXY}
20127      */
20128     getBox: function(contentBox, local) {
20129         var me = this,
20130             xy,
20131             left,
20132             top,
20133             getBorderWidth = me.getBorderWidth,
20134             getPadding = me.getPadding,
20135             l, r, t, b, w, h, bx;
20136         if (!local) {
20137             xy = me.getXY();
20138         } else {
20139             left = parseInt(me.getStyle("left"), 10) || 0;
20140             top = parseInt(me.getStyle("top"), 10) || 0;
20141             xy = [left, top];
20142         }
20143         w = me.getWidth();
20144         h = me.getHeight();
20145         if (!contentBox) {
20146             bx = {
20147                 x: xy[0],
20148                 y: xy[1],
20149                 0: xy[0],
20150                 1: xy[1],
20151                 width: w,
20152                 height: h
20153             };
20154         } else {
20155             l = getBorderWidth.call(me, "l") + getPadding.call(me, "l");
20156             r = getBorderWidth.call(me, "r") + getPadding.call(me, "r");
20157             t = getBorderWidth.call(me, "t") + getPadding.call(me, "t");
20158             b = getBorderWidth.call(me, "b") + getPadding.call(me, "b");
20159             bx = {
20160                 x: xy[0] + l,
20161                 y: xy[1] + t,
20162                 0: xy[0] + l,
20163                 1: xy[1] + t,
20164                 width: w - (l + r),
20165                 height: h - (t + b)
20166             };
20167         }
20168         bx.right = bx.x + bx.width;
20169         bx.bottom = bx.y + bx.height;
20170         return bx;
20171     },
20172
20173     /**
20174      * Move this element relative to its current position.
20175      * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
20176      * @param {Number} distance How far to move the element in pixels
20177      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
20178      */
20179     move: function(direction, distance, animate) {
20180         var me = this,
20181             xy = me.getXY(),
20182             x = xy[0],
20183             y = xy[1],
20184             left = [x - distance, y],
20185             right = [x + distance, y],
20186             top = [x, y - distance],
20187             bottom = [x, y + distance],
20188             hash = {
20189                 l: left,
20190                 left: left,
20191                 r: right,
20192                 right: right,
20193                 t: top,
20194                 top: top,
20195                 up: top,
20196                 b: bottom,
20197                 bottom: bottom,
20198                 down: bottom
20199             };
20200
20201         direction = direction.toLowerCase();
20202         me.moveTo(hash[direction][0], hash[direction][1], animate);
20203     },
20204
20205     /**
20206      * Quick set left and top adding default units
20207      * @param {String} left The left CSS property value
20208      * @param {String} top The top CSS property value
20209      * @return {Ext.Element} this
20210      */
20211     setLeftTop: function(left, top) {
20212         var me = this,
20213             style = me.dom.style;
20214         style.left = me.addUnits(left);
20215         style.top = me.addUnits(top);
20216         return me;
20217     },
20218
20219     /**
20220      * Returns the region of this element.
20221      * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
20222      * @return {Ext.util.Region} A Region containing "top, left, bottom, right" member data.
20223      */
20224     getRegion: function() {
20225         return this.getPageBox(true);
20226     },
20227
20228     /**
20229      * Returns the <b>content</b> region of this element. That is the region within the borders and padding.
20230      * @return {Ext.util.Region} A Region containing "top, left, bottom, right" member data.
20231      */
20232     getViewRegion: function() {
20233         var me = this,
20234             isBody = me.dom === document.body,
20235             scroll, pos, top, left, width, height;
20236
20237         // For the body we want to do some special logic
20238         if (isBody) {
20239             scroll = me.getScroll();
20240             left = scroll.left;
20241             top = scroll.top;
20242             width = Ext.Element.getViewportWidth();
20243             height = Ext.Element.getViewportHeight();
20244         }
20245         else {
20246             pos = me.getXY();
20247             left = pos[0] + me.getBorderWidth('l') + me.getPadding('l');
20248             top = pos[1] + me.getBorderWidth('t') + me.getPadding('t');
20249             width = me.getWidth(true);
20250             height = me.getHeight(true);
20251         }
20252
20253         return Ext.create('Ext.util.Region', top, left + width, top + height, left);
20254     },
20255
20256     /**
20257      * Return an object defining the area of this Element which can be passed to {@link #setBox} to
20258      * set another Element's size/location to match this element.
20259      * @param {Boolean} asRegion(optional) If true an Ext.util.Region will be returned
20260      * @return {Object} box An object in the format<pre><code>
20261 {
20262     x: &lt;Element's X position>,
20263     y: &lt;Element's Y position>,
20264     width: &lt;Element's width>,
20265     height: &lt;Element's height>,
20266     bottom: &lt;Element's lower bound>,
20267     right: &lt;Element's rightmost bound>
20268 }
20269 </code></pre>
20270      * The returned object may also be addressed as an Array where index 0 contains the X position
20271      * and index 1 contains the Y position. So the result may also be used for {@link #setXY}
20272      */
20273     getPageBox : function(getRegion) {
20274         var me = this,
20275             el = me.dom,
20276             isDoc = el === document.body,
20277             w = isDoc ? Ext.Element.getViewWidth()  : el.offsetWidth,
20278             h = isDoc ? Ext.Element.getViewHeight() : el.offsetHeight,
20279             xy = me.getXY(),
20280             t = xy[1],
20281             r = xy[0] + w,
20282             b = xy[1] + h,
20283             l = xy[0];
20284
20285         if (getRegion) {
20286             return Ext.create('Ext.util.Region', t, r, b, l);
20287         }
20288         else {
20289             return {
20290                 left: l,
20291                 top: t,
20292                 width: w,
20293                 height: h,
20294                 right: r,
20295                 bottom: b
20296             };
20297         }
20298     },
20299
20300     /**
20301      * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
20302      * @param {Number} x X value for new position (coordinates are page-based)
20303      * @param {Number} y Y value for new position (coordinates are page-based)
20304      * @param {Number/String} width The new width. This may be one of:<div class="mdetail-params"><ul>
20305      * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels)</li>
20306      * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
20307      * </ul></div>
20308      * @param {Number/String} height The new height. This may be one of:<div class="mdetail-params"><ul>
20309      * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels)</li>
20310      * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
20311      * </ul></div>
20312      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
20313      * @return {Ext.Element} this
20314      */
20315     setBounds: function(x, y, width, height, animate) {
20316         var me = this;
20317         if (!animate || !me.anim) {
20318             me.setSize(width, height);
20319             me.setLocation(x, y);
20320         } else {
20321             if (!Ext.isObject(animate)) {
20322                 animate = {};
20323             }
20324             me.animate(Ext.applyIf({
20325                 to: {
20326                     x: x,
20327                     y: y,
20328                     width: me.adjustWidth(width),
20329                     height: me.adjustHeight(height)
20330                 }
20331             }, animate));
20332         }
20333         return me;
20334     },
20335
20336     /**
20337      * Sets the element's position and size the specified region. If animation is true then width, height, x and y will be animated concurrently.
20338      * @param {Ext.util.Region} region The region to fill
20339      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
20340      * @return {Ext.Element} this
20341      */
20342     setRegion: function(region, animate) {
20343         return this.setBounds(region.left, region.top, region.right - region.left, region.bottom - region.top, animate);
20344     }
20345 });
20346 })();
20347
20348 /**
20349  * @class Ext.Element
20350  */
20351 Ext.override(Ext.Element, {
20352     /**
20353      * Returns true if this element is scrollable.
20354      * @return {Boolean}
20355      */
20356     isScrollable : function(){
20357         var dom = this.dom;
20358         return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
20359     },
20360
20361     /**
20362      * Returns the current scroll position of the element.
20363      * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
20364      */
20365     getScroll : function() {
20366         var d = this.dom, 
20367             doc = document,
20368             body = doc.body,
20369             docElement = doc.documentElement,
20370             l,
20371             t,
20372             ret;
20373
20374         if (d == doc || d == body) {
20375             if (Ext.isIE && Ext.isStrict) {
20376                 l = docElement.scrollLeft; 
20377                 t = docElement.scrollTop;
20378             } else {
20379                 l = window.pageXOffset;
20380                 t = window.pageYOffset;
20381             }
20382             ret = {
20383                 left: l || (body ? body.scrollLeft : 0), 
20384                 top : t || (body ? body.scrollTop : 0)
20385             };
20386         } else {
20387             ret = {
20388                 left: d.scrollLeft, 
20389                 top : d.scrollTop
20390             };
20391         }
20392         
20393         return ret;
20394     },
20395     
20396     /**
20397      * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
20398      * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
20399      * @param {Number} value The new scroll value
20400      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
20401      * @return {Ext.Element} this
20402      */
20403     scrollTo : function(side, value, animate) {
20404         //check if we're scrolling top or left
20405         var top = /top/i.test(side),
20406             me = this,
20407             dom = me.dom,
20408             obj = {},
20409             prop;
20410         if (!animate || !me.anim) {
20411             // just setting the value, so grab the direction
20412             prop = 'scroll' + (top ? 'Top' : 'Left');
20413             dom[prop] = value;
20414         }
20415         else {
20416             if (!Ext.isObject(animate)) {
20417                 animate = {};
20418             }
20419             obj['scroll' + (top ? 'Top' : 'Left')] = value;
20420             me.animate(Ext.applyIf({
20421                 to: obj
20422             }, animate));
20423         }
20424         return me;
20425     },
20426
20427     /**
20428      * Scrolls this element into view within the passed container.
20429      * @param {String/HTMLElement/Ext.Element} container (optional) The container element to scroll (defaults to document.body).  Should be a
20430      * string (id), dom node, or Ext.Element.
20431      * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
20432      * @return {Ext.Element} this
20433      */
20434     scrollIntoView : function(container, hscroll) {
20435         container = Ext.getDom(container) || Ext.getBody().dom;
20436         var el = this.dom,
20437             offsets = this.getOffsetsTo(container),
20438             // el's box
20439             left = offsets[0] + container.scrollLeft,
20440             top = offsets[1] + container.scrollTop,
20441             bottom = top + el.offsetHeight,
20442             right = left + el.offsetWidth,
20443             // ct's box
20444             ctClientHeight = container.clientHeight,
20445             ctScrollTop = parseInt(container.scrollTop, 10),
20446             ctScrollLeft = parseInt(container.scrollLeft, 10),
20447             ctBottom = ctScrollTop + ctClientHeight,
20448             ctRight = ctScrollLeft + container.clientWidth;
20449
20450         if (el.offsetHeight > ctClientHeight || top < ctScrollTop) {
20451             container.scrollTop = top;
20452         } else if (bottom > ctBottom) {
20453             container.scrollTop = bottom - ctClientHeight;
20454         }
20455         // corrects IE, other browsers will ignore
20456         container.scrollTop = container.scrollTop;
20457
20458         if (hscroll !== false) {
20459             if (el.offsetWidth > container.clientWidth || left < ctScrollLeft) {
20460                 container.scrollLeft = left;
20461             }
20462             else if (right > ctRight) {
20463                 container.scrollLeft = right - container.clientWidth;
20464             }
20465             container.scrollLeft = container.scrollLeft;
20466         }
20467         return this;
20468     },
20469
20470     // private
20471     scrollChildIntoView : function(child, hscroll) {
20472         Ext.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
20473     },
20474
20475     /**
20476      * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
20477      * within this element's scrollable range.
20478      * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
20479      * @param {Number} distance How far to scroll the element in pixels
20480      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
20481      * @return {Boolean} Returns true if a scroll was triggered or false if the element
20482      * was scrolled as far as it could go.
20483      */
20484      scroll : function(direction, distance, animate) {
20485         if (!this.isScrollable()) {
20486             return false;
20487         }
20488         var el = this.dom,
20489             l = el.scrollLeft, t = el.scrollTop,
20490             w = el.scrollWidth, h = el.scrollHeight,
20491             cw = el.clientWidth, ch = el.clientHeight,
20492             scrolled = false, v,
20493             hash = {
20494                 l: Math.min(l + distance, w-cw),
20495                 r: v = Math.max(l - distance, 0),
20496                 t: Math.max(t - distance, 0),
20497                 b: Math.min(t + distance, h-ch)
20498             };
20499             hash.d = hash.b;
20500             hash.u = hash.t;
20501
20502         direction = direction.substr(0, 1);
20503         if ((v = hash[direction]) > -1) {
20504             scrolled = true;
20505             this.scrollTo(direction == 'l' || direction == 'r' ? 'left' : 'top', v, this.anim(animate));
20506         }
20507         return scrolled;
20508     }
20509 });
20510 /**
20511  * @class Ext.Element
20512  */
20513 Ext.Element.addMethods(
20514     function() {
20515         var VISIBILITY      = "visibility",
20516             DISPLAY         = "display",
20517             HIDDEN          = "hidden",
20518             NONE            = "none",
20519             XMASKED         = Ext.baseCSSPrefix + "masked",
20520             XMASKEDRELATIVE = Ext.baseCSSPrefix + "masked-relative",
20521             data            = Ext.Element.data;
20522
20523         return {
20524             /**
20525              * Checks whether the element is currently visible using both visibility and display properties.
20526              * @param {Boolean} [deep=false] True to walk the dom and see if parent elements are hidden
20527              * @return {Boolean} True if the element is currently visible, else false
20528              */
20529             isVisible : function(deep) {
20530                 var vis = !this.isStyle(VISIBILITY, HIDDEN) && !this.isStyle(DISPLAY, NONE),
20531                     p   = this.dom.parentNode;
20532
20533                 if (deep !== true || !vis) {
20534                     return vis;
20535                 }
20536
20537                 while (p && !(/^body/i.test(p.tagName))) {
20538                     if (!Ext.fly(p, '_isVisible').isVisible()) {
20539                         return false;
20540                     }
20541                     p = p.parentNode;
20542                 }
20543                 return true;
20544             },
20545
20546             /**
20547              * Returns true if display is not "none"
20548              * @return {Boolean}
20549              */
20550             isDisplayed : function() {
20551                 return !this.isStyle(DISPLAY, NONE);
20552             },
20553
20554             /**
20555              * Convenience method for setVisibilityMode(Element.DISPLAY)
20556              * @param {String} display (optional) What to set display to when visible
20557              * @return {Ext.Element} this
20558              */
20559             enableDisplayMode : function(display) {
20560                 this.setVisibilityMode(Ext.Element.DISPLAY);
20561
20562                 if (!Ext.isEmpty(display)) {
20563                     data(this.dom, 'originalDisplay', display);
20564                 }
20565
20566                 return this;
20567             },
20568
20569             /**
20570              * Puts a mask over this element to disable user interaction. Requires core.css.
20571              * This method can only be applied to elements which accept child nodes.
20572              * @param {String} msg (optional) A message to display in the mask
20573              * @param {String} msgCls (optional) A css class to apply to the msg element
20574              * @return {Ext.Element} The mask element
20575              */
20576             mask : function(msg, msgCls) {
20577                 var me  = this,
20578                     dom = me.dom,
20579                     setExpression = dom.style.setExpression,
20580                     dh  = Ext.DomHelper,
20581                     EXTELMASKMSG = Ext.baseCSSPrefix + "mask-msg",
20582                     el,
20583                     mask;
20584
20585                 if (!(/^body/i.test(dom.tagName) && me.getStyle('position') == 'static')) {
20586                     me.addCls(XMASKEDRELATIVE);
20587                 }
20588                 el = data(dom, 'maskMsg');
20589                 if (el) {
20590                     el.remove();
20591                 }
20592                 el = data(dom, 'mask');
20593                 if (el) {
20594                     el.remove();
20595                 }
20596
20597                 mask = dh.append(dom, {cls : Ext.baseCSSPrefix + "mask"}, true);
20598                 data(dom, 'mask', mask);
20599
20600                 me.addCls(XMASKED);
20601                 mask.setDisplayed(true);
20602
20603                 if (typeof msg == 'string') {
20604                     var mm = dh.append(dom, {cls : EXTELMASKMSG, cn:{tag:'div'}}, true);
20605                     data(dom, 'maskMsg', mm);
20606                     mm.dom.className = msgCls ? EXTELMASKMSG + " " + msgCls : EXTELMASKMSG;
20607                     mm.dom.firstChild.innerHTML = msg;
20608                     mm.setDisplayed(true);
20609                     mm.center(me);
20610                 }
20611                 // NOTE: CSS expressions are resource intensive and to be used only as a last resort
20612                 // These expressions are removed as soon as they are no longer necessary - in the unmask method.
20613                 // In normal use cases an element will be masked for a limited period of time.
20614                 // Fix for https://sencha.jira.com/browse/EXTJSIV-19.
20615                 // IE6 strict mode and IE6-9 quirks mode takes off left+right padding when calculating width!
20616                 if (!Ext.supports.IncludePaddingInWidthCalculation && setExpression) {
20617                     mask.dom.style.setExpression('width', 'this.parentNode.offsetWidth + "px"');
20618                 }
20619
20620                 // Some versions and modes of IE subtract top+bottom padding when calculating height.
20621                 // Different versions from those which make the same error for width!
20622                 if (!Ext.supports.IncludePaddingInHeightCalculation && setExpression) {
20623                     mask.dom.style.setExpression('height', 'this.parentNode.offsetHeight + "px"');
20624                 }
20625                 // ie will not expand full height automatically
20626                 else if (Ext.isIE && !(Ext.isIE7 && Ext.isStrict) && me.getStyle('height') == 'auto') {
20627                     mask.setSize(undefined, me.getHeight());
20628                 }
20629                 return mask;
20630             },
20631
20632             /**
20633              * Removes a previously applied mask.
20634              */
20635             unmask : function() {
20636                 var me      = this,
20637                     dom     = me.dom,
20638                     mask    = data(dom, 'mask'),
20639                     maskMsg = data(dom, 'maskMsg');
20640
20641                 if (mask) {
20642                     // Remove resource-intensive CSS expressions as soon as they are not required.
20643                     if (mask.dom.style.clearExpression) {
20644                         mask.dom.style.clearExpression('width');
20645                         mask.dom.style.clearExpression('height');
20646                     }
20647                     if (maskMsg) {
20648                         maskMsg.remove();
20649                         data(dom, 'maskMsg', undefined);
20650                     }
20651
20652                     mask.remove();
20653                     data(dom, 'mask', undefined);
20654                     me.removeCls([XMASKED, XMASKEDRELATIVE]);
20655                 }
20656             },
20657             /**
20658              * Returns true if this element is masked. Also re-centers any displayed message within the mask.
20659              * @return {Boolean}
20660              */
20661             isMasked : function() {
20662                 var me = this,
20663                     mask = data(me.dom, 'mask'),
20664                     maskMsg = data(me.dom, 'maskMsg');
20665
20666                 if (mask && mask.isVisible()) {
20667                     if (maskMsg) {
20668                         maskMsg.center(me);
20669                     }
20670                     return true;
20671                 }
20672                 return false;
20673             },
20674
20675             /**
20676              * Creates an iframe shim for this element to keep selects and other windowed objects from
20677              * showing through.
20678              * @return {Ext.Element} The new shim element
20679              */
20680             createShim : function() {
20681                 var el = document.createElement('iframe'),
20682                     shim;
20683
20684                 el.frameBorder = '0';
20685                 el.className = Ext.baseCSSPrefix + 'shim';
20686                 el.src = Ext.SSL_SECURE_URL;
20687                 shim = Ext.get(this.dom.parentNode.insertBefore(el, this.dom));
20688                 shim.autoBoxAdjust = false;
20689                 return shim;
20690             }
20691         };
20692     }()
20693 );
20694 /**
20695  * @class Ext.Element
20696  */
20697 Ext.Element.addMethods({
20698     /**
20699      * Convenience method for constructing a KeyMap
20700      * @param {String/Number/Number[]/Object} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
20701      * <code>{key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}</code>
20702      * @param {Function} fn The function to call
20703      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the specified function is executed. Defaults to this Element.
20704      * @return {Ext.util.KeyMap} The KeyMap created
20705      */
20706     addKeyListener : function(key, fn, scope){
20707         var config;
20708         if(typeof key != 'object' || Ext.isArray(key)){
20709             config = {
20710                 key: key,
20711                 fn: fn,
20712                 scope: scope
20713             };
20714         }else{
20715             config = {
20716                 key : key.key,
20717                 shift : key.shift,
20718                 ctrl : key.ctrl,
20719                 alt : key.alt,
20720                 fn: fn,
20721                 scope: scope
20722             };
20723         }
20724         return Ext.create('Ext.util.KeyMap', this, config);
20725     },
20726
20727     /**
20728      * Creates a KeyMap for this element
20729      * @param {Object} config The KeyMap config. See {@link Ext.util.KeyMap} for more details
20730      * @return {Ext.util.KeyMap} The KeyMap created
20731      */
20732     addKeyMap : function(config){
20733         return Ext.create('Ext.util.KeyMap', this, config);
20734     }
20735 });
20736
20737 //Import the newly-added Ext.Element functions into CompositeElementLite. We call this here because
20738 //Element.keys.js is the last extra Ext.Element include in the ext-all.js build
20739 Ext.CompositeElementLite.importElementMethods();
20740
20741 /**
20742  * @class Ext.CompositeElementLite
20743  */
20744 Ext.apply(Ext.CompositeElementLite.prototype, {
20745     addElements : function(els, root){
20746         if(!els){
20747             return this;
20748         }
20749         if(typeof els == "string"){
20750             els = Ext.Element.selectorFunction(els, root);
20751         }
20752         var yels = this.elements;
20753         Ext.each(els, function(e) {
20754             yels.push(Ext.get(e));
20755         });
20756         return this;
20757     },
20758
20759     /**
20760      * Returns the first Element
20761      * @return {Ext.Element}
20762      */
20763     first : function(){
20764         return this.item(0);
20765     },
20766
20767     /**
20768      * Returns the last Element
20769      * @return {Ext.Element}
20770      */
20771     last : function(){
20772         return this.item(this.getCount()-1);
20773     },
20774
20775     /**
20776      * Returns true if this composite contains the passed element
20777      * @param el {String/HTMLElement/Ext.Element/Number} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection.
20778      * @return Boolean
20779      */
20780     contains : function(el){
20781         return this.indexOf(el) != -1;
20782     },
20783
20784     /**
20785     * Removes the specified element(s).
20786     * @param {String/HTMLElement/Ext.Element/Number} el The id of an element, the Element itself, the index of the element in this composite
20787     * or an array of any of those.
20788     * @param {Boolean} removeDom (optional) True to also remove the element from the document
20789     * @return {Ext.CompositeElement} this
20790     */
20791     removeElement : function(keys, removeDom){
20792         var me = this,
20793             els = this.elements,
20794             el;
20795         Ext.each(keys, function(val){
20796             if ((el = (els[val] || els[val = me.indexOf(val)]))) {
20797                 if(removeDom){
20798                     if(el.dom){
20799                         el.remove();
20800                     }else{
20801                         Ext.removeNode(el);
20802                     }
20803                 }
20804                 Ext.Array.erase(els, val, 1);
20805             }
20806         });
20807         return this;
20808     }
20809 });
20810
20811 /**
20812  * @class Ext.CompositeElement
20813  * @extends Ext.CompositeElementLite
20814  * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter
20815  * members, or to perform collective actions upon the whole set.</p>
20816  * <p>Although they are not listed, this class supports all of the methods of {@link Ext.Element} and
20817  * {@link Ext.fx.Anim}. The methods from these classes will be performed on all the elements in this collection.</p>
20818  * <p>All methods return <i>this</i> and can be chained.</p>
20819  * Usage:
20820 <pre><code>
20821 var els = Ext.select("#some-el div.some-class", true);
20822 // or select directly from an existing element
20823 var el = Ext.get('some-el');
20824 el.select('div.some-class', true);
20825
20826 els.setWidth(100); // all elements become 100 width
20827 els.hide(true); // all elements fade out and hide
20828 // or
20829 els.setWidth(100).hide(true);
20830 </code></pre>
20831  */
20832 Ext.CompositeElement = Ext.extend(Ext.CompositeElementLite, {
20833
20834     constructor : function(els, root){
20835         this.elements = [];
20836         this.add(els, root);
20837     },
20838
20839     // private
20840     getElement : function(el){
20841         // In this case just return it, since we already have a reference to it
20842         return el;
20843     },
20844
20845     // private
20846     transformElement : function(el){
20847         return Ext.get(el);
20848     }
20849 });
20850
20851 /**
20852  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
20853  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
20854  * {@link Ext.CompositeElementLite CompositeElementLite} object.
20855  * @param {String/HTMLElement[]} selector The CSS selector or an array of elements
20856  * @param {Boolean} [unique] true to create a unique Ext.Element for each element (defaults to a shared flyweight object)
20857  * @param {HTMLElement/String} [root] The root element of the query or id of the root
20858  * @return {Ext.CompositeElementLite/Ext.CompositeElement}
20859  * @member Ext.Element
20860  * @method select
20861  */
20862 Ext.Element.select = function(selector, unique, root){
20863     var els;
20864     if(typeof selector == "string"){
20865         els = Ext.Element.selectorFunction(selector, root);
20866     }else if(selector.length !== undefined){
20867         els = selector;
20868     }else{
20869     }
20870     return (unique === true) ? new Ext.CompositeElement(els) : new Ext.CompositeElementLite(els);
20871 };
20872
20873 /**
20874  * Shorthand of {@link Ext.Element#select}.
20875  * @member Ext
20876  * @method select
20877  * @alias Ext.Element#select
20878  */
20879 Ext.select = Ext.Element.select;
20880
20881
20882