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