|
1 | 1 | /**
|
2 |
| - * @license AngularJS v1.2.19 |
| 2 | + * @license AngularJS v1.2.20 |
3 | 3 | * (c) 2010-2014 Google, Inc. http://angularjs.org
|
4 | 4 | * License: MIT
|
5 | 5 | */
|
|
64 | 64 | * <ANY class="slide" ng-include="..."></ANY>
|
65 | 65 | * ```
|
66 | 66 | *
|
67 |
| - * Keep in mind that if an animation is running, any child elements cannot be animated until the parent element's |
68 |
| - * animation has completed. |
| 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. |
69 | 83 | *
|
70 | 84 | * <h2>CSS-defined Animations</h2>
|
71 | 85 | * The animate service will automatically apply two CSS classes to the animated element and these two CSS classes
|
@@ -255,6 +269,19 @@ angular.module('ngAnimate', ['ng'])
|
255 | 269 | * Please visit the {@link ngAnimate `ngAnimate`} module overview page learn more about how to use animations in your application.
|
256 | 270 | *
|
257 | 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 | + }) |
258 | 285 |
|
259 | 286 | //this private service is only used within CSS-enabled animations
|
260 | 287 | //IE8 + IE9 do not support rAF natively, but that is fine since they
|
@@ -283,6 +310,7 @@ angular.module('ngAnimate', ['ng'])
|
283 | 310 |
|
284 | 311 | var ELEMENT_NODE = 1;
|
285 | 312 | var NG_ANIMATE_STATE = '$$ngAnimateState';
|
| 313 | + var NG_ANIMATE_CHILDREN = '$$ngAnimateChildren'; |
286 | 314 | var NG_ANIMATE_CLASS_NAME = 'ng-animate';
|
287 | 315 | var rootAnimateState = {running: true};
|
288 | 316 |
|
@@ -332,6 +360,12 @@ angular.module('ngAnimate', ['ng'])
|
332 | 360 | return classNameFilter.test(className);
|
333 | 361 | };
|
334 | 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 | + |
335 | 369 | function lookup(name) {
|
336 | 370 | if (name) {
|
337 | 371 | var matches = [],
|
@@ -558,7 +592,7 @@ angular.module('ngAnimate', ['ng'])
|
558 | 592 | parentElement = prepareElement(parentElement);
|
559 | 593 | afterElement = prepareElement(afterElement);
|
560 | 594 |
|
561 |
| - this.enabled(false, element); |
| 595 | + blockElementAnimations(element); |
562 | 596 | $delegate.enter(element, parentElement, afterElement);
|
563 | 597 | $rootScope.$$postDigest(function() {
|
564 | 598 | element = stripCommentsFromElement(element);
|
@@ -596,7 +630,7 @@ angular.module('ngAnimate', ['ng'])
|
596 | 630 | leave : function(element, doneCallback) {
|
597 | 631 | element = angular.element(element);
|
598 | 632 | cancelChildAnimations(element);
|
599 |
| - this.enabled(false, element); |
| 633 | + blockElementAnimations(element); |
600 | 634 | $rootScope.$$postDigest(function() {
|
601 | 635 | performAnimation('leave', 'ng-leave', stripCommentsFromElement(element), null, null, function() {
|
602 | 636 | $delegate.leave(element);
|
@@ -640,7 +674,7 @@ angular.module('ngAnimate', ['ng'])
|
640 | 674 | afterElement = prepareElement(afterElement);
|
641 | 675 |
|
642 | 676 | cancelChildAnimations(element);
|
643 |
| - this.enabled(false, element); |
| 677 | + blockElementAnimations(element); |
644 | 678 | $delegate.move(element, parentElement, afterElement);
|
645 | 679 | $rootScope.$$postDigest(function() {
|
646 | 680 | element = stripCommentsFromElement(element);
|
@@ -814,9 +848,12 @@ angular.module('ngAnimate', ['ng'])
|
814 | 848 |
|
815 | 849 | //only allow animations if the currently running animation is not structural
|
816 | 850 | //or if there is no animation running at all
|
817 |
| - var skipAnimations = runner.isClassBased ? |
818 |
| - ngAnimateState.disabled || (lastAnimation && !lastAnimation.isClassBased) : |
819 |
| - false; |
| 851 | + var skipAnimations; |
| 852 | + if (runner.isClassBased) { |
| 853 | + skipAnimations = ngAnimateState.running || |
| 854 | + ngAnimateState.disabled || |
| 855 | + (lastAnimation && !lastAnimation.isClassBased); |
| 856 | + } |
820 | 857 |
|
821 | 858 | //skip the animation if animations are disabled, a parent is already being animated,
|
822 | 859 | //the element is not currently attached to the document body or then completely close
|
@@ -1033,30 +1070,49 @@ angular.module('ngAnimate', ['ng'])
|
1033 | 1070 | }
|
1034 | 1071 |
|
1035 | 1072 | function animationsDisabled(element, parentElement) {
|
1036 |
| - if (rootAnimateState.disabled) return true; |
| 1073 | + if (rootAnimateState.disabled) { |
| 1074 | + return true; |
| 1075 | + } |
1037 | 1076 |
|
1038 |
| - if(isMatchingElement(element, $rootElement)) { |
1039 |
| - return rootAnimateState.disabled || rootAnimateState.running; |
| 1077 | + if (isMatchingElement(element, $rootElement)) { |
| 1078 | + return rootAnimateState.running; |
1040 | 1079 | }
|
1041 | 1080 |
|
| 1081 | + var allowChildAnimations, parentRunningAnimation, hasParent; |
1042 | 1082 | do {
|
1043 | 1083 | //the element did not reach the root element which means that it
|
1044 | 1084 | //is not apart of the DOM. Therefore there is no reason to do
|
1045 | 1085 | //any animations on it
|
1046 |
| - if(parentElement.length === 0) break; |
| 1086 | + if (parentElement.length === 0) break; |
1047 | 1087 |
|
1048 | 1088 | var isRoot = isMatchingElement(parentElement, $rootElement);
|
1049 |
| - var state = isRoot ? rootAnimateState : parentElement.data(NG_ANIMATE_STATE); |
1050 |
| - var result = state && (!!state.disabled || state.running || state.totalActive > 0); |
1051 |
| - if(isRoot || result) { |
1052 |
| - return result; |
| 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; |
1053 | 1098 | }
|
1054 | 1099 |
|
1055 |
| - if(isRoot) return true; |
| 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); |
1056 | 1112 | }
|
1057 | 1113 | while(parentElement = parentElement.parent());
|
1058 | 1114 |
|
1059 |
| - return true; |
| 1115 | + return !hasParent || (!allowChildAnimations && parentRunningAnimation); |
1060 | 1116 | }
|
1061 | 1117 | }]);
|
1062 | 1118 |
|
|
0 commit comments