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