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