Upgrade to ExtJS 4.0.1 - Released 05/18/2011
[extjs.git] / src / core / src / dom / Element.style.js
1 /**
2  * @class Ext.core.Element
3  */
4 (function(){
5     Ext.core.Element.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>';
6     // local style camelizing for speed
7     var supports = Ext.supports,
8         view = document.defaultView,
9         opacityRe = /alpha\(opacity=(.*)\)/i,
10         trimRe = /^\s+|\s+$/g,
11         spacesRe = /\s+/,
12         wordsRe = /\w/g,
13         adjustDirect2DTableRe = /table-row|table-.*-group/,
14         INTERNAL = '_internal',
15         PADDING = 'padding',
16         MARGIN = 'margin',
17         BORDER = 'border',
18         LEFT = '-left',
19         RIGHT = '-right',
20         TOP = '-top',
21         BOTTOM = '-bottom',
22         WIDTH = '-width',
23         MATH = Math,
24         HIDDEN = 'hidden',
25         ISCLIPPED = 'isClipped',
26         OVERFLOW = 'overflow',
27         OVERFLOWX = 'overflow-x',
28         OVERFLOWY = 'overflow-y',
29         ORIGINALCLIP = 'originalClip',
30         // special markup used throughout Ext when box wrapping elements
31         borders = {l: BORDER + LEFT + WIDTH, r: BORDER + RIGHT + WIDTH, t: BORDER + TOP + WIDTH, b: BORDER + BOTTOM + WIDTH},
32         paddings = {l: PADDING + LEFT, r: PADDING + RIGHT, t: PADDING + TOP, b: PADDING + BOTTOM},
33         margins = {l: MARGIN + LEFT, r: MARGIN + RIGHT, t: MARGIN + TOP, b: MARGIN + BOTTOM},
34         data = Ext.core.Element.data;
35
36     Ext.override(Ext.core.Element, {
37         
38         /**
39          * TODO: Look at this
40          */
41         // private  ==> used by Fx
42         adjustWidth : function(width) {
43             var me = this,
44                 isNum = (typeof width == 'number');
45                 
46             if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
47                width -= (me.getBorderWidth("lr") + me.getPadding("lr"));
48             }
49             return (isNum && width < 0) ? 0 : width;
50         },
51
52         // private   ==> used by Fx
53         adjustHeight : function(height) {
54             var me = this,
55                 isNum = (typeof height == "number");
56                 
57             if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
58                height -= (me.getBorderWidth("tb") + me.getPadding("tb"));
59             }
60             return (isNum && height < 0) ? 0 : height;
61         },
62
63
64         /**
65          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
66          * @param {String/Array} className The CSS classes to add separated by space, or an array of classes
67          * @return {Ext.core.Element} this
68          */
69         addCls : function(className){
70             var me = this,
71                 cls = [],
72                 space = ((me.dom.className.replace(trimRe, '') == '') ? "" : " "),
73                 i, len, v;
74             if (!Ext.isDefined(className)) {
75                 return me;
76             }
77             // Separate case is for speed
78             if (!Ext.isArray(className)) {
79                 if (typeof className === 'string') {
80                     className = className.replace(trimRe, '').split(spacesRe);
81                     if (className.length === 1) {
82                         className = className[0];
83                         if (!me.hasCls(className)) {
84                             me.dom.className += space + className;
85                         }
86                     } else {
87                         this.addCls(className);
88                     }
89                 }
90             } else {
91                 for (i = 0, len = className.length; i < len; i++) {
92                     v = className[i];
93                     if (typeof v == 'string' && (' ' + me.dom.className + ' ').indexOf(' ' + v + ' ') == -1) {
94                         cls.push(v);
95                     }
96                 }
97                 if (cls.length) {
98                     me.dom.className += space + cls.join(" ");
99                 }
100             }
101             return me;
102         },
103
104         /**
105          * Removes one or more CSS classes from the element.
106          * @param {String/Array} className The CSS classes to remove separated by space, or an array of classes
107          * @return {Ext.core.Element} this
108          */
109         removeCls : function(className){
110             var me = this,
111                 i, idx, len, cls, elClasses;
112             if (!Ext.isDefined(className)) {
113                 return me;
114             }
115             if (!Ext.isArray(className)){
116                 className = className.replace(trimRe, '').split(spacesRe);
117             }
118             if (me.dom && me.dom.className) {
119                 elClasses = me.dom.className.replace(trimRe, '').split(spacesRe);
120                 for (i = 0, len = className.length; i < len; i++) {
121                     cls = className[i];
122                     if (typeof cls == 'string') {
123                         cls = cls.replace(trimRe, '');
124                         idx = Ext.Array.indexOf(elClasses, cls);
125                         if (idx != -1) {
126                             elClasses.splice(idx, 1);
127                         }
128                     }
129                 }
130                 me.dom.className = elClasses.join(" ");
131             }
132             return me;
133         },
134
135         /**
136          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
137          * @param {String/Array} className The CSS class to add, or an array of classes
138          * @return {Ext.core.Element} this
139          */
140         radioCls : function(className){
141             var cn = this.dom.parentNode.childNodes,
142                 v, i, len;
143             className = Ext.isArray(className) ? className : [className];
144             for (i = 0, len = cn.length; i < len; i++) {
145                 v = cn[i];
146                 if (v && v.nodeType == 1) {
147                     Ext.fly(v, '_internal').removeCls(className);
148                 }
149             }
150             return this.addCls(className);
151         },
152
153         /**
154          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
155          * @param {String} className The CSS class to toggle
156          * @return {Ext.core.Element} this
157          * @method
158          */
159         toggleCls : Ext.supports.ClassList ?
160             function(className) {
161                 this.dom.classList.toggle(Ext.String.trim(className));
162                 return this;
163             } :
164             function(className) {
165                 return this.hasCls(className) ? this.removeCls(className) : this.addCls(className);
166             },
167
168         /**
169          * Checks if the specified CSS class exists on this element's DOM node.
170          * @param {String} className The CSS class to check for
171          * @return {Boolean} True if the class exists, else false
172          * @method
173          */
174         hasCls : Ext.supports.ClassList ?
175             function(className) {
176                 if (!className) {
177                     return false;
178                 }
179                 className = className.split(spacesRe);
180                 var ln = className.length,
181                     i = 0;
182                 for (; i < ln; i++) {
183                     if (className[i] && this.dom.classList.contains(className[i])) {
184                         return true;
185                     }
186                 }
187                 return false;
188             } :
189             function(className){
190                 return className && (' ' + this.dom.className + ' ').indexOf(' ' + className + ' ') != -1;
191             },
192
193         /**
194          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
195          * @param {String} oldClassName The CSS class to replace
196          * @param {String} newClassName The replacement CSS class
197          * @return {Ext.core.Element} this
198          */
199         replaceCls : function(oldClassName, newClassName){
200             return this.removeCls(oldClassName).addCls(newClassName);
201         },
202
203         isStyle : function(style, val) {
204             return this.getStyle(style) == val;
205         },
206
207         /**
208          * Normalizes currentStyle and computedStyle.
209          * @param {String} property The style property whose value is returned.
210          * @return {String} The current value of the style property for this element.
211          * @method
212          */
213         getStyle : function(){
214             return view && view.getComputedStyle ?
215                 function(prop){
216                     var el = this.dom,
217                         v, cs, out, display, cleaner;
218
219                     if(el == document){
220                         return null;
221                     }
222                     prop = Ext.core.Element.normalize(prop);
223                     out = (v = el.style[prop]) ? v :
224                            (cs = view.getComputedStyle(el, "")) ? cs[prop] : null;
225                            
226                     // Ignore cases when the margin is correctly reported as 0, the bug only shows
227                     // numbers larger.
228                     if(prop == 'marginRight' && out != '0px' && !supports.RightMargin){
229                         cleaner = Ext.core.Element.getRightMarginFixCleaner(el);
230                         display = this.getStyle('display');
231                         el.style.display = 'inline-block';
232                         out = view.getComputedStyle(el, '').marginRight;
233                         el.style.display = display;
234                         cleaner();
235                     }
236                     
237                     if(prop == 'backgroundColor' && out == 'rgba(0, 0, 0, 0)' && !supports.TransparentColor){
238                         out = 'transparent';
239                     }
240                     return out;
241                 } :
242                 function(prop){
243                     var el = this.dom,
244                         m, cs;
245
246                     if (el == document) {
247                         return null;
248                     }
249                     
250                     if (prop == 'opacity') {
251                         if (el.style.filter.match) {
252                             m = el.style.filter.match(opacityRe);
253                             if(m){
254                                 var fv = parseFloat(m[1]);
255                                 if(!isNaN(fv)){
256                                     return fv ? fv / 100 : 0;
257                                 }
258                             }
259                         }
260                         return 1;
261                     }
262                     prop = Ext.core.Element.normalize(prop);
263                     return el.style[prop] || ((cs = el.currentStyle) ? cs[prop] : null);
264                 };
265         }(),
266
267         /**
268          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
269          * are convert to standard 6 digit hex color.
270          * @param {String} attr The css attribute
271          * @param {String} defaultValue The default value to use when a valid color isn't found
272          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
273          * color anims.
274          */
275         getColor : function(attr, defaultValue, prefix){
276             var v = this.getStyle(attr),
277                 color = prefix || prefix === '' ? prefix : '#',
278                 h;
279
280             if(!v || (/transparent|inherit/.test(v))) {
281                 return defaultValue;
282             }
283             if(/^r/.test(v)){
284                 Ext.each(v.slice(4, v.length -1).split(','), function(s){
285                     h = parseInt(s, 10);
286                     color += (h < 16 ? '0' : '') + h.toString(16);
287                 });
288             }else{
289                 v = v.replace('#', '');
290                 color += v.length == 3 ? v.replace(/^(\w)(\w)(\w)$/, '$1$1$2$2$3$3') : v;
291             }
292             return(color.length > 5 ? color.toLowerCase() : defaultValue);
293         },
294
295         /**
296          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
297          * @param {String/Object} property The style property to be set, or an object of multiple styles.
298          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
299          * @return {Ext.core.Element} this
300          */
301         setStyle : function(prop, value){
302             var me = this,
303                 tmp, style;
304
305             if (!me.dom) {
306                 return me;
307             }
308
309             if (!Ext.isObject(prop)) {
310                 tmp = {};
311                 tmp[prop] = value;
312                 prop = tmp;
313             }
314             for (style in prop) {
315                 if (prop.hasOwnProperty(style)) {
316                     value = Ext.value(prop[style], '');
317                     if (style == 'opacity') {
318                         me.setOpacity(value);
319                     }
320                     else {
321                         me.dom.style[Ext.core.Element.normalize(style)] = value;
322                     }
323                 }
324             }
325             return me;
326         },
327
328         /**
329          * Set the opacity of the element
330          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
331          * @param {Boolean/Object} animate (optional) a standard Element animation config object or <tt>true</tt> for
332          * the default animation (<tt>{duration: .35, easing: 'easeIn'}</tt>)
333          * @return {Ext.core.Element} this
334          */
335         setOpacity: function(opacity, animate) {
336             var me = this,
337                 dom = me.dom,
338                 val,
339                 style;
340
341             if (!me.dom) {
342                 return me;
343             }
344
345             style = me.dom.style;
346
347             if (!animate || !me.anim) {
348                 if (!Ext.supports.Opacity) {
349                     opacity = opacity < 1 ? 'alpha(opacity=' + opacity * 100 + ')': '';
350                     val = style.filter.replace(opacityRe, '').replace(trimRe, '');
351
352                     style.zoom = 1;
353                     style.filter = val + (val.length > 0 ? ' ': '') + opacity;
354                 }
355                 else {
356                     style.opacity = opacity;
357                 }
358             }
359             else {
360                 if (!Ext.isObject(animate)) {
361                     animate = {
362                         duration: 350,
363                         easing: 'ease-in'
364                     };
365                 }
366                 me.animate(Ext.applyIf({
367                     to: {
368                         opacity: opacity
369                     }
370                 },
371                 animate));
372             }
373             return me;
374         },
375
376
377         /**
378          * Clears any opacity settings from this element. Required in some cases for IE.
379          * @return {Ext.core.Element} this
380          */
381         clearOpacity : function(){
382             var style = this.dom.style;
383             if(!Ext.supports.Opacity){
384                 if(!Ext.isEmpty(style.filter)){
385                     style.filter = style.filter.replace(opacityRe, '').replace(trimRe, '');
386                 }
387             }else{
388                 style.opacity = style['-moz-opacity'] = style['-khtml-opacity'] = '';
389             }
390             return this;
391         },
392         
393         /**
394          * @private
395          * Returns 1 if the browser returns the subpixel dimension rounded to the lowest pixel.
396          * @return {Number} 0 or 1 
397          */
398         adjustDirect2DDimension: function(dimension) {
399             var me = this,
400                 dom = me.dom,
401                 display = me.getStyle('display'),
402                 inlineDisplay = dom.style['display'],
403                 inlinePosition = dom.style['position'],
404                 originIndex = dimension === 'width' ? 0 : 1,
405                 floating;
406                 
407             if (display === 'inline') {
408                 dom.style['display'] = 'inline-block';
409             }
410
411             dom.style['position'] = display.match(adjustDirect2DTableRe) ? 'absolute' : 'static';
412
413             // floating will contain digits that appears after the decimal point
414             // if height or width are set to auto we fallback to msTransformOrigin calculation
415             floating = (parseFloat(me.getStyle(dimension)) || parseFloat(dom.currentStyle.msTransformOrigin.split(' ')[originIndex]) * 2) % 1;
416             
417             dom.style['position'] = inlinePosition;
418             
419             if (display === 'inline') {
420                 dom.style['display'] = inlineDisplay;
421             }
422
423             return floating;
424         },
425         
426         /**
427          * Returns the offset height of the element
428          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
429          * @return {Number} The element's height
430          */
431         getHeight: function(contentHeight, preciseHeight) {
432             var me = this,
433                 dom = me.dom,
434                 hidden = Ext.isIE && me.isStyle('display', 'none'),
435                 height, overflow, style, floating;
436
437             // IE Quirks mode acts more like a max-size measurement unless overflow is hidden during measurement.
438             // We will put the overflow back to it's original value when we are done measuring.
439             if (Ext.isIEQuirks) {
440                 style = dom.style;
441                 overflow = style.overflow;
442                 me.setStyle({ overflow: 'hidden'});
443             }
444
445             height = dom.offsetHeight;
446
447             height = MATH.max(height, hidden ? 0 : dom.clientHeight) || 0;
448
449             // IE9 Direct2D dimension rounding bug
450             if (!hidden && Ext.supports.Direct2DBug) {
451                 floating = me.adjustDirect2DDimension('height');
452                 if (preciseHeight) {
453                     height += floating;
454                 }
455                 else if (floating > 0 && floating < 0.5) {
456                     height++;
457                 }
458             }
459
460             if (contentHeight) {
461                 height -= (me.getBorderWidth("tb") + me.getPadding("tb"));
462             }
463
464             if (Ext.isIEQuirks) {
465                 me.setStyle({ overflow: overflow});
466             }
467
468             if (height < 0) {
469                 height = 0;
470             }
471             return height;
472         },
473                 
474         /**
475          * Returns the offset width of the element
476          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
477          * @return {Number} The element's width
478          */
479         getWidth: function(contentWidth, preciseWidth) {
480             var me = this,
481                 dom = me.dom,
482                 hidden = Ext.isIE && me.isStyle('display', 'none'),
483                 rect, width, overflow, style, floating, parentPosition;
484
485             // IE Quirks mode acts more like a max-size measurement unless overflow is hidden during measurement.
486             // We will put the overflow back to it's original value when we are done measuring.
487             if (Ext.isIEQuirks) {
488                 style = dom.style;
489                 overflow = style.overflow;
490                 me.setStyle({overflow: 'hidden'});
491             }
492             
493             // Fix Opera 10.5x width calculation issues 
494             if (Ext.isOpera10_5) {
495                 if (dom.parentNode.currentStyle.position === 'relative') {
496                     parentPosition = dom.parentNode.style.position;
497                     dom.parentNode.style.position = 'static';
498                     width = dom.offsetWidth;
499                     dom.parentNode.style.position = parentPosition;
500                 }
501                 width = Math.max(width || 0, dom.offsetWidth);
502             
503             // Gecko will in some cases report an offsetWidth that is actually less than the width of the
504             // text contents, because it measures fonts with sub-pixel precision but rounds the calculated
505             // value down. Using getBoundingClientRect instead of offsetWidth allows us to get the precise
506             // subpixel measurements so we can force them to always be rounded up. See
507             // https://bugzilla.mozilla.org/show_bug.cgi?id=458617
508             } else if (Ext.supports.BoundingClientRect) {
509                 rect = dom.getBoundingClientRect();
510                 width = rect.right - rect.left;
511                 width = preciseWidth ? width : Math.ceil(width);
512             } else {
513                 width = dom.offsetWidth;
514             }
515
516             width = MATH.max(width, hidden ? 0 : dom.clientWidth) || 0;
517
518             // IE9 Direct2D dimension rounding bug
519             if (!hidden && Ext.supports.Direct2DBug) {
520                 floating = me.adjustDirect2DDimension('width');
521                 if (preciseWidth) {
522                     width += floating;
523                 }
524                 else if (floating > 0 && floating < 0.5) {
525                     width++;
526                 }
527             }
528             
529             if (contentWidth) {
530                 width -= (me.getBorderWidth("lr") + me.getPadding("lr"));
531             }
532             
533             if (Ext.isIEQuirks) {
534                 me.setStyle({ overflow: overflow});
535             }
536
537             if (width < 0) {
538                 width = 0;
539             }
540             return width;
541         },
542
543         /**
544          * Set the width of this Element.
545          * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
546          * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
547          * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
548          * </ul></div>
549          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
550          * @return {Ext.core.Element} this
551          */
552         setWidth : function(width, animate){
553             var me = this;
554             width = me.adjustWidth(width);
555             if (!animate || !me.anim) {
556                 me.dom.style.width = me.addUnits(width);
557             }
558             else {
559                 if (!Ext.isObject(animate)) {
560                     animate = {};
561                 }
562                 me.animate(Ext.applyIf({
563                     to: {
564                         width: width
565                     }
566                 }, animate));
567             }
568             return me;
569         },
570
571         /**
572          * Set the height of this Element.
573          * <pre><code>
574 // change the height to 200px and animate with default configuration
575 Ext.fly('elementId').setHeight(200, true);
576
577 // change the height to 150px and animate with a custom configuration
578 Ext.fly('elId').setHeight(150, {
579     duration : .5, // animation will have a duration of .5 seconds
580     // will change the content to "finished"
581     callback: function(){ this.{@link #update}("finished"); }
582 });
583          * </code></pre>
584          * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
585          * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels.)</li>
586          * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
587          * </ul></div>
588          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
589          * @return {Ext.core.Element} this
590          */
591          setHeight : function(height, animate){
592             var me = this;
593             height = me.adjustHeight(height);
594             if (!animate || !me.anim) {
595                 me.dom.style.height = me.addUnits(height);
596             }
597             else {
598                 if (!Ext.isObject(animate)) {
599                     animate = {};
600                 }
601                 me.animate(Ext.applyIf({
602                     to: {
603                         height: height
604                     }
605                 }, animate));
606             }
607             return me;
608         },
609
610         /**
611          * Gets the width of the border(s) for the specified side(s)
612          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
613          * passing <tt>'lr'</tt> would get the border <b><u>l</u></b>eft width + the border <b><u>r</u></b>ight width.
614          * @return {Number} The width of the sides passed added together
615          */
616         getBorderWidth : function(side){
617             return this.addStyles(side, borders);
618         },
619
620         /**
621          * Gets the width of the padding(s) for the specified side(s)
622          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
623          * passing <tt>'lr'</tt> would get the padding <b><u>l</u></b>eft + the padding <b><u>r</u></b>ight.
624          * @return {Number} The padding of the sides passed added together
625          */
626         getPadding : function(side){
627             return this.addStyles(side, paddings);
628         },
629
630         /**
631          *  Store the current overflow setting and clip overflow on the element - use <tt>{@link #unclip}</tt> to remove
632          * @return {Ext.core.Element} this
633          */
634         clip : function(){
635             var me = this,
636                 dom = me.dom;
637
638             if(!data(dom, ISCLIPPED)){
639                 data(dom, ISCLIPPED, true);
640                 data(dom, ORIGINALCLIP, {
641                     o: me.getStyle(OVERFLOW),
642                     x: me.getStyle(OVERFLOWX),
643                     y: me.getStyle(OVERFLOWY)
644                 });
645                 me.setStyle(OVERFLOW, HIDDEN);
646                 me.setStyle(OVERFLOWX, HIDDEN);
647                 me.setStyle(OVERFLOWY, HIDDEN);
648             }
649             return me;
650         },
651
652         /**
653          *  Return clipping (overflow) to original clipping before <tt>{@link #clip}</tt> was called
654          * @return {Ext.core.Element} this
655          */
656         unclip : function(){
657             var me = this,
658                 dom = me.dom,
659                 clip;
660
661             if(data(dom, ISCLIPPED)){
662                 data(dom, ISCLIPPED, false);
663                 clip = data(dom, ORIGINALCLIP);
664                 if(o.o){
665                     me.setStyle(OVERFLOW, o.o);
666                 }
667                 if(o.x){
668                     me.setStyle(OVERFLOWX, o.x);
669                 }
670                 if(o.y){
671                     me.setStyle(OVERFLOWY, o.y);
672                 }
673             }
674             return me;
675         },
676
677         // private
678         addStyles : function(sides, styles){
679             var totalSize = 0,
680                 sidesArr = sides.match(wordsRe),
681                 i = 0,
682                 len = sidesArr.length,
683                 side, size;
684             for (; i < len; i++) {
685                 side = sidesArr[i];
686                 size = side && parseInt(this.getStyle(styles[side]), 10);
687                 if (size) {
688                     totalSize += MATH.abs(size);
689                 }
690             }
691             return totalSize;
692         },
693
694         margins : margins,
695         
696         /**
697          * More flexible version of {@link #setStyle} for setting style properties.
698          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
699          * a function which returns such a specification.
700          * @return {Ext.core.Element} this
701          */
702         applyStyles : function(style){
703             Ext.core.DomHelper.applyStyles(this.dom, style);
704             return this;
705         },
706
707         /**
708          * Returns an object with properties matching the styles requested.
709          * For example, el.getStyles('color', 'font-size', 'width') might return
710          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
711          * @param {String} style1 A style name
712          * @param {String} style2 A style name
713          * @param {String} etc.
714          * @return {Object} The style object
715          */
716         getStyles : function(){
717             var styles = {},
718                 len = arguments.length,
719                 i = 0, style;
720                 
721             for(; i < len; ++i) {
722                 style = arguments[i];
723                 styles[style] = this.getStyle(style);
724             }
725             return styles;
726         },
727
728        /**
729         * <p>Wraps the specified element with a special 9 element markup/CSS block that renders by default as
730         * a gray container with a gradient background, rounded corners and a 4-way shadow.</p>
731         * <p>This special markup is used throughout Ext when box wrapping elements ({@link Ext.button.Button},
732         * {@link Ext.panel.Panel} when <tt>{@link Ext.panel.Panel#frame frame=true}</tt>, {@link Ext.window.Window}).  The markup
733         * is of this form:</p>
734         * <pre><code>
735     Ext.core.Element.boxMarkup =
736     &#39;&lt;div class="{0}-tl">&lt;div class="{0}-tr">&lt;div class="{0}-tc">&lt;/div>&lt;/div>&lt;/div>
737      &lt;div class="{0}-ml">&lt;div class="{0}-mr">&lt;div class="{0}-mc">&lt;/div>&lt;/div>&lt;/div>
738      &lt;div class="{0}-bl">&lt;div class="{0}-br">&lt;div class="{0}-bc">&lt;/div>&lt;/div>&lt;/div>&#39;;
739         * </code></pre>
740         * <p>Example usage:</p>
741         * <pre><code>
742     // Basic box wrap
743     Ext.get("foo").boxWrap();
744
745     // You can also add a custom class and use CSS inheritance rules to customize the box look.
746     // 'x-box-blue' is a built-in alternative -- look at the related CSS definitions as an example
747     // for how to create a custom box wrap style.
748     Ext.get("foo").boxWrap().addCls("x-box-blue");
749         * </code></pre>
750         * @param {String} class (optional) A base CSS class to apply to the containing wrapper element
751         * (defaults to <tt>'x-box'</tt>). Note that there are a number of CSS rules that are dependent on
752         * this name to make the overall effect work, so if you supply an alternate base class, make sure you
753         * also supply all of the necessary rules.
754         * @return {Ext.core.Element} The outermost wrapping element of the created box structure.
755         */
756         boxWrap : function(cls){
757             cls = cls || Ext.baseCSSPrefix + 'box';
758             var el = Ext.get(this.insertHtml("beforeBegin", "<div class='" + cls + "'>" + Ext.String.format(Ext.core.Element.boxMarkup, cls) + "</div>"));
759             Ext.DomQuery.selectNode('.' + cls + '-mc', el.dom).appendChild(this.dom);
760             return el;
761         },
762
763         /**
764          * Set the size of this Element. If animation is true, both width and height will be animated concurrently.
765          * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
766          * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
767          * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
768          * <li>A size object in the format <code>{width: widthValue, height: heightValue}</code>.</li>
769          * </ul></div>
770          * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
771          * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels).</li>
772          * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
773          * </ul></div>
774          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
775          * @return {Ext.core.Element} this
776          */
777         setSize : function(width, height, animate){
778             var me = this;
779             if (Ext.isObject(width)){ // in case of object from getSize()
780                 height = width.height;
781                 width = width.width;
782             }
783             width = me.adjustWidth(width);
784             height = me.adjustHeight(height);
785             if(!animate || !me.anim){
786                 me.dom.style.width = me.addUnits(width);
787                 me.dom.style.height = me.addUnits(height);
788             }
789             else {
790                 if (!Ext.isObject(animate)) {
791                     animate = {};
792                 }
793                 me.animate(Ext.applyIf({
794                     to: {
795                         width: width,
796                         height: height
797                     }
798                 }, animate));
799             }
800             return me;
801         },
802
803         /**
804          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
805          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
806          * if a height has not been set using CSS.
807          * @return {Number}
808          */
809         getComputedHeight : function(){
810             var me = this,
811                 h = Math.max(me.dom.offsetHeight, me.dom.clientHeight);
812             if(!h){
813                 h = parseFloat(me.getStyle('height')) || 0;
814                 if(!me.isBorderBox()){
815                     h += me.getFrameWidth('tb');
816                 }
817             }
818             return h;
819         },
820
821         /**
822          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
823          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
824          * if a width has not been set using CSS.
825          * @return {Number}
826          */
827         getComputedWidth : function(){
828             var me = this,
829                 w = Math.max(me.dom.offsetWidth, me.dom.clientWidth);
830                 
831             if(!w){
832                 w = parseFloat(me.getStyle('width')) || 0;
833                 if(!me.isBorderBox()){
834                     w += me.getFrameWidth('lr');
835                 }
836             }
837             return w;
838         },
839
840         /**
841          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
842          for more information about the sides.
843          * @param {String} sides
844          * @return {Number}
845          */
846         getFrameWidth : function(sides, onlyContentBox){
847             return onlyContentBox && this.isBorderBox() ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
848         },
849
850         /**
851          * Sets up event handlers to add and remove a css class when the mouse is over this element
852          * @param {String} className
853          * @return {Ext.core.Element} this
854          */
855         addClsOnOver : function(className){
856             var dom = this.dom;
857             this.hover(
858                 function(){
859                     Ext.fly(dom, INTERNAL).addCls(className);
860                 },
861                 function(){
862                     Ext.fly(dom, INTERNAL).removeCls(className);
863                 }
864             );
865             return this;
866         },
867
868         /**
869          * Sets up event handlers to add and remove a css class when this element has the focus
870          * @param {String} className
871          * @return {Ext.core.Element} this
872          */
873         addClsOnFocus : function(className){
874             var me = this,
875                 dom = me.dom;
876             me.on("focus", function(){
877                 Ext.fly(dom, INTERNAL).addCls(className);
878             });
879             me.on("blur", function(){
880                 Ext.fly(dom, INTERNAL).removeCls(className);
881             });
882             return me;
883         },
884
885         /**
886          * 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)
887          * @param {String} className
888          * @return {Ext.core.Element} this
889          */
890         addClsOnClick : function(className){
891             var dom = this.dom;
892             this.on("mousedown", function(){
893                 Ext.fly(dom, INTERNAL).addCls(className);
894                 var d = Ext.getDoc(),
895                     fn = function(){
896                         Ext.fly(dom, INTERNAL).removeCls(className);
897                         d.removeListener("mouseup", fn);
898                     };
899                 d.on("mouseup", fn);
900             });
901             return this;
902         },
903
904         /**
905          * <p>Returns the dimensions of the element available to lay content out in.<p>
906          * <p>If the element (or any ancestor element) has CSS style <code>display : none</code>, the dimensions will be zero.</p>
907          * example:<pre><code>
908         var vpSize = Ext.getBody().getViewSize();
909
910         // all Windows created afterwards will have a default value of 90% height and 95% width
911         Ext.Window.override({
912             width: vpSize.width * 0.9,
913             height: vpSize.height * 0.95
914         });
915         // To handle window resizing you would have to hook onto onWindowResize.
916         * </code></pre>
917         *
918         * getViewSize utilizes clientHeight/clientWidth which excludes sizing of scrollbars.
919         * To obtain the size including scrollbars, use getStyleSize
920         *
921         * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
922         */
923
924         getViewSize : function(){
925             var me = this,
926                 dom = me.dom,
927                 isDoc = (dom == Ext.getDoc().dom || dom == Ext.getBody().dom),
928                 style, overflow, ret;
929
930             // If the body, use static methods
931             if (isDoc) {
932                 ret = {
933                     width : Ext.core.Element.getViewWidth(),
934                     height : Ext.core.Element.getViewHeight()
935                 };
936
937             // Else use clientHeight/clientWidth
938             }
939             else {
940                 // IE 6 & IE Quirks mode acts more like a max-size measurement unless overflow is hidden during measurement.
941                 // We will put the overflow back to it's original value when we are done measuring.
942                 if (Ext.isIE6 || Ext.isIEQuirks) {
943                     style = dom.style;
944                     overflow = style.overflow;
945                     me.setStyle({ overflow: 'hidden'});
946                 }
947                 ret = {
948                     width : dom.clientWidth,
949                     height : dom.clientHeight
950                 };
951                 if (Ext.isIE6 || Ext.isIEQuirks) {
952                     me.setStyle({ overflow: overflow });
953                 }
954             }
955             return ret;
956         },
957
958         /**
959         * <p>Returns the dimensions of the element available to lay content out in.<p>
960         *
961         * getStyleSize utilizes prefers style sizing if present, otherwise it chooses the larger of offsetHeight/clientHeight and offsetWidth/clientWidth.
962         * To obtain the size excluding scrollbars, use getViewSize
963         *
964         * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
965         */
966
967         getStyleSize : function(){
968             var me = this,
969                 doc = document,
970                 d = this.dom,
971                 isDoc = (d == doc || d == doc.body),
972                 s = d.style,
973                 w, h;
974
975             // If the body, use static methods
976             if (isDoc) {
977                 return {
978                     width : Ext.core.Element.getViewWidth(),
979                     height : Ext.core.Element.getViewHeight()
980                 };
981             }
982             // Use Styles if they are set
983             if(s.width && s.width != 'auto'){
984                 w = parseFloat(s.width);
985                 if(me.isBorderBox()){
986                    w -= me.getFrameWidth('lr');
987                 }
988             }
989             // Use Styles if they are set
990             if(s.height && s.height != 'auto'){
991                 h = parseFloat(s.height);
992                 if(me.isBorderBox()){
993                    h -= me.getFrameWidth('tb');
994                 }
995             }
996             // Use getWidth/getHeight if style not set.
997             return {width: w || me.getWidth(true), height: h || me.getHeight(true)};
998         },
999
1000         /**
1001          * Returns the size of the element.
1002          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
1003          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
1004          */
1005         getSize : function(contentSize){
1006             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
1007         },
1008
1009         /**
1010          * Forces the browser to repaint this element
1011          * @return {Ext.core.Element} this
1012          */
1013         repaint : function(){
1014             var dom = this.dom;
1015             this.addCls(Ext.baseCSSPrefix + 'repaint');
1016             setTimeout(function(){
1017                 Ext.fly(dom).removeCls(Ext.baseCSSPrefix + 'repaint');
1018             }, 1);
1019             return this;
1020         },
1021
1022         /**
1023          * Disables text selection for this element (normalized across browsers)
1024          * @return {Ext.core.Element} this
1025          */
1026         unselectable : function(){
1027             var me = this;
1028             me.dom.unselectable = "on";
1029
1030             me.swallowEvent("selectstart", true);
1031             me.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
1032             me.addCls(Ext.baseCSSPrefix + 'unselectable');
1033             
1034             return me;
1035         },
1036
1037         /**
1038          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
1039          * then it returns the calculated width of the sides (see getPadding)
1040          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
1041          * @return {Object/Number}
1042          */
1043         getMargin : function(side){
1044             var me = this,
1045                 hash = {t:"top", l:"left", r:"right", b: "bottom"},
1046                 o = {},
1047                 key;
1048
1049             if (!side) {
1050                 for (key in me.margins){
1051                     o[hash[key]] = parseFloat(me.getStyle(me.margins[key])) || 0;
1052                 }
1053                 return o;
1054             } else {
1055                 return me.addStyles.call(me, side, me.margins);
1056             }
1057         }
1058     });
1059 })();