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