Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

1.8 effects #731

Closed
wants to merge 39 commits into from

7 participants

@gnarf
Owner

So - I don't feel like this branch is by any means DONE... I just want an easy way for people to look at what I'm doing and discuss it. It currently passes all jQuery core unit tests in chrome so I figure its time to share it on a wider scale. It currently weighs in around 150 bytes more than the current core after min and gzip.

jQuery 1.8 Effects

To quote my own summary from an email thread:

My goal in the end is to have a plugin to sit alongside jQuery 1.8 (like jQuery.color.js) which implements animations that CAN be done with transitions or css animations, yet lets all the "complex" animations fall through to the default provider in jQuery core. This would allow you to define your animations in terms of CSS animation / transition style apis, and know that if the browser can't support CSS3, jQuery core will be doing what its doing now. This plugin may or may not find its way into UI Effects Core as jQuery.color already has.

General organization

jQuery.Animation is a new function which implements most of what our current doAnimation function hiding inside jQuery.fn.animate currently does. It returns an animation object which has the standard promise methods ( .done() and .fail() ) plus a helper called .finished() to publish its state information to the outside world. fail() functions will be called with a boolean gotoEnd whenever someone .stop()s the animation. .done() functions will NOT be called on .stop( gotoEnd ) but will be called whenever the animation comes to its natural completion. .finish() functions will be called whenever the animation completes - regardless of it had been .stop()ed.

jQuery.Tween is very similar to the old jQuery.fx (so much so that it is being used as such for backwards-compat). It handles the actual easing/get/set operations for a single element and property from 0% to 100%. Animations contain an array of Tweens that will be updated when their tick function is called.

New hook points

jQuery.Animation.preFilter( function( element, props, opts ) {})

Pre-filters allow a plugin to look at the props and opts of an animation being created and overide some default functionality, or do something else entirely with it. The hide / show special case preFilter in the code is a good example. It creates its own tweens for the hide and show scope to retain access to in the finish() callbacks it attaches to hook all of timmy's fxstop goodness together. Pre-filters are allowed to return their own object so long as it implements .stop( gotoEnd ), and .finish(). jQuery.fn.animate will be none the wiser.

jQuery.Animation.tweener( propsList, function( property, value ) {})

Allows you to peek at the property/value combinations and decide how to create a tween for that property or value. It should return truthy if it created the tween. If you want the tween to be managed by the animation you should use the animation.createTween( property, value ) helper, or push your own object containing a run( percent ) function to animation.tweens array.

jQuery.Tween.propHooks[ property ]

A { get: function( tween ) {}, set: function( tween ) {} } hook point for individual properties with a _default that uses jQuery.css and element.style

Current Issues

  • The "special easing" code right now bugs me because it really should run before the hide/show preFilter, but since makes it basically impossible to accept an array as a possible animated property value, anyone wanting an array value will have to prepend to the preFilter - perhaps not such a bad thing...

  • I have not yet tested this code for performance or potential memory leaks.

  • Should be prefilter to be in line with the example set by ajax: jQuery.ajaxPrefilter. I would like to avoid making it jQuery.animatePrefilter so jQuery.Animate.prefilter makes more sense to me...

@jaubourg
Collaborator

I'm in Dominican Republic right now, completely sun toasted and in pain with a very weak internet access. I'll be sure to check on this once I get back (end of the coming week).

src/effects.js
((54 lines not shown))
+ return true;
+ }
+ }
+ }
+ }
+ var propIndex, propTweens, index, tweenersLength, result;
+ for ( propIndex in props ) {
+ if ( !loop( tweeners[ propIndex ], propIndex ) ) {
+ loop( tweeners[ "*" ], propIndex );
+ }
+ }
+}
+
+jQuery.extend({
+ Animation: function( elem, properties, options ) {
+ var result,
@rwaldron Collaborator
rwaldron added a note

jQuery.extend is probably overkill, why not just define as function expressions:

jQuery.Animation = function( elem, properties, options ) { ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
src/effects.js
((58 lines not shown))
+ }
+ var propIndex, propTweens, index, tweenersLength, result;
+ for ( propIndex in props ) {
+ if ( !loop( tweeners[ propIndex ], propIndex ) ) {
+ loop( tweeners[ "*" ], propIndex );
+ }
+ }
+}
+
+jQuery.extend({
+ Animation: function( elem, properties, options ) {
+ var result,
+ index = 0,
+ tweenerIndex = 0,
+ length = preFilters.length,
+ deferred = jQuery.Deferred().always( function() {
@rwaldron Collaborator
rwaldron added a note

ways( function( => ways(function(

This is one of the padding exceptions. I'll let you sort the rest out instead of littering with line comments <3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
src/effects.js
((133 lines not shown))
+ result = preFilters[ index ].call( animation,
+ elem, animation.props, animation.opts );
+ if ( result ) {
+ return result;
+ }
+ }
+ callTweeners( animation, animation.props );
+ jQuery.extend( animation.tick, {
+ animation: animation,
+ queue: animation.opts.queue,
+ elem: elem
+ });
+ jQuery.fx.timer( animation.tick );
+ return animation;
+ },
+ Tween: function( elem, options, prop, end, easing ) {
@rwaldron Collaborator
rwaldron added a note

Same as above...

jQuery.Tween = function( elem, options, prop, end, easing ) { ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
src/effects.js
((160 lines not shown))
+ props = [ "*" ];
+ } else {
+ props = props.split(" ");
+ }
+
+ var prop,
+ index = 0,
+ length = props.length;
+
+ for ( ; index < length ; index++ ) {
+ prop = props[ index ];
+ tweeners[ prop ] = tweeners[ prop ] || [];
+ tweeners[ prop ].unshift( callback );
+ }
+ }
+});
@rwaldron Collaborator
rwaldron added a note

I'm going to start sounding like a broken record...

jQuery.Animation.preFilter = function(...)

jQuery.Animation.tweener = function(...)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
src/effects.js
((237 lines not shown))
+ if ( opts.overflow ) {
+ elem.style.overflow = "hidden";
+ this.finish( function() {
+ jQuery.each( [ "", "X", "Y" ], function( index, value ) {
+ elem.style[ "overflow" + value ] = opts.overflow[ index ];
+ });
+ });
+ }
+});
+
+// special case hide/show stuff
+jQuery.Animation.preFilter( function( elem, props, opts ) {
+ var prop, value, length, dataShow, tween,
+ index = 0,
+ orig = {},
+ hidden = jQuery( elem ).is( ":hidden" ),
@rwaldron Collaborator
rwaldron added a note

We recently changed the style guide on this one:

( ":hidden" ), => (":hidden"),

If a single arg is a string, no padding :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
src/effects.js
((296 lines not shown))
+ }
+ }
+ }
+ }
+
+ function hide() {
+ jQuery( elem ).hide();
+ }
+});
+
+jQuery.Tween.prototype = {
+ constructor: jQuery.Tween,
+ init: function( elem, options, prop, end, easing, unit ) {
+ this.elem = elem;
+ this.prop = prop;
+ this.easing = easing || 'swing';
@rwaldron Collaborator
rwaldron added a note

Double quotes mon ami :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
src/effects.js
((337 lines not shown))
+ } else {
+ _default.set( this );
+ }
+ return this;
+ }
+};
+
+jQuery.Tween.prototype.init.prototype = jQuery.Tween.prototype;
+
+jQuery.Tween.propHooks = {
+ _default: {
+ get: function( tween ) {
+ var parsed, result;
+
+ if (
+ tween.elem[ tween.prop ] != null &&
@rwaldron Collaborator
rwaldron added a note

move to previous line? it looks a little odd is all :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@rwaldron rwaldron commented on the diff
test/unit/effects.js
@@ -792,7 +792,7 @@ jQuery.checkOverflowDisplay = function(){
start();
};
-test( "jQuery.fx.prototype.cur()", 6, function() {
+test( "jQuery.fx.prototype.cur() - <1.8 Back Compat", 7, function() {
@rwaldron Collaborator
rwaldron added a note

Hahaha this is MY FAVORITE PART. <333

I totally expected test changes... Impressive!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@gnarf
Owner

After style tweaks and finding a possible use of options.step that didn't work and hacking that back in - Its weighs +306 now.

@rwaldron
Collaborator

+306 for a complete rewrite draft is pretty incredible

@rwaldron
Collaborator

Wait, is that +306 from 1.8pre? Or +306 after making the changes?

@gnarf
Owner

Eh - I was hoping that it would shrink @rwldrn ... There are still a few places I think I can shrink now.

@rwaldron
Collaborator

Nice! If I come up with any good reduction strategies, I'll ping you.

@gnarf
Owner
jQuery Size - compared to last make
  252237   (+990) jquery.js
   94087   (+497) jquery.min.js
   33551   (+291) jquery.min.js.gz

Back under 300... Based on the base commit of this branch ( 1.8pre -> 1.8-effects )

@gnarf
Owner

The last commit solves the issue I had posted in the original (now edited) PR Summary:

  • The first preFilter defined here might have its camelCase / cssHooks.expand stuff put in before all prefilters to make that a "standard" you can count on even if you are "prepend"ing (not a real word @danheberden) to the preFilters
@gnarf
Owner

At this point the Red Lines in the overall diff are becoming a hassle... Check out my blog post for a solution to hide all the red lines in a github diff for an easier time reviewing big stuff like this.

For those of you too lazy to read the post, open up the console and run this short jQuery to hide all the red lines:

$("div.diff-view tr .gd").closest("tr").toggle().length;
@gnarf
Owner

Also - now with vendor prefix support thanks to .cssProps and @mikesherov

@rwaldron
Collaborator

M-F'ing Teamwork... FOR THE WIN.

@dmethvin dmethvin closed this
@dmethvin dmethvin reopened this
@gnarf
Owner
jQuery Size - compared to last make
  251977   (+730) jquery.js
   93979   (+389) jquery.min.js
   33480   (+220) jquery.min.js.gz
jQuery build complete.
@gibson042
Collaborator

I found a few more bytes by reimplementing finished with jQuery.Callbacks: 584fc9b8f1ec12c7c89d44c161e2a96ba77d48ae

It works for me locally and seems right, but there might be another gotcha hiding in there. Thoughts?

@gibson042
Collaborator

12 more... 5fb3dbb077b2e91ada8a14155598e6694cbaae85

@gibson042
Collaborator

8 more, and I'm done (for now): fae8255d6e66cc7f9cda5486722033e55aded930

src/effects.js
((312 lines not shown))
+// special case show/hide prefilter
+Animation.preFilter(function( elem, props, opts ) {
+ var index, prop, value, length, dataShow, tween,
+ orig = {},
+ hidden = jQuery( elem ).is(":hidden"),

Don't know how often this is called, but would it be worth it to just call jQuery.expr.filters.hidden( elem ) directly?

@gnarf Owner
gnarf added a note

Once per animation on startup - I'm not that concerned with it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@gnarf
Owner

After @gibson042's epic work:

jQuery Size - compared to last make
  252008   (+761) jquery.js
   93987   (+397) jquery.min.js
   33455   (+195) jquery.min.js.gz
@gibson042
Collaborator

feel free to subtract 13 more: 6ad87a5eec9e5867b0c60c58446842bd4d3aae45 :D

@gnarf
Owner

@staabm see 488e7ec6eda2e03250f394504a4c13b53763fc82 for the undoing of that call.

@gnarf
Owner

Merged @gibson042 1.8-effects - epic work man.

@gibson042
Collaborator

I just can't quit... 1 last byte and a faster .show: dd4b3822d127d2e5c8a0c841f39dbb4595b9103c

@gibson042
Collaborator

I like the concept a lot, and was hoping something like that would work. Let me know when you're ready for more eyes on it.

@mikesherov
Collaborator

It'll, work, I just munged the logic. Updating soon. It'll work. It's only 8 bytes now.

@gnarf
Owner

Merged in @gibson042 dd4b382 -- @mikesherov I had a merge conflict trying to merge up to c839079, think you could rebase em?

@mikesherov
Collaborator
@gnarf
Owner

Added a new "Issue" to the pull:

  • Should be prefilter to be in line with the example set by ajax: jQuery.ajaxPrefilter. I would like to avoid making it jQuery.animatePrefilter so jQuery.Animate.prefilter makes more sense to me...
@gibson042
Collaborator

5 more from a2d4fe777d17ccb3ba69e4e5e57f200982df9bc3

@mikesherov
Collaborator

Rebased:
mikesherov@ad881e6 - 10 bytes
mikesherov@45547c3 - 6 bytes
mikesherov@58b8d7c - 8 bytes

@gnarf
Owner

Merged with @mikesherov's branch

@gnarf
Owner

Team Effort FTW:

jQuery Size - compared to last make
  251450   (+203) jquery.js
   93799   (+209) jquery.min.js
   33416   (+156) jquery.min.js.gz
gnarf and others added some commits
@gnarf gnarf 1.8 Effects First Draft Stuff eac9dce
@gnarf gnarf Working on implementing some of the features of show 44a0f6b
@gnarf gnarf Bringing some more code into the new style 0273fcb
@gnarf gnarf createTween helper function on animation 584d7e4
@gnarf gnarf More Refactored code ffed19e
@gnarf gnarf Lint Free e7c61ad
@gnarf gnarf More tests passing e204e4a
@gnarf gnarf Random cleanup 0282747
@gnarf gnarf Adding prop to fake fx obj 191d710
@gnarf gnarf Cleaning up a bit 9d69625
@gnarf gnarf More cleanup e5131f4
@gnarf gnarf threequals facdd42
@gnarf gnarf Vaccuming 00e9f22
@gnarf gnarf Passing effects tests 6366bf9
@gnarf gnarf Adding some new tests 6399ba5
@gnarf gnarf Rolling up some changes from @rwldrn's style suggestions
`call( "spacy" )` => `call("spacy")`
double quotes everywhere
avoiding extends in new code
d3da953
@gnarf gnarf Merging change from PR#732 - Nice optimization @SineSwiper dc847f5
@gnarf gnarf Vaccuming some bytes 4387961
@gnarf gnarf Property naming / cssHooks expand filters happen first ( +18 min.gz ) fc6fe4c
@gnarf gnarf Unrolling most jQuery.extends (other than jQuery.fn.extend()) results…
… in -2 bytes .min.gz
566ceb2
@gnarf gnarf Removing a silly code path, and reorganizing for gzip. (-2) e748d46
@gnarf gnarf Adding a comment for canidate for moving d053c55
@gibson042 gibson042 code compression: -84 bytes min+gzip 0e1e400
@gnarf gnarf Unit test that breaks the deferred change by @gibson042 b0d1f3d
@gnarf gnarf a few more bytes
Conflicts:

	src/effects.js
ec70f22
@gibson042 gibson042 12 more... 9973885
@gibson042 gibson042 -8 bytes min+gzip ba2eea5
@gnarf gnarf Adding a comment to a sticky line 86b3fb5
@gibson042 gibson042 -13 bytes min+gzip 605f350
@gibson042 gibson042 I just can't quit... 1 last byte e022748
@mikesherov mikesherov move undershoot logic to css.js, fixes #11604 3f587ef
@mikesherov mikesherov 6 more bytes gone a2c38de
@mikesherov mikesherov remove fxAttrs for a savings of 8 bytes! cdc60e7
@gnarf gnarf Merge branch '1.8pre' into 1.8-effects
Conflicts:
	src/css.js
dee5c00
@gibson042 gibson042 slightly unconventional -5 min+gzip 2257f13
@gibson042 gibson042 tweak genFx to satisfy gzip 38d5eef
@gibson042
Collaborator

tweak @mikesherov's new genFx for 12 bytes: 38d5eef

@mikesherov
Collaborator

Nice. Figured you'd squeeze more out, but 12? Nice.

@gnarf
Owner

Pulled @gibson042 's genFx tweak in

@mikesherov
Collaborator

found 4 more bytes: 0b23ca0

@gibson042
Collaborator

Can you explain in more detail the "special easing" issue?

@gnarf
Owner
jQuery Size - compared to last make
  253180   (+345) jquery.js
   93963   (+208) jquery.min.js
   33454   (+156) jquery.min.js.gz
@gnarf
Owner

1.8-effects to ground control. @dmethvin do we have clearance to land?

@dmethvin
Owner

Landed in 1.8pre: 58ed62e

Cigars all around!

@dmethvin dmethvin closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Apr 20, 2012
  1. @gnarf

    1.8 Effects First Draft Stuff

    gnarf authored
  2. @gnarf
  3. @gnarf
  4. @gnarf
  5. @gnarf

    More Refactored code

    gnarf authored
  6. @gnarf

    Lint Free

    gnarf authored
  7. @gnarf

    More tests passing

    gnarf authored
  8. @gnarf

    Random cleanup

    gnarf authored
  9. @gnarf

    Adding prop to fake fx obj

    gnarf authored
  10. @gnarf

    Cleaning up a bit

    gnarf authored
  11. @gnarf

    More cleanup

    gnarf authored
  12. @gnarf

    threequals

    gnarf authored
  13. @gnarf

    Vaccuming

    gnarf authored
  14. @gnarf

    Passing effects tests

    gnarf authored
  15. @gnarf

    Adding some new tests

    gnarf authored
  16. @gnarf

    Rolling up some changes from @rwldrn's style suggestions

    gnarf authored
    `call( "spacy" )` => `call("spacy")`
    double quotes everywhere
    avoiding extends in new code
  17. @gnarf
  18. @gnarf

    Vaccuming some bytes

    gnarf authored
  19. @gnarf
  20. @gnarf
  21. @gnarf
  22. @gnarf
  23. @gibson042 @gnarf

    code compression: -84 bytes min+gzip

    gibson042 authored gnarf committed
  24. @gnarf
  25. @gnarf

    a few more bytes

    gnarf authored
    Conflicts:
    
    	src/effects.js
  26. @gibson042 @gnarf

    12 more...

    gibson042 authored gnarf committed
  27. @gibson042 @gnarf

    -8 bytes min+gzip

    gibson042 authored gnarf committed
  28. @gnarf
  29. @gibson042 @gnarf

    -13 bytes min+gzip

    gibson042 authored gnarf committed
  30. @gibson042 @gnarf

    I just can't quit... 1 last byte

    gibson042 authored gnarf committed
  31. @mikesherov @gnarf

    move undershoot logic to css.js, fixes #11604

    mikesherov authored gnarf committed
  32. @mikesherov @gnarf

    6 more bytes gone

    mikesherov authored gnarf committed
  33. @mikesherov @gnarf

    remove fxAttrs for a savings of 8 bytes!

    mikesherov authored gnarf committed
  34. @gnarf

    Merge branch '1.8pre' into 1.8-effects

    gnarf authored
    Conflicts:
    	src/css.js
  35. @gibson042 @gnarf

    slightly unconventional -5 min+gzip

    gibson042 authored gnarf committed
Commits on Apr 21, 2012
  1. @gibson042

    tweak genFx to satisfy gzip

    gibson042 authored
Commits on Apr 22, 2012
  1. @mikesherov
Commits on Apr 23, 2012
  1. @gnarf
  2. @gnarf
This page is out of date. Refresh to see the latest.
Showing with 618 additions and 546 deletions.
  1. +16 −8 src/css.js
  2. +537 −534 src/effects.js
  3. +3 −3 test/unit/css.js
  4. +62 −1 test/unit/effects.js
View
24 src/css.js
@@ -1,10 +1,12 @@
(function( jQuery ) {
+jQuery.cssExpand = [ "Top", "Right", "Bottom", "Left" ];
+
var ralpha = /alpha\([^)]*\)/i,
ropacity = /opacity=([^)]*)/,
// fixed for IE9, see #8346
rupper = /([A-Z]|^ms)/g,
- rnum = /^[\-+]?(?:\d*\.)?\d+$/i,
+ rnumsplit = /^([\-+]?(?:\d*\.)?\d+)(.*)$/i,
rnumnonpx = /^-?(?:\d*\.)?\d+(?!px)[^\d\s]+$/i,
rrelNum = /^([\-+])=([\-+.\de]+)/,
rmargin = /^margin/,
@@ -12,7 +14,7 @@ var ralpha = /alpha\([^)]*\)/i,
cssShow = { position: "absolute", visibility: "hidden", display: "block" },
// order is important!
- cssExpand = [ "Top", "Right", "Bottom", "Left" ],
+ cssExpand = jQuery.cssExpand,
cssPrefixes = [ "O", "Webkit", "Moz", "ms" ],
curCSS;
@@ -264,6 +266,13 @@ if ( document.defaultView && document.defaultView.getComputedStyle ) {
};
}
+function setPositiveNumber( elem, value ) {
+ var matches = rnumsplit.exec( value );
+ return matches ?
+ Math.max( 0, matches[ 1 ] ) + ( matches [ 2 ] || "px" )
+ : value;
+}
+
function getWidthOrHeight( elem, name, extra ) {
// Start with offset property, which is equivalent to the border-box value
@@ -348,11 +357,7 @@ jQuery.each([ "height", "width" ], function( i, name ) {
}
},
- set: function( elem, value ) {
- return rnum.test( value ) ?
- value + "px" :
- value;
- }
+ set: setPositiveNumber
};
});
@@ -436,7 +441,6 @@ jQuery.each({
padding: "",
border: "Width"
}, function( prefix, suffix ) {
-
jQuery.cssHooks[ prefix + suffix ] = {
expand: function( value ) {
var i,
@@ -453,6 +457,10 @@ jQuery.each({
return expanded;
}
};
+
+ if ( !rmargin.test( prefix ) ) {
+ jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
+ }
});
})( jQuery );
View
1,071 src/effects.js
@@ -1,274 +1,487 @@
(function( jQuery ) {
-var elemdisplay = {},
- iframe, iframeDoc,
+var fxNow, timerId, iframe, iframeDoc,
+ elemdisplay = {},
rfxtypes = /^(?:toggle|show|hide)$/,
- rfxnum = /^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,
- rMarginProp = /^margin/,
- timerId,
- fxAttrs = [
- // height animations
- [ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
- // width animations
- [ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
- // opacity animations
- [ "opacity" ]
- ],
- fxNow;
-
-jQuery.fn.extend({
- show: function( speed, easing, callback ) {
- var elem, display;
+ rfxnum = /^([\-+]=)?((?:\d*\.)?\d+)([a-z%]*)$/i,
+ rrun = /\.run$/,
+ animationPrefilters = [],
+ tweeners = {
+ "*": [function( prop, value ) {
+ var end, unit,
+ tween = this.createTween( prop, value ),
+ parts = rfxnum.exec( value ),
+ start = tween.cur();
+
+ if ( parts ) {
+ end = +parts[2];
+ unit = parts[3] || ( jQuery.cssNumber[ prop ] ? "" : "px" );
+
+ // We need to compute starting value
+ if ( unit !== "px" ) {
+ jQuery.style( this, prop, (end || 1) + unit);
+ start = start * (end || 1) / tween.cur() || 0;
+ jQuery.style( this, prop, start + unit);
+ }
- if ( speed || speed === 0 ) {
- return this.animate( genFx("show", 3), speed, easing, callback );
+ tween.unit = unit;
+ tween.start = start;
+ // If a +=/-= token was provided, we're doing a relative animation
+ tween.end = parts[1] ? start + end * ( parts[1] === "-=" ? -1 : 1 ) : end;
+ }
+ return tween;
+ }]
+ },
+ oldToggle = jQuery.fn.toggle;
- } else {
- for ( var i = 0, j = this.length; i < j; i++ ) {
- elem = this[ i ];
+// Animations created synchronously will run synchronously
+function createFxNow() {
+ setTimeout(function() {
+ fxNow = undefined;
+ }, 0 );
+ return ( fxNow = jQuery.now() );
+}
- if ( elem.style ) {
- display = elem.style.display;
+function callTweeners( animation, props ) {
+ jQuery.each( props, function( prop, value ) {
+ var collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ),
+ index = 0,
+ length = collection.length;
+ for ( ; index < length; index++ ) {
+ if ( collection[ index ].call( animation, prop, value ) ) {
+ // we're done with this property
+ return;
+ }
+ }
+ });
+}
- // Reset the inline display of this element to learn if it is
- // being hidden by cascaded rules or not
- if ( !jQuery._data(elem, "olddisplay") && display === "none" ) {
- display = elem.style.display = "";
- }
+function Animation( elem, properties, options ) {
+ var result,
+ index = 0,
+ tweenerIndex = 0,
+ length = animationPrefilters.length,
+ finished = jQuery.Deferred(),
+ deferred = jQuery.Deferred().always(function( ended ) {
+ // remove cirular reference
+ delete animation.tick;
+
+ if ( deferred.state() === "resolved" || ended ) {
+ // fire callbacks
+ finished.resolveWith( this );
+ }
+ }),
+ animation = deferred.promise({
+ elem: elem,
+ originalProperties: properties,
+ originalOptions: options,
+ props: jQuery.extend( {}, properties ),
+ opts: jQuery.extend( {}, options ),
+ startTime: fxNow || createFxNow(),
+ duration: options.duration,
+ finish: finished.done,
+ tweens: [],
+ createTween: function( prop, end, easing ) {
+ var tween = jQuery.Tween( elem, animation.opts, prop, end,
+ animation.opts.specialEasing[ prop ] || animation.opts.easing );
+ animation.tweens.push( tween );
+ return tween;
+ },
+ tick: function() {
+ var currentTime = fxNow || createFxNow(),
+ remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
+ percent = 1 - ( remaining / animation.duration || 0 ),
+ index = 0,
+ length = animation.tweens.length;
+
+ for ( ; index < length ; index++ ) {
+ animation.tweens[ index ].run( percent );
+ }
- // Set elements which have been overridden with display: none
- // in a stylesheet to whatever the default browser style is
- // for such an element
- if ( (display === "" && jQuery.css(elem, "display") === "none") ||
- !jQuery.contains( elem.ownerDocument.documentElement, elem ) ) {
- jQuery._data( elem, "olddisplay", defaultDisplay(elem.nodeName) );
- }
+ if ( percent < 1 && length ) {
+ return remaining;
+ } else {
+ deferred.resolveWith( elem, [ currentTime ] );
+ return false;
+ }
+ },
+ stop: function( gotoEnd ) {
+ var index = 0,
+ // if we are going to the end, we want to run all the tweens
+ // otherwise we skip this part
+ length = gotoEnd ? animation.tweens.length : 0;
+
+ for ( ; index < length ; index++ ) {
+ animation.tweens[ index ].run( 1 );
}
+ deferred.rejectWith( elem, [ gotoEnd ] );
+ return this;
}
+ }),
+ props = animation.props;
- // Set the display of most of the elements in a second loop
- // to avoid the constant reflow
- for ( i = 0; i < j; i++ ) {
- elem = this[ i ];
+ propFilter( props );
- if ( elem.style ) {
- display = elem.style.display;
+ for ( ; index < length ; index++ ) {
+ result = animationPrefilters[ index ].call( animation,
+ elem, props, animation.opts );
+ if ( result ) {
+ return result;
+ }
+ }
- if ( display === "" || display === "none" ) {
- elem.style.display = jQuery._data( elem, "olddisplay" ) || "";
- }
- }
- }
+ callTweeners( animation, props );
- return this;
- }
- },
+ jQuery.extend( animation.tick, {
+ anim: animation,
+ queue: animation.opts.queue,
+ elem: elem
+ });
- hide: function( speed, easing, callback ) {
- if ( speed || speed === 0 ) {
- return this.animate( genFx("hide", 3), speed, easing, callback);
+ jQuery.fx.timer( animation.tick );
+ return animation;
+}
- } else {
- var elem, display,
- i = 0,
- j = this.length;
+function propFilter( props ) {
+ var index, name, hooks, replace;
- for ( ; i < j; i++ ) {
- elem = this[i];
- if ( elem.style ) {
- display = jQuery.css( elem, "display" );
+ // camelCase and expand cssHook pass
+ for ( index in props ) {
+ name = jQuery.camelCase( index );
+ if ( index !== name ) {
+ props[ name ] = props[ index ];
+ delete props[ index ];
+ }
- if ( display !== "none" && !jQuery._data( elem, "olddisplay" ) ) {
- jQuery._data( elem, "olddisplay", display );
- }
- }
- }
+ hooks = jQuery.cssHooks[ name ];
+ if ( hooks && "expand" in hooks ) {
+ replace = hooks.expand( props[ name ] );
+ delete props[ name ];
- // Set the display of the elements in a second loop
- // to avoid the constant reflow
- for ( i = 0; i < j; i++ ) {
- if ( this[i].style ) {
- this[i].style.display = "none";
+ // not quite $.extend, this wont overwrite keys already present.
+ // also - reusing 'index' from above because we have the correct "name"
+ for ( index in replace ) {
+ if ( index in props ) {
+ continue;
}
+ props[ index ] = replace[ index ];
}
+ }
+ }
+}
- return this;
+jQuery.Animation = jQuery.extend( Animation, {
+
+ tweener: function( props, callback ) {
+ if ( jQuery.isFunction( props ) ) {
+ callback = props;
+ props = [ "*" ];
+ } else {
+ props = props.split(" ");
+ }
+
+ var prop,
+ index = 0,
+ length = props.length;
+
+ for ( ; index < length ; index++ ) {
+ prop = props[ index ];
+ tweeners[ prop ] = tweeners[ prop ] || [];
+ tweeners[ prop ].unshift( callback );
}
},
- // Save the old toggle function
- _toggle: jQuery.fn.toggle,
+ prefilter: function( callback, prepend ) {
+ if ( prepend ) {
+ animationPrefilters.unshift( callback );
+ } else {
+ animationPrefilters.push( callback );
+ }
+ }
+});
- toggle: function( fn, fn2, callback ) {
- var bool = typeof fn === "boolean";
+Animation.prefilter(function( elem, props, opts ) {
+ var index, value,
+ style = elem.style;
+
+ // custom easing pass
+ opts.specialEasing = opts.specialEasing || {};
+ for ( index in props ) {
+ value = props[ index ];
+ if ( jQuery.isArray( value ) ) {
+ opts.specialEasing[ index ] = value[ 1 ];
+ value = props[ index ] = value[ 0 ];
+ }
+ }
- if ( jQuery.isFunction(fn) && jQuery.isFunction(fn2) ) {
- this._toggle.apply( this, arguments );
+ // height/width overflow pass
+ if ( elem.nodeType === 1 && ( props.height || props.width ) ) {
+ // Make sure that nothing sneaks out
+ // Record all 3 overflow attributes because IE does not
+ // change the overflow attribute when overflowX and
+ // overflowY are set to the same value
+ opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
- } else if ( fn == null || bool ) {
- this.each(function() {
- var state = bool ? fn : jQuery(this).is(":hidden");
- jQuery(this)[ state ? "show" : "hide" ]();
- });
+ // Set display property to inline-block for height/width
+ // animations on inline elements that are having width/height animated
+ if ( jQuery.css( elem, "display" ) === "inline" &&
+ jQuery.css( elem, "float" ) === "none" ) {
+
+ // inline-level elements accept inline-block;
+ // block-level elements need to be inline with layout
+ if ( !jQuery.support.inlineBlockNeedsLayout || defaultDisplay( elem.nodeName ) === "inline" ) {
+ style.display = "inline-block";
+
+ } else {
+ style.zoom = 1;
+ }
+ }
+ }
+
+ if ( opts.overflow ) {
+ style.overflow = "hidden";
+ this.finish(function() {
+ style.overflow = opts.overflow[ 0 ];
+ style.overflowX = opts.overflow[ 1 ];
+ style.overflowY = opts.overflow[ 2 ];
+ });
+ }
+});
+// special case show/hide prefilter
+Animation.prefilter(function( elem, props, opts ) {
+ var index, prop, value, length, dataShow, tween,
+ orig = {},
+ handled = [],
+ hidden = jQuery( elem ).is(":hidden");
+
+ for ( index in props ) {
+ value = props[ index ];
+ if ( rfxtypes.exec( value ) ) {
+ delete props[ index ];
+ if ( value === ( hidden ? "hide" : "show" ) ) {
+ continue;
+ }
+ handled.push( index );
+ }
+ }
+
+ length = handled.length;
+ if ( length ) {
+ dataShow = jQuery._data( elem, "fxshow" ) || jQuery._data( elem, "fxshow", {} );
+ if ( hidden ) {
+ showHide([ elem ], true );
} else {
- this.animate(genFx("toggle", 3), fn, fn2, callback);
+ this.finish(function() {
+ showHide([ elem ]);
+ });
}
+ this.finish(function() {
+ var prop;
+ jQuery.removeData( elem, "fxshow", true );
+ for ( prop in orig ) {
+ jQuery.style( elem, prop, orig[ prop ] );
+ }
+ });
+ for ( index = 0 ; index < length ; index++ ) {
+ prop = handled[ index ];
+ tween = this.createTween( prop, hidden ? dataShow[ prop ] : 0 );
+ orig[ prop ] = dataShow[ prop ] || jQuery.style( elem, prop );
+
+ if ( !( prop in dataShow ) ) {
+ dataShow[ prop ] = tween.start;
+ if ( hidden ) {
+ tween.end = tween.start;
+ tween.start = prop === "width" || prop === "height" ? 1 : 0;
+ }
+ }
+ }
+ }
+});
- return this;
+function Tween( elem, options, prop, end, easing ) {
+ return new Tween.prototype.init( elem, options, prop, end, easing );
+}
+jQuery.Tween = Tween;
+
+Tween.prototype = {
+ constructor: Tween,
+ init: function( elem, options, prop, end, easing, unit ) {
+ this.elem = elem;
+ this.prop = prop;
+ this.easing = easing || "swing";
+ this.options = options;
+ this.start = this.now = this.cur();
+ this.end = end;
+ this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );
},
+ cur: function() {
+ var hooks = Tween.propHooks[ this.prop ];
- fadeTo: function( speed, to, easing, callback ) {
- return this.filter(":hidden").css("opacity", 0).show().end()
- .animate({opacity: to}, speed, easing, callback);
+ return hooks && hooks.get ?
+ hooks.get( this ) :
+ Tween.propHooks._default.get( this );
},
+ run: function( percent ) {
+ var eased,
+ hooks = Tween.propHooks[ this.prop ];
- animate: function( prop, speed, easing, callback ) {
- var optall = jQuery.speed( speed, easing, callback );
+ this.pos = eased = jQuery.easing[ this.easing ]( percent, this.options.duration * percent, 0, 1, this.options.duration );
+ this.now = ( this.end - this.start ) * eased + this.start;
- if ( jQuery.isEmptyObject( prop ) ) {
- return this.each( optall.complete, [ false ] );
+ if ( this.options.step ) {
+ this.options.step.call( this.elem, this.now, this );
}
- // Do not change referenced properties as per-property easing will be lost
- prop = jQuery.extend( {}, prop );
-
- function doAnimation() {
- // XXX 'this' does not always have a nodeName when running the
- // test suite
-
- if ( optall.queue === false ) {
- jQuery._mark( this );
- }
+ if ( hooks && hooks.set ) {
+ hooks.set( this );
+ } else {
+ Tween.propHooks._default.set( this );
+ }
+ return this;
+ }
+};
- var opt = jQuery.extend( {}, optall ),
- isElement = this.nodeType === 1,
- hidden = isElement && jQuery(this).is(":hidden"),
- 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 ) {
- name = jQuery.camelCase( p );
- if ( p !== name ) {
- prop[ name ] = prop[ p ];
- delete prop[ p ];
- }
+Tween.prototype.init.prototype = Tween.prototype;
- if ( ( hooks = jQuery.cssHooks[ name ] ) && "expand" in hooks ) {
- replace = hooks.expand( prop[ name ] );
- delete prop[ name ];
+Tween.propHooks = {
+ _default: {
+ get: function( tween ) {
+ var parsed, result;
- // 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 ];
- }
- }
- }
+ if ( tween.elem[ tween.prop ] != null &&
+ (!tween.elem.style || tween.elem.style[ tween.prop ] == null) ) {
+ return tween.elem[ tween.prop ];
}
- 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 ];
- val = prop[ name ] = val[ 0 ];
- } else {
- opt.animatedProperties[ name ] = opt.specialEasing && opt.specialEasing[ name ] || opt.easing || 'swing';
- }
+ result = jQuery.css( tween.elem, tween.prop );
+ // Empty strings, null, undefined and "auto" are converted to 0,
+ // complex values such as "rotate(1rad)" are returned as is,
+ // simple values such as "10px" are parsed to Float.
+ return isNaN( parsed = parseFloat( result ) ) ?
+ !result || result === "auto" ? 0 : result : parsed;
+ },
+ set: function( tween ) {
+ // use step hook for back compat - use cssHook if its there - use .style if its
+ // available and use plain properties where available
+ if ( jQuery.fx.step[ tween.prop ] ) {
+ jQuery.fx.step[ tween.prop ]( tween );
+ } else if ( tween.elem.style && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) {
+ jQuery.style( tween.elem, tween.prop, tween.now + tween.unit );
+ } else {
+ tween.elem[ tween.prop ] = tween.now;
+ }
+ }
+ }
+};
- if ( val === "hide" && hidden || val === "show" && !hidden ) {
- return opt.complete.call( this );
- }
+function showHide( elements, show ) {
+ var elem, display,
+ values = [],
+ index = 0,
+ length = elements.length;
- if ( isElement && ( name === "height" || name === "width" ) ) {
- // Make sure that nothing sneaks out
- // Record all 3 overflow attributes because IE does not
- // change the overflow attribute when overflowX and
- // overflowY are set to the same value
- opt.overflow = [ this.style.overflow, this.style.overflowX, this.style.overflowY ];
-
- // Set display property to inline-block for height/width
- // animations on inline elements that are having width/height animated
- if ( jQuery.css( this, "display" ) === "inline" &&
- jQuery.css( this, "float" ) === "none" ) {
-
- // inline-level elements accept inline-block;
- // block-level elements need to be inline with layout
- if ( !jQuery.support.inlineBlockNeedsLayout || defaultDisplay( this.nodeName ) === "inline" ) {
- this.style.display = "inline-block";
-
- } else {
- this.style.zoom = 1;
- }
- }
- }
+ for ( ; index < length; index++ ) {
+ elem = elements[ index ];
+ if ( !elem.style ) {
+ continue;
+ }
+ values[ index ] = jQuery._data( elem, "olddisplay" );
+ if ( show ) {
+ // Reset the inline display of this element to learn if it is
+ // being hidden by cascaded rules or not
+ if ( !values[ index ] && elem.style.display === "none" ) {
+ elem.style.display = "";
}
- if ( opt.overflow != null ) {
- this.style.overflow = "hidden";
+ // Set elements which have been overridden with display: none
+ // in a stylesheet to whatever the default browser style is
+ // for such an element
+ if ( (elem.style.display === "" && jQuery.css( elem, "display" ) === "none") ||
+ !jQuery.contains( elem.ownerDocument.documentElement, elem ) ) {
+ values[ index ] = jQuery._data( elem, "olddisplay", defaultDisplay(elem.nodeName) );
}
+ } else {
+ display = jQuery.css( elem, "display" );
- for ( p in prop ) {
- e = new jQuery.fx( this, opt, p );
- val = prop[ p ];
-
- if ( rfxtypes.test( val ) ) {
+ if ( !values[ index ] && display !== "none" ) {
+ jQuery._data( elem, "olddisplay", display );
+ }
+ }
+ }
- // Tracks whether to show or hide based on private
- // data attached to the element
- method = jQuery._data( this, "toggle" + p ) || ( val === "toggle" ? hidden ? "show" : "hide" : 0 );
- if ( method ) {
- jQuery._data( this, "toggle" + p, method === "show" ? "hide" : "show" );
- e[ method ]();
- } else {
- e[ val ]();
- }
+ // Set the display of most of the elements in a second loop
+ // to avoid the constant reflow
+ for ( index = 0; index < length; index++ ) {
+ elem = elements[ index ];
+ if ( !elem.style ) {
+ continue;
+ }
+ if ( !show || elem.style.display === "none" || elem.style.display === "" ) {
+ elem.style.display = show ? values[ index ] || "" : "none";
+ }
+ }
- } else {
- parts = rfxnum.exec( val );
- start = e.cur();
+ return elements;
+}
- if ( parts ) {
- end = parseFloat( parts[2] );
- unit = parts[3] || ( jQuery.cssNumber[ p ] ? "" : "px" );
+jQuery.fn.extend({
+ show: function( speed, easing, callback ) {
+ return speed || speed === 0 ?
+ this.animate( genFx( "show", true ), speed, easing, callback ) :
+ showHide( this, true );
+ },
+ hide: function( speed, easing, callback ) {
+ return speed || speed === 0 ?
+ this.animate( genFx( "hide", true ), speed, easing, callback ) :
+ showHide( this );
+ },
+ toggle: function( fn, fn2, callback ) {
+ var bool = typeof fn === "boolean";
- // We need to compute starting value
- if ( unit !== "px" ) {
- jQuery.style( this, p, (end || 1) + unit);
- start = ( (end || 1) / e.cur() ) * start;
- jQuery.style( this, p, start + unit);
- }
+ if ( jQuery.isFunction( fn ) && jQuery.isFunction( fn2 ) ) {
+ oldToggle.apply( this, arguments );
- // If a +=/-= token was provided, we're doing a relative animation
- if ( parts[1] ) {
- end = ( (parts[ 1 ] === "-=" ? -1 : 1) * end ) + start;
- }
+ } else if ( fn == null || bool ) {
+ this.each(function() {
+ var state = bool ? fn : jQuery( this ).is(":hidden");
+ showHide([ this ], state );
+ });
- e.custom( start, end, unit );
+ } else {
+ this.animate( genFx( "toggle", true ), fn, fn2, callback );
+ }
- } else {
- e.custom( start, val, "" );
- }
- }
- }
+ return this;
+ },
+ fadeTo: function( speed, to, easing, callback ) {
+ return this.filter(":hidden").css("opacity", 0).show().end()
+ .animate({opacity: to}, speed, easing, callback);
+ },
+ animate: function( prop, speed, easing, callback ) {
+ var optall = jQuery.speed( speed, easing, callback ),
+ doAnimation = function() {
+ Animation( this, prop, optall ).finish( optall.complete );
+ };
- // For JS strict compliance
- return true;
+ if ( jQuery.isEmptyObject( prop ) ) {
+ return this.each( optall.complete, [ false ] );
}
+ // Do not change referenced properties as per-property easing will be lost
+ prop = jQuery.extend( {}, prop );
+
return optall.queue === false ?
this.each( doAnimation ) :
this.queue( optall.queue, doAnimation );
},
-
stop: function( type, clearQueue, gotoEnd ) {
+ var stopQueue = function( elem, data, index ) {
+ var hooks = data[ index ];
+ jQuery.removeData( elem, index, true );
+ hooks.stop( gotoEnd );
+ };
+
if ( typeof type !== "string" ) {
gotoEnd = clearQueue;
clearQueue = type;
@@ -289,15 +502,9 @@ jQuery.fn.extend({
jQuery._unmark( true, this );
}
- function stopQueue( elem, data, index ) {
- var hooks = data[ index ];
- jQuery.removeData( elem, index, true );
- hooks.stop( gotoEnd );
- }
-
if ( type == null ) {
for ( index in data ) {
- if ( data[ index ] && data[ index ].stop && index.indexOf(".run") === index.length - 4 ) {
+ if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
stopQueue( this, data, index );
}
}
@@ -307,13 +514,7 @@ jQuery.fn.extend({
for ( index = timers.length; index--; ) {
if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) {
- if ( gotoEnd ) {
-
- // force the next step to be the last
- timers[ index ]( true );
- } else {
- timers[ index ].saveState();
- }
+ timers[ index ].anim.stop( gotoEnd );
hadTimers = true;
timers.splice( index, 1 );
}
@@ -327,35 +528,33 @@ jQuery.fn.extend({
}
});
}
-
});
-// Animations created synchronously will run synchronously
-function createFxNow() {
- setTimeout( clearFxNow, 0 );
- return ( fxNow = jQuery.now() );
-}
-
-function clearFxNow() {
- fxNow = undefined;
-}
-
// Generate parameters to create a standard animation
-function genFx( type, num ) {
- var obj = {};
+function genFx( type, includeWidth ) {
+ var which,
+ attrs = { height: type },
+ i = 0;
+
+ // if we include width, step value is 1 to do all cssExpand values,
+ // if we don't include width, step value is 2 to skip over Left and Right
+ for( ; i < 4 ; i += 2 - includeWidth ) {
+ which = jQuery.cssExpand[ i ];
+ attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
+ }
- jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice( 0, num )), function() {
- obj[ this ] = type;
- });
+ if ( includeWidth ) {
+ attrs.opacity = attrs.width = type;
+ }
- return obj;
+ return attrs;
}
// Generate shortcuts for custom animations
jQuery.each({
- slideDown: genFx( "show", 1 ),
- slideUp: genFx( "hide", 1 ),
- slideToggle: genFx( "toggle", 1 ),
+ slideDown: genFx("show"),
+ slideUp: genFx("hide"),
+ slideToggle: genFx("toggle"),
fadeIn: { opacity: "show" },
fadeOut: { opacity: "hide" },
fadeToggle: { opacity: "toggle" }
@@ -365,285 +564,91 @@ jQuery.each({
};
});
-jQuery.extend({
- speed: function( speed, easing, fn ) {
- var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
- complete: fn || !fn && easing ||
- jQuery.isFunction( speed ) && speed,
- duration: speed,
- easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
- };
-
- opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
- opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
-
- // normalize opt.queue - true/undefined/null -> "fx"
- if ( opt.queue == null || opt.queue === true ) {
- opt.queue = "fx";
- }
-
- // Queueing
- opt.old = opt.complete;
-
- opt.complete = function( noUnmark ) {
- if ( jQuery.isFunction( opt.old ) ) {
- opt.old.call( this );
- }
-
- if ( opt.queue ) {
- jQuery.dequeue( this, opt.queue );
- } else if ( noUnmark !== false ) {
- jQuery._unmark( this );
- }
- };
-
- return opt;
- },
-
- easing: {
- linear: function( p ) {
- return p;
- },
- swing: function( p ) {
- return ( -Math.cos( p*Math.PI ) / 2 ) + 0.5;
- }
- },
+jQuery.speed = function( speed, easing, fn ) {
+ var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
+ complete: fn || !fn && easing ||
+ jQuery.isFunction( speed ) && speed,
+ duration: speed,
+ easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
+ };
- timers: [],
+ opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
+ opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
- fx: function( elem, options, prop ) {
- this.options = options;
- this.elem = elem;
- this.prop = prop;
-
- options.orig = options.orig || {};
+ // normalize opt.queue - true/undefined/null -> "fx"
+ if ( opt.queue == null || opt.queue === true ) {
+ opt.queue = "fx";
}
-});
-
-jQuery.fx.prototype = {
- // Simple function for setting a style value
- update: function() {
- if ( this.options.step ) {
- this.options.step.call( this.elem, this.now, this );
- }
-
- ( jQuery.fx.step[ this.prop ] || jQuery.fx.step._default )( this );
- },
-
- // Get the current size
- cur: function() {
- if ( this.elem[ this.prop ] != null && (!this.elem.style || this.elem.style[ this.prop ] == null) ) {
- return this.elem[ this.prop ];
- }
-
- var parsed,
- r = jQuery.css( this.elem, this.prop );
- // Empty strings, null, undefined and "auto" are converted to 0,
- // complex values such as "rotate(1rad)" are returned as is,
- // simple values such as "10px" are parsed to Float.
- return isNaN( parsed = parseFloat( r ) ) ? !r || r === "auto" ? 0 : r : parsed;
- },
-
- // Start an animation from one number to another
- custom: function( from, to, unit ) {
- var self = this,
- fx = jQuery.fx;
-
- this.startTime = fxNow || createFxNow();
- this.end = to;
- this.now = this.start = from;
- this.pos = this.state = 0;
- this.unit = unit || this.unit || ( jQuery.cssNumber[ this.prop ] ? "" : "px" );
-
- function t( gotoEnd ) {
- return self.step( gotoEnd );
- }
-
- t.queue = this.options.queue;
- t.elem = this.elem;
- t.saveState = function() {
- if ( jQuery._data( self.elem, "fxshow" + self.prop ) === undefined ) {
- if ( self.options.hide ) {
- jQuery._data( self.elem, "fxshow" + self.prop, self.start );
- } else if ( self.options.show ) {
- jQuery._data( self.elem, "fxshow" + self.prop, self.end );
- }
- }
- };
+ // Queueing
+ opt.old = opt.complete;
- if ( t() && jQuery.timers.push(t) && !timerId ) {
- timerId = setInterval( fx.tick, fx.interval );
+ opt.complete = function( noUnmark ) {
+ if ( jQuery.isFunction( opt.old ) ) {
+ opt.old.call( this );
}
- },
-
- // Simple 'show' function
- show: function() {
- var dataShow = jQuery._data( this.elem, "fxshow" + this.prop );
- // Remember where we started, so that we can go back to it later
- this.options.orig[ this.prop ] = dataShow || jQuery.style( this.elem, this.prop );
- this.options.show = true;
-
- // Begin the animation
- // Make sure that we start at a small width/height to avoid any flash of content
- if ( dataShow !== undefined ) {
- // This show is picking up where a previous hide or show left off
- this.custom( this.cur(), dataShow );
- } else {
- this.custom( this.prop === "width" || this.prop === "height" ? 1 : 0, this.cur() );
+ if ( opt.queue ) {
+ jQuery.dequeue( this, opt.queue );
+ } else if ( noUnmark !== false ) {
+ jQuery._unmark( this );
}
+ };
- // Start by showing the element
- jQuery( this.elem ).show();
- },
-
- // Simple 'hide' function
- hide: function() {
- // Remember where we started, so that we can go back to it later
- this.options.orig[ this.prop ] = jQuery._data( this.elem, "fxshow" + this.prop ) || jQuery.style( this.elem, this.prop );
- this.options.hide = true;
+ return opt;
+};
- // Begin the animation
- this.custom( this.cur(), 0 );
+jQuery.easing = {
+ linear: function( p ) {
+ return p;
},
-
- // Each step of an animation
- step: function( gotoEnd ) {
- var p, n, complete,
- t = fxNow || createFxNow(),
- done = true,
- elem = this.elem,
- options = this.options;
-
- if ( gotoEnd || t >= options.duration + this.startTime ) {
- this.now = this.end;
- this.pos = this.state = 1;
- this.update();
-
- options.animatedProperties[ this.prop ] = true;
-
- for ( p in options.animatedProperties ) {
- if ( options.animatedProperties[ p ] !== true ) {
- done = false;
- }
- }
-
- if ( done ) {
- // Reset the overflow
- if ( options.overflow != null && !jQuery.support.shrinkWrapBlocks ) {
-
- jQuery.each( [ "", "X", "Y" ], function( index, value ) {
- elem.style[ "overflow" + value ] = options.overflow[ index ];
- });
- }
-
- // Hide the element if the "hide" operation was done
- if ( options.hide ) {
- jQuery( elem ).hide();
- }
-
- // Reset the properties, if the item has been hidden or shown
- if ( options.hide || options.show ) {
- for ( p in options.animatedProperties ) {
- jQuery.style( elem, p, options.orig[ p ] );
- jQuery.removeData( elem, "fxshow" + p, true );
- // Toggle data is no longer needed
- jQuery.removeData( elem, "toggle" + p, true );
- }
- }
-
- // Execute the complete function
- // in the event that the complete function throws an exception
- // we must ensure it won't be called twice. #5684
-
- complete = options.complete;
- if ( complete ) {
-
- options.complete = false;
- complete.call( elem );
- }
- }
-
- return false;
-
- } else {
- // classical easing cannot be used with an Infinity duration
- if ( options.duration == Infinity ) {
- this.now = t;
- } else {
- n = t - this.startTime;
- this.state = n / options.duration;
-
- // Perform the easing function, defaults to swing
- this.pos = jQuery.easing[ options.animatedProperties[this.prop] ]( this.state, n, 0, 1, options.duration );
- this.now = this.start + ( (this.end - this.start) * this.pos );
- }
- // Perform the next step of the animation
- this.update();
- }
-
- return true;
+ swing: function( p ) {
+ return 0.5 - Math.cos( p*Math.PI ) / 2;
}
};
-jQuery.extend( jQuery.fx, {
- tick: function() {
- var timer,
- timers = jQuery.timers,
- i = 0;
-
- for ( ; i < timers.length; i++ ) {
- timer = timers[ i ];
- // Checks the timer has not already been removed
- if ( !timer() && timers[ i ] === timer ) {
- timers.splice( i--, 1 );
- }
- }
-
- if ( !timers.length ) {
- jQuery.fx.stop();
+jQuery.timers = [];
+jQuery.fx = Tween.prototype.init;
+jQuery.fx.tick = function() {
+ var timer,
+ timers = jQuery.timers,
+ i = 0;
+
+ for ( ; i < timers.length; i++ ) {
+ timer = timers[ i ];
+ // Checks the timer has not already been removed
+ if ( !timer() && timers[ i ] === timer ) {
+ timers.splice( i--, 1 );
}
- },
+ }
- interval: 13,
+ if ( !timers.length ) {
+ jQuery.fx.stop();
+ }
+};
- stop: function() {
- clearInterval( timerId );
- timerId = null;
- },
+jQuery.fx.timer = function( timer ) {
+ if ( timer() && jQuery.timers.push( timer ) && !timerId ) {
+ timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval );
+ }
+};
- speeds: {
- slow: 600,
- fast: 200,
- // Default speed
- _default: 400
- },
+jQuery.fx.interval = 13;
- step: {
- opacity: function( fx ) {
- jQuery.style( fx.elem, "opacity", fx.now );
- },
+jQuery.fx.stop = function() {
+ clearInterval( timerId );
+ timerId = null;
+};
- _default: function( fx ) {
- if ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) {
- fx.elem.style[ fx.prop ] = fx.now + fx.unit;
- } else {
- fx.elem[ fx.prop ] = fx.now;
- }
- }
- }
-});
+jQuery.fx.speeds = {
+ slow: 600,
+ fast: 200,
+ // Default speed
+ _default: 400
+};
-// Ensure props that can't be negative don't go there on undershoot easing
-jQuery.each( fxAttrs.concat.apply( [], fxAttrs ), function( i, prop ) {
- // Exclude marginTop, marginLeft, marginBottom and marginRight from this list
- if ( !rMarginProp.test( prop ) ) {
- jQuery.fx.step[ prop ] = function( fx ) {
- jQuery.style( fx.elem, prop, Math.max(0, fx.now) + fx.unit );
- };
- }
-});
+// Back Compat <1.8 extension point
+jQuery.fx.step = {};
if ( jQuery.expr && jQuery.expr.filters ) {
jQuery.expr.filters.animated = function( elem ) {
@@ -655,47 +660,45 @@ if ( jQuery.expr && jQuery.expr.filters ) {
// Try to restore the default display value of an element
function defaultDisplay( nodeName ) {
+ if ( elemdisplay[ nodeName ] ) {
+ return elemdisplay[ nodeName ];
+ }
- if ( !elemdisplay[ nodeName ] ) {
-
- var body = document.body,
- elem = jQuery( "<" + nodeName + ">" ).appendTo( body ),
- display = elem.css( "display" );
- elem.remove();
-
- // If the simple way fails,
- // get element's real default display by attaching it to a temp iframe
- if ( display === "none" || display === "" ) {
- // No iframe to use yet, so create it
- if ( !iframe ) {
- iframe = document.createElement( "iframe" );
- iframe.frameBorder = iframe.width = iframe.height = 0;
- }
-
- body.appendChild( iframe );
-
- // Create a cacheable copy of the iframe document on first call.
- // IE and Opera will allow us to reuse the iframeDoc without re-writing the fake HTML
- // document to it; WebKit & Firefox won't allow reusing the iframe document.
- if ( !iframeDoc || !iframe.createElement ) {
- iframeDoc = ( iframe.contentWindow || iframe.contentDocument ).document;
- iframeDoc.write( ( jQuery.support.boxModel ? "<!doctype html>" : "" ) + "<html><body>" );
- iframeDoc.close();
- }
-
- elem = iframeDoc.createElement( nodeName );
-
- iframeDoc.body.appendChild( elem );
-
- display = jQuery.css( elem, "display" );
- body.removeChild( iframe );
+ var elem = jQuery( "<" + nodeName + ">" ).appendTo( document.body ),
+ display = elem.css("display");
+ elem.remove();
+
+ // If the simple way fails,
+ // get element's real default display by attaching it to a temp iframe
+ if ( display === "none" || display === "" ) {
+ // Use the already-created iframe if possible
+ iframe = document.body.appendChild(
+ iframe || jQuery.extend( document.createElement("iframe"), {
+ frameBorder: 0,
+ width: 0,
+ height: 0
+ })
+ );
+
+ // Create a cacheable copy of the iframe document on first call.
+ // IE and Opera will allow us to reuse the iframeDoc without re-writing the fake HTML
+ // document to it; WebKit & Firefox won't allow reusing the iframe document.
+ if ( !iframeDoc || !iframe.createElement ) {
+ iframeDoc = ( iframe.contentWindow || iframe.contentDocument ).document;
+ iframeDoc.write( ( jQuery.support.boxModel ? "<!doctype html>" : "" ) + "<html><body>" );
+ iframeDoc.close();
}
- // Store the correct default display
- elemdisplay[ nodeName ] = display;
+ elem = iframeDoc.body.appendChild( iframeDoc.createElement(nodeName) );
+
+ display = jQuery.css( elem, "display" );
+ document.body.removeChild( iframe );
}
- return elemdisplay[ nodeName ];
+ // Store the correct default display
+ elemdisplay[ nodeName ] = display;
+
+ return display;
}
})( jQuery );
View
6 test/unit/css.js
@@ -37,13 +37,13 @@ test("css(String|Hash)", function() {
div2.remove();
- // handle negative numbers by ignoring #1599, #4216
+ // handle negative numbers by setting to zero #11604
jQuery("#nothiddendiv").css( {width: 1, height: 1} );
var width = parseFloat(jQuery("#nothiddendiv").css("width")), height = parseFloat(jQuery("#nothiddendiv").css("height"));
jQuery("#nothiddendiv").css({ width: -1, height: -1 });
- equal( parseFloat(jQuery("#nothiddendiv").css("width")), width, "Test negative width ignored");
- equal( parseFloat(jQuery("#nothiddendiv").css("height")), height, "Test negative height ignored");
+ equal( parseFloat(jQuery("#nothiddendiv").css("width")), 0, "Test negative width set to 0");
+ equal( parseFloat(jQuery("#nothiddendiv").css("height")), 0, "Test negative height set to 0");
equal( jQuery("<div style='display: none;'>").css("display"), "none", "Styles on disconnected nodes");
View
63 test/unit/effects.js
@@ -792,7 +792,7 @@ jQuery.checkOverflowDisplay = function(){
start();
};
-test( "jQuery.fx.prototype.cur()", 6, function() {
+test( "jQuery.fx.prototype.cur() - <1.8 Back Compat", 7, function() {
@rwaldron Collaborator
rwaldron added a note

Hahaha this is MY FAVORITE PART. <333

I totally expected test changes... Impressive!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
var div = jQuery( "<div></div>" ).appendTo( "#qunit-fixture" ).css({
color: "#ABC",
border: "5px solid black",
@@ -814,6 +814,8 @@ test( "jQuery.fx.prototype.cur()", 6, function() {
// backgroundPosition actually returns 0% 0% in most browser
// this fakes a "" return
+ // hook now gets called twice because Tween will grab the current
+ // value as it is being newed
jQuery.cssHooks.backgroundPosition = {
get: function() {
ok( true, "hook used" );
@@ -1387,3 +1389,62 @@ test("animate will scale margin properties individually", function() {
});
start();
});
+
+// Start 1.8 Animation tests
+asyncTest( "jQuery.Animation( object, props, opts )", 1, function() {
+ var testObject = {
+ foo: 0,
+ bar: 1,
+ width: 100
+ },
+ testDest = {
+ foo: 1,
+ bar: 0,
+ width: 200
+ };
+
+ jQuery.Animation( testObject, testDest, { duration: 1 })
+ .done( function() {
+ deepEqual( testObject, testDest, "Animated foo and bar" );
+ start();
+ });
+});
+
+asyncTest( "Animate Option: step: function( percent, tween )", 1, function() {
+ var counter = {};
+ jQuery( "#foo" ).animate({
+ prop1: 1,
+ prop2: 2,
+ prop3: 3
+ }, {
+ duration: 1,
+ step: function( value, tween ) {
+ calls = counter[ tween.prop ] = counter[ tween.prop ] || [];
+ calls.push( value );
+ }
+ }).queue( function( next ) {
+ deepEqual( counter, {
+ prop1: [0, 1],
+ prop2: [0, 2],
+ prop3: [0, 3]
+ }, "Step function was called once at 0% and once at 100% for each property");
+ next();
+ start();
+ });
+});
+
+
+asyncTest( "Animate callbacks have correct context", 2, function() {
+ var foo = jQuery( "#foo" );
+ foo.animate({
+ height: 10
+ }, 10, function() {
+ equal( foo[ 0 ], this, "Complete callback after stop(true) `this` is element" );
+ }).stop( true, true );
+ foo.animate({
+ height: 100
+ }, 10, function() {
+ equal( foo[ 0 ], this, "Complete callback `this` is element" );
+ start();
+ });
+});
Something went wrong with that request. Please try again.