diff --git a/src/css.js b/src/css.js index 7d5abe975..4d4e2dc82 100644 --- a/src/css.js +++ b/src/css.js @@ -10,8 +10,10 @@ var ralpha = /alpha\([^)]*\)/i, rmargin = /^margin/, cssShow = { position: "absolute", visibility: "hidden", display: "block" }, - cssWidth = [ "Left", "Right" ], - cssHeight = [ "Top", "Bottom" ], + + // order is important! + cssExpand = [ "Top", "Right", "Bottom", "Left" ], + curCSS, getComputedStyle, @@ -169,10 +171,10 @@ jQuery.each(["height", "width"], function( i, name ) { get: function( elem, computed, extra ) { if ( computed ) { if ( elem.offsetWidth !== 0 ) { - return getWH( elem, name, extra ); + return getWidthOrHeight( elem, name, extra ); } else { return jQuery.swap( elem, cssShow, function() { - return getWH( elem, name, extra ); + return getWidthOrHeight( elem, name, extra ); }); } } @@ -326,24 +328,23 @@ if ( document.documentElement.currentStyle ) { curCSS = getComputedStyle || currentStyle; -function getWH( elem, name, extra ) { +function getWidthOrHeight( elem, name, extra ) { // Start with offset property var val = name === "width" ? elem.offsetWidth : elem.offsetHeight, - which = name === "width" ? cssWidth : cssHeight, - i = 0, - len = which.length; + i = name === "width" ? 1 : 0, + len = 4; if ( val > 0 ) { if ( extra !== "border" ) { - for ( ; i < len; i++ ) { + for ( ; i < len; i += 2 ) { if ( !extra ) { - val -= parseFloat( jQuery.css( elem, "padding" + which[ i ] ) ) || 0; + val -= parseFloat( jQuery.css( elem, "padding" + cssExpand[ i ] ) ) || 0; } if ( extra === "margin" ) { - val += parseFloat( jQuery.css( elem, extra + which[ i ] ) ) || 0; + val += parseFloat( jQuery.css( elem, extra + cssExpand[ i ] ) ) || 0; } else { - val -= parseFloat( jQuery.css( elem, "border" + which[ i ] + "Width" ) ) || 0; + val -= parseFloat( jQuery.css( elem, "border" + cssExpand[ i ] + "Width" ) ) || 0; } } } @@ -354,20 +355,20 @@ function getWH( elem, name, extra ) { // Fall back to computed then uncomputed css if necessary val = curCSS( elem, name, name ); if ( val < 0 || val == null ) { - val = elem.style[ name ] || 0; + val = elem.style[ name ]; } // Normalize "", auto, and prepare for extra val = parseFloat( val ) || 0; // Add padding, border, margin if ( extra ) { - for ( ; i < len; i++ ) { - val += parseFloat( jQuery.css( elem, "padding" + which[ i ] ) ) || 0; + for ( ; i < len; i += 2 ) { + val += parseFloat( jQuery.css( elem, "padding" + cssExpand[ i ] ) ) || 0; if ( extra !== "padding" ) { - val += parseFloat( jQuery.css( elem, "border" + which[ i ] + "Width" ) ) || 0; + val += parseFloat( jQuery.css( elem, "border" + cssExpand[ i ] + "Width" ) ) || 0; } if ( extra === "margin" ) { - val += parseFloat( jQuery.css( elem, extra + which[ i ] ) ) || 0; + val += parseFloat( jQuery.css( elem, extra + cssExpand[ i ]) ) || 0; } } } @@ -388,4 +389,29 @@ if ( jQuery.expr && jQuery.expr.filters ) { }; } +// These hooks are used by animate to expand properties +jQuery.each({ + margin: "", + padding: "", + border: "Width" +}, function( prefix, suffix ) { + + jQuery.cssHooks[ prefix + suffix ] = { + expand: function( value ) { + var i, + + // assumes a single number if not a string + parts = typeof value === "string" ? value.split(" ") : [ value ], + expanded = {}; + + for ( i = 0; i < 4; i++ ) { + expanded[ prefix + cssExpand[ i ] + suffix ] = + parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; + } + + return expanded; + } + }; +}); + })( jQuery ); diff --git a/src/effects.js b/src/effects.js index 5c6a20bbb..926b40b54 100644 --- a/src/effects.js +++ b/src/effects.js @@ -142,24 +142,37 @@ jQuery.fn.extend({ var opt = jQuery.extend( {}, optall ), isElement = this.nodeType === 1, hidden = isElement && jQuery(this).is(":hidden"), - name, val, p, e, + name, val, p, e, hooks, replace, parts, start, end, unit, method; // will store per property easing and be used to determine when an animation is complete opt.animatedProperties = {}; + // first pass over propertys to expand / normalize for ( p in prop ) { - - // property name normalization name = jQuery.camelCase( p ); if ( p !== name ) { prop[ name ] = prop[ p ]; delete prop[ p ]; } + + if ( ( hooks = jQuery.cssHooks[ name ] ) && "expand" in hooks ) { + replace = hooks.expand( prop[ name ] ); + delete prop[ name ]; + // not quite $.extend, this wont overwrite keys already present. + // also - reusing 'p' from above because we have the correct "name" + for ( p in replace ) { + if ( ! ( p in prop ) ) { + prop[ p ] = replace[ p ]; + } + } + } + } + + for ( name in prop ) { val = prop[ name ]; - // easing resolution: per property > opt.specialEasing > opt.easing > 'swing' (default) if ( jQuery.isArray( val ) ) { opt.animatedProperties[ name ] = val[ 1 ]; diff --git a/test/unit/css.js b/test/unit/css.js index 2021d897d..0eec26774 100644 --- a/test/unit/css.js +++ b/test/unit/css.js @@ -549,4 +549,44 @@ test("outerWidth(true) and css('margin') returning % instead of px in Webkit, se el = jQuery( "
" ).css({ width: "50%", marginRight: "50%" }).appendTo( container ); equal( el.outerWidth(true), 400, "outerWidth(true) and css('margin') returning % instead of px in Webkit, see #10639" ); +}); + +test( "cssHooks - expand", function() { + expect( 15 ); + var result, + properties = { + margin: [ "marginTop", "marginRight", "marginBottom", "marginLeft" ], + borderWidth: [ "borderTopWidth", "borderRightWidth", "borderBottomWidth", "borderLeftWidth"], + padding: [ "paddingTop", "paddingRight", "paddingBottom", "paddingLeft" ] + }; + + jQuery.each( properties, function( property, keys ) { + var hook = jQuery.cssHooks[ property ], + expected = {}; + jQuery.each( keys, function( _, key ) { + expected[ key ] = 10; + }); + result = hook.expand( 10 ); + deepEqual( result, expected, property + " expands properly with a number" ); + + jQuery.each( keys, function( _, key ) { + expected[ key ] = "10px"; + }); + result = hook.expand( "10px" ); + deepEqual( result, expected, property + " expands properly with '10px'" ); + + expected[ keys[1] ] = expected[ keys[3] ] = "20px"; + result = hook.expand( "10px 20px" ); + deepEqual( result, expected, property + " expands properly with '10px 20px'" ); + + expected[ keys[2] ] = "30px"; + result = hook.expand( "10px 20px 30px" ); + deepEqual( result, expected, property + " expands properly with '10px 20px 30px'" ); + + expected[ keys[3] ] = "40px"; + result = hook.expand( "10px 20px 30px 40px" ); + deepEqual( result, expected, property + " expands properly with '10px 20px 30px 40px'" ); + + }); + }); \ No newline at end of file diff --git a/test/unit/effects.js b/test/unit/effects.js index 82067cb8b..1728cf8f9 100644 --- a/test/unit/effects.js +++ b/test/unit/effects.js @@ -1271,3 +1271,30 @@ asyncTest( "callbacks that throw exceptions will be removed (#5684)", function() start(); }, 1); }); + +test("animate will scale margin properties individually", function() { + expect( 2 ); + stop(); + + var foo = jQuery( "#foo" ).css({ + margin: 0, + marginLeft: 100 + }); + + ok( foo.css( "marginLeft" ) !== foo.css( "marginRight" ), "Sanity Check" ); + + foo.animate({ + margin: 200 + }).stop(); + + ok( foo.css( "marginLeft") !== foo.css( "marginRight" ), "The margin properties are different"); + + // clean up for next test + foo.css({ + marginLeft: '', + marginRight: '', + marginTop: '', + marginBottom: '' + }); + start(); +});