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