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