refactor(compiler): cleanup and preparation for integration
- Rename `DirectiveMetadata` into `CompileDirectiveMetadata`, merge with `NormalizedDirectiveMetadata` and remove `ChangeDetectionMetadata` - Store change detector factories not as array but directly at the `CompiledTemplate` or the embedded template to make instantiation easier later on - Already analyze variable values and map them to `Directive.exportAs` - Keep the directive sort order as specified in the `@View()` annotation - Allow to clear the runtime cache in `StyleCompiler` and `TemplateCompiler` - Ignore `script` elements to match the semantics of the current compiler - Make all components dynamically loadable and remove the previously introduced property `@Component#dynamicLoadable` for now until we find a better option to configure this - Don’t allow to specify bindings in `@View#directives` and `@View#pipes` as this was never supported by the transformer (see below for the breaking change) BREAKING CHANGE: - don't support DI bindings in `@View#directives` and `@View@pipes` any more in preparation of integrating the new compiler. Use `@Directive#bindings` to reexport directives under a different token instead. Part of #3605 Closes #4314
This commit is contained in:
		
							parent
							
								
									eb7839e0ec
								
							
						
					
					
						commit
						cc0c30484f
					
				| @ -14,7 +14,7 @@ import { | |||||||
|   ASTWithSource |   ASTWithSource | ||||||
| } from 'angular2/src/core/change_detection/change_detection'; | } from 'angular2/src/core/change_detection/change_detection'; | ||||||
| 
 | 
 | ||||||
| import {NormalizedDirectiveMetadata, TypeMetadata} from './directive_metadata'; | import {CompileDirectiveMetadata, CompileTypeMetadata} from './directive_metadata'; | ||||||
| import { | import { | ||||||
|   TemplateAst, |   TemplateAst, | ||||||
|   ElementAst, |   ElementAst, | ||||||
| @ -32,9 +32,10 @@ import { | |||||||
|   AttrAst, |   AttrAst, | ||||||
|   TextAst |   TextAst | ||||||
| } from './template_ast'; | } from './template_ast'; | ||||||
|  | import {LifecycleHooks} from 'angular2/src/core/compiler/interfaces'; | ||||||
| 
 | 
 | ||||||
| export function createChangeDetectorDefinitions( | export function createChangeDetectorDefinitions( | ||||||
|     componentType: TypeMetadata, componentStrategy: ChangeDetectionStrategy, |     componentType: CompileTypeMetadata, componentStrategy: ChangeDetectionStrategy, | ||||||
|     genConfig: ChangeDetectorGenConfig, parsedTemplate: TemplateAst[]): ChangeDetectorDefinition[] { |     genConfig: ChangeDetectorGenConfig, parsedTemplate: TemplateAst[]): ChangeDetectorDefinition[] { | ||||||
|   var pvVisitors = []; |   var pvVisitors = []; | ||||||
|   var visitor = new ProtoViewVisitor(null, pvVisitors, componentStrategy); |   var visitor = new ProtoViewVisitor(null, pvVisitors, componentStrategy); | ||||||
| @ -59,7 +60,9 @@ class ProtoViewVisitor implements TemplateAstVisitor { | |||||||
| 
 | 
 | ||||||
|   visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any { |   visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any { | ||||||
|     this.boundElementCount++; |     this.boundElementCount++; | ||||||
|     templateVisitAll(this, ast.directives); |     for (var i = 0; i < ast.directives.length; i++) { | ||||||
|  |       ast.directives[i].visit(this, i); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     var childVisitor = |     var childVisitor = | ||||||
|         new ProtoViewVisitor(this, this.allVisitors, ChangeDetectionStrategy.Default); |         new ProtoViewVisitor(this, this.allVisitors, ChangeDetectionStrategy.Default); | ||||||
| @ -76,7 +79,7 @@ class ProtoViewVisitor implements TemplateAstVisitor { | |||||||
|     } |     } | ||||||
|     templateVisitAll(this, ast.properties, null); |     templateVisitAll(this, ast.properties, null); | ||||||
|     templateVisitAll(this, ast.events); |     templateVisitAll(this, ast.events); | ||||||
|     templateVisitAll(this, ast.vars); |     templateVisitAll(this, ast.exportAsVars); | ||||||
|     for (var i = 0; i < ast.directives.length; i++) { |     for (var i = 0; i < ast.directives.length; i++) { | ||||||
|       ast.directives[i].visit(this, i); |       ast.directives[i].visit(this, i); | ||||||
|     } |     } | ||||||
| @ -94,8 +97,8 @@ class ProtoViewVisitor implements TemplateAstVisitor { | |||||||
|   visitEvent(ast: BoundEventAst, directiveRecord: DirectiveRecord): any { |   visitEvent(ast: BoundEventAst, directiveRecord: DirectiveRecord): any { | ||||||
|     var bindingRecord = |     var bindingRecord = | ||||||
|         isPresent(directiveRecord) ? |         isPresent(directiveRecord) ? | ||||||
|             BindingRecord.createForHostEvent(ast.handler, ast.name, directiveRecord) : |             BindingRecord.createForHostEvent(ast.handler, ast.fullName, directiveRecord) : | ||||||
|             BindingRecord.createForEvent(ast.handler, ast.name, this.boundElementCount - 1); |             BindingRecord.createForEvent(ast.handler, ast.fullName, this.boundElementCount - 1); | ||||||
|     this.eventRecords.push(bindingRecord); |     this.eventRecords.push(bindingRecord); | ||||||
|     return null; |     return null; | ||||||
|   } |   } | ||||||
| @ -138,17 +141,20 @@ class ProtoViewVisitor implements TemplateAstVisitor { | |||||||
|   visitDirective(ast: DirectiveAst, directiveIndexAsNumber: number): any { |   visitDirective(ast: DirectiveAst, directiveIndexAsNumber: number): any { | ||||||
|     var directiveIndex = new DirectiveIndex(this.boundElementCount - 1, directiveIndexAsNumber); |     var directiveIndex = new DirectiveIndex(this.boundElementCount - 1, directiveIndexAsNumber); | ||||||
|     var directiveMetadata = ast.directive; |     var directiveMetadata = ast.directive; | ||||||
|     var changeDetectionMeta = directiveMetadata.changeDetection; |  | ||||||
|     var directiveRecord = new DirectiveRecord({ |     var directiveRecord = new DirectiveRecord({ | ||||||
|       directiveIndex: directiveIndex, |       directiveIndex: directiveIndex, | ||||||
|       callAfterContentInit: changeDetectionMeta.callAfterContentInit, |       callAfterContentInit: | ||||||
|       callAfterContentChecked: changeDetectionMeta.callAfterContentChecked, |           directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.AfterContentInit) !== -1, | ||||||
|       callAfterViewInit: changeDetectionMeta.callAfterViewInit, |       callAfterContentChecked: | ||||||
|       callAfterViewChecked: changeDetectionMeta.callAfterViewChecked, |           directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.AfterContentChecked) !== -1, | ||||||
|       callOnChanges: changeDetectionMeta.callOnChanges, |       callAfterViewInit: | ||||||
|       callDoCheck: changeDetectionMeta.callDoCheck, |           directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.AfterViewInit) !== -1, | ||||||
|       callOnInit: changeDetectionMeta.callOnInit, |       callAfterViewChecked: | ||||||
|       changeDetection: changeDetectionMeta.changeDetection |           directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.AfterViewChecked) !== -1, | ||||||
|  |       callOnChanges: directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.OnChanges) !== -1, | ||||||
|  |       callDoCheck: directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.DoCheck) !== -1, | ||||||
|  |       callOnInit: directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.OnInit) !== -1, | ||||||
|  |       changeDetection: directiveMetadata.changeDetection | ||||||
|     }); |     }); | ||||||
|     this.directiveRecords.push(directiveRecord); |     this.directiveRecords.push(directiveRecord); | ||||||
| 
 | 
 | ||||||
| @ -165,6 +171,7 @@ class ProtoViewVisitor implements TemplateAstVisitor { | |||||||
|     } |     } | ||||||
|     templateVisitAll(this, ast.hostProperties, directiveRecord); |     templateVisitAll(this, ast.hostProperties, directiveRecord); | ||||||
|     templateVisitAll(this, ast.hostEvents, directiveRecord); |     templateVisitAll(this, ast.hostEvents, directiveRecord); | ||||||
|  |     templateVisitAll(this, ast.exportAsVars); | ||||||
|     return null; |     return null; | ||||||
|   } |   } | ||||||
|   visitDirectiveProperty(ast: BoundDirectivePropertyAst, directiveRecord: DirectiveRecord): any { |   visitDirectiveProperty(ast: BoundDirectivePropertyAst, directiveRecord: DirectiveRecord): any { | ||||||
| @ -178,7 +185,7 @@ class ProtoViewVisitor implements TemplateAstVisitor { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| function createChangeDefinitions(pvVisitors: ProtoViewVisitor[], componentType: TypeMetadata, | function createChangeDefinitions(pvVisitors: ProtoViewVisitor[], componentType: CompileTypeMetadata, | ||||||
|                                  genConfig: ChangeDetectorGenConfig): ChangeDetectorDefinition[] { |                                  genConfig: ChangeDetectorGenConfig): ChangeDetectorDefinition[] { | ||||||
|   var pvVariableNames = _collectNestedProtoViewsVariableNames(pvVisitors); |   var pvVariableNames = _collectNestedProtoViewsVariableNames(pvVisitors); | ||||||
|   return pvVisitors.map(pvVisitor => { |   return pvVisitors.map(pvVisitor => { | ||||||
| @ -202,6 +209,7 @@ function _collectNestedProtoViewsVariableNames(pvVisitors: ProtoViewVisitor[]): | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| function _protoViewId(hostComponentType: TypeMetadata, pvIndex: number, viewType: string): string { | function _protoViewId(hostComponentType: CompileTypeMetadata, pvIndex: number, viewType: string): | ||||||
|  |     string { | ||||||
|   return `${hostComponentType.name}_${viewType}_${pvIndex}`; |   return `${hostComponentType.name}_${viewType}_${pvIndex}`; | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| import {TypeMetadata} from './directive_metadata'; | import {CompileTypeMetadata} from './directive_metadata'; | ||||||
| import {SourceExpression, moduleRef} from './source_module'; | import {SourceExpressions, moduleRef} from './source_module'; | ||||||
| import { | import { | ||||||
|   ChangeDetectorJITGenerator |   ChangeDetectorJITGenerator | ||||||
| } from 'angular2/src/core/change_detection/change_detection_jit_generator'; | } from 'angular2/src/core/change_detection/change_detection_jit_generator'; | ||||||
| @ -32,7 +32,7 @@ var PREGEN_PROTO_CHANGE_DETECTOR_MODULE = | |||||||
| export class ChangeDetectionCompiler { | export class ChangeDetectionCompiler { | ||||||
|   constructor(private _genConfig: ChangeDetectorGenConfig) {} |   constructor(private _genConfig: ChangeDetectorGenConfig) {} | ||||||
| 
 | 
 | ||||||
|   compileComponentRuntime(componentType: TypeMetadata, strategy: ChangeDetectionStrategy, |   compileComponentRuntime(componentType: CompileTypeMetadata, strategy: ChangeDetectionStrategy, | ||||||
|                           parsedTemplate: TemplateAst[]): Function[] { |                           parsedTemplate: TemplateAst[]): Function[] { | ||||||
|     var changeDetectorDefinitions = |     var changeDetectorDefinitions = | ||||||
|         createChangeDetectorDefinitions(componentType, strategy, this._genConfig, parsedTemplate); |         createChangeDetectorDefinitions(componentType, strategy, this._genConfig, parsedTemplate); | ||||||
| @ -51,8 +51,8 @@ export class ChangeDetectionCompiler { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   compileComponentCodeGen(componentType: TypeMetadata, strategy: ChangeDetectionStrategy, |   compileComponentCodeGen(componentType: CompileTypeMetadata, strategy: ChangeDetectionStrategy, | ||||||
|                           parsedTemplate: TemplateAst[]): SourceExpression { |                           parsedTemplate: TemplateAst[]): SourceExpressions { | ||||||
|     var changeDetectorDefinitions = |     var changeDetectorDefinitions = | ||||||
|         createChangeDetectorDefinitions(componentType, strategy, this._genConfig, parsedTemplate); |         createChangeDetectorDefinitions(componentType, strategy, this._genConfig, parsedTemplate); | ||||||
|     var factories = []; |     var factories = []; | ||||||
| @ -75,7 +75,6 @@ export class ChangeDetectionCompiler { | |||||||
|         return codegen.generateSource(); |         return codegen.generateSource(); | ||||||
|       } |       } | ||||||
|     }); |     }); | ||||||
|     var expression = `[ ${factories.join(',')} ]`; |     return new SourceExpressions(sourceParts, factories); | ||||||
|     return new SourceExpression(sourceParts, expression); |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,4 +1,5 @@ | |||||||
| import {isPresent, Type} from 'angular2/src/core/facade/lang'; | import {isPresent, isBlank, Type, isString} from 'angular2/src/core/facade/lang'; | ||||||
|  | import {SetWrapper, StringMapWrapper, ListWrapper} from 'angular2/src/core/facade/collection'; | ||||||
| import { | import { | ||||||
|   TemplateCmd, |   TemplateCmd, | ||||||
|   text, |   text, | ||||||
| @ -25,8 +26,8 @@ import { | |||||||
|   BoundDirectivePropertyAst, |   BoundDirectivePropertyAst, | ||||||
|   templateVisitAll |   templateVisitAll | ||||||
| } from './template_ast'; | } from './template_ast'; | ||||||
| import {TypeMetadata, NormalizedDirectiveMetadata} from './directive_metadata'; | import {CompileTypeMetadata, CompileDirectiveMetadata} from './directive_metadata'; | ||||||
| import {SourceExpression, moduleRef} from './source_module'; | import {SourceExpressions, SourceExpression, moduleRef} from './source_module'; | ||||||
| 
 | 
 | ||||||
| import {ViewEncapsulation} from 'angular2/src/core/render/api'; | import {ViewEncapsulation} from 'angular2/src/core/render/api'; | ||||||
| import {shimHostAttribute, shimContentAttribute} from './style_compiler'; | import {shimHostAttribute, shimContentAttribute} from './style_compiler'; | ||||||
| @ -34,21 +35,25 @@ import {escapeSingleQuoteString} from './util'; | |||||||
| import {Injectable} from 'angular2/src/core/di'; | import {Injectable} from 'angular2/src/core/di'; | ||||||
| 
 | 
 | ||||||
| export var TEMPLATE_COMMANDS_MODULE_REF = moduleRef('angular2/src/core/compiler/template_commands'); | export var TEMPLATE_COMMANDS_MODULE_REF = moduleRef('angular2/src/core/compiler/template_commands'); | ||||||
|  | const IMPLICIT_VAR = '%implicit'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class CommandCompiler { | export class CommandCompiler { | ||||||
|   compileComponentRuntime(component: NormalizedDirectiveMetadata, template: TemplateAst[], |   compileComponentRuntime(component: CompileDirectiveMetadata, template: TemplateAst[], | ||||||
|  |                           changeDetectorFactories: Function[], | ||||||
|                           componentTemplateFactory: Function): TemplateCmd[] { |                           componentTemplateFactory: Function): TemplateCmd[] { | ||||||
|     var visitor = |     var visitor = new CommandBuilderVisitor( | ||||||
|         new CommandBuilderVisitor(new RuntimeCommandFactory(componentTemplateFactory), component); |         new RuntimeCommandFactory(componentTemplateFactory, changeDetectorFactories), component, 0); | ||||||
|     templateVisitAll(visitor, template); |     templateVisitAll(visitor, template); | ||||||
|     return visitor.result; |     return visitor.result; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   compileComponentCodeGen(component: NormalizedDirectiveMetadata, template: TemplateAst[], |   compileComponentCodeGen(component: CompileDirectiveMetadata, template: TemplateAst[], | ||||||
|  |                           changeDetectorFactoryExpressions: string[], | ||||||
|                           componentTemplateFactory: Function): SourceExpression { |                           componentTemplateFactory: Function): SourceExpression { | ||||||
|     var visitor = |     var visitor = new CommandBuilderVisitor( | ||||||
|         new CommandBuilderVisitor(new CodegenCommandFactory(componentTemplateFactory), component); |         new CodegenCommandFactory(componentTemplateFactory, changeDetectorFactoryExpressions), | ||||||
|  |         component, 0); | ||||||
|     templateVisitAll(visitor, template); |     templateVisitAll(visitor, template); | ||||||
|     var source = `[${visitor.result.join(',')}]`; |     var source = `[${visitor.result.join(',')}]`; | ||||||
|     return new SourceExpression([], source); |     return new SourceExpression([], source); | ||||||
| @ -58,22 +63,23 @@ export class CommandCompiler { | |||||||
| interface CommandFactory<R> { | interface CommandFactory<R> { | ||||||
|   createText(value: string, isBound: boolean, ngContentIndex: number): R; |   createText(value: string, isBound: boolean, ngContentIndex: number): R; | ||||||
|   createNgContent(ngContentIndex: number): R; |   createNgContent(ngContentIndex: number): R; | ||||||
|   createBeginElement(name: string, attrNameAndValues: string[], eventNames: string[], |   createBeginElement(name: string, attrNameAndValues: string[], eventTargetAndNames: string[], | ||||||
|                      variableNameAndValues: string[], directives: NormalizedDirectiveMetadata[], |                      variableNameAndValues: string[], directives: CompileDirectiveMetadata[], | ||||||
|                      isBound: boolean, ngContentIndex: number): R; |                      isBound: boolean, ngContentIndex: number): R; | ||||||
|   createEndElement(): R; |   createEndElement(): R; | ||||||
|   createBeginComponent(name: string, attrNameAndValues: string[], eventNames: string[], |   createBeginComponent(name: string, attrNameAndValues: string[], eventTargetAndNames: string[], | ||||||
|                        variableNameAndValues: string[], directives: NormalizedDirectiveMetadata[], |                        variableNameAndValues: string[], directives: CompileDirectiveMetadata[], | ||||||
|                        nativeShadow: boolean, ngContentIndex: number): R; |                        nativeShadow: boolean, ngContentIndex: number): R; | ||||||
|   createEndComponent(): R; |   createEndComponent(): R; | ||||||
|   createEmbeddedTemplate(attrNameAndValues: string[], variableNameAndValues: string[], |   createEmbeddedTemplate(embeddedTemplateIndex: number, attrNameAndValues: string[], | ||||||
|                          directives: NormalizedDirectiveMetadata[], isMerged: boolean, |                          variableNameAndValues: string[], directives: CompileDirectiveMetadata[], | ||||||
|                          ngContentIndex: number, children: R[]): R; |                          isMerged: boolean, ngContentIndex: number, children: R[]): R; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class RuntimeCommandFactory implements CommandFactory<TemplateCmd> { | class RuntimeCommandFactory implements CommandFactory<TemplateCmd> { | ||||||
|   constructor(public componentTemplateFactory: Function) {} |   constructor(public componentTemplateFactory: Function, | ||||||
|   private _mapDirectives(directives: NormalizedDirectiveMetadata[]): Type[] { |               public changeDetectorFactories: Function[]) {} | ||||||
|  |   private _mapDirectives(directives: CompileDirectiveMetadata[]): Type[] { | ||||||
|     return directives.map(directive => directive.type.runtime); |     return directives.map(directive => directive.type.runtime); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -81,35 +87,46 @@ class RuntimeCommandFactory implements CommandFactory<TemplateCmd> { | |||||||
|     return text(value, isBound, ngContentIndex); |     return text(value, isBound, ngContentIndex); | ||||||
|   } |   } | ||||||
|   createNgContent(ngContentIndex: number): TemplateCmd { return ngContent(ngContentIndex); } |   createNgContent(ngContentIndex: number): TemplateCmd { return ngContent(ngContentIndex); } | ||||||
|   createBeginElement(name: string, attrNameAndValues: string[], eventNames: string[], |   createBeginElement(name: string, attrNameAndValues: string[], eventTargetAndNames: string[], | ||||||
|                      variableNameAndValues: string[], directives: NormalizedDirectiveMetadata[], |                      variableNameAndValues: string[], directives: CompileDirectiveMetadata[], | ||||||
|                      isBound: boolean, ngContentIndex: number): TemplateCmd { |                      isBound: boolean, ngContentIndex: number): TemplateCmd { | ||||||
|     return beginElement(name, attrNameAndValues, eventNames, variableNameAndValues, |     return beginElement(name, attrNameAndValues, eventTargetAndNames, variableNameAndValues, | ||||||
|                         this._mapDirectives(directives), isBound, ngContentIndex); |                         this._mapDirectives(directives), isBound, ngContentIndex); | ||||||
|   } |   } | ||||||
|   createEndElement(): TemplateCmd { return endElement(); } |   createEndElement(): TemplateCmd { return endElement(); } | ||||||
|   createBeginComponent(name: string, attrNameAndValues: string[], eventNames: string[], |   createBeginComponent(name: string, attrNameAndValues: string[], eventTargetAndNames: string[], | ||||||
|                        variableNameAndValues: string[], directives: NormalizedDirectiveMetadata[], |                        variableNameAndValues: string[], directives: CompileDirectiveMetadata[], | ||||||
|                        nativeShadow: boolean, ngContentIndex: number): TemplateCmd { |                        nativeShadow: boolean, ngContentIndex: number): TemplateCmd { | ||||||
|     return beginComponent(name, attrNameAndValues, eventNames, variableNameAndValues, |     return beginComponent(name, attrNameAndValues, eventTargetAndNames, variableNameAndValues, | ||||||
|                           this._mapDirectives(directives), nativeShadow, ngContentIndex, |                           this._mapDirectives(directives), nativeShadow, ngContentIndex, | ||||||
|                           this.componentTemplateFactory(directives[0])); |                           this.componentTemplateFactory(directives[0])); | ||||||
|   } |   } | ||||||
|   createEndComponent(): TemplateCmd { return endComponent(); } |   createEndComponent(): TemplateCmd { return endComponent(); } | ||||||
|   createEmbeddedTemplate(attrNameAndValues: string[], variableNameAndValues: string[], |   createEmbeddedTemplate(embeddedTemplateIndex: number, attrNameAndValues: string[], | ||||||
|                          directives: NormalizedDirectiveMetadata[], isMerged: boolean, |                          variableNameAndValues: string[], directives: CompileDirectiveMetadata[], | ||||||
|                          ngContentIndex: number, children: TemplateCmd[]): TemplateCmd { |                          isMerged: boolean, ngContentIndex: number, | ||||||
|  |                          children: TemplateCmd[]): TemplateCmd { | ||||||
|     return embeddedTemplate(attrNameAndValues, variableNameAndValues, |     return embeddedTemplate(attrNameAndValues, variableNameAndValues, | ||||||
|                             this._mapDirectives(directives), isMerged, ngContentIndex, children); |                             this._mapDirectives(directives), isMerged, ngContentIndex, | ||||||
|  |                             this.changeDetectorFactories[embeddedTemplateIndex], children); | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function escapeStringArray(data: string[]): string { | function escapePrimitiveArray(data: any[]): string { | ||||||
|   return `[${data.map( value => escapeSingleQuoteString(value)).join(',')}]`; |   return `[${data.map( (value) => { | ||||||
|  |     if (isString(value)) { | ||||||
|  |       return escapeSingleQuoteString(value); | ||||||
|  |     } else if (isBlank(value)) { | ||||||
|  |       return 'null'; | ||||||
|  |     } else { | ||||||
|  |       return value; | ||||||
|  |     } | ||||||
|  |   }).join(',')}]`;
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class CodegenCommandFactory implements CommandFactory<string> { | class CodegenCommandFactory implements CommandFactory<string> { | ||||||
|   constructor(public componentTemplateFactory: Function) {} |   constructor(public componentTemplateFactory: Function, | ||||||
|  |               public changeDetectorFactoryExpressions: string[]) {} | ||||||
| 
 | 
 | ||||||
|   createText(value: string, isBound: boolean, ngContentIndex: number): string { |   createText(value: string, isBound: boolean, ngContentIndex: number): string { | ||||||
|     return `${TEMPLATE_COMMANDS_MODULE_REF}text(${escapeSingleQuoteString(value)}, ${isBound}, ${ngContentIndex})`; |     return `${TEMPLATE_COMMANDS_MODULE_REF}text(${escapeSingleQuoteString(value)}, ${isBound}, ${ngContentIndex})`; | ||||||
| @ -117,26 +134,27 @@ class CodegenCommandFactory implements CommandFactory<string> { | |||||||
|   createNgContent(ngContentIndex: number): string { |   createNgContent(ngContentIndex: number): string { | ||||||
|     return `${TEMPLATE_COMMANDS_MODULE_REF}ngContent(${ngContentIndex})`; |     return `${TEMPLATE_COMMANDS_MODULE_REF}ngContent(${ngContentIndex})`; | ||||||
|   } |   } | ||||||
|   createBeginElement(name: string, attrNameAndValues: string[], eventNames: string[], |   createBeginElement(name: string, attrNameAndValues: string[], eventTargetAndNames: string[], | ||||||
|                      variableNameAndValues: string[], directives: NormalizedDirectiveMetadata[], |                      variableNameAndValues: string[], directives: CompileDirectiveMetadata[], | ||||||
|                      isBound: boolean, ngContentIndex: number): string { |                      isBound: boolean, ngContentIndex: number): string { | ||||||
|     return `${TEMPLATE_COMMANDS_MODULE_REF}beginElement(${escapeSingleQuoteString(name)}, ${escapeStringArray(attrNameAndValues)}, ${escapeStringArray(eventNames)}, ${escapeStringArray(variableNameAndValues)}, [${_escapeDirectives(directives).join(',')}], ${isBound}, ${ngContentIndex})`; |     return `${TEMPLATE_COMMANDS_MODULE_REF}beginElement(${escapeSingleQuoteString(name)}, ${escapePrimitiveArray(attrNameAndValues)}, ${escapePrimitiveArray(eventTargetAndNames)}, ${escapePrimitiveArray(variableNameAndValues)}, [${_escapeDirectives(directives).join(',')}], ${isBound}, ${ngContentIndex})`; | ||||||
|   } |   } | ||||||
|   createEndElement(): string { return `${TEMPLATE_COMMANDS_MODULE_REF}endElement()`; } |   createEndElement(): string { return `${TEMPLATE_COMMANDS_MODULE_REF}endElement()`; } | ||||||
|   createBeginComponent(name: string, attrNameAndValues: string[], eventNames: string[], |   createBeginComponent(name: string, attrNameAndValues: string[], eventTargetAndNames: string[], | ||||||
|                        variableNameAndValues: string[], directives: NormalizedDirectiveMetadata[], |                        variableNameAndValues: string[], directives: CompileDirectiveMetadata[], | ||||||
|                        nativeShadow: boolean, ngContentIndex: number): string { |                        nativeShadow: boolean, ngContentIndex: number): string { | ||||||
|     return `${TEMPLATE_COMMANDS_MODULE_REF}beginComponent(${escapeSingleQuoteString(name)}, ${escapeStringArray(attrNameAndValues)}, ${escapeStringArray(eventNames)}, ${escapeStringArray(variableNameAndValues)}, [${_escapeDirectives(directives).join(',')}], ${nativeShadow}, ${ngContentIndex}, ${this.componentTemplateFactory(directives[0])})`; |     return `${TEMPLATE_COMMANDS_MODULE_REF}beginComponent(${escapeSingleQuoteString(name)}, ${escapePrimitiveArray(attrNameAndValues)}, ${escapePrimitiveArray(eventTargetAndNames)}, ${escapePrimitiveArray(variableNameAndValues)}, [${_escapeDirectives(directives).join(',')}], ${nativeShadow}, ${ngContentIndex}, ${this.componentTemplateFactory(directives[0])})`; | ||||||
|   } |   } | ||||||
|   createEndComponent(): string { return `${TEMPLATE_COMMANDS_MODULE_REF}endComponent()`; } |   createEndComponent(): string { return `${TEMPLATE_COMMANDS_MODULE_REF}endComponent()`; } | ||||||
|   createEmbeddedTemplate(attrNameAndValues: string[], variableNameAndValues: string[], |   createEmbeddedTemplate(embeddedTemplateIndex: number, attrNameAndValues: string[], | ||||||
|                          directives: NormalizedDirectiveMetadata[], isMerged: boolean, |                          variableNameAndValues: string[], directives: CompileDirectiveMetadata[], | ||||||
|                          ngContentIndex: number, children: string[]): string { |                          isMerged: boolean, ngContentIndex: number, children: string[]): string { | ||||||
|     return `${TEMPLATE_COMMANDS_MODULE_REF}embeddedTemplate(${escapeStringArray(attrNameAndValues)}, ${escapeStringArray(variableNameAndValues)}, [${_escapeDirectives(directives).join(',')}], ${isMerged}, ${ngContentIndex}, [${children.join(',')}])`; |     return `${TEMPLATE_COMMANDS_MODULE_REF}embeddedTemplate(${escapePrimitiveArray(attrNameAndValues)}, ${escapePrimitiveArray(variableNameAndValues)}, ` + | ||||||
|  |            `[${_escapeDirectives(directives).join(',')}], ${isMerged}, ${ngContentIndex}, ${this.changeDetectorFactoryExpressions[embeddedTemplateIndex]}, [${children.join(',')}])`; | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function _escapeDirectives(directives: NormalizedDirectiveMetadata[]): string[] { | function _escapeDirectives(directives: CompileDirectiveMetadata[]): string[] { | ||||||
|   return directives.map(directiveType => |   return directives.map(directiveType => | ||||||
|                             `${moduleRef(directiveType.type.moduleId)}${directiveType.type.name}`); |                             `${moduleRef(directiveType.type.moduleId)}${directiveType.type.name}`); | ||||||
| } | } | ||||||
| @ -150,10 +168,11 @@ function visitAndReturnContext(visitor: TemplateAstVisitor, asts: TemplateAst[], | |||||||
| class CommandBuilderVisitor<R> implements TemplateAstVisitor { | class CommandBuilderVisitor<R> implements TemplateAstVisitor { | ||||||
|   result: R[] = []; |   result: R[] = []; | ||||||
|   transitiveNgContentCount: number = 0; |   transitiveNgContentCount: number = 0; | ||||||
|   constructor(public commandFactory: CommandFactory<R>, |   constructor(public commandFactory: CommandFactory<R>, public component: CompileDirectiveMetadata, | ||||||
|               public component: NormalizedDirectiveMetadata) {} |               public embeddedTemplateIndex: number) {} | ||||||
| 
 | 
 | ||||||
|   private _readAttrNameAndValues(localComponent: NormalizedDirectiveMetadata, |   private _readAttrNameAndValues(localComponent: CompileDirectiveMetadata, | ||||||
|  |                                  directives: CompileDirectiveMetadata[], | ||||||
|                                  attrAsts: TemplateAst[]): string[] { |                                  attrAsts: TemplateAst[]): string[] { | ||||||
|     var attrNameAndValues: string[] = visitAndReturnContext(this, attrAsts, []); |     var attrNameAndValues: string[] = visitAndReturnContext(this, attrAsts, []); | ||||||
|     if (isPresent(localComponent) && |     if (isPresent(localComponent) && | ||||||
| @ -165,7 +184,13 @@ class CommandBuilderVisitor<R> implements TemplateAstVisitor { | |||||||
|       attrNameAndValues.push(shimContentAttribute(this.component.type.id)); |       attrNameAndValues.push(shimContentAttribute(this.component.type.id)); | ||||||
|       attrNameAndValues.push(''); |       attrNameAndValues.push(''); | ||||||
|     } |     } | ||||||
|     return attrNameAndValues; |     directives.forEach(directiveMeta => { | ||||||
|  |       StringMapWrapper.forEach(directiveMeta.hostAttributes, (value, name) => { | ||||||
|  |         attrNameAndValues.push(name); | ||||||
|  |         attrNameAndValues.push(value); | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|  |     return removeKeyValueArrayDuplicates(attrNameAndValues); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   visitNgContent(ast: NgContentAst, context: any): any { |   visitNgContent(ast: NgContentAst, context: any): any { | ||||||
| @ -174,43 +199,61 @@ class CommandBuilderVisitor<R> implements TemplateAstVisitor { | |||||||
|     return null; |     return null; | ||||||
|   } |   } | ||||||
|   visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any { |   visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any { | ||||||
|     var childVisitor = new CommandBuilderVisitor(this.commandFactory, this.component); |     this.embeddedTemplateIndex++; | ||||||
|  |     var childVisitor = | ||||||
|  |         new CommandBuilderVisitor(this.commandFactory, this.component, this.embeddedTemplateIndex); | ||||||
|     templateVisitAll(childVisitor, ast.children); |     templateVisitAll(childVisitor, ast.children); | ||||||
|     var isMerged = childVisitor.transitiveNgContentCount > 0; |     var isMerged = childVisitor.transitiveNgContentCount > 0; | ||||||
|     this.transitiveNgContentCount += childVisitor.transitiveNgContentCount; |     var variableNameAndValues = []; | ||||||
|     var directivesAndEventNames = visitAndReturnContext(this, ast.directives, [[], []]); |     ast.vars.forEach((varAst) => { | ||||||
|  |       variableNameAndValues.push(varAst.name); | ||||||
|  |       variableNameAndValues.push(varAst.value); | ||||||
|  |     }); | ||||||
|  |     var directives = []; | ||||||
|  |     ListWrapper.forEachWithIndex(ast.directives, (directiveAst: DirectiveAst, index: number) => { | ||||||
|  |       directiveAst.visit(this, new DirectiveContext(index, [], [], directives)); | ||||||
|  |     }); | ||||||
|     this.result.push(this.commandFactory.createEmbeddedTemplate( |     this.result.push(this.commandFactory.createEmbeddedTemplate( | ||||||
|         this._readAttrNameAndValues(null, ast.attrs), visitAndReturnContext(this, ast.vars, []), |         this.embeddedTemplateIndex, this._readAttrNameAndValues(null, directives, ast.attrs), | ||||||
|         directivesAndEventNames[0], isMerged, ast.ngContentIndex, childVisitor.result)); |         variableNameAndValues, directives, isMerged, ast.ngContentIndex, childVisitor.result)); | ||||||
|  |     this.transitiveNgContentCount += childVisitor.transitiveNgContentCount; | ||||||
|  |     this.embeddedTemplateIndex = childVisitor.embeddedTemplateIndex; | ||||||
|     return null; |     return null; | ||||||
|   } |   } | ||||||
|   visitElement(ast: ElementAst, context: any): any { |   visitElement(ast: ElementAst, context: any): any { | ||||||
|     var component = ast.getComponent(); |     var component = ast.getComponent(); | ||||||
|     var eventNames = visitAndReturnContext(this, ast.events, []); |     var eventTargetAndNames = visitAndReturnContext(this, ast.events, []); | ||||||
|  |     var variableNameAndValues = []; | ||||||
|  |     if (isBlank(component)) { | ||||||
|  |       ast.exportAsVars.forEach((varAst) => { | ||||||
|  |         variableNameAndValues.push(varAst.name); | ||||||
|  |         variableNameAndValues.push(IMPLICIT_VAR); | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|     var directives = []; |     var directives = []; | ||||||
|     visitAndReturnContext(this, ast.directives, [directives, eventNames]); |     ListWrapper.forEachWithIndex(ast.directives, (directiveAst: DirectiveAst, index: number) => { | ||||||
|     var attrNameAndValues = this._readAttrNameAndValues(component, ast.attrs); |       directiveAst.visit(this, new DirectiveContext(index, eventTargetAndNames, | ||||||
|     var vars = visitAndReturnContext(this, ast.vars, []); |                                                     variableNameAndValues, directives)); | ||||||
|  |     }); | ||||||
|  |     eventTargetAndNames = removeKeyValueArrayDuplicates(eventTargetAndNames); | ||||||
|  | 
 | ||||||
|  |     var attrNameAndValues = this._readAttrNameAndValues(component, directives, ast.attrs); | ||||||
|     if (isPresent(component)) { |     if (isPresent(component)) { | ||||||
|       this.result.push(this.commandFactory.createBeginComponent( |       this.result.push(this.commandFactory.createBeginComponent( | ||||||
|           ast.name, attrNameAndValues, eventNames, vars, directives, |           ast.name, attrNameAndValues, eventTargetAndNames, variableNameAndValues, directives, | ||||||
|           component.template.encapsulation === ViewEncapsulation.Native, ast.ngContentIndex)); |           component.template.encapsulation === ViewEncapsulation.Native, ast.ngContentIndex)); | ||||||
|       templateVisitAll(this, ast.children); |       templateVisitAll(this, ast.children); | ||||||
|       this.result.push(this.commandFactory.createEndComponent()); |       this.result.push(this.commandFactory.createEndComponent()); | ||||||
|     } else { |     } else { | ||||||
|       this.result.push(this.commandFactory.createBeginElement(ast.name, attrNameAndValues, |       this.result.push(this.commandFactory.createBeginElement( | ||||||
|                                                               eventNames, vars, directives, |           ast.name, attrNameAndValues, eventTargetAndNames, variableNameAndValues, directives, | ||||||
|           ast.isBound(), ast.ngContentIndex)); |           ast.isBound(), ast.ngContentIndex)); | ||||||
|       templateVisitAll(this, ast.children); |       templateVisitAll(this, ast.children); | ||||||
|       this.result.push(this.commandFactory.createEndElement()); |       this.result.push(this.commandFactory.createEndElement()); | ||||||
|     } |     } | ||||||
|     return null; |     return null; | ||||||
|   } |   } | ||||||
|   visitVariable(ast: VariableAst, variableNameAndValues: string[]): any { |   visitVariable(ast: VariableAst, ctx: any): any { return null; } | ||||||
|     variableNameAndValues.push(ast.name); |  | ||||||
|     variableNameAndValues.push(ast.value); |  | ||||||
|     return null; |  | ||||||
|   } |  | ||||||
|   visitAttr(ast: AttrAst, attrNameAndValues: string[]): any { |   visitAttr(ast: AttrAst, attrNameAndValues: string[]): any { | ||||||
|     attrNameAndValues.push(ast.name); |     attrNameAndValues.push(ast.name); | ||||||
|     attrNameAndValues.push(ast.value); |     attrNameAndValues.push(ast.value); | ||||||
| @ -224,15 +267,42 @@ class CommandBuilderVisitor<R> implements TemplateAstVisitor { | |||||||
|     this.result.push(this.commandFactory.createText(ast.value, false, ast.ngContentIndex)); |     this.result.push(this.commandFactory.createText(ast.value, false, ast.ngContentIndex)); | ||||||
|     return null; |     return null; | ||||||
|   } |   } | ||||||
|   visitDirective(ast: DirectiveAst, directivesAndEventNames: any[][]): any { |   visitDirective(ast: DirectiveAst, ctx: DirectiveContext): any { | ||||||
|     directivesAndEventNames[0].push(ast.directive); |     ctx.targetDirectives.push(ast.directive); | ||||||
|     templateVisitAll(this, ast.hostEvents, directivesAndEventNames[1]); |     templateVisitAll(this, ast.hostEvents, ctx.eventTargetAndNames); | ||||||
|  |     ast.exportAsVars.forEach(varAst => { | ||||||
|  |       ctx.targetVariableNameAndValues.push(varAst.name); | ||||||
|  |       ctx.targetVariableNameAndValues.push(ctx.index); | ||||||
|  |     }); | ||||||
|     return null; |     return null; | ||||||
|   } |   } | ||||||
|   visitEvent(ast: BoundEventAst, eventNames: string[]): any { |   visitEvent(ast: BoundEventAst, eventTargetAndNames: string[]): any { | ||||||
|     eventNames.push(ast.getFullName()); |     eventTargetAndNames.push(ast.target); | ||||||
|  |     eventTargetAndNames.push(ast.name); | ||||||
|     return null; |     return null; | ||||||
|   } |   } | ||||||
|   visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { return null; } |   visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { return null; } | ||||||
|   visitElementProperty(ast: BoundElementPropertyAst, context: any): any { return null; } |   visitElementProperty(ast: BoundElementPropertyAst, context: any): any { return null; } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | function removeKeyValueArrayDuplicates(keyValueArray: string[]): string[] { | ||||||
|  |   var knownPairs = new Set(); | ||||||
|  |   var resultKeyValueArray = []; | ||||||
|  |   for (var i = 0; i < keyValueArray.length; i += 2) { | ||||||
|  |     var key = keyValueArray[i]; | ||||||
|  |     var value = keyValueArray[i + 1]; | ||||||
|  |     var pairId = `${key}:${value}`; | ||||||
|  |     if (!SetWrapper.has(knownPairs, pairId)) { | ||||||
|  |       resultKeyValueArray.push(key); | ||||||
|  |       resultKeyValueArray.push(value); | ||||||
|  |       knownPairs.add(pairId); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return resultKeyValueArray; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class DirectiveContext { | ||||||
|  |   constructor(public index: number, public eventTargetAndNames: string[], | ||||||
|  |               public targetVariableNameAndValues: any[], | ||||||
|  |               public targetDirectives: CompileDirectiveMetadata[]) {} | ||||||
|  | } | ||||||
| @ -1,9 +1,7 @@ | |||||||
| export {TemplateCompiler} from './template_compiler'; | export {TemplateCompiler} from './template_compiler'; | ||||||
| export { | export { | ||||||
|   DirectiveMetadata, |   CompileDirectiveMetadata, | ||||||
|   TypeMetadata, |   CompileTypeMetadata, | ||||||
|   TemplateMetadata, |   CompileTemplateMetadata | ||||||
|   ChangeDetectionMetadata, |  | ||||||
|   INormalizedDirectiveMetadata |  | ||||||
| } from './directive_metadata'; | } from './directive_metadata'; | ||||||
| export {SourceModule, SourceWithImports} from './source_module'; | export {SourceModule, SourceWithImports} from './source_module'; | ||||||
|  | |||||||
| @ -1,12 +1,27 @@ | |||||||
| import {isPresent, normalizeBool, serializeEnum, Type} from 'angular2/src/core/facade/lang'; | import { | ||||||
|  |   isPresent, | ||||||
|  |   isBlank, | ||||||
|  |   normalizeBool, | ||||||
|  |   serializeEnum, | ||||||
|  |   Type, | ||||||
|  |   RegExpWrapper, | ||||||
|  |   StringWrapper | ||||||
|  | } from 'angular2/src/core/facade/lang'; | ||||||
|  | import {StringMapWrapper} from 'angular2/src/core/facade/collection'; | ||||||
| import { | import { | ||||||
|   ChangeDetectionStrategy, |   ChangeDetectionStrategy, | ||||||
|   changeDetectionStrategyFromJson |   CHANGE_DECTION_STRATEGY_VALUES | ||||||
| } from 'angular2/src/core/change_detection/change_detection'; | } from 'angular2/src/core/change_detection/change_detection'; | ||||||
| import {ViewEncapsulation, viewEncapsulationFromJson} from 'angular2/src/core/render/api'; | import {ViewEncapsulation, VIEW_ENCAPSULATION_VALUES} from 'angular2/src/core/render/api'; | ||||||
| import {CssSelector} from 'angular2/src/core/render/dom/compiler/selector'; | import {CssSelector} from 'angular2/src/core/render/dom/compiler/selector'; | ||||||
|  | import {splitAtColon} from './util'; | ||||||
|  | import {LifecycleHooks, LIFECYCLE_HOOKS_VALUES} from 'angular2/src/core/compiler/interfaces'; | ||||||
| 
 | 
 | ||||||
| export class TypeMetadata { | // group 1: "property" from "[property]"
 | ||||||
|  | // group 2: "event" from "(event)"
 | ||||||
|  | var HOST_REG_EXP = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))$/g; | ||||||
|  | 
 | ||||||
|  | export class CompileTypeMetadata { | ||||||
|   id: number; |   id: number; | ||||||
|   runtime: Type; |   runtime: Type; | ||||||
|   name: string; |   name: string; | ||||||
| @ -19,8 +34,9 @@ export class TypeMetadata { | |||||||
|     this.moduleId = moduleId; |     this.moduleId = moduleId; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   static fromJson(data: StringMap<string, any>): TypeMetadata { |   static fromJson(data: StringMap<string, any>): CompileTypeMetadata { | ||||||
|     return new TypeMetadata({id: data['id'], name: data['name'], moduleId: data['moduleId']}); |     return new CompileTypeMetadata( | ||||||
|  |         {id: data['id'], name: data['name'], moduleId: data['moduleId']}); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   toJson(): StringMap<string, any> { |   toJson(): StringMap<string, any> { | ||||||
| @ -33,169 +49,39 @@ export class TypeMetadata { | |||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export class ChangeDetectionMetadata { | export class CompileTemplateMetadata { | ||||||
|   changeDetection: ChangeDetectionStrategy; |  | ||||||
|   properties: string[]; |  | ||||||
|   events: string[]; |  | ||||||
|   hostListeners: StringMap<string, string>; |  | ||||||
|   hostProperties: StringMap<string, string>; |  | ||||||
|   callAfterContentInit: boolean; |  | ||||||
|   callAfterContentChecked: boolean; |  | ||||||
|   callAfterViewInit: boolean; |  | ||||||
|   callAfterViewChecked: boolean; |  | ||||||
|   callOnChanges: boolean; |  | ||||||
|   callDoCheck: boolean; |  | ||||||
|   callOnInit: boolean; |  | ||||||
|   constructor({changeDetection, properties, events, hostListeners, hostProperties, |  | ||||||
|                callAfterContentInit, callAfterContentChecked, callAfterViewInit, |  | ||||||
|                callAfterViewChecked, callOnChanges, callDoCheck, callOnInit}: { |  | ||||||
|     changeDetection?: ChangeDetectionStrategy, |  | ||||||
|     properties?: string[], |  | ||||||
|     events?: string[], |  | ||||||
|     hostListeners?: StringMap<string, string>, |  | ||||||
|     hostProperties?: StringMap<string, string>, |  | ||||||
|     callAfterContentInit?: boolean, |  | ||||||
|     callAfterContentChecked?: boolean, |  | ||||||
|     callAfterViewInit?: boolean, |  | ||||||
|     callAfterViewChecked?: boolean, |  | ||||||
|     callOnChanges?: boolean, |  | ||||||
|     callDoCheck?: boolean, |  | ||||||
|     callOnInit?: boolean |  | ||||||
|   } = {}) { |  | ||||||
|     this.changeDetection = changeDetection; |  | ||||||
|     this.properties = isPresent(properties) ? properties : []; |  | ||||||
|     this.events = isPresent(events) ? events : []; |  | ||||||
|     this.hostListeners = isPresent(hostListeners) ? hostListeners : {}; |  | ||||||
|     this.hostProperties = isPresent(hostProperties) ? hostProperties : {}; |  | ||||||
|     this.callAfterContentInit = normalizeBool(callAfterContentInit); |  | ||||||
|     this.callAfterContentChecked = normalizeBool(callAfterContentChecked); |  | ||||||
|     this.callAfterViewInit = normalizeBool(callAfterViewInit); |  | ||||||
|     this.callAfterViewChecked = normalizeBool(callAfterViewChecked); |  | ||||||
|     this.callOnChanges = normalizeBool(callOnChanges); |  | ||||||
|     this.callDoCheck = normalizeBool(callDoCheck); |  | ||||||
|     this.callOnInit = normalizeBool(callOnInit); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   static fromJson(data: StringMap<string, any>): ChangeDetectionMetadata { |  | ||||||
|     return new ChangeDetectionMetadata({ |  | ||||||
|       changeDetection: isPresent(data['changeDetection']) ? |  | ||||||
|                            changeDetectionStrategyFromJson(data['changeDetection']) : |  | ||||||
|                            data['changeDetection'], |  | ||||||
|       properties: data['properties'], |  | ||||||
|       events: data['events'], |  | ||||||
|       hostListeners: data['hostListeners'], |  | ||||||
|       hostProperties: data['hostProperties'], |  | ||||||
|       callAfterContentInit: data['callAfterContentInit'], |  | ||||||
|       callAfterContentChecked: data['callAfterContentChecked'], |  | ||||||
|       callAfterViewInit: data['callAfterViewInit'], |  | ||||||
|       callAfterViewChecked: data['callAfterViewChecked'], |  | ||||||
|       callOnChanges: data['callOnChanges'], |  | ||||||
|       callDoCheck: data['callDoCheck'], |  | ||||||
|       callOnInit: data['callOnInit'] |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   toJson(): StringMap<string, any> { |  | ||||||
|     return { |  | ||||||
|       'changeDetection': isPresent(this.changeDetection) ? serializeEnum(this.changeDetection) : |  | ||||||
|                                                            this.changeDetection, |  | ||||||
|       'properties': this.properties, |  | ||||||
|       'events': this.events, |  | ||||||
|       'hostListeners': this.hostListeners, |  | ||||||
|       'hostProperties': this.hostProperties, |  | ||||||
|       'callAfterContentInit': this.callAfterContentInit, |  | ||||||
|       'callAfterContentChecked': this.callAfterContentChecked, |  | ||||||
|       'callAfterViewInit': this.callAfterViewInit, |  | ||||||
|       'callAfterViewChecked': this.callAfterViewChecked, |  | ||||||
|       'callOnChanges': this.callOnChanges, |  | ||||||
|       'callDoCheck': this.callDoCheck, |  | ||||||
|       'callOnInit': this.callOnInit |  | ||||||
|     }; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export class TemplateMetadata { |  | ||||||
|   encapsulation: ViewEncapsulation; |   encapsulation: ViewEncapsulation; | ||||||
|   template: string; |   template: string; | ||||||
|   templateUrl: string; |   templateUrl: string; | ||||||
|   styles: string[]; |   styles: string[]; | ||||||
|   styleUrls: string[]; |   styleUrls: string[]; | ||||||
|   hostAttributes: StringMap<string, string>; |   ngContentSelectors: string[]; | ||||||
|   constructor({encapsulation, template, templateUrl, styles, styleUrls, hostAttributes}: { |   constructor({encapsulation, template, templateUrl, styles, styleUrls, ngContentSelectors}: { | ||||||
|     encapsulation?: ViewEncapsulation, |     encapsulation?: ViewEncapsulation, | ||||||
|     template?: string, |     template?: string, | ||||||
|     templateUrl?: string, |     templateUrl?: string, | ||||||
|     styles?: string[], |     styles?: string[], | ||||||
|     styleUrls?: string[], |     styleUrls?: string[], | ||||||
|     hostAttributes?: StringMap<string, string> |     ngContentSelectors?: string[] | ||||||
|   } = {}) { |   } = {}) { | ||||||
|     this.encapsulation = isPresent(encapsulation) ? encapsulation : ViewEncapsulation.None; |     this.encapsulation = encapsulation; | ||||||
|     this.template = template; |     this.template = template; | ||||||
|     this.templateUrl = templateUrl; |     this.templateUrl = templateUrl; | ||||||
|     this.styles = isPresent(styles) ? styles : []; |     this.styles = isPresent(styles) ? styles : []; | ||||||
|     this.styleUrls = isPresent(styleUrls) ? styleUrls : []; |     this.styleUrls = isPresent(styleUrls) ? styleUrls : []; | ||||||
|     this.hostAttributes = isPresent(hostAttributes) ? hostAttributes : {}; |     this.ngContentSelectors = isPresent(ngContentSelectors) ? ngContentSelectors : []; | ||||||
|   } |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| 
 |   static fromJson(data: StringMap<string, any>): CompileTemplateMetadata { | ||||||
| export class DirectiveMetadata { |     return new CompileTemplateMetadata({ | ||||||
|   type: TypeMetadata; |  | ||||||
|   isComponent: boolean; |  | ||||||
|   dynamicLoadable: boolean; |  | ||||||
|   selector: string; |  | ||||||
|   changeDetection: ChangeDetectionMetadata; |  | ||||||
|   template: TemplateMetadata; |  | ||||||
|   constructor({type, isComponent, dynamicLoadable, selector, changeDetection, template}: { |  | ||||||
|     type?: TypeMetadata, |  | ||||||
|     isComponent?: boolean, |  | ||||||
|     dynamicLoadable?: boolean, |  | ||||||
|     selector?: string, |  | ||||||
|     changeDetection?: ChangeDetectionMetadata, |  | ||||||
|     template?: TemplateMetadata |  | ||||||
|   } = {}) { |  | ||||||
|     this.type = type; |  | ||||||
|     this.isComponent = normalizeBool(isComponent); |  | ||||||
|     this.dynamicLoadable = normalizeBool(dynamicLoadable); |  | ||||||
|     this.selector = selector; |  | ||||||
|     this.changeDetection = changeDetection; |  | ||||||
|     this.template = template; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export class NormalizedTemplateMetadata { |  | ||||||
|   encapsulation: ViewEncapsulation; |  | ||||||
|   template: string; |  | ||||||
|   styles: string[]; |  | ||||||
|   styleAbsUrls: string[]; |  | ||||||
|   ngContentSelectors: string[]; |  | ||||||
|   hostAttributes: StringMap<string, string>; |  | ||||||
|   constructor({encapsulation, template, styles, styleAbsUrls, ngContentSelectors, hostAttributes}: { |  | ||||||
|     encapsulation?: ViewEncapsulation, |  | ||||||
|     template?: string, |  | ||||||
|     styles?: string[], |  | ||||||
|     styleAbsUrls?: string[], |  | ||||||
|     ngContentSelectors?: string[], |  | ||||||
|     hostAttributes?: StringMap<string, string> |  | ||||||
|   } = {}) { |  | ||||||
|     this.encapsulation = encapsulation; |  | ||||||
|     this.template = template; |  | ||||||
|     this.styles = styles; |  | ||||||
|     this.styleAbsUrls = styleAbsUrls; |  | ||||||
|     this.ngContentSelectors = ngContentSelectors; |  | ||||||
|     this.hostAttributes = hostAttributes; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   static fromJson(data: StringMap<string, any>): NormalizedTemplateMetadata { |  | ||||||
|     return new NormalizedTemplateMetadata({ |  | ||||||
|       encapsulation: isPresent(data['encapsulation']) ? |       encapsulation: isPresent(data['encapsulation']) ? | ||||||
|                          viewEncapsulationFromJson(data['encapsulation']) : |                          VIEW_ENCAPSULATION_VALUES[data['encapsulation']] : | ||||||
|                          data['encapsulation'], |                          data['encapsulation'], | ||||||
|       template: data['template'], |       template: data['template'], | ||||||
|  |       templateUrl: data['templateUrl'], | ||||||
|       styles: data['styles'], |       styles: data['styles'], | ||||||
|       styleAbsUrls: data['styleAbsUrls'], |       styleUrls: data['styleUrls'], | ||||||
|       ngContentSelectors: data['ngContentSelectors'], |       ngContentSelectors: data['ngContentSelectors'] | ||||||
|       hostAttributes: data['hostAttributes'] |  | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -204,51 +90,141 @@ export class NormalizedTemplateMetadata { | |||||||
|       'encapsulation': |       'encapsulation': | ||||||
|           isPresent(this.encapsulation) ? serializeEnum(this.encapsulation) : this.encapsulation, |           isPresent(this.encapsulation) ? serializeEnum(this.encapsulation) : this.encapsulation, | ||||||
|       'template': this.template, |       'template': this.template, | ||||||
|  |       'templateUrl': this.templateUrl, | ||||||
|       'styles': this.styles, |       'styles': this.styles, | ||||||
|       'styleAbsUrls': this.styleAbsUrls, |       'styleUrls': this.styleUrls, | ||||||
|       'ngContentSelectors': this.ngContentSelectors, |       'ngContentSelectors': this.ngContentSelectors | ||||||
|       'hostAttributes': this.hostAttributes |  | ||||||
|     }; |     }; | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export interface INormalizedDirectiveMetadata {} | export class CompileDirectiveMetadata { | ||||||
| 
 |   static create({type, isComponent, dynamicLoadable, selector, exportAs, changeDetection, | ||||||
| export class NormalizedDirectiveMetadata implements INormalizedDirectiveMetadata { |                  properties, events, host, lifecycleHooks, template}: { | ||||||
|   type: TypeMetadata; |     type?: CompileTypeMetadata, | ||||||
|   isComponent: boolean; |  | ||||||
|   dynamicLoadable: boolean; |  | ||||||
|   selector: string; |  | ||||||
|   changeDetection: ChangeDetectionMetadata; |  | ||||||
|   template: NormalizedTemplateMetadata; |  | ||||||
|   constructor({type, isComponent, dynamicLoadable, selector, changeDetection, template}: { |  | ||||||
|     id?: number, |  | ||||||
|     type?: TypeMetadata, |  | ||||||
|     isComponent?: boolean, |     isComponent?: boolean, | ||||||
|     dynamicLoadable?: boolean, |     dynamicLoadable?: boolean, | ||||||
|     selector?: string, |     selector?: string, | ||||||
|     changeDetection?: ChangeDetectionMetadata, |     exportAs?: string, | ||||||
|     template?: NormalizedTemplateMetadata |     changeDetection?: ChangeDetectionStrategy, | ||||||
|  |     properties?: string[], | ||||||
|  |     events?: string[], | ||||||
|  |     host?: StringMap<string, string>, | ||||||
|  |     lifecycleHooks?: LifecycleHooks[], | ||||||
|  |     template?: CompileTemplateMetadata | ||||||
|  |   } = {}): CompileDirectiveMetadata { | ||||||
|  |     var hostListeners = {}; | ||||||
|  |     var hostProperties = {}; | ||||||
|  |     var hostAttributes = {}; | ||||||
|  |     if (isPresent(host)) { | ||||||
|  |       StringMapWrapper.forEach(host, (value: string, key: string) => { | ||||||
|  |         var matches = RegExpWrapper.firstMatch(HOST_REG_EXP, key); | ||||||
|  |         if (isBlank(matches)) { | ||||||
|  |           hostAttributes[key] = value; | ||||||
|  |         } else if (isPresent(matches[1])) { | ||||||
|  |           hostProperties[matches[1]] = value; | ||||||
|  |         } else if (isPresent(matches[2])) { | ||||||
|  |           hostListeners[matches[2]] = value; | ||||||
|  |         } | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  |     var propsMap = {}; | ||||||
|  |     if (isPresent(properties)) { | ||||||
|  |       properties.forEach((bindConfig: string) => { | ||||||
|  |         // canonical syntax: `dirProp: elProp`
 | ||||||
|  |         // if there is no `:`, use dirProp = elProp
 | ||||||
|  |         var parts = splitAtColon(bindConfig, [bindConfig, bindConfig]); | ||||||
|  |         propsMap[parts[0]] = parts[1]; | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  |     var eventsMap = {}; | ||||||
|  |     if (isPresent(events)) { | ||||||
|  |       events.forEach((bindConfig: string) => { | ||||||
|  |         // canonical syntax: `dirProp: elProp`
 | ||||||
|  |         // if there is no `:`, use dirProp = elProp
 | ||||||
|  |         var parts = splitAtColon(bindConfig, [bindConfig, bindConfig]); | ||||||
|  |         eventsMap[parts[0]] = parts[1]; | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return new CompileDirectiveMetadata({ | ||||||
|  |       type: type, | ||||||
|  |       isComponent: normalizeBool(isComponent), | ||||||
|  |       dynamicLoadable: normalizeBool(dynamicLoadable), | ||||||
|  |       selector: selector, | ||||||
|  |       exportAs: exportAs, | ||||||
|  |       changeDetection: changeDetection, | ||||||
|  |       properties: propsMap, | ||||||
|  |       events: eventsMap, | ||||||
|  |       hostListeners: hostListeners, | ||||||
|  |       hostProperties: hostProperties, | ||||||
|  |       hostAttributes: hostAttributes, | ||||||
|  |       lifecycleHooks: isPresent(lifecycleHooks) ? lifecycleHooks : [], template: template | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   type: CompileTypeMetadata; | ||||||
|  |   isComponent: boolean; | ||||||
|  |   dynamicLoadable: boolean; | ||||||
|  |   selector: string; | ||||||
|  |   exportAs: string; | ||||||
|  |   changeDetection: ChangeDetectionStrategy; | ||||||
|  |   properties: StringMap<string, string>; | ||||||
|  |   events: StringMap<string, string>; | ||||||
|  |   hostListeners: StringMap<string, string>; | ||||||
|  |   hostProperties: StringMap<string, string>; | ||||||
|  |   hostAttributes: StringMap<string, string>; | ||||||
|  |   lifecycleHooks: LifecycleHooks[]; | ||||||
|  |   template: CompileTemplateMetadata; | ||||||
|  |   constructor({type, isComponent, dynamicLoadable, selector, exportAs, changeDetection, properties, | ||||||
|  |                events, hostListeners, hostProperties, hostAttributes, lifecycleHooks, template}: { | ||||||
|  |     type?: CompileTypeMetadata, | ||||||
|  |     isComponent?: boolean, | ||||||
|  |     dynamicLoadable?: boolean, | ||||||
|  |     selector?: string, | ||||||
|  |     exportAs?: string, | ||||||
|  |     changeDetection?: ChangeDetectionStrategy, | ||||||
|  |     properties?: StringMap<string, string>, | ||||||
|  |     events?: StringMap<string, string>, | ||||||
|  |     hostListeners?: StringMap<string, string>, | ||||||
|  |     hostProperties?: StringMap<string, string>, | ||||||
|  |     hostAttributes?: StringMap<string, string>, | ||||||
|  |     lifecycleHooks?: LifecycleHooks[], | ||||||
|  |     template?: CompileTemplateMetadata | ||||||
|   } = {}) { |   } = {}) { | ||||||
|     this.type = type; |     this.type = type; | ||||||
|     this.isComponent = normalizeBool(isComponent); |     this.isComponent = isComponent; | ||||||
|     this.dynamicLoadable = normalizeBool(dynamicLoadable); |     this.dynamicLoadable = dynamicLoadable; | ||||||
|     this.selector = selector; |     this.selector = selector; | ||||||
|  |     this.exportAs = exportAs; | ||||||
|     this.changeDetection = changeDetection; |     this.changeDetection = changeDetection; | ||||||
|  |     this.properties = properties; | ||||||
|  |     this.events = events; | ||||||
|  |     this.hostListeners = hostListeners; | ||||||
|  |     this.hostProperties = hostProperties; | ||||||
|  |     this.hostAttributes = hostAttributes; | ||||||
|  |     this.lifecycleHooks = lifecycleHooks; | ||||||
|     this.template = template; |     this.template = template; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   static fromJson(data: StringMap<string, any>): NormalizedDirectiveMetadata { |   static fromJson(data: StringMap<string, any>): CompileDirectiveMetadata { | ||||||
|     return new NormalizedDirectiveMetadata({ |     return new CompileDirectiveMetadata({ | ||||||
|       isComponent: data['isComponent'], |       isComponent: data['isComponent'], | ||||||
|       dynamicLoadable: data['dynamicLoadable'], |       dynamicLoadable: data['dynamicLoadable'], | ||||||
|       selector: data['selector'], |       selector: data['selector'], | ||||||
|       type: isPresent(data['type']) ? TypeMetadata.fromJson(data['type']) : data['type'], |       exportAs: data['exportAs'], | ||||||
|  |       type: isPresent(data['type']) ? CompileTypeMetadata.fromJson(data['type']) : data['type'], | ||||||
|       changeDetection: isPresent(data['changeDetection']) ? |       changeDetection: isPresent(data['changeDetection']) ? | ||||||
|                            ChangeDetectionMetadata.fromJson(data['changeDetection']) : |                            CHANGE_DECTION_STRATEGY_VALUES[data['changeDetection']] : | ||||||
|                            data['changeDetection'], |                            data['changeDetection'], | ||||||
|       template: |       properties: data['properties'], | ||||||
|           isPresent(data['template']) ? NormalizedTemplateMetadata.fromJson(data['template']) : |       events: data['events'], | ||||||
|  |       hostListeners: data['hostListeners'], | ||||||
|  |       hostProperties: data['hostProperties'], | ||||||
|  |       hostAttributes: data['hostAttributes'], | ||||||
|  |       lifecycleHooks: | ||||||
|  |           (<any[]>data['lifecycleHooks']).map(hookValue => LIFECYCLE_HOOKS_VALUES[hookValue]), | ||||||
|  |       template: isPresent(data['template']) ? CompileTemplateMetadata.fromJson(data['template']) : | ||||||
|                                               data['template'] |                                               data['template'] | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
| @ -258,45 +234,38 @@ export class NormalizedDirectiveMetadata implements INormalizedDirectiveMetadata | |||||||
|       'isComponent': this.isComponent, |       'isComponent': this.isComponent, | ||||||
|       'dynamicLoadable': this.dynamicLoadable, |       'dynamicLoadable': this.dynamicLoadable, | ||||||
|       'selector': this.selector, |       'selector': this.selector, | ||||||
|  |       'exportAs': this.exportAs, | ||||||
|       'type': isPresent(this.type) ? this.type.toJson() : this.type, |       'type': isPresent(this.type) ? this.type.toJson() : this.type, | ||||||
|       'changeDetection': |       'changeDetection': isPresent(this.changeDetection) ? serializeEnum(this.changeDetection) : | ||||||
|           isPresent(this.changeDetection) ? this.changeDetection.toJson() : this.changeDetection, |                                                            this.changeDetection, | ||||||
|  |       'properties': this.properties, | ||||||
|  |       'events': this.events, | ||||||
|  |       'hostListeners': this.hostListeners, | ||||||
|  |       'hostProperties': this.hostProperties, | ||||||
|  |       'hostAttributes': this.hostAttributes, | ||||||
|  |       'lifecycleHooks': this.lifecycleHooks.map(hook => serializeEnum(hook)), | ||||||
|       'template': isPresent(this.template) ? this.template.toJson() : this.template |       'template': isPresent(this.template) ? this.template.toJson() : this.template | ||||||
|     }; |     }; | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function createHostComponentMeta(componentType: TypeMetadata, componentSelector: string): | export function createHostComponentMeta(componentType: CompileTypeMetadata, | ||||||
|     NormalizedDirectiveMetadata { |                                         componentSelector: string): CompileDirectiveMetadata { | ||||||
|   var template = CssSelector.parse(componentSelector)[0].getMatchingElementTemplate(); |   var template = CssSelector.parse(componentSelector)[0].getMatchingElementTemplate(); | ||||||
|   return new NormalizedDirectiveMetadata({ |   return CompileDirectiveMetadata.create({ | ||||||
|     type: new TypeMetadata({ |     type: new CompileTypeMetadata({ | ||||||
|       runtime: Object, |       runtime: Object, | ||||||
|       id: (componentType.id * -1) - 1, |       id: (componentType.id * -1) - 1, | ||||||
|       name: `Host${componentType.name}`, |       name: `Host${componentType.name}`, | ||||||
|       moduleId: componentType.moduleId |       moduleId: componentType.moduleId | ||||||
|     }), |     }), | ||||||
|     template: new NormalizedTemplateMetadata({ |     template: new CompileTemplateMetadata( | ||||||
|       template: template, |         {template: template, templateUrl: '', styles: [], styleUrls: [], ngContentSelectors: []}), | ||||||
|       styles: [], |  | ||||||
|       styleAbsUrls: [], |  | ||||||
|       hostAttributes: {}, |  | ||||||
|       ngContentSelectors: [] |  | ||||||
|     }), |  | ||||||
|     changeDetection: new ChangeDetectionMetadata({ |  | ||||||
|     changeDetection: ChangeDetectionStrategy.Default, |     changeDetection: ChangeDetectionStrategy.Default, | ||||||
|     properties: [], |     properties: [], | ||||||
|     events: [], |     events: [], | ||||||
|       hostListeners: {}, |     host: {}, | ||||||
|       hostProperties: {}, |     lifecycleHooks: [], | ||||||
|       callAfterContentInit: false, |  | ||||||
|       callAfterContentChecked: false, |  | ||||||
|       callAfterViewInit: false, |  | ||||||
|       callAfterViewChecked: false, |  | ||||||
|       callOnChanges: false, |  | ||||||
|       callDoCheck: false, |  | ||||||
|       callOnInit: false |  | ||||||
|     }), |  | ||||||
|     isComponent: true, |     isComponent: true, | ||||||
|     dynamicLoadable: false, |     dynamicLoadable: false, | ||||||
|     selector: '*' |     selector: '*' | ||||||
|  | |||||||
| @ -8,14 +8,14 @@ import { | |||||||
|   RegExpWrapper |   RegExpWrapper | ||||||
| } from 'angular2/src/core/facade/lang'; | } from 'angular2/src/core/facade/lang'; | ||||||
| import {BaseException} from 'angular2/src/core/facade/exceptions'; | import {BaseException} from 'angular2/src/core/facade/exceptions'; | ||||||
| import {MapWrapper, StringMapWrapper} from 'angular2/src/core/facade/collection'; | import {MapWrapper, StringMapWrapper, ListWrapper} from 'angular2/src/core/facade/collection'; | ||||||
| import * as cpl from './directive_metadata'; | import * as cpl from './directive_metadata'; | ||||||
| import * as dirAnn from 'angular2/src/core/metadata/directives'; | import * as dirAnn from 'angular2/src/core/metadata/directives'; | ||||||
| import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver'; | import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver'; | ||||||
| import {ViewResolver} from 'angular2/src/core/compiler/view_resolver'; | import {ViewResolver} from 'angular2/src/core/compiler/view_resolver'; | ||||||
| import {ViewMetadata} from 'angular2/src/core/metadata/view'; | import {ViewMetadata} from 'angular2/src/core/metadata/view'; | ||||||
| import {hasLifecycleHook} from 'angular2/src/core/compiler/directive_lifecycle_reflector'; | import {hasLifecycleHook} from 'angular2/src/core/compiler/directive_lifecycle_reflector'; | ||||||
| import {LifecycleHooks} from 'angular2/src/core/compiler/interfaces'; | import {LifecycleHooks, LIFECYCLE_HOOKS_VALUES} from 'angular2/src/core/compiler/interfaces'; | ||||||
| import {reflector} from 'angular2/src/core/reflection/reflection'; | import {reflector} from 'angular2/src/core/reflection/reflection'; | ||||||
| import {Injectable} from 'angular2/src/core/di'; | import {Injectable} from 'angular2/src/core/di'; | ||||||
| 
 | 
 | ||||||
| @ -26,81 +26,55 @@ var HOST_REG_EXP = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))$/g; | |||||||
| @Injectable() | @Injectable() | ||||||
| export class RuntimeMetadataResolver { | export class RuntimeMetadataResolver { | ||||||
|   private _directiveCounter = 0; |   private _directiveCounter = 0; | ||||||
|   private _cache: Map<Type, cpl.DirectiveMetadata> = new Map(); |   private _cache: Map<Type, cpl.CompileDirectiveMetadata> = new Map(); | ||||||
| 
 | 
 | ||||||
|   constructor(private _directiveResolver: DirectiveResolver, private _viewResolver: ViewResolver) {} |   constructor(private _directiveResolver: DirectiveResolver, private _viewResolver: ViewResolver) {} | ||||||
| 
 | 
 | ||||||
|   getMetadata(directiveType: Type): cpl.DirectiveMetadata { |   getMetadata(directiveType: Type): cpl.CompileDirectiveMetadata { | ||||||
|     var meta = this._cache.get(directiveType); |     var meta = this._cache.get(directiveType); | ||||||
|     if (isBlank(meta)) { |     if (isBlank(meta)) { | ||||||
|       var directiveAnnotation = this._directiveResolver.resolve(directiveType); |       var directiveAnnotation = this._directiveResolver.resolve(directiveType); | ||||||
|       var moduleId = calcModuleId(directiveType, directiveAnnotation); |       var moduleId = calcModuleId(directiveType, directiveAnnotation); | ||||||
|       var templateMeta = null; |       var templateMeta = null; | ||||||
|       var hostListeners = {}; |  | ||||||
|       var hostProperties = {}; |  | ||||||
|       var hostAttributes = {}; |  | ||||||
|       var changeDetectionStrategy = null; |       var changeDetectionStrategy = null; | ||||||
|       var dynamicLoadable: boolean = false; |  | ||||||
| 
 | 
 | ||||||
|       if (isPresent(directiveAnnotation.host)) { |  | ||||||
|         StringMapWrapper.forEach(directiveAnnotation.host, (value: string, key: string) => { |  | ||||||
|           var matches = RegExpWrapper.firstMatch(HOST_REG_EXP, key); |  | ||||||
|           if (isBlank(matches)) { |  | ||||||
|             hostAttributes[key] = value; |  | ||||||
|           } else if (isPresent(matches[1])) { |  | ||||||
|             hostProperties[matches[1]] = value; |  | ||||||
|           } else if (isPresent(matches[2])) { |  | ||||||
|             hostListeners[matches[2]] = value; |  | ||||||
|           } |  | ||||||
|         }); |  | ||||||
|       } |  | ||||||
|       if (directiveAnnotation instanceof dirAnn.ComponentMetadata) { |       if (directiveAnnotation instanceof dirAnn.ComponentMetadata) { | ||||||
|         var compAnnotation = <dirAnn.ComponentMetadata>directiveAnnotation; |         var compAnnotation = <dirAnn.ComponentMetadata>directiveAnnotation; | ||||||
|         var viewAnnotation = this._viewResolver.resolve(directiveType); |         var viewAnnotation = this._viewResolver.resolve(directiveType); | ||||||
|         templateMeta = new cpl.TemplateMetadata({ |         templateMeta = new cpl.CompileTemplateMetadata({ | ||||||
|           encapsulation: viewAnnotation.encapsulation, |           encapsulation: viewAnnotation.encapsulation, | ||||||
|           template: viewAnnotation.template, |           template: viewAnnotation.template, | ||||||
|           templateUrl: viewAnnotation.templateUrl, |           templateUrl: viewAnnotation.templateUrl, | ||||||
|           styles: viewAnnotation.styles, |           styles: viewAnnotation.styles, | ||||||
|           styleUrls: viewAnnotation.styleUrls, |           styleUrls: viewAnnotation.styleUrls | ||||||
|           hostAttributes: hostAttributes |  | ||||||
|         }); |         }); | ||||||
|         changeDetectionStrategy = compAnnotation.changeDetection; |         changeDetectionStrategy = compAnnotation.changeDetection; | ||||||
|         dynamicLoadable = compAnnotation.dynamicLoadable; |  | ||||||
|       } |       } | ||||||
|       meta = new cpl.DirectiveMetadata({ |       meta = cpl.CompileDirectiveMetadata.create({ | ||||||
|         selector: directiveAnnotation.selector, |         selector: directiveAnnotation.selector, | ||||||
|  |         exportAs: directiveAnnotation.exportAs, | ||||||
|         isComponent: isPresent(templateMeta), |         isComponent: isPresent(templateMeta), | ||||||
|         dynamicLoadable: dynamicLoadable, |         dynamicLoadable: true, | ||||||
|         type: new cpl.TypeMetadata({ |         type: new cpl.CompileTypeMetadata({ | ||||||
|           id: this._directiveCounter++, |           id: this._directiveCounter++, | ||||||
|           name: stringify(directiveType), |           name: stringify(directiveType), | ||||||
|           moduleId: moduleId, |           moduleId: moduleId, | ||||||
|           runtime: directiveType |           runtime: directiveType | ||||||
|         }), |         }), | ||||||
|         template: templateMeta, |         template: templateMeta, | ||||||
|         changeDetection: new cpl.ChangeDetectionMetadata({ |  | ||||||
|         changeDetection: changeDetectionStrategy, |         changeDetection: changeDetectionStrategy, | ||||||
|         properties: directiveAnnotation.properties, |         properties: directiveAnnotation.properties, | ||||||
|         events: directiveAnnotation.events, |         events: directiveAnnotation.events, | ||||||
|           hostListeners: hostListeners, |         host: directiveAnnotation.host, | ||||||
|           hostProperties: hostProperties, |         lifecycleHooks: ListWrapper.filter(LIFECYCLE_HOOKS_VALUES, | ||||||
|           callAfterContentInit: hasLifecycleHook(LifecycleHooks.AfterContentInit, directiveType), |                                            hook => hasLifecycleHook(hook, directiveType)) | ||||||
|           callAfterContentChecked: |  | ||||||
|               hasLifecycleHook(LifecycleHooks.AfterContentChecked, directiveType), |  | ||||||
|           callAfterViewInit: hasLifecycleHook(LifecycleHooks.AfterViewInit, directiveType), |  | ||||||
|           callAfterViewChecked: hasLifecycleHook(LifecycleHooks.AfterViewChecked, directiveType), |  | ||||||
|           callOnChanges: hasLifecycleHook(LifecycleHooks.OnChanges, directiveType), |  | ||||||
|           callDoCheck: hasLifecycleHook(LifecycleHooks.DoCheck, directiveType), |  | ||||||
|           callOnInit: hasLifecycleHook(LifecycleHooks.OnInit, directiveType), |  | ||||||
|         }) |  | ||||||
|       }); |       }); | ||||||
|       this._cache.set(directiveType, meta); |       this._cache.set(directiveType, meta); | ||||||
|     } |     } | ||||||
|     return meta; |     return meta; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   getViewDirectivesMetadata(component: Type): cpl.DirectiveMetadata[] { |   getViewDirectivesMetadata(component: Type): cpl.CompileDirectiveMetadata[] { | ||||||
|     var view = this._viewResolver.resolve(component); |     var view = this._viewResolver.resolve(component); | ||||||
|     var directives = flattenDirectives(view); |     var directives = flattenDirectives(view); | ||||||
|     for (var i = 0; i < directives.length; i++) { |     for (var i = 0; i < directives.length; i++) { | ||||||
| @ -113,8 +87,9 @@ export class RuntimeMetadataResolver { | |||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function removeDuplicatedDirectives(directives: cpl.DirectiveMetadata[]): cpl.DirectiveMetadata[] { | function removeDuplicatedDirectives(directives: cpl.CompileDirectiveMetadata[]): | ||||||
|   var directivesMap: Map<number, cpl.DirectiveMetadata> = new Map(); |     cpl.CompileDirectiveMetadata[] { | ||||||
|  |   var directivesMap: Map<number, cpl.CompileDirectiveMetadata> = new Map(); | ||||||
|   directives.forEach((dirMeta) => { directivesMap.set(dirMeta.type.id, dirMeta); }); |   directives.forEach((dirMeta) => { directivesMap.set(dirMeta.type.id, dirMeta); }); | ||||||
|   return MapWrapper.values(directivesMap); |   return MapWrapper.values(directivesMap); | ||||||
| } | } | ||||||
|  | |||||||
| @ -35,6 +35,10 @@ export class SourceExpression { | |||||||
|   constructor(public declarations: string[], public expression: string) {} |   constructor(public declarations: string[], public expression: string) {} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | export class SourceExpressions { | ||||||
|  |   constructor(public declarations: string[], public expressions: string[]) {} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| export class SourceWithImports { | export class SourceWithImports { | ||||||
|   constructor(public source: string, public imports: string[][]) {} |   constructor(public source: string, public imports: string[][]) {} | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| import {TypeMetadata, NormalizedDirectiveMetadata} from './directive_metadata'; | import {CompileTypeMetadata, CompileDirectiveMetadata} from './directive_metadata'; | ||||||
| import {SourceModule, SourceExpression, moduleRef} from './source_module'; | import {SourceModule, SourceExpression, moduleRef} from './source_module'; | ||||||
| import {ViewEncapsulation} from 'angular2/src/core/render/api'; | import {ViewEncapsulation} from 'angular2/src/core/render/api'; | ||||||
| import {XHR} from 'angular2/src/core/render/xhr'; | import {XHR} from 'angular2/src/core/render/xhr'; | ||||||
| @ -29,16 +29,16 @@ export class StyleCompiler { | |||||||
| 
 | 
 | ||||||
|   constructor(private _xhr: XHR, private _urlResolver: UrlResolver) {} |   constructor(private _xhr: XHR, private _urlResolver: UrlResolver) {} | ||||||
| 
 | 
 | ||||||
|   compileComponentRuntime(component: NormalizedDirectiveMetadata): Promise<string[]> { |   compileComponentRuntime(component: CompileDirectiveMetadata): Promise<string[]> { | ||||||
|     var styles = component.template.styles; |     var styles = component.template.styles; | ||||||
|     var styleAbsUrls = component.template.styleAbsUrls; |     var styleAbsUrls = component.template.styleUrls; | ||||||
|     return this._loadStyles(styles, styleAbsUrls, |     return this._loadStyles(styles, styleAbsUrls, | ||||||
|                             component.template.encapsulation === ViewEncapsulation.Emulated) |                             component.template.encapsulation === ViewEncapsulation.Emulated) | ||||||
|         .then(styles => styles.map(style => StringWrapper.replaceAll(style, COMPONENT_REGEX, |         .then(styles => styles.map(style => StringWrapper.replaceAll(style, COMPONENT_REGEX, | ||||||
|                                                                      `${component.type.id}`))); |                                                                      `${component.type.id}`))); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   compileComponentCodeGen(component: NormalizedDirectiveMetadata): SourceExpression { |   compileComponentCodeGen(component: CompileDirectiveMetadata): SourceExpression { | ||||||
|     var shim = component.template.encapsulation === ViewEncapsulation.Emulated; |     var shim = component.template.encapsulation === ViewEncapsulation.Emulated; | ||||||
|     var suffix; |     var suffix; | ||||||
|     if (shim) { |     if (shim) { | ||||||
| @ -48,7 +48,7 @@ export class StyleCompiler { | |||||||
|     } else { |     } else { | ||||||
|       suffix = ''; |       suffix = ''; | ||||||
|     } |     } | ||||||
|     return this._styleCodeGen(component.template.styles, component.template.styleAbsUrls, shim, |     return this._styleCodeGen(component.template.styles, component.template.styleUrls, shim, | ||||||
|                               suffix); |                               suffix); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -62,6 +62,8 @@ export class StyleCompiler { | |||||||
|     ]; |     ]; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   clearCache() { this._styleCache.clear(); } | ||||||
|  | 
 | ||||||
|   private _loadStyles(plainStyles: string[], absUrls: string[], |   private _loadStyles(plainStyles: string[], absUrls: string[], | ||||||
|                       encapsulate: boolean): Promise<string[]> { |                       encapsulate: boolean): Promise<string[]> { | ||||||
|     var promises = absUrls.map((absUrl) => { |     var promises = absUrls.map((absUrl) => { | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| import {AST} from 'angular2/src/core/change_detection/change_detection'; | import {AST} from 'angular2/src/core/change_detection/change_detection'; | ||||||
| import {isPresent} from 'angular2/src/core/facade/lang'; | import {isPresent} from 'angular2/src/core/facade/lang'; | ||||||
| import {NormalizedDirectiveMetadata} from './directive_metadata'; | import {CompileDirectiveMetadata} from './directive_metadata'; | ||||||
| 
 | 
 | ||||||
| export interface TemplateAst { | export interface TemplateAst { | ||||||
|   sourceInfo: string; |   sourceInfo: string; | ||||||
| @ -38,7 +38,7 @@ export class BoundEventAst implements TemplateAst { | |||||||
|   visit(visitor: TemplateAstVisitor, context: any): any { |   visit(visitor: TemplateAstVisitor, context: any): any { | ||||||
|     return visitor.visitEvent(this, context); |     return visitor.visitEvent(this, context); | ||||||
|   } |   } | ||||||
|   getFullName(): string { |   get fullName() { | ||||||
|     if (isPresent(this.target)) { |     if (isPresent(this.target)) { | ||||||
|       return `${this.target}:${this.name}`; |       return `${this.target}:${this.name}`; | ||||||
|     } else { |     } else { | ||||||
| @ -57,7 +57,7 @@ export class VariableAst implements TemplateAst { | |||||||
| export class ElementAst implements TemplateAst { | export class ElementAst implements TemplateAst { | ||||||
|   constructor(public name: string, public attrs: AttrAst[], |   constructor(public name: string, public attrs: AttrAst[], | ||||||
|               public properties: BoundElementPropertyAst[], public events: BoundEventAst[], |               public properties: BoundElementPropertyAst[], public events: BoundEventAst[], | ||||||
|               public vars: VariableAst[], public directives: DirectiveAst[], |               public exportAsVars: VariableAst[], public directives: DirectiveAst[], | ||||||
|               public children: TemplateAst[], public ngContentIndex: number, |               public children: TemplateAst[], public ngContentIndex: number, | ||||||
|               public sourceInfo: string) {} |               public sourceInfo: string) {} | ||||||
|   visit(visitor: TemplateAstVisitor, context: any): any { |   visit(visitor: TemplateAstVisitor, context: any): any { | ||||||
| @ -65,11 +65,11 @@ export class ElementAst implements TemplateAst { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   isBound(): boolean { |   isBound(): boolean { | ||||||
|     return (this.properties.length > 0 || this.events.length > 0 || this.vars.length > 0 || |     return (this.properties.length > 0 || this.events.length > 0 || this.exportAsVars.length > 0 || | ||||||
|             this.directives.length > 0); |             this.directives.length > 0); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   getComponent(): NormalizedDirectiveMetadata { |   getComponent(): CompileDirectiveMetadata { | ||||||
|     return this.directives.length > 0 && this.directives[0].directive.isComponent ? |     return this.directives.length > 0 && this.directives[0].directive.isComponent ? | ||||||
|                this.directives[0].directive : |                this.directives[0].directive : | ||||||
|                null; |                null; | ||||||
| @ -94,10 +94,10 @@ export class BoundDirectivePropertyAst implements TemplateAst { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export class DirectiveAst implements TemplateAst { | export class DirectiveAst implements TemplateAst { | ||||||
|   constructor(public directive: NormalizedDirectiveMetadata, |   constructor(public directive: CompileDirectiveMetadata, | ||||||
|               public properties: BoundDirectivePropertyAst[], |               public properties: BoundDirectivePropertyAst[], | ||||||
|               public hostProperties: BoundElementPropertyAst[], public hostEvents: BoundEventAst[], |               public hostProperties: BoundElementPropertyAst[], public hostEvents: BoundEventAst[], | ||||||
|               public sourceInfo: string) {} |               public exportAsVars: VariableAst[], public sourceInfo: string) {} | ||||||
|   visit(visitor: TemplateAstVisitor, context: any): any { |   visit(visitor: TemplateAstVisitor, context: any): any { | ||||||
|     return visitor.visitDirective(this, context); |     return visitor.visitDirective(this, context); | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -1,16 +1,12 @@ | |||||||
| import {Type, Json, isBlank, stringify} from 'angular2/src/core/facade/lang'; | import {Type, Json, isBlank, stringify} from 'angular2/src/core/facade/lang'; | ||||||
| import {BaseException} from 'angular2/src/core/facade/exceptions'; |  | ||||||
| import {ListWrapper, SetWrapper} from 'angular2/src/core/facade/collection'; | import {ListWrapper, SetWrapper} from 'angular2/src/core/facade/collection'; | ||||||
| import {PromiseWrapper, Promise} from 'angular2/src/core/facade/async'; | import {PromiseWrapper, Promise} from 'angular2/src/core/facade/async'; | ||||||
| import {CompiledTemplate, TemplateCmd} from 'angular2/src/core/compiler/template_commands'; | import {CompiledTemplate, TemplateCmd} from 'angular2/src/core/compiler/template_commands'; | ||||||
| import { | import { | ||||||
|   createHostComponentMeta, |   createHostComponentMeta, | ||||||
|   DirectiveMetadata, |   CompileDirectiveMetadata, | ||||||
|   INormalizedDirectiveMetadata, |   CompileTypeMetadata, | ||||||
|   NormalizedDirectiveMetadata, |   CompileTemplateMetadata | ||||||
|   TypeMetadata, |  | ||||||
|   ChangeDetectionMetadata, |  | ||||||
|   NormalizedTemplateMetadata |  | ||||||
| } from './directive_metadata'; | } from './directive_metadata'; | ||||||
| import {TemplateAst} from './template_ast'; | import {TemplateAst} from './template_ast'; | ||||||
| import {Injectable} from 'angular2/src/core/di'; | import {Injectable} from 'angular2/src/core/di'; | ||||||
| @ -36,7 +32,8 @@ export class TemplateCompiler { | |||||||
|               private _commandCompiler: CommandCompiler, |               private _commandCompiler: CommandCompiler, | ||||||
|               private _cdCompiler: ChangeDetectionCompiler) {} |               private _cdCompiler: ChangeDetectionCompiler) {} | ||||||
| 
 | 
 | ||||||
|   normalizeDirectiveMetadata(directive: DirectiveMetadata): Promise<INormalizedDirectiveMetadata> { |   normalizeDirectiveMetadata(directive: | ||||||
|  |                                  CompileDirectiveMetadata): Promise<CompileDirectiveMetadata> { | ||||||
|     var normalizedTemplatePromise; |     var normalizedTemplatePromise; | ||||||
|     if (directive.isComponent) { |     if (directive.isComponent) { | ||||||
|       normalizedTemplatePromise = |       normalizedTemplatePromise = | ||||||
| @ -45,49 +42,58 @@ export class TemplateCompiler { | |||||||
|       normalizedTemplatePromise = PromiseWrapper.resolve(null); |       normalizedTemplatePromise = PromiseWrapper.resolve(null); | ||||||
|     } |     } | ||||||
|     return normalizedTemplatePromise.then( |     return normalizedTemplatePromise.then( | ||||||
|         (normalizedTemplate) => new NormalizedDirectiveMetadata({ |         (normalizedTemplate) => new CompileDirectiveMetadata({ | ||||||
|           selector: directive.selector, |  | ||||||
|           dynamicLoadable: directive.dynamicLoadable, |  | ||||||
|           isComponent: directive.isComponent, |  | ||||||
|           type: directive.type, |           type: directive.type, | ||||||
|           changeDetection: directive.changeDetection, template: normalizedTemplate |           isComponent: directive.isComponent, | ||||||
|  |           dynamicLoadable: directive.dynamicLoadable, | ||||||
|  |           selector: directive.selector, | ||||||
|  |           exportAs: directive.exportAs, | ||||||
|  |           changeDetection: directive.changeDetection, | ||||||
|  |           properties: directive.properties, | ||||||
|  |           events: directive.events, | ||||||
|  |           hostListeners: directive.hostListeners, | ||||||
|  |           hostProperties: directive.hostProperties, | ||||||
|  |           hostAttributes: directive.hostAttributes, | ||||||
|  |           lifecycleHooks: directive.lifecycleHooks, template: normalizedTemplate | ||||||
|         })); |         })); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   serializeDirectiveMetadata(metadata: INormalizedDirectiveMetadata): string { |   serializeDirectiveMetadata(metadata: CompileDirectiveMetadata): string { | ||||||
|     return Json.stringify((<NormalizedDirectiveMetadata>metadata).toJson()); |     return Json.stringify(metadata.toJson()); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   deserializeDirectiveMetadata(data: string): INormalizedDirectiveMetadata { |   deserializeDirectiveMetadata(data: string): CompileDirectiveMetadata { | ||||||
|     return NormalizedDirectiveMetadata.fromJson(Json.parse(data)); |     return CompileDirectiveMetadata.fromJson(Json.parse(data)); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   compileHostComponentRuntime(type: Type): Promise<CompiledTemplate> { |   compileHostComponentRuntime(type: Type): Promise<CompiledTemplate> { | ||||||
|     var compMeta: DirectiveMetadata = this._runtimeMetadataResolver.getMetadata(type); |     var compMeta: CompileDirectiveMetadata = this._runtimeMetadataResolver.getMetadata(type); | ||||||
|     if (isBlank(compMeta) || !compMeta.isComponent || !compMeta.dynamicLoadable) { |     var hostMeta: CompileDirectiveMetadata = | ||||||
|       throw new BaseException( |  | ||||||
|           `Could not compile '${stringify(type)}' because it is not dynamically loadable.`); |  | ||||||
|     } |  | ||||||
|     var hostMeta: NormalizedDirectiveMetadata = |  | ||||||
|         createHostComponentMeta(compMeta.type, compMeta.selector); |         createHostComponentMeta(compMeta.type, compMeta.selector); | ||||||
|     this._compileComponentRuntime(hostMeta, [compMeta], new Set()); |     this._compileComponentRuntime(hostMeta, [compMeta], new Set()); | ||||||
|     return this._compiledTemplateDone.get(hostMeta.type.id); |     return this._compiledTemplateDone.get(hostMeta.type.id); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private _compileComponentRuntime(compMeta: NormalizedDirectiveMetadata, |   clearCache() { | ||||||
|                                    viewDirectives: DirectiveMetadata[], |     this._styleCompiler.clearCache(); | ||||||
|  |     this._compiledTemplateCache.clear(); | ||||||
|  |     this._compiledTemplateDone.clear(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private _compileComponentRuntime(compMeta: CompileDirectiveMetadata, | ||||||
|  |                                    viewDirectives: CompileDirectiveMetadata[], | ||||||
|                                    compilingComponentIds: Set<number>): CompiledTemplate { |                                    compilingComponentIds: Set<number>): CompiledTemplate { | ||||||
|     var compiledTemplate = this._compiledTemplateCache.get(compMeta.type.id); |     var compiledTemplate = this._compiledTemplateCache.get(compMeta.type.id); | ||||||
|     var done = this._compiledTemplateDone.get(compMeta.type.id); |     var done = this._compiledTemplateDone.get(compMeta.type.id); | ||||||
|     if (isBlank(compiledTemplate)) { |     if (isBlank(compiledTemplate)) { | ||||||
|       var styles; |       var styles; | ||||||
|       var changeDetectorFactories; |       var changeDetectorFactory; | ||||||
|       var commands; |       var commands; | ||||||
|       compiledTemplate = |       compiledTemplate = | ||||||
|           new CompiledTemplate(compMeta.type.id, () => [changeDetectorFactories, commands, styles]); |           new CompiledTemplate(compMeta.type.id, () => [changeDetectorFactory, commands, styles]); | ||||||
|       this._compiledTemplateCache.set(compMeta.type.id, compiledTemplate); |       this._compiledTemplateCache.set(compMeta.type.id, compiledTemplate); | ||||||
|       compilingComponentIds.add(compMeta.type.id); |       compilingComponentIds.add(compMeta.type.id); | ||||||
|       done = PromiseWrapper.all([this._styleCompiler.compileComponentRuntime(compMeta)].concat( |       done = PromiseWrapper.all([<any>this._styleCompiler.compileComponentRuntime(compMeta)].concat( | ||||||
|                                     viewDirectives.map( |                                     viewDirectives.map( | ||||||
|                                         dirMeta => this.normalizeDirectiveMetadata(dirMeta)))) |                                         dirMeta => this.normalizeDirectiveMetadata(dirMeta)))) | ||||||
|                  .then((stylesAndNormalizedViewDirMetas: any[]) => { |                  .then((stylesAndNormalizedViewDirMetas: any[]) => { | ||||||
| @ -96,10 +102,12 @@ export class TemplateCompiler { | |||||||
|                    var parsedTemplate = this._templateParser.parse( |                    var parsedTemplate = this._templateParser.parse( | ||||||
|                        compMeta.template.template, normalizedViewDirMetas, compMeta.type.name); |                        compMeta.template.template, normalizedViewDirMetas, compMeta.type.name); | ||||||
| 
 | 
 | ||||||
|                    changeDetectorFactories = this._cdCompiler.compileComponentRuntime( |                    var changeDetectorFactories = this._cdCompiler.compileComponentRuntime( | ||||||
|                        compMeta.type, compMeta.changeDetection.changeDetection, parsedTemplate); |                        compMeta.type, compMeta.changeDetection, parsedTemplate); | ||||||
|  |                    changeDetectorFactory = changeDetectorFactories[0]; | ||||||
|                    styles = stylesAndNormalizedViewDirMetas[0]; |                    styles = stylesAndNormalizedViewDirMetas[0]; | ||||||
|                    commands = this._compileCommandsRuntime(compMeta, parsedTemplate, |                    commands = this._compileCommandsRuntime(compMeta, parsedTemplate, | ||||||
|  |                                                            changeDetectorFactories, | ||||||
|                                                            compilingComponentIds, childPromises); |                                                            compilingComponentIds, childPromises); | ||||||
|                    return PromiseWrapper.all(childPromises); |                    return PromiseWrapper.all(childPromises); | ||||||
|                  }) |                  }) | ||||||
| @ -112,12 +120,14 @@ export class TemplateCompiler { | |||||||
|     return compiledTemplate; |     return compiledTemplate; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private _compileCommandsRuntime(compMeta: NormalizedDirectiveMetadata, |   private _compileCommandsRuntime(compMeta: CompileDirectiveMetadata, parsedTemplate: TemplateAst[], | ||||||
|                                   parsedTemplate: TemplateAst[], compilingComponentIds: Set<number>, |                                   changeDetectorFactories: Function[], | ||||||
|  |                                   compilingComponentIds: Set<number>, | ||||||
|                                   childPromises: Promise<any>[]): TemplateCmd[] { |                                   childPromises: Promise<any>[]): TemplateCmd[] { | ||||||
|     return this._commandCompiler.compileComponentRuntime( |     return this._commandCompiler.compileComponentRuntime( | ||||||
|         compMeta, parsedTemplate, (childComponentDir: NormalizedDirectiveMetadata) => { |         compMeta, parsedTemplate, changeDetectorFactories, | ||||||
|           var childViewDirectives: DirectiveMetadata[] = |         (childComponentDir: CompileDirectiveMetadata) => { | ||||||
|  |           var childViewDirectives: CompileDirectiveMetadata[] = | ||||||
|               this._runtimeMetadataResolver.getViewDirectivesMetadata( |               this._runtimeMetadataResolver.getViewDirectivesMetadata( | ||||||
|                   childComponentDir.type.runtime); |                   childComponentDir.type.runtime); | ||||||
|           var childIsRecursive = SetWrapper.has(compilingComponentIds, childComponentDir.type.id); |           var childIsRecursive = SetWrapper.has(compilingComponentIds, childComponentDir.type.id); | ||||||
| @ -135,12 +145,12 @@ export class TemplateCompiler { | |||||||
|                           components: NormalizedComponentWithViewDirectives[]): SourceModule { |                           components: NormalizedComponentWithViewDirectives[]): SourceModule { | ||||||
|     var declarations = []; |     var declarations = []; | ||||||
|     var templateArguments = []; |     var templateArguments = []; | ||||||
|     var componentMetas: NormalizedDirectiveMetadata[] = []; |     var componentMetas: CompileDirectiveMetadata[] = []; | ||||||
|     components.forEach(componentWithDirs => { |     components.forEach(componentWithDirs => { | ||||||
|       var compMeta = <NormalizedDirectiveMetadata>componentWithDirs.component; |       var compMeta = <CompileDirectiveMetadata>componentWithDirs.component; | ||||||
|       componentMetas.push(compMeta); |       componentMetas.push(compMeta); | ||||||
|       this._processTemplateCodeGen(compMeta, |       this._processTemplateCodeGen(compMeta, | ||||||
|                                    <NormalizedDirectiveMetadata[]>componentWithDirs.directives, |                                    <CompileDirectiveMetadata[]>componentWithDirs.directives, | ||||||
|                                    declarations, templateArguments); |                                    declarations, templateArguments); | ||||||
|       if (compMeta.dynamicLoadable) { |       if (compMeta.dynamicLoadable) { | ||||||
|         var hostMeta = createHostComponentMeta(compMeta.type, compMeta.selector); |         var hostMeta = createHostComponentMeta(compMeta.type, compMeta.selector); | ||||||
| @ -148,7 +158,7 @@ export class TemplateCompiler { | |||||||
|         this._processTemplateCodeGen(hostMeta, [compMeta], declarations, templateArguments); |         this._processTemplateCodeGen(hostMeta, [compMeta], declarations, templateArguments); | ||||||
|       } |       } | ||||||
|     }); |     }); | ||||||
|     ListWrapper.forEachWithIndex(componentMetas, (compMeta: NormalizedDirectiveMetadata, |     ListWrapper.forEachWithIndex(componentMetas, (compMeta: CompileDirectiveMetadata, | ||||||
|                                                   index: number) => { |                                                   index: number) => { | ||||||
|       var templateDataFn = codeGenValueFn([], `[${templateArguments[index].join(',')}]`); |       var templateDataFn = codeGenValueFn([], `[${templateArguments[index].join(',')}]`); | ||||||
|       declarations.push( |       declarations.push( | ||||||
| @ -161,32 +171,33 @@ export class TemplateCompiler { | |||||||
|     return this._styleCompiler.compileStylesheetCodeGen(moduleId, cssText); |     return this._styleCompiler.compileStylesheetCodeGen(moduleId, cssText); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private _processTemplateCodeGen(compMeta: NormalizedDirectiveMetadata, |   private _processTemplateCodeGen(compMeta: CompileDirectiveMetadata, | ||||||
|                                   directives: NormalizedDirectiveMetadata[], |                                   directives: CompileDirectiveMetadata[], | ||||||
|                                   targetDeclarations: string[], targetTemplateArguments: any[][]) { |                                   targetDeclarations: string[], targetTemplateArguments: any[][]) { | ||||||
|     var styleExpr = this._styleCompiler.compileComponentCodeGen(compMeta); |     var styleExpr = this._styleCompiler.compileComponentCodeGen(compMeta); | ||||||
|     var parsedTemplate = |     var parsedTemplate = | ||||||
|         this._templateParser.parse(compMeta.template.template, directives, compMeta.type.name); |         this._templateParser.parse(compMeta.template.template, directives, compMeta.type.name); | ||||||
|     var changeDetectorsExpr = this._cdCompiler.compileComponentCodeGen( |     var changeDetectorsExprs = this._cdCompiler.compileComponentCodeGen( | ||||||
|         compMeta.type, compMeta.changeDetection.changeDetection, parsedTemplate); |         compMeta.type, compMeta.changeDetection, parsedTemplate); | ||||||
|     var commandsExpr = this._commandCompiler.compileComponentCodeGen( |     var commandsExpr = this._commandCompiler.compileComponentCodeGen( | ||||||
|         compMeta, parsedTemplate, codeGenComponentTemplateFactory); |         compMeta, parsedTemplate, changeDetectorsExprs.expressions, | ||||||
|  |         codeGenComponentTemplateFactory); | ||||||
| 
 | 
 | ||||||
|     addAll(styleExpr.declarations, targetDeclarations); |     addAll(styleExpr.declarations, targetDeclarations); | ||||||
|     addAll(changeDetectorsExpr.declarations, targetDeclarations); |     addAll(changeDetectorsExprs.declarations, targetDeclarations); | ||||||
|     addAll(commandsExpr.declarations, targetDeclarations); |     addAll(commandsExpr.declarations, targetDeclarations); | ||||||
| 
 | 
 | ||||||
|     targetTemplateArguments.push( |     targetTemplateArguments.push( | ||||||
|         [changeDetectorsExpr.expression, commandsExpr.expression, styleExpr.expression]); |         [changeDetectorsExprs.expressions[0], commandsExpr.expression, styleExpr.expression]); | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export class NormalizedComponentWithViewDirectives { | export class NormalizedComponentWithViewDirectives { | ||||||
|   constructor(public component: INormalizedDirectiveMetadata, |   constructor(public component: CompileDirectiveMetadata, | ||||||
|               public directives: INormalizedDirectiveMetadata[]) {} |               public directives: CompileDirectiveMetadata[]) {} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function templateVariableName(type: TypeMetadata): string { | function templateVariableName(type: CompileTypeMetadata): string { | ||||||
|   return `${type.name}Template`; |   return `${type.name}Template`; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -200,6 +211,6 @@ function addAll(source: any[], target: any[]) { | |||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function codeGenComponentTemplateFactory(nestedCompType: NormalizedDirectiveMetadata): string { | function codeGenComponentTemplateFactory(nestedCompType: CompileDirectiveMetadata): string { | ||||||
|   return `${moduleRef(templateModuleName(nestedCompType.type.moduleId))}${templateVariableName(nestedCompType.type)}`; |   return `${moduleRef(templateModuleName(nestedCompType.type.moduleId))}${templateVariableName(nestedCompType.type)}`; | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,8 +1,7 @@ | |||||||
| import { | import { | ||||||
|   TypeMetadata, |   CompileTypeMetadata, | ||||||
|   TemplateMetadata, |   CompileDirectiveMetadata, | ||||||
|   NormalizedDirectiveMetadata, |   CompileTemplateMetadata | ||||||
|   NormalizedTemplateMetadata |  | ||||||
| } from './directive_metadata'; | } from './directive_metadata'; | ||||||
| import {isPresent, isBlank} from 'angular2/src/core/facade/lang'; | import {isPresent, isBlank} from 'angular2/src/core/facade/lang'; | ||||||
| import {Promise, PromiseWrapper} from 'angular2/src/core/facade/async'; | import {Promise, PromiseWrapper} from 'angular2/src/core/facade/async'; | ||||||
| @ -11,6 +10,7 @@ import {XHR} from 'angular2/src/core/render/xhr'; | |||||||
| import {UrlResolver} from 'angular2/src/core/services/url_resolver'; | import {UrlResolver} from 'angular2/src/core/services/url_resolver'; | ||||||
| import {resolveStyleUrls} from './style_url_resolver'; | import {resolveStyleUrls} from './style_url_resolver'; | ||||||
| import {Injectable} from 'angular2/src/core/di'; | import {Injectable} from 'angular2/src/core/di'; | ||||||
|  | import {ViewEncapsulation} from 'angular2/src/core/render/api'; | ||||||
| 
 | 
 | ||||||
| import { | import { | ||||||
|   HtmlAstVisitor, |   HtmlAstVisitor, | ||||||
| @ -22,21 +22,15 @@ import { | |||||||
| } from './html_ast'; | } from './html_ast'; | ||||||
| import {HtmlParser} from './html_parser'; | import {HtmlParser} from './html_parser'; | ||||||
| 
 | 
 | ||||||
| const NG_CONTENT_SELECT_ATTR = 'select'; | import {preparseElement, PreparsedElement, PreparsedElementType} from './template_preparser'; | ||||||
| const NG_CONTENT_ELEMENT = 'ng-content'; |  | ||||||
| const LINK_ELEMENT = 'link'; |  | ||||||
| const LINK_STYLE_REL_ATTR = 'rel'; |  | ||||||
| const LINK_STYLE_HREF_ATTR = 'href'; |  | ||||||
| const LINK_STYLE_REL_VALUE = 'stylesheet'; |  | ||||||
| const STYLE_ELEMENT = 'style'; |  | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class TemplateNormalizer { | export class TemplateNormalizer { | ||||||
|   constructor(private _xhr: XHR, private _urlResolver: UrlResolver, |   constructor(private _xhr: XHR, private _urlResolver: UrlResolver, | ||||||
|               private _domParser: HtmlParser) {} |               private _domParser: HtmlParser) {} | ||||||
| 
 | 
 | ||||||
|   normalizeTemplate(directiveType: TypeMetadata, |   normalizeTemplate(directiveType: CompileTypeMetadata, | ||||||
|                     template: TemplateMetadata): Promise<NormalizedTemplateMetadata> { |                     template: CompileTemplateMetadata): Promise<CompileTemplateMetadata> { | ||||||
|     if (isPresent(template.template)) { |     if (isPresent(template.template)) { | ||||||
|       return PromiseWrapper.resolve(this.normalizeLoadedTemplate( |       return PromiseWrapper.resolve(this.normalizeLoadedTemplate( | ||||||
|           directiveType, template, template.template, directiveType.moduleId)); |           directiveType, template, template.template, directiveType.moduleId)); | ||||||
| @ -48,11 +42,11 @@ export class TemplateNormalizer { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   normalizeLoadedTemplate(directiveType: TypeMetadata, templateMeta: TemplateMetadata, |   normalizeLoadedTemplate(directiveType: CompileTypeMetadata, templateMeta: CompileTemplateMetadata, | ||||||
|                           template: string, templateAbsUrl: string): NormalizedTemplateMetadata { |                           template: string, templateAbsUrl: string): CompileTemplateMetadata { | ||||||
|     var domNodes = this._domParser.parse(template, directiveType.name); |     var domNodes = this._domParser.parse(template, directiveType.name); | ||||||
|     var visitor = new TemplatePreparseVisitor(); |     var visitor = new TemplatePreparseVisitor(); | ||||||
|     var remainingNodes = htmlVisitAll(visitor, domNodes); |     htmlVisitAll(visitor, domNodes); | ||||||
|     var allStyles = templateMeta.styles.concat(visitor.styles); |     var allStyles = templateMeta.styles.concat(visitor.styles); | ||||||
| 
 | 
 | ||||||
|     var allStyleAbsUrls = |     var allStyleAbsUrls = | ||||||
| @ -65,11 +59,17 @@ export class TemplateNormalizer { | |||||||
|       styleWithImports.styleUrls.forEach(styleUrl => allStyleAbsUrls.push(styleUrl)); |       styleWithImports.styleUrls.forEach(styleUrl => allStyleAbsUrls.push(styleUrl)); | ||||||
|       return styleWithImports.style; |       return styleWithImports.style; | ||||||
|     }); |     }); | ||||||
|     return new NormalizedTemplateMetadata({ |     var encapsulation = templateMeta.encapsulation; | ||||||
|       encapsulation: templateMeta.encapsulation, |     if (encapsulation === ViewEncapsulation.Emulated && allResolvedStyles.length === 0 && | ||||||
|       template: this._domParser.unparse(remainingNodes), |         allStyleAbsUrls.length === 0) { | ||||||
|  |       encapsulation = ViewEncapsulation.None; | ||||||
|  |     } | ||||||
|  |     return new CompileTemplateMetadata({ | ||||||
|  |       encapsulation: encapsulation, | ||||||
|  |       template: template, | ||||||
|  |       templateUrl: templateAbsUrl, | ||||||
|       styles: allResolvedStyles, |       styles: allResolvedStyles, | ||||||
|       styleAbsUrls: allStyleAbsUrls, |       styleUrls: allStyleAbsUrls, | ||||||
|       ngContentSelectors: visitor.ngContentSelectors |       ngContentSelectors: visitor.ngContentSelectors | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
| @ -80,25 +80,13 @@ class TemplatePreparseVisitor implements HtmlAstVisitor { | |||||||
|   styles: string[] = []; |   styles: string[] = []; | ||||||
|   styleUrls: string[] = []; |   styleUrls: string[] = []; | ||||||
| 
 | 
 | ||||||
|   visitElement(ast: HtmlElementAst, context: any): HtmlElementAst { |   visitElement(ast: HtmlElementAst, context: any): any { | ||||||
|     var selectAttr = null; |     var preparsedElement = preparseElement(ast); | ||||||
|     var hrefAttr = null; |     switch (preparsedElement.type) { | ||||||
|     var relAttr = null; |       case PreparsedElementType.NG_CONTENT: | ||||||
|     ast.attrs.forEach(attr => { |         this.ngContentSelectors.push(preparsedElement.selectAttr); | ||||||
|       if (attr.name == NG_CONTENT_SELECT_ATTR) { |         break; | ||||||
|         selectAttr = attr.value; |       case PreparsedElementType.STYLE: | ||||||
|       } else if (attr.name == LINK_STYLE_HREF_ATTR) { |  | ||||||
|         hrefAttr = attr.value; |  | ||||||
|       } else if (attr.name == LINK_STYLE_REL_ATTR) { |  | ||||||
|         relAttr = attr.value; |  | ||||||
|       } |  | ||||||
|     }); |  | ||||||
|     var nodeName = ast.name; |  | ||||||
|     var keepElement = true; |  | ||||||
|     if (nodeName == NG_CONTENT_ELEMENT) { |  | ||||||
|       this.ngContentSelectors.push(normalizeNgContentSelect(selectAttr)); |  | ||||||
|     } else if (nodeName == STYLE_ELEMENT) { |  | ||||||
|       keepElement = false; |  | ||||||
|         var textContent = ''; |         var textContent = ''; | ||||||
|         ast.children.forEach(child => { |         ast.children.forEach(child => { | ||||||
|           if (child instanceof HtmlTextAst) { |           if (child instanceof HtmlTextAst) { | ||||||
| @ -106,24 +94,16 @@ class TemplatePreparseVisitor implements HtmlAstVisitor { | |||||||
|           } |           } | ||||||
|         }); |         }); | ||||||
|         this.styles.push(textContent); |         this.styles.push(textContent); | ||||||
|     } else if (nodeName == LINK_ELEMENT && relAttr == LINK_STYLE_REL_VALUE) { |         break; | ||||||
|       keepElement = false; |       case PreparsedElementType.STYLESHEET: | ||||||
|       this.styleUrls.push(hrefAttr); |         this.styleUrls.push(preparsedElement.hrefAttr); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     if (preparsedElement.type !== PreparsedElementType.NON_BINDABLE) { | ||||||
|  |       htmlVisitAll(this, ast.children); | ||||||
|     } |     } | ||||||
|     if (keepElement) { |  | ||||||
|       return new HtmlElementAst(ast.name, ast.attrs, htmlVisitAll(this, ast.children), |  | ||||||
|                                 ast.sourceInfo); |  | ||||||
|     } else { |  | ||||||
|     return null; |     return null; | ||||||
|   } |   } | ||||||
|   } |   visitAttr(ast: HtmlAttrAst, context: any): any { return null; } | ||||||
|   visitAttr(ast: HtmlAttrAst, context: any): HtmlAttrAst { return ast; } |   visitText(ast: HtmlTextAst, context: any): any { return null; } | ||||||
|   visitText(ast: HtmlTextAst, context: any): HtmlTextAst { return ast; } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| function normalizeNgContentSelect(selectAttr: string): string { |  | ||||||
|   if (isBlank(selectAttr) || selectAttr.length === 0) { |  | ||||||
|     return '*'; |  | ||||||
|   } |  | ||||||
|   return selectAttr; |  | ||||||
| } | } | ||||||
| @ -1,4 +1,9 @@ | |||||||
| import {MapWrapper, ListWrapper, StringMapWrapper} from 'angular2/src/core/facade/collection'; | import { | ||||||
|  |   MapWrapper, | ||||||
|  |   ListWrapper, | ||||||
|  |   StringMapWrapper, | ||||||
|  |   SetWrapper | ||||||
|  | } from 'angular2/src/core/facade/collection'; | ||||||
| import { | import { | ||||||
|   RegExpWrapper, |   RegExpWrapper, | ||||||
|   isPresent, |   isPresent, | ||||||
| @ -12,7 +17,7 @@ import {Injectable} from 'angular2/src/core/di'; | |||||||
| import {BaseException} from 'angular2/src/core/facade/exceptions'; | import {BaseException} from 'angular2/src/core/facade/exceptions'; | ||||||
| import {Parser, AST, ASTWithSource} from 'angular2/src/core/change_detection/change_detection'; | import {Parser, AST, ASTWithSource} from 'angular2/src/core/change_detection/change_detection'; | ||||||
| import {TemplateBinding} from 'angular2/src/core/change_detection/parser/ast'; | import {TemplateBinding} from 'angular2/src/core/change_detection/parser/ast'; | ||||||
| import {NormalizedDirectiveMetadata} from './directive_metadata'; | import {CompileDirectiveMetadata} from './directive_metadata'; | ||||||
| import {HtmlParser} from './html_parser'; | import {HtmlParser} from './html_parser'; | ||||||
| 
 | 
 | ||||||
| import { | import { | ||||||
| @ -33,6 +38,7 @@ import { | |||||||
| import {CssSelector, SelectorMatcher} from 'angular2/src/core/render/dom/compiler/selector'; | import {CssSelector, SelectorMatcher} from 'angular2/src/core/render/dom/compiler/selector'; | ||||||
| 
 | 
 | ||||||
| import {ElementSchemaRegistry} from 'angular2/src/core/render/dom/schema/element_schema_registry'; | import {ElementSchemaRegistry} from 'angular2/src/core/render/dom/schema/element_schema_registry'; | ||||||
|  | import {preparseElement, PreparsedElement, PreparsedElementType} from './template_preparser'; | ||||||
| 
 | 
 | ||||||
| import { | import { | ||||||
|   HtmlAstVisitor, |   HtmlAstVisitor, | ||||||
| @ -43,7 +49,7 @@ import { | |||||||
|   htmlVisitAll |   htmlVisitAll | ||||||
| } from './html_ast'; | } from './html_ast'; | ||||||
| 
 | 
 | ||||||
| import {dashCaseToCamelCase, camelCaseToDashCase} from './util'; | import {dashCaseToCamelCase, camelCaseToDashCase, splitAtColon} from './util'; | ||||||
| 
 | 
 | ||||||
| // Group 1 = "bind-"
 | // Group 1 = "bind-"
 | ||||||
| // Group 2 = "var-" or "#"
 | // Group 2 = "var-" or "#"
 | ||||||
| @ -56,12 +62,10 @@ import {dashCaseToCamelCase, camelCaseToDashCase} from './util'; | |||||||
| var BIND_NAME_REGEXP = | var BIND_NAME_REGEXP = | ||||||
|     /^(?:(?:(?:(bind-)|(var-|#)|(on-)|(bindon-))(.+))|\[\(([^\)]+)\)\]|\[([^\]]+)\]|\(([^\)]+)\))$/g; |     /^(?:(?:(?:(bind-)|(var-|#)|(on-)|(bindon-))(.+))|\[\(([^\)]+)\)\]|\[([^\]]+)\]|\(([^\)]+)\))$/g; | ||||||
| 
 | 
 | ||||||
| const NG_CONTENT_ELEMENT = 'ng-content'; |  | ||||||
| const TEMPLATE_ELEMENT = 'template'; | const TEMPLATE_ELEMENT = 'template'; | ||||||
| const TEMPLATE_ATTR = 'template'; | const TEMPLATE_ATTR = 'template'; | ||||||
| const TEMPLATE_ATTR_PREFIX = '*'; | const TEMPLATE_ATTR_PREFIX = '*'; | ||||||
| const CLASS_ATTR = 'class'; | const CLASS_ATTR = 'class'; | ||||||
| const IMPLICIT_VAR_NAME = '$implicit'; |  | ||||||
| 
 | 
 | ||||||
| var PROPERTY_PARTS_SEPARATOR = new RegExp('\\.'); | var PROPERTY_PARTS_SEPARATOR = new RegExp('\\.'); | ||||||
| const ATTRIBUTE_PREFIX = 'attr'; | const ATTRIBUTE_PREFIX = 'attr'; | ||||||
| @ -75,7 +79,7 @@ export class TemplateParser { | |||||||
|   constructor(private _exprParser: Parser, private _schemaRegistry: ElementSchemaRegistry, |   constructor(private _exprParser: Parser, private _schemaRegistry: ElementSchemaRegistry, | ||||||
|               private _htmlParser: HtmlParser) {} |               private _htmlParser: HtmlParser) {} | ||||||
| 
 | 
 | ||||||
|   parse(template: string, directives: NormalizedDirectiveMetadata[], |   parse(template: string, directives: CompileDirectiveMetadata[], | ||||||
|         sourceInfo: string): TemplateAst[] { |         sourceInfo: string): TemplateAst[] { | ||||||
|     var parseVisitor = new TemplateParseVisitor(directives, this._exprParser, this._schemaRegistry); |     var parseVisitor = new TemplateParseVisitor(directives, this._exprParser, this._schemaRegistry); | ||||||
|     var result = |     var result = | ||||||
| @ -91,12 +95,15 @@ export class TemplateParser { | |||||||
| class TemplateParseVisitor implements HtmlAstVisitor { | class TemplateParseVisitor implements HtmlAstVisitor { | ||||||
|   selectorMatcher: SelectorMatcher; |   selectorMatcher: SelectorMatcher; | ||||||
|   errors: string[] = []; |   errors: string[] = []; | ||||||
|   constructor(directives: NormalizedDirectiveMetadata[], private _exprParser: Parser, |   directivesIndexByTypeId: Map<number, number> = new Map(); | ||||||
|  |   constructor(directives: CompileDirectiveMetadata[], private _exprParser: Parser, | ||||||
|               private _schemaRegistry: ElementSchemaRegistry) { |               private _schemaRegistry: ElementSchemaRegistry) { | ||||||
|     this.selectorMatcher = new SelectorMatcher(); |     this.selectorMatcher = new SelectorMatcher(); | ||||||
|     directives.forEach(directive => { |     ListWrapper.forEachWithIndex(directives, | ||||||
|  |                                  (directive: CompileDirectiveMetadata, index: number) => { | ||||||
|                                    var selector = CssSelector.parse(directive.selector); |                                    var selector = CssSelector.parse(directive.selector); | ||||||
|                                    this.selectorMatcher.addSelectables(selector, directive); |                                    this.selectorMatcher.addSelectables(selector, directive); | ||||||
|  |                                    this.directivesIndexByTypeId.set(directive.type.id, index); | ||||||
|                                  }); |                                  }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -154,6 +161,17 @@ class TemplateParseVisitor implements HtmlAstVisitor { | |||||||
| 
 | 
 | ||||||
|   visitElement(element: HtmlElementAst, component: Component): any { |   visitElement(element: HtmlElementAst, component: Component): any { | ||||||
|     var nodeName = element.name; |     var nodeName = element.name; | ||||||
|  |     var preparsedElement = preparseElement(element); | ||||||
|  |     if (preparsedElement.type === PreparsedElementType.SCRIPT || | ||||||
|  |         preparsedElement.type === PreparsedElementType.STYLE || | ||||||
|  |         preparsedElement.type === PreparsedElementType.STYLESHEET || | ||||||
|  |         preparsedElement.type === PreparsedElementType.NON_BINDABLE) { | ||||||
|  |       // Skipping <script> for security reasons
 | ||||||
|  |       // Skipping <style> and stylesheets as we already processed them
 | ||||||
|  |       // in the StyleCompiler
 | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     var matchableAttrs: string[][] = []; |     var matchableAttrs: string[][] = []; | ||||||
|     var elementOrDirectiveProps: BoundElementOrDirectiveProperty[] = []; |     var elementOrDirectiveProps: BoundElementOrDirectiveProperty[] = []; | ||||||
|     var vars: VariableAst[] = []; |     var vars: VariableAst[] = []; | ||||||
| @ -177,26 +195,29 @@ class TemplateParseVisitor implements HtmlAstVisitor { | |||||||
|         hasInlineTemplates = true; |         hasInlineTemplates = true; | ||||||
|       } |       } | ||||||
|     }); |     }); | ||||||
|  |     var isTemplateElement = nodeName == TEMPLATE_ELEMENT; | ||||||
|     var elementCssSelector = this._createElementCssSelector(nodeName, matchableAttrs); |     var elementCssSelector = this._createElementCssSelector(nodeName, matchableAttrs); | ||||||
|     var directives = this._createDirectiveAsts( |     var directives = this._createDirectiveAsts( | ||||||
|         element.name, this._parseDirectives(this.selectorMatcher, elementCssSelector), |         element.name, this._parseDirectives(this.selectorMatcher, elementCssSelector), | ||||||
|         elementOrDirectiveProps, element.sourceInfo); |         elementOrDirectiveProps, isTemplateElement ? [] : vars, element.sourceInfo); | ||||||
|     var elementProps: BoundElementPropertyAst[] = |     var elementProps: BoundElementPropertyAst[] = | ||||||
|         this._createElementPropertyAsts(element.name, elementOrDirectiveProps, directives); |         this._createElementPropertyAsts(element.name, elementOrDirectiveProps, directives); | ||||||
|     var children = htmlVisitAll(this, element.children, Component.create(directives)); |     var children = htmlVisitAll(this, element.children, Component.create(directives)); | ||||||
|     var elementNgContentIndex = |     var elementNgContentIndex = | ||||||
|         hasInlineTemplates ? null : component.findNgContentIndex(elementCssSelector); |         hasInlineTemplates ? null : component.findNgContentIndex(elementCssSelector); | ||||||
|     var parsedElement; |     var parsedElement; | ||||||
|     if (nodeName == NG_CONTENT_ELEMENT) { |     if (preparsedElement.type === PreparsedElementType.NG_CONTENT) { | ||||||
|       parsedElement = new NgContentAst(elementNgContentIndex, element.sourceInfo); |       parsedElement = new NgContentAst(elementNgContentIndex, element.sourceInfo); | ||||||
|     } else if (nodeName == TEMPLATE_ELEMENT) { |     } else if (isTemplateElement) { | ||||||
|       this._assertNoComponentsNorElementBindingsOnTemplate(directives, elementProps, events, |       this._assertNoComponentsNorElementBindingsOnTemplate(directives, elementProps, events, | ||||||
|                                                            element.sourceInfo); |                                                            element.sourceInfo); | ||||||
|       parsedElement = new EmbeddedTemplateAst(attrs, vars, directives, children, |       parsedElement = new EmbeddedTemplateAst(attrs, vars, directives, children, | ||||||
|                                               elementNgContentIndex, element.sourceInfo); |                                               elementNgContentIndex, element.sourceInfo); | ||||||
|     } else { |     } else { | ||||||
|       this._assertOnlyOneComponent(directives, element.sourceInfo); |       this._assertOnlyOneComponent(directives, element.sourceInfo); | ||||||
|       parsedElement = new ElementAst(nodeName, attrs, elementProps, events, vars, directives, |       var elementExportAsVars = ListWrapper.filter(vars, varAst => varAst.value.length === 0); | ||||||
|  |       parsedElement = | ||||||
|  |           new ElementAst(nodeName, attrs, elementProps, events, elementExportAsVars, directives, | ||||||
|                          children, elementNgContentIndex, element.sourceInfo); |                          children, elementNgContentIndex, element.sourceInfo); | ||||||
|     } |     } | ||||||
|     if (hasInlineTemplates) { |     if (hasInlineTemplates) { | ||||||
| @ -204,7 +225,7 @@ class TemplateParseVisitor implements HtmlAstVisitor { | |||||||
|           this._createElementCssSelector(TEMPLATE_ELEMENT, templateMatchableAttrs); |           this._createElementCssSelector(TEMPLATE_ELEMENT, templateMatchableAttrs); | ||||||
|       var templateDirectives = this._createDirectiveAsts( |       var templateDirectives = this._createDirectiveAsts( | ||||||
|           element.name, this._parseDirectives(this.selectorMatcher, templateCssSelector), |           element.name, this._parseDirectives(this.selectorMatcher, templateCssSelector), | ||||||
|           templateElementOrDirectiveProps, element.sourceInfo); |           templateElementOrDirectiveProps, [], element.sourceInfo); | ||||||
|       var templateElementProps: BoundElementPropertyAst[] = this._createElementPropertyAsts( |       var templateElementProps: BoundElementPropertyAst[] = this._createElementPropertyAsts( | ||||||
|           element.name, templateElementOrDirectiveProps, templateDirectives); |           element.name, templateElementOrDirectiveProps, templateDirectives); | ||||||
|       this._assertNoComponentsNorElementBindingsOnTemplate(templateDirectives, templateElementProps, |       this._assertNoComponentsNorElementBindingsOnTemplate(templateDirectives, templateElementProps, | ||||||
| @ -263,8 +284,8 @@ class TemplateParseVisitor implements HtmlAstVisitor { | |||||||
|       } else if (isPresent( |       } else if (isPresent( | ||||||
|                      bindParts[2])) {  // match: var-name / var-name="iden" / #name / #name="iden"
 |                      bindParts[2])) {  // match: var-name / var-name="iden" / #name / #name="iden"
 | ||||||
|         var identifier = bindParts[5]; |         var identifier = bindParts[5]; | ||||||
|         var value = attrValue.length === 0 ? IMPLICIT_VAR_NAME : attrValue; |         this._parseVariable(identifier, attrValue, attr.sourceInfo, targetMatchableAttrs, | ||||||
|         this._parseVariable(identifier, value, attr.sourceInfo, targetMatchableAttrs, targetVars); |                             targetVars); | ||||||
| 
 | 
 | ||||||
|       } else if (isPresent(bindParts[3])) {  // match: on-event
 |       } else if (isPresent(bindParts[3])) {  // match: on-event
 | ||||||
|         this._parseEvent(bindParts[5], attrValue, attr.sourceInfo, targetMatchableAttrs, |         this._parseEvent(bindParts[5], attrValue, attr.sourceInfo, targetMatchableAttrs, | ||||||
| @ -377,7 +398,7 @@ class TemplateParseVisitor implements HtmlAstVisitor { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private _parseDirectives(selectorMatcher: SelectorMatcher, |   private _parseDirectives(selectorMatcher: SelectorMatcher, | ||||||
|                            elementCssSelector: CssSelector): NormalizedDirectiveMetadata[] { |                            elementCssSelector: CssSelector): CompileDirectiveMetadata[] { | ||||||
|     var directives = []; |     var directives = []; | ||||||
|     selectorMatcher.match(elementCssSelector, |     selectorMatcher.match(elementCssSelector, | ||||||
|                           (selector, directive) => { directives.push(directive); }); |                           (selector, directive) => { directives.push(directive); }); | ||||||
| @ -385,7 +406,7 @@ class TemplateParseVisitor implements HtmlAstVisitor { | |||||||
|     // as selectorMatcher uses Maps inside.
 |     // as selectorMatcher uses Maps inside.
 | ||||||
|     // Also need to make components the first directive in the array
 |     // Also need to make components the first directive in the array
 | ||||||
|     ListWrapper.sort(directives, |     ListWrapper.sort(directives, | ||||||
|                      (dir1: NormalizedDirectiveMetadata, dir2: NormalizedDirectiveMetadata) => { |                      (dir1: CompileDirectiveMetadata, dir2: CompileDirectiveMetadata) => { | ||||||
|                        var dir1Comp = dir1.isComponent; |                        var dir1Comp = dir1.isComponent; | ||||||
|                        var dir2Comp = dir2.isComponent; |                        var dir2Comp = dir2.isComponent; | ||||||
|                        if (dir1Comp && !dir2Comp) { |                        if (dir1Comp && !dir2Comp) { | ||||||
| @ -393,29 +414,44 @@ class TemplateParseVisitor implements HtmlAstVisitor { | |||||||
|                        } else if (!dir1Comp && dir2Comp) { |                        } else if (!dir1Comp && dir2Comp) { | ||||||
|                          return 1; |                          return 1; | ||||||
|                        } else { |                        } else { | ||||||
|                          return StringWrapper.compare(dir1.type.name, dir2.type.name); |                          return this.directivesIndexByTypeId.get(dir1.type.id) - | ||||||
|  |                                 this.directivesIndexByTypeId.get(dir2.type.id); | ||||||
|                        } |                        } | ||||||
|                      }); |                      }); | ||||||
|     return directives; |     return directives; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private _createDirectiveAsts(elementName: string, directives: NormalizedDirectiveMetadata[], |   private _createDirectiveAsts(elementName: string, directives: CompileDirectiveMetadata[], | ||||||
|                                props: BoundElementOrDirectiveProperty[], |                                props: BoundElementOrDirectiveProperty[], | ||||||
|  |                                possibleExportAsVars: VariableAst[], | ||||||
|                                sourceInfo: string): DirectiveAst[] { |                                sourceInfo: string): DirectiveAst[] { | ||||||
|     return directives.map((directive: NormalizedDirectiveMetadata) => { |     var matchedVariables: Set<string> = new Set(); | ||||||
|  |     var directiveAsts = directives.map((directive: CompileDirectiveMetadata) => { | ||||||
|       var hostProperties: BoundElementPropertyAst[] = []; |       var hostProperties: BoundElementPropertyAst[] = []; | ||||||
|       var hostEvents: BoundEventAst[] = []; |       var hostEvents: BoundEventAst[] = []; | ||||||
|       var directiveProperties: BoundDirectivePropertyAst[] = []; |       var directiveProperties: BoundDirectivePropertyAst[] = []; | ||||||
|       var changeDetection = directive.changeDetection; |       this._createDirectiveHostPropertyAsts(elementName, directive.hostProperties, sourceInfo, | ||||||
|       if (isPresent(changeDetection)) { |                                             hostProperties); | ||||||
|         this._createDirectiveHostPropertyAsts(elementName, changeDetection.hostProperties, |       this._createDirectiveHostEventAsts(directive.hostListeners, sourceInfo, hostEvents); | ||||||
|                                               sourceInfo, hostProperties); |       this._createDirectivePropertyAsts(directive.properties, props, directiveProperties); | ||||||
|         this._createDirectiveHostEventAsts(changeDetection.hostListeners, sourceInfo, hostEvents); |       var exportAsVars = []; | ||||||
|         this._createDirectivePropertyAsts(changeDetection.properties, props, directiveProperties); |       possibleExportAsVars.forEach((varAst) => { | ||||||
|  |         if ((varAst.value.length === 0 && directive.isComponent) || | ||||||
|  |             (directive.exportAs == varAst.value)) { | ||||||
|  |           exportAsVars.push(varAst); | ||||||
|  |           matchedVariables.add(varAst.name); | ||||||
|         } |         } | ||||||
|       return new DirectiveAst(directive, directiveProperties, hostProperties, hostEvents, |  | ||||||
|                               sourceInfo); |  | ||||||
|       }); |       }); | ||||||
|  |       return new DirectiveAst(directive, directiveProperties, hostProperties, hostEvents, | ||||||
|  |                               exportAsVars, sourceInfo); | ||||||
|  |     }); | ||||||
|  |     possibleExportAsVars.forEach((varAst) => { | ||||||
|  |       if (varAst.value.length > 0 && !SetWrapper.has(matchedVariables, varAst.name)) { | ||||||
|  |         this._reportError( | ||||||
|  |             `There is no directive with "exportAs" set to "${varAst.value}" at ${varAst.sourceInfo}`); | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |     return directiveAsts; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private _createDirectiveHostPropertyAsts(elementName: string, |   private _createDirectiveHostPropertyAsts(elementName: string, | ||||||
| @ -439,7 +475,7 @@ class TemplateParseVisitor implements HtmlAstVisitor { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private _createDirectivePropertyAsts(directiveProperties: string[], |   private _createDirectivePropertyAsts(directiveProperties: StringMap<string, string>, | ||||||
|                                        boundProps: BoundElementOrDirectiveProperty[], |                                        boundProps: BoundElementOrDirectiveProperty[], | ||||||
|                                        targetBoundDirectiveProps: BoundDirectivePropertyAst[]) { |                                        targetBoundDirectiveProps: BoundDirectivePropertyAst[]) { | ||||||
|     if (isPresent(directiveProperties)) { |     if (isPresent(directiveProperties)) { | ||||||
| @ -447,12 +483,8 @@ class TemplateParseVisitor implements HtmlAstVisitor { | |||||||
|       boundProps.forEach(boundProp => |       boundProps.forEach(boundProp => | ||||||
|                              boundPropsByName.set(dashCaseToCamelCase(boundProp.name), boundProp)); |                              boundPropsByName.set(dashCaseToCamelCase(boundProp.name), boundProp)); | ||||||
| 
 | 
 | ||||||
|       directiveProperties.forEach((bindConfig: string) => { |       StringMapWrapper.forEach(directiveProperties, (elProp: string, dirProp: string) => { | ||||||
|         // canonical syntax: `dirProp: elProp`
 |         elProp = dashCaseToCamelCase(elProp); | ||||||
|         // if there is no `:`, use dirProp = elProp
 |  | ||||||
|         var parts = splitAtColon(bindConfig, [bindConfig, bindConfig]); |  | ||||||
|         var dirProp = parts[0]; |  | ||||||
|         var elProp = dashCaseToCamelCase(parts[1]); |  | ||||||
|         var boundProp = boundPropsByName.get(elProp); |         var boundProp = boundPropsByName.get(elProp); | ||||||
| 
 | 
 | ||||||
|         // Bindings are optional, so this binding only needs to be set up if an expression is given.
 |         // Bindings are optional, so this binding only needs to be set up if an expression is given.
 | ||||||
| @ -566,15 +598,6 @@ export function splitClasses(classAttrValue: string): string[] { | |||||||
|   return StringWrapper.split(classAttrValue.trim(), /\s+/g); |   return StringWrapper.split(classAttrValue.trim(), /\s+/g); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function splitAtColon(input: string, defaultValues: string[]): string[] { |  | ||||||
|   var parts = StringWrapper.split(input.trim(), /\s*:\s*/g); |  | ||||||
|   if (parts.length > 1) { |  | ||||||
|     return parts; |  | ||||||
|   } else { |  | ||||||
|     return defaultValues; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| class Component { | class Component { | ||||||
|   static create(directives: DirectiveAst[]): Component { |   static create(directives: DirectiveAst[]): Component { | ||||||
|     if (directives.length === 0 || !directives[0].directive.isComponent) { |     if (directives.length === 0 || !directives[0].directive.isComponent) { | ||||||
|  | |||||||
							
								
								
									
										67
									
								
								modules/angular2/src/compiler/template_preparser.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								modules/angular2/src/compiler/template_preparser.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,67 @@ | |||||||
|  | import {HtmlElementAst} from './html_ast'; | ||||||
|  | import {isBlank, isPresent} from 'angular2/src/core/facade/lang'; | ||||||
|  | 
 | ||||||
|  | const NG_CONTENT_SELECT_ATTR = 'select'; | ||||||
|  | const NG_CONTENT_ELEMENT = 'ng-content'; | ||||||
|  | const LINK_ELEMENT = 'link'; | ||||||
|  | const LINK_STYLE_REL_ATTR = 'rel'; | ||||||
|  | const LINK_STYLE_HREF_ATTR = 'href'; | ||||||
|  | const LINK_STYLE_REL_VALUE = 'stylesheet'; | ||||||
|  | const STYLE_ELEMENT = 'style'; | ||||||
|  | const SCRIPT_ELEMENT = 'script'; | ||||||
|  | const NG_NON_BINDABLE_ATTR = 'ng-non-bindable'; | ||||||
|  | 
 | ||||||
|  | export function preparseElement(ast: HtmlElementAst): PreparsedElement { | ||||||
|  |   var selectAttr = null; | ||||||
|  |   var hrefAttr = null; | ||||||
|  |   var relAttr = null; | ||||||
|  |   var nonBindable = false; | ||||||
|  |   ast.attrs.forEach(attr => { | ||||||
|  |     if (attr.name == NG_CONTENT_SELECT_ATTR) { | ||||||
|  |       selectAttr = attr.value; | ||||||
|  |     } else if (attr.name == LINK_STYLE_HREF_ATTR) { | ||||||
|  |       hrefAttr = attr.value; | ||||||
|  |     } else if (attr.name == LINK_STYLE_REL_ATTR) { | ||||||
|  |       relAttr = attr.value; | ||||||
|  |     } else if (attr.name == NG_NON_BINDABLE_ATTR) { | ||||||
|  |       nonBindable = true; | ||||||
|  |     } | ||||||
|  |   }); | ||||||
|  |   selectAttr = normalizeNgContentSelect(selectAttr); | ||||||
|  |   var nodeName = ast.name; | ||||||
|  |   var type = PreparsedElementType.OTHER; | ||||||
|  |   if (nonBindable) { | ||||||
|  |     type = PreparsedElementType.NON_BINDABLE; | ||||||
|  |   } else if (nodeName == NG_CONTENT_ELEMENT) { | ||||||
|  |     type = PreparsedElementType.NG_CONTENT; | ||||||
|  |   } else if (nodeName == STYLE_ELEMENT) { | ||||||
|  |     type = PreparsedElementType.STYLE; | ||||||
|  |   } else if (nodeName == SCRIPT_ELEMENT) { | ||||||
|  |     type = PreparsedElementType.SCRIPT; | ||||||
|  |   } else if (nodeName == LINK_ELEMENT && relAttr == LINK_STYLE_REL_VALUE) { | ||||||
|  |     type = PreparsedElementType.STYLESHEET; | ||||||
|  |   } | ||||||
|  |   return new PreparsedElement(type, selectAttr, hrefAttr); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export enum PreparsedElementType { | ||||||
|  |   NG_CONTENT, | ||||||
|  |   STYLE, | ||||||
|  |   STYLESHEET, | ||||||
|  |   SCRIPT, | ||||||
|  |   NON_BINDABLE, | ||||||
|  |   OTHER | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export class PreparsedElement { | ||||||
|  |   constructor(public type: PreparsedElementType, public selectAttr: string, | ||||||
|  |               public hrefAttr: string) {} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | function normalizeNgContentSelect(selectAttr: string): string { | ||||||
|  |   if (isBlank(selectAttr) || selectAttr.length === 0) { | ||||||
|  |     return '*'; | ||||||
|  |   } | ||||||
|  |   return selectAttr; | ||||||
|  | } | ||||||
| @ -72,3 +72,13 @@ export function codeGenValueFn(params: string[], value: string): string { | |||||||
|     return `function(${params.join(',')}) { return ${value}; }`; |     return `function(${params.join(',')}) { return ${value}; }`; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | export function splitAtColon(input: string, defaultValues: string[]): string[] { | ||||||
|  |   var parts = StringWrapper.split(input.trim(), /\s*:\s*/g); | ||||||
|  |   if (parts.length > 1) { | ||||||
|  |     return parts; | ||||||
|  |   } else { | ||||||
|  |     return defaultValues; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | |||||||
| @ -42,7 +42,7 @@ export { | |||||||
|   DebugContext, |   DebugContext, | ||||||
|   ChangeDetectorGenConfig |   ChangeDetectorGenConfig | ||||||
| } from './interfaces'; | } from './interfaces'; | ||||||
| export {ChangeDetectionStrategy, changeDetectionStrategyFromJson} from './constants'; | export {ChangeDetectionStrategy, CHANGE_DECTION_STRATEGY_VALUES} from './constants'; | ||||||
| export {DynamicProtoChangeDetector} from './proto_change_detector'; | export {DynamicProtoChangeDetector} from './proto_change_detector'; | ||||||
| export {BindingRecord, BindingTarget} from './binding_record'; | export {BindingRecord, BindingTarget} from './binding_record'; | ||||||
| export {DirectiveIndex, DirectiveRecord} from './directive_record'; | export {DirectiveIndex, DirectiveRecord} from './directive_record'; | ||||||
|  | |||||||
| @ -1,11 +1,4 @@ | |||||||
| import { | import {StringWrapper, normalizeBool, isBlank} from 'angular2/src/core/facade/lang'; | ||||||
|   StringWrapper, |  | ||||||
|   normalizeBool, |  | ||||||
|   isBlank, |  | ||||||
|   serializeEnum, |  | ||||||
|   deserializeEnum |  | ||||||
| } from 'angular2/src/core/facade/lang'; |  | ||||||
| import {MapWrapper} from 'angular2/src/core/facade/collection'; |  | ||||||
| 
 | 
 | ||||||
| export enum ChangeDetectionStrategy { | export enum ChangeDetectionStrategy { | ||||||
|   /** |   /** | ||||||
| @ -48,19 +41,15 @@ export enum ChangeDetectionStrategy { | |||||||
|   OnPushObserve |   OnPushObserve | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| var strategyMap: Map<number, ChangeDetectionStrategy> = MapWrapper.createFromPairs([ | export var CHANGE_DECTION_STRATEGY_VALUES = [ | ||||||
|   [0, ChangeDetectionStrategy.CheckOnce], |   ChangeDetectionStrategy.CheckOnce, | ||||||
|   [1, ChangeDetectionStrategy.Checked], |   ChangeDetectionStrategy.Checked, | ||||||
|   [2, ChangeDetectionStrategy.CheckAlways], |   ChangeDetectionStrategy.CheckAlways, | ||||||
|   [3, ChangeDetectionStrategy.Detached], |   ChangeDetectionStrategy.Detached, | ||||||
|   [4, ChangeDetectionStrategy.OnPush], |   ChangeDetectionStrategy.OnPush, | ||||||
|   [5, ChangeDetectionStrategy.Default], |   ChangeDetectionStrategy.Default, | ||||||
|   [6, ChangeDetectionStrategy.OnPushObserve] |   ChangeDetectionStrategy.OnPushObserve | ||||||
| ]); | ]; | ||||||
| 
 |  | ||||||
| export function changeDetectionStrategyFromJson(value: number): ChangeDetectionStrategy { |  | ||||||
|   return deserializeEnum(value, strategyMap); |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| export function isDefaultChangeDetectionStrategy(changeDetectionStrategy: ChangeDetectionStrategy): | export function isDefaultChangeDetectionStrategy(changeDetectionStrategy: ChangeDetectionStrategy): | ||||||
|     boolean { |     boolean { | ||||||
|  | |||||||
| @ -117,7 +117,6 @@ export class DirectiveResolver { | |||||||
|         properties: mergedProperties, |         properties: mergedProperties, | ||||||
|         events: mergedEvents, |         events: mergedEvents, | ||||||
|         host: mergedHost, |         host: mergedHost, | ||||||
|         dynamicLoadable: dm.dynamicLoadable, |  | ||||||
|         bindings: dm.bindings, |         bindings: dm.bindings, | ||||||
|         exportAs: dm.exportAs, |         exportAs: dm.exportAs, | ||||||
|         moduleId: dm.moduleId, |         moduleId: dm.moduleId, | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| import {StringMap} from 'angular2/src/core/facade/collection'; | import {StringMap, MapWrapper} from 'angular2/src/core/facade/collection'; | ||||||
| 
 | 
 | ||||||
| export enum LifecycleHooks { | export enum LifecycleHooks { | ||||||
|   OnInit, |   OnInit, | ||||||
| @ -11,6 +11,17 @@ export enum LifecycleHooks { | |||||||
|   AfterViewChecked |   AfterViewChecked | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | export var LIFECYCLE_HOOKS_VALUES = [ | ||||||
|  |   LifecycleHooks.OnInit, | ||||||
|  |   LifecycleHooks.OnDestroy, | ||||||
|  |   LifecycleHooks.DoCheck, | ||||||
|  |   LifecycleHooks.OnChanges, | ||||||
|  |   LifecycleHooks.AfterContentInit, | ||||||
|  |   LifecycleHooks.AfterContentChecked, | ||||||
|  |   LifecycleHooks.AfterViewInit, | ||||||
|  |   LifecycleHooks.AfterViewChecked | ||||||
|  | ]; | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * Lifecycle hooks are guaranteed to be called in the following order: |  * Lifecycle hooks are guaranteed to be called in the following order: | ||||||
|  * - `OnChanges` (if any bindings have changed), |  * - `OnChanges` (if any bindings have changed), | ||||||
|  | |||||||
| @ -10,7 +10,7 @@ import { | |||||||
| } from 'angular2/src/core/render/render'; | } from 'angular2/src/core/render/render'; | ||||||
| 
 | 
 | ||||||
| export class CompiledTemplate { | export class CompiledTemplate { | ||||||
|   private _changeDetectorFactories: Function[] = null; |   private _changeDetectorFactory: Function = null; | ||||||
|   private _styles: string[] = null; |   private _styles: string[] = null; | ||||||
|   private _commands: TemplateCmd[] = null; |   private _commands: TemplateCmd[] = null; | ||||||
|   // Note: paramGetter is a function so that we can have cycles between templates!
 |   // Note: paramGetter is a function so that we can have cycles between templates!
 | ||||||
| @ -19,15 +19,15 @@ export class CompiledTemplate { | |||||||
|   private _init() { |   private _init() { | ||||||
|     if (isBlank(this._commands)) { |     if (isBlank(this._commands)) { | ||||||
|       var params = this._paramGetter(); |       var params = this._paramGetter(); | ||||||
|       this._changeDetectorFactories = params[0]; |       this._changeDetectorFactory = params[0]; | ||||||
|       this._commands = params[1]; |       this._commands = params[1]; | ||||||
|       this._styles = params[2]; |       this._styles = params[2]; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   get changeDetectorFactories(): Function[] { |   get changeDetectorFactory(): Function { | ||||||
|     this._init(); |     this._init(); | ||||||
|     return this._changeDetectorFactories; |     return this._changeDetectorFactory; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   get styles(): string[] { |   get styles(): string[] { | ||||||
| @ -69,26 +69,28 @@ export function ngContent(ngContentIndex: number): NgContentCmd { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export interface IBeginElementCmd extends TemplateCmd, RenderBeginElementCmd { | export interface IBeginElementCmd extends TemplateCmd, RenderBeginElementCmd { | ||||||
|   variableNameAndValues: string[]; |   variableNameAndValues: Array<string | number>; | ||||||
|   eventNames: string[]; |   eventTargetAndNames: string[]; | ||||||
|   directives: Type[]; |   directives: Type[]; | ||||||
|   visit(visitor: CommandVisitor, context: any): any; |   visit(visitor: CommandVisitor, context: any): any; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export class BeginElementCmd implements TemplateCmd, IBeginElementCmd, RenderBeginElementCmd { | export class BeginElementCmd implements TemplateCmd, IBeginElementCmd, RenderBeginElementCmd { | ||||||
|   constructor(public name: string, public attrNameAndValues: string[], public eventNames: string[], |   constructor(public name: string, public attrNameAndValues: string[], | ||||||
|               public variableNameAndValues: string[], public directives: Type[], |               public eventTargetAndNames: string[], | ||||||
|  |               public variableNameAndValues: Array<string | number>, public directives: Type[], | ||||||
|               public isBound: boolean, public ngContentIndex: number) {} |               public isBound: boolean, public ngContentIndex: number) {} | ||||||
|   visit(visitor: CommandVisitor, context: any): any { |   visit(visitor: CommandVisitor, context: any): any { | ||||||
|     return visitor.visitBeginElement(this, context); |     return visitor.visitBeginElement(this, context); | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function beginElement(name: string, attrNameAndValues: string[], eventNames: string[], | export function beginElement(name: string, attrNameAndValues: string[], | ||||||
|                              variableNameAndValues: string[], directives: Type[], isBound: boolean, |                              eventTargetAndNames: string[], | ||||||
|                              ngContentIndex: number): BeginElementCmd { |                              variableNameAndValues: Array<string | number>, directives: Type[], | ||||||
|   return new BeginElementCmd(name, attrNameAndValues, eventNames, variableNameAndValues, directives, |                              isBound: boolean, ngContentIndex: number): BeginElementCmd { | ||||||
|                              isBound, ngContentIndex); |   return new BeginElementCmd(name, attrNameAndValues, eventTargetAndNames, variableNameAndValues, | ||||||
|  |                              directives, isBound, ngContentIndex); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export class EndElementCmd implements TemplateCmd { | export class EndElementCmd implements TemplateCmd { | ||||||
| @ -103,8 +105,9 @@ export class BeginComponentCmd implements TemplateCmd, IBeginElementCmd, RenderB | |||||||
|   isBound: boolean = true; |   isBound: boolean = true; | ||||||
|   templateId: number; |   templateId: number; | ||||||
|   component: Type; |   component: Type; | ||||||
|   constructor(public name: string, public attrNameAndValues: string[], public eventNames: string[], |   constructor(public name: string, public attrNameAndValues: string[], | ||||||
|               public variableNameAndValues: string[], public directives: Type[], |               public eventTargetAndNames: string[], | ||||||
|  |               public variableNameAndValues: Array<string | number>, public directives: Type[], | ||||||
|               public nativeShadow: boolean, public ngContentIndex: number, |               public nativeShadow: boolean, public ngContentIndex: number, | ||||||
|               public template: CompiledTemplate) { |               public template: CompiledTemplate) { | ||||||
|     this.component = directives[0]; |     this.component = directives[0]; | ||||||
| @ -115,11 +118,11 @@ export class BeginComponentCmd implements TemplateCmd, IBeginElementCmd, RenderB | |||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function beginComponent(name: string, attrNameAnsValues: string[], eventNames: string[], | export function beginComponent( | ||||||
|                                variableNameAndValues: string[], directives: Type[], |     name: string, attrNameAnsValues: string[], eventTargetAndNames: string[], | ||||||
|                                nativeShadow: boolean, ngContentIndex: number, |     variableNameAndValues: Array<string | number>, directives: Type[], nativeShadow: boolean, | ||||||
|                                template: CompiledTemplate): BeginComponentCmd { |     ngContentIndex: number, template: CompiledTemplate): BeginComponentCmd { | ||||||
|   return new BeginComponentCmd(name, attrNameAnsValues, eventNames, variableNameAndValues, |   return new BeginComponentCmd(name, attrNameAnsValues, eventTargetAndNames, variableNameAndValues, | ||||||
|                                directives, nativeShadow, ngContentIndex, template); |                                directives, nativeShadow, ngContentIndex, template); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -135,10 +138,10 @@ export class EmbeddedTemplateCmd implements TemplateCmd, IBeginElementCmd, | |||||||
|     RenderEmbeddedTemplateCmd { |     RenderEmbeddedTemplateCmd { | ||||||
|   isBound: boolean = true; |   isBound: boolean = true; | ||||||
|   name: string = null; |   name: string = null; | ||||||
|   eventNames: string[] = EMPTY_ARR; |   eventTargetAndNames: string[] = EMPTY_ARR; | ||||||
|   constructor(public attrNameAndValues: string[], public variableNameAndValues: string[], |   constructor(public attrNameAndValues: string[], public variableNameAndValues: string[], | ||||||
|               public directives: Type[], public isMerged: boolean, public ngContentIndex: number, |               public directives: Type[], public isMerged: boolean, public ngContentIndex: number, | ||||||
|               public children: TemplateCmd[]) {} |               public changeDetectorFactory: Function, public children: TemplateCmd[]) {} | ||||||
|   visit(visitor: CommandVisitor, context: any): any { |   visit(visitor: CommandVisitor, context: any): any { | ||||||
|     return visitor.visitEmbeddedTemplate(this, context); |     return visitor.visitEmbeddedTemplate(this, context); | ||||||
|   } |   } | ||||||
| @ -146,20 +149,13 @@ export class EmbeddedTemplateCmd implements TemplateCmd, IBeginElementCmd, | |||||||
| 
 | 
 | ||||||
| export function embeddedTemplate(attrNameAndValues: string[], variableNameAndValues: string[], | export function embeddedTemplate(attrNameAndValues: string[], variableNameAndValues: string[], | ||||||
|                                  directives: Type[], isMerged: boolean, ngContentIndex: number, |                                  directives: Type[], isMerged: boolean, ngContentIndex: number, | ||||||
|                                  children: TemplateCmd[]): EmbeddedTemplateCmd { |                                  changeDetectorFactory: Function, children: TemplateCmd[]): | ||||||
|  |     EmbeddedTemplateCmd { | ||||||
|   return new EmbeddedTemplateCmd(attrNameAndValues, variableNameAndValues, directives, isMerged, |   return new EmbeddedTemplateCmd(attrNameAndValues, variableNameAndValues, directives, isMerged, | ||||||
|                                  ngContentIndex, children); |                                  ngContentIndex, changeDetectorFactory, children); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export interface CommandVisitor extends RenderCommandVisitor { | export interface CommandVisitor extends RenderCommandVisitor {} | ||||||
|   visitText(cmd: TextCmd, context: any): any; |  | ||||||
|   visitNgContent(cmd: NgContentCmd, context: any): any; |  | ||||||
|   visitBeginElement(cmd: BeginElementCmd, context: any): any; |  | ||||||
|   visitEndElement(context: any): any; |  | ||||||
|   visitBeginComponent(cmd: BeginComponentCmd, context: any): any; |  | ||||||
|   visitEndComponent(context: any): any; |  | ||||||
|   visitEmbeddedTemplate(cmd: EmbeddedTemplateCmd, context: any): any; |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| export function visitAllCommands(visitor: CommandVisitor, cmds: TemplateCmd[], | export function visitAllCommands(visitor: CommandVisitor, cmds: TemplateCmd[], | ||||||
|                                  context: any = null) { |                                  context: any = null) { | ||||||
|  | |||||||
| @ -36,7 +36,7 @@ class Directive extends DirectiveMetadata { | |||||||
|  */ |  */ | ||||||
| class Component extends ComponentMetadata { | class Component extends ComponentMetadata { | ||||||
|   const Component({String selector, List<String> properties, |   const Component({String selector, List<String> properties, | ||||||
|   List<String> events, Map<String, String> host, bool dynamicLoadable, |   List<String> events, Map<String, String> host, | ||||||
|   List bindings, String exportAs, String moduleId, |   List bindings, String exportAs, String moduleId, | ||||||
|   Map<String, dynamic> queries, |   Map<String, dynamic> queries, | ||||||
|   bool compileChildren, List viewBindings, ChangeDetectionStrategy changeDetection}) |   bool compileChildren, List viewBindings, ChangeDetectionStrategy changeDetection}) | ||||||
| @ -45,7 +45,6 @@ class Component extends ComponentMetadata { | |||||||
|     properties: properties, |     properties: properties, | ||||||
|     events: events, |     events: events, | ||||||
|     host: host, |     host: host, | ||||||
|     dynamicLoadable: dynamicLoadable, |  | ||||||
|     bindings: bindings, |     bindings: bindings, | ||||||
|     exportAs: exportAs, |     exportAs: exportAs, | ||||||
|     moduleId: moduleId, |     moduleId: moduleId, | ||||||
|  | |||||||
| @ -76,8 +76,8 @@ export interface ComponentDecorator extends TypeDecorator { | |||||||
|   View(obj: { |   View(obj: { | ||||||
|     templateUrl?: string, |     templateUrl?: string, | ||||||
|     template?: string, |     template?: string, | ||||||
|     directives?: Array<Type | any | any[]>, |     directives?: Array<Type | any[]>, | ||||||
|     pipes?: Array<Type | any | any[]>, |     pipes?: Array<Type | any[]>, | ||||||
|     renderer?: string, |     renderer?: string, | ||||||
|     styles?: string[], |     styles?: string[], | ||||||
|     styleUrls?: string[], |     styleUrls?: string[], | ||||||
| @ -96,8 +96,8 @@ export interface ViewDecorator extends TypeDecorator { | |||||||
|   View(obj: { |   View(obj: { | ||||||
|     templateUrl?: string, |     templateUrl?: string, | ||||||
|     template?: string, |     template?: string, | ||||||
|     directives?: Array<Type | any | any[]>, |     directives?: Array<Type | any[]>, | ||||||
|     pipes?: Array<Type | any | any[]>, |     pipes?: Array<Type | any[]>, | ||||||
|     renderer?: string, |     renderer?: string, | ||||||
|     styles?: string[], |     styles?: string[], | ||||||
|     styleUrls?: string[], |     styleUrls?: string[], | ||||||
| @ -218,7 +218,6 @@ export interface ComponentFactory { | |||||||
|     properties?: string[], |     properties?: string[], | ||||||
|     events?: string[], |     events?: string[], | ||||||
|     host?: StringMap<string, string>, |     host?: StringMap<string, string>, | ||||||
|     dynamicLoadable?: boolean, |  | ||||||
|     bindings?: any[], |     bindings?: any[], | ||||||
|     exportAs?: string, |     exportAs?: string, | ||||||
|     moduleId?: string, |     moduleId?: string, | ||||||
| @ -232,7 +231,6 @@ export interface ComponentFactory { | |||||||
|     properties?: string[], |     properties?: string[], | ||||||
|     events?: string[], |     events?: string[], | ||||||
|     host?: StringMap<string, string>, |     host?: StringMap<string, string>, | ||||||
|     dynamicLoadable?: boolean, |  | ||||||
|     bindings?: any[], |     bindings?: any[], | ||||||
|     exportAs?: string, |     exportAs?: string, | ||||||
|     moduleId?: string, |     moduleId?: string, | ||||||
| @ -290,7 +288,8 @@ export interface ViewFactory { | |||||||
|   (obj: { |   (obj: { | ||||||
|     templateUrl?: string, |     templateUrl?: string, | ||||||
|     template?: string, |     template?: string, | ||||||
|     directives?: Array<Type | any | any[]>, |     directives?: Array<Type | any[]>, | ||||||
|  |     pipes?: Array<Type | any[]>, | ||||||
|     encapsulation?: ViewEncapsulation, |     encapsulation?: ViewEncapsulation, | ||||||
|     styles?: string[], |     styles?: string[], | ||||||
|     styleUrls?: string[], |     styleUrls?: string[], | ||||||
| @ -298,7 +297,8 @@ export interface ViewFactory { | |||||||
|   new (obj: { |   new (obj: { | ||||||
|     templateUrl?: string, |     templateUrl?: string, | ||||||
|     template?: string, |     template?: string, | ||||||
|     directives?: Array<Type | any | any[]>, |     directives?: Array<Type | any[]>, | ||||||
|  |     pipes?: Array<Type | any[]>, | ||||||
|     encapsulation?: ViewEncapsulation, |     encapsulation?: ViewEncapsulation, | ||||||
|     styles?: string[], |     styles?: string[], | ||||||
|     styleUrls?: string[], |     styleUrls?: string[], | ||||||
|  | |||||||
| @ -826,27 +826,6 @@ export class DirectiveMetadata extends InjectableMetadata { | |||||||
|  */ |  */ | ||||||
| @CONST() | @CONST() | ||||||
| export class ComponentMetadata extends DirectiveMetadata { | export class ComponentMetadata extends DirectiveMetadata { | ||||||
|   /** |  | ||||||
|   * Declare that this component can be programatically loaded. |  | ||||||
|   * Every component that is used in bootstrap, routing, ... has to be |  | ||||||
|   * annotated with this. |  | ||||||
|   * |  | ||||||
|   * ## Example |  | ||||||
|   * |  | ||||||
|   * ``` |  | ||||||
|   * @Component({ |  | ||||||
|   *   selector: 'root', |  | ||||||
|   *   dynamicLoadable: true |  | ||||||
|   * }) |  | ||||||
|   * @View({ |  | ||||||
|   *   template: 'hello world!' |  | ||||||
|   * }) |  | ||||||
|   * class RootComponent { |  | ||||||
|   * } |  | ||||||
|   * ``` |  | ||||||
|    */ |  | ||||||
|   dynamicLoadable: boolean; |  | ||||||
| 
 |  | ||||||
|   /** |   /** | ||||||
|    * Defines the used change detection strategy. |    * Defines the used change detection strategy. | ||||||
|    * |    * | ||||||
| @ -900,14 +879,13 @@ export class ComponentMetadata extends DirectiveMetadata { | |||||||
|    */ |    */ | ||||||
|   viewBindings: any[]; |   viewBindings: any[]; | ||||||
| 
 | 
 | ||||||
|   constructor({selector, properties, events, host, dynamicLoadable, exportAs, moduleId, bindings, |   constructor({selector, properties, events, host, exportAs, moduleId, bindings, viewBindings, | ||||||
|                queries, viewBindings, changeDetection = ChangeDetectionStrategy.Default, |                changeDetection = ChangeDetectionStrategy.Default, queries, compileChildren = true}: | ||||||
|                compileChildren = true}: { |                   { | ||||||
|                     selector?: string, |                     selector?: string, | ||||||
|                     properties?: string[], |                     properties?: string[], | ||||||
|                     events?: string[], |                     events?: string[], | ||||||
|                     host?: StringMap<string, string>, |                     host?: StringMap<string, string>, | ||||||
|     dynamicLoadable?: boolean, |  | ||||||
|                     bindings?: any[], |                     bindings?: any[], | ||||||
|                     exportAs?: string, |                     exportAs?: string, | ||||||
|                     moduleId?: string, |                     moduleId?: string, | ||||||
| @ -930,7 +908,6 @@ export class ComponentMetadata extends DirectiveMetadata { | |||||||
| 
 | 
 | ||||||
|     this.changeDetection = changeDetection; |     this.changeDetection = changeDetection; | ||||||
|     this.viewBindings = viewBindings; |     this.viewBindings = viewBindings; | ||||||
|     this.dynamicLoadable = dynamicLoadable; |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -82,12 +82,9 @@ export class ViewMetadata { | |||||||
|    * } |    * } | ||||||
|    * ``` |    * ``` | ||||||
|    */ |    */ | ||||||
|   // TODO(tbosch): use Type | Binding | any[] when Dart supports union types,
 |   directives: Array<Type | any[]>; | ||||||
|   // as otherwise we would need to import Binding type and Dart would warn
 |  | ||||||
|   // for an unused import.
 |  | ||||||
|   directives: Array<Type | any | any[]>; |  | ||||||
| 
 | 
 | ||||||
|   pipes: Array<Type | any | any[]>; |   pipes: Array<Type | any[]>; | ||||||
| 
 | 
 | ||||||
|   /** |   /** | ||||||
|    * Specify how the template and the styles should be encapsulated. |    * Specify how the template and the styles should be encapsulated. | ||||||
| @ -100,8 +97,8 @@ export class ViewMetadata { | |||||||
|   constructor({templateUrl, template, directives, pipes, encapsulation, styles, styleUrls}: { |   constructor({templateUrl, template, directives, pipes, encapsulation, styles, styleUrls}: { | ||||||
|     templateUrl?: string, |     templateUrl?: string, | ||||||
|     template?: string, |     template?: string, | ||||||
|     directives?: Array<Type | any | any[]>, |     directives?: Array<Type | any[]>, | ||||||
|     pipes?: Array<Type | any | any[]>, |     pipes?: Array<Type | any[]>, | ||||||
|     encapsulation?: ViewEncapsulation, |     encapsulation?: ViewEncapsulation, | ||||||
|     styles?: string[], |     styles?: string[], | ||||||
|     styleUrls?: string[], |     styleUrls?: string[], | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| import {isPresent, isBlank, RegExpWrapper, deserializeEnum} from 'angular2/src/core/facade/lang'; | import {isPresent, isBlank, RegExpWrapper} from 'angular2/src/core/facade/lang'; | ||||||
| import {Promise} from 'angular2/src/core/facade/async'; | import {Promise} from 'angular2/src/core/facade/async'; | ||||||
| import {Map, MapWrapper, StringMap, StringMapWrapper} from 'angular2/src/core/facade/collection'; | import {Map, MapWrapper, StringMap, StringMapWrapper} from 'angular2/src/core/facade/collection'; | ||||||
| import { | import { | ||||||
| @ -309,11 +309,8 @@ export enum ViewEncapsulation { | |||||||
|   None |   None | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| var encapsulationMap: Map<number, ViewEncapsulation> = MapWrapper.createFromPairs( | export var VIEW_ENCAPSULATION_VALUES = | ||||||
|     [[0, ViewEncapsulation.Emulated], [1, ViewEncapsulation.Native], [2, ViewEncapsulation.None]]); |     [ViewEncapsulation.Emulated, ViewEncapsulation.Native, ViewEncapsulation.None]; | ||||||
| export function viewEncapsulationFromJson(value: number): ViewEncapsulation { |  | ||||||
|   return deserializeEnum(value, encapsulationMap); |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| export class ViewDefinition { | export class ViewDefinition { | ||||||
|   componentId: string; |   componentId: string; | ||||||
| @ -409,7 +406,7 @@ export interface RenderNgContentCmd extends RenderBeginCmd { ngContentIndex: num | |||||||
| export interface RenderBeginElementCmd extends RenderBeginCmd { | export interface RenderBeginElementCmd extends RenderBeginCmd { | ||||||
|   name: string; |   name: string; | ||||||
|   attrNameAndValues: string[]; |   attrNameAndValues: string[]; | ||||||
|   eventNames: string[]; |   eventTargetAndNames: string[]; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export interface RenderBeginComponentCmd extends RenderBeginElementCmd { | export interface RenderBeginComponentCmd extends RenderBeginElementCmd { | ||||||
| @ -422,15 +419,14 @@ export interface RenderEmbeddedTemplateCmd extends RenderBeginElementCmd { | |||||||
|   children: RenderTemplateCmd[]; |   children: RenderTemplateCmd[]; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // TODO(tbosch): change ts2dart to allow to use `CMD` as type in these methods!
 |  | ||||||
| export interface RenderCommandVisitor { | export interface RenderCommandVisitor { | ||||||
|   visitText /*<CMD extends RenderTextCmd>*/ (cmd: any, context: any): any; |   visitText(cmd: any, context: any): any; | ||||||
|   visitNgContent /*<CMD extends RenderNgContentCmd>*/ (cmd: any, context: any): any; |   visitNgContent(cmd: any, context: any): any; | ||||||
|   visitBeginElement /*<CMD extends RenderBeginElementCmd>*/ (cmd: any, context: any): any; |   visitBeginElement(cmd: any, context: any): any; | ||||||
|   visitEndElement(context: any): any; |   visitEndElement(context: any): any; | ||||||
|   visitBeginComponent /*<CMD extends RenderBeginComponentCmd>*/ (cmd: any, context: any): any; |   visitBeginComponent(cmd: any, context: any): any; | ||||||
|   visitEndComponent(context: any): any; |   visitEndComponent(context: any): any; | ||||||
|   visitEmbeddedTemplate /*<CMD extends RenderEmbeddedTemplateCmd>*/ (cmd: any, context: any): any; |   visitEmbeddedTemplate(cmd: any, context: any): any; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -29,7 +29,6 @@ export class MockDirectiveResolver extends DirectiveResolver { | |||||||
|         properties: dm.properties, |         properties: dm.properties, | ||||||
|         events: dm.events, |         events: dm.events, | ||||||
|         host: dm.host, |         host: dm.host, | ||||||
|         dynamicLoadable: dm.dynamicLoadable, |  | ||||||
|         bindings: bindings, |         bindings: bindings, | ||||||
|         exportAs: dm.exportAs, |         exportAs: dm.exportAs, | ||||||
|         moduleId: dm.moduleId, |         moduleId: dm.moduleId, | ||||||
|  | |||||||
| @ -14,9 +14,8 @@ import { | |||||||
| } from 'angular2/test_lib'; | } from 'angular2/test_lib'; | ||||||
| import {MapWrapper} from 'angular2/src/core/facade/collection'; | import {MapWrapper} from 'angular2/src/core/facade/collection'; | ||||||
| import { | import { | ||||||
|   NormalizedDirectiveMetadata, |   CompileDirectiveMetadata, | ||||||
|   TypeMetadata, |   CompileTypeMetadata | ||||||
|   ChangeDetectionMetadata |  | ||||||
| } from 'angular2/src/compiler/directive_metadata'; | } from 'angular2/src/compiler/directive_metadata'; | ||||||
| import {TemplateParser} from 'angular2/src/compiler/template_parser'; | import {TemplateParser} from 'angular2/src/compiler/template_parser'; | ||||||
| import { | import { | ||||||
| @ -60,10 +59,10 @@ export function main() { | |||||||
|       pipes = new TestPipes(); |       pipes = new TestPipes(); | ||||||
|     })); |     })); | ||||||
| 
 | 
 | ||||||
|     function createChangeDetector(template: string, directives: NormalizedDirectiveMetadata[], |     function createChangeDetector(template: string, directives: CompileDirectiveMetadata[], | ||||||
|                                   protoViewIndex: number = 0): ChangeDetector { |                                   protoViewIndex: number = 0): ChangeDetector { | ||||||
|       var protoChangeDetectors = |       var protoChangeDetectors = | ||||||
|           createChangeDetectorDefinitions(new TypeMetadata({name: 'SomeComp'}), |           createChangeDetectorDefinitions(new CompileTypeMetadata({name: 'SomeComp'}), | ||||||
|                                           ChangeDetectionStrategy.Default, |                                           ChangeDetectionStrategy.Default, | ||||||
|                                           new ChangeDetectorGenConfig(true, true, false, false), |                                           new ChangeDetectorGenConfig(true, true, false, false), | ||||||
|                                           parser.parse(template, directives, 'TestComp')) |                                           parser.parse(template, directives, 'TestComp')) | ||||||
| @ -97,6 +96,14 @@ export function main() { | |||||||
|       expect(context.eventLog).toEqual(['click']); |       expect(context.eventLog).toEqual(['click']); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|  |     it('should handle events with targets', () => { | ||||||
|  |       var changeDetector = createChangeDetector('<div (window:click)="onEvent($event)">', [], 0); | ||||||
|  | 
 | ||||||
|  |       eventLocals.set('$event', 'click'); | ||||||
|  |       changeDetector.handleEvent('window:click', 0, eventLocals); | ||||||
|  |       expect(context.eventLog).toEqual(['click']); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|     it('should watch variables', () => { |     it('should watch variables', () => { | ||||||
|       var changeDetector = createChangeDetector('<div #some-var [el-prop]="someVar">', [], 0); |       var changeDetector = createChangeDetector('<div #some-var [el-prop]="someVar">', [], 0); | ||||||
| 
 | 
 | ||||||
| @ -106,10 +113,10 @@ export function main() { | |||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     it('should write directive properties', () => { |     it('should write directive properties', () => { | ||||||
|       var dirMeta = new NormalizedDirectiveMetadata({ |       var dirMeta = CompileDirectiveMetadata.create({ | ||||||
|         type: new TypeMetadata({name: 'SomeDir'}), |         type: new CompileTypeMetadata({name: 'SomeDir'}), | ||||||
|         selector: 'div', |         selector: '[dir-prop]', | ||||||
|         changeDetection: new ChangeDetectionMetadata({properties: ['dirProp']}) |         properties: ['dirProp'] | ||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
|       var changeDetector = createChangeDetector('<div [dir-prop]="someProp">', [dirMeta], 0); |       var changeDetector = createChangeDetector('<div [dir-prop]="someProp">', [dirMeta], 0); | ||||||
| @ -119,11 +126,25 @@ export function main() { | |||||||
|       expect(directive.dirProp).toEqual('someValue'); |       expect(directive.dirProp).toEqual('someValue'); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|  |     it('should write template directive properties', () => { | ||||||
|  |       var dirMeta = CompileDirectiveMetadata.create({ | ||||||
|  |         type: new CompileTypeMetadata({name: 'SomeDir'}), | ||||||
|  |         selector: '[dir-prop]', | ||||||
|  |         properties: ['dirProp'] | ||||||
|  |       }); | ||||||
|  | 
 | ||||||
|  |       var changeDetector = createChangeDetector('<template [dir-prop]="someProp">', [dirMeta], 0); | ||||||
|  | 
 | ||||||
|  |       context.someProp = 'someValue'; | ||||||
|  |       changeDetector.detectChanges(); | ||||||
|  |       expect(directive.dirProp).toEqual('someValue'); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|     it('should watch directive host properties', () => { |     it('should watch directive host properties', () => { | ||||||
|       var dirMeta = new NormalizedDirectiveMetadata({ |       var dirMeta = CompileDirectiveMetadata.create({ | ||||||
|         type: new TypeMetadata({name: 'SomeDir'}), |         type: new CompileTypeMetadata({name: 'SomeDir'}), | ||||||
|         selector: 'div', |         selector: 'div', | ||||||
|         changeDetection: new ChangeDetectionMetadata({hostProperties: {'elProp': 'dirProp'}}) |         host: {'[elProp]': 'dirProp'} | ||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
|       var changeDetector = createChangeDetector('<div>', [dirMeta], 0); |       var changeDetector = createChangeDetector('<div>', [dirMeta], 0); | ||||||
| @ -134,11 +155,10 @@ export function main() { | |||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     it('should handle directive events', () => { |     it('should handle directive events', () => { | ||||||
|       var dirMeta = new NormalizedDirectiveMetadata({ |       var dirMeta = CompileDirectiveMetadata.create({ | ||||||
|         type: new TypeMetadata({name: 'SomeDir'}), |         type: new CompileTypeMetadata({name: 'SomeDir'}), | ||||||
|         selector: 'div', |         selector: 'div', | ||||||
|         changeDetection: |         host: {'(click)': 'onEvent($event)'} | ||||||
|             new ChangeDetectionMetadata({hostListeners: {'click': 'onEvent($event)'}}) |  | ||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
|       var changeDetector = createChangeDetector('<div>', [dirMeta], 0); |       var changeDetector = createChangeDetector('<div>', [dirMeta], 0); | ||||||
|  | |||||||
| @ -21,11 +21,15 @@ import {Promise} from 'angular2/src/core/facade/async'; | |||||||
| import {ChangeDetectionCompiler} from 'angular2/src/compiler/change_detector_compiler'; | import {ChangeDetectionCompiler} from 'angular2/src/compiler/change_detector_compiler'; | ||||||
| 
 | 
 | ||||||
| import { | import { | ||||||
|   NormalizedDirectiveMetadata, |   CompileDirectiveMetadata, | ||||||
|   TypeMetadata, |   CompileTypeMetadata | ||||||
|   ChangeDetectionMetadata |  | ||||||
| } from 'angular2/src/compiler/directive_metadata'; | } from 'angular2/src/compiler/directive_metadata'; | ||||||
| import {SourceModule, SourceExpression, moduleRef} from 'angular2/src/compiler/source_module'; | import { | ||||||
|  |   SourceModule, | ||||||
|  |   SourceExpression, | ||||||
|  |   SourceExpressions, | ||||||
|  |   moduleRef | ||||||
|  | } from 'angular2/src/compiler/source_module'; | ||||||
| 
 | 
 | ||||||
| import {TemplateParser} from 'angular2/src/compiler/template_parser'; | import {TemplateParser} from 'angular2/src/compiler/template_parser'; | ||||||
| 
 | 
 | ||||||
| @ -63,8 +67,8 @@ export function main() { | |||||||
| 
 | 
 | ||||||
|     describe('compileComponentRuntime', () => { |     describe('compileComponentRuntime', () => { | ||||||
|       function detectChanges(compiler: ChangeDetectionCompiler, template: string, |       function detectChanges(compiler: ChangeDetectionCompiler, template: string, | ||||||
|                              directives: NormalizedDirectiveMetadata[] = CONST_EXPR([])): string[] { |                              directives: CompileDirectiveMetadata[] = CONST_EXPR([])): string[] { | ||||||
|         var type = new TypeMetadata({name: 'SomeComp'}); |         var type = new CompileTypeMetadata({name: 'SomeComp'}); | ||||||
|         var parsedTemplate = parser.parse(template, directives, 'TestComp'); |         var parsedTemplate = parser.parse(template, directives, 'TestComp'); | ||||||
|         var factories = |         var factories = | ||||||
|             compiler.compileComponentRuntime(type, ChangeDetectionStrategy.Default, parsedTemplate); |             compiler.compileComponentRuntime(type, ChangeDetectionStrategy.Default, parsedTemplate); | ||||||
| @ -99,13 +103,13 @@ export function main() { | |||||||
| 
 | 
 | ||||||
|     describe('compileComponentCodeGen', () => { |     describe('compileComponentCodeGen', () => { | ||||||
|       function detectChanges(compiler: ChangeDetectionCompiler, template: string, |       function detectChanges(compiler: ChangeDetectionCompiler, template: string, | ||||||
|                              directives: NormalizedDirectiveMetadata[] = CONST_EXPR([])): |                              directives: CompileDirectiveMetadata[] = CONST_EXPR([])): | ||||||
|           Promise<string[]> { |           Promise<string[]> { | ||||||
|         var type = new TypeMetadata({name: 'SomeComp'}); |         var type = new CompileTypeMetadata({name: 'SomeComp'}); | ||||||
|         var parsedTemplate = parser.parse(template, directives, 'TestComp'); |         var parsedTemplate = parser.parse(template, directives, 'TestComp'); | ||||||
|         var sourceExpression = |         var sourceExpressions = | ||||||
|             compiler.compileComponentCodeGen(type, ChangeDetectionStrategy.Default, parsedTemplate); |             compiler.compileComponentCodeGen(type, ChangeDetectionStrategy.Default, parsedTemplate); | ||||||
|         var testableModule = createTestableModule(sourceExpression, 0).getSourceWithImports(); |         var testableModule = createTestableModule(sourceExpressions, 0).getSourceWithImports(); | ||||||
|         return evalModule(testableModule.source, testableModule.imports, null); |         return evalModule(testableModule.source, testableModule.imports, null); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
| @ -122,9 +126,10 @@ export function main() { | |||||||
|   }); |   }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function createTestableModule(source: SourceExpression, changeDetectorIndex: number): SourceModule { | function createTestableModule(source: SourceExpressions, changeDetectorIndex: number): | ||||||
|  |     SourceModule { | ||||||
|   var resultExpression = |   var resultExpression = | ||||||
|       `${THIS_MODULE_REF}testChangeDetector((${source.expression})[${changeDetectorIndex}])`; |       `${THIS_MODULE_REF}testChangeDetector(([${source.expressions.join(',')}])[${changeDetectorIndex}])`; | ||||||
|   var testableSource = `${source.declarations.join('\n')} |   var testableSource = `${source.declarations.join('\n')} | ||||||
|   ${codeGenExportVariable('run')}${codeGenValueFn(['_'], resultExpression)};`;
 |   ${codeGenExportVariable('run')}${codeGenValueFn(['_'], resultExpression)};`;
 | ||||||
|   return new SourceModule(null, testableSource); |   return new SourceModule(null, testableSource); | ||||||
|  | |||||||
| @ -29,9 +29,9 @@ import { | |||||||
| } from 'angular2/src/core/compiler/template_commands'; | } from 'angular2/src/core/compiler/template_commands'; | ||||||
| import {CommandCompiler} from 'angular2/src/compiler/command_compiler'; | import {CommandCompiler} from 'angular2/src/compiler/command_compiler'; | ||||||
| import { | import { | ||||||
|   NormalizedDirectiveMetadata, |   CompileDirectiveMetadata, | ||||||
|   TypeMetadata, |   CompileTypeMetadata, | ||||||
|   NormalizedTemplateMetadata |   CompileTemplateMetadata | ||||||
| } from 'angular2/src/compiler/directive_metadata'; | } from 'angular2/src/compiler/directive_metadata'; | ||||||
| import {SourceModule, SourceExpression, moduleRef} from 'angular2/src/compiler/source_module'; | import {SourceModule, SourceExpression, moduleRef} from 'angular2/src/compiler/source_module'; | ||||||
| import {ViewEncapsulation} from 'angular2/src/core/render/api'; | import {ViewEncapsulation} from 'angular2/src/core/render/api'; | ||||||
| @ -61,12 +61,12 @@ export class RootComp {} | |||||||
| export class SomeDir {} | export class SomeDir {} | ||||||
| export class AComp {} | export class AComp {} | ||||||
| 
 | 
 | ||||||
| var RootCompTypeMeta = | var RootCompTypeMeta = new CompileTypeMetadata( | ||||||
|     new TypeMetadata({id: 1, name: 'RootComp', runtime: RootComp, moduleId: THIS_MODULE_NAME}); |     {id: 1, name: 'RootComp', runtime: RootComp, moduleId: THIS_MODULE_NAME}); | ||||||
| var SomeDirTypeMeta = | var SomeDirTypeMeta = | ||||||
|     new TypeMetadata({id: 2, name: 'SomeDir', runtime: SomeDir, moduleId: THIS_MODULE_NAME}); |     new CompileTypeMetadata({id: 2, name: 'SomeDir', runtime: SomeDir, moduleId: THIS_MODULE_NAME}); | ||||||
| var ACompTypeMeta = | var ACompTypeMeta = | ||||||
|     new TypeMetadata({id: 3, name: 'AComp', runtime: AComp, moduleId: THIS_MODULE_NAME}); |     new CompileTypeMetadata({id: 3, name: 'AComp', runtime: AComp, moduleId: THIS_MODULE_NAME}); | ||||||
| 
 | 
 | ||||||
| var NESTED_COMPONENT = new CompiledTemplate(45, () => []); | var NESTED_COMPONENT = new CompiledTemplate(45, () => []); | ||||||
| 
 | 
 | ||||||
| @ -84,12 +84,12 @@ export function main() { | |||||||
|     })); |     })); | ||||||
| 
 | 
 | ||||||
|     function createComp({type, selector, template, encapsulation, ngContentSelectors}: { |     function createComp({type, selector, template, encapsulation, ngContentSelectors}: { | ||||||
|       type?: TypeMetadata, |       type?: CompileTypeMetadata, | ||||||
|       selector?: string, |       selector?: string, | ||||||
|       template?: string, |       template?: string, | ||||||
|       encapsulation?: ViewEncapsulation, |       encapsulation?: ViewEncapsulation, | ||||||
|       ngContentSelectors?: string[] |       ngContentSelectors?: string[] | ||||||
|     }): NormalizedDirectiveMetadata { |     }): CompileDirectiveMetadata { | ||||||
|       if (isBlank(encapsulation)) { |       if (isBlank(encapsulation)) { | ||||||
|         encapsulation = ViewEncapsulation.None; |         encapsulation = ViewEncapsulation.None; | ||||||
|       } |       } | ||||||
| @ -102,11 +102,11 @@ export function main() { | |||||||
|       if (isBlank(template)) { |       if (isBlank(template)) { | ||||||
|         template = ''; |         template = ''; | ||||||
|       } |       } | ||||||
|       return new NormalizedDirectiveMetadata({ |       return CompileDirectiveMetadata.create({ | ||||||
|         selector: selector, |         selector: selector, | ||||||
|         isComponent: true, |         isComponent: true, | ||||||
|         type: type, |         type: type, | ||||||
|         template: new NormalizedTemplateMetadata({ |         template: new CompileTemplateMetadata({ | ||||||
|           template: template, |           template: template, | ||||||
|           ngContentSelectors: ngContentSelectors, |           ngContentSelectors: ngContentSelectors, | ||||||
|           encapsulation: encapsulation |           encapsulation: encapsulation | ||||||
| @ -114,8 +114,10 @@ export function main() { | |||||||
|       }); |       }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function createDirective(type: TypeMetadata, selector: string): NormalizedDirectiveMetadata { |     function createDirective(type: CompileTypeMetadata, selector: string, exportAs: string = null): | ||||||
|       return new NormalizedDirectiveMetadata({selector: selector, isComponent: false, type: type}); |         CompileDirectiveMetadata { | ||||||
|  |       return CompileDirectiveMetadata.create( | ||||||
|  |           {selector: selector, exportAs: exportAs, isComponent: false, type: type}); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -159,18 +161,47 @@ export function main() { | |||||||
|         it('should create bound element commands', inject([AsyncTestCompleter], (async) => { |         it('should create bound element commands', inject([AsyncTestCompleter], (async) => { | ||||||
|              var rootComp = createComp({ |              var rootComp = createComp({ | ||||||
|                type: RootCompTypeMeta, |                type: RootCompTypeMeta, | ||||||
|                template: '<div a="b" #some-var="someValue" (click)="someHandler">' |                template: '<div a="b" #some-var (click)="someHandler" (window:scroll)="scrollTo()">' | ||||||
|              }); |              }); | ||||||
|              var dir = createDirective(SomeDirTypeMeta, '[a]'); |              run(rootComp, []) | ||||||
|              run(rootComp, [dir]) |  | ||||||
|                  .then((data) => { |                  .then((data) => { | ||||||
|                    expect(data).toEqual([ |                    expect(data).toEqual([ | ||||||
|                      [ |                      [ | ||||||
|                        BEGIN_ELEMENT, |                        BEGIN_ELEMENT, | ||||||
|                        'div', |                        'div', | ||||||
|                        ['a', 'b'], |                        ['a', 'b'], | ||||||
|                        ['click'], |                        [null, 'click', 'window', 'scroll'], | ||||||
|                        ['someVar', 'someValue'], |                        ['someVar', '%implicit'], | ||||||
|  |                        [], | ||||||
|  |                        true, | ||||||
|  |                        null | ||||||
|  |                      ], | ||||||
|  |                      [END_ELEMENT] | ||||||
|  |                    ]); | ||||||
|  |                    async.done(); | ||||||
|  |                  }); | ||||||
|  |            })); | ||||||
|  | 
 | ||||||
|  |         it('should create element commands with directives', | ||||||
|  |            inject([AsyncTestCompleter], (async) => { | ||||||
|  |              var rootComp = | ||||||
|  |                  createComp({type: RootCompTypeMeta, template: '<div a #some-var="someExport">'}); | ||||||
|  |              var dir = CompileDirectiveMetadata.create({ | ||||||
|  |                selector: '[a]', | ||||||
|  |                exportAs: 'someExport', | ||||||
|  |                isComponent: false, | ||||||
|  |                type: SomeDirTypeMeta, | ||||||
|  |                host: {'(click)': 'doIt()', '(window:scroll)': 'doIt()', 'role': 'button'} | ||||||
|  |              }); | ||||||
|  |              run(rootComp, [dir]) | ||||||
|  |                  .then((data) => { | ||||||
|  |                    expect(data).toEqual([ | ||||||
|  |                      [ | ||||||
|  |                        BEGIN_ELEMENT, | ||||||
|  |                        'div', | ||||||
|  |                        ['a', '', 'role', 'button'], | ||||||
|  |                        [null, 'click', 'window', 'scroll'], | ||||||
|  |                        ['someVar', 0], | ||||||
|                        ['SomeDirType'], |                        ['SomeDirType'], | ||||||
|                        true, |                        true, | ||||||
|                        null |                        null | ||||||
| @ -214,10 +245,8 @@ export function main() { | |||||||
|       describe('components', () => { |       describe('components', () => { | ||||||
| 
 | 
 | ||||||
|         it('should create component commands', inject([AsyncTestCompleter], (async) => { |         it('should create component commands', inject([AsyncTestCompleter], (async) => { | ||||||
|              var rootComp = createComp({ |              var rootComp = createComp( | ||||||
|                type: RootCompTypeMeta, |                  {type: RootCompTypeMeta, template: '<a a="b" #some-var (click)="someHandler">'}); | ||||||
|                template: '<a a="b" #some-var="someValue" (click)="someHandler">' |  | ||||||
|              }); |  | ||||||
|              var comp = createComp({type: ACompTypeMeta, selector: 'a'}); |              var comp = createComp({type: ACompTypeMeta, selector: 'a'}); | ||||||
|              run(rootComp, [comp]) |              run(rootComp, [comp]) | ||||||
|                  .then((data) => { |                  .then((data) => { | ||||||
| @ -226,8 +255,8 @@ export function main() { | |||||||
|                        BEGIN_COMPONENT, |                        BEGIN_COMPONENT, | ||||||
|                        'a', |                        'a', | ||||||
|                        ['a', 'b'], |                        ['a', 'b'], | ||||||
|                        ['click'], |                        [null, 'click'], | ||||||
|                        ['someVar', 'someValue'], |                        ['someVar', 0], | ||||||
|                        ['ACompType'], |                        ['ACompType'], | ||||||
|                        false, |                        false, | ||||||
|                        null, |                        null, | ||||||
| @ -305,7 +334,7 @@ export function main() { | |||||||
|                template: '<template a="b" #some-var="someValue"></template>' |                template: '<template a="b" #some-var="someValue"></template>' | ||||||
|              }); |              }); | ||||||
|              var dir = createDirective(SomeDirTypeMeta, '[a]'); |              var dir = createDirective(SomeDirTypeMeta, '[a]'); | ||||||
|              run(rootComp, [dir]) |              run(rootComp, [dir], 1) | ||||||
|                  .then((data) => { |                  .then((data) => { | ||||||
|                    expect(data).toEqual([ |                    expect(data).toEqual([ | ||||||
|                      [ |                      [ | ||||||
| @ -315,6 +344,7 @@ export function main() { | |||||||
|                        ['SomeDirType'], |                        ['SomeDirType'], | ||||||
|                        false, |                        false, | ||||||
|                        null, |                        null, | ||||||
|  |                        'cd1', | ||||||
|                        [] |                        [] | ||||||
|                      ] |                      ] | ||||||
|                    ]); |                    ]); | ||||||
| @ -325,10 +355,20 @@ export function main() { | |||||||
|         it('should created nested nodes', inject([AsyncTestCompleter], (async) => { |         it('should created nested nodes', inject([AsyncTestCompleter], (async) => { | ||||||
|              var rootComp = |              var rootComp = | ||||||
|                  createComp({type: RootCompTypeMeta, template: '<template>t</template>'}); |                  createComp({type: RootCompTypeMeta, template: '<template>t</template>'}); | ||||||
|              run(rootComp, []) |              run(rootComp, [], 1) | ||||||
|                  .then((data) => { |                  .then((data) => { | ||||||
|                    expect(data).toEqual( |                    expect(data).toEqual([ | ||||||
|                        [[EMBEDDED_TEMPLATE, [], [], [], false, null, [[TEXT, 't', false, null]]]]); |                      [ | ||||||
|  |                        EMBEDDED_TEMPLATE, | ||||||
|  |                        [], | ||||||
|  |                        [], | ||||||
|  |                        [], | ||||||
|  |                        false, | ||||||
|  |                        null, | ||||||
|  |                        'cd1', | ||||||
|  |                        [[TEXT, 't', false, null]] | ||||||
|  |                      ] | ||||||
|  |                    ]); | ||||||
|                    async.done(); |                    async.done(); | ||||||
|                  }); |                  }); | ||||||
|            })); |            })); | ||||||
| @ -339,10 +379,10 @@ export function main() { | |||||||
|                type: RootCompTypeMeta, |                type: RootCompTypeMeta, | ||||||
|                template: '<template><ng-content></ng-content></template>' |                template: '<template><ng-content></ng-content></template>' | ||||||
|              }); |              }); | ||||||
|              run(rootComp, []) |              run(rootComp, [], 1) | ||||||
|                  .then((data) => { |                  .then((data) => { | ||||||
|                    expect(data).toEqual( |                    expect(data).toEqual( | ||||||
|                        [[EMBEDDED_TEMPLATE, [], [], [], true, null, [[NG_CONTENT, null]]]]); |                        [[EMBEDDED_TEMPLATE, [], [], [], true, null, 'cd1', [[NG_CONTENT, null]]]]); | ||||||
|                    async.done(); |                    async.done(); | ||||||
|                  }); |                  }); | ||||||
|            })); |            })); | ||||||
| @ -364,17 +404,21 @@ export function main() { | |||||||
| 
 | 
 | ||||||
|     describe('compileComponentRuntime', () => { |     describe('compileComponentRuntime', () => { | ||||||
|       beforeEach(() => { |       beforeEach(() => { | ||||||
|         componentTemplateFactory = (directive: NormalizedDirectiveMetadata) => { |         componentTemplateFactory = (directive: CompileDirectiveMetadata) => { | ||||||
|           return new CompiledTemplate(directive.type.id, () => []); |           return new CompiledTemplate(directive.type.id, () => []); | ||||||
|         }; |         }; | ||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
|       function run(component: NormalizedDirectiveMetadata, |       function run(component: CompileDirectiveMetadata, directives: CompileDirectiveMetadata[], | ||||||
|                    directives: NormalizedDirectiveMetadata[]): Promise<any[][]> { |                    embeddedTemplateCount: number = 0): Promise<any[][]> { | ||||||
|  |         var changeDetectorFactories = []; | ||||||
|  |         for (var i = 0; i < embeddedTemplateCount + 1; i++) { | ||||||
|  |           (function(i) { changeDetectorFactories.push((_) => `cd${i}`); })(i); | ||||||
|  |         } | ||||||
|         var parsedTemplate = |         var parsedTemplate = | ||||||
|             parser.parse(component.template.template, directives, component.type.name); |             parser.parse(component.template.template, directives, component.type.name); | ||||||
|         var commands = commandCompiler.compileComponentRuntime(component, parsedTemplate, |         var commands = commandCompiler.compileComponentRuntime( | ||||||
|                                                                componentTemplateFactory); |             component, parsedTemplate, changeDetectorFactories, componentTemplateFactory); | ||||||
|         return PromiseWrapper.resolve(humanize(commands)); |         return PromiseWrapper.resolve(humanize(commands)); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
| @ -384,17 +428,21 @@ export function main() { | |||||||
| 
 | 
 | ||||||
|     describe('compileComponentCodeGen', () => { |     describe('compileComponentCodeGen', () => { | ||||||
|       beforeEach(() => { |       beforeEach(() => { | ||||||
|         componentTemplateFactory = (directive: NormalizedDirectiveMetadata) => { |         componentTemplateFactory = (directive: CompileDirectiveMetadata) => { | ||||||
|           return `new ${TEMPLATE_COMMANDS_MODULE_REF}CompiledTemplate(${directive.type.id}, ${codeGenValueFn([], '{}')})`; |           return `new ${TEMPLATE_COMMANDS_MODULE_REF}CompiledTemplate(${directive.type.id}, ${codeGenValueFn([], '{}')})`; | ||||||
|         }; |         }; | ||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
|       function run(component: NormalizedDirectiveMetadata, |       function run(component: CompileDirectiveMetadata, directives: CompileDirectiveMetadata[], | ||||||
|                    directives: NormalizedDirectiveMetadata[]): Promise<any[][]> { |                    embeddedTemplateCount: number = 0): Promise<any[][]> { | ||||||
|  |         var changeDetectorFactoryExpressions = []; | ||||||
|  |         for (var i = 0; i < embeddedTemplateCount + 1; i++) { | ||||||
|  |           changeDetectorFactoryExpressions.push(codeGenValueFn(['_'], `'cd${i}'`)); | ||||||
|  |         } | ||||||
|         var parsedTemplate = |         var parsedTemplate = | ||||||
|             parser.parse(component.template.template, directives, component.type.name); |             parser.parse(component.template.template, directives, component.type.name); | ||||||
|         var sourceModule = commandCompiler.compileComponentCodeGen(component, parsedTemplate, |         var sourceModule = commandCompiler.compileComponentCodeGen( | ||||||
|                                                                    componentTemplateFactory); |             component, parsedTemplate, changeDetectorFactoryExpressions, componentTemplateFactory); | ||||||
|         var testableModule = createTestableModule(sourceModule).getSourceWithImports(); |         var testableModule = createTestableModule(sourceModule).getSourceWithImports(); | ||||||
|         return evalModule(testableModule.source, testableModule.imports, null); |         return evalModule(testableModule.source, testableModule.imports, null); | ||||||
|       } |       } | ||||||
| @ -432,7 +480,7 @@ class CommandHumanizer implements CommandVisitor { | |||||||
|       BEGIN_ELEMENT, |       BEGIN_ELEMENT, | ||||||
|       cmd.name, |       cmd.name, | ||||||
|       cmd.attrNameAndValues, |       cmd.attrNameAndValues, | ||||||
|       cmd.eventNames, |       cmd.eventTargetAndNames, | ||||||
|       cmd.variableNameAndValues, |       cmd.variableNameAndValues, | ||||||
|       cmd.directives.map(checkAndStringifyType), |       cmd.directives.map(checkAndStringifyType), | ||||||
|       cmd.isBound, |       cmd.isBound, | ||||||
| @ -449,12 +497,11 @@ class CommandHumanizer implements CommandVisitor { | |||||||
|       BEGIN_COMPONENT, |       BEGIN_COMPONENT, | ||||||
|       cmd.name, |       cmd.name, | ||||||
|       cmd.attrNameAndValues, |       cmd.attrNameAndValues, | ||||||
|       cmd.eventNames, |       cmd.eventTargetAndNames, | ||||||
|       cmd.variableNameAndValues, |       cmd.variableNameAndValues, | ||||||
|       cmd.directives.map(checkAndStringifyType), |       cmd.directives.map(checkAndStringifyType), | ||||||
|       cmd.nativeShadow, |       cmd.nativeShadow, | ||||||
|       cmd.ngContentIndex, |       cmd.ngContentIndex, | ||||||
|       // TODO humanizeTemplate(cmd.template)
 |  | ||||||
|       cmd.template.id |       cmd.template.id | ||||||
|     ]); |     ]); | ||||||
|     return null; |     return null; | ||||||
| @ -471,6 +518,7 @@ class CommandHumanizer implements CommandVisitor { | |||||||
|       cmd.directives.map(checkAndStringifyType), |       cmd.directives.map(checkAndStringifyType), | ||||||
|       cmd.isMerged, |       cmd.isMerged, | ||||||
|       cmd.ngContentIndex, |       cmd.ngContentIndex, | ||||||
|  |       cmd.changeDetectorFactory(null), | ||||||
|       humanize(cmd.children) |       humanize(cmd.children) | ||||||
|     ]); |     ]); | ||||||
|     return null; |     return null; | ||||||
|  | |||||||
| @ -13,98 +13,76 @@ import { | |||||||
| } from 'angular2/test_lib'; | } from 'angular2/test_lib'; | ||||||
| 
 | 
 | ||||||
| import { | import { | ||||||
|   NormalizedDirectiveMetadata, |   CompileDirectiveMetadata, | ||||||
|   TypeMetadata, |   CompileTypeMetadata, | ||||||
|   NormalizedTemplateMetadata, |   CompileTemplateMetadata | ||||||
|   ChangeDetectionMetadata |  | ||||||
| } from 'angular2/src/compiler/directive_metadata'; | } from 'angular2/src/compiler/directive_metadata'; | ||||||
| import {ViewEncapsulation} from 'angular2/src/core/render/api'; | import {ViewEncapsulation} from 'angular2/src/core/render/api'; | ||||||
| import {ChangeDetectionStrategy} from 'angular2/src/core/change_detection'; | import {ChangeDetectionStrategy} from 'angular2/src/core/change_detection'; | ||||||
|  | import {LifecycleHooks} from 'angular2/src/core/compiler/interfaces'; | ||||||
| 
 | 
 | ||||||
| export function main() { | export function main() { | ||||||
|   describe('DirectiveMetadata', () => { |   describe('DirectiveMetadata', () => { | ||||||
|     var fullTypeMeta: TypeMetadata; |     var fullTypeMeta: CompileTypeMetadata; | ||||||
|     var fullTemplateMeta: NormalizedTemplateMetadata; |     var fullTemplateMeta: CompileTemplateMetadata; | ||||||
|     var fullChangeDetectionMeta: ChangeDetectionMetadata; |     var fullDirectiveMeta: CompileDirectiveMetadata; | ||||||
|     var fullDirectiveMeta: NormalizedDirectiveMetadata; |  | ||||||
| 
 | 
 | ||||||
|     beforeEach(() => { |     beforeEach(() => { | ||||||
|       fullTypeMeta = new TypeMetadata({id: 23, name: 'SomeType', moduleId: 'someUrl'}); |       fullTypeMeta = new CompileTypeMetadata({id: 23, name: 'SomeType', moduleId: 'someUrl'}); | ||||||
|       fullTemplateMeta = new NormalizedTemplateMetadata({ |       fullTemplateMeta = new CompileTemplateMetadata({ | ||||||
|         encapsulation: ViewEncapsulation.Emulated, |         encapsulation: ViewEncapsulation.Emulated, | ||||||
|         template: '<a></a>', |         template: '<a></a>', | ||||||
|  |         templateUrl: 'someTemplateUrl', | ||||||
|         styles: ['someStyle'], |         styles: ['someStyle'], | ||||||
|         styleAbsUrls: ['someStyleUrl'], |         styleUrls: ['someStyleUrl'], | ||||||
|         hostAttributes: {'attr1': 'attrValue2'}, |  | ||||||
|         ngContentSelectors: ['*'] |         ngContentSelectors: ['*'] | ||||||
|       }); |       }); | ||||||
|       fullChangeDetectionMeta = new ChangeDetectionMetadata({ |       fullDirectiveMeta = CompileDirectiveMetadata.create({ | ||||||
|         changeDetection: ChangeDetectionStrategy.Default, |  | ||||||
|         properties: ['someProp'], |  | ||||||
|         events: ['someEvent'], |  | ||||||
|         hostListeners: {'event1': 'handler1'}, |  | ||||||
|         hostProperties: {'prop1': 'expr1'}, |  | ||||||
|         callAfterContentInit: true, |  | ||||||
|         callAfterContentChecked: true, |  | ||||||
|         callAfterViewInit: true, |  | ||||||
|         callAfterViewChecked: true, |  | ||||||
|         callOnChanges: true, |  | ||||||
|         callDoCheck: true, |  | ||||||
|         callOnInit: true |  | ||||||
|       }); |  | ||||||
|       fullDirectiveMeta = new NormalizedDirectiveMetadata({ |  | ||||||
|         selector: 'someSelector', |         selector: 'someSelector', | ||||||
|         isComponent: true, |         isComponent: true, | ||||||
|         dynamicLoadable: true, |         dynamicLoadable: true, | ||||||
|         type: fullTypeMeta, template: fullTemplateMeta, |         type: fullTypeMeta, template: fullTemplateMeta, | ||||||
|         changeDetection: fullChangeDetectionMeta, |         changeDetection: ChangeDetectionStrategy.Default, | ||||||
|  |         properties: ['someProp'], | ||||||
|  |         events: ['someEvent'], | ||||||
|  |         host: {'(event1)': 'handler1', '[prop1]': 'expr1', 'attr1': 'attrValue2'}, | ||||||
|  |         lifecycleHooks: [LifecycleHooks.OnChanges] | ||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     describe('DirectiveMetadata', () => { |     describe('DirectiveMetadata', () => { | ||||||
|       it('should serialize with full data', () => { |       it('should serialize with full data', () => { | ||||||
|         expect(NormalizedDirectiveMetadata.fromJson(fullDirectiveMeta.toJson())) |         expect(CompileDirectiveMetadata.fromJson(fullDirectiveMeta.toJson())) | ||||||
|             .toEqual(fullDirectiveMeta); |             .toEqual(fullDirectiveMeta); | ||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
|       it('should serialize with no data', () => { |       it('should serialize with no data', () => { | ||||||
|         var empty = new NormalizedDirectiveMetadata(); |         var empty = CompileDirectiveMetadata.create(); | ||||||
|         expect(NormalizedDirectiveMetadata.fromJson(empty.toJson())).toEqual(empty); |         expect(CompileDirectiveMetadata.fromJson(empty.toJson())).toEqual(empty); | ||||||
|       }); |       }); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     describe('TypeMetadata', () => { |     describe('TypeMetadata', () => { | ||||||
|       it('should serialize with full data', |       it('should serialize with full data', () => { | ||||||
|          () => { expect(TypeMetadata.fromJson(fullTypeMeta.toJson())).toEqual(fullTypeMeta); }); |         expect(CompileTypeMetadata.fromJson(fullTypeMeta.toJson())).toEqual(fullTypeMeta); | ||||||
|  |       }); | ||||||
| 
 | 
 | ||||||
|       it('should serialize with no data', () => { |       it('should serialize with no data', () => { | ||||||
|         var empty = new TypeMetadata(); |         var empty = new CompileTypeMetadata(); | ||||||
|         expect(TypeMetadata.fromJson(empty.toJson())).toEqual(empty); |         expect(CompileTypeMetadata.fromJson(empty.toJson())).toEqual(empty); | ||||||
|       }); |       }); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     describe('TemplateMetadata', () => { |     describe('TemplateMetadata', () => { | ||||||
|       it('should serialize with full data', () => { |       it('should serialize with full data', () => { | ||||||
|         expect(NormalizedTemplateMetadata.fromJson(fullTemplateMeta.toJson())) |         expect(CompileTemplateMetadata.fromJson(fullTemplateMeta.toJson())) | ||||||
|             .toEqual(fullTemplateMeta); |             .toEqual(fullTemplateMeta); | ||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
|       it('should serialize with no data', () => { |       it('should serialize with no data', () => { | ||||||
|         var empty = new NormalizedTemplateMetadata(); |         var empty = new CompileTemplateMetadata(); | ||||||
|         expect(NormalizedTemplateMetadata.fromJson(empty.toJson())).toEqual(empty); |         expect(CompileTemplateMetadata.fromJson(empty.toJson())).toEqual(empty); | ||||||
|       }); |  | ||||||
|     }); |  | ||||||
| 
 |  | ||||||
|     describe('ChangeDetectionMetadata', () => { |  | ||||||
|       it('should serialize with full data', () => { |  | ||||||
|         expect(ChangeDetectionMetadata.fromJson(fullChangeDetectionMeta.toJson())) |  | ||||||
|             .toEqual(fullChangeDetectionMeta); |  | ||||||
|       }); |  | ||||||
| 
 |  | ||||||
|       it('should serialize with no data', () => { |  | ||||||
|         var empty = new ChangeDetectionMetadata(); |  | ||||||
|         expect(ChangeDetectionMetadata.fromJson(empty.toJson())).toEqual(empty); |  | ||||||
|       }); |       }); | ||||||
|     }); |     }); | ||||||
|   }); |   }); | ||||||
|  | |||||||
| @ -80,22 +80,6 @@ export function main() { | |||||||
|               ]); |               ]); | ||||||
|         }); |         }); | ||||||
|       }); |       }); | ||||||
| 
 |  | ||||||
|       describe('ng-non-bindable', () => { |  | ||||||
|         it('should ignore text nodes and elements inside of elements with ng-non-bindable', () => { |  | ||||||
|           expect(humanizeDom( |  | ||||||
|                      parser.parse('<div ng-non-bindable>hello<span></span></div>', 'TestComp'))) |  | ||||||
|               .toEqual([ |  | ||||||
|                 [HtmlElementAst, 'div', 'TestComp > div:nth-child(0)'], |  | ||||||
|                 [ |  | ||||||
|                   HtmlAttrAst, |  | ||||||
|                   'ng-non-bindable', |  | ||||||
|                   '', |  | ||||||
|                   'TestComp > div:nth-child(0)[ng-non-bindable=]' |  | ||||||
|                 ] |  | ||||||
|               ]); |  | ||||||
|         }); |  | ||||||
|       }); |  | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     describe('unparse', () => { |     describe('unparse', () => { | ||||||
|  | |||||||
| @ -15,6 +15,7 @@ import { | |||||||
| 
 | 
 | ||||||
| import {stringify} from 'angular2/src/core/facade/lang'; | import {stringify} from 'angular2/src/core/facade/lang'; | ||||||
| import {RuntimeMetadataResolver} from 'angular2/src/compiler/runtime_metadata'; | import {RuntimeMetadataResolver} from 'angular2/src/compiler/runtime_metadata'; | ||||||
|  | import {LifecycleHooks, LIFECYCLE_HOOKS_VALUES} from 'angular2/src/core/compiler/interfaces'; | ||||||
| import { | import { | ||||||
|   Component, |   Component, | ||||||
|   View, |   View, | ||||||
| @ -43,27 +44,20 @@ export function main() { | |||||||
|          inject([RuntimeMetadataResolver], (resolver: RuntimeMetadataResolver) => { |          inject([RuntimeMetadataResolver], (resolver: RuntimeMetadataResolver) => { | ||||||
|            var meta = resolver.getMetadata(ComponentWithEverything); |            var meta = resolver.getMetadata(ComponentWithEverything); | ||||||
|            expect(meta.selector).toEqual('someSelector'); |            expect(meta.selector).toEqual('someSelector'); | ||||||
|  |            expect(meta.exportAs).toEqual('someExportAs'); | ||||||
|            expect(meta.isComponent).toBe(true); |            expect(meta.isComponent).toBe(true); | ||||||
|            expect(meta.dynamicLoadable).toBe(true); |            expect(meta.dynamicLoadable).toBe(true); | ||||||
|            expect(meta.type.runtime).toBe(ComponentWithEverything); |            expect(meta.type.runtime).toBe(ComponentWithEverything); | ||||||
|            expect(meta.type.name).toEqual(stringify(ComponentWithEverything)); |            expect(meta.type.name).toEqual(stringify(ComponentWithEverything)); | ||||||
|            expect(meta.type.moduleId).toEqual('someModuleId'); |            expect(meta.type.moduleId).toEqual('someModuleId'); | ||||||
|            expect(meta.changeDetection.callAfterContentChecked).toBe(true); |            expect(meta.lifecycleHooks).toEqual(LIFECYCLE_HOOKS_VALUES); | ||||||
|            expect(meta.changeDetection.callAfterContentInit).toBe(true); |            expect(meta.changeDetection).toBe(ChangeDetectionStrategy.CheckAlways); | ||||||
|            expect(meta.changeDetection.callAfterViewChecked).toBe(true); |            expect(meta.properties).toEqual({'someProp': 'someProp'}); | ||||||
|            expect(meta.changeDetection.callAfterViewInit).toBe(true); |            expect(meta.events).toEqual({'someEvent': 'someEvent'}); | ||||||
|            expect(meta.changeDetection.callDoCheck).toBe(true); |            expect(meta.hostListeners).toEqual({'someHostListener': 'someHostListenerExpr'}); | ||||||
|            expect(meta.changeDetection.callOnChanges).toBe(true); |            expect(meta.hostProperties).toEqual({'someHostProp': 'someHostPropExpr'}); | ||||||
|            expect(meta.changeDetection.callOnInit).toBe(true); |            expect(meta.hostAttributes).toEqual({'someHostAttr': 'someHostAttrValue'}); | ||||||
|            expect(meta.changeDetection.changeDetection).toBe(ChangeDetectionStrategy.CheckAlways); |  | ||||||
|            expect(meta.changeDetection.properties).toEqual(['someProp']); |  | ||||||
|            expect(meta.changeDetection.events).toEqual(['someEvent']); |  | ||||||
|            expect(meta.changeDetection.hostListeners) |  | ||||||
|                .toEqual({'someHostListener': 'someHostListenerExpr'}); |  | ||||||
|            expect(meta.changeDetection.hostProperties) |  | ||||||
|                .toEqual({'someHostProp': 'someHostPropExpr'}); |  | ||||||
|            expect(meta.template.encapsulation).toBe(ViewEncapsulation.Emulated); |            expect(meta.template.encapsulation).toBe(ViewEncapsulation.Emulated); | ||||||
|            expect(meta.template.hostAttributes).toEqual({'someHostAttr': 'someHostAttrValue'}); |  | ||||||
|            expect(meta.template.styles).toEqual(['someStyle']); |            expect(meta.template.styles).toEqual(['someStyle']); | ||||||
|            expect(meta.template.styleUrls).toEqual(['someStyleUrl']); |            expect(meta.template.styleUrls).toEqual(['someStyleUrl']); | ||||||
|            expect(meta.template.template).toEqual('someTemplate'); |            expect(meta.template.template).toEqual('someTemplate'); | ||||||
| @ -105,7 +99,7 @@ class DirectiveWithoutModuleId { | |||||||
|     '(someHostListener)': 'someHostListenerExpr', |     '(someHostListener)': 'someHostListenerExpr', | ||||||
|     'someHostAttr': 'someHostAttrValue' |     'someHostAttr': 'someHostAttrValue' | ||||||
|   }, |   }, | ||||||
|   dynamicLoadable: true, |   exportAs: 'someExportAs', | ||||||
|   moduleId: 'someModuleId', |   moduleId: 'someModuleId', | ||||||
|   changeDetection: ChangeDetectionStrategy.CheckAlways |   changeDetection: ChangeDetectionStrategy.CheckAlways | ||||||
| }) | }) | ||||||
|  | |||||||
| @ -17,14 +17,14 @@ import {SpyXHR} from '../core/spies'; | |||||||
| import {XHR} from 'angular2/src/core/render/xhr'; | import {XHR} from 'angular2/src/core/render/xhr'; | ||||||
| import {BaseException, WrappedException} from 'angular2/src/core/facade/exceptions'; | import {BaseException, WrappedException} from 'angular2/src/core/facade/exceptions'; | ||||||
| 
 | 
 | ||||||
| import {CONST_EXPR, isPresent, StringWrapper} from 'angular2/src/core/facade/lang'; | import {CONST_EXPR, isPresent, isBlank, StringWrapper} from 'angular2/src/core/facade/lang'; | ||||||
| import {PromiseWrapper, Promise} from 'angular2/src/core/facade/async'; | import {PromiseWrapper, Promise} from 'angular2/src/core/facade/async'; | ||||||
| import {evalModule} from './eval_module'; | import {evalModule} from './eval_module'; | ||||||
| import {StyleCompiler} from 'angular2/src/compiler/style_compiler'; | import {StyleCompiler} from 'angular2/src/compiler/style_compiler'; | ||||||
| import { | import { | ||||||
|   NormalizedDirectiveMetadata, |   CompileDirectiveMetadata, | ||||||
|   NormalizedTemplateMetadata, |   CompileTemplateMetadata, | ||||||
|   TypeMetadata |   CompileTypeMetadata | ||||||
| } from 'angular2/src/compiler/directive_metadata'; | } from 'angular2/src/compiler/directive_metadata'; | ||||||
| import {SourceExpression, SourceModule} from 'angular2/src/compiler/source_module'; | import {SourceExpression, SourceModule} from 'angular2/src/compiler/source_module'; | ||||||
| import {ViewEncapsulation} from 'angular2/src/core/render/api'; | import {ViewEncapsulation} from 'angular2/src/core/render/api'; | ||||||
| @ -52,152 +52,249 @@ export function main() { | |||||||
|     beforeEach(inject([StyleCompiler], (_compiler) => { compiler = _compiler; })); |     beforeEach(inject([StyleCompiler], (_compiler) => { compiler = _compiler; })); | ||||||
| 
 | 
 | ||||||
|     function comp(styles: string[], styleAbsUrls: string[], encapsulation: ViewEncapsulation): |     function comp(styles: string[], styleAbsUrls: string[], encapsulation: ViewEncapsulation): | ||||||
|         NormalizedDirectiveMetadata { |         CompileDirectiveMetadata { | ||||||
|       return new NormalizedDirectiveMetadata({ |       return CompileDirectiveMetadata.create({ | ||||||
|         type: new TypeMetadata({id: 23, moduleId: 'someUrl'}), |         type: new CompileTypeMetadata({id: 23, moduleId: 'someUrl'}), | ||||||
|         template: new NormalizedTemplateMetadata( |         template: new CompileTemplateMetadata( | ||||||
|             {styles: styles, styleAbsUrls: styleAbsUrls, encapsulation: encapsulation}) |             {styles: styles, styleUrls: styleAbsUrls, encapsulation: encapsulation}) | ||||||
|       }); |       }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     describe('compileComponentRuntime', () => { |     describe('compileComponentRuntime', () => { | ||||||
|       function runTest(styles: string[], styleAbsUrls: string[], encapsulation: ViewEncapsulation, |       var xhrUrlResults; | ||||||
|                        expectedStyles: string[]) { |       var xhrCount; | ||||||
|         return inject([AsyncTestCompleter], (async) => { | 
 | ||||||
|  |       beforeEach(() => { | ||||||
|  |         xhrCount = 0; | ||||||
|  |         xhrUrlResults = {}; | ||||||
|  |         xhrUrlResults[IMPORT_ABS_MODULE_NAME] = 'span {color: blue}'; | ||||||
|  |         xhrUrlResults[IMPORT_ABS_MODULE_NAME_WITH_IMPORT] = | ||||||
|  |             `a {color: green}@import ${IMPORT_REL_MODULE_NAME};`; | ||||||
|  |       }); | ||||||
|  | 
 | ||||||
|  |       function compile(styles: string[], styleAbsUrls: string[], encapsulation: ViewEncapsulation): | ||||||
|  |           Promise<string[]> { | ||||||
|         // Note: Can't use MockXHR as the xhr is called recursively,
 |         // Note: Can't use MockXHR as the xhr is called recursively,
 | ||||||
|         // so we can't trigger flush.
 |         // so we can't trigger flush.
 | ||||||
|         xhr.spy('get').andCallFake((url) => { |         xhr.spy('get').andCallFake((url) => { | ||||||
|             var response; |           var response = xhrUrlResults[url]; | ||||||
|             if (url == IMPORT_ABS_MODULE_NAME) { |           xhrCount++; | ||||||
|               response = 'span {color: blue}'; |           if (isBlank(response)) { | ||||||
|             } else if (url == IMPORT_ABS_MODULE_NAME_WITH_IMPORT) { |  | ||||||
|               response = `a {color: green}@import ${IMPORT_REL_MODULE_NAME};`; |  | ||||||
|             } else { |  | ||||||
|             throw new BaseException(`Unexpected url ${url}`); |             throw new BaseException(`Unexpected url ${url}`); | ||||||
|           } |           } | ||||||
|           return PromiseWrapper.resolve(response); |           return PromiseWrapper.resolve(response); | ||||||
|         }); |         }); | ||||||
|           compiler.compileComponentRuntime(comp(styles, styleAbsUrls, encapsulation)) |         return compiler.compileComponentRuntime(comp(styles, styleAbsUrls, encapsulation)); | ||||||
|               .then((value) => { |  | ||||||
|                 compareStyles(value, expectedStyles); |  | ||||||
|                 async.done(); |  | ||||||
|               }); |  | ||||||
|         }); |  | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       describe('no shim', () => { |       describe('no shim', () => { | ||||||
|         var encapsulation = ViewEncapsulation.None; |         var encapsulation = ViewEncapsulation.None; | ||||||
| 
 | 
 | ||||||
|         it('should compile plain css rules', |         it('should compile plain css rules', inject([AsyncTestCompleter], (async) => { | ||||||
|            runTest(['div {color: red}', 'span {color: blue}'], [], encapsulation, |              compile(['div {color: red}', 'span {color: blue}'], [], encapsulation) | ||||||
|                    ['div {color: red}', 'span {color: blue}'])); |                  .then(styles => { | ||||||
|  |                    expect(styles).toEqual(['div {color: red}', 'span {color: blue}']); | ||||||
|  |                    async.done(); | ||||||
|  |                  }); | ||||||
|  |            })); | ||||||
| 
 | 
 | ||||||
|         it('should allow to import rules', |         it('should allow to import rules', inject([AsyncTestCompleter], (async) => { | ||||||
|            runTest(['div {color: red}'], [IMPORT_ABS_MODULE_NAME], encapsulation, |              compile(['div {color: red}'], [IMPORT_ABS_MODULE_NAME], encapsulation) | ||||||
|                    ['div {color: red}', 'span {color: blue}'])); |                  .then(styles => { | ||||||
|  |                    expect(styles).toEqual(['div {color: red}', 'span {color: blue}']); | ||||||
|  |                    async.done(); | ||||||
|  |                  }); | ||||||
|  |            })); | ||||||
| 
 | 
 | ||||||
|         it('should allow to import rules transitively', |         it('should allow to import rules transitively', inject([AsyncTestCompleter], (async) => { | ||||||
|            runTest(['div {color: red}'], [IMPORT_ABS_MODULE_NAME_WITH_IMPORT], encapsulation, |              compile(['div {color: red}'], [IMPORT_ABS_MODULE_NAME_WITH_IMPORT], encapsulation) | ||||||
|                    ['div {color: red}', 'a {color: green}', 'span {color: blue}'])); |                  .then(styles => { | ||||||
|  |                    expect(styles) | ||||||
|  |                        .toEqual(['div {color: red}', 'a {color: green}', 'span {color: blue}']); | ||||||
|  |                    async.done(); | ||||||
|  |                  }); | ||||||
|  |            })); | ||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
|       describe('with shim', () => { |       describe('with shim', () => { | ||||||
|         var encapsulation = ViewEncapsulation.Emulated; |         var encapsulation = ViewEncapsulation.Emulated; | ||||||
| 
 | 
 | ||||||
|         it('should compile plain css rules', |         it('should compile plain css rules', inject([AsyncTestCompleter], (async) => { | ||||||
|            runTest( |              compile(['div {\ncolor: red;\n}', 'span {\ncolor: blue;\n}'], [], encapsulation) | ||||||
|                ['div {\ncolor: red;\n}', 'span {\ncolor: blue;\n}'], [], encapsulation, |                  .then(styles => { | ||||||
|                ['div[_ngcontent-23] {\ncolor: red;\n}', 'span[_ngcontent-23] {\ncolor: blue;\n}'])); |                    expect(styles).toEqual([ | ||||||
|  |                      'div[_ngcontent-23] {\ncolor: red;\n}', | ||||||
|  |                      'span[_ngcontent-23] {\ncolor: blue;\n}' | ||||||
|  |                    ]); | ||||||
|  |                    async.done(); | ||||||
|  |                  }); | ||||||
|  |            })); | ||||||
| 
 | 
 | ||||||
|         it('should allow to import rules', |         it('should allow to import rules', inject([AsyncTestCompleter], (async) => { | ||||||
|            runTest( |              compile(['div {\ncolor: red;\n}'], [IMPORT_ABS_MODULE_NAME], encapsulation) | ||||||
|                ['div {\ncolor: red;\n}'], [IMPORT_ABS_MODULE_NAME], encapsulation, |                  .then(styles => { | ||||||
|                ['div[_ngcontent-23] {\ncolor: red;\n}', 'span[_ngcontent-23] {\ncolor: blue;\n}'])); |                    expect(styles).toEqual([ | ||||||
|  |                      'div[_ngcontent-23] {\ncolor: red;\n}', | ||||||
|  |                      'span[_ngcontent-23] {\ncolor: blue;\n}' | ||||||
|  |                    ]); | ||||||
|  |                    async.done(); | ||||||
|  |                  }); | ||||||
|  |            })); | ||||||
| 
 | 
 | ||||||
|         it('should allow to import rules transitively', |         it('should allow to import rules transitively', inject([AsyncTestCompleter], (async) => { | ||||||
|            runTest(['div {\ncolor: red;\n}'], [IMPORT_ABS_MODULE_NAME_WITH_IMPORT], encapsulation, [ |              compile(['div {\ncolor: red;\n}'], [IMPORT_ABS_MODULE_NAME_WITH_IMPORT], encapsulation) | ||||||
|  |                  .then(styles => { | ||||||
|  |                    expect(styles).toEqual([ | ||||||
|                      'div[_ngcontent-23] {\ncolor: red;\n}', |                      'div[_ngcontent-23] {\ncolor: red;\n}', | ||||||
|                      'a[_ngcontent-23] {\ncolor: green;\n}', |                      'a[_ngcontent-23] {\ncolor: green;\n}', | ||||||
|                      'span[_ngcontent-23] {\ncolor: blue;\n}' |                      'span[_ngcontent-23] {\ncolor: blue;\n}' | ||||||
|            ])); |                    ]); | ||||||
|  |                    async.done(); | ||||||
|                  }); |                  }); | ||||||
|  |            })); | ||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
|     describe('compileComponentCodeGen', () => { |       it('should cache stylesheets for parallel requests', inject([AsyncTestCompleter], (async) => { | ||||||
|       function runTest(styles: string[], styleAbsUrls: string[], encapsulation: ViewEncapsulation, |            PromiseWrapper.all([ | ||||||
|                        expectedStyles: string[]) { |                            compile([], [IMPORT_ABS_MODULE_NAME], ViewEncapsulation.None), | ||||||
|         return inject([AsyncTestCompleter], (async) => { |                            compile([], [IMPORT_ABS_MODULE_NAME], ViewEncapsulation.None) | ||||||
|           var sourceExpression = |                          ]) | ||||||
|               compiler.compileComponentCodeGen(comp(styles, styleAbsUrls, encapsulation)); |                .then((styleArrays) => { | ||||||
|           var sourceWithImports = testableExpression(sourceExpression).getSourceWithImports(); |                  expect(styleArrays[0]).toEqual(['span {color: blue}']); | ||||||
|           evalModule(sourceWithImports.source, sourceWithImports.imports, null) |                  expect(styleArrays[1]).toEqual(['span {color: blue}']); | ||||||
|               .then((value) => { |                  expect(xhrCount).toBe(1); | ||||||
|                 compareStyles(value, expectedStyles); |                  async.done(); | ||||||
|  |                }); | ||||||
|  |          })); | ||||||
|  | 
 | ||||||
|  |       it('should cache stylesheets for serial requests', inject([AsyncTestCompleter], (async) => { | ||||||
|  |            compile([], [IMPORT_ABS_MODULE_NAME], ViewEncapsulation.None) | ||||||
|  |                .then((styles0) => { | ||||||
|  |                  xhrUrlResults[IMPORT_ABS_MODULE_NAME] = 'span {color: black}'; | ||||||
|  |                  return compile([], [IMPORT_ABS_MODULE_NAME], ViewEncapsulation.None) | ||||||
|  |                      .then((styles1) => { | ||||||
|  |                        expect(styles0).toEqual(['span {color: blue}']); | ||||||
|  |                        expect(styles1).toEqual(['span {color: blue}']); | ||||||
|  |                        expect(xhrCount).toBe(1); | ||||||
|                        async.done(); |                        async.done(); | ||||||
|                      }); |                      }); | ||||||
|                }); |                }); | ||||||
|       } |          })); | ||||||
|  | 
 | ||||||
|  |       it('should allow to clear the cache', inject([AsyncTestCompleter], (async) => { | ||||||
|  |            compile([], [IMPORT_ABS_MODULE_NAME], ViewEncapsulation.None) | ||||||
|  |                .then((_) => { | ||||||
|  |                  compiler.clearCache(); | ||||||
|  |                  xhrUrlResults[IMPORT_ABS_MODULE_NAME] = 'span {color: black}'; | ||||||
|  |                  return compile([], [IMPORT_ABS_MODULE_NAME], ViewEncapsulation.None); | ||||||
|  |                }) | ||||||
|  |                .then((styles) => { | ||||||
|  |                  expect(xhrCount).toBe(2); | ||||||
|  |                  expect(styles).toEqual(['span {color: black}']); | ||||||
|  |                  async.done(); | ||||||
|  |                }); | ||||||
|  |          })); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     describe('compileComponentCodeGen', () => { | ||||||
|  |       function compile(styles: string[], styleAbsUrls: string[], encapsulation: ViewEncapsulation): | ||||||
|  |           Promise<string[]> { | ||||||
|  |         var sourceExpression = | ||||||
|  |             compiler.compileComponentCodeGen(comp(styles, styleAbsUrls, encapsulation)); | ||||||
|  |         var sourceWithImports = testableExpression(sourceExpression).getSourceWithImports(); | ||||||
|  |         return evalModule(sourceWithImports.source, sourceWithImports.imports, null); | ||||||
|  |       }; | ||||||
| 
 | 
 | ||||||
|       describe('no shim', () => { |       describe('no shim', () => { | ||||||
|         var encapsulation = ViewEncapsulation.None; |         var encapsulation = ViewEncapsulation.None; | ||||||
| 
 | 
 | ||||||
|         it('should compile plain css ruless', |         it('should compile plain css ruless', inject([AsyncTestCompleter], (async) => { | ||||||
|            runTest(['div {color: red}', 'span {color: blue}'], [], encapsulation, |              compile(['div {color: red}', 'span {color: blue}'], [], encapsulation) | ||||||
|                    ['div {color: red}', 'span {color: blue}'])); |                  .then(styles => { | ||||||
|  |                    expect(styles).toEqual(['div {color: red}', 'span {color: blue}']); | ||||||
|  |                    async.done(); | ||||||
|  |                  }); | ||||||
|  |            })); | ||||||
| 
 | 
 | ||||||
|         it('should compile css rules with newlines and quotes', |         it('should compile css rules with newlines and quotes', | ||||||
|            runTest(['div\n{"color": \'red\'}'], [], encapsulation, ['div\n{"color": \'red\'}'])); |            inject([AsyncTestCompleter], (async) => { | ||||||
|  |              compile(['div\n{"color": \'red\'}'], [], encapsulation) | ||||||
|  |                  .then(styles => { | ||||||
|  |                    expect(styles).toEqual(['div\n{"color": \'red\'}']); | ||||||
|  |                    async.done(); | ||||||
|  |                  }); | ||||||
|  |            })); | ||||||
| 
 | 
 | ||||||
|         it('should allow to import rules', |         it('should allow to import rules', inject([AsyncTestCompleter], (async) => { | ||||||
|            runTest(['div {color: red}'], [IMPORT_ABS_MODULE_NAME], encapsulation, |              compile(['div {color: red}'], [IMPORT_ABS_MODULE_NAME], encapsulation) | ||||||
|                    ['div {color: red}', 'span {color: blue}']), |                  .then(styles => { | ||||||
|            1000); |                    expect(styles).toEqual(['div {color: red}', 'span {color: blue}']); | ||||||
|  |                    async.done(); | ||||||
|  |                  }); | ||||||
|  |            })); | ||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
|       describe('with shim', () => { |       describe('with shim', () => { | ||||||
|         var encapsulation = ViewEncapsulation.Emulated; |         var encapsulation = ViewEncapsulation.Emulated; | ||||||
| 
 | 
 | ||||||
|         it('should compile plain css ruless', |         it('should compile plain css ruless', inject([AsyncTestCompleter], (async) => { | ||||||
|            runTest( |              compile(['div {\ncolor: red;\n}', 'span {\ncolor: blue;\n}'], [], encapsulation) | ||||||
|                ['div {\ncolor: red;\n}', 'span {\ncolor: blue;\n}'], [], encapsulation, |                  .then(styles => { | ||||||
|                ['div[_ngcontent-23] {\ncolor: red;\n}', 'span[_ngcontent-23] {\ncolor: blue;\n}'])); |                    expect(styles).toEqual([ | ||||||
|  |                      'div[_ngcontent-23] {\ncolor: red;\n}', | ||||||
|  |                      'span[_ngcontent-23] {\ncolor: blue;\n}' | ||||||
|  |                    ]); | ||||||
|  |                    async.done(); | ||||||
|  |                  }); | ||||||
|  |            })); | ||||||
| 
 | 
 | ||||||
|         it('should allow to import rules', |         it('should allow to import rules', inject([AsyncTestCompleter], (async) => { | ||||||
|            runTest( |              compile(['div {color: red}'], [IMPORT_ABS_MODULE_NAME], encapsulation) | ||||||
|                ['div {color: red}'], [IMPORT_ABS_MODULE_NAME], encapsulation, |                  .then(styles => { | ||||||
|                ['div[_ngcontent-23] {\ncolor: red;\n}', 'span[_ngcontent-23] {\ncolor: blue;\n}']), |                    expect(styles).toEqual([ | ||||||
|            1000); |                      'div[_ngcontent-23] {\ncolor: red;\n}', | ||||||
|  |                      'span[_ngcontent-23] {\ncolor: blue;\n}' | ||||||
|  |                    ]); | ||||||
|  |                    async.done(); | ||||||
|  |                  }); | ||||||
|  |            })); | ||||||
|       }); |       }); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     describe('compileStylesheetCodeGen', () => { |     describe('compileStylesheetCodeGen', () => { | ||||||
|       function runTest(style: string, expectedStyles: string[], expectedShimStyles: string[]) { |       function compile(style: string): Promise<string[]> { | ||||||
|         return inject([AsyncTestCompleter], (async) => { |  | ||||||
|         var sourceModules = compiler.compileStylesheetCodeGen(MODULE_NAME, style); |         var sourceModules = compiler.compileStylesheetCodeGen(MODULE_NAME, style); | ||||||
|           PromiseWrapper.all(sourceModules.map(sourceModule => { |         return PromiseWrapper.all(sourceModules.map(sourceModule => { | ||||||
|                           var sourceWithImports = |           var sourceWithImports = testableModule(sourceModule).getSourceWithImports(); | ||||||
|                               testableModule(sourceModule).getSourceWithImports(); |           return evalModule(sourceWithImports.source, sourceWithImports.imports, null); | ||||||
|                           return evalModule(sourceWithImports.source, sourceWithImports.imports, |         })); | ||||||
|                                             null); |  | ||||||
|                         })) |  | ||||||
|               .then((values) => { |  | ||||||
|                 compareStyles(values[0], expectedStyles); |  | ||||||
|                 compareStyles(values[1], expectedShimStyles); |  | ||||||
| 
 |  | ||||||
|                 async.done(); |  | ||||||
|               }); |  | ||||||
|         }); |  | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       it('should compile plain css rules', runTest('div {color: red;}', ['div {color: red;}'], |       it('should compile plain css rules', inject([AsyncTestCompleter], (async) => { | ||||||
|                                                    ['div[_ngcontent-%COMP%] {\ncolor: red;\n}'])); |            compile('div {color: red;}') | ||||||
|  |                .then(stylesAndShimStyles => { | ||||||
|  |                  expect(stylesAndShimStyles) | ||||||
|  |                      .toEqual( | ||||||
|  |                          [['div {color: red;}'], ['div[_ngcontent-%COMP%] {\ncolor: red;\n}']]); | ||||||
|  |                  async.done(); | ||||||
|  |                }); | ||||||
|  |          })); | ||||||
| 
 | 
 | ||||||
|       it('should allow to import rules with relative paths', |       it('should allow to import rules with relative paths', | ||||||
|          runTest(`div {color: red}@import ${IMPORT_REL_MODULE_NAME};`, |          inject([AsyncTestCompleter], (async) => { | ||||||
|                  ['div {color: red}', 'span {color: blue}'], [ |            compile(`div {color: red}@import ${IMPORT_REL_MODULE_NAME};`) | ||||||
|  |                .then(stylesAndShimStyles => { | ||||||
|  |                  expect(stylesAndShimStyles) | ||||||
|  |                      .toEqual([ | ||||||
|  |                        ['div {color: red}', 'span {color: blue}'], | ||||||
|  |                        [ | ||||||
|                          'div[_ngcontent-%COMP%] {\ncolor: red;\n}', |                          'div[_ngcontent-%COMP%] {\ncolor: red;\n}', | ||||||
|                          'span[_ngcontent-%COMP%] {\ncolor: blue;\n}' |                          'span[_ngcontent-%COMP%] {\ncolor: blue;\n}' | ||||||
|                  ])); |                        ] | ||||||
|  |                      ]); | ||||||
|  |                  async.done(); | ||||||
|  |                }); | ||||||
|  |          })); | ||||||
|     }); |     }); | ||||||
|   }); |   }); | ||||||
| } | } | ||||||
|  | |||||||
| @ -21,11 +21,7 @@ import { | |||||||
|   TemplateCompiler, |   TemplateCompiler, | ||||||
|   NormalizedComponentWithViewDirectives |   NormalizedComponentWithViewDirectives | ||||||
| } from 'angular2/src/compiler/template_compiler'; | } from 'angular2/src/compiler/template_compiler'; | ||||||
| import { | import {CompileDirectiveMetadata} from 'angular2/src/compiler/directive_metadata'; | ||||||
|   DirectiveMetadata, |  | ||||||
|   NormalizedDirectiveMetadata, |  | ||||||
|   INormalizedDirectiveMetadata |  | ||||||
| } from 'angular2/src/compiler/directive_metadata'; |  | ||||||
| import {evalModule} from './eval_module'; | import {evalModule} from './eval_module'; | ||||||
| import {SourceModule, moduleRef} from 'angular2/src/compiler/source_module'; | import {SourceModule, moduleRef} from 'angular2/src/compiler/source_module'; | ||||||
| import {XHR} from 'angular2/src/core/render/xhr'; | import {XHR} from 'angular2/src/core/render/xhr'; | ||||||
| @ -104,6 +100,18 @@ export function main() { | |||||||
|                    async.done(); |                    async.done(); | ||||||
|                  }); |                  }); | ||||||
|            })); |            })); | ||||||
|  | 
 | ||||||
|  |         it('should pass the right change detector to embedded templates', | ||||||
|  |            inject([AsyncTestCompleter], (async) => { | ||||||
|  |              compile([CompWithEmbeddedTemplate]) | ||||||
|  |                  .then((humanizedTemplate) => { | ||||||
|  |                    expect(humanizedTemplate['commands'][1]['commands'][0]).toEqual('<template>'); | ||||||
|  |                    expect(humanizedTemplate['commands'][1]['commands'][1]['cd']) | ||||||
|  |                        .toEqual(['elementProperty(href)=someCtxValue']); | ||||||
|  | 
 | ||||||
|  |                    async.done(); | ||||||
|  |                  }); | ||||||
|  |            })); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       describe('compileHostComponentRuntime', () => { |       describe('compileHostComponentRuntime', () => { | ||||||
| @ -113,28 +121,53 @@ export function main() { | |||||||
| 
 | 
 | ||||||
|         runTests(compile); |         runTests(compile); | ||||||
| 
 | 
 | ||||||
|         it('should cache components', inject([AsyncTestCompleter, XHR], (async, xhr: MockXHR) => { |         it('should cache components for parallel requests', | ||||||
|              // we expect only one request!
 |            inject([AsyncTestCompleter, XHR], (async, xhr: MockXHR) => { | ||||||
|              xhr.expect('angular2/test/compiler/compUrl.html', ''); |              xhr.expect('angular2/test/compiler/compUrl.html', 'a'); | ||||||
|              PromiseWrapper.all([ |              PromiseWrapper.all([compile([CompWithTemplateUrl]), compile([CompWithTemplateUrl])]) | ||||||
|                              compiler.compileHostComponentRuntime(CompWithTemplateUrl), |  | ||||||
|                              compiler.compileHostComponentRuntime(CompWithTemplateUrl) |  | ||||||
|                            ]) |  | ||||||
|                  .then((humanizedTemplates) => { |                  .then((humanizedTemplates) => { | ||||||
|                    expect(humanizedTemplates[0]).toEqual(humanizedTemplates[1]); |                    expect(humanizedTemplates[0]['commands'][1]['commands']).toEqual(['#text(a)']); | ||||||
|  |                    expect(humanizedTemplates[1]['commands'][1]['commands']).toEqual(['#text(a)']); | ||||||
|  | 
 | ||||||
|                    async.done(); |                    async.done(); | ||||||
|                  }); |                  }); | ||||||
|              xhr.flush(); |              xhr.flush(); | ||||||
|            })); |            })); | ||||||
| 
 | 
 | ||||||
|         it('should only allow dynamic loadable components', () => { |         it('should cache components for sequential requests', | ||||||
|           expect(() => compiler.compileHostComponentRuntime(PlainDirective)) |            inject([AsyncTestCompleter, XHR], (async, xhr: MockXHR) => { | ||||||
|               .toThrowError( |              xhr.expect('angular2/test/compiler/compUrl.html', 'a'); | ||||||
|                   `Could not compile '${stringify(PlainDirective)}' because it is not dynamically loadable.`); |              compile([CompWithTemplateUrl]) | ||||||
|           expect(() => compiler.compileHostComponentRuntime(CompWithoutHost)) |                  .then((humanizedTemplate0) => { | ||||||
|               .toThrowError( |                    return compile([CompWithTemplateUrl]) | ||||||
|                   `Could not compile '${stringify(CompWithoutHost)}' because it is not dynamically loadable.`); |                        .then((humanizedTemplate1) => { | ||||||
|  |                          expect(humanizedTemplate0['commands'][1]['commands']) | ||||||
|  |                              .toEqual(['#text(a)']); | ||||||
|  |                          expect(humanizedTemplate1['commands'][1]['commands']) | ||||||
|  |                              .toEqual(['#text(a)']); | ||||||
|  |                          async.done(); | ||||||
|                        }); |                        }); | ||||||
|  |                  }); | ||||||
|  |              xhr.flush(); | ||||||
|  |            })); | ||||||
|  | 
 | ||||||
|  |         it('should allow to clear the cache', | ||||||
|  |            inject([AsyncTestCompleter, XHR], (async, xhr: MockXHR) => { | ||||||
|  |              xhr.expect('angular2/test/compiler/compUrl.html', 'a'); | ||||||
|  |              compile([CompWithTemplateUrl]) | ||||||
|  |                  .then((humanizedTemplate) => { | ||||||
|  |                    compiler.clearCache(); | ||||||
|  |                    xhr.expect('angular2/test/compiler/compUrl.html', 'b'); | ||||||
|  |                    var result = compile([CompWithTemplateUrl]); | ||||||
|  |                    xhr.flush(); | ||||||
|  |                    return result; | ||||||
|  |                  }) | ||||||
|  |                  .then((humanizedTemplate) => { | ||||||
|  |                    expect(humanizedTemplate['commands'][1]['commands']).toEqual(['#text(b)']); | ||||||
|  |                    async.done(); | ||||||
|  |                  }); | ||||||
|  |              xhr.flush(); | ||||||
|  |            })); | ||||||
| 
 | 
 | ||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
| @ -145,7 +178,7 @@ export function main() { | |||||||
|               runtimeMetadataResolver.getViewDirectivesMetadata(component)); |               runtimeMetadataResolver.getViewDirectivesMetadata(component)); | ||||||
|           return PromiseWrapper.all(compAndViewDirMetas.map( |           return PromiseWrapper.all(compAndViewDirMetas.map( | ||||||
|                                         meta => compiler.normalizeDirectiveMetadata(meta))) |                                         meta => compiler.normalizeDirectiveMetadata(meta))) | ||||||
|               .then((normalizedCompAndViewDirMetas: NormalizedDirectiveMetadata[]) => |               .then((normalizedCompAndViewDirMetas: CompileDirectiveMetadata[]) => | ||||||
|                         new NormalizedComponentWithViewDirectives( |                         new NormalizedComponentWithViewDirectives( | ||||||
|                             normalizedCompAndViewDirMetas[0], |                             normalizedCompAndViewDirMetas[0], | ||||||
|                             normalizedCompAndViewDirMetas.slice(1))); |                             normalizedCompAndViewDirMetas.slice(1))); | ||||||
| @ -173,15 +206,15 @@ export function main() { | |||||||
|       it('should serialize and deserialize', inject([AsyncTestCompleter], (async) => { |       it('should serialize and deserialize', inject([AsyncTestCompleter], (async) => { | ||||||
|            compiler.normalizeDirectiveMetadata( |            compiler.normalizeDirectiveMetadata( | ||||||
|                        runtimeMetadataResolver.getMetadata(CompWithBindingsAndStyles)) |                        runtimeMetadataResolver.getMetadata(CompWithBindingsAndStyles)) | ||||||
|                .then((meta: NormalizedDirectiveMetadata) => { |                .then((meta: CompileDirectiveMetadata) => { | ||||||
|                  var json = compiler.serializeDirectiveMetadata(meta); |                  var json = compiler.serializeDirectiveMetadata(meta); | ||||||
|                  expect(isString(json)).toBe(true); |                  expect(isString(json)).toBe(true); | ||||||
|                  // Note: serializing will clear our the runtime type!
 |                  // Note: serializing will clear our the runtime type!
 | ||||||
|                  var clonedMeta = |                  var clonedMeta = compiler.deserializeDirectiveMetadata(json); | ||||||
|                      <NormalizedDirectiveMetadata>compiler.deserializeDirectiveMetadata(json); |  | ||||||
|                  expect(meta.changeDetection).toEqual(clonedMeta.changeDetection); |                  expect(meta.changeDetection).toEqual(clonedMeta.changeDetection); | ||||||
|                  expect(meta.template).toEqual(clonedMeta.template); |                  expect(meta.template).toEqual(clonedMeta.template); | ||||||
|                  expect(meta.selector).toEqual(clonedMeta.selector); |                  expect(meta.selector).toEqual(clonedMeta.selector); | ||||||
|  |                  expect(meta.exportAs).toEqual(clonedMeta.exportAs); | ||||||
|                  expect(meta.type.name).toEqual(clonedMeta.type.name); |                  expect(meta.type.name).toEqual(clonedMeta.type.name); | ||||||
|                  async.done(); |                  async.done(); | ||||||
|                }); |                }); | ||||||
| @ -194,7 +227,7 @@ export function main() { | |||||||
|            xhr.expect('angular2/test/compiler/compUrl.html', 'loadedTemplate'); |            xhr.expect('angular2/test/compiler/compUrl.html', 'loadedTemplate'); | ||||||
|            compiler.normalizeDirectiveMetadata( |            compiler.normalizeDirectiveMetadata( | ||||||
|                        runtimeMetadataResolver.getMetadata(CompWithTemplateUrl)) |                        runtimeMetadataResolver.getMetadata(CompWithTemplateUrl)) | ||||||
|                .then((meta: NormalizedDirectiveMetadata) => { |                .then((meta: CompileDirectiveMetadata) => { | ||||||
|                  expect(meta.template.template).toEqual('loadedTemplate'); |                  expect(meta.template.template).toEqual('loadedTemplate'); | ||||||
|                  async.done(); |                  async.done(); | ||||||
|                }); |                }); | ||||||
| @ -203,13 +236,19 @@ export function main() { | |||||||
| 
 | 
 | ||||||
|       it('should copy all the other fields', inject([AsyncTestCompleter], (async) => { |       it('should copy all the other fields', inject([AsyncTestCompleter], (async) => { | ||||||
|            var meta = runtimeMetadataResolver.getMetadata(CompWithBindingsAndStyles); |            var meta = runtimeMetadataResolver.getMetadata(CompWithBindingsAndStyles); | ||||||
|            compiler.normalizeDirectiveMetadata(meta) |            compiler.normalizeDirectiveMetadata(meta).then((normMeta: CompileDirectiveMetadata) => { | ||||||
|                .then((normMeta: NormalizedDirectiveMetadata) => { |  | ||||||
|                  expect(normMeta.selector).toEqual(meta.selector); |  | ||||||
|                  expect(normMeta.dynamicLoadable).toEqual(meta.dynamicLoadable); |  | ||||||
|                  expect(normMeta.isComponent).toEqual(meta.isComponent); |  | ||||||
|              expect(normMeta.type).toEqual(meta.type); |              expect(normMeta.type).toEqual(meta.type); | ||||||
|  |              expect(normMeta.isComponent).toEqual(meta.isComponent); | ||||||
|  |              expect(normMeta.dynamicLoadable).toEqual(meta.dynamicLoadable); | ||||||
|  |              expect(normMeta.selector).toEqual(meta.selector); | ||||||
|  |              expect(normMeta.exportAs).toEqual(meta.exportAs); | ||||||
|              expect(normMeta.changeDetection).toEqual(meta.changeDetection); |              expect(normMeta.changeDetection).toEqual(meta.changeDetection); | ||||||
|  |              expect(normMeta.properties).toEqual(meta.properties); | ||||||
|  |              expect(normMeta.events).toEqual(meta.events); | ||||||
|  |              expect(normMeta.hostListeners).toEqual(meta.hostListeners); | ||||||
|  |              expect(normMeta.hostProperties).toEqual(meta.hostProperties); | ||||||
|  |              expect(normMeta.hostAttributes).toEqual(meta.hostAttributes); | ||||||
|  |              expect(normMeta.lifecycleHooks).toEqual(meta.lifecycleHooks); | ||||||
|              async.done(); |              async.done(); | ||||||
|            }); |            }); | ||||||
|          })); |          })); | ||||||
| @ -233,24 +272,30 @@ export function main() { | |||||||
| 
 | 
 | ||||||
| @Component({ | @Component({ | ||||||
|   selector: 'comp-a', |   selector: 'comp-a', | ||||||
|   dynamicLoadable: true, |  | ||||||
|   host: {'[title]': 'someProp'}, |   host: {'[title]': 'someProp'}, | ||||||
|   moduleId: THIS_MODULE |   moduleId: THIS_MODULE, | ||||||
|  |   exportAs: 'someExportAs' | ||||||
| }) | }) | ||||||
| @View({template: '<a [href]="someProp"></a>', styles: ['div {color: red}']}) | @View({template: '<a [href]="someProp"></a>', styles: ['div {color: red}']}) | ||||||
| class CompWithBindingsAndStyles { | class CompWithBindingsAndStyles { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @Component({selector: 'tree', dynamicLoadable: true, moduleId: THIS_MODULE}) | @Component({selector: 'tree', moduleId: THIS_MODULE}) | ||||||
| @View({template: '<tree></tree>', directives: [TreeComp]}) | @View({template: '<tree></tree>', directives: [TreeComp]}) | ||||||
| class TreeComp { | class TreeComp { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @Component({selector: 'comp-url', dynamicLoadable: true, moduleId: THIS_MODULE}) | @Component({selector: 'comp-url', moduleId: THIS_MODULE}) | ||||||
| @View({templateUrl: 'compUrl.html'}) | @View({templateUrl: 'compUrl.html'}) | ||||||
| class CompWithTemplateUrl { | class CompWithTemplateUrl { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @Component({selector: 'comp-tpl', moduleId: THIS_MODULE}) | ||||||
|  | @View({template: '<template><a [href]="someProp"></a></template>'}) | ||||||
|  | class CompWithEmbeddedTemplate { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| @Directive({selector: 'plain', moduleId: THIS_MODULE}) | @Directive({selector: 'plain', moduleId: THIS_MODULE}) | ||||||
| class PlainDirective { | class PlainDirective { | ||||||
| } | } | ||||||
| @ -260,9 +305,8 @@ class PlainDirective { | |||||||
| class CompWithoutHost { | class CompWithoutHost { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function testableTemplateModule(sourceModule: SourceModule, comp: INormalizedDirectiveMetadata): | function testableTemplateModule(sourceModule: SourceModule, normComp: CompileDirectiveMetadata): | ||||||
|     SourceModule { |     SourceModule { | ||||||
|   var normComp = <NormalizedDirectiveMetadata>comp; |  | ||||||
|   var resultExpression = `${THIS_MODULE_REF}humanizeTemplate(Host${normComp.type.name}Template)`; |   var resultExpression = `${THIS_MODULE_REF}humanizeTemplate(Host${normComp.type.name}Template)`; | ||||||
|   var testableSource = `${sourceModule.sourceWithModuleRefs} |   var testableSource = `${sourceModule.sourceWithModuleRefs} | ||||||
|   ${codeGenExportVariable('run')}${codeGenValueFn(['_'], resultExpression)};`;
 |   ${codeGenExportVariable('run')}${codeGenValueFn(['_'], resultExpression)};`;
 | ||||||
| @ -290,7 +334,7 @@ export function humanizeTemplate(template: CompiledTemplate, | |||||||
|   result = { |   result = { | ||||||
|     'styles': template.styles, |     'styles': template.styles, | ||||||
|     'commands': commands, |     'commands': commands, | ||||||
|     'cd': testChangeDetector(template.changeDetectorFactories[0]) |     'cd': testChangeDetector(template.changeDetectorFactory) | ||||||
|   }; |   }; | ||||||
|   humanizedTemplates.set(template.id, result); |   humanizedTemplates.set(template.id, result); | ||||||
|   visitAllCommands(new CommandHumanizer(commands, humanizedTemplates), template.commands); |   visitAllCommands(new CommandHumanizer(commands, humanizedTemplates), template.commands); | ||||||
| @ -316,7 +360,10 @@ function testChangeDetector(changeDetectorFactory: Function): string[] { | |||||||
| class CommandHumanizer implements CommandVisitor { | class CommandHumanizer implements CommandVisitor { | ||||||
|   constructor(private result: any[], |   constructor(private result: any[], | ||||||
|               private humanizedTemplates: Map<number, StringMap<string, any>>) {} |               private humanizedTemplates: Map<number, StringMap<string, any>>) {} | ||||||
|   visitText(cmd: TextCmd, context: any): any { return null; } |   visitText(cmd: TextCmd, context: any): any { | ||||||
|  |     this.result.push(`#text(${cmd.value})`); | ||||||
|  |     return null; | ||||||
|  |   } | ||||||
|   visitNgContent(cmd: NgContentCmd, context: any): any { return null; } |   visitNgContent(cmd: NgContentCmd, context: any): any { return null; } | ||||||
|   visitBeginElement(cmd: BeginElementCmd, context: any): any { |   visitBeginElement(cmd: BeginElementCmd, context: any): any { | ||||||
|     this.result.push(`<${cmd.name}>`); |     this.result.push(`<${cmd.name}>`); | ||||||
| @ -332,5 +379,10 @@ class CommandHumanizer implements CommandVisitor { | |||||||
|     return null; |     return null; | ||||||
|   } |   } | ||||||
|   visitEndComponent(context: any): any { return this.visitEndElement(context); } |   visitEndComponent(context: any): any { return this.visitEndElement(context); } | ||||||
|   visitEmbeddedTemplate(cmd: EmbeddedTemplateCmd, context: any): any { return null; } |   visitEmbeddedTemplate(cmd: EmbeddedTemplateCmd, context: any): any { | ||||||
|  |     this.result.push(`<template>`); | ||||||
|  |     this.result.push({'cd': testChangeDetector(cmd.changeDetectorFactory)}); | ||||||
|  |     this.result.push(`</template>`); | ||||||
|  |     return null; | ||||||
|  |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -14,9 +14,8 @@ import { | |||||||
| } from 'angular2/test_lib'; | } from 'angular2/test_lib'; | ||||||
| 
 | 
 | ||||||
| import { | import { | ||||||
|   TypeMetadata, |   CompileTypeMetadata, | ||||||
|   NormalizedTemplateMetadata, |   CompileTemplateMetadata | ||||||
|   TemplateMetadata |  | ||||||
| } from 'angular2/src/compiler/directive_metadata'; | } from 'angular2/src/compiler/directive_metadata'; | ||||||
| import {ViewEncapsulation} from 'angular2/src/core/render/api'; | import {ViewEncapsulation} from 'angular2/src/core/render/api'; | ||||||
| 
 | 
 | ||||||
| @ -27,27 +26,29 @@ import {TEST_BINDINGS} from './test_bindings'; | |||||||
| 
 | 
 | ||||||
| export function main() { | export function main() { | ||||||
|   describe('TemplateNormalizer', () => { |   describe('TemplateNormalizer', () => { | ||||||
|     var dirType: TypeMetadata; |     var dirType: CompileTypeMetadata; | ||||||
| 
 | 
 | ||||||
|     beforeEachBindings(() => TEST_BINDINGS); |     beforeEachBindings(() => TEST_BINDINGS); | ||||||
| 
 | 
 | ||||||
|     beforeEach( |     beforeEach(() => { | ||||||
|         () => { dirType = new TypeMetadata({moduleId: 'some/module/id', name: 'SomeComp'}); }); |       dirType = new CompileTypeMetadata({moduleId: 'some/module/id', name: 'SomeComp'}); | ||||||
|  |     }); | ||||||
| 
 | 
 | ||||||
|     describe('loadTemplate', () => { |     describe('loadTemplate', () => { | ||||||
|       describe('inline template', () => { |       describe('inline template', () => { | ||||||
|         it('should store the template', |         it('should store the template', | ||||||
|            inject([AsyncTestCompleter, TemplateNormalizer], |            inject([AsyncTestCompleter, TemplateNormalizer], | ||||||
|                   (async, normalizer: TemplateNormalizer) => { |                   (async, normalizer: TemplateNormalizer) => { | ||||||
|                     normalizer.normalizeTemplate(dirType, new TemplateMetadata({ |                     normalizer.normalizeTemplate(dirType, new CompileTemplateMetadata({ | ||||||
|                                                    encapsulation: null, |                                                    encapsulation: null, | ||||||
|                                                    template: 'a', |                                                    template: 'a', | ||||||
|                                                    templateUrl: null, |                                                    templateUrl: null, | ||||||
|                                                    styles: [], |                                                    styles: [], | ||||||
|                                                    styleUrls: ['test.css'] |                                                    styleUrls: ['test.css'] | ||||||
|                                                  })) |                                                  })) | ||||||
|                         .then((template: NormalizedTemplateMetadata) => { |                         .then((template: CompileTemplateMetadata) => { | ||||||
|                           expect(template.template).toEqual('a'); |                           expect(template.template).toEqual('a'); | ||||||
|  |                           expect(template.templateUrl).toEqual('some/module/id'); | ||||||
|                           async.done(); |                           async.done(); | ||||||
|                         }); |                         }); | ||||||
|                   })); |                   })); | ||||||
| @ -55,15 +56,15 @@ export function main() { | |||||||
|         it('should resolve styles on the annotation against the moduleId', |         it('should resolve styles on the annotation against the moduleId', | ||||||
|            inject([AsyncTestCompleter, TemplateNormalizer], |            inject([AsyncTestCompleter, TemplateNormalizer], | ||||||
|                   (async, normalizer: TemplateNormalizer) => { |                   (async, normalizer: TemplateNormalizer) => { | ||||||
|                     normalizer.normalizeTemplate(dirType, new TemplateMetadata({ |                     normalizer.normalizeTemplate(dirType, new CompileTemplateMetadata({ | ||||||
|                                                    encapsulation: null, |                                                    encapsulation: null, | ||||||
|                                                    template: '', |                                                    template: '', | ||||||
|                                                    templateUrl: null, |                                                    templateUrl: null, | ||||||
|                                                    styles: [], |                                                    styles: [], | ||||||
|                                                    styleUrls: ['test.css'] |                                                    styleUrls: ['test.css'] | ||||||
|                                                  })) |                                                  })) | ||||||
|                         .then((template: NormalizedTemplateMetadata) => { |                         .then((template: CompileTemplateMetadata) => { | ||||||
|                           expect(template.styleAbsUrls).toEqual(['some/module/test.css']); |                           expect(template.styleUrls).toEqual(['some/module/test.css']); | ||||||
|                           async.done(); |                           async.done(); | ||||||
|                         }); |                         }); | ||||||
|                   })); |                   })); | ||||||
| @ -71,15 +72,15 @@ export function main() { | |||||||
|         it('should resolve styles in the template against the moduleId', |         it('should resolve styles in the template against the moduleId', | ||||||
|            inject([AsyncTestCompleter, TemplateNormalizer], |            inject([AsyncTestCompleter, TemplateNormalizer], | ||||||
|                   (async, normalizer: TemplateNormalizer) => { |                   (async, normalizer: TemplateNormalizer) => { | ||||||
|                     normalizer.normalizeTemplate(dirType, new TemplateMetadata({ |                     normalizer.normalizeTemplate(dirType, new CompileTemplateMetadata({ | ||||||
|                                                    encapsulation: null, |                                                    encapsulation: null, | ||||||
|                                                    template: '<style>@import test.css</style>', |                                                    template: '<style>@import test.css</style>', | ||||||
|                                                    templateUrl: null, |                                                    templateUrl: null, | ||||||
|                                                    styles: [], |                                                    styles: [], | ||||||
|                                                    styleUrls: [] |                                                    styleUrls: [] | ||||||
|                                                  })) |                                                  })) | ||||||
|                         .then((template: NormalizedTemplateMetadata) => { |                         .then((template: CompileTemplateMetadata) => { | ||||||
|                           expect(template.styleAbsUrls).toEqual(['some/module/test.css']); |                           expect(template.styleUrls).toEqual(['some/module/test.css']); | ||||||
|                           async.done(); |                           async.done(); | ||||||
|                         }); |                         }); | ||||||
|                   })); |                   })); | ||||||
| @ -91,15 +92,16 @@ export function main() { | |||||||
|            inject([AsyncTestCompleter, TemplateNormalizer, XHR], |            inject([AsyncTestCompleter, TemplateNormalizer, XHR], | ||||||
|                   (async, normalizer: TemplateNormalizer, xhr: MockXHR) => { |                   (async, normalizer: TemplateNormalizer, xhr: MockXHR) => { | ||||||
|                     xhr.expect('some/module/sometplurl', 'a'); |                     xhr.expect('some/module/sometplurl', 'a'); | ||||||
|                     normalizer.normalizeTemplate(dirType, new TemplateMetadata({ |                     normalizer.normalizeTemplate(dirType, new CompileTemplateMetadata({ | ||||||
|                                                    encapsulation: null, |                                                    encapsulation: null, | ||||||
|                                                    template: null, |                                                    template: null, | ||||||
|                                                    templateUrl: 'sometplurl', |                                                    templateUrl: 'sometplurl', | ||||||
|                                                    styles: [], |                                                    styles: [], | ||||||
|                                                    styleUrls: ['test.css'] |                                                    styleUrls: ['test.css'] | ||||||
|                                                  })) |                                                  })) | ||||||
|                         .then((template: NormalizedTemplateMetadata) => { |                         .then((template: CompileTemplateMetadata) => { | ||||||
|                           expect(template.template).toEqual('a'); |                           expect(template.template).toEqual('a'); | ||||||
|  |                           expect(template.templateUrl).toEqual('some/module/sometplurl'); | ||||||
|                           async.done(); |                           async.done(); | ||||||
|                         }); |                         }); | ||||||
|                     xhr.flush(); |                     xhr.flush(); | ||||||
| @ -109,15 +111,15 @@ export function main() { | |||||||
|            inject([AsyncTestCompleter, TemplateNormalizer, XHR], |            inject([AsyncTestCompleter, TemplateNormalizer, XHR], | ||||||
|                   (async, normalizer: TemplateNormalizer, xhr: MockXHR) => { |                   (async, normalizer: TemplateNormalizer, xhr: MockXHR) => { | ||||||
|                     xhr.expect('some/module/tpl/sometplurl', ''); |                     xhr.expect('some/module/tpl/sometplurl', ''); | ||||||
|                     normalizer.normalizeTemplate(dirType, new TemplateMetadata({ |                     normalizer.normalizeTemplate(dirType, new CompileTemplateMetadata({ | ||||||
|                                                    encapsulation: null, |                                                    encapsulation: null, | ||||||
|                                                    template: null, |                                                    template: null, | ||||||
|                                                    templateUrl: 'tpl/sometplurl', |                                                    templateUrl: 'tpl/sometplurl', | ||||||
|                                                    styles: [], |                                                    styles: [], | ||||||
|                                                    styleUrls: ['test.css'] |                                                    styleUrls: ['test.css'] | ||||||
|                                                  })) |                                                  })) | ||||||
|                         .then((template: NormalizedTemplateMetadata) => { |                         .then((template: CompileTemplateMetadata) => { | ||||||
|                           expect(template.styleAbsUrls).toEqual(['some/module/test.css']); |                           expect(template.styleUrls).toEqual(['some/module/test.css']); | ||||||
|                           async.done(); |                           async.done(); | ||||||
|                         }); |                         }); | ||||||
|                     xhr.flush(); |                     xhr.flush(); | ||||||
| @ -127,15 +129,15 @@ export function main() { | |||||||
|            inject([AsyncTestCompleter, TemplateNormalizer, XHR], |            inject([AsyncTestCompleter, TemplateNormalizer, XHR], | ||||||
|                   (async, normalizer: TemplateNormalizer, xhr: MockXHR) => { |                   (async, normalizer: TemplateNormalizer, xhr: MockXHR) => { | ||||||
|                     xhr.expect('some/module/tpl/sometplurl', '<style>@import test.css</style>'); |                     xhr.expect('some/module/tpl/sometplurl', '<style>@import test.css</style>'); | ||||||
|                     normalizer.normalizeTemplate(dirType, new TemplateMetadata({ |                     normalizer.normalizeTemplate(dirType, new CompileTemplateMetadata({ | ||||||
|                                                    encapsulation: null, |                                                    encapsulation: null, | ||||||
|                                                    template: null, |                                                    template: null, | ||||||
|                                                    templateUrl: 'tpl/sometplurl', |                                                    templateUrl: 'tpl/sometplurl', | ||||||
|                                                    styles: [], |                                                    styles: [], | ||||||
|                                                    styleUrls: [] |                                                    styleUrls: [] | ||||||
|                                                  })) |                                                  })) | ||||||
|                         .then((template: NormalizedTemplateMetadata) => { |                         .then((template: CompileTemplateMetadata) => { | ||||||
|                           expect(template.styleAbsUrls).toEqual(['some/module/tpl/test.css']); |                           expect(template.styleUrls).toEqual(['some/module/tpl/test.css']); | ||||||
|                           async.done(); |                           async.done(); | ||||||
|                         }); |                         }); | ||||||
|                     xhr.flush(); |                     xhr.flush(); | ||||||
| @ -151,8 +153,8 @@ export function main() { | |||||||
| 
 | 
 | ||||||
|            var viewEncapsulation = ViewEncapsulation.Native; |            var viewEncapsulation = ViewEncapsulation.Native; | ||||||
|            var template = normalizer.normalizeLoadedTemplate( |            var template = normalizer.normalizeLoadedTemplate( | ||||||
|                dirType, |                dirType, new CompileTemplateMetadata( | ||||||
|                new TemplateMetadata({encapsulation: viewEncapsulation, styles: [], styleUrls: []}), |                             {encapsulation: viewEncapsulation, styles: [], styleUrls: []}), | ||||||
|                '', 'some/module/'); |                '', 'some/module/'); | ||||||
|            expect(template.encapsulation).toBe(viewEncapsulation); |            expect(template.encapsulation).toBe(viewEncapsulation); | ||||||
|          })); |          })); | ||||||
| @ -160,88 +162,90 @@ export function main() { | |||||||
|       it('should keep the template as html', |       it('should keep the template as html', | ||||||
|          inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => { |          inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => { | ||||||
|            var template = normalizer.normalizeLoadedTemplate( |            var template = normalizer.normalizeLoadedTemplate( | ||||||
|                dirType, new TemplateMetadata({encapsulation: null, styles: [], styleUrls: []}), 'a', |                dirType, | ||||||
|  |                new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}), 'a', | ||||||
|                'some/module/'); |                'some/module/'); | ||||||
|            expect(template.template).toEqual('a') |            expect(template.template).toEqual('a') | ||||||
|          })); |          })); | ||||||
| 
 | 
 | ||||||
|       it('should collect and keep ngContent', |       it('should collect ngContent', | ||||||
|          inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => { |          inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => { | ||||||
|            var template = normalizer.normalizeLoadedTemplate( |            var template = normalizer.normalizeLoadedTemplate( | ||||||
|                dirType, new TemplateMetadata({encapsulation: null, styles: [], styleUrls: []}), |                dirType, | ||||||
|  |                new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}), | ||||||
|                '<ng-content select="a"></ng-content>', 'some/module/'); |                '<ng-content select="a"></ng-content>', 'some/module/'); | ||||||
|            expect(template.ngContentSelectors).toEqual(['a']); |            expect(template.ngContentSelectors).toEqual(['a']); | ||||||
|            expect(template.template).toEqual('<ng-content select="a"></ng-content>'); |  | ||||||
|          })); |          })); | ||||||
| 
 | 
 | ||||||
|       it('should normalize ngContent wildcard selector', |       it('should normalize ngContent wildcard selector', | ||||||
|          inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => { |          inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => { | ||||||
|            var template = normalizer.normalizeLoadedTemplate( |            var template = normalizer.normalizeLoadedTemplate( | ||||||
|                dirType, new TemplateMetadata({encapsulation: null, styles: [], styleUrls: []}), |                dirType, | ||||||
|  |                new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}), | ||||||
|                '<ng-content></ng-content><ng-content select></ng-content><ng-content select="*"></ng-content>', |                '<ng-content></ng-content><ng-content select></ng-content><ng-content select="*"></ng-content>', | ||||||
|                'some/module/'); |                'some/module/'); | ||||||
|            expect(template.ngContentSelectors).toEqual(['*', '*', '*']); |            expect(template.ngContentSelectors).toEqual(['*', '*', '*']); | ||||||
|          })); |          })); | ||||||
| 
 | 
 | ||||||
|       it('should collect and remove top level styles in the template', |       it('should collect top level styles in the template', | ||||||
|          inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => { |          inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => { | ||||||
|            var template = normalizer.normalizeLoadedTemplate( |            var template = normalizer.normalizeLoadedTemplate( | ||||||
|                dirType, new TemplateMetadata({encapsulation: null, styles: [], styleUrls: []}), |                dirType, | ||||||
|  |                new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}), | ||||||
|                '<style>a</style>', 'some/module/'); |                '<style>a</style>', 'some/module/'); | ||||||
|            expect(template.styles).toEqual(['a']); |            expect(template.styles).toEqual(['a']); | ||||||
|            expect(template.template).toEqual(''); |  | ||||||
|          })); |          })); | ||||||
| 
 | 
 | ||||||
|       it('should collect and remove styles inside in elements', |       it('should collect styles inside in elements', | ||||||
|          inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => { |          inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => { | ||||||
|            var template = normalizer.normalizeLoadedTemplate( |            var template = normalizer.normalizeLoadedTemplate( | ||||||
|                dirType, new TemplateMetadata({encapsulation: null, styles: [], styleUrls: []}), |                dirType, | ||||||
|  |                new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}), | ||||||
|                '<div><style>a</style></div>', 'some/module/'); |                '<div><style>a</style></div>', 'some/module/'); | ||||||
|            expect(template.styles).toEqual(['a']); |            expect(template.styles).toEqual(['a']); | ||||||
|            expect(template.template).toEqual('<div></div>'); |  | ||||||
|          })); |          })); | ||||||
| 
 | 
 | ||||||
|       it('should collect and remove styleUrls in the template', |       it('should collect styleUrls in the template', | ||||||
|          inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => { |          inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => { | ||||||
|            var template = normalizer.normalizeLoadedTemplate( |            var template = normalizer.normalizeLoadedTemplate( | ||||||
|                dirType, new TemplateMetadata({encapsulation: null, styles: [], styleUrls: []}), |                dirType, | ||||||
|  |                new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}), | ||||||
|                '<link rel="stylesheet" href="aUrl">', 'some/module/'); |                '<link rel="stylesheet" href="aUrl">', 'some/module/'); | ||||||
|            expect(template.styleAbsUrls).toEqual(['some/module/aUrl']); |            expect(template.styleUrls).toEqual(['some/module/aUrl']); | ||||||
|            expect(template.template).toEqual(''); |  | ||||||
|          })); |          })); | ||||||
| 
 | 
 | ||||||
|       it('should collect and remove styleUrls in elements', |       it('should collect styleUrls in elements', | ||||||
|          inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => { |          inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => { | ||||||
|            var template = normalizer.normalizeLoadedTemplate( |            var template = normalizer.normalizeLoadedTemplate( | ||||||
|                dirType, new TemplateMetadata({encapsulation: null, styles: [], styleUrls: []}), |                dirType, | ||||||
|  |                new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}), | ||||||
|                '<div><link rel="stylesheet" href="aUrl"></div>', 'some/module/'); |                '<div><link rel="stylesheet" href="aUrl"></div>', 'some/module/'); | ||||||
|            expect(template.styleAbsUrls).toEqual(['some/module/aUrl']); |            expect(template.styleUrls).toEqual(['some/module/aUrl']); | ||||||
|            expect(template.template).toEqual('<div></div>'); |  | ||||||
|          })); |          })); | ||||||
| 
 | 
 | ||||||
|       it('should keep link elements with non stylesheet rel attribute', |       it('should ignore link elements with non stylesheet rel attribute', | ||||||
|          inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => { |          inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => { | ||||||
|            var template = normalizer.normalizeLoadedTemplate( |            var template = normalizer.normalizeLoadedTemplate( | ||||||
|                dirType, new TemplateMetadata({encapsulation: null, styles: [], styleUrls: []}), |                dirType, | ||||||
|  |                new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}), | ||||||
|                '<link href="b" rel="a"></link>', 'some/module/'); |                '<link href="b" rel="a"></link>', 'some/module/'); | ||||||
|            expect(template.styleAbsUrls).toEqual([]); |            expect(template.styleUrls).toEqual([]); | ||||||
|            expect(template.template).toEqual('<link href="b" rel="a"></link>'); |  | ||||||
|          })); |          })); | ||||||
| 
 | 
 | ||||||
|       it('should extract @import style urls into styleAbsUrl', |       it('should extract @import style urls into styleAbsUrl', | ||||||
|          inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => { |          inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => { | ||||||
|            var template = normalizer.normalizeLoadedTemplate( |            var template = normalizer.normalizeLoadedTemplate( | ||||||
|                dirType, new TemplateMetadata( |                dirType, new CompileTemplateMetadata( | ||||||
|                             {encapsulation: null, styles: ['@import "test.css";'], styleUrls: []}), |                             {encapsulation: null, styles: ['@import "test.css";'], styleUrls: []}), | ||||||
|                '', 'some/module/id'); |                '', 'some/module/id'); | ||||||
|            expect(template.styles).toEqual(['']); |            expect(template.styles).toEqual(['']); | ||||||
|            expect(template.styleAbsUrls).toEqual(['some/module/test.css']); |            expect(template.styleUrls).toEqual(['some/module/test.css']); | ||||||
|          })); |          })); | ||||||
| 
 | 
 | ||||||
|       it('should resolve relative urls in inline styles', |       it('should resolve relative urls in inline styles', | ||||||
|          inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => { |          inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => { | ||||||
|            var template = normalizer.normalizeLoadedTemplate( |            var template = normalizer.normalizeLoadedTemplate( | ||||||
|                dirType, new TemplateMetadata({ |                dirType, new CompileTemplateMetadata({ | ||||||
|                  encapsulation: null, |                  encapsulation: null, | ||||||
|                  styles: ['.foo{background-image: url(\'double.jpg\');'], |                  styles: ['.foo{background-image: url(\'double.jpg\');'], | ||||||
|                  styleUrls: [] |                  styleUrls: [] | ||||||
| @ -254,15 +258,32 @@ export function main() { | |||||||
|       it('should resolve relative style urls in styleUrls', |       it('should resolve relative style urls in styleUrls', | ||||||
|          inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => { |          inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => { | ||||||
|            var template = normalizer.normalizeLoadedTemplate( |            var template = normalizer.normalizeLoadedTemplate( | ||||||
|                dirType, |                dirType, new CompileTemplateMetadata( | ||||||
|                new TemplateMetadata({encapsulation: null, styles: [], styleUrls: ['test.css']}), '', |                             {encapsulation: null, styles: [], styleUrls: ['test.css']}), | ||||||
|                'some/module/id'); |                '', 'some/module/id'); | ||||||
|            expect(template.styles).toEqual([]); |            expect(template.styles).toEqual([]); | ||||||
|            expect(template.styleAbsUrls).toEqual(['some/module/test.css']); |            expect(template.styleUrls).toEqual(['some/module/test.css']); | ||||||
|  |          })); | ||||||
|  | 
 | ||||||
|  |       it('should normalize ViewEncapsulation.Emulated to ViewEncapsulation.None if there are no stlyes nor stylesheets', | ||||||
|  |          inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => { | ||||||
|  |            var template = normalizer.normalizeLoadedTemplate( | ||||||
|  |                dirType, new CompileTemplateMetadata( | ||||||
|  |                             {encapsulation: ViewEncapsulation.Emulated, styles: [], styleUrls: []}), | ||||||
|  |                '', 'some/module/id'); | ||||||
|  |            expect(template.encapsulation).toEqual(ViewEncapsulation.None); | ||||||
|  |          })); | ||||||
|  | 
 | ||||||
|  |       it('should ignore elements with ng-non-bindable attribute and their children', | ||||||
|  |          inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => { | ||||||
|  |            var template = normalizer.normalizeLoadedTemplate( | ||||||
|  |                dirType, | ||||||
|  |                new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}), | ||||||
|  |                '<div ng-non-bindable><ng-content select="a"></ng-content></div><ng-content ng-non-bindable select="b"></ng-content>', | ||||||
|  |                'some/module/'); | ||||||
|  |            expect(template.ngContentSelectors).toEqual([]); | ||||||
|          })); |          })); | ||||||
| 
 | 
 | ||||||
|     }); |     }); | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|   }); |   }); | ||||||
| } | } | ||||||
|  | |||||||
| @ -16,10 +16,9 @@ import {TEST_BINDINGS} from './test_bindings'; | |||||||
| import {isPresent} from 'angular2/src/core/facade/lang'; | import {isPresent} from 'angular2/src/core/facade/lang'; | ||||||
| import {TemplateParser, splitClasses} from 'angular2/src/compiler/template_parser'; | import {TemplateParser, splitClasses} from 'angular2/src/compiler/template_parser'; | ||||||
| import { | import { | ||||||
|   NormalizedDirectiveMetadata, |   CompileDirectiveMetadata, | ||||||
|   TypeMetadata, |   CompileTypeMetadata, | ||||||
|   ChangeDetectionMetadata, |   CompileTemplateMetadata | ||||||
|   NormalizedTemplateMetadata |  | ||||||
| } from 'angular2/src/compiler/directive_metadata'; | } from 'angular2/src/compiler/directive_metadata'; | ||||||
| import { | import { | ||||||
|   templateVisitAll, |   templateVisitAll, | ||||||
| @ -59,14 +58,14 @@ export function main() { | |||||||
| 
 | 
 | ||||||
|     beforeEach(inject([TemplateParser], (_parser) => { |     beforeEach(inject([TemplateParser], (_parser) => { | ||||||
|       parser = _parser; |       parser = _parser; | ||||||
|       ngIf = new NormalizedDirectiveMetadata({ |       ngIf = CompileDirectiveMetadata.create({ | ||||||
|         selector: '[ng-if]', |         selector: '[ng-if]', | ||||||
|         type: new TypeMetadata({name: 'NgIf'}), |         type: new CompileTypeMetadata({name: 'NgIf'}), | ||||||
|         changeDetection: new ChangeDetectionMetadata({properties: ['ngIf']}) |         properties: ['ngIf'] | ||||||
|       }); |       }); | ||||||
|     })); |     })); | ||||||
| 
 | 
 | ||||||
|     function parse(template: string, directives: NormalizedDirectiveMetadata[]): TemplateAst[] { |     function parse(template: string, directives: CompileDirectiveMetadata[]): TemplateAst[] { | ||||||
|       return parser.parse(template, directives, 'TestComp'); |       return parser.parse(template, directives, 'TestComp'); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -319,68 +318,39 @@ export function main() { | |||||||
| 
 | 
 | ||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
|       describe('variables', () => { |  | ||||||
| 
 |  | ||||||
|         it('should parse variables via #... and not report them as attributes', () => { |  | ||||||
|           expect(humanizeTemplateAsts(parse('<div #a="b">', []))) |  | ||||||
|               .toEqual([ |  | ||||||
|                 [ElementAst, 'div', 'TestComp > div:nth-child(0)'], |  | ||||||
|                 [VariableAst, 'a', 'b', 'TestComp > div:nth-child(0)[#a=b]'] |  | ||||||
|               ]); |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|         it('should parse variables via var-... and not report them as attributes', () => { |  | ||||||
|           expect(humanizeTemplateAsts(parse('<div var-a="b">', []))) |  | ||||||
|               .toEqual([ |  | ||||||
|                 [ElementAst, 'div', 'TestComp > div:nth-child(0)'], |  | ||||||
|                 [VariableAst, 'a', 'b', 'TestComp > div:nth-child(0)[var-a=b]'] |  | ||||||
|               ]); |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|         it('should camel case variables', () => { |  | ||||||
|           expect(humanizeTemplateAsts(parse('<div var-some-a="b">', []))) |  | ||||||
|               .toEqual([ |  | ||||||
|                 [ElementAst, 'div', 'TestComp > div:nth-child(0)'], |  | ||||||
|                 [VariableAst, 'someA', 'b', 'TestComp > div:nth-child(0)[var-some-a=b]'] |  | ||||||
|               ]); |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|         it('should use $implicit as variable name if none was specified', () => { |  | ||||||
|           expect(humanizeTemplateAsts(parse('<div var-a>', []))) |  | ||||||
|               .toEqual([ |  | ||||||
|                 [ElementAst, 'div', 'TestComp > div:nth-child(0)'], |  | ||||||
|                 [VariableAst, 'a', '$implicit', 'TestComp > div:nth-child(0)[var-a=]'] |  | ||||||
|               ]); |  | ||||||
|         }); |  | ||||||
|       }); |  | ||||||
| 
 |  | ||||||
|       describe('directives', () => { |       describe('directives', () => { | ||||||
|         it('should locate directives ordered by name and components first', () => { |         it('should locate directives components first and ordered by the directives array in the View', | ||||||
|           var dirA = new NormalizedDirectiveMetadata( |            () => { | ||||||
|               {selector: '[a=b]', type: new TypeMetadata({name: 'DirA'})}); |              var dirA = CompileDirectiveMetadata.create( | ||||||
|           var dirB = new NormalizedDirectiveMetadata( |                  {selector: '[a]', type: new CompileTypeMetadata({name: 'DirA', id: 3})}); | ||||||
|               {selector: '[a]', type: new TypeMetadata({name: 'DirB'})}); |              var dirB = CompileDirectiveMetadata.create( | ||||||
|           var comp = new NormalizedDirectiveMetadata({ |                  {selector: '[b]', type: new CompileTypeMetadata({name: 'DirB', id: 2})}); | ||||||
|  |              var dirC = CompileDirectiveMetadata.create( | ||||||
|  |                  {selector: '[c]', type: new CompileTypeMetadata({name: 'DirC', id: 1})}); | ||||||
|  |              var comp = CompileDirectiveMetadata.create({ | ||||||
|                selector: 'div', |                selector: 'div', | ||||||
|                isComponent: true, |                isComponent: true, | ||||||
|             type: new TypeMetadata({name: 'ZComp'}), |                type: new CompileTypeMetadata({name: 'ZComp'}), | ||||||
|             template: new NormalizedTemplateMetadata({ngContentSelectors: []}) |                template: new CompileTemplateMetadata({ngContentSelectors: []}) | ||||||
|              }); |              }); | ||||||
|           expect(humanizeTemplateAsts(parse('<div a="b">', [dirB, dirA, comp]))) |              expect(humanizeTemplateAsts(parse('<div a c b>', [dirA, dirB, dirC, comp]))) | ||||||
|                  .toEqual([ |                  .toEqual([ | ||||||
|                    [ElementAst, 'div', 'TestComp > div:nth-child(0)'], |                    [ElementAst, 'div', 'TestComp > div:nth-child(0)'], | ||||||
|                 [AttrAst, 'a', 'b', 'TestComp > div:nth-child(0)[a=b]'], |                    [AttrAst, 'a', '', 'TestComp > div:nth-child(0)[a=]'], | ||||||
|  |                    [AttrAst, 'b', '', 'TestComp > div:nth-child(0)[b=]'], | ||||||
|  |                    [AttrAst, 'c', '', 'TestComp > div:nth-child(0)[c=]'], | ||||||
|                    [DirectiveAst, comp, 'TestComp > div:nth-child(0)'], |                    [DirectiveAst, comp, 'TestComp > div:nth-child(0)'], | ||||||
|                    [DirectiveAst, dirA, 'TestComp > div:nth-child(0)'], |                    [DirectiveAst, dirA, 'TestComp > div:nth-child(0)'], | ||||||
|                 [DirectiveAst, dirB, 'TestComp > div:nth-child(0)'] |                    [DirectiveAst, dirB, 'TestComp > div:nth-child(0)'], | ||||||
|  |                    [DirectiveAst, dirC, 'TestComp > div:nth-child(0)'] | ||||||
|                  ]); |                  ]); | ||||||
|            }); |            }); | ||||||
| 
 | 
 | ||||||
|         it('should locate directives in property bindings', () => { |         it('should locate directives in property bindings', () => { | ||||||
|           var dirA = new NormalizedDirectiveMetadata( |           var dirA = CompileDirectiveMetadata.create( | ||||||
|               {selector: '[a=b]', type: new TypeMetadata({name: 'DirA'})}); |               {selector: '[a=b]', type: new CompileTypeMetadata({name: 'DirA'})}); | ||||||
|           var dirB = new NormalizedDirectiveMetadata( |           var dirB = CompileDirectiveMetadata.create( | ||||||
|               {selector: '[b]', type: new TypeMetadata({name: 'DirB'})}); |               {selector: '[b]', type: new CompileTypeMetadata({name: 'DirB'})}); | ||||||
|           expect(humanizeTemplateAsts(parse('<div [a]="b">', [dirA, dirB]))) |           expect(humanizeTemplateAsts(parse('<div [a]="b">', [dirA, dirB]))) | ||||||
|               .toEqual([ |               .toEqual([ | ||||||
|                 [ElementAst, 'div', 'TestComp > div:nth-child(0)'], |                 [ElementAst, 'div', 'TestComp > div:nth-child(0)'], | ||||||
| @ -397,23 +367,23 @@ export function main() { | |||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|         it('should locate directives in variable bindings', () => { |         it('should locate directives in variable bindings', () => { | ||||||
|           var dirA = new NormalizedDirectiveMetadata( |           var dirA = CompileDirectiveMetadata.create( | ||||||
|               {selector: '[a=b]', type: new TypeMetadata({name: 'DirA'})}); |               {selector: '[a=b]', exportAs: 'b', type: new CompileTypeMetadata({name: 'DirA'})}); | ||||||
|           var dirB = new NormalizedDirectiveMetadata( |           var dirB = CompileDirectiveMetadata.create( | ||||||
|               {selector: '[b]', type: new TypeMetadata({name: 'DirB'})}); |               {selector: '[b]', type: new CompileTypeMetadata({name: 'DirB'})}); | ||||||
|           expect(humanizeTemplateAsts(parse('<div #a="b">', [dirA, dirB]))) |           expect(humanizeTemplateAsts(parse('<div #a="b">', [dirA, dirB]))) | ||||||
|               .toEqual([ |               .toEqual([ | ||||||
|                 [ElementAst, 'div', 'TestComp > div:nth-child(0)'], |                 [ElementAst, 'div', 'TestComp > div:nth-child(0)'], | ||||||
|                 [VariableAst, 'a', 'b', 'TestComp > div:nth-child(0)[#a=b]'], |                 [DirectiveAst, dirA, 'TestComp > div:nth-child(0)'], | ||||||
|                 [DirectiveAst, dirA, 'TestComp > div:nth-child(0)'] |                 [VariableAst, 'a', 'b', 'TestComp > div:nth-child(0)[#a=b]'] | ||||||
|               ]); |               ]); | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|         it('should parse directive host properties', () => { |         it('should parse directive host properties', () => { | ||||||
|           var dirA = new NormalizedDirectiveMetadata({ |           var dirA = CompileDirectiveMetadata.create({ | ||||||
|             selector: 'div', |             selector: 'div', | ||||||
|             type: new TypeMetadata({name: 'DirA'}), |             type: new CompileTypeMetadata({name: 'DirA'}), | ||||||
|             changeDetection: new ChangeDetectionMetadata({hostProperties: {'a': 'expr'}}) |             host: {'[a]': 'expr'} | ||||||
|           }); |           }); | ||||||
|           expect(humanizeTemplateAsts(parse('<div></div>', [dirA]))) |           expect(humanizeTemplateAsts(parse('<div></div>', [dirA]))) | ||||||
|               .toEqual([ |               .toEqual([ | ||||||
| @ -431,10 +401,10 @@ export function main() { | |||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|         it('should parse directive host listeners', () => { |         it('should parse directive host listeners', () => { | ||||||
|           var dirA = new NormalizedDirectiveMetadata({ |           var dirA = CompileDirectiveMetadata.create({ | ||||||
|             selector: 'div', |             selector: 'div', | ||||||
|             type: new TypeMetadata({name: 'DirA'}), |             type: new CompileTypeMetadata({name: 'DirA'}), | ||||||
|             changeDetection: new ChangeDetectionMetadata({hostListeners: {'a': 'expr'}}) |             host: {'(a)': 'expr'} | ||||||
|           }); |           }); | ||||||
|           expect(humanizeTemplateAsts(parse('<div></div>', [dirA]))) |           expect(humanizeTemplateAsts(parse('<div></div>', [dirA]))) | ||||||
|               .toEqual([ |               .toEqual([ | ||||||
| @ -445,10 +415,10 @@ export function main() { | |||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|         it('should parse directive properties', () => { |         it('should parse directive properties', () => { | ||||||
|           var dirA = new NormalizedDirectiveMetadata({ |           var dirA = CompileDirectiveMetadata.create({ | ||||||
|             selector: 'div', |             selector: 'div', | ||||||
|             type: new TypeMetadata({name: 'DirA'}), |             type: new CompileTypeMetadata({name: 'DirA'}), | ||||||
|             changeDetection: new ChangeDetectionMetadata({properties: ['aProp']}) |             properties: ['aProp'] | ||||||
|           }); |           }); | ||||||
|           expect(humanizeTemplateAsts(parse('<div [a-prop]="expr"></div>', [dirA]))) |           expect(humanizeTemplateAsts(parse('<div [a-prop]="expr"></div>', [dirA]))) | ||||||
|               .toEqual([ |               .toEqual([ | ||||||
| @ -464,10 +434,10 @@ export function main() { | |||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|         it('should parse renamed directive properties', () => { |         it('should parse renamed directive properties', () => { | ||||||
|           var dirA = new NormalizedDirectiveMetadata({ |           var dirA = CompileDirectiveMetadata.create({ | ||||||
|             selector: 'div', |             selector: 'div', | ||||||
|             type: new TypeMetadata({name: 'DirA'}), |             type: new CompileTypeMetadata({name: 'DirA'}), | ||||||
|             changeDetection: new ChangeDetectionMetadata({properties: ['b:a']}) |             properties: ['b:a'] | ||||||
|           }); |           }); | ||||||
|           expect(humanizeTemplateAsts(parse('<div [a]="expr"></div>', [dirA]))) |           expect(humanizeTemplateAsts(parse('<div [a]="expr"></div>', [dirA]))) | ||||||
|               .toEqual([ |               .toEqual([ | ||||||
| @ -478,11 +448,8 @@ export function main() { | |||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|         it('should parse literal directive properties', () => { |         it('should parse literal directive properties', () => { | ||||||
|           var dirA = new NormalizedDirectiveMetadata({ |           var dirA = CompileDirectiveMetadata.create( | ||||||
|             selector: 'div', |               {selector: 'div', type: new CompileTypeMetadata({name: 'DirA'}), properties: ['a']}); | ||||||
|             type: new TypeMetadata({name: 'DirA'}), |  | ||||||
|             changeDetection: new ChangeDetectionMetadata({properties: ['a']}) |  | ||||||
|           }); |  | ||||||
|           expect(humanizeTemplateAsts(parse('<div a="literal"></div>', [dirA]))) |           expect(humanizeTemplateAsts(parse('<div a="literal"></div>', [dirA]))) | ||||||
|               .toEqual([ |               .toEqual([ | ||||||
|                 [ElementAst, 'div', 'TestComp > div:nth-child(0)'], |                 [ElementAst, 'div', 'TestComp > div:nth-child(0)'], | ||||||
| @ -498,11 +465,8 @@ export function main() { | |||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|         it('should support optional directive properties', () => { |         it('should support optional directive properties', () => { | ||||||
|           var dirA = new NormalizedDirectiveMetadata({ |           var dirA = CompileDirectiveMetadata.create( | ||||||
|             selector: 'div', |               {selector: 'div', type: new CompileTypeMetadata({name: 'DirA'}), properties: ['a']}); | ||||||
|             type: new TypeMetadata({name: 'DirA'}), |  | ||||||
|             changeDetection: new ChangeDetectionMetadata({properties: ['a']}) |  | ||||||
|           }); |  | ||||||
|           expect(humanizeTemplateAsts(parse('<div></div>', [dirA]))) |           expect(humanizeTemplateAsts(parse('<div></div>', [dirA]))) | ||||||
|               .toEqual([ |               .toEqual([ | ||||||
|                 [ElementAst, 'div', 'TestComp > div:nth-child(0)'], |                 [ElementAst, 'div', 'TestComp > div:nth-child(0)'], | ||||||
| @ -512,6 +476,83 @@ export function main() { | |||||||
| 
 | 
 | ||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
|  |       describe('variables', () => { | ||||||
|  | 
 | ||||||
|  |         it('should parse variables via #... and not report them as attributes', () => { | ||||||
|  |           expect(humanizeTemplateAsts(parse('<div #a>', []))) | ||||||
|  |               .toEqual([ | ||||||
|  |                 [ElementAst, 'div', 'TestComp > div:nth-child(0)'], | ||||||
|  |                 [VariableAst, 'a', '', 'TestComp > div:nth-child(0)[#a=]'] | ||||||
|  |               ]); | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         it('should parse variables via var-... and not report them as attributes', () => { | ||||||
|  |           expect(humanizeTemplateAsts(parse('<div var-a>', []))) | ||||||
|  |               .toEqual([ | ||||||
|  |                 [ElementAst, 'div', 'TestComp > div:nth-child(0)'], | ||||||
|  |                 [VariableAst, 'a', '', 'TestComp > div:nth-child(0)[var-a=]'] | ||||||
|  |               ]); | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         it('should camel case variables', () => { | ||||||
|  |           expect(humanizeTemplateAsts(parse('<div var-some-a>', []))) | ||||||
|  |               .toEqual([ | ||||||
|  |                 [ElementAst, 'div', 'TestComp > div:nth-child(0)'], | ||||||
|  |                 [VariableAst, 'someA', '', 'TestComp > div:nth-child(0)[var-some-a=]'] | ||||||
|  |               ]); | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         it('should assign variables with empty value to element', () => { | ||||||
|  |           expect(humanizeTemplateAsts(parse('<div #a></div>', []))) | ||||||
|  |               .toEqual([ | ||||||
|  |                 [ElementAst, 'div', 'TestComp > div:nth-child(0)'], | ||||||
|  |                 [VariableAst, 'a', '', 'TestComp > div:nth-child(0)[#a=]'] | ||||||
|  |               ]); | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         it('should assign variables to directives via exportAs', () => { | ||||||
|  |           var dirA = CompileDirectiveMetadata.create( | ||||||
|  |               {selector: '[a]', type: new CompileTypeMetadata({name: 'DirA'}), exportAs: 'dirA'}); | ||||||
|  |           expect(humanizeTemplateAsts(parse('<div #a="dirA"></div>', [dirA]))) | ||||||
|  |               .toEqual([ | ||||||
|  |                 [ElementAst, 'div', 'TestComp > div:nth-child(0)'], | ||||||
|  |                 [DirectiveAst, dirA, 'TestComp > div:nth-child(0)'], | ||||||
|  |                 [VariableAst, 'a', 'dirA', 'TestComp > div:nth-child(0)[#a=dirA]'] | ||||||
|  |               ]); | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         it('should report variables with values that dont match a directive as errors', () => { | ||||||
|  |           expect(() => parse('<div #a="dirA"></div>', [])).toThrowError(`Template parse errors:
 | ||||||
|  | There is no directive with "exportAs" set to "dirA" at TestComp > div:nth-child(0)[#a=dirA]`);
 | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         it('should allow variables with values that dont match a directive on embedded template elements', | ||||||
|  |            () => { | ||||||
|  |              expect(humanizeTemplateAsts(parse('<template #a="b"></template>', []))) | ||||||
|  |                  .toEqual([ | ||||||
|  |                    [EmbeddedTemplateAst, 'TestComp > template:nth-child(0)'], | ||||||
|  |                    [VariableAst, 'a', 'b', 'TestComp > template:nth-child(0)[#a=b]'] | ||||||
|  |                  ]); | ||||||
|  |            }); | ||||||
|  | 
 | ||||||
|  |         it('should assign variables with empty value to components', () => { | ||||||
|  |           var dirA = CompileDirectiveMetadata.create({ | ||||||
|  |             selector: '[a]', | ||||||
|  |             isComponent: true, | ||||||
|  |             type: new CompileTypeMetadata({name: 'DirA'}), | ||||||
|  |             exportAs: 'dirA', template: new CompileTemplateMetadata({ngContentSelectors: []}) | ||||||
|  |           }); | ||||||
|  |           expect(humanizeTemplateAsts(parse('<div #a></div>', [dirA]))) | ||||||
|  |               .toEqual([ | ||||||
|  |                 [ElementAst, 'div', 'TestComp > div:nth-child(0)'], | ||||||
|  |                 [VariableAst, 'a', '', 'TestComp > div:nth-child(0)[#a=]'], | ||||||
|  |                 [DirectiveAst, dirA, 'TestComp > div:nth-child(0)'], | ||||||
|  |                 [VariableAst, 'a', '', 'TestComp > div:nth-child(0)[#a=]'] | ||||||
|  |               ]); | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |       }); | ||||||
|  | 
 | ||||||
|       describe('explicit templates', () => { |       describe('explicit templates', () => { | ||||||
|         it('should create embedded templates for <template> elements', () => { |         it('should create embedded templates for <template> elements', () => { | ||||||
|           expect(humanizeTemplateAsts(parse('<template></template>', []))) |           expect(humanizeTemplateAsts(parse('<template></template>', []))) | ||||||
| @ -563,13 +604,13 @@ export function main() { | |||||||
| 
 | 
 | ||||||
|         describe('directives', () => { |         describe('directives', () => { | ||||||
|           it('should locate directives in property bindings', () => { |           it('should locate directives in property bindings', () => { | ||||||
|             var dirA = new NormalizedDirectiveMetadata({ |             var dirA = CompileDirectiveMetadata.create({ | ||||||
|               selector: '[a=b]', |               selector: '[a=b]', | ||||||
|               type: new TypeMetadata({name: 'DirA'}), |               type: new CompileTypeMetadata({name: 'DirA'}), | ||||||
|               changeDetection: new ChangeDetectionMetadata({properties: ['a']}) |               properties: ['a'] | ||||||
|             }); |             }); | ||||||
|             var dirB = new NormalizedDirectiveMetadata( |             var dirB = CompileDirectiveMetadata.create( | ||||||
|                 {selector: '[b]', type: new TypeMetadata({name: 'DirB'})}); |                 {selector: '[b]', type: new CompileTypeMetadata({name: 'DirB'})}); | ||||||
|             expect(humanizeTemplateAsts(parse('<div template="a b" b>', [dirA, dirB]))) |             expect(humanizeTemplateAsts(parse('<div template="a b" b>', [dirA, dirB]))) | ||||||
|                 .toEqual([ |                 .toEqual([ | ||||||
|                   [EmbeddedTemplateAst, 'TestComp > div:nth-child(0)'], |                   [EmbeddedTemplateAst, 'TestComp > div:nth-child(0)'], | ||||||
| @ -587,10 +628,10 @@ export function main() { | |||||||
|           }); |           }); | ||||||
| 
 | 
 | ||||||
|           it('should locate directives in variable bindings', () => { |           it('should locate directives in variable bindings', () => { | ||||||
|             var dirA = new NormalizedDirectiveMetadata( |             var dirA = CompileDirectiveMetadata.create( | ||||||
|                 {selector: '[a=b]', type: new TypeMetadata({name: 'DirA'})}); |                 {selector: '[a=b]', type: new CompileTypeMetadata({name: 'DirA'})}); | ||||||
|             var dirB = new NormalizedDirectiveMetadata( |             var dirB = CompileDirectiveMetadata.create( | ||||||
|                 {selector: '[b]', type: new TypeMetadata({name: 'DirB'})}); |                 {selector: '[b]', type: new CompileTypeMetadata({name: 'DirB'})}); | ||||||
|             expect(humanizeTemplateAsts(parse('<div template="#a=b" b>', [dirA, dirB]))) |             expect(humanizeTemplateAsts(parse('<div template="#a=b" b>', [dirA, dirB]))) | ||||||
|                 .toEqual([ |                 .toEqual([ | ||||||
|                   [EmbeddedTemplateAst, 'TestComp > div:nth-child(0)'], |                   [EmbeddedTemplateAst, 'TestComp > div:nth-child(0)'], | ||||||
| @ -624,12 +665,12 @@ export function main() { | |||||||
| 
 | 
 | ||||||
|     describe('content projection', () => { |     describe('content projection', () => { | ||||||
|       function createComp(selector: string, ngContentSelectors: string[]): |       function createComp(selector: string, ngContentSelectors: string[]): | ||||||
|           NormalizedDirectiveMetadata { |           CompileDirectiveMetadata { | ||||||
|         return new NormalizedDirectiveMetadata({ |         return CompileDirectiveMetadata.create({ | ||||||
|           selector: selector, |           selector: selector, | ||||||
|           isComponent: true, |           isComponent: true, | ||||||
|           type: new TypeMetadata({name: 'SomeComp'}), |           type: new CompileTypeMetadata({name: 'SomeComp'}), | ||||||
|           template: new NormalizedTemplateMetadata({ngContentSelectors: ngContentSelectors}) |           template: new CompileTemplateMetadata({ngContentSelectors: ngContentSelectors}) | ||||||
|         }) |         }) | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
| @ -725,38 +766,38 @@ Parser Error: Unexpected token 'b' at column 3 in [a b] in TestComp > div:nth-ch | |||||||
| 
 | 
 | ||||||
|       it('should not throw on invalid property names if the property is used by a directive', |       it('should not throw on invalid property names if the property is used by a directive', | ||||||
|          () => { |          () => { | ||||||
|            var dirA = new NormalizedDirectiveMetadata({ |            var dirA = CompileDirectiveMetadata.create({ | ||||||
|              selector: 'div', |              selector: 'div', | ||||||
|              type: new TypeMetadata({name: 'DirA'}), |              type: new CompileTypeMetadata({name: 'DirA'}), | ||||||
|              changeDetection: new ChangeDetectionMetadata({properties: ['invalidProp']}) |              properties: ['invalidProp'] | ||||||
|            }); |            }); | ||||||
|            expect(() => parse('<div [invalid-prop]></div>', [dirA])).not.toThrow(); |            expect(() => parse('<div [invalid-prop]></div>', [dirA])).not.toThrow(); | ||||||
|          }); |          }); | ||||||
| 
 | 
 | ||||||
|       it('should not allow more than 1 component per element', () => { |       it('should not allow more than 1 component per element', () => { | ||||||
|         var dirA = new NormalizedDirectiveMetadata({ |         var dirA = CompileDirectiveMetadata.create({ | ||||||
|           selector: 'div', |           selector: 'div', | ||||||
|           isComponent: true, |           isComponent: true, | ||||||
|           type: new TypeMetadata({name: 'DirA'}), |           type: new CompileTypeMetadata({name: 'DirA'}), | ||||||
|           template: new NormalizedTemplateMetadata({ngContentSelectors: []}) |           template: new CompileTemplateMetadata({ngContentSelectors: []}) | ||||||
|         }); |         }); | ||||||
|         var dirB = new NormalizedDirectiveMetadata({ |         var dirB = CompileDirectiveMetadata.create({ | ||||||
|           selector: 'div', |           selector: 'div', | ||||||
|           isComponent: true, |           isComponent: true, | ||||||
|           type: new TypeMetadata({name: 'DirB'}), |           type: new CompileTypeMetadata({name: 'DirB'}), | ||||||
|           template: new NormalizedTemplateMetadata({ngContentSelectors: []}) |           template: new CompileTemplateMetadata({ngContentSelectors: []}) | ||||||
|         }); |         }); | ||||||
|         expect(() => parse('<div>', [dirB, dirA])).toThrowError(`Template parse errors:
 |         expect(() => parse('<div>', [dirB, dirA])).toThrowError(`Template parse errors:
 | ||||||
| More than one component: DirA,DirB in TestComp > div:nth-child(0)`);
 | More than one component: DirB,DirA in TestComp > div:nth-child(0)`);
 | ||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
|       it('should not allow components or element nor event bindings on explicit embedded templates', |       it('should not allow components or element nor event bindings on explicit embedded templates', | ||||||
|          () => { |          () => { | ||||||
|            var dirA = new NormalizedDirectiveMetadata({ |            var dirA = CompileDirectiveMetadata.create({ | ||||||
|              selector: '[a]', |              selector: '[a]', | ||||||
|              isComponent: true, |              isComponent: true, | ||||||
|              type: new TypeMetadata({name: 'DirA'}), |              type: new CompileTypeMetadata({name: 'DirA'}), | ||||||
|              template: new NormalizedTemplateMetadata({ngContentSelectors: []}) |              template: new CompileTemplateMetadata({ngContentSelectors: []}) | ||||||
|            }); |            }); | ||||||
|            expect(() => parse('<template [a]="b" (e)="f"></template>', [dirA])) |            expect(() => parse('<template [a]="b" (e)="f"></template>', [dirA])) | ||||||
|                .toThrowError(`Template parse errors:
 |                .toThrowError(`Template parse errors:
 | ||||||
| @ -766,17 +807,45 @@ Event binding e on an embedded template in TestComp > template:nth-child(0)[(e)= | |||||||
|          }); |          }); | ||||||
| 
 | 
 | ||||||
|       it('should not allow components or element bindings on inline embedded templates', () => { |       it('should not allow components or element bindings on inline embedded templates', () => { | ||||||
|         var dirA = new NormalizedDirectiveMetadata({ |         var dirA = CompileDirectiveMetadata.create({ | ||||||
|           selector: '[a]', |           selector: '[a]', | ||||||
|           isComponent: true, |           isComponent: true, | ||||||
|           type: new TypeMetadata({name: 'DirA'}), |           type: new CompileTypeMetadata({name: 'DirA'}), | ||||||
|           template: new NormalizedTemplateMetadata({ngContentSelectors: []}) |           template: new CompileTemplateMetadata({ngContentSelectors: []}) | ||||||
|         }); |         }); | ||||||
|         expect(() => parse('<div *a="b">', [dirA])).toThrowError(`Template parse errors:
 |         expect(() => parse('<div *a="b">', [dirA])).toThrowError(`Template parse errors:
 | ||||||
| Components on an embedded template: DirA in TestComp > div:nth-child(0) | Components on an embedded template: DirA in TestComp > div:nth-child(0) | ||||||
| Property binding a not used by any directive on an embedded template in TestComp > div:nth-child(0)[*a=b]`);
 | Property binding a not used by any directive on an embedded template in TestComp > div:nth-child(0)[*a=b]`);
 | ||||||
|       }); |       }); | ||||||
|     }); |     }); | ||||||
|  | 
 | ||||||
|  |     describe('ignore elements', () => { | ||||||
|  |       it('should ignore <script> elements but include them for source info', () => { | ||||||
|  |         expect(humanizeTemplateAsts(parse('<script></script>a', []))) | ||||||
|  |             .toEqual([[TextAst, 'a', 'TestComp > #text(a):nth-child(1)']]); | ||||||
|  | 
 | ||||||
|  |       }); | ||||||
|  | 
 | ||||||
|  |       it('should ignore <style> elements but include them for source info', () => { | ||||||
|  |         expect(humanizeTemplateAsts(parse('<style></style>a', []))) | ||||||
|  |             .toEqual([[TextAst, 'a', 'TestComp > #text(a):nth-child(1)']]); | ||||||
|  | 
 | ||||||
|  |       }); | ||||||
|  | 
 | ||||||
|  |       it('should ignore <link rel="stylesheet"> elements but include them for source info', () => { | ||||||
|  |         expect(humanizeTemplateAsts(parse('<link rel="stylesheet"></link>a', []))) | ||||||
|  |             .toEqual([[TextAst, 'a', 'TestComp > #text(a):nth-child(1)']]); | ||||||
|  | 
 | ||||||
|  |       }); | ||||||
|  | 
 | ||||||
|  |       it('should ignore elements with ng-non-bindable, including their children, but include them for source info', | ||||||
|  |          () => { | ||||||
|  |            expect(humanizeTemplateAsts(parse('<div ng-non-bindable>b</div>a', []))) | ||||||
|  |                .toEqual([[TextAst, 'a', 'TestComp > #text(a):nth-child(1)']]); | ||||||
|  | 
 | ||||||
|  |          }); | ||||||
|  | 
 | ||||||
|  |     }); | ||||||
|   }); |   }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -805,7 +874,7 @@ class TemplateHumanizer implements TemplateAstVisitor { | |||||||
|     templateVisitAll(this, ast.attrs); |     templateVisitAll(this, ast.attrs); | ||||||
|     templateVisitAll(this, ast.properties); |     templateVisitAll(this, ast.properties); | ||||||
|     templateVisitAll(this, ast.events); |     templateVisitAll(this, ast.events); | ||||||
|     templateVisitAll(this, ast.vars); |     templateVisitAll(this, ast.exportAsVars); | ||||||
|     templateVisitAll(this, ast.directives); |     templateVisitAll(this, ast.directives); | ||||||
|     templateVisitAll(this, ast.children); |     templateVisitAll(this, ast.children); | ||||||
|     return null; |     return null; | ||||||
| @ -852,6 +921,7 @@ class TemplateHumanizer implements TemplateAstVisitor { | |||||||
|     templateVisitAll(this, ast.properties); |     templateVisitAll(this, ast.properties); | ||||||
|     templateVisitAll(this, ast.hostProperties); |     templateVisitAll(this, ast.hostProperties); | ||||||
|     templateVisitAll(this, ast.hostEvents); |     templateVisitAll(this, ast.hostEvents); | ||||||
|  |     templateVisitAll(this, ast.exportAsVars); | ||||||
|     return null; |     return null; | ||||||
|   } |   } | ||||||
|   visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { |   visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { | ||||||
|  | |||||||
| @ -397,23 +397,6 @@ export function main() { | |||||||
|                }); |                }); | ||||||
|          })); |          })); | ||||||
| 
 | 
 | ||||||
|       it('should use the last directive binding per directive', |  | ||||||
|          inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { |  | ||||||
|            tcb.overrideView(MyComp, new ViewMetadata({ |  | ||||||
|                               template: '<p no-duplicate></p>', |  | ||||||
|                               directives: [ |  | ||||||
|                                 bind(DuplicateDir) |  | ||||||
|                                     .toClass(DuplicateDir), |  | ||||||
|                                 bind(DuplicateDir).toClass(OtherDuplicateDir) |  | ||||||
|                               ] |  | ||||||
|                             })) |  | ||||||
|                .createAsync(MyComp) |  | ||||||
|                .then((rootTC) => { |  | ||||||
|                  expect(rootTC.debugElement.nativeElement).toHaveText('othernoduplicate'); |  | ||||||
|                  async.done(); |  | ||||||
|                }); |  | ||||||
|          })); |  | ||||||
| 
 |  | ||||||
|       it('should support directives where a selector matches property binding', |       it('should support directives where a selector matches property binding', | ||||||
|          inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { |          inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { | ||||||
|            tcb.overrideView(MyComp, new ViewMetadata( |            tcb.overrideView(MyComp, new ViewMetadata( | ||||||
| @ -436,27 +419,11 @@ export function main() { | |||||||
|                }); |                }); | ||||||
|          })); |          })); | ||||||
| 
 | 
 | ||||||
|       it('should allow specifying directives as bindings', |  | ||||||
|          inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { |  | ||||||
|            tcb.overrideView(MyComp, new ViewMetadata({ |  | ||||||
|                               template: '<child-cmp></child-cmp>', |  | ||||||
|                               directives: [bind(ChildComp).toClass(ChildComp)] |  | ||||||
|                             })) |  | ||||||
| 
 |  | ||||||
|                .createAsync(MyComp) |  | ||||||
|                .then((rootTC) => { |  | ||||||
|                  rootTC.detectChanges(); |  | ||||||
| 
 |  | ||||||
|                  expect(rootTC.debugElement.nativeElement).toHaveText('hello'); |  | ||||||
|                  async.done(); |  | ||||||
|                }); |  | ||||||
|          })); |  | ||||||
| 
 |  | ||||||
|       it('should read directives metadata from their binding token', |       it('should read directives metadata from their binding token', | ||||||
|          inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { |          inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { | ||||||
|            tcb.overrideView(MyComp, new ViewMetadata({ |            tcb.overrideView(MyComp, new ViewMetadata({ | ||||||
|                               template: '<div public-api><div needs-public-api></div></div>', |                               template: '<div public-api><div needs-public-api></div></div>', | ||||||
|                               directives: [bind(PublicApi).toClass(PrivateImpl), NeedsPublicApi] |                               directives: [PrivateImpl, NeedsPublicApi] | ||||||
|                             })) |                             })) | ||||||
| 
 | 
 | ||||||
|                .createAsync(MyComp) |                .createAsync(MyComp) | ||||||
| @ -2043,12 +2010,14 @@ class NeedsAttribute { | |||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @Directive({selector: '[public-api]'}) |  | ||||||
| @Injectable() | @Injectable() | ||||||
| class PublicApi { | class PublicApi { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @Directive({selector: '[private-impl]'}) | @Directive({ | ||||||
|  |   selector: '[public-api]', | ||||||
|  |   bindings: [new Binding(PublicApi, {toAlias: PrivateImpl, deps: []})] | ||||||
|  | }) | ||||||
| @Injectable() | @Injectable() | ||||||
| class PrivateImpl extends PublicApi { | class PrivateImpl extends PublicApi { | ||||||
| } | } | ||||||
|  | |||||||
| @ -42,11 +42,7 @@ export function main() { | |||||||
| @Component({selector: 'app', viewBindings: [forwardRef(() => Frame)]}) | @Component({selector: 'app', viewBindings: [forwardRef(() => Frame)]}) | ||||||
| @View({ | @View({ | ||||||
|   template: `<door><lock></lock></door>`, |   template: `<door><lock></lock></door>`, | ||||||
|   directives: [ |   directives: [forwardRef(() => Door), forwardRef(() => Lock)] | ||||||
|     bind(forwardRef(() => Door)) |  | ||||||
|         .toClass(forwardRef(() => Door)), |  | ||||||
|     bind(forwardRef(() => Lock)).toClass(forwardRef(() => Lock)) |  | ||||||
|   ] |  | ||||||
| }) | }) | ||||||
| class App { | class App { | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user