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