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