angular-pack 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 045fc40c0d2642373cc8332984696c8bc7f19753
4
+ data.tar.gz: bace2c97d7adb12628fcfa7869a9c53f7af1b055
5
+ SHA512:
6
+ metadata.gz: b3e7805be6f3b620905d57af4968d62d6985e3777002a7dceb1f9636e80b1fcb1da60f794743b8491308220e640f1161a4c398fbad0c85477051847fee8d2ec2
7
+ data.tar.gz: faa01161c85e418bb9523c1e42a333d71535cafcd05a35615f1dbc48586ff47e89d153f00bee7591aaf2dc3bcf38ad2d0e047af7b6fcee97c0f6de3547043bc9
data/.gitignore ADDED
@@ -0,0 +1,23 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
23
+ .idea
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in angular-pack-pack.gemspec
4
+ gemspec
data/MIT-LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Ali Hasan Imam
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,46 @@
1
+ # angular-pack
2
+
3
+ Rails wrapper for AngularJS and AngularUI libraries.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'angular-pack'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install angular-pack
18
+
19
+ ## Usage
20
+
21
+ Add necessary libraries you want to use in your application.js file.
22
+
23
+ //=require angular
24
+ //=require ui.utils
25
+
26
+ Following libraries are available to use:
27
+
28
+ * AngularJS
29
+ * Angular Animate
30
+ * Angular Cookies
31
+ * Angular Loader
32
+ * Angular Mocks
33
+ * Angular Resource
34
+ * Angular Route
35
+ * Angular Sanitize
36
+ * Angular Scenario
37
+ * Angular Touch
38
+ * es5-shim (EcmaScript 5 Support for IE <= 8)
39
+ * NG Grid
40
+ * UI Router
41
+ * UI Bootstrap
42
+ * UI Date
43
+ * UI Map
44
+ * UI Sortable
45
+ * UI Utils
46
+
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,17 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'angular/pack/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "angular-pack"
8
+ spec.version = Angular::Pack::VERSION
9
+ spec.authors = ["Ali Hasan Imam"]
10
+ spec.email = ["imam.aiub@gmail.com"]
11
+ spec.summary = 'Add AngulaJS library to your project.'
12
+ spec.description = 'AngularJS and AngularUI functionality.'
13
+ spec.homepage = "https://github.com/codedefine/angular-pack"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ end
@@ -0,0 +1,6 @@
1
+ require "angular/pack/version"
2
+
3
+ module AngularPack
4
+ class Engine < ::Rails::Engine
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module Angular
2
+ module Pack
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,1483 @@
1
+ /**
2
+ * @license AngularJS v1.2.9
3
+ * (c) 2010-2014 Google, Inc. http://angularjs.org
4
+ * License: MIT
5
+ */
6
+ (function(window, angular, undefined) {'use strict';
7
+
8
+ /* jshint maxlen: false */
9
+
10
+ /**
11
+ * @ngdoc overview
12
+ * @name ngAnimate
13
+ * @description
14
+ *
15
+ * # ngAnimate
16
+ *
17
+ * The `ngAnimate` module provides support for JavaScript, CSS3 transition and CSS3 keyframe animation hooks within existing core and custom directives.
18
+ *
19
+ * {@installModule animate}
20
+ *
21
+ * <div doc-module-components="ngAnimate"></div>
22
+ *
23
+ * # Usage
24
+ *
25
+ * To see animations in action, all that is required is to define the appropriate CSS classes
26
+ * or to register a JavaScript animation via the myModule.animation() function. The directives that support animation automatically are:
27
+ * `ngRepeat`, `ngInclude`, `ngIf`, `ngSwitch`, `ngShow`, `ngHide`, `ngView` and `ngClass`. Custom directives can take advantage of animation
28
+ * by using the `$animate` service.
29
+ *
30
+ * Below is a more detailed breakdown of the supported animation events provided by pre-existing ng directives:
31
+ *
32
+ * | Directive | Supported Animations |
33
+ * |---------------------------------------------------------- |----------------------------------------------------|
34
+ * | {@link ng.directive:ngRepeat#usage_animations ngRepeat} | enter, leave and move |
35
+ * | {@link ngRoute.directive:ngView#usage_animations ngView} | enter and leave |
36
+ * | {@link ng.directive:ngInclude#usage_animations ngInclude} | enter and leave |
37
+ * | {@link ng.directive:ngSwitch#usage_animations ngSwitch} | enter and leave |
38
+ * | {@link ng.directive:ngIf#usage_animations ngIf} | enter and leave |
39
+ * | {@link ng.directive:ngClass#usage_animations ngClass} | add and remove |
40
+ * | {@link ng.directive:ngShow#usage_animations ngShow & ngHide} | add and remove (the ng-hide class value) |
41
+ *
42
+ * You can find out more information about animations upon visiting each directive page.
43
+ *
44
+ * Below is an example of how to apply animations to a directive that supports animation hooks:
45
+ *
46
+ * <pre>
47
+ * <style type="text/css">
48
+ * .slide.ng-enter, .slide.ng-leave {
49
+ * -webkit-transition:0.5s linear all;
50
+ * transition:0.5s linear all;
51
+ * }
52
+ *
53
+ * .slide.ng-enter { } /&#42; starting animations for enter &#42;/
54
+ * .slide.ng-enter-active { } /&#42; terminal animations for enter &#42;/
55
+ * .slide.ng-leave { } /&#42; starting animations for leave &#42;/
56
+ * .slide.ng-leave-active { } /&#42; terminal animations for leave &#42;/
57
+ * </style>
58
+ *
59
+ * <!--
60
+ * the animate service will automatically add .ng-enter and .ng-leave to the element
61
+ * to trigger the CSS transition/animations
62
+ * -->
63
+ * <ANY class="slide" ng-include="..."></ANY>
64
+ * </pre>
65
+ *
66
+ * Keep in mind that if an animation is running, any child elements cannot be animated until the parent element's
67
+ * animation has completed.
68
+ *
69
+ * <h2>CSS-defined Animations</h2>
70
+ * The animate service will automatically apply two CSS classes to the animated element and these two CSS classes
71
+ * are designed to contain the start and end CSS styling. Both CSS transitions and keyframe animations are supported
72
+ * and can be used to play along with this naming structure.
73
+ *
74
+ * The following code below demonstrates how to perform animations using **CSS transitions** with Angular:
75
+ *
76
+ * <pre>
77
+ * <style type="text/css">
78
+ * /&#42;
79
+ * The animate class is apart of the element and the ng-enter class
80
+ * is attached to the element once the enter animation event is triggered
81
+ * &#42;/
82
+ * .reveal-animation.ng-enter {
83
+ * -webkit-transition: 1s linear all; /&#42; Safari/Chrome &#42;/
84
+ * transition: 1s linear all; /&#42; All other modern browsers and IE10+ &#42;/
85
+ *
86
+ * /&#42; The animation preparation code &#42;/
87
+ * opacity: 0;
88
+ * }
89
+ *
90
+ * /&#42;
91
+ * Keep in mind that you want to combine both CSS
92
+ * classes together to avoid any CSS-specificity
93
+ * conflicts
94
+ * &#42;/
95
+ * .reveal-animation.ng-enter.ng-enter-active {
96
+ * /&#42; The animation code itself &#42;/
97
+ * opacity: 1;
98
+ * }
99
+ * </style>
100
+ *
101
+ * <div class="view-container">
102
+ * <div ng-view class="reveal-animation"></div>
103
+ * </div>
104
+ * </pre>
105
+ *
106
+ * The following code below demonstrates how to perform animations using **CSS animations** with Angular:
107
+ *
108
+ * <pre>
109
+ * <style type="text/css">
110
+ * .reveal-animation.ng-enter {
111
+ * -webkit-animation: enter_sequence 1s linear; /&#42; Safari/Chrome &#42;/
112
+ * animation: enter_sequence 1s linear; /&#42; IE10+ and Future Browsers &#42;/
113
+ * }
114
+ * &#64-webkit-keyframes enter_sequence {
115
+ * from { opacity:0; }
116
+ * to { opacity:1; }
117
+ * }
118
+ * &#64keyframes enter_sequence {
119
+ * from { opacity:0; }
120
+ * to { opacity:1; }
121
+ * }
122
+ * </style>
123
+ *
124
+ * <div class="view-container">
125
+ * <div ng-view class="reveal-animation"></div>
126
+ * </div>
127
+ * </pre>
128
+ *
129
+ * Both CSS3 animations and transitions can be used together and the animate service will figure out the correct duration and delay timing.
130
+ *
131
+ * Upon DOM mutation, the event class is added first (something like `ng-enter`), then the browser prepares itself to add
132
+ * the active class (in this case `ng-enter-active`) which then triggers the animation. The animation module will automatically
133
+ * detect the CSS code to determine when the animation ends. Once the animation is over then both CSS classes will be
134
+ * removed from the DOM. If a browser does not support CSS transitions or CSS animations then the animation will start and end
135
+ * immediately resulting in a DOM element that is at its final state. This final state is when the DOM element
136
+ * has no CSS transition/animation classes applied to it.
137
+ *
138
+ * <h3>CSS Staggering Animations</h3>
139
+ * A Staggering animation is a collection of animations that are issued with a slight delay in between each successive operation resulting in a
140
+ * curtain-like effect. The ngAnimate module, as of 1.2.0, supports staggering animations and the stagger effect can be
141
+ * performed by creating a **ng-EVENT-stagger** CSS class and attaching that class to the base CSS class used for
142
+ * the animation. The style property expected within the stagger class can either be a **transition-delay** or an
143
+ * **animation-delay** property (or both if your animation contains both transitions and keyframe animations).
144
+ *
145
+ * <pre>
146
+ * .my-animation.ng-enter {
147
+ * /&#42; standard transition code &#42;/
148
+ * -webkit-transition: 1s linear all;
149
+ * transition: 1s linear all;
150
+ * opacity:0;
151
+ * }
152
+ * .my-animation.ng-enter-stagger {
153
+ * /&#42; this will have a 100ms delay between each successive leave animation &#42;/
154
+ * -webkit-transition-delay: 0.1s;
155
+ * transition-delay: 0.1s;
156
+ *
157
+ * /&#42; in case the stagger doesn't work then these two values
158
+ * must be set to 0 to avoid an accidental CSS inheritance &#42;/
159
+ * -webkit-transition-duration: 0s;
160
+ * transition-duration: 0s;
161
+ * }
162
+ * .my-animation.ng-enter.ng-enter-active {
163
+ * /&#42; standard transition styles &#42;/
164
+ * opacity:1;
165
+ * }
166
+ * </pre>
167
+ *
168
+ * Staggering animations work by default in ngRepeat (so long as the CSS class is defined). Outside of ngRepeat, to use staggering animations
169
+ * on your own, they can be triggered by firing multiple calls to the same event on $animate. However, the restrictions surrounding this
170
+ * are that each of the elements must have the same CSS className value as well as the same parent element. A stagger operation
171
+ * will also be reset if more than 10ms has passed after the last animation has been fired.
172
+ *
173
+ * The following code will issue the **ng-leave-stagger** event on the element provided:
174
+ *
175
+ * <pre>
176
+ * var kids = parent.children();
177
+ *
178
+ * $animate.leave(kids[0]); //stagger index=0
179
+ * $animate.leave(kids[1]); //stagger index=1
180
+ * $animate.leave(kids[2]); //stagger index=2
181
+ * $animate.leave(kids[3]); //stagger index=3
182
+ * $animate.leave(kids[4]); //stagger index=4
183
+ *
184
+ * $timeout(function() {
185
+ * //stagger has reset itself
186
+ * $animate.leave(kids[5]); //stagger index=0
187
+ * $animate.leave(kids[6]); //stagger index=1
188
+ * }, 100, false);
189
+ * </pre>
190
+ *
191
+ * Stagger animations are currently only supported within CSS-defined animations.
192
+ *
193
+ * <h2>JavaScript-defined Animations</h2>
194
+ * In the event that you do not want to use CSS3 transitions or CSS3 animations or if you wish to offer animations on browsers that do not
195
+ * yet support CSS transitions/animations, then you can make use of JavaScript animations defined inside of your AngularJS module.
196
+ *
197
+ * <pre>
198
+ * //!annotate="YourApp" Your AngularJS Module|Replace this or ngModule with the module that you used to define your application.
199
+ * var ngModule = angular-pack.module('YourApp', ['ngAnimate']);
200
+ * ngModule.animation('.my-crazy-animation', function() {
201
+ * return {
202
+ * enter: function(element, done) {
203
+ * //run the animation here and call done when the animation is complete
204
+ * return function(cancelled) {
205
+ * //this (optional) function will be called when the animation
206
+ * //completes or when the animation is cancelled (the cancelled
207
+ * //flag will be set to true if cancelled).
208
+ * };
209
+ * },
210
+ * leave: function(element, done) { },
211
+ * move: function(element, done) { },
212
+ *
213
+ * //animation that can be triggered before the class is added
214
+ * beforeAddClass: function(element, className, done) { },
215
+ *
216
+ * //animation that can be triggered after the class is added
217
+ * addClass: function(element, className, done) { },
218
+ *
219
+ * //animation that can be triggered before the class is removed
220
+ * beforeRemoveClass: function(element, className, done) { },
221
+ *
222
+ * //animation that can be triggered after the class is removed
223
+ * removeClass: function(element, className, done) { }
224
+ * };
225
+ * });
226
+ * </pre>
227
+ *
228
+ * JavaScript-defined animations are created with a CSS-like class selector and a collection of events which are set to run
229
+ * a javascript callback function. When an animation is triggered, $animate will look for a matching animation which fits
230
+ * the element's CSS class attribute value and then run the matching animation event function (if found).
231
+ * In other words, if the CSS classes present on the animated element match any of the JavaScript animations then the callback function will
232
+ * be executed. It should be also noted that only simple, single class selectors are allowed (compound class selectors are not supported).
233
+ *
234
+ * Within a JavaScript animation, an object containing various event callback animation functions is expected to be returned.
235
+ * As explained above, these callbacks are triggered based on the animation event. Therefore if an enter animation is run,
236
+ * and the JavaScript animation is found, then the enter callback will handle that animation (in addition to the CSS keyframe animation
237
+ * or transition code that is defined via a stylesheet).
238
+ *
239
+ */
240
+
241
+ angular.module('ngAnimate', ['ng'])
242
+
243
+ /**
244
+ * @ngdoc object
245
+ * @name ngAnimate.$animateProvider
246
+ * @description
247
+ *
248
+ * The `$animateProvider` allows developers to register JavaScript animation event handlers directly inside of a module.
249
+ * When an animation is triggered, the $animate service will query the $animate service to find any animations that match
250
+ * the provided name value.
251
+ *
252
+ * Requires the {@link ngAnimate `ngAnimate`} module to be installed.
253
+ *
254
+ * Please visit the {@link ngAnimate `ngAnimate`} module overview page learn more about how to use animations in your application.
255
+ *
256
+ */
257
+ .factory('$$animateReflow', ['$window', '$timeout', function($window, $timeout) {
258
+ var requestAnimationFrame = $window.requestAnimationFrame ||
259
+ $window.webkitRequestAnimationFrame ||
260
+ function(fn) {
261
+ return $timeout(fn, 10, false);
262
+ };
263
+
264
+ var cancelAnimationFrame = $window.cancelAnimationFrame ||
265
+ $window.webkitCancelAnimationFrame ||
266
+ function(timer) {
267
+ return $timeout.cancel(timer);
268
+ };
269
+ return function(fn) {
270
+ var id = requestAnimationFrame(fn);
271
+ return function() {
272
+ cancelAnimationFrame(id);
273
+ };
274
+ };
275
+ }])
276
+
277
+ .config(['$provide', '$animateProvider', function($provide, $animateProvider) {
278
+ var noop = angular.noop;
279
+ var forEach = angular.forEach;
280
+ var selectors = $animateProvider.$$selectors;
281
+
282
+ var ELEMENT_NODE = 1;
283
+ var NG_ANIMATE_STATE = '$$ngAnimateState';
284
+ var NG_ANIMATE_CLASS_NAME = 'ng-animate';
285
+ var rootAnimateState = {running: true};
286
+
287
+ function extractElementNode(element) {
288
+ for(var i = 0; i < element.length; i++) {
289
+ var elm = element[i];
290
+ if(elm.nodeType == ELEMENT_NODE) {
291
+ return elm;
292
+ }
293
+ }
294
+ }
295
+
296
+ function isMatchingElement(elm1, elm2) {
297
+ return extractElementNode(elm1) == extractElementNode(elm2);
298
+ }
299
+
300
+ $provide.decorator('$animate', ['$delegate', '$injector', '$sniffer', '$rootElement', '$timeout', '$rootScope', '$document',
301
+ function($delegate, $injector, $sniffer, $rootElement, $timeout, $rootScope, $document) {
302
+
303
+ $rootElement.data(NG_ANIMATE_STATE, rootAnimateState);
304
+
305
+ // disable animations during bootstrap, but once we bootstrapped, wait again
306
+ // for another digest until enabling animations. The reason why we digest twice
307
+ // is because all structural animations (enter, leave and move) all perform a
308
+ // post digest operation before animating. If we only wait for a single digest
309
+ // to pass then the structural animation would render its animation on page load.
310
+ // (which is what we're trying to avoid when the application first boots up.)
311
+ $rootScope.$$postDigest(function() {
312
+ $rootScope.$$postDigest(function() {
313
+ rootAnimateState.running = false;
314
+ });
315
+ });
316
+
317
+ var classNameFilter = $animateProvider.classNameFilter();
318
+ var isAnimatableClassName = !classNameFilter
319
+ ? function() { return true; }
320
+ : function(className) {
321
+ return classNameFilter.test(className);
322
+ };
323
+
324
+ function async(fn) {
325
+ return $timeout(fn, 0, false);
326
+ }
327
+
328
+ function lookup(name) {
329
+ if (name) {
330
+ var matches = [],
331
+ flagMap = {},
332
+ classes = name.substr(1).split('.');
333
+
334
+ //the empty string value is the default animation
335
+ //operation which performs CSS transition and keyframe
336
+ //animations sniffing. This is always included for each
337
+ //element animation procedure if the browser supports
338
+ //transitions and/or keyframe animations
339
+ if ($sniffer.transitions || $sniffer.animations) {
340
+ classes.push('');
341
+ }
342
+
343
+ for(var i=0; i < classes.length; i++) {
344
+ var klass = classes[i],
345
+ selectorFactoryName = selectors[klass];
346
+ if(selectorFactoryName && !flagMap[klass]) {
347
+ matches.push($injector.get(selectorFactoryName));
348
+ flagMap[klass] = true;
349
+ }
350
+ }
351
+ return matches;
352
+ }
353
+ }
354
+
355
+ /**
356
+ * @ngdoc object
357
+ * @name ngAnimate.$animate
358
+ * @function
359
+ *
360
+ * @description
361
+ * The `$animate` service provides animation detection support while performing DOM operations (enter, leave and move) as well as during addClass and removeClass operations.
362
+ * When any of these operations are run, the $animate service
363
+ * will examine any JavaScript-defined animations (which are defined by using the $animateProvider provider object)
364
+ * as well as any CSS-defined animations against the CSS classes present on the element once the DOM operation is run.
365
+ *
366
+ * The `$animate` service is used behind the scenes with pre-existing directives and animation with these directives
367
+ * will work out of the box without any extra configuration.
368
+ *
369
+ * Requires the {@link ngAnimate `ngAnimate`} module to be installed.
370
+ *
371
+ * Please visit the {@link ngAnimate `ngAnimate`} module overview page learn more about how to use animations in your application.
372
+ *
373
+ */
374
+ return {
375
+ /**
376
+ * @ngdoc function
377
+ * @name ngAnimate.$animate#enter
378
+ * @methodOf ngAnimate.$animate
379
+ * @function
380
+ *
381
+ * @description
382
+ * Appends the element to the parentElement element that resides in the document and then runs the enter animation. Once
383
+ * the animation is started, the following CSS classes will be present on the element for the duration of the animation:
384
+ *
385
+ * Below is a breakdown of each step that occurs during enter animation:
386
+ *
387
+ * | Animation Step | What the element class attribute looks like |
388
+ * |----------------------------------------------------------------------------------------------|---------------------------------------------|
389
+ * | 1. $animate.enter(...) is called | class="my-animation" |
390
+ * | 2. element is inserted into the parentElement element or beside the afterElement element | class="my-animation" |
391
+ * | 3. $animate runs any JavaScript-defined animations on the element | class="my-animation ng-animate" |
392
+ * | 4. the .ng-enter class is added to the element | class="my-animation ng-animate ng-enter" |
393
+ * | 5. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate ng-enter" |
394
+ * | 6. $animate waits for 10ms (this performs a reflow) | class="my-animation ng-animate ng-enter" |
395
+ * | 7. the .ng-enter-active and .ng-animate-active classes are added (this triggers the CSS transition/animation) | class="my-animation ng-animate ng-animate-active ng-enter ng-enter-active" |
396
+ * | 8. $animate waits for X milliseconds for the animation to complete | class="my-animation ng-animate ng-animate-active ng-enter ng-enter-active" |
397
+ * | 9. The animation ends and all generated CSS classes are removed from the element | class="my-animation" |
398
+ * | 10. The doneCallback() callback is fired (if provided) | class="my-animation" |
399
+ *
400
+ * @param {jQuery/jqLite element} element the element that will be the focus of the enter animation
401
+ * @param {jQuery/jqLite element} parentElement the parent element of the element that will be the focus of the enter animation
402
+ * @param {jQuery/jqLite element} afterElement the sibling element (which is the previous element) of the element that will be the focus of the enter animation
403
+ * @param {function()=} doneCallback the callback function that will be called once the animation is complete
404
+ */
405
+ enter : function(element, parentElement, afterElement, doneCallback) {
406
+ this.enabled(false, element);
407
+ $delegate.enter(element, parentElement, afterElement);
408
+ $rootScope.$$postDigest(function() {
409
+ performAnimation('enter', 'ng-enter', element, parentElement, afterElement, noop, doneCallback);
410
+ });
411
+ },
412
+
413
+ /**
414
+ * @ngdoc function
415
+ * @name ngAnimate.$animate#leave
416
+ * @methodOf ngAnimate.$animate
417
+ * @function
418
+ *
419
+ * @description
420
+ * Runs the leave animation operation and, upon completion, removes the element from the DOM. Once
421
+ * the animation is started, the following CSS classes will be added for the duration of the animation:
422
+ *
423
+ * Below is a breakdown of each step that occurs during leave animation:
424
+ *
425
+ * | Animation Step | What the element class attribute looks like |
426
+ * |----------------------------------------------------------------------------------------------|---------------------------------------------|
427
+ * | 1. $animate.leave(...) is called | class="my-animation" |
428
+ * | 2. $animate runs any JavaScript-defined animations on the element | class="my-animation ng-animate" |
429
+ * | 3. the .ng-leave class is added to the element | class="my-animation ng-animate ng-leave" |
430
+ * | 4. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate ng-leave" |
431
+ * | 5. $animate waits for 10ms (this performs a reflow) | class="my-animation ng-animate ng-leave" |
432
+ * | 6. the .ng-leave-active and .ng-animate-active classes is added (this triggers the CSS transition/animation) | class="my-animation ng-animate ng-animate-active ng-leave ng-leave-active" |
433
+ * | 7. $animate waits for X milliseconds for the animation to complete | class="my-animation ng-animate ng-animate-active ng-leave ng-leave-active" |
434
+ * | 8. The animation ends and all generated CSS classes are removed from the element | class="my-animation" |
435
+ * | 9. The element is removed from the DOM | ... |
436
+ * | 10. The doneCallback() callback is fired (if provided) | ... |
437
+ *
438
+ * @param {jQuery/jqLite element} element the element that will be the focus of the leave animation
439
+ * @param {function()=} doneCallback the callback function that will be called once the animation is complete
440
+ */
441
+ leave : function(element, doneCallback) {
442
+ cancelChildAnimations(element);
443
+ this.enabled(false, element);
444
+ $rootScope.$$postDigest(function() {
445
+ performAnimation('leave', 'ng-leave', element, null, null, function() {
446
+ $delegate.leave(element);
447
+ }, doneCallback);
448
+ });
449
+ },
450
+
451
+ /**
452
+ * @ngdoc function
453
+ * @name ngAnimate.$animate#move
454
+ * @methodOf ngAnimate.$animate
455
+ * @function
456
+ *
457
+ * @description
458
+ * Fires the move DOM operation. Just before the animation starts, the animate service will either append it into the parentElement container or
459
+ * add the element directly after the afterElement element if present. Then the move animation will be run. Once
460
+ * the animation is started, the following CSS classes will be added for the duration of the animation:
461
+ *
462
+ * Below is a breakdown of each step that occurs during move animation:
463
+ *
464
+ * | Animation Step | What the element class attribute looks like |
465
+ * |----------------------------------------------------------------------------------------------|---------------------------------------------|
466
+ * | 1. $animate.move(...) is called | class="my-animation" |
467
+ * | 2. element is moved into the parentElement element or beside the afterElement element | class="my-animation" |
468
+ * | 3. $animate runs any JavaScript-defined animations on the element | class="my-animation ng-animate" |
469
+ * | 4. the .ng-move class is added to the element | class="my-animation ng-animate ng-move" |
470
+ * | 5. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate ng-move" |
471
+ * | 6. $animate waits for 10ms (this performs a reflow) | class="my-animation ng-animate ng-move" |
472
+ * | 7. the .ng-move-active and .ng-animate-active classes is added (this triggers the CSS transition/animation) | class="my-animation ng-animate ng-animate-active ng-move ng-move-active" |
473
+ * | 8. $animate waits for X milliseconds for the animation to complete | class="my-animation ng-animate ng-animate-active ng-move ng-move-active" |
474
+ * | 9. The animation ends and all generated CSS classes are removed from the element | class="my-animation" |
475
+ * | 10. The doneCallback() callback is fired (if provided) | class="my-animation" |
476
+ *
477
+ * @param {jQuery/jqLite element} element the element that will be the focus of the move animation
478
+ * @param {jQuery/jqLite element} parentElement the parentElement element of the element that will be the focus of the move animation
479
+ * @param {jQuery/jqLite element} afterElement the sibling element (which is the previous element) of the element that will be the focus of the move animation
480
+ * @param {function()=} doneCallback the callback function that will be called once the animation is complete
481
+ */
482
+ move : function(element, parentElement, afterElement, doneCallback) {
483
+ cancelChildAnimations(element);
484
+ this.enabled(false, element);
485
+ $delegate.move(element, parentElement, afterElement);
486
+ $rootScope.$$postDigest(function() {
487
+ performAnimation('move', 'ng-move', element, parentElement, afterElement, noop, doneCallback);
488
+ });
489
+ },
490
+
491
+ /**
492
+ * @ngdoc function
493
+ * @name ngAnimate.$animate#addClass
494
+ * @methodOf ngAnimate.$animate
495
+ *
496
+ * @description
497
+ * Triggers a custom animation event based off the className variable and then attaches the className value to the element as a CSS class.
498
+ * Unlike the other animation methods, the animate service will suffix the className value with {@type -add} in order to provide
499
+ * the animate service the setup and active CSS classes in order to trigger the animation (this will be skipped if no CSS transitions
500
+ * or keyframes are defined on the -add or base CSS class).
501
+ *
502
+ * Below is a breakdown of each step that occurs during addClass animation:
503
+ *
504
+ * | Animation Step | What the element class attribute looks like |
505
+ * |------------------------------------------------------------------------------------------------|---------------------------------------------|
506
+ * | 1. $animate.addClass(element, 'super') is called | class="my-animation" |
507
+ * | 2. $animate runs any JavaScript-defined animations on the element | class="my-animation ng-animate" |
508
+ * | 3. the .super-add class are added to the element | class="my-animation ng-animate super-add" |
509
+ * | 4. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate super-add" |
510
+ * | 5. $animate waits for 10ms (this performs a reflow) | class="my-animation ng-animate super-add" |
511
+ * | 6. the .super, .super-add-active and .ng-animate-active classes are added (this triggers the CSS transition/animation) | class="my-animation ng-animate ng-animate-active super super-add super-add-active" |
512
+ * | 7. $animate waits for X milliseconds for the animation to complete | class="my-animation super-add super-add-active" |
513
+ * | 8. The animation ends and all generated CSS classes are removed from the element | class="my-animation super" |
514
+ * | 9. The super class is kept on the element | class="my-animation super" |
515
+ * | 10. The doneCallback() callback is fired (if provided) | class="my-animation super" |
516
+ *
517
+ * @param {jQuery/jqLite element} element the element that will be animated
518
+ * @param {string} className the CSS class that will be added to the element and then animated
519
+ * @param {function()=} doneCallback the callback function that will be called once the animation is complete
520
+ */
521
+ addClass : function(element, className, doneCallback) {
522
+ performAnimation('addClass', className, element, null, null, function() {
523
+ $delegate.addClass(element, className);
524
+ }, doneCallback);
525
+ },
526
+
527
+ /**
528
+ * @ngdoc function
529
+ * @name ngAnimate.$animate#removeClass
530
+ * @methodOf ngAnimate.$animate
531
+ *
532
+ * @description
533
+ * Triggers a custom animation event based off the className variable and then removes the CSS class provided by the className value
534
+ * from the element. Unlike the other animation methods, the animate service will suffix the className value with {@type -remove} in
535
+ * order to provide the animate service the setup and active CSS classes in order to trigger the animation (this will be skipped if
536
+ * no CSS transitions or keyframes are defined on the -remove or base CSS classes).
537
+ *
538
+ * Below is a breakdown of each step that occurs during removeClass animation:
539
+ *
540
+ * | Animation Step | What the element class attribute looks like |
541
+ * |-----------------------------------------------------------------------------------------------|---------------------------------------------|
542
+ * | 1. $animate.removeClass(element, 'super') is called | class="my-animation super" |
543
+ * | 2. $animate runs any JavaScript-defined animations on the element | class="my-animation super ng-animate" |
544
+ * | 3. the .super-remove class are added to the element | class="my-animation super ng-animate super-remove"|
545
+ * | 4. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation super ng-animate super-remove" |
546
+ * | 5. $animate waits for 10ms (this performs a reflow) | class="my-animation super ng-animate super-remove" |
547
+ * | 6. the .super-remove-active and .ng-animate-active classes are added and .super is removed (this triggers the CSS transition/animation) | class="my-animation ng-animate ng-animate-active super-remove super-remove-active" |
548
+ * | 7. $animate waits for X milliseconds for the animation to complete | class="my-animation ng-animate ng-animate-active super-remove super-remove-active" |
549
+ * | 8. The animation ends and all generated CSS classes are removed from the element | class="my-animation" |
550
+ * | 9. The doneCallback() callback is fired (if provided) | class="my-animation" |
551
+ *
552
+ *
553
+ * @param {jQuery/jqLite element} element the element that will be animated
554
+ * @param {string} className the CSS class that will be animated and then removed from the element
555
+ * @param {function()=} doneCallback the callback function that will be called once the animation is complete
556
+ */
557
+ removeClass : function(element, className, doneCallback) {
558
+ performAnimation('removeClass', className, element, null, null, function() {
559
+ $delegate.removeClass(element, className);
560
+ }, doneCallback);
561
+ },
562
+
563
+ /**
564
+ * @ngdoc function
565
+ * @name ngAnimate.$animate#enabled
566
+ * @methodOf ngAnimate.$animate
567
+ * @function
568
+ *
569
+ * @param {boolean=} value If provided then set the animation on or off.
570
+ * @param {jQuery/jqLite element=} element If provided then the element will be used to represent the enable/disable operation
571
+ * @return {boolean} Current animation state.
572
+ *
573
+ * @description
574
+ * Globally enables/disables animations.
575
+ *
576
+ */
577
+ enabled : function(value, element) {
578
+ switch(arguments.length) {
579
+ case 2:
580
+ if(value) {
581
+ cleanup(element);
582
+ } else {
583
+ var data = element.data(NG_ANIMATE_STATE) || {};
584
+ data.disabled = true;
585
+ element.data(NG_ANIMATE_STATE, data);
586
+ }
587
+ break;
588
+
589
+ case 1:
590
+ rootAnimateState.disabled = !value;
591
+ break;
592
+
593
+ default:
594
+ value = !rootAnimateState.disabled;
595
+ break;
596
+ }
597
+ return !!value;
598
+ }
599
+ };
600
+
601
+ /*
602
+ all animations call this shared animation triggering function internally.
603
+ The animationEvent variable refers to the JavaScript animation event that will be triggered
604
+ and the className value is the name of the animation that will be applied within the
605
+ CSS code. Element, parentElement and afterElement are provided DOM elements for the animation
606
+ and the onComplete callback will be fired once the animation is fully complete.
607
+ */
608
+ function performAnimation(animationEvent, className, element, parentElement, afterElement, domOperation, doneCallback) {
609
+ var currentClassName, classes, node = extractElementNode(element);
610
+ if(node) {
611
+ currentClassName = node.className;
612
+ classes = currentClassName + ' ' + className;
613
+ }
614
+
615
+ //transcluded directives may sometimes fire an animation using only comment nodes
616
+ //best to catch this early on to prevent any animation operations from occurring
617
+ if(!node || !isAnimatableClassName(classes)) {
618
+ fireDOMOperation();
619
+ fireBeforeCallbackAsync();
620
+ fireAfterCallbackAsync();
621
+ closeAnimation();
622
+ return;
623
+ }
624
+
625
+ var animationLookup = (' ' + classes).replace(/\s+/g,'.');
626
+ if (!parentElement) {
627
+ parentElement = afterElement ? afterElement.parent() : element.parent();
628
+ }
629
+
630
+ var matches = lookup(animationLookup);
631
+ var isClassBased = animationEvent == 'addClass' || animationEvent == 'removeClass';
632
+ var ngAnimateState = element.data(NG_ANIMATE_STATE) || {};
633
+
634
+ //skip the animation if animations are disabled, a parent is already being animated,
635
+ //the element is not currently attached to the document body or then completely close
636
+ //the animation if any matching animations are not found at all.
637
+ //NOTE: IE8 + IE9 should close properly (run closeAnimation()) in case a NO animation is not found.
638
+ if (animationsDisabled(element, parentElement) || matches.length === 0) {
639
+ fireDOMOperation();
640
+ fireBeforeCallbackAsync();
641
+ fireAfterCallbackAsync();
642
+ closeAnimation();
643
+ return;
644
+ }
645
+
646
+ var animations = [];
647
+
648
+ //only add animations if the currently running animation is not structural
649
+ //or if there is no animation running at all
650
+ var allowAnimations = isClassBased ?
651
+ !ngAnimateState.disabled && (!ngAnimateState.running || !ngAnimateState.structural) :
652
+ true;
653
+
654
+ if(allowAnimations) {
655
+ forEach(matches, function(animation) {
656
+ //add the animation to the queue to if it is allowed to be cancelled
657
+ if(!animation.allowCancel || animation.allowCancel(element, animationEvent, className)) {
658
+ var beforeFn, afterFn = animation[animationEvent];
659
+
660
+ //Special case for a leave animation since there is no point in performing an
661
+ //animation on a element node that has already been removed from the DOM
662
+ if(animationEvent == 'leave') {
663
+ beforeFn = afterFn;
664
+ afterFn = null; //this must be falsy so that the animation is skipped for leave
665
+ } else {
666
+ beforeFn = animation['before' + animationEvent.charAt(0).toUpperCase() + animationEvent.substr(1)];
667
+ }
668
+ animations.push({
669
+ before : beforeFn,
670
+ after : afterFn
671
+ });
672
+ }
673
+ });
674
+ }
675
+
676
+ //this would mean that an animation was not allowed so let the existing
677
+ //animation do it's thing and close this one early
678
+ if(animations.length === 0) {
679
+ fireDOMOperation();
680
+ fireBeforeCallbackAsync();
681
+ fireAfterCallbackAsync();
682
+ fireDoneCallbackAsync();
683
+ return;
684
+ }
685
+
686
+ var ONE_SPACE = ' ';
687
+ //this value will be searched for class-based CSS className lookup. Therefore,
688
+ //we prefix and suffix the current className value with spaces to avoid substring
689
+ //lookups of className tokens
690
+ var futureClassName = ONE_SPACE + currentClassName + ONE_SPACE;
691
+ if(ngAnimateState.running) {
692
+ //if an animation is currently running on the element then lets take the steps
693
+ //to cancel that animation and fire any required callbacks
694
+ $timeout.cancel(ngAnimateState.closeAnimationTimeout);
695
+ cleanup(element);
696
+ cancelAnimations(ngAnimateState.animations);
697
+
698
+ //in the event that the CSS is class is quickly added and removed back
699
+ //then we don't want to wait until after the reflow to add/remove the CSS
700
+ //class since both class animations may run into a race condition.
701
+ //The code below will check to see if that is occurring and will
702
+ //immediately remove the former class before the reflow so that the
703
+ //animation can snap back to the original animation smoothly
704
+ var isFullyClassBasedAnimation = isClassBased && !ngAnimateState.structural;
705
+ var isRevertingClassAnimation = isFullyClassBasedAnimation &&
706
+ ngAnimateState.className == className &&
707
+ animationEvent != ngAnimateState.event;
708
+
709
+ //if the class is removed during the reflow then it will revert the styles temporarily
710
+ //back to the base class CSS styling causing a jump-like effect to occur. This check
711
+ //here ensures that the domOperation is only performed after the reflow has commenced
712
+ if(ngAnimateState.beforeComplete || isRevertingClassAnimation) {
713
+ (ngAnimateState.done || noop)(true);
714
+ } else if(isFullyClassBasedAnimation) {
715
+ //class-based animations will compare element className values after cancelling the
716
+ //previous animation to see if the element properties already contain the final CSS
717
+ //class and if so then the animation will be skipped. Since the domOperation will
718
+ //be performed only after the reflow is complete then our element's className value
719
+ //will be invalid. Therefore the same string manipulation that would occur within the
720
+ //DOM operation will be performed below so that the class comparison is valid...
721
+ futureClassName = ngAnimateState.event == 'removeClass' ?
722
+ futureClassName.replace(ONE_SPACE + ngAnimateState.className + ONE_SPACE, ONE_SPACE) :
723
+ futureClassName + ngAnimateState.className + ONE_SPACE;
724
+ }
725
+ }
726
+
727
+ //There is no point in perform a class-based animation if the element already contains
728
+ //(on addClass) or doesn't contain (on removeClass) the className being animated.
729
+ //The reason why this is being called after the previous animations are cancelled
730
+ //is so that the CSS classes present on the element can be properly examined.
731
+ var classNameToken = ONE_SPACE + className + ONE_SPACE;
732
+ if((animationEvent == 'addClass' && futureClassName.indexOf(classNameToken) >= 0) ||
733
+ (animationEvent == 'removeClass' && futureClassName.indexOf(classNameToken) == -1)) {
734
+ fireDOMOperation();
735
+ fireBeforeCallbackAsync();
736
+ fireAfterCallbackAsync();
737
+ fireDoneCallbackAsync();
738
+ return;
739
+ }
740
+
741
+ //the ng-animate class does nothing, but it's here to allow for
742
+ //parent animations to find and cancel child animations when needed
743
+ element.addClass(NG_ANIMATE_CLASS_NAME);
744
+
745
+ element.data(NG_ANIMATE_STATE, {
746
+ running:true,
747
+ event:animationEvent,
748
+ className:className,
749
+ structural:!isClassBased,
750
+ animations:animations,
751
+ done:onBeforeAnimationsComplete
752
+ });
753
+
754
+ //first we run the before animations and when all of those are complete
755
+ //then we perform the DOM operation and run the next set of animations
756
+ invokeRegisteredAnimationFns(animations, 'before', onBeforeAnimationsComplete);
757
+
758
+ function onBeforeAnimationsComplete(cancelled) {
759
+ fireDOMOperation();
760
+ if(cancelled === true) {
761
+ closeAnimation();
762
+ return;
763
+ }
764
+
765
+ //set the done function to the final done function
766
+ //so that the DOM event won't be executed twice by accident
767
+ //if the after animation is cancelled as well
768
+ var data = element.data(NG_ANIMATE_STATE);
769
+ if(data) {
770
+ data.done = closeAnimation;
771
+ element.data(NG_ANIMATE_STATE, data);
772
+ }
773
+ invokeRegisteredAnimationFns(animations, 'after', closeAnimation);
774
+ }
775
+
776
+ function invokeRegisteredAnimationFns(animations, phase, allAnimationFnsComplete) {
777
+ phase == 'after' ?
778
+ fireAfterCallbackAsync() :
779
+ fireBeforeCallbackAsync();
780
+
781
+ var endFnName = phase + 'End';
782
+ forEach(animations, function(animation, index) {
783
+ var animationPhaseCompleted = function() {
784
+ progress(index, phase);
785
+ };
786
+
787
+ //there are no before functions for enter + move since the DOM
788
+ //operations happen before the performAnimation method fires
789
+ if(phase == 'before' && (animationEvent == 'enter' || animationEvent == 'move')) {
790
+ animationPhaseCompleted();
791
+ return;
792
+ }
793
+
794
+ if(animation[phase]) {
795
+ animation[endFnName] = isClassBased ?
796
+ animation[phase](element, className, animationPhaseCompleted) :
797
+ animation[phase](element, animationPhaseCompleted);
798
+ } else {
799
+ animationPhaseCompleted();
800
+ }
801
+ });
802
+
803
+ function progress(index, phase) {
804
+ var phaseCompletionFlag = phase + 'Complete';
805
+ var currentAnimation = animations[index];
806
+ currentAnimation[phaseCompletionFlag] = true;
807
+ (currentAnimation[endFnName] || noop)();
808
+
809
+ for(var i=0;i<animations.length;i++) {
810
+ if(!animations[i][phaseCompletionFlag]) return;
811
+ }
812
+
813
+ allAnimationFnsComplete();
814
+ }
815
+ }
816
+
817
+ function fireDOMCallback(animationPhase) {
818
+ element.triggerHandler('$animate:' + animationPhase, {
819
+ event : animationEvent,
820
+ className : className
821
+ });
822
+ }
823
+
824
+ function fireBeforeCallbackAsync() {
825
+ async(function() {
826
+ fireDOMCallback('before');
827
+ });
828
+ }
829
+
830
+ function fireAfterCallbackAsync() {
831
+ async(function() {
832
+ fireDOMCallback('after');
833
+ });
834
+ }
835
+
836
+ function fireDoneCallbackAsync() {
837
+ doneCallback && async(doneCallback);
838
+ }
839
+
840
+ //it is less complicated to use a flag than managing and cancelling
841
+ //timeouts containing multiple callbacks.
842
+ function fireDOMOperation() {
843
+ if(!fireDOMOperation.hasBeenRun) {
844
+ fireDOMOperation.hasBeenRun = true;
845
+ domOperation();
846
+ }
847
+ }
848
+
849
+ function closeAnimation() {
850
+ if(!closeAnimation.hasBeenRun) {
851
+ closeAnimation.hasBeenRun = true;
852
+ var data = element.data(NG_ANIMATE_STATE);
853
+ if(data) {
854
+ /* only structural animations wait for reflow before removing an
855
+ animation, but class-based animations don't. An example of this
856
+ failing would be when a parent HTML tag has a ng-class attribute
857
+ causing ALL directives below to skip animations during the digest */
858
+ if(isClassBased) {
859
+ cleanup(element);
860
+ } else {
861
+ data.closeAnimationTimeout = async(function() {
862
+ cleanup(element);
863
+ });
864
+ element.data(NG_ANIMATE_STATE, data);
865
+ }
866
+ }
867
+ fireDoneCallbackAsync();
868
+ }
869
+ }
870
+ }
871
+
872
+ function cancelChildAnimations(element) {
873
+ var node = extractElementNode(element);
874
+ forEach(node.querySelectorAll('.' + NG_ANIMATE_CLASS_NAME), function(element) {
875
+ element = angular.element(element);
876
+ var data = element.data(NG_ANIMATE_STATE);
877
+ if(data) {
878
+ cancelAnimations(data.animations);
879
+ cleanup(element);
880
+ }
881
+ });
882
+ }
883
+
884
+ function cancelAnimations(animations) {
885
+ var isCancelledFlag = true;
886
+ forEach(animations, function(animation) {
887
+ if(!animation.beforeComplete) {
888
+ (animation.beforeEnd || noop)(isCancelledFlag);
889
+ }
890
+ if(!animation.afterComplete) {
891
+ (animation.afterEnd || noop)(isCancelledFlag);
892
+ }
893
+ });
894
+ }
895
+
896
+ function cleanup(element) {
897
+ if(isMatchingElement(element, $rootElement)) {
898
+ if(!rootAnimateState.disabled) {
899
+ rootAnimateState.running = false;
900
+ rootAnimateState.structural = false;
901
+ }
902
+ } else {
903
+ element.removeClass(NG_ANIMATE_CLASS_NAME);
904
+ element.removeData(NG_ANIMATE_STATE);
905
+ }
906
+ }
907
+
908
+ function animationsDisabled(element, parentElement) {
909
+ if (rootAnimateState.disabled) return true;
910
+
911
+ if(isMatchingElement(element, $rootElement)) {
912
+ return rootAnimateState.disabled || rootAnimateState.running;
913
+ }
914
+
915
+ do {
916
+ //the element did not reach the root element which means that it
917
+ //is not apart of the DOM. Therefore there is no reason to do
918
+ //any animations on it
919
+ if(parentElement.length === 0) break;
920
+
921
+ var isRoot = isMatchingElement(parentElement, $rootElement);
922
+ var state = isRoot ? rootAnimateState : parentElement.data(NG_ANIMATE_STATE);
923
+ var result = state && (!!state.disabled || !!state.running);
924
+ if(isRoot || result) {
925
+ return result;
926
+ }
927
+
928
+ if(isRoot) return true;
929
+ }
930
+ while(parentElement = parentElement.parent());
931
+
932
+ return true;
933
+ }
934
+ }]);
935
+
936
+ $animateProvider.register('', ['$window', '$sniffer', '$timeout', '$$animateReflow',
937
+ function($window, $sniffer, $timeout, $$animateReflow) {
938
+ // Detect proper transitionend/animationend event names.
939
+ var CSS_PREFIX = '', TRANSITION_PROP, TRANSITIONEND_EVENT, ANIMATION_PROP, ANIMATIONEND_EVENT;
940
+
941
+ // If unprefixed events are not supported but webkit-prefixed are, use the latter.
942
+ // Otherwise, just use W3C names, browsers not supporting them at all will just ignore them.
943
+ // Note: Chrome implements `window.onwebkitanimationend` and doesn't implement `window.onanimationend`
944
+ // but at the same time dispatches the `animationend` event and not `webkitAnimationEnd`.
945
+ // Register both events in case `window.onanimationend` is not supported because of that,
946
+ // do the same for `transitionend` as Safari is likely to exhibit similar behavior.
947
+ // Also, the only modern browser that uses vendor prefixes for transitions/keyframes is webkit
948
+ // therefore there is no reason to test anymore for other vendor prefixes: http://caniuse.com/#search=transition
949
+ if (window.ontransitionend === undefined && window.onwebkittransitionend !== undefined) {
950
+ CSS_PREFIX = '-webkit-';
951
+ TRANSITION_PROP = 'WebkitTransition';
952
+ TRANSITIONEND_EVENT = 'webkitTransitionEnd transitionend';
953
+ } else {
954
+ TRANSITION_PROP = 'transition';
955
+ TRANSITIONEND_EVENT = 'transitionend';
956
+ }
957
+
958
+ if (window.onanimationend === undefined && window.onwebkitanimationend !== undefined) {
959
+ CSS_PREFIX = '-webkit-';
960
+ ANIMATION_PROP = 'WebkitAnimation';
961
+ ANIMATIONEND_EVENT = 'webkitAnimationEnd animationend';
962
+ } else {
963
+ ANIMATION_PROP = 'animation';
964
+ ANIMATIONEND_EVENT = 'animationend';
965
+ }
966
+
967
+ var DURATION_KEY = 'Duration';
968
+ var PROPERTY_KEY = 'Property';
969
+ var DELAY_KEY = 'Delay';
970
+ var ANIMATION_ITERATION_COUNT_KEY = 'IterationCount';
971
+ var NG_ANIMATE_PARENT_KEY = '$$ngAnimateKey';
972
+ var NG_ANIMATE_CSS_DATA_KEY = '$$ngAnimateCSS3Data';
973
+ var ELAPSED_TIME_MAX_DECIMAL_PLACES = 3;
974
+ var CLOSING_TIME_BUFFER = 1.5;
975
+ var ONE_SECOND = 1000;
976
+
977
+ var animationCounter = 0;
978
+ var lookupCache = {};
979
+ var parentCounter = 0;
980
+ var animationReflowQueue = [];
981
+ var animationElementQueue = [];
982
+ var cancelAnimationReflow;
983
+ var closingAnimationTime = 0;
984
+ var timeOut = false;
985
+ function afterReflow(element, callback) {
986
+ if(cancelAnimationReflow) {
987
+ cancelAnimationReflow();
988
+ }
989
+
990
+ animationReflowQueue.push(callback);
991
+
992
+ var node = extractElementNode(element);
993
+ element = angular.element(node);
994
+ animationElementQueue.push(element);
995
+
996
+ var elementData = element.data(NG_ANIMATE_CSS_DATA_KEY);
997
+
998
+ var stagger = elementData.stagger;
999
+ var staggerTime = elementData.itemIndex * (Math.max(stagger.animationDelay, stagger.transitionDelay) || 0);
1000
+
1001
+ var animationTime = (elementData.maxDelay + elementData.maxDuration) * CLOSING_TIME_BUFFER;
1002
+ closingAnimationTime = Math.max(closingAnimationTime, (staggerTime + animationTime) * ONE_SECOND);
1003
+
1004
+ //by placing a counter we can avoid an accidental
1005
+ //race condition which may close an animation when
1006
+ //a follow-up animation is midway in its animation
1007
+ elementData.animationCount = animationCounter;
1008
+
1009
+ cancelAnimationReflow = $$animateReflow(function() {
1010
+ forEach(animationReflowQueue, function(fn) {
1011
+ fn();
1012
+ });
1013
+
1014
+ //copy the list of elements so that successive
1015
+ //animations won't conflict if they're added before
1016
+ //the closing animation timeout has run
1017
+ var elementQueueSnapshot = [];
1018
+ var animationCounterSnapshot = animationCounter;
1019
+ forEach(animationElementQueue, function(elm) {
1020
+ elementQueueSnapshot.push(elm);
1021
+ });
1022
+
1023
+ $timeout(function() {
1024
+ closeAllAnimations(elementQueueSnapshot, animationCounterSnapshot);
1025
+ elementQueueSnapshot = null;
1026
+ }, closingAnimationTime, false);
1027
+
1028
+ animationReflowQueue = [];
1029
+ animationElementQueue = [];
1030
+ cancelAnimationReflow = null;
1031
+ lookupCache = {};
1032
+ closingAnimationTime = 0;
1033
+ animationCounter++;
1034
+ });
1035
+ }
1036
+
1037
+ function closeAllAnimations(elements, count) {
1038
+ forEach(elements, function(element) {
1039
+ var elementData = element.data(NG_ANIMATE_CSS_DATA_KEY);
1040
+ if(elementData && elementData.animationCount == count) {
1041
+ (elementData.closeAnimationFn || noop)();
1042
+ }
1043
+ });
1044
+ }
1045
+
1046
+ function getElementAnimationDetails(element, cacheKey) {
1047
+ var data = cacheKey ? lookupCache[cacheKey] : null;
1048
+ if(!data) {
1049
+ var transitionDuration = 0;
1050
+ var transitionDelay = 0;
1051
+ var animationDuration = 0;
1052
+ var animationDelay = 0;
1053
+ var transitionDelayStyle;
1054
+ var animationDelayStyle;
1055
+ var transitionDurationStyle;
1056
+ var transitionPropertyStyle;
1057
+
1058
+ //we want all the styles defined before and after
1059
+ forEach(element, function(element) {
1060
+ if (element.nodeType == ELEMENT_NODE) {
1061
+ var elementStyles = $window.getComputedStyle(element) || {};
1062
+
1063
+ transitionDurationStyle = elementStyles[TRANSITION_PROP + DURATION_KEY];
1064
+
1065
+ transitionDuration = Math.max(parseMaxTime(transitionDurationStyle), transitionDuration);
1066
+
1067
+ transitionPropertyStyle = elementStyles[TRANSITION_PROP + PROPERTY_KEY];
1068
+
1069
+ transitionDelayStyle = elementStyles[TRANSITION_PROP + DELAY_KEY];
1070
+
1071
+ transitionDelay = Math.max(parseMaxTime(transitionDelayStyle), transitionDelay);
1072
+
1073
+ animationDelayStyle = elementStyles[ANIMATION_PROP + DELAY_KEY];
1074
+
1075
+ animationDelay = Math.max(parseMaxTime(animationDelayStyle), animationDelay);
1076
+
1077
+ var aDuration = parseMaxTime(elementStyles[ANIMATION_PROP + DURATION_KEY]);
1078
+
1079
+ if(aDuration > 0) {
1080
+ aDuration *= parseInt(elementStyles[ANIMATION_PROP + ANIMATION_ITERATION_COUNT_KEY], 10) || 1;
1081
+ }
1082
+
1083
+ animationDuration = Math.max(aDuration, animationDuration);
1084
+ }
1085
+ });
1086
+ data = {
1087
+ total : 0,
1088
+ transitionPropertyStyle: transitionPropertyStyle,
1089
+ transitionDurationStyle: transitionDurationStyle,
1090
+ transitionDelayStyle: transitionDelayStyle,
1091
+ transitionDelay: transitionDelay,
1092
+ transitionDuration: transitionDuration,
1093
+ animationDelayStyle: animationDelayStyle,
1094
+ animationDelay: animationDelay,
1095
+ animationDuration: animationDuration
1096
+ };
1097
+ if(cacheKey) {
1098
+ lookupCache[cacheKey] = data;
1099
+ }
1100
+ }
1101
+ return data;
1102
+ }
1103
+
1104
+ function parseMaxTime(str) {
1105
+ var maxValue = 0;
1106
+ var values = angular.isString(str) ?
1107
+ str.split(/\s*,\s*/) :
1108
+ [];
1109
+ forEach(values, function(value) {
1110
+ maxValue = Math.max(parseFloat(value) || 0, maxValue);
1111
+ });
1112
+ return maxValue;
1113
+ }
1114
+
1115
+ function getCacheKey(element) {
1116
+ var parentElement = element.parent();
1117
+ var parentID = parentElement.data(NG_ANIMATE_PARENT_KEY);
1118
+ if(!parentID) {
1119
+ parentElement.data(NG_ANIMATE_PARENT_KEY, ++parentCounter);
1120
+ parentID = parentCounter;
1121
+ }
1122
+ return parentID + '-' + extractElementNode(element).className;
1123
+ }
1124
+
1125
+ function animateSetup(element, className, calculationDecorator) {
1126
+ var cacheKey = getCacheKey(element);
1127
+ var eventCacheKey = cacheKey + ' ' + className;
1128
+ var stagger = {};
1129
+ var itemIndex = lookupCache[eventCacheKey] ? ++lookupCache[eventCacheKey].total : 0;
1130
+
1131
+ if(itemIndex > 0) {
1132
+ var staggerClassName = className + '-stagger';
1133
+ var staggerCacheKey = cacheKey + ' ' + staggerClassName;
1134
+ var applyClasses = !lookupCache[staggerCacheKey];
1135
+
1136
+ applyClasses && element.addClass(staggerClassName);
1137
+
1138
+ stagger = getElementAnimationDetails(element, staggerCacheKey);
1139
+
1140
+ applyClasses && element.removeClass(staggerClassName);
1141
+ }
1142
+
1143
+ /* the animation itself may need to add/remove special CSS classes
1144
+ * before calculating the anmation styles */
1145
+ calculationDecorator = calculationDecorator ||
1146
+ function(fn) { return fn(); };
1147
+
1148
+ element.addClass(className);
1149
+
1150
+ var timings = calculationDecorator(function() {
1151
+ return getElementAnimationDetails(element, eventCacheKey);
1152
+ });
1153
+
1154
+ /* there is no point in performing a reflow if the animation
1155
+ timeout is empty (this would cause a flicker bug normally
1156
+ in the page. There is also no point in performing an animation
1157
+ that only has a delay and no duration */
1158
+ var maxDelay = Math.max(timings.transitionDelay, timings.animationDelay);
1159
+ var maxDuration = Math.max(timings.transitionDuration, timings.animationDuration);
1160
+ if(maxDuration === 0) {
1161
+ element.removeClass(className);
1162
+ return false;
1163
+ }
1164
+
1165
+ //temporarily disable the transition so that the enter styles
1166
+ //don't animate twice (this is here to avoid a bug in Chrome/FF).
1167
+ var activeClassName = '';
1168
+ timings.transitionDuration > 0 ?
1169
+ blockTransitions(element) :
1170
+ blockKeyframeAnimations(element);
1171
+
1172
+ forEach(className.split(' '), function(klass, i) {
1173
+ activeClassName += (i > 0 ? ' ' : '') + klass + '-active';
1174
+ });
1175
+
1176
+ element.data(NG_ANIMATE_CSS_DATA_KEY, {
1177
+ className : className,
1178
+ activeClassName : activeClassName,
1179
+ maxDuration : maxDuration,
1180
+ maxDelay : maxDelay,
1181
+ classes : className + ' ' + activeClassName,
1182
+ timings : timings,
1183
+ stagger : stagger,
1184
+ itemIndex : itemIndex
1185
+ });
1186
+
1187
+ return true;
1188
+ }
1189
+
1190
+ function blockTransitions(element) {
1191
+ extractElementNode(element).style[TRANSITION_PROP + PROPERTY_KEY] = 'none';
1192
+ }
1193
+
1194
+ function blockKeyframeAnimations(element) {
1195
+ extractElementNode(element).style[ANIMATION_PROP] = 'none 0s';
1196
+ }
1197
+
1198
+ function unblockTransitions(element) {
1199
+ var prop = TRANSITION_PROP + PROPERTY_KEY;
1200
+ var node = extractElementNode(element);
1201
+ if(node.style[prop] && node.style[prop].length > 0) {
1202
+ node.style[prop] = '';
1203
+ }
1204
+ }
1205
+
1206
+ function unblockKeyframeAnimations(element) {
1207
+ var prop = ANIMATION_PROP;
1208
+ var node = extractElementNode(element);
1209
+ if(node.style[prop] && node.style[prop].length > 0) {
1210
+ node.style[prop] = '';
1211
+ }
1212
+ }
1213
+
1214
+ function animateRun(element, className, activeAnimationComplete) {
1215
+ var elementData = element.data(NG_ANIMATE_CSS_DATA_KEY);
1216
+ var node = extractElementNode(element);
1217
+ if(node.className.indexOf(className) == -1 || !elementData) {
1218
+ activeAnimationComplete();
1219
+ return;
1220
+ }
1221
+
1222
+ var timings = elementData.timings;
1223
+ var stagger = elementData.stagger;
1224
+ var maxDuration = elementData.maxDuration;
1225
+ var activeClassName = elementData.activeClassName;
1226
+ var maxDelayTime = Math.max(timings.transitionDelay, timings.animationDelay) * ONE_SECOND;
1227
+ var startTime = Date.now();
1228
+ var css3AnimationEvents = ANIMATIONEND_EVENT + ' ' + TRANSITIONEND_EVENT;
1229
+ var itemIndex = elementData.itemIndex;
1230
+
1231
+ var style = '', appliedStyles = [];
1232
+ if(timings.transitionDuration > 0) {
1233
+ var propertyStyle = timings.transitionPropertyStyle;
1234
+ if(propertyStyle.indexOf('all') == -1) {
1235
+ style += CSS_PREFIX + 'transition-property: ' + propertyStyle + ';';
1236
+ style += CSS_PREFIX + 'transition-duration: ' + timings.transitionDurationStyle + ';';
1237
+ appliedStyles.push(CSS_PREFIX + 'transition-property');
1238
+ appliedStyles.push(CSS_PREFIX + 'transition-duration');
1239
+ }
1240
+ }
1241
+
1242
+ if(itemIndex > 0) {
1243
+ if(stagger.transitionDelay > 0 && stagger.transitionDuration === 0) {
1244
+ var delayStyle = timings.transitionDelayStyle;
1245
+ style += CSS_PREFIX + 'transition-delay: ' +
1246
+ prepareStaggerDelay(delayStyle, stagger.transitionDelay, itemIndex) + '; ';
1247
+ appliedStyles.push(CSS_PREFIX + 'transition-delay');
1248
+ }
1249
+
1250
+ if(stagger.animationDelay > 0 && stagger.animationDuration === 0) {
1251
+ style += CSS_PREFIX + 'animation-delay: ' +
1252
+ prepareStaggerDelay(timings.animationDelayStyle, stagger.animationDelay, itemIndex) + '; ';
1253
+ appliedStyles.push(CSS_PREFIX + 'animation-delay');
1254
+ }
1255
+ }
1256
+
1257
+ if(appliedStyles.length > 0) {
1258
+ //the element being animated may sometimes contain comment nodes in
1259
+ //the jqLite object, so we're safe to use a single variable to house
1260
+ //the styles since there is always only one element being animated
1261
+ var oldStyle = node.getAttribute('style') || '';
1262
+ node.setAttribute('style', oldStyle + ' ' + style);
1263
+ }
1264
+
1265
+ element.on(css3AnimationEvents, onAnimationProgress);
1266
+ element.addClass(activeClassName);
1267
+ elementData.closeAnimationFn = function() {
1268
+ onEnd();
1269
+ activeAnimationComplete();
1270
+ };
1271
+ return onEnd;
1272
+
1273
+ // This will automatically be called by $animate so
1274
+ // there is no need to attach this internally to the
1275
+ // timeout done method.
1276
+ function onEnd(cancelled) {
1277
+ element.off(css3AnimationEvents, onAnimationProgress);
1278
+ element.removeClass(activeClassName);
1279
+ animateClose(element, className);
1280
+ var node = extractElementNode(element);
1281
+ for (var i in appliedStyles) {
1282
+ node.style.removeProperty(appliedStyles[i]);
1283
+ }
1284
+ }
1285
+
1286
+ function onAnimationProgress(event) {
1287
+ event.stopPropagation();
1288
+ var ev = event.originalEvent || event;
1289
+ var timeStamp = ev.$manualTimeStamp || ev.timeStamp || Date.now();
1290
+
1291
+ /* Firefox (or possibly just Gecko) likes to not round values up
1292
+ * when a ms measurement is used for the animation */
1293
+ var elapsedTime = parseFloat(ev.elapsedTime.toFixed(ELAPSED_TIME_MAX_DECIMAL_PLACES));
1294
+
1295
+ /* $manualTimeStamp is a mocked timeStamp value which is set
1296
+ * within browserTrigger(). This is only here so that tests can
1297
+ * mock animations properly. Real events fallback to event.timeStamp,
1298
+ * or, if they don't, then a timeStamp is automatically created for them.
1299
+ * We're checking to see if the timeStamp surpasses the expected delay,
1300
+ * but we're using elapsedTime instead of the timeStamp on the 2nd
1301
+ * pre-condition since animations sometimes close off early */
1302
+ if(Math.max(timeStamp - startTime, 0) >= maxDelayTime && elapsedTime >= maxDuration) {
1303
+ activeAnimationComplete();
1304
+ }
1305
+ }
1306
+ }
1307
+
1308
+ function prepareStaggerDelay(delayStyle, staggerDelay, index) {
1309
+ var style = '';
1310
+ forEach(delayStyle.split(','), function(val, i) {
1311
+ style += (i > 0 ? ',' : '') +
1312
+ (index * staggerDelay + parseInt(val, 10)) + 's';
1313
+ });
1314
+ return style;
1315
+ }
1316
+
1317
+ function animateBefore(element, className, calculationDecorator) {
1318
+ if(animateSetup(element, className, calculationDecorator)) {
1319
+ return function(cancelled) {
1320
+ cancelled && animateClose(element, className);
1321
+ };
1322
+ }
1323
+ }
1324
+
1325
+ function animateAfter(element, className, afterAnimationComplete) {
1326
+ if(element.data(NG_ANIMATE_CSS_DATA_KEY)) {
1327
+ return animateRun(element, className, afterAnimationComplete);
1328
+ } else {
1329
+ animateClose(element, className);
1330
+ afterAnimationComplete();
1331
+ }
1332
+ }
1333
+
1334
+ function animate(element, className, animationComplete) {
1335
+ //If the animateSetup function doesn't bother returning a
1336
+ //cancellation function then it means that there is no animation
1337
+ //to perform at all
1338
+ var preReflowCancellation = animateBefore(element, className);
1339
+ if(!preReflowCancellation) {
1340
+ animationComplete();
1341
+ return;
1342
+ }
1343
+
1344
+ //There are two cancellation functions: one is before the first
1345
+ //reflow animation and the second is during the active state
1346
+ //animation. The first function will take care of removing the
1347
+ //data from the element which will not make the 2nd animation
1348
+ //happen in the first place
1349
+ var cancel = preReflowCancellation;
1350
+ afterReflow(element, function() {
1351
+ unblockTransitions(element);
1352
+ unblockKeyframeAnimations(element);
1353
+ //once the reflow is complete then we point cancel to
1354
+ //the new cancellation function which will remove all of the
1355
+ //animation properties from the active animation
1356
+ cancel = animateAfter(element, className, animationComplete);
1357
+ });
1358
+
1359
+ return function(cancelled) {
1360
+ (cancel || noop)(cancelled);
1361
+ };
1362
+ }
1363
+
1364
+ function animateClose(element, className) {
1365
+ element.removeClass(className);
1366
+ element.removeData(NG_ANIMATE_CSS_DATA_KEY);
1367
+ }
1368
+
1369
+ return {
1370
+ allowCancel : function(element, animationEvent, className) {
1371
+ //always cancel the current animation if it is a
1372
+ //structural animation
1373
+ var oldClasses = (element.data(NG_ANIMATE_CSS_DATA_KEY) || {}).classes;
1374
+ if(!oldClasses || ['enter','leave','move'].indexOf(animationEvent) >= 0) {
1375
+ return true;
1376
+ }
1377
+
1378
+ var parentElement = element.parent();
1379
+ var clone = angular.element(extractElementNode(element).cloneNode());
1380
+
1381
+ //make the element super hidden and override any CSS style values
1382
+ clone.attr('style','position:absolute; top:-9999px; left:-9999px');
1383
+ clone.removeAttr('id');
1384
+ clone.empty();
1385
+
1386
+ forEach(oldClasses.split(' '), function(klass) {
1387
+ clone.removeClass(klass);
1388
+ });
1389
+
1390
+ var suffix = animationEvent == 'addClass' ? '-add' : '-remove';
1391
+ clone.addClass(suffixClasses(className, suffix));
1392
+ parentElement.append(clone);
1393
+
1394
+ var timings = getElementAnimationDetails(clone);
1395
+ clone.remove();
1396
+
1397
+ return Math.max(timings.transitionDuration, timings.animationDuration) > 0;
1398
+ },
1399
+
1400
+ enter : function(element, animationCompleted) {
1401
+ return animate(element, 'ng-enter', animationCompleted);
1402
+ },
1403
+
1404
+ leave : function(element, animationCompleted) {
1405
+ return animate(element, 'ng-leave', animationCompleted);
1406
+ },
1407
+
1408
+ move : function(element, animationCompleted) {
1409
+ return animate(element, 'ng-move', animationCompleted);
1410
+ },
1411
+
1412
+ beforeAddClass : function(element, className, animationCompleted) {
1413
+ var cancellationMethod = animateBefore(element, suffixClasses(className, '-add'), function(fn) {
1414
+
1415
+ /* when a CSS class is added to an element then the transition style that
1416
+ * is applied is the transition defined on the element when the CSS class
1417
+ * is added at the time of the animation. This is how CSS3 functions
1418
+ * outside of ngAnimate. */
1419
+ element.addClass(className);
1420
+ var timings = fn();
1421
+ element.removeClass(className);
1422
+ return timings;
1423
+ });
1424
+
1425
+ if(cancellationMethod) {
1426
+ afterReflow(element, function() {
1427
+ unblockTransitions(element);
1428
+ unblockKeyframeAnimations(element);
1429
+ animationCompleted();
1430
+ });
1431
+ return cancellationMethod;
1432
+ }
1433
+ animationCompleted();
1434
+ },
1435
+
1436
+ addClass : function(element, className, animationCompleted) {
1437
+ return animateAfter(element, suffixClasses(className, '-add'), animationCompleted);
1438
+ },
1439
+
1440
+ beforeRemoveClass : function(element, className, animationCompleted) {
1441
+ var cancellationMethod = animateBefore(element, suffixClasses(className, '-remove'), function(fn) {
1442
+ /* when classes are removed from an element then the transition style
1443
+ * that is applied is the transition defined on the element without the
1444
+ * CSS class being there. This is how CSS3 functions outside of ngAnimate.
1445
+ * http://plnkr.co/edit/j8OzgTNxHTb4n3zLyjGW?p=preview */
1446
+ var klass = element.attr('class');
1447
+ element.removeClass(className);
1448
+ var timings = fn();
1449
+ element.attr('class', klass);
1450
+ return timings;
1451
+ });
1452
+
1453
+ if(cancellationMethod) {
1454
+ afterReflow(element, function() {
1455
+ unblockTransitions(element);
1456
+ unblockKeyframeAnimations(element);
1457
+ animationCompleted();
1458
+ });
1459
+ return cancellationMethod;
1460
+ }
1461
+ animationCompleted();
1462
+ },
1463
+
1464
+ removeClass : function(element, className, animationCompleted) {
1465
+ return animateAfter(element, suffixClasses(className, '-remove'), animationCompleted);
1466
+ }
1467
+ };
1468
+
1469
+ function suffixClasses(classes, suffix) {
1470
+ var className = '';
1471
+ classes = angular.isArray(classes) ? classes : classes.split(/\s+/);
1472
+ forEach(classes, function(klass, i) {
1473
+ if(klass && klass.length > 0) {
1474
+ className += (i > 0 ? ' ' : '') + klass + suffix;
1475
+ }
1476
+ });
1477
+ return className;
1478
+ }
1479
+ }]);
1480
+ }]);
1481
+
1482
+
1483
+ })(window, window.angular);