1/** 2 * @license AngularJS v1.2.24 3 * (c) 2010-2014 Google, Inc. http://angularjs.org 4 * License: MIT 5 */ 6(function(window, angular, undefined) {'use strict'; 7 8/* jshint maxlen: false */ 9 10/** 11 * @ngdoc module 12 * @name ngAnimate 13 * @description 14 * 15 * # ngAnimate 16 * 17 * The `ngAnimate` module provides support for JavaScript, CSS3 transition and CSS3 keyframe animation hooks within existing core and custom directives. 18 * 19 * 20 * <div doc-module-components="ngAnimate"></div> 21 * 22 * # Usage 23 * 24 * To see animations in action, all that is required is to define the appropriate CSS classes 25 * or to register a JavaScript animation via the myModule.animation() function. The directives that support animation automatically are: 26 * `ngRepeat`, `ngInclude`, `ngIf`, `ngSwitch`, `ngShow`, `ngHide`, `ngView` and `ngClass`. Custom directives can take advantage of animation 27 * by using the `$animate` service. 28 * 29 * Below is a more detailed breakdown of the supported animation events provided by pre-existing ng directives: 30 * 31 * | Directive | Supported Animations | 32 * |---------------------------------------------------------- |----------------------------------------------------| 33 * | {@link ng.directive:ngRepeat#usage_animations ngRepeat} | enter, leave and move | 34 * | {@link ngRoute.directive:ngView#usage_animations ngView} | enter and leave | 35 * | {@link ng.directive:ngInclude#usage_animations ngInclude} | enter and leave | 36 * | {@link ng.directive:ngSwitch#usage_animations ngSwitch} | enter and leave | 37 * | {@link ng.directive:ngIf#usage_animations ngIf} | enter and leave | 38 * | {@link ng.directive:ngClass#usage_animations ngClass} | add and remove | 39 * | {@link ng.directive:ngShow#usage_animations ngShow & ngHide} | add and remove (the ng-hide class value) | 40 * | {@link ng.directive:form#usage_animations form} | add and remove (dirty, pristine, valid, invalid & all other validations) | 41 * | {@link ng.directive:ngModel#usage_animations ngModel} | add and remove (dirty, pristine, valid, invalid & all other validations) | 42 * 43 * You can find out more information about animations upon visiting each directive page. 44 * 45 * Below is an example of how to apply animations to a directive that supports animation hooks: 46 * 47 * ```html 48 * <style type="text/css"> 49 * .slide.ng-enter, .slide.ng-leave { 50 * -webkit-transition:0.5s linear all; 51 * transition:0.5s linear all; 52 * } 53 * 54 * .slide.ng-enter { } /* starting animations for enter */ 55 * .slide.ng-enter.ng-enter-active { } /* terminal animations for enter */ 56 * .slide.ng-leave { } /* starting animations for leave */ 57 * .slide.ng-leave.ng-leave-active { } /* terminal animations for leave */ 58 * </style> 59 * 60 * <!-- 61 * the animate service will automatically add .ng-enter and .ng-leave to the element 62 * to trigger the CSS transition/animations 63 * --> 64 * <ANY class="slide" ng-include="..."></ANY> 65 * ``` 66 * 67 * Keep in mind that, by default, if an animation is running, any child elements cannot be animated 68 * until the parent element's animation has completed. This blocking feature can be overridden by 69 * placing the `ng-animate-children` attribute on a parent container tag. 70 * 71 * ```html 72 * <div class="slide-animation" ng-if="on" ng-animate-children> 73 * <div class="fade-animation" ng-if="on"> 74 * <div class="explode-animation" ng-if="on"> 75 * ... 76 * </div> 77 * </div> 78 * </div> 79 * ``` 80 * 81 * When the `on` expression value changes and an animation is triggered then each of the elements within 82 * will all animate without the block being applied to child elements. 83 * 84 * <h2>CSS-defined Animations</h2> 85 * The animate service will automatically apply two CSS classes to the animated element and these two CSS classes 86 * are designed to contain the start and end CSS styling. Both CSS transitions and keyframe animations are supported 87 * and can be used to play along with this naming structure. 88 * 89 * The following code below demonstrates how to perform animations using **CSS transitions** with Angular: 90 * 91 * ```html 92 * <style type="text/css"> 93 * /* 94 * The animate class is apart of the element and the ng-enter class 95 * is attached to the element once the enter animation event is triggered 96 * */ 97 * .reveal-animation.ng-enter { 98 * -webkit-transition: 1s linear all; /* Safari/Chrome */ 99 * transition: 1s linear all; /* All other modern browsers and IE10+ */ 100 * 101 * /* The animation preparation code */ 102 * opacity: 0; 103 * } 104 * 105 * /* 106 * Keep in mind that you want to combine both CSS 107 * classes together to avoid any CSS-specificity 108 * conflicts 109 * */ 110 * .reveal-animation.ng-enter.ng-enter-active { 111 * /* The animation code itself */ 112 * opacity: 1; 113 * } 114 * </style> 115 * 116 * <div class="view-container"> 117 * <div ng-view class="reveal-animation"></div> 118 * </div> 119 * ``` 120 * 121 * The following code below demonstrates how to perform animations using **CSS animations** with Angular: 122 * 123 * ```html 124 * <style type="text/css"> 125 * .reveal-animation.ng-enter { 126 * -webkit-animation: enter_sequence 1s linear; /* Safari/Chrome */ 127 * animation: enter_sequence 1s linear; /* IE10+ and Future Browsers */ 128 * } 129 * @-webkit-keyframes enter_sequence { 130 * from { opacity:0; } 131 * to { opacity:1; } 132 * } 133 * @keyframes enter_sequence { 134 * from { opacity:0; } 135 * to { opacity:1; } 136 * } 137 * </style> 138 * 139 * <div class="view-container"> 140 * <div ng-view class="reveal-animation"></div> 141 * </div> 142 * ``` 143 * 144 * Both CSS3 animations and transitions can be used together and the animate service will figure out the correct duration and delay timing. 145 * 146 * Upon DOM mutation, the event class is added first (something like `ng-enter`), then the browser prepares itself to add 147 * the active class (in this case `ng-enter-active`) which then triggers the animation. The animation module will automatically 148 * detect the CSS code to determine when the animation ends. Once the animation is over then both CSS classes will be 149 * removed from the DOM. If a browser does not support CSS transitions or CSS animations then the animation will start and end 150 * immediately resulting in a DOM element that is at its final state. This final state is when the DOM element 151 * has no CSS transition/animation classes applied to it. 152 * 153 * <h3>CSS Staggering Animations</h3> 154 * A Staggering animation is a collection of animations that are issued with a slight delay in between each successive operation resulting in a 155 * curtain-like effect. The ngAnimate module, as of 1.2.0, supports staggering animations and the stagger effect can be 156 * performed by creating a **ng-EVENT-stagger** CSS class and attaching that class to the base CSS class used for 157 * the animation. The style property expected within the stagger class can either be a **transition-delay** or an 158 * **animation-delay** property (or both if your animation contains both transitions and keyframe animations). 159 * 160 * ```css 161 * .my-animation.ng-enter { 162 * /* standard transition code */ 163 * -webkit-transition: 1s linear all; 164 * transition: 1s linear all; 165 * opacity:0; 166 * } 167 * .my-animation.ng-enter-stagger { 168 * /* this will have a 100ms delay between each successive leave animation */ 169 * -webkit-transition-delay: 0.1s; 170 * transition-delay: 0.1s; 171 * 172 * /* in case the stagger doesn't work then these two values 173 * must be set to 0 to avoid an accidental CSS inheritance */ 174 * -webkit-transition-duration: 0s; 175 * transition-duration: 0s; 176 * } 177 * .my-animation.ng-enter.ng-enter-active { 178 * /* standard transition styles */ 179 * opacity:1; 180 * } 181 * ``` 182 * 183 * Staggering animations work by default in ngRepeat (so long as the CSS class is defined). Outside of ngRepeat, to use staggering animations 184 * on your own, they can be triggered by firing multiple calls to the same event on $animate. However, the restrictions surrounding this 185 * are that each of the elements must have the same CSS className value as well as the same parent element. A stagger operation 186 * will also be reset if more than 10ms has passed after the last animation has been fired. 187 * 188 * The following code will issue the **ng-leave-stagger** event on the element provided: 189 * 190 * ```js 191 * var kids = parent.children(); 192 * 193 * $animate.leave(kids[0]); //stagger index=0 194 * $animate.leave(kids[1]); //stagger index=1 195 * $animate.leave(kids[2]); //stagger index=2 196 * $animate.leave(kids[3]); //stagger index=3 197 * $animate.leave(kids[4]); //stagger index=4 198 * 199 * $timeout(function() { 200 * //stagger has reset itself 201 * $animate.leave(kids[5]); //stagger index=0 202 * $animate.leave(kids[6]); //stagger index=1 203 * }, 100, false); 204 * ``` 205 * 206 * Stagger animations are currently only supported within CSS-defined animations. 207 * 208 * <h2>JavaScript-defined Animations</h2> 209 * In the event that you do not want to use CSS3 transitions or CSS3 animations or if you wish to offer animations on browsers that do not 210 * yet support CSS transitions/animations, then you can make use of JavaScript animations defined inside of your AngularJS module. 211 * 212 * ```js 213 * //!annotate="YourApp" Your AngularJS Module|Replace this or ngModule with the module that you used to define your application. 214 * var ngModule = angular.module('YourApp', ['ngAnimate']); 215 * ngModule.animation('.my-crazy-animation', function() { 216 * return { 217 * enter: function(element, done) { 218 * //run the animation here and call done when the animation is complete 219 * return function(cancelled) { 220 * //this (optional) function will be called when the animation 221 * //completes or when the animation is cancelled (the cancelled 222 * //flag will be set to true if cancelled). 223 * }; 224 * }, 225 * leave: function(element, done) { }, 226 * move: function(element, done) { }, 227 * 228 * //animation that can be triggered before the class is added 229 * beforeAddClass: function(element, className, done) { }, 230 * 231 * //animation that can be triggered after the class is added 232 * addClass: function(element, className, done) { }, 233 * 234 * //animation that can be triggered before the class is removed 235 * beforeRemoveClass: function(element, className, done) { }, 236 * 237 * //animation that can be triggered after the class is removed 238 * removeClass: function(element, className, done) { } 239 * }; 240 * }); 241 * ``` 242 * 243 * JavaScript-defined animations are created with a CSS-like class selector and a collection of events which are set to run 244 * a javascript callback function. When an animation is triggered, $animate will look for a matching animation which fits 245 * the element's CSS class attribute value and then run the matching animation event function (if found). 246 * In other words, if the CSS classes present on the animated element match any of the JavaScript animations then the callback function will 247 * be executed. It should be also noted that only simple, single class selectors are allowed (compound class selectors are not supported). 248 *
249 * Within a JavaScript animation, an object containing various event callback animation functions is expected to be returned. 250 * As explained above, these callbacks are triggered based on the animation event. Therefore if an enter animation is run, 251 * and the JavaScript animation is found, then the enter callback will handle that animation (in addition to the CSS keyframe animation 252 * or transition code that is defined via a stylesheet). 253 * 254 */ 255 256angular.module('ngAnimate', ['ng']) 257 258 /** 259 * @ngdoc provider 260 * @name $animateProvider 261 * @description 262 * 263 * The `$animateProvider` allows developers to register JavaScript animation event handlers directly inside of a module. 264 * When an animation is triggered, the $animate service will query the $animate service to find any animations that match 265 * the provided name value. 266 * 267 * Requires the {@link ngAnimate `ngAnimate`} module to be installed. 268 * 269 * Please visit the {@link ngAnimate `ngAnimate`} module overview page learn more about how to use animations in your application. 270 * 271 */ 272 .directive('ngAnimateChildren', function() { 273 var NG_ANIMATE_CHILDREN = '$$ngAnimateChildren'; 274 return function(scope, element, attrs) { 275 var val = attrs.ngAnimateChildren; 276 if(angular.isString(val) && val.length === 0) { //empty attribute 277 element.data(NG_ANIMATE_CHILDREN, true); 278 } else { 279 scope.$watch(val, function(value) { 280 element.data(NG_ANIMATE_CHILDREN, !!value); 281 }); 282 } 283 }; 284 }) 285 286 //this private service is only used within CSS-enabled animations 287 //IE8 + IE9 do not support rAF natively, but that is fine since they 288 //also don't support transitions and keyframes which means that the code 289 //below will never be used by the two browsers. 290 .factory('$$animateReflow', ['$$rAF', '$document', function($$rAF, $document) { 291 var bod = $document[0].body; 292 return function(fn) { 293 //the returned function acts as the cancellation function 294 return $$rAF(function() { 295 //the line below will force the browser to perform a repaint 296 //so that all the animated elements within the animation frame 297 //will be properly updated and drawn on screen. This is 298 //required to perform multi-class CSS based animations with 299 //Firefox. DO NOT REMOVE THIS LINE. 300 var a = bod.offsetWidth + 1; 301 fn(); 302 }); 303 }; 304 }]) 305 306 .config(['$provide', '$animateProvider', function($provide, $animateProvider) { 307 var noop = angular.noop; 308 var forEach = angular.forEach; 309 var selectors = $animateProvider.$$selectors; 310 311 var ELEMENT_NODE = 1; 312 var NG_ANIMATE_STATE = '$$ngAnimateState'; 313 var NG_ANIMATE_CHILDREN = '$$ngAnimateChildren'; 314 var NG_ANIMATE_CLASS_NAME = 'ng-animate'; 315 var rootAnimateState = {running: true}; 316 317 function extractElementNode(element) { 318 for(var i = 0; i < element.length; i++) { 319 var elm = element[i]; 320 if(elm.nodeType == ELEMENT_NODE) { 321 return elm; 322 } 323 } 324 } 325 326 function prepareElement(element) { 327 return element && angular.element(element); 328 } 329 330 function stripCommentsFromElement(element) { 331 return angular.element(extractElementNode(element)); 332 } 333 334 function isMatchingElement(elm1, elm2) { 335 return extractElementNode(elm1) == extractElementNode(elm2); 336 } 337 338 $provide.decorator('$animate', ['$delegate', '$injector', '$sniffer', '$rootElement', '$$asyncCallback', '$rootScope', '$document', 339 function($delegate, $injector, $sniffer, $rootElement, $$asyncCallback, $rootScope, $document) { 340 341 var globalAnimationCounter = 0; 342 $rootElement.data(NG_ANIMATE_STATE, rootAnimateState); 343 344 // disable animations during bootstrap, but once we bootstrapped, wait again 345 // for another digest until enabling animations. The reason why we digest twice 346 // is because all structural animations (enter, leave and move) all perform a 347 // post digest operation before animating. If we only wait for a single digest 348 // to pass then the structural animation would render its animation on page load. 349 // (which is what we're trying to avoid when the application first boots up.) 350 $rootScope.$$postDigest(function() { 351 $rootScope.$$postDigest(function() { 352 rootAnimateState.running = false;
353 }); 354 }); 355 356 var classNameFilter = $animateProvider.classNameFilter(); 357 var isAnimatableClassName = !classNameFilter 358 ? function() { return true; } 359 : function(className) { 360 return classNameFilter.test(className); 361 }; 362 363 function blockElementAnimations(element) { 364 var data = element.data(NG_ANIMATE_STATE) || {}; 365 data.running = true; 366 element.data(NG_ANIMATE_STATE, data); 367 } 368 369 function lookup(name) { 370 if (name) { 371 var matches = [], 372 flagMap = {}, 373 classes = name.substr(1).split('.'); 374 375 //the empty string value is the default animation 376 //operation which performs CSS transition and keyframe 377 //animations sniffing. This is always included for each 378 //element animation procedure if the browser supports 379 //transitions and/or keyframe animations. The default 380 //animation is added to the top of the list to prevent 381 //any previous animations from affecting the element styling 382 //prior to the element being animated. 383 if ($sniffer.transitions || $sniffer.animations) { 384 matches.push($injector.get(selectors[''])); 385 } 386 387 for(var i=0; i < classes.length; i++) { 388 var klass = classes[i], 389 selectorFactoryName = selectors[klass]; 390 if(selectorFactoryName && !flagMap[klass]) { 391 matches.push($injector.get(selectorFactoryName)); 392 flagMap[klass] = true; 393 } 394 } 395 return matches; 396 } 397 } 398 399 function animationRunner(element, animationEvent, className) { 400 //transcluded directives may sometimes fire an animation using only comment nodes 401 //best to catch this early on to prevent any animation operations from occurring 402 var node = element[0]; 403 if(!node) { 404 return; 405 } 406 407 var isSetClassOperation = animationEvent == 'setClass'; 408 var isClassBased = isSetClassOperation || 409 animationEvent == 'addClass' || 410 animationEvent == 'removeClass'; 411 412 var classNameAdd, classNameRemove; 413 if(angular.isArray(className)) { 414 classNameAdd = className[0]; 415 classNameRemove = className[1]; 416 className = classNameAdd + ' ' + classNameRemove; 417 } 418 419 var currentClassName = element.attr('class'); 420 var classes = currentClassName + ' ' + className; 421 if(!isAnimatableClassName(classes)) { 422 return; 423 } 424 425 var beforeComplete = noop, 426 beforeCancel = [], 427 before = [], 428 afterComplete = noop, 429 afterCancel = [], 430 after = []; 431 432 var animationLookup = (' ' + classes).replace(/\s+/g,'.'); 433 forEach(lookup(animationLookup), function(animationFactory) { 434 var created = registerAnimation(animationFactory, animationEvent); 435 if(!created && isSetClassOperation) { 436 registerAnimation(animationFactory, 'addClass'); 437 registerAnimation(animationFactory, 'removeClass'); 438 } 439 }); 440 441 function registerAnimation(animationFactory, event) { 442 var afterFn = animationFactory[event]; 443 var beforeFn = animationFactory['before' + event.charAt(0).toUpperCase() + event.substr(1)]; 444 if(afterFn || beforeFn) { 445 if(event == 'leave') { 446 beforeFn = afterFn; 447 //when set as null then animation knows to skip this phase 448 afterFn = null; 449 } 450 after.push({ 451 event : event, fn : afterFn 452 }); 453 before.push({ 454 event : event, fn : beforeFn 455 }); 456 return true; 457 } 458 } 459 460 function run(fns, cancellations, allCompleteFn) { 461 var animations = []; 462 forEach(fns, function(animation) { 463 animation.fn && animations.push(animation); 464 }); 465 466 var count = 0; 467 function afterAnimationComplete(index) { 468 if(cancellations) { 469 (cancellations[index] || noop)(); 470 if(++count < animations.length) return; 471 cancellations = null; 472 } 473 allCompleteFn(); 474 } 475 476 //The code below adds directly to the array in order to work with 477 //both sync and async animations. Sync animations are when the done() 478 //operation is called right away. DO NOT REFACTOR!
479 forEach(animations, function(animation, index) { 480 var progress = function() { 481 afterAnimationComplete(index); 482 }; 483 switch(animation.event) { 484 case 'setClass': 485 cancellations.push(animation.fn(element, classNameAdd, classNameRemove, progress)); 486 break; 487 case 'addClass': 488 cancellations.push(animation.fn(element, classNameAdd || className, progress)); 489 break; 490 case 'removeClass': 491 cancellations.push(animation.fn(element, classNameRemove || className, progress)); 492 break; 493 default: 494 cancellations.push(animation.fn(element, progress)); 495 break; 496 } 497 }); 498 499 if(cancellations && cancellations.length === 0) { 500 allCompleteFn(); 501 } 502 } 503 504 return { 505 node : node, 506 event : animationEvent, 507 className : className, 508 isClassBased : isClassBased, 509 isSetClassOperation : isSetClassOperation, 510 before : function(allCompleteFn) { 511 beforeComplete = allCompleteFn; 512 run(before, beforeCancel, function() { 513 beforeComplete = noop; 514 allCompleteFn(); 515 }); 516 }, 517 after : function(allCompleteFn) { 518 afterComplete = allCompleteFn; 519 run(after, afterCancel, function() { 520 afterComplete = noop; 521 allCompleteFn(); 522 }); 523 }, 524 cancel : function() { 525 if(beforeCancel) { 526 forEach(beforeCancel, function(cancelFn) { 527 (cancelFn || noop)(true); 528 }); 529 beforeComplete(true); 530 } 531 if(afterCancel) { 532 forEach(afterCancel, function(cancelFn) { 533 (cancelFn || noop)(true); 534 }); 535 afterComplete(true); 536 } 537 } 538 }; 539 } 540 541 /** 542 * @ngdoc service 543 * @name $animate 544 * @kind function 545 * 546 * @description 547 * The `$animate` service provides animation detection support while performing DOM operations (enter, leave and move) as well as during addClass and removeClass operations. 548 * When any of these operations are run, the $animate service 549 * will examine any JavaScript-defined animations (which are defined by using the $animateProvider provider object) 550 * as well as any CSS-defined animations against the CSS classes present on the element once the DOM operation is run. 551 * 552 * The `$animate` service is used behind the scenes with pre-existing directives and animation with these directives 553 * will work out of the box without any extra configuration. 554 * 555 * Requires the {@link ngAnimate `ngAnimate`} module to be installed. 556 * 557 * Please visit the {@link ngAnimate `ngAnimate`} module overview page learn more about how to use animations in your application. 558 * 559 */ 560 return { 561 /** 562 * @ngdoc method 563 * @name $animate#enter 564 * @kind function 565 * 566 * @description 567 * Appends the element to the parentElement element that resides in the document and then runs the enter animation. Once 568 * the animation is started, the following CSS classes will be present on the element for the duration of the animation: 569 * 570 * Below is a breakdown of each step that occurs during enter animation: 571 * 572 * | Animation Step | What the element class attribute looks like | 573 * |----------------------------------------------------------------------------------------------|---------------------------------------------| 574 * | 1. $animate.enter(...) is called | class="my-animation" | 575 * | 2. element is inserted into the parentElement element or beside the afterElement element | class="my-animation" | 576 * | 3. $animate runs any JavaScript-defined animations on the element | class="my-animation ng-animate" | 577 * | 4. the .ng-enter class is added to the element | class="my-animation ng-animate ng-enter" | 578 * | 5. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate ng-enter" | 579 * | 6. $animate waits for 10ms (this performs a reflow) | class="my-animation ng-animate ng-enter" | 580 * | 7. the .ng-enter-active and .ng-animate-active classes are added (this triggers the CSS transition/animation) | class="my-animation ng-animate ng-animate-active ng-enter ng-enter-active" | 581 * | 8. $animate waits for X milliseconds for the animation to complete | class="my-animation ng-animate ng-animate-active ng-enter ng-enter-active" | 582 * | 9. The animation ends and all generated CSS classes are removed from the element | class="my-animation" | 583 * | 10. The doneCallback() callback is fired (if provided) | class="my-animation" | 584 * 585 * @param {DOMElement} element the element that will be the focus of the enter animation 586 * @param {DOMElement} parentElement the parent element of the element that will be the focus of the enter animation 587 * @param {DOMElement} afterElement the sibling element (which is the previous element) of the element that will be the focus of the enter animation 588 * @param {function()=} doneCallback the callback function that will be called once the animation is complete 589 */ 590 enter : function(element, parentElement, afterElement, doneCallback) { 591 element = angular.element(element); 592 parentElement = prepareElement(parentElement); 593 afterElement = prepareElement(afterElement); 594 595 blockElementAnimations(element); 596 $delegate.enter(element, parentElement, afterElement); 597 $rootScope.$$postDigest(function() { 598 element = stripCommentsFromElement(element); 599 performAnimation('enter', 'ng-enter', element, parentElement, afterElement, noop, doneCallback); 600 }); 601 }, 602 603 /** 604 * @ngdoc method 605 * @name $animate#leave 606 * @kind function 607 *
608 * @description 609 * Runs the leave animation operation and, upon completion, removes the element from the DOM. Once 610 * the animation is started, the following CSS classes will be added for the duration of the animation: 611 * 612 * Below is a breakdown of each step that occurs during leave animation: 613 * 614 * | Animation Step | What the element class attribute looks like | 615 * |----------------------------------------------------------------------------------------------|---------------------------------------------| 616 * | 1. $animate.leave(...) is called | class="my-animation" | 617 * | 2. $animate runs any JavaScript-defined animations on the element | class="my-animation ng-animate" | 618 * | 3. the .ng-leave class is added to the element | class="my-animation ng-animate ng-leave" | 619 * | 4. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate ng-leave" | 620 * | 5. $animate waits for 10ms (this performs a reflow) | class="my-animation ng-animate ng-leave" | 621 * | 6. the .ng-leave-active and .ng-animate-active classes is added (this triggers the CSS transition/animation) | class="my-animation ng-animate ng-animate-active ng-leave ng-leave-active" | 622 * | 7. $animate waits for X milliseconds for the animation to complete | class="my-animation ng-animate ng-animate-active ng-leave ng-leave-active" | 623 * | 8. The animation ends and all generated CSS classes are removed from the element | class="my-animation" | 624 * | 9. The element is removed from the DOM | ... | 625 * | 10. The doneCallback() callback is fired (if provided) | ... | 626 * 627 * @param {DOMElement} element the element that will be the focus of the leave animation 628 * @param {function()=} doneCallback the callback function that will be called once the animation is complete 629 */ 630 leave : function(element, doneCallback) { 631 element = angular.element(element); 632 cancelChildAnimations(element); 633 blockElementAnimations(element); 634 $rootScope.$$postDigest(function() { 635 performAnimation('leave', 'ng-leave', stripCommentsFromElement(element), null, null, function() { 636 $delegate.leave(element); 637 }, doneCallback); 638 }); 639 }, 640 641 /** 642 * @ngdoc method 643 * @name $animate#move 644 * @kind function 645 * 646 * @description 647 * Fires the move DOM operation. Just before the animation starts, the animate service will either append it into the parentElement container or 648 * add the element directly after the afterElement element if present. Then the move animation will be run. Once 649 * the animation is started, the following CSS classes will be added for the duration of the animation: 650 * 651 * Below is a breakdown of each step that occurs during move animation: 652 * 653 * | Animation Step | What the element class attribute looks like | 654 * |----------------------------------------------------------------------------------------------|---------------------------------------------| 655 * | 1. $animate.move(...) is called | class="my-animation" | 656 * | 2. element is moved into the parentElement element or beside the afterElement element | class="my-animation" | 657 * | 3. $animate runs any JavaScript-defined animations on the element | class="my-animation ng-animate" | 658 * | 4. the .ng-move class is added to the element | class="my-animation ng-animate ng-move" | 659 * | 5. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate ng-move" | 660 * | 6. $animate waits for 10ms (this performs a reflow) | class="my-animation ng-animate ng-move" | 661 * | 7. the .ng-move-active and .ng-animate-active classes is added (this triggers the CSS transition/animation) | class="my-animation ng-animate ng-animate-active ng-move ng-move-active" | 662 * | 8. $animate waits for X milliseconds for the animation to complete | class="my-animation ng-animate ng-animate-active ng-move ng-move-active" | 663 * | 9. The animation ends and all generated CSS classes are removed from the element | class="my-animation" | 664 * | 10. The doneCallback() callback is fired (if provided) | class="my-animation" | 665 * 666 * @param {DOMElement} element the element that will be the focus of the move animation 667 * @param {DOMElement} parentElement the parentElement element of the element that will be the focus of the move animation 668 * @param {DOMElement} afterElement the sibling element (which is the previous element) of the element that will be the focus of the move animation 669 * @param {function()=} doneCallback the callback function that will be called once the animation is complete 670 */ 671 move : function(element, parentElement, afterElement, doneCallback) { 672 element = angular.element(element); 673 parentElement = prepareElement(parentElement); 674 afterElement = prepareElement(afterElement); 675 676 cancelChildAnimations(element); 677 blockElementAnimations(element); 678 $delegate.move(element, parentElement, afterElement); 679 $rootScope.$$postDigest(function() { 680 element = stripCommentsFromElement(element); 681 performAnimation('move', 'ng-move', element, parentElement, afterElement, noop, doneCallback); 682 }); 683 }, 684 685 /** 686 * @ngdoc method 687 * @name $animate#addClass 688 *
689 * @description 690 * Triggers a custom animation event based off the className variable and then attaches the className value to the element as a CSS class. 691 * Unlike the other animation methods, the animate service will suffix the className value with {@type -add} in order to provide 692 * the animate service the setup and active CSS classes in order to trigger the animation (this will be skipped if no CSS transitions 693 * or keyframes are defined on the -add or base CSS class). 694 * 695 * Below is a breakdown of each step that occurs during addClass animation: 696 * 697 * | Animation Step | What the element class attribute looks like | 698 * |------------------------------------------------------------------------------------------------|---------------------------------------------| 699 * | 1. $animate.addClass(element, 'super') is called | class="my-animation" | 700 * | 2. $animate runs any JavaScript-defined animations on the element | class="my-animation ng-animate" | 701 * | 3. the .super-add class are added to the element | class="my-animation ng-animate super-add" | 702 * | 4. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate super-add" | 703 * | 5. $animate waits for 10ms (this performs a reflow) | class="my-animation ng-animate super-add" | 704 * | 6. the .super, .super-add-active and .ng-animate-active classes are added (this triggers the CSS transition/animation) | class="my-animation ng-animate ng-animate-active super super-add super-add-active" | 705 * | 7. $animate waits for X milliseconds for the animation to complete | class="my-animation super super-add super-add-active" | 706 * | 8. The animation ends and all generated CSS classes are removed from the element | class="my-animation super" | 707 * | 9. The super class is kept on the element | class="my-animation super" | 708 * | 10. The doneCallback() callback is fired (if provided) | class="my-animation super" | 709 * 710 * @param {DOMElement} element the element that will be animated 711 * @param {string} className the CSS class that will be added to the element and then animated 712 * @param {function()=} doneCallback the callback function that will be called once the animation is complete 713 */ 714 addClass : function(element, className, doneCallback) { 715 element = angular.element(element); 716 element = stripCommentsFromElement(element); 717 performAnimation('addClass', className, element, null, null, function() { 718 $delegate.addClass(element, className); 719 }, doneCallback); 720 }, 721 722 /** 723 * @ngdoc method 724 * @name $animate#removeClass 725 * 726 * @description 727 * Triggers a custom animation event based off the className variable and then removes the CSS class provided by the className value 728 * from the element. Unlike the other animation methods, the animate service will suffix the className value with {@type -remove} in 729 * order to provide the animate service the setup and active CSS classes in order to trigger the animation (this will be skipped if 730 * no CSS transitions or keyframes are defined on the -remove or base CSS classes). 731 * 732 * Below is a breakdown of each step that occurs during removeClass animation: 733 * 734 * | Animation Step | What the element class attribute looks like | 735 * |-----------------------------------------------------------------------------------------------|---------------------------------------------| 736 * | 1. $animate.removeClass(element, 'super') is called | class="my-animation super" | 737 * | 2. $animate runs any JavaScript-defined animations on the element | class="my-animation super ng-animate" | 738 * | 3. the .super-remove class are added to the element | class="my-animation super ng-animate super-remove"| 739 * | 4. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation super ng-animate super-remove" | 740 * | 5. $animate waits for 10ms (this performs a reflow) | class="my-animation super ng-animate super-remove" | 741 * | 6. the .super-remove-active and .ng-animate-active classes are added and .super is removed (this triggers the CSS transition/animation) | class="my-animation ng-animate ng-animate-active super-remove super-remove-active" | 742 * | 7. $animate waits for X milliseconds for the animation to complete | class="my-animation ng-animate ng-animate-active super-remove super-remove-active" | 743 * | 8. The animation ends and all generated CSS classes are removed from the element | class="my-animation" | 744 * | 9. The doneCallback() callback is fired (if provided) | class="my-animation" | 745 * 746 * 747 * @param {DOMElement} element the element that will be animated 748 * @param {string} className the CSS class that will be animated and then removed from the element 749 * @param {function()=} doneCallback the callback function that will be called once the animation is complete 750 */ 751 removeClass : function(element, className, doneCallback) { 752 element = angular.element(element); 753 element = stripCommentsFromElement(element); 754 performAnimation('removeClass', className, element, null, null, function() { 755 $delegate.removeClass(element, className); 756 }, doneCallback); 757 }, 758 759 /** 760 * 761 * @ngdoc function 762 * @name $animate#setClass 763 * @function
764 * @description Adds and/or removes the given CSS classes to and from the element. 765 * Once complete, the done() callback will be fired (if provided). 766 * @param {DOMElement} element the element which will its CSS classes changed 767 * removed from it 768 * @param {string} add the CSS classes which will be added to the element 769 * @param {string} remove the CSS class which will be removed from the element 770 * @param {Function=} done the callback function (if provided) that will be fired after the 771 * CSS classes have been set on the element 772 */ 773 setClass : function(element, add, remove, doneCallback) { 774 element = angular.element(element); 775 element = stripCommentsFromElement(element); 776 performAnimation('setClass', [add, remove], element, null, null, function() { 777 $delegate.setClass(element, add, remove); 778 }, doneCallback); 779 }, 780 781 /** 782 * @ngdoc method 783 * @name $animate#enabled 784 * @kind function 785 * 786 * @param {boolean=} value If provided then set the animation on or off. 787 * @param {DOMElement=} element If provided then the element will be used to represent the enable/disable operation 788 * @return {boolean} Current animation state. 789 * 790 * @description 791 * Globally enables/disables animations. 792 * 793 */ 794 enabled : function(value, element) { 795 switch(arguments.length) { 796 case 2: 797 if(value) { 798 cleanup(element); 799 } else { 800 var data = element.data(NG_ANIMATE_STATE) || {}; 801 data.disabled = true; 802 element.data(NG_ANIMATE_STATE, data); 803 } 804 break; 805 806 case 1: 807 rootAnimateState.disabled = !value; 808 break; 809 810 default: 811 value = !rootAnimateState.disabled; 812 break; 813 } 814 return !!value; 815 } 816 }; 817 818 /* 819 all animations call this shared animation triggering function internally. 820 The animationEvent variable refers to the JavaScript animation event that will be triggered 821 and the className value is the name of the animation that will be applied within the 822 CSS code. Element, parentElement and afterElement are provided DOM elements for the animation 823 and the onComplete callback will be fired once the animation is fully complete. 824 */ 825 function performAnimation(animationEvent, className, element, parentElement, afterElement, domOperation, doneCallback) { 826 827 var runner = animationRunner(element, animationEvent, className); 828 if(!runner) { 829 fireDOMOperation(); 830 fireBeforeCallbackAsync(); 831 fireAfterCallbackAsync(); 832 closeAnimation(); 833 return; 834 } 835 836 className = runner.className; 837 var elementEvents = angular.element._data(runner.node); 838 elementEvents = elementEvents && elementEvents.events; 839 840 if (!parentElement) { 841 parentElement = afterElement ? afterElement.parent() : element.parent(); 842 } 843 844 var ngAnimateState = element.data(NG_ANIMATE_STATE) || {}; 845 var runningAnimations = ngAnimateState.active || {}; 846 var totalActiveAnimations = ngAnimateState.totalActive || 0; 847 var lastAnimation = ngAnimateState.last; 848 849 //only allow animations if the currently running animation is not structural 850 //or if there is no animation running at all 851 var skipAnimations; 852 if (runner.isClassBased) { 853 skipAnimations = ngAnimateState.running || 854 ngAnimateState.disabled || 855 (lastAnimation && !lastAnimation.isClassBased); 856 } 857 858 //skip the animation if animations are disabled, a parent is already being animated, 859 //the element is not currently attached to the document body or then completely close 860 //the animation if any matching animations are not found at all. 861 //NOTE: IE8 + IE9 should close properly (run closeAnimation()) in case an animation was found. 862 if (skipAnimations || animationsDisabled(element, parentElement)) { 863 fireDOMOperation(); 864 fireBeforeCallbackAsync(); 865 fireAfterCallbackAsync(); 866 closeAnimation(); 867 return; 868 } 869 870 var skipAnimation = false;
871 if(totalActiveAnimations > 0) { 872 var animationsToCancel = []; 873 if(!runner.isClassBased) { 874 if(animationEvent == 'leave' && runningAnimations['ng-leave']) { 875 skipAnimation = true; 876 } else { 877 //cancel all animations when a structural animation takes place 878 for(var klass in runningAnimations) { 879 animationsToCancel.push(runningAnimations[klass]); 880 cleanup(element, klass); 881 } 882 runningAnimations = {}; 883 totalActiveAnimations = 0; 884 } 885 } else if(lastAnimation.event == 'setClass') { 886 animationsToCancel.push(lastAnimation); 887 cleanup(element, className); 888 } 889 else if(runningAnimations[className]) { 890 var current = runningAnimations[className]; 891 if(current.event == animationEvent) { 892 skipAnimation = true; 893 } else { 894 animationsToCancel.push(current); 895 cleanup(element, className); 896 } 897 } 898 899 if(animationsToCancel.length > 0) { 900 forEach(animationsToCancel, function(operation) { 901 operation.cancel(); 902 }); 903 } 904 } 905 906 if(runner.isClassBased && !runner.isSetClassOperation && !skipAnimation) { 907 skipAnimation = (animationEvent == 'addClass') == element.hasClass(className); //opposite of XOR 908 } 909 910 if(skipAnimation) { 911 fireDOMOperation(); 912 fireBeforeCallbackAsync(); 913 fireAfterCallbackAsync(); 914 fireDoneCallbackAsync(); 915 return; 916 } 917 918 if(animationEvent == 'leave') { 919 //there's no need to ever remove the listener since the element 920 //will be removed (destroyed) after the leave animation ends or 921 //is cancelled midway 922 element.one('$destroy', function(e) { 923 var element = angular.element(this); 924 var state = element.data(NG_ANIMATE_STATE); 925 if(state) { 926 var activeLeaveAnimation = state.active['ng-leave']; 927 if(activeLeaveAnimation) { 928 activeLeaveAnimation.cancel(); 929 cleanup(element, 'ng-leave'); 930 } 931 } 932 }); 933 } 934 935 //the ng-animate class does nothing, but it's here to allow for 936 //parent animations to find and cancel child animations when needed 937 element.addClass(NG_ANIMATE_CLASS_NAME); 938 939 var localAnimationCount = globalAnimationCounter++; 940 totalActiveAnimations++; 941 runningAnimations[className] = runner; 942 943 element.data(NG_ANIMATE_STATE, { 944 last : runner, 945 active : runningAnimations, 946 index : localAnimationCount, 947 totalActive : totalActiveAnimations 948 }); 949 950 //first we run the before animations and when all of those are complete 951 //then we perform the DOM operation and run the next set of animations 952 fireBeforeCallbackAsync(); 953 runner.before(function(cancelled) { 954 var data = element.data(NG_ANIMATE_STATE); 955 cancelled = cancelled || 956 !data || !data.active[className] || 957 (runner.isClassBased && data.active[className].event != animationEvent); 958 959 fireDOMOperation(); 960 if(cancelled === true) { 961 closeAnimation(); 962 } else { 963 fireAfterCallbackAsync(); 964 runner.after(closeAnimation); 965 } 966 }); 967 968 function fireDOMCallback(animationPhase) { 969 var eventName = '$animate:' + animationPhase; 970 if(elementEvents && elementEvents[eventName] && elementEvents[eventName].length > 0) { 971 $$asyncCallback(function() { 972 element.triggerHandler(eventName, { 973 event : animationEvent, 974 className : className 975 }); 976 }); 977 } 978 } 979 980 function fireBeforeCallbackAsync() { 981 fireDOMCallback('before'); 982 } 983 984 function fireAfterCallbackAsync() { 985 fireDOMCallback('after'); 986 } 987 988 function fireDoneCallbackAsync() { 989 fireDOMCallback('close'); 990 if(doneCallback) { 991 $$asyncCallback(function() { 992 doneCallback(); 993 }); 994 } 995 } 996 997 //it is less complicated to use a flag than managing and canceling 998 //timeouts containing multiple callbacks. 999 function fireDOMOperation() { 1000 if(!fireDOMOperation.hasBeenRun) { 1001 fireDOMOperation.hasBeenRun = true; 1002 domOperation(); 1003 } 1004 } 1005 1006 function closeAnimation() { 1007 if(!closeAnimation.hasBeenRun) { 1008 closeAnimation.hasBeenRun = true; 1009 var data = element.data(NG_ANIMATE_STATE); 1010 if(data) { 1011 /* only structural animations wait for reflow before removing an 1012 animation, but class-based animations don't. An example of this 1013 failing would be when a parent HTML tag has a ng-class attribute 1014 causing ALL directives below to skip animations during the digest */ 1015 if(runner && runner.isClassBased) { 1016 cleanup(element, className); 1017 } else { 1018 $$asyncCallback(function() { 1019 var data = element.data(NG_ANIMATE_STATE) || {}; 1020 if(localAnimationCount == data.index) { 1021 cleanup(element, className, animationEvent); 1022 } 1023 }); 1024 element.data(NG_ANIMATE_STATE, data); 1025 } 1026 } 1027 fireDoneCallbackAsync(); 1028 } 1029 } 1030 } 1031 1032 function cancelChildAnimations(element) { 1033 var node = extractElementNode(element); 1034 if (node) { 1035 var nodes = angular.isFunction(node.getElementsByClassName) ? 1036 node.getElementsByClassName(NG_ANIMATE_CLASS_NAME) : 1037 node.querySelectorAll('.' + NG_ANIMATE_CLASS_NAME);
1038 forEach(nodes, function(element) { 1039 element = angular.element(element); 1040 var data = element.data(NG_ANIMATE_STATE); 1041 if(data && data.active) { 1042 forEach(data.active, function(runner) { 1043 runner.cancel(); 1044 }); 1045 } 1046 }); 1047 } 1048 } 1049 1050 function cleanup(element, className) { 1051 if(isMatchingElement(element, $rootElement)) { 1052 if(!rootAnimateState.disabled) { 1053 rootAnimateState.running = false; 1054 rootAnimateState.structural = false; 1055 } 1056 } else if(className) { 1057 var data = element.data(NG_ANIMATE_STATE) || {}; 1058 1059 var removeAnimations = className === true; 1060 if(!removeAnimations && data.active && data.active[className]) { 1061 data.totalActive--; 1062 delete data.active[className]; 1063 } 1064 1065 if(removeAnimations || !data.totalActive) { 1066 element.removeClass(NG_ANIMATE_CLASS_NAME); 1067 element.removeData(NG_ANIMATE_STATE); 1068 } 1069 } 1070 } 1071 1072 function animationsDisabled(element, parentElement) { 1073 if (rootAnimateState.disabled) { 1074 return true; 1075 } 1076 1077 if (isMatchingElement(element, $rootElement)) { 1078 return rootAnimateState.running; 1079 } 1080 1081 var allowChildAnimations, parentRunningAnimation, hasParent; 1082 do { 1083 //the element did not reach the root element which means that it 1084 //is not apart of the DOM. Therefore there is no reason to do 1085 //any animations on it 1086 if (parentElement.length === 0) break; 1087 1088 var isRoot = isMatchingElement(parentElement, $rootElement); 1089 var state = isRoot ? rootAnimateState : (parentElement.data(NG_ANIMATE_STATE) || {}); 1090 if (state.disabled) { 1091 return true; 1092 } 1093 1094 //no matter what, for an animation to work it must reach the root element 1095 //this implies that the element is attached to the DOM when the animation is run 1096 if (isRoot) { 1097 hasParent = true; 1098 } 1099 1100 //once a flag is found that is strictly false then everything before 1101 //it will be discarded and all child animations will be restricted 1102 if (allowChildAnimations !== false) { 1103 var animateChildrenFlag = parentElement.data(NG_ANIMATE_CHILDREN); 1104 if(angular.isDefined(animateChildrenFlag)) { 1105 allowChildAnimations = animateChildrenFlag; 1106 } 1107 } 1108 1109 parentRunningAnimation = parentRunningAnimation || 1110 state.running || 1111 (state.last && !state.last.isClassBased); 1112 } 1113 while(parentElement = parentElement.parent()); 1114 1115 return !hasParent || (!allowChildAnimations && parentRunningAnimation); 1116 } 1117 }]); 1118 1119 $animateProvider.register('', ['$window', '$sniffer', '$timeout', '$$animateReflow', 1120 function($window, $sniffer, $timeout, $$animateReflow) { 1121 // Detect proper transitionend/animationend event names. 1122 var CSS_PREFIX = '', TRANSITION_PROP, TRANSITIONEND_EVENT, ANIMATION_PROP, ANIMATIONEND_EVENT; 1123 1124 // If unprefixed events are not supported but webkit-prefixed are, use the latter.
1125 // Otherwise, just use W3C names, browsers not supporting them at all will just ignore them. 1126 // Note: Chrome implements `window.onwebkitanimationend` and doesn't implement `window.onanimationend` 1127 // but at the same time dispatches the `animationend` event and not `webkitAnimationEnd`. 1128 // Register both events in case `window.onanimationend` is not supported because of that, 1129 // do the same for `transitionend` as Safari is likely to exhibit similar behavior. 1130 // Also, the only modern browser that uses vendor prefixes for transitions/keyframes is webkit 1131 // therefore there is no reason to test anymore for other vendor prefixes: http://caniuse.com/#search=transition 1132 if (window.ontransitionend === undefined && window.onwebkittransitionend !== undefined) { 1133 CSS_PREFIX = '-webkit-'; 1134 TRANSITION_PROP = 'WebkitTransition'; 1135 TRANSITIONEND_EVENT = 'webkitTransitionEnd transitionend'; 1136 } else { 1137 TRANSITION_PROP = 'transition'; 1138 TRANSITIONEND_EVENT = 'transitionend'; 1139 } 1140 1141 if (window.onanimationend === undefined && window.onwebkitanimationend !== undefined) { 1142 CSS_PREFIX = '-webkit-'; 1143 ANIMATION_PROP = 'WebkitAnimation'; 1144 ANIMATIONEND_EVENT = 'webkitAnimationEnd animationend'; 1145 } else { 1146 ANIMATION_PROP = 'animation'; 1147 ANIMATIONEND_EVENT = 'animationend'; 1148 } 1149 1150 var DURATION_KEY = 'Duration'; 1151 var PROPERTY_KEY = 'Property'; 1152 var DELAY_KEY = 'Delay'; 1153 var ANIMATION_ITERATION_COUNT_KEY = 'IterationCount'; 1154 var NG_ANIMATE_PARENT_KEY = '$$ngAnimateKey'; 1155 var NG_ANIMATE_CSS_DATA_KEY = '$$ngAnimateCSS3Data'; 1156 var NG_ANIMATE_BLOCK_CLASS_NAME = 'ng-animate-block-transitions'; 1157 var ELAPSED_TIME_MAX_DECIMAL_PLACES = 3; 1158 var CLOSING_TIME_BUFFER = 1.5; 1159 var ONE_SECOND = 1000; 1160 1161 var lookupCache = {}; 1162 var parentCounter = 0; 1163 var animationReflowQueue = []; 1164 var cancelAnimationReflow; 1165 function afterReflow(element, callback) { 1166 if(cancelAnimationReflow) { 1167 cancelAnimationReflow(); 1168 } 1169 animationReflowQueue.push(callback); 1170 cancelAnimationReflow = $$animateReflow(function() { 1171 forEach(animationReflowQueue, function(fn) { 1172 fn(); 1173 }); 1174 1175 animationReflowQueue = []; 1176 cancelAnimationReflow = null; 1177 lookupCache = {}; 1178 }); 1179 } 1180 1181 var closingTimer = null; 1182 var closingTimestamp = 0; 1183 var animationElementQueue = []; 1184 function animationCloseHandler(element, totalTime) { 1185 var node = extractElementNode(element); 1186 element = angular.element(node); 1187 1188 //this item will be garbage collected by the closing 1189 //animation timeout 1190 animationElementQueue.push(element); 1191 1192 //but it may not need to cancel out the existing timeout 1193 //if the timestamp is less than the previous one 1194 var futureTimestamp = Date.now() + totalTime; 1195 if(futureTimestamp <= closingTimestamp) { 1196 return; 1197 } 1198 1199 $timeout.cancel(closingTimer); 1200 1201 closingTimestamp = futureTimestamp; 1202 closingTimer = $timeout(function() { 1203 closeAllAnimations(animationElementQueue); 1204 animationElementQueue = []; 1205 }, totalTime, false); 1206 } 1207 1208 function closeAllAnimations(elements) { 1209 forEach(elements, function(element) { 1210 var elementData = element.data(NG_ANIMATE_CSS_DATA_KEY); 1211 if(elementData) { 1212 (elementData.closeAnimationFn || noop)(); 1213 } 1214 }); 1215 } 1216 1217 function getElementAnimationDetails(element, cacheKey) { 1218 var data = cacheKey ? lookupCache[cacheKey] : null; 1219 if(!data) { 1220 var transitionDuration = 0; 1221 var transitionDelay = 0; 1222 var animationDuration = 0; 1223 var animationDelay = 0; 1224 var transitionDelayStyle; 1225 var animationDelayStyle; 1226 var transitionDurationStyle; 1227 var transitionPropertyStyle; 1228 1229 //we want all the styles defined before and after
1230 forEach(element, function(element) { 1231 if (element.nodeType == ELEMENT_NODE) { 1232 var elementStyles = $window.getComputedStyle(element) || {}; 1233 1234 transitionDurationStyle = elementStyles[TRANSITION_PROP + DURATION_KEY]; 1235 1236 transitionDuration = Math.max(parseMaxTime(transitionDurationStyle), transitionDuration); 1237 1238 transitionPropertyStyle = elementStyles[TRANSITION_PROP + PROPERTY_KEY]; 1239 1240 transitionDelayStyle = elementStyles[TRANSITION_PROP + DELAY_KEY]; 1241 1242 transitionDelay = Math.max(parseMaxTime(transitionDelayStyle), transitionDelay); 1243 1244 animationDelayStyle = elementStyles[ANIMATION_PROP + DELAY_KEY]; 1245 1246 animationDelay = Math.max(parseMaxTime(animationDelayStyle), animationDelay); 1247 1248 var aDuration = parseMaxTime(elementStyles[ANIMATION_PROP + DURATION_KEY]); 1249 1250 if(aDuration > 0) { 1251 aDuration *= parseInt(elementStyles[ANIMATION_PROP + ANIMATION_ITERATION_COUNT_KEY], 10) || 1; 1252 } 1253 1254 animationDuration = Math.max(aDuration, animationDuration); 1255 } 1256 }); 1257 data = { 1258 total : 0, 1259 transitionPropertyStyle: transitionPropertyStyle, 1260 transitionDurationStyle: transitionDurationStyle, 1261 transitionDelayStyle: transitionDelayStyle, 1262 transitionDelay: transitionDelay, 1263 transitionDuration: transitionDuration, 1264 animationDelayStyle: animationDelayStyle, 1265 animationDelay: animationDelay, 1266 animationDuration: animationDuration 1267 }; 1268 if(cacheKey) { 1269 lookupCache[cacheKey] = data; 1270 } 1271 } 1272 return data; 1273 } 1274 1275 function parseMaxTime(str) { 1276 var maxValue = 0; 1277 var values = angular.isString(str) ? 1278 str.split(/\s*,\s*/) : 1279 []; 1280 forEach(values, function(value) { 1281 maxValue = Math.max(parseFloat(value) || 0, maxValue); 1282 }); 1283 return maxValue; 1284 } 1285 1286 function getCacheKey(element) { 1287 var parentElement = element.parent(); 1288 var parentID = parentElement.data(NG_ANIMATE_PARENT_KEY); 1289 if(!parentID) { 1290 parentElement.data(NG_ANIMATE_PARENT_KEY, ++parentCounter); 1291 parentID = parentCounter; 1292 } 1293 return parentID + '-' + extractElementNode(element).getAttribute('class'); 1294 } 1295 1296 function animateSetup(animationEvent, element, className, calculationDecorator) { 1297 var cacheKey = getCacheKey(element); 1298 var eventCacheKey = cacheKey + ' ' + className; 1299 var itemIndex = lookupCache[eventCacheKey] ? ++lookupCache[eventCacheKey].total : 0; 1300 1301 var stagger = {}; 1302 if(itemIndex > 0) { 1303 var staggerClassName = className + '-stagger'; 1304 var staggerCacheKey = cacheKey + ' ' + staggerClassName; 1305 var applyClasses = !lookupCache[staggerCacheKey]; 1306 1307 applyClasses && element.addClass(staggerClassName); 1308 1309 stagger = getElementAnimationDetails(element, staggerCacheKey); 1310 1311 applyClasses && element.removeClass(staggerClassName); 1312 } 1313 1314 /* the animation itself may need to add/remove special CSS classes 1315 * before calculating the anmation styles */ 1316 calculationDecorator = calculationDecorator || 1317 function(fn) { return fn(); }; 1318 1319 element.addClass(className); 1320 1321 var formerData = element.data(NG_ANIMATE_CSS_DATA_KEY) || {}; 1322 1323 var timings = calculationDecorator(function() { 1324 return getElementAnimationDetails(element, eventCacheKey); 1325 }); 1326 1327 var transitionDuration = timings.transitionDuration; 1328 var animationDuration = timings.animationDuration; 1329 if(transitionDuration === 0 && animationDuration === 0) { 1330 element.removeClass(className); 1331 return false; 1332 } 1333 1334 element.data(NG_ANIMATE_CSS_DATA_KEY, { 1335 running : formerData.running || 0, 1336 itemIndex : itemIndex, 1337 stagger : stagger, 1338 timings : timings, 1339 closeAnimationFn : noop 1340 }); 1341 1342 //temporarily disable the transition so that the enter styles 1343 //don't animate twice (this is here to avoid a bug in Chrome/FF). 1344 var isCurrentlyAnimating = formerData.running > 0 || animationEvent == 'setClass'; 1345 if(transitionDuration > 0) { 1346 blockTransitions(element, className, isCurrentlyAnimating); 1347 } 1348 1349 //staggering keyframe animations work by adjusting the `animation-delay` CSS property 1350 //on the given element, however, the delay value can only calculated after the reflow 1351 //since by that time $animate knows how many elements are being animated. Therefore, 1352 //until the reflow occurs the element needs to be blocked (where the keyframe animation 1353 //is set to `none 0s`). This blocking mechanism should only be set for when a stagger 1354 //animation is detected and when the element item index is greater than 0. 1355 if(animationDuration > 0 && stagger.animationDelay > 0 && stagger.animationDuration === 0) { 1356 blockKeyframeAnimations(element); 1357 } 1358 1359 return true; 1360 } 1361 1362 function isStructuralAnimation(className) { 1363 return className == 'ng-enter' || className == 'ng-move' || className == 'ng-leave'; 1364 } 1365 1366 function blockTransitions(element, className, isAnimating) { 1367 if(isStructuralAnimation(className) || !isAnimating) { 1368 extractElementNode(element).style[TRANSITION_PROP + PROPERTY_KEY] = 'none'; 1369 } else { 1370 element.addClass(NG_ANIMATE_BLOCK_CLASS_NAME); 1371 } 1372 } 1373 1374 function blockKeyframeAnimations(element) { 1375 extractElementNode(element).style[ANIMATION_PROP] = 'none 0s'; 1376 } 1377 1378 function unblockTransitions(element, className) { 1379 var prop = TRANSITION_PROP + PROPERTY_KEY; 1380 var node = extractElementNode(element); 1381 if(node.style[prop] && node.style[prop].length > 0) { 1382 node.style[prop] = ''; 1383 } 1384 element.removeClass(NG_ANIMATE_BLOCK_CLASS_NAME); 1385 } 1386 1387 function unblockKeyframeAnimations(element) { 1388 var prop = ANIMATION_PROP; 1389 var node = extractElementNode(element); 1390 if(node.style[prop] && node.style[prop].length > 0) { 1391 node.style[prop] = ''; 1392 } 1393 } 1394 1395 function animateRun(animationEvent, element, className, activeAnimationComplete) { 1396 var node = extractElementNode(element); 1397 var elementData = element.data(NG_ANIMATE_CSS_DATA_KEY); 1398 if(node.getAttribute('class').indexOf(className) == -1 || !elementData) { 1399 activeAnimationComplete(); 1400 return; 1401 } 1402 1403 var activeClassName = '';
1404 forEach(className.split(' '), function(klass, i) { 1405 activeClassName += (i > 0 ? ' ' : '') + klass + '-active'; 1406 }); 1407 1408 var stagger = elementData.stagger; 1409 var timings = elementData.timings; 1410 var itemIndex = elementData.itemIndex; 1411 var maxDuration = Math.max(timings.transitionDuration, timings.animationDuration); 1412 var maxDelay = Math.max(timings.transitionDelay, timings.animationDelay); 1413 var maxDelayTime = maxDelay * ONE_SECOND; 1414 1415 var startTime = Date.now(); 1416 var css3AnimationEvents = ANIMATIONEND_EVENT + ' ' + TRANSITIONEND_EVENT; 1417 1418 var style = '', appliedStyles = []; 1419 if(timings.transitionDuration > 0) { 1420 var propertyStyle = timings.transitionPropertyStyle; 1421 if(propertyStyle.indexOf('all') == -1) { 1422 style += CSS_PREFIX + 'transition-property: ' + propertyStyle + ';'; 1423 style += CSS_PREFIX + 'transition-duration: ' + timings.transitionDurationStyle + ';'; 1424 appliedStyles.push(CSS_PREFIX + 'transition-property'); 1425 appliedStyles.push(CSS_PREFIX + 'transition-duration'); 1426 } 1427 } 1428 1429 if(itemIndex > 0) { 1430 if(stagger.transitionDelay > 0 && stagger.transitionDuration === 0) { 1431 var delayStyle = timings.transitionDelayStyle; 1432 style += CSS_PREFIX + 'transition-delay: ' + 1433 prepareStaggerDelay(delayStyle, stagger.transitionDelay, itemIndex) + '; '; 1434 appliedStyles.push(CSS_PREFIX + 'transition-delay'); 1435 } 1436 1437 if(stagger.animationDelay > 0 && stagger.animationDuration === 0) { 1438 style += CSS_PREFIX + 'animation-delay: ' + 1439 prepareStaggerDelay(timings.animationDelayStyle, stagger.animationDelay, itemIndex) + '; '; 1440 appliedStyles.push(CSS_PREFIX + 'animation-delay'); 1441 } 1442 } 1443 1444 if(appliedStyles.length > 0) { 1445 //the element being animated may sometimes contain comment nodes in 1446 //the jqLite object, so we're safe to use a single variable to house 1447 //the styles since there is always only one element being animated 1448 var oldStyle = node.getAttribute('style') || ''; 1449 node.setAttribute('style', oldStyle + '; ' + style); 1450 } 1451 1452 element.on(css3AnimationEvents, onAnimationProgress); 1453 element.addClass(activeClassName); 1454 elementData.closeAnimationFn = function() { 1455 onEnd(); 1456 activeAnimationComplete(); 1457 }; 1458 1459 var staggerTime = itemIndex * (Math.max(stagger.animationDelay, stagger.transitionDelay) || 0); 1460 var animationTime = (maxDelay + maxDuration) * CLOSING_TIME_BUFFER; 1461 var totalTime = (staggerTime + animationTime) * ONE_SECOND; 1462 1463 elementData.running++; 1464 animationCloseHandler(element, totalTime); 1465 return onEnd; 1466 1467 // This will automatically be called by $animate so 1468 // there is no need to attach this internally to the 1469 // timeout done method. 1470 function onEnd(cancelled) { 1471 element.off(css3AnimationEvents, onAnimationProgress); 1472 element.removeClass(activeClassName); 1473 animateClose(element, className); 1474 var node = extractElementNode(element); 1475 for (var i in appliedStyles) { 1476 node.style.removeProperty(appliedStyles[i]); 1477 } 1478 } 1479 1480 function onAnimationProgress(event) { 1481 event.stopPropagation(); 1482 var ev = event.originalEvent || event; 1483 var timeStamp = ev.$manualTimeStamp || ev.timeStamp || Date.now(); 1484 1485 /* Firefox (or possibly just Gecko) likes to not round values up 1486 * when a ms measurement is used for the animation */ 1487 var elapsedTime = parseFloat(ev.elapsedTime.toFixed(ELAPSED_TIME_MAX_DECIMAL_PLACES)); 1488 1489 /* $manualTimeStamp is a mocked timeStamp value which is set 1490 * within browserTrigger(). This is only here so that tests can 1491 * mock animations properly. Real events fallback to event.timeStamp, 1492 * or, if they don't, then a timeStamp is automatically created for them. 1493 * We're checking to see if the timeStamp surpasses the expected delay, 1494 * but we're using elapsedTime instead of the timeStamp on the 2nd 1495 * pre-condition since animations sometimes close off early */ 1496 if(Math.max(timeStamp - startTime, 0) >= maxDelayTime && elapsedTime >= maxDuration) { 1497 activeAnimationComplete(); 1498 } 1499 } 1500 } 1501 1502 function prepareStaggerDelay(delayStyle, staggerDelay, index) { 1503 var style = '';
1504 forEach(delayStyle.split(','), function(val, i) { 1505 style += (i > 0 ? ',' : '') + 1506 (index * staggerDelay + parseInt(val, 10)) + 's'; 1507 }); 1508 return style; 1509 } 1510 1511 function animateBefore(animationEvent, element, className, calculationDecorator) { 1512 if(animateSetup(animationEvent, element, className, calculationDecorator)) { 1513 return function(cancelled) { 1514 cancelled && animateClose(element, className); 1515 }; 1516 } 1517 } 1518 1519 function animateAfter(animationEvent, element, className, afterAnimationComplete) { 1520 if(element.data(NG_ANIMATE_CSS_DATA_KEY)) { 1521 return animateRun(animationEvent, element, className, afterAnimationComplete); 1522 } else { 1523 animateClose(element, className); 1524 afterAnimationComplete(); 1525 } 1526 } 1527 1528 function animate(animationEvent, element, className, animationComplete) { 1529 //If the animateSetup function doesn't bother returning a 1530 //cancellation function then it means that there is no animation 1531 //to perform at all 1532 var preReflowCancellation = animateBefore(animationEvent, element, className); 1533 if(!preReflowCancellation) { 1534 animationComplete(); 1535 return; 1536 } 1537 1538 //There are two cancellation functions: one is before the first 1539 //reflow animation and the second is during the active state 1540 //animation. The first function will take care of removing the 1541 //data from the element which will not make the 2nd animation 1542 //happen in the first place 1543 var cancel = preReflowCancellation; 1544 afterReflow(element, function() { 1545 unblockTransitions(element, className); 1546 unblockKeyframeAnimations(element); 1547 //once the reflow is complete then we point cancel to 1548 //the new cancellation function which will remove all of the 1549 //animation properties from the active animation 1550 cancel = animateAfter(animationEvent, element, className, animationComplete); 1551 }); 1552 1553 return function(cancelled) { 1554 (cancel || noop)(cancelled); 1555 }; 1556 } 1557 1558 function animateClose(element, className) { 1559 element.removeClass(className); 1560 var data = element.data(NG_ANIMATE_CSS_DATA_KEY); 1561 if(data) { 1562 if(data.running) { 1563 data.running--; 1564 } 1565 if(!data.running || data.running === 0) { 1566 element.removeData(NG_ANIMATE_CSS_DATA_KEY); 1567 } 1568 } 1569 } 1570 1571 return { 1572 enter : function(element, animationCompleted) { 1573 return animate('enter', element, 'ng-enter', animationCompleted); 1574 }, 1575 1576 leave : function(element, animationCompleted) { 1577 return animate('leave', element, 'ng-leave', animationCompleted); 1578 }, 1579 1580 move : function(element, animationCompleted) { 1581 return animate('move', element, 'ng-move', animationCompleted); 1582 }, 1583 1584 beforeSetClass : function(element, add, remove, animationCompleted) { 1585 var className = suffixClasses(remove, '-remove') + ' ' + 1586 suffixClasses(add, '-add'); 1587 var cancellationMethod = animateBefore('setClass', element, className, function(fn) { 1588 /* when classes are removed from an element then the transition style 1589 * that is applied is the transition defined on the element without the 1590 * CSS class being there. This is how CSS3 functions outside of ngAnimate. 1591 * http://plnkr.co/edit/j8OzgTNxHTb4n3zLyjGW?p=preview */ 1592 var klass = element.attr('class'); 1593 element.removeClass(remove); 1594 element.addClass(add); 1595 var timings = fn(); 1596 element.attr('class', klass); 1597 return timings; 1598 }); 1599 1600 if(cancellationMethod) { 1601 afterReflow(element, function() { 1602 unblockTransitions(element, className); 1603 unblockKeyframeAnimations(element); 1604 animationCompleted(); 1605 }); 1606 return cancellationMethod; 1607 } 1608 animationCompleted(); 1609 }, 1610 1611 beforeAddClass : function(element, className, animationCompleted) { 1612 var cancellationMethod = animateBefore('addClass', element, suffixClasses(className, '-add'), function(fn) { 1613 1614 /* when a CSS class is added to an element then the transition style that 1615 * is applied is the transition defined on the element when the CSS class 1616 * is added at the time of the animation. This is how CSS3 functions 1617 * outside of ngAnimate. */ 1618 element.addClass(className); 1619 var timings = fn(); 1620 element.removeClass(className); 1621 return timings; 1622 }); 1623 1624 if(cancellationMethod) { 1625 afterReflow(element, function() { 1626 unblockTransitions(element, className); 1627 unblockKeyframeAnimations(element); 1628 animationCompleted(); 1629 }); 1630 return cancellationMethod; 1631 } 1632 animationCompleted(); 1633 }, 1634 1635 setClass : function(element, add, remove, animationCompleted) { 1636 remove = suffixClasses(remove, '-remove'); 1637 add = suffixClasses(add, '-add'); 1638 var className = remove + ' ' + add; 1639 return animateAfter('setClass', element, className, animationCompleted); 1640 }, 1641 1642 addClass : function(element, className, animationCompleted) { 1643 return animateAfter('addClass', element, suffixClasses(className, '-add'), animationCompleted); 1644 }, 1645 1646 beforeRemoveClass : function(element, className, animationCompleted) { 1647 var cancellationMethod = animateBefore('removeClass', element, suffixClasses(className, '-remove'), function(fn) { 1648 /* when classes are removed from an element then the transition style 1649 * that is applied is the transition defined on the element without the 1650 * CSS class being there. This is how CSS3 functions outside of ngAnimate. 1651 * http://plnkr.co/edit/j8OzgTNxHTb4n3zLyjGW?p=preview */ 1652 var klass = element.attr('class'); 1653 element.removeClass(className); 1654 var timings = fn(); 1655 element.attr('class', klass); 1656 return timings; 1657 }); 1658 1659 if(cancellationMethod) { 1660 afterReflow(element, function() { 1661 unblockTransitions(element, className); 1662 unblockKeyframeAnimations(element); 1663 animationCompleted(); 1664 }); 1665 return cancellationMethod; 1666 } 1667 animationCompleted(); 1668 }, 1669 1670 removeClass : function(element, className, animationCompleted) { 1671 return animateAfter('removeClass', element, suffixClasses(className, '-remove'), animationCompleted); 1672 } 1673 }; 1674 1675 function suffixClasses(classes, suffix) { 1676 var className = '';
1677 classes = angular.isArray(classes) ? classes : classes.split(/\s+/); 1678 forEach(classes, function(klass, i) { 1679 if(klass && klass.length > 0) { 1680 className += (i > 0 ? ' ' : '') + klass + suffix; 1681 } 1682 }); 1683 return className; 1684 } 1685 }]); 1686 }]); 1687 1688 1689})(window, window.angular);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.