Remove .hyphen property from tests (left behind in refactoring)
[jquery.git] / src / css.js
blobad42a048ae6319b568f1a3f83cb2aed8234ac6cf
1 var curCSS, iframe, iframeDoc,
2         ralpha = /alpha\([^)]*\)/i,
3         ropacity = /opacity=([^)]*)/,
4         rposition = /^(top|right|bottom|left)$/,
5         // swappable if display is none or starts with table except "table", "table-cell", or "table-caption"
6         // see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
7         rdisplayswap = /^(none|table(?!-c[ea]).+)/,
8         rmargin = /^margin/,
9         rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ),
10         rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ),
11         rrelNum = new RegExp( "^([-+])=(" + core_pnum + ")", "i" ),
12         elemdisplay = { BODY: "block" },
14         cssShow = { position: "absolute", visibility: "hidden", display: "block" },
15         cssNormalTransform = {
16                 letterSpacing: 0,
17                 fontWeight: 400
18         },
20         cssExpand = [ "Top", "Right", "Bottom", "Left" ],
21         cssPrefixes = [ "Webkit", "O", "Moz", "ms" ];
23 // return a css property mapped to a potentially vendor prefixed property
24 function vendorPropName( style, name ) {
26         // shortcut for names that are not vendor prefixed
27         if ( name in style ) {
28                 return name;
29         }
31         // check for vendor prefixed names
32         var capName = name.charAt(0).toUpperCase() + name.slice(1),
33                 origName = name,
34                 i = cssPrefixes.length;
36         while ( i-- ) {
37                 name = cssPrefixes[ i ] + capName;
38                 if ( name in style ) {
39                         return name;
40                 }
41         }
43         return origName;
46 function isHidden( elem, el ) {
47         elem = el || elem;
48         return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem );
51 function showHide( elements, show ) {
52         var elem, display,
53                 values = [],
54                 index = 0,
55                 length = elements.length;
57         for ( ; index < length; index++ ) {
58                 elem = elements[ index ];
59                 if ( !elem.style ) {
60                         continue;
61                 }
62                 values[ index ] = jQuery._data( elem, "olddisplay" );
63                 if ( show ) {
64                         // Reset the inline display of this element to learn if it is
65                         // being hidden by cascaded rules or not
66                         if ( !values[ index ] && elem.style.display === "none" ) {
67                                 elem.style.display = "";
68                         }
70                         // Set elements which have been overridden with display: none
71                         // in a stylesheet to whatever the default browser style is
72                         // for such an element
73                         if ( elem.style.display === "" && isHidden( elem ) ) {
74                                 values[ index ] = jQuery._data( elem, "olddisplay", css_defaultDisplay(elem.nodeName) );
75                         }
76                 } else {
77                         display = curCSS( elem, "display" );
79                         if ( !values[ index ] && display !== "none" ) {
80                                 jQuery._data( elem, "olddisplay", display );
81                         }
82                 }
83         }
85         // Set the display of most of the elements in a second loop
86         // to avoid the constant reflow
87         for ( index = 0; index < length; index++ ) {
88                 elem = elements[ index ];
89                 if ( !elem.style ) {
90                         continue;
91                 }
92                 if ( !show || elem.style.display === "none" || elem.style.display === "" ) {
93                         elem.style.display = show ? values[ index ] || "" : "none";
94                 }
95         }
97         return elements;
100 jQuery.fn.extend({
101         css: function( name, value ) {
102                 return jQuery.access( this, function( elem, name, value ) {
103                         return value !== undefined ?
104                                 jQuery.style( elem, name, value ) :
105                                 jQuery.css( elem, name );
106                 }, name, value, arguments.length > 1 );
107         },
108         show: function() {
109                 return showHide( this, true );
110         },
111         hide: function() {
112                 return showHide( this );
113         },
114         toggle: function( state ) {
115                 var bool = typeof state === "boolean";
117                 return this.each(function() {
118                         if ( bool ? state : isHidden( this ) ) {
119                                 jQuery( this ).show();
120                         } else {
121                                 jQuery( this ).hide();
122                         }
123                 });
124         }
127 jQuery.extend({
128         // Add in style property hooks for overriding the default
129         // behavior of getting and setting a style property
130         cssHooks: {
131                 opacity: {
132                         get: function( elem, computed ) {
133                                 if ( computed ) {
134                                         // We should always get a number back from opacity
135                                         var ret = curCSS( elem, "opacity" );
136                                         return ret === "" ? "1" : ret;
138                                 }
139                         }
140                 }
141         },
143         // Exclude the following css properties to add px
144         cssNumber: {
145                 "fillOpacity": true,
146                 "fontWeight": true,
147                 "lineHeight": true,
148                 "opacity": true,
149                 "orphans": true,
150                 "widows": true,
151                 "zIndex": true,
152                 "zoom": true
153         },
155         // Add in properties whose names you wish to fix before
156         // setting or getting the value
157         cssProps: {
158                 // normalize float css property
159                 "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat"
160         },
162         // Get and set the style property on a DOM Node
163         style: function( elem, name, value, extra ) {
164                 // Don't set styles on text and comment nodes
165                 if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
166                         return;
167                 }
169                 // Make sure that we're working with the right name
170                 var ret, type, hooks,
171                         origName = jQuery.camelCase( name ),
172                         style = elem.style;
174                 name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) );
176                 // gets hook for the prefixed version
177                 // followed by the unprefixed version
178                 hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
180                 // Check if we're setting a value
181                 if ( value !== undefined ) {
182                         type = typeof value;
184                         // convert relative number strings (+= or -=) to relative numbers. #7345
185                         if ( type === "string" && (ret = rrelNum.exec( value )) ) {
186                                 value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) );
187                                 // Fixes bug #9237
188                                 type = "number";
189                         }
191                         // Make sure that NaN and null values aren't set. See: #7116
192                         if ( value == null || type === "number" && isNaN( value ) ) {
193                                 return;
194                         }
196                         // If a number was passed in, add 'px' to the (except for certain CSS properties)
197                         if ( type === "number" && !jQuery.cssNumber[ origName ] ) {
198                                 value += "px";
199                         }
201                         // If a hook was provided, use that value, otherwise just set the specified value
202                         if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) {
203                                 // Wrapped to prevent IE from throwing errors when 'invalid' values are provided
204                                 // Fixes bug #5509
205                                 try {
206                                         style[ name ] = value;
207                                 } catch(e) {}
208                         }
210                 } else {
211                         // If a hook was provided get the non-computed value from there
212                         if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
213                                 return ret;
214                         }
216                         // Otherwise just get the value from the style object
217                         return style[ name ];
218                 }
219         },
221         css: function( elem, name, numeric, extra ) {
222                 var val, num, hooks,
223                         origName = jQuery.camelCase( name );
225                 // Make sure that we're working with the right name
226                 name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) );
228                 // gets hook for the prefixed version
229                 // followed by the unprefixed version
230                 hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
232                 // If a hook was provided get the computed value from there
233                 if ( hooks && "get" in hooks ) {
234                         val = hooks.get( elem, true, extra );
235                 }
237                 // Otherwise, if a way to get the computed value exists, use that
238                 if ( val === undefined ) {
239                         val = curCSS( elem, name );
240                 }
242                 //convert "normal" to computed value
243                 if ( val === "normal" && name in cssNormalTransform ) {
244                         val = cssNormalTransform[ name ];
245                 }
247                 // Return, converting to number if forced or a qualifier was provided and val looks numeric
248                 if ( numeric || extra !== undefined ) {
249                         num = parseFloat( val );
250                         return numeric || jQuery.isNumeric( num ) ? num || 0 : val;
251                 }
252                 return val;
253         },
255         // A method for quickly swapping in/out CSS properties to get correct calculations
256         swap: function( elem, options, callback ) {
257                 var ret, name,
258                         old = {};
260                 // Remember the old values, and insert the new ones
261                 for ( name in options ) {
262                         old[ name ] = elem.style[ name ];
263                         elem.style[ name ] = options[ name ];
264                 }
266                 ret = callback.call( elem );
268                 // Revert the old values
269                 for ( name in options ) {
270                         elem.style[ name ] = old[ name ];
271                 }
273                 return ret;
274         }
277 // NOTE: To any future maintainer, we've window.getComputedStyle
278 // because jsdom on node.js will break without it.
279 if ( window.getComputedStyle ) {
280         curCSS = function( elem, name ) {
281                 var ret, width, minWidth, maxWidth,
282                         computed = window.getComputedStyle( elem, null ),
283                         style = elem.style;
285                 if ( computed ) {
287                         // getPropertyValue is only needed for .css('filter') in IE9, see #12537
288                         ret = computed.getPropertyValue( name ) || computed[ name ];
290                         if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) {
291                                 ret = jQuery.style( elem, name );
292                         }
294                         // A tribute to the "awesome hack by Dean Edwards"
295                         // Chrome < 17 and Safari 5.0 uses "computed value" instead of "used value" for margin-right
296                         // Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels
297                         // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values
298                         if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) {
299                                 width = style.width;
300                                 minWidth = style.minWidth;
301                                 maxWidth = style.maxWidth;
303                                 style.minWidth = style.maxWidth = style.width = ret;
304                                 ret = computed.width;
306                                 style.width = width;
307                                 style.minWidth = minWidth;
308                                 style.maxWidth = maxWidth;
309                         }
310                 }
312                 return ret;
313         };
314 } else if ( document.documentElement.currentStyle ) {
315         curCSS = function( elem, name ) {
316                 var left, rsLeft,
317                         ret = elem.currentStyle && elem.currentStyle[ name ],
318                         style = elem.style;
320                 // Avoid setting ret to empty string here
321                 // so we don't default to auto
322                 if ( ret == null && style && style[ name ] ) {
323                         ret = style[ name ];
324                 }
326                 // From the awesome hack by Dean Edwards
327                 // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
329                 // If we're not dealing with a regular pixel number
330                 // but a number that has a weird ending, we need to convert it to pixels
331                 // but not position css attributes, as those are proportional to the parent element instead
332                 // and we can't measure the parent instead because it might trigger a "stacking dolls" problem
333                 if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) {
335                         // Remember the original values
336                         left = style.left;
337                         rsLeft = elem.runtimeStyle && elem.runtimeStyle.left;
339                         // Put in the new values to get a computed value out
340                         if ( rsLeft ) {
341                                 elem.runtimeStyle.left = elem.currentStyle.left;
342                         }
343                         style.left = name === "fontSize" ? "1em" : ret;
344                         ret = style.pixelLeft + "px";
346                         // Revert the changed values
347                         style.left = left;
348                         if ( rsLeft ) {
349                                 elem.runtimeStyle.left = rsLeft;
350                         }
351                 }
353                 return ret === "" ? "auto" : ret;
354         };
357 function setPositiveNumber( elem, value, subtract ) {
358         var matches = rnumsplit.exec( value );
359         return matches ?
360                         Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) :
361                         value;
364 function augmentWidthOrHeight( elem, name, extra, isBorderBox ) {
365         var i = extra === ( isBorderBox ? "border" : "content" ) ?
366                 // If we already have the right measurement, avoid augmentation
367                 4 :
368                 // Otherwise initialize for horizontal or vertical properties
369                 name === "width" ? 1 : 0,
371                 val = 0;
373         for ( ; i < 4; i += 2 ) {
374                 // both box models exclude margin, so add it if we want it
375                 if ( extra === "margin" ) {
376                         // we use jQuery.css instead of curCSS here
377                         // because of the reliableMarginRight CSS hook!
378                         val += jQuery.css( elem, extra + cssExpand[ i ], true );
379                 }
381                 // From this point on we use curCSS for maximum performance (relevant in animations)
382                 if ( isBorderBox ) {
383                         // border-box includes padding, so remove it if we want content
384                         if ( extra === "content" ) {
385                                 val -= parseFloat( curCSS( elem, "padding" + cssExpand[ i ] ) ) || 0;
386                         }
388                         // at this point, extra isn't border nor margin, so remove border
389                         if ( extra !== "margin" ) {
390                                 val -= parseFloat( curCSS( elem, "border" + cssExpand[ i ] + "Width" ) ) || 0;
391                         }
392                 } else {
393                         // at this point, extra isn't content, so add padding
394                         val += parseFloat( curCSS( elem, "padding" + cssExpand[ i ] ) ) || 0;
396                         // at this point, extra isn't content nor padding, so add border
397                         if ( extra !== "padding" ) {
398                                 val += parseFloat( curCSS( elem, "border" + cssExpand[ i ] + "Width" ) ) || 0;
399                         }
400                 }
401         }
403         return val;
406 function getWidthOrHeight( elem, name, extra ) {
408         // Start with offset property, which is equivalent to the border-box value
409         var val = name === "width" ? elem.offsetWidth : elem.offsetHeight,
410                 valueIsBorderBox = true,
411                 isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing" ) === "border-box";
413         // some non-html elements return undefined for offsetWidth, so check for null/undefined
414         // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285
415         // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668
416         if ( val <= 0 || val == null ) {
417                 // Fall back to computed then uncomputed css if necessary
418                 val = curCSS( elem, name );
419                 if ( val < 0 || val == null ) {
420                         val = elem.style[ name ];
421                 }
423                 // Computed unit is not pixels. Stop here and return.
424                 if ( rnumnonpx.test(val) ) {
425                         return val;
426                 }
428                 // we need the check for style in case a browser which returns unreliable values
429                 // for getComputedStyle silently falls back to the reliable elem.style
430                 valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] );
432                 // Normalize "", auto, and prepare for extra
433                 val = parseFloat( val ) || 0;
434         }
436         // use the active box-sizing model to add/subtract irrelevant styles
437         return ( val +
438                 augmentWidthOrHeight(
439                         elem,
440                         name,
441                         extra || ( isBorderBox ? "border" : "content" ),
442                         valueIsBorderBox
443                 )
444         ) + "px";
448 // Try to determine the default display value of an element
449 function css_defaultDisplay( nodeName ) {
450         if ( elemdisplay[ nodeName ] ) {
451                 return elemdisplay[ nodeName ];
452         }
454         var elem = jQuery( "<" + nodeName + ">" ).appendTo( document.body ),
455                 display = elem.css("display");
456         elem.remove();
458         // If the simple way fails,
459         // get element's real default display by attaching it to a temp iframe
460         if ( display === "none" || display === "" ) {
461                 // Use the already-created iframe if possible
462                 iframe = document.body.appendChild(
463                         iframe || jQuery.extend( document.createElement("iframe"), {
464                                 frameBorder: 0,
465                                 width: 0,
466                                 height: 0
467                         })
468                 );
470                 // Create a cacheable copy of the iframe document on first call.
471                 // IE and Opera will allow us to reuse the iframeDoc without re-writing the fake HTML
472                 // document to it; WebKit & Firefox won't allow reusing the iframe document.
473                 if ( !iframeDoc || !iframe.createElement ) {
474                         iframeDoc = ( iframe.contentWindow || iframe.contentDocument ).document;
475                         iframeDoc.write("<!doctype html><html><body>");
476                         iframeDoc.close();
477                 }
479                 elem = iframeDoc.body.appendChild( iframeDoc.createElement(nodeName) );
481                 display = curCSS( elem, "display" );
482                 document.body.removeChild( iframe );
483         }
485         // Store the correct default display
486         elemdisplay[ nodeName ] = display;
488         return display;
491 jQuery.each([ "height", "width" ], function( i, name ) {
492         jQuery.cssHooks[ name ] = {
493                 get: function( elem, computed, extra ) {
494                         if ( computed ) {
495                                 // certain elements can have dimension info if we invisibly show them
496                                 // however, it must have a current display style that would benefit from this
497                                 if ( elem.offsetWidth === 0 && rdisplayswap.test( curCSS( elem, "display" ) ) ) {
498                                         return jQuery.swap( elem, cssShow, function() {
499                                                 return getWidthOrHeight( elem, name, extra );
500                                         });
501                                 } else {
502                                         return getWidthOrHeight( elem, name, extra );
503                                 }
504                         }
505                 },
507                 set: function( elem, value, extra ) {
508                         return setPositiveNumber( elem, value, extra ?
509                                 augmentWidthOrHeight(
510                                         elem,
511                                         name,
512                                         extra,
513                                         jQuery.support.boxSizing && jQuery.css( elem, "boxSizing" ) === "border-box"
514                                 ) : 0
515                         );
516                 }
517         };
520 if ( !jQuery.support.opacity ) {
521         jQuery.cssHooks.opacity = {
522                 get: function( elem, computed ) {
523                         // IE uses filters for opacity
524                         return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "" ) ?
525                                 ( 0.01 * parseFloat( RegExp.$1 ) ) + "" :
526                                 computed ? "1" : "";
527                 },
529                 set: function( elem, value ) {
530                         var style = elem.style,
531                                 currentStyle = elem.currentStyle,
532                                 opacity = jQuery.isNumeric( value ) ? "alpha(opacity=" + value * 100 + ")" : "",
533                                 filter = currentStyle && currentStyle.filter || style.filter || "";
535                         // IE has trouble with opacity if it does not have layout
536                         // Force it by setting the zoom level
537                         style.zoom = 1;
539                         // if setting opacity to 1, and no other filters exist - attempt to remove filter attribute #6652
540                         if ( value >= 1 && jQuery.trim( filter.replace( ralpha, "" ) ) === "" &&
541                                 style.removeAttribute ) {
543                                 // Setting style.filter to null, "" & " " still leave "filter:" in the cssText
544                                 // if "filter:" is present at all, clearType is disabled, we want to avoid this
545                                 // style.removeAttribute is IE Only, but so apparently is this code path...
546                                 style.removeAttribute( "filter" );
548                                 // if there there is no filter style applied in a css rule, we are done
549                                 if ( currentStyle && !currentStyle.filter ) {
550                                         return;
551                                 }
552                         }
554                         // otherwise, set new filter values
555                         style.filter = ralpha.test( filter ) ?
556                                 filter.replace( ralpha, opacity ) :
557                                 filter + " " + opacity;
558                 }
559         };
562 // These hooks cannot be added until DOM ready because the support test
563 // for it is not run until after DOM ready
564 jQuery(function() {
565         if ( !jQuery.support.reliableMarginRight ) {
566                 jQuery.cssHooks.marginRight = {
567                         get: function( elem, computed ) {
568                                 // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
569                                 // Work around by temporarily setting element display to inline-block
570                                 return jQuery.swap( elem, { "display": "inline-block" }, function() {
571                                         if ( computed ) {
572                                                 return curCSS( elem, "marginRight" );
573                                         }
574                                 });
575                         }
576                 };
577         }
579         // Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
580         // getComputedStyle returns percent when specified for top/left/bottom/right
581         // rather than make the css module depend on the offset module, we just check for it here
582         if ( !jQuery.support.pixelPosition && jQuery.fn.position ) {
583                 jQuery.each( [ "top", "left" ], function( i, prop ) {
584                         jQuery.cssHooks[ prop ] = {
585                                 get: function( elem, computed ) {
586                                         if ( computed ) {
587                                                 var ret = curCSS( elem, prop );
588                                                 // if curCSS returns percentage, fallback to offset
589                                                 return rnumnonpx.test( ret ) ? jQuery( elem ).position()[ prop ] + "px" : ret;
590                                         }
591                                 }
592                         };
593                 });
594         }
598 if ( jQuery.expr && jQuery.expr.filters ) {
599         jQuery.expr.filters.hidden = function( elem ) {
600                 return ( elem.offsetWidth === 0 && elem.offsetHeight === 0 ) || (!jQuery.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || curCSS( elem, "display" )) === "none");
601         };
603         jQuery.expr.filters.visible = function( elem ) {
604                 return !jQuery.expr.filters.hidden( elem );
605         };
608 // These hooks are used by animate to expand properties
609 jQuery.each({
610         margin: "",
611         padding: "",
612         border: "Width"
613 }, function( prefix, suffix ) {
614         jQuery.cssHooks[ prefix + suffix ] = {
615                 expand: function( value ) {
616                         var i,
618                                 // assumes a single number if not a string
619                                 parts = typeof value === "string" ? value.split(" ") : [ value ],
620                                 expanded = {};
622                         for ( i = 0; i < 4; i++ ) {
623                                 expanded[ prefix + cssExpand[ i ] + suffix ] =
624                                         parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
625                         }
627                         return expanded;
628                 }
629         };
631         if ( !rmargin.test( prefix ) ) {
632                 jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
633         }