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