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