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