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