Release: push dist to same remote as project
[jquery.git] / src / effects.js
blob3aa408b51e0c99d900e97bc309e616e9d4d720f5
1 define([
2         "./core",
3         "./var/document",
4         "./var/pnum",
5         "./css/var/cssExpand",
6         "./css/var/isHidden",
7         "./css/defaultDisplay",
8         "./data/var/dataPriv",
10         "./core/init",
11         "./effects/Tween",
12         "./queue",
13         "./css",
14         "./deferred",
15         "./traversing"
16 ], function( jQuery, document, pnum, cssExpand, isHidden, defaultDisplay, dataPriv ) {
18 var
19         fxNow, timerId,
20         rfxtypes = /^(?:toggle|show|hide)$/,
21         rfxnum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ),
22         rrun = /queueHooks$/,
23         animationPrefilters = [ defaultPrefilter ],
24         tweeners = {
25                 "*": [ function( prop, value ) {
26                         var tween = this.createTween( prop, value ),
27                                 target = tween.cur(),
28                                 parts = rfxnum.exec( value ),
29                                 unit = parts && parts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ),
31                                 // Starting value computation is required for potential unit mismatches
32                                 start = ( jQuery.cssNumber[ prop ] || unit !== "px" && +target ) &&
33                                         rfxnum.exec( jQuery.css( tween.elem, prop ) ),
34                                 scale = 1,
35                                 maxIterations = 20;
37                         if ( start && start[ 3 ] !== unit ) {
38                                 // Trust units reported by jQuery.css
39                                 unit = unit || start[ 3 ];
41                                 // Make sure we update the tween properties later on
42                                 parts = parts || [];
44                                 // Iteratively approximate from a nonzero starting point
45                                 start = +target || 1;
47                                 do {
48                                         // If previous iteration zeroed out, double until we get *something*.
49                                         // Use string for doubling so we don't accidentally see scale as unchanged below
50                                         scale = scale || ".5";
52                                         // Adjust and apply
53                                         start = start / scale;
54                                         jQuery.style( tween.elem, prop, start + unit );
56                                 // Update scale, tolerating zero or NaN from tween.cur(),
57                                 // break the loop if scale is unchanged or perfect, or if we've just had enough
58                                 } while (
59                                         scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations
60                                 );
61                         }
63                         // Update tween properties
64                         if ( parts ) {
65                                 start = tween.start = +start || +target || 0;
66                                 tween.unit = unit;
67                                 // If a +=/-= token was provided, we're doing a relative animation
68                                 tween.end = parts[ 1 ] ?
69                                         start + ( parts[ 1 ] + 1 ) * parts[ 2 ] :
70                                         +parts[ 2 ];
71                         }
73                         return tween;
74                 } ]
75         };
77 function raf() {
78         if ( timerId ) {
79                 window.requestAnimationFrame( raf );
80                 jQuery.fx.tick();
81         }
84 // Animations created synchronously will run synchronously
85 function createFxNow() {
86         setTimeout(function() {
87                 fxNow = undefined;
88         });
89         return ( fxNow = jQuery.now() );
92 // Generate parameters to create a standard animation
93 function genFx( type, includeWidth ) {
94         var which,
95                 i = 0,
96                 attrs = { height: type };
98         // If we include width, step value is 1 to do all cssExpand values,
99         // otherwise step value is 2 to skip over Left and Right
100         includeWidth = includeWidth ? 1 : 0;
101         for ( ; i < 4 ; i += 2 - includeWidth ) {
102                 which = cssExpand[ i ];
103                 attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
104         }
106         if ( includeWidth ) {
107                 attrs.opacity = attrs.width = type;
108         }
110         return attrs;
113 function createTween( value, prop, animation ) {
114         var tween,
115                 collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ),
116                 index = 0,
117                 length = collection.length;
118         for ( ; index < length; index++ ) {
119                 if ( (tween = collection[ index ].call( animation, prop, value )) ) {
121                         // We're done with this property
122                         return tween;
123                 }
124         }
127 function defaultPrefilter( elem, props, opts ) {
128         /* jshint validthis: true */
129         var prop, value, toggle, tween, hooks, oldfire, display, checkDisplay,
130                 anim = this,
131                 orig = {},
132                 style = elem.style,
133                 hidden = elem.nodeType && isHidden( elem ),
134                 dataShow = dataPriv.get( elem, "fxshow" );
136         // Handle queue: false promises
137         if ( !opts.queue ) {
138                 hooks = jQuery._queueHooks( elem, "fx" );
139                 if ( hooks.unqueued == null ) {
140                         hooks.unqueued = 0;
141                         oldfire = hooks.empty.fire;
142                         hooks.empty.fire = function() {
143                                 if ( !hooks.unqueued ) {
144                                         oldfire();
145                                 }
146                         };
147                 }
148                 hooks.unqueued++;
150                 anim.always(function() {
151                         // Ensure the complete handler is called before this completes
152                         anim.always(function() {
153                                 hooks.unqueued--;
154                                 if ( !jQuery.queue( elem, "fx" ).length ) {
155                                         hooks.empty.fire();
156                                 }
157                         });
158                 });
159         }
161         // Height/width overflow pass
162         if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) {
163                 // Make sure that nothing sneaks out
164                 // Record all 3 overflow attributes because IE9-10 do not
165                 // change the overflow attribute when overflowX and
166                 // overflowY are set to the same value
167                 opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
169                 // Set display property to inline-block for height/width
170                 // animations on inline elements that are having width/height animated
171                 display = jQuery.css( elem, "display" );
173                 // Test default display if display is currently "none"
174                 checkDisplay = display === "none" ?
175                         dataPriv.get( elem, "olddisplay" ) || defaultDisplay( elem.nodeName ) : display;
177                 if ( checkDisplay === "inline" && jQuery.css( elem, "float" ) === "none" ) {
178                         style.display = "inline-block";
179                 }
180         }
182         if ( opts.overflow ) {
183                 style.overflow = "hidden";
184                 anim.always(function() {
185                         style.overflow = opts.overflow[ 0 ];
186                         style.overflowX = opts.overflow[ 1 ];
187                         style.overflowY = opts.overflow[ 2 ];
188                 });
189         }
191         // show/hide pass
192         for ( prop in props ) {
193                 value = props[ prop ];
194                 if ( rfxtypes.exec( value ) ) {
195                         delete props[ prop ];
196                         toggle = toggle || value === "toggle";
197                         if ( value === ( hidden ? "hide" : "show" ) ) {
199                                 // If there is dataShow left over from a stopped hide or show
200                                 // and we are going to proceed with show, we should pretend to be hidden
201                                 if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) {
202                                         hidden = true;
203                                 } else {
204                                         continue;
205                                 }
206                         }
207                         orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop );
209                 // Any non-fx value stops us from restoring the original display value
210                 } else {
211                         display = undefined;
212                 }
213         }
215         if ( !jQuery.isEmptyObject( orig ) ) {
216                 if ( dataShow ) {
217                         if ( "hidden" in dataShow ) {
218                                 hidden = dataShow.hidden;
219                         }
220                 } else {
221                         dataShow = dataPriv.access( elem, "fxshow", {} );
222                 }
224                 // Store state if its toggle - enables .stop().toggle() to "reverse"
225                 if ( toggle ) {
226                         dataShow.hidden = !hidden;
227                 }
228                 if ( hidden ) {
229                         jQuery( elem ).show();
230                 } else {
231                         anim.done(function() {
232                                 jQuery( elem ).hide();
233                         });
234                 }
235                 anim.done(function() {
236                         var prop;
238                         dataPriv.remove( elem, "fxshow" );
239                         for ( prop in orig ) {
240                                 jQuery.style( elem, prop, orig[ prop ] );
241                         }
242                 });
243                 for ( prop in orig ) {
244                         tween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim );
246                         if ( !( prop in dataShow ) ) {
247                                 dataShow[ prop ] = tween.start;
248                                 if ( hidden ) {
249                                         tween.end = tween.start;
250                                         tween.start = prop === "width" || prop === "height" ? 1 : 0;
251                                 }
252                         }
253                 }
255         // If this is a noop like .hide().hide(), restore an overwritten display value
256         } else if ( (display === "none" ? defaultDisplay( elem.nodeName ) : display) === "inline" ) {
257                 style.display = display;
258         }
261 function propFilter( props, specialEasing ) {
262         var index, name, easing, value, hooks;
264         // camelCase, specialEasing and expand cssHook pass
265         for ( index in props ) {
266                 name = jQuery.camelCase( index );
267                 easing = specialEasing[ name ];
268                 value = props[ index ];
269                 if ( jQuery.isArray( value ) ) {
270                         easing = value[ 1 ];
271                         value = props[ index ] = value[ 0 ];
272                 }
274                 if ( index !== name ) {
275                         props[ name ] = value;
276                         delete props[ index ];
277                 }
279                 hooks = jQuery.cssHooks[ name ];
280                 if ( hooks && "expand" in hooks ) {
281                         value = hooks.expand( value );
282                         delete props[ name ];
284                         // Not quite $.extend, this won't overwrite existing keys.
285                         // Reusing 'index' because we have the correct "name"
286                         for ( index in value ) {
287                                 if ( !( index in props ) ) {
288                                         props[ index ] = value[ index ];
289                                         specialEasing[ index ] = easing;
290                                 }
291                         }
292                 } else {
293                         specialEasing[ name ] = easing;
294                 }
295         }
298 function Animation( elem, properties, options ) {
299         var result,
300                 stopped,
301                 index = 0,
302                 length = animationPrefilters.length,
303                 deferred = jQuery.Deferred().always( function() {
304                         // Don't match elem in the :animated selector
305                         delete tick.elem;
306                 }),
307                 tick = function() {
308                         if ( stopped ) {
309                                 return false;
310                         }
311                         var currentTime = fxNow || createFxNow(),
312                                 remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
313                                 // Support: Android 2.3
314                                 // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497)
315                                 temp = remaining / animation.duration || 0,
316                                 percent = 1 - temp,
317                                 index = 0,
318                                 length = animation.tweens.length;
320                         for ( ; index < length ; index++ ) {
321                                 animation.tweens[ index ].run( percent );
322                         }
324                         deferred.notifyWith( elem, [ animation, percent, remaining ]);
326                         if ( percent < 1 && length ) {
327                                 return remaining;
328                         } else {
329                                 deferred.resolveWith( elem, [ animation ] );
330                                 return false;
331                         }
332                 },
333                 animation = deferred.promise({
334                         elem: elem,
335                         props: jQuery.extend( {}, properties ),
336                         opts: jQuery.extend( true, { specialEasing: {} }, options ),
337                         originalProperties: properties,
338                         originalOptions: options,
339                         startTime: fxNow || createFxNow(),
340                         duration: options.duration,
341                         tweens: [],
342                         createTween: function( prop, end ) {
343                                 var tween = jQuery.Tween( elem, animation.opts, prop, end,
344                                                 animation.opts.specialEasing[ prop ] || animation.opts.easing );
345                                 animation.tweens.push( tween );
346                                 return tween;
347                         },
348                         stop: function( gotoEnd ) {
349                                 var index = 0,
350                                         // If we are going to the end, we want to run all the tweens
351                                         // otherwise we skip this part
352                                         length = gotoEnd ? animation.tweens.length : 0;
353                                 if ( stopped ) {
354                                         return this;
355                                 }
356                                 stopped = true;
357                                 for ( ; index < length ; index++ ) {
358                                         animation.tweens[ index ].run( 1 );
359                                 }
361                                 // Resolve when we played the last frame; otherwise, reject
362                                 if ( gotoEnd ) {
363                                         deferred.resolveWith( elem, [ animation, gotoEnd ] );
364                                 } else {
365                                         deferred.rejectWith( elem, [ animation, gotoEnd ] );
366                                 }
367                                 return this;
368                         }
369                 }),
370                 props = animation.props;
372         propFilter( props, animation.opts.specialEasing );
374         for ( ; index < length ; index++ ) {
375                 result = animationPrefilters[ index ].call( animation, elem, props, animation.opts );
376                 if ( result ) {
377                         return result;
378                 }
379         }
381         jQuery.map( props, createTween, animation );
383         if ( jQuery.isFunction( animation.opts.start ) ) {
384                 animation.opts.start.call( elem, animation );
385         }
387         jQuery.fx.timer(
388                 jQuery.extend( tick, {
389                         elem: elem,
390                         anim: animation,
391                         queue: animation.opts.queue
392                 })
393         );
395         // attach callbacks from options
396         return animation.progress( animation.opts.progress )
397                 .done( animation.opts.done, animation.opts.complete )
398                 .fail( animation.opts.fail )
399                 .always( animation.opts.always );
402 jQuery.Animation = jQuery.extend( Animation, {
404         tweener: function( props, callback ) {
405                 if ( jQuery.isFunction( props ) ) {
406                         callback = props;
407                         props = [ "*" ];
408                 } else {
409                         props = props.split(" ");
410                 }
412                 var prop,
413                         index = 0,
414                         length = props.length;
416                 for ( ; index < length ; index++ ) {
417                         prop = props[ index ];
418                         tweeners[ prop ] = tweeners[ prop ] || [];
419                         tweeners[ prop ].unshift( callback );
420                 }
421         },
423         prefilter: function( callback, prepend ) {
424                 if ( prepend ) {
425                         animationPrefilters.unshift( callback );
426                 } else {
427                         animationPrefilters.push( callback );
428                 }
429         }
432 jQuery.speed = function( speed, easing, fn ) {
433         var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
434                 complete: fn || !fn && easing ||
435                         jQuery.isFunction( speed ) && speed,
436                 duration: speed,
437                 easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
438         };
440         // Go to the end state if fx are off or if document is hidden
441         if ( jQuery.fx.off || document.hidden ) {
442                 opt.duration = 0;
444         } else {
445                 opt.duration = typeof opt.duration === "number" ?
446                         opt.duration : opt.duration in jQuery.fx.speeds ?
447                                 jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
448         }
450         // Normalize opt.queue - true/undefined/null -> "fx"
451         if ( opt.queue == null || opt.queue === true ) {
452                 opt.queue = "fx";
453         }
455         // Queueing
456         opt.old = opt.complete;
458         opt.complete = function() {
459                 if ( jQuery.isFunction( opt.old ) ) {
460                         opt.old.call( this );
461                 }
463                 if ( opt.queue ) {
464                         jQuery.dequeue( this, opt.queue );
465                 }
466         };
468         return opt;
471 jQuery.fn.extend({
472         fadeTo: function( speed, to, easing, callback ) {
474                 // Show any hidden elements after setting opacity to 0
475                 return this.filter( isHidden ).css( "opacity", 0 ).show()
477                         // Animate to the value specified
478                         .end().animate({ opacity: to }, speed, easing, callback );
479         },
480         animate: function( prop, speed, easing, callback ) {
481                 var empty = jQuery.isEmptyObject( prop ),
482                         optall = jQuery.speed( speed, easing, callback ),
483                         doAnimation = function() {
484                                 // Operate on a copy of prop so per-property easing won't be lost
485                                 var anim = Animation( this, jQuery.extend( {}, prop ), optall );
487                                 // Empty animations, or finishing resolves immediately
488                                 if ( empty || dataPriv.get( this, "finish" ) ) {
489                                         anim.stop( true );
490                                 }
491                         };
492                         doAnimation.finish = doAnimation;
494                 return empty || optall.queue === false ?
495                         this.each( doAnimation ) :
496                         this.queue( optall.queue, doAnimation );
497         },
498         stop: function( type, clearQueue, gotoEnd ) {
499                 var stopQueue = function( hooks ) {
500                         var stop = hooks.stop;
501                         delete hooks.stop;
502                         stop( gotoEnd );
503                 };
505                 if ( typeof type !== "string" ) {
506                         gotoEnd = clearQueue;
507                         clearQueue = type;
508                         type = undefined;
509                 }
510                 if ( clearQueue && type !== false ) {
511                         this.queue( type || "fx", [] );
512                 }
514                 return this.each(function() {
515                         var dequeue = true,
516                                 index = type != null && type + "queueHooks",
517                                 timers = jQuery.timers,
518                                 data = dataPriv.get( this );
520                         if ( index ) {
521                                 if ( data[ index ] && data[ index ].stop ) {
522                                         stopQueue( data[ index ] );
523                                 }
524                         } else {
525                                 for ( index in data ) {
526                                         if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
527                                                 stopQueue( data[ index ] );
528                                         }
529                                 }
530                         }
532                         for ( index = timers.length; index--; ) {
533                                 if ( timers[ index ].elem === this &&
534                                         (type == null || timers[ index ].queue === type) ) {
536                                         timers[ index ].anim.stop( gotoEnd );
537                                         dequeue = false;
538                                         timers.splice( index, 1 );
539                                 }
540                         }
542                         // Start the next in the queue if the last step wasn't forced.
543                         // Timers currently will call their complete callbacks, which
544                         // will dequeue but only if they were gotoEnd.
545                         if ( dequeue || !gotoEnd ) {
546                                 jQuery.dequeue( this, type );
547                         }
548                 });
549         },
550         finish: function( type ) {
551                 if ( type !== false ) {
552                         type = type || "fx";
553                 }
554                 return this.each(function() {
555                         var index,
556                                 data = dataPriv.get( this ),
557                                 queue = data[ type + "queue" ],
558                                 hooks = data[ type + "queueHooks" ],
559                                 timers = jQuery.timers,
560                                 length = queue ? queue.length : 0;
562                         // Enable finishing flag on private data
563                         data.finish = true;
565                         // Empty the queue first
566                         jQuery.queue( this, type, [] );
568                         if ( hooks && hooks.stop ) {
569                                 hooks.stop.call( this, true );
570                         }
572                         // Look for any active animations, and finish them
573                         for ( index = timers.length; index--; ) {
574                                 if ( timers[ index ].elem === this && timers[ index ].queue === type ) {
575                                         timers[ index ].anim.stop( true );
576                                         timers.splice( index, 1 );
577                                 }
578                         }
580                         // Look for any animations in the old queue and finish them
581                         for ( index = 0; index < length; index++ ) {
582                                 if ( queue[ index ] && queue[ index ].finish ) {
583                                         queue[ index ].finish.call( this );
584                                 }
585                         }
587                         // Turn off finishing flag
588                         delete data.finish;
589                 });
590         }
593 jQuery.each([ "toggle", "show", "hide" ], function( i, name ) {
594         var cssFn = jQuery.fn[ name ];
595         jQuery.fn[ name ] = function( speed, easing, callback ) {
596                 return speed == null || typeof speed === "boolean" ?
597                         cssFn.apply( this, arguments ) :
598                         this.animate( genFx( name, true ), speed, easing, callback );
599         };
602 // Generate shortcuts for custom animations
603 jQuery.each({
604         slideDown: genFx("show"),
605         slideUp: genFx("hide"),
606         slideToggle: genFx("toggle"),
607         fadeIn: { opacity: "show" },
608         fadeOut: { opacity: "hide" },
609         fadeToggle: { opacity: "toggle" }
610 }, function( name, props ) {
611         jQuery.fn[ name ] = function( speed, easing, callback ) {
612                 return this.animate( props, speed, easing, callback );
613         };
616 jQuery.timers = [];
617 jQuery.fx.tick = function() {
618         var timer,
619                 i = 0,
620                 timers = jQuery.timers;
622         fxNow = jQuery.now();
624         for ( ; i < timers.length; i++ ) {
625                 timer = timers[ i ];
626                 // Checks the timer has not already been removed
627                 if ( !timer() && timers[ i ] === timer ) {
628                         timers.splice( i--, 1 );
629                 }
630         }
632         if ( !timers.length ) {
633                 jQuery.fx.stop();
634         }
635         fxNow = undefined;
638 jQuery.fx.timer = function( timer ) {
639         jQuery.timers.push( timer );
640         if ( timer() ) {
641                 jQuery.fx.start();
642         } else {
643                 jQuery.timers.pop();
644         }
647 jQuery.fx.interval = 13;
648 jQuery.fx.start = function() {
649         if ( !timerId ) {
650                 timerId = window.requestAnimationFrame ?
651                         window.requestAnimationFrame( raf ) :
652                         setInterval( jQuery.fx.tick, jQuery.fx.interval );
653         }
656 jQuery.fx.stop = function() {
657         if ( window.cancelAnimationFrame ) {
658                 window.cancelAnimationFrame( timerId );
659         } else {
660                 clearInterval( timerId );
661         }
663         timerId = null;
666 jQuery.fx.speeds = {
667         slow: 600,
668         fast: 200,
669         // Default speed
670         _default: 400
673 return jQuery;