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