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         /**
129          * Sets the element's visibility mode. When setVisible() is called it
130          * will use this to determine whether to set the visibility or the display property.
131          * @param visMode Element.VISIBILITY or Element.DISPLAY
132          * @return {Roo.Element} this
133          */
134         setVisibilityMode : function(visMode){
135             this.visibilityMode = visMode;
136             return this;
137         },
138         /**
139          * Convenience method for setVisibilityMode(Element.DISPLAY)
140          * @param {String} display (optional) What to set display to when visible
141          * @return {Roo.Element} this
142          */
143         enableDisplayMode : function(display){
144             this.setVisibilityMode(El.DISPLAY);
145             if(typeof display != "undefined") { this.originalDisplay = display; }
146             return this;
147         },
148
149         /**
150          * 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)
151          * @param {String} selector The simple selector to test
152          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
153                 search as a number or element (defaults to 10 || document.body)
154          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
155          * @return {HTMLElement} The matching DOM node (or null if no match was found)
156          */
157         findParent : function(simpleSelector, maxDepth, returnEl){
158             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
159             maxDepth = maxDepth || 50;
160             if(typeof maxDepth != "number"){
161                 stopEl = Roo.getDom(maxDepth);
162                 maxDepth = 10;
163             }
164             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
165                 if(dq.is(p, simpleSelector)){
166                     return returnEl ? Roo.get(p) : p;
167                 }
168                 depth++;
169                 p = p.parentNode;
170             }
171             return null;
172         },
173
174
175         /**
176          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
177          * @param {String} selector The simple selector to test
178          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
179                 search as a number or element (defaults to 10 || document.body)
180          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
181          * @return {HTMLElement} The matching DOM node (or null if no match was found)
182          */
183         findParentNode : function(simpleSelector, maxDepth, returnEl){
184             var p = Roo.fly(this.dom.parentNode, '_internal');
185             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
186         },
187
188         /**
189          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
190          * This is a shortcut for findParentNode() that always returns an Roo.Element.
191          * @param {String} selector The simple selector to test
192          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
193                 search as a number or element (defaults to 10 || document.body)
194          * @return {Roo.Element} The matching DOM node (or null if no match was found)
195          */
196         up : function(simpleSelector, maxDepth){
197             return this.findParentNode(simpleSelector, maxDepth, true);
198         },
199
200
201
202         /**
203          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
204          * @param {String} selector The simple selector to test
205          * @return {Boolean} True if this element matches the selector, else false
206          */
207         is : function(simpleSelector){
208             return Roo.DomQuery.is(this.dom, simpleSelector);
209         },
210
211         /**
212          * Perform animation on this element.
213          * @param {Object} args The YUI animation control args
214          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
215          * @param {Function} onComplete (optional) Function to call when animation completes
216          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
217          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
218          * @return {Roo.Element} this
219          */
220         animate : function(args, duration, onComplete, easing, animType){
221             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
222             return this;
223         },
224
225         /*
226          * @private Internal animation call
227          */
228         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
229             animType = animType || 'run';
230             opt = opt || {};
231             var anim = Roo.lib.Anim[animType](
232                 this.dom, args,
233                 (opt.duration || defaultDur) || .35,
234                 (opt.easing || defaultEase) || 'easeOut',
235                 function(){
236                     Roo.callback(cb, this);
237                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
238                 },
239                 this
240             );
241             opt.anim = anim;
242             return anim;
243         },
244
245         // private legacy anim prep
246         preanim : function(a, i){
247             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
248         },
249
250         /**
251          * Removes worthless text nodes
252          * @param {Boolean} forceReclean (optional) By default the element
253          * keeps track if it has been cleaned already so
254          * you can call this over and over. However, if you update the element and
255          * need to force a reclean, you can pass true.
256          */
257         clean : function(forceReclean){
258             if(this.isCleaned && forceReclean !== true){
259                 return this;
260             }
261             var ns = /\S/;
262             var d = this.dom, n = d.firstChild, ni = -1;
263             while(n){
264                 var nx = n.nextSibling;
265                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
266                     d.removeChild(n);
267                 }else{
268                     n.nodeIndex = ++ni;
269                 }
270                 n = nx;
271             }
272             this.isCleaned = true;
273             return this;
274         },
275
276         // private
277         calcOffsetsTo : function(el){
278             el = Roo.get(el);
279             var d = el.dom;
280             var restorePos = false;
281             if(el.getStyle('position') == 'static'){
282                 el.position('relative');
283                 restorePos = true;
284             }
285             var x = 0, y =0;
286             var op = this.dom;
287             while(op && op != d && op.tagName != 'HTML'){
288                 x+= op.offsetLeft;
289                 y+= op.offsetTop;
290                 op = op.offsetParent;
291             }
292             if(restorePos){
293                 el.position('static');
294             }
295             return [x, y];
296         },
297
298         /**
299          * Scrolls this element into view within the passed container.
300          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
301          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
302          * @return {Roo.Element} this
303          */
304         scrollIntoView : function(container, hscroll){
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
314             var ch = c.clientHeight;
315             var ct = parseInt(c.scrollTop, 10);
316             var cl = parseInt(c.scrollLeft, 10);
317             var cb = ct + ch;
318             var cr = cl + c.clientWidth;
319
320             if(t < ct){
321                 c.scrollTop = t;
322             }else if(b > cb){
323                 c.scrollTop = b-ch;
324             }
325
326             if(hscroll !== false){
327                 if(l < cl){
328                     c.scrollLeft = l;
329                 }else if(r > cr){
330                     c.scrollLeft = r-c.clientWidth;
331                 }
332             }
333             return this;
334         },
335
336         // private
337         scrollChildIntoView : function(child, hscroll){
338             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
339         },
340
341         /**
342          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
343          * the new height may not be available immediately.
344          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
345          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
346          * @param {Function} onComplete (optional) Function to call when animation completes
347          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
348          * @return {Roo.Element} this
349          */
350         autoHeight : function(animate, duration, onComplete, easing){
351             var oldHeight = this.getHeight();
352             this.clip();
353             this.setHeight(1); // force clipping
354             setTimeout(function(){
355                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
356                 if(!animate){
357                     this.setHeight(height);
358                     this.unclip();
359                     if(typeof onComplete == "function"){
360                         onComplete();
361                     }
362                 }else{
363                     this.setHeight(oldHeight); // restore original height
364                     this.setHeight(height, animate, duration, function(){
365                         this.unclip();
366                         if(typeof onComplete == "function") { onComplete(); }
367                     }.createDelegate(this), easing);
368                 }
369             }.createDelegate(this), 0);
370             return this;
371         },
372
373         /**
374          * Returns true if this element is an ancestor of the passed element
375          * @param {HTMLElement/String} el The element to check
376          * @return {Boolean} True if this element is an ancestor of el, else false
377          */
378         contains : function(el){
379             if(!el){return false;}
380             return D.isAncestor(this.dom, el.dom ? el.dom : el);
381         },
382
383         /**
384          * Checks whether the element is currently visible using both visibility and display properties.
385          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
386          * @return {Boolean} True if the element is currently visible, else false
387          */
388         isVisible : function(deep) {
389             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
390             if(deep !== true || !vis){
391                 return vis;
392             }
393             var p = this.dom.parentNode;
394             while(p && p.tagName.toLowerCase() != "body"){
395                 if(!Roo.fly(p, '_isVisible').isVisible()){
396                     return false;
397                 }
398                 p = p.parentNode;
399             }
400             return true;
401         },
402
403         /**
404          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
405          * @param {String} selector The CSS selector
406          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
407          * @return {CompositeElement/CompositeElementLite} The composite element
408          */
409         select : function(selector, unique){
410             return El.select(selector, unique, this.dom);
411         },
412
413         /**
414          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
415          * @param {String} selector The CSS selector
416          * @return {Array} An array of the matched nodes
417          */
418         query : function(selector, unique){
419             return Roo.DomQuery.select(selector, this.dom);
420         },
421
422         /**
423          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
424          * @param {String} selector The CSS selector
425          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
426          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
427          */
428         child : function(selector, returnDom){
429             var n = Roo.DomQuery.selectNode(selector, this.dom);
430             return returnDom ? n : Roo.get(n);
431         },
432
433         /**
434          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
435          * @param {String} selector The CSS selector
436          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
437          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
438          */
439         down : function(selector, returnDom){
440             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
441             return returnDom ? n : Roo.get(n);
442         },
443
444         /**
445          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
446          * @param {String} group The group the DD object is member of
447          * @param {Object} config The DD config object
448          * @param {Object} overrides An object containing methods to override/implement on the DD object
449          * @return {Roo.dd.DD} The DD object
450          */
451         initDD : function(group, config, overrides){
452             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
453             return Roo.apply(dd, overrides);
454         },
455
456         /**
457          * Initializes a {@link Roo.dd.DDProxy} object for this element.
458          * @param {String} group The group the DDProxy object is member of
459          * @param {Object} config The DDProxy config object
460          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
461          * @return {Roo.dd.DDProxy} The DDProxy object
462          */
463         initDDProxy : function(group, config, overrides){
464             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
465             return Roo.apply(dd, overrides);
466         },
467
468         /**
469          * Initializes a {@link Roo.dd.DDTarget} object for this element.
470          * @param {String} group The group the DDTarget object is member of
471          * @param {Object} config The DDTarget config object
472          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
473          * @return {Roo.dd.DDTarget} The DDTarget object
474          */
475         initDDTarget : function(group, config, overrides){
476             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
477             return Roo.apply(dd, overrides);
478         },
479
480         /**
481          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
482          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
483          * @param {Boolean} visible Whether the element is visible
484          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
485          * @return {Roo.Element} this
486          */
487          setVisible : function(visible, animate){
488             if(!animate || !A){
489                 if(this.visibilityMode == El.DISPLAY){
490                     this.setDisplayed(visible);
491                 }else{
492                     this.fixDisplay();
493                     this.dom.style.visibility = visible ? "visible" : "hidden";
494                 }
495             }else{
496                 // closure for composites
497                 var dom = this.dom;
498                 var visMode = this.visibilityMode;
499                 if(visible){
500                     this.setOpacity(.01);
501                     this.setVisible(true);
502                 }
503                 this.anim({opacity: { to: (visible?1:0) }},
504                       this.preanim(arguments, 1),
505                       null, .35, 'easeIn', function(){
506                          if(!visible){
507                              if(visMode == El.DISPLAY){
508                                  dom.style.display = "none";
509                              }else{
510                                  dom.style.visibility = "hidden";
511                              }
512                              Roo.get(dom).setOpacity(1);
513                          }
514                      });
515             }
516             return this;
517         },
518
519         /**
520          * Returns true if display is not "none"
521          * @return {Boolean}
522          */
523         isDisplayed : function() {
524             return this.getStyle("display") != "none";
525         },
526
527         /**
528          * Toggles the element's visibility or display, depending on visibility mode.
529          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
530          * @return {Roo.Element} this
531          */
532         toggle : function(animate){
533             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
534             return this;
535         },
536
537         /**
538          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
539          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
540          * @return {Roo.Element} this
541          */
542         setDisplayed : function(value) {
543             if(typeof value == "boolean"){
544                value = value ? this.originalDisplay : "none";
545             }
546             this.setStyle("display", value);
547             return this;
548         },
549
550         /**
551          * Tries to focus the element. Any exceptions are caught and ignored.
552          * @return {Roo.Element} this
553          */
554         focus : function() {
555             try{
556                 this.dom.focus();
557             }catch(e){}
558             return this;
559         },
560
561         /**
562          * Tries to blur the element. Any exceptions are caught and ignored.
563          * @return {Roo.Element} this
564          */
565         blur : function() {
566             try{
567                 this.dom.blur();
568             }catch(e){}
569             return this;
570         },
571
572         /**
573          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
574          * @param {String/Array} className The CSS class to add, or an array of classes
575          * @return {Roo.Element} this
576          */
577         addClass : function(className){
578             if(className instanceof Array){
579                 for(var i = 0, len = className.length; i < len; i++) {
580                     this.addClass(className[i]);
581                 }
582             }else{
583                 if(className && !this.hasClass(className)){
584                     this.dom.className = this.dom.className + " " + className;
585                 }
586             }
587             return this;
588         },
589
590         /**
591          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
592          * @param {String/Array} className The CSS class to add, or an array of classes
593          * @return {Roo.Element} this
594          */
595         radioClass : function(className){
596             var siblings = this.dom.parentNode.childNodes;
597             for(var i = 0; i < siblings.length; i++) {
598                 var s = siblings[i];
599                 if(s.nodeType == 1){
600                     Roo.get(s).removeClass(className);
601                 }
602             }
603             this.addClass(className);
604             return this;
605         },
606
607         /**
608          * Removes one or more CSS classes from the element.
609          * @param {String/Array} className The CSS class to remove, or an array of classes
610          * @return {Roo.Element} this
611          */
612         removeClass : function(className){
613             if(!className || !this.dom.className){
614                 return this;
615             }
616             if(className instanceof Array){
617                 for(var i = 0, len = className.length; i < len; i++) {
618                     this.removeClass(className[i]);
619                 }
620             }else{
621                 if(this.hasClass(className)){
622                     var re = this.classReCache[className];
623                     if (!re) {
624                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
625                        this.classReCache[className] = re;
626                     }
627                     this.dom.className =
628                         this.dom.className.replace(re, " ");
629                 }
630             }
631             return this;
632         },
633
634         // private
635         classReCache: {},
636
637         /**
638          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
639          * @param {String} className The CSS class to toggle
640          * @return {Roo.Element} this
641          */
642         toggleClass : function(className){
643             if(this.hasClass(className)){
644                 this.removeClass(className);
645             }else{
646                 this.addClass(className);
647             }
648             return this;
649         },
650
651         /**
652          * Checks if the specified CSS class exists on this element's DOM node.
653          * @param {String} className The CSS class to check for
654          * @return {Boolean} True if the class exists, else false
655          */
656         hasClass : function(className){
657             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
658         },
659
660         /**
661          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
662          * @param {String} oldClassName The CSS class to replace
663          * @param {String} newClassName The replacement CSS class
664          * @return {Roo.Element} this
665          */
666         replaceClass : function(oldClassName, newClassName){
667             this.removeClass(oldClassName);
668             this.addClass(newClassName);
669             return this;
670         },
671
672         /**
673          * Returns an object with properties matching the styles requested.
674          * For example, el.getStyles('color', 'font-size', 'width') might return
675          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
676          * @param {String} style1 A style name
677          * @param {String} style2 A style name
678          * @param {String} etc.
679          * @return {Object} The style object
680          */
681         getStyles : function(){
682             var a = arguments, len = a.length, r = {};
683             for(var i = 0; i < len; i++){
684                 r[a[i]] = this.getStyle(a[i]);
685             }
686             return r;
687         },
688
689         /**
690          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
691          * @param {String} property The style property whose value is returned.
692          * @return {String} The current value of the style property for this element.
693          */
694         getStyle : function(){
695             return view && view.getComputedStyle ?
696                 function(prop){
697                     Roo.log('1');
698                     var el = this.dom, v, cs, camel;
699                     if(prop == 'float'){
700                         prop = "cssFloat";
701                     }
702                     if(el.style && (v = el.style[prop])){
703                         return v;
704                     }
705                     if(cs = view.getComputedStyle(el, "")){
706                         if(!(camel = propCache[prop])){
707                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
708                         }
709                         return cs[camel];
710                     }
711                     return null;
712                 } :
713                 function(prop){
714                     Roo.log('1');
715                     var el = this.dom, v, cs, camel;
716                     if(prop == 'opacity'){
717                         if(typeof el.style.filter == 'string'){
718                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
719                             if(m){
720                                 var fv = parseFloat(m[1]);
721                                 if(!isNaN(fv)){
722                                     return fv ? fv / 100 : 0;
723                                 }
724                             }
725                         }
726                         return 1;
727                     }else if(prop == 'float'){
728                         prop = "styleFloat";
729                     }
730                     if(!(camel = propCache[prop])){
731                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
732                     }
733                     if(v = el.style[camel]){
734                         return v;
735                     }
736                     if(cs = el.currentStyle){
737                         return cs[camel];
738                     }
739                     return null;
740                 };
741         }(),
742
743         /**
744          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
745          * @param {String/Object} property The style property to be set, or an object of multiple styles.
746          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
747          * @return {Roo.Element} this
748          */
749         setStyle : function(prop, value){
750             if(typeof prop == "string"){
751                 
752                 if (prop == 'float') {
753                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
754                     return this;
755                 }
756                 
757                 var camel;
758                 if(!(camel = propCache[prop])){
759                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
760                 }
761                 
762                 if(camel == 'opacity') {
763                     this.setOpacity(value);
764                 }else{
765                     this.dom.style[camel] = value;
766                 }
767             }else{
768                 for(var style in prop){
769                     if(typeof prop[style] != "function"){
770                        this.setStyle(style, prop[style]);
771                     }
772                 }
773             }
774             return this;
775         },
776
777         /**
778          * More flexible version of {@link #setStyle} for setting style properties.
779          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
780          * a function which returns such a specification.
781          * @return {Roo.Element} this
782          */
783         applyStyles : function(style){
784             Roo.DomHelper.applyStyles(this.dom, style);
785             return this;
786         },
787
788         /**
789           * 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).
790           * @return {Number} The X position of the element
791           */
792         getX : function(){
793             return D.getX(this.dom);
794         },
795
796         /**
797           * 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).
798           * @return {Number} The Y position of the element
799           */
800         getY : function(){
801             return D.getY(this.dom);
802         },
803
804         /**
805           * 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).
806           * @return {Array} The XY position of the element
807           */
808         getXY : function(){
809             return D.getXY(this.dom);
810         },
811
812         /**
813          * 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).
814          * @param {Number} The X position of the element
815          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
816          * @return {Roo.Element} this
817          */
818         setX : function(x, animate){
819             if(!animate || !A){
820                 D.setX(this.dom, x);
821             }else{
822                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
823             }
824             return this;
825         },
826
827         /**
828          * 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).
829          * @param {Number} The Y position of the element
830          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
831          * @return {Roo.Element} this
832          */
833         setY : function(y, animate){
834             if(!animate || !A){
835                 D.setY(this.dom, y);
836             }else{
837                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
838             }
839             return this;
840         },
841
842         /**
843          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
844          * @param {String} left The left CSS property value
845          * @return {Roo.Element} this
846          */
847         setLeft : function(left){
848             this.setStyle("left", this.addUnits(left));
849             return this;
850         },
851
852         /**
853          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
854          * @param {String} top The top CSS property value
855          * @return {Roo.Element} this
856          */
857         setTop : function(top){
858             this.setStyle("top", this.addUnits(top));
859             return this;
860         },
861
862         /**
863          * Sets the element's CSS right style.
864          * @param {String} right The right CSS property value
865          * @return {Roo.Element} this
866          */
867         setRight : function(right){
868             this.setStyle("right", this.addUnits(right));
869             return this;
870         },
871
872         /**
873          * Sets the element's CSS bottom style.
874          * @param {String} bottom The bottom CSS property value
875          * @return {Roo.Element} this
876          */
877         setBottom : function(bottom){
878             this.setStyle("bottom", this.addUnits(bottom));
879             return this;
880         },
881
882         /**
883          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
884          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
885          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
886          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
887          * @return {Roo.Element} this
888          */
889         setXY : function(pos, animate){
890             if(!animate || !A){
891                 D.setXY(this.dom, pos);
892             }else{
893                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
894             }
895             return this;
896         },
897
898         /**
899          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
900          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
901          * @param {Number} x X value for new position (coordinates are page-based)
902          * @param {Number} y Y value for new position (coordinates are page-based)
903          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
904          * @return {Roo.Element} this
905          */
906         setLocation : function(x, y, animate){
907             this.setXY([x, y], this.preanim(arguments, 2));
908             return this;
909         },
910
911         /**
912          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
913          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
914          * @param {Number} x X value for new position (coordinates are page-based)
915          * @param {Number} y Y value for new position (coordinates are page-based)
916          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
917          * @return {Roo.Element} this
918          */
919         moveTo : function(x, y, animate){
920             this.setXY([x, y], this.preanim(arguments, 2));
921             return this;
922         },
923
924         /**
925          * Returns the region of the given element.
926          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
927          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
928          */
929         getRegion : function(){
930             return D.getRegion(this.dom);
931         },
932
933         /**
934          * Returns the offset height of the element
935          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
936          * @return {Number} The element's height
937          */
938         getHeight : function(contentHeight){
939             var h = this.dom.offsetHeight || 0;
940             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
941         },
942
943         /**
944          * Returns the offset width of the element
945          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
946          * @return {Number} The element's width
947          */
948         getWidth : function(contentWidth){
949             var w = this.dom.offsetWidth || 0;
950             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
951         },
952
953         /**
954          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
955          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
956          * if a height has not been set using CSS.
957          * @return {Number}
958          */
959         getComputedHeight : function(){
960             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
961             if(!h){
962                 h = parseInt(this.getStyle('height'), 10) || 0;
963                 if(!this.isBorderBox()){
964                     h += this.getFrameWidth('tb');
965                 }
966             }
967             return h;
968         },
969
970         /**
971          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
972          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
973          * if a width has not been set using CSS.
974          * @return {Number}
975          */
976         getComputedWidth : function(){
977             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
978             if(!w){
979                 w = parseInt(this.getStyle('width'), 10) || 0;
980                 if(!this.isBorderBox()){
981                     w += this.getFrameWidth('lr');
982                 }
983             }
984             return w;
985         },
986
987         /**
988          * Returns the size of the element.
989          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
990          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
991          */
992         getSize : function(contentSize){
993             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
994         },
995
996         /**
997          * Returns the width and height of the viewport.
998          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
999          */
1000         getViewSize : function(){
1001             var d = this.dom, doc = document, aw = 0, ah = 0;
1002             if(d == doc || d == doc.body){
1003                 return {width : D.getViewWidth(), height: D.getViewHeight()};
1004             }else{
1005                 return {
1006                     width : d.clientWidth,
1007                     height: d.clientHeight
1008                 };
1009             }
1010         },
1011
1012         /**
1013          * Returns the value of the "value" attribute
1014          * @param {Boolean} asNumber true to parse the value as a number
1015          * @return {String/Number}
1016          */
1017         getValue : function(asNumber){
1018             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
1019         },
1020
1021         // private
1022         adjustWidth : function(width){
1023             if(typeof width == "number"){
1024                 if(this.autoBoxAdjust && !this.isBorderBox()){
1025                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
1026                 }
1027                 if(width < 0){
1028                     width = 0;
1029                 }
1030             }
1031             return width;
1032         },
1033
1034         // private
1035         adjustHeight : function(height){
1036             if(typeof height == "number"){
1037                if(this.autoBoxAdjust && !this.isBorderBox()){
1038                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
1039                }
1040                if(height < 0){
1041                    height = 0;
1042                }
1043             }
1044             return height;
1045         },
1046
1047         /**
1048          * Set the width of the element
1049          * @param {Number} width The new width
1050          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
1051          * @return {Roo.Element} this
1052          */
1053         setWidth : function(width, animate){
1054             width = this.adjustWidth(width);
1055             if(!animate || !A){
1056                 this.dom.style.width = this.addUnits(width);
1057             }else{
1058                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
1059             }
1060             return this;
1061         },
1062
1063         /**
1064          * Set the height of the element
1065          * @param {Number} height The new height
1066          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
1067          * @return {Roo.Element} this
1068          */
1069          setHeight : function(height, animate){
1070             height = this.adjustHeight(height);
1071             if(!animate || !A){
1072                 this.dom.style.height = this.addUnits(height);
1073             }else{
1074                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
1075             }
1076             return this;
1077         },
1078
1079         /**
1080          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
1081          * @param {Number} width The new width
1082          * @param {Number} height The new height
1083          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
1084          * @return {Roo.Element} this
1085          */
1086          setSize : function(width, height, animate){
1087             if(typeof width == "object"){ // in case of object from getSize()
1088                 height = width.height; width = width.width;
1089             }
1090             width = this.adjustWidth(width); height = this.adjustHeight(height);
1091             if(!animate || !A){
1092                 this.dom.style.width = this.addUnits(width);
1093                 this.dom.style.height = this.addUnits(height);
1094             }else{
1095                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
1096             }
1097             return this;
1098         },
1099
1100         /**
1101          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
1102          * @param {Number} x X value for new position (coordinates are page-based)
1103          * @param {Number} y Y value for new position (coordinates are page-based)
1104          * @param {Number} width The new width
1105          * @param {Number} height The new height
1106          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
1107          * @return {Roo.Element} this
1108          */
1109         setBounds : function(x, y, width, height, animate){
1110             if(!animate || !A){
1111                 this.setSize(width, height);
1112                 this.setLocation(x, y);
1113             }else{
1114                 width = this.adjustWidth(width); height = this.adjustHeight(height);
1115                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
1116                               this.preanim(arguments, 4), 'motion');
1117             }
1118             return this;
1119         },
1120
1121         /**
1122          * 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.
1123          * @param {Roo.lib.Region} region The region to fill
1124          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
1125          * @return {Roo.Element} this
1126          */
1127         setRegion : function(region, animate){
1128             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
1129             return this;
1130         },
1131
1132         /**
1133          * Appends an event handler
1134          *
1135          * @param {String}   eventName     The type of event to append
1136          * @param {Function} fn        The method the event invokes
1137          * @param {Object} scope       (optional) The scope (this object) of the fn
1138          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
1139          */
1140         addListener : function(eventName, fn, scope, options){
1141             if (this.dom) {
1142                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
1143             }
1144         },
1145
1146         /**
1147          * Removes an event handler from this element
1148          * @param {String} eventName the type of event to remove
1149          * @param {Function} fn the method the event invokes
1150          * @return {Roo.Element} this
1151          */
1152         removeListener : function(eventName, fn){
1153             Roo.EventManager.removeListener(this.dom,  eventName, fn);
1154             return this;
1155         },
1156
1157         /**
1158          * Removes all previous added listeners from this element
1159          * @return {Roo.Element} this
1160          */
1161         removeAllListeners : function(){
1162             E.purgeElement(this.dom);
1163             return this;
1164         },
1165
1166         relayEvent : function(eventName, observable){
1167             this.on(eventName, function(e){
1168                 observable.fireEvent(eventName, e);
1169             });
1170         },
1171
1172         /**
1173          * Set the opacity of the element
1174          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
1175          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
1176          * @return {Roo.Element} this
1177          */
1178          setOpacity : function(opacity, animate){
1179             if(!animate || !A){
1180                 var s = this.dom.style;
1181                 if(Roo.isIE){
1182                     s.zoom = 1;
1183                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
1184                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
1185                 }else{
1186                     s.opacity = opacity;
1187                 }
1188             }else{
1189                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
1190             }
1191             return this;
1192         },
1193
1194         /**
1195          * Gets the left X coordinate
1196          * @param {Boolean} local True to get the local css position instead of page coordinate
1197          * @return {Number}
1198          */
1199         getLeft : function(local){
1200             if(!local){
1201                 return this.getX();
1202             }else{
1203                 return parseInt(this.getStyle("left"), 10) || 0;
1204             }
1205         },
1206
1207         /**
1208          * Gets the right X coordinate of the element (element X position + element width)
1209          * @param {Boolean} local True to get the local css position instead of page coordinate
1210          * @return {Number}
1211          */
1212         getRight : function(local){
1213             if(!local){
1214                 return this.getX() + this.getWidth();
1215             }else{
1216                 return (this.getLeft(true) + this.getWidth()) || 0;
1217             }
1218         },
1219
1220         /**
1221          * Gets the top Y coordinate
1222          * @param {Boolean} local True to get the local css position instead of page coordinate
1223          * @return {Number}
1224          */
1225         getTop : function(local) {
1226             if(!local){
1227                 return this.getY();
1228             }else{
1229                 return parseInt(this.getStyle("top"), 10) || 0;
1230             }
1231         },
1232
1233         /**
1234          * Gets the bottom Y coordinate of the element (element Y position + element height)
1235          * @param {Boolean} local True to get the local css position instead of page coordinate
1236          * @return {Number}
1237          */
1238         getBottom : function(local){
1239             if(!local){
1240                 return this.getY() + this.getHeight();
1241             }else{
1242                 return (this.getTop(true) + this.getHeight()) || 0;
1243             }
1244         },
1245
1246         /**
1247         * Initializes positioning on this element. If a desired position is not passed, it will make the
1248         * the element positioned relative IF it is not already positioned.
1249         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
1250         * @param {Number} zIndex (optional) The zIndex to apply
1251         * @param {Number} x (optional) Set the page X position
1252         * @param {Number} y (optional) Set the page Y position
1253         */
1254         position : function(pos, zIndex, x, y){
1255             if(!pos){
1256                if(this.getStyle('position') == 'static'){
1257                    this.setStyle('position', 'relative');
1258                }
1259             }else{
1260                 this.setStyle("position", pos);
1261             }
1262             if(zIndex){
1263                 this.setStyle("z-index", zIndex);
1264             }
1265             if(x !== undefined && y !== undefined){
1266                 this.setXY([x, y]);
1267             }else if(x !== undefined){
1268                 this.setX(x);
1269             }else if(y !== undefined){
1270                 this.setY(y);
1271             }
1272         },
1273
1274         /**
1275         * Clear positioning back to the default when the document was loaded
1276         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
1277         * @return {Roo.Element} this
1278          */
1279         clearPositioning : function(value){
1280             value = value ||'';
1281             this.setStyle({
1282                 "left": value,
1283                 "right": value,
1284                 "top": value,
1285                 "bottom": value,
1286                 "z-index": "",
1287                 "position" : "static"
1288             });
1289             return this;
1290         },
1291
1292         /**
1293         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
1294         * snapshot before performing an update and then restoring the element.
1295         * @return {Object}
1296         */
1297         getPositioning : function(){
1298             var l = this.getStyle("left");
1299             var t = this.getStyle("top");
1300             return {
1301                 "position" : this.getStyle("position"),
1302                 "left" : l,
1303                 "right" : l ? "" : this.getStyle("right"),
1304                 "top" : t,
1305                 "bottom" : t ? "" : this.getStyle("bottom"),
1306                 "z-index" : this.getStyle("z-index")
1307             };
1308         },
1309
1310         /**
1311          * Gets the width of the border(s) for the specified side(s)
1312          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
1313          * passing lr would get the border (l)eft width + the border (r)ight width.
1314          * @return {Number} The width of the sides passed added together
1315          */
1316         getBorderWidth : function(side){
1317             return this.addStyles(side, El.borders);
1318         },
1319
1320         /**
1321          * Gets the width of the padding(s) for the specified side(s)
1322          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
1323          * passing lr would get the padding (l)eft + the padding (r)ight.
1324          * @return {Number} The padding of the sides passed added together
1325          */
1326         getPadding : function(side){
1327             return this.addStyles(side, El.paddings);
1328         },
1329
1330         /**
1331         * Set positioning with an object returned by getPositioning().
1332         * @param {Object} posCfg
1333         * @return {Roo.Element} this
1334          */
1335         setPositioning : function(pc){
1336             this.applyStyles(pc);
1337             if(pc.right == "auto"){
1338                 this.dom.style.right = "";
1339             }
1340             if(pc.bottom == "auto"){
1341                 this.dom.style.bottom = "";
1342             }
1343             return this;
1344         },
1345
1346         // private
1347         fixDisplay : function(){
1348             if(this.getStyle("display") == "none"){
1349                 this.setStyle("visibility", "hidden");
1350                 this.setStyle("display", this.originalDisplay); // first try reverting to default
1351                 if(this.getStyle("display") == "none"){ // if that fails, default to block
1352                     this.setStyle("display", "block");
1353                 }
1354             }
1355         },
1356
1357         /**
1358          * Quick set left and top adding default units
1359          * @param {String} left The left CSS property value
1360          * @param {String} top The top CSS property value
1361          * @return {Roo.Element} this
1362          */
1363          setLeftTop : function(left, top){
1364             this.dom.style.left = this.addUnits(left);
1365             this.dom.style.top = this.addUnits(top);
1366             return this;
1367         },
1368
1369         /**
1370          * Move this element relative to its current position.
1371          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
1372          * @param {Number} distance How far to move the element in pixels
1373          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
1374          * @return {Roo.Element} this
1375          */
1376          move : function(direction, distance, animate){
1377             var xy = this.getXY();
1378             direction = direction.toLowerCase();
1379             switch(direction){
1380                 case "l":
1381                 case "left":
1382                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
1383                     break;
1384                case "r":
1385                case "right":
1386                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
1387                     break;
1388                case "t":
1389                case "top":
1390                case "up":
1391                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
1392                     break;
1393                case "b":
1394                case "bottom":
1395                case "down":
1396                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
1397                     break;
1398             }
1399             return this;
1400         },
1401
1402         /**
1403          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
1404          * @return {Roo.Element} this
1405          */
1406         clip : function(){
1407             if(!this.isClipped){
1408                this.isClipped = true;
1409                this.originalClip = {
1410                    "o": this.getStyle("overflow"),
1411                    "x": this.getStyle("overflow-x"),
1412                    "y": this.getStyle("overflow-y")
1413                };
1414                this.setStyle("overflow", "hidden");
1415                this.setStyle("overflow-x", "hidden");
1416                this.setStyle("overflow-y", "hidden");
1417             }
1418             return this;
1419         },
1420
1421         /**
1422          *  Return clipping (overflow) to original clipping before clip() was called
1423          * @return {Roo.Element} this
1424          */
1425         unclip : function(){
1426             if(this.isClipped){
1427                 this.isClipped = false;
1428                 var o = this.originalClip;
1429                 if(o.o){this.setStyle("overflow", o.o);}
1430                 if(o.x){this.setStyle("overflow-x", o.x);}
1431                 if(o.y){this.setStyle("overflow-y", o.y);}
1432             }
1433             return this;
1434         },
1435
1436
1437         /**
1438          * Gets the x,y coordinates specified by the anchor position on the element.
1439          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
1440          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
1441          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
1442          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
1443          * @return {Array} [x, y] An array containing the element's x and y coordinates
1444          */
1445         getAnchorXY : function(anchor, local, s){
1446             //Passing a different size is useful for pre-calculating anchors,
1447             //especially for anchored animations that change the el size.
1448
1449             var w, h, vp = false;
1450             if(!s){
1451                 var d = this.dom;
1452                 if(d == document.body || d == document){
1453                     vp = true;
1454                     w = D.getViewWidth(); h = D.getViewHeight();
1455                 }else{
1456                     w = this.getWidth(); h = this.getHeight();
1457                 }
1458             }else{
1459                 w = s.width;  h = s.height;
1460             }
1461             var x = 0, y = 0, r = Math.round;
1462             switch((anchor || "tl").toLowerCase()){
1463                 case "c":
1464                     x = r(w*.5);
1465                     y = r(h*.5);
1466                 break;
1467                 case "t":
1468                     x = r(w*.5);
1469                     y = 0;
1470                 break;
1471                 case "l":
1472                     x = 0;
1473                     y = r(h*.5);
1474                 break;
1475                 case "r":
1476                     x = w;
1477                     y = r(h*.5);
1478                 break;
1479                 case "b":
1480                     x = r(w*.5);
1481                     y = h;
1482                 break;
1483                 case "tl":
1484                     x = 0;
1485                     y = 0;
1486                 break;
1487                 case "bl":
1488                     x = 0;
1489                     y = h;
1490                 break;
1491                 case "br":
1492                     x = w;
1493                     y = h;
1494                 break;
1495                 case "tr":
1496                     x = w;
1497                     y = 0;
1498                 break;
1499             }
1500             if(local === true){
1501                 return [x, y];
1502             }
1503             if(vp){
1504                 var sc = this.getScroll();
1505                 return [x + sc.left, y + sc.top];
1506             }
1507             //Add the element's offset xy
1508             var o = this.getXY();
1509             return [x+o[0], y+o[1]];
1510         },
1511
1512         /**
1513          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
1514          * supported position values.
1515          * @param {String/HTMLElement/Roo.Element} element The element to align to.
1516          * @param {String} position The position to align to.
1517          * @param {Array} offsets (optional) Offset the positioning by [x, y]
1518          * @return {Array} [x, y]
1519          */
1520         getAlignToXY : function(el, p, o){
1521             el = Roo.get(el);
1522             var d = this.dom;
1523             if(!el.dom){
1524                 throw "Element.alignTo with an element that doesn't exist";
1525             }
1526             var c = false; //constrain to viewport
1527             var p1 = "", p2 = "";
1528             o = o || [0,0];
1529
1530             if(!p){
1531                 p = "tl-bl";
1532             }else if(p == "?"){
1533                 p = "tl-bl?";
1534             }else if(p.indexOf("-") == -1){
1535                 p = "tl-" + p;
1536             }
1537             p = p.toLowerCase();
1538             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
1539             if(!m){
1540                throw "Element.alignTo with an invalid alignment " + p;
1541             }
1542             p1 = m[1]; p2 = m[2]; c = !!m[3];
1543
1544             //Subtract the aligned el's internal xy from the target's offset xy
1545             //plus custom offset to get the aligned el's new offset xy
1546             var a1 = this.getAnchorXY(p1, true);
1547             var a2 = el.getAnchorXY(p2, false);
1548             var x = a2[0] - a1[0] + o[0];
1549             var y = a2[1] - a1[1] + o[1];
1550             if(c){
1551                 //constrain the aligned el to viewport if necessary
1552                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
1553                 // 5px of margin for ie
1554                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
1555
1556                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
1557                 //perpendicular to the vp border, allow the aligned el to slide on that border,
1558                 //otherwise swap the aligned el to the opposite border of the target.
1559                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
1560                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
1561                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
1562                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
1563
1564                var doc = document;
1565                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
1566                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
1567
1568                if((x+w) > dw + scrollX){
1569                     x = swapX ? r.left-w : dw+scrollX-w;
1570                 }
1571                if(x < scrollX){
1572                    x = swapX ? r.right : scrollX;
1573                }
1574                if((y+h) > dh + scrollY){
1575                     y = swapY ? r.top-h : dh+scrollY-h;
1576                 }
1577                if (y < scrollY){
1578                    y = swapY ? r.bottom : scrollY;
1579                }
1580             }
1581             return [x,y];
1582         },
1583
1584         // private
1585         getConstrainToXY : function(){
1586             var os = {top:0, left:0, bottom:0, right: 0};
1587
1588             return function(el, local, offsets, proposedXY){
1589                 el = Roo.get(el);
1590                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
1591
1592                 var vw, vh, vx = 0, vy = 0;
1593                 if(el.dom == document.body || el.dom == document){
1594                     vw = Roo.lib.Dom.getViewWidth();
1595                     vh = Roo.lib.Dom.getViewHeight();
1596                 }else{
1597                     vw = el.dom.clientWidth;
1598                     vh = el.dom.clientHeight;
1599                     if(!local){
1600                         var vxy = el.getXY();
1601                         vx = vxy[0];
1602                         vy = vxy[1];
1603                     }
1604                 }
1605
1606                 var s = el.getScroll();
1607
1608                 vx += offsets.left + s.left;
1609                 vy += offsets.top + s.top;
1610
1611                 vw -= offsets.right;
1612                 vh -= offsets.bottom;
1613
1614                 var vr = vx+vw;
1615                 var vb = vy+vh;
1616
1617                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
1618                 var x = xy[0], y = xy[1];
1619                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
1620
1621                 // only move it if it needs it
1622                 var moved = false;
1623
1624                 // first validate right/bottom
1625                 if((x + w) > vr){
1626                     x = vr - w;
1627                     moved = true;
1628                 }
1629                 if((y + h) > vb){
1630                     y = vb - h;
1631                     moved = true;
1632                 }
1633                 // then make sure top/left isn't negative
1634                 if(x < vx){
1635                     x = vx;
1636                     moved = true;
1637                 }
1638                 if(y < vy){
1639                     y = vy;
1640                     moved = true;
1641                 }
1642                 return moved ? [x, y] : false;
1643             };
1644         }(),
1645
1646         // private
1647         adjustForConstraints : function(xy, parent, offsets){
1648             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
1649         },
1650
1651         /**
1652          * Aligns this element with another element relative to the specified anchor points. If the other element is the
1653          * document it aligns it to the viewport.
1654          * The position parameter is optional, and can be specified in any one of the following formats:
1655          * <ul>
1656          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
1657          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
1658          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
1659          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
1660          *   <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
1661          *       element's anchor point, and the second value is used as the target's anchor point.</li>
1662          * </ul>
1663          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
1664          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
1665          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
1666          * that specified in order to enforce the viewport constraints.
1667          * Following are all of the supported anchor positions:
1668     <pre>
1669     Value  Description
1670     -----  -----------------------------
1671     tl     The top left corner (default)
1672     t      The center of the top edge
1673     tr     The top right corner
1674     l      The center of the left edge
1675     c      In the center of the element
1676     r      The center of the right edge
1677     bl     The bottom left corner
1678     b      The center of the bottom edge
1679     br     The bottom right corner
1680     </pre>
1681     Example Usage:
1682     <pre><code>
1683     // align el to other-el using the default positioning ("tl-bl", non-constrained)
1684     el.alignTo("other-el");
1685
1686     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
1687     el.alignTo("other-el", "tr?");
1688
1689     // align the bottom right corner of el with the center left edge of other-el
1690     el.alignTo("other-el", "br-l?");
1691
1692     // align the center of el with the bottom left corner of other-el and
1693     // adjust the x position by -6 pixels (and the y position by 0)
1694     el.alignTo("other-el", "c-bl", [-6, 0]);
1695     </code></pre>
1696          * @param {String/HTMLElement/Roo.Element} element The element to align to.
1697          * @param {String} position The position to align to.
1698          * @param {Array} offsets (optional) Offset the positioning by [x, y]
1699          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
1700          * @return {Roo.Element} this
1701          */
1702         alignTo : function(element, position, offsets, animate){
1703             var xy = this.getAlignToXY(element, position, offsets);
1704             this.setXY(xy, this.preanim(arguments, 3));
1705             return this;
1706         },
1707
1708         /**
1709          * Anchors an element to another element and realigns it when the window is resized.
1710          * @param {String/HTMLElement/Roo.Element} element The element to align to.
1711          * @param {String} position The position to align to.
1712          * @param {Array} offsets (optional) Offset the positioning by [x, y]
1713          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
1714          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
1715          * is a number, it is used as the buffer delay (defaults to 50ms).
1716          * @param {Function} callback The function to call after the animation finishes
1717          * @return {Roo.Element} this
1718          */
1719         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
1720             var action = function(){
1721                 this.alignTo(el, alignment, offsets, animate);
1722                 Roo.callback(callback, this);
1723             };
1724             Roo.EventManager.onWindowResize(action, this);
1725             var tm = typeof monitorScroll;
1726             if(tm != 'undefined'){
1727                 Roo.EventManager.on(window, 'scroll', action, this,
1728                     {buffer: tm == 'number' ? monitorScroll : 50});
1729             }
1730             action.call(this); // align immediately
1731             return this;
1732         },
1733         /**
1734          * Clears any opacity settings from this element. Required in some cases for IE.
1735          * @return {Roo.Element} this
1736          */
1737         clearOpacity : function(){
1738             if (window.ActiveXObject) {
1739                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
1740                     this.dom.style.filter = "";
1741                 }
1742             } else {
1743                 this.dom.style.opacity = "";
1744                 this.dom.style["-moz-opacity"] = "";
1745                 this.dom.style["-khtml-opacity"] = "";
1746             }
1747             return this;
1748         },
1749
1750         /**
1751          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
1752          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
1753          * @return {Roo.Element} this
1754          */
1755         hide : function(animate){
1756             this.setVisible(false, this.preanim(arguments, 0));
1757             return this;
1758         },
1759
1760         /**
1761         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
1762         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
1763          * @return {Roo.Element} this
1764          */
1765         show : function(animate){
1766             this.setVisible(true, this.preanim(arguments, 0));
1767             return this;
1768         },
1769
1770         /**
1771          * @private Test if size has a unit, otherwise appends the default
1772          */
1773         addUnits : function(size){
1774             return Roo.Element.addUnits(size, this.defaultUnit);
1775         },
1776
1777         /**
1778          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
1779          * @return {Roo.Element} this
1780          */
1781         beginMeasure : function(){
1782             var el = this.dom;
1783             if(el.offsetWidth || el.offsetHeight){
1784                 return this; // offsets work already
1785             }
1786             var changed = [];
1787             var p = this.dom, b = document.body; // start with this element
1788             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
1789                 var pe = Roo.get(p);
1790                 if(pe.getStyle('display') == 'none'){
1791                     changed.push({el: p, visibility: pe.getStyle("visibility")});
1792                     p.style.visibility = "hidden";
1793                     p.style.display = "block";
1794                 }
1795                 p = p.parentNode;
1796             }
1797             this._measureChanged = changed;
1798             return this;
1799
1800         },
1801
1802         /**
1803          * Restores displays to before beginMeasure was called
1804          * @return {Roo.Element} this
1805          */
1806         endMeasure : function(){
1807             var changed = this._measureChanged;
1808             if(changed){
1809                 for(var i = 0, len = changed.length; i < len; i++) {
1810                     var r = changed[i];
1811                     r.el.style.visibility = r.visibility;
1812                     r.el.style.display = "none";
1813                 }
1814                 this._measureChanged = null;
1815             }
1816             return this;
1817         },
1818
1819         /**
1820         * Update the innerHTML of this element, optionally searching for and processing scripts
1821         * @param {String} html The new HTML
1822         * @param {Boolean} loadScripts (optional) true to look for and process scripts
1823         * @param {Function} callback For async script loading you can be noticed when the update completes
1824         * @return {Roo.Element} this
1825          */
1826         update : function(html, loadScripts, callback){
1827             if(typeof html == "undefined"){
1828                 html = "";
1829             }
1830             if(loadScripts !== true){
1831                 this.dom.innerHTML = html;
1832                 if(typeof callback == "function"){
1833                     callback();
1834                 }
1835                 return this;
1836             }
1837             var id = Roo.id();
1838             var dom = this.dom;
1839
1840             html += '<span id="' + id + '"></span>';
1841
1842             E.onAvailable(id, function(){
1843                 var hd = document.getElementsByTagName("head")[0];
1844                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
1845                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
1846                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
1847
1848                 var match;
1849                 while(match = re.exec(html)){
1850                     var attrs = match[1];
1851                     var srcMatch = attrs ? attrs.match(srcRe) : false;
1852                     if(srcMatch && srcMatch[2]){
1853                        var s = document.createElement("script");
1854                        s.src = srcMatch[2];
1855                        var typeMatch = attrs.match(typeRe);
1856                        if(typeMatch && typeMatch[2]){
1857                            s.type = typeMatch[2];
1858                        }
1859                        hd.appendChild(s);
1860                     }else if(match[2] && match[2].length > 0){
1861                         if(window.execScript) {
1862                            window.execScript(match[2]);
1863                         } else {
1864                             /**
1865                              * eval:var:id
1866                              * eval:var:dom
1867                              * eval:var:html
1868                              * 
1869                              */
1870                            window.eval(match[2]);
1871                         }
1872                     }
1873                 }
1874                 var el = document.getElementById(id);
1875                 if(el){el.parentNode.removeChild(el);}
1876                 if(typeof callback == "function"){
1877                     callback();
1878                 }
1879             });
1880             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
1881             return this;
1882         },
1883
1884         /**
1885          * Direct access to the UpdateManager update() method (takes the same parameters).
1886          * @param {String/Function} url The url for this request or a function to call to get the url
1887          * @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}
1888          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
1889          * @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.
1890          * @return {Roo.Element} this
1891          */
1892         load : function(){
1893             var um = this.getUpdateManager();
1894             um.update.apply(um, arguments);
1895             return this;
1896         },
1897
1898         /**
1899         * Gets this element's UpdateManager
1900         * @return {Roo.UpdateManager} The UpdateManager
1901         */
1902         getUpdateManager : function(){
1903             if(!this.updateManager){
1904                 this.updateManager = new Roo.UpdateManager(this);
1905             }
1906             return this.updateManager;
1907         },
1908
1909         /**
1910          * Disables text selection for this element (normalized across browsers)
1911          * @return {Roo.Element} this
1912          */
1913         unselectable : function(){
1914             this.dom.unselectable = "on";
1915             this.swallowEvent("selectstart", true);
1916             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
1917             this.addClass("x-unselectable");
1918             return this;
1919         },
1920
1921         /**
1922         * Calculates the x, y to center this element on the screen
1923         * @return {Array} The x, y values [x, y]
1924         */
1925         getCenterXY : function(){
1926             return this.getAlignToXY(document, 'c-c');
1927         },
1928
1929         /**
1930         * Centers the Element in either the viewport, or another Element.
1931         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
1932         */
1933         center : function(centerIn){
1934             this.alignTo(centerIn || document, 'c-c');
1935             return this;
1936         },
1937
1938         /**
1939          * Tests various css rules/browsers to determine if this element uses a border box
1940          * @return {Boolean}
1941          */
1942         isBorderBox : function(){
1943             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
1944         },
1945
1946         /**
1947          * Return a box {x, y, width, height} that can be used to set another elements
1948          * size/location to match this element.
1949          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
1950          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
1951          * @return {Object} box An object in the format {x, y, width, height}
1952          */
1953         getBox : function(contentBox, local){
1954             var xy;
1955             if(!local){
1956                 xy = this.getXY();
1957             }else{
1958                 var left = parseInt(this.getStyle("left"), 10) || 0;
1959                 var top = parseInt(this.getStyle("top"), 10) || 0;
1960                 xy = [left, top];
1961             }
1962             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
1963             if(!contentBox){
1964                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
1965             }else{
1966                 var l = this.getBorderWidth("l")+this.getPadding("l");
1967                 var r = this.getBorderWidth("r")+this.getPadding("r");
1968                 var t = this.getBorderWidth("t")+this.getPadding("t");
1969                 var b = this.getBorderWidth("b")+this.getPadding("b");
1970                 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)};
1971             }
1972             bx.right = bx.x + bx.width;
1973             bx.bottom = bx.y + bx.height;
1974             return bx;
1975         },
1976
1977         /**
1978          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
1979          for more information about the sides.
1980          * @param {String} sides
1981          * @return {Number}
1982          */
1983         getFrameWidth : function(sides, onlyContentBox){
1984             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
1985         },
1986
1987         /**
1988          * 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.
1989          * @param {Object} box The box to fill {x, y, width, height}
1990          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
1991          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
1992          * @return {Roo.Element} this
1993          */
1994         setBox : function(box, adjust, animate){
1995             var w = box.width, h = box.height;
1996             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
1997                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
1998                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
1999             }
2000             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
2001             return this;
2002         },
2003
2004         /**
2005          * Forces the browser to repaint this element
2006          * @return {Roo.Element} this
2007          */
2008          repaint : function(){
2009             var dom = this.dom;
2010             this.addClass("x-repaint");
2011             setTimeout(function(){
2012                 Roo.get(dom).removeClass("x-repaint");
2013             }, 1);
2014             return this;
2015         },
2016
2017         /**
2018          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
2019          * then it returns the calculated width of the sides (see getPadding)
2020          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
2021          * @return {Object/Number}
2022          */
2023         getMargins : function(side){
2024             if(!side){
2025                 return {
2026                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
2027                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
2028                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
2029                     right: parseInt(this.getStyle("margin-right"), 10) || 0
2030                 };
2031             }else{
2032                 return this.addStyles(side, El.margins);
2033              }
2034         },
2035
2036         // private
2037         addStyles : function(sides, styles){
2038             var val = 0, v, w;
2039             for(var i = 0, len = sides.length; i < len; i++){
2040                 v = this.getStyle(styles[sides.charAt(i)]);
2041                 if(v){
2042                      w = parseInt(v, 10);
2043                      if(w){ val += w; }
2044                 }
2045             }
2046             return val;
2047         },
2048
2049         /**
2050          * Creates a proxy element of this element
2051          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
2052          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
2053          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
2054          * @return {Roo.Element} The new proxy element
2055          */
2056         createProxy : function(config, renderTo, matchBox){
2057             if(renderTo){
2058                 renderTo = Roo.getDom(renderTo);
2059             }else{
2060                 renderTo = document.body;
2061             }
2062             config = typeof config == "object" ?
2063                 config : {tag : "div", cls: config};
2064             var proxy = Roo.DomHelper.append(renderTo, config, true);
2065             if(matchBox){
2066                proxy.setBox(this.getBox());
2067             }
2068             return proxy;
2069         },
2070
2071         /**
2072          * Puts a mask over this element to disable user interaction. Requires core.css.
2073          * This method can only be applied to elements which accept child nodes.
2074          * @param {String} msg (optional) A message to display in the mask
2075          * @param {String} msgCls (optional) A css class to apply to the msg element
2076          * @return {Element} The mask  element
2077          */
2078         mask : function(msg, msgCls)
2079         {
2080             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
2081                 this.setStyle("position", "relative");
2082             }
2083             if(!this._mask){
2084                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
2085             }
2086             this.addClass("x-masked");
2087             this._mask.setDisplayed(true);
2088             
2089             // we wander
2090             var z = 0;
2091             var dom = this.dom;
2092             while (dom && dom.style) {
2093                 if (!isNaN(parseInt(dom.style.zIndex))) {
2094                     z = Math.max(z, parseInt(dom.style.zIndex));
2095                 }
2096                 dom = dom.parentNode;
2097             }
2098             // if we are masking the body - then it hides everything..
2099             if (this.dom == document.body) {
2100                 z = 1000000;
2101                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
2102                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
2103             }
2104            
2105             if(typeof msg == 'string'){
2106                 if(!this._maskMsg){
2107                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
2108                 }
2109                 var mm = this._maskMsg;
2110                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
2111                 if (mm.dom.firstChild) { // weird IE issue?
2112                     mm.dom.firstChild.innerHTML = msg;
2113                 }
2114                 mm.setDisplayed(true);
2115                 mm.center(this);
2116                 mm.setStyle('z-index', z + 102);
2117             }
2118             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
2119                 this._mask.setHeight(this.getHeight());
2120             }
2121             this._mask.setStyle('z-index', z + 100);
2122             
2123             return this._mask;
2124         },
2125
2126         /**
2127          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
2128          * it is cached for reuse.
2129          */
2130         unmask : function(removeEl){
2131             if(this._mask){
2132                 if(removeEl === true){
2133                     this._mask.remove();
2134                     delete this._mask;
2135                     if(this._maskMsg){
2136                         this._maskMsg.remove();
2137                         delete this._maskMsg;
2138                     }
2139                 }else{
2140                     this._mask.setDisplayed(false);
2141                     if(this._maskMsg){
2142                         this._maskMsg.setDisplayed(false);
2143                     }
2144                 }
2145             }
2146             this.removeClass("x-masked");
2147         },
2148
2149         /**
2150          * Returns true if this element is masked
2151          * @return {Boolean}
2152          */
2153         isMasked : function(){
2154             return this._mask && this._mask.isVisible();
2155         },
2156
2157         /**
2158          * Creates an iframe shim for this element to keep selects and other windowed objects from
2159          * showing through.
2160          * @return {Roo.Element} The new shim element
2161          */
2162         createShim : function(){
2163             var el = document.createElement('iframe');
2164             el.frameBorder = 'no';
2165             el.className = 'roo-shim';
2166             if(Roo.isIE && Roo.isSecure){
2167                 el.src = Roo.SSL_SECURE_URL;
2168             }
2169             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
2170             shim.autoBoxAdjust = false;
2171             return shim;
2172         },
2173
2174         /**
2175          * Removes this element from the DOM and deletes it from the cache
2176          */
2177         remove : function(){
2178             if(this.dom.parentNode){
2179                 this.dom.parentNode.removeChild(this.dom);
2180             }
2181             delete El.cache[this.dom.id];
2182         },
2183
2184         /**
2185          * Sets up event handlers to add and remove a css class when the mouse is over this element
2186          * @param {String} className
2187          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
2188          * mouseout events for children elements
2189          * @return {Roo.Element} this
2190          */
2191         addClassOnOver : function(className, preventFlicker){
2192             this.on("mouseover", function(){
2193                 Roo.fly(this, '_internal').addClass(className);
2194             }, this.dom);
2195             var removeFn = function(e){
2196                 if(preventFlicker !== true || !e.within(this, true)){
2197                     Roo.fly(this, '_internal').removeClass(className);
2198                 }
2199             };
2200             this.on("mouseout", removeFn, this.dom);
2201             return this;
2202         },
2203
2204         /**
2205          * Sets up event handlers to add and remove a css class when this element has the focus
2206          * @param {String} className
2207          * @return {Roo.Element} this
2208          */
2209         addClassOnFocus : function(className){
2210             this.on("focus", function(){
2211                 Roo.fly(this, '_internal').addClass(className);
2212             }, this.dom);
2213             this.on("blur", function(){
2214                 Roo.fly(this, '_internal').removeClass(className);
2215             }, this.dom);
2216             return this;
2217         },
2218         /**
2219          * 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)
2220          * @param {String} className
2221          * @return {Roo.Element} this
2222          */
2223         addClassOnClick : function(className){
2224             var dom = this.dom;
2225             this.on("mousedown", function(){
2226                 Roo.fly(dom, '_internal').addClass(className);
2227                 var d = Roo.get(document);
2228                 var fn = function(){
2229                     Roo.fly(dom, '_internal').removeClass(className);
2230                     d.removeListener("mouseup", fn);
2231                 };
2232                 d.on("mouseup", fn);
2233             });
2234             return this;
2235         },
2236
2237         /**
2238          * Stops the specified event from bubbling and optionally prevents the default action
2239          * @param {String} eventName
2240          * @param {Boolean} preventDefault (optional) true to prevent the default action too
2241          * @return {Roo.Element} this
2242          */
2243         swallowEvent : function(eventName, preventDefault){
2244             var fn = function(e){
2245                 e.stopPropagation();
2246                 if(preventDefault){
2247                     e.preventDefault();
2248                 }
2249             };
2250             if(eventName instanceof Array){
2251                 for(var i = 0, len = eventName.length; i < len; i++){
2252                      this.on(eventName[i], fn);
2253                 }
2254                 return this;
2255             }
2256             this.on(eventName, fn);
2257             return this;
2258         },
2259
2260         /**
2261          * @private
2262          */
2263       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
2264
2265         /**
2266          * Sizes this element to its parent element's dimensions performing
2267          * neccessary box adjustments.
2268          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
2269          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
2270          * @return {Roo.Element} this
2271          */
2272         fitToParent : function(monitorResize, targetParent) {
2273           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
2274           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
2275           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
2276             return;
2277           }
2278           var p = Roo.get(targetParent || this.dom.parentNode);
2279           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
2280           if (monitorResize === true) {
2281             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
2282             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
2283           }
2284           return this;
2285         },
2286
2287         /**
2288          * Gets the next sibling, skipping text nodes
2289          * @return {HTMLElement} The next sibling or null
2290          */
2291         getNextSibling : function(){
2292             var n = this.dom.nextSibling;
2293             while(n && n.nodeType != 1){
2294                 n = n.nextSibling;
2295             }
2296             return n;
2297         },
2298
2299         /**
2300          * Gets the previous sibling, skipping text nodes
2301          * @return {HTMLElement} The previous sibling or null
2302          */
2303         getPrevSibling : function(){
2304             var n = this.dom.previousSibling;
2305             while(n && n.nodeType != 1){
2306                 n = n.previousSibling;
2307             }
2308             return n;
2309         },
2310
2311
2312         /**
2313          * Appends the passed element(s) to this element
2314          * @param {String/HTMLElement/Array/Element/CompositeElement} el
2315          * @return {Roo.Element} this
2316          */
2317         appendChild: function(el){
2318             el = Roo.get(el);
2319             el.appendTo(this);
2320             return this;
2321         },
2322
2323         /**
2324          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
2325          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
2326          * automatically generated with the specified attributes.
2327          * @param {HTMLElement} insertBefore (optional) a child element of this element
2328          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
2329          * @return {Roo.Element} The new child element
2330          */
2331         createChild: function(config, insertBefore, returnDom){
2332             config = config || {tag:'div'};
2333             if(insertBefore){
2334                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
2335             }
2336             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
2337         },
2338
2339         /**
2340          * Appends this element to the passed element
2341          * @param {String/HTMLElement/Element} el The new parent element
2342          * @return {Roo.Element} this
2343          */
2344         appendTo: function(el){
2345             el = Roo.getDom(el);
2346             el.appendChild(this.dom);
2347             return this;
2348         },
2349
2350         /**
2351          * Inserts this element before the passed element in the DOM
2352          * @param {String/HTMLElement/Element} el The element to insert before
2353          * @return {Roo.Element} this
2354          */
2355         insertBefore: function(el){
2356             el = Roo.getDom(el);
2357             el.parentNode.insertBefore(this.dom, el);
2358             return this;
2359         },
2360
2361         /**
2362          * Inserts this element after the passed element in the DOM
2363          * @param {String/HTMLElement/Element} el The element to insert after
2364          * @return {Roo.Element} this
2365          */
2366         insertAfter: function(el){
2367             el = Roo.getDom(el);
2368             el.parentNode.insertBefore(this.dom, el.nextSibling);
2369             return this;
2370         },
2371
2372         /**
2373          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
2374          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
2375          * @return {Roo.Element} The new child
2376          */
2377         insertFirst: function(el, returnDom){
2378             el = el || {};
2379             if(typeof el == 'object' && !el.nodeType){ // dh config
2380                 return this.createChild(el, this.dom.firstChild, returnDom);
2381             }else{
2382                 el = Roo.getDom(el);
2383                 this.dom.insertBefore(el, this.dom.firstChild);
2384                 return !returnDom ? Roo.get(el) : el;
2385             }
2386         },
2387
2388         /**
2389          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
2390          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
2391          * @param {String} where (optional) 'before' or 'after' defaults to before
2392          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
2393          * @return {Roo.Element} the inserted Element
2394          */
2395         insertSibling: function(el, where, returnDom){
2396             where = where ? where.toLowerCase() : 'before';
2397             el = el || {};
2398             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
2399
2400             if(typeof el == 'object' && !el.nodeType){ // dh config
2401                 if(where == 'after' && !this.dom.nextSibling){
2402                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
2403                 }else{
2404                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
2405                 }
2406
2407             }else{
2408                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
2409                             where == 'before' ? this.dom : this.dom.nextSibling);
2410                 if(!returnDom){
2411                     rt = Roo.get(rt);
2412                 }
2413             }
2414             return rt;
2415         },
2416
2417         /**
2418          * Creates and wraps this element with another element
2419          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
2420          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
2421          * @return {HTMLElement/Element} The newly created wrapper element
2422          */
2423         wrap: function(config, returnDom){
2424             if(!config){
2425                 config = {tag: "div"};
2426             }
2427             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
2428             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
2429             return newEl;
2430         },
2431
2432         /**
2433          * Replaces the passed element with this element
2434          * @param {String/HTMLElement/Element} el The element to replace
2435          * @return {Roo.Element} this
2436          */
2437         replace: function(el){
2438             el = Roo.get(el);
2439             this.insertBefore(el);
2440             el.remove();
2441             return this;
2442         },
2443
2444         /**
2445          * Inserts an html fragment into this element
2446          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
2447          * @param {String} html The HTML fragment
2448          * @param {Boolean} returnEl True to return an Roo.Element
2449          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
2450          */
2451         insertHtml : function(where, html, returnEl){
2452             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
2453             return returnEl ? Roo.get(el) : el;
2454         },
2455
2456         /**
2457          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
2458          * @param {Object} o The object with the attributes
2459          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
2460          * @return {Roo.Element} this
2461          */
2462         set : function(o, useSet){
2463             var el = this.dom;
2464             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
2465             for(var attr in o){
2466                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
2467                 if(attr=="cls"){
2468                     el.className = o["cls"];
2469                 }else{
2470                     if(useSet) {
2471                         el.setAttribute(attr, o[attr]);
2472                     } else {
2473                         el[attr] = o[attr];
2474                     }
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