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