You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
5172 lines
238 KiB
5172 lines
238 KiB
/**
|
|
* @license Angular v12.2.15
|
|
* (c) 2010-2021 Google LLC. https://angular.io/
|
|
* License: MIT
|
|
*/
|
|
|
|
(function (global, factory) {
|
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/animations'), require('@angular/core')) :
|
|
typeof define === 'function' && define.amd ? define('@angular/animations/browser', ['exports', '@angular/animations', '@angular/core'], factory) :
|
|
(global = global || self, factory((global.ng = global.ng || {}, global.ng.animations = global.ng.animations || {}, global.ng.animations.browser = {}), global.ng.animations, global.ng.core));
|
|
}(this, (function (exports, animations, core) { 'use strict';
|
|
|
|
/**
|
|
* @license
|
|
* Copyright Google LLC All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://angular.io/license
|
|
*/
|
|
function isBrowser() {
|
|
return (typeof window !== 'undefined' && typeof window.document !== 'undefined');
|
|
}
|
|
function isNode() {
|
|
// Checking only for `process` isn't enough to identify whether or not we're in a Node
|
|
// environment, because Webpack by default will polyfill the `process`. While we can discern
|
|
// that Webpack polyfilled it by looking at `process.browser`, it's very Webpack-specific and
|
|
// might not be future-proof. Instead we look at the stringified version of `process` which
|
|
// is `[object process]` in Node and `[object Object]` when polyfilled.
|
|
return typeof process !== 'undefined' && {}.toString.call(process) === '[object process]';
|
|
}
|
|
function optimizeGroupPlayer(players) {
|
|
switch (players.length) {
|
|
case 0:
|
|
return new animations.NoopAnimationPlayer();
|
|
case 1:
|
|
return players[0];
|
|
default:
|
|
return new animations.ɵAnimationGroupPlayer(players);
|
|
}
|
|
}
|
|
function normalizeKeyframes(driver, normalizer, element, keyframes, preStyles, postStyles) {
|
|
if (preStyles === void 0) { preStyles = {}; }
|
|
if (postStyles === void 0) { postStyles = {}; }
|
|
var errors = [];
|
|
var normalizedKeyframes = [];
|
|
var previousOffset = -1;
|
|
var previousKeyframe = null;
|
|
keyframes.forEach(function (kf) {
|
|
var offset = kf['offset'];
|
|
var isSameOffset = offset == previousOffset;
|
|
var normalizedKeyframe = (isSameOffset && previousKeyframe) || {};
|
|
Object.keys(kf).forEach(function (prop) {
|
|
var normalizedProp = prop;
|
|
var normalizedValue = kf[prop];
|
|
if (prop !== 'offset') {
|
|
normalizedProp = normalizer.normalizePropertyName(normalizedProp, errors);
|
|
switch (normalizedValue) {
|
|
case animations.ɵPRE_STYLE:
|
|
normalizedValue = preStyles[prop];
|
|
break;
|
|
case animations.AUTO_STYLE:
|
|
normalizedValue = postStyles[prop];
|
|
break;
|
|
default:
|
|
normalizedValue =
|
|
normalizer.normalizeStyleValue(prop, normalizedProp, normalizedValue, errors);
|
|
break;
|
|
}
|
|
}
|
|
normalizedKeyframe[normalizedProp] = normalizedValue;
|
|
});
|
|
if (!isSameOffset) {
|
|
normalizedKeyframes.push(normalizedKeyframe);
|
|
}
|
|
previousKeyframe = normalizedKeyframe;
|
|
previousOffset = offset;
|
|
});
|
|
if (errors.length) {
|
|
var LINE_START = '\n - ';
|
|
throw new Error("Unable to animate due to the following errors:" + LINE_START + errors.join(LINE_START));
|
|
}
|
|
return normalizedKeyframes;
|
|
}
|
|
function listenOnPlayer(player, eventName, event, callback) {
|
|
switch (eventName) {
|
|
case 'start':
|
|
player.onStart(function () { return callback(event && copyAnimationEvent(event, 'start', player)); });
|
|
break;
|
|
case 'done':
|
|
player.onDone(function () { return callback(event && copyAnimationEvent(event, 'done', player)); });
|
|
break;
|
|
case 'destroy':
|
|
player.onDestroy(function () { return callback(event && copyAnimationEvent(event, 'destroy', player)); });
|
|
break;
|
|
}
|
|
}
|
|
function copyAnimationEvent(e, phaseName, player) {
|
|
var totalTime = player.totalTime;
|
|
var disabled = player.disabled ? true : false;
|
|
var event = makeAnimationEvent(e.element, e.triggerName, e.fromState, e.toState, phaseName || e.phaseName, totalTime == undefined ? e.totalTime : totalTime, disabled);
|
|
var data = e['_data'];
|
|
if (data != null) {
|
|
event['_data'] = data;
|
|
}
|
|
return event;
|
|
}
|
|
function makeAnimationEvent(element, triggerName, fromState, toState, phaseName, totalTime, disabled) {
|
|
if (phaseName === void 0) { phaseName = ''; }
|
|
if (totalTime === void 0) { totalTime = 0; }
|
|
return { element: element, triggerName: triggerName, fromState: fromState, toState: toState, phaseName: phaseName, totalTime: totalTime, disabled: !!disabled };
|
|
}
|
|
function getOrSetAsInMap(map, key, defaultValue) {
|
|
var value;
|
|
if (map instanceof Map) {
|
|
value = map.get(key);
|
|
if (!value) {
|
|
map.set(key, value = defaultValue);
|
|
}
|
|
}
|
|
else {
|
|
value = map[key];
|
|
if (!value) {
|
|
value = map[key] = defaultValue;
|
|
}
|
|
}
|
|
return value;
|
|
}
|
|
function parseTimelineCommand(command) {
|
|
var separatorPos = command.indexOf(':');
|
|
var id = command.substring(1, separatorPos);
|
|
var action = command.substr(separatorPos + 1);
|
|
return [id, action];
|
|
}
|
|
var _contains = function (elm1, elm2) { return false; };
|
|
var ɵ0 = _contains;
|
|
var _matches = function (element, selector) { return false; };
|
|
var ɵ1 = _matches;
|
|
var _query = function (element, selector, multi) {
|
|
return [];
|
|
};
|
|
var ɵ2 = _query;
|
|
// Define utility methods for browsers and platform-server(domino) where Element
|
|
// and utility methods exist.
|
|
var _isNode = isNode();
|
|
if (_isNode || typeof Element !== 'undefined') {
|
|
if (!isBrowser()) {
|
|
_contains = function (elm1, elm2) { return elm1.contains(elm2); };
|
|
}
|
|
else {
|
|
_contains = function (elm1, elm2) {
|
|
while (elm2 && elm2 !== document.documentElement) {
|
|
if (elm2 === elm1) {
|
|
return true;
|
|
}
|
|
elm2 = elm2.parentNode || elm2.host; // consider host to support shadow DOM
|
|
}
|
|
return false;
|
|
};
|
|
}
|
|
_matches = (function () {
|
|
if (_isNode || Element.prototype.matches) {
|
|
return function (element, selector) { return element.matches(selector); };
|
|
}
|
|
else {
|
|
var proto = Element.prototype;
|
|
var fn_1 = proto.matchesSelector || proto.mozMatchesSelector || proto.msMatchesSelector ||
|
|
proto.oMatchesSelector || proto.webkitMatchesSelector;
|
|
if (fn_1) {
|
|
return function (element, selector) { return fn_1.apply(element, [selector]); };
|
|
}
|
|
else {
|
|
return _matches;
|
|
}
|
|
}
|
|
})();
|
|
_query = function (element, selector, multi) {
|
|
var results = [];
|
|
if (multi) {
|
|
// DO NOT REFACTOR TO USE SPREAD SYNTAX.
|
|
// For element queries that return sufficiently large NodeList objects,
|
|
// using spread syntax to populate the results array causes a RangeError
|
|
// due to the call stack limit being reached. `Array.from` can not be used
|
|
// as well, since NodeList is not iterable in IE 11, see
|
|
// https://developer.mozilla.org/en-US/docs/Web/API/NodeList
|
|
// More info is available in #38551.
|
|
var elems = element.querySelectorAll(selector);
|
|
for (var i = 0; i < elems.length; i++) {
|
|
results.push(elems[i]);
|
|
}
|
|
}
|
|
else {
|
|
var elm = element.querySelector(selector);
|
|
if (elm) {
|
|
results.push(elm);
|
|
}
|
|
}
|
|
return results;
|
|
};
|
|
}
|
|
function containsVendorPrefix(prop) {
|
|
// Webkit is the only real popular vendor prefix nowadays
|
|
// cc: http://shouldiprefix.com/
|
|
return prop.substring(1, 6) == 'ebkit'; // webkit or Webkit
|
|
}
|
|
var _CACHED_BODY = null;
|
|
var _IS_WEBKIT = false;
|
|
function validateStyleProperty(prop) {
|
|
if (!_CACHED_BODY) {
|
|
_CACHED_BODY = getBodyNode() || {};
|
|
_IS_WEBKIT = _CACHED_BODY.style ? ('WebkitAppearance' in _CACHED_BODY.style) : false;
|
|
}
|
|
var result = true;
|
|
if (_CACHED_BODY.style && !containsVendorPrefix(prop)) {
|
|
result = prop in _CACHED_BODY.style;
|
|
if (!result && _IS_WEBKIT) {
|
|
var camelProp = 'Webkit' + prop.charAt(0).toUpperCase() + prop.substr(1);
|
|
result = camelProp in _CACHED_BODY.style;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
function getBodyNode() {
|
|
if (typeof document != 'undefined') {
|
|
return document.body;
|
|
}
|
|
return null;
|
|
}
|
|
var matchesElement = _matches;
|
|
var containsElement = _contains;
|
|
var invokeQuery = _query;
|
|
function hypenatePropsObject(object) {
|
|
var newObj = {};
|
|
Object.keys(object).forEach(function (prop) {
|
|
var newProp = prop.replace(/([a-z])([A-Z])/g, '$1-$2');
|
|
newObj[newProp] = object[prop];
|
|
});
|
|
return newObj;
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright Google LLC All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://angular.io/license
|
|
*/
|
|
/**
|
|
* @publicApi
|
|
*/
|
|
var NoopAnimationDriver = /** @class */ (function () {
|
|
function NoopAnimationDriver() {
|
|
}
|
|
NoopAnimationDriver.prototype.validateStyleProperty = function (prop) {
|
|
return validateStyleProperty(prop);
|
|
};
|
|
NoopAnimationDriver.prototype.matchesElement = function (element, selector) {
|
|
return matchesElement(element, selector);
|
|
};
|
|
NoopAnimationDriver.prototype.containsElement = function (elm1, elm2) {
|
|
return containsElement(elm1, elm2);
|
|
};
|
|
NoopAnimationDriver.prototype.query = function (element, selector, multi) {
|
|
return invokeQuery(element, selector, multi);
|
|
};
|
|
NoopAnimationDriver.prototype.computeStyle = function (element, prop, defaultValue) {
|
|
return defaultValue || '';
|
|
};
|
|
NoopAnimationDriver.prototype.animate = function (element, keyframes, duration, delay, easing, previousPlayers, scrubberAccessRequested) {
|
|
if (previousPlayers === void 0) { previousPlayers = []; }
|
|
return new animations.NoopAnimationPlayer(duration, delay);
|
|
};
|
|
return NoopAnimationDriver;
|
|
}());
|
|
NoopAnimationDriver.decorators = [
|
|
{ type: core.Injectable }
|
|
];
|
|
/**
|
|
* @publicApi
|
|
*/
|
|
var AnimationDriver = /** @class */ (function () {
|
|
function AnimationDriver() {
|
|
}
|
|
return AnimationDriver;
|
|
}());
|
|
AnimationDriver.NOOP = ( /* @__PURE__ */new NoopAnimationDriver());
|
|
|
|
/**
|
|
* @license
|
|
* Copyright Google LLC All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://angular.io/license
|
|
*/
|
|
var ONE_SECOND = 1000;
|
|
var SUBSTITUTION_EXPR_START = '{{';
|
|
var SUBSTITUTION_EXPR_END = '}}';
|
|
var ENTER_CLASSNAME = 'ng-enter';
|
|
var LEAVE_CLASSNAME = 'ng-leave';
|
|
var ENTER_SELECTOR = '.ng-enter';
|
|
var LEAVE_SELECTOR = '.ng-leave';
|
|
var NG_TRIGGER_CLASSNAME = 'ng-trigger';
|
|
var NG_TRIGGER_SELECTOR = '.ng-trigger';
|
|
var NG_ANIMATING_CLASSNAME = 'ng-animating';
|
|
var NG_ANIMATING_SELECTOR = '.ng-animating';
|
|
function resolveTimingValue(value) {
|
|
if (typeof value == 'number')
|
|
return value;
|
|
var matches = value.match(/^(-?[\.\d]+)(m?s)/);
|
|
if (!matches || matches.length < 2)
|
|
return 0;
|
|
return _convertTimeValueToMS(parseFloat(matches[1]), matches[2]);
|
|
}
|
|
function _convertTimeValueToMS(value, unit) {
|
|
switch (unit) {
|
|
case 's':
|
|
return value * ONE_SECOND;
|
|
default: // ms or something else
|
|
return value;
|
|
}
|
|
}
|
|
function resolveTiming(timings, errors, allowNegativeValues) {
|
|
return timings.hasOwnProperty('duration') ?
|
|
timings :
|
|
parseTimeExpression(timings, errors, allowNegativeValues);
|
|
}
|
|
function parseTimeExpression(exp, errors, allowNegativeValues) {
|
|
var regex = /^(-?[\.\d]+)(m?s)(?:\s+(-?[\.\d]+)(m?s))?(?:\s+([-a-z]+(?:\(.+?\))?))?$/i;
|
|
var duration;
|
|
var delay = 0;
|
|
var easing = '';
|
|
if (typeof exp === 'string') {
|
|
var matches = exp.match(regex);
|
|
if (matches === null) {
|
|
errors.push("The provided timing value \"" + exp + "\" is invalid.");
|
|
return { duration: 0, delay: 0, easing: '' };
|
|
}
|
|
duration = _convertTimeValueToMS(parseFloat(matches[1]), matches[2]);
|
|
var delayMatch = matches[3];
|
|
if (delayMatch != null) {
|
|
delay = _convertTimeValueToMS(parseFloat(delayMatch), matches[4]);
|
|
}
|
|
var easingVal = matches[5];
|
|
if (easingVal) {
|
|
easing = easingVal;
|
|
}
|
|
}
|
|
else {
|
|
duration = exp;
|
|
}
|
|
if (!allowNegativeValues) {
|
|
var containsErrors = false;
|
|
var startIndex = errors.length;
|
|
if (duration < 0) {
|
|
errors.push("Duration values below 0 are not allowed for this animation step.");
|
|
containsErrors = true;
|
|
}
|
|
if (delay < 0) {
|
|
errors.push("Delay values below 0 are not allowed for this animation step.");
|
|
containsErrors = true;
|
|
}
|
|
if (containsErrors) {
|
|
errors.splice(startIndex, 0, "The provided timing value \"" + exp + "\" is invalid.");
|
|
}
|
|
}
|
|
return { duration: duration, delay: delay, easing: easing };
|
|
}
|
|
function copyObj(obj, destination) {
|
|
if (destination === void 0) { destination = {}; }
|
|
Object.keys(obj).forEach(function (prop) {
|
|
destination[prop] = obj[prop];
|
|
});
|
|
return destination;
|
|
}
|
|
function normalizeStyles(styles) {
|
|
var normalizedStyles = {};
|
|
if (Array.isArray(styles)) {
|
|
styles.forEach(function (data) { return copyStyles(data, false, normalizedStyles); });
|
|
}
|
|
else {
|
|
copyStyles(styles, false, normalizedStyles);
|
|
}
|
|
return normalizedStyles;
|
|
}
|
|
function copyStyles(styles, readPrototype, destination) {
|
|
if (destination === void 0) { destination = {}; }
|
|
if (readPrototype) {
|
|
// we make use of a for-in loop so that the
|
|
// prototypically inherited properties are
|
|
// revealed from the backFill map
|
|
for (var prop in styles) {
|
|
destination[prop] = styles[prop];
|
|
}
|
|
}
|
|
else {
|
|
copyObj(styles, destination);
|
|
}
|
|
return destination;
|
|
}
|
|
function getStyleAttributeString(element, key, value) {
|
|
// Return the key-value pair string to be added to the style attribute for the
|
|
// given CSS style key.
|
|
if (value) {
|
|
return key + ':' + value + ';';
|
|
}
|
|
else {
|
|
return '';
|
|
}
|
|
}
|
|
function writeStyleAttribute(element) {
|
|
// Read the style property of the element and manually reflect it to the
|
|
// style attribute. This is needed because Domino on platform-server doesn't
|
|
// understand the full set of allowed CSS properties and doesn't reflect some
|
|
// of them automatically.
|
|
var styleAttrValue = '';
|
|
for (var i = 0; i < element.style.length; i++) {
|
|
var key = element.style.item(i);
|
|
styleAttrValue += getStyleAttributeString(element, key, element.style.getPropertyValue(key));
|
|
}
|
|
for (var key in element.style) {
|
|
// Skip internal Domino properties that don't need to be reflected.
|
|
if (!element.style.hasOwnProperty(key) || key.startsWith('_')) {
|
|
continue;
|
|
}
|
|
var dashKey = camelCaseToDashCase(key);
|
|
styleAttrValue += getStyleAttributeString(element, dashKey, element.style[key]);
|
|
}
|
|
element.setAttribute('style', styleAttrValue);
|
|
}
|
|
function setStyles(element, styles, formerStyles) {
|
|
if (element['style']) {
|
|
Object.keys(styles).forEach(function (prop) {
|
|
var camelProp = dashCaseToCamelCase(prop);
|
|
if (formerStyles && !formerStyles.hasOwnProperty(prop)) {
|
|
formerStyles[prop] = element.style[camelProp];
|
|
}
|
|
element.style[camelProp] = styles[prop];
|
|
});
|
|
// On the server set the 'style' attribute since it's not automatically reflected.
|
|
if (isNode()) {
|
|
writeStyleAttribute(element);
|
|
}
|
|
}
|
|
}
|
|
function eraseStyles(element, styles) {
|
|
if (element['style']) {
|
|
Object.keys(styles).forEach(function (prop) {
|
|
var camelProp = dashCaseToCamelCase(prop);
|
|
element.style[camelProp] = '';
|
|
});
|
|
// On the server set the 'style' attribute since it's not automatically reflected.
|
|
if (isNode()) {
|
|
writeStyleAttribute(element);
|
|
}
|
|
}
|
|
}
|
|
function normalizeAnimationEntry(steps) {
|
|
if (Array.isArray(steps)) {
|
|
if (steps.length == 1)
|
|
return steps[0];
|
|
return animations.sequence(steps);
|
|
}
|
|
return steps;
|
|
}
|
|
function validateStyleParams(value, options, errors) {
|
|
var params = options.params || {};
|
|
var matches = extractStyleParams(value);
|
|
if (matches.length) {
|
|
matches.forEach(function (varName) {
|
|
if (!params.hasOwnProperty(varName)) {
|
|
errors.push("Unable to resolve the local animation param " + varName + " in the given list of values");
|
|
}
|
|
});
|
|
}
|
|
}
|
|
var PARAM_REGEX = new RegExp(SUBSTITUTION_EXPR_START + "\\s*(.+?)\\s*" + SUBSTITUTION_EXPR_END, 'g');
|
|
function extractStyleParams(value) {
|
|
var params = [];
|
|
if (typeof value === 'string') {
|
|
var match = void 0;
|
|
while (match = PARAM_REGEX.exec(value)) {
|
|
params.push(match[1]);
|
|
}
|
|
PARAM_REGEX.lastIndex = 0;
|
|
}
|
|
return params;
|
|
}
|
|
function interpolateParams(value, params, errors) {
|
|
var original = value.toString();
|
|
var str = original.replace(PARAM_REGEX, function (_, varName) {
|
|
var localVal = params[varName];
|
|
// this means that the value was never overridden by the data passed in by the user
|
|
if (!params.hasOwnProperty(varName)) {
|
|
errors.push("Please provide a value for the animation param " + varName);
|
|
localVal = '';
|
|
}
|
|
return localVal.toString();
|
|
});
|
|
// we do this to assert that numeric values stay as they are
|
|
return str == original ? value : str;
|
|
}
|
|
function iteratorToArray(iterator) {
|
|
var arr = [];
|
|
var item = iterator.next();
|
|
while (!item.done) {
|
|
arr.push(item.value);
|
|
item = iterator.next();
|
|
}
|
|
return arr;
|
|
}
|
|
var DASH_CASE_REGEXP = /-+([a-z0-9])/g;
|
|
function dashCaseToCamelCase(input) {
|
|
return input.replace(DASH_CASE_REGEXP, function () {
|
|
var m = [];
|
|
for (var _i = 0; _i < arguments.length; _i++) {
|
|
m[_i] = arguments[_i];
|
|
}
|
|
return m[1].toUpperCase();
|
|
});
|
|
}
|
|
function camelCaseToDashCase(input) {
|
|
return input.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
|
|
}
|
|
function allowPreviousPlayerStylesMerge(duration, delay) {
|
|
return duration === 0 || delay === 0;
|
|
}
|
|
function balancePreviousStylesIntoKeyframes(element, keyframes, previousStyles) {
|
|
var previousStyleProps = Object.keys(previousStyles);
|
|
if (previousStyleProps.length && keyframes.length) {
|
|
var startingKeyframe_1 = keyframes[0];
|
|
var missingStyleProps_1 = [];
|
|
previousStyleProps.forEach(function (prop) {
|
|
if (!startingKeyframe_1.hasOwnProperty(prop)) {
|
|
missingStyleProps_1.push(prop);
|
|
}
|
|
startingKeyframe_1[prop] = previousStyles[prop];
|
|
});
|
|
if (missingStyleProps_1.length) {
|
|
var _loop_1 = function () {
|
|
var kf = keyframes[i];
|
|
missingStyleProps_1.forEach(function (prop) {
|
|
kf[prop] = computeStyle(element, prop);
|
|
});
|
|
};
|
|
// tslint:disable-next-line
|
|
for (var i = 1; i < keyframes.length; i++) {
|
|
_loop_1();
|
|
}
|
|
}
|
|
}
|
|
return keyframes;
|
|
}
|
|
function visitDslNode(visitor, node, context) {
|
|
switch (node.type) {
|
|
case 7 /* Trigger */:
|
|
return visitor.visitTrigger(node, context);
|
|
case 0 /* State */:
|
|
return visitor.visitState(node, context);
|
|
case 1 /* Transition */:
|
|
return visitor.visitTransition(node, context);
|
|
case 2 /* Sequence */:
|
|
return visitor.visitSequence(node, context);
|
|
case 3 /* Group */:
|
|
return visitor.visitGroup(node, context);
|
|
case 4 /* Animate */:
|
|
return visitor.visitAnimate(node, context);
|
|
case 5 /* Keyframes */:
|
|
return visitor.visitKeyframes(node, context);
|
|
case 6 /* Style */:
|
|
return visitor.visitStyle(node, context);
|
|
case 8 /* Reference */:
|
|
return visitor.visitReference(node, context);
|
|
case 9 /* AnimateChild */:
|
|
return visitor.visitAnimateChild(node, context);
|
|
case 10 /* AnimateRef */:
|
|
return visitor.visitAnimateRef(node, context);
|
|
case 11 /* Query */:
|
|
return visitor.visitQuery(node, context);
|
|
case 12 /* Stagger */:
|
|
return visitor.visitStagger(node, context);
|
|
default:
|
|
throw new Error("Unable to resolve animation metadata node #" + node.type);
|
|
}
|
|
}
|
|
function computeStyle(element, prop) {
|
|
return window.getComputedStyle(element)[prop];
|
|
}
|
|
|
|
/*! *****************************************************************************
|
|
Copyright (c) Microsoft Corporation.
|
|
|
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
purpose with or without fee is hereby granted.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
PERFORMANCE OF THIS SOFTWARE.
|
|
***************************************************************************** */
|
|
/* global Reflect, Promise */
|
|
var extendStatics = function (d, b) {
|
|
extendStatics = Object.setPrototypeOf ||
|
|
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
function (d, b) { for (var p in b)
|
|
if (Object.prototype.hasOwnProperty.call(b, p))
|
|
d[p] = b[p]; };
|
|
return extendStatics(d, b);
|
|
};
|
|
function __extends(d, b) {
|
|
if (typeof b !== "function" && b !== null)
|
|
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
|
extendStatics(d, b);
|
|
function __() { this.constructor = d; }
|
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
}
|
|
var __assign = function () {
|
|
__assign = Object.assign || function __assign(t) {
|
|
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
s = arguments[i];
|
|
for (var p in s)
|
|
if (Object.prototype.hasOwnProperty.call(s, p))
|
|
t[p] = s[p];
|
|
}
|
|
return t;
|
|
};
|
|
return __assign.apply(this, arguments);
|
|
};
|
|
function __rest(s, e) {
|
|
var t = {};
|
|
for (var p in s)
|
|
if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
t[p] = s[p];
|
|
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
t[p[i]] = s[p[i]];
|
|
}
|
|
return t;
|
|
}
|
|
function __decorate(decorators, target, key, desc) {
|
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
|
|
r = Reflect.decorate(decorators, target, key, desc);
|
|
else
|
|
for (var i = decorators.length - 1; i >= 0; i--)
|
|
if (d = decorators[i])
|
|
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
}
|
|
function __param(paramIndex, decorator) {
|
|
return function (target, key) { decorator(target, key, paramIndex); };
|
|
}
|
|
function __metadata(metadataKey, metadataValue) {
|
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function")
|
|
return Reflect.metadata(metadataKey, metadataValue);
|
|
}
|
|
function __awaiter(thisArg, _arguments, P, generator) {
|
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
function fulfilled(value) { try {
|
|
step(generator.next(value));
|
|
}
|
|
catch (e) {
|
|
reject(e);
|
|
} }
|
|
function rejected(value) { try {
|
|
step(generator["throw"](value));
|
|
}
|
|
catch (e) {
|
|
reject(e);
|
|
} }
|
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
});
|
|
}
|
|
function __generator(thisArg, body) {
|
|
var _ = { label: 0, sent: function () { if (t[0] & 1)
|
|
throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function () { return this; }), g;
|
|
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
function step(op) {
|
|
if (f)
|
|
throw new TypeError("Generator is already executing.");
|
|
while (_)
|
|
try {
|
|
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done)
|
|
return t;
|
|
if (y = 0, t)
|
|
op = [op[0] & 2, t.value];
|
|
switch (op[0]) {
|
|
case 0:
|
|
case 1:
|
|
t = op;
|
|
break;
|
|
case 4:
|
|
_.label++;
|
|
return { value: op[1], done: false };
|
|
case 5:
|
|
_.label++;
|
|
y = op[1];
|
|
op = [0];
|
|
continue;
|
|
case 7:
|
|
op = _.ops.pop();
|
|
_.trys.pop();
|
|
continue;
|
|
default:
|
|
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
|
|
_ = 0;
|
|
continue;
|
|
}
|
|
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) {
|
|
_.label = op[1];
|
|
break;
|
|
}
|
|
if (op[0] === 6 && _.label < t[1]) {
|
|
_.label = t[1];
|
|
t = op;
|
|
break;
|
|
}
|
|
if (t && _.label < t[2]) {
|
|
_.label = t[2];
|
|
_.ops.push(op);
|
|
break;
|
|
}
|
|
if (t[2])
|
|
_.ops.pop();
|
|
_.trys.pop();
|
|
continue;
|
|
}
|
|
op = body.call(thisArg, _);
|
|
}
|
|
catch (e) {
|
|
op = [6, e];
|
|
y = 0;
|
|
}
|
|
finally {
|
|
f = t = 0;
|
|
}
|
|
if (op[0] & 5)
|
|
throw op[1];
|
|
return { value: op[0] ? op[1] : void 0, done: true };
|
|
}
|
|
}
|
|
var __createBinding = Object.create ? (function (o, m, k, k2) {
|
|
if (k2 === undefined)
|
|
k2 = k;
|
|
Object.defineProperty(o, k2, { enumerable: true, get: function () { return m[k]; } });
|
|
}) : (function (o, m, k, k2) {
|
|
if (k2 === undefined)
|
|
k2 = k;
|
|
o[k2] = m[k];
|
|
});
|
|
function __exportStar(m, o) {
|
|
for (var p in m)
|
|
if (p !== "default" && !Object.prototype.hasOwnProperty.call(o, p))
|
|
__createBinding(o, m, p);
|
|
}
|
|
function __values(o) {
|
|
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
|
if (m)
|
|
return m.call(o);
|
|
if (o && typeof o.length === "number")
|
|
return {
|
|
next: function () {
|
|
if (o && i >= o.length)
|
|
o = void 0;
|
|
return { value: o && o[i++], done: !o };
|
|
}
|
|
};
|
|
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
|
}
|
|
function __read(o, n) {
|
|
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
if (!m)
|
|
return o;
|
|
var i = m.call(o), r, ar = [], e;
|
|
try {
|
|
while ((n === void 0 || n-- > 0) && !(r = i.next()).done)
|
|
ar.push(r.value);
|
|
}
|
|
catch (error) {
|
|
e = { error: error };
|
|
}
|
|
finally {
|
|
try {
|
|
if (r && !r.done && (m = i["return"]))
|
|
m.call(i);
|
|
}
|
|
finally {
|
|
if (e)
|
|
throw e.error;
|
|
}
|
|
}
|
|
return ar;
|
|
}
|
|
/** @deprecated */
|
|
function __spread() {
|
|
for (var ar = [], i = 0; i < arguments.length; i++)
|
|
ar = ar.concat(__read(arguments[i]));
|
|
return ar;
|
|
}
|
|
/** @deprecated */
|
|
function __spreadArrays() {
|
|
for (var s = 0, i = 0, il = arguments.length; i < il; i++)
|
|
s += arguments[i].length;
|
|
for (var r = Array(s), k = 0, i = 0; i < il; i++)
|
|
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
|
|
r[k] = a[j];
|
|
return r;
|
|
}
|
|
function __spreadArray(to, from, pack) {
|
|
if (pack || arguments.length === 2)
|
|
for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
if (ar || !(i in from)) {
|
|
if (!ar)
|
|
ar = Array.prototype.slice.call(from, 0, i);
|
|
ar[i] = from[i];
|
|
}
|
|
}
|
|
return to.concat(ar || Array.prototype.slice.call(from));
|
|
}
|
|
function __await(v) {
|
|
return this instanceof __await ? (this.v = v, this) : new __await(v);
|
|
}
|
|
function __asyncGenerator(thisArg, _arguments, generator) {
|
|
if (!Symbol.asyncIterator)
|
|
throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
var g = generator.apply(thisArg, _arguments || []), i, q = [];
|
|
return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i;
|
|
function verb(n) { if (g[n])
|
|
i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }
|
|
function resume(n, v) { try {
|
|
step(g[n](v));
|
|
}
|
|
catch (e) {
|
|
settle(q[0][3], e);
|
|
} }
|
|
function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
|
|
function fulfill(value) { resume("next", value); }
|
|
function reject(value) { resume("throw", value); }
|
|
function settle(f, v) { if (f(v), q.shift(), q.length)
|
|
resume(q[0][0], q[0][1]); }
|
|
}
|
|
function __asyncDelegator(o) {
|
|
var i, p;
|
|
return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i;
|
|
function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; } : f; }
|
|
}
|
|
function __asyncValues(o) {
|
|
if (!Symbol.asyncIterator)
|
|
throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
var m = o[Symbol.asyncIterator], i;
|
|
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
|
|
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
|
|
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function (v) { resolve({ value: v, done: d }); }, reject); }
|
|
}
|
|
function __makeTemplateObject(cooked, raw) {
|
|
if (Object.defineProperty) {
|
|
Object.defineProperty(cooked, "raw", { value: raw });
|
|
}
|
|
else {
|
|
cooked.raw = raw;
|
|
}
|
|
return cooked;
|
|
}
|
|
;
|
|
var __setModuleDefault = Object.create ? (function (o, v) {
|
|
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
}) : function (o, v) {
|
|
o["default"] = v;
|
|
};
|
|
function __importStar(mod) {
|
|
if (mod && mod.__esModule)
|
|
return mod;
|
|
var result = {};
|
|
if (mod != null)
|
|
for (var k in mod)
|
|
if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k))
|
|
__createBinding(result, mod, k);
|
|
__setModuleDefault(result, mod);
|
|
return result;
|
|
}
|
|
function __importDefault(mod) {
|
|
return (mod && mod.__esModule) ? mod : { default: mod };
|
|
}
|
|
function __classPrivateFieldGet(receiver, state, kind, f) {
|
|
if (kind === "a" && !f)
|
|
throw new TypeError("Private accessor was defined without a getter");
|
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver))
|
|
throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
}
|
|
function __classPrivateFieldSet(receiver, state, value, kind, f) {
|
|
if (kind === "m")
|
|
throw new TypeError("Private method is not writable");
|
|
if (kind === "a" && !f)
|
|
throw new TypeError("Private accessor was defined without a setter");
|
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver))
|
|
throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright Google LLC All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://angular.io/license
|
|
*/
|
|
var ANY_STATE = '*';
|
|
function parseTransitionExpr(transitionValue, errors) {
|
|
var expressions = [];
|
|
if (typeof transitionValue == 'string') {
|
|
transitionValue.split(/\s*,\s*/).forEach(function (str) { return parseInnerTransitionStr(str, expressions, errors); });
|
|
}
|
|
else {
|
|
expressions.push(transitionValue);
|
|
}
|
|
return expressions;
|
|
}
|
|
function parseInnerTransitionStr(eventStr, expressions, errors) {
|
|
if (eventStr[0] == ':') {
|
|
var result = parseAnimationAlias(eventStr, errors);
|
|
if (typeof result == 'function') {
|
|
expressions.push(result);
|
|
return;
|
|
}
|
|
eventStr = result;
|
|
}
|
|
var match = eventStr.match(/^(\*|[-\w]+)\s*(<?[=-]>)\s*(\*|[-\w]+)$/);
|
|
if (match == null || match.length < 4) {
|
|
errors.push("The provided transition expression \"" + eventStr + "\" is not supported");
|
|
return expressions;
|
|
}
|
|
var fromState = match[1];
|
|
var separator = match[2];
|
|
var toState = match[3];
|
|
expressions.push(makeLambdaFromStates(fromState, toState));
|
|
var isFullAnyStateExpr = fromState == ANY_STATE && toState == ANY_STATE;
|
|
if (separator[0] == '<' && !isFullAnyStateExpr) {
|
|
expressions.push(makeLambdaFromStates(toState, fromState));
|
|
}
|
|
}
|
|
function parseAnimationAlias(alias, errors) {
|
|
switch (alias) {
|
|
case ':enter':
|
|
return 'void => *';
|
|
case ':leave':
|
|
return '* => void';
|
|
case ':increment':
|
|
return function (fromState, toState) { return parseFloat(toState) > parseFloat(fromState); };
|
|
case ':decrement':
|
|
return function (fromState, toState) { return parseFloat(toState) < parseFloat(fromState); };
|
|
default:
|
|
errors.push("The transition alias value \"" + alias + "\" is not supported");
|
|
return '* => *';
|
|
}
|
|
}
|
|
// DO NOT REFACTOR ... keep the follow set instantiations
|
|
// with the values intact (closure compiler for some reason
|
|
// removes follow-up lines that add the values outside of
|
|
// the constructor...
|
|
var TRUE_BOOLEAN_VALUES = new Set(['true', '1']);
|
|
var FALSE_BOOLEAN_VALUES = new Set(['false', '0']);
|
|
function makeLambdaFromStates(lhs, rhs) {
|
|
var LHS_MATCH_BOOLEAN = TRUE_BOOLEAN_VALUES.has(lhs) || FALSE_BOOLEAN_VALUES.has(lhs);
|
|
var RHS_MATCH_BOOLEAN = TRUE_BOOLEAN_VALUES.has(rhs) || FALSE_BOOLEAN_VALUES.has(rhs);
|
|
return function (fromState, toState) {
|
|
var lhsMatch = lhs == ANY_STATE || lhs == fromState;
|
|
var rhsMatch = rhs == ANY_STATE || rhs == toState;
|
|
if (!lhsMatch && LHS_MATCH_BOOLEAN && typeof fromState === 'boolean') {
|
|
lhsMatch = fromState ? TRUE_BOOLEAN_VALUES.has(lhs) : FALSE_BOOLEAN_VALUES.has(lhs);
|
|
}
|
|
if (!rhsMatch && RHS_MATCH_BOOLEAN && typeof toState === 'boolean') {
|
|
rhsMatch = toState ? TRUE_BOOLEAN_VALUES.has(rhs) : FALSE_BOOLEAN_VALUES.has(rhs);
|
|
}
|
|
return lhsMatch && rhsMatch;
|
|
};
|
|
}
|
|
|
|
var SELF_TOKEN = ':self';
|
|
var SELF_TOKEN_REGEX = new RegExp("s*" + SELF_TOKEN + "s*,?", 'g');
|
|
/*
|
|
* [Validation]
|
|
* The visitor code below will traverse the animation AST generated by the animation verb functions
|
|
* (the output is a tree of objects) and attempt to perform a series of validations on the data. The
|
|
* following corner-cases will be validated:
|
|
*
|
|
* 1. Overlap of animations
|
|
* Given that a CSS property cannot be animated in more than one place at the same time, it's
|
|
* important that this behavior is detected and validated. The way in which this occurs is that
|
|
* each time a style property is examined, a string-map containing the property will be updated with
|
|
* the start and end times for when the property is used within an animation step.
|
|
*
|
|
* If there are two or more parallel animations that are currently running (these are invoked by the
|
|
* group()) on the same element then the validator will throw an error. Since the start/end timing
|
|
* values are collected for each property then if the current animation step is animating the same
|
|
* property and its timing values fall anywhere into the window of time that the property is
|
|
* currently being animated within then this is what causes an error.
|
|
*
|
|
* 2. Timing values
|
|
* The validator will validate to see if a timing value of `duration delay easing` or
|
|
* `durationNumber` is valid or not.
|
|
*
|
|
* (note that upon validation the code below will replace the timing data with an object containing
|
|
* {duration,delay,easing}.
|
|
*
|
|
* 3. Offset Validation
|
|
* Each of the style() calls are allowed to have an offset value when placed inside of keyframes().
|
|
* Offsets within keyframes() are considered valid when:
|
|
*
|
|
* - No offsets are used at all
|
|
* - Each style() entry contains an offset value
|
|
* - Each offset is between 0 and 1
|
|
* - Each offset is greater to or equal than the previous one
|
|
*
|
|
* Otherwise an error will be thrown.
|
|
*/
|
|
function buildAnimationAst(driver, metadata, errors) {
|
|
return new AnimationAstBuilderVisitor(driver).build(metadata, errors);
|
|
}
|
|
var ROOT_SELECTOR = '';
|
|
var AnimationAstBuilderVisitor = /** @class */ (function () {
|
|
function AnimationAstBuilderVisitor(_driver) {
|
|
this._driver = _driver;
|
|
}
|
|
AnimationAstBuilderVisitor.prototype.build = function (metadata, errors) {
|
|
var context = new AnimationAstBuilderContext(errors);
|
|
this._resetContextStyleTimingState(context);
|
|
return visitDslNode(this, normalizeAnimationEntry(metadata), context);
|
|
};
|
|
AnimationAstBuilderVisitor.prototype._resetContextStyleTimingState = function (context) {
|
|
context.currentQuerySelector = ROOT_SELECTOR;
|
|
context.collectedStyles = {};
|
|
context.collectedStyles[ROOT_SELECTOR] = {};
|
|
context.currentTime = 0;
|
|
};
|
|
AnimationAstBuilderVisitor.prototype.visitTrigger = function (metadata, context) {
|
|
var _this = this;
|
|
var queryCount = context.queryCount = 0;
|
|
var depCount = context.depCount = 0;
|
|
var states = [];
|
|
var transitions = [];
|
|
if (metadata.name.charAt(0) == '@') {
|
|
context.errors.push('animation triggers cannot be prefixed with an `@` sign (e.g. trigger(\'@foo\', [...]))');
|
|
}
|
|
metadata.definitions.forEach(function (def) {
|
|
_this._resetContextStyleTimingState(context);
|
|
if (def.type == 0 /* State */) {
|
|
var stateDef_1 = def;
|
|
var name = stateDef_1.name;
|
|
name.toString().split(/\s*,\s*/).forEach(function (n) {
|
|
stateDef_1.name = n;
|
|
states.push(_this.visitState(stateDef_1, context));
|
|
});
|
|
stateDef_1.name = name;
|
|
}
|
|
else if (def.type == 1 /* Transition */) {
|
|
var transition = _this.visitTransition(def, context);
|
|
queryCount += transition.queryCount;
|
|
depCount += transition.depCount;
|
|
transitions.push(transition);
|
|
}
|
|
else {
|
|
context.errors.push('only state() and transition() definitions can sit inside of a trigger()');
|
|
}
|
|
});
|
|
return {
|
|
type: 7 /* Trigger */,
|
|
name: metadata.name,
|
|
states: states,
|
|
transitions: transitions,
|
|
queryCount: queryCount,
|
|
depCount: depCount,
|
|
options: null
|
|
};
|
|
};
|
|
AnimationAstBuilderVisitor.prototype.visitState = function (metadata, context) {
|
|
var styleAst = this.visitStyle(metadata.styles, context);
|
|
var astParams = (metadata.options && metadata.options.params) || null;
|
|
if (styleAst.containsDynamicStyles) {
|
|
var missingSubs_1 = new Set();
|
|
var params_1 = astParams || {};
|
|
styleAst.styles.forEach(function (value) {
|
|
if (isObject(value)) {
|
|
var stylesObj_1 = value;
|
|
Object.keys(stylesObj_1).forEach(function (prop) {
|
|
extractStyleParams(stylesObj_1[prop]).forEach(function (sub) {
|
|
if (!params_1.hasOwnProperty(sub)) {
|
|
missingSubs_1.add(sub);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
});
|
|
if (missingSubs_1.size) {
|
|
var missingSubsArr = iteratorToArray(missingSubs_1.values());
|
|
context.errors.push("state(\"" + metadata
|
|
.name + "\", ...) must define default values for all the following style substitutions: " + missingSubsArr.join(', '));
|
|
}
|
|
}
|
|
return {
|
|
type: 0 /* State */,
|
|
name: metadata.name,
|
|
style: styleAst,
|
|
options: astParams ? { params: astParams } : null
|
|
};
|
|
};
|
|
AnimationAstBuilderVisitor.prototype.visitTransition = function (metadata, context) {
|
|
context.queryCount = 0;
|
|
context.depCount = 0;
|
|
var animation = visitDslNode(this, normalizeAnimationEntry(metadata.animation), context);
|
|
var matchers = parseTransitionExpr(metadata.expr, context.errors);
|
|
return {
|
|
type: 1 /* Transition */,
|
|
matchers: matchers,
|
|
animation: animation,
|
|
queryCount: context.queryCount,
|
|
depCount: context.depCount,
|
|
options: normalizeAnimationOptions(metadata.options)
|
|
};
|
|
};
|
|
AnimationAstBuilderVisitor.prototype.visitSequence = function (metadata, context) {
|
|
var _this = this;
|
|
return {
|
|
type: 2 /* Sequence */,
|
|
steps: metadata.steps.map(function (s) { return visitDslNode(_this, s, context); }),
|
|
options: normalizeAnimationOptions(metadata.options)
|
|
};
|
|
};
|
|
AnimationAstBuilderVisitor.prototype.visitGroup = function (metadata, context) {
|
|
var _this = this;
|
|
var currentTime = context.currentTime;
|
|
var furthestTime = 0;
|
|
var steps = metadata.steps.map(function (step) {
|
|
context.currentTime = currentTime;
|
|
var innerAst = visitDslNode(_this, step, context);
|
|
furthestTime = Math.max(furthestTime, context.currentTime);
|
|
return innerAst;
|
|
});
|
|
context.currentTime = furthestTime;
|
|
return {
|
|
type: 3 /* Group */,
|
|
steps: steps,
|
|
options: normalizeAnimationOptions(metadata.options)
|
|
};
|
|
};
|
|
AnimationAstBuilderVisitor.prototype.visitAnimate = function (metadata, context) {
|
|
var timingAst = constructTimingAst(metadata.timings, context.errors);
|
|
context.currentAnimateTimings = timingAst;
|
|
var styleAst;
|
|
var styleMetadata = metadata.styles ? metadata.styles : animations.style({});
|
|
if (styleMetadata.type == 5 /* Keyframes */) {
|
|
styleAst = this.visitKeyframes(styleMetadata, context);
|
|
}
|
|
else {
|
|
var styleMetadata_1 = metadata.styles;
|
|
var isEmpty = false;
|
|
if (!styleMetadata_1) {
|
|
isEmpty = true;
|
|
var newStyleData = {};
|
|
if (timingAst.easing) {
|
|
newStyleData['easing'] = timingAst.easing;
|
|
}
|
|
styleMetadata_1 = animations.style(newStyleData);
|
|
}
|
|
context.currentTime += timingAst.duration + timingAst.delay;
|
|
var _styleAst = this.visitStyle(styleMetadata_1, context);
|
|
_styleAst.isEmptyStep = isEmpty;
|
|
styleAst = _styleAst;
|
|
}
|
|
context.currentAnimateTimings = null;
|
|
return {
|
|
type: 4 /* Animate */,
|
|
timings: timingAst,
|
|
style: styleAst,
|
|
options: null
|
|
};
|
|
};
|
|
AnimationAstBuilderVisitor.prototype.visitStyle = function (metadata, context) {
|
|
var ast = this._makeStyleAst(metadata, context);
|
|
this._validateStyleAst(ast, context);
|
|
return ast;
|
|
};
|
|
AnimationAstBuilderVisitor.prototype._makeStyleAst = function (metadata, context) {
|
|
var styles = [];
|
|
if (Array.isArray(metadata.styles)) {
|
|
metadata.styles.forEach(function (styleTuple) {
|
|
if (typeof styleTuple == 'string') {
|
|
if (styleTuple == animations.AUTO_STYLE) {
|
|
styles.push(styleTuple);
|
|
}
|
|
else {
|
|
context.errors.push("The provided style string value " + styleTuple + " is not allowed.");
|
|
}
|
|
}
|
|
else {
|
|
styles.push(styleTuple);
|
|
}
|
|
});
|
|
}
|
|
else {
|
|
styles.push(metadata.styles);
|
|
}
|
|
var containsDynamicStyles = false;
|
|
var collectedEasing = null;
|
|
styles.forEach(function (styleData) {
|
|
if (isObject(styleData)) {
|
|
var styleMap = styleData;
|
|
var easing = styleMap['easing'];
|
|
if (easing) {
|
|
collectedEasing = easing;
|
|
delete styleMap['easing'];
|
|
}
|
|
if (!containsDynamicStyles) {
|
|
for (var prop in styleMap) {
|
|
var value = styleMap[prop];
|
|
if (value.toString().indexOf(SUBSTITUTION_EXPR_START) >= 0) {
|
|
containsDynamicStyles = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
return {
|
|
type: 6 /* Style */,
|
|
styles: styles,
|
|
easing: collectedEasing,
|
|
offset: metadata.offset,
|
|
containsDynamicStyles: containsDynamicStyles,
|
|
options: null
|
|
};
|
|
};
|
|
AnimationAstBuilderVisitor.prototype._validateStyleAst = function (ast, context) {
|
|
var _this = this;
|
|
var timings = context.currentAnimateTimings;
|
|
var endTime = context.currentTime;
|
|
var startTime = context.currentTime;
|
|
if (timings && startTime > 0) {
|
|
startTime -= timings.duration + timings.delay;
|
|
}
|
|
ast.styles.forEach(function (tuple) {
|
|
if (typeof tuple == 'string')
|
|
return;
|
|
Object.keys(tuple).forEach(function (prop) {
|
|
if (!_this._driver.validateStyleProperty(prop)) {
|
|
context.errors.push("The provided animation property \"" + prop + "\" is not a supported CSS property for animations");
|
|
return;
|
|
}
|
|
var collectedStyles = context.collectedStyles[context.currentQuerySelector];
|
|
var collectedEntry = collectedStyles[prop];
|
|
var updateCollectedStyle = true;
|
|
if (collectedEntry) {
|
|
if (startTime != endTime && startTime >= collectedEntry.startTime &&
|
|
endTime <= collectedEntry.endTime) {
|
|
context.errors.push("The CSS property \"" + prop + "\" that exists between the times of \"" + collectedEntry.startTime + "ms\" and \"" + collectedEntry
|
|
.endTime + "ms\" is also being animated in a parallel animation between the times of \"" + startTime + "ms\" and \"" + endTime + "ms\"");
|
|
updateCollectedStyle = false;
|
|
}
|
|
// we always choose the smaller start time value since we
|
|
// want to have a record of the entire animation window where
|
|
// the style property is being animated in between
|
|
startTime = collectedEntry.startTime;
|
|
}
|
|
if (updateCollectedStyle) {
|
|
collectedStyles[prop] = { startTime: startTime, endTime: endTime };
|
|
}
|
|
if (context.options) {
|
|
validateStyleParams(tuple[prop], context.options, context.errors);
|
|
}
|
|
});
|
|
});
|
|
};
|
|
AnimationAstBuilderVisitor.prototype.visitKeyframes = function (metadata, context) {
|
|
var _this = this;
|
|
var ast = { type: 5 /* Keyframes */, styles: [], options: null };
|
|
if (!context.currentAnimateTimings) {
|
|
context.errors.push("keyframes() must be placed inside of a call to animate()");
|
|
return ast;
|
|
}
|
|
var MAX_KEYFRAME_OFFSET = 1;
|
|
var totalKeyframesWithOffsets = 0;
|
|
var offsets = [];
|
|
var offsetsOutOfOrder = false;
|
|
var keyframesOutOfRange = false;
|
|
var previousOffset = 0;
|
|
var keyframes = metadata.steps.map(function (styles) {
|
|
var style = _this._makeStyleAst(styles, context);
|
|
var offsetVal = style.offset != null ? style.offset : consumeOffset(style.styles);
|
|
var offset = 0;
|
|
if (offsetVal != null) {
|
|
totalKeyframesWithOffsets++;
|
|
offset = style.offset = offsetVal;
|
|
}
|
|
keyframesOutOfRange = keyframesOutOfRange || offset < 0 || offset > 1;
|
|
offsetsOutOfOrder = offsetsOutOfOrder || offset < previousOffset;
|
|
previousOffset = offset;
|
|
offsets.push(offset);
|
|
return style;
|
|
});
|
|
if (keyframesOutOfRange) {
|
|
context.errors.push("Please ensure that all keyframe offsets are between 0 and 1");
|
|
}
|
|
if (offsetsOutOfOrder) {
|
|
context.errors.push("Please ensure that all keyframe offsets are in order");
|
|
}
|
|
var length = metadata.steps.length;
|
|
var generatedOffset = 0;
|
|
if (totalKeyframesWithOffsets > 0 && totalKeyframesWithOffsets < length) {
|
|
context.errors.push("Not all style() steps within the declared keyframes() contain offsets");
|
|
}
|
|
else if (totalKeyframesWithOffsets == 0) {
|
|
generatedOffset = MAX_KEYFRAME_OFFSET / (length - 1);
|
|
}
|
|
var limit = length - 1;
|
|
var currentTime = context.currentTime;
|
|
var currentAnimateTimings = context.currentAnimateTimings;
|
|
var animateDuration = currentAnimateTimings.duration;
|
|
keyframes.forEach(function (kf, i) {
|
|
var offset = generatedOffset > 0 ? (i == limit ? 1 : (generatedOffset * i)) : offsets[i];
|
|
var durationUpToThisFrame = offset * animateDuration;
|
|
context.currentTime = currentTime + currentAnimateTimings.delay + durationUpToThisFrame;
|
|
currentAnimateTimings.duration = durationUpToThisFrame;
|
|
_this._validateStyleAst(kf, context);
|
|
kf.offset = offset;
|
|
ast.styles.push(kf);
|
|
});
|
|
return ast;
|
|
};
|
|
AnimationAstBuilderVisitor.prototype.visitReference = function (metadata, context) {
|
|
return {
|
|
type: 8 /* Reference */,
|
|
animation: visitDslNode(this, normalizeAnimationEntry(metadata.animation), context),
|
|
options: normalizeAnimationOptions(metadata.options)
|
|
};
|
|
};
|
|
AnimationAstBuilderVisitor.prototype.visitAnimateChild = function (metadata, context) {
|
|
context.depCount++;
|
|
return {
|
|
type: 9 /* AnimateChild */,
|
|
options: normalizeAnimationOptions(metadata.options)
|
|
};
|
|
};
|
|
AnimationAstBuilderVisitor.prototype.visitAnimateRef = function (metadata, context) {
|
|
return {
|
|
type: 10 /* AnimateRef */,
|
|
animation: this.visitReference(metadata.animation, context),
|
|
options: normalizeAnimationOptions(metadata.options)
|
|
};
|
|
};
|
|
AnimationAstBuilderVisitor.prototype.visitQuery = function (metadata, context) {
|
|
var parentSelector = context.currentQuerySelector;
|
|
var options = (metadata.options || {});
|
|
context.queryCount++;
|
|
context.currentQuery = metadata;
|
|
var _a = __read(normalizeSelector(metadata.selector), 2), selector = _a[0], includeSelf = _a[1];
|
|
context.currentQuerySelector =
|
|
parentSelector.length ? (parentSelector + ' ' + selector) : selector;
|
|
getOrSetAsInMap(context.collectedStyles, context.currentQuerySelector, {});
|
|
var animation = visitDslNode(this, normalizeAnimationEntry(metadata.animation), context);
|
|
context.currentQuery = null;
|
|
context.currentQuerySelector = parentSelector;
|
|
return {
|
|
type: 11 /* Query */,
|
|
selector: selector,
|
|
limit: options.limit || 0,
|
|
optional: !!options.optional,
|
|
includeSelf: includeSelf,
|
|
animation: animation,
|
|
originalSelector: metadata.selector,
|
|
options: normalizeAnimationOptions(metadata.options)
|
|
};
|
|
};
|
|
AnimationAstBuilderVisitor.prototype.visitStagger = function (metadata, context) {
|
|
if (!context.currentQuery) {
|
|
context.errors.push("stagger() can only be used inside of query()");
|
|
}
|
|
var timings = metadata.timings === 'full' ?
|
|
{ duration: 0, delay: 0, easing: 'full' } :
|
|
resolveTiming(metadata.timings, context.errors, true);
|
|
return {
|
|
type: 12 /* Stagger */,
|
|
animation: visitDslNode(this, normalizeAnimationEntry(metadata.animation), context),
|
|
timings: timings,
|
|
options: null
|
|
};
|
|
};
|
|
return AnimationAstBuilderVisitor;
|
|
}());
|
|
function normalizeSelector(selector) {
|
|
var hasAmpersand = selector.split(/\s*,\s*/).find(function (token) { return token == SELF_TOKEN; }) ? true : false;
|
|
if (hasAmpersand) {
|
|
selector = selector.replace(SELF_TOKEN_REGEX, '');
|
|
}
|
|
// the :enter and :leave selectors are filled in at runtime during timeline building
|
|
selector = selector.replace(/@\*/g, NG_TRIGGER_SELECTOR)
|
|
.replace(/@\w+/g, function (match) { return NG_TRIGGER_SELECTOR + '-' + match.substr(1); })
|
|
.replace(/:animating/g, NG_ANIMATING_SELECTOR);
|
|
return [selector, hasAmpersand];
|
|
}
|
|
function normalizeParams(obj) {
|
|
return obj ? copyObj(obj) : null;
|
|
}
|
|
var AnimationAstBuilderContext = /** @class */ (function () {
|
|
function AnimationAstBuilderContext(errors) {
|
|
this.errors = errors;
|
|
this.queryCount = 0;
|
|
this.depCount = 0;
|
|
this.currentTransition = null;
|
|
this.currentQuery = null;
|
|
this.currentQuerySelector = null;
|
|
this.currentAnimateTimings = null;
|
|
this.currentTime = 0;
|
|
this.collectedStyles = {};
|
|
this.options = null;
|
|
}
|
|
return AnimationAstBuilderContext;
|
|
}());
|
|
function consumeOffset(styles) {
|
|
if (typeof styles == 'string')
|
|
return null;
|
|
var offset = null;
|
|
if (Array.isArray(styles)) {
|
|
styles.forEach(function (styleTuple) {
|
|
if (isObject(styleTuple) && styleTuple.hasOwnProperty('offset')) {
|
|
var obj = styleTuple;
|
|
offset = parseFloat(obj['offset']);
|
|
delete obj['offset'];
|
|
}
|
|
});
|
|
}
|
|
else if (isObject(styles) && styles.hasOwnProperty('offset')) {
|
|
var obj = styles;
|
|
offset = parseFloat(obj['offset']);
|
|
delete obj['offset'];
|
|
}
|
|
return offset;
|
|
}
|
|
function isObject(value) {
|
|
return !Array.isArray(value) && typeof value == 'object';
|
|
}
|
|
function constructTimingAst(value, errors) {
|
|
var timings = null;
|
|
if (value.hasOwnProperty('duration')) {
|
|
timings = value;
|
|
}
|
|
else if (typeof value == 'number') {
|
|
var duration = resolveTiming(value, errors).duration;
|
|
return makeTimingAst(duration, 0, '');
|
|
}
|
|
var strValue = value;
|
|
var isDynamic = strValue.split(/\s+/).some(function (v) { return v.charAt(0) == '{' && v.charAt(1) == '{'; });
|
|
if (isDynamic) {
|
|
var ast = makeTimingAst(0, 0, '');
|
|
ast.dynamic = true;
|
|
ast.strValue = strValue;
|
|
return ast;
|
|
}
|
|
timings = timings || resolveTiming(strValue, errors);
|
|
return makeTimingAst(timings.duration, timings.delay, timings.easing);
|
|
}
|
|
function normalizeAnimationOptions(options) {
|
|
if (options) {
|
|
options = copyObj(options);
|
|
if (options['params']) {
|
|
options['params'] = normalizeParams(options['params']);
|
|
}
|
|
}
|
|
else {
|
|
options = {};
|
|
}
|
|
return options;
|
|
}
|
|
function makeTimingAst(duration, delay, easing) {
|
|
return { duration: duration, delay: delay, easing: easing };
|
|
}
|
|
|
|
function createTimelineInstruction(element, keyframes, preStyleProps, postStyleProps, duration, delay, easing, subTimeline) {
|
|
if (easing === void 0) { easing = null; }
|
|
if (subTimeline === void 0) { subTimeline = false; }
|
|
return {
|
|
type: 1 /* TimelineAnimation */,
|
|
element: element,
|
|
keyframes: keyframes,
|
|
preStyleProps: preStyleProps,
|
|
postStyleProps: postStyleProps,
|
|
duration: duration,
|
|
delay: delay,
|
|
totalTime: duration + delay,
|
|
easing: easing,
|
|
subTimeline: subTimeline
|
|
};
|
|
}
|
|
|
|
var ElementInstructionMap = /** @class */ (function () {
|
|
function ElementInstructionMap() {
|
|
this._map = new Map();
|
|
}
|
|
ElementInstructionMap.prototype.consume = function (element) {
|
|
var instructions = this._map.get(element);
|
|
if (instructions) {
|
|
this._map.delete(element);
|
|
}
|
|
else {
|
|
instructions = [];
|
|
}
|
|
return instructions;
|
|
};
|
|
ElementInstructionMap.prototype.append = function (element, instructions) {
|
|
var existingInstructions = this._map.get(element);
|
|
if (!existingInstructions) {
|
|
this._map.set(element, existingInstructions = []);
|
|
}
|
|
existingInstructions.push.apply(existingInstructions, __spreadArray([], __read(instructions)));
|
|
};
|
|
ElementInstructionMap.prototype.has = function (element) {
|
|
return this._map.has(element);
|
|
};
|
|
ElementInstructionMap.prototype.clear = function () {
|
|
this._map.clear();
|
|
};
|
|
return ElementInstructionMap;
|
|
}());
|
|
|
|
var ONE_FRAME_IN_MILLISECONDS = 1;
|
|
var ENTER_TOKEN = ':enter';
|
|
var ENTER_TOKEN_REGEX = new RegExp(ENTER_TOKEN, 'g');
|
|
var LEAVE_TOKEN = ':leave';
|
|
var LEAVE_TOKEN_REGEX = new RegExp(LEAVE_TOKEN, 'g');
|
|
/*
|
|
* The code within this file aims to generate web-animations-compatible keyframes from Angular's
|
|
* animation DSL code.
|
|
*
|
|
* The code below will be converted from:
|
|
*
|
|
* ```
|
|
* sequence([
|
|
* style({ opacity: 0 }),
|
|
* animate(1000, style({ opacity: 0 }))
|
|
* ])
|
|
* ```
|
|
*
|
|
* To:
|
|
* ```
|
|
* keyframes = [{ opacity: 0, offset: 0 }, { opacity: 1, offset: 1 }]
|
|
* duration = 1000
|
|
* delay = 0
|
|
* easing = ''
|
|
* ```
|
|
*
|
|
* For this operation to cover the combination of animation verbs (style, animate, group, etc...) a
|
|
* combination of prototypical inheritance, AST traversal and merge-sort-like algorithms are used.
|
|
*
|
|
* [AST Traversal]
|
|
* Each of the animation verbs, when executed, will return an string-map object representing what
|
|
* type of action it is (style, animate, group, etc...) and the data associated with it. This means
|
|
* that when functional composition mix of these functions is evaluated (like in the example above)
|
|
* then it will end up producing a tree of objects representing the animation itself.
|
|
*
|
|
* When this animation object tree is processed by the visitor code below it will visit each of the
|
|
* verb statements within the visitor. And during each visit it will build the context of the
|
|
* animation keyframes by interacting with the `TimelineBuilder`.
|
|
*
|
|
* [TimelineBuilder]
|
|
* This class is responsible for tracking the styles and building a series of keyframe objects for a
|
|
* timeline between a start and end time. The builder starts off with an initial timeline and each
|
|
* time the AST comes across a `group()`, `keyframes()` or a combination of the two wihtin a
|
|
* `sequence()` then it will generate a sub timeline for each step as well as a new one after
|
|
* they are complete.
|
|
*
|
|
* As the AST is traversed, the timing state on each of the timelines will be incremented. If a sub
|
|
* timeline was created (based on one of the cases above) then the parent timeline will attempt to
|
|
* merge the styles used within the sub timelines into itself (only with group() this will happen).
|
|
* This happens with a merge operation (much like how the merge works in mergesort) and it will only
|
|
* copy the most recently used styles from the sub timelines into the parent timeline. This ensures
|
|
* that if the styles are used later on in another phase of the animation then they will be the most
|
|
* up-to-date values.
|
|
*
|
|
* [How Missing Styles Are Updated]
|
|
* Each timeline has a `backFill` property which is responsible for filling in new styles into
|
|
* already processed keyframes if a new style shows up later within the animation sequence.
|
|
*
|
|
* ```
|
|
* sequence([
|
|
* style({ width: 0 }),
|
|
* animate(1000, style({ width: 100 })),
|
|
* animate(1000, style({ width: 200 })),
|
|
* animate(1000, style({ width: 300 }))
|
|
* animate(1000, style({ width: 400, height: 400 })) // notice how `height` doesn't exist anywhere
|
|
* else
|
|
* ])
|
|
* ```
|
|
*
|
|
* What is happening here is that the `height` value is added later in the sequence, but is missing
|
|
* from all previous animation steps. Therefore when a keyframe is created it would also be missing
|
|
* from all previous keyframes up until where it is first used. For the timeline keyframe generation
|
|
* to properly fill in the style it will place the previous value (the value from the parent
|
|
* timeline) or a default value of `*` into the backFill object. Given that each of the keyframe
|
|
* styles are objects that prototypically inhert from the backFill object, this means that if a
|
|
* value is added into the backFill then it will automatically propagate any missing values to all
|
|
* keyframes. Therefore the missing `height` value will be properly filled into the already
|
|
* processed keyframes.
|
|
*
|
|
* When a sub-timeline is created it will have its own backFill property. This is done so that
|
|
* styles present within the sub-timeline do not accidentally seep into the previous/future timeline
|
|
* keyframes
|
|
*
|
|
* (For prototypically-inherited contents to be detected a `for(i in obj)` loop must be used.)
|
|
*
|
|
* [Validation]
|
|
* The code in this file is not responsible for validation. That functionality happens with within
|
|
* the `AnimationValidatorVisitor` code.
|
|
*/
|
|
function buildAnimationTimelines(driver, rootElement, ast, enterClassName, leaveClassName, startingStyles, finalStyles, options, subInstructions, errors) {
|
|
if (startingStyles === void 0) { startingStyles = {}; }
|
|
if (finalStyles === void 0) { finalStyles = {}; }
|
|
if (errors === void 0) { errors = []; }
|
|
return new AnimationTimelineBuilderVisitor().buildKeyframes(driver, rootElement, ast, enterClassName, leaveClassName, startingStyles, finalStyles, options, subInstructions, errors);
|
|
}
|
|
var AnimationTimelineBuilderVisitor = /** @class */ (function () {
|
|
function AnimationTimelineBuilderVisitor() {
|
|
}
|
|
AnimationTimelineBuilderVisitor.prototype.buildKeyframes = function (driver, rootElement, ast, enterClassName, leaveClassName, startingStyles, finalStyles, options, subInstructions, errors) {
|
|
if (errors === void 0) { errors = []; }
|
|
subInstructions = subInstructions || new ElementInstructionMap();
|
|
var context = new AnimationTimelineContext(driver, rootElement, subInstructions, enterClassName, leaveClassName, errors, []);
|
|
context.options = options;
|
|
context.currentTimeline.setStyles([startingStyles], null, context.errors, options);
|
|
visitDslNode(this, ast, context);
|
|
// this checks to see if an actual animation happened
|
|
var timelines = context.timelines.filter(function (timeline) { return timeline.containsAnimation(); });
|
|
if (timelines.length && Object.keys(finalStyles).length) {
|
|
var tl = timelines[timelines.length - 1];
|
|
if (!tl.allowOnlyTimelineStyles()) {
|
|
tl.setStyles([finalStyles], null, context.errors, options);
|
|
}
|
|
}
|
|
return timelines.length ? timelines.map(function (timeline) { return timeline.buildKeyframes(); }) :
|
|
[createTimelineInstruction(rootElement, [], [], [], 0, 0, '', false)];
|
|
};
|
|
AnimationTimelineBuilderVisitor.prototype.visitTrigger = function (ast, context) {
|
|
// these values are not visited in this AST
|
|
};
|
|
AnimationTimelineBuilderVisitor.prototype.visitState = function (ast, context) {
|
|
// these values are not visited in this AST
|
|
};
|
|
AnimationTimelineBuilderVisitor.prototype.visitTransition = function (ast, context) {
|
|
// these values are not visited in this AST
|
|
};
|
|
AnimationTimelineBuilderVisitor.prototype.visitAnimateChild = function (ast, context) {
|
|
var elementInstructions = context.subInstructions.consume(context.element);
|
|
if (elementInstructions) {
|
|
var innerContext = context.createSubContext(ast.options);
|
|
var startTime = context.currentTimeline.currentTime;
|
|
var endTime = this._visitSubInstructions(elementInstructions, innerContext, innerContext.options);
|
|
if (startTime != endTime) {
|
|
// we do this on the upper context because we created a sub context for
|
|
// the sub child animations
|
|
context.transformIntoNewTimeline(endTime);
|
|
}
|
|
}
|
|
context.previousNode = ast;
|
|
};
|
|
AnimationTimelineBuilderVisitor.prototype.visitAnimateRef = function (ast, context) {
|
|
var innerContext = context.createSubContext(ast.options);
|
|
innerContext.transformIntoNewTimeline();
|
|
this.visitReference(ast.animation, innerContext);
|
|
context.transformIntoNewTimeline(innerContext.currentTimeline.currentTime);
|
|
context.previousNode = ast;
|
|
};
|
|
AnimationTimelineBuilderVisitor.prototype._visitSubInstructions = function (instructions, context, options) {
|
|
var startTime = context.currentTimeline.currentTime;
|
|
var furthestTime = startTime;
|
|
// this is a special-case for when a user wants to skip a sub
|
|
// animation from being fired entirely.
|
|
var duration = options.duration != null ? resolveTimingValue(options.duration) : null;
|
|
var delay = options.delay != null ? resolveTimingValue(options.delay) : null;
|
|
if (duration !== 0) {
|
|
instructions.forEach(function (instruction) {
|
|
var instructionTimings = context.appendInstructionToTimeline(instruction, duration, delay);
|
|
furthestTime =
|
|
Math.max(furthestTime, instructionTimings.duration + instructionTimings.delay);
|
|
});
|
|
}
|
|
return furthestTime;
|
|
};
|
|
AnimationTimelineBuilderVisitor.prototype.visitReference = function (ast, context) {
|
|
context.updateOptions(ast.options, true);
|
|
visitDslNode(this, ast.animation, context);
|
|
context.previousNode = ast;
|
|
};
|
|
AnimationTimelineBuilderVisitor.prototype.visitSequence = function (ast, context) {
|
|
var _this = this;
|
|
var subContextCount = context.subContextCount;
|
|
var ctx = context;
|
|
var options = ast.options;
|
|
if (options && (options.params || options.delay)) {
|
|
ctx = context.createSubContext(options);
|
|
ctx.transformIntoNewTimeline();
|
|
if (options.delay != null) {
|
|
if (ctx.previousNode.type == 6 /* Style */) {
|
|
ctx.currentTimeline.snapshotCurrentStyles();
|
|
ctx.previousNode = DEFAULT_NOOP_PREVIOUS_NODE;
|
|
}
|
|
var delay = resolveTimingValue(options.delay);
|
|
ctx.delayNextStep(delay);
|
|
}
|
|
}
|
|
if (ast.steps.length) {
|
|
ast.steps.forEach(function (s) { return visitDslNode(_this, s, ctx); });
|
|
// this is here just incase the inner steps only contain or end with a style() call
|
|
ctx.currentTimeline.applyStylesToKeyframe();
|
|
// this means that some animation function within the sequence
|
|
// ended up creating a sub timeline (which means the current
|
|
// timeline cannot overlap with the contents of the sequence)
|
|
if (ctx.subContextCount > subContextCount) {
|
|
ctx.transformIntoNewTimeline();
|
|
}
|
|
}
|
|
context.previousNode = ast;
|
|
};
|
|
AnimationTimelineBuilderVisitor.prototype.visitGroup = function (ast, context) {
|
|
var _this = this;
|
|
var innerTimelines = [];
|
|
var furthestTime = context.currentTimeline.currentTime;
|
|
var delay = ast.options && ast.options.delay ? resolveTimingValue(ast.options.delay) : 0;
|
|
ast.steps.forEach(function (s) {
|
|
var innerContext = context.createSubContext(ast.options);
|
|
if (delay) {
|
|
innerContext.delayNextStep(delay);
|
|
}
|
|
visitDslNode(_this, s, innerContext);
|
|
furthestTime = Math.max(furthestTime, innerContext.currentTimeline.currentTime);
|
|
innerTimelines.push(innerContext.currentTimeline);
|
|
});
|
|
// this operation is run after the AST loop because otherwise
|
|
// if the parent timeline's collected styles were updated then
|
|
// it would pass in invalid data into the new-to-be forked items
|
|
innerTimelines.forEach(function (timeline) { return context.currentTimeline.mergeTimelineCollectedStyles(timeline); });
|
|
context.transformIntoNewTimeline(furthestTime);
|
|
context.previousNode = ast;
|
|
};
|
|
AnimationTimelineBuilderVisitor.prototype._visitTiming = function (ast, context) {
|
|
if (ast.dynamic) {
|
|
var strValue = ast.strValue;
|
|
var timingValue = context.params ? interpolateParams(strValue, context.params, context.errors) : strValue;
|
|
return resolveTiming(timingValue, context.errors);
|
|
}
|
|
else {
|
|
return { duration: ast.duration, delay: ast.delay, easing: ast.easing };
|
|
}
|
|
};
|
|
AnimationTimelineBuilderVisitor.prototype.visitAnimate = function (ast, context) {
|
|
var timings = context.currentAnimateTimings = this._visitTiming(ast.timings, context);
|
|
var timeline = context.currentTimeline;
|
|
if (timings.delay) {
|
|
context.incrementTime(timings.delay);
|
|
timeline.snapshotCurrentStyles();
|
|
}
|
|
var style = ast.style;
|
|
if (style.type == 5 /* Keyframes */) {
|
|
this.visitKeyframes(style, context);
|
|
}
|
|
else {
|
|
context.incrementTime(timings.duration);
|
|
this.visitStyle(style, context);
|
|
timeline.applyStylesToKeyframe();
|
|
}
|
|
context.currentAnimateTimings = null;
|
|
context.previousNode = ast;
|
|
};
|
|
AnimationTimelineBuilderVisitor.prototype.visitStyle = function (ast, context) {
|
|
var timeline = context.currentTimeline;
|
|
var timings = context.currentAnimateTimings;
|
|
// this is a special case for when a style() call
|
|
// directly follows an animate() call (but not inside of an animate() call)
|
|
if (!timings && timeline.getCurrentStyleProperties().length) {
|
|
timeline.forwardFrame();
|
|
}
|
|
var easing = (timings && timings.easing) || ast.easing;
|
|
if (ast.isEmptyStep) {
|
|
timeline.applyEmptyStep(easing);
|
|
}
|
|
else {
|
|
timeline.setStyles(ast.styles, easing, context.errors, context.options);
|
|
}
|
|
context.previousNode = ast;
|
|
};
|
|
AnimationTimelineBuilderVisitor.prototype.visitKeyframes = function (ast, context) {
|
|
var currentAnimateTimings = context.currentAnimateTimings;
|
|
var startTime = (context.currentTimeline).duration;
|
|
var duration = currentAnimateTimings.duration;
|
|
var innerContext = context.createSubContext();
|
|
var innerTimeline = innerContext.currentTimeline;
|
|
innerTimeline.easing = currentAnimateTimings.easing;
|
|
ast.styles.forEach(function (step) {
|
|
var offset = step.offset || 0;
|
|
innerTimeline.forwardTime(offset * duration);
|
|
innerTimeline.setStyles(step.styles, step.easing, context.errors, context.options);
|
|
innerTimeline.applyStylesToKeyframe();
|
|
});
|
|
// this will ensure that the parent timeline gets all the styles from
|
|
// the child even if the new timeline below is not used
|
|
context.currentTimeline.mergeTimelineCollectedStyles(innerTimeline);
|
|
// we do this because the window between this timeline and the sub timeline
|
|
// should ensure that the styles within are exactly the same as they were before
|
|
context.transformIntoNewTimeline(startTime + duration);
|
|
context.previousNode = ast;
|
|
};
|
|
AnimationTimelineBuilderVisitor.prototype.visitQuery = function (ast, context) {
|
|
var _this = this;
|
|
// in the event that the first step before this is a style step we need
|
|
// to ensure the styles are applied before the children are animated
|
|
var startTime = context.currentTimeline.currentTime;
|
|
var options = (ast.options || {});
|
|
var delay = options.delay ? resolveTimingValue(options.delay) : 0;
|
|
if (delay &&
|
|
(context.previousNode.type === 6 /* Style */ ||
|
|
(startTime == 0 && context.currentTimeline.getCurrentStyleProperties().length))) {
|
|
context.currentTimeline.snapshotCurrentStyles();
|
|
context.previousNode = DEFAULT_NOOP_PREVIOUS_NODE;
|
|
}
|
|
var furthestTime = startTime;
|
|
var elms = context.invokeQuery(ast.selector, ast.originalSelector, ast.limit, ast.includeSelf, options.optional ? true : false, context.errors);
|
|
context.currentQueryTotal = elms.length;
|
|
var sameElementTimeline = null;
|
|
elms.forEach(function (element, i) {
|
|
context.currentQueryIndex = i;
|
|
var innerContext = context.createSubContext(ast.options, element);
|
|
if (delay) {
|
|
innerContext.delayNextStep(delay);
|
|
}
|
|
if (element === context.element) {
|
|
sameElementTimeline = innerContext.currentTimeline;
|
|
}
|
|
visitDslNode(_this, ast.animation, innerContext);
|
|
// this is here just incase the inner steps only contain or end
|
|
// with a style() call (which is here to signal that this is a preparatory
|
|
// call to style an element before it is animated again)
|
|
innerContext.currentTimeline.applyStylesToKeyframe();
|
|
var endTime = innerContext.currentTimeline.currentTime;
|
|
furthestTime = Math.max(furthestTime, endTime);
|
|
});
|
|
context.currentQueryIndex = 0;
|
|
context.currentQueryTotal = 0;
|
|
context.transformIntoNewTimeline(furthestTime);
|
|
if (sameElementTimeline) {
|
|
context.currentTimeline.mergeTimelineCollectedStyles(sameElementTimeline);
|
|
context.currentTimeline.snapshotCurrentStyles();
|
|
}
|
|
context.previousNode = ast;
|
|
};
|
|
AnimationTimelineBuilderVisitor.prototype.visitStagger = function (ast, context) {
|
|
var parentContext = context.parentContext;
|
|
var tl = context.currentTimeline;
|
|
var timings = ast.timings;
|
|
var duration = Math.abs(timings.duration);
|
|
var maxTime = duration * (context.currentQueryTotal - 1);
|
|
var delay = duration * context.currentQueryIndex;
|
|
var staggerTransformer = timings.duration < 0 ? 'reverse' : timings.easing;
|
|
switch (staggerTransformer) {
|
|
case 'reverse':
|
|
delay = maxTime - delay;
|
|
break;
|
|
case 'full':
|
|
delay = parentContext.currentStaggerTime;
|
|
break;
|
|
}
|
|
var timeline = context.currentTimeline;
|
|
if (delay) {
|
|
timeline.delayNextStep(delay);
|
|
}
|
|
var startingTime = timeline.currentTime;
|
|
visitDslNode(this, ast.animation, context);
|
|
context.previousNode = ast;
|
|
// time = duration + delay
|
|
// the reason why this computation is so complex is because
|
|
// the inner timeline may either have a delay value or a stretched
|
|
// keyframe depending on if a subtimeline is not used or is used.
|
|
parentContext.currentStaggerTime =
|
|
(tl.currentTime - startingTime) + (tl.startTime - parentContext.currentTimeline.startTime);
|
|
};
|
|
return AnimationTimelineBuilderVisitor;
|
|
}());
|
|
var DEFAULT_NOOP_PREVIOUS_NODE = {};
|
|
var AnimationTimelineContext = /** @class */ (function () {
|
|
function AnimationTimelineContext(_driver, element, subInstructions, _enterClassName, _leaveClassName, errors, timelines, initialTimeline) {
|
|
this._driver = _driver;
|
|
this.element = element;
|
|
this.subInstructions = subInstructions;
|
|
this._enterClassName = _enterClassName;
|
|
this._leaveClassName = _leaveClassName;
|
|
this.errors = errors;
|
|
this.timelines = timelines;
|
|
this.parentContext = null;
|
|
this.currentAnimateTimings = null;
|
|
this.previousNode = DEFAULT_NOOP_PREVIOUS_NODE;
|
|
this.subContextCount = 0;
|
|
this.options = {};
|
|
this.currentQueryIndex = 0;
|
|
this.currentQueryTotal = 0;
|
|
this.currentStaggerTime = 0;
|
|
this.currentTimeline = initialTimeline || new TimelineBuilder(this._driver, element, 0);
|
|
timelines.push(this.currentTimeline);
|
|
}
|
|
Object.defineProperty(AnimationTimelineContext.prototype, "params", {
|
|
get: function () {
|
|
return this.options.params;
|
|
},
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
AnimationTimelineContext.prototype.updateOptions = function (options, skipIfExists) {
|
|
var _this = this;
|
|
if (!options)
|
|
return;
|
|
var newOptions = options;
|
|
var optionsToUpdate = this.options;
|
|
// NOTE: this will get patched up when other animation methods support duration overrides
|
|
if (newOptions.duration != null) {
|
|
optionsToUpdate.duration = resolveTimingValue(newOptions.duration);
|
|
}
|
|
if (newOptions.delay != null) {
|
|
optionsToUpdate.delay = resolveTimingValue(newOptions.delay);
|
|
}
|
|
var newParams = newOptions.params;
|
|
if (newParams) {
|
|
var paramsToUpdate_1 = optionsToUpdate.params;
|
|
if (!paramsToUpdate_1) {
|
|
paramsToUpdate_1 = this.options.params = {};
|
|
}
|
|
Object.keys(newParams).forEach(function (name) {
|
|
if (!skipIfExists || !paramsToUpdate_1.hasOwnProperty(name)) {
|
|
paramsToUpdate_1[name] = interpolateParams(newParams[name], paramsToUpdate_1, _this.errors);
|
|
}
|
|
});
|
|
}
|
|
};
|
|
AnimationTimelineContext.prototype._copyOptions = function () {
|
|
var options = {};
|
|
if (this.options) {
|
|
var oldParams_1 = this.options.params;
|
|
if (oldParams_1) {
|
|
var params_1 = options['params'] = {};
|
|
Object.keys(oldParams_1).forEach(function (name) {
|
|
params_1[name] = oldParams_1[name];
|
|
});
|
|
}
|
|
}
|
|
return options;
|
|
};
|
|
AnimationTimelineContext.prototype.createSubContext = function (options, element, newTime) {
|
|
if (options === void 0) { options = null; }
|
|
var target = element || this.element;
|
|
var context = new AnimationTimelineContext(this._driver, target, this.subInstructions, this._enterClassName, this._leaveClassName, this.errors, this.timelines, this.currentTimeline.fork(target, newTime || 0));
|
|
context.previousNode = this.previousNode;
|
|
context.currentAnimateTimings = this.currentAnimateTimings;
|
|
context.options = this._copyOptions();
|
|
context.updateOptions(options);
|
|
context.currentQueryIndex = this.currentQueryIndex;
|
|
context.currentQueryTotal = this.currentQueryTotal;
|
|
context.parentContext = this;
|
|
this.subContextCount++;
|
|
return context;
|
|
};
|
|
AnimationTimelineContext.prototype.transformIntoNewTimeline = function (newTime) {
|
|
this.previousNode = DEFAULT_NOOP_PREVIOUS_NODE;
|
|
this.currentTimeline = this.currentTimeline.fork(this.element, newTime);
|
|
this.timelines.push(this.currentTimeline);
|
|
return this.currentTimeline;
|
|
};
|
|
AnimationTimelineContext.prototype.appendInstructionToTimeline = function (instruction, duration, delay) {
|
|
var updatedTimings = {
|
|
duration: duration != null ? duration : instruction.duration,
|
|
delay: this.currentTimeline.currentTime + (delay != null ? delay : 0) + instruction.delay,
|
|
easing: ''
|
|
};
|
|
var builder = new SubTimelineBuilder(this._driver, instruction.element, instruction.keyframes, instruction.preStyleProps, instruction.postStyleProps, updatedTimings, instruction.stretchStartingKeyframe);
|
|
this.timelines.push(builder);
|
|
return updatedTimings;
|
|
};
|
|
AnimationTimelineContext.prototype.incrementTime = function (time) {
|
|
this.currentTimeline.forwardTime(this.currentTimeline.duration + time);
|
|
};
|
|
AnimationTimelineContext.prototype.delayNextStep = function (delay) {
|
|
// negative delays are not yet supported
|
|
if (delay > 0) {
|
|
this.currentTimeline.delayNextStep(delay);
|
|
}
|
|
};
|
|
AnimationTimelineContext.prototype.invokeQuery = function (selector, originalSelector, limit, includeSelf, optional, errors) {
|
|
var results = [];
|
|
if (includeSelf) {
|
|
results.push(this.element);
|
|
}
|
|
if (selector.length > 0) { // if :self is only used then the selector is empty
|
|
selector = selector.replace(ENTER_TOKEN_REGEX, '.' + this._enterClassName);
|
|
selector = selector.replace(LEAVE_TOKEN_REGEX, '.' + this._leaveClassName);
|
|
var multi = limit != 1;
|
|
var elements = this._driver.query(this.element, selector, multi);
|
|
if (limit !== 0) {
|
|
elements = limit < 0 ? elements.slice(elements.length + limit, elements.length) :
|
|
elements.slice(0, limit);
|
|
}
|
|
results.push.apply(results, __spreadArray([], __read(elements)));
|
|
}
|
|
if (!optional && results.length == 0) {
|
|
errors.push("`query(\"" + originalSelector + "\")` returned zero elements. (Use `query(\"" + originalSelector + "\", { optional: true })` if you wish to allow this.)");
|
|
}
|
|
return results;
|
|
};
|
|
return AnimationTimelineContext;
|
|
}());
|
|
var TimelineBuilder = /** @class */ (function () {
|
|
function TimelineBuilder(_driver, element, startTime, _elementTimelineStylesLookup) {
|
|
this._driver = _driver;
|
|
this.element = element;
|
|
this.startTime = startTime;
|
|
this._elementTimelineStylesLookup = _elementTimelineStylesLookup;
|
|
this.duration = 0;
|
|
this._previousKeyframe = {};
|
|
this._currentKeyframe = {};
|
|
this._keyframes = new Map();
|
|
this._styleSummary = {};
|
|
this._pendingStyles = {};
|
|
this._backFill = {};
|
|
this._currentEmptyStepKeyframe = null;
|
|
if (!this._elementTimelineStylesLookup) {
|
|
this._elementTimelineStylesLookup = new Map();
|
|
}
|
|
this._localTimelineStyles = Object.create(this._backFill, {});
|
|
this._globalTimelineStyles = this._elementTimelineStylesLookup.get(element);
|
|
if (!this._globalTimelineStyles) {
|
|
this._globalTimelineStyles = this._localTimelineStyles;
|
|
this._elementTimelineStylesLookup.set(element, this._localTimelineStyles);
|
|
}
|
|
this._loadKeyframe();
|
|
}
|
|
TimelineBuilder.prototype.containsAnimation = function () {
|
|
switch (this._keyframes.size) {
|
|
case 0:
|
|
return false;
|
|
case 1:
|
|
return this.getCurrentStyleProperties().length > 0;
|
|
default:
|
|
return true;
|
|
}
|
|
};
|
|
TimelineBuilder.prototype.getCurrentStyleProperties = function () {
|
|
return Object.keys(this._currentKeyframe);
|
|
};
|
|
Object.defineProperty(TimelineBuilder.prototype, "currentTime", {
|
|
get: function () {
|
|
return this.startTime + this.duration;
|
|
},
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
TimelineBuilder.prototype.delayNextStep = function (delay) {
|
|
// in the event that a style() step is placed right before a stagger()
|
|
// and that style() step is the very first style() value in the animation
|
|
// then we need to make a copy of the keyframe [0, copy, 1] so that the delay
|
|
// properly applies the style() values to work with the stagger...
|
|
var hasPreStyleStep = this._keyframes.size == 1 && Object.keys(this._pendingStyles).length;
|
|
if (this.duration || hasPreStyleStep) {
|
|
this.forwardTime(this.currentTime + delay);
|
|
if (hasPreStyleStep) {
|
|
this.snapshotCurrentStyles();
|
|
}
|
|
}
|
|
else {
|
|
this.startTime += delay;
|
|
}
|
|
};
|
|
TimelineBuilder.prototype.fork = function (element, currentTime) {
|
|
this.applyStylesToKeyframe();
|
|
return new TimelineBuilder(this._driver, element, currentTime || this.currentTime, this._elementTimelineStylesLookup);
|
|
};
|
|
TimelineBuilder.prototype._loadKeyframe = function () {
|
|
if (this._currentKeyframe) {
|
|
this._previousKeyframe = this._currentKeyframe;
|
|
}
|
|
this._currentKeyframe = this._keyframes.get(this.duration);
|
|
if (!this._currentKeyframe) {
|
|
this._currentKeyframe = Object.create(this._backFill, {});
|
|
this._keyframes.set(this.duration, this._currentKeyframe);
|
|
}
|
|
};
|
|
TimelineBuilder.prototype.forwardFrame = function () {
|
|
this.duration += ONE_FRAME_IN_MILLISECONDS;
|
|
this._loadKeyframe();
|
|
};
|
|
TimelineBuilder.prototype.forwardTime = function (time) {
|
|
this.applyStylesToKeyframe();
|
|
this.duration = time;
|
|
this._loadKeyframe();
|
|
};
|
|
TimelineBuilder.prototype._updateStyle = function (prop, value) {
|
|
this._localTimelineStyles[prop] = value;
|
|
this._globalTimelineStyles[prop] = value;
|
|
this._styleSummary[prop] = { time: this.currentTime, value: value };
|
|
};
|
|
TimelineBuilder.prototype.allowOnlyTimelineStyles = function () {
|
|
return this._currentEmptyStepKeyframe !== this._currentKeyframe;
|
|
};
|
|
TimelineBuilder.prototype.applyEmptyStep = function (easing) {
|
|
var _this = this;
|
|
if (easing) {
|
|
this._previousKeyframe['easing'] = easing;
|
|
}
|
|
// special case for animate(duration):
|
|
// all missing styles are filled with a `*` value then
|
|
// if any destination styles are filled in later on the same
|
|
// keyframe then they will override the overridden styles
|
|
// We use `_globalTimelineStyles` here because there may be
|
|
// styles in previous keyframes that are not present in this timeline
|
|
Object.keys(this._globalTimelineStyles).forEach(function (prop) {
|
|
_this._backFill[prop] = _this._globalTimelineStyles[prop] || animations.AUTO_STYLE;
|
|
_this._currentKeyframe[prop] = animations.AUTO_STYLE;
|
|
});
|
|
this._currentEmptyStepKeyframe = this._currentKeyframe;
|
|
};
|
|
TimelineBuilder.prototype.setStyles = function (input, easing, errors, options) {
|
|
var _this = this;
|
|
if (easing) {
|
|
this._previousKeyframe['easing'] = easing;
|
|
}
|
|
var params = (options && options.params) || {};
|
|
var styles = flattenStyles(input, this._globalTimelineStyles);
|
|
Object.keys(styles).forEach(function (prop) {
|
|
var val = interpolateParams(styles[prop], params, errors);
|
|
_this._pendingStyles[prop] = val;
|
|
if (!_this._localTimelineStyles.hasOwnProperty(prop)) {
|
|
_this._backFill[prop] = _this._globalTimelineStyles.hasOwnProperty(prop) ?
|
|
_this._globalTimelineStyles[prop] :
|
|
animations.AUTO_STYLE;
|
|
}
|
|
_this._updateStyle(prop, val);
|
|
});
|
|
};
|
|
TimelineBuilder.prototype.applyStylesToKeyframe = function () {
|
|
var _this = this;
|
|
var styles = this._pendingStyles;
|
|
var props = Object.keys(styles);
|
|
if (props.length == 0)
|
|
return;
|
|
this._pendingStyles = {};
|
|
props.forEach(function (prop) {
|
|
var val = styles[prop];
|
|
_this._currentKeyframe[prop] = val;
|
|
});
|
|
Object.keys(this._localTimelineStyles).forEach(function (prop) {
|
|
if (!_this._currentKeyframe.hasOwnProperty(prop)) {
|
|
_this._currentKeyframe[prop] = _this._localTimelineStyles[prop];
|
|
}
|
|
});
|
|
};
|
|
TimelineBuilder.prototype.snapshotCurrentStyles = function () {
|
|
var _this = this;
|
|
Object.keys(this._localTimelineStyles).forEach(function (prop) {
|
|
var val = _this._localTimelineStyles[prop];
|
|
_this._pendingStyles[prop] = val;
|
|
_this._updateStyle(prop, val);
|
|
});
|
|
};
|
|
TimelineBuilder.prototype.getFinalKeyframe = function () {
|
|
return this._keyframes.get(this.duration);
|
|
};
|
|
Object.defineProperty(TimelineBuilder.prototype, "properties", {
|
|
get: function () {
|
|
var properties = [];
|
|
for (var prop in this._currentKeyframe) {
|
|
properties.push(prop);
|
|
}
|
|
return properties;
|
|
},
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
TimelineBuilder.prototype.mergeTimelineCollectedStyles = function (timeline) {
|
|
var _this = this;
|
|
Object.keys(timeline._styleSummary).forEach(function (prop) {
|
|
var details0 = _this._styleSummary[prop];
|
|
var details1 = timeline._styleSummary[prop];
|
|
if (!details0 || details1.time > details0.time) {
|
|
_this._updateStyle(prop, details1.value);
|
|
}
|
|
});
|
|
};
|
|
TimelineBuilder.prototype.buildKeyframes = function () {
|
|
var _this = this;
|
|
this.applyStylesToKeyframe();
|
|
var preStyleProps = new Set();
|
|
var postStyleProps = new Set();
|
|
var isEmpty = this._keyframes.size === 1 && this.duration === 0;
|
|
var finalKeyframes = [];
|
|
this._keyframes.forEach(function (keyframe, time) {
|
|
var finalKeyframe = copyStyles(keyframe, true);
|
|
Object.keys(finalKeyframe).forEach(function (prop) {
|
|
var value = finalKeyframe[prop];
|
|
if (value == animations.ɵPRE_STYLE) {
|
|
preStyleProps.add(prop);
|
|
}
|
|
else if (value == animations.AUTO_STYLE) {
|
|
postStyleProps.add(prop);
|
|
}
|
|
});
|
|
if (!isEmpty) {
|
|
finalKeyframe['offset'] = time / _this.duration;
|
|
}
|
|
finalKeyframes.push(finalKeyframe);
|
|
});
|
|
var preProps = preStyleProps.size ? iteratorToArray(preStyleProps.values()) : [];
|
|
var postProps = postStyleProps.size ? iteratorToArray(postStyleProps.values()) : [];
|
|
// special case for a 0-second animation (which is designed just to place styles onscreen)
|
|
if (isEmpty) {
|
|
var kf0 = finalKeyframes[0];
|
|
var kf1 = copyObj(kf0);
|
|
kf0['offset'] = 0;
|
|
kf1['offset'] = 1;
|
|
finalKeyframes = [kf0, kf1];
|
|
}
|
|
return createTimelineInstruction(this.element, finalKeyframes, preProps, postProps, this.duration, this.startTime, this.easing, false);
|
|
};
|
|
return TimelineBuilder;
|
|
}());
|
|
var SubTimelineBuilder = /** @class */ (function (_super) {
|
|
__extends(SubTimelineBuilder, _super);
|
|
function SubTimelineBuilder(driver, element, keyframes, preStyleProps, postStyleProps, timings, _stretchStartingKeyframe) {
|
|
if (_stretchStartingKeyframe === void 0) { _stretchStartingKeyframe = false; }
|
|
var _this = _super.call(this, driver, element, timings.delay) || this;
|
|
_this.keyframes = keyframes;
|
|
_this.preStyleProps = preStyleProps;
|
|
_this.postStyleProps = postStyleProps;
|
|
_this._stretchStartingKeyframe = _stretchStartingKeyframe;
|
|
_this.timings = { duration: timings.duration, delay: timings.delay, easing: timings.easing };
|
|
return _this;
|
|
}
|
|
SubTimelineBuilder.prototype.containsAnimation = function () {
|
|
return this.keyframes.length > 1;
|
|
};
|
|
SubTimelineBuilder.prototype.buildKeyframes = function () {
|
|
var keyframes = this.keyframes;
|
|
var _a = this.timings, delay = _a.delay, duration = _a.duration, easing = _a.easing;
|
|
if (this._stretchStartingKeyframe && delay) {
|
|
var newKeyframes = [];
|
|
var totalTime = duration + delay;
|
|
var startingGap = delay / totalTime;
|
|
// the original starting keyframe now starts once the delay is done
|
|
var newFirstKeyframe = copyStyles(keyframes[0], false);
|
|
newFirstKeyframe['offset'] = 0;
|
|
newKeyframes.push(newFirstKeyframe);
|
|
var oldFirstKeyframe = copyStyles(keyframes[0], false);
|
|
oldFirstKeyframe['offset'] = roundOffset(startingGap);
|
|
newKeyframes.push(oldFirstKeyframe);
|
|
/*
|
|
When the keyframe is stretched then it means that the delay before the animation
|
|
starts is gone. Instead the first keyframe is placed at the start of the animation
|
|
and it is then copied to where it starts when the original delay is over. This basically
|
|
means nothing animates during that delay, but the styles are still renderered. For this
|
|
to work the original offset values that exist in the original keyframes must be "warped"
|
|
so that they can take the new keyframe + delay into account.
|
|
|
|
delay=1000, duration=1000, keyframes = 0 .5 1
|
|
|
|
turns into
|
|
|
|
delay=0, duration=2000, keyframes = 0 .33 .66 1
|
|
*/
|
|
// offsets between 1 ... n -1 are all warped by the keyframe stretch
|
|
var limit = keyframes.length - 1;
|
|
for (var i = 1; i <= limit; i++) {
|
|
var kf = copyStyles(keyframes[i], false);
|
|
var oldOffset = kf['offset'];
|
|
var timeAtKeyframe = delay + oldOffset * duration;
|
|
kf['offset'] = roundOffset(timeAtKeyframe / totalTime);
|
|
newKeyframes.push(kf);
|
|
}
|
|
// the new starting keyframe should be added at the start
|
|
duration = totalTime;
|
|
delay = 0;
|
|
easing = '';
|
|
keyframes = newKeyframes;
|
|
}
|
|
return createTimelineInstruction(this.element, keyframes, this.preStyleProps, this.postStyleProps, duration, delay, easing, true);
|
|
};
|
|
return SubTimelineBuilder;
|
|
}(TimelineBuilder));
|
|
function roundOffset(offset, decimalPoints) {
|
|
if (decimalPoints === void 0) { decimalPoints = 3; }
|
|
var mult = Math.pow(10, decimalPoints - 1);
|
|
return Math.round(offset * mult) / mult;
|
|
}
|
|
function flattenStyles(input, allStyles) {
|
|
var styles = {};
|
|
var allProperties;
|
|
input.forEach(function (token) {
|
|
if (token === '*') {
|
|
allProperties = allProperties || Object.keys(allStyles);
|
|
allProperties.forEach(function (prop) {
|
|
styles[prop] = animations.AUTO_STYLE;
|
|
});
|
|
}
|
|
else {
|
|
copyStyles(token, false, styles);
|
|
}
|
|
});
|
|
return styles;
|
|
}
|
|
|
|
var Animation = /** @class */ (function () {
|
|
function Animation(_driver, input) {
|
|
this._driver = _driver;
|
|
var errors = [];
|
|
var ast = buildAnimationAst(_driver, input, errors);
|
|
if (errors.length) {
|
|
var errorMessage = "animation validation failed:\n" + errors.join('\n');
|
|
throw new Error(errorMessage);
|
|
}
|
|
this._animationAst = ast;
|
|
}
|
|
Animation.prototype.buildTimelines = function (element, startingStyles, destinationStyles, options, subInstructions) {
|
|
var start = Array.isArray(startingStyles) ? normalizeStyles(startingStyles) :
|
|
startingStyles;
|
|
var dest = Array.isArray(destinationStyles) ? normalizeStyles(destinationStyles) :
|
|
destinationStyles;
|
|
var errors = [];
|
|
subInstructions = subInstructions || new ElementInstructionMap();
|
|
var result = buildAnimationTimelines(this._driver, element, this._animationAst, ENTER_CLASSNAME, LEAVE_CLASSNAME, start, dest, options, subInstructions, errors);
|
|
if (errors.length) {
|
|
var errorMessage = "animation building failed:\n" + errors.join('\n');
|
|
throw new Error(errorMessage);
|
|
}
|
|
return result;
|
|
};
|
|
return Animation;
|
|
}());
|
|
|
|
/**
|
|
* @license
|
|
* Copyright Google LLC All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://angular.io/license
|
|
*/
|
|
/**
|
|
* @publicApi
|
|
*/
|
|
var AnimationStyleNormalizer = /** @class */ (function () {
|
|
function AnimationStyleNormalizer() {
|
|
}
|
|
return AnimationStyleNormalizer;
|
|
}());
|
|
/**
|
|
* @publicApi
|
|
*/
|
|
var NoopAnimationStyleNormalizer = /** @class */ (function () {
|
|
function NoopAnimationStyleNormalizer() {
|
|
}
|
|
NoopAnimationStyleNormalizer.prototype.normalizePropertyName = function (propertyName, errors) {
|
|
return propertyName;
|
|
};
|
|
NoopAnimationStyleNormalizer.prototype.normalizeStyleValue = function (userProvidedProperty, normalizedProperty, value, errors) {
|
|
return value;
|
|
};
|
|
return NoopAnimationStyleNormalizer;
|
|
}());
|
|
|
|
var WebAnimationsStyleNormalizer = /** @class */ (function (_super) {
|
|
__extends(WebAnimationsStyleNormalizer, _super);
|
|
function WebAnimationsStyleNormalizer() {
|
|
return _super !== null && _super.apply(this, arguments) || this;
|
|
}
|
|
WebAnimationsStyleNormalizer.prototype.normalizePropertyName = function (propertyName, errors) {
|
|
return dashCaseToCamelCase(propertyName);
|
|
};
|
|
WebAnimationsStyleNormalizer.prototype.normalizeStyleValue = function (userProvidedProperty, normalizedProperty, value, errors) {
|
|
var unit = '';
|
|
var strVal = value.toString().trim();
|
|
if (DIMENSIONAL_PROP_MAP[normalizedProperty] && value !== 0 && value !== '0') {
|
|
if (typeof value === 'number') {
|
|
unit = 'px';
|
|
}
|
|
else {
|
|
var valAndSuffixMatch = value.match(/^[+-]?[\d\.]+([a-z]*)$/);
|
|
if (valAndSuffixMatch && valAndSuffixMatch[1].length == 0) {
|
|
errors.push("Please provide a CSS unit value for " + userProvidedProperty + ":" + value);
|
|
}
|
|
}
|
|
}
|
|
return strVal + unit;
|
|
};
|
|
return WebAnimationsStyleNormalizer;
|
|
}(AnimationStyleNormalizer));
|
|
var ɵ0$1 = function () { return makeBooleanMap('width,height,minWidth,minHeight,maxWidth,maxHeight,left,top,bottom,right,fontSize,outlineWidth,outlineOffset,paddingTop,paddingLeft,paddingBottom,paddingRight,marginTop,marginLeft,marginBottom,marginRight,borderRadius,borderWidth,borderTopWidth,borderLeftWidth,borderRightWidth,borderBottomWidth,textIndent,perspective'
|
|
.split(',')); };
|
|
var DIMENSIONAL_PROP_MAP = (ɵ0$1)();
|
|
function makeBooleanMap(keys) {
|
|
var map = {};
|
|
keys.forEach(function (key) { return map[key] = true; });
|
|
return map;
|
|
}
|
|
|
|
function createTransitionInstruction(element, triggerName, fromState, toState, isRemovalTransition, fromStyles, toStyles, timelines, queriedElements, preStyleProps, postStyleProps, totalTime, errors) {
|
|
return {
|
|
type: 0 /* TransitionAnimation */,
|
|
element: element,
|
|
triggerName: triggerName,
|
|
isRemovalTransition: isRemovalTransition,
|
|
fromState: fromState,
|
|
fromStyles: fromStyles,
|
|
toState: toState,
|
|
toStyles: toStyles,
|
|
timelines: timelines,
|
|
queriedElements: queriedElements,
|
|
preStyleProps: preStyleProps,
|
|
postStyleProps: postStyleProps,
|
|
totalTime: totalTime,
|
|
errors: errors
|
|
};
|
|
}
|
|
|
|
var EMPTY_OBJECT = {};
|
|
var AnimationTransitionFactory = /** @class */ (function () {
|
|
function AnimationTransitionFactory(_triggerName, ast, _stateStyles) {
|
|
this._triggerName = _triggerName;
|
|
this.ast = ast;
|
|
this._stateStyles = _stateStyles;
|
|
}
|
|
AnimationTransitionFactory.prototype.match = function (currentState, nextState, element, params) {
|
|
return oneOrMoreTransitionsMatch(this.ast.matchers, currentState, nextState, element, params);
|
|
};
|
|
AnimationTransitionFactory.prototype.buildStyles = function (stateName, params, errors) {
|
|
var backupStateStyler = this._stateStyles['*'];
|
|
var stateStyler = this._stateStyles[stateName];
|
|
var backupStyles = backupStateStyler ? backupStateStyler.buildStyles(params, errors) : {};
|
|
return stateStyler ? stateStyler.buildStyles(params, errors) : backupStyles;
|
|
};
|
|
AnimationTransitionFactory.prototype.build = function (driver, element, currentState, nextState, enterClassName, leaveClassName, currentOptions, nextOptions, subInstructions, skipAstBuild) {
|
|
var errors = [];
|
|
var transitionAnimationParams = this.ast.options && this.ast.options.params || EMPTY_OBJECT;
|
|
var currentAnimationParams = currentOptions && currentOptions.params || EMPTY_OBJECT;
|
|
var currentStateStyles = this.buildStyles(currentState, currentAnimationParams, errors);
|
|
var nextAnimationParams = nextOptions && nextOptions.params || EMPTY_OBJECT;
|
|
var nextStateStyles = this.buildStyles(nextState, nextAnimationParams, errors);
|
|
var queriedElements = new Set();
|
|
var preStyleMap = new Map();
|
|
var postStyleMap = new Map();
|
|
var isRemoval = nextState === 'void';
|
|
var animationOptions = { params: Object.assign(Object.assign({}, transitionAnimationParams), nextAnimationParams) };
|
|
var timelines = skipAstBuild ?
|
|
[] :
|
|
buildAnimationTimelines(driver, element, this.ast.animation, enterClassName, leaveClassName, currentStateStyles, nextStateStyles, animationOptions, subInstructions, errors);
|
|
var totalTime = 0;
|
|
timelines.forEach(function (tl) {
|
|
totalTime = Math.max(tl.duration + tl.delay, totalTime);
|
|
});
|
|
if (errors.length) {
|
|
return createTransitionInstruction(element, this._triggerName, currentState, nextState, isRemoval, currentStateStyles, nextStateStyles, [], [], preStyleMap, postStyleMap, totalTime, errors);
|
|
}
|
|
timelines.forEach(function (tl) {
|
|
var elm = tl.element;
|
|
var preProps = getOrSetAsInMap(preStyleMap, elm, {});
|
|
tl.preStyleProps.forEach(function (prop) { return preProps[prop] = true; });
|
|
var postProps = getOrSetAsInMap(postStyleMap, elm, {});
|
|
tl.postStyleProps.forEach(function (prop) { return postProps[prop] = true; });
|
|
if (elm !== element) {
|
|
queriedElements.add(elm);
|
|
}
|
|
});
|
|
var queriedElementsList = iteratorToArray(queriedElements.values());
|
|
return createTransitionInstruction(element, this._triggerName, currentState, nextState, isRemoval, currentStateStyles, nextStateStyles, timelines, queriedElementsList, preStyleMap, postStyleMap, totalTime);
|
|
};
|
|
return AnimationTransitionFactory;
|
|
}());
|
|
function oneOrMoreTransitionsMatch(matchFns, currentState, nextState, element, params) {
|
|
return matchFns.some(function (fn) { return fn(currentState, nextState, element, params); });
|
|
}
|
|
var AnimationStateStyles = /** @class */ (function () {
|
|
function AnimationStateStyles(styles, defaultParams, normalizer) {
|
|
this.styles = styles;
|
|
this.defaultParams = defaultParams;
|
|
this.normalizer = normalizer;
|
|
}
|
|
AnimationStateStyles.prototype.buildStyles = function (params, errors) {
|
|
var _this = this;
|
|
var finalStyles = {};
|
|
var combinedParams = copyObj(this.defaultParams);
|
|
Object.keys(params).forEach(function (key) {
|
|
var value = params[key];
|
|
if (value != null) {
|
|
combinedParams[key] = value;
|
|
}
|
|
});
|
|
this.styles.styles.forEach(function (value) {
|
|
if (typeof value !== 'string') {
|
|
var styleObj_1 = value;
|
|
Object.keys(styleObj_1).forEach(function (prop) {
|
|
var val = styleObj_1[prop];
|
|
if (val.length > 1) {
|
|
val = interpolateParams(val, combinedParams, errors);
|
|
}
|
|
var normalizedProp = _this.normalizer.normalizePropertyName(prop, errors);
|
|
val = _this.normalizer.normalizeStyleValue(prop, normalizedProp, val, errors);
|
|
finalStyles[normalizedProp] = val;
|
|
});
|
|
}
|
|
});
|
|
return finalStyles;
|
|
};
|
|
return AnimationStateStyles;
|
|
}());
|
|
|
|
function buildTrigger(name, ast, normalizer) {
|
|
return new AnimationTrigger(name, ast, normalizer);
|
|
}
|
|
var AnimationTrigger = /** @class */ (function () {
|
|
function AnimationTrigger(name, ast, _normalizer) {
|
|
var _this = this;
|
|
this.name = name;
|
|
this.ast = ast;
|
|
this._normalizer = _normalizer;
|
|
this.transitionFactories = [];
|
|
this.states = {};
|
|
ast.states.forEach(function (ast) {
|
|
var defaultParams = (ast.options && ast.options.params) || {};
|
|
_this.states[ast.name] = new AnimationStateStyles(ast.style, defaultParams, _normalizer);
|
|
});
|
|
balanceProperties(this.states, 'true', '1');
|
|
balanceProperties(this.states, 'false', '0');
|
|
ast.transitions.forEach(function (ast) {
|
|
_this.transitionFactories.push(new AnimationTransitionFactory(name, ast, _this.states));
|
|
});
|
|
this.fallbackTransition = createFallbackTransition(name, this.states, this._normalizer);
|
|
}
|
|
Object.defineProperty(AnimationTrigger.prototype, "containsQueries", {
|
|
get: function () {
|
|
return this.ast.queryCount > 0;
|
|
},
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
AnimationTrigger.prototype.matchTransition = function (currentState, nextState, element, params) {
|
|
var entry = this.transitionFactories.find(function (f) { return f.match(currentState, nextState, element, params); });
|
|
return entry || null;
|
|
};
|
|
AnimationTrigger.prototype.matchStyles = function (currentState, params, errors) {
|
|
return this.fallbackTransition.buildStyles(currentState, params, errors);
|
|
};
|
|
return AnimationTrigger;
|
|
}());
|
|
function createFallbackTransition(triggerName, states, normalizer) {
|
|
var matchers = [function (fromState, toState) { return true; }];
|
|
var animation = { type: 2 /* Sequence */, steps: [], options: null };
|
|
var transition = {
|
|
type: 1 /* Transition */,
|
|
animation: animation,
|
|
matchers: matchers,
|
|
options: null,
|
|
queryCount: 0,
|
|
depCount: 0
|
|
};
|
|
return new AnimationTransitionFactory(triggerName, transition, states);
|
|
}
|
|
function balanceProperties(obj, key1, key2) {
|
|
if (obj.hasOwnProperty(key1)) {
|
|
if (!obj.hasOwnProperty(key2)) {
|
|
obj[key2] = obj[key1];
|
|
}
|
|
}
|
|
else if (obj.hasOwnProperty(key2)) {
|
|
obj[key1] = obj[key2];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright Google LLC All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://angular.io/license
|
|
*/
|
|
var EMPTY_INSTRUCTION_MAP = new ElementInstructionMap();
|
|
var TimelineAnimationEngine = /** @class */ (function () {
|
|
function TimelineAnimationEngine(bodyNode, _driver, _normalizer) {
|
|
this.bodyNode = bodyNode;
|
|
this._driver = _driver;
|
|
this._normalizer = _normalizer;
|
|
this._animations = {};
|
|
this._playersById = {};
|
|
this.players = [];
|
|
}
|
|
TimelineAnimationEngine.prototype.register = function (id, metadata) {
|
|
var errors = [];
|
|
var ast = buildAnimationAst(this._driver, metadata, errors);
|
|
if (errors.length) {
|
|
throw new Error("Unable to build the animation due to the following errors: " + errors.join('\n'));
|
|
}
|
|
else {
|
|
this._animations[id] = ast;
|
|
}
|
|
};
|
|
TimelineAnimationEngine.prototype._buildPlayer = function (i, preStyles, postStyles) {
|
|
var element = i.element;
|
|
var keyframes = normalizeKeyframes(this._driver, this._normalizer, element, i.keyframes, preStyles, postStyles);
|
|
return this._driver.animate(element, keyframes, i.duration, i.delay, i.easing, [], true);
|
|
};
|
|
TimelineAnimationEngine.prototype.create = function (id, element, options) {
|
|
var _this = this;
|
|
if (options === void 0) { options = {}; }
|
|
var errors = [];
|
|
var ast = this._animations[id];
|
|
var instructions;
|
|
var autoStylesMap = new Map();
|
|
if (ast) {
|
|
instructions = buildAnimationTimelines(this._driver, element, ast, ENTER_CLASSNAME, LEAVE_CLASSNAME, {}, {}, options, EMPTY_INSTRUCTION_MAP, errors);
|
|
instructions.forEach(function (inst) {
|
|
var styles = getOrSetAsInMap(autoStylesMap, inst.element, {});
|
|
inst.postStyleProps.forEach(function (prop) { return styles[prop] = null; });
|
|
});
|
|
}
|
|
else {
|
|
errors.push('The requested animation doesn\'t exist or has already been destroyed');
|
|
instructions = [];
|
|
}
|
|
if (errors.length) {
|
|
throw new Error("Unable to create the animation due to the following errors: " + errors.join('\n'));
|
|
}
|
|
autoStylesMap.forEach(function (styles, element) {
|
|
Object.keys(styles).forEach(function (prop) {
|
|
styles[prop] = _this._driver.computeStyle(element, prop, animations.AUTO_STYLE);
|
|
});
|
|
});
|
|
var players = instructions.map(function (i) {
|
|
var styles = autoStylesMap.get(i.element);
|
|
return _this._buildPlayer(i, {}, styles);
|
|
});
|
|
var player = optimizeGroupPlayer(players);
|
|
this._playersById[id] = player;
|
|
player.onDestroy(function () { return _this.destroy(id); });
|
|
this.players.push(player);
|
|
return player;
|
|
};
|
|
TimelineAnimationEngine.prototype.destroy = function (id) {
|
|
var player = this._getPlayer(id);
|
|
player.destroy();
|
|
delete this._playersById[id];
|
|
var index = this.players.indexOf(player);
|
|
if (index >= 0) {
|
|
this.players.splice(index, 1);
|
|
}
|
|
};
|
|
TimelineAnimationEngine.prototype._getPlayer = function (id) {
|
|
var player = this._playersById[id];
|
|
if (!player) {
|
|
throw new Error("Unable to find the timeline player referenced by " + id);
|
|
}
|
|
return player;
|
|
};
|
|
TimelineAnimationEngine.prototype.listen = function (id, element, eventName, callback) {
|
|
// triggerName, fromState, toState are all ignored for timeline animations
|
|
var baseEvent = makeAnimationEvent(element, '', '', '');
|
|
listenOnPlayer(this._getPlayer(id), eventName, baseEvent, callback);
|
|
return function () { };
|
|
};
|
|
TimelineAnimationEngine.prototype.command = function (id, element, command, args) {
|
|
if (command == 'register') {
|
|
this.register(id, args[0]);
|
|
return;
|
|
}
|
|
if (command == 'create') {
|
|
var options = (args[0] || {});
|
|
this.create(id, element, options);
|
|
return;
|
|
}
|
|
var player = this._getPlayer(id);
|
|
switch (command) {
|
|
case 'play':
|
|
player.play();
|
|
break;
|
|
case 'pause':
|
|
player.pause();
|
|
break;
|
|
case 'reset':
|
|
player.reset();
|
|
break;
|
|
case 'restart':
|
|
player.restart();
|
|
break;
|
|
case 'finish':
|
|
player.finish();
|
|
break;
|
|
case 'init':
|
|
player.init();
|
|
break;
|
|
case 'setPosition':
|
|
player.setPosition(parseFloat(args[0]));
|
|
break;
|
|
case 'destroy':
|
|
this.destroy(id);
|
|
break;
|
|
}
|
|
};
|
|
return TimelineAnimationEngine;
|
|
}());
|
|
|
|
var QUEUED_CLASSNAME = 'ng-animate-queued';
|
|
var QUEUED_SELECTOR = '.ng-animate-queued';
|
|
var DISABLED_CLASSNAME = 'ng-animate-disabled';
|
|
var DISABLED_SELECTOR = '.ng-animate-disabled';
|
|
var STAR_CLASSNAME = 'ng-star-inserted';
|
|
var STAR_SELECTOR = '.ng-star-inserted';
|
|
var EMPTY_PLAYER_ARRAY = [];
|
|
var NULL_REMOVAL_STATE = {
|
|
namespaceId: '',
|
|
setForRemoval: false,
|
|
setForMove: false,
|
|
hasAnimation: false,
|
|
removedBeforeQueried: false
|
|
};
|
|
var NULL_REMOVED_QUERIED_STATE = {
|
|
namespaceId: '',
|
|
setForMove: false,
|
|
setForRemoval: false,
|
|
hasAnimation: false,
|
|
removedBeforeQueried: true
|
|
};
|
|
var REMOVAL_FLAG = '__ng_removed';
|
|
var StateValue = /** @class */ (function () {
|
|
function StateValue(input, namespaceId) {
|
|
if (namespaceId === void 0) { namespaceId = ''; }
|
|
this.namespaceId = namespaceId;
|
|
var isObj = input && input.hasOwnProperty('value');
|
|
var value = isObj ? input['value'] : input;
|
|
this.value = normalizeTriggerValue(value);
|
|
if (isObj) {
|
|
var options = copyObj(input);
|
|
delete options['value'];
|
|
this.options = options;
|
|
}
|
|
else {
|
|
this.options = {};
|
|
}
|
|
if (!this.options.params) {
|
|
this.options.params = {};
|
|
}
|
|
}
|
|
Object.defineProperty(StateValue.prototype, "params", {
|
|
get: function () {
|
|
return this.options.params;
|
|
},
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
StateValue.prototype.absorbOptions = function (options) {
|
|
var newParams = options.params;
|
|
if (newParams) {
|
|
var oldParams_1 = this.options.params;
|
|
Object.keys(newParams).forEach(function (prop) {
|
|
if (oldParams_1[prop] == null) {
|
|
oldParams_1[prop] = newParams[prop];
|
|
}
|
|
});
|
|
}
|
|
};
|
|
return StateValue;
|
|
}());
|
|
var VOID_VALUE = 'void';
|
|
var DEFAULT_STATE_VALUE = new StateValue(VOID_VALUE);
|
|
var AnimationTransitionNamespace = /** @class */ (function () {
|
|
function AnimationTransitionNamespace(id, hostElement, _engine) {
|
|
this.id = id;
|
|
this.hostElement = hostElement;
|
|
this._engine = _engine;
|
|
this.players = [];
|
|
this._triggers = {};
|
|
this._queue = [];
|
|
this._elementListeners = new Map();
|
|
this._hostClassName = 'ng-tns-' + id;
|
|
addClass(hostElement, this._hostClassName);
|
|
}
|
|
AnimationTransitionNamespace.prototype.listen = function (element, name, phase, callback) {
|
|
var _this = this;
|
|
if (!this._triggers.hasOwnProperty(name)) {
|
|
throw new Error("Unable to listen on the animation trigger event \"" + phase + "\" because the animation trigger \"" + name + "\" doesn't exist!");
|
|
}
|
|
if (phase == null || phase.length == 0) {
|
|
throw new Error("Unable to listen on the animation trigger \"" + name + "\" because the provided event is undefined!");
|
|
}
|
|
if (!isTriggerEventValid(phase)) {
|
|
throw new Error("The provided animation trigger event \"" + phase + "\" for the animation trigger \"" + name + "\" is not supported!");
|
|
}
|
|
var listeners = getOrSetAsInMap(this._elementListeners, element, []);
|
|
var data = { name: name, phase: phase, callback: callback };
|
|
listeners.push(data);
|
|
var triggersWithStates = getOrSetAsInMap(this._engine.statesByElement, element, {});
|
|
if (!triggersWithStates.hasOwnProperty(name)) {
|
|
addClass(element, NG_TRIGGER_CLASSNAME);
|
|
addClass(element, NG_TRIGGER_CLASSNAME + '-' + name);
|
|
triggersWithStates[name] = DEFAULT_STATE_VALUE;
|
|
}
|
|
return function () {
|
|
// the event listener is removed AFTER the flush has occurred such
|
|
// that leave animations callbacks can fire (otherwise if the node
|
|
// is removed in between then the listeners would be deregistered)
|
|
_this._engine.afterFlush(function () {
|
|
var index = listeners.indexOf(data);
|
|
if (index >= 0) {
|
|
listeners.splice(index, 1);
|
|
}
|
|
if (!_this._triggers[name]) {
|
|
delete triggersWithStates[name];
|
|
}
|
|
});
|
|
};
|
|
};
|
|
AnimationTransitionNamespace.prototype.register = function (name, ast) {
|
|
if (this._triggers[name]) {
|
|
// throw
|
|
return false;
|
|
}
|
|
else {
|
|
this._triggers[name] = ast;
|
|
return true;
|
|
}
|
|
};
|
|
AnimationTransitionNamespace.prototype._getTrigger = function (name) {
|
|
var trigger = this._triggers[name];
|
|
if (!trigger) {
|
|
throw new Error("The provided animation trigger \"" + name + "\" has not been registered!");
|
|
}
|
|
return trigger;
|
|
};
|
|
AnimationTransitionNamespace.prototype.trigger = function (element, triggerName, value, defaultToFallback) {
|
|
var _this = this;
|
|
if (defaultToFallback === void 0) { defaultToFallback = true; }
|
|
var trigger = this._getTrigger(triggerName);
|
|
var player = new TransitionAnimationPlayer(this.id, triggerName, element);
|
|
var triggersWithStates = this._engine.statesByElement.get(element);
|
|
if (!triggersWithStates) {
|
|
addClass(element, NG_TRIGGER_CLASSNAME);
|
|
addClass(element, NG_TRIGGER_CLASSNAME + '-' + triggerName);
|
|
this._engine.statesByElement.set(element, triggersWithStates = {});
|
|
}
|
|
var fromState = triggersWithStates[triggerName];
|
|
var toState = new StateValue(value, this.id);
|
|
var isObj = value && value.hasOwnProperty('value');
|
|
if (!isObj && fromState) {
|
|
toState.absorbOptions(fromState.options);
|
|
}
|
|
triggersWithStates[triggerName] = toState;
|
|
if (!fromState) {
|
|
fromState = DEFAULT_STATE_VALUE;
|
|
}
|
|
var isRemoval = toState.value === VOID_VALUE;
|
|
// normally this isn't reached by here, however, if an object expression
|
|
// is passed in then it may be a new object each time. Comparing the value
|
|
// is important since that will stay the same despite there being a new object.
|
|
// The removal arc here is special cased because the same element is triggered
|
|
// twice in the event that it contains animations on the outer/inner portions
|
|
// of the host container
|
|
if (!isRemoval && fromState.value === toState.value) {
|
|
// this means that despite the value not changing, some inner params
|
|
// have changed which means that the animation final styles need to be applied
|
|
if (!objEquals(fromState.params, toState.params)) {
|
|
var errors = [];
|
|
var fromStyles_1 = trigger.matchStyles(fromState.value, fromState.params, errors);
|
|
var toStyles_1 = trigger.matchStyles(toState.value, toState.params, errors);
|
|
if (errors.length) {
|
|
this._engine.reportError(errors);
|
|
}
|
|
else {
|
|
this._engine.afterFlush(function () {
|
|
eraseStyles(element, fromStyles_1);
|
|
setStyles(element, toStyles_1);
|
|
});
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
var playersOnElement = getOrSetAsInMap(this._engine.playersByElement, element, []);
|
|
playersOnElement.forEach(function (player) {
|
|
// only remove the player if it is queued on the EXACT same trigger/namespace
|
|
// we only also deal with queued players here because if the animation has
|
|
// started then we want to keep the player alive until the flush happens
|
|
// (which is where the previousPlayers are passed into the new palyer)
|
|
if (player.namespaceId == _this.id && player.triggerName == triggerName && player.queued) {
|
|
player.destroy();
|
|
}
|
|
});
|
|
var transition = trigger.matchTransition(fromState.value, toState.value, element, toState.params);
|
|
var isFallbackTransition = false;
|
|
if (!transition) {
|
|
if (!defaultToFallback)
|
|
return;
|
|
transition = trigger.fallbackTransition;
|
|
isFallbackTransition = true;
|
|
}
|
|
this._engine.totalQueuedPlayers++;
|
|
this._queue.push({ element: element, triggerName: triggerName, transition: transition, fromState: fromState, toState: toState, player: player, isFallbackTransition: isFallbackTransition });
|
|
if (!isFallbackTransition) {
|
|
addClass(element, QUEUED_CLASSNAME);
|
|
player.onStart(function () {
|
|
removeClass(element, QUEUED_CLASSNAME);
|
|
});
|
|
}
|
|
player.onDone(function () {
|
|
var index = _this.players.indexOf(player);
|
|
if (index >= 0) {
|
|
_this.players.splice(index, 1);
|
|
}
|
|
var players = _this._engine.playersByElement.get(element);
|
|
if (players) {
|
|
var index_1 = players.indexOf(player);
|
|
if (index_1 >= 0) {
|
|
players.splice(index_1, 1);
|
|
}
|
|
}
|
|
});
|
|
this.players.push(player);
|
|
playersOnElement.push(player);
|
|
return player;
|
|
};
|
|
AnimationTransitionNamespace.prototype.deregister = function (name) {
|
|
var _this = this;
|
|
delete this._triggers[name];
|
|
this._engine.statesByElement.forEach(function (stateMap, element) {
|
|
delete stateMap[name];
|
|
});
|
|
this._elementListeners.forEach(function (listeners, element) {
|
|
_this._elementListeners.set(element, listeners.filter(function (entry) {
|
|
return entry.name != name;
|
|
}));
|
|
});
|
|
};
|
|
AnimationTransitionNamespace.prototype.clearElementCache = function (element) {
|
|
this._engine.statesByElement.delete(element);
|
|
this._elementListeners.delete(element);
|
|
var elementPlayers = this._engine.playersByElement.get(element);
|
|
if (elementPlayers) {
|
|
elementPlayers.forEach(function (player) { return player.destroy(); });
|
|
this._engine.playersByElement.delete(element);
|
|
}
|
|
};
|
|
AnimationTransitionNamespace.prototype._signalRemovalForInnerTriggers = function (rootElement, context) {
|
|
var _this = this;
|
|
var elements = this._engine.driver.query(rootElement, NG_TRIGGER_SELECTOR, true);
|
|
// emulate a leave animation for all inner nodes within this node.
|
|
// If there are no animations found for any of the nodes then clear the cache
|
|
// for the element.
|
|
elements.forEach(function (elm) {
|
|
// this means that an inner remove() operation has already kicked off
|
|
// the animation on this element...
|
|
if (elm[REMOVAL_FLAG])
|
|
return;
|
|
var namespaces = _this._engine.fetchNamespacesByElement(elm);
|
|
if (namespaces.size) {
|
|
namespaces.forEach(function (ns) { return ns.triggerLeaveAnimation(elm, context, false, true); });
|
|
}
|
|
else {
|
|
_this.clearElementCache(elm);
|
|
}
|
|
});
|
|
// If the child elements were removed along with the parent, their animations might not
|
|
// have completed. Clear all the elements from the cache so we don't end up with a memory leak.
|
|
this._engine.afterFlushAnimationsDone(function () { return elements.forEach(function (elm) { return _this.clearElementCache(elm); }); });
|
|
};
|
|
AnimationTransitionNamespace.prototype.triggerLeaveAnimation = function (element, context, destroyAfterComplete, defaultToFallback) {
|
|
var _this = this;
|
|
var triggerStates = this._engine.statesByElement.get(element);
|
|
if (triggerStates) {
|
|
var players_1 = [];
|
|
Object.keys(triggerStates).forEach(function (triggerName) {
|
|
// this check is here in the event that an element is removed
|
|
// twice (both on the host level and the component level)
|
|
if (_this._triggers[triggerName]) {
|
|
var player = _this.trigger(element, triggerName, VOID_VALUE, defaultToFallback);
|
|
if (player) {
|
|
players_1.push(player);
|
|
}
|
|
}
|
|
});
|
|
if (players_1.length) {
|
|
this._engine.markElementAsRemoved(this.id, element, true, context);
|
|
if (destroyAfterComplete) {
|
|
optimizeGroupPlayer(players_1).onDone(function () { return _this._engine.processLeaveNode(element); });
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
AnimationTransitionNamespace.prototype.prepareLeaveAnimationListeners = function (element) {
|
|
var _this = this;
|
|
var listeners = this._elementListeners.get(element);
|
|
var elementStates = this._engine.statesByElement.get(element);
|
|
// if this statement fails then it means that the element was picked up
|
|
// by an earlier flush (or there are no listeners at all to track the leave).
|
|
if (listeners && elementStates) {
|
|
var visitedTriggers_1 = new Set();
|
|
listeners.forEach(function (listener) {
|
|
var triggerName = listener.name;
|
|
if (visitedTriggers_1.has(triggerName))
|
|
return;
|
|
visitedTriggers_1.add(triggerName);
|
|
var trigger = _this._triggers[triggerName];
|
|
var transition = trigger.fallbackTransition;
|
|
var fromState = elementStates[triggerName] || DEFAULT_STATE_VALUE;
|
|
var toState = new StateValue(VOID_VALUE);
|
|
var player = new TransitionAnimationPlayer(_this.id, triggerName, element);
|
|
_this._engine.totalQueuedPlayers++;
|
|
_this._queue.push({
|
|
element: element,
|
|
triggerName: triggerName,
|
|
transition: transition,
|
|
fromState: fromState,
|
|
toState: toState,
|
|
player: player,
|
|
isFallbackTransition: true
|
|
});
|
|
});
|
|
}
|
|
};
|
|
AnimationTransitionNamespace.prototype.removeNode = function (element, context) {
|
|
var _this = this;
|
|
var engine = this._engine;
|
|
if (element.childElementCount) {
|
|
this._signalRemovalForInnerTriggers(element, context);
|
|
}
|
|
// this means that a * => VOID animation was detected and kicked off
|
|
if (this.triggerLeaveAnimation(element, context, true))
|
|
return;
|
|
// find the player that is animating and make sure that the
|
|
// removal is delayed until that player has completed
|
|
var containsPotentialParentTransition = false;
|
|
if (engine.totalAnimations) {
|
|
var currentPlayers = engine.players.length ? engine.playersByQueriedElement.get(element) : [];
|
|
// when this `if statement` does not continue forward it means that
|
|
// a previous animation query has selected the current element and
|
|
// is animating it. In this situation want to continue forwards and
|
|
// allow the element to be queued up for animation later.
|
|
if (currentPlayers && currentPlayers.length) {
|
|
containsPotentialParentTransition = true;
|
|
}
|
|
else {
|
|
var parent = element;
|
|
while (parent = parent.parentNode) {
|
|
var triggers = engine.statesByElement.get(parent);
|
|
if (triggers) {
|
|
containsPotentialParentTransition = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// at this stage we know that the element will either get removed
|
|
// during flush or will be picked up by a parent query. Either way
|
|
// we need to fire the listeners for this element when it DOES get
|
|
// removed (once the query parent animation is done or after flush)
|
|
this.prepareLeaveAnimationListeners(element);
|
|
// whether or not a parent has an animation we need to delay the deferral of the leave
|
|
// operation until we have more information (which we do after flush() has been called)
|
|
if (containsPotentialParentTransition) {
|
|
engine.markElementAsRemoved(this.id, element, false, context);
|
|
}
|
|
else {
|
|
var removalFlag = element[REMOVAL_FLAG];
|
|
if (!removalFlag || removalFlag === NULL_REMOVAL_STATE) {
|
|
// we do this after the flush has occurred such
|
|
// that the callbacks can be fired
|
|
engine.afterFlush(function () { return _this.clearElementCache(element); });
|
|
engine.destroyInnerAnimations(element);
|
|
engine._onRemovalComplete(element, context);
|
|
}
|
|
}
|
|
};
|
|
AnimationTransitionNamespace.prototype.insertNode = function (element, parent) {
|
|
addClass(element, this._hostClassName);
|
|
};
|
|
AnimationTransitionNamespace.prototype.drainQueuedTransitions = function (microtaskId) {
|
|
var _this = this;
|
|
var instructions = [];
|
|
this._queue.forEach(function (entry) {
|
|
var player = entry.player;
|
|
if (player.destroyed)
|
|
return;
|
|
var element = entry.element;
|
|
var listeners = _this._elementListeners.get(element);
|
|
if (listeners) {
|
|
listeners.forEach(function (listener) {
|
|
if (listener.name == entry.triggerName) {
|
|
var baseEvent = makeAnimationEvent(element, entry.triggerName, entry.fromState.value, entry.toState.value);
|
|
baseEvent['_data'] = microtaskId;
|
|
listenOnPlayer(entry.player, listener.phase, baseEvent, listener.callback);
|
|
}
|
|
});
|
|
}
|
|
if (player.markedForDestroy) {
|
|
_this._engine.afterFlush(function () {
|
|
// now we can destroy the element properly since the event listeners have
|
|
// been bound to the player
|
|
player.destroy();
|
|
});
|
|
}
|
|
else {
|
|
instructions.push(entry);
|
|
}
|
|
});
|
|
this._queue = [];
|
|
return instructions.sort(function (a, b) {
|
|
// if depCount == 0 them move to front
|
|
// otherwise if a contains b then move back
|
|
var d0 = a.transition.ast.depCount;
|
|
var d1 = b.transition.ast.depCount;
|
|
if (d0 == 0 || d1 == 0) {
|
|
return d0 - d1;
|
|
}
|
|
return _this._engine.driver.containsElement(a.element, b.element) ? 1 : -1;
|
|
});
|
|
};
|
|
AnimationTransitionNamespace.prototype.destroy = function (context) {
|
|
this.players.forEach(function (p) { return p.destroy(); });
|
|
this._signalRemovalForInnerTriggers(this.hostElement, context);
|
|
};
|
|
AnimationTransitionNamespace.prototype.elementContainsData = function (element) {
|
|
var containsData = false;
|
|
if (this._elementListeners.has(element))
|
|
containsData = true;
|
|
containsData =
|
|
(this._queue.find(function (entry) { return entry.element === element; }) ? true : false) || containsData;
|
|
return containsData;
|
|
};
|
|
return AnimationTransitionNamespace;
|
|
}());
|
|
var TransitionAnimationEngine = /** @class */ (function () {
|
|
function TransitionAnimationEngine(bodyNode, driver, _normalizer) {
|
|
this.bodyNode = bodyNode;
|
|
this.driver = driver;
|
|
this._normalizer = _normalizer;
|
|
this.players = [];
|
|
this.newHostElements = new Map();
|
|
this.playersByElement = new Map();
|
|
this.playersByQueriedElement = new Map();
|
|
this.statesByElement = new Map();
|
|
this.disabledNodes = new Set();
|
|
this.totalAnimations = 0;
|
|
this.totalQueuedPlayers = 0;
|
|
this._namespaceLookup = {};
|
|
this._namespaceList = [];
|
|
this._flushFns = [];
|
|
this._whenQuietFns = [];
|
|
this.namespacesByHostElement = new Map();
|
|
this.collectedEnterElements = [];
|
|
this.collectedLeaveElements = [];
|
|
// this method is designed to be overridden by the code that uses this engine
|
|
this.onRemovalComplete = function (element, context) { };
|
|
}
|
|
/** @internal */
|
|
TransitionAnimationEngine.prototype._onRemovalComplete = function (element, context) {
|
|
this.onRemovalComplete(element, context);
|
|
};
|
|
Object.defineProperty(TransitionAnimationEngine.prototype, "queuedPlayers", {
|
|
get: function () {
|
|
var players = [];
|
|
this._namespaceList.forEach(function (ns) {
|
|
ns.players.forEach(function (player) {
|
|
if (player.queued) {
|
|
players.push(player);
|
|
}
|
|
});
|
|
});
|
|
return players;
|
|
},
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
TransitionAnimationEngine.prototype.createNamespace = function (namespaceId, hostElement) {
|
|
var ns = new AnimationTransitionNamespace(namespaceId, hostElement, this);
|
|
if (this.bodyNode && this.driver.containsElement(this.bodyNode, hostElement)) {
|
|
this._balanceNamespaceList(ns, hostElement);
|
|
}
|
|
else {
|
|
// defer this later until flush during when the host element has
|
|
// been inserted so that we know exactly where to place it in
|
|
// the namespace list
|
|
this.newHostElements.set(hostElement, ns);
|
|
// given that this host element is apart of the animation code, it
|
|
// may or may not be inserted by a parent node that is of an
|
|
// animation renderer type. If this happens then we can still have
|
|
// access to this item when we query for :enter nodes. If the parent
|
|
// is a renderer then the set data-structure will normalize the entry
|
|
this.collectEnterElement(hostElement);
|
|
}
|
|
return this._namespaceLookup[namespaceId] = ns;
|
|
};
|
|
TransitionAnimationEngine.prototype._balanceNamespaceList = function (ns, hostElement) {
|
|
var limit = this._namespaceList.length - 1;
|
|
if (limit >= 0) {
|
|
var found = false;
|
|
for (var i = limit; i >= 0; i--) {
|
|
var nextNamespace = this._namespaceList[i];
|
|
if (this.driver.containsElement(nextNamespace.hostElement, hostElement)) {
|
|
this._namespaceList.splice(i + 1, 0, ns);
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
this._namespaceList.splice(0, 0, ns);
|
|
}
|
|
}
|
|
else {
|
|
this._namespaceList.push(ns);
|
|
}
|
|
this.namespacesByHostElement.set(hostElement, ns);
|
|
return ns;
|
|
};
|
|
TransitionAnimationEngine.prototype.register = function (namespaceId, hostElement) {
|
|
var ns = this._namespaceLookup[namespaceId];
|
|
if (!ns) {
|
|
ns = this.createNamespace(namespaceId, hostElement);
|
|
}
|
|
return ns;
|
|
};
|
|
TransitionAnimationEngine.prototype.registerTrigger = function (namespaceId, name, trigger) {
|
|
var ns = this._namespaceLookup[namespaceId];
|
|
if (ns && ns.register(name, trigger)) {
|
|
this.totalAnimations++;
|
|
}
|
|
};
|
|
TransitionAnimationEngine.prototype.destroy = function (namespaceId, context) {
|
|
var _this = this;
|
|
if (!namespaceId)
|
|
return;
|
|
var ns = this._fetchNamespace(namespaceId);
|
|
this.afterFlush(function () {
|
|
_this.namespacesByHostElement.delete(ns.hostElement);
|
|
delete _this._namespaceLookup[namespaceId];
|
|
var index = _this._namespaceList.indexOf(ns);
|
|
if (index >= 0) {
|
|
_this._namespaceList.splice(index, 1);
|
|
}
|
|
});
|
|
this.afterFlushAnimationsDone(function () { return ns.destroy(context); });
|
|
};
|
|
TransitionAnimationEngine.prototype._fetchNamespace = function (id) {
|
|
return this._namespaceLookup[id];
|
|
};
|
|
TransitionAnimationEngine.prototype.fetchNamespacesByElement = function (element) {
|
|
// normally there should only be one namespace per element, however
|
|
// if @triggers are placed on both the component element and then
|
|
// its host element (within the component code) then there will be
|
|
// two namespaces returned. We use a set here to simply the dedupe
|
|
// of namespaces incase there are multiple triggers both the elm and host
|
|
var namespaces = new Set();
|
|
var elementStates = this.statesByElement.get(element);
|
|
if (elementStates) {
|
|
var keys = Object.keys(elementStates);
|
|
for (var i = 0; i < keys.length; i++) {
|
|
var nsId = elementStates[keys[i]].namespaceId;
|
|
if (nsId) {
|
|
var ns = this._fetchNamespace(nsId);
|
|
if (ns) {
|
|
namespaces.add(ns);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return namespaces;
|
|
};
|
|
TransitionAnimationEngine.prototype.trigger = function (namespaceId, element, name, value) {
|
|
if (isElementNode(element)) {
|
|
var ns = this._fetchNamespace(namespaceId);
|
|
if (ns) {
|
|
ns.trigger(element, name, value);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
TransitionAnimationEngine.prototype.insertNode = function (namespaceId, element, parent, insertBefore) {
|
|
if (!isElementNode(element))
|
|
return;
|
|
// special case for when an element is removed and reinserted (move operation)
|
|
// when this occurs we do not want to use the element for deletion later
|
|
var details = element[REMOVAL_FLAG];
|
|
if (details && details.setForRemoval) {
|
|
details.setForRemoval = false;
|
|
details.setForMove = true;
|
|
var index = this.collectedLeaveElements.indexOf(element);
|
|
if (index >= 0) {
|
|
this.collectedLeaveElements.splice(index, 1);
|
|
}
|
|
}
|
|
// in the event that the namespaceId is blank then the caller
|
|
// code does not contain any animation code in it, but it is
|
|
// just being called so that the node is marked as being inserted
|
|
if (namespaceId) {
|
|
var ns = this._fetchNamespace(namespaceId);
|
|
// This if-statement is a workaround for router issue #21947.
|
|
// The router sometimes hits a race condition where while a route
|
|
// is being instantiated a new navigation arrives, triggering leave
|
|
// animation of DOM that has not been fully initialized, until this
|
|
// is resolved, we need to handle the scenario when DOM is not in a
|
|
// consistent state during the animation.
|
|
if (ns) {
|
|
ns.insertNode(element, parent);
|
|
}
|
|
}
|
|
// only *directives and host elements are inserted before
|
|
if (insertBefore) {
|
|
this.collectEnterElement(element);
|
|
}
|
|
};
|
|
TransitionAnimationEngine.prototype.collectEnterElement = function (element) {
|
|
this.collectedEnterElements.push(element);
|
|
};
|
|
TransitionAnimationEngine.prototype.markElementAsDisabled = function (element, value) {
|
|
if (value) {
|
|
if (!this.disabledNodes.has(element)) {
|
|
this.disabledNodes.add(element);
|
|
addClass(element, DISABLED_CLASSNAME);
|
|
}
|
|
}
|
|
else if (this.disabledNodes.has(element)) {
|
|
this.disabledNodes.delete(element);
|
|
removeClass(element, DISABLED_CLASSNAME);
|
|
}
|
|
};
|
|
TransitionAnimationEngine.prototype.removeNode = function (namespaceId, element, isHostElement, context) {
|
|
if (isElementNode(element)) {
|
|
var ns = namespaceId ? this._fetchNamespace(namespaceId) : null;
|
|
if (ns) {
|
|
ns.removeNode(element, context);
|
|
}
|
|
else {
|
|
this.markElementAsRemoved(namespaceId, element, false, context);
|
|
}
|
|
if (isHostElement) {
|
|
var hostNS = this.namespacesByHostElement.get(element);
|
|
if (hostNS && hostNS.id !== namespaceId) {
|
|
hostNS.removeNode(element, context);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
this._onRemovalComplete(element, context);
|
|
}
|
|
};
|
|
TransitionAnimationEngine.prototype.markElementAsRemoved = function (namespaceId, element, hasAnimation, context) {
|
|
this.collectedLeaveElements.push(element);
|
|
element[REMOVAL_FLAG] =
|
|
{ namespaceId: namespaceId, setForRemoval: context, hasAnimation: hasAnimation, removedBeforeQueried: false };
|
|
};
|
|
TransitionAnimationEngine.prototype.listen = function (namespaceId, element, name, phase, callback) {
|
|
if (isElementNode(element)) {
|
|
return this._fetchNamespace(namespaceId).listen(element, name, phase, callback);
|
|
}
|
|
return function () { };
|
|
};
|
|
TransitionAnimationEngine.prototype._buildInstruction = function (entry, subTimelines, enterClassName, leaveClassName, skipBuildAst) {
|
|
return entry.transition.build(this.driver, entry.element, entry.fromState.value, entry.toState.value, enterClassName, leaveClassName, entry.fromState.options, entry.toState.options, subTimelines, skipBuildAst);
|
|
};
|
|
TransitionAnimationEngine.prototype.destroyInnerAnimations = function (containerElement) {
|
|
var _this = this;
|
|
var elements = this.driver.query(containerElement, NG_TRIGGER_SELECTOR, true);
|
|
elements.forEach(function (element) { return _this.destroyActiveAnimationsForElement(element); });
|
|
if (this.playersByQueriedElement.size == 0)
|
|
return;
|
|
elements = this.driver.query(containerElement, NG_ANIMATING_SELECTOR, true);
|
|
elements.forEach(function (element) { return _this.finishActiveQueriedAnimationOnElement(element); });
|
|
};
|
|
TransitionAnimationEngine.prototype.destroyActiveAnimationsForElement = function (element) {
|
|
var players = this.playersByElement.get(element);
|
|
if (players) {
|
|
players.forEach(function (player) {
|
|
// special case for when an element is set for destruction, but hasn't started.
|
|
// in this situation we want to delay the destruction until the flush occurs
|
|
// so that any event listeners attached to the player are triggered.
|
|
if (player.queued) {
|
|
player.markedForDestroy = true;
|
|
}
|
|
else {
|
|
player.destroy();
|
|
}
|
|
});
|
|
}
|
|
};
|
|
TransitionAnimationEngine.prototype.finishActiveQueriedAnimationOnElement = function (element) {
|
|
var players = this.playersByQueriedElement.get(element);
|
|
if (players) {
|
|
players.forEach(function (player) { return player.finish(); });
|
|
}
|
|
};
|
|
TransitionAnimationEngine.prototype.whenRenderingDone = function () {
|
|
var _this = this;
|
|
return new Promise(function (resolve) {
|
|
if (_this.players.length) {
|
|
return optimizeGroupPlayer(_this.players).onDone(function () { return resolve(); });
|
|
}
|
|
else {
|
|
resolve();
|
|
}
|
|
});
|
|
};
|
|
TransitionAnimationEngine.prototype.processLeaveNode = function (element) {
|
|
var _this = this;
|
|
var details = element[REMOVAL_FLAG];
|
|
if (details && details.setForRemoval) {
|
|
// this will prevent it from removing it twice
|
|
element[REMOVAL_FLAG] = NULL_REMOVAL_STATE;
|
|
if (details.namespaceId) {
|
|
this.destroyInnerAnimations(element);
|
|
var ns = this._fetchNamespace(details.namespaceId);
|
|
if (ns) {
|
|
ns.clearElementCache(element);
|
|
}
|
|
}
|
|
this._onRemovalComplete(element, details.setForRemoval);
|
|
}
|
|
if (this.driver.matchesElement(element, DISABLED_SELECTOR)) {
|
|
this.markElementAsDisabled(element, false);
|
|
}
|
|
this.driver.query(element, DISABLED_SELECTOR, true).forEach(function (node) {
|
|
_this.markElementAsDisabled(node, false);
|
|
});
|
|
};
|
|
TransitionAnimationEngine.prototype.flush = function (microtaskId) {
|
|
var _this = this;
|
|
if (microtaskId === void 0) { microtaskId = -1; }
|
|
var players = [];
|
|
if (this.newHostElements.size) {
|
|
this.newHostElements.forEach(function (ns, element) { return _this._balanceNamespaceList(ns, element); });
|
|
this.newHostElements.clear();
|
|
}
|
|
if (this.totalAnimations && this.collectedEnterElements.length) {
|
|
for (var i = 0; i < this.collectedEnterElements.length; i++) {
|
|
var elm = this.collectedEnterElements[i];
|
|
addClass(elm, STAR_CLASSNAME);
|
|
}
|
|
}
|
|
if (this._namespaceList.length &&
|
|
(this.totalQueuedPlayers || this.collectedLeaveElements.length)) {
|
|
var cleanupFns = [];
|
|
try {
|
|
players = this._flushAnimations(cleanupFns, microtaskId);
|
|
}
|
|
finally {
|
|
for (var i = 0; i < cleanupFns.length; i++) {
|
|
cleanupFns[i]();
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
for (var i = 0; i < this.collectedLeaveElements.length; i++) {
|
|
var element = this.collectedLeaveElements[i];
|
|
this.processLeaveNode(element);
|
|
}
|
|
}
|
|
this.totalQueuedPlayers = 0;
|
|
this.collectedEnterElements.length = 0;
|
|
this.collectedLeaveElements.length = 0;
|
|
this._flushFns.forEach(function (fn) { return fn(); });
|
|
this._flushFns = [];
|
|
if (this._whenQuietFns.length) {
|
|
// we move these over to a variable so that
|
|
// if any new callbacks are registered in another
|
|
// flush they do not populate the existing set
|
|
var quietFns_1 = this._whenQuietFns;
|
|
this._whenQuietFns = [];
|
|
if (players.length) {
|
|
optimizeGroupPlayer(players).onDone(function () {
|
|
quietFns_1.forEach(function (fn) { return fn(); });
|
|
});
|
|
}
|
|
else {
|
|
quietFns_1.forEach(function (fn) { return fn(); });
|
|
}
|
|
}
|
|
};
|
|
TransitionAnimationEngine.prototype.reportError = function (errors) {
|
|
throw new Error("Unable to process animations due to the following failed trigger transitions\n " + errors.join('\n'));
|
|
};
|
|
TransitionAnimationEngine.prototype._flushAnimations = function (cleanupFns, microtaskId) {
|
|
var _this = this;
|
|
var subTimelines = new ElementInstructionMap();
|
|
var skippedPlayers = [];
|
|
var skippedPlayersMap = new Map();
|
|
var queuedInstructions = [];
|
|
var queriedElements = new Map();
|
|
var allPreStyleElements = new Map();
|
|
var allPostStyleElements = new Map();
|
|
var disabledElementsSet = new Set();
|
|
this.disabledNodes.forEach(function (node) {
|
|
disabledElementsSet.add(node);
|
|
var nodesThatAreDisabled = _this.driver.query(node, QUEUED_SELECTOR, true);
|
|
for (var i_1 = 0; i_1 < nodesThatAreDisabled.length; i_1++) {
|
|
disabledElementsSet.add(nodesThatAreDisabled[i_1]);
|
|
}
|
|
});
|
|
var bodyNode = this.bodyNode;
|
|
var allTriggerElements = Array.from(this.statesByElement.keys());
|
|
var enterNodeMap = buildRootMap(allTriggerElements, this.collectedEnterElements);
|
|
// this must occur before the instructions are built below such that
|
|
// the :enter queries match the elements (since the timeline queries
|
|
// are fired during instruction building).
|
|
var enterNodeMapIds = new Map();
|
|
var i = 0;
|
|
enterNodeMap.forEach(function (nodes, root) {
|
|
var className = ENTER_CLASSNAME + i++;
|
|
enterNodeMapIds.set(root, className);
|
|
nodes.forEach(function (node) { return addClass(node, className); });
|
|
});
|
|
var allLeaveNodes = [];
|
|
var mergedLeaveNodes = new Set();
|
|
var leaveNodesWithoutAnimations = new Set();
|
|
for (var i_2 = 0; i_2 < this.collectedLeaveElements.length; i_2++) {
|
|
var element = this.collectedLeaveElements[i_2];
|
|
var details = element[REMOVAL_FLAG];
|
|
if (details && details.setForRemoval) {
|
|
allLeaveNodes.push(element);
|
|
mergedLeaveNodes.add(element);
|
|
if (details.hasAnimation) {
|
|
this.driver.query(element, STAR_SELECTOR, true).forEach(function (elm) { return mergedLeaveNodes.add(elm); });
|
|
}
|
|
else {
|
|
leaveNodesWithoutAnimations.add(element);
|
|
}
|
|
}
|
|
}
|
|
var leaveNodeMapIds = new Map();
|
|
var leaveNodeMap = buildRootMap(allTriggerElements, Array.from(mergedLeaveNodes));
|
|
leaveNodeMap.forEach(function (nodes, root) {
|
|
var className = LEAVE_CLASSNAME + i++;
|
|
leaveNodeMapIds.set(root, className);
|
|
nodes.forEach(function (node) { return addClass(node, className); });
|
|
});
|
|
cleanupFns.push(function () {
|
|
enterNodeMap.forEach(function (nodes, root) {
|
|
var className = enterNodeMapIds.get(root);
|
|
nodes.forEach(function (node) { return removeClass(node, className); });
|
|
});
|
|
leaveNodeMap.forEach(function (nodes, root) {
|
|
var className = leaveNodeMapIds.get(root);
|
|
nodes.forEach(function (node) { return removeClass(node, className); });
|
|
});
|
|
allLeaveNodes.forEach(function (element) {
|
|
_this.processLeaveNode(element);
|
|
});
|
|
});
|
|
var allPlayers = [];
|
|
var erroneousTransitions = [];
|
|
for (var i_3 = this._namespaceList.length - 1; i_3 >= 0; i_3--) {
|
|
var ns = this._namespaceList[i_3];
|
|
ns.drainQueuedTransitions(microtaskId).forEach(function (entry) {
|
|
var player = entry.player;
|
|
var element = entry.element;
|
|
allPlayers.push(player);
|
|
if (_this.collectedEnterElements.length) {
|
|
var details = element[REMOVAL_FLAG];
|
|
// move animations are currently not supported...
|
|
if (details && details.setForMove) {
|
|
player.destroy();
|
|
return;
|
|
}
|
|
}
|
|
var nodeIsOrphaned = !bodyNode || !_this.driver.containsElement(bodyNode, element);
|
|
var leaveClassName = leaveNodeMapIds.get(element);
|
|
var enterClassName = enterNodeMapIds.get(element);
|
|
var instruction = _this._buildInstruction(entry, subTimelines, enterClassName, leaveClassName, nodeIsOrphaned);
|
|
if (instruction.errors && instruction.errors.length) {
|
|
erroneousTransitions.push(instruction);
|
|
return;
|
|
}
|
|
// even though the element may not be apart of the DOM, it may
|
|
// still be added at a later point (due to the mechanics of content
|
|
// projection and/or dynamic component insertion) therefore it's
|
|
// important we still style the element.
|
|
if (nodeIsOrphaned) {
|
|
player.onStart(function () { return eraseStyles(element, instruction.fromStyles); });
|
|
player.onDestroy(function () { return setStyles(element, instruction.toStyles); });
|
|
skippedPlayers.push(player);
|
|
return;
|
|
}
|
|
// if a unmatched transition is queued to go then it SHOULD NOT render
|
|
// an animation and cancel the previously running animations.
|
|
if (entry.isFallbackTransition) {
|
|
player.onStart(function () { return eraseStyles(element, instruction.fromStyles); });
|
|
player.onDestroy(function () { return setStyles(element, instruction.toStyles); });
|
|
skippedPlayers.push(player);
|
|
return;
|
|
}
|
|
// this means that if a parent animation uses this animation as a sub trigger
|
|
// then it will instruct the timeline builder to not add a player delay, but
|
|
// instead stretch the first keyframe gap up until the animation starts. The
|
|
// reason this is important is to prevent extra initialization styles from being
|
|
// required by the user in the animation.
|
|
instruction.timelines.forEach(function (tl) { return tl.stretchStartingKeyframe = true; });
|
|
subTimelines.append(element, instruction.timelines);
|
|
var tuple = { instruction: instruction, player: player, element: element };
|
|
queuedInstructions.push(tuple);
|
|
instruction.queriedElements.forEach(function (element) { return getOrSetAsInMap(queriedElements, element, []).push(player); });
|
|
instruction.preStyleProps.forEach(function (stringMap, element) {
|
|
var props = Object.keys(stringMap);
|
|
if (props.length) {
|
|
var setVal_1 = allPreStyleElements.get(element);
|
|
if (!setVal_1) {
|
|
allPreStyleElements.set(element, setVal_1 = new Set());
|
|
}
|
|
props.forEach(function (prop) { return setVal_1.add(prop); });
|
|
}
|
|
});
|
|
instruction.postStyleProps.forEach(function (stringMap, element) {
|
|
var props = Object.keys(stringMap);
|
|
var setVal = allPostStyleElements.get(element);
|
|
if (!setVal) {
|
|
allPostStyleElements.set(element, setVal = new Set());
|
|
}
|
|
props.forEach(function (prop) { return setVal.add(prop); });
|
|
});
|
|
});
|
|
}
|
|
if (erroneousTransitions.length) {
|
|
var errors_1 = [];
|
|
erroneousTransitions.forEach(function (instruction) {
|
|
errors_1.push("@" + instruction.triggerName + " has failed due to:\n");
|
|
instruction.errors.forEach(function (error) { return errors_1.push("- " + error + "\n"); });
|
|
});
|
|
allPlayers.forEach(function (player) { return player.destroy(); });
|
|
this.reportError(errors_1);
|
|
}
|
|
var allPreviousPlayersMap = new Map();
|
|
// this map works to tell which element in the DOM tree is contained by
|
|
// which animation. Further down below this map will get populated once
|
|
// the players are built and in doing so it can efficiently figure out
|
|
// if a sub player is skipped due to a parent player having priority.
|
|
var animationElementMap = new Map();
|
|
queuedInstructions.forEach(function (entry) {
|
|
var element = entry.element;
|
|
if (subTimelines.has(element)) {
|
|
animationElementMap.set(element, element);
|
|
_this._beforeAnimationBuild(entry.player.namespaceId, entry.instruction, allPreviousPlayersMap);
|
|
}
|
|
});
|
|
skippedPlayers.forEach(function (player) {
|
|
var element = player.element;
|
|
var previousPlayers = _this._getPreviousPlayers(element, false, player.namespaceId, player.triggerName, null);
|
|
previousPlayers.forEach(function (prevPlayer) {
|
|
getOrSetAsInMap(allPreviousPlayersMap, element, []).push(prevPlayer);
|
|
prevPlayer.destroy();
|
|
});
|
|
});
|
|
// this is a special case for nodes that will be removed (either by)
|
|
// having their own leave animations or by being queried in a container
|
|
// that will be removed once a parent animation is complete. The idea
|
|
// here is that * styles must be identical to ! styles because of
|
|
// backwards compatibility (* is also filled in by default in many places).
|
|
// Otherwise * styles will return an empty value or auto since the element
|
|
// that is being getComputedStyle'd will not be visible (since * = destination)
|
|
var replaceNodes = allLeaveNodes.filter(function (node) {
|
|
return replacePostStylesAsPre(node, allPreStyleElements, allPostStyleElements);
|
|
});
|
|
// POST STAGE: fill the * styles
|
|
var postStylesMap = new Map();
|
|
var allLeaveQueriedNodes = cloakAndComputeStyles(postStylesMap, this.driver, leaveNodesWithoutAnimations, allPostStyleElements, animations.AUTO_STYLE);
|
|
allLeaveQueriedNodes.forEach(function (node) {
|
|
if (replacePostStylesAsPre(node, allPreStyleElements, allPostStyleElements)) {
|
|
replaceNodes.push(node);
|
|
}
|
|
});
|
|
// PRE STAGE: fill the ! styles
|
|
var preStylesMap = new Map();
|
|
enterNodeMap.forEach(function (nodes, root) {
|
|
cloakAndComputeStyles(preStylesMap, _this.driver, new Set(nodes), allPreStyleElements, animations.ɵPRE_STYLE);
|
|
});
|
|
replaceNodes.forEach(function (node) {
|
|
var post = postStylesMap.get(node);
|
|
var pre = preStylesMap.get(node);
|
|
postStylesMap.set(node, Object.assign(Object.assign({}, post), pre));
|
|
});
|
|
var rootPlayers = [];
|
|
var subPlayers = [];
|
|
var NO_PARENT_ANIMATION_ELEMENT_DETECTED = {};
|
|
queuedInstructions.forEach(function (entry) {
|
|
var element = entry.element, player = entry.player, instruction = entry.instruction;
|
|
// this means that it was never consumed by a parent animation which
|
|
// means that it is independent and therefore should be set for animation
|
|
if (subTimelines.has(element)) {
|
|
if (disabledElementsSet.has(element)) {
|
|
player.onDestroy(function () { return setStyles(element, instruction.toStyles); });
|
|
player.disabled = true;
|
|
player.overrideTotalTime(instruction.totalTime);
|
|
skippedPlayers.push(player);
|
|
return;
|
|
}
|
|
// this will flow up the DOM and query the map to figure out
|
|
// if a parent animation has priority over it. In the situation
|
|
// that a parent is detected then it will cancel the loop. If
|
|
// nothing is detected, or it takes a few hops to find a parent,
|
|
// then it will fill in the missing nodes and signal them as having
|
|
// a detected parent (or a NO_PARENT value via a special constant).
|
|
var parentWithAnimation_1 = NO_PARENT_ANIMATION_ELEMENT_DETECTED;
|
|
if (animationElementMap.size > 1) {
|
|
var elm = element;
|
|
var parentsToAdd = [];
|
|
while (elm = elm.parentNode) {
|
|
var detectedParent = animationElementMap.get(elm);
|
|
if (detectedParent) {
|
|
parentWithAnimation_1 = detectedParent;
|
|
break;
|
|
}
|
|
parentsToAdd.push(elm);
|
|
}
|
|
parentsToAdd.forEach(function (parent) { return animationElementMap.set(parent, parentWithAnimation_1); });
|
|
}
|
|
var innerPlayer = _this._buildAnimation(player.namespaceId, instruction, allPreviousPlayersMap, skippedPlayersMap, preStylesMap, postStylesMap);
|
|
player.setRealPlayer(innerPlayer);
|
|
if (parentWithAnimation_1 === NO_PARENT_ANIMATION_ELEMENT_DETECTED) {
|
|
rootPlayers.push(player);
|
|
}
|
|
else {
|
|
var parentPlayers = _this.playersByElement.get(parentWithAnimation_1);
|
|
if (parentPlayers && parentPlayers.length) {
|
|
player.parentPlayer = optimizeGroupPlayer(parentPlayers);
|
|
}
|
|
skippedPlayers.push(player);
|
|
}
|
|
}
|
|
else {
|
|
eraseStyles(element, instruction.fromStyles);
|
|
player.onDestroy(function () { return setStyles(element, instruction.toStyles); });
|
|
// there still might be a ancestor player animating this
|
|
// element therefore we will still add it as a sub player
|
|
// even if its animation may be disabled
|
|
subPlayers.push(player);
|
|
if (disabledElementsSet.has(element)) {
|
|
skippedPlayers.push(player);
|
|
}
|
|
}
|
|
});
|
|
// find all of the sub players' corresponding inner animation player
|
|
subPlayers.forEach(function (player) {
|
|
// even if any players are not found for a sub animation then it
|
|
// will still complete itself after the next tick since it's Noop
|
|
var playersForElement = skippedPlayersMap.get(player.element);
|
|
if (playersForElement && playersForElement.length) {
|
|
var innerPlayer = optimizeGroupPlayer(playersForElement);
|
|
player.setRealPlayer(innerPlayer);
|
|
}
|
|
});
|
|
// the reason why we don't actually play the animation is
|
|
// because all that a skipped player is designed to do is to
|
|
// fire the start/done transition callback events
|
|
skippedPlayers.forEach(function (player) {
|
|
if (player.parentPlayer) {
|
|
player.syncPlayerEvents(player.parentPlayer);
|
|
}
|
|
else {
|
|
player.destroy();
|
|
}
|
|
});
|
|
// run through all of the queued removals and see if they
|
|
// were picked up by a query. If not then perform the removal
|
|
// operation right away unless a parent animation is ongoing.
|
|
for (var i_4 = 0; i_4 < allLeaveNodes.length; i_4++) {
|
|
var element = allLeaveNodes[i_4];
|
|
var details = element[REMOVAL_FLAG];
|
|
removeClass(element, LEAVE_CLASSNAME);
|
|
// this means the element has a removal animation that is being
|
|
// taken care of and therefore the inner elements will hang around
|
|
// until that animation is over (or the parent queried animation)
|
|
if (details && details.hasAnimation)
|
|
continue;
|
|
var players = [];
|
|
// if this element is queried or if it contains queried children
|
|
// then we want for the element not to be removed from the page
|
|
// until the queried animations have finished
|
|
if (queriedElements.size) {
|
|
var queriedPlayerResults = queriedElements.get(element);
|
|
if (queriedPlayerResults && queriedPlayerResults.length) {
|
|
players.push.apply(players, __spreadArray([], __read(queriedPlayerResults)));
|
|
}
|
|
var queriedInnerElements = this.driver.query(element, NG_ANIMATING_SELECTOR, true);
|
|
for (var j = 0; j < queriedInnerElements.length; j++) {
|
|
var queriedPlayers = queriedElements.get(queriedInnerElements[j]);
|
|
if (queriedPlayers && queriedPlayers.length) {
|
|
players.push.apply(players, __spreadArray([], __read(queriedPlayers)));
|
|
}
|
|
}
|
|
}
|
|
var activePlayers = players.filter(function (p) { return !p.destroyed; });
|
|
if (activePlayers.length) {
|
|
removeNodesAfterAnimationDone(this, element, activePlayers);
|
|
}
|
|
else {
|
|
this.processLeaveNode(element);
|
|
}
|
|
}
|
|
// this is required so the cleanup method doesn't remove them
|
|
allLeaveNodes.length = 0;
|
|
rootPlayers.forEach(function (player) {
|
|
_this.players.push(player);
|
|
player.onDone(function () {
|
|
player.destroy();
|
|
var index = _this.players.indexOf(player);
|
|
_this.players.splice(index, 1);
|
|
});
|
|
player.play();
|
|
});
|
|
return rootPlayers;
|
|
};
|
|
TransitionAnimationEngine.prototype.elementContainsData = function (namespaceId, element) {
|
|
var containsData = false;
|
|
var details = element[REMOVAL_FLAG];
|
|
if (details && details.setForRemoval)
|
|
containsData = true;
|
|
if (this.playersByElement.has(element))
|
|
containsData = true;
|
|
if (this.playersByQueriedElement.has(element))
|
|
containsData = true;
|
|
if (this.statesByElement.has(element))
|
|
containsData = true;
|
|
return this._fetchNamespace(namespaceId).elementContainsData(element) || containsData;
|
|
};
|
|
TransitionAnimationEngine.prototype.afterFlush = function (callback) {
|
|
this._flushFns.push(callback);
|
|
};
|
|
TransitionAnimationEngine.prototype.afterFlushAnimationsDone = function (callback) {
|
|
this._whenQuietFns.push(callback);
|
|
};
|
|
TransitionAnimationEngine.prototype._getPreviousPlayers = function (element, isQueriedElement, namespaceId, triggerName, toStateValue) {
|
|
var players = [];
|
|
if (isQueriedElement) {
|
|
var queriedElementPlayers = this.playersByQueriedElement.get(element);
|
|
if (queriedElementPlayers) {
|
|
players = queriedElementPlayers;
|
|
}
|
|
}
|
|
else {
|
|
var elementPlayers = this.playersByElement.get(element);
|
|
if (elementPlayers) {
|
|
var isRemovalAnimation_1 = !toStateValue || toStateValue == VOID_VALUE;
|
|
elementPlayers.forEach(function (player) {
|
|
if (player.queued)
|
|
return;
|
|
if (!isRemovalAnimation_1 && player.triggerName != triggerName)
|
|
return;
|
|
players.push(player);
|
|
});
|
|
}
|
|
}
|
|
if (namespaceId || triggerName) {
|
|
players = players.filter(function (player) {
|
|
if (namespaceId && namespaceId != player.namespaceId)
|
|
return false;
|
|
if (triggerName && triggerName != player.triggerName)
|
|
return false;
|
|
return true;
|
|
});
|
|
}
|
|
return players;
|
|
};
|
|
TransitionAnimationEngine.prototype._beforeAnimationBuild = function (namespaceId, instruction, allPreviousPlayersMap) {
|
|
var e_1, _a;
|
|
var triggerName = instruction.triggerName;
|
|
var rootElement = instruction.element;
|
|
// when a removal animation occurs, ALL previous players are collected
|
|
// and destroyed (even if they are outside of the current namespace)
|
|
var targetNameSpaceId = instruction.isRemovalTransition ? undefined : namespaceId;
|
|
var targetTriggerName = instruction.isRemovalTransition ? undefined : triggerName;
|
|
var _loop_1 = function (timelineInstruction) {
|
|
var element = timelineInstruction.element;
|
|
var isQueriedElement = element !== rootElement;
|
|
var players = getOrSetAsInMap(allPreviousPlayersMap, element, []);
|
|
var previousPlayers = this_1._getPreviousPlayers(element, isQueriedElement, targetNameSpaceId, targetTriggerName, instruction.toState);
|
|
previousPlayers.forEach(function (player) {
|
|
var realPlayer = player.getRealPlayer();
|
|
if (realPlayer.beforeDestroy) {
|
|
realPlayer.beforeDestroy();
|
|
}
|
|
player.destroy();
|
|
players.push(player);
|
|
});
|
|
};
|
|
var this_1 = this;
|
|
try {
|
|
for (var _b = __values(instruction.timelines), _c = _b.next(); !_c.done; _c = _b.next()) {
|
|
var timelineInstruction = _c.value;
|
|
_loop_1(timelineInstruction);
|
|
}
|
|
}
|
|
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
finally {
|
|
try {
|
|
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
|
}
|
|
finally { if (e_1) throw e_1.error; }
|
|
}
|
|
// this needs to be done so that the PRE/POST styles can be
|
|
// computed properly without interfering with the previous animation
|
|
eraseStyles(rootElement, instruction.fromStyles);
|
|
};
|
|
TransitionAnimationEngine.prototype._buildAnimation = function (namespaceId, instruction, allPreviousPlayersMap, skippedPlayersMap, preStylesMap, postStylesMap) {
|
|
var _this = this;
|
|
var triggerName = instruction.triggerName;
|
|
var rootElement = instruction.element;
|
|
// we first run this so that the previous animation player
|
|
// data can be passed into the successive animation players
|
|
var allQueriedPlayers = [];
|
|
var allConsumedElements = new Set();
|
|
var allSubElements = new Set();
|
|
var allNewPlayers = instruction.timelines.map(function (timelineInstruction) {
|
|
var element = timelineInstruction.element;
|
|
allConsumedElements.add(element);
|
|
// FIXME (matsko): make sure to-be-removed animations are removed properly
|
|
var details = element[REMOVAL_FLAG];
|
|
if (details && details.removedBeforeQueried)
|
|
return new animations.NoopAnimationPlayer(timelineInstruction.duration, timelineInstruction.delay);
|
|
var isQueriedElement = element !== rootElement;
|
|
var previousPlayers = flattenGroupPlayers((allPreviousPlayersMap.get(element) || EMPTY_PLAYER_ARRAY)
|
|
.map(function (p) { return p.getRealPlayer(); }))
|
|
.filter(function (p) {
|
|
// the `element` is not apart of the AnimationPlayer definition, but
|
|
// Mock/WebAnimations
|
|
// use the element within their implementation. This will be added in Angular5 to
|
|
// AnimationPlayer
|
|
var pp = p;
|
|
return pp.element ? pp.element === element : false;
|
|
});
|
|
var preStyles = preStylesMap.get(element);
|
|
var postStyles = postStylesMap.get(element);
|
|
var keyframes = normalizeKeyframes(_this.driver, _this._normalizer, element, timelineInstruction.keyframes, preStyles, postStyles);
|
|
var player = _this._buildPlayer(timelineInstruction, keyframes, previousPlayers);
|
|
// this means that this particular player belongs to a sub trigger. It is
|
|
// important that we match this player up with the corresponding (@trigger.listener)
|
|
if (timelineInstruction.subTimeline && skippedPlayersMap) {
|
|
allSubElements.add(element);
|
|
}
|
|
if (isQueriedElement) {
|
|
var wrappedPlayer = new TransitionAnimationPlayer(namespaceId, triggerName, element);
|
|
wrappedPlayer.setRealPlayer(player);
|
|
allQueriedPlayers.push(wrappedPlayer);
|
|
}
|
|
return player;
|
|
});
|
|
allQueriedPlayers.forEach(function (player) {
|
|
getOrSetAsInMap(_this.playersByQueriedElement, player.element, []).push(player);
|
|
player.onDone(function () { return deleteOrUnsetInMap(_this.playersByQueriedElement, player.element, player); });
|
|
});
|
|
allConsumedElements.forEach(function (element) { return addClass(element, NG_ANIMATING_CLASSNAME); });
|
|
var player = optimizeGroupPlayer(allNewPlayers);
|
|
player.onDestroy(function () {
|
|
allConsumedElements.forEach(function (element) { return removeClass(element, NG_ANIMATING_CLASSNAME); });
|
|
setStyles(rootElement, instruction.toStyles);
|
|
});
|
|
// this basically makes all of the callbacks for sub element animations
|
|
// be dependent on the upper players for when they finish
|
|
allSubElements.forEach(function (element) {
|
|
getOrSetAsInMap(skippedPlayersMap, element, []).push(player);
|
|
});
|
|
return player;
|
|
};
|
|
TransitionAnimationEngine.prototype._buildPlayer = function (instruction, keyframes, previousPlayers) {
|
|
if (keyframes.length > 0) {
|
|
return this.driver.animate(instruction.element, keyframes, instruction.duration, instruction.delay, instruction.easing, previousPlayers);
|
|
}
|
|
// special case for when an empty transition|definition is provided
|
|
// ... there is no point in rendering an empty animation
|
|
return new animations.NoopAnimationPlayer(instruction.duration, instruction.delay);
|
|
};
|
|
return TransitionAnimationEngine;
|
|
}());
|
|
var TransitionAnimationPlayer = /** @class */ (function () {
|
|
function TransitionAnimationPlayer(namespaceId, triggerName, element) {
|
|
this.namespaceId = namespaceId;
|
|
this.triggerName = triggerName;
|
|
this.element = element;
|
|
this._player = new animations.NoopAnimationPlayer();
|
|
this._containsRealPlayer = false;
|
|
this._queuedCallbacks = {};
|
|
this.destroyed = false;
|
|
this.markedForDestroy = false;
|
|
this.disabled = false;
|
|
this.queued = true;
|
|
this.totalTime = 0;
|
|
}
|
|
TransitionAnimationPlayer.prototype.setRealPlayer = function (player) {
|
|
var _this = this;
|
|
if (this._containsRealPlayer)
|
|
return;
|
|
this._player = player;
|
|
Object.keys(this._queuedCallbacks).forEach(function (phase) {
|
|
_this._queuedCallbacks[phase].forEach(function (callback) { return listenOnPlayer(player, phase, undefined, callback); });
|
|
});
|
|
this._queuedCallbacks = {};
|
|
this._containsRealPlayer = true;
|
|
this.overrideTotalTime(player.totalTime);
|
|
this.queued = false;
|
|
};
|
|
TransitionAnimationPlayer.prototype.getRealPlayer = function () {
|
|
return this._player;
|
|
};
|
|
TransitionAnimationPlayer.prototype.overrideTotalTime = function (totalTime) {
|
|
this.totalTime = totalTime;
|
|
};
|
|
TransitionAnimationPlayer.prototype.syncPlayerEvents = function (player) {
|
|
var _this = this;
|
|
var p = this._player;
|
|
if (p.triggerCallback) {
|
|
player.onStart(function () { return p.triggerCallback('start'); });
|
|
}
|
|
player.onDone(function () { return _this.finish(); });
|
|
player.onDestroy(function () { return _this.destroy(); });
|
|
};
|
|
TransitionAnimationPlayer.prototype._queueEvent = function (name, callback) {
|
|
getOrSetAsInMap(this._queuedCallbacks, name, []).push(callback);
|
|
};
|
|
TransitionAnimationPlayer.prototype.onDone = function (fn) {
|
|
if (this.queued) {
|
|
this._queueEvent('done', fn);
|
|
}
|
|
this._player.onDone(fn);
|
|
};
|
|
TransitionAnimationPlayer.prototype.onStart = function (fn) {
|
|
if (this.queued) {
|
|
this._queueEvent('start', fn);
|
|
}
|
|
this._player.onStart(fn);
|
|
};
|
|
TransitionAnimationPlayer.prototype.onDestroy = function (fn) {
|
|
if (this.queued) {
|
|
this._queueEvent('destroy', fn);
|
|
}
|
|
this._player.onDestroy(fn);
|
|
};
|
|
TransitionAnimationPlayer.prototype.init = function () {
|
|
this._player.init();
|
|
};
|
|
TransitionAnimationPlayer.prototype.hasStarted = function () {
|
|
return this.queued ? false : this._player.hasStarted();
|
|
};
|
|
TransitionAnimationPlayer.prototype.play = function () {
|
|
!this.queued && this._player.play();
|
|
};
|
|
TransitionAnimationPlayer.prototype.pause = function () {
|
|
!this.queued && this._player.pause();
|
|
};
|
|
TransitionAnimationPlayer.prototype.restart = function () {
|
|
!this.queued && this._player.restart();
|
|
};
|
|
TransitionAnimationPlayer.prototype.finish = function () {
|
|
this._player.finish();
|
|
};
|
|
TransitionAnimationPlayer.prototype.destroy = function () {
|
|
this.destroyed = true;
|
|
this._player.destroy();
|
|
};
|
|
TransitionAnimationPlayer.prototype.reset = function () {
|
|
!this.queued && this._player.reset();
|
|
};
|
|
TransitionAnimationPlayer.prototype.setPosition = function (p) {
|
|
if (!this.queued) {
|
|
this._player.setPosition(p);
|
|
}
|
|
};
|
|
TransitionAnimationPlayer.prototype.getPosition = function () {
|
|
return this.queued ? 0 : this._player.getPosition();
|
|
};
|
|
/** @internal */
|
|
TransitionAnimationPlayer.prototype.triggerCallback = function (phaseName) {
|
|
var p = this._player;
|
|
if (p.triggerCallback) {
|
|
p.triggerCallback(phaseName);
|
|
}
|
|
};
|
|
return TransitionAnimationPlayer;
|
|
}());
|
|
function deleteOrUnsetInMap(map, key, value) {
|
|
var currentValues;
|
|
if (map instanceof Map) {
|
|
currentValues = map.get(key);
|
|
if (currentValues) {
|
|
if (currentValues.length) {
|
|
var index = currentValues.indexOf(value);
|
|
currentValues.splice(index, 1);
|
|
}
|
|
if (currentValues.length == 0) {
|
|
map.delete(key);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
currentValues = map[key];
|
|
if (currentValues) {
|
|
if (currentValues.length) {
|
|
var index = currentValues.indexOf(value);
|
|
currentValues.splice(index, 1);
|
|
}
|
|
if (currentValues.length == 0) {
|
|
delete map[key];
|
|
}
|
|
}
|
|
}
|
|
return currentValues;
|
|
}
|
|
function normalizeTriggerValue(value) {
|
|
// we use `!= null` here because it's the most simple
|
|
// way to test against a "falsy" value without mixing
|
|
// in empty strings or a zero value. DO NOT OPTIMIZE.
|
|
return value != null ? value : null;
|
|
}
|
|
function isElementNode(node) {
|
|
return node && node['nodeType'] === 1;
|
|
}
|
|
function isTriggerEventValid(eventName) {
|
|
return eventName == 'start' || eventName == 'done';
|
|
}
|
|
function cloakElement(element, value) {
|
|
var oldValue = element.style.display;
|
|
element.style.display = value != null ? value : 'none';
|
|
return oldValue;
|
|
}
|
|
function cloakAndComputeStyles(valuesMap, driver, elements, elementPropsMap, defaultStyle) {
|
|
var cloakVals = [];
|
|
elements.forEach(function (element) { return cloakVals.push(cloakElement(element)); });
|
|
var failedElements = [];
|
|
elementPropsMap.forEach(function (props, element) {
|
|
var styles = {};
|
|
props.forEach(function (prop) {
|
|
var value = styles[prop] = driver.computeStyle(element, prop, defaultStyle);
|
|
// there is no easy way to detect this because a sub element could be removed
|
|
// by a parent animation element being detached.
|
|
if (!value || value.length == 0) {
|
|
element[REMOVAL_FLAG] = NULL_REMOVED_QUERIED_STATE;
|
|
failedElements.push(element);
|
|
}
|
|
});
|
|
valuesMap.set(element, styles);
|
|
});
|
|
// we use a index variable here since Set.forEach(a, i) does not return
|
|
// an index value for the closure (but instead just the value)
|
|
var i = 0;
|
|
elements.forEach(function (element) { return cloakElement(element, cloakVals[i++]); });
|
|
return failedElements;
|
|
}
|
|
/*
|
|
Since the Angular renderer code will return a collection of inserted
|
|
nodes in all areas of a DOM tree, it's up to this algorithm to figure
|
|
out which nodes are roots for each animation @trigger.
|
|
|
|
By placing each inserted node into a Set and traversing upwards, it
|
|
is possible to find the @trigger elements and well any direct *star
|
|
insertion nodes, if a @trigger root is found then the enter element
|
|
is placed into the Map[@trigger] spot.
|
|
*/
|
|
function buildRootMap(roots, nodes) {
|
|
var rootMap = new Map();
|
|
roots.forEach(function (root) { return rootMap.set(root, []); });
|
|
if (nodes.length == 0)
|
|
return rootMap;
|
|
var NULL_NODE = 1;
|
|
var nodeSet = new Set(nodes);
|
|
var localRootMap = new Map();
|
|
function getRoot(node) {
|
|
if (!node)
|
|
return NULL_NODE;
|
|
var root = localRootMap.get(node);
|
|
if (root)
|
|
return root;
|
|
var parent = node.parentNode;
|
|
if (rootMap.has(parent)) { // ngIf inside @trigger
|
|
root = parent;
|
|
}
|
|
else if (nodeSet.has(parent)) { // ngIf inside ngIf
|
|
root = NULL_NODE;
|
|
}
|
|
else { // recurse upwards
|
|
root = getRoot(parent);
|
|
}
|
|
localRootMap.set(node, root);
|
|
return root;
|
|
}
|
|
nodes.forEach(function (node) {
|
|
var root = getRoot(node);
|
|
if (root !== NULL_NODE) {
|
|
rootMap.get(root).push(node);
|
|
}
|
|
});
|
|
return rootMap;
|
|
}
|
|
var CLASSES_CACHE_KEY = '$$classes';
|
|
function containsClass(element, className) {
|
|
if (element.classList) {
|
|
return element.classList.contains(className);
|
|
}
|
|
else {
|
|
var classes = element[CLASSES_CACHE_KEY];
|
|
return classes && classes[className];
|
|
}
|
|
}
|
|
function addClass(element, className) {
|
|
if (element.classList) {
|
|
element.classList.add(className);
|
|
}
|
|
else {
|
|
var classes = element[CLASSES_CACHE_KEY];
|
|
if (!classes) {
|
|
classes = element[CLASSES_CACHE_KEY] = {};
|
|
}
|
|
classes[className] = true;
|
|
}
|
|
}
|
|
function removeClass(element, className) {
|
|
if (element.classList) {
|
|
element.classList.remove(className);
|
|
}
|
|
else {
|
|
var classes = element[CLASSES_CACHE_KEY];
|
|
if (classes) {
|
|
delete classes[className];
|
|
}
|
|
}
|
|
}
|
|
function removeNodesAfterAnimationDone(engine, element, players) {
|
|
optimizeGroupPlayer(players).onDone(function () { return engine.processLeaveNode(element); });
|
|
}
|
|
function flattenGroupPlayers(players) {
|
|
var finalPlayers = [];
|
|
_flattenGroupPlayersRecur(players, finalPlayers);
|
|
return finalPlayers;
|
|
}
|
|
function _flattenGroupPlayersRecur(players, finalPlayers) {
|
|
for (var i = 0; i < players.length; i++) {
|
|
var player = players[i];
|
|
if (player instanceof animations.ɵAnimationGroupPlayer) {
|
|
_flattenGroupPlayersRecur(player.players, finalPlayers);
|
|
}
|
|
else {
|
|
finalPlayers.push(player);
|
|
}
|
|
}
|
|
}
|
|
function objEquals(a, b) {
|
|
var k1 = Object.keys(a);
|
|
var k2 = Object.keys(b);
|
|
if (k1.length != k2.length)
|
|
return false;
|
|
for (var i = 0; i < k1.length; i++) {
|
|
var prop = k1[i];
|
|
if (!b.hasOwnProperty(prop) || a[prop] !== b[prop])
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
function replacePostStylesAsPre(element, allPreStyleElements, allPostStyleElements) {
|
|
var postEntry = allPostStyleElements.get(element);
|
|
if (!postEntry)
|
|
return false;
|
|
var preEntry = allPreStyleElements.get(element);
|
|
if (preEntry) {
|
|
postEntry.forEach(function (data) { return preEntry.add(data); });
|
|
}
|
|
else {
|
|
allPreStyleElements.set(element, postEntry);
|
|
}
|
|
allPostStyleElements.delete(element);
|
|
return true;
|
|
}
|
|
|
|
var AnimationEngine = /** @class */ (function () {
|
|
function AnimationEngine(bodyNode, _driver, _normalizer) {
|
|
var _this = this;
|
|
this.bodyNode = bodyNode;
|
|
this._driver = _driver;
|
|
this._normalizer = _normalizer;
|
|
this._triggerCache = {};
|
|
// this method is designed to be overridden by the code that uses this engine
|
|
this.onRemovalComplete = function (element, context) { };
|
|
this._transitionEngine = new TransitionAnimationEngine(bodyNode, _driver, _normalizer);
|
|
this._timelineEngine = new TimelineAnimationEngine(bodyNode, _driver, _normalizer);
|
|
this._transitionEngine.onRemovalComplete = function (element, context) { return _this.onRemovalComplete(element, context); };
|
|
}
|
|
AnimationEngine.prototype.registerTrigger = function (componentId, namespaceId, hostElement, name, metadata) {
|
|
var cacheKey = componentId + '-' + name;
|
|
var trigger = this._triggerCache[cacheKey];
|
|
if (!trigger) {
|
|
var errors = [];
|
|
var ast = buildAnimationAst(this._driver, metadata, errors);
|
|
if (errors.length) {
|
|
throw new Error("The animation trigger \"" + name + "\" has failed to build due to the following errors:\n - " + errors.join('\n - '));
|
|
}
|
|
trigger = buildTrigger(name, ast, this._normalizer);
|
|
this._triggerCache[cacheKey] = trigger;
|
|
}
|
|
this._transitionEngine.registerTrigger(namespaceId, name, trigger);
|
|
};
|
|
AnimationEngine.prototype.register = function (namespaceId, hostElement) {
|
|
this._transitionEngine.register(namespaceId, hostElement);
|
|
};
|
|
AnimationEngine.prototype.destroy = function (namespaceId, context) {
|
|
this._transitionEngine.destroy(namespaceId, context);
|
|
};
|
|
AnimationEngine.prototype.onInsert = function (namespaceId, element, parent, insertBefore) {
|
|
this._transitionEngine.insertNode(namespaceId, element, parent, insertBefore);
|
|
};
|
|
AnimationEngine.prototype.onRemove = function (namespaceId, element, context, isHostElement) {
|
|
this._transitionEngine.removeNode(namespaceId, element, isHostElement || false, context);
|
|
};
|
|
AnimationEngine.prototype.disableAnimations = function (element, disable) {
|
|
this._transitionEngine.markElementAsDisabled(element, disable);
|
|
};
|
|
AnimationEngine.prototype.process = function (namespaceId, element, property, value) {
|
|
if (property.charAt(0) == '@') {
|
|
var _a = __read(parseTimelineCommand(property), 2), id = _a[0], action = _a[1];
|
|
var args = value;
|
|
this._timelineEngine.command(id, element, action, args);
|
|
}
|
|
else {
|
|
this._transitionEngine.trigger(namespaceId, element, property, value);
|
|
}
|
|
};
|
|
AnimationEngine.prototype.listen = function (namespaceId, element, eventName, eventPhase, callback) {
|
|
// @@listen
|
|
if (eventName.charAt(0) == '@') {
|
|
var _a = __read(parseTimelineCommand(eventName), 2), id = _a[0], action = _a[1];
|
|
return this._timelineEngine.listen(id, element, action, callback);
|
|
}
|
|
return this._transitionEngine.listen(namespaceId, element, eventName, eventPhase, callback);
|
|
};
|
|
AnimationEngine.prototype.flush = function (microtaskId) {
|
|
if (microtaskId === void 0) { microtaskId = -1; }
|
|
this._transitionEngine.flush(microtaskId);
|
|
};
|
|
Object.defineProperty(AnimationEngine.prototype, "players", {
|
|
get: function () {
|
|
return this._transitionEngine.players
|
|
.concat(this._timelineEngine.players);
|
|
},
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
AnimationEngine.prototype.whenRenderingDone = function () {
|
|
return this._transitionEngine.whenRenderingDone();
|
|
};
|
|
return AnimationEngine;
|
|
}());
|
|
|
|
/**
|
|
* @license
|
|
* Copyright Google LLC All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://angular.io/license
|
|
*/
|
|
/**
|
|
* Returns an instance of `SpecialCasedStyles` if and when any special (non animateable) styles are
|
|
* detected.
|
|
*
|
|
* In CSS there exist properties that cannot be animated within a keyframe animation
|
|
* (whether it be via CSS keyframes or web-animations) and the animation implementation
|
|
* will ignore them. This function is designed to detect those special cased styles and
|
|
* return a container that will be executed at the start and end of the animation.
|
|
*
|
|
* @returns an instance of `SpecialCasedStyles` if any special styles are detected otherwise `null`
|
|
*/
|
|
function packageNonAnimatableStyles(element, styles) {
|
|
var startStyles = null;
|
|
var endStyles = null;
|
|
if (Array.isArray(styles) && styles.length) {
|
|
startStyles = filterNonAnimatableStyles(styles[0]);
|
|
if (styles.length > 1) {
|
|
endStyles = filterNonAnimatableStyles(styles[styles.length - 1]);
|
|
}
|
|
}
|
|
else if (styles) {
|
|
startStyles = filterNonAnimatableStyles(styles);
|
|
}
|
|
return (startStyles || endStyles) ? new SpecialCasedStyles(element, startStyles, endStyles) :
|
|
null;
|
|
}
|
|
/**
|
|
* Designed to be executed during a keyframe-based animation to apply any special-cased styles.
|
|
*
|
|
* When started (when the `start()` method is run) then the provided `startStyles`
|
|
* will be applied. When finished (when the `finish()` method is called) the
|
|
* `endStyles` will be applied as well any any starting styles. Finally when
|
|
* `destroy()` is called then all styles will be removed.
|
|
*/
|
|
var SpecialCasedStyles = /** @class */ (function () {
|
|
function SpecialCasedStyles(_element, _startStyles, _endStyles) {
|
|
this._element = _element;
|
|
this._startStyles = _startStyles;
|
|
this._endStyles = _endStyles;
|
|
this._state = 0 /* Pending */;
|
|
var initialStyles = SpecialCasedStyles.initialStylesByElement.get(_element);
|
|
if (!initialStyles) {
|
|
SpecialCasedStyles.initialStylesByElement.set(_element, initialStyles = {});
|
|
}
|
|
this._initialStyles = initialStyles;
|
|
}
|
|
SpecialCasedStyles.prototype.start = function () {
|
|
if (this._state < 1 /* Started */) {
|
|
if (this._startStyles) {
|
|
setStyles(this._element, this._startStyles, this._initialStyles);
|
|
}
|
|
this._state = 1 /* Started */;
|
|
}
|
|
};
|
|
SpecialCasedStyles.prototype.finish = function () {
|
|
this.start();
|
|
if (this._state < 2 /* Finished */) {
|
|
setStyles(this._element, this._initialStyles);
|
|
if (this._endStyles) {
|
|
setStyles(this._element, this._endStyles);
|
|
this._endStyles = null;
|
|
}
|
|
this._state = 1 /* Started */;
|
|
}
|
|
};
|
|
SpecialCasedStyles.prototype.destroy = function () {
|
|
this.finish();
|
|
if (this._state < 3 /* Destroyed */) {
|
|
SpecialCasedStyles.initialStylesByElement.delete(this._element);
|
|
if (this._startStyles) {
|
|
eraseStyles(this._element, this._startStyles);
|
|
this._endStyles = null;
|
|
}
|
|
if (this._endStyles) {
|
|
eraseStyles(this._element, this._endStyles);
|
|
this._endStyles = null;
|
|
}
|
|
setStyles(this._element, this._initialStyles);
|
|
this._state = 3 /* Destroyed */;
|
|
}
|
|
};
|
|
return SpecialCasedStyles;
|
|
}());
|
|
SpecialCasedStyles.initialStylesByElement = ( /* @__PURE__ */new WeakMap());
|
|
function filterNonAnimatableStyles(styles) {
|
|
var result = null;
|
|
var props = Object.keys(styles);
|
|
for (var i = 0; i < props.length; i++) {
|
|
var prop = props[i];
|
|
if (isNonAnimatableStyle(prop)) {
|
|
result = result || {};
|
|
result[prop] = styles[prop];
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
function isNonAnimatableStyle(prop) {
|
|
return prop === 'display' || prop === 'position';
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright Google LLC All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://angular.io/license
|
|
*/
|
|
var ELAPSED_TIME_MAX_DECIMAL_PLACES = 3;
|
|
var ANIMATION_PROP = 'animation';
|
|
var ANIMATIONEND_EVENT = 'animationend';
|
|
var ONE_SECOND$1 = 1000;
|
|
var ElementAnimationStyleHandler = /** @class */ (function () {
|
|
function ElementAnimationStyleHandler(_element, _name, _duration, _delay, _easing, _fillMode, _onDoneFn) {
|
|
var _this = this;
|
|
this._element = _element;
|
|
this._name = _name;
|
|
this._duration = _duration;
|
|
this._delay = _delay;
|
|
this._easing = _easing;
|
|
this._fillMode = _fillMode;
|
|
this._onDoneFn = _onDoneFn;
|
|
this._finished = false;
|
|
this._destroyed = false;
|
|
this._startTime = 0;
|
|
this._position = 0;
|
|
this._eventFn = function (e) { return _this._handleCallback(e); };
|
|
}
|
|
ElementAnimationStyleHandler.prototype.apply = function () {
|
|
applyKeyframeAnimation(this._element, this._duration + "ms " + this._easing + " " + this._delay + "ms 1 normal " + this._fillMode + " " + this._name);
|
|
addRemoveAnimationEvent(this._element, this._eventFn, false);
|
|
this._startTime = Date.now();
|
|
};
|
|
ElementAnimationStyleHandler.prototype.pause = function () {
|
|
playPauseAnimation(this._element, this._name, 'paused');
|
|
};
|
|
ElementAnimationStyleHandler.prototype.resume = function () {
|
|
playPauseAnimation(this._element, this._name, 'running');
|
|
};
|
|
ElementAnimationStyleHandler.prototype.setPosition = function (position) {
|
|
var index = findIndexForAnimation(this._element, this._name);
|
|
this._position = position * this._duration;
|
|
setAnimationStyle(this._element, 'Delay', "-" + this._position + "ms", index);
|
|
};
|
|
ElementAnimationStyleHandler.prototype.getPosition = function () {
|
|
return this._position;
|
|
};
|
|
ElementAnimationStyleHandler.prototype._handleCallback = function (event) {
|
|
var timestamp = event._ngTestManualTimestamp || Date.now();
|
|
var elapsedTime = parseFloat(event.elapsedTime.toFixed(ELAPSED_TIME_MAX_DECIMAL_PLACES)) * ONE_SECOND$1;
|
|
if (event.animationName == this._name &&
|
|
Math.max(timestamp - this._startTime, 0) >= this._delay && elapsedTime >= this._duration) {
|
|
this.finish();
|
|
}
|
|
};
|
|
ElementAnimationStyleHandler.prototype.finish = function () {
|
|
if (this._finished)
|
|
return;
|
|
this._finished = true;
|
|
this._onDoneFn();
|
|
addRemoveAnimationEvent(this._element, this._eventFn, true);
|
|
};
|
|
ElementAnimationStyleHandler.prototype.destroy = function () {
|
|
if (this._destroyed)
|
|
return;
|
|
this._destroyed = true;
|
|
this.finish();
|
|
removeKeyframeAnimation(this._element, this._name);
|
|
};
|
|
return ElementAnimationStyleHandler;
|
|
}());
|
|
function playPauseAnimation(element, name, status) {
|
|
var index = findIndexForAnimation(element, name);
|
|
setAnimationStyle(element, 'PlayState', status, index);
|
|
}
|
|
function applyKeyframeAnimation(element, value) {
|
|
var anim = getAnimationStyle(element, '').trim();
|
|
var index = 0;
|
|
if (anim.length) {
|
|
index = countChars(anim, ',') + 1;
|
|
value = anim + ", " + value;
|
|
}
|
|
setAnimationStyle(element, '', value);
|
|
return index;
|
|
}
|
|
function removeKeyframeAnimation(element, name) {
|
|
var anim = getAnimationStyle(element, '');
|
|
var tokens = anim.split(',');
|
|
var index = findMatchingTokenIndex(tokens, name);
|
|
if (index >= 0) {
|
|
tokens.splice(index, 1);
|
|
var newValue = tokens.join(',');
|
|
setAnimationStyle(element, '', newValue);
|
|
}
|
|
}
|
|
function findIndexForAnimation(element, value) {
|
|
var anim = getAnimationStyle(element, '');
|
|
if (anim.indexOf(',') > 0) {
|
|
var tokens = anim.split(',');
|
|
return findMatchingTokenIndex(tokens, value);
|
|
}
|
|
return findMatchingTokenIndex([anim], value);
|
|
}
|
|
function findMatchingTokenIndex(tokens, searchToken) {
|
|
for (var i = 0; i < tokens.length; i++) {
|
|
if (tokens[i].indexOf(searchToken) >= 0) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
function addRemoveAnimationEvent(element, fn, doRemove) {
|
|
doRemove ? element.removeEventListener(ANIMATIONEND_EVENT, fn) :
|
|
element.addEventListener(ANIMATIONEND_EVENT, fn);
|
|
}
|
|
function setAnimationStyle(element, name, value, index) {
|
|
var prop = ANIMATION_PROP + name;
|
|
if (index != null) {
|
|
var oldValue = element.style[prop];
|
|
if (oldValue.length) {
|
|
var tokens = oldValue.split(',');
|
|
tokens[index] = value;
|
|
value = tokens.join(',');
|
|
}
|
|
}
|
|
element.style[prop] = value;
|
|
}
|
|
function getAnimationStyle(element, name) {
|
|
return element.style[ANIMATION_PROP + name] || '';
|
|
}
|
|
function countChars(value, char) {
|
|
var count = 0;
|
|
for (var i = 0; i < value.length; i++) {
|
|
var c = value.charAt(i);
|
|
if (c === char)
|
|
count++;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
var DEFAULT_FILL_MODE = 'forwards';
|
|
var DEFAULT_EASING = 'linear';
|
|
var CssKeyframesPlayer = /** @class */ (function () {
|
|
function CssKeyframesPlayer(element, keyframes, animationName, _duration, _delay, easing, _finalStyles, _specialStyles) {
|
|
this.element = element;
|
|
this.keyframes = keyframes;
|
|
this.animationName = animationName;
|
|
this._duration = _duration;
|
|
this._delay = _delay;
|
|
this._finalStyles = _finalStyles;
|
|
this._specialStyles = _specialStyles;
|
|
this._onDoneFns = [];
|
|
this._onStartFns = [];
|
|
this._onDestroyFns = [];
|
|
this.currentSnapshot = {};
|
|
this._state = 0;
|
|
this.easing = easing || DEFAULT_EASING;
|
|
this.totalTime = _duration + _delay;
|
|
this._buildStyler();
|
|
}
|
|
CssKeyframesPlayer.prototype.onStart = function (fn) {
|
|
this._onStartFns.push(fn);
|
|
};
|
|
CssKeyframesPlayer.prototype.onDone = function (fn) {
|
|
this._onDoneFns.push(fn);
|
|
};
|
|
CssKeyframesPlayer.prototype.onDestroy = function (fn) {
|
|
this._onDestroyFns.push(fn);
|
|
};
|
|
CssKeyframesPlayer.prototype.destroy = function () {
|
|
this.init();
|
|
if (this._state >= 4 /* DESTROYED */)
|
|
return;
|
|
this._state = 4 /* DESTROYED */;
|
|
this._styler.destroy();
|
|
this._flushStartFns();
|
|
this._flushDoneFns();
|
|
if (this._specialStyles) {
|
|
this._specialStyles.destroy();
|
|
}
|
|
this._onDestroyFns.forEach(function (fn) { return fn(); });
|
|
this._onDestroyFns = [];
|
|
};
|
|
CssKeyframesPlayer.prototype._flushDoneFns = function () {
|
|
this._onDoneFns.forEach(function (fn) { return fn(); });
|
|
this._onDoneFns = [];
|
|
};
|
|
CssKeyframesPlayer.prototype._flushStartFns = function () {
|
|
this._onStartFns.forEach(function (fn) { return fn(); });
|
|
this._onStartFns = [];
|
|
};
|
|
CssKeyframesPlayer.prototype.finish = function () {
|
|
this.init();
|
|
if (this._state >= 3 /* FINISHED */)
|
|
return;
|
|
this._state = 3 /* FINISHED */;
|
|
this._styler.finish();
|
|
this._flushStartFns();
|
|
if (this._specialStyles) {
|
|
this._specialStyles.finish();
|
|
}
|
|
this._flushDoneFns();
|
|
};
|
|
CssKeyframesPlayer.prototype.setPosition = function (value) {
|
|
this._styler.setPosition(value);
|
|
};
|
|
CssKeyframesPlayer.prototype.getPosition = function () {
|
|
return this._styler.getPosition();
|
|
};
|
|
CssKeyframesPlayer.prototype.hasStarted = function () {
|
|
return this._state >= 2 /* STARTED */;
|
|
};
|
|
CssKeyframesPlayer.prototype.init = function () {
|
|
if (this._state >= 1 /* INITIALIZED */)
|
|
return;
|
|
this._state = 1 /* INITIALIZED */;
|
|
var elm = this.element;
|
|
this._styler.apply();
|
|
if (this._delay) {
|
|
this._styler.pause();
|
|
}
|
|
};
|
|
CssKeyframesPlayer.prototype.play = function () {
|
|
this.init();
|
|
if (!this.hasStarted()) {
|
|
this._flushStartFns();
|
|
this._state = 2 /* STARTED */;
|
|
if (this._specialStyles) {
|
|
this._specialStyles.start();
|
|
}
|
|
}
|
|
this._styler.resume();
|
|
};
|
|
CssKeyframesPlayer.prototype.pause = function () {
|
|
this.init();
|
|
this._styler.pause();
|
|
};
|
|
CssKeyframesPlayer.prototype.restart = function () {
|
|
this.reset();
|
|
this.play();
|
|
};
|
|
CssKeyframesPlayer.prototype.reset = function () {
|
|
this._state = 0 /* RESET */;
|
|
this._styler.destroy();
|
|
this._buildStyler();
|
|
this._styler.apply();
|
|
};
|
|
CssKeyframesPlayer.prototype._buildStyler = function () {
|
|
var _this = this;
|
|
this._styler = new ElementAnimationStyleHandler(this.element, this.animationName, this._duration, this._delay, this.easing, DEFAULT_FILL_MODE, function () { return _this.finish(); });
|
|
};
|
|
/** @internal */
|
|
CssKeyframesPlayer.prototype.triggerCallback = function (phaseName) {
|
|
var methods = phaseName == 'start' ? this._onStartFns : this._onDoneFns;
|
|
methods.forEach(function (fn) { return fn(); });
|
|
methods.length = 0;
|
|
};
|
|
CssKeyframesPlayer.prototype.beforeDestroy = function () {
|
|
var _this = this;
|
|
this.init();
|
|
var styles = {};
|
|
if (this.hasStarted()) {
|
|
var finished_1 = this._state >= 3 /* FINISHED */;
|
|
Object.keys(this._finalStyles).forEach(function (prop) {
|
|
if (prop != 'offset') {
|
|
styles[prop] = finished_1 ? _this._finalStyles[prop] : computeStyle(_this.element, prop);
|
|
}
|
|
});
|
|
}
|
|
this.currentSnapshot = styles;
|
|
};
|
|
return CssKeyframesPlayer;
|
|
}());
|
|
|
|
var DirectStylePlayer = /** @class */ (function (_super) {
|
|
__extends(DirectStylePlayer, _super);
|
|
function DirectStylePlayer(element, styles) {
|
|
var _this = _super.call(this) || this;
|
|
_this.element = element;
|
|
_this._startingStyles = {};
|
|
_this.__initialized = false;
|
|
_this._styles = hypenatePropsObject(styles);
|
|
return _this;
|
|
}
|
|
DirectStylePlayer.prototype.init = function () {
|
|
var _this = this;
|
|
if (this.__initialized || !this._startingStyles)
|
|
return;
|
|
this.__initialized = true;
|
|
Object.keys(this._styles).forEach(function (prop) {
|
|
_this._startingStyles[prop] = _this.element.style[prop];
|
|
});
|
|
_super.prototype.init.call(this);
|
|
};
|
|
DirectStylePlayer.prototype.play = function () {
|
|
var _this = this;
|
|
if (!this._startingStyles)
|
|
return;
|
|
this.init();
|
|
Object.keys(this._styles)
|
|
.forEach(function (prop) { return _this.element.style.setProperty(prop, _this._styles[prop]); });
|
|
_super.prototype.play.call(this);
|
|
};
|
|
DirectStylePlayer.prototype.destroy = function () {
|
|
var _this = this;
|
|
if (!this._startingStyles)
|
|
return;
|
|
Object.keys(this._startingStyles).forEach(function (prop) {
|
|
var value = _this._startingStyles[prop];
|
|
if (value) {
|
|
_this.element.style.setProperty(prop, value);
|
|
}
|
|
else {
|
|
_this.element.style.removeProperty(prop);
|
|
}
|
|
});
|
|
this._startingStyles = null;
|
|
_super.prototype.destroy.call(this);
|
|
};
|
|
return DirectStylePlayer;
|
|
}(animations.NoopAnimationPlayer));
|
|
|
|
var KEYFRAMES_NAME_PREFIX = 'gen_css_kf_';
|
|
var TAB_SPACE = ' ';
|
|
var CssKeyframesDriver = /** @class */ (function () {
|
|
function CssKeyframesDriver() {
|
|
this._count = 0;
|
|
}
|
|
CssKeyframesDriver.prototype.validateStyleProperty = function (prop) {
|
|
return validateStyleProperty(prop);
|
|
};
|
|
CssKeyframesDriver.prototype.matchesElement = function (element, selector) {
|
|
return matchesElement(element, selector);
|
|
};
|
|
CssKeyframesDriver.prototype.containsElement = function (elm1, elm2) {
|
|
return containsElement(elm1, elm2);
|
|
};
|
|
CssKeyframesDriver.prototype.query = function (element, selector, multi) {
|
|
return invokeQuery(element, selector, multi);
|
|
};
|
|
CssKeyframesDriver.prototype.computeStyle = function (element, prop, defaultValue) {
|
|
return window.getComputedStyle(element)[prop];
|
|
};
|
|
CssKeyframesDriver.prototype.buildKeyframeElement = function (element, name, keyframes) {
|
|
keyframes = keyframes.map(function (kf) { return hypenatePropsObject(kf); });
|
|
var keyframeStr = "@keyframes " + name + " {\n";
|
|
var tab = '';
|
|
keyframes.forEach(function (kf) {
|
|
tab = TAB_SPACE;
|
|
var offset = parseFloat(kf['offset']);
|
|
keyframeStr += "" + tab + offset * 100 + "% {\n";
|
|
tab += TAB_SPACE;
|
|
Object.keys(kf).forEach(function (prop) {
|
|
var value = kf[prop];
|
|
switch (prop) {
|
|
case 'offset':
|
|
return;
|
|
case 'easing':
|
|
if (value) {
|
|
keyframeStr += tab + "animation-timing-function: " + value + ";\n";
|
|
}
|
|
return;
|
|
default:
|
|
keyframeStr += "" + tab + prop + ": " + value + ";\n";
|
|
return;
|
|
}
|
|
});
|
|
keyframeStr += tab + "}\n";
|
|
});
|
|
keyframeStr += "}\n";
|
|
var kfElm = document.createElement('style');
|
|
kfElm.textContent = keyframeStr;
|
|
return kfElm;
|
|
};
|
|
CssKeyframesDriver.prototype.animate = function (element, keyframes, duration, delay, easing, previousPlayers, scrubberAccessRequested) {
|
|
if (previousPlayers === void 0) { previousPlayers = []; }
|
|
if ((typeof ngDevMode === 'undefined' || ngDevMode) && scrubberAccessRequested) {
|
|
notifyFaultyScrubber();
|
|
}
|
|
var previousCssKeyframePlayers = previousPlayers.filter(function (player) { return player instanceof CssKeyframesPlayer; });
|
|
var previousStyles = {};
|
|
if (allowPreviousPlayerStylesMerge(duration, delay)) {
|
|
previousCssKeyframePlayers.forEach(function (player) {
|
|
var styles = player.currentSnapshot;
|
|
Object.keys(styles).forEach(function (prop) { return previousStyles[prop] = styles[prop]; });
|
|
});
|
|
}
|
|
keyframes = balancePreviousStylesIntoKeyframes(element, keyframes, previousStyles);
|
|
var finalStyles = flattenKeyframesIntoStyles(keyframes);
|
|
// if there is no animation then there is no point in applying
|
|
// styles and waiting for an event to get fired. This causes lag.
|
|
// It's better to just directly apply the styles to the element
|
|
// via the direct styling animation player.
|
|
if (duration == 0) {
|
|
return new DirectStylePlayer(element, finalStyles);
|
|
}
|
|
var animationName = "" + KEYFRAMES_NAME_PREFIX + this._count++;
|
|
var kfElm = this.buildKeyframeElement(element, animationName, keyframes);
|
|
var nodeToAppendKfElm = findNodeToAppendKeyframeElement(element);
|
|
nodeToAppendKfElm.appendChild(kfElm);
|
|
var specialStyles = packageNonAnimatableStyles(element, keyframes);
|
|
var player = new CssKeyframesPlayer(element, keyframes, animationName, duration, delay, easing, finalStyles, specialStyles);
|
|
player.onDestroy(function () { return removeElement(kfElm); });
|
|
return player;
|
|
};
|
|
return CssKeyframesDriver;
|
|
}());
|
|
function findNodeToAppendKeyframeElement(element) {
|
|
var _a;
|
|
var rootNode = (_a = element.getRootNode) === null || _a === void 0 ? void 0 : _a.call(element);
|
|
if (typeof ShadowRoot !== 'undefined' && rootNode instanceof ShadowRoot) {
|
|
return rootNode;
|
|
}
|
|
return document.head;
|
|
}
|
|
function flattenKeyframesIntoStyles(keyframes) {
|
|
var flatKeyframes = {};
|
|
if (keyframes) {
|
|
var kfs = Array.isArray(keyframes) ? keyframes : [keyframes];
|
|
kfs.forEach(function (kf) {
|
|
Object.keys(kf).forEach(function (prop) {
|
|
if (prop == 'offset' || prop == 'easing')
|
|
return;
|
|
flatKeyframes[prop] = kf[prop];
|
|
});
|
|
});
|
|
}
|
|
return flatKeyframes;
|
|
}
|
|
function removeElement(node) {
|
|
node.parentNode.removeChild(node);
|
|
}
|
|
var warningIssued = false;
|
|
function notifyFaultyScrubber() {
|
|
if (warningIssued)
|
|
return;
|
|
console.warn('@angular/animations: please load the web-animations.js polyfill to allow programmatic access...\n', ' visit https://bit.ly/IWukam to learn more about using the web-animation-js polyfill.');
|
|
warningIssued = true;
|
|
}
|
|
|
|
var WebAnimationsPlayer = /** @class */ (function () {
|
|
function WebAnimationsPlayer(element, keyframes, options, _specialStyles) {
|
|
this.element = element;
|
|
this.keyframes = keyframes;
|
|
this.options = options;
|
|
this._specialStyles = _specialStyles;
|
|
this._onDoneFns = [];
|
|
this._onStartFns = [];
|
|
this._onDestroyFns = [];
|
|
this._initialized = false;
|
|
this._finished = false;
|
|
this._started = false;
|
|
this._destroyed = false;
|
|
this.time = 0;
|
|
this.parentPlayer = null;
|
|
this.currentSnapshot = {};
|
|
this._duration = options['duration'];
|
|
this._delay = options['delay'] || 0;
|
|
this.time = this._duration + this._delay;
|
|
}
|
|
WebAnimationsPlayer.prototype._onFinish = function () {
|
|
if (!this._finished) {
|
|
this._finished = true;
|
|
this._onDoneFns.forEach(function (fn) { return fn(); });
|
|
this._onDoneFns = [];
|
|
}
|
|
};
|
|
WebAnimationsPlayer.prototype.init = function () {
|
|
this._buildPlayer();
|
|
this._preparePlayerBeforeStart();
|
|
};
|
|
WebAnimationsPlayer.prototype._buildPlayer = function () {
|
|
var _this = this;
|
|
if (this._initialized)
|
|
return;
|
|
this._initialized = true;
|
|
var keyframes = this.keyframes;
|
|
this.domPlayer =
|
|
this._triggerWebAnimation(this.element, keyframes, this.options);
|
|
this._finalKeyframe = keyframes.length ? keyframes[keyframes.length - 1] : {};
|
|
this.domPlayer.addEventListener('finish', function () { return _this._onFinish(); });
|
|
};
|
|
WebAnimationsPlayer.prototype._preparePlayerBeforeStart = function () {
|
|
// this is required so that the player doesn't start to animate right away
|
|
if (this._delay) {
|
|
this._resetDomPlayerState();
|
|
}
|
|
else {
|
|
this.domPlayer.pause();
|
|
}
|
|
};
|
|
/** @internal */
|
|
WebAnimationsPlayer.prototype._triggerWebAnimation = function (element, keyframes, options) {
|
|
// jscompiler doesn't seem to know animate is a native property because it's not fully
|
|
// supported yet across common browsers (we polyfill it for Edge/Safari) [CL #143630929]
|
|
return element['animate'](keyframes, options);
|
|
};
|
|
WebAnimationsPlayer.prototype.onStart = function (fn) {
|
|
this._onStartFns.push(fn);
|
|
};
|
|
WebAnimationsPlayer.prototype.onDone = function (fn) {
|
|
this._onDoneFns.push(fn);
|
|
};
|
|
WebAnimationsPlayer.prototype.onDestroy = function (fn) {
|
|
this._onDestroyFns.push(fn);
|
|
};
|
|
WebAnimationsPlayer.prototype.play = function () {
|
|
this._buildPlayer();
|
|
if (!this.hasStarted()) {
|
|
this._onStartFns.forEach(function (fn) { return fn(); });
|
|
this._onStartFns = [];
|
|
this._started = true;
|
|
if (this._specialStyles) {
|
|
this._specialStyles.start();
|
|
}
|
|
}
|
|
this.domPlayer.play();
|
|
};
|
|
WebAnimationsPlayer.prototype.pause = function () {
|
|
this.init();
|
|
this.domPlayer.pause();
|
|
};
|
|
WebAnimationsPlayer.prototype.finish = function () {
|
|
this.init();
|
|
if (this._specialStyles) {
|
|
this._specialStyles.finish();
|
|
}
|
|
this._onFinish();
|
|
this.domPlayer.finish();
|
|
};
|
|
WebAnimationsPlayer.prototype.reset = function () {
|
|
this._resetDomPlayerState();
|
|
this._destroyed = false;
|
|
this._finished = false;
|
|
this._started = false;
|
|
};
|
|
WebAnimationsPlayer.prototype._resetDomPlayerState = function () {
|
|
if (this.domPlayer) {
|
|
this.domPlayer.cancel();
|
|
}
|
|
};
|
|
WebAnimationsPlayer.prototype.restart = function () {
|
|
this.reset();
|
|
this.play();
|
|
};
|
|
WebAnimationsPlayer.prototype.hasStarted = function () {
|
|
return this._started;
|
|
};
|
|
WebAnimationsPlayer.prototype.destroy = function () {
|
|
if (!this._destroyed) {
|
|
this._destroyed = true;
|
|
this._resetDomPlayerState();
|
|
this._onFinish();
|
|
if (this._specialStyles) {
|
|
this._specialStyles.destroy();
|
|
}
|
|
this._onDestroyFns.forEach(function (fn) { return fn(); });
|
|
this._onDestroyFns = [];
|
|
}
|
|
};
|
|
WebAnimationsPlayer.prototype.setPosition = function (p) {
|
|
if (this.domPlayer === undefined) {
|
|
this.init();
|
|
}
|
|
this.domPlayer.currentTime = p * this.time;
|
|
};
|
|
WebAnimationsPlayer.prototype.getPosition = function () {
|
|
return this.domPlayer.currentTime / this.time;
|
|
};
|
|
Object.defineProperty(WebAnimationsPlayer.prototype, "totalTime", {
|
|
get: function () {
|
|
return this._delay + this._duration;
|
|
},
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
WebAnimationsPlayer.prototype.beforeDestroy = function () {
|
|
var _this = this;
|
|
var styles = {};
|
|
if (this.hasStarted()) {
|
|
Object.keys(this._finalKeyframe).forEach(function (prop) {
|
|
if (prop != 'offset') {
|
|
styles[prop] =
|
|
_this._finished ? _this._finalKeyframe[prop] : computeStyle(_this.element, prop);
|
|
}
|
|
});
|
|
}
|
|
this.currentSnapshot = styles;
|
|
};
|
|
/** @internal */
|
|
WebAnimationsPlayer.prototype.triggerCallback = function (phaseName) {
|
|
var methods = phaseName == 'start' ? this._onStartFns : this._onDoneFns;
|
|
methods.forEach(function (fn) { return fn(); });
|
|
methods.length = 0;
|
|
};
|
|
return WebAnimationsPlayer;
|
|
}());
|
|
|
|
var WebAnimationsDriver = /** @class */ (function () {
|
|
function WebAnimationsDriver() {
|
|
this._isNativeImpl = /\{\s*\[native\s+code\]\s*\}/.test(getElementAnimateFn().toString());
|
|
this._cssKeyframesDriver = new CssKeyframesDriver();
|
|
}
|
|
WebAnimationsDriver.prototype.validateStyleProperty = function (prop) {
|
|
return validateStyleProperty(prop);
|
|
};
|
|
WebAnimationsDriver.prototype.matchesElement = function (element, selector) {
|
|
return matchesElement(element, selector);
|
|
};
|
|
WebAnimationsDriver.prototype.containsElement = function (elm1, elm2) {
|
|
return containsElement(elm1, elm2);
|
|
};
|
|
WebAnimationsDriver.prototype.query = function (element, selector, multi) {
|
|
return invokeQuery(element, selector, multi);
|
|
};
|
|
WebAnimationsDriver.prototype.computeStyle = function (element, prop, defaultValue) {
|
|
return window.getComputedStyle(element)[prop];
|
|
};
|
|
WebAnimationsDriver.prototype.overrideWebAnimationsSupport = function (supported) {
|
|
this._isNativeImpl = supported;
|
|
};
|
|
WebAnimationsDriver.prototype.animate = function (element, keyframes, duration, delay, easing, previousPlayers, scrubberAccessRequested) {
|
|
if (previousPlayers === void 0) { previousPlayers = []; }
|
|
var useKeyframes = !scrubberAccessRequested && !this._isNativeImpl;
|
|
if (useKeyframes) {
|
|
return this._cssKeyframesDriver.animate(element, keyframes, duration, delay, easing, previousPlayers);
|
|
}
|
|
var fill = delay == 0 ? 'both' : 'forwards';
|
|
var playerOptions = { duration: duration, delay: delay, fill: fill };
|
|
// we check for this to avoid having a null|undefined value be present
|
|
// for the easing (which results in an error for certain browsers #9752)
|
|
if (easing) {
|
|
playerOptions['easing'] = easing;
|
|
}
|
|
var previousStyles = {};
|
|
var previousWebAnimationPlayers = previousPlayers.filter(function (player) { return player instanceof WebAnimationsPlayer; });
|
|
if (allowPreviousPlayerStylesMerge(duration, delay)) {
|
|
previousWebAnimationPlayers.forEach(function (player) {
|
|
var styles = player.currentSnapshot;
|
|
Object.keys(styles).forEach(function (prop) { return previousStyles[prop] = styles[prop]; });
|
|
});
|
|
}
|
|
keyframes = keyframes.map(function (styles) { return copyStyles(styles, false); });
|
|
keyframes = balancePreviousStylesIntoKeyframes(element, keyframes, previousStyles);
|
|
var specialStyles = packageNonAnimatableStyles(element, keyframes);
|
|
return new WebAnimationsPlayer(element, keyframes, playerOptions, specialStyles);
|
|
};
|
|
return WebAnimationsDriver;
|
|
}());
|
|
function supportsWebAnimations() {
|
|
return typeof getElementAnimateFn() === 'function';
|
|
}
|
|
function getElementAnimateFn() {
|
|
return (isBrowser() && Element.prototype['animate']) || {};
|
|
}
|
|
|
|
/**
|
|
* @license
|
|
* Copyright Google LLC All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://angular.io/license
|
|
*/
|
|
|
|
/**
|
|
* @license
|
|
* Copyright Google LLC All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://angular.io/license
|
|
*/
|
|
|
|
/**
|
|
* @license
|
|
* Copyright Google LLC All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://angular.io/license
|
|
*/
|
|
|
|
/**
|
|
* @license
|
|
* Copyright Google LLC All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://angular.io/license
|
|
*/
|
|
|
|
/**
|
|
* Generated bundle index. Do not edit.
|
|
*/
|
|
|
|
exports.AnimationDriver = AnimationDriver;
|
|
exports.ɵAnimation = Animation;
|
|
exports.ɵAnimationEngine = AnimationEngine;
|
|
exports.ɵAnimationStyleNormalizer = AnimationStyleNormalizer;
|
|
exports.ɵCssKeyframesDriver = CssKeyframesDriver;
|
|
exports.ɵCssKeyframesPlayer = CssKeyframesPlayer;
|
|
exports.ɵNoopAnimationDriver = NoopAnimationDriver;
|
|
exports.ɵNoopAnimationStyleNormalizer = NoopAnimationStyleNormalizer;
|
|
exports.ɵWebAnimationsDriver = WebAnimationsDriver;
|
|
exports.ɵWebAnimationsPlayer = WebAnimationsPlayer;
|
|
exports.ɵWebAnimationsStyleNormalizer = WebAnimationsStyleNormalizer;
|
|
exports.ɵallowPreviousPlayerStylesMerge = allowPreviousPlayerStylesMerge;
|
|
exports.ɵangular_packages_animations_browser_browser_a = SpecialCasedStyles;
|
|
exports.ɵcontainsElement = containsElement;
|
|
exports.ɵinvokeQuery = invokeQuery;
|
|
exports.ɵmatchesElement = matchesElement;
|
|
exports.ɵsupportsWebAnimations = supportsWebAnimations;
|
|
exports.ɵvalidateStyleProperty = validateStyleProperty;
|
|
|
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
|
})));
|
|
//# sourceMappingURL=animations-browser.umd.js.map
|