98
modules/@angular/compiler/src/animation/animation_ast.ts
Normal file
98
modules/@angular/compiler/src/animation/animation_ast.ts
Normal file
@ -0,0 +1,98 @@
|
||||
export abstract class AnimationAst {
|
||||
public startTime: number = 0;
|
||||
public playTime: number = 0;
|
||||
abstract visit(visitor: AnimationAstVisitor, context: any): any;
|
||||
}
|
||||
|
||||
export abstract class AnimationStateAst extends AnimationAst {
|
||||
abstract visit(visitor: AnimationAstVisitor, context: any): any;
|
||||
}
|
||||
|
||||
export interface AnimationAstVisitor {
|
||||
visitAnimationEntry(ast: AnimationEntryAst, context: any): any;
|
||||
visitAnimationStateDeclaration(ast: AnimationStateDeclarationAst, context: any): any;
|
||||
visitAnimationStateTransition(ast: AnimationStateTransitionAst, context: any): any;
|
||||
visitAnimationStep(ast: AnimationStepAst, context: any): any;
|
||||
visitAnimationSequence(ast: AnimationSequenceAst, context: any): any;
|
||||
visitAnimationGroup(ast: AnimationGroupAst, context: any): any;
|
||||
visitAnimationKeyframe(ast: AnimationKeyframeAst, context: any): any;
|
||||
visitAnimationStyles(ast: AnimationStylesAst, context: any): any;
|
||||
}
|
||||
|
||||
export class AnimationEntryAst extends AnimationAst {
|
||||
constructor(public name: string,
|
||||
public stateDeclarations: AnimationStateDeclarationAst[],
|
||||
public stateTransitions: AnimationStateTransitionAst[]) {
|
||||
super();
|
||||
}
|
||||
visit(visitor: AnimationAstVisitor, context: any): any {
|
||||
return visitor.visitAnimationEntry(this, context);
|
||||
}
|
||||
}
|
||||
|
||||
export class AnimationStateDeclarationAst extends AnimationStateAst {
|
||||
constructor(public stateName: string, public styles: AnimationStylesAst) {
|
||||
super();
|
||||
}
|
||||
visit(visitor: AnimationAstVisitor, context: any): any {
|
||||
return visitor.visitAnimationStateDeclaration(this, context);
|
||||
}
|
||||
}
|
||||
|
||||
export class AnimationStateTransitionExpression {
|
||||
constructor(public fromState: string, public toState: string) {}
|
||||
}
|
||||
|
||||
export class AnimationStateTransitionAst extends AnimationStateAst {
|
||||
constructor(public stateChanges: AnimationStateTransitionExpression[], public animation: AnimationSequenceAst) {
|
||||
super();
|
||||
}
|
||||
visit(visitor: AnimationAstVisitor, context: any): any {
|
||||
return visitor.visitAnimationStateTransition(this, context);
|
||||
}
|
||||
}
|
||||
|
||||
export class AnimationStepAst extends AnimationAst {
|
||||
constructor(public startingStyles: AnimationStylesAst,
|
||||
public keyframes: AnimationKeyframeAst[],
|
||||
public duration: number,
|
||||
public delay: number,
|
||||
public easing: string) {
|
||||
super();
|
||||
}
|
||||
visit(visitor: AnimationAstVisitor, context: any): any {
|
||||
return visitor.visitAnimationStep(this, context);
|
||||
}
|
||||
}
|
||||
|
||||
export class AnimationStylesAst extends AnimationAst {
|
||||
constructor(public styles: Array<{[key: string]: string | number}>) { super(); }
|
||||
visit(visitor: AnimationAstVisitor, context: any): any {
|
||||
return visitor.visitAnimationStyles(this, context);
|
||||
}
|
||||
}
|
||||
|
||||
export class AnimationKeyframeAst extends AnimationAst {
|
||||
constructor(public offset: number, public styles: AnimationStylesAst) { super(); }
|
||||
visit(visitor: AnimationAstVisitor, context: any): any {
|
||||
return visitor.visitAnimationKeyframe(this, context);
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class AnimationWithStepsAst extends AnimationAst {
|
||||
constructor(public steps: AnimationAst[]) { super(); }
|
||||
}
|
||||
|
||||
export class AnimationGroupAst extends AnimationWithStepsAst {
|
||||
constructor(steps: AnimationAst[]) { super(steps); }
|
||||
visit(visitor: AnimationAstVisitor, context: any): any {
|
||||
return visitor.visitAnimationGroup(this, context);
|
||||
}
|
||||
}
|
||||
|
||||
export class AnimationSequenceAst extends AnimationWithStepsAst {
|
||||
constructor(steps: AnimationAst[]) { super(steps); }
|
||||
visit(visitor: AnimationAstVisitor, context: any): any {
|
||||
return visitor.visitAnimationSequence(this, context);
|
||||
}
|
||||
}
|
368
modules/@angular/compiler/src/animation/animation_compiler.ts
Normal file
368
modules/@angular/compiler/src/animation/animation_compiler.ts
Normal file
@ -0,0 +1,368 @@
|
||||
import {BaseException} from '../facade/exceptions';
|
||||
import {ListWrapper, Map, StringMapWrapper} from '../facade/collection';
|
||||
import {isPresent, isBlank, isArray} from '../facade/lang';
|
||||
|
||||
import {Identifiers} from '../identifiers';
|
||||
import * as o from '../output/output_ast';
|
||||
|
||||
import {AUTO_STYLE} from '@angular/core';
|
||||
import {ANY_STATE, EMPTY_STATE} from '../../core_private';
|
||||
|
||||
import {
|
||||
AnimationParseError,
|
||||
ParsedAnimationResult,
|
||||
parseAnimationEntry
|
||||
} from './animation_parser';
|
||||
|
||||
import {CompileDirectiveMetadata} from "../compile_metadata";
|
||||
|
||||
import {
|
||||
AnimationAst,
|
||||
AnimationEntryAst,
|
||||
AnimationStateAst,
|
||||
AnimationStateDeclarationAst,
|
||||
AnimationStateTransitionAst,
|
||||
AnimationKeyframeAst,
|
||||
AnimationStylesAst,
|
||||
AnimationSequenceAst,
|
||||
AnimationGroupAst,
|
||||
AnimationStepAst,
|
||||
AnimationAstVisitor
|
||||
} from './animation_ast';
|
||||
|
||||
export class CompiledAnimation {
|
||||
constructor(public name: string,
|
||||
public statesMapStatement: o.Statement,
|
||||
public statesVariableName: string,
|
||||
public fnStatement: o.Statement,
|
||||
public fnVariable: o.Expression) {}
|
||||
}
|
||||
|
||||
export class AnimationCompiler {
|
||||
compileComponent(component: CompileDirectiveMetadata): CompiledAnimation[] {
|
||||
var compiledAnimations: CompiledAnimation[] = [];
|
||||
var index = 0;
|
||||
component.template.animations.forEach(entry => {
|
||||
var result = parseAnimationEntry(entry);
|
||||
if (result.errors.length > 0) {
|
||||
var errorMessage = '';
|
||||
result.errors.forEach((error: AnimationParseError) => { errorMessage += "\n- " + error.msg; });
|
||||
// todo (matsko): include the component name when throwing
|
||||
throw new BaseException(
|
||||
`Unable to parse the animation sequence for "${entry.name}" due to the following errors: ` +
|
||||
errorMessage);
|
||||
}
|
||||
|
||||
var factoryName = `${component.type.name}_${entry.name}_${index}`;
|
||||
index++;
|
||||
|
||||
var visitor = new _AnimationBuilder(entry.name, factoryName);
|
||||
compiledAnimations.push(visitor.build(result.ast));
|
||||
});
|
||||
return compiledAnimations;
|
||||
}
|
||||
}
|
||||
|
||||
var _ANIMATION_FACTORY_ELEMENT_VAR = o.variable('element');
|
||||
var _ANIMATION_FACTORY_VIEW_VAR = o.variable('view');
|
||||
var _ANIMATION_FACTORY_RENDERER_VAR = _ANIMATION_FACTORY_VIEW_VAR.prop('renderer');
|
||||
var _ANIMATION_CURRENT_STATE_VAR = o.variable('currentState');
|
||||
var _ANIMATION_NEXT_STATE_VAR = o.variable('nextState');
|
||||
var _ANIMATION_PLAYER_VAR = o.variable('player');
|
||||
var _ANIMATION_START_STATE_STYLES_VAR = o.variable('startStateStyles');
|
||||
var _ANIMATION_END_STATE_STYLES_VAR = o.variable('endStateStyles');
|
||||
var _ANIMATION_COLLECTED_STYLES = o.variable('collectedStyles');
|
||||
var EMPTY_MAP = o.literalMap([]);
|
||||
|
||||
class _AnimationBuilder implements AnimationAstVisitor {
|
||||
private _fnVarName: string;
|
||||
private _statesMapVarName: string;
|
||||
private _statesMapVar: any;
|
||||
|
||||
constructor(public animationName: string, factoryName: string) {
|
||||
this._fnVarName = factoryName + '_factory';
|
||||
this._statesMapVarName = factoryName + '_states';
|
||||
this._statesMapVar = o.variable(this._statesMapVarName);
|
||||
}
|
||||
|
||||
visitAnimationStyles(ast: AnimationStylesAst,
|
||||
context: _AnimationBuilderContext): o.Expression {
|
||||
var stylesArr = [];
|
||||
if (context.isExpectingFirstStyleStep) {
|
||||
stylesArr.push(_ANIMATION_START_STATE_STYLES_VAR);
|
||||
context.isExpectingFirstStyleStep = false;
|
||||
}
|
||||
|
||||
ast.styles.forEach(entry => {
|
||||
stylesArr.push(o.literalMap(StringMapWrapper.keys(entry).map(key => [key, o.literal(entry[key])])));
|
||||
});
|
||||
|
||||
return o.importExpr(Identifiers.AnimationStyles).instantiate([
|
||||
o.importExpr(Identifiers.collectAndResolveStyles).callFn([
|
||||
_ANIMATION_COLLECTED_STYLES,
|
||||
o.literalArr(stylesArr)
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
visitAnimationKeyframe(ast: AnimationKeyframeAst,
|
||||
context: _AnimationBuilderContext): o.Expression {
|
||||
return o.importExpr(Identifiers.AnimationKeyframe).instantiate([
|
||||
o.literal(ast.offset),
|
||||
ast.styles.visit(this, context)
|
||||
]);
|
||||
}
|
||||
|
||||
visitAnimationStep(ast: AnimationStepAst, context: _AnimationBuilderContext): o.Expression {
|
||||
if (context.endStateAnimateStep === ast) {
|
||||
return this._visitEndStateAnimation(ast, context);
|
||||
}
|
||||
|
||||
var startingStylesExpr = ast.startingStyles.visit(this, context);
|
||||
var keyframeExpressions = ast.keyframes.map(keyframeEntry => keyframeEntry.visit(this, context));
|
||||
return this._callAnimateMethod(ast, startingStylesExpr, o.literalArr(keyframeExpressions));
|
||||
}
|
||||
|
||||
_visitEndStateAnimation(ast: AnimationStepAst,
|
||||
context: _AnimationBuilderContext): o.Expression {
|
||||
var startingStylesExpr = ast.startingStyles.visit(this, context);
|
||||
var keyframeExpressions = ast.keyframes.map(keyframe => keyframe.visit(this, context));
|
||||
var keyframesExpr = o.importExpr(Identifiers.balanceAnimationKeyframes).callFn([
|
||||
_ANIMATION_COLLECTED_STYLES,
|
||||
_ANIMATION_END_STATE_STYLES_VAR,
|
||||
o.literalArr(keyframeExpressions)
|
||||
]);
|
||||
|
||||
return this._callAnimateMethod(ast, startingStylesExpr, keyframesExpr);
|
||||
}
|
||||
|
||||
_callAnimateMethod(ast: AnimationStepAst, startingStylesExpr, keyframesExpr) {
|
||||
return _ANIMATION_FACTORY_RENDERER_VAR.callMethod('animate', [
|
||||
_ANIMATION_FACTORY_ELEMENT_VAR,
|
||||
startingStylesExpr,
|
||||
keyframesExpr,
|
||||
o.literal(ast.duration),
|
||||
o.literal(ast.delay),
|
||||
o.literal(ast.easing)
|
||||
]);
|
||||
}
|
||||
|
||||
visitAnimationSequence(ast: AnimationSequenceAst,
|
||||
context: _AnimationBuilderContext): o.Expression {
|
||||
var playerExprs = ast.steps.map(step => step.visit(this, context));
|
||||
return o.importExpr(Identifiers.AnimationSequencePlayer).instantiate([
|
||||
o.literalArr(playerExprs)]);
|
||||
}
|
||||
|
||||
visitAnimationGroup(ast: AnimationGroupAst, context: _AnimationBuilderContext): o.Expression {
|
||||
var playerExprs = ast.steps.map(step => step.visit(this, context));
|
||||
return o.importExpr(Identifiers.AnimationGroupPlayer).instantiate([
|
||||
o.literalArr(playerExprs)]);
|
||||
}
|
||||
|
||||
visitAnimationStateDeclaration(ast: AnimationStateDeclarationAst, context: _AnimationBuilderContext): void {
|
||||
var flatStyles: {[key: string]: string|number} = {};
|
||||
_getStylesArray(ast).forEach(entry => {
|
||||
StringMapWrapper.forEach(entry, (value, key) => {
|
||||
flatStyles[key] = value;
|
||||
});
|
||||
});
|
||||
context.stateMap.registerState(ast.stateName, flatStyles);
|
||||
}
|
||||
|
||||
visitAnimationStateTransition(ast: AnimationStateTransitionAst, context: _AnimationBuilderContext): any {
|
||||
var steps = ast.animation.steps;
|
||||
var lastStep = steps[steps.length - 1];
|
||||
if (_isEndStateAnimateStep(lastStep)) {
|
||||
context.endStateAnimateStep = <AnimationStepAst>lastStep;
|
||||
}
|
||||
|
||||
context.isExpectingFirstStyleStep = true;
|
||||
|
||||
var stateChangePreconditions = [];
|
||||
|
||||
ast.stateChanges.forEach(stateChange => {
|
||||
stateChangePreconditions.push(
|
||||
_compareToAnimationStateExpr(_ANIMATION_CURRENT_STATE_VAR, stateChange.fromState)
|
||||
.and(_compareToAnimationStateExpr(_ANIMATION_NEXT_STATE_VAR, stateChange.toState))
|
||||
);
|
||||
|
||||
if (stateChange.fromState != ANY_STATE) {
|
||||
context.stateMap.registerState(stateChange.fromState);
|
||||
}
|
||||
|
||||
if (stateChange.toState != ANY_STATE) {
|
||||
context.stateMap.registerState(stateChange.toState);
|
||||
}
|
||||
});
|
||||
|
||||
var animationPlayerExpr = ast.animation.visit(this, context);
|
||||
|
||||
var reducedStateChangesPrecondition = stateChangePreconditions.reduce((a,b) => a.or(b));
|
||||
var precondition = _ANIMATION_PLAYER_VAR.equals(o.NULL_EXPR).and(reducedStateChangesPrecondition);
|
||||
|
||||
return new o.IfStmt(precondition, [
|
||||
_ANIMATION_PLAYER_VAR.set(animationPlayerExpr).toStmt()
|
||||
]);
|
||||
}
|
||||
|
||||
visitAnimationEntry(ast: AnimationEntryAst, context: _AnimationBuilderContext): any {
|
||||
//visit each of the declarations first to build the context state map
|
||||
ast.stateDeclarations.forEach(def => def.visit(this, context));
|
||||
|
||||
var statements = [];
|
||||
statements.push(
|
||||
_ANIMATION_FACTORY_VIEW_VAR.callMethod('cancelActiveAnimation', [
|
||||
_ANIMATION_FACTORY_ELEMENT_VAR,
|
||||
o.literal(this.animationName),
|
||||
_ANIMATION_NEXT_STATE_VAR.equals(o.literal(EMPTY_STATE))
|
||||
]).toStmt());
|
||||
|
||||
statements.push(_ANIMATION_COLLECTED_STYLES.set(EMPTY_MAP).toDeclStmt());
|
||||
statements.push(_ANIMATION_PLAYER_VAR.set(o.NULL_EXPR).toDeclStmt());
|
||||
|
||||
statements.push(
|
||||
_ANIMATION_START_STATE_STYLES_VAR.set(
|
||||
this._statesMapVar.key(_ANIMATION_CURRENT_STATE_VAR)
|
||||
).toDeclStmt());
|
||||
|
||||
statements.push(
|
||||
new o.IfStmt(_ANIMATION_START_STATE_STYLES_VAR.equals(o.NULL_EXPR), [
|
||||
_ANIMATION_START_STATE_STYLES_VAR.set(EMPTY_MAP).toStmt()
|
||||
]));
|
||||
|
||||
statements.push(
|
||||
_ANIMATION_END_STATE_STYLES_VAR.set(
|
||||
this._statesMapVar.key(_ANIMATION_NEXT_STATE_VAR)
|
||||
).toDeclStmt());
|
||||
|
||||
statements.push(
|
||||
new o.IfStmt(_ANIMATION_END_STATE_STYLES_VAR.equals(o.NULL_EXPR), [
|
||||
_ANIMATION_END_STATE_STYLES_VAR.set(EMPTY_MAP).toStmt()
|
||||
]));
|
||||
|
||||
// before we start any animation we want to clear out the starting
|
||||
// styles from the element's style property (since they were placed
|
||||
// there at the end of the last animation
|
||||
statements.push(
|
||||
_ANIMATION_FACTORY_RENDERER_VAR.callMethod('setElementStyles', [
|
||||
_ANIMATION_FACTORY_ELEMENT_VAR,
|
||||
o.importExpr(Identifiers.clearAnimationStyles).callFn([_ANIMATION_START_STATE_STYLES_VAR])
|
||||
]).toStmt());
|
||||
|
||||
ast.stateTransitions.forEach(transAst => statements.push(transAst.visit(this, context)));
|
||||
|
||||
// this check ensures that the animation factory always returns a player
|
||||
// so that the onDone callback can be used for tracking
|
||||
statements.push(
|
||||
new o.IfStmt(_ANIMATION_PLAYER_VAR.equals(o.NULL_EXPR), [
|
||||
_ANIMATION_PLAYER_VAR.set(
|
||||
o.importExpr(Identifiers.NoOpAnimationPlayer).instantiate([])
|
||||
).toStmt()
|
||||
]));
|
||||
|
||||
// once complete we want to apply the styles on the element
|
||||
// since the destination state's values should persist once
|
||||
// the animation sequence has completed.
|
||||
statements.push(
|
||||
_ANIMATION_PLAYER_VAR.callMethod('onDone', [
|
||||
o.fn([], [
|
||||
_ANIMATION_FACTORY_RENDERER_VAR.callMethod('setElementStyles', [
|
||||
_ANIMATION_FACTORY_ELEMENT_VAR,
|
||||
o.importExpr(Identifiers.balanceAnimationStyles).callFn([
|
||||
_ANIMATION_START_STATE_STYLES_VAR,
|
||||
_ANIMATION_END_STATE_STYLES_VAR
|
||||
])
|
||||
]).toStmt()
|
||||
])
|
||||
]).toStmt());
|
||||
|
||||
statements.push(
|
||||
_ANIMATION_FACTORY_VIEW_VAR.callMethod('registerAndStartAnimation', [
|
||||
_ANIMATION_FACTORY_ELEMENT_VAR,
|
||||
o.literal(this.animationName),
|
||||
_ANIMATION_PLAYER_VAR
|
||||
]).toStmt());
|
||||
|
||||
return o.fn([
|
||||
new o.FnParam(_ANIMATION_FACTORY_VIEW_VAR.name, o.importType(Identifiers.AppView)),
|
||||
new o.FnParam(_ANIMATION_FACTORY_ELEMENT_VAR.name, o.DYNAMIC_TYPE),
|
||||
new o.FnParam(_ANIMATION_CURRENT_STATE_VAR.name, o.DYNAMIC_TYPE),
|
||||
new o.FnParam(_ANIMATION_NEXT_STATE_VAR.name, o.DYNAMIC_TYPE)
|
||||
], statements);
|
||||
}
|
||||
|
||||
build(ast: AnimationAst): CompiledAnimation {
|
||||
var context = new _AnimationBuilderContext();
|
||||
var fnStatement = ast.visit(this, context).toDeclStmt(this._fnVarName);
|
||||
var fnVariable = o.variable(this._fnVarName);
|
||||
|
||||
var lookupMap = [];
|
||||
StringMapWrapper.forEach(context.stateMap.states, (value, stateName) => {
|
||||
var variableValue = EMPTY_MAP;
|
||||
if (isPresent(value)) {
|
||||
let styleMap = [];
|
||||
StringMapWrapper.forEach(value, (value, key) => {
|
||||
styleMap.push([key, o.literal(value)]);
|
||||
});
|
||||
variableValue = o.literalMap(styleMap);
|
||||
}
|
||||
lookupMap.push([stateName, variableValue]);
|
||||
});
|
||||
|
||||
var compiledStatesMapExpr = this._statesMapVar.set(o.literalMap(lookupMap)).toDeclStmt();
|
||||
return new CompiledAnimation(this.animationName,
|
||||
compiledStatesMapExpr,
|
||||
this._statesMapVarName,
|
||||
fnStatement,
|
||||
fnVariable);
|
||||
}
|
||||
}
|
||||
|
||||
class _AnimationBuilderContext {
|
||||
stateMap = new _AnimationBuilderStateMap();
|
||||
endStateAnimateStep: AnimationStepAst = null;
|
||||
isExpectingFirstStyleStep = false;
|
||||
}
|
||||
|
||||
class _AnimationBuilderStateMap {
|
||||
private _states: {[key: string]: {[prop: string]: string|number}} = {};
|
||||
get states() { return this._states; }
|
||||
registerState(name: string, value: {[prop: string]: string|number} = null): void {
|
||||
var existingEntry = this._states[name];
|
||||
if (isBlank(existingEntry)) {
|
||||
this._states[name] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _compareToAnimationStateExpr(value: o.Expression, animationState: string): o.Expression {
|
||||
var emptyStateLiteral = o.literal(EMPTY_STATE);
|
||||
switch (animationState) {
|
||||
case EMPTY_STATE:
|
||||
return value.equals(emptyStateLiteral);
|
||||
|
||||
case ANY_STATE:
|
||||
return o.literal(true);
|
||||
|
||||
default:
|
||||
return value.equals(o.literal(animationState));
|
||||
}
|
||||
}
|
||||
|
||||
function _isEndStateAnimateStep(step: AnimationAst): boolean {
|
||||
// the final animation step is characterized by having only TWO
|
||||
// keyframe values and it must have zero styles for both keyframes
|
||||
if (step instanceof AnimationStepAst
|
||||
&& step.duration > 0
|
||||
&& step.keyframes.length == 2) {
|
||||
var styles1 = _getStylesArray(step.keyframes[0])[0];
|
||||
var styles2 = _getStylesArray(step.keyframes[1])[0];
|
||||
return StringMapWrapper.isEmpty(styles1) && StringMapWrapper.isEmpty(styles2);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function _getStylesArray(obj: any) {
|
||||
return obj.styles.styles;
|
||||
}
|
561
modules/@angular/compiler/src/animation/animation_parser.ts
Normal file
561
modules/@angular/compiler/src/animation/animation_parser.ts
Normal file
@ -0,0 +1,561 @@
|
||||
import {ListWrapper, StringMapWrapper} from '../facade/collection';
|
||||
import {Math} from '../facade/math';
|
||||
import {ANY_STATE, EMPTY_STATE} from '../../core_private';
|
||||
import {
|
||||
IS_DART,
|
||||
RegExpWrapper,
|
||||
isArray,
|
||||
isPresent,
|
||||
isBlank,
|
||||
isNumber,
|
||||
isString,
|
||||
isStringMap,
|
||||
NumberWrapper
|
||||
} from '../facade/lang';
|
||||
|
||||
import {FILL_STYLE_FLAG} from '../../core_private';
|
||||
|
||||
import {
|
||||
CompileAnimationEntryMetadata,
|
||||
CompileAnimationStateMetadata,
|
||||
CompileAnimationStateDeclarationMetadata,
|
||||
CompileAnimationStateTransitionMetadata,
|
||||
CompileAnimationMetadata,
|
||||
CompileAnimationWithStepsMetadata,
|
||||
CompileAnimationStyleMetadata,
|
||||
CompileAnimationAnimateMetadata,
|
||||
CompileAnimationGroupMetadata,
|
||||
CompileAnimationSequenceMetadata,
|
||||
CompileAnimationKeyframesSequenceMetadata
|
||||
} from '../compile_metadata';
|
||||
|
||||
import {
|
||||
AnimationAst,
|
||||
AnimationEntryAst,
|
||||
AnimationStateAst,
|
||||
AnimationStateTransitionAst,
|
||||
AnimationStateDeclarationAst,
|
||||
AnimationKeyframeAst,
|
||||
AnimationStylesAst,
|
||||
AnimationWithStepsAst,
|
||||
AnimationSequenceAst,
|
||||
AnimationGroupAst,
|
||||
AnimationStepAst,
|
||||
AnimationStateTransitionExpression
|
||||
} from './animation_ast';
|
||||
|
||||
import {StylesCollection} from './styles_collection';
|
||||
import {ParseError} from '../parse_util';
|
||||
|
||||
const _INITIAL_KEYFRAME = 0;
|
||||
const _TERMINAL_KEYFRAME = 1;
|
||||
const _ONE_SECOND = 1000;
|
||||
|
||||
export class AnimationParseError extends ParseError {
|
||||
constructor(message) { super(null, message); }
|
||||
toString(): string { return `${this.msg}`; }
|
||||
}
|
||||
|
||||
export class ParsedAnimationResult {
|
||||
constructor(public ast: AnimationEntryAst, public errors: AnimationParseError[]) {}
|
||||
}
|
||||
|
||||
export function parseAnimationEntry(entry: CompileAnimationEntryMetadata): ParsedAnimationResult {
|
||||
var errors: AnimationParseError[] = [];
|
||||
var stateStyles: {[key: string]: AnimationStylesAst} = {};
|
||||
var transitions: CompileAnimationStateTransitionMetadata[] = [];
|
||||
|
||||
var stateDeclarationAsts = [];
|
||||
entry.definitions.forEach(def => {
|
||||
if (def instanceof CompileAnimationStateDeclarationMetadata) {
|
||||
_parseAnimationDeclarationStates(def, errors).forEach(ast => {
|
||||
stateDeclarationAsts.push(ast);
|
||||
stateStyles[ast.stateName] = ast.styles;
|
||||
});
|
||||
} else {
|
||||
transitions.push(<CompileAnimationStateTransitionMetadata>def);
|
||||
}
|
||||
});
|
||||
|
||||
var stateTransitionAsts = transitions.map(transDef =>
|
||||
_parseAnimationStateTransition(transDef, stateStyles, errors));
|
||||
|
||||
var ast = new AnimationEntryAst(entry.name, stateDeclarationAsts, stateTransitionAsts);
|
||||
return new ParsedAnimationResult(ast, errors);
|
||||
}
|
||||
|
||||
function _parseAnimationDeclarationStates(stateMetadata: CompileAnimationStateDeclarationMetadata, errors: AnimationParseError[]): AnimationStateDeclarationAst[] {
|
||||
var styleValues: {[key: string]: string|number}[] = [];
|
||||
stateMetadata.styles.styles.forEach(stylesEntry => {
|
||||
// TODO (matsko): change this when we get CSS class integration support
|
||||
if (isStringMap(stylesEntry)) {
|
||||
styleValues.push(<{[key: string]: string|number}>stylesEntry);
|
||||
} else {
|
||||
errors.push(new AnimationParseError(`State based animations cannot contain references to other states`));
|
||||
}
|
||||
});
|
||||
var defStyles = new AnimationStylesAst(styleValues);
|
||||
|
||||
var states = stateMetadata.stateNameExpr.split(/\s*,\s*/);
|
||||
return states.map(state => new AnimationStateDeclarationAst(state, defStyles));
|
||||
}
|
||||
|
||||
function _parseAnimationStateTransition(transitionStateMetadata: CompileAnimationStateTransitionMetadata,
|
||||
stateStyles: {[key: string]: AnimationStylesAst},
|
||||
errors: AnimationParseError[]): AnimationStateTransitionAst {
|
||||
var styles = new StylesCollection();
|
||||
var transitionExprs = [];
|
||||
var transitionStates = transitionStateMetadata.stateChangeExpr.split(/\s*,\s*/);
|
||||
transitionStates.forEach(expr => {
|
||||
_parseAnimationTransitionExpr(expr, errors).forEach(transExpr => {
|
||||
transitionExprs.push(transExpr);
|
||||
});
|
||||
});
|
||||
var entry = _normalizeAnimationEntry(transitionStateMetadata.animation);
|
||||
var animation = _normalizeStyleSteps(entry, stateStyles, errors);
|
||||
var animationAst = _parseTransitionAnimation(animation, 0, styles, stateStyles, errors);
|
||||
if (errors.length == 0) {
|
||||
_fillAnimationAstStartingKeyframes(animationAst, styles, errors);
|
||||
}
|
||||
|
||||
var sequenceAst = (animationAst instanceof AnimationSequenceAst)
|
||||
? <AnimationSequenceAst>animationAst
|
||||
: new AnimationSequenceAst([animationAst]);
|
||||
|
||||
return new AnimationStateTransitionAst(transitionExprs, sequenceAst);
|
||||
}
|
||||
|
||||
function _parseAnimationTransitionExpr(eventStr: string, errors: AnimationParseError[]): AnimationStateTransitionExpression[] {
|
||||
var expressions = [];
|
||||
var match = eventStr.match(/^(\*|[-\w]+)\s*(<?[=-]>)\s*(\*|[-\w]+)$/);
|
||||
if (!isPresent(match) || match.length < 4) {
|
||||
errors.push(new AnimationParseError(`the provided ${eventStr} is not of a supported format`));
|
||||
return expressions;
|
||||
}
|
||||
|
||||
var fromState = match[1];
|
||||
var separator = match[2];
|
||||
var toState = match[3];
|
||||
expressions.push(
|
||||
new AnimationStateTransitionExpression(fromState, toState));
|
||||
|
||||
var isFullAnyStateExpr = fromState == ANY_STATE && toState == ANY_STATE;
|
||||
if (separator[0] == '<' && !isFullAnyStateExpr) {
|
||||
expressions.push(
|
||||
new AnimationStateTransitionExpression(toState, fromState));
|
||||
}
|
||||
return expressions;
|
||||
}
|
||||
|
||||
function _fetchSylesFromState(stateName: string,
|
||||
stateStyles: {[key: string]: AnimationStylesAst}): CompileAnimationStyleMetadata {
|
||||
var entry = stateStyles[stateName];
|
||||
if (isPresent(entry)) {
|
||||
var styles = <{[key: string]: string | number}[]>entry.styles;
|
||||
return new CompileAnimationStyleMetadata(0, styles);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function _normalizeAnimationEntry(entry: CompileAnimationMetadata | CompileAnimationMetadata[])
|
||||
:CompileAnimationMetadata {
|
||||
return isArray(entry)
|
||||
? new CompileAnimationSequenceMetadata(<CompileAnimationMetadata[]>entry)
|
||||
: <CompileAnimationMetadata>entry;
|
||||
}
|
||||
|
||||
function _normalizeStyleMetadata(entry: CompileAnimationStyleMetadata,
|
||||
stateStyles: {[key: string]: AnimationStylesAst},
|
||||
errors: AnimationParseError[]): Array<{[key: string]: string|number}> {
|
||||
var normalizedStyles = [];
|
||||
entry.styles.forEach(styleEntry => {
|
||||
if (isString(styleEntry)) {
|
||||
ListWrapper.addAll(normalizedStyles, _resolveStylesFromState(<string>styleEntry, stateStyles, errors));
|
||||
} else {
|
||||
normalizedStyles.push(<{[key: string]: string | number}>styleEntry);
|
||||
}
|
||||
});
|
||||
return normalizedStyles;
|
||||
}
|
||||
|
||||
function _normalizeStyleSteps(entry: CompileAnimationMetadata,
|
||||
stateStyles: {[key: string]: AnimationStylesAst},
|
||||
errors: AnimationParseError[]): CompileAnimationMetadata {
|
||||
var steps = _normalizeStyleStepEntry(entry, stateStyles, errors);
|
||||
return new CompileAnimationSequenceMetadata(steps);
|
||||
}
|
||||
|
||||
function _mergeAnimationStyles(stylesList: any[], newItem: {[key: string]: string|number}|string) {
|
||||
if (isStringMap(newItem) && stylesList.length > 0) {
|
||||
var lastIndex = stylesList.length - 1;
|
||||
var lastItem = stylesList[lastIndex];
|
||||
if (isStringMap(lastItem)) {
|
||||
stylesList[lastIndex] = StringMapWrapper.merge(
|
||||
<{[key: string]: string|number}>lastItem,
|
||||
<{[key: string]: string|number}>newItem
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
stylesList.push(newItem);
|
||||
}
|
||||
|
||||
function _normalizeStyleStepEntry(entry: CompileAnimationMetadata,
|
||||
stateStyles: {[key: string]: AnimationStylesAst},
|
||||
errors: AnimationParseError[]): CompileAnimationMetadata[] {
|
||||
var steps: CompileAnimationMetadata[];
|
||||
if (entry instanceof CompileAnimationWithStepsMetadata) {
|
||||
steps = entry.steps;
|
||||
} else {
|
||||
return [entry];
|
||||
}
|
||||
|
||||
var newSteps: CompileAnimationMetadata[] = [];
|
||||
var combinedStyles: {[key: string]: string | number}[];
|
||||
steps.forEach(step => {
|
||||
if (step instanceof CompileAnimationStyleMetadata) {
|
||||
// this occurs when a style step is followed by a previous style step
|
||||
// or when the first style step is run. We want to concatenate all subsequent
|
||||
// style steps together into a single style step such that we have the correct
|
||||
// starting keyframe data to pass into the animation player.
|
||||
if (!isPresent(combinedStyles)) {
|
||||
combinedStyles = [];
|
||||
}
|
||||
_normalizeStyleMetadata(<CompileAnimationStyleMetadata>step, stateStyles, errors).forEach(entry => {
|
||||
_mergeAnimationStyles(combinedStyles, entry);
|
||||
});
|
||||
} else {
|
||||
// it is important that we create a metadata entry of the combined styles
|
||||
// before we go on an process the animate, sequence or group metadata steps.
|
||||
// This will ensure that the AST will have the previous styles painted on
|
||||
// screen before any further animations that use the styles take place.
|
||||
if (isPresent(combinedStyles)) {
|
||||
newSteps.push(new CompileAnimationStyleMetadata(0, combinedStyles));
|
||||
combinedStyles = null;
|
||||
}
|
||||
|
||||
if (step instanceof CompileAnimationAnimateMetadata) {
|
||||
// we do not recurse into CompileAnimationAnimateMetadata since
|
||||
// those style steps are not going to be squashed
|
||||
var animateStyleValue = (<CompileAnimationAnimateMetadata>step).styles;
|
||||
if (animateStyleValue instanceof CompileAnimationStyleMetadata) {
|
||||
animateStyleValue.styles = _normalizeStyleMetadata(animateStyleValue, stateStyles, errors);
|
||||
} else if (animateStyleValue instanceof CompileAnimationKeyframesSequenceMetadata) {
|
||||
animateStyleValue.steps.forEach(step => {
|
||||
step.styles = _normalizeStyleMetadata(step, stateStyles, errors);
|
||||
});
|
||||
}
|
||||
} else if (step instanceof CompileAnimationWithStepsMetadata) {
|
||||
let innerSteps = _normalizeStyleStepEntry(step, stateStyles, errors);
|
||||
step = step instanceof CompileAnimationGroupMetadata
|
||||
? new CompileAnimationGroupMetadata(innerSteps)
|
||||
: new CompileAnimationSequenceMetadata(innerSteps);
|
||||
}
|
||||
|
||||
newSteps.push(step);
|
||||
}
|
||||
});
|
||||
|
||||
// this happens when only styles were animated within the sequence
|
||||
if (isPresent(combinedStyles)) {
|
||||
newSteps.push(new CompileAnimationStyleMetadata(0, combinedStyles));
|
||||
}
|
||||
|
||||
return newSteps;
|
||||
}
|
||||
|
||||
|
||||
function _resolveStylesFromState(stateName: string, stateStyles: {[key: string]: AnimationStylesAst}, errors: AnimationParseError[]) {
|
||||
var styles: {[key: string]: string|number}[] = [];
|
||||
if (stateName[0] != ':') {
|
||||
errors.push(new AnimationParseError(`Animation states via styles must be prefixed with a ":"`));
|
||||
} else {
|
||||
var normalizedStateName = stateName.substring(1);
|
||||
var value = stateStyles[normalizedStateName];
|
||||
if (!isPresent(value)) {
|
||||
errors.push(new AnimationParseError(`Unable to apply styles due to missing a state: "${normalizedStateName}"`));
|
||||
} else {
|
||||
value.styles.forEach(stylesEntry => {
|
||||
if (isStringMap(stylesEntry)) {
|
||||
styles.push(<{[key: string]: string | number}>stylesEntry);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return styles;
|
||||
}
|
||||
|
||||
class _AnimationTimings {
|
||||
constructor(public duration: number, public delay: number, public easing: string) {}
|
||||
}
|
||||
|
||||
function _parseAnimationKeyframes(keyframeSequence: CompileAnimationKeyframesSequenceMetadata,
|
||||
currentTime: number,
|
||||
collectedStyles: StylesCollection,
|
||||
stateStyles: {[key: string]: AnimationStylesAst},
|
||||
errors: AnimationParseError[]): AnimationKeyframeAst[] {
|
||||
var totalEntries = keyframeSequence.steps.length;
|
||||
var totalOffsets = 0;
|
||||
keyframeSequence.steps.forEach(step => totalOffsets += (isPresent(step.offset) ? 1 : 0));
|
||||
|
||||
if (totalOffsets > 0 && totalOffsets < totalEntries) {
|
||||
errors.push(new AnimationParseError(`Not all style() entries contain an offset for the provided keyframe()`));
|
||||
totalOffsets = totalEntries;
|
||||
}
|
||||
|
||||
var limit = totalEntries - 1;
|
||||
var margin = totalOffsets == 0 ? (1 / limit) : 0;
|
||||
var rawKeyframes = [];
|
||||
var index = 0;
|
||||
var doSortKeyframes = false;
|
||||
var lastOffset = 0;
|
||||
keyframeSequence.steps.forEach(styleMetadata => {
|
||||
var offset = styleMetadata.offset;
|
||||
var keyframeStyles: {[key: string]: string|number} = {};
|
||||
styleMetadata.styles.forEach(entry => {
|
||||
StringMapWrapper.forEach(<{[key: string]: string|number}>entry, (value, prop) => {
|
||||
if (prop != 'offset') {
|
||||
keyframeStyles[prop] = value;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (isPresent(offset)) {
|
||||
doSortKeyframes = doSortKeyframes || (offset < lastOffset);
|
||||
} else {
|
||||
offset = index == limit ? _TERMINAL_KEYFRAME : (margin * index);
|
||||
}
|
||||
|
||||
rawKeyframes.push([offset, keyframeStyles]);
|
||||
lastOffset = offset;
|
||||
index++;
|
||||
});
|
||||
|
||||
if (doSortKeyframes) {
|
||||
ListWrapper.sort(rawKeyframes, (a,b) => a[0] <= b[0] ? -1 : 1);
|
||||
}
|
||||
|
||||
var i;
|
||||
var firstKeyframe = rawKeyframes[0];
|
||||
if (firstKeyframe[0] != _INITIAL_KEYFRAME) {
|
||||
ListWrapper.insert(rawKeyframes, 0, firstKeyframe = [_INITIAL_KEYFRAME, {}]);
|
||||
}
|
||||
|
||||
var firstKeyframeStyles = firstKeyframe[1];
|
||||
var limit = rawKeyframes.length - 1;
|
||||
var lastKeyframe = rawKeyframes[limit];
|
||||
if (lastKeyframe[0] != _TERMINAL_KEYFRAME) {
|
||||
rawKeyframes.push(lastKeyframe = [_TERMINAL_KEYFRAME, {}]);
|
||||
limit++;
|
||||
}
|
||||
|
||||
var lastKeyframeStyles = lastKeyframe[1];
|
||||
for (i = 1; i <= limit; i++) {
|
||||
let entry = rawKeyframes[i];
|
||||
let styles = entry[1];
|
||||
|
||||
StringMapWrapper.forEach(styles, (value, prop) => {
|
||||
if (!isPresent(firstKeyframeStyles[prop])) {
|
||||
firstKeyframeStyles[prop] = FILL_STYLE_FLAG;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (i = limit - 1; i >= 0; i--) {
|
||||
let entry = rawKeyframes[i];
|
||||
let styles = entry[1];
|
||||
|
||||
StringMapWrapper.forEach(styles, (value, prop) => {
|
||||
if (!isPresent(lastKeyframeStyles[prop])) {
|
||||
lastKeyframeStyles[prop] = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return rawKeyframes.map(entry => new AnimationKeyframeAst(entry[0], new AnimationStylesAst([entry[1]])));
|
||||
}
|
||||
|
||||
function _parseTransitionAnimation(entry: CompileAnimationMetadata,
|
||||
currentTime: number,
|
||||
collectedStyles: StylesCollection,
|
||||
stateStyles: {[key: string]: AnimationStylesAst},
|
||||
errors: AnimationParseError[]): AnimationAst {
|
||||
var ast;
|
||||
var playTime = 0;
|
||||
var startingTime = currentTime;
|
||||
if (entry instanceof CompileAnimationWithStepsMetadata) {
|
||||
var maxDuration = 0;
|
||||
var steps = [];
|
||||
var isGroup = entry instanceof CompileAnimationGroupMetadata;
|
||||
var previousStyles;
|
||||
entry.steps.forEach(entry => {
|
||||
// these will get picked up by the next step...
|
||||
var time = isGroup ? startingTime : currentTime;
|
||||
if (entry instanceof CompileAnimationStyleMetadata) {
|
||||
entry.styles.forEach(stylesEntry => {
|
||||
// by this point we know that we only have stringmap values
|
||||
var map = <{[key: string]: string|number}>stylesEntry;
|
||||
StringMapWrapper.forEach(map, (value, prop) => {
|
||||
collectedStyles.insertAtTime(prop, time, value);
|
||||
});
|
||||
});
|
||||
previousStyles = entry.styles;
|
||||
return;
|
||||
}
|
||||
|
||||
var innerAst = _parseTransitionAnimation(entry, time, collectedStyles, stateStyles, errors);
|
||||
if (isPresent(previousStyles)) {
|
||||
if (entry instanceof CompileAnimationWithStepsMetadata) {
|
||||
let startingStyles = new AnimationStylesAst(previousStyles);
|
||||
steps.push(new AnimationStepAst(startingStyles, [], 0, 0, ''));
|
||||
} else {
|
||||
var innerStep = <AnimationStepAst>innerAst;
|
||||
ListWrapper.addAll(innerStep.startingStyles.styles, previousStyles);
|
||||
}
|
||||
previousStyles = null;
|
||||
}
|
||||
|
||||
var astDuration = innerAst.playTime;
|
||||
currentTime += astDuration;
|
||||
playTime += astDuration;
|
||||
maxDuration = Math.max(astDuration, maxDuration);
|
||||
steps.push(innerAst);
|
||||
});
|
||||
if (isPresent(previousStyles)) {
|
||||
let startingStyles = new AnimationStylesAst(previousStyles);
|
||||
steps.push(new AnimationStepAst(startingStyles, [], 0, 0, ''));
|
||||
}
|
||||
if (isGroup) {
|
||||
ast = new AnimationGroupAst(steps);
|
||||
playTime = maxDuration;
|
||||
currentTime = startingTime + playTime;
|
||||
} else {
|
||||
ast = new AnimationSequenceAst(steps);
|
||||
}
|
||||
} else if (entry instanceof CompileAnimationAnimateMetadata) {
|
||||
var timings = _parseTimeExpression(entry.timings, errors);
|
||||
var styles = entry.styles;
|
||||
|
||||
var keyframes;
|
||||
if (styles instanceof CompileAnimationKeyframesSequenceMetadata) {
|
||||
keyframes = _parseAnimationKeyframes(styles, currentTime, collectedStyles, stateStyles, errors);
|
||||
} else {
|
||||
let styleData = <CompileAnimationStyleMetadata>styles;
|
||||
let offset = _TERMINAL_KEYFRAME;
|
||||
let styleAst = new AnimationStylesAst(<{[key: string]: string|number}[]>styleData.styles);
|
||||
var keyframe = new AnimationKeyframeAst(offset, styleAst);
|
||||
keyframes = [keyframe];
|
||||
}
|
||||
|
||||
ast = new AnimationStepAst(new AnimationStylesAst([]), keyframes, timings.duration, timings.delay, timings.easing);
|
||||
playTime = timings.duration + timings.delay;
|
||||
currentTime += playTime;
|
||||
|
||||
keyframes.forEach(keyframe =>
|
||||
keyframe.styles.styles.forEach(entry =>
|
||||
StringMapWrapper.forEach(entry, (value, prop) =>
|
||||
collectedStyles.insertAtTime(prop, currentTime, value))
|
||||
)
|
||||
);
|
||||
} else {
|
||||
// if the code reaches this stage then an error
|
||||
// has already been populated within the _normalizeStyleSteps()
|
||||
// operation...
|
||||
ast = new AnimationStepAst(null, [], 0, 0, '');
|
||||
}
|
||||
|
||||
ast.playTime = playTime;
|
||||
ast.startTime = startingTime;
|
||||
return ast;
|
||||
}
|
||||
|
||||
function _fillAnimationAstStartingKeyframes(ast: AnimationAst, collectedStyles: StylesCollection,
|
||||
errors: AnimationParseError[]): void {
|
||||
// steps that only contain style will not be filled
|
||||
if ((ast instanceof AnimationStepAst) && ast.keyframes.length > 0) {
|
||||
var keyframes = ast.keyframes;
|
||||
if (keyframes.length == 1) {
|
||||
var endKeyframe = keyframes[0];
|
||||
var startKeyframe = _createStartKeyframeFromEndKeyframe(endKeyframe, ast.startTime,
|
||||
ast.playTime, collectedStyles, errors);
|
||||
ast.keyframes = [startKeyframe, endKeyframe];
|
||||
}
|
||||
} else if (ast instanceof AnimationWithStepsAst) {
|
||||
ast.steps.forEach(entry => _fillAnimationAstStartingKeyframes(entry, collectedStyles, errors));
|
||||
}
|
||||
}
|
||||
|
||||
function _parseTimeExpression(exp: string | number,
|
||||
errors: AnimationParseError[]): _AnimationTimings {
|
||||
var regex = /^([\.\d]+)(m?s)(?:\s+([\.\d]+)(m?s))?(?:\s+([-a-z]+(?:\(.+?\))?))?/gi;
|
||||
var duration: number;
|
||||
var delay: number = 0;
|
||||
var easing: string = null;
|
||||
if (isString(exp)) {
|
||||
var matches = RegExpWrapper.firstMatch(regex, <string>exp);
|
||||
if (!isPresent(matches)) {
|
||||
errors.push(new AnimationParseError(`The provided timing value "${exp}" is invalid.`));
|
||||
return new _AnimationTimings(0, 0, null);
|
||||
}
|
||||
|
||||
var durationMatch = NumberWrapper.parseFloat(matches[1]);
|
||||
var durationUnit = matches[2];
|
||||
if (durationUnit == 's') {
|
||||
durationMatch *= _ONE_SECOND;
|
||||
}
|
||||
duration = Math.floor(durationMatch);
|
||||
|
||||
var delayMatch = matches[3];
|
||||
var delayUnit = matches[4];
|
||||
if (isPresent(delayMatch)) {
|
||||
var delayVal: number = NumberWrapper.parseFloat(delayMatch);
|
||||
if (isPresent(delayUnit) && delayUnit == 's') {
|
||||
delayVal *= _ONE_SECOND;
|
||||
}
|
||||
delay = Math.floor(delayVal);
|
||||
}
|
||||
|
||||
var easingVal = matches[5];
|
||||
if (!isBlank(easingVal)) {
|
||||
easing = easingVal;
|
||||
}
|
||||
} else {
|
||||
duration = <number>exp;
|
||||
}
|
||||
|
||||
return new _AnimationTimings(duration, delay, easing);
|
||||
}
|
||||
|
||||
function _createStartKeyframeFromEndKeyframe(endKeyframe: AnimationKeyframeAst, startTime: number,
|
||||
duration: number, collectedStyles: StylesCollection,
|
||||
errors: AnimationParseError[]): AnimationKeyframeAst {
|
||||
var values: {[key: string]: string | number} = {};
|
||||
var endTime = startTime + duration;
|
||||
endKeyframe.styles.styles.forEach((styleData: {[key: string]: string|number}) => {
|
||||
StringMapWrapper.forEach(styleData, (val, prop) => {
|
||||
if (prop == 'offset') return;
|
||||
|
||||
var resultIndex = collectedStyles.indexOfAtOrBeforeTime(prop, startTime);
|
||||
var resultEntry, nextEntry, value;
|
||||
if (isPresent(resultIndex)) {
|
||||
resultEntry = collectedStyles.getByIndex(prop, resultIndex);
|
||||
value = resultEntry.value;
|
||||
nextEntry = collectedStyles.getByIndex(prop, resultIndex + 1);
|
||||
} else {
|
||||
// this is a flag that the runtime code uses to pass
|
||||
// in a value either from the state declaration styles
|
||||
// or using the AUTO_STYLE value (e.g. getComputedStyle)
|
||||
value = FILL_STYLE_FLAG;
|
||||
}
|
||||
|
||||
if (isPresent(nextEntry) && !nextEntry.matches(endTime, val)) {
|
||||
errors.push(new AnimationParseError(
|
||||
`The animated CSS property "${prop}" unexpectedly changes between steps "${resultEntry.time}ms" and "${endTime}ms" at "${nextEntry.time}ms"`));
|
||||
}
|
||||
|
||||
values[prop] = value;
|
||||
});
|
||||
});
|
||||
|
||||
return new AnimationKeyframeAst(_INITIAL_KEYFRAME, new AnimationStylesAst([values]));
|
||||
}
|
52
modules/@angular/compiler/src/animation/styles_collection.ts
Normal file
52
modules/@angular/compiler/src/animation/styles_collection.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import {isPresent} from '../facade/lang';
|
||||
import {ListWrapper} from '../facade/collection';
|
||||
|
||||
export class StylesCollectionEntry {
|
||||
constructor(public time: number, public value: string | number) {}
|
||||
|
||||
matches(time: number, value: string | number): boolean {
|
||||
return time == this.time && value == this.value;
|
||||
}
|
||||
}
|
||||
|
||||
export class StylesCollection {
|
||||
styles: {[key: string]: StylesCollectionEntry[]} = {};
|
||||
|
||||
insertAtTime(property: string, time: number, value: string | number) {
|
||||
var tuple = new StylesCollectionEntry(time, value);
|
||||
var entries = this.styles[property];
|
||||
if (!isPresent(entries)) {
|
||||
entries = this.styles[property] = [];
|
||||
}
|
||||
|
||||
// insert this at the right stop in the array
|
||||
// this way we can keep it sorted
|
||||
var insertionIndex = 0;
|
||||
for (var i = entries.length - 1; i >= 0; i--) {
|
||||
if (entries[i].time <= time) {
|
||||
insertionIndex = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ListWrapper.insert(entries, insertionIndex, tuple);
|
||||
}
|
||||
|
||||
getByIndex(property: string, index: number): StylesCollectionEntry {
|
||||
var items = this.styles[property];
|
||||
if (isPresent(items)) {
|
||||
return index >= items.length ? null : items[index];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
indexOfAtOrBeforeTime(property: string, time: number): number {
|
||||
var entries = this.styles[property];
|
||||
if (isPresent(entries)) {
|
||||
for (var i = entries.length - 1; i >= 0; i--) {
|
||||
if (entries[i].time <= time) return i;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -17,17 +17,16 @@ import {
|
||||
Type,
|
||||
isString,
|
||||
RegExpWrapper,
|
||||
StringWrapper,
|
||||
NumberWrapper,
|
||||
isArray
|
||||
} from '../src/facade/lang';
|
||||
import {unimplemented, BaseException} from '../src/facade/exceptions';
|
||||
import {
|
||||
StringMapWrapper,
|
||||
} from '../src/facade/collection';
|
||||
import {StringMapWrapper, ListWrapper} from '../src/facade/collection';
|
||||
import {CssSelector} from './selector';
|
||||
import {splitAtColon, sanitizeIdentifier} from './util';
|
||||
import {getUrlScheme} from './url_resolver';
|
||||
|
||||
// group 1: "property" from "[property]"
|
||||
// group 2: "event" from "(event)"
|
||||
var HOST_REG_EXP = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))$/g;
|
||||
|
||||
@ -49,6 +48,174 @@ export function metadataFromJson(data: {[key: string]: any}): any {
|
||||
return _COMPILE_METADATA_FROM_JSON[data['class']](data);
|
||||
}
|
||||
|
||||
export class CompileAnimationEntryMetadata {
|
||||
static fromJson(data: {[key: string]: any}): CompileAnimationEntryMetadata {
|
||||
var value = data['value'];
|
||||
var defs = _arrayFromJson(value['definitions'], metadataFromJson);
|
||||
return new CompileAnimationEntryMetadata(value['name'], defs);
|
||||
}
|
||||
|
||||
constructor(public name: string = null, public definitions: CompileAnimationStateMetadata[] = null) {}
|
||||
|
||||
toJson(): {[key: string]: any} {
|
||||
return {
|
||||
'class': 'AnimationEntryMetadata',
|
||||
'value': {
|
||||
'name' : this.name,
|
||||
'definitions': _arrayToJson(this.definitions)
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class CompileAnimationStateMetadata {}
|
||||
|
||||
export class CompileAnimationStateDeclarationMetadata extends CompileAnimationStateMetadata {
|
||||
static fromJson(data: {[key: string]: any}): CompileAnimationStateDeclarationMetadata {
|
||||
var value = data['value'];
|
||||
var styles = _objFromJson(value['styles'], metadataFromJson);
|
||||
return new CompileAnimationStateDeclarationMetadata(value['stateNameExpr'], styles);
|
||||
}
|
||||
|
||||
constructor(public stateNameExpr: string, public styles: CompileAnimationStyleMetadata) { super(); }
|
||||
|
||||
toJson(): {[key: string]: any} {
|
||||
return {
|
||||
'class': 'AnimationStateDeclarationMetadata',
|
||||
'value': {
|
||||
'stateNameExpr': this.stateNameExpr,
|
||||
'styles': this.styles.toJson()
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class CompileAnimationStateTransitionMetadata extends CompileAnimationStateMetadata {
|
||||
static fromJson(data: {[key: string]: any}): CompileAnimationStateTransitionMetadata {
|
||||
var value = data['value'];
|
||||
var animation = _objFromJson(value['animation'], metadataFromJson);
|
||||
return new CompileAnimationStateTransitionMetadata(value['stateChangeExpr'], animation);
|
||||
}
|
||||
|
||||
constructor(public stateChangeExpr: string, public animation: CompileAnimationMetadata) { super(); }
|
||||
|
||||
toJson(): {[key: string]: any} {
|
||||
return {
|
||||
'class': 'AnimationStateTransitionMetadata',
|
||||
'value': {
|
||||
'stateChangeExpr': this.stateChangeExpr,
|
||||
'animation': this.animation.toJson()
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class CompileAnimationMetadata {
|
||||
abstract toJson(): {[key: string]: any};
|
||||
}
|
||||
|
||||
export class CompileAnimationKeyframesSequenceMetadata extends CompileAnimationMetadata {
|
||||
static fromJson(data: {[key: string]: any}): CompileAnimationKeyframesSequenceMetadata {
|
||||
var steps = _arrayFromJson(data['value'], metadataFromJson);
|
||||
return new CompileAnimationKeyframesSequenceMetadata(<CompileAnimationStyleMetadata[]>steps);
|
||||
}
|
||||
|
||||
constructor(public steps: CompileAnimationStyleMetadata[] = []) {
|
||||
super();
|
||||
}
|
||||
|
||||
toJson(): {[key: string]: any} {
|
||||
return {
|
||||
'class': 'AnimationKeyframesSequenceMetadata',
|
||||
'value': _arrayToJson(this.steps)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class CompileAnimationStyleMetadata extends CompileAnimationMetadata {
|
||||
static fromJson(data: {[key: string]: any}): CompileAnimationStyleMetadata {
|
||||
var value = data['value'];
|
||||
var offsetVal = value['offset'];
|
||||
var offset = isPresent(offsetVal) ? NumberWrapper.parseFloat(offsetVal) : null;
|
||||
var styles = <Array<string|{[key: string]: string | number}>>value['styles'];
|
||||
return new CompileAnimationStyleMetadata(offset, styles);
|
||||
}
|
||||
|
||||
constructor(public offset: number, public styles: Array<string|{[key: string]: string | number}> = null) { super(); }
|
||||
|
||||
toJson(): {[key: string]: any} {
|
||||
return {
|
||||
'class': 'AnimationStyleMetadata',
|
||||
'value': {
|
||||
'offset': this.offset,
|
||||
'styles': this.styles
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class CompileAnimationAnimateMetadata extends CompileAnimationMetadata {
|
||||
static fromJson(data: {[key: string]: any}): CompileAnimationAnimateMetadata {
|
||||
var value = data['value'];
|
||||
var timings = <string|number>value['timings'];
|
||||
var styles = _objFromJson(value['styles'], metadataFromJson);
|
||||
return new CompileAnimationAnimateMetadata(timings, styles);
|
||||
}
|
||||
|
||||
constructor(public timings: string|number = 0,
|
||||
public styles: CompileAnimationStyleMetadata|CompileAnimationKeyframesSequenceMetadata = null) { super(); }
|
||||
|
||||
toJson(): {[key: string]: any} {
|
||||
return {
|
||||
'class': 'AnimationAnimateMetadata',
|
||||
'value': {
|
||||
'timings': this.timings,
|
||||
'styles': _objToJson(this.styles)
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class CompileAnimationWithStepsMetadata extends CompileAnimationMetadata {
|
||||
constructor(public steps: CompileAnimationMetadata[] = null) { super(); }
|
||||
}
|
||||
|
||||
export class CompileAnimationSequenceMetadata extends CompileAnimationWithStepsMetadata {
|
||||
static fromJson(data: {[key: string]: any}): CompileAnimationSequenceMetadata {
|
||||
var steps = _arrayFromJson(data['value'], metadataFromJson);
|
||||
return new CompileAnimationSequenceMetadata(steps);
|
||||
}
|
||||
|
||||
constructor(steps: CompileAnimationMetadata[] = null) {
|
||||
super(steps);
|
||||
}
|
||||
|
||||
toJson(): {[key: string]: any} {
|
||||
return {
|
||||
'class': 'AnimationSequenceMetadata',
|
||||
'value': _arrayToJson(this.steps)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class CompileAnimationGroupMetadata extends CompileAnimationWithStepsMetadata {
|
||||
static fromJson(data: {[key: string]: any}): CompileAnimationGroupMetadata {
|
||||
var steps = _arrayFromJson(data["value"], metadataFromJson);
|
||||
return new CompileAnimationGroupMetadata(steps);
|
||||
}
|
||||
|
||||
constructor(steps: CompileAnimationMetadata[] = null) {
|
||||
super(steps);
|
||||
}
|
||||
|
||||
toJson(): {[key: string]: any} {
|
||||
return {
|
||||
'class': 'AnimationGroupMetadata',
|
||||
'value': _arrayToJson(this.steps)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class CompileIdentifierMetadata implements CompileMetadataWithIdentifier {
|
||||
runtime: any;
|
||||
name: string;
|
||||
@ -477,24 +644,28 @@ export class CompileTemplateMetadata {
|
||||
templateUrl: string;
|
||||
styles: string[];
|
||||
styleUrls: string[];
|
||||
animations: CompileAnimationEntryMetadata[];
|
||||
ngContentSelectors: string[];
|
||||
constructor({encapsulation, template, templateUrl, styles, styleUrls, ngContentSelectors}: {
|
||||
constructor({encapsulation, template, templateUrl, styles, styleUrls, animations, ngContentSelectors}: {
|
||||
encapsulation?: ViewEncapsulation,
|
||||
template?: string,
|
||||
templateUrl?: string,
|
||||
styles?: string[],
|
||||
styleUrls?: string[],
|
||||
ngContentSelectors?: string[]
|
||||
ngContentSelectors?: string[],
|
||||
animations?: CompileAnimationEntryMetadata[]
|
||||
} = {}) {
|
||||
this.encapsulation = isPresent(encapsulation) ? encapsulation : ViewEncapsulation.Emulated;
|
||||
this.template = template;
|
||||
this.templateUrl = templateUrl;
|
||||
this.styles = isPresent(styles) ? styles : [];
|
||||
this.styleUrls = isPresent(styleUrls) ? styleUrls : [];
|
||||
this.animations = isPresent(animations) ? ListWrapper.flatten(animations) : [];
|
||||
this.ngContentSelectors = isPresent(ngContentSelectors) ? ngContentSelectors : [];
|
||||
}
|
||||
|
||||
static fromJson(data: {[key: string]: any}): CompileTemplateMetadata {
|
||||
var animations = <CompileAnimationEntryMetadata[]>_arrayFromJson(data['animations'], metadataFromJson);
|
||||
return new CompileTemplateMetadata({
|
||||
encapsulation: isPresent(data['encapsulation']) ?
|
||||
VIEW_ENCAPSULATION_VALUES[data['encapsulation']] :
|
||||
@ -503,6 +674,7 @@ export class CompileTemplateMetadata {
|
||||
templateUrl: data['templateUrl'],
|
||||
styles: data['styles'],
|
||||
styleUrls: data['styleUrls'],
|
||||
animations: animations,
|
||||
ngContentSelectors: data['ngContentSelectors']
|
||||
});
|
||||
}
|
||||
@ -515,6 +687,7 @@ export class CompileTemplateMetadata {
|
||||
'templateUrl': this.templateUrl,
|
||||
'styles': this.styles,
|
||||
'styleUrls': this.styleUrls,
|
||||
'animations': _objToJson(this.animations),
|
||||
'ngContentSelectors': this.ngContentSelectors
|
||||
};
|
||||
}
|
||||
@ -718,7 +891,7 @@ export function createHostComponentMeta(componentType: CompileTypeMetadata,
|
||||
isHost: true
|
||||
}),
|
||||
template: new CompileTemplateMetadata(
|
||||
{template: template, templateUrl: '', styles: [], styleUrls: [], ngContentSelectors: []}),
|
||||
{template: template, templateUrl: '', styles: [], styleUrls: [], ngContentSelectors: [], animations:[]}),
|
||||
changeDetection: ChangeDetectionStrategy.Default,
|
||||
inputs: [],
|
||||
outputs: [],
|
||||
@ -777,7 +950,15 @@ var _COMPILE_METADATA_FROM_JSON = {
|
||||
'Type': CompileTypeMetadata.fromJson,
|
||||
'Provider': CompileProviderMetadata.fromJson,
|
||||
'Identifier': CompileIdentifierMetadata.fromJson,
|
||||
'Factory': CompileFactoryMetadata.fromJson
|
||||
'Factory': CompileFactoryMetadata.fromJson,
|
||||
'AnimationEntryMetadata': CompileAnimationEntryMetadata.fromJson,
|
||||
'AnimationStateDeclarationMetadata': CompileAnimationStateDeclarationMetadata.fromJson,
|
||||
'AnimationStateTransitionMetadata': CompileAnimationStateTransitionMetadata.fromJson,
|
||||
'AnimationSequenceMetadata': CompileAnimationSequenceMetadata.fromJson,
|
||||
'AnimationGroupMetadata': CompileAnimationGroupMetadata.fromJson,
|
||||
'AnimationAnimateMetadata': CompileAnimationAnimateMetadata.fromJson,
|
||||
'AnimationStyleMetadata': CompileAnimationStyleMetadata.fromJson,
|
||||
'AnimationKeyframesSequenceMetadata': CompileAnimationKeyframesSequenceMetadata.fromJson
|
||||
};
|
||||
|
||||
function _arrayFromJson(obj: any[], fn: (a: {[key: string]: any}) => any): any {
|
||||
|
@ -109,7 +109,8 @@ export class DirectiveNormalizer {
|
||||
templateUrl: templateAbsUrl,
|
||||
styles: allResolvedStyles,
|
||||
styleUrls: allStyleAbsUrls,
|
||||
ngContentSelectors: visitor.ngContentSelectors
|
||||
ngContentSelectors: visitor.ngContentSelectors,
|
||||
animations: templateMeta.animations
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -41,7 +41,13 @@ import {
|
||||
pureProxy7,
|
||||
pureProxy8,
|
||||
pureProxy9,
|
||||
pureProxy10
|
||||
pureProxy10,
|
||||
AnimationKeyframe as AnimationKeyframe_,
|
||||
AnimationStyles as AnimationStyles_,
|
||||
NoOpAnimationPlayer as NoOpAnimationPlayer_,
|
||||
AnimationGroupPlayer as AnimationGroupPlayer_,
|
||||
AnimationSequencePlayer as AnimationSequencePlayer_,
|
||||
AnimationStyleUtil
|
||||
} from '../core_private';
|
||||
|
||||
import {CompileIdentifierMetadata, CompileTokenMetadata} from './compile_metadata';
|
||||
@ -83,6 +89,13 @@ var impCheckBinding = checkBinding;
|
||||
var impCastByValue = castByValue;
|
||||
var impEMPTY_ARRAY = EMPTY_ARRAY;
|
||||
var impEMPTY_MAP = EMPTY_MAP;
|
||||
var impAnimationGroupPlayer = AnimationGroupPlayer_;
|
||||
var impAnimationSequencePlayer = AnimationSequencePlayer_;
|
||||
var impAnimationKeyframe = AnimationKeyframe_;
|
||||
var impAnimationStyles = AnimationStyles_;
|
||||
var impNoOpAnimationPlayer = NoOpAnimationPlayer_;
|
||||
|
||||
var ANIMATION_STYLE_UTIL_ASSET_URL = assetUrl('core','animation/animation_style_util');
|
||||
|
||||
export class Identifiers {
|
||||
static ViewUtils = new CompileIdentifierMetadata(
|
||||
@ -205,6 +218,51 @@ export class Identifiers {
|
||||
moduleUrl: assetUrl('core', 'security'),
|
||||
runtime: SecurityContext,
|
||||
});
|
||||
static AnimationKeyframe = new CompileIdentifierMetadata({
|
||||
name: 'AnimationKeyframe',
|
||||
moduleUrl: assetUrl('core','animation/animation_keyframe'),
|
||||
runtime: impAnimationKeyframe
|
||||
});
|
||||
static AnimationStyles = new CompileIdentifierMetadata({
|
||||
name: 'AnimationStyles',
|
||||
moduleUrl: assetUrl('core','animation/animation_styles'),
|
||||
runtime: impAnimationStyles
|
||||
});
|
||||
static NoOpAnimationPlayer = new CompileIdentifierMetadata({
|
||||
name: 'NoOpAnimationPlayer',
|
||||
moduleUrl: assetUrl('core','animation/animation_player'),
|
||||
runtime: impNoOpAnimationPlayer
|
||||
});
|
||||
static AnimationGroupPlayer = new CompileIdentifierMetadata({
|
||||
name: 'AnimationGroupPlayer',
|
||||
moduleUrl: assetUrl('core','animation/animation_group_player'),
|
||||
runtime: impAnimationGroupPlayer
|
||||
});
|
||||
static AnimationSequencePlayer = new CompileIdentifierMetadata({
|
||||
name: 'AnimationSequencePlayer',
|
||||
moduleUrl: assetUrl('core','animation/animation_sequence_player'),
|
||||
runtime: impAnimationSequencePlayer
|
||||
});
|
||||
static balanceAnimationStyles = new CompileIdentifierMetadata({
|
||||
name: 'balanceAnimationStyles',
|
||||
moduleUrl: ANIMATION_STYLE_UTIL_ASSET_URL,
|
||||
runtime: AnimationStyleUtil.balanceStyles
|
||||
});
|
||||
static balanceAnimationKeyframes = new CompileIdentifierMetadata({
|
||||
name: 'balanceAnimationKeyframes',
|
||||
moduleUrl: ANIMATION_STYLE_UTIL_ASSET_URL,
|
||||
runtime: AnimationStyleUtil.balanceKeyframes
|
||||
});
|
||||
static clearAnimationStyles = new CompileIdentifierMetadata({
|
||||
name: 'clearAnimationStyles',
|
||||
moduleUrl: ANIMATION_STYLE_UTIL_ASSET_URL,
|
||||
runtime: AnimationStyleUtil.clearStyles
|
||||
});
|
||||
static collectAndResolveStyles = new CompileIdentifierMetadata({
|
||||
name: 'collectAndResolveStyles',
|
||||
moduleUrl: ANIMATION_STYLE_UTIL_ASSET_URL,
|
||||
runtime: AnimationStyleUtil.collectAndResolveStyles
|
||||
});
|
||||
}
|
||||
|
||||
export function identifierToken(identifier: CompileIdentifierMetadata): CompileTokenMetadata {
|
||||
|
@ -33,6 +33,7 @@ import {
|
||||
import {StringMapWrapper} from '../src/facade/collection';
|
||||
import {BaseException} from '../src/facade/exceptions';
|
||||
import * as cpl from './compile_metadata';
|
||||
import * as anmd from '@angular/core';
|
||||
import {DirectiveResolver} from './directive_resolver';
|
||||
import {PipeResolver} from './pipe_resolver';
|
||||
import {ViewResolver} from './view_resolver';
|
||||
@ -77,6 +78,44 @@ export class CompileMetadataResolver {
|
||||
return sanitizeIdentifier(identifier);
|
||||
}
|
||||
|
||||
getAnimationEntryMetadata(entry: anmd.AnimationEntryMetadata): cpl.CompileAnimationEntryMetadata {
|
||||
var defs = entry.definitions.map(def => this.getAnimationStateMetadata(def));
|
||||
return new cpl.CompileAnimationEntryMetadata(entry.name, defs);
|
||||
}
|
||||
|
||||
getAnimationStateMetadata(value: anmd.AnimationStateMetadata): cpl.CompileAnimationStateMetadata {
|
||||
if (value instanceof anmd.AnimationStateDeclarationMetadata) {
|
||||
var styles = this.getAnimationStyleMetadata(value.styles);
|
||||
return new cpl.CompileAnimationStateDeclarationMetadata(value.stateNameExpr, styles);
|
||||
} else if (value instanceof anmd.AnimationStateTransitionMetadata) {
|
||||
return new cpl.CompileAnimationStateTransitionMetadata(value.stateChangeExpr, this.getAnimationMetadata(value.animation));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getAnimationStyleMetadata(value: anmd.AnimationStyleMetadata): cpl.CompileAnimationStyleMetadata {
|
||||
return new cpl.CompileAnimationStyleMetadata(value.offset, value.styles);
|
||||
}
|
||||
|
||||
getAnimationMetadata(value: anmd.AnimationMetadata): cpl.CompileAnimationMetadata {
|
||||
if (value instanceof anmd.AnimationStyleMetadata) {
|
||||
return this.getAnimationStyleMetadata(value);
|
||||
} else if (value instanceof anmd.AnimationKeyframesSequenceMetadata) {
|
||||
return new cpl.CompileAnimationKeyframesSequenceMetadata(value.steps.map(entry => this.getAnimationStyleMetadata(entry)));
|
||||
} else if (value instanceof anmd.AnimationAnimateMetadata) {
|
||||
let animateData = <cpl.CompileAnimationStyleMetadata|cpl.CompileAnimationKeyframesSequenceMetadata>this.getAnimationMetadata(value.styles);
|
||||
return new cpl.CompileAnimationAnimateMetadata(value.timings, animateData);
|
||||
} else if (value instanceof anmd.AnimationWithStepsMetadata) {
|
||||
var steps = value.steps.map(step => this.getAnimationMetadata(step));
|
||||
if (value instanceof anmd.AnimationGroupMetadata) {
|
||||
return new cpl.CompileAnimationGroupMetadata(steps);
|
||||
} else {
|
||||
return new cpl.CompileAnimationSequenceMetadata(steps);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getDirectiveMetadata(directiveType: Type): cpl.CompileDirectiveMetadata {
|
||||
var meta = this._directiveCache.get(directiveType);
|
||||
if (isBlank(meta)) {
|
||||
@ -90,12 +129,17 @@ export class CompileMetadataResolver {
|
||||
var cmpMeta = <ComponentMetadata>dirMeta;
|
||||
var viewMeta = this._viewResolver.resolve(directiveType);
|
||||
assertArrayOfStrings('styles', viewMeta.styles);
|
||||
var animations = isPresent(viewMeta.animations)
|
||||
? viewMeta.animations.map(e => this.getAnimationEntryMetadata(e))
|
||||
: null;
|
||||
|
||||
templateMeta = new cpl.CompileTemplateMetadata({
|
||||
encapsulation: viewMeta.encapsulation,
|
||||
template: viewMeta.template,
|
||||
templateUrl: viewMeta.templateUrl,
|
||||
styles: viewMeta.styles,
|
||||
styleUrls: viewMeta.styleUrls
|
||||
styleUrls: viewMeta.styleUrls,
|
||||
animations: animations
|
||||
});
|
||||
changeDetectionStrategy = cmpMeta.changeDetection;
|
||||
if (isPresent(dirMeta.viewProviders)) {
|
||||
|
@ -168,4 +168,4 @@ function _splitSuffix(path: string): string[] {
|
||||
} else {
|
||||
return [path, ''];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -41,6 +41,14 @@ class _InterpretiveAppView extends DebugAppView<any> implements DynamicInstance
|
||||
return super.injectorGet(token, nodeIndex, notFoundResult);
|
||||
}
|
||||
}
|
||||
detachInternal(): void {
|
||||
var m = this.methods.get('detachInternal');
|
||||
if (isPresent(m)) {
|
||||
return m();
|
||||
} else {
|
||||
return super.detachInternal();
|
||||
}
|
||||
}
|
||||
destroyInternal(): void {
|
||||
var m = this.methods.get('destroyInternal');
|
||||
if (isPresent(m)) {
|
||||
|
@ -103,8 +103,7 @@ export class RuntimeCompiler implements ComponentResolver {
|
||||
|
||||
var childPromises = [];
|
||||
compiledTemplate.init(this._compileComponent(compMeta, parsedTemplate, styles,
|
||||
pipes, compilingComponentsPath,
|
||||
childPromises));
|
||||
pipes, compilingComponentsPath, childPromises));
|
||||
return PromiseWrapper.all(childPromises).then((_) => { return compiledTemplate; });
|
||||
});
|
||||
this._compiledTemplateDone.set(cacheKey, done);
|
||||
@ -113,7 +112,8 @@ export class RuntimeCompiler implements ComponentResolver {
|
||||
}
|
||||
|
||||
private _compileComponent(compMeta: CompileDirectiveMetadata, parsedTemplate: TemplateAst[],
|
||||
styles: string[], pipes: CompilePipeMetadata[],
|
||||
styles: string[],
|
||||
pipes: CompilePipeMetadata[],
|
||||
compilingComponentsPath: any[],
|
||||
childPromises: Promise<any>[]): Function {
|
||||
var compileResult = this._viewCompiler.compileComponent(
|
||||
|
@ -213,7 +213,12 @@ export enum PropertyBindingType {
|
||||
/**
|
||||
* A binding to a style rule (e.g. `[style.rule]="expression"`).
|
||||
*/
|
||||
Style
|
||||
Style,
|
||||
|
||||
/**
|
||||
* A binding to an animation reference (e.g. `[animate.key]="expression"`).
|
||||
*/
|
||||
Animation
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -75,12 +75,13 @@ import {ProviderElementContext, ProviderViewContext} from './provider_parser';
|
||||
// Group 4 = "ref-/#"
|
||||
// Group 5 = "on-"
|
||||
// Group 6 = "bindon-"
|
||||
// Group 7 = the identifier after "bind-", "var-/#", or "on-"
|
||||
// Group 8 = identifier inside [()]
|
||||
// Group 9 = identifier inside []
|
||||
// Group 10 = identifier inside ()
|
||||
// Group 7 = "animate-/@"
|
||||
// Group 8 = the identifier after "bind-", "var-/#", or "on-"
|
||||
// Group 9 = identifier inside [()]
|
||||
// Group 10 = identifier inside []
|
||||
// Group 11 = identifier inside ()
|
||||
var BIND_NAME_REGEXP =
|
||||
/^(?:(?:(?:(bind-)|(var-)|(let-)|(ref-|#)|(on-)|(bindon-))(.+))|\[\(([^\)]+)\)\]|\[([^\]]+)\]|\(([^\)]+)\))$/g;
|
||||
/^(?:(?:(?:(bind-)|(var-)|(let-)|(ref-|#)|(on-)|(bindon-)|(animate-|@))(.+))|\[\(([^\)]+)\)\]|\[([^\]]+)\]|\(([^\)]+)\))$/g;
|
||||
|
||||
const TEMPLATE_ELEMENT = 'template';
|
||||
const TEMPLATE_ATTR = 'template';
|
||||
@ -303,6 +304,7 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
var elementOrDirectiveProps: BoundElementOrDirectiveProperty[] = [];
|
||||
var elementOrDirectiveRefs: ElementOrDirectiveRef[] = [];
|
||||
var elementVars: VariableAst[] = [];
|
||||
var animationProps: BoundElementPropertyAst[] = [];
|
||||
var events: BoundEventAst[] = [];
|
||||
|
||||
var templateElementOrDirectiveProps: BoundElementOrDirectiveProperty[] = [];
|
||||
@ -316,7 +318,7 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
|
||||
element.attrs.forEach(attr => {
|
||||
var hasBinding =
|
||||
this._parseAttr(isTemplateElement, attr, matchableAttrs, elementOrDirectiveProps, events,
|
||||
this._parseAttr(isTemplateElement, attr, matchableAttrs, elementOrDirectiveProps, animationProps, events,
|
||||
elementOrDirectiveRefs, elementVars);
|
||||
var hasTemplateBinding = this._parseInlineTemplateBinding(
|
||||
attr, templateMatchableAttrs, templateElementOrDirectiveProps, templateElementVars);
|
||||
@ -337,7 +339,7 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
elementOrDirectiveProps, elementOrDirectiveRefs,
|
||||
element.sourceSpan, references);
|
||||
var elementProps: BoundElementPropertyAst[] =
|
||||
this._createElementPropertyAsts(element.name, elementOrDirectiveProps, directiveAsts);
|
||||
this._createElementPropertyAsts(element.name, elementOrDirectiveProps, directiveAsts).concat(animationProps);
|
||||
var isViewRoot = parent.isTemplateElement || hasInlineTemplates;
|
||||
var providerContext =
|
||||
new ProviderElementContext(this.providerViewContext, parent.providerContext, isViewRoot,
|
||||
@ -439,7 +441,9 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
|
||||
private _parseAttr(isTemplateElement: boolean, attr: HtmlAttrAst,
|
||||
targetMatchableAttrs: string[][],
|
||||
targetProps: BoundElementOrDirectiveProperty[], targetEvents: BoundEventAst[],
|
||||
targetProps: BoundElementOrDirectiveProperty[],
|
||||
targetAnimationProps: BoundElementPropertyAst[],
|
||||
targetEvents: BoundEventAst[],
|
||||
targetRefs: ElementOrDirectiveRef[], targetVars: VariableAst[]): boolean {
|
||||
var attrName = this._normalizeAttributeName(attr.name);
|
||||
var attrValue = attr.value;
|
||||
@ -448,11 +452,11 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
if (isPresent(bindParts)) {
|
||||
hasBinding = true;
|
||||
if (isPresent(bindParts[1])) { // match: bind-prop
|
||||
this._parseProperty(bindParts[7], attrValue, attr.sourceSpan, targetMatchableAttrs,
|
||||
this._parseProperty(bindParts[8], attrValue, attr.sourceSpan, targetMatchableAttrs,
|
||||
targetProps);
|
||||
|
||||
} else if (isPresent(bindParts[2])) { // match: var-name / var-name="iden"
|
||||
var identifier = bindParts[7];
|
||||
var identifier = bindParts[8];
|
||||
if (isTemplateElement) {
|
||||
this._reportError(`"var-" on <template> elements is deprecated. Use "let-" instead!`,
|
||||
attr.sourceSpan, ParseErrorLevel.WARNING);
|
||||
@ -465,38 +469,41 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
|
||||
} else if (isPresent(bindParts[3])) { // match: let-name
|
||||
if (isTemplateElement) {
|
||||
var identifier = bindParts[7];
|
||||
var identifier = bindParts[8];
|
||||
this._parseVariable(identifier, attrValue, attr.sourceSpan, targetVars);
|
||||
} else {
|
||||
this._reportError(`"let-" is only supported on template elements.`, attr.sourceSpan);
|
||||
}
|
||||
|
||||
} else if (isPresent(bindParts[4])) { // match: ref- / #iden
|
||||
var identifier = bindParts[7];
|
||||
var identifier = bindParts[8];
|
||||
this._parseReference(identifier, attrValue, attr.sourceSpan, targetRefs);
|
||||
|
||||
} else if (isPresent(bindParts[5])) { // match: on-event
|
||||
this._parseEvent(bindParts[7], attrValue, attr.sourceSpan, targetMatchableAttrs,
|
||||
this._parseEvent(bindParts[8], attrValue, attr.sourceSpan, targetMatchableAttrs,
|
||||
targetEvents);
|
||||
|
||||
} else if (isPresent(bindParts[6])) { // match: bindon-prop
|
||||
this._parseProperty(bindParts[7], attrValue, attr.sourceSpan, targetMatchableAttrs,
|
||||
targetProps);
|
||||
this._parseAssignmentEvent(bindParts[7], attrValue, attr.sourceSpan, targetMatchableAttrs,
|
||||
targetEvents);
|
||||
|
||||
} else if (isPresent(bindParts[8])) { // match: [(expr)]
|
||||
this._parseProperty(bindParts[8], attrValue, attr.sourceSpan, targetMatchableAttrs,
|
||||
targetProps);
|
||||
this._parseAssignmentEvent(bindParts[8], attrValue, attr.sourceSpan, targetMatchableAttrs,
|
||||
targetEvents);
|
||||
|
||||
} else if (isPresent(bindParts[9])) { // match: [expr]
|
||||
} else if (isPresent(bindParts[7])) { // match: animate-name
|
||||
this._parseAnimation(bindParts[8], attrValue, attr.sourceSpan, targetMatchableAttrs,
|
||||
targetAnimationProps);
|
||||
} else if (isPresent(bindParts[9])) { // match: [(expr)]
|
||||
this._parseProperty(bindParts[9], attrValue, attr.sourceSpan, targetMatchableAttrs,
|
||||
targetProps);
|
||||
this._parseAssignmentEvent(bindParts[9], attrValue, attr.sourceSpan, targetMatchableAttrs,
|
||||
targetEvents);
|
||||
|
||||
} else if (isPresent(bindParts[10])) { // match: (event)
|
||||
this._parseEvent(bindParts[10], attrValue, attr.sourceSpan, targetMatchableAttrs,
|
||||
} else if (isPresent(bindParts[10])) { // match: [expr]
|
||||
this._parseProperty(bindParts[10], attrValue, attr.sourceSpan, targetMatchableAttrs,
|
||||
targetProps);
|
||||
|
||||
} else if (isPresent(bindParts[11])) { // match: (event)
|
||||
this._parseEvent(bindParts[11], attrValue, attr.sourceSpan, targetMatchableAttrs,
|
||||
targetEvents);
|
||||
}
|
||||
} else {
|
||||
@ -536,6 +543,14 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
targetMatchableAttrs, targetProps);
|
||||
}
|
||||
|
||||
private _parseAnimation(name: string, expression: string, sourceSpan: ParseSourceSpan,
|
||||
targetMatchableAttrs: string[][],
|
||||
targetAnimationProps: BoundElementPropertyAst[]) {
|
||||
var ast = this._parseBinding(expression, sourceSpan);
|
||||
targetMatchableAttrs.push([name, ast.source]);
|
||||
targetAnimationProps.push(new BoundElementPropertyAst(name, PropertyBindingType.Animation, SecurityContext.NONE, ast, null, sourceSpan));
|
||||
}
|
||||
|
||||
private _parsePropertyInterpolation(name: string, value: string, sourceSpan: ParseSourceSpan,
|
||||
targetMatchableAttrs: string[][],
|
||||
targetProps: BoundElementOrDirectiveProperty[]): boolean {
|
||||
@ -716,8 +731,8 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
bindingType = PropertyBindingType.Property;
|
||||
if (!this._schemaRegistry.hasProperty(elementName, boundPropertyName)) {
|
||||
this._reportError(
|
||||
`Can't bind to '${boundPropertyName}' since it isn't a known native property`,
|
||||
sourceSpan);
|
||||
`Can't bind to '${boundPropertyName}' since it isn't a known native property`,
|
||||
sourceSpan);
|
||||
}
|
||||
} else {
|
||||
if (parts[0] == ATTRIBUTE_PREFIX) {
|
||||
|
@ -27,6 +27,8 @@ import {CompilerConfig} from '../config';
|
||||
import {CompileBinding} from './compile_binding';
|
||||
import {Identifiers} from '../identifiers';
|
||||
|
||||
import {CompiledAnimation} from '../animation/animation_compiler';
|
||||
|
||||
export class CompileView implements NameResolver {
|
||||
public viewType: ViewType;
|
||||
public viewQueries: CompileTokenMap<CompileQuery[]>;
|
||||
@ -48,6 +50,7 @@ export class CompileView implements NameResolver {
|
||||
public afterContentLifecycleCallbacksMethod: CompileMethod;
|
||||
public afterViewLifecycleCallbacksMethod: CompileMethod;
|
||||
public destroyMethod: CompileMethod;
|
||||
public detachMethod: CompileMethod;
|
||||
public eventHandlerMethods: o.ClassMethod[] = [];
|
||||
|
||||
public fields: o.ClassField[] = [];
|
||||
@ -66,13 +69,17 @@ export class CompileView implements NameResolver {
|
||||
public literalArrayCount = 0;
|
||||
public literalMapCount = 0;
|
||||
public pipeCount = 0;
|
||||
public animations = new Map<string, CompiledAnimation>();
|
||||
|
||||
public componentContext: o.Expression;
|
||||
|
||||
constructor(public component: CompileDirectiveMetadata, public genConfig: CompilerConfig,
|
||||
public pipeMetas: CompilePipeMetadata[], public styles: o.Expression,
|
||||
public pipeMetas: CompilePipeMetadata[],
|
||||
public styles: o.Expression,
|
||||
animations: CompiledAnimation[],
|
||||
public viewIndex: number, public declarationElement: CompileElement,
|
||||
public templateVariableBindings: string[][]) {
|
||||
animations.forEach(entry => this.animations.set(entry.name, entry));
|
||||
this.createMethod = new CompileMethod(this);
|
||||
this.injectorGetMethod = new CompileMethod(this);
|
||||
this.updateContentQueriesMethod = new CompileMethod(this);
|
||||
@ -84,6 +91,7 @@ export class CompileView implements NameResolver {
|
||||
this.afterContentLifecycleCallbacksMethod = new CompileMethod(this);
|
||||
this.afterViewLifecycleCallbacksMethod = new CompileMethod(this);
|
||||
this.destroyMethod = new CompileMethod(this);
|
||||
this.detachMethod = new CompileMethod(this);
|
||||
|
||||
this.viewType = getViewType(component, viewIndex);
|
||||
this.className = `_View_${component.type.name}${viewIndex}`;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {SecurityContext} from '../../core_private';
|
||||
import {LifecycleHooks, isDefaultChangeDetectionStrategy} from '../../core_private';
|
||||
import {EMPTY_STATE as EMPTY_ANIMATION_STATE, LifecycleHooks, isDefaultChangeDetectionStrategy} from '../../core_private';
|
||||
|
||||
import {isBlank, isPresent} from '../../src/facade/lang';
|
||||
|
||||
@ -24,6 +24,7 @@ import {camelCaseToDashCase} from '../util';
|
||||
import {convertCdExpressionToIr} from './expression_converter';
|
||||
|
||||
import {CompileBinding} from './compile_binding';
|
||||
import {BaseException} from '@angular/core';
|
||||
|
||||
|
||||
function createBindFieldExpr(exprIndex: number): o.ReadPropExpr {
|
||||
@ -95,36 +96,92 @@ function bindAndWriteToRenderer(boundProps: BoundElementPropertyAst[], context:
|
||||
var fieldExpr = createBindFieldExpr(bindingIndex);
|
||||
var currValExpr = createCurrValueExpr(bindingIndex);
|
||||
var renderMethod: string;
|
||||
var oldRenderValue: o.Expression = sanitizedValue(boundProp, fieldExpr);
|
||||
var renderValue: o.Expression = sanitizedValue(boundProp, currValExpr);
|
||||
var updateStmts = [];
|
||||
switch (boundProp.type) {
|
||||
case PropertyBindingType.Property:
|
||||
renderMethod = 'setElementProperty';
|
||||
if (view.genConfig.logBindingUpdate) {
|
||||
updateStmts.push(logBindingUpdateStmt(renderNode, boundProp.name, currValExpr));
|
||||
updateStmts.push(logBindingUpdateStmt(renderNode, boundProp.name, renderValue));
|
||||
}
|
||||
updateStmts.push(
|
||||
o.THIS_EXPR.prop('renderer')
|
||||
.callMethod('setElementProperty', [renderNode, o.literal(boundProp.name), renderValue])
|
||||
.toStmt()
|
||||
);
|
||||
break;
|
||||
case PropertyBindingType.Attribute:
|
||||
renderMethod = 'setElementAttribute';
|
||||
renderValue =
|
||||
renderValue.isBlank().conditional(o.NULL_EXPR, renderValue.callMethod('toString', []));
|
||||
renderValue = renderValue.isBlank().conditional(o.NULL_EXPR, renderValue.callMethod('toString', []));
|
||||
updateStmts.push(
|
||||
o.THIS_EXPR.prop('renderer')
|
||||
.callMethod('setElementAttribute', [renderNode, o.literal(boundProp.name), renderValue])
|
||||
.toStmt()
|
||||
);
|
||||
break;
|
||||
case PropertyBindingType.Class:
|
||||
renderMethod = 'setElementClass';
|
||||
updateStmts.push(
|
||||
o.THIS_EXPR.prop('renderer')
|
||||
.callMethod('setElementClass', [renderNode, o.literal(boundProp.name), renderValue])
|
||||
.toStmt()
|
||||
);
|
||||
break;
|
||||
case PropertyBindingType.Style:
|
||||
renderMethod = 'setElementStyle';
|
||||
var strValue: o.Expression = renderValue.callMethod('toString', []);
|
||||
if (isPresent(boundProp.unit)) {
|
||||
strValue = strValue.plus(o.literal(boundProp.unit));
|
||||
}
|
||||
renderValue = renderValue.isBlank().conditional(o.NULL_EXPR, strValue);
|
||||
updateStmts.push(
|
||||
o.THIS_EXPR.prop('renderer')
|
||||
.callMethod('setElementStyle', [renderNode, o.literal(boundProp.name), renderValue])
|
||||
.toStmt()
|
||||
);
|
||||
break;
|
||||
case PropertyBindingType.Animation:
|
||||
var animationName = boundProp.name;
|
||||
var animation = view.componentView.animations.get(animationName);
|
||||
if (!isPresent(animation)) {
|
||||
throw new BaseException(`Internal Error: couldn't find an animation entry for ${boundProp.name}`);
|
||||
}
|
||||
|
||||
// it's important to normalize the void value as `void` explicitly
|
||||
// so that the styles data can be obtained from the stringmap
|
||||
var emptyStateValue = o.literal(EMPTY_ANIMATION_STATE);
|
||||
|
||||
// void => ...
|
||||
var oldRenderVar = o.variable('oldRenderVar');
|
||||
updateStmts.push(oldRenderVar.set(oldRenderValue).toDeclStmt());
|
||||
updateStmts.push(
|
||||
new o.IfStmt(oldRenderVar.equals(o.importExpr(Identifiers.uninitialized)), [
|
||||
oldRenderVar.set(emptyStateValue).toStmt()
|
||||
]));
|
||||
|
||||
// ... => void
|
||||
var newRenderVar = o.variable('newRenderVar');
|
||||
updateStmts.push(newRenderVar.set(renderValue).toDeclStmt());
|
||||
updateStmts.push(
|
||||
new o.IfStmt(newRenderVar.equals(o.importExpr(Identifiers.uninitialized)), [
|
||||
newRenderVar.set(emptyStateValue).toStmt()
|
||||
]));
|
||||
|
||||
updateStmts.push(
|
||||
animation.fnVariable.callFn([
|
||||
o.THIS_EXPR,
|
||||
renderNode,
|
||||
oldRenderVar,
|
||||
newRenderVar
|
||||
]).toStmt());
|
||||
|
||||
view.detachMethod.addStmt(
|
||||
animation.fnVariable.callFn([
|
||||
o.THIS_EXPR,
|
||||
renderNode,
|
||||
oldRenderValue,
|
||||
emptyStateValue
|
||||
]).toStmt());
|
||||
|
||||
break;
|
||||
}
|
||||
updateStmts.push(
|
||||
o.THIS_EXPR.prop('renderer')
|
||||
.callMethod(renderMethod, [renderNode, o.literal(boundProp.name), renderValue])
|
||||
.toStmt());
|
||||
|
||||
bind(view, currValExpr, fieldExpr, boundProp.value, context, updateStmts,
|
||||
view.detectChangesRenderPropertiesMethod);
|
||||
|
@ -45,6 +45,8 @@ import {
|
||||
CompileTokenMetadata
|
||||
} from '../compile_metadata';
|
||||
|
||||
import {AnimationCompiler} from '../animation/animation_compiler';
|
||||
|
||||
const IMPLICIT_TEMPLATE_VAR = '\$implicit';
|
||||
const CLASS_ATTR = 'class';
|
||||
const STYLE_ATTR = 'style';
|
||||
@ -79,6 +81,8 @@ export function finishView(view: CompileView, targetStatements: o.Statement[]) {
|
||||
class ViewBuilderVisitor implements TemplateAstVisitor {
|
||||
nestedViewCount: number = 0;
|
||||
|
||||
private _animationCompiler = new AnimationCompiler();
|
||||
|
||||
constructor(public view: CompileView, public targetDependencies: ViewCompileDependency[]) {}
|
||||
|
||||
private _isRootNode(parent: CompileElement): boolean { return parent.view !== this.view; }
|
||||
@ -270,9 +274,11 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
|
||||
ast.providers, ast.hasViewContainer, true, ast.references);
|
||||
this.view.nodes.push(compileElement);
|
||||
|
||||
var compiledAnimations = this._animationCompiler.compileComponent(this.view.component);
|
||||
|
||||
this.nestedViewCount++;
|
||||
var embeddedView = new CompileView(
|
||||
this.view.component, this.view.genConfig, this.view.pipeMetas, o.NULL_EXPR,
|
||||
this.view.component, this.view.genConfig, this.view.pipeMetas, o.NULL_EXPR, compiledAnimations,
|
||||
this.view.viewIndex + this.nestedViewCount, compileElement, templateVariableBindings);
|
||||
this.nestedViewCount += buildView(embeddedView, ast.children, this.targetDependencies);
|
||||
|
||||
@ -423,7 +429,8 @@ function createViewClass(view: CompileView, renderCompTypeVar: o.ReadVarExpr,
|
||||
[new o.FnParam(DetectChangesVars.throwOnChange.name, o.BOOL_TYPE)],
|
||||
generateDetectChangesMethod(view)),
|
||||
new o.ClassMethod('dirtyParentQueriesInternal', [], view.dirtyParentQueriesMethod.finish()),
|
||||
new o.ClassMethod('destroyInternal', [], view.destroyMethod.finish())
|
||||
new o.ClassMethod('destroyInternal', [], view.destroyMethod.finish()),
|
||||
new o.ClassMethod('detachInternal', [], view.detachMethod.finish())
|
||||
].concat(view.eventHandlerMethods);
|
||||
var superClass = view.genConfig.genDebugInfo ? Identifiers.DebugAppView : Identifiers.AppView;
|
||||
var viewClass = new o.ClassStmt(view.className, o.importExpr(superClass, [getContextType(view)]),
|
||||
|
@ -8,6 +8,8 @@ import {CompileDirectiveMetadata, CompilePipeMetadata} from '../compile_metadata
|
||||
import {TemplateAst} from '../template_ast';
|
||||
import {CompilerConfig} from '../config';
|
||||
|
||||
import {AnimationCompiler} from '../animation/animation_compiler';
|
||||
|
||||
export class ViewCompileResult {
|
||||
constructor(public statements: o.Statement[], public viewFactoryVar: string,
|
||||
public dependencies: ViewCompileDependency[]) {}
|
||||
@ -15,13 +17,19 @@ export class ViewCompileResult {
|
||||
|
||||
@Injectable()
|
||||
export class ViewCompiler {
|
||||
private _animationCompiler = new AnimationCompiler();
|
||||
constructor(private _genConfig: CompilerConfig) {}
|
||||
|
||||
compileComponent(component: CompileDirectiveMetadata, template: TemplateAst[],
|
||||
styles: o.Expression, pipes: CompilePipeMetadata[]): ViewCompileResult {
|
||||
var statements = [];
|
||||
var dependencies = [];
|
||||
var view = new CompileView(component, this._genConfig, pipes, styles, 0,
|
||||
var compiledAnimations = this._animationCompiler.compileComponent(component);
|
||||
var statements = [];
|
||||
compiledAnimations.map(entry => {
|
||||
statements.push(entry.statesMapStatement);
|
||||
statements.push(entry.fnStatement);
|
||||
});
|
||||
var view = new CompileView(component, this._genConfig, pipes, styles, compiledAnimations, 0,
|
||||
CompileElement.createNull(), []);
|
||||
buildView(view, template, dependencies);
|
||||
// Need to separate binding from creation to be able to refer to
|
||||
|
@ -91,7 +91,8 @@ export class ViewResolver {
|
||||
pipes: compMeta.pipes,
|
||||
encapsulation: compMeta.encapsulation,
|
||||
styles: compMeta.styles,
|
||||
styleUrls: compMeta.styleUrls
|
||||
styleUrls: compMeta.styleUrls,
|
||||
animations: compMeta.animations
|
||||
});
|
||||
}
|
||||
} else {
|
||||
|
Reference in New Issue
Block a user