rails-angularjs 1.3.16 → 1.4.0.pre.rc.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -13
  3. data/lib/rails-angularjs/version.rb +1 -2
  4. data/vendor/assets/javascripts/angular-animate.js +3231 -1885
  5. data/vendor/assets/javascripts/angular-animate.min.js +45 -29
  6. data/vendor/assets/javascripts/angular-animate.min.js.map +3 -3
  7. data/vendor/assets/javascripts/angular-aria.js +89 -75
  8. data/vendor/assets/javascripts/angular-aria.min.js +9 -9
  9. data/vendor/assets/javascripts/angular-aria.min.js.map +2 -2
  10. data/vendor/assets/javascripts/angular-cookies.js +255 -141
  11. data/vendor/assets/javascripts/angular-cookies.min.js +5 -4
  12. data/vendor/assets/javascripts/angular-cookies.min.js.map +3 -3
  13. data/vendor/assets/javascripts/angular-loader.js +41 -17
  14. data/vendor/assets/javascripts/angular-loader.min.js +5 -5
  15. data/vendor/assets/javascripts/angular-loader.min.js.map +2 -2
  16. data/vendor/assets/javascripts/{unstable/angular-messageFormat.js → angular-message-format.js} +76 -18
  17. data/vendor/assets/javascripts/angular-message-format.min.js +26 -0
  18. data/vendor/assets/javascripts/angular-message-format.min.js.map +8 -0
  19. data/vendor/assets/javascripts/angular-messages.js +430 -153
  20. data/vendor/assets/javascripts/angular-messages.min.js +7 -6
  21. data/vendor/assets/javascripts/angular-messages.min.js.map +3 -3
  22. data/vendor/assets/javascripts/angular-mocks.js +76 -92
  23. data/vendor/assets/javascripts/angular-resource.js +4 -4
  24. data/vendor/assets/javascripts/angular-resource.min.js +3 -3
  25. data/vendor/assets/javascripts/angular-resource.min.js.map +1 -1
  26. data/vendor/assets/javascripts/angular-route.js +8 -6
  27. data/vendor/assets/javascripts/angular-route.min.js +2 -2
  28. data/vendor/assets/javascripts/angular-route.min.js.map +1 -1
  29. data/vendor/assets/javascripts/angular-sanitize.js +32 -28
  30. data/vendor/assets/javascripts/angular-sanitize.min.js +11 -11
  31. data/vendor/assets/javascripts/angular-sanitize.min.js.map +2 -2
  32. data/vendor/assets/javascripts/angular-scenario.js +4155 -2553
  33. data/vendor/assets/javascripts/angular-touch.js +15 -12
  34. data/vendor/assets/javascripts/angular-touch.min.js +9 -9
  35. data/vendor/assets/javascripts/angular-touch.min.js.map +2 -2
  36. data/vendor/assets/javascripts/angular.js +4092 -2529
  37. data/vendor/assets/javascripts/angular.min.js +283 -247
  38. data/vendor/assets/javascripts/angular.min.js.map +3 -3
  39. metadata +8 -39
  40. data/vendor/assets/javascripts/unstable/angular-animate.js +0 -3268
  41. data/vendor/assets/javascripts/unstable/angular-animate.min.js +0 -49
  42. data/vendor/assets/javascripts/unstable/angular-animate.min.js.map +0 -8
  43. data/vendor/assets/javascripts/unstable/angular-aria.js +0 -377
  44. data/vendor/assets/javascripts/unstable/angular-aria.min.js +0 -13
  45. data/vendor/assets/javascripts/unstable/angular-aria.min.js.map +0 -8
  46. data/vendor/assets/javascripts/unstable/angular-cookies.js +0 -320
  47. data/vendor/assets/javascripts/unstable/angular-cookies.min.js +0 -9
  48. data/vendor/assets/javascripts/unstable/angular-cookies.min.js.map +0 -8
  49. data/vendor/assets/javascripts/unstable/angular-loader.js +0 -429
  50. data/vendor/assets/javascripts/unstable/angular-loader.min.js +0 -9
  51. data/vendor/assets/javascripts/unstable/angular-loader.min.js.map +0 -8
  52. data/vendor/assets/javascripts/unstable/angular-messageFormat.min.js +0 -26
  53. data/vendor/assets/javascripts/unstable/angular-messageFormat.min.js.map +0 -8
  54. data/vendor/assets/javascripts/unstable/angular-messages.js +0 -667
  55. data/vendor/assets/javascripts/unstable/angular-messages.min.js +0 -11
  56. data/vendor/assets/javascripts/unstable/angular-messages.min.js.map +0 -8
  57. data/vendor/assets/javascripts/unstable/angular-mocks.js +0 -2452
  58. data/vendor/assets/javascripts/unstable/angular-resource.js +0 -668
  59. data/vendor/assets/javascripts/unstable/angular-resource.min.js +0 -13
  60. data/vendor/assets/javascripts/unstable/angular-resource.min.js.map +0 -8
  61. data/vendor/assets/javascripts/unstable/angular-route.js +0 -991
  62. data/vendor/assets/javascripts/unstable/angular-route.min.js +0 -15
  63. data/vendor/assets/javascripts/unstable/angular-route.min.js.map +0 -8
  64. data/vendor/assets/javascripts/unstable/angular-sanitize.js +0 -683
  65. data/vendor/assets/javascripts/unstable/angular-sanitize.min.js +0 -16
  66. data/vendor/assets/javascripts/unstable/angular-sanitize.min.js.map +0 -8
  67. data/vendor/assets/javascripts/unstable/angular-scenario.js +0 -39082
  68. data/vendor/assets/javascripts/unstable/angular-touch.js +0 -625
  69. data/vendor/assets/javascripts/unstable/angular-touch.min.js +0 -13
  70. data/vendor/assets/javascripts/unstable/angular-touch.min.js.map +0 -8
  71. data/vendor/assets/javascripts/unstable/angular.js +0 -27674
  72. data/vendor/assets/javascripts/unstable/angular.min.js +0 -286
  73. data/vendor/assets/javascripts/unstable/angular.min.js.map +0 -8
@@ -1,3268 +0,0 @@
1
- /**
2
- * @license AngularJS v1.4.0-rc.0
3
- * (c) 2010-2015 Google, Inc. http://angularjs.org
4
- * License: MIT
5
- */
6
- (function(window, angular, undefined) {'use strict';
7
-
8
- /* jshint ignore:start */
9
- var noop = angular.noop;
10
- var extend = angular.extend;
11
- var jqLite = angular.element;
12
- var forEach = angular.forEach;
13
- var isArray = angular.isArray;
14
- var isString = angular.isString;
15
- var isObject = angular.isObject;
16
- var isUndefined = angular.isUndefined;
17
- var isDefined = angular.isDefined;
18
- var isFunction = angular.isFunction;
19
- var isElement = angular.isElement;
20
-
21
- var ELEMENT_NODE = 1;
22
- var COMMENT_NODE = 8;
23
-
24
- var NG_ANIMATE_CHILDREN_DATA = '$$ngAnimateChildren';
25
-
26
- var isPromiseLike = function(p) {
27
- return p && p.then ? true : false;
28
- }
29
-
30
- function mergeClasses(a,b) {
31
- if (!a && !b) return '';
32
- if (!a) return b;
33
- if (!b) return a;
34
- if (isArray(a)) a = a.join(' ');
35
- if (isArray(b)) b = b.join(' ');
36
- return a + ' ' + b;
37
- }
38
-
39
- function packageStyles(options) {
40
- var styles = {};
41
- if (options && (options.to || options.from)) {
42
- styles.to = options.to;
43
- styles.from = options.from;
44
- }
45
- return styles;
46
- }
47
-
48
- function pendClasses(classes, fix, isPrefix) {
49
- var className = '';
50
- classes = isArray(classes)
51
- ? classes
52
- : classes && isString(classes) && classes.length
53
- ? classes.split(/\s+/)
54
- : [];
55
- forEach(classes, function(klass, i) {
56
- if (klass && klass.length > 0) {
57
- className += (i > 0) ? ' ' : '';
58
- className += isPrefix ? fix + klass
59
- : klass + fix;
60
- }
61
- });
62
- return className;
63
- }
64
-
65
- function removeFromArray(arr, val) {
66
- var index = arr.indexOf(val);
67
- if (val >= 0) {
68
- arr.splice(index, 1);
69
- }
70
- }
71
-
72
- function stripCommentsFromElement(element) {
73
- if (element.nodeType === ELEMENT_NODE) {
74
- return jqLite(element);
75
- }
76
- if (element.length === 0) return [];
77
-
78
- // there is no point of stripping anything if the element
79
- // is the only element within the jqLite wrapper.
80
- // (it's important that we retain the element instance.)
81
- if (element.length === 1) {
82
- return element[0].nodeType === ELEMENT_NODE && element;
83
- } else {
84
- return jqLite(extractElementNode(element));
85
- }
86
- }
87
-
88
- function extractElementNode(element) {
89
- if (!element[0]) return element;
90
- for (var i = 0; i < element.length; i++) {
91
- var elm = element[i];
92
- if (elm.nodeType == ELEMENT_NODE) {
93
- return elm;
94
- }
95
- }
96
- }
97
-
98
- function $$addClass($$jqLite, element, className) {
99
- forEach(element, function(elm) {
100
- $$jqLite.addClass(elm, className);
101
- });
102
- }
103
-
104
- function $$removeClass($$jqLite, element, className) {
105
- forEach(element, function(elm) {
106
- $$jqLite.removeClass(elm, className);
107
- });
108
- }
109
-
110
- function applyAnimationClassesFactory($$jqLite) {
111
- return function(element, options) {
112
- if (options.addClass) {
113
- $$addClass($$jqLite, element, options.addClass);
114
- options.addClass = null;
115
- }
116
- if (options.removeClass) {
117
- $$removeClass($$jqLite, element, options.removeClass);
118
- element.removeClass(options.removeClass);
119
- options.removeClass = null;
120
- }
121
- }
122
- }
123
-
124
- function prepareAnimationOptions(options) {
125
- options = options || {};
126
- if (!options.$$prepared) {
127
- var domOperation = options.domOperation || noop;
128
- options.domOperation = function() {
129
- options.$$domOperationFired = true;
130
- domOperation();
131
- domOperation = noop;
132
- };
133
- options.$$prepared = true;
134
- }
135
- return options;
136
- }
137
-
138
- function applyAnimationStyles(element, options) {
139
- applyAnimationFromStyles(element, options);
140
- applyAnimationToStyles(element, options);
141
- }
142
-
143
- function applyAnimationFromStyles(element, options) {
144
- if (options.from) {
145
- element.css(options.from);
146
- options.from = null;
147
- }
148
- }
149
-
150
- function applyAnimationToStyles(element, options) {
151
- if (options.to) {
152
- element.css(options.to);
153
- options.to = null;
154
- }
155
- }
156
-
157
- function mergeAnimationOptions(element, target, newOptions) {
158
- var toAdd = (target.addClass || '') + ' ' + (newOptions.addClass || '');
159
- var toRemove = (target.removeClass || '') + ' ' + (newOptions.removeClass || '');
160
- var classes = resolveElementClasses(element.attr('class'), toAdd, toRemove);
161
-
162
- extend(target, newOptions);
163
-
164
- if (classes.addClass) {
165
- target.addClass = classes.addClass;
166
- } else {
167
- target.addClass = null;
168
- }
169
-
170
- if (classes.removeClass) {
171
- target.removeClass = classes.removeClass;
172
- } else {
173
- target.removeClass = null;
174
- }
175
-
176
- return target;
177
- }
178
-
179
- function resolveElementClasses(existing, toAdd, toRemove) {
180
- var ADD_CLASS = 1;
181
- var REMOVE_CLASS = -1;
182
-
183
- var flags = {};
184
- existing = splitClassesToLookup(existing);
185
-
186
- toAdd = splitClassesToLookup(toAdd);
187
- forEach(toAdd, function(value, key) {
188
- flags[key] = ADD_CLASS;
189
- });
190
-
191
- toRemove = splitClassesToLookup(toRemove);
192
- forEach(toRemove, function(value, key) {
193
- flags[key] = flags[key] === ADD_CLASS ? null : REMOVE_CLASS;
194
- });
195
-
196
- var classes = {
197
- addClass: '',
198
- removeClass: ''
199
- };
200
-
201
- forEach(flags, function(val, klass) {
202
- var prop, allow;
203
- if (val === ADD_CLASS) {
204
- prop = 'addClass';
205
- allow = !existing[klass];
206
- } else if (val === REMOVE_CLASS) {
207
- prop = 'removeClass';
208
- allow = existing[klass];
209
- }
210
- if (allow) {
211
- if (classes[prop].length) {
212
- classes[prop] += ' ';
213
- }
214
- classes[prop] += klass;
215
- }
216
- });
217
-
218
- function splitClassesToLookup(classes) {
219
- if (isString(classes)) {
220
- classes = classes.split(' ');
221
- }
222
-
223
- var obj = {};
224
- forEach(classes, function(klass) {
225
- // sometimes the split leaves empty string values
226
- // incase extra spaces were applied to the options
227
- if (klass.length) {
228
- obj[klass] = true;
229
- }
230
- });
231
- return obj;
232
- }
233
-
234
- return classes;
235
- }
236
-
237
- var $$AnimateChildrenDirective = [function() {
238
- return function(scope, element, attrs) {
239
- var val = attrs.ngAnimateChildren;
240
- if (angular.isString(val) && val.length === 0) { //empty attribute
241
- element.data(NG_ANIMATE_CHILDREN_DATA, true);
242
- } else {
243
- attrs.$observe('ngAnimateChildren', function(value) {
244
- value = value === 'on' || value === 'true';
245
- element.data(NG_ANIMATE_CHILDREN_DATA, value);
246
- });
247
- }
248
- };
249
- }];
250
-
251
- /**
252
- * @ngdoc service
253
- * @name $animateCss
254
- * @kind object
255
- *
256
- * @description
257
- * The `$animateCss` service is a useful utility to trigger customized CSS-based transitions/keyframes
258
- * from a JavaScript-based animation or directly from a directive. The purpose of `$animateCss` is NOT
259
- * to side-step how `$animate` and ngAnimate work, but the goal is to allow pre-existing animations or
260
- * directives to create more complex animations that can be purely driven using CSS code.
261
- *
262
- * Note that only browsers that support CSS transitions and/or keyframe animations are capable of
263
- * rendering animations triggered via `$animateCss` (bad news for IE9 and lower).
264
- *
265
- * ## Usage
266
- * Once again, `$animateCss` is designed to be used inside of a registered JavaScript animation that
267
- * is powered by ngAnimate. It is possible to use `$animateCss` directly inside of a directive, however,
268
- * any automatic control over cancelling animations and/or preventing animations from being run on
269
- * child elements will not be handled by Angular. For this to work as expected, please use `$animate` to
270
- * trigger the animation and then setup a JavaScript animation that injects `$animateCss` to trigger
271
- * the CSS animation.
272
- *
273
- * The example below shows how we can create a folding animation on an element using `ng-if`:
274
- *
275
- * ```html
276
- * <!-- notice the `fold-animation` CSS class -->
277
- * <div ng-if="onOff" class="fold-animation">
278
- * This element will go BOOM
279
- * </div>
280
- * <button ng-click="onOff=true">Fold In</button>
281
- * ```
282
- *
283
- * Now we create the **JavaScript animation** that will trigger the CSS transition:
284
- *
285
- * ```js
286
- * ngModule.animation('.fold-animation', ['$animateCss', function($animateCss) {
287
- * return {
288
- * enter: function(element, doneFn) {
289
- * var height = element[0].offsetHeight;
290
- * var animation = $animateCss(element, {
291
- * from: { height:'0px' },
292
- * to: { height:height + 'px' },
293
- * duration: 1 // one second
294
- * });
295
- *
296
- * // if no possible animation can be triggered due
297
- * // to the combination of options then `animation`
298
- * // will be returned as undefined
299
- * animation.start().done(doneFn);
300
- * }
301
- * }
302
- * }]);
303
- * ```
304
- *
305
- * ## More Advanced Uses
306
- *
307
- * `$animateCss` is the underlying code that ngAnimate uses to power **CSS-based animations** behind the scenes. Therefore CSS hooks
308
- * like `.ng-EVENT`, `.ng-EVENT-active`, `.ng-EVENT-stagger` are all features that can be triggered using `$animateCss` via JavaScript code.
309
- *
310
- * This also means that just about any combination of adding classes, removing classes, setting styles, dynamically setting a keyframe animation,
311
- * applying a hardcoded duration or delay value, changing the animation easing or applying a stagger animation are all options that work with
312
- * `$animateCss`. The service itself is smart enough to figure out the combination of options and examine the element styling properties in order
313
- * to provide a working animation that will run in CSS.
314
- *
315
- * The example below showcases a more advanced version of the `.fold-animation` from the example above:
316
- *
317
- * ```js
318
- * ngModule.animation('.fold-animation', ['$animateCss', function($animateCss) {
319
- * return {
320
- * enter: function(element, doneFn) {
321
- * var height = element[0].offsetHeight;
322
- * var animation = $animateCss(element, {
323
- * addClass: 'red large-text pulse-twice',
324
- * easing: 'ease-out',
325
- * from: { height:'0px' },
326
- * to: { height:height + 'px' },
327
- * duration: 1 // one second
328
- * });
329
- *
330
- * // if no possible animation can be triggered due
331
- * // to the combination of options then `animation`
332
- * // will be returned as undefined
333
- * animation.start().done(doneFn);
334
- * }
335
- * }
336
- * }]);
337
- * ```
338
- *
339
- * Since we're adding/removing CSS classes then the CSS transition will also pick those up:
340
- *
341
- * ```css
342
- * /&#42; since a hardcoded duration value of 1 was provided in the JavaScript animation code,
343
- * the CSS classes below will be transitioned despite them being defined as regular CSS classes &#42;/
344
- * .red { background:red; }
345
- * .large-text { font-size:20px; }
346
- *
347
- * /&#42; we can also use a keyframe animation and $animateCss will make it work alongside the transition &#42;/
348
- * .pulse-twice {
349
- * animation: 0.5s pulse linear 2;
350
- * -webkit-animation: 0.5s pulse linear 2;
351
- * }
352
- *
353
- * @keyframes pulse {
354
- * from { transform: scale(0.5); }
355
- * to { transform: scale(1.5); }
356
- * }
357
- *
358
- * @-webkit-keyframes pulse {
359
- * from { -webkit-transform: scale(0.5); }
360
- * to { -webkit-transform: scale(1.5); }
361
- * }
362
- * ```
363
- *
364
- * Given this complex combination of CSS classes, styles and options, `$animateCss` will figure everything out and make the animation happen.
365
- *
366
- * ## How the Options are handled
367
- *
368
- * `$animateCss` is very versatile and intelligent when it comes to figuring out what configurations to apply to the element to ensure the animation
369
- * 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
370
- * styles using the `from` and `to` properties.
371
- *
372
- * ```js
373
- * var animation = $animateCss(element, {
374
- * from: { background:'red' },
375
- * to: { background:'blue' }
376
- * });
377
- * ```
378
- *
379
- * ```css
380
- * .rotating-animation {
381
- * animation:0.5s rotate linear;
382
- * -webkit-animation:0.5s rotate linear;
383
- * }
384
- *
385
- * @keyframes rotate {
386
- * from { transform: rotate(0deg); }
387
- * to { transform: rotate(360deg); }
388
- * }
389
- *
390
- * @-webkit-keyframes rotate {
391
- * from { -webkit-transform: rotate(0deg); }
392
- * to { -webkit-transform: rotate(360deg); }
393
- * }
394
- * ```
395
- *
396
- * 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
397
- * going to be detected from what the keyframe styles on the CSS class are. In this event, `$animateCss` will automatically create an inline transition
398
- * 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
399
- * 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
400
- * and spread across the transition and keyframe animation.
401
- *
402
- * ## What is returned
403
- *
404
- * `$animateCss` works in two stages: a preparation phase and an animation phase. Therefore when `$animateCss` is first called it will NOT actually
405
- * 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
406
- * added and removed on the element). Once `$animateCss` is called it will return an object with the following properties:
407
- *
408
- * ```js
409
- * var animation = $animateCss(element, { ... });
410
- * ```
411
- *
412
- * Now what do the contents of our `animation` variable look like:
413
- *
414
- * ```js
415
- * {
416
- * // starts the animation
417
- * start: Function,
418
- *
419
- * // ends (aborts) the animation
420
- * end: Function,
421
- *
422
- * // the total number of seconds that the animation will run for
423
- * duration: Number,
424
- *
425
- * // the total number of seconds that the animation will delay for before starting
426
- * delay: Number,
427
- *
428
- * // whether or not transitions were detected and will therefore be used for the animation
429
- * transitions: Boolean,
430
- *
431
- * // whether or not keyframe animations were detected and will therefore be used for the animation
432
- * keyframes: Boolean
433
- * }
434
- * ```
435
- *
436
- * 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.
437
- * 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 stlyes may have been
438
- * applied to the element during the preparation phase). Note that all other properties such as duration, delay, transitions and keyframes are just properties
439
- * and that changing them will not reconfigure the parameters of the animation.
440
- *
441
- * By calling `animation.start()` we do get back a promise, however, due to the nature of animations we may not want to tap into the default behaviour of
442
- * animations (since they cause a digest to occur which may slow down the animation performance-wise). Therefore instead of calling `then` to capture when
443
- * the animation ends be sure to call `done(callback)` (this is the recommended way to use `$animateCss` within JavaScript-animations).
444
- *
445
- * The example below should put this into perspective:
446
- *
447
- * ```js
448
- * var animation = $animateCss(element, { ... });
449
- *
450
- * // remember that if there is no CSS animation detected on the element
451
- * // then the value returned from $animateCss will be null
452
- * if (animation) {
453
- * animation.start().done(function() {
454
- * // yaay the animation is over
455
- * doneCallback();
456
- * });
457
- * } else {
458
- * doneCallback();
459
- * }
460
- * ```
461
- *
462
- * @param {DOMElement} element the element that will be animated
463
- * @param {object} options the animation-related options that will be applied during the animation
464
- *
465
- * * `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
466
- * 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.)
467
- * * `easing` - The CSS easing value that will be applied to the transition or keyframe animation (or both).
468
- * * `transition` - The raw CSS transition style that will be used (e.g. `1s linear all`).
469
- * * `keyframe` - The raw CSS keyframe animation style that will be used (e.g. `1s my_animation linear`).
470
- * * `from` - The starting CSS styles (a key/value object) that will be applied at the start of the animation.
471
- * * `to` - The ending CSS styles (a key/value object) that will be applied across the animation via a CSS transition.
472
- * * `addClass` - A space separated list of CSS classes that will be added to the element and spread across the animation.
473
- * * `removeClass` - A space separated list of CSS classes that will be removed from the element and spread across the animation.
474
- * * `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`
475
- * is provided then the animation will be skipped entirely.
476
- * * `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
477
- * 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
478
- * 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
479
- * CSS delay value.
480
- * * `stagger` - A numeric time value representing the delay between successively animated elements
481
- * ({@link ngAnimate#css-staggering-animations Click here to learn how CSS-based staggering works in ngAnimate.})
482
- * * `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
483
- * `stagger` option value of `0.1` is used then there will be a stagger delay of `600ms`)
484
- *
485
- * @return {null|object} an object with a start method and details about the animation. If no animation is detected then a value of `null` will be returned.
486
- *
487
- * * `start` - The method to start the animation. This will return a `Promise` when called.
488
- * * `end` - This method will cancel the animation and remove all applied CSS classes and styles.
489
- */
490
-
491
- // Detect proper transitionend/animationend event names.
492
- var CSS_PREFIX = '', TRANSITION_PROP, TRANSITIONEND_EVENT, ANIMATION_PROP, ANIMATIONEND_EVENT;
493
-
494
- // If unprefixed events are not supported but webkit-prefixed are, use the latter.
495
- // Otherwise, just use W3C names, browsers not supporting them at all will just ignore them.
496
- // Note: Chrome implements `window.onwebkitanimationend` and doesn't implement `window.onanimationend`
497
- // but at the same time dispatches the `animationend` event and not `webkitAnimationEnd`.
498
- // Register both events in case `window.onanimationend` is not supported because of that,
499
- // do the same for `transitionend` as Safari is likely to exhibit similar behavior.
500
- // Also, the only modern browser that uses vendor prefixes for transitions/keyframes is webkit
501
- // therefore there is no reason to test anymore for other vendor prefixes:
502
- // http://caniuse.com/#search=transition
503
- if (window.ontransitionend === undefined && window.onwebkittransitionend !== undefined) {
504
- CSS_PREFIX = '-webkit-';
505
- TRANSITION_PROP = 'WebkitTransition';
506
- TRANSITIONEND_EVENT = 'webkitTransitionEnd transitionend';
507
- } else {
508
- TRANSITION_PROP = 'transition';
509
- TRANSITIONEND_EVENT = 'transitionend';
510
- }
511
-
512
- if (window.onanimationend === undefined && window.onwebkitanimationend !== undefined) {
513
- CSS_PREFIX = '-webkit-';
514
- ANIMATION_PROP = 'WebkitAnimation';
515
- ANIMATIONEND_EVENT = 'webkitAnimationEnd animationend';
516
- } else {
517
- ANIMATION_PROP = 'animation';
518
- ANIMATIONEND_EVENT = 'animationend';
519
- }
520
-
521
- var DURATION_KEY = 'Duration';
522
- var PROPERTY_KEY = 'Property';
523
- var DELAY_KEY = 'Delay';
524
- var TIMING_KEY = 'TimingFunction';
525
- var ANIMATION_ITERATION_COUNT_KEY = 'IterationCount';
526
- var ANIMATION_PLAYSTATE_KEY = 'PlayState';
527
- var ELAPSED_TIME_MAX_DECIMAL_PLACES = 3;
528
- var CLOSING_TIME_BUFFER = 1.5;
529
- var ONE_SECOND = 1000;
530
- var BASE_TEN = 10;
531
-
532
- var SAFE_FAST_FORWARD_DURATION_VALUE = 9999;
533
-
534
- var ANIMATION_DELAY_PROP = ANIMATION_PROP + DELAY_KEY;
535
- var ANIMATION_DURATION_PROP = ANIMATION_PROP + DURATION_KEY;
536
-
537
- var TRANSITION_DELAY_PROP = TRANSITION_PROP + DELAY_KEY;
538
- var TRANSITION_DURATION_PROP = TRANSITION_PROP + DURATION_KEY;
539
-
540
- var DETECT_CSS_PROPERTIES = {
541
- transitionDuration: TRANSITION_DURATION_PROP,
542
- transitionDelay: TRANSITION_DELAY_PROP,
543
- transitionProperty: TRANSITION_PROP + PROPERTY_KEY,
544
- animationDuration: ANIMATION_DURATION_PROP,
545
- animationDelay: ANIMATION_DELAY_PROP,
546
- animationIterationCount: ANIMATION_PROP + ANIMATION_ITERATION_COUNT_KEY
547
- };
548
-
549
- var DETECT_STAGGER_CSS_PROPERTIES = {
550
- transitionDuration: TRANSITION_DURATION_PROP,
551
- transitionDelay: TRANSITION_DELAY_PROP,
552
- animationDuration: ANIMATION_DURATION_PROP,
553
- animationDelay: ANIMATION_DELAY_PROP
554
- };
555
-
556
- function computeCssStyles($window, element, properties) {
557
- var styles = Object.create(null);
558
- var detectedStyles = $window.getComputedStyle(element) || {};
559
- forEach(properties, function(formalStyleName, actualStyleName) {
560
- var val = detectedStyles[formalStyleName];
561
- if (val) {
562
- var c = val.charAt(0);
563
-
564
- // only numerical-based values have a negative sign or digit as the first value
565
- if (c === '-' || c === '+' || c >= 0) {
566
- val = parseMaxTime(val);
567
- }
568
-
569
- // by setting this to null in the event that the delay is not set or is set directly as 0
570
- // then we can still allow for zegative values to be used later on and not mistake this
571
- // value for being greater than any other negative value.
572
- if (val === 0) {
573
- val = null;
574
- }
575
- styles[actualStyleName] = val;
576
- }
577
- });
578
-
579
- return styles;
580
- }
581
-
582
- function parseMaxTime(str) {
583
- var maxValue = 0;
584
- var values = str.split(/\s*,\s*/);
585
- forEach(values, function(value) {
586
- // it's always safe to consider only second values and omit `ms` values since
587
- // getComputedStyle will always handle the conversion for us
588
- if (value.charAt(value.length - 1) == 's') {
589
- value = value.substring(0, value.length - 1);
590
- }
591
- value = parseFloat(value) || 0;
592
- maxValue = maxValue ? Math.max(value, maxValue) : value;
593
- });
594
- return maxValue;
595
- }
596
-
597
- function truthyTimingValue(val) {
598
- return val === 0 || val != null;
599
- }
600
-
601
- function getCssTransitionDurationStyle(duration, applyOnlyDuration) {
602
- var style = TRANSITION_PROP;
603
- var value = duration + 's';
604
- if (applyOnlyDuration) {
605
- style += DURATION_KEY;
606
- } else {
607
- value += ' linear all';
608
- }
609
- return [style, value];
610
- }
611
-
612
- function getCssKeyframeDurationStyle(duration) {
613
- return [ANIMATION_DURATION_PROP, duration + 's'];
614
- }
615
-
616
- function getCssDelayStyle(delay, isKeyframeAnimation) {
617
- var prop = isKeyframeAnimation ? ANIMATION_DELAY_PROP : TRANSITION_DELAY_PROP;
618
- return [prop, delay + 's'];
619
- }
620
-
621
- function blockTransitions(node, duration) {
622
- // we use a negative delay value since it performs blocking
623
- // yet it doesn't kill any existing transitions running on the
624
- // same element which makes this safe for class-based animations
625
- var value = duration ? '-' + duration + 's' : '';
626
- applyInlineStyle(node, [TRANSITION_DELAY_PROP, value]);
627
- return [TRANSITION_DELAY_PROP, value];
628
- }
629
-
630
- function blockKeyframeAnimations(node, applyBlock) {
631
- var value = applyBlock ? 'paused' : '';
632
- var key = ANIMATION_PROP + ANIMATION_PLAYSTATE_KEY;
633
- applyInlineStyle(node, [key, value]);
634
- return [key, value];
635
- }
636
-
637
- function applyInlineStyle(node, styleTuple) {
638
- var prop = styleTuple[0];
639
- var value = styleTuple[1];
640
- node.style[prop] = value;
641
- }
642
-
643
- function createLocalCacheLookup() {
644
- var cache = Object.create(null);
645
- return {
646
- flush: function() {
647
- cache = Object.create(null);
648
- },
649
-
650
- count: function(key) {
651
- var entry = cache[key];
652
- return entry ? entry.total : 0;
653
- },
654
-
655
- get: function(key) {
656
- var entry = cache[key];
657
- return entry && entry.value;
658
- },
659
-
660
- put: function(key, value) {
661
- if (!cache[key]) {
662
- cache[key] = { total: 1, value: value };
663
- } else {
664
- cache[key].total++;
665
- }
666
- }
667
- };
668
- }
669
-
670
- var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
671
- var gcsLookup = createLocalCacheLookup();
672
- var gcsStaggerLookup = createLocalCacheLookup();
673
-
674
- this.$get = ['$window', '$$jqLite', '$$AnimateRunner', '$timeout',
675
- '$document', '$sniffer', '$$rAF',
676
- function($window, $$jqLite, $$AnimateRunner, $timeout,
677
- $document, $sniffer, $$rAF) {
678
-
679
- var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
680
-
681
- var parentCounter = 0;
682
- function gcsHashFn(node, extraClasses) {
683
- var KEY = "$$ngAnimateParentKey";
684
- var parentNode = node.parentNode;
685
- var parentID = parentNode[KEY] || (parentNode[KEY] = ++parentCounter);
686
- return parentID + '-' + node.getAttribute('class') + '-' + extraClasses;
687
- }
688
-
689
- function computeCachedCssStyles(node, className, cacheKey, properties) {
690
- var timings = gcsLookup.get(cacheKey);
691
-
692
- if (!timings) {
693
- timings = computeCssStyles($window, node, properties);
694
- if (timings.animationIterationCount === 'infinite') {
695
- timings.animationIterationCount = 1;
696
- }
697
- }
698
-
699
- // we keep putting this in multiple times even though the value and the cacheKey are the same
700
- // because we're keeping an interal tally of how many duplicate animations are detected.
701
- gcsLookup.put(cacheKey, timings);
702
- return timings;
703
- }
704
-
705
- function computeCachedCssStaggerStyles(node, className, cacheKey, properties) {
706
- var stagger;
707
-
708
- // if we have one or more existing matches of matching elements
709
- // containing the same parent + CSS styles (which is how cacheKey works)
710
- // then staggering is possible
711
- if (gcsLookup.count(cacheKey) > 0) {
712
- stagger = gcsStaggerLookup.get(cacheKey);
713
-
714
- if (!stagger) {
715
- var staggerClassName = pendClasses(className, '-stagger');
716
-
717
- $$jqLite.addClass(node, staggerClassName);
718
-
719
- stagger = computeCssStyles($window, node, properties);
720
-
721
- // force the conversion of a null value to zero incase not set
722
- stagger.animationDuration = Math.max(stagger.animationDuration, 0);
723
- stagger.transitionDuration = Math.max(stagger.transitionDuration, 0);
724
-
725
- $$jqLite.removeClass(node, staggerClassName);
726
-
727
- gcsStaggerLookup.put(cacheKey, stagger);
728
- }
729
- }
730
-
731
- return stagger || {};
732
- }
733
-
734
- var bod = $document[0].body;
735
- var cancelLastRAFRequest;
736
- var rafWaitQueue = [];
737
- function waitUntilQuiet(callback) {
738
- if (cancelLastRAFRequest) {
739
- cancelLastRAFRequest(); //cancels the request
740
- }
741
- rafWaitQueue.push(callback);
742
- cancelLastRAFRequest = $$rAF(function() {
743
- cancelLastRAFRequest = null;
744
- gcsLookup.flush();
745
- gcsStaggerLookup.flush();
746
-
747
- //the line below will force the browser to perform a repaint so
748
- //that all the animated elements within the animation frame will
749
- //be properly updated and drawn on screen. This is required to
750
- //ensure that the preparation animation is properly flushed so that
751
- //the active state picks up from there. DO NOT REMOVE THIS LINE.
752
- //DO NOT OPTIMIZE THIS LINE. THE MINIFIER WILL REMOVE IT OTHERWISE WHICH
753
- //WILL RESULT IN AN UNPREDICTABLE BUG THAT IS VERY HARD TO TRACK DOWN AND
754
- //WILL TAKE YEARS AWAY FROM YOUR LIFE.
755
- var width = bod.offsetWidth + 1;
756
- forEach(rafWaitQueue, function(cb) {
757
- cb(width);
758
- });
759
- rafWaitQueue.length = 0;
760
- });
761
- }
762
-
763
- return init;
764
-
765
- function computeTimings(node, className, cacheKey) {
766
- var timings = computeCachedCssStyles(node, className, cacheKey, DETECT_CSS_PROPERTIES);
767
- var aD = timings.animationDelay;
768
- var tD = timings.transitionDelay;
769
- timings.maxDelay = aD && tD
770
- ? Math.max(aD, tD)
771
- : (aD || tD);
772
- timings.maxDuration = Math.max(
773
- timings.animationDuration * timings.animationIterationCount,
774
- timings.transitionDuration);
775
-
776
- return timings;
777
- }
778
-
779
- function init(element, options) {
780
- var node = element[0];
781
- options = prepareAnimationOptions(options);
782
-
783
- var temporaryStyles = [];
784
- var classes = element.attr('class');
785
- var styles = packageStyles(options);
786
- var animationClosed;
787
- var animationPaused;
788
- var animationCompleted;
789
- var runner;
790
- var runnerHost;
791
- var maxDelay;
792
- var maxDelayTime;
793
- var maxDuration;
794
- var maxDurationTime;
795
-
796
- if (options.duration === 0 || (!$sniffer.animations && !$sniffer.transitions)) {
797
- close();
798
- return;
799
- }
800
-
801
- var method = options.event && isArray(options.event)
802
- ? options.event.join(' ')
803
- : options.event;
804
-
805
- var isStructural = method && options.structural;
806
- var structuralClassName = '';
807
- var addRemoveClassName = '';
808
-
809
- if (isStructural) {
810
- structuralClassName = pendClasses(method, 'ng-', true);
811
- } else if (method) {
812
- structuralClassName = method;
813
- }
814
-
815
- if (options.addClass) {
816
- addRemoveClassName += pendClasses(options.addClass, '-add');
817
- }
818
-
819
- if (options.removeClass) {
820
- if (addRemoveClassName.length) {
821
- addRemoveClassName += ' ';
822
- }
823
- addRemoveClassName += pendClasses(options.removeClass, '-remove');
824
- }
825
-
826
- var setupClasses = [structuralClassName, addRemoveClassName].join(' ').trim();
827
- var fullClassName = classes + ' ' + setupClasses;
828
- var activeClasses = pendClasses(setupClasses, '-active');
829
- var hasToStyles = styles.to && Object.keys(styles.to).length > 0;
830
-
831
- // there is no way we can trigger an animation since no styles and
832
- // no classes are being applied which would then trigger a transition
833
- if (!hasToStyles && !setupClasses) {
834
- close();
835
- return false;
836
- }
837
-
838
- var cacheKey, stagger;
839
- if (options.stagger > 0) {
840
- var staggerVal = parseFloat(options.stagger);
841
- stagger = {
842
- transitionDelay: staggerVal,
843
- animationDelay: staggerVal,
844
- transitionDuration: 0,
845
- animationDuration: 0
846
- };
847
- } else {
848
- cacheKey = gcsHashFn(node, fullClassName);
849
- stagger = computeCachedCssStaggerStyles(node, setupClasses, cacheKey, DETECT_STAGGER_CSS_PROPERTIES);
850
- }
851
-
852
- $$jqLite.addClass(element, setupClasses);
853
-
854
- var applyOnlyDuration;
855
-
856
- if (options.transitionStyle) {
857
- var transitionStyle = [TRANSITION_PROP, options.transitionStyle];
858
- applyInlineStyle(node, transitionStyle);
859
- temporaryStyles.push(transitionStyle);
860
- }
861
-
862
- if (options.duration >= 0) {
863
- applyOnlyDuration = node.style[TRANSITION_PROP].length > 0;
864
- var durationStyle = getCssTransitionDurationStyle(options.duration, applyOnlyDuration);
865
-
866
- // we set the duration so that it will be picked up by getComputedStyle later
867
- applyInlineStyle(node, durationStyle);
868
- temporaryStyles.push(durationStyle);
869
- }
870
-
871
- if (options.keyframeStyle) {
872
- var keyframeStyle = [ANIMATION_PROP, options.keyframeStyle];
873
- applyInlineStyle(node, keyframeStyle);
874
- temporaryStyles.push(keyframeStyle);
875
- }
876
-
877
- var itemIndex = stagger
878
- ? options.staggerIndex >= 0
879
- ? options.staggerIndex
880
- : gcsLookup.count(cacheKey)
881
- : 0;
882
-
883
- var isFirst = itemIndex === 0;
884
-
885
- // this is a pre-emptive way of forcing the setup classes to be added and applied INSTANTLY
886
- // without causing any combination of transitions to kick in. By adding a negative delay value
887
- // it forces the setup class' transition to end immediately. We later then remove the negative
888
- // transition delay to allow for the transition to naturally do it's thing. The beauty here is
889
- // that if there is no transition defined then nothing will happen and this will also allow
890
- // other transitions to be stacked on top of each other without any chopping them out.
891
- if (isFirst) {
892
- blockTransitions(node, SAFE_FAST_FORWARD_DURATION_VALUE);
893
- }
894
-
895
- var timings = computeTimings(node, fullClassName, cacheKey);
896
- var relativeDelay = timings.maxDelay;
897
- maxDelay = Math.max(relativeDelay, 0);
898
- maxDuration = timings.maxDuration;
899
-
900
- var flags = {};
901
- flags.hasTransitions = timings.transitionDuration > 0;
902
- flags.hasAnimations = timings.animationDuration > 0;
903
- flags.hasTransitionAll = flags.hasTransitions && timings.transitionProperty == 'all';
904
- flags.applyTransitionDuration = hasToStyles && (
905
- (flags.hasTransitions && !flags.hasTransitionAll)
906
- || (flags.hasAnimations && !flags.hasTransitions));
907
- flags.applyAnimationDuration = options.duration && flags.hasAnimations;
908
- flags.applyTransitionDelay = truthyTimingValue(options.delay) && (flags.applyTransitionDuration || flags.hasTransitions);
909
- flags.applyAnimationDelay = truthyTimingValue(options.delay) && flags.hasAnimations;
910
- flags.recalculateTimingStyles = addRemoveClassName.length > 0;
911
-
912
- if (flags.applyTransitionDuration || flags.applyAnimationDuration) {
913
- maxDuration = options.duration ? parseFloat(options.duration) : maxDuration;
914
-
915
- if (flags.applyTransitionDuration) {
916
- flags.hasTransitions = true;
917
- timings.transitionDuration = maxDuration;
918
- applyOnlyDuration = node.style[TRANSITION_PROP + PROPERTY_KEY].length > 0;
919
- temporaryStyles.push(getCssTransitionDurationStyle(maxDuration, applyOnlyDuration));
920
- }
921
-
922
- if (flags.applyAnimationDuration) {
923
- flags.hasAnimations = true;
924
- timings.animationDuration = maxDuration;
925
- temporaryStyles.push(getCssKeyframeDurationStyle(maxDuration));
926
- }
927
- }
928
-
929
- flags.transitionClassBlock = timings.transitionProperty === 'none' &&
930
- timings.transitionDuration === 0;
931
-
932
- // there may be a situation where a structural animation is combined together
933
- // with CSS classes that need to resolve before the animation is computed.
934
- // However this means that there is no explicit CSS code to block the animation
935
- // from happening (by setting 0s none in the class name). If this is the case
936
- // we need to apply the classes before the first rAF so we know to continue if
937
- // there actually is a detected transition or keyframe animation
938
- var applyClassesEarly = maxDuration === 0
939
- && isStructural
940
- && addRemoveClassName.length > 0
941
- && !flags.transitionClassBlock;
942
-
943
- // this is an early check to avoid having to do another call to getComputedStyle
944
- // call which is expensive. GCS calls are cached to speed things up.
945
- if (!applyClassesEarly && maxDuration === 0 && !flags.recalculateTimingStyles) {
946
- close();
947
- return false;
948
- }
949
-
950
- if (applyClassesEarly) {
951
- applyAnimationClasses(element, options);
952
-
953
- // no need to calculate this anymore
954
- flags.recalculateTimingStyles = false;
955
-
956
- fullClassName = node.className + ' ' + setupClasses;
957
- cacheKey = gcsHashFn(node, fullClassName);
958
-
959
- timings = computeTimings(node, fullClassName, cacheKey);
960
- relativeDelay = timings.maxDelay;
961
- maxDelay = Math.max(relativeDelay, 0);
962
- maxDuration = timings.maxDuration;
963
- }
964
-
965
- if (maxDuration === 0 && !flags.recalculateTimingStyles) {
966
- close();
967
- return false;
968
- }
969
-
970
- // we need to recalculate the delay value since we used a pre-emptive negative
971
- // delay value and the delay value is required for the final event checking. This
972
- // property will ensure that this will happen after the RAF phase has passed.
973
- if (timings.transitionDuration > 0) {
974
- flags.recalculateTimingStyles = flags.recalculateTimingStyles || isFirst;
975
- }
976
-
977
- maxDelayTime = maxDelay * ONE_SECOND;
978
- maxDurationTime = maxDuration * ONE_SECOND;
979
- if (!options.skipBlocking) {
980
- flags.blockTransition = timings.transitionDuration > 0;
981
- flags.blockKeyframeAnimation = timings.animationDuration > 0 &&
982
- stagger.animationDelay > 0 &&
983
- stagger.animationDuration === 0;
984
- }
985
-
986
- if (flags.blockTransition) {
987
- applyAnimationFromStyles(element, options);
988
- } else {
989
- blockTransitions(node, false);
990
- }
991
-
992
- applyBlocking(maxDuration);
993
-
994
- // TODO(matsko): for 1.5 change this code to have an animator object for better debugging
995
- return {
996
- end: endFn,
997
- start: function() {
998
- if (animationClosed) return;
999
-
1000
- runnerHost = {
1001
- end: endFn,
1002
- cancel: cancelFn,
1003
- resume: null, //this will be set during the start() phase
1004
- pause: null
1005
- };
1006
-
1007
- runner = new $$AnimateRunner(runnerHost);
1008
-
1009
- waitUntilQuiet(start);
1010
-
1011
- // we don't have access to pause/resume the animation
1012
- // since it hasn't run yet. AnimateRunner will therefore
1013
- // set noop functions for resume and pause and they will
1014
- // later be overridden once the animation is triggered
1015
- return runner;
1016
- }
1017
- };
1018
-
1019
- function endFn() {
1020
- close();
1021
- }
1022
-
1023
- function cancelFn() {
1024
- close(true);
1025
- }
1026
-
1027
- function close(rejected) { // jshint ignore:line
1028
- // if the promise has been called already then we shouldn't close
1029
- // the animation again
1030
- if (animationClosed || (animationCompleted && animationPaused)) return;
1031
- animationClosed = true;
1032
- animationPaused = false;
1033
-
1034
- $$jqLite.removeClass(element, setupClasses);
1035
- $$jqLite.removeClass(element, activeClasses);
1036
-
1037
- blockKeyframeAnimations(node, false);
1038
- blockTransitions(node, false);
1039
-
1040
- forEach(temporaryStyles, function(entry) {
1041
- // There is only one way to remove inline style properties entirely from elements.
1042
- // By using `removeProperty` this works, but we need to convert camel-cased CSS
1043
- // styles down to hyphenated values.
1044
- node.style[entry[0]] = '';
1045
- });
1046
-
1047
- applyAnimationClasses(element, options);
1048
- applyAnimationStyles(element, options);
1049
-
1050
- // the reason why we have this option is to allow a synchronous closing callback
1051
- // that is fired as SOON as the animation ends (when the CSS is removed) or if
1052
- // the animation never takes off at all. A good example is a leave animation since
1053
- // the element must be removed just after the animation is over or else the element
1054
- // will appear on screen for one animation frame causing an overbearing flicker.
1055
- if (options.onDone) {
1056
- options.onDone();
1057
- }
1058
-
1059
- // if the preparation function fails then the promise is not setup
1060
- if (runner) {
1061
- runner.complete(!rejected);
1062
- }
1063
- }
1064
-
1065
- function applyBlocking(duration) {
1066
- if (flags.blockTransition) {
1067
- blockTransitions(node, duration);
1068
- }
1069
-
1070
- if (flags.blockKeyframeAnimation) {
1071
- blockKeyframeAnimations(node, !!duration);
1072
- }
1073
- }
1074
-
1075
- function start() {
1076
- if (animationClosed) return;
1077
-
1078
- var startTime, events = [];
1079
-
1080
- // even though we only pause keyframe animations here the pause flag
1081
- // will still happen when transitions are used. Only the transition will
1082
- // not be paused since that is not possible. If the animation ends when
1083
- // paused then it will not complete until unpaused or cancelled.
1084
- var playPause = function(playAnimation) {
1085
- if (!animationCompleted) {
1086
- animationPaused = !playAnimation;
1087
- if (timings.animationDuration) {
1088
- var value = blockKeyframeAnimations(node, animationPaused);
1089
- animationPaused
1090
- ? temporaryStyles.push(value)
1091
- : removeFromArray(temporaryStyles, value);
1092
- }
1093
- } else if (animationPaused && playAnimation) {
1094
- animationPaused = false;
1095
- close();
1096
- }
1097
- };
1098
-
1099
- // checking the stagger duration prevents an accidently cascade of the CSS delay style
1100
- // being inherited from the parent. If the transition duration is zero then we can safely
1101
- // rely that the delay value is an intential stagger delay style.
1102
- var maxStagger = itemIndex > 0
1103
- && ((timings.transitionDuration && stagger.transitionDuration === 0) ||
1104
- (timings.animationDuration && stagger.animationDuration === 0))
1105
- && Math.max(stagger.animationDelay, stagger.transitionDelay);
1106
- if (maxStagger) {
1107
- $timeout(triggerAnimationStart,
1108
- Math.floor(maxStagger * itemIndex * ONE_SECOND),
1109
- false);
1110
- } else {
1111
- triggerAnimationStart();
1112
- }
1113
-
1114
- // this will decorate the existing promise runner with pause/resume methods
1115
- runnerHost.resume = function() {
1116
- playPause(true);
1117
- };
1118
-
1119
- runnerHost.pause = function() {
1120
- playPause(false);
1121
- };
1122
-
1123
- function triggerAnimationStart() {
1124
- // just incase a stagger animation kicks in when the animation
1125
- // itself was cancelled entirely
1126
- if (animationClosed) return;
1127
-
1128
- applyBlocking(false);
1129
-
1130
- forEach(temporaryStyles, function(entry) {
1131
- var key = entry[0];
1132
- var value = entry[1];
1133
- node.style[key] = value;
1134
- });
1135
-
1136
- applyAnimationClasses(element, options);
1137
- $$jqLite.addClass(element, activeClasses);
1138
-
1139
- if (flags.recalculateTimingStyles) {
1140
- fullClassName = node.className + ' ' + setupClasses;
1141
- cacheKey = gcsHashFn(node, fullClassName);
1142
-
1143
- timings = computeTimings(node, fullClassName, cacheKey);
1144
- relativeDelay = timings.maxDelay;
1145
- maxDelay = Math.max(relativeDelay, 0);
1146
- maxDuration = timings.maxDuration;
1147
-
1148
- if (maxDuration === 0) {
1149
- close();
1150
- return;
1151
- }
1152
-
1153
- flags.hasTransitions = timings.transitionDuration > 0;
1154
- flags.hasAnimations = timings.animationDuration > 0;
1155
- }
1156
-
1157
- if (flags.applyTransitionDelay || flags.applyAnimationDelay) {
1158
- relativeDelay = typeof options.delay !== "boolean" && truthyTimingValue(options.delay)
1159
- ? parseFloat(options.delay)
1160
- : relativeDelay;
1161
-
1162
- maxDelay = Math.max(relativeDelay, 0);
1163
-
1164
- var delayStyle;
1165
- if (flags.applyTransitionDelay) {
1166
- timings.transitionDelay = relativeDelay;
1167
- delayStyle = getCssDelayStyle(relativeDelay);
1168
- temporaryStyles.push(delayStyle);
1169
- node.style[delayStyle[0]] = delayStyle[1];
1170
- }
1171
-
1172
- if (flags.applyAnimationDelay) {
1173
- timings.animationDelay = relativeDelay;
1174
- delayStyle = getCssDelayStyle(relativeDelay, true);
1175
- temporaryStyles.push(delayStyle);
1176
- node.style[delayStyle[0]] = delayStyle[1];
1177
- }
1178
- }
1179
-
1180
- maxDelayTime = maxDelay * ONE_SECOND;
1181
- maxDurationTime = maxDuration * ONE_SECOND;
1182
-
1183
- if (options.easing) {
1184
- var easeProp, easeVal = options.easing;
1185
- if (flags.hasTransitions) {
1186
- easeProp = TRANSITION_PROP + TIMING_KEY;
1187
- temporaryStyles.push([easeProp, easeVal]);
1188
- node.style[easeProp] = easeVal;
1189
- }
1190
- if (flags.hasAnimations) {
1191
- easeProp = ANIMATION_PROP + TIMING_KEY;
1192
- temporaryStyles.push([easeProp, easeVal]);
1193
- node.style[easeProp] = easeVal;
1194
- }
1195
- }
1196
-
1197
- if (timings.transitionDuration) {
1198
- events.push(TRANSITIONEND_EVENT);
1199
- }
1200
-
1201
- if (timings.animationDuration) {
1202
- events.push(ANIMATIONEND_EVENT);
1203
- }
1204
-
1205
- startTime = Date.now();
1206
- element.on(events.join(' '), onAnimationProgress);
1207
- $timeout(onAnimationExpired, maxDelayTime + CLOSING_TIME_BUFFER * maxDurationTime);
1208
-
1209
- applyAnimationToStyles(element, options);
1210
- }
1211
-
1212
- function onAnimationExpired() {
1213
- // although an expired animation is a failed animation, getting to
1214
- // this outcome is very easy if the CSS code screws up. Therefore we
1215
- // should still continue normally as if the animation completed correctly.
1216
- close();
1217
- }
1218
-
1219
- function onAnimationProgress(event) {
1220
- event.stopPropagation();
1221
- var ev = event.originalEvent || event;
1222
- var timeStamp = ev.$manualTimeStamp || ev.timeStamp || Date.now();
1223
-
1224
- /* Firefox (or possibly just Gecko) likes to not round values up
1225
- * when a ms measurement is used for the animation */
1226
- var elapsedTime = parseFloat(ev.elapsedTime.toFixed(ELAPSED_TIME_MAX_DECIMAL_PLACES));
1227
-
1228
- /* $manualTimeStamp is a mocked timeStamp value which is set
1229
- * within browserTrigger(). This is only here so that tests can
1230
- * mock animations properly. Real events fallback to event.timeStamp,
1231
- * or, if they don't, then a timeStamp is automatically created for them.
1232
- * We're checking to see if the timeStamp surpasses the expected delay,
1233
- * but we're using elapsedTime instead of the timeStamp on the 2nd
1234
- * pre-condition since animations sometimes close off early */
1235
- if (Math.max(timeStamp - startTime, 0) >= maxDelayTime && elapsedTime >= maxDuration) {
1236
- // we set this flag to ensure that if the transition is paused then, when resumed,
1237
- // the animation will automatically close itself since transitions cannot be paused.
1238
- animationCompleted = true;
1239
- close();
1240
- }
1241
- }
1242
- }
1243
- }
1244
- }];
1245
- }];
1246
-
1247
- var $$AnimateCssDriverProvider = ['$$animationProvider', function($$animationProvider) {
1248
- $$animationProvider.drivers.push('$$animateCssDriver');
1249
-
1250
- var NG_ANIMATE_SHIM_CLASS_NAME = 'ng-animate-shim';
1251
- var NG_ANIMATE_ANCHOR_CLASS_NAME = 'ng-animate-anchor';
1252
- var NG_ANIMATE_ANCHOR_SUFFIX = '-anchor';
1253
-
1254
- var NG_OUT_ANCHOR_CLASS_NAME = 'ng-anchor-out';
1255
- var NG_IN_ANCHOR_CLASS_NAME = 'ng-anchor-in';
1256
-
1257
- this.$get = ['$animateCss', '$rootScope', '$$AnimateRunner', '$rootElement', '$document', '$sniffer',
1258
- function($animateCss, $rootScope, $$AnimateRunner, $rootElement, $document, $sniffer) {
1259
-
1260
- // only browsers that support these properties can render animations
1261
- if (!$sniffer.animations && !$sniffer.transitions) return noop;
1262
-
1263
- var bodyNode = $document[0].body;
1264
- var rootNode = $rootElement[0];
1265
-
1266
- var rootBodyElement = jqLite(bodyNode.parentNode === rootNode ? bodyNode : rootNode);
1267
-
1268
- return function initDriverFn(animationDetails) {
1269
- return animationDetails.from && animationDetails.to
1270
- ? prepareFromToAnchorAnimation(animationDetails.from,
1271
- animationDetails.to,
1272
- animationDetails.classes,
1273
- animationDetails.anchors)
1274
- : prepareRegularAnimation(animationDetails);
1275
- };
1276
-
1277
- function filterCssClasses(classes) {
1278
- //remove all the `ng-` stuff
1279
- return classes.replace(/\bng-\S+\b/g, '');
1280
- }
1281
-
1282
- function getUniqueValues(a, b) {
1283
- if (isString(a)) a = a.split(' ');
1284
- if (isString(b)) b = b.split(' ');
1285
- return a.filter(function(val) {
1286
- return b.indexOf(val) === -1;
1287
- }).join(' ');
1288
- }
1289
-
1290
- function prepareAnchoredAnimation(classes, outAnchor, inAnchor) {
1291
- var clone = jqLite(outAnchor[0].cloneNode(true));
1292
- var startingClasses = filterCssClasses(clone.attr('class') || '');
1293
- var anchorClasses = pendClasses(classes, NG_ANIMATE_ANCHOR_SUFFIX);
1294
-
1295
- outAnchor.addClass(NG_ANIMATE_SHIM_CLASS_NAME);
1296
- inAnchor.addClass(NG_ANIMATE_SHIM_CLASS_NAME);
1297
-
1298
- clone.addClass(NG_ANIMATE_ANCHOR_CLASS_NAME);
1299
- clone.addClass(anchorClasses);
1300
-
1301
- rootBodyElement.append(clone);
1302
-
1303
- var animatorOut = prepareOutAnimation();
1304
- if (!animatorOut) {
1305
- return end();
1306
- }
1307
-
1308
- return {
1309
- start: function() {
1310
- var runner;
1311
-
1312
- var currentAnimation = animatorOut.start();
1313
- currentAnimation.done(function() {
1314
- currentAnimation = null;
1315
- var animatorIn = prepareInAnimation();
1316
- if (animatorIn) {
1317
- currentAnimation = animatorIn.start();
1318
- currentAnimation.done(function() {
1319
- currentAnimation = null;
1320
- end();
1321
- runner.complete();
1322
- });
1323
- return currentAnimation;
1324
- }
1325
- // in the event that there is no `in` animation
1326
- end();
1327
- runner.complete();
1328
- });
1329
-
1330
- runner = new $$AnimateRunner({
1331
- end: endFn,
1332
- cancel: endFn
1333
- });
1334
-
1335
- return runner;
1336
-
1337
- function endFn() {
1338
- if (currentAnimation) {
1339
- currentAnimation.end();
1340
- }
1341
- }
1342
- }
1343
- };
1344
-
1345
- function calculateAnchorStyles(anchor) {
1346
- var styles = {};
1347
-
1348
- var coords = anchor[0].getBoundingClientRect();
1349
-
1350
- // we iterate directly since safari messes up and doesn't return
1351
- // all the keys for the coods object when iterated
1352
- forEach(['width','height','top','left'], function(key) {
1353
- var value = coords[key];
1354
- switch (key) {
1355
- case 'top':
1356
- value += bodyNode.scrollTop;
1357
- break;
1358
- case 'left':
1359
- value += bodyNode.scrollLeft;
1360
- break;
1361
- }
1362
- styles[key] = Math.floor(value) + 'px';
1363
- });
1364
- return styles;
1365
- }
1366
-
1367
- function prepareOutAnimation() {
1368
- return $animateCss(clone, {
1369
- addClass: NG_OUT_ANCHOR_CLASS_NAME,
1370
- delay: true,
1371
- from: calculateAnchorStyles(outAnchor)
1372
- });
1373
- }
1374
-
1375
- function prepareInAnimation() {
1376
- var endingClasses = filterCssClasses(inAnchor.attr('class'));
1377
- var classes = getUniqueValues(endingClasses, startingClasses);
1378
- return $animateCss(clone, {
1379
- to: calculateAnchorStyles(inAnchor),
1380
- addClass: NG_IN_ANCHOR_CLASS_NAME + ' ' + classes,
1381
- removeClass: NG_OUT_ANCHOR_CLASS_NAME + ' ' + startingClasses,
1382
- delay: true
1383
- });
1384
- }
1385
-
1386
- function end() {
1387
- clone.remove();
1388
- outAnchor.removeClass(NG_ANIMATE_SHIM_CLASS_NAME);
1389
- inAnchor.removeClass(NG_ANIMATE_SHIM_CLASS_NAME);
1390
- }
1391
- }
1392
-
1393
- function prepareFromToAnchorAnimation(from, to, classes, anchors) {
1394
- var fromAnimation = prepareRegularAnimation(from);
1395
- var toAnimation = prepareRegularAnimation(to);
1396
-
1397
- var anchorAnimations = [];
1398
- forEach(anchors, function(anchor) {
1399
- var outElement = anchor['out'];
1400
- var inElement = anchor['in'];
1401
- var animator = prepareAnchoredAnimation(classes, outElement, inElement);
1402
- if (animator) {
1403
- anchorAnimations.push(animator);
1404
- }
1405
- });
1406
-
1407
- // no point in doing anything when there are no elements to animate
1408
- if (!fromAnimation && !toAnimation && anchorAnimations.length === 0) return;
1409
-
1410
- return {
1411
- start: function() {
1412
- var animationRunners = [];
1413
-
1414
- if (fromAnimation) {
1415
- animationRunners.push(fromAnimation.start());
1416
- }
1417
-
1418
- if (toAnimation) {
1419
- animationRunners.push(toAnimation.start());
1420
- }
1421
-
1422
- forEach(anchorAnimations, function(animation) {
1423
- animationRunners.push(animation.start());
1424
- });
1425
-
1426
- var runner = new $$AnimateRunner({
1427
- end: endFn,
1428
- cancel: endFn // CSS-driven animations cannot be cancelled, only ended
1429
- });
1430
-
1431
- $$AnimateRunner.all(animationRunners, function(status) {
1432
- runner.complete(status);
1433
- });
1434
-
1435
- return runner;
1436
-
1437
- function endFn() {
1438
- forEach(animationRunners, function(runner) {
1439
- runner.end();
1440
- });
1441
- }
1442
- }
1443
- };
1444
- }
1445
-
1446
- function prepareRegularAnimation(animationDetails) {
1447
- var element = animationDetails.element;
1448
- var options = animationDetails.options || {};
1449
- options.structural = animationDetails.structural;
1450
-
1451
- // we special case the leave animation since we want to ensure that
1452
- // the element is removed as soon as the animation is over. Otherwise
1453
- // a flicker might appear or the element may not be removed at all
1454
- options.event = animationDetails.event;
1455
- if (options.event === 'leave' && animationDetails.domOperation) {
1456
- options.onDone = animationDetails.domOperation;
1457
- }
1458
-
1459
- return $animateCss(element, options);
1460
- }
1461
- }];
1462
- }];
1463
-
1464
- // TODO(matsko): use caching here to speed things up for detection
1465
- // TODO(matsko): add documentation
1466
- // by the time...
1467
-
1468
- var $$AnimateJsProvider = ['$animateProvider', function($animateProvider) {
1469
- this.$get = ['$injector', '$$AnimateRunner', '$$rAFMutex', '$$jqLite',
1470
- function($injector, $$AnimateRunner, $$rAFMutex, $$jqLite) {
1471
-
1472
- var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
1473
- // $animateJs(element, 'enter');
1474
- return function(element, event, classes, options) {
1475
- // the `classes` argument is optional and if it is not used
1476
- // then the classes will be resolved from the element's className
1477
- // property as well as options.addClass/options.removeClass.
1478
- if (arguments.length === 3 && isObject(classes)) {
1479
- options = classes;
1480
- classes = null;
1481
- }
1482
-
1483
- options = prepareAnimationOptions(options);
1484
- if (!classes) {
1485
- classes = element.attr('class') || '';
1486
- if (options.addClass) {
1487
- classes += ' ' + options.addClass;
1488
- }
1489
- if (options.removeClass) {
1490
- classes += ' ' + options.removeClass;
1491
- }
1492
- }
1493
-
1494
- var classesToAdd = options.addClass;
1495
- var classesToRemove = options.removeClass;
1496
-
1497
- // the lookupAnimations function returns a series of animation objects that are
1498
- // matched up with one or more of the CSS classes. These animation objects are
1499
- // defined via the module.animation factory function. If nothing is detected then
1500
- // we don't return anything which then makes $animation query the next driver.
1501
- var animations = lookupAnimations(classes);
1502
- var before, after;
1503
- if (animations.length) {
1504
- var afterFn, beforeFn;
1505
- if (event == 'leave') {
1506
- beforeFn = 'leave';
1507
- afterFn = 'afterLeave'; // TODO(matsko): get rid of this
1508
- } else {
1509
- beforeFn = 'before' + event.charAt(0).toUpperCase() + event.substr(1);
1510
- afterFn = event;
1511
- }
1512
-
1513
- if (event !== 'enter' && event !== 'move') {
1514
- before = packageAnimations(element, event, options, animations, beforeFn);
1515
- }
1516
- after = packageAnimations(element, event, options, animations, afterFn);
1517
- }
1518
-
1519
- // no matching animations
1520
- if (!before && !after) return;
1521
-
1522
- function applyOptions() {
1523
- options.domOperation();
1524
- applyAnimationClasses(element, options);
1525
- }
1526
-
1527
- return {
1528
- start: function() {
1529
- var closeActiveAnimations;
1530
- var chain = [];
1531
-
1532
- if (before) {
1533
- chain.push(function(fn) {
1534
- closeActiveAnimations = before(fn);
1535
- });
1536
- }
1537
-
1538
- if (chain.length) {
1539
- chain.push(function(fn) {
1540
- applyOptions();
1541
- fn(true);
1542
- });
1543
- } else {
1544
- applyOptions();
1545
- }
1546
-
1547
- if (after) {
1548
- chain.push(function(fn) {
1549
- closeActiveAnimations = after(fn);
1550
- });
1551
- }
1552
-
1553
- var animationClosed = false;
1554
- var runner = new $$AnimateRunner({
1555
- end: function() {
1556
- endAnimations();
1557
- },
1558
- cancel: function() {
1559
- endAnimations(true);
1560
- }
1561
- });
1562
-
1563
- $$AnimateRunner.chain(chain, onComplete);
1564
- return runner;
1565
-
1566
- function onComplete(success) {
1567
- animationClosed = true;
1568
- applyOptions();
1569
- applyAnimationStyles(element, options);
1570
- runner.complete(success);
1571
- }
1572
-
1573
- function endAnimations(cancelled) {
1574
- if (!animationClosed) {
1575
- (closeActiveAnimations || noop)(cancelled);
1576
- onComplete(cancelled);
1577
- }
1578
- }
1579
- }
1580
- };
1581
-
1582
- function executeAnimationFn(fn, element, event, options, onDone) {
1583
- var args;
1584
- switch (event) {
1585
- case 'animate':
1586
- args = [element, options.from, options.to, onDone];
1587
- break;
1588
-
1589
- case 'setClass':
1590
- args = [element, classesToAdd, classesToRemove, onDone];
1591
- break;
1592
-
1593
- case 'addClass':
1594
- args = [element, classesToAdd, onDone];
1595
- break;
1596
-
1597
- case 'removeClass':
1598
- args = [element, classesToRemove, onDone];
1599
- break;
1600
-
1601
- default:
1602
- args = [element, onDone];
1603
- break;
1604
- }
1605
-
1606
- args.push(options);
1607
-
1608
- var value = fn.apply(fn, args);
1609
-
1610
- // optional onEnd / onCancel callback
1611
- return isFunction(value) ? value : noop;
1612
- }
1613
-
1614
- function groupEventedAnimations(element, event, options, animations, fnName) {
1615
- var operations = [];
1616
- forEach(animations, function(ani) {
1617
- var animation = ani[fnName];
1618
- if (!animation) return;
1619
-
1620
- // note that all of these animations will run in parallel
1621
- operations.push(function() {
1622
- var runner;
1623
- var endProgressCb;
1624
-
1625
- var resolved = false;
1626
- var onAnimationComplete = function(rejected) {
1627
- if (!resolved) {
1628
- resolved = true;
1629
- (endProgressCb || noop)(rejected);
1630
- runner.complete(!rejected);
1631
- }
1632
- };
1633
-
1634
- runner = new $$AnimateRunner({
1635
- end: function() {
1636
- onAnimationComplete();
1637
- },
1638
- cancel: function() {
1639
- onAnimationComplete(true);
1640
- }
1641
- });
1642
-
1643
- endProgressCb = executeAnimationFn(animation, element, event, options, function(result) {
1644
- var cancelled = result === false;
1645
- onAnimationComplete(cancelled);
1646
- });
1647
-
1648
- return runner;
1649
- });
1650
- });
1651
-
1652
- return operations;
1653
- }
1654
-
1655
- function packageAnimations(element, event, options, animations, fnName) {
1656
- var operations = groupEventedAnimations(element, event, options, animations, fnName);
1657
- if (operations.length === 0) {
1658
- var a,b;
1659
- if (fnName === 'beforeSetClass') {
1660
- a = groupEventedAnimations(element, 'removeClass', options, animations, 'beforeRemoveClass');
1661
- b = groupEventedAnimations(element, 'addClass', options, animations, 'beforeAddClass');
1662
- } else if (fnName === 'setClass') {
1663
- a = groupEventedAnimations(element, 'removeClass', options, animations, 'removeClass');
1664
- b = groupEventedAnimations(element, 'addClass', options, animations, 'addClass');
1665
- }
1666
-
1667
- if (a) {
1668
- operations = operations.concat(a);
1669
- }
1670
- if (b) {
1671
- operations = operations.concat(b);
1672
- }
1673
- }
1674
-
1675
- if (operations.length === 0) return;
1676
-
1677
- // TODO(matsko): add documentation
1678
- return function startAnimation(callback) {
1679
- var runners = [];
1680
- if (operations.length) {
1681
- forEach(operations, function(animateFn) {
1682
- runners.push(animateFn());
1683
- });
1684
- }
1685
-
1686
- runners.length ? $$AnimateRunner.all(runners, callback) : callback();
1687
-
1688
- return function endFn(reject) {
1689
- forEach(runners, function(runner) {
1690
- reject ? runner.cancel() : runner.end();
1691
- });
1692
- };
1693
- };
1694
- }
1695
- };
1696
-
1697
- function lookupAnimations(classes) {
1698
- classes = isArray(classes) ? classes : classes.split(' ');
1699
- var matches = [], flagMap = {};
1700
- for (var i=0; i < classes.length; i++) {
1701
- var klass = classes[i],
1702
- animationFactory = $animateProvider.$$registeredAnimations[klass];
1703
- if (animationFactory && !flagMap[klass]) {
1704
- matches.push($injector.get(animationFactory));
1705
- flagMap[klass] = true;
1706
- }
1707
- }
1708
- return matches;
1709
- }
1710
- }];
1711
- }];
1712
-
1713
- var $$AnimateJsDriverProvider = ['$$animationProvider', function($$animationProvider) {
1714
- $$animationProvider.drivers.push('$$animateJsDriver');
1715
- this.$get = ['$$animateJs', '$$AnimateRunner', function($$animateJs, $$AnimateRunner) {
1716
- return function initDriverFn(animationDetails) {
1717
- if (animationDetails.from && animationDetails.to) {
1718
- var fromAnimation = prepareAnimation(animationDetails.from);
1719
- var toAnimation = prepareAnimation(animationDetails.to);
1720
- if (!fromAnimation && !toAnimation) return;
1721
-
1722
- return {
1723
- start: function() {
1724
- var animationRunners = [];
1725
-
1726
- if (fromAnimation) {
1727
- animationRunners.push(fromAnimation.start());
1728
- }
1729
-
1730
- if (toAnimation) {
1731
- animationRunners.push(toAnimation.start());
1732
- }
1733
-
1734
- $$AnimateRunner.all(animationRunners, done);
1735
-
1736
- var runner = new $$AnimateRunner({
1737
- end: endFnFactory(),
1738
- cancel: endFnFactory()
1739
- });
1740
-
1741
- return runner;
1742
-
1743
- function endFnFactory() {
1744
- return function() {
1745
- forEach(animationRunners, function(runner) {
1746
- // at this point we cannot cancel animations for groups just yet. 1.5+
1747
- runner.end();
1748
- });
1749
- };
1750
- }
1751
-
1752
- function done(status) {
1753
- runner.complete(status);
1754
- }
1755
- }
1756
- };
1757
- } else {
1758
- return prepareAnimation(animationDetails);
1759
- }
1760
- };
1761
-
1762
- function prepareAnimation(animationDetails) {
1763
- // TODO(matsko): make sure to check for grouped animations and delegate down to normal animations
1764
- var element = animationDetails.element;
1765
- var event = animationDetails.event;
1766
- var options = animationDetails.options;
1767
- var classes = animationDetails.classes;
1768
- return $$animateJs(element, event, classes, options);
1769
- }
1770
- }];
1771
- }];
1772
-
1773
- var NG_ANIMATE_ATTR_NAME = 'data-ng-animate';
1774
- var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
1775
- var PRE_DIGEST_STATE = 1;
1776
- var RUNNING_STATE = 2;
1777
-
1778
- var rules = this.rules = {
1779
- skip: [],
1780
- cancel: [],
1781
- join: []
1782
- };
1783
-
1784
- function isAllowed(ruleType, element, currentAnimation, previousAnimation) {
1785
- return rules[ruleType].some(function(fn) {
1786
- return fn(element, currentAnimation, previousAnimation);
1787
- });
1788
- }
1789
-
1790
- function hasAnimationClasses(options, and) {
1791
- options = options || {};
1792
- var a = (options.addClass || '').length > 0;
1793
- var b = (options.removeClass || '').length > 0;
1794
- return and ? a && b : a || b;
1795
- }
1796
-
1797
- rules.join.push(function(element, newAnimation, currentAnimation) {
1798
- // if the new animation is class-based then we can just tack that on
1799
- return !newAnimation.structural && hasAnimationClasses(newAnimation.options);
1800
- });
1801
-
1802
- rules.skip.push(function(element, newAnimation, currentAnimation) {
1803
- // there is no need to animate anything if no classes are being added and
1804
- // there is no structural animation that will be triggered
1805
- return !newAnimation.structural && !hasAnimationClasses(newAnimation.options);
1806
- });
1807
-
1808
- rules.skip.push(function(element, newAnimation, currentAnimation) {
1809
- // why should we trigger a new structural animation if the element will
1810
- // be removed from the DOM anyway?
1811
- return currentAnimation.event == 'leave' && newAnimation.structural;
1812
- });
1813
-
1814
- rules.skip.push(function(element, newAnimation, currentAnimation) {
1815
- // if there is a current animation then skip the class-based animation
1816
- return currentAnimation.structural && !newAnimation.structural;
1817
- });
1818
-
1819
- rules.cancel.push(function(element, newAnimation, currentAnimation) {
1820
- // there can never be two structural animations running at the same time
1821
- return currentAnimation.structural && newAnimation.structural;
1822
- });
1823
-
1824
- rules.cancel.push(function(element, newAnimation, currentAnimation) {
1825
- // if the previous animation is already running, but the new animation will
1826
- // be triggered, but the new animation is structural
1827
- return currentAnimation.state === RUNNING_STATE && newAnimation.structural;
1828
- });
1829
-
1830
- this.$get = ['$$rAF', '$rootScope', '$rootElement', '$document', '$$HashMap',
1831
- '$$animation', '$$AnimateRunner', '$templateRequest', '$$jqLite',
1832
- function($$rAF, $rootScope, $rootElement, $document, $$HashMap,
1833
- $$animation, $$AnimateRunner, $templateRequest, $$jqLite) {
1834
-
1835
- var activeAnimationsLookup = new $$HashMap();
1836
- var disabledElementsLookup = new $$HashMap();
1837
-
1838
- var animationsEnabled = null;
1839
-
1840
- // Wait until all directive and route-related templates are downloaded and
1841
- // compiled. The $templateRequest.totalPendingRequests variable keeps track of
1842
- // all of the remote templates being currently downloaded. If there are no
1843
- // templates currently downloading then the watcher will still fire anyway.
1844
- var deregisterWatch = $rootScope.$watch(
1845
- function() { return $templateRequest.totalPendingRequests === 0; },
1846
- function(isEmpty) {
1847
- if (!isEmpty) return;
1848
- deregisterWatch();
1849
-
1850
- // Now that all templates have been downloaded, $animate will wait until
1851
- // the post digest queue is empty before enabling animations. By having two
1852
- // calls to $postDigest calls we can ensure that the flag is enabled at the
1853
- // very end of the post digest queue. Since all of the animations in $animate
1854
- // use $postDigest, it's important that the code below executes at the end.
1855
- // This basically means that the page is fully downloaded and compiled before
1856
- // any animations are triggered.
1857
- $rootScope.$$postDigest(function() {
1858
- $rootScope.$$postDigest(function() {
1859
- // we check for null directly in the event that the application already called
1860
- // .enabled() with whatever arguments that it provided it with
1861
- if (animationsEnabled === null) {
1862
- animationsEnabled = true;
1863
- }
1864
- });
1865
- });
1866
- }
1867
- );
1868
-
1869
- var bodyElement = jqLite($document[0].body);
1870
-
1871
- var callbackRegistry = {};
1872
-
1873
- // remember that the classNameFilter is set during the provider/config
1874
- // stage therefore we can optimize here and setup a helper function
1875
- var classNameFilter = $animateProvider.classNameFilter();
1876
- var isAnimatableClassName = !classNameFilter
1877
- ? function() { return true; }
1878
- : function(className) {
1879
- return classNameFilter.test(className);
1880
- };
1881
-
1882
- var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
1883
-
1884
- function normalizeAnimationOptions(element, options) {
1885
- return mergeAnimationOptions(element, options, {});
1886
- }
1887
-
1888
- function findCallbacks(element, event) {
1889
- var targetNode = element[0];
1890
-
1891
- var matches = [];
1892
- var entries = callbackRegistry[event];
1893
- if (entries) {
1894
- forEach(entries, function(entry) {
1895
- if (entry.node.contains(targetNode)) {
1896
- matches.push(entry.callback);
1897
- }
1898
- });
1899
- }
1900
-
1901
- return matches;
1902
- }
1903
-
1904
- function triggerCallback(event, element, phase, data) {
1905
- $$rAF(function() {
1906
- forEach(findCallbacks(element, event), function(callback) {
1907
- callback(element, phase, data);
1908
- });
1909
- });
1910
- }
1911
-
1912
- return {
1913
- on: function(event, container, callback) {
1914
- var node = extractElementNode(container);
1915
- callbackRegistry[event] = callbackRegistry[event] || [];
1916
- callbackRegistry[event].push({
1917
- node: node,
1918
- callback: callback
1919
- });
1920
- },
1921
-
1922
- off: function(event, container, callback) {
1923
- var entries = callbackRegistry[event];
1924
- if (!entries) return;
1925
-
1926
- callbackRegistry[event] = arguments.length === 1
1927
- ? null
1928
- : filterFromRegistry(entries, container, callback);
1929
-
1930
- function filterFromRegistry(list, matchContainer, matchCallback) {
1931
- var containerNode = extractElementNode(matchContainer);
1932
- return list.filter(function(entry) {
1933
- var isMatch = entry.node === containerNode &&
1934
- (!matchCallback || entry.callback === matchCallback);
1935
- return !isMatch;
1936
- });
1937
- }
1938
- },
1939
-
1940
- push: function(element, event, options, domOperation) {
1941
- options = options || {};
1942
- options.domOperation = domOperation;
1943
- return queueAnimation(element, event, options);
1944
- },
1945
-
1946
- // this method has four signatures:
1947
- // () - global getter
1948
- // (bool) - global setter
1949
- // (element) - element getter
1950
- // (element, bool) - element setter<F37>
1951
- enabled: function(element, bool) {
1952
- var argCount = arguments.length;
1953
-
1954
- if (argCount === 0) {
1955
- // () - Global getter
1956
- bool = !!animationsEnabled;
1957
- } else {
1958
- var hasElement = isElement(element);
1959
-
1960
- if (!hasElement) {
1961
- // (bool) - Global setter
1962
- bool = animationsEnabled = !!element;
1963
- } else {
1964
- var node = element.length ? element[0] : element;
1965
- var recordExists = disabledElementsLookup.get(node);
1966
-
1967
- if (argCount === 1) {
1968
- // (element) - Element getter
1969
- bool = !recordExists;
1970
- } else {
1971
- // (element, bool) - Element setter
1972
- bool = !!bool;
1973
- if (!bool) {
1974
- disabledElementsLookup.put(node, true);
1975
- } else if (recordExists) {
1976
- disabledElementsLookup.remove(node);
1977
- }
1978
- }
1979
- }
1980
- }
1981
-
1982
- return bool;
1983
- }
1984
- };
1985
-
1986
- function queueAnimation(element, event, options) {
1987
- element = stripCommentsFromElement(element);
1988
- var node = element[0];
1989
-
1990
- options = prepareAnimationOptions(options);
1991
- var parent = element.parent();
1992
-
1993
- // we create a fake runner with a working promise.
1994
- // These methods will become available after the digest has passed
1995
- var runner = new $$AnimateRunner();
1996
-
1997
- // there are situations where a directive issues an animation for
1998
- // a jqLite wrapper that contains only comment nodes... If this
1999
- // happens then there is no way we can perform an animation
2000
- if (!node) {
2001
- runner.end();
2002
- return runner;
2003
- }
2004
-
2005
- if (isArray(options.addClass)) {
2006
- options.addClass = options.addClass.join(' ');
2007
- }
2008
-
2009
- if (isArray(options.removeClass)) {
2010
- options.removeClass = options.removeClass.join(' ');
2011
- }
2012
-
2013
- if (options.from && !isObject(options.from)) {
2014
- options.from = null;
2015
- }
2016
-
2017
- if (options.to && !isObject(options.to)) {
2018
- options.to = null;
2019
- }
2020
-
2021
- var className = [node.className, options.addClass, options.removeClass].join(' ');
2022
- if (!isAnimatableClassName(className)) {
2023
- runner.end();
2024
- return runner;
2025
- }
2026
-
2027
- var isStructural = ['enter', 'move', 'leave'].indexOf(event) >= 0;
2028
-
2029
- // this is a hard disable of all animations for the application or on
2030
- // the element itself, therefore there is no need to continue further
2031
- // past this point if not enabled
2032
- var skipAnimations = !animationsEnabled || disabledElementsLookup.get(node);
2033
- var existingAnimation = (!skipAnimations && activeAnimationsLookup.get(node)) || {};
2034
- var hasExistingAnimation = !!existingAnimation.state;
2035
-
2036
- // there is no point in traversing the same collection of parent ancestors if a followup
2037
- // animation will be run on the same element that already did all that checking work
2038
- if (!skipAnimations && (!hasExistingAnimation || existingAnimation.state != PRE_DIGEST_STATE)) {
2039
- skipAnimations = !areAnimationsAllowed(element, parent, event);
2040
- }
2041
-
2042
- if (skipAnimations) {
2043
- close();
2044
- return runner;
2045
- }
2046
-
2047
- if (isStructural) {
2048
- closeChildAnimations(element);
2049
- }
2050
-
2051
- var newAnimation = {
2052
- structural: isStructural,
2053
- element: element,
2054
- event: event,
2055
- options: options,
2056
- runner: runner
2057
- };
2058
-
2059
- if (hasExistingAnimation) {
2060
- var skipAnimationFlag = isAllowed('skip', element, newAnimation, existingAnimation);
2061
- if (skipAnimationFlag) {
2062
- if (existingAnimation.state === RUNNING_STATE) {
2063
- close();
2064
- return runner;
2065
- } else {
2066
- mergeAnimationOptions(element, existingAnimation.options, options);
2067
- return existingAnimation.runner;
2068
- }
2069
- }
2070
-
2071
- var cancelAnimationFlag = isAllowed('cancel', element, newAnimation, existingAnimation);
2072
- if (cancelAnimationFlag) {
2073
- if (existingAnimation.state === RUNNING_STATE) {
2074
- existingAnimation.runner.end();
2075
- } else {
2076
- mergeAnimationOptions(element, newAnimation.options, existingAnimation.options);
2077
- }
2078
- } else {
2079
- // a joined animation means that this animation will take over the existing one
2080
- // so an example would involve a leave animation taking over an enter. Then when
2081
- // the postDigest kicks in the enter will be ignored.
2082
- var joinAnimationFlag = isAllowed('join', element, newAnimation, existingAnimation);
2083
- if (joinAnimationFlag) {
2084
- if (existingAnimation.state === RUNNING_STATE) {
2085
- normalizeAnimationOptions(element, options);
2086
- } else {
2087
- event = newAnimation.event = existingAnimation.event;
2088
- options = mergeAnimationOptions(element, existingAnimation.options, newAnimation.options);
2089
- return runner;
2090
- }
2091
- }
2092
- }
2093
- } else {
2094
- // normalization in this case means that it removes redundant CSS classes that
2095
- // already exist (addClass) or do not exist (removeClass) on the element
2096
- normalizeAnimationOptions(element, options);
2097
- }
2098
-
2099
- // when the options are merged and cleaned up we may end up not having to do
2100
- // an animation at all, therefore we should check this before issuing a post
2101
- // digest callback. Structural animations will always run no matter what.
2102
- var isValidAnimation = newAnimation.structural;
2103
- if (!isValidAnimation) {
2104
- // animate (from/to) can be quickly checked first, otherwise we check if any classes are present
2105
- isValidAnimation = (newAnimation.event === 'animate' && Object.keys(newAnimation.options.to || {}).length > 0)
2106
- || hasAnimationClasses(newAnimation.options);
2107
- }
2108
-
2109
- if (!isValidAnimation) {
2110
- close();
2111
- return runner;
2112
- }
2113
-
2114
- closeParentClassBasedAnimations(parent);
2115
-
2116
- // the counter keeps track of cancelled animations
2117
- var counter = (existingAnimation.counter || 0) + 1;
2118
- newAnimation.counter = counter;
2119
-
2120
- markElementAnimationState(element, PRE_DIGEST_STATE, newAnimation);
2121
-
2122
- $rootScope.$$postDigest(function() {
2123
- var animationDetails = activeAnimationsLookup.get(node);
2124
- var animationCancelled = !animationDetails;
2125
- animationDetails = animationDetails || {};
2126
-
2127
- // if addClass/removeClass is called before something like enter then the
2128
- // registered parent element may not be present. The code below will ensure
2129
- // that a final value for parent element is obtained
2130
- var parentElement = element.parent() || [];
2131
-
2132
- // animate/structural/class-based animations all have requirements. Otherwise there
2133
- // is no point in performing an animation. The parent node must also be set.
2134
- var isValidAnimation = parentElement.length > 0
2135
- && (animationDetails.event === 'animate'
2136
- || animationDetails.structural
2137
- || hasAnimationClasses(animationDetails.options));
2138
-
2139
- // this means that the previous animation was cancelled
2140
- // even if the follow-up animation is the same event
2141
- if (animationCancelled || animationDetails.counter !== counter || !isValidAnimation) {
2142
- // if another animation did not take over then we need
2143
- // to make sure that the domOperation and options are
2144
- // handled accordingly
2145
- if (animationCancelled) {
2146
- applyAnimationClasses(element, options);
2147
- applyAnimationStyles(element, options);
2148
- }
2149
-
2150
- // if the event changed from something like enter to leave then we do
2151
- // it, otherwise if it's the same then the end result will be the same too
2152
- if (animationCancelled || (isStructural && animationDetails.event !== event)) {
2153
- options.domOperation();
2154
- }
2155
-
2156
- return;
2157
- }
2158
-
2159
- // this combined multiple class to addClass / removeClass into a setClass event
2160
- // so long as a structural event did not take over the animation
2161
- event = !animationDetails.structural && hasAnimationClasses(animationDetails.options, true)
2162
- ? 'setClass'
2163
- : animationDetails.event;
2164
-
2165
- closeParentClassBasedAnimations(parentElement);
2166
-
2167
- markElementAnimationState(element, RUNNING_STATE);
2168
- var realRunner = $$animation(element, event, animationDetails.options);
2169
- realRunner.done(function(status) {
2170
- close(!status);
2171
- var animationDetails = activeAnimationsLookup.get(node);
2172
- if (animationDetails && animationDetails.counter === counter) {
2173
- clearElementAnimationState(element);
2174
- }
2175
- notifyProgress(runner, event, 'close', {});
2176
- });
2177
-
2178
- // this will update the runner's flow-control events based on
2179
- // the `realRunner` object.
2180
- runner.setHost(realRunner);
2181
- notifyProgress(runner, event, 'start', {});
2182
- });
2183
-
2184
- return runner;
2185
-
2186
- function notifyProgress(runner, event, phase, data) {
2187
- triggerCallback(event, element, phase, data);
2188
- runner.progress(event, phase, data);
2189
- }
2190
-
2191
- function close(reject) { // jshint ignore:line
2192
- applyAnimationClasses(element, options);
2193
- applyAnimationStyles(element, options);
2194
- options.domOperation();
2195
- runner.complete(!reject);
2196
- }
2197
- }
2198
-
2199
- function closeChildAnimations(element) {
2200
- var node = element[0];
2201
- var children = node.querySelectorAll('[' + NG_ANIMATE_ATTR_NAME + ']');
2202
- forEach(children, function(child) {
2203
- var state = parseInt(child.getAttribute(NG_ANIMATE_ATTR_NAME));
2204
- var animationDetails = activeAnimationsLookup.get(child);
2205
- switch (state) {
2206
- case RUNNING_STATE:
2207
- animationDetails.runner.end();
2208
- /* falls through */
2209
- case PRE_DIGEST_STATE:
2210
- if (animationDetails) {
2211
- activeAnimationsLookup.remove(child);
2212
- }
2213
- break;
2214
- }
2215
- });
2216
- }
2217
-
2218
- function clearElementAnimationState(element) {
2219
- element = element.length ? element[0] : element;
2220
- element.removeAttribute(NG_ANIMATE_ATTR_NAME);
2221
- activeAnimationsLookup.remove(element);
2222
- }
2223
-
2224
- function isMatchingElement(a,b) {
2225
- a = a.length ? a[0] : a;
2226
- b = b.length ? b[0] : b;
2227
- return a === b;
2228
- }
2229
-
2230
- function closeParentClassBasedAnimations(startingElement) {
2231
- var parentNode = startingElement[0];
2232
- do {
2233
- if (!parentNode || parentNode.nodeType !== ELEMENT_NODE) break;
2234
-
2235
- var animationDetails = activeAnimationsLookup.get(parentNode);
2236
- if (animationDetails) {
2237
- examineParentAnimation(parentNode, animationDetails);
2238
- }
2239
-
2240
- parentNode = parentNode.parentNode;
2241
- } while (true);
2242
-
2243
- // since animations are detected from CSS classes, we need to flush all parent
2244
- // class-based animations so that the parent classes are all present for child
2245
- // animations to properly function (otherwise any CSS selectors may not work)
2246
- function examineParentAnimation(node, animationDetails) {
2247
- // enter/leave/move always have priority
2248
- if (animationDetails.structural) return;
2249
-
2250
- if (animationDetails.state === RUNNING_STATE) {
2251
- animationDetails.runner.end();
2252
- }
2253
- clearElementAnimationState(node);
2254
- }
2255
- }
2256
-
2257
- function areAnimationsAllowed(element, parent, event) {
2258
- var bodyElementDetected = false;
2259
- var rootElementDetected = false;
2260
- var parentAnimationDetected = false;
2261
- var animateChildren;
2262
-
2263
- while (parent && parent.length) {
2264
- var parentNode = parent[0];
2265
- if (parentNode.nodeType !== ELEMENT_NODE) {
2266
- // no point in inspecting the #document element
2267
- break;
2268
- }
2269
-
2270
- var details = activeAnimationsLookup.get(parentNode) || {};
2271
- // either an enter, leave or move animation will commence
2272
- // therefore we can't allow any animations to take place
2273
- // but if a parent animation is class-based then that's ok
2274
- if (!parentAnimationDetected) {
2275
- parentAnimationDetected = details.structural || disabledElementsLookup.get(parentNode);
2276
- }
2277
-
2278
- if (isUndefined(animateChildren) || animateChildren === true) {
2279
- var value = parent.data(NG_ANIMATE_CHILDREN_DATA);
2280
- if (isDefined(value)) {
2281
- animateChildren = value;
2282
- }
2283
- }
2284
-
2285
- // there is no need to continue traversing at this point
2286
- if (parentAnimationDetected && animateChildren === false) break;
2287
-
2288
- if (!rootElementDetected) {
2289
- // angular doesn't want to attempt to animate elements outside of the application
2290
- // therefore we need to ensure that the rootElement is an ancestor of the current element
2291
- rootElementDetected = isMatchingElement(parent, $rootElement);
2292
- }
2293
-
2294
- if (!bodyElementDetected) {
2295
- // we also need to ensure that the element is or will be apart of the body element
2296
- // otherwise it is pointless to even issue an animation to be rendered
2297
- bodyElementDetected = isMatchingElement(parent, bodyElement);
2298
- }
2299
-
2300
- parent = parent.parent();
2301
- }
2302
-
2303
- var allowAnimation = !parentAnimationDetected || animateChildren;
2304
- return allowAnimation && rootElementDetected && bodyElementDetected;
2305
- }
2306
-
2307
- function markElementAnimationState(element, state, details) {
2308
- details = details || {};
2309
- details.state = state;
2310
-
2311
- element = element.length ? element[0] : element;
2312
- element.setAttribute(NG_ANIMATE_ATTR_NAME, state);
2313
-
2314
- var oldValue = activeAnimationsLookup.get(element);
2315
- var newValue = oldValue
2316
- ? extend(oldValue, details)
2317
- : details;
2318
- activeAnimationsLookup.put(element, newValue);
2319
- }
2320
- }];
2321
- }];
2322
-
2323
- var $$rAFMutexFactory = ['$$rAF', function($$rAF) {
2324
- return function() {
2325
- var passed = false;
2326
- $$rAF(function() {
2327
- passed = true;
2328
- });
2329
- return function(fn) {
2330
- passed ? fn() : $$rAF(fn);
2331
- };
2332
- };
2333
- }];
2334
-
2335
- var $$AnimateRunnerFactory = ['$q', '$$rAFMutex', function($q, $$rAFMutex) {
2336
- var INITIAL_STATE = 0;
2337
- var DONE_PENDING_STATE = 1;
2338
- var DONE_COMPLETE_STATE = 2;
2339
-
2340
- AnimateRunner.chain = function(chain, callback) {
2341
- var index = 0;
2342
-
2343
- next();
2344
- function next() {
2345
- if (index === chain.length) {
2346
- callback(true);
2347
- return;
2348
- }
2349
-
2350
- chain[index](function(response) {
2351
- if (response === false) {
2352
- callback(false);
2353
- return;
2354
- }
2355
- index++;
2356
- next();
2357
- });
2358
- }
2359
- };
2360
-
2361
- AnimateRunner.all = function(runners, callback) {
2362
- var count = 0;
2363
- var status = true;
2364
- forEach(runners, function(runner) {
2365
- runner.done(onProgress);
2366
- });
2367
-
2368
- function onProgress(response) {
2369
- status = status && response;
2370
- if (++count === runners.length) {
2371
- callback(status);
2372
- }
2373
- }
2374
- };
2375
-
2376
- function AnimateRunner(host) {
2377
- this.setHost(host);
2378
-
2379
- this._doneCallbacks = [];
2380
- this._runInAnimationFrame = $$rAFMutex();
2381
- this._state = 0;
2382
- }
2383
-
2384
- AnimateRunner.prototype = {
2385
- setHost: function(host) {
2386
- this.host = host || {};
2387
- },
2388
-
2389
- done: function(fn) {
2390
- if (this._state === DONE_COMPLETE_STATE) {
2391
- fn();
2392
- } else {
2393
- this._doneCallbacks.push(fn);
2394
- }
2395
- },
2396
-
2397
- progress: noop,
2398
-
2399
- getPromise: function() {
2400
- if (!this.promise) {
2401
- var self = this;
2402
- this.promise = $q(function(resolve, reject) {
2403
- self.done(function(status) {
2404
- status === false ? reject() : resolve();
2405
- });
2406
- });
2407
- }
2408
- return this.promise;
2409
- },
2410
-
2411
- then: function(resolveHandler, rejectHandler) {
2412
- return this.getPromise().then(resolveHandler, rejectHandler);
2413
- },
2414
-
2415
- 'catch': function(handler) {
2416
- return this.getPromise()['catch'](handler);
2417
- },
2418
-
2419
- 'finally': function(handler) {
2420
- return this.getPromise()['finally'](handler);
2421
- },
2422
-
2423
- pause: function() {
2424
- if (this.host.pause) {
2425
- this.host.pause();
2426
- }
2427
- },
2428
-
2429
- resume: function() {
2430
- if (this.host.resume) {
2431
- this.host.resume();
2432
- }
2433
- },
2434
-
2435
- end: function() {
2436
- if (this.host.end) {
2437
- this.host.end();
2438
- }
2439
- this._resolve(true);
2440
- },
2441
-
2442
- cancel: function() {
2443
- if (this.host.cancel) {
2444
- this.host.cancel();
2445
- }
2446
- this._resolve(false);
2447
- },
2448
-
2449
- complete: function(response) {
2450
- var self = this;
2451
- if (self._state === INITIAL_STATE) {
2452
- self._state = DONE_PENDING_STATE;
2453
- self._runInAnimationFrame(function() {
2454
- self._resolve(response);
2455
- });
2456
- }
2457
- },
2458
-
2459
- _resolve: function(response) {
2460
- if (this._state !== DONE_COMPLETE_STATE) {
2461
- forEach(this._doneCallbacks, function(fn) {
2462
- fn(response);
2463
- });
2464
- this._doneCallbacks.length = 0;
2465
- this._state = DONE_COMPLETE_STATE;
2466
- }
2467
- }
2468
- };
2469
-
2470
- return AnimateRunner;
2471
- }];
2472
-
2473
- var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
2474
- var NG_ANIMATE_CLASSNAME = 'ng-animate';
2475
- var NG_ANIMATE_REF_ATTR = 'ng-animate-ref';
2476
-
2477
- var drivers = this.drivers = [];
2478
-
2479
- var RUNNER_STORAGE_KEY = '$$animationRunner';
2480
-
2481
- function setRunner(element, runner) {
2482
- element.data(RUNNER_STORAGE_KEY, runner);
2483
- }
2484
-
2485
- function removeRunner(element) {
2486
- element.removeData(RUNNER_STORAGE_KEY);
2487
- }
2488
-
2489
- function getRunner(element) {
2490
- return element.data(RUNNER_STORAGE_KEY);
2491
- }
2492
-
2493
- this.$get = ['$$jqLite', '$rootScope', '$injector', '$$AnimateRunner',
2494
- function($$jqLite, $rootScope, $injector, $$AnimateRunner) {
2495
-
2496
- var animationQueue = [];
2497
- var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
2498
-
2499
- // TODO(matsko): document the signature in a better way
2500
- return function(element, event, options) {
2501
- options = prepareAnimationOptions(options);
2502
- var isStructural = ['enter', 'move', 'leave'].indexOf(event) >= 0;
2503
-
2504
- // there is no animation at the current moment, however
2505
- // these runner methods will get later updated with the
2506
- // methods leading into the driver's end/cancel methods
2507
- // for now they just stop the animation from starting
2508
- var runner = new $$AnimateRunner({
2509
- end: function() { close(); },
2510
- cancel: function() { close(true); }
2511
- });
2512
-
2513
- if (!drivers.length) {
2514
- close();
2515
- return runner;
2516
- }
2517
-
2518
- setRunner(element, runner);
2519
-
2520
- var classes = mergeClasses(element.attr('class'), mergeClasses(options.addClass, options.removeClass));
2521
- var tempClasses = options.tempClasses;
2522
- if (tempClasses) {
2523
- classes += ' ' + tempClasses;
2524
- options.tempClasses = null;
2525
- }
2526
-
2527
- animationQueue.push({
2528
- // this data is used by the postDigest code and passed into
2529
- // the driver step function
2530
- element: element,
2531
- classes: classes,
2532
- event: event,
2533
- structural: isStructural,
2534
- options: options,
2535
- start: start,
2536
- close: close
2537
- });
2538
-
2539
- element.on('$destroy', handleDestroyedElement);
2540
-
2541
- // we only want there to be one function called within the post digest
2542
- // block. This way we can group animations for all the animations that
2543
- // were apart of the same postDigest flush call.
2544
- if (animationQueue.length > 1) return runner;
2545
-
2546
- $rootScope.$$postDigest(function() {
2547
- var animations = [];
2548
- forEach(animationQueue, function(entry) {
2549
- // the element was destroyed early on which removed the runner
2550
- // form its storage. This means we can't animate this element
2551
- // at all and it already has been closed due to destruction.
2552
- if (getRunner(entry.element)) {
2553
- animations.push(entry);
2554
- }
2555
- });
2556
-
2557
- // now any future animations will be in another postDigest
2558
- animationQueue.length = 0;
2559
-
2560
- forEach(groupAnimations(animations), function(animationEntry) {
2561
- var startFn = animationEntry.start;
2562
- var closeFn = animationEntry.close;
2563
- var operation = invokeFirstDriver(animationEntry);
2564
- var startAnimation = operation && operation.start; /// TODO(matsko): only recognize operation.start()
2565
- if (!startAnimation) {
2566
- closeFn();
2567
- } else {
2568
- startFn();
2569
- var animationRunner = startAnimation();
2570
- animationRunner.done(function(status) {
2571
- closeFn(!status);
2572
- });
2573
- updateAnimationRunners(animationEntry, animationRunner);
2574
- }
2575
- });
2576
- });
2577
-
2578
- return runner;
2579
-
2580
- // TODO(matsko): change to reference nodes
2581
- function getAnchorNodes(node) {
2582
- var SELECTOR = '[' + NG_ANIMATE_REF_ATTR + ']';
2583
- var items = node.hasAttribute(NG_ANIMATE_REF_ATTR)
2584
- ? [node]
2585
- : node.querySelectorAll(SELECTOR);
2586
- var anchors = [];
2587
- forEach(items, function(node) {
2588
- var attr = node.getAttribute(NG_ANIMATE_REF_ATTR);
2589
- if (attr && attr.length) {
2590
- anchors.push(node);
2591
- }
2592
- });
2593
- return anchors;
2594
- }
2595
-
2596
- function groupAnimations(animations) {
2597
- var preparedAnimations = [];
2598
- var refLookup = {};
2599
- forEach(animations, function(animation, index) {
2600
- var element = animation.element;
2601
- var node = element[0];
2602
- var event = animation.event;
2603
- var enterOrMove = ['enter', 'move'].indexOf(event) >= 0;
2604
- var anchorNodes = animation.structural ? getAnchorNodes(node) : [];
2605
-
2606
- if (anchorNodes.length) {
2607
- var direction = enterOrMove ? 'to' : 'from';
2608
-
2609
- forEach(anchorNodes, function(anchor) {
2610
- var key = anchor.getAttribute(NG_ANIMATE_REF_ATTR);
2611
- refLookup[key] = refLookup[key] || {};
2612
- refLookup[key][direction] = {
2613
- animationID: index,
2614
- element: jqLite(anchor)
2615
- };
2616
- });
2617
- } else {
2618
- preparedAnimations.push(animation);
2619
- }
2620
- });
2621
-
2622
- var usedIndicesLookup = {};
2623
- var anchorGroups = {};
2624
- forEach(refLookup, function(operations, key) {
2625
- var from = operations.from;
2626
- var to = operations.to;
2627
-
2628
- if (!from || !to) {
2629
- // only one of these is set therefore we can't have an
2630
- // anchor animation since all three pieces are required
2631
- var index = from ? from.animationID : to.animationID;
2632
- var indexKey = index.toString();
2633
- if (!usedIndicesLookup[indexKey]) {
2634
- usedIndicesLookup[indexKey] = true;
2635
- preparedAnimations.push(animations[index]);
2636
- }
2637
- return;
2638
- }
2639
-
2640
- var fromAnimation = animations[from.animationID];
2641
- var toAnimation = animations[to.animationID];
2642
- var lookupKey = from.animationID.toString();
2643
- if (!anchorGroups[lookupKey]) {
2644
- var group = anchorGroups[lookupKey] = {
2645
- // TODO(matsko): double-check this code
2646
- start: function() {
2647
- fromAnimation.start();
2648
- toAnimation.start();
2649
- },
2650
- close: function() {
2651
- fromAnimation.close();
2652
- toAnimation.close();
2653
- },
2654
- classes: cssClassesIntersection(fromAnimation.classes, toAnimation.classes),
2655
- from: fromAnimation,
2656
- to: toAnimation,
2657
- anchors: [] // TODO(matsko): change to reference nodes
2658
- };
2659
-
2660
- // the anchor animations require that the from and to elements both have at least
2661
- // one shared CSS class which effictively marries the two elements together to use
2662
- // the same animation driver and to properly sequence the anchor animation.
2663
- if (group.classes.length) {
2664
- preparedAnimations.push(group);
2665
- } else {
2666
- preparedAnimations.push(fromAnimation);
2667
- preparedAnimations.push(toAnimation);
2668
- }
2669
- }
2670
-
2671
- anchorGroups[lookupKey].anchors.push({
2672
- 'out': from.element, 'in': to.element
2673
- });
2674
- });
2675
-
2676
- return preparedAnimations;
2677
- }
2678
-
2679
- function cssClassesIntersection(a,b) {
2680
- a = a.split(' ');
2681
- b = b.split(' ');
2682
- var matches = [];
2683
-
2684
- for (var i = 0; i < a.length; i++) {
2685
- var aa = a[i];
2686
- if (aa.substring(0,3) === 'ng-') continue;
2687
-
2688
- for (var j = 0; j < b.length; j++) {
2689
- if (aa === b[j]) {
2690
- matches.push(aa);
2691
- break;
2692
- }
2693
- }
2694
- }
2695
-
2696
- return matches.join(' ');
2697
- }
2698
-
2699
- function invokeFirstDriver(animationDetails) {
2700
- // we loop in reverse order since the more general drivers (like CSS and JS)
2701
- // may attempt more elements, but custom drivers are more particular
2702
- for (var i = drivers.length - 1; i >= 0; i--) {
2703
- var driverName = drivers[i];
2704
- if (!$injector.has(driverName)) continue; // TODO(matsko): remove this check
2705
-
2706
- var factory = $injector.get(driverName);
2707
- var driver = factory(animationDetails);
2708
- if (driver) {
2709
- return driver;
2710
- }
2711
- }
2712
- }
2713
-
2714
- function start() {
2715
- element.addClass(NG_ANIMATE_CLASSNAME);
2716
- if (tempClasses) {
2717
- $$jqLite.addClass(element, tempClasses);
2718
- }
2719
- }
2720
-
2721
- function updateAnimationRunners(animation, newRunner) {
2722
- if (animation.from && animation.to) {
2723
- update(animation.from.element);
2724
- update(animation.to.element);
2725
- } else {
2726
- update(animation.element);
2727
- }
2728
-
2729
- function update(element) {
2730
- getRunner(element).setHost(newRunner);
2731
- }
2732
- }
2733
-
2734
- function handleDestroyedElement() {
2735
- var runner = getRunner(element);
2736
- if (runner && (event !== 'leave' || !options.$$domOperationFired)) {
2737
- runner.end();
2738
- }
2739
- }
2740
-
2741
- function close(rejected) { // jshint ignore:line
2742
- element.off('$destroy', handleDestroyedElement);
2743
- removeRunner(element);
2744
-
2745
- applyAnimationClasses(element, options);
2746
- applyAnimationStyles(element, options);
2747
- options.domOperation();
2748
-
2749
- if (tempClasses) {
2750
- $$jqLite.removeClass(element, tempClasses);
2751
- }
2752
-
2753
- element.removeClass(NG_ANIMATE_CLASSNAME);
2754
- runner.complete(!rejected);
2755
- }
2756
- };
2757
- }];
2758
- }];
2759
-
2760
- /* global angularAnimateModule: true,
2761
-
2762
- $$rAFMutexFactory,
2763
- $$AnimateChildrenDirective,
2764
- $$AnimateRunnerFactory,
2765
- $$AnimateQueueProvider,
2766
- $$AnimationProvider,
2767
- $AnimateCssProvider,
2768
- $$AnimateCssDriverProvider,
2769
- $$AnimateJsProvider,
2770
- $$AnimateJsDriverProvider,
2771
- */
2772
-
2773
- /**
2774
- * @ngdoc module
2775
- * @name ngAnimate
2776
- * @description
2777
- *
2778
- * The `ngAnimate` module provides support for CSS-based animations (keyframes and transitions) as well as JavaScript-based animations via
2779
- * callback hooks. Animations are not enabled by default, however, by including `ngAnimate` then the animation hooks are enabled for an Angular app.
2780
- *
2781
- * <div doc-module-components="ngAnimate"></div>
2782
- *
2783
- * # Usage
2784
- * 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
2785
- * using CSS (by using matching CSS selectors/styles) and the latter triggers animations that are registered via `module.animation()`. For
2786
- * both CSS and JS animations the sole requirement is to have a matching `CSS class` that exists both in the registered animation and within
2787
- * the HTML element that the animation will be triggered on.
2788
- *
2789
- * ## Directive Support
2790
- * The following directives are "animation aware":
2791
- *
2792
- * | Directive | Supported Animations |
2793
- * |----------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------|
2794
- * | {@link ng.directive:ngRepeat#animations ngRepeat} | enter, leave and move |
2795
- * | {@link ngRoute.directive:ngView#animations ngView} | enter and leave |
2796
- * | {@link ng.directive:ngInclude#animations ngInclude} | enter and leave |
2797
- * | {@link ng.directive:ngSwitch#animations ngSwitch} | enter and leave |
2798
- * | {@link ng.directive:ngIf#animations ngIf} | enter and leave |
2799
- * | {@link ng.directive:ngClass#animations ngClass} | add and remove (the CSS class(es) present) |
2800
- * | {@link ng.directive:ngShow#animations ngShow} & {@link ng.directive:ngHide#animations ngHide} | add and remove (the ng-hide class value) |
2801
- * | {@link ng.directive:form#animation-hooks form} & {@link ng.directive:ngModel#animation-hooks ngModel} | add and remove (dirty, pristine, valid, invalid & all other validations) |
2802
- * | {@link module:ngMessages#animations ngMessages} | add and remove (ng-active & ng-inactive) |
2803
- * | {@link module:ngMessages#animations ngMessage} | enter and leave |
2804
- *
2805
- * (More information can be found by visiting each the documentation associated with each directive.)
2806
- *
2807
- * ## CSS-based Animations
2808
- *
2809
- * 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
2810
- * and CSS code we can create an animation that will be picked up by Angular when an the underlying directive performs an operation.
2811
- *
2812
- * The example below shows how an `enter` animation can be made possible on a element using `ng-if`:
2813
- *
2814
- * ```html
2815
- * <div ng-if="bool" class="fade">
2816
- * Fade me in out
2817
- * </div>
2818
- * <button ng-click="bool=true">Fade In!</button>
2819
- * <button ng-click="bool=false">Fade Out!</button>
2820
- * ```
2821
- *
2822
- * Notice the CSS class **fade**? We can now create the CSS transition code that references this class:
2823
- *
2824
- * ```css
2825
- * /&#42; The starting CSS styles for the enter animation &#42;/
2826
- * .fade.ng-enter {
2827
- * transition:0.5s linear all;
2828
- * opacity:0;
2829
- * }
2830
- *
2831
- * /&#42; The starting CSS styles for the enter animation &#42;/
2832
- * .fade.ng-enter.ng-enter-active {
2833
- * opacity:1;
2834
- * }
2835
- * ```
2836
- *
2837
- * The key thing to remember here is that, depending on the animation event (which each of the directives above trigger depending on what's going on) two
2838
- * generated CSS classes will be applied to the element; in the example above we have `.ng-enter` and `.ng-enter-active`. For CSS transitions, the transition
2839
- * code **must** be defined within the starting CSS class (in this case `.ng-enter`). The destination class is what the transition will animate towards.
2840
- *
2841
- * If for example we wanted to create animations for `leave` and `move` (ngRepeat triggers move) then we can do so using the same CSS naming conventions:
2842
- *
2843
- * ```css
2844
- * /&#42; now the element will fade out before it is removed from the DOM &#42;/
2845
- * .fade.ng-leave {
2846
- * transition:0.5s linear all;
2847
- * opacity:1;
2848
- * }
2849
- * .fade.ng-leave.ng-leave-active {
2850
- * opacity:0;
2851
- * }
2852
- * ```
2853
- *
2854
- * We can also make use of **CSS Keyframes** by referencing the keyframe animation within the starting CSS class:
2855
- *
2856
- * ```css
2857
- * /&#42; there is no need to define anything inside of the destination
2858
- * CSS class since the keyframe will take charge of the animation &#42;/
2859
- * .fade.ng-leave {
2860
- * animation: my_fade_animation 0.5s linear;
2861
- * -webkit-animation: my_fade_animation 0.5s linear;
2862
- * }
2863
- *
2864
- * @keyframes my_fade_animation {
2865
- * from { opacity:1; }
2866
- * to { opacity:0; }
2867
- * }
2868
- *
2869
- * @-webkit-keyframes my_fade_animation {
2870
- * from { opacity:1; }
2871
- * to { opacity:0; }
2872
- * }
2873
- * ```
2874
- *
2875
- * Feel free also mix transitions and keyframes together as well as any other CSS classes on the same element.
2876
- *
2877
- * ### CSS Class-based Animations
2878
- *
2879
- * Class-based animations (animations that are triggered via `ngClass`, `ngShow`, `ngHide` and some other directives) have a slightly different
2880
- * naming convention. Class-based animations are basic enough that a standard transition or keyframe can be referenced on the class being added
2881
- * and removed.
2882
- *
2883
- * For example if we wanted to do a CSS animation for `ngHide` then we place an animation on the `.ng-hide` CSS class:
2884
- *
2885
- * ```html
2886
- * <div ng-show="bool" class="fade">
2887
- * Show and hide me
2888
- * </div>
2889
- * <button ng-click="bool=true">Toggle</button>
2890
- *
2891
- * <style>
2892
- * .fade.ng-hide {
2893
- * transition:0.5s linear all;
2894
- * opacity:0;
2895
- * }
2896
- * </style>
2897
- * ```
2898
- *
2899
- * All that is going on here with ngShow/ngHide behind the scenes is the `.ng-hide` class is added/removed (when the hidden state is valid). Since
2900
- * ngShow and ngHide are animation aware then we can match up a transition and ngAnimate handles the rest.
2901
- *
2902
- * In addition the addition and removal of the CSS class, ngAnimate also provides two helper methods that we can use to further decorate the animation
2903
- * with CSS styles.
2904
- *
2905
- * ```html
2906
- * <div ng-class="{on:onOff}" class="highlight">
2907
- * Highlight this box
2908
- * </div>
2909
- * <button ng-click="onOff=!onOff">Toggle</button>
2910
- *
2911
- * <style>
2912
- * .highlight {
2913
- * transition:0.5s linear all;
2914
- * }
2915
- * .highlight.on-add {
2916
- * background:white;
2917
- * }
2918
- * .highlight.on {
2919
- * background:yellow;
2920
- * }
2921
- * .highlight.on-remove {
2922
- * background:black;
2923
- * }
2924
- * </style>
2925
- * ```
2926
- *
2927
- * We can also make use of CSS keyframes by placing them within the CSS classes.
2928
- *
2929
- *
2930
- * ### CSS Staggering Animations
2931
- * A Staggering animation is a collection of animations that are issued with a slight delay in between each successive operation resulting in a
2932
- * curtain-like effect. The ngAnimate module (versions >=1.2) supports staggering animations and the stagger effect can be
2933
- * performed by creating a **ng-EVENT-stagger** CSS class and attaching that class to the base CSS class used for
2934
- * the animation. The style property expected within the stagger class can either be a **transition-delay** or an
2935
- * **animation-delay** property (or both if your animation contains both transitions and keyframe animations).
2936
- *
2937
- * ```css
2938
- * .my-animation.ng-enter {
2939
- * /&#42; standard transition code &#42;/
2940
- * transition: 1s linear all;
2941
- * opacity:0;
2942
- * }
2943
- * .my-animation.ng-enter-stagger {
2944
- * /&#42; this will have a 100ms delay between each successive leave animation &#42;/
2945
- * transition-delay: 0.1s;
2946
- *
2947
- * /&#42; in case the stagger doesn't work then the duration value
2948
- * must be set to 0 to avoid an accidental CSS inheritance &#42;/
2949
- * transition-duration: 0s;
2950
- * }
2951
- * .my-animation.ng-enter.ng-enter-active {
2952
- * /&#42; standard transition styles &#42;/
2953
- * opacity:1;
2954
- * }
2955
- * ```
2956
- *
2957
- * Staggering animations work by default in ngRepeat (so long as the CSS class is defined). Outside of ngRepeat, to use staggering animations
2958
- * on your own, they can be triggered by firing multiple calls to the same event on $animate. However, the restrictions surrounding this
2959
- * are that each of the elements must have the same CSS className value as well as the same parent element. A stagger operation
2960
- * will also be reset if one or more animation frames have passed since the multiple calls to `$animate` were fired.
2961
- *
2962
- * The following code will issue the **ng-leave-stagger** event on the element provided:
2963
- *
2964
- * ```js
2965
- * var kids = parent.children();
2966
- *
2967
- * $animate.leave(kids[0]); //stagger index=0
2968
- * $animate.leave(kids[1]); //stagger index=1
2969
- * $animate.leave(kids[2]); //stagger index=2
2970
- * $animate.leave(kids[3]); //stagger index=3
2971
- * $animate.leave(kids[4]); //stagger index=4
2972
- *
2973
- * window.requestAnimationFrame(function() {
2974
- * //stagger has reset itself
2975
- * $animate.leave(kids[5]); //stagger index=0
2976
- * $animate.leave(kids[6]); //stagger index=1
2977
- *
2978
- * $scope.$digest();
2979
- * });
2980
- * ```
2981
- *
2982
- * Stagger animations are currently only supported within CSS-defined animations.
2983
- *
2984
- * ## JavaScript-based Animations
2985
- *
2986
- * ngAnimate also allows for animations to be consumed by JavaScript code. The approach is similar to CSS-based animations (where there is a shared
2987
- * CSS class that is referenced in our HTML code) but in addition we need to register the JavaScript animation on the module. By making use of the
2988
- * `module.animation()` module function we can register the ainmation.
2989
- *
2990
- * Let's see an example of a enter/leave animation using `ngRepeat`:
2991
- *
2992
- * ```html
2993
- * <div ng-repeat="item in items" class="slide">
2994
- * {{ item }}
2995
- * </div>
2996
- * ```
2997
- *
2998
- * See the **slide** CSS class? Let's use that class to define an animation that we'll structure in our module code by using `module.animation`:
2999
- *
3000
- * ```js
3001
- * myModule.animation('.slide', [function() {
3002
- * return {
3003
- * // make note that other events (like addClass/removeClass)
3004
- * // have different function input parameters
3005
- * enter: function(element, doneFn) {
3006
- * jQuery(element).fadeIn(1000, doneFn);
3007
- *
3008
- * // remember to call doneFn so that angular
3009
- * // knows that the animation has concluded
3010
- * },
3011
- *
3012
- * move: function(element, doneFn) {
3013
- * jQuery(element).fadeIn(1000, doneFn);
3014
- * },
3015
- *
3016
- * leave: function(element, doneFn) {
3017
- * jQuery(element).fadeOut(1000, doneFn);
3018
- * }
3019
- * }
3020
- * }]
3021
- * ```
3022
- *
3023
- * The nice thing about JS-based animations is that we can inject other services and make use of advanced animation libraries such as
3024
- * greensock.js and velocity.js.
3025
- *
3026
- * If our animation code class-based (meaning that something like `ngClass`, `ngHide` and `ngShow` triggers it) then we can still define
3027
- * our animations inside of the same registered animation, however, the function input arguments are a bit different:
3028
- *
3029
- * ```html
3030
- * <div ng-class="color" class="colorful">
3031
- * this box is moody
3032
- * </div>
3033
- * <button ng-click="color='red'">Change to red</button>
3034
- * <button ng-click="color='blue'">Change to blue</button>
3035
- * <button ng-click="color='green'">Change to green</button>
3036
- * ```
3037
- *
3038
- * ```js
3039
- * myModule.animation('.colorful', [function() {
3040
- * return {
3041
- * addClass: function(element, className, doneFn) {
3042
- * // do some cool animation and call the doneFn
3043
- * },
3044
- * removeClass: function(element, className, doneFn) {
3045
- * // do some cool animation and call the doneFn
3046
- * },
3047
- * setClass: function(element, addedClass, removedClass, doneFn) {
3048
- * // do some cool animation and call the doneFn
3049
- * }
3050
- * }
3051
- * }]
3052
- * ```
3053
- *
3054
- * ## CSS + JS Animations Together
3055
- *
3056
- * 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,
3057
- * defining CSS and JS animations to work off of the same CSS class will not work anymore. Therefore example below will only result in **JS animations taking
3058
- * charge of the animation**:
3059
- *
3060
- * ```html
3061
- * <div ng-if="bool" class="slide">
3062
- * Slide in and out
3063
- * </div>
3064
- * ```
3065
- *
3066
- * ```js
3067
- * myModule.animation('.slide', [function() {
3068
- * return {
3069
- * enter: function(element, doneFn) {
3070
- * jQuery(element).slideIn(1000, doneFn);
3071
- * }
3072
- * }
3073
- * }]
3074
- * ```
3075
- *
3076
- * ```css
3077
- * .slide.ng-enter {
3078
- * transition:0.5s linear all;
3079
- * transform:translateY(-100px);
3080
- * }
3081
- * .slide.ng-enter.ng-enter-active {
3082
- * transform:translateY(0);
3083
- * }
3084
- * ```
3085
- *
3086
- * Does this mean that CSS and JS animations cannot be used together? Do JS-based animations always have higher priority? We can suppliment for the
3087
- * lack of CSS animations by making use of the `$animateCss` service to trigger our own tweaked-out, CSS-based animations directly from
3088
- * our own JS-based animation code:
3089
- *
3090
- * ```js
3091
- * myModule.animation('.slide', ['$animateCss', function($animateCss) {
3092
- * return {
3093
- * enter: function(element, doneFn) {
3094
- * var animation = $animateCss(element, {
3095
- * event: 'enter'
3096
- * });
3097
- *
3098
- * if (animation) {
3099
- * // this will trigger `.slide.ng-enter` and `.slide.ng-enter-active`.
3100
- * var runner = animation.start();
3101
- * runner.done(doneFn);
3102
- * } else { //no CSS animation was detected
3103
- * doneFn();
3104
- * }
3105
- * }
3106
- * }
3107
- * }]
3108
- * ```
3109
- *
3110
- * The nice thing here is that we can save bandwidth by sticking to our CSS-based animation code and we don't need to rely on a 3rd-party animation framework.
3111
- *
3112
- * The `$animateCss` service is very powerful since we can feed in all kinds of extra properties that will be evaluated and fed into a CSS transition or
3113
- * keyframe animation. For example if we wanted to animate the height of an element while adding and removing classes then we can do so by providing that
3114
- * data into `$animateCss` directly:
3115
- *
3116
- * ```js
3117
- * myModule.animation('.slide', ['$animateCss', function($animateCss) {
3118
- * return {
3119
- * enter: function(element, doneFn) {
3120
- * var animation = $animateCss(element, {
3121
- * event: 'enter',
3122
- * addClass: 'maroon-setting',
3123
- * from: { height:0 },
3124
- * to: { height: 200 }
3125
- * });
3126
- *
3127
- * if (animation) {
3128
- * animation.start().done(doneFn);
3129
- * } else {
3130
- * doneFn();
3131
- * }
3132
- * }
3133
- * }
3134
- * }]
3135
- * ```
3136
- *
3137
- * Now we can fill in the rest via our transition CSS code:
3138
- *
3139
- * ```css
3140
- * /&#42; the transition tells ngAnimate to make the animation happen &#42;/
3141
- * .slide.ng-enter { transition:0.5s linear all; }
3142
- *
3143
- * /&#42; this extra CSS class will be absorbed into the transition
3144
- * since the $animateCss code is adding the class &#42;/
3145
- * .maroon-setting { background:red; }
3146
- * ```
3147
- *
3148
- * And `$animateCss` will figure out the rest. Just make sure to have the `done()` callback fire the `doneFn` function to signal when the animation is over.
3149
- *
3150
- * To learn more about what's possible be sure to visit the {@link ngAnimate.$animateCss $animateCss service}.
3151
- *
3152
- *
3153
- * ## Using $animate in your directive code
3154
- *
3155
- * 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?
3156
- * 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
3157
- * imagine we have a greeting box that shows and hides itself when the data changes
3158
- *
3159
- * ```html
3160
- * <greeing-box active="onOrOff">Hi there</greeting-box>
3161
- * ```
3162
- *
3163
- * ```js
3164
- * ngModule.directive('greetingBox', ['$animate', function($animate) {
3165
- * return function(scope, element, attrs) {
3166
- * attrs.$observe('active', function(value) {
3167
- * value ? $animate.addClass(element, 'on') ? $animate.removeClass(element, 'on');
3168
- * });
3169
- * });
3170
- * }]);
3171
- * ```
3172
- *
3173
- * Now the `on` CSS class is added and removed on the greeting box component. Now if we add a CSS class on top of the greeting box element
3174
- * in our HTML code then we can trigger a CSS or JS animation to happen.
3175
- *
3176
- * ```css
3177
- * /&#42; normally we would create a CSS class to reference on the element &#42;/
3178
- * [greeting-box].on { transition:0.5s linear all; background:green; color:white; }
3179
- * ```
3180
- *
3181
- * The `$animate` service contains a variety of other methods like `enter`, `leave`, `animate` and `setClass`. To learn more about what's
3182
- * possible be sure to visit the {@link ng.$animate $animate service API page}.
3183
- *
3184
- *
3185
- * ### Preventing Collisions With Third Party Libraries
3186
- *
3187
- * Some third-party frameworks place animation duration defaults across many element or className
3188
- * selectors in order to make their code small and reuseable. This can lead to issues with ngAnimate, which
3189
- * is expecting actual animations on these elements and has to wait for their completion.
3190
- *
3191
- * You can prevent this unwanted behavior by using a prefix on all your animation classes:
3192
- *
3193
- * ```css
3194
- * /&#42; prefixed with animate- &#42;/
3195
- * .animate-fade-add.animate-fade-add-active {
3196
- * transition:1s linear all;
3197
- * opacity:0;
3198
- * }
3199
- * ```
3200
- *
3201
- * You then configure `$animate` to enforce this prefix:
3202
- *
3203
- * ```js
3204
- * $animateProvider.classNameFilter(/animate-/);
3205
- * ```
3206
- *
3207
- * This also may provide your application with a speed boost since only specific elements containing CSS class prefix
3208
- * will be evaluated for animation when any DOM changes occur in the application.
3209
- *
3210
- * ## Callbacks and Promises
3211
- *
3212
- * When `$animate` is called it returns a promise that can be used to capture when the animation has ended. Therefore if we were to trigger
3213
- * an animation (within our directive code) then we can continue performing directive and scope related activities after the animation has
3214
- * ended by chaining onto the returned promise that animation method returns.
3215
- *
3216
- * ```js
3217
- * // somewhere within the depths of the directive
3218
- * $animate.enter(element, parent).then(function() {
3219
- * //the animation has completed
3220
- * });
3221
- * ```
3222
- *
3223
- * (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
3224
- * anymore.)
3225
- *
3226
- * In addition to the animation promise, we can also make use of animation-related callbacks within our directives and controller code by registering
3227
- * an event listener using the `$animate` service. Let's say for example that an animation was triggered on our `ng-view` element and we wanted our
3228
- * routing controller to hook into that:
3229
- *
3230
- * ```js
3231
- * ngModule.controller('HomePageController', ['$animate', function($animate) {
3232
- * $animate.on('enter', '[ng-view]', function(element) {
3233
- * // the animation for this route has completed
3234
- * }]);
3235
- * }])
3236
- * ```
3237
- *
3238
- * (Note that you will need to trigger a digest within the callback to get angular to notice any scope-related changes.)
3239
- */
3240
-
3241
- /**
3242
- * @ngdoc service
3243
- * @name $animate
3244
- * @kind object
3245
- *
3246
- * @description
3247
- * The ngAnimate `$animate` service documentation is the same for the core `$animate` service.
3248
- *
3249
- * Click here {@link ng.$animate $animate to learn more about animations with `$animate`}.
3250
- */
3251
- angular.module('ngAnimate', [])
3252
- .directive('ngAnimateChildren', $$AnimateChildrenDirective)
3253
-
3254
- .factory('$$rAFMutex', $$rAFMutexFactory)
3255
-
3256
- .factory('$$AnimateRunner', $$AnimateRunnerFactory)
3257
-
3258
- .provider('$$animateQueue', $$AnimateQueueProvider)
3259
- .provider('$$animation', $$AnimationProvider)
3260
-
3261
- .provider('$animateCss', $AnimateCssProvider)
3262
- .provider('$$animateCssDriver', $$AnimateCssDriverProvider)
3263
-
3264
- .provider('$$animateJs', $$AnimateJsProvider)
3265
- .provider('$$animateJsDriver', $$AnimateJsDriverProvider);
3266
-
3267
-
3268
- })(window, window.angular);