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