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