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