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