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