Upgrade to ExtJS 4.0.1 - Released 05/18/2011
[extjs.git] / pkgs / dom.js
1 /*
2 Ext JS - JavaScript Library
3 Copyright (c) 2006-2011, Sencha Inc.
4 All rights reserved.
5 licensing@sencha.com
6 */
7 /**
8  * @class Ext.core.DomHelper
9  * <p>The DomHelper class provides a layer of abstraction from DOM and transparently supports creating
10  * elements via DOM or using HTML fragments. It also has the ability to create HTML fragment templates
11  * from your DOM building code.</p>
12  *
13  * <p><b><u>DomHelper element specification object</u></b></p>
14  * <p>A specification object is used when creating elements. Attributes of this object
15  * are assumed to be element attributes, except for 4 special attributes:
16  * <div class="mdetail-params"><ul>
17  * <li><b><tt>tag</tt></b> : <div class="sub-desc">The tag name of the element</div></li>
18  * <li><b><tt>children</tt></b> : or <tt>cn</tt><div class="sub-desc">An array of the
19  * same kind of element definition objects to be created and appended. These can be nested
20  * as deep as you want.</div></li>
21  * <li><b><tt>cls</tt></b> : <div class="sub-desc">The class attribute of the element.
22  * This will end up being either the "class" attribute on a HTML fragment or className
23  * for a DOM node, depending on whether DomHelper is using fragments or DOM.</div></li>
24  * <li><b><tt>html</tt></b> : <div class="sub-desc">The innerHTML for the element</div></li>
25  * </ul></div></p>
26  * <p><b>NOTE:</b> For other arbitrary attributes, the value will currently <b>not</b> be automatically
27  * HTML-escaped prior to building the element's HTML string. This means that if your attribute value
28  * contains special characters that would not normally be allowed in a double-quoted attribute value,
29  * you <b>must</b> manually HTML-encode it beforehand (see {@link Ext.String#htmlEncode}) or risk
30  * malformed HTML being created. This behavior may change in a future release.</p>
31  *
32  * <p><b><u>Insertion methods</u></b></p>
33  * <p>Commonly used insertion methods:
34  * <div class="mdetail-params"><ul>
35  * <li><b><tt>{@link #append}</tt></b> : <div class="sub-desc"></div></li>
36  * <li><b><tt>{@link #insertBefore}</tt></b> : <div class="sub-desc"></div></li>
37  * <li><b><tt>{@link #insertAfter}</tt></b> : <div class="sub-desc"></div></li>
38  * <li><b><tt>{@link #overwrite}</tt></b> : <div class="sub-desc"></div></li>
39  * <li><b><tt>{@link #createTemplate}</tt></b> : <div class="sub-desc"></div></li>
40  * <li><b><tt>{@link #insertHtml}</tt></b> : <div class="sub-desc"></div></li>
41  * </ul></div></p>
42  *
43  * <p><b><u>Example</u></b></p>
44  * <p>This is an example, where an unordered list with 3 children items is appended to an existing
45  * element with id <tt>'my-div'</tt>:<br>
46  <pre><code>
47 var dh = Ext.core.DomHelper; // create shorthand alias
48 // specification object
49 var spec = {
50     id: 'my-ul',
51     tag: 'ul',
52     cls: 'my-list',
53     // append children after creating
54     children: [     // may also specify 'cn' instead of 'children'
55         {tag: 'li', id: 'item0', html: 'List Item 0'},
56         {tag: 'li', id: 'item1', html: 'List Item 1'},
57         {tag: 'li', id: 'item2', html: 'List Item 2'}
58     ]
59 };
60 var list = dh.append(
61     'my-div', // the context element 'my-div' can either be the id or the actual node
62     spec      // the specification object
63 );
64  </code></pre></p>
65  * <p>Element creation specification parameters in this class may also be passed as an Array of
66  * specification objects. This can be used to insert multiple sibling nodes into an existing
67  * container very efficiently. For example, to add more list items to the example above:<pre><code>
68 dh.append('my-ul', [
69     {tag: 'li', id: 'item3', html: 'List Item 3'},
70     {tag: 'li', id: 'item4', html: 'List Item 4'}
71 ]);
72  * </code></pre></p>
73  *
74  * <p><b><u>Templating</u></b></p>
75  * <p>The real power is in the built-in templating. Instead of creating or appending any elements,
76  * <tt>{@link #createTemplate}</tt> returns a Template object which can be used over and over to
77  * insert new elements. Revisiting the example above, we could utilize templating this time:
78  * <pre><code>
79 // create the node
80 var list = dh.append('my-div', {tag: 'ul', cls: 'my-list'});
81 // get template
82 var tpl = dh.createTemplate({tag: 'li', id: 'item{0}', html: 'List Item {0}'});
83
84 for(var i = 0; i < 5, i++){
85     tpl.append(list, [i]); // use template to append to the actual node
86 }
87  * </code></pre></p>
88  * <p>An example using a template:<pre><code>
89 var html = '<a id="{0}" href="{1}" class="nav">{2}</a>';
90
91 var tpl = new Ext.core.DomHelper.createTemplate(html);
92 tpl.append('blog-roll', ['link1', 'http://www.edspencer.net/', "Ed&#39;s Site"]);
93 tpl.append('blog-roll', ['link2', 'http://www.dustindiaz.com/', "Dustin&#39;s Site"]);
94  * </code></pre></p>
95  *
96  * <p>The same example using named parameters:<pre><code>
97 var html = '<a id="{id}" href="{url}" class="nav">{text}</a>';
98
99 var tpl = new Ext.core.DomHelper.createTemplate(html);
100 tpl.append('blog-roll', {
101     id: 'link1',
102     url: 'http://www.edspencer.net/',
103     text: "Ed&#39;s Site"
104 });
105 tpl.append('blog-roll', {
106     id: 'link2',
107     url: 'http://www.dustindiaz.com/',
108     text: "Dustin&#39;s Site"
109 });
110  * </code></pre></p>
111  *
112  * <p><b><u>Compiling Templates</u></b></p>
113  * <p>Templates are applied using regular expressions. The performance is great, but if
114  * you are adding a bunch of DOM elements using the same template, you can increase
115  * performance even further by {@link Ext.Template#compile "compiling"} the template.
116  * The way "{@link Ext.Template#compile compile()}" works is the template is parsed and
117  * broken up at the different variable points and a dynamic function is created and eval'ed.
118  * The generated function performs string concatenation of these parts and the passed
119  * variables instead of using regular expressions.
120  * <pre><code>
121 var html = '<a id="{id}" href="{url}" class="nav">{text}</a>';
122
123 var tpl = new Ext.core.DomHelper.createTemplate(html);
124 tpl.compile();
125
126 //... use template like normal
127  * </code></pre></p>
128  *
129  * <p><b><u>Performance Boost</u></b></p>
130  * <p>DomHelper will transparently create HTML fragments when it can. Using HTML fragments instead
131  * of DOM can significantly boost performance.</p>
132  * <p>Element creation specification parameters may also be strings. If {@link #useDom} is <tt>false</tt>,
133  * then the string is used as innerHTML. If {@link #useDom} is <tt>true</tt>, a string specification
134  * results in the creation of a text node. Usage:</p>
135  * <pre><code>
136 Ext.core.DomHelper.useDom = true; // force it to use DOM; reduces performance
137  * </code></pre>
138  * @singleton
139  */
140 Ext.ns('Ext.core');
141 Ext.core.DomHelper = function(){
142     var tempTableEl = null,
143         emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i,
144         tableRe = /^table|tbody|tr|td$/i,
145         confRe = /tag|children|cn|html$/i,
146         tableElRe = /td|tr|tbody/i,
147         endRe = /end/i,
148         pub,
149         // kill repeat to save bytes
150         afterbegin = 'afterbegin',
151         afterend = 'afterend',
152         beforebegin = 'beforebegin',
153         beforeend = 'beforeend',
154         ts = '<table>',
155         te = '</table>',
156         tbs = ts+'<tbody>',
157         tbe = '</tbody>'+te,
158         trs = tbs + '<tr>',
159         tre = '</tr>'+tbe;
160
161     // private
162     function doInsert(el, o, returnElement, pos, sibling, append){
163         el = Ext.getDom(el);
164         var newNode;
165         if (pub.useDom) {
166             newNode = createDom(o, null);
167             if (append) {
168                 el.appendChild(newNode);
169             } else {
170                 (sibling == 'firstChild' ? el : el.parentNode).insertBefore(newNode, el[sibling] || el);
171             }
172         } else {
173             newNode = Ext.core.DomHelper.insertHtml(pos, el, Ext.core.DomHelper.createHtml(o));
174         }
175         return returnElement ? Ext.get(newNode, true) : newNode;
176     }
177     
178     function createDom(o, parentNode){
179         var el,
180             doc = document,
181             useSet,
182             attr,
183             val,
184             cn;
185
186         if (Ext.isArray(o)) {                       // Allow Arrays of siblings to be inserted
187             el = doc.createDocumentFragment(); // in one shot using a DocumentFragment
188             for (var i = 0, l = o.length; i < l; i++) {
189                 createDom(o[i], el);
190             }
191         } else if (typeof o == 'string') {         // Allow a string as a child spec.
192             el = doc.createTextNode(o);
193         } else {
194             el = doc.createElement( o.tag || 'div' );
195             useSet = !!el.setAttribute; // In IE some elements don't have setAttribute
196             for (attr in o) {
197                 if(!confRe.test(attr)){
198                     val = o[attr];
199                     if(attr == 'cls'){
200                         el.className = val;
201                     }else{
202                         if(useSet){
203                             el.setAttribute(attr, val);
204                         }else{
205                             el[attr] = val;
206                         }
207                     }
208                 }
209             }
210             Ext.core.DomHelper.applyStyles(el, o.style);
211
212             if ((cn = o.children || o.cn)) {
213                 createDom(cn, el);
214             } else if (o.html) {
215                 el.innerHTML = o.html;
216             }
217         }
218         if(parentNode){
219            parentNode.appendChild(el);
220         }
221         return el;
222     }
223
224     // build as innerHTML where available
225     function createHtml(o){
226         var b = '',
227             attr,
228             val,
229             key,
230             cn,
231             i;
232
233         if(typeof o == "string"){
234             b = o;
235         } else if (Ext.isArray(o)) {
236             for (i=0; i < o.length; i++) {
237                 if(o[i]) {
238                     b += createHtml(o[i]);
239                 }
240             }
241         } else {
242             b += '<' + (o.tag = o.tag || 'div');
243             for (attr in o) {
244                 val = o[attr];
245                 if(!confRe.test(attr)){
246                     if (typeof val == "object") {
247                         b += ' ' + attr + '="';
248                         for (key in val) {
249                             b += key + ':' + val[key] + ';';
250                         }
251                         b += '"';
252                     }else{
253                         b += ' ' + ({cls : 'class', htmlFor : 'for'}[attr] || attr) + '="' + val + '"';
254                     }
255                 }
256             }
257             // Now either just close the tag or try to add children and close the tag.
258             if (emptyTags.test(o.tag)) {
259                 b += '/>';
260             } else {
261                 b += '>';
262                 if ((cn = o.children || o.cn)) {
263                     b += createHtml(cn);
264                 } else if(o.html){
265                     b += o.html;
266                 }
267                 b += '</' + o.tag + '>';
268             }
269         }
270         return b;
271     }
272
273     function ieTable(depth, s, h, e){
274         tempTableEl.innerHTML = [s, h, e].join('');
275         var i = -1,
276             el = tempTableEl,
277             ns;
278         while(++i < depth){
279             el = el.firstChild;
280         }
281 //      If the result is multiple siblings, then encapsulate them into one fragment.
282         ns = el.nextSibling;
283         if (ns){
284             var df = document.createDocumentFragment();
285             while(el){
286                 ns = el.nextSibling;
287                 df.appendChild(el);
288                 el = ns;
289             }
290             el = df;
291         }
292         return el;
293     }
294
295     /**
296      * @ignore
297      * Nasty code for IE's broken table implementation
298      */
299     function insertIntoTable(tag, where, el, html) {
300         var node,
301             before;
302
303         tempTableEl = tempTableEl || document.createElement('div');
304
305         if(tag == 'td' && (where == afterbegin || where == beforeend) ||
306            !tableElRe.test(tag) && (where == beforebegin || where == afterend)) {
307             return null;
308         }
309         before = where == beforebegin ? el :
310                  where == afterend ? el.nextSibling :
311                  where == afterbegin ? el.firstChild : null;
312
313         if (where == beforebegin || where == afterend) {
314             el = el.parentNode;
315         }
316
317         if (tag == 'td' || (tag == 'tr' && (where == beforeend || where == afterbegin))) {
318             node = ieTable(4, trs, html, tre);
319         } else if ((tag == 'tbody' && (where == beforeend || where == afterbegin)) ||
320                    (tag == 'tr' && (where == beforebegin || where == afterend))) {
321             node = ieTable(3, tbs, html, tbe);
322         } else {
323             node = ieTable(2, ts, html, te);
324         }
325         el.insertBefore(node, before);
326         return node;
327     }
328     
329     /**
330      * @ignore
331      * Fix for IE9 createContextualFragment missing method
332      */   
333     function createContextualFragment(html){
334         var div = document.createElement("div"),
335             fragment = document.createDocumentFragment(),
336             i = 0,
337             length, childNodes;
338         
339         div.innerHTML = html;
340         childNodes = div.childNodes;
341         length = childNodes.length;
342
343         for (; i < length; i++) {
344             fragment.appendChild(childNodes[i].cloneNode(true));
345         }
346
347         return fragment;
348     }
349     
350     pub = {
351         /**
352          * Returns the markup for the passed Element(s) config.
353          * @param {Object} o The DOM object spec (and children)
354          * @return {String}
355          */
356         markup : function(o){
357             return createHtml(o);
358         },
359
360         /**
361          * Applies a style specification to an element.
362          * @param {String/HTMLElement} el The element to apply styles to
363          * @param {String/Object/Function} styles A style specification string e.g. 'width:100px', or object in the form {width:'100px'}, or
364          * a function which returns such a specification.
365          */
366         applyStyles : function(el, styles){
367             if (styles) {
368                 el = Ext.fly(el);
369                 if (typeof styles == "function") {
370                     styles = styles.call();
371                 }
372                 if (typeof styles == "string") {
373                     styles = Ext.core.Element.parseStyles(styles);
374                 }
375                 if (typeof styles == "object") {
376                     el.setStyle(styles);
377                 }
378             }
379         },
380
381         /**
382          * Inserts an HTML fragment into the DOM.
383          * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
384          * @param {HTMLElement/TextNode} el The context element
385          * @param {String} html The HTML fragment
386          * @return {HTMLElement} The new node
387          */
388         insertHtml : function(where, el, html){
389             var hash = {},
390                 hashVal,
391                 range,
392                 rangeEl,
393                 setStart,
394                 frag,
395                 rs;
396
397             where = where.toLowerCase();
398             // add these here because they are used in both branches of the condition.
399             hash[beforebegin] = ['BeforeBegin', 'previousSibling'];
400             hash[afterend] = ['AfterEnd', 'nextSibling'];
401             
402             // if IE and context element is an HTMLElement
403             if (el.insertAdjacentHTML) {
404                 if(tableRe.test(el.tagName) && (rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html))){
405                     return rs;
406                 }
407                 
408                 // add these two to the hash.
409                 hash[afterbegin] = ['AfterBegin', 'firstChild'];
410                 hash[beforeend] = ['BeforeEnd', 'lastChild'];
411                 if ((hashVal = hash[where])) {
412                     el.insertAdjacentHTML(hashVal[0], html);
413                     return el[hashVal[1]];
414                 }
415             // if (not IE and context element is an HTMLElement) or TextNode
416             } else {
417                 // we cannot insert anything inside a textnode so...
418                 if (Ext.isTextNode(el)) {
419                     where = where === 'afterbegin' ? 'beforebegin' : where; 
420                     where = where === 'beforeend' ? 'afterend' : where;
421                 }
422                 range = Ext.supports.CreateContextualFragment ? el.ownerDocument.createRange() : undefined;
423                 setStart = 'setStart' + (endRe.test(where) ? 'After' : 'Before');
424                 if (hash[where]) {
425                     if (range) {
426                         range[setStart](el);
427                         frag = range.createContextualFragment(html);
428                     } else {
429                         frag = createContextualFragment(html);
430                     }
431                     el.parentNode.insertBefore(frag, where == beforebegin ? el : el.nextSibling);
432                     return el[(where == beforebegin ? 'previous' : 'next') + 'Sibling'];
433                 } else {
434                     rangeEl = (where == afterbegin ? 'first' : 'last') + 'Child';
435                     if (el.firstChild) {
436                         if (range) {
437                             range[setStart](el[rangeEl]);
438                             frag = range.createContextualFragment(html);
439                         } else {
440                             frag = createContextualFragment(html);
441                         }
442                         
443                         if(where == afterbegin){
444                             el.insertBefore(frag, el.firstChild);
445                         }else{
446                             el.appendChild(frag);
447                         }
448                     } else {
449                         el.innerHTML = html;
450                     }
451                     return el[rangeEl];
452                 }
453             }
454             //<debug>
455             Ext.Error.raise({
456                 sourceClass: 'Ext.core.DomHelper',
457                 sourceMethod: 'insertHtml',
458                 htmlToInsert: html,
459                 targetElement: el,
460                 msg: 'Illegal insertion point reached: "' + where + '"'
461             });
462             //</debug>
463         },
464
465         /**
466          * Creates new DOM element(s) and inserts them before el.
467          * @param {Mixed} el The context element
468          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
469          * @param {Boolean} returnElement (optional) true to return a Ext.core.Element
470          * @return {HTMLElement/Ext.core.Element} The new node
471          */
472         insertBefore : function(el, o, returnElement){
473             return doInsert(el, o, returnElement, beforebegin);
474         },
475
476         /**
477          * Creates new DOM element(s) and inserts them after el.
478          * @param {Mixed} el The context element
479          * @param {Object} o The DOM object spec (and children)
480          * @param {Boolean} returnElement (optional) true to return a Ext.core.Element
481          * @return {HTMLElement/Ext.core.Element} The new node
482          */
483         insertAfter : function(el, o, returnElement){
484             return doInsert(el, o, returnElement, afterend, 'nextSibling');
485         },
486
487         /**
488          * Creates new DOM element(s) and inserts them as the first child of el.
489          * @param {Mixed} el The context element
490          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
491          * @param {Boolean} returnElement (optional) true to return a Ext.core.Element
492          * @return {HTMLElement/Ext.core.Element} The new node
493          */
494         insertFirst : function(el, o, returnElement){
495             return doInsert(el, o, returnElement, afterbegin, 'firstChild');
496         },
497
498         /**
499          * Creates new DOM element(s) and appends them to el.
500          * @param {Mixed} el The context element
501          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
502          * @param {Boolean} returnElement (optional) true to return a Ext.core.Element
503          * @return {HTMLElement/Ext.core.Element} The new node
504          */
505         append : function(el, o, returnElement){
506             return doInsert(el, o, returnElement, beforeend, '', true);
507         },
508
509         /**
510          * Creates new DOM element(s) and overwrites the contents of el with them.
511          * @param {Mixed} el The context element
512          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
513          * @param {Boolean} returnElement (optional) true to return a Ext.core.Element
514          * @return {HTMLElement/Ext.core.Element} The new node
515          */
516         overwrite : function(el, o, returnElement){
517             el = Ext.getDom(el);
518             el.innerHTML = createHtml(o);
519             return returnElement ? Ext.get(el.firstChild) : el.firstChild;
520         },
521
522         createHtml : createHtml,
523         
524         /**
525          * Creates new DOM element(s) without inserting them to the document.
526          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
527          * @return {HTMLElement} The new uninserted node
528          * @method
529          */
530         createDom: createDom,
531         
532         /** True to force the use of DOM instead of html fragments @type Boolean */
533         useDom : false,
534         
535         /**
536          * Creates a new Ext.Template from the DOM object spec.
537          * @param {Object} o The DOM object spec (and children)
538          * @return {Ext.Template} The new template
539          */
540         createTemplate : function(o){
541             var html = Ext.core.DomHelper.createHtml(o);
542             return Ext.create('Ext.Template', html);
543         }
544     };
545     return pub;
546 }();
547
548 /*
549  * This is code is also distributed under MIT license for use
550  * with jQuery and prototype JavaScript libraries.
551  */
552 /**
553  * @class Ext.DomQuery
554 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).
555 <p>
556 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>
557
558 <p>
559 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.
560 </p>
561 <h4>Element Selectors:</h4>
562 <ul class="list">
563     <li> <b>*</b> any element</li>
564     <li> <b>E</b> an element with the tag E</li>
565     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
566     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
567     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
568     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
569 </ul>
570 <h4>Attribute Selectors:</h4>
571 <p>The use of &#64; and quotes are optional. For example, div[&#64;foo='bar'] is also a valid attribute selector.</p>
572 <ul class="list">
573     <li> <b>E[foo]</b> has an attribute "foo"</li>
574     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
575     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
576     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
577     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
578     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
579     <li> <b>E[foo!=bar]</b> attribute "foo" does not equal "bar"</li>
580 </ul>
581 <h4>Pseudo Classes:</h4>
582 <ul class="list">
583     <li> <b>E:first-child</b> E is the first child of its parent</li>
584     <li> <b>E:last-child</b> E is the last child of its parent</li>
585     <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>
586     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
587     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
588     <li> <b>E:only-child</b> E is the only child of its parent</li>
589     <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>
590     <li> <b>E:first</b> the first E in the resultset</li>
591     <li> <b>E:last</b> the last E in the resultset</li>
592     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
593     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
594     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
595     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
596     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
597     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
598     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
599     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
600     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
601     <li> <b>E:any(S1|S2|S2)</b> an E element which matches any of the simple selectors S1, S2 or S3//\\</li>
602 </ul>
603 <h4>CSS Value Selectors:</h4>
604 <ul class="list">
605     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
606     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
607     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
608     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
609     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
610     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
611 </ul>
612  * @singleton
613  */
614 Ext.ns('Ext.core');
615
616 Ext.core.DomQuery = Ext.DomQuery = function(){
617     var cache = {},
618         simpleCache = {},
619         valueCache = {},
620         nonSpace = /\S/,
621         trimRe = /^\s+|\s+$/g,
622         tplRe = /\{(\d+)\}/g,
623         modeRe = /^(\s?[\/>+~]\s?|\s|$)/,
624         tagTokenRe = /^(#)?([\w-\*]+)/,
625         nthRe = /(\d*)n\+?(\d*)/,
626         nthRe2 = /\D/,
627         // This is for IE MSXML which does not support expandos.
628     // IE runs the same speed using setAttribute, however FF slows way down
629     // and Safari completely fails so they need to continue to use expandos.
630     isIE = window.ActiveXObject ? true : false,
631     key = 30803;
632
633     // this eval is stop the compressor from
634     // renaming the variable to something shorter
635     eval("var batch = 30803;");
636
637     // Retrieve the child node from a particular
638     // parent at the specified index.
639     function child(parent, index){
640         var i = 0,
641             n = parent.firstChild;
642         while(n){
643             if(n.nodeType == 1){
644                if(++i == index){
645                    return n;
646                }
647             }
648             n = n.nextSibling;
649         }
650         return null;
651     }
652
653     // retrieve the next element node
654     function next(n){
655         while((n = n.nextSibling) && n.nodeType != 1);
656         return n;
657     }
658
659     // retrieve the previous element node
660     function prev(n){
661         while((n = n.previousSibling) && n.nodeType != 1);
662         return n;
663     }
664
665     // Mark each child node with a nodeIndex skipping and
666     // removing empty text nodes.
667     function children(parent){
668         var n = parent.firstChild,
669         nodeIndex = -1,
670         nextNode;
671         while(n){
672             nextNode = n.nextSibling;
673             // clean worthless empty nodes.
674             if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
675             parent.removeChild(n);
676             }else{
677             // add an expando nodeIndex
678             n.nodeIndex = ++nodeIndex;
679             }
680             n = nextNode;
681         }
682         return this;
683     }
684
685
686     // nodeSet - array of nodes
687     // cls - CSS Class
688     function byClassName(nodeSet, cls){
689         if(!cls){
690             return nodeSet;
691         }
692         var result = [], ri = -1;
693         for(var i = 0, ci; ci = nodeSet[i]; i++){
694             if((' '+ci.className+' ').indexOf(cls) != -1){
695                 result[++ri] = ci;
696             }
697         }
698         return result;
699     };
700
701     function attrValue(n, attr){
702         // if its an array, use the first node.
703         if(!n.tagName && typeof n.length != "undefined"){
704             n = n[0];
705         }
706         if(!n){
707             return null;
708         }
709
710         if(attr == "for"){
711             return n.htmlFor;
712         }
713         if(attr == "class" || attr == "className"){
714             return n.className;
715         }
716         return n.getAttribute(attr) || n[attr];
717
718     };
719
720
721     // ns - nodes
722     // mode - false, /, >, +, ~
723     // tagName - defaults to "*"
724     function getNodes(ns, mode, tagName){
725         var result = [], ri = -1, cs;
726         if(!ns){
727             return result;
728         }
729         tagName = tagName || "*";
730         // convert to array
731         if(typeof ns.getElementsByTagName != "undefined"){
732             ns = [ns];
733         }
734
735         // no mode specified, grab all elements by tagName
736         // at any depth
737         if(!mode){
738             for(var i = 0, ni; ni = ns[i]; i++){
739                 cs = ni.getElementsByTagName(tagName);
740                 for(var j = 0, ci; ci = cs[j]; j++){
741                     result[++ri] = ci;
742                 }
743             }
744         // Direct Child mode (/ or >)
745         // E > F or E/F all direct children elements of E that have the tag
746         } else if(mode == "/" || mode == ">"){
747             var utag = tagName.toUpperCase();
748             for(var i = 0, ni, cn; ni = ns[i]; i++){
749                 cn = ni.childNodes;
750                 for(var j = 0, cj; cj = cn[j]; j++){
751                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
752                         result[++ri] = cj;
753                     }
754                 }
755             }
756         // Immediately Preceding mode (+)
757         // E + F all elements with the tag F that are immediately preceded by an element with the tag E
758         }else if(mode == "+"){
759             var utag = tagName.toUpperCase();
760             for(var i = 0, n; n = ns[i]; i++){
761                 while((n = n.nextSibling) && n.nodeType != 1);
762                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
763                     result[++ri] = n;
764                 }
765             }
766         // Sibling mode (~)
767         // E ~ F all elements with the tag F that are preceded by a sibling element with the tag E
768         }else if(mode == "~"){
769             var utag = tagName.toUpperCase();
770             for(var i = 0, n; n = ns[i]; i++){
771                 while((n = n.nextSibling)){
772                     if (n.nodeName == utag || n.nodeName == tagName || tagName == '*'){
773                         result[++ri] = n;
774                     }
775                 }
776             }
777         }
778         return result;
779     }
780
781     function concat(a, b){
782         if(b.slice){
783             return a.concat(b);
784         }
785         for(var i = 0, l = b.length; i < l; i++){
786             a[a.length] = b[i];
787         }
788         return a;
789     }
790
791     function byTag(cs, tagName){
792         if(cs.tagName || cs == document){
793             cs = [cs];
794         }
795         if(!tagName){
796             return cs;
797         }
798         var result = [], ri = -1;
799         tagName = tagName.toLowerCase();
800         for(var i = 0, ci; ci = cs[i]; i++){
801             if(ci.nodeType == 1 && ci.tagName.toLowerCase() == tagName){
802                 result[++ri] = ci;
803             }
804         }
805         return result;
806     }
807
808     function byId(cs, id){
809         if(cs.tagName || cs == document){
810             cs = [cs];
811         }
812         if(!id){
813             return cs;
814         }
815         var result = [], ri = -1;
816         for(var i = 0, ci; ci = cs[i]; i++){
817             if(ci && ci.id == id){
818                 result[++ri] = ci;
819                 return result;
820             }
821         }
822         return result;
823     }
824
825     // operators are =, !=, ^=, $=, *=, %=, |= and ~=
826     // custom can be "{"
827     function byAttribute(cs, attr, value, op, custom){
828         var result = [],
829             ri = -1,
830             useGetStyle = custom == "{",
831             fn = Ext.DomQuery.operators[op],
832             a,
833             xml,
834             hasXml;
835
836         for(var i = 0, ci; ci = cs[i]; i++){
837             // skip non-element nodes.
838             if(ci.nodeType != 1){
839                 continue;
840             }
841             // only need to do this for the first node
842             if(!hasXml){
843                 xml = Ext.DomQuery.isXml(ci);
844                 hasXml = true;
845             }
846
847             // we only need to change the property names if we're dealing with html nodes, not XML
848             if(!xml){
849                 if(useGetStyle){
850                     a = Ext.DomQuery.getStyle(ci, attr);
851                 } else if (attr == "class" || attr == "className"){
852                     a = ci.className;
853                 } else if (attr == "for"){
854                     a = ci.htmlFor;
855                 } else if (attr == "href"){
856                     // getAttribute href bug
857                     // http://www.glennjones.net/Post/809/getAttributehrefbug.htm
858                     a = ci.getAttribute("href", 2);
859                 } else{
860                     a = ci.getAttribute(attr);
861                 }
862             }else{
863                 a = ci.getAttribute(attr);
864             }
865             if((fn && fn(a, value)) || (!fn && a)){
866                 result[++ri] = ci;
867             }
868         }
869         return result;
870     }
871
872     function byPseudo(cs, name, value){
873         return Ext.DomQuery.pseudos[name](cs, value);
874     }
875
876     function nodupIEXml(cs){
877         var d = ++key,
878             r;
879         cs[0].setAttribute("_nodup", d);
880         r = [cs[0]];
881         for(var i = 1, len = cs.length; i < len; i++){
882             var c = cs[i];
883             if(!c.getAttribute("_nodup") != d){
884                 c.setAttribute("_nodup", d);
885                 r[r.length] = c;
886             }
887         }
888         for(var i = 0, len = cs.length; i < len; i++){
889             cs[i].removeAttribute("_nodup");
890         }
891         return r;
892     }
893
894     function nodup(cs){
895         if(!cs){
896             return [];
897         }
898         var len = cs.length, c, i, r = cs, cj, ri = -1;
899         if(!len || typeof cs.nodeType != "undefined" || len == 1){
900             return cs;
901         }
902         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
903             return nodupIEXml(cs);
904         }
905         var d = ++key;
906         cs[0]._nodup = d;
907         for(i = 1; c = cs[i]; i++){
908             if(c._nodup != d){
909                 c._nodup = d;
910             }else{
911                 r = [];
912                 for(var j = 0; j < i; j++){
913                     r[++ri] = cs[j];
914                 }
915                 for(j = i+1; cj = cs[j]; j++){
916                     if(cj._nodup != d){
917                         cj._nodup = d;
918                         r[++ri] = cj;
919                     }
920                 }
921                 return r;
922             }
923         }
924         return r;
925     }
926
927     function quickDiffIEXml(c1, c2){
928         var d = ++key,
929             r = [];
930         for(var i = 0, len = c1.length; i < len; i++){
931             c1[i].setAttribute("_qdiff", d);
932         }
933         for(var i = 0, len = c2.length; i < len; i++){
934             if(c2[i].getAttribute("_qdiff") != d){
935                 r[r.length] = c2[i];
936             }
937         }
938         for(var i = 0, len = c1.length; i < len; i++){
939            c1[i].removeAttribute("_qdiff");
940         }
941         return r;
942     }
943
944     function quickDiff(c1, c2){
945         var len1 = c1.length,
946             d = ++key,
947             r = [];
948         if(!len1){
949             return c2;
950         }
951         if(isIE && typeof c1[0].selectSingleNode != "undefined"){
952             return quickDiffIEXml(c1, c2);
953         }
954         for(var i = 0; i < len1; i++){
955             c1[i]._qdiff = d;
956         }
957         for(var i = 0, len = c2.length; i < len; i++){
958             if(c2[i]._qdiff != d){
959                 r[r.length] = c2[i];
960             }
961         }
962         return r;
963     }
964
965     function quickId(ns, mode, root, id){
966         if(ns == root){
967            var d = root.ownerDocument || root;
968            return d.getElementById(id);
969         }
970         ns = getNodes(ns, mode, "*");
971         return byId(ns, id);
972     }
973
974     return {
975         getStyle : function(el, name){
976             return Ext.fly(el).getStyle(name);
977         },
978         /**
979          * Compiles a selector/xpath query into a reusable function. The returned function
980          * takes one parameter "root" (optional), which is the context node from where the query should start.
981          * @param {String} selector The selector/xpath query
982          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
983          * @return {Function}
984          */
985         compile : function(path, type){
986             type = type || "select";
987
988             // setup fn preamble
989             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"],
990                 mode,
991                 lastPath,
992                 matchers = Ext.DomQuery.matchers,
993                 matchersLn = matchers.length,
994                 modeMatch,
995                 // accept leading mode switch
996                 lmode = path.match(modeRe);
997
998             if(lmode && lmode[1]){
999                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
1000                 path = path.replace(lmode[1], "");
1001             }
1002
1003             // strip leading slashes
1004             while(path.substr(0, 1)=="/"){
1005                 path = path.substr(1);
1006             }
1007
1008             while(path && lastPath != path){
1009                 lastPath = path;
1010                 var tokenMatch = path.match(tagTokenRe);
1011                 if(type == "select"){
1012                     if(tokenMatch){
1013                         // ID Selector
1014                         if(tokenMatch[1] == "#"){
1015                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tokenMatch[2]+'");';
1016                         }else{
1017                             fn[fn.length] = 'n = getNodes(n, mode, "'+tokenMatch[2]+'");';
1018                         }
1019                         path = path.replace(tokenMatch[0], "");
1020                     }else if(path.substr(0, 1) != '@'){
1021                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
1022                     }
1023                 // type of "simple"
1024                 }else{
1025                     if(tokenMatch){
1026                         if(tokenMatch[1] == "#"){
1027                             fn[fn.length] = 'n = byId(n, "'+tokenMatch[2]+'");';
1028                         }else{
1029                             fn[fn.length] = 'n = byTag(n, "'+tokenMatch[2]+'");';
1030                         }
1031                         path = path.replace(tokenMatch[0], "");
1032                     }
1033                 }
1034                 while(!(modeMatch = path.match(modeRe))){
1035                     var matched = false;
1036                     for(var j = 0; j < matchersLn; j++){
1037                         var t = matchers[j];
1038                         var m = path.match(t.re);
1039                         if(m){
1040                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
1041                                 return m[i];
1042                             });
1043                             path = path.replace(m[0], "");
1044                             matched = true;
1045                             break;
1046                         }
1047                     }
1048                     // prevent infinite loop on bad selector
1049                     if(!matched){
1050                         //<debug>
1051                         Ext.Error.raise({
1052                             sourceClass: 'Ext.DomQuery',
1053                             sourceMethod: 'compile',
1054                             msg: 'Error parsing selector. Parsing failed at "' + path + '"'
1055                         });
1056                         //</debug>
1057                     }
1058                 }
1059                 if(modeMatch[1]){
1060                     fn[fn.length] = 'mode="'+modeMatch[1].replace(trimRe, "")+'";';
1061                     path = path.replace(modeMatch[1], "");
1062                 }
1063             }
1064             // close fn out
1065             fn[fn.length] = "return nodup(n);\n}";
1066
1067             // eval fn and return it
1068             eval(fn.join(""));
1069             return f;
1070         },
1071
1072         /**
1073          * Selects an array of DOM nodes using JavaScript-only implementation.
1074          *
1075          * Use {@link #select} to take advantage of browsers built-in support for CSS selectors.
1076          *
1077          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
1078          * @param {Node/String} root (optional) The start of the query (defaults to document).
1079          * @return {Array} An Array of DOM elements which match the selector. If there are
1080          * no matches, and empty Array is returned.
1081          */
1082         jsSelect: function(path, root, type){
1083             // set root to doc if not specified.
1084             root = root || document;
1085
1086             if(typeof root == "string"){
1087                 root = document.getElementById(root);
1088             }
1089             var paths = path.split(","),
1090                 results = [];
1091
1092             // loop over each selector
1093             for(var i = 0, len = paths.length; i < len; i++){
1094                 var subPath = paths[i].replace(trimRe, "");
1095                 // compile and place in cache
1096                 if(!cache[subPath]){
1097                     cache[subPath] = Ext.DomQuery.compile(subPath);
1098                     if(!cache[subPath]){
1099                         //<debug>
1100                         Ext.Error.raise({
1101                             sourceClass: 'Ext.DomQuery',
1102                             sourceMethod: 'jsSelect',
1103                             msg: subPath + ' is not a valid selector'
1104                         });
1105                         //</debug>
1106                     }
1107                 }
1108                 var result = cache[subPath](root);
1109                 if(result && result != document){
1110                     results = results.concat(result);
1111                 }
1112             }
1113
1114             // if there were multiple selectors, make sure dups
1115             // are eliminated
1116             if(paths.length > 1){
1117                 return nodup(results);
1118             }
1119             return results;
1120         },
1121
1122         isXml: function(el) {
1123             var docEl = (el ? el.ownerDocument || el : 0).documentElement;
1124             return docEl ? docEl.nodeName !== "HTML" : false;
1125         },
1126
1127         /**
1128          * Selects an array of DOM nodes by CSS/XPath selector.
1129          *
1130          * Uses [document.querySelectorAll][0] if browser supports that, otherwise falls back to
1131          * {@link #jsSelect} to do the work.
1132          * 
1133          * Aliased as {@link Ext#query}.
1134          * 
1135          * [0]: https://developer.mozilla.org/en/DOM/document.querySelectorAll
1136          *
1137          * @param {String} path The selector/xpath query
1138          * @param {Node} root (optional) The start of the query (defaults to document).
1139          * @return {Array} An array of DOM elements (not a NodeList as returned by `querySelectorAll`).
1140          * Empty array when no matches.
1141          * @method
1142          */
1143         select : document.querySelectorAll ? function(path, root, type) {
1144             root = root || document;
1145             if (!Ext.DomQuery.isXml(root)) {
1146             try {
1147                 var cs = root.querySelectorAll(path);
1148                 return Ext.Array.toArray(cs);
1149             }
1150             catch (ex) {}
1151             }
1152             return Ext.DomQuery.jsSelect.call(this, path, root, type);
1153         } : function(path, root, type) {
1154             return Ext.DomQuery.jsSelect.call(this, path, root, type);
1155         },
1156
1157         /**
1158          * Selects a single element.
1159          * @param {String} selector The selector/xpath query
1160          * @param {Node} root (optional) The start of the query (defaults to document).
1161          * @return {Element} The DOM element which matched the selector.
1162          */
1163         selectNode : function(path, root){
1164             return Ext.DomQuery.select(path, root)[0];
1165         },
1166
1167         /**
1168          * Selects the value of a node, optionally replacing null with the defaultValue.
1169          * @param {String} selector The selector/xpath query
1170          * @param {Node} root (optional) The start of the query (defaults to document).
1171          * @param {String} defaultValue
1172          * @return {String}
1173          */
1174         selectValue : function(path, root, defaultValue){
1175             path = path.replace(trimRe, "");
1176             if(!valueCache[path]){
1177                 valueCache[path] = Ext.DomQuery.compile(path, "select");
1178             }
1179             var n = valueCache[path](root), v;
1180             n = n[0] ? n[0] : n;
1181
1182             // overcome a limitation of maximum textnode size
1183             // Rumored to potentially crash IE6 but has not been confirmed.
1184             // http://reference.sitepoint.com/javascript/Node/normalize
1185             // https://developer.mozilla.org/En/DOM/Node.normalize
1186             if (typeof n.normalize == 'function') n.normalize();
1187
1188             v = (n && n.firstChild ? n.firstChild.nodeValue : null);
1189             return ((v === null||v === undefined||v==='') ? defaultValue : v);
1190         },
1191
1192         /**
1193          * Selects the value of a node, parsing integers and floats. Returns the defaultValue, or 0 if none is specified.
1194          * @param {String} selector The selector/xpath query
1195          * @param {Node} root (optional) The start of the query (defaults to document).
1196          * @param {Number} defaultValue
1197          * @return {Number}
1198          */
1199         selectNumber : function(path, root, defaultValue){
1200             var v = Ext.DomQuery.selectValue(path, root, defaultValue || 0);
1201             return parseFloat(v);
1202         },
1203
1204         /**
1205          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
1206          * @param {String/HTMLElement/Array} el An element id, element or array of elements
1207          * @param {String} selector The simple selector to test
1208          * @return {Boolean}
1209          */
1210         is : function(el, ss){
1211             if(typeof el == "string"){
1212                 el = document.getElementById(el);
1213             }
1214             var isArray = Ext.isArray(el),
1215                 result = Ext.DomQuery.filter(isArray ? el : [el], ss);
1216             return isArray ? (result.length == el.length) : (result.length > 0);
1217         },
1218
1219         /**
1220          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
1221          * @param {Array} el An array of elements to filter
1222          * @param {String} selector The simple selector to test
1223          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
1224          * the selector instead of the ones that match
1225          * @return {Array} An Array of DOM elements which match the selector. If there are
1226          * no matches, and empty Array is returned.
1227          */
1228         filter : function(els, ss, nonMatches){
1229             ss = ss.replace(trimRe, "");
1230             if(!simpleCache[ss]){
1231                 simpleCache[ss] = Ext.DomQuery.compile(ss, "simple");
1232             }
1233             var result = simpleCache[ss](els);
1234             return nonMatches ? quickDiff(result, els) : result;
1235         },
1236
1237         /**
1238          * Collection of matching regular expressions and code snippets.
1239          * Each capture group within () will be replace the {} in the select
1240          * statement as specified by their index.
1241          */
1242         matchers : [{
1243                 re: /^\.([\w-]+)/,
1244                 select: 'n = byClassName(n, " {1} ");'
1245             }, {
1246                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
1247                 select: 'n = byPseudo(n, "{1}", "{2}");'
1248             },{
1249                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
1250                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
1251             }, {
1252                 re: /^#([\w-]+)/,
1253                 select: 'n = byId(n, "{1}");'
1254             },{
1255                 re: /^@([\w-]+)/,
1256                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
1257             }
1258         ],
1259
1260         /**
1261          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
1262          * 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;.
1263          */
1264         operators : {
1265             "=" : function(a, v){
1266                 return a == v;
1267             },
1268             "!=" : function(a, v){
1269                 return a != v;
1270             },
1271             "^=" : function(a, v){
1272                 return a && a.substr(0, v.length) == v;
1273             },
1274             "$=" : function(a, v){
1275                 return a && a.substr(a.length-v.length) == v;
1276             },
1277             "*=" : function(a, v){
1278                 return a && a.indexOf(v) !== -1;
1279             },
1280             "%=" : function(a, v){
1281                 return (a % v) == 0;
1282             },
1283             "|=" : function(a, v){
1284                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
1285             },
1286             "~=" : function(a, v){
1287                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
1288             }
1289         },
1290
1291         /**
1292 Object hash of "pseudo class" filter functions which are used when filtering selections. 
1293 Each function is passed two parameters:
1294
1295 - **c** : Array
1296     An Array of DOM elements to filter.
1297     
1298 - **v** : String
1299     The argument (if any) supplied in the selector.
1300
1301 A filter function returns an Array of DOM elements which conform to the pseudo class.
1302 In addition to the provided pseudo classes listed above such as `first-child` and `nth-child`,
1303 developers may add additional, custom psuedo class filters to select elements according to application-specific requirements.
1304
1305 For example, to filter `a` elements to only return links to __external__ resources:
1306
1307     Ext.DomQuery.pseudos.external = function(c, v){
1308         var r = [], ri = -1;
1309         for(var i = 0, ci; ci = c[i]; i++){
1310             // Include in result set only if it's a link to an external resource
1311             if(ci.hostname != location.hostname){
1312                 r[++ri] = ci;
1313             }
1314         }
1315         return r;
1316     };
1317
1318 Then external links could be gathered with the following statement:
1319
1320     var externalLinks = Ext.select("a:external");
1321
1322         * @markdown
1323         */
1324         pseudos : {
1325             "first-child" : function(c){
1326                 var r = [], ri = -1, n;
1327                 for(var i = 0, ci; ci = n = c[i]; i++){
1328                     while((n = n.previousSibling) && n.nodeType != 1);
1329                     if(!n){
1330                         r[++ri] = ci;
1331                     }
1332                 }
1333                 return r;
1334             },
1335
1336             "last-child" : function(c){
1337                 var r = [], ri = -1, n;
1338                 for(var i = 0, ci; ci = n = c[i]; i++){
1339                     while((n = n.nextSibling) && n.nodeType != 1);
1340                     if(!n){
1341                         r[++ri] = ci;
1342                     }
1343                 }
1344                 return r;
1345             },
1346
1347             "nth-child" : function(c, a) {
1348                 var r = [], ri = -1,
1349                     m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a),
1350                     f = (m[1] || 1) - 0, l = m[2] - 0;
1351                 for(var i = 0, n; n = c[i]; i++){
1352                     var pn = n.parentNode;
1353                     if (batch != pn._batch) {
1354                         var j = 0;
1355                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
1356                             if(cn.nodeType == 1){
1357                                cn.nodeIndex = ++j;
1358                             }
1359                         }
1360                         pn._batch = batch;
1361                     }
1362                     if (f == 1) {
1363                         if (l == 0 || n.nodeIndex == l){
1364                             r[++ri] = n;
1365                         }
1366                     } else if ((n.nodeIndex + l) % f == 0){
1367                         r[++ri] = n;
1368                     }
1369                 }
1370
1371                 return r;
1372             },
1373
1374             "only-child" : function(c){
1375                 var r = [], ri = -1;;
1376                 for(var i = 0, ci; ci = c[i]; i++){
1377                     if(!prev(ci) && !next(ci)){
1378                         r[++ri] = ci;
1379                     }
1380                 }
1381                 return r;
1382             },
1383
1384             "empty" : function(c){
1385                 var r = [], ri = -1;
1386                 for(var i = 0, ci; ci = c[i]; i++){
1387                     var cns = ci.childNodes, j = 0, cn, empty = true;
1388                     while(cn = cns[j]){
1389                         ++j;
1390                         if(cn.nodeType == 1 || cn.nodeType == 3){
1391                             empty = false;
1392                             break;
1393                         }
1394                     }
1395                     if(empty){
1396                         r[++ri] = ci;
1397                     }
1398                 }
1399                 return r;
1400             },
1401
1402             "contains" : function(c, v){
1403                 var r = [], ri = -1;
1404                 for(var i = 0, ci; ci = c[i]; i++){
1405                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
1406                         r[++ri] = ci;
1407                     }
1408                 }
1409                 return r;
1410             },
1411
1412             "nodeValue" : function(c, v){
1413                 var r = [], ri = -1;
1414                 for(var i = 0, ci; ci = c[i]; i++){
1415                     if(ci.firstChild && ci.firstChild.nodeValue == v){
1416                         r[++ri] = ci;
1417                     }
1418                 }
1419                 return r;
1420             },
1421
1422             "checked" : function(c){
1423                 var r = [], ri = -1;
1424                 for(var i = 0, ci; ci = c[i]; i++){
1425                     if(ci.checked == true){
1426                         r[++ri] = ci;
1427                     }
1428                 }
1429                 return r;
1430             },
1431
1432             "not" : function(c, ss){
1433                 return Ext.DomQuery.filter(c, ss, true);
1434             },
1435
1436             "any" : function(c, selectors){
1437                 var ss = selectors.split('|'),
1438                     r = [], ri = -1, s;
1439                 for(var i = 0, ci; ci = c[i]; i++){
1440                     for(var j = 0; s = ss[j]; j++){
1441                         if(Ext.DomQuery.is(ci, s)){
1442                             r[++ri] = ci;
1443                             break;
1444                         }
1445                     }
1446                 }
1447                 return r;
1448             },
1449
1450             "odd" : function(c){
1451                 return this["nth-child"](c, "odd");
1452             },
1453
1454             "even" : function(c){
1455                 return this["nth-child"](c, "even");
1456             },
1457
1458             "nth" : function(c, a){
1459                 return c[a-1] || [];
1460             },
1461
1462             "first" : function(c){
1463                 return c[0] || [];
1464             },
1465
1466             "last" : function(c){
1467                 return c[c.length-1] || [];
1468             },
1469
1470             "has" : function(c, ss){
1471                 var s = Ext.DomQuery.select,
1472                     r = [], ri = -1;
1473                 for(var i = 0, ci; ci = c[i]; i++){
1474                     if(s(ss, ci).length > 0){
1475                         r[++ri] = ci;
1476                     }
1477                 }
1478                 return r;
1479             },
1480
1481             "next" : function(c, ss){
1482                 var is = Ext.DomQuery.is,
1483                     r = [], ri = -1;
1484                 for(var i = 0, ci; ci = c[i]; i++){
1485                     var n = next(ci);
1486                     if(n && is(n, ss)){
1487                         r[++ri] = ci;
1488                     }
1489                 }
1490                 return r;
1491             },
1492
1493             "prev" : function(c, ss){
1494                 var is = Ext.DomQuery.is,
1495                     r = [], ri = -1;
1496                 for(var i = 0, ci; ci = c[i]; i++){
1497                     var n = prev(ci);
1498                     if(n && is(n, ss)){
1499                         r[++ri] = ci;
1500                     }
1501                 }
1502                 return r;
1503             }
1504         }
1505     };
1506 }();
1507
1508 /**
1509  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Ext.DomQuery#select}
1510  * @param {String} path The selector/xpath query
1511  * @param {Node} root (optional) The start of the query (defaults to document).
1512  * @return {Array}
1513  * @member Ext
1514  * @method query
1515  */
1516 Ext.query = Ext.DomQuery.select;
1517
1518 /**
1519  * @class Ext.core.Element
1520  * <p>Encapsulates a DOM element, adding simple DOM manipulation facilities, normalizing for browser differences.</p>
1521  * <p>All instances of this class inherit the methods of {@link Ext.fx.Anim} making visual effects easily available to all DOM elements.</p>
1522  * <p>Note that the events documented in this class are not Ext events, they encapsulate browser events. To
1523  * access the underlying browser event, see {@link Ext.EventObject#browserEvent}. Some older
1524  * browsers may not support the full range of events. Which events are supported is beyond the control of ExtJs.</p>
1525  * Usage:<br>
1526 <pre><code>
1527 // by id
1528 var el = Ext.get("my-div");
1529
1530 // by DOM element reference
1531 var el = Ext.get(myDivElement);
1532 </code></pre>
1533  * <b>Animations</b><br />
1534  * <p>When an element is manipulated, by default there is no animation.</p>
1535  * <pre><code>
1536 var el = Ext.get("my-div");
1537
1538 // no animation
1539 el.setWidth(100);
1540  * </code></pre>
1541  * <p>Many of the functions for manipulating an element have an optional "animate" parameter.  This
1542  * parameter can be specified as boolean (<tt>true</tt>) for default animation effects.</p>
1543  * <pre><code>
1544 // default animation
1545 el.setWidth(100, true);
1546  * </code></pre>
1547  *
1548  * <p>To configure the effects, an object literal with animation options to use as the Element animation
1549  * configuration object can also be specified. Note that the supported Element animation configuration
1550  * options are a subset of the {@link Ext.fx.Anim} animation options specific to Fx effects.  The supported
1551  * Element animation configuration options are:</p>
1552 <pre>
1553 Option    Default   Description
1554 --------- --------  ---------------------------------------------
1555 {@link Ext.fx.Anim#duration duration}  .35       The duration of the animation in seconds
1556 {@link Ext.fx.Anim#easing easing}    easeOut   The easing method
1557 {@link Ext.fx.Anim#callback callback}  none      A function to execute when the anim completes
1558 {@link Ext.fx.Anim#scope scope}     this      The scope (this) of the callback function
1559 </pre>
1560  *
1561  * <pre><code>
1562 // Element animation options object
1563 var opt = {
1564     {@link Ext.fx.Anim#duration duration}: 1,
1565     {@link Ext.fx.Anim#easing easing}: 'elasticIn',
1566     {@link Ext.fx.Anim#callback callback}: this.foo,
1567     {@link Ext.fx.Anim#scope scope}: this
1568 };
1569 // animation with some options set
1570 el.setWidth(100, opt);
1571  * </code></pre>
1572  * <p>The Element animation object being used for the animation will be set on the options
1573  * object as "anim", which allows you to stop or manipulate the animation. Here is an example:</p>
1574  * <pre><code>
1575 // using the "anim" property to get the Anim object
1576 if(opt.anim.isAnimated()){
1577     opt.anim.stop();
1578 }
1579  * </code></pre>
1580  * <p>Also see the <tt>{@link #animate}</tt> method for another animation technique.</p>
1581  * <p><b> Composite (Collections of) Elements</b></p>
1582  * <p>For working with collections of Elements, see {@link Ext.CompositeElement}</p>
1583  * @constructor Create a new Element directly.
1584  * @param {String/HTMLElement} element
1585  * @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).
1586  */
1587  (function() {
1588     var DOC = document,
1589         EC = Ext.cache;
1590
1591     Ext.Element = Ext.core.Element = function(element, forceNew) {
1592         var dom = typeof element == "string" ? DOC.getElementById(element) : element,
1593         id;
1594
1595         if (!dom) {
1596             return null;
1597         }
1598
1599         id = dom.id;
1600
1601         if (!forceNew && id && EC[id]) {
1602             // element object already exists
1603             return EC[id].el;
1604         }
1605
1606         /**
1607      * The DOM element
1608      * @type HTMLElement
1609      */
1610         this.dom = dom;
1611
1612         /**
1613      * The DOM element ID
1614      * @type String
1615      */
1616         this.id = id || Ext.id(dom);
1617     };
1618
1619     var DH = Ext.core.DomHelper,
1620     El = Ext.core.Element;
1621
1622
1623     El.prototype = {
1624         /**
1625      * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
1626      * @param {Object} o The object with the attributes
1627      * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
1628      * @return {Ext.core.Element} this
1629      */
1630         set: function(o, useSet) {
1631             var el = this.dom,
1632                 attr,
1633                 val;
1634             useSet = (useSet !== false) && !!el.setAttribute;
1635
1636             for (attr in o) {
1637                 if (o.hasOwnProperty(attr)) {
1638                     val = o[attr];
1639                     if (attr == 'style') {
1640                         DH.applyStyles(el, val);
1641                     } else if (attr == 'cls') {
1642                         el.className = val;
1643                     } else if (useSet) {
1644                         el.setAttribute(attr, val);
1645                     } else {
1646                         el[attr] = val;
1647                     }
1648                 }
1649             }
1650             return this;
1651         },
1652
1653         //  Mouse events
1654         /**
1655      * @event click
1656      * Fires when a mouse click is detected within the element.
1657      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1658      * @param {HtmlElement} t The target of the event.
1659      * @param {Object} o The options configuration passed to the {@link #addListener} call.
1660      */
1661         /**
1662      * @event contextmenu
1663      * Fires when a right click is detected within the element.
1664      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1665      * @param {HtmlElement} t The target of the event.
1666      * @param {Object} o The options configuration passed to the {@link #addListener} call.
1667      */
1668         /**
1669      * @event dblclick
1670      * Fires when a mouse double click is detected within the element.
1671      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1672      * @param {HtmlElement} t The target of the event.
1673      * @param {Object} o The options configuration passed to the {@link #addListener} call.
1674      */
1675         /**
1676      * @event mousedown
1677      * Fires when a mousedown is detected within the element.
1678      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1679      * @param {HtmlElement} t The target of the event.
1680      * @param {Object} o The options configuration passed to the {@link #addListener} call.
1681      */
1682         /**
1683      * @event mouseup
1684      * Fires when a mouseup is detected within the element.
1685      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1686      * @param {HtmlElement} t The target of the event.
1687      * @param {Object} o The options configuration passed to the {@link #addListener} call.
1688      */
1689         /**
1690      * @event mouseover
1691      * Fires when a mouseover is detected within the element.
1692      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1693      * @param {HtmlElement} t The target of the event.
1694      * @param {Object} o The options configuration passed to the {@link #addListener} call.
1695      */
1696         /**
1697      * @event mousemove
1698      * Fires when a mousemove is detected with the element.
1699      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1700      * @param {HtmlElement} t The target of the event.
1701      * @param {Object} o The options configuration passed to the {@link #addListener} call.
1702      */
1703         /**
1704      * @event mouseout
1705      * Fires when a mouseout is detected with the element.
1706      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1707      * @param {HtmlElement} t The target of the event.
1708      * @param {Object} o The options configuration passed to the {@link #addListener} call.
1709      */
1710         /**
1711      * @event mouseenter
1712      * Fires when the mouse enters the element.
1713      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1714      * @param {HtmlElement} t The target of the event.
1715      * @param {Object} o The options configuration passed to the {@link #addListener} call.
1716      */
1717         /**
1718      * @event mouseleave
1719      * Fires when the mouse leaves the element.
1720      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1721      * @param {HtmlElement} t The target of the event.
1722      * @param {Object} o The options configuration passed to the {@link #addListener} call.
1723      */
1724
1725         //  Keyboard events
1726         /**
1727      * @event keypress
1728      * Fires when a keypress is detected within the element.
1729      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1730      * @param {HtmlElement} t The target of the event.
1731      * @param {Object} o The options configuration passed to the {@link #addListener} call.
1732      */
1733         /**
1734      * @event keydown
1735      * Fires when a keydown is detected within the element.
1736      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1737      * @param {HtmlElement} t The target of the event.
1738      * @param {Object} o The options configuration passed to the {@link #addListener} call.
1739      */
1740         /**
1741      * @event keyup
1742      * Fires when a keyup is detected within the element.
1743      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1744      * @param {HtmlElement} t The target of the event.
1745      * @param {Object} o The options configuration passed to the {@link #addListener} call.
1746      */
1747
1748
1749         //  HTML frame/object events
1750         /**
1751      * @event load
1752      * Fires when the user agent finishes loading all content within the element. Only supported by window, frames, objects and images.
1753      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1754      * @param {HtmlElement} t The target of the event.
1755      * @param {Object} o The options configuration passed to the {@link #addListener} call.
1756      */
1757         /**
1758      * @event unload
1759      * 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.
1760      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1761      * @param {HtmlElement} t The target of the event.
1762      * @param {Object} o The options configuration passed to the {@link #addListener} call.
1763      */
1764         /**
1765      * @event abort
1766      * Fires when an object/image is stopped from loading before completely loaded.
1767      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1768      * @param {HtmlElement} t The target of the event.
1769      * @param {Object} o The options configuration passed to the {@link #addListener} call.
1770      */
1771         /**
1772      * @event error
1773      * Fires when an object/image/frame cannot be loaded properly.
1774      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1775      * @param {HtmlElement} t The target of the event.
1776      * @param {Object} o The options configuration passed to the {@link #addListener} call.
1777      */
1778         /**
1779      * @event resize
1780      * Fires when a document view is resized.
1781      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1782      * @param {HtmlElement} t The target of the event.
1783      * @param {Object} o The options configuration passed to the {@link #addListener} call.
1784      */
1785         /**
1786      * @event scroll
1787      * Fires when a document view is scrolled.
1788      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1789      * @param {HtmlElement} t The target of the event.
1790      * @param {Object} o The options configuration passed to the {@link #addListener} call.
1791      */
1792
1793         //  Form events
1794         /**
1795      * @event select
1796      * Fires when a user selects some text in a text field, including input and textarea.
1797      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1798      * @param {HtmlElement} t The target of the event.
1799      * @param {Object} o The options configuration passed to the {@link #addListener} call.
1800      */
1801         /**
1802      * @event change
1803      * Fires when a control loses the input focus and its value has been modified since gaining focus.
1804      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1805      * @param {HtmlElement} t The target of the event.
1806      * @param {Object} o The options configuration passed to the {@link #addListener} call.
1807      */
1808         /**
1809      * @event submit
1810      * Fires when a form is submitted.
1811      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1812      * @param {HtmlElement} t The target of the event.
1813      * @param {Object} o The options configuration passed to the {@link #addListener} call.
1814      */
1815         /**
1816      * @event reset
1817      * Fires when a form is reset.
1818      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1819      * @param {HtmlElement} t The target of the event.
1820      * @param {Object} o The options configuration passed to the {@link #addListener} call.
1821      */
1822         /**
1823      * @event focus
1824      * Fires when an element receives focus either via the pointing device or by tab navigation.
1825      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1826      * @param {HtmlElement} t The target of the event.
1827      * @param {Object} o The options configuration passed to the {@link #addListener} call.
1828      */
1829         /**
1830      * @event blur
1831      * Fires when an element loses focus either via the pointing device or by tabbing navigation.
1832      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1833      * @param {HtmlElement} t The target of the event.
1834      * @param {Object} o The options configuration passed to the {@link #addListener} call.
1835      */
1836
1837         //  User Interface events
1838         /**
1839      * @event DOMFocusIn
1840      * Where supported. Similar to HTML focus event, but can be applied to any focusable element.
1841      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1842      * @param {HtmlElement} t The target of the event.
1843      * @param {Object} o The options configuration passed to the {@link #addListener} call.
1844      */
1845         /**
1846      * @event DOMFocusOut
1847      * Where supported. Similar to HTML blur event, but can be applied to any focusable element.
1848      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1849      * @param {HtmlElement} t The target of the event.
1850      * @param {Object} o The options configuration passed to the {@link #addListener} call.
1851      */
1852         /**
1853      * @event DOMActivate
1854      * Where supported. Fires when an element is activated, for instance, through a mouse click or a keypress.
1855      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1856      * @param {HtmlElement} t The target of the event.
1857      * @param {Object} o The options configuration passed to the {@link #addListener} call.
1858      */
1859
1860         //  DOM Mutation events
1861         /**
1862      * @event DOMSubtreeModified
1863      * Where supported. Fires when the subtree is modified.
1864      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1865      * @param {HtmlElement} t The target of the event.
1866      * @param {Object} o The options configuration passed to the {@link #addListener} call.
1867      */
1868         /**
1869      * @event DOMNodeInserted
1870      * Where supported. Fires when a node has been added as a child of another node.
1871      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1872      * @param {HtmlElement} t The target of the event.
1873      * @param {Object} o The options configuration passed to the {@link #addListener} call.
1874      */
1875         /**
1876      * @event DOMNodeRemoved
1877      * Where supported. Fires when a descendant node of the element is removed.
1878      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1879      * @param {HtmlElement} t The target of the event.
1880      * @param {Object} o The options configuration passed to the {@link #addListener} call.
1881      */
1882         /**
1883      * @event DOMNodeRemovedFromDocument
1884      * Where supported. Fires when a node is being removed from a document.
1885      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1886      * @param {HtmlElement} t The target of the event.
1887      * @param {Object} o The options configuration passed to the {@link #addListener} call.
1888      */
1889         /**
1890      * @event DOMNodeInsertedIntoDocument
1891      * Where supported. Fires when a node is being inserted into a document.
1892      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1893      * @param {HtmlElement} t The target of the event.
1894      * @param {Object} o The options configuration passed to the {@link #addListener} call.
1895      */
1896         /**
1897      * @event DOMAttrModified
1898      * Where supported. Fires when an attribute has been modified.
1899      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1900      * @param {HtmlElement} t The target of the event.
1901      * @param {Object} o The options configuration passed to the {@link #addListener} call.
1902      */
1903         /**
1904      * @event DOMCharacterDataModified
1905      * Where supported. Fires when the character data has been modified.
1906      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1907      * @param {HtmlElement} t The target of the event.
1908      * @param {Object} o The options configuration passed to the {@link #addListener} call.
1909      */
1910
1911         /**
1912      * The default unit to append to CSS values where a unit isn't provided (defaults to px).
1913      * @type String
1914      */
1915         defaultUnit: "px",
1916
1917         /**
1918      * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
1919      * @param {String} selector The simple selector to test
1920      * @return {Boolean} True if this element matches the selector, else false
1921      */
1922         is: function(simpleSelector) {
1923             return Ext.DomQuery.is(this.dom, simpleSelector);
1924         },
1925
1926         /**
1927      * Tries to focus the element. Any exceptions are caught and ignored.
1928      * @param {Number} defer (optional) Milliseconds to defer the focus
1929      * @return {Ext.core.Element} this
1930      */
1931         focus: function(defer,
1932                         /* private */
1933                         dom) {
1934             var me = this;
1935             dom = dom || me.dom;
1936             try {
1937                 if (Number(defer)) {
1938                     Ext.defer(me.focus, defer, null, [null, dom]);
1939                 } else {
1940                     dom.focus();
1941                 }
1942             } catch(e) {}
1943             return me;
1944         },
1945
1946         /**
1947      * Tries to blur the element. Any exceptions are caught and ignored.
1948      * @return {Ext.core.Element} this
1949      */
1950         blur: function() {
1951             try {
1952                 this.dom.blur();
1953             } catch(e) {}
1954             return this;
1955         },
1956
1957         /**
1958      * Returns the value of the "value" attribute
1959      * @param {Boolean} asNumber true to parse the value as a number
1960      * @return {String/Number}
1961      */
1962         getValue: function(asNumber) {
1963             var val = this.dom.value;
1964             return asNumber ? parseInt(val, 10) : val;
1965         },
1966
1967         /**
1968      * Appends an event handler to this element.  The shorthand version {@link #on} is equivalent.
1969      * @param {String} eventName The name of event to handle.
1970      * @param {Function} fn The handler function the event invokes. This function is passed
1971      * the following parameters:<ul>
1972      * <li><b>evt</b> : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
1973      * <li><b>el</b> : HtmlElement<div class="sub-desc">The DOM element which was the target of the event.
1974      * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
1975      * <li><b>o</b> : Object<div class="sub-desc">The options object from the addListener call.</div></li>
1976      * </ul>
1977      * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
1978      * <b>If omitted, defaults to this Element.</b>.
1979      * @param {Object} options (optional) An object containing handler configuration properties.
1980      * This may contain any of the following properties:<ul>
1981      * <li><b>scope</b> Object : <div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the handler function is executed.
1982      * <b>If omitted, defaults to this Element.</b></div></li>
1983      * <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>
1984      * <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>
1985      * <li><b>preventDefault</b> Boolean: <div class="sub-desc">True to prevent the default action</div></li>
1986      * <li><b>stopPropagation</b> Boolean: <div class="sub-desc">True to prevent event propagation</div></li>
1987      * <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>
1988      * <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>
1989      * <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>
1990      * <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>
1991      * <li><b>buffer</b> Number: <div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
1992      * by the specified number of milliseconds. If the event fires again within that time, the original
1993      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
1994      * </ul><br>
1995      * <p>
1996      * <b>Combining Options</b><br>
1997      * In the following examples, the shorthand form {@link #on} is used rather than the more verbose
1998      * addListener.  The two are equivalent.  Using the options argument, it is possible to combine different
1999      * types of listeners:<br>
2000      * <br>
2001      * A delayed, one-time listener that auto stops the event and adds a custom argument (forumId) to the
2002      * options object. The options object is available as the third parameter in the handler function.<div style="margin: 5px 20px 20px;">
2003      * Code:<pre><code>
2004 el.on('click', this.onClick, this, {
2005     single: true,
2006     delay: 100,
2007     stopEvent : true,
2008     forumId: 4
2009 });</code></pre></p>
2010      * <p>
2011      * <b>Attaching multiple handlers in 1 call</b><br>
2012      * The method also allows for a single argument to be passed which is a config object containing properties
2013      * which specify multiple handlers.</p>
2014      * <p>
2015      * Code:<pre><code>
2016 el.on({
2017     'click' : {
2018         fn: this.onClick,
2019         scope: this,
2020         delay: 100
2021     },
2022     'mouseover' : {
2023         fn: this.onMouseOver,
2024         scope: this
2025     },
2026     'mouseout' : {
2027         fn: this.onMouseOut,
2028         scope: this
2029     }
2030 });</code></pre>
2031      * <p>
2032      * Or a shorthand syntax:<br>
2033      * Code:<pre><code></p>
2034 el.on({
2035     'click' : this.onClick,
2036     'mouseover' : this.onMouseOver,
2037     'mouseout' : this.onMouseOut,
2038     scope: this
2039 });
2040      * </code></pre></p>
2041      * <p><b>delegate</b></p>
2042      * <p>This is a configuration option that you can pass along when registering a handler for
2043      * an event to assist with event delegation. Event delegation is a technique that is used to
2044      * reduce memory consumption and prevent exposure to memory-leaks. By registering an event
2045      * for a container element as opposed to each element within a container. By setting this
2046      * configuration option to a simple selector, the target element will be filtered to look for
2047      * a descendant of the target.
2048      * For example:<pre><code>
2049 // using this markup:
2050 &lt;div id='elId'>
2051     &lt;p id='p1'>paragraph one&lt;/p>
2052     &lt;p id='p2' class='clickable'>paragraph two&lt;/p>
2053     &lt;p id='p3'>paragraph three&lt;/p>
2054 &lt;/div>
2055 // utilize event delegation to registering just one handler on the container element:
2056 el = Ext.get('elId');
2057 el.on(
2058     'click',
2059     function(e,t) {
2060         // handle click
2061         console.info(t.id); // 'p2'
2062     },
2063     this,
2064     {
2065         // filter the target element to be a descendant with the class 'clickable'
2066         delegate: '.clickable'
2067     }
2068 );
2069      * </code></pre></p>
2070      * @return {Ext.core.Element} this
2071      */
2072         addListener: function(eventName, fn, scope, options) {
2073             Ext.EventManager.on(this.dom, eventName, fn, scope || this, options);
2074             return this;
2075         },
2076
2077         /**
2078      * Removes an event handler from this element.  The shorthand version {@link #un} is equivalent.
2079      * <b>Note</b>: if a <i>scope</i> was explicitly specified when {@link #addListener adding} the
2080      * listener, the same scope must be specified here.
2081      * Example:
2082      * <pre><code>
2083 el.removeListener('click', this.handlerFn);
2084 // or
2085 el.un('click', this.handlerFn);
2086 </code></pre>
2087      * @param {String} eventName The name of the event from which to remove the handler.
2088      * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
2089      * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
2090      * then this must refer to the same object.
2091      * @return {Ext.core.Element} this
2092      */
2093         removeListener: function(eventName, fn, scope) {
2094             Ext.EventManager.un(this.dom, eventName, fn, scope || this);
2095             return this;
2096         },
2097
2098         /**
2099      * Removes all previous added listeners from this element
2100      * @return {Ext.core.Element} this
2101      */
2102         removeAllListeners: function() {
2103             Ext.EventManager.removeAll(this.dom);
2104             return this;
2105         },
2106
2107         /**
2108          * Recursively removes all previous added listeners from this element and its children
2109          * @return {Ext.core.Element} this
2110          */
2111         purgeAllListeners: function() {
2112             Ext.EventManager.purgeElement(this);
2113             return this;
2114         },
2115
2116         /**
2117          * @private Test if size has a unit, otherwise appends the passed unit string, or the default for this Element.
2118          * @param size {Mixed} The size to set
2119          * @param units {String} The units to append to a numeric size value
2120          */
2121         addUnits: function(size, units) {
2122
2123             // Most common case first: Size is set to a number
2124             if (Ext.isNumber(size)) {
2125                 return size + (units || this.defaultUnit || 'px');
2126             }
2127
2128             // Size set to a value which means "auto"
2129             if (size === "" || size == "auto" || size === undefined || size === null) {
2130                 return size || '';
2131             }
2132
2133             // Otherwise, warn if it's not a valid CSS measurement
2134             if (!unitPattern.test(size)) {
2135                 //<debug>
2136                 if (Ext.isDefined(Ext.global.console)) {
2137                     Ext.global.console.warn("Warning, size detected as NaN on Element.addUnits.");
2138                 }
2139                 //</debug>
2140                 return size || '';
2141             }
2142             return size;
2143         },
2144
2145         /**
2146          * Tests various css rules/browsers to determine if this element uses a border box
2147          * @return {Boolean}
2148          */
2149         isBorderBox: function() {
2150             return Ext.isBorderBox || noBoxAdjust[(this.dom.tagName || "").toLowerCase()];
2151         },
2152
2153         /**
2154          * <p>Removes this element's dom reference.  Note that event and cache removal is handled at {@link Ext#removeNode Ext.removeNode}</p>
2155          */
2156         remove: function() {
2157             var me = this,
2158             dom = me.dom;
2159
2160             if (dom) {
2161                 delete me.dom;
2162                 Ext.removeNode(dom);
2163             }
2164         },
2165
2166         /**
2167          * Sets up event handlers to call the passed functions when the mouse is moved into and out of the Element.
2168          * @param {Function} overFn The function to call when the mouse enters the Element.
2169          * @param {Function} outFn The function to call when the mouse leaves the Element.
2170          * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the functions are executed. Defaults to the Element's DOM element.
2171          * @param {Object} options (optional) Options for the listener. See {@link Ext.util.Observable#addListener the <tt>options</tt> parameter}.
2172          * @return {Ext.core.Element} this
2173          */
2174         hover: function(overFn, outFn, scope, options) {
2175             var me = this;
2176             me.on('mouseenter', overFn, scope || me.dom, options);
2177             me.on('mouseleave', outFn, scope || me.dom, options);
2178             return me;
2179         },
2180
2181         /**
2182          * Returns true if this element is an ancestor of the passed element
2183          * @param {HTMLElement/String} el The element to check
2184          * @return {Boolean} True if this element is an ancestor of el, else false
2185          */
2186         contains: function(el) {
2187             return ! el ? false: Ext.core.Element.isAncestor(this.dom, el.dom ? el.dom: el);
2188         },
2189
2190         /**
2191          * Returns the value of a namespaced attribute from the element's underlying DOM node.
2192          * @param {String} namespace The namespace in which to look for the attribute
2193          * @param {String} name The attribute name
2194          * @return {String} The attribute value
2195          * @deprecated
2196          */
2197         getAttributeNS: function(ns, name) {
2198             return this.getAttribute(name, ns);
2199         },
2200
2201         /**
2202          * Returns the value of an attribute from the element's underlying DOM node.
2203          * @param {String} name The attribute name
2204          * @param {String} namespace (optional) The namespace in which to look for the attribute
2205          * @return {String} The attribute value
2206          * @method
2207          */
2208         getAttribute: (Ext.isIE && !(Ext.isIE9 && document.documentMode === 9)) ?
2209         function(name, ns) {
2210             var d = this.dom,
2211             type;
2212             if(ns) {
2213                 type = typeof d[ns + ":" + name];
2214                 if (type != 'undefined' && type != 'unknown') {
2215                     return d[ns + ":" + name] || null;
2216                 }
2217                 return null;
2218             }
2219             if (name === "for") {
2220                 name = "htmlFor";
2221             }
2222             return d[name] || null;
2223         }: function(name, ns) {
2224             var d = this.dom;
2225             if (ns) {
2226                return d.getAttributeNS(ns, name) || d.getAttribute(ns + ":" + name);
2227             }
2228             return  d.getAttribute(name) || d[name] || null;
2229         },
2230
2231         /**
2232          * Update the innerHTML of this element
2233          * @param {String} html The new HTML
2234          * @return {Ext.core.Element} this
2235          */
2236         update: function(html) {
2237             if (this.dom) {
2238                 this.dom.innerHTML = html;
2239             }
2240             return this;
2241         }
2242     };
2243
2244     var ep = El.prototype;
2245
2246     El.addMethods = function(o) {
2247         Ext.apply(ep, o);
2248     };
2249
2250     /**
2251      * Appends an event handler (shorthand for {@link #addListener}).
2252      * @param {String} eventName The name of event to handle.
2253      * @param {Function} fn The handler function the event invokes.
2254      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function is executed.
2255      * @param {Object} options (optional) An object containing standard {@link #addListener} options
2256      * @member Ext.core.Element
2257      * @method on
2258      */
2259     ep.on = ep.addListener;
2260
2261     /**
2262      * Removes an event handler from this element (see {@link #removeListener} for additional notes).
2263      * @param {String} eventName The name of the event from which to remove the handler.
2264      * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
2265      * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
2266      * then this must refer to the same object.
2267      * @return {Ext.core.Element} this
2268      * @member Ext.core.Element
2269      * @method un
2270      */
2271     ep.un = ep.removeListener;
2272
2273     /**
2274      * Removes all previous added listeners from this element
2275      * @return {Ext.core.Element} this
2276      * @member Ext.core.Element
2277      * @method clearListeners
2278      */
2279     ep.clearListeners = ep.removeAllListeners;
2280
2281     /**
2282      * Removes this element's dom reference.  Note that event and cache removal is handled at {@link Ext#removeNode Ext.removeNode}.
2283      * Alias to {@link #remove}.
2284      * @member Ext.core.Element
2285      * @method destroy
2286      */
2287     ep.destroy = ep.remove;
2288
2289     /**
2290      * true to automatically adjust width and height settings for box-model issues (default to true)
2291      */
2292     ep.autoBoxAdjust = true;
2293
2294     // private
2295     var unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
2296     docEl;
2297
2298     /**
2299      * Retrieves Ext.core.Element objects.
2300      * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method
2301      * retrieves Ext.core.Element objects which encapsulate DOM elements. To retrieve a Component by
2302      * its ID, use {@link Ext.ComponentManager#get}.</p>
2303      * <p>Uses simple caching to consistently return the same object. Automatically fixes if an
2304      * object was recreated with the same id via AJAX or DOM.</p>
2305      * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
2306      * @return {Element} The Element object (or null if no matching element was found)
2307      * @static
2308      * @member Ext.core.Element
2309      * @method get
2310      */
2311     El.get = function(el) {
2312         var ex,
2313         elm,
2314         id;
2315         if (!el) {
2316             return null;
2317         }
2318         if (typeof el == "string") {
2319             // element id
2320             if (! (elm = DOC.getElementById(el))) {
2321                 return null;
2322             }
2323             if (EC[el] && EC[el].el) {
2324                 ex = EC[el].el;
2325                 ex.dom = elm;
2326             } else {
2327                 ex = El.addToCache(new El(elm));
2328             }
2329             return ex;
2330         } else if (el.tagName) {
2331             // dom element
2332             if (! (id = el.id)) {
2333                 id = Ext.id(el);
2334             }
2335             if (EC[id] && EC[id].el) {
2336                 ex = EC[id].el;
2337                 ex.dom = el;
2338             } else {
2339                 ex = El.addToCache(new El(el));
2340             }
2341             return ex;
2342         } else if (el instanceof El) {
2343             if (el != docEl) {
2344                 // refresh dom element in case no longer valid,
2345                 // catch case where it hasn't been appended
2346                 // If an el instance is passed, don't pass to getElementById without some kind of id
2347                 if (Ext.isIE && (el.id == undefined || el.id == '')) {
2348                     el.dom = el.dom;
2349                 } else {
2350                     el.dom = DOC.getElementById(el.id) || el.dom;
2351                 }
2352             }
2353             return el;
2354         } else if (el.isComposite) {
2355             return el;
2356         } else if (Ext.isArray(el)) {
2357             return El.select(el);
2358         } else if (el == DOC) {
2359             // create a bogus element object representing the document object
2360             if (!docEl) {
2361                 var f = function() {};
2362                 f.prototype = El.prototype;
2363                 docEl = new f();
2364                 docEl.dom = DOC;
2365             }
2366             return docEl;
2367         }
2368         return null;
2369     };
2370
2371     El.addToCache = function(el, id) {
2372         if (el) {
2373             id = id || el.id;
2374             EC[id] = {
2375                 el: el,
2376                 data: {},
2377                 events: {}
2378             };
2379         }
2380         return el;
2381     };
2382
2383     // private method for getting and setting element data
2384     El.data = function(el, key, value) {
2385         el = El.get(el);
2386         if (!el) {
2387             return null;
2388         }
2389         var c = EC[el.id].data;
2390         if (arguments.length == 2) {
2391             return c[key];
2392         } else {
2393             return (c[key] = value);
2394         }
2395     };
2396
2397     // private
2398     // Garbage collection - uncache elements/purge listeners on orphaned elements
2399     // so we don't hold a reference and cause the browser to retain them
2400     function garbageCollect() {
2401         if (!Ext.enableGarbageCollector) {
2402             clearInterval(El.collectorThreadId);
2403         } else {
2404             var eid,
2405             el,
2406             d,
2407             o;
2408
2409             for (eid in EC) {
2410                 if (!EC.hasOwnProperty(eid)) {
2411                     continue;
2412                 }
2413                 o = EC[eid];
2414                 if (o.skipGarbageCollection) {
2415                     continue;
2416                 }
2417                 el = o.el;
2418                 d = el.dom;
2419                 // -------------------------------------------------------
2420                 // Determining what is garbage:
2421                 // -------------------------------------------------------
2422                 // !d
2423                 // dom node is null, definitely garbage
2424                 // -------------------------------------------------------
2425                 // !d.parentNode
2426                 // no parentNode == direct orphan, definitely garbage
2427                 // -------------------------------------------------------
2428                 // !d.offsetParent && !document.getElementById(eid)
2429                 // display none elements have no offsetParent so we will
2430                 // also try to look it up by it's id. However, check
2431                 // offsetParent first so we don't do unneeded lookups.
2432                 // This enables collection of elements that are not orphans
2433                 // directly, but somewhere up the line they have an orphan
2434                 // parent.
2435                 // -------------------------------------------------------
2436                 if (!d || !d.parentNode || (!d.offsetParent && !DOC.getElementById(eid))) {
2437                     if (d && Ext.enableListenerCollection) {
2438                         Ext.EventManager.removeAll(d);
2439                     }
2440                     delete EC[eid];
2441                 }
2442             }
2443             // Cleanup IE Object leaks
2444             if (Ext.isIE) {
2445                 var t = {};
2446                 for (eid in EC) {
2447                     if (!EC.hasOwnProperty(eid)) {
2448                         continue;
2449                     }
2450                     t[eid] = EC[eid];
2451                 }
2452                 EC = Ext.cache = t;
2453             }
2454         }
2455     }
2456     El.collectorThreadId = setInterval(garbageCollect, 30000);
2457
2458     var flyFn = function() {};
2459     flyFn.prototype = El.prototype;
2460
2461     // dom is optional
2462     El.Flyweight = function(dom) {
2463         this.dom = dom;
2464     };
2465
2466     El.Flyweight.prototype = new flyFn();
2467     El.Flyweight.prototype.isFlyweight = true;
2468     El._flyweights = {};
2469
2470     /**
2471      * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
2472      * the dom node can be overwritten by other code. Shorthand of {@link Ext.core.Element#fly}</p>
2473      * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by
2474      * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get Ext.get}
2475      * will be more appropriate to take advantage of the caching provided by the Ext.core.Element class.</p>
2476      * @param {String/HTMLElement} el The dom node or id
2477      * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts
2478      * (e.g. internally Ext uses "_global")
2479      * @return {Element} The shared Element object (or null if no matching element was found)
2480      * @member Ext.core.Element
2481      * @method fly
2482      */
2483     El.fly = function(el, named) {
2484         var ret = null;
2485         named = named || '_global';
2486         el = Ext.getDom(el);
2487         if (el) {
2488             (El._flyweights[named] = El._flyweights[named] || new El.Flyweight()).dom = el;
2489             ret = El._flyweights[named];
2490         }
2491         return ret;
2492     };
2493
2494     /**
2495      * Retrieves Ext.core.Element objects.
2496      * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method
2497      * retrieves Ext.core.Element objects which encapsulate DOM elements. To retrieve a Component by
2498      * its ID, use {@link Ext.ComponentManager#get}.</p>
2499      * <p>Uses simple caching to consistently return the same object. Automatically fixes if an
2500      * object was recreated with the same id via AJAX or DOM.</p>
2501      * Shorthand of {@link Ext.core.Element#get}
2502      * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
2503      * @return {Element} The Element object (or null if no matching element was found)
2504      * @member Ext
2505      * @method get
2506      */
2507     Ext.get = El.get;
2508
2509     /**
2510      * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
2511      * the dom node can be overwritten by other code. Shorthand of {@link Ext.core.Element#fly}</p>
2512      * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by
2513      * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get Ext.get}
2514      * will be more appropriate to take advantage of the caching provided by the Ext.core.Element class.</p>
2515      * @param {String/HTMLElement} el The dom node or id
2516      * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts
2517      * (e.g. internally Ext uses "_global")
2518      * @return {Element} The shared Element object (or null if no matching element was found)
2519      * @member Ext
2520      * @method fly
2521      */
2522     Ext.fly = El.fly;
2523
2524     // speedy lookup for elements never to box adjust
2525     var noBoxAdjust = Ext.isStrict ? {
2526         select: 1
2527     }: {
2528         input: 1,
2529         select: 1,
2530         textarea: 1
2531     };
2532     if (Ext.isIE || Ext.isGecko) {
2533         noBoxAdjust['button'] = 1;
2534     }
2535 })();
2536
2537 /**
2538  * @class Ext.core.Element
2539  */
2540 Ext.core.Element.addMethods({
2541     /**
2542      * 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)
2543      * @param {String} selector The simple selector to test
2544      * @param {Number/Mixed} maxDepth (optional) The max depth to search as a number or element (defaults to 50 || document.body)
2545      * @param {Boolean} returnEl (optional) True to return a Ext.core.Element object instead of DOM node
2546      * @return {HTMLElement} The matching DOM node (or null if no match was found)
2547      */
2548     findParent : function(simpleSelector, maxDepth, returnEl) {
2549         var p = this.dom,
2550             b = document.body,
2551             depth = 0,
2552             stopEl;
2553
2554         maxDepth = maxDepth || 50;
2555         if (isNaN(maxDepth)) {
2556             stopEl = Ext.getDom(maxDepth);
2557             maxDepth = Number.MAX_VALUE;
2558         }
2559         while (p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl) {
2560             if (Ext.DomQuery.is(p, simpleSelector)) {
2561                 return returnEl ? Ext.get(p) : p;
2562             }
2563             depth++;
2564             p = p.parentNode;
2565         }
2566         return null;
2567     },
2568     
2569     /**
2570      * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
2571      * @param {String} selector The simple selector to test
2572      * @param {Number/Mixed} maxDepth (optional) The max depth to
2573             search as a number or element (defaults to 10 || document.body)
2574      * @param {Boolean} returnEl (optional) True to return a Ext.core.Element object instead of DOM node
2575      * @return {HTMLElement} The matching DOM node (or null if no match was found)
2576      */
2577     findParentNode : function(simpleSelector, maxDepth, returnEl) {
2578         var p = Ext.fly(this.dom.parentNode, '_internal');
2579         return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
2580     },
2581
2582     /**
2583      * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
2584      * This is a shortcut for findParentNode() that always returns an Ext.core.Element.
2585      * @param {String} selector The simple selector to test
2586      * @param {Number/Mixed} maxDepth (optional) The max depth to
2587             search as a number or element (defaults to 10 || document.body)
2588      * @return {Ext.core.Element} The matching DOM node (or null if no match was found)
2589      */
2590     up : function(simpleSelector, maxDepth) {
2591         return this.findParentNode(simpleSelector, maxDepth, true);
2592     },
2593
2594     /**
2595      * Creates a {@link Ext.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
2596      * @param {String} selector The CSS selector
2597      * @return {CompositeElement/CompositeElement} The composite element
2598      */
2599     select : function(selector) {
2600         return Ext.core.Element.select(selector, false,  this.dom);
2601     },
2602
2603     /**
2604      * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
2605      * @param {String} selector The CSS selector
2606      * @return {Array} An array of the matched nodes
2607      */
2608     query : function(selector) {
2609         return Ext.DomQuery.select(selector, this.dom);
2610     },
2611
2612     /**
2613      * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
2614      * @param {String} selector The CSS selector
2615      * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.core.Element (defaults to false)
2616      * @return {HTMLElement/Ext.core.Element} The child Ext.core.Element (or DOM node if returnDom = true)
2617      */
2618     down : function(selector, returnDom) {
2619         var n = Ext.DomQuery.selectNode(selector, this.dom);
2620         return returnDom ? n : Ext.get(n);
2621     },
2622
2623     /**
2624      * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
2625      * @param {String} selector The CSS selector
2626      * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.core.Element (defaults to false)
2627      * @return {HTMLElement/Ext.core.Element} The child Ext.core.Element (or DOM node if returnDom = true)
2628      */
2629     child : function(selector, returnDom) {
2630         var node,
2631             me = this,
2632             id;
2633         id = Ext.get(me).id;
2634         // Escape . or :
2635         id = id.replace(/[\.:]/g, "\\$0");
2636         node = Ext.DomQuery.selectNode('#' + id + " > " + selector, me.dom);
2637         return returnDom ? node : Ext.get(node);
2638     },
2639
2640      /**
2641      * Gets the parent node for this element, optionally chaining up trying to match a selector
2642      * @param {String} selector (optional) Find a parent node that matches the passed simple selector
2643      * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.core.Element
2644      * @return {Ext.core.Element/HTMLElement} The parent node or null
2645      */
2646     parent : function(selector, returnDom) {
2647         return this.matchNode('parentNode', 'parentNode', selector, returnDom);
2648     },
2649
2650      /**
2651      * Gets the next sibling, skipping text nodes
2652      * @param {String} selector (optional) Find the next sibling that matches the passed simple selector
2653      * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.core.Element
2654      * @return {Ext.core.Element/HTMLElement} The next sibling or null
2655      */
2656     next : function(selector, returnDom) {
2657         return this.matchNode('nextSibling', 'nextSibling', selector, returnDom);
2658     },
2659
2660     /**
2661      * Gets the previous sibling, skipping text nodes
2662      * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector
2663      * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.core.Element
2664      * @return {Ext.core.Element/HTMLElement} The previous sibling or null
2665      */
2666     prev : function(selector, returnDom) {
2667         return this.matchNode('previousSibling', 'previousSibling', selector, returnDom);
2668     },
2669
2670
2671     /**
2672      * Gets the first child, skipping text nodes
2673      * @param {String} selector (optional) Find the next sibling that matches the passed simple selector
2674      * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.core.Element
2675      * @return {Ext.core.Element/HTMLElement} The first child or null
2676      */
2677     first : function(selector, returnDom) {
2678         return this.matchNode('nextSibling', 'firstChild', selector, returnDom);
2679     },
2680
2681     /**
2682      * Gets the last child, skipping text nodes
2683      * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector
2684      * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.core.Element
2685      * @return {Ext.core.Element/HTMLElement} The last child or null
2686      */
2687     last : function(selector, returnDom) {
2688         return this.matchNode('previousSibling', 'lastChild', selector, returnDom);
2689     },
2690
2691     matchNode : function(dir, start, selector, returnDom) {
2692         if (!this.dom) {
2693             return null;
2694         }
2695         
2696         var n = this.dom[start];
2697         while (n) {
2698             if (n.nodeType == 1 && (!selector || Ext.DomQuery.is(n, selector))) {
2699                 return !returnDom ? Ext.get(n) : n;
2700             }
2701             n = n[dir];
2702         }
2703         return null;
2704     }
2705 });
2706
2707 /**
2708  * @class Ext.core.Element
2709  */
2710 Ext.core.Element.addMethods({
2711     /**
2712      * Appends the passed element(s) to this element
2713      * @param {String/HTMLElement/Array/Element/CompositeElement} el
2714      * @return {Ext.core.Element} this
2715      */
2716     appendChild : function(el) {
2717         return Ext.get(el).appendTo(this);
2718     },
2719
2720     /**
2721      * Appends this element to the passed element
2722      * @param {Mixed} el The new parent element
2723      * @return {Ext.core.Element} this
2724      */
2725     appendTo : function(el) {
2726         Ext.getDom(el).appendChild(this.dom);
2727         return this;
2728     },
2729
2730     /**
2731      * Inserts this element before the passed element in the DOM
2732      * @param {Mixed} el The element before which this element will be inserted
2733      * @return {Ext.core.Element} this
2734      */
2735     insertBefore : function(el) {
2736         el = Ext.getDom(el);
2737         el.parentNode.insertBefore(this.dom, el);
2738         return this;
2739     },
2740
2741     /**
2742      * Inserts this element after the passed element in the DOM
2743      * @param {Mixed} el The element to insert after
2744      * @return {Ext.core.Element} this
2745      */
2746     insertAfter : function(el) {
2747         el = Ext.getDom(el);
2748         el.parentNode.insertBefore(this.dom, el.nextSibling);
2749         return this;
2750     },
2751
2752     /**
2753      * Inserts (or creates) an element (or DomHelper config) as the first child of this element
2754      * @param {Mixed/Object} el The id or element to insert or a DomHelper config to create and insert
2755      * @return {Ext.core.Element} The new child
2756      */
2757     insertFirst : function(el, returnDom) {
2758         el = el || {};
2759         if (el.nodeType || el.dom || typeof el == 'string') { // element
2760             el = Ext.getDom(el);
2761             this.dom.insertBefore(el, this.dom.firstChild);
2762             return !returnDom ? Ext.get(el) : el;
2763         }
2764         else { // dh config
2765             return this.createChild(el, this.dom.firstChild, returnDom);
2766         }
2767     },
2768
2769     /**
2770      * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
2771      * @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.
2772      * @param {String} where (optional) 'before' or 'after' defaults to before
2773      * @param {Boolean} returnDom (optional) True to return the .;ll;l,raw DOM element instead of Ext.core.Element
2774      * @return {Ext.core.Element} The inserted Element. If an array is passed, the last inserted element is returned.
2775      */
2776     insertSibling: function(el, where, returnDom){
2777         var me = this, rt,
2778         isAfter = (where || 'before').toLowerCase() == 'after',
2779         insertEl;
2780
2781         if(Ext.isArray(el)){
2782             insertEl = me;
2783             Ext.each(el, function(e) {
2784                 rt = Ext.fly(insertEl, '_internal').insertSibling(e, where, returnDom);
2785                 if(isAfter){
2786                     insertEl = rt;
2787                 }
2788             });
2789             return rt;
2790         }
2791
2792         el = el || {};
2793
2794         if(el.nodeType || el.dom){
2795             rt = me.dom.parentNode.insertBefore(Ext.getDom(el), isAfter ? me.dom.nextSibling : me.dom);
2796             if (!returnDom) {
2797                 rt = Ext.get(rt);
2798             }
2799         }else{
2800             if (isAfter && !me.dom.nextSibling) {
2801                 rt = Ext.core.DomHelper.append(me.dom.parentNode, el, !returnDom);
2802             } else {
2803                 rt = Ext.core.DomHelper[isAfter ? 'insertAfter' : 'insertBefore'](me.dom, el, !returnDom);
2804             }
2805         }
2806         return rt;
2807     },
2808
2809     /**
2810      * Replaces the passed element with this element
2811      * @param {Mixed} el The element to replace
2812      * @return {Ext.core.Element} this
2813      */
2814     replace : function(el) {
2815         el = Ext.get(el);
2816         this.insertBefore(el);
2817         el.remove();
2818         return this;
2819     },
2820     
2821     /**
2822      * Replaces this element with the passed element
2823      * @param {Mixed/Object} el The new element or a DomHelper config of an element to create
2824      * @return {Ext.core.Element} this
2825      */
2826     replaceWith: function(el){
2827         var me = this;
2828             
2829         if(el.nodeType || el.dom || typeof el == 'string'){
2830             el = Ext.get(el);
2831             me.dom.parentNode.insertBefore(el, me.dom);
2832         }else{
2833             el = Ext.core.DomHelper.insertBefore(me.dom, el);
2834         }
2835         
2836         delete Ext.cache[me.id];
2837         Ext.removeNode(me.dom);      
2838         me.id = Ext.id(me.dom = el);
2839         Ext.core.Element.addToCache(me.isFlyweight ? new Ext.core.Element(me.dom) : me);     
2840         return me;
2841     },
2842     
2843     /**
2844      * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
2845      * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
2846      * automatically generated with the specified attributes.
2847      * @param {HTMLElement} insertBefore (optional) a child element of this element
2848      * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
2849      * @return {Ext.core.Element} The new child element
2850      */
2851     createChild : function(config, insertBefore, returnDom) {
2852         config = config || {tag:'div'};
2853         if (insertBefore) {
2854             return Ext.core.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
2855         }
2856         else {
2857             return Ext.core.DomHelper[!this.dom.firstChild ? 'insertFirst' : 'append'](this.dom, config,  returnDom !== true);
2858         }
2859     },
2860
2861     /**
2862      * Creates and wraps this element with another element
2863      * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
2864      * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.core.Element
2865      * @return {HTMLElement/Element} The newly created wrapper element
2866      */
2867     wrap : function(config, returnDom) {
2868         var newEl = Ext.core.DomHelper.insertBefore(this.dom, config || {tag: "div"}, !returnDom),
2869             d = newEl.dom || newEl;
2870
2871         d.appendChild(this.dom);
2872         return newEl;
2873     },
2874
2875     /**
2876      * Inserts an html fragment into this element
2877      * @param {String} where Where to insert the html in relation to this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
2878      * @param {String} html The HTML fragment
2879      * @param {Boolean} returnEl (optional) True to return an Ext.core.Element (defaults to false)
2880      * @return {HTMLElement/Ext.core.Element} The inserted node (or nearest related if more than 1 inserted)
2881      */
2882     insertHtml : function(where, html, returnEl) {
2883         var el = Ext.core.DomHelper.insertHtml(where, this.dom, html);
2884         return returnEl ? Ext.get(el) : el;
2885     }
2886 });
2887
2888 /**
2889  * @class Ext.core.Element
2890  */
2891 (function(){
2892     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>';
2893     // local style camelizing for speed
2894     var supports = Ext.supports,
2895         view = document.defaultView,
2896         opacityRe = /alpha\(opacity=(.*)\)/i,
2897         trimRe = /^\s+|\s+$/g,
2898         spacesRe = /\s+/,
2899         wordsRe = /\w/g,
2900         adjustDirect2DTableRe = /table-row|table-.*-group/,
2901         INTERNAL = '_internal',
2902         PADDING = 'padding',
2903         MARGIN = 'margin',
2904         BORDER = 'border',
2905         LEFT = '-left',
2906         RIGHT = '-right',
2907         TOP = '-top',
2908         BOTTOM = '-bottom',
2909         WIDTH = '-width',
2910         MATH = Math,
2911         HIDDEN = 'hidden',
2912         ISCLIPPED = 'isClipped',
2913         OVERFLOW = 'overflow',
2914         OVERFLOWX = 'overflow-x',
2915         OVERFLOWY = 'overflow-y',
2916         ORIGINALCLIP = 'originalClip',
2917         // special markup used throughout Ext when box wrapping elements
2918         borders = {l: BORDER + LEFT + WIDTH, r: BORDER + RIGHT + WIDTH, t: BORDER + TOP + WIDTH, b: BORDER + BOTTOM + WIDTH},
2919         paddings = {l: PADDING + LEFT, r: PADDING + RIGHT, t: PADDING + TOP, b: PADDING + BOTTOM},
2920         margins = {l: MARGIN + LEFT, r: MARGIN + RIGHT, t: MARGIN + TOP, b: MARGIN + BOTTOM},
2921         data = Ext.core.Element.data;
2922
2923     Ext.override(Ext.core.Element, {
2924         
2925         /**
2926          * TODO: Look at this
2927          */
2928         // private  ==> used by Fx
2929         adjustWidth : function(width) {
2930             var me = this,
2931                 isNum = (typeof width == 'number');
2932                 
2933             if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
2934                width -= (me.getBorderWidth("lr") + me.getPadding("lr"));
2935             }
2936             return (isNum && width < 0) ? 0 : width;
2937         },
2938
2939         // private   ==> used by Fx
2940         adjustHeight : function(height) {
2941             var me = this,
2942                 isNum = (typeof height == "number");
2943                 
2944             if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
2945                height -= (me.getBorderWidth("tb") + me.getPadding("tb"));
2946             }
2947             return (isNum && height < 0) ? 0 : height;
2948         },
2949
2950
2951         /**
2952          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
2953          * @param {String/Array} className The CSS classes to add separated by space, or an array of classes
2954          * @return {Ext.core.Element} this
2955          */
2956         addCls : function(className){
2957             var me = this,
2958                 cls = [],
2959                 space = ((me.dom.className.replace(trimRe, '') == '') ? "" : " "),
2960                 i, len, v;
2961             if (!Ext.isDefined(className)) {
2962                 return me;
2963             }
2964             // Separate case is for speed
2965             if (!Ext.isArray(className)) {
2966                 if (typeof className === 'string') {
2967                     className = className.replace(trimRe, '').split(spacesRe);
2968                     if (className.length === 1) {
2969                         className = className[0];
2970                         if (!me.hasCls(className)) {
2971                             me.dom.className += space + className;
2972                         }
2973                     } else {
2974                         this.addCls(className);
2975                     }
2976                 }
2977             } else {
2978                 for (i = 0, len = className.length; i < len; i++) {
2979                     v = className[i];
2980                     if (typeof v == 'string' && (' ' + me.dom.className + ' ').indexOf(' ' + v + ' ') == -1) {
2981                         cls.push(v);
2982                     }
2983                 }
2984                 if (cls.length) {
2985                     me.dom.className += space + cls.join(" ");
2986                 }
2987             }
2988             return me;
2989         },
2990
2991         /**
2992          * Removes one or more CSS classes from the element.
2993          * @param {String/Array} className The CSS classes to remove separated by space, or an array of classes
2994          * @return {Ext.core.Element} this
2995          */
2996         removeCls : function(className){
2997             var me = this,
2998                 i, idx, len, cls, elClasses;
2999             if (!Ext.isDefined(className)) {
3000                 return me;
3001             }
3002             if (!Ext.isArray(className)){
3003                 className = className.replace(trimRe, '').split(spacesRe);
3004             }
3005             if (me.dom && me.dom.className) {
3006                 elClasses = me.dom.className.replace(trimRe, '').split(spacesRe);
3007                 for (i = 0, len = className.length; i < len; i++) {
3008                     cls = className[i];
3009                     if (typeof cls == 'string') {
3010                         cls = cls.replace(trimRe, '');
3011                         idx = Ext.Array.indexOf(elClasses, cls);
3012                         if (idx != -1) {
3013                             elClasses.splice(idx, 1);
3014                         }
3015                     }
3016                 }
3017                 me.dom.className = elClasses.join(" ");
3018             }
3019             return me;
3020         },
3021
3022         /**
3023          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
3024          * @param {String/Array} className The CSS class to add, or an array of classes
3025          * @return {Ext.core.Element} this
3026          */
3027         radioCls : function(className){
3028             var cn = this.dom.parentNode.childNodes,
3029                 v, i, len;
3030             className = Ext.isArray(className) ? className : [className];
3031             for (i = 0, len = cn.length; i < len; i++) {
3032                 v = cn[i];
3033                 if (v && v.nodeType == 1) {
3034                     Ext.fly(v, '_internal').removeCls(className);
3035                 }
3036             }
3037             return this.addCls(className);
3038         },
3039
3040         /**
3041          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
3042          * @param {String} className The CSS class to toggle
3043          * @return {Ext.core.Element} this
3044          * @method
3045          */
3046         toggleCls : Ext.supports.ClassList ?
3047             function(className) {
3048                 this.dom.classList.toggle(Ext.String.trim(className));
3049                 return this;
3050             } :
3051             function(className) {
3052                 return this.hasCls(className) ? this.removeCls(className) : this.addCls(className);
3053             },
3054
3055         /**
3056          * Checks if the specified CSS class exists on this element's DOM node.
3057          * @param {String} className The CSS class to check for
3058          * @return {Boolean} True if the class exists, else false
3059          * @method
3060          */
3061         hasCls : Ext.supports.ClassList ?
3062             function(className) {
3063                 if (!className) {
3064                     return false;
3065                 }
3066                 className = className.split(spacesRe);
3067                 var ln = className.length,
3068                     i = 0;
3069                 for (; i < ln; i++) {
3070                     if (className[i] && this.dom.classList.contains(className[i])) {
3071                         return true;
3072                     }
3073                 }
3074                 return false;
3075             } :
3076             function(className){
3077                 return className && (' ' + this.dom.className + ' ').indexOf(' ' + className + ' ') != -1;
3078             },
3079
3080         /**
3081          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
3082          * @param {String} oldClassName The CSS class to replace
3083          * @param {String} newClassName The replacement CSS class
3084          * @return {Ext.core.Element} this
3085          */
3086         replaceCls : function(oldClassName, newClassName){
3087             return this.removeCls(oldClassName).addCls(newClassName);
3088         },
3089
3090         isStyle : function(style, val) {
3091             return this.getStyle(style) == val;
3092         },
3093
3094         /**
3095          * Normalizes currentStyle and computedStyle.
3096          * @param {String} property The style property whose value is returned.
3097          * @return {String} The current value of the style property for this element.
3098          * @method
3099          */
3100         getStyle : function(){
3101             return view && view.getComputedStyle ?
3102                 function(prop){
3103                     var el = this.dom,
3104                         v, cs, out, display, cleaner;
3105
3106                     if(el == document){
3107                         return null;
3108                     }
3109                     prop = Ext.core.Element.normalize(prop);
3110                     out = (v = el.style[prop]) ? v :
3111                            (cs = view.getComputedStyle(el, "")) ? cs[prop] : null;
3112                            
3113                     // Ignore cases when the margin is correctly reported as 0, the bug only shows
3114                     // numbers larger.
3115                     if(prop == 'marginRight' && out != '0px' && !supports.RightMargin){
3116                         cleaner = Ext.core.Element.getRightMarginFixCleaner(el);
3117                         display = this.getStyle('display');
3118                         el.style.display = 'inline-block';
3119                         out = view.getComputedStyle(el, '').marginRight;
3120                         el.style.display = display;
3121                         cleaner();
3122                     }
3123                     
3124                     if(prop == 'backgroundColor' && out == 'rgba(0, 0, 0, 0)' && !supports.TransparentColor){
3125                         out = 'transparent';
3126                     }
3127                     return out;
3128                 } :
3129                 function(prop){
3130                     var el = this.dom,
3131                         m, cs;
3132
3133                     if (el == document) {
3134                         return null;
3135                     }
3136                     
3137                     if (prop == 'opacity') {
3138                         if (el.style.filter.match) {
3139                             m = el.style.filter.match(opacityRe);
3140                             if(m){
3141                                 var fv = parseFloat(m[1]);
3142                                 if(!isNaN(fv)){
3143                                     return fv ? fv / 100 : 0;
3144                                 }
3145                             }
3146                         }
3147                         return 1;
3148                     }
3149                     prop = Ext.core.Element.normalize(prop);
3150                     return el.style[prop] || ((cs = el.currentStyle) ? cs[prop] : null);
3151                 };
3152         }(),
3153
3154         /**
3155          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
3156          * are convert to standard 6 digit hex color.
3157          * @param {String} attr The css attribute
3158          * @param {String} defaultValue The default value to use when a valid color isn't found
3159          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
3160          * color anims.
3161          */
3162         getColor : function(attr, defaultValue, prefix){
3163             var v = this.getStyle(attr),
3164                 color = prefix || prefix === '' ? prefix : '#',
3165                 h;
3166
3167             if(!v || (/transparent|inherit/.test(v))) {
3168                 return defaultValue;
3169             }
3170             if(/^r/.test(v)){
3171                 Ext.each(v.slice(4, v.length -1).split(','), function(s){
3172                     h = parseInt(s, 10);
3173                     color += (h < 16 ? '0' : '') + h.toString(16);
3174                 });
3175             }else{
3176                 v = v.replace('#', '');
3177                 color += v.length == 3 ? v.replace(/^(\w)(\w)(\w)$/, '$1$1$2$2$3$3') : v;
3178             }
3179             return(color.length > 5 ? color.toLowerCase() : defaultValue);
3180         },
3181
3182         /**
3183          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
3184          * @param {String/Object} property The style property to be set, or an object of multiple styles.
3185          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
3186          * @return {Ext.core.Element} this
3187          */
3188         setStyle : function(prop, value){
3189             var me = this,
3190                 tmp, style;
3191
3192             if (!me.dom) {
3193                 return me;
3194             }
3195
3196             if (!Ext.isObject(prop)) {
3197                 tmp = {};
3198                 tmp[prop] = value;
3199                 prop = tmp;
3200             }
3201             for (style in prop) {
3202                 if (prop.hasOwnProperty(style)) {
3203                     value = Ext.value(prop[style], '');
3204                     if (style == 'opacity') {
3205                         me.setOpacity(value);
3206                     }
3207                     else {
3208                         me.dom.style[Ext.core.Element.normalize(style)] = value;
3209                     }
3210                 }
3211             }
3212             return me;
3213         },
3214
3215         /**
3216          * Set the opacity of the element
3217          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
3218          * @param {Boolean/Object} animate (optional) a standard Element animation config object or <tt>true</tt> for
3219          * the default animation (<tt>{duration: .35, easing: 'easeIn'}</tt>)
3220          * @return {Ext.core.Element} this
3221          */
3222         setOpacity: function(opacity, animate) {
3223             var me = this,
3224                 dom = me.dom,
3225                 val,
3226                 style;
3227
3228             if (!me.dom) {
3229                 return me;
3230             }
3231
3232             style = me.dom.style;
3233
3234             if (!animate || !me.anim) {
3235                 if (!Ext.supports.Opacity) {
3236                     opacity = opacity < 1 ? 'alpha(opacity=' + opacity * 100 + ')': '';
3237                     val = style.filter.replace(opacityRe, '').replace(trimRe, '');
3238
3239                     style.zoom = 1;
3240                     style.filter = val + (val.length > 0 ? ' ': '') + opacity;
3241                 }
3242                 else {
3243                     style.opacity = opacity;
3244                 }
3245             }
3246             else {
3247                 if (!Ext.isObject(animate)) {
3248                     animate = {
3249                         duration: 350,
3250                         easing: 'ease-in'
3251                     };
3252                 }
3253                 me.animate(Ext.applyIf({
3254                     to: {
3255                         opacity: opacity
3256                     }
3257                 },
3258                 animate));
3259             }
3260             return me;
3261         },
3262
3263
3264         /**
3265          * Clears any opacity settings from this element. Required in some cases for IE.
3266          * @return {Ext.core.Element} this
3267          */
3268         clearOpacity : function(){
3269             var style = this.dom.style;
3270             if(!Ext.supports.Opacity){
3271                 if(!Ext.isEmpty(style.filter)){
3272                     style.filter = style.filter.replace(opacityRe, '').replace(trimRe, '');
3273                 }
3274             }else{
3275                 style.opacity = style['-moz-opacity'] = style['-khtml-opacity'] = '';
3276             }
3277             return this;
3278         },
3279         
3280         /**
3281          * @private
3282          * Returns 1 if the browser returns the subpixel dimension rounded to the lowest pixel.
3283          * @return {Number} 0 or 1 
3284          */
3285         adjustDirect2DDimension: function(dimension) {
3286             var me = this,
3287                 dom = me.dom,
3288                 display = me.getStyle('display'),
3289                 inlineDisplay = dom.style['display'],
3290                 inlinePosition = dom.style['position'],
3291                 originIndex = dimension === 'width' ? 0 : 1,
3292                 floating;
3293                 
3294             if (display === 'inline') {
3295                 dom.style['display'] = 'inline-block';
3296             }
3297
3298             dom.style['position'] = display.match(adjustDirect2DTableRe) ? 'absolute' : 'static';
3299
3300             // floating will contain digits that appears after the decimal point
3301             // if height or width are set to auto we fallback to msTransformOrigin calculation
3302             floating = (parseFloat(me.getStyle(dimension)) || parseFloat(dom.currentStyle.msTransformOrigin.split(' ')[originIndex]) * 2) % 1;
3303             
3304             dom.style['position'] = inlinePosition;
3305             
3306             if (display === 'inline') {
3307                 dom.style['display'] = inlineDisplay;
3308             }
3309
3310             return floating;
3311         },
3312         
3313         /**
3314          * Returns the offset height of the element
3315          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
3316          * @return {Number} The element's height
3317          */
3318         getHeight: function(contentHeight, preciseHeight) {
3319             var me = this,
3320                 dom = me.dom,
3321                 hidden = Ext.isIE && me.isStyle('display', 'none'),
3322                 height, overflow, style, floating;
3323
3324             // IE Quirks mode acts more like a max-size measurement unless overflow is hidden during measurement.
3325             // We will put the overflow back to it's original value when we are done measuring.
3326             if (Ext.isIEQuirks) {
3327                 style = dom.style;
3328                 overflow = style.overflow;
3329                 me.setStyle({ overflow: 'hidden'});
3330             }
3331
3332             height = dom.offsetHeight;
3333
3334             height = MATH.max(height, hidden ? 0 : dom.clientHeight) || 0;
3335
3336             // IE9 Direct2D dimension rounding bug
3337             if (!hidden && Ext.supports.Direct2DBug) {
3338                 floating = me.adjustDirect2DDimension('height');
3339                 if (preciseHeight) {
3340                     height += floating;
3341                 }
3342                 else if (floating > 0 && floating < 0.5) {
3343                     height++;
3344                 }
3345             }
3346
3347             if (contentHeight) {
3348                 height -= (me.getBorderWidth("tb") + me.getPadding("tb"));
3349             }
3350
3351             if (Ext.isIEQuirks) {
3352                 me.setStyle({ overflow: overflow});
3353             }
3354
3355             if (height < 0) {
3356                 height = 0;
3357             }
3358             return height;
3359         },
3360                 
3361         /**
3362          * Returns the offset width of the element
3363          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
3364          * @return {Number} The element's width
3365          */
3366         getWidth: function(contentWidth, preciseWidth) {
3367             var me = this,
3368                 dom = me.dom,
3369                 hidden = Ext.isIE && me.isStyle('display', 'none'),
3370                 rect, width, overflow, style, floating, parentPosition;
3371
3372             // IE Quirks mode acts more like a max-size measurement unless overflow is hidden during measurement.
3373             // We will put the overflow back to it's original value when we are done measuring.
3374             if (Ext.isIEQuirks) {
3375                 style = dom.style;
3376                 overflow = style.overflow;
3377                 me.setStyle({overflow: 'hidden'});
3378             }
3379             
3380             // Fix Opera 10.5x width calculation issues 
3381             if (Ext.isOpera10_5) {
3382                 if (dom.parentNode.currentStyle.position === 'relative') {
3383                     parentPosition = dom.parentNode.style.position;
3384                     dom.parentNode.style.position = 'static';
3385                     width = dom.offsetWidth;
3386                     dom.parentNode.style.position = parentPosition;
3387                 }
3388                 width = Math.max(width || 0, dom.offsetWidth);
3389             
3390             // Gecko will in some cases report an offsetWidth that is actually less than the width of the
3391             // text contents, because it measures fonts with sub-pixel precision but rounds the calculated
3392             // value down. Using getBoundingClientRect instead of offsetWidth allows us to get the precise
3393             // subpixel measurements so we can force them to always be rounded up. See
3394             // https://bugzilla.mozilla.org/show_bug.cgi?id=458617
3395             } else if (Ext.supports.BoundingClientRect) {
3396                 rect = dom.getBoundingClientRect();
3397                 width = rect.right - rect.left;
3398                 width = preciseWidth ? width : Math.ceil(width);
3399             } else {
3400                 width = dom.offsetWidth;
3401             }
3402
3403             width = MATH.max(width, hidden ? 0 : dom.clientWidth) || 0;
3404
3405             // IE9 Direct2D dimension rounding bug
3406             if (!hidden && Ext.supports.Direct2DBug) {
3407                 floating = me.adjustDirect2DDimension('width');
3408                 if (preciseWidth) {
3409                     width += floating;
3410                 }
3411                 else if (floating > 0 && floating < 0.5) {
3412                     width++;
3413                 }
3414             }
3415             
3416             if (contentWidth) {
3417                 width -= (me.getBorderWidth("lr") + me.getPadding("lr"));
3418             }
3419             
3420             if (Ext.isIEQuirks) {
3421                 me.setStyle({ overflow: overflow});
3422             }
3423
3424             if (width < 0) {
3425                 width = 0;
3426             }
3427             return width;
3428         },
3429
3430         /**
3431          * Set the width of this Element.
3432          * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
3433          * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
3434          * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
3435          * </ul></div>
3436          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
3437          * @return {Ext.core.Element} this
3438          */
3439         setWidth : function(width, animate){
3440             var me = this;
3441             width = me.adjustWidth(width);
3442             if (!animate || !me.anim) {
3443                 me.dom.style.width = me.addUnits(width);
3444             }
3445             else {
3446                 if (!Ext.isObject(animate)) {
3447                     animate = {};
3448                 }
3449                 me.animate(Ext.applyIf({
3450                     to: {
3451                         width: width
3452                     }
3453                 }, animate));
3454             }
3455             return me;
3456         },
3457
3458         /**
3459          * Set the height of this Element.
3460          * <pre><code>
3461 // change the height to 200px and animate with default configuration
3462 Ext.fly('elementId').setHeight(200, true);
3463
3464 // change the height to 150px and animate with a custom configuration
3465 Ext.fly('elId').setHeight(150, {
3466     duration : .5, // animation will have a duration of .5 seconds
3467     // will change the content to "finished"
3468     callback: function(){ this.{@link #update}("finished"); }
3469 });
3470          * </code></pre>
3471          * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
3472          * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels.)</li>
3473          * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
3474          * </ul></div>
3475          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
3476          * @return {Ext.core.Element} this
3477          */
3478          setHeight : function(height, animate){
3479             var me = this;
3480             height = me.adjustHeight(height);
3481             if (!animate || !me.anim) {
3482                 me.dom.style.height = me.addUnits(height);
3483             }
3484             else {
3485                 if (!Ext.isObject(animate)) {
3486                     animate = {};
3487                 }
3488                 me.animate(Ext.applyIf({
3489                     to: {
3490                         height: height
3491                     }
3492                 }, animate));
3493             }
3494             return me;
3495         },
3496
3497         /**
3498          * Gets the width of the border(s) for the specified side(s)
3499          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
3500          * passing <tt>'lr'</tt> would get the border <b><u>l</u></b>eft width + the border <b><u>r</u></b>ight width.
3501          * @return {Number} The width of the sides passed added together
3502          */
3503         getBorderWidth : function(side){
3504             return this.addStyles(side, borders);
3505         },
3506
3507         /**
3508          * Gets the width of the padding(s) for the specified side(s)
3509          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
3510          * passing <tt>'lr'</tt> would get the padding <b><u>l</u></b>eft + the padding <b><u>r</u></b>ight.
3511          * @return {Number} The padding of the sides passed added together
3512          */
3513         getPadding : function(side){
3514             return this.addStyles(side, paddings);
3515         },
3516
3517         /**
3518          *  Store the current overflow setting and clip overflow on the element - use <tt>{@link #unclip}</tt> to remove
3519          * @return {Ext.core.Element} this
3520          */
3521         clip : function(){
3522             var me = this,
3523                 dom = me.dom;
3524
3525             if(!data(dom, ISCLIPPED)){
3526                 data(dom, ISCLIPPED, true);
3527                 data(dom, ORIGINALCLIP, {
3528                     o: me.getStyle(OVERFLOW),
3529                     x: me.getStyle(OVERFLOWX),
3530                     y: me.getStyle(OVERFLOWY)
3531                 });
3532                 me.setStyle(OVERFLOW, HIDDEN);
3533                 me.setStyle(OVERFLOWX, HIDDEN);
3534                 me.setStyle(OVERFLOWY, HIDDEN);
3535             }
3536             return me;
3537         },
3538
3539         /**
3540          *  Return clipping (overflow) to original clipping before <tt>{@link #clip}</tt> was called
3541          * @return {Ext.core.Element} this
3542          */
3543         unclip : function(){
3544             var me = this,
3545                 dom = me.dom,
3546                 clip;
3547
3548             if(data(dom, ISCLIPPED)){
3549                 data(dom, ISCLIPPED, false);
3550                 clip = data(dom, ORIGINALCLIP);
3551                 if(o.o){
3552                     me.setStyle(OVERFLOW, o.o);
3553                 }
3554                 if(o.x){
3555                     me.setStyle(OVERFLOWX, o.x);
3556                 }
3557                 if(o.y){
3558                     me.setStyle(OVERFLOWY, o.y);
3559                 }
3560             }
3561             return me;
3562         },
3563
3564         // private
3565         addStyles : function(sides, styles){
3566             var totalSize = 0,
3567                 sidesArr = sides.match(wordsRe),
3568                 i = 0,
3569                 len = sidesArr.length,
3570                 side, size;
3571             for (; i < len; i++) {
3572                 side = sidesArr[i];
3573                 size = side && parseInt(this.getStyle(styles[side]), 10);
3574                 if (size) {
3575                     totalSize += MATH.abs(size);
3576                 }
3577             }
3578             return totalSize;
3579         },
3580
3581         margins : margins,
3582         
3583         /**
3584          * More flexible version of {@link #setStyle} for setting style properties.
3585          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
3586          * a function which returns such a specification.
3587          * @return {Ext.core.Element} this
3588          */
3589         applyStyles : function(style){
3590             Ext.core.DomHelper.applyStyles(this.dom, style);
3591             return this;
3592         },
3593
3594         /**
3595          * Returns an object with properties matching the styles requested.
3596          * For example, el.getStyles('color', 'font-size', 'width') might return
3597          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
3598          * @param {String} style1 A style name
3599          * @param {String} style2 A style name
3600          * @param {String} etc.
3601          * @return {Object} The style object
3602          */
3603         getStyles : function(){
3604             var styles = {},
3605                 len = arguments.length,
3606                 i = 0, style;
3607                 
3608             for(; i < len; ++i) {
3609                 style = arguments[i];
3610                 styles[style] = this.getStyle(style);
3611             }
3612             return styles;
3613         },
3614
3615        /**
3616         * <p>Wraps the specified element with a special 9 element markup/CSS block that renders by default as
3617         * a gray container with a gradient background, rounded corners and a 4-way shadow.</p>
3618         * <p>This special markup is used throughout Ext when box wrapping elements ({@link Ext.button.Button},
3619         * {@link Ext.panel.Panel} when <tt>{@link Ext.panel.Panel#frame frame=true}</tt>, {@link Ext.window.Window}).  The markup
3620         * is of this form:</p>
3621         * <pre><code>
3622     Ext.core.Element.boxMarkup =
3623     &#39;&lt;div class="{0}-tl">&lt;div class="{0}-tr">&lt;div class="{0}-tc">&lt;/div>&lt;/div>&lt;/div>
3624      &lt;div class="{0}-ml">&lt;div class="{0}-mr">&lt;div class="{0}-mc">&lt;/div>&lt;/div>&lt;/div>
3625      &lt;div class="{0}-bl">&lt;div class="{0}-br">&lt;div class="{0}-bc">&lt;/div>&lt;/div>&lt;/div>&#39;;
3626         * </code></pre>
3627         * <p>Example usage:</p>
3628         * <pre><code>
3629     // Basic box wrap
3630     Ext.get("foo").boxWrap();
3631
3632     // You can also add a custom class and use CSS inheritance rules to customize the box look.
3633     // 'x-box-blue' is a built-in alternative -- look at the related CSS definitions as an example
3634     // for how to create a custom box wrap style.
3635     Ext.get("foo").boxWrap().addCls("x-box-blue");
3636         * </code></pre>
3637         * @param {String} class (optional) A base CSS class to apply to the containing wrapper element
3638         * (defaults to <tt>'x-box'</tt>). Note that there are a number of CSS rules that are dependent on
3639         * this name to make the overall effect work, so if you supply an alternate base class, make sure you
3640         * also supply all of the necessary rules.
3641         * @return {Ext.core.Element} The outermost wrapping element of the created box structure.
3642         */
3643         boxWrap : function(cls){
3644             cls = cls || Ext.baseCSSPrefix + 'box';
3645             var el = Ext.get(this.insertHtml("beforeBegin", "<div class='" + cls + "'>" + Ext.String.format(Ext.core.Element.boxMarkup, cls) + "</div>"));
3646             Ext.DomQuery.selectNode('.' + cls + '-mc', el.dom).appendChild(this.dom);
3647             return el;
3648         },
3649
3650         /**
3651          * Set the size of this Element. If animation is true, both width and height will be animated concurrently.
3652          * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
3653          * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
3654          * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
3655          * <li>A size object in the format <code>{width: widthValue, height: heightValue}</code>.</li>
3656          * </ul></div>
3657          * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
3658          * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels).</li>
3659          * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
3660          * </ul></div>
3661          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
3662          * @return {Ext.core.Element} this
3663          */
3664         setSize : function(width, height, animate){
3665             var me = this;
3666             if (Ext.isObject(width)){ // in case of object from getSize()
3667                 height = width.height;
3668                 width = width.width;
3669             }
3670             width = me.adjustWidth(width);
3671             height = me.adjustHeight(height);
3672             if(!animate || !me.anim){
3673                 me.dom.style.width = me.addUnits(width);
3674                 me.dom.style.height = me.addUnits(height);
3675             }
3676             else {
3677                 if (!Ext.isObject(animate)) {
3678                     animate = {};
3679                 }
3680                 me.animate(Ext.applyIf({
3681                     to: {
3682                         width: width,
3683                         height: height
3684                     }
3685                 }, animate));
3686             }
3687             return me;
3688         },
3689
3690         /**
3691          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
3692          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
3693          * if a height has not been set using CSS.
3694          * @return {Number}
3695          */
3696         getComputedHeight : function(){
3697             var me = this,
3698                 h = Math.max(me.dom.offsetHeight, me.dom.clientHeight);
3699             if(!h){
3700                 h = parseFloat(me.getStyle('height')) || 0;
3701                 if(!me.isBorderBox()){
3702                     h += me.getFrameWidth('tb');
3703                 }
3704             }
3705             return h;
3706         },
3707
3708         /**
3709          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
3710          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
3711          * if a width has not been set using CSS.
3712          * @return {Number}
3713          */
3714         getComputedWidth : function(){
3715             var me = this,
3716                 w = Math.max(me.dom.offsetWidth, me.dom.clientWidth);
3717                 
3718             if(!w){
3719                 w = parseFloat(me.getStyle('width')) || 0;
3720                 if(!me.isBorderBox()){
3721                     w += me.getFrameWidth('lr');
3722                 }
3723             }
3724             return w;
3725         },
3726
3727         /**
3728          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
3729          for more information about the sides.
3730          * @param {String} sides
3731          * @return {Number}
3732          */
3733         getFrameWidth : function(sides, onlyContentBox){
3734             return onlyContentBox && this.isBorderBox() ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
3735         },
3736
3737         /**
3738          * Sets up event handlers to add and remove a css class when the mouse is over this element
3739          * @param {String} className
3740          * @return {Ext.core.Element} this
3741          */
3742         addClsOnOver : function(className){
3743             var dom = this.dom;
3744             this.hover(
3745                 function(){
3746                     Ext.fly(dom, INTERNAL).addCls(className);
3747                 },
3748                 function(){
3749                     Ext.fly(dom, INTERNAL).removeCls(className);
3750                 }
3751             );
3752             return this;
3753         },
3754
3755         /**
3756          * Sets up event handlers to add and remove a css class when this element has the focus
3757          * @param {String} className
3758          * @return {Ext.core.Element} this
3759          */
3760         addClsOnFocus : function(className){
3761             var me = this,
3762                 dom = me.dom;
3763             me.on("focus", function(){
3764                 Ext.fly(dom, INTERNAL).addCls(className);
3765             });
3766             me.on("blur", function(){
3767                 Ext.fly(dom, INTERNAL).removeCls(className);
3768             });
3769             return me;
3770         },
3771
3772         /**
3773          * 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)
3774          * @param {String} className
3775          * @return {Ext.core.Element} this
3776          */
3777         addClsOnClick : function(className){
3778             var dom = this.dom;
3779             this.on("mousedown", function(){
3780                 Ext.fly(dom, INTERNAL).addCls(className);
3781                 var d = Ext.getDoc(),
3782                     fn = function(){
3783                         Ext.fly(dom, INTERNAL).removeCls(className);
3784                         d.removeListener("mouseup", fn);
3785                     };
3786                 d.on("mouseup", fn);
3787             });
3788             return this;
3789         },
3790
3791         /**
3792          * <p>Returns the dimensions of the element available to lay content out in.<p>
3793          * <p>If the element (or any ancestor element) has CSS style <code>display : none</code>, the dimensions will be zero.</p>
3794          * example:<pre><code>
3795         var vpSize = Ext.getBody().getViewSize();
3796
3797         // all Windows created afterwards will have a default value of 90% height and 95% width
3798         Ext.Window.override({
3799             width: vpSize.width * 0.9,
3800             height: vpSize.height * 0.95
3801         });
3802         // To handle window resizing you would have to hook onto onWindowResize.
3803         * </code></pre>
3804         *
3805         * getViewSize utilizes clientHeight/clientWidth which excludes sizing of scrollbars.
3806         * To obtain the size including scrollbars, use getStyleSize
3807         *
3808         * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
3809         */
3810
3811         getViewSize : function(){
3812             var me = this,
3813                 dom = me.dom,
3814                 isDoc = (dom == Ext.getDoc().dom || dom == Ext.getBody().dom),
3815                 style, overflow, ret;
3816
3817             // If the body, use static methods
3818             if (isDoc) {
3819                 ret = {
3820                     width : Ext.core.Element.getViewWidth(),
3821                     height : Ext.core.Element.getViewHeight()
3822                 };
3823
3824             // Else use clientHeight/clientWidth
3825             }
3826             else {
3827                 // IE 6 & IE Quirks mode acts more like a max-size measurement unless overflow is hidden during measurement.
3828                 // We will put the overflow back to it's original value when we are done measuring.
3829                 if (Ext.isIE6 || Ext.isIEQuirks) {
3830                     style = dom.style;
3831                     overflow = style.overflow;
3832                     me.setStyle({ overflow: 'hidden'});
3833                 }
3834                 ret = {
3835                     width : dom.clientWidth,
3836                     height : dom.clientHeight
3837                 };
3838                 if (Ext.isIE6 || Ext.isIEQuirks) {
3839                     me.setStyle({ overflow: overflow });
3840                 }
3841             }
3842             return ret;
3843         },
3844
3845         /**
3846         * <p>Returns the dimensions of the element available to lay content out in.<p>
3847         *
3848         * getStyleSize utilizes prefers style sizing if present, otherwise it chooses the larger of offsetHeight/clientHeight and offsetWidth/clientWidth.
3849         * To obtain the size excluding scrollbars, use getViewSize
3850         *
3851         * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
3852         */
3853
3854         getStyleSize : function(){
3855             var me = this,
3856                 doc = document,
3857                 d = this.dom,
3858                 isDoc = (d == doc || d == doc.body),
3859                 s = d.style,
3860                 w, h;
3861
3862             // If the body, use static methods
3863             if (isDoc) {
3864                 return {
3865                     width : Ext.core.Element.getViewWidth(),
3866                     height : Ext.core.Element.getViewHeight()
3867                 };
3868             }
3869             // Use Styles if they are set
3870             if(s.width && s.width != 'auto'){
3871                 w = parseFloat(s.width);
3872                 if(me.isBorderBox()){
3873                    w -= me.getFrameWidth('lr');
3874                 }
3875             }
3876             // Use Styles if they are set
3877             if(s.height && s.height != 'auto'){
3878                 h = parseFloat(s.height);
3879                 if(me.isBorderBox()){
3880                    h -= me.getFrameWidth('tb');
3881                 }
3882             }
3883             // Use getWidth/getHeight if style not set.
3884             return {width: w || me.getWidth(true), height: h || me.getHeight(true)};
3885         },
3886
3887         /**
3888          * Returns the size of the element.
3889          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
3890          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
3891          */
3892         getSize : function(contentSize){
3893             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
3894         },
3895
3896         /**
3897          * Forces the browser to repaint this element
3898          * @return {Ext.core.Element} this
3899          */
3900         repaint : function(){
3901             var dom = this.dom;
3902             this.addCls(Ext.baseCSSPrefix + 'repaint');
3903             setTimeout(function(){
3904                 Ext.fly(dom).removeCls(Ext.baseCSSPrefix + 'repaint');
3905             }, 1);
3906             return this;
3907         },
3908
3909         /**
3910          * Disables text selection for this element (normalized across browsers)
3911          * @return {Ext.core.Element} this
3912          */
3913         unselectable : function(){
3914             var me = this;
3915             me.dom.unselectable = "on";
3916
3917             me.swallowEvent("selectstart", true);
3918             me.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
3919             me.addCls(Ext.baseCSSPrefix + 'unselectable');
3920             
3921             return me;
3922         },
3923
3924         /**
3925          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
3926          * then it returns the calculated width of the sides (see getPadding)
3927          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
3928          * @return {Object/Number}
3929          */
3930         getMargin : function(side){
3931             var me = this,
3932                 hash = {t:"top", l:"left", r:"right", b: "bottom"},
3933                 o = {},
3934                 key;
3935
3936             if (!side) {
3937                 for (key in me.margins){
3938                     o[hash[key]] = parseFloat(me.getStyle(me.margins[key])) || 0;
3939                 }
3940                 return o;
3941             } else {
3942                 return me.addStyles.call(me, side, me.margins);
3943             }
3944         }
3945     });
3946 })();
3947 /**
3948  * @class Ext.core.Element
3949  */
3950 /**
3951  * Visibility mode constant for use with {@link #setVisibilityMode}. Use visibility to hide element
3952  * @static
3953  * @type Number
3954  */
3955 Ext.core.Element.VISIBILITY = 1;
3956 /**
3957  * Visibility mode constant for use with {@link #setVisibilityMode}. Use display to hide element
3958  * @static
3959  * @type Number
3960  */
3961 Ext.core.Element.DISPLAY = 2;
3962
3963 /**
3964  * Visibility mode constant for use with {@link #setVisibilityMode}. Use offsets (x and y positioning offscreen)
3965  * to hide element.
3966  * @static
3967  * @type Number
3968  */
3969 Ext.core.Element.OFFSETS = 3;
3970
3971
3972 Ext.core.Element.ASCLASS = 4;
3973
3974 /**
3975  * Defaults to 'x-hide-nosize'
3976  * @static
3977  * @type String
3978  */
3979 Ext.core.Element.visibilityCls = Ext.baseCSSPrefix + 'hide-nosize';
3980
3981 Ext.core.Element.addMethods(function(){
3982     var El = Ext.core.Element,
3983         OPACITY = "opacity",
3984         VISIBILITY = "visibility",
3985         DISPLAY = "display",
3986         HIDDEN = "hidden",
3987         OFFSETS = "offsets",
3988         ASCLASS = "asclass",
3989         NONE = "none",
3990         NOSIZE = 'nosize',
3991         ORIGINALDISPLAY = 'originalDisplay',
3992         VISMODE = 'visibilityMode',
3993         ISVISIBLE = 'isVisible',
3994         data = El.data,
3995         getDisplay = function(dom){
3996             var d = data(dom, ORIGINALDISPLAY);
3997             if(d === undefined){
3998                 data(dom, ORIGINALDISPLAY, d = '');
3999             }
4000             return d;
4001         },
4002         getVisMode = function(dom){
4003             var m = data(dom, VISMODE);
4004             if(m === undefined){
4005                 data(dom, VISMODE, m = 1);
4006             }
4007             return m;
4008         };
4009
4010     return {
4011         /**
4012          * The element's default display mode  (defaults to "")
4013          * @type String
4014          */
4015         originalDisplay : "",
4016         visibilityMode : 1,
4017
4018         /**
4019          * Sets the element's visibility mode. When setVisible() is called it
4020          * will use this to determine whether to set the visibility or the display property.
4021          * @param {Number} visMode Ext.core.Element.VISIBILITY or Ext.core.Element.DISPLAY
4022          * @return {Ext.core.Element} this
4023          */
4024         setVisibilityMode : function(visMode){
4025             data(this.dom, VISMODE, visMode);
4026             return this;
4027         },
4028
4029         /**
4030          * Checks whether the element is currently visible using both visibility and display properties.
4031          * @return {Boolean} True if the element is currently visible, else false
4032          */
4033         isVisible : function() {
4034             var me = this,
4035                 dom = me.dom,
4036                 visible = data(dom, ISVISIBLE);
4037
4038             if(typeof visible == 'boolean'){ //return the cached value if registered
4039                 return visible;
4040             }
4041             //Determine the current state based on display states
4042             visible = !me.isStyle(VISIBILITY, HIDDEN) &&
4043                       !me.isStyle(DISPLAY, NONE) &&
4044                       !((getVisMode(dom) == El.ASCLASS) && me.hasCls(me.visibilityCls || El.visibilityCls));
4045
4046             data(dom, ISVISIBLE, visible);
4047             return visible;
4048         },
4049
4050         /**
4051          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
4052          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
4053          * @param {Boolean} visible Whether the element is visible
4054          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
4055          * @return {Ext.core.Element} this
4056          */
4057         setVisible : function(visible, animate){
4058             var me = this, isDisplay, isVisibility, isOffsets, isNosize,
4059                 dom = me.dom,
4060                 visMode = getVisMode(dom);
4061
4062
4063             // hideMode string override
4064             if (typeof animate == 'string'){
4065                 switch (animate) {
4066                     case DISPLAY:
4067                         visMode = El.DISPLAY;
4068                         break;
4069                     case VISIBILITY:
4070                         visMode = El.VISIBILITY;
4071                         break;
4072                     case OFFSETS:
4073                         visMode = El.OFFSETS;
4074                         break;
4075                     case NOSIZE:
4076                     case ASCLASS:
4077                         visMode = El.ASCLASS;
4078                         break;
4079                 }
4080                 me.setVisibilityMode(visMode);
4081                 animate = false;
4082             }
4083
4084             if (!animate || !me.anim) {
4085                 if(visMode == El.ASCLASS ){
4086
4087                     me[visible?'removeCls':'addCls'](me.visibilityCls || El.visibilityCls);
4088
4089                 } else if (visMode == El.DISPLAY){
4090
4091                     return me.setDisplayed(visible);
4092
4093                 } else if (visMode == El.OFFSETS){
4094
4095                     if (!visible){
4096                         // Remember position for restoring, if we are not already hidden by offsets.
4097                         if (!me.hideModeStyles) {
4098                             me.hideModeStyles = {
4099                                 position: me.getStyle('position'),
4100                                 top: me.getStyle('top'),
4101                                 left: me.getStyle('left')
4102                             };
4103                         }
4104                         me.applyStyles({position: 'absolute', top: '-10000px', left: '-10000px'});
4105                     }
4106
4107                     // Only "restore" as position if we have actually been hidden using offsets.
4108                     // Calling setVisible(true) on a positioned element should not reposition it.
4109                     else if (me.hideModeStyles) {
4110                         me.applyStyles(me.hideModeStyles || {position: '', top: '', left: ''});
4111                         delete me.hideModeStyles;
4112                     }
4113
4114                 }else{
4115                     me.fixDisplay();
4116                     // Show by clearing visibility style. Explicitly setting to "visible" overrides parent visibility setting.
4117                     dom.style.visibility = visible ? '' : HIDDEN;
4118                 }
4119             }else{
4120                 // closure for composites
4121                 if(visible){
4122                     me.setOpacity(0.01);
4123                     me.setVisible(true);
4124                 }
4125                 if (!Ext.isObject(animate)) {
4126                     animate = {
4127                         duration: 350,
4128                         easing: 'ease-in'
4129                     };
4130                 }
4131                 me.animate(Ext.applyIf({
4132                     callback: function() {
4133                         visible || me.setVisible(false).setOpacity(1);
4134                     },
4135                     to: {
4136                         opacity: (visible) ? 1 : 0
4137                     }
4138                 }, animate));
4139             }
4140             data(dom, ISVISIBLE, visible);  //set logical visibility state
4141             return me;
4142         },
4143
4144
4145         /**
4146          * @private
4147          * Determine if the Element has a relevant height and width available based
4148          * upon current logical visibility state
4149          */
4150         hasMetrics  : function(){
4151             var dom = this.dom;
4152             return this.isVisible() || (getVisMode(dom) == El.OFFSETS) || (getVisMode(dom) == El.VISIBILITY);
4153         },
4154
4155         /**
4156          * Toggles the element's visibility or display, depending on visibility mode.
4157          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
4158          * @return {Ext.core.Element} this
4159          */
4160         toggle : function(animate){
4161             var me = this;
4162             me.setVisible(!me.isVisible(), me.anim(animate));
4163             return me;
4164         },
4165
4166         /**
4167          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
4168          * @param {Mixed} value Boolean value to display the element using its default display, or a string to set the display directly.
4169          * @return {Ext.core.Element} this
4170          */
4171         setDisplayed : function(value) {
4172             if(typeof value == "boolean"){
4173                value = value ? getDisplay(this.dom) : NONE;
4174             }
4175             this.setStyle(DISPLAY, value);
4176             return this;
4177         },
4178
4179         // private
4180         fixDisplay : function(){
4181             var me = this;
4182             if (me.isStyle(DISPLAY, NONE)) {
4183                 me.setStyle(VISIBILITY, HIDDEN);
4184                 me.setStyle(DISPLAY, getDisplay(this.dom)); // first try reverting to default
4185                 if (me.isStyle(DISPLAY, NONE)) { // if that fails, default to block
4186                     me.setStyle(DISPLAY, "block");
4187                 }
4188             }
4189         },
4190
4191         /**
4192          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
4193          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
4194          * @return {Ext.core.Element} this
4195          */
4196         hide : function(animate){
4197             // hideMode override
4198             if (typeof animate == 'string'){
4199                 this.setVisible(false, animate);
4200                 return this;
4201             }
4202             this.setVisible(false, this.anim(animate));
4203             return this;
4204         },
4205
4206         /**
4207         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
4208         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
4209          * @return {Ext.core.Element} this
4210          */
4211         show : function(animate){
4212             // hideMode override
4213             if (typeof animate == 'string'){
4214                 this.setVisible(true, animate);
4215                 return this;
4216             }
4217             this.setVisible(true, this.anim(animate));
4218             return this;
4219         }
4220     };
4221 }());
4222 /**
4223  * @class Ext.core.Element
4224  */
4225 Ext.applyIf(Ext.core.Element.prototype, {
4226     // @private override base Ext.util.Animate mixin for animate for backwards compatibility
4227     animate: function(config) {
4228         var me = this;
4229         if (!me.id) {
4230             me = Ext.get(me.dom);
4231         }
4232         if (Ext.fx.Manager.hasFxBlock(me.id)) {
4233             return me;
4234         }
4235         Ext.fx.Manager.queueFx(Ext.create('Ext.fx.Anim', me.anim(config)));
4236         return this;
4237     },
4238
4239     // @private override base Ext.util.Animate mixin for animate for backwards compatibility
4240     anim: function(config) {
4241         if (!Ext.isObject(config)) {
4242             return (config) ? {} : false;
4243         }
4244
4245         var me = this,
4246             duration = config.duration || Ext.fx.Anim.prototype.duration,
4247             easing = config.easing || 'ease',
4248             animConfig;
4249
4250         if (config.stopAnimation) {
4251             me.stopAnimation();
4252         }
4253
4254         Ext.applyIf(config, Ext.fx.Manager.getFxDefaults(me.id));
4255
4256         // Clear any 'paused' defaults.
4257         Ext.fx.Manager.setFxDefaults(me.id, {
4258             delay: 0
4259         });
4260
4261         animConfig = {
4262             target: me,
4263             remove: config.remove,
4264             alternate: config.alternate || false,
4265             duration: duration,
4266             easing: easing,
4267             callback: config.callback,
4268             listeners: config.listeners,
4269             iterations: config.iterations || 1,
4270             scope: config.scope,
4271             block: config.block,
4272             concurrent: config.concurrent,
4273             delay: config.delay || 0,
4274             paused: true,
4275             keyframes: config.keyframes,
4276             from: config.from || {},
4277             to: Ext.apply({}, config)
4278         };
4279         Ext.apply(animConfig.to, config.to);
4280
4281         // Anim API properties - backward compat
4282         delete animConfig.to.to;
4283         delete animConfig.to.from;
4284         delete animConfig.to.remove;
4285         delete animConfig.to.alternate;
4286         delete animConfig.to.keyframes;
4287         delete animConfig.to.iterations;
4288         delete animConfig.to.listeners;
4289         delete animConfig.to.target;
4290         delete animConfig.to.paused;
4291         delete animConfig.to.callback;
4292         delete animConfig.to.scope;
4293         delete animConfig.to.duration;
4294         delete animConfig.to.easing;
4295         delete animConfig.to.concurrent;
4296         delete animConfig.to.block;
4297         delete animConfig.to.stopAnimation;
4298         delete animConfig.to.delay;
4299         return animConfig;
4300     },
4301
4302     /**
4303      * Slides the element into view.  An anchor point can be optionally passed to set the point of
4304      * origin for the slide effect.  This function automatically handles wrapping the element with
4305      * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
4306      * Usage:
4307      *<pre><code>
4308 // default: slide the element in from the top
4309 el.slideIn();
4310
4311 // custom: slide the element in from the right with a 2-second duration
4312 el.slideIn('r', { duration: 2 });
4313
4314 // common config options shown with default values
4315 el.slideIn('t', {
4316     easing: 'easeOut',
4317     duration: 500
4318 });
4319 </code></pre>
4320      * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
4321      * @param {Object} options (optional) Object literal with any of the Fx config options
4322      * @return {Ext.core.Element} The Element
4323      */
4324     slideIn: function(anchor, obj, slideOut) { 
4325         var me = this,
4326             elStyle = me.dom.style,
4327             beforeAnim, wrapAnim;
4328
4329         anchor = anchor || "t";
4330         obj = obj || {};
4331
4332         beforeAnim = function() {
4333             var animScope = this,
4334                 listeners = obj.listeners,
4335                 box, position, restoreSize, wrap, anim;
4336
4337             if (!slideOut) {
4338                 me.fixDisplay();
4339             }
4340
4341             box = me.getBox();
4342             if ((anchor == 't' || anchor == 'b') && box.height == 0) {
4343                 box.height = me.dom.scrollHeight;
4344             }
4345             else if ((anchor == 'l' || anchor == 'r') && box.width == 0) {
4346                 box.width = me.dom.scrollWidth;
4347             }
4348             
4349             position = me.getPositioning();
4350             me.setSize(box.width, box.height);
4351
4352             wrap = me.wrap({
4353                 style: {
4354                     visibility: slideOut ? 'visible' : 'hidden'
4355                 }
4356             });
4357             wrap.setPositioning(position);
4358             if (wrap.isStyle('position', 'static')) {
4359                 wrap.position('relative');
4360             }
4361             me.clearPositioning('auto');
4362             wrap.clip();
4363
4364             // This element is temporarily positioned absolute within its wrapper.
4365             // Restore to its default, CSS-inherited visibility setting.
4366             // We cannot explicitly poke visibility:visible into its style because that overrides the visibility of the wrap.
4367             me.setStyle({
4368                 visibility: '',
4369                 position: 'absolute'
4370             });
4371             if (slideOut) {
4372                 wrap.setSize(box.width, box.height);
4373             }
4374
4375             switch (anchor) {
4376                 case 't':
4377                     anim = {
4378                         from: {
4379                             width: box.width + 'px',
4380                             height: '0px'
4381                         },
4382                         to: {
4383                             width: box.width + 'px',
4384                             height: box.height + 'px'
4385                         }
4386                     };
4387                     elStyle.bottom = '0px';
4388                     break;
4389                 case 'l':
4390                     anim = {
4391                         from: {
4392                             width: '0px',
4393                             height: box.height + 'px'
4394                         },
4395                         to: {
4396                             width: box.width + 'px',
4397                             height: box.height + 'px'
4398                         }
4399                     };
4400                     elStyle.right = '0px';
4401                     break;
4402                 case 'r':
4403                     anim = {
4404                         from: {
4405                             x: box.x + box.width,
4406                             width: '0px',
4407                             height: box.height + 'px'
4408                         },
4409                         to: {
4410                             x: box.x,
4411                             width: box.width + 'px',
4412                             height: box.height + 'px'
4413                         }
4414                     };
4415                     break;
4416                 case 'b':
4417                     anim = {
4418                         from: {
4419                             y: box.y + box.height,
4420                             width: box.width + 'px',
4421                             height: '0px'
4422                         },
4423                         to: {
4424                             y: box.y,
4425                             width: box.width + 'px',
4426                             height: box.height + 'px'
4427                         }
4428                     };
4429                     break;
4430                 case 'tl':
4431                     anim = {
4432                         from: {
4433                             x: box.x,
4434                             y: box.y,
4435                             width: '0px',
4436                             height: '0px'
4437                         },
4438                         to: {
4439                             width: box.width + 'px',
4440                             height: box.height + 'px'
4441                         }
4442                     };
4443                     elStyle.bottom = '0px';
4444                     elStyle.right = '0px';
4445                     break;
4446                 case 'bl':
4447                     anim = {
4448                         from: {
4449                             x: box.x + box.width,
4450                             width: '0px',
4451                             height: '0px'
4452                         },
4453                         to: {
4454                             x: box.x,
4455                             width: box.width + 'px',
4456                             height: box.height + 'px'
4457                         }
4458                     };
4459                     elStyle.right = '0px';
4460                     break;
4461                 case 'br':
4462                     anim = {
4463                         from: {
4464                             x: box.x + box.width,
4465                             y: box.y + box.height,
4466                             width: '0px',
4467                             height: '0px'
4468                         },
4469                         to: {
4470                             x: box.x,
4471                             y: box.y,
4472                             width: box.width + 'px',
4473                             height: box.height + 'px'
4474                         }
4475                     };
4476                     break;
4477                 case 'tr':
4478                     anim = {
4479                         from: {
4480                             y: box.y + box.height,
4481                             width: '0px',
4482                             height: '0px'
4483                         },
4484                         to: {
4485                             y: box.y,
4486                             width: box.width + 'px',
4487                             height: box.height + 'px'
4488                         }
4489                     };
4490                     elStyle.bottom = '0px';
4491                     break;
4492             }
4493
4494             wrap.show();
4495             wrapAnim = Ext.apply({}, obj);
4496             delete wrapAnim.listeners;
4497             wrapAnim = Ext.create('Ext.fx.Anim', Ext.applyIf(wrapAnim, {
4498                 target: wrap,
4499                 duration: 500,
4500                 easing: 'ease-out',
4501                 from: slideOut ? anim.to : anim.from,
4502                 to: slideOut ? anim.from : anim.to
4503             }));
4504
4505             // In the absence of a callback, this listener MUST be added first
4506             wrapAnim.on('afteranimate', function() {
4507                 if (slideOut) {
4508                     me.setPositioning(position);
4509                     if (obj.useDisplay) {
4510                         me.setDisplayed(false);
4511                     } else {
4512                         me.hide();   
4513                     }
4514                 }
4515                 else {
4516                     me.clearPositioning();
4517                     me.setPositioning(position);
4518                 }
4519                 if (wrap.dom) {
4520                     wrap.dom.parentNode.insertBefore(me.dom, wrap.dom); 
4521                     wrap.remove();
4522                 }
4523                 me.setSize(box.width, box.height);
4524                 animScope.end();
4525             });
4526             // Add configured listeners after
4527             if (listeners) {
4528                 wrapAnim.on(listeners);
4529             }
4530         };
4531
4532         me.animate({
4533             duration: obj.duration ? obj.duration * 2 : 1000,
4534             listeners: {
4535                 beforeanimate: {
4536                     fn: beforeAnim
4537                 },
4538                 afteranimate: {
4539                     fn: function() {
4540                         if (wrapAnim && wrapAnim.running) {
4541                             wrapAnim.end();
4542                         }
4543                     }
4544                 }
4545             }
4546         });
4547         return me;
4548     },
4549
4550     
4551     /**
4552      * Slides the element out of view.  An anchor point can be optionally passed to set the end point
4553      * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
4554      * 'hidden') but block elements will still take up space in the document.  The element must be removed
4555      * from the DOM using the 'remove' config option if desired.  This function automatically handles 
4556      * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
4557      * Usage:
4558      *<pre><code>
4559 // default: slide the element out to the top
4560 el.slideOut();
4561
4562 // custom: slide the element out to the right with a 2-second duration
4563 el.slideOut('r', { duration: 2 });
4564
4565 // common config options shown with default values
4566 el.slideOut('t', {
4567     easing: 'easeOut',
4568     duration: 500,
4569     remove: false,
4570     useDisplay: false
4571 });
4572 </code></pre>
4573      * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
4574      * @param {Object} options (optional) Object literal with any of the Fx config options
4575      * @return {Ext.core.Element} The Element
4576      */
4577     slideOut: function(anchor, o) {
4578         return this.slideIn(anchor, o, true);
4579     },
4580
4581     /**
4582      * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
4583      * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document.
4584      * Usage:
4585      *<pre><code>
4586 // default
4587 el.puff();
4588
4589 // common config options shown with default values
4590 el.puff({
4591     easing: 'easeOut',
4592     duration: 500,
4593     useDisplay: false
4594 });
4595 </code></pre>
4596      * @param {Object} options (optional) Object literal with any of the Fx config options
4597      * @return {Ext.core.Element} The Element
4598      */
4599
4600     puff: function(obj) {
4601         var me = this,
4602             beforeAnim;
4603         obj = Ext.applyIf(obj || {}, {
4604             easing: 'ease-out',
4605             duration: 500,
4606             useDisplay: false
4607         });
4608
4609         beforeAnim = function() {
4610             me.clearOpacity();
4611             me.show();
4612
4613             var box = me.getBox(),
4614                 fontSize = me.getStyle('fontSize'),
4615                 position = me.getPositioning();
4616             this.to = {
4617                 width: box.width * 2,
4618                 height: box.height * 2,
4619                 x: box.x - (box.width / 2),
4620                 y: box.y - (box.height /2),
4621                 opacity: 0,
4622                 fontSize: '200%'
4623             };
4624             this.on('afteranimate',function() {
4625                 if (me.dom) {
4626                     if (obj.useDisplay) {
4627                         me.setDisplayed(false);
4628                     } else {
4629                         me.hide();
4630                     }
4631                     me.clearOpacity();  
4632                     me.setPositioning(position);
4633                     me.setStyle({fontSize: fontSize});
4634                 }
4635             });
4636         };
4637
4638         me.animate({
4639             duration: obj.duration,
4640             easing: obj.easing,
4641             listeners: {
4642                 beforeanimate: {
4643                     fn: beforeAnim
4644                 }
4645             }
4646         });
4647         return me;
4648     },
4649
4650     /**
4651      * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
4652      * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
4653      * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
4654      * Usage:
4655      *<pre><code>
4656 // default
4657 el.switchOff();
4658
4659 // all config options shown with default values
4660 el.switchOff({
4661     easing: 'easeIn',
4662     duration: .3,
4663     remove: false,
4664     useDisplay: false
4665 });
4666 </code></pre>
4667      * @param {Object} options (optional) Object literal with any of the Fx config options
4668      * @return {Ext.core.Element} The Element
4669      */
4670     switchOff: function(obj) {
4671         var me = this,
4672             beforeAnim;
4673         
4674         obj = Ext.applyIf(obj || {}, {
4675             easing: 'ease-in',
4676             duration: 500,
4677             remove: false,
4678             useDisplay: false
4679         });
4680
4681         beforeAnim = function() {
4682             var animScope = this,
4683                 size = me.getSize(),
4684                 xy = me.getXY(),
4685                 keyframe, position;
4686             me.clearOpacity();
4687             me.clip();
4688             position = me.getPositioning();
4689
4690             keyframe = Ext.create('Ext.fx.Animator', {
4691                 target: me,
4692                 duration: obj.duration,
4693                 easing: obj.easing,
4694                 keyframes: {
4695                     33: {
4696                         opacity: 0.3
4697                     },
4698                     66: {
4699                         height: 1,
4700                         y: xy[1] + size.height / 2
4701                     },
4702                     100: {
4703                         width: 1,
4704                         x: xy[0] + size.width / 2
4705                     }
4706                 }
4707             });
4708             keyframe.on('afteranimate', function() {
4709                 if (obj.useDisplay) {
4710                     me.setDisplayed(false);
4711                 } else {
4712                     me.hide();
4713                 }  
4714                 me.clearOpacity();
4715                 me.setPositioning(position);
4716                 me.setSize(size);
4717                 animScope.end();
4718             });
4719         };
4720         me.animate({
4721             duration: (obj.duration * 2),
4722             listeners: {
4723                 beforeanimate: {
4724                     fn: beforeAnim
4725                 }
4726             }
4727         });
4728         return me;
4729     },
4730
4731    /**
4732     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
4733     * Usage:
4734 <pre><code>
4735 // default: a single light blue ripple
4736 el.frame();
4737
4738 // custom: 3 red ripples lasting 3 seconds total
4739 el.frame("#ff0000", 3, { duration: 3 });
4740
4741 // common config options shown with default values
4742 el.frame("#C3DAF9", 1, {
4743     duration: 1 //duration of each individual ripple.
4744     // Note: Easing is not configurable and will be ignored if included
4745 });
4746 </code></pre>
4747     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
4748     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
4749     * @param {Object} options (optional) Object literal with any of the Fx config options
4750     * @return {Ext.core.Element} The Element
4751     */
4752     frame : function(color, count, obj){
4753         var me = this,
4754             beforeAnim;
4755
4756         color = color || '#C3DAF9';
4757         count = count || 1;
4758         obj = obj || {};
4759
4760         beforeAnim = function() {
4761             me.show();
4762             var animScope = this,
4763                 box = me.getBox(),
4764                 proxy = Ext.getBody().createChild({
4765                     style: {
4766                         position : 'absolute',
4767                         'pointer-events': 'none',
4768                         'z-index': 35000,
4769                         border : '0px solid ' + color
4770                     }
4771                 }),
4772                 proxyAnim;
4773             proxyAnim = Ext.create('Ext.fx.Anim', {
4774                 target: proxy,
4775                 duration: obj.duration || 1000,
4776                 iterations: count,
4777                 from: {
4778                     top: box.y,
4779                     left: box.x,
4780                     borderWidth: 0,
4781                     opacity: 1,
4782                     height: box.height,
4783                     width: box.width
4784                 },
4785                 to: {
4786                     top: box.y - 20,
4787                     left: box.x - 20,
4788                     borderWidth: 10,
4789                     opacity: 0,
4790                     height: box.height + 40,
4791                     width: box.width + 40
4792                 }
4793             });
4794             proxyAnim.on('afteranimate', function() {
4795                 proxy.remove();
4796                 animScope.end();
4797             });
4798         };
4799
4800         me.animate({
4801             duration: (obj.duration * 2) || 2000,
4802             listeners: {
4803                 beforeanimate: {
4804                     fn: beforeAnim
4805                 }
4806             }
4807         });
4808         return me;
4809     },
4810
4811     /**
4812      * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
4813      * ending point of the effect.
4814      * Usage:
4815      *<pre><code>
4816 // default: slide the element downward while fading out
4817 el.ghost();
4818
4819 // custom: slide the element out to the right with a 2-second duration
4820 el.ghost('r', { duration: 2 });
4821
4822 // common config options shown with default values
4823 el.ghost('b', {
4824     easing: 'easeOut',
4825     duration: 500
4826 });
4827 </code></pre>
4828      * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
4829      * @param {Object} options (optional) Object literal with any of the Fx config options
4830      * @return {Ext.core.Element} The Element
4831      */
4832     ghost: function(anchor, obj) {
4833         var me = this,
4834             beforeAnim;
4835
4836         anchor = anchor || "b";
4837         beforeAnim = function() {
4838             var width = me.getWidth(),
4839                 height = me.getHeight(),
4840                 xy = me.getXY(),
4841                 position = me.getPositioning(),
4842                 to = {
4843                     opacity: 0
4844                 };
4845             switch (anchor) {
4846                 case 't':
4847                     to.y = xy[1] - height;
4848                     break;
4849                 case 'l':
4850                     to.x = xy[0] - width;
4851                     break;
4852                 case 'r':
4853                     to.x = xy[0] + width;
4854                     break;
4855                 case 'b':
4856                     to.y = xy[1] + height;
4857                     break;
4858                 case 'tl':
4859                     to.x = xy[0] - width;
4860                     to.y = xy[1] - height;
4861                     break;
4862                 case 'bl':
4863                     to.x = xy[0] - width;
4864                     to.y = xy[1] + height;
4865                     break;
4866                 case 'br':
4867                     to.x = xy[0] + width;
4868                     to.y = xy[1] + height;
4869                     break;
4870                 case 'tr':
4871                     to.x = xy[0] + width;
4872                     to.y = xy[1] - height;
4873                     break;
4874             }
4875             this.to = to;
4876             this.on('afteranimate', function () {
4877                 if (me.dom) {
4878                     me.hide();
4879                     me.clearOpacity();
4880                     me.setPositioning(position);
4881                 }
4882             });
4883         };
4884
4885         me.animate(Ext.applyIf(obj || {}, {
4886             duration: 500,
4887             easing: 'ease-out',
4888             listeners: {
4889                 beforeanimate: {
4890                     fn: beforeAnim
4891                 }
4892             }
4893         }));
4894         return me;
4895     },
4896
4897     /**
4898      * Highlights the Element by setting a color (applies to the background-color by default, but can be
4899      * changed using the "attr" config option) and then fading back to the original color. If no original
4900      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
4901      * Usage:
4902 <pre><code>
4903 // default: highlight background to yellow
4904 el.highlight();
4905
4906 // custom: highlight foreground text to blue for 2 seconds
4907 el.highlight("0000ff", { attr: 'color', duration: 2 });
4908
4909 // common config options shown with default values
4910 el.highlight("ffff9c", {
4911     attr: "backgroundColor", //can be any valid CSS property (attribute) that supports a color value
4912     endColor: (current color) or "ffffff",
4913     easing: 'easeIn',
4914     duration: 1000
4915 });
4916 </code></pre>
4917      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
4918      * @param {Object} options (optional) Object literal with any of the Fx config options
4919      * @return {Ext.core.Element} The Element
4920      */ 
4921     highlight: function(color, o) {
4922         var me = this,
4923             dom = me.dom,
4924             from = {},
4925             restore, to, attr, lns, event, fn;
4926
4927         o = o || {};
4928         lns = o.listeners || {};
4929         attr = o.attr || 'backgroundColor';
4930         from[attr] = color || 'ffff9c';
4931         
4932         if (!o.to) {
4933             to = {};
4934             to[attr] = o.endColor || me.getColor(attr, 'ffffff', '');
4935         }
4936         else {
4937             to = o.to;
4938         }
4939         
4940         // Don't apply directly on lns, since we reference it in our own callbacks below
4941         o.listeners = Ext.apply(Ext.apply({}, lns), {
4942             beforeanimate: function() {
4943                 restore = dom.style[attr];
4944                 me.clearOpacity();
4945                 me.show();
4946                 
4947                 event = lns.beforeanimate;
4948                 if (event) {
4949                     fn = event.fn || event;
4950                     return fn.apply(event.scope || lns.scope || window, arguments);
4951                 }
4952             },
4953             afteranimate: function() {
4954                 if (dom) {
4955                     dom.style[attr] = restore;
4956                 }
4957                 
4958                 event = lns.afteranimate;
4959                 if (event) {
4960                     fn = event.fn || event;
4961                     fn.apply(event.scope || lns.scope || window, arguments);
4962                 }
4963             }
4964         });
4965
4966         me.animate(Ext.apply({}, o, {
4967             duration: 1000,
4968             easing: 'ease-in',
4969             from: from,
4970             to: to
4971         }));
4972         return me;
4973     },
4974
4975    /**
4976     * @deprecated 4.0
4977     * Creates a pause before any subsequent queued effects begin.  If there are
4978     * no effects queued after the pause it will have no effect.
4979     * Usage:
4980 <pre><code>
4981 el.pause(1);
4982 </code></pre>
4983     * @param {Number} seconds The length of time to pause (in seconds)
4984     * @return {Ext.Element} The Element
4985     */
4986     pause: function(ms) {
4987         var me = this;
4988         Ext.fx.Manager.setFxDefaults(me.id, {
4989             delay: ms
4990         });
4991         return me;
4992     },
4993
4994    /**
4995     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
4996     * using the <tt>{@link #endOpacity}</tt> config option.
4997     * Usage:
4998 <pre><code>
4999 // default: fade in from opacity 0 to 100%
5000 el.fadeIn();
5001
5002 // custom: fade in from opacity 0 to 75% over 2 seconds
5003 el.fadeIn({ endOpacity: .75, duration: 2});
5004
5005 // common config options shown with default values
5006 el.fadeIn({
5007     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
5008     easing: 'easeOut',
5009     duration: 500
5010 });
5011 </code></pre>
5012     * @param {Object} options (optional) Object literal with any of the Fx config options
5013     * @return {Ext.Element} The Element
5014     */
5015     fadeIn: function(o) {
5016         this.animate(Ext.apply({}, o, {
5017             opacity: 1
5018         }));
5019         return this;
5020     },
5021
5022    /**
5023     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
5024     * using the <tt>{@link #endOpacity}</tt> config option.  Note that IE may require
5025     * <tt>{@link #useDisplay}:true</tt> in order to redisplay correctly.
5026     * Usage:
5027 <pre><code>
5028 // default: fade out from the element's current opacity to 0
5029 el.fadeOut();
5030
5031 // custom: fade out from the element's current opacity to 25% over 2 seconds
5032 el.fadeOut({ endOpacity: .25, duration: 2});
5033
5034 // common config options shown with default values
5035 el.fadeOut({
5036     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
5037     easing: 'easeOut',
5038     duration: 500,
5039     remove: false,
5040     useDisplay: false
5041 });
5042 </code></pre>
5043     * @param {Object} options (optional) Object literal with any of the Fx config options
5044     * @return {Ext.Element} The Element
5045     */
5046     fadeOut: function(o) {
5047         this.animate(Ext.apply({}, o, {
5048             opacity: 0
5049         }));
5050         return this;
5051     },
5052
5053    /**
5054     * @deprecated 4.0
5055     * Animates the transition of an element's dimensions from a starting height/width
5056     * to an ending height/width.  This method is a convenience implementation of {@link shift}.
5057     * Usage:
5058 <pre><code>
5059 // change height and width to 100x100 pixels
5060 el.scale(100, 100);
5061
5062 // common config options shown with default values.  The height and width will default to
5063 // the element&#39;s existing values if passed as null.
5064 el.scale(
5065     [element&#39;s width],
5066     [element&#39;s height], {
5067         easing: 'easeOut',
5068         duration: .35
5069     }
5070 );
5071 </code></pre>
5072     * @param {Number} width  The new width (pass undefined to keep the original width)
5073     * @param {Number} height  The new height (pass undefined to keep the original height)
5074     * @param {Object} options (optional) Object literal with any of the Fx config options
5075     * @return {Ext.Element} The Element
5076     */
5077     scale: function(w, h, o) {
5078         this.animate(Ext.apply({}, o, {
5079             width: w,
5080             height: h
5081         }));
5082         return this;
5083     },
5084
5085    /**
5086     * @deprecated 4.0
5087     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
5088     * Any of these properties not specified in the config object will not be changed.  This effect 
5089     * requires that at least one new dimension, position or opacity setting must be passed in on
5090     * the config object in order for the function to have any effect.
5091     * Usage:
5092 <pre><code>
5093 // slide the element horizontally to x position 200 while changing the height and opacity
5094 el.shift({ x: 200, height: 50, opacity: .8 });
5095
5096 // common config options shown with default values.
5097 el.shift({
5098     width: [element&#39;s width],
5099     height: [element&#39;s height],
5100     x: [element&#39;s x position],
5101     y: [element&#39;s y position],
5102     opacity: [element&#39;s opacity],
5103     easing: 'easeOut',
5104     duration: .35
5105 });
5106 </code></pre>
5107     * @param {Object} options  Object literal with any of the Fx config options
5108     * @return {Ext.Element} The Element
5109     */
5110     shift: function(config) {
5111         this.animate(config);
5112         return this;
5113     }
5114 });
5115
5116 /**
5117  * @class Ext.core.Element
5118  */
5119 Ext.applyIf(Ext.core.Element, {
5120     unitRe: /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
5121     camelRe: /(-[a-z])/gi,
5122     opacityRe: /alpha\(opacity=(.*)\)/i,
5123     cssRe: /([a-z0-9-]+)\s*:\s*([^;\s]+(?:\s*[^;\s]+)*);?/gi,
5124     propertyCache: {},
5125     defaultUnit : "px",
5126     borders: {l: 'border-left-width', r: 'border-right-width', t: 'border-top-width', b: 'border-bottom-width'},
5127     paddings: {l: 'padding-left', r: 'padding-right', t: 'padding-top', b: 'padding-bottom'},
5128     margins: {l: 'margin-left', r: 'margin-right', t: 'margin-top', b: 'margin-bottom'},
5129
5130     // Reference the prototype's version of the method. Signatures are identical.
5131     addUnits : Ext.core.Element.prototype.addUnits,
5132
5133     /**
5134      * Parses a number or string representing margin sizes into an object. Supports CSS-style margin declarations
5135      * (e.g. 10, "10", "10 10", "10 10 10" and "10 10 10 10" are all valid options and would return the same result)
5136      * @static
5137      * @param {Number|String} box The encoded margins
5138      * @return {Object} An object with margin sizes for top, right, bottom and left
5139      */
5140     parseBox : function(box) {
5141         if (Ext.isObject(box)) {
5142             return {
5143                 top: box.top || 0,
5144                 right: box.right || 0,
5145                 bottom: box.bottom || 0,
5146                 left: box.left || 0
5147             };
5148         } else {
5149             if (typeof box != 'string') {
5150                 box = box.toString();
5151             }
5152             var parts  = box.split(' '),
5153                 ln = parts.length;
5154     
5155             if (ln == 1) {
5156                 parts[1] = parts[2] = parts[3] = parts[0];
5157             }
5158             else if (ln == 2) {
5159                 parts[2] = parts[0];
5160                 parts[3] = parts[1];
5161             }
5162             else if (ln == 3) {
5163                 parts[3] = parts[1];
5164             }
5165     
5166             return {
5167                 top   :parseFloat(parts[0]) || 0,
5168                 right :parseFloat(parts[1]) || 0,
5169                 bottom:parseFloat(parts[2]) || 0,
5170                 left  :parseFloat(parts[3]) || 0
5171             };
5172         }
5173         
5174     },
5175     
5176     /**
5177      * Parses a number or string representing margin sizes into an object. Supports CSS-style margin declarations
5178      * (e.g. 10, "10", "10 10", "10 10 10" and "10 10 10 10" are all valid options and would return the same result)
5179      * @static
5180      * @param {Number|String} box The encoded margins
5181      * @param {String} units The type of units to add
5182      * @return {String} An string with unitized (px if units is not specified) metrics for top, right, bottom and left
5183      */
5184     unitizeBox : function(box, units) {
5185         var A = this.addUnits,
5186             B = this.parseBox(box);
5187             
5188         return A(B.top, units) + ' ' +
5189                A(B.right, units) + ' ' +
5190                A(B.bottom, units) + ' ' +
5191                A(B.left, units);
5192         
5193     },
5194
5195     // private
5196     camelReplaceFn : function(m, a) {
5197         return a.charAt(1).toUpperCase();
5198     },
5199
5200     /**
5201      * Normalizes CSS property keys from dash delimited to camel case JavaScript Syntax.
5202      * For example:
5203      * <ul>
5204      *  <li>border-width -> borderWidth</li>
5205      *  <li>padding-top -> paddingTop</li>
5206      * </ul>
5207      * @static
5208      * @param {String} prop The property to normalize
5209      * @return {String} The normalized string
5210      */
5211     normalize : function(prop) {
5212         if (prop == 'float') {
5213             prop = Ext.supports.Float ? 'cssFloat' : 'styleFloat';
5214         }
5215         return this.propertyCache[prop] || (this.propertyCache[prop] = prop.replace(this.camelRe, this.camelReplaceFn));
5216     },
5217
5218     /**
5219      * Retrieves the document height
5220      * @static
5221      * @return {Number} documentHeight
5222      */
5223     getDocumentHeight: function() {
5224         return Math.max(!Ext.isStrict ? document.body.scrollHeight : document.documentElement.scrollHeight, this.getViewportHeight());
5225     },
5226
5227     /**
5228      * Retrieves the document width
5229      * @static
5230      * @return {Number} documentWidth
5231      */
5232     getDocumentWidth: function() {
5233         return Math.max(!Ext.isStrict ? document.body.scrollWidth : document.documentElement.scrollWidth, this.getViewportWidth());
5234     },
5235
5236     /**
5237      * Retrieves the viewport height of the window.
5238      * @static
5239      * @return {Number} viewportHeight
5240      */
5241     getViewportHeight: function(){
5242         return window.innerHeight;
5243     },
5244
5245     /**
5246      * Retrieves the viewport width of the window.
5247      * @static
5248      * @return {Number} viewportWidth
5249      */
5250     getViewportWidth : function() {
5251         return window.innerWidth;
5252     },
5253
5254     /**
5255      * Retrieves the viewport size of the window.
5256      * @static
5257      * @return {Object} object containing width and height properties
5258      */
5259     getViewSize : function() {
5260         return {
5261             width: window.innerWidth,
5262             height: window.innerHeight
5263         };
5264     },
5265
5266     /**
5267      * Retrieves the current orientation of the window. This is calculated by
5268      * determing if the height is greater than the width.
5269      * @static
5270      * @return {String} Orientation of window: 'portrait' or 'landscape'
5271      */
5272     getOrientation : function() {
5273         if (Ext.supports.OrientationChange) {
5274             return (window.orientation == 0) ? 'portrait' : 'landscape';
5275         }
5276         
5277         return (window.innerHeight > window.innerWidth) ? 'portrait' : 'landscape';
5278     },
5279
5280     /** 
5281      * Returns the top Element that is located at the passed coordinates
5282      * @static
5283      * @param {Number} x The x coordinate
5284      * @param {Number} x The y coordinate
5285      * @return {String} The found Element
5286      */
5287     fromPoint: function(x, y) {
5288         return Ext.get(document.elementFromPoint(x, y));
5289     },
5290     
5291     /**
5292      * Converts a CSS string into an object with a property for each style.
5293      * <p>
5294      * The sample code below would return an object with 2 properties, one
5295      * for background-color and one for color.</p>
5296      * <pre><code>
5297 var css = 'background-color: red;color: blue; ';
5298 console.log(Ext.core.Element.parseStyles(css));
5299      * </code></pre>
5300      * @static
5301      * @param {String} styles A CSS string
5302      * @return {Object} styles
5303      */
5304     parseStyles: function(styles){
5305         var out = {},
5306             cssRe = this.cssRe,
5307             matches;
5308             
5309         if (styles) {
5310             // Since we're using the g flag on the regex, we need to set the lastIndex.
5311             // This automatically happens on some implementations, but not others, see:
5312             // http://stackoverflow.com/questions/2645273/javascript-regular-expression-literal-persists-between-function-calls
5313             // http://blog.stevenlevithan.com/archives/fixing-javascript-regexp
5314             cssRe.lastIndex = 0;
5315             while ((matches = cssRe.exec(styles))) {
5316                 out[matches[1]] = matches[2];
5317             }
5318         }
5319         return out;
5320     }
5321 });
5322
5323 /**
5324  * @class Ext.CompositeElementLite
5325  * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter
5326  * members, or to perform collective actions upon the whole set.</p>
5327  * <p>Although they are not listed, this class supports all of the methods of {@link Ext.core.Element} and
5328  * {@link Ext.fx.Anim}. The methods from these classes will be performed on all the elements in this collection.</p>
5329  * Example:<pre><code>
5330 var els = Ext.select("#some-el div.some-class");
5331 // or select directly from an existing element
5332 var el = Ext.get('some-el');
5333 el.select('div.some-class');
5334
5335 els.setWidth(100); // all elements become 100 width
5336 els.hide(true); // all elements fade out and hide
5337 // or
5338 els.setWidth(100).hide(true);
5339 </code></pre>
5340  */
5341 Ext.CompositeElementLite = function(els, root){
5342     /**
5343      * <p>The Array of DOM elements which this CompositeElement encapsulates. Read-only.</p>
5344      * <p>This will not <i>usually</i> be accessed in developers' code, but developers wishing
5345      * to augment the capabilities of the CompositeElementLite class may use it when adding
5346      * methods to the class.</p>
5347      * <p>For example to add the <code>nextAll</code> method to the class to <b>add</b> all
5348      * following siblings of selected elements, the code would be</p><code><pre>
5349 Ext.override(Ext.CompositeElementLite, {
5350     nextAll: function() {
5351         var els = this.elements, i, l = els.length, n, r = [], ri = -1;
5352
5353 //      Loop through all elements in this Composite, accumulating
5354 //      an Array of all siblings.
5355         for (i = 0; i < l; i++) {
5356             for (n = els[i].nextSibling; n; n = n.nextSibling) {
5357                 r[++ri] = n;
5358             }
5359         }
5360
5361 //      Add all found siblings to this Composite
5362         return this.add(r);
5363     }
5364 });</pre></code>
5365      * @type Array
5366      * @property elements
5367      */
5368     this.elements = [];
5369     this.add(els, root);
5370     this.el = new Ext.core.Element.Flyweight();
5371 };
5372
5373 Ext.CompositeElementLite.prototype = {
5374     isComposite: true,
5375
5376     // private
5377     getElement : function(el){
5378         // Set the shared flyweight dom property to the current element
5379         var e = this.el;
5380         e.dom = el;
5381         e.id = el.id;
5382         return e;
5383     },
5384
5385     // private
5386     transformElement : function(el){
5387         return Ext.getDom(el);
5388     },
5389
5390     /**
5391      * Returns the number of elements in this Composite.
5392      * @return Number
5393      */
5394     getCount : function(){
5395         return this.elements.length;
5396     },
5397     /**
5398      * Adds elements to this Composite object.
5399      * @param {Mixed} els Either an Array of DOM elements to add, or another Composite object who's elements should be added.
5400      * @return {CompositeElement} This Composite object.
5401      */
5402     add : function(els, root){
5403         var me = this,
5404             elements = me.elements;
5405         if(!els){
5406             return this;
5407         }
5408         if(typeof els == "string"){
5409             els = Ext.core.Element.selectorFunction(els, root);
5410         }else if(els.isComposite){
5411             els = els.elements;
5412         }else if(!Ext.isIterable(els)){
5413             els = [els];
5414         }
5415
5416         for(var i = 0, len = els.length; i < len; ++i){
5417             elements.push(me.transformElement(els[i]));
5418         }
5419         return me;
5420     },
5421
5422     invoke : function(fn, args){
5423         var me = this,
5424             els = me.elements,
5425             len = els.length,
5426             e,
5427             i;
5428
5429         for(i = 0; i < len; i++) {
5430             e = els[i];
5431             if(e){
5432                 Ext.core.Element.prototype[fn].apply(me.getElement(e), args);
5433             }
5434         }
5435         return me;
5436     },
5437     /**
5438      * Returns a flyweight Element of the dom element object at the specified index
5439      * @param {Number} index
5440      * @return {Ext.core.Element}
5441      */
5442     item : function(index){
5443         var me = this,
5444             el = me.elements[index],
5445             out = null;
5446
5447         if(el){
5448             out = me.getElement(el);
5449         }
5450         return out;
5451     },
5452
5453     // fixes scope with flyweight
5454     addListener : function(eventName, handler, scope, opt){
5455         var els = this.elements,
5456             len = els.length,
5457             i, e;
5458
5459         for(i = 0; i<len; i++) {
5460             e = els[i];
5461             if(e) {
5462                 Ext.EventManager.on(e, eventName, handler, scope || e, opt);
5463             }
5464         }
5465         return this;
5466     },
5467     /**
5468      * <p>Calls the passed function for each element in this composite.</p>
5469      * @param {Function} fn The function to call. The function is passed the following parameters:<ul>
5470      * <li><b>el</b> : Element<div class="sub-desc">The current Element in the iteration.
5471      * <b>This is the flyweight (shared) Ext.core.Element instance, so if you require a
5472      * a reference to the dom node, use el.dom.</b></div></li>
5473      * <li><b>c</b> : Composite<div class="sub-desc">This Composite object.</div></li>
5474      * <li><b>idx</b> : Number<div class="sub-desc">The zero-based index in the iteration.</div></li>
5475      * </ul>
5476      * @param {Object} scope (optional) The scope (<i>this</i> reference) in which the function is executed. (defaults to the Element)
5477      * @return {CompositeElement} this
5478      */
5479     each : function(fn, scope){
5480         var me = this,
5481             els = me.elements,
5482             len = els.length,
5483             i, e;
5484
5485         for(i = 0; i<len; i++) {
5486             e = els[i];
5487             if(e){
5488                 e = this.getElement(e);
5489                 if(fn.call(scope || e, e, me, i) === false){
5490                     break;
5491                 }
5492             }
5493         }
5494         return me;
5495     },
5496
5497     /**
5498     * Clears this Composite and adds the elements passed.
5499     * @param {Mixed} els Either an array of DOM elements, or another Composite from which to fill this Composite.
5500     * @return {CompositeElement} this
5501     */
5502     fill : function(els){
5503         var me = this;
5504         me.elements = [];
5505         me.add(els);
5506         return me;
5507     },
5508
5509     /**
5510      * Filters this composite to only elements that match the passed selector.
5511      * @param {String/Function} selector A string CSS selector or a comparison function.
5512      * The comparison function will be called with the following arguments:<ul>
5513      * <li><code>el</code> : Ext.core.Element<div class="sub-desc">The current DOM element.</div></li>
5514      * <li><code>index</code> : Number<div class="sub-desc">The current index within the collection.</div></li>
5515      * </ul>
5516      * @return {CompositeElement} this
5517      */
5518     filter : function(selector){
5519         var els = [],
5520             me = this,
5521             fn = Ext.isFunction(selector) ? selector
5522                 : function(el){
5523                     return el.is(selector);
5524                 };
5525
5526         me.each(function(el, self, i) {
5527             if (fn(el, i) !== false) {
5528                 els[els.length] = me.transformElement(el);
5529             }
5530         });
5531         
5532         me.elements = els;
5533         return me;
5534     },
5535
5536     /**
5537      * Find the index of the passed element within the composite collection.
5538      * @param el {Mixed} The id of an element, or an Ext.core.Element, or an HtmlElement to find within the composite collection.
5539      * @return Number The index of the passed Ext.core.Element in the composite collection, or -1 if not found.
5540      */
5541     indexOf : function(el){
5542         return Ext.Array.indexOf(this.elements, this.transformElement(el));
5543     },
5544
5545     /**
5546     * Replaces the specified element with the passed element.
5547     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
5548     * to replace.
5549     * @param {Mixed} replacement The id of an element or the Element itself.
5550     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
5551     * @return {CompositeElement} this
5552     */
5553     replaceElement : function(el, replacement, domReplace){
5554         var index = !isNaN(el) ? el : this.indexOf(el),
5555             d;
5556         if(index > -1){
5557             replacement = Ext.getDom(replacement);
5558             if(domReplace){
5559                 d = this.elements[index];
5560                 d.parentNode.insertBefore(replacement, d);
5561                 Ext.removeNode(d);
5562             }
5563             this.elements.splice(index, 1, replacement);
5564         }
5565         return this;
5566     },
5567
5568     /**
5569      * Removes all elements.
5570      */
5571     clear : function(){
5572         this.elements = [];
5573     }
5574 };
5575
5576 Ext.CompositeElementLite.prototype.on = Ext.CompositeElementLite.prototype.addListener;
5577
5578 /**
5579  * @private
5580  * Copies all of the functions from Ext.core.Element's prototype onto CompositeElementLite's prototype.
5581  * This is called twice - once immediately below, and once again after additional Ext.core.Element
5582  * are added in Ext JS
5583  */
5584 Ext.CompositeElementLite.importElementMethods = function() {
5585     var fnName,
5586         ElProto = Ext.core.Element.prototype,
5587         CelProto = Ext.CompositeElementLite.prototype;
5588
5589     for (fnName in ElProto) {
5590         if (typeof ElProto[fnName] == 'function'){
5591             (function(fnName) {
5592                 CelProto[fnName] = CelProto[fnName] || function() {
5593                     return this.invoke(fnName, arguments);
5594                 };
5595             }).call(CelProto, fnName);
5596
5597         }
5598     }
5599 };
5600
5601 Ext.CompositeElementLite.importElementMethods();
5602
5603 if(Ext.DomQuery){
5604     Ext.core.Element.selectorFunction = Ext.DomQuery.select;
5605 }
5606
5607 /**
5608  * Selects elements based on the passed CSS selector to enable {@link Ext.core.Element Element} methods
5609  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
5610  * {@link Ext.CompositeElementLite CompositeElementLite} object.
5611  * @param {String/Array} selector The CSS selector or an array of elements
5612  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
5613  * @return {CompositeElementLite/CompositeElement}
5614  * @member Ext.core.Element
5615  * @method select
5616  */
5617 Ext.core.Element.select = function(selector, root){
5618     var els;
5619     if(typeof selector == "string"){
5620         els = Ext.core.Element.selectorFunction(selector, root);
5621     }else if(selector.length !== undefined){
5622         els = selector;
5623     }else{
5624         //<debug>
5625         Ext.Error.raise({
5626             sourceClass: "Ext.core.Element",
5627             sourceMethod: "select",
5628             selector: selector,
5629             root: root,
5630             msg: "Invalid selector specified: " + selector
5631         });
5632         //</debug>
5633     }
5634     return new Ext.CompositeElementLite(els);
5635 };
5636 /**
5637  * Selects elements based on the passed CSS selector to enable {@link Ext.core.Element Element} methods
5638  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
5639  * {@link Ext.CompositeElementLite CompositeElementLite} object.
5640  * @param {String/Array} selector The CSS selector or an array of elements
5641  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
5642  * @return {CompositeElementLite/CompositeElement}
5643  * @member Ext
5644  * @method select
5645  */
5646 Ext.select = Ext.core.Element.select;
5647
5648 /**
5649  * @class Ext.util.DelayedTask
5650  * 
5651  * The DelayedTask class provides a convenient way to "buffer" the execution of a method,
5652  * performing setTimeout where a new timeout cancels the old timeout. When called, the
5653  * task will wait the specified time period before executing. If durng that time period,
5654  * the task is called again, the original call will be cancelled. This continues so that
5655  * the function is only called a single time for each iteration.
5656  * 
5657  * This method is especially useful for things like detecting whether a user has finished
5658  * typing in a text field. An example would be performing validation on a keypress. You can
5659  * use this class to buffer the keypress events for a certain number of milliseconds, and
5660  * perform only if they stop for that amount of time.  
5661  * 
5662  * ## Usage
5663  * 
5664  *     var task = new Ext.util.DelayedTask(function(){
5665  *         alert(Ext.getDom('myInputField').value.length);
5666  *     });
5667  *     
5668  *     // Wait 500ms before calling our function. If the user presses another key
5669  *     // during that 500ms, it will be cancelled and we'll wait another 500ms.
5670  *     Ext.get('myInputField').on('keypress', function(){
5671  *         task.{@link #delay}(500);
5672  *     });
5673  * 
5674  * Note that we are using a DelayedTask here to illustrate a point. The configuration
5675  * option `buffer` for {@link Ext.util.Observable#addListener addListener/on} will
5676  * also setup a delayed task for you to buffer events.
5677  * 
5678  * @constructor The parameters to this constructor serve as defaults and are not required.
5679  * @param {Function} fn (optional) The default function to call.
5680  * @param {Object} scope The default scope (The <code><b>this</b></code> reference) in which the
5681  * function is called. If not specified, <code>this</code> will refer to the browser window.
5682  * @param {Array} args (optional) The default Array of arguments.
5683  */
5684 Ext.util.DelayedTask = function(fn, scope, args) {
5685     var me = this,
5686         id,
5687         call = function() {
5688             clearInterval(id);
5689             id = null;
5690             fn.apply(scope, args || []);
5691         };
5692
5693     /**
5694      * Cancels any pending timeout and queues a new one
5695      * @param {Number} delay The milliseconds to delay
5696      * @param {Function} newFn (optional) Overrides function passed to constructor
5697      * @param {Object} newScope (optional) Overrides scope passed to constructor. Remember that if no scope
5698      * is specified, <code>this</code> will refer to the browser window.
5699      * @param {Array} newArgs (optional) Overrides args passed to constructor
5700      */
5701     this.delay = function(delay, newFn, newScope, newArgs) {
5702         me.cancel();
5703         fn = newFn || fn;
5704         scope = newScope || scope;
5705         args = newArgs || args;
5706         id = setInterval(call, delay);
5707     };
5708
5709     /**
5710      * Cancel the last queued timeout
5711      */
5712     this.cancel = function(){
5713         if (id) {
5714             clearInterval(id);
5715             id = null;
5716         }
5717     };
5718 };
5719 Ext.require('Ext.util.DelayedTask', function() {
5720
5721     Ext.util.Event = Ext.extend(Object, (function() {
5722         function createBuffered(handler, listener, o, scope) {
5723             listener.task = new Ext.util.DelayedTask();
5724             return function() {
5725                 listener.task.delay(o.buffer, handler, scope, Ext.Array.toArray(arguments));
5726             };
5727         }
5728
5729         function createDelayed(handler, listener, o, scope) {
5730             return function() {
5731                 var task = new Ext.util.DelayedTask();
5732                 if (!listener.tasks) {
5733                     listener.tasks = [];
5734                 }
5735                 listener.tasks.push(task);
5736                 task.delay(o.delay || 10, handler, scope, Ext.Array.toArray(arguments));
5737             };
5738         }
5739
5740         function createSingle(handler, listener, o, scope) {
5741             return function() {
5742                 listener.ev.removeListener(listener.fn, scope);
5743                 return handler.apply(scope, arguments);
5744             };
5745         }
5746
5747         return {
5748             isEvent: true,
5749
5750             constructor: function(observable, name) {
5751                 this.name = name;
5752                 this.observable = observable;
5753                 this.listeners = [];
5754             },
5755
5756             addListener: function(fn, scope, options) {
5757                 var me = this,
5758                     listener;
5759                     scope = scope || me.observable;
5760
5761                 //<debug error>
5762                 if (!fn) {
5763                     Ext.Error.raise({
5764                         sourceClass: Ext.getClassName(this.observable),
5765                         sourceMethod: "addListener",
5766                         msg: "The specified callback function is undefined"
5767                     });
5768                 }
5769                 //</debug>
5770
5771                 if (!me.isListening(fn, scope)) {
5772                     listener = me.createListener(fn, scope, options);
5773                     if (me.firing) {
5774                         // if we are currently firing this event, don't disturb the listener loop
5775                         me.listeners = me.listeners.slice(0);
5776                     }
5777                     me.listeners.push(listener);
5778                 }
5779             },
5780
5781             createListener: function(fn, scope, o) {
5782                 o = o || {};
5783                 scope = scope || this.observable;
5784
5785                 var listener = {
5786                         fn: fn,
5787                         scope: scope,
5788                         o: o,
5789                         ev: this
5790                     },
5791                     handler = fn;
5792
5793                 // The order is important. The 'single' wrapper must be wrapped by the 'buffer' and 'delayed' wrapper
5794                 // because the event removal that the single listener does destroys the listener's DelayedTask(s)
5795                 if (o.single) {
5796                     handler = createSingle(handler, listener, o, scope);
5797                 }
5798                 if (o.delay) {
5799                     handler = createDelayed(handler, listener, o, scope);
5800                 }
5801                 if (o.buffer) {
5802                     handler = createBuffered(handler, listener, o, scope);
5803                 }
5804
5805                 listener.fireFn = handler;
5806                 return listener;
5807             },
5808
5809             findListener: function(fn, scope) {
5810                 var listeners = this.listeners,
5811                 i = listeners.length,
5812                 listener,
5813                 s;
5814
5815                 while (i--) {
5816                     listener = listeners[i];
5817                     if (listener) {
5818                         s = listener.scope;
5819                         if (listener.fn == fn && (s == scope || s == this.observable)) {
5820                             return i;
5821                         }
5822                     }
5823                 }
5824
5825                 return - 1;
5826             },
5827
5828             isListening: function(fn, scope) {
5829                 return this.findListener(fn, scope) !== -1;
5830             },
5831
5832             removeListener: function(fn, scope) {
5833                 var me = this,
5834                     index,
5835                     listener,
5836                     k;
5837                 index = me.findListener(fn, scope);
5838                 if (index != -1) {
5839                     listener = me.listeners[index];
5840
5841                     if (me.firing) {
5842                         me.listeners = me.listeners.slice(0);
5843                     }
5844
5845                     // cancel and remove a buffered handler that hasn't fired yet
5846                     if (listener.task) {
5847                         listener.task.cancel();
5848                         delete listener.task;
5849                     }
5850
5851                     // cancel and remove all delayed handlers that haven't fired yet
5852                     k = listener.tasks && listener.tasks.length;
5853                     if (k) {
5854                         while (k--) {
5855                             listener.tasks[k].cancel();
5856                         }
5857                         delete listener.tasks;
5858                     }
5859
5860                     // remove this listener from the listeners array
5861                     me.listeners.splice(index, 1);
5862                     return true;
5863                 }
5864
5865                 return false;
5866             },
5867
5868             // Iterate to stop any buffered/delayed events
5869             clearListeners: function() {
5870                 var listeners = this.listeners,
5871                     i = listeners.length;
5872
5873                 while (i--) {
5874                     this.removeListener(listeners[i].fn, listeners[i].scope);
5875                 }
5876             },
5877
5878             fire: function() {
5879                 var me = this,
5880                     listeners = me.listeners,
5881                     count = listeners.length,
5882                     i,
5883                     args,
5884                     listener;
5885
5886                 if (count > 0) {
5887                     me.firing = true;
5888                     for (i = 0; i < count; i++) {
5889                         listener = listeners[i];
5890                         args = arguments.length ? Array.prototype.slice.call(arguments, 0) : [];
5891                         if (listener.o) {
5892                             args.push(listener.o);
5893                         }
5894                         if (listener && listener.fireFn.apply(listener.scope || me.observable, args) === false) {
5895                             return (me.firing = false);
5896                         }
5897                     }
5898                 }
5899                 me.firing = false;
5900                 return true;
5901             }
5902         };
5903     })());
5904 });
5905
5906 /**
5907  * @class Ext.EventManager
5908  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides
5909  * several useful events directly.
5910  * See {@link Ext.EventObject} for more details on normalized event objects.
5911  * @singleton
5912  */
5913 Ext.EventManager = {
5914
5915     // --------------------- onReady ---------------------
5916
5917     /**
5918      * Check if we have bound our global onReady listener
5919      * @private
5920      */
5921     hasBoundOnReady: false,
5922
5923     /**
5924      * Check if fireDocReady has been called
5925      * @private
5926      */
5927     hasFiredReady: false,
5928
5929     /**
5930      * Timer for the document ready event in old IE versions
5931      * @private
5932      */
5933     readyTimeout: null,
5934
5935     /**
5936      * Checks if we have bound an onreadystatechange event
5937      * @private
5938      */
5939     hasOnReadyStateChange: false,
5940
5941     /**
5942      * Holds references to any onReady functions
5943      * @private
5944      */
5945     readyEvent: new Ext.util.Event(),
5946
5947     /**
5948      * Check the ready state for old IE versions
5949      * @private
5950      * @return {Boolean} True if the document is ready
5951      */
5952     checkReadyState: function(){
5953         var me = Ext.EventManager;
5954
5955         if(window.attachEvent){
5956             // See here for reference: http://javascript.nwbox.com/IEContentLoaded/
5957             if (window != top) {
5958                 return false;
5959             }
5960             try{
5961                 document.documentElement.doScroll('left');
5962             }catch(e){
5963                 return false;
5964             }
5965             me.fireDocReady();
5966             return true;
5967         }
5968         if (document.readyState == 'complete') {
5969             me.fireDocReady();
5970             return true;
5971         }
5972         me.readyTimeout = setTimeout(arguments.callee, 2);
5973         return false;
5974     },
5975
5976     /**
5977      * Binds the appropriate browser event for checking if the DOM has loaded.
5978      * @private
5979      */
5980     bindReadyEvent: function(){
5981         var me = Ext.EventManager;
5982         if (me.hasBoundOnReady) {
5983             return;
5984         }
5985
5986         if (document.addEventListener) {
5987             document.addEventListener('DOMContentLoaded', me.fireDocReady, false);
5988             // fallback, load will ~always~ fire
5989             window.addEventListener('load', me.fireDocReady, false);
5990         } else {
5991             // check if the document is ready, this will also kick off the scroll checking timer
5992             if (!me.checkReadyState()) {
5993                 document.attachEvent('onreadystatechange', me.checkReadyState);
5994                 me.hasOnReadyStateChange = true;
5995             }
5996             // fallback, onload will ~always~ fire
5997             window.attachEvent('onload', me.fireDocReady, false);
5998         }
5999         me.hasBoundOnReady = true;
6000     },
6001
6002     /**
6003      * We know the document is loaded, so trigger any onReady events.
6004      * @private
6005      */
6006     fireDocReady: function(){
6007         var me = Ext.EventManager;
6008
6009         // only unbind these events once
6010         if (!me.hasFiredReady) {
6011             me.hasFiredReady = true;
6012
6013             if (document.addEventListener) {
6014                 document.removeEventListener('DOMContentLoaded', me.fireDocReady, false);
6015                 window.removeEventListener('load', me.fireDocReady, false);
6016             } else {
6017                 if (me.readyTimeout !== null) {
6018                     clearTimeout(me.readyTimeout);
6019                 }
6020                 if (me.hasOnReadyStateChange) {
6021                     document.detachEvent('onreadystatechange', me.checkReadyState);
6022                 }
6023                 window.detachEvent('onload', me.fireDocReady);
6024             }
6025             Ext.supports.init();
6026         }
6027         if (!Ext.isReady) {
6028             Ext.isReady = true;
6029             me.onWindowUnload();
6030             me.readyEvent.fire();
6031         }
6032     },
6033
6034     /**
6035      * Adds a listener to be notified when the document is ready (before onload and before images are loaded). Can be
6036      * accessed shorthanded as Ext.onReady().
6037      * @param {Function} fn The method the event invokes.
6038      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
6039      * @param {boolean} options (optional) Options object as passed to {@link Ext.core.Element#addListener}.
6040      */
6041     onDocumentReady: function(fn, scope, options){
6042         options = options || {};
6043         var me = Ext.EventManager,
6044             readyEvent = me.readyEvent;
6045
6046         // force single to be true so our event is only ever fired once.
6047         options.single = true;
6048
6049         // Document already loaded, let's just fire it
6050         if (Ext.isReady) {
6051             readyEvent.addListener(fn, scope, options);
6052             readyEvent.fire();
6053         } else {
6054             options.delay = options.delay || 1;
6055             readyEvent.addListener(fn, scope, options);
6056             me.bindReadyEvent();
6057         }
6058     },
6059
6060
6061     // --------------------- event binding ---------------------
6062
6063     /**
6064      * Contains a list of all document mouse downs, so we can ensure they fire even when stopEvent is called.
6065      * @private
6066      */
6067     stoppedMouseDownEvent: new Ext.util.Event(),
6068
6069     /**
6070      * Options to parse for the 4th argument to addListener.
6071      * @private
6072      */
6073     propRe: /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate|freezeEvent)$/,
6074
6075     /**
6076      * Get the id of the element. If one has not been assigned, automatically assign it.
6077      * @param {Mixed} element The element to get the id for.
6078      * @return {String} id
6079      */
6080     getId : function(element) {
6081         var skipGarbageCollection = false,
6082             id;
6083     
6084         element = Ext.getDom(element);
6085     
6086         if (element === document || element === window) {
6087             id = element === document ? Ext.documentId : Ext.windowId;
6088         }
6089         else {
6090             id = Ext.id(element);
6091         }
6092         // skip garbage collection for special elements (window, document, iframes)
6093         if (element && (element.getElementById || element.navigator)) {
6094             skipGarbageCollection = true;
6095         }
6096     
6097         if (!Ext.cache[id]){
6098             Ext.core.Element.addToCache(new Ext.core.Element(element), id);
6099             if (skipGarbageCollection) {
6100                 Ext.cache[id].skipGarbageCollection = true;
6101             }
6102         }
6103         return id;
6104     },
6105
6106     /**
6107      * Convert a "config style" listener into a set of flat arguments so they can be passed to addListener
6108      * @private
6109      * @param {Object} element The element the event is for
6110      * @param {Object} event The event configuration
6111      * @param {Object} isRemove True if a removal should be performed, otherwise an add will be done.
6112      */
6113     prepareListenerConfig: function(element, config, isRemove){
6114         var me = this,
6115             propRe = me.propRe,
6116             key, value, args;
6117
6118         // loop over all the keys in the object
6119         for (key in config) {
6120             if (config.hasOwnProperty(key)) {
6121                 // if the key is something else then an event option
6122                 if (!propRe.test(key)) {
6123                     value = config[key];
6124                     // if the value is a function it must be something like click: function(){}, scope: this
6125                     // which means that there might be multiple event listeners with shared options
6126                     if (Ext.isFunction(value)) {
6127                         // shared options
6128                         args = [element, key, value, config.scope, config];
6129                     } else {
6130                         // if its not a function, it must be an object like click: {fn: function(){}, scope: this}
6131                         args = [element, key, value.fn, value.scope, value];
6132                     }
6133
6134                     if (isRemove === true) {
6135                         me.removeListener.apply(this, args);
6136                     } else {
6137                         me.addListener.apply(me, args);
6138                     }
6139                 }
6140             }
6141         }
6142     },
6143
6144     /**
6145      * Normalize cross browser event differences
6146      * @private
6147      * @param {Object} eventName The event name
6148      * @param {Object} fn The function to execute
6149      * @return {Object} The new event name/function
6150      */
6151     normalizeEvent: function(eventName, fn){
6152         if (/mouseenter|mouseleave/.test(eventName) && !Ext.supports.MouseEnterLeave) {
6153             if (fn) {
6154                 fn = Ext.Function.createInterceptor(fn, this.contains, this);
6155             }
6156             eventName = eventName == 'mouseenter' ? 'mouseover' : 'mouseout';
6157         } else if (eventName == 'mousewheel' && !Ext.supports.MouseWheel && !Ext.isOpera){
6158             eventName = 'DOMMouseScroll';
6159         }
6160         return {
6161             eventName: eventName,
6162             fn: fn
6163         };
6164     },
6165
6166     /**
6167      * Checks whether the event's relatedTarget is contained inside (or <b>is</b>) the element.
6168      * @private
6169      * @param {Object} event
6170      */
6171     contains: function(event){
6172         var parent = event.browserEvent.currentTarget,
6173             child = this.getRelatedTarget(event);
6174
6175         if (parent && parent.firstChild) {
6176             while (child) {
6177                 if (child === parent) {
6178                     return false;
6179                 }
6180                 child = child.parentNode;
6181                 if (child && (child.nodeType != 1)) {
6182                     child = null;
6183                 }
6184             }
6185         }
6186         return true;
6187     },
6188
6189     /**
6190     * Appends an event handler to an element.  The shorthand version {@link #on} is equivalent.  Typically you will
6191     * use {@link Ext.core.Element#addListener} directly on an Element in favor of calling this version.
6192     * @param {String/HTMLElement} el The html element or id to assign the event handler to.
6193     * @param {String} eventName The name of the event to listen for.
6194     * @param {Function} handler The handler function the event invokes. This function is passed
6195     * the following parameters:<ul>
6196     * <li>evt : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
6197     * <li>t : Element<div class="sub-desc">The {@link Ext.core.Element Element} which was the target of the event.
6198     * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
6199     * <li>o : Object<div class="sub-desc">The options object from the addListener call.</div></li>
6200     * </ul>
6201     * @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>.
6202     * @param {Object} options (optional) An object containing handler configuration properties.
6203     * This may contain any of the following properties:<ul>
6204     * <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>
6205     * <li>delegate : String<div class="sub-desc">A simple selector to filter the target or look for a descendant of the target</div></li>
6206     * <li>stopEvent : Boolean<div class="sub-desc">True to stop the event. That is stop propagation, and prevent the default action.</div></li>
6207     * <li>preventDefault : Boolean<div class="sub-desc">True to prevent the default action</div></li>
6208     * <li>stopPropagation : Boolean<div class="sub-desc">True to prevent event propagation</div></li>
6209     * <li>normalized : Boolean<div class="sub-desc">False to pass a browser event to the handler function instead of an Ext.EventObject</div></li>
6210     * <li>delay : Number<div class="sub-desc">The number of milliseconds to delay the invocation of the handler after te event fires.</div></li>
6211     * <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>
6212     * <li>buffer : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
6213     * by the specified number of milliseconds. If the event fires again within that time, the original
6214     * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
6215     * <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>
6216     * </ul><br>
6217     * <p>See {@link Ext.core.Element#addListener} for examples of how to use these options.</p>
6218     */
6219     addListener: function(element, eventName, fn, scope, options){
6220         // Check if we've been passed a "config style" event.
6221         if (Ext.isObject(eventName)) {
6222             this.prepareListenerConfig(element, eventName);
6223             return;
6224         }
6225
6226         var dom = Ext.getDom(element),
6227             bind,
6228             wrap;
6229
6230         //<debug>
6231         if (!dom){
6232             Ext.Error.raise({
6233                 sourceClass: 'Ext.EventManager',
6234                 sourceMethod: 'addListener',
6235                 targetElement: element,
6236                 eventName: eventName,
6237                 msg: 'Error adding "' + eventName + '\" listener for nonexistent element "' + element + '"'
6238             });
6239         }
6240         if (!fn) {
6241             Ext.Error.raise({
6242                 sourceClass: 'Ext.EventManager',
6243                 sourceMethod: 'addListener',
6244                 targetElement: element,
6245                 eventName: eventName,
6246                 msg: 'Error adding "' + eventName + '\" listener. The handler function is undefined.'
6247             });
6248         }
6249         //</debug>
6250
6251         // create the wrapper function
6252         options = options || {};
6253
6254         bind = this.normalizeEvent(eventName, fn);
6255         wrap = this.createListenerWrap(dom, eventName, bind.fn, scope, options);
6256
6257
6258         if (dom.attachEvent) {
6259             dom.attachEvent('on' + bind.eventName, wrap);
6260         } else {
6261             dom.addEventListener(bind.eventName, wrap, options.capture || false);
6262         }
6263
6264         if (dom == document && eventName == 'mousedown') {
6265             this.stoppedMouseDownEvent.addListener(wrap);
6266         }
6267
6268         // add all required data into the event cache
6269         this.getEventListenerCache(dom, eventName).push({
6270             fn: fn,
6271             wrap: wrap,
6272             scope: scope
6273         });
6274     },
6275
6276     /**
6277     * Removes an event handler from an element.  The shorthand version {@link #un} is equivalent.  Typically
6278     * you will use {@link Ext.core.Element#removeListener} directly on an Element in favor of calling this version.
6279     * @param {String/HTMLElement} el The id or html element from which to remove the listener.
6280     * @param {String} eventName The name of the event.
6281     * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
6282     * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
6283     * then this must refer to the same object.
6284     */
6285     removeListener : function(element, eventName, fn, scope) {
6286         // handle our listener config object syntax
6287         if (Ext.isObject(eventName)) {
6288             this.prepareListenerConfig(element, eventName, true);
6289             return;
6290         }
6291
6292         var dom = Ext.getDom(element),
6293             cache = this.getEventListenerCache(dom, eventName),
6294             bindName = this.normalizeEvent(eventName).eventName,
6295             i = cache.length, j,
6296             listener, wrap, tasks;
6297
6298
6299         while (i--) {
6300             listener = cache[i];
6301
6302             if (listener && (!fn || listener.fn == fn) && (!scope || listener.scope === scope)) {
6303                 wrap = listener.wrap;
6304
6305                 // clear buffered calls
6306                 if (wrap.task) {
6307                     clearTimeout(wrap.task);
6308                     delete wrap.task;
6309                 }
6310
6311                 // clear delayed calls
6312                 j = wrap.tasks && wrap.tasks.length;
6313                 if (j) {
6314                     while (j--) {
6315                         clearTimeout(wrap.tasks[j]);
6316                     }
6317                     delete wrap.tasks;
6318                 }
6319
6320                 if (dom.detachEvent) {
6321                     dom.detachEvent('on' + bindName, wrap);
6322                 } else {
6323                     dom.removeEventListener(bindName, wrap, false);
6324                 }
6325
6326                 if (wrap && dom == document && eventName == 'mousedown') {
6327                     this.stoppedMouseDownEvent.removeListener(wrap);
6328                 }
6329
6330                 // remove listener from cache
6331                 cache.splice(i, 1);
6332             }
6333         }
6334     },
6335
6336     /**
6337     * Removes all event handers from an element.  Typically you will use {@link Ext.core.Element#removeAllListeners}
6338     * directly on an Element in favor of calling this version.
6339     * @param {String/HTMLElement} el The id or html element from which to remove all event handlers.
6340     */
6341     removeAll : function(element){
6342         var dom = Ext.getDom(element),
6343             cache, ev;
6344         if (!dom) {
6345             return;
6346         }
6347         cache = this.getElementEventCache(dom);
6348
6349         for (ev in cache) {
6350             if (cache.hasOwnProperty(ev)) {
6351                 this.removeListener(dom, ev);
6352             }
6353         }
6354         Ext.cache[dom.id].events = {};
6355     },
6356
6357     /**
6358      * Recursively removes all previous added listeners from an element and its children. Typically you will use {@link Ext.core.Element#purgeAllListeners}
6359      * directly on an Element in favor of calling this version.
6360      * @param {String/HTMLElement} el The id or html element from which to remove all event handlers.
6361      * @param {String} eventName (optional) The name of the event.
6362      */
6363     purgeElement : function(element, eventName) {
6364         var dom = Ext.getDom(element),
6365             i = 0, len;
6366
6367         if(eventName) {
6368             this.removeListener(dom, eventName);
6369         }
6370         else {
6371             this.removeAll(dom);
6372         }
6373
6374         if(dom && dom.childNodes) {
6375             for(len = element.childNodes.length; i < len; i++) {
6376                 this.purgeElement(element.childNodes[i], eventName);
6377             }
6378         }
6379     },
6380
6381     /**
6382      * Create the wrapper function for the event
6383      * @private
6384      * @param {HTMLElement} dom The dom element
6385      * @param {String} ename The event name
6386      * @param {Function} fn The function to execute
6387      * @param {Object} scope The scope to execute callback in
6388      * @param {Object} options The options
6389      * @return {Function} the wrapper function
6390      */
6391     createListenerWrap : function(dom, ename, fn, scope, options) {
6392         options = !Ext.isObject(options) ? {} : options;
6393
6394         var f, gen;
6395
6396         return function wrap(e, args) {
6397             // Compile the implementation upon first firing
6398             if (!gen) {
6399                 f = ['if(!Ext) {return;}'];
6400
6401                 if(options.buffer || options.delay || options.freezeEvent) {
6402                     f.push('e = new Ext.EventObjectImpl(e, ' + (options.freezeEvent ? 'true' : 'false' ) + ');');
6403                 } else {
6404                     f.push('e = Ext.EventObject.setEvent(e);');
6405                 }
6406
6407                 if (options.delegate) {
6408                     f.push('var t = e.getTarget("' + options.delegate + '", this);');
6409                     f.push('if(!t) {return;}');
6410                 } else {
6411                     f.push('var t = e.target;');
6412                 }
6413
6414                 if (options.target) {
6415                     f.push('if(e.target !== options.target) {return;}');
6416                 }
6417
6418                 if(options.stopEvent) {
6419                     f.push('e.stopEvent();');
6420                 } else {
6421                     if(options.preventDefault) {
6422                         f.push('e.preventDefault();');
6423                     }
6424                     if(options.stopPropagation) {
6425                         f.push('e.stopPropagation();');
6426                     }
6427                 }
6428
6429                 if(options.normalized === false) {
6430                     f.push('e = e.browserEvent;');
6431                 }
6432
6433                 if(options.buffer) {
6434                     f.push('(wrap.task && clearTimeout(wrap.task));');
6435                     f.push('wrap.task = setTimeout(function(){');
6436                 }
6437
6438                 if(options.delay) {
6439                     f.push('wrap.tasks = wrap.tasks || [];');
6440                     f.push('wrap.tasks.push(setTimeout(function(){');
6441                 }
6442
6443                 // finally call the actual handler fn
6444                 f.push('fn.call(scope || dom, e, t, options);');
6445
6446                 if(options.single) {
6447                     f.push('Ext.EventManager.removeListener(dom, ename, fn, scope);');
6448                 }
6449
6450                 if(options.delay) {
6451                     f.push('}, ' + options.delay + '));');
6452                 }
6453
6454                 if(options.buffer) {
6455                     f.push('}, ' + options.buffer + ');');
6456                 }
6457
6458                 gen = Ext.functionFactory('e', 'options', 'fn', 'scope', 'ename', 'dom', 'wrap', 'args', f.join('\n'));
6459             }
6460
6461             gen.call(dom, e, options, fn, scope, ename, dom, wrap, args);
6462         };
6463     },
6464
6465     /**
6466      * Get the event cache for a particular element for a particular event
6467      * @private
6468      * @param {HTMLElement} element The element
6469      * @param {Object} eventName The event name
6470      * @return {Array} The events for the element
6471      */
6472     getEventListenerCache : function(element, eventName) {
6473         var eventCache = this.getElementEventCache(element);
6474         return eventCache[eventName] || (eventCache[eventName] = []);
6475     },
6476
6477     /**
6478      * Gets the event cache for the object
6479      * @private
6480      * @param {HTMLElement} element The element
6481      * @return {Object} The event cache for the object
6482      */
6483     getElementEventCache : function(element) {
6484         var elementCache = Ext.cache[this.getId(element)];
6485         return elementCache.events || (elementCache.events = {});
6486     },
6487
6488     // --------------------- utility methods ---------------------
6489     mouseLeaveRe: /(mouseout|mouseleave)/,
6490     mouseEnterRe: /(mouseover|mouseenter)/,
6491
6492     /**
6493      * Stop the event (preventDefault and stopPropagation)
6494      * @param {Event} The event to stop
6495      */
6496     stopEvent: function(event) {
6497         this.stopPropagation(event);
6498         this.preventDefault(event);
6499     },
6500
6501     /**
6502      * Cancels bubbling of the event.
6503      * @param {Event} The event to stop bubbling.
6504      */
6505     stopPropagation: function(event) {
6506         event = event.browserEvent || event;
6507         if (event.stopPropagation) {
6508             event.stopPropagation();
6509         } else {
6510             event.cancelBubble = true;
6511         }
6512     },
6513
6514     /**
6515      * Prevents the browsers default handling of the event.
6516      * @param {Event} The event to prevent the default
6517      */
6518     preventDefault: function(event) {
6519         event = event.browserEvent || event;
6520         if (event.preventDefault) {
6521             event.preventDefault();
6522         } else {
6523             event.returnValue = false;
6524             // Some keys events require setting the keyCode to -1 to be prevented
6525             try {
6526               // all ctrl + X and F1 -> F12
6527               if (event.ctrlKey || event.keyCode > 111 && event.keyCode < 124) {
6528                   event.keyCode = -1;
6529               }
6530             } catch (e) {
6531                 // see this outdated document http://support.microsoft.com/kb/934364/en-us for more info
6532             }
6533         }
6534     },
6535
6536     /**
6537      * Gets the related target from the event.
6538      * @param {Object} event The event
6539      * @return {HTMLElement} The related target.
6540      */
6541     getRelatedTarget: function(event) {
6542         event = event.browserEvent || event;
6543         var target = event.relatedTarget;
6544         if (!target) {
6545             if (this.mouseLeaveRe.test(event.type)) {
6546                 target = event.toElement;
6547             } else if (this.mouseEnterRe.test(event.type)) {
6548                 target = event.fromElement;
6549             }
6550         }
6551         return this.resolveTextNode(target);
6552     },
6553
6554     /**
6555      * Gets the x coordinate from the event
6556      * @param {Object} event The event
6557      * @return {Number} The x coordinate
6558      */
6559     getPageX: function(event) {
6560         return this.getXY(event)[0];
6561     },
6562
6563     /**
6564      * Gets the y coordinate from the event
6565      * @param {Object} event The event
6566      * @return {Number} The y coordinate
6567      */
6568     getPageY: function(event) {
6569         return this.getXY(event)[1];
6570     },
6571
6572     /**
6573      * Gets the x & ycoordinate from the event
6574      * @param {Object} event The event
6575      * @return {Array} The x/y coordinate
6576      */
6577     getPageXY: function(event) {
6578         event = event.browserEvent || event;
6579         var x = event.pageX,
6580             y = event.pageY,
6581             doc = document.documentElement,
6582             body = document.body;
6583
6584         // pageX/pageY not available (undefined, not null), use clientX/clientY instead
6585         if (!x && x !== 0) {
6586             x = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
6587             y = event.clientY + (doc && doc.scrollTop  || body && body.scrollTop  || 0) - (doc && doc.clientTop  || body && body.clientTop  || 0);
6588         }
6589         return [x, y];
6590     },
6591
6592     /**
6593      * Gets the target of the event.
6594      * @param {Object} event The event
6595      * @return {HTMLElement} target
6596      */
6597     getTarget: function(event) {
6598         event = event.browserEvent || event;
6599         return this.resolveTextNode(event.target || event.srcElement);
6600     },
6601
6602     /**
6603      * Resolve any text nodes accounting for browser differences.
6604      * @private
6605      * @param {HTMLElement} node The node
6606      * @return {HTMLElement} The resolved node
6607      */
6608     // 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.
6609     resolveTextNode: Ext.isGecko ?
6610         function(node) {
6611             if (!node) {
6612                 return;
6613             }
6614             // work around firefox bug, https://bugzilla.mozilla.org/show_bug.cgi?id=101197
6615             var s = HTMLElement.prototype.toString.call(node);
6616             if (s == '[xpconnect wrapped native prototype]' || s == '[object XULElement]') {
6617                 return;
6618             }
6619                 return node.nodeType == 3 ? node.parentNode: node;
6620             }: function(node) {
6621                 return node && node.nodeType == 3 ? node.parentNode: node;
6622             },
6623
6624     // --------------------- custom event binding ---------------------
6625
6626     // Keep track of the current width/height
6627     curWidth: 0,
6628     curHeight: 0,
6629
6630     /**
6631      * Adds a listener to be notified when the browser window is resized and provides resize event buffering (100 milliseconds),
6632      * passes new viewport width and height to handlers.
6633      * @param {Function} fn      The handler function the window resize event invokes.
6634      * @param {Object}   scope   The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
6635      * @param {boolean}  options Options object as passed to {@link Ext.core.Element#addListener}
6636      */
6637     onWindowResize: function(fn, scope, options){
6638         var resize = this.resizeEvent;
6639         if(!resize){
6640             this.resizeEvent = resize = new Ext.util.Event();
6641             this.on(window, 'resize', this.fireResize, this, {buffer: 100});
6642         }
6643         resize.addListener(fn, scope, options);
6644     },
6645
6646     /**
6647      * Fire the resize event.
6648      * @private
6649      */
6650     fireResize: function(){
6651         var me = this,
6652             w = Ext.core.Element.getViewWidth(),
6653             h = Ext.core.Element.getViewHeight();
6654
6655          //whacky problem in IE where the resize event will sometimes fire even though the w/h are the same.
6656          if(me.curHeight != h || me.curWidth != w){
6657              me.curHeight = h;
6658              me.curWidth = w;
6659              me.resizeEvent.fire(w, h);
6660          }
6661     },
6662
6663     /**
6664      * Removes the passed window resize listener.
6665      * @param {Function} fn        The method the event invokes
6666      * @param {Object}   scope    The scope of handler
6667      */
6668     removeResizeListener: function(fn, scope){
6669         if (this.resizeEvent) {
6670             this.resizeEvent.removeListener(fn, scope);
6671         }
6672     },
6673
6674     onWindowUnload: function() {
6675         var unload = this.unloadEvent;
6676         if (!unload) {
6677             this.unloadEvent = unload = new Ext.util.Event();
6678             this.addListener(window, 'unload', this.fireUnload, this);
6679         }
6680     },
6681
6682     /**
6683      * Fires the unload event for items bound with onWindowUnload
6684      * @private
6685      */
6686     fireUnload: function() {
6687         // wrap in a try catch, could have some problems during unload
6688         try {
6689             this.removeUnloadListener();
6690             // Work around FF3 remembering the last scroll position when refreshing the grid and then losing grid view
6691             if (Ext.isGecko3) {
6692                 var gridviews = Ext.ComponentQuery.query('gridview'),
6693                     i = 0,
6694                     ln = gridviews.length;
6695                 for (; i < ln; i++) {
6696                     gridviews[i].scrollToTop();
6697                 }
6698             }
6699             // Purge all elements in the cache
6700             var el,
6701                 cache = Ext.cache;
6702             for (el in cache) {
6703                 if (cache.hasOwnProperty(el)) {
6704                     Ext.EventManager.removeAll(el);
6705                 }
6706             }
6707         } catch(e) {
6708         }
6709     },
6710
6711     /**
6712      * Removes the passed window unload listener.
6713      * @param {Function} fn        The method the event invokes
6714      * @param {Object}   scope    The scope of handler
6715      */
6716     removeUnloadListener: function(){
6717         if (this.unloadEvent) {
6718             this.removeListener(window, 'unload', this.fireUnload);
6719         }
6720     },
6721
6722     /**
6723      * note 1: IE fires ONLY the keydown event on specialkey autorepeat
6724      * note 2: Safari < 3.1, Gecko (Mac/Linux) & Opera fire only the keypress event on specialkey autorepeat
6725      * (research done by @Jan Wolter at http://unixpapa.com/js/key.html)
6726      * @private
6727      */
6728     useKeyDown: Ext.isWebKit ?
6729                    parseInt(navigator.userAgent.match(/AppleWebKit\/(\d+)/)[1], 10) >= 525 :
6730                    !((Ext.isGecko && !Ext.isWindows) || Ext.isOpera),
6731
6732     /**
6733      * Indicates which event to use for getting key presses.
6734      * @return {String} The appropriate event name.
6735      */
6736     getKeyEvent: function(){
6737         return this.useKeyDown ? 'keydown' : 'keypress';
6738     }
6739 };
6740
6741 /**
6742  * Alias for {@link Ext.Loader#onReady Ext.Loader.onReady} with withDomReady set to true
6743  * @member Ext
6744  * @method onReady
6745  */
6746 Ext.onReady = function(fn, scope, options) {
6747     Ext.Loader.onReady(fn, scope, true, options);
6748 };
6749
6750 /**
6751  * Alias for {@link Ext.EventManager#onDocumentReady Ext.EventManager.onDocumentReady}
6752  * @member Ext
6753  * @method onDocumentReady
6754  */
6755 Ext.onDocumentReady = Ext.EventManager.onDocumentReady;
6756
6757 /**
6758  * Alias for {@link Ext.EventManager#addListener Ext.EventManager.addListener}
6759  * @member Ext.EventManager
6760  * @method on
6761  */
6762 Ext.EventManager.on = Ext.EventManager.addListener;
6763
6764 /**
6765  * Alias for {@link Ext.EventManager#removeListener Ext.EventManager.removeListener}
6766  * @member Ext.EventManager
6767  * @method un
6768  */
6769 Ext.EventManager.un = Ext.EventManager.removeListener;
6770
6771 (function(){
6772     var initExtCss = function() {
6773         // find the body element
6774         var bd = document.body || document.getElementsByTagName('body')[0],
6775             baseCSSPrefix = Ext.baseCSSPrefix,
6776             cls = [],
6777             htmlCls = [],
6778             html;
6779
6780         if (!bd) {
6781             return false;
6782         }
6783
6784         html = bd.parentNode;
6785
6786         //Let's keep this human readable!
6787         if (Ext.isIE) {
6788             cls.push(baseCSSPrefix + 'ie');
6789         }
6790         if (Ext.isIE6) {
6791             cls.push(baseCSSPrefix + 'ie6');
6792         }
6793         if (Ext.isIE7) {
6794             cls.push(baseCSSPrefix + 'ie7');
6795         }
6796         if (Ext.isIE8) {
6797             cls.push(baseCSSPrefix + 'ie8');
6798         }
6799         if (Ext.isIE9) {
6800             cls.push(baseCSSPrefix + 'ie9');
6801         }
6802         if (Ext.isGecko) {
6803             cls.push(baseCSSPrefix + 'gecko');
6804         }
6805         if (Ext.isGecko3) {
6806             cls.push(baseCSSPrefix + 'gecko3');
6807         }
6808         if (Ext.isGecko4) {
6809             cls.push(baseCSSPrefix + 'gecko4');
6810         }
6811         if (Ext.isOpera) {
6812             cls.push(baseCSSPrefix + 'opera');
6813         }
6814         if (Ext.isWebKit) {
6815             cls.push(baseCSSPrefix + 'webkit');
6816         }
6817         if (Ext.isSafari) {
6818             cls.push(baseCSSPrefix + 'safari');
6819         }
6820         if (Ext.isSafari2) {
6821             cls.push(baseCSSPrefix + 'safari2');
6822         }
6823         if (Ext.isSafari3) {
6824             cls.push(baseCSSPrefix + 'safari3');
6825         }
6826         if (Ext.isSafari4) {
6827             cls.push(baseCSSPrefix + 'safari4');
6828         }
6829         if (Ext.isChrome) {
6830             cls.push(baseCSSPrefix + 'chrome');
6831         }
6832         if (Ext.isMac) {
6833             cls.push(baseCSSPrefix + 'mac');
6834         }
6835         if (Ext.isLinux) {
6836             cls.push(baseCSSPrefix + 'linux');
6837         }
6838         if (!Ext.supports.CSS3BorderRadius) {
6839             cls.push(baseCSSPrefix + 'nbr');
6840         }
6841         if (!Ext.supports.CSS3LinearGradient) {
6842             cls.push(baseCSSPrefix + 'nlg');
6843         }
6844         if (!Ext.scopeResetCSS) {
6845             cls.push(baseCSSPrefix + 'reset');
6846         }
6847
6848         // add to the parent to allow for selectors x-strict x-border-box, also set the isBorderBox property correctly
6849         if (html) {
6850             if (Ext.isStrict && (Ext.isIE6 || Ext.isIE7)) {
6851                 Ext.isBorderBox = false;
6852             }
6853             else {
6854                 Ext.isBorderBox = true;
6855             }
6856
6857             htmlCls.push(baseCSSPrefix + (Ext.isBorderBox ? 'border-box' : 'strict'));
6858             if (!Ext.isStrict) {
6859                 htmlCls.push(baseCSSPrefix + 'quirks');
6860                 if (Ext.isIE && !Ext.isStrict) {
6861                     Ext.isIEQuirks = true;
6862                 }
6863             }
6864             Ext.fly(html, '_internal').addCls(htmlCls);
6865         }
6866
6867         Ext.fly(bd, '_internal').addCls(cls);
6868         return true;
6869     };
6870
6871     Ext.onReady(initExtCss);
6872 })();
6873
6874 /**
6875  * @class Ext.EventObject
6876
6877 Just as {@link Ext.core.Element} wraps around a native DOM node, Ext.EventObject
6878 wraps the browser's native event-object normalizing cross-browser differences,
6879 such as which mouse button is clicked, keys pressed, mechanisms to stop
6880 event-propagation along with a method to prevent default actions from taking place.
6881
6882 For example:
6883
6884     function handleClick(e, t){ // e is not a standard event object, it is a Ext.EventObject
6885         e.preventDefault();
6886         var target = e.getTarget(); // same as t (the target HTMLElement)
6887         ...
6888     }
6889
6890     var myDiv = {@link Ext#get Ext.get}("myDiv");  // get reference to an {@link Ext.core.Element}
6891     myDiv.on(         // 'on' is shorthand for addListener
6892         "click",      // perform an action on click of myDiv
6893         handleClick   // reference to the action handler
6894     );
6895
6896     // other methods to do the same:
6897     Ext.EventManager.on("myDiv", 'click', handleClick);
6898     Ext.EventManager.addListener("myDiv", 'click', handleClick);
6899
6900  * @singleton
6901  * @markdown
6902  */
6903 Ext.define('Ext.EventObjectImpl', {
6904     uses: ['Ext.util.Point'],
6905
6906     /** Key constant @type Number */
6907     BACKSPACE: 8,
6908     /** Key constant @type Number */
6909     TAB: 9,
6910     /** Key constant @type Number */
6911     NUM_CENTER: 12,
6912     /** Key constant @type Number */
6913     ENTER: 13,
6914     /** Key constant @type Number */
6915     RETURN: 13,
6916     /** Key constant @type Number */
6917     SHIFT: 16,
6918     /** Key constant @type Number */
6919     CTRL: 17,
6920     /** Key constant @type Number */
6921     ALT: 18,
6922     /** Key constant @type Number */
6923     PAUSE: 19,
6924     /** Key constant @type Number */
6925     CAPS_LOCK: 20,
6926     /** Key constant @type Number */
6927     ESC: 27,
6928     /** Key constant @type Number */
6929     SPACE: 32,
6930     /** Key constant @type Number */
6931     PAGE_UP: 33,
6932     /** Key constant @type Number */
6933     PAGE_DOWN: 34,
6934     /** Key constant @type Number */
6935     END: 35,
6936     /** Key constant @type Number */
6937     HOME: 36,
6938     /** Key constant @type Number */
6939     LEFT: 37,
6940     /** Key constant @type Number */
6941     UP: 38,
6942     /** Key constant @type Number */
6943     RIGHT: 39,
6944     /** Key constant @type Number */
6945     DOWN: 40,
6946     /** Key constant @type Number */
6947     PRINT_SCREEN: 44,
6948     /** Key constant @type Number */
6949     INSERT: 45,
6950     /** Key constant @type Number */
6951     DELETE: 46,
6952     /** Key constant @type Number */
6953     ZERO: 48,
6954     /** Key constant @type Number */
6955     ONE: 49,
6956     /** Key constant @type Number */
6957     TWO: 50,
6958     /** Key constant @type Number */
6959     THREE: 51,
6960     /** Key constant @type Number */
6961     FOUR: 52,
6962     /** Key constant @type Number */
6963     FIVE: 53,
6964     /** Key constant @type Number */
6965     SIX: 54,
6966     /** Key constant @type Number */
6967     SEVEN: 55,
6968     /** Key constant @type Number */
6969     EIGHT: 56,
6970     /** Key constant @type Number */
6971     NINE: 57,
6972     /** Key constant @type Number */
6973     A: 65,
6974     /** Key constant @type Number */
6975     B: 66,
6976     /** Key constant @type Number */
6977     C: 67,
6978     /** Key constant @type Number */
6979     D: 68,
6980     /** Key constant @type Number */
6981     E: 69,
6982     /** Key constant @type Number */
6983     F: 70,
6984     /** Key constant @type Number */
6985     G: 71,
6986     /** Key constant @type Number */
6987     H: 72,
6988     /** Key constant @type Number */
6989     I: 73,
6990     /** Key constant @type Number */
6991     J: 74,
6992     /** Key constant @type Number */
6993     K: 75,
6994     /** Key constant @type Number */
6995     L: 76,
6996     /** Key constant @type Number */
6997     M: 77,
6998     /** Key constant @type Number */
6999     N: 78,
7000     /** Key constant @type Number */
7001     O: 79,
7002     /** Key constant @type Number */
7003     P: 80,
7004     /** Key constant @type Number */
7005     Q: 81,
7006     /** Key constant @type Number */
7007     R: 82,
7008     /** Key constant @type Number */
7009     S: 83,
7010     /** Key constant @type Number */
7011     T: 84,
7012     /** Key constant @type Number */
7013     U: 85,
7014     /** Key constant @type Number */
7015     V: 86,
7016     /** Key constant @type Number */
7017     W: 87,
7018     /** Key constant @type Number */
7019     X: 88,
7020     /** Key constant @type Number */
7021     Y: 89,
7022     /** Key constant @type Number */
7023     Z: 90,
7024     /** Key constant @type Number */
7025     CONTEXT_MENU: 93,
7026     /** Key constant @type Number */
7027     NUM_ZERO: 96,
7028     /** Key constant @type Number */
7029     NUM_ONE: 97,
7030     /** Key constant @type Number */
7031     NUM_TWO: 98,
7032     /** Key constant @type Number */
7033     NUM_THREE: 99,
7034     /** Key constant @type Number */
7035     NUM_FOUR: 100,
7036     /** Key constant @type Number */
7037     NUM_FIVE: 101,
7038     /** Key constant @type Number */
7039     NUM_SIX: 102,
7040     /** Key constant @type Number */
7041     NUM_SEVEN: 103,
7042     /** Key constant @type Number */
7043     NUM_EIGHT: 104,
7044     /** Key constant @type Number */
7045     NUM_NINE: 105,
7046     /** Key constant @type Number */
7047     NUM_MULTIPLY: 106,
7048     /** Key constant @type Number */
7049     NUM_PLUS: 107,
7050     /** Key constant @type Number */
7051     NUM_MINUS: 109,
7052     /** Key constant @type Number */
7053     NUM_PERIOD: 110,
7054     /** Key constant @type Number */
7055     NUM_DIVISION: 111,
7056     /** Key constant @type Number */
7057     F1: 112,
7058     /** Key constant @type Number */
7059     F2: 113,
7060     /** Key constant @type Number */
7061     F3: 114,
7062     /** Key constant @type Number */
7063     F4: 115,
7064     /** Key constant @type Number */
7065     F5: 116,
7066     /** Key constant @type Number */
7067     F6: 117,
7068     /** Key constant @type Number */
7069     F7: 118,
7070     /** Key constant @type Number */
7071     F8: 119,
7072     /** Key constant @type Number */
7073     F9: 120,
7074     /** Key constant @type Number */
7075     F10: 121,
7076     /** Key constant @type Number */
7077     F11: 122,
7078     /** Key constant @type Number */
7079     F12: 123,
7080
7081     /**
7082      * Simple click regex
7083      * @private
7084      */
7085     clickRe: /(dbl)?click/,
7086     // safari keypress events for special keys return bad keycodes
7087     safariKeys: {
7088         3: 13, // enter
7089         63234: 37, // left
7090         63235: 39, // right
7091         63232: 38, // up
7092         63233: 40, // down
7093         63276: 33, // page up
7094         63277: 34, // page down
7095         63272: 46, // delete
7096         63273: 36, // home
7097         63275: 35 // end
7098     },
7099     // normalize button clicks, don't see any way to feature detect this.
7100     btnMap: Ext.isIE ? {
7101         1: 0,
7102         4: 1,
7103         2: 2
7104     } : {
7105         0: 0,
7106         1: 1,
7107         2: 2
7108     },
7109
7110     constructor: function(event, freezeEvent){
7111         if (event) {
7112             this.setEvent(event.browserEvent || event, freezeEvent);
7113         }
7114     },
7115
7116     setEvent: function(event, freezeEvent){
7117         var me = this, button, options;
7118
7119         if (event == me || (event && event.browserEvent)) { // already wrapped
7120             return event;
7121         }
7122         me.browserEvent = event;
7123         if (event) {
7124             // normalize buttons
7125             button = event.button ? me.btnMap[event.button] : (event.which ? event.which - 1 : -1);
7126             if (me.clickRe.test(event.type) && button == -1) {
7127                 button = 0;
7128             }
7129             options = {
7130                 type: event.type,
7131                 button: button,
7132                 shiftKey: event.shiftKey,
7133                 // mac metaKey behaves like ctrlKey
7134                 ctrlKey: event.ctrlKey || event.metaKey || false,
7135                 altKey: event.altKey,
7136                 // in getKey these will be normalized for the mac
7137                 keyCode: event.keyCode,
7138                 charCode: event.charCode,
7139                 // cache the targets for the delayed and or buffered events
7140                 target: Ext.EventManager.getTarget(event),
7141                 relatedTarget: Ext.EventManager.getRelatedTarget(event),
7142                 currentTarget: event.currentTarget,
7143                 xy: (freezeEvent ? me.getXY() : null)
7144             };
7145         } else {
7146             options = {
7147                 button: -1,
7148                 shiftKey: false,
7149                 ctrlKey: false,
7150                 altKey: false,
7151                 keyCode: 0,
7152                 charCode: 0,
7153                 target: null,
7154                 xy: [0, 0]
7155             };
7156         }
7157         Ext.apply(me, options);
7158         return me;
7159     },
7160
7161     /**
7162      * Stop the event (preventDefault and stopPropagation)
7163      */
7164     stopEvent: function(){
7165         this.stopPropagation();
7166         this.preventDefault();
7167     },
7168
7169     /**
7170      * Prevents the browsers default handling of the event.
7171      */
7172     preventDefault: function(){
7173         if (this.browserEvent) {
7174             Ext.EventManager.preventDefault(this.browserEvent);
7175         }
7176     },
7177
7178     /**
7179      * Cancels bubbling of the event.
7180      */
7181     stopPropagation: function(){
7182         var browserEvent = this.browserEvent;
7183
7184         if (browserEvent) {
7185             if (browserEvent.type == 'mousedown') {
7186                 Ext.EventManager.stoppedMouseDownEvent.fire(this);
7187             }
7188             Ext.EventManager.stopPropagation(browserEvent);
7189         }
7190     },
7191
7192     /**
7193      * Gets the character code for the event.
7194      * @return {Number}
7195      */
7196     getCharCode: function(){
7197         return this.charCode || this.keyCode;
7198     },
7199
7200     /**
7201      * Returns a normalized keyCode for the event.
7202      * @return {Number} The key code
7203      */
7204     getKey: function(){
7205         return this.normalizeKey(this.keyCode || this.charCode);
7206     },
7207
7208     /**
7209      * Normalize key codes across browsers
7210      * @private
7211      * @param {Number} key The key code
7212      * @return {Number} The normalized code
7213      */
7214     normalizeKey: function(key){
7215         // can't feature detect this
7216         return Ext.isWebKit ? (this.safariKeys[key] || key) : key;
7217     },
7218
7219     /**
7220      * Gets the x coordinate of the event.
7221      * @return {Number}
7222      * @deprecated 4.0 Replaced by {@link #getX}
7223      */
7224     getPageX: function(){
7225         return this.getX();
7226     },
7227
7228     /**
7229      * Gets the y coordinate of the event.
7230      * @return {Number}
7231      * @deprecated 4.0 Replaced by {@link #getY}
7232      */
7233     getPageY: function(){
7234         return this.getY();
7235     },
7236     
7237     /**
7238      * Gets the x coordinate of the event.
7239      * @return {Number}
7240      */
7241     getX: function() {
7242         return this.getXY()[0];
7243     },    
7244     
7245     /**
7246      * Gets the y coordinate of the event.
7247      * @return {Number}
7248      */
7249     getY: function() {
7250         return this.getXY()[1];
7251     },
7252         
7253     /**
7254      * Gets the page coordinates of the event.
7255      * @return {Array} The xy values like [x, y]
7256      */
7257     getXY: function() {
7258         if (!this.xy) {
7259             // same for XY
7260             this.xy = Ext.EventManager.getPageXY(this.browserEvent);
7261         }
7262         return this.xy;
7263     },
7264
7265     /**
7266      * Gets the target for the event.
7267      * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
7268      * @param {Number/Mixed} maxDepth (optional) The max depth to search as a number or element (defaults to 10 || document.body)
7269      * @param {Boolean} returnEl (optional) True to return a Ext.core.Element object instead of DOM node
7270      * @return {HTMLelement}
7271      */
7272     getTarget : function(selector, maxDepth, returnEl){
7273         if (selector) {
7274             return Ext.fly(this.target).findParent(selector, maxDepth, returnEl);
7275         }
7276         return returnEl ? Ext.get(this.target) : this.target;
7277     },
7278
7279     /**
7280      * Gets the related target.
7281      * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
7282      * @param {Number/Mixed} maxDepth (optional) The max depth to search as a number or element (defaults to 10 || document.body)
7283      * @param {Boolean} returnEl (optional) True to return a Ext.core.Element object instead of DOM node
7284      * @return {HTMLElement}
7285      */
7286     getRelatedTarget : function(selector, maxDepth, returnEl){
7287         if (selector) {
7288             return Ext.fly(this.relatedTarget).findParent(selector, maxDepth, returnEl);
7289         }
7290         return returnEl ? Ext.get(this.relatedTarget) : this.relatedTarget;
7291     },
7292
7293     /**
7294      * Normalizes mouse wheel delta across browsers
7295      * @return {Number} The delta
7296      */
7297     getWheelDelta : function(){
7298         var event = this.browserEvent,
7299             delta = 0;
7300
7301         if (event.wheelDelta) { /* IE/Opera. */
7302             delta = event.wheelDelta / 120;
7303         } else if (event.detail){ /* Mozilla case. */
7304             delta = -event.detail / 3;
7305         }
7306         return delta;
7307     },
7308
7309     /**
7310     * 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.
7311     * Example usage:<pre><code>
7312 // Handle click on any child of an element
7313 Ext.getBody().on('click', function(e){
7314     if(e.within('some-el')){
7315         alert('Clicked on a child of some-el!');
7316     }
7317 });
7318
7319 // Handle click directly on an element, ignoring clicks on child nodes
7320 Ext.getBody().on('click', function(e,t){
7321     if((t.id == 'some-el') && !e.within(t, true)){
7322         alert('Clicked directly on some-el!');
7323     }
7324 });
7325 </code></pre>
7326      * @param {Mixed} el The id, DOM element or Ext.core.Element to check
7327      * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7328      * @param {Boolean} allowEl {optional} true to also check if the passed element is the target or related target
7329      * @return {Boolean}
7330      */
7331     within : function(el, related, allowEl){
7332         if(el){
7333             var t = related ? this.getRelatedTarget() : this.getTarget(),
7334                 result;
7335
7336             if (t) {
7337                 result = Ext.fly(el).contains(t);
7338                 if (!result && allowEl) {
7339                     result = t == Ext.getDom(el);
7340                 }
7341                 return result;
7342             }
7343         }
7344         return false;
7345     },
7346
7347     /**
7348      * Checks if the key pressed was a "navigation" key
7349      * @return {Boolean} True if the press is a navigation keypress
7350      */
7351     isNavKeyPress : function(){
7352         var me = this,
7353             k = this.normalizeKey(me.keyCode);
7354
7355        return (k >= 33 && k <= 40) ||  // Page Up/Down, End, Home, Left, Up, Right, Down
7356        k == me.RETURN ||
7357        k == me.TAB ||
7358        k == me.ESC;
7359     },
7360
7361     /**
7362      * Checks if the key pressed was a "special" key
7363      * @return {Boolean} True if the press is a special keypress
7364      */
7365     isSpecialKey : function(){
7366         var k = this.normalizeKey(this.keyCode);
7367         return (this.type == 'keypress' && this.ctrlKey) ||
7368         this.isNavKeyPress() ||
7369         (k == this.BACKSPACE) || // Backspace
7370         (k >= 16 && k <= 20) || // Shift, Ctrl, Alt, Pause, Caps Lock
7371         (k >= 44 && k <= 46);   // Print Screen, Insert, Delete
7372     },
7373
7374     /**
7375      * Returns a point object that consists of the object coordinates.
7376      * @return {Ext.util.Point} point
7377      */
7378     getPoint : function(){
7379         var xy = this.getXY();
7380         return Ext.create('Ext.util.Point', xy[0], xy[1]);
7381     },
7382
7383    /**
7384     * Returns true if the control, meta, shift or alt key was pressed during this event.
7385     * @return {Boolean}
7386     */
7387     hasModifier : function(){
7388         return this.ctrlKey || this.altKey || this.shiftKey || this.metaKey;
7389     },
7390
7391     /**
7392      * Injects a DOM event using the data in this object and (optionally) a new target.
7393      * This is a low-level technique and not likely to be used by application code. The
7394      * currently supported event types are:
7395      * <p><b>HTMLEvents</b></p>
7396      * <ul>
7397      * <li>load</li>
7398      * <li>unload</li>
7399      * <li>select</li>
7400      * <li>change</li>
7401      * <li>submit</li>
7402      * <li>reset</li>
7403      * <li>resize</li>
7404      * <li>scroll</li>
7405      * </ul>
7406      * <p><b>MouseEvents</b></p>
7407      * <ul>
7408      * <li>click</li>
7409      * <li>dblclick</li>
7410      * <li>mousedown</li>
7411      * <li>mouseup</li>
7412      * <li>mouseover</li>
7413      * <li>mousemove</li>
7414      * <li>mouseout</li>
7415      * </ul>
7416      * <p><b>UIEvents</b></p>
7417      * <ul>
7418      * <li>focusin</li>
7419      * <li>focusout</li>
7420      * <li>activate</li>
7421      * <li>focus</li>
7422      * <li>blur</li>
7423      * </ul>
7424      * @param {Element/HTMLElement} target If specified, the target for the event. This
7425      * is likely to be used when relaying a DOM event. If not specified, {@link #getTarget}
7426      * is used to determine the target.
7427      */
7428     injectEvent: function () {
7429         var API,
7430             dispatchers = {}; // keyed by event type (e.g., 'mousedown')
7431
7432         // Good reference: http://developer.yahoo.com/yui/docs/UserAction.js.html
7433
7434         // IE9 has createEvent, but this code causes major problems with htmleditor (it
7435         // blocks all mouse events and maybe more). TODO
7436
7437         if (!Ext.isIE && document.createEvent) { // if (DOM compliant)
7438             API = {
7439                 createHtmlEvent: function (doc, type, bubbles, cancelable) {
7440                     var event = doc.createEvent('HTMLEvents');
7441
7442                     event.initEvent(type, bubbles, cancelable);
7443                     return event;
7444                 },
7445
7446                 createMouseEvent: function (doc, type, bubbles, cancelable, detail,
7447                                             clientX, clientY, ctrlKey, altKey, shiftKey, metaKey,
7448                                             button, relatedTarget) {
7449                     var event = doc.createEvent('MouseEvents'),
7450                         view = doc.defaultView || window;
7451
7452                     if (event.initMouseEvent) {
7453                         event.initMouseEvent(type, bubbles, cancelable, view, detail,
7454                                     clientX, clientY, clientX, clientY, ctrlKey, altKey,
7455                                     shiftKey, metaKey, button, relatedTarget);
7456                     } else { // old Safari
7457                         event = doc.createEvent('UIEvents');
7458                         event.initEvent(type, bubbles, cancelable);
7459                         event.view = view;
7460                         event.detail = detail;
7461                         event.screenX = clientX;
7462                         event.screenY = clientY;
7463                         event.clientX = clientX;
7464                         event.clientY = clientY;
7465                         event.ctrlKey = ctrlKey;
7466                         event.altKey = altKey;
7467                         event.metaKey = metaKey;
7468                         event.shiftKey = shiftKey;
7469                         event.button = button;
7470                         event.relatedTarget = relatedTarget;
7471                     }
7472
7473                     return event;
7474                 },
7475
7476                 createUIEvent: function (doc, type, bubbles, cancelable, detail) {
7477                     var event = doc.createEvent('UIEvents'),
7478                         view = doc.defaultView || window;
7479
7480                     event.initUIEvent(type, bubbles, cancelable, view, detail);
7481                     return event;
7482                 },
7483
7484                 fireEvent: function (target, type, event) {
7485                     target.dispatchEvent(event);
7486                 },
7487
7488                 fixTarget: function (target) {
7489                     // Safari3 doesn't have window.dispatchEvent()
7490                     if (target == window && !target.dispatchEvent) {
7491                         return document;
7492                     }
7493
7494                     return target;
7495                 }
7496             }
7497         } else if (document.createEventObject) { // else if (IE)
7498             var crazyIEButtons = { 0: 1, 1: 4, 2: 2 };
7499
7500             API = {
7501                 createHtmlEvent: function (doc, type, bubbles, cancelable) {
7502                     var event = doc.createEventObject();
7503                     event.bubbles = bubbles;
7504                     event.cancelable = cancelable;
7505                     return event;
7506                 },
7507
7508                 createMouseEvent: function (doc, type, bubbles, cancelable, detail,
7509                                             clientX, clientY, ctrlKey, altKey, shiftKey, metaKey,
7510                                             button, relatedTarget) {
7511                     var event = doc.createEventObject();
7512                     event.bubbles = bubbles;
7513                     event.cancelable = cancelable;
7514                     event.detail = detail;
7515                     event.screenX = clientX;
7516                     event.screenY = clientY;
7517                     event.clientX = clientX;
7518                     event.clientY = clientY;
7519                     event.ctrlKey = ctrlKey;
7520                     event.altKey = altKey;
7521                     event.shiftKey = shiftKey;
7522                     event.metaKey = metaKey;
7523                     event.button = crazyIEButtons[button] || button;
7524                     event.relatedTarget = relatedTarget; // cannot assign to/fromElement
7525                     return event;
7526                 },
7527
7528                 createUIEvent: function (doc, type, bubbles, cancelable, detail) {
7529                     var event = doc.createEventObject();
7530                     event.bubbles = bubbles;
7531                     event.cancelable = cancelable;
7532                     return event;
7533                 },
7534
7535                 fireEvent: function (target, type, event) {
7536                     target.fireEvent('on' + type, event);
7537                 },
7538
7539                 fixTarget: function (target) {
7540                     if (target == document) {
7541                         // IE6,IE7 thinks window==document and doesn't have window.fireEvent()
7542                         // IE6,IE7 cannot properly call document.fireEvent()
7543                         return document.documentElement;
7544                     }
7545
7546                     return target;
7547                 }
7548             };
7549         }
7550
7551         //----------------
7552         // HTMLEvents
7553
7554         Ext.Object.each({
7555                 load:   [false, false],
7556                 unload: [false, false],
7557                 select: [true, false],
7558                 change: [true, false],
7559                 submit: [true, true],
7560                 reset:  [true, false],
7561                 resize: [true, false],
7562                 scroll: [true, false]
7563             },
7564             function (name, value) {
7565                 var bubbles = value[0], cancelable = value[1];
7566                 dispatchers[name] = function (targetEl, srcEvent) {
7567                     var e = API.createHtmlEvent(name, bubbles, cancelable);
7568                     API.fireEvent(targetEl, name, e);
7569                 };
7570             });
7571
7572         //----------------
7573         // MouseEvents
7574
7575         function createMouseEventDispatcher (type, detail) {
7576             var cancelable = (type != 'mousemove');
7577             return function (targetEl, srcEvent) {
7578                 var xy = srcEvent.getXY(),
7579                     e = API.createMouseEvent(targetEl.ownerDocument, type, true, cancelable,
7580                                 detail, xy[0], xy[1], srcEvent.ctrlKey, srcEvent.altKey,
7581                                 srcEvent.shiftKey, srcEvent.metaKey, srcEvent.button,
7582                                 srcEvent.relatedTarget);
7583                 API.fireEvent(targetEl, type, e);
7584             };
7585         }
7586
7587         Ext.each(['click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mousemove', 'mouseout'],
7588             function (eventName) {
7589                 dispatchers[eventName] = createMouseEventDispatcher(eventName, 1);
7590             });
7591
7592         //----------------
7593         // UIEvents
7594
7595         Ext.Object.each({
7596                 focusin:  [true, false],
7597                 focusout: [true, false],
7598                 activate: [true, true],
7599                 focus:    [false, false],
7600                 blur:     [false, false]
7601             },
7602             function (name, value) {
7603                 var bubbles = value[0], cancelable = value[1];
7604                 dispatchers[name] = function (targetEl, srcEvent) {
7605                     var e = API.createUIEvent(targetEl.ownerDocument, name, bubbles, cancelable, 1);
7606                     API.fireEvent(targetEl, name, e);
7607                 };
7608             });
7609
7610         //---------
7611         if (!API) {
7612             // not even sure what ancient browsers fall into this category...
7613
7614             dispatchers = {}; // never mind all those we just built :P
7615
7616             API = {
7617                 fixTarget: function (t) {
7618                     return t;
7619                 }
7620             };
7621         }
7622
7623         function cannotInject (target, srcEvent) {
7624             //<debug>
7625             // TODO log something
7626             //</debug>
7627         }
7628
7629         return function (target) {
7630             var me = this,
7631                 dispatcher = dispatchers[me.type] || cannotInject,
7632                 t = target ? (target.dom || target) : me.getTarget();
7633
7634             t = API.fixTarget(t);
7635             dispatcher(t, me);
7636         };
7637     }() // call to produce method
7638
7639 }, function() {
7640
7641 Ext.EventObject = new Ext.EventObjectImpl();
7642
7643 });
7644
7645
7646 /**
7647  * @class Ext.core.Element
7648  */
7649 (function(){
7650     var doc = document,
7651         activeElement = null,
7652         isCSS1 = doc.compatMode == "CSS1Compat",
7653         ELEMENT = Ext.core.Element,
7654         fly = function(el){
7655             if (!_fly) {
7656                 _fly = new Ext.core.Element.Flyweight();
7657             }
7658             _fly.dom = el;
7659             return _fly;
7660         }, _fly;
7661
7662     // If the browser does not support document.activeElement we need some assistance.
7663     // This covers old Safari 3.2 (4.0 added activeElement along with just about all
7664     // other browsers). We need this support to handle issues with old Safari.
7665     if (!('activeElement' in doc) && doc.addEventListener) {
7666         doc.addEventListener('focus',
7667             function (ev) {
7668                 if (ev && ev.target) {
7669                     activeElement = (ev.target == doc) ? null : ev.target;
7670                 }
7671             }, true);
7672     }
7673
7674     /*
7675      * Helper function to create the function that will restore the selection.
7676      */
7677     function makeSelectionRestoreFn (activeEl, start, end) {
7678         return function () {
7679             activeEl.selectionStart = start;
7680             activeEl.selectionEnd = end;
7681         };
7682     }
7683
7684     Ext.apply(ELEMENT, {
7685         isAncestor : function(p, c) {
7686             var ret = false;
7687
7688             p = Ext.getDom(p);
7689             c = Ext.getDom(c);
7690             if (p && c) {
7691                 if (p.contains) {
7692                     return p.contains(c);
7693                 } else if (p.compareDocumentPosition) {
7694                     return !!(p.compareDocumentPosition(c) & 16);
7695                 } else {
7696                     while ((c = c.parentNode)) {
7697                         ret = c == p || ret;
7698                     }
7699                 }
7700             }
7701             return ret;
7702         },
7703
7704         /**
7705          * Returns the active element in the DOM. If the browser supports activeElement
7706          * on the document, this is returned. If not, the focus is tracked and the active
7707          * element is maintained internally.
7708          * @return {HTMLElement} The active (focused) element in the document.
7709          */
7710         getActiveElement: function () {
7711             return doc.activeElement || activeElement;
7712         },
7713
7714         /**
7715          * Creates a function to call to clean up problems with the work-around for the
7716          * WebKit RightMargin bug. The work-around is to add "display: 'inline-block'" to
7717          * the element before calling getComputedStyle and then to restore its original
7718          * display value. The problem with this is that it corrupts the selection of an
7719          * INPUT or TEXTAREA element (as in the "I-beam" goes away but ths focus remains).
7720          * To cleanup after this, we need to capture the selection of any such element and
7721          * then restore it after we have restored the display style.
7722          *
7723          * @param target {Element} The top-most element being adjusted.
7724          * @private
7725          */
7726         getRightMarginFixCleaner: function (target) {
7727             var supports = Ext.supports,
7728                 hasInputBug = supports.DisplayChangeInputSelectionBug,
7729                 hasTextAreaBug = supports.DisplayChangeTextAreaSelectionBug;
7730
7731             if (hasInputBug || hasTextAreaBug) {
7732                 var activeEl = doc.activeElement || activeElement, // save a call
7733                     tag = activeEl && activeEl.tagName,
7734                     start,
7735                     end;
7736
7737                 if ((hasTextAreaBug && tag == 'TEXTAREA') ||
7738                     (hasInputBug && tag == 'INPUT' && activeEl.type == 'text')) {
7739                     if (ELEMENT.isAncestor(target, activeEl)) {
7740                         start = activeEl.selectionStart;
7741                         end = activeEl.selectionEnd;
7742
7743                         if (Ext.isNumber(start) && Ext.isNumber(end)) { // to be safe...
7744                             // We don't create the raw closure here inline because that
7745                             // will be costly even if we don't want to return it (nested
7746                             // function decls and exprs are often instantiated on entry
7747                             // regardless of whether execution ever reaches them):
7748                             return makeSelectionRestoreFn(activeEl, start, end);
7749                         }
7750                     }
7751                 }
7752             }
7753
7754             return Ext.emptyFn; // avoid special cases, just return a nop
7755         },
7756
7757         getViewWidth : function(full) {
7758             return full ? ELEMENT.getDocumentWidth() : ELEMENT.getViewportWidth();
7759         },
7760
7761         getViewHeight : function(full) {
7762             return full ? ELEMENT.getDocumentHeight() : ELEMENT.getViewportHeight();
7763         },
7764
7765         getDocumentHeight: function() {
7766             return Math.max(!isCSS1 ? doc.body.scrollHeight : doc.documentElement.scrollHeight, ELEMENT.getViewportHeight());
7767         },
7768
7769         getDocumentWidth: function() {
7770             return Math.max(!isCSS1 ? doc.body.scrollWidth : doc.documentElement.scrollWidth, ELEMENT.getViewportWidth());
7771         },
7772
7773         getViewportHeight: function(){
7774             return Ext.isIE ?
7775                    (Ext.isStrict ? doc.documentElement.clientHeight : doc.body.clientHeight) :
7776                    self.innerHeight;
7777         },
7778
7779         getViewportWidth : function() {
7780             return (!Ext.isStrict && !Ext.isOpera) ? doc.body.clientWidth :
7781                    Ext.isIE ? doc.documentElement.clientWidth : self.innerWidth;
7782         },
7783
7784         getY : function(el) {
7785             return ELEMENT.getXY(el)[1];
7786         },
7787
7788         getX : function(el) {
7789             return ELEMENT.getXY(el)[0];
7790         },
7791
7792         getXY : function(el) {
7793             var p,
7794                 pe,
7795                 b,
7796                 bt,
7797                 bl,
7798                 dbd,
7799                 x = 0,
7800                 y = 0,
7801                 scroll,
7802                 hasAbsolute,
7803                 bd = (doc.body || doc.documentElement),
7804                 ret = [0,0];
7805
7806             el = Ext.getDom(el);
7807
7808             if(el != bd){
7809                 hasAbsolute = fly(el).isStyle("position", "absolute");
7810
7811                 if (el.getBoundingClientRect) {
7812                     b = el.getBoundingClientRect();
7813                     scroll = fly(document).getScroll();
7814                     ret = [Math.round(b.left + scroll.left), Math.round(b.top + scroll.top)];
7815                 } else {
7816                     p = el;
7817
7818                     while (p) {
7819                         pe = fly(p);
7820                         x += p.offsetLeft;
7821                         y += p.offsetTop;
7822
7823                         hasAbsolute = hasAbsolute || pe.isStyle("position", "absolute");
7824
7825                         if (Ext.isGecko) {
7826                             y += bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
7827                             x += bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
7828
7829                             if (p != el && !pe.isStyle('overflow','visible')) {
7830                                 x += bl;
7831                                 y += bt;
7832                             }
7833                         }
7834                         p = p.offsetParent;
7835                     }
7836
7837                     if (Ext.isSafari && hasAbsolute) {
7838                         x -= bd.offsetLeft;
7839                         y -= bd.offsetTop;
7840                     }
7841
7842                     if (Ext.isGecko && !hasAbsolute) {
7843                         dbd = fly(bd);
7844                         x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
7845                         y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
7846                     }
7847
7848                     p = el.parentNode;
7849                     while (p && p != bd) {
7850                         if (!Ext.isOpera || (p.tagName != 'TR' && !fly(p).isStyle("display", "inline"))) {
7851                             x -= p.scrollLeft;
7852                             y -= p.scrollTop;
7853                         }
7854                         p = p.parentNode;
7855                     }
7856                     ret = [x,y];
7857                 }
7858             }
7859             return ret;
7860         },
7861
7862         setXY : function(el, xy) {
7863             (el = Ext.fly(el, '_setXY')).position();
7864
7865             var pts = el.translatePoints(xy),
7866                 style = el.dom.style,
7867                 pos;
7868
7869             for (pos in pts) {
7870                 if (!isNaN(pts[pos])) {
7871                     style[pos] = pts[pos] + "px";
7872                 }
7873             }
7874         },
7875
7876         setX : function(el, x) {
7877             ELEMENT.setXY(el, [x, false]);
7878         },
7879
7880         setY : function(el, y) {
7881             ELEMENT.setXY(el, [false, y]);
7882         },
7883
7884         /**
7885          * Serializes a DOM form into a url encoded string
7886          * @param {Object} form The form
7887          * @return {String} The url encoded form
7888          */
7889         serializeForm: function(form) {
7890             var fElements = form.elements || (document.forms[form] || Ext.getDom(form)).elements,
7891                 hasSubmit = false,
7892                 encoder = encodeURIComponent,
7893                 name,
7894                 data = '',
7895                 type,
7896                 hasValue;
7897
7898             Ext.each(fElements, function(element){
7899                 name = element.name;
7900                 type = element.type;
7901
7902                 if (!element.disabled && name) {
7903                     if (/select-(one|multiple)/i.test(type)) {
7904                         Ext.each(element.options, function(opt){
7905                             if (opt.selected) {
7906                                 hasValue = opt.hasAttribute ? opt.hasAttribute('value') : opt.getAttributeNode('value').specified;
7907                                 data += Ext.String.format("{0}={1}&", encoder(name), encoder(hasValue ? opt.value : opt.text));
7908                             }
7909                         });
7910                     } else if (!(/file|undefined|reset|button/i.test(type))) {
7911                         if (!(/radio|checkbox/i.test(type) && !element.checked) && !(type == 'submit' && hasSubmit)) {
7912                             data += encoder(name) + '=' + encoder(element.value) + '&';
7913                             hasSubmit = /submit/i.test(type);
7914                         }
7915                     }
7916                 }
7917             });
7918             return data.substr(0, data.length - 1);
7919         }
7920     });
7921 })();
7922
7923 /**
7924  * @class Ext.core.Element
7925  */
7926
7927 Ext.core.Element.addMethods({
7928
7929     /**
7930      * Monitors this Element for the mouse leaving. Calls the function after the specified delay only if
7931      * the mouse was not moved back into the Element within the delay. If the mouse <i>was</i> moved
7932      * back in, the function is not called.
7933      * @param {Number} delay The delay <b>in milliseconds</b> to wait for possible mouse re-entry before calling the handler function.
7934      * @param {Function} handler The function to call if the mouse remains outside of this Element for the specified time.
7935      * @param {Object} scope The scope (<code>this</code> reference) in which the handler function executes. Defaults to this Element.
7936      * @return {Object} The listeners object which was added to this element so that monitoring can be stopped. Example usage:</pre><code>
7937 // Hide the menu if the mouse moves out for 250ms or more
7938 this.mouseLeaveMonitor = this.menuEl.monitorMouseLeave(250, this.hideMenu, this);
7939
7940 ...
7941 // Remove mouseleave monitor on menu destroy
7942 this.menuEl.un(this.mouseLeaveMonitor);
7943 </code></pre>
7944      */
7945     monitorMouseLeave: function(delay, handler, scope) {
7946         var me = this,
7947             timer,
7948             listeners = {
7949                 mouseleave: function(e) {
7950                     timer = setTimeout(Ext.Function.bind(handler, scope||me, [e]), delay);
7951                 },
7952                 mouseenter: function() {
7953                     clearTimeout(timer);
7954                 },
7955                 freezeEvent: true
7956             };
7957
7958         me.on(listeners);
7959         return listeners;
7960     },
7961
7962     /**
7963      * Stops the specified event(s) from bubbling and optionally prevents the default action
7964      * @param {String/Array} eventName an event / array of events to stop from bubbling
7965      * @param {Boolean} preventDefault (optional) true to prevent the default action too
7966      * @return {Ext.core.Element} this
7967      */
7968     swallowEvent : function(eventName, preventDefault) {
7969         var me = this;
7970         function fn(e) {
7971             e.stopPropagation();
7972             if (preventDefault) {
7973                 e.preventDefault();
7974             }
7975         }
7976         
7977         if (Ext.isArray(eventName)) {
7978             Ext.each(eventName, function(e) {
7979                  me.on(e, fn);
7980             });
7981             return me;
7982         }
7983         me.on(eventName, fn);
7984         return me;
7985     },
7986
7987     /**
7988      * Create an event handler on this element such that when the event fires and is handled by this element,
7989      * it will be relayed to another object (i.e., fired again as if it originated from that object instead).
7990      * @param {String} eventName The type of event to relay
7991      * @param {Object} object Any object that extends {@link Ext.util.Observable} that will provide the context
7992      * for firing the relayed event
7993      */
7994     relayEvent : function(eventName, observable) {
7995         this.on(eventName, function(e) {
7996             observable.fireEvent(eventName, e);
7997         });
7998     },
7999
8000     /**
8001      * Removes Empty, or whitespace filled text nodes. Combines adjacent text nodes.
8002      * @param {Boolean} forceReclean (optional) By default the element
8003      * keeps track if it has been cleaned already so
8004      * you can call this over and over. However, if you update the element and
8005      * need to force a reclean, you can pass true.
8006      */
8007     clean : function(forceReclean) {
8008         var me  = this,
8009             dom = me.dom,
8010             n   = dom.firstChild,
8011             nx,
8012             ni  = -1;
8013
8014         if (Ext.core.Element.data(dom, 'isCleaned') && forceReclean !== true) {
8015             return me;
8016         }
8017
8018         while (n) {
8019             nx = n.nextSibling;
8020             if (n.nodeType == 3) {
8021                 // Remove empty/whitespace text nodes
8022                 if (!(/\S/.test(n.nodeValue))) {
8023                     dom.removeChild(n);
8024                 // Combine adjacent text nodes
8025                 } else if (nx && nx.nodeType == 3) {
8026                     n.appendData(Ext.String.trim(nx.data));
8027                     dom.removeChild(nx);
8028                     nx = n.nextSibling;
8029                     n.nodeIndex = ++ni;
8030                 }
8031             } else {
8032                 // Recursively clean
8033                 Ext.fly(n).clean();
8034                 n.nodeIndex = ++ni;
8035             }
8036             n = nx;
8037         }
8038
8039         Ext.core.Element.data(dom, 'isCleaned', true);
8040         return me;
8041     },
8042
8043     /**
8044      * Direct access to the Ext.ElementLoader {@link Ext.ElementLoader#load} method. The method takes the same object
8045      * parameter as {@link Ext.ElementLoader#load}
8046      * @return {Ext.core.Element} this
8047      */
8048     load : function(options) {
8049         this.getLoader().load(options);
8050         return this;
8051     },
8052
8053     /**
8054     * Gets this element's {@link Ext.ElementLoader ElementLoader}
8055     * @return {Ext.ElementLoader} The loader
8056     */
8057     getLoader : function() {
8058         var dom = this.dom,
8059             data = Ext.core.Element.data,
8060             loader = data(dom, 'loader');
8061             
8062         if (!loader) {
8063             loader = Ext.create('Ext.ElementLoader', {
8064                 target: this
8065             });
8066             data(dom, 'loader', loader);
8067         }
8068         return loader;
8069     },
8070
8071     /**
8072     * Update the innerHTML of this element, optionally searching for and processing scripts
8073     * @param {String} html The new HTML
8074     * @param {Boolean} loadScripts (optional) True to look for and process scripts (defaults to false)
8075     * @param {Function} callback (optional) For async script loading you can be notified when the update completes
8076     * @return {Ext.core.Element} this
8077      */
8078     update : function(html, loadScripts, callback) {
8079         var me = this,
8080             id,
8081             dom,
8082             interval;
8083             
8084         if (!me.dom) {
8085             return me;
8086         }
8087         html = html || '';
8088         dom = me.dom;
8089
8090         if (loadScripts !== true) {
8091             dom.innerHTML = html;
8092             Ext.callback(callback, me);
8093             return me;
8094         }
8095
8096         id  = Ext.id();
8097         html += '<span id="' + id + '"></span>';
8098
8099         interval = setInterval(function(){
8100             if (!document.getElementById(id)) {
8101                 return false;    
8102             }
8103             clearInterval(interval);
8104             var DOC    = document,
8105                 hd     = DOC.getElementsByTagName("head")[0],
8106                 re     = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig,
8107                 srcRe  = /\ssrc=([\'\"])(.*?)\1/i,
8108                 typeRe = /\stype=([\'\"])(.*?)\1/i,
8109                 match,
8110                 attrs,
8111                 srcMatch,
8112                 typeMatch,
8113                 el,
8114                 s;
8115
8116             while ((match = re.exec(html))) {
8117                 attrs = match[1];
8118                 srcMatch = attrs ? attrs.match(srcRe) : false;
8119                 if (srcMatch && srcMatch[2]) {
8120                    s = DOC.createElement("script");
8121                    s.src = srcMatch[2];
8122                    typeMatch = attrs.match(typeRe);
8123                    if (typeMatch && typeMatch[2]) {
8124                        s.type = typeMatch[2];
8125                    }
8126                    hd.appendChild(s);
8127                 } else if (match[2] && match[2].length > 0) {
8128                     if (window.execScript) {
8129                        window.execScript(match[2]);
8130                     } else {
8131                        window.eval(match[2]);
8132                     }
8133                 }
8134             }
8135             
8136             el = DOC.getElementById(id);
8137             if (el) {
8138                 Ext.removeNode(el);
8139             }
8140             Ext.callback(callback, me);
8141         }, 20);
8142         dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, '');
8143         return me;
8144     },
8145
8146     // inherit docs, overridden so we can add removeAnchor
8147     removeAllListeners : function() {
8148         this.removeAnchor();
8149         Ext.EventManager.removeAll(this.dom);
8150         return this;
8151     },
8152
8153     /**
8154      * Creates a proxy element of this element
8155      * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8156      * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8157      * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8158      * @return {Ext.core.Element} The new proxy element
8159      */
8160     createProxy : function(config, renderTo, matchBox) {
8161         config = (typeof config == 'object') ? config : {tag : "div", cls: config};
8162
8163         var me = this,
8164             proxy = renderTo ? Ext.core.DomHelper.append(renderTo, config, true) :
8165                                Ext.core.DomHelper.insertBefore(me.dom, config, true);
8166
8167         proxy.setVisibilityMode(Ext.core.Element.DISPLAY);
8168         proxy.hide();
8169         if (matchBox && me.setBox && me.getBox) { // check to make sure Element.position.js is loaded
8170            proxy.setBox(me.getBox());
8171         }
8172         return proxy;
8173     }
8174 });
8175 Ext.core.Element.prototype.clearListeners = Ext.core.Element.prototype.removeAllListeners;
8176
8177 /**
8178  * @class Ext.core.Element
8179  */
8180 Ext.core.Element.addMethods({
8181     /**
8182      * Gets the x,y coordinates specified by the anchor position on the element.
8183      * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo}
8184      * for details on supported anchor positions.
8185      * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead
8186      * of page coordinates
8187      * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8188      * {width: (target width), height: (target height)} (defaults to the element's current size)
8189      * @return {Array} [x, y] An array containing the element's x and y coordinates
8190      */
8191     getAnchorXY : function(anchor, local, s){
8192         //Passing a different size is useful for pre-calculating anchors,
8193         //especially for anchored animations that change the el size.
8194         anchor = (anchor || "tl").toLowerCase();
8195         s = s || {};
8196
8197         var me = this,
8198             vp = me.dom == document.body || me.dom == document,
8199             w = s.width || vp ? Ext.core.Element.getViewWidth() : me.getWidth(),
8200             h = s.height || vp ? Ext.core.Element.getViewHeight() : me.getHeight(),
8201             xy,
8202             r = Math.round,
8203             o = me.getXY(),
8204             scroll = me.getScroll(),
8205             extraX = vp ? scroll.left : !local ? o[0] : 0,
8206             extraY = vp ? scroll.top : !local ? o[1] : 0,
8207             hash = {
8208                 c  : [r(w * 0.5), r(h * 0.5)],
8209                 t  : [r(w * 0.5), 0],
8210                 l  : [0, r(h * 0.5)],
8211                 r  : [w, r(h * 0.5)],
8212                 b  : [r(w * 0.5), h],
8213                 tl : [0, 0],
8214                 bl : [0, h],
8215                 br : [w, h],
8216                 tr : [w, 0]
8217             };
8218
8219         xy = hash[anchor];
8220         return [xy[0] + extraX, xy[1] + extraY];
8221     },
8222
8223     /**
8224      * Anchors an element to another element and realigns it when the window is resized.
8225      * @param {Mixed} element The element to align to.
8226      * @param {String} position The position to align to.
8227      * @param {Array} offsets (optional) Offset the positioning by [x, y]
8228      * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8229      * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8230      * is a number, it is used as the buffer delay (defaults to 50ms).
8231      * @param {Function} callback The function to call after the animation finishes
8232      * @return {Ext.core.Element} this
8233      */
8234     anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8235         var me = this,
8236             dom = me.dom,
8237             scroll = !Ext.isEmpty(monitorScroll),
8238             action = function(){
8239                 Ext.fly(dom).alignTo(el, alignment, offsets, animate);
8240                 Ext.callback(callback, Ext.fly(dom));
8241             },
8242             anchor = this.getAnchor();
8243
8244         // previous listener anchor, remove it
8245         this.removeAnchor();
8246         Ext.apply(anchor, {
8247             fn: action,
8248             scroll: scroll
8249         });
8250
8251         Ext.EventManager.onWindowResize(action, null);
8252
8253         if(scroll){
8254             Ext.EventManager.on(window, 'scroll', action, null,
8255                 {buffer: !isNaN(monitorScroll) ? monitorScroll : 50});
8256         }
8257         action.call(me); // align immediately
8258         return me;
8259     },
8260
8261     /**
8262      * Remove any anchor to this element. See {@link #anchorTo}.
8263      * @return {Ext.core.Element} this
8264      */
8265     removeAnchor : function(){
8266         var me = this,
8267             anchor = this.getAnchor();
8268
8269         if(anchor && anchor.fn){
8270             Ext.EventManager.removeResizeListener(anchor.fn);
8271             if(anchor.scroll){
8272                 Ext.EventManager.un(window, 'scroll', anchor.fn);
8273             }
8274             delete anchor.fn;
8275         }
8276         return me;
8277     },
8278
8279     // private
8280     getAnchor : function(){
8281         var data = Ext.core.Element.data,
8282             dom = this.dom;
8283             if (!dom) {
8284                 return;
8285             }
8286             var anchor = data(dom, '_anchor');
8287
8288         if(!anchor){
8289             anchor = data(dom, '_anchor', {});
8290         }
8291         return anchor;
8292     },
8293
8294     getAlignVector: function(el, spec, offset) {
8295         var me = this,
8296             side = {t:"top", l:"left", r:"right", b: "bottom"},
8297             thisRegion = me.getRegion(),
8298             elRegion;
8299
8300         el = Ext.get(el);
8301         if(!el || !el.dom){
8302             //<debug>
8303             Ext.Error.raise({
8304                 sourceClass: 'Ext.core.Element',
8305                 sourceMethod: 'getAlignVector',
8306                 msg: 'Attempted to align an element that doesn\'t exist'
8307             });
8308             //</debug>
8309         }
8310
8311         elRegion = el.getRegion();
8312     },
8313
8314     /**
8315      * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8316      * supported position values.
8317      * @param {Mixed} element The element to align to.
8318      * @param {String} position (optional, defaults to "tl-bl?") The position to align to.
8319      * @param {Array} offsets (optional) Offset the positioning by [x, y]
8320      * @return {Array} [x, y]
8321      */
8322     getAlignToXY : function(el, p, o){
8323         el = Ext.get(el);
8324
8325         if(!el || !el.dom){
8326             //<debug>
8327             Ext.Error.raise({
8328                 sourceClass: 'Ext.core.Element',
8329                 sourceMethod: 'getAlignToXY',
8330                 msg: 'Attempted to align an element that doesn\'t exist'
8331             });
8332             //</debug>
8333         }
8334
8335         o = o || [0,0];
8336         p = (!p || p == "?" ? "tl-bl?" : (!(/-/).test(p) && p !== "" ? "tl-" + p : p || "tl-bl")).toLowerCase();
8337
8338         var me = this,
8339             d = me.dom,
8340             a1,
8341             a2,
8342             x,
8343             y,
8344             //constrain the aligned el to viewport if necessary
8345             w,
8346             h,
8347             r,
8348             dw = Ext.core.Element.getViewWidth() -10, // 10px of margin for ie
8349             dh = Ext.core.Element.getViewHeight()-10, // 10px of margin for ie
8350             p1y,
8351             p1x,
8352             p2y,
8353             p2x,
8354             swapY,
8355             swapX,
8356             doc = document,
8357             docElement = doc.documentElement,
8358             docBody = doc.body,
8359             scrollX = (docElement.scrollLeft || docBody.scrollLeft || 0)+5,
8360             scrollY = (docElement.scrollTop || docBody.scrollTop || 0)+5,
8361             c = false, //constrain to viewport
8362             p1 = "",
8363             p2 = "",
8364             m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8365
8366         if(!m){
8367             //<debug>
8368             Ext.Error.raise({
8369                 sourceClass: 'Ext.core.Element',
8370                 sourceMethod: 'getAlignToXY',
8371                 el: el,
8372                 position: p,
8373                 offset: o,
8374                 msg: 'Attemmpted to align an element with an invalid position: "' + p + '"'
8375             });
8376             //</debug>
8377         }
8378
8379         p1 = m[1];
8380         p2 = m[2];
8381         c = !!m[3];
8382
8383         //Subtract the aligned el's internal xy from the target's offset xy
8384         //plus custom offset to get the aligned el's new offset xy
8385         a1 = me.getAnchorXY(p1, true);
8386         a2 = el.getAnchorXY(p2, false);
8387
8388         x = a2[0] - a1[0] + o[0];
8389         y = a2[1] - a1[1] + o[1];
8390
8391         if(c){
8392            w = me.getWidth();
8393            h = me.getHeight();
8394            r = el.getRegion();
8395            //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8396            //perpendicular to the vp border, allow the aligned el to slide on that border,
8397            //otherwise swap the aligned el to the opposite border of the target.
8398            p1y = p1.charAt(0);
8399            p1x = p1.charAt(p1.length-1);
8400            p2y = p2.charAt(0);
8401            p2x = p2.charAt(p2.length-1);
8402            swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8403            swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8404
8405
8406            if (x + w > dw + scrollX) {
8407                 x = swapX ? r.left-w : dw+scrollX-w;
8408            }
8409            if (x < scrollX) {
8410                x = swapX ? r.right : scrollX;
8411            }
8412            if (y + h > dh + scrollY) {
8413                 y = swapY ? r.top-h : dh+scrollY-h;
8414             }
8415            if (y < scrollY){
8416                y = swapY ? r.bottom : scrollY;
8417            }
8418         }
8419         return [x,y];
8420     },
8421
8422     /**
8423      * Aligns this element with another element relative to the specified anchor points. If the other element is the
8424      * document it aligns it to the viewport.
8425      * The position parameter is optional, and can be specified in any one of the following formats:
8426      * <ul>
8427      *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8428      *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8429      *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8430      *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8431      *   <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
8432      *       element's anchor point, and the second value is used as the target's anchor point.</li>
8433      * </ul>
8434      * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8435      * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8436      * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8437      * that specified in order to enforce the viewport constraints.
8438      * Following are all of the supported anchor positions:
8439 <pre>
8440 Value  Description
8441 -----  -----------------------------
8442 tl     The top left corner (default)
8443 t      The center of the top edge
8444 tr     The top right corner
8445 l      The center of the left edge
8446 c      In the center of the element
8447 r      The center of the right edge
8448 bl     The bottom left corner
8449 b      The center of the bottom edge
8450 br     The bottom right corner
8451 </pre>
8452 Example Usage:
8453 <pre><code>
8454 // align el to other-el using the default positioning ("tl-bl", non-constrained)
8455 el.alignTo("other-el");
8456
8457 // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8458 el.alignTo("other-el", "tr?");
8459
8460 // align the bottom right corner of el with the center left edge of other-el
8461 el.alignTo("other-el", "br-l?");
8462
8463 // align the center of el with the bottom left corner of other-el and
8464 // adjust the x position by -6 pixels (and the y position by 0)
8465 el.alignTo("other-el", "c-bl", [-6, 0]);
8466 </code></pre>
8467      * @param {Mixed} element The element to align to.
8468      * @param {String} position (optional, defaults to "tl-bl?") The position to align to.
8469      * @param {Array} offsets (optional) Offset the positioning by [x, y]
8470      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8471      * @return {Ext.core.Element} this
8472      */
8473     alignTo : function(element, position, offsets, animate){
8474         var me = this;
8475         return me.setXY(me.getAlignToXY(element, position, offsets),
8476                         me.anim && !!animate ? me.anim(animate) : false);
8477     },
8478
8479     // private ==>  used outside of core
8480     adjustForConstraints : function(xy, parent) {
8481         var vector = this.getConstrainVector(parent, xy);
8482         if (vector) {
8483             xy[0] += vector[0];
8484             xy[1] += vector[1];
8485         }
8486         return xy;
8487     },
8488
8489     /**
8490      * <p>Returns the <code>[X, Y]</code> vector by which this element must be translated to make a best attempt
8491      * to constrain within the passed constraint. Returns <code>false</code> is this element does not need to be moved.</p>
8492      * <p>Priority is given to constraining the top and left within the constraint.</p>
8493      * <p>The constraint may either be an existing element into which this element is to be constrained, or
8494      * an {@link Ext.util.Region Region} into which this element is to be constrained.</p>
8495      * @param constrainTo {Mixed} The Element or {@link Ext.util.Region Region} into which this element is to be constrained.
8496      * @param proposedPosition {Array} A proposed <code>[X, Y]</code> position to test for validity and to produce a vector for instead
8497      * of using this Element's current position;
8498      * @returns {Array} <b>If</b> this element <i>needs</i> to be translated, an <code>[X, Y]</code>
8499      * vector by which this element must be translated. Otherwise, <code>false</code>.
8500      */
8501     getConstrainVector: function(constrainTo, proposedPosition) {
8502         if (!(constrainTo instanceof Ext.util.Region)) {
8503             constrainTo = Ext.get(constrainTo).getViewRegion();
8504         }
8505         var thisRegion = this.getRegion(),
8506             vector = [0, 0],
8507             shadowSize = this.shadow && this.shadow.offset,
8508             overflowed = false;
8509
8510         // Shift this region to occupy the proposed position
8511         if (proposedPosition) {
8512             thisRegion.translateBy(proposedPosition[0] - thisRegion.x, proposedPosition[1] - thisRegion.y);
8513         }
8514
8515         // Reduce the constrain region to allow for shadow
8516         // TODO: Rewrite the Shadow class. When that's done, get the extra for each side from the Shadow.
8517         if (shadowSize) {
8518             constrainTo.adjust(0, -shadowSize, -shadowSize, shadowSize);
8519         }
8520
8521         // Constrain the X coordinate by however much this Element overflows
8522         if (thisRegion.right > constrainTo.right) {
8523             overflowed = true;
8524             vector[0] = (constrainTo.right - thisRegion.right);    // overflowed the right
8525         }
8526         if (thisRegion.left + vector[0] < constrainTo.left) {
8527             overflowed = true;
8528             vector[0] = (constrainTo.left - thisRegion.left);      // overflowed the left
8529         }
8530
8531         // Constrain the Y coordinate by however much this Element overflows
8532         if (thisRegion.bottom > constrainTo.bottom) {
8533             overflowed = true;
8534             vector[1] = (constrainTo.bottom - thisRegion.bottom);  // overflowed the bottom
8535         }
8536         if (thisRegion.top + vector[1] < constrainTo.top) {
8537             overflowed = true;
8538             vector[1] = (constrainTo.top - thisRegion.top);        // overflowed the top
8539         }
8540         return overflowed ? vector : false;
8541     },
8542
8543     /**
8544     * Calculates the x, y to center this element on the screen
8545     * @return {Array} The x, y values [x, y]
8546     */
8547     getCenterXY : function(){
8548         return this.getAlignToXY(document, 'c-c');
8549     },
8550
8551     /**
8552     * Centers the Element in either the viewport, or another Element.
8553     * @param {Mixed} centerIn (optional) The element in which to center the element.
8554     */
8555     center : function(centerIn){
8556         return this.alignTo(centerIn || document, 'c-c');
8557     }
8558 });
8559
8560 /**
8561  * @class Ext.core.Element
8562  */
8563 (function(){
8564
8565 var ELEMENT = Ext.core.Element,
8566     LEFT = "left",
8567     RIGHT = "right",
8568     TOP = "top",
8569     BOTTOM = "bottom",
8570     POSITION = "position",
8571     STATIC = "static",
8572     RELATIVE = "relative",
8573     AUTO = "auto",
8574     ZINDEX = "z-index";
8575
8576 Ext.override(Ext.core.Element, {
8577     /**
8578       * 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).
8579       * @return {Number} The X position of the element
8580       */
8581     getX : function(){
8582         return ELEMENT.getX(this.dom);
8583     },
8584
8585     /**
8586       * 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).
8587       * @return {Number} The Y position of the element
8588       */
8589     getY : function(){
8590         return ELEMENT.getY(this.dom);
8591     },
8592
8593     /**
8594       * 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).
8595       * @return {Array} The XY position of the element
8596       */
8597     getXY : function(){
8598         return ELEMENT.getXY(this.dom);
8599     },
8600
8601     /**
8602       * 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.
8603       * @param {Mixed} element The element to get the offsets from.
8604       * @return {Array} The XY page offsets (e.g. [100, -200])
8605       */
8606     getOffsetsTo : function(el){
8607         var o = this.getXY(),
8608             e = Ext.fly(el, '_internal').getXY();
8609         return [o[0]-e[0],o[1]-e[1]];
8610     },
8611
8612     /**
8613      * 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).
8614      * @param {Number} The X position of the element
8615      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8616      * @return {Ext.core.Element} this
8617      */
8618     setX : function(x, animate){
8619         return this.setXY([x, this.getY()], animate);
8620     },
8621
8622     /**
8623      * 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).
8624      * @param {Number} The Y position of the element
8625      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8626      * @return {Ext.core.Element} this
8627      */
8628     setY : function(y, animate){
8629         return this.setXY([this.getX(), y], animate);
8630     },
8631
8632     /**
8633      * Sets the element's left position directly using CSS style (instead of {@link #setX}).
8634      * @param {String} left The left CSS property value
8635      * @return {Ext.core.Element} this
8636      */
8637     setLeft : function(left){
8638         this.setStyle(LEFT, this.addUnits(left));
8639         return this;
8640     },
8641
8642     /**
8643      * Sets the element's top position directly using CSS style (instead of {@link #setY}).
8644      * @param {String} top The top CSS property value
8645      * @return {Ext.core.Element} this
8646      */
8647     setTop : function(top){
8648         this.setStyle(TOP, this.addUnits(top));
8649         return this;
8650     },
8651
8652     /**
8653      * Sets the element's CSS right style.
8654      * @param {String} right The right CSS property value
8655      * @return {Ext.core.Element} this
8656      */
8657     setRight : function(right){
8658         this.setStyle(RIGHT, this.addUnits(right));
8659         return this;
8660     },
8661
8662     /**
8663      * Sets the element's CSS bottom style.
8664      * @param {String} bottom The bottom CSS property value
8665      * @return {Ext.core.Element} this
8666      */
8667     setBottom : function(bottom){
8668         this.setStyle(BOTTOM, this.addUnits(bottom));
8669         return this;
8670     },
8671
8672     /**
8673      * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8674      * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8675      * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
8676      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8677      * @return {Ext.core.Element} this
8678      */
8679     setXY: function(pos, animate) {
8680         var me = this;
8681         if (!animate || !me.anim) {
8682             ELEMENT.setXY(me.dom, pos);
8683         }
8684         else {
8685             if (!Ext.isObject(animate)) {
8686                 animate = {};
8687             }
8688             me.animate(Ext.applyIf({ to: { x: pos[0], y: pos[1] } }, animate));
8689         }
8690         return me;
8691     },
8692
8693     /**
8694      * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8695      * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8696      * @param {Number} x X value for new position (coordinates are page-based)
8697      * @param {Number} y Y value for new position (coordinates are page-based)
8698      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8699      * @return {Ext.core.Element} this
8700      */
8701     setLocation : function(x, y, animate){
8702         return this.setXY([x, y], animate);
8703     },
8704
8705     /**
8706      * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8707      * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8708      * @param {Number} x X value for new position (coordinates are page-based)
8709      * @param {Number} y Y value for new position (coordinates are page-based)
8710      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8711      * @return {Ext.core.Element} this
8712      */
8713     moveTo : function(x, y, animate){
8714         return this.setXY([x, y], animate);
8715     },
8716
8717     /**
8718      * Gets the left X coordinate
8719      * @param {Boolean} local True to get the local css position instead of page coordinate
8720      * @return {Number}
8721      */
8722     getLeft : function(local){
8723         return !local ? this.getX() : parseInt(this.getStyle(LEFT), 10) || 0;
8724     },
8725
8726     /**
8727      * Gets the right X coordinate of the element (element X position + element width)
8728      * @param {Boolean} local True to get the local css position instead of page coordinate
8729      * @return {Number}
8730      */
8731     getRight : function(local){
8732         var me = this;
8733         return !local ? me.getX() + me.getWidth() : (me.getLeft(true) + me.getWidth()) || 0;
8734     },
8735
8736     /**
8737      * Gets the top Y coordinate
8738      * @param {Boolean} local True to get the local css position instead of page coordinate
8739      * @return {Number}
8740      */
8741     getTop : function(local) {
8742         return !local ? this.getY() : parseInt(this.getStyle(TOP), 10) || 0;
8743     },
8744
8745     /**
8746      * Gets the bottom Y coordinate of the element (element Y position + element height)
8747      * @param {Boolean} local True to get the local css position instead of page coordinate
8748      * @return {Number}
8749      */
8750     getBottom : function(local){
8751         var me = this;
8752         return !local ? me.getY() + me.getHeight() : (me.getTop(true) + me.getHeight()) || 0;
8753     },
8754
8755     /**
8756     * Initializes positioning on this element. If a desired position is not passed, it will make the
8757     * the element positioned relative IF it is not already positioned.
8758     * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8759     * @param {Number} zIndex (optional) The zIndex to apply
8760     * @param {Number} x (optional) Set the page X position
8761     * @param {Number} y (optional) Set the page Y position
8762     */
8763     position : function(pos, zIndex, x, y) {
8764         var me = this;
8765
8766         if (!pos && me.isStyle(POSITION, STATIC)){
8767             me.setStyle(POSITION, RELATIVE);
8768         } else if(pos) {
8769             me.setStyle(POSITION, pos);
8770         }
8771         if (zIndex){
8772             me.setStyle(ZINDEX, zIndex);
8773         }
8774         if (x || y) {
8775             me.setXY([x || false, y || false]);
8776         }
8777     },
8778
8779     /**
8780     * Clear positioning back to the default when the document was loaded
8781     * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8782     * @return {Ext.core.Element} this
8783      */
8784     clearPositioning : function(value){
8785         value = value || '';
8786         this.setStyle({
8787             left : value,
8788             right : value,
8789             top : value,
8790             bottom : value,
8791             "z-index" : "",
8792             position : STATIC
8793         });
8794         return this;
8795     },
8796
8797     /**
8798     * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8799     * snapshot before performing an update and then restoring the element.
8800     * @return {Object}
8801     */
8802     getPositioning : function(){
8803         var l = this.getStyle(LEFT);
8804         var t = this.getStyle(TOP);
8805         return {
8806             "position" : this.getStyle(POSITION),
8807             "left" : l,
8808             "right" : l ? "" : this.getStyle(RIGHT),
8809             "top" : t,
8810             "bottom" : t ? "" : this.getStyle(BOTTOM),
8811             "z-index" : this.getStyle(ZINDEX)
8812         };
8813     },
8814
8815     /**
8816     * Set positioning with an object returned by getPositioning().
8817     * @param {Object} posCfg
8818     * @return {Ext.core.Element} this
8819      */
8820     setPositioning : function(pc){
8821         var me = this,
8822             style = me.dom.style;
8823
8824         me.setStyle(pc);
8825
8826         if(pc.right == AUTO){
8827             style.right = "";
8828         }
8829         if(pc.bottom == AUTO){
8830             style.bottom = "";
8831         }
8832
8833         return me;
8834     },
8835
8836     /**
8837      * Translates the passed page coordinates into left/top css values for this element
8838      * @param {Number/Array} x The page x or an array containing [x, y]
8839      * @param {Number} y (optional) The page y, required if x is not an array
8840      * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
8841      */
8842     translatePoints: function(x, y) {
8843         if (Ext.isArray(x)) {
8844              y = x[1];
8845              x = x[0];
8846         }
8847         var me = this,
8848             relative = me.isStyle(POSITION, RELATIVE),
8849             o = me.getXY(),
8850             left = parseInt(me.getStyle(LEFT), 10),
8851             top = parseInt(me.getStyle(TOP), 10);
8852
8853         if (!Ext.isNumber(left)) {
8854             left = relative ? 0 : me.dom.offsetLeft;
8855         }
8856         if (!Ext.isNumber(top)) {
8857             top = relative ? 0 : me.dom.offsetTop;
8858         }
8859         left = (Ext.isNumber(x)) ? x - o[0] + left : undefined;
8860         top = (Ext.isNumber(y)) ? y - o[1] + top : undefined;
8861         return {
8862             left: left,
8863             top: top
8864         };
8865     },
8866
8867     /**
8868      * 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.
8869      * @param {Object} box The box to fill {x, y, width, height}
8870      * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8871      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8872      * @return {Ext.core.Element} this
8873      */
8874     setBox: function(box, adjust, animate) {
8875         var me = this,
8876             w = box.width,
8877             h = box.height;
8878         if ((adjust && !me.autoBoxAdjust) && !me.isBorderBox()) {
8879             w -= (me.getBorderWidth("lr") + me.getPadding("lr"));
8880             h -= (me.getBorderWidth("tb") + me.getPadding("tb"));
8881         }
8882         me.setBounds(box.x, box.y, w, h, animate);
8883         return me;
8884     },
8885
8886     /**
8887      * Return an object defining the area of this Element which can be passed to {@link #setBox} to
8888      * set another Element's size/location to match this element.
8889      * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8890      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8891      * @return {Object} box An object in the format<pre><code>
8892 {
8893     x: &lt;Element's X position>,
8894     y: &lt;Element's Y position>,
8895     width: &lt;Element's width>,
8896     height: &lt;Element's height>,
8897     bottom: &lt;Element's lower bound>,
8898     right: &lt;Element's rightmost bound>
8899 }
8900 </code></pre>
8901      * The returned object may also be addressed as an Array where index 0 contains the X position
8902      * and index 1 contains the Y position. So the result may also be used for {@link #setXY}
8903      */
8904     getBox: function(contentBox, local) {
8905         var me = this,
8906             xy,
8907             left,
8908             top,
8909             getBorderWidth = me.getBorderWidth,
8910             getPadding = me.getPadding,
8911             l, r, t, b, w, h, bx;
8912         if (!local) {
8913             xy = me.getXY();
8914         } else {
8915             left = parseInt(me.getStyle("left"), 10) || 0;
8916             top = parseInt(me.getStyle("top"), 10) || 0;
8917             xy = [left, top];
8918         }
8919         w = me.getWidth();
8920         h = me.getHeight();
8921         if (!contentBox) {
8922             bx = {
8923                 x: xy[0],
8924                 y: xy[1],
8925                 0: xy[0],
8926                 1: xy[1],
8927                 width: w,
8928                 height: h
8929             };
8930         } else {
8931             l = getBorderWidth.call(me, "l") + getPadding.call(me, "l");
8932             r = getBorderWidth.call(me, "r") + getPadding.call(me, "r");
8933             t = getBorderWidth.call(me, "t") + getPadding.call(me, "t");
8934             b = getBorderWidth.call(me, "b") + getPadding.call(me, "b");
8935             bx = {
8936                 x: xy[0] + l,
8937                 y: xy[1] + t,
8938                 0: xy[0] + l,
8939                 1: xy[1] + t,
8940                 width: w - (l + r),
8941                 height: h - (t + b)
8942             };
8943         }
8944         bx.right = bx.x + bx.width;
8945         bx.bottom = bx.y + bx.height;
8946         return bx;
8947     },
8948
8949     /**
8950      * Move this element relative to its current position.
8951      * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
8952      * @param {Number} distance How far to move the element in pixels
8953      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8954      * @return {Ext.core.Element} this
8955      */
8956     move: function(direction, distance, animate) {
8957         var me = this,
8958             xy = me.getXY(),
8959             x = xy[0],
8960             y = xy[1],
8961             left = [x - distance, y],
8962             right = [x + distance, y],
8963             top = [x, y - distance],
8964             bottom = [x, y + distance],
8965             hash = {
8966                 l: left,
8967                 left: left,
8968                 r: right,
8969                 right: right,
8970                 t: top,
8971                 top: top,
8972                 up: top,
8973                 b: bottom,
8974                 bottom: bottom,
8975                 down: bottom
8976             };
8977
8978         direction = direction.toLowerCase();
8979         me.moveTo(hash[direction][0], hash[direction][1], animate);
8980     },
8981
8982     /**
8983      * Quick set left and top adding default units
8984      * @param {String} left The left CSS property value
8985      * @param {String} top The top CSS property value
8986      * @return {Ext.core.Element} this
8987      */
8988     setLeftTop: function(left, top) {
8989         var me = this,
8990             style = me.dom.style;
8991         style.left = me.addUnits(left);
8992         style.top = me.addUnits(top);
8993         return me;
8994     },
8995
8996     /**
8997      * Returns the region of this element.
8998      * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
8999      * @return {Region} A Ext.util.Region containing "top, left, bottom, right" member data.
9000      */
9001     getRegion: function() {
9002         return this.getPageBox(true);
9003     },
9004
9005     /**
9006      * Returns the <b>content</b> region of this element. That is the region within the borders and padding.
9007      * @return {Region} A Ext.util.Region containing "top, left, bottom, right" member data.
9008      */
9009     getViewRegion: function() {
9010         var me = this,
9011             isBody = me.dom === document.body,
9012             scroll, pos, top, left, width, height;
9013             
9014         // For the body we want to do some special logic
9015         if (isBody) {
9016             scroll = me.getScroll();
9017             left = scroll.left;
9018             top = scroll.top;
9019             width = Ext.core.Element.getViewportWidth();
9020             height = Ext.core.Element.getViewportHeight();
9021         }
9022         else {
9023             pos = me.getXY();
9024             left = pos[0] + me.getBorderWidth('l') + me.getPadding('l');
9025             top = pos[1] + me.getBorderWidth('t') + me.getPadding('t');
9026             width = me.getWidth(true);
9027             height = me.getHeight(true);
9028         }
9029
9030         return Ext.create('Ext.util.Region', top, left + width, top + height, left);
9031     },
9032
9033     /**
9034      * Return an object defining the area of this Element which can be passed to {@link #setBox} to
9035      * set another Element's size/location to match this element.
9036      * @param {Boolean} asRegion(optional) If true an Ext.util.Region will be returned
9037      * @return {Object} box An object in the format<pre><code>
9038 {
9039     x: &lt;Element's X position>,
9040     y: &lt;Element's Y position>,
9041     width: &lt;Element's width>,
9042     height: &lt;Element's height>,
9043     bottom: &lt;Element's lower bound>,
9044     right: &lt;Element's rightmost bound>
9045 }
9046 </code></pre>
9047      * The returned object may also be addressed as an Array where index 0 contains the X position
9048      * and index 1 contains the Y position. So the result may also be used for {@link #setXY}
9049      */
9050     getPageBox : function(getRegion) {
9051         var me = this,
9052             el = me.dom,
9053             isDoc = el === document.body,
9054             w = isDoc ? Ext.core.Element.getViewWidth()  : el.offsetWidth,
9055             h = isDoc ? Ext.core.Element.getViewHeight() : el.offsetHeight,
9056             xy = me.getXY(),
9057             t = xy[1],
9058             r = xy[0] + w,
9059             b = xy[1] + h,
9060             l = xy[0];
9061
9062         if (getRegion) {
9063             return Ext.create('Ext.util.Region', t, r, b, l);
9064         }
9065         else {
9066             return {
9067                 left: l,
9068                 top: t,
9069                 width: w,
9070                 height: h,
9071                 right: r,
9072                 bottom: b
9073             };
9074         }
9075     },
9076
9077     /**
9078      * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
9079      * @param {Number} x X value for new position (coordinates are page-based)
9080      * @param {Number} y Y value for new position (coordinates are page-based)
9081      * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
9082      * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels)</li>
9083      * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
9084      * </ul></div>
9085      * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
9086      * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels)</li>
9087      * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
9088      * </ul></div>
9089      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9090      * @return {Ext.core.Element} this
9091      */
9092     setBounds: function(x, y, width, height, animate) {
9093         var me = this;
9094         if (!animate || !me.anim) {
9095             me.setSize(width, height);
9096             me.setLocation(x, y);
9097         } else {
9098             if (!Ext.isObject(animate)) {
9099                 animate = {};
9100             }
9101             me.animate(Ext.applyIf({
9102                 to: {
9103                     x: x,
9104                     y: y,
9105                     width: me.adjustWidth(width),
9106                     height: me.adjustHeight(height)
9107                 }
9108             }, animate));
9109         }
9110         return me;
9111     },
9112
9113     /**
9114      * Sets the element's position and size the specified region. If animation is true then width, height, x and y will be animated concurrently.
9115      * @param {Ext.util.Region} region The region to fill
9116      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9117      * @return {Ext.core.Element} this
9118      */
9119     setRegion: function(region, animate) {
9120         return this.setBounds(region.left, region.top, region.right - region.left, region.bottom - region.top, animate);
9121     }
9122 });
9123 })();
9124
9125 /**
9126  * @class Ext.core.Element
9127  */
9128 Ext.override(Ext.core.Element, {
9129     /**
9130      * Returns true if this element is scrollable.
9131      * @return {Boolean}
9132      */
9133     isScrollable : function(){
9134         var dom = this.dom;
9135         return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9136     },
9137
9138     /**
9139      * Returns the current scroll position of the element.
9140      * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9141      */
9142     getScroll : function() {
9143         var d = this.dom, 
9144             doc = document,
9145             body = doc.body,
9146             docElement = doc.documentElement,
9147             l,
9148             t,
9149             ret;
9150
9151         if (d == doc || d == body) {
9152             if (Ext.isIE && Ext.isStrict) {
9153                 l = docElement.scrollLeft; 
9154                 t = docElement.scrollTop;
9155             } else {
9156                 l = window.pageXOffset;
9157                 t = window.pageYOffset;
9158             }
9159             ret = {
9160                 left: l || (body ? body.scrollLeft : 0), 
9161                 top : t || (body ? body.scrollTop : 0)
9162             };
9163         } else {
9164             ret = {
9165                 left: d.scrollLeft, 
9166                 top : d.scrollTop
9167             };
9168         }
9169         
9170         return ret;
9171     },
9172     
9173     /**
9174      * 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().
9175      * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9176      * @param {Number} value The new scroll value
9177      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9178      * @return {Element} this
9179      */
9180     scrollTo : function(side, value, animate) {
9181         //check if we're scrolling top or left
9182         var top = /top/i.test(side),
9183             me = this,
9184             dom = me.dom,
9185             obj = {},
9186             prop;
9187         if (!animate || !me.anim) {
9188             // just setting the value, so grab the direction
9189             prop = 'scroll' + (top ? 'Top' : 'Left');
9190             dom[prop] = value;
9191         }
9192         else {
9193             if (!Ext.isObject(animate)) {
9194                 animate = {};
9195             }
9196             obj['scroll' + (top ? 'Top' : 'Left')] = value;
9197             me.animate(Ext.applyIf({
9198                 to: obj
9199             }, animate));
9200         }
9201         return me;
9202     },
9203
9204     /**
9205      * Scrolls this element into view within the passed container.
9206      * @param {Mixed} container (optional) The container element to scroll (defaults to document.body).  Should be a
9207      * string (id), dom node, or Ext.core.Element.
9208      * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
9209      * @return {Ext.core.Element} this
9210      */
9211     scrollIntoView : function(container, hscroll) {
9212         container = Ext.getDom(container) || Ext.getBody().dom;
9213         var el = this.dom,
9214             offsets = this.getOffsetsTo(container),
9215             // el's box
9216             left = offsets[0] + container.scrollLeft,
9217             top = offsets[1] + container.scrollTop,
9218             bottom = top + el.offsetHeight,
9219             right = left + el.offsetWidth,
9220             // ct's box
9221             ctClientHeight = container.clientHeight,
9222             ctScrollTop = parseInt(container.scrollTop, 10),
9223             ctScrollLeft = parseInt(container.scrollLeft, 10),
9224             ctBottom = ctScrollTop + ctClientHeight,
9225             ctRight = ctScrollLeft + container.clientWidth;
9226
9227         if (el.offsetHeight > ctClientHeight || top < ctScrollTop) {
9228             container.scrollTop = top;
9229         } else if (bottom > ctBottom) {
9230             container.scrollTop = bottom - ctClientHeight;
9231         }
9232         // corrects IE, other browsers will ignore
9233         container.scrollTop = container.scrollTop;
9234
9235         if (hscroll !== false) {
9236             if (el.offsetWidth > container.clientWidth || left < ctScrollLeft) {
9237                 container.scrollLeft = left;
9238             }
9239             else if (right > ctRight) {
9240                 container.scrollLeft = right - container.clientWidth;
9241             }
9242             container.scrollLeft = container.scrollLeft;
9243         }
9244         return this;
9245     },
9246
9247     // private
9248     scrollChildIntoView : function(child, hscroll) {
9249         Ext.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
9250     },
9251
9252     /**
9253      * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9254      * within this element's scrollable range.
9255      * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
9256      * @param {Number} distance How far to scroll the element in pixels
9257      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9258      * @return {Boolean} Returns true if a scroll was triggered or false if the element
9259      * was scrolled as far as it could go.
9260      */
9261      scroll : function(direction, distance, animate) {
9262         if (!this.isScrollable()) {
9263             return false;
9264         }
9265         var el = this.dom,
9266             l = el.scrollLeft, t = el.scrollTop,
9267             w = el.scrollWidth, h = el.scrollHeight,
9268             cw = el.clientWidth, ch = el.clientHeight,
9269             scrolled = false, v,
9270             hash = {
9271                 l: Math.min(l + distance, w-cw),
9272                 r: v = Math.max(l - distance, 0),
9273                 t: Math.max(t - distance, 0),
9274                 b: Math.min(t + distance, h-ch)
9275             };
9276             hash.d = hash.b;
9277             hash.u = hash.t;
9278
9279         direction = direction.substr(0, 1);
9280         if ((v = hash[direction]) > -1) {
9281             scrolled = true;
9282             this.scrollTo(direction == 'l' || direction == 'r' ? 'left' : 'top', v, this.anim(animate));
9283         }
9284         return scrolled;
9285     }
9286 });
9287 /**
9288  * @class Ext.core.Element
9289  */
9290 Ext.core.Element.addMethods(
9291     function() {
9292         var VISIBILITY      = "visibility",
9293             DISPLAY         = "display",
9294             HIDDEN          = "hidden",
9295             NONE            = "none",
9296             XMASKED         = Ext.baseCSSPrefix + "masked",
9297             XMASKEDRELATIVE = Ext.baseCSSPrefix + "masked-relative",
9298             data            = Ext.core.Element.data;
9299
9300         return {
9301             /**
9302              * Checks whether the element is currently visible using both visibility and display properties.
9303              * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
9304              * @return {Boolean} True if the element is currently visible, else false
9305              */
9306             isVisible : function(deep) {
9307                 var vis = !this.isStyle(VISIBILITY, HIDDEN) && !this.isStyle(DISPLAY, NONE),
9308                     p   = this.dom.parentNode;
9309
9310                 if (deep !== true || !vis) {
9311                     return vis;
9312                 }
9313
9314                 while (p && !(/^body/i.test(p.tagName))) {
9315                     if (!Ext.fly(p, '_isVisible').isVisible()) {
9316                         return false;
9317                     }
9318                     p = p.parentNode;
9319                 }
9320                 return true;
9321             },
9322
9323             /**
9324              * Returns true if display is not "none"
9325              * @return {Boolean}
9326              */
9327             isDisplayed : function() {
9328                 return !this.isStyle(DISPLAY, NONE);
9329             },
9330
9331             /**
9332              * Convenience method for setVisibilityMode(Element.DISPLAY)
9333              * @param {String} display (optional) What to set display to when visible
9334              * @return {Ext.core.Element} this
9335              */
9336             enableDisplayMode : function(display) {
9337                 this.setVisibilityMode(Ext.core.Element.DISPLAY);
9338
9339                 if (!Ext.isEmpty(display)) {
9340                     data(this.dom, 'originalDisplay', display);
9341                 }
9342
9343                 return this;
9344             },
9345
9346             /**
9347              * Puts a mask over this element to disable user interaction. Requires core.css.
9348              * This method can only be applied to elements which accept child nodes.
9349              * @param {String} msg (optional) A message to display in the mask
9350              * @param {String} msgCls (optional) A css class to apply to the msg element
9351              * @return {Element} The mask element
9352              */
9353             mask : function(msg, msgCls) {
9354                 var me  = this,
9355                     dom = me.dom,
9356                     setExpression = dom.style.setExpression,
9357                     dh  = Ext.core.DomHelper,
9358                     EXTELMASKMSG = Ext.baseCSSPrefix + "mask-msg",
9359                     el,
9360                     mask;
9361
9362                 if (!(/^body/i.test(dom.tagName) && me.getStyle('position') == 'static')) {
9363                     me.addCls(XMASKEDRELATIVE);
9364                 }
9365                 el = data(dom, 'maskMsg');
9366                 if (el) {
9367                     el.remove();
9368                 }
9369                 el = data(dom, 'mask');
9370                 if (el) {
9371                     el.remove();
9372                 }
9373
9374                 mask = dh.append(dom, {cls : Ext.baseCSSPrefix + "mask"}, true);
9375                 data(dom, 'mask', mask);
9376
9377                 me.addCls(XMASKED);
9378                 mask.setDisplayed(true);
9379
9380                 if (typeof msg == 'string') {
9381                     var mm = dh.append(dom, {cls : EXTELMASKMSG, cn:{tag:'div'}}, true);
9382                     data(dom, 'maskMsg', mm);
9383                     mm.dom.className = msgCls ? EXTELMASKMSG + " " + msgCls : EXTELMASKMSG;
9384                     mm.dom.firstChild.innerHTML = msg;
9385                     mm.setDisplayed(true);
9386                     mm.center(me);
9387                 }
9388                 // NOTE: CSS expressions are resource intensive and to be used only as a last resort
9389                 // These expressions are removed as soon as they are no longer necessary - in the unmask method.
9390                 // In normal use cases an element will be masked for a limited period of time.
9391                 // Fix for https://sencha.jira.com/browse/EXTJSIV-19.
9392                 // IE6 strict mode and IE6-9 quirks mode takes off left+right padding when calculating width!
9393                 if (!Ext.supports.IncludePaddingInWidthCalculation && setExpression) {
9394                     mask.dom.style.setExpression('width', 'this.parentNode.offsetWidth + "px"');
9395                 }
9396
9397                 // Some versions and modes of IE subtract top+bottom padding when calculating height.
9398                 // Different versions from those which make the same error for width!
9399                 if (!Ext.supports.IncludePaddingInHeightCalculation && setExpression) {
9400                     mask.dom.style.setExpression('height', 'this.parentNode.offsetHeight + "px"');
9401                 }
9402                 // ie will not expand full height automatically
9403                 else if (Ext.isIE && !(Ext.isIE7 && Ext.isStrict) && me.getStyle('height') == 'auto') {
9404                     mask.setSize(undefined, me.getHeight());
9405                 }
9406                 return mask;
9407             },
9408
9409             /**
9410              * Removes a previously applied mask.
9411              */
9412             unmask : function() {
9413                 var me      = this,
9414                     dom     = me.dom,
9415                     mask    = data(dom, 'mask'),
9416                     maskMsg = data(dom, 'maskMsg');
9417
9418                 if (mask) {
9419                     // Remove resource-intensive CSS expressions as soon as they are not required.
9420                     if (mask.dom.style.clearExpression) {
9421                         mask.dom.style.clearExpression('width');
9422                         mask.dom.style.clearExpression('height');
9423                     }
9424                     if (maskMsg) {
9425                         maskMsg.remove();
9426                         data(dom, 'maskMsg', undefined);
9427                     }
9428
9429                     mask.remove();
9430                     data(dom, 'mask', undefined);
9431                     me.removeCls([XMASKED, XMASKEDRELATIVE]);
9432                 }
9433             },
9434             /**
9435              * Returns true if this element is masked. Also re-centers any displayed message within the mask.
9436              * @return {Boolean}
9437              */
9438             isMasked : function() {
9439                 var me = this,
9440                     mask = data(me.dom, 'mask'),
9441                     maskMsg = data(me.dom, 'maskMsg');
9442
9443                 if (mask && mask.isVisible()) {
9444                     if (maskMsg) {
9445                         maskMsg.center(me);
9446                     }
9447                     return true;
9448                 }
9449                 return false;
9450             },
9451
9452             /**
9453              * Creates an iframe shim for this element to keep selects and other windowed objects from
9454              * showing through.
9455              * @return {Ext.core.Element} The new shim element
9456              */
9457             createShim : function() {
9458                 var el = document.createElement('iframe'),
9459                     shim;
9460
9461                 el.frameBorder = '0';
9462                 el.className = Ext.baseCSSPrefix + 'shim';
9463                 el.src = Ext.SSL_SECURE_URL;
9464                 shim = Ext.get(this.dom.parentNode.insertBefore(el, this.dom));
9465                 shim.autoBoxAdjust = false;
9466                 return shim;
9467             }
9468         };
9469     }()
9470 );
9471 /**
9472  * @class Ext.core.Element
9473  */
9474 Ext.core.Element.addMethods({
9475     /**
9476      * Convenience method for constructing a KeyMap
9477      * @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:
9478      * <code>{key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}</code>
9479      * @param {Function} fn The function to call
9480      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the specified function is executed. Defaults to this Element.
9481      * @return {Ext.util.KeyMap} The KeyMap created
9482      */
9483     addKeyListener : function(key, fn, scope){
9484         var config;
9485         if(typeof key != 'object' || Ext.isArray(key)){
9486             config = {
9487                 key: key,
9488                 fn: fn,
9489                 scope: scope
9490             };
9491         }else{
9492             config = {
9493                 key : key.key,
9494                 shift : key.shift,
9495                 ctrl : key.ctrl,
9496                 alt : key.alt,
9497                 fn: fn,
9498                 scope: scope
9499             };
9500         }
9501         return Ext.create('Ext.util.KeyMap', this, config);
9502     },
9503
9504     /**
9505      * Creates a KeyMap for this element
9506      * @param {Object} config The KeyMap config. See {@link Ext.util.KeyMap} for more details
9507      * @return {Ext.util.KeyMap} The KeyMap created
9508      */
9509     addKeyMap : function(config){
9510         return Ext.create('Ext.util.KeyMap', this, config);
9511     }
9512 });
9513
9514 //Import the newly-added Ext.core.Element functions into CompositeElementLite. We call this here because
9515 //Element.keys.js is the last extra Ext.core.Element include in the ext-all.js build
9516 Ext.CompositeElementLite.importElementMethods();
9517
9518 /**
9519  * @class Ext.CompositeElementLite
9520  */
9521 Ext.apply(Ext.CompositeElementLite.prototype, {
9522     addElements : function(els, root){
9523         if(!els){
9524             return this;
9525         }
9526         if(typeof els == "string"){
9527             els = Ext.core.Element.selectorFunction(els, root);
9528         }
9529         var yels = this.elements;
9530         Ext.each(els, function(e) {
9531             yels.push(Ext.get(e));
9532         });
9533         return this;
9534     },
9535
9536     /**
9537      * Returns the first Element
9538      * @return {Ext.core.Element}
9539      */
9540     first : function(){
9541         return this.item(0);
9542     },
9543
9544     /**
9545      * Returns the last Element
9546      * @return {Ext.core.Element}
9547      */
9548     last : function(){
9549         return this.item(this.getCount()-1);
9550     },
9551
9552     /**
9553      * Returns true if this composite contains the passed element
9554      * @param el {Mixed} The id of an element, or an Ext.core.Element, or an HtmlElement to find within the composite collection.
9555      * @return Boolean
9556      */
9557     contains : function(el){
9558         return this.indexOf(el) != -1;
9559     },
9560
9561     /**
9562     * Removes the specified element(s).
9563     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
9564     * or an array of any of those.
9565     * @param {Boolean} removeDom (optional) True to also remove the element from the document
9566     * @return {CompositeElement} this
9567     */
9568     removeElement : function(keys, removeDom){
9569         var me = this,
9570             els = this.elements,
9571             el;
9572         Ext.each(keys, function(val){
9573             if ((el = (els[val] || els[val = me.indexOf(val)]))) {
9574                 if(removeDom){
9575                     if(el.dom){
9576                         el.remove();
9577                     }else{
9578                         Ext.removeNode(el);
9579                     }
9580                 }
9581                 els.splice(val, 1);
9582             }
9583         });
9584         return this;
9585     }
9586 });
9587
9588 /**
9589  * @class Ext.CompositeElement
9590  * @extends Ext.CompositeElementLite
9591  * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter
9592  * members, or to perform collective actions upon the whole set.</p>
9593  * <p>Although they are not listed, this class supports all of the methods of {@link Ext.core.Element} and
9594  * {@link Ext.fx.Anim}. The methods from these classes will be performed on all the elements in this collection.</p>
9595  * <p>All methods return <i>this</i> and can be chained.</p>
9596  * Usage:
9597 <pre><code>
9598 var els = Ext.select("#some-el div.some-class", true);
9599 // or select directly from an existing element
9600 var el = Ext.get('some-el');
9601 el.select('div.some-class', true);
9602
9603 els.setWidth(100); // all elements become 100 width
9604 els.hide(true); // all elements fade out and hide
9605 // or
9606 els.setWidth(100).hide(true);
9607 </code></pre>
9608  */
9609 Ext.CompositeElement = Ext.extend(Ext.CompositeElementLite, {
9610     
9611     constructor : function(els, root){
9612         this.elements = [];
9613         this.add(els, root);
9614     },
9615     
9616     // private
9617     getElement : function(el){
9618         // In this case just return it, since we already have a reference to it
9619         return el;
9620     },
9621     
9622     // private
9623     transformElement : function(el){
9624         return Ext.get(el);
9625     }
9626
9627     /**
9628     * Adds elements to this composite.
9629     * @param {String/Array} els A string CSS selector, an array of elements or an element
9630     * @return {CompositeElement} this
9631     */
9632
9633     /**
9634      * Returns the Element object at the specified index
9635      * @param {Number} index
9636      * @return {Ext.core.Element}
9637      */
9638
9639     /**
9640      * Iterates each `element` in this `composite` calling the supplied function using {@link Ext#each Ext.each}.
9641      * @param {Function} fn 
9642
9643 The function to be called with each
9644 `element`. If the supplied function returns <tt>false</tt>,
9645 iteration stops. This function is called with the following arguments:
9646
9647 - `element` : __Ext.core.Element++
9648     The element at the current `index` in the `composite`
9649     
9650 - `composite` : __Object__ 
9651     This composite.
9652
9653 - `index` : __Number__ 
9654     The current index within the `composite`
9655
9656      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the specified function is executed.
9657      * Defaults to the <code>element</code> at the current <code>index</code>
9658      * within the composite.
9659      * @return {CompositeElement} this
9660      * @markdown
9661      */
9662 });
9663
9664 /**
9665  * Selects elements based on the passed CSS selector to enable {@link Ext.core.Element Element} methods
9666  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
9667  * {@link Ext.CompositeElementLite CompositeElementLite} object.
9668  * @param {String/Array} selector The CSS selector or an array of elements
9669  * @param {Boolean} unique (optional) true to create a unique Ext.core.Element for each element (defaults to a shared flyweight object)
9670  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9671  * @return {CompositeElementLite/CompositeElement}
9672  * @member Ext.core.Element
9673  * @method select
9674  */
9675 Ext.core.Element.select = function(selector, unique, root){
9676     var els;
9677     if(typeof selector == "string"){
9678         els = Ext.core.Element.selectorFunction(selector, root);
9679     }else if(selector.length !== undefined){
9680         els = selector;
9681     }else{
9682         //<debug>
9683         Ext.Error.raise({
9684             sourceClass: "Ext.core.Element",
9685             sourceMethod: "select",
9686             selector: selector,
9687             unique: unique,
9688             root: root,
9689             msg: "Invalid selector specified: " + selector
9690         });
9691         //</debug>
9692     }
9693     return (unique === true) ? new Ext.CompositeElement(els) : new Ext.CompositeElementLite(els);
9694 };
9695
9696 /**
9697  * Selects elements based on the passed CSS selector to enable {@link Ext.core.Element Element} methods
9698  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
9699  * {@link Ext.CompositeElementLite CompositeElementLite} object.
9700  * @param {String/Array} selector The CSS selector or an array of elements
9701  * @param {Boolean} unique (optional) true to create a unique Ext.core.Element for each element (defaults to a shared flyweight object)
9702  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9703  * @return {CompositeElementLite/CompositeElement}
9704  * @member Ext
9705  * @method select
9706  */
9707 Ext.select = Ext.core.Element.select;
9708