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