@angular-wave/angular.ts 0.0.50 → 0.0.52
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/angular-ts.esm.js +2 -2
- package/dist/angular-ts.umd.js +2 -2
- package/package.json +1 -1
- package/src/animations/animate-children-directive.js +19 -99
- package/src/animations/animate-children-directive.md +80 -0
- package/src/animations/animate-css-driver.js +250 -256
- package/src/animations/animate-css.js +646 -875
- package/src/animations/animate-css.md +263 -0
- package/src/animations/animate-js-driver.js +54 -56
- package/src/animations/animate-js.js +303 -306
- package/src/animations/animate-queue.js +707 -716
- package/src/animations/animate-swap.js +30 -119
- package/src/animations/animate-swap.md +88 -0
- package/src/animations/animation.js +3 -3
- package/src/animations/shared.js +2 -1
- package/src/core/animate/animate-runner.js +147 -145
- package/src/core/animate/animate.js +568 -582
- package/src/core/animate/anomate.md +13 -0
- package/src/core/compile/compile.spec.js +5 -6
- package/src/core/core.html +0 -1
- package/src/core/parser/ast-type.js +21 -20
- package/src/core/parser/ast.js +34 -35
- package/src/core/parser/interpreter.js +405 -136
- package/src/core/parser/lexer.js +14 -13
- package/src/core/parser/parse.js +31 -45
- package/src/core/parser/parse.spec.js +429 -444
- package/src/core/parser/parser.js +17 -9
- package/src/directive/select/select.js +301 -305
- package/src/loader.js +5 -1
- package/src/public.js +0 -1
- package/src/router/directives/state-directives.js +256 -574
- package/src/router/directives/state-directives.md +435 -0
- package/src/router/directives/view-directive.js +3 -3
- package/src/router/index.js +7 -7
- package/src/types.js +0 -13
- package/types/animations/animate-children-directive.d.ts +5 -80
- package/types/animations/animate-css-driver.d.ts +11 -0
- package/types/animations/animate-css.d.ts +8 -0
- package/types/animations/animate-js-driver.d.ts +8 -0
- package/types/animations/animate-js.d.ts +12 -0
- package/types/animations/animate-queue.d.ts +19 -0
- package/types/animations/animate-swap.d.ts +5 -89
- package/types/animations/shared.d.ts +1 -1
- package/types/core/animate/animate-runner.d.ts +32 -0
- package/types/core/animate/animate.d.ts +509 -0
- package/types/core/parser/ast-type.d.ts +24 -20
- package/types/core/parser/ast.d.ts +13 -14
- package/types/core/parser/interpreter.d.ts +24 -19
- package/types/core/parser/lexer.d.ts +6 -2
- package/types/core/parser/parse.d.ts +44 -38
- package/types/core/parser/parser.d.ts +2 -10
- package/types/directive/select/select.d.ts +79 -0
- package/types/loader.d.ts +397 -0
- package/types/router/directives/state-directives.d.ts +31 -0
- package/types/types.d.ts +0 -1
- package/src/core/document.spec.js +0 -52
- package/src/core/parser/shared.js +0 -234
- package/types/core/parser/shared.d.ts +0 -35
|
@@ -24,829 +24,820 @@ import {
|
|
|
24
24
|
|
|
25
25
|
const NG_ANIMATE_ATTR_NAME = "data-ng-animate";
|
|
26
26
|
const NG_ANIMATE_PIN_DATA = "$ngAnimatePin";
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
27
|
+
$$AnimateQueueProvider.$inject = ["$animateProvider"];
|
|
28
|
+
export function $$AnimateQueueProvider($animateProvider) {
|
|
29
|
+
const PRE_DIGEST_STATE = 1;
|
|
30
|
+
const RUNNING_STATE = 2;
|
|
31
|
+
const ONE_SPACE = " ";
|
|
32
|
+
|
|
33
|
+
const rules = (this.rules = {
|
|
34
|
+
skip: [],
|
|
35
|
+
cancel: [],
|
|
36
|
+
join: [],
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
function getEventData(options) {
|
|
40
|
+
return {
|
|
41
|
+
addClass: options.addClass,
|
|
42
|
+
removeClass: options.removeClass,
|
|
43
|
+
from: options.from,
|
|
44
|
+
to: options.to,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function makeTruthyCssClassMap(classString) {
|
|
49
|
+
if (!classString) {
|
|
50
|
+
return null;
|
|
47
51
|
}
|
|
48
52
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
return null;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const keys = classString.split(ONE_SPACE);
|
|
55
|
-
const map = Object.create(null);
|
|
56
|
-
|
|
57
|
-
keys.forEach((key) => {
|
|
58
|
-
map[key] = true;
|
|
59
|
-
});
|
|
60
|
-
return map;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function hasMatchingClasses(newClassString, currentClassString) {
|
|
64
|
-
if (newClassString && currentClassString) {
|
|
65
|
-
const currentClassMap = makeTruthyCssClassMap(currentClassString);
|
|
66
|
-
return newClassString
|
|
67
|
-
.split(ONE_SPACE)
|
|
68
|
-
.some((className) => currentClassMap[className]);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function isAllowed(ruleType, currentAnimation, previousAnimation) {
|
|
73
|
-
return rules[ruleType].some((fn) =>
|
|
74
|
-
fn(currentAnimation, previousAnimation),
|
|
75
|
-
);
|
|
76
|
-
}
|
|
53
|
+
const keys = classString.split(ONE_SPACE);
|
|
54
|
+
const map = Object.create(null);
|
|
77
55
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
56
|
+
keys.forEach((key) => {
|
|
57
|
+
map[key] = true;
|
|
58
|
+
});
|
|
59
|
+
return map;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function hasMatchingClasses(newClassString, currentClassString) {
|
|
63
|
+
if (newClassString && currentClassString) {
|
|
64
|
+
const currentClassMap = makeTruthyCssClassMap(currentClassString);
|
|
65
|
+
return newClassString
|
|
66
|
+
.split(ONE_SPACE)
|
|
67
|
+
.some((className) => currentClassMap[className]);
|
|
82
68
|
}
|
|
69
|
+
}
|
|
83
70
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
!newAnimation.structural && hasAnimationClasses(newAnimation),
|
|
88
|
-
);
|
|
89
|
-
|
|
90
|
-
rules.skip.push(
|
|
91
|
-
(newAnimation) =>
|
|
92
|
-
// there is no need to animate anything if no classes are being added and
|
|
93
|
-
// there is no structural animation that will be triggered
|
|
94
|
-
!newAnimation.structural && !hasAnimationClasses(newAnimation),
|
|
71
|
+
function isAllowed(ruleType, currentAnimation, previousAnimation) {
|
|
72
|
+
return rules[ruleType].some((fn) =>
|
|
73
|
+
fn(currentAnimation, previousAnimation),
|
|
95
74
|
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function hasAnimationClasses(animation, and) {
|
|
78
|
+
const a = (animation.addClass || "").length > 0;
|
|
79
|
+
const b = (animation.removeClass || "").length > 0;
|
|
80
|
+
return and ? a && b : a || b;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
rules.join.push(
|
|
84
|
+
(newAnimation) =>
|
|
85
|
+
// if the new animation is class-based then we can just tack that on
|
|
86
|
+
!newAnimation.structural && hasAnimationClasses(newAnimation),
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
rules.skip.push(
|
|
90
|
+
(newAnimation) =>
|
|
91
|
+
// there is no need to animate anything if no classes are being added and
|
|
92
|
+
// there is no structural animation that will be triggered
|
|
93
|
+
!newAnimation.structural && !hasAnimationClasses(newAnimation),
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
rules.skip.push(
|
|
97
|
+
(newAnimation, currentAnimation) =>
|
|
98
|
+
// why should we trigger a new structural animation if the element will
|
|
99
|
+
// be removed from the DOM anyway?
|
|
100
|
+
currentAnimation.event === "leave" && newAnimation.structural,
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
rules.skip.push(
|
|
104
|
+
(newAnimation, currentAnimation) =>
|
|
105
|
+
// if there is an ongoing current animation then don't even bother running the class-based animation
|
|
106
|
+
currentAnimation.structural &&
|
|
107
|
+
currentAnimation.state === RUNNING_STATE &&
|
|
108
|
+
!newAnimation.structural,
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
rules.cancel.push(
|
|
112
|
+
(newAnimation, currentAnimation) =>
|
|
113
|
+
// there can never be two structural animations running at the same time
|
|
114
|
+
currentAnimation.structural && newAnimation.structural,
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
rules.cancel.push(
|
|
118
|
+
(newAnimation, currentAnimation) =>
|
|
119
|
+
// if the previous animation is already running, but the new animation will
|
|
120
|
+
// be triggered, but the new animation is structural
|
|
121
|
+
currentAnimation.state === RUNNING_STATE && newAnimation.structural,
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
rules.cancel.push((newAnimation, currentAnimation) => {
|
|
125
|
+
// cancel the animation if classes added / removed in both animation cancel each other out,
|
|
126
|
+
// but only if the current animation isn't structural
|
|
127
|
+
|
|
128
|
+
if (currentAnimation.structural) return false;
|
|
129
|
+
|
|
130
|
+
const nA = newAnimation.addClass;
|
|
131
|
+
const nR = newAnimation.removeClass;
|
|
132
|
+
const cA = currentAnimation.addClass;
|
|
133
|
+
const cR = currentAnimation.removeClass;
|
|
134
|
+
|
|
135
|
+
// early detection to save the global CPU shortage :)
|
|
136
|
+
if (
|
|
137
|
+
(isUndefined(nA) && isUndefined(nR)) ||
|
|
138
|
+
(isUndefined(cA) && isUndefined(cR))
|
|
139
|
+
) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
96
142
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
143
|
+
return hasMatchingClasses(nA, cR) || hasMatchingClasses(nR, cA);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
this.$get = [
|
|
147
|
+
"$rootScope",
|
|
148
|
+
"$rootElement",
|
|
149
|
+
"$document",
|
|
150
|
+
"$$animation",
|
|
151
|
+
"$$AnimateRunner",
|
|
152
|
+
"$templateRequest",
|
|
153
|
+
function (
|
|
154
|
+
$rootScope,
|
|
155
|
+
$rootElement,
|
|
156
|
+
$document,
|
|
157
|
+
$$animation,
|
|
158
|
+
$$AnimateRunner,
|
|
159
|
+
$templateRequest,
|
|
160
|
+
) {
|
|
161
|
+
const activeAnimationsLookup = new Map();
|
|
162
|
+
const disabledElementsLookup = new Map();
|
|
163
|
+
let animationsEnabled = null;
|
|
164
|
+
|
|
165
|
+
function removeFromDisabledElementsLookup(evt) {
|
|
166
|
+
disabledElementsLookup.delete(evt.target);
|
|
167
|
+
}
|
|
103
168
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
169
|
+
function postDigestTaskFactory() {
|
|
170
|
+
let postDigestCalled = false;
|
|
171
|
+
return function (fn) {
|
|
172
|
+
// we only issue a call to postDigest before
|
|
173
|
+
// it has first passed. This prevents any callbacks
|
|
174
|
+
// from not firing once the animation has completed
|
|
175
|
+
// since it will be out of the digest cycle.
|
|
176
|
+
if (postDigestCalled) {
|
|
177
|
+
fn();
|
|
178
|
+
} else {
|
|
179
|
+
$rootScope.$$postDigest(() => {
|
|
180
|
+
postDigestCalled = true;
|
|
181
|
+
fn();
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
}
|
|
111
186
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
187
|
+
// Wait until all directive and route-related templates are downloaded and
|
|
188
|
+
// compiled. The $templateRequest.totalPendingRequests variable keeps track of
|
|
189
|
+
// all of the remote templates being currently downloaded. If there are no
|
|
190
|
+
// templates currently downloading then the watcher will still fire anyway.
|
|
191
|
+
const deregisterWatch = $rootScope.$watch(
|
|
192
|
+
() => $templateRequest.totalPendingRequests === 0,
|
|
193
|
+
(isEmpty) => {
|
|
194
|
+
if (!isEmpty) return;
|
|
195
|
+
deregisterWatch();
|
|
196
|
+
|
|
197
|
+
// Now that all templates have been downloaded, $animate will wait until
|
|
198
|
+
// the post digest queue is empty before enabling animations. By having two
|
|
199
|
+
// calls to $postDigest calls we can ensure that the flag is enabled at the
|
|
200
|
+
// very end of the post digest queue. Since all of the animations in $animate
|
|
201
|
+
// use $postDigest, it's important that the code below executes at the end.
|
|
202
|
+
// This basically means that the page is fully downloaded and compiled before
|
|
203
|
+
// any animations are triggered.
|
|
204
|
+
$rootScope.$$postDigest(() => {
|
|
205
|
+
$rootScope.$$postDigest(() => {
|
|
206
|
+
// we check for null directly in the event that the application already called
|
|
207
|
+
// .enabled() with whatever arguments that it provided it with
|
|
208
|
+
if (animationsEnabled === null) {
|
|
209
|
+
animationsEnabled = true;
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
},
|
|
214
|
+
);
|
|
117
215
|
|
|
118
|
-
|
|
119
|
-
(newAnimation, currentAnimation) =>
|
|
120
|
-
// if the previous animation is already running, but the new animation will
|
|
121
|
-
// be triggered, but the new animation is structural
|
|
122
|
-
currentAnimation.state === RUNNING_STATE && newAnimation.structural,
|
|
123
|
-
);
|
|
216
|
+
const callbackRegistry = Object.create(null);
|
|
124
217
|
|
|
125
|
-
|
|
126
|
-
//
|
|
127
|
-
|
|
218
|
+
// remember that the `customFilter`/`classNameFilter` are set during the
|
|
219
|
+
// provider/config stage therefore we can optimize here and setup helper functions
|
|
220
|
+
const customFilter = $animateProvider.customFilter();
|
|
221
|
+
const classNameFilter = $animateProvider.classNameFilter();
|
|
222
|
+
const returnTrue = function () {
|
|
223
|
+
return true;
|
|
224
|
+
};
|
|
128
225
|
|
|
129
|
-
|
|
226
|
+
const isAnimatableByFilter = customFilter || returnTrue;
|
|
227
|
+
const isAnimatableClassName = !classNameFilter
|
|
228
|
+
? returnTrue
|
|
229
|
+
: function (node, options) {
|
|
230
|
+
const className = [
|
|
231
|
+
node.getAttribute("class"),
|
|
232
|
+
options.addClass,
|
|
233
|
+
options.removeClass,
|
|
234
|
+
].join(" ");
|
|
235
|
+
return classNameFilter.test(className);
|
|
236
|
+
};
|
|
130
237
|
|
|
131
|
-
const
|
|
132
|
-
const nR = newAnimation.removeClass;
|
|
133
|
-
const cA = currentAnimation.addClass;
|
|
134
|
-
const cR = currentAnimation.removeClass;
|
|
238
|
+
const applyAnimationClasses = applyAnimationClassesFactory();
|
|
135
239
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
(isUndefined(nA) && isUndefined(nR)) ||
|
|
139
|
-
(isUndefined(cA) && isUndefined(cR))
|
|
140
|
-
) {
|
|
141
|
-
return false;
|
|
240
|
+
function normalizeAnimationDetails(element, animation) {
|
|
241
|
+
return mergeAnimationDetails(element, animation, {});
|
|
142
242
|
}
|
|
143
243
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
"$document",
|
|
151
|
-
"$$animation",
|
|
152
|
-
"$$AnimateRunner",
|
|
153
|
-
"$templateRequest",
|
|
154
|
-
"$$isDocumentHidden",
|
|
155
|
-
function (
|
|
156
|
-
$rootScope,
|
|
157
|
-
$rootElement,
|
|
158
|
-
$document,
|
|
159
|
-
$$animation,
|
|
160
|
-
$$AnimateRunner,
|
|
161
|
-
$templateRequest,
|
|
162
|
-
$$isDocumentHidden,
|
|
163
|
-
) {
|
|
164
|
-
const activeAnimationsLookup = new Map();
|
|
165
|
-
const disabledElementsLookup = new Map();
|
|
166
|
-
let animationsEnabled = null;
|
|
167
|
-
|
|
168
|
-
function removeFromDisabledElementsLookup(evt) {
|
|
169
|
-
disabledElementsLookup.delete(evt.target);
|
|
170
|
-
}
|
|
244
|
+
// IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.
|
|
245
|
+
const contains =
|
|
246
|
+
window.Node.prototype.contains ||
|
|
247
|
+
function (arg) {
|
|
248
|
+
return this === arg || !!(this.compareDocumentPosition(arg) & 16);
|
|
249
|
+
};
|
|
171
250
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
if (
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
fn();
|
|
185
|
-
});
|
|
251
|
+
function findCallbacks(targetParentNode, targetNode, event) {
|
|
252
|
+
const matches = [];
|
|
253
|
+
const entries = callbackRegistry[event];
|
|
254
|
+
if (entries) {
|
|
255
|
+
entries.forEach((entry) => {
|
|
256
|
+
if (contains.call(entry.node, targetNode)) {
|
|
257
|
+
matches.push(entry.callback);
|
|
258
|
+
} else if (
|
|
259
|
+
event === "leave" &&
|
|
260
|
+
contains.call(entry.node, targetParentNode)
|
|
261
|
+
) {
|
|
262
|
+
matches.push(entry.callback);
|
|
186
263
|
}
|
|
187
|
-
};
|
|
264
|
+
});
|
|
188
265
|
}
|
|
189
266
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
// all of the remote templates being currently downloaded. If there are no
|
|
193
|
-
// templates currently downloading then the watcher will still fire anyway.
|
|
194
|
-
const deregisterWatch = $rootScope.$watch(
|
|
195
|
-
() => $templateRequest.totalPendingRequests === 0,
|
|
196
|
-
(isEmpty) => {
|
|
197
|
-
if (!isEmpty) return;
|
|
198
|
-
deregisterWatch();
|
|
199
|
-
|
|
200
|
-
// Now that all templates have been downloaded, $animate will wait until
|
|
201
|
-
// the post digest queue is empty before enabling animations. By having two
|
|
202
|
-
// calls to $postDigest calls we can ensure that the flag is enabled at the
|
|
203
|
-
// very end of the post digest queue. Since all of the animations in $animate
|
|
204
|
-
// use $postDigest, it's important that the code below executes at the end.
|
|
205
|
-
// This basically means that the page is fully downloaded and compiled before
|
|
206
|
-
// any animations are triggered.
|
|
207
|
-
$rootScope.$$postDigest(() => {
|
|
208
|
-
$rootScope.$$postDigest(() => {
|
|
209
|
-
// we check for null directly in the event that the application already called
|
|
210
|
-
// .enabled() with whatever arguments that it provided it with
|
|
211
|
-
if (animationsEnabled === null) {
|
|
212
|
-
animationsEnabled = true;
|
|
213
|
-
}
|
|
214
|
-
});
|
|
215
|
-
});
|
|
216
|
-
},
|
|
217
|
-
);
|
|
218
|
-
|
|
219
|
-
const callbackRegistry = Object.create(null);
|
|
267
|
+
return matches;
|
|
268
|
+
}
|
|
220
269
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
270
|
+
function filterFromRegistry(list, matchContainer, matchCallback) {
|
|
271
|
+
const containerNode = extractElementNode(matchContainer);
|
|
272
|
+
return list.filter((entry) => {
|
|
273
|
+
const isMatch =
|
|
274
|
+
entry.node === containerNode &&
|
|
275
|
+
(!matchCallback || entry.callback === matchCallback);
|
|
276
|
+
return !isMatch;
|
|
277
|
+
});
|
|
278
|
+
}
|
|
228
279
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
node.getAttribute("class"),
|
|
235
|
-
options.addClass,
|
|
236
|
-
options.removeClass,
|
|
237
|
-
].join(" ");
|
|
238
|
-
return classNameFilter.test(className);
|
|
239
|
-
};
|
|
240
|
-
|
|
241
|
-
const applyAnimationClasses = applyAnimationClassesFactory();
|
|
242
|
-
|
|
243
|
-
function normalizeAnimationDetails(element, animation) {
|
|
244
|
-
return mergeAnimationDetails(element, animation, {});
|
|
280
|
+
function cleanupEventListeners(phase, node) {
|
|
281
|
+
if (phase === "close" && !node.parentNode) {
|
|
282
|
+
// If the element is not attached to a parentNode, it has been removed by
|
|
283
|
+
// the domOperation, and we can safely remove the event callbacks
|
|
284
|
+
$animate.off(node);
|
|
245
285
|
}
|
|
286
|
+
}
|
|
246
287
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
const matches = [];
|
|
256
|
-
const entries = callbackRegistry[event];
|
|
257
|
-
if (entries) {
|
|
258
|
-
entries.forEach((entry) => {
|
|
259
|
-
if (contains.call(entry.node, targetNode)) {
|
|
260
|
-
matches.push(entry.callback);
|
|
261
|
-
} else if (
|
|
262
|
-
event === "leave" &&
|
|
263
|
-
contains.call(entry.node, targetParentNode)
|
|
264
|
-
) {
|
|
265
|
-
matches.push(entry.callback);
|
|
266
|
-
}
|
|
267
|
-
});
|
|
268
|
-
}
|
|
288
|
+
let $animate = {
|
|
289
|
+
on(event, container, callback) {
|
|
290
|
+
const node = extractElementNode(container);
|
|
291
|
+
callbackRegistry[event] = callbackRegistry[event] || [];
|
|
292
|
+
callbackRegistry[event].push({
|
|
293
|
+
node,
|
|
294
|
+
callback,
|
|
295
|
+
});
|
|
269
296
|
|
|
270
|
-
|
|
271
|
-
|
|
297
|
+
// Remove the callback when the element is removed from the DOM
|
|
298
|
+
JQLite(container).on("$destroy", () => {
|
|
299
|
+
const animationDetails = activeAnimationsLookup.get(node);
|
|
272
300
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
return !isMatch;
|
|
301
|
+
if (!animationDetails) {
|
|
302
|
+
// If there's an animation ongoing, the callback calling code will remove
|
|
303
|
+
// the event listeners. If we'd remove here, the callbacks would be removed
|
|
304
|
+
// before the animation ends
|
|
305
|
+
$animate.off(event, container, callback);
|
|
306
|
+
}
|
|
280
307
|
});
|
|
281
|
-
}
|
|
308
|
+
},
|
|
309
|
+
|
|
310
|
+
off(event, container, callback) {
|
|
311
|
+
if (arguments.length === 1 && !isString(arguments[0])) {
|
|
312
|
+
container = arguments[0];
|
|
313
|
+
for (const eventType in callbackRegistry) {
|
|
314
|
+
callbackRegistry[eventType] = filterFromRegistry(
|
|
315
|
+
callbackRegistry[eventType],
|
|
316
|
+
container,
|
|
317
|
+
);
|
|
318
|
+
}
|
|
282
319
|
|
|
283
|
-
|
|
284
|
-
if (phase === "close" && !node.parentNode) {
|
|
285
|
-
// If the element is not attached to a parentNode, it has been removed by
|
|
286
|
-
// the domOperation, and we can safely remove the event callbacks
|
|
287
|
-
$animate.off(node);
|
|
320
|
+
return;
|
|
288
321
|
}
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
let $animate = {
|
|
292
|
-
on(event, container, callback) {
|
|
293
|
-
const node = extractElementNode(container);
|
|
294
|
-
callbackRegistry[event] = callbackRegistry[event] || [];
|
|
295
|
-
callbackRegistry[event].push({
|
|
296
|
-
node,
|
|
297
|
-
callback,
|
|
298
|
-
});
|
|
299
|
-
|
|
300
|
-
// Remove the callback when the element is removed from the DOM
|
|
301
|
-
JQLite(container).on("$destroy", () => {
|
|
302
|
-
const animationDetails = activeAnimationsLookup.get(node);
|
|
303
322
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
323
|
+
const entries = callbackRegistry[event];
|
|
324
|
+
if (!entries) return;
|
|
325
|
+
|
|
326
|
+
callbackRegistry[event] =
|
|
327
|
+
arguments.length === 1
|
|
328
|
+
? null
|
|
329
|
+
: filterFromRegistry(entries, container, callback);
|
|
330
|
+
},
|
|
331
|
+
|
|
332
|
+
pin(element, parentElement) {
|
|
333
|
+
assertArg(isElement(element), "element", "not an element");
|
|
334
|
+
assertArg(
|
|
335
|
+
isElement(parentElement),
|
|
336
|
+
"parentElement",
|
|
337
|
+
"not an element",
|
|
338
|
+
);
|
|
339
|
+
element.data(NG_ANIMATE_PIN_DATA, parentElement);
|
|
340
|
+
},
|
|
341
|
+
|
|
342
|
+
push(element, event, options, domOperation) {
|
|
343
|
+
options = options || {};
|
|
344
|
+
options.domOperation = domOperation;
|
|
345
|
+
return queueAnimation(element, event, options);
|
|
346
|
+
},
|
|
347
|
+
|
|
348
|
+
// this method has four signatures:
|
|
349
|
+
// () - global getter
|
|
350
|
+
// (bool) - global setter
|
|
351
|
+
// (element) - element getter
|
|
352
|
+
// (element, bool) - element setter<F37>
|
|
353
|
+
enabled(element, bool) {
|
|
354
|
+
const argCount = arguments.length;
|
|
355
|
+
|
|
356
|
+
if (argCount === 0) {
|
|
357
|
+
// () - Global getter
|
|
358
|
+
bool = !!animationsEnabled;
|
|
359
|
+
} else {
|
|
360
|
+
const hasElement = isElement(element);
|
|
325
361
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
callbackRegistry[event] =
|
|
330
|
-
arguments.length === 1
|
|
331
|
-
? null
|
|
332
|
-
: filterFromRegistry(entries, container, callback);
|
|
333
|
-
},
|
|
334
|
-
|
|
335
|
-
pin(element, parentElement) {
|
|
336
|
-
assertArg(isElement(element), "element", "not an element");
|
|
337
|
-
assertArg(
|
|
338
|
-
isElement(parentElement),
|
|
339
|
-
"parentElement",
|
|
340
|
-
"not an element",
|
|
341
|
-
);
|
|
342
|
-
element.data(NG_ANIMATE_PIN_DATA, parentElement);
|
|
343
|
-
},
|
|
344
|
-
|
|
345
|
-
push(element, event, options, domOperation) {
|
|
346
|
-
options = options || {};
|
|
347
|
-
options.domOperation = domOperation;
|
|
348
|
-
return queueAnimation(element, event, options);
|
|
349
|
-
},
|
|
350
|
-
|
|
351
|
-
// this method has four signatures:
|
|
352
|
-
// () - global getter
|
|
353
|
-
// (bool) - global setter
|
|
354
|
-
// (element) - element getter
|
|
355
|
-
// (element, bool) - element setter<F37>
|
|
356
|
-
enabled(element, bool) {
|
|
357
|
-
const argCount = arguments.length;
|
|
358
|
-
|
|
359
|
-
if (argCount === 0) {
|
|
360
|
-
// () - Global getter
|
|
361
|
-
bool = !!animationsEnabled;
|
|
362
|
+
if (!hasElement) {
|
|
363
|
+
// (bool) - Global setter
|
|
364
|
+
bool = animationsEnabled = !!element;
|
|
362
365
|
} else {
|
|
363
|
-
const
|
|
366
|
+
const node = getDomNode(element);
|
|
364
367
|
|
|
365
|
-
if (
|
|
366
|
-
// (
|
|
367
|
-
bool =
|
|
368
|
+
if (argCount === 1) {
|
|
369
|
+
// (element) - Element getter
|
|
370
|
+
bool = !disabledElementsLookup.get(node);
|
|
368
371
|
} else {
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
// (
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
// The element is added to the map for the first time.
|
|
378
|
-
// Create a listener to remove it on `$destroy` (to avoid memory leak).
|
|
379
|
-
JQLite(element).on(
|
|
380
|
-
"$destroy",
|
|
381
|
-
removeFromDisabledElementsLookup,
|
|
382
|
-
);
|
|
383
|
-
}
|
|
384
|
-
disabledElementsLookup.set(node, !bool);
|
|
372
|
+
// (element, bool) - Element setter
|
|
373
|
+
if (!disabledElementsLookup.has(node)) {
|
|
374
|
+
// The element is added to the map for the first time.
|
|
375
|
+
// Create a listener to remove it on `$destroy` (to avoid memory leak).
|
|
376
|
+
JQLite(element).on(
|
|
377
|
+
"$destroy",
|
|
378
|
+
removeFromDisabledElementsLookup,
|
|
379
|
+
);
|
|
385
380
|
}
|
|
381
|
+
disabledElementsLookup.set(node, !bool);
|
|
386
382
|
}
|
|
387
383
|
}
|
|
384
|
+
}
|
|
388
385
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
return $animate;
|
|
386
|
+
return bool;
|
|
387
|
+
},
|
|
388
|
+
};
|
|
394
389
|
|
|
395
|
-
|
|
396
|
-
// we always make a copy of the options since
|
|
397
|
-
// there should never be any side effects on
|
|
398
|
-
// the input data when running `$animateCss`.
|
|
399
|
-
let options = structuredClone(initialOptions);
|
|
390
|
+
return $animate;
|
|
400
391
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
392
|
+
function queueAnimation(originalElement, event, initialOptions) {
|
|
393
|
+
// we always make a copy of the options since
|
|
394
|
+
// there should never be any side effects on
|
|
395
|
+
// the input data when running `$animateCss`.
|
|
396
|
+
let options = structuredClone(initialOptions);
|
|
404
397
|
|
|
405
|
-
|
|
398
|
+
let element = stripCommentsFromElement(originalElement);
|
|
399
|
+
const node = getDomNode(element);
|
|
400
|
+
const parentNode = node && node.parentNode;
|
|
406
401
|
|
|
407
|
-
|
|
408
|
-
// These methods will become available after the digest has passed
|
|
409
|
-
const runner = new $$AnimateRunner();
|
|
402
|
+
options = prepareAnimationOptions(options);
|
|
410
403
|
|
|
411
|
-
|
|
412
|
-
|
|
404
|
+
// we create a fake runner with a working promise.
|
|
405
|
+
// These methods will become available after the digest has passed
|
|
406
|
+
const runner = new $$AnimateRunner();
|
|
413
407
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
}
|
|
408
|
+
// this is used to trigger callbacks in postDigest mode
|
|
409
|
+
const runInNextPostDigestOrNow = postDigestTaskFactory();
|
|
417
410
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
411
|
+
if (Array.isArray(options.addClass)) {
|
|
412
|
+
options.addClass = options.addClass.join(" ");
|
|
413
|
+
}
|
|
421
414
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
415
|
+
if (options.addClass && !isString(options.addClass)) {
|
|
416
|
+
options.addClass = null;
|
|
417
|
+
}
|
|
425
418
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
419
|
+
if (Array.isArray(options.removeClass)) {
|
|
420
|
+
options.removeClass = options.removeClass.join(" ");
|
|
421
|
+
}
|
|
429
422
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
423
|
+
if (options.removeClass && !isString(options.removeClass)) {
|
|
424
|
+
options.removeClass = null;
|
|
425
|
+
}
|
|
433
426
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
427
|
+
if (options.from && !isObject(options.from)) {
|
|
428
|
+
options.from = null;
|
|
429
|
+
}
|
|
437
430
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
if (
|
|
442
|
-
!animationsEnabled ||
|
|
443
|
-
!node ||
|
|
444
|
-
!isAnimatableByFilter(node, event, initialOptions) ||
|
|
445
|
-
!isAnimatableClassName(node, options)
|
|
446
|
-
) {
|
|
447
|
-
close();
|
|
448
|
-
return runner;
|
|
449
|
-
}
|
|
431
|
+
if (options.to && !isObject(options.to)) {
|
|
432
|
+
options.to = null;
|
|
433
|
+
}
|
|
450
434
|
|
|
451
|
-
|
|
435
|
+
// If animations are hard-disabled for the whole application there is no need to continue.
|
|
436
|
+
// There are also situations where a directive issues an animation for a JQLite wrapper that
|
|
437
|
+
// contains only comment nodes. In this case, there is no way we can perform an animation.
|
|
438
|
+
if (
|
|
439
|
+
!animationsEnabled ||
|
|
440
|
+
!node ||
|
|
441
|
+
!isAnimatableByFilter(node, event, initialOptions) ||
|
|
442
|
+
!isAnimatableClassName(node, options)
|
|
443
|
+
) {
|
|
444
|
+
close();
|
|
445
|
+
return runner;
|
|
446
|
+
}
|
|
452
447
|
|
|
453
|
-
|
|
448
|
+
const isStructural = ["enter", "move", "leave"].indexOf(event) >= 0;
|
|
449
|
+
|
|
450
|
+
// This is a hard disable of all animations the element itself, therefore there is no need to
|
|
451
|
+
// continue further past this point if not enabled
|
|
452
|
+
// Animations are also disabled if the document is currently hidden (page is not visible
|
|
453
|
+
// to the user), because browsers slow down or do not flush calls to requestAnimationFrame
|
|
454
|
+
let skipAnimations =
|
|
455
|
+
document.hidden || disabledElementsLookup.get(node);
|
|
456
|
+
const existingAnimation =
|
|
457
|
+
(!skipAnimations && activeAnimationsLookup.get(node)) || {};
|
|
458
|
+
const hasExistingAnimation = !!existingAnimation.state;
|
|
459
|
+
|
|
460
|
+
// there is no point in traversing the same collection of parent ancestors if a followup
|
|
461
|
+
// animation will be run on the same element that already did all that checking work
|
|
462
|
+
if (
|
|
463
|
+
!skipAnimations &&
|
|
464
|
+
(!hasExistingAnimation ||
|
|
465
|
+
existingAnimation.state !== PRE_DIGEST_STATE)
|
|
466
|
+
) {
|
|
467
|
+
skipAnimations = !areAnimationsAllowed(node, parentNode);
|
|
468
|
+
}
|
|
454
469
|
|
|
455
|
-
|
|
456
|
-
//
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
470
|
+
if (skipAnimations) {
|
|
471
|
+
// Callbacks should fire even if the document is hidden (regression fix for issue #14120)
|
|
472
|
+
if (document.hidden)
|
|
473
|
+
notifyProgress(runner, event, "start", getEventData(options));
|
|
474
|
+
close();
|
|
475
|
+
if (document.hidden)
|
|
476
|
+
notifyProgress(runner, event, "close", getEventData(options));
|
|
477
|
+
return runner;
|
|
478
|
+
}
|
|
464
479
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
!skipAnimations &&
|
|
469
|
-
(!hasExistingAnimation ||
|
|
470
|
-
existingAnimation.state !== PRE_DIGEST_STATE)
|
|
471
|
-
) {
|
|
472
|
-
skipAnimations = !areAnimationsAllowed(node, parentNode);
|
|
473
|
-
}
|
|
480
|
+
if (isStructural) {
|
|
481
|
+
closeChildAnimations(node);
|
|
482
|
+
}
|
|
474
483
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
+
const newAnimation = {
|
|
485
|
+
structural: isStructural,
|
|
486
|
+
element,
|
|
487
|
+
event,
|
|
488
|
+
addClass: options.addClass,
|
|
489
|
+
removeClass: options.removeClass,
|
|
490
|
+
close,
|
|
491
|
+
options,
|
|
492
|
+
runner,
|
|
493
|
+
};
|
|
484
494
|
|
|
485
|
-
|
|
486
|
-
|
|
495
|
+
if (hasExistingAnimation) {
|
|
496
|
+
const skipAnimationFlag = isAllowed(
|
|
497
|
+
"skip",
|
|
498
|
+
newAnimation,
|
|
499
|
+
existingAnimation,
|
|
500
|
+
);
|
|
501
|
+
if (skipAnimationFlag) {
|
|
502
|
+
if (existingAnimation.state === RUNNING_STATE) {
|
|
503
|
+
close();
|
|
504
|
+
return runner;
|
|
505
|
+
}
|
|
506
|
+
mergeAnimationDetails(element, existingAnimation, newAnimation);
|
|
507
|
+
return existingAnimation.runner;
|
|
487
508
|
}
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
if (skipAnimationFlag) {
|
|
507
|
-
if (existingAnimation.state === RUNNING_STATE) {
|
|
508
|
-
close();
|
|
509
|
-
return runner;
|
|
510
|
-
}
|
|
509
|
+
const cancelAnimationFlag = isAllowed(
|
|
510
|
+
"cancel",
|
|
511
|
+
newAnimation,
|
|
512
|
+
existingAnimation,
|
|
513
|
+
);
|
|
514
|
+
if (cancelAnimationFlag) {
|
|
515
|
+
if (existingAnimation.state === RUNNING_STATE) {
|
|
516
|
+
// this will end the animation right away and it is safe
|
|
517
|
+
// to do so since the animation is already running and the
|
|
518
|
+
// runner callback code will run in async
|
|
519
|
+
existingAnimation.runner.end();
|
|
520
|
+
} else if (existingAnimation.structural) {
|
|
521
|
+
// this means that the animation is queued into a digest, but
|
|
522
|
+
// hasn't started yet. Therefore it is safe to run the close
|
|
523
|
+
// method which will call the runner methods in async.
|
|
524
|
+
existingAnimation.close();
|
|
525
|
+
} else {
|
|
526
|
+
// this will merge the new animation options into existing animation options
|
|
511
527
|
mergeAnimationDetails(element, existingAnimation, newAnimation);
|
|
528
|
+
|
|
512
529
|
return existingAnimation.runner;
|
|
513
530
|
}
|
|
514
|
-
|
|
515
|
-
|
|
531
|
+
} else {
|
|
532
|
+
// a joined animation means that this animation will take over the existing one
|
|
533
|
+
// so an example would involve a leave animation taking over an enter. Then when
|
|
534
|
+
// the postDigest kicks in the enter will be ignored.
|
|
535
|
+
const joinAnimationFlag = isAllowed(
|
|
536
|
+
"join",
|
|
516
537
|
newAnimation,
|
|
517
538
|
existingAnimation,
|
|
518
539
|
);
|
|
519
|
-
if (
|
|
540
|
+
if (joinAnimationFlag) {
|
|
520
541
|
if (existingAnimation.state === RUNNING_STATE) {
|
|
521
|
-
|
|
522
|
-
// to do so since the animation is already running and the
|
|
523
|
-
// runner callback code will run in async
|
|
524
|
-
existingAnimation.runner.end();
|
|
525
|
-
} else if (existingAnimation.structural) {
|
|
526
|
-
// this means that the animation is queued into a digest, but
|
|
527
|
-
// hasn't started yet. Therefore it is safe to run the close
|
|
528
|
-
// method which will call the runner methods in async.
|
|
529
|
-
existingAnimation.close();
|
|
542
|
+
normalizeAnimationDetails(element, newAnimation);
|
|
530
543
|
} else {
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
} else {
|
|
537
|
-
// a joined animation means that this animation will take over the existing one
|
|
538
|
-
// so an example would involve a leave animation taking over an enter. Then when
|
|
539
|
-
// the postDigest kicks in the enter will be ignored.
|
|
540
|
-
const joinAnimationFlag = isAllowed(
|
|
541
|
-
"join",
|
|
542
|
-
newAnimation,
|
|
543
|
-
existingAnimation,
|
|
544
|
-
);
|
|
545
|
-
if (joinAnimationFlag) {
|
|
546
|
-
if (existingAnimation.state === RUNNING_STATE) {
|
|
547
|
-
normalizeAnimationDetails(element, newAnimation);
|
|
548
|
-
} else {
|
|
549
|
-
applyGeneratedPreparationClasses(
|
|
550
|
-
element,
|
|
551
|
-
isStructural ? event : null,
|
|
552
|
-
options,
|
|
553
|
-
);
|
|
544
|
+
applyGeneratedPreparationClasses(
|
|
545
|
+
element,
|
|
546
|
+
isStructural ? event : null,
|
|
547
|
+
options,
|
|
548
|
+
);
|
|
554
549
|
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
550
|
+
event = newAnimation.event = existingAnimation.event;
|
|
551
|
+
options = mergeAnimationDetails(
|
|
552
|
+
element,
|
|
553
|
+
existingAnimation,
|
|
554
|
+
newAnimation,
|
|
555
|
+
);
|
|
561
556
|
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
}
|
|
557
|
+
// we return the same runner since only the option values of this animation will
|
|
558
|
+
// be fed into the `existingAnimation`.
|
|
559
|
+
return existingAnimation.runner;
|
|
566
560
|
}
|
|
567
561
|
}
|
|
568
|
-
} else {
|
|
569
|
-
// normalization in this case means that it removes redundant CSS classes that
|
|
570
|
-
// already exist (addClass) or do not exist (removeClass) on the element
|
|
571
|
-
normalizeAnimationDetails(element, newAnimation);
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
// when the options are merged and cleaned up we may end up not having to do
|
|
575
|
-
// an animation at all, therefore we should check this before issuing a post
|
|
576
|
-
// digest callback. Structural animations will always run no matter what.
|
|
577
|
-
let isValidAnimation = newAnimation.structural;
|
|
578
|
-
if (!isValidAnimation) {
|
|
579
|
-
// animate (from/to) can be quickly checked first, otherwise we check if any classes are present
|
|
580
|
-
isValidAnimation =
|
|
581
|
-
(newAnimation.event === "animate" &&
|
|
582
|
-
Object.keys(newAnimation.options.to || {}).length > 0) ||
|
|
583
|
-
hasAnimationClasses(newAnimation);
|
|
584
562
|
}
|
|
563
|
+
} else {
|
|
564
|
+
// normalization in this case means that it removes redundant CSS classes that
|
|
565
|
+
// already exist (addClass) or do not exist (removeClass) on the element
|
|
566
|
+
normalizeAnimationDetails(element, newAnimation);
|
|
567
|
+
}
|
|
585
568
|
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
569
|
+
// when the options are merged and cleaned up we may end up not having to do
|
|
570
|
+
// an animation at all, therefore we should check this before issuing a post
|
|
571
|
+
// digest callback. Structural animations will always run no matter what.
|
|
572
|
+
let isValidAnimation = newAnimation.structural;
|
|
573
|
+
if (!isValidAnimation) {
|
|
574
|
+
// animate (from/to) can be quickly checked first, otherwise we check if any classes are present
|
|
575
|
+
isValidAnimation =
|
|
576
|
+
(newAnimation.event === "animate" &&
|
|
577
|
+
Object.keys(newAnimation.options.to || {}).length > 0) ||
|
|
578
|
+
hasAnimationClasses(newAnimation);
|
|
579
|
+
}
|
|
591
580
|
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
581
|
+
if (!isValidAnimation) {
|
|
582
|
+
close();
|
|
583
|
+
clearElementAnimationState(node);
|
|
584
|
+
return runner;
|
|
585
|
+
}
|
|
595
586
|
|
|
596
|
-
|
|
587
|
+
// the counter keeps track of cancelled animations
|
|
588
|
+
const counter = (existingAnimation.counter || 0) + 1;
|
|
589
|
+
newAnimation.counter = counter;
|
|
590
|
+
|
|
591
|
+
markElementAnimationState(node, PRE_DIGEST_STATE, newAnimation);
|
|
592
|
+
|
|
593
|
+
$rootScope.$$postDigest(() => {
|
|
594
|
+
// It is possible that the DOM nodes inside `originalElement` have been replaced. This can
|
|
595
|
+
// happen if the animated element is a transcluded clone and also has a `templateUrl`
|
|
596
|
+
// directive on it. Therefore, we must recreate `element` in order to interact with the
|
|
597
|
+
// actual DOM nodes.
|
|
598
|
+
// Note: We still need to use the old `node` for certain things, such as looking up in
|
|
599
|
+
// HashMaps where it was used as the key.
|
|
600
|
+
|
|
601
|
+
element = stripCommentsFromElement(originalElement);
|
|
602
|
+
|
|
603
|
+
let animationDetails = activeAnimationsLookup.get(node);
|
|
604
|
+
const animationCancelled = !animationDetails;
|
|
605
|
+
animationDetails = animationDetails || {};
|
|
606
|
+
|
|
607
|
+
// if addClass/removeClass is called before something like enter then the
|
|
608
|
+
// registered parent element may not be present. The code below will ensure
|
|
609
|
+
// that a final value for parent element is obtained
|
|
610
|
+
const parentElement = element.parent() || [];
|
|
611
|
+
|
|
612
|
+
// animate/structural/class-based animations all have requirements. Otherwise there
|
|
613
|
+
// is no point in performing an animation. The parent node must also be set.
|
|
614
|
+
const isValidAnimation =
|
|
615
|
+
parentElement.length > 0 &&
|
|
616
|
+
(animationDetails.event === "animate" ||
|
|
617
|
+
animationDetails.structural ||
|
|
618
|
+
hasAnimationClasses(animationDetails));
|
|
619
|
+
|
|
620
|
+
// this means that the previous animation was cancelled
|
|
621
|
+
// even if the follow-up animation is the same event
|
|
622
|
+
if (
|
|
623
|
+
animationCancelled ||
|
|
624
|
+
animationDetails.counter !== counter ||
|
|
625
|
+
!isValidAnimation
|
|
626
|
+
) {
|
|
627
|
+
// if another animation did not take over then we need
|
|
628
|
+
// to make sure that the domOperation and options are
|
|
629
|
+
// handled accordingly
|
|
630
|
+
if (animationCancelled) {
|
|
631
|
+
applyAnimationClasses(element, options);
|
|
632
|
+
applyAnimationStyles(element, options);
|
|
633
|
+
}
|
|
597
634
|
|
|
598
|
-
|
|
599
|
-
//
|
|
600
|
-
// happen if the animated element is a transcluded clone and also has a `templateUrl`
|
|
601
|
-
// directive on it. Therefore, we must recreate `element` in order to interact with the
|
|
602
|
-
// actual DOM nodes.
|
|
603
|
-
// Note: We still need to use the old `node` for certain things, such as looking up in
|
|
604
|
-
// HashMaps where it was used as the key.
|
|
605
|
-
|
|
606
|
-
element = stripCommentsFromElement(originalElement);
|
|
607
|
-
|
|
608
|
-
let animationDetails = activeAnimationsLookup.get(node);
|
|
609
|
-
const animationCancelled = !animationDetails;
|
|
610
|
-
animationDetails = animationDetails || {};
|
|
611
|
-
|
|
612
|
-
// if addClass/removeClass is called before something like enter then the
|
|
613
|
-
// registered parent element may not be present. The code below will ensure
|
|
614
|
-
// that a final value for parent element is obtained
|
|
615
|
-
const parentElement = element.parent() || [];
|
|
616
|
-
|
|
617
|
-
// animate/structural/class-based animations all have requirements. Otherwise there
|
|
618
|
-
// is no point in performing an animation. The parent node must also be set.
|
|
619
|
-
const isValidAnimation =
|
|
620
|
-
parentElement.length > 0 &&
|
|
621
|
-
(animationDetails.event === "animate" ||
|
|
622
|
-
animationDetails.structural ||
|
|
623
|
-
hasAnimationClasses(animationDetails));
|
|
624
|
-
|
|
625
|
-
// this means that the previous animation was cancelled
|
|
626
|
-
// even if the follow-up animation is the same event
|
|
635
|
+
// if the event changed from something like enter to leave then we do
|
|
636
|
+
// it, otherwise if it's the same then the end result will be the same too
|
|
627
637
|
if (
|
|
628
638
|
animationCancelled ||
|
|
629
|
-
animationDetails.
|
|
630
|
-
!isValidAnimation
|
|
639
|
+
(isStructural && animationDetails.event !== event)
|
|
631
640
|
) {
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
if (animationCancelled) {
|
|
636
|
-
applyAnimationClasses(element, options);
|
|
637
|
-
applyAnimationStyles(element, options);
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
// if the event changed from something like enter to leave then we do
|
|
641
|
-
// it, otherwise if it's the same then the end result will be the same too
|
|
642
|
-
if (
|
|
643
|
-
animationCancelled ||
|
|
644
|
-
(isStructural && animationDetails.event !== event)
|
|
645
|
-
) {
|
|
646
|
-
options.domOperation();
|
|
647
|
-
runner.end();
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
// in the event that the element animation was not cancelled or a follow-up animation
|
|
651
|
-
// isn't allowed to animate from here then we need to clear the state of the element
|
|
652
|
-
// so that any future animations won't read the expired animation data.
|
|
653
|
-
if (!isValidAnimation) {
|
|
654
|
-
clearElementAnimationState(node);
|
|
655
|
-
}
|
|
641
|
+
options.domOperation();
|
|
642
|
+
runner.end();
|
|
643
|
+
}
|
|
656
644
|
|
|
657
|
-
|
|
645
|
+
// in the event that the element animation was not cancelled or a follow-up animation
|
|
646
|
+
// isn't allowed to animate from here then we need to clear the state of the element
|
|
647
|
+
// so that any future animations won't read the expired animation data.
|
|
648
|
+
if (!isValidAnimation) {
|
|
649
|
+
clearElementAnimationState(node);
|
|
658
650
|
}
|
|
659
651
|
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
event =
|
|
663
|
-
!animationDetails.structural &&
|
|
664
|
-
hasAnimationClasses(animationDetails, true)
|
|
665
|
-
? "setClass"
|
|
666
|
-
: animationDetails.event;
|
|
667
|
-
|
|
668
|
-
markElementAnimationState(node, RUNNING_STATE);
|
|
669
|
-
const realRunner = $$animation(
|
|
670
|
-
element,
|
|
671
|
-
event,
|
|
672
|
-
animationDetails.options,
|
|
673
|
-
);
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
674
654
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
655
|
+
// this combined multiple class to addClass / removeClass into a setClass event
|
|
656
|
+
// so long as a structural event did not take over the animation
|
|
657
|
+
event =
|
|
658
|
+
!animationDetails.structural &&
|
|
659
|
+
hasAnimationClasses(animationDetails, true)
|
|
660
|
+
? "setClass"
|
|
661
|
+
: animationDetails.event;
|
|
679
662
|
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
663
|
+
markElementAnimationState(node, RUNNING_STATE);
|
|
664
|
+
const realRunner = $$animation(
|
|
665
|
+
element,
|
|
666
|
+
event,
|
|
667
|
+
animationDetails.options,
|
|
668
|
+
);
|
|
669
|
+
|
|
670
|
+
// this will update the runner's flow-control events based on
|
|
671
|
+
// the `realRunner` object.
|
|
672
|
+
runner.setHost(realRunner);
|
|
673
|
+
notifyProgress(runner, event, "start", getEventData(options));
|
|
674
|
+
|
|
675
|
+
realRunner.done((status) => {
|
|
676
|
+
close(!status);
|
|
677
|
+
const animationDetails = activeAnimationsLookup.get(node);
|
|
678
|
+
if (animationDetails && animationDetails.counter === counter) {
|
|
679
|
+
clearElementAnimationState(node);
|
|
680
|
+
}
|
|
681
|
+
notifyProgress(runner, event, "close", getEventData(options));
|
|
688
682
|
});
|
|
683
|
+
});
|
|
689
684
|
|
|
690
|
-
|
|
685
|
+
return runner;
|
|
691
686
|
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
687
|
+
function notifyProgress(runner, event, phase, data) {
|
|
688
|
+
runInNextPostDigestOrNow(() => {
|
|
689
|
+
const callbacks = findCallbacks(parentNode, node, event);
|
|
690
|
+
if (callbacks.length) {
|
|
691
|
+
callbacks.forEach((callback) => {
|
|
692
|
+
callback(element, phase, data);
|
|
693
|
+
});
|
|
694
|
+
cleanupEventListeners(phase, node);
|
|
695
|
+
} else {
|
|
696
|
+
cleanupEventListeners(phase, node);
|
|
697
|
+
}
|
|
698
|
+
});
|
|
699
|
+
runner.progress(event, phase, data);
|
|
700
|
+
}
|
|
706
701
|
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
}
|
|
702
|
+
function close(reject) {
|
|
703
|
+
clearGeneratedClasses(element, options);
|
|
704
|
+
applyAnimationClasses(element, options);
|
|
705
|
+
applyAnimationStyles(element, options);
|
|
706
|
+
options.domOperation();
|
|
707
|
+
runner.complete(!reject);
|
|
714
708
|
}
|
|
709
|
+
}
|
|
715
710
|
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
)
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
case PRE_DIGEST_STATE:
|
|
730
|
-
activeAnimationsLookup.delete(child);
|
|
731
|
-
break;
|
|
732
|
-
}
|
|
711
|
+
function closeChildAnimations(node) {
|
|
712
|
+
const children = node.querySelectorAll(`[${NG_ANIMATE_ATTR_NAME}]`);
|
|
713
|
+
forEach(children, (child) => {
|
|
714
|
+
const state = parseInt(child.getAttribute(NG_ANIMATE_ATTR_NAME), 10);
|
|
715
|
+
const animationDetails = activeAnimationsLookup.get(child);
|
|
716
|
+
if (animationDetails) {
|
|
717
|
+
switch (state) {
|
|
718
|
+
case RUNNING_STATE:
|
|
719
|
+
animationDetails.runner.end();
|
|
720
|
+
/* falls through */
|
|
721
|
+
case PRE_DIGEST_STATE:
|
|
722
|
+
activeAnimationsLookup.delete(child);
|
|
723
|
+
break;
|
|
733
724
|
}
|
|
734
|
-
}
|
|
735
|
-
}
|
|
725
|
+
}
|
|
726
|
+
});
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
function clearElementAnimationState(node) {
|
|
730
|
+
node.removeAttribute(NG_ANIMATE_ATTR_NAME);
|
|
731
|
+
activeAnimationsLookup.delete(node);
|
|
732
|
+
}
|
|
736
733
|
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
734
|
+
/**
|
|
735
|
+
* This fn returns false if any of the following is true:
|
|
736
|
+
* a) animations on any parent element are disabled, and animations on the element aren't explicitly allowed
|
|
737
|
+
* b) a parent element has an ongoing structural animation, and animateChildren is false
|
|
738
|
+
* c) the element is not a child of the body
|
|
739
|
+
* d) the element is not a child of the $rootElement
|
|
740
|
+
*/
|
|
741
|
+
function areAnimationsAllowed(node, parentNode) {
|
|
742
|
+
const bodyNode = $document[0].body;
|
|
743
|
+
const rootNode = getDomNode($rootElement);
|
|
744
|
+
|
|
745
|
+
let bodyNodeDetected = node === bodyNode || node.nodeName === "HTML";
|
|
746
|
+
let rootNodeDetected = node === rootNode;
|
|
747
|
+
let parentAnimationDetected = false;
|
|
748
|
+
let elementDisabled = disabledElementsLookup.get(node);
|
|
749
|
+
let animateChildren;
|
|
750
|
+
|
|
751
|
+
let parentHost = getOrSetCacheData(node, NG_ANIMATE_PIN_DATA);
|
|
752
|
+
if (parentHost) {
|
|
753
|
+
parentNode = getDomNode(parentHost);
|
|
740
754
|
}
|
|
741
755
|
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
* d) the element is not a child of the $rootElement
|
|
748
|
-
*/
|
|
749
|
-
function areAnimationsAllowed(node, parentNode) {
|
|
750
|
-
const bodyNode = $document[0].body;
|
|
751
|
-
const rootNode = getDomNode($rootElement);
|
|
752
|
-
|
|
753
|
-
let bodyNodeDetected = node === bodyNode || node.nodeName === "HTML";
|
|
754
|
-
let rootNodeDetected = node === rootNode;
|
|
755
|
-
let parentAnimationDetected = false;
|
|
756
|
-
let elementDisabled = disabledElementsLookup.get(node);
|
|
757
|
-
let animateChildren;
|
|
758
|
-
|
|
759
|
-
let parentHost = getOrSetCacheData(node, NG_ANIMATE_PIN_DATA);
|
|
760
|
-
if (parentHost) {
|
|
761
|
-
parentNode = getDomNode(parentHost);
|
|
756
|
+
while (parentNode) {
|
|
757
|
+
if (!rootNodeDetected) {
|
|
758
|
+
// AngularJS doesn't want to attempt to animate elements outside of the application
|
|
759
|
+
// therefore we need to ensure that the rootElement is an ancestor of the current element
|
|
760
|
+
rootNodeDetected = parentNode === rootNode;
|
|
762
761
|
}
|
|
763
762
|
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
rootNodeDetected = parentNode === rootNode;
|
|
769
|
-
}
|
|
763
|
+
if (parentNode.nodeType !== Node.ELEMENT_NODE) {
|
|
764
|
+
// no point in inspecting the #document element
|
|
765
|
+
break;
|
|
766
|
+
}
|
|
770
767
|
|
|
771
|
-
|
|
772
|
-
|
|
768
|
+
const details = activeAnimationsLookup.get(parentNode) || {};
|
|
769
|
+
// either an enter, leave or move animation will commence
|
|
770
|
+
// therefore we can't allow any animations to take place
|
|
771
|
+
// but if a parent animation is class-based then that's ok
|
|
772
|
+
if (!parentAnimationDetected) {
|
|
773
|
+
const parentNodeDisabled = disabledElementsLookup.get(parentNode);
|
|
774
|
+
|
|
775
|
+
if (parentNodeDisabled === true && elementDisabled !== false) {
|
|
776
|
+
// disable animations if the user hasn't explicitly enabled animations on the
|
|
777
|
+
// current element
|
|
778
|
+
elementDisabled = true;
|
|
779
|
+
// element is disabled via parent element, no need to check anything else
|
|
773
780
|
break;
|
|
781
|
+
} else if (parentNodeDisabled === false) {
|
|
782
|
+
elementDisabled = false;
|
|
774
783
|
}
|
|
784
|
+
parentAnimationDetected = details.structural;
|
|
785
|
+
}
|
|
775
786
|
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
if (parentNodeDisabled === true && elementDisabled !== false) {
|
|
784
|
-
// disable animations if the user hasn't explicitly enabled animations on the
|
|
785
|
-
// current element
|
|
786
|
-
elementDisabled = true;
|
|
787
|
-
// element is disabled via parent element, no need to check anything else
|
|
788
|
-
break;
|
|
789
|
-
} else if (parentNodeDisabled === false) {
|
|
790
|
-
elementDisabled = false;
|
|
791
|
-
}
|
|
792
|
-
parentAnimationDetected = details.structural;
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
if (isUndefined(animateChildren) || animateChildren === true) {
|
|
796
|
-
const value = getOrSetCacheData(
|
|
797
|
-
parentNode,
|
|
798
|
-
NG_ANIMATE_CHILDREN_DATA,
|
|
799
|
-
);
|
|
800
|
-
if (isDefined(value)) {
|
|
801
|
-
animateChildren = value;
|
|
802
|
-
}
|
|
787
|
+
if (isUndefined(animateChildren) || animateChildren === true) {
|
|
788
|
+
const value = getOrSetCacheData(
|
|
789
|
+
parentNode,
|
|
790
|
+
NG_ANIMATE_CHILDREN_DATA,
|
|
791
|
+
);
|
|
792
|
+
if (isDefined(value)) {
|
|
793
|
+
animateChildren = value;
|
|
803
794
|
}
|
|
795
|
+
}
|
|
804
796
|
|
|
805
|
-
|
|
806
|
-
|
|
797
|
+
// there is no need to continue traversing at this point
|
|
798
|
+
if (parentAnimationDetected && animateChildren === false) break;
|
|
807
799
|
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
800
|
+
if (!bodyNodeDetected) {
|
|
801
|
+
// we also need to ensure that the element is or will be a part of the body element
|
|
802
|
+
// otherwise it is pointless to even issue an animation to be rendered
|
|
803
|
+
bodyNodeDetected = parentNode === bodyNode;
|
|
804
|
+
}
|
|
813
805
|
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
806
|
+
if (bodyNodeDetected && rootNodeDetected) {
|
|
807
|
+
// If both body and root have been found, any other checks are pointless,
|
|
808
|
+
// as no animation data should live outside the application
|
|
809
|
+
break;
|
|
810
|
+
}
|
|
819
811
|
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
}
|
|
812
|
+
if (!rootNodeDetected) {
|
|
813
|
+
// If `rootNode` is not detected, check if `parentNode` is pinned to another element
|
|
814
|
+
parentHost = getOrSetCacheData(parentNode, NG_ANIMATE_PIN_DATA);
|
|
815
|
+
if (parentHost) {
|
|
816
|
+
// The pin target element becomes the next parent element
|
|
817
|
+
parentNode = getDomNode(parentHost);
|
|
818
|
+
continue;
|
|
828
819
|
}
|
|
829
|
-
|
|
830
|
-
parentNode = parentNode.parentNode;
|
|
831
820
|
}
|
|
832
821
|
|
|
833
|
-
|
|
834
|
-
(!parentAnimationDetected || animateChildren) &&
|
|
835
|
-
elementDisabled !== true;
|
|
836
|
-
return allowAnimation && rootNodeDetected && bodyNodeDetected;
|
|
822
|
+
parentNode = parentNode.parentNode;
|
|
837
823
|
}
|
|
838
824
|
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
825
|
+
const allowAnimation =
|
|
826
|
+
(!parentAnimationDetected || animateChildren) &&
|
|
827
|
+
elementDisabled !== true;
|
|
828
|
+
return allowAnimation && rootNodeDetected && bodyNodeDetected;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
function markElementAnimationState(node, state, details) {
|
|
832
|
+
details = details || {};
|
|
833
|
+
details.state = state;
|
|
842
834
|
|
|
843
|
-
|
|
835
|
+
node.setAttribute(NG_ANIMATE_ATTR_NAME, state);
|
|
844
836
|
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
];
|
|
837
|
+
const oldValue = activeAnimationsLookup.get(node);
|
|
838
|
+
const newValue = oldValue ? extend(oldValue, details) : details;
|
|
839
|
+
activeAnimationsLookup.set(node, newValue);
|
|
840
|
+
}
|
|
841
|
+
},
|
|
842
|
+
];
|
|
843
|
+
}
|