Upgrade to ExtJS 4.0.2 - Released 06/09/2011
[extjs.git] / builds / ext-core-dev.js
1 /*
2
3 This file is part of Ext JS 4
4
5 Copyright (c) 2011 Sencha Inc
6
7 Contact:  http://www.sencha.com/contact
8
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.  Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
11
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
13
14 */
15 /**
16  * @class Ext
17  * @singleton
18  */
19 (function() {
20     var global = this,
21         objectPrototype = Object.prototype,
22         toString = objectPrototype.toString,
23         enumerables = true,
24         enumerablesTest = { toString: 1 },
25         i;
26
27     if (typeof Ext === 'undefined') {
28         global.Ext = {};
29     }
30
31     Ext.global = global;
32
33     for (i in enumerablesTest) {
34         enumerables = null;
35     }
36
37     if (enumerables) {
38         enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable',
39                        'toLocaleString', 'toString', 'constructor'];
40     }
41
42     /**
43      * An array containing extra enumerables for old browsers
44      * @type Array
45      */
46     Ext.enumerables = enumerables;
47
48     /**
49      * Copies all the properties of config to the specified object.
50      * Note that if recursive merging and cloning without referencing the original objects / arrays is needed, use
51      * {@link Ext.Object#merge} instead.
52      * @param {Object} object The receiver of the properties
53      * @param {Object} config The source of the properties
54      * @param {Object} defaults A different object that will also be applied for default values
55      * @return {Object} returns obj
56      */
57     Ext.apply = function(object, config, defaults) {
58         if (defaults) {
59             Ext.apply(object, defaults);
60         }
61
62         if (object && config && typeof config === 'object') {
63             var i, j, k;
64
65             for (i in config) {
66                 object[i] = config[i];
67             }
68
69             if (enumerables) {
70                 for (j = enumerables.length; j--;) {
71                     k = enumerables[j];
72                     if (config.hasOwnProperty(k)) {
73                         object[k] = config[k];
74                     }
75                 }
76             }
77         }
78
79         return object;
80     };
81
82     Ext.buildSettings = Ext.apply({
83         baseCSSPrefix: 'x-',
84         scopeResetCSS: false
85     }, Ext.buildSettings || {});
86
87     Ext.apply(Ext, {
88         /**
89          * A reusable empty function
90          */
91         emptyFn: function() {},
92
93         baseCSSPrefix: Ext.buildSettings.baseCSSPrefix,
94
95         /**
96          * Copies all the properties of config to object if they don't already exist.
97          * @param {Object} object The receiver of the properties
98          * @param {Object} config The source of the properties
99          * @return {Object} returns obj
100          */
101         applyIf: function(object, config) {
102             var property;
103
104             if (object) {
105                 for (property in config) {
106                     if (object[property] === undefined) {
107                         object[property] = config[property];
108                     }
109                 }
110             }
111
112             return object;
113         },
114
115         /**
116          * Iterates either an array or an object. This method delegates to
117          * {@link Ext.Array#each Ext.Array.each} if the given value is iterable, and {@link Ext.Object#each Ext.Object.each} otherwise.
118          *
119          * @param {Object/Array} object The object or array to be iterated.
120          * @param {Function} fn The function to be called for each iteration. See and {@link Ext.Array#each Ext.Array.each} and
121          * {@link Ext.Object#each Ext.Object.each} for detailed lists of arguments passed to this function depending on the given object
122          * type that is being iterated.
123          * @param {Object} scope (Optional) The scope (`this` reference) in which the specified function is executed.
124          * Defaults to the object being iterated itself.
125          * @markdown
126          */
127         iterate: function(object, fn, scope) {
128             if (Ext.isEmpty(object)) {
129                 return;
130             }
131
132             if (scope === undefined) {
133                 scope = object;
134             }
135
136             if (Ext.isIterable(object)) {
137                 Ext.Array.each.call(Ext.Array, object, fn, scope);
138             }
139             else {
140                 Ext.Object.each.call(Ext.Object, object, fn, scope);
141             }
142         }
143     });
144
145     Ext.apply(Ext, {
146
147         /**
148          * This method deprecated. Use {@link Ext#define Ext.define} instead.
149          * @method
150          * @param {Function} superclass
151          * @param {Object} overrides
152          * @return {Function} The subclass constructor from the <tt>overrides</tt> parameter, or a generated one if not provided.
153          * @deprecated 4.0.0 Use {@link Ext#define Ext.define} instead
154          */
155         extend: function() {
156             // inline overrides
157             var objectConstructor = objectPrototype.constructor,
158                 inlineOverrides = function(o) {
159                 for (var m in o) {
160                     if (!o.hasOwnProperty(m)) {
161                         continue;
162                     }
163                     this[m] = o[m];
164                 }
165             };
166
167             return function(subclass, superclass, overrides) {
168                 // First we check if the user passed in just the superClass with overrides
169                 if (Ext.isObject(superclass)) {
170                     overrides = superclass;
171                     superclass = subclass;
172                     subclass = overrides.constructor !== objectConstructor ? overrides.constructor : function() {
173                         superclass.apply(this, arguments);
174                     };
175                 }
176
177                 if (!superclass) {
178                     Ext.Error.raise({
179                         sourceClass: 'Ext',
180                         sourceMethod: 'extend',
181                         msg: 'Attempting to extend from a class which has not been loaded on the page.'
182                     });
183                 }
184
185                 // We create a new temporary class
186                 var F = function() {},
187                     subclassProto, superclassProto = superclass.prototype;
188
189                 F.prototype = superclassProto;
190                 subclassProto = subclass.prototype = new F();
191                 subclassProto.constructor = subclass;
192                 subclass.superclass = superclassProto;
193
194                 if (superclassProto.constructor === objectConstructor) {
195                     superclassProto.constructor = superclass;
196                 }
197
198                 subclass.override = function(overrides) {
199                     Ext.override(subclass, overrides);
200                 };
201
202                 subclassProto.override = inlineOverrides;
203                 subclassProto.proto = subclassProto;
204
205                 subclass.override(overrides);
206                 subclass.extend = function(o) {
207                     return Ext.extend(subclass, o);
208                 };
209
210                 return subclass;
211             };
212         }(),
213
214         /**
215          * Proxy to {@link Ext.Base#override}. Please refer {@link Ext.Base#override} for further details.
216
217     Ext.define('My.cool.Class', {
218         sayHi: function() {
219             alert('Hi!');
220         }
221     }
222
223     Ext.override(My.cool.Class, {
224         sayHi: function() {
225             alert('About to say...');
226
227             this.callOverridden();
228         }
229     });
230
231     var cool = new My.cool.Class();
232     cool.sayHi(); // alerts 'About to say...'
233                   // alerts 'Hi!'
234
235          * Please note that `this.callOverridden()` only works if the class was previously
236          * created with {@link Ext#define)
237          *
238          * @param {Object} cls The class to override
239          * @param {Object} overrides The list of functions to add to origClass. This should be specified as an object literal
240          * containing one or more methods.
241          * @method override
242          * @markdown
243          */
244         override: function(cls, overrides) {
245             if (cls.prototype.$className) {
246                 return cls.override(overrides);
247             }
248             else {
249                 Ext.apply(cls.prototype, overrides);
250             }
251         }
252     });
253
254     // A full set of static methods to do type checking
255     Ext.apply(Ext, {
256
257         /**
258          * Returns the given value itself if it's not empty, as described in {@link Ext#isEmpty}; returns the default
259          * value (second argument) otherwise.
260          *
261          * @param {Mixed} value The value to test
262          * @param {Mixed} defaultValue The value to return if the original value is empty
263          * @param {Boolean} allowBlank (optional) true to allow zero length strings to qualify as non-empty (defaults to false)
264          * @return {Mixed} value, if non-empty, else defaultValue
265          */
266         valueFrom: function(value, defaultValue, allowBlank){
267             return Ext.isEmpty(value, allowBlank) ? defaultValue : value;
268         },
269
270         /**
271          * Returns the type of the given variable in string format. List of possible values are:
272          *
273          * - `undefined`: If the given value is `undefined`
274          * - `null`: If the given value is `null`
275          * - `string`: If the given value is a string
276          * - `number`: If the given value is a number
277          * - `boolean`: If the given value is a boolean value
278          * - `date`: If the given value is a `Date` object
279          * - `function`: If the given value is a function reference
280          * - `object`: If the given value is an object
281          * - `array`: If the given value is an array
282          * - `regexp`: If the given value is a regular expression
283          * - `element`: If the given value is a DOM Element
284          * - `textnode`: If the given value is a DOM text node and contains something other than whitespace
285          * - `whitespace`: If the given value is a DOM text node and contains only whitespace
286          *
287          * @param {Mixed} value
288          * @return {String}
289          * @markdown
290          */
291         typeOf: function(value) {
292             if (value === null) {
293                 return 'null';
294             }
295
296             var type = typeof value;
297
298             if (type === 'undefined' || type === 'string' || type === 'number' || type === 'boolean') {
299                 return type;
300             }
301
302             var typeToString = toString.call(value);
303
304             switch(typeToString) {
305                 case '[object Array]':
306                     return 'array';
307                 case '[object Date]':
308                     return 'date';
309                 case '[object Boolean]':
310                     return 'boolean';
311                 case '[object Number]':
312                     return 'number';
313                 case '[object RegExp]':
314                     return 'regexp';
315             }
316
317             if (type === 'function') {
318                 return 'function';
319             }
320
321             if (type === 'object') {
322                 if (value.nodeType !== undefined) {
323                     if (value.nodeType === 3) {
324                         return (/\S/).test(value.nodeValue) ? 'textnode' : 'whitespace';
325                     }
326                     else {
327                         return 'element';
328                     }
329                 }
330
331                 return 'object';
332             }
333
334             Ext.Error.raise({
335                 sourceClass: 'Ext',
336                 sourceMethod: 'typeOf',
337                 msg: 'Failed to determine the type of the specified value "' + value + '". This is most likely a bug.'
338             });
339         },
340
341         /**
342          * Returns true if the passed value is empty, false otherwise. The value is deemed to be empty if it is either:
343          *
344          * - `null`
345          * - `undefined`
346          * - a zero-length array
347          * - a zero-length string (Unless the `allowEmptyString` parameter is set to `true`)
348          *
349          * @param {Mixed} value The value to test
350          * @param {Boolean} allowEmptyString (optional) true to allow empty strings (defaults to false)
351          * @return {Boolean}
352          * @markdown
353          */
354         isEmpty: function(value, allowEmptyString) {
355             return (value === null) || (value === undefined) || (!allowEmptyString ? value === '' : false) || (Ext.isArray(value) && value.length === 0);
356         },
357
358         /**
359          * Returns true if the passed value is a JavaScript Array, false otherwise.
360          *
361          * @param {Mixed} target The target to test
362          * @return {Boolean}
363          * @method
364          */
365         isArray: ('isArray' in Array) ? Array.isArray : function(value) {
366             return toString.call(value) === '[object Array]';
367         },
368
369         /**
370          * Returns true if the passed value is a JavaScript Date object, false otherwise.
371          * @param {Object} object The object to test
372          * @return {Boolean}
373          */
374         isDate: function(value) {
375             return toString.call(value) === '[object Date]';
376         },
377
378         /**
379          * Returns true if the passed value is a JavaScript Object, false otherwise.
380          * @param {Mixed} value The value to test
381          * @return {Boolean}
382          * @method
383          */
384         isObject: (toString.call(null) === '[object Object]') ?
385         function(value) {
386             // check ownerDocument here as well to exclude DOM nodes
387             return value !== null && value !== undefined && toString.call(value) === '[object Object]' && value.ownerDocument === undefined;
388         } :
389         function(value) {
390             return toString.call(value) === '[object Object]';
391         },
392
393         /**
394          * Returns true if the passed value is a JavaScript 'primitive', a string, number or boolean.
395          * @param {Mixed} value The value to test
396          * @return {Boolean}
397          */
398         isPrimitive: function(value) {
399             var type = typeof value;
400
401             return type === 'string' || type === 'number' || type === 'boolean';
402         },
403
404         /**
405          * Returns true if the passed value is a JavaScript Function, false otherwise.
406          * @param {Mixed} value The value to test
407          * @return {Boolean}
408          * @method
409          */
410         isFunction:
411         // Safari 3.x and 4.x returns 'function' for typeof <NodeList>, hence we need to fall back to using
412         // Object.prorotype.toString (slower)
413         (typeof document !== 'undefined' && typeof document.getElementsByTagName('body') === 'function') ? function(value) {
414             return toString.call(value) === '[object Function]';
415         } : function(value) {
416             return typeof value === 'function';
417         },
418
419         /**
420          * Returns true if the passed value is a number. Returns false for non-finite numbers.
421          * @param {Mixed} value The value to test
422          * @return {Boolean}
423          */
424         isNumber: function(value) {
425             return typeof value === 'number' && isFinite(value);
426         },
427
428         /**
429          * Validates that a value is numeric.
430          * @param {Mixed} value Examples: 1, '1', '2.34'
431          * @return {Boolean} True if numeric, false otherwise
432          */
433         isNumeric: function(value) {
434             return !isNaN(parseFloat(value)) && isFinite(value);
435         },
436
437         /**
438          * Returns true if the passed value is a string.
439          * @param {Mixed} value The value to test
440          * @return {Boolean}
441          */
442         isString: function(value) {
443             return typeof value === 'string';
444         },
445
446         /**
447          * Returns true if the passed value is a boolean.
448          *
449          * @param {Mixed} value The value to test
450          * @return {Boolean}
451          */
452         isBoolean: function(value) {
453             return typeof value === 'boolean';
454         },
455
456         /**
457          * Returns true if the passed value is an HTMLElement
458          * @param {Mixed} value The value to test
459          * @return {Boolean}
460          */
461         isElement: function(value) {
462             return value ? value.nodeType === 1 : false;
463         },
464
465         /**
466          * Returns true if the passed value is a TextNode
467          * @param {Mixed} value The value to test
468          * @return {Boolean}
469          */
470         isTextNode: function(value) {
471             return value ? value.nodeName === "#text" : false;
472         },
473
474         /**
475          * Returns true if the passed value is defined.
476          * @param {Mixed} value The value to test
477          * @return {Boolean}
478          */
479         isDefined: function(value) {
480             return typeof value !== 'undefined';
481         },
482
483         /**
484          * Returns true if the passed value is iterable, false otherwise
485          * @param {Mixed} value The value to test
486          * @return {Boolean}
487          */
488         isIterable: function(value) {
489             return (value && typeof value !== 'string') ? value.length !== undefined : false;
490         }
491     });
492
493     Ext.apply(Ext, {
494
495         /**
496          * Clone almost any type of variable including array, object, DOM nodes and Date without keeping the old reference
497          * @param {Mixed} item The variable to clone
498          * @return {Mixed} clone
499          */
500         clone: function(item) {
501             if (item === null || item === undefined) {
502                 return item;
503             }
504
505             // DOM nodes
506             // TODO proxy this to Ext.Element.clone to handle automatic id attribute changing
507             // recursively
508             if (item.nodeType && item.cloneNode) {
509                 return item.cloneNode(true);
510             }
511
512             var type = toString.call(item);
513
514             // Date
515             if (type === '[object Date]') {
516                 return new Date(item.getTime());
517             }
518
519             var i, j, k, clone, key;
520
521             // Array
522             if (type === '[object Array]') {
523                 i = item.length;
524
525                 clone = [];
526
527                 while (i--) {
528                     clone[i] = Ext.clone(item[i]);
529                 }
530             }
531             // Object
532             else if (type === '[object Object]' && item.constructor === Object) {
533                 clone = {};
534
535                 for (key in item) {
536                     clone[key] = Ext.clone(item[key]);
537                 }
538
539                 if (enumerables) {
540                     for (j = enumerables.length; j--;) {
541                         k = enumerables[j];
542                         clone[k] = item[k];
543                     }
544                 }
545             }
546
547             return clone || item;
548         },
549
550         /**
551          * @private
552          * Generate a unique reference of Ext in the global scope, useful for sandboxing
553          */
554         getUniqueGlobalNamespace: function() {
555             var uniqueGlobalNamespace = this.uniqueGlobalNamespace;
556
557             if (uniqueGlobalNamespace === undefined) {
558                 var i = 0;
559
560                 do {
561                     uniqueGlobalNamespace = 'ExtBox' + (++i);
562                 } while (Ext.global[uniqueGlobalNamespace] !== undefined);
563
564                 Ext.global[uniqueGlobalNamespace] = Ext;
565                 this.uniqueGlobalNamespace = uniqueGlobalNamespace;
566             }
567
568             return uniqueGlobalNamespace;
569         },
570
571         /**
572          * @private
573          */
574         functionFactory: function() {
575             var args = Array.prototype.slice.call(arguments);
576
577             if (args.length > 0) {
578                 args[args.length - 1] = 'var Ext=window.' + this.getUniqueGlobalNamespace() + ';' +
579                     args[args.length - 1];
580             }
581
582             return Function.prototype.constructor.apply(Function.prototype, args);
583         }
584     });
585
586     /**
587      * Old alias to {@link Ext#typeOf}
588      * @deprecated 4.0.0 Use {@link Ext#typeOf} instead
589      * @method
590      * @alias Ext#typeOf
591      */
592     Ext.type = Ext.typeOf;
593
594 })();
595
596 /**
597  * @author Jacky Nguyen <jacky@sencha.com>
598  * @docauthor Jacky Nguyen <jacky@sencha.com>
599  * @class Ext.Version
600  *
601  * A utility class that wrap around a string version number and provide convenient
602  * method to perform comparison. See also: {@link Ext.Version#compare compare}. Example:
603
604     var version = new Ext.Version('1.0.2beta');
605     console.log("Version is " + version); // Version is 1.0.2beta
606
607     console.log(version.getMajor()); // 1
608     console.log(version.getMinor()); // 0
609     console.log(version.getPatch()); // 2
610     console.log(version.getBuild()); // 0
611     console.log(version.getRelease()); // beta
612
613     console.log(version.isGreaterThan('1.0.1')); // True
614     console.log(version.isGreaterThan('1.0.2alpha')); // True
615     console.log(version.isGreaterThan('1.0.2RC')); // False
616     console.log(version.isGreaterThan('1.0.2')); // False
617     console.log(version.isLessThan('1.0.2')); // True
618
619     console.log(version.match(1.0)); // True
620     console.log(version.match('1.0.2')); // True
621
622  * @markdown
623  */
624 (function() {
625
626 // Current core version
627 var version = '4.0.2', Version;
628     Ext.Version = Version = Ext.extend(Object, {
629
630         /**
631          * @param {String/Number} version The version number in the follow standard format: major[.minor[.patch[.build[release]]]]
632          * Examples: 1.0 or 1.2.3beta or 1.2.3.4RC
633          * @return {Ext.Version} this
634          */
635         constructor: function(version) {
636             var parts, releaseStartIndex;
637
638             if (version instanceof Version) {
639                 return version;
640             }
641
642             this.version = this.shortVersion = String(version).toLowerCase().replace(/_/g, '.').replace(/[\-+]/g, '');
643
644             releaseStartIndex = this.version.search(/([^\d\.])/);
645
646             if (releaseStartIndex !== -1) {
647                 this.release = this.version.substr(releaseStartIndex, version.length);
648                 this.shortVersion = this.version.substr(0, releaseStartIndex);
649             }
650
651             this.shortVersion = this.shortVersion.replace(/[^\d]/g, '');
652
653             parts = this.version.split('.');
654
655             this.major = parseInt(parts.shift() || 0, 10);
656             this.minor = parseInt(parts.shift() || 0, 10);
657             this.patch = parseInt(parts.shift() || 0, 10);
658             this.build = parseInt(parts.shift() || 0, 10);
659
660             return this;
661         },
662
663         /**
664          * Override the native toString method
665          * @private
666          * @return {String} version
667          */
668         toString: function() {
669             return this.version;
670         },
671
672         /**
673          * Override the native valueOf method
674          * @private
675          * @return {String} version
676          */
677         valueOf: function() {
678             return this.version;
679         },
680
681         /**
682          * Returns the major component value
683          * @return {Number} major
684          */
685         getMajor: function() {
686             return this.major || 0;
687         },
688
689         /**
690          * Returns the minor component value
691          * @return {Number} minor
692          */
693         getMinor: function() {
694             return this.minor || 0;
695         },
696
697         /**
698          * Returns the patch component value
699          * @return {Number} patch
700          */
701         getPatch: function() {
702             return this.patch || 0;
703         },
704
705         /**
706          * Returns the build component value
707          * @return {Number} build
708          */
709         getBuild: function() {
710             return this.build || 0;
711         },
712
713         /**
714          * Returns the release component value
715          * @return {Number} release
716          */
717         getRelease: function() {
718             return this.release || '';
719         },
720
721         /**
722          * Returns whether this version if greater than the supplied argument
723          * @param {String/Number} target The version to compare with
724          * @return {Boolean} True if this version if greater than the target, false otherwise
725          */
726         isGreaterThan: function(target) {
727             return Version.compare(this.version, target) === 1;
728         },
729
730         /**
731          * Returns whether this version if smaller than the supplied argument
732          * @param {String/Number} target The version to compare with
733          * @return {Boolean} True if this version if smaller than the target, false otherwise
734          */
735         isLessThan: function(target) {
736             return Version.compare(this.version, target) === -1;
737         },
738
739         /**
740          * Returns whether this version equals to the supplied argument
741          * @param {String/Number} target The version to compare with
742          * @return {Boolean} True if this version equals to the target, false otherwise
743          */
744         equals: function(target) {
745             return Version.compare(this.version, target) === 0;
746         },
747
748         /**
749          * Returns whether this version matches the supplied argument. Example:
750          * <pre><code>
751          * var version = new Ext.Version('1.0.2beta');
752          * console.log(version.match(1)); // True
753          * console.log(version.match(1.0)); // True
754          * console.log(version.match('1.0.2')); // True
755          * console.log(version.match('1.0.2RC')); // False
756          * </code></pre>
757          * @param {String/Number} target The version to compare with
758          * @return {Boolean} True if this version matches the target, false otherwise
759          */
760         match: function(target) {
761             target = String(target);
762             return this.version.substr(0, target.length) === target;
763         },
764
765         /**
766          * Returns this format: [major, minor, patch, build, release]. Useful for comparison
767          * @return {Array}
768          */
769         toArray: function() {
770             return [this.getMajor(), this.getMinor(), this.getPatch(), this.getBuild(), this.getRelease()];
771         },
772
773         /**
774          * Returns shortVersion version without dots and release
775          * @return {String}
776          */
777         getShortVersion: function() {
778             return this.shortVersion;
779         }
780     });
781
782     Ext.apply(Version, {
783         // @private
784         releaseValueMap: {
785             'dev': -6,
786             'alpha': -5,
787             'a': -5,
788             'beta': -4,
789             'b': -4,
790             'rc': -3,
791             '#': -2,
792             'p': -1,
793             'pl': -1
794         },
795
796         /**
797          * Converts a version component to a comparable value
798          *
799          * @static
800          * @param {Mixed} value The value to convert
801          * @return {Mixed}
802          */
803         getComponentValue: function(value) {
804             return !value ? 0 : (isNaN(value) ? this.releaseValueMap[value] || value : parseInt(value, 10));
805         },
806
807         /**
808          * Compare 2 specified versions, starting from left to right. If a part contains special version strings,
809          * they are handled in the following order:
810          * 'dev' < 'alpha' = 'a' < 'beta' = 'b' < 'RC' = 'rc' < '#' < 'pl' = 'p' < 'anything else'
811          *
812          * @static
813          * @param {String} current The current version to compare to
814          * @param {String} target The target version to compare to
815          * @return {Number} Returns -1 if the current version is smaller than the target version, 1 if greater, and 0 if they're equivalent
816          */
817         compare: function(current, target) {
818             var currentValue, targetValue, i;
819
820             current = new Version(current).toArray();
821             target = new Version(target).toArray();
822
823             for (i = 0; i < Math.max(current.length, target.length); i++) {
824                 currentValue = this.getComponentValue(current[i]);
825                 targetValue = this.getComponentValue(target[i]);
826
827                 if (currentValue < targetValue) {
828                     return -1;
829                 } else if (currentValue > targetValue) {
830                     return 1;
831                 }
832             }
833
834             return 0;
835         }
836     });
837
838     Ext.apply(Ext, {
839         /**
840          * @private
841          */
842         versions: {},
843
844         /**
845          * @private
846          */
847         lastRegisteredVersion: null,
848
849         /**
850          * Set version number for the given package name.
851          *
852          * @param {String} packageName The package name, for example: 'core', 'touch', 'extjs'
853          * @param {String/Ext.Version} version The version, for example: '1.2.3alpha', '2.4.0-dev'
854          * @return {Ext}
855          */
856         setVersion: function(packageName, version) {
857             Ext.versions[packageName] = new Version(version);
858             Ext.lastRegisteredVersion = Ext.versions[packageName];
859
860             return this;
861         },
862
863         /**
864          * Get the version number of the supplied package name; will return the last registered version
865          * (last Ext.setVersion call) if there's no package name given.
866          *
867          * @param {String} packageName (Optional) The package name, for example: 'core', 'touch', 'extjs'
868          * @return {Ext.Version} The version
869          */
870         getVersion: function(packageName) {
871             if (packageName === undefined) {
872                 return Ext.lastRegisteredVersion;
873             }
874
875             return Ext.versions[packageName];
876         },
877
878         /**
879          * Create a closure for deprecated code.
880          *
881     // This means Ext.oldMethod is only supported in 4.0.0beta and older.
882     // If Ext.getVersion('extjs') returns a version that is later than '4.0.0beta', for example '4.0.0RC',
883     // the closure will not be invoked
884     Ext.deprecate('extjs', '4.0.0beta', function() {
885         Ext.oldMethod = Ext.newMethod;
886
887         ...
888     });
889
890          * @param {String} packageName The package name
891          * @param {String} since The last version before it's deprecated
892          * @param {Function} closure The callback function to be executed with the specified version is less than the current version
893          * @param {Object} scope The execution scope (<tt>this</tt>) if the closure
894          * @markdown
895          */
896         deprecate: function(packageName, since, closure, scope) {
897             if (Version.compare(Ext.getVersion(packageName), since) < 1) {
898                 closure.call(scope);
899             }
900         }
901     }); // End Versioning
902
903     Ext.setVersion('core', version);
904
905 })();
906
907 /**
908  * @class Ext.String
909  *
910  * A collection of useful static methods to deal with strings
911  * @singleton
912  */
913
914 Ext.String = {
915     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,
916     escapeRe: /('|\\)/g,
917     formatRe: /\{(\d+)\}/g,
918     escapeRegexRe: /([-.*+?^${}()|[\]\/\\])/g,
919
920     /**
921      * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
922      * @param {String} value The string to encode
923      * @return {String} The encoded text
924      * @method
925      */
926     htmlEncode: (function() {
927         var entities = {
928             '&': '&amp;',
929             '>': '&gt;',
930             '<': '&lt;',
931             '"': '&quot;'
932         }, keys = [], p, regex;
933         
934         for (p in entities) {
935             keys.push(p);
936         }
937         
938         regex = new RegExp('(' + keys.join('|') + ')', 'g');
939         
940         return function(value) {
941             return (!value) ? value : String(value).replace(regex, function(match, capture) {
942                 return entities[capture];    
943             });
944         };
945     })(),
946
947     /**
948      * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
949      * @param {String} value The string to decode
950      * @return {String} The decoded text
951      * @method
952      */
953     htmlDecode: (function() {
954         var entities = {
955             '&amp;': '&',
956             '&gt;': '>',
957             '&lt;': '<',
958             '&quot;': '"'
959         }, keys = [], p, regex;
960         
961         for (p in entities) {
962             keys.push(p);
963         }
964         
965         regex = new RegExp('(' + keys.join('|') + '|&#[0-9]{1,5};' + ')', 'g');
966         
967         return function(value) {
968             return (!value) ? value : String(value).replace(regex, function(match, capture) {
969                 if (capture in entities) {
970                     return entities[capture];
971                 } else {
972                     return String.fromCharCode(parseInt(capture.substr(2), 10));
973                 }
974             });
975         };
976     })(),
977
978     /**
979      * Appends content to the query string of a URL, handling logic for whether to place
980      * a question mark or ampersand.
981      * @param {String} url The URL to append to.
982      * @param {String} string The content to append to the URL.
983      * @return (String) The resulting URL
984      */
985     urlAppend : function(url, string) {
986         if (!Ext.isEmpty(string)) {
987             return url + (url.indexOf('?') === -1 ? '?' : '&') + string;
988         }
989
990         return url;
991     },
992
993     /**
994      * Trims whitespace from either end of a string, leaving spaces within the string intact.  Example:
995      * @example
996 var s = '  foo bar  ';
997 alert('-' + s + '-');         //alerts "- foo bar -"
998 alert('-' + Ext.String.trim(s) + '-');  //alerts "-foo bar-"
999
1000      * @param {String} string The string to escape
1001      * @return {String} The trimmed string
1002      */
1003     trim: function(string) {
1004         return string.replace(Ext.String.trimRegex, "");
1005     },
1006
1007     /**
1008      * Capitalize the given string
1009      * @param {String} string
1010      * @return {String}
1011      */
1012     capitalize: function(string) {
1013         return string.charAt(0).toUpperCase() + string.substr(1);
1014     },
1015
1016     /**
1017      * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
1018      * @param {String} value The string to truncate
1019      * @param {Number} length The maximum length to allow before truncating
1020      * @param {Boolean} word True to try to find a common word break
1021      * @return {String} The converted text
1022      */
1023     ellipsis: function(value, len, word) {
1024         if (value && value.length > len) {
1025             if (word) {
1026                 var vs = value.substr(0, len - 2),
1027                 index = Math.max(vs.lastIndexOf(' '), vs.lastIndexOf('.'), vs.lastIndexOf('!'), vs.lastIndexOf('?'));
1028                 if (index !== -1 && index >= (len - 15)) {
1029                     return vs.substr(0, index) + "...";
1030                 }
1031             }
1032             return value.substr(0, len - 3) + "...";
1033         }
1034         return value;
1035     },
1036
1037     /**
1038      * Escapes the passed string for use in a regular expression
1039      * @param {String} string
1040      * @return {String}
1041      */
1042     escapeRegex: function(string) {
1043         return string.replace(Ext.String.escapeRegexRe, "\\$1");
1044     },
1045
1046     /**
1047      * Escapes the passed string for ' and \
1048      * @param {String} string The string to escape
1049      * @return {String} The escaped string
1050      */
1051     escape: function(string) {
1052         return string.replace(Ext.String.escapeRe, "\\$1");
1053     },
1054
1055     /**
1056      * Utility function that allows you to easily switch a string between two alternating values.  The passed value
1057      * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
1058      * they are already different, the first value passed in is returned.  Note that this method returns the new value
1059      * but does not change the current string.
1060      * <pre><code>
1061     // alternate sort directions
1062     sort = Ext.String.toggle(sort, 'ASC', 'DESC');
1063
1064     // instead of conditional logic:
1065     sort = (sort == 'ASC' ? 'DESC' : 'ASC');
1066        </code></pre>
1067      * @param {String} string The current string
1068      * @param {String} value The value to compare to the current string
1069      * @param {String} other The new value to use if the string already equals the first value passed in
1070      * @return {String} The new value
1071      */
1072     toggle: function(string, value, other) {
1073         return string === value ? other : value;
1074     },
1075
1076     /**
1077      * Pads the left side of a string with a specified character.  This is especially useful
1078      * for normalizing number and date strings.  Example usage:
1079      *
1080      * <pre><code>
1081 var s = Ext.String.leftPad('123', 5, '0');
1082 // s now contains the string: '00123'
1083        </code></pre>
1084      * @param {String} string The original string
1085      * @param {Number} size The total length of the output string
1086      * @param {String} character (optional) The character with which to pad the original string (defaults to empty string " ")
1087      * @return {String} The padded string
1088      */
1089     leftPad: function(string, size, character) {
1090         var result = String(string);
1091         character = character || " ";
1092         while (result.length < size) {
1093             result = character + result;
1094         }
1095         return result;
1096     },
1097
1098     /**
1099      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
1100      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
1101      * <pre><code>
1102 var cls = 'my-class', text = 'Some text';
1103 var s = Ext.String.format('&lt;div class="{0}">{1}&lt;/div>', cls, text);
1104 // s now contains the string: '&lt;div class="my-class">Some text&lt;/div>'
1105        </code></pre>
1106      * @param {String} string The tokenized string to be formatted
1107      * @param {String} value1 The value to replace token {0}
1108      * @param {String} value2 Etc...
1109      * @return {String} The formatted string
1110      */
1111     format: function(format) {
1112         var args = Ext.Array.toArray(arguments, 1);
1113         return format.replace(Ext.String.formatRe, function(m, i) {
1114             return args[i];
1115         });
1116     }
1117 };
1118
1119 /**
1120  * @class Ext.Number
1121  *
1122  * A collection of useful static methods to deal with numbers
1123  * @singleton
1124  */
1125
1126 (function() {
1127
1128 var isToFixedBroken = (0.9).toFixed() !== '1';
1129
1130 Ext.Number = {
1131     /**
1132      * Checks whether or not the passed number is within a desired range.  If the number is already within the
1133      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
1134      * exceeded. Note that this method returns the constrained value but does not change the current number.
1135      * @param {Number} number The number to check
1136      * @param {Number} min The minimum number in the range
1137      * @param {Number} max The maximum number in the range
1138      * @return {Number} The constrained value if outside the range, otherwise the current value
1139      */
1140     constrain: function(number, min, max) {
1141         number = parseFloat(number);
1142
1143         if (!isNaN(min)) {
1144             number = Math.max(number, min);
1145         }
1146         if (!isNaN(max)) {
1147             number = Math.min(number, max);
1148         }
1149         return number;
1150     },
1151
1152     /**
1153      * Snaps the passed number between stopping points based upon a passed increment value.
1154      * @param {Number} value The unsnapped value.
1155      * @param {Number} increment The increment by which the value must move.
1156      * @param {Number} minValue The minimum value to which the returned value must be constrained. Overrides the increment..
1157      * @param {Number} maxValue The maximum value to which the returned value must be constrained. Overrides the increment..
1158      * @return {Number} The value of the nearest snap target.
1159      */
1160     snap : function(value, increment, minValue, maxValue) {
1161         var newValue = value,
1162             m;
1163
1164         if (!(increment && value)) {
1165             return value;
1166         }
1167         m = value % increment;
1168         if (m !== 0) {
1169             newValue -= m;
1170             if (m * 2 >= increment) {
1171                 newValue += increment;
1172             } else if (m * 2 < -increment) {
1173                 newValue -= increment;
1174             }
1175         }
1176         return Ext.Number.constrain(newValue, minValue,  maxValue);
1177     },
1178
1179     /**
1180      * Formats a number using fixed-point notation
1181      * @param {Number} value The number to format
1182      * @param {Number} precision The number of digits to show after the decimal point
1183      */
1184     toFixed: function(value, precision) {
1185         if (isToFixedBroken) {
1186             precision = precision || 0;
1187             var pow = Math.pow(10, precision);
1188             return (Math.round(value * pow) / pow).toFixed(precision);
1189         }
1190
1191         return value.toFixed(precision);
1192     },
1193
1194     /**
1195      * Validate that a value is numeric and convert it to a number if necessary. Returns the specified default value if
1196      * it is not.
1197
1198 Ext.Number.from('1.23', 1); // returns 1.23
1199 Ext.Number.from('abc', 1); // returns 1
1200
1201      * @param {Mixed} value
1202      * @param {Number} defaultValue The value to return if the original value is non-numeric
1203      * @return {Number} value, if numeric, defaultValue otherwise
1204      */
1205     from: function(value, defaultValue) {
1206         if (isFinite(value)) {
1207             value = parseFloat(value);
1208         }
1209
1210         return !isNaN(value) ? value : defaultValue;
1211     }
1212 };
1213
1214 })();
1215
1216 /**
1217  * This method is deprecated, please use {@link Ext.Number#from Ext.Number.from} instead
1218  *
1219  * @deprecated 4.0.0 Replaced by Ext.Number.from
1220  * @member Ext
1221  * @method num
1222  */
1223 Ext.num = function() {
1224     return Ext.Number.from.apply(this, arguments);
1225 };
1226 /**
1227  * @author Jacky Nguyen <jacky@sencha.com>
1228  * @docauthor Jacky Nguyen <jacky@sencha.com>
1229  * @class Ext.Array
1230  *
1231  * A set of useful static methods to deal with arrays; provide missing methods for older browsers.
1232
1233  * @singleton
1234  * @markdown
1235  */
1236 (function() {
1237
1238     var arrayPrototype = Array.prototype,
1239         slice = arrayPrototype.slice,
1240         supportsSplice = function () {
1241             var array = [],
1242                 lengthBefore,
1243                 j = 20;
1244
1245             if (!array.splice) {
1246                 return false;
1247             }
1248
1249             // This detects a bug in IE8 splice method:
1250             // see http://social.msdn.microsoft.com/Forums/en-US/iewebdevelopment/thread/6e946d03-e09f-4b22-a4dd-cd5e276bf05a/
1251
1252             while (j--) {
1253                 array.push("A");
1254             }
1255
1256             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");
1257
1258             lengthBefore = array.length; //41
1259             array.splice(13, 0, "XXX"); // add one element
1260
1261             if (lengthBefore+1 != array.length) {
1262                 return false;
1263             }
1264             // end IE8 bug
1265
1266             return true;
1267         }(),
1268         supportsForEach = 'forEach' in arrayPrototype,
1269         supportsMap = 'map' in arrayPrototype,
1270         supportsIndexOf = 'indexOf' in arrayPrototype,
1271         supportsEvery = 'every' in arrayPrototype,
1272         supportsSome = 'some' in arrayPrototype,
1273         supportsFilter = 'filter' in arrayPrototype,
1274         supportsSort = function() {
1275             var a = [1,2,3,4,5].sort(function(){ return 0; });
1276             return a[0] === 1 && a[1] === 2 && a[2] === 3 && a[3] === 4 && a[4] === 5;
1277         }(),
1278         supportsSliceOnNodeList = true,
1279         ExtArray;
1280
1281     try {
1282         // IE 6 - 8 will throw an error when using Array.prototype.slice on NodeList
1283         if (typeof document !== 'undefined') {
1284             slice.call(document.getElementsByTagName('body'));
1285         }
1286     } catch (e) {
1287         supportsSliceOnNodeList = false;
1288     }
1289
1290     function fixArrayIndex (array, index) {
1291         return (index < 0) ? Math.max(0, array.length + index)
1292                            : Math.min(array.length, index);
1293     }
1294
1295     /*
1296     Does the same work as splice, but with a slightly more convenient signature. The splice
1297     method has bugs in IE8, so this is the implementation we use on that platform.
1298
1299     The rippling of items in the array can be tricky. Consider two use cases:
1300
1301                   index=2
1302                   removeCount=2
1303                  /=====\
1304         +---+---+---+---+---+---+---+---+
1305         | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
1306         +---+---+---+---+---+---+---+---+
1307                          /  \/  \/  \/  \
1308                         /   /\  /\  /\   \
1309                        /   /  \/  \/  \   +--------------------------+
1310                       /   /   /\  /\   +--------------------------+   \
1311                      /   /   /  \/  +--------------------------+   \   \
1312                     /   /   /   /+--------------------------+   \   \   \
1313                    /   /   /   /                             \   \   \   \
1314                   v   v   v   v                               v   v   v   v
1315         +---+---+---+---+---+---+       +---+---+---+---+---+---+---+---+---+
1316         | 0 | 1 | 4 | 5 | 6 | 7 |       | 0 | 1 | a | b | c | 4 | 5 | 6 | 7 |
1317         +---+---+---+---+---+---+       +---+---+---+---+---+---+---+---+---+
1318         A                               B        \=========/
1319                                                  insert=[a,b,c]
1320
1321     In case A, it is obvious that copying of [4,5,6,7] must be left-to-right so
1322     that we don't end up with [0,1,6,7,6,7]. In case B, we have the opposite; we
1323     must go right-to-left or else we would end up with [0,1,a,b,c,4,4,4,4].
1324     */
1325     function replaceSim (array, index, removeCount, insert) {
1326         var add = insert ? insert.length : 0,
1327             length = array.length,
1328             pos = fixArrayIndex(array, index);
1329
1330         // we try to use Array.push when we can for efficiency...
1331         if (pos === length) {
1332             if (add) {
1333                 array.push.apply(array, insert);
1334             }
1335         } else {
1336             var remove = Math.min(removeCount, length - pos),
1337                 tailOldPos = pos + remove,
1338                 tailNewPos = tailOldPos + add - remove,
1339                 tailCount = length - tailOldPos,
1340                 lengthAfterRemove = length - remove,
1341                 i;
1342
1343             if (tailNewPos < tailOldPos) { // case A
1344                 for (i = 0; i < tailCount; ++i) {
1345                     array[tailNewPos+i] = array[tailOldPos+i];
1346                 }
1347             } else if (tailNewPos > tailOldPos) { // case B
1348                 for (i = tailCount; i--; ) {
1349                     array[tailNewPos+i] = array[tailOldPos+i];
1350                 }
1351             } // else, add == remove (nothing to do)
1352
1353             if (add && pos === lengthAfterRemove) {
1354                 array.length = lengthAfterRemove; // truncate array
1355                 array.push.apply(array, insert);
1356             } else {
1357                 array.length = lengthAfterRemove + add; // reserves space
1358                 for (i = 0; i < add; ++i) {
1359                     array[pos+i] = insert[i];
1360                 }
1361             }
1362         }
1363
1364         return array;
1365     }
1366
1367     function replaceNative (array, index, removeCount, insert) {
1368         if (insert && insert.length) {
1369             if (index < array.length) {
1370                 array.splice.apply(array, [index, removeCount].concat(insert));
1371             } else {
1372                 array.push.apply(array, insert);
1373             }
1374         } else {
1375             array.splice(index, removeCount);
1376         }
1377         return array;
1378     }
1379
1380     function eraseSim (array, index, removeCount) {
1381         return replaceSim(array, index, removeCount);
1382     }
1383
1384     function eraseNative (array, index, removeCount) {
1385         array.splice(index, removeCount);
1386         return array;
1387     }
1388
1389     function spliceSim (array, index, removeCount) {
1390         var pos = fixArrayIndex(array, index),
1391             removed = array.slice(index, fixArrayIndex(array, pos+removeCount));
1392
1393         if (arguments.length < 4) {
1394             replaceSim(array, pos, removeCount);
1395         } else {
1396             replaceSim(array, pos, removeCount, slice.call(arguments, 3));
1397         }
1398
1399         return removed;
1400     }
1401
1402     function spliceNative (array) {
1403         return array.splice.apply(array, slice.call(arguments, 1));
1404     }
1405
1406     var erase = supportsSplice ? eraseNative : eraseSim,
1407         replace = supportsSplice ? replaceNative : replaceSim,
1408         splice = supportsSplice ? spliceNative : spliceSim;
1409
1410     // NOTE: from here on, use erase, replace or splice (not native methods)...
1411
1412     ExtArray = Ext.Array = {
1413         /**
1414          * Iterates an array or an iterable value and invoke the given callback function for each item.
1415          *
1416          *     var countries = ['Vietnam', 'Singapore', 'United States', 'Russia'];
1417          *
1418          *     Ext.Array.each(countries, function(name, index, countriesItSelf) {
1419          *         console.log(name);
1420          *     });
1421          *
1422          *     var sum = function() {
1423          *         var sum = 0;
1424          *
1425          *         Ext.Array.each(arguments, function(value) {
1426          *             sum += value;
1427          *         });
1428          *
1429          *         return sum;
1430          *     };
1431          *
1432          *     sum(1, 2, 3); // returns 6
1433          *
1434          * The iteration can be stopped by returning false in the function callback.
1435          *
1436          *     Ext.Array.each(countries, function(name, index, countriesItSelf) {
1437          *         if (name === 'Singapore') {
1438          *             return false; // break here
1439          *         }
1440          *     });
1441          *
1442          * {@link Ext#each Ext.each} is alias for {@link Ext.Array#each Ext.Array.each}
1443          *
1444          * @param {Array/NodeList/Mixed} iterable The value to be iterated. If this
1445          * argument is not iterable, the callback function is called once.
1446          * @param {Function} fn The callback function. If it returns false, the iteration stops and this method returns
1447          * the current `index`. Arguments passed to this callback function are:
1448          *
1449          * - `item` : Mixed - The item at the current `index` in the passed `array`
1450          * - `index` : Number - The current `index` within the `array`
1451          * - `allItems` : Array/NodeList/Mixed - The `array` passed as the first argument to `Ext.Array.each`
1452          *
1453          * @param {Object} scope (Optional) The scope (`this` reference) in which the specified function is executed.
1454          * @param {Boolean} reverse (Optional) Reverse the iteration order (loop from the end to the beginning)
1455          * Defaults false
1456          * @return {Boolean} See description for the `fn` parameter.
1457          */
1458         each: function(array, fn, scope, reverse) {
1459             array = ExtArray.from(array);
1460
1461             var i,
1462                 ln = array.length;
1463
1464             if (reverse !== true) {
1465                 for (i = 0; i < ln; i++) {
1466                     if (fn.call(scope || array[i], array[i], i, array) === false) {
1467                         return i;
1468                     }
1469                 }
1470             }
1471             else {
1472                 for (i = ln - 1; i > -1; i--) {
1473                     if (fn.call(scope || array[i], array[i], i, array) === false) {
1474                         return i;
1475                     }
1476                 }
1477             }
1478
1479             return true;
1480         },
1481
1482         /**
1483          * Iterates an array and invoke the given callback function for each item. Note that this will simply
1484          * delegate to the native Array.prototype.forEach method if supported.
1485          * It doesn't support stopping the iteration by returning false in the callback function like
1486          * {@link Ext.Array#each}. However, performance could be much better in modern browsers comparing with
1487          * {@link Ext.Array#each}
1488          *
1489          * @param {Array} array The array to iterate
1490          * @param {Function} fn The function callback, to be invoked these arguments:
1491          *
1492          * - `item` : Mixed - The item at the current `index` in the passed `array`
1493          * - `index` : Number - The current `index` within the `array`
1494          * - `allItems` : Array - The `array` itself which was passed as the first argument
1495          *
1496          * @param {Object} scope (Optional) The execution scope (`this`) in which the specified function is executed.
1497          */
1498         forEach: function(array, fn, scope) {
1499             if (supportsForEach) {
1500                 return array.forEach(fn, scope);
1501             }
1502
1503             var i = 0,
1504                 ln = array.length;
1505
1506             for (; i < ln; i++) {
1507                 fn.call(scope, array[i], i, array);
1508             }
1509         },
1510
1511         /**
1512          * Get the index of the provided `item` in the given `array`, a supplement for the
1513          * missing arrayPrototype.indexOf in Internet Explorer.
1514          *
1515          * @param {Array} array The array to check
1516          * @param {Mixed} item The item to look for
1517          * @param {Number} from (Optional) The index at which to begin the search
1518          * @return {Number} The index of item in the array (or -1 if it is not found)
1519          */
1520         indexOf: function(array, item, from) {
1521             if (supportsIndexOf) {
1522                 return array.indexOf(item, from);
1523             }
1524
1525             var i, length = array.length;
1526
1527             for (i = (from < 0) ? Math.max(0, length + from) : from || 0; i < length; i++) {
1528                 if (array[i] === item) {
1529                     return i;
1530                 }
1531             }
1532
1533             return -1;
1534         },
1535
1536         /**
1537          * Checks whether or not the given `array` contains the specified `item`
1538          *
1539          * @param {Array} array The array to check
1540          * @param {Mixed} item The item to look for
1541          * @return {Boolean} True if the array contains the item, false otherwise
1542          */
1543         contains: function(array, item) {
1544             if (supportsIndexOf) {
1545                 return array.indexOf(item) !== -1;
1546             }
1547
1548             var i, ln;
1549
1550             for (i = 0, ln = array.length; i < ln; i++) {
1551                 if (array[i] === item) {
1552                     return true;
1553                 }
1554             }
1555
1556             return false;
1557         },
1558
1559         /**
1560          * Converts any iterable (numeric indices and a length property) into a true array.
1561          *
1562          *     function test() {
1563          *         var args = Ext.Array.toArray(arguments),
1564          *             fromSecondToLastArgs = Ext.Array.toArray(arguments, 1);
1565          *
1566          *         alert(args.join(' '));
1567          *         alert(fromSecondToLastArgs.join(' '));
1568          *     }
1569          *
1570          *     test('just', 'testing', 'here'); // alerts 'just testing here';
1571          *                                      // alerts 'testing here';
1572          *
1573          *     Ext.Array.toArray(document.getElementsByTagName('div')); // will convert the NodeList into an array
1574          *     Ext.Array.toArray('splitted'); // returns ['s', 'p', 'l', 'i', 't', 't', 'e', 'd']
1575          *     Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i']
1576          *
1577          * {@link Ext#toArray Ext.toArray} is alias for {@link Ext.Array#toArray Ext.Array.toArray}
1578          *
1579          * @param {Mixed} iterable the iterable object to be turned into a true Array.
1580          * @param {Number} start (Optional) a zero-based index that specifies the start of extraction. Defaults to 0
1581          * @param {Number} end (Optional) a zero-based index that specifies the end of extraction. Defaults to the last
1582          * index of the iterable value
1583          * @return {Array} array
1584          */
1585         toArray: function(iterable, start, end){
1586             if (!iterable || !iterable.length) {
1587                 return [];
1588             }
1589
1590             if (typeof iterable === 'string') {
1591                 iterable = iterable.split('');
1592             }
1593
1594             if (supportsSliceOnNodeList) {
1595                 return slice.call(iterable, start || 0, end || iterable.length);
1596             }
1597
1598             var array = [],
1599                 i;
1600
1601             start = start || 0;
1602             end = end ? ((end < 0) ? iterable.length + end : end) : iterable.length;
1603
1604             for (i = start; i < end; i++) {
1605                 array.push(iterable[i]);
1606             }
1607
1608             return array;
1609         },
1610
1611         /**
1612          * Plucks the value of a property from each item in the Array. Example:
1613          *
1614          *     Ext.Array.pluck(Ext.query("p"), "className"); // [el1.className, el2.className, ..., elN.className]
1615          *
1616          * @param {Array|NodeList} array The Array of items to pluck the value from.
1617          * @param {String} propertyName The property name to pluck from each element.
1618          * @return {Array} The value from each item in the Array.
1619          */
1620         pluck: function(array, propertyName) {
1621             var ret = [],
1622                 i, ln, item;
1623
1624             for (i = 0, ln = array.length; i < ln; i++) {
1625                 item = array[i];
1626
1627                 ret.push(item[propertyName]);
1628             }
1629
1630             return ret;
1631         },
1632
1633         /**
1634          * Creates a new array with the results of calling a provided function on every element in this array.
1635          *
1636          * @param {Array} array
1637          * @param {Function} fn Callback function for each item
1638          * @param {Object} scope Callback function scope
1639          * @return {Array} results
1640          */
1641         map: function(array, fn, scope) {
1642             if (supportsMap) {
1643                 return array.map(fn, scope);
1644             }
1645
1646             var results = [],
1647                 i = 0,
1648                 len = array.length;
1649
1650             for (; i < len; i++) {
1651                 results[i] = fn.call(scope, array[i], i, array);
1652             }
1653
1654             return results;
1655         },
1656
1657         /**
1658          * Executes the specified function for each array element until the function returns a falsy value.
1659          * If such an item is found, the function will return false immediately.
1660          * Otherwise, it will return true.
1661          *
1662          * @param {Array} array
1663          * @param {Function} fn Callback function for each item
1664          * @param {Object} scope Callback function scope
1665          * @return {Boolean} True if no false value is returned by the callback function.
1666          */
1667         every: function(array, fn, scope) {
1668             if (!fn) {
1669                 Ext.Error.raise('Ext.Array.every must have a callback function passed as second argument.');
1670             }
1671             if (supportsEvery) {
1672                 return array.every(fn, scope);
1673             }
1674
1675             var i = 0,
1676                 ln = array.length;
1677
1678             for (; i < ln; ++i) {
1679                 if (!fn.call(scope, array[i], i, array)) {
1680                     return false;
1681                 }
1682             }
1683
1684             return true;
1685         },
1686
1687         /**
1688          * Executes the specified function for each array element until the function returns a truthy value.
1689          * If such an item is found, the function will return true immediately. Otherwise, it will return false.
1690          *
1691          * @param {Array} array
1692          * @param {Function} fn Callback function for each item
1693          * @param {Object} scope Callback function scope
1694          * @return {Boolean} True if the callback function returns a truthy value.
1695          */
1696         some: function(array, fn, scope) {
1697             if (!fn) {
1698                 Ext.Error.raise('Ext.Array.some must have a callback function passed as second argument.');
1699             }
1700             if (supportsSome) {
1701                 return array.some(fn, scope);
1702             }
1703
1704             var i = 0,
1705                 ln = array.length;
1706
1707             for (; i < ln; ++i) {
1708                 if (fn.call(scope, array[i], i, array)) {
1709                     return true;
1710                 }
1711             }
1712
1713             return false;
1714         },
1715
1716         /**
1717          * Filter through an array and remove empty item as defined in {@link Ext#isEmpty Ext.isEmpty}
1718          *
1719          * See {@link Ext.Array#filter}
1720          *
1721          * @param {Array} array
1722          * @return {Array} results
1723          */
1724         clean: function(array) {
1725             var results = [],
1726                 i = 0,
1727                 ln = array.length,
1728                 item;
1729
1730             for (; i < ln; i++) {
1731                 item = array[i];
1732
1733                 if (!Ext.isEmpty(item)) {
1734                     results.push(item);
1735                 }
1736             }
1737
1738             return results;
1739         },
1740
1741         /**
1742          * Returns a new array with unique items
1743          *
1744          * @param {Array} array
1745          * @return {Array} results
1746          */
1747         unique: function(array) {
1748             var clone = [],
1749                 i = 0,
1750                 ln = array.length,
1751                 item;
1752
1753             for (; i < ln; i++) {
1754                 item = array[i];
1755
1756                 if (ExtArray.indexOf(clone, item) === -1) {
1757                     clone.push(item);
1758                 }
1759             }
1760
1761             return clone;
1762         },
1763
1764         /**
1765          * Creates a new array with all of the elements of this array for which
1766          * the provided filtering function returns true.
1767          *
1768          * @param {Array} array
1769          * @param {Function} fn Callback function for each item
1770          * @param {Object} scope Callback function scope
1771          * @return {Array} results
1772          */
1773         filter: function(array, fn, scope) {
1774             if (supportsFilter) {
1775                 return array.filter(fn, scope);
1776             }
1777
1778             var results = [],
1779                 i = 0,
1780                 ln = array.length;
1781
1782             for (; i < ln; i++) {
1783                 if (fn.call(scope, array[i], i, array)) {
1784                     results.push(array[i]);
1785                 }
1786             }
1787
1788             return results;
1789         },
1790
1791         /**
1792          * Converts a value to an array if it's not already an array; returns:
1793          *
1794          * - An empty array if given value is `undefined` or `null`
1795          * - Itself if given value is already an array
1796          * - An array copy if given value is {@link Ext#isIterable iterable} (arguments, NodeList and alike)
1797          * - An array with one item which is the given value, otherwise
1798          *
1799          * @param {Array/Mixed} value The value to convert to an array if it's not already is an array
1800          * @param {Boolean} (Optional) newReference True to clone the given array and return a new reference if necessary,
1801          * defaults to false
1802          * @return {Array} array
1803          */
1804         from: function(value, newReference) {
1805             if (value === undefined || value === null) {
1806                 return [];
1807             }
1808
1809             if (Ext.isArray(value)) {
1810                 return (newReference) ? slice.call(value) : value;
1811             }
1812
1813             if (value && value.length !== undefined && typeof value !== 'string') {
1814                 return Ext.toArray(value);
1815             }
1816
1817             return [value];
1818         },
1819
1820         /**
1821          * Removes the specified item from the array if it exists
1822          *
1823          * @param {Array} array The array
1824          * @param {Mixed} item The item to remove
1825          * @return {Array} The passed array itself
1826          */
1827         remove: function(array, item) {
1828             var index = ExtArray.indexOf(array, item);
1829
1830             if (index !== -1) {
1831                 erase(array, index, 1);
1832             }
1833
1834             return array;
1835         },
1836
1837         /**
1838          * Push an item into the array only if the array doesn't contain it yet
1839          *
1840          * @param {Array} array The array
1841          * @param {Mixed} item The item to include
1842          */
1843         include: function(array, item) {
1844             if (!ExtArray.contains(array, item)) {
1845                 array.push(item);
1846             }
1847         },
1848
1849         /**
1850          * Clone a flat array without referencing the previous one. Note that this is different
1851          * from Ext.clone since it doesn't handle recursive cloning. It's simply a convenient, easy-to-remember method
1852          * for Array.prototype.slice.call(array)
1853          *
1854          * @param {Array} array The array
1855          * @return {Array} The clone array
1856          */
1857         clone: function(array) {
1858             return slice.call(array);
1859         },
1860
1861         /**
1862          * Merge multiple arrays into one with unique items.
1863          *
1864          * {@link Ext.Array#union} is alias for {@link Ext.Array#merge}
1865          *
1866          * @param {Array} array1
1867          * @param {Array} array2
1868          * @param {Array} etc
1869          * @return {Array} merged
1870          */
1871         merge: function() {
1872             var args = slice.call(arguments),
1873                 array = [],
1874                 i, ln;
1875
1876             for (i = 0, ln = args.length; i < ln; i++) {
1877                 array = array.concat(args[i]);
1878             }
1879
1880             return ExtArray.unique(array);
1881         },
1882
1883         /**
1884          * Merge multiple arrays into one with unique items that exist in all of the arrays.
1885          *
1886          * @param {Array} array1
1887          * @param {Array} array2
1888          * @param {Array} etc
1889          * @return {Array} intersect
1890          */
1891         intersect: function() {
1892             var intersect = [],
1893                 arrays = slice.call(arguments),
1894                 i, j, k, minArray, array, x, y, ln, arraysLn, arrayLn;
1895
1896             if (!arrays.length) {
1897                 return intersect;
1898             }
1899
1900             // Find the smallest array
1901             for (i = x = 0,ln = arrays.length; i < ln,array = arrays[i]; i++) {
1902                 if (!minArray || array.length < minArray.length) {
1903                     minArray = array;
1904                     x = i;
1905                 }
1906             }
1907
1908             minArray = ExtArray.unique(minArray);
1909             erase(arrays, x, 1);
1910
1911             // Use the smallest unique'd array as the anchor loop. If the other array(s) do contain
1912             // an item in the small array, we're likely to find it before reaching the end
1913             // of the inner loop and can terminate the search early.
1914             for (i = 0,ln = minArray.length; i < ln,x = minArray[i]; i++) {
1915                 var count = 0;
1916
1917                 for (j = 0,arraysLn = arrays.length; j < arraysLn,array = arrays[j]; j++) {
1918                     for (k = 0,arrayLn = array.length; k < arrayLn,y = array[k]; k++) {
1919                         if (x === y) {
1920                             count++;
1921                             break;
1922                         }
1923                     }
1924                 }
1925
1926                 if (count === arraysLn) {
1927                     intersect.push(x);
1928                 }
1929             }
1930
1931             return intersect;
1932         },
1933
1934         /**
1935          * Perform a set difference A-B by subtracting all items in array B from array A.
1936          *
1937          * @param {Array} arrayA
1938          * @param {Array} arrayB
1939          * @return {Array} difference
1940          */
1941         difference: function(arrayA, arrayB) {
1942             var clone = slice.call(arrayA),
1943                 ln = clone.length,
1944                 i, j, lnB;
1945
1946             for (i = 0,lnB = arrayB.length; i < lnB; i++) {
1947                 for (j = 0; j < ln; j++) {
1948                     if (clone[j] === arrayB[i]) {
1949                         erase(clone, j, 1);
1950                         j--;
1951                         ln--;
1952                     }
1953                 }
1954             }
1955
1956             return clone;
1957         },
1958
1959         /**
1960          * Returns a shallow copy of a part of an array. This is equivalent to the native
1961          * call "Array.prototype.slice.call(array, begin, end)". This is often used when "array"
1962          * is "arguments" since the arguments object does not supply a slice method but can
1963          * be the context object to Array.prototype.slice.
1964          *
1965          * @param {Array} array The array (or arguments object).
1966          * @param {Number} begin The index at which to begin. Negative values are offsets from
1967          * the end of the array.
1968          * @param {Number} end The index at which to end. The copied items do not include
1969          * end. Negative values are offsets from the end of the array. If end is omitted,
1970          * all items up to the end of the array are copied.
1971          * @return {Array} The copied piece of the array.
1972          */
1973         slice: function(array, begin, end) {
1974             return slice.call(array, begin, end);
1975         },
1976
1977         /**
1978          * Sorts the elements of an Array.
1979          * By default, this method sorts the elements alphabetically and ascending.
1980          *
1981          * @param {Array} array The array to sort.
1982          * @param {Function} sortFn (optional) The comparison function.
1983          * @return {Array} The sorted array.
1984          */
1985         sort: function(array, sortFn) {
1986             if (supportsSort) {
1987                 if (sortFn) {
1988                     return array.sort(sortFn);
1989                 } else {
1990                     return array.sort();
1991                 }
1992             }
1993
1994             var length = array.length,
1995                 i = 0,
1996                 comparison,
1997                 j, min, tmp;
1998
1999             for (; i < length; i++) {
2000                 min = i;
2001                 for (j = i + 1; j < length; j++) {
2002                     if (sortFn) {
2003                         comparison = sortFn(array[j], array[min]);
2004                         if (comparison < 0) {
2005                             min = j;
2006                         }
2007                     } else if (array[j] < array[min]) {
2008                         min = j;
2009                     }
2010                 }
2011                 if (min !== i) {
2012                     tmp = array[i];
2013                     array[i] = array[min];
2014                     array[min] = tmp;
2015                 }
2016             }
2017
2018             return array;
2019         },
2020
2021         /**
2022          * Recursively flattens into 1-d Array. Injects Arrays inline.
2023          *
2024          */
2025         flatten: function(array) {
2026             var worker = [];
2027
2028             function rFlatten(a) {
2029                 var i, ln, v;
2030
2031                 for (i = 0, ln = a.length; i < ln; i++) {
2032                     v = a[i];
2033
2034                     if (Ext.isArray(v)) {
2035                         rFlatten(v);
2036                     } else {
2037                         worker.push(v);
2038                     }
2039                 }
2040
2041                 return worker;
2042             }
2043
2044             return rFlatten(array);
2045         },
2046
2047         /**
2048          * Returns the minimum value in the Array.
2049          *
2050          * @param {Array|NodeList} array The Array from which to select the minimum value.
2051          * @param {Function} comparisonFn (optional) a function to perform the comparision which determines minimization.
2052          * If omitted the "<" operator will be used. Note: gt = 1; eq = 0; lt = -1
2053          * @return {Mixed} minValue The minimum value
2054          */
2055         min: function(array, comparisonFn) {
2056             var min = array[0],
2057                 i, ln, item;
2058
2059             for (i = 0, ln = array.length; i < ln; i++) {
2060                 item = array[i];
2061
2062                 if (comparisonFn) {
2063                     if (comparisonFn(min, item) === 1) {
2064                         min = item;
2065                     }
2066                 }
2067                 else {
2068                     if (item < min) {
2069                         min = item;
2070                     }
2071                 }
2072             }
2073
2074             return min;
2075         },
2076
2077         /**
2078          * Returns the maximum value in the Array.
2079          *
2080          * @param {Array|NodeList} array The Array from which to select the maximum value.
2081          * @param {Function} comparisonFn (optional) a function to perform the comparision which determines maximization.
2082          * If omitted the ">" operator will be used. Note: gt = 1; eq = 0; lt = -1
2083          * @return {Mixed} maxValue The maximum value
2084          */
2085         max: function(array, comparisonFn) {
2086             var max = array[0],
2087                 i, ln, item;
2088
2089             for (i = 0, ln = array.length; i < ln; i++) {
2090                 item = array[i];
2091
2092                 if (comparisonFn) {
2093                     if (comparisonFn(max, item) === -1) {
2094                         max = item;
2095                     }
2096                 }
2097                 else {
2098                     if (item > max) {
2099                         max = item;
2100                     }
2101                 }
2102             }
2103
2104             return max;
2105         },
2106
2107         /**
2108          * Calculates the mean of all items in the array.
2109          *
2110          * @param {Array} array The Array to calculate the mean value of.
2111          * @return {Number} The mean.
2112          */
2113         mean: function(array) {
2114             return array.length > 0 ? ExtArray.sum(array) / array.length : undefined;
2115         },
2116
2117         /**
2118          * Calculates the sum of all items in the given array.
2119          *
2120          * @param {Array} array The Array to calculate the sum value of.
2121          * @return {Number} The sum.
2122          */
2123         sum: function(array) {
2124             var sum = 0,
2125                 i, ln, item;
2126
2127             for (i = 0,ln = array.length; i < ln; i++) {
2128                 item = array[i];
2129
2130                 sum += item;
2131             }
2132
2133             return sum;
2134         },
2135
2136         _replaceSim: replaceSim, // for unit testing
2137         _spliceSim: spliceSim,
2138
2139         /**
2140          * Removes items from an array. This is functionally equivalent to the splice method
2141          * of Array, but works around bugs in IE8's splice method and does not copy the
2142          * removed elements in order to return them (because very often they are ignored).
2143          *
2144          * @param {Array} array The Array on which to replace.
2145          * @param {Number} index The index in the array at which to operate.
2146          * @param {Number} removeCount The number of items to remove at index.
2147          * @return {Array} The array passed.
2148          * @method
2149          */
2150         erase: erase,
2151
2152         /**
2153          * Inserts items in to an array.
2154          * 
2155          * @param {Array} array The Array on which to replace.
2156          * @param {Number} index The index in the array at which to operate.
2157          * @param {Array} items The array of items to insert at index.
2158          * @return {Array} The array passed.
2159          */
2160         insert: function (array, index, items) {
2161             return replace(array, index, 0, items);
2162         },
2163
2164         /**
2165          * Replaces items in an array. This is functionally equivalent to the splice method
2166          * of Array, but works around bugs in IE8's splice method and is often more convenient
2167          * to call because it accepts an array of items to insert rather than use a variadic
2168          * argument list.
2169          * 
2170          * @param {Array} array The Array on which to replace.
2171          * @param {Number} index The index in the array at which to operate.
2172          * @param {Number} removeCount The number of items to remove at index (can be 0).
2173          * @param {Array} insert An optional array of items to insert at index.
2174          * @return {Array} The array passed.
2175          * @method
2176          */
2177         replace: replace,
2178
2179         /**
2180          * Replaces items in an array. This is equivalent to the splice method of Array, but
2181          * works around bugs in IE8's splice method. The signature is exactly the same as the
2182          * splice method except that the array is the first argument. All arguments following
2183          * removeCount are inserted in the array at index.
2184          *
2185          * @param {Array} array The Array on which to replace.
2186          * @param {Number} index The index in the array at which to operate.
2187          * @param {Number} removeCount The number of items to remove at index (can be 0).
2188          * @return {Array} An array containing the removed items.
2189          * @method
2190          */
2191         splice: splice
2192     };
2193
2194     /**
2195      * @method
2196      * @member Ext
2197      * @alias Ext.Array#each
2198      */
2199     Ext.each = ExtArray.each;
2200
2201     /**
2202      * @method
2203      * @member Ext.Array
2204      * @alias Ext.Array#merge
2205      */
2206     ExtArray.union = ExtArray.merge;
2207
2208     /**
2209      * Old alias to {@link Ext.Array#min}
2210      * @deprecated 4.0.0 Use {@link Ext.Array#min} instead
2211      * @method
2212      * @member Ext
2213      * @alias Ext.Array#min
2214      */
2215     Ext.min = ExtArray.min;
2216
2217     /**
2218      * Old alias to {@link Ext.Array#max}
2219      * @deprecated 4.0.0 Use {@link Ext.Array#max} instead
2220      * @method
2221      * @member Ext
2222      * @alias Ext.Array#max
2223      */
2224     Ext.max = ExtArray.max;
2225
2226     /**
2227      * Old alias to {@link Ext.Array#sum}
2228      * @deprecated 4.0.0 Use {@link Ext.Array#sum} instead
2229      * @method
2230      * @member Ext
2231      * @alias Ext.Array#sum
2232      */
2233     Ext.sum = ExtArray.sum;
2234
2235     /**
2236      * Old alias to {@link Ext.Array#mean}
2237      * @deprecated 4.0.0 Use {@link Ext.Array#mean} instead
2238      * @method
2239      * @member Ext
2240      * @alias Ext.Array#mean
2241      */
2242     Ext.mean = ExtArray.mean;
2243
2244     /**
2245      * Old alias to {@link Ext.Array#flatten}
2246      * @deprecated 4.0.0 Use {@link Ext.Array#flatten} instead
2247      * @method
2248      * @member Ext
2249      * @alias Ext.Array#flatten
2250      */
2251     Ext.flatten = ExtArray.flatten;
2252
2253     /**
2254      * Old alias to {@link Ext.Array#clean}
2255      * @deprecated 4.0.0 Use {@link Ext.Array#clean} instead
2256      * @method
2257      * @member Ext
2258      * @alias Ext.Array#clean
2259      */
2260     Ext.clean = ExtArray.clean;
2261
2262     /**
2263      * Old alias to {@link Ext.Array#unique}
2264      * @deprecated 4.0.0 Use {@link Ext.Array#unique} instead
2265      * @method
2266      * @member Ext
2267      * @alias Ext.Array#unique
2268      */
2269     Ext.unique = ExtArray.unique;
2270
2271     /**
2272      * Old alias to {@link Ext.Array#pluck Ext.Array.pluck}
2273      * @deprecated 4.0.0 Use {@link Ext.Array#pluck Ext.Array.pluck} instead
2274      * @method
2275      * @member Ext
2276      * @alias Ext.Array#pluck
2277      */
2278     Ext.pluck = ExtArray.pluck;
2279
2280     /**
2281      * @method
2282      * @member Ext
2283      * @alias Ext.Array#toArray
2284      */
2285     Ext.toArray = function() {
2286         return ExtArray.toArray.apply(ExtArray, arguments);
2287     };
2288 })();
2289
2290 /**
2291  * @class Ext.Function
2292  *
2293  * A collection of useful static methods to deal with function callbacks
2294  * @singleton
2295  */
2296 Ext.Function = {
2297
2298     /**
2299      * A very commonly used method throughout the framework. It acts as a wrapper around another method
2300      * which originally accepts 2 arguments for `name` and `value`.
2301      * The wrapped function then allows "flexible" value setting of either:
2302      *
2303      * - `name` and `value` as 2 arguments
2304      * - one single object argument with multiple key - value pairs
2305      *
2306      * For example:
2307      *
2308      *     var setValue = Ext.Function.flexSetter(function(name, value) {
2309      *         this[name] = value;
2310      *     });
2311      *
2312      *     // Afterwards
2313      *     // Setting a single name - value
2314      *     setValue('name1', 'value1');
2315      *
2316      *     // Settings multiple name - value pairs
2317      *     setValue({
2318      *         name1: 'value1',
2319      *         name2: 'value2',
2320      *         name3: 'value3'
2321      *     });
2322      *
2323      * @param {Function} setter
2324      * @returns {Function} flexSetter
2325      */
2326     flexSetter: function(fn) {
2327         return function(a, b) {
2328             var k, i;
2329
2330             if (a === null) {
2331                 return this;
2332             }
2333
2334             if (typeof a !== 'string') {
2335                 for (k in a) {
2336                     if (a.hasOwnProperty(k)) {
2337                         fn.call(this, k, a[k]);
2338                     }
2339                 }
2340
2341                 if (Ext.enumerables) {
2342                     for (i = Ext.enumerables.length; i--;) {
2343                         k = Ext.enumerables[i];
2344                         if (a.hasOwnProperty(k)) {
2345                             fn.call(this, k, a[k]);
2346                         }
2347                     }
2348                 }
2349             } else {
2350                 fn.call(this, a, b);
2351             }
2352
2353             return this;
2354         };
2355     },
2356
2357     /**
2358      * Create a new function from the provided `fn`, change `this` to the provided scope, optionally
2359      * overrides arguments for the call. (Defaults to the arguments passed by the caller)
2360      *
2361      * {@link Ext#bind Ext.bind} is alias for {@link Ext.Function#bind Ext.Function.bind}
2362      *
2363      * @param {Function} fn The function to delegate.
2364      * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed.
2365      * **If omitted, defaults to the browser window.**
2366      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
2367      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
2368      * if a number the args are inserted at the specified position
2369      * @return {Function} The new function
2370      */
2371     bind: function(fn, scope, args, appendArgs) {
2372         var method = fn,
2373             slice = Array.prototype.slice;
2374
2375         return function() {
2376             var callArgs = args || arguments;
2377
2378             if (appendArgs === true) {
2379                 callArgs = slice.call(arguments, 0);
2380                 callArgs = callArgs.concat(args);
2381             }
2382             else if (Ext.isNumber(appendArgs)) {
2383                 callArgs = slice.call(arguments, 0); // copy arguments first
2384                 Ext.Array.insert(callArgs, appendArgs, args);
2385             }
2386
2387             return method.apply(scope || window, callArgs);
2388         };
2389     },
2390
2391     /**
2392      * Create a new function from the provided `fn`, the arguments of which are pre-set to `args`.
2393      * New arguments passed to the newly created callback when it's invoked are appended after the pre-set ones.
2394      * This is especially useful when creating callbacks.
2395      *
2396      * For example:
2397      *
2398      *     var originalFunction = function(){
2399      *         alert(Ext.Array.from(arguments).join(' '));
2400      *     };
2401      *
2402      *     var callback = Ext.Function.pass(originalFunction, ['Hello', 'World']);
2403      *
2404      *     callback(); // alerts 'Hello World'
2405      *     callback('by Me'); // alerts 'Hello World by Me'
2406      *
2407      * {@link Ext#pass Ext.pass} is alias for {@link Ext.Function#pass Ext.Function.pass}
2408      *
2409      * @param {Function} fn The original function
2410      * @param {Array} args The arguments to pass to new callback
2411      * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed.
2412      * @return {Function} The new callback function
2413      */
2414     pass: function(fn, args, scope) {
2415         if (args) {
2416             args = Ext.Array.from(args);
2417         }
2418
2419         return function() {
2420             return fn.apply(scope, args.concat(Ext.Array.toArray(arguments)));
2421         };
2422     },
2423
2424     /**
2425      * Create an alias to the provided method property with name `methodName` of `object`.
2426      * Note that the execution scope will still be bound to the provided `object` itself.
2427      *
2428      * @param {Object/Function} object
2429      * @param {String} methodName
2430      * @return {Function} aliasFn
2431      */
2432     alias: function(object, methodName) {
2433         return function() {
2434             return object[methodName].apply(object, arguments);
2435         };
2436     },
2437
2438     /**
2439      * Creates an interceptor function. The passed function is called before the original one. If it returns false,
2440      * the original one is not called. The resulting function returns the results of the original function.
2441      * The passed function is called with the parameters of the original function. Example usage:
2442      *
2443      *     var sayHi = function(name){
2444      *         alert('Hi, ' + name);
2445      *     }
2446      *
2447      *     sayHi('Fred'); // alerts "Hi, Fred"
2448      *
2449      *     // create a new function that validates input without
2450      *     // directly modifying the original function:
2451      *     var sayHiToFriend = Ext.Function.createInterceptor(sayHi, function(name){
2452      *         return name == 'Brian';
2453      *     });
2454      *
2455      *     sayHiToFriend('Fred');  // no alert
2456      *     sayHiToFriend('Brian'); // alerts "Hi, Brian"
2457      *
2458      * @param {Function} origFn The original function.
2459      * @param {Function} newFn The function to call before the original
2460      * @param {Object} scope (optional) The scope (`this` reference) in which the passed function is executed.
2461      * **If omitted, defaults to the scope in which the original function is called or the browser window.**
2462      * @param {Mixed} returnValue (optional) The value to return if the passed function return false (defaults to null).
2463      * @return {Function} The new function
2464      */
2465     createInterceptor: function(origFn, newFn, scope, returnValue) {
2466         var method = origFn;
2467         if (!Ext.isFunction(newFn)) {
2468             return origFn;
2469         }
2470         else {
2471             return function() {
2472                 var me = this,
2473                     args = arguments;
2474                 newFn.target = me;
2475                 newFn.method = origFn;
2476                 return (newFn.apply(scope || me || window, args) !== false) ? origFn.apply(me || window, args) : returnValue || null;
2477             };
2478         }
2479     },
2480
2481     /**
2482      * Creates a delegate (callback) which, when called, executes after a specific delay.
2483      *
2484      * @param {Function} fn The function which will be called on a delay when the returned function is called.
2485      * Optionally, a replacement (or additional) argument list may be specified.
2486      * @param {Number} delay The number of milliseconds to defer execution by whenever called.
2487      * @param {Object} scope (optional) The scope (`this` reference) used by the function at execution time.
2488      * @param {Array} args (optional) Override arguments for the call. (Defaults to the arguments passed by the caller)
2489      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
2490      * if a number the args are inserted at the specified position.
2491      * @return {Function} A function which, when called, executes the original function after the specified delay.
2492      */
2493     createDelayed: function(fn, delay, scope, args, appendArgs) {
2494         if (scope || args) {
2495             fn = Ext.Function.bind(fn, scope, args, appendArgs);
2496         }
2497         return function() {
2498             var me = this;
2499             setTimeout(function() {
2500                 fn.apply(me, arguments);
2501             }, delay);
2502         };
2503     },
2504
2505     /**
2506      * Calls this function after the number of millseconds specified, optionally in a specific scope. Example usage:
2507      *
2508      *     var sayHi = function(name){
2509      *         alert('Hi, ' + name);
2510      *     }
2511      *
2512      *     // executes immediately:
2513      *     sayHi('Fred');
2514      *
2515      *     // executes after 2 seconds:
2516      *     Ext.Function.defer(sayHi, 2000, this, ['Fred']);
2517      *
2518      *     // this syntax is sometimes useful for deferring
2519      *     // execution of an anonymous function:
2520      *     Ext.Function.defer(function(){
2521      *         alert('Anonymous');
2522      *     }, 100);
2523      *
2524      * {@link Ext#defer Ext.defer} is alias for {@link Ext.Function#defer Ext.Function.defer}
2525      *
2526      * @param {Function} fn The function to defer.
2527      * @param {Number} millis The number of milliseconds for the setTimeout call
2528      * (if less than or equal to 0 the function is executed immediately)
2529      * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed.
2530      * **If omitted, defaults to the browser window.**
2531      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
2532      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
2533      * if a number the args are inserted at the specified position
2534      * @return {Number} The timeout id that can be used with clearTimeout
2535      */
2536     defer: function(fn, millis, obj, args, appendArgs) {
2537         fn = Ext.Function.bind(fn, obj, args, appendArgs);
2538         if (millis > 0) {
2539             return setTimeout(fn, millis);
2540         }
2541         fn();
2542         return 0;
2543     },
2544
2545     /**
2546      * Create a combined function call sequence of the original function + the passed function.
2547      * The resulting function returns the results of the original function.
2548      * The passed function is called with the parameters of the original function. Example usage:
2549      *
2550      *     var sayHi = function(name){
2551      *         alert('Hi, ' + name);
2552      *     }
2553      *
2554      *     sayHi('Fred'); // alerts "Hi, Fred"
2555      *
2556      *     var sayGoodbye = Ext.Function.createSequence(sayHi, function(name){
2557      *         alert('Bye, ' + name);
2558      *     });
2559      *
2560      *     sayGoodbye('Fred'); // both alerts show
2561      *
2562      * @param {Function} origFn The original function.
2563      * @param {Function} newFn The function to sequence
2564      * @param {Object} scope (optional) The scope (`this` reference) in which the passed function is executed.
2565      * If omitted, defaults to the scope in which the original function is called or the browser window.
2566      * @return {Function} The new function
2567      */
2568     createSequence: function(origFn, newFn, scope) {
2569         if (!Ext.isFunction(newFn)) {
2570             return origFn;
2571         }
2572         else {
2573             return function() {
2574                 var retval = origFn.apply(this || window, arguments);
2575                 newFn.apply(scope || this || window, arguments);
2576                 return retval;
2577             };
2578         }
2579     },
2580
2581     /**
2582      * Creates a delegate function, optionally with a bound scope which, when called, buffers
2583      * the execution of the passed function for the configured number of milliseconds.
2584      * If called again within that period, the impending invocation will be canceled, and the
2585      * timeout period will begin again.
2586      *
2587      * @param {Function} fn The function to invoke on a buffered timer.
2588      * @param {Number} buffer The number of milliseconds by which to buffer the invocation of the
2589      * function.
2590      * @param {Object} scope (optional) The scope (`this` reference) in which
2591      * the passed function is executed. If omitted, defaults to the scope specified by the caller.
2592      * @param {Array} args (optional) Override arguments for the call. Defaults to the arguments
2593      * passed by the caller.
2594      * @return {Function} A function which invokes the passed function after buffering for the specified time.
2595      */
2596     createBuffered: function(fn, buffer, scope, args) {
2597         return function(){
2598             var timerId;
2599             return function() {
2600                 var me = this;
2601                 if (timerId) {
2602                     clearInterval(timerId);
2603                     timerId = null;
2604                 }
2605                 timerId = setTimeout(function(){
2606                     fn.apply(scope || me, args || arguments);
2607                 }, buffer);
2608             };
2609         }();
2610     },
2611
2612     /**
2613      * Creates a throttled version of the passed function which, when called repeatedly and
2614      * rapidly, invokes the passed function only after a certain interval has elapsed since the
2615      * previous invocation.
2616      *
2617      * This is useful for wrapping functions which may be called repeatedly, such as
2618      * a handler of a mouse move event when the processing is expensive.
2619      *
2620      * @param {Function} fn The function to execute at a regular time interval.
2621      * @param {Number} interval The interval **in milliseconds** on which the passed function is executed.
2622      * @param {Object} scope (optional) The scope (`this` reference) in which
2623      * the passed function is executed. If omitted, defaults to the scope specified by the caller.
2624      * @returns {Function} A function which invokes the passed function at the specified interval.
2625      */
2626     createThrottled: function(fn, interval, scope) {
2627         var lastCallTime, elapsed, lastArgs, timer, execute = function() {
2628             fn.apply(scope || this, lastArgs);
2629             lastCallTime = new Date().getTime();
2630         };
2631
2632         return function() {
2633             elapsed = new Date().getTime() - lastCallTime;
2634             lastArgs = arguments;
2635
2636             clearTimeout(timer);
2637             if (!lastCallTime || (elapsed >= interval)) {
2638                 execute();
2639             } else {
2640                 timer = setTimeout(execute, interval - elapsed);
2641             }
2642         };
2643     }
2644 };
2645
2646 /**
2647  * @method
2648  * @member Ext
2649  * @alias Ext.Function#defer
2650  */
2651 Ext.defer = Ext.Function.alias(Ext.Function, 'defer');
2652
2653 /**
2654  * @method
2655  * @member Ext
2656  * @alias Ext.Function#pass
2657  */
2658 Ext.pass = Ext.Function.alias(Ext.Function, 'pass');
2659
2660 /**
2661  * @method
2662  * @member Ext
2663  * @alias Ext.Function#bind
2664  */
2665 Ext.bind = Ext.Function.alias(Ext.Function, 'bind');
2666
2667 /**
2668  * @author Jacky Nguyen <jacky@sencha.com>
2669  * @docauthor Jacky Nguyen <jacky@sencha.com>
2670  * @class Ext.Object
2671  *
2672  * A collection of useful static methods to deal with objects
2673  *
2674  * @singleton
2675  */
2676
2677 (function() {
2678
2679 var ExtObject = Ext.Object = {
2680
2681     /**
2682      * Convert a `name` - `value` pair to an array of objects with support for nested structures; useful to construct
2683      * query strings. For example:
2684
2685     var objects = Ext.Object.toQueryObjects('hobbies', ['reading', 'cooking', 'swimming']);
2686
2687     // objects then equals:
2688     [
2689         { name: 'hobbies', value: 'reading' },
2690         { name: 'hobbies', value: 'cooking' },
2691         { name: 'hobbies', value: 'swimming' },
2692     ];
2693
2694     var objects = Ext.Object.toQueryObjects('dateOfBirth', {
2695         day: 3,
2696         month: 8,
2697         year: 1987,
2698         extra: {
2699             hour: 4
2700             minute: 30
2701         }
2702     }, true); // Recursive
2703
2704     // objects then equals:
2705     [
2706         { name: 'dateOfBirth[day]', value: 3 },
2707         { name: 'dateOfBirth[month]', value: 8 },
2708         { name: 'dateOfBirth[year]', value: 1987 },
2709         { name: 'dateOfBirth[extra][hour]', value: 4 },
2710         { name: 'dateOfBirth[extra][minute]', value: 30 },
2711     ];
2712
2713      * @param {String} name
2714      * @param {Mixed} value
2715      * @param {Boolean} recursive
2716      * @markdown
2717      */
2718     toQueryObjects: function(name, value, recursive) {
2719         var self = ExtObject.toQueryObjects,
2720             objects = [],
2721             i, ln;
2722
2723         if (Ext.isArray(value)) {
2724             for (i = 0, ln = value.length; i < ln; i++) {
2725                 if (recursive) {
2726                     objects = objects.concat(self(name + '[' + i + ']', value[i], true));
2727                 }
2728                 else {
2729                     objects.push({
2730                         name: name,
2731                         value: value[i]
2732                     });
2733                 }
2734             }
2735         }
2736         else if (Ext.isObject(value)) {
2737             for (i in value) {
2738                 if (value.hasOwnProperty(i)) {
2739                     if (recursive) {
2740                         objects = objects.concat(self(name + '[' + i + ']', value[i], true));
2741                     }
2742                     else {
2743                         objects.push({
2744                             name: name,
2745                             value: value[i]
2746                         });
2747                     }
2748                 }
2749             }
2750         }
2751         else {
2752             objects.push({
2753                 name: name,
2754                 value: value
2755             });
2756         }
2757
2758         return objects;
2759     },
2760
2761     /**
2762      * Takes an object and converts it to an encoded query string
2763
2764 - Non-recursive:
2765
2766     Ext.Object.toQueryString({foo: 1, bar: 2}); // returns "foo=1&bar=2"
2767     Ext.Object.toQueryString({foo: null, bar: 2}); // returns "foo=&bar=2"
2768     Ext.Object.toQueryString({'some price': '$300'}); // returns "some%20price=%24300"
2769     Ext.Object.toQueryString({date: new Date(2011, 0, 1)}); // returns "date=%222011-01-01T00%3A00%3A00%22"
2770     Ext.Object.toQueryString({colors: ['red', 'green', 'blue']}); // returns "colors=red&colors=green&colors=blue"
2771
2772 - Recursive:
2773
2774     Ext.Object.toQueryString({
2775         username: 'Jacky',
2776         dateOfBirth: {
2777             day: 1,
2778             month: 2,
2779             year: 1911
2780         },
2781         hobbies: ['coding', 'eating', 'sleeping', ['nested', 'stuff']]
2782     }, true); // returns the following string (broken down and url-decoded for ease of reading purpose):
2783               // username=Jacky
2784               //    &dateOfBirth[day]=1&dateOfBirth[month]=2&dateOfBirth[year]=1911
2785               //    &hobbies[0]=coding&hobbies[1]=eating&hobbies[2]=sleeping&hobbies[3][0]=nested&hobbies[3][1]=stuff
2786
2787      *
2788      * @param {Object} object The object to encode
2789      * @param {Boolean} recursive (optional) Whether or not to interpret the object in recursive format.
2790      * (PHP / Ruby on Rails servers and similar). Defaults to false
2791      * @return {String} queryString
2792      * @markdown
2793      */
2794     toQueryString: function(object, recursive) {
2795         var paramObjects = [],
2796             params = [],
2797             i, j, ln, paramObject, value;
2798
2799         for (i in object) {
2800             if (object.hasOwnProperty(i)) {
2801                 paramObjects = paramObjects.concat(ExtObject.toQueryObjects(i, object[i], recursive));
2802             }
2803         }
2804
2805         for (j = 0, ln = paramObjects.length; j < ln; j++) {
2806             paramObject = paramObjects[j];
2807             value = paramObject.value;
2808
2809             if (Ext.isEmpty(value)) {
2810                 value = '';
2811             }
2812             else if (Ext.isDate(value)) {
2813                 value = Ext.Date.toString(value);
2814             }
2815
2816             params.push(encodeURIComponent(paramObject.name) + '=' + encodeURIComponent(String(value)));
2817         }
2818
2819         return params.join('&');
2820     },
2821
2822     /**
2823      * Converts a query string back into an object.
2824      *
2825 - Non-recursive:
2826
2827     Ext.Object.fromQueryString(foo=1&bar=2); // returns {foo: 1, bar: 2}
2828     Ext.Object.fromQueryString(foo=&bar=2); // returns {foo: null, bar: 2}
2829     Ext.Object.fromQueryString(some%20price=%24300); // returns {'some price': '$300'}
2830     Ext.Object.fromQueryString(colors=red&colors=green&colors=blue); // returns {colors: ['red', 'green', 'blue']}
2831
2832 - Recursive:
2833
2834     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);
2835
2836     // returns
2837     {
2838         username: 'Jacky',
2839         dateOfBirth: {
2840             day: '1',
2841             month: '2',
2842             year: '1911'
2843         },
2844         hobbies: ['coding', 'eating', 'sleeping', ['nested', 'stuff']]
2845     }
2846
2847      * @param {String} queryString The query string to decode
2848      * @param {Boolean} recursive (Optional) Whether or not to recursively decode the string. This format is supported by
2849      * PHP / Ruby on Rails servers and similar. Defaults to false
2850      * @return {Object}
2851      */
2852     fromQueryString: function(queryString, recursive) {
2853         var parts = queryString.replace(/^\?/, '').split('&'),
2854             object = {},
2855             temp, components, name, value, i, ln,
2856             part, j, subLn, matchedKeys, matchedName,
2857             keys, key, nextKey;
2858
2859         for (i = 0, ln = parts.length; i < ln; i++) {
2860             part = parts[i];
2861
2862             if (part.length > 0) {
2863                 components = part.split('=');
2864                 name = decodeURIComponent(components[0]);
2865                 value = (components[1] !== undefined) ? decodeURIComponent(components[1]) : '';
2866
2867                 if (!recursive) {
2868                     if (object.hasOwnProperty(name)) {
2869                         if (!Ext.isArray(object[name])) {
2870                             object[name] = [object[name]];
2871                         }
2872
2873                         object[name].push(value);
2874                     }
2875                     else {
2876                         object[name] = value;
2877                     }
2878                 }
2879                 else {
2880                     matchedKeys = name.match(/(\[):?([^\]]*)\]/g);
2881                     matchedName = name.match(/^([^\[]+)/);
2882
2883                     if (!matchedName) {
2884                         Ext.Error.raise({
2885                             sourceClass: "Ext.Object",
2886                             sourceMethod: "fromQueryString",
2887                             queryString: queryString,
2888                             recursive: recursive,
2889                             msg: 'Malformed query string given, failed parsing name from "' + part + '"'
2890                         });
2891                     }
2892
2893                     name = matchedName[0];
2894                     keys = [];
2895
2896                     if (matchedKeys === null) {
2897                         object[name] = value;
2898                         continue;
2899                     }
2900
2901                     for (j = 0, subLn = matchedKeys.length; j < subLn; j++) {
2902                         key = matchedKeys[j];
2903                         key = (key.length === 2) ? '' : key.substring(1, key.length - 1);
2904                         keys.push(key);
2905                     }
2906
2907                     keys.unshift(name);
2908
2909                     temp = object;
2910
2911                     for (j = 0, subLn = keys.length; j < subLn; j++) {
2912                         key = keys[j];
2913
2914                         if (j === subLn - 1) {
2915                             if (Ext.isArray(temp) && key === '') {
2916                                 temp.push(value);
2917                             }
2918                             else {
2919                                 temp[key] = value;
2920                             }
2921                         }
2922                         else {
2923                             if (temp[key] === undefined || typeof temp[key] === 'string') {
2924                                 nextKey = keys[j+1];
2925
2926                                 temp[key] = (Ext.isNumeric(nextKey) || nextKey === '') ? [] : {};
2927                             }
2928
2929                             temp = temp[key];
2930                         }
2931                     }
2932                 }
2933             }
2934         }
2935
2936         return object;
2937     },
2938
2939     /**
2940      * Iterate through an object and invoke the given callback function for each iteration. The iteration can be stop
2941      * by returning `false` in the callback function. For example:
2942
2943     var person = {
2944         name: 'Jacky'
2945         hairColor: 'black'
2946         loves: ['food', 'sleeping', 'wife']
2947     };
2948
2949     Ext.Object.each(person, function(key, value, myself) {
2950         console.log(key + ":" + value);
2951
2952         if (key === 'hairColor') {
2953             return false; // stop the iteration
2954         }
2955     });
2956
2957      * @param {Object} object The object to iterate
2958      * @param {Function} fn The callback function. Passed arguments for each iteration are:
2959
2960 - {String} `key`
2961 - {Mixed} `value`
2962 - {Object} `object` The object itself
2963
2964      * @param {Object} scope (Optional) The execution scope (`this`) of the callback function
2965      * @markdown
2966      */
2967     each: function(object, fn, scope) {
2968         for (var property in object) {
2969             if (object.hasOwnProperty(property)) {
2970                 if (fn.call(scope || object, property, object[property], object) === false) {
2971                     return;
2972                 }
2973             }
2974         }
2975     },
2976
2977     /**
2978      * Merges any number of objects recursively without referencing them or their children.
2979
2980     var extjs = {
2981         companyName: 'Ext JS',
2982         products: ['Ext JS', 'Ext GWT', 'Ext Designer'],
2983         isSuperCool: true
2984         office: {
2985             size: 2000,
2986             location: 'Palo Alto',
2987             isFun: true
2988         }
2989     };
2990
2991     var newStuff = {
2992         companyName: 'Sencha Inc.',
2993         products: ['Ext JS', 'Ext GWT', 'Ext Designer', 'Sencha Touch', 'Sencha Animator'],
2994         office: {
2995             size: 40000,
2996             location: 'Redwood City'
2997         }
2998     };
2999
3000     var sencha = Ext.Object.merge(extjs, newStuff);
3001
3002     // extjs and sencha then equals to
3003     {
3004         companyName: 'Sencha Inc.',
3005         products: ['Ext JS', 'Ext GWT', 'Ext Designer', 'Sencha Touch', 'Sencha Animator'],
3006         isSuperCool: true
3007         office: {
3008             size: 30000,
3009             location: 'Redwood City'
3010             isFun: true
3011         }
3012     }
3013
3014      * @param {Object} object,...
3015      * @return {Object} merged The object that is created as a result of merging all the objects passed in.
3016      * @markdown
3017      */
3018     merge: function(source, key, value) {
3019         if (typeof key === 'string') {
3020             if (value && value.constructor === Object) {
3021                 if (source[key] && source[key].constructor === Object) {
3022                     ExtObject.merge(source[key], value);
3023                 }
3024                 else {
3025                     source[key] = Ext.clone(value);
3026                 }
3027             }
3028             else {
3029                 source[key] = value;
3030             }
3031
3032             return source;
3033         }
3034
3035         var i = 1,
3036             ln = arguments.length,
3037             object, property;
3038
3039         for (; i < ln; i++) {
3040             object = arguments[i];
3041
3042             for (property in object) {
3043                 if (object.hasOwnProperty(property)) {
3044                     ExtObject.merge(source, property, object[property]);
3045                 }
3046             }
3047         }
3048
3049         return source;
3050     },
3051
3052     /**
3053      * Returns the first matching key corresponding to the given value.
3054      * If no matching value is found, null is returned.
3055
3056     var person = {
3057         name: 'Jacky',
3058         loves: 'food'
3059     };
3060
3061     alert(Ext.Object.getKey(sencha, 'loves')); // alerts 'food'
3062
3063      * @param {Object} object
3064      * @param {Object} value The value to find
3065      * @markdown
3066      */
3067     getKey: function(object, value) {
3068         for (var property in object) {
3069             if (object.hasOwnProperty(property) && object[property] === value) {
3070                 return property;
3071             }
3072         }
3073
3074         return null;
3075     },
3076
3077     /**
3078      * Gets all values of the given object as an array.
3079
3080     var values = Ext.Object.getValues({
3081         name: 'Jacky',
3082         loves: 'food'
3083     }); // ['Jacky', 'food']
3084
3085      * @param {Object} object
3086      * @return {Array} An array of values from the object
3087      * @markdown
3088      */
3089     getValues: function(object) {
3090         var values = [],
3091             property;
3092
3093         for (property in object) {
3094             if (object.hasOwnProperty(property)) {
3095                 values.push(object[property]);
3096             }
3097         }
3098
3099         return values;
3100     },
3101
3102     /**
3103      * Gets all keys of the given object as an array.
3104
3105     var values = Ext.Object.getKeys({
3106         name: 'Jacky',
3107         loves: 'food'
3108     }); // ['name', 'loves']
3109
3110      * @param {Object} object
3111      * @return {Array} An array of keys from the object
3112      * @method
3113      */
3114     getKeys: ('keys' in Object.prototype) ? Object.keys : function(object) {
3115         var keys = [],
3116             property;
3117
3118         for (property in object) {
3119             if (object.hasOwnProperty(property)) {
3120                 keys.push(property);
3121             }
3122         }
3123
3124         return keys;
3125     },
3126
3127     /**
3128      * Gets the total number of this object's own properties
3129
3130     var size = Ext.Object.getSize({
3131         name: 'Jacky',
3132         loves: 'food'
3133     }); // size equals 2
3134
3135      * @param {Object} object
3136      * @return {Number} size
3137      * @markdown
3138      */
3139     getSize: function(object) {
3140         var size = 0,
3141             property;
3142
3143         for (property in object) {
3144             if (object.hasOwnProperty(property)) {
3145                 size++;
3146             }
3147         }
3148
3149         return size;
3150     }
3151 };
3152
3153
3154 /**
3155  * A convenient alias method for {@link Ext.Object#merge}
3156  *
3157  * @member Ext
3158  * @method merge
3159  */
3160 Ext.merge = Ext.Object.merge;
3161
3162 /**
3163  * A convenient alias method for {@link Ext.Object#toQueryString}
3164  *
3165  * @member Ext
3166  * @method urlEncode
3167  * @deprecated 4.0.0 Use {@link Ext.Object#toQueryString Ext.Object.toQueryString} instead
3168  */
3169 Ext.urlEncode = function() {
3170     var args = Ext.Array.from(arguments),
3171         prefix = '';
3172
3173     // Support for the old `pre` argument
3174     if ((typeof args[1] === 'string')) {
3175         prefix = args[1] + '&';
3176         args[1] = false;
3177     }
3178
3179     return prefix + Ext.Object.toQueryString.apply(Ext.Object, args);
3180 };
3181
3182 /**
3183  * A convenient alias method for {@link Ext.Object#fromQueryString}
3184  *
3185  * @member Ext
3186  * @method urlDecode
3187  * @deprecated 4.0.0 Use {@link Ext.Object#fromQueryString Ext.Object.fromQueryString} instead
3188  */
3189 Ext.urlDecode = function() {
3190     return Ext.Object.fromQueryString.apply(Ext.Object, arguments);
3191 };
3192
3193 })();
3194
3195 /**
3196  * @class Ext.Date
3197  * A set of useful static methods to deal with date
3198  * Note that if Ext.Date is required and loaded, it will copy all methods / properties to
3199  * this object for convenience
3200  *
3201  * The date parsing and formatting syntax contains a subset of
3202  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
3203  * supported will provide results equivalent to their PHP versions.
3204  *
3205  * The following is a list of all currently supported formats:
3206  * <pre class="">
3207 Format  Description                                                               Example returned values
3208 ------  -----------------------------------------------------------------------   -----------------------
3209   d     Day of the month, 2 digits with leading zeros                             01 to 31
3210   D     A short textual representation of the day of the week                     Mon to Sun
3211   j     Day of the month without leading zeros                                    1 to 31
3212   l     A full textual representation of the day of the week                      Sunday to Saturday
3213   N     ISO-8601 numeric representation of the day of the week                    1 (for Monday) through 7 (for Sunday)
3214   S     English ordinal suffix for the day of the month, 2 characters             st, nd, rd or th. Works well with j
3215   w     Numeric representation of the day of the week                             0 (for Sunday) to 6 (for Saturday)
3216   z     The day of the year (starting from 0)                                     0 to 364 (365 in leap years)
3217   W     ISO-8601 week number of year, weeks starting on Monday                    01 to 53
3218   F     A full textual representation of a month, such as January or March        January to December
3219   m     Numeric representation of a month, with leading zeros                     01 to 12
3220   M     A short textual representation of a month                                 Jan to Dec
3221   n     Numeric representation of a month, without leading zeros                  1 to 12
3222   t     Number of days in the given month                                         28 to 31
3223   L     Whether it&#39;s a leap year                                                  1 if it is a leap year, 0 otherwise.
3224   o     ISO-8601 year number (identical to (Y), but if the ISO week number (W)    Examples: 1998 or 2004
3225         belongs to the previous or next year, that year is used instead)
3226   Y     A full numeric representation of a year, 4 digits                         Examples: 1999 or 2003
3227   y     A two digit representation of a year                                      Examples: 99 or 03
3228   a     Lowercase Ante meridiem and Post meridiem                                 am or pm
3229   A     Uppercase Ante meridiem and Post meridiem                                 AM or PM
3230   g     12-hour format of an hour without leading zeros                           1 to 12
3231   G     24-hour format of an hour without leading zeros                           0 to 23
3232   h     12-hour format of an hour with leading zeros                              01 to 12
3233   H     24-hour format of an hour with leading zeros                              00 to 23
3234   i     Minutes, with leading zeros                                               00 to 59
3235   s     Seconds, with leading zeros                                               00 to 59
3236   u     Decimal fraction of a second                                              Examples:
3237         (minimum 1 digit, arbitrary number of digits allowed)                     001 (i.e. 0.001s) or
3238                                                                                   100 (i.e. 0.100s) or
3239                                                                                   999 (i.e. 0.999s) or
3240                                                                                   999876543210 (i.e. 0.999876543210s)
3241   O     Difference to Greenwich time (GMT) in hours and minutes                   Example: +1030
3242   P     Difference to Greenwich time (GMT) with colon between hours and minutes   Example: -08:00
3243   T     Timezone abbreviation of the machine running the code                     Examples: EST, MDT, PDT ...
3244   Z     Timezone offset in seconds (negative if west of UTC, positive if east)    -43200 to 50400
3245   c     ISO 8601 date
3246         Notes:                                                                    Examples:
3247         1) If unspecified, the month / day defaults to the current month / day,   1991 or
3248            the time defaults to midnight, while the timezone defaults to the      1992-10 or
3249            browser's timezone. If a time is specified, it must include both hours 1993-09-20 or
3250            and minutes. The "T" delimiter, seconds, milliseconds and timezone     1994-08-19T16:20+01:00 or
3251            are optional.                                                          1995-07-18T17:21:28-02:00 or
3252         2) The decimal fraction of a second, if specified, must contain at        1996-06-17T18:22:29.98765+03:00 or
3253            least 1 digit (there is no limit to the maximum number                 1997-05-16T19:23:30,12345-0400 or
3254            of digits allowed), and may be delimited by either a '.' or a ','      1998-04-15T20:24:31.2468Z or
3255         Refer to the examples on the right for the various levels of              1999-03-14T20:24:32Z or
3256         date-time granularity which are supported, or see                         2000-02-13T21:25:33
3257         http://www.w3.org/TR/NOTE-datetime for more info.                         2001-01-12 22:26:34
3258   U     Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)                1193432466 or -2138434463
3259   MS    Microsoft AJAX serialized dates                                           \/Date(1238606590509)\/ (i.e. UTC milliseconds since epoch) or
3260                                                                                   \/Date(1238606590509+0800)\/
3261 </pre>
3262  *
3263  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
3264  * <pre><code>
3265 // Sample date:
3266 // 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
3267
3268 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
3269 console.log(Ext.Date.format(dt, 'Y-m-d'));                          // 2007-01-10
3270 console.log(Ext.Date.format(dt, 'F j, Y, g:i a'));                  // January 10, 2007, 3:05 pm
3271 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
3272 </code></pre>
3273  *
3274  * Here are some standard date/time patterns that you might find helpful.  They
3275  * are not part of the source of Ext.Date, but to use them you can simply copy this
3276  * block of code into any script that is included after Ext.Date and they will also become
3277  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
3278  * <pre><code>
3279 Ext.Date.patterns = {
3280     ISO8601Long:"Y-m-d H:i:s",
3281     ISO8601Short:"Y-m-d",
3282     ShortDate: "n/j/Y",
3283     LongDate: "l, F d, Y",
3284     FullDateTime: "l, F d, Y g:i:s A",
3285     MonthDay: "F d",
3286     ShortTime: "g:i A",
3287     LongTime: "g:i:s A",
3288     SortableDateTime: "Y-m-d\\TH:i:s",
3289     UniversalSortableDateTime: "Y-m-d H:i:sO",
3290     YearMonth: "F, Y"
3291 };
3292 </code></pre>
3293  *
3294  * Example usage:
3295  * <pre><code>
3296 var dt = new Date();
3297 console.log(Ext.Date.format(dt, Ext.Date.patterns.ShortDate));
3298 </code></pre>
3299  * <p>Developer-written, custom formats may be used by supplying both a formatting and a parsing function
3300  * which perform to specialized requirements. The functions are stored in {@link #parseFunctions} and {@link #formatFunctions}.</p>
3301  * @singleton
3302  */
3303
3304 /*
3305  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
3306  * (see http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/)
3307  * They generate precompiled functions from format patterns instead of parsing and
3308  * processing each pattern every time a date is formatted. These functions are available
3309  * on every Date object.
3310  */
3311
3312 (function() {
3313
3314 // create private copy of Ext's Ext.util.Format.format() method
3315 // - to remove unnecessary dependency
3316 // - to resolve namespace conflict with MS-Ajax's implementation
3317 function xf(format) {
3318     var args = Array.prototype.slice.call(arguments, 1);
3319     return format.replace(/\{(\d+)\}/g, function(m, i) {
3320         return args[i];
3321     });
3322 }
3323
3324 Ext.Date = {
3325     /**
3326      * Returns the current timestamp
3327      * @return {Date} The current timestamp
3328      * @method
3329      */
3330     now: Date.now || function() {
3331         return +new Date();
3332     },
3333
3334     /**
3335      * @private
3336      * Private for now
3337      */
3338     toString: function(date) {
3339         var pad = Ext.String.leftPad;
3340
3341         return date.getFullYear() + "-"
3342             + pad(date.getMonth() + 1, 2, '0') + "-"
3343             + pad(date.getDate(), 2, '0') + "T"
3344             + pad(date.getHours(), 2, '0') + ":"
3345             + pad(date.getMinutes(), 2, '0') + ":"
3346             + pad(date.getSeconds(), 2, '0');
3347     },
3348
3349     /**
3350      * Returns the number of milliseconds between two dates
3351      * @param {Date} dateA The first date
3352      * @param {Date} dateB (optional) The second date, defaults to now
3353      * @return {Number} The difference in milliseconds
3354      */
3355     getElapsed: function(dateA, dateB) {
3356         return Math.abs(dateA - (dateB || new Date()));
3357     },
3358
3359     /**
3360      * Global flag which determines if strict date parsing should be used.
3361      * Strict date parsing will not roll-over invalid dates, which is the
3362      * default behaviour of javascript Date objects.
3363      * (see {@link #parse} for more information)
3364      * Defaults to <tt>false</tt>.
3365      * @static
3366      * @type Boolean
3367     */
3368     useStrict: false,
3369
3370     // private
3371     formatCodeToRegex: function(character, currentGroup) {
3372         // Note: currentGroup - position in regex result array (see notes for Ext.Date.parseCodes below)
3373         var p = utilDate.parseCodes[character];
3374
3375         if (p) {
3376           p = typeof p == 'function'? p() : p;
3377           utilDate.parseCodes[character] = p; // reassign function result to prevent repeated execution
3378         }
3379
3380         return p ? Ext.applyIf({
3381           c: p.c ? xf(p.c, currentGroup || "{0}") : p.c
3382         }, p) : {
3383             g: 0,
3384             c: null,
3385             s: Ext.String.escapeRegex(character) // treat unrecognised characters as literals
3386         };
3387     },
3388
3389     /**
3390      * <p>An object hash in which each property is a date parsing function. The property name is the
3391      * format string which that function parses.</p>
3392      * <p>This object is automatically populated with date parsing functions as
3393      * date formats are requested for Ext standard formatting strings.</p>
3394      * <p>Custom parsing functions may be inserted into this object, keyed by a name which from then on
3395      * may be used as a format string to {@link #parse}.<p>
3396      * <p>Example:</p><pre><code>
3397 Ext.Date.parseFunctions['x-date-format'] = myDateParser;
3398 </code></pre>
3399      * <p>A parsing function should return a Date object, and is passed the following parameters:<div class="mdetail-params"><ul>
3400      * <li><code>date</code> : String<div class="sub-desc">The date string to parse.</div></li>
3401      * <li><code>strict</code> : Boolean<div class="sub-desc">True to validate date strings while parsing
3402      * (i.e. prevent javascript Date "rollover") (The default must be false).
3403      * Invalid date strings should return null when parsed.</div></li>
3404      * </ul></div></p>
3405      * <p>To enable Dates to also be <i>formatted</i> according to that format, a corresponding
3406      * formatting function must be placed into the {@link #formatFunctions} property.
3407      * @property parseFunctions
3408      * @static
3409      * @type Object
3410      */
3411     parseFunctions: {
3412         "MS": function(input, strict) {
3413             // note: the timezone offset is ignored since the MS Ajax server sends
3414             // a UTC milliseconds-since-Unix-epoch value (negative values are allowed)
3415             var re = new RegExp('\\/Date\\(([-+])?(\\d+)(?:[+-]\\d{4})?\\)\\/');
3416             var r = (input || '').match(re);
3417             return r? new Date(((r[1] || '') + r[2]) * 1) : null;
3418         }
3419     },
3420     parseRegexes: [],
3421
3422     /**
3423      * <p>An object hash in which each property is a date formatting function. The property name is the
3424      * format string which corresponds to the produced formatted date string.</p>
3425      * <p>This object is automatically populated with date formatting functions as
3426      * date formats are requested for Ext standard formatting strings.</p>
3427      * <p>Custom formatting functions may be inserted into this object, keyed by a name which from then on
3428      * may be used as a format string to {@link #format}. Example:</p><pre><code>
3429 Ext.Date.formatFunctions['x-date-format'] = myDateFormatter;
3430 </code></pre>
3431      * <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>
3432      * <li><code>date</code> : Date<div class="sub-desc">The Date to format.</div></li>
3433      * </ul></div></p>
3434      * <p>To enable date strings to also be <i>parsed</i> according to that format, a corresponding
3435      * parsing function must be placed into the {@link #parseFunctions} property.
3436      * @property formatFunctions
3437      * @static
3438      * @type Object
3439      */
3440     formatFunctions: {
3441         "MS": function() {
3442             // UTC milliseconds since Unix epoch (MS-AJAX serialized date format (MRSF))
3443             return '\\/Date(' + this.getTime() + ')\\/';
3444         }
3445     },
3446
3447     y2kYear : 50,
3448
3449     /**
3450      * Date interval constant
3451      * @static
3452      * @type String
3453      */
3454     MILLI : "ms",
3455
3456     /**
3457      * Date interval constant
3458      * @static
3459      * @type String
3460      */
3461     SECOND : "s",
3462
3463     /**
3464      * Date interval constant
3465      * @static
3466      * @type String
3467      */
3468     MINUTE : "mi",
3469
3470     /** Date interval constant
3471      * @static
3472      * @type String
3473      */
3474     HOUR : "h",
3475
3476     /**
3477      * Date interval constant
3478      * @static
3479      * @type String
3480      */
3481     DAY : "d",
3482
3483     /**
3484      * Date interval constant
3485      * @static
3486      * @type String
3487      */
3488     MONTH : "mo",
3489
3490     /**
3491      * Date interval constant
3492      * @static
3493      * @type String
3494      */
3495     YEAR : "y",
3496
3497     /**
3498      * <p>An object hash containing default date values used during date parsing.</p>
3499      * <p>The following properties are available:<div class="mdetail-params"><ul>
3500      * <li><code>y</code> : Number<div class="sub-desc">The default year value. (defaults to undefined)</div></li>
3501      * <li><code>m</code> : Number<div class="sub-desc">The default 1-based month value. (defaults to undefined)</div></li>
3502      * <li><code>d</code> : Number<div class="sub-desc">The default day value. (defaults to undefined)</div></li>
3503      * <li><code>h</code> : Number<div class="sub-desc">The default hour value. (defaults to undefined)</div></li>
3504      * <li><code>i</code> : Number<div class="sub-desc">The default minute value. (defaults to undefined)</div></li>
3505      * <li><code>s</code> : Number<div class="sub-desc">The default second value. (defaults to undefined)</div></li>
3506      * <li><code>ms</code> : Number<div class="sub-desc">The default millisecond value. (defaults to undefined)</div></li>
3507      * </ul></div></p>
3508      * <p>Override these properties to customize the default date values used by the {@link #parse} method.</p>
3509      * <p><b>Note: In countries which experience Daylight Saving Time (i.e. DST), the <tt>h</tt>, <tt>i</tt>, <tt>s</tt>
3510      * and <tt>ms</tt> properties may coincide with the exact time in which DST takes effect.
3511      * It is the responsiblity of the developer to account for this.</b></p>
3512      * Example Usage:
3513      * <pre><code>
3514 // set default day value to the first day of the month
3515 Ext.Date.defaults.d = 1;
3516
3517 // parse a February date string containing only year and month values.
3518 // setting the default day value to 1 prevents weird date rollover issues
3519 // when attempting to parse the following date string on, for example, March 31st 2009.
3520 Ext.Date.parse('2009-02', 'Y-m'); // returns a Date object representing February 1st 2009
3521 </code></pre>
3522      * @property defaults
3523      * @static
3524      * @type Object
3525      */
3526     defaults: {},
3527
3528     /**
3529      * An array of textual day names.
3530      * Override these values for international dates.
3531      * Example:
3532      * <pre><code>
3533 Ext.Date.dayNames = [
3534     'SundayInYourLang',
3535     'MondayInYourLang',
3536     ...
3537 ];
3538 </code></pre>
3539      * @type Array
3540      * @static
3541      */
3542     dayNames : [
3543         "Sunday",
3544         "Monday",
3545         "Tuesday",
3546         "Wednesday",
3547         "Thursday",
3548         "Friday",
3549         "Saturday"
3550     ],
3551
3552     /**
3553      * An array of textual month names.
3554      * Override these values for international dates.
3555      * Example:
3556      * <pre><code>
3557 Ext.Date.monthNames = [
3558     'JanInYourLang',
3559     'FebInYourLang',
3560     ...
3561 ];
3562 </code></pre>
3563      * @type Array
3564      * @static
3565      */
3566     monthNames : [
3567         "January",
3568         "February",
3569         "March",
3570         "April",
3571         "May",
3572         "June",
3573         "July",
3574         "August",
3575         "September",
3576         "October",
3577         "November",
3578         "December"
3579     ],
3580
3581     /**
3582      * An object hash of zero-based javascript month numbers (with short month names as keys. note: keys are case-sensitive).
3583      * Override these values for international dates.
3584      * Example:
3585      * <pre><code>
3586 Ext.Date.monthNumbers = {
3587     'ShortJanNameInYourLang':0,
3588     'ShortFebNameInYourLang':1,
3589     ...
3590 };
3591 </code></pre>
3592      * @type Object
3593      * @static
3594      */
3595     monthNumbers : {
3596         Jan:0,
3597         Feb:1,
3598         Mar:2,
3599         Apr:3,
3600         May:4,
3601         Jun:5,
3602         Jul:6,
3603         Aug:7,
3604         Sep:8,
3605         Oct:9,
3606         Nov:10,
3607         Dec:11
3608     },
3609     /**
3610      * <p>The date format string that the {@link Ext.util.Format#dateRenderer}
3611      * and {@link Ext.util.Format#date} functions use.  See {@link Ext.Date} for details.</p>
3612      * <p>This defaults to <code>m/d/Y</code>, but may be overridden in a locale file.</p>
3613      * @property defaultFormat
3614      * @static
3615      * @type String
3616      */
3617     defaultFormat : "m/d/Y",
3618     /**
3619      * Get the short month name for the given month number.
3620      * Override this function for international dates.
3621      * @param {Number} month A zero-based javascript month number.
3622      * @return {String} The short month name.
3623      * @static
3624      */
3625     getShortMonthName : function(month) {
3626         return utilDate.monthNames[month].substring(0, 3);
3627     },
3628
3629     /**
3630      * Get the short day name for the given day number.
3631      * Override this function for international dates.
3632      * @param {Number} day A zero-based javascript day number.
3633      * @return {String} The short day name.
3634      * @static
3635      */
3636     getShortDayName : function(day) {
3637         return utilDate.dayNames[day].substring(0, 3);
3638     },
3639
3640     /**
3641      * Get the zero-based javascript month number for the given short/full month name.
3642      * Override this function for international dates.
3643      * @param {String} name The short/full month name.
3644      * @return {Number} The zero-based javascript month number.
3645      * @static
3646      */
3647     getMonthNumber : function(name) {
3648         // handle camel casing for english month names (since the keys for the Ext.Date.monthNumbers hash are case sensitive)
3649         return utilDate.monthNumbers[name.substring(0, 1).toUpperCase() + name.substring(1, 3).toLowerCase()];
3650     },
3651
3652     /**
3653      * Checks if the specified format contains hour information
3654      * @param {String} format The format to check
3655      * @return {Boolean} True if the format contains hour information
3656      * @static
3657      * @method
3658      */
3659     formatContainsHourInfo : (function(){
3660         var stripEscapeRe = /(\\.)/g,
3661             hourInfoRe = /([gGhHisucUOPZ]|MS)/;
3662         return function(format){
3663             return hourInfoRe.test(format.replace(stripEscapeRe, ''));
3664         };
3665     })(),
3666
3667     /**
3668      * Checks if the specified format contains information about
3669      * anything other than the time.
3670      * @param {String} format The format to check
3671      * @return {Boolean} True if the format contains information about
3672      * date/day information.
3673      * @static
3674      * @method
3675      */
3676     formatContainsDateInfo : (function(){
3677         var stripEscapeRe = /(\\.)/g,
3678             dateInfoRe = /([djzmnYycU]|MS)/;
3679
3680         return function(format){
3681             return dateInfoRe.test(format.replace(stripEscapeRe, ''));
3682         };
3683     })(),
3684
3685     /**
3686      * The base format-code to formatting-function hashmap used by the {@link #format} method.
3687      * Formatting functions are strings (or functions which return strings) which
3688      * will return the appropriate value when evaluated in the context of the Date object
3689      * from which the {@link #format} method is called.
3690      * Add to / override these mappings for custom date formatting.
3691      * Note: Ext.Date.format() treats characters as literals if an appropriate mapping cannot be found.
3692      * Example:
3693      * <pre><code>
3694 Ext.Date.formatCodes.x = "Ext.util.Format.leftPad(this.getDate(), 2, '0')";
3695 console.log(Ext.Date.format(new Date(), 'X'); // returns the current day of the month
3696 </code></pre>
3697      * @type Object
3698      * @static
3699      */
3700     formatCodes : {
3701         d: "Ext.String.leftPad(this.getDate(), 2, '0')",
3702         D: "Ext.Date.getShortDayName(this.getDay())", // get localised short day name
3703         j: "this.getDate()",
3704         l: "Ext.Date.dayNames[this.getDay()]",
3705         N: "(this.getDay() ? this.getDay() : 7)",
3706         S: "Ext.Date.getSuffix(this)",
3707         w: "this.getDay()",
3708         z: "Ext.Date.getDayOfYear(this)",
3709         W: "Ext.String.leftPad(Ext.Date.getWeekOfYear(this), 2, '0')",
3710         F: "Ext.Date.monthNames[this.getMonth()]",
3711         m: "Ext.String.leftPad(this.getMonth() + 1, 2, '0')",
3712         M: "Ext.Date.getShortMonthName(this.getMonth())", // get localised short month name
3713         n: "(this.getMonth() + 1)",
3714         t: "Ext.Date.getDaysInMonth(this)",
3715         L: "(Ext.Date.isLeapYear(this) ? 1 : 0)",
3716         o: "(this.getFullYear() + (Ext.Date.getWeekOfYear(this) == 1 && this.getMonth() > 0 ? +1 : (Ext.Date.getWeekOfYear(this) >= 52 && this.getMonth() < 11 ? -1 : 0)))",
3717         Y: "Ext.String.leftPad(this.getFullYear(), 4, '0')",
3718         y: "('' + this.getFullYear()).substring(2, 4)",
3719         a: "(this.getHours() < 12 ? 'am' : 'pm')",
3720         A: "(this.getHours() < 12 ? 'AM' : 'PM')",
3721         g: "((this.getHours() % 12) ? this.getHours() % 12 : 12)",
3722         G: "this.getHours()",
3723         h: "Ext.String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0')",
3724         H: "Ext.String.leftPad(this.getHours(), 2, '0')",
3725         i: "Ext.String.leftPad(this.getMinutes(), 2, '0')",
3726         s: "Ext.String.leftPad(this.getSeconds(), 2, '0')",
3727         u: "Ext.String.leftPad(this.getMilliseconds(), 3, '0')",
3728         O: "Ext.Date.getGMTOffset(this)",
3729         P: "Ext.Date.getGMTOffset(this, true)",
3730         T: "Ext.Date.getTimezone(this)",
3731         Z: "(this.getTimezoneOffset() * -60)",
3732
3733         c: function() { // ISO-8601 -- GMT format
3734             for (var c = "Y-m-dTH:i:sP", code = [], i = 0, l = c.length; i < l; ++i) {
3735                 var e = c.charAt(i);
3736                 code.push(e == "T" ? "'T'" : utilDate.getFormatCode(e)); // treat T as a character literal
3737             }
3738             return code.join(" + ");
3739         },
3740         /*
3741         c: function() { // ISO-8601 -- UTC format
3742             return [
3743               "this.getUTCFullYear()", "'-'",
3744               "Ext.util.Format.leftPad(this.getUTCMonth() + 1, 2, '0')", "'-'",
3745               "Ext.util.Format.leftPad(this.getUTCDate(), 2, '0')",
3746               "'T'",
3747               "Ext.util.Format.leftPad(this.getUTCHours(), 2, '0')", "':'",
3748               "Ext.util.Format.leftPad(this.getUTCMinutes(), 2, '0')", "':'",
3749               "Ext.util.Format.leftPad(this.getUTCSeconds(), 2, '0')",
3750               "'Z'"
3751             ].join(" + ");
3752         },
3753         */
3754
3755         U: "Math.round(this.getTime() / 1000)"
3756     },
3757
3758     /**
3759      * Checks if the passed Date parameters will cause a javascript Date "rollover".
3760      * @param {Number} year 4-digit year
3761      * @param {Number} month 1-based month-of-year
3762      * @param {Number} day Day of month
3763      * @param {Number} hour (optional) Hour
3764      * @param {Number} minute (optional) Minute
3765      * @param {Number} second (optional) Second
3766      * @param {Number} millisecond (optional) Millisecond
3767      * @return {Boolean} true if the passed parameters do not cause a Date "rollover", false otherwise.
3768      * @static
3769      */
3770     isValid : function(y, m, d, h, i, s, ms) {
3771         // setup defaults
3772         h = h || 0;
3773         i = i || 0;
3774         s = s || 0;
3775         ms = ms || 0;
3776
3777         // Special handling for year < 100
3778         var dt = utilDate.add(new Date(y < 100 ? 100 : y, m - 1, d, h, i, s, ms), utilDate.YEAR, y < 100 ? y - 100 : 0);
3779
3780         return y == dt.getFullYear() &&
3781             m == dt.getMonth() + 1 &&
3782             d == dt.getDate() &&
3783             h == dt.getHours() &&
3784             i == dt.getMinutes() &&
3785             s == dt.getSeconds() &&
3786             ms == dt.getMilliseconds();
3787     },
3788
3789     /**
3790      * Parses the passed string using the specified date format.
3791      * Note that this function expects normal calendar dates, meaning that months are 1-based (i.e. 1 = January).
3792      * The {@link #defaults} hash will be used for any date value (i.e. year, month, day, hour, minute, second or millisecond)
3793      * which cannot be found in the passed string. If a corresponding default date value has not been specified in the {@link #defaults} hash,
3794      * the current date's year, month, day or DST-adjusted zero-hour time value will be used instead.
3795      * Keep in mind that the input date string must precisely match the specified format string
3796      * in order for the parse operation to be successful (failed parse operations return a null value).
3797      * <p>Example:</p><pre><code>
3798 //dt = Fri May 25 2007 (current date)
3799 var dt = new Date();
3800
3801 //dt = Thu May 25 2006 (today&#39;s month/day in 2006)
3802 dt = Ext.Date.parse("2006", "Y");
3803
3804 //dt = Sun Jan 15 2006 (all date parts specified)
3805 dt = Ext.Date.parse("2006-01-15", "Y-m-d");
3806
3807 //dt = Sun Jan 15 2006 15:20:01
3808 dt = Ext.Date.parse("2006-01-15 3:20:01 PM", "Y-m-d g:i:s A");
3809
3810 // attempt to parse Sun Feb 29 2006 03:20:01 in strict mode
3811 dt = Ext.Date.parse("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null
3812 </code></pre>
3813      * @param {String} input The raw date string.
3814      * @param {String} format The expected date string format.
3815      * @param {Boolean} strict (optional) True to validate date strings while parsing (i.e. prevents javascript Date "rollover")
3816                         (defaults to false). Invalid date strings will return null when parsed.
3817      * @return {Date} The parsed Date.
3818      * @static
3819      */
3820     parse : function(input, format, strict) {
3821         var p = utilDate.parseFunctions;
3822         if (p[format] == null) {
3823             utilDate.createParser(format);
3824         }
3825         return p[format](input, Ext.isDefined(strict) ? strict : utilDate.useStrict);
3826     },
3827
3828     // Backwards compat
3829     parseDate: function(input, format, strict){
3830         return utilDate.parse(input, format, strict);
3831     },
3832
3833
3834     // private
3835     getFormatCode : function(character) {
3836         var f = utilDate.formatCodes[character];
3837
3838         if (f) {
3839           f = typeof f == 'function'? f() : f;
3840           utilDate.formatCodes[character] = f; // reassign function result to prevent repeated execution
3841         }
3842
3843         // note: unknown characters are treated as literals
3844         return f || ("'" + Ext.String.escape(character) + "'");
3845     },
3846
3847     // private
3848     createFormat : function(format) {
3849         var code = [],
3850             special = false,
3851             ch = '';
3852
3853         for (var i = 0; i < format.length; ++i) {
3854             ch = format.charAt(i);
3855             if (!special && ch == "\\") {
3856                 special = true;
3857             } else if (special) {
3858                 special = false;
3859                 code.push("'" + Ext.String.escape(ch) + "'");
3860             } else {
3861                 code.push(utilDate.getFormatCode(ch));
3862             }
3863         }
3864         utilDate.formatFunctions[format] = Ext.functionFactory("return " + code.join('+'));
3865     },
3866
3867     // private
3868     createParser : (function() {
3869         var code = [
3870             "var dt, y, m, d, h, i, s, ms, o, z, zz, u, v,",
3871                 "def = Ext.Date.defaults,",
3872                 "results = String(input).match(Ext.Date.parseRegexes[{0}]);", // either null, or an array of matched strings
3873
3874             "if(results){",
3875                 "{1}",
3876
3877                 "if(u != null){", // i.e. unix time is defined
3878                     "v = new Date(u * 1000);", // give top priority to UNIX time
3879                 "}else{",
3880                     // create Date object representing midnight of the current day;
3881                     // this will provide us with our date defaults
3882                     // (note: clearTime() handles Daylight Saving Time automatically)
3883                     "dt = Ext.Date.clearTime(new Date);",
3884
3885                     // date calculations (note: these calculations create a dependency on Ext.Number.from())
3886                     "y = Ext.Number.from(y, Ext.Number.from(def.y, dt.getFullYear()));",
3887                     "m = Ext.Number.from(m, Ext.Number.from(def.m - 1, dt.getMonth()));",
3888                     "d = Ext.Number.from(d, Ext.Number.from(def.d, dt.getDate()));",
3889
3890                     // time calculations (note: these calculations create a dependency on Ext.Number.from())
3891                     "h  = Ext.Number.from(h, Ext.Number.from(def.h, dt.getHours()));",
3892                     "i  = Ext.Number.from(i, Ext.Number.from(def.i, dt.getMinutes()));",
3893                     "s  = Ext.Number.from(s, Ext.Number.from(def.s, dt.getSeconds()));",
3894                     "ms = Ext.Number.from(ms, Ext.Number.from(def.ms, dt.getMilliseconds()));",
3895
3896                     "if(z >= 0 && y >= 0){",
3897                         // both the year and zero-based day of year are defined and >= 0.
3898                         // these 2 values alone provide sufficient info to create a full date object
3899
3900                         // create Date object representing January 1st for the given year
3901                         // handle years < 100 appropriately
3902                         "v = Ext.Date.add(new Date(y < 100 ? 100 : y, 0, 1, h, i, s, ms), Ext.Date.YEAR, y < 100 ? y - 100 : 0);",
3903
3904                         // then add day of year, checking for Date "rollover" if necessary
3905                         "v = !strict? v : (strict === true && (z <= 364 || (Ext.Date.isLeapYear(v) && z <= 365))? Ext.Date.add(v, Ext.Date.DAY, z) : null);",
3906                     "}else if(strict === true && !Ext.Date.isValid(y, m + 1, d, h, i, s, ms)){", // check for Date "rollover"
3907                         "v = null;", // invalid date, so return null
3908                     "}else{",
3909                         // plain old Date object
3910                         // handle years < 100 properly
3911                         "v = Ext.Date.add(new Date(y < 100 ? 100 : y, m, d, h, i, s, ms), Ext.Date.YEAR, y < 100 ? y - 100 : 0);",
3912                     "}",
3913                 "}",
3914             "}",
3915
3916             "if(v){",
3917                 // favour UTC offset over GMT offset
3918                 "if(zz != null){",
3919                     // reset to UTC, then add offset
3920                     "v = Ext.Date.add(v, Ext.Date.SECOND, -v.getTimezoneOffset() * 60 - zz);",
3921                 "}else if(o){",
3922                     // reset to GMT, then add offset
3923                     "v = Ext.Date.add(v, Ext.Date.MINUTE, -v.getTimezoneOffset() + (sn == '+'? -1 : 1) * (hr * 60 + mn));",
3924                 "}",
3925             "}",
3926
3927             "return v;"
3928         ].join('\n');
3929
3930         return function(format) {
3931             var regexNum = utilDate.parseRegexes.length,
3932                 currentGroup = 1,
3933                 calc = [],
3934                 regex = [],
3935                 special = false,
3936                 ch = "";
3937
3938             for (var i = 0; i < format.length; ++i) {
3939                 ch = format.charAt(i);
3940                 if (!special && ch == "\\") {
3941                     special = true;
3942                 } else if (special) {
3943                     special = false;
3944                     regex.push(Ext.String.escape(ch));
3945                 } else {
3946                     var obj = utilDate.formatCodeToRegex(ch, currentGroup);
3947                     currentGroup += obj.g;
3948                     regex.push(obj.s);
3949                     if (obj.g && obj.c) {
3950                         calc.push(obj.c);
3951                     }
3952                 }
3953             }
3954
3955             utilDate.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$", 'i');
3956             utilDate.parseFunctions[format] = Ext.functionFactory("input", "strict", xf(code, regexNum, calc.join('')));
3957         };
3958     })(),
3959
3960     // private
3961     parseCodes : {
3962         /*
3963          * Notes:
3964          * g = {Number} calculation group (0 or 1. only group 1 contributes to date calculations.)
3965          * c = {String} calculation method (required for group 1. null for group 0. {0} = currentGroup - position in regex result array)
3966          * s = {String} regex pattern. all matches are stored in results[], and are accessible by the calculation mapped to 'c'
3967          */
3968         d: {
3969             g:1,
3970             c:"d = parseInt(results[{0}], 10);\n",
3971             s:"(\\d{2})" // day of month with leading zeroes (01 - 31)
3972         },
3973         j: {
3974             g:1,
3975             c:"d = parseInt(results[{0}], 10);\n",
3976             s:"(\\d{1,2})" // day of month without leading zeroes (1 - 31)
3977         },
3978         D: function() {
3979             for (var a = [], i = 0; i < 7; a.push(utilDate.getShortDayName(i)), ++i); // get localised short day names
3980             return {
3981                 g:0,
3982                 c:null,
3983                 s:"(?:" + a.join("|") +")"
3984             };
3985         },
3986         l: function() {
3987             return {
3988                 g:0,
3989                 c:null,
3990                 s:"(?:" + utilDate.dayNames.join("|") + ")"
3991             };
3992         },
3993         N: {
3994             g:0,
3995             c:null,
3996             s:"[1-7]" // ISO-8601 day number (1 (monday) - 7 (sunday))
3997         },
3998         S: {
3999             g:0,
4000             c:null,
4001             s:"(?:st|nd|rd|th)"
4002         },
4003         w: {
4004             g:0,
4005             c:null,
4006             s:"[0-6]" // javascript day number (0 (sunday) - 6 (saturday))
4007         },
4008         z: {
4009             g:1,
4010             c:"z = parseInt(results[{0}], 10);\n",
4011             s:"(\\d{1,3})" // day of the year (0 - 364 (365 in leap years))
4012         },
4013         W: {
4014             g:0,
4015             c:null,
4016             s:"(?:\\d{2})" // ISO-8601 week number (with leading zero)
4017         },
4018         F: function() {
4019             return {
4020                 g:1,
4021                 c:"m = parseInt(Ext.Date.getMonthNumber(results[{0}]), 10);\n", // get localised month number
4022                 s:"(" + utilDate.monthNames.join("|") + ")"
4023             };
4024         },
4025         M: function() {
4026             for (var a = [], i = 0; i < 12; a.push(utilDate.getShortMonthName(i)), ++i); // get localised short month names
4027             return Ext.applyIf({
4028                 s:"(" + a.join("|") + ")"
4029             }, utilDate.formatCodeToRegex("F"));
4030         },
4031         m: {
4032             g:1,
4033             c:"m = parseInt(results[{0}], 10) - 1;\n",
4034             s:"(\\d{2})" // month number with leading zeros (01 - 12)
4035         },
4036         n: {
4037             g:1,
4038             c:"m = parseInt(results[{0}], 10) - 1;\n",
4039             s:"(\\d{1,2})" // month number without leading zeros (1 - 12)
4040         },
4041         t: {
4042             g:0,
4043             c:null,
4044             s:"(?:\\d{2})" // no. of days in the month (28 - 31)
4045         },
4046         L: {
4047             g:0,
4048             c:null,
4049             s:"(?:1|0)"
4050         },
4051         o: function() {
4052             return utilDate.formatCodeToRegex("Y");
4053         },
4054         Y: {
4055             g:1,
4056             c:"y = parseInt(results[{0}], 10);\n",
4057             s:"(\\d{4})" // 4-digit year
4058         },
4059         y: {
4060             g:1,
4061             c:"var ty = parseInt(results[{0}], 10);\n"
4062                 + "y = ty > Ext.Date.y2kYear ? 1900 + ty : 2000 + ty;\n", // 2-digit year
4063             s:"(\\d{1,2})"
4064         },
4065         /*
4066          * In the am/pm parsing routines, we allow both upper and lower case
4067          * even though it doesn't exactly match the spec. It gives much more flexibility
4068          * in being able to specify case insensitive regexes.
4069          */
4070         a: {
4071             g:1,
4072             c:"if (/(am)/i.test(results[{0}])) {\n"
4073                 + "if (!h || h == 12) { h = 0; }\n"
4074                 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
4075             s:"(am|pm|AM|PM)"
4076         },
4077         A: {
4078             g:1,
4079             c:"if (/(am)/i.test(results[{0}])) {\n"
4080                 + "if (!h || h == 12) { h = 0; }\n"
4081                 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
4082             s:"(AM|PM|am|pm)"
4083         },
4084         g: function() {
4085             return utilDate.formatCodeToRegex("G");
4086         },
4087         G: {
4088             g:1,
4089             c:"h = parseInt(results[{0}], 10);\n",
4090             s:"(\\d{1,2})" // 24-hr format of an hour without leading zeroes (0 - 23)
4091         },
4092         h: function() {
4093             return utilDate.formatCodeToRegex("H");
4094         },
4095         H: {
4096             g:1,
4097             c:"h = parseInt(results[{0}], 10);\n",
4098             s:"(\\d{2})" //  24-hr format of an hour with leading zeroes (00 - 23)
4099         },
4100         i: {
4101             g:1,
4102             c:"i = parseInt(results[{0}], 10);\n",
4103             s:"(\\d{2})" // minutes with leading zeros (00 - 59)
4104         },
4105         s: {
4106             g:1,
4107             c:"s = parseInt(results[{0}], 10);\n",
4108             s:"(\\d{2})" // seconds with leading zeros (00 - 59)
4109         },
4110         u: {
4111             g:1,
4112             c:"ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",
4113             s:"(\\d+)" // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
4114         },
4115         O: {
4116             g:1,
4117             c:[
4118                 "o = results[{0}];",
4119                 "var sn = o.substring(0,1),", // get + / - sign
4120                     "hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
4121                     "mn = o.substring(3,5) % 60;", // get minutes
4122                 "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
4123             ].join("\n"),
4124             s: "([+\-]\\d{4})" // GMT offset in hrs and mins
4125         },
4126         P: {
4127             g:1,
4128             c:[
4129                 "o = results[{0}];",
4130                 "var sn = o.substring(0,1),", // get + / - sign
4131                     "hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
4132                     "mn = o.substring(4,6) % 60;", // get minutes
4133                 "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
4134             ].join("\n"),
4135             s: "([+\-]\\d{2}:\\d{2})" // GMT offset in hrs and mins (with colon separator)
4136         },
4137         T: {
4138             g:0,
4139             c:null,
4140             s:"[A-Z]{1,4}" // timezone abbrev. may be between 1 - 4 chars
4141         },
4142         Z: {
4143             g:1,
4144             c:"zz = results[{0}] * 1;\n" // -43200 <= UTC offset <= 50400
4145                   + "zz = (-43200 <= zz && zz <= 50400)? zz : null;\n",
4146             s:"([+\-]?\\d{1,5})" // leading '+' sign is optional for UTC offset
4147         },
4148         c: function() {
4149             var calc = [],
4150                 arr = [
4151                     utilDate.formatCodeToRegex("Y", 1), // year
4152                     utilDate.formatCodeToRegex("m", 2), // month
4153                     utilDate.formatCodeToRegex("d", 3), // day
4154                     utilDate.formatCodeToRegex("h", 4), // hour
4155                     utilDate.formatCodeToRegex("i", 5), // minute
4156                     utilDate.formatCodeToRegex("s", 6), // second
4157                     {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)
4158                     {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
4159                         "if(results[8]) {", // timezone specified
4160                             "if(results[8] == 'Z'){",
4161                                 "zz = 0;", // UTC
4162                             "}else if (results[8].indexOf(':') > -1){",
4163                                 utilDate.formatCodeToRegex("P", 8).c, // timezone offset with colon separator
4164                             "}else{",
4165                                 utilDate.formatCodeToRegex("O", 8).c, // timezone offset without colon separator
4166                             "}",
4167                         "}"
4168                     ].join('\n')}
4169                 ];
4170
4171             for (var i = 0, l = arr.length; i < l; ++i) {
4172                 calc.push(arr[i].c);
4173             }
4174
4175             return {
4176                 g:1,
4177                 c:calc.join(""),
4178                 s:[
4179                     arr[0].s, // year (required)
4180                     "(?:", "-", arr[1].s, // month (optional)
4181                         "(?:", "-", arr[2].s, // day (optional)
4182                             "(?:",
4183                                 "(?:T| )?", // time delimiter -- either a "T" or a single blank space
4184                                 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
4185                                 "(?::", arr[5].s, ")?", // seconds (optional)
4186                                 "(?:(?:\\.|,)(\\d+))?", // decimal fraction of a second (e.g. ",12345" or ".98765") (optional)
4187                                 "(Z|(?:[-+]\\d{2}(?::)?\\d{2}))?", // "Z" (UTC) or "-0530" (UTC offset without colon delimiter) or "+08:00" (UTC offset with colon delimiter) (optional)
4188                             ")?",
4189                         ")?",
4190                     ")?"
4191                 ].join("")
4192             };
4193         },
4194         U: {
4195             g:1,
4196             c:"u = parseInt(results[{0}], 10);\n",
4197             s:"(-?\\d+)" // leading minus sign indicates seconds before UNIX epoch
4198         }
4199     },
4200
4201     //Old Ext.Date prototype methods.
4202     // private
4203     dateFormat: function(date, format) {
4204         return utilDate.format(date, format);
4205     },
4206
4207     /**
4208      * Formats a date given the supplied format string.
4209      * @param {Date} date The date to format
4210      * @param {String} format The format string
4211      * @return {String} The formatted date
4212      */
4213     format: function(date, format) {
4214         if (utilDate.formatFunctions[format] == null) {
4215             utilDate.createFormat(format);
4216         }
4217         var result = utilDate.formatFunctions[format].call(date);
4218         return result + '';
4219     },
4220
4221     /**
4222      * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
4223      *
4224      * Note: The date string returned by the javascript Date object's toString() method varies
4225      * between browsers (e.g. FF vs IE) and system region settings (e.g. IE in Asia vs IE in America).
4226      * For a given date string e.g. "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)",
4227      * getTimezone() first tries to get the timezone abbreviation from between a pair of parentheses
4228      * (which may or may not be present), failing which it proceeds to get the timezone abbreviation
4229      * from the GMT offset portion of the date string.
4230      * @param {Date} date The date
4231      * @return {String} The abbreviated timezone name (e.g. 'CST', 'PDT', 'EDT', 'MPST' ...).
4232      */
4233     getTimezone : function(date) {
4234         // the following list shows the differences between date strings from different browsers on a WinXP SP2 machine from an Asian locale:
4235         //
4236         // Opera  : "Thu, 25 Oct 2007 22:53:45 GMT+0800" -- shortest (weirdest) date string of the lot
4237         // 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)
4238         // FF     : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone
4239         // IE     : "Thu Oct 25 22:54:35 UTC+0800 2007" -- (Asian system setting) look for 3-4 letter timezone abbrev
4240         // IE     : "Thu Oct 25 17:06:37 PDT 2007" -- (American system setting) look for 3-4 letter timezone abbrev
4241         //
4242         // this crazy regex attempts to guess the correct timezone abbreviation despite these differences.
4243         // step 1: (?:\((.*)\) -- find timezone in parentheses
4244         // 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
4245         // step 3: remove all non uppercase characters found in step 1 and 2
4246         return date.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, "");
4247     },
4248
4249     /**
4250      * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
4251      * @param {Date} date The date
4252      * @param {Boolean} colon (optional) true to separate the hours and minutes with a colon (defaults to false).
4253      * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600').
4254      */
4255     getGMTOffset : function(date, colon) {
4256         var offset = date.getTimezoneOffset();
4257         return (offset > 0 ? "-" : "+")
4258             + Ext.String.leftPad(Math.floor(Math.abs(offset) / 60), 2, "0")
4259             + (colon ? ":" : "")
4260             + Ext.String.leftPad(Math.abs(offset % 60), 2, "0");
4261     },
4262
4263     /**
4264      * Get the numeric day number of the year, adjusted for leap year.
4265      * @param {Date} date The date
4266      * @return {Number} 0 to 364 (365 in leap years).
4267      */
4268     getDayOfYear: function(date) {
4269         var num = 0,
4270             d = Ext.Date.clone(date),
4271             m = date.getMonth(),
4272             i;
4273
4274         for (i = 0, d.setDate(1), d.setMonth(0); i < m; d.setMonth(++i)) {
4275             num += utilDate.getDaysInMonth(d);
4276         }
4277         return num + date.getDate() - 1;
4278     },
4279
4280     /**
4281      * Get the numeric ISO-8601 week number of the year.
4282      * (equivalent to the format specifier 'W', but without a leading zero).
4283      * @param {Date} date The date
4284      * @return {Number} 1 to 53
4285      * @method
4286      */
4287     getWeekOfYear : (function() {
4288         // adapted from http://www.merlyn.demon.co.uk/weekcalc.htm
4289         var ms1d = 864e5, // milliseconds in a day
4290             ms7d = 7 * ms1d; // milliseconds in a week
4291
4292         return function(date) { // return a closure so constants get calculated only once
4293             var DC3 = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate() + 3) / ms1d, // an Absolute Day Number
4294                 AWN = Math.floor(DC3 / 7), // an Absolute Week Number
4295                 Wyr = new Date(AWN * ms7d).getUTCFullYear();
4296
4297             return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
4298         };
4299     })(),
4300
4301     /**
4302      * Checks if the current date falls within a leap year.
4303      * @param {Date} date The date
4304      * @return {Boolean} True if the current date falls within a leap year, false otherwise.
4305      */
4306     isLeapYear : function(date) {
4307         var year = date.getFullYear();
4308         return !!((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
4309     },
4310
4311     /**
4312      * Get the first day of the current month, adjusted for leap year.  The returned value
4313      * is the numeric day index within the week (0-6) which can be used in conjunction with
4314      * the {@link #monthNames} array to retrieve the textual day name.
4315      * Example:
4316      * <pre><code>
4317 var dt = new Date('1/10/2007'),
4318     firstDay = Ext.Date.getFirstDayOfMonth(dt);
4319 console.log(Ext.Date.dayNames[firstDay]); //output: 'Monday'
4320      * </code></pre>
4321      * @param {Date} date The date
4322      * @return {Number} The day number (0-6).
4323      */
4324     getFirstDayOfMonth : function(date) {
4325         var day = (date.getDay() - (date.getDate() - 1)) % 7;
4326         return (day < 0) ? (day + 7) : day;
4327     },
4328
4329     /**
4330      * Get the last day of the current month, adjusted for leap year.  The returned value
4331      * is the numeric day index within the week (0-6) which can be used in conjunction with
4332      * the {@link #monthNames} array to retrieve the textual day name.
4333      * Example:
4334      * <pre><code>
4335 var dt = new Date('1/10/2007'),
4336     lastDay = Ext.Date.getLastDayOfMonth(dt);
4337 console.log(Ext.Date.dayNames[lastDay]); //output: 'Wednesday'
4338      * </code></pre>
4339      * @param {Date} date The date
4340      * @return {Number} The day number (0-6).
4341      */
4342     getLastDayOfMonth : function(date) {
4343         return utilDate.getLastDateOfMonth(date).getDay();
4344     },
4345
4346
4347     /**
4348      * Get the date of the first day of the month in which this date resides.
4349      * @param {Date} date The date
4350      * @return {Date}
4351      */
4352     getFirstDateOfMonth : function(date) {
4353         return new Date(date.getFullYear(), date.getMonth(), 1);
4354     },
4355
4356     /**
4357      * Get the date of the last day of the month in which this date resides.
4358      * @param {Date} date The date
4359      * @return {Date}
4360      */
4361     getLastDateOfMonth : function(date) {
4362         return new Date(date.getFullYear(), date.getMonth(), utilDate.getDaysInMonth(date));
4363     },
4364
4365     /**
4366      * Get the number of days in the current month, adjusted for leap year.
4367      * @param {Date} date The date
4368      * @return {Number} The number of days in the month.
4369      * @method
4370      */
4371     getDaysInMonth: (function() {
4372         var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
4373
4374         return function(date) { // return a closure for efficiency
4375             var m = date.getMonth();
4376
4377             return m == 1 && utilDate.isLeapYear(date) ? 29 : daysInMonth[m];
4378         };
4379     })(),
4380
4381     /**
4382      * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
4383      * @param {Date} date The date
4384      * @return {String} 'st, 'nd', 'rd' or 'th'.
4385      */
4386     getSuffix : function(date) {
4387         switch (date.getDate()) {
4388             case 1:
4389             case 21:
4390             case 31:
4391                 return "st";
4392             case 2:
4393             case 22:
4394                 return "nd";
4395             case 3:
4396             case 23:
4397                 return "rd";
4398             default:
4399                 return "th";
4400         }
4401     },
4402
4403     /**
4404      * Creates and returns a new Date instance with the exact same date value as the called instance.
4405      * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
4406      * variable will also be changed.  When the intention is to create a new variable that will not
4407      * modify the original instance, you should create a clone.
4408      *
4409      * Example of correctly cloning a date:
4410      * <pre><code>
4411 //wrong way:
4412 var orig = new Date('10/1/2006');
4413 var copy = orig;
4414 copy.setDate(5);
4415 console.log(orig);  //returns 'Thu Oct 05 2006'!
4416
4417 //correct way:
4418 var orig = new Date('10/1/2006'),
4419     copy = Ext.Date.clone(orig);
4420 copy.setDate(5);
4421 console.log(orig);  //returns 'Thu Oct 01 2006'
4422      * </code></pre>
4423      * @param {Date} date The date
4424      * @return {Date} The new Date instance.
4425      */
4426     clone : function(date) {
4427         return new Date(date.getTime());
4428     },
4429
4430     /**
4431      * Checks if the current date is affected by Daylight Saving Time (DST).
4432      * @param {Date} date The date
4433      * @return {Boolean} True if the current date is affected by DST.
4434      */
4435     isDST : function(date) {
4436         // adapted from http://sencha.com/forum/showthread.php?p=247172#post247172
4437         // courtesy of @geoffrey.mcgill
4438         return new Date(date.getFullYear(), 0, 1).getTimezoneOffset() != date.getTimezoneOffset();
4439     },
4440
4441     /**
4442      * Attempts to clear all time information from this Date by setting the time to midnight of the same day,
4443      * automatically adjusting for Daylight Saving Time (DST) where applicable.
4444      * (note: DST timezone information for the browser's host operating system is assumed to be up-to-date)
4445      * @param {Date} date The date
4446      * @param {Boolean} clone true to create a clone of this date, clear the time and return it (defaults to false).
4447      * @return {Date} this or the clone.
4448      */
4449     clearTime : function(date, clone) {
4450         if (clone) {
4451             return Ext.Date.clearTime(Ext.Date.clone(date));
4452         }
4453
4454         // get current date before clearing time
4455         var d = date.getDate();
4456
4457         // clear time
4458         date.setHours(0);
4459         date.setMinutes(0);
4460         date.setSeconds(0);
4461         date.setMilliseconds(0);
4462
4463         if (date.getDate() != d) { // account for DST (i.e. day of month changed when setting hour = 0)
4464             // note: DST adjustments are assumed to occur in multiples of 1 hour (this is almost always the case)
4465             // refer to http://www.timeanddate.com/time/aboutdst.html for the (rare) exceptions to this rule
4466
4467             // increment hour until cloned date == current date
4468             for (var hr = 1, c = utilDate.add(date, Ext.Date.HOUR, hr); c.getDate() != d; hr++, c = utilDate.add(date, Ext.Date.HOUR, hr));
4469
4470             date.setDate(d);
4471             date.setHours(c.getHours());
4472         }
4473
4474         return date;
4475     },
4476
4477     /**
4478      * Provides a convenient method for performing basic date arithmetic. This method
4479      * does not modify the Date instance being called - it creates and returns
4480      * a new Date instance containing the resulting date value.
4481      *
4482      * Examples:
4483      * <pre><code>
4484 // Basic usage:
4485 var dt = Ext.Date.add(new Date('10/29/2006'), Ext.Date.DAY, 5);
4486 console.log(dt); //returns 'Fri Nov 03 2006 00:00:00'
4487
4488 // Negative values will be subtracted:
4489 var dt2 = Ext.Date.add(new Date('10/1/2006'), Ext.Date.DAY, -5);
4490 console.log(dt2); //returns 'Tue Sep 26 2006 00:00:00'
4491
4492      * </code></pre>
4493      *
4494      * @param {Date} date The date to modify
4495      * @param {String} interval A valid date interval enum value.
4496      * @param {Number} value The amount to add to the current date.
4497      * @return {Date} The new Date instance.
4498      */
4499     add : function(date, interval, value) {
4500         var d = Ext.Date.clone(date),
4501             Date = Ext.Date;
4502         if (!interval || value === 0) return d;
4503
4504         switch(interval.toLowerCase()) {
4505             case Ext.Date.MILLI:
4506                 d.setMilliseconds(d.getMilliseconds() + value);
4507                 break;
4508             case Ext.Date.SECOND:
4509                 d.setSeconds(d.getSeconds() + value);
4510                 break;
4511             case Ext.Date.MINUTE:
4512                 d.setMinutes(d.getMinutes() + value);
4513                 break;
4514             case Ext.Date.HOUR:
4515                 d.setHours(d.getHours() + value);
4516                 break;
4517             case Ext.Date.DAY:
4518                 d.setDate(d.getDate() + value);
4519                 break;
4520             case Ext.Date.MONTH:
4521                 var day = date.getDate();
4522                 if (day > 28) {
4523                     day = Math.min(day, Ext.Date.getLastDateOfMonth(Ext.Date.add(Ext.Date.getFirstDateOfMonth(date), 'mo', value)).getDate());
4524                 }
4525                 d.setDate(day);
4526                 d.setMonth(date.getMonth() + value);
4527                 break;
4528             case Ext.Date.YEAR:
4529                 d.setFullYear(date.getFullYear() + value);
4530                 break;
4531         }
4532         return d;
4533     },
4534
4535     /**
4536      * Checks if a date falls on or between the given start and end dates.
4537      * @param {Date} date The date to check
4538      * @param {Date} start Start date
4539      * @param {Date} end End date
4540      * @return {Boolean} true if this date falls on or between the given start and end dates.
4541      */
4542     between : function(date, start, end) {
4543         var t = date.getTime();
4544         return start.getTime() <= t && t <= end.getTime();
4545     },
4546
4547     //Maintains compatibility with old static and prototype window.Date methods.
4548     compat: function() {
4549         var nativeDate = window.Date,
4550             p, u,
4551             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'],
4552             proto = ['dateFormat', 'format', 'getTimezone', 'getGMTOffset', 'getDayOfYear', 'getWeekOfYear', 'isLeapYear', 'getFirstDayOfMonth', 'getLastDayOfMonth', 'getDaysInMonth', 'getSuffix', 'clone', 'isDST', 'clearTime', 'add', 'between'];
4553
4554         //Append statics
4555         Ext.Array.forEach(statics, function(s) {
4556             nativeDate[s] = utilDate[s];
4557         });
4558
4559         //Append to prototype
4560         Ext.Array.forEach(proto, function(s) {
4561             nativeDate.prototype[s] = function() {
4562                 var args = Array.prototype.slice.call(arguments);
4563                 args.unshift(this);
4564                 return utilDate[s].apply(utilDate, args);
4565             };
4566         });
4567     }
4568 };
4569
4570 var utilDate = Ext.Date;
4571
4572 })();
4573
4574 /**
4575  * @author Jacky Nguyen <jacky@sencha.com>
4576  * @docauthor Jacky Nguyen <jacky@sencha.com>
4577  * @class Ext.Base
4578  *
4579  * The root of all classes created with {@link Ext#define}
4580  * All prototype and static members of this class are inherited by any other class
4581  *
4582  */
4583 (function(flexSetter) {
4584
4585 var Base = Ext.Base = function() {};
4586     Base.prototype = {
4587         $className: 'Ext.Base',
4588
4589         $class: Base,
4590
4591         /**
4592          * Get the reference to the current class from which this object was instantiated. Unlike {@link Ext.Base#statics},
4593          * `this.self` is scope-dependent and it's meant to be used for dynamic inheritance. See {@link Ext.Base#statics}
4594          * for a detailed comparison
4595          *
4596          *     Ext.define('My.Cat', {
4597          *         statics: {
4598          *             speciesName: 'Cat' // My.Cat.speciesName = 'Cat'
4599          *         },
4600          *
4601          *         constructor: function() {
4602          *             alert(this.self.speciesName); / dependent on 'this'
4603          *
4604          *             return this;
4605          *         },
4606          *
4607          *         clone: function() {
4608          *             return new this.self();
4609          *         }
4610          *     });
4611          *
4612          *
4613          *     Ext.define('My.SnowLeopard', {
4614          *         extend: 'My.Cat',
4615          *         statics: {
4616          *             speciesName: 'Snow Leopard'         // My.SnowLeopard.speciesName = 'Snow Leopard'
4617          *         }
4618          *     });
4619          *
4620          *     var cat = new My.Cat();                     // alerts 'Cat'
4621          *     var snowLeopard = new My.SnowLeopard();     // alerts 'Snow Leopard'
4622          *
4623          *     var clone = snowLeopard.clone();
4624          *     alert(Ext.getClassName(clone));             // alerts 'My.SnowLeopard'
4625          *
4626          * @type Class
4627          * @protected
4628          */
4629         self: Base,
4630
4631         // Default constructor, simply returns `this`
4632         constructor: function() {
4633             return this;
4634         },
4635
4636         /**
4637          * Initialize configuration for this class. a typical example:
4638          *
4639          *     Ext.define('My.awesome.Class', {
4640          *         // The default config
4641          *         config: {
4642          *             name: 'Awesome',
4643          *             isAwesome: true
4644          *         },
4645          *
4646          *         constructor: function(config) {
4647          *             this.initConfig(config);
4648          *
4649          *             return this;
4650          *         }
4651          *     });
4652          *
4653          *     var awesome = new My.awesome.Class({
4654          *         name: 'Super Awesome'
4655          *     });
4656          *
4657          *     alert(awesome.getName()); // 'Super Awesome'
4658          *
4659          * @protected
4660          * @param {Object} config
4661          * @return {Object} mixins The mixin prototypes as key - value pairs
4662          */
4663         initConfig: function(config) {
4664             if (!this.$configInited) {
4665                 this.config = Ext.Object.merge({}, this.config || {}, config || {});
4666
4667                 this.applyConfig(this.config);
4668
4669                 this.$configInited = true;
4670             }
4671
4672             return this;
4673         },
4674
4675         /**
4676          * @private
4677          */
4678         setConfig: function(config) {
4679             this.applyConfig(config || {});
4680
4681             return this;
4682         },
4683
4684         /**
4685          * @private
4686          */
4687         applyConfig: flexSetter(function(name, value) {
4688             var setter = 'set' + Ext.String.capitalize(name);
4689
4690             if (typeof this[setter] === 'function') {
4691                 this[setter].call(this, value);
4692             }
4693
4694             return this;
4695         }),
4696
4697         /**
4698          * Call the parent's overridden method. For example:
4699          *
4700          *     Ext.define('My.own.A', {
4701          *         constructor: function(test) {
4702          *             alert(test);
4703          *         }
4704          *     });
4705          *
4706          *     Ext.define('My.own.B', {
4707          *         extend: 'My.own.A',
4708          *
4709          *         constructor: function(test) {
4710          *             alert(test);
4711          *
4712          *             this.callParent([test + 1]);
4713          *         }
4714          *     });
4715          *
4716          *     Ext.define('My.own.C', {
4717          *         extend: 'My.own.B',
4718          *
4719          *         constructor: function() {
4720          *             alert("Going to call parent's overriden constructor...");
4721          *
4722          *             this.callParent(arguments);
4723          *         }
4724          *     });
4725          *
4726          *     var a = new My.own.A(1); // alerts '1'
4727          *     var b = new My.own.B(1); // alerts '1', then alerts '2'
4728          *     var c = new My.own.C(2); // alerts "Going to call parent's overriden constructor..."
4729          *                              // alerts '2', then alerts '3'
4730          *
4731          * @protected
4732          * @param {Array/Arguments} args The arguments, either an array or the `arguments` object
4733          * from the current method, for example: `this.callParent(arguments)`
4734          * @return {Mixed} Returns the result from the superclass' method
4735          */
4736         callParent: function(args) {
4737             var method = this.callParent.caller,
4738                 parentClass, methodName;
4739
4740             if (!method.$owner) {
4741                 if (!method.caller) {
4742                     Ext.Error.raise({
4743                         sourceClass: Ext.getClassName(this),
4744                         sourceMethod: "callParent",
4745                         msg: "Attempting to call a protected method from the public scope, which is not allowed"
4746                     });
4747                 }
4748
4749                 method = method.caller;
4750             }
4751
4752             parentClass = method.$owner.superclass;
4753             methodName = method.$name;
4754
4755             if (!(methodName in parentClass)) {
4756                 Ext.Error.raise({
4757                     sourceClass: Ext.getClassName(this),
4758                     sourceMethod: methodName,
4759                     msg: "this.callParent() was called but there's no such method (" + methodName +
4760                          ") found in the parent class (" + (Ext.getClassName(parentClass) || 'Object') + ")"
4761                  });
4762             }
4763
4764             return parentClass[methodName].apply(this, args || []);
4765         },
4766
4767
4768         /**
4769          * Get the reference to the class from which this object was instantiated. Note that unlike {@link Ext.Base#self},
4770          * `this.statics()` is scope-independent and it always returns the class from which it was called, regardless of what
4771          * `this` points to during run-time
4772          *
4773          *     Ext.define('My.Cat', {
4774          *         statics: {
4775          *             totalCreated: 0,
4776          *             speciesName: 'Cat' // My.Cat.speciesName = 'Cat'
4777          *         },
4778          *  
4779          *         constructor: function() {
4780          *             var statics = this.statics();
4781          *  
4782          *             alert(statics.speciesName);     // always equals to 'Cat' no matter what 'this' refers to
4783          *                                             // equivalent to: My.Cat.speciesName
4784          *  
4785          *             alert(this.self.speciesName);   // dependent on 'this'
4786          *  
4787          *             statics.totalCreated++;
4788          *  
4789          *             return this;
4790          *         },
4791          *  
4792          *         clone: function() {
4793          *             var cloned = new this.self;                      // dependent on 'this'
4794          *  
4795          *             cloned.groupName = this.statics().speciesName;   // equivalent to: My.Cat.speciesName
4796          *  
4797          *             return cloned;
4798          *         }
4799          *     });
4800          *
4801          *
4802          *     Ext.define('My.SnowLeopard', {
4803          *         extend: 'My.Cat',
4804          *  
4805          *         statics: {
4806          *             speciesName: 'Snow Leopard'     // My.SnowLeopard.speciesName = 'Snow Leopard'
4807          *         },
4808          *  
4809          *         constructor: function() {
4810          *             this.callParent();
4811          *         }
4812          *     });
4813          *
4814          *     var cat = new My.Cat();                 // alerts 'Cat', then alerts 'Cat'
4815          *
4816          *     var snowLeopard = new My.SnowLeopard(); // alerts 'Cat', then alerts 'Snow Leopard'
4817          *
4818          *     var clone = snowLeopard.clone();
4819          *     alert(Ext.getClassName(clone));         // alerts 'My.SnowLeopard'
4820          *     alert(clone.groupName);                 // alerts 'Cat'
4821          *
4822          *     alert(My.Cat.totalCreated);             // alerts 3
4823          *
4824          * @protected
4825          * @return {Class}
4826          */
4827         statics: function() {
4828             var method = this.statics.caller,
4829                 self = this.self;
4830
4831             if (!method) {
4832                 return self;
4833             }
4834
4835             return method.$owner;
4836         },
4837
4838         /**
4839          * Call the original method that was previously overridden with {@link Ext.Base#override}
4840          *
4841          *     Ext.define('My.Cat', {
4842          *         constructor: function() {
4843          *             alert("I'm a cat!");
4844          *   
4845          *             return this;
4846          *         }
4847          *     });
4848          *
4849          *     My.Cat.override({
4850          *         constructor: function() {
4851          *             alert("I'm going to be a cat!");
4852          *   
4853          *             var instance = this.callOverridden();
4854          *   
4855          *             alert("Meeeeoooowwww");
4856          *   
4857          *             return instance;
4858          *         }
4859          *     });
4860          *
4861          *     var kitty = new My.Cat(); // alerts "I'm going to be a cat!"
4862          *                               // alerts "I'm a cat!"
4863          *                               // alerts "Meeeeoooowwww"
4864          *
4865          * @param {Array/Arguments} args The arguments, either an array or the `arguments` object
4866          * @return {Mixed} Returns the result after calling the overridden method
4867          */
4868         callOverridden: function(args) {
4869             var method = this.callOverridden.caller;
4870
4871             if (!method.$owner) {
4872                 Ext.Error.raise({
4873                     sourceClass: Ext.getClassName(this),
4874                     sourceMethod: "callOverridden",
4875                     msg: "Attempting to call a protected method from the public scope, which is not allowed"
4876                 });
4877             }
4878
4879             if (!method.$previous) {
4880                 Ext.Error.raise({
4881                     sourceClass: Ext.getClassName(this),
4882                     sourceMethod: "callOverridden",
4883                     msg: "this.callOverridden was called in '" + method.$name +
4884                          "' but this method has never been overridden"
4885                  });
4886             }
4887
4888             return method.$previous.apply(this, args || []);
4889         },
4890
4891         destroy: function() {}
4892     };
4893
4894     // These static properties will be copied to every newly created class with {@link Ext#define}
4895     Ext.apply(Ext.Base, {
4896         /**
4897          * Create a new instance of this Class.
4898          *
4899          *     Ext.define('My.cool.Class', {
4900          *         ...
4901          *     });
4902          *      
4903          *     My.cool.Class.create({
4904          *         someConfig: true
4905          *     });
4906          *
4907          * All parameters are passed to the constructor of the class.
4908          *
4909          * @return {Object} the created instance.
4910          * @static
4911          */
4912         create: function() {
4913             return Ext.create.apply(Ext, [this].concat(Array.prototype.slice.call(arguments, 0)));
4914         },
4915
4916         /**
4917          * @private
4918          */
4919         own: flexSetter(function(name, value) {
4920             if (typeof value === 'function') {
4921                 this.ownMethod(name, value);
4922             }
4923             else {
4924                 this.prototype[name] = value;
4925             }
4926         }),
4927
4928         /**
4929          * @private
4930          */
4931         ownMethod: function(name, fn) {
4932             var originalFn;
4933
4934             if (fn.$owner !== undefined && fn !== Ext.emptyFn) {
4935                 originalFn = fn;
4936
4937                 fn = function() {
4938                     return originalFn.apply(this, arguments);
4939                 };
4940             }
4941
4942             var className;
4943             className = Ext.getClassName(this);
4944             if (className) {
4945                 fn.displayName = className + '#' + name;
4946             }
4947             fn.$owner = this;
4948             fn.$name = name;
4949
4950             this.prototype[name] = fn;
4951         },
4952
4953         /**
4954          * Add / override static properties of this class.
4955          *
4956          *     Ext.define('My.cool.Class', {
4957          *         ...
4958          *     });
4959          *
4960          *     My.cool.Class.addStatics({
4961          *         someProperty: 'someValue',      // My.cool.Class.someProperty = 'someValue'
4962          *         method1: function() { ... },    // My.cool.Class.method1 = function() { ... };
4963          *         method2: function() { ... }     // My.cool.Class.method2 = function() { ... };
4964          *     });
4965          *
4966          * @param {Object} members
4967          * @return {Ext.Base} this
4968          * @static
4969          */
4970         addStatics: function(members) {
4971             for (var name in members) {
4972                 if (members.hasOwnProperty(name)) {
4973                     this[name] = members[name];
4974                 }
4975             }
4976
4977             return this;
4978         },
4979
4980         /**
4981          * Add methods / properties to the prototype of this class.
4982          *
4983          *     Ext.define('My.awesome.Cat', {
4984          *         constructor: function() {
4985          *             ...
4986          *         }
4987          *     });
4988          *
4989          *      My.awesome.Cat.implement({
4990          *          meow: function() {
4991          *             alert('Meowww...');
4992          *          }
4993          *      });
4994          *
4995          *      var kitty = new My.awesome.Cat;
4996          *      kitty.meow();
4997          *
4998          * @param {Object} members
4999          * @static
5000          */
5001         implement: function(members) {
5002             var prototype = this.prototype,
5003                 name, i, member, previous;
5004             var className = Ext.getClassName(this);
5005             for (name in members) {
5006                 if (members.hasOwnProperty(name)) {
5007                     member = members[name];
5008
5009                     if (typeof member === 'function') {
5010                         member.$owner = this;
5011                         member.$name = name;
5012                         if (className) {
5013                             member.displayName = className + '#' + name;
5014                         }
5015                     }
5016
5017                     prototype[name] = member;
5018                 }
5019             }
5020
5021             if (Ext.enumerables) {
5022                 var enumerables = Ext.enumerables;
5023
5024                 for (i = enumerables.length; i--;) {
5025                     name = enumerables[i];
5026
5027                     if (members.hasOwnProperty(name)) {
5028                         member = members[name];
5029                         member.$owner = this;
5030                         member.$name = name;
5031                         prototype[name] = member;
5032                     }
5033                 }
5034             }
5035         },
5036
5037         /**
5038          * Borrow another class' members to the prototype of this class.
5039          *
5040          *     Ext.define('Bank', {
5041          *         money: '$$$',
5042          *         printMoney: function() {
5043          *             alert('$$$$$$$');
5044          *         }
5045          *     });
5046          *
5047          *     Ext.define('Thief', {
5048          *         ...
5049          *     });
5050          *
5051          *     Thief.borrow(Bank, ['money', 'printMoney']);
5052          *
5053          *     var steve = new Thief();
5054          *
5055          *     alert(steve.money); // alerts '$$$'
5056          *     steve.printMoney(); // alerts '$$$$$$$'
5057          *
5058          * @param {Ext.Base} fromClass The class to borrow members from
5059          * @param {Array/String} members The names of the members to borrow
5060          * @return {Ext.Base} this
5061          * @static
5062          * @private
5063          */
5064         borrow: function(fromClass, members) {
5065             var fromPrototype = fromClass.prototype,
5066                 i, ln, member;
5067
5068             members = Ext.Array.from(members);
5069
5070             for (i = 0, ln = members.length; i < ln; i++) {
5071                 member = members[i];
5072
5073                 this.own(member, fromPrototype[member]);
5074             }
5075
5076             return this;
5077         },
5078
5079         /**
5080          * Override prototype members of this class. Overridden methods can be invoked via
5081          * {@link Ext.Base#callOverridden}
5082          *
5083          *     Ext.define('My.Cat', {
5084          *         constructor: function() {
5085          *             alert("I'm a cat!");
5086          *
5087          *             return this;
5088          *         }
5089          *     });
5090          *
5091          *     My.Cat.override({
5092          *         constructor: function() {
5093          *             alert("I'm going to be a cat!");
5094          *
5095          *             var instance = this.callOverridden();
5096          *
5097          *             alert("Meeeeoooowwww");
5098          *
5099          *             return instance;
5100          *         }
5101          *     });
5102          *
5103          *     var kitty = new My.Cat(); // alerts "I'm going to be a cat!"
5104          *                               // alerts "I'm a cat!"
5105          *                               // alerts "Meeeeoooowwww"
5106          *
5107          * @param {Object} members
5108          * @return {Ext.Base} this
5109          * @static
5110          */
5111         override: function(members) {
5112             var prototype = this.prototype,
5113                 name, i, member, previous;
5114
5115             for (name in members) {
5116                 if (members.hasOwnProperty(name)) {
5117                     member = members[name];
5118
5119                     if (typeof member === 'function') {
5120                         if (typeof prototype[name] === 'function') {
5121                             previous = prototype[name];
5122                             member.$previous = previous;
5123                         }
5124
5125                         this.ownMethod(name, member);
5126                     }
5127                     else {
5128                         prototype[name] = member;
5129                     }
5130                 }
5131             }
5132
5133             if (Ext.enumerables) {
5134                 var enumerables = Ext.enumerables;
5135
5136                 for (i = enumerables.length; i--;) {
5137                     name = enumerables[i];
5138
5139                     if (members.hasOwnProperty(name)) {
5140                         if (prototype[name] !== undefined) {
5141                             previous = prototype[name];
5142                             members[name].$previous = previous;
5143                         }
5144
5145                         this.ownMethod(name, members[name]);
5146                     }
5147                 }
5148             }
5149
5150             return this;
5151         },
5152
5153         /**
5154          * Used internally by the mixins pre-processor
5155          * @private
5156          */
5157         mixin: flexSetter(function(name, cls) {
5158             var mixin = cls.prototype,
5159                 my = this.prototype,
5160                 i, fn;
5161
5162             for (i in mixin) {
5163                 if (mixin.hasOwnProperty(i)) {
5164                     if (my[i] === undefined) {
5165                         if (typeof mixin[i] === 'function') {
5166                             fn = mixin[i];
5167
5168                             if (fn.$owner === undefined) {
5169                                 this.ownMethod(i, fn);
5170                             }
5171                             else {
5172                                 my[i] = fn;
5173                             }
5174                         }
5175                         else {
5176                             my[i] = mixin[i];
5177                         }
5178                     }
5179                     else if (i === 'config' && my.config && mixin.config) {
5180                         Ext.Object.merge(my.config, mixin.config);
5181                     }
5182                 }
5183             }
5184
5185             if (my.mixins === undefined) {
5186                 my.mixins = {};
5187             }
5188
5189             my.mixins[name] = mixin;
5190         }),
5191
5192         /**
5193          * Get the current class' name in string format.
5194          *
5195          *     Ext.define('My.cool.Class', {
5196          *         constructor: function() {
5197          *             alert(this.self.getName()); // alerts 'My.cool.Class'
5198          *         }
5199          *     });
5200          *
5201          *     My.cool.Class.getName(); // 'My.cool.Class'
5202          *
5203          * @return {String} className
5204          */
5205         getName: function() {
5206             return Ext.getClassName(this);
5207         },
5208
5209         /**
5210          * Create aliases for existing prototype methods. Example:
5211          *
5212          *     Ext.define('My.cool.Class', {
5213          *         method1: function() { ... },
5214          *         method2: function() { ... }
5215          *     });
5216          *
5217          *     var test = new My.cool.Class();
5218          *
5219          *     My.cool.Class.createAlias({
5220          *         method3: 'method1',
5221          *         method4: 'method2'
5222          *     });
5223          *
5224          *     test.method3(); // test.method1()
5225          *
5226          *     My.cool.Class.createAlias('method5', 'method3');
5227          *
5228          *     test.method5(); // test.method3() -> test.method1()
5229          *
5230          * @param {String/Object} alias The new method name, or an object to set multiple aliases. See
5231          * {@link Ext.Function#flexSetter flexSetter}
5232          * @param {String/Object} origin The original method name
5233          * @static
5234          * @method
5235          */
5236         createAlias: flexSetter(function(alias, origin) {
5237             this.prototype[alias] = this.prototype[origin];
5238         })
5239     });
5240
5241 })(Ext.Function.flexSetter);
5242
5243 /**
5244  * @author Jacky Nguyen <jacky@sencha.com>
5245  * @docauthor Jacky Nguyen <jacky@sencha.com>
5246  * @class Ext.Class
5247  * 
5248  * Handles class creation throughout the whole framework. Note that most of the time {@link Ext#define Ext.define} should
5249  * be used instead, since it's a higher level wrapper that aliases to {@link Ext.ClassManager#create}
5250  * to enable namespacing and dynamic dependency resolution.
5251  * 
5252  * # Basic syntax: #
5253  * 
5254  *     Ext.define(className, properties);
5255  * 
5256  * in which `properties` is an object represent a collection of properties that apply to the class. See
5257  * {@link Ext.ClassManager#create} for more detailed instructions.
5258  * 
5259  *     Ext.define('Person', {
5260  *          name: 'Unknown',
5261  * 
5262  *          constructor: function(name) {
5263  *              if (name) {
5264  *                  this.name = name;
5265  *              }
5266  * 
5267  *              return this;
5268  *          },
5269  * 
5270  *          eat: function(foodType) {
5271  *              alert("I'm eating: " + foodType);
5272  * 
5273  *              return this;
5274  *          }
5275  *     });
5276  * 
5277  *     var aaron = new Person("Aaron");
5278  *     aaron.eat("Sandwich"); // alert("I'm eating: Sandwich");
5279  * 
5280  * Ext.Class has a powerful set of extensible {@link Ext.Class#registerPreprocessor pre-processors} which takes care of
5281  * everything related to class creation, including but not limited to inheritance, mixins, configuration, statics, etc.
5282  * 
5283  * # Inheritance: #
5284  * 
5285  *     Ext.define('Developer', {
5286  *          extend: 'Person',
5287  * 
5288  *          constructor: function(name, isGeek) {
5289  *              this.isGeek = isGeek;
5290  * 
5291  *              // Apply a method from the parent class' prototype
5292  *              this.callParent([name]);
5293  * 
5294  *              return this;
5295  * 
5296  *          },
5297  * 
5298  *          code: function(language) {
5299  *              alert("I'm coding in: " + language);
5300  * 
5301  *              this.eat("Bugs");
5302  * 
5303  *              return this;
5304  *          }
5305  *     });
5306  * 
5307  *     var jacky = new Developer("Jacky", true);
5308  *     jacky.code("JavaScript"); // alert("I'm coding in: JavaScript");
5309  *                               // alert("I'm eating: Bugs");
5310  * 
5311  * See {@link Ext.Base#callParent} for more details on calling superclass' methods
5312  * 
5313  * # Mixins: #
5314  * 
5315  *     Ext.define('CanPlayGuitar', {
5316  *          playGuitar: function() {
5317  *             alert("F#...G...D...A");
5318  *          }
5319  *     });
5320  * 
5321  *     Ext.define('CanComposeSongs', {
5322  *          composeSongs: function() { ... }
5323  *     });
5324  * 
5325  *     Ext.define('CanSing', {
5326  *          sing: function() {
5327  *              alert("I'm on the highway to hell...")
5328  *          }
5329  *     });
5330  * 
5331  *     Ext.define('Musician', {
5332  *          extend: 'Person',
5333  * 
5334  *          mixins: {
5335  *              canPlayGuitar: 'CanPlayGuitar',
5336  *              canComposeSongs: 'CanComposeSongs',
5337  *              canSing: 'CanSing'
5338  *          }
5339  *     })
5340  * 
5341  *     Ext.define('CoolPerson', {
5342  *          extend: 'Person',
5343  * 
5344  *          mixins: {
5345  *              canPlayGuitar: 'CanPlayGuitar',
5346  *              canSing: 'CanSing'
5347  *          },
5348  * 
5349  *          sing: function() {
5350  *              alert("Ahem....");
5351  * 
5352  *              this.mixins.canSing.sing.call(this);
5353  * 
5354  *              alert("[Playing guitar at the same time...]");
5355  * 
5356  *              this.playGuitar();
5357  *          }
5358  *     });
5359  * 
5360  *     var me = new CoolPerson("Jacky");
5361  * 
5362  *     me.sing(); // alert("Ahem...");
5363  *                // alert("I'm on the highway to hell...");
5364  *                // alert("[Playing guitar at the same time...]");
5365  *                // alert("F#...G...D...A");
5366  * 
5367  * # Config: #
5368  * 
5369  *     Ext.define('SmartPhone', {
5370  *          config: {
5371  *              hasTouchScreen: false,
5372  *              operatingSystem: 'Other',
5373  *              price: 500
5374  *          },
5375  * 
5376  *          isExpensive: false,
5377  * 
5378  *          constructor: function(config) {
5379  *              this.initConfig(config);
5380  * 
5381  *              return this;
5382  *          },
5383  * 
5384  *          applyPrice: function(price) {
5385  *              this.isExpensive = (price > 500);
5386  * 
5387  *              return price;
5388  *          },
5389  * 
5390  *          applyOperatingSystem: function(operatingSystem) {
5391  *              if (!(/^(iOS|Android|BlackBerry)$/i).test(operatingSystem)) {
5392  *                  return 'Other';
5393  *              }
5394  * 
5395  *              return operatingSystem;
5396  *          }
5397  *     });
5398  * 
5399  *     var iPhone = new SmartPhone({
5400  *          hasTouchScreen: true,
5401  *          operatingSystem: 'iOS'
5402  *     });
5403  * 
5404  *     iPhone.getPrice(); // 500;
5405  *     iPhone.getOperatingSystem(); // 'iOS'
5406  *     iPhone.getHasTouchScreen(); // true;
5407  *     iPhone.hasTouchScreen(); // true
5408  * 
5409  *     iPhone.isExpensive; // false;
5410  *     iPhone.setPrice(600);
5411  *     iPhone.getPrice(); // 600
5412  *     iPhone.isExpensive; // true;
5413  * 
5414  *     iPhone.setOperatingSystem('AlienOS');
5415  *     iPhone.getOperatingSystem(); // 'Other'
5416  * 
5417  * # Statics: #
5418  * 
5419  *     Ext.define('Computer', {
5420  *          statics: {
5421  *              factory: function(brand) {
5422  *                 // 'this' in static methods refer to the class itself
5423  *                  return new this(brand);
5424  *              }
5425  *          },
5426  * 
5427  *          constructor: function() { ... }
5428  *     });
5429  * 
5430  *     var dellComputer = Computer.factory('Dell');
5431  * 
5432  * Also see {@link Ext.Base#statics} and {@link Ext.Base#self} for more details on accessing
5433  * static properties within class methods
5434  *
5435  */
5436 (function() {
5437
5438     var Class,
5439         Base = Ext.Base,
5440         baseStaticProperties = [],
5441         baseStaticProperty;
5442
5443     for (baseStaticProperty in Base) {
5444         if (Base.hasOwnProperty(baseStaticProperty)) {
5445             baseStaticProperties.push(baseStaticProperty);
5446         }
5447     }
5448
5449     /**
5450      * @method constructor
5451      * Creates new class.
5452      * @param {Object} classData An object represent the properties of this class
5453      * @param {Function} createdFn Optional, the callback function to be executed when this class is fully created.
5454      * Note that the creation process can be asynchronous depending on the pre-processors used.
5455      * @return {Ext.Base} The newly created class
5456      */
5457     Ext.Class = Class = function(newClass, classData, onClassCreated) {
5458         if (typeof newClass !== 'function') {
5459             onClassCreated = classData;
5460             classData = newClass;
5461             newClass = function() {
5462                 return this.constructor.apply(this, arguments);
5463             };
5464         }
5465
5466         if (!classData) {
5467             classData = {};
5468         }
5469
5470         var preprocessorStack = classData.preprocessors || Class.getDefaultPreprocessors(),
5471             registeredPreprocessors = Class.getPreprocessors(),
5472             index = 0,
5473             preprocessors = [],
5474             preprocessor, preprocessors, staticPropertyName, process, i, j, ln;
5475
5476         for (i = 0, ln = baseStaticProperties.length; i < ln; i++) {
5477             staticPropertyName = baseStaticProperties[i];
5478             newClass[staticPropertyName] = Base[staticPropertyName];
5479         }
5480
5481         delete classData.preprocessors;
5482
5483         for (j = 0, ln = preprocessorStack.length; j < ln; j++) {
5484             preprocessor = preprocessorStack[j];
5485
5486             if (typeof preprocessor === 'string') {
5487                 preprocessor = registeredPreprocessors[preprocessor];
5488
5489                 if (!preprocessor.always) {
5490                     if (classData.hasOwnProperty(preprocessor.name)) {
5491                         preprocessors.push(preprocessor.fn);
5492                     }
5493                 }
5494                 else {
5495                     preprocessors.push(preprocessor.fn);
5496                 }
5497             }
5498             else {
5499                 preprocessors.push(preprocessor);
5500             }
5501         }
5502
5503         classData.onClassCreated = onClassCreated;
5504
5505         classData.onBeforeClassCreated = function(cls, data) {
5506             onClassCreated = data.onClassCreated;
5507
5508             delete data.onBeforeClassCreated;
5509             delete data.onClassCreated;
5510
5511             cls.implement(data);
5512
5513             if (onClassCreated) {
5514                 onClassCreated.call(cls, cls);
5515             }
5516         };
5517
5518         process = function(cls, data) {
5519             preprocessor = preprocessors[index++];
5520
5521             if (!preprocessor) {
5522                 data.onBeforeClassCreated.apply(this, arguments);
5523                 return;
5524             }
5525
5526             if (preprocessor.call(this, cls, data, process) !== false) {
5527                 process.apply(this, arguments);
5528             }
5529         };
5530
5531         process.call(Class, newClass, classData);
5532
5533         return newClass;
5534     };
5535
5536     Ext.apply(Class, {
5537
5538         /** @private */
5539         preprocessors: {},
5540
5541         /**
5542          * Register a new pre-processor to be used during the class creation process
5543          *
5544          * @member Ext.Class registerPreprocessor
5545          * @param {String} name The pre-processor's name
5546          * @param {Function} fn The callback function to be executed. Typical format:
5547
5548     function(cls, data, fn) {
5549         // Your code here
5550
5551         // Execute this when the processing is finished.
5552         // Asynchronous processing is perfectly ok
5553         if (fn) {
5554             fn.call(this, cls, data);
5555         }
5556     });
5557
5558          * Passed arguments for this function are:
5559          *
5560          * - `{Function} cls`: The created class
5561          * - `{Object} data`: The set of properties passed in {@link Ext.Class} constructor
5562          * - `{Function} fn`: The callback function that <b>must</b> to be executed when this pre-processor finishes,
5563          * regardless of whether the processing is synchronous or aynchronous
5564          *
5565          * @return {Ext.Class} this
5566          * @markdown
5567          */
5568         registerPreprocessor: function(name, fn, always) {
5569             this.preprocessors[name] = {
5570                 name: name,
5571                 always: always ||  false,
5572                 fn: fn
5573             };
5574
5575             return this;
5576         },
5577
5578         /**
5579          * Retrieve a pre-processor callback function by its name, which has been registered before
5580          *
5581          * @param {String} name
5582          * @return {Function} preprocessor
5583          */
5584         getPreprocessor: function(name) {
5585             return this.preprocessors[name];
5586         },
5587
5588         getPreprocessors: function() {
5589             return this.preprocessors;
5590         },
5591
5592         /**
5593          * Retrieve the array stack of default pre-processors
5594          *
5595          * @return {Function} defaultPreprocessors
5596          */
5597         getDefaultPreprocessors: function() {
5598             return this.defaultPreprocessors || [];
5599         },
5600
5601         /**
5602          * Set the default array stack of default pre-processors
5603          *
5604          * @param {Array} preprocessors
5605          * @return {Ext.Class} this
5606          */
5607         setDefaultPreprocessors: function(preprocessors) {
5608             this.defaultPreprocessors = Ext.Array.from(preprocessors);
5609
5610             return this;
5611         },
5612
5613         /**
5614          * Insert this pre-processor at a specific position in the stack, optionally relative to
5615          * any existing pre-processor. For example:
5616
5617     Ext.Class.registerPreprocessor('debug', function(cls, data, fn) {
5618         // Your code here
5619
5620         if (fn) {
5621             fn.call(this, cls, data);
5622         }
5623     }).insertDefaultPreprocessor('debug', 'last');
5624
5625          * @param {String} name The pre-processor name. Note that it needs to be registered with
5626          * {@link Ext#registerPreprocessor registerPreprocessor} before this
5627          * @param {String} offset The insertion position. Four possible values are:
5628          * 'first', 'last', or: 'before', 'after' (relative to the name provided in the third argument)
5629          * @param {String} relativeName
5630          * @return {Ext.Class} this
5631          * @markdown
5632          */
5633         setDefaultPreprocessorPosition: function(name, offset, relativeName) {
5634             var defaultPreprocessors = this.defaultPreprocessors,
5635                 index;
5636
5637             if (typeof offset === 'string') {
5638                 if (offset === 'first') {
5639                     defaultPreprocessors.unshift(name);
5640
5641                     return this;
5642                 }
5643                 else if (offset === 'last') {
5644                     defaultPreprocessors.push(name);
5645
5646                     return this;
5647                 }
5648
5649                 offset = (offset === 'after') ? 1 : -1;
5650             }
5651
5652             index = Ext.Array.indexOf(defaultPreprocessors, relativeName);
5653
5654             if (index !== -1) {
5655                 Ext.Array.splice(defaultPreprocessors, Math.max(0, index + offset), 0, name);
5656             }
5657
5658             return this;
5659         }
5660     });
5661
5662     /**
5663      * @cfg {String} extend
5664      * The parent class that this class extends. For example:
5665      *
5666      *     Ext.define('Person', {
5667      *         say: function(text) { alert(text); }
5668      *     });
5669      *
5670      *     Ext.define('Developer', {
5671      *         extend: 'Person',
5672      *         say: function(text) { this.callParent(["print "+text]); }
5673      *     });
5674      */
5675     Class.registerPreprocessor('extend', function(cls, data) {
5676         var extend = data.extend,
5677             base = Ext.Base,
5678             basePrototype = base.prototype,
5679             prototype = function() {},
5680             parent, i, k, ln, staticName, parentStatics,
5681             parentPrototype, clsPrototype;
5682
5683         if (extend && extend !== Object) {
5684             parent = extend;
5685         }
5686         else {
5687             parent = base;
5688         }
5689
5690         parentPrototype = parent.prototype;
5691
5692         prototype.prototype = parentPrototype;
5693         clsPrototype = cls.prototype = new prototype();
5694
5695         if (!('$class' in parent)) {
5696             for (i in basePrototype) {
5697                 if (!parentPrototype[i]) {
5698                     parentPrototype[i] = basePrototype[i];
5699                 }
5700             }
5701         }
5702
5703         clsPrototype.self = cls;
5704
5705         cls.superclass = clsPrototype.superclass = parentPrototype;
5706
5707         delete data.extend;
5708
5709         // Statics inheritance
5710         parentStatics = parentPrototype.$inheritableStatics;
5711
5712         if (parentStatics) {
5713             for (k = 0, ln = parentStatics.length; k < ln; k++) {
5714                 staticName = parentStatics[k];
5715
5716                 if (!cls.hasOwnProperty(staticName)) {
5717                     cls[staticName] = parent[staticName];
5718                 }
5719             }
5720         }
5721
5722         // Merge the parent class' config object without referencing it
5723         if (parentPrototype.config) {
5724             clsPrototype.config = Ext.Object.merge({}, parentPrototype.config);
5725         }
5726         else {
5727             clsPrototype.config = {};
5728         }
5729
5730         if (clsPrototype.$onExtended) {
5731             clsPrototype.$onExtended.call(cls, cls, data);
5732         }
5733
5734         if (data.onClassExtended) {
5735             clsPrototype.$onExtended = data.onClassExtended;
5736             delete data.onClassExtended;
5737         }
5738
5739     }, true);
5740
5741     /**
5742      * @cfg {Object} statics
5743      * List of static methods for this class. For example:
5744      *
5745      *     Ext.define('Computer', {
5746      *          statics: {
5747      *              factory: function(brand) {
5748      *                  // 'this' in static methods refer to the class itself
5749      *                  return new this(brand);
5750      *              }
5751      *          },
5752      *
5753      *          constructor: function() { ... }
5754      *     });
5755      *
5756      *     var dellComputer = Computer.factory('Dell');
5757      */
5758     Class.registerPreprocessor('statics', function(cls, data) {
5759         var statics = data.statics,
5760             name;
5761
5762         for (name in statics) {
5763             if (statics.hasOwnProperty(name)) {
5764                 cls[name] = statics[name];
5765             }
5766         }
5767
5768         delete data.statics;
5769     });
5770
5771     /**
5772      * @cfg {Object} inheritableStatics
5773      * List of inheritable static methods for this class.
5774      * Otherwise just like {@link #statics} but subclasses inherit these methods.
5775      */
5776     Class.registerPreprocessor('inheritableStatics', function(cls, data) {
5777         var statics = data.inheritableStatics,
5778             inheritableStatics,
5779             prototype = cls.prototype,
5780             name;
5781
5782         inheritableStatics = prototype.$inheritableStatics;
5783
5784         if (!inheritableStatics) {
5785             inheritableStatics = prototype.$inheritableStatics = [];
5786         }
5787
5788         for (name in statics) {
5789             if (statics.hasOwnProperty(name)) {
5790                 cls[name] = statics[name];
5791                 inheritableStatics.push(name);
5792             }
5793         }
5794
5795         delete data.inheritableStatics;
5796     });
5797
5798     /**
5799      * @cfg {Object} mixins
5800      * List of classes to mix into this class. For example:
5801      *
5802      *     Ext.define('CanSing', {
5803      *          sing: function() {
5804      *              alert("I'm on the highway to hell...")
5805      *          }
5806      *     });
5807      *
5808      *     Ext.define('Musician', {
5809      *          extend: 'Person',
5810      *
5811      *          mixins: {
5812      *              canSing: 'CanSing'
5813      *          }
5814      *     })
5815      */
5816     Class.registerPreprocessor('mixins', function(cls, data) {
5817         cls.mixin(data.mixins);
5818
5819         delete data.mixins;
5820     });
5821
5822     /**
5823      * @cfg {Object} config
5824      * List of configuration options with their default values, for which automatically
5825      * accessor methods are generated.  For example:
5826      *
5827      *     Ext.define('SmartPhone', {
5828      *          config: {
5829      *              hasTouchScreen: false,
5830      *              operatingSystem: 'Other',
5831      *              price: 500
5832      *          },
5833      *          constructor: function(cfg) {
5834      *              this.initConfig(cfg);
5835      *          }
5836      *     });
5837      *
5838      *     var iPhone = new SmartPhone({
5839      *          hasTouchScreen: true,
5840      *          operatingSystem: 'iOS'
5841      *     });
5842      *
5843      *     iPhone.getPrice(); // 500;
5844      *     iPhone.getOperatingSystem(); // 'iOS'
5845      *     iPhone.getHasTouchScreen(); // true;
5846      *     iPhone.hasTouchScreen(); // true
5847      */
5848     Class.registerPreprocessor('config', function(cls, data) {
5849         var prototype = cls.prototype;
5850
5851         Ext.Object.each(data.config, function(name) {
5852             var cName = name.charAt(0).toUpperCase() + name.substr(1),
5853                 pName = name,
5854                 apply = 'apply' + cName,
5855                 setter = 'set' + cName,
5856                 getter = 'get' + cName;
5857
5858             if (!(apply in prototype) && !data.hasOwnProperty(apply)) {
5859                 data[apply] = function(val) {
5860                     return val;
5861                 };
5862             }
5863
5864             if (!(setter in prototype) && !data.hasOwnProperty(setter)) {
5865                 data[setter] = function(val) {
5866                     var ret = this[apply].call(this, val, this[pName]);
5867
5868                     if (ret !== undefined) {
5869                         this[pName] = ret;
5870                     }
5871
5872                     return this;
5873                 };
5874             }
5875
5876             if (!(getter in prototype) && !data.hasOwnProperty(getter)) {
5877                 data[getter] = function() {
5878                     return this[pName];
5879                 };
5880             }
5881         });
5882
5883         Ext.Object.merge(prototype.config, data.config);
5884         delete data.config;
5885     });
5886
5887     Class.setDefaultPreprocessors(['extend', 'statics', 'inheritableStatics', 'mixins', 'config']);
5888
5889     // Backwards compatible
5890     Ext.extend = function(subclass, superclass, members) {
5891         if (arguments.length === 2 && Ext.isObject(superclass)) {
5892             members = superclass;
5893             superclass = subclass;
5894             subclass = null;
5895         }
5896
5897         var cls;
5898
5899         if (!superclass) {
5900             Ext.Error.raise("Attempting to extend from a class which has not been loaded on the page.");
5901         }
5902
5903         members.extend = superclass;
5904         members.preprocessors = ['extend', 'mixins', 'config', 'statics'];
5905
5906         if (subclass) {
5907             cls = new Class(subclass, members);
5908         }
5909         else {
5910             cls = new Class(members);
5911         }
5912
5913         cls.prototype.override = function(o) {
5914             for (var m in o) {
5915                 if (o.hasOwnProperty(m)) {
5916                     this[m] = o[m];
5917                 }
5918             }
5919         };
5920
5921         return cls;
5922     };
5923
5924 })();
5925
5926 /**
5927  * @author Jacky Nguyen <jacky@sencha.com>
5928  * @docauthor Jacky Nguyen <jacky@sencha.com>
5929  * @class Ext.ClassManager
5930  *
5931  * Ext.ClassManager manages all classes and handles mapping from string class name to
5932  * actual class objects throughout the whole framework. It is not generally accessed directly, rather through
5933  * these convenient shorthands:
5934  *
5935  * - {@link Ext#define Ext.define}
5936  * - {@link Ext#create Ext.create}
5937  * - {@link Ext#widget Ext.widget}
5938  * - {@link Ext#getClass Ext.getClass}
5939  * - {@link Ext#getClassName Ext.getClassName}
5940  *
5941  * @singleton
5942  */
5943 (function(Class, alias) {
5944
5945     var slice = Array.prototype.slice;
5946
5947     var Manager = Ext.ClassManager = {
5948
5949         /**
5950          * @property {Object} classes
5951          * All classes which were defined through the ClassManager. Keys are the
5952          * name of the classes and the values are references to the classes.
5953          * @private
5954          */
5955         classes: {},
5956
5957         /**
5958          * @private
5959          */
5960         existCache: {},
5961
5962         /**
5963          * @private
5964          */
5965         namespaceRewrites: [{
5966             from: 'Ext.',
5967             to: Ext
5968         }],
5969
5970         /**
5971          * @private
5972          */
5973         maps: {
5974             alternateToName: {},
5975             aliasToName: {},
5976             nameToAliases: {}
5977         },
5978
5979         /** @private */
5980         enableNamespaceParseCache: true,
5981
5982         /** @private */
5983         namespaceParseCache: {},
5984
5985         /** @private */
5986         instantiators: [],
5987
5988         /** @private */
5989         instantiationCounts: {},
5990
5991         /**
5992          * Checks if a class has already been created.
5993          *
5994          * @param {String} className
5995          * @return {Boolean} exist
5996          */
5997         isCreated: function(className) {
5998             var i, ln, part, root, parts;
5999
6000             if (typeof className !== 'string' || className.length < 1) {
6001                 Ext.Error.raise({
6002                     sourceClass: "Ext.ClassManager",
6003                     sourceMethod: "exist",
6004                     msg: "Invalid classname, must be a string and must not be empty"
6005                 });
6006             }
6007
6008             if (this.classes.hasOwnProperty(className) || this.existCache.hasOwnProperty(className)) {
6009                 return true;
6010             }
6011
6012             root = Ext.global;
6013             parts = this.parseNamespace(className);
6014
6015             for (i = 0, ln = parts.length; i < ln; i++) {
6016                 part = parts[i];
6017
6018                 if (typeof part !== 'string') {
6019                     root = part;
6020                 } else {
6021                     if (!root || !root[part]) {
6022                         return false;
6023                     }
6024
6025                     root = root[part];
6026                 }
6027             }
6028
6029             Ext.Loader.historyPush(className);
6030
6031             this.existCache[className] = true;
6032
6033             return true;
6034         },
6035
6036         /**
6037          * Supports namespace rewriting
6038          * @private
6039          */
6040         parseNamespace: function(namespace) {
6041             if (typeof namespace !== 'string') {
6042                 Ext.Error.raise({
6043                     sourceClass: "Ext.ClassManager",
6044                     sourceMethod: "parseNamespace",
6045                     msg: "Invalid namespace, must be a string"
6046                 });
6047             }
6048
6049             var cache = this.namespaceParseCache;
6050
6051             if (this.enableNamespaceParseCache) {
6052                 if (cache.hasOwnProperty(namespace)) {
6053                     return cache[namespace];
6054                 }
6055             }
6056
6057             var parts = [],
6058                 rewrites = this.namespaceRewrites,
6059                 rewrite, from, to, i, ln, root = Ext.global;
6060
6061             for (i = 0, ln = rewrites.length; i < ln; i++) {
6062                 rewrite = rewrites[i];
6063                 from = rewrite.from;
6064                 to = rewrite.to;
6065
6066                 if (namespace === from || namespace.substring(0, from.length) === from) {
6067                     namespace = namespace.substring(from.length);
6068
6069                     if (typeof to !== 'string') {
6070                         root = to;
6071                     } else {
6072                         parts = parts.concat(to.split('.'));
6073                     }
6074
6075                     break;
6076                 }
6077             }
6078
6079             parts.push(root);
6080
6081             parts = parts.concat(namespace.split('.'));
6082
6083             if (this.enableNamespaceParseCache) {
6084                 cache[namespace] = parts;
6085             }
6086
6087             return parts;
6088         },
6089
6090         /**
6091          * Creates a namespace and assign the `value` to the created object
6092          *
6093          *     Ext.ClassManager.setNamespace('MyCompany.pkg.Example', someObject);
6094          *
6095          *     alert(MyCompany.pkg.Example === someObject); // alerts true
6096          *
6097          * @param {String} name
6098          * @param {Mixed} value
6099          */
6100         setNamespace: function(name, value) {
6101             var root = Ext.global,
6102                 parts = this.parseNamespace(name),
6103                 leaf = parts.pop(),
6104                 i, ln, part;
6105
6106             for (i = 0, ln = parts.length; i < ln; i++) {
6107                 part = parts[i];
6108
6109                 if (typeof part !== 'string') {
6110                     root = part;
6111                 } else {
6112                     if (!root[part]) {
6113                         root[part] = {};
6114                     }
6115
6116                     root = root[part];
6117                 }
6118             }
6119
6120             root[leaf] = value;
6121
6122             return root[leaf];
6123         },
6124
6125         /**
6126          * The new Ext.ns, supports namespace rewriting
6127          * @private
6128          */
6129         createNamespaces: function() {
6130             var root = Ext.global,
6131                 parts, part, i, j, ln, subLn;
6132
6133             for (i = 0, ln = arguments.length; i < ln; i++) {
6134                 parts = this.parseNamespace(arguments[i]);
6135
6136                 for (j = 0, subLn = parts.length; j < subLn; j++) {
6137                     part = parts[j];
6138
6139                     if (typeof part !== 'string') {
6140                         root = part;
6141                     } else {
6142                         if (!root[part]) {
6143                             root[part] = {};
6144                         }
6145
6146                         root = root[part];
6147                     }
6148                 }
6149             }
6150
6151             return root;
6152         },
6153
6154         /**
6155          * Sets a name reference to a class.
6156          *
6157          * @param {String} name
6158          * @param {Object} value
6159          * @return {Ext.ClassManager} this
6160          */
6161         set: function(name, value) {
6162             var targetName = this.getName(value);
6163
6164             this.classes[name] = this.setNamespace(name, value);
6165
6166             if (targetName && targetName !== name) {
6167                 this.maps.alternateToName[name] = targetName;
6168             }
6169
6170             return this;
6171         },
6172
6173         /**
6174          * Retrieve a class by its name.
6175          *
6176          * @param {String} name
6177          * @return {Class} class
6178          */
6179         get: function(name) {
6180             if (this.classes.hasOwnProperty(name)) {
6181                 return this.classes[name];
6182             }
6183
6184             var root = Ext.global,
6185                 parts = this.parseNamespace(name),
6186                 part, i, ln;
6187
6188             for (i = 0, ln = parts.length; i < ln; i++) {
6189                 part = parts[i];
6190
6191                 if (typeof part !== 'string') {
6192                     root = part;
6193                 } else {
6194                     if (!root || !root[part]) {
6195                         return null;
6196                     }
6197
6198                     root = root[part];
6199                 }
6200             }
6201
6202             return root;
6203         },
6204
6205         /**
6206          * Register the alias for a class.
6207          *
6208          * @param {Class/String} cls a reference to a class or a className
6209          * @param {String} alias Alias to use when referring to this class
6210          */
6211         setAlias: function(cls, alias) {
6212             var aliasToNameMap = this.maps.aliasToName,
6213                 nameToAliasesMap = this.maps.nameToAliases,
6214                 className;
6215
6216             if (typeof cls === 'string') {
6217                 className = cls;
6218             } else {
6219                 className = this.getName(cls);
6220             }
6221
6222             if (alias && aliasToNameMap[alias] !== className) {
6223                 if (aliasToNameMap.hasOwnProperty(alias) && Ext.isDefined(Ext.global.console)) {
6224                     Ext.global.console.log("[Ext.ClassManager] Overriding existing alias: '" + alias + "' " +
6225                         "of: '" + aliasToNameMap[alias] + "' with: '" + className + "'. Be sure it's intentional.");
6226                 }
6227
6228                 aliasToNameMap[alias] = className;
6229             }
6230
6231             if (!nameToAliasesMap[className]) {
6232                 nameToAliasesMap[className] = [];
6233             }
6234
6235             if (alias) {
6236                 Ext.Array.include(nameToAliasesMap[className], alias);
6237             }
6238
6239             return this;
6240         },
6241
6242         /**
6243          * Get a reference to the class by its alias.
6244          *
6245          * @param {String} alias
6246          * @return {Class} class
6247          */
6248         getByAlias: function(alias) {
6249             return this.get(this.getNameByAlias(alias));
6250         },
6251
6252         /**
6253          * Get the name of a class by its alias.
6254          *
6255          * @param {String} alias
6256          * @return {String} className
6257          */
6258         getNameByAlias: function(alias) {
6259             return this.maps.aliasToName[alias] || '';
6260         },
6261
6262         /**
6263          * Get the name of a class by its alternate name.
6264          *
6265          * @param {String} alternate
6266          * @return {String} className
6267          */
6268         getNameByAlternate: function(alternate) {
6269             return this.maps.alternateToName[alternate] || '';
6270         },
6271
6272         /**
6273          * Get the aliases of a class by the class name
6274          *
6275          * @param {String} name
6276          * @return {Array} aliases
6277          */
6278         getAliasesByName: function(name) {
6279             return this.maps.nameToAliases[name] || [];
6280         },
6281
6282         /**
6283          * Get the name of the class by its reference or its instance.
6284          *
6285          *     Ext.ClassManager.getName(Ext.Action); // returns "Ext.Action"
6286          *
6287          * {@link Ext#getClassName Ext.getClassName} is alias for {@link Ext.ClassManager#getName Ext.ClassManager.getName}.
6288          *
6289          * @param {Class/Object} object
6290          * @return {String} className
6291          */
6292         getName: function(object) {
6293             return object && object.$className || '';
6294         },
6295
6296         /**
6297          * Get the class of the provided object; returns null if it's not an instance
6298          * of any class created with Ext.define.
6299          *
6300          *     var component = new Ext.Component();
6301          *
6302          *     Ext.ClassManager.getClass(component); // returns Ext.Component
6303          *
6304          * {@link Ext#getClass Ext.getClass} is alias for {@link Ext.ClassManager#getClass Ext.ClassManager.getClass}.
6305          *
6306          * @param {Object} object
6307          * @return {Class} class
6308          */
6309         getClass: function(object) {
6310             return object && object.self || null;
6311         },
6312
6313         /**
6314          * Defines a class.
6315          *
6316          *     Ext.ClassManager.create('My.awesome.Class', {
6317          *         someProperty: 'something',
6318          *         someMethod: function() { ... }
6319          *         ...
6320          *
6321          *     }, function() {
6322          *         alert('Created!');
6323          *         alert(this === My.awesome.Class); // alerts true
6324          *
6325          *         var myInstance = new this();
6326          *     });
6327          *
6328          * {@link Ext#define Ext.define} is alias for {@link Ext.ClassManager#create Ext.ClassManager.create}.
6329          *
6330          * @param {String} className The class name to create in string dot-namespaced format, for example:
6331          * 'My.very.awesome.Class', 'FeedViewer.plugin.CoolPager'
6332          * It is highly recommended to follow this simple convention:
6333          *
6334          * - The root and the class name are 'CamelCased'
6335          * - Everything else is lower-cased
6336          *
6337          * @param {Object} data The key - value pairs of properties to apply to this class. Property names can be of any valid
6338          * strings, except those in the reserved list below:
6339          *
6340          * - {@link Ext.Base#self self}
6341          * - {@link Ext.Class#alias alias}
6342          * - {@link Ext.Class#alternateClassName alternateClassName}
6343          * - {@link Ext.Class#config config}
6344          * - {@link Ext.Class#extend extend}
6345          * - {@link Ext.Class#inheritableStatics inheritableStatics}
6346          * - {@link Ext.Class#mixins mixins}
6347          * - {@link Ext.Class#requires requires}
6348          * - {@link Ext.Class#singleton singleton}
6349          * - {@link Ext.Class#statics statics}
6350          * - {@link Ext.Class#uses uses}
6351          *
6352          * @param {Function} createdFn Optional callback to execute after the class is created, the execution scope of which
6353          * (`this`) will be the newly created class itself.
6354          * @return {Ext.Base}
6355          */
6356         create: function(className, data, createdFn) {
6357             var manager = this;
6358
6359             if (typeof className !== 'string') {
6360                 Ext.Error.raise({
6361                     sourceClass: "Ext",
6362                     sourceMethod: "define",
6363                     msg: "Invalid class name '" + className + "' specified, must be a non-empty string"
6364                 });
6365             }
6366
6367             data.$className = className;
6368
6369             return new Class(data, function() {
6370                 var postprocessorStack = data.postprocessors || manager.defaultPostprocessors,
6371                     registeredPostprocessors = manager.postprocessors,
6372                     index = 0,
6373                     postprocessors = [],
6374                     postprocessor, postprocessors, process, i, ln;
6375
6376                 delete data.postprocessors;
6377
6378                 for (i = 0, ln = postprocessorStack.length; i < ln; i++) {
6379                     postprocessor = postprocessorStack[i];
6380
6381                     if (typeof postprocessor === 'string') {
6382                         postprocessor = registeredPostprocessors[postprocessor];
6383
6384                         if (!postprocessor.always) {
6385                             if (data[postprocessor.name] !== undefined) {
6386                                 postprocessors.push(postprocessor.fn);
6387                             }
6388                         }
6389                         else {
6390                             postprocessors.push(postprocessor.fn);
6391                         }
6392                     }
6393                     else {
6394                         postprocessors.push(postprocessor);
6395                     }
6396                 }
6397
6398                 process = function(clsName, cls, clsData) {
6399                     postprocessor = postprocessors[index++];
6400
6401                     if (!postprocessor) {
6402                         manager.set(className, cls);
6403
6404                         Ext.Loader.historyPush(className);
6405
6406                         if (createdFn) {
6407                             createdFn.call(cls, cls);
6408                         }
6409
6410                         return;
6411                     }
6412
6413                     if (postprocessor.call(this, clsName, cls, clsData, process) !== false) {
6414                         process.apply(this, arguments);
6415                     }
6416                 };
6417
6418                 process.call(manager, className, this, data);
6419             });
6420         },
6421
6422         /**
6423          * Instantiate a class by its alias.
6424          *
6425          * If {@link Ext.Loader} is {@link Ext.Loader#setConfig enabled} and the class has not been defined yet, it will
6426          * attempt to load the class via synchronous loading.
6427          *
6428          *     var window = Ext.ClassManager.instantiateByAlias('widget.window', { width: 600, height: 800, ... });
6429          *
6430          * {@link Ext#createByAlias Ext.createByAlias} is alias for {@link Ext.ClassManager#instantiateByAlias Ext.ClassManager.instantiateByAlias}.
6431          *
6432          * @param {String} alias
6433          * @param {Mixed} args,... Additional arguments after the alias will be passed to the
6434          * class constructor.
6435          * @return {Object} instance
6436          */
6437         instantiateByAlias: function() {
6438             var alias = arguments[0],
6439                 args = slice.call(arguments),
6440                 className = this.getNameByAlias(alias);
6441
6442             if (!className) {
6443                 className = this.maps.aliasToName[alias];
6444
6445                 if (!className) {
6446                     Ext.Error.raise({
6447                         sourceClass: "Ext",
6448                         sourceMethod: "createByAlias",
6449                         msg: "Cannot create an instance of unrecognized alias: " + alias
6450                     });
6451                 }
6452
6453                 if (Ext.global.console) {
6454                     Ext.global.console.warn("[Ext.Loader] Synchronously loading '" + className + "'; consider adding " +
6455                          "Ext.require('" + alias + "') above Ext.onReady");
6456                 }
6457
6458                 Ext.syncRequire(className);
6459             }
6460
6461             args[0] = className;
6462
6463             return this.instantiate.apply(this, args);
6464         },
6465
6466         /**
6467          * Instantiate a class by either full name, alias or alternate name.
6468          *
6469          * If {@link Ext.Loader} is {@link Ext.Loader#setConfig enabled} and the class has not been defined yet, it will
6470          * attempt to load the class via synchronous loading.
6471          *
6472          * For example, all these three lines return the same result:
6473          *
6474          *     // alias
6475          *     var window = Ext.ClassManager.instantiate('widget.window', { width: 600, height: 800, ... });
6476          *
6477          *     // alternate name
6478          *     var window = Ext.ClassManager.instantiate('Ext.Window', { width: 600, height: 800, ... });
6479          *
6480          *     // full class name
6481          *     var window = Ext.ClassManager.instantiate('Ext.window.Window', { width: 600, height: 800, ... });
6482          *
6483          * {@link Ext#create Ext.create} is alias for {@link Ext.ClassManager#instantiate Ext.ClassManager.instantiate}.
6484          *
6485          * @param {String} name
6486          * @param {Mixed} args,... Additional arguments after the name will be passed to the class' constructor.
6487          * @return {Object} instance
6488          */
6489         instantiate: function() {
6490             var name = arguments[0],
6491                 args = slice.call(arguments, 1),
6492                 alias = name,
6493                 possibleName, cls;
6494
6495             if (typeof name !== 'function') {
6496                 if ((typeof name !== 'string' || name.length < 1)) {
6497                     Ext.Error.raise({
6498                         sourceClass: "Ext",
6499                         sourceMethod: "create",
6500                         msg: "Invalid class name or alias '" + name + "' specified, must be a non-empty string"
6501                     });
6502                 }
6503
6504                 cls = this.get(name);
6505             }
6506             else {
6507                 cls = name;
6508             }
6509
6510             // No record of this class name, it's possibly an alias, so look it up
6511             if (!cls) {
6512                 possibleName = this.getNameByAlias(name);
6513
6514                 if (possibleName) {
6515                     name = possibleName;
6516
6517                     cls = this.get(name);
6518                 }
6519             }
6520
6521             // Still no record of this class name, it's possibly an alternate name, so look it up
6522             if (!cls) {
6523                 possibleName = this.getNameByAlternate(name);
6524
6525                 if (possibleName) {
6526                     name = possibleName;
6527
6528                     cls = this.get(name);
6529                 }
6530             }
6531
6532             // Still not existing at this point, try to load it via synchronous mode as the last resort
6533             if (!cls) {
6534                 if (Ext.global.console) {
6535                     Ext.global.console.warn("[Ext.Loader] Synchronously loading '" + name + "'; consider adding " +
6536                          "Ext.require('" + ((possibleName) ? alias : name) + "') above Ext.onReady");
6537                 }
6538
6539                 Ext.syncRequire(name);
6540
6541                 cls = this.get(name);
6542             }
6543
6544             if (!cls) {
6545                 Ext.Error.raise({
6546                     sourceClass: "Ext",
6547                     sourceMethod: "create",
6548                     msg: "Cannot create an instance of unrecognized class name / alias: " + alias
6549                 });
6550             }
6551
6552             if (typeof cls !== 'function') {
6553                 Ext.Error.raise({
6554                     sourceClass: "Ext",
6555                     sourceMethod: "create",
6556                     msg: "'" + name + "' is a singleton and cannot be instantiated"
6557                 });
6558             }
6559
6560             if (!this.instantiationCounts[name]) {
6561                 this.instantiationCounts[name] = 0;
6562             }
6563
6564             this.instantiationCounts[name]++;
6565
6566             return this.getInstantiator(args.length)(cls, args);
6567         },
6568
6569         /**
6570          * @private
6571          * @param name
6572          * @param args
6573          */
6574         dynInstantiate: function(name, args) {
6575             args = Ext.Array.from(args, true);
6576             args.unshift(name);
6577
6578             return this.instantiate.apply(this, args);
6579         },
6580
6581         /**
6582          * @private
6583          * @param length
6584          */
6585         getInstantiator: function(length) {
6586             if (!this.instantiators[length]) {
6587                 var i = length,
6588                     args = [];
6589
6590                 for (i = 0; i < length; i++) {
6591                     args.push('a['+i+']');
6592                 }
6593
6594                 this.instantiators[length] = new Function('c', 'a', 'return new c('+args.join(',')+')');
6595             }
6596
6597             return this.instantiators[length];
6598         },
6599
6600         /**
6601          * @private
6602          */
6603         postprocessors: {},
6604
6605         /**
6606          * @private
6607          */
6608         defaultPostprocessors: [],
6609
6610         /**
6611          * Register a post-processor function.
6612          *
6613          * @param {String} name
6614          * @param {Function} postprocessor
6615          */
6616         registerPostprocessor: function(name, fn, always) {
6617             this.postprocessors[name] = {
6618                 name: name,
6619                 always: always ||  false,
6620                 fn: fn
6621             };
6622
6623             return this;
6624         },
6625
6626         /**
6627          * Set the default post processors array stack which are applied to every class.
6628          *
6629          * @param {String/Array} The name of a registered post processor or an array of registered names.
6630          * @return {Ext.ClassManager} this
6631          */
6632         setDefaultPostprocessors: function(postprocessors) {
6633             this.defaultPostprocessors = Ext.Array.from(postprocessors);
6634
6635             return this;
6636         },
6637
6638         /**
6639          * Insert this post-processor at a specific position in the stack, optionally relative to
6640          * any existing post-processor
6641          *
6642          * @param {String} name The post-processor name. Note that it needs to be registered with
6643          * {@link Ext.ClassManager#registerPostprocessor} before this
6644          * @param {String} offset The insertion position. Four possible values are:
6645          * 'first', 'last', or: 'before', 'after' (relative to the name provided in the third argument)
6646          * @param {String} relativeName
6647          * @return {Ext.ClassManager} this
6648          */
6649         setDefaultPostprocessorPosition: function(name, offset, relativeName) {
6650             var defaultPostprocessors = this.defaultPostprocessors,
6651                 index;
6652
6653             if (typeof offset === 'string') {
6654                 if (offset === 'first') {
6655                     defaultPostprocessors.unshift(name);
6656
6657                     return this;
6658                 }
6659                 else if (offset === 'last') {
6660                     defaultPostprocessors.push(name);
6661
6662                     return this;
6663                 }
6664
6665                 offset = (offset === 'after') ? 1 : -1;
6666             }
6667
6668             index = Ext.Array.indexOf(defaultPostprocessors, relativeName);
6669
6670             if (index !== -1) {
6671                 Ext.Array.splice(defaultPostprocessors, Math.max(0, index + offset), 0, name);
6672             }
6673
6674             return this;
6675         },
6676
6677         /**
6678          * Converts a string expression to an array of matching class names. An expression can either refers to class aliases
6679          * or class names. Expressions support wildcards:
6680          *
6681          *     // returns ['Ext.window.Window']
6682          *     var window = Ext.ClassManager.getNamesByExpression('widget.window');
6683          *
6684          *     // returns ['widget.panel', 'widget.window', ...]
6685          *     var allWidgets = Ext.ClassManager.getNamesByExpression('widget.*');
6686          *
6687          *     // returns ['Ext.data.Store', 'Ext.data.ArrayProxy', ...]
6688          *     var allData = Ext.ClassManager.getNamesByExpression('Ext.data.*');
6689          *
6690          * @param {String} expression
6691          * @return {Array} classNames
6692          * @markdown
6693          */
6694         getNamesByExpression: function(expression) {
6695             var nameToAliasesMap = this.maps.nameToAliases,
6696                 names = [],
6697                 name, alias, aliases, possibleName, regex, i, ln;
6698
6699             if (typeof expression !== 'string' || expression.length < 1) {
6700                 Ext.Error.raise({
6701                     sourceClass: "Ext.ClassManager",
6702                     sourceMethod: "getNamesByExpression",
6703                     msg: "Expression " + expression + " is invalid, must be a non-empty string"
6704                 });
6705             }
6706
6707             if (expression.indexOf('*') !== -1) {
6708                 expression = expression.replace(/\*/g, '(.*?)');
6709                 regex = new RegExp('^' + expression + '$');
6710
6711                 for (name in nameToAliasesMap) {
6712                     if (nameToAliasesMap.hasOwnProperty(name)) {
6713                         aliases = nameToAliasesMap[name];
6714
6715                         if (name.search(regex) !== -1) {
6716                             names.push(name);
6717                         }
6718                         else {
6719                             for (i = 0, ln = aliases.length; i < ln; i++) {
6720                                 alias = aliases[i];
6721
6722                                 if (alias.search(regex) !== -1) {
6723                                     names.push(name);
6724                                     break;
6725                                 }
6726                             }
6727                         }
6728                     }
6729                 }
6730
6731             } else {
6732                 possibleName = this.getNameByAlias(expression);
6733
6734                 if (possibleName) {
6735                     names.push(possibleName);
6736                 } else {
6737                     possibleName = this.getNameByAlternate(expression);
6738
6739                     if (possibleName) {
6740                         names.push(possibleName);
6741                     } else {
6742                         names.push(expression);
6743                     }
6744                 }
6745             }
6746
6747             return names;
6748         }
6749     };
6750
6751     /**
6752      * @cfg {[String]} alias
6753      * @member Ext.Class
6754      * List of short aliases for class names.  Most useful for defining xtypes for widgets:
6755      *
6756      *     Ext.define('MyApp.CoolPanel', {
6757      *         extend: 'Ext.panel.Panel',
6758      *         alias: ['widget.coolpanel'],
6759      *         title: 'Yeah!'
6760      *     });
6761      *
6762      *     // Using Ext.create
6763      *     Ext.widget('widget.coolpanel');
6764      *     // Using the shorthand for widgets and in xtypes
6765      *     Ext.widget('panel', {
6766      *         items: [
6767      *             {xtype: 'coolpanel', html: 'Foo'},
6768      *             {xtype: 'coolpanel', html: 'Bar'}
6769      *         ]
6770      *     });
6771      */
6772     Manager.registerPostprocessor('alias', function(name, cls, data) {
6773         var aliases = data.alias,
6774             widgetPrefix = 'widget.',
6775             i, ln, alias;
6776
6777         if (!(aliases instanceof Array)) {
6778             aliases = [aliases];
6779         }
6780
6781         for (i = 0, ln = aliases.length; i < ln; i++) {
6782             alias = aliases[i];
6783
6784             if (typeof alias !== 'string') {
6785                 Ext.Error.raise({
6786                     sourceClass: "Ext",
6787                     sourceMethod: "define",
6788                     msg: "Invalid alias of: '" + alias + "' for class: '" + name + "'; must be a valid string"
6789                 });
6790             }
6791
6792             this.setAlias(cls, alias);
6793         }
6794
6795         // This is ugly, will change to make use of parseNamespace for alias later on
6796         for (i = 0, ln = aliases.length; i < ln; i++) {
6797             alias = aliases[i];
6798
6799             if (alias.substring(0, widgetPrefix.length) === widgetPrefix) {
6800                 // Only the first alias with 'widget.' prefix will be used for xtype
6801                 cls.xtype = cls.$xtype = alias.substring(widgetPrefix.length);
6802                 break;
6803             }
6804         }
6805     });
6806
6807     /**
6808      * @cfg {Boolean} singleton
6809      * @member Ext.Class
6810      * When set to true, the class will be instanciated as singleton.  For example:
6811      *
6812      *     Ext.define('Logger', {
6813      *         singleton: true,
6814      *         log: function(msg) {
6815      *             console.log(msg);
6816      *         }
6817      *     });
6818      *
6819      *     Logger.log('Hello');
6820      */
6821     Manager.registerPostprocessor('singleton', function(name, cls, data, fn) {
6822         fn.call(this, name, new cls(), data);
6823         return false;
6824     });
6825
6826     /**
6827      * @cfg {String/[String]} alternateClassName
6828      * @member Ext.Class
6829      * Defines alternate names for this class.  For example:
6830      *
6831      *     Ext.define('Developer', {
6832      *         alternateClassName: ['Coder', 'Hacker'],
6833      *         code: function(msg) {
6834      *             alert('Typing... ' + msg);
6835      *         }
6836      *     });
6837      *
6838      *     var joe = Ext.create('Developer');
6839      *     joe.code('stackoverflow');
6840      *
6841      *     var rms = Ext.create('Hacker');
6842      *     rms.code('hack hack');
6843      */
6844     Manager.registerPostprocessor('alternateClassName', function(name, cls, data) {
6845         var alternates = data.alternateClassName,
6846             i, ln, alternate;
6847
6848         if (!(alternates instanceof Array)) {
6849             alternates = [alternates];
6850         }
6851
6852         for (i = 0, ln = alternates.length; i < ln; i++) {
6853             alternate = alternates[i];
6854
6855             if (typeof alternate !== 'string') {
6856                 Ext.Error.raise({
6857                     sourceClass: "Ext",
6858                     sourceMethod: "define",
6859                     msg: "Invalid alternate of: '" + alternate + "' for class: '" + name + "'; must be a valid string"
6860                 });
6861             }
6862
6863             this.set(alternate, cls);
6864         }
6865     });
6866
6867     Manager.setDefaultPostprocessors(['alias', 'singleton', 'alternateClassName']);
6868
6869     Ext.apply(Ext, {
6870         /**
6871          * @method
6872          * @member Ext
6873          * @alias Ext.ClassManager#instantiate
6874          */
6875         create: alias(Manager, 'instantiate'),
6876
6877         /**
6878          * @private
6879          * API to be stablized
6880          *
6881          * @param {Mixed} item
6882          * @param {String} namespace
6883          */
6884         factory: function(item, namespace) {
6885             if (item instanceof Array) {
6886                 var i, ln;
6887
6888                 for (i = 0, ln = item.length; i < ln; i++) {
6889                     item[i] = Ext.factory(item[i], namespace);
6890                 }
6891
6892                 return item;
6893             }
6894
6895             var isString = (typeof item === 'string');
6896
6897             if (isString || (item instanceof Object && item.constructor === Object)) {
6898                 var name, config = {};
6899
6900                 if (isString) {
6901                     name = item;
6902                 }
6903                 else {
6904                     name = item.className;
6905                     config = item;
6906                     delete config.className;
6907                 }
6908
6909                 if (namespace !== undefined && name.indexOf(namespace) === -1) {
6910                     name = namespace + '.' + Ext.String.capitalize(name);
6911                 }
6912
6913                 return Ext.create(name, config);
6914             }
6915
6916             if (typeof item === 'function') {
6917                 return Ext.create(item);
6918             }
6919
6920             return item;
6921         },
6922
6923         /**
6924          * Convenient shorthand to create a widget by its xtype, also see {@link Ext.ClassManager#instantiateByAlias}
6925          *
6926          *     var button = Ext.widget('button'); // Equivalent to Ext.create('widget.button')
6927          *     var panel = Ext.widget('panel'); // Equivalent to Ext.create('widget.panel')
6928          *
6929          * @method
6930          * @member Ext
6931          * @param {String} name  xtype of the widget to create.
6932          * @return {Object} widget instance
6933          */
6934         widget: function(name) {
6935             var args = slice.call(arguments);
6936             args[0] = 'widget.' + name;
6937
6938             return Manager.instantiateByAlias.apply(Manager, args);
6939         },
6940
6941         /**
6942          * @method
6943          * @member Ext
6944          * @alias Ext.ClassManager#instantiateByAlias
6945          */
6946         createByAlias: alias(Manager, 'instantiateByAlias'),
6947
6948         /**
6949          * @method
6950          * @member Ext
6951          * @alias Ext.ClassManager#create
6952          */
6953         define: alias(Manager, 'create'),
6954
6955         /**
6956          * @method
6957          * @member Ext
6958          * @alias Ext.ClassManager#getName
6959          */
6960         getClassName: alias(Manager, 'getName'),
6961
6962         /**
6963          *
6964          * @param {Mixed} object
6965          */
6966         getDisplayName: function(object) {
6967             if (object.displayName) {
6968                 return object.displayName;
6969             }
6970
6971             if (object.$name && object.$class) {
6972                 return Ext.getClassName(object.$class) + '#' + object.$name;
6973             }
6974
6975             if (object.$className) {
6976                 return object.$className;
6977             }
6978
6979             return 'Anonymous';
6980         },
6981
6982         /**
6983          * @method
6984          * @member Ext
6985          * @alias Ext.ClassManager#getClass
6986          */
6987         getClass: alias(Manager, 'getClass'),
6988
6989         /**
6990          * Creates namespaces to be used for scoping variables and classes so that they are not global.
6991          * Specifying the last node of a namespace implicitly creates all other nodes. Usage:
6992          *
6993          *     Ext.namespace('Company', 'Company.data');
6994          *
6995          *     // equivalent and preferable to the above syntax
6996          *     Ext.namespace('Company.data');
6997          *
6998          *     Company.Widget = function() { ... };
6999          *
7000          *     Company.data.CustomStore = function(config) { ... };
7001          *
7002          * @method
7003          * @member Ext
7004          * @param {String} namespace1
7005          * @param {String} namespace2
7006          * @param {String} etc
7007          * @return {Object} The namespace object. (If multiple arguments are passed, this will be the last namespace created)
7008          */
7009         namespace: alias(Manager, 'createNamespaces')
7010     });
7011
7012     /**
7013      * Old name for {@link Ext#widget}.
7014      * @deprecated 4.0.0 Use {@link Ext#widget} instead.
7015      * @method
7016      * @member Ext
7017      * @alias Ext#widget
7018      */
7019     Ext.createWidget = Ext.widget;
7020
7021     /**
7022      * Convenient alias for {@link Ext#namespace Ext.namespace}
7023      * @method
7024      * @member Ext
7025      * @alias Ext#namespace
7026      */
7027     Ext.ns = Ext.namespace;
7028
7029     Class.registerPreprocessor('className', function(cls, data) {
7030         if (data.$className) {
7031             cls.$className = data.$className;
7032             cls.displayName = cls.$className;
7033         }
7034     }, true);
7035
7036     Class.setDefaultPreprocessorPosition('className', 'first');
7037
7038 })(Ext.Class, Ext.Function.alias);
7039
7040 /**
7041  * @class Ext.Loader
7042  * @singleton
7043  * @author Jacky Nguyen <jacky@sencha.com>
7044  * @docauthor Jacky Nguyen <jacky@sencha.com>
7045  *
7046  * Ext.Loader is the heart of the new dynamic dependency loading capability in Ext JS 4+. It is most commonly used
7047  * via the {@link Ext#require} shorthand. Ext.Loader supports both asynchronous and synchronous loading
7048  * approaches, and leverage their advantages for the best development flow. We'll discuss about the pros and cons
7049  * of each approach:
7050  *
7051  * # Asynchronous Loading
7052  *
7053  * - *Advantages:*
7054  *       + Cross-domain
7055  *       + No web server needed: you can run the application via the file system protocol
7056  *     (i.e: `file://path/to/your/index.html`)
7057  *       + Best possible debugging experience: error messages come with the exact file name and line number
7058  *
7059  * - *Disadvantages:*
7060  *       + Dependencies need to be specified before-hand
7061  *
7062  * ### Method 1: Explicitly include what you need:
7063  *
7064  *     // Syntax
7065  *     Ext.require({String/Array} expressions);
7066  *
7067  *     // Example: Single alias
7068  *     Ext.require('widget.window');
7069  *
7070  *     // Example: Single class name
7071  *     Ext.require('Ext.window.Window');
7072  *
7073  *     // Example: Multiple aliases / class names mix
7074  *     Ext.require(['widget.window', 'layout.border', 'Ext.data.Connection']);
7075  *
7076  *     // Wildcards
7077  *     Ext.require(['widget.*', 'layout.*', 'Ext.data.*']);
7078  *
7079  * ### Method 2: Explicitly exclude what you don't need:
7080  *
7081  *     // Syntax: Note that it must be in this chaining format.
7082  *     Ext.exclude({String/Array} expressions)
7083  *        .require({String/Array} expressions);
7084  *
7085  *     // Include everything except Ext.data.*
7086  *     Ext.exclude('Ext.data.*').require('*'); 
7087  *
7088  *     // Include all widgets except widget.checkbox*,
7089  *     // which will match widget.checkbox, widget.checkboxfield, widget.checkboxgroup, etc.
7090  *     Ext.exclude('widget.checkbox*').require('widget.*');
7091  *
7092  * # Synchronous Loading on Demand
7093  *
7094  * - *Advantages:*
7095  *       + There's no need to specify dependencies before-hand, which is always the convenience of including
7096  *     ext-all.js before
7097  *
7098  * - *Disadvantages:*
7099  *       + Not as good debugging experience since file name won't be shown (except in Firebug at the moment)
7100  *       + Must be from the same domain due to XHR restriction
7101  *       + Need a web server, same reason as above
7102  *
7103  * There's one simple rule to follow: Instantiate everything with Ext.create instead of the `new` keyword
7104  *
7105  *     Ext.create('widget.window', { ... }); // Instead of new Ext.window.Window({...});
7106  *
7107  *     Ext.create('Ext.window.Window', {}); // Same as above, using full class name instead of alias
7108  *
7109  *     Ext.widget('window', {}); // Same as above, all you need is the traditional `xtype`
7110  *
7111  * Behind the scene, {@link Ext.ClassManager} will automatically check whether the given class name / alias has already
7112  * existed on the page. If it's not, Ext.Loader will immediately switch itself to synchronous mode and automatic load
7113  * the given class and all its dependencies.
7114  *
7115  * # Hybrid Loading - The Best of Both Worlds
7116  *
7117  * It has all the advantages combined from asynchronous and synchronous loading. The development flow is simple:
7118  *
7119  * ### Step 1: Start writing your application using synchronous approach.
7120  * 
7121  * Ext.Loader will automatically fetch all dependencies on demand as they're needed during run-time. For example:
7122  *
7123  *     Ext.onReady(function(){
7124  *         var window = Ext.createWidget('window', {
7125  *             width: 500,
7126  *             height: 300,
7127  *             layout: {
7128  *                 type: 'border',
7129  *                 padding: 5
7130  *             },
7131  *             title: 'Hello Dialog',
7132  *             items: [{
7133  *                 title: 'Navigation',
7134  *                 collapsible: true,
7135  *                 region: 'west',
7136  *                 width: 200,
7137  *                 html: 'Hello',
7138  *                 split: true
7139  *             }, {
7140  *                 title: 'TabPanel',
7141  *                 region: 'center'
7142  *             }]
7143  *         });
7144  *
7145  *         window.show();
7146  *     })
7147  *
7148  * ### Step 2: Along the way, when you need better debugging ability, watch the console for warnings like these:
7149  *
7150  *     [Ext.Loader] Synchronously loading 'Ext.window.Window'; consider adding Ext.require('Ext.window.Window') before your application's code ClassManager.js:432
7151  *     [Ext.Loader] Synchronously loading 'Ext.layout.container.Border'; consider adding Ext.require('Ext.layout.container.Border') before your application's code
7152  *
7153  * Simply copy and paste the suggested code above `Ext.onReady`, e.g.:
7154  *
7155  *     Ext.require('Ext.window.Window');
7156  *     Ext.require('Ext.layout.container.Border');
7157  *
7158  *     Ext.onReady(...);
7159  *
7160  * Everything should now load via asynchronous mode.
7161  *
7162  * # Deployment
7163  *
7164  * It's important to note that dynamic loading should only be used during development on your local machines.
7165  * During production, all dependencies should be combined into one single JavaScript file. Ext.Loader makes
7166  * the whole process of transitioning from / to between development / maintenance and production as easy as
7167  * possible. Internally {@link Ext.Loader#history Ext.Loader.history} maintains the list of all dependencies
7168  * your application needs in the exact loading sequence. It's as simple as concatenating all files in this
7169  * array into one, then include it on top of your application.
7170  *
7171  * This process will be automated with Sencha Command, to be released and documented towards Ext JS 4 Final.
7172  */
7173 (function(Manager, Class, flexSetter, alias) {
7174
7175     var
7176         dependencyProperties = ['extend', 'mixins', 'requires'],
7177         Loader;
7178
7179     Loader = Ext.Loader = {
7180         /**
7181          * @private
7182          */
7183         documentHead: typeof document !== 'undefined' && (document.head || document.getElementsByTagName('head')[0]),
7184
7185         /**
7186          * Flag indicating whether there are still files being loaded
7187          * @private
7188          */
7189         isLoading: false,
7190
7191         /**
7192          * Maintain the queue for all dependencies. Each item in the array is an object of the format:
7193          * {
7194          *      requires: [...], // The required classes for this queue item
7195          *      callback: function() { ... } // The function to execute when all classes specified in requires exist
7196          * }
7197          * @private
7198          */
7199         queue: [],
7200
7201         /**
7202          * Maintain the list of files that have already been handled so that they never get double-loaded
7203          * @private
7204          */
7205         isFileLoaded: {},
7206
7207         /**
7208          * Maintain the list of listeners to execute when all required scripts are fully loaded
7209          * @private
7210          */
7211         readyListeners: [],
7212
7213         /**
7214          * Contains optional dependencies to be loaded last
7215          * @private
7216          */
7217         optionalRequires: [],
7218
7219         /**
7220          * Map of fully qualified class names to an array of dependent classes.
7221          * @private
7222          */
7223         requiresMap: {},
7224
7225         /**
7226          * @private
7227          */
7228         numPendingFiles: 0,
7229
7230         /**
7231          * @private
7232          */
7233         numLoadedFiles: 0,
7234
7235         /** @private */
7236         hasFileLoadError: false,
7237
7238         /**
7239          * @private
7240          */
7241         classNameToFilePathMap: {},
7242
7243         /**
7244          * @property {[String]} history
7245          * An array of class names to keep track of the dependency loading order.
7246          * This is not guaranteed to be the same everytime due to the asynchronous nature of the Loader.
7247          */
7248         history: [],
7249
7250         /**
7251          * Configuration
7252          * @private
7253          */
7254         config: {
7255             /**
7256              * @cfg {Boolean} enabled
7257              * Whether or not to enable the dynamic dependency loading feature Defaults to false
7258              */
7259             enabled: false,
7260
7261             /**
7262              * @cfg {Boolean} disableCaching
7263              * Appends current timestamp to script files to prevent caching Defaults to true
7264              */
7265             disableCaching: true,
7266
7267             /**
7268              * @cfg {String} disableCachingParam
7269              * The get parameter name for the cache buster's timestamp. Defaults to '_dc'
7270              */
7271             disableCachingParam: '_dc',
7272
7273             /**
7274              * @cfg {Object} paths
7275              * The mapping from namespaces to file paths
7276              *
7277              *     {
7278              *         'Ext': '.', // This is set by default, Ext.layout.container.Container will be
7279              *                     // loaded from ./layout/Container.js
7280              *
7281              *         'My': './src/my_own_folder' // My.layout.Container will be loaded from
7282              *                                     // ./src/my_own_folder/layout/Container.js
7283              *     }
7284              *
7285              * Note that all relative paths are relative to the current HTML document.
7286              * If not being specified, for example, `Other.awesome.Class`
7287              * will simply be loaded from `./Other/awesome/Class.js`
7288              */
7289             paths: {
7290                 'Ext': '.'
7291             }
7292         },
7293
7294         /**
7295          * Set the configuration for the loader. This should be called right after ext-core.js
7296          * (or ext-core-debug.js) is included in the page, e.g.:
7297          *
7298          *     <script type="text/javascript" src="ext-core-debug.js"></script>
7299          *     <script type="text/javascript">
7300          *       Ext.Loader.setConfig({
7301          *           enabled: true,
7302          *           paths: {
7303          *               'My': 'my_own_path'
7304          *           }
7305          *       });
7306          *     <script>
7307          *     <script type="text/javascript">
7308          *       Ext.require(...);
7309          *
7310          *       Ext.onReady(function() {
7311          *           // application code here
7312          *       });
7313          *     </script>
7314          *
7315          * Refer to config options of {@link Ext.Loader} for the list of possible properties.
7316          *
7317          * @param {String/Object} name  Name of the value to override, or a config object to override multiple values.
7318          * @param {Object} value  (optional) The new value to set, needed if first parameter is String.
7319          * @return {Ext.Loader} this
7320          */
7321         setConfig: function(name, value) {
7322             if (Ext.isObject(name) && arguments.length === 1) {
7323                 Ext.Object.merge(this.config, name);
7324             }
7325             else {
7326                 this.config[name] = (Ext.isObject(value)) ? Ext.Object.merge(this.config[name], value) : value;
7327             }
7328
7329             return this;
7330         },
7331
7332         /**
7333          * Get the config value corresponding to the specified name.
7334          * If no name is given, will return the config object.
7335          * @param {String} name The config property name
7336          * @return {Object/Mixed}
7337          */
7338         getConfig: function(name) {
7339             if (name) {
7340                 return this.config[name];
7341             }
7342
7343             return this.config;
7344         },
7345
7346         /**
7347          * Sets the path of a namespace. For Example:
7348          *
7349          *     Ext.Loader.setPath('Ext', '.');
7350          *
7351          * @param {String/Object} name See {@link Ext.Function#flexSetter flexSetter}
7352          * @param {String} path See {@link Ext.Function#flexSetter flexSetter}
7353          * @return {Ext.Loader} this
7354          * @method
7355          */
7356         setPath: flexSetter(function(name, path) {
7357             this.config.paths[name] = path;
7358
7359             return this;
7360         }),
7361
7362         /**
7363          * Translates a className to a file path by adding the the proper prefix and converting the .'s to /'s.
7364          * For example:
7365          *
7366          *     Ext.Loader.setPath('My', '/path/to/My');
7367          *
7368          *     alert(Ext.Loader.getPath('My.awesome.Class')); // alerts '/path/to/My/awesome/Class.js'
7369          *
7370          * Note that the deeper namespace levels, if explicitly set, are always resolved first. For example:
7371          *
7372          *     Ext.Loader.setPath({
7373          *         'My': '/path/to/lib',
7374          *         'My.awesome': '/other/path/for/awesome/stuff',
7375          *         'My.awesome.more': '/more/awesome/path'
7376          *     });
7377          *
7378          *     alert(Ext.Loader.getPath('My.awesome.Class')); // alerts '/other/path/for/awesome/stuff/Class.js'
7379          *
7380          *     alert(Ext.Loader.getPath('My.awesome.more.Class')); // alerts '/more/awesome/path/Class.js'
7381          *
7382          *     alert(Ext.Loader.getPath('My.cool.Class')); // alerts '/path/to/lib/cool/Class.js'
7383          *
7384          *     alert(Ext.Loader.getPath('Unknown.strange.Stuff')); // alerts 'Unknown/strange/Stuff.js'
7385          *
7386          * @param {String} className
7387          * @return {String} path
7388          */
7389         getPath: function(className) {
7390             var path = '',
7391                 paths = this.config.paths,
7392                 prefix = this.getPrefix(className);
7393
7394             if (prefix.length > 0) {
7395                 if (prefix === className) {
7396                     return paths[prefix];
7397                 }
7398
7399                 path = paths[prefix];
7400                 className = className.substring(prefix.length + 1);
7401             }
7402
7403             if (path.length > 0) {
7404                 path += '/';
7405             }
7406
7407             return path.replace(/\/\.\//g, '/') + className.replace(/\./g, "/") + '.js';
7408         },
7409
7410         /**
7411          * @private
7412          * @param {String} className
7413          */
7414         getPrefix: function(className) {
7415             var paths = this.config.paths,
7416                 prefix, deepestPrefix = '';
7417
7418             if (paths.hasOwnProperty(className)) {
7419                 return className;
7420             }
7421
7422             for (prefix in paths) {
7423                 if (paths.hasOwnProperty(prefix) && prefix + '.' === className.substring(0, prefix.length + 1)) {
7424                     if (prefix.length > deepestPrefix.length) {
7425                         deepestPrefix = prefix;
7426                     }
7427                 }
7428             }
7429
7430             return deepestPrefix;
7431         },
7432
7433         /**
7434          * Refresh all items in the queue. If all dependencies for an item exist during looping,
7435          * it will execute the callback and call refreshQueue again. Triggers onReady when the queue is
7436          * empty
7437          * @private
7438          */
7439         refreshQueue: function() {
7440             var ln = this.queue.length,
7441                 i, item, j, requires;
7442
7443             if (ln === 0) {
7444                 this.triggerReady();
7445                 return;
7446             }
7447
7448             for (i = 0; i < ln; i++) {
7449                 item = this.queue[i];
7450
7451                 if (item) {
7452                     requires = item.requires;
7453
7454                     // Don't bother checking when the number of files loaded
7455                     // is still less than the array length
7456                     if (requires.length > this.numLoadedFiles) {
7457                         continue;
7458                     }
7459
7460                     j = 0;
7461
7462                     do {
7463                         if (Manager.isCreated(requires[j])) {
7464                             // Take out from the queue
7465                             Ext.Array.erase(requires, j, 1);
7466                         }
7467                         else {
7468                             j++;
7469                         }
7470                     } while (j < requires.length);
7471
7472                     if (item.requires.length === 0) {
7473                         Ext.Array.erase(this.queue, i, 1);
7474                         item.callback.call(item.scope);
7475                         this.refreshQueue();
7476                         break;
7477                     }
7478                 }
7479             }
7480
7481             return this;
7482         },
7483
7484         /**
7485          * Inject a script element to document's head, call onLoad and onError accordingly
7486          * @private
7487          */
7488         injectScriptElement: function(url, onLoad, onError, scope) {
7489             var script = document.createElement('script'),
7490                 me = this,
7491                 onLoadFn = function() {
7492                     me.cleanupScriptElement(script);
7493                     onLoad.call(scope);
7494                 },
7495                 onErrorFn = function() {
7496                     me.cleanupScriptElement(script);
7497                     onError.call(scope);
7498                 };
7499
7500             script.type = 'text/javascript';
7501             script.src = url;
7502             script.onload = onLoadFn;
7503             script.onerror = onErrorFn;
7504             script.onreadystatechange = function() {
7505                 if (this.readyState === 'loaded' || this.readyState === 'complete') {
7506                     onLoadFn();
7507                 }
7508             };
7509
7510             this.documentHead.appendChild(script);
7511
7512             return script;
7513         },
7514
7515         /**
7516          * @private
7517          */
7518         cleanupScriptElement: function(script) {
7519             script.onload = null;
7520             script.onreadystatechange = null;
7521             script.onerror = null;
7522
7523             return this;
7524         },
7525
7526         /**
7527          * Load a script file, supports both asynchronous and synchronous approaches
7528          *
7529          * @param {String} url
7530          * @param {Function} onLoad
7531          * @param {Scope} scope
7532          * @param {Boolean} synchronous
7533          * @private
7534          */
7535         loadScriptFile: function(url, onLoad, onError, scope, synchronous) {
7536             var me = this,
7537                 noCacheUrl = url + (this.getConfig('disableCaching') ? ('?' + this.getConfig('disableCachingParam') + '=' + Ext.Date.now()) : ''),
7538                 fileName = url.split('/').pop(),
7539                 isCrossOriginRestricted = false,
7540                 xhr, status, onScriptError;
7541
7542             scope = scope || this;
7543
7544             this.isLoading = true;
7545
7546             if (!synchronous) {
7547                 onScriptError = function() {
7548                     onError.call(scope, "Failed loading '" + url + "', please verify that the file exists", synchronous);
7549                 };
7550
7551                 if (!Ext.isReady && Ext.onDocumentReady) {
7552                     Ext.onDocumentReady(function() {
7553                         me.injectScriptElement(noCacheUrl, onLoad, onScriptError, scope);
7554                     });
7555                 }
7556                 else {
7557                     this.injectScriptElement(noCacheUrl, onLoad, onScriptError, scope);
7558                 }
7559             }
7560             else {
7561                 if (typeof XMLHttpRequest !== 'undefined') {
7562                     xhr = new XMLHttpRequest();
7563                 } else {
7564                     xhr = new ActiveXObject('Microsoft.XMLHTTP');
7565                 }
7566
7567                 try {
7568                     xhr.open('GET', noCacheUrl, false);
7569                     xhr.send(null);
7570                 } catch (e) {
7571                     isCrossOriginRestricted = true;
7572                 }
7573
7574                 status = (xhr.status === 1223) ? 204 : xhr.status;
7575
7576                 if (!isCrossOriginRestricted) {
7577                     isCrossOriginRestricted = (status === 0);
7578                 }
7579
7580                 if (isCrossOriginRestricted
7581                 ) {
7582                     onError.call(this, "Failed loading synchronously via XHR: '" + url + "'; It's likely that the file is either " +
7583                                        "being loaded from a different domain or from the local file system whereby cross origin " +
7584                                        "requests are not allowed due to security reasons. Use asynchronous loading with " +
7585                                        "Ext.require instead.", synchronous);
7586                 }
7587                 else if (status >= 200 && status < 300
7588                 ) {
7589                     // Firebug friendly, file names are still shown even though they're eval'ed code
7590                     new Function(xhr.responseText + "\n//@ sourceURL=" + fileName)();
7591
7592                     onLoad.call(scope);
7593                 }
7594                 else {
7595                     onError.call(this, "Failed loading synchronously via XHR: '" + url + "'; please " +
7596                                        "verify that the file exists. " +
7597                                        "XHR status code: " + status, synchronous);
7598                 }
7599
7600                 // Prevent potential IE memory leak
7601                 xhr = null;
7602             }
7603         },
7604
7605         /**
7606          * Explicitly exclude files from being loaded. Useful when used in conjunction with a broad include expression.
7607          * Can be chained with more `require` and `exclude` methods, e.g.:
7608          *
7609          *     Ext.exclude('Ext.data.*').require('*');
7610          *
7611          *     Ext.exclude('widget.button*').require('widget.*');
7612          *
7613          * {@link Ext#exclude Ext.exclude} is alias for {@link Ext.Loader#exclude Ext.Loader.exclude} for convenience.
7614          *
7615          * @param {String/[String]} excludes
7616          * @return {Object} object contains `require` method for chaining
7617          */
7618         exclude: function(excludes) {
7619             var me = this;
7620
7621             return {
7622                 require: function(expressions, fn, scope) {
7623                     return me.require(expressions, fn, scope, excludes);
7624                 },
7625
7626                 syncRequire: function(expressions, fn, scope) {
7627                     return me.syncRequire(expressions, fn, scope, excludes);
7628                 }
7629             };
7630         },
7631
7632         /**
7633          * Synchronously loads all classes by the given names and all their direct dependencies;
7634          * optionally executes the given callback function when finishes, within the optional scope.
7635          *
7636          * {@link Ext#syncRequire Ext.syncRequire} is alias for {@link Ext.Loader#syncRequire Ext.Loader.syncRequire} for convenience.
7637          *
7638          * @param {String/[String]} expressions Can either be a string or an array of string
7639          * @param {Function} fn (Optional) The callback function
7640          * @param {Object} scope (Optional) The execution scope (`this`) of the callback function
7641          * @param {String/[String]} excludes (Optional) Classes to be excluded, useful when being used with expressions
7642          */
7643         syncRequire: function() {
7644             this.syncModeEnabled = true;
7645             this.require.apply(this, arguments);
7646             this.refreshQueue();
7647             this.syncModeEnabled = false;
7648         },
7649
7650         /**
7651          * Loads all classes by the given names and all their direct dependencies;
7652          * optionally executes the given callback function when finishes, within the optional scope.
7653          *
7654          * {@link Ext#require Ext.require} is alias for {@link Ext.Loader#require Ext.Loader.require} for convenience.
7655          *
7656          * @param {String/[String]} expressions Can either be a string or an array of string
7657          * @param {Function} fn (Optional) The callback function
7658          * @param {Object} scope (Optional) The execution scope (`this`) of the callback function
7659          * @param {String/[String]} excludes (Optional) Classes to be excluded, useful when being used with expressions
7660          */
7661         require: function(expressions, fn, scope, excludes) {
7662             var filePath, expression, exclude, className, excluded = {},
7663                 excludedClassNames = [],
7664                 possibleClassNames = [],
7665                 possibleClassName, classNames = [],
7666                 i, j, ln, subLn;
7667
7668             expressions = Ext.Array.from(expressions);
7669             excludes = Ext.Array.from(excludes);
7670
7671             fn = fn || Ext.emptyFn;
7672
7673             scope = scope || Ext.global;
7674
7675             for (i = 0, ln = excludes.length; i < ln; i++) {
7676                 exclude = excludes[i];
7677
7678                 if (typeof exclude === 'string' && exclude.length > 0) {
7679                     excludedClassNames = Manager.getNamesByExpression(exclude);
7680
7681                     for (j = 0, subLn = excludedClassNames.length; j < subLn; j++) {
7682                         excluded[excludedClassNames[j]] = true;
7683                     }
7684                 }
7685             }
7686
7687             for (i = 0, ln = expressions.length; i < ln; i++) {
7688                 expression = expressions[i];
7689
7690                 if (typeof expression === 'string' && expression.length > 0) {
7691                     possibleClassNames = Manager.getNamesByExpression(expression);
7692
7693                     for (j = 0, subLn = possibleClassNames.length; j < subLn; j++) {
7694                         possibleClassName = possibleClassNames[j];
7695
7696                         if (!excluded.hasOwnProperty(possibleClassName) && !Manager.isCreated(possibleClassName)) {
7697                             Ext.Array.include(classNames, possibleClassName);
7698                         }
7699                     }
7700                 }
7701             }
7702
7703             // If the dynamic dependency feature is not being used, throw an error
7704             // if the dependencies are not defined
7705             if (!this.config.enabled) {
7706                 if (classNames.length > 0) {
7707                     Ext.Error.raise({
7708                         sourceClass: "Ext.Loader",
7709                         sourceMethod: "require",
7710                         msg: "Ext.Loader is not enabled, so dependencies cannot be resolved dynamically. " +
7711                              "Missing required class" + ((classNames.length > 1) ? "es" : "") + ": " + classNames.join(', ')
7712                     });
7713                 }
7714             }
7715
7716             if (classNames.length === 0) {
7717                 fn.call(scope);
7718                 return this;
7719             }
7720
7721             this.queue.push({
7722                 requires: classNames,
7723                 callback: fn,
7724                 scope: scope
7725             });
7726
7727             classNames = classNames.slice();
7728
7729             for (i = 0, ln = classNames.length; i < ln; i++) {
7730                 className = classNames[i];
7731
7732                 if (!this.isFileLoaded.hasOwnProperty(className)) {
7733                     this.isFileLoaded[className] = false;
7734
7735                     filePath = this.getPath(className);
7736
7737                     this.classNameToFilePathMap[className] = filePath;
7738
7739                     this.numPendingFiles++;
7740
7741                     this.loadScriptFile(
7742                         filePath,
7743                         Ext.Function.pass(this.onFileLoaded, [className, filePath], this),
7744                         Ext.Function.pass(this.onFileLoadError, [className, filePath]),
7745                         this,
7746                         this.syncModeEnabled
7747                     );
7748                 }
7749             }
7750
7751             return this;
7752         },
7753
7754         /**
7755          * @private
7756          * @param {String} className
7757          * @param {String} filePath
7758          */
7759         onFileLoaded: function(className, filePath) {
7760             this.numLoadedFiles++;
7761
7762             this.isFileLoaded[className] = true;
7763
7764             this.numPendingFiles--;
7765
7766             if (this.numPendingFiles === 0) {
7767                 this.refreshQueue();
7768             }
7769
7770             if (this.numPendingFiles <= 1) {
7771                 window.status = "Finished loading all dependencies, onReady fired!";
7772             }
7773             else {
7774                 window.status = "Loading dependencies, " + this.numPendingFiles + " files left...";
7775             }
7776
7777             if (!this.syncModeEnabled && this.numPendingFiles === 0 && this.isLoading && !this.hasFileLoadError) {
7778                 var queue = this.queue,
7779                     requires,
7780                     i, ln, j, subLn, missingClasses = [], missingPaths = [];
7781
7782                 for (i = 0, ln = queue.length; i < ln; i++) {
7783                     requires = queue[i].requires;
7784
7785                     for (j = 0, subLn = requires.length; j < ln; j++) {
7786                         if (this.isFileLoaded[requires[j]]) {
7787                             missingClasses.push(requires[j]);
7788                         }
7789                     }
7790                 }
7791
7792                 if (missingClasses.length < 1) {
7793                     return;
7794                 }
7795
7796                 missingClasses = Ext.Array.filter(missingClasses, function(item) {
7797                     return !this.requiresMap.hasOwnProperty(item);
7798                 }, this);
7799
7800                 for (i = 0,ln = missingClasses.length; i < ln; i++) {
7801                     missingPaths.push(this.classNameToFilePathMap[missingClasses[i]]);
7802                 }
7803
7804                 Ext.Error.raise({
7805                     sourceClass: "Ext.Loader",
7806                     sourceMethod: "onFileLoaded",
7807                     msg: "The following classes are not declared even if their files have been " +
7808                             "loaded: '" + missingClasses.join("', '") + "'. Please check the source code of their " +
7809                             "corresponding files for possible typos: '" + missingPaths.join("', '") + "'"
7810                 });
7811             }
7812         },
7813
7814         /**
7815          * @private
7816          */
7817         onFileLoadError: function(className, filePath, errorMessage, isSynchronous) {
7818             this.numPendingFiles--;
7819             this.hasFileLoadError = true;
7820
7821             Ext.Error.raise({
7822                 sourceClass: "Ext.Loader",
7823                 classToLoad: className,
7824                 loadPath: filePath,
7825                 loadingType: isSynchronous ? 'synchronous' : 'async',
7826                 msg: errorMessage
7827             });
7828         },
7829
7830         /**
7831          * @private
7832          */
7833         addOptionalRequires: function(requires) {
7834             var optionalRequires = this.optionalRequires,
7835                 i, ln, require;
7836
7837             requires = Ext.Array.from(requires);
7838
7839             for (i = 0, ln = requires.length; i < ln; i++) {
7840                 require = requires[i];
7841
7842                 Ext.Array.include(optionalRequires, require);
7843             }
7844
7845             return this;
7846         },
7847
7848         /**
7849          * @private
7850          */
7851         triggerReady: function(force) {
7852             var readyListeners = this.readyListeners,
7853                 optionalRequires, listener;
7854
7855             if (this.isLoading || force) {
7856                 this.isLoading = false;
7857
7858                 if (this.optionalRequires.length) {
7859                     // Clone then empty the array to eliminate potential recursive loop issue
7860                     optionalRequires = Ext.Array.clone(this.optionalRequires);
7861
7862                     // Empty the original array
7863                     this.optionalRequires.length = 0;
7864
7865                     this.require(optionalRequires, Ext.Function.pass(this.triggerReady, [true], this), this);
7866                     return this;
7867                 }
7868
7869                 while (readyListeners.length) {
7870                     listener = readyListeners.shift();
7871                     listener.fn.call(listener.scope);
7872
7873                     if (this.isLoading) {
7874                         return this;
7875                     }
7876                 }
7877             }
7878
7879             return this;
7880         },
7881
7882         /**
7883          * Adds new listener to be executed when all required scripts are fully loaded.
7884          *
7885          * @param {Function} fn The function callback to be executed
7886          * @param {Object} scope The execution scope (`this`) of the callback function
7887          * @param {Boolean} withDomReady Whether or not to wait for document dom ready as well
7888          */
7889         onReady: function(fn, scope, withDomReady, options) {
7890             var oldFn;
7891
7892             if (withDomReady !== false && Ext.onDocumentReady) {
7893                 oldFn = fn;
7894
7895                 fn = function() {
7896                     Ext.onDocumentReady(oldFn, scope, options);
7897                 };
7898             }
7899
7900             if (!this.isLoading) {
7901                 fn.call(scope);
7902             }
7903             else {
7904                 this.readyListeners.push({
7905                     fn: fn,
7906                     scope: scope
7907                 });
7908             }
7909         },
7910
7911         /**
7912          * @private
7913          * @param {String} className
7914          */
7915         historyPush: function(className) {
7916             if (className && this.isFileLoaded.hasOwnProperty(className)) {
7917                 Ext.Array.include(this.history, className);
7918             }
7919
7920             return this;
7921         }
7922     };
7923
7924     /**
7925      * @member Ext
7926      * @method require
7927      * @alias Ext.Loader#require
7928      */
7929     Ext.require = alias(Loader, 'require');
7930
7931     /**
7932      * @member Ext
7933      * @method syncRequire
7934      * @alias Ext.Loader#syncRequire
7935      */
7936     Ext.syncRequire = alias(Loader, 'syncRequire');
7937
7938     /**
7939      * @member Ext
7940      * @method exclude
7941      * @alias Ext.Loader#exclude
7942      */
7943     Ext.exclude = alias(Loader, 'exclude');
7944
7945     /**
7946      * @member Ext
7947      * @method onReady
7948      * @alias Ext.Loader#onReady
7949      */
7950     Ext.onReady = function(fn, scope, options) {
7951         Loader.onReady(fn, scope, true, options);
7952     };
7953
7954     /**
7955      * @cfg {[String]} requires
7956      * @member Ext.Class
7957      * List of classes that have to be loaded before instanciating this class.
7958      * For example:
7959      *
7960      *     Ext.define('Mother', {
7961      *         requires: ['Child'],
7962      *         giveBirth: function() {
7963      *             // we can be sure that child class is available.
7964      *             return new Child();
7965      *         }
7966      *     });
7967      */
7968     Class.registerPreprocessor('loader', function(cls, data, continueFn) {
7969         var me = this,
7970             dependencies = [],
7971             className = Manager.getName(cls),
7972             i, j, ln, subLn, value, propertyName, propertyValue;
7973
7974         /*
7975         Basically loop through the dependencyProperties, look for string class names and push
7976         them into a stack, regardless of whether the property's value is a string, array or object. For example:
7977         {
7978               extend: 'Ext.MyClass',
7979               requires: ['Ext.some.OtherClass'],
7980               mixins: {
7981                   observable: 'Ext.util.Observable';
7982               }
7983         }
7984         which will later be transformed into:
7985         {
7986               extend: Ext.MyClass,
7987               requires: [Ext.some.OtherClass],
7988               mixins: {
7989                   observable: Ext.util.Observable;
7990               }
7991         }
7992         */
7993
7994         for (i = 0, ln = dependencyProperties.length; i < ln; i++) {
7995             propertyName = dependencyProperties[i];
7996
7997             if (data.hasOwnProperty(propertyName)) {
7998                 propertyValue = data[propertyName];
7999
8000                 if (typeof propertyValue === 'string') {
8001                     dependencies.push(propertyValue);
8002                 }
8003                 else if (propertyValue instanceof Array) {
8004                     for (j = 0, subLn = propertyValue.length; j < subLn; j++) {
8005                         value = propertyValue[j];
8006
8007                         if (typeof value === 'string') {
8008                             dependencies.push(value);
8009                         }
8010                     }
8011                 }
8012                 else {
8013                     for (j in propertyValue) {
8014                         if (propertyValue.hasOwnProperty(j)) {
8015                             value = propertyValue[j];
8016
8017                             if (typeof value === 'string') {
8018                                 dependencies.push(value);
8019                             }
8020                         }
8021                     }
8022                 }
8023             }
8024         }
8025
8026         if (dependencies.length === 0) {
8027 //            Loader.historyPush(className);
8028             return;
8029         }
8030
8031         var deadlockPath = [],
8032             requiresMap = Loader.requiresMap,
8033             detectDeadlock;
8034
8035         /*
8036         Automatically detect deadlocks before-hand,
8037         will throw an error with detailed path for ease of debugging. Examples of deadlock cases:
8038
8039         - A extends B, then B extends A
8040         - A requires B, B requires C, then C requires A
8041
8042         The detectDeadlock function will recursively transverse till the leaf, hence it can detect deadlocks
8043         no matter how deep the path is.
8044         */
8045
8046         if (className) {
8047             requiresMap[className] = dependencies;
8048
8049             detectDeadlock = function(cls) {
8050                 deadlockPath.push(cls);
8051
8052                 if (requiresMap[cls]) {
8053                     if (Ext.Array.contains(requiresMap[cls], className)) {
8054                         Ext.Error.raise({
8055                             sourceClass: "Ext.Loader",
8056                             msg: "Deadlock detected while loading dependencies! '" + className + "' and '" +
8057                                 deadlockPath[1] + "' " + "mutually require each other. Path: " +
8058                                 deadlockPath.join(' -> ') + " -> " + deadlockPath[0]
8059                         });
8060                     }
8061
8062                     for (i = 0, ln = requiresMap[cls].length; i < ln; i++) {
8063                         detectDeadlock(requiresMap[cls][i]);
8064                     }
8065                 }
8066             };
8067
8068             detectDeadlock(className);
8069         }
8070
8071
8072         Loader.require(dependencies, function() {
8073             for (i = 0, ln = dependencyProperties.length; i < ln; i++) {
8074                 propertyName = dependencyProperties[i];
8075
8076                 if (data.hasOwnProperty(propertyName)) {
8077                     propertyValue = data[propertyName];
8078
8079                     if (typeof propertyValue === 'string') {
8080                         data[propertyName] = Manager.get(propertyValue);
8081                     }
8082                     else if (propertyValue instanceof Array) {
8083                         for (j = 0, subLn = propertyValue.length; j < subLn; j++) {
8084                             value = propertyValue[j];
8085
8086                             if (typeof value === 'string') {
8087                                 data[propertyName][j] = Manager.get(value);
8088                             }
8089                         }
8090                     }
8091                     else {
8092                         for (var k in propertyValue) {
8093                             if (propertyValue.hasOwnProperty(k)) {
8094                                 value = propertyValue[k];
8095
8096                                 if (typeof value === 'string') {
8097                                     data[propertyName][k] = Manager.get(value);
8098                                 }
8099                             }
8100                         }
8101                     }
8102                 }
8103             }
8104
8105             continueFn.call(me, cls, data);
8106         });
8107
8108         return false;
8109     }, true);
8110
8111     Class.setDefaultPreprocessorPosition('loader', 'after', 'className');
8112
8113     /**
8114      * @cfg {[String]} uses
8115      * @member Ext.Class
8116      * List of classes to load together with this class.  These aren't neccessarily loaded before
8117      * this class is instanciated. For example:
8118      *
8119      *     Ext.define('Mother', {
8120      *         uses: ['Child'],
8121      *         giveBirth: function() {
8122      *             // This code might, or might not work:
8123      *             // return new Child();
8124      *
8125      *             // Instead use Ext.create() to load the class at the spot if not loaded already:
8126      *             return Ext.create('Child');
8127      *         }
8128      *     });
8129      */
8130     Manager.registerPostprocessor('uses', function(name, cls, data) {
8131         var uses = Ext.Array.from(data.uses),
8132             items = [],
8133             i, ln, item;
8134
8135         for (i = 0, ln = uses.length; i < ln; i++) {
8136             item = uses[i];
8137
8138             if (typeof item === 'string') {
8139                 items.push(item);
8140             }
8141         }
8142
8143         Loader.addOptionalRequires(items);
8144     });
8145
8146     Manager.setDefaultPostprocessorPosition('uses', 'last');
8147
8148 })(Ext.ClassManager, Ext.Class, Ext.Function.flexSetter, Ext.Function.alias);
8149
8150 /**
8151  * @class Ext.Error
8152  * @private
8153  * @extends Error
8154
8155 A wrapper class for the native JavaScript Error object that adds a few useful capabilities for handling
8156 errors in an Ext application. When you use Ext.Error to {@link #raise} an error from within any class that
8157 uses the Ext 4 class system, the Error class can automatically add the source class and method from which
8158 the error was raised. It also includes logic to automatically log the eroor to the console, if available,
8159 with additional metadata about the error. In all cases, the error will always be thrown at the end so that
8160 execution will halt.
8161
8162 Ext.Error also offers a global error {@link #handle handling} method that can be overridden in order to
8163 handle application-wide errors in a single spot. You can optionally {@link #ignore} errors altogether,
8164 although in a real application it's usually a better idea to override the handling function and perform
8165 logging or some other method of reporting the errors in a way that is meaningful to the application.
8166
8167 At its simplest you can simply raise an error as a simple string from within any code:
8168
8169 #Example usage:#
8170
8171     Ext.Error.raise('Something bad happened!');
8172
8173 If raised from plain JavaScript code, the error will be logged to the console (if available) and the message
8174 displayed. In most cases however you'll be raising errors from within a class, and it may often be useful to add
8175 additional metadata about the error being raised.  The {@link #raise} method can also take a config object.
8176 In this form the `msg` attribute becomes the error description, and any other data added to the config gets
8177 added to the error object and, if the console is available, logged to the console for inspection.
8178
8179 #Example usage:#
8180
8181     Ext.define('Ext.Foo', {
8182         doSomething: function(option){
8183             if (someCondition === false) {
8184                 Ext.Error.raise({
8185                     msg: 'You cannot do that!',
8186                     option: option,   // whatever was passed into the method
8187                     'error code': 100 // other arbitrary info
8188                 });
8189             }
8190         }
8191     });
8192
8193 If a console is available (that supports the `console.dir` function) you'll see console output like:
8194
8195     An error was raised with the following data:
8196     option:         Object { foo: "bar"}
8197         foo:        "bar"
8198     error code:     100
8199     msg:            "You cannot do that!"
8200     sourceClass:   "Ext.Foo"
8201     sourceMethod:  "doSomething"
8202
8203     uncaught exception: You cannot do that!
8204
8205 As you can see, the error will report exactly where it was raised and will include as much information as the
8206 raising code can usefully provide.
8207
8208 If you want to handle all application errors globally you can simply override the static {@link #handle} method
8209 and provide whatever handling logic you need. If the method returns true then the error is considered handled
8210 and will not be thrown to the browser. If anything but true is returned then the error will be thrown normally.
8211
8212 #Example usage:#
8213
8214     Ext.Error.handle = function(err) {
8215         if (err.someProperty == 'NotReallyAnError') {
8216             // maybe log something to the application here if applicable
8217             return true;
8218         }
8219         // any non-true return value (including none) will cause the error to be thrown
8220     }
8221
8222  * Create a new Error object
8223  * @param {Object} config The config object
8224  * @markdown
8225  * @author Brian Moeskau <brian@sencha.com>
8226  * @docauthor Brian Moeskau <brian@sencha.com>
8227  */
8228 Ext.Error = Ext.extend(Error, {
8229     statics: {
8230         /**
8231          * @property ignore
8232 Static flag that can be used to globally disable error reporting to the browser if set to true
8233 (defaults to false). Note that if you ignore Ext errors it's likely that some other code may fail
8234 and throw a native JavaScript error thereafter, so use with caution. In most cases it will probably
8235 be preferable to supply a custom error {@link #handle handling} function instead.
8236
8237 #Example usage:#
8238
8239     Ext.Error.ignore = true;
8240
8241          * @markdown
8242          * @static
8243          */
8244         ignore: false,
8245
8246         /**
8247          * @property notify
8248 Static flag that can be used to globally control error notification to the user. Unlike
8249 Ex.Error.ignore, this does not effect exceptions. They are still thrown. This value can be
8250 set to false to disable the alert notification (default is true for IE6 and IE7).
8251
8252 Only the first error will generate an alert. Internally this flag is set to false when the
8253 first error occurs prior to displaying the alert.
8254
8255 This flag is not used in a release build.
8256
8257 #Example usage:#
8258
8259     Ext.Error.notify = false;
8260
8261          * @markdown
8262          * @static
8263          */
8264         //notify: Ext.isIE6 || Ext.isIE7,
8265
8266         /**
8267 Raise an error that can include additional data and supports automatic console logging if available.
8268 You can pass a string error message or an object with the `msg` attribute which will be used as the
8269 error message. The object can contain any other name-value attributes (or objects) to be logged
8270 along with the error.
8271
8272 Note that after displaying the error message a JavaScript error will ultimately be thrown so that
8273 execution will halt.
8274
8275 #Example usage:#
8276
8277     Ext.Error.raise('A simple string error message');
8278
8279     // or...
8280
8281     Ext.define('Ext.Foo', {
8282         doSomething: function(option){
8283             if (someCondition === false) {
8284                 Ext.Error.raise({
8285                     msg: 'You cannot do that!',
8286                     option: option,   // whatever was passed into the method
8287                     'error code': 100 // other arbitrary info
8288                 });
8289             }
8290         }
8291     });
8292          * @param {String/Object} err The error message string, or an object containing the
8293          * attribute "msg" that will be used as the error message. Any other data included in
8294          * the object will also be logged to the browser console, if available.
8295          * @static
8296          * @markdown
8297          */
8298         raise: function(err){
8299             err = err || {};
8300             if (Ext.isString(err)) {
8301                 err = { msg: err };
8302             }
8303
8304             var method = this.raise.caller;
8305
8306             if (method) {
8307                 if (method.$name) {
8308                     err.sourceMethod = method.$name;
8309                 }
8310                 if (method.$owner) {
8311                     err.sourceClass = method.$owner.$className;
8312                 }
8313             }
8314
8315             if (Ext.Error.handle(err) !== true) {
8316                 var msg = Ext.Error.prototype.toString.call(err);
8317
8318                 Ext.log({
8319                     msg: msg,
8320                     level: 'error',
8321                     dump: err,
8322                     stack: true
8323                 });
8324
8325                 throw new Ext.Error(err);
8326             }
8327         },
8328
8329         /**
8330 Globally handle any Ext errors that may be raised, optionally providing custom logic to
8331 handle different errors individually. Return true from the function to bypass throwing the
8332 error to the browser, otherwise the error will be thrown and execution will halt.
8333
8334 #Example usage:#
8335
8336     Ext.Error.handle = function(err) {
8337         if (err.someProperty == 'NotReallyAnError') {
8338             // maybe log something to the application here if applicable
8339             return true;
8340         }
8341         // any non-true return value (including none) will cause the error to be thrown
8342     }
8343
8344          * @param {Ext.Error} err The Ext.Error object being raised. It will contain any attributes
8345          * that were originally raised with it, plus properties about the method and class from which
8346          * the error originated (if raised from a class that uses the Ext 4 class system).
8347          * @static
8348          * @markdown
8349          */
8350         handle: function(){
8351             return Ext.Error.ignore;
8352         }
8353     },
8354
8355     // This is the standard property that is the name of the constructor.
8356     name: 'Ext.Error',
8357
8358     /**
8359      * @param {String/Object} config The error message string, or an object containing the
8360      * attribute "msg" that will be used as the error message. Any other data included in
8361      * the object will be applied to the error instance and logged to the browser console, if available.
8362      */
8363     constructor: function(config){
8364         if (Ext.isString(config)) {
8365             config = { msg: config };
8366         }
8367
8368         var me = this;
8369
8370         Ext.apply(me, config);
8371
8372         me.message = me.message || me.msg; // 'message' is standard ('msg' is non-standard)
8373         // note: the above does not work in old WebKit (me.message is readonly) (Safari 4)
8374     },
8375
8376     /**
8377 Provides a custom string representation of the error object. This is an override of the base JavaScript
8378 `Object.toString` method, which is useful so that when logged to the browser console, an error object will
8379 be displayed with a useful message instead of `[object Object]`, the default `toString` result.
8380
8381 The default implementation will include the error message along with the raising class and method, if available,
8382 but this can be overridden with a custom implementation either at the prototype level (for all errors) or on
8383 a particular error instance, if you want to provide a custom description that will show up in the console.
8384      * @markdown
8385      * @return {String} The error message. If raised from within the Ext 4 class system, the error message
8386      * will also include the raising class and method names, if available.
8387      */
8388     toString: function(){
8389         var me = this,
8390             className = me.className ? me.className  : '',
8391             methodName = me.methodName ? '.' + me.methodName + '(): ' : '',
8392             msg = me.msg || '(No description provided)';
8393
8394         return className + methodName + msg;
8395     }
8396 });
8397
8398 /*
8399  * This mechanism is used to notify the user of the first error encountered on the page. This
8400  * was previously internal to Ext.Error.raise and is a desirable feature since errors often
8401  * slip silently under the radar. It cannot live in Ext.Error.raise since there are times
8402  * where exceptions are handled in a try/catch.
8403  */
8404 (function () {
8405     var prevOnError, timer, errors = 0,
8406         extraordinarilyBad = /(out of stack)|(too much recursion)|(stack overflow)|(out of memory)/i,
8407         win = Ext.global;
8408
8409     if (typeof window === 'undefined') {
8410         return; // build system or some such environment...
8411     }
8412
8413     // This method is called to notify the user of the current error status.
8414     function notify () {
8415         var counters = Ext.log.counters,
8416             supports = Ext.supports,
8417             hasOnError = supports && supports.WindowOnError; // TODO - timing
8418
8419         // Put log counters to the status bar (for most browsers):
8420         if (counters && (counters.error + counters.warn + counters.info + counters.log)) {
8421             var msg = [ 'Logged Errors:',counters.error, 'Warnings:',counters.warn,
8422                         'Info:',counters.info, 'Log:',counters.log].join(' ');
8423             if (errors) {
8424                 msg = '*** Errors: ' + errors + ' - ' + msg;
8425             } else if (counters.error) {
8426                 msg = '*** ' + msg;
8427             }
8428             win.status = msg;
8429         }
8430
8431         // Display an alert on the first error:
8432         if (!Ext.isDefined(Ext.Error.notify)) {
8433             Ext.Error.notify = Ext.isIE6 || Ext.isIE7; // TODO - timing
8434         }
8435         if (Ext.Error.notify && (hasOnError ? errors : (counters && counters.error))) {
8436             Ext.Error.notify = false;
8437
8438             if (timer) {
8439                 win.clearInterval(timer); // ticks can queue up so stop...
8440                 timer = null;
8441             }
8442
8443             alert('Unhandled error on page: See console or log');
8444             poll();
8445         }
8446     }
8447
8448     // Sets up polling loop. This is the only way to know about errors in some browsers
8449     // (Opera/Safari) and is the only way to update the status bar for warnings and other
8450     // non-errors.
8451     function poll () {
8452         timer = win.setInterval(notify, 1000);
8453     }
8454
8455     // window.onerror is ideal (esp in IE) because you get full context. This is harmless
8456     // otherwise (never called) which is good because you cannot feature detect it.
8457     prevOnError = win.onerror || Ext.emptyFn;
8458     win.onerror = function (message) {
8459         ++errors;
8460
8461         if (!extraordinarilyBad.test(message)) {
8462             // too much recursion + our alert right now = crash IE
8463             // our polling loop will pick it up even if we don't alert now
8464             notify();
8465         }
8466
8467         return prevOnError.apply(this, arguments);
8468     };
8469     poll();
8470 })();
8471
8472
8473
8474 /*
8475
8476 This file is part of Ext JS 4
8477
8478 Copyright (c) 2011 Sencha Inc
8479
8480 Contact:  http://www.sencha.com/contact
8481
8482 GNU General Public License Usage
8483 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.
8484
8485 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
8486
8487 */
8488 /**
8489  * @class Ext.JSON
8490  * Modified version of Douglas Crockford"s json.js that doesn"t
8491  * mess with the Object prototype
8492  * http://www.json.org/js.html
8493  * @singleton
8494  */
8495 Ext.JSON = new(function() {
8496     var useHasOwn = !! {}.hasOwnProperty,
8497     isNative = function() {
8498         var useNative = null;
8499
8500         return function() {
8501             if (useNative === null) {
8502                 useNative = Ext.USE_NATIVE_JSON && window.JSON && JSON.toString() == '[object JSON]';
8503             }
8504
8505             return useNative;
8506         };
8507     }(),
8508     pad = function(n) {
8509         return n < 10 ? "0" + n : n;
8510     },
8511     doDecode = function(json) {
8512         return eval("(" + json + ')');
8513     },
8514     doEncode = function(o) {
8515         if (!Ext.isDefined(o) || o === null) {
8516             return "null";
8517         } else if (Ext.isArray(o)) {
8518             return encodeArray(o);
8519         } else if (Ext.isDate(o)) {
8520             return Ext.JSON.encodeDate(o);
8521         } else if (Ext.isString(o)) {
8522             return encodeString(o);
8523         } else if (typeof o == "number") {
8524             //don't use isNumber here, since finite checks happen inside isNumber
8525             return isFinite(o) ? String(o) : "null";
8526         } else if (Ext.isBoolean(o)) {
8527             return String(o);
8528         } else if (Ext.isObject(o)) {
8529             return encodeObject(o);
8530         } else if (typeof o === "function") {
8531             return "null";
8532         }
8533         return 'undefined';
8534     },
8535     m = {
8536         "\b": '\\b',
8537         "\t": '\\t',
8538         "\n": '\\n',
8539         "\f": '\\f',
8540         "\r": '\\r',
8541         '"': '\\"',
8542         "\\": '\\\\',
8543         '\x0b': '\\u000b' //ie doesn't handle \v
8544     },
8545     charToReplace = /[\\\"\x00-\x1f\x7f-\uffff]/g,
8546     encodeString = function(s) {
8547         return '"' + s.replace(charToReplace, function(a) {
8548             var c = m[a];
8549             return typeof c === 'string' ? c : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
8550         }) + '"';
8551     },
8552     encodeArray = function(o) {
8553         var a = ["[", ""],
8554         // Note empty string in case there are no serializable members.
8555         len = o.length,
8556         i;
8557         for (i = 0; i < len; i += 1) {
8558             a.push(doEncode(o[i]), ',');
8559         }
8560         // Overwrite trailing comma (or empty string)
8561         a[a.length - 1] = ']';
8562         return a.join("");
8563     },
8564     encodeObject = function(o) {
8565         var a = ["{", ""],
8566         // Note empty string in case there are no serializable members.
8567         i;
8568         for (i in o) {
8569             if (!useHasOwn || o.hasOwnProperty(i)) {
8570                 a.push(doEncode(i), ":", doEncode(o[i]), ',');
8571             }
8572         }
8573         // Overwrite trailing comma (or empty string)
8574         a[a.length - 1] = '}';
8575         return a.join("");
8576     };
8577
8578     /**
8579      * <p>Encodes a Date. This returns the actual string which is inserted into the JSON string as the literal expression.
8580      * <b>The returned value includes enclosing double quotation marks.</b></p>
8581      * <p>The default return format is "yyyy-mm-ddThh:mm:ss".</p>
8582      * <p>To override this:</p><pre><code>
8583      Ext.JSON.encodeDate = function(d) {
8584      return d.format('"Y-m-d"');
8585      };
8586      </code></pre>
8587      * @param {Date} d The Date to encode
8588      * @return {String} The string literal to use in a JSON string.
8589      */
8590     this.encodeDate = function(o) {
8591         return '"' + o.getFullYear() + "-" 
8592         + pad(o.getMonth() + 1) + "-"
8593         + pad(o.getDate()) + "T"
8594         + pad(o.getHours()) + ":"
8595         + pad(o.getMinutes()) + ":"
8596         + pad(o.getSeconds()) + '"';
8597     };
8598
8599     /**
8600      * Encodes an Object, Array or other value
8601      * @param {Mixed} o The variable to encode
8602      * @return {String} The JSON string
8603      */
8604     this.encode = function() {
8605         var ec;
8606         return function(o) {
8607             if (!ec) {
8608                 // setup encoding function on first access
8609                 ec = isNative() ? JSON.stringify : doEncode;
8610             }
8611             return ec(o);
8612         };
8613     }();
8614
8615
8616     /**
8617      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError unless the safe option is set.
8618      * @param {String} json The JSON string
8619      * @param {Boolean} safe (optional) Whether to return null or throw an exception if the JSON is invalid.
8620      * @return {Object} The resulting object
8621      */
8622     this.decode = function() {
8623         var dc;
8624         return function(json, safe) {
8625             if (!dc) {
8626                 // setup decoding function on first access
8627                 dc = isNative() ? JSON.parse : doDecode;
8628             }
8629             try {
8630                 return dc(json);
8631             } catch (e) {
8632                 if (safe === true) {
8633                     return null;
8634                 }
8635                 Ext.Error.raise({
8636                     sourceClass: "Ext.JSON",
8637                     sourceMethod: "decode",
8638                     msg: "You're trying to decode an invalid JSON String: " + json
8639                 });
8640             }
8641         };
8642     }();
8643
8644 })();
8645 /**
8646  * Shorthand for {@link Ext.JSON#encode}
8647  * @param {Mixed} o The variable to encode
8648  * @return {String} The JSON string
8649  * @member Ext
8650  * @method encode
8651  */
8652 Ext.encode = Ext.JSON.encode;
8653 /**
8654  * Shorthand for {@link Ext.JSON#decode}
8655  * @param {String} json The JSON string
8656  * @param {Boolean} safe (optional) Whether to return null or throw an exception if the JSON is invalid.
8657  * @return {Object} The resulting object
8658  * @member Ext
8659  * @method decode
8660  */
8661 Ext.decode = Ext.JSON.decode;
8662
8663
8664 /**
8665  * @class Ext
8666
8667  The Ext namespace (global object) encapsulates all classes, singletons, and utility methods provided by Sencha's libraries.</p>
8668  Most user interface Components are at a lower level of nesting in the namespace, but many common utility functions are provided
8669  as direct properties of the Ext namespace.
8670
8671  Also many frequently used methods from other classes are provided as shortcuts within the Ext namespace.
8672  For example {@link Ext#getCmp Ext.getCmp} aliases {@link Ext.ComponentManager#get Ext.ComponentManager.get}.
8673
8674  Many applications are initiated with {@link Ext#onReady Ext.onReady} which is called once the DOM is ready.
8675  This ensures all scripts have been loaded, preventing dependency issues. For example
8676
8677      Ext.onReady(function(){
8678          new Ext.Component({
8679              renderTo: document.body,
8680              html: 'DOM ready!'
8681          });
8682      });
8683
8684 For more information about how to use the Ext classes, see
8685
8686 - <a href="http://www.sencha.com/learn/">The Learning Center</a>
8687 - <a href="http://www.sencha.com/learn/Ext_FAQ">The FAQ</a>
8688 - <a href="http://www.sencha.com/forum/">The forums</a>
8689
8690  * @singleton
8691  * @markdown
8692  */
8693 Ext.apply(Ext, {
8694     userAgent: navigator.userAgent.toLowerCase(),
8695     cache: {},
8696     idSeed: 1000,
8697     BLANK_IMAGE_URL : 'data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==',
8698     isStrict: document.compatMode == "CSS1Compat",
8699     windowId: 'ext-window',
8700     documentId: 'ext-document',
8701
8702     /**
8703      * True when the document is fully initialized and ready for action
8704      * @type Boolean
8705      */
8706     isReady: false,
8707
8708     /**
8709      * True to automatically uncache orphaned Ext.core.Elements periodically (defaults to true)
8710      * @type Boolean
8711      */
8712     enableGarbageCollector: true,
8713
8714     /**
8715      * True to automatically purge event listeners during garbageCollection (defaults to true).
8716      * @type Boolean
8717      */
8718     enableListenerCollection: true,
8719
8720     /**
8721      * Generates unique ids. If the element already has an id, it is unchanged
8722      * @param {Mixed} el (optional) The element to generate an id for
8723      * @param {String} prefix (optional) Id prefix (defaults "ext-gen")
8724      * @return {String} The generated Id.
8725      */
8726     id: function(el, prefix) {
8727         var me = this,
8728             sandboxPrefix = '';
8729         el = Ext.getDom(el, true) || {};
8730         if (el === document) {
8731             el.id = me.documentId;
8732         }
8733         else if (el === window) {
8734             el.id = me.windowId;
8735         }
8736         if (!el.id) {
8737             if (me.isSandboxed) {
8738                 if (!me.uniqueGlobalNamespace) {
8739                     me.getUniqueGlobalNamespace();
8740                 }
8741                 sandboxPrefix = me.uniqueGlobalNamespace + '-';
8742             }
8743             el.id = sandboxPrefix + (prefix || "ext-gen") + (++Ext.idSeed);
8744         }
8745         return el.id;
8746     },
8747
8748     /**
8749      * Returns the current document body as an {@link Ext.core.Element}.
8750      * @return Ext.core.Element The document body
8751      */
8752     getBody: function() {
8753         return Ext.get(document.body || false);
8754     },
8755
8756     /**
8757      * Returns the current document head as an {@link Ext.core.Element}.
8758      * @return Ext.core.Element The document head
8759      * @method
8760      */
8761     getHead: function() {
8762         var head;
8763
8764         return function() {
8765             if (head == undefined) {
8766                 head = Ext.get(document.getElementsByTagName("head")[0]);
8767             }
8768
8769             return head;
8770         };
8771     }(),
8772
8773     /**
8774      * Returns the current HTML document object as an {@link Ext.core.Element}.
8775      * @return Ext.core.Element The document
8776      */
8777     getDoc: function() {
8778         return Ext.get(document);
8779     },
8780
8781     /**
8782      * This is shorthand reference to {@link Ext.ComponentManager#get}.
8783      * Looks up an existing {@link Ext.Component Component} by {@link Ext.Component#id id}
8784      * @param {String} id The component {@link Ext.Component#id id}
8785      * @return Ext.Component The Component, <tt>undefined</tt> if not found, or <tt>null</tt> if a
8786      * Class was found.
8787     */
8788     getCmp: function(id) {
8789         return Ext.ComponentManager.get(id);
8790     },
8791
8792     /**
8793      * Returns the current orientation of the mobile device
8794      * @return {String} Either 'portrait' or 'landscape'
8795      */
8796     getOrientation: function() {
8797         return window.innerHeight > window.innerWidth ? 'portrait' : 'landscape';
8798     },
8799
8800     /**
8801      * Attempts to destroy any objects passed to it by removing all event listeners, removing them from the
8802      * DOM (if applicable) and calling their destroy functions (if available).  This method is primarily
8803      * intended for arguments of type {@link Ext.core.Element} and {@link Ext.Component}, but any subclass of
8804      * {@link Ext.util.Observable} can be passed in.  Any number of elements and/or components can be
8805      * passed into this function in a single call as separate arguments.
8806      * @param {Mixed} arg1 An {@link Ext.core.Element}, {@link Ext.Component}, or an Array of either of these to destroy
8807      * @param {Mixed} arg2 (optional)
8808      * @param {Mixed} etc... (optional)
8809      */
8810     destroy: function() {
8811         var ln = arguments.length,
8812         i, arg;
8813
8814         for (i = 0; i < ln; i++) {
8815             arg = arguments[i];
8816             if (arg) {
8817                 if (Ext.isArray(arg)) {
8818                     this.destroy.apply(this, arg);
8819                 }
8820                 else if (Ext.isFunction(arg.destroy)) {
8821                     arg.destroy();
8822                 }
8823                 else if (arg.dom) {
8824                     arg.remove();
8825                 }
8826             }
8827         }
8828     },
8829
8830     /**
8831      * Execute a callback function in a particular scope. If no function is passed the call is ignored.
8832      * 
8833      * For example, these lines are equivalent:
8834      * 
8835      *     Ext.callback(myFunc, this, [arg1, arg2]);
8836      *     Ext.isFunction(myFunc) && myFunc.apply(this, [arg1, arg2]);
8837      * 
8838      * @param {Function} callback The callback to execute
8839      * @param {Object} scope (optional) The scope to execute in
8840      * @param {Array} args (optional) The arguments to pass to the function
8841      * @param {Number} delay (optional) Pass a number to delay the call by a number of milliseconds.
8842      */
8843     callback: function(callback, scope, args, delay){
8844         if(Ext.isFunction(callback)){
8845             args = args || [];
8846             scope = scope || window;
8847             if (delay) {
8848                 Ext.defer(callback, delay, scope, args);
8849             } else {
8850                 callback.apply(scope, args);
8851             }
8852         }
8853     },
8854
8855     /**
8856      * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
8857      * @param {String} value The string to encode
8858      * @return {String} The encoded text
8859      */
8860     htmlEncode : function(value) {
8861         return Ext.String.htmlEncode(value);
8862     },
8863
8864     /**
8865      * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
8866      * @param {String} value The string to decode
8867      * @return {String} The decoded text
8868      */
8869     htmlDecode : function(value) {
8870          return Ext.String.htmlDecode(value);
8871     },
8872
8873     /**
8874      * Appends content to the query string of a URL, handling logic for whether to place
8875      * a question mark or ampersand.
8876      * @param {String} url The URL to append to.
8877      * @param {String} s The content to append to the URL.
8878      * @return (String) The resulting URL
8879      */
8880     urlAppend : function(url, s) {
8881         if (!Ext.isEmpty(s)) {
8882             return url + (url.indexOf('?') === -1 ? '?' : '&') + s;
8883         }
8884         return url;
8885     }
8886 });
8887
8888
8889 Ext.ns = Ext.namespace;
8890
8891 // for old browsers
8892 window.undefined = window.undefined;
8893
8894 /**
8895  * @class Ext
8896  * Ext core utilities and functions.
8897  * @singleton
8898  */
8899 (function(){
8900     var check = function(regex){
8901             return regex.test(Ext.userAgent);
8902         },
8903         docMode = document.documentMode,
8904         isOpera = check(/opera/),
8905         isOpera10_5 = isOpera && check(/version\/10\.5/),
8906         isChrome = check(/\bchrome\b/),
8907         isWebKit = check(/webkit/),
8908         isSafari = !isChrome && check(/safari/),
8909         isSafari2 = isSafari && check(/applewebkit\/4/), // unique to Safari 2
8910         isSafari3 = isSafari && check(/version\/3/),
8911         isSafari4 = isSafari && check(/version\/4/),
8912         isIE = !isOpera && check(/msie/),
8913         isIE7 = isIE && (check(/msie 7/) || docMode == 7),
8914         isIE8 = isIE && (check(/msie 8/) && docMode != 7 && docMode != 9 || docMode == 8),
8915         isIE9 = isIE && (check(/msie 9/) && docMode != 7 && docMode != 8 || docMode == 9),
8916         isIE6 = isIE && check(/msie 6/),
8917         isGecko = !isWebKit && check(/gecko/),
8918         isGecko3 = isGecko && check(/rv:1\.9/),
8919         isGecko4 = isGecko && check(/rv:2\.0/),
8920         isFF3_0 = isGecko3 && check(/rv:1\.9\.0/),
8921         isFF3_5 = isGecko3 && check(/rv:1\.9\.1/),
8922         isFF3_6 = isGecko3 && check(/rv:1\.9\.2/),
8923         isWindows = check(/windows|win32/),
8924         isMac = check(/macintosh|mac os x/),
8925         isLinux = check(/linux/),
8926         scrollbarSize = null,
8927         webKitVersion = isWebKit && (/webkit\/(\d+\.\d+)/.exec(Ext.userAgent));
8928
8929     // remove css image flicker
8930     try {
8931         document.execCommand("BackgroundImageCache", false, true);
8932     } catch(e) {}
8933
8934     Ext.setVersion('extjs', '4.0.2');
8935     Ext.apply(Ext, {
8936         /**
8937          * URL to a blank file used by Ext when in secure mode for iframe src and onReady src to prevent
8938          * the IE insecure content warning (<tt>'about:blank'</tt>, except for IE in secure mode, which is <tt>'javascript:""'</tt>).
8939          * @type String
8940          */
8941         SSL_SECURE_URL : Ext.isSecure && isIE ? 'javascript:""' : 'about:blank',
8942
8943         /**
8944          * True if the {@link Ext.fx.Anim} Class is available
8945          * @type Boolean
8946          * @property enableFx
8947          */
8948
8949         /**
8950          * True to scope the reset CSS to be just applied to Ext components. Note that this wraps root containers
8951          * with an additional element. Also remember that when you turn on this option, you have to use ext-all-scoped {
8952          * unless you use the bootstrap.js to load your javascript, in which case it will be handled for you.
8953          * @type Boolean
8954          */
8955         scopeResetCSS : Ext.buildSettings.scopeResetCSS,
8956
8957         /**
8958          * EXPERIMENTAL - True to cascade listener removal to child elements when an element is removed.
8959          * Currently not optimized for performance.
8960          * @type Boolean
8961          */
8962         enableNestedListenerRemoval : false,
8963
8964         /**
8965          * Indicates whether to use native browser parsing for JSON methods.
8966          * This option is ignored if the browser does not support native JSON methods.
8967          * <b>Note: Native JSON methods will not work with objects that have functions.
8968          * Also, property names must be quoted, otherwise the data will not parse.</b> (Defaults to false)
8969          * @type Boolean
8970          */
8971         USE_NATIVE_JSON : false,
8972
8973         /**
8974          * Return the dom node for the passed String (id), dom node, or Ext.core.Element.
8975          * Optional 'strict' flag is needed for IE since it can return 'name' and
8976          * 'id' elements by using getElementById.
8977          * Here are some examples:
8978          * <pre><code>
8979 // gets dom node based on id
8980 var elDom = Ext.getDom('elId');
8981 // gets dom node based on the dom node
8982 var elDom1 = Ext.getDom(elDom);
8983
8984 // If we don&#39;t know if we are working with an
8985 // Ext.core.Element or a dom node use Ext.getDom
8986 function(el){
8987     var dom = Ext.getDom(el);
8988     // do something with the dom node
8989 }
8990          * </code></pre>
8991          * <b>Note</b>: the dom node to be found actually needs to exist (be rendered, etc)
8992          * when this method is called to be successful.
8993          * @param {Mixed} el
8994          * @return HTMLElement
8995          */
8996         getDom : function(el, strict) {
8997             if (!el || !document) {
8998                 return null;
8999             }
9000             if (el.dom) {
9001                 return el.dom;
9002             } else {
9003                 if (typeof el == 'string') {
9004                     var e = document.getElementById(el);
9005                     // IE returns elements with the 'name' and 'id' attribute.
9006                     // we do a strict check to return the element with only the id attribute
9007                     if (e && isIE && strict) {
9008                         if (el == e.getAttribute('id')) {
9009                             return e;
9010                         } else {
9011                             return null;
9012                         }
9013                     }
9014                     return e;
9015                 } else {
9016                     return el;
9017                 }
9018             }
9019         },
9020
9021         /**
9022          * Removes a DOM node from the document.
9023          * <p>Removes this element from the document, removes all DOM event listeners, and deletes the cache reference.
9024          * All DOM event listeners are removed from this element. If {@link Ext#enableNestedListenerRemoval Ext.enableNestedListenerRemoval} is
9025          * <code>true</code>, then DOM event listeners are also removed from all child nodes. The body node
9026          * will be ignored if passed in.</p>
9027          * @param {HTMLElement} node The node to remove
9028          * @method
9029          */
9030         removeNode : isIE6 || isIE7 ? function() {
9031             var d;
9032             return function(n){
9033                 if(n && n.tagName != 'BODY'){
9034                     (Ext.enableNestedListenerRemoval) ? Ext.EventManager.purgeElement(n) : Ext.EventManager.removeAll(n);
9035                     d = d || document.createElement('div');
9036                     d.appendChild(n);
9037                     d.innerHTML = '';
9038                     delete Ext.cache[n.id];
9039                 }
9040             };
9041         }() : function(n) {
9042             if (n && n.parentNode && n.tagName != 'BODY') {
9043                 (Ext.enableNestedListenerRemoval) ? Ext.EventManager.purgeElement(n) : Ext.EventManager.removeAll(n);
9044                 n.parentNode.removeChild(n);
9045                 delete Ext.cache[n.id];
9046             }
9047         },
9048
9049         /**
9050          * True if the detected browser is Opera.
9051          * @type Boolean
9052          */
9053         isOpera : isOpera,
9054
9055         /**
9056          * True if the detected browser is Opera 10.5x.
9057          * @type Boolean
9058          */
9059         isOpera10_5 : isOpera10_5,
9060
9061         /**
9062          * True if the detected browser uses WebKit.
9063          * @type Boolean
9064          */
9065         isWebKit : isWebKit,
9066
9067         /**
9068          * True if the detected browser is Chrome.
9069          * @type Boolean
9070          */
9071         isChrome : isChrome,
9072
9073         /**
9074          * True if the detected browser is Safari.
9075          * @type Boolean
9076          */
9077         isSafari : isSafari,
9078
9079         /**
9080          * True if the detected browser is Safari 3.x.
9081          * @type Boolean
9082          */
9083         isSafari3 : isSafari3,
9084
9085         /**
9086          * True if the detected browser is Safari 4.x.
9087          * @type Boolean
9088          */
9089         isSafari4 : isSafari4,
9090
9091         /**
9092          * True if the detected browser is Safari 2.x.
9093          * @type Boolean
9094          */
9095         isSafari2 : isSafari2,
9096
9097         /**
9098          * True if the detected browser is Internet Explorer.
9099          * @type Boolean
9100          */
9101         isIE : isIE,
9102
9103         /**
9104          * True if the detected browser is Internet Explorer 6.x.
9105          * @type Boolean
9106          */
9107         isIE6 : isIE6,
9108
9109         /**
9110          * True if the detected browser is Internet Explorer 7.x.
9111          * @type Boolean
9112          */
9113         isIE7 : isIE7,
9114
9115         /**
9116          * True if the detected browser is Internet Explorer 8.x.
9117          * @type Boolean
9118          */
9119         isIE8 : isIE8,
9120
9121         /**
9122          * True if the detected browser is Internet Explorer 9.x.
9123          * @type Boolean
9124          */
9125         isIE9 : isIE9,
9126
9127         /**
9128          * True if the detected browser uses the Gecko layout engine (e.g. Mozilla, Firefox).
9129          * @type Boolean
9130          */
9131         isGecko : isGecko,
9132
9133         /**
9134          * True if the detected browser uses a Gecko 1.9+ layout engine (e.g. Firefox 3.x).
9135          * @type Boolean
9136          */
9137         isGecko3 : isGecko3,
9138
9139         /**
9140          * True if the detected browser uses a Gecko 2.0+ layout engine (e.g. Firefox 4.x).
9141          * @type Boolean
9142          */
9143         isGecko4 : isGecko4,
9144
9145         /**
9146          * True if the detected browser uses FireFox 3.0
9147          * @type Boolean
9148          */
9149
9150         isFF3_0 : isFF3_0,
9151         /**
9152          * True if the detected browser uses FireFox 3.5
9153          * @type Boolean
9154          */
9155
9156         isFF3_5 : isFF3_5,
9157         /**
9158          * True if the detected browser uses FireFox 3.6
9159          * @type Boolean
9160          */
9161         isFF3_6 : isFF3_6,
9162
9163         /**
9164          * True if the detected platform is Linux.
9165          * @type Boolean
9166          */
9167         isLinux : isLinux,
9168
9169         /**
9170          * True if the detected platform is Windows.
9171          * @type Boolean
9172          */
9173         isWindows : isWindows,
9174
9175         /**
9176          * True if the detected platform is Mac OS.
9177          * @type Boolean
9178          */
9179         isMac : isMac,
9180
9181         /**
9182          * The current version of WebKit (-1 if the browser does not use WebKit).
9183          * @type Float
9184          */
9185         webKitVersion: webKitVersion ? parseFloat(webKitVersion[1]) : -1,
9186
9187         /**
9188          * URL to a 1x1 transparent gif image used by Ext to create inline icons with CSS background images.
9189          * In older versions of IE, this defaults to "http://sencha.com/s.gif" and you should change this to a URL on your server.
9190          * For other browsers it uses an inline data URL.
9191          * @type String
9192          */
9193         BLANK_IMAGE_URL : (isIE6 || isIE7) ? 'http:/' + '/www.sencha.com/s.gif' : 'data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==',
9194
9195         /**
9196          * <p>Utility method for returning a default value if the passed value is empty.</p>
9197          * <p>The value is deemed to be empty if it is<div class="mdetail-params"><ul>
9198          * <li>null</li>
9199          * <li>undefined</li>
9200          * <li>an empty array</li>
9201          * <li>a zero length string (Unless the <tt>allowBlank</tt> parameter is <tt>true</tt>)</li>
9202          * </ul></div>
9203          * @param {Mixed} value The value to test
9204          * @param {Mixed} defaultValue The value to return if the original value is empty
9205          * @param {Boolean} allowBlank (optional) true to allow zero length strings to qualify as non-empty (defaults to false)
9206          * @return {Mixed} value, if non-empty, else defaultValue
9207          * @deprecated 4.0.0 Use {@link Ext#valueFrom} instead
9208          */
9209         value : function(v, defaultValue, allowBlank){
9210             return Ext.isEmpty(v, allowBlank) ? defaultValue : v;
9211         },
9212
9213         /**
9214          * Escapes the passed string for use in a regular expression
9215          * @param {String} str
9216          * @return {String}
9217          * @deprecated 4.0.0 Use {@link Ext.String#escapeRegex} instead
9218          */
9219         escapeRe : function(s) {
9220             return s.replace(/([-.*+?^${}()|[\]\/\\])/g, "\\$1");
9221         },
9222
9223         /**
9224          * Applies event listeners to elements by selectors when the document is ready.
9225          * The event name is specified with an <tt>&#64;</tt> suffix.
9226          * <pre><code>
9227 Ext.addBehaviors({
9228     // add a listener for click on all anchors in element with id foo
9229     '#foo a&#64;click' : function(e, t){
9230         // do something
9231     },
9232
9233     // add the same listener to multiple selectors (separated by comma BEFORE the &#64;)
9234     '#foo a, #bar span.some-class&#64;mouseover' : function(){
9235         // do something
9236     }
9237 });
9238          * </code></pre>
9239          * @param {Object} obj The list of behaviors to apply
9240          */
9241         addBehaviors : function(o){
9242             if(!Ext.isReady){
9243                 Ext.onReady(function(){
9244                     Ext.addBehaviors(o);
9245                 });
9246             } else {
9247                 var cache = {}, // simple cache for applying multiple behaviors to same selector does query multiple times
9248                     parts,
9249                     b,
9250                     s;
9251                 for (b in o) {
9252                     if ((parts = b.split('@'))[1]) { // for Object prototype breakers
9253                         s = parts[0];
9254                         if(!cache[s]){
9255                             cache[s] = Ext.select(s);
9256                         }
9257                         cache[s].on(parts[1], o[b]);
9258                     }
9259                 }
9260                 cache = null;
9261             }
9262         },
9263
9264         /**
9265          * Returns the size of the browser scrollbars. This can differ depending on
9266          * operating system settings, such as the theme or font size.
9267          * @param {Boolean} force (optional) true to force a recalculation of the value.
9268          * @return {Object} An object containing the width of a vertical scrollbar and the
9269          * height of a horizontal scrollbar.
9270          */
9271         getScrollbarSize: function (force) {
9272             if(!Ext.isReady){
9273                 return 0;
9274             }
9275
9276             if(force === true || scrollbarSize === null){
9277                 // BrowserBug: IE9
9278                 // When IE9 positions an element offscreen via offsets, the offsetWidth is
9279                 // inaccurately reported. For IE9 only, we render on screen before removing.
9280                 var cssClass = Ext.isIE9 ? '' : Ext.baseCSSPrefix + 'hide-offsets',
9281                     // Append our div, do our calculation and then remove it
9282                     div = Ext.getBody().createChild('<div class="' + cssClass + '" style="width:100px;height:50px;overflow:hidden;"><div style="height:200px;"></div></div>'),
9283                     child = div.child('div', true),
9284                     w1 = child.offsetWidth;
9285
9286                 div.setStyle('overflow', (Ext.isWebKit || Ext.isGecko) ? 'auto' : 'scroll');
9287
9288                 var w2 = child.offsetWidth, width = w1 - w2;
9289                 div.remove();
9290
9291                 // We assume width == height for now. TODO: is this always true?
9292                 scrollbarSize = { width: width, height: width };
9293             }
9294
9295             return scrollbarSize;
9296         },
9297
9298         /**
9299          * Utility method for getting the width of the browser's vertical scrollbar. This
9300          * can differ depending on operating system settings, such as the theme or font size.
9301          *
9302          * This method is deprected in favor of {@link #getScrollbarSize}.
9303          *
9304          * @param {Boolean} force (optional) true to force a recalculation of the value.
9305          * @return {Number} The width of a vertical scrollbar.
9306          * @deprecated
9307          */
9308         getScrollBarWidth: function(force){
9309             var size = Ext.getScrollbarSize(force);
9310             return size.width + 2; // legacy fudge factor
9311         },
9312
9313         /**
9314          * Copies a set of named properties fom the source object to the destination object.
9315          *
9316          * Example:
9317          *
9318          *     ImageComponent = Ext.extend(Ext.Component, {
9319          *         initComponent: function() {
9320          *             this.autoEl = { tag: 'img' };
9321          *             MyComponent.superclass.initComponent.apply(this, arguments);
9322          *             this.initialBox = Ext.copyTo({}, this.initialConfig, 'x,y,width,height');
9323          *         }
9324          *     });
9325          *
9326          * Important note: To borrow class prototype methods, use {@link Ext.Base#borrow} instead.
9327          *
9328          * @param {Object} dest The destination object.
9329          * @param {Object} source The source object.
9330          * @param {Array/String} names Either an Array of property names, or a comma-delimited list
9331          * of property names to copy.
9332          * @param {Boolean} usePrototypeKeys (Optional) Defaults to false. Pass true to copy keys off of the prototype as well as the instance.
9333          * @return {Object} The modified object.
9334          */
9335         copyTo : function(dest, source, names, usePrototypeKeys){
9336             if(typeof names == 'string'){
9337                 names = names.split(/[,;\s]/);
9338             }
9339             Ext.each(names, function(name){
9340                 if(usePrototypeKeys || source.hasOwnProperty(name)){
9341                     dest[name] = source[name];
9342                 }
9343             }, this);
9344             return dest;
9345         },
9346
9347         /**
9348          * Attempts to destroy and then remove a set of named properties of the passed object.
9349          * @param {Object} o The object (most likely a Component) who's properties you wish to destroy.
9350          * @param {Mixed} arg1 The name of the property to destroy and remove from the object.
9351          * @param {Mixed} etc... More property names to destroy and remove.
9352          */
9353         destroyMembers : function(o){
9354             for (var i = 1, a = arguments, len = a.length; i < len; i++) {
9355                 Ext.destroy(o[a[i]]);
9356                 delete o[a[i]];
9357             }
9358         },
9359
9360         /**
9361          * Logs a message. If a console is present it will be used. On Opera, the method
9362          * "opera.postError" is called. In other cases, the message is logged to an array
9363          * "Ext.log.out". An attached debugger can watch this array and view the log. The
9364          * log buffer is limited to a maximum of "Ext.log.max" entries (defaults to 100).
9365          *
9366          * If additional parameters are passed, they are joined and appended to the message.
9367          * 
9368          * This method does nothing in a release build.
9369          *
9370          * @param {String|Object} message The message to log or an options object with any
9371          * of the following properties:
9372          *
9373          *  - `msg`: The message to log (required).
9374          *  - `level`: One of: "error", "warn", "info" or "log" (the default is "log").
9375          *  - `dump`: An object to dump to the log as part of the message.
9376          *  - `stack`: True to include a stack trace in the log.
9377          * @markdown
9378          */
9379         log : function (message) {
9380             var options, dump,
9381                 con = Ext.global.console,
9382                 log = Ext.log,
9383                 level = 'log',
9384                 stack,
9385                 members,
9386                 member;
9387
9388             if (!Ext.isString(message)) {
9389                 options = message;
9390                 message = options.msg || '';
9391                 level = options.level || level;
9392                 dump = options.dump;
9393                 stack = options.stack;
9394
9395                 if (dump && !(con && con.dir)) {
9396                     members = [];
9397
9398                     // Cannot use Ext.encode since it can recurse endlessly (if we're lucky)
9399                     // ...and the data could be prettier!
9400                     Ext.Object.each(dump, function (name, value) {
9401                         if (typeof(value) === "function") {
9402                             return;
9403                         }
9404
9405                         if (!Ext.isDefined(value) || value === null ||
9406                                 Ext.isDate(value) ||
9407                                 Ext.isString(value) || (typeof(value) == "number") ||
9408                                 Ext.isBoolean(value)) {
9409                             member = Ext.encode(value);
9410                         } else if (Ext.isArray(value)) {
9411                             member = '[ ]';
9412                         } else if (Ext.isObject(value)) {
9413                             member = '{ }';
9414                         } else {
9415                             member = 'undefined';
9416                         }
9417                         members.push(Ext.encode(name) + ': ' + member);
9418                     });
9419
9420                     if (members.length) {
9421                         message += ' \nData: {\n  ' + members.join(',\n  ') + '\n}';
9422                     }
9423                     dump = null;
9424                 }
9425             }
9426
9427             if (arguments.length > 1) {
9428                 message += Array.prototype.slice.call(arguments, 1).join('');
9429             }
9430
9431             // Not obvious, but 'console' comes and goes when Firebug is turned on/off, so
9432             // an early test may fail either direction if Firebug is toggled.
9433             //
9434             if (con) { // if (Firebug-like console)
9435                 if (con[level]) {
9436                     con[level](message);
9437                 } else {
9438                     con.log(message);
9439                 }
9440
9441                 if (dump) {
9442                     con.dir(dump);
9443                 }
9444
9445                 if (stack && con.trace) {
9446                     // Firebug's console.error() includes a trace already...
9447                     if (!con.firebug || level != 'error') {
9448                         con.trace();
9449                     }
9450                 }
9451             } else {
9452                 // w/o console, all messages are equal, so munge the level into the message:
9453                 if (level != 'log') {
9454                     message = level.toUpperCase() + ': ' + message;
9455                 }
9456
9457                 if (Ext.isOpera) {
9458                     opera.postError(message);
9459                 } else {
9460                     var out = log.out || (log.out = []),
9461                         max = log.max || (log.max = 100);
9462
9463                     if (out.length >= max) {
9464                         // this formula allows out.max to change (via debugger), where the
9465                         // more obvious "max/4" would not quite be the same
9466                         Ext.Array.erase(out, 0, out.length - 3 * Math.floor(max / 4)); // keep newest 75%
9467                     }
9468
9469                     out.push(message);
9470                 }
9471             }
9472
9473             // Mostly informational, but the Ext.Error notifier uses them:
9474             var counters = log.counters ||
9475                           (log.counters = { error: 0, warn: 0, info: 0, log: 0 });
9476
9477             ++counters[level];
9478         },
9479
9480         /**
9481          * Partitions the set into two sets: a true set and a false set.
9482          * Example:
9483          * Example2:
9484          * <pre><code>
9485 // Example 1:
9486 Ext.partition([true, false, true, true, false]); // [[true, true, true], [false, false]]
9487
9488 // Example 2:
9489 Ext.partition(
9490     Ext.query("p"),
9491     function(val){
9492         return val.className == "class1"
9493     }
9494 );
9495 // true are those paragraph elements with a className of "class1",
9496 // false set are those that do not have that className.
9497          * </code></pre>
9498          * @param {Array|NodeList} arr The array to partition
9499          * @param {Function} truth (optional) a function to determine truth.  If this is omitted the element
9500          * itself must be able to be evaluated for its truthfulness.
9501          * @return {Array} [array of truish values, array of falsy values]
9502          * @deprecated 4.0.0 Will be removed in the next major version
9503          */
9504         partition : function(arr, truth){
9505             var ret = [[],[]];
9506             Ext.each(arr, function(v, i, a) {
9507                 ret[ (truth && truth(v, i, a)) || (!truth && v) ? 0 : 1].push(v);
9508             });
9509             return ret;
9510         },
9511
9512         /**
9513          * Invokes a method on each item in an Array.
9514          * <pre><code>
9515 // Example:
9516 Ext.invoke(Ext.query("p"), "getAttribute", "id");
9517 // [el1.getAttribute("id"), el2.getAttribute("id"), ..., elN.getAttribute("id")]
9518          * </code></pre>
9519          * @param {Array|NodeList} arr The Array of items to invoke the method on.
9520          * @param {String} methodName The method name to invoke.
9521          * @param {...*} args Arguments to send into the method invocation.
9522          * @return {Array} The results of invoking the method on each item in the array.
9523          * @deprecated 4.0.0 Will be removed in the next major version
9524          */
9525         invoke : function(arr, methodName){
9526             var ret = [],
9527                 args = Array.prototype.slice.call(arguments, 2);
9528             Ext.each(arr, function(v,i) {
9529                 if (v && typeof v[methodName] == 'function') {
9530                     ret.push(v[methodName].apply(v, args));
9531                 } else {
9532                     ret.push(undefined);
9533                 }
9534             });
9535             return ret;
9536         },
9537
9538         /**
9539          * <p>Zips N sets together.</p>
9540          * <pre><code>
9541 // Example 1:
9542 Ext.zip([1,2,3],[4,5,6]); // [[1,4],[2,5],[3,6]]
9543 // Example 2:
9544 Ext.zip(
9545     [ "+", "-", "+"],
9546     [  12,  10,  22],
9547     [  43,  15,  96],
9548     function(a, b, c){
9549         return "$" + a + "" + b + "." + c
9550     }
9551 ); // ["$+12.43", "$-10.15", "$+22.96"]
9552          * </code></pre>
9553          * @param {Arrays|NodeLists} arr This argument may be repeated. Array(s) to contribute values.
9554          * @param {Function} zipper (optional) The last item in the argument list. This will drive how the items are zipped together.
9555          * @return {Array} The zipped set.
9556          * @deprecated 4.0.0 Will be removed in the next major version
9557          */
9558         zip : function(){
9559             var parts = Ext.partition(arguments, function( val ){ return typeof val != 'function'; }),
9560                 arrs = parts[0],
9561                 fn = parts[1][0],
9562                 len = Ext.max(Ext.pluck(arrs, "length")),
9563                 ret = [];
9564
9565             for (var i = 0; i < len; i++) {
9566                 ret[i] = [];
9567                 if(fn){
9568                     ret[i] = fn.apply(fn, Ext.pluck(arrs, i));
9569                 }else{
9570                     for (var j = 0, aLen = arrs.length; j < aLen; j++){
9571                         ret[i].push( arrs[j][i] );
9572                     }
9573                 }
9574             }
9575             return ret;
9576         },
9577
9578         /**
9579          * Turns an array into a sentence, joined by a specified connector - e.g.:
9580          * Ext.toSentence(['Adama', 'Tigh', 'Roslin']); //'Adama, Tigh and Roslin'
9581          * Ext.toSentence(['Adama', 'Tigh', 'Roslin'], 'or'); //'Adama, Tigh or Roslin'
9582          * @param {Array} items The array to create a sentence from
9583          * @param {String} connector The string to use to connect the last two words. Usually 'and' or 'or' - defaults to 'and'.
9584          * @return {String} The sentence string
9585          * @deprecated 4.0.0 Will be removed in the next major version
9586          */
9587         toSentence: function(items, connector) {
9588             var length = items.length;
9589
9590             if (length <= 1) {
9591                 return items[0];
9592             } else {
9593                 var head = items.slice(0, length - 1),
9594                     tail = items[length - 1];
9595
9596                 return Ext.util.Format.format("{0} {1} {2}", head.join(", "), connector || 'and', tail);
9597             }
9598         },
9599
9600         /**
9601          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
9602          * you may want to set this to true.
9603          * @type Boolean
9604          */
9605         useShims: isIE6
9606     });
9607 })();
9608
9609 /**
9610  * Loads Ext.app.Application class and starts it up with given configuration after the page is ready.
9611  *
9612  * See Ext.app.Application for details.
9613  *
9614  * @param {Object} config
9615  */
9616 Ext.application = function(config) {
9617     Ext.require('Ext.app.Application');
9618
9619     Ext.onReady(function() {
9620         Ext.create('Ext.app.Application', config);
9621     });
9622 };
9623
9624 /**
9625  * @class Ext.util.Format
9626
9627 This class is a centralized place for formatting functions inside the library. It includes
9628 functions to format various different types of data, such as text, dates and numeric values.
9629
9630 __Localization__
9631 This class contains several options for localization. These can be set once the library has loaded,
9632 all calls to the functions from that point will use the locale settings that were specified.
9633 Options include:
9634 - thousandSeparator
9635 - decimalSeparator
9636 - currenyPrecision
9637 - currencySign
9638 - currencyAtEnd
9639 This class also uses the default date format defined here: {@link Ext.Date#defaultFormat}.
9640
9641 __Using with renderers__
9642 There are two helper functions that return a new function that can be used in conjunction with 
9643 grid renderers:
9644
9645     columns: [{
9646         dataIndex: 'date',
9647         renderer: Ext.util.Format.dateRenderer('Y-m-d')
9648     }, {
9649         dataIndex: 'time',
9650         renderer: Ext.util.Format.numberRenderer('0.000')
9651     }]
9652     
9653 Functions that only take a single argument can also be passed directly:
9654     columns: [{
9655         dataIndex: 'cost',
9656         renderer: Ext.util.Format.usMoney
9657     }, {
9658         dataIndex: 'productCode',
9659         renderer: Ext.util.Format.uppercase
9660     }]
9661     
9662 __Using with XTemplates__
9663 XTemplates can also directly use Ext.util.Format functions:
9664
9665     new Ext.XTemplate([
9666         'Date: {startDate:date("Y-m-d")}',
9667         'Cost: {cost:usMoney}'
9668     ]);
9669
9670  * @markdown
9671  * @singleton
9672  */
9673 (function() {
9674     Ext.ns('Ext.util');
9675
9676     Ext.util.Format = {};
9677     var UtilFormat     = Ext.util.Format,
9678         stripTagsRE    = /<\/?[^>]+>/gi,
9679         stripScriptsRe = /(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig,
9680         nl2brRe        = /\r?\n/g,
9681
9682         // A RegExp to remove from a number format string, all characters except digits and '.'
9683         formatCleanRe  = /[^\d\.]/g,
9684
9685         // A RegExp to remove from a number format string, all characters except digits and the local decimal separator.
9686         // Created on first use. The local decimal separator character must be initialized for this to be created.
9687         I18NFormatCleanRe;
9688
9689     Ext.apply(UtilFormat, {
9690         /**
9691          * @type String
9692          * @property thousandSeparator
9693          * <p>The character that the {@link #number} function uses as a thousand separator.</p>
9694          * <p>This defaults to <code>,</code>, but may be overridden in a locale file.</p>
9695          */
9696         thousandSeparator: ',',
9697
9698         /**
9699          * @type String
9700          * @property decimalSeparator
9701          * <p>The character that the {@link #number} function uses as a decimal point.</p>
9702          * <p>This defaults to <code>.</code>, but may be overridden in a locale file.</p>
9703          */
9704         decimalSeparator: '.',
9705
9706         /**
9707          * @type Number
9708          * @property currencyPrecision
9709          * <p>The number of decimal places that the {@link #currency} function displays.</p>
9710          * <p>This defaults to <code>2</code>, but may be overridden in a locale file.</p>
9711          */
9712         currencyPrecision: 2,
9713
9714         /**
9715          * @type String
9716          * @property currencySign
9717          * <p>The currency sign that the {@link #currency} function displays.</p>
9718          * <p>This defaults to <code>$</code>, but may be overridden in a locale file.</p>
9719          */
9720         currencySign: '$',
9721
9722         /**
9723          * @type Boolean
9724          * @property currencyAtEnd
9725          * <p>This may be set to <code>true</code> to make the {@link #currency} function
9726          * append the currency sign to the formatted value.</p>
9727          * <p>This defaults to <code>false</code>, but may be overridden in a locale file.</p>
9728          */
9729         currencyAtEnd: false,
9730
9731         /**
9732          * Checks a reference and converts it to empty string if it is undefined
9733          * @param {Mixed} value Reference to check
9734          * @return {Mixed} Empty string if converted, otherwise the original value
9735          */
9736         undef : function(value) {
9737             return value !== undefined ? value : "";
9738         },
9739
9740         /**
9741          * Checks a reference and converts it to the default value if it's empty
9742          * @param {Mixed} value Reference to check
9743          * @param {String} defaultValue The value to insert of it's undefined (defaults to "")
9744          * @return {String}
9745          */
9746         defaultValue : function(value, defaultValue) {
9747             return value !== undefined && value !== '' ? value : defaultValue;
9748         },
9749
9750         /**
9751          * Returns a substring from within an original string
9752          * @param {String} value The original text
9753          * @param {Number} start The start index of the substring
9754          * @param {Number} length The length of the substring
9755          * @return {String} The substring
9756          */
9757         substr : function(value, start, length) {
9758             return String(value).substr(start, length);
9759         },
9760
9761         /**
9762          * Converts a string to all lower case letters
9763          * @param {String} value The text to convert
9764          * @return {String} The converted text
9765          */
9766         lowercase : function(value) {
9767             return String(value).toLowerCase();
9768         },
9769
9770         /**
9771          * Converts a string to all upper case letters
9772          * @param {String} value The text to convert
9773          * @return {String} The converted text
9774          */
9775         uppercase : function(value) {
9776             return String(value).toUpperCase();
9777         },
9778
9779         /**
9780          * Format a number as US currency
9781          * @param {Number/String} value The numeric value to format
9782          * @return {String} The formatted currency string
9783          */
9784         usMoney : function(v) {
9785             return UtilFormat.currency(v, '$', 2);
9786         },
9787
9788         /**
9789          * Format a number as a currency
9790          * @param {Number/String} value The numeric value to format
9791          * @param {String} sign The currency sign to use (defaults to {@link #currencySign})
9792          * @param {Number} decimals The number of decimals to use for the currency (defaults to {@link #currencyPrecision})
9793          * @param {Boolean} end True if the currency sign should be at the end of the string (defaults to {@link #currencyAtEnd})
9794          * @return {String} The formatted currency string
9795          */
9796         currency: function(v, currencySign, decimals, end) {
9797             var negativeSign = '',
9798                 format = ",0",
9799                 i = 0;
9800             v = v - 0;
9801             if (v < 0) {
9802                 v = -v;
9803                 negativeSign = '-';
9804             }
9805             decimals = decimals || UtilFormat.currencyPrecision;
9806             format += format + (decimals > 0 ? '.' : '');
9807             for (; i < decimals; i++) {
9808                 format += '0';
9809             }
9810             v = UtilFormat.number(v, format); 
9811             if ((end || UtilFormat.currencyAtEnd) === true) {
9812                 return Ext.String.format("{0}{1}{2}", negativeSign, v, currencySign || UtilFormat.currencySign);
9813             } else {
9814                 return Ext.String.format("{0}{1}{2}", negativeSign, currencySign || UtilFormat.currencySign, v);
9815             }
9816         },
9817
9818         /**
9819          * Formats the passed date using the specified format pattern.
9820          * @param {String/Date} value The value to format. If a string is passed, it is converted to a Date by the Javascript
9821          * Date object's <a href="http://www.w3schools.com/jsref/jsref_parse.asp">parse()</a> method.
9822          * @param {String} format (Optional) Any valid date format string. Defaults to {@link Ext.Date#defaultFormat}.
9823          * @return {String} The formatted date string.
9824          */
9825         date: function(v, format) {
9826             if (!v) {
9827                 return "";
9828             }
9829             if (!Ext.isDate(v)) {
9830                 v = new Date(Date.parse(v));
9831             }
9832             return Ext.Date.dateFormat(v, format || Ext.Date.defaultFormat);
9833         },
9834
9835         /**
9836          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
9837          * @param {String} format Any valid date format string. Defaults to {@link Ext.Date#defaultFormat}.
9838          * @return {Function} The date formatting function
9839          */
9840         dateRenderer : function(format) {
9841             return function(v) {
9842                 return UtilFormat.date(v, format);
9843             };
9844         },
9845
9846         /**
9847          * Strips all HTML tags
9848          * @param {Mixed} value The text from which to strip tags
9849          * @return {String} The stripped text
9850          */
9851         stripTags : function(v) {
9852             return !v ? v : String(v).replace(stripTagsRE, "");
9853         },
9854
9855         /**
9856          * Strips all script tags
9857          * @param {Mixed} value The text from which to strip script tags
9858          * @return {String} The stripped text
9859          */
9860         stripScripts : function(v) {
9861             return !v ? v : String(v).replace(stripScriptsRe, "");
9862         },
9863
9864         /**
9865          * Simple format for a file size (xxx bytes, xxx KB, xxx MB)
9866          * @param {Number/String} size The numeric value to format
9867          * @return {String} The formatted file size
9868          */
9869         fileSize : function(size) {
9870             if (size < 1024) {
9871                 return size + " bytes";
9872             } else if (size < 1048576) {
9873                 return (Math.round(((size*10) / 1024))/10) + " KB";
9874             } else {
9875                 return (Math.round(((size*10) / 1048576))/10) + " MB";
9876             }
9877         },
9878
9879         /**
9880          * It does simple math for use in a template, for example:<pre><code>
9881          * var tpl = new Ext.Template('{value} * 10 = {value:math("* 10")}');
9882          * </code></pre>
9883          * @return {Function} A function that operates on the passed value.
9884          * @method
9885          */
9886         math : function(){
9887             var fns = {};
9888
9889             return function(v, a){
9890                 if (!fns[a]) {
9891                     fns[a] = Ext.functionFactory('v', 'return v ' + a + ';');
9892                 }
9893                 return fns[a](v);
9894             };
9895         }(),
9896
9897         /**
9898          * Rounds the passed number to the required decimal precision.
9899          * @param {Number/String} value The numeric value to round.
9900          * @param {Number} precision The number of decimal places to which to round the first parameter's value.
9901          * @return {Number} The rounded value.
9902          */
9903         round : function(value, precision) {
9904             var result = Number(value);
9905             if (typeof precision == 'number') {
9906                 precision = Math.pow(10, precision);
9907                 result = Math.round(value * precision) / precision;
9908             }
9909             return result;
9910         },
9911
9912         /**
9913          * <p>Formats the passed number according to the passed format string.</p>
9914          * <p>The number of digits after the decimal separator character specifies the number of
9915          * decimal places in the resulting string. The <u>local-specific</u> decimal character is used in the result.</p>
9916          * <p>The <i>presence</i> of a thousand separator character in the format string specifies that
9917          * the <u>locale-specific</u> thousand separator (if any) is inserted separating thousand groups.</p>
9918          * <p>By default, "," is expected as the thousand separator, and "." is expected as the decimal separator.</p>
9919          * <p><b>New to Ext4</b></p>
9920          * <p>Locale-specific characters are always used in the formatted output when inserting
9921          * thousand and decimal separators.</p>
9922          * <p>The format string must specify separator characters according to US/UK conventions ("," as the
9923          * thousand separator, and "." as the decimal separator)</p>
9924          * <p>To allow specification of format strings according to local conventions for separator characters, add
9925          * the string <code>/i</code> to the end of the format string.</p>
9926          * <div style="margin-left:40px">examples (123456.789):
9927          * <div style="margin-left:10px">
9928          * 0 - (123456) show only digits, no precision<br>
9929          * 0.00 - (123456.78) show only digits, 2 precision<br>
9930          * 0.0000 - (123456.7890) show only digits, 4 precision<br>
9931          * 0,000 - (123,456) show comma and digits, no precision<br>
9932          * 0,000.00 - (123,456.78) show comma and digits, 2 precision<br>
9933          * 0,0.00 - (123,456.78) shortcut method, show comma and digits, 2 precision<br>
9934          * To allow specification of the formatting string using UK/US grouping characters (,) and decimal (.) for international numbers, add /i to the end.
9935          * For example: 0.000,00/i
9936          * </div></div>
9937          * @param {Number} v The number to format.
9938          * @param {String} format The way you would like to format this text.
9939          * @return {String} The formatted number.
9940          */
9941         number: function(v, formatString) {
9942             if (!formatString) {
9943                 return v;
9944             }
9945             v = Ext.Number.from(v, NaN);
9946             if (isNaN(v)) {
9947                 return '';
9948             }
9949             var comma = UtilFormat.thousandSeparator,
9950                 dec   = UtilFormat.decimalSeparator,
9951                 i18n  = false,
9952                 neg   = v < 0,
9953                 hasComma,
9954                 psplit;
9955
9956             v = Math.abs(v);
9957
9958             // The "/i" suffix allows caller to use a locale-specific formatting string.
9959             // Clean the format string by removing all but numerals and the decimal separator.
9960             // Then split the format string into pre and post decimal segments according to *what* the
9961             // decimal separator is. If they are specifying "/i", they are using the local convention in the format string.
9962             if (formatString.substr(formatString.length - 2) == '/i') {
9963                 if (!I18NFormatCleanRe) {
9964                     I18NFormatCleanRe = new RegExp('[^\\d\\' + UtilFormat.decimalSeparator + ']','g');
9965                 }
9966                 formatString = formatString.substr(0, formatString.length - 2);
9967                 i18n   = true;
9968                 hasComma = formatString.indexOf(comma) != -1;
9969                 psplit = formatString.replace(I18NFormatCleanRe, '').split(dec);
9970             } else {
9971                 hasComma = formatString.indexOf(',') != -1;
9972                 psplit = formatString.replace(formatCleanRe, '').split('.');
9973             }
9974
9975             if (1 < psplit.length) {
9976                 v = v.toFixed(psplit[1].length);
9977             } else if(2 < psplit.length) {
9978                 Ext.Error.raise({
9979                     sourceClass: "Ext.util.Format",
9980                     sourceMethod: "number",
9981                     value: v,
9982                     formatString: formatString,
9983                     msg: "Invalid number format, should have no more than 1 decimal"
9984                 });
9985             } else {
9986                 v = v.toFixed(0);
9987             }
9988
9989             var fnum = v.toString();
9990
9991             psplit = fnum.split('.');
9992
9993             if (hasComma) {
9994                 var cnum = psplit[0],
9995                     parr = [],
9996                     j    = cnum.length,
9997                     m    = Math.floor(j / 3),
9998                     n    = cnum.length % 3 || 3,
9999                     i;
10000
10001                 for (i = 0; i < j; i += n) {
10002                     if (i !== 0) {
10003                         n = 3;
10004                     }
10005
10006                     parr[parr.length] = cnum.substr(i, n);
10007                     m -= 1;
10008                 }
10009                 fnum = parr.join(comma);
10010                 if (psplit[1]) {
10011                     fnum += dec + psplit[1];
10012                 }
10013             } else {
10014                 if (psplit[1]) {
10015                     fnum = psplit[0] + dec + psplit[1];
10016                 }
10017             }
10018             
10019             if (neg) {
10020                 /*
10021                  * Edge case. If we have a very small negative number it will get rounded to 0,
10022                  * however the initial check at the top will still report as negative. Replace
10023                  * everything but 1-9 and check if the string is empty to determine a 0 value.
10024                  */
10025                 neg = fnum.replace(/[^1-9]/g, '') !== '';
10026             }
10027
10028             return (neg ? '-' : '') + formatString.replace(/[\d,?\.?]+/, fnum);
10029         },
10030
10031         /**
10032          * Returns a number rendering function that can be reused to apply a number format multiple times efficiently
10033          * @param {String} format Any valid number format string for {@link #number}
10034          * @return {Function} The number formatting function
10035          */
10036         numberRenderer : function(format) {
10037             return function(v) {
10038                 return UtilFormat.number(v, format);
10039             };
10040         },
10041
10042         /**
10043          * Selectively do a plural form of a word based on a numeric value. For example, in a template,
10044          * {commentCount:plural("Comment")}  would result in "1 Comment" if commentCount was 1 or would be "x Comments"
10045          * if the value is 0 or greater than 1.
10046          * @param {Number} value The value to compare against
10047          * @param {String} singular The singular form of the word
10048          * @param {String} plural (optional) The plural form of the word (defaults to the singular with an "s")
10049          */
10050         plural : function(v, s, p) {
10051             return v +' ' + (v == 1 ? s : (p ? p : s+'s'));
10052         },
10053
10054         /**
10055          * Converts newline characters to the HTML tag &lt;br/>
10056          * @param {String} The string value to format.
10057          * @return {String} The string with embedded &lt;br/> tags in place of newlines.
10058          */
10059         nl2br : function(v) {
10060             return Ext.isEmpty(v) ? '' : v.replace(nl2brRe, '<br/>');
10061         },
10062
10063         /**
10064          * Capitalize the given string. See {@link Ext.String#capitalize}.
10065          * @method
10066          */
10067         capitalize: Ext.String.capitalize,
10068
10069         /**
10070          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length.
10071          * See {@link Ext.String#ellipsis}.
10072          * @method
10073          */
10074         ellipsis: Ext.String.ellipsis,
10075
10076         /**
10077          * Formats to a string. See {@link Ext.String#format}
10078          * @method
10079          */
10080         format: Ext.String.format,
10081
10082         /**
10083          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
10084          * See {@link Ext.String#htmlDecode}.
10085          * @method
10086          */
10087         htmlDecode: Ext.String.htmlDecode,
10088
10089         /**
10090          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
10091          * See {@link Ext.String#htmlEncode}.
10092          * @method
10093          */
10094         htmlEncode: Ext.String.htmlEncode,
10095
10096         /**
10097          * Adds left padding to a string. See {@link Ext.String#leftPad}
10098          * @method
10099          */
10100         leftPad: Ext.String.leftPad,
10101
10102         /**
10103          * Trims any whitespace from either side of a string. See {@link Ext.String#trim}.
10104          * @method
10105          */
10106         trim : Ext.String.trim,
10107
10108         /**
10109          * Parses a number or string representing margin sizes into an object. Supports CSS-style margin declarations
10110          * (e.g. 10, "10", "10 10", "10 10 10" and "10 10 10 10" are all valid options and would return the same result)
10111          * @param {Number|String} v The encoded margins
10112          * @return {Object} An object with margin sizes for top, right, bottom and left
10113          */
10114         parseBox : function(box) {
10115             if (Ext.isNumber(box)) {
10116                 box = box.toString();
10117             }
10118             var parts  = box.split(' '),
10119                 ln = parts.length;
10120
10121             if (ln == 1) {
10122                 parts[1] = parts[2] = parts[3] = parts[0];
10123             }
10124             else if (ln == 2) {
10125                 parts[2] = parts[0];
10126                 parts[3] = parts[1];
10127             }
10128             else if (ln == 3) {
10129                 parts[3] = parts[1];
10130             }
10131
10132             return {
10133                 top   :parseInt(parts[0], 10) || 0,
10134                 right :parseInt(parts[1], 10) || 0,
10135                 bottom:parseInt(parts[2], 10) || 0,
10136                 left  :parseInt(parts[3], 10) || 0
10137             };
10138         },
10139
10140         /**
10141          * Escapes the passed string for use in a regular expression
10142          * @param {String} str
10143          * @return {String}
10144          */
10145         escapeRegex : function(s) {
10146             return s.replace(/([\-.*+?\^${}()|\[\]\/\\])/g, "\\$1");
10147         }
10148     });
10149 })();
10150
10151 /**
10152  * @class Ext.util.TaskRunner
10153  * Provides the ability to execute one or more arbitrary tasks in a multithreaded
10154  * manner.  Generally, you can use the singleton {@link Ext.TaskManager} instead, but
10155  * if needed, you can create separate instances of TaskRunner.  Any number of
10156  * separate tasks can be started at any time and will run independently of each
10157  * other. Example usage:
10158  * <pre><code>
10159 // Start a simple clock task that updates a div once per second
10160 var updateClock = function(){
10161     Ext.fly('clock').update(new Date().format('g:i:s A'));
10162
10163 var task = {
10164     run: updateClock,
10165     interval: 1000 //1 second
10166 }
10167 var runner = new Ext.util.TaskRunner();
10168 runner.start(task);
10169
10170 // equivalent using TaskManager
10171 Ext.TaskManager.start({
10172     run: updateClock,
10173     interval: 1000
10174 });
10175
10176  * </code></pre>
10177  * <p>See the {@link #start} method for details about how to configure a task object.</p>
10178  * Also see {@link Ext.util.DelayedTask}. 
10179  * 
10180  * @constructor
10181  * @param {Number} interval (optional) The minimum precision in milliseconds supported by this TaskRunner instance
10182  * (defaults to 10)
10183  */
10184 Ext.ns('Ext.util');
10185
10186 Ext.util.TaskRunner = function(interval) {
10187     interval = interval || 10;
10188     var tasks = [],
10189     removeQueue = [],
10190     id = 0,
10191     running = false,
10192
10193     // private
10194     stopThread = function() {
10195         running = false;
10196         clearInterval(id);
10197         id = 0;
10198     },
10199
10200     // private
10201     startThread = function() {
10202         if (!running) {
10203             running = true;
10204             id = setInterval(runTasks, interval);
10205         }
10206     },
10207
10208     // private
10209     removeTask = function(t) {
10210         removeQueue.push(t);
10211         if (t.onStop) {
10212             t.onStop.apply(t.scope || t);
10213         }
10214     },
10215
10216     // private
10217     runTasks = function() {
10218         var rqLen = removeQueue.length,
10219             now = new Date().getTime(),
10220             i;
10221
10222         if (rqLen > 0) {
10223             for (i = 0; i < rqLen; i++) {
10224                 Ext.Array.remove(tasks, removeQueue[i]);
10225             }
10226             removeQueue = [];
10227             if (tasks.length < 1) {
10228                 stopThread();
10229                 return;
10230             }
10231         }
10232         i = 0;
10233         var t,
10234             itime,
10235             rt,
10236             len = tasks.length;
10237         for (; i < len; ++i) {
10238             t = tasks[i];
10239             itime = now - t.taskRunTime;
10240             if (t.interval <= itime) {
10241                 rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
10242                 t.taskRunTime = now;
10243                 if (rt === false || t.taskRunCount === t.repeat) {
10244                     removeTask(t);
10245                     return;
10246                 }
10247             }
10248             if (t.duration && t.duration <= (now - t.taskStartTime)) {
10249                 removeTask(t);
10250             }
10251         }
10252     };
10253
10254     /**
10255      * Starts a new task.
10256      * @method start
10257      * @param {Object} task <p>A config object that supports the following properties:<ul>
10258      * <li><code>run</code> : Function<div class="sub-desc"><p>The function to execute each time the task is invoked. The
10259      * function will be called at each interval and passed the <code>args</code> argument if specified, and the
10260      * current invocation count if not.</p>
10261      * <p>If a particular scope (<code>this</code> reference) is required, be sure to specify it using the <code>scope</code> argument.</p>
10262      * <p>Return <code>false</code> from this function to terminate the task.</p></div></li>
10263      * <li><code>interval</code> : Number<div class="sub-desc">The frequency in milliseconds with which the task
10264      * should be invoked.</div></li>
10265      * <li><code>args</code> : Array<div class="sub-desc">(optional) An array of arguments to be passed to the function
10266      * specified by <code>run</code>. If not specified, the current invocation count is passed.</div></li>
10267      * <li><code>scope</code> : Object<div class="sub-desc">(optional) The scope (<tt>this</tt> reference) in which to execute the
10268      * <code>run</code> function. Defaults to the task config object.</div></li>
10269      * <li><code>duration</code> : Number<div class="sub-desc">(optional) The length of time in milliseconds to invoke
10270      * the task before stopping automatically (defaults to indefinite).</div></li>
10271      * <li><code>repeat</code> : Number<div class="sub-desc">(optional) The number of times to invoke the task before
10272      * stopping automatically (defaults to indefinite).</div></li>
10273      * </ul></p>
10274      * <p>Before each invocation, Ext injects the property <code>taskRunCount</code> into the task object so
10275      * that calculations based on the repeat count can be performed.</p>
10276      * @return {Object} The task
10277      */
10278     this.start = function(task) {
10279         tasks.push(task);
10280         task.taskStartTime = new Date().getTime();
10281         task.taskRunTime = 0;
10282         task.taskRunCount = 0;
10283         startThread();
10284         return task;
10285     };
10286
10287     /**
10288      * Stops an existing running task.
10289      * @method stop
10290      * @param {Object} task The task to stop
10291      * @return {Object} The task
10292      */
10293     this.stop = function(task) {
10294         removeTask(task);
10295         return task;
10296     };
10297
10298     /**
10299      * Stops all tasks that are currently running.
10300      * @method stopAll
10301      */
10302     this.stopAll = function() {
10303         stopThread();
10304         for (var i = 0, len = tasks.length; i < len; i++) {
10305             if (tasks[i].onStop) {
10306                 tasks[i].onStop();
10307             }
10308         }
10309         tasks = [];
10310         removeQueue = [];
10311     };
10312 };
10313
10314 /**
10315  * @class Ext.TaskManager
10316  * @extends Ext.util.TaskRunner
10317  * A static {@link Ext.util.TaskRunner} instance that can be used to start and stop arbitrary tasks.  See
10318  * {@link Ext.util.TaskRunner} for supported methods and task config properties.
10319  * <pre><code>
10320 // Start a simple clock task that updates a div once per second
10321 var task = {
10322     run: function(){
10323         Ext.fly('clock').update(new Date().format('g:i:s A'));
10324     },
10325     interval: 1000 //1 second
10326 }
10327 Ext.TaskManager.start(task);
10328 </code></pre>
10329  * <p>See the {@link #start} method for details about how to configure a task object.</p>
10330  * @singleton
10331  */
10332 Ext.TaskManager = Ext.create('Ext.util.TaskRunner');
10333 /**
10334  * @class Ext.is
10335  * 
10336  * Determines information about the current platform the application is running on.
10337  * 
10338  * @singleton
10339  */
10340 Ext.is = {
10341     init : function(navigator) {
10342         var platforms = this.platforms,
10343             ln = platforms.length,
10344             i, platform;
10345
10346         navigator = navigator || window.navigator;
10347
10348         for (i = 0; i < ln; i++) {
10349             platform = platforms[i];
10350             this[platform.identity] = platform.regex.test(navigator[platform.property]);
10351         }
10352
10353         /**
10354          * @property Desktop True if the browser is running on a desktop machine
10355          * @type {Boolean}
10356          */
10357         this.Desktop = this.Mac || this.Windows || (this.Linux && !this.Android);
10358         /**
10359          * @property Tablet True if the browser is running on a tablet (iPad)
10360          */
10361         this.Tablet = this.iPad;
10362         /**
10363          * @property Phone True if the browser is running on a phone.
10364          * @type {Boolean}
10365          */
10366         this.Phone = !this.Desktop && !this.Tablet;
10367         /**
10368          * @property iOS True if the browser is running on iOS
10369          * @type {Boolean}
10370          */
10371         this.iOS = this.iPhone || this.iPad || this.iPod;
10372         
10373         /**
10374          * @property Standalone Detects when application has been saved to homescreen.
10375          * @type {Boolean}
10376          */
10377         this.Standalone = !!window.navigator.standalone;
10378     },
10379     
10380     /**
10381      * @property iPhone True when the browser is running on a iPhone
10382      * @type {Boolean}
10383      */
10384     platforms: [{
10385         property: 'platform',
10386         regex: /iPhone/i,
10387         identity: 'iPhone'
10388     },
10389     
10390     /**
10391      * @property iPod True when the browser is running on a iPod
10392      * @type {Boolean}
10393      */
10394     {
10395         property: 'platform',
10396         regex: /iPod/i,
10397         identity: 'iPod'
10398     },
10399     
10400     /**
10401      * @property iPad True when the browser is running on a iPad
10402      * @type {Boolean}
10403      */
10404     {
10405         property: 'userAgent',
10406         regex: /iPad/i,
10407         identity: 'iPad'
10408     },
10409     
10410     /**
10411      * @property Blackberry True when the browser is running on a Blackberry
10412      * @type {Boolean}
10413      */
10414     {
10415         property: 'userAgent',
10416         regex: /Blackberry/i,
10417         identity: 'Blackberry'
10418     },
10419     
10420     /**
10421      * @property Android True when the browser is running on an Android device
10422      * @type {Boolean}
10423      */
10424     {
10425         property: 'userAgent',
10426         regex: /Android/i,
10427         identity: 'Android'
10428     },
10429     
10430     /**
10431      * @property Mac True when the browser is running on a Mac
10432      * @type {Boolean}
10433      */
10434     {
10435         property: 'platform',
10436         regex: /Mac/i,
10437         identity: 'Mac'
10438     },
10439     
10440     /**
10441      * @property Windows True when the browser is running on Windows
10442      * @type {Boolean}
10443      */
10444     {
10445         property: 'platform',
10446         regex: /Win/i,
10447         identity: 'Windows'
10448     },
10449     
10450     /**
10451      * @property Linux True when the browser is running on Linux
10452      * @type {Boolean}
10453      */
10454     {
10455         property: 'platform',
10456         regex: /Linux/i,
10457         identity: 'Linux'
10458     }]
10459 };
10460
10461 Ext.is.init();
10462
10463 /**
10464  * @class Ext.supports
10465  *
10466  * Determines information about features are supported in the current environment
10467  * 
10468  * @singleton
10469  */
10470 Ext.supports = {
10471     init : function() {
10472         var doc = document,
10473             div = doc.createElement('div'),
10474             tests = this.tests,
10475             ln = tests.length,
10476             i, test;
10477
10478         div.innerHTML = [
10479             '<div style="height:30px;width:50px;">',
10480                 '<div style="height:20px;width:20px;"></div>',
10481             '</div>',
10482             '<div style="width: 200px; height: 200px; position: relative; padding: 5px;">',
10483                 '<div style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"></div>',
10484             '</div>',
10485             '<div style="float:left; background-color:transparent;"></div>'
10486         ].join('');
10487
10488         doc.body.appendChild(div);
10489
10490         for (i = 0; i < ln; i++) {
10491             test = tests[i];
10492             this[test.identity] = test.fn.call(this, doc, div);
10493         }
10494
10495         doc.body.removeChild(div);
10496     },
10497
10498     /**
10499      * @property CSS3BoxShadow True if document environment supports the CSS3 box-shadow style.
10500      * @type {Boolean}
10501      */
10502     CSS3BoxShadow: Ext.isDefined(document.documentElement.style.boxShadow),
10503
10504     /**
10505      * @property ClassList True if document environment supports the HTML5 classList API.
10506      * @type {Boolean}
10507      */
10508     ClassList: !!document.documentElement.classList,
10509
10510     /**
10511      * @property OrientationChange True if the device supports orientation change
10512      * @type {Boolean}
10513      */
10514     OrientationChange: ((typeof window.orientation != 'undefined') && ('onorientationchange' in window)),
10515     
10516     /**
10517      * @property DeviceMotion True if the device supports device motion (acceleration and rotation rate)
10518      * @type {Boolean}
10519      */
10520     DeviceMotion: ('ondevicemotion' in window),
10521     
10522     /**
10523      * @property Touch True if the device supports touch
10524      * @type {Boolean}
10525      */
10526     // is.Desktop is needed due to the bug in Chrome 5.0.375, Safari 3.1.2
10527     // and Safari 4.0 (they all have 'ontouchstart' in the window object).
10528     Touch: ('ontouchstart' in window) && (!Ext.is.Desktop),
10529
10530     tests: [
10531         /**
10532          * @property Transitions True if the device supports CSS3 Transitions
10533          * @type {Boolean}
10534          */
10535         {
10536             identity: 'Transitions',
10537             fn: function(doc, div) {
10538                 var prefix = [
10539                         'webkit',
10540                         'Moz',
10541                         'o',
10542                         'ms',
10543                         'khtml'
10544                     ],
10545                     TE = 'TransitionEnd',
10546                     transitionEndName = [
10547                         prefix[0] + TE,
10548                         'transitionend', //Moz bucks the prefixing convention
10549                         prefix[2] + TE,
10550                         prefix[3] + TE,
10551                         prefix[4] + TE
10552                     ],
10553                     ln = prefix.length,
10554                     i = 0,
10555                     out = false;
10556                 div = Ext.get(div);
10557                 for (; i < ln; i++) {
10558                     if (div.getStyle(prefix[i] + "TransitionProperty")) {
10559                         Ext.supports.CSS3Prefix = prefix[i];
10560                         Ext.supports.CSS3TransitionEnd = transitionEndName[i];
10561                         out = true;
10562                         break;
10563                     }
10564                 }
10565                 return out;
10566             }
10567         },
10568         
10569         /**
10570          * @property RightMargin True if the device supports right margin.
10571          * See https://bugs.webkit.org/show_bug.cgi?id=13343 for why this is needed.
10572          * @type {Boolean}
10573          */
10574         {
10575             identity: 'RightMargin',
10576             fn: function(doc, div) {
10577                 var view = doc.defaultView;
10578                 return !(view && view.getComputedStyle(div.firstChild.firstChild, null).marginRight != '0px');
10579             }
10580         },
10581
10582         /**
10583          * @property DisplayChangeInputSelectionBug True if INPUT elements lose their
10584          * selection when their display style is changed. Essentially, if a text input
10585          * has focus and its display style is changed, the I-beam disappears.
10586          * 
10587          * This bug is encountered due to the work around in place for the {@link #RightMargin}
10588          * bug. This has been observed in Safari 4.0.4 and older, and appears to be fixed
10589          * in Safari 5. It's not clear if Safari 4.1 has the bug, but it has the same WebKit
10590          * version number as Safari 5 (according to http://unixpapa.com/js/gecko.html).
10591          */
10592         {
10593             identity: 'DisplayChangeInputSelectionBug',
10594             fn: function() {
10595                 var webKitVersion = Ext.webKitVersion;
10596                 // WebKit but older than Safari 5 or Chrome 6:
10597                 return 0 < webKitVersion && webKitVersion < 533;
10598             }
10599         },
10600
10601         /**
10602          * @property DisplayChangeTextAreaSelectionBug True if TEXTAREA elements lose their
10603          * selection when their display style is changed. Essentially, if a text area has
10604          * focus and its display style is changed, the I-beam disappears.
10605          *
10606          * This bug is encountered due to the work around in place for the {@link #RightMargin}
10607          * bug. This has been observed in Chrome 10 and Safari 5 and older, and appears to
10608          * be fixed in Chrome 11.
10609          */
10610         {
10611             identity: 'DisplayChangeTextAreaSelectionBug',
10612             fn: function() {
10613                 var webKitVersion = Ext.webKitVersion;
10614
10615                 /*
10616                 Has bug w/textarea:
10617
10618                 (Chrome) Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; en-US)
10619                             AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.127
10620                             Safari/534.16
10621                 (Safari) Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; en-us)
10622                             AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5
10623                             Safari/533.21.1
10624
10625                 No bug:
10626
10627                 (Chrome) Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_7)
10628                             AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.57
10629                             Safari/534.24
10630                 */
10631                 return 0 < webKitVersion && webKitVersion < 534.24;
10632             }
10633         },
10634
10635         /**
10636          * @property TransparentColor True if the device supports transparent color
10637          * @type {Boolean}
10638          */
10639         {
10640             identity: 'TransparentColor',
10641             fn: function(doc, div, view) {
10642                 view = doc.defaultView;
10643                 return !(view && view.getComputedStyle(div.lastChild, null).backgroundColor != 'transparent');
10644             }
10645         },
10646
10647         /**
10648          * @property ComputedStyle True if the browser supports document.defaultView.getComputedStyle()
10649          * @type {Boolean}
10650          */
10651         {
10652             identity: 'ComputedStyle',
10653             fn: function(doc, div, view) {
10654                 view = doc.defaultView;
10655                 return view && view.getComputedStyle;
10656             }
10657         },
10658         
10659         /**
10660          * @property SVG True if the device supports SVG
10661          * @type {Boolean}
10662          */
10663         {
10664             identity: 'Svg',
10665             fn: function(doc) {
10666                 return !!doc.createElementNS && !!doc.createElementNS( "http:/" + "/www.w3.org/2000/svg", "svg").createSVGRect;
10667             }
10668         },
10669     
10670         /**
10671          * @property Canvas True if the device supports Canvas
10672          * @type {Boolean}
10673          */
10674         {
10675             identity: 'Canvas',
10676             fn: function(doc) {
10677                 return !!doc.createElement('canvas').getContext;
10678             }
10679         },
10680         
10681         /**
10682          * @property VML True if the device supports VML
10683          * @type {Boolean}
10684          */
10685         {
10686             identity: 'Vml',
10687             fn: function(doc) {
10688                 var d = doc.createElement("div");
10689                 d.innerHTML = "<!--[if vml]><br><br><![endif]-->";
10690                 return (d.childNodes.length == 2);
10691             }
10692         },
10693         
10694         /**
10695          * @property Float True if the device supports CSS float
10696          * @type {Boolean}
10697          */
10698         {
10699             identity: 'Float',
10700             fn: function(doc, div) {
10701                 return !!div.lastChild.style.cssFloat;
10702             }
10703         },
10704         
10705         /**
10706          * @property AudioTag True if the device supports the HTML5 audio tag
10707          * @type {Boolean}
10708          */
10709         {
10710             identity: 'AudioTag',
10711             fn: function(doc) {
10712                 return !!doc.createElement('audio').canPlayType;
10713             }
10714         },
10715         
10716         /**
10717          * @property History True if the device supports HTML5 history
10718          * @type {Boolean}
10719          */
10720         {
10721             identity: 'History',
10722             fn: function() {
10723                 return !!(window.history && history.pushState);
10724             }
10725         },
10726         
10727         /**
10728          * @property CSS3DTransform True if the device supports CSS3DTransform
10729          * @type {Boolean}
10730          */
10731         {
10732             identity: 'CSS3DTransform',
10733             fn: function() {
10734                 return (typeof WebKitCSSMatrix != 'undefined' && new WebKitCSSMatrix().hasOwnProperty('m41'));
10735             }
10736         },
10737
10738                 /**
10739          * @property CSS3LinearGradient True if the device supports CSS3 linear gradients
10740          * @type {Boolean}
10741          */
10742         {
10743             identity: 'CSS3LinearGradient',
10744             fn: function(doc, div) {
10745                 var property = 'background-image:',
10746                     webkit   = '-webkit-gradient(linear, left top, right bottom, from(black), to(white))',
10747                     w3c      = 'linear-gradient(left top, black, white)',
10748                     moz      = '-moz-' + w3c,
10749                     options  = [property + webkit, property + w3c, property + moz];
10750                 
10751                 div.style.cssText = options.join(';');
10752                 
10753                 return ("" + div.style.backgroundImage).indexOf('gradient') !== -1;
10754             }
10755         },
10756         
10757         /**
10758          * @property CSS3BorderRadius True if the device supports CSS3 border radius
10759          * @type {Boolean}
10760          */
10761         {
10762             identity: 'CSS3BorderRadius',
10763             fn: function(doc, div) {
10764                 var domPrefixes = ['borderRadius', 'BorderRadius', 'MozBorderRadius', 'WebkitBorderRadius', 'OBorderRadius', 'KhtmlBorderRadius'],
10765                     pass = false,
10766                     i;
10767                 for (i = 0; i < domPrefixes.length; i++) {
10768                     if (document.body.style[domPrefixes[i]] !== undefined) {
10769                         return true;
10770                     }
10771                 }
10772                 return pass;
10773             }
10774         },
10775         
10776         /**
10777          * @property GeoLocation True if the device supports GeoLocation
10778          * @type {Boolean}
10779          */
10780         {
10781             identity: 'GeoLocation',
10782             fn: function() {
10783                 return (typeof navigator != 'undefined' && typeof navigator.geolocation != 'undefined') || (typeof google != 'undefined' && typeof google.gears != 'undefined');
10784             }
10785         },
10786         /**
10787          * @property MouseEnterLeave True if the browser supports mouseenter and mouseleave events
10788          * @type {Boolean}
10789          */
10790         {
10791             identity: 'MouseEnterLeave',
10792             fn: function(doc, div){
10793                 return ('onmouseenter' in div && 'onmouseleave' in div);
10794             }
10795         },
10796         /**
10797          * @property MouseWheel True if the browser supports the mousewheel event
10798          * @type {Boolean}
10799          */
10800         {
10801             identity: 'MouseWheel',
10802             fn: function(doc, div) {
10803                 return ('onmousewheel' in div);
10804             }
10805         },
10806         /**
10807          * @property Opacity True if the browser supports normal css opacity
10808          * @type {Boolean}
10809          */
10810         {
10811             identity: 'Opacity',
10812             fn: function(doc, div){
10813                 // Not a strict equal comparison in case opacity can be converted to a number.
10814                 if (Ext.isIE6 || Ext.isIE7 || Ext.isIE8) {
10815                     return false;
10816                 }
10817                 div.firstChild.style.cssText = 'opacity:0.73';
10818                 return div.firstChild.style.opacity == '0.73';
10819             }
10820         },
10821         /**
10822          * @property Placeholder True if the browser supports the HTML5 placeholder attribute on inputs
10823          * @type {Boolean}
10824          */
10825         {
10826             identity: 'Placeholder',
10827             fn: function(doc) {
10828                 return 'placeholder' in doc.createElement('input');
10829             }
10830         },
10831         
10832         /**
10833          * @property Direct2DBug True if when asking for an element's dimension via offsetWidth or offsetHeight, 
10834          * getBoundingClientRect, etc. the browser returns the subpixel width rounded to the nearest pixel.
10835          * @type {Boolean}
10836          */
10837         {
10838             identity: 'Direct2DBug',
10839             fn: function() {
10840                 return Ext.isString(document.body.style.msTransformOrigin);
10841             }
10842         },
10843         /**
10844          * @property BoundingClientRect True if the browser supports the getBoundingClientRect method on elements
10845          * @type {Boolean}
10846          */
10847         {
10848             identity: 'BoundingClientRect',
10849             fn: function(doc, div) {
10850                 return Ext.isFunction(div.getBoundingClientRect);
10851             }
10852         },
10853         {
10854             identity: 'IncludePaddingInWidthCalculation',
10855             fn: function(doc, div){
10856                 var el = Ext.get(div.childNodes[1].firstChild);
10857                 return el.getWidth() == 210;
10858             }
10859         },
10860         {
10861             identity: 'IncludePaddingInHeightCalculation',
10862             fn: function(doc, div){
10863                 var el = Ext.get(div.childNodes[1].firstChild);
10864                 return el.getHeight() == 210;
10865             }
10866         },
10867         
10868         /**
10869          * @property ArraySort True if the Array sort native method isn't bugged.
10870          * @type {Boolean}
10871          */
10872         {
10873             identity: 'ArraySort',
10874             fn: function() {
10875                 var a = [1,2,3,4,5].sort(function(){ return 0; });
10876                 return a[0] === 1 && a[1] === 2 && a[2] === 3 && a[3] === 4 && a[4] === 5;
10877             }
10878         },
10879         /**
10880          * @property Range True if browser support document.createRange native method.
10881          * @type {Boolean}
10882          */
10883         {
10884             identity: 'Range',
10885             fn: function() {
10886                 return !!document.createRange;
10887             }
10888         },
10889         /**
10890          * @property CreateContextualFragment True if browser support CreateContextualFragment range native methods.
10891          * @type {Boolean}
10892          */
10893         {
10894             identity: 'CreateContextualFragment',
10895             fn: function() {
10896                 var range = Ext.supports.Range ? document.createRange() : false;
10897                 
10898                 return range && !!range.createContextualFragment;
10899             }
10900         },
10901
10902         /**
10903          * @property WindowOnError True if browser supports window.onerror.
10904          * @type {Boolean}
10905          */
10906         {
10907             identity: 'WindowOnError',
10908             fn: function () {
10909                 // sadly, we cannot feature detect this...
10910                 return Ext.isIE || Ext.isGecko || Ext.webKitVersion >= 534.16; // Chrome 10+
10911             }
10912         }
10913     ]
10914 };
10915
10916
10917
10918 /*
10919
10920 This file is part of Ext JS 4
10921
10922 Copyright (c) 2011 Sencha Inc
10923
10924 Contact:  http://www.sencha.com/contact
10925
10926 GNU General Public License Usage
10927 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.
10928
10929 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
10930
10931 */
10932 /**
10933  * @class Ext.core.DomHelper
10934  * <p>The DomHelper class provides a layer of abstraction from DOM and transparently supports creating
10935  * elements via DOM or using HTML fragments. It also has the ability to create HTML fragment templates
10936  * from your DOM building code.</p>
10937  *
10938  * <p><b><u>DomHelper element specification object</u></b></p>
10939  * <p>A specification object is used when creating elements. Attributes of this object
10940  * are assumed to be element attributes, except for 4 special attributes:
10941  * <div class="mdetail-params"><ul>
10942  * <li><b><tt>tag</tt></b> : <div class="sub-desc">The tag name of the element</div></li>
10943  * <li><b><tt>children</tt></b> : or <tt>cn</tt><div class="sub-desc">An array of the
10944  * same kind of element definition objects to be created and appended. These can be nested
10945  * as deep as you want.</div></li>
10946  * <li><b><tt>cls</tt></b> : <div class="sub-desc">The class attribute of the element.
10947  * This will end up being either the "class" attribute on a HTML fragment or className
10948  * for a DOM node, depending on whether DomHelper is using fragments or DOM.</div></li>
10949  * <li><b><tt>html</tt></b> : <div class="sub-desc">The innerHTML for the element</div></li>
10950  * </ul></div></p>
10951  * <p><b>NOTE:</b> For other arbitrary attributes, the value will currently <b>not</b> be automatically
10952  * HTML-escaped prior to building the element's HTML string. This means that if your attribute value
10953  * contains special characters that would not normally be allowed in a double-quoted attribute value,
10954  * you <b>must</b> manually HTML-encode it beforehand (see {@link Ext.String#htmlEncode}) or risk
10955  * malformed HTML being created. This behavior may change in a future release.</p>
10956  *
10957  * <p><b><u>Insertion methods</u></b></p>
10958  * <p>Commonly used insertion methods:
10959  * <div class="mdetail-params"><ul>
10960  * <li><b><tt>{@link #append}</tt></b> : <div class="sub-desc"></div></li>
10961  * <li><b><tt>{@link #insertBefore}</tt></b> : <div class="sub-desc"></div></li>
10962  * <li><b><tt>{@link #insertAfter}</tt></b> : <div class="sub-desc"></div></li>
10963  * <li><b><tt>{@link #overwrite}</tt></b> : <div class="sub-desc"></div></li>
10964  * <li><b><tt>{@link #createTemplate}</tt></b> : <div class="sub-desc"></div></li>
10965  * <li><b><tt>{@link #insertHtml}</tt></b> : <div class="sub-desc"></div></li>
10966  * </ul></div></p>
10967  *
10968  * <p><b><u>Example</u></b></p>
10969  * <p>This is an example, where an unordered list with 3 children items is appended to an existing
10970  * element with id <tt>'my-div'</tt>:<br>
10971  <pre><code>
10972 var dh = Ext.core.DomHelper; // create shorthand alias
10973 // specification object
10974 var spec = {
10975     id: 'my-ul',
10976     tag: 'ul',
10977     cls: 'my-list',
10978     // append children after creating
10979     children: [     // may also specify 'cn' instead of 'children'
10980         {tag: 'li', id: 'item0', html: 'List Item 0'},
10981         {tag: 'li', id: 'item1', html: 'List Item 1'},
10982         {tag: 'li', id: 'item2', html: 'List Item 2'}
10983     ]
10984 };
10985 var list = dh.append(
10986     'my-div', // the context element 'my-div' can either be the id or the actual node
10987     spec      // the specification object
10988 );
10989  </code></pre></p>
10990  * <p>Element creation specification parameters in this class may also be passed as an Array of
10991  * specification objects. This can be used to insert multiple sibling nodes into an existing
10992  * container very efficiently. For example, to add more list items to the example above:<pre><code>
10993 dh.append('my-ul', [
10994     {tag: 'li', id: 'item3', html: 'List Item 3'},
10995     {tag: 'li', id: 'item4', html: 'List Item 4'}
10996 ]);
10997  * </code></pre></p>
10998  *
10999  * <p><b><u>Templating</u></b></p>
11000  * <p>The real power is in the built-in templating. Instead of creating or appending any elements,
11001  * <tt>{@link #createTemplate}</tt> returns a Template object which can be used over and over to
11002  * insert new elements. Revisiting the example above, we could utilize templating this time:
11003  * <pre><code>
11004 // create the node
11005 var list = dh.append('my-div', {tag: 'ul', cls: 'my-list'});
11006 // get template
11007 var tpl = dh.createTemplate({tag: 'li', id: 'item{0}', html: 'List Item {0}'});
11008
11009 for(var i = 0; i < 5, i++){
11010     tpl.append(list, [i]); // use template to append to the actual node
11011 }
11012  * </code></pre></p>
11013  * <p>An example using a template:<pre><code>
11014 var html = '<a id="{0}" href="{1}" class="nav">{2}</a>';
11015
11016 var tpl = new Ext.core.DomHelper.createTemplate(html);
11017 tpl.append('blog-roll', ['link1', 'http://www.edspencer.net/', "Ed&#39;s Site"]);
11018 tpl.append('blog-roll', ['link2', 'http://www.dustindiaz.com/', "Dustin&#39;s Site"]);
11019  * </code></pre></p>
11020  *
11021  * <p>The same example using named parameters:<pre><code>
11022 var html = '<a id="{id}" href="{url}" class="nav">{text}</a>';
11023
11024 var tpl = new Ext.core.DomHelper.createTemplate(html);
11025 tpl.append('blog-roll', {
11026     id: 'link1',
11027     url: 'http://www.edspencer.net/',
11028     text: "Ed&#39;s Site"
11029 });
11030 tpl.append('blog-roll', {
11031     id: 'link2',
11032     url: 'http://www.dustindiaz.com/',
11033     text: "Dustin&#39;s Site"
11034 });
11035  * </code></pre></p>
11036  *
11037  * <p><b><u>Compiling Templates</u></b></p>
11038  * <p>Templates are applied using regular expressions. The performance is great, but if
11039  * you are adding a bunch of DOM elements using the same template, you can increase
11040  * performance even further by {@link Ext.Template#compile "compiling"} the template.
11041  * The way "{@link Ext.Template#compile compile()}" works is the template is parsed and
11042  * broken up at the different variable points and a dynamic function is created and eval'ed.
11043  * The generated function performs string concatenation of these parts and the passed
11044  * variables instead of using regular expressions.
11045  * <pre><code>
11046 var html = '<a id="{id}" href="{url}" class="nav">{text}</a>';
11047
11048 var tpl = new Ext.core.DomHelper.createTemplate(html);
11049 tpl.compile();
11050
11051 //... use template like normal
11052  * </code></pre></p>
11053  *
11054  * <p><b><u>Performance Boost</u></b></p>
11055  * <p>DomHelper will transparently create HTML fragments when it can. Using HTML fragments instead
11056  * of DOM can significantly boost performance.</p>
11057  * <p>Element creation specification parameters may also be strings. If {@link #useDom} is <tt>false</tt>,
11058  * then the string is used as innerHTML. If {@link #useDom} is <tt>true</tt>, a string specification
11059  * results in the creation of a text node. Usage:</p>
11060  * <pre><code>
11061 Ext.core.DomHelper.useDom = true; // force it to use DOM; reduces performance
11062  * </code></pre>
11063  * @singleton
11064  */
11065 Ext.ns('Ext.core');
11066 Ext.core.DomHelper = function(){
11067     var tempTableEl = null,
11068         emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i,
11069         tableRe = /^table|tbody|tr|td$/i,
11070         confRe = /tag|children|cn|html$/i,
11071         tableElRe = /td|tr|tbody/i,
11072         endRe = /end/i,
11073         pub,
11074         // kill repeat to save bytes
11075         afterbegin = 'afterbegin',
11076         afterend = 'afterend',
11077         beforebegin = 'beforebegin',
11078         beforeend = 'beforeend',
11079         ts = '<table>',
11080         te = '</table>',
11081         tbs = ts+'<tbody>',
11082         tbe = '</tbody>'+te,
11083         trs = tbs + '<tr>',
11084         tre = '</tr>'+tbe;
11085
11086     // private
11087     function doInsert(el, o, returnElement, pos, sibling, append){
11088         el = Ext.getDom(el);
11089         var newNode;
11090         if (pub.useDom) {
11091             newNode = createDom(o, null);
11092             if (append) {
11093                 el.appendChild(newNode);
11094             } else {
11095                 (sibling == 'firstChild' ? el : el.parentNode).insertBefore(newNode, el[sibling] || el);
11096             }
11097         } else {
11098             newNode = Ext.core.DomHelper.insertHtml(pos, el, Ext.core.DomHelper.createHtml(o));
11099         }
11100         return returnElement ? Ext.get(newNode, true) : newNode;
11101     }
11102     
11103     function createDom(o, parentNode){
11104         var el,
11105             doc = document,
11106             useSet,
11107             attr,
11108             val,
11109             cn;
11110
11111         if (Ext.isArray(o)) {                       // Allow Arrays of siblings to be inserted
11112             el = doc.createDocumentFragment(); // in one shot using a DocumentFragment
11113             for (var i = 0, l = o.length; i < l; i++) {
11114                 createDom(o[i], el);
11115             }
11116         } else if (typeof o == 'string') {         // Allow a string as a child spec.
11117             el = doc.createTextNode(o);
11118         } else {
11119             el = doc.createElement( o.tag || 'div' );
11120             useSet = !!el.setAttribute; // In IE some elements don't have setAttribute
11121             for (attr in o) {
11122                 if(!confRe.test(attr)){
11123                     val = o[attr];
11124                     if(attr == 'cls'){
11125                         el.className = val;
11126                     }else{
11127                         if(useSet){
11128                             el.setAttribute(attr, val);
11129                         }else{
11130                             el[attr] = val;
11131                         }
11132                     }
11133                 }
11134             }
11135             Ext.core.DomHelper.applyStyles(el, o.style);
11136
11137             if ((cn = o.children || o.cn)) {
11138                 createDom(cn, el);
11139             } else if (o.html) {
11140                 el.innerHTML = o.html;
11141             }
11142         }
11143         if(parentNode){
11144            parentNode.appendChild(el);
11145         }
11146         return el;
11147     }
11148
11149     // build as innerHTML where available
11150     function createHtml(o){
11151         var b = '',
11152             attr,
11153             val,
11154             key,
11155             cn,
11156             i;
11157
11158         if(typeof o == "string"){
11159             b = o;
11160         } else if (Ext.isArray(o)) {
11161             for (i=0; i < o.length; i++) {
11162                 if(o[i]) {
11163                     b += createHtml(o[i]);
11164                 }
11165             }
11166         } else {
11167             b += '<' + (o.tag = o.tag || 'div');
11168             for (attr in o) {
11169                 val = o[attr];
11170                 if(!confRe.test(attr)){
11171                     if (typeof val == "object") {
11172                         b += ' ' + attr + '="';
11173                         for (key in val) {
11174                             b += key + ':' + val[key] + ';';
11175                         }
11176                         b += '"';
11177                     }else{
11178                         b += ' ' + ({cls : 'class', htmlFor : 'for'}[attr] || attr) + '="' + val + '"';
11179                     }
11180                 }
11181             }
11182             // Now either just close the tag or try to add children and close the tag.
11183             if (emptyTags.test(o.tag)) {
11184                 b += '/>';
11185             } else {
11186                 b += '>';
11187                 if ((cn = o.children || o.cn)) {
11188                     b += createHtml(cn);
11189                 } else if(o.html){
11190                     b += o.html;
11191                 }
11192                 b += '</' + o.tag + '>';
11193             }
11194         }
11195         return b;
11196     }
11197
11198     function ieTable(depth, s, h, e){
11199         tempTableEl.innerHTML = [s, h, e].join('');
11200         var i = -1,
11201             el = tempTableEl,
11202             ns;
11203         while(++i < depth){
11204             el = el.firstChild;
11205         }
11206 //      If the result is multiple siblings, then encapsulate them into one fragment.
11207         ns = el.nextSibling;
11208         if (ns){
11209             var df = document.createDocumentFragment();
11210             while(el){
11211                 ns = el.nextSibling;
11212                 df.appendChild(el);
11213                 el = ns;
11214             }
11215             el = df;
11216         }
11217         return el;
11218     }
11219
11220     /**
11221      * @ignore
11222      * Nasty code for IE's broken table implementation
11223      */
11224     function insertIntoTable(tag, where, el, html) {
11225         var node,
11226             before;
11227
11228         tempTableEl = tempTableEl || document.createElement('div');
11229
11230         if(tag == 'td' && (where == afterbegin || where == beforeend) ||
11231            !tableElRe.test(tag) && (where == beforebegin || where == afterend)) {
11232             return null;
11233         }
11234         before = where == beforebegin ? el :
11235                  where == afterend ? el.nextSibling :
11236                  where == afterbegin ? el.firstChild : null;
11237
11238         if (where == beforebegin || where == afterend) {
11239             el = el.parentNode;
11240         }
11241
11242         if (tag == 'td' || (tag == 'tr' && (where == beforeend || where == afterbegin))) {
11243             node = ieTable(4, trs, html, tre);
11244         } else if ((tag == 'tbody' && (where == beforeend || where == afterbegin)) ||
11245                    (tag == 'tr' && (where == beforebegin || where == afterend))) {
11246             node = ieTable(3, tbs, html, tbe);
11247         } else {
11248             node = ieTable(2, ts, html, te);
11249         }
11250         el.insertBefore(node, before);
11251         return node;
11252     }
11253     
11254     /**
11255      * @ignore
11256      * Fix for IE9 createContextualFragment missing method
11257      */   
11258     function createContextualFragment(html){
11259         var div = document.createElement("div"),
11260             fragment = document.createDocumentFragment(),
11261             i = 0,
11262             length, childNodes;
11263         
11264         div.innerHTML = html;
11265         childNodes = div.childNodes;
11266         length = childNodes.length;
11267
11268         for (; i < length; i++) {
11269             fragment.appendChild(childNodes[i].cloneNode(true));
11270         }
11271
11272         return fragment;
11273     }
11274     
11275     pub = {
11276         /**
11277          * Returns the markup for the passed Element(s) config.
11278          * @param {Object} o The DOM object spec (and children)
11279          * @return {String}
11280          */
11281         markup : function(o){
11282             return createHtml(o);
11283         },
11284
11285         /**
11286          * Applies a style specification to an element.
11287          * @param {String/HTMLElement} el The element to apply styles to
11288          * @param {String/Object/Function} styles A style specification string e.g. 'width:100px', or object in the form {width:'100px'}, or
11289          * a function which returns such a specification.
11290          */
11291         applyStyles : function(el, styles){
11292             if (styles) {
11293                 el = Ext.fly(el);
11294                 if (typeof styles == "function") {
11295                     styles = styles.call();
11296                 }
11297                 if (typeof styles == "string") {
11298                     styles = Ext.core.Element.parseStyles(styles);
11299                 }
11300                 if (typeof styles == "object") {
11301                     el.setStyle(styles);
11302                 }
11303             }
11304         },
11305
11306         /**
11307          * Inserts an HTML fragment into the DOM.
11308          * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
11309          * @param {HTMLElement/TextNode} el The context element
11310          * @param {String} html The HTML fragment
11311          * @return {HTMLElement} The new node
11312          */
11313         insertHtml : function(where, el, html){
11314             var hash = {},
11315                 hashVal,
11316                 range,
11317                 rangeEl,
11318                 setStart,
11319                 frag,
11320                 rs;
11321
11322             where = where.toLowerCase();
11323             // add these here because they are used in both branches of the condition.
11324             hash[beforebegin] = ['BeforeBegin', 'previousSibling'];
11325             hash[afterend] = ['AfterEnd', 'nextSibling'];
11326             
11327             // if IE and context element is an HTMLElement
11328             if (el.insertAdjacentHTML) {
11329                 if(tableRe.test(el.tagName) && (rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html))){
11330                     return rs;
11331                 }
11332                 
11333                 // add these two to the hash.
11334                 hash[afterbegin] = ['AfterBegin', 'firstChild'];
11335                 hash[beforeend] = ['BeforeEnd', 'lastChild'];
11336                 if ((hashVal = hash[where])) {
11337                     el.insertAdjacentHTML(hashVal[0], html);
11338                     return el[hashVal[1]];
11339                 }
11340             // if (not IE and context element is an HTMLElement) or TextNode
11341             } else {
11342                 // we cannot insert anything inside a textnode so...
11343                 if (Ext.isTextNode(el)) {
11344                     where = where === 'afterbegin' ? 'beforebegin' : where; 
11345                     where = where === 'beforeend' ? 'afterend' : where;
11346                 }
11347                 range = Ext.supports.CreateContextualFragment ? el.ownerDocument.createRange() : undefined;
11348                 setStart = 'setStart' + (endRe.test(where) ? 'After' : 'Before');
11349                 if (hash[where]) {
11350                     if (range) {
11351                         range[setStart](el);
11352                         frag = range.createContextualFragment(html);
11353                     } else {
11354                         frag = createContextualFragment(html);
11355                     }
11356                     el.parentNode.insertBefore(frag, where == beforebegin ? el : el.nextSibling);
11357                     return el[(where == beforebegin ? 'previous' : 'next') + 'Sibling'];
11358                 } else {
11359                     rangeEl = (where == afterbegin ? 'first' : 'last') + 'Child';
11360                     if (el.firstChild) {
11361                         if (range) {
11362                             range[setStart](el[rangeEl]);
11363                             frag = range.createContextualFragment(html);
11364                         } else {
11365                             frag = createContextualFragment(html);
11366                         }
11367                         
11368                         if(where == afterbegin){
11369                             el.insertBefore(frag, el.firstChild);
11370                         }else{
11371                             el.appendChild(frag);
11372                         }
11373                     } else {
11374                         el.innerHTML = html;
11375                     }
11376                     return el[rangeEl];
11377                 }
11378             }
11379             Ext.Error.raise({
11380                 sourceClass: 'Ext.core.DomHelper',
11381                 sourceMethod: 'insertHtml',
11382                 htmlToInsert: html,
11383                 targetElement: el,
11384                 msg: 'Illegal insertion point reached: "' + where + '"'
11385             });
11386         },
11387
11388         /**
11389          * Creates new DOM element(s) and inserts them before el.
11390          * @param {Mixed} el The context element
11391          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
11392          * @param {Boolean} returnElement (optional) true to return a Ext.core.Element
11393          * @return {HTMLElement/Ext.core.Element} The new node
11394          */
11395         insertBefore : function(el, o, returnElement){
11396             return doInsert(el, o, returnElement, beforebegin);
11397         },
11398
11399         /**
11400          * Creates new DOM element(s) and inserts them after el.
11401          * @param {Mixed} el The context element
11402          * @param {Object} o The DOM object spec (and children)
11403          * @param {Boolean} returnElement (optional) true to return a Ext.core.Element
11404          * @return {HTMLElement/Ext.core.Element} The new node
11405          */
11406         insertAfter : function(el, o, returnElement){
11407             return doInsert(el, o, returnElement, afterend, 'nextSibling');
11408         },
11409
11410         /**
11411          * Creates new DOM element(s) and inserts them as the first child of el.
11412          * @param {Mixed} el The context element
11413          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
11414          * @param {Boolean} returnElement (optional) true to return a Ext.core.Element
11415          * @return {HTMLElement/Ext.core.Element} The new node
11416          */
11417         insertFirst : function(el, o, returnElement){
11418             return doInsert(el, o, returnElement, afterbegin, 'firstChild');
11419         },
11420
11421         /**
11422          * Creates new DOM element(s) and appends them to el.
11423          * @param {Mixed} el The context element
11424          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
11425          * @param {Boolean} returnElement (optional) true to return a Ext.core.Element
11426          * @return {HTMLElement/Ext.core.Element} The new node
11427          */
11428         append : function(el, o, returnElement){
11429             return doInsert(el, o, returnElement, beforeend, '', true);
11430         },
11431
11432         /**
11433          * Creates new DOM element(s) and overwrites the contents of el with them.
11434          * @param {Mixed} el The context element
11435          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
11436          * @param {Boolean} returnElement (optional) true to return a Ext.core.Element
11437          * @return {HTMLElement/Ext.core.Element} The new node
11438          */
11439         overwrite : function(el, o, returnElement){
11440             el = Ext.getDom(el);
11441             el.innerHTML = createHtml(o);
11442             return returnElement ? Ext.get(el.firstChild) : el.firstChild;
11443         },
11444
11445         createHtml : createHtml,
11446         
11447         /**
11448          * Creates new DOM element(s) without inserting them to the document.
11449          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
11450          * @return {HTMLElement} The new uninserted node
11451          * @method
11452          */
11453         createDom: createDom,
11454         
11455         /** True to force the use of DOM instead of html fragments @type Boolean */
11456         useDom : false,
11457         
11458         /**
11459          * Creates a new Ext.Template from the DOM object spec.
11460          * @param {Object} o The DOM object spec (and children)
11461          * @return {Ext.Template} The new template
11462          */
11463         createTemplate : function(o){
11464             var html = Ext.core.DomHelper.createHtml(o);
11465             return Ext.create('Ext.Template', html);
11466         }
11467     };
11468     return pub;
11469 }();
11470
11471 /*
11472  * This is code is also distributed under MIT license for use
11473  * with jQuery and prototype JavaScript libraries.
11474  */
11475 /**
11476  * @class Ext.DomQuery
11477 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).
11478 <p>
11479 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>
11480
11481 <p>
11482 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.
11483 </p>
11484 <h4>Element Selectors:</h4>
11485 <ul class="list">
11486     <li> <b>*</b> any element</li>
11487     <li> <b>E</b> an element with the tag E</li>
11488     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
11489     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
11490     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
11491     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
11492 </ul>
11493 <h4>Attribute Selectors:</h4>
11494 <p>The use of &#64; and quotes are optional. For example, div[&#64;foo='bar'] is also a valid attribute selector.</p>
11495 <ul class="list">
11496     <li> <b>E[foo]</b> has an attribute "foo"</li>
11497     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
11498     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
11499     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
11500     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
11501     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
11502     <li> <b>E[foo!=bar]</b> attribute "foo" does not equal "bar"</li>
11503 </ul>
11504 <h4>Pseudo Classes:</h4>
11505 <ul class="list">
11506     <li> <b>E:first-child</b> E is the first child of its parent</li>
11507     <li> <b>E:last-child</b> E is the last child of its parent</li>
11508     <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>
11509     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
11510     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
11511     <li> <b>E:only-child</b> E is the only child of its parent</li>
11512     <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>
11513     <li> <b>E:first</b> the first E in the resultset</li>
11514     <li> <b>E:last</b> the last E in the resultset</li>
11515     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
11516     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
11517     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
11518     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
11519     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
11520     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
11521     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
11522     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
11523     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
11524     <li> <b>E:any(S1|S2|S2)</b> an E element which matches any of the simple selectors S1, S2 or S3//\\</li>
11525 </ul>
11526 <h4>CSS Value Selectors:</h4>
11527 <ul class="list">
11528     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
11529     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
11530     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
11531     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
11532     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
11533     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
11534 </ul>
11535  * @singleton
11536  */
11537 Ext.ns('Ext.core');
11538
11539 Ext.core.DomQuery = Ext.DomQuery = function(){
11540     var cache = {},
11541         simpleCache = {},
11542         valueCache = {},
11543         nonSpace = /\S/,
11544         trimRe = /^\s+|\s+$/g,
11545         tplRe = /\{(\d+)\}/g,
11546         modeRe = /^(\s?[\/>+~]\s?|\s|$)/,
11547         tagTokenRe = /^(#)?([\w-\*]+)/,
11548         nthRe = /(\d*)n\+?(\d*)/,
11549         nthRe2 = /\D/,
11550         // This is for IE MSXML which does not support expandos.
11551     // IE runs the same speed using setAttribute, however FF slows way down
11552     // and Safari completely fails so they need to continue to use expandos.
11553     isIE = window.ActiveXObject ? true : false,
11554     key = 30803;
11555
11556     // this eval is stop the compressor from
11557     // renaming the variable to something shorter
11558     eval("var batch = 30803;");
11559
11560     // Retrieve the child node from a particular
11561     // parent at the specified index.
11562     function child(parent, index){
11563         var i = 0,
11564             n = parent.firstChild;
11565         while(n){
11566             if(n.nodeType == 1){
11567                if(++i == index){
11568                    return n;
11569                }
11570             }
11571             n = n.nextSibling;
11572         }
11573         return null;
11574     }
11575
11576     // retrieve the next element node
11577     function next(n){
11578         while((n = n.nextSibling) && n.nodeType != 1);
11579         return n;
11580     }
11581
11582     // retrieve the previous element node
11583     function prev(n){
11584         while((n = n.previousSibling) && n.nodeType != 1);
11585         return n;
11586     }
11587
11588     // Mark each child node with a nodeIndex skipping and
11589     // removing empty text nodes.
11590     function children(parent){
11591         var n = parent.firstChild,
11592         nodeIndex = -1,
11593         nextNode;
11594         while(n){
11595             nextNode = n.nextSibling;
11596             // clean worthless empty nodes.
11597             if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
11598             parent.removeChild(n);
11599             }else{
11600             // add an expando nodeIndex
11601             n.nodeIndex = ++nodeIndex;
11602             }
11603             n = nextNode;
11604         }
11605         return this;
11606     }
11607
11608
11609     // nodeSet - array of nodes
11610     // cls - CSS Class
11611     function byClassName(nodeSet, cls){
11612         if(!cls){
11613             return nodeSet;
11614         }
11615         var result = [], ri = -1;
11616         for(var i = 0, ci; ci = nodeSet[i]; i++){
11617             if((' '+ci.className+' ').indexOf(cls) != -1){
11618                 result[++ri] = ci;
11619             }
11620         }
11621         return result;
11622     };
11623
11624     function attrValue(n, attr){
11625         // if its an array, use the first node.
11626         if(!n.tagName && typeof n.length != "undefined"){
11627             n = n[0];
11628         }
11629         if(!n){
11630             return null;
11631         }
11632
11633         if(attr == "for"){
11634             return n.htmlFor;
11635         }
11636         if(attr == "class" || attr == "className"){
11637             return n.className;
11638         }
11639         return n.getAttribute(attr) || n[attr];
11640
11641     };
11642
11643
11644     // ns - nodes
11645     // mode - false, /, >, +, ~
11646     // tagName - defaults to "*"
11647     function getNodes(ns, mode, tagName){
11648         var result = [], ri = -1, cs;
11649         if(!ns){
11650             return result;
11651         }
11652         tagName = tagName || "*";
11653         // convert to array
11654         if(typeof ns.getElementsByTagName != "undefined"){
11655             ns = [ns];
11656         }
11657
11658         // no mode specified, grab all elements by tagName
11659         // at any depth
11660         if(!mode){
11661             for(var i = 0, ni; ni = ns[i]; i++){
11662                 cs = ni.getElementsByTagName(tagName);
11663                 for(var j = 0, ci; ci = cs[j]; j++){
11664                     result[++ri] = ci;
11665                 }
11666             }
11667         // Direct Child mode (/ or >)
11668         // E > F or E/F all direct children elements of E that have the tag
11669         } else if(mode == "/" || mode == ">"){
11670             var utag = tagName.toUpperCase();
11671             for(var i = 0, ni, cn; ni = ns[i]; i++){
11672                 cn = ni.childNodes;
11673                 for(var j = 0, cj; cj = cn[j]; j++){
11674                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
11675                         result[++ri] = cj;
11676                     }
11677                 }
11678             }
11679         // Immediately Preceding mode (+)
11680         // E + F all elements with the tag F that are immediately preceded by an element with the tag E
11681         }else if(mode == "+"){
11682             var utag = tagName.toUpperCase();
11683             for(var i = 0, n; n = ns[i]; i++){
11684                 while((n = n.nextSibling) && n.nodeType != 1);
11685                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
11686                     result[++ri] = n;
11687                 }
11688             }
11689         // Sibling mode (~)
11690         // E ~ F all elements with the tag F that are preceded by a sibling element with the tag E
11691         }else if(mode == "~"){
11692             var utag = tagName.toUpperCase();
11693             for(var i = 0, n; n = ns[i]; i++){
11694                 while((n = n.nextSibling)){
11695                     if (n.nodeName == utag || n.nodeName == tagName || tagName == '*'){
11696                         result[++ri] = n;
11697                     }
11698                 }
11699             }
11700         }
11701         return result;
11702     }
11703
11704     function concat(a, b){
11705         if(b.slice){
11706             return a.concat(b);
11707         }
11708         for(var i = 0, l = b.length; i < l; i++){
11709             a[a.length] = b[i];
11710         }
11711         return a;
11712     }
11713
11714     function byTag(cs, tagName){
11715         if(cs.tagName || cs == document){
11716             cs = [cs];
11717         }
11718         if(!tagName){
11719             return cs;
11720         }
11721         var result = [], ri = -1;
11722         tagName = tagName.toLowerCase();
11723         for(var i = 0, ci; ci = cs[i]; i++){
11724             if(ci.nodeType == 1 && ci.tagName.toLowerCase() == tagName){
11725                 result[++ri] = ci;
11726             }
11727         }
11728         return result;
11729     }
11730
11731     function byId(cs, id){
11732         if(cs.tagName || cs == document){
11733             cs = [cs];
11734         }
11735         if(!id){
11736             return cs;
11737         }
11738         var result = [], ri = -1;
11739         for(var i = 0, ci; ci = cs[i]; i++){
11740             if(ci && ci.id == id){
11741                 result[++ri] = ci;
11742                 return result;
11743             }
11744         }
11745         return result;
11746     }
11747
11748     // operators are =, !=, ^=, $=, *=, %=, |= and ~=
11749     // custom can be "{"
11750     function byAttribute(cs, attr, value, op, custom){
11751         var result = [],
11752             ri = -1,
11753             useGetStyle = custom == "{",
11754             fn = Ext.DomQuery.operators[op],
11755             a,
11756             xml,
11757             hasXml;
11758
11759         for(var i = 0, ci; ci = cs[i]; i++){
11760             // skip non-element nodes.
11761             if(ci.nodeType != 1){
11762                 continue;
11763             }
11764             // only need to do this for the first node
11765             if(!hasXml){
11766                 xml = Ext.DomQuery.isXml(ci);
11767                 hasXml = true;
11768             }
11769
11770             // we only need to change the property names if we're dealing with html nodes, not XML
11771             if(!xml){
11772                 if(useGetStyle){
11773                     a = Ext.DomQuery.getStyle(ci, attr);
11774                 } else if (attr == "class" || attr == "className"){
11775                     a = ci.className;
11776                 } else if (attr == "for"){
11777                     a = ci.htmlFor;
11778                 } else if (attr == "href"){
11779                     // getAttribute href bug
11780                     // http://www.glennjones.net/Post/809/getAttributehrefbug.htm
11781                     a = ci.getAttribute("href", 2);
11782                 } else{
11783                     a = ci.getAttribute(attr);
11784                 }
11785             }else{
11786                 a = ci.getAttribute(attr);
11787             }
11788             if((fn && fn(a, value)) || (!fn && a)){
11789                 result[++ri] = ci;
11790             }
11791         }
11792         return result;
11793     }
11794
11795     function byPseudo(cs, name, value){
11796         return Ext.DomQuery.pseudos[name](cs, value);
11797     }
11798
11799     function nodupIEXml(cs){
11800         var d = ++key,
11801             r;
11802         cs[0].setAttribute("_nodup", d);
11803         r = [cs[0]];
11804         for(var i = 1, len = cs.length; i < len; i++){
11805             var c = cs[i];
11806             if(!c.getAttribute("_nodup") != d){
11807                 c.setAttribute("_nodup", d);
11808                 r[r.length] = c;
11809             }
11810         }
11811         for(var i = 0, len = cs.length; i < len; i++){
11812             cs[i].removeAttribute("_nodup");
11813         }
11814         return r;
11815     }
11816
11817     function nodup(cs){
11818         if(!cs){
11819             return [];
11820         }
11821         var len = cs.length, c, i, r = cs, cj, ri = -1;
11822         if(!len || typeof cs.nodeType != "undefined" || len == 1){
11823             return cs;
11824         }
11825         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
11826             return nodupIEXml(cs);
11827         }
11828         var d = ++key;
11829         cs[0]._nodup = d;
11830         for(i = 1; c = cs[i]; i++){
11831             if(c._nodup != d){
11832                 c._nodup = d;
11833             }else{
11834                 r = [];
11835                 for(var j = 0; j < i; j++){
11836                     r[++ri] = cs[j];
11837                 }
11838                 for(j = i+1; cj = cs[j]; j++){
11839                     if(cj._nodup != d){
11840                         cj._nodup = d;
11841                         r[++ri] = cj;
11842                     }
11843                 }
11844                 return r;
11845             }
11846         }
11847         return r;
11848     }
11849
11850     function quickDiffIEXml(c1, c2){
11851         var d = ++key,
11852             r = [];
11853         for(var i = 0, len = c1.length; i < len; i++){
11854             c1[i].setAttribute("_qdiff", d);
11855         }
11856         for(var i = 0, len = c2.length; i < len; i++){
11857             if(c2[i].getAttribute("_qdiff") != d){
11858                 r[r.length] = c2[i];
11859             }
11860         }
11861         for(var i = 0, len = c1.length; i < len; i++){
11862            c1[i].removeAttribute("_qdiff");
11863         }
11864         return r;
11865     }
11866
11867     function quickDiff(c1, c2){
11868         var len1 = c1.length,
11869             d = ++key,
11870             r = [];
11871         if(!len1){
11872             return c2;
11873         }
11874         if(isIE && typeof c1[0].selectSingleNode != "undefined"){
11875             return quickDiffIEXml(c1, c2);
11876         }
11877         for(var i = 0; i < len1; i++){
11878             c1[i]._qdiff = d;
11879         }
11880         for(var i = 0, len = c2.length; i < len; i++){
11881             if(c2[i]._qdiff != d){
11882                 r[r.length] = c2[i];
11883             }
11884         }
11885         return r;
11886     }
11887
11888     function quickId(ns, mode, root, id){
11889         if(ns == root){
11890            var d = root.ownerDocument || root;
11891            return d.getElementById(id);
11892         }
11893         ns = getNodes(ns, mode, "*");
11894         return byId(ns, id);
11895     }
11896
11897     return {
11898         getStyle : function(el, name){
11899             return Ext.fly(el).getStyle(name);
11900         },
11901         /**
11902          * Compiles a selector/xpath query into a reusable function. The returned function
11903          * takes one parameter "root" (optional), which is the context node from where the query should start.
11904          * @param {String} selector The selector/xpath query
11905          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
11906          * @return {Function}
11907          */
11908         compile : function(path, type){
11909             type = type || "select";
11910
11911             // setup fn preamble
11912             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"],
11913                 mode,
11914                 lastPath,
11915                 matchers = Ext.DomQuery.matchers,
11916                 matchersLn = matchers.length,
11917                 modeMatch,
11918                 // accept leading mode switch
11919                 lmode = path.match(modeRe);
11920
11921             if(lmode && lmode[1]){
11922                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
11923                 path = path.replace(lmode[1], "");
11924             }
11925
11926             // strip leading slashes
11927             while(path.substr(0, 1)=="/"){
11928                 path = path.substr(1);
11929             }
11930
11931             while(path && lastPath != path){
11932                 lastPath = path;
11933                 var tokenMatch = path.match(tagTokenRe);
11934                 if(type == "select"){
11935                     if(tokenMatch){
11936                         // ID Selector
11937                         if(tokenMatch[1] == "#"){
11938                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tokenMatch[2]+'");';
11939                         }else{
11940                             fn[fn.length] = 'n = getNodes(n, mode, "'+tokenMatch[2]+'");';
11941                         }
11942                         path = path.replace(tokenMatch[0], "");
11943                     }else if(path.substr(0, 1) != '@'){
11944                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
11945                     }
11946                 // type of "simple"
11947                 }else{
11948                     if(tokenMatch){
11949                         if(tokenMatch[1] == "#"){
11950                             fn[fn.length] = 'n = byId(n, "'+tokenMatch[2]+'");';
11951                         }else{
11952                             fn[fn.length] = 'n = byTag(n, "'+tokenMatch[2]+'");';
11953                         }
11954                         path = path.replace(tokenMatch[0], "");
11955                     }
11956                 }
11957                 while(!(modeMatch = path.match(modeRe))){
11958                     var matched = false;
11959                     for(var j = 0; j < matchersLn; j++){
11960                         var t = matchers[j];
11961                         var m = path.match(t.re);
11962                         if(m){
11963                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
11964                                 return m[i];
11965                             });
11966                             path = path.replace(m[0], "");
11967                             matched = true;
11968                             break;
11969                         }
11970                     }
11971                     // prevent infinite loop on bad selector
11972                     if(!matched){
11973                         Ext.Error.raise({
11974                             sourceClass: 'Ext.DomQuery',
11975                             sourceMethod: 'compile',
11976                             msg: 'Error parsing selector. Parsing failed at "' + path + '"'
11977                         });
11978                     }
11979                 }
11980                 if(modeMatch[1]){
11981                     fn[fn.length] = 'mode="'+modeMatch[1].replace(trimRe, "")+'";';
11982                     path = path.replace(modeMatch[1], "");
11983                 }
11984             }
11985             // close fn out
11986             fn[fn.length] = "return nodup(n);\n}";
11987
11988             // eval fn and return it
11989             eval(fn.join(""));
11990             return f;
11991         },
11992
11993         /**
11994          * Selects an array of DOM nodes using JavaScript-only implementation.
11995          *
11996          * Use {@link #select} to take advantage of browsers built-in support for CSS selectors.
11997          *
11998          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
11999          * @param {Node/String} root (optional) The start of the query (defaults to document).
12000          * @return {Array} An Array of DOM elements which match the selector. If there are
12001          * no matches, and empty Array is returned.
12002          */
12003         jsSelect: function(path, root, type){
12004             // set root to doc if not specified.
12005             root = root || document;
12006
12007             if(typeof root == "string"){
12008                 root = document.getElementById(root);
12009             }
12010             var paths = path.split(","),
12011                 results = [];
12012
12013             // loop over each selector
12014             for(var i = 0, len = paths.length; i < len; i++){
12015                 var subPath = paths[i].replace(trimRe, "");
12016                 // compile and place in cache
12017                 if(!cache[subPath]){
12018                     cache[subPath] = Ext.DomQuery.compile(subPath);
12019                     if(!cache[subPath]){
12020                         Ext.Error.raise({
12021                             sourceClass: 'Ext.DomQuery',
12022                             sourceMethod: 'jsSelect',
12023                             msg: subPath + ' is not a valid selector'
12024                         });
12025                     }
12026                 }
12027                 var result = cache[subPath](root);
12028                 if(result && result != document){
12029                     results = results.concat(result);
12030                 }
12031             }
12032
12033             // if there were multiple selectors, make sure dups
12034             // are eliminated
12035             if(paths.length > 1){
12036                 return nodup(results);
12037             }
12038             return results;
12039         },
12040
12041         isXml: function(el) {
12042             var docEl = (el ? el.ownerDocument || el : 0).documentElement;
12043             return docEl ? docEl.nodeName !== "HTML" : false;
12044         },
12045
12046         /**
12047          * Selects an array of DOM nodes by CSS/XPath selector.
12048          *
12049          * Uses [document.querySelectorAll][0] if browser supports that, otherwise falls back to
12050          * {@link #jsSelect} to do the work.
12051          * 
12052          * Aliased as {@link Ext#query}.
12053          * 
12054          * [0]: https://developer.mozilla.org/en/DOM/document.querySelectorAll
12055          *
12056          * @param {String} path The selector/xpath query
12057          * @param {Node} root (optional) The start of the query (defaults to document).
12058          * @return {Array} An array of DOM elements (not a NodeList as returned by `querySelectorAll`).
12059          * Empty array when no matches.
12060          * @method
12061          */
12062         select : document.querySelectorAll ? function(path, root, type) {
12063             root = root || document;
12064             if (!Ext.DomQuery.isXml(root)) {
12065             try {
12066                 var cs = root.querySelectorAll(path);
12067                 return Ext.Array.toArray(cs);
12068             }
12069             catch (ex) {}
12070             }
12071             return Ext.DomQuery.jsSelect.call(this, path, root, type);
12072         } : function(path, root, type) {
12073             return Ext.DomQuery.jsSelect.call(this, path, root, type);
12074         },
12075
12076         /**
12077          * Selects a single element.
12078          * @param {String} selector The selector/xpath query
12079          * @param {Node} root (optional) The start of the query (defaults to document).
12080          * @return {Element} The DOM element which matched the selector.
12081          */
12082         selectNode : function(path, root){
12083             return Ext.DomQuery.select(path, root)[0];
12084         },
12085
12086         /**
12087          * Selects the value of a node, optionally replacing null with the defaultValue.
12088          * @param {String} selector The selector/xpath query
12089          * @param {Node} root (optional) The start of the query (defaults to document).
12090          * @param {String} defaultValue
12091          * @return {String}
12092          */
12093         selectValue : function(path, root, defaultValue){
12094             path = path.replace(trimRe, "");
12095             if(!valueCache[path]){
12096                 valueCache[path] = Ext.DomQuery.compile(path, "select");
12097             }
12098             var n = valueCache[path](root), v;
12099             n = n[0] ? n[0] : n;
12100
12101             // overcome a limitation of maximum textnode size
12102             // Rumored to potentially crash IE6 but has not been confirmed.
12103             // http://reference.sitepoint.com/javascript/Node/normalize
12104             // https://developer.mozilla.org/En/DOM/Node.normalize
12105             if (typeof n.normalize == 'function') n.normalize();
12106
12107             v = (n && n.firstChild ? n.firstChild.nodeValue : null);
12108             return ((v === null||v === undefined||v==='') ? defaultValue : v);
12109         },
12110
12111         /**
12112          * Selects the value of a node, parsing integers and floats. Returns the defaultValue, or 0 if none is specified.
12113          * @param {String} selector The selector/xpath query
12114          * @param {Node} root (optional) The start of the query (defaults to document).
12115          * @param {Number} defaultValue
12116          * @return {Number}
12117          */
12118         selectNumber : function(path, root, defaultValue){
12119             var v = Ext.DomQuery.selectValue(path, root, defaultValue || 0);
12120             return parseFloat(v);
12121         },
12122
12123         /**
12124          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
12125          * @param {String/HTMLElement/Array} el An element id, element or array of elements
12126          * @param {String} selector The simple selector to test
12127          * @return {Boolean}
12128          */
12129         is : function(el, ss){
12130             if(typeof el == "string"){
12131                 el = document.getElementById(el);
12132             }
12133             var isArray = Ext.isArray(el),
12134                 result = Ext.DomQuery.filter(isArray ? el : [el], ss);
12135             return isArray ? (result.length == el.length) : (result.length > 0);
12136         },
12137
12138         /**
12139          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
12140          * @param {Array} el An array of elements to filter
12141          * @param {String} selector The simple selector to test
12142          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
12143          * the selector instead of the ones that match
12144          * @return {Array} An Array of DOM elements which match the selector. If there are
12145          * no matches, and empty Array is returned.
12146          */
12147         filter : function(els, ss, nonMatches){
12148             ss = ss.replace(trimRe, "");
12149             if(!simpleCache[ss]){
12150                 simpleCache[ss] = Ext.DomQuery.compile(ss, "simple");
12151             }
12152             var result = simpleCache[ss](els);
12153             return nonMatches ? quickDiff(result, els) : result;
12154         },
12155
12156         /**
12157          * Collection of matching regular expressions and code snippets.
12158          * Each capture group within () will be replace the {} in the select
12159          * statement as specified by their index.
12160          */
12161         matchers : [{
12162                 re: /^\.([\w-]+)/,
12163                 select: 'n = byClassName(n, " {1} ");'
12164             }, {
12165                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
12166                 select: 'n = byPseudo(n, "{1}", "{2}");'
12167             },{
12168                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
12169                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
12170             }, {
12171                 re: /^#([\w-]+)/,
12172                 select: 'n = byId(n, "{1}");'
12173             },{
12174                 re: /^@([\w-]+)/,
12175                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
12176             }
12177         ],
12178
12179         /**
12180          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
12181          * 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;.
12182          */
12183         operators : {
12184             "=" : function(a, v){
12185                 return a == v;
12186             },
12187             "!=" : function(a, v){
12188                 return a != v;
12189             },
12190             "^=" : function(a, v){
12191                 return a && a.substr(0, v.length) == v;
12192             },
12193             "$=" : function(a, v){
12194                 return a && a.substr(a.length-v.length) == v;
12195             },
12196             "*=" : function(a, v){
12197                 return a && a.indexOf(v) !== -1;
12198             },
12199             "%=" : function(a, v){
12200                 return (a % v) == 0;
12201             },
12202             "|=" : function(a, v){
12203                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
12204             },
12205             "~=" : function(a, v){
12206                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
12207             }
12208         },
12209
12210         /**
12211 Object hash of "pseudo class" filter functions which are used when filtering selections. 
12212 Each function is passed two parameters:
12213
12214 - **c** : Array
12215     An Array of DOM elements to filter.
12216     
12217 - **v** : String
12218     The argument (if any) supplied in the selector.
12219
12220 A filter function returns an Array of DOM elements which conform to the pseudo class.
12221 In addition to the provided pseudo classes listed above such as `first-child` and `nth-child`,
12222 developers may add additional, custom psuedo class filters to select elements according to application-specific requirements.
12223
12224 For example, to filter `a` elements to only return links to __external__ resources:
12225
12226     Ext.DomQuery.pseudos.external = function(c, v){
12227         var r = [], ri = -1;
12228         for(var i = 0, ci; ci = c[i]; i++){
12229             // Include in result set only if it's a link to an external resource
12230             if(ci.hostname != location.hostname){
12231                 r[++ri] = ci;
12232             }
12233         }
12234         return r;
12235     };
12236
12237 Then external links could be gathered with the following statement:
12238
12239     var externalLinks = Ext.select("a:external");
12240
12241         * @markdown
12242         */
12243         pseudos : {
12244             "first-child" : function(c){
12245                 var r = [], ri = -1, n;
12246                 for(var i = 0, ci; ci = n = c[i]; i++){
12247                     while((n = n.previousSibling) && n.nodeType != 1);
12248                     if(!n){
12249                         r[++ri] = ci;
12250                     }
12251                 }
12252                 return r;
12253             },
12254
12255             "last-child" : function(c){
12256                 var r = [], ri = -1, n;
12257                 for(var i = 0, ci; ci = n = c[i]; i++){
12258                     while((n = n.nextSibling) && n.nodeType != 1);
12259                     if(!n){
12260                         r[++ri] = ci;
12261                     }
12262                 }
12263                 return r;
12264             },
12265
12266             "nth-child" : function(c, a) {
12267                 var r = [], ri = -1,
12268                     m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a),
12269                     f = (m[1] || 1) - 0, l = m[2] - 0;
12270                 for(var i = 0, n; n = c[i]; i++){
12271                     var pn = n.parentNode;
12272                     if (batch != pn._batch) {
12273                         var j = 0;
12274                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
12275                             if(cn.nodeType == 1){
12276                                cn.nodeIndex = ++j;
12277                             }
12278                         }
12279                         pn._batch = batch;
12280                     }
12281                     if (f == 1) {
12282                         if (l == 0 || n.nodeIndex == l){
12283                             r[++ri] = n;
12284                         }
12285                     } else if ((n.nodeIndex + l) % f == 0){
12286                         r[++ri] = n;
12287                     }
12288                 }
12289
12290                 return r;
12291             },
12292
12293             "only-child" : function(c){
12294                 var r = [], ri = -1;;
12295                 for(var i = 0, ci; ci = c[i]; i++){
12296                     if(!prev(ci) && !next(ci)){
12297                         r[++ri] = ci;
12298                     }
12299                 }
12300                 return r;
12301             },
12302
12303             "empty" : function(c){
12304                 var r = [], ri = -1;
12305                 for(var i = 0, ci; ci = c[i]; i++){
12306                     var cns = ci.childNodes, j = 0, cn, empty = true;
12307                     while(cn = cns[j]){
12308                         ++j;
12309                         if(cn.nodeType == 1 || cn.nodeType == 3){
12310                             empty = false;
12311                             break;
12312                         }
12313                     }
12314                     if(empty){
12315                         r[++ri] = ci;
12316                     }
12317                 }
12318                 return r;
12319             },
12320
12321             "contains" : function(c, v){
12322                 var r = [], ri = -1;
12323                 for(var i = 0, ci; ci = c[i]; i++){
12324                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
12325                         r[++ri] = ci;
12326                     }
12327                 }
12328                 return r;
12329             },
12330
12331             "nodeValue" : function(c, v){
12332                 var r = [], ri = -1;
12333                 for(var i = 0, ci; ci = c[i]; i++){
12334                     if(ci.firstChild && ci.firstChild.nodeValue == v){
12335                         r[++ri] = ci;
12336                     }
12337                 }
12338                 return r;
12339             },
12340
12341             "checked" : function(c){
12342                 var r = [], ri = -1;
12343                 for(var i = 0, ci; ci = c[i]; i++){
12344                     if(ci.checked == true){
12345                         r[++ri] = ci;
12346                     }
12347                 }
12348                 return r;
12349             },
12350
12351             "not" : function(c, ss){
12352                 return Ext.DomQuery.filter(c, ss, true);
12353             },
12354
12355             "any" : function(c, selectors){
12356                 var ss = selectors.split('|'),
12357                     r = [], ri = -1, s;
12358                 for(var i = 0, ci; ci = c[i]; i++){
12359                     for(var j = 0; s = ss[j]; j++){
12360                         if(Ext.DomQuery.is(ci, s)){
12361                             r[++ri] = ci;
12362                             break;
12363                         }
12364                     }
12365                 }
12366                 return r;
12367             },
12368
12369             "odd" : function(c){
12370                 return this["nth-child"](c, "odd");
12371             },
12372
12373             "even" : function(c){
12374                 return this["nth-child"](c, "even");
12375             },
12376
12377             "nth" : function(c, a){
12378                 return c[a-1] || [];
12379             },
12380
12381             "first" : function(c){
12382                 return c[0] || [];
12383             },
12384
12385             "last" : function(c){
12386                 return c[c.length-1] || [];
12387             },
12388
12389             "has" : function(c, ss){
12390                 var s = Ext.DomQuery.select,
12391                     r = [], ri = -1;
12392                 for(var i = 0, ci; ci = c[i]; i++){
12393                     if(s(ss, ci).length > 0){
12394                         r[++ri] = ci;
12395                     }
12396                 }
12397                 return r;
12398             },
12399
12400             "next" : function(c, ss){
12401                 var is = Ext.DomQuery.is,
12402                     r = [], ri = -1;
12403                 for(var i = 0, ci; ci = c[i]; i++){
12404                     var n = next(ci);
12405                     if(n && is(n, ss)){
12406                         r[++ri] = ci;
12407                     }
12408                 }
12409                 return r;
12410             },
12411
12412             "prev" : function(c, ss){
12413                 var is = Ext.DomQuery.is,
12414                     r = [], ri = -1;
12415                 for(var i = 0, ci; ci = c[i]; i++){
12416                     var n = prev(ci);
12417                     if(n && is(n, ss)){
12418                         r[++ri] = ci;
12419                     }
12420                 }
12421                 return r;
12422             }
12423         }
12424     };
12425 }();
12426
12427 /**
12428  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Ext.DomQuery#select}
12429  * @param {String} path The selector/xpath query
12430  * @param {Node} root (optional) The start of the query (defaults to document).
12431  * @return {Array}
12432  * @member Ext
12433  * @method query
12434  */
12435 Ext.query = Ext.DomQuery.select;
12436
12437 /**
12438  * @class Ext.core.Element
12439  * <p>Encapsulates a DOM element, adding simple DOM manipulation facilities, normalizing for browser differences.</p>
12440  * <p>All instances of this class inherit the methods of {@link Ext.fx.Anim} making visual effects easily available to all DOM elements.</p>
12441  * <p>Note that the events documented in this class are not Ext events, they encapsulate browser events. To
12442  * access the underlying browser event, see {@link Ext.EventObject#browserEvent}. Some older
12443  * browsers may not support the full range of events. Which events are supported is beyond the control of ExtJs.</p>
12444  * Usage:<br>
12445 <pre><code>
12446 // by id
12447 var el = Ext.get("my-div");
12448
12449 // by DOM element reference
12450 var el = Ext.get(myDivElement);
12451 </code></pre>
12452  * <b>Animations</b><br />
12453  * <p>When an element is manipulated, by default there is no animation.</p>
12454  * <pre><code>
12455 var el = Ext.get("my-div");
12456
12457 // no animation
12458 el.setWidth(100);
12459  * </code></pre>
12460  * <p>Many of the functions for manipulating an element have an optional "animate" parameter.  This
12461  * parameter can be specified as boolean (<tt>true</tt>) for default animation effects.</p>
12462  * <pre><code>
12463 // default animation
12464 el.setWidth(100, true);
12465  * </code></pre>
12466  *
12467  * <p>To configure the effects, an object literal with animation options to use as the Element animation
12468  * configuration object can also be specified. Note that the supported Element animation configuration
12469  * options are a subset of the {@link Ext.fx.Anim} animation options specific to Fx effects.  The supported
12470  * Element animation configuration options are:</p>
12471 <pre>
12472 Option    Default   Description
12473 --------- --------  ---------------------------------------------
12474 {@link Ext.fx.Anim#duration duration}  .35       The duration of the animation in seconds
12475 {@link Ext.fx.Anim#easing easing}    easeOut   The easing method
12476 {@link Ext.fx.Anim#callback callback}  none      A function to execute when the anim completes
12477 {@link Ext.fx.Anim#scope scope}     this      The scope (this) of the callback function
12478 </pre>
12479  *
12480  * <pre><code>
12481 // Element animation options object
12482 var opt = {
12483     {@link Ext.fx.Anim#duration duration}: 1,
12484     {@link Ext.fx.Anim#easing easing}: 'elasticIn',
12485     {@link Ext.fx.Anim#callback callback}: this.foo,
12486     {@link Ext.fx.Anim#scope scope}: this
12487 };
12488 // animation with some options set
12489 el.setWidth(100, opt);
12490  * </code></pre>
12491  * <p>The Element animation object being used for the animation will be set on the options
12492  * object as "anim", which allows you to stop or manipulate the animation. Here is an example:</p>
12493  * <pre><code>
12494 // using the "anim" property to get the Anim object
12495 if(opt.anim.isAnimated()){
12496     opt.anim.stop();
12497 }
12498  * </code></pre>
12499  * <p>Also see the <tt>{@link #animate}</tt> method for another animation technique.</p>
12500  * <p><b> Composite (Collections of) Elements</b></p>
12501  * <p>For working with collections of Elements, see {@link Ext.CompositeElement}</p>
12502  * @constructor Create a new Element directly.
12503  * @param {String/HTMLElement} element
12504  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
12505  */
12506  (function() {
12507     var DOC = document,
12508         EC = Ext.cache;
12509
12510     Ext.Element = Ext.core.Element = function(element, forceNew) {
12511         var dom = typeof element == "string" ? DOC.getElementById(element) : element,
12512         id;
12513
12514         if (!dom) {
12515             return null;
12516         }
12517
12518         id = dom.id;
12519
12520         if (!forceNew && id && EC[id]) {
12521             // element object already exists
12522             return EC[id].el;
12523         }
12524
12525         /**
12526      * The DOM element
12527      * @type HTMLElement
12528      */
12529         this.dom = dom;
12530
12531         /**
12532      * The DOM element ID
12533      * @type String
12534      */
12535         this.id = id || Ext.id(dom);
12536     };
12537
12538     var DH = Ext.core.DomHelper,
12539     El = Ext.core.Element;
12540
12541
12542     El.prototype = {
12543         /**
12544      * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
12545      * @param {Object} o The object with the attributes
12546      * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
12547      * @return {Ext.core.Element} this
12548      */
12549         set: function(o, useSet) {
12550             var el = this.dom,
12551                 attr,
12552                 val;
12553             useSet = (useSet !== false) && !!el.setAttribute;
12554
12555             for (attr in o) {
12556                 if (o.hasOwnProperty(attr)) {
12557                     val = o[attr];
12558                     if (attr == 'style') {
12559                         DH.applyStyles(el, val);
12560                     } else if (attr == 'cls') {
12561                         el.className = val;
12562                     } else if (useSet) {
12563                         el.setAttribute(attr, val);
12564                     } else {
12565                         el[attr] = val;
12566                     }
12567                 }
12568             }
12569             return this;
12570         },
12571
12572         //  Mouse events
12573         /**
12574      * @event click
12575      * Fires when a mouse click is detected within the element.
12576      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12577      * @param {HtmlElement} t The target of the event.
12578      * @param {Object} o The options configuration passed to the {@link #addListener} call.
12579      */
12580         /**
12581      * @event contextmenu
12582      * Fires when a right click is detected within the element.
12583      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12584      * @param {HtmlElement} t The target of the event.
12585      * @param {Object} o The options configuration passed to the {@link #addListener} call.
12586      */
12587         /**
12588      * @event dblclick
12589      * Fires when a mouse double click is detected within the element.
12590      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12591      * @param {HtmlElement} t The target of the event.
12592      * @param {Object} o The options configuration passed to the {@link #addListener} call.
12593      */
12594         /**
12595      * @event mousedown
12596      * Fires when a mousedown is detected within the element.
12597      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12598      * @param {HtmlElement} t The target of the event.
12599      * @param {Object} o The options configuration passed to the {@link #addListener} call.
12600      */
12601         /**
12602      * @event mouseup
12603      * Fires when a mouseup is detected within the element.
12604      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12605      * @param {HtmlElement} t The target of the event.
12606      * @param {Object} o The options configuration passed to the {@link #addListener} call.
12607      */
12608         /**
12609      * @event mouseover
12610      * Fires when a mouseover is detected within the element.
12611      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12612      * @param {HtmlElement} t The target of the event.
12613      * @param {Object} o The options configuration passed to the {@link #addListener} call.
12614      */
12615         /**
12616      * @event mousemove
12617      * Fires when a mousemove is detected with the element.
12618      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12619      * @param {HtmlElement} t The target of the event.
12620      * @param {Object} o The options configuration passed to the {@link #addListener} call.
12621      */
12622         /**
12623      * @event mouseout
12624      * Fires when a mouseout is detected with the element.
12625      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12626      * @param {HtmlElement} t The target of the event.
12627      * @param {Object} o The options configuration passed to the {@link #addListener} call.
12628      */
12629         /**
12630      * @event mouseenter
12631      * Fires when the mouse enters the element.
12632      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12633      * @param {HtmlElement} t The target of the event.
12634      * @param {Object} o The options configuration passed to the {@link #addListener} call.
12635      */
12636         /**
12637      * @event mouseleave
12638      * Fires when the mouse leaves the element.
12639      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12640      * @param {HtmlElement} t The target of the event.
12641      * @param {Object} o The options configuration passed to the {@link #addListener} call.
12642      */
12643
12644         //  Keyboard events
12645         /**
12646      * @event keypress
12647      * Fires when a keypress is detected within the element.
12648      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12649      * @param {HtmlElement} t The target of the event.
12650      * @param {Object} o The options configuration passed to the {@link #addListener} call.
12651      */
12652         /**
12653      * @event keydown
12654      * Fires when a keydown is detected within the element.
12655      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12656      * @param {HtmlElement} t The target of the event.
12657      * @param {Object} o The options configuration passed to the {@link #addListener} call.
12658      */
12659         /**
12660      * @event keyup
12661      * Fires when a keyup is detected within the element.
12662      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12663      * @param {HtmlElement} t The target of the event.
12664      * @param {Object} o The options configuration passed to the {@link #addListener} call.
12665      */
12666
12667
12668         //  HTML frame/object events
12669         /**
12670      * @event load
12671      * Fires when the user agent finishes loading all content within the element. Only supported by window, frames, objects and images.
12672      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12673      * @param {HtmlElement} t The target of the event.
12674      * @param {Object} o The options configuration passed to the {@link #addListener} call.
12675      */
12676         /**
12677      * @event unload
12678      * Fires when the user agent removes all content from a window or frame. For elements, it fires when the target element or any of its content has been removed.
12679      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12680      * @param {HtmlElement} t The target of the event.
12681      * @param {Object} o The options configuration passed to the {@link #addListener} call.
12682      */
12683         /**
12684      * @event abort
12685      * Fires when an object/image is stopped from loading before completely loaded.
12686      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12687      * @param {HtmlElement} t The target of the event.
12688      * @param {Object} o The options configuration passed to the {@link #addListener} call.
12689      */
12690         /**
12691      * @event error
12692      * Fires when an object/image/frame cannot be loaded properly.
12693      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12694      * @param {HtmlElement} t The target of the event.
12695      * @param {Object} o The options configuration passed to the {@link #addListener} call.
12696      */
12697         /**
12698      * @event resize
12699      * Fires when a document view is resized.
12700      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12701      * @param {HtmlElement} t The target of the event.
12702      * @param {Object} o The options configuration passed to the {@link #addListener} call.
12703      */
12704         /**
12705      * @event scroll
12706      * Fires when a document view is scrolled.
12707      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12708      * @param {HtmlElement} t The target of the event.
12709      * @param {Object} o The options configuration passed to the {@link #addListener} call.
12710      */
12711
12712         //  Form events
12713         /**
12714      * @event select
12715      * Fires when a user selects some text in a text field, including input and textarea.
12716      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12717      * @param {HtmlElement} t The target of the event.
12718      * @param {Object} o The options configuration passed to the {@link #addListener} call.
12719      */
12720         /**
12721      * @event change
12722      * Fires when a control loses the input focus and its value has been modified since gaining focus.
12723      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12724      * @param {HtmlElement} t The target of the event.
12725      * @param {Object} o The options configuration passed to the {@link #addListener} call.
12726      */
12727         /**
12728      * @event submit
12729      * Fires when a form is submitted.
12730      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12731      * @param {HtmlElement} t The target of the event.
12732      * @param {Object} o The options configuration passed to the {@link #addListener} call.
12733      */
12734         /**
12735      * @event reset
12736      * Fires when a form is reset.
12737      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12738      * @param {HtmlElement} t The target of the event.
12739      * @param {Object} o The options configuration passed to the {@link #addListener} call.
12740      */
12741         /**
12742      * @event focus
12743      * Fires when an element receives focus either via the pointing device or by tab navigation.
12744      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12745      * @param {HtmlElement} t The target of the event.
12746      * @param {Object} o The options configuration passed to the {@link #addListener} call.
12747      */
12748         /**
12749      * @event blur
12750      * Fires when an element loses focus either via the pointing device or by tabbing navigation.
12751      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12752      * @param {HtmlElement} t The target of the event.
12753      * @param {Object} o The options configuration passed to the {@link #addListener} call.
12754      */
12755
12756         //  User Interface events
12757         /**
12758      * @event DOMFocusIn
12759      * Where supported. Similar to HTML focus event, but can be applied to any focusable element.
12760      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12761      * @param {HtmlElement} t The target of the event.
12762      * @param {Object} o The options configuration passed to the {@link #addListener} call.
12763      */
12764         /**
12765      * @event DOMFocusOut
12766      * Where supported. Similar to HTML blur event, but can be applied to any focusable element.
12767      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12768      * @param {HtmlElement} t The target of the event.
12769      * @param {Object} o The options configuration passed to the {@link #addListener} call.
12770      */
12771         /**
12772      * @event DOMActivate
12773      * Where supported. Fires when an element is activated, for instance, through a mouse click or a keypress.
12774      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12775      * @param {HtmlElement} t The target of the event.
12776      * @param {Object} o The options configuration passed to the {@link #addListener} call.
12777      */
12778
12779         //  DOM Mutation events
12780         /**
12781      * @event DOMSubtreeModified
12782      * Where supported. Fires when the subtree is modified.
12783      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12784      * @param {HtmlElement} t The target of the event.
12785      * @param {Object} o The options configuration passed to the {@link #addListener} call.
12786      */
12787         /**
12788      * @event DOMNodeInserted
12789      * Where supported. Fires when a node has been added as a child of another node.
12790      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12791      * @param {HtmlElement} t The target of the event.
12792      * @param {Object} o The options configuration passed to the {@link #addListener} call.
12793      */
12794         /**
12795      * @event DOMNodeRemoved
12796      * Where supported. Fires when a descendant node of the element is removed.
12797      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12798      * @param {HtmlElement} t The target of the event.
12799      * @param {Object} o The options configuration passed to the {@link #addListener} call.
12800      */
12801         /**
12802      * @event DOMNodeRemovedFromDocument
12803      * Where supported. Fires when a node is being removed from a document.
12804      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12805      * @param {HtmlElement} t The target of the event.
12806      * @param {Object} o The options configuration passed to the {@link #addListener} call.
12807      */
12808         /**
12809      * @event DOMNodeInsertedIntoDocument
12810      * Where supported. Fires when a node is being inserted into a document.
12811      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12812      * @param {HtmlElement} t The target of the event.
12813      * @param {Object} o The options configuration passed to the {@link #addListener} call.
12814      */
12815         /**
12816      * @event DOMAttrModified
12817      * Where supported. Fires when an attribute has been modified.
12818      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12819      * @param {HtmlElement} t The target of the event.
12820      * @param {Object} o The options configuration passed to the {@link #addListener} call.
12821      */
12822         /**
12823      * @event DOMCharacterDataModified
12824      * Where supported. Fires when the character data has been modified.
12825      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12826      * @param {HtmlElement} t The target of the event.
12827      * @param {Object} o The options configuration passed to the {@link #addListener} call.
12828      */
12829
12830         /**
12831      * The default unit to append to CSS values where a unit isn't provided (defaults to px).
12832      * @type String
12833      */
12834         defaultUnit: "px",
12835
12836         /**
12837      * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
12838      * @param {String} selector The simple selector to test
12839      * @return {Boolean} True if this element matches the selector, else false
12840      */
12841         is: function(simpleSelector) {
12842             return Ext.DomQuery.is(this.dom, simpleSelector);
12843         },
12844
12845         /**
12846      * Tries to focus the element. Any exceptions are caught and ignored.
12847      * @param {Number} defer (optional) Milliseconds to defer the focus
12848      * @return {Ext.core.Element} this
12849      */
12850         focus: function(defer,
12851                         /* private */
12852                         dom) {
12853             var me = this;
12854             dom = dom || me.dom;
12855             try {
12856                 if (Number(defer)) {
12857                     Ext.defer(me.focus, defer, null, [null, dom]);
12858                 } else {
12859                     dom.focus();
12860                 }
12861             } catch(e) {}
12862             return me;
12863         },
12864
12865         /**
12866      * Tries to blur the element. Any exceptions are caught and ignored.
12867      * @return {Ext.core.Element} this
12868      */
12869         blur: function() {
12870             try {
12871                 this.dom.blur();
12872             } catch(e) {}
12873             return this;
12874         },
12875
12876         /**
12877      * Returns the value of the "value" attribute
12878      * @param {Boolean} asNumber true to parse the value as a number
12879      * @return {String/Number}
12880      */
12881         getValue: function(asNumber) {
12882             var val = this.dom.value;
12883             return asNumber ? parseInt(val, 10) : val;
12884         },
12885
12886         /**
12887      * Appends an event handler to this element.  The shorthand version {@link #on} is equivalent.
12888      * @param {String} eventName The name of event to handle.
12889      * @param {Function} fn The handler function the event invokes. This function is passed
12890      * the following parameters:<ul>
12891      * <li><b>evt</b> : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
12892      * <li><b>el</b> : HtmlElement<div class="sub-desc">The DOM element which was the target of the event.
12893      * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
12894      * <li><b>o</b> : Object<div class="sub-desc">The options object from the addListener call.</div></li>
12895      * </ul>
12896      * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
12897      * <b>If omitted, defaults to this Element.</b>.
12898      * @param {Object} options (optional) An object containing handler configuration properties.
12899      * This may contain any of the following properties:<ul>
12900      * <li><b>scope</b> Object : <div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the handler function is executed.
12901      * <b>If omitted, defaults to this Element.</b></div></li>
12902      * <li><b>delegate</b> String: <div class="sub-desc">A simple selector to filter the target or look for a descendant of the target. See below for additional details.</div></li>
12903      * <li><b>stopEvent</b> Boolean: <div class="sub-desc">True to stop the event. That is stop propagation, and prevent the default action.</div></li>
12904      * <li><b>preventDefault</b> Boolean: <div class="sub-desc">True to prevent the default action</div></li>
12905      * <li><b>stopPropagation</b> Boolean: <div class="sub-desc">True to prevent event propagation</div></li>
12906      * <li><b>normalized</b> Boolean: <div class="sub-desc">False to pass a browser event to the handler function instead of an Ext.EventObject</div></li>
12907      * <li><b>target</b> Ext.core.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>
12908      * <li><b>delay</b> Number: <div class="sub-desc">The number of milliseconds to delay the invocation of the handler after the event fires.</div></li>
12909      * <li><b>single</b> 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>
12910      * <li><b>buffer</b> Number: <div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
12911      * by the specified number of milliseconds. If the event fires again within that time, the original
12912      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
12913      * </ul><br>
12914      * <p>
12915      * <b>Combining Options</b><br>
12916      * In the following examples, the shorthand form {@link #on} is used rather than the more verbose
12917      * addListener.  The two are equivalent.  Using the options argument, it is possible to combine different
12918      * types of listeners:<br>
12919      * <br>
12920      * A delayed, one-time listener that auto stops the event and adds a custom argument (forumId) to the
12921      * options object. The options object is available as the third parameter in the handler function.<div style="margin: 5px 20px 20px;">
12922      * Code:<pre><code>
12923 el.on('click', this.onClick, this, {
12924     single: true,
12925     delay: 100,
12926     stopEvent : true,
12927     forumId: 4
12928 });</code></pre></p>
12929      * <p>
12930      * <b>Attaching multiple handlers in 1 call</b><br>
12931      * The method also allows for a single argument to be passed which is a config object containing properties
12932      * which specify multiple handlers.</p>
12933      * <p>
12934      * Code:<pre><code>
12935 el.on({
12936     'click' : {
12937         fn: this.onClick,
12938         scope: this,
12939         delay: 100
12940     },
12941     'mouseover' : {
12942         fn: this.onMouseOver,
12943         scope: this
12944     },
12945     'mouseout' : {
12946         fn: this.onMouseOut,
12947         scope: this
12948     }
12949 });</code></pre>
12950      * <p>
12951      * Or a shorthand syntax:<br>
12952      * Code:<pre><code></p>
12953 el.on({
12954     'click' : this.onClick,
12955     'mouseover' : this.onMouseOver,
12956     'mouseout' : this.onMouseOut,
12957     scope: this
12958 });
12959      * </code></pre></p>
12960      * <p><b>delegate</b></p>
12961      * <p>This is a configuration option that you can pass along when registering a handler for
12962      * an event to assist with event delegation. Event delegation is a technique that is used to
12963      * reduce memory consumption and prevent exposure to memory-leaks. By registering an event
12964      * for a container element as opposed to each element within a container. By setting this
12965      * configuration option to a simple selector, the target element will be filtered to look for
12966      * a descendant of the target.
12967      * For example:<pre><code>
12968 // using this markup:
12969 &lt;div id='elId'>
12970     &lt;p id='p1'>paragraph one&lt;/p>
12971     &lt;p id='p2' class='clickable'>paragraph two&lt;/p>
12972     &lt;p id='p3'>paragraph three&lt;/p>
12973 &lt;/div>
12974 // utilize event delegation to registering just one handler on the container element:
12975 el = Ext.get('elId');
12976 el.on(
12977     'click',
12978     function(e,t) {
12979         // handle click
12980         console.info(t.id); // 'p2'
12981     },
12982     this,
12983     {
12984         // filter the target element to be a descendant with the class 'clickable'
12985         delegate: '.clickable'
12986     }
12987 );
12988      * </code></pre></p>
12989      * @return {Ext.core.Element} this
12990      */
12991         addListener: function(eventName, fn, scope, options) {
12992             Ext.EventManager.on(this.dom, eventName, fn, scope || this, options);
12993             return this;
12994         },
12995
12996         /**
12997      * Removes an event handler from this element.  The shorthand version {@link #un} is equivalent.
12998      * <b>Note</b>: if a <i>scope</i> was explicitly specified when {@link #addListener adding} the
12999      * listener, the same scope must be specified here.
13000      * Example:
13001      * <pre><code>
13002 el.removeListener('click', this.handlerFn);
13003 // or
13004 el.un('click', this.handlerFn);
13005 </code></pre>
13006      * @param {String} eventName The name of the event from which to remove the handler.
13007      * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
13008      * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
13009      * then this must refer to the same object.
13010      * @return {Ext.core.Element} this
13011      */
13012         removeListener: function(eventName, fn, scope) {
13013             Ext.EventManager.un(this.dom, eventName, fn, scope || this);
13014             return this;
13015         },
13016
13017         /**
13018      * Removes all previous added listeners from this element
13019      * @return {Ext.core.Element} this
13020      */
13021         removeAllListeners: function() {
13022             Ext.EventManager.removeAll(this.dom);
13023             return this;
13024         },
13025
13026         /**
13027          * Recursively removes all previous added listeners from this element and its children
13028          * @return {Ext.core.Element} this
13029          */
13030         purgeAllListeners: function() {
13031             Ext.EventManager.purgeElement(this);
13032             return this;
13033         },
13034
13035         /**
13036          * @private Test if size has a unit, otherwise appends the passed unit string, or the default for this Element.
13037          * @param size {Mixed} The size to set
13038          * @param units {String} The units to append to a numeric size value
13039          */
13040         addUnits: function(size, units) {
13041
13042             // Most common case first: Size is set to a number
13043             if (Ext.isNumber(size)) {
13044                 return size + (units || this.defaultUnit || 'px');
13045             }
13046
13047             // Size set to a value which means "auto"
13048             if (size === "" || size == "auto" || size === undefined || size === null) {
13049                 return size || '';
13050             }
13051
13052             // Otherwise, warn if it's not a valid CSS measurement
13053             if (!unitPattern.test(size)) {
13054                 if (Ext.isDefined(Ext.global.console)) {
13055                     Ext.global.console.warn("Warning, size detected as NaN on Element.addUnits.");
13056                 }
13057                 return size || '';
13058             }
13059             return size;
13060         },
13061
13062         /**
13063          * Tests various css rules/browsers to determine if this element uses a border box
13064          * @return {Boolean}
13065          */
13066         isBorderBox: function() {
13067             return Ext.isBorderBox || noBoxAdjust[(this.dom.tagName || "").toLowerCase()];
13068         },
13069
13070         /**
13071          * <p>Removes this element's dom reference.  Note that event and cache removal is handled at {@link Ext#removeNode Ext.removeNode}</p>
13072          */
13073         remove: function() {
13074             var me = this,
13075             dom = me.dom;
13076
13077             if (dom) {
13078                 delete me.dom;
13079                 Ext.removeNode(dom);
13080             }
13081         },
13082
13083         /**
13084          * Sets up event handlers to call the passed functions when the mouse is moved into and out of the Element.
13085          * @param {Function} overFn The function to call when the mouse enters the Element.
13086          * @param {Function} outFn The function to call when the mouse leaves the Element.
13087          * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the functions are executed. Defaults to the Element's DOM element.
13088          * @param {Object} options (optional) Options for the listener. See {@link Ext.util.Observable#addListener the <tt>options</tt> parameter}.
13089          * @return {Ext.core.Element} this
13090          */
13091         hover: function(overFn, outFn, scope, options) {
13092             var me = this;
13093             me.on('mouseenter', overFn, scope || me.dom, options);
13094             me.on('mouseleave', outFn, scope || me.dom, options);
13095             return me;
13096         },
13097
13098         /**
13099          * Returns true if this element is an ancestor of the passed element
13100          * @param {HTMLElement/String} el The element to check
13101          * @return {Boolean} True if this element is an ancestor of el, else false
13102          */
13103         contains: function(el) {
13104             return ! el ? false: Ext.core.Element.isAncestor(this.dom, el.dom ? el.dom: el);
13105         },
13106
13107         /**
13108          * Returns the value of a namespaced attribute from the element's underlying DOM node.
13109          * @param {String} namespace The namespace in which to look for the attribute
13110          * @param {String} name The attribute name
13111          * @return {String} The attribute value
13112          * @deprecated
13113          */
13114         getAttributeNS: function(ns, name) {
13115             return this.getAttribute(name, ns);
13116         },
13117
13118         /**
13119          * Returns the value of an attribute from the element's underlying DOM node.
13120          * @param {String} name The attribute name
13121          * @param {String} namespace (optional) The namespace in which to look for the attribute
13122          * @return {String} The attribute value
13123          * @method
13124          */
13125         getAttribute: (Ext.isIE && !(Ext.isIE9 && document.documentMode === 9)) ?
13126         function(name, ns) {
13127             var d = this.dom,
13128             type;
13129             if(ns) {
13130                 type = typeof d[ns + ":" + name];
13131                 if (type != 'undefined' && type != 'unknown') {
13132                     return d[ns + ":" + name] || null;
13133                 }
13134                 return null;
13135             }
13136             if (name === "for") {
13137                 name = "htmlFor";
13138             }
13139             return d[name] || null;
13140         }: function(name, ns) {
13141             var d = this.dom;
13142             if (ns) {
13143                return d.getAttributeNS(ns, name) || d.getAttribute(ns + ":" + name);
13144             }
13145             return  d.getAttribute(name) || d[name] || null;
13146         },
13147
13148         /**
13149          * Update the innerHTML of this element
13150          * @param {String} html The new HTML
13151          * @return {Ext.core.Element} this
13152          */
13153         update: function(html) {
13154             if (this.dom) {
13155                 this.dom.innerHTML = html;
13156             }
13157             return this;
13158         }
13159     };
13160
13161     var ep = El.prototype;
13162
13163     El.addMethods = function(o) {
13164         Ext.apply(ep, o);
13165     };
13166
13167     /**
13168      * Appends an event handler (shorthand for {@link #addListener}).
13169      * @param {String} eventName The name of event to handle.
13170      * @param {Function} fn The handler function the event invokes.
13171      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function is executed.
13172      * @param {Object} options (optional) An object containing standard {@link #addListener} options
13173      * @member Ext.core.Element
13174      * @method on
13175      */
13176     ep.on = ep.addListener;
13177
13178     /**
13179      * Removes an event handler from this element (see {@link #removeListener} for additional notes).
13180      * @param {String} eventName The name of the event from which to remove the handler.
13181      * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
13182      * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
13183      * then this must refer to the same object.
13184      * @return {Ext.core.Element} this
13185      * @member Ext.core.Element
13186      * @method un
13187      */
13188     ep.un = ep.removeListener;
13189
13190     /**
13191      * Removes all previous added listeners from this element
13192      * @return {Ext.core.Element} this
13193      * @member Ext.core.Element
13194      * @method clearListeners
13195      */
13196     ep.clearListeners = ep.removeAllListeners;
13197
13198     /**
13199      * Removes this element's dom reference.  Note that event and cache removal is handled at {@link Ext#removeNode Ext.removeNode}.
13200      * Alias to {@link #remove}.
13201      * @member Ext.core.Element
13202      * @method destroy
13203      */
13204     ep.destroy = ep.remove;
13205
13206     /**
13207      * true to automatically adjust width and height settings for box-model issues (default to true)
13208      */
13209     ep.autoBoxAdjust = true;
13210
13211     // private
13212     var unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
13213     docEl;
13214
13215     /**
13216      * Retrieves Ext.core.Element objects.
13217      * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method
13218      * retrieves Ext.core.Element objects which encapsulate DOM elements. To retrieve a Component by
13219      * its ID, use {@link Ext.ComponentManager#get}.</p>
13220      * <p>Uses simple caching to consistently return the same object. Automatically fixes if an
13221      * object was recreated with the same id via AJAX or DOM.</p>
13222      * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
13223      * @return {Element} The Element object (or null if no matching element was found)
13224      * @static
13225      * @member Ext.core.Element
13226      * @method get
13227      */
13228     El.get = function(el) {
13229         var ex,
13230         elm,
13231         id;
13232         if (!el) {
13233             return null;
13234         }
13235         if (typeof el == "string") {
13236             // element id
13237             if (! (elm = DOC.getElementById(el))) {
13238                 return null;
13239             }
13240             if (EC[el] && EC[el].el) {
13241                 ex = EC[el].el;
13242                 ex.dom = elm;
13243             } else {
13244                 ex = El.addToCache(new El(elm));
13245             }
13246             return ex;
13247         } else if (el.tagName) {
13248             // dom element
13249             if (! (id = el.id)) {
13250                 id = Ext.id(el);
13251             }
13252             if (EC[id] && EC[id].el) {
13253                 ex = EC[id].el;
13254                 ex.dom = el;
13255             } else {
13256                 ex = El.addToCache(new El(el));
13257             }
13258             return ex;
13259         } else if (el instanceof El) {
13260             if (el != docEl) {
13261                 // refresh dom element in case no longer valid,
13262                 // catch case where it hasn't been appended
13263                 // If an el instance is passed, don't pass to getElementById without some kind of id
13264                 if (Ext.isIE && (el.id == undefined || el.id == '')) {
13265                     el.dom = el.dom;
13266                 } else {
13267                     el.dom = DOC.getElementById(el.id) || el.dom;
13268                 }
13269             }
13270             return el;
13271         } else if (el.isComposite) {
13272             return el;
13273         } else if (Ext.isArray(el)) {
13274             return El.select(el);
13275         } else if (el == DOC) {
13276             // create a bogus element object representing the document object
13277             if (!docEl) {
13278                 var f = function() {};
13279                 f.prototype = El.prototype;
13280                 docEl = new f();
13281                 docEl.dom = DOC;
13282             }
13283             return docEl;
13284         }
13285         return null;
13286     };
13287
13288     El.addToCache = function(el, id) {
13289         if (el) {
13290             id = id || el.id;
13291             EC[id] = {
13292                 el: el,
13293                 data: {},
13294                 events: {}
13295             };
13296         }
13297         return el;
13298     };
13299
13300     // private method for getting and setting element data
13301     El.data = function(el, key, value) {
13302         el = El.get(el);
13303         if (!el) {
13304             return null;
13305         }
13306         var c = EC[el.id].data;
13307         if (arguments.length == 2) {
13308             return c[key];
13309         } else {
13310             return (c[key] = value);
13311         }
13312     };
13313
13314     // private
13315     // Garbage collection - uncache elements/purge listeners on orphaned elements
13316     // so we don't hold a reference and cause the browser to retain them
13317     function garbageCollect() {
13318         if (!Ext.enableGarbageCollector) {
13319             clearInterval(El.collectorThreadId);
13320         } else {
13321             var eid,
13322             el,
13323             d,
13324             o;
13325
13326             for (eid in EC) {
13327                 if (!EC.hasOwnProperty(eid)) {
13328                     continue;
13329                 }
13330                 o = EC[eid];
13331                 if (o.skipGarbageCollection) {
13332                     continue;
13333                 }
13334                 el = o.el;
13335                 d = el.dom;
13336                 // -------------------------------------------------------
13337                 // Determining what is garbage:
13338                 // -------------------------------------------------------
13339                 // !d
13340                 // dom node is null, definitely garbage
13341                 // -------------------------------------------------------
13342                 // !d.parentNode
13343                 // no parentNode == direct orphan, definitely garbage
13344                 // -------------------------------------------------------
13345                 // !d.offsetParent && !document.getElementById(eid)
13346                 // display none elements have no offsetParent so we will
13347                 // also try to look it up by it's id. However, check
13348                 // offsetParent first so we don't do unneeded lookups.
13349                 // This enables collection of elements that are not orphans
13350                 // directly, but somewhere up the line they have an orphan
13351                 // parent.
13352                 // -------------------------------------------------------
13353                 if (!d || !d.parentNode || (!d.offsetParent && !DOC.getElementById(eid))) {
13354                     if (d && Ext.enableListenerCollection) {
13355                         Ext.EventManager.removeAll(d);
13356                     }
13357                     delete EC[eid];
13358                 }
13359             }
13360             // Cleanup IE Object leaks
13361             if (Ext.isIE) {
13362                 var t = {};
13363                 for (eid in EC) {
13364                     if (!EC.hasOwnProperty(eid)) {
13365                         continue;
13366                     }
13367                     t[eid] = EC[eid];
13368                 }
13369                 EC = Ext.cache = t;
13370             }
13371         }
13372     }
13373     El.collectorThreadId = setInterval(garbageCollect, 30000);
13374
13375     var flyFn = function() {};
13376     flyFn.prototype = El.prototype;
13377
13378     // dom is optional
13379     El.Flyweight = function(dom) {
13380         this.dom = dom;
13381     };
13382
13383     El.Flyweight.prototype = new flyFn();
13384     El.Flyweight.prototype.isFlyweight = true;
13385     El._flyweights = {};
13386
13387     /**
13388      * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
13389      * the dom node can be overwritten by other code. Shorthand of {@link Ext.core.Element#fly}</p>
13390      * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by
13391      * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get Ext.get}
13392      * will be more appropriate to take advantage of the caching provided by the Ext.core.Element class.</p>
13393      * @param {String/HTMLElement} el The dom node or id
13394      * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts
13395      * (e.g. internally Ext uses "_global")
13396      * @return {Element} The shared Element object (or null if no matching element was found)
13397      * @member Ext.core.Element
13398      * @method fly
13399      */
13400     El.fly = function(el, named) {
13401         var ret = null;
13402         named = named || '_global';
13403         el = Ext.getDom(el);
13404         if (el) {
13405             (El._flyweights[named] = El._flyweights[named] || new El.Flyweight()).dom = el;
13406             ret = El._flyweights[named];
13407         }
13408         return ret;
13409     };
13410
13411     /**
13412      * Retrieves Ext.core.Element objects.
13413      * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method
13414      * retrieves Ext.core.Element objects which encapsulate DOM elements. To retrieve a Component by
13415      * its ID, use {@link Ext.ComponentManager#get}.</p>
13416      * <p>Uses simple caching to consistently return the same object. Automatically fixes if an
13417      * object was recreated with the same id via AJAX or DOM.</p>
13418      * Shorthand of {@link Ext.core.Element#get}
13419      * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
13420      * @return {Element} The Element object (or null if no matching element was found)
13421      * @member Ext
13422      * @method get
13423      */
13424     Ext.get = El.get;
13425
13426     /**
13427      * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
13428      * the dom node can be overwritten by other code. Shorthand of {@link Ext.core.Element#fly}</p>
13429      * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by
13430      * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get Ext.get}
13431      * will be more appropriate to take advantage of the caching provided by the Ext.core.Element class.</p>
13432      * @param {String/HTMLElement} el The dom node or id
13433      * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts
13434      * (e.g. internally Ext uses "_global")
13435      * @return {Element} The shared Element object (or null if no matching element was found)
13436      * @member Ext
13437      * @method fly
13438      */
13439     Ext.fly = El.fly;
13440
13441     // speedy lookup for elements never to box adjust
13442     var noBoxAdjust = Ext.isStrict ? {
13443         select: 1
13444     }: {
13445         input: 1,
13446         select: 1,
13447         textarea: 1
13448     };
13449     if (Ext.isIE || Ext.isGecko) {
13450         noBoxAdjust['button'] = 1;
13451     }
13452 })();
13453
13454 /**
13455  * @class Ext.core.Element
13456  */
13457 Ext.core.Element.addMethods({
13458     /**
13459      * 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)
13460      * @param {String} selector The simple selector to test
13461      * @param {Number/Mixed} maxDepth (optional) The max depth to search as a number or element (defaults to 50 || document.body)
13462      * @param {Boolean} returnEl (optional) True to return a Ext.core.Element object instead of DOM node
13463      * @return {HTMLElement} The matching DOM node (or null if no match was found)
13464      */
13465     findParent : function(simpleSelector, maxDepth, returnEl) {
13466         var p = this.dom,
13467             b = document.body,
13468             depth = 0,
13469             stopEl;
13470
13471         maxDepth = maxDepth || 50;
13472         if (isNaN(maxDepth)) {
13473             stopEl = Ext.getDom(maxDepth);
13474             maxDepth = Number.MAX_VALUE;
13475         }
13476         while (p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl) {
13477             if (Ext.DomQuery.is(p, simpleSelector)) {
13478                 return returnEl ? Ext.get(p) : p;
13479             }
13480             depth++;
13481             p = p.parentNode;
13482         }
13483         return null;
13484     },
13485     
13486     /**
13487      * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
13488      * @param {String} selector The simple selector to test
13489      * @param {Number/Mixed} maxDepth (optional) The max depth to
13490             search as a number or element (defaults to 10 || document.body)
13491      * @param {Boolean} returnEl (optional) True to return a Ext.core.Element object instead of DOM node
13492      * @return {HTMLElement} The matching DOM node (or null if no match was found)
13493      */
13494     findParentNode : function(simpleSelector, maxDepth, returnEl) {
13495         var p = Ext.fly(this.dom.parentNode, '_internal');
13496         return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
13497     },
13498
13499     /**
13500      * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
13501      * This is a shortcut for findParentNode() that always returns an Ext.core.Element.
13502      * @param {String} selector The simple selector to test
13503      * @param {Number/Mixed} maxDepth (optional) The max depth to
13504             search as a number or element (defaults to 10 || document.body)
13505      * @return {Ext.core.Element} The matching DOM node (or null if no match was found)
13506      */
13507     up : function(simpleSelector, maxDepth) {
13508         return this.findParentNode(simpleSelector, maxDepth, true);
13509     },
13510
13511     /**
13512      * Creates a {@link Ext.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
13513      * @param {String} selector The CSS selector
13514      * @return {CompositeElement/CompositeElement} The composite element
13515      */
13516     select : function(selector) {
13517         return Ext.core.Element.select(selector, false,  this.dom);
13518     },
13519
13520     /**
13521      * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
13522      * @param {String} selector The CSS selector
13523      * @return {Array} An array of the matched nodes
13524      */
13525     query : function(selector) {
13526         return Ext.DomQuery.select(selector, this.dom);
13527     },
13528
13529     /**
13530      * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
13531      * @param {String} selector The CSS selector
13532      * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.core.Element (defaults to false)
13533      * @return {HTMLElement/Ext.core.Element} The child Ext.core.Element (or DOM node if returnDom = true)
13534      */
13535     down : function(selector, returnDom) {
13536         var n = Ext.DomQuery.selectNode(selector, this.dom);
13537         return returnDom ? n : Ext.get(n);
13538     },
13539
13540     /**
13541      * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
13542      * @param {String} selector The CSS selector
13543      * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.core.Element (defaults to false)
13544      * @return {HTMLElement/Ext.core.Element} The child Ext.core.Element (or DOM node if returnDom = true)
13545      */
13546     child : function(selector, returnDom) {
13547         var node,
13548             me = this,
13549             id;
13550         id = Ext.get(me).id;
13551         // Escape . or :
13552         id = id.replace(/[\.:]/g, "\\$0");
13553         node = Ext.DomQuery.selectNode('#' + id + " > " + selector, me.dom);
13554         return returnDom ? node : Ext.get(node);
13555     },
13556
13557      /**
13558      * Gets the parent node for this element, optionally chaining up trying to match a selector
13559      * @param {String} selector (optional) Find a parent node that matches the passed simple selector
13560      * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.core.Element
13561      * @return {Ext.core.Element/HTMLElement} The parent node or null
13562      */
13563     parent : function(selector, returnDom) {
13564         return this.matchNode('parentNode', 'parentNode', selector, returnDom);
13565     },
13566
13567      /**
13568      * Gets the next sibling, skipping text nodes
13569      * @param {String} selector (optional) Find the next sibling that matches the passed simple selector
13570      * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.core.Element
13571      * @return {Ext.core.Element/HTMLElement} The next sibling or null
13572      */
13573     next : function(selector, returnDom) {
13574         return this.matchNode('nextSibling', 'nextSibling', selector, returnDom);
13575     },
13576
13577     /**
13578      * Gets the previous sibling, skipping text nodes
13579      * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector
13580      * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.core.Element
13581      * @return {Ext.core.Element/HTMLElement} The previous sibling or null
13582      */
13583     prev : function(selector, returnDom) {
13584         return this.matchNode('previousSibling', 'previousSibling', selector, returnDom);
13585     },
13586
13587
13588     /**
13589      * Gets the first child, skipping text nodes
13590      * @param {String} selector (optional) Find the next sibling that matches the passed simple selector
13591      * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.core.Element
13592      * @return {Ext.core.Element/HTMLElement} The first child or null
13593      */
13594     first : function(selector, returnDom) {
13595         return this.matchNode('nextSibling', 'firstChild', selector, returnDom);
13596     },
13597
13598     /**
13599      * Gets the last child, skipping text nodes
13600      * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector
13601      * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.core.Element
13602      * @return {Ext.core.Element/HTMLElement} The last child or null
13603      */
13604     last : function(selector, returnDom) {
13605         return this.matchNode('previousSibling', 'lastChild', selector, returnDom);
13606     },
13607
13608     matchNode : function(dir, start, selector, returnDom) {
13609         if (!this.dom) {
13610             return null;
13611         }
13612         
13613         var n = this.dom[start];
13614         while (n) {
13615             if (n.nodeType == 1 && (!selector || Ext.DomQuery.is(n, selector))) {
13616                 return !returnDom ? Ext.get(n) : n;
13617             }
13618             n = n[dir];
13619         }
13620         return null;
13621     }
13622 });
13623
13624 /**
13625  * @class Ext.core.Element
13626  */
13627 Ext.core.Element.addMethods({
13628     /**
13629      * Appends the passed element(s) to this element
13630      * @param {String/HTMLElement/Array/Element/CompositeElement} el
13631      * @return {Ext.core.Element} this
13632      */
13633     appendChild : function(el) {
13634         return Ext.get(el).appendTo(this);
13635     },
13636
13637     /**
13638      * Appends this element to the passed element
13639      * @param {Mixed} el The new parent element
13640      * @return {Ext.core.Element} this
13641      */
13642     appendTo : function(el) {
13643         Ext.getDom(el).appendChild(this.dom);
13644         return this;
13645     },
13646
13647     /**
13648      * Inserts this element before the passed element in the DOM
13649      * @param {Mixed} el The element before which this element will be inserted
13650      * @return {Ext.core.Element} this
13651      */
13652     insertBefore : function(el) {
13653         el = Ext.getDom(el);
13654         el.parentNode.insertBefore(this.dom, el);
13655         return this;
13656     },
13657
13658     /**
13659      * Inserts this element after the passed element in the DOM
13660      * @param {Mixed} el The element to insert after
13661      * @return {Ext.core.Element} this
13662      */
13663     insertAfter : function(el) {
13664         el = Ext.getDom(el);
13665         el.parentNode.insertBefore(this.dom, el.nextSibling);
13666         return this;
13667     },
13668
13669     /**
13670      * Inserts (or creates) an element (or DomHelper config) as the first child of this element
13671      * @param {Mixed/Object} el The id or element to insert or a DomHelper config to create and insert
13672      * @return {Ext.core.Element} The new child
13673      */
13674     insertFirst : function(el, returnDom) {
13675         el = el || {};
13676         if (el.nodeType || el.dom || typeof el == 'string') { // element
13677             el = Ext.getDom(el);
13678             this.dom.insertBefore(el, this.dom.firstChild);
13679             return !returnDom ? Ext.get(el) : el;
13680         }
13681         else { // dh config
13682             return this.createChild(el, this.dom.firstChild, returnDom);
13683         }
13684     },
13685
13686     /**
13687      * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
13688      * @param {Mixed/Object/Array} el The id, element to insert or a DomHelper config to create and insert *or* an array of any of those.
13689      * @param {String} where (optional) 'before' or 'after' defaults to before
13690      * @param {Boolean} returnDom (optional) True to return the .;ll;l,raw DOM element instead of Ext.core.Element
13691      * @return {Ext.core.Element} The inserted Element. If an array is passed, the last inserted element is returned.
13692      */
13693     insertSibling: function(el, where, returnDom){
13694         var me = this, rt,
13695         isAfter = (where || 'before').toLowerCase() == 'after',
13696         insertEl;
13697
13698         if(Ext.isArray(el)){
13699             insertEl = me;
13700             Ext.each(el, function(e) {
13701                 rt = Ext.fly(insertEl, '_internal').insertSibling(e, where, returnDom);
13702                 if(isAfter){
13703                     insertEl = rt;
13704                 }
13705             });
13706             return rt;
13707         }
13708
13709         el = el || {};
13710
13711         if(el.nodeType || el.dom){
13712             rt = me.dom.parentNode.insertBefore(Ext.getDom(el), isAfter ? me.dom.nextSibling : me.dom);
13713             if (!returnDom) {
13714                 rt = Ext.get(rt);
13715             }
13716         }else{
13717             if (isAfter && !me.dom.nextSibling) {
13718                 rt = Ext.core.DomHelper.append(me.dom.parentNode, el, !returnDom);
13719             } else {
13720                 rt = Ext.core.DomHelper[isAfter ? 'insertAfter' : 'insertBefore'](me.dom, el, !returnDom);
13721             }
13722         }
13723         return rt;
13724     },
13725
13726     /**
13727      * Replaces the passed element with this element
13728      * @param {Mixed} el The element to replace
13729      * @return {Ext.core.Element} this
13730      */
13731     replace : function(el) {
13732         el = Ext.get(el);
13733         this.insertBefore(el);
13734         el.remove();
13735         return this;
13736     },
13737     
13738     /**
13739      * Replaces this element with the passed element
13740      * @param {Mixed/Object} el The new element or a DomHelper config of an element to create
13741      * @return {Ext.core.Element} this
13742      */
13743     replaceWith: function(el){
13744         var me = this;
13745             
13746         if(el.nodeType || el.dom || typeof el == 'string'){
13747             el = Ext.get(el);
13748             me.dom.parentNode.insertBefore(el, me.dom);
13749         }else{
13750             el = Ext.core.DomHelper.insertBefore(me.dom, el);
13751         }
13752         
13753         delete Ext.cache[me.id];
13754         Ext.removeNode(me.dom);      
13755         me.id = Ext.id(me.dom = el);
13756         Ext.core.Element.addToCache(me.isFlyweight ? new Ext.core.Element(me.dom) : me);     
13757         return me;
13758     },
13759     
13760     /**
13761      * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
13762      * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
13763      * automatically generated with the specified attributes.
13764      * @param {HTMLElement} insertBefore (optional) a child element of this element
13765      * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
13766      * @return {Ext.core.Element} The new child element
13767      */
13768     createChild : function(config, insertBefore, returnDom) {
13769         config = config || {tag:'div'};
13770         if (insertBefore) {
13771             return Ext.core.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
13772         }
13773         else {
13774             return Ext.core.DomHelper[!this.dom.firstChild ? 'insertFirst' : 'append'](this.dom, config,  returnDom !== true);
13775         }
13776     },
13777
13778     /**
13779      * Creates and wraps this element with another element
13780      * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
13781      * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.core.Element
13782      * @return {HTMLElement/Element} The newly created wrapper element
13783      */
13784     wrap : function(config, returnDom) {
13785         var newEl = Ext.core.DomHelper.insertBefore(this.dom, config || {tag: "div"}, !returnDom),
13786             d = newEl.dom || newEl;
13787
13788         d.appendChild(this.dom);
13789         return newEl;
13790     },
13791
13792     /**
13793      * Inserts an html fragment into this element
13794      * @param {String} where Where to insert the html in relation to this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
13795      * @param {String} html The HTML fragment
13796      * @param {Boolean} returnEl (optional) True to return an Ext.core.Element (defaults to false)
13797      * @return {HTMLElement/Ext.core.Element} The inserted node (or nearest related if more than 1 inserted)
13798      */
13799     insertHtml : function(where, html, returnEl) {
13800         var el = Ext.core.DomHelper.insertHtml(where, this.dom, html);
13801         return returnEl ? Ext.get(el) : el;
13802     }
13803 });
13804
13805 /**
13806  * @class Ext.core.Element
13807  */
13808 (function(){
13809     Ext.core.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>';
13810     // local style camelizing for speed
13811     var supports = Ext.supports,
13812         view = document.defaultView,
13813         opacityRe = /alpha\(opacity=(.*)\)/i,
13814         trimRe = /^\s+|\s+$/g,
13815         spacesRe = /\s+/,
13816         wordsRe = /\w/g,
13817         adjustDirect2DTableRe = /table-row|table-.*-group/,
13818         INTERNAL = '_internal',
13819         PADDING = 'padding',
13820         MARGIN = 'margin',
13821         BORDER = 'border',
13822         LEFT = '-left',
13823         RIGHT = '-right',
13824         TOP = '-top',
13825         BOTTOM = '-bottom',
13826         WIDTH = '-width',
13827         MATH = Math,
13828         HIDDEN = 'hidden',
13829         ISCLIPPED = 'isClipped',
13830         OVERFLOW = 'overflow',
13831         OVERFLOWX = 'overflow-x',
13832         OVERFLOWY = 'overflow-y',
13833         ORIGINALCLIP = 'originalClip',
13834         // special markup used throughout Ext when box wrapping elements
13835         borders = {l: BORDER + LEFT + WIDTH, r: BORDER + RIGHT + WIDTH, t: BORDER + TOP + WIDTH, b: BORDER + BOTTOM + WIDTH},
13836         paddings = {l: PADDING + LEFT, r: PADDING + RIGHT, t: PADDING + TOP, b: PADDING + BOTTOM},
13837         margins = {l: MARGIN + LEFT, r: MARGIN + RIGHT, t: MARGIN + TOP, b: MARGIN + BOTTOM},
13838         data = Ext.core.Element.data;
13839
13840     Ext.override(Ext.core.Element, {
13841         
13842         /**
13843          * TODO: Look at this
13844          */
13845         // private  ==> used by Fx
13846         adjustWidth : function(width) {
13847             var me = this,
13848                 isNum = (typeof width == 'number');
13849                 
13850             if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
13851                width -= (me.getBorderWidth("lr") + me.getPadding("lr"));
13852             }
13853             return (isNum && width < 0) ? 0 : width;
13854         },
13855
13856         // private   ==> used by Fx
13857         adjustHeight : function(height) {
13858             var me = this,
13859                 isNum = (typeof height == "number");
13860                 
13861             if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
13862                height -= (me.getBorderWidth("tb") + me.getPadding("tb"));
13863             }
13864             return (isNum && height < 0) ? 0 : height;
13865         },
13866
13867
13868         /**
13869          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
13870          * @param {String/Array} className The CSS classes to add separated by space, or an array of classes
13871          * @return {Ext.core.Element} this
13872          */
13873         addCls : function(className){
13874             var me = this,
13875                 cls = [],
13876                 space = ((me.dom.className.replace(trimRe, '') == '') ? "" : " "),
13877                 i, len, v;
13878             if (className === undefined) {
13879                 return me;
13880             }
13881             // Separate case is for speed
13882             if (Object.prototype.toString.call(className) !== '[object Array]') {
13883                 if (typeof className === 'string') {
13884                     className = className.replace(trimRe, '').split(spacesRe);
13885                     if (className.length === 1) {
13886                         className = className[0];
13887                         if (!me.hasCls(className)) {
13888                             me.dom.className += space + className;
13889                         }
13890                     } else {
13891                         this.addCls(className);
13892                     }
13893                 }
13894             } else {
13895                 for (i = 0, len = className.length; i < len; i++) {
13896                     v = className[i];
13897                     if (typeof v == 'string' && (' ' + me.dom.className + ' ').indexOf(' ' + v + ' ') == -1) {
13898                         cls.push(v);
13899                     }
13900                 }
13901                 if (cls.length) {
13902                     me.dom.className += space + cls.join(" ");
13903                 }
13904             }
13905             return me;
13906         },
13907
13908         /**
13909          * Removes one or more CSS classes from the element.
13910          * @param {String/Array} className The CSS classes to remove separated by space, or an array of classes
13911          * @return {Ext.core.Element} this
13912          */
13913         removeCls : function(className){
13914             var me = this,
13915                 i, idx, len, cls, elClasses;
13916             if (className === undefined) {
13917                 return me;
13918             }
13919             if (Object.prototype.toString.call(className) !== '[object Array]') {
13920                 className = className.replace(trimRe, '').split(spacesRe);
13921             }
13922             if (me.dom && me.dom.className) {
13923                 elClasses = me.dom.className.replace(trimRe, '').split(spacesRe);
13924                 for (i = 0, len = className.length; i < len; i++) {
13925                     cls = className[i];
13926                     if (typeof cls == 'string') {
13927                         cls = cls.replace(trimRe, '');
13928                         idx = Ext.Array.indexOf(elClasses, cls);
13929                         if (idx != -1) {
13930                             Ext.Array.erase(elClasses, idx, 1);
13931                         }
13932                     }
13933                 }
13934                 me.dom.className = elClasses.join(" ");
13935             }
13936             return me;
13937         },
13938
13939         /**
13940          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
13941          * @param {String/Array} className The CSS class to add, or an array of classes
13942          * @return {Ext.core.Element} this
13943          */
13944         radioCls : function(className){
13945             var cn = this.dom.parentNode.childNodes,
13946                 v, i, len;
13947             className = Ext.isArray(className) ? className : [className];
13948             for (i = 0, len = cn.length; i < len; i++) {
13949                 v = cn[i];
13950                 if (v && v.nodeType == 1) {
13951                     Ext.fly(v, '_internal').removeCls(className);
13952                 }
13953             }
13954             return this.addCls(className);
13955         },
13956
13957         /**
13958          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
13959          * @param {String} className The CSS class to toggle
13960          * @return {Ext.core.Element} this
13961          * @method
13962          */
13963         toggleCls : Ext.supports.ClassList ?
13964             function(className) {
13965                 this.dom.classList.toggle(Ext.String.trim(className));
13966                 return this;
13967             } :
13968             function(className) {
13969                 return this.hasCls(className) ? this.removeCls(className) : this.addCls(className);
13970             },
13971
13972         /**
13973          * Checks if the specified CSS class exists on this element's DOM node.
13974          * @param {String} className The CSS class to check for
13975          * @return {Boolean} True if the class exists, else false
13976          * @method
13977          */
13978         hasCls : Ext.supports.ClassList ?
13979             function(className) {
13980                 if (!className) {
13981                     return false;
13982                 }
13983                 className = className.split(spacesRe);
13984                 var ln = className.length,
13985                     i = 0;
13986                 for (; i < ln; i++) {
13987                     if (className[i] && this.dom.classList.contains(className[i])) {
13988                         return true;
13989                     }
13990                 }
13991                 return false;
13992             } :
13993             function(className){
13994                 return className && (' ' + this.dom.className + ' ').indexOf(' ' + className + ' ') != -1;
13995             },
13996
13997         /**
13998          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
13999          * @param {String} oldClassName The CSS class to replace
14000          * @param {String} newClassName The replacement CSS class
14001          * @return {Ext.core.Element} this
14002          */
14003         replaceCls : function(oldClassName, newClassName){
14004             return this.removeCls(oldClassName).addCls(newClassName);
14005         },
14006
14007         isStyle : function(style, val) {
14008             return this.getStyle(style) == val;
14009         },
14010
14011         /**
14012          * Normalizes currentStyle and computedStyle.
14013          * @param {String} property The style property whose value is returned.
14014          * @return {String} The current value of the style property for this element.
14015          * @method
14016          */
14017         getStyle : function(){
14018             return view && view.getComputedStyle ?
14019                 function(prop){
14020                     var el = this.dom,
14021                         v, cs, out, display, cleaner;
14022
14023                     if(el == document){
14024                         return null;
14025                     }
14026                     prop = Ext.core.Element.normalize(prop);
14027                     out = (v = el.style[prop]) ? v :
14028                            (cs = view.getComputedStyle(el, "")) ? cs[prop] : null;
14029                            
14030                     // Ignore cases when the margin is correctly reported as 0, the bug only shows
14031                     // numbers larger.
14032                     if(prop == 'marginRight' && out != '0px' && !supports.RightMargin){
14033                         cleaner = Ext.core.Element.getRightMarginFixCleaner(el);
14034                         display = this.getStyle('display');
14035                         el.style.display = 'inline-block';
14036                         out = view.getComputedStyle(el, '').marginRight;
14037                         el.style.display = display;
14038                         cleaner();
14039                     }
14040                     
14041                     if(prop == 'backgroundColor' && out == 'rgba(0, 0, 0, 0)' && !supports.TransparentColor){
14042                         out = 'transparent';
14043                     }
14044                     return out;
14045                 } :
14046                 function(prop){
14047                     var el = this.dom,
14048                         m, cs;
14049
14050                     if (el == document) {
14051                         return null;
14052                     }
14053                     
14054                     if (prop == 'opacity') {
14055                         if (el.style.filter.match) {
14056                             m = el.style.filter.match(opacityRe);
14057                             if(m){
14058                                 var fv = parseFloat(m[1]);
14059                                 if(!isNaN(fv)){
14060                                     return fv ? fv / 100 : 0;
14061                                 }
14062                             }
14063                         }
14064                         return 1;
14065                     }
14066                     prop = Ext.core.Element.normalize(prop);
14067                     return el.style[prop] || ((cs = el.currentStyle) ? cs[prop] : null);
14068                 };
14069         }(),
14070
14071         /**
14072          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
14073          * are convert to standard 6 digit hex color.
14074          * @param {String} attr The css attribute
14075          * @param {String} defaultValue The default value to use when a valid color isn't found
14076          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
14077          * color anims.
14078          */
14079         getColor : function(attr, defaultValue, prefix){
14080             var v = this.getStyle(attr),
14081                 color = prefix || prefix === '' ? prefix : '#',
14082                 h;
14083
14084             if(!v || (/transparent|inherit/.test(v))) {
14085                 return defaultValue;
14086             }
14087             if(/^r/.test(v)){
14088                 Ext.each(v.slice(4, v.length -1).split(','), function(s){
14089                     h = parseInt(s, 10);
14090                     color += (h < 16 ? '0' : '') + h.toString(16);
14091                 });
14092             }else{
14093                 v = v.replace('#', '');
14094                 color += v.length == 3 ? v.replace(/^(\w)(\w)(\w)$/, '$1$1$2$2$3$3') : v;
14095             }
14096             return(color.length > 5 ? color.toLowerCase() : defaultValue);
14097         },
14098
14099         /**
14100          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
14101          * @param {String/Object} property The style property to be set, or an object of multiple styles.
14102          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
14103          * @return {Ext.core.Element} this
14104          */
14105         setStyle : function(prop, value){
14106             var me = this,
14107                 tmp, style;
14108
14109             if (!me.dom) {
14110                 return me;
14111             }
14112             if (typeof prop === 'string') {
14113                 tmp = {};
14114                 tmp[prop] = value;
14115                 prop = tmp;
14116             }
14117             for (style in prop) {
14118                 if (prop.hasOwnProperty(style)) {
14119                     value = Ext.value(prop[style], '');
14120                     if (style == 'opacity') {
14121                         me.setOpacity(value);
14122                     }
14123                     else {
14124                         me.dom.style[Ext.core.Element.normalize(style)] = value;
14125                     }
14126                 }
14127             }
14128             return me;
14129         },
14130
14131         /**
14132          * Set the opacity of the element
14133          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
14134          * @param {Boolean/Object} animate (optional) a standard Element animation config object or <tt>true</tt> for
14135          * the default animation (<tt>{duration: .35, easing: 'easeIn'}</tt>)
14136          * @return {Ext.core.Element} this
14137          */
14138         setOpacity: function(opacity, animate) {
14139             var me = this,
14140                 dom = me.dom,
14141                 val,
14142                 style;
14143
14144             if (!me.dom) {
14145                 return me;
14146             }
14147
14148             style = me.dom.style;
14149
14150             if (!animate || !me.anim) {
14151                 if (!Ext.supports.Opacity) {
14152                     opacity = opacity < 1 ? 'alpha(opacity=' + opacity * 100 + ')': '';
14153                     val = style.filter.replace(opacityRe, '').replace(trimRe, '');
14154
14155                     style.zoom = 1;
14156                     style.filter = val + (val.length > 0 ? ' ': '') + opacity;
14157                 }
14158                 else {
14159                     style.opacity = opacity;
14160                 }
14161             }
14162             else {
14163                 if (!Ext.isObject(animate)) {
14164                     animate = {
14165                         duration: 350,
14166                         easing: 'ease-in'
14167                     };
14168                 }
14169                 me.animate(Ext.applyIf({
14170                     to: {
14171                         opacity: opacity
14172                     }
14173                 },
14174                 animate));
14175             }
14176             return me;
14177         },
14178
14179
14180         /**
14181          * Clears any opacity settings from this element. Required in some cases for IE.
14182          * @return {Ext.core.Element} this
14183          */
14184         clearOpacity : function(){
14185             var style = this.dom.style;
14186             if(!Ext.supports.Opacity){
14187                 if(!Ext.isEmpty(style.filter)){
14188                     style.filter = style.filter.replace(opacityRe, '').replace(trimRe, '');
14189                 }
14190             }else{
14191                 style.opacity = style['-moz-opacity'] = style['-khtml-opacity'] = '';
14192             }
14193             return this;
14194         },
14195         
14196         /**
14197          * @private
14198          * Returns 1 if the browser returns the subpixel dimension rounded to the lowest pixel.
14199          * @return {Number} 0 or 1 
14200          */
14201         adjustDirect2DDimension: function(dimension) {
14202             var me = this,
14203                 dom = me.dom,
14204                 display = me.getStyle('display'),
14205                 inlineDisplay = dom.style['display'],
14206                 inlinePosition = dom.style['position'],
14207                 originIndex = dimension === 'width' ? 0 : 1,
14208                 floating;
14209                 
14210             if (display === 'inline') {
14211                 dom.style['display'] = 'inline-block';
14212             }
14213
14214             dom.style['position'] = display.match(adjustDirect2DTableRe) ? 'absolute' : 'static';
14215
14216             // floating will contain digits that appears after the decimal point
14217             // if height or width are set to auto we fallback to msTransformOrigin calculation
14218             floating = (parseFloat(me.getStyle(dimension)) || parseFloat(dom.currentStyle.msTransformOrigin.split(' ')[originIndex]) * 2) % 1;
14219             
14220             dom.style['position'] = inlinePosition;
14221             
14222             if (display === 'inline') {
14223                 dom.style['display'] = inlineDisplay;
14224             }
14225
14226             return floating;
14227         },
14228         
14229         /**
14230          * Returns the offset height of the element
14231          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
14232          * @return {Number} The element's height
14233          */
14234         getHeight: function(contentHeight, preciseHeight) {
14235             var me = this,
14236                 dom = me.dom,
14237                 hidden = Ext.isIE && me.isStyle('display', 'none'),
14238                 height, overflow, style, floating;
14239
14240             // IE Quirks mode acts more like a max-size measurement unless overflow is hidden during measurement.
14241             // We will put the overflow back to it's original value when we are done measuring.
14242             if (Ext.isIEQuirks) {
14243                 style = dom.style;
14244                 overflow = style.overflow;
14245                 me.setStyle({ overflow: 'hidden'});
14246             }
14247
14248             height = dom.offsetHeight;
14249
14250             height = MATH.max(height, hidden ? 0 : dom.clientHeight) || 0;
14251
14252             // IE9 Direct2D dimension rounding bug
14253             if (!hidden && Ext.supports.Direct2DBug) {
14254                 floating = me.adjustDirect2DDimension('height');
14255                 if (preciseHeight) {
14256                     height += floating;
14257                 }
14258                 else if (floating > 0 && floating < 0.5) {
14259                     height++;
14260                 }
14261             }
14262
14263             if (contentHeight) {
14264                 height -= (me.getBorderWidth("tb") + me.getPadding("tb"));
14265             }
14266
14267             if (Ext.isIEQuirks) {
14268                 me.setStyle({ overflow: overflow});
14269             }
14270
14271             if (height < 0) {
14272                 height = 0;
14273             }
14274             return height;
14275         },
14276                 
14277         /**
14278          * Returns the offset width of the element
14279          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
14280          * @return {Number} The element's width
14281          */
14282         getWidth: function(contentWidth, preciseWidth) {
14283             var me = this,
14284                 dom = me.dom,
14285                 hidden = Ext.isIE && me.isStyle('display', 'none'),
14286                 rect, width, overflow, style, floating, parentPosition;
14287
14288             // IE Quirks mode acts more like a max-size measurement unless overflow is hidden during measurement.
14289             // We will put the overflow back to it's original value when we are done measuring.
14290             if (Ext.isIEQuirks) {
14291                 style = dom.style;
14292                 overflow = style.overflow;
14293                 me.setStyle({overflow: 'hidden'});
14294             }
14295             
14296             // Fix Opera 10.5x width calculation issues 
14297             if (Ext.isOpera10_5) {
14298                 if (dom.parentNode.currentStyle.position === 'relative') {
14299                     parentPosition = dom.parentNode.style.position;
14300                     dom.parentNode.style.position = 'static';
14301                     width = dom.offsetWidth;
14302                     dom.parentNode.style.position = parentPosition;
14303                 }
14304                 width = Math.max(width || 0, dom.offsetWidth);
14305             
14306             // Gecko will in some cases report an offsetWidth that is actually less than the width of the
14307             // text contents, because it measures fonts with sub-pixel precision but rounds the calculated
14308             // value down. Using getBoundingClientRect instead of offsetWidth allows us to get the precise
14309             // subpixel measurements so we can force them to always be rounded up. See
14310             // https://bugzilla.mozilla.org/show_bug.cgi?id=458617
14311             } else if (Ext.supports.BoundingClientRect) {
14312                 rect = dom.getBoundingClientRect();
14313                 width = rect.right - rect.left;
14314                 width = preciseWidth ? width : Math.ceil(width);
14315             } else {
14316                 width = dom.offsetWidth;
14317             }
14318
14319             width = MATH.max(width, hidden ? 0 : dom.clientWidth) || 0;
14320
14321             // IE9 Direct2D dimension rounding bug
14322             if (!hidden && Ext.supports.Direct2DBug) {
14323                 floating = me.adjustDirect2DDimension('width');
14324                 if (preciseWidth) {
14325                     width += floating;
14326                 }
14327                 else if (floating > 0 && floating < 0.5) {
14328                     width++;
14329                 }
14330             }
14331             
14332             if (contentWidth) {
14333                 width -= (me.getBorderWidth("lr") + me.getPadding("lr"));
14334             }
14335             
14336             if (Ext.isIEQuirks) {
14337                 me.setStyle({ overflow: overflow});
14338             }
14339
14340             if (width < 0) {
14341                 width = 0;
14342             }
14343             return width;
14344         },
14345
14346         /**
14347          * Set the width of this Element.
14348          * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
14349          * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
14350          * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
14351          * </ul></div>
14352          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
14353          * @return {Ext.core.Element} this
14354          */
14355         setWidth : function(width, animate){
14356             var me = this;
14357             width = me.adjustWidth(width);
14358             if (!animate || !me.anim) {
14359                 me.dom.style.width = me.addUnits(width);
14360             }
14361             else {
14362                 if (!Ext.isObject(animate)) {
14363                     animate = {};
14364                 }
14365                 me.animate(Ext.applyIf({
14366                     to: {
14367                         width: width
14368                     }
14369                 }, animate));
14370             }
14371             return me;
14372         },
14373
14374         /**
14375          * Set the height of this Element.
14376          * <pre><code>
14377 // change the height to 200px and animate with default configuration
14378 Ext.fly('elementId').setHeight(200, true);
14379
14380 // change the height to 150px and animate with a custom configuration
14381 Ext.fly('elId').setHeight(150, {
14382     duration : .5, // animation will have a duration of .5 seconds
14383     // will change the content to "finished"
14384     callback: function(){ this.{@link #update}("finished"); }
14385 });
14386          * </code></pre>
14387          * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
14388          * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels.)</li>
14389          * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
14390          * </ul></div>
14391          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
14392          * @return {Ext.core.Element} this
14393          */
14394          setHeight : function(height, animate){
14395             var me = this;
14396             height = me.adjustHeight(height);
14397             if (!animate || !me.anim) {
14398                 me.dom.style.height = me.addUnits(height);
14399             }
14400             else {
14401                 if (!Ext.isObject(animate)) {
14402                     animate = {};
14403                 }
14404                 me.animate(Ext.applyIf({
14405                     to: {
14406                         height: height
14407                     }
14408                 }, animate));
14409             }
14410             return me;
14411         },
14412
14413         /**
14414          * Gets the width of the border(s) for the specified side(s)
14415          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
14416          * passing <tt>'lr'</tt> would get the border <b><u>l</u></b>eft width + the border <b><u>r</u></b>ight width.
14417          * @return {Number} The width of the sides passed added together
14418          */
14419         getBorderWidth : function(side){
14420             return this.addStyles(side, borders);
14421         },
14422
14423         /**
14424          * Gets the width of the padding(s) for the specified side(s)
14425          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
14426          * passing <tt>'lr'</tt> would get the padding <b><u>l</u></b>eft + the padding <b><u>r</u></b>ight.
14427          * @return {Number} The padding of the sides passed added together
14428          */
14429         getPadding : function(side){
14430             return this.addStyles(side, paddings);
14431         },
14432
14433         /**
14434          *  Store the current overflow setting and clip overflow on the element - use <tt>{@link #unclip}</tt> to remove
14435          * @return {Ext.core.Element} this
14436          */
14437         clip : function(){
14438             var me = this,
14439                 dom = me.dom;
14440
14441             if(!data(dom, ISCLIPPED)){
14442                 data(dom, ISCLIPPED, true);
14443                 data(dom, ORIGINALCLIP, {
14444                     o: me.getStyle(OVERFLOW),
14445                     x: me.getStyle(OVERFLOWX),
14446                     y: me.getStyle(OVERFLOWY)
14447                 });
14448                 me.setStyle(OVERFLOW, HIDDEN);
14449                 me.setStyle(OVERFLOWX, HIDDEN);
14450                 me.setStyle(OVERFLOWY, HIDDEN);
14451             }
14452             return me;
14453         },
14454
14455         /**
14456          *  Return clipping (overflow) to original clipping before <tt>{@link #clip}</tt> was called
14457          * @return {Ext.core.Element} this
14458          */
14459         unclip : function(){
14460             var me = this,
14461                 dom = me.dom,
14462                 clip;
14463
14464             if(data(dom, ISCLIPPED)){
14465                 data(dom, ISCLIPPED, false);
14466                 clip = data(dom, ORIGINALCLIP);
14467                 if(o.o){
14468                     me.setStyle(OVERFLOW, o.o);
14469                 }
14470                 if(o.x){
14471                     me.setStyle(OVERFLOWX, o.x);
14472                 }
14473                 if(o.y){
14474                     me.setStyle(OVERFLOWY, o.y);
14475                 }
14476             }
14477             return me;
14478         },
14479
14480         // private
14481         addStyles : function(sides, styles){
14482             var totalSize = 0,
14483                 sidesArr = sides.match(wordsRe),
14484                 i = 0,
14485                 len = sidesArr.length,
14486                 side, size;
14487             for (; i < len; i++) {
14488                 side = sidesArr[i];
14489                 size = side && parseInt(this.getStyle(styles[side]), 10);
14490                 if (size) {
14491                     totalSize += MATH.abs(size);
14492                 }
14493             }
14494             return totalSize;
14495         },
14496
14497         margins : margins,
14498         
14499         /**
14500          * More flexible version of {@link #setStyle} for setting style properties.
14501          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
14502          * a function which returns such a specification.
14503          * @return {Ext.core.Element} this
14504          */
14505         applyStyles : function(style){
14506             Ext.core.DomHelper.applyStyles(this.dom, style);
14507             return this;
14508         },
14509
14510         /**
14511          * Returns an object with properties matching the styles requested.
14512          * For example, el.getStyles('color', 'font-size', 'width') might return
14513          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
14514          * @param {String} style1 A style name
14515          * @param {String} style2 A style name
14516          * @param {String} etc.
14517          * @return {Object} The style object
14518          */
14519         getStyles : function(){
14520             var styles = {},
14521                 len = arguments.length,
14522                 i = 0, style;
14523                 
14524             for(; i < len; ++i) {
14525                 style = arguments[i];
14526                 styles[style] = this.getStyle(style);
14527             }
14528             return styles;
14529         },
14530
14531        /**
14532         * <p>Wraps the specified element with a special 9 element markup/CSS block that renders by default as
14533         * a gray container with a gradient background, rounded corners and a 4-way shadow.</p>
14534         * <p>This special markup is used throughout Ext when box wrapping elements ({@link Ext.button.Button},
14535         * {@link Ext.panel.Panel} when <tt>{@link Ext.panel.Panel#frame frame=true}</tt>, {@link Ext.window.Window}).  The markup
14536         * is of this form:</p>
14537         * <pre><code>
14538     Ext.core.Element.boxMarkup =
14539     &#39;&lt;div class="{0}-tl">&lt;div class="{0}-tr">&lt;div class="{0}-tc">&lt;/div>&lt;/div>&lt;/div>
14540      &lt;div class="{0}-ml">&lt;div class="{0}-mr">&lt;div class="{0}-mc">&lt;/div>&lt;/div>&lt;/div>
14541      &lt;div class="{0}-bl">&lt;div class="{0}-br">&lt;div class="{0}-bc">&lt;/div>&lt;/div>&lt;/div>&#39;;
14542         * </code></pre>
14543         * <p>Example usage:</p>
14544         * <pre><code>
14545     // Basic box wrap
14546     Ext.get("foo").boxWrap();
14547
14548     // You can also add a custom class and use CSS inheritance rules to customize the box look.
14549     // 'x-box-blue' is a built-in alternative -- look at the related CSS definitions as an example
14550     // for how to create a custom box wrap style.
14551     Ext.get("foo").boxWrap().addCls("x-box-blue");
14552         * </code></pre>
14553         * @param {String} class (optional) A base CSS class to apply to the containing wrapper element
14554         * (defaults to <tt>'x-box'</tt>). Note that there are a number of CSS rules that are dependent on
14555         * this name to make the overall effect work, so if you supply an alternate base class, make sure you
14556         * also supply all of the necessary rules.
14557         * @return {Ext.core.Element} The outermost wrapping element of the created box structure.
14558         */
14559         boxWrap : function(cls){
14560             cls = cls || Ext.baseCSSPrefix + 'box';
14561             var el = Ext.get(this.insertHtml("beforeBegin", "<div class='" + cls + "'>" + Ext.String.format(Ext.core.Element.boxMarkup, cls) + "</div>"));
14562             Ext.DomQuery.selectNode('.' + cls + '-mc', el.dom).appendChild(this.dom);
14563             return el;
14564         },
14565
14566         /**
14567          * Set the size of this Element. If animation is true, both width and height will be animated concurrently.
14568          * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
14569          * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
14570          * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
14571          * <li>A size object in the format <code>{width: widthValue, height: heightValue}</code>.</li>
14572          * </ul></div>
14573          * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
14574          * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels).</li>
14575          * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
14576          * </ul></div>
14577          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
14578          * @return {Ext.core.Element} this
14579          */
14580         setSize : function(width, height, animate){
14581             var me = this;
14582             if (Ext.isObject(width)) { // in case of object from getSize()
14583                 animate = height;
14584                 height = width.height;
14585                 width = width.width;
14586             }
14587             width = me.adjustWidth(width);
14588             height = me.adjustHeight(height);
14589             if(!animate || !me.anim){
14590                 me.dom.style.width = me.addUnits(width);
14591                 me.dom.style.height = me.addUnits(height);
14592             }
14593             else {
14594                 if (animate === true) {
14595                     animate = {};
14596                 }
14597                 me.animate(Ext.applyIf({
14598                     to: {
14599                         width: width,
14600                         height: height
14601                     }
14602                 }, animate));
14603             }
14604             return me;
14605         },
14606
14607         /**
14608          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
14609          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
14610          * if a height has not been set using CSS.
14611          * @return {Number}
14612          */
14613         getComputedHeight : function(){
14614             var me = this,
14615                 h = Math.max(me.dom.offsetHeight, me.dom.clientHeight);
14616             if(!h){
14617                 h = parseFloat(me.getStyle('height')) || 0;
14618                 if(!me.isBorderBox()){
14619                     h += me.getFrameWidth('tb');
14620                 }
14621             }
14622             return h;
14623         },
14624
14625         /**
14626          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
14627          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
14628          * if a width has not been set using CSS.
14629          * @return {Number}
14630          */
14631         getComputedWidth : function(){
14632             var me = this,
14633                 w = Math.max(me.dom.offsetWidth, me.dom.clientWidth);
14634                 
14635             if(!w){
14636                 w = parseFloat(me.getStyle('width')) || 0;
14637                 if(!me.isBorderBox()){
14638                     w += me.getFrameWidth('lr');
14639                 }
14640             }
14641             return w;
14642         },
14643
14644         /**
14645          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
14646          for more information about the sides.
14647          * @param {String} sides
14648          * @return {Number}
14649          */
14650         getFrameWidth : function(sides, onlyContentBox){
14651             return onlyContentBox && this.isBorderBox() ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
14652         },
14653
14654         /**
14655          * Sets up event handlers to add and remove a css class when the mouse is over this element
14656          * @param {String} className
14657          * @return {Ext.core.Element} this
14658          */
14659         addClsOnOver : function(className){
14660             var dom = this.dom;
14661             this.hover(
14662                 function(){
14663                     Ext.fly(dom, INTERNAL).addCls(className);
14664                 },
14665                 function(){
14666                     Ext.fly(dom, INTERNAL).removeCls(className);
14667                 }
14668             );
14669             return this;
14670         },
14671
14672         /**
14673          * Sets up event handlers to add and remove a css class when this element has the focus
14674          * @param {String} className
14675          * @return {Ext.core.Element} this
14676          */
14677         addClsOnFocus : function(className){
14678             var me = this,
14679                 dom = me.dom;
14680             me.on("focus", function(){
14681                 Ext.fly(dom, INTERNAL).addCls(className);
14682             });
14683             me.on("blur", function(){
14684                 Ext.fly(dom, INTERNAL).removeCls(className);
14685             });
14686             return me;
14687         },
14688
14689         /**
14690          * 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)
14691          * @param {String} className
14692          * @return {Ext.core.Element} this
14693          */
14694         addClsOnClick : function(className){
14695             var dom = this.dom;
14696             this.on("mousedown", function(){
14697                 Ext.fly(dom, INTERNAL).addCls(className);
14698                 var d = Ext.getDoc(),
14699                     fn = function(){
14700                         Ext.fly(dom, INTERNAL).removeCls(className);
14701                         d.removeListener("mouseup", fn);
14702                     };
14703                 d.on("mouseup", fn);
14704             });
14705             return this;
14706         },
14707
14708         /**
14709          * <p>Returns the dimensions of the element available to lay content out in.<p>
14710          * <p>If the element (or any ancestor element) has CSS style <code>display : none</code>, the dimensions will be zero.</p>
14711          * example:<pre><code>
14712         var vpSize = Ext.getBody().getViewSize();
14713
14714         // all Windows created afterwards will have a default value of 90% height and 95% width
14715         Ext.Window.override({
14716             width: vpSize.width * 0.9,
14717             height: vpSize.height * 0.95
14718         });
14719         // To handle window resizing you would have to hook onto onWindowResize.
14720         * </code></pre>
14721         *
14722         * getViewSize utilizes clientHeight/clientWidth which excludes sizing of scrollbars.
14723         * To obtain the size including scrollbars, use getStyleSize
14724         *
14725         * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
14726         */
14727
14728         getViewSize : function(){
14729             var me = this,
14730                 dom = me.dom,
14731                 isDoc = (dom == Ext.getDoc().dom || dom == Ext.getBody().dom),
14732                 style, overflow, ret;
14733
14734             // If the body, use static methods
14735             if (isDoc) {
14736                 ret = {
14737                     width : Ext.core.Element.getViewWidth(),
14738                     height : Ext.core.Element.getViewHeight()
14739                 };
14740
14741             // Else use clientHeight/clientWidth
14742             }
14743             else {
14744                 // IE 6 & IE Quirks mode acts more like a max-size measurement unless overflow is hidden during measurement.
14745                 // We will put the overflow back to it's original value when we are done measuring.
14746                 if (Ext.isIE6 || Ext.isIEQuirks) {
14747                     style = dom.style;
14748                     overflow = style.overflow;
14749                     me.setStyle({ overflow: 'hidden'});
14750                 }
14751                 ret = {
14752                     width : dom.clientWidth,
14753                     height : dom.clientHeight
14754                 };
14755                 if (Ext.isIE6 || Ext.isIEQuirks) {
14756                     me.setStyle({ overflow: overflow });
14757                 }
14758             }
14759             return ret;
14760         },
14761
14762         /**
14763         * <p>Returns the dimensions of the element available to lay content out in.<p>
14764         *
14765         * getStyleSize utilizes prefers style sizing if present, otherwise it chooses the larger of offsetHeight/clientHeight and offsetWidth/clientWidth.
14766         * To obtain the size excluding scrollbars, use getViewSize
14767         *
14768         * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
14769         */
14770
14771         getStyleSize : function(){
14772             var me = this,
14773                 doc = document,
14774                 d = this.dom,
14775                 isDoc = (d == doc || d == doc.body),
14776                 s = d.style,
14777                 w, h;
14778
14779             // If the body, use static methods
14780             if (isDoc) {
14781                 return {
14782                     width : Ext.core.Element.getViewWidth(),
14783                     height : Ext.core.Element.getViewHeight()
14784                 };
14785             }
14786             // Use Styles if they are set
14787             if(s.width && s.width != 'auto'){
14788                 w = parseFloat(s.width);
14789                 if(me.isBorderBox()){
14790                    w -= me.getFrameWidth('lr');
14791                 }
14792             }
14793             // Use Styles if they are set
14794             if(s.height && s.height != 'auto'){
14795                 h = parseFloat(s.height);
14796                 if(me.isBorderBox()){
14797                    h -= me.getFrameWidth('tb');
14798                 }
14799             }
14800             // Use getWidth/getHeight if style not set.
14801             return {width: w || me.getWidth(true), height: h || me.getHeight(true)};
14802         },
14803
14804         /**
14805          * Returns the size of the element.
14806          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
14807          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
14808          */
14809         getSize : function(contentSize){
14810             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
14811         },
14812
14813         /**
14814          * Forces the browser to repaint this element
14815          * @return {Ext.core.Element} this
14816          */
14817         repaint : function(){
14818             var dom = this.dom;
14819             this.addCls(Ext.baseCSSPrefix + 'repaint');
14820             setTimeout(function(){
14821                 Ext.fly(dom).removeCls(Ext.baseCSSPrefix + 'repaint');
14822             }, 1);
14823             return this;
14824         },
14825
14826         /**
14827          * Disables text selection for this element (normalized across browsers)
14828          * @return {Ext.core.Element} this
14829          */
14830         unselectable : function(){
14831             var me = this;
14832             me.dom.unselectable = "on";
14833
14834             me.swallowEvent("selectstart", true);
14835             me.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
14836             me.addCls(Ext.baseCSSPrefix + 'unselectable');
14837             
14838             return me;
14839         },
14840
14841         /**
14842          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
14843          * then it returns the calculated width of the sides (see getPadding)
14844          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
14845          * @return {Object/Number}
14846          */
14847         getMargin : function(side){
14848             var me = this,
14849                 hash = {t:"top", l:"left", r:"right", b: "bottom"},
14850                 o = {},
14851                 key;
14852
14853             if (!side) {
14854                 for (key in me.margins){
14855                     o[hash[key]] = parseFloat(me.getStyle(me.margins[key])) || 0;
14856                 }
14857                 return o;
14858             } else {
14859                 return me.addStyles.call(me, side, me.margins);
14860             }
14861         }
14862     });
14863 })();
14864 /**
14865  * @class Ext.core.Element
14866  */
14867 /**
14868  * Visibility mode constant for use with {@link #setVisibilityMode}. Use visibility to hide element
14869  * @static
14870  * @type Number
14871  */
14872 Ext.core.Element.VISIBILITY = 1;
14873 /**
14874  * Visibility mode constant for use with {@link #setVisibilityMode}. Use display to hide element
14875  * @static
14876  * @type Number
14877  */
14878 Ext.core.Element.DISPLAY = 2;
14879
14880 /**
14881  * Visibility mode constant for use with {@link #setVisibilityMode}. Use offsets (x and y positioning offscreen)
14882  * to hide element.
14883  * @static
14884  * @type Number
14885  */
14886 Ext.core.Element.OFFSETS = 3;
14887
14888
14889 Ext.core.Element.ASCLASS = 4;
14890
14891 /**
14892  * Defaults to 'x-hide-nosize'
14893  * @static
14894  * @type String
14895  */
14896 Ext.core.Element.visibilityCls = Ext.baseCSSPrefix + 'hide-nosize';
14897
14898 Ext.core.Element.addMethods(function(){
14899     var El = Ext.core.Element,
14900         OPACITY = "opacity",
14901         VISIBILITY = "visibility",
14902         DISPLAY = "display",
14903         HIDDEN = "hidden",
14904         OFFSETS = "offsets",
14905         ASCLASS = "asclass",
14906         NONE = "none",
14907         NOSIZE = 'nosize',
14908         ORIGINALDISPLAY = 'originalDisplay',
14909         VISMODE = 'visibilityMode',
14910         ISVISIBLE = 'isVisible',
14911         data = El.data,
14912         getDisplay = function(dom){
14913             var d = data(dom, ORIGINALDISPLAY);
14914             if(d === undefined){
14915                 data(dom, ORIGINALDISPLAY, d = '');
14916             }
14917             return d;
14918         },
14919         getVisMode = function(dom){
14920             var m = data(dom, VISMODE);
14921             if(m === undefined){
14922                 data(dom, VISMODE, m = 1);
14923             }
14924             return m;
14925         };
14926
14927     return {
14928         /**
14929          * The element's default display mode  (defaults to "")
14930          * @type String
14931          */
14932         originalDisplay : "",
14933         visibilityMode : 1,
14934
14935         /**
14936          * Sets the element's visibility mode. When setVisible() is called it
14937          * will use this to determine whether to set the visibility or the display property.
14938          * @param {Number} visMode Ext.core.Element.VISIBILITY or Ext.core.Element.DISPLAY
14939          * @return {Ext.core.Element} this
14940          */
14941         setVisibilityMode : function(visMode){
14942             data(this.dom, VISMODE, visMode);
14943             return this;
14944         },
14945
14946         /**
14947          * Checks whether the element is currently visible using both visibility and display properties.
14948          * @return {Boolean} True if the element is currently visible, else false
14949          */
14950         isVisible : function() {
14951             var me = this,
14952                 dom = me.dom,
14953                 visible = data(dom, ISVISIBLE);
14954
14955             if(typeof visible == 'boolean'){ //return the cached value if registered
14956                 return visible;
14957             }
14958             //Determine the current state based on display states
14959             visible = !me.isStyle(VISIBILITY, HIDDEN) &&
14960                       !me.isStyle(DISPLAY, NONE) &&
14961                       !((getVisMode(dom) == El.ASCLASS) && me.hasCls(me.visibilityCls || El.visibilityCls));
14962
14963             data(dom, ISVISIBLE, visible);
14964             return visible;
14965         },
14966
14967         /**
14968          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
14969          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
14970          * @param {Boolean} visible Whether the element is visible
14971          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
14972          * @return {Ext.core.Element} this
14973          */
14974         setVisible : function(visible, animate){
14975             var me = this, isDisplay, isVisibility, isOffsets, isNosize,
14976                 dom = me.dom,
14977                 visMode = getVisMode(dom);
14978
14979
14980             // hideMode string override
14981             if (typeof animate == 'string'){
14982                 switch (animate) {
14983                     case DISPLAY:
14984                         visMode = El.DISPLAY;
14985                         break;
14986                     case VISIBILITY:
14987                         visMode = El.VISIBILITY;
14988                         break;
14989                     case OFFSETS:
14990                         visMode = El.OFFSETS;
14991                         break;
14992                     case NOSIZE:
14993                     case ASCLASS:
14994                         visMode = El.ASCLASS;
14995                         break;
14996                 }
14997                 me.setVisibilityMode(visMode);
14998                 animate = false;
14999             }
15000
15001             if (!animate || !me.anim) {
15002                 if(visMode == El.ASCLASS ){
15003
15004                     me[visible?'removeCls':'addCls'](me.visibilityCls || El.visibilityCls);
15005
15006                 } else if (visMode == El.DISPLAY){
15007
15008                     return me.setDisplayed(visible);
15009
15010                 } else if (visMode == El.OFFSETS){
15011
15012                     if (!visible){
15013                         // Remember position for restoring, if we are not already hidden by offsets.
15014                         if (!me.hideModeStyles) {
15015                             me.hideModeStyles = {
15016                                 position: me.getStyle('position'),
15017                                 top: me.getStyle('top'),
15018                                 left: me.getStyle('left')
15019                             };
15020                         }
15021                         me.applyStyles({position: 'absolute', top: '-10000px', left: '-10000px'});
15022                     }
15023
15024                     // Only "restore" as position if we have actually been hidden using offsets.
15025                     // Calling setVisible(true) on a positioned element should not reposition it.
15026                     else if (me.hideModeStyles) {
15027                         me.applyStyles(me.hideModeStyles || {position: '', top: '', left: ''});
15028                         delete me.hideModeStyles;
15029                     }
15030
15031                 }else{
15032                     me.fixDisplay();
15033                     // Show by clearing visibility style. Explicitly setting to "visible" overrides parent visibility setting.
15034                     dom.style.visibility = visible ? '' : HIDDEN;
15035                 }
15036             }else{
15037                 // closure for composites
15038                 if(visible){
15039                     me.setOpacity(0.01);
15040                     me.setVisible(true);
15041                 }
15042                 if (!Ext.isObject(animate)) {
15043                     animate = {
15044                         duration: 350,
15045                         easing: 'ease-in'
15046                     };
15047                 }
15048                 me.animate(Ext.applyIf({
15049                     callback: function() {
15050                         visible || me.setVisible(false).setOpacity(1);
15051                     },
15052                     to: {
15053                         opacity: (visible) ? 1 : 0
15054                     }
15055                 }, animate));
15056             }
15057             data(dom, ISVISIBLE, visible);  //set logical visibility state
15058             return me;
15059         },
15060
15061
15062         /**
15063          * @private
15064          * Determine if the Element has a relevant height and width available based
15065          * upon current logical visibility state
15066          */
15067         hasMetrics  : function(){
15068             var dom = this.dom;
15069             return this.isVisible() || (getVisMode(dom) == El.OFFSETS) || (getVisMode(dom) == El.VISIBILITY);
15070         },
15071
15072         /**
15073          * Toggles the element's visibility or display, depending on visibility mode.
15074          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
15075          * @return {Ext.core.Element} this
15076          */
15077         toggle : function(animate){
15078             var me = this;
15079             me.setVisible(!me.isVisible(), me.anim(animate));
15080             return me;
15081         },
15082
15083         /**
15084          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
15085          * @param {Mixed} value Boolean value to display the element using its default display, or a string to set the display directly.
15086          * @return {Ext.core.Element} this
15087          */
15088         setDisplayed : function(value) {
15089             if(typeof value == "boolean"){
15090                value = value ? getDisplay(this.dom) : NONE;
15091             }
15092             this.setStyle(DISPLAY, value);
15093             return this;
15094         },
15095
15096         // private
15097         fixDisplay : function(){
15098             var me = this;
15099             if (me.isStyle(DISPLAY, NONE)) {
15100                 me.setStyle(VISIBILITY, HIDDEN);
15101                 me.setStyle(DISPLAY, getDisplay(this.dom)); // first try reverting to default
15102                 if (me.isStyle(DISPLAY, NONE)) { // if that fails, default to block
15103                     me.setStyle(DISPLAY, "block");
15104                 }
15105             }
15106         },
15107
15108         /**
15109          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
15110          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
15111          * @return {Ext.core.Element} this
15112          */
15113         hide : function(animate){
15114             // hideMode override
15115             if (typeof animate == 'string'){
15116                 this.setVisible(false, animate);
15117                 return this;
15118             }
15119             this.setVisible(false, this.anim(animate));
15120             return this;
15121         },
15122
15123         /**
15124         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
15125         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
15126          * @return {Ext.core.Element} this
15127          */
15128         show : function(animate){
15129             // hideMode override
15130             if (typeof animate == 'string'){
15131                 this.setVisible(true, animate);
15132                 return this;
15133             }
15134             this.setVisible(true, this.anim(animate));
15135             return this;
15136         }
15137     };
15138 }());
15139 /**
15140  * @class Ext.core.Element
15141  */
15142 Ext.applyIf(Ext.core.Element.prototype, {
15143     // @private override base Ext.util.Animate mixin for animate for backwards compatibility
15144     animate: function(config) {
15145         var me = this;
15146         if (!me.id) {
15147             me = Ext.get(me.dom);
15148         }
15149         if (Ext.fx.Manager.hasFxBlock(me.id)) {
15150             return me;
15151         }
15152         Ext.fx.Manager.queueFx(Ext.create('Ext.fx.Anim', me.anim(config)));
15153         return this;
15154     },
15155
15156     // @private override base Ext.util.Animate mixin for animate for backwards compatibility
15157     anim: function(config) {
15158         if (!Ext.isObject(config)) {
15159             return (config) ? {} : false;
15160         }
15161
15162         var me = this,
15163             duration = config.duration || Ext.fx.Anim.prototype.duration,
15164             easing = config.easing || 'ease',
15165             animConfig;
15166
15167         if (config.stopAnimation) {
15168             me.stopAnimation();
15169         }
15170
15171         Ext.applyIf(config, Ext.fx.Manager.getFxDefaults(me.id));
15172
15173         // Clear any 'paused' defaults.
15174         Ext.fx.Manager.setFxDefaults(me.id, {
15175             delay: 0
15176         });
15177
15178         animConfig = {
15179             target: me,
15180             remove: config.remove,
15181             alternate: config.alternate || false,
15182             duration: duration,
15183             easing: easing,
15184             callback: config.callback,
15185             listeners: config.listeners,
15186             iterations: config.iterations || 1,
15187             scope: config.scope,
15188             block: config.block,
15189             concurrent: config.concurrent,
15190             delay: config.delay || 0,
15191             paused: true,
15192             keyframes: config.keyframes,
15193             from: config.from || {},
15194             to: Ext.apply({}, config)
15195         };
15196         Ext.apply(animConfig.to, config.to);
15197
15198         // Anim API properties - backward compat
15199         delete animConfig.to.to;
15200         delete animConfig.to.from;
15201         delete animConfig.to.remove;
15202         delete animConfig.to.alternate;
15203         delete animConfig.to.keyframes;
15204         delete animConfig.to.iterations;
15205         delete animConfig.to.listeners;
15206         delete animConfig.to.target;
15207         delete animConfig.to.paused;
15208         delete animConfig.to.callback;
15209         delete animConfig.to.scope;
15210         delete animConfig.to.duration;
15211         delete animConfig.to.easing;
15212         delete animConfig.to.concurrent;
15213         delete animConfig.to.block;
15214         delete animConfig.to.stopAnimation;
15215         delete animConfig.to.delay;
15216         return animConfig;
15217     },
15218
15219     /**
15220      * Slides the element into view.  An anchor point can be optionally passed to set the point of
15221      * origin for the slide effect.  This function automatically handles wrapping the element with
15222      * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
15223      * Usage:
15224      *<pre><code>
15225 // default: slide the element in from the top
15226 el.slideIn();
15227
15228 // custom: slide the element in from the right with a 2-second duration
15229 el.slideIn('r', { duration: 2 });
15230
15231 // common config options shown with default values
15232 el.slideIn('t', {
15233     easing: 'easeOut',
15234     duration: 500
15235 });
15236 </code></pre>
15237      * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
15238      * @param {Object} options (optional) Object literal with any of the Fx config options
15239      * @return {Ext.core.Element} The Element
15240      */
15241     slideIn: function(anchor, obj, slideOut) { 
15242         var me = this,
15243             elStyle = me.dom.style,
15244             beforeAnim, wrapAnim;
15245
15246         anchor = anchor || "t";
15247         obj = obj || {};
15248
15249         beforeAnim = function() {
15250             var animScope = this,
15251                 listeners = obj.listeners,
15252                 box, position, restoreSize, wrap, anim;
15253
15254             if (!slideOut) {
15255                 me.fixDisplay();
15256             }
15257
15258             box = me.getBox();
15259             if ((anchor == 't' || anchor == 'b') && box.height == 0) {
15260                 box.height = me.dom.scrollHeight;
15261             }
15262             else if ((anchor == 'l' || anchor == 'r') && box.width == 0) {
15263                 box.width = me.dom.scrollWidth;
15264             }
15265             
15266             position = me.getPositioning();
15267             me.setSize(box.width, box.height);
15268
15269             wrap = me.wrap({
15270                 style: {
15271                     visibility: slideOut ? 'visible' : 'hidden'
15272                 }
15273             });
15274             wrap.setPositioning(position);
15275             if (wrap.isStyle('position', 'static')) {
15276                 wrap.position('relative');
15277             }
15278             me.clearPositioning('auto');
15279             wrap.clip();
15280
15281             // This element is temporarily positioned absolute within its wrapper.
15282             // Restore to its default, CSS-inherited visibility setting.
15283             // We cannot explicitly poke visibility:visible into its style because that overrides the visibility of the wrap.
15284             me.setStyle({
15285                 visibility: '',
15286                 position: 'absolute'
15287             });
15288             if (slideOut) {
15289                 wrap.setSize(box.width, box.height);
15290             }
15291
15292             switch (anchor) {
15293                 case 't':
15294                     anim = {
15295                         from: {
15296                             width: box.width + 'px',
15297                             height: '0px'
15298                         },
15299                         to: {
15300                             width: box.width + 'px',
15301                             height: box.height + 'px'
15302                         }
15303                     };
15304                     elStyle.bottom = '0px';
15305                     break;
15306                 case 'l':
15307                     anim = {
15308                         from: {
15309                             width: '0px',
15310                             height: box.height + 'px'
15311                         },
15312                         to: {
15313                             width: box.width + 'px',
15314                             height: box.height + 'px'
15315                         }
15316                     };
15317                     elStyle.right = '0px';
15318                     break;
15319                 case 'r':
15320                     anim = {
15321                         from: {
15322                             x: box.x + box.width,
15323                             width: '0px',
15324                             height: box.height + 'px'
15325                         },
15326                         to: {
15327                             x: box.x,
15328                             width: box.width + 'px',
15329                             height: box.height + 'px'
15330                         }
15331                     };
15332                     break;
15333                 case 'b':
15334                     anim = {
15335                         from: {
15336                             y: box.y + box.height,
15337                             width: box.width + 'px',
15338                             height: '0px'
15339                         },
15340                         to: {
15341                             y: box.y,
15342                             width: box.width + 'px',
15343                             height: box.height + 'px'
15344                         }
15345                     };
15346                     break;
15347                 case 'tl':
15348                     anim = {
15349                         from: {
15350                             x: box.x,
15351                             y: box.y,
15352                             width: '0px',
15353                             height: '0px'
15354                         },
15355                         to: {
15356                             width: box.width + 'px',
15357                             height: box.height + 'px'
15358                         }
15359                     };
15360                     elStyle.bottom = '0px';
15361                     elStyle.right = '0px';
15362                     break;
15363                 case 'bl':
15364                     anim = {
15365                         from: {
15366                             x: box.x + box.width,
15367                             width: '0px',
15368                             height: '0px'
15369                         },
15370                         to: {
15371                             x: box.x,
15372                             width: box.width + 'px',
15373                             height: box.height + 'px'
15374                         }
15375                     };
15376                     elStyle.right = '0px';
15377                     break;
15378                 case 'br':
15379                     anim = {
15380                         from: {
15381                             x: box.x + box.width,
15382                             y: box.y + box.height,
15383                             width: '0px',
15384                             height: '0px'
15385                         },
15386                         to: {
15387                             x: box.x,
15388                             y: box.y,
15389                             width: box.width + 'px',
15390                             height: box.height + 'px'
15391                         }
15392                     };
15393                     break;
15394                 case 'tr':
15395                     anim = {
15396                         from: {
15397                             y: box.y + box.height,
15398                             width: '0px',
15399                             height: '0px'
15400                         },
15401                         to: {
15402                             y: box.y,
15403                             width: box.width + 'px',
15404                             height: box.height + 'px'
15405                         }
15406                     };
15407                     elStyle.bottom = '0px';
15408                     break;
15409             }
15410
15411             wrap.show();
15412             wrapAnim = Ext.apply({}, obj);
15413             delete wrapAnim.listeners;
15414             wrapAnim = Ext.create('Ext.fx.Anim', Ext.applyIf(wrapAnim, {
15415                 target: wrap,
15416                 duration: 500,
15417                 easing: 'ease-out',
15418                 from: slideOut ? anim.to : anim.from,
15419                 to: slideOut ? anim.from : anim.to
15420             }));
15421
15422             // In the absence of a callback, this listener MUST be added first
15423             wrapAnim.on('afteranimate', function() {
15424                 if (slideOut) {
15425                     me.setPositioning(position);
15426                     if (obj.useDisplay) {
15427                         me.setDisplayed(false);
15428                     } else {
15429                         me.hide();   
15430                     }
15431                 }
15432                 else {
15433                     me.clearPositioning();
15434                     me.setPositioning(position);
15435                 }
15436                 if (wrap.dom) {
15437                     wrap.dom.parentNode.insertBefore(me.dom, wrap.dom); 
15438                     wrap.remove();
15439                 }
15440                 me.setSize(box.width, box.height);
15441                 animScope.end();
15442             });
15443             // Add configured listeners after
15444             if (listeners) {
15445                 wrapAnim.on(listeners);
15446             }
15447         };
15448
15449         me.animate({
15450             duration: obj.duration ? obj.duration * 2 : 1000,
15451             listeners: {
15452                 beforeanimate: {
15453                     fn: beforeAnim
15454                 },
15455                 afteranimate: {
15456                     fn: function() {
15457                         if (wrapAnim && wrapAnim.running) {
15458                             wrapAnim.end();
15459                         }
15460                     }
15461                 }
15462             }
15463         });
15464         return me;
15465     },
15466
15467     
15468     /**
15469      * Slides the element out of view.  An anchor point can be optionally passed to set the end point
15470      * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
15471      * 'hidden') but block elements will still take up space in the document.  The element must be removed
15472      * from the DOM using the 'remove' config option if desired.  This function automatically handles 
15473      * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
15474      * Usage:
15475      *<pre><code>
15476 // default: slide the element out to the top
15477 el.slideOut();
15478
15479 // custom: slide the element out to the right with a 2-second duration
15480 el.slideOut('r', { duration: 2 });
15481
15482 // common config options shown with default values
15483 el.slideOut('t', {
15484     easing: 'easeOut',
15485     duration: 500,
15486     remove: false,
15487     useDisplay: false
15488 });
15489 </code></pre>
15490      * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
15491      * @param {Object} options (optional) Object literal with any of the Fx config options
15492      * @return {Ext.core.Element} The Element
15493      */
15494     slideOut: function(anchor, o) {
15495         return this.slideIn(anchor, o, true);
15496     },
15497
15498     /**
15499      * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
15500      * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document.
15501      * Usage:
15502      *<pre><code>
15503 // default
15504 el.puff();
15505
15506 // common config options shown with default values
15507 el.puff({
15508     easing: 'easeOut',
15509     duration: 500,
15510     useDisplay: false
15511 });
15512 </code></pre>
15513      * @param {Object} options (optional) Object literal with any of the Fx config options
15514      * @return {Ext.core.Element} The Element
15515      */
15516
15517     puff: function(obj) {
15518         var me = this,
15519             beforeAnim;
15520         obj = Ext.applyIf(obj || {}, {
15521             easing: 'ease-out',
15522             duration: 500,
15523             useDisplay: false
15524         });
15525
15526         beforeAnim = function() {
15527             me.clearOpacity();
15528             me.show();
15529
15530             var box = me.getBox(),
15531                 fontSize = me.getStyle('fontSize'),
15532                 position = me.getPositioning();
15533             this.to = {
15534                 width: box.width * 2,
15535                 height: box.height * 2,
15536                 x: box.x - (box.width / 2),
15537                 y: box.y - (box.height /2),
15538                 opacity: 0,
15539                 fontSize: '200%'
15540             };
15541             this.on('afteranimate',function() {
15542                 if (me.dom) {
15543                     if (obj.useDisplay) {
15544                         me.setDisplayed(false);
15545                     } else {
15546                         me.hide();
15547                     }
15548                     me.clearOpacity();  
15549                     me.setPositioning(position);
15550                     me.setStyle({fontSize: fontSize});
15551                 }
15552             });
15553         };
15554
15555         me.animate({
15556             duration: obj.duration,
15557             easing: obj.easing,
15558             listeners: {
15559                 beforeanimate: {
15560                     fn: beforeAnim
15561                 }
15562             }
15563         });
15564         return me;
15565     },
15566
15567     /**
15568      * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
15569      * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
15570      * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
15571      * Usage:
15572      *<pre><code>
15573 // default
15574 el.switchOff();
15575
15576 // all config options shown with default values
15577 el.switchOff({
15578     easing: 'easeIn',
15579     duration: .3,
15580     remove: false,
15581     useDisplay: false
15582 });
15583 </code></pre>
15584      * @param {Object} options (optional) Object literal with any of the Fx config options
15585      * @return {Ext.core.Element} The Element
15586      */
15587     switchOff: function(obj) {
15588         var me = this,
15589             beforeAnim;
15590         
15591         obj = Ext.applyIf(obj || {}, {
15592             easing: 'ease-in',
15593             duration: 500,
15594             remove: false,
15595             useDisplay: false
15596         });
15597
15598         beforeAnim = function() {
15599             var animScope = this,
15600                 size = me.getSize(),
15601                 xy = me.getXY(),
15602                 keyframe, position;
15603             me.clearOpacity();
15604             me.clip();
15605             position = me.getPositioning();
15606
15607             keyframe = Ext.create('Ext.fx.Animator', {
15608                 target: me,
15609                 duration: obj.duration,
15610                 easing: obj.easing,
15611                 keyframes: {
15612                     33: {
15613                         opacity: 0.3
15614                     },
15615                     66: {
15616                         height: 1,
15617                         y: xy[1] + size.height / 2
15618                     },
15619                     100: {
15620                         width: 1,
15621                         x: xy[0] + size.width / 2
15622                     }
15623                 }
15624             });
15625             keyframe.on('afteranimate', function() {
15626                 if (obj.useDisplay) {
15627                     me.setDisplayed(false);
15628                 } else {
15629                     me.hide();
15630                 }  
15631                 me.clearOpacity();
15632                 me.setPositioning(position);
15633                 me.setSize(size);
15634                 animScope.end();
15635             });
15636         };
15637         me.animate({
15638             duration: (obj.duration * 2),
15639             listeners: {
15640                 beforeanimate: {
15641                     fn: beforeAnim
15642                 }
15643             }
15644         });
15645         return me;
15646     },
15647
15648    /**
15649     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
15650     * Usage:
15651 <pre><code>
15652 // default: a single light blue ripple
15653 el.frame();
15654
15655 // custom: 3 red ripples lasting 3 seconds total
15656 el.frame("#ff0000", 3, { duration: 3 });
15657
15658 // common config options shown with default values
15659 el.frame("#C3DAF9", 1, {
15660     duration: 1 //duration of each individual ripple.
15661     // Note: Easing is not configurable and will be ignored if included
15662 });
15663 </code></pre>
15664     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
15665     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
15666     * @param {Object} options (optional) Object literal with any of the Fx config options
15667     * @return {Ext.core.Element} The Element
15668     */
15669     frame : function(color, count, obj){
15670         var me = this,
15671             beforeAnim;
15672
15673         color = color || '#C3DAF9';
15674         count = count || 1;
15675         obj = obj || {};
15676
15677         beforeAnim = function() {
15678             me.show();
15679             var animScope = this,
15680                 box = me.getBox(),
15681                 proxy = Ext.getBody().createChild({
15682                     style: {
15683                         position : 'absolute',
15684                         'pointer-events': 'none',
15685                         'z-index': 35000,
15686                         border : '0px solid ' + color
15687                     }
15688                 }),
15689                 proxyAnim;
15690             proxyAnim = Ext.create('Ext.fx.Anim', {
15691                 target: proxy,
15692                 duration: obj.duration || 1000,
15693                 iterations: count,
15694                 from: {
15695                     top: box.y,
15696                     left: box.x,
15697                     borderWidth: 0,
15698                     opacity: 1,
15699                     height: box.height,
15700                     width: box.width
15701                 },
15702                 to: {
15703                     top: box.y - 20,
15704                     left: box.x - 20,
15705                     borderWidth: 10,
15706                     opacity: 0,
15707                     height: box.height + 40,
15708                     width: box.width + 40
15709                 }
15710             });
15711             proxyAnim.on('afteranimate', function() {
15712                 proxy.remove();
15713                 animScope.end();
15714             });
15715         };
15716
15717         me.animate({
15718             duration: (obj.duration * 2) || 2000,
15719             listeners: {
15720                 beforeanimate: {
15721                     fn: beforeAnim
15722                 }
15723             }
15724         });
15725         return me;
15726     },
15727
15728     /**
15729      * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
15730      * ending point of the effect.
15731      * Usage:
15732      *<pre><code>
15733 // default: slide the element downward while fading out
15734 el.ghost();
15735
15736 // custom: slide the element out to the right with a 2-second duration
15737 el.ghost('r', { duration: 2 });
15738
15739 // common config options shown with default values
15740 el.ghost('b', {
15741     easing: 'easeOut',
15742     duration: 500
15743 });
15744 </code></pre>
15745      * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
15746      * @param {Object} options (optional) Object literal with any of the Fx config options
15747      * @return {Ext.core.Element} The Element
15748      */
15749     ghost: function(anchor, obj) {
15750         var me = this,
15751             beforeAnim;
15752
15753         anchor = anchor || "b";
15754         beforeAnim = function() {
15755             var width = me.getWidth(),
15756                 height = me.getHeight(),
15757                 xy = me.getXY(),
15758                 position = me.getPositioning(),
15759                 to = {
15760                     opacity: 0
15761                 };
15762             switch (anchor) {
15763                 case 't':
15764                     to.y = xy[1] - height;
15765                     break;
15766                 case 'l':
15767                     to.x = xy[0] - width;
15768                     break;
15769                 case 'r':
15770                     to.x = xy[0] + width;
15771                     break;
15772                 case 'b':
15773                     to.y = xy[1] + height;
15774                     break;
15775                 case 'tl':
15776                     to.x = xy[0] - width;
15777                     to.y = xy[1] - height;
15778                     break;
15779                 case 'bl':
15780                     to.x = xy[0] - width;
15781                     to.y = xy[1] + height;
15782                     break;
15783                 case 'br':
15784                     to.x = xy[0] + width;
15785                     to.y = xy[1] + height;
15786                     break;
15787                 case 'tr':
15788                     to.x = xy[0] + width;
15789                     to.y = xy[1] - height;
15790                     break;
15791             }
15792             this.to = to;
15793             this.on('afteranimate', function () {
15794                 if (me.dom) {
15795                     me.hide();
15796                     me.clearOpacity();
15797                     me.setPositioning(position);
15798                 }
15799             });
15800         };
15801
15802         me.animate(Ext.applyIf(obj || {}, {
15803             duration: 500,
15804             easing: 'ease-out',
15805             listeners: {
15806                 beforeanimate: {
15807                     fn: beforeAnim
15808                 }
15809             }
15810         }));
15811         return me;
15812     },
15813
15814     /**
15815      * Highlights the Element by setting a color (applies to the background-color by default, but can be
15816      * changed using the "attr" config option) and then fading back to the original color. If no original
15817      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
15818      * Usage:
15819 <pre><code>
15820 // default: highlight background to yellow
15821 el.highlight();
15822
15823 // custom: highlight foreground text to blue for 2 seconds
15824 el.highlight("0000ff", { attr: 'color', duration: 2 });
15825
15826 // common config options shown with default values
15827 el.highlight("ffff9c", {
15828     attr: "backgroundColor", //can be any valid CSS property (attribute) that supports a color value
15829     endColor: (current color) or "ffffff",
15830     easing: 'easeIn',
15831     duration: 1000
15832 });
15833 </code></pre>
15834      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
15835      * @param {Object} options (optional) Object literal with any of the Fx config options
15836      * @return {Ext.core.Element} The Element
15837      */ 
15838     highlight: function(color, o) {
15839         var me = this,
15840             dom = me.dom,
15841             from = {},
15842             restore, to, attr, lns, event, fn;
15843
15844         o = o || {};
15845         lns = o.listeners || {};
15846         attr = o.attr || 'backgroundColor';
15847         from[attr] = color || 'ffff9c';
15848         
15849         if (!o.to) {
15850             to = {};
15851             to[attr] = o.endColor || me.getColor(attr, 'ffffff', '');
15852         }
15853         else {
15854             to = o.to;
15855         }
15856         
15857         // Don't apply directly on lns, since we reference it in our own callbacks below
15858         o.listeners = Ext.apply(Ext.apply({}, lns), {
15859             beforeanimate: function() {
15860                 restore = dom.style[attr];
15861                 me.clearOpacity();
15862                 me.show();
15863                 
15864                 event = lns.beforeanimate;
15865                 if (event) {
15866                     fn = event.fn || event;
15867                     return fn.apply(event.scope || lns.scope || window, arguments);
15868                 }
15869             },
15870             afteranimate: function() {
15871                 if (dom) {
15872                     dom.style[attr] = restore;
15873                 }
15874                 
15875                 event = lns.afteranimate;
15876                 if (event) {
15877                     fn = event.fn || event;
15878                     fn.apply(event.scope || lns.scope || window, arguments);
15879                 }
15880             }
15881         });
15882
15883         me.animate(Ext.apply({}, o, {
15884             duration: 1000,
15885             easing: 'ease-in',
15886             from: from,
15887             to: to
15888         }));
15889         return me;
15890     },
15891
15892    /**
15893     * @deprecated 4.0
15894     * Creates a pause before any subsequent queued effects begin.  If there are
15895     * no effects queued after the pause it will have no effect.
15896     * Usage:
15897 <pre><code>
15898 el.pause(1);
15899 </code></pre>
15900     * @param {Number} seconds The length of time to pause (in seconds)
15901     * @return {Ext.Element} The Element
15902     */
15903     pause: function(ms) {
15904         var me = this;
15905         Ext.fx.Manager.setFxDefaults(me.id, {
15906             delay: ms
15907         });
15908         return me;
15909     },
15910
15911    /**
15912     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
15913     * using the <tt>{@link #endOpacity}</tt> config option.
15914     * Usage:
15915 <pre><code>
15916 // default: fade in from opacity 0 to 100%
15917 el.fadeIn();
15918
15919 // custom: fade in from opacity 0 to 75% over 2 seconds
15920 el.fadeIn({ endOpacity: .75, duration: 2});
15921
15922 // common config options shown with default values
15923 el.fadeIn({
15924     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
15925     easing: 'easeOut',
15926     duration: 500
15927 });
15928 </code></pre>
15929     * @param {Object} options (optional) Object literal with any of the Fx config options
15930     * @return {Ext.Element} The Element
15931     */
15932     fadeIn: function(o) {
15933         this.animate(Ext.apply({}, o, {
15934             opacity: 1
15935         }));
15936         return this;
15937     },
15938
15939    /**
15940     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
15941     * using the <tt>{@link #endOpacity}</tt> config option.  Note that IE may require
15942     * <tt>{@link #useDisplay}:true</tt> in order to redisplay correctly.
15943     * Usage:
15944 <pre><code>
15945 // default: fade out from the element's current opacity to 0
15946 el.fadeOut();
15947
15948 // custom: fade out from the element's current opacity to 25% over 2 seconds
15949 el.fadeOut({ endOpacity: .25, duration: 2});
15950
15951 // common config options shown with default values
15952 el.fadeOut({
15953     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
15954     easing: 'easeOut',
15955     duration: 500,
15956     remove: false,
15957     useDisplay: false
15958 });
15959 </code></pre>
15960     * @param {Object} options (optional) Object literal with any of the Fx config options
15961     * @return {Ext.Element} The Element
15962     */
15963     fadeOut: function(o) {
15964         this.animate(Ext.apply({}, o, {
15965             opacity: 0
15966         }));
15967         return this;
15968     },
15969
15970    /**
15971     * @deprecated 4.0
15972     * Animates the transition of an element's dimensions from a starting height/width
15973     * to an ending height/width.  This method is a convenience implementation of {@link #shift}.
15974     * Usage:
15975 <pre><code>
15976 // change height and width to 100x100 pixels
15977 el.scale(100, 100);
15978
15979 // common config options shown with default values.  The height and width will default to
15980 // the element&#39;s existing values if passed as null.
15981 el.scale(
15982     [element&#39;s width],
15983     [element&#39;s height], {
15984         easing: 'easeOut',
15985         duration: .35
15986     }
15987 );
15988 </code></pre>
15989     * @param {Number} width  The new width (pass undefined to keep the original width)
15990     * @param {Number} height  The new height (pass undefined to keep the original height)
15991     * @param {Object} options (optional) Object literal with any of the Fx config options
15992     * @return {Ext.Element} The Element
15993     */
15994     scale: function(w, h, o) {
15995         this.animate(Ext.apply({}, o, {
15996             width: w,
15997             height: h
15998         }));
15999         return this;
16000     },
16001
16002    /**
16003     * @deprecated 4.0
16004     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
16005     * Any of these properties not specified in the config object will not be changed.  This effect 
16006     * requires that at least one new dimension, position or opacity setting must be passed in on
16007     * the config object in order for the function to have any effect.
16008     * Usage:
16009 <pre><code>
16010 // slide the element horizontally to x position 200 while changing the height and opacity
16011 el.shift({ x: 200, height: 50, opacity: .8 });
16012
16013 // common config options shown with default values.
16014 el.shift({
16015     width: [element&#39;s width],
16016     height: [element&#39;s height],
16017     x: [element&#39;s x position],
16018     y: [element&#39;s y position],
16019     opacity: [element&#39;s opacity],
16020     easing: 'easeOut',
16021     duration: .35
16022 });
16023 </code></pre>
16024     * @param {Object} options  Object literal with any of the Fx config options
16025     * @return {Ext.Element} The Element
16026     */
16027     shift: function(config) {
16028         this.animate(config);
16029         return this;
16030     }
16031 });
16032
16033 /**
16034  * @class Ext.core.Element
16035  */
16036 Ext.applyIf(Ext.core.Element, {
16037     unitRe: /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
16038     camelRe: /(-[a-z])/gi,
16039     opacityRe: /alpha\(opacity=(.*)\)/i,
16040     cssRe: /([a-z0-9-]+)\s*:\s*([^;\s]+(?:\s*[^;\s]+)*);?/gi,
16041     propertyCache: {},
16042     defaultUnit : "px",
16043     borders: {l: 'border-left-width', r: 'border-right-width', t: 'border-top-width', b: 'border-bottom-width'},
16044     paddings: {l: 'padding-left', r: 'padding-right', t: 'padding-top', b: 'padding-bottom'},
16045     margins: {l: 'margin-left', r: 'margin-right', t: 'margin-top', b: 'margin-bottom'},
16046
16047     // Reference the prototype's version of the method. Signatures are identical.
16048     addUnits : Ext.core.Element.prototype.addUnits,
16049
16050     /**
16051      * Parses a number or string representing margin sizes into an object. Supports CSS-style margin declarations
16052      * (e.g. 10, "10", "10 10", "10 10 10" and "10 10 10 10" are all valid options and would return the same result)
16053      * @static
16054      * @param {Number|String} box The encoded margins
16055      * @return {Object} An object with margin sizes for top, right, bottom and left
16056      */
16057     parseBox : function(box) {
16058         if (Ext.isObject(box)) {
16059             return {
16060                 top: box.top || 0,
16061                 right: box.right || 0,
16062                 bottom: box.bottom || 0,
16063                 left: box.left || 0
16064             };
16065         } else {
16066             if (typeof box != 'string') {
16067                 box = box.toString();
16068             }
16069             var parts  = box.split(' '),
16070                 ln = parts.length;
16071     
16072             if (ln == 1) {
16073                 parts[1] = parts[2] = parts[3] = parts[0];
16074             }
16075             else if (ln == 2) {
16076                 parts[2] = parts[0];
16077                 parts[3] = parts[1];
16078             }
16079             else if (ln == 3) {
16080                 parts[3] = parts[1];
16081             }
16082     
16083             return {
16084                 top   :parseFloat(parts[0]) || 0,
16085                 right :parseFloat(parts[1]) || 0,
16086                 bottom:parseFloat(parts[2]) || 0,
16087                 left  :parseFloat(parts[3]) || 0
16088             };
16089         }
16090         
16091     },
16092     
16093     /**
16094      * Parses a number or string representing margin sizes into an object. Supports CSS-style margin declarations
16095      * (e.g. 10, "10", "10 10", "10 10 10" and "10 10 10 10" are all valid options and would return the same result)
16096      * @static
16097      * @param {Number|String} box The encoded margins
16098      * @param {String} units The type of units to add
16099      * @return {String} An string with unitized (px if units is not specified) metrics for top, right, bottom and left
16100      */
16101     unitizeBox : function(box, units) {
16102         var A = this.addUnits,
16103             B = this.parseBox(box);
16104             
16105         return A(B.top, units) + ' ' +
16106                A(B.right, units) + ' ' +
16107                A(B.bottom, units) + ' ' +
16108                A(B.left, units);
16109         
16110     },
16111
16112     // private
16113     camelReplaceFn : function(m, a) {
16114         return a.charAt(1).toUpperCase();
16115     },
16116
16117     /**
16118      * Normalizes CSS property keys from dash delimited to camel case JavaScript Syntax.
16119      * For example:
16120      * <ul>
16121      *  <li>border-width -> borderWidth</li>
16122      *  <li>padding-top -> paddingTop</li>
16123      * </ul>
16124      * @static
16125      * @param {String} prop The property to normalize
16126      * @return {String} The normalized string
16127      */
16128     normalize : function(prop) {
16129         if (prop == 'float') {
16130             prop = Ext.supports.Float ? 'cssFloat' : 'styleFloat';
16131         }
16132         return this.propertyCache[prop] || (this.propertyCache[prop] = prop.replace(this.camelRe, this.camelReplaceFn));
16133     },
16134
16135     /**
16136      * Retrieves the document height
16137      * @static
16138      * @return {Number} documentHeight
16139      */
16140     getDocumentHeight: function() {
16141         return Math.max(!Ext.isStrict ? document.body.scrollHeight : document.documentElement.scrollHeight, this.getViewportHeight());
16142     },
16143
16144     /**
16145      * Retrieves the document width
16146      * @static
16147      * @return {Number} documentWidth
16148      */
16149     getDocumentWidth: function() {
16150         return Math.max(!Ext.isStrict ? document.body.scrollWidth : document.documentElement.scrollWidth, this.getViewportWidth());
16151     },
16152
16153     /**
16154      * Retrieves the viewport height of the window.
16155      * @static
16156      * @return {Number} viewportHeight
16157      */
16158     getViewportHeight: function(){
16159         return window.innerHeight;
16160     },
16161
16162     /**
16163      * Retrieves the viewport width of the window.
16164      * @static
16165      * @return {Number} viewportWidth
16166      */
16167     getViewportWidth : function() {
16168         return window.innerWidth;
16169     },
16170
16171     /**
16172      * Retrieves the viewport size of the window.
16173      * @static
16174      * @return {Object} object containing width and height properties
16175      */
16176     getViewSize : function() {
16177         return {
16178             width: window.innerWidth,
16179             height: window.innerHeight
16180         };
16181     },
16182
16183     /**
16184      * Retrieves the current orientation of the window. This is calculated by
16185      * determing if the height is greater than the width.
16186      * @static
16187      * @return {String} Orientation of window: 'portrait' or 'landscape'
16188      */
16189     getOrientation : function() {
16190         if (Ext.supports.OrientationChange) {
16191             return (window.orientation == 0) ? 'portrait' : 'landscape';
16192         }
16193         
16194         return (window.innerHeight > window.innerWidth) ? 'portrait' : 'landscape';
16195     },
16196
16197     /** 
16198      * Returns the top Element that is located at the passed coordinates
16199      * @static
16200      * @param {Number} x The x coordinate
16201      * @param {Number} x The y coordinate
16202      * @return {String} The found Element
16203      */
16204     fromPoint: function(x, y) {
16205         return Ext.get(document.elementFromPoint(x, y));
16206     },
16207     
16208     /**
16209      * Converts a CSS string into an object with a property for each style.
16210      * <p>
16211      * The sample code below would return an object with 2 properties, one
16212      * for background-color and one for color.</p>
16213      * <pre><code>
16214 var css = 'background-color: red;color: blue; ';
16215 console.log(Ext.core.Element.parseStyles(css));
16216      * </code></pre>
16217      * @static
16218      * @param {String} styles A CSS string
16219      * @return {Object} styles
16220      */
16221     parseStyles: function(styles){
16222         var out = {},
16223             cssRe = this.cssRe,
16224             matches;
16225             
16226         if (styles) {
16227             // Since we're using the g flag on the regex, we need to set the lastIndex.
16228             // This automatically happens on some implementations, but not others, see:
16229             // http://stackoverflow.com/questions/2645273/javascript-regular-expression-literal-persists-between-function-calls
16230             // http://blog.stevenlevithan.com/archives/fixing-javascript-regexp
16231             cssRe.lastIndex = 0;
16232             while ((matches = cssRe.exec(styles))) {
16233                 out[matches[1]] = matches[2];
16234             }
16235         }
16236         return out;
16237     }
16238 });
16239
16240 /**
16241  * @class Ext.CompositeElementLite
16242  * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter
16243  * members, or to perform collective actions upon the whole set.</p>
16244  * <p>Although they are not listed, this class supports all of the methods of {@link Ext.core.Element} and
16245  * {@link Ext.fx.Anim}. The methods from these classes will be performed on all the elements in this collection.</p>
16246  * Example:<pre><code>
16247 var els = Ext.select("#some-el div.some-class");
16248 // or select directly from an existing element
16249 var el = Ext.get('some-el');
16250 el.select('div.some-class');
16251
16252 els.setWidth(100); // all elements become 100 width
16253 els.hide(true); // all elements fade out and hide
16254 // or
16255 els.setWidth(100).hide(true);
16256 </code></pre>
16257  */
16258 Ext.CompositeElementLite = function(els, root){
16259     /**
16260      * <p>The Array of DOM elements which this CompositeElement encapsulates. Read-only.</p>
16261      * <p>This will not <i>usually</i> be accessed in developers' code, but developers wishing
16262      * to augment the capabilities of the CompositeElementLite class may use it when adding
16263      * methods to the class.</p>
16264      * <p>For example to add the <code>nextAll</code> method to the class to <b>add</b> all
16265      * following siblings of selected elements, the code would be</p><code><pre>
16266 Ext.override(Ext.CompositeElementLite, {
16267     nextAll: function() {
16268         var els = this.elements, i, l = els.length, n, r = [], ri = -1;
16269
16270 //      Loop through all elements in this Composite, accumulating
16271 //      an Array of all siblings.
16272         for (i = 0; i < l; i++) {
16273             for (n = els[i].nextSibling; n; n = n.nextSibling) {
16274                 r[++ri] = n;
16275             }
16276         }
16277
16278 //      Add all found siblings to this Composite
16279         return this.add(r);
16280     }
16281 });</pre></code>
16282      * @type Array
16283      * @property elements
16284      */
16285     this.elements = [];
16286     this.add(els, root);
16287     this.el = new Ext.core.Element.Flyweight();
16288 };
16289
16290 Ext.CompositeElementLite.prototype = {
16291     isComposite: true,
16292
16293     // private
16294     getElement : function(el){
16295         // Set the shared flyweight dom property to the current element
16296         var e = this.el;
16297         e.dom = el;
16298         e.id = el.id;
16299         return e;
16300     },
16301
16302     // private
16303     transformElement : function(el){
16304         return Ext.getDom(el);
16305     },
16306
16307     /**
16308      * Returns the number of elements in this Composite.
16309      * @return Number
16310      */
16311     getCount : function(){
16312         return this.elements.length;
16313     },
16314     /**
16315      * Adds elements to this Composite object.
16316      * @param {Mixed} els Either an Array of DOM elements to add, or another Composite object who's elements should be added.
16317      * @return {CompositeElement} This Composite object.
16318      */
16319     add : function(els, root){
16320         var me = this,
16321             elements = me.elements;
16322         if(!els){
16323             return this;
16324         }
16325         if(typeof els == "string"){
16326             els = Ext.core.Element.selectorFunction(els, root);
16327         }else if(els.isComposite){
16328             els = els.elements;
16329         }else if(!Ext.isIterable(els)){
16330             els = [els];
16331         }
16332
16333         for(var i = 0, len = els.length; i < len; ++i){
16334             elements.push(me.transformElement(els[i]));
16335         }
16336         return me;
16337     },
16338
16339     invoke : function(fn, args){
16340         var me = this,
16341             els = me.elements,
16342             len = els.length,
16343             e,
16344             i;
16345
16346         for(i = 0; i < len; i++) {
16347             e = els[i];
16348             if(e){
16349                 Ext.core.Element.prototype[fn].apply(me.getElement(e), args);
16350             }
16351         }
16352         return me;
16353     },
16354     /**
16355      * Returns a flyweight Element of the dom element object at the specified index
16356      * @param {Number} index
16357      * @return {Ext.core.Element}
16358      */
16359     item : function(index){
16360         var me = this,
16361             el = me.elements[index],
16362             out = null;
16363
16364         if(el){
16365             out = me.getElement(el);
16366         }
16367         return out;
16368     },
16369
16370     // fixes scope with flyweight
16371     addListener : function(eventName, handler, scope, opt){
16372         var els = this.elements,
16373             len = els.length,
16374             i, e;
16375
16376         for(i = 0; i<len; i++) {
16377             e = els[i];
16378             if(e) {
16379                 Ext.EventManager.on(e, eventName, handler, scope || e, opt);
16380             }
16381         }
16382         return this;
16383     },
16384     /**
16385      * <p>Calls the passed function for each element in this composite.</p>
16386      * @param {Function} fn The function to call. The function is passed the following parameters:<ul>
16387      * <li><b>el</b> : Element<div class="sub-desc">The current Element in the iteration.
16388      * <b>This is the flyweight (shared) Ext.core.Element instance, so if you require a
16389      * a reference to the dom node, use el.dom.</b></div></li>
16390      * <li><b>c</b> : Composite<div class="sub-desc">This Composite object.</div></li>
16391      * <li><b>idx</b> : Number<div class="sub-desc">The zero-based index in the iteration.</div></li>
16392      * </ul>
16393      * @param {Object} scope (optional) The scope (<i>this</i> reference) in which the function is executed. (defaults to the Element)
16394      * @return {CompositeElement} this
16395      */
16396     each : function(fn, scope){
16397         var me = this,
16398             els = me.elements,
16399             len = els.length,
16400             i, e;
16401
16402         for(i = 0; i<len; i++) {
16403             e = els[i];
16404             if(e){
16405                 e = this.getElement(e);
16406                 if(fn.call(scope || e, e, me, i) === false){
16407                     break;
16408                 }
16409             }
16410         }
16411         return me;
16412     },
16413
16414     /**
16415     * Clears this Composite and adds the elements passed.
16416     * @param {Mixed} els Either an array of DOM elements, or another Composite from which to fill this Composite.
16417     * @return {CompositeElement} this
16418     */
16419     fill : function(els){
16420         var me = this;
16421         me.elements = [];
16422         me.add(els);
16423         return me;
16424     },
16425
16426     /**
16427      * Filters this composite to only elements that match the passed selector.
16428      * @param {String/Function} selector A string CSS selector or a comparison function.
16429      * The comparison function will be called with the following arguments:<ul>
16430      * <li><code>el</code> : Ext.core.Element<div class="sub-desc">The current DOM element.</div></li>
16431      * <li><code>index</code> : Number<div class="sub-desc">The current index within the collection.</div></li>
16432      * </ul>
16433      * @return {CompositeElement} this
16434      */
16435     filter : function(selector){
16436         var els = [],
16437             me = this,
16438             fn = Ext.isFunction(selector) ? selector
16439                 : function(el){
16440                     return el.is(selector);
16441                 };
16442
16443         me.each(function(el, self, i) {
16444             if (fn(el, i) !== false) {
16445                 els[els.length] = me.transformElement(el);
16446             }
16447         });
16448         
16449         me.elements = els;
16450         return me;
16451     },
16452
16453     /**
16454      * Find the index of the passed element within the composite collection.
16455      * @param el {Mixed} The id of an element, or an Ext.core.Element, or an HtmlElement to find within the composite collection.
16456      * @return Number The index of the passed Ext.core.Element in the composite collection, or -1 if not found.
16457      */
16458     indexOf : function(el){
16459         return Ext.Array.indexOf(this.elements, this.transformElement(el));
16460     },
16461
16462     /**
16463     * Replaces the specified element with the passed element.
16464     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
16465     * to replace.
16466     * @param {Mixed} replacement The id of an element or the Element itself.
16467     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
16468     * @return {CompositeElement} this
16469     */
16470     replaceElement : function(el, replacement, domReplace){
16471         var index = !isNaN(el) ? el : this.indexOf(el),
16472             d;
16473         if(index > -1){
16474             replacement = Ext.getDom(replacement);
16475             if(domReplace){
16476                 d = this.elements[index];
16477                 d.parentNode.insertBefore(replacement, d);
16478                 Ext.removeNode(d);
16479             }
16480             Ext.Array.splice(this.elements, index, 1, replacement);
16481         }
16482         return this;
16483     },
16484
16485     /**
16486      * Removes all elements.
16487      */
16488     clear : function(){
16489         this.elements = [];
16490     }
16491 };
16492
16493 Ext.CompositeElementLite.prototype.on = Ext.CompositeElementLite.prototype.addListener;
16494
16495 /**
16496  * @private
16497  * Copies all of the functions from Ext.core.Element's prototype onto CompositeElementLite's prototype.
16498  * This is called twice - once immediately below, and once again after additional Ext.core.Element
16499  * are added in Ext JS
16500  */
16501 Ext.CompositeElementLite.importElementMethods = function() {
16502     var fnName,
16503         ElProto = Ext.core.Element.prototype,
16504         CelProto = Ext.CompositeElementLite.prototype;
16505
16506     for (fnName in ElProto) {
16507         if (typeof ElProto[fnName] == 'function'){
16508             (function(fnName) {
16509                 CelProto[fnName] = CelProto[fnName] || function() {
16510                     return this.invoke(fnName, arguments);
16511                 };
16512             }).call(CelProto, fnName);
16513
16514         }
16515     }
16516 };
16517
16518 Ext.CompositeElementLite.importElementMethods();
16519
16520 if(Ext.DomQuery){
16521     Ext.core.Element.selectorFunction = Ext.DomQuery.select;
16522 }
16523
16524 /**
16525  * Selects elements based on the passed CSS selector to enable {@link Ext.core.Element Element} methods
16526  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
16527  * {@link Ext.CompositeElementLite CompositeElementLite} object.
16528  * @param {String/Array} selector The CSS selector or an array of elements
16529  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
16530  * @return {CompositeElementLite/CompositeElement}
16531  * @member Ext.core.Element
16532  * @method select
16533  */
16534 Ext.core.Element.select = function(selector, root){
16535     var els;
16536     if(typeof selector == "string"){
16537         els = Ext.core.Element.selectorFunction(selector, root);
16538     }else if(selector.length !== undefined){
16539         els = selector;
16540     }else{
16541         Ext.Error.raise({
16542             sourceClass: "Ext.core.Element",
16543             sourceMethod: "select",
16544             selector: selector,
16545             root: root,
16546             msg: "Invalid selector specified: " + selector
16547         });
16548     }
16549     return new Ext.CompositeElementLite(els);
16550 };
16551 /**
16552  * Selects elements based on the passed CSS selector to enable {@link Ext.core.Element Element} methods
16553  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
16554  * {@link Ext.CompositeElementLite CompositeElementLite} object.
16555  * @param {String/Array} selector The CSS selector or an array of elements
16556  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
16557  * @return {CompositeElementLite/CompositeElement}
16558  * @member Ext
16559  * @method select
16560  */
16561 Ext.select = Ext.core.Element.select;
16562
16563 /**
16564  * @class Ext.util.DelayedTask
16565  * 
16566  * The DelayedTask class provides a convenient way to "buffer" the execution of a method,
16567  * performing setTimeout where a new timeout cancels the old timeout. When called, the
16568  * task will wait the specified time period before executing. If durng that time period,
16569  * the task is called again, the original call will be cancelled. This continues so that
16570  * the function is only called a single time for each iteration.
16571  * 
16572  * This method is especially useful for things like detecting whether a user has finished
16573  * typing in a text field. An example would be performing validation on a keypress. You can
16574  * use this class to buffer the keypress events for a certain number of milliseconds, and
16575  * perform only if they stop for that amount of time.  
16576  * 
16577  * ## Usage
16578  * 
16579  *     var task = new Ext.util.DelayedTask(function(){
16580  *         alert(Ext.getDom('myInputField').value.length);
16581  *     });
16582  *     
16583  *     // Wait 500ms before calling our function. If the user presses another key
16584  *     // during that 500ms, it will be cancelled and we'll wait another 500ms.
16585  *     Ext.get('myInputField').on('keypress', function(){
16586  *         task.{@link #delay}(500);
16587  *     });
16588  * 
16589  * Note that we are using a DelayedTask here to illustrate a point. The configuration
16590  * option `buffer` for {@link Ext.util.Observable#addListener addListener/on} will
16591  * also setup a delayed task for you to buffer events.
16592  * 
16593  * @constructor The parameters to this constructor serve as defaults and are not required.
16594  * @param {Function} fn (optional) The default function to call.
16595  * @param {Object} scope (optional) The default scope (The <code><b>this</b></code> reference) in which the
16596  * function is called. If not specified, <code>this</code> will refer to the browser window.
16597  * @param {Array} args (optional) The default Array of arguments.
16598  */
16599 Ext.util.DelayedTask = function(fn, scope, args) {
16600     var me = this,
16601         id,
16602         call = function() {
16603             clearInterval(id);
16604             id = null;
16605             fn.apply(scope, args || []);
16606         };
16607
16608     /**
16609      * Cancels any pending timeout and queues a new one
16610      * @param {Number} delay The milliseconds to delay
16611      * @param {Function} newFn (optional) Overrides function passed to constructor
16612      * @param {Object} newScope (optional) Overrides scope passed to constructor. Remember that if no scope
16613      * is specified, <code>this</code> will refer to the browser window.
16614      * @param {Array} newArgs (optional) Overrides args passed to constructor
16615      */
16616     this.delay = function(delay, newFn, newScope, newArgs) {
16617         me.cancel();
16618         fn = newFn || fn;
16619         scope = newScope || scope;
16620         args = newArgs || args;
16621         id = setInterval(call, delay);
16622     };
16623
16624     /**
16625      * Cancel the last queued timeout
16626      */
16627     this.cancel = function(){
16628         if (id) {
16629             clearInterval(id);
16630             id = null;
16631         }
16632     };
16633 };
16634 Ext.require('Ext.util.DelayedTask', function() {
16635
16636     Ext.util.Event = Ext.extend(Object, (function() {
16637         function createBuffered(handler, listener, o, scope) {
16638             listener.task = new Ext.util.DelayedTask();
16639             return function() {
16640                 listener.task.delay(o.buffer, handler, scope, Ext.Array.toArray(arguments));
16641             };
16642         }
16643
16644         function createDelayed(handler, listener, o, scope) {
16645             return function() {
16646                 var task = new Ext.util.DelayedTask();
16647                 if (!listener.tasks) {
16648                     listener.tasks = [];
16649                 }
16650                 listener.tasks.push(task);
16651                 task.delay(o.delay || 10, handler, scope, Ext.Array.toArray(arguments));
16652             };
16653         }
16654
16655         function createSingle(handler, listener, o, scope) {
16656             return function() {
16657                 listener.ev.removeListener(listener.fn, scope);
16658                 return handler.apply(scope, arguments);
16659             };
16660         }
16661
16662         return {
16663             isEvent: true,
16664
16665             constructor: function(observable, name) {
16666                 this.name = name;
16667                 this.observable = observable;
16668                 this.listeners = [];
16669             },
16670
16671             addListener: function(fn, scope, options) {
16672                 var me = this,
16673                     listener;
16674                     scope = scope || me.observable;
16675
16676                 if (!fn) {
16677                     Ext.Error.raise({
16678                         sourceClass: Ext.getClassName(this.observable),
16679                         sourceMethod: "addListener",
16680                         msg: "The specified callback function is undefined"
16681                     });
16682                 }
16683
16684                 if (!me.isListening(fn, scope)) {
16685                     listener = me.createListener(fn, scope, options);
16686                     if (me.firing) {
16687                         // if we are currently firing this event, don't disturb the listener loop
16688                         me.listeners = me.listeners.slice(0);
16689                     }
16690                     me.listeners.push(listener);
16691                 }
16692             },
16693
16694             createListener: function(fn, scope, o) {
16695                 o = o || {};
16696                 scope = scope || this.observable;
16697
16698                 var listener = {
16699                         fn: fn,
16700                         scope: scope,
16701                         o: o,
16702                         ev: this
16703                     },
16704                     handler = fn;
16705
16706                 // The order is important. The 'single' wrapper must be wrapped by the 'buffer' and 'delayed' wrapper
16707                 // because the event removal that the single listener does destroys the listener's DelayedTask(s)
16708                 if (o.single) {
16709                     handler = createSingle(handler, listener, o, scope);
16710                 }
16711                 if (o.delay) {
16712                     handler = createDelayed(handler, listener, o, scope);
16713                 }
16714                 if (o.buffer) {
16715                     handler = createBuffered(handler, listener, o, scope);
16716                 }
16717
16718                 listener.fireFn = handler;
16719                 return listener;
16720             },
16721
16722             findListener: function(fn, scope) {
16723                 var listeners = this.listeners,
16724                 i = listeners.length,
16725                 listener,
16726                 s;
16727
16728                 while (i--) {
16729                     listener = listeners[i];
16730                     if (listener) {
16731                         s = listener.scope;
16732                         if (listener.fn == fn && (s == scope || s == this.observable)) {
16733                             return i;
16734                         }
16735                     }
16736                 }
16737
16738                 return - 1;
16739             },
16740
16741             isListening: function(fn, scope) {
16742                 return this.findListener(fn, scope) !== -1;
16743             },
16744
16745             removeListener: function(fn, scope) {
16746                 var me = this,
16747                     index,
16748                     listener,
16749                     k;
16750                 index = me.findListener(fn, scope);
16751                 if (index != -1) {
16752                     listener = me.listeners[index];
16753
16754                     if (me.firing) {
16755                         me.listeners = me.listeners.slice(0);
16756                     }
16757
16758                     // cancel and remove a buffered handler that hasn't fired yet
16759                     if (listener.task) {
16760                         listener.task.cancel();
16761                         delete listener.task;
16762                     }
16763
16764                     // cancel and remove all delayed handlers that haven't fired yet
16765                     k = listener.tasks && listener.tasks.length;
16766                     if (k) {
16767                         while (k--) {
16768                             listener.tasks[k].cancel();
16769                         }
16770                         delete listener.tasks;
16771                     }
16772
16773                     // remove this listener from the listeners array
16774                     Ext.Array.erase(me.listeners, index, 1);
16775                     return true;
16776                 }
16777
16778                 return false;
16779             },
16780
16781             // Iterate to stop any buffered/delayed events
16782             clearListeners: function() {
16783                 var listeners = this.listeners,
16784                     i = listeners.length;
16785
16786                 while (i--) {
16787                     this.removeListener(listeners[i].fn, listeners[i].scope);
16788                 }
16789             },
16790
16791             fire: function() {
16792                 var me = this,
16793                     listeners = me.listeners,
16794                     count = listeners.length,
16795                     i,
16796                     args,
16797                     listener;
16798
16799                 if (count > 0) {
16800                     me.firing = true;
16801                     for (i = 0; i < count; i++) {
16802                         listener = listeners[i];
16803                         args = arguments.length ? Array.prototype.slice.call(arguments, 0) : [];
16804                         if (listener.o) {
16805                             args.push(listener.o);
16806                         }
16807                         if (listener && listener.fireFn.apply(listener.scope || me.observable, args) === false) {
16808                             return (me.firing = false);
16809                         }
16810                     }
16811                 }
16812                 me.firing = false;
16813                 return true;
16814             }
16815         };
16816     })());
16817 });
16818
16819 /**
16820  * @class Ext.EventManager
16821  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides
16822  * several useful events directly.
16823  * See {@link Ext.EventObject} for more details on normalized event objects.
16824  * @singleton
16825  */
16826 Ext.EventManager = {
16827
16828     // --------------------- onReady ---------------------
16829
16830     /**
16831      * Check if we have bound our global onReady listener
16832      * @private
16833      */
16834     hasBoundOnReady: false,
16835
16836     /**
16837      * Check if fireDocReady has been called
16838      * @private
16839      */
16840     hasFiredReady: false,
16841
16842     /**
16843      * Timer for the document ready event in old IE versions
16844      * @private
16845      */
16846     readyTimeout: null,
16847
16848     /**
16849      * Checks if we have bound an onreadystatechange event
16850      * @private
16851      */
16852     hasOnReadyStateChange: false,
16853
16854     /**
16855      * Holds references to any onReady functions
16856      * @private
16857      */
16858     readyEvent: new Ext.util.Event(),
16859
16860     /**
16861      * Check the ready state for old IE versions
16862      * @private
16863      * @return {Boolean} True if the document is ready
16864      */
16865     checkReadyState: function(){
16866         var me = Ext.EventManager;
16867
16868         if(window.attachEvent){
16869             // See here for reference: http://javascript.nwbox.com/IEContentLoaded/
16870             if (window != top) {
16871                 return false;
16872             }
16873             try{
16874                 document.documentElement.doScroll('left');
16875             }catch(e){
16876                 return false;
16877             }
16878             me.fireDocReady();
16879             return true;
16880         }
16881         if (document.readyState == 'complete') {
16882             me.fireDocReady();
16883             return true;
16884         }
16885         me.readyTimeout = setTimeout(arguments.callee, 2);
16886         return false;
16887     },
16888
16889     /**
16890      * Binds the appropriate browser event for checking if the DOM has loaded.
16891      * @private
16892      */
16893     bindReadyEvent: function(){
16894         var me = Ext.EventManager;
16895         if (me.hasBoundOnReady) {
16896             return;
16897         }
16898
16899         if (document.addEventListener) {
16900             document.addEventListener('DOMContentLoaded', me.fireDocReady, false);
16901             // fallback, load will ~always~ fire
16902             window.addEventListener('load', me.fireDocReady, false);
16903         } else {
16904             // check if the document is ready, this will also kick off the scroll checking timer
16905             if (!me.checkReadyState()) {
16906                 document.attachEvent('onreadystatechange', me.checkReadyState);
16907                 me.hasOnReadyStateChange = true;
16908             }
16909             // fallback, onload will ~always~ fire
16910             window.attachEvent('onload', me.fireDocReady, false);
16911         }
16912         me.hasBoundOnReady = true;
16913     },
16914
16915     /**
16916      * We know the document is loaded, so trigger any onReady events.
16917      * @private
16918      */
16919     fireDocReady: function(){
16920         var me = Ext.EventManager;
16921
16922         // only unbind these events once
16923         if (!me.hasFiredReady) {
16924             me.hasFiredReady = true;
16925
16926             if (document.addEventListener) {
16927                 document.removeEventListener('DOMContentLoaded', me.fireDocReady, false);
16928                 window.removeEventListener('load', me.fireDocReady, false);
16929             } else {
16930                 if (me.readyTimeout !== null) {
16931                     clearTimeout(me.readyTimeout);
16932                 }
16933                 if (me.hasOnReadyStateChange) {
16934                     document.detachEvent('onreadystatechange', me.checkReadyState);
16935                 }
16936                 window.detachEvent('onload', me.fireDocReady);
16937             }
16938             Ext.supports.init();
16939         }
16940         if (!Ext.isReady) {
16941             Ext.isReady = true;
16942             me.onWindowUnload();
16943             me.readyEvent.fire();
16944         }
16945     },
16946
16947     /**
16948      * Adds a listener to be notified when the document is ready (before onload and before images are loaded). Can be
16949      * accessed shorthanded as Ext.onReady().
16950      * @param {Function} fn The method the event invokes.
16951      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
16952      * @param {boolean} options (optional) Options object as passed to {@link Ext.core.Element#addListener}.
16953      */
16954     onDocumentReady: function(fn, scope, options){
16955         options = options || {};
16956         var me = Ext.EventManager,
16957             readyEvent = me.readyEvent;
16958
16959         // force single to be true so our event is only ever fired once.
16960         options.single = true;
16961
16962         // Document already loaded, let's just fire it
16963         if (Ext.isReady) {
16964             readyEvent.addListener(fn, scope, options);
16965             readyEvent.fire();
16966         } else {
16967             options.delay = options.delay || 1;
16968             readyEvent.addListener(fn, scope, options);
16969             me.bindReadyEvent();
16970         }
16971     },
16972
16973
16974     // --------------------- event binding ---------------------
16975
16976     /**
16977      * Contains a list of all document mouse downs, so we can ensure they fire even when stopEvent is called.
16978      * @private
16979      */
16980     stoppedMouseDownEvent: new Ext.util.Event(),
16981
16982     /**
16983      * Options to parse for the 4th argument to addListener.
16984      * @private
16985      */
16986     propRe: /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate|freezeEvent)$/,
16987
16988     /**
16989      * Get the id of the element. If one has not been assigned, automatically assign it.
16990      * @param {Mixed} element The element to get the id for.
16991      * @return {String} id
16992      */
16993     getId : function(element) {
16994         var skipGarbageCollection = false,
16995             id;
16996     
16997         element = Ext.getDom(element);
16998     
16999         if (element === document || element === window) {
17000             id = element === document ? Ext.documentId : Ext.windowId;
17001         }
17002         else {
17003             id = Ext.id(element);
17004         }
17005         // skip garbage collection for special elements (window, document, iframes)
17006         if (element && (element.getElementById || element.navigator)) {
17007             skipGarbageCollection = true;
17008         }
17009     
17010         if (!Ext.cache[id]){
17011             Ext.core.Element.addToCache(new Ext.core.Element(element), id);
17012             if (skipGarbageCollection) {
17013                 Ext.cache[id].skipGarbageCollection = true;
17014             }
17015         }
17016         return id;
17017     },
17018
17019     /**
17020      * Convert a "config style" listener into a set of flat arguments so they can be passed to addListener
17021      * @private
17022      * @param {Object} element The element the event is for
17023      * @param {Object} event The event configuration
17024      * @param {Object} isRemove True if a removal should be performed, otherwise an add will be done.
17025      */
17026     prepareListenerConfig: function(element, config, isRemove){
17027         var me = this,
17028             propRe = me.propRe,
17029             key, value, args;
17030
17031         // loop over all the keys in the object
17032         for (key in config) {
17033             if (config.hasOwnProperty(key)) {
17034                 // if the key is something else then an event option
17035                 if (!propRe.test(key)) {
17036                     value = config[key];
17037                     // if the value is a function it must be something like click: function(){}, scope: this
17038                     // which means that there might be multiple event listeners with shared options
17039                     if (Ext.isFunction(value)) {
17040                         // shared options
17041                         args = [element, key, value, config.scope, config];
17042                     } else {
17043                         // if its not a function, it must be an object like click: {fn: function(){}, scope: this}
17044                         args = [element, key, value.fn, value.scope, value];
17045                     }
17046
17047                     if (isRemove === true) {
17048                         me.removeListener.apply(this, args);
17049                     } else {
17050                         me.addListener.apply(me, args);
17051                     }
17052                 }
17053             }
17054         }
17055     },
17056
17057     /**
17058      * Normalize cross browser event differences
17059      * @private
17060      * @param {Object} eventName The event name
17061      * @param {Object} fn The function to execute
17062      * @return {Object} The new event name/function
17063      */
17064     normalizeEvent: function(eventName, fn){
17065         if (/mouseenter|mouseleave/.test(eventName) && !Ext.supports.MouseEnterLeave) {
17066             if (fn) {
17067                 fn = Ext.Function.createInterceptor(fn, this.contains, this);
17068             }
17069             eventName = eventName == 'mouseenter' ? 'mouseover' : 'mouseout';
17070         } else if (eventName == 'mousewheel' && !Ext.supports.MouseWheel && !Ext.isOpera){
17071             eventName = 'DOMMouseScroll';
17072         }
17073         return {
17074             eventName: eventName,
17075             fn: fn
17076         };
17077     },
17078
17079     /**
17080      * Checks whether the event's relatedTarget is contained inside (or <b>is</b>) the element.
17081      * @private
17082      * @param {Object} event
17083      */
17084     contains: function(event){
17085         var parent = event.browserEvent.currentTarget,
17086             child = this.getRelatedTarget(event);
17087
17088         if (parent && parent.firstChild) {
17089             while (child) {
17090                 if (child === parent) {
17091                     return false;
17092                 }
17093                 child = child.parentNode;
17094                 if (child && (child.nodeType != 1)) {
17095                     child = null;
17096                 }
17097             }
17098         }
17099         return true;
17100     },
17101
17102     /**
17103     * Appends an event handler to an element.  The shorthand version {@link #on} is equivalent.  Typically you will
17104     * use {@link Ext.core.Element#addListener} directly on an Element in favor of calling this version.
17105     * @param {String/HTMLElement} el The html element or id to assign the event handler to.
17106     * @param {String} eventName The name of the event to listen for.
17107     * @param {Function} handler The handler function the event invokes. This function is passed
17108     * the following parameters:<ul>
17109     * <li>evt : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
17110     * <li>t : Element<div class="sub-desc">The {@link Ext.core.Element Element} which was the target of the event.
17111     * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
17112     * <li>o : Object<div class="sub-desc">The options object from the addListener call.</div></li>
17113     * </ul>
17114     * @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>.
17115     * @param {Object} options (optional) An object containing handler configuration properties.
17116     * This may contain any of the following properties:<ul>
17117     * <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>
17118     * <li>delegate : String<div class="sub-desc">A simple selector to filter the target or look for a descendant of the target</div></li>
17119     * <li>stopEvent : Boolean<div class="sub-desc">True to stop the event. That is stop propagation, and prevent the default action.</div></li>
17120     * <li>preventDefault : Boolean<div class="sub-desc">True to prevent the default action</div></li>
17121     * <li>stopPropagation : Boolean<div class="sub-desc">True to prevent event propagation</div></li>
17122     * <li>normalized : Boolean<div class="sub-desc">False to pass a browser event to the handler function instead of an Ext.EventObject</div></li>
17123     * <li>delay : Number<div class="sub-desc">The number of milliseconds to delay the invocation of the handler after te event fires.</div></li>
17124     * <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>
17125     * <li>buffer : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
17126     * by the specified number of milliseconds. If the event fires again within that time, the original
17127     * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
17128     * <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>
17129     * </ul><br>
17130     * <p>See {@link Ext.core.Element#addListener} for examples of how to use these options.</p>
17131     */
17132     addListener: function(element, eventName, fn, scope, options){
17133         // Check if we've been passed a "config style" event.
17134         if (typeof eventName !== 'string') {
17135             this.prepareListenerConfig(element, eventName);
17136             return;
17137         }
17138
17139         var dom = Ext.getDom(element),
17140             bind,
17141             wrap;
17142
17143         if (!dom){
17144             Ext.Error.raise({
17145                 sourceClass: 'Ext.EventManager',
17146                 sourceMethod: 'addListener',
17147                 targetElement: element,
17148                 eventName: eventName,
17149                 msg: 'Error adding "' + eventName + '\" listener for nonexistent element "' + element + '"'
17150             });
17151         }
17152         if (!fn) {
17153             Ext.Error.raise({
17154                 sourceClass: 'Ext.EventManager',
17155                 sourceMethod: 'addListener',
17156                 targetElement: element,
17157                 eventName: eventName,
17158                 msg: 'Error adding "' + eventName + '\" listener. The handler function is undefined.'
17159             });
17160         }
17161
17162         // create the wrapper function
17163         options = options || {};
17164
17165         bind = this.normalizeEvent(eventName, fn);
17166         wrap = this.createListenerWrap(dom, eventName, bind.fn, scope, options);
17167
17168
17169         if (dom.attachEvent) {
17170             dom.attachEvent('on' + bind.eventName, wrap);
17171         } else {
17172             dom.addEventListener(bind.eventName, wrap, options.capture || false);
17173         }
17174
17175         if (dom == document && eventName == 'mousedown') {
17176             this.stoppedMouseDownEvent.addListener(wrap);
17177         }
17178
17179         // add all required data into the event cache
17180         this.getEventListenerCache(dom, eventName).push({
17181             fn: fn,
17182             wrap: wrap,
17183             scope: scope
17184         });
17185     },
17186
17187     /**
17188     * Removes an event handler from an element.  The shorthand version {@link #un} is equivalent.  Typically
17189     * you will use {@link Ext.core.Element#removeListener} directly on an Element in favor of calling this version.
17190     * @param {String/HTMLElement} el The id or html element from which to remove the listener.
17191     * @param {String} eventName The name of the event.
17192     * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
17193     * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
17194     * then this must refer to the same object.
17195     */
17196     removeListener : function(element, eventName, fn, scope) {
17197         // handle our listener config object syntax
17198         if (typeof eventName !== 'string') {
17199             this.prepareListenerConfig(element, eventName, true);
17200             return;
17201         }
17202
17203         var dom = Ext.getDom(element),
17204             cache = this.getEventListenerCache(dom, eventName),
17205             bindName = this.normalizeEvent(eventName).eventName,
17206             i = cache.length, j,
17207             listener, wrap, tasks;
17208
17209
17210         while (i--) {
17211             listener = cache[i];
17212
17213             if (listener && (!fn || listener.fn == fn) && (!scope || listener.scope === scope)) {
17214                 wrap = listener.wrap;
17215
17216                 // clear buffered calls
17217                 if (wrap.task) {
17218                     clearTimeout(wrap.task);
17219                     delete wrap.task;
17220                 }
17221
17222                 // clear delayed calls
17223                 j = wrap.tasks && wrap.tasks.length;
17224                 if (j) {
17225                     while (j--) {
17226                         clearTimeout(wrap.tasks[j]);
17227                     }
17228                     delete wrap.tasks;
17229                 }
17230
17231                 if (dom.detachEvent) {
17232                     dom.detachEvent('on' + bindName, wrap);
17233                 } else {
17234                     dom.removeEventListener(bindName, wrap, false);
17235                 }
17236
17237                 if (wrap && dom == document && eventName == 'mousedown') {
17238                     this.stoppedMouseDownEvent.removeListener(wrap);
17239                 }
17240
17241                 // remove listener from cache
17242                 Ext.Array.erase(cache, i, 1);
17243             }
17244         }
17245     },
17246
17247     /**
17248     * Removes all event handers from an element.  Typically you will use {@link Ext.core.Element#removeAllListeners}
17249     * directly on an Element in favor of calling this version.
17250     * @param {String/HTMLElement} el The id or html element from which to remove all event handlers.
17251     */
17252     removeAll : function(element){
17253         var dom = Ext.getDom(element),
17254             cache, ev;
17255         if (!dom) {
17256             return;
17257         }
17258         cache = this.getElementEventCache(dom);
17259
17260         for (ev in cache) {
17261             if (cache.hasOwnProperty(ev)) {
17262                 this.removeListener(dom, ev);
17263             }
17264         }
17265         Ext.cache[dom.id].events = {};
17266     },
17267
17268     /**
17269      * Recursively removes all previous added listeners from an element and its children. Typically you will use {@link Ext.core.Element#purgeAllListeners}
17270      * directly on an Element in favor of calling this version.
17271      * @param {String/HTMLElement} el The id or html element from which to remove all event handlers.
17272      * @param {String} eventName (optional) The name of the event.
17273      */
17274     purgeElement : function(element, eventName) {
17275         var dom = Ext.getDom(element),
17276             i = 0, len;
17277
17278         if(eventName) {
17279             this.removeListener(dom, eventName);
17280         }
17281         else {
17282             this.removeAll(dom);
17283         }
17284
17285         if(dom && dom.childNodes) {
17286             for(len = element.childNodes.length; i < len; i++) {
17287                 this.purgeElement(element.childNodes[i], eventName);
17288             }
17289         }
17290     },
17291
17292     /**
17293      * Create the wrapper function for the event
17294      * @private
17295      * @param {HTMLElement} dom The dom element
17296      * @param {String} ename The event name
17297      * @param {Function} fn The function to execute
17298      * @param {Object} scope The scope to execute callback in
17299      * @param {Object} options The options
17300      * @return {Function} the wrapper function
17301      */
17302     createListenerWrap : function(dom, ename, fn, scope, options) {
17303         options = options || {};
17304
17305         var f, gen;
17306
17307         return function wrap(e, args) {
17308             // Compile the implementation upon first firing
17309             if (!gen) {
17310                 f = ['if(!Ext) {return;}'];
17311
17312                 if(options.buffer || options.delay || options.freezeEvent) {
17313                     f.push('e = new Ext.EventObjectImpl(e, ' + (options.freezeEvent ? 'true' : 'false' ) + ');');
17314                 } else {
17315                     f.push('e = Ext.EventObject.setEvent(e);');
17316                 }
17317
17318                 if (options.delegate) {
17319                     f.push('var t = e.getTarget("' + options.delegate + '", this);');
17320                     f.push('if(!t) {return;}');
17321                 } else {
17322                     f.push('var t = e.target;');
17323                 }
17324
17325                 if (options.target) {
17326                     f.push('if(e.target !== options.target) {return;}');
17327                 }
17328
17329                 if(options.stopEvent) {
17330                     f.push('e.stopEvent();');
17331                 } else {
17332                     if(options.preventDefault) {
17333                         f.push('e.preventDefault();');
17334                     }
17335                     if(options.stopPropagation) {
17336                         f.push('e.stopPropagation();');
17337                     }
17338                 }
17339
17340                 if(options.normalized === false) {
17341                     f.push('e = e.browserEvent;');
17342                 }
17343
17344                 if(options.buffer) {
17345                     f.push('(wrap.task && clearTimeout(wrap.task));');
17346                     f.push('wrap.task = setTimeout(function(){');
17347                 }
17348
17349                 if(options.delay) {
17350                     f.push('wrap.tasks = wrap.tasks || [];');
17351                     f.push('wrap.tasks.push(setTimeout(function(){');
17352                 }
17353
17354                 // finally call the actual handler fn
17355                 f.push('fn.call(scope || dom, e, t, options);');
17356
17357                 if(options.single) {
17358                     f.push('Ext.EventManager.removeListener(dom, ename, fn, scope);');
17359                 }
17360
17361                 if(options.delay) {
17362                     f.push('}, ' + options.delay + '));');
17363                 }
17364
17365                 if(options.buffer) {
17366                     f.push('}, ' + options.buffer + ');');
17367                 }
17368
17369                 gen = Ext.functionFactory('e', 'options', 'fn', 'scope', 'ename', 'dom', 'wrap', 'args', f.join('\n'));
17370             }
17371
17372             gen.call(dom, e, options, fn, scope, ename, dom, wrap, args);
17373         };
17374     },
17375
17376     /**
17377      * Get the event cache for a particular element for a particular event
17378      * @private
17379      * @param {HTMLElement} element The element
17380      * @param {Object} eventName The event name
17381      * @return {Array} The events for the element
17382      */
17383     getEventListenerCache : function(element, eventName) {
17384         if (!element) {
17385             return [];
17386         }
17387         
17388         var eventCache = this.getElementEventCache(element);
17389         return eventCache[eventName] || (eventCache[eventName] = []);
17390     },
17391
17392     /**
17393      * Gets the event cache for the object
17394      * @private
17395      * @param {HTMLElement} element The element
17396      * @return {Object} The event cache for the object
17397      */
17398     getElementEventCache : function(element) {
17399         if (!element) {
17400             return {};
17401         }
17402         var elementCache = Ext.cache[this.getId(element)];
17403         return elementCache.events || (elementCache.events = {});
17404     },
17405
17406     // --------------------- utility methods ---------------------
17407     mouseLeaveRe: /(mouseout|mouseleave)/,
17408     mouseEnterRe: /(mouseover|mouseenter)/,
17409
17410     /**
17411      * Stop the event (preventDefault and stopPropagation)
17412      * @param {Event} The event to stop
17413      */
17414     stopEvent: function(event) {
17415         this.stopPropagation(event);
17416         this.preventDefault(event);
17417     },
17418
17419     /**
17420      * Cancels bubbling of the event.
17421      * @param {Event} The event to stop bubbling.
17422      */
17423     stopPropagation: function(event) {
17424         event = event.browserEvent || event;
17425         if (event.stopPropagation) {
17426             event.stopPropagation();
17427         } else {
17428             event.cancelBubble = true;
17429         }
17430     },
17431
17432     /**
17433      * Prevents the browsers default handling of the event.
17434      * @param {Event} The event to prevent the default
17435      */
17436     preventDefault: function(event) {
17437         event = event.browserEvent || event;
17438         if (event.preventDefault) {
17439             event.preventDefault();
17440         } else {
17441             event.returnValue = false;
17442             // Some keys events require setting the keyCode to -1 to be prevented
17443             try {
17444               // all ctrl + X and F1 -> F12
17445               if (event.ctrlKey || event.keyCode > 111 && event.keyCode < 124) {
17446                   event.keyCode = -1;
17447               }
17448             } catch (e) {
17449                 // see this outdated document http://support.microsoft.com/kb/934364/en-us for more info
17450             }
17451         }
17452     },
17453
17454     /**
17455      * Gets the related target from the event.
17456      * @param {Object} event The event
17457      * @return {HTMLElement} The related target.
17458      */
17459     getRelatedTarget: function(event) {
17460         event = event.browserEvent || event;
17461         var target = event.relatedTarget;
17462         if (!target) {
17463             if (this.mouseLeaveRe.test(event.type)) {
17464                 target = event.toElement;
17465             } else if (this.mouseEnterRe.test(event.type)) {
17466                 target = event.fromElement;
17467             }
17468         }
17469         return this.resolveTextNode(target);
17470     },
17471
17472     /**
17473      * Gets the x coordinate from the event
17474      * @param {Object} event The event
17475      * @return {Number} The x coordinate
17476      */
17477     getPageX: function(event) {
17478         return this.getXY(event)[0];
17479     },
17480
17481     /**
17482      * Gets the y coordinate from the event
17483      * @param {Object} event The event
17484      * @return {Number} The y coordinate
17485      */
17486     getPageY: function(event) {
17487         return this.getXY(event)[1];
17488     },
17489
17490     /**
17491      * Gets the x & ycoordinate from the event
17492      * @param {Object} event The event
17493      * @return {Array} The x/y coordinate
17494      */
17495     getPageXY: function(event) {
17496         event = event.browserEvent || event;
17497         var x = event.pageX,
17498             y = event.pageY,
17499             doc = document.documentElement,
17500             body = document.body;
17501
17502         // pageX/pageY not available (undefined, not null), use clientX/clientY instead
17503         if (!x && x !== 0) {
17504             x = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
17505             y = event.clientY + (doc && doc.scrollTop  || body && body.scrollTop  || 0) - (doc && doc.clientTop  || body && body.clientTop  || 0);
17506         }
17507         return [x, y];
17508     },
17509
17510     /**
17511      * Gets the target of the event.
17512      * @param {Object} event The event
17513      * @return {HTMLElement} target
17514      */
17515     getTarget: function(event) {
17516         event = event.browserEvent || event;
17517         return this.resolveTextNode(event.target || event.srcElement);
17518     },
17519
17520     /**
17521      * Resolve any text nodes accounting for browser differences.
17522      * @private
17523      * @param {HTMLElement} node The node
17524      * @return {HTMLElement} The resolved node
17525      */
17526     // 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.
17527     resolveTextNode: Ext.isGecko ?
17528         function(node) {
17529             if (!node) {
17530                 return;
17531             }
17532             // work around firefox bug, https://bugzilla.mozilla.org/show_bug.cgi?id=101197
17533             var s = HTMLElement.prototype.toString.call(node);
17534             if (s == '[xpconnect wrapped native prototype]' || s == '[object XULElement]') {
17535                 return;
17536             }
17537                 return node.nodeType == 3 ? node.parentNode: node;
17538             }: function(node) {
17539                 return node && node.nodeType == 3 ? node.parentNode: node;
17540             },
17541
17542     // --------------------- custom event binding ---------------------
17543
17544     // Keep track of the current width/height
17545     curWidth: 0,
17546     curHeight: 0,
17547
17548     /**
17549      * Adds a listener to be notified when the browser window is resized and provides resize event buffering (100 milliseconds),
17550      * passes new viewport width and height to handlers.
17551      * @param {Function} fn      The handler function the window resize event invokes.
17552      * @param {Object}   scope   The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
17553      * @param {boolean}  options Options object as passed to {@link Ext.core.Element#addListener}
17554      */
17555     onWindowResize: function(fn, scope, options){
17556         var resize = this.resizeEvent;
17557         if(!resize){
17558             this.resizeEvent = resize = new Ext.util.Event();
17559             this.on(window, 'resize', this.fireResize, this, {buffer: 100});
17560         }
17561         resize.addListener(fn, scope, options);
17562     },
17563
17564     /**
17565      * Fire the resize event.
17566      * @private
17567      */
17568     fireResize: function(){
17569         var me = this,
17570             w = Ext.core.Element.getViewWidth(),
17571             h = Ext.core.Element.getViewHeight();
17572
17573          //whacky problem in IE where the resize event will sometimes fire even though the w/h are the same.
17574          if(me.curHeight != h || me.curWidth != w){
17575              me.curHeight = h;
17576              me.curWidth = w;
17577              me.resizeEvent.fire(w, h);
17578          }
17579     },
17580
17581     /**
17582      * Removes the passed window resize listener.
17583      * @param {Function} fn        The method the event invokes
17584      * @param {Object}   scope    The scope of handler
17585      */
17586     removeResizeListener: function(fn, scope){
17587         if (this.resizeEvent) {
17588             this.resizeEvent.removeListener(fn, scope);
17589         }
17590     },
17591
17592     onWindowUnload: function() {
17593         var unload = this.unloadEvent;
17594         if (!unload) {
17595             this.unloadEvent = unload = new Ext.util.Event();
17596             this.addListener(window, 'unload', this.fireUnload, this);
17597         }
17598     },
17599
17600     /**
17601      * Fires the unload event for items bound with onWindowUnload
17602      * @private
17603      */
17604     fireUnload: function() {
17605         // wrap in a try catch, could have some problems during unload
17606         try {
17607             this.removeUnloadListener();
17608             // Work around FF3 remembering the last scroll position when refreshing the grid and then losing grid view
17609             if (Ext.isGecko3) {
17610                 var gridviews = Ext.ComponentQuery.query('gridview'),
17611                     i = 0,
17612                     ln = gridviews.length;
17613                 for (; i < ln; i++) {
17614                     gridviews[i].scrollToTop();
17615                 }
17616             }
17617             // Purge all elements in the cache
17618             var el,
17619                 cache = Ext.cache;
17620             for (el in cache) {
17621                 if (cache.hasOwnProperty(el)) {
17622                     Ext.EventManager.removeAll(el);
17623                 }
17624             }
17625         } catch(e) {
17626         }
17627     },
17628
17629     /**
17630      * Removes the passed window unload listener.
17631      * @param {Function} fn        The method the event invokes
17632      * @param {Object}   scope    The scope of handler
17633      */
17634     removeUnloadListener: function(){
17635         if (this.unloadEvent) {
17636             this.removeListener(window, 'unload', this.fireUnload);
17637         }
17638     },
17639
17640     /**
17641      * note 1: IE fires ONLY the keydown event on specialkey autorepeat
17642      * note 2: Safari < 3.1, Gecko (Mac/Linux) & Opera fire only the keypress event on specialkey autorepeat
17643      * (research done by @Jan Wolter at http://unixpapa.com/js/key.html)
17644      * @private
17645      */
17646     useKeyDown: Ext.isWebKit ?
17647                    parseInt(navigator.userAgent.match(/AppleWebKit\/(\d+)/)[1], 10) >= 525 :
17648                    !((Ext.isGecko && !Ext.isWindows) || Ext.isOpera),
17649
17650     /**
17651      * Indicates which event to use for getting key presses.
17652      * @return {String} The appropriate event name.
17653      */
17654     getKeyEvent: function(){
17655         return this.useKeyDown ? 'keydown' : 'keypress';
17656     }
17657 };
17658
17659 /**
17660  * Alias for {@link Ext.Loader#onReady Ext.Loader.onReady} with withDomReady set to true
17661  * @member Ext
17662  * @method onReady
17663  */
17664 Ext.onReady = function(fn, scope, options) {
17665     Ext.Loader.onReady(fn, scope, true, options);
17666 };
17667
17668 /**
17669  * Alias for {@link Ext.EventManager#onDocumentReady Ext.EventManager.onDocumentReady}
17670  * @member Ext
17671  * @method onDocumentReady
17672  */
17673 Ext.onDocumentReady = Ext.EventManager.onDocumentReady;
17674
17675 /**
17676  * Alias for {@link Ext.EventManager#addListener Ext.EventManager.addListener}
17677  * @member Ext.EventManager
17678  * @method on
17679  */
17680 Ext.EventManager.on = Ext.EventManager.addListener;
17681
17682 /**
17683  * Alias for {@link Ext.EventManager#removeListener Ext.EventManager.removeListener}
17684  * @member Ext.EventManager
17685  * @method un
17686  */
17687 Ext.EventManager.un = Ext.EventManager.removeListener;
17688
17689 (function(){
17690     var initExtCss = function() {
17691         // find the body element
17692         var bd = document.body || document.getElementsByTagName('body')[0],
17693             baseCSSPrefix = Ext.baseCSSPrefix,
17694             cls = [baseCSSPrefix + 'body'],
17695             htmlCls = [],
17696             html;
17697
17698         if (!bd) {
17699             return false;
17700         }
17701
17702         html = bd.parentNode;
17703
17704         //Let's keep this human readable!
17705         if (Ext.isIE) {
17706             cls.push(baseCSSPrefix + 'ie');
17707         }
17708         if (Ext.isIE6) {
17709             cls.push(baseCSSPrefix + 'ie6');
17710         }
17711         if (Ext.isIE7) {
17712             cls.push(baseCSSPrefix + 'ie7');
17713         }
17714         if (Ext.isIE8) {
17715             cls.push(baseCSSPrefix + 'ie8');
17716         }
17717         if (Ext.isIE9) {
17718             cls.push(baseCSSPrefix + 'ie9');
17719         }
17720         if (Ext.isGecko) {
17721             cls.push(baseCSSPrefix + 'gecko');
17722         }
17723         if (Ext.isGecko3) {
17724             cls.push(baseCSSPrefix + 'gecko3');
17725         }
17726         if (Ext.isGecko4) {
17727             cls.push(baseCSSPrefix + 'gecko4');
17728         }
17729         if (Ext.isOpera) {
17730             cls.push(baseCSSPrefix + 'opera');
17731         }
17732         if (Ext.isWebKit) {
17733             cls.push(baseCSSPrefix + 'webkit');
17734         }
17735         if (Ext.isSafari) {
17736             cls.push(baseCSSPrefix + 'safari');
17737         }
17738         if (Ext.isSafari2) {
17739             cls.push(baseCSSPrefix + 'safari2');
17740         }
17741         if (Ext.isSafari3) {
17742             cls.push(baseCSSPrefix + 'safari3');
17743         }
17744         if (Ext.isSafari4) {
17745             cls.push(baseCSSPrefix + 'safari4');
17746         }
17747         if (Ext.isChrome) {
17748             cls.push(baseCSSPrefix + 'chrome');
17749         }
17750         if (Ext.isMac) {
17751             cls.push(baseCSSPrefix + 'mac');
17752         }
17753         if (Ext.isLinux) {
17754             cls.push(baseCSSPrefix + 'linux');
17755         }
17756         if (!Ext.supports.CSS3BorderRadius) {
17757             cls.push(baseCSSPrefix + 'nbr');
17758         }
17759         if (!Ext.supports.CSS3LinearGradient) {
17760             cls.push(baseCSSPrefix + 'nlg');
17761         }
17762         if (!Ext.scopeResetCSS) {
17763             cls.push(baseCSSPrefix + 'reset');
17764         }
17765
17766         // add to the parent to allow for selectors x-strict x-border-box, also set the isBorderBox property correctly
17767         if (html) {
17768             if (Ext.isStrict && (Ext.isIE6 || Ext.isIE7)) {
17769                 Ext.isBorderBox = false;
17770             }
17771             else {
17772                 Ext.isBorderBox = true;
17773             }
17774
17775             htmlCls.push(baseCSSPrefix + (Ext.isBorderBox ? 'border-box' : 'strict'));
17776             if (!Ext.isStrict) {
17777                 htmlCls.push(baseCSSPrefix + 'quirks');
17778                 if (Ext.isIE && !Ext.isStrict) {
17779                     Ext.isIEQuirks = true;
17780                 }
17781             }
17782             Ext.fly(html, '_internal').addCls(htmlCls);
17783         }
17784
17785         Ext.fly(bd, '_internal').addCls(cls);
17786         return true;
17787     };
17788
17789     Ext.onReady(initExtCss);
17790 })();
17791
17792 /**
17793  * @class Ext.EventObject
17794
17795 Just as {@link Ext.core.Element} wraps around a native DOM node, Ext.EventObject
17796 wraps the browser's native event-object normalizing cross-browser differences,
17797 such as which mouse button is clicked, keys pressed, mechanisms to stop
17798 event-propagation along with a method to prevent default actions from taking place.
17799
17800 For example:
17801
17802     function handleClick(e, t){ // e is not a standard event object, it is a Ext.EventObject
17803         e.preventDefault();
17804         var target = e.getTarget(); // same as t (the target HTMLElement)
17805         ...
17806     }
17807
17808     var myDiv = {@link Ext#get Ext.get}("myDiv");  // get reference to an {@link Ext.core.Element}
17809     myDiv.on(         // 'on' is shorthand for addListener
17810         "click",      // perform an action on click of myDiv
17811         handleClick   // reference to the action handler
17812     );
17813
17814     // other methods to do the same:
17815     Ext.EventManager.on("myDiv", 'click', handleClick);
17816     Ext.EventManager.addListener("myDiv", 'click', handleClick);
17817
17818  * @singleton
17819  * @markdown
17820  */
17821 Ext.define('Ext.EventObjectImpl', {
17822     uses: ['Ext.util.Point'],
17823
17824     /** Key constant @type Number */
17825     BACKSPACE: 8,
17826     /** Key constant @type Number */
17827     TAB: 9,
17828     /** Key constant @type Number */
17829     NUM_CENTER: 12,
17830     /** Key constant @type Number */
17831     ENTER: 13,
17832     /** Key constant @type Number */
17833     RETURN: 13,
17834     /** Key constant @type Number */
17835     SHIFT: 16,
17836     /** Key constant @type Number */
17837     CTRL: 17,
17838     /** Key constant @type Number */
17839     ALT: 18,
17840     /** Key constant @type Number */
17841     PAUSE: 19,
17842     /** Key constant @type Number */
17843     CAPS_LOCK: 20,
17844     /** Key constant @type Number */
17845     ESC: 27,
17846     /** Key constant @type Number */
17847     SPACE: 32,
17848     /** Key constant @type Number */
17849     PAGE_UP: 33,
17850     /** Key constant @type Number */
17851     PAGE_DOWN: 34,
17852     /** Key constant @type Number */
17853     END: 35,
17854     /** Key constant @type Number */
17855     HOME: 36,
17856     /** Key constant @type Number */
17857     LEFT: 37,
17858     /** Key constant @type Number */
17859     UP: 38,
17860     /** Key constant @type Number */
17861     RIGHT: 39,
17862     /** Key constant @type Number */
17863     DOWN: 40,
17864     /** Key constant @type Number */
17865     PRINT_SCREEN: 44,
17866     /** Key constant @type Number */
17867     INSERT: 45,
17868     /** Key constant @type Number */
17869     DELETE: 46,
17870     /** Key constant @type Number */
17871     ZERO: 48,
17872     /** Key constant @type Number */
17873     ONE: 49,
17874     /** Key constant @type Number */
17875     TWO: 50,
17876     /** Key constant @type Number */
17877     THREE: 51,
17878     /** Key constant @type Number */
17879     FOUR: 52,
17880     /** Key constant @type Number */
17881     FIVE: 53,
17882     /** Key constant @type Number */
17883     SIX: 54,
17884     /** Key constant @type Number */
17885     SEVEN: 55,
17886     /** Key constant @type Number */
17887     EIGHT: 56,
17888     /** Key constant @type Number */
17889     NINE: 57,
17890     /** Key constant @type Number */
17891     A: 65,
17892     /** Key constant @type Number */
17893     B: 66,
17894     /** Key constant @type Number */
17895     C: 67,
17896     /** Key constant @type Number */
17897     D: 68,
17898     /** Key constant @type Number */
17899     E: 69,
17900     /** Key constant @type Number */
17901     F: 70,
17902     /** Key constant @type Number */
17903     G: 71,
17904     /** Key constant @type Number */
17905     H: 72,
17906     /** Key constant @type Number */
17907     I: 73,
17908     /** Key constant @type Number */
17909     J: 74,
17910     /** Key constant @type Number */
17911     K: 75,
17912     /** Key constant @type Number */
17913     L: 76,
17914     /** Key constant @type Number */
17915     M: 77,
17916     /** Key constant @type Number */
17917     N: 78,
17918     /** Key constant @type Number */
17919     O: 79,
17920     /** Key constant @type Number */
17921     P: 80,
17922     /** Key constant @type Number */
17923     Q: 81,
17924     /** Key constant @type Number */
17925     R: 82,
17926     /** Key constant @type Number */
17927     S: 83,
17928     /** Key constant @type Number */
17929     T: 84,
17930     /** Key constant @type Number */
17931     U: 85,
17932     /** Key constant @type Number */
17933     V: 86,
17934     /** Key constant @type Number */
17935     W: 87,
17936     /** Key constant @type Number */
17937     X: 88,
17938     /** Key constant @type Number */
17939     Y: 89,
17940     /** Key constant @type Number */
17941     Z: 90,
17942     /** Key constant @type Number */
17943     CONTEXT_MENU: 93,
17944     /** Key constant @type Number */
17945     NUM_ZERO: 96,
17946     /** Key constant @type Number */
17947     NUM_ONE: 97,
17948     /** Key constant @type Number */
17949     NUM_TWO: 98,
17950     /** Key constant @type Number */
17951     NUM_THREE: 99,
17952     /** Key constant @type Number */
17953     NUM_FOUR: 100,
17954     /** Key constant @type Number */
17955     NUM_FIVE: 101,
17956     /** Key constant @type Number */
17957     NUM_SIX: 102,
17958     /** Key constant @type Number */
17959     NUM_SEVEN: 103,
17960     /** Key constant @type Number */
17961     NUM_EIGHT: 104,
17962     /** Key constant @type Number */
17963     NUM_NINE: 105,
17964     /** Key constant @type Number */
17965     NUM_MULTIPLY: 106,
17966     /** Key constant @type Number */
17967     NUM_PLUS: 107,
17968     /** Key constant @type Number */
17969     NUM_MINUS: 109,
17970     /** Key constant @type Number */
17971     NUM_PERIOD: 110,
17972     /** Key constant @type Number */
17973     NUM_DIVISION: 111,
17974     /** Key constant @type Number */
17975     F1: 112,
17976     /** Key constant @type Number */
17977     F2: 113,
17978     /** Key constant @type Number */
17979     F3: 114,
17980     /** Key constant @type Number */
17981     F4: 115,
17982     /** Key constant @type Number */
17983     F5: 116,
17984     /** Key constant @type Number */
17985     F6: 117,
17986     /** Key constant @type Number */
17987     F7: 118,
17988     /** Key constant @type Number */
17989     F8: 119,
17990     /** Key constant @type Number */
17991     F9: 120,
17992     /** Key constant @type Number */
17993     F10: 121,
17994     /** Key constant @type Number */
17995     F11: 122,
17996     /** Key constant @type Number */
17997     F12: 123,
17998     /**
17999      * The mouse wheel delta scaling factor. This value depends on browser version and OS and
18000      * attempts to produce a similar scrolling experience across all platforms and browsers.
18001      * 
18002      * To change this value:
18003      * 
18004      *      Ext.EventObjectImpl.prototype.WHEEL_SCALE = 72;
18005      * 
18006      * @type Number
18007      * @markdown
18008      */
18009     WHEEL_SCALE: (function () {
18010         var scale;
18011
18012         if (Ext.isGecko) {
18013             // Firefox uses 3 on all platforms
18014             scale = 3;
18015         } else if (Ext.isMac) {
18016             // Continuous scrolling devices have momentum and produce much more scroll than
18017             // discrete devices on the same OS and browser. To make things exciting, Safari
18018             // (and not Chrome) changed from small values to 120 (like IE).
18019
18020             if (Ext.isSafari && Ext.webKitVersion >= 532.0) {
18021                 // Safari changed the scrolling factor to match IE (for details see
18022                 // https://bugs.webkit.org/show_bug.cgi?id=24368). The WebKit version where this
18023                 // change was introduced was 532.0
18024                 //      Detailed discussion:
18025                 //      https://bugs.webkit.org/show_bug.cgi?id=29601
18026                 //      http://trac.webkit.org/browser/trunk/WebKit/chromium/src/mac/WebInputEventFactory.mm#L1063
18027                 scale = 120;
18028             } else {
18029                 // MS optical wheel mouse produces multiples of 12 which is close enough
18030                 // to help tame the speed of the continuous mice...
18031                 scale = 12;
18032             }
18033
18034             // Momentum scrolling produces very fast scrolling, so increase the scale factor
18035             // to help produce similar results cross platform. This could be even larger and
18036             // it would help those mice, but other mice would become almost unusable as a
18037             // result (since we cannot tell which device type is in use).
18038             scale *= 3;
18039         } else {
18040             // IE, Opera and other Windows browsers use 120.
18041             scale = 120;
18042         }
18043
18044         return scale;
18045     })(),
18046
18047     /**
18048      * Simple click regex
18049      * @private
18050      */
18051     clickRe: /(dbl)?click/,
18052     // safari keypress events for special keys return bad keycodes
18053     safariKeys: {
18054         3: 13, // enter
18055         63234: 37, // left
18056         63235: 39, // right
18057         63232: 38, // up
18058         63233: 40, // down
18059         63276: 33, // page up
18060         63277: 34, // page down
18061         63272: 46, // delete
18062         63273: 36, // home
18063         63275: 35 // end
18064     },
18065     // normalize button clicks, don't see any way to feature detect this.
18066     btnMap: Ext.isIE ? {
18067         1: 0,
18068         4: 1,
18069         2: 2
18070     } : {
18071         0: 0,
18072         1: 1,
18073         2: 2
18074     },
18075
18076     constructor: function(event, freezeEvent){
18077         if (event) {
18078             this.setEvent(event.browserEvent || event, freezeEvent);
18079         }
18080     },
18081
18082     setEvent: function(event, freezeEvent){
18083         var me = this, button, options;
18084
18085         if (event == me || (event && event.browserEvent)) { // already wrapped
18086             return event;
18087         }
18088         me.browserEvent = event;
18089         if (event) {
18090             // normalize buttons
18091             button = event.button ? me.btnMap[event.button] : (event.which ? event.which - 1 : -1);
18092             if (me.clickRe.test(event.type) && button == -1) {
18093                 button = 0;
18094             }
18095             options = {
18096                 type: event.type,
18097                 button: button,
18098                 shiftKey: event.shiftKey,
18099                 // mac metaKey behaves like ctrlKey
18100                 ctrlKey: event.ctrlKey || event.metaKey || false,
18101                 altKey: event.altKey,
18102                 // in getKey these will be normalized for the mac
18103                 keyCode: event.keyCode,
18104                 charCode: event.charCode,
18105                 // cache the targets for the delayed and or buffered events
18106                 target: Ext.EventManager.getTarget(event),
18107                 relatedTarget: Ext.EventManager.getRelatedTarget(event),
18108                 currentTarget: event.currentTarget,
18109                 xy: (freezeEvent ? me.getXY() : null)
18110             };
18111         } else {
18112             options = {
18113                 button: -1,
18114                 shiftKey: false,
18115                 ctrlKey: false,
18116                 altKey: false,
18117                 keyCode: 0,
18118                 charCode: 0,
18119                 target: null,
18120                 xy: [0, 0]
18121             };
18122         }
18123         Ext.apply(me, options);
18124         return me;
18125     },
18126
18127     /**
18128      * Stop the event (preventDefault and stopPropagation)
18129      */
18130     stopEvent: function(){
18131         this.stopPropagation();
18132         this.preventDefault();
18133     },
18134
18135     /**
18136      * Prevents the browsers default handling of the event.
18137      */
18138     preventDefault: function(){
18139         if (this.browserEvent) {
18140             Ext.EventManager.preventDefault(this.browserEvent);
18141         }
18142     },
18143
18144     /**
18145      * Cancels bubbling of the event.
18146      */
18147     stopPropagation: function(){
18148         var browserEvent = this.browserEvent;
18149
18150         if (browserEvent) {
18151             if (browserEvent.type == 'mousedown') {
18152                 Ext.EventManager.stoppedMouseDownEvent.fire(this);
18153             }
18154             Ext.EventManager.stopPropagation(browserEvent);
18155         }
18156     },
18157
18158     /**
18159      * Gets the character code for the event.
18160      * @return {Number}
18161      */
18162     getCharCode: function(){
18163         return this.charCode || this.keyCode;
18164     },
18165
18166     /**
18167      * Returns a normalized keyCode for the event.
18168      * @return {Number} The key code
18169      */
18170     getKey: function(){
18171         return this.normalizeKey(this.keyCode || this.charCode);
18172     },
18173
18174     /**
18175      * Normalize key codes across browsers
18176      * @private
18177      * @param {Number} key The key code
18178      * @return {Number} The normalized code
18179      */
18180     normalizeKey: function(key){
18181         // can't feature detect this
18182         return Ext.isWebKit ? (this.safariKeys[key] || key) : key;
18183     },
18184
18185     /**
18186      * Gets the x coordinate of the event.
18187      * @return {Number}
18188      * @deprecated 4.0 Replaced by {@link #getX}
18189      */
18190     getPageX: function(){
18191         return this.getX();
18192     },
18193
18194     /**
18195      * Gets the y coordinate of the event.
18196      * @return {Number}
18197      * @deprecated 4.0 Replaced by {@link #getY}
18198      */
18199     getPageY: function(){
18200         return this.getY();
18201     },
18202     
18203     /**
18204      * Gets the x coordinate of the event.
18205      * @return {Number}
18206      */
18207     getX: function() {
18208         return this.getXY()[0];
18209     },    
18210     
18211     /**
18212      * Gets the y coordinate of the event.
18213      * @return {Number}
18214      */
18215     getY: function() {
18216         return this.getXY()[1];
18217     },
18218         
18219     /**
18220      * Gets the page coordinates of the event.
18221      * @return {Array} The xy values like [x, y]
18222      */
18223     getXY: function() {
18224         if (!this.xy) {
18225             // same for XY
18226             this.xy = Ext.EventManager.getPageXY(this.browserEvent);
18227         }
18228         return this.xy;
18229     },
18230
18231     /**
18232      * Gets the target for the event.
18233      * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
18234      * @param {Number/Mixed} maxDepth (optional) The max depth to search as a number or element (defaults to 10 || document.body)
18235      * @param {Boolean} returnEl (optional) True to return a Ext.core.Element object instead of DOM node
18236      * @return {HTMLelement}
18237      */
18238     getTarget : function(selector, maxDepth, returnEl){
18239         if (selector) {
18240             return Ext.fly(this.target).findParent(selector, maxDepth, returnEl);
18241         }
18242         return returnEl ? Ext.get(this.target) : this.target;
18243     },
18244
18245     /**
18246      * Gets the related target.
18247      * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
18248      * @param {Number/Mixed} maxDepth (optional) The max depth to search as a number or element (defaults to 10 || document.body)
18249      * @param {Boolean} returnEl (optional) True to return a Ext.core.Element object instead of DOM node
18250      * @return {HTMLElement}
18251      */
18252     getRelatedTarget : function(selector, maxDepth, returnEl){
18253         if (selector) {
18254             return Ext.fly(this.relatedTarget).findParent(selector, maxDepth, returnEl);
18255         }
18256         return returnEl ? Ext.get(this.relatedTarget) : this.relatedTarget;
18257     },
18258
18259     /**
18260      * Correctly scales a given wheel delta.
18261      * @param {Number} delta The delta value.
18262      */
18263     correctWheelDelta : function (delta) {
18264         var scale = this.WHEEL_SCALE,
18265             ret = Math.round(delta / scale + 0.5);
18266
18267         if (!ret && delta) {
18268             ret = (delta < 0) ? -1 : 1; // don't allow non-zero deltas to go to zero!
18269         }
18270
18271         return ret;
18272     },
18273
18274     /**
18275      * Returns the mouse wheel deltas for this event.
18276      * @return {Object} An object with "x" and "y" properties holding the mouse wheel deltas.
18277      */
18278     getWheelDeltas : function () {
18279         var me = this,
18280             event = me.browserEvent,
18281             dx = 0, dy = 0; // the deltas
18282
18283         if (Ext.isDefined(event.wheelDeltaX)) { // WebKit has both dimensions
18284             dx = event.wheelDeltaX;
18285             dy = event.wheelDeltaY;
18286         } else if (event.wheelDelta) { // old WebKit and IE
18287             dy = event.wheelDelta;
18288         } else if (event.detail) { // Gecko
18289             dy = -event.detail; // gecko is backwards
18290
18291             // Gecko sometimes returns really big values if the user changes settings to
18292             // scroll a whole page per scroll
18293             if (dy > 100) {
18294                 dy = 3;
18295             } else if (dy < -100) {
18296                 dy = -3;
18297             }
18298
18299             // Firefox 3.1 adds an axis field to the event to indicate direction of
18300             // scroll.  See https://developer.mozilla.org/en/Gecko-Specific_DOM_Events
18301             if (Ext.isDefined(event.axis) && event.axis === event.HORIZONTAL_AXIS) {
18302                 dx = dy;
18303                 dy = 0;
18304             }
18305         }
18306
18307         return {
18308             x: me.correctWheelDelta(dx),
18309             y: me.correctWheelDelta(dy)
18310         };
18311     },
18312
18313     /**
18314      * Normalizes mouse wheel y-delta across browsers. To get x-delta information, use
18315      * {@link #getWheelDeltas} instead.
18316      * @return {Number} The mouse wheel y-delta
18317      */
18318     getWheelDelta : function(){
18319         var deltas = this.getWheelDeltas();
18320
18321         return deltas.y;
18322     },
18323
18324     /**
18325     * 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.
18326     * Example usage:<pre><code>
18327 // Handle click on any child of an element
18328 Ext.getBody().on('click', function(e){
18329     if(e.within('some-el')){
18330         alert('Clicked on a child of some-el!');
18331     }
18332 });
18333
18334 // Handle click directly on an element, ignoring clicks on child nodes
18335 Ext.getBody().on('click', function(e,t){
18336     if((t.id == 'some-el') && !e.within(t, true)){
18337         alert('Clicked directly on some-el!');
18338     }
18339 });
18340 </code></pre>
18341      * @param {Mixed} el The id, DOM element or Ext.core.Element to check
18342      * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
18343      * @param {Boolean} allowEl {optional} true to also check if the passed element is the target or related target
18344      * @return {Boolean}
18345      */
18346     within : function(el, related, allowEl){
18347         if(el){
18348             var t = related ? this.getRelatedTarget() : this.getTarget(),
18349                 result;
18350
18351             if (t) {
18352                 result = Ext.fly(el).contains(t);
18353                 if (!result && allowEl) {
18354                     result = t == Ext.getDom(el);
18355                 }
18356                 return result;
18357             }
18358         }
18359         return false;
18360     },
18361
18362     /**
18363      * Checks if the key pressed was a "navigation" key
18364      * @return {Boolean} True if the press is a navigation keypress
18365      */
18366     isNavKeyPress : function(){
18367         var me = this,
18368             k = this.normalizeKey(me.keyCode);
18369
18370        return (k >= 33 && k <= 40) ||  // Page Up/Down, End, Home, Left, Up, Right, Down
18371        k == me.RETURN ||
18372        k == me.TAB ||
18373        k == me.ESC;
18374     },
18375
18376     /**
18377      * Checks if the key pressed was a "special" key
18378      * @return {Boolean} True if the press is a special keypress
18379      */
18380     isSpecialKey : function(){
18381         var k = this.normalizeKey(this.keyCode);
18382         return (this.type == 'keypress' && this.ctrlKey) ||
18383         this.isNavKeyPress() ||
18384         (k == this.BACKSPACE) || // Backspace
18385         (k >= 16 && k <= 20) || // Shift, Ctrl, Alt, Pause, Caps Lock
18386         (k >= 44 && k <= 46);   // Print Screen, Insert, Delete
18387     },
18388
18389     /**
18390      * Returns a point object that consists of the object coordinates.
18391      * @return {Ext.util.Point} point
18392      */
18393     getPoint : function(){
18394         var xy = this.getXY();
18395         return Ext.create('Ext.util.Point', xy[0], xy[1]);
18396     },
18397
18398    /**
18399     * Returns true if the control, meta, shift or alt key was pressed during this event.
18400     * @return {Boolean}
18401     */
18402     hasModifier : function(){
18403         return this.ctrlKey || this.altKey || this.shiftKey || this.metaKey;
18404     },
18405
18406     /**
18407      * Injects a DOM event using the data in this object and (optionally) a new target.
18408      * This is a low-level technique and not likely to be used by application code. The
18409      * currently supported event types are:
18410      * <p><b>HTMLEvents</b></p>
18411      * <ul>
18412      * <li>load</li>
18413      * <li>unload</li>
18414      * <li>select</li>
18415      * <li>change</li>
18416      * <li>submit</li>
18417      * <li>reset</li>
18418      * <li>resize</li>
18419      * <li>scroll</li>
18420      * </ul>
18421      * <p><b>MouseEvents</b></p>
18422      * <ul>
18423      * <li>click</li>
18424      * <li>dblclick</li>
18425      * <li>mousedown</li>
18426      * <li>mouseup</li>
18427      * <li>mouseover</li>
18428      * <li>mousemove</li>
18429      * <li>mouseout</li>
18430      * </ul>
18431      * <p><b>UIEvents</b></p>
18432      * <ul>
18433      * <li>focusin</li>
18434      * <li>focusout</li>
18435      * <li>activate</li>
18436      * <li>focus</li>
18437      * <li>blur</li>
18438      * </ul>
18439      * @param {Element/HTMLElement} target If specified, the target for the event. This
18440      * is likely to be used when relaying a DOM event. If not specified, {@link #getTarget}
18441      * is used to determine the target.
18442      */
18443     injectEvent: function () {
18444         var API,
18445             dispatchers = {}; // keyed by event type (e.g., 'mousedown')
18446
18447         // Good reference: http://developer.yahoo.com/yui/docs/UserAction.js.html
18448
18449         // IE9 has createEvent, but this code causes major problems with htmleditor (it
18450         // blocks all mouse events and maybe more). TODO
18451
18452         if (!Ext.isIE && document.createEvent) { // if (DOM compliant)
18453             API = {
18454                 createHtmlEvent: function (doc, type, bubbles, cancelable) {
18455                     var event = doc.createEvent('HTMLEvents');
18456
18457                     event.initEvent(type, bubbles, cancelable);
18458                     return event;
18459                 },
18460
18461                 createMouseEvent: function (doc, type, bubbles, cancelable, detail,
18462                                             clientX, clientY, ctrlKey, altKey, shiftKey, metaKey,
18463                                             button, relatedTarget) {
18464                     var event = doc.createEvent('MouseEvents'),
18465                         view = doc.defaultView || window;
18466
18467                     if (event.initMouseEvent) {
18468                         event.initMouseEvent(type, bubbles, cancelable, view, detail,
18469                                     clientX, clientY, clientX, clientY, ctrlKey, altKey,
18470                                     shiftKey, metaKey, button, relatedTarget);
18471                     } else { // old Safari
18472                         event = doc.createEvent('UIEvents');
18473                         event.initEvent(type, bubbles, cancelable);
18474                         event.view = view;
18475                         event.detail = detail;
18476                         event.screenX = clientX;
18477                         event.screenY = clientY;
18478                         event.clientX = clientX;
18479                         event.clientY = clientY;
18480                         event.ctrlKey = ctrlKey;
18481                         event.altKey = altKey;
18482                         event.metaKey = metaKey;
18483                         event.shiftKey = shiftKey;
18484                         event.button = button;
18485                         event.relatedTarget = relatedTarget;
18486                     }
18487
18488                     return event;
18489                 },
18490
18491                 createUIEvent: function (doc, type, bubbles, cancelable, detail) {
18492                     var event = doc.createEvent('UIEvents'),
18493                         view = doc.defaultView || window;
18494
18495                     event.initUIEvent(type, bubbles, cancelable, view, detail);
18496                     return event;
18497                 },
18498
18499                 fireEvent: function (target, type, event) {
18500                     target.dispatchEvent(event);
18501                 },
18502
18503                 fixTarget: function (target) {
18504                     // Safari3 doesn't have window.dispatchEvent()
18505                     if (target == window && !target.dispatchEvent) {
18506                         return document;
18507                     }
18508
18509                     return target;
18510                 }
18511             };
18512         } else if (document.createEventObject) { // else if (IE)
18513             var crazyIEButtons = { 0: 1, 1: 4, 2: 2 };
18514
18515             API = {
18516                 createHtmlEvent: function (doc, type, bubbles, cancelable) {
18517                     var event = doc.createEventObject();
18518                     event.bubbles = bubbles;
18519                     event.cancelable = cancelable;
18520                     return event;
18521                 },
18522
18523                 createMouseEvent: function (doc, type, bubbles, cancelable, detail,
18524                                             clientX, clientY, ctrlKey, altKey, shiftKey, metaKey,
18525                                             button, relatedTarget) {
18526                     var event = doc.createEventObject();
18527                     event.bubbles = bubbles;
18528                     event.cancelable = cancelable;
18529                     event.detail = detail;
18530                     event.screenX = clientX;
18531                     event.screenY = clientY;
18532                     event.clientX = clientX;
18533                     event.clientY = clientY;
18534                     event.ctrlKey = ctrlKey;
18535                     event.altKey = altKey;
18536                     event.shiftKey = shiftKey;
18537                     event.metaKey = metaKey;
18538                     event.button = crazyIEButtons[button] || button;
18539                     event.relatedTarget = relatedTarget; // cannot assign to/fromElement
18540                     return event;
18541                 },
18542
18543                 createUIEvent: function (doc, type, bubbles, cancelable, detail) {
18544                     var event = doc.createEventObject();
18545                     event.bubbles = bubbles;
18546                     event.cancelable = cancelable;
18547                     return event;
18548                 },
18549
18550                 fireEvent: function (target, type, event) {
18551                     target.fireEvent('on' + type, event);
18552                 },
18553
18554                 fixTarget: function (target) {
18555                     if (target == document) {
18556                         // IE6,IE7 thinks window==document and doesn't have window.fireEvent()
18557                         // IE6,IE7 cannot properly call document.fireEvent()
18558                         return document.documentElement;
18559                     }
18560
18561                     return target;
18562                 }
18563             };
18564         }
18565
18566         //----------------
18567         // HTMLEvents
18568
18569         Ext.Object.each({
18570                 load:   [false, false],
18571                 unload: [false, false],
18572                 select: [true, false],
18573                 change: [true, false],
18574                 submit: [true, true],
18575                 reset:  [true, false],
18576                 resize: [true, false],
18577                 scroll: [true, false]
18578             },
18579             function (name, value) {
18580                 var bubbles = value[0], cancelable = value[1];
18581                 dispatchers[name] = function (targetEl, srcEvent) {
18582                     var e = API.createHtmlEvent(name, bubbles, cancelable);
18583                     API.fireEvent(targetEl, name, e);
18584                 };
18585             });
18586
18587         //----------------
18588         // MouseEvents
18589
18590         function createMouseEventDispatcher (type, detail) {
18591             var cancelable = (type != 'mousemove');
18592             return function (targetEl, srcEvent) {
18593                 var xy = srcEvent.getXY(),
18594                     e = API.createMouseEvent(targetEl.ownerDocument, type, true, cancelable,
18595                                 detail, xy[0], xy[1], srcEvent.ctrlKey, srcEvent.altKey,
18596                                 srcEvent.shiftKey, srcEvent.metaKey, srcEvent.button,
18597                                 srcEvent.relatedTarget);
18598                 API.fireEvent(targetEl, type, e);
18599             };
18600         }
18601
18602         Ext.each(['click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mousemove', 'mouseout'],
18603             function (eventName) {
18604                 dispatchers[eventName] = createMouseEventDispatcher(eventName, 1);
18605             });
18606
18607         //----------------
18608         // UIEvents
18609
18610         Ext.Object.each({
18611                 focusin:  [true, false],
18612                 focusout: [true, false],
18613                 activate: [true, true],
18614                 focus:    [false, false],
18615                 blur:     [false, false]
18616             },
18617             function (name, value) {
18618                 var bubbles = value[0], cancelable = value[1];
18619                 dispatchers[name] = function (targetEl, srcEvent) {
18620                     var e = API.createUIEvent(targetEl.ownerDocument, name, bubbles, cancelable, 1);
18621                     API.fireEvent(targetEl, name, e);
18622                 };
18623             });
18624
18625         //---------
18626         if (!API) {
18627             // not even sure what ancient browsers fall into this category...
18628
18629             dispatchers = {}; // never mind all those we just built :P
18630
18631             API = {
18632                 fixTarget: function (t) {
18633                     return t;
18634                 }
18635             };
18636         }
18637
18638         function cannotInject (target, srcEvent) {
18639             // TODO log something
18640         }
18641
18642         return function (target) {
18643             var me = this,
18644                 dispatcher = dispatchers[me.type] || cannotInject,
18645                 t = target ? (target.dom || target) : me.getTarget();
18646
18647             t = API.fixTarget(t);
18648             dispatcher(t, me);
18649         };
18650     }() // call to produce method
18651
18652 }, function() {
18653
18654 Ext.EventObject = new Ext.EventObjectImpl();
18655
18656 });
18657
18658
18659 /**
18660  * @class Ext.core.Element
18661  */
18662 (function(){
18663     var doc = document,
18664         activeElement = null,
18665         isCSS1 = doc.compatMode == "CSS1Compat",
18666         ELEMENT = Ext.core.Element,
18667         fly = function(el){
18668             if (!_fly) {
18669                 _fly = new Ext.core.Element.Flyweight();
18670             }
18671             _fly.dom = el;
18672             return _fly;
18673         }, _fly;
18674
18675     // If the browser does not support document.activeElement we need some assistance.
18676     // This covers old Safari 3.2 (4.0 added activeElement along with just about all
18677     // other browsers). We need this support to handle issues with old Safari.
18678     if (!('activeElement' in doc) && doc.addEventListener) {
18679         doc.addEventListener('focus',
18680             function (ev) {
18681                 if (ev && ev.target) {
18682                     activeElement = (ev.target == doc) ? null : ev.target;
18683                 }
18684             }, true);
18685     }
18686
18687     /*
18688      * Helper function to create the function that will restore the selection.
18689      */
18690     function makeSelectionRestoreFn (activeEl, start, end) {
18691         return function () {
18692             activeEl.selectionStart = start;
18693             activeEl.selectionEnd = end;
18694         };
18695     }
18696
18697     Ext.apply(ELEMENT, {
18698         isAncestor : function(p, c) {
18699             var ret = false;
18700
18701             p = Ext.getDom(p);
18702             c = Ext.getDom(c);
18703             if (p && c) {
18704                 if (p.contains) {
18705                     return p.contains(c);
18706                 } else if (p.compareDocumentPosition) {
18707                     return !!(p.compareDocumentPosition(c) & 16);
18708                 } else {
18709                     while ((c = c.parentNode)) {
18710                         ret = c == p || ret;
18711                     }
18712                 }
18713             }
18714             return ret;
18715         },
18716
18717         /**
18718          * Returns the active element in the DOM. If the browser supports activeElement
18719          * on the document, this is returned. If not, the focus is tracked and the active
18720          * element is maintained internally.
18721          * @return {HTMLElement} The active (focused) element in the document.
18722          */
18723         getActiveElement: function () {
18724             return doc.activeElement || activeElement;
18725         },
18726
18727         /**
18728          * Creates a function to call to clean up problems with the work-around for the
18729          * WebKit RightMargin bug. The work-around is to add "display: 'inline-block'" to
18730          * the element before calling getComputedStyle and then to restore its original
18731          * display value. The problem with this is that it corrupts the selection of an
18732          * INPUT or TEXTAREA element (as in the "I-beam" goes away but ths focus remains).
18733          * To cleanup after this, we need to capture the selection of any such element and
18734          * then restore it after we have restored the display style.
18735          *
18736          * @param target {Element} The top-most element being adjusted.
18737          * @private
18738          */
18739         getRightMarginFixCleaner: function (target) {
18740             var supports = Ext.supports,
18741                 hasInputBug = supports.DisplayChangeInputSelectionBug,
18742                 hasTextAreaBug = supports.DisplayChangeTextAreaSelectionBug;
18743
18744             if (hasInputBug || hasTextAreaBug) {
18745                 var activeEl = doc.activeElement || activeElement, // save a call
18746                     tag = activeEl && activeEl.tagName,
18747                     start,
18748                     end;
18749
18750                 if ((hasTextAreaBug && tag == 'TEXTAREA') ||
18751                     (hasInputBug && tag == 'INPUT' && activeEl.type == 'text')) {
18752                     if (ELEMENT.isAncestor(target, activeEl)) {
18753                         start = activeEl.selectionStart;
18754                         end = activeEl.selectionEnd;
18755
18756                         if (Ext.isNumber(start) && Ext.isNumber(end)) { // to be safe...
18757                             // We don't create the raw closure here inline because that
18758                             // will be costly even if we don't want to return it (nested
18759                             // function decls and exprs are often instantiated on entry
18760                             // regardless of whether execution ever reaches them):
18761                             return makeSelectionRestoreFn(activeEl, start, end);
18762                         }
18763                     }
18764                 }
18765             }
18766
18767             return Ext.emptyFn; // avoid special cases, just return a nop
18768         },
18769
18770         getViewWidth : function(full) {
18771             return full ? ELEMENT.getDocumentWidth() : ELEMENT.getViewportWidth();
18772         },
18773
18774         getViewHeight : function(full) {
18775             return full ? ELEMENT.getDocumentHeight() : ELEMENT.getViewportHeight();
18776         },
18777
18778         getDocumentHeight: function() {
18779             return Math.max(!isCSS1 ? doc.body.scrollHeight : doc.documentElement.scrollHeight, ELEMENT.getViewportHeight());
18780         },
18781
18782         getDocumentWidth: function() {
18783             return Math.max(!isCSS1 ? doc.body.scrollWidth : doc.documentElement.scrollWidth, ELEMENT.getViewportWidth());
18784         },
18785
18786         getViewportHeight: function(){
18787             return Ext.isIE ?
18788                    (Ext.isStrict ? doc.documentElement.clientHeight : doc.body.clientHeight) :
18789                    self.innerHeight;
18790         },
18791
18792         getViewportWidth : function() {
18793             return (!Ext.isStrict && !Ext.isOpera) ? doc.body.clientWidth :
18794                    Ext.isIE ? doc.documentElement.clientWidth : self.innerWidth;
18795         },
18796
18797         getY : function(el) {
18798             return ELEMENT.getXY(el)[1];
18799         },
18800
18801         getX : function(el) {
18802             return ELEMENT.getXY(el)[0];
18803         },
18804
18805         getXY : function(el) {
18806             var p,
18807                 pe,
18808                 b,
18809                 bt,
18810                 bl,
18811                 dbd,
18812                 x = 0,
18813                 y = 0,
18814                 scroll,
18815                 hasAbsolute,
18816                 bd = (doc.body || doc.documentElement),
18817                 ret = [0,0];
18818
18819             el = Ext.getDom(el);
18820
18821             if(el != bd){
18822                 hasAbsolute = fly(el).isStyle("position", "absolute");
18823
18824                 if (el.getBoundingClientRect) {
18825                     b = el.getBoundingClientRect();
18826                     scroll = fly(document).getScroll();
18827                     ret = [Math.round(b.left + scroll.left), Math.round(b.top + scroll.top)];
18828                 } else {
18829                     p = el;
18830
18831                     while (p) {
18832                         pe = fly(p);
18833                         x += p.offsetLeft;
18834                         y += p.offsetTop;
18835
18836                         hasAbsolute = hasAbsolute || pe.isStyle("position", "absolute");
18837
18838                         if (Ext.isGecko) {
18839                             y += bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
18840                             x += bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
18841
18842                             if (p != el && !pe.isStyle('overflow','visible')) {
18843                                 x += bl;
18844                                 y += bt;
18845                             }
18846                         }
18847                         p = p.offsetParent;
18848                     }
18849
18850                     if (Ext.isSafari && hasAbsolute) {
18851                         x -= bd.offsetLeft;
18852                         y -= bd.offsetTop;
18853                     }
18854
18855                     if (Ext.isGecko && !hasAbsolute) {
18856                         dbd = fly(bd);
18857                         x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
18858                         y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
18859                     }
18860
18861                     p = el.parentNode;
18862                     while (p && p != bd) {
18863                         if (!Ext.isOpera || (p.tagName != 'TR' && !fly(p).isStyle("display", "inline"))) {
18864                             x -= p.scrollLeft;
18865                             y -= p.scrollTop;
18866                         }
18867                         p = p.parentNode;
18868                     }
18869                     ret = [x,y];
18870                 }
18871             }
18872             return ret;
18873         },
18874
18875         setXY : function(el, xy) {
18876             (el = Ext.fly(el, '_setXY')).position();
18877
18878             var pts = el.translatePoints(xy),
18879                 style = el.dom.style,
18880                 pos;
18881
18882             for (pos in pts) {
18883                 if (!isNaN(pts[pos])) {
18884                     style[pos] = pts[pos] + "px";
18885                 }
18886             }
18887         },
18888
18889         setX : function(el, x) {
18890             ELEMENT.setXY(el, [x, false]);
18891         },
18892
18893         setY : function(el, y) {
18894             ELEMENT.setXY(el, [false, y]);
18895         },
18896
18897         /**
18898          * Serializes a DOM form into a url encoded string
18899          * @param {Object} form The form
18900          * @return {String} The url encoded form
18901          */
18902         serializeForm: function(form) {
18903             var fElements = form.elements || (document.forms[form] || Ext.getDom(form)).elements,
18904                 hasSubmit = false,
18905                 encoder = encodeURIComponent,
18906                 name,
18907                 data = '',
18908                 type,
18909                 hasValue;
18910
18911             Ext.each(fElements, function(element){
18912                 name = element.name;
18913                 type = element.type;
18914
18915                 if (!element.disabled && name) {
18916                     if (/select-(one|multiple)/i.test(type)) {
18917                         Ext.each(element.options, function(opt){
18918                             if (opt.selected) {
18919                                 hasValue = opt.hasAttribute ? opt.hasAttribute('value') : opt.getAttributeNode('value').specified;
18920                                 data += Ext.String.format("{0}={1}&", encoder(name), encoder(hasValue ? opt.value : opt.text));
18921                             }
18922                         });
18923                     } else if (!(/file|undefined|reset|button/i.test(type))) {
18924                         if (!(/radio|checkbox/i.test(type) && !element.checked) && !(type == 'submit' && hasSubmit)) {
18925                             data += encoder(name) + '=' + encoder(element.value) + '&';
18926                             hasSubmit = /submit/i.test(type);
18927                         }
18928                     }
18929                 }
18930             });
18931             return data.substr(0, data.length - 1);
18932         }
18933     });
18934 })();
18935
18936 /**
18937  * @class Ext.core.Element
18938  */
18939
18940 Ext.core.Element.addMethods({
18941
18942     /**
18943      * Monitors this Element for the mouse leaving. Calls the function after the specified delay only if
18944      * the mouse was not moved back into the Element within the delay. If the mouse <i>was</i> moved
18945      * back in, the function is not called.
18946      * @param {Number} delay The delay <b>in milliseconds</b> to wait for possible mouse re-entry before calling the handler function.
18947      * @param {Function} handler The function to call if the mouse remains outside of this Element for the specified time.
18948      * @param {Object} scope The scope (<code>this</code> reference) in which the handler function executes. Defaults to this Element.
18949      * @return {Object} The listeners object which was added to this element so that monitoring can be stopped. Example usage:</pre><code>
18950 // Hide the menu if the mouse moves out for 250ms or more
18951 this.mouseLeaveMonitor = this.menuEl.monitorMouseLeave(250, this.hideMenu, this);
18952
18953 ...
18954 // Remove mouseleave monitor on menu destroy
18955 this.menuEl.un(this.mouseLeaveMonitor);
18956 </code></pre>
18957      */
18958     monitorMouseLeave: function(delay, handler, scope) {
18959         var me = this,
18960             timer,
18961             listeners = {
18962                 mouseleave: function(e) {
18963                     timer = setTimeout(Ext.Function.bind(handler, scope||me, [e]), delay);
18964                 },
18965                 mouseenter: function() {
18966                     clearTimeout(timer);
18967                 },
18968                 freezeEvent: true
18969             };
18970
18971         me.on(listeners);
18972         return listeners;
18973     },
18974
18975     /**
18976      * Stops the specified event(s) from bubbling and optionally prevents the default action
18977      * @param {String/Array} eventName an event / array of events to stop from bubbling
18978      * @param {Boolean} preventDefault (optional) true to prevent the default action too
18979      * @return {Ext.core.Element} this
18980      */
18981     swallowEvent : function(eventName, preventDefault) {
18982         var me = this;
18983         function fn(e) {
18984             e.stopPropagation();
18985             if (preventDefault) {
18986                 e.preventDefault();
18987             }
18988         }
18989         
18990         if (Ext.isArray(eventName)) {
18991             Ext.each(eventName, function(e) {
18992                  me.on(e, fn);
18993             });
18994             return me;
18995         }
18996         me.on(eventName, fn);
18997         return me;
18998     },
18999
19000     /**
19001      * Create an event handler on this element such that when the event fires and is handled by this element,
19002      * it will be relayed to another object (i.e., fired again as if it originated from that object instead).
19003      * @param {String} eventName The type of event to relay
19004      * @param {Object} object Any object that extends {@link Ext.util.Observable} that will provide the context
19005      * for firing the relayed event
19006      */
19007     relayEvent : function(eventName, observable) {
19008         this.on(eventName, function(e) {
19009             observable.fireEvent(eventName, e);
19010         });
19011     },
19012
19013     /**
19014      * Removes Empty, or whitespace filled text nodes. Combines adjacent text nodes.
19015      * @param {Boolean} forceReclean (optional) By default the element
19016      * keeps track if it has been cleaned already so
19017      * you can call this over and over. However, if you update the element and
19018      * need to force a reclean, you can pass true.
19019      */
19020     clean : function(forceReclean) {
19021         var me  = this,
19022             dom = me.dom,
19023             n   = dom.firstChild,
19024             nx,
19025             ni  = -1;
19026
19027         if (Ext.core.Element.data(dom, 'isCleaned') && forceReclean !== true) {
19028             return me;
19029         }
19030
19031         while (n) {
19032             nx = n.nextSibling;
19033             if (n.nodeType == 3) {
19034                 // Remove empty/whitespace text nodes
19035                 if (!(/\S/.test(n.nodeValue))) {
19036                     dom.removeChild(n);
19037                 // Combine adjacent text nodes
19038                 } else if (nx && nx.nodeType == 3) {
19039                     n.appendData(Ext.String.trim(nx.data));
19040                     dom.removeChild(nx);
19041                     nx = n.nextSibling;
19042                     n.nodeIndex = ++ni;
19043                 }
19044             } else {
19045                 // Recursively clean
19046                 Ext.fly(n).clean();
19047                 n.nodeIndex = ++ni;
19048             }
19049             n = nx;
19050         }
19051
19052         Ext.core.Element.data(dom, 'isCleaned', true);
19053         return me;
19054     },
19055
19056     /**
19057      * Direct access to the Ext.ElementLoader {@link Ext.ElementLoader#load} method. The method takes the same object
19058      * parameter as {@link Ext.ElementLoader#load}
19059      * @return {Ext.core.Element} this
19060      */
19061     load : function(options) {
19062         this.getLoader().load(options);
19063         return this;
19064     },
19065
19066     /**
19067     * Gets this element's {@link Ext.ElementLoader ElementLoader}
19068     * @return {Ext.ElementLoader} The loader
19069     */
19070     getLoader : function() {
19071         var dom = this.dom,
19072             data = Ext.core.Element.data,
19073             loader = data(dom, 'loader');
19074             
19075         if (!loader) {
19076             loader = Ext.create('Ext.ElementLoader', {
19077                 target: this
19078             });
19079             data(dom, 'loader', loader);
19080         }
19081         return loader;
19082     },
19083
19084     /**
19085     * Update the innerHTML of this element, optionally searching for and processing scripts
19086     * @param {String} html The new HTML
19087     * @param {Boolean} loadScripts (optional) True to look for and process scripts (defaults to false)
19088     * @param {Function} callback (optional) For async script loading you can be notified when the update completes
19089     * @return {Ext.core.Element} this
19090      */
19091     update : function(html, loadScripts, callback) {
19092         var me = this,
19093             id,
19094             dom,
19095             interval;
19096             
19097         if (!me.dom) {
19098             return me;
19099         }
19100         html = html || '';
19101         dom = me.dom;
19102
19103         if (loadScripts !== true) {
19104             dom.innerHTML = html;
19105             Ext.callback(callback, me);
19106             return me;
19107         }
19108
19109         id  = Ext.id();
19110         html += '<span id="' + id + '"></span>';
19111
19112         interval = setInterval(function(){
19113             if (!document.getElementById(id)) {
19114                 return false;    
19115             }
19116             clearInterval(interval);
19117             var DOC    = document,
19118                 hd     = DOC.getElementsByTagName("head")[0],
19119                 re     = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig,
19120                 srcRe  = /\ssrc=([\'\"])(.*?)\1/i,
19121                 typeRe = /\stype=([\'\"])(.*?)\1/i,
19122                 match,
19123                 attrs,
19124                 srcMatch,
19125                 typeMatch,
19126                 el,
19127                 s;
19128
19129             while ((match = re.exec(html))) {
19130                 attrs = match[1];
19131                 srcMatch = attrs ? attrs.match(srcRe) : false;
19132                 if (srcMatch && srcMatch[2]) {
19133                    s = DOC.createElement("script");
19134                    s.src = srcMatch[2];
19135                    typeMatch = attrs.match(typeRe);
19136                    if (typeMatch && typeMatch[2]) {
19137                        s.type = typeMatch[2];
19138                    }
19139                    hd.appendChild(s);
19140                 } else if (match[2] && match[2].length > 0) {
19141                     if (window.execScript) {
19142                        window.execScript(match[2]);
19143                     } else {
19144                        window.eval(match[2]);
19145                     }
19146                 }
19147             }
19148             
19149             el = DOC.getElementById(id);
19150             if (el) {
19151                 Ext.removeNode(el);
19152             }
19153             Ext.callback(callback, me);
19154         }, 20);
19155         dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, '');
19156         return me;
19157     },
19158
19159     // inherit docs, overridden so we can add removeAnchor
19160     removeAllListeners : function() {
19161         this.removeAnchor();
19162         Ext.EventManager.removeAll(this.dom);
19163         return this;
19164     },
19165
19166     /**
19167      * Creates a proxy element of this element
19168      * @param {String/Object} config The class name of the proxy element or a DomHelper config object
19169      * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
19170      * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
19171      * @return {Ext.core.Element} The new proxy element
19172      */
19173     createProxy : function(config, renderTo, matchBox) {
19174         config = (typeof config == 'object') ? config : {tag : "div", cls: config};
19175
19176         var me = this,
19177             proxy = renderTo ? Ext.core.DomHelper.append(renderTo, config, true) :
19178                                Ext.core.DomHelper.insertBefore(me.dom, config, true);
19179
19180         proxy.setVisibilityMode(Ext.core.Element.DISPLAY);
19181         proxy.hide();
19182         if (matchBox && me.setBox && me.getBox) { // check to make sure Element.position.js is loaded
19183            proxy.setBox(me.getBox());
19184         }
19185         return proxy;
19186     }
19187 });
19188 Ext.core.Element.prototype.clearListeners = Ext.core.Element.prototype.removeAllListeners;
19189
19190 /**
19191  * @class Ext.core.Element
19192  */
19193 Ext.core.Element.addMethods({
19194     /**
19195      * Gets the x,y coordinates specified by the anchor position on the element.
19196      * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo}
19197      * for details on supported anchor positions.
19198      * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead
19199      * of page coordinates
19200      * @param {Object} size (optional) An object containing the size to use for calculating anchor position
19201      * {width: (target width), height: (target height)} (defaults to the element's current size)
19202      * @return {Array} [x, y] An array containing the element's x and y coordinates
19203      */
19204     getAnchorXY : function(anchor, local, s){
19205         //Passing a different size is useful for pre-calculating anchors,
19206         //especially for anchored animations that change the el size.
19207         anchor = (anchor || "tl").toLowerCase();
19208         s = s || {};
19209
19210         var me = this,
19211             vp = me.dom == document.body || me.dom == document,
19212             w = s.width || vp ? Ext.core.Element.getViewWidth() : me.getWidth(),
19213             h = s.height || vp ? Ext.core.Element.getViewHeight() : me.getHeight(),
19214             xy,
19215             r = Math.round,
19216             o = me.getXY(),
19217             scroll = me.getScroll(),
19218             extraX = vp ? scroll.left : !local ? o[0] : 0,
19219             extraY = vp ? scroll.top : !local ? o[1] : 0,
19220             hash = {
19221                 c  : [r(w * 0.5), r(h * 0.5)],
19222                 t  : [r(w * 0.5), 0],
19223                 l  : [0, r(h * 0.5)],
19224                 r  : [w, r(h * 0.5)],
19225                 b  : [r(w * 0.5), h],
19226                 tl : [0, 0],
19227                 bl : [0, h],
19228                 br : [w, h],
19229                 tr : [w, 0]
19230             };
19231
19232         xy = hash[anchor];
19233         return [xy[0] + extraX, xy[1] + extraY];
19234     },
19235
19236     /**
19237      * Anchors an element to another element and realigns it when the window is resized.
19238      * @param {Mixed} element The element to align to.
19239      * @param {String} position The position to align to.
19240      * @param {Array} offsets (optional) Offset the positioning by [x, y]
19241      * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
19242      * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
19243      * is a number, it is used as the buffer delay (defaults to 50ms).
19244      * @param {Function} callback The function to call after the animation finishes
19245      * @return {Ext.core.Element} this
19246      */
19247     anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
19248         var me = this,
19249             dom = me.dom,
19250             scroll = !Ext.isEmpty(monitorScroll),
19251             action = function(){
19252                 Ext.fly(dom).alignTo(el, alignment, offsets, animate);
19253                 Ext.callback(callback, Ext.fly(dom));
19254             },
19255             anchor = this.getAnchor();
19256
19257         // previous listener anchor, remove it
19258         this.removeAnchor();
19259         Ext.apply(anchor, {
19260             fn: action,
19261             scroll: scroll
19262         });
19263
19264         Ext.EventManager.onWindowResize(action, null);
19265
19266         if(scroll){
19267             Ext.EventManager.on(window, 'scroll', action, null,
19268                 {buffer: !isNaN(monitorScroll) ? monitorScroll : 50});
19269         }
19270         action.call(me); // align immediately
19271         return me;
19272     },
19273
19274     /**
19275      * Remove any anchor to this element. See {@link #anchorTo}.
19276      * @return {Ext.core.Element} this
19277      */
19278     removeAnchor : function(){
19279         var me = this,
19280             anchor = this.getAnchor();
19281
19282         if(anchor && anchor.fn){
19283             Ext.EventManager.removeResizeListener(anchor.fn);
19284             if(anchor.scroll){
19285                 Ext.EventManager.un(window, 'scroll', anchor.fn);
19286             }
19287             delete anchor.fn;
19288         }
19289         return me;
19290     },
19291
19292     // private
19293     getAnchor : function(){
19294         var data = Ext.core.Element.data,
19295             dom = this.dom;
19296             if (!dom) {
19297                 return;
19298             }
19299             var anchor = data(dom, '_anchor');
19300
19301         if(!anchor){
19302             anchor = data(dom, '_anchor', {});
19303         }
19304         return anchor;
19305     },
19306
19307     getAlignVector: function(el, spec, offset) {
19308         var me = this,
19309             side = {t:"top", l:"left", r:"right", b: "bottom"},
19310             thisRegion = me.getRegion(),
19311             elRegion;
19312
19313         el = Ext.get(el);
19314         if(!el || !el.dom){
19315             Ext.Error.raise({
19316                 sourceClass: 'Ext.core.Element',
19317                 sourceMethod: 'getAlignVector',
19318                 msg: 'Attempted to align an element that doesn\'t exist'
19319             });
19320         }
19321
19322         elRegion = el.getRegion();
19323     },
19324
19325     /**
19326      * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
19327      * supported position values.
19328      * @param {Mixed} element The element to align to.
19329      * @param {String} position (optional, defaults to "tl-bl?") The position to align to.
19330      * @param {Array} offsets (optional) Offset the positioning by [x, y]
19331      * @return {Array} [x, y]
19332      */
19333     getAlignToXY : function(el, p, o){
19334         el = Ext.get(el);
19335
19336         if(!el || !el.dom){
19337             Ext.Error.raise({
19338                 sourceClass: 'Ext.core.Element',
19339                 sourceMethod: 'getAlignToXY',
19340                 msg: 'Attempted to align an element that doesn\'t exist'
19341             });
19342         }
19343
19344         o = o || [0,0];
19345         p = (!p || p == "?" ? "tl-bl?" : (!(/-/).test(p) && p !== "" ? "tl-" + p : p || "tl-bl")).toLowerCase();
19346
19347         var me = this,
19348             d = me.dom,
19349             a1,
19350             a2,
19351             x,
19352             y,
19353             //constrain the aligned el to viewport if necessary
19354             w,
19355             h,
19356             r,
19357             dw = Ext.core.Element.getViewWidth() -10, // 10px of margin for ie
19358             dh = Ext.core.Element.getViewHeight()-10, // 10px of margin for ie
19359             p1y,
19360             p1x,
19361             p2y,
19362             p2x,
19363             swapY,
19364             swapX,
19365             doc = document,
19366             docElement = doc.documentElement,
19367             docBody = doc.body,
19368             scrollX = (docElement.scrollLeft || docBody.scrollLeft || 0)+5,
19369             scrollY = (docElement.scrollTop || docBody.scrollTop || 0)+5,
19370             c = false, //constrain to viewport
19371             p1 = "",
19372             p2 = "",
19373             m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
19374
19375         if(!m){
19376             Ext.Error.raise({
19377                 sourceClass: 'Ext.core.Element',
19378                 sourceMethod: 'getAlignToXY',
19379                 el: el,
19380                 position: p,
19381                 offset: o,
19382                 msg: 'Attemmpted to align an element with an invalid position: "' + p + '"'
19383             });
19384         }
19385
19386         p1 = m[1];
19387         p2 = m[2];
19388         c = !!m[3];
19389
19390         //Subtract the aligned el's internal xy from the target's offset xy
19391         //plus custom offset to get the aligned el's new offset xy
19392         a1 = me.getAnchorXY(p1, true);
19393         a2 = el.getAnchorXY(p2, false);
19394
19395         x = a2[0] - a1[0] + o[0];
19396         y = a2[1] - a1[1] + o[1];
19397
19398         if(c){
19399            w = me.getWidth();
19400            h = me.getHeight();
19401            r = el.getRegion();
19402            //If we are at a viewport boundary and the aligned el is anchored on a target border that is
19403            //perpendicular to the vp border, allow the aligned el to slide on that border,
19404            //otherwise swap the aligned el to the opposite border of the target.
19405            p1y = p1.charAt(0);
19406            p1x = p1.charAt(p1.length-1);
19407            p2y = p2.charAt(0);
19408            p2x = p2.charAt(p2.length-1);
19409            swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
19410            swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
19411
19412
19413            if (x + w > dw + scrollX) {
19414                 x = swapX ? r.left-w : dw+scrollX-w;
19415            }
19416            if (x < scrollX) {
19417                x = swapX ? r.right : scrollX;
19418            }
19419            if (y + h > dh + scrollY) {
19420                 y = swapY ? r.top-h : dh+scrollY-h;
19421             }
19422            if (y < scrollY){
19423                y = swapY ? r.bottom : scrollY;
19424            }
19425         }
19426         return [x,y];
19427     },
19428
19429     /**
19430      * Aligns this element with another element relative to the specified anchor points. If the other element is the
19431      * document it aligns it to the viewport.
19432      * The position parameter is optional, and can be specified in any one of the following formats:
19433      * <ul>
19434      *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
19435      *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
19436      *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
19437      *       deprecated in favor of the newer two anchor syntax below</i>.</li>
19438      *   <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
19439      *       element's anchor point, and the second value is used as the target's anchor point.</li>
19440      * </ul>
19441      * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
19442      * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
19443      * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
19444      * that specified in order to enforce the viewport constraints.
19445      * Following are all of the supported anchor positions:
19446 <pre>
19447 Value  Description
19448 -----  -----------------------------
19449 tl     The top left corner (default)
19450 t      The center of the top edge
19451 tr     The top right corner
19452 l      The center of the left edge
19453 c      In the center of the element
19454 r      The center of the right edge
19455 bl     The bottom left corner
19456 b      The center of the bottom edge
19457 br     The bottom right corner
19458 </pre>
19459 Example Usage:
19460 <pre><code>
19461 // align el to other-el using the default positioning ("tl-bl", non-constrained)
19462 el.alignTo("other-el");
19463
19464 // align the top left corner of el with the top right corner of other-el (constrained to viewport)
19465 el.alignTo("other-el", "tr?");
19466
19467 // align the bottom right corner of el with the center left edge of other-el
19468 el.alignTo("other-el", "br-l?");
19469
19470 // align the center of el with the bottom left corner of other-el and
19471 // adjust the x position by -6 pixels (and the y position by 0)
19472 el.alignTo("other-el", "c-bl", [-6, 0]);
19473 </code></pre>
19474      * @param {Mixed} element The element to align to.
19475      * @param {String} position (optional, defaults to "tl-bl?") The position to align to.
19476      * @param {Array} offsets (optional) Offset the positioning by [x, y]
19477      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
19478      * @return {Ext.core.Element} this
19479      */
19480     alignTo : function(element, position, offsets, animate){
19481         var me = this;
19482         return me.setXY(me.getAlignToXY(element, position, offsets),
19483                         me.anim && !!animate ? me.anim(animate) : false);
19484     },
19485
19486     // private ==>  used outside of core
19487     adjustForConstraints : function(xy, parent) {
19488         var vector = this.getConstrainVector(parent, xy);
19489         if (vector) {
19490             xy[0] += vector[0];
19491             xy[1] += vector[1];
19492         }
19493         return xy;
19494     },
19495
19496     /**
19497      * <p>Returns the <code>[X, Y]</code> vector by which this element must be translated to make a best attempt
19498      * to constrain within the passed constraint. Returns <code>false</code> is this element does not need to be moved.</p>
19499      * <p>Priority is given to constraining the top and left within the constraint.</p>
19500      * <p>The constraint may either be an existing element into which this element is to be constrained, or
19501      * an {@link Ext.util.Region Region} into which this element is to be constrained.</p>
19502      * @param constrainTo {Mixed} The Element or {@link Ext.util.Region Region} into which this element is to be constrained.
19503      * @param proposedPosition {Array} A proposed <code>[X, Y]</code> position to test for validity and to produce a vector for instead
19504      * of using this Element's current position;
19505      * @returns {Array} <b>If</b> this element <i>needs</i> to be translated, an <code>[X, Y]</code>
19506      * vector by which this element must be translated. Otherwise, <code>false</code>.
19507      */
19508     getConstrainVector: function(constrainTo, proposedPosition) {
19509         if (!(constrainTo instanceof Ext.util.Region)) {
19510             constrainTo = Ext.get(constrainTo).getViewRegion();
19511         }
19512         var thisRegion = this.getRegion(),
19513             vector = [0, 0],
19514             shadowSize = this.shadow && this.shadow.offset,
19515             overflowed = false;
19516
19517         // Shift this region to occupy the proposed position
19518         if (proposedPosition) {
19519             thisRegion.translateBy(proposedPosition[0] - thisRegion.x, proposedPosition[1] - thisRegion.y);
19520         }
19521
19522         // Reduce the constrain region to allow for shadow
19523         // TODO: Rewrite the Shadow class. When that's done, get the extra for each side from the Shadow.
19524         if (shadowSize) {
19525             constrainTo.adjust(0, -shadowSize, -shadowSize, shadowSize);
19526         }
19527
19528         // Constrain the X coordinate by however much this Element overflows
19529         if (thisRegion.right > constrainTo.right) {
19530             overflowed = true;
19531             vector[0] = (constrainTo.right - thisRegion.right);    // overflowed the right
19532         }
19533         if (thisRegion.left + vector[0] < constrainTo.left) {
19534             overflowed = true;
19535             vector[0] = (constrainTo.left - thisRegion.left);      // overflowed the left
19536         }
19537
19538         // Constrain the Y coordinate by however much this Element overflows
19539         if (thisRegion.bottom > constrainTo.bottom) {
19540             overflowed = true;
19541             vector[1] = (constrainTo.bottom - thisRegion.bottom);  // overflowed the bottom
19542         }
19543         if (thisRegion.top + vector[1] < constrainTo.top) {
19544             overflowed = true;
19545             vector[1] = (constrainTo.top - thisRegion.top);        // overflowed the top
19546         }
19547         return overflowed ? vector : false;
19548     },
19549
19550     /**
19551     * Calculates the x, y to center this element on the screen
19552     * @return {Array} The x, y values [x, y]
19553     */
19554     getCenterXY : function(){
19555         return this.getAlignToXY(document, 'c-c');
19556     },
19557
19558     /**
19559     * Centers the Element in either the viewport, or another Element.
19560     * @param {Mixed} centerIn (optional) The element in which to center the element.
19561     */
19562     center : function(centerIn){
19563         return this.alignTo(centerIn || document, 'c-c');
19564     }
19565 });
19566
19567 /**
19568  * @class Ext.core.Element
19569  */
19570 (function(){
19571
19572 var ELEMENT = Ext.core.Element,
19573     LEFT = "left",
19574     RIGHT = "right",
19575     TOP = "top",
19576     BOTTOM = "bottom",
19577     POSITION = "position",
19578     STATIC = "static",
19579     RELATIVE = "relative",
19580     AUTO = "auto",
19581     ZINDEX = "z-index";
19582
19583 Ext.override(Ext.core.Element, {
19584     /**
19585       * 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).
19586       * @return {Number} The X position of the element
19587       */
19588     getX : function(){
19589         return ELEMENT.getX(this.dom);
19590     },
19591
19592     /**
19593       * 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).
19594       * @return {Number} The Y position of the element
19595       */
19596     getY : function(){
19597         return ELEMENT.getY(this.dom);
19598     },
19599
19600     /**
19601       * 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).
19602       * @return {Array} The XY position of the element
19603       */
19604     getXY : function(){
19605         return ELEMENT.getXY(this.dom);
19606     },
19607
19608     /**
19609       * 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.
19610       * @param {Mixed} element The element to get the offsets from.
19611       * @return {Array} The XY page offsets (e.g. [100, -200])
19612       */
19613     getOffsetsTo : function(el){
19614         var o = this.getXY(),
19615             e = Ext.fly(el, '_internal').getXY();
19616         return [o[0]-e[0],o[1]-e[1]];
19617     },
19618
19619     /**
19620      * 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).
19621      * @param {Number} The X position of the element
19622      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
19623      * @return {Ext.core.Element} this
19624      */
19625     setX : function(x, animate){
19626         return this.setXY([x, this.getY()], animate);
19627     },
19628
19629     /**
19630      * 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).
19631      * @param {Number} The Y position of the element
19632      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
19633      * @return {Ext.core.Element} this
19634      */
19635     setY : function(y, animate){
19636         return this.setXY([this.getX(), y], animate);
19637     },
19638
19639     /**
19640      * Sets the element's left position directly using CSS style (instead of {@link #setX}).
19641      * @param {String} left The left CSS property value
19642      * @return {Ext.core.Element} this
19643      */
19644     setLeft : function(left){
19645         this.setStyle(LEFT, this.addUnits(left));
19646         return this;
19647     },
19648
19649     /**
19650      * Sets the element's top position directly using CSS style (instead of {@link #setY}).
19651      * @param {String} top The top CSS property value
19652      * @return {Ext.core.Element} this
19653      */
19654     setTop : function(top){
19655         this.setStyle(TOP, this.addUnits(top));
19656         return this;
19657     },
19658
19659     /**
19660      * Sets the element's CSS right style.
19661      * @param {String} right The right CSS property value
19662      * @return {Ext.core.Element} this
19663      */
19664     setRight : function(right){
19665         this.setStyle(RIGHT, this.addUnits(right));
19666         return this;
19667     },
19668
19669     /**
19670      * Sets the element's CSS bottom style.
19671      * @param {String} bottom The bottom CSS property value
19672      * @return {Ext.core.Element} this
19673      */
19674     setBottom : function(bottom){
19675         this.setStyle(BOTTOM, this.addUnits(bottom));
19676         return this;
19677     },
19678
19679     /**
19680      * Sets the position of the element in page coordinates, regardless of how the element is positioned.
19681      * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
19682      * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
19683      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
19684      * @return {Ext.core.Element} this
19685      */
19686     setXY: function(pos, animate) {
19687         var me = this;
19688         if (!animate || !me.anim) {
19689             ELEMENT.setXY(me.dom, pos);
19690         }
19691         else {
19692             if (!Ext.isObject(animate)) {
19693                 animate = {};
19694             }
19695             me.animate(Ext.applyIf({ to: { x: pos[0], y: pos[1] } }, animate));
19696         }
19697         return me;
19698     },
19699
19700     /**
19701      * Sets the position of the element in page coordinates, regardless of how the element is positioned.
19702      * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
19703      * @param {Number} x X value for new position (coordinates are page-based)
19704      * @param {Number} y Y value for new position (coordinates are page-based)
19705      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
19706      * @return {Ext.core.Element} this
19707      */
19708     setLocation : function(x, y, animate){
19709         return this.setXY([x, y], animate);
19710     },
19711
19712     /**
19713      * Sets the position of the element in page coordinates, regardless of how the element is positioned.
19714      * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
19715      * @param {Number} x X value for new position (coordinates are page-based)
19716      * @param {Number} y Y value for new position (coordinates are page-based)
19717      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
19718      * @return {Ext.core.Element} this
19719      */
19720     moveTo : function(x, y, animate){
19721         return this.setXY([x, y], animate);
19722     },
19723
19724     /**
19725      * Gets the left X coordinate
19726      * @param {Boolean} local True to get the local css position instead of page coordinate
19727      * @return {Number}
19728      */
19729     getLeft : function(local){
19730         return !local ? this.getX() : parseInt(this.getStyle(LEFT), 10) || 0;
19731     },
19732
19733     /**
19734      * Gets the right X coordinate of the element (element X position + element width)
19735      * @param {Boolean} local True to get the local css position instead of page coordinate
19736      * @return {Number}
19737      */
19738     getRight : function(local){
19739         var me = this;
19740         return !local ? me.getX() + me.getWidth() : (me.getLeft(true) + me.getWidth()) || 0;
19741     },
19742
19743     /**
19744      * Gets the top Y coordinate
19745      * @param {Boolean} local True to get the local css position instead of page coordinate
19746      * @return {Number}
19747      */
19748     getTop : function(local) {
19749         return !local ? this.getY() : parseInt(this.getStyle(TOP), 10) || 0;
19750     },
19751
19752     /**
19753      * Gets the bottom Y coordinate of the element (element Y position + element height)
19754      * @param {Boolean} local True to get the local css position instead of page coordinate
19755      * @return {Number}
19756      */
19757     getBottom : function(local){
19758         var me = this;
19759         return !local ? me.getY() + me.getHeight() : (me.getTop(true) + me.getHeight()) || 0;
19760     },
19761
19762     /**
19763     * Initializes positioning on this element. If a desired position is not passed, it will make the
19764     * the element positioned relative IF it is not already positioned.
19765     * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
19766     * @param {Number} zIndex (optional) The zIndex to apply
19767     * @param {Number} x (optional) Set the page X position
19768     * @param {Number} y (optional) Set the page Y position
19769     */
19770     position : function(pos, zIndex, x, y) {
19771         var me = this;
19772
19773         if (!pos && me.isStyle(POSITION, STATIC)){
19774             me.setStyle(POSITION, RELATIVE);
19775         } else if(pos) {
19776             me.setStyle(POSITION, pos);
19777         }
19778         if (zIndex){
19779             me.setStyle(ZINDEX, zIndex);
19780         }
19781         if (x || y) {
19782             me.setXY([x || false, y || false]);
19783         }
19784     },
19785
19786     /**
19787     * Clear positioning back to the default when the document was loaded
19788     * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
19789     * @return {Ext.core.Element} this
19790      */
19791     clearPositioning : function(value){
19792         value = value || '';
19793         this.setStyle({
19794             left : value,
19795             right : value,
19796             top : value,
19797             bottom : value,
19798             "z-index" : "",
19799             position : STATIC
19800         });
19801         return this;
19802     },
19803
19804     /**
19805     * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
19806     * snapshot before performing an update and then restoring the element.
19807     * @return {Object}
19808     */
19809     getPositioning : function(){
19810         var l = this.getStyle(LEFT);
19811         var t = this.getStyle(TOP);
19812         return {
19813             "position" : this.getStyle(POSITION),
19814             "left" : l,
19815             "right" : l ? "" : this.getStyle(RIGHT),
19816             "top" : t,
19817             "bottom" : t ? "" : this.getStyle(BOTTOM),
19818             "z-index" : this.getStyle(ZINDEX)
19819         };
19820     },
19821
19822     /**
19823     * Set positioning with an object returned by getPositioning().
19824     * @param {Object} posCfg
19825     * @return {Ext.core.Element} this
19826      */
19827     setPositioning : function(pc){
19828         var me = this,
19829             style = me.dom.style;
19830
19831         me.setStyle(pc);
19832
19833         if(pc.right == AUTO){
19834             style.right = "";
19835         }
19836         if(pc.bottom == AUTO){
19837             style.bottom = "";
19838         }
19839
19840         return me;
19841     },
19842
19843     /**
19844      * Translates the passed page coordinates into left/top css values for this element
19845      * @param {Number/Array} x The page x or an array containing [x, y]
19846      * @param {Number} y (optional) The page y, required if x is not an array
19847      * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
19848      */
19849     translatePoints: function(x, y) {
19850         if (Ext.isArray(x)) {
19851              y = x[1];
19852              x = x[0];
19853         }
19854         var me = this,
19855             relative = me.isStyle(POSITION, RELATIVE),
19856             o = me.getXY(),
19857             left = parseInt(me.getStyle(LEFT), 10),
19858             top = parseInt(me.getStyle(TOP), 10);
19859
19860         if (!Ext.isNumber(left)) {
19861             left = relative ? 0 : me.dom.offsetLeft;
19862         }
19863         if (!Ext.isNumber(top)) {
19864             top = relative ? 0 : me.dom.offsetTop;
19865         }
19866         left = (Ext.isNumber(x)) ? x - o[0] + left : undefined;
19867         top = (Ext.isNumber(y)) ? y - o[1] + top : undefined;
19868         return {
19869             left: left,
19870             top: top
19871         };
19872     },
19873
19874     /**
19875      * 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.
19876      * @param {Object} box The box to fill {x, y, width, height}
19877      * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
19878      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
19879      * @return {Ext.core.Element} this
19880      */
19881     setBox: function(box, adjust, animate) {
19882         var me = this,
19883             w = box.width,
19884             h = box.height;
19885         if ((adjust && !me.autoBoxAdjust) && !me.isBorderBox()) {
19886             w -= (me.getBorderWidth("lr") + me.getPadding("lr"));
19887             h -= (me.getBorderWidth("tb") + me.getPadding("tb"));
19888         }
19889         me.setBounds(box.x, box.y, w, h, animate);
19890         return me;
19891     },
19892
19893     /**
19894      * Return an object defining the area of this Element which can be passed to {@link #setBox} to
19895      * set another Element's size/location to match this element.
19896      * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
19897      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
19898      * @return {Object} box An object in the format<pre><code>
19899 {
19900     x: &lt;Element's X position>,
19901     y: &lt;Element's Y position>,
19902     width: &lt;Element's width>,
19903     height: &lt;Element's height>,
19904     bottom: &lt;Element's lower bound>,
19905     right: &lt;Element's rightmost bound>
19906 }
19907 </code></pre>
19908      * The returned object may also be addressed as an Array where index 0 contains the X position
19909      * and index 1 contains the Y position. So the result may also be used for {@link #setXY}
19910      */
19911     getBox: function(contentBox, local) {
19912         var me = this,
19913             xy,
19914             left,
19915             top,
19916             getBorderWidth = me.getBorderWidth,
19917             getPadding = me.getPadding,
19918             l, r, t, b, w, h, bx;
19919         if (!local) {
19920             xy = me.getXY();
19921         } else {
19922             left = parseInt(me.getStyle("left"), 10) || 0;
19923             top = parseInt(me.getStyle("top"), 10) || 0;
19924             xy = [left, top];
19925         }
19926         w = me.getWidth();
19927         h = me.getHeight();
19928         if (!contentBox) {
19929             bx = {
19930                 x: xy[0],
19931                 y: xy[1],
19932                 0: xy[0],
19933                 1: xy[1],
19934                 width: w,
19935                 height: h
19936             };
19937         } else {
19938             l = getBorderWidth.call(me, "l") + getPadding.call(me, "l");
19939             r = getBorderWidth.call(me, "r") + getPadding.call(me, "r");
19940             t = getBorderWidth.call(me, "t") + getPadding.call(me, "t");
19941             b = getBorderWidth.call(me, "b") + getPadding.call(me, "b");
19942             bx = {
19943                 x: xy[0] + l,
19944                 y: xy[1] + t,
19945                 0: xy[0] + l,
19946                 1: xy[1] + t,
19947                 width: w - (l + r),
19948                 height: h - (t + b)
19949             };
19950         }
19951         bx.right = bx.x + bx.width;
19952         bx.bottom = bx.y + bx.height;
19953         return bx;
19954     },
19955
19956     /**
19957      * Move this element relative to its current position.
19958      * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
19959      * @param {Number} distance How far to move the element in pixels
19960      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
19961      * @return {Ext.core.Element} this
19962      */
19963     move: function(direction, distance, animate) {
19964         var me = this,
19965             xy = me.getXY(),
19966             x = xy[0],
19967             y = xy[1],
19968             left = [x - distance, y],
19969             right = [x + distance, y],
19970             top = [x, y - distance],
19971             bottom = [x, y + distance],
19972             hash = {
19973                 l: left,
19974                 left: left,
19975                 r: right,
19976                 right: right,
19977                 t: top,
19978                 top: top,
19979                 up: top,
19980                 b: bottom,
19981                 bottom: bottom,
19982                 down: bottom
19983             };
19984
19985         direction = direction.toLowerCase();
19986         me.moveTo(hash[direction][0], hash[direction][1], animate);
19987     },
19988
19989     /**
19990      * Quick set left and top adding default units
19991      * @param {String} left The left CSS property value
19992      * @param {String} top The top CSS property value
19993      * @return {Ext.core.Element} this
19994      */
19995     setLeftTop: function(left, top) {
19996         var me = this,
19997             style = me.dom.style;
19998         style.left = me.addUnits(left);
19999         style.top = me.addUnits(top);
20000         return me;
20001     },
20002
20003     /**
20004      * Returns the region of this element.
20005      * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
20006      * @return {Region} A Ext.util.Region containing "top, left, bottom, right" member data.
20007      */
20008     getRegion: function() {
20009         return this.getPageBox(true);
20010     },
20011
20012     /**
20013      * Returns the <b>content</b> region of this element. That is the region within the borders and padding.
20014      * @return {Region} A Ext.util.Region containing "top, left, bottom, right" member data.
20015      */
20016     getViewRegion: function() {
20017         var me = this,
20018             isBody = me.dom === document.body,
20019             scroll, pos, top, left, width, height;
20020             
20021         // For the body we want to do some special logic
20022         if (isBody) {
20023             scroll = me.getScroll();
20024             left = scroll.left;
20025             top = scroll.top;
20026             width = Ext.core.Element.getViewportWidth();
20027             height = Ext.core.Element.getViewportHeight();
20028         }
20029         else {
20030             pos = me.getXY();
20031             left = pos[0] + me.getBorderWidth('l') + me.getPadding('l');
20032             top = pos[1] + me.getBorderWidth('t') + me.getPadding('t');
20033             width = me.getWidth(true);
20034             height = me.getHeight(true);
20035         }
20036
20037         return Ext.create('Ext.util.Region', top, left + width, top + height, left);
20038     },
20039
20040     /**
20041      * Return an object defining the area of this Element which can be passed to {@link #setBox} to
20042      * set another Element's size/location to match this element.
20043      * @param {Boolean} asRegion(optional) If true an Ext.util.Region will be returned
20044      * @return {Object} box An object in the format<pre><code>
20045 {
20046     x: &lt;Element's X position>,
20047     y: &lt;Element's Y position>,
20048     width: &lt;Element's width>,
20049     height: &lt;Element's height>,
20050     bottom: &lt;Element's lower bound>,
20051     right: &lt;Element's rightmost bound>
20052 }
20053 </code></pre>
20054      * The returned object may also be addressed as an Array where index 0 contains the X position
20055      * and index 1 contains the Y position. So the result may also be used for {@link #setXY}
20056      */
20057     getPageBox : function(getRegion) {
20058         var me = this,
20059             el = me.dom,
20060             isDoc = el === document.body,
20061             w = isDoc ? Ext.core.Element.getViewWidth()  : el.offsetWidth,
20062             h = isDoc ? Ext.core.Element.getViewHeight() : el.offsetHeight,
20063             xy = me.getXY(),
20064             t = xy[1],
20065             r = xy[0] + w,
20066             b = xy[1] + h,
20067             l = xy[0];
20068
20069         if (getRegion) {
20070             return Ext.create('Ext.util.Region', t, r, b, l);
20071         }
20072         else {
20073             return {
20074                 left: l,
20075                 top: t,
20076                 width: w,
20077                 height: h,
20078                 right: r,
20079                 bottom: b
20080             };
20081         }
20082     },
20083
20084     /**
20085      * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
20086      * @param {Number} x X value for new position (coordinates are page-based)
20087      * @param {Number} y Y value for new position (coordinates are page-based)
20088      * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
20089      * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels)</li>
20090      * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
20091      * </ul></div>
20092      * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
20093      * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels)</li>
20094      * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
20095      * </ul></div>
20096      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
20097      * @return {Ext.core.Element} this
20098      */
20099     setBounds: function(x, y, width, height, animate) {
20100         var me = this;
20101         if (!animate || !me.anim) {
20102             me.setSize(width, height);
20103             me.setLocation(x, y);
20104         } else {
20105             if (!Ext.isObject(animate)) {
20106                 animate = {};
20107             }
20108             me.animate(Ext.applyIf({
20109                 to: {
20110                     x: x,
20111                     y: y,
20112                     width: me.adjustWidth(width),
20113                     height: me.adjustHeight(height)
20114                 }
20115             }, animate));
20116         }
20117         return me;
20118     },
20119
20120     /**
20121      * Sets the element's position and size the specified region. If animation is true then width, height, x and y will be animated concurrently.
20122      * @param {Ext.util.Region} region The region to fill
20123      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
20124      * @return {Ext.core.Element} this
20125      */
20126     setRegion: function(region, animate) {
20127         return this.setBounds(region.left, region.top, region.right - region.left, region.bottom - region.top, animate);
20128     }
20129 });
20130 })();
20131
20132 /**
20133  * @class Ext.core.Element
20134  */
20135 Ext.override(Ext.core.Element, {
20136     /**
20137      * Returns true if this element is scrollable.
20138      * @return {Boolean}
20139      */
20140     isScrollable : function(){
20141         var dom = this.dom;
20142         return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
20143     },
20144
20145     /**
20146      * Returns the current scroll position of the element.
20147      * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
20148      */
20149     getScroll : function() {
20150         var d = this.dom, 
20151             doc = document,
20152             body = doc.body,
20153             docElement = doc.documentElement,
20154             l,
20155             t,
20156             ret;
20157
20158         if (d == doc || d == body) {
20159             if (Ext.isIE && Ext.isStrict) {
20160                 l = docElement.scrollLeft; 
20161                 t = docElement.scrollTop;
20162             } else {
20163                 l = window.pageXOffset;
20164                 t = window.pageYOffset;
20165             }
20166             ret = {
20167                 left: l || (body ? body.scrollLeft : 0), 
20168                 top : t || (body ? body.scrollTop : 0)
20169             };
20170         } else {
20171             ret = {
20172                 left: d.scrollLeft, 
20173                 top : d.scrollTop
20174             };
20175         }
20176         
20177         return ret;
20178     },
20179     
20180     /**
20181      * 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().
20182      * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
20183      * @param {Number} value The new scroll value
20184      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
20185      * @return {Element} this
20186      */
20187     scrollTo : function(side, value, animate) {
20188         //check if we're scrolling top or left
20189         var top = /top/i.test(side),
20190             me = this,
20191             dom = me.dom,
20192             obj = {},
20193             prop;
20194         if (!animate || !me.anim) {
20195             // just setting the value, so grab the direction
20196             prop = 'scroll' + (top ? 'Top' : 'Left');
20197             dom[prop] = value;
20198         }
20199         else {
20200             if (!Ext.isObject(animate)) {
20201                 animate = {};
20202             }
20203             obj['scroll' + (top ? 'Top' : 'Left')] = value;
20204             me.animate(Ext.applyIf({
20205                 to: obj
20206             }, animate));
20207         }
20208         return me;
20209     },
20210
20211     /**
20212      * Scrolls this element into view within the passed container.
20213      * @param {Mixed} container (optional) The container element to scroll (defaults to document.body).  Should be a
20214      * string (id), dom node, or Ext.core.Element.
20215      * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
20216      * @return {Ext.core.Element} this
20217      */
20218     scrollIntoView : function(container, hscroll) {
20219         container = Ext.getDom(container) || Ext.getBody().dom;
20220         var el = this.dom,
20221             offsets = this.getOffsetsTo(container),
20222             // el's box
20223             left = offsets[0] + container.scrollLeft,
20224             top = offsets[1] + container.scrollTop,
20225             bottom = top + el.offsetHeight,
20226             right = left + el.offsetWidth,
20227             // ct's box
20228             ctClientHeight = container.clientHeight,
20229             ctScrollTop = parseInt(container.scrollTop, 10),
20230             ctScrollLeft = parseInt(container.scrollLeft, 10),
20231             ctBottom = ctScrollTop + ctClientHeight,
20232             ctRight = ctScrollLeft + container.clientWidth;
20233
20234         if (el.offsetHeight > ctClientHeight || top < ctScrollTop) {
20235             container.scrollTop = top;
20236         } else if (bottom > ctBottom) {
20237             container.scrollTop = bottom - ctClientHeight;
20238         }
20239         // corrects IE, other browsers will ignore
20240         container.scrollTop = container.scrollTop;
20241
20242         if (hscroll !== false) {
20243             if (el.offsetWidth > container.clientWidth || left < ctScrollLeft) {
20244                 container.scrollLeft = left;
20245             }
20246             else if (right > ctRight) {
20247                 container.scrollLeft = right - container.clientWidth;
20248             }
20249             container.scrollLeft = container.scrollLeft;
20250         }
20251         return this;
20252     },
20253
20254     // private
20255     scrollChildIntoView : function(child, hscroll) {
20256         Ext.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
20257     },
20258
20259     /**
20260      * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
20261      * within this element's scrollable range.
20262      * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
20263      * @param {Number} distance How far to scroll the element in pixels
20264      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
20265      * @return {Boolean} Returns true if a scroll was triggered or false if the element
20266      * was scrolled as far as it could go.
20267      */
20268      scroll : function(direction, distance, animate) {
20269         if (!this.isScrollable()) {
20270             return false;
20271         }
20272         var el = this.dom,
20273             l = el.scrollLeft, t = el.scrollTop,
20274             w = el.scrollWidth, h = el.scrollHeight,
20275             cw = el.clientWidth, ch = el.clientHeight,
20276             scrolled = false, v,
20277             hash = {
20278                 l: Math.min(l + distance, w-cw),
20279                 r: v = Math.max(l - distance, 0),
20280                 t: Math.max(t - distance, 0),
20281                 b: Math.min(t + distance, h-ch)
20282             };
20283             hash.d = hash.b;
20284             hash.u = hash.t;
20285
20286         direction = direction.substr(0, 1);
20287         if ((v = hash[direction]) > -1) {
20288             scrolled = true;
20289             this.scrollTo(direction == 'l' || direction == 'r' ? 'left' : 'top', v, this.anim(animate));
20290         }
20291         return scrolled;
20292     }
20293 });
20294 /**
20295  * @class Ext.core.Element
20296  */
20297 Ext.core.Element.addMethods(
20298     function() {
20299         var VISIBILITY      = "visibility",
20300             DISPLAY         = "display",
20301             HIDDEN          = "hidden",
20302             NONE            = "none",
20303             XMASKED         = Ext.baseCSSPrefix + "masked",
20304             XMASKEDRELATIVE = Ext.baseCSSPrefix + "masked-relative",
20305             data            = Ext.core.Element.data;
20306
20307         return {
20308             /**
20309              * Checks whether the element is currently visible using both visibility and display properties.
20310              * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
20311              * @return {Boolean} True if the element is currently visible, else false
20312              */
20313             isVisible : function(deep) {
20314                 var vis = !this.isStyle(VISIBILITY, HIDDEN) && !this.isStyle(DISPLAY, NONE),
20315                     p   = this.dom.parentNode;
20316
20317                 if (deep !== true || !vis) {
20318                     return vis;
20319                 }
20320
20321                 while (p && !(/^body/i.test(p.tagName))) {
20322                     if (!Ext.fly(p, '_isVisible').isVisible()) {
20323                         return false;
20324                     }
20325                     p = p.parentNode;
20326                 }
20327                 return true;
20328             },
20329
20330             /**
20331              * Returns true if display is not "none"
20332              * @return {Boolean}
20333              */
20334             isDisplayed : function() {
20335                 return !this.isStyle(DISPLAY, NONE);
20336             },
20337
20338             /**
20339              * Convenience method for setVisibilityMode(Element.DISPLAY)
20340              * @param {String} display (optional) What to set display to when visible
20341              * @return {Ext.core.Element} this
20342              */
20343             enableDisplayMode : function(display) {
20344                 this.setVisibilityMode(Ext.core.Element.DISPLAY);
20345
20346                 if (!Ext.isEmpty(display)) {
20347                     data(this.dom, 'originalDisplay', display);
20348                 }
20349
20350                 return this;
20351             },
20352
20353             /**
20354              * Puts a mask over this element to disable user interaction. Requires core.css.
20355              * This method can only be applied to elements which accept child nodes.
20356              * @param {String} msg (optional) A message to display in the mask
20357              * @param {String} msgCls (optional) A css class to apply to the msg element
20358              * @return {Element} The mask element
20359              */
20360             mask : function(msg, msgCls) {
20361                 var me  = this,
20362                     dom = me.dom,
20363                     setExpression = dom.style.setExpression,
20364                     dh  = Ext.core.DomHelper,
20365                     EXTELMASKMSG = Ext.baseCSSPrefix + "mask-msg",
20366                     el,
20367                     mask;
20368
20369                 if (!(/^body/i.test(dom.tagName) && me.getStyle('position') == 'static')) {
20370                     me.addCls(XMASKEDRELATIVE);
20371                 }
20372                 el = data(dom, 'maskMsg');
20373                 if (el) {
20374                     el.remove();
20375                 }
20376                 el = data(dom, 'mask');
20377                 if (el) {
20378                     el.remove();
20379                 }
20380
20381                 mask = dh.append(dom, {cls : Ext.baseCSSPrefix + "mask"}, true);
20382                 data(dom, 'mask', mask);
20383
20384                 me.addCls(XMASKED);
20385                 mask.setDisplayed(true);
20386
20387                 if (typeof msg == 'string') {
20388                     var mm = dh.append(dom, {cls : EXTELMASKMSG, cn:{tag:'div'}}, true);
20389                     data(dom, 'maskMsg', mm);
20390                     mm.dom.className = msgCls ? EXTELMASKMSG + " " + msgCls : EXTELMASKMSG;
20391                     mm.dom.firstChild.innerHTML = msg;
20392                     mm.setDisplayed(true);
20393                     mm.center(me);
20394                 }
20395                 // NOTE: CSS expressions are resource intensive and to be used only as a last resort
20396                 // These expressions are removed as soon as they are no longer necessary - in the unmask method.
20397                 // In normal use cases an element will be masked for a limited period of time.
20398                 // Fix for https://sencha.jira.com/browse/EXTJSIV-19.
20399                 // IE6 strict mode and IE6-9 quirks mode takes off left+right padding when calculating width!
20400                 if (!Ext.supports.IncludePaddingInWidthCalculation && setExpression) {
20401                     mask.dom.style.setExpression('width', 'this.parentNode.offsetWidth + "px"');
20402                 }
20403
20404                 // Some versions and modes of IE subtract top+bottom padding when calculating height.
20405                 // Different versions from those which make the same error for width!
20406                 if (!Ext.supports.IncludePaddingInHeightCalculation && setExpression) {
20407                     mask.dom.style.setExpression('height', 'this.parentNode.offsetHeight + "px"');
20408                 }
20409                 // ie will not expand full height automatically
20410                 else if (Ext.isIE && !(Ext.isIE7 && Ext.isStrict) && me.getStyle('height') == 'auto') {
20411                     mask.setSize(undefined, me.getHeight());
20412                 }
20413                 return mask;
20414             },
20415
20416             /**
20417              * Removes a previously applied mask.
20418              */
20419             unmask : function() {
20420                 var me      = this,
20421                     dom     = me.dom,
20422                     mask    = data(dom, 'mask'),
20423                     maskMsg = data(dom, 'maskMsg');
20424
20425                 if (mask) {
20426                     // Remove resource-intensive CSS expressions as soon as they are not required.
20427                     if (mask.dom.style.clearExpression) {
20428                         mask.dom.style.clearExpression('width');
20429                         mask.dom.style.clearExpression('height');
20430                     }
20431                     if (maskMsg) {
20432                         maskMsg.remove();
20433                         data(dom, 'maskMsg', undefined);
20434                     }
20435
20436                     mask.remove();
20437                     data(dom, 'mask', undefined);
20438                     me.removeCls([XMASKED, XMASKEDRELATIVE]);
20439                 }
20440             },
20441             /**
20442              * Returns true if this element is masked. Also re-centers any displayed message within the mask.
20443              * @return {Boolean}
20444              */
20445             isMasked : function() {
20446                 var me = this,
20447                     mask = data(me.dom, 'mask'),
20448                     maskMsg = data(me.dom, 'maskMsg');
20449
20450                 if (mask && mask.isVisible()) {
20451                     if (maskMsg) {
20452                         maskMsg.center(me);
20453                     }
20454                     return true;
20455                 }
20456                 return false;
20457             },
20458
20459             /**
20460              * Creates an iframe shim for this element to keep selects and other windowed objects from
20461              * showing through.
20462              * @return {Ext.core.Element} The new shim element
20463              */
20464             createShim : function() {
20465                 var el = document.createElement('iframe'),
20466                     shim;
20467
20468                 el.frameBorder = '0';
20469                 el.className = Ext.baseCSSPrefix + 'shim';
20470                 el.src = Ext.SSL_SECURE_URL;
20471                 shim = Ext.get(this.dom.parentNode.insertBefore(el, this.dom));
20472                 shim.autoBoxAdjust = false;
20473                 return shim;
20474             }
20475         };
20476     }()
20477 );
20478 /**
20479  * @class Ext.core.Element
20480  */
20481 Ext.core.Element.addMethods({
20482     /**
20483      * Convenience method for constructing a KeyMap
20484      * @param {Number/Array/Object/String} 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:
20485      * <code>{key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}</code>
20486      * @param {Function} fn The function to call
20487      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the specified function is executed. Defaults to this Element.
20488      * @return {Ext.util.KeyMap} The KeyMap created
20489      */
20490     addKeyListener : function(key, fn, scope){
20491         var config;
20492         if(typeof key != 'object' || Ext.isArray(key)){
20493             config = {
20494                 key: key,
20495                 fn: fn,
20496                 scope: scope
20497             };
20498         }else{
20499             config = {
20500                 key : key.key,
20501                 shift : key.shift,
20502                 ctrl : key.ctrl,
20503                 alt : key.alt,
20504                 fn: fn,
20505                 scope: scope
20506             };
20507         }
20508         return Ext.create('Ext.util.KeyMap', this, config);
20509     },
20510
20511     /**
20512      * Creates a KeyMap for this element
20513      * @param {Object} config The KeyMap config. See {@link Ext.util.KeyMap} for more details
20514      * @return {Ext.util.KeyMap} The KeyMap created
20515      */
20516     addKeyMap : function(config){
20517         return Ext.create('Ext.util.KeyMap', this, config);
20518     }
20519 });
20520
20521 //Import the newly-added Ext.core.Element functions into CompositeElementLite. We call this here because
20522 //Element.keys.js is the last extra Ext.core.Element include in the ext-all.js build
20523 Ext.CompositeElementLite.importElementMethods();
20524
20525 /**
20526  * @class Ext.CompositeElementLite
20527  */
20528 Ext.apply(Ext.CompositeElementLite.prototype, {
20529     addElements : function(els, root){
20530         if(!els){
20531             return this;
20532         }
20533         if(typeof els == "string"){
20534             els = Ext.core.Element.selectorFunction(els, root);
20535         }
20536         var yels = this.elements;
20537         Ext.each(els, function(e) {
20538             yels.push(Ext.get(e));
20539         });
20540         return this;
20541     },
20542
20543     /**
20544      * Returns the first Element
20545      * @return {Ext.core.Element}
20546      */
20547     first : function(){
20548         return this.item(0);
20549     },
20550
20551     /**
20552      * Returns the last Element
20553      * @return {Ext.core.Element}
20554      */
20555     last : function(){
20556         return this.item(this.getCount()-1);
20557     },
20558
20559     /**
20560      * Returns true if this composite contains the passed element
20561      * @param el {Mixed} The id of an element, or an Ext.core.Element, or an HtmlElement to find within the composite collection.
20562      * @return Boolean
20563      */
20564     contains : function(el){
20565         return this.indexOf(el) != -1;
20566     },
20567
20568     /**
20569     * Removes the specified element(s).
20570     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
20571     * or an array of any of those.
20572     * @param {Boolean} removeDom (optional) True to also remove the element from the document
20573     * @return {CompositeElement} this
20574     */
20575     removeElement : function(keys, removeDom){
20576         var me = this,
20577             els = this.elements,
20578             el;
20579         Ext.each(keys, function(val){
20580             if ((el = (els[val] || els[val = me.indexOf(val)]))) {
20581                 if(removeDom){
20582                     if(el.dom){
20583                         el.remove();
20584                     }else{
20585                         Ext.removeNode(el);
20586                     }
20587                 }
20588                 Ext.Array.erase(els, val, 1);
20589             }
20590         });
20591         return this;
20592     }
20593 });
20594
20595 /**
20596  * @class Ext.CompositeElement
20597  * @extends Ext.CompositeElementLite
20598  * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter
20599  * members, or to perform collective actions upon the whole set.</p>
20600  * <p>Although they are not listed, this class supports all of the methods of {@link Ext.core.Element} and
20601  * {@link Ext.fx.Anim}. The methods from these classes will be performed on all the elements in this collection.</p>
20602  * <p>All methods return <i>this</i> and can be chained.</p>
20603  * Usage:
20604 <pre><code>
20605 var els = Ext.select("#some-el div.some-class", true);
20606 // or select directly from an existing element
20607 var el = Ext.get('some-el');
20608 el.select('div.some-class', true);
20609
20610 els.setWidth(100); // all elements become 100 width
20611 els.hide(true); // all elements fade out and hide
20612 // or
20613 els.setWidth(100).hide(true);
20614 </code></pre>
20615  */
20616 Ext.CompositeElement = Ext.extend(Ext.CompositeElementLite, {
20617     
20618     constructor : function(els, root){
20619         this.elements = [];
20620         this.add(els, root);
20621     },
20622     
20623     // private
20624     getElement : function(el){
20625         // In this case just return it, since we already have a reference to it
20626         return el;
20627     },
20628     
20629     // private
20630     transformElement : function(el){
20631         return Ext.get(el);
20632     }
20633
20634     /**
20635     * Adds elements to this composite.
20636     * @param {String/Array} els A string CSS selector, an array of elements or an element
20637     * @return {CompositeElement} this
20638     */
20639
20640     /**
20641      * Returns the Element object at the specified index
20642      * @param {Number} index
20643      * @return {Ext.core.Element}
20644      */
20645
20646     /**
20647      * Iterates each `element` in this `composite` calling the supplied function using {@link Ext#each Ext.each}.
20648      * @param {Function} fn 
20649
20650 The function to be called with each
20651 `element`. If the supplied function returns <tt>false</tt>,
20652 iteration stops. This function is called with the following arguments:
20653
20654 - `element` : __Ext.core.Element++
20655     The element at the current `index` in the `composite`
20656     
20657 - `composite` : __Object__ 
20658     This composite.
20659
20660 - `index` : __Number__ 
20661     The current index within the `composite`
20662
20663      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the specified function is executed.
20664      * Defaults to the <code>element</code> at the current <code>index</code>
20665      * within the composite.
20666      * @return {CompositeElement} this
20667      * @markdown
20668      */
20669 });
20670
20671 /**
20672  * Selects elements based on the passed CSS selector to enable {@link Ext.core.Element Element} methods
20673  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
20674  * {@link Ext.CompositeElementLite CompositeElementLite} object.
20675  * @param {String/Array} selector The CSS selector or an array of elements
20676  * @param {Boolean} unique (optional) true to create a unique Ext.core.Element for each element (defaults to a shared flyweight object)
20677  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
20678  * @return {CompositeElementLite/CompositeElement}
20679  * @member Ext.core.Element
20680  * @method select
20681  */
20682 Ext.core.Element.select = function(selector, unique, root){
20683     var els;
20684     if(typeof selector == "string"){
20685         els = Ext.core.Element.selectorFunction(selector, root);
20686     }else if(selector.length !== undefined){
20687         els = selector;
20688     }else{
20689         Ext.Error.raise({
20690             sourceClass: "Ext.core.Element",
20691             sourceMethod: "select",
20692             selector: selector,
20693             unique: unique,
20694             root: root,
20695             msg: "Invalid selector specified: " + selector
20696         });
20697     }
20698     return (unique === true) ? new Ext.CompositeElement(els) : new Ext.CompositeElementLite(els);
20699 };
20700
20701 /**
20702  * Selects elements based on the passed CSS selector to enable {@link Ext.core.Element Element} methods
20703  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
20704  * {@link Ext.CompositeElementLite CompositeElementLite} object.
20705  * @param {String/Array} selector The CSS selector or an array of elements
20706  * @param {Boolean} unique (optional) true to create a unique Ext.core.Element for each element (defaults to a shared flyweight object)
20707  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
20708  * @return {CompositeElementLite/CompositeElement}
20709  * @member Ext
20710  * @method select
20711  */
20712 Ext.select = Ext.core.Element.select;
20713
20714
20715