decoupling styles retrieval from the attr method
[jquery.git] / src / css.js
1 // exclude the following css properties to add px
2 var exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i,
3         // cache defaultView
4         defaultView = document.defaultView || {},
5         // normalize float css property
6         styleFloat = jQuery.support.cssFloat ? "cssFloat" : "styleFloat";
7
8 jQuery.fn.css = function( name, value ) {
9         var options = name, isFunction = jQuery.isFunction( value );
10
11         // ignore negative width and height values
12         if ( (name == 'width' || name == 'height') && parseFloat(value) < 0 )
13                 value = undefined;
14
15         if ( typeof name === "string" ) {
16                 // Are we setting the style?
17                 if ( value === undefined ) {
18                         return this.length ?
19                                 jQuery.css( this[0], name ) :
20                                 null;
21
22                 // Convert name, value params to options hash format
23                 } else {
24                         options = {};
25                         options[ name ] = value;
26                 }
27         }
28
29         // For each element...
30         for ( var i = 0, l = this.length; i < l; i++ ) {
31                 var elem = this[i];
32
33                 // Set all the styles
34                 for ( var prop in options ) {
35                         value = options[prop];
36
37                         if ( isFunction ) {
38                                 value = value.call( elem, i );
39                         }
40
41                         if ( typeof value === "number" && !exclude.test(prop) ) {
42                                 value = value + "px";
43                         }
44
45                         jQuery.style( elem, prop, value );
46                 }
47         }
48
49         return this;
50 };
51
52 jQuery.extend({
53         style: function( elem, name, value ) {
54                 // don't set styles on text and comment nodes
55                 if (!elem || elem.nodeType == 3 || elem.nodeType == 8)
56                         return undefined;
57
58                 var style = elem.style || elem, set = value !== undefined;
59
60                 // IE uses filters for opacity
61                 if ( !jQuery.support.opacity && name == "opacity" ) {
62                         if ( set ) {
63                                 // IE has trouble with opacity if it does not have layout
64                                 // Force it by setting the zoom level
65                                 style.zoom = 1;
66
67                                 // Set the alpha filter to set the opacity
68                                 style.filter = (style.filter || "").replace( /alpha\([^)]*\)/, "" ) +
69                                         (parseInt( value ) + '' == "NaN" ? "" : "alpha(opacity=" + value * 100 + ")");
70                         }
71
72                         return style.filter && style.filter.indexOf("opacity=") >= 0 ?
73                                 (parseFloat( style.filter.match(/opacity=([^)]*)/)[1] ) / 100) + '':
74                                 "";
75                 }
76
77                 // Make sure we're using the right name for getting the float value
78                 if ( /float/i.test( name ) )
79                         name = styleFloat;
80
81                 name = name.replace(/-([a-z])/ig, function(all, letter){
82                         return letter.toUpperCase();
83                 });
84
85                 if ( set )
86                         style[ name ] = value;
87
88                 return style[ name ];
89         },
90
91         css: function( elem, name, force, extra ) {
92                 if ( name == "width" || name == "height" ) {
93                         var val, props = { position: "absolute", visibility: "hidden", display:"block" }, which = name == "width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ];
94
95                         function getWH() {
96                                 val = name == "width" ? elem.offsetWidth : elem.offsetHeight;
97
98                                 if ( extra === "border" )
99                                         return;
100
101                                 jQuery.each( which, function() {
102                                         if ( !extra )
103                                                 val -= parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0;
104                                         if ( extra === "margin" )
105                                                 val += parseFloat(jQuery.curCSS( elem, "margin" + this, true)) || 0;
106                                         else
107                                                 val -= parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0;
108                                 });
109                         }
110
111                         if ( elem.offsetWidth !== 0 )
112                                 getWH();
113                         else
114                                 jQuery.swap( elem, props, getWH );
115
116                         return Math.max(0, Math.round(val));
117                 }
118
119                 return jQuery.curCSS( elem, name, force );
120         },
121
122         curCSS: function( elem, name, force ) {
123                 var ret, style = elem.style;
124
125                 // IE uses filters for opacity
126                 if ( !jQuery.support.opacity && name == "opacity" ) {
127                         ret = style.filter && style.filter.indexOf("opacity=") >= 0 ?
128                                 (parseFloat( style.filter.match(/opacity=([^)]*)/)[1] ) / 100) + '':
129                                 "";
130
131                         return ret === "" ?
132                                 "1" :
133                                 ret;
134                 }
135
136                 // Make sure we're using the right name for getting the float value
137                 if ( /float/i.test( name ) )
138                         name = styleFloat;
139
140                 if ( !force && style && style[ name ] ) {
141                         ret = style[ name ];
142
143                 } else if ( defaultView.getComputedStyle ) {
144
145                         // Only "float" is needed here
146                         if ( /float/i.test( name ) )
147                                 name = "float";
148
149                         name = name.replace( /([A-Z])/g, "-$1" ).toLowerCase();
150
151                         var computedStyle = defaultView.getComputedStyle( elem, null );
152
153                         if ( computedStyle )
154                                 ret = computedStyle.getPropertyValue( name );
155
156                         // We should always get a number back from opacity
157                         if ( name == "opacity" && ret == "" )
158                                 ret = "1";
159
160                 } else if ( elem.currentStyle ) {
161                         var camelCase = name.replace(/\-(\w)/g, function(all, letter){
162                                 return letter.toUpperCase();
163                         });
164
165                         ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ];
166
167                         // From the awesome hack by Dean Edwards
168                         // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
169
170                         // If we're not dealing with a regular pixel number
171                         // but a number that has a weird ending, we need to convert it to pixels
172                         if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) {
173                                 // Remember the original values
174                                 var left = style.left, rsLeft = elem.runtimeStyle.left;
175
176                                 // Put in the new values to get a computed value out
177                                 elem.runtimeStyle.left = elem.currentStyle.left;
178                                 style.left = ret || 0;
179                                 ret = style.pixelLeft + "px";
180
181                                 // Revert the changed values
182                                 style.left = left;
183                                 elem.runtimeStyle.left = rsLeft;
184                         }
185                 }
186
187                 return ret;
188         },
189
190         // A method for quickly swapping in/out CSS properties to get correct calculations
191         swap: function( elem, options, callback ) {
192                 var old = {};
193                 // Remember the old values, and insert the new ones
194                 for ( var name in options ) {
195                         old[ name ] = elem.style[ name ];
196                         elem.style[ name ] = options[ name ];
197                 }
198
199                 callback.call( elem );
200
201                 // Revert the old values
202                 for ( var name in options )
203                         elem.style[ name ] = old[ name ];
204         }
205 });