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