effects.js (17417B)
1 define( [ 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 26 var 27 fxNow, inProgress, 28 rfxtypes = /^(?:toggle|show|hide)$/, 29 rrun = /queueHooks$/; 30 31 function 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 44 function createFxNow() { 45 window.setTimeout( function() { 46 fxNow = undefined; 47 } ); 48 return ( fxNow = Date.now() ); 49 } 50 51 // Generate parameters to create a standard animation 52 function 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 72 function 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 86 function 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 258 function 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 295 function 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 )` (trac-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 420 jQuery.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 460 jQuery.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 504 jQuery.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 527 doAnimation.finish = doAnimation; 528 529 return empty || optall.queue === false ? 530 this.each( doAnimation ) : 531 this.queue( optall.queue, doAnimation ); 532 }, 533 stop: function( type, clearQueue, gotoEnd ) { 534 var stopQueue = function( hooks ) { 535 var stop = hooks.stop; 536 delete hooks.stop; 537 stop( gotoEnd ); 538 }; 539 540 if ( typeof type !== "string" ) { 541 gotoEnd = clearQueue; 542 clearQueue = type; 543 type = undefined; 544 } 545 if ( clearQueue ) { 546 this.queue( type || "fx", [] ); 547 } 548 549 return this.each( function() { 550 var dequeue = true, 551 index = type != null && type + "queueHooks", 552 timers = jQuery.timers, 553 data = dataPriv.get( this ); 554 555 if ( index ) { 556 if ( data[ index ] && data[ index ].stop ) { 557 stopQueue( data[ index ] ); 558 } 559 } else { 560 for ( index in data ) { 561 if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { 562 stopQueue( data[ index ] ); 563 } 564 } 565 } 566 567 for ( index = timers.length; index--; ) { 568 if ( timers[ index ].elem === this && 569 ( type == null || timers[ index ].queue === type ) ) { 570 571 timers[ index ].anim.stop( gotoEnd ); 572 dequeue = false; 573 timers.splice( index, 1 ); 574 } 575 } 576 577 // Start the next in the queue if the last step wasn't forced. 578 // Timers currently will call their complete callbacks, which 579 // will dequeue but only if they were gotoEnd. 580 if ( dequeue || !gotoEnd ) { 581 jQuery.dequeue( this, type ); 582 } 583 } ); 584 }, 585 finish: function( type ) { 586 if ( type !== false ) { 587 type = type || "fx"; 588 } 589 return this.each( function() { 590 var index, 591 data = dataPriv.get( this ), 592 queue = data[ type + "queue" ], 593 hooks = data[ type + "queueHooks" ], 594 timers = jQuery.timers, 595 length = queue ? queue.length : 0; 596 597 // Enable finishing flag on private data 598 data.finish = true; 599 600 // Empty the queue first 601 jQuery.queue( this, type, [] ); 602 603 if ( hooks && hooks.stop ) { 604 hooks.stop.call( this, true ); 605 } 606 607 // Look for any active animations, and finish them 608 for ( index = timers.length; index--; ) { 609 if ( timers[ index ].elem === this && timers[ index ].queue === type ) { 610 timers[ index ].anim.stop( true ); 611 timers.splice( index, 1 ); 612 } 613 } 614 615 // Look for any animations in the old queue and finish them 616 for ( index = 0; index < length; index++ ) { 617 if ( queue[ index ] && queue[ index ].finish ) { 618 queue[ index ].finish.call( this ); 619 } 620 } 621 622 // Turn off finishing flag 623 delete data.finish; 624 } ); 625 } 626 } ); 627 628 jQuery.each( [ "toggle", "show", "hide" ], function( _i, name ) { 629 var cssFn = jQuery.fn[ name ]; 630 jQuery.fn[ name ] = function( speed, easing, callback ) { 631 return speed == null || typeof speed === "boolean" ? 632 cssFn.apply( this, arguments ) : 633 this.animate( genFx( name, true ), speed, easing, callback ); 634 }; 635 } ); 636 637 // Generate shortcuts for custom animations 638 jQuery.each( { 639 slideDown: genFx( "show" ), 640 slideUp: genFx( "hide" ), 641 slideToggle: genFx( "toggle" ), 642 fadeIn: { opacity: "show" }, 643 fadeOut: { opacity: "hide" }, 644 fadeToggle: { opacity: "toggle" } 645 }, function( name, props ) { 646 jQuery.fn[ name ] = function( speed, easing, callback ) { 647 return this.animate( props, speed, easing, callback ); 648 }; 649 } ); 650 651 jQuery.timers = []; 652 jQuery.fx.tick = function() { 653 var timer, 654 i = 0, 655 timers = jQuery.timers; 656 657 fxNow = Date.now(); 658 659 for ( ; i < timers.length; i++ ) { 660 timer = timers[ i ]; 661 662 // Run the timer and safely remove it when done (allowing for external removal) 663 if ( !timer() && timers[ i ] === timer ) { 664 timers.splice( i--, 1 ); 665 } 666 } 667 668 if ( !timers.length ) { 669 jQuery.fx.stop(); 670 } 671 fxNow = undefined; 672 }; 673 674 jQuery.fx.timer = function( timer ) { 675 jQuery.timers.push( timer ); 676 jQuery.fx.start(); 677 }; 678 679 jQuery.fx.interval = 13; 680 jQuery.fx.start = function() { 681 if ( inProgress ) { 682 return; 683 } 684 685 inProgress = true; 686 schedule(); 687 }; 688 689 jQuery.fx.stop = function() { 690 inProgress = null; 691 }; 692 693 jQuery.fx.speeds = { 694 slow: 600, 695 fast: 200, 696 697 // Default speed 698 _default: 400 699 }; 700 701 return jQuery; 702 } );