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