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