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