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