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