@angular-wave/angular.ts 0.0.51 → 0.0.53

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/dist/angular-ts.esm.js +2 -2
  2. package/dist/angular-ts.umd.js +2 -2
  3. package/package.json +1 -1
  4. package/src/animations/animate-children-directive.js +19 -99
  5. package/src/animations/animate-children-directive.md +80 -0
  6. package/src/animations/animate-css-driver.js +250 -256
  7. package/src/animations/animate-css.js +646 -875
  8. package/src/animations/animate-css.md +263 -0
  9. package/src/animations/animate-js-driver.js +54 -56
  10. package/src/animations/animate-js.js +303 -306
  11. package/src/animations/animate-queue.js +707 -716
  12. package/src/animations/animate-swap.js +30 -119
  13. package/src/animations/animate-swap.md +88 -0
  14. package/src/animations/animation.js +3 -3
  15. package/src/core/animate/animate-css.js +21 -6
  16. package/src/core/animate/animate-runner.js +147 -145
  17. package/src/core/animate/animate.js +572 -585
  18. package/src/core/animate/animate.spec.js +194 -286
  19. package/src/core/animate/anomate.md +13 -0
  20. package/src/core/animate/helpers.js +10 -0
  21. package/src/core/compile/compile.spec.js +5 -6
  22. package/src/core/core.html +0 -1
  23. package/src/directive/select/select.js +301 -305
  24. package/src/public.js +0 -1
  25. package/src/router/directives/state-directives.js +256 -574
  26. package/src/router/directives/state-directives.md +435 -0
  27. package/src/router/directives/view-directive.js +3 -3
  28. package/src/router/index.js +7 -7
  29. package/types/animations/animate-children-directive.d.ts +5 -80
  30. package/types/animations/animate-css-driver.d.ts +11 -0
  31. package/types/animations/animate-css.d.ts +8 -0
  32. package/types/animations/animate-js-driver.d.ts +8 -0
  33. package/types/animations/animate-js.d.ts +12 -0
  34. package/types/animations/animate-queue.d.ts +19 -0
  35. package/types/animations/animate-swap.d.ts +5 -89
  36. package/types/core/animate/animate-css.d.ts +1 -1
  37. package/types/core/animate/animate-runner.d.ts +32 -0
  38. package/types/core/animate/animate.d.ts +509 -0
  39. package/types/core/animate/helpers.d.ts +8 -0
  40. package/types/directive/select/select.d.ts +79 -0
  41. package/types/router/directives/state-directives.d.ts +31 -0
  42. package/src/core/document.spec.js +0 -52
@@ -32,220 +32,6 @@ import {
32
32
 
33
33
  const ANIMATE_TIMER_KEY = "$$animateCss";
34
34
 
35
- /**
36
- * @ngdoc service
37
- * @name $animateCss
38
- * @kind object
39
- *
40
- * @description
41
- * The `$animateCss` service is a useful utility to trigger customized CSS-based transitions/keyframes
42
- * from a JavaScript-based animation or directly from a directive. The purpose of `$animateCss` is NOT
43
- * to side-step how `$animate` and ngAnimate work, but the goal is to allow pre-existing animations or
44
- * directives to create more complex animations that can be purely driven using CSS code.
45
- *
46
- * Note that only browsers that support CSS transitions and/or keyframe animations are capable of
47
- * rendering animations triggered via `$animateCss` (bad news for IE9 and lower).
48
- *
49
- * ## General Use
50
- * Once again, `$animateCss` is designed to be used inside of a registered JavaScript animation that
51
- * is powered by ngAnimate. It is possible to use `$animateCss` directly inside of a directive, however,
52
- * any automatic control over cancelling animations and/or preventing animations from being run on
53
- * child elements will not be handled by AngularJS. For this to work as expected, please use `$animate` to
54
- * trigger the animation and then setup a JavaScript animation that injects `$animateCss` to trigger
55
- * the CSS animation.
56
- *
57
- * The example below shows how we can create a folding animation on an element using `ng-if`:
58
- *
59
- * ```html
60
- * <!-- notice the `fold-animation` CSS class -->
61
- * <div ng-if="onOff" class="fold-animation">
62
- * This element will go BOOM
63
- * </div>
64
- * <button ng-click="onOff=true">Fold In</button>
65
- * ```
66
- *
67
- * Now we create the **JavaScript animation** that will trigger the CSS transition:
68
- *
69
- * ```js
70
- * ngModule.animation('.fold-animation', ['$animateCss', function($animateCss) {
71
- * return {
72
- * enter: function(element, doneFn) {
73
- * let height = element[0].offsetHeight;
74
- * return $animateCss(element, {
75
- * from: { height:'0px' },
76
- * to: { height:height + 'px' },
77
- * duration: 1 // one second
78
- * });
79
- * }
80
- * }
81
- * }]);
82
- * ```
83
- *
84
- * ## More Advanced Uses
85
- *
86
- * `$animateCss` is the underlying code that ngAnimate uses to power **CSS-based animations** behind the scenes. Therefore CSS hooks
87
- * like `.ng-EVENT`, `.ng-EVENT-active`, `.ng-EVENT-stagger` are all features that can be triggered using `$animateCss` via JavaScript code.
88
- *
89
- * This also means that just about any combination of adding classes, removing classes, setting styles, dynamically setting a keyframe animation,
90
- * applying a hardcoded duration or delay value, changing the animation easing or applying a stagger animation are all options that work with
91
- * `$animateCss`. The service itself is smart enough to figure out the combination of options and examine the element styling properties in order
92
- * to provide a working animation that will run in CSS.
93
- *
94
- * The example below showcases a more advanced version of the `.fold-animation` from the example above:
95
- *
96
- * ```js
97
- * ngModule.animation('.fold-animation', ['$animateCss', function($animateCss) {
98
- * return {
99
- * enter: function(element, doneFn) {
100
- * let height = element[0].offsetHeight;
101
- * return $animateCss(element, {
102
- * addClass: 'red large-text pulse-twice',
103
- * easing: 'ease-out',
104
- * from: { height:'0px' },
105
- * to: { height:height + 'px' },
106
- * duration: 1 // one second
107
- * });
108
- * }
109
- * }
110
- * }]);
111
- * ```
112
- *
113
- * Since we're adding/removing CSS classes then the CSS transition will also pick those up:
114
- *
115
- * ```css
116
- * /&#42; since a hardcoded duration value of 1 was provided in the JavaScript animation code,
117
- * the CSS classes below will be transitioned despite them being defined as regular CSS classes &#42;/
118
- * .red { background:red; }
119
- * .large-text { font-size:20px; }
120
- *
121
- * /&#42; we can also use a keyframe animation and $animateCss will make it work alongside the transition &#42;/
122
- * .pulse-twice {
123
- * animation: 0.5s pulse linear 2;
124
- * -webkit-animation: 0.5s pulse linear 2;
125
- * }
126
- *
127
- * @keyframes pulse {
128
- * from { transform: scale(0.5); }
129
- * to { transform: scale(1.5); }
130
- * }
131
- *
132
- * @-webkit-keyframes pulse {
133
- * from { -webkit-transform: scale(0.5); }
134
- * to { -webkit-transform: scale(1.5); }
135
- * }
136
- * ```
137
- *
138
- * Given this complex combination of CSS classes, styles and options, `$animateCss` will figure everything out and make the animation happen.
139
- *
140
- * ## How the Options are handled
141
- *
142
- * `$animateCss` is very versatile and intelligent when it comes to figuring out what configurations to apply to the element to ensure the animation
143
- * works with the options provided. Say for example we were adding a class that contained a keyframe value and we wanted to also animate some inline
144
- * styles using the `from` and `to` properties.
145
- *
146
- * ```js
147
- * let animator = $animateCss(element, {
148
- * from: { background:'red' },
149
- * to: { background:'blue' }
150
- * });
151
- * animator.start();
152
- * ```
153
- *
154
- * ```css
155
- * .rotating-animation {
156
- * animation:0.5s rotate linear;
157
- * -webkit-animation:0.5s rotate linear;
158
- * }
159
- *
160
- * @keyframes rotate {
161
- * from { transform: rotate(0deg); }
162
- * to { transform: rotate(360deg); }
163
- * }
164
- *
165
- * @-webkit-keyframes rotate {
166
- * from { -webkit-transform: rotate(0deg); }
167
- * to { -webkit-transform: rotate(360deg); }
168
- * }
169
- * ```
170
- *
171
- * The missing pieces here are that we do not have a transition set (within the CSS code nor within the `$animateCss` options) and the duration of the animation is
172
- * going to be detected from what the keyframe styles on the CSS class are. In this event, `$animateCss` will automatically create an inline transition
173
- * style matching the duration detected from the keyframe style (which is present in the CSS class that is being added) and then prepare both the transition
174
- * and keyframe animations to run in parallel on the element. Then when the animation is underway the provided `from` and `to` CSS styles will be applied
175
- * and spread across the transition and keyframe animation.
176
- *
177
- * ## What is returned
178
- *
179
- * `$animateCss` works in two stages: a preparation phase and an animation phase. Therefore when `$animateCss` is first called it will NOT actually
180
- * start the animation. All that is going on here is that the element is being prepared for the animation (which means that the generated CSS classes are
181
- * added and removed on the element). Once `$animateCss` is called it will return an object with the following properties:
182
- *
183
- * ```js
184
- * let animator = $animateCss(element, { ... });
185
- * ```
186
- *
187
- * Now what do the contents of our `animator` variable look like:
188
- *
189
- * ```js
190
- * {
191
- * // starts the animation
192
- * start: Function,
193
- *
194
- * // ends (aborts) the animation
195
- * end: Function
196
- * }
197
- * ```
198
- *
199
- * To actually start the animation we need to run `animation.start()` which will then return a promise that we can hook into to detect when the animation ends.
200
- * If we choose not to run the animation then we MUST run `animation.end()` to perform a cleanup on the element (since some CSS classes and styles may have been
201
- * applied to the element during the preparation phase). Note that all other properties such as duration, delay, transitions and keyframes are just properties
202
- * and that changing them will not reconfigure the parameters of the animation.
203
- *
204
- * ### runner.done() vs runner.then()
205
- * It is documented that `animation.start()` will return a promise object and this is true, however, there is also an additional method available on the
206
- * runner called `.done(callbackFn)`. The done method works the same as `.finally(callbackFn)`, however, it does **not trigger a digest to occur**.
207
- * Therefore, for performance reasons, it's always best to use `runner.done(callback)` instead of `runner.then()`, `runner.catch()` or `runner.finally()`
208
- * unless you really need a digest to kick off afterwards.
209
- *
210
- * Keep in mind that, to make this easier, ngAnimate has tweaked the JS animations API to recognize when a runner instance is returned from $animateCss
211
- * (so there is no need to call `runner.done(doneFn)` inside of your JavaScript animation code).
212
- * Check the {@link ngAnimate.$animateCss#usage animation code above} to see how this works.
213
- *
214
- * @param {Element} element the element that will be animated
215
- * @param {object} options the animation-related options that will be applied during the animation
216
- *
217
- * * `event` - The DOM event (e.g. enter, leave, move). When used, a generated CSS class of `ng-EVENT` and `ng-EVENT-active` will be applied
218
- * to the element during the animation. Multiple events can be provided when spaces are used as a separator. (Note that this will not perform any DOM operation.)
219
- * * `structural` - Indicates that the `ng-` prefix will be added to the event class. Setting to `false` or omitting will turn `ng-EVENT` and
220
- * `ng-EVENT-active` in `EVENT` and `EVENT-active`. Unused if `event` is omitted.
221
- * * `easing` - The CSS easing value that will be applied to the transition or keyframe animation (or both).
222
- * * `transitionStyle` - The raw CSS transition style that will be used (e.g. `1s linear all`).
223
- * * `keyframeStyle` - The raw CSS keyframe animation style that will be used (e.g. `1s my_animation linear`).
224
- * * `from` - The starting CSS styles (a key/value object) that will be applied at the start of the animation.
225
- * * `to` - The ending CSS styles (a key/value object) that will be applied across the animation via a CSS transition.
226
- * * `addClass` - A space separated list of CSS classes that will be added to the element and spread across the animation.
227
- * * `removeClass` - A space separated list of CSS classes that will be removed from the element and spread across the animation.
228
- * * `duration` - A number value representing the total duration of the transition and/or keyframe (note that a value of 1 is 1000ms). If a value of `0`
229
- * is provided then the animation will be skipped entirely.
230
- * * `delay` - A number value representing the total delay of the transition and/or keyframe (note that a value of 1 is 1000ms). If a value of `true` is
231
- * used then whatever delay value is detected from the CSS classes will be mirrored on the elements styles (e.g. by setting delay true then the style value
232
- * of the element will be `transition-delay: DETECTED_VALUE`). Using `true` is useful when you want the CSS classes and inline styles to all share the same
233
- * CSS delay value.
234
- * * `stagger` - A numeric time value representing the delay between successively animated elements
235
- * ({@link ngAnimate#css-staggering-animations Click here to learn how CSS-based staggering works in ngAnimate.})
236
- * * `staggerIndex` - The numeric index representing the stagger item (e.g. a value of 5 is equal to the sixth item in the stagger; therefore when a
237
- * `stagger` option value of `0.1` is used then there will be a stagger delay of `600ms`)
238
- * * `applyClassesEarly` - Whether or not the classes being added or removed will be used when detecting the animation. This is set by `$animate` when enter/leave/move animations are fired to ensure that the CSS classes are resolved in time. (Note that this will prevent any transitions from occurring on the classes being added and removed.)
239
- * * `cleanupStyles` - Whether or not the provided `from` and `to` styles will be removed once
240
- * the animation is closed. This is useful for when the styles are used purely for the sake of
241
- * the animation and do not have a lasting visual effect on the element (e.g. a collapse and open animation).
242
- * By default this value is set to `false`.
243
- *
244
- * @return {object} an object with start and end methods and details about the animation.
245
- *
246
- * * `start` - The method to start the animation. This will return a `Promise` when called.
247
- * * `end` - This method will cancel the animation and remove all applied CSS classes and styles.
248
- */
249
35
  const ONE_SECOND = 1000;
250
36
 
251
37
  const ELAPSED_TIME_MAX_DECIMAL_PLACES = 3;
@@ -351,791 +137,776 @@ function registerRestorableStyles(backup, node, properties) {
351
137
  });
352
138
  }
353
139
 
354
- export const $AnimateCssProvider = [
355
- function () {
356
- this.$get = [
357
- "$$AnimateRunner",
358
- "$timeout",
359
- "$$animateCache",
360
- "$$rAFScheduler",
361
- "$$animateQueue",
362
- function (
363
- $$AnimateRunner,
364
- $timeout,
365
- $$animateCache,
366
- $$rAFScheduler,
367
- $$animateQueue,
140
+ export function $AnimateCssProvider() {
141
+ this.$get = [
142
+ "$$AnimateRunner",
143
+ "$timeout",
144
+ "$$animateCache",
145
+ "$$rAFScheduler",
146
+ "$$animateQueue",
147
+ function (
148
+ $$AnimateRunner,
149
+ $timeout,
150
+ $$animateCache,
151
+ $$rAFScheduler,
152
+ $$animateQueue,
153
+ ) {
154
+ const applyAnimationClasses = applyAnimationClassesFactory();
155
+
156
+ function computeCachedCssStyles(
157
+ node,
158
+ className,
159
+ cacheKey,
160
+ allowNoDuration,
161
+ properties,
368
162
  ) {
369
- const applyAnimationClasses = applyAnimationClassesFactory();
163
+ let timings = $$animateCache.get(cacheKey);
370
164
 
371
- function computeCachedCssStyles(
372
- node,
373
- className,
374
- cacheKey,
375
- allowNoDuration,
376
- properties,
377
- ) {
378
- let timings = $$animateCache.get(cacheKey);
379
-
380
- if (!timings) {
381
- timings = computeCssStyles(node, properties);
382
- if (timings.animationIterationCount === "infinite") {
383
- timings.animationIterationCount = 1;
384
- }
165
+ if (!timings) {
166
+ timings = computeCssStyles(node, properties);
167
+ if (timings.animationIterationCount === "infinite") {
168
+ timings.animationIterationCount = 1;
385
169
  }
170
+ }
386
171
 
387
- // if a css animation has no duration we
388
- // should mark that so that repeated addClass/removeClass calls are skipped
389
- const hasDuration =
390
- allowNoDuration ||
391
- timings.transitionDuration > 0 ||
392
- timings.animationDuration > 0;
172
+ // if a css animation has no duration we
173
+ // should mark that so that repeated addClass/removeClass calls are skipped
174
+ const hasDuration =
175
+ allowNoDuration ||
176
+ timings.transitionDuration > 0 ||
177
+ timings.animationDuration > 0;
393
178
 
394
- // we keep putting this in multiple times even though the value and the cacheKey are the same
395
- // because we're keeping an internal tally of how many duplicate animations are detected.
396
- $$animateCache.put(cacheKey, timings, hasDuration);
179
+ // we keep putting this in multiple times even though the value and the cacheKey are the same
180
+ // because we're keeping an internal tally of how many duplicate animations are detected.
181
+ $$animateCache.put(cacheKey, timings, hasDuration);
182
+
183
+ return timings;
184
+ }
397
185
 
398
- return timings;
186
+ function computeCachedCssStaggerStyles(
187
+ node,
188
+ className,
189
+ cacheKey,
190
+ properties,
191
+ ) {
192
+ let stagger;
193
+ const staggerCacheKey = `stagger-${cacheKey}`;
194
+
195
+ // if we have one or more existing matches of matching elements
196
+ // containing the same parent + CSS styles (which is how cacheKey works)
197
+ // then staggering is possible
198
+ if ($$animateCache.count(cacheKey) > 0) {
199
+ stagger = $$animateCache.get(staggerCacheKey);
200
+
201
+ if (!stagger) {
202
+ const staggerClassName = pendClasses(className, "-stagger");
203
+
204
+ node.className += ` ${staggerClassName}`;
205
+ stagger = computeCssStyles(node, properties);
206
+
207
+ // force the conversion of a null value to zero incase not set
208
+ stagger.animationDuration = Math.max(stagger.animationDuration, 0);
209
+ stagger.transitionDuration = Math.max(
210
+ stagger.transitionDuration,
211
+ 0,
212
+ );
213
+
214
+ node.classList.remove(staggerClassName);
215
+
216
+ $$animateCache.put(staggerCacheKey, stagger, true);
217
+ }
399
218
  }
400
219
 
401
- function computeCachedCssStaggerStyles(
220
+ return stagger || {};
221
+ }
222
+
223
+ const rafWaitQueue = [];
224
+ function waitUntilQuiet(callback) {
225
+ rafWaitQueue.push(callback);
226
+ $$rAFScheduler.waitUntilQuiet(() => {
227
+ $$animateCache.flush();
228
+
229
+ // DO NOT REMOVE THIS LINE OR REFACTOR OUT THE `pageWidth` variable.
230
+ // the line below will force the browser to perform a repaint so
231
+ // that all the animated elements within the animation frame will
232
+ // be properly updated and drawn on screen. This is required to
233
+ // ensure that the preparation animation is properly flushed so that
234
+ // the active state picks up from there. DO NOT REMOVE THIS LINE.
235
+ // DO NOT OPTIMIZE THIS LINE. THE MINIFIER WILL REMOVE IT OTHERWISE WHICH
236
+ // WILL RESULT IN AN UNPREDICTABLE BUG THAT IS VERY HARD TO TRACK DOWN AND
237
+ // WILL TAKE YEARS AWAY FROM YOUR LIFE.
238
+
239
+ const pageWidth = document.body.offsetWidth + 1;
240
+
241
+ // we use a for loop to ensure that if the queue is changed
242
+ // during this looping then it will consider new requests
243
+ for (let i = 0; i < rafWaitQueue.length; i++) {
244
+ rafWaitQueue[i](pageWidth);
245
+ }
246
+ rafWaitQueue.length = 0;
247
+ });
248
+ }
249
+
250
+ function computeTimings(node, className, cacheKey, allowNoDuration) {
251
+ const timings = computeCachedCssStyles(
402
252
  node,
403
253
  className,
404
254
  cacheKey,
405
- properties,
406
- ) {
407
- let stagger;
408
- const staggerCacheKey = `stagger-${cacheKey}`;
409
-
410
- // if we have one or more existing matches of matching elements
411
- // containing the same parent + CSS styles (which is how cacheKey works)
412
- // then staggering is possible
413
- if ($$animateCache.count(cacheKey) > 0) {
414
- stagger = $$animateCache.get(staggerCacheKey);
415
-
416
- if (!stagger) {
417
- const staggerClassName = pendClasses(className, "-stagger");
418
-
419
- node.className += ` ${staggerClassName}`;
420
- stagger = computeCssStyles(node, properties);
421
-
422
- // force the conversion of a null value to zero incase not set
423
- stagger.animationDuration = Math.max(
424
- stagger.animationDuration,
425
- 0,
426
- );
427
- stagger.transitionDuration = Math.max(
428
- stagger.transitionDuration,
429
- 0,
430
- );
255
+ allowNoDuration,
256
+ DETECT_CSS_PROPERTIES,
257
+ );
258
+ const aD = timings.animationDelay;
259
+ const tD = timings.transitionDelay;
260
+ timings.maxDelay = aD && tD ? Math.max(aD, tD) : aD || tD;
261
+ timings.maxDuration = Math.max(
262
+ timings.animationDuration * timings.animationIterationCount,
263
+ timings.transitionDuration,
264
+ );
265
+
266
+ return timings;
267
+ }
431
268
 
432
- node.classList.remove(staggerClassName);
269
+ return function init(element, initialOptions) {
270
+ // all of the animation functions should create
271
+ // a copy of the options data, however, if a
272
+ // parent service has already created a copy then
273
+ // we should stick to using that
274
+ let options = initialOptions || {};
275
+ if (!options.$$prepared) {
276
+ options = prepareAnimationOptions(structuredClone(options));
277
+ }
433
278
 
434
- $$animateCache.put(staggerCacheKey, stagger, true);
435
- }
279
+ const restoreStyles = {};
280
+ const node = getDomNode(element);
281
+ if (!node || !node.parentNode || !$$animateQueue.enabled()) {
282
+ return closeAndReturnNoopAnimator();
283
+ }
284
+
285
+ const temporaryStyles = [];
286
+ const classes = element.attr("class");
287
+ const styles = packageStyles(options);
288
+ let animationClosed;
289
+ let animationPaused;
290
+ let animationCompleted;
291
+ let runner;
292
+ let runnerHost;
293
+ let maxDelay;
294
+ let maxDelayTime;
295
+ let maxDuration;
296
+ let maxDurationTime;
297
+ let startTime;
298
+ const events = [];
299
+
300
+ if (options.duration === 0) {
301
+ return closeAndReturnNoopAnimator();
302
+ }
303
+
304
+ const method =
305
+ options.event && Array.isArray(options.event)
306
+ ? options.event.join(" ")
307
+ : options.event;
308
+
309
+ const isStructural = method && options.structural;
310
+ let structuralClassName = "";
311
+ let addRemoveClassName = "";
312
+
313
+ if (isStructural) {
314
+ structuralClassName = pendClasses(method, EVENT_CLASS_PREFIX, true);
315
+ } else if (method) {
316
+ structuralClassName = method;
317
+ }
318
+
319
+ if (options.addClass) {
320
+ addRemoveClassName += pendClasses(options.addClass, ADD_CLASS_SUFFIX);
321
+ }
322
+
323
+ if (options.removeClass) {
324
+ if (addRemoveClassName.length) {
325
+ addRemoveClassName += " ";
436
326
  }
327
+ addRemoveClassName += pendClasses(
328
+ options.removeClass,
329
+ REMOVE_CLASS_SUFFIX,
330
+ );
331
+ }
437
332
 
438
- return stagger || {};
333
+ // there may be a situation where a structural animation is combined together
334
+ // with CSS classes that need to resolve before the animation is computed.
335
+ // However this means that there is no explicit CSS code to block the animation
336
+ // from happening (by setting 0s none in the class name). If this is the case
337
+ // we need to apply the classes before the first rAF so we know to continue if
338
+ // there actually is a detected transition or keyframe animation
339
+ if (options.applyClassesEarly && addRemoveClassName.length) {
340
+ applyAnimationClasses(element, options);
439
341
  }
440
342
 
441
- const rafWaitQueue = [];
442
- function waitUntilQuiet(callback) {
443
- rafWaitQueue.push(callback);
444
- $$rAFScheduler.waitUntilQuiet(() => {
445
- $$animateCache.flush();
446
-
447
- // DO NOT REMOVE THIS LINE OR REFACTOR OUT THE `pageWidth` variable.
448
- // the line below will force the browser to perform a repaint so
449
- // that all the animated elements within the animation frame will
450
- // be properly updated and drawn on screen. This is required to
451
- // ensure that the preparation animation is properly flushed so that
452
- // the active state picks up from there. DO NOT REMOVE THIS LINE.
453
- // DO NOT OPTIMIZE THIS LINE. THE MINIFIER WILL REMOVE IT OTHERWISE WHICH
454
- // WILL RESULT IN AN UNPREDICTABLE BUG THAT IS VERY HARD TO TRACK DOWN AND
455
- // WILL TAKE YEARS AWAY FROM YOUR LIFE.
456
-
457
- const pageWidth = document.body.offsetWidth + 1;
458
-
459
- // we use a for loop to ensure that if the queue is changed
460
- // during this looping then it will consider new requests
461
- for (let i = 0; i < rafWaitQueue.length; i++) {
462
- rafWaitQueue[i](pageWidth);
463
- }
464
- rafWaitQueue.length = 0;
465
- });
343
+ let preparationClasses = [structuralClassName, addRemoveClassName]
344
+ .join(" ")
345
+ .trim();
346
+ let fullClassName = `${classes} ${preparationClasses}`;
347
+ const hasToStyles = styles.to && Object.keys(styles.to).length > 0;
348
+ const containsKeyframeAnimation =
349
+ (options.keyframeStyle || "").length > 0;
350
+
351
+ // there is no way we can trigger an animation if no styles and
352
+ // no classes are being applied which would then trigger a transition,
353
+ // unless there a is raw keyframe value that is applied to the element.
354
+ if (!containsKeyframeAnimation && !hasToStyles && !preparationClasses) {
355
+ return closeAndReturnNoopAnimator();
466
356
  }
467
357
 
468
- function computeTimings(node, className, cacheKey, allowNoDuration) {
469
- const timings = computeCachedCssStyles(
358
+ let stagger;
359
+ let cacheKey = $$animateCache.cacheKey(
360
+ node,
361
+ method,
362
+ options.addClass,
363
+ options.removeClass,
364
+ );
365
+ if ($$animateCache.containsCachedAnimationWithoutDuration(cacheKey)) {
366
+ preparationClasses = null;
367
+ return closeAndReturnNoopAnimator();
368
+ }
369
+
370
+ if (options.stagger > 0) {
371
+ const staggerVal = parseFloat(options.stagger);
372
+ stagger = {
373
+ transitionDelay: staggerVal,
374
+ animationDelay: staggerVal,
375
+ transitionDuration: 0,
376
+ animationDuration: 0,
377
+ };
378
+ } else {
379
+ stagger = computeCachedCssStaggerStyles(
470
380
  node,
471
- className,
381
+ preparationClasses,
472
382
  cacheKey,
473
- allowNoDuration,
474
- DETECT_CSS_PROPERTIES,
475
- );
476
- const aD = timings.animationDelay;
477
- const tD = timings.transitionDelay;
478
- timings.maxDelay = aD && tD ? Math.max(aD, tD) : aD || tD;
479
- timings.maxDuration = Math.max(
480
- timings.animationDuration * timings.animationIterationCount,
481
- timings.transitionDuration,
383
+ DETECT_STAGGER_CSS_PROPERTIES,
482
384
  );
385
+ }
483
386
 
484
- return timings;
387
+ if (!options.$$skipPreparationClasses) {
388
+ element[0].classList.add(preparationClasses);
485
389
  }
486
390
 
487
- return function init(element, initialOptions) {
488
- // all of the animation functions should create
489
- // a copy of the options data, however, if a
490
- // parent service has already created a copy then
491
- // we should stick to using that
492
- let options = initialOptions || {};
493
- if (!options.$$prepared) {
494
- options = prepareAnimationOptions(structuredClone(options));
495
- }
391
+ let applyOnlyDuration;
496
392
 
497
- const restoreStyles = {};
498
- const node = getDomNode(element);
499
- if (!node || !node.parentNode || !$$animateQueue.enabled()) {
500
- return closeAndReturnNoopAnimator();
501
- }
393
+ if (options.transitionStyle) {
394
+ const transitionStyle = [TRANSITION_PROP, options.transitionStyle];
395
+ applyInlineStyle(node, transitionStyle);
396
+ temporaryStyles.push(transitionStyle);
397
+ }
502
398
 
503
- const temporaryStyles = [];
504
- const classes = element.attr("class");
505
- const styles = packageStyles(options);
506
- let animationClosed;
507
- let animationPaused;
508
- let animationCompleted;
509
- let runner;
510
- let runnerHost;
511
- let maxDelay;
512
- let maxDelayTime;
513
- let maxDuration;
514
- let maxDurationTime;
515
- let startTime;
516
- const events = [];
517
-
518
- if (options.duration === 0) {
519
- return closeAndReturnNoopAnimator();
520
- }
399
+ if (options.duration >= 0) {
400
+ applyOnlyDuration = node.style[TRANSITION_PROP].length > 0;
401
+ const durationStyle = getCssTransitionDurationStyle(
402
+ options.duration,
403
+ applyOnlyDuration,
404
+ );
521
405
 
522
- const method =
523
- options.event && Array.isArray(options.event)
524
- ? options.event.join(" ")
525
- : options.event;
406
+ // we set the duration so that it will be picked up by getComputedStyle later
407
+ applyInlineStyle(node, durationStyle);
408
+ temporaryStyles.push(durationStyle);
409
+ }
526
410
 
527
- const isStructural = method && options.structural;
528
- let structuralClassName = "";
529
- let addRemoveClassName = "";
411
+ if (options.keyframeStyle) {
412
+ const keyframeStyle = [ANIMATION_PROP, options.keyframeStyle];
413
+ applyInlineStyle(node, keyframeStyle);
414
+ temporaryStyles.push(keyframeStyle);
415
+ }
530
416
 
531
- if (isStructural) {
532
- structuralClassName = pendClasses(method, EVENT_CLASS_PREFIX, true);
533
- } else if (method) {
534
- structuralClassName = method;
535
- }
417
+ const itemIndex = stagger
418
+ ? options.staggerIndex >= 0
419
+ ? options.staggerIndex
420
+ : $$animateCache.count(cacheKey)
421
+ : 0;
422
+
423
+ const isFirst = itemIndex === 0;
424
+
425
+ // this is a pre-emptive way of forcing the setup classes to be added and applied INSTANTLY
426
+ // without causing any combination of transitions to kick in. By adding a negative delay value
427
+ // it forces the setup class' transition to end immediately. We later then remove the negative
428
+ // transition delay to allow for the transition to naturally do it's thing. The beauty here is
429
+ // that if there is no transition defined then nothing will happen and this will also allow
430
+ // other transitions to be stacked on top of each other without any chopping them out.
431
+ if (isFirst && !options.skipBlocking) {
432
+ blockTransitions(node, SAFE_FAST_FORWARD_DURATION_VALUE);
433
+ }
536
434
 
537
- if (options.addClass) {
538
- addRemoveClassName += pendClasses(
539
- options.addClass,
540
- ADD_CLASS_SUFFIX,
435
+ let timings = computeTimings(
436
+ node,
437
+ fullClassName,
438
+ cacheKey,
439
+ !isStructural,
440
+ );
441
+ let relativeDelay = timings.maxDelay;
442
+ maxDelay = Math.max(relativeDelay, 0);
443
+ maxDuration = timings.maxDuration;
444
+
445
+ const flags = {};
446
+ flags.hasTransitions = timings.transitionDuration > 0;
447
+ flags.hasAnimations = timings.animationDuration > 0;
448
+ flags.hasTransitionAll =
449
+ flags.hasTransitions && timings.transitionProperty === "all";
450
+ flags.applyTransitionDuration =
451
+ hasToStyles &&
452
+ ((flags.hasTransitions && !flags.hasTransitionAll) ||
453
+ (flags.hasAnimations && !flags.hasTransitions));
454
+ flags.applyAnimationDuration = options.duration && flags.hasAnimations;
455
+ flags.applyTransitionDelay =
456
+ truthyTimingValue(options.delay) &&
457
+ (flags.applyTransitionDuration || flags.hasTransitions);
458
+ flags.applyAnimationDelay =
459
+ truthyTimingValue(options.delay) && flags.hasAnimations;
460
+ flags.recalculateTimingStyles = addRemoveClassName.length > 0;
461
+
462
+ if (flags.applyTransitionDuration || flags.applyAnimationDuration) {
463
+ maxDuration = options.duration
464
+ ? parseFloat(options.duration)
465
+ : maxDuration;
466
+
467
+ if (flags.applyTransitionDuration) {
468
+ flags.hasTransitions = true;
469
+ timings.transitionDuration = maxDuration;
470
+ applyOnlyDuration =
471
+ node.style[TRANSITION_PROP + PROPERTY_KEY].length > 0;
472
+ temporaryStyles.push(
473
+ getCssTransitionDurationStyle(maxDuration, applyOnlyDuration),
541
474
  );
542
475
  }
543
476
 
544
- if (options.removeClass) {
545
- if (addRemoveClassName.length) {
546
- addRemoveClassName += " ";
547
- }
548
- addRemoveClassName += pendClasses(
549
- options.removeClass,
550
- REMOVE_CLASS_SUFFIX,
551
- );
477
+ if (flags.applyAnimationDuration) {
478
+ flags.hasAnimations = true;
479
+ timings.animationDuration = maxDuration;
480
+ temporaryStyles.push(getCssKeyframeDurationStyle(maxDuration));
552
481
  }
482
+ }
553
483
 
554
- // there may be a situation where a structural animation is combined together
555
- // with CSS classes that need to resolve before the animation is computed.
556
- // However this means that there is no explicit CSS code to block the animation
557
- // from happening (by setting 0s none in the class name). If this is the case
558
- // we need to apply the classes before the first rAF so we know to continue if
559
- // there actually is a detected transition or keyframe animation
560
- if (options.applyClassesEarly && addRemoveClassName.length) {
561
- applyAnimationClasses(element, options);
484
+ if (maxDuration === 0 && !flags.recalculateTimingStyles) {
485
+ return closeAndReturnNoopAnimator();
486
+ }
487
+
488
+ const activeClasses = pendClasses(
489
+ preparationClasses,
490
+ ACTIVE_CLASS_SUFFIX,
491
+ );
492
+
493
+ if (options.delay != null) {
494
+ var delayStyle;
495
+ if (typeof options.delay !== "boolean") {
496
+ delayStyle = parseFloat(options.delay);
497
+ // number in options.delay means we have to recalculate the delay for the closing timeout
498
+ maxDelay = Math.max(delayStyle, 0);
562
499
  }
563
500
 
564
- let preparationClasses = [structuralClassName, addRemoveClassName]
565
- .join(" ")
566
- .trim();
567
- let fullClassName = `${classes} ${preparationClasses}`;
568
- const hasToStyles = styles.to && Object.keys(styles.to).length > 0;
569
- const containsKeyframeAnimation =
570
- (options.keyframeStyle || "").length > 0;
571
-
572
- // there is no way we can trigger an animation if no styles and
573
- // no classes are being applied which would then trigger a transition,
574
- // unless there a is raw keyframe value that is applied to the element.
575
- if (
576
- !containsKeyframeAnimation &&
577
- !hasToStyles &&
578
- !preparationClasses
579
- ) {
580
- return closeAndReturnNoopAnimator();
501
+ if (flags.applyTransitionDelay) {
502
+ temporaryStyles.push(getCssDelayStyle(delayStyle));
581
503
  }
582
504
 
583
- let stagger;
584
- let cacheKey = $$animateCache.cacheKey(
585
- node,
586
- method,
587
- options.addClass,
588
- options.removeClass,
589
- );
590
- if ($$animateCache.containsCachedAnimationWithoutDuration(cacheKey)) {
591
- preparationClasses = null;
592
- return closeAndReturnNoopAnimator();
505
+ if (flags.applyAnimationDelay) {
506
+ temporaryStyles.push(getCssDelayStyle(delayStyle, true));
593
507
  }
508
+ }
594
509
 
595
- if (options.stagger > 0) {
596
- const staggerVal = parseFloat(options.stagger);
597
- stagger = {
598
- transitionDelay: staggerVal,
599
- animationDelay: staggerVal,
600
- transitionDuration: 0,
601
- animationDuration: 0,
602
- };
603
- } else {
604
- stagger = computeCachedCssStaggerStyles(
510
+ // we need to recalculate the delay value since we used a pre-emptive negative
511
+ // delay value and the delay value is required for the final event checking. This
512
+ // property will ensure that this will happen after the RAF phase has passed.
513
+ if (options.duration == null && timings.transitionDuration > 0) {
514
+ flags.recalculateTimingStyles =
515
+ flags.recalculateTimingStyles || isFirst;
516
+ }
517
+
518
+ maxDelayTime = maxDelay * ONE_SECOND;
519
+ maxDurationTime = maxDuration * ONE_SECOND;
520
+ if (!options.skipBlocking) {
521
+ flags.blockTransition = timings.transitionDuration > 0;
522
+ flags.blockKeyframeAnimation =
523
+ timings.animationDuration > 0 &&
524
+ stagger.animationDelay > 0 &&
525
+ stagger.animationDuration === 0;
526
+ }
527
+
528
+ if (options.from) {
529
+ if (options.cleanupStyles) {
530
+ registerRestorableStyles(
531
+ restoreStyles,
605
532
  node,
606
- preparationClasses,
607
- cacheKey,
608
- DETECT_STAGGER_CSS_PROPERTIES,
533
+ Object.keys(options.from),
609
534
  );
610
535
  }
536
+ applyAnimationFromStyles(element, options);
537
+ }
611
538
 
612
- if (!options.$$skipPreparationClasses) {
613
- element[0].classList.add(preparationClasses);
614
- }
539
+ if (flags.blockTransition || flags.blockKeyframeAnimation) {
540
+ applyBlocking(maxDuration);
541
+ } else if (!options.skipBlocking) {
542
+ blockTransitions(node, false);
543
+ }
615
544
 
616
- let applyOnlyDuration;
545
+ // TODO(matsko): for 1.5 change this code to have an animator object for better debugging
546
+ return {
547
+ $$willAnimate: true,
548
+ end: endFn,
549
+ start() {
550
+ if (animationClosed) return;
617
551
 
618
- if (options.transitionStyle) {
619
- const transitionStyle = [TRANSITION_PROP, options.transitionStyle];
620
- applyInlineStyle(node, transitionStyle);
621
- temporaryStyles.push(transitionStyle);
622
- }
552
+ runnerHost = {
553
+ end: endFn,
554
+ cancel: cancelFn,
555
+ resume: null, // this will be set during the start() phase
556
+ pause: null,
557
+ };
623
558
 
624
- if (options.duration >= 0) {
625
- applyOnlyDuration = node.style[TRANSITION_PROP].length > 0;
626
- const durationStyle = getCssTransitionDurationStyle(
627
- options.duration,
628
- applyOnlyDuration,
629
- );
559
+ runner = new $$AnimateRunner(runnerHost);
630
560
 
631
- // we set the duration so that it will be picked up by getComputedStyle later
632
- applyInlineStyle(node, durationStyle);
633
- temporaryStyles.push(durationStyle);
634
- }
561
+ waitUntilQuiet(start);
635
562
 
636
- if (options.keyframeStyle) {
637
- const keyframeStyle = [ANIMATION_PROP, options.keyframeStyle];
638
- applyInlineStyle(node, keyframeStyle);
639
- temporaryStyles.push(keyframeStyle);
640
- }
563
+ // we don't have access to pause/resume the animation
564
+ // since it hasn't run yet. AnimateRunner will therefore
565
+ // set noop functions for resume and pause and they will
566
+ // later be overridden once the animation is triggered
567
+ return runner;
568
+ },
569
+ };
641
570
 
642
- const itemIndex = stagger
643
- ? options.staggerIndex >= 0
644
- ? options.staggerIndex
645
- : $$animateCache.count(cacheKey)
646
- : 0;
647
-
648
- const isFirst = itemIndex === 0;
649
-
650
- // this is a pre-emptive way of forcing the setup classes to be added and applied INSTANTLY
651
- // without causing any combination of transitions to kick in. By adding a negative delay value
652
- // it forces the setup class' transition to end immediately. We later then remove the negative
653
- // transition delay to allow for the transition to naturally do it's thing. The beauty here is
654
- // that if there is no transition defined then nothing will happen and this will also allow
655
- // other transitions to be stacked on top of each other without any chopping them out.
656
- if (isFirst && !options.skipBlocking) {
657
- blockTransitions(node, SAFE_FAST_FORWARD_DURATION_VALUE);
658
- }
571
+ function endFn() {
572
+ close();
573
+ }
659
574
 
660
- let timings = computeTimings(
661
- node,
662
- fullClassName,
663
- cacheKey,
664
- !isStructural,
665
- );
666
- let relativeDelay = timings.maxDelay;
667
- maxDelay = Math.max(relativeDelay, 0);
668
- maxDuration = timings.maxDuration;
669
-
670
- const flags = {};
671
- flags.hasTransitions = timings.transitionDuration > 0;
672
- flags.hasAnimations = timings.animationDuration > 0;
673
- flags.hasTransitionAll =
674
- flags.hasTransitions && timings.transitionProperty === "all";
675
- flags.applyTransitionDuration =
676
- hasToStyles &&
677
- ((flags.hasTransitions && !flags.hasTransitionAll) ||
678
- (flags.hasAnimations && !flags.hasTransitions));
679
- flags.applyAnimationDuration =
680
- options.duration && flags.hasAnimations;
681
- flags.applyTransitionDelay =
682
- truthyTimingValue(options.delay) &&
683
- (flags.applyTransitionDuration || flags.hasTransitions);
684
- flags.applyAnimationDelay =
685
- truthyTimingValue(options.delay) && flags.hasAnimations;
686
- flags.recalculateTimingStyles = addRemoveClassName.length > 0;
687
-
688
- if (flags.applyTransitionDuration || flags.applyAnimationDuration) {
689
- maxDuration = options.duration
690
- ? parseFloat(options.duration)
691
- : maxDuration;
692
-
693
- if (flags.applyTransitionDuration) {
694
- flags.hasTransitions = true;
695
- timings.transitionDuration = maxDuration;
696
- applyOnlyDuration =
697
- node.style[TRANSITION_PROP + PROPERTY_KEY].length > 0;
698
- temporaryStyles.push(
699
- getCssTransitionDurationStyle(maxDuration, applyOnlyDuration),
700
- );
701
- }
575
+ function cancelFn() {
576
+ close(true);
577
+ }
702
578
 
703
- if (flags.applyAnimationDuration) {
704
- flags.hasAnimations = true;
705
- timings.animationDuration = maxDuration;
706
- temporaryStyles.push(getCssKeyframeDurationStyle(maxDuration));
707
- }
579
+ function close(rejected) {
580
+ // if the promise has been called already then we shouldn't close
581
+ // the animation again
582
+ if (animationClosed || (animationCompleted && animationPaused))
583
+ return;
584
+ animationClosed = true;
585
+ animationPaused = false;
586
+
587
+ if (preparationClasses && !options.$$skipPreparationClasses) {
588
+ preparationClasses
589
+ .split(" ")
590
+ .forEach((cls) => element.classList.remove(cls));
708
591
  }
709
592
 
710
- if (maxDuration === 0 && !flags.recalculateTimingStyles) {
711
- return closeAndReturnNoopAnimator();
593
+ if (activeClasses) {
594
+ activeClasses
595
+ .split(" ")
596
+ .forEach((cls) => element.classList.remove(cls));
712
597
  }
713
598
 
714
- const activeClasses = pendClasses(
715
- preparationClasses,
716
- ACTIVE_CLASS_SUFFIX,
717
- );
599
+ blockKeyframeAnimations(node, false);
600
+ blockTransitions(node, false);
718
601
 
719
- if (options.delay != null) {
720
- var delayStyle;
721
- if (typeof options.delay !== "boolean") {
722
- delayStyle = parseFloat(options.delay);
723
- // number in options.delay means we have to recalculate the delay for the closing timeout
724
- maxDelay = Math.max(delayStyle, 0);
725
- }
602
+ forEach(temporaryStyles, (entry) => {
603
+ // There is only one way to remove inline style properties entirely from elements.
604
+ // By using `removeProperty` this works, but we need to convert camel-cased CSS
605
+ // styles down to hyphenated values.
606
+ node.style[entry[0]] = "";
607
+ });
726
608
 
727
- if (flags.applyTransitionDelay) {
728
- temporaryStyles.push(getCssDelayStyle(delayStyle));
729
- }
609
+ applyAnimationClasses(element, options);
610
+ applyAnimationStyles(element, options);
730
611
 
731
- if (flags.applyAnimationDelay) {
732
- temporaryStyles.push(getCssDelayStyle(delayStyle, true));
733
- }
612
+ if (Object.keys(restoreStyles).length) {
613
+ forEach(restoreStyles, (value, prop) => {
614
+ if (value) {
615
+ node.style.setProperty(prop, value);
616
+ } else {
617
+ node.style.removeProperty(prop);
618
+ }
619
+ });
734
620
  }
735
621
 
736
- // we need to recalculate the delay value since we used a pre-emptive negative
737
- // delay value and the delay value is required for the final event checking. This
738
- // property will ensure that this will happen after the RAF phase has passed.
739
- if (options.duration == null && timings.transitionDuration > 0) {
740
- flags.recalculateTimingStyles =
741
- flags.recalculateTimingStyles || isFirst;
622
+ // the reason why we have this option is to allow a synchronous closing callback
623
+ // that is fired as SOON as the animation ends (when the CSS is removed) or if
624
+ // the animation never takes off at all. A good example is a leave animation since
625
+ // the element must be removed just after the animation is over or else the element
626
+ // will appear on screen for one animation frame causing an overbearing flicker.
627
+ if (options.onDone) {
628
+ options.onDone();
742
629
  }
743
630
 
744
- maxDelayTime = maxDelay * ONE_SECOND;
745
- maxDurationTime = maxDuration * ONE_SECOND;
746
- if (!options.skipBlocking) {
747
- flags.blockTransition = timings.transitionDuration > 0;
748
- flags.blockKeyframeAnimation =
749
- timings.animationDuration > 0 &&
750
- stagger.animationDelay > 0 &&
751
- stagger.animationDuration === 0;
631
+ if (events && events.length) {
632
+ // Remove the transitionend / animationend listener(s)
633
+ element.off(events.join(" "), onAnimationProgress);
752
634
  }
753
635
 
754
- if (options.from) {
755
- if (options.cleanupStyles) {
756
- registerRestorableStyles(
757
- restoreStyles,
758
- node,
759
- Object.keys(options.from),
760
- );
761
- }
762
- applyAnimationFromStyles(element, options);
636
+ // Cancel the fallback closing timeout and remove the timer data
637
+ const animationTimerData = element.data(ANIMATE_TIMER_KEY);
638
+ if (animationTimerData) {
639
+ $timeout.cancel(animationTimerData[0].timer);
640
+ element.removeData(ANIMATE_TIMER_KEY);
763
641
  }
764
642
 
765
- if (flags.blockTransition || flags.blockKeyframeAnimation) {
766
- applyBlocking(maxDuration);
767
- } else if (!options.skipBlocking) {
768
- blockTransitions(node, false);
643
+ // if the preparation function fails then the promise is not setup
644
+ if (runner) {
645
+ runner.complete(!rejected);
769
646
  }
647
+ }
770
648
 
771
- // TODO(matsko): for 1.5 change this code to have an animator object for better debugging
772
- return {
773
- $$willAnimate: true,
774
- end: endFn,
775
- start() {
776
- if (animationClosed) return;
649
+ function applyBlocking(duration) {
650
+ if (flags.blockTransition) {
651
+ blockTransitions(node, duration);
652
+ }
777
653
 
778
- runnerHost = {
779
- end: endFn,
780
- cancel: cancelFn,
781
- resume: null, // this will be set during the start() phase
782
- pause: null,
783
- };
654
+ if (flags.blockKeyframeAnimation) {
655
+ blockKeyframeAnimations(node, !!duration);
656
+ }
657
+ }
784
658
 
785
- runner = new $$AnimateRunner(runnerHost);
659
+ function closeAndReturnNoopAnimator() {
660
+ runner = new $$AnimateRunner({
661
+ end: endFn,
662
+ cancel: cancelFn,
663
+ });
786
664
 
787
- waitUntilQuiet(start);
665
+ // should flush the cache animation
666
+ waitUntilQuiet(() => {});
667
+ close();
788
668
 
789
- // we don't have access to pause/resume the animation
790
- // since it hasn't run yet. AnimateRunner will therefore
791
- // set noop functions for resume and pause and they will
792
- // later be overridden once the animation is triggered
669
+ return {
670
+ $$willAnimate: false,
671
+ start() {
793
672
  return runner;
794
673
  },
674
+ end: endFn,
795
675
  };
676
+ }
796
677
 
797
- function endFn() {
798
- close();
799
- }
678
+ function onAnimationProgress(event) {
679
+ event.stopPropagation();
680
+ const ev = event.originalEvent || event;
800
681
 
801
- function cancelFn() {
802
- close(true);
682
+ if (ev.target !== node) {
683
+ // Since TransitionEvent / AnimationEvent bubble up,
684
+ // we have to ignore events by finished child animations
685
+ return;
803
686
  }
804
687
 
805
- function close(rejected) {
806
- // if the promise has been called already then we shouldn't close
807
- // the animation again
808
- if (animationClosed || (animationCompleted && animationPaused))
809
- return;
810
- animationClosed = true;
811
- animationPaused = false;
812
-
813
- if (preparationClasses && !options.$$skipPreparationClasses) {
814
- preparationClasses
815
- .split(" ")
816
- .forEach((cls) => element.classList.remove(cls));
817
- }
688
+ // we now always use `Date.now()` due to the recent changes with
689
+ // event.timeStamp in Firefox, Webkit and Chrome (see #13494 for more info)
690
+ const timeStamp = ev.$manualTimeStamp || Date.now();
818
691
 
819
- if (activeClasses) {
820
- activeClasses
821
- .split(" ")
822
- .forEach((cls) => element.classList.remove(cls));
823
- }
824
-
825
- blockKeyframeAnimations(node, false);
826
- blockTransitions(node, false);
692
+ /* Firefox (or possibly just Gecko) likes to not round values up
693
+ * when a ms measurement is used for the animation */
694
+ const elapsedTime = parseFloat(
695
+ ev.elapsedTime.toFixed(ELAPSED_TIME_MAX_DECIMAL_PLACES),
696
+ );
827
697
 
828
- forEach(temporaryStyles, (entry) => {
829
- // There is only one way to remove inline style properties entirely from elements.
830
- // By using `removeProperty` this works, but we need to convert camel-cased CSS
831
- // styles down to hyphenated values.
832
- node.style[entry[0]] = "";
833
- });
698
+ /* $manualTimeStamp is a mocked timeStamp value which is set
699
+ * within browserTrigger(). This is only here so that tests can
700
+ * mock animations properly. Real events fallback to event.timeStamp,
701
+ * or, if they don't, then a timeStamp is automatically created for them.
702
+ * We're checking to see if the timeStamp surpasses the expected delay,
703
+ * but we're using elapsedTime instead of the timeStamp on the 2nd
704
+ * pre-condition since animationPauseds sometimes close off early */
705
+ if (
706
+ Math.max(timeStamp - startTime, 0) >= maxDelayTime &&
707
+ elapsedTime >= maxDuration
708
+ ) {
709
+ // we set this flag to ensure that if the transition is paused then, when resumed,
710
+ // the animation will automatically close itself since transitions cannot be paused.
711
+ animationCompleted = true;
712
+ close();
713
+ }
714
+ }
834
715
 
835
- applyAnimationClasses(element, options);
836
- applyAnimationStyles(element, options);
716
+ function start() {
717
+ if (animationClosed) return;
718
+ if (!node.parentNode) {
719
+ close();
720
+ return;
721
+ }
837
722
 
838
- if (Object.keys(restoreStyles).length) {
839
- forEach(restoreStyles, (value, prop) => {
840
- if (value) {
841
- node.style.setProperty(prop, value);
723
+ // even though we only pause keyframe animations here the pause flag
724
+ // will still happen when transitions are used. Only the transition will
725
+ // not be paused since that is not possible. If the animation ends when
726
+ // paused then it will not complete until unpaused or cancelled.
727
+ const playPause = function (playAnimation) {
728
+ if (!animationCompleted) {
729
+ animationPaused = !playAnimation;
730
+ if (timings.animationDuration) {
731
+ const value = blockKeyframeAnimations(node, animationPaused);
732
+ if (animationPaused) {
733
+ temporaryStyles.push(value);
842
734
  } else {
843
- node.style.removeProperty(prop);
735
+ removeFromArray(temporaryStyles, value);
844
736
  }
845
- });
846
- }
847
-
848
- // the reason why we have this option is to allow a synchronous closing callback
849
- // that is fired as SOON as the animation ends (when the CSS is removed) or if
850
- // the animation never takes off at all. A good example is a leave animation since
851
- // the element must be removed just after the animation is over or else the element
852
- // will appear on screen for one animation frame causing an overbearing flicker.
853
- if (options.onDone) {
854
- options.onDone();
855
- }
856
-
857
- if (events && events.length) {
858
- // Remove the transitionend / animationend listener(s)
859
- element.off(events.join(" "), onAnimationProgress);
860
- }
861
-
862
- // Cancel the fallback closing timeout and remove the timer data
863
- const animationTimerData = element.data(ANIMATE_TIMER_KEY);
864
- if (animationTimerData) {
865
- $timeout.cancel(animationTimerData[0].timer);
866
- element.removeData(ANIMATE_TIMER_KEY);
867
- }
868
-
869
- // if the preparation function fails then the promise is not setup
870
- if (runner) {
871
- runner.complete(!rejected);
872
- }
873
- }
874
-
875
- function applyBlocking(duration) {
876
- if (flags.blockTransition) {
877
- blockTransitions(node, duration);
737
+ }
738
+ } else if (animationPaused && playAnimation) {
739
+ animationPaused = false;
740
+ close();
878
741
  }
742
+ };
879
743
 
880
- if (flags.blockKeyframeAnimation) {
881
- blockKeyframeAnimations(node, !!duration);
882
- }
744
+ // checking the stagger duration prevents an accidentally cascade of the CSS delay style
745
+ // being inherited from the parent. If the transition duration is zero then we can safely
746
+ // rely that the delay value is an intentional stagger delay style.
747
+ const maxStagger =
748
+ itemIndex > 0 &&
749
+ ((timings.transitionDuration && stagger.transitionDuration === 0) ||
750
+ (timings.animationDuration && stagger.animationDuration === 0)) &&
751
+ Math.max(stagger.animationDelay, stagger.transitionDelay);
752
+ if (maxStagger) {
753
+ $timeout(
754
+ triggerAnimationStart,
755
+ Math.floor(maxStagger * itemIndex * ONE_SECOND),
756
+ false,
757
+ );
758
+ } else {
759
+ triggerAnimationStart();
883
760
  }
884
761
 
885
- function closeAndReturnNoopAnimator() {
886
- runner = new $$AnimateRunner({
887
- end: endFn,
888
- cancel: cancelFn,
889
- });
890
-
891
- // should flush the cache animation
892
- waitUntilQuiet(() => {});
893
- close();
894
-
895
- return {
896
- $$willAnimate: false,
897
- start() {
898
- return runner;
899
- },
900
- end: endFn,
901
- };
902
- }
762
+ // this will decorate the existing promise runner with pause/resume methods
763
+ runnerHost.resume = function () {
764
+ playPause(true);
765
+ };
903
766
 
904
- function onAnimationProgress(event) {
905
- event.stopPropagation();
906
- const ev = event.originalEvent || event;
767
+ runnerHost.pause = function () {
768
+ playPause(false);
769
+ };
907
770
 
908
- if (ev.target !== node) {
909
- // Since TransitionEvent / AnimationEvent bubble up,
910
- // we have to ignore events by finished child animations
911
- return;
912
- }
771
+ function triggerAnimationStart() {
772
+ // just incase a stagger animation kicks in when the animation
773
+ // itself was cancelled entirely
774
+ if (animationClosed) return;
913
775
 
914
- // we now always use `Date.now()` due to the recent changes with
915
- // event.timeStamp in Firefox, Webkit and Chrome (see #13494 for more info)
916
- const timeStamp = ev.$manualTimeStamp || Date.now();
776
+ applyBlocking(false);
917
777
 
918
- /* Firefox (or possibly just Gecko) likes to not round values up
919
- * when a ms measurement is used for the animation */
920
- const elapsedTime = parseFloat(
921
- ev.elapsedTime.toFixed(ELAPSED_TIME_MAX_DECIMAL_PLACES),
922
- );
778
+ forEach(temporaryStyles, (entry) => {
779
+ const key = entry[0];
780
+ const value = entry[1];
781
+ node.style[key] = value;
782
+ });
923
783
 
924
- /* $manualTimeStamp is a mocked timeStamp value which is set
925
- * within browserTrigger(). This is only here so that tests can
926
- * mock animations properly. Real events fallback to event.timeStamp,
927
- * or, if they don't, then a timeStamp is automatically created for them.
928
- * We're checking to see if the timeStamp surpasses the expected delay,
929
- * but we're using elapsedTime instead of the timeStamp on the 2nd
930
- * pre-condition since animationPauseds sometimes close off early */
931
- if (
932
- Math.max(timeStamp - startTime, 0) >= maxDelayTime &&
933
- elapsedTime >= maxDuration
934
- ) {
935
- // we set this flag to ensure that if the transition is paused then, when resumed,
936
- // the animation will automatically close itself since transitions cannot be paused.
937
- animationCompleted = true;
938
- close();
939
- }
940
- }
784
+ applyAnimationClasses(element, options);
785
+ element.className += ` ${activeClasses}`;
786
+ if (flags.recalculateTimingStyles) {
787
+ fullClassName = `${node.getAttribute("class")} ${preparationClasses}`;
788
+ cacheKey = $$animateCache.cacheKey(
789
+ node,
790
+ method,
791
+ options.addClass,
792
+ options.removeClass,
793
+ );
941
794
 
942
- function start() {
943
- if (animationClosed) return;
944
- if (!node.parentNode) {
945
- close();
946
- return;
947
- }
795
+ timings = computeTimings(node, fullClassName, cacheKey, false);
796
+ relativeDelay = timings.maxDelay;
797
+ maxDelay = Math.max(relativeDelay, 0);
798
+ maxDuration = timings.maxDuration;
948
799
 
949
- // even though we only pause keyframe animations here the pause flag
950
- // will still happen when transitions are used. Only the transition will
951
- // not be paused since that is not possible. If the animation ends when
952
- // paused then it will not complete until unpaused or cancelled.
953
- const playPause = function (playAnimation) {
954
- if (!animationCompleted) {
955
- animationPaused = !playAnimation;
956
- if (timings.animationDuration) {
957
- const value = blockKeyframeAnimations(node, animationPaused);
958
- if (animationPaused) {
959
- temporaryStyles.push(value);
960
- } else {
961
- removeFromArray(temporaryStyles, value);
962
- }
963
- }
964
- } else if (animationPaused && playAnimation) {
965
- animationPaused = false;
800
+ if (maxDuration === 0) {
966
801
  close();
802
+ return;
967
803
  }
968
- };
969
804
 
970
- // checking the stagger duration prevents an accidentally cascade of the CSS delay style
971
- // being inherited from the parent. If the transition duration is zero then we can safely
972
- // rely that the delay value is an intentional stagger delay style.
973
- const maxStagger =
974
- itemIndex > 0 &&
975
- ((timings.transitionDuration &&
976
- stagger.transitionDuration === 0) ||
977
- (timings.animationDuration &&
978
- stagger.animationDuration === 0)) &&
979
- Math.max(stagger.animationDelay, stagger.transitionDelay);
980
- if (maxStagger) {
981
- $timeout(
982
- triggerAnimationStart,
983
- Math.floor(maxStagger * itemIndex * ONE_SECOND),
984
- false,
985
- );
986
- } else {
987
- triggerAnimationStart();
805
+ flags.hasTransitions = timings.transitionDuration > 0;
806
+ flags.hasAnimations = timings.animationDuration > 0;
988
807
  }
989
808
 
990
- // this will decorate the existing promise runner with pause/resume methods
991
- runnerHost.resume = function () {
992
- playPause(true);
993
- };
994
-
995
- runnerHost.pause = function () {
996
- playPause(false);
997
- };
998
-
999
- function triggerAnimationStart() {
1000
- // just incase a stagger animation kicks in when the animation
1001
- // itself was cancelled entirely
1002
- if (animationClosed) return;
1003
-
1004
- applyBlocking(false);
1005
-
1006
- forEach(temporaryStyles, (entry) => {
1007
- const key = entry[0];
1008
- const value = entry[1];
1009
- node.style[key] = value;
1010
- });
1011
-
1012
- applyAnimationClasses(element, options);
1013
- element.className += ` ${activeClasses}`;
1014
- if (flags.recalculateTimingStyles) {
1015
- fullClassName = `${node.getAttribute("class")} ${preparationClasses}`;
1016
- cacheKey = $$animateCache.cacheKey(
1017
- node,
1018
- method,
1019
- options.addClass,
1020
- options.removeClass,
1021
- );
809
+ if (flags.applyAnimationDelay) {
810
+ relativeDelay =
811
+ typeof options.delay !== "boolean" &&
812
+ truthyTimingValue(options.delay)
813
+ ? parseFloat(options.delay)
814
+ : relativeDelay;
815
+
816
+ maxDelay = Math.max(relativeDelay, 0);
817
+ timings.animationDelay = relativeDelay;
818
+ delayStyle = getCssDelayStyle(relativeDelay, true);
819
+ temporaryStyles.push(delayStyle);
820
+ node.style[delayStyle[0]] = delayStyle[1];
821
+ }
1022
822
 
1023
- timings = computeTimings(node, fullClassName, cacheKey, false);
1024
- relativeDelay = timings.maxDelay;
1025
- maxDelay = Math.max(relativeDelay, 0);
1026
- maxDuration = timings.maxDuration;
823
+ maxDelayTime = maxDelay * ONE_SECOND;
824
+ maxDurationTime = maxDuration * ONE_SECOND;
1027
825
 
1028
- if (maxDuration === 0) {
1029
- close();
1030
- return;
1031
- }
1032
-
1033
- flags.hasTransitions = timings.transitionDuration > 0;
1034
- flags.hasAnimations = timings.animationDuration > 0;
826
+ if (options.easing) {
827
+ let easeProp;
828
+ const easeVal = options.easing;
829
+ if (flags.hasTransitions) {
830
+ easeProp = TRANSITION_PROP + TIMING_KEY;
831
+ temporaryStyles.push([easeProp, easeVal]);
832
+ node.style[easeProp] = easeVal;
1035
833
  }
1036
-
1037
- if (flags.applyAnimationDelay) {
1038
- relativeDelay =
1039
- typeof options.delay !== "boolean" &&
1040
- truthyTimingValue(options.delay)
1041
- ? parseFloat(options.delay)
1042
- : relativeDelay;
1043
-
1044
- maxDelay = Math.max(relativeDelay, 0);
1045
- timings.animationDelay = relativeDelay;
1046
- delayStyle = getCssDelayStyle(relativeDelay, true);
1047
- temporaryStyles.push(delayStyle);
1048
- node.style[delayStyle[0]] = delayStyle[1];
1049
- }
1050
-
1051
- maxDelayTime = maxDelay * ONE_SECOND;
1052
- maxDurationTime = maxDuration * ONE_SECOND;
1053
-
1054
- if (options.easing) {
1055
- let easeProp;
1056
- const easeVal = options.easing;
1057
- if (flags.hasTransitions) {
1058
- easeProp = TRANSITION_PROP + TIMING_KEY;
1059
- temporaryStyles.push([easeProp, easeVal]);
1060
- node.style[easeProp] = easeVal;
1061
- }
1062
- if (flags.hasAnimations) {
1063
- easeProp = ANIMATION_PROP + TIMING_KEY;
1064
- temporaryStyles.push([easeProp, easeVal]);
1065
- node.style[easeProp] = easeVal;
1066
- }
834
+ if (flags.hasAnimations) {
835
+ easeProp = ANIMATION_PROP + TIMING_KEY;
836
+ temporaryStyles.push([easeProp, easeVal]);
837
+ node.style[easeProp] = easeVal;
1067
838
  }
839
+ }
1068
840
 
1069
- if (timings.transitionDuration) {
1070
- events.push(TRANSITIONEND_EVENT);
1071
- }
841
+ if (timings.transitionDuration) {
842
+ events.push(TRANSITIONEND_EVENT);
843
+ }
1072
844
 
1073
- if (timings.animationDuration) {
1074
- events.push(ANIMATIONEND_EVENT);
1075
- }
845
+ if (timings.animationDuration) {
846
+ events.push(ANIMATIONEND_EVENT);
847
+ }
1076
848
 
1077
- startTime = Date.now();
1078
- const timerTime =
1079
- maxDelayTime + CLOSING_TIME_BUFFER * maxDurationTime;
1080
- const endTime = startTime + timerTime;
1081
-
1082
- const animationsData = element.data(ANIMATE_TIMER_KEY) || [];
1083
- let setupFallbackTimer = true;
1084
- if (animationsData.length) {
1085
- const currentTimerData = animationsData[0];
1086
- setupFallbackTimer = endTime > currentTimerData.expectedEndTime;
1087
- if (setupFallbackTimer) {
1088
- $timeout.cancel(currentTimerData.timer);
1089
- } else {
1090
- animationsData.push(close);
1091
- }
1092
- }
849
+ startTime = Date.now();
850
+ const timerTime =
851
+ maxDelayTime + CLOSING_TIME_BUFFER * maxDurationTime;
852
+ const endTime = startTime + timerTime;
1093
853
 
854
+ const animationsData = element.data(ANIMATE_TIMER_KEY) || [];
855
+ let setupFallbackTimer = true;
856
+ if (animationsData.length) {
857
+ const currentTimerData = animationsData[0];
858
+ setupFallbackTimer = endTime > currentTimerData.expectedEndTime;
1094
859
  if (setupFallbackTimer) {
1095
- const timer = $timeout(onAnimationExpired, timerTime, false);
1096
- animationsData[0] = {
1097
- timer,
1098
- expectedEndTime: endTime,
1099
- };
860
+ $timeout.cancel(currentTimerData.timer);
861
+ } else {
1100
862
  animationsData.push(close);
1101
- element.data(ANIMATE_TIMER_KEY, animationsData);
1102
863
  }
864
+ }
1103
865
 
1104
- if (events.length) {
1105
- element.on(events.join(" "), onAnimationProgress);
1106
- }
866
+ if (setupFallbackTimer) {
867
+ const timer = $timeout(onAnimationExpired, timerTime, false);
868
+ animationsData[0] = {
869
+ timer,
870
+ expectedEndTime: endTime,
871
+ };
872
+ animationsData.push(close);
873
+ element.data(ANIMATE_TIMER_KEY, animationsData);
874
+ }
1107
875
 
1108
- if (options.to) {
1109
- if (options.cleanupStyles) {
1110
- registerRestorableStyles(
1111
- restoreStyles,
1112
- node,
1113
- Object.keys(options.to),
1114
- );
1115
- }
1116
- applyAnimationToStyles(element, options);
876
+ if (events.length) {
877
+ element.on(events.join(" "), onAnimationProgress);
878
+ }
879
+
880
+ if (options.to) {
881
+ if (options.cleanupStyles) {
882
+ registerRestorableStyles(
883
+ restoreStyles,
884
+ node,
885
+ Object.keys(options.to),
886
+ );
1117
887
  }
888
+ applyAnimationToStyles(element, options);
1118
889
  }
890
+ }
1119
891
 
1120
- function onAnimationExpired() {
1121
- const animationsData = element.data(ANIMATE_TIMER_KEY);
892
+ function onAnimationExpired() {
893
+ const animationsData = element.data(ANIMATE_TIMER_KEY);
1122
894
 
1123
- // this will be false in the event that the element was
1124
- // removed from the DOM (via a leave animation or something
1125
- // similar)
1126
- if (animationsData) {
1127
- for (let i = 1; i < animationsData.length; i++) {
1128
- animationsData[i]();
1129
- }
1130
- element.removeData(ANIMATE_TIMER_KEY);
895
+ // this will be false in the event that the element was
896
+ // removed from the DOM (via a leave animation or something
897
+ // similar)
898
+ if (animationsData) {
899
+ for (let i = 1; i < animationsData.length; i++) {
900
+ animationsData[i]();
1131
901
  }
902
+ element.removeData(ANIMATE_TIMER_KEY);
1132
903
  }
1133
904
  }
1134
- };
1135
- },
1136
- ];
1137
- },
1138
- ];
905
+ }
906
+ };
907
+ },
908
+ ];
909
+ }
1139
910
 
1140
911
  function blockTransitions(node, duration) {
1141
912
  // we use a negative delay value since it performs blocking