UNPKG

17.4 kBJavaScriptView Raw
1define( [
2 "./core",
3 "./core/camelCase",
4 "./var/document",
5 "./var/isFunction",
6 "./var/rcssNum",
7 "./var/rnothtmlwhite",
8 "./css/var/cssExpand",
9 "./css/var/isHiddenWithinTree",
10 "./css/adjustCSS",
11 "./data/var/dataPriv",
12 "./css/showHide",
13
14 "./core/init",
15 "./queue",
16 "./deferred",
17 "./traversing",
18 "./manipulation",
19 "./css",
20 "./effects/Tween"
21], function( jQuery, camelCase, document, isFunction, rcssNum, rnothtmlwhite, cssExpand,
22 isHiddenWithinTree, adjustCSS, dataPriv, showHide ) {
23
24"use strict";
25
26var
27 fxNow, inProgress,
28 rfxtypes = /^(?:toggle|show|hide)$/,
29 rrun = /queueHooks$/;
30
31function schedule() {
32 if ( inProgress ) {
33 if ( document.hidden === false && window.requestAnimationFrame ) {
34 window.requestAnimationFrame( schedule );
35 } else {
36 window.setTimeout( schedule, jQuery.fx.interval );
37 }
38
39 jQuery.fx.tick();
40 }
41}
42
43// Animations created synchronously will run synchronously
44function createFxNow() {
45 window.setTimeout( function() {
46 fxNow = undefined;
47 } );
48 return ( fxNow = Date.now() );
49}
50
51// Generate parameters to create a standard animation
52function genFx( type, includeWidth ) {
53 var which,
54 i = 0,
55 attrs = { height: type };
56
57 // If we include width, step value is 1 to do all cssExpand values,
58 // otherwise step value is 2 to skip over Left and Right
59 includeWidth = includeWidth ? 1 : 0;
60 for ( ; i < 4; i += 2 - includeWidth ) {
61 which = cssExpand[ i ];
62 attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
63 }
64
65 if ( includeWidth ) {
66 attrs.opacity = attrs.width = type;
67 }
68
69 return attrs;
70}
71
72function createTween( value, prop, animation ) {
73 var tween,
74 collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ),
75 index = 0,
76 length = collection.length;
77 for ( ; index < length; index++ ) {
78 if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) {
79
80 // We're done with this property
81 return tween;
82 }
83 }
84}
85
86function defaultPrefilter( elem, props, opts ) {
87 var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display,
88 isBox = "width" in props || "height" in props,
89 anim = this,
90 orig = {},
91 style = elem.style,
92 hidden = elem.nodeType && isHiddenWithinTree( elem ),
93 dataShow = dataPriv.get( elem, "fxshow" );
94
95 // Queue-skipping animations hijack the fx hooks
96 if ( !opts.queue ) {
97 hooks = jQuery._queueHooks( elem, "fx" );
98 if ( hooks.unqueued == null ) {
99 hooks.unqueued = 0;
100 oldfire = hooks.empty.fire;
101 hooks.empty.fire = function() {
102 if ( !hooks.unqueued ) {
103 oldfire();
104 }
105 };
106 }
107 hooks.unqueued++;
108
109 anim.always( function() {
110
111 // Ensure the complete handler is called before this completes
112 anim.always( function() {
113 hooks.unqueued--;
114 if ( !jQuery.queue( elem, "fx" ).length ) {
115 hooks.empty.fire();
116 }
117 } );
118 } );
119 }
120
121 // Detect show/hide animations
122 for ( prop in props ) {
123 value = props[ prop ];
124 if ( rfxtypes.test( value ) ) {
125 delete props[ prop ];
126 toggle = toggle || value === "toggle";
127 if ( value === ( hidden ? "hide" : "show" ) ) {
128
129 // Pretend to be hidden if this is a "show" and
130 // there is still data from a stopped show/hide
131 if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) {
132 hidden = true;
133
134 // Ignore all other no-op show/hide data
135 } else {
136 continue;
137 }
138 }
139 orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop );
140 }
141 }
142
143 // Bail out if this is a no-op like .hide().hide()
144 propTween = !jQuery.isEmptyObject( props );
145 if ( !propTween && jQuery.isEmptyObject( orig ) ) {
146 return;
147 }
148
149 // Restrict "overflow" and "display" styles during box animations
150 if ( isBox && elem.nodeType === 1 ) {
151
152 // Support: IE <=9 - 11, Edge 12 - 15
153 // Record all 3 overflow attributes because IE does not infer the shorthand
154 // from identically-valued overflowX and overflowY and Edge just mirrors
155 // the overflowX value there.
156 opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
157
158 // Identify a display type, preferring old show/hide data over the CSS cascade
159 restoreDisplay = dataShow && dataShow.display;
160 if ( restoreDisplay == null ) {
161 restoreDisplay = dataPriv.get( elem, "display" );
162 }
163 display = jQuery.css( elem, "display" );
164 if ( display === "none" ) {
165 if ( restoreDisplay ) {
166 display = restoreDisplay;
167 } else {
168
169 // Get nonempty value(s) by temporarily forcing visibility
170 showHide( [ elem ], true );
171 restoreDisplay = elem.style.display || restoreDisplay;
172 display = jQuery.css( elem, "display" );
173 showHide( [ elem ] );
174 }
175 }
176
177 // Animate inline elements as inline-block
178 if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) {
179 if ( jQuery.css( elem, "float" ) === "none" ) {
180
181 // Restore the original display value at the end of pure show/hide animations
182 if ( !propTween ) {
183 anim.done( function() {
184 style.display = restoreDisplay;
185 } );
186 if ( restoreDisplay == null ) {
187 display = style.display;
188 restoreDisplay = display === "none" ? "" : display;
189 }
190 }
191 style.display = "inline-block";
192 }
193 }
194 }
195
196 if ( opts.overflow ) {
197 style.overflow = "hidden";
198 anim.always( function() {
199 style.overflow = opts.overflow[ 0 ];
200 style.overflowX = opts.overflow[ 1 ];
201 style.overflowY = opts.overflow[ 2 ];
202 } );
203 }
204
205 // Implement show/hide animations
206 propTween = false;
207 for ( prop in orig ) {
208
209 // General show/hide setup for this element animation
210 if ( !propTween ) {
211 if ( dataShow ) {
212 if ( "hidden" in dataShow ) {
213 hidden = dataShow.hidden;
214 }
215 } else {
216 dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } );
217 }
218
219 // Store hidden/visible for toggle so `.stop().toggle()` "reverses"
220 if ( toggle ) {
221 dataShow.hidden = !hidden;
222 }
223
224 // Show elements before animating them
225 if ( hidden ) {
226 showHide( [ elem ], true );
227 }
228
229 /* eslint-disable no-loop-func */
230
231 anim.done( function() {
232
233 /* eslint-enable no-loop-func */
234
235 // The final step of a "hide" animation is actually hiding the element
236 if ( !hidden ) {
237 showHide( [ elem ] );
238 }
239 dataPriv.remove( elem, "fxshow" );
240 for ( prop in orig ) {
241 jQuery.style( elem, prop, orig[ prop ] );
242 }
243 } );
244 }
245
246 // Per-property setup
247 propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim );
248 if ( !( prop in dataShow ) ) {
249 dataShow[ prop ] = propTween.start;
250 if ( hidden ) {
251 propTween.end = propTween.start;
252 propTween.start = 0;
253 }
254 }
255 }
256}
257
258function propFilter( props, specialEasing ) {
259 var index, name, easing, value, hooks;
260
261 // camelCase, specialEasing and expand cssHook pass
262 for ( index in props ) {
263 name = camelCase( index );
264 easing = specialEasing[ name ];
265 value = props[ index ];
266 if ( Array.isArray( value ) ) {
267 easing = value[ 1 ];
268 value = props[ index ] = value[ 0 ];
269 }
270
271 if ( index !== name ) {
272 props[ name ] = value;
273 delete props[ index ];
274 }
275
276 hooks = jQuery.cssHooks[ name ];
277 if ( hooks && "expand" in hooks ) {
278 value = hooks.expand( value );
279 delete props[ name ];
280
281 // Not quite $.extend, this won't overwrite existing keys.
282 // Reusing 'index' because we have the correct "name"
283 for ( index in value ) {
284 if ( !( index in props ) ) {
285 props[ index ] = value[ index ];
286 specialEasing[ index ] = easing;
287 }
288 }
289 } else {
290 specialEasing[ name ] = easing;
291 }
292 }
293}
294
295function Animation( elem, properties, options ) {
296 var result,
297 stopped,
298 index = 0,
299 length = Animation.prefilters.length,
300 deferred = jQuery.Deferred().always( function() {
301
302 // Don't match elem in the :animated selector
303 delete tick.elem;
304 } ),
305 tick = function() {
306 if ( stopped ) {
307 return false;
308 }
309 var currentTime = fxNow || createFxNow(),
310 remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
311
312 // Support: Android 2.3 only
313 // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497)
314 temp = remaining / animation.duration || 0,
315 percent = 1 - temp,
316 index = 0,
317 length = animation.tweens.length;
318
319 for ( ; index < length; index++ ) {
320 animation.tweens[ index ].run( percent );
321 }
322
323 deferred.notifyWith( elem, [ animation, percent, remaining ] );
324
325 // If there's more to do, yield
326 if ( percent < 1 && length ) {
327 return remaining;
328 }
329
330 // If this was an empty animation, synthesize a final progress notification
331 if ( !length ) {
332 deferred.notifyWith( elem, [ animation, 1, 0 ] );
333 }
334
335 // Resolve the animation and report its conclusion
336 deferred.resolveWith( elem, [ animation ] );
337 return false;
338 },
339 animation = deferred.promise( {
340 elem: elem,
341 props: jQuery.extend( {}, properties ),
342 opts: jQuery.extend( true, {
343 specialEasing: {},
344 easing: jQuery.easing._default
345 }, options ),
346 originalProperties: properties,
347 originalOptions: options,
348 startTime: fxNow || createFxNow(),
349 duration: options.duration,
350 tweens: [],
351 createTween: function( prop, end ) {
352 var tween = jQuery.Tween( elem, animation.opts, prop, end,
353 animation.opts.specialEasing[ prop ] || animation.opts.easing );
354 animation.tweens.push( tween );
355 return tween;
356 },
357 stop: function( gotoEnd ) {
358 var index = 0,
359
360 // If we are going to the end, we want to run all the tweens
361 // otherwise we skip this part
362 length = gotoEnd ? animation.tweens.length : 0;
363 if ( stopped ) {
364 return this;
365 }
366 stopped = true;
367 for ( ; index < length; index++ ) {
368 animation.tweens[ index ].run( 1 );
369 }
370
371 // Resolve when we played the last frame; otherwise, reject
372 if ( gotoEnd ) {
373 deferred.notifyWith( elem, [ animation, 1, 0 ] );
374 deferred.resolveWith( elem, [ animation, gotoEnd ] );
375 } else {
376 deferred.rejectWith( elem, [ animation, gotoEnd ] );
377 }
378 return this;
379 }
380 } ),
381 props = animation.props;
382
383 propFilter( props, animation.opts.specialEasing );
384
385 for ( ; index < length; index++ ) {
386 result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts );
387 if ( result ) {
388 if ( isFunction( result.stop ) ) {
389 jQuery._queueHooks( animation.elem, animation.opts.queue ).stop =
390 result.stop.bind( result );
391 }
392 return result;
393 }
394 }
395
396 jQuery.map( props, createTween, animation );
397
398 if ( isFunction( animation.opts.start ) ) {
399 animation.opts.start.call( elem, animation );
400 }
401
402 // Attach callbacks from options
403 animation
404 .progress( animation.opts.progress )
405 .done( animation.opts.done, animation.opts.complete )
406 .fail( animation.opts.fail )
407 .always( animation.opts.always );
408
409 jQuery.fx.timer(
410 jQuery.extend( tick, {
411 elem: elem,
412 anim: animation,
413 queue: animation.opts.queue
414 } )
415 );
416
417 return animation;
418}
419
420jQuery.Animation = jQuery.extend( Animation, {
421
422 tweeners: {
423 "*": [ function( prop, value ) {
424 var tween = this.createTween( prop, value );
425 adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween );
426 return tween;
427 } ]
428 },
429
430 tweener: function( props, callback ) {
431 if ( isFunction( props ) ) {
432 callback = props;
433 props = [ "*" ];
434 } else {
435 props = props.match( rnothtmlwhite );
436 }
437
438 var prop,
439 index = 0,
440 length = props.length;
441
442 for ( ; index < length; index++ ) {
443 prop = props[ index ];
444 Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || [];
445 Animation.tweeners[ prop ].unshift( callback );
446 }
447 },
448
449 prefilters: [ defaultPrefilter ],
450
451 prefilter: function( callback, prepend ) {
452 if ( prepend ) {
453 Animation.prefilters.unshift( callback );
454 } else {
455 Animation.prefilters.push( callback );
456 }
457 }
458} );
459
460jQuery.speed = function( speed, easing, fn ) {
461 var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
462 complete: fn || !fn && easing ||
463 isFunction( speed ) && speed,
464 duration: speed,
465 easing: fn && easing || easing && !isFunction( easing ) && easing
466 };
467
468 // Go to the end state if fx are off
469 if ( jQuery.fx.off ) {
470 opt.duration = 0;
471
472 } else {
473 if ( typeof opt.duration !== "number" ) {
474 if ( opt.duration in jQuery.fx.speeds ) {
475 opt.duration = jQuery.fx.speeds[ opt.duration ];
476
477 } else {
478 opt.duration = jQuery.fx.speeds._default;
479 }
480 }
481 }
482
483 // Normalize opt.queue - true/undefined/null -> "fx"
484 if ( opt.queue == null || opt.queue === true ) {
485 opt.queue = "fx";
486 }
487
488 // Queueing
489 opt.old = opt.complete;
490
491 opt.complete = function() {
492 if ( isFunction( opt.old ) ) {
493 opt.old.call( this );
494 }
495
496 if ( opt.queue ) {
497 jQuery.dequeue( this, opt.queue );
498 }
499 };
500
501 return opt;
502};
503
504jQuery.fn.extend( {
505 fadeTo: function( speed, to, easing, callback ) {
506
507 // Show any hidden elements after setting opacity to 0
508 return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show()
509
510 // Animate to the value specified
511 .end().animate( { opacity: to }, speed, easing, callback );
512 },
513 animate: function( prop, speed, easing, callback ) {
514 var empty = jQuery.isEmptyObject( prop ),
515 optall = jQuery.speed( speed, easing, callback ),
516 doAnimation = function() {
517
518 // Operate on a copy of prop so per-property easing won't be lost
519 var anim = Animation( this, jQuery.extend( {}, prop ), optall );
520
521 // Empty animations, or finishing resolves immediately
522 if ( empty || dataPriv.get( this, "finish" ) ) {
523 anim.stop( true );
524 }
525 };
526 doAnimation.finish = doAnimation;
527
528 return empty || optall.queue === false ?
529 this.each( doAnimation ) :
530 this.queue( optall.queue, doAnimation );
531 },
532 stop: function( type, clearQueue, gotoEnd ) {
533 var stopQueue = function( hooks ) {
534 var stop = hooks.stop;
535 delete hooks.stop;
536 stop( gotoEnd );
537 };
538
539 if ( typeof type !== "string" ) {
540 gotoEnd = clearQueue;
541 clearQueue = type;
542 type = undefined;
543 }
544 if ( clearQueue ) {
545 this.queue( type || "fx", [] );
546 }
547
548 return this.each( function() {
549 var dequeue = true,
550 index = type != null && type + "queueHooks",
551 timers = jQuery.timers,
552 data = dataPriv.get( this );
553
554 if ( index ) {
555 if ( data[ index ] && data[ index ].stop ) {
556 stopQueue( data[ index ] );
557 }
558 } else {
559 for ( index in data ) {
560 if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
561 stopQueue( data[ index ] );
562 }
563 }
564 }
565
566 for ( index = timers.length; index--; ) {
567 if ( timers[ index ].elem === this &&
568 ( type == null || timers[ index ].queue === type ) ) {
569
570 timers[ index ].anim.stop( gotoEnd );
571 dequeue = false;
572 timers.splice( index, 1 );
573 }
574 }
575
576 // Start the next in the queue if the last step wasn't forced.
577 // Timers currently will call their complete callbacks, which
578 // will dequeue but only if they were gotoEnd.
579 if ( dequeue || !gotoEnd ) {
580 jQuery.dequeue( this, type );
581 }
582 } );
583 },
584 finish: function( type ) {
585 if ( type !== false ) {
586 type = type || "fx";
587 }
588 return this.each( function() {
589 var index,
590 data = dataPriv.get( this ),
591 queue = data[ type + "queue" ],
592 hooks = data[ type + "queueHooks" ],
593 timers = jQuery.timers,
594 length = queue ? queue.length : 0;
595
596 // Enable finishing flag on private data
597 data.finish = true;
598
599 // Empty the queue first
600 jQuery.queue( this, type, [] );
601
602 if ( hooks && hooks.stop ) {
603 hooks.stop.call( this, true );
604 }
605
606 // Look for any active animations, and finish them
607 for ( index = timers.length; index--; ) {
608 if ( timers[ index ].elem === this && timers[ index ].queue === type ) {
609 timers[ index ].anim.stop( true );
610 timers.splice( index, 1 );
611 }
612 }
613
614 // Look for any animations in the old queue and finish them
615 for ( index = 0; index < length; index++ ) {
616 if ( queue[ index ] && queue[ index ].finish ) {
617 queue[ index ].finish.call( this );
618 }
619 }
620
621 // Turn off finishing flag
622 delete data.finish;
623 } );
624 }
625} );
626
627jQuery.each( [ "toggle", "show", "hide" ], function( _i, name ) {
628 var cssFn = jQuery.fn[ name ];
629 jQuery.fn[ name ] = function( speed, easing, callback ) {
630 return speed == null || typeof speed === "boolean" ?
631 cssFn.apply( this, arguments ) :
632 this.animate( genFx( name, true ), speed, easing, callback );
633 };
634} );
635
636// Generate shortcuts for custom animations
637jQuery.each( {
638 slideDown: genFx( "show" ),
639 slideUp: genFx( "hide" ),
640 slideToggle: genFx( "toggle" ),
641 fadeIn: { opacity: "show" },
642 fadeOut: { opacity: "hide" },
643 fadeToggle: { opacity: "toggle" }
644}, function( name, props ) {
645 jQuery.fn[ name ] = function( speed, easing, callback ) {
646 return this.animate( props, speed, easing, callback );
647 };
648} );
649
650jQuery.timers = [];
651jQuery.fx.tick = function() {
652 var timer,
653 i = 0,
654 timers = jQuery.timers;
655
656 fxNow = Date.now();
657
658 for ( ; i < timers.length; i++ ) {
659 timer = timers[ i ];
660
661 // Run the timer and safely remove it when done (allowing for external removal)
662 if ( !timer() && timers[ i ] === timer ) {
663 timers.splice( i--, 1 );
664 }
665 }
666
667 if ( !timers.length ) {
668 jQuery.fx.stop();
669 }
670 fxNow = undefined;
671};
672
673jQuery.fx.timer = function( timer ) {
674 jQuery.timers.push( timer );
675 jQuery.fx.start();
676};
677
678jQuery.fx.interval = 13;
679jQuery.fx.start = function() {
680 if ( inProgress ) {
681 return;
682 }
683
684 inProgress = true;
685 schedule();
686};
687
688jQuery.fx.stop = function() {
689 inProgress = null;
690};
691
692jQuery.fx.speeds = {
693 slow: 600,
694 fast: 200,
695
696 // Default speed
697 _default: 400
698};
699
700return jQuery;
701} );