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