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