angularjs-rails 1.5.8 → 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d04d5e9d07101020a4ff8ce65da72f687ae662ac
4
- data.tar.gz: a331710f55c8863f808ff55b19e13f7fbea622d9
3
+ metadata.gz: b29491e1b540e7e1d87b0d88f3fbb5b4c9323ad1
4
+ data.tar.gz: b13e9fd5f9ac54ddf962b54cb69b14b5b2c5b0ad
5
5
  SHA512:
6
- metadata.gz: 6a6f1fd278f3a44256c869257be4ba5ce847847dd1f7061bb0e8d865207a7a628bfec0549ed5e7217987325bc5f3423d1bd7306dff786dc39a4d796d1ff46387
7
- data.tar.gz: ac1409c3a42f91f5801b8084e8ade1a5f9f06e2519c55100ff7f2817210d92d88ad890a16037d03f9019e0aa74a8fabba2881f1687d7e743b797f79884d31e24
6
+ metadata.gz: 10e912a2d702bf2ad0b87351940ae8e36fd4c6c5af5293c451dfb08ffc5184b171c66e4f527c378203a1519f28e91c2222d9f46ac700ac1b2956eaace08fa3ff
7
+ data.tar.gz: ea1946717238ceb9794c24141d470c49a681ad3937df953ebbb58d1270c025df4aeb073c61de03863a14f7a928d516c2a9a9baeb47bbbcd82aa41b10f2bc0c5e
data/README.md CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  angularjs-rails wraps the [Angular.js](http://angularjs.org) library for use in Rails 3.1 and above. Assets will minify automatically during production.
4
4
 
5
+ **If you find this gem useful, please consider donating - your contributions will help me keep this gem updated.**
6
+
7
+ [![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=NCT7TZEFY2T9Y)
8
+
9
+
5
10
  ## Usage
6
11
 
7
12
  Add the following to your Gemfile:
@@ -29,4 +34,4 @@ The major, minor, and patch version numbers will always represent the Angular.js
29
34
 
30
35
  ## IMPORTANT: Requesting upgrades for new Angular.js versions
31
36
 
32
- Thanks to Nick Clark, we have an auto-upgrader that will upgrade the package to the latest version. To request that the latest version of Angular.JS be pushed as a gem to RubyGems, please create a new issue instead of pull requests.
37
+ To request that the latest version of Angular.JS be pushed as a gem to RubyGems, please create a new issue instead of pull requests.
@@ -3,4 +3,4 @@ module AngularJS
3
3
  class Engine < ::Rails::Engine
4
4
  end
5
5
  end
6
- end
6
+ end
@@ -1,6 +1,6 @@
1
1
  module AngularJS
2
2
  module Rails
3
- VERSION = "1.5.8"
3
+ VERSION = "1.8.0"
4
4
  UNSTABLE_VERSION = "2.0.0-beta.17"
5
5
  end
6
6
  end
@@ -1,6 +1,6 @@
1
1
  /**
2
- * @license AngularJS v1.5.8
3
- * (c) 2010-2016 Google, Inc. http://angularjs.org
2
+ * @license AngularJS v1.8.0
3
+ * (c) 2010-2020 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
6
6
  (function(window, angular) {'use strict';
@@ -29,7 +29,7 @@ var CSS_PREFIX = '', TRANSITION_PROP, TRANSITIONEND_EVENT, ANIMATION_PROP, ANIMA
29
29
  // Also, the only modern browser that uses vendor prefixes for transitions/keyframes is webkit
30
30
  // therefore there is no reason to test anymore for other vendor prefixes:
31
31
  // http://caniuse.com/#search=transition
32
- if ((window.ontransitionend === void 0) && (window.onwebkittransitionend !== void 0)) {
32
+ if ((window.ontransitionend === undefined) && (window.onwebkittransitionend !== undefined)) {
33
33
  CSS_PREFIX = '-webkit-';
34
34
  TRANSITION_PROP = 'WebkitTransition';
35
35
  TRANSITIONEND_EVENT = 'webkitTransitionEnd transitionend';
@@ -38,7 +38,7 @@ if ((window.ontransitionend === void 0) && (window.onwebkittransitionend !== voi
38
38
  TRANSITIONEND_EVENT = 'transitionend';
39
39
  }
40
40
 
41
- if ((window.onanimationend === void 0) && (window.onwebkitanimationend !== void 0)) {
41
+ if ((window.onanimationend === undefined) && (window.onwebkitanimationend !== undefined)) {
42
42
  CSS_PREFIX = '-webkit-';
43
43
  ANIMATION_PROP = 'WebkitAnimation';
44
44
  ANIMATIONEND_EVENT = 'webkitAnimationEnd animationend';
@@ -63,7 +63,7 @@ var TRANSITION_DURATION_PROP = TRANSITION_PROP + DURATION_KEY;
63
63
  var ngMinErr = angular.$$minErr('ng');
64
64
  function assertArg(arg, name, reason) {
65
65
  if (!arg) {
66
- throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required"));
66
+ throw ngMinErr('areq', 'Argument \'{0}\' is {1}', (name || '?'), (reason || 'required'));
67
67
  }
68
68
  return arg;
69
69
  }
@@ -139,7 +139,7 @@ function extractElementNode(element) {
139
139
  if (!element[0]) return element;
140
140
  for (var i = 0; i < element.length; i++) {
141
141
  var elm = element[i];
142
- if (elm.nodeType == ELEMENT_NODE) {
142
+ if (elm.nodeType === ELEMENT_NODE) {
143
143
  return elm;
144
144
  }
145
145
  }
@@ -306,7 +306,7 @@ function getDomNode(element) {
306
306
  return (element instanceof jqLite) ? element[0] : element;
307
307
  }
308
308
 
309
- function applyGeneratedPreparationClasses(element, event, options) {
309
+ function applyGeneratedPreparationClasses($$jqLite, element, event, options) {
310
310
  var classes = '';
311
311
  if (event) {
312
312
  classes = pendClasses(event, EVENT_CLASS_PREFIX, true);
@@ -334,15 +334,6 @@ function clearGeneratedClasses(element, options) {
334
334
  }
335
335
  }
336
336
 
337
- function blockTransitions(node, duration) {
338
- // we use a negative delay value since it performs blocking
339
- // yet it doesn't kill any existing transitions running on the
340
- // same element which makes this safe for class-based animations
341
- var value = duration ? '-' + duration + 's' : '';
342
- applyInlineStyle(node, [TRANSITION_DELAY_PROP, value]);
343
- return [TRANSITION_DELAY_PROP, value];
344
- }
345
-
346
337
  function blockKeyframeAnimations(node, applyBlock) {
347
338
  var value = applyBlock ? 'paused' : '';
348
339
  var key = ANIMATION_PROP + ANIMATION_PLAYSTATE_KEY;
@@ -362,6 +353,17 @@ function concatWithSpace(a,b) {
362
353
  return a + ' ' + b;
363
354
  }
364
355
 
356
+ var helpers = {
357
+ blockTransitions: function(node, duration) {
358
+ // we use a negative delay value since it performs blocking
359
+ // yet it doesn't kill any existing transitions running on the
360
+ // same element which makes this safe for class-based animations
361
+ var value = duration ? '-' + duration + 's' : '';
362
+ applyInlineStyle(node, [TRANSITION_DELAY_PROP, value]);
363
+ return [TRANSITION_DELAY_PROP, value];
364
+ }
365
+ };
366
+
365
367
  var $$rAFSchedulerFactory = ['$$rAF', function($$rAF) {
366
368
  var queue, cancelFn;
367
369
 
@@ -423,7 +425,7 @@ var $$rAFSchedulerFactory = ['$$rAF', function($$rAF) {
423
425
  * of the children's parents are currently animating. By default, when an element has an active `enter`, `leave`, or `move`
424
426
  * (structural) animation, child elements that also have an active structural animation are not animated.
425
427
  *
426
- * Note that even if `ngAnimteChildren` is set, no child animations will run when the parent element is removed from the DOM (`leave` animation).
428
+ * Note that even if `ngAnimateChildren` is set, no child animations will run when the parent element is removed from the DOM (`leave` animation).
427
429
  *
428
430
  *
429
431
  * @param {string} ngAnimateChildren If the value is empty, `true` or `on`,
@@ -432,7 +434,7 @@ var $$rAFSchedulerFactory = ['$$rAF', function($$rAF) {
432
434
  * @example
433
435
  * <example module="ngAnimateChildren" name="ngAnimateChildren" deps="angular-animate.js" animations="true">
434
436
  <file name="index.html">
435
- <div ng-controller="mainController as main">
437
+ <div ng-controller="MainController as main">
436
438
  <label>Show container? <input type="checkbox" ng-model="main.enterElement" /></label>
437
439
  <label>Animate children? <input type="checkbox" ng-model="main.animateChildren" /></label>
438
440
  <hr>
@@ -482,7 +484,7 @@ var $$rAFSchedulerFactory = ['$$rAF', function($$rAF) {
482
484
  </file>
483
485
  <file name="script.js">
484
486
  angular.module('ngAnimateChildren', ['ngAnimate'])
485
- .controller('mainController', function() {
487
+ .controller('MainController', function MainController() {
486
488
  this.animateChildren = false;
487
489
  this.enterElement = false;
488
490
  });
@@ -510,6 +512,8 @@ var $$AnimateChildrenDirective = ['$interpolate', function($interpolate) {
510
512
  };
511
513
  }];
512
514
 
515
+ /* exported $AnimateCssProvider */
516
+
513
517
  var ANIMATE_TIMER_KEY = '$$animateCss';
514
518
 
515
519
  /**
@@ -526,11 +530,11 @@ var ANIMATE_TIMER_KEY = '$$animateCss';
526
530
  * Note that only browsers that support CSS transitions and/or keyframe animations are capable of
527
531
  * rendering animations triggered via `$animateCss` (bad news for IE9 and lower).
528
532
  *
529
- * ## Usage
533
+ * ## General Use
530
534
  * Once again, `$animateCss` is designed to be used inside of a registered JavaScript animation that
531
535
  * is powered by ngAnimate. It is possible to use `$animateCss` directly inside of a directive, however,
532
536
  * any automatic control over cancelling animations and/or preventing animations from being run on
533
- * child elements will not be handled by Angular. For this to work as expected, please use `$animate` to
537
+ * child elements will not be handled by AngularJS. For this to work as expected, please use `$animate` to
534
538
  * trigger the animation and then setup a JavaScript animation that injects `$animateCss` to trigger
535
539
  * the CSS animation.
536
540
  *
@@ -727,7 +731,6 @@ var ANIMATE_TIMER_KEY = '$$animateCss';
727
731
  * * `end` - This method will cancel the animation and remove all applied CSS classes and styles.
728
732
  */
729
733
  var ONE_SECOND = 1000;
730
- var BASE_TEN = 10;
731
734
 
732
735
  var ELAPSED_TIME_MAX_DECIMAL_PLACES = 3;
733
736
  var CLOSING_TIME_BUFFER = 1.5;
@@ -789,7 +792,7 @@ function parseMaxTime(str) {
789
792
  forEach(values, function(value) {
790
793
  // it's always safe to consider only second values and omit `ms` values since
791
794
  // getComputedStyle will always handle the conversion for us
792
- if (value.charAt(value.length - 1) == 's') {
795
+ if (value.charAt(value.length - 1) === 's') {
793
796
  value = value.substring(0, value.length - 1);
794
797
  }
795
798
  value = parseFloat(value) || 0;
@@ -813,33 +816,6 @@ function getCssTransitionDurationStyle(duration, applyOnlyDuration) {
813
816
  return [style, value];
814
817
  }
815
818
 
816
- function createLocalCacheLookup() {
817
- var cache = Object.create(null);
818
- return {
819
- flush: function() {
820
- cache = Object.create(null);
821
- },
822
-
823
- count: function(key) {
824
- var entry = cache[key];
825
- return entry ? entry.total : 0;
826
- },
827
-
828
- get: function(key) {
829
- var entry = cache[key];
830
- return entry && entry.value;
831
- },
832
-
833
- put: function(key, value) {
834
- if (!cache[key]) {
835
- cache[key] = { total: 1, value: value };
836
- } else {
837
- cache[key].total++;
838
- }
839
- }
840
- };
841
- }
842
-
843
819
  // we do not reassign an already present style value since
844
820
  // if we detect the style property value again we may be
845
821
  // detecting styles that were added via the `from` styles.
@@ -857,27 +833,17 @@ function registerRestorableStyles(backup, node, properties) {
857
833
  });
858
834
  }
859
835
 
860
- var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
861
- var gcsLookup = createLocalCacheLookup();
862
- var gcsStaggerLookup = createLocalCacheLookup();
836
+ var $AnimateCssProvider = ['$animateProvider', /** @this */ function($animateProvider) {
863
837
 
864
- this.$get = ['$window', '$$jqLite', '$$AnimateRunner', '$timeout',
838
+ this.$get = ['$window', '$$jqLite', '$$AnimateRunner', '$timeout', '$$animateCache',
865
839
  '$$forceReflow', '$sniffer', '$$rAFScheduler', '$$animateQueue',
866
- function($window, $$jqLite, $$AnimateRunner, $timeout,
840
+ function($window, $$jqLite, $$AnimateRunner, $timeout, $$animateCache,
867
841
  $$forceReflow, $sniffer, $$rAFScheduler, $$animateQueue) {
868
842
 
869
843
  var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
870
844
 
871
- var parentCounter = 0;
872
- function gcsHashFn(node, extraClasses) {
873
- var KEY = "$$ngAnimateParentKey";
874
- var parentNode = node.parentNode;
875
- var parentID = parentNode[KEY] || (parentNode[KEY] = ++parentCounter);
876
- return parentID + '-' + node.getAttribute('class') + '-' + extraClasses;
877
- }
878
-
879
- function computeCachedCssStyles(node, className, cacheKey, properties) {
880
- var timings = gcsLookup.get(cacheKey);
845
+ function computeCachedCssStyles(node, className, cacheKey, allowNoDuration, properties) {
846
+ var timings = $$animateCache.get(cacheKey);
881
847
 
882
848
  if (!timings) {
883
849
  timings = computeCssStyles($window, node, properties);
@@ -886,20 +852,26 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
886
852
  }
887
853
  }
888
854
 
855
+ // if a css animation has no duration we
856
+ // should mark that so that repeated addClass/removeClass calls are skipped
857
+ var hasDuration = allowNoDuration || (timings.transitionDuration > 0 || timings.animationDuration > 0);
858
+
889
859
  // we keep putting this in multiple times even though the value and the cacheKey are the same
890
860
  // because we're keeping an internal tally of how many duplicate animations are detected.
891
- gcsLookup.put(cacheKey, timings);
861
+ $$animateCache.put(cacheKey, timings, hasDuration);
862
+
892
863
  return timings;
893
864
  }
894
865
 
895
866
  function computeCachedCssStaggerStyles(node, className, cacheKey, properties) {
896
867
  var stagger;
868
+ var staggerCacheKey = 'stagger-' + cacheKey;
897
869
 
898
870
  // if we have one or more existing matches of matching elements
899
871
  // containing the same parent + CSS styles (which is how cacheKey works)
900
872
  // then staggering is possible
901
- if (gcsLookup.count(cacheKey) > 0) {
902
- stagger = gcsStaggerLookup.get(cacheKey);
873
+ if ($$animateCache.count(cacheKey) > 0) {
874
+ stagger = $$animateCache.get(staggerCacheKey);
903
875
 
904
876
  if (!stagger) {
905
877
  var staggerClassName = pendClasses(className, '-stagger');
@@ -914,20 +886,18 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
914
886
 
915
887
  $$jqLite.removeClass(node, staggerClassName);
916
888
 
917
- gcsStaggerLookup.put(cacheKey, stagger);
889
+ $$animateCache.put(staggerCacheKey, stagger, true);
918
890
  }
919
891
  }
920
892
 
921
893
  return stagger || {};
922
894
  }
923
895
 
924
- var cancelLastRAFRequest;
925
896
  var rafWaitQueue = [];
926
897
  function waitUntilQuiet(callback) {
927
898
  rafWaitQueue.push(callback);
928
899
  $$rAFScheduler.waitUntilQuiet(function() {
929
- gcsLookup.flush();
930
- gcsStaggerLookup.flush();
900
+ $$animateCache.flush();
931
901
 
932
902
  // DO NOT REMOVE THIS LINE OR REFACTOR OUT THE `pageWidth` variable.
933
903
  // PLEASE EXAMINE THE `$$forceReflow` service to understand why.
@@ -942,8 +912,8 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
942
912
  });
943
913
  }
944
914
 
945
- function computeTimings(node, className, cacheKey) {
946
- var timings = computeCachedCssStyles(node, className, cacheKey, DETECT_CSS_PROPERTIES);
915
+ function computeTimings(node, className, cacheKey, allowNoDuration) {
916
+ var timings = computeCachedCssStyles(node, className, cacheKey, allowNoDuration, DETECT_CSS_PROPERTIES);
947
917
  var aD = timings.animationDelay;
948
918
  var tD = timings.transitionDelay;
949
919
  timings.maxDelay = aD && tD
@@ -1030,7 +1000,6 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
1030
1000
 
1031
1001
  var preparationClasses = [structuralClassName, addRemoveClassName].join(' ').trim();
1032
1002
  var fullClassName = classes + ' ' + preparationClasses;
1033
- var activeClasses = pendClasses(preparationClasses, ACTIVE_CLASS_SUFFIX);
1034
1003
  var hasToStyles = styles.to && Object.keys(styles.to).length > 0;
1035
1004
  var containsKeyframeAnimation = (options.keyframeStyle || '').length > 0;
1036
1005
 
@@ -1043,7 +1012,12 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
1043
1012
  return closeAndReturnNoopAnimator();
1044
1013
  }
1045
1014
 
1046
- var cacheKey, stagger;
1015
+ var stagger, cacheKey = $$animateCache.cacheKey(node, method, options.addClass, options.removeClass);
1016
+ if ($$animateCache.containsCachedAnimationWithoutDuration(cacheKey)) {
1017
+ preparationClasses = null;
1018
+ return closeAndReturnNoopAnimator();
1019
+ }
1020
+
1047
1021
  if (options.stagger > 0) {
1048
1022
  var staggerVal = parseFloat(options.stagger);
1049
1023
  stagger = {
@@ -1053,7 +1027,6 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
1053
1027
  animationDuration: 0
1054
1028
  };
1055
1029
  } else {
1056
- cacheKey = gcsHashFn(node, fullClassName);
1057
1030
  stagger = computeCachedCssStaggerStyles(node, preparationClasses, cacheKey, DETECT_STAGGER_CSS_PROPERTIES);
1058
1031
  }
1059
1032
 
@@ -1087,7 +1060,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
1087
1060
  var itemIndex = stagger
1088
1061
  ? options.staggerIndex >= 0
1089
1062
  ? options.staggerIndex
1090
- : gcsLookup.count(cacheKey)
1063
+ : $$animateCache.count(cacheKey)
1091
1064
  : 0;
1092
1065
 
1093
1066
  var isFirst = itemIndex === 0;
@@ -1099,10 +1072,10 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
1099
1072
  // that if there is no transition defined then nothing will happen and this will also allow
1100
1073
  // other transitions to be stacked on top of each other without any chopping them out.
1101
1074
  if (isFirst && !options.skipBlocking) {
1102
- blockTransitions(node, SAFE_FAST_FORWARD_DURATION_VALUE);
1075
+ helpers.blockTransitions(node, SAFE_FAST_FORWARD_DURATION_VALUE);
1103
1076
  }
1104
1077
 
1105
- var timings = computeTimings(node, fullClassName, cacheKey);
1078
+ var timings = computeTimings(node, fullClassName, cacheKey, !isStructural);
1106
1079
  var relativeDelay = timings.maxDelay;
1107
1080
  maxDelay = Math.max(relativeDelay, 0);
1108
1081
  maxDuration = timings.maxDuration;
@@ -1110,7 +1083,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
1110
1083
  var flags = {};
1111
1084
  flags.hasTransitions = timings.transitionDuration > 0;
1112
1085
  flags.hasAnimations = timings.animationDuration > 0;
1113
- flags.hasTransitionAll = flags.hasTransitions && timings.transitionProperty == 'all';
1086
+ flags.hasTransitionAll = flags.hasTransitions && timings.transitionProperty === 'all';
1114
1087
  flags.applyTransitionDuration = hasToStyles && (
1115
1088
  (flags.hasTransitions && !flags.hasTransitionAll)
1116
1089
  || (flags.hasAnimations && !flags.hasTransitions));
@@ -1140,9 +1113,11 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
1140
1113
  return closeAndReturnNoopAnimator();
1141
1114
  }
1142
1115
 
1116
+ var activeClasses = pendClasses(preparationClasses, ACTIVE_CLASS_SUFFIX);
1117
+
1143
1118
  if (options.delay != null) {
1144
1119
  var delayStyle;
1145
- if (typeof options.delay !== "boolean") {
1120
+ if (typeof options.delay !== 'boolean') {
1146
1121
  delayStyle = parseFloat(options.delay);
1147
1122
  // number in options.delay means we have to recalculate the delay for the closing timeout
1148
1123
  maxDelay = Math.max(delayStyle, 0);
@@ -1183,7 +1158,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
1183
1158
  if (flags.blockTransition || flags.blockKeyframeAnimation) {
1184
1159
  applyBlocking(maxDuration);
1185
1160
  } else if (!options.skipBlocking) {
1186
- blockTransitions(node, false);
1161
+ helpers.blockTransitions(node, false);
1187
1162
  }
1188
1163
 
1189
1164
  // TODO(matsko): for 1.5 change this code to have an animator object for better debugging
@@ -1220,20 +1195,23 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
1220
1195
  close(true);
1221
1196
  }
1222
1197
 
1223
- function close(rejected) { // jshint ignore:line
1198
+ function close(rejected) {
1224
1199
  // if the promise has been called already then we shouldn't close
1225
1200
  // the animation again
1226
1201
  if (animationClosed || (animationCompleted && animationPaused)) return;
1227
1202
  animationClosed = true;
1228
1203
  animationPaused = false;
1229
1204
 
1230
- if (!options.$$skipPreparationClasses) {
1205
+ if (preparationClasses && !options.$$skipPreparationClasses) {
1231
1206
  $$jqLite.removeClass(element, preparationClasses);
1232
1207
  }
1233
- $$jqLite.removeClass(element, activeClasses);
1208
+
1209
+ if (activeClasses) {
1210
+ $$jqLite.removeClass(element, activeClasses);
1211
+ }
1234
1212
 
1235
1213
  blockKeyframeAnimations(node, false);
1236
- blockTransitions(node, false);
1214
+ helpers.blockTransitions(node, false);
1237
1215
 
1238
1216
  forEach(temporaryStyles, function(entry) {
1239
1217
  // There is only one way to remove inline style properties entirely from elements.
@@ -1247,8 +1225,11 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
1247
1225
 
1248
1226
  if (Object.keys(restoreStyles).length) {
1249
1227
  forEach(restoreStyles, function(value, prop) {
1250
- value ? node.style.setProperty(prop, value)
1251
- : node.style.removeProperty(prop);
1228
+ if (value) {
1229
+ node.style.setProperty(prop, value);
1230
+ } else {
1231
+ node.style.removeProperty(prop);
1232
+ }
1252
1233
  });
1253
1234
  }
1254
1235
 
@@ -1281,7 +1262,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
1281
1262
 
1282
1263
  function applyBlocking(duration) {
1283
1264
  if (flags.blockTransition) {
1284
- blockTransitions(node, duration);
1265
+ helpers.blockTransitions(node, duration);
1285
1266
  }
1286
1267
 
1287
1268
  if (flags.blockKeyframeAnimation) {
@@ -1312,6 +1293,12 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
1312
1293
  event.stopPropagation();
1313
1294
  var ev = event.originalEvent || event;
1314
1295
 
1296
+ if (ev.target !== node) {
1297
+ // Since TransitionEvent / AnimationEvent bubble up,
1298
+ // we have to ignore events by finished child animations
1299
+ return;
1300
+ }
1301
+
1315
1302
  // we now always use `Date.now()` due to the recent changes with
1316
1303
  // event.timeStamp in Firefox, Webkit and Chrome (see #13494 for more info)
1317
1304
  var timeStamp = ev.$manualTimeStamp || Date.now();
@@ -1351,9 +1338,11 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
1351
1338
  animationPaused = !playAnimation;
1352
1339
  if (timings.animationDuration) {
1353
1340
  var value = blockKeyframeAnimations(node, animationPaused);
1354
- animationPaused
1355
- ? temporaryStyles.push(value)
1356
- : removeFromArray(temporaryStyles, value);
1341
+ if (animationPaused) {
1342
+ temporaryStyles.push(value);
1343
+ } else {
1344
+ removeFromArray(temporaryStyles, value);
1345
+ }
1357
1346
  }
1358
1347
  } else if (animationPaused && playAnimation) {
1359
1348
  animationPaused = false;
@@ -1402,10 +1391,10 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
1402
1391
  $$jqLite.addClass(element, activeClasses);
1403
1392
 
1404
1393
  if (flags.recalculateTimingStyles) {
1405
- fullClassName = node.className + ' ' + preparationClasses;
1406
- cacheKey = gcsHashFn(node, fullClassName);
1394
+ fullClassName = node.getAttribute('class') + ' ' + preparationClasses;
1395
+ cacheKey = $$animateCache.cacheKey(node, method, options.addClass, options.removeClass);
1407
1396
 
1408
- timings = computeTimings(node, fullClassName, cacheKey);
1397
+ timings = computeTimings(node, fullClassName, cacheKey, false);
1409
1398
  relativeDelay = timings.maxDelay;
1410
1399
  maxDelay = Math.max(relativeDelay, 0);
1411
1400
  maxDuration = timings.maxDuration;
@@ -1420,7 +1409,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
1420
1409
  }
1421
1410
 
1422
1411
  if (flags.applyAnimationDelay) {
1423
- relativeDelay = typeof options.delay !== "boolean" && truthyTimingValue(options.delay)
1412
+ relativeDelay = typeof options.delay !== 'boolean' && truthyTimingValue(options.delay)
1424
1413
  ? parseFloat(options.delay)
1425
1414
  : relativeDelay;
1426
1415
 
@@ -1512,7 +1501,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
1512
1501
  }];
1513
1502
  }];
1514
1503
 
1515
- var $$AnimateCssDriverProvider = ['$$animationProvider', function($$animationProvider) {
1504
+ var $$AnimateCssDriverProvider = ['$$animationProvider', /** @this */ function($$animationProvider) {
1516
1505
  $$animationProvider.drivers.push('$$animateCssDriver');
1517
1506
 
1518
1507
  var NG_ANIMATE_SHIM_CLASS_NAME = 'ng-animate-shim';
@@ -1541,8 +1530,6 @@ var $$AnimateCssDriverProvider = ['$$animationProvider', function($$animationPro
1541
1530
  isDocumentFragment(rootNode) || bodyNode.contains(rootNode) ? rootNode : bodyNode
1542
1531
  );
1543
1532
 
1544
- var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
1545
-
1546
1533
  return function initDriverFn(animationDetails) {
1547
1534
  return animationDetails.from && animationDetails.to
1548
1535
  ? prepareFromToAnchorAnimation(animationDetails.from,
@@ -1784,7 +1771,7 @@ var $$AnimateCssDriverProvider = ['$$animationProvider', function($$animationPro
1784
1771
  // TODO(matsko): add documentation
1785
1772
  // by the time...
1786
1773
 
1787
- var $$AnimateJsProvider = ['$animateProvider', function($animateProvider) {
1774
+ var $$AnimateJsProvider = ['$animateProvider', /** @this */ function($animateProvider) {
1788
1775
  this.$get = ['$injector', '$$AnimateRunner', '$$jqLite',
1789
1776
  function($injector, $$AnimateRunner, $$jqLite) {
1790
1777
 
@@ -1823,7 +1810,7 @@ var $$AnimateJsProvider = ['$animateProvider', function($animateProvider) {
1823
1810
  var before, after;
1824
1811
  if (animations.length) {
1825
1812
  var afterFn, beforeFn;
1826
- if (event == 'leave') {
1813
+ if (event === 'leave') {
1827
1814
  beforeFn = 'leave';
1828
1815
  afterFn = 'afterLeave'; // TODO(matsko): get rid of this
1829
1816
  } else {
@@ -2008,7 +1995,7 @@ var $$AnimateJsProvider = ['$animateProvider', function($animateProvider) {
2008
1995
  function packageAnimations(element, event, options, animations, fnName) {
2009
1996
  var operations = groupEventedAnimations(element, event, options, animations, fnName);
2010
1997
  if (operations.length === 0) {
2011
- var a,b;
1998
+ var a, b;
2012
1999
  if (fnName === 'beforeSetClass') {
2013
2000
  a = groupEventedAnimations(element, 'removeClass', options, animations, 'beforeRemoveClass');
2014
2001
  b = groupEventedAnimations(element, 'addClass', options, animations, 'beforeAddClass');
@@ -2036,11 +2023,19 @@ var $$AnimateJsProvider = ['$animateProvider', function($animateProvider) {
2036
2023
  });
2037
2024
  }
2038
2025
 
2039
- runners.length ? $$AnimateRunner.all(runners, callback) : callback();
2026
+ if (runners.length) {
2027
+ $$AnimateRunner.all(runners, callback);
2028
+ } else {
2029
+ callback();
2030
+ }
2040
2031
 
2041
2032
  return function endFn(reject) {
2042
2033
  forEach(runners, function(runner) {
2043
- reject ? runner.cancel() : runner.end();
2034
+ if (reject) {
2035
+ runner.cancel();
2036
+ } else {
2037
+ runner.end();
2038
+ }
2044
2039
  });
2045
2040
  };
2046
2041
  };
@@ -2050,7 +2045,7 @@ var $$AnimateJsProvider = ['$animateProvider', function($animateProvider) {
2050
2045
  function lookupAnimations(classes) {
2051
2046
  classes = isArray(classes) ? classes : classes.split(' ');
2052
2047
  var matches = [], flagMap = {};
2053
- for (var i=0; i < classes.length; i++) {
2048
+ for (var i = 0; i < classes.length; i++) {
2054
2049
  var klass = classes[i],
2055
2050
  animationFactory = $animateProvider.$$registeredAnimations[klass];
2056
2051
  if (animationFactory && !flagMap[klass]) {
@@ -2063,7 +2058,7 @@ var $$AnimateJsProvider = ['$animateProvider', function($animateProvider) {
2063
2058
  }];
2064
2059
  }];
2065
2060
 
2066
- var $$AnimateJsDriverProvider = ['$$animationProvider', function($$animationProvider) {
2061
+ var $$AnimateJsDriverProvider = ['$$animationProvider', /** @this */ function($$animationProvider) {
2067
2062
  $$animationProvider.drivers.push('$$animateJsDriver');
2068
2063
  this.$get = ['$$animateJs', '$$AnimateRunner', function($$animateJs, $$AnimateRunner) {
2069
2064
  return function initDriverFn(animationDetails) {
@@ -2125,7 +2120,7 @@ var $$AnimateJsDriverProvider = ['$$animationProvider', function($$animationProv
2125
2120
 
2126
2121
  var NG_ANIMATE_ATTR_NAME = 'data-ng-animate';
2127
2122
  var NG_ANIMATE_PIN_DATA = '$ngAnimatePin';
2128
- var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
2123
+ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animateProvider) {
2129
2124
  var PRE_DIGEST_STATE = 1;
2130
2125
  var RUNNING_STATE = 2;
2131
2126
  var ONE_SPACE = ' ';
@@ -2136,6 +2131,15 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
2136
2131
  join: []
2137
2132
  };
2138
2133
 
2134
+ function getEventData(options) {
2135
+ return {
2136
+ addClass: options.addClass,
2137
+ removeClass: options.removeClass,
2138
+ from: options.from,
2139
+ to: options.to
2140
+ };
2141
+ }
2142
+
2139
2143
  function makeTruthyCssClassMap(classString) {
2140
2144
  if (!classString) {
2141
2145
  return null;
@@ -2159,9 +2163,9 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
2159
2163
  }
2160
2164
  }
2161
2165
 
2162
- function isAllowed(ruleType, element, currentAnimation, previousAnimation) {
2166
+ function isAllowed(ruleType, currentAnimation, previousAnimation) {
2163
2167
  return rules[ruleType].some(function(fn) {
2164
- return fn(element, currentAnimation, previousAnimation);
2168
+ return fn(currentAnimation, previousAnimation);
2165
2169
  });
2166
2170
  }
2167
2171
 
@@ -2171,40 +2175,40 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
2171
2175
  return and ? a && b : a || b;
2172
2176
  }
2173
2177
 
2174
- rules.join.push(function(element, newAnimation, currentAnimation) {
2178
+ rules.join.push(function(newAnimation, currentAnimation) {
2175
2179
  // if the new animation is class-based then we can just tack that on
2176
2180
  return !newAnimation.structural && hasAnimationClasses(newAnimation);
2177
2181
  });
2178
2182
 
2179
- rules.skip.push(function(element, newAnimation, currentAnimation) {
2183
+ rules.skip.push(function(newAnimation, currentAnimation) {
2180
2184
  // there is no need to animate anything if no classes are being added and
2181
2185
  // there is no structural animation that will be triggered
2182
2186
  return !newAnimation.structural && !hasAnimationClasses(newAnimation);
2183
2187
  });
2184
2188
 
2185
- rules.skip.push(function(element, newAnimation, currentAnimation) {
2189
+ rules.skip.push(function(newAnimation, currentAnimation) {
2186
2190
  // why should we trigger a new structural animation if the element will
2187
2191
  // be removed from the DOM anyway?
2188
- return currentAnimation.event == 'leave' && newAnimation.structural;
2192
+ return currentAnimation.event === 'leave' && newAnimation.structural;
2189
2193
  });
2190
2194
 
2191
- rules.skip.push(function(element, newAnimation, currentAnimation) {
2195
+ rules.skip.push(function(newAnimation, currentAnimation) {
2192
2196
  // if there is an ongoing current animation then don't even bother running the class-based animation
2193
2197
  return currentAnimation.structural && currentAnimation.state === RUNNING_STATE && !newAnimation.structural;
2194
2198
  });
2195
2199
 
2196
- rules.cancel.push(function(element, newAnimation, currentAnimation) {
2200
+ rules.cancel.push(function(newAnimation, currentAnimation) {
2197
2201
  // there can never be two structural animations running at the same time
2198
2202
  return currentAnimation.structural && newAnimation.structural;
2199
2203
  });
2200
2204
 
2201
- rules.cancel.push(function(element, newAnimation, currentAnimation) {
2205
+ rules.cancel.push(function(newAnimation, currentAnimation) {
2202
2206
  // if the previous animation is already running, but the new animation will
2203
2207
  // be triggered, but the new animation is structural
2204
2208
  return currentAnimation.state === RUNNING_STATE && newAnimation.structural;
2205
2209
  });
2206
2210
 
2207
- rules.cancel.push(function(element, newAnimation, currentAnimation) {
2211
+ rules.cancel.push(function(newAnimation, currentAnimation) {
2208
2212
  // cancel the animation if classes added / removed in both animation cancel each other out,
2209
2213
  // but only if the current animation isn't structural
2210
2214
 
@@ -2223,15 +2227,21 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
2223
2227
  return hasMatchingClasses(nA, cR) || hasMatchingClasses(nR, cA);
2224
2228
  });
2225
2229
 
2226
- this.$get = ['$$rAF', '$rootScope', '$rootElement', '$document', '$$HashMap',
2230
+ this.$get = ['$$rAF', '$rootScope', '$rootElement', '$document', '$$Map',
2227
2231
  '$$animation', '$$AnimateRunner', '$templateRequest', '$$jqLite', '$$forceReflow',
2228
- function($$rAF, $rootScope, $rootElement, $document, $$HashMap,
2229
- $$animation, $$AnimateRunner, $templateRequest, $$jqLite, $$forceReflow) {
2232
+ '$$isDocumentHidden',
2233
+ function($$rAF, $rootScope, $rootElement, $document, $$Map,
2234
+ $$animation, $$AnimateRunner, $templateRequest, $$jqLite, $$forceReflow,
2235
+ $$isDocumentHidden) {
2230
2236
 
2231
- var activeAnimationsLookup = new $$HashMap();
2232
- var disabledElementsLookup = new $$HashMap();
2237
+ var activeAnimationsLookup = new $$Map();
2238
+ var disabledElementsLookup = new $$Map();
2233
2239
  var animationsEnabled = null;
2234
2240
 
2241
+ function removeFromDisabledElementsLookup(evt) {
2242
+ disabledElementsLookup.delete(evt.target);
2243
+ }
2244
+
2235
2245
  function postDigestTaskFactory() {
2236
2246
  var postDigestCalled = false;
2237
2247
  return function(fn) {
@@ -2281,14 +2291,17 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
2281
2291
 
2282
2292
  var callbackRegistry = Object.create(null);
2283
2293
 
2284
- // remember that the classNameFilter is set during the provider/config
2285
- // stage therefore we can optimize here and setup a helper function
2294
+ // remember that the `customFilter`/`classNameFilter` are set during the
2295
+ // provider/config stage therefore we can optimize here and setup helper functions
2296
+ var customFilter = $animateProvider.customFilter();
2286
2297
  var classNameFilter = $animateProvider.classNameFilter();
2287
- var isAnimatableClassName = !classNameFilter
2288
- ? function() { return true; }
2289
- : function(className) {
2290
- return classNameFilter.test(className);
2291
- };
2298
+ var returnTrue = function() { return true; };
2299
+
2300
+ var isAnimatableByFilter = customFilter || returnTrue;
2301
+ var isAnimatableClassName = !classNameFilter ? returnTrue : function(node, options) {
2302
+ var className = [node.getAttribute('class'), options.addClass, options.removeClass].join(' ');
2303
+ return classNameFilter.test(className);
2304
+ };
2292
2305
 
2293
2306
  var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
2294
2307
 
@@ -2297,16 +2310,12 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
2297
2310
  }
2298
2311
 
2299
2312
  // IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.
2300
- var contains = window.Node.prototype.contains || function(arg) {
2301
- // jshint bitwise: false
2313
+ var contains = window.Node.prototype.contains || /** @this */ function(arg) {
2314
+ // eslint-disable-next-line no-bitwise
2302
2315
  return this === arg || !!(this.compareDocumentPosition(arg) & 16);
2303
- // jshint bitwise: true
2304
2316
  };
2305
2317
 
2306
- function findCallbacks(parent, element, event) {
2307
- var targetNode = getDomNode(element);
2308
- var targetParentNode = getDomNode(parent);
2309
-
2318
+ function findCallbacks(targetParentNode, targetNode, event) {
2310
2319
  var matches = [];
2311
2320
  var entries = callbackRegistry[event];
2312
2321
  if (entries) {
@@ -2331,11 +2340,11 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
2331
2340
  });
2332
2341
  }
2333
2342
 
2334
- function cleanupEventListeners(phase, element) {
2335
- if (phase === 'close' && !element[0].parentNode) {
2343
+ function cleanupEventListeners(phase, node) {
2344
+ if (phase === 'close' && !node.parentNode) {
2336
2345
  // If the element is not attached to a parentNode, it has been removed by
2337
2346
  // the domOperation, and we can safely remove the event callbacks
2338
- $animate.off(element);
2347
+ $animate.off(node);
2339
2348
  }
2340
2349
  }
2341
2350
 
@@ -2416,7 +2425,12 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
2416
2425
  bool = !disabledElementsLookup.get(node);
2417
2426
  } else {
2418
2427
  // (element, bool) - Element setter
2419
- disabledElementsLookup.put(node, !bool);
2428
+ if (!disabledElementsLookup.has(node)) {
2429
+ // The element is added to the map for the first time.
2430
+ // Create a listener to remove it on `$destroy` (to avoid memory leak).
2431
+ jqLite(element).on('$destroy', removeFromDisabledElementsLookup);
2432
+ }
2433
+ disabledElementsLookup.set(node, !bool);
2420
2434
  }
2421
2435
  }
2422
2436
  }
@@ -2427,18 +2441,15 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
2427
2441
 
2428
2442
  return $animate;
2429
2443
 
2430
- function queueAnimation(element, event, initialOptions) {
2444
+ function queueAnimation(originalElement, event, initialOptions) {
2431
2445
  // we always make a copy of the options since
2432
2446
  // there should never be any side effects on
2433
2447
  // the input data when running `$animateCss`.
2434
2448
  var options = copy(initialOptions);
2435
2449
 
2436
- var node, parent;
2437
- element = stripCommentsFromElement(element);
2438
- if (element) {
2439
- node = getDomNode(element);
2440
- parent = element.parent();
2441
- }
2450
+ var element = stripCommentsFromElement(originalElement);
2451
+ var node = getDomNode(element);
2452
+ var parentNode = node && node.parentNode;
2442
2453
 
2443
2454
  options = prepareAnimationOptions(options);
2444
2455
 
@@ -2473,49 +2484,45 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
2473
2484
  options.to = null;
2474
2485
  }
2475
2486
 
2476
- // there are situations where a directive issues an animation for
2477
- // a jqLite wrapper that contains only comment nodes... If this
2478
- // happens then there is no way we can perform an animation
2479
- if (!node) {
2480
- close();
2481
- return runner;
2482
- }
2483
-
2484
- var className = [node.className, options.addClass, options.removeClass].join(' ');
2485
- if (!isAnimatableClassName(className)) {
2487
+ // If animations are hard-disabled for the whole application there is no need to continue.
2488
+ // There are also situations where a directive issues an animation for a jqLite wrapper that
2489
+ // contains only comment nodes. In this case, there is no way we can perform an animation.
2490
+ if (!animationsEnabled ||
2491
+ !node ||
2492
+ !isAnimatableByFilter(node, event, initialOptions) ||
2493
+ !isAnimatableClassName(node, options)) {
2486
2494
  close();
2487
2495
  return runner;
2488
2496
  }
2489
2497
 
2490
2498
  var isStructural = ['enter', 'move', 'leave'].indexOf(event) >= 0;
2491
2499
 
2492
- var documentHidden = $document[0].hidden;
2500
+ var documentHidden = $$isDocumentHidden();
2493
2501
 
2494
- // this is a hard disable of all animations for the application or on
2495
- // the element itself, therefore there is no need to continue further
2496
- // past this point if not enabled
2502
+ // This is a hard disable of all animations the element itself, therefore there is no need to
2503
+ // continue further past this point if not enabled
2497
2504
  // Animations are also disabled if the document is currently hidden (page is not visible
2498
2505
  // to the user), because browsers slow down or do not flush calls to requestAnimationFrame
2499
- var skipAnimations = !animationsEnabled || documentHidden || disabledElementsLookup.get(node);
2506
+ var skipAnimations = documentHidden || disabledElementsLookup.get(node);
2500
2507
  var existingAnimation = (!skipAnimations && activeAnimationsLookup.get(node)) || {};
2501
2508
  var hasExistingAnimation = !!existingAnimation.state;
2502
2509
 
2503
2510
  // there is no point in traversing the same collection of parent ancestors if a followup
2504
2511
  // animation will be run on the same element that already did all that checking work
2505
- if (!skipAnimations && (!hasExistingAnimation || existingAnimation.state != PRE_DIGEST_STATE)) {
2506
- skipAnimations = !areAnimationsAllowed(element, parent, event);
2512
+ if (!skipAnimations && (!hasExistingAnimation || existingAnimation.state !== PRE_DIGEST_STATE)) {
2513
+ skipAnimations = !areAnimationsAllowed(node, parentNode, event);
2507
2514
  }
2508
2515
 
2509
2516
  if (skipAnimations) {
2510
2517
  // Callbacks should fire even if the document is hidden (regression fix for issue #14120)
2511
- if (documentHidden) notifyProgress(runner, event, 'start');
2518
+ if (documentHidden) notifyProgress(runner, event, 'start', getEventData(options));
2512
2519
  close();
2513
- if (documentHidden) notifyProgress(runner, event, 'close');
2520
+ if (documentHidden) notifyProgress(runner, event, 'close', getEventData(options));
2514
2521
  return runner;
2515
2522
  }
2516
2523
 
2517
2524
  if (isStructural) {
2518
- closeChildAnimations(element);
2525
+ closeChildAnimations(node);
2519
2526
  }
2520
2527
 
2521
2528
  var newAnimation = {
@@ -2530,7 +2537,7 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
2530
2537
  };
2531
2538
 
2532
2539
  if (hasExistingAnimation) {
2533
- var skipAnimationFlag = isAllowed('skip', element, newAnimation, existingAnimation);
2540
+ var skipAnimationFlag = isAllowed('skip', newAnimation, existingAnimation);
2534
2541
  if (skipAnimationFlag) {
2535
2542
  if (existingAnimation.state === RUNNING_STATE) {
2536
2543
  close();
@@ -2540,7 +2547,7 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
2540
2547
  return existingAnimation.runner;
2541
2548
  }
2542
2549
  }
2543
- var cancelAnimationFlag = isAllowed('cancel', element, newAnimation, existingAnimation);
2550
+ var cancelAnimationFlag = isAllowed('cancel', newAnimation, existingAnimation);
2544
2551
  if (cancelAnimationFlag) {
2545
2552
  if (existingAnimation.state === RUNNING_STATE) {
2546
2553
  // this will end the animation right away and it is safe
@@ -2562,12 +2569,12 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
2562
2569
  // a joined animation means that this animation will take over the existing one
2563
2570
  // so an example would involve a leave animation taking over an enter. Then when
2564
2571
  // the postDigest kicks in the enter will be ignored.
2565
- var joinAnimationFlag = isAllowed('join', element, newAnimation, existingAnimation);
2572
+ var joinAnimationFlag = isAllowed('join', newAnimation, existingAnimation);
2566
2573
  if (joinAnimationFlag) {
2567
2574
  if (existingAnimation.state === RUNNING_STATE) {
2568
2575
  normalizeAnimationDetails(element, newAnimation);
2569
2576
  } else {
2570
- applyGeneratedPreparationClasses(element, isStructural ? event : null, options);
2577
+ applyGeneratedPreparationClasses($$jqLite, element, isStructural ? event : null, options);
2571
2578
 
2572
2579
  event = newAnimation.event = existingAnimation.event;
2573
2580
  options = mergeAnimationDetails(element, existingAnimation, newAnimation);
@@ -2596,7 +2603,7 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
2596
2603
 
2597
2604
  if (!isValidAnimation) {
2598
2605
  close();
2599
- clearElementAnimationState(element);
2606
+ clearElementAnimationState(node);
2600
2607
  return runner;
2601
2608
  }
2602
2609
 
@@ -2604,9 +2611,18 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
2604
2611
  var counter = (existingAnimation.counter || 0) + 1;
2605
2612
  newAnimation.counter = counter;
2606
2613
 
2607
- markElementAnimationState(element, PRE_DIGEST_STATE, newAnimation);
2614
+ markElementAnimationState(node, PRE_DIGEST_STATE, newAnimation);
2608
2615
 
2609
2616
  $rootScope.$$postDigest(function() {
2617
+ // It is possible that the DOM nodes inside `originalElement` have been replaced. This can
2618
+ // happen if the animated element is a transcluded clone and also has a `templateUrl`
2619
+ // directive on it. Therefore, we must recreate `element` in order to interact with the
2620
+ // actual DOM nodes.
2621
+ // Note: We still need to use the old `node` for certain things, such as looking up in
2622
+ // HashMaps where it was used as the key.
2623
+
2624
+ element = stripCommentsFromElement(originalElement);
2625
+
2610
2626
  var animationDetails = activeAnimationsLookup.get(node);
2611
2627
  var animationCancelled = !animationDetails;
2612
2628
  animationDetails = animationDetails || {};
@@ -2645,7 +2661,7 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
2645
2661
  // isn't allowed to animate from here then we need to clear the state of the element
2646
2662
  // so that any future animations won't read the expired animation data.
2647
2663
  if (!isValidAnimation) {
2648
- clearElementAnimationState(element);
2664
+ clearElementAnimationState(node);
2649
2665
  }
2650
2666
 
2651
2667
  return;
@@ -2657,21 +2673,21 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
2657
2673
  ? 'setClass'
2658
2674
  : animationDetails.event;
2659
2675
 
2660
- markElementAnimationState(element, RUNNING_STATE);
2676
+ markElementAnimationState(node, RUNNING_STATE);
2661
2677
  var realRunner = $$animation(element, event, animationDetails.options);
2662
2678
 
2663
2679
  // this will update the runner's flow-control events based on
2664
2680
  // the `realRunner` object.
2665
2681
  runner.setHost(realRunner);
2666
- notifyProgress(runner, event, 'start', {});
2682
+ notifyProgress(runner, event, 'start', getEventData(options));
2667
2683
 
2668
2684
  realRunner.done(function(status) {
2669
2685
  close(!status);
2670
2686
  var animationDetails = activeAnimationsLookup.get(node);
2671
2687
  if (animationDetails && animationDetails.counter === counter) {
2672
- clearElementAnimationState(getDomNode(element));
2688
+ clearElementAnimationState(node);
2673
2689
  }
2674
- notifyProgress(runner, event, 'close', {});
2690
+ notifyProgress(runner, event, 'close', getEventData(options));
2675
2691
  });
2676
2692
  });
2677
2693
 
@@ -2679,7 +2695,7 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
2679
2695
 
2680
2696
  function notifyProgress(runner, event, phase, data) {
2681
2697
  runInNextPostDigestOrNow(function() {
2682
- var callbacks = findCallbacks(parent, element, event);
2698
+ var callbacks = findCallbacks(parentNode, node, event);
2683
2699
  if (callbacks.length) {
2684
2700
  // do not optimize this call here to RAF because
2685
2701
  // we don't know how heavy the callback code here will
@@ -2689,16 +2705,16 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
2689
2705
  forEach(callbacks, function(callback) {
2690
2706
  callback(element, phase, data);
2691
2707
  });
2692
- cleanupEventListeners(phase, element);
2708
+ cleanupEventListeners(phase, node);
2693
2709
  });
2694
2710
  } else {
2695
- cleanupEventListeners(phase, element);
2711
+ cleanupEventListeners(phase, node);
2696
2712
  }
2697
2713
  });
2698
2714
  runner.progress(event, phase, data);
2699
2715
  }
2700
2716
 
2701
- function close(reject) { // jshint ignore:line
2717
+ function close(reject) {
2702
2718
  clearGeneratedClasses(element, options);
2703
2719
  applyAnimationClasses(element, options);
2704
2720
  applyAnimationStyles(element, options);
@@ -2707,11 +2723,10 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
2707
2723
  }
2708
2724
  }
2709
2725
 
2710
- function closeChildAnimations(element) {
2711
- var node = getDomNode(element);
2726
+ function closeChildAnimations(node) {
2712
2727
  var children = node.querySelectorAll('[' + NG_ANIMATE_ATTR_NAME + ']');
2713
2728
  forEach(children, function(child) {
2714
- var state = parseInt(child.getAttribute(NG_ANIMATE_ATTR_NAME));
2729
+ var state = parseInt(child.getAttribute(NG_ANIMATE_ATTR_NAME), 10);
2715
2730
  var animationDetails = activeAnimationsLookup.get(child);
2716
2731
  if (animationDetails) {
2717
2732
  switch (state) {
@@ -2719,21 +2734,16 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
2719
2734
  animationDetails.runner.end();
2720
2735
  /* falls through */
2721
2736
  case PRE_DIGEST_STATE:
2722
- activeAnimationsLookup.remove(child);
2737
+ activeAnimationsLookup.delete(child);
2723
2738
  break;
2724
2739
  }
2725
2740
  }
2726
2741
  });
2727
2742
  }
2728
2743
 
2729
- function clearElementAnimationState(element) {
2730
- var node = getDomNode(element);
2744
+ function clearElementAnimationState(node) {
2731
2745
  node.removeAttribute(NG_ANIMATE_ATTR_NAME);
2732
- activeAnimationsLookup.remove(node);
2733
- }
2734
-
2735
- function isMatchingElement(nodeOrElmA, nodeOrElmB) {
2736
- return getDomNode(nodeOrElmA) === getDomNode(nodeOrElmB);
2746
+ activeAnimationsLookup.delete(node);
2737
2747
  }
2738
2748
 
2739
2749
  /**
@@ -2743,54 +2753,54 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
2743
2753
  * c) the element is not a child of the body
2744
2754
  * d) the element is not a child of the $rootElement
2745
2755
  */
2746
- function areAnimationsAllowed(element, parentElement, event) {
2747
- var bodyElement = jqLite($document[0].body);
2748
- var bodyElementDetected = isMatchingElement(element, bodyElement) || element[0].nodeName === 'HTML';
2749
- var rootElementDetected = isMatchingElement(element, $rootElement);
2756
+ function areAnimationsAllowed(node, parentNode, event) {
2757
+ var bodyNode = $document[0].body;
2758
+ var rootNode = getDomNode($rootElement);
2759
+
2760
+ var bodyNodeDetected = (node === bodyNode) || node.nodeName === 'HTML';
2761
+ var rootNodeDetected = (node === rootNode);
2750
2762
  var parentAnimationDetected = false;
2763
+ var elementDisabled = disabledElementsLookup.get(node);
2751
2764
  var animateChildren;
2752
- var elementDisabled = disabledElementsLookup.get(getDomNode(element));
2753
2765
 
2754
- var parentHost = jqLite.data(element[0], NG_ANIMATE_PIN_DATA);
2766
+ var parentHost = jqLite.data(node, NG_ANIMATE_PIN_DATA);
2755
2767
  if (parentHost) {
2756
- parentElement = parentHost;
2768
+ parentNode = getDomNode(parentHost);
2757
2769
  }
2758
2770
 
2759
- parentElement = getDomNode(parentElement);
2760
-
2761
- while (parentElement) {
2762
- if (!rootElementDetected) {
2763
- // angular doesn't want to attempt to animate elements outside of the application
2771
+ while (parentNode) {
2772
+ if (!rootNodeDetected) {
2773
+ // AngularJS doesn't want to attempt to animate elements outside of the application
2764
2774
  // therefore we need to ensure that the rootElement is an ancestor of the current element
2765
- rootElementDetected = isMatchingElement(parentElement, $rootElement);
2775
+ rootNodeDetected = (parentNode === rootNode);
2766
2776
  }
2767
2777
 
2768
- if (parentElement.nodeType !== ELEMENT_NODE) {
2778
+ if (parentNode.nodeType !== ELEMENT_NODE) {
2769
2779
  // no point in inspecting the #document element
2770
2780
  break;
2771
2781
  }
2772
2782
 
2773
- var details = activeAnimationsLookup.get(parentElement) || {};
2783
+ var details = activeAnimationsLookup.get(parentNode) || {};
2774
2784
  // either an enter, leave or move animation will commence
2775
2785
  // therefore we can't allow any animations to take place
2776
2786
  // but if a parent animation is class-based then that's ok
2777
2787
  if (!parentAnimationDetected) {
2778
- var parentElementDisabled = disabledElementsLookup.get(parentElement);
2788
+ var parentNodeDisabled = disabledElementsLookup.get(parentNode);
2779
2789
 
2780
- if (parentElementDisabled === true && elementDisabled !== false) {
2790
+ if (parentNodeDisabled === true && elementDisabled !== false) {
2781
2791
  // disable animations if the user hasn't explicitly enabled animations on the
2782
2792
  // current element
2783
2793
  elementDisabled = true;
2784
2794
  // element is disabled via parent element, no need to check anything else
2785
2795
  break;
2786
- } else if (parentElementDisabled === false) {
2796
+ } else if (parentNodeDisabled === false) {
2787
2797
  elementDisabled = false;
2788
2798
  }
2789
2799
  parentAnimationDetected = details.structural;
2790
2800
  }
2791
2801
 
2792
2802
  if (isUndefined(animateChildren) || animateChildren === true) {
2793
- var value = jqLite.data(parentElement, NG_ANIMATE_CHILDREN_DATA);
2803
+ var value = jqLite.data(parentNode, NG_ANIMATE_CHILDREN_DATA);
2794
2804
  if (isDefined(value)) {
2795
2805
  animateChildren = value;
2796
2806
  }
@@ -2799,57 +2809,115 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
2799
2809
  // there is no need to continue traversing at this point
2800
2810
  if (parentAnimationDetected && animateChildren === false) break;
2801
2811
 
2802
- if (!bodyElementDetected) {
2812
+ if (!bodyNodeDetected) {
2803
2813
  // we also need to ensure that the element is or will be a part of the body element
2804
2814
  // otherwise it is pointless to even issue an animation to be rendered
2805
- bodyElementDetected = isMatchingElement(parentElement, bodyElement);
2815
+ bodyNodeDetected = (parentNode === bodyNode);
2806
2816
  }
2807
2817
 
2808
- if (bodyElementDetected && rootElementDetected) {
2818
+ if (bodyNodeDetected && rootNodeDetected) {
2809
2819
  // If both body and root have been found, any other checks are pointless,
2810
2820
  // as no animation data should live outside the application
2811
2821
  break;
2812
2822
  }
2813
2823
 
2814
- if (!rootElementDetected) {
2815
- // If no rootElement is detected, check if the parentElement is pinned to another element
2816
- parentHost = jqLite.data(parentElement, NG_ANIMATE_PIN_DATA);
2824
+ if (!rootNodeDetected) {
2825
+ // If `rootNode` is not detected, check if `parentNode` is pinned to another element
2826
+ parentHost = jqLite.data(parentNode, NG_ANIMATE_PIN_DATA);
2817
2827
  if (parentHost) {
2818
2828
  // The pin target element becomes the next parent element
2819
- parentElement = getDomNode(parentHost);
2829
+ parentNode = getDomNode(parentHost);
2820
2830
  continue;
2821
2831
  }
2822
2832
  }
2823
2833
 
2824
- parentElement = parentElement.parentNode;
2834
+ parentNode = parentNode.parentNode;
2825
2835
  }
2826
2836
 
2827
2837
  var allowAnimation = (!parentAnimationDetected || animateChildren) && elementDisabled !== true;
2828
- return allowAnimation && rootElementDetected && bodyElementDetected;
2838
+ return allowAnimation && rootNodeDetected && bodyNodeDetected;
2829
2839
  }
2830
2840
 
2831
- function markElementAnimationState(element, state, details) {
2841
+ function markElementAnimationState(node, state, details) {
2832
2842
  details = details || {};
2833
2843
  details.state = state;
2834
2844
 
2835
- var node = getDomNode(element);
2836
2845
  node.setAttribute(NG_ANIMATE_ATTR_NAME, state);
2837
2846
 
2838
2847
  var oldValue = activeAnimationsLookup.get(node);
2839
2848
  var newValue = oldValue
2840
2849
  ? extend(oldValue, details)
2841
2850
  : details;
2842
- activeAnimationsLookup.put(node, newValue);
2851
+ activeAnimationsLookup.set(node, newValue);
2843
2852
  }
2844
2853
  }];
2845
2854
  }];
2846
2855
 
2847
- var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
2856
+ /** @this */
2857
+ var $$AnimateCacheProvider = function() {
2858
+
2859
+ var KEY = '$$ngAnimateParentKey';
2860
+ var parentCounter = 0;
2861
+ var cache = Object.create(null);
2862
+
2863
+ this.$get = [function() {
2864
+ return {
2865
+ cacheKey: function(node, method, addClass, removeClass) {
2866
+ var parentNode = node.parentNode;
2867
+ var parentID = parentNode[KEY] || (parentNode[KEY] = ++parentCounter);
2868
+ var parts = [parentID, method, node.getAttribute('class')];
2869
+ if (addClass) {
2870
+ parts.push(addClass);
2871
+ }
2872
+ if (removeClass) {
2873
+ parts.push(removeClass);
2874
+ }
2875
+ return parts.join(' ');
2876
+ },
2877
+
2878
+ containsCachedAnimationWithoutDuration: function(key) {
2879
+ var entry = cache[key];
2880
+
2881
+ // nothing cached, so go ahead and animate
2882
+ // otherwise it should be a valid animation
2883
+ return (entry && !entry.isValid) || false;
2884
+ },
2885
+
2886
+ flush: function() {
2887
+ cache = Object.create(null);
2888
+ },
2889
+
2890
+ count: function(key) {
2891
+ var entry = cache[key];
2892
+ return entry ? entry.total : 0;
2893
+ },
2894
+
2895
+ get: function(key) {
2896
+ var entry = cache[key];
2897
+ return entry && entry.value;
2898
+ },
2899
+
2900
+ put: function(key, value, isValid) {
2901
+ if (!cache[key]) {
2902
+ cache[key] = { total: 1, value: value, isValid: isValid };
2903
+ } else {
2904
+ cache[key].total++;
2905
+ cache[key].value = value;
2906
+ }
2907
+ }
2908
+ };
2909
+ }];
2910
+ };
2911
+
2912
+ /* exported $$AnimationProvider */
2913
+
2914
+ var $$AnimationProvider = ['$animateProvider', /** @this */ function($animateProvider) {
2848
2915
  var NG_ANIMATE_REF_ATTR = 'ng-animate-ref';
2849
2916
 
2850
2917
  var drivers = this.drivers = [];
2851
2918
 
2852
2919
  var RUNNER_STORAGE_KEY = '$$animationRunner';
2920
+ var PREPARE_CLASSES_KEY = '$$animatePrepareClasses';
2853
2921
 
2854
2922
  function setRunner(element, runner) {
2855
2923
  element.data(RUNNER_STORAGE_KEY, runner);
@@ -2863,22 +2931,23 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
2863
2931
  return element.data(RUNNER_STORAGE_KEY);
2864
2932
  }
2865
2933
 
2866
- this.$get = ['$$jqLite', '$rootScope', '$injector', '$$AnimateRunner', '$$HashMap', '$$rAFScheduler',
2867
- function($$jqLite, $rootScope, $injector, $$AnimateRunner, $$HashMap, $$rAFScheduler) {
2934
+ this.$get = ['$$jqLite', '$rootScope', '$injector', '$$AnimateRunner', '$$Map', '$$rAFScheduler', '$$animateCache',
2935
+ function($$jqLite, $rootScope, $injector, $$AnimateRunner, $$Map, $$rAFScheduler, $$animateCache) {
2868
2936
 
2869
2937
  var animationQueue = [];
2870
2938
  var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
2871
2939
 
2872
2940
  function sortAnimations(animations) {
2873
2941
  var tree = { children: [] };
2874
- var i, lookup = new $$HashMap();
2942
+ var i, lookup = new $$Map();
2875
2943
 
2876
- // this is done first beforehand so that the hashmap
2944
+ // this is done first beforehand so that the map
2877
2945
  // is filled with a list of the elements that will be animated
2878
2946
  for (i = 0; i < animations.length; i++) {
2879
2947
  var animation = animations[i];
2880
- lookup.put(animation.domNode, animations[i] = {
2948
+ lookup.set(animation.domNode, animations[i] = {
2881
2949
  domNode: animation.domNode,
2950
+ element: animation.element,
2882
2951
  fn: animation.fn,
2883
2952
  children: []
2884
2953
  });
@@ -2896,7 +2965,7 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
2896
2965
 
2897
2966
  var elementNode = entry.domNode;
2898
2967
  var parentNode = elementNode.parentNode;
2899
- lookup.put(elementNode, entry);
2968
+ lookup.set(elementNode, entry);
2900
2969
 
2901
2970
  var parentEntry;
2902
2971
  while (parentNode) {
@@ -2935,7 +3004,7 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
2935
3004
  result.push(row);
2936
3005
  row = [];
2937
3006
  }
2938
- row.push(entry.fn);
3007
+ row.push(entry);
2939
3008
  entry.children.forEach(function(childEntry) {
2940
3009
  nextLevelEntries++;
2941
3010
  queue.push(childEntry);
@@ -2970,8 +3039,6 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
2970
3039
  return runner;
2971
3040
  }
2972
3041
 
2973
- setRunner(element, runner);
2974
-
2975
3042
  var classes = mergeClasses(element.attr('class'), mergeClasses(options.addClass, options.removeClass));
2976
3043
  var tempClasses = options.tempClasses;
2977
3044
  if (tempClasses) {
@@ -2979,12 +3046,12 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
2979
3046
  options.tempClasses = null;
2980
3047
  }
2981
3048
 
2982
- var prepareClassName;
2983
3049
  if (isStructural) {
2984
- prepareClassName = 'ng-' + event + PREPARE_CLASS_SUFFIX;
2985
- $$jqLite.addClass(element, prepareClassName);
3050
+ element.data(PREPARE_CLASSES_KEY, 'ng-' + event + PREPARE_CLASS_SUFFIX);
2986
3051
  }
2987
3052
 
3053
+ setRunner(element, runner);
3054
+
2988
3055
  animationQueue.push({
2989
3056
  // this data is used by the postDigest code and passed into
2990
3057
  // the driver step function
@@ -3024,16 +3091,31 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
3024
3091
  var toBeSortedAnimations = [];
3025
3092
 
3026
3093
  forEach(groupedAnimations, function(animationEntry) {
3094
+ var element = animationEntry.from ? animationEntry.from.element : animationEntry.element;
3095
+ var extraClasses = options.addClass;
3096
+
3097
+ extraClasses = (extraClasses ? (extraClasses + ' ') : '') + NG_ANIMATE_CLASSNAME;
3098
+ var cacheKey = $$animateCache.cacheKey(element[0], animationEntry.event, extraClasses, options.removeClass);
3099
+
3027
3100
  toBeSortedAnimations.push({
3028
- domNode: getDomNode(animationEntry.from ? animationEntry.from.element : animationEntry.element),
3101
+ element: element,
3102
+ domNode: getDomNode(element),
3029
3103
  fn: function triggerAnimationStart() {
3104
+ var startAnimationFn, closeFn = animationEntry.close;
3105
+
3106
+ // in the event that we've cached the animation status for this element
3107
+ // and it's in fact an invalid animation (something that has duration = 0)
3108
+ // then we should skip all the heavy work from here on
3109
+ if ($$animateCache.containsCachedAnimationWithoutDuration(cacheKey)) {
3110
+ closeFn();
3111
+ return;
3112
+ }
3113
+
3030
3114
  // it's important that we apply the `ng-animate` CSS class and the
3031
3115
  // temporary classes before we do any driver invoking since these
3032
3116
  // CSS classes may be required for proper CSS detection.
3033
3117
  animationEntry.beforeStart();
3034
3118
 
3035
- var startAnimationFn, closeFn = animationEntry.close;
3036
-
3037
3119
  // in the event that the element was removed before the digest runs or
3038
3120
  // during the RAF sequencing then we should not trigger the animation.
3039
3121
  var targetElement = animationEntry.anchors
@@ -3063,7 +3145,32 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
3063
3145
  // we need to sort each of the animations in order of parent to child
3064
3146
  // relationships. This ensures that the child classes are applied at the
3065
3147
  // right time.
3066
- $$rAFScheduler(sortAnimations(toBeSortedAnimations));
3148
+ var finalAnimations = sortAnimations(toBeSortedAnimations);
3149
+ for (var i = 0; i < finalAnimations.length; i++) {
3150
+ var innerArray = finalAnimations[i];
3151
+ for (var j = 0; j < innerArray.length; j++) {
3152
+ var entry = innerArray[j];
3153
+ var element = entry.element;
3154
+
3155
+ // the RAFScheduler code only uses functions
3156
+ finalAnimations[i][j] = entry.fn;
3157
+
3158
+ // the first row of elements shouldn't have a prepare-class added to them
3159
+ // since the elements are at the top of the animation hierarchy and they
3160
+ // will be applied without a RAF having to pass...
3161
+ if (i === 0) {
3162
+ element.removeData(PREPARE_CLASSES_KEY);
3163
+ continue;
3164
+ }
3165
+
3166
+ var prepareClassName = element.data(PREPARE_CLASSES_KEY);
3167
+ if (prepareClassName) {
3168
+ $$jqLite.addClass(element, prepareClassName);
3169
+ }
3170
+ }
3171
+ }
3172
+
3173
+ $$rAFScheduler(finalAnimations);
3067
3174
  });
3068
3175
 
3069
3176
  return runner;
@@ -3201,10 +3308,10 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
3201
3308
  }
3202
3309
 
3203
3310
  function beforeStart() {
3204
- element.addClass(NG_ANIMATE_CLASSNAME);
3205
- if (tempClasses) {
3206
- $$jqLite.addClass(element, tempClasses);
3207
- }
3311
+ tempClasses = (tempClasses ? (tempClasses + ' ') : '') + NG_ANIMATE_CLASSNAME;
3312
+ $$jqLite.addClass(element, tempClasses);
3313
+
3314
+ var prepareClassName = element.data(PREPARE_CLASSES_KEY);
3208
3315
  if (prepareClassName) {
3209
3316
  $$jqLite.removeClass(element, prepareClassName);
3210
3317
  prepareClassName = null;
@@ -3232,7 +3339,7 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
3232
3339
  }
3233
3340
  }
3234
3341
 
3235
- function close(rejected) { // jshint ignore:line
3342
+ function close(rejected) {
3236
3343
  element.off('$destroy', handleDestroyedElement);
3237
3344
  removeRunner(element);
3238
3345
 
@@ -3244,7 +3351,6 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
3244
3351
  $$jqLite.removeClass(element, tempClasses);
3245
3352
  }
3246
3353
 
3247
- element.removeClass(NG_ANIMATE_CLASSNAME);
3248
3354
  runner.complete(!rejected);
3249
3355
  }
3250
3356
  };
@@ -3338,12 +3444,13 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
3338
3444
  * </file>
3339
3445
  * </example>
3340
3446
  */
3341
- var ngAnimateSwapDirective = ['$animate', '$rootScope', function($animate, $rootScope) {
3447
+ var ngAnimateSwapDirective = ['$animate', function($animate) {
3342
3448
  return {
3343
3449
  restrict: 'A',
3344
3450
  transclude: 'element',
3345
3451
  terminal: true,
3346
- priority: 600, // we use 600 here to ensure that the directive is caught before others
3452
+ priority: 550, // We use 550 here to ensure that the directive is caught before others,
3453
+ // but after `ngIf` (at priority 600).
3347
3454
  link: function(scope, $element, attrs, ctrl, $transclude) {
3348
3455
  var previousElement, previousScope;
3349
3456
  scope.$watchCollection(attrs.ngAnimateSwap || attrs['for'], function(value) {
@@ -3355,10 +3462,10 @@ var ngAnimateSwapDirective = ['$animate', '$rootScope', function($animate, $root
3355
3462
  previousScope = null;
3356
3463
  }
3357
3464
  if (value || value === 0) {
3358
- previousScope = scope.$new();
3359
- $transclude(previousScope, function(element) {
3360
- previousElement = element;
3361
- $animate.enter(element, null, $element);
3465
+ $transclude(function(clone, childScope) {
3466
+ previousElement = clone;
3467
+ previousScope = childScope;
3468
+ $animate.enter(clone, null, $element);
3362
3469
  });
3363
3470
  }
3364
3471
  });
@@ -3372,11 +3479,9 @@ var ngAnimateSwapDirective = ['$animate', '$rootScope', function($animate, $root
3372
3479
  * @description
3373
3480
  *
3374
3481
  * The `ngAnimate` module provides support for CSS-based animations (keyframes and transitions) as well as JavaScript-based animations via
3375
- * callback hooks. Animations are not enabled by default, however, by including `ngAnimate` the animation hooks are enabled for an Angular app.
3482
+ * callback hooks. Animations are not enabled by default, however, by including `ngAnimate` the animation hooks are enabled for an AngularJS app.
3376
3483
  *
3377
- * <div doc-module-components="ngAnimate"></div>
3378
- *
3379
- * # Usage
3484
+ * ## Usage
3380
3485
  * Simply put, there are two ways to make use of animations when ngAnimate is used: by using **CSS** and **JavaScript**. The former works purely based
3381
3486
  * using CSS (by using matching CSS selectors/styles) and the latter triggers animations that are registered via `module.animation()`. For
3382
3487
  * both CSS and JS animations the sole requirement is to have a matching `CSS class` that exists both in the registered animation and within
@@ -3385,25 +3490,33 @@ var ngAnimateSwapDirective = ['$animate', '$rootScope', function($animate, $root
3385
3490
  * ## Directive Support
3386
3491
  * The following directives are "animation aware":
3387
3492
  *
3388
- * | Directive | Supported Animations |
3389
- * |----------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------|
3390
- * | {@link ng.directive:ngRepeat#animations ngRepeat} | enter, leave and move |
3391
- * | {@link ngRoute.directive:ngView#animations ngView} | enter and leave |
3392
- * | {@link ng.directive:ngInclude#animations ngInclude} | enter and leave |
3393
- * | {@link ng.directive:ngSwitch#animations ngSwitch} | enter and leave |
3394
- * | {@link ng.directive:ngIf#animations ngIf} | enter and leave |
3395
- * | {@link ng.directive:ngClass#animations ngClass} | add and remove (the CSS class(es) present) |
3396
- * | {@link ng.directive:ngShow#animations ngShow} & {@link ng.directive:ngHide#animations ngHide} | add and remove (the ng-hide class value) |
3397
- * | {@link ng.directive:form#animation-hooks form} & {@link ng.directive:ngModel#animation-hooks ngModel} | add and remove (dirty, pristine, valid, invalid & all other validations) |
3398
- * | {@link module:ngMessages#animations ngMessages} | add and remove (ng-active & ng-inactive) |
3399
- * | {@link module:ngMessages#animations ngMessage} | enter and leave |
3400
- *
3401
- * (More information can be found by visiting each the documentation associated with each directive.)
3493
+ * | Directive | Supported Animations |
3494
+ * |-------------------------------------------------------------------------------|---------------------------------------------------------------------------|
3495
+ * | {@link ng.directive:form#animations form / ngForm} | add and remove ({@link ng.directive:form#css-classes various classes}) |
3496
+ * | {@link ngAnimate.directive:ngAnimateSwap#animations ngAnimateSwap} | enter and leave |
3497
+ * | {@link ng.directive:ngClass#animations ngClass / {{class&#125;&#8203;&#125;} | add and remove |
3498
+ * | {@link ng.directive:ngClassEven#animations ngClassEven} | add and remove |
3499
+ * | {@link ng.directive:ngClassOdd#animations ngClassOdd} | add and remove |
3500
+ * | {@link ng.directive:ngHide#animations ngHide} | add and remove (the `ng-hide` class) |
3501
+ * | {@link ng.directive:ngIf#animations ngIf} | enter and leave |
3502
+ * | {@link ng.directive:ngInclude#animations ngInclude} | enter and leave |
3503
+ * | {@link module:ngMessages#animations ngMessage / ngMessageExp} | enter and leave |
3504
+ * | {@link module:ngMessages#animations ngMessages} | add and remove (the `ng-active`/`ng-inactive` classes) |
3505
+ * | {@link ng.directive:ngModel#animations ngModel} | add and remove ({@link ng.directive:ngModel#css-classes various classes}) |
3506
+ * | {@link ng.directive:ngRepeat#animations ngRepeat} | enter, leave, and move |
3507
+ * | {@link ng.directive:ngShow#animations ngShow} | add and remove (the `ng-hide` class) |
3508
+ * | {@link ng.directive:ngSwitch#animations ngSwitch} | enter and leave |
3509
+ * | {@link ngRoute.directive:ngView#animations ngView} | enter and leave |
3510
+ *
3511
+ * (More information can be found by visiting the documentation associated with each directive.)
3512
+ *
3513
+ * For a full breakdown of the steps involved during each animation event, refer to the
3514
+ * {@link ng.$animate `$animate` API docs}.
3402
3515
  *
3403
3516
  * ## CSS-based Animations
3404
3517
  *
3405
3518
  * CSS-based animations with ngAnimate are unique since they require no JavaScript code at all. By using a CSS class that we reference between our HTML
3406
- * and CSS code we can create an animation that will be picked up by Angular when an the underlying directive performs an operation.
3519
+ * and CSS code we can create an animation that will be picked up by AngularJS when an underlying directive performs an operation.
3407
3520
  *
3408
3521
  * The example below shows how an `enter` animation can be made possible on an element using `ng-if`:
3409
3522
  *
@@ -3543,6 +3656,10 @@ var ngAnimateSwapDirective = ['$animate', '$rootScope', function($animate, $root
3543
3656
  * /&#42; As of 1.4.4, this must always be set: it signals ngAnimate
3544
3657
  * to not accidentally inherit a delay property from another CSS class &#42;/
3545
3658
  * transition-duration: 0s;
3659
+ *
3660
+ * /&#42; if you are using animations instead of transitions you should configure as follows:
3661
+ * animation-delay: 0.1s;
3662
+ * animation-duration: 0s; &#42;/
3546
3663
  * }
3547
3664
  * .my-animation.ng-enter.ng-enter-active {
3548
3665
  * /&#42; standard transition styles &#42;/
@@ -3631,9 +3748,22 @@ var ngAnimateSwapDirective = ['$animate', '$rootScope', function($animate, $root
3631
3748
  * .message.ng-enter-prepare {
3632
3749
  * opacity: 0;
3633
3750
  * }
3634
- *
3635
3751
  * ```
3636
3752
  *
3753
+ * ### Animating between value changes
3754
+ *
3755
+ * Sometimes you need to animate between different expression states, whose values
3756
+ * don't necessary need to be known or referenced in CSS styles.
3757
+ * Unless possible with another {@link ngAnimate#directive-support "animation aware" directive},
3758
+ * that specific use case can always be covered with {@link ngAnimate.directive:ngAnimateSwap} as
3759
+ * can be seen in {@link ngAnimate.directive:ngAnimateSwap#examples this example}.
3760
+ *
3761
+ * Note that {@link ngAnimate.directive:ngAnimateSwap} is a *structural directive*, which means it
3762
+ * creates a new instance of the element (including any other/child directives it may have) and
3763
+ * links it to a new scope every time *swap* happens. In some cases this might not be desirable
3764
+ * (e.g. for performance reasons, or when you wish to retain internal state on the original
3765
+ * element instance).
3766
+ *
3637
3767
  * ## JavaScript-based Animations
3638
3768
  *
3639
3769
  * ngAnimate also allows for animations to be consumed by JavaScript code. The approach is similar to CSS-based animations (where there is a shared
@@ -3658,7 +3788,7 @@ var ngAnimateSwapDirective = ['$animate', '$rootScope', function($animate, $root
3658
3788
  * enter: function(element, doneFn) {
3659
3789
  * jQuery(element).fadeIn(1000, doneFn);
3660
3790
  *
3661
- * // remember to call doneFn so that angular
3791
+ * // remember to call doneFn so that AngularJS
3662
3792
  * // knows that the animation has concluded
3663
3793
  * },
3664
3794
  *
@@ -3706,7 +3836,7 @@ var ngAnimateSwapDirective = ['$animate', '$rootScope', function($animate, $root
3706
3836
  *
3707
3837
  * ## CSS + JS Animations Together
3708
3838
  *
3709
- * AngularJS 1.4 and higher has taken steps to make the amalgamation of CSS and JS animations more flexible. However, unlike earlier versions of Angular,
3839
+ * AngularJS 1.4 and higher has taken steps to make the amalgamation of CSS and JS animations more flexible. However, unlike earlier versions of AngularJS,
3710
3840
  * defining CSS and JS animations to work off of the same CSS class will not work anymore. Therefore the example below will only result in **JS animations taking
3711
3841
  * charge of the animation**:
3712
3842
  *
@@ -3898,7 +4028,7 @@ var ngAnimateSwapDirective = ['$animate', '$rootScope', function($animate, $root
3898
4028
  deps="angular-animate.js;angular-route.js"
3899
4029
  animations="true">
3900
4030
  <file name="index.html">
3901
- <a href="#/">Home</a>
4031
+ <a href="#!/">Home</a>
3902
4032
  <hr />
3903
4033
  <div class="view-container">
3904
4034
  <div ng-view class="view"></div>
@@ -3918,22 +4048,23 @@ var ngAnimateSwapDirective = ['$animate', '$rootScope', function($animate, $root
3918
4048
  }])
3919
4049
  .run(['$rootScope', function($rootScope) {
3920
4050
  $rootScope.records = [
3921
- { id:1, title: "Miss Beulah Roob" },
3922
- { id:2, title: "Trent Morissette" },
3923
- { id:3, title: "Miss Ava Pouros" },
3924
- { id:4, title: "Rod Pouros" },
3925
- { id:5, title: "Abdul Rice" },
3926
- { id:6, title: "Laurie Rutherford Sr." },
3927
- { id:7, title: "Nakia McLaughlin" },
3928
- { id:8, title: "Jordon Blanda DVM" },
3929
- { id:9, title: "Rhoda Hand" },
3930
- { id:10, title: "Alexandrea Sauer" }
4051
+ { id: 1, title: 'Miss Beulah Roob' },
4052
+ { id: 2, title: 'Trent Morissette' },
4053
+ { id: 3, title: 'Miss Ava Pouros' },
4054
+ { id: 4, title: 'Rod Pouros' },
4055
+ { id: 5, title: 'Abdul Rice' },
4056
+ { id: 6, title: 'Laurie Rutherford Sr.' },
4057
+ { id: 7, title: 'Nakia McLaughlin' },
4058
+ { id: 8, title: 'Jordon Blanda DVM' },
4059
+ { id: 9, title: 'Rhoda Hand' },
4060
+ { id: 10, title: 'Alexandrea Sauer' }
3931
4061
  ];
3932
4062
  }])
3933
4063
  .controller('HomeController', [function() {
3934
4064
  //empty
3935
4065
  }])
3936
- .controller('ProfileController', ['$rootScope', '$routeParams', function($rootScope, $routeParams) {
4066
+ .controller('ProfileController', ['$rootScope', '$routeParams',
4067
+ function ProfileController($rootScope, $routeParams) {
3937
4068
  var index = parseInt($routeParams.id, 10);
3938
4069
  var record = $rootScope.records[index - 1];
3939
4070
 
@@ -3945,7 +4076,7 @@ var ngAnimateSwapDirective = ['$animate', '$rootScope', function($animate, $root
3945
4076
  <h2>Welcome to the home page</h1>
3946
4077
  <p>Please click on an element</p>
3947
4078
  <a class="record"
3948
- ng-href="#/profile/{{ record.id }}"
4079
+ ng-href="#!/profile/{{ record.id }}"
3949
4080
  ng-animate-ref="{{ record.id }}"
3950
4081
  ng-repeat="record in records">
3951
4082
  {{ record.title }}
@@ -4021,7 +4152,7 @@ var ngAnimateSwapDirective = ['$animate', '$rootScope', function($animate, $root
4021
4152
  *
4022
4153
  * ## Using $animate in your directive code
4023
4154
  *
4024
- * So far we've explored how to feed in animations into an Angular application, but how do we trigger animations within our own directives in our application?
4155
+ * So far we've explored how to feed in animations into an AngularJS application, but how do we trigger animations within our own directives in our application?
4025
4156
  * By injecting the `$animate` service into our directive code, we can trigger structural and class-based hooks which can then be consumed by animations. Let's
4026
4157
  * imagine we have a greeting box that shows and hides itself when the data changes
4027
4158
  *
@@ -4064,7 +4195,7 @@ var ngAnimateSwapDirective = ['$animate', '$rootScope', function($animate, $root
4064
4195
  * });
4065
4196
  * ```
4066
4197
  *
4067
- * (Note that earlier versions of Angular prior to v1.4 required the promise code to be wrapped using `$scope.$apply(...)`. This is not the case
4198
+ * (Note that earlier versions of AngularJS prior to v1.4 required the promise code to be wrapped using `$scope.$apply(...)`. This is not the case
4068
4199
  * anymore.)
4069
4200
  *
4070
4201
  * In addition to the animation promise, we can also make use of animation-related callbacks within our directives and controller code by registering
@@ -4079,7 +4210,7 @@ var ngAnimateSwapDirective = ['$animate', '$rootScope', function($animate, $root
4079
4210
  * }])
4080
4211
  * ```
4081
4212
  *
4082
- * (Note that you will need to trigger a digest within the callback to get angular to notice any scope-related changes.)
4213
+ * (Note that you will need to trigger a digest within the callback to get AngularJS to notice any scope-related changes.)
4083
4214
  */
4084
4215
 
4085
4216
  var copy;
@@ -4106,7 +4237,7 @@ var noop;
4106
4237
  * Click here {@link ng.$animate to learn more about animations with `$animate`}.
4107
4238
  */
4108
4239
  angular.module('ngAnimate', [], function initAngularHelpers() {
4109
- // Access helpers from angular core.
4240
+ // Access helpers from AngularJS core.
4110
4241
  // Do it inside a `config` block to ensure `window.angular` is available.
4111
4242
  noop = angular.noop;
4112
4243
  copy = angular.copy;
@@ -4121,12 +4252,14 @@ angular.module('ngAnimate', [], function initAngularHelpers() {
4121
4252
  isFunction = angular.isFunction;
4122
4253
  isElement = angular.isElement;
4123
4254
  })
4255
+ .info({ angularVersion: '1.8.0' })
4124
4256
  .directive('ngAnimateSwap', ngAnimateSwapDirective)
4125
4257
 
4126
4258
  .directive('ngAnimateChildren', $$AnimateChildrenDirective)
4127
4259
  .factory('$$rAFScheduler', $$rAFSchedulerFactory)
4128
4260
 
4129
4261
  .provider('$$animateQueue', $$AnimateQueueProvider)
4262
+ .provider('$$animateCache', $$AnimateCacheProvider)
4130
4263
  .provider('$$animation', $$AnimationProvider)
4131
4264
 
4132
4265
  .provider('$animateCss', $AnimateCssProvider)