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