commit extjs-2.2.1
[extjs.git] / source / core / Element.js
1 /*\r
2  * Ext JS Library 2.2.1\r
3  * Copyright(c) 2006-2009, Ext JS, LLC.\r
4  * licensing@extjs.com\r
5  * \r
6  * http://extjs.com/license\r
7  */\r
8 \r
9 /**\r
10  * @class Ext.Element\r
11  * Represents an Element in the DOM.<br><br>\r
12  * Usage:<br>\r
13 <pre><code>\r
14 // by id\r
15 var el = Ext.get("my-div");\r
16 \r
17 // by DOM element reference\r
18 var el = Ext.get(myDivElement);\r
19 </code></pre>\r
20  * <b>Animations</b><br />\r
21  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter\r
22  * should either be a boolean (true) or an object literal with animation options. Note that the supported Element animation\r
23  * options are a subset of the {@link Ext.Fx} animation options specific to Fx effects.  The Element animation options are:\r
24 <pre>\r
25 Option    Default   Description\r
26 --------- --------  ---------------------------------------------\r
27 duration  .35       The duration of the animation in seconds\r
28 easing    easeOut   The easing method\r
29 callback  none      A function to execute when the anim completes\r
30 scope     this      The scope (this) of the callback function\r
31 </pre>\r
32 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or\r
33 * manipulate the animation. Here's an example:\r
34 <pre><code>\r
35 var el = Ext.get("my-div");\r
36 \r
37 // no animation\r
38 el.setWidth(100);\r
39 \r
40 // default animation\r
41 el.setWidth(100, true);\r
42 \r
43 // animation with some options set\r
44 el.setWidth(100, {\r
45     duration: 1,\r
46     callback: this.foo,\r
47     scope: this\r
48 });\r
49 \r
50 // using the "anim" property to get the Anim object\r
51 var opt = {\r
52     duration: 1,\r
53     callback: this.foo,\r
54     scope: this\r
55 };\r
56 el.setWidth(100, opt);\r
57 ...\r
58 if(opt.anim.isAnimated()){\r
59     opt.anim.stop();\r
60 }\r
61 </code></pre>\r
62 * <b> Composite (Collections of) Elements</b><br />\r
63  * For working with collections of Elements, see {@link Ext.CompositeElement}\r
64  * @constructor Create a new Element directly.\r
65  * @param {String/HTMLElement} element\r
66  * @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).\r
67  */\r
68 (function(){\r
69 var D = Ext.lib.Dom;\r
70 var E = Ext.lib.Event;\r
71 var A = Ext.lib.Anim;\r
72 \r
73 // local style camelizing for speed\r
74 var propCache = {};\r
75 var camelRe = /(-[a-z])/gi;\r
76 var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };\r
77 var view = document.defaultView;\r
78 \r
79 Ext.Element = function(element, forceNew){\r
80     var dom = typeof element == "string" ?\r
81             document.getElementById(element) : element;\r
82     if(!dom){ // invalid id/element\r
83         return null;\r
84     }\r
85     var id = dom.id;\r
86     if(forceNew !== true && id && Ext.Element.cache[id]){ // element object already exists\r
87         return Ext.Element.cache[id];\r
88     }\r
89 \r
90     /**\r
91      * The DOM element\r
92      * @type HTMLElement\r
93      */\r
94     this.dom = dom;\r
95 \r
96     /**\r
97      * The DOM element ID\r
98      * @type String\r
99      */\r
100     this.id = id || Ext.id(dom);\r
101 };\r
102 \r
103 var El = Ext.Element;\r
104 \r
105 El.prototype = {\r
106     /**\r
107      * The element's default display mode  (defaults to "")\r
108      * @type String\r
109      */\r
110     originalDisplay : "",\r
111 \r
112     visibilityMode : 1,\r
113     /**\r
114      * The default unit to append to CSS values where a unit isn't provided (defaults to px).\r
115      * @type String\r
116      */\r
117     defaultUnit : "px",\r
118     /**\r
119      * Sets the element's visibility mode. When setVisible() is called it\r
120      * will use this to determine whether to set the visibility or the display property.\r
121      * @param visMode Element.VISIBILITY or Element.DISPLAY\r
122      * @return {Ext.Element} this\r
123      */\r
124     setVisibilityMode : function(visMode){\r
125         this.visibilityMode = visMode;\r
126         return this;\r
127     },\r
128     /**\r
129      * Convenience method for setVisibilityMode(Element.DISPLAY)\r
130      * @param {String} display (optional) What to set display to when visible\r
131      * @return {Ext.Element} this\r
132      */\r
133     enableDisplayMode : function(display){\r
134         this.setVisibilityMode(El.DISPLAY);\r
135         if(typeof display != "undefined") this.originalDisplay = display;\r
136         return this;\r
137     },\r
138 \r
139     /**\r
140      * 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)\r
141      * @param {String} selector The simple selector to test\r
142      * @param {Number/Mixed} maxDepth (optional) The max depth to\r
143             search as a number or element (defaults to 10 || document.body)\r
144      * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node\r
145      * @return {HTMLElement} The matching DOM node (or null if no match was found)\r
146      */\r
147     findParent : function(simpleSelector, maxDepth, returnEl){\r
148         var p = this.dom, b = document.body, depth = 0, dq = Ext.DomQuery, stopEl;\r
149         maxDepth = maxDepth || 50;\r
150         if(typeof maxDepth != "number"){\r
151             stopEl = Ext.getDom(maxDepth);\r
152             maxDepth = 10;\r
153         }\r
154         while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){\r
155             if(dq.is(p, simpleSelector)){\r
156                 return returnEl ? Ext.get(p) : p;\r
157             }\r
158             depth++;\r
159             p = p.parentNode;\r
160         }\r
161         return null;\r
162     },\r
163 \r
164 \r
165     /**\r
166      * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)\r
167      * @param {String} selector The simple selector to test\r
168      * @param {Number/Mixed} maxDepth (optional) The max depth to\r
169             search as a number or element (defaults to 10 || document.body)\r
170      * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node\r
171      * @return {HTMLElement} The matching DOM node (or null if no match was found)\r
172      */\r
173     findParentNode : function(simpleSelector, maxDepth, returnEl){\r
174         var p = Ext.fly(this.dom.parentNode, '_internal');\r
175         return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;\r
176     },\r
177 \r
178     /**\r
179      * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).\r
180      * This is a shortcut for findParentNode() that always returns an Ext.Element.\r
181      * @param {String} selector The simple selector to test\r
182      * @param {Number/Mixed} maxDepth (optional) The max depth to\r
183             search as a number or element (defaults to 10 || document.body)\r
184      * @return {Ext.Element} The matching DOM node (or null if no match was found)\r
185      */\r
186     up : function(simpleSelector, maxDepth){\r
187         return this.findParentNode(simpleSelector, maxDepth, true);\r
188     },\r
189 \r
190 \r
191 \r
192     /**\r
193      * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)\r
194      * @param {String} selector The simple selector to test\r
195      * @return {Boolean} True if this element matches the selector, else false\r
196      */\r
197     is : function(simpleSelector){\r
198         return Ext.DomQuery.is(this.dom, simpleSelector);\r
199     },\r
200 \r
201     /**\r
202      * Perform animation on this element.\r
203      * @param {Object} args The animation control args\r
204      * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)\r
205      * @param {Function} onComplete (optional) Function to call when animation completes\r
206      * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')\r
207      * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'\r
208      * @return {Ext.Element} this\r
209      */\r
210     animate : function(args, duration, onComplete, easing, animType){\r
211         this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);\r
212         return this;\r
213     },\r
214 \r
215     /*\r
216      * @private Internal animation call\r
217      */\r
218     anim : function(args, opt, animType, defaultDur, defaultEase, cb){\r
219         animType = animType || 'run';\r
220         opt = opt || {};\r
221         var anim = Ext.lib.Anim[animType](\r
222             this.dom, args,\r
223             (opt.duration || defaultDur) || .35,\r
224             (opt.easing || defaultEase) || 'easeOut',\r
225             function(){\r
226                 Ext.callback(cb, this);\r
227                 Ext.callback(opt.callback, opt.scope || this, [this, opt]);\r
228             },\r
229             this\r
230         );\r
231         opt.anim = anim;\r
232         return anim;\r
233     },\r
234 \r
235     // private legacy anim prep\r
236     preanim : function(a, i){\r
237         return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});\r
238     },\r
239 \r
240     /**\r
241      * Removes worthless text nodes\r
242      * @param {Boolean} forceReclean (optional) By default the element\r
243      * keeps track if it has been cleaned already so\r
244      * you can call this over and over. However, if you update the element and\r
245      * need to force a reclean, you can pass true.\r
246      */\r
247     clean : function(forceReclean){\r
248         if(this.isCleaned && forceReclean !== true){\r
249             return this;\r
250         }\r
251         var ns = /\S/;\r
252         var d = this.dom, n = d.firstChild, ni = -1;\r
253             while(n){\r
254                 var nx = n.nextSibling;\r
255                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){\r
256                     d.removeChild(n);\r
257                 }else{\r
258                     n.nodeIndex = ++ni;\r
259                 }\r
260                 n = nx;\r
261             }\r
262             this.isCleaned = true;\r
263             return this;\r
264         },\r
265 \r
266     /**\r
267      * Scrolls this element into view within the passed container.\r
268      * @param {Mixed} container (optional) The container element to scroll (defaults to document.body).  Should be a\r
269      * string (id), dom node, or Ext.Element.\r
270      * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)\r
271      * @return {Ext.Element} this\r
272      */\r
273     scrollIntoView : function(container, hscroll){\r
274         var c = Ext.getDom(container) || Ext.getBody().dom;\r
275         var el = this.dom;\r
276 \r
277         var o = this.getOffsetsTo(c),\r
278             l = o[0] + c.scrollLeft,\r
279             t = o[1] + c.scrollTop,\r
280             b = t+el.offsetHeight,\r
281             r = l+el.offsetWidth;\r
282 \r
283         var ch = c.clientHeight;\r
284         var ct = parseInt(c.scrollTop, 10);\r
285         var cl = parseInt(c.scrollLeft, 10);\r
286         var cb = ct + ch;\r
287         var cr = cl + c.clientWidth;\r
288 \r
289         if(el.offsetHeight > ch || t < ct){\r
290                 c.scrollTop = t;\r
291         }else if(b > cb){\r
292             c.scrollTop = b-ch;\r
293         }\r
294         c.scrollTop = c.scrollTop; // corrects IE, other browsers will ignore\r
295 \r
296         if(hscroll !== false){\r
297                         if(el.offsetWidth > c.clientWidth || l < cl){\r
298                 c.scrollLeft = l;\r
299             }else if(r > cr){\r
300                 c.scrollLeft = r-c.clientWidth;\r
301             }\r
302             c.scrollLeft = c.scrollLeft;\r
303         }\r
304         return this;\r
305     },\r
306 \r
307     // private\r
308     scrollChildIntoView : function(child, hscroll){\r
309         Ext.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);\r
310     },\r
311 \r
312     /**\r
313      * Measures the element's content height and updates height to match. Note: this function uses setTimeout so\r
314      * the new height may not be available immediately.\r
315      * @param {Boolean} animate (optional) Animate the transition (defaults to false)\r
316      * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)\r
317      * @param {Function} onComplete (optional) Function to call when animation completes\r
318      * @param {String} easing (optional) Easing method to use (defaults to easeOut)\r
319      * @return {Ext.Element} this\r
320      */\r
321     autoHeight : function(animate, duration, onComplete, easing){\r
322         var oldHeight = this.getHeight();\r
323         this.clip();\r
324         this.setHeight(1); // force clipping\r
325         setTimeout(function(){\r
326             var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari\r
327             if(!animate){\r
328                 this.setHeight(height);\r
329                 this.unclip();\r
330                 if(typeof onComplete == "function"){\r
331                     onComplete();\r
332                 }\r
333             }else{\r
334                 this.setHeight(oldHeight); // restore original height\r
335                 this.setHeight(height, animate, duration, function(){\r
336                     this.unclip();\r
337                     if(typeof onComplete == "function") onComplete();\r
338                 }.createDelegate(this), easing);\r
339             }\r
340         }.createDelegate(this), 0);\r
341         return this;\r
342     },\r
343 \r
344     /**\r
345      * Returns true if this element is an ancestor of the passed element\r
346      * @param {HTMLElement/String} el The element to check\r
347      * @return {Boolean} True if this element is an ancestor of el, else false\r
348      */\r
349     contains : function(el){\r
350         if(!el){return false;}\r
351         return D.isAncestor(this.dom, el.dom ? el.dom : el);\r
352     },\r
353 \r
354     /**\r
355      * Checks whether the element is currently visible using both visibility and display properties.\r
356      * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)\r
357      * @return {Boolean} True if the element is currently visible, else false\r
358      */\r
359     isVisible : function(deep) {\r
360         var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");\r
361         if(deep !== true || !vis){\r
362             return vis;\r
363         }\r
364         var p = this.dom.parentNode;\r
365         while(p && p.tagName.toLowerCase() != "body"){\r
366             if(!Ext.fly(p, '_isVisible').isVisible()){\r
367                 return false;\r
368             }\r
369             p = p.parentNode;\r
370         }\r
371         return true;\r
372     },\r
373 \r
374     /**\r
375      * Creates a {@link Ext.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).\r
376      * @param {String} selector The CSS selector\r
377      * @param {Boolean} unique (optional) True to create a unique Ext.Element for each child (defaults to false, which creates a single shared flyweight object)\r
378      * @return {CompositeElement/CompositeElementLite} The composite element\r
379      */\r
380     select : function(selector, unique){\r
381         return El.select(selector, unique, this.dom);\r
382     },\r
383 \r
384     /**\r
385      * Selects child nodes based on the passed CSS selector (the selector should not contain an id).\r
386      * @param {String} selector The CSS selector\r
387      * @return {Array} An array of the matched nodes\r
388      */\r
389     query : function(selector){\r
390         return Ext.DomQuery.select(selector, this.dom);\r
391     },\r
392 \r
393     /**\r
394      * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).\r
395      * @param {String} selector The CSS selector\r
396      * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.Element (defaults to false)\r
397      * @return {HTMLElement/Ext.Element} The child Ext.Element (or DOM node if returnDom = true)\r
398      */\r
399     child : function(selector, returnDom){\r
400         var n = Ext.DomQuery.selectNode(selector, this.dom);\r
401         return returnDom ? n : Ext.get(n);\r
402     },\r
403 \r
404     /**\r
405      * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).\r
406      * @param {String} selector The CSS selector\r
407      * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.Element (defaults to false)\r
408      * @return {HTMLElement/Ext.Element} The child Ext.Element (or DOM node if returnDom = true)\r
409      */\r
410     down : function(selector, returnDom){\r
411         var n = Ext.DomQuery.selectNode(" > " + selector, this.dom);\r
412         return returnDom ? n : Ext.get(n);\r
413     },\r
414 \r
415     /**\r
416      * Initializes a {@link Ext.dd.DD} drag drop object for this element.\r
417      * @param {String} group The group the DD object is member of\r
418      * @param {Object} config The DD config object\r
419      * @param {Object} overrides An object containing methods to override/implement on the DD object\r
420      * @return {Ext.dd.DD} The DD object\r
421      */\r
422     initDD : function(group, config, overrides){\r
423         var dd = new Ext.dd.DD(Ext.id(this.dom), group, config);\r
424         return Ext.apply(dd, overrides);\r
425     },\r
426 \r
427     /**\r
428      * Initializes a {@link Ext.dd.DDProxy} object for this element.\r
429      * @param {String} group The group the DDProxy object is member of\r
430      * @param {Object} config The DDProxy config object\r
431      * @param {Object} overrides An object containing methods to override/implement on the DDProxy object\r
432      * @return {Ext.dd.DDProxy} The DDProxy object\r
433      */\r
434     initDDProxy : function(group, config, overrides){\r
435         var dd = new Ext.dd.DDProxy(Ext.id(this.dom), group, config);\r
436         return Ext.apply(dd, overrides);\r
437     },\r
438 \r
439     /**\r
440      * Initializes a {@link Ext.dd.DDTarget} object for this element.\r
441      * @param {String} group The group the DDTarget object is member of\r
442      * @param {Object} config The DDTarget config object\r
443      * @param {Object} overrides An object containing methods to override/implement on the DDTarget object\r
444      * @return {Ext.dd.DDTarget} The DDTarget object\r
445      */\r
446     initDDTarget : function(group, config, overrides){\r
447         var dd = new Ext.dd.DDTarget(Ext.id(this.dom), group, config);\r
448         return Ext.apply(dd, overrides);\r
449     },\r
450 \r
451     /**\r
452      * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use\r
453      * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.\r
454      * @param {Boolean} visible Whether the element is visible\r
455      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object\r
456      * @return {Ext.Element} this\r
457      */\r
458      setVisible : function(visible, animate){\r
459         if(!animate || !A){\r
460             if(this.visibilityMode == El.DISPLAY){\r
461                 this.setDisplayed(visible);\r
462             }else{\r
463                 this.fixDisplay();\r
464                 this.dom.style.visibility = visible ? "visible" : "hidden";\r
465             }\r
466         }else{\r
467             // closure for composites\r
468             var dom = this.dom;\r
469             var visMode = this.visibilityMode;\r
470             if(visible){\r
471                 this.setOpacity(.01);\r
472                 this.setVisible(true);\r
473             }\r
474             this.anim({opacity: { to: (visible?1:0) }},\r
475                   this.preanim(arguments, 1),\r
476                   null, .35, 'easeIn', function(){\r
477                      if(!visible){\r
478                          if(visMode == El.DISPLAY){\r
479                              dom.style.display = "none";\r
480                          }else{\r
481                              dom.style.visibility = "hidden";\r
482                          }\r
483                          Ext.get(dom).setOpacity(1);\r
484                      }\r
485                  });\r
486         }\r
487         return this;\r
488     },\r
489 \r
490     /**\r
491      * Returns true if display is not "none"\r
492      * @return {Boolean}\r
493      */\r
494     isDisplayed : function() {\r
495         return this.getStyle("display") != "none";\r
496     },\r
497 \r
498     /**\r
499      * Toggles the element's visibility or display, depending on visibility mode.\r
500      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object\r
501      * @return {Ext.Element} this\r
502      */\r
503     toggle : function(animate){\r
504         this.setVisible(!this.isVisible(), this.preanim(arguments, 0));\r
505         return this;\r
506     },\r
507 \r
508     /**\r
509      * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.\r
510      * @param {Mixed} value Boolean value to display the element using its default display, or a string to set the display directly.\r
511      * @return {Ext.Element} this\r
512      */\r
513     setDisplayed : function(value) {\r
514         if(typeof value == "boolean"){\r
515            value = value ? this.originalDisplay : "none";\r
516         }\r
517         this.setStyle("display", value);\r
518         return this;\r
519     },\r
520 \r
521     /**\r
522      * Tries to focus the element. Any exceptions are caught and ignored.\r
523      * @return {Ext.Element} this\r
524      */\r
525     focus : function() {\r
526         try{\r
527             this.dom.focus();\r
528         }catch(e){}\r
529         return this;\r
530     },\r
531 \r
532     /**\r
533      * Tries to blur the element. Any exceptions are caught and ignored.\r
534      * @return {Ext.Element} this\r
535      */\r
536     blur : function() {\r
537         try{\r
538             this.dom.blur();\r
539         }catch(e){}\r
540         return this;\r
541     },\r
542 \r
543     /**\r
544      * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.\r
545      * @param {String/Array} className The CSS class to add, or an array of classes\r
546      * @return {Ext.Element} this\r
547      */\r
548     addClass : function(className){\r
549         if(Ext.isArray(className)){\r
550             for(var i = 0, len = className.length; i < len; i++) {\r
551                 this.addClass(className[i]);\r
552             }\r
553         }else{\r
554             if(className && !this.hasClass(className)){\r
555                 this.dom.className = this.dom.className + " " + className;\r
556             }\r
557         }\r
558         return this;\r
559     },\r
560 \r
561     /**\r
562      * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.\r
563      * @param {String/Array} className The CSS class to add, or an array of classes\r
564      * @return {Ext.Element} this\r
565      */\r
566     radioClass : function(className){\r
567         var siblings = this.dom.parentNode.childNodes;\r
568         for(var i = 0; i < siblings.length; i++) {\r
569                 var s = siblings[i];\r
570                 if(s.nodeType == 1){\r
571                     Ext.get(s).removeClass(className);\r
572                 }\r
573         }\r
574         this.addClass(className);\r
575         return this;\r
576     },\r
577 \r
578     /**\r
579      * Removes one or more CSS classes from the element.\r
580      * @param {String/Array} className The CSS class to remove, or an array of classes\r
581      * @return {Ext.Element} this\r
582      */\r
583     removeClass : function(className){\r
584         if(!className || !this.dom.className){\r
585             return this;\r
586         }\r
587         if(Ext.isArray(className)){\r
588             for(var i = 0, len = className.length; i < len; i++) {\r
589                 this.removeClass(className[i]);\r
590             }\r
591         }else{\r
592             if(this.hasClass(className)){\r
593                 var re = this.classReCache[className];\r
594                 if (!re) {\r
595                    re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");\r
596                    this.classReCache[className] = re;\r
597                 }\r
598                 this.dom.className =\r
599                     this.dom.className.replace(re, " ");\r
600             }\r
601         }\r
602         return this;\r
603     },\r
604 \r
605     // private\r
606     classReCache: {},\r
607 \r
608     /**\r
609      * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).\r
610      * @param {String} className The CSS class to toggle\r
611      * @return {Ext.Element} this\r
612      */\r
613     toggleClass : function(className){\r
614         if(this.hasClass(className)){\r
615             this.removeClass(className);\r
616         }else{\r
617             this.addClass(className);\r
618         }\r
619         return this;\r
620     },\r
621 \r
622     /**\r
623      * Checks if the specified CSS class exists on this element's DOM node.\r
624      * @param {String} className The CSS class to check for\r
625      * @return {Boolean} True if the class exists, else false\r
626      */\r
627     hasClass : function(className){\r
628         return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;\r
629     },\r
630 \r
631     /**\r
632      * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.\r
633      * @param {String} oldClassName The CSS class to replace\r
634      * @param {String} newClassName The replacement CSS class\r
635      * @return {Ext.Element} this\r
636      */\r
637     replaceClass : function(oldClassName, newClassName){\r
638         this.removeClass(oldClassName);\r
639         this.addClass(newClassName);\r
640         return this;\r
641     },\r
642 \r
643     /**\r
644      * Returns an object with properties matching the styles requested.\r
645      * For example, el.getStyles('color', 'font-size', 'width') might return\r
646      * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.\r
647      * @param {String} style1 A style name\r
648      * @param {String} style2 A style name\r
649      * @param {String} etc.\r
650      * @return {Object} The style object\r
651      */\r
652     getStyles : function(){\r
653         var a = arguments, len = a.length, r = {};\r
654         for(var i = 0; i < len; i++){\r
655             r[a[i]] = this.getStyle(a[i]);\r
656         }\r
657         return r;\r
658     },\r
659 \r
660     /**\r
661      * Normalizes currentStyle and computedStyle.\r
662      * @param {String} property The style property whose value is returned.\r
663      * @return {String} The current value of the style property for this element.\r
664      */\r
665     getStyle : function(){\r
666         return view && view.getComputedStyle ?\r
667             function(prop){\r
668                 var el = this.dom, v, cs, camel;\r
669                 if(prop == 'float'){\r
670                     prop = "cssFloat";\r
671                 }\r
672                 if(v = el.style[prop]){\r
673                     return v;\r
674                 }\r
675                 if(cs = view.getComputedStyle(el, "")){\r
676                     if(!(camel = propCache[prop])){\r
677                         camel = propCache[prop] = prop.replace(camelRe, camelFn);\r
678                     }\r
679                     return cs[camel];\r
680                 }\r
681                 return null;\r
682             } :\r
683             function(prop){\r
684                 var el = this.dom, v, cs, camel;\r
685                 if(prop == 'opacity'){\r
686                     if(typeof el.style.filter == 'string'){\r
687                         var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);\r
688                         if(m){\r
689                             var fv = parseFloat(m[1]);\r
690                             if(!isNaN(fv)){\r
691                                 return fv ? fv / 100 : 0;\r
692                             }\r
693                         }\r
694                     }\r
695                     return 1;\r
696                 }else if(prop == 'float'){\r
697                     prop = "styleFloat";\r
698                 }\r
699                 if(!(camel = propCache[prop])){\r
700                     camel = propCache[prop] = prop.replace(camelRe, camelFn);\r
701                 }\r
702                 if(v = el.style[camel]){\r
703                     return v;\r
704                 }\r
705                 if(cs = el.currentStyle){\r
706                     return cs[camel];\r
707                 }\r
708                 return null;\r
709             };\r
710     }(),\r
711 \r
712     /**\r
713      * Wrapper for setting style properties, also takes single object parameter of multiple styles.\r
714      * @param {String/Object} property The style property to be set, or an object of multiple styles.\r
715      * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.\r
716      * @return {Ext.Element} this\r
717      */\r
718     setStyle : function(prop, value){\r
719         if(typeof prop == "string"){\r
720             var camel;\r
721             if(!(camel = propCache[prop])){\r
722                 camel = propCache[prop] = prop.replace(camelRe, camelFn);\r
723             }\r
724             if(camel == 'opacity') {\r
725                 this.setOpacity(value);\r
726             }else{\r
727                 this.dom.style[camel] = value;\r
728             }\r
729         }else{\r
730             for(var style in prop){\r
731                 if(typeof prop[style] != "function"){\r
732                    this.setStyle(style, prop[style]);\r
733                 }\r
734             }\r
735         }\r
736         return this;\r
737     },\r
738 \r
739     /**\r
740      * More flexible version of {@link #setStyle} for setting style properties.\r
741      * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or\r
742      * a function which returns such a specification.\r
743      * @return {Ext.Element} this\r
744      */\r
745     applyStyles : function(style){\r
746         Ext.DomHelper.applyStyles(this.dom, style);\r
747         return this;\r
748     },\r
749 \r
750     /**\r
751       * 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).\r
752       * @return {Number} The X position of the element\r
753       */\r
754     getX : function(){\r
755         return D.getX(this.dom);\r
756     },\r
757 \r
758     /**\r
759       * 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).\r
760       * @return {Number} The Y position of the element\r
761       */\r
762     getY : function(){\r
763         return D.getY(this.dom);\r
764     },\r
765 \r
766     /**\r
767       * 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).\r
768       * @return {Array} The XY position of the element\r
769       */\r
770     getXY : function(){\r
771         return D.getXY(this.dom);\r
772     },\r
773 \r
774     /**\r
775       * 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.\r
776       * @param {Mixed} element The element to get the offsets from.\r
777       * @return {Array} The XY page offsets (e.g. [100, -200])\r
778       */\r
779     getOffsetsTo : function(el){\r
780         var o = this.getXY();\r
781         var e = Ext.fly(el, '_internal').getXY();\r
782         return [o[0]-e[0],o[1]-e[1]];\r
783     },\r
784 \r
785     /**\r
786      * 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).\r
787      * @param {Number} The X position of the element\r
788      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object\r
789      * @return {Ext.Element} this\r
790      */\r
791     setX : function(x, animate){\r
792         if(!animate || !A){\r
793             D.setX(this.dom, x);\r
794         }else{\r
795             this.setXY([x, this.getY()], this.preanim(arguments, 1));\r
796         }\r
797         return this;\r
798     },\r
799 \r
800     /**\r
801      * 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).\r
802      * @param {Number} The Y position of the element\r
803      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object\r
804      * @return {Ext.Element} this\r
805      */\r
806     setY : function(y, animate){\r
807         if(!animate || !A){\r
808             D.setY(this.dom, y);\r
809         }else{\r
810             this.setXY([this.getX(), y], this.preanim(arguments, 1));\r
811         }\r
812         return this;\r
813     },\r
814 \r
815     /**\r
816      * Sets the element's left position directly using CSS style (instead of {@link #setX}).\r
817      * @param {String} left The left CSS property value\r
818      * @return {Ext.Element} this\r
819      */\r
820     setLeft : function(left){\r
821         this.setStyle("left", this.addUnits(left));\r
822         return this;\r
823     },\r
824 \r
825     /**\r
826      * Sets the element's top position directly using CSS style (instead of {@link #setY}).\r
827      * @param {String} top The top CSS property value\r
828      * @return {Ext.Element} this\r
829      */\r
830     setTop : function(top){\r
831         this.setStyle("top", this.addUnits(top));\r
832         return this;\r
833     },\r
834 \r
835     /**\r
836      * Sets the element's CSS right style.\r
837      * @param {String} right The right CSS property value\r
838      * @return {Ext.Element} this\r
839      */\r
840     setRight : function(right){\r
841         this.setStyle("right", this.addUnits(right));\r
842         return this;\r
843     },\r
844 \r
845     /**\r
846      * Sets the element's CSS bottom style.\r
847      * @param {String} bottom The bottom CSS property value\r
848      * @return {Ext.Element} this\r
849      */\r
850     setBottom : function(bottom){\r
851         this.setStyle("bottom", this.addUnits(bottom));\r
852         return this;\r
853     },\r
854 \r
855     /**\r
856      * Sets the position of the element in page coordinates, regardless of how the element is positioned.\r
857      * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).\r
858      * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)\r
859      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object\r
860      * @return {Ext.Element} this\r
861      */\r
862     setXY : function(pos, animate){\r
863         if(!animate || !A){\r
864             D.setXY(this.dom, pos);\r
865         }else{\r
866             this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');\r
867         }\r
868         return this;\r
869     },\r
870 \r
871     /**\r
872      * Sets the position of the element in page coordinates, regardless of how the element is positioned.\r
873      * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).\r
874      * @param {Number} x X value for new position (coordinates are page-based)\r
875      * @param {Number} y Y value for new position (coordinates are page-based)\r
876      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object\r
877      * @return {Ext.Element} this\r
878      */\r
879     setLocation : function(x, y, animate){\r
880         this.setXY([x, y], this.preanim(arguments, 2));\r
881         return this;\r
882     },\r
883 \r
884     /**\r
885      * Sets the position of the element in page coordinates, regardless of how the element is positioned.\r
886      * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).\r
887      * @param {Number} x X value for new position (coordinates are page-based)\r
888      * @param {Number} y Y value for new position (coordinates are page-based)\r
889      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object\r
890      * @return {Ext.Element} this\r
891      */\r
892     moveTo : function(x, y, animate){\r
893         this.setXY([x, y], this.preanim(arguments, 2));\r
894         return this;\r
895     },\r
896 \r
897     /**\r
898      * Returns the region of the given element.\r
899      * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).\r
900      * @return {Region} A Ext.lib.Region containing "top, left, bottom, right" member data.\r
901      */\r
902     getRegion : function(){\r
903         return D.getRegion(this.dom);\r
904     },\r
905 \r
906     /**\r
907      * Returns the offset height of the element\r
908      * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding\r
909      * @return {Number} The element's height\r
910      */\r
911     getHeight : function(contentHeight){\r
912         var h = this.dom.offsetHeight || 0;\r
913         h = contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");\r
914         return h < 0 ? 0 : h;\r
915     },\r
916 \r
917     /**\r
918      * Returns the offset width of the element\r
919      * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding\r
920      * @return {Number} The element's width\r
921      */\r
922     getWidth : function(contentWidth){\r
923         var w = this.dom.offsetWidth || 0;\r
924         w = contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");\r
925         return w < 0 ? 0 : w;\r
926     },\r
927 \r
928     /**\r
929      * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders\r
930      * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements\r
931      * if a height has not been set using CSS.\r
932      * @return {Number}\r
933      */\r
934     getComputedHeight : function(){\r
935         var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);\r
936         if(!h){\r
937             h = parseInt(this.getStyle('height'), 10) || 0;\r
938             if(!this.isBorderBox()){\r
939                 h += this.getFrameWidth('tb');\r
940             }\r
941         }\r
942         return h;\r
943     },\r
944 \r
945     /**\r
946      * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders\r
947      * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements\r
948      * if a width has not been set using CSS.\r
949      * @return {Number}\r
950      */\r
951     getComputedWidth : function(){\r
952         var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);\r
953         if(!w){\r
954             w = parseInt(this.getStyle('width'), 10) || 0;\r
955             if(!this.isBorderBox()){\r
956                 w += this.getFrameWidth('lr');\r
957             }\r
958         }\r
959         return w;\r
960     },\r
961 \r
962     /**\r
963      * Returns the size of the element.\r
964      * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding\r
965      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}\r
966      */\r
967     getSize : function(contentSize){\r
968         return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};\r
969     },\r
970 \r
971     getStyleSize : function(){\r
972         var w, h, d = this.dom, s = d.style;\r
973         if(s.width && s.width != 'auto'){\r
974             w = parseInt(s.width, 10);\r
975             if(Ext.isBorderBox){\r
976                w -= this.getFrameWidth('lr');\r
977             }\r
978         }\r
979         if(s.height && s.height != 'auto'){\r
980             h = parseInt(s.height, 10);\r
981             if(Ext.isBorderBox){\r
982                h -= this.getFrameWidth('tb');\r
983             }\r
984         }\r
985         return {width: w || this.getWidth(true), height: h || this.getHeight(true)};\r
986 \r
987     },\r
988 \r
989     /**\r
990      * Returns the width and height of the viewport.\r
991      * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}\r
992      */\r
993     getViewSize : function(){\r
994         var d = this.dom, doc = document, aw = 0, ah = 0;\r
995         if(d == doc || d == doc.body){\r
996             return {width : D.getViewWidth(), height: D.getViewHeight()};\r
997         }else{\r
998             return {\r
999                 width : d.clientWidth,\r
1000                 height: d.clientHeight\r
1001             };\r
1002         }\r
1003     },\r
1004 \r
1005     /**\r
1006      * Returns the value of the "value" attribute\r
1007      * @param {Boolean} asNumber true to parse the value as a number\r
1008      * @return {String/Number}\r
1009      */\r
1010     getValue : function(asNumber){\r
1011         return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;\r
1012     },\r
1013 \r
1014     // private\r
1015     adjustWidth : function(width){\r
1016         if(typeof width == "number"){\r
1017             if(this.autoBoxAdjust && !this.isBorderBox()){\r
1018                width -= (this.getBorderWidth("lr") + this.getPadding("lr"));\r
1019             }\r
1020             if(width < 0){\r
1021                 width = 0;\r
1022             }\r
1023         }\r
1024         return width;\r
1025     },\r
1026 \r
1027     // private\r
1028     adjustHeight : function(height){\r
1029         if(typeof height == "number"){\r
1030            if(this.autoBoxAdjust && !this.isBorderBox()){\r
1031                height -= (this.getBorderWidth("tb") + this.getPadding("tb"));\r
1032            }\r
1033            if(height < 0){\r
1034                height = 0;\r
1035            }\r
1036         }\r
1037         return height;\r
1038     },\r
1039 \r
1040     /**\r
1041      * Set the width of the element\r
1042      * @param {Number} width The new width\r
1043      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
1044      * @return {Ext.Element} this\r
1045      */\r
1046     setWidth : function(width, animate){\r
1047         width = this.adjustWidth(width);\r
1048         if(!animate || !A){\r
1049             this.dom.style.width = this.addUnits(width);\r
1050         }else{\r
1051             this.anim({width: {to: width}}, this.preanim(arguments, 1));\r
1052         }\r
1053         return this;\r
1054     },\r
1055 \r
1056     /**\r
1057      * Set the height of the element\r
1058      * @param {Number} height The new height\r
1059      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
1060      * @return {Ext.Element} this\r
1061      */\r
1062      setHeight : function(height, animate){\r
1063         height = this.adjustHeight(height);\r
1064         if(!animate || !A){\r
1065             this.dom.style.height = this.addUnits(height);\r
1066         }else{\r
1067             this.anim({height: {to: height}}, this.preanim(arguments, 1));\r
1068         }\r
1069         return this;\r
1070     },\r
1071 \r
1072     /**\r
1073      * Set the size of the element. If animation is true, both width an height will be animated concurrently.\r
1074      * @param {Number} width The new width\r
1075      * @param {Number} height The new height\r
1076      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
1077      * @return {Ext.Element} this\r
1078      */\r
1079      setSize : function(width, height, animate){\r
1080         if(typeof width == "object"){ // in case of object from getSize()\r
1081             height = width.height; width = width.width;\r
1082         }\r
1083         width = this.adjustWidth(width); height = this.adjustHeight(height);\r
1084         if(!animate || !A){\r
1085             this.dom.style.width = this.addUnits(width);\r
1086             this.dom.style.height = this.addUnits(height);\r
1087         }else{\r
1088             this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));\r
1089         }\r
1090         return this;\r
1091     },\r
1092 \r
1093     /**\r
1094      * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.\r
1095      * @param {Number} x X value for new position (coordinates are page-based)\r
1096      * @param {Number} y Y value for new position (coordinates are page-based)\r
1097      * @param {Number} width The new width\r
1098      * @param {Number} height The new height\r
1099      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
1100      * @return {Ext.Element} this\r
1101      */\r
1102     setBounds : function(x, y, width, height, animate){\r
1103         if(!animate || !A){\r
1104             this.setSize(width, height);\r
1105             this.setLocation(x, y);\r
1106         }else{\r
1107             width = this.adjustWidth(width); height = this.adjustHeight(height);\r
1108             this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},\r
1109                           this.preanim(arguments, 4), 'motion');\r
1110         }\r
1111         return this;\r
1112     },\r
1113 \r
1114     /**\r
1115      * Sets the element's position and size the specified region. If animation is true then width, height, x and y will be animated concurrently.\r
1116      * @param {Ext.lib.Region} region The region to fill\r
1117      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
1118      * @return {Ext.Element} this\r
1119      */\r
1120     setRegion : function(region, animate){\r
1121         this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));\r
1122         return this;\r
1123     },\r
1124 \r
1125     /**\r
1126      * Appends an event handler to this element.  The shorthand version {@link #on} is equivalent.\r
1127      * @param {String} eventName The type of event to handle\r
1128      * @param {Function} fn The handler function the event invokes. This function is passed\r
1129      * the following parameters:<ul>\r
1130      * <li>evt : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>\r
1131      * <li>t : Element<div class="sub-desc">The {@link Ext.Element Element} which was the target of the event.\r
1132      * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>\r
1133      * <li>o : Object<div class="sub-desc">The options object from the addListener call.</div></li>\r
1134      * </ul>\r
1135      * @param {Object} scope (optional) The scope (The <tt>this</tt> reference) of the handler function. Defaults\r
1136      * to this Element.\r
1137      * @param {Object} options (optional) An object containing handler configuration properties.\r
1138      * This may contain any of the following properties:<ul>\r
1139      * <li>scope {Object} : The scope in which to execute the handler function. The handler function's "this" context.</li>\r
1140      * <li>delegate {String} : A simple selector to filter the target or look for a descendant of the target</li>\r
1141      * <li>stopEvent {Boolean} : True to stop the event. That is stop propagation, and prevent the default action.</li>\r
1142      * <li>preventDefault {Boolean} : True to prevent the default action</li>\r
1143      * <li>stopPropagation {Boolean} : True to prevent event propagation</li>\r
1144      * <li>normalized {Boolean} : False to pass a browser event to the handler function instead of an Ext.EventObject</li>\r
1145      * <li>delay {Number} : The number of milliseconds to delay the invocation of the handler after te event fires.</li>\r
1146      * <li>single {Boolean} : True to add a handler to handle just the next firing of the event, and then remove itself.</li>\r
1147      * <li>buffer {Number} : Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed\r
1148      * by the specified number of milliseconds. If the event fires again within that time, the original\r
1149      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>\r
1150      * </ul><br>\r
1151      * <p>\r
1152      * <b>Combining Options</b><br>\r
1153      * In the following examples, the shorthand form {@link #on} is used rather than the more verbose\r
1154      * addListener.  The two are equivalent.  Using the options argument, it is possible to combine different\r
1155      * types of listeners:<br>\r
1156      * <br>\r
1157      * A normalized, delayed, one-time listener that auto stops the event and adds a custom argument (forumId) to the\r
1158      * options object. The options object is available as the third parameter in the handler function.<div style="margin: 5px 20px 20px;">\r
1159      * Code:<pre><code>\r
1160 el.on('click', this.onClick, this, {\r
1161     single: true,\r
1162     delay: 100,\r
1163     stopEvent : true,\r
1164     forumId: 4\r
1165 });</code></pre></p>\r
1166      * <p>\r
1167      * <b>Attaching multiple handlers in 1 call</b><br>\r
1168       * The method also allows for a single argument to be passed which is a config object containing properties\r
1169      * which specify multiple handlers.</p>\r
1170      * <p>\r
1171      * Code:<pre><code></p>\r
1172 el.on({\r
1173     'click' : {\r
1174         fn: this.onClick,\r
1175         scope: this,\r
1176         delay: 100\r
1177     },\r
1178     'mouseover' : {\r
1179         fn: this.onMouseOver,\r
1180         scope: this\r
1181     },\r
1182     'mouseout' : {\r
1183         fn: this.onMouseOut,\r
1184         scope: this\r
1185     }\r
1186 });</code></pre>\r
1187      * <p>\r
1188      * Or a shorthand syntax:<br>\r
1189      * Code:<pre><code></p>\r
1190 el.on({\r
1191     'click' : this.onClick,\r
1192     'mouseover' : this.onMouseOver,\r
1193     'mouseout' : this.onMouseOut,\r
1194     scope: this\r
1195 });</code></pre>\r
1196      */\r
1197     addListener : function(eventName, fn, scope, options){\r
1198         Ext.EventManager.on(this.dom,  eventName, fn, scope || this, options);\r
1199     },\r
1200 \r
1201     /**\r
1202      * Removes an event handler from this element.  The shorthand version {@link #un} is equivalent.  Example:\r
1203      * <pre><code>\r
1204 el.removeListener('click', this.handlerFn);\r
1205 // or\r
1206 el.un('click', this.handlerFn);\r
1207 </code></pre>\r
1208      * @param {String} eventName the type of event to remove\r
1209      * @param {Function} fn the method the event invokes\r
1210      * @param {Object} scope (optional) The scope (The <tt>this</tt> reference) of the handler function. Defaults\r
1211      * to this Element.\r
1212      * @return {Ext.Element} this\r
1213      */\r
1214     removeListener : function(eventName, fn, scope){\r
1215         Ext.EventManager.removeListener(this.dom,  eventName, fn, scope || this);\r
1216         return this;\r
1217     },\r
1218 \r
1219     /**\r
1220      * Removes all previous added listeners from this element\r
1221      * @return {Ext.Element} this\r
1222      */\r
1223     removeAllListeners : function(){\r
1224         Ext.EventManager.removeAll(this.dom);\r
1225         return this;\r
1226     },\r
1227 \r
1228     /**\r
1229      * Create an event handler on this element such that when the event fires and is handled by this element,\r
1230      * it will be relayed to another object (i.e., fired again as if it originated from that object instead).\r
1231      * @param {String} eventName The type of event to relay\r
1232      * @param {Object} object Any object that extends {@link Ext.util.Observable} that will provide the context\r
1233      * for firing the relayed event\r
1234      */\r
1235     relayEvent : function(eventName, observable){\r
1236         this.on(eventName, function(e){\r
1237             observable.fireEvent(eventName, e);\r
1238         });\r
1239     },\r
1240 \r
1241     /**\r
1242      * Set the opacity of the element\r
1243      * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc\r
1244      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
1245      * @return {Ext.Element} this\r
1246      */\r
1247      setOpacity : function(opacity, animate){\r
1248         if(!animate || !A){\r
1249             var s = this.dom.style;\r
1250             if(Ext.isIE){\r
1251                 s.zoom = 1;\r
1252                 s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +\r
1253                            (opacity == 1 ? "" : " alpha(opacity=" + opacity * 100 + ")");\r
1254             }else{\r
1255                 s.opacity = opacity;\r
1256             }\r
1257         }else{\r
1258             this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');\r
1259         }\r
1260         return this;\r
1261     },\r
1262 \r
1263     /**\r
1264      * Gets the left X coordinate\r
1265      * @param {Boolean} local True to get the local css position instead of page coordinate\r
1266      * @return {Number}\r
1267      */\r
1268     getLeft : function(local){\r
1269         if(!local){\r
1270             return this.getX();\r
1271         }else{\r
1272             return parseInt(this.getStyle("left"), 10) || 0;\r
1273         }\r
1274     },\r
1275 \r
1276     /**\r
1277      * Gets the right X coordinate of the element (element X position + element width)\r
1278      * @param {Boolean} local True to get the local css position instead of page coordinate\r
1279      * @return {Number}\r
1280      */\r
1281     getRight : function(local){\r
1282         if(!local){\r
1283             return this.getX() + this.getWidth();\r
1284         }else{\r
1285             return (this.getLeft(true) + this.getWidth()) || 0;\r
1286         }\r
1287     },\r
1288 \r
1289     /**\r
1290      * Gets the top Y coordinate\r
1291      * @param {Boolean} local True to get the local css position instead of page coordinate\r
1292      * @return {Number}\r
1293      */\r
1294     getTop : function(local) {\r
1295         if(!local){\r
1296             return this.getY();\r
1297         }else{\r
1298             return parseInt(this.getStyle("top"), 10) || 0;\r
1299         }\r
1300     },\r
1301 \r
1302     /**\r
1303      * Gets the bottom Y coordinate of the element (element Y position + element height)\r
1304      * @param {Boolean} local True to get the local css position instead of page coordinate\r
1305      * @return {Number}\r
1306      */\r
1307     getBottom : function(local){\r
1308         if(!local){\r
1309             return this.getY() + this.getHeight();\r
1310         }else{\r
1311             return (this.getTop(true) + this.getHeight()) || 0;\r
1312         }\r
1313     },\r
1314 \r
1315     /**\r
1316     * Initializes positioning on this element. If a desired position is not passed, it will make the\r
1317     * the element positioned relative IF it is not already positioned.\r
1318     * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"\r
1319     * @param {Number} zIndex (optional) The zIndex to apply\r
1320     * @param {Number} x (optional) Set the page X position\r
1321     * @param {Number} y (optional) Set the page Y position\r
1322     */\r
1323     position : function(pos, zIndex, x, y){\r
1324         if(!pos){\r
1325            if(this.getStyle('position') == 'static'){\r
1326                this.setStyle('position', 'relative');\r
1327            }\r
1328         }else{\r
1329             this.setStyle("position", pos);\r
1330         }\r
1331         if(zIndex){\r
1332             this.setStyle("z-index", zIndex);\r
1333         }\r
1334         if(x !== undefined && y !== undefined){\r
1335             this.setXY([x, y]);\r
1336         }else if(x !== undefined){\r
1337             this.setX(x);\r
1338         }else if(y !== undefined){\r
1339             this.setY(y);\r
1340         }\r
1341     },\r
1342 \r
1343     /**\r
1344     * Clear positioning back to the default when the document was loaded\r
1345     * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.\r
1346     * @return {Ext.Element} this\r
1347      */\r
1348     clearPositioning : function(value){\r
1349         value = value ||'';\r
1350         this.setStyle({\r
1351             "left": value,\r
1352             "right": value,\r
1353             "top": value,\r
1354             "bottom": value,\r
1355             "z-index": "",\r
1356             "position" : "static"\r
1357         });\r
1358         return this;\r
1359     },\r
1360 \r
1361     /**\r
1362     * Gets an object with all CSS positioning properties. Useful along with setPostioning to get\r
1363     * snapshot before performing an update and then restoring the element.\r
1364     * @return {Object}\r
1365     */\r
1366     getPositioning : function(){\r
1367         var l = this.getStyle("left");\r
1368         var t = this.getStyle("top");\r
1369         return {\r
1370             "position" : this.getStyle("position"),\r
1371             "left" : l,\r
1372             "right" : l ? "" : this.getStyle("right"),\r
1373             "top" : t,\r
1374             "bottom" : t ? "" : this.getStyle("bottom"),\r
1375             "z-index" : this.getStyle("z-index")\r
1376         };\r
1377     },\r
1378 \r
1379     /**\r
1380      * Gets the width of the border(s) for the specified side(s)\r
1381      * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,\r
1382      * passing lr would get the border (l)eft width + the border (r)ight width.\r
1383      * @return {Number} The width of the sides passed added together\r
1384      */\r
1385     getBorderWidth : function(side){\r
1386         return this.addStyles(side, El.borders);\r
1387     },\r
1388 \r
1389     /**\r
1390      * Gets the width of the padding(s) for the specified side(s)\r
1391      * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,\r
1392      * passing lr would get the padding (l)eft + the padding (r)ight.\r
1393      * @return {Number} The padding of the sides passed added together\r
1394      */\r
1395     getPadding : function(side){\r
1396         return this.addStyles(side, El.paddings);\r
1397     },\r
1398 \r
1399     /**\r
1400     * Set positioning with an object returned by getPositioning().\r
1401     * @param {Object} posCfg\r
1402     * @return {Ext.Element} this\r
1403      */\r
1404     setPositioning : function(pc){\r
1405         this.applyStyles(pc);\r
1406         if(pc.right == "auto"){\r
1407             this.dom.style.right = "";\r
1408         }\r
1409         if(pc.bottom == "auto"){\r
1410             this.dom.style.bottom = "";\r
1411         }\r
1412         return this;\r
1413     },\r
1414 \r
1415     // private\r
1416     fixDisplay : function(){\r
1417         if(this.getStyle("display") == "none"){\r
1418             this.setStyle("visibility", "hidden");\r
1419             this.setStyle("display", this.originalDisplay); // first try reverting to default\r
1420             if(this.getStyle("display") == "none"){ // if that fails, default to block\r
1421                 this.setStyle("display", "block");\r
1422             }\r
1423         }\r
1424     },\r
1425 \r
1426     // private\r
1427         setOverflow : function(v){\r
1428         if(v=='auto' && Ext.isMac && Ext.isGecko2){ // work around stupid FF 2.0/Mac scroll bar bug\r
1429                 this.dom.style.overflow = 'hidden';\r
1430                 (function(){this.dom.style.overflow = 'auto';}).defer(1, this);\r
1431         }else{\r
1432                 this.dom.style.overflow = v;\r
1433         }\r
1434         },\r
1435 \r
1436     /**\r
1437      * Quick set left and top adding default units\r
1438      * @param {String} left The left CSS property value\r
1439      * @param {String} top The top CSS property value\r
1440      * @return {Ext.Element} this\r
1441      */\r
1442      setLeftTop : function(left, top){\r
1443         this.dom.style.left = this.addUnits(left);\r
1444         this.dom.style.top = this.addUnits(top);\r
1445         return this;\r
1446     },\r
1447 \r
1448     /**\r
1449      * Move this element relative to its current position.\r
1450      * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").\r
1451      * @param {Number} distance How far to move the element in pixels\r
1452      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
1453      * @return {Ext.Element} this\r
1454      */\r
1455      move : function(direction, distance, animate){\r
1456         var xy = this.getXY();\r
1457         direction = direction.toLowerCase();\r
1458         switch(direction){\r
1459             case "l":\r
1460             case "left":\r
1461                 this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));\r
1462                 break;\r
1463            case "r":\r
1464            case "right":\r
1465                 this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));\r
1466                 break;\r
1467            case "t":\r
1468            case "top":\r
1469            case "up":\r
1470                 this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));\r
1471                 break;\r
1472            case "b":\r
1473            case "bottom":\r
1474            case "down":\r
1475                 this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));\r
1476                 break;\r
1477         }\r
1478         return this;\r
1479     },\r
1480 \r
1481     /**\r
1482      *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove\r
1483      * @return {Ext.Element} this\r
1484      */\r
1485     clip : function(){\r
1486         if(!this.isClipped){\r
1487            this.isClipped = true;\r
1488            this.originalClip = {\r
1489                "o": this.getStyle("overflow"),\r
1490                "x": this.getStyle("overflow-x"),\r
1491                "y": this.getStyle("overflow-y")\r
1492            };\r
1493            this.setStyle("overflow", "hidden");\r
1494            this.setStyle("overflow-x", "hidden");\r
1495            this.setStyle("overflow-y", "hidden");\r
1496         }\r
1497         return this;\r
1498     },\r
1499 \r
1500     /**\r
1501      *  Return clipping (overflow) to original clipping before clip() was called\r
1502      * @return {Ext.Element} this\r
1503      */\r
1504     unclip : function(){\r
1505         if(this.isClipped){\r
1506             this.isClipped = false;\r
1507             var o = this.originalClip;\r
1508             if(o.o){this.setStyle("overflow", o.o);}\r
1509             if(o.x){this.setStyle("overflow-x", o.x);}\r
1510             if(o.y){this.setStyle("overflow-y", o.y);}\r
1511         }\r
1512         return this;\r
1513     },\r
1514 \r
1515 \r
1516     /**\r
1517      * Gets the x,y coordinates specified by the anchor position on the element.\r
1518      * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo}\r
1519      * for details on supported anchor positions.\r
1520      * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead\r
1521      * of page coordinates\r
1522      * @param {Object} size (optional) An object containing the size to use for calculating anchor position\r
1523      * {width: (target width), height: (target height)} (defaults to the element's current size)\r
1524      * @return {Array} [x, y] An array containing the element's x and y coordinates\r
1525      */\r
1526     getAnchorXY : function(anchor, local, s){\r
1527         //Passing a different size is useful for pre-calculating anchors,\r
1528         //especially for anchored animations that change the el size.\r
1529 \r
1530         var w, h, vp = false;\r
1531         if(!s){\r
1532             var d = this.dom;\r
1533             if(d == document.body || d == document){\r
1534                 vp = true;\r
1535                 w = D.getViewWidth(); h = D.getViewHeight();\r
1536             }else{\r
1537                 w = this.getWidth(); h = this.getHeight();\r
1538             }\r
1539         }else{\r
1540             w = s.width;  h = s.height;\r
1541         }\r
1542         var x = 0, y = 0, r = Math.round;\r
1543         switch((anchor || "tl").toLowerCase()){\r
1544             case "c":\r
1545                 x = r(w*.5);\r
1546                 y = r(h*.5);\r
1547             break;\r
1548             case "t":\r
1549                 x = r(w*.5);\r
1550                 y = 0;\r
1551             break;\r
1552             case "l":\r
1553                 x = 0;\r
1554                 y = r(h*.5);\r
1555             break;\r
1556             case "r":\r
1557                 x = w;\r
1558                 y = r(h*.5);\r
1559             break;\r
1560             case "b":\r
1561                 x = r(w*.5);\r
1562                 y = h;\r
1563             break;\r
1564             case "tl":\r
1565                 x = 0;\r
1566                 y = 0;\r
1567             break;\r
1568             case "bl":\r
1569                 x = 0;\r
1570                 y = h;\r
1571             break;\r
1572             case "br":\r
1573                 x = w;\r
1574                 y = h;\r
1575             break;\r
1576             case "tr":\r
1577                 x = w;\r
1578                 y = 0;\r
1579             break;\r
1580         }\r
1581         if(local === true){\r
1582             return [x, y];\r
1583         }\r
1584         if(vp){\r
1585             var sc = this.getScroll();\r
1586             return [x + sc.left, y + sc.top];\r
1587         }\r
1588         //Add the element's offset xy\r
1589         var o = this.getXY();\r
1590         return [x+o[0], y+o[1]];\r
1591     },\r
1592 \r
1593     /**\r
1594      * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the\r
1595      * supported position values.\r
1596      * @param {Mixed} element The element to align to.\r
1597      * @param {String} position The position to align to.\r
1598      * @param {Array} offsets (optional) Offset the positioning by [x, y]\r
1599      * @return {Array} [x, y]\r
1600      */\r
1601     getAlignToXY : function(el, p, o){\r
1602         el = Ext.get(el);\r
1603         if(!el || !el.dom){\r
1604             throw "Element.alignToXY with an element that doesn't exist";\r
1605         }\r
1606         var d = this.dom;\r
1607         var c = false; //constrain to viewport\r
1608         var p1 = "", p2 = "";\r
1609         o = o || [0,0];\r
1610 \r
1611         if(!p){\r
1612             p = "tl-bl";\r
1613         }else if(p == "?"){\r
1614             p = "tl-bl?";\r
1615         }else if(p.indexOf("-") == -1){\r
1616             p = "tl-" + p;\r
1617         }\r
1618         p = p.toLowerCase();\r
1619         var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);\r
1620         if(!m){\r
1621            throw "Element.alignTo with an invalid alignment " + p;\r
1622         }\r
1623         p1 = m[1]; p2 = m[2]; c = !!m[3];\r
1624 \r
1625         //Subtract the aligned el's internal xy from the target's offset xy\r
1626         //plus custom offset to get the aligned el's new offset xy\r
1627         var a1 = this.getAnchorXY(p1, true);\r
1628         var a2 = el.getAnchorXY(p2, false);\r
1629 \r
1630         var x = a2[0] - a1[0] + o[0];\r
1631         var y = a2[1] - a1[1] + o[1];\r
1632 \r
1633         if(c){\r
1634             //constrain the aligned el to viewport if necessary\r
1635             var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();\r
1636             // 5px of margin for ie\r
1637             var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;\r
1638 \r
1639             //If we are at a viewport boundary and the aligned el is anchored on a target border that is\r
1640             //perpendicular to the vp border, allow the aligned el to slide on that border,\r
1641             //otherwise swap the aligned el to the opposite border of the target.\r
1642             var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);\r
1643            var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);\r
1644            var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));\r
1645            var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));\r
1646 \r
1647            var doc = document;\r
1648            var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;\r
1649            var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;\r
1650 \r
1651            if((x+w) > dw + scrollX){\r
1652                 x = swapX ? r.left-w : dw+scrollX-w;\r
1653             }\r
1654            if(x < scrollX){\r
1655                x = swapX ? r.right : scrollX;\r
1656            }\r
1657            if((y+h) > dh + scrollY){\r
1658                 y = swapY ? r.top-h : dh+scrollY-h;\r
1659             }\r
1660            if (y < scrollY){\r
1661                y = swapY ? r.bottom : scrollY;\r
1662            }\r
1663         }\r
1664         return [x,y];\r
1665     },\r
1666 \r
1667     // private\r
1668     getConstrainToXY : function(){\r
1669         var os = {top:0, left:0, bottom:0, right: 0};\r
1670 \r
1671         return function(el, local, offsets, proposedXY){\r
1672             el = Ext.get(el);\r
1673             offsets = offsets ? Ext.applyIf(offsets, os) : os;\r
1674 \r
1675             var vw, vh, vx = 0, vy = 0;\r
1676             if(el.dom == document.body || el.dom == document){\r
1677                 vw = Ext.lib.Dom.getViewWidth();\r
1678                 vh = Ext.lib.Dom.getViewHeight();\r
1679             }else{\r
1680                 vw = el.dom.clientWidth;\r
1681                 vh = el.dom.clientHeight;\r
1682                 if(!local){\r
1683                     var vxy = el.getXY();\r
1684                     vx = vxy[0];\r
1685                     vy = vxy[1];\r
1686                 }\r
1687             }\r
1688 \r
1689             var s = el.getScroll();\r
1690 \r
1691             vx += offsets.left + s.left;\r
1692             vy += offsets.top + s.top;\r
1693 \r
1694             vw -= offsets.right;\r
1695             vh -= offsets.bottom;\r
1696 \r
1697             var vr = vx+vw;\r
1698             var vb = vy+vh;\r
1699 \r
1700             var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);\r
1701             var x = xy[0], y = xy[1];\r
1702             var w = this.dom.offsetWidth, h = this.dom.offsetHeight;\r
1703 \r
1704             // only move it if it needs it\r
1705             var moved = false;\r
1706 \r
1707             // first validate right/bottom\r
1708             if((x + w) > vr){\r
1709                 x = vr - w;\r
1710                 moved = true;\r
1711             }\r
1712             if((y + h) > vb){\r
1713                 y = vb - h;\r
1714                 moved = true;\r
1715             }\r
1716             // then make sure top/left isn't negative\r
1717             if(x < vx){\r
1718                 x = vx;\r
1719                 moved = true;\r
1720             }\r
1721             if(y < vy){\r
1722                 y = vy;\r
1723                 moved = true;\r
1724             }\r
1725             return moved ? [x, y] : false;\r
1726         };\r
1727     }(),\r
1728 \r
1729     // private\r
1730     adjustForConstraints : function(xy, parent, offsets){\r
1731         return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;\r
1732     },\r
1733 \r
1734     /**\r
1735      * Aligns this element with another element relative to the specified anchor points. If the other element is the\r
1736      * document it aligns it to the viewport.\r
1737      * The position parameter is optional, and can be specified in any one of the following formats:\r
1738      * <ul>\r
1739      *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>\r
1740      *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.\r
1741      *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been\r
1742      *       deprecated in favor of the newer two anchor syntax below</i>.</li>\r
1743      *   <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\r
1744      *       element's anchor point, and the second value is used as the target's anchor point.</li>\r
1745      * </ul>\r
1746      * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of\r
1747      * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to\r
1748      * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than\r
1749      * that specified in order to enforce the viewport constraints.\r
1750      * Following are all of the supported anchor positions:\r
1751 <pre>\r
1752 Value  Description\r
1753 -----  -----------------------------\r
1754 tl     The top left corner (default)\r
1755 t      The center of the top edge\r
1756 tr     The top right corner\r
1757 l      The center of the left edge\r
1758 c      In the center of the element\r
1759 r      The center of the right edge\r
1760 bl     The bottom left corner\r
1761 b      The center of the bottom edge\r
1762 br     The bottom right corner\r
1763 </pre>\r
1764 Example Usage:\r
1765 <pre><code>\r
1766 // align el to other-el using the default positioning ("tl-bl", non-constrained)\r
1767 el.alignTo("other-el");\r
1768 \r
1769 // align the top left corner of el with the top right corner of other-el (constrained to viewport)\r
1770 el.alignTo("other-el", "tr?");\r
1771 \r
1772 // align the bottom right corner of el with the center left edge of other-el\r
1773 el.alignTo("other-el", "br-l?");\r
1774 \r
1775 // align the center of el with the bottom left corner of other-el and\r
1776 // adjust the x position by -6 pixels (and the y position by 0)\r
1777 el.alignTo("other-el", "c-bl", [-6, 0]);\r
1778 </code></pre>\r
1779      * @param {Mixed} element The element to align to.\r
1780      * @param {String} position The position to align to.\r
1781      * @param {Array} offsets (optional) Offset the positioning by [x, y]\r
1782      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
1783      * @return {Ext.Element} this\r
1784      */\r
1785     alignTo : function(element, position, offsets, animate){\r
1786         var xy = this.getAlignToXY(element, position, offsets);\r
1787         this.setXY(xy, this.preanim(arguments, 3));\r
1788         return this;\r
1789     },\r
1790 \r
1791     /**\r
1792      * Anchors an element to another element and realigns it when the window is resized.\r
1793      * @param {Mixed} element The element to align to.\r
1794      * @param {String} position The position to align to.\r
1795      * @param {Array} offsets (optional) Offset the positioning by [x, y]\r
1796      * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object\r
1797      * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter\r
1798      * is a number, it is used as the buffer delay (defaults to 50ms).\r
1799      * @param {Function} callback The function to call after the animation finishes\r
1800      * @return {Ext.Element} this\r
1801      */\r
1802     anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){\r
1803         var action = function(){\r
1804             this.alignTo(el, alignment, offsets, animate);\r
1805             Ext.callback(callback, this);\r
1806         };\r
1807         Ext.EventManager.onWindowResize(action, this);\r
1808         var tm = typeof monitorScroll;\r
1809         if(tm != 'undefined'){\r
1810             Ext.EventManager.on(window, 'scroll', action, this,\r
1811                 {buffer: tm == 'number' ? monitorScroll : 50});\r
1812         }\r
1813         action.call(this); // align immediately\r
1814         return this;\r
1815     },\r
1816     /**\r
1817      * Clears any opacity settings from this element. Required in some cases for IE.\r
1818      * @return {Ext.Element} this\r
1819      */\r
1820     clearOpacity : function(){\r
1821         if (window.ActiveXObject) {\r
1822             if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){\r
1823                 this.dom.style.filter = "";\r
1824             }\r
1825         } else {\r
1826             this.dom.style.opacity = "";\r
1827             this.dom.style["-moz-opacity"] = "";\r
1828             this.dom.style["-khtml-opacity"] = "";\r
1829         }\r
1830         return this;\r
1831     },\r
1832 \r
1833     /**\r
1834      * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.\r
1835      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
1836      * @return {Ext.Element} this\r
1837      */\r
1838     hide : function(animate){\r
1839         this.setVisible(false, this.preanim(arguments, 0));\r
1840         return this;\r
1841     },\r
1842 \r
1843     /**\r
1844     * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.\r
1845     * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
1846      * @return {Ext.Element} this\r
1847      */\r
1848     show : function(animate){\r
1849         this.setVisible(true, this.preanim(arguments, 0));\r
1850         return this;\r
1851     },\r
1852 \r
1853     /**\r
1854      * @private Test if size has a unit, otherwise appends the default\r
1855      */\r
1856     addUnits : function(size){\r
1857         return Ext.Element.addUnits(size, this.defaultUnit);\r
1858     },\r
1859 \r
1860     /**\r
1861     * Update the innerHTML of this element, optionally searching for and processing scripts\r
1862     * @param {String} html The new HTML\r
1863     * @param {Boolean} loadScripts (optional) True to look for and process scripts (defaults to false)\r
1864     * @param {Function} callback (optional) For async script loading you can be notified when the update completes\r
1865     * @return {Ext.Element} this\r
1866      */\r
1867     update : function(html, loadScripts, callback){\r
1868         if(typeof html == "undefined"){\r
1869             html = "";\r
1870         }\r
1871         if(loadScripts !== true){\r
1872             this.dom.innerHTML = html;\r
1873             if(typeof callback == "function"){\r
1874                 callback();\r
1875             }\r
1876             return this;\r
1877         }\r
1878         var id = Ext.id();\r
1879         var dom = this.dom;\r
1880 \r
1881         html += '<span id="' + id + '"></span>';\r
1882 \r
1883         E.onAvailable(id, function(){\r
1884             var hd = document.getElementsByTagName("head")[0];\r
1885             var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;\r
1886             var srcRe = /\ssrc=([\'\"])(.*?)\1/i;\r
1887             var typeRe = /\stype=([\'\"])(.*?)\1/i;\r
1888 \r
1889             var match;\r
1890             while(match = re.exec(html)){\r
1891                 var attrs = match[1];\r
1892                 var srcMatch = attrs ? attrs.match(srcRe) : false;\r
1893                 if(srcMatch && srcMatch[2]){\r
1894                    var s = document.createElement("script");\r
1895                    s.src = srcMatch[2];\r
1896                    var typeMatch = attrs.match(typeRe);\r
1897                    if(typeMatch && typeMatch[2]){\r
1898                        s.type = typeMatch[2];\r
1899                    }\r
1900                    hd.appendChild(s);\r
1901                 }else if(match[2] && match[2].length > 0){\r
1902                     if(window.execScript) {\r
1903                        window.execScript(match[2]);\r
1904                     } else {\r
1905                        window.eval(match[2]);\r
1906                     }\r
1907                 }\r
1908             }\r
1909             var el = document.getElementById(id);\r
1910             if(el){Ext.removeNode(el);}\r
1911             if(typeof callback == "function"){\r
1912                 callback();\r
1913             }\r
1914         });\r
1915         dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");\r
1916         return this;\r
1917     },\r
1918 \r
1919     /**\r
1920      * Direct access to the Updater {@link Ext.Updater#update} method. The method takes the same object\r
1921      * parameter as {@link Ext.Updater#update}\r
1922      * @return {Ext.Element} this\r
1923      */\r
1924     load : function(){\r
1925         var um = this.getUpdater();\r
1926         um.update.apply(um, arguments);\r
1927         return this;\r
1928     },\r
1929 \r
1930     /**\r
1931     * Gets this element's Updater\r
1932     * @return {Ext.Updater} The Updater\r
1933     */\r
1934     getUpdater : function(){\r
1935         if(!this.updateManager){\r
1936             this.updateManager = new Ext.Updater(this);\r
1937         }\r
1938         return this.updateManager;\r
1939     },\r
1940 \r
1941     /**\r
1942      * Disables text selection for this element (normalized across browsers)\r
1943      * @return {Ext.Element} this\r
1944      */\r
1945     unselectable : function(){\r
1946         this.dom.unselectable = "on";\r
1947         this.swallowEvent("selectstart", true);\r
1948         this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");\r
1949         this.addClass("x-unselectable");\r
1950         return this;\r
1951     },\r
1952 \r
1953     /**\r
1954     * Calculates the x, y to center this element on the screen\r
1955     * @return {Array} The x, y values [x, y]\r
1956     */\r
1957     getCenterXY : function(){\r
1958         return this.getAlignToXY(document, 'c-c');\r
1959     },\r
1960 \r
1961     /**\r
1962     * Centers the Element in either the viewport, or another Element.\r
1963     * @param {Mixed} centerIn (optional) The element in which to center the element.\r
1964     */\r
1965     center : function(centerIn){\r
1966         this.alignTo(centerIn || document, 'c-c');\r
1967         return this;\r
1968     },\r
1969 \r
1970     /**\r
1971      * Tests various css rules/browsers to determine if this element uses a border box\r
1972      * @return {Boolean}\r
1973      */\r
1974     isBorderBox : function(){\r
1975         return noBoxAdjust[this.dom.tagName.toLowerCase()] || Ext.isBorderBox;\r
1976     },\r
1977 \r
1978     /**\r
1979      * Return a box {x, y, width, height} that can be used to set another elements\r
1980      * size/location to match this element.\r
1981      * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.\r
1982      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.\r
1983      * @return {Object} box An object in the format {x, y, width, height}\r
1984      */\r
1985     getBox : function(contentBox, local){\r
1986         var xy;\r
1987         if(!local){\r
1988             xy = this.getXY();\r
1989         }else{\r
1990             var left = parseInt(this.getStyle("left"), 10) || 0;\r
1991             var top = parseInt(this.getStyle("top"), 10) || 0;\r
1992             xy = [left, top];\r
1993         }\r
1994         var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;\r
1995         if(!contentBox){\r
1996             bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};\r
1997         }else{\r
1998             var l = this.getBorderWidth("l")+this.getPadding("l");\r
1999             var r = this.getBorderWidth("r")+this.getPadding("r");\r
2000             var t = this.getBorderWidth("t")+this.getPadding("t");\r
2001             var b = this.getBorderWidth("b")+this.getPadding("b");\r
2002             bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};\r
2003         }\r
2004         bx.right = bx.x + bx.width;\r
2005         bx.bottom = bx.y + bx.height;\r
2006         return bx;\r
2007     },\r
2008 \r
2009     /**\r
2010      * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()\r
2011      for more information about the sides.\r
2012      * @param {String} sides\r
2013      * @return {Number}\r
2014      */\r
2015     getFrameWidth : function(sides, onlyContentBox){\r
2016         return onlyContentBox && Ext.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));\r
2017     },\r
2018 \r
2019     /**\r
2020      * 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.\r
2021      * @param {Object} box The box to fill {x, y, width, height}\r
2022      * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically\r
2023      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
2024      * @return {Ext.Element} this\r
2025      */\r
2026     setBox : function(box, adjust, animate){\r
2027         var w = box.width, h = box.height;\r
2028         if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){\r
2029            w -= (this.getBorderWidth("lr") + this.getPadding("lr"));\r
2030            h -= (this.getBorderWidth("tb") + this.getPadding("tb"));\r
2031         }\r
2032         this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));\r
2033         return this;\r
2034     },\r
2035 \r
2036     /**\r
2037      * Forces the browser to repaint this element\r
2038      * @return {Ext.Element} this\r
2039      */\r
2040      repaint : function(){\r
2041         var dom = this.dom;\r
2042         this.addClass("x-repaint");\r
2043         setTimeout(function(){\r
2044             Ext.get(dom).removeClass("x-repaint");\r
2045         }, 1);\r
2046         return this;\r
2047     },\r
2048 \r
2049     /**\r
2050      * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,\r
2051      * then it returns the calculated width of the sides (see getPadding)\r
2052      * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides\r
2053      * @return {Object/Number}\r
2054      */\r
2055     getMargins : function(side){\r
2056         if(!side){\r
2057             return {\r
2058                 top: parseInt(this.getStyle("margin-top"), 10) || 0,\r
2059                 left: parseInt(this.getStyle("margin-left"), 10) || 0,\r
2060                 bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,\r
2061                 right: parseInt(this.getStyle("margin-right"), 10) || 0\r
2062             };\r
2063         }else{\r
2064             return this.addStyles(side, El.margins);\r
2065          }\r
2066     },\r
2067 \r
2068     // private\r
2069     addStyles : function(sides, styles){\r
2070         var val = 0, v, w;\r
2071         for(var i = 0, len = sides.length; i < len; i++){\r
2072             v = this.getStyle(styles[sides.charAt(i)]);\r
2073             if(v){\r
2074                  w = parseInt(v, 10);\r
2075                  if(w){ val += (w >= 0 ? w : -1 * w); }\r
2076             }\r
2077         }\r
2078         return val;\r
2079     },\r
2080 \r
2081     /**\r
2082      * Creates a proxy element of this element\r
2083      * @param {String/Object} config The class name of the proxy element or a DomHelper config object\r
2084      * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)\r
2085      * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)\r
2086      * @return {Ext.Element} The new proxy element\r
2087      */\r
2088     createProxy : function(config, renderTo, matchBox){\r
2089         config = typeof config == "object" ?\r
2090             config : {tag : "div", cls: config};\r
2091 \r
2092         var proxy;\r
2093         if(renderTo){\r
2094             proxy = Ext.DomHelper.append(renderTo, config, true);\r
2095         }else {\r
2096             proxy = Ext.DomHelper.insertBefore(this.dom, config, true);\r
2097         }\r
2098         if(matchBox){\r
2099            proxy.setBox(this.getBox());\r
2100         }\r
2101         return proxy;\r
2102     },\r
2103 \r
2104     /**\r
2105      * Puts a mask over this element to disable user interaction. Requires core.css.\r
2106      * This method can only be applied to elements which accept child nodes.\r
2107      * @param {String} msg (optional) A message to display in the mask\r
2108      * @param {String} msgCls (optional) A css class to apply to the msg element\r
2109      * @return {Element} The mask element\r
2110      */\r
2111     mask : function(msg, msgCls){\r
2112         if(this.getStyle("position") == "static"){\r
2113             this.addClass("x-masked-relative");\r
2114         }\r
2115         if(this._maskMsg){\r
2116             this._maskMsg.remove();\r
2117         }\r
2118         if(this._mask){\r
2119             this._mask.remove();\r
2120         }\r
2121 \r
2122         this._mask = Ext.DomHelper.append(this.dom, {cls:"ext-el-mask"}, true);\r
2123 \r
2124         this.addClass("x-masked");\r
2125         this._mask.setDisplayed(true);\r
2126         if(typeof msg == 'string'){\r
2127             this._maskMsg = Ext.DomHelper.append(this.dom, {cls:"ext-el-mask-msg", cn:{tag:'div'}}, true);\r
2128             var mm = this._maskMsg;\r
2129             mm.dom.className = msgCls ? "ext-el-mask-msg " + msgCls : "ext-el-mask-msg";\r
2130             mm.dom.firstChild.innerHTML = msg;\r
2131             mm.setDisplayed(true);\r
2132             mm.center(this);\r
2133         }\r
2134         if(Ext.isIE && !(Ext.isIE7 && Ext.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically\r
2135             this._mask.setSize(this.getWidth(), this.getHeight());\r
2136         }\r
2137         return this._mask;\r
2138     },\r
2139 \r
2140     /**\r
2141      * Removes a previously applied mask.\r
2142      */\r
2143     unmask : function(){\r
2144         if(this._mask){\r
2145             if(this._maskMsg){\r
2146                 this._maskMsg.remove();\r
2147                 delete this._maskMsg;\r
2148             }\r
2149             this._mask.remove();\r
2150             delete this._mask;\r
2151         }\r
2152         this.removeClass(["x-masked", "x-masked-relative"]);\r
2153     },\r
2154 \r
2155     /**\r
2156      * Returns true if this element is masked\r
2157      * @return {Boolean}\r
2158      */\r
2159     isMasked : function(){\r
2160         return this._mask && this._mask.isVisible();\r
2161     },\r
2162 \r
2163     /**\r
2164      * Creates an iframe shim for this element to keep selects and other windowed objects from\r
2165      * showing through.\r
2166      * @return {Ext.Element} The new shim element\r
2167      */\r
2168     createShim : function(){\r
2169         var el = document.createElement('iframe');\r
2170         el.frameBorder = '0';\r
2171         el.className = 'ext-shim';\r
2172         if(Ext.isIE && Ext.isSecure){\r
2173             el.src = Ext.SSL_SECURE_URL;\r
2174         }\r
2175         var shim = Ext.get(this.dom.parentNode.insertBefore(el, this.dom));\r
2176         shim.autoBoxAdjust = false;\r
2177         return shim;\r
2178     },\r
2179 \r
2180     /**\r
2181      * Removes this element from the DOM and deletes it from the cache\r
2182      */\r
2183     remove : function(){\r
2184         Ext.removeNode(this.dom);\r
2185         delete El.cache[this.dom.id];\r
2186     },\r
2187 \r
2188     /**\r
2189      * Sets up event handlers to call the passed functions when the mouse is over this element. Automatically\r
2190      * filters child element mouse events.\r
2191      * @param {Function} overFn\r
2192      * @param {Function} outFn\r
2193      * @param {Object} scope (optional)\r
2194      * @return {Ext.Element} this\r
2195      */\r
2196     hover : function(overFn, outFn, scope){\r
2197         var preOverFn = function(e){\r
2198             if(!e.within(this, true)){\r
2199                 overFn.apply(scope || this, arguments);\r
2200             }\r
2201         };\r
2202         var preOutFn = function(e){\r
2203             if(!e.within(this, true)){\r
2204                 outFn.apply(scope || this, arguments);\r
2205             }\r
2206         };\r
2207         this.on("mouseover", preOverFn, this.dom);\r
2208         this.on("mouseout", preOutFn, this.dom);\r
2209         return this;\r
2210     },\r
2211 \r
2212     /**\r
2213      * Sets up event handlers to add and remove a css class when the mouse is over this element\r
2214      * @param {String} className\r
2215      * @return {Ext.Element} this\r
2216      */\r
2217     addClassOnOver : function(className){\r
2218         this.hover(\r
2219             function(){\r
2220                 Ext.fly(this, '_internal').addClass(className);\r
2221             },\r
2222             function(){\r
2223                 Ext.fly(this, '_internal').removeClass(className);\r
2224             }\r
2225         );\r
2226         return this;\r
2227     },\r
2228 \r
2229     /**\r
2230      * Sets up event handlers to add and remove a css class when this element has the focus\r
2231      * @param {String} className\r
2232      * @return {Ext.Element} this\r
2233      */\r
2234     addClassOnFocus : function(className){\r
2235         this.on("focus", function(){\r
2236             Ext.fly(this, '_internal').addClass(className);\r
2237         }, this.dom);\r
2238         this.on("blur", function(){\r
2239             Ext.fly(this, '_internal').removeClass(className);\r
2240         }, this.dom);\r
2241         return this;\r
2242     },\r
2243     /**\r
2244      * 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)\r
2245      * @param {String} className\r
2246      * @return {Ext.Element} this\r
2247      */\r
2248     addClassOnClick : function(className){\r
2249         var dom = this.dom;\r
2250         this.on("mousedown", function(){\r
2251             Ext.fly(dom, '_internal').addClass(className);\r
2252             var d = Ext.getDoc();\r
2253             var fn = function(){\r
2254                 Ext.fly(dom, '_internal').removeClass(className);\r
2255                 d.removeListener("mouseup", fn);\r
2256             };\r
2257             d.on("mouseup", fn);\r
2258         });\r
2259         return this;\r
2260     },\r
2261 \r
2262     /**\r
2263      * Stops the specified event(s) from bubbling and optionally prevents the default action\r
2264      * @param {String/Array} eventName an event / array of events to stop from bubbling\r
2265      * @param {Boolean} preventDefault (optional) true to prevent the default action too\r
2266      * @return {Ext.Element} this\r
2267      */\r
2268     swallowEvent : function(eventName, preventDefault){\r
2269         var fn = function(e){\r
2270             e.stopPropagation();\r
2271             if(preventDefault){\r
2272                 e.preventDefault();\r
2273             }\r
2274         };\r
2275         if(Ext.isArray(eventName)){\r
2276             for(var i = 0, len = eventName.length; i < len; i++){\r
2277                  this.on(eventName[i], fn);\r
2278             }\r
2279             return this;\r
2280         }\r
2281         this.on(eventName, fn);\r
2282         return this;\r
2283     },\r
2284 \r
2285     /**\r
2286      * Gets the parent node for this element, optionally chaining up trying to match a selector\r
2287      * @param {String} selector (optional) Find a parent node that matches the passed simple selector\r
2288      * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element\r
2289      * @return {Ext.Element/HTMLElement} The parent node or null\r
2290          */\r
2291     parent : function(selector, returnDom){\r
2292         return this.matchNode('parentNode', 'parentNode', selector, returnDom);\r
2293     },\r
2294 \r
2295      /**\r
2296      * Gets the next sibling, skipping text nodes\r
2297      * @param {String} selector (optional) Find the next sibling that matches the passed simple selector\r
2298      * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element\r
2299      * @return {Ext.Element/HTMLElement} The next sibling or null\r
2300          */\r
2301     next : function(selector, returnDom){\r
2302         return this.matchNode('nextSibling', 'nextSibling', selector, returnDom);\r
2303     },\r
2304 \r
2305     /**\r
2306      * Gets the previous sibling, skipping text nodes\r
2307      * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector\r
2308      * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element\r
2309      * @return {Ext.Element/HTMLElement} The previous sibling or null\r
2310          */\r
2311     prev : function(selector, returnDom){\r
2312         return this.matchNode('previousSibling', 'previousSibling', selector, returnDom);\r
2313     },\r
2314 \r
2315 \r
2316     /**\r
2317      * Gets the first child, skipping text nodes\r
2318      * @param {String} selector (optional) Find the next sibling that matches the passed simple selector\r
2319      * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element\r
2320      * @return {Ext.Element/HTMLElement} The first child or null\r
2321          */\r
2322     first : function(selector, returnDom){\r
2323         return this.matchNode('nextSibling', 'firstChild', selector, returnDom);\r
2324     },\r
2325 \r
2326     /**\r
2327      * Gets the last child, skipping text nodes\r
2328      * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector\r
2329      * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element\r
2330      * @return {Ext.Element/HTMLElement} The last child or null\r
2331          */\r
2332     last : function(selector, returnDom){\r
2333         return this.matchNode('previousSibling', 'lastChild', selector, returnDom);\r
2334     },\r
2335 \r
2336     matchNode : function(dir, start, selector, returnDom){\r
2337         var n = this.dom[start];\r
2338         while(n){\r
2339             if(n.nodeType == 1 && (!selector || Ext.DomQuery.is(n, selector))){\r
2340                 return !returnDom ? Ext.get(n) : n;\r
2341             }\r
2342             n = n[dir];\r
2343         }\r
2344         return null;\r
2345     },\r
2346 \r
2347     /**\r
2348      * Appends the passed element(s) to this element\r
2349      * @param {String/HTMLElement/Array/Element/CompositeElement} el\r
2350      * @return {Ext.Element} this\r
2351      */\r
2352     appendChild: function(el){\r
2353         el = Ext.get(el);\r
2354         el.appendTo(this);\r
2355         return this;\r
2356     },\r
2357 \r
2358     /**\r
2359      * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.\r
2360      * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be\r
2361      * automatically generated with the specified attributes.\r
2362      * @param {HTMLElement} insertBefore (optional) a child element of this element\r
2363      * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element\r
2364      * @return {Ext.Element} The new child element\r
2365      */\r
2366     createChild: function(config, insertBefore, returnDom){\r
2367         config = config || {tag:'div'};\r
2368         if(insertBefore){\r
2369             return Ext.DomHelper.insertBefore(insertBefore, config, returnDom !== true);\r
2370         }\r
2371         return Ext.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);\r
2372     },\r
2373 \r
2374     /**\r
2375      * Appends this element to the passed element\r
2376      * @param {Mixed} el The new parent element\r
2377      * @return {Ext.Element} this\r
2378      */\r
2379     appendTo: function(el){\r
2380         el = Ext.getDom(el);\r
2381         el.appendChild(this.dom);\r
2382         return this;\r
2383     },\r
2384 \r
2385     /**\r
2386      * Inserts this element before the passed element in the DOM\r
2387      * @param {Mixed} el The element before which this element will be inserted\r
2388      * @return {Ext.Element} this\r
2389      */\r
2390     insertBefore: function(el){\r
2391         el = Ext.getDom(el);\r
2392         el.parentNode.insertBefore(this.dom, el);\r
2393         return this;\r
2394     },\r
2395 \r
2396     /**\r
2397      * Inserts this element after the passed element in the DOM\r
2398      * @param {Mixed} el The element to insert after\r
2399      * @return {Ext.Element} this\r
2400      */\r
2401     insertAfter: function(el){\r
2402         el = Ext.getDom(el);\r
2403         el.parentNode.insertBefore(this.dom, el.nextSibling);\r
2404         return this;\r
2405     },\r
2406 \r
2407     /**\r
2408      * Inserts (or creates) an element (or DomHelper config) as the first child of this element\r
2409      * @param {Mixed/Object} el The id or element to insert or a DomHelper config to create and insert\r
2410      * @return {Ext.Element} The new child\r
2411      */\r
2412     insertFirst: function(el, returnDom){\r
2413         el = el || {};\r
2414         if(typeof el == 'object' && !el.nodeType && !el.dom){ // dh config\r
2415             return this.createChild(el, this.dom.firstChild, returnDom);\r
2416         }else{\r
2417             el = Ext.getDom(el);\r
2418             this.dom.insertBefore(el, this.dom.firstChild);\r
2419             return !returnDom ? Ext.get(el) : el;\r
2420         }\r
2421     },\r
2422 \r
2423     /**\r
2424      * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element\r
2425      * @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.\r
2426      * @param {String} where (optional) 'before' or 'after' defaults to before\r
2427      * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.Element\r
2428      * @return {Ext.Element} the inserted Element\r
2429      */\r
2430     insertSibling: function(el, where, returnDom){\r
2431         var rt;\r
2432         if(Ext.isArray(el)){\r
2433             for(var i = 0, len = el.length; i < len; i++){\r
2434                 rt = this.insertSibling(el[i], where, returnDom);\r
2435             }\r
2436             return rt;\r
2437         }\r
2438         where = where ? where.toLowerCase() : 'before';\r
2439         el = el || {};\r
2440         var refNode = where == 'before' ? this.dom : this.dom.nextSibling;\r
2441 \r
2442         if(typeof el == 'object' && !el.nodeType && !el.dom){ // dh config\r
2443             if(where == 'after' && !this.dom.nextSibling){\r
2444                 rt = Ext.DomHelper.append(this.dom.parentNode, el, !returnDom);\r
2445             }else{\r
2446                 rt = Ext.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);\r
2447             }\r
2448 \r
2449         }else{\r
2450             rt = this.dom.parentNode.insertBefore(Ext.getDom(el), refNode);\r
2451             if(!returnDom){\r
2452                 rt = Ext.get(rt);\r
2453             }\r
2454         }\r
2455         return rt;\r
2456     },\r
2457 \r
2458     /**\r
2459      * Creates and wraps this element with another element\r
2460      * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div\r
2461      * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.Element\r
2462      * @return {HTMLElement/Element} The newly created wrapper element\r
2463      */\r
2464     wrap: function(config, returnDom){\r
2465         if(!config){\r
2466             config = {tag: "div"};\r
2467         }\r
2468         var newEl = Ext.DomHelper.insertBefore(this.dom, config, !returnDom);\r
2469         newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);\r
2470         return newEl;\r
2471     },\r
2472 \r
2473     /**\r
2474      * Replaces the passed element with this element\r
2475      * @param {Mixed} el The element to replace\r
2476      * @return {Ext.Element} this\r
2477      */\r
2478     replace: function(el){\r
2479         el = Ext.get(el);\r
2480         this.insertBefore(el);\r
2481         el.remove();\r
2482         return this;\r
2483     },\r
2484 \r
2485     /**\r
2486      * Replaces this element with the passed element\r
2487      * @param {Mixed/Object} el The new element or a DomHelper config of an element to create\r
2488      * @return {Ext.Element} this\r
2489      */\r
2490     replaceWith: function(el){\r
2491         if(typeof el == 'object' && !el.nodeType && !el.dom){ // dh config\r
2492             el = this.insertSibling(el, 'before');\r
2493         }else{\r
2494             el = Ext.getDom(el);\r
2495             this.dom.parentNode.insertBefore(el, this.dom);\r
2496         }\r
2497         El.uncache(this.id);\r
2498         Ext.removeNode(this.dom);\r
2499         this.dom = el;\r
2500         this.id = Ext.id(el);\r
2501         El.cache[this.id] = this;\r
2502         return this;\r
2503     },\r
2504 \r
2505     /**\r
2506      * Inserts an html fragment into this element\r
2507      * @param {String} where Where to insert the html in relation to this element - beforeBegin, afterBegin, beforeEnd, afterEnd.\r
2508      * @param {String} html The HTML fragment\r
2509      * @param {Boolean} returnEl (optional) True to return an Ext.Element (defaults to false)\r
2510      * @return {HTMLElement/Ext.Element} The inserted node (or nearest related if more than 1 inserted)\r
2511      */\r
2512     insertHtml : function(where, html, returnEl){\r
2513         var el = Ext.DomHelper.insertHtml(where, this.dom, html);\r
2514         return returnEl ? Ext.get(el) : el;\r
2515     },\r
2516 \r
2517     /**\r
2518      * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)\r
2519      * @param {Object} o The object with the attributes\r
2520      * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.\r
2521      * @return {Ext.Element} this\r
2522      */\r
2523     set : function(o, useSet){\r
2524         var el = this.dom;\r
2525         useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;\r
2526         for(var attr in o){\r
2527             if(attr == "style" || typeof o[attr] == "function") continue;\r
2528             if(attr=="cls"){\r
2529                 el.className = o["cls"];\r
2530             }else if(o.hasOwnProperty(attr)){\r
2531                 if(useSet) el.setAttribute(attr, o[attr]);\r
2532                 else el[attr] = o[attr];\r
2533             }\r
2534         }\r
2535         if(o.style){\r
2536             Ext.DomHelper.applyStyles(el, o.style);\r
2537         }\r
2538         return this;\r
2539     },\r
2540 \r
2541     /**\r
2542      * Convenience method for constructing a KeyMap\r
2543      * @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:\r
2544      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}\r
2545      * @param {Function} fn The function to call\r
2546      * @param {Object} scope (optional) The scope of the function\r
2547      * @return {Ext.KeyMap} The KeyMap created\r
2548      */\r
2549     addKeyListener : function(key, fn, scope){\r
2550         var config;\r
2551         if(typeof key != "object" || Ext.isArray(key)){\r
2552             config = {\r
2553                 key: key,\r
2554                 fn: fn,\r
2555                 scope: scope\r
2556             };\r
2557         }else{\r
2558             config = {\r
2559                 key : key.key,\r
2560                 shift : key.shift,\r
2561                 ctrl : key.ctrl,\r
2562                 alt : key.alt,\r
2563                 fn: fn,\r
2564                 scope: scope\r
2565             };\r
2566         }\r
2567         return new Ext.KeyMap(this, config);\r
2568     },\r
2569 \r
2570     /**\r
2571      * Creates a KeyMap for this element\r
2572      * @param {Object} config The KeyMap config. See {@link Ext.KeyMap} for more details\r
2573      * @return {Ext.KeyMap} The KeyMap created\r
2574      */\r
2575     addKeyMap : function(config){\r
2576         return new Ext.KeyMap(this, config);\r
2577     },\r
2578 \r
2579     /**\r
2580      * Returns true if this element is scrollable.\r
2581      * @return {Boolean}\r
2582      */\r
2583      isScrollable : function(){\r
2584         var dom = this.dom;\r
2585         return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;\r
2586     },\r
2587 \r
2588     /**\r
2589      * 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().\r
2590      * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.\r
2591      * @param {Number} value The new scroll value\r
2592      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
2593      * @return {Element} this\r
2594      */\r
2595     scrollTo : function(side, value, animate){\r
2596         var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";\r
2597         if(!animate || !A){\r
2598             this.dom[prop] = value;\r
2599         }else{\r
2600             var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];\r
2601             this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');\r
2602         }\r
2603         return this;\r
2604     },\r
2605 \r
2606     /**\r
2607      * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is\r
2608      * within this element's scrollable range.\r
2609      * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").\r
2610      * @param {Number} distance How far to scroll the element in pixels\r
2611      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
2612      * @return {Boolean} Returns true if a scroll was triggered or false if the element\r
2613      * was scrolled as far as it could go.\r
2614      */\r
2615      scroll : function(direction, distance, animate){\r
2616          if(!this.isScrollable()){\r
2617              return;\r
2618          }\r
2619          var el = this.dom;\r
2620          var l = el.scrollLeft, t = el.scrollTop;\r
2621          var w = el.scrollWidth, h = el.scrollHeight;\r
2622          var cw = el.clientWidth, ch = el.clientHeight;\r
2623          direction = direction.toLowerCase();\r
2624          var scrolled = false;\r
2625          var a = this.preanim(arguments, 2);\r
2626          switch(direction){\r
2627              case "l":\r
2628              case "left":\r
2629                  if(w - l > cw){\r
2630                      var v = Math.min(l + distance, w-cw);\r
2631                      this.scrollTo("left", v, a);\r
2632                      scrolled = true;\r
2633                  }\r
2634                  break;\r
2635             case "r":\r
2636             case "right":\r
2637                  if(l > 0){\r
2638                      var v = Math.max(l - distance, 0);\r
2639                      this.scrollTo("left", v, a);\r
2640                      scrolled = true;\r
2641                  }\r
2642                  break;\r
2643             case "t":\r
2644             case "top":\r
2645             case "up":\r
2646                  if(t > 0){\r
2647                      var v = Math.max(t - distance, 0);\r
2648                      this.scrollTo("top", v, a);\r
2649                      scrolled = true;\r
2650                  }\r
2651                  break;\r
2652             case "b":\r
2653             case "bottom":\r
2654             case "down":\r
2655                  if(h - t > ch){\r
2656                      var v = Math.min(t + distance, h-ch);\r
2657                      this.scrollTo("top", v, a);\r
2658                      scrolled = true;\r
2659                  }\r
2660                  break;\r
2661          }\r
2662          return scrolled;\r
2663     },\r
2664 \r
2665     /**\r
2666      * Translates the passed page coordinates into left/top css values for this element\r
2667      * @param {Number/Array} x The page x or an array containing [x, y]\r
2668      * @param {Number} y (optional) The page y, required if x is not an array\r
2669      * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}\r
2670      */\r
2671     translatePoints : function(x, y){\r
2672         if(typeof x == 'object' || Ext.isArray(x)){\r
2673             y = x[1]; x = x[0];\r
2674         }\r
2675         var p = this.getStyle('position');\r
2676         var o = this.getXY();\r
2677 \r
2678         var l = parseInt(this.getStyle('left'), 10);\r
2679         var t = parseInt(this.getStyle('top'), 10);\r
2680 \r
2681         if(isNaN(l)){\r
2682             l = (p == "relative") ? 0 : this.dom.offsetLeft;\r
2683         }\r
2684         if(isNaN(t)){\r
2685             t = (p == "relative") ? 0 : this.dom.offsetTop;\r
2686         }\r
2687 \r
2688         return {left: (x - o[0] + l), top: (y - o[1] + t)};\r
2689     },\r
2690 \r
2691     /**\r
2692      * Returns the current scroll position of the element.\r
2693      * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}\r
2694      */\r
2695     getScroll : function(){\r
2696         var d = this.dom, doc = document;\r
2697         if(d == doc || d == doc.body){\r
2698             var l, t;\r
2699             if(Ext.isIE && Ext.isStrict){\r
2700                 l = doc.documentElement.scrollLeft || (doc.body.scrollLeft || 0);\r
2701                 t = doc.documentElement.scrollTop || (doc.body.scrollTop || 0);\r
2702             }else{\r
2703                 l = window.pageXOffset || (doc.body.scrollLeft || 0);\r
2704                 t = window.pageYOffset || (doc.body.scrollTop || 0);\r
2705             }\r
2706             return {left: l, top: t};\r
2707         }else{\r
2708             return {left: d.scrollLeft, top: d.scrollTop};\r
2709         }\r
2710     },\r
2711 \r
2712     /**\r
2713      * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values\r
2714      * are convert to standard 6 digit hex color.\r
2715      * @param {String} attr The css attribute\r
2716      * @param {String} defaultValue The default value to use when a valid color isn't found\r
2717      * @param {String} prefix (optional) defaults to #. Use an empty string when working with\r
2718      * color anims.\r
2719      */\r
2720     getColor : function(attr, defaultValue, prefix){\r
2721         var v = this.getStyle(attr);\r
2722         if(!v || v == "transparent" || v == "inherit") {\r
2723             return defaultValue;\r
2724         }\r
2725         var color = typeof prefix == "undefined" ? "#" : prefix;\r
2726         if(v.substr(0, 4) == "rgb("){\r
2727             var rvs = v.slice(4, v.length -1).split(",");\r
2728             for(var i = 0; i < 3; i++){\r
2729                 var h = parseInt(rvs[i]);\r
2730                 var s = h.toString(16);\r
2731                 if(h < 16){\r
2732                     s = "0" + s;\r
2733                 }\r
2734                 color += s;\r
2735             }\r
2736         } else {\r
2737             if(v.substr(0, 1) == "#"){\r
2738                 if(v.length == 4) {\r
2739                     for(var i = 1; i < 4; i++){\r
2740                         var c = v.charAt(i);\r
2741                         color +=  c + c;\r
2742                     }\r
2743                 }else if(v.length == 7){\r
2744                     color += v.substr(1);\r
2745                 }\r
2746             }\r
2747         }\r
2748         return(color.length > 5 ? color.toLowerCase() : defaultValue);\r
2749     },\r
2750 \r
2751     /**\r
2752      * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a\r
2753      * gradient background, rounded corners and a 4-way shadow.  Example usage:\r
2754      * <pre><code>\r
2755 // Basic box wrap\r
2756 Ext.get("foo").boxWrap();\r
2757 \r
2758 // You can also add a custom class and use CSS inheritance rules to customize the box look.\r
2759 // 'x-box-blue' is a built-in alternative -- look at the related CSS definitions as an example\r
2760 // for how to create a custom box wrap style.\r
2761 Ext.get("foo").boxWrap().addClass("x-box-blue");\r
2762 </pre></code>\r
2763      * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').\r
2764      * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,\r
2765      * so if you supply an alternate base class, make sure you also supply all of the necessary rules.\r
2766      * @return {Ext.Element} this\r
2767      */\r
2768     boxWrap : function(cls){\r
2769         cls = cls || 'x-box';\r
2770         var el = Ext.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));\r
2771         el.child('.'+cls+'-mc').dom.appendChild(this.dom);\r
2772         return el;\r
2773     },\r
2774 \r
2775     /**\r
2776      * Returns the value of a namespaced attribute from the element's underlying DOM node.\r
2777      * @param {String} namespace The namespace in which to look for the attribute\r
2778      * @param {String} name The attribute name\r
2779      * @return {String} The attribute value\r
2780      */\r
2781     getAttributeNS : Ext.isIE ? function(ns, name){\r
2782         var d = this.dom;\r
2783         var type = typeof d[ns+":"+name];\r
2784         if(type != 'undefined' && type != 'unknown'){\r
2785             return d[ns+":"+name];\r
2786         }\r
2787         return d[name];\r
2788     } : function(ns, name){\r
2789         var d = this.dom;\r
2790         return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];\r
2791     },\r
2792 \r
2793     /**\r
2794      * Returns the width in pixels of the passed text, or the width of the text in this Element.\r
2795      * @param {String} text The text to measure. Defaults to the innerHTML of the element.\r
2796      * @param {Number} min (Optional) The minumum value to return.\r
2797      * @param {Number} max (Optional) The maximum value to return.\r
2798      * @return {Number} The text width in pixels.\r
2799      */\r
2800     getTextWidth : function(text, min, max){\r
2801         return (Ext.util.TextMetrics.measure(this.dom, Ext.value(text, this.dom.innerHTML, true)).width).constrain(min || 0, max || 1000000);\r
2802     }\r
2803 };\r
2804 \r
2805 var ep = El.prototype;\r
2806 \r
2807 /**\r
2808  * Appends an event handler (shorthand for {@link #addListener}).\r
2809  * @param {String} eventName The type of event to handle\r
2810  * @param {Function} fn The handler function the event invokes\r
2811  * @param {Object} scope (optional) The scope (this element) of the handler function\r
2812  * @param {Object} options (optional) An object containing standard {@link #addListener} options\r
2813  * @member Ext.Element\r
2814  * @method on\r
2815  */\r
2816 ep.on = ep.addListener;\r
2817     // backwards compat\r
2818 ep.mon = ep.addListener;\r
2819 \r
2820 ep.getUpdateManager = ep.getUpdater;\r
2821 \r
2822 /**\r
2823  * Removes an event handler from this element (shorthand for {@link #removeListener}).\r
2824  * @param {String} eventName the type of event to remove\r
2825  * @param {Function} fn the method the event invokes\r
2826  * @param {Object} scope (optional) The scope (The <tt>this</tt> reference) of the handler function. Defaults\r
2827  * to this Element.\r
2828  * @return {Ext.Element} this\r
2829  * @member Ext.Element\r
2830  * @method un\r
2831  */\r
2832 ep.un = ep.removeListener;\r
2833 \r
2834 /**\r
2835  * true to automatically adjust width and height settings for box-model issues (default to true)\r
2836  */\r
2837 ep.autoBoxAdjust = true;\r
2838 \r
2839 // private\r
2840 El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;\r
2841 \r
2842 // private\r
2843 El.addUnits = function(v, defaultUnit){\r
2844     if(v === "" || v == "auto"){\r
2845         return v;\r
2846     }\r
2847     if(v === undefined){\r
2848         return '';\r
2849     }\r
2850     if(typeof v == "number" || !El.unitPattern.test(v)){\r
2851         return v + (defaultUnit || 'px');\r
2852     }\r
2853     return v;\r
2854 };\r
2855 \r
2856 // special markup used throughout Ext when box wrapping elements\r
2857 El.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>';\r
2858 /**\r
2859  * Visibility mode constant - Use visibility to hide element\r
2860  * @static\r
2861  * @type Number\r
2862  */\r
2863 El.VISIBILITY = 1;\r
2864 /**\r
2865  * Visibility mode constant - Use display to hide element\r
2866  * @static\r
2867  * @type Number\r
2868  */\r
2869 El.DISPLAY = 2;\r
2870 \r
2871 El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};\r
2872 El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};\r
2873 El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};\r
2874 \r
2875 \r
2876 \r
2877 /**\r
2878  * @private\r
2879  */\r
2880 El.cache = {};\r
2881 \r
2882 var docEl;\r
2883 \r
2884 /**\r
2885  * Static method to retrieve Ext.Element objects.\r
2886  * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method\r
2887  * retrieves Ext.Element objects which encapsulate DOM elements. To retrieve a Component by\r
2888  * its ID, use {@link Ext.ComponentMgr#get}.</p>\r
2889  * <p>Uses simple caching to consistently return the same object.\r
2890  * Automatically fixes if an object was recreated with the same id via AJAX or DOM.</p>\r
2891  * @param {Mixed} el The id of the node, a DOM Node or an existing Element.\r
2892  * @return {Element} The {@link Ext.Element Element} object (or null if no matching element was found)\r
2893  * @static\r
2894  */\r
2895 El.get = function(el){\r
2896     var ex, elm, id;\r
2897     if(!el){ return null; }\r
2898     if(typeof el == "string"){ // element id\r
2899         if(!(elm = document.getElementById(el))){\r
2900             return null;\r
2901         }\r
2902         if(ex = El.cache[el]){\r
2903             ex.dom = elm;\r
2904         }else{\r
2905             ex = El.cache[el] = new El(elm);\r
2906         }\r
2907         return ex;\r
2908     }else if(el.tagName){ // dom element\r
2909         if(!(id = el.id)){\r
2910             id = Ext.id(el);\r
2911         }\r
2912         if(ex = El.cache[id]){\r
2913             ex.dom = el;\r
2914         }else{\r
2915             ex = El.cache[id] = new El(el);\r
2916         }\r
2917         return ex;\r
2918     }else if(el instanceof El){\r
2919         if(el != docEl){\r
2920             el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,\r
2921                                                           // catch case where it hasn't been appended\r
2922             El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it\r
2923         }\r
2924         return el;\r
2925     }else if(el.isComposite){\r
2926         return el;\r
2927     }else if(Ext.isArray(el)){\r
2928         return El.select(el);\r
2929     }else if(el == document){\r
2930         // create a bogus element object representing the document object\r
2931         if(!docEl){\r
2932             var f = function(){};\r
2933             f.prototype = El.prototype;\r
2934             docEl = new f();\r
2935             docEl.dom = document;\r
2936         }\r
2937         return docEl;\r
2938     }\r
2939     return null;\r
2940 };\r
2941 \r
2942 // private\r
2943 El.uncache = function(el){\r
2944     for(var i = 0, a = arguments, len = a.length; i < len; i++) {\r
2945         if(a[i]){\r
2946             delete El.cache[a[i].id || a[i]];\r
2947         }\r
2948     }\r
2949 };\r
2950 \r
2951 // private\r
2952 // Garbage collection - uncache elements/purge listeners on orphaned elements\r
2953 // so we don't hold a reference and cause the browser to retain them\r
2954 El.garbageCollect = function(){\r
2955     if(!Ext.enableGarbageCollector){\r
2956         clearInterval(El.collectorThread);\r
2957         return;\r
2958     }\r
2959     for(var eid in El.cache){\r
2960         var el = El.cache[eid], d = el.dom;\r
2961         // -------------------------------------------------------\r
2962         // Determining what is garbage:\r
2963         // -------------------------------------------------------\r
2964         // !d\r
2965         // dom node is null, definitely garbage\r
2966         // -------------------------------------------------------\r
2967         // !d.parentNode\r
2968         // no parentNode == direct orphan, definitely garbage\r
2969         // -------------------------------------------------------\r
2970         // !d.offsetParent && !document.getElementById(eid)\r
2971         // display none elements have no offsetParent so we will\r
2972         // also try to look it up by it's id. However, check\r
2973         // offsetParent first so we don't do unneeded lookups.\r
2974         // This enables collection of elements that are not orphans\r
2975         // directly, but somewhere up the line they have an orphan\r
2976         // parent.\r
2977         // -------------------------------------------------------\r
2978         if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){\r
2979             delete El.cache[eid];\r
2980             if(d && Ext.enableListenerCollection){\r
2981                 Ext.EventManager.removeAll(d);\r
2982             }\r
2983         }\r
2984     }\r
2985 }\r
2986 El.collectorThreadId = setInterval(El.garbageCollect, 30000);\r
2987 \r
2988 var flyFn = function(){};\r
2989 flyFn.prototype = El.prototype;\r
2990 var _cls = new flyFn();\r
2991 \r
2992 // dom is optional\r
2993 El.Flyweight = function(dom){\r
2994     this.dom = dom;\r
2995 };\r
2996 \r
2997 El.Flyweight.prototype = _cls;\r
2998 El.Flyweight.prototype.isFlyweight = true;\r
2999 \r
3000 El._flyweights = {};\r
3001 /**\r
3002  * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -\r
3003  * the dom node can be overwritten by other code.\r
3004  * @param {String/HTMLElement} el The dom node or id\r
3005  * @param {String} named (optional) Allows for creation of named reusable flyweights to\r
3006  *                                  prevent conflicts (e.g. internally Ext uses "_internal")\r
3007  * @static\r
3008  * @return {Element} The shared Element object (or null if no matching element was found)\r
3009  */\r
3010 El.fly = function(el, named){\r
3011     named = named || '_global';\r
3012     el = Ext.getDom(el);\r
3013     if(!el){\r
3014         return null;\r
3015     }\r
3016     if(!El._flyweights[named]){\r
3017         El._flyweights[named] = new El.Flyweight();\r
3018     }\r
3019     El._flyweights[named].dom = el;\r
3020     return El._flyweights[named];\r
3021 };\r
3022 \r
3023 /**\r
3024  * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.\r
3025  * Automatically fixes if an object was recreated with the same id via AJAX or DOM.\r
3026  * Shorthand of {@link Ext.Element#get}\r
3027  * @param {Mixed} el The id of the node, a DOM Node or an existing Element.\r
3028  * @return {Element} The Element object\r
3029  * @member Ext\r
3030  * @method get\r
3031  */\r
3032 Ext.get = El.get;\r
3033 /**\r
3034  * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -\r
3035  * the dom node can be overwritten by other code.\r
3036  * Shorthand of {@link Ext.Element#fly}\r
3037  * @param {String/HTMLElement} el The dom node or id\r
3038  * @param {String} named (optional) Allows for creation of named reusable flyweights to\r
3039  *                                  prevent conflicts (e.g. internally Ext uses "_internal")\r
3040  * @static\r
3041  * @return {Element} The shared Element object\r
3042  * @member Ext\r
3043  * @method fly\r
3044  */\r
3045 Ext.fly = El.fly;\r
3046 \r
3047 // speedy lookup for elements never to box adjust\r
3048 var noBoxAdjust = Ext.isStrict ? {\r
3049     select:1\r
3050 } : {\r
3051     input:1, select:1, textarea:1\r
3052 };\r
3053 if(Ext.isIE || Ext.isGecko){\r
3054     noBoxAdjust['button'] = 1;\r
3055 }\r
3056 \r
3057 \r
3058 Ext.EventManager.on(window, 'unload', function(){\r
3059     delete El.cache;\r
3060     delete El._flyweights;\r
3061 });\r
3062 })();