@ -63,7 +63,7 @@ export class ChangeDetectionCompiler {
|
||||
var index = 0;
|
||||
var sourceParts = changeDetectorDefinitions.map(definition => {
|
||||
var codegen: any;
|
||||
var sourcePart;
|
||||
var sourcePart: string;
|
||||
// TODO(tbosch): move the 2 code generators to the same place, one with .dart and one with .ts
|
||||
// suffix
|
||||
// and have the same API for calling them!
|
||||
@ -74,7 +74,7 @@ export class ChangeDetectionCompiler {
|
||||
'dynamic' :
|
||||
`${moduleRef(componentType.moduleUrl)}${componentType.name}`;
|
||||
codegen.generate(typeRef, className, definition);
|
||||
factories.push(`(dispatcher) => new ${className}(dispatcher)`);
|
||||
factories.push(`${className}.newChangeDetector`);
|
||||
sourcePart = codegen.toString();
|
||||
} else {
|
||||
codegen = new ChangeDetectorJITGenerator(
|
||||
|
@ -1,15 +1,15 @@
|
||||
import {isPresent, isBlank, Type, isString, StringWrapper} from 'angular2/src/facade/lang';
|
||||
import {isPresent, isBlank, Type, isString, StringWrapper, IS_DART} from 'angular2/src/facade/lang';
|
||||
import {SetWrapper, StringMapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {
|
||||
TemplateCmd,
|
||||
text,
|
||||
ngContent,
|
||||
beginElement,
|
||||
endElement,
|
||||
beginComponent,
|
||||
endComponent,
|
||||
embeddedTemplate,
|
||||
CompiledTemplate
|
||||
TextCmd,
|
||||
NgContentCmd,
|
||||
BeginElementCmd,
|
||||
EndElementCmd,
|
||||
BeginComponentCmd,
|
||||
EndComponentCmd,
|
||||
EmbeddedTemplateCmd,
|
||||
CompiledComponentTemplate
|
||||
} from 'angular2/src/core/linker/template_commands';
|
||||
import {
|
||||
TemplateAst,
|
||||
@ -32,12 +32,11 @@ import {SourceExpressions, SourceExpression, moduleRef} from './source_module';
|
||||
|
||||
import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
|
||||
import {
|
||||
shimHostAttribute,
|
||||
shimContentAttribute,
|
||||
shimContentAttributeExpr,
|
||||
shimHostAttributeExpr
|
||||
} from './style_compiler';
|
||||
import {escapeSingleQuoteString, MODULE_SUFFIX} from './util';
|
||||
escapeSingleQuoteString,
|
||||
codeGenConstConstructorCall,
|
||||
codeGenValueFn,
|
||||
MODULE_SUFFIX
|
||||
} from './util';
|
||||
import {Injectable} from 'angular2/src/core/di';
|
||||
|
||||
export var TEMPLATE_COMMANDS_MODULE_REF =
|
||||
@ -49,28 +48,24 @@ const STYLE_ATTR = 'style';
|
||||
|
||||
@Injectable()
|
||||
export class CommandCompiler {
|
||||
compileComponentRuntime(component: CompileDirectiveMetadata, appId: string, templateId: number,
|
||||
template: TemplateAst[], changeDetectorFactories: Function[],
|
||||
compileComponentRuntime(component: CompileDirectiveMetadata, template: TemplateAst[],
|
||||
changeDetectorFactories: Function[],
|
||||
componentTemplateFactory: Function): TemplateCmd[] {
|
||||
var visitor = new CommandBuilderVisitor(
|
||||
new RuntimeCommandFactory(component, appId, templateId, componentTemplateFactory,
|
||||
changeDetectorFactories),
|
||||
0);
|
||||
new RuntimeCommandFactory(component, componentTemplateFactory, changeDetectorFactories), 0);
|
||||
templateVisitAll(visitor, template);
|
||||
return visitor.result;
|
||||
}
|
||||
|
||||
compileComponentCodeGen(component: CompileDirectiveMetadata, appIdExpr: string,
|
||||
templateIdExpr: string, template: TemplateAst[],
|
||||
compileComponentCodeGen(component: CompileDirectiveMetadata, template: TemplateAst[],
|
||||
changeDetectorFactoryExpressions: string[],
|
||||
componentTemplateFactory: Function): SourceExpression {
|
||||
var visitor = new CommandBuilderVisitor(
|
||||
new CodegenCommandFactory(component, appIdExpr, templateIdExpr, componentTemplateFactory,
|
||||
changeDetectorFactoryExpressions),
|
||||
0);
|
||||
var visitor =
|
||||
new CommandBuilderVisitor(new CodegenCommandFactory(component, componentTemplateFactory,
|
||||
changeDetectorFactoryExpressions),
|
||||
0);
|
||||
templateVisitAll(visitor, template);
|
||||
var source = `[${visitor.result.join(',')}]`;
|
||||
return new SourceExpression([], source);
|
||||
return new SourceExpression([], codeGenArray(visitor.result));
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,7 +78,7 @@ interface CommandFactory<R> {
|
||||
createEndElement(): R;
|
||||
createBeginComponent(name: string, attrNameAndValues: string[], eventTargetAndNames: string[],
|
||||
variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
|
||||
nativeShadow: boolean, ngContentIndex: number): R;
|
||||
encapsulation: ViewEncapsulation, ngContentIndex: number): R;
|
||||
createEndComponent(): R;
|
||||
createEmbeddedTemplate(embeddedTemplateIndex: number, attrNameAndValues: string[],
|
||||
variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
|
||||
@ -91,112 +86,89 @@ interface CommandFactory<R> {
|
||||
}
|
||||
|
||||
class RuntimeCommandFactory implements CommandFactory<TemplateCmd> {
|
||||
constructor(private component: CompileDirectiveMetadata, private appId: string,
|
||||
private templateId: number, private componentTemplateFactory: Function,
|
||||
constructor(private component: CompileDirectiveMetadata,
|
||||
private componentTemplateFactory: Function,
|
||||
private changeDetectorFactories: Function[]) {}
|
||||
private _mapDirectives(directives: CompileDirectiveMetadata[]): Type[] {
|
||||
return directives.map(directive => directive.type.runtime);
|
||||
}
|
||||
private _addStyleShimAttributes(attrNameAndValues: string[],
|
||||
localComponent: CompileDirectiveMetadata,
|
||||
localTemplateId: number): string[] {
|
||||
var additionalStyles = [];
|
||||
if (isPresent(localComponent) &&
|
||||
localComponent.template.encapsulation === ViewEncapsulation.Emulated) {
|
||||
additionalStyles.push(shimHostAttribute(this.appId, localTemplateId));
|
||||
additionalStyles.push('');
|
||||
}
|
||||
if (this.component.template.encapsulation === ViewEncapsulation.Emulated) {
|
||||
additionalStyles.push(shimContentAttribute(this.appId, this.templateId));
|
||||
additionalStyles.push('');
|
||||
}
|
||||
return additionalStyles.concat(attrNameAndValues);
|
||||
}
|
||||
|
||||
createText(value: string, isBound: boolean, ngContentIndex: number): TemplateCmd {
|
||||
return text(value, isBound, ngContentIndex);
|
||||
return new TextCmd(value, isBound, ngContentIndex);
|
||||
}
|
||||
createNgContent(index: number, ngContentIndex: number): TemplateCmd {
|
||||
return ngContent(index, ngContentIndex);
|
||||
return new NgContentCmd(index, ngContentIndex);
|
||||
}
|
||||
createBeginElement(name: string, attrNameAndValues: string[], eventTargetAndNames: string[],
|
||||
variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
|
||||
isBound: boolean, ngContentIndex: number): TemplateCmd {
|
||||
return beginElement(name, this._addStyleShimAttributes(attrNameAndValues, null, null),
|
||||
eventTargetAndNames, variableNameAndValues, this._mapDirectives(directives),
|
||||
isBound, ngContentIndex);
|
||||
return new BeginElementCmd(name, attrNameAndValues, eventTargetAndNames, variableNameAndValues,
|
||||
this._mapDirectives(directives), isBound, ngContentIndex);
|
||||
}
|
||||
createEndElement(): TemplateCmd { return endElement(); }
|
||||
createEndElement(): TemplateCmd { return new EndElementCmd(); }
|
||||
createBeginComponent(name: string, attrNameAndValues: string[], eventTargetAndNames: string[],
|
||||
variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
|
||||
nativeShadow: boolean, ngContentIndex: number): TemplateCmd {
|
||||
var nestedTemplate = this.componentTemplateFactory(directives[0]);
|
||||
return beginComponent(
|
||||
name, this._addStyleShimAttributes(attrNameAndValues, directives[0], nestedTemplate.id),
|
||||
eventTargetAndNames, variableNameAndValues, this._mapDirectives(directives), nativeShadow,
|
||||
ngContentIndex, nestedTemplate);
|
||||
encapsulation: ViewEncapsulation, ngContentIndex: number): TemplateCmd {
|
||||
var nestedTemplateAccessor = this.componentTemplateFactory(directives[0]);
|
||||
return new BeginComponentCmd(name, attrNameAndValues, eventTargetAndNames,
|
||||
variableNameAndValues, this._mapDirectives(directives),
|
||||
encapsulation, ngContentIndex, nestedTemplateAccessor);
|
||||
}
|
||||
createEndComponent(): TemplateCmd { return endComponent(); }
|
||||
createEndComponent(): TemplateCmd { return new EndComponentCmd(); }
|
||||
createEmbeddedTemplate(embeddedTemplateIndex: number, attrNameAndValues: string[],
|
||||
variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
|
||||
isMerged: boolean, ngContentIndex: number,
|
||||
children: TemplateCmd[]): TemplateCmd {
|
||||
return embeddedTemplate(attrNameAndValues, variableNameAndValues,
|
||||
this._mapDirectives(directives), isMerged, ngContentIndex,
|
||||
this.changeDetectorFactories[embeddedTemplateIndex], children);
|
||||
return new EmbeddedTemplateCmd(attrNameAndValues, variableNameAndValues,
|
||||
this._mapDirectives(directives), isMerged, ngContentIndex,
|
||||
this.changeDetectorFactories[embeddedTemplateIndex], children);
|
||||
}
|
||||
}
|
||||
|
||||
class CodegenCommandFactory implements CommandFactory<string> {
|
||||
constructor(private component: CompileDirectiveMetadata, private appIdExpr: string,
|
||||
private templateIdExpr: string, private componentTemplateFactory: Function,
|
||||
class CodegenCommandFactory implements CommandFactory<Expression> {
|
||||
constructor(private component: CompileDirectiveMetadata,
|
||||
private componentTemplateFactory: Function,
|
||||
private changeDetectorFactoryExpressions: string[]) {}
|
||||
|
||||
private _addStyleShimAttributes(attrNameAndValues: string[],
|
||||
localComponent: CompileDirectiveMetadata,
|
||||
localTemplateIdExpr: string): any[] {
|
||||
var additionalStlyes = [];
|
||||
if (isPresent(localComponent) &&
|
||||
localComponent.template.encapsulation === ViewEncapsulation.Emulated) {
|
||||
additionalStlyes.push(
|
||||
new Expression(shimHostAttributeExpr(this.appIdExpr, localTemplateIdExpr)));
|
||||
additionalStlyes.push('');
|
||||
}
|
||||
if (this.component.template.encapsulation === ViewEncapsulation.Emulated) {
|
||||
additionalStlyes.push(
|
||||
new Expression(shimContentAttributeExpr(this.appIdExpr, this.templateIdExpr)));
|
||||
additionalStlyes.push('');
|
||||
}
|
||||
return additionalStlyes.concat(attrNameAndValues);
|
||||
createText(value: string, isBound: boolean, ngContentIndex: number): Expression {
|
||||
return new Expression(
|
||||
`${codeGenConstConstructorCall(TEMPLATE_COMMANDS_MODULE_REF+'TextCmd')}(${escapeSingleQuoteString(value)}, ${isBound}, ${ngContentIndex})`);
|
||||
}
|
||||
|
||||
createText(value: string, isBound: boolean, ngContentIndex: number): string {
|
||||
return `${TEMPLATE_COMMANDS_MODULE_REF}text(${escapeSingleQuoteString(value)}, ${isBound}, ${ngContentIndex})`;
|
||||
}
|
||||
createNgContent(index: number, ngContentIndex: number): string {
|
||||
return `${TEMPLATE_COMMANDS_MODULE_REF}ngContent(${index}, ${ngContentIndex})`;
|
||||
createNgContent(index: number, ngContentIndex: number): Expression {
|
||||
return new Expression(
|
||||
`${codeGenConstConstructorCall(TEMPLATE_COMMANDS_MODULE_REF+'NgContentCmd')}(${index}, ${ngContentIndex})`);
|
||||
}
|
||||
createBeginElement(name: string, attrNameAndValues: string[], eventTargetAndNames: string[],
|
||||
variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
|
||||
isBound: boolean, ngContentIndex: number): string {
|
||||
var attrsExpression = codeGenArray(this._addStyleShimAttributes(attrNameAndValues, null, null));
|
||||
return `${TEMPLATE_COMMANDS_MODULE_REF}beginElement(${escapeSingleQuoteString(name)}, ${attrsExpression}, ${codeGenArray(eventTargetAndNames)}, ${codeGenArray(variableNameAndValues)}, ${codeGenDirectivesArray(directives)}, ${isBound}, ${ngContentIndex})`;
|
||||
isBound: boolean, ngContentIndex: number): Expression {
|
||||
var attrsExpression = codeGenArray(attrNameAndValues);
|
||||
return new Expression(
|
||||
`${codeGenConstConstructorCall(TEMPLATE_COMMANDS_MODULE_REF+'BeginElementCmd')}(${escapeSingleQuoteString(name)}, ${attrsExpression}, ` +
|
||||
`${codeGenArray(eventTargetAndNames)}, ${codeGenArray(variableNameAndValues)}, ${codeGenDirectivesArray(directives)}, ${isBound}, ${ngContentIndex})`);
|
||||
}
|
||||
createEndElement(): Expression {
|
||||
return new Expression(
|
||||
`${codeGenConstConstructorCall(TEMPLATE_COMMANDS_MODULE_REF+'EndElementCmd')}()`);
|
||||
}
|
||||
createEndElement(): string { return `${TEMPLATE_COMMANDS_MODULE_REF}endElement()`; }
|
||||
createBeginComponent(name: string, attrNameAndValues: string[], eventTargetAndNames: string[],
|
||||
variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
|
||||
nativeShadow: boolean, ngContentIndex: number): string {
|
||||
var nestedCompExpr = this.componentTemplateFactory(directives[0]);
|
||||
var attrsExpression = codeGenArray(
|
||||
this._addStyleShimAttributes(attrNameAndValues, directives[0], `${nestedCompExpr}.id`));
|
||||
return `${TEMPLATE_COMMANDS_MODULE_REF}beginComponent(${escapeSingleQuoteString(name)}, ${attrsExpression}, ${codeGenArray(eventTargetAndNames)}, ${codeGenArray(variableNameAndValues)}, ${codeGenDirectivesArray(directives)}, ${nativeShadow}, ${ngContentIndex}, ${nestedCompExpr})`;
|
||||
encapsulation: ViewEncapsulation, ngContentIndex: number): Expression {
|
||||
var attrsExpression = codeGenArray(attrNameAndValues);
|
||||
return new Expression(
|
||||
`${codeGenConstConstructorCall(TEMPLATE_COMMANDS_MODULE_REF+'BeginComponentCmd')}(${escapeSingleQuoteString(name)}, ${attrsExpression}, ` +
|
||||
`${codeGenArray(eventTargetAndNames)}, ${codeGenArray(variableNameAndValues)}, ${codeGenDirectivesArray(directives)}, ${codeGenViewEncapsulation(encapsulation)}, ${ngContentIndex}, ${this.componentTemplateFactory(directives[0])})`);
|
||||
}
|
||||
createEndComponent(): Expression {
|
||||
return new Expression(
|
||||
`${codeGenConstConstructorCall(TEMPLATE_COMMANDS_MODULE_REF+'EndComponentCmd')}()`);
|
||||
}
|
||||
createEndComponent(): string { return `${TEMPLATE_COMMANDS_MODULE_REF}endComponent()`; }
|
||||
createEmbeddedTemplate(embeddedTemplateIndex: number, attrNameAndValues: string[],
|
||||
variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
|
||||
isMerged: boolean, ngContentIndex: number, children: string[]): string {
|
||||
return `${TEMPLATE_COMMANDS_MODULE_REF}embeddedTemplate(${codeGenArray(attrNameAndValues)}, ${codeGenArray(variableNameAndValues)}, ` +
|
||||
`${codeGenDirectivesArray(directives)}, ${isMerged}, ${ngContentIndex}, ${this.changeDetectorFactoryExpressions[embeddedTemplateIndex]}, [${children.join(',')}])`;
|
||||
isMerged: boolean, ngContentIndex: number,
|
||||
children: Expression[]): Expression {
|
||||
return new Expression(
|
||||
`${codeGenConstConstructorCall(TEMPLATE_COMMANDS_MODULE_REF+'EmbeddedTemplateCmd')}(${codeGenArray(attrNameAndValues)}, ${codeGenArray(variableNameAndValues)}, ` +
|
||||
`${codeGenDirectivesArray(directives)}, ${isMerged}, ${ngContentIndex}, ${this.changeDetectorFactoryExpressions[embeddedTemplateIndex]}, ${codeGenArray(children)})`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -270,7 +242,7 @@ class CommandBuilderVisitor<R> implements TemplateAstVisitor {
|
||||
if (isPresent(component)) {
|
||||
this.result.push(this.commandFactory.createBeginComponent(
|
||||
ast.name, attrNameAndValues, eventTargetAndNames, variableNameAndValues, directives,
|
||||
component.template.encapsulation === ViewEncapsulation.Native, ast.ngContentIndex));
|
||||
component.template.encapsulation, ast.ngContentIndex));
|
||||
templateVisitAll(this, ast.children);
|
||||
this.result.push(this.commandFactory.createEndComponent());
|
||||
} else {
|
||||
@ -383,11 +355,21 @@ function escapeValue(value: any): string {
|
||||
}
|
||||
|
||||
function codeGenArray(data: any[]): string {
|
||||
return `[${data.map(escapeValue).join(',')}]`;
|
||||
var base = `[${data.map(escapeValue).join(',')}]`;
|
||||
return IS_DART ? `const ${base}` : base;
|
||||
}
|
||||
|
||||
function codeGenDirectivesArray(directives: CompileDirectiveMetadata[]): string {
|
||||
var expressions = directives.map(
|
||||
directiveType => `${moduleRef(directiveType.type.moduleUrl)}${directiveType.type.name}`);
|
||||
return `[${expressions.join(',')}]`;
|
||||
var base = `[${expressions.join(',')}]`;
|
||||
return IS_DART ? `const ${base}` : base;
|
||||
}
|
||||
|
||||
function codeGenViewEncapsulation(value: ViewEncapsulation): string {
|
||||
if (IS_DART) {
|
||||
return `${TEMPLATE_COMMANDS_MODULE_REF}${value}`;
|
||||
} else {
|
||||
return `${value}`;
|
||||
}
|
||||
}
|
||||
|
@ -9,21 +9,12 @@ import {UrlResolver} from 'angular2/src/compiler/url_resolver';
|
||||
import {extractStyleUrls} from './style_url_resolver';
|
||||
import {
|
||||
escapeSingleQuoteString,
|
||||
codeGenConcatArray,
|
||||
codeGenMapArray,
|
||||
codeGenReplaceAll,
|
||||
codeGenExportVariable,
|
||||
codeGenToString,
|
||||
MODULE_SUFFIX
|
||||
} from './util';
|
||||
import {Injectable} from 'angular2/src/core/di';
|
||||
|
||||
const COMPONENT_VARIABLE = '%COMP%';
|
||||
var COMPONENT_REGEX = /%COMP%/g;
|
||||
const HOST_ATTR = `_nghost-${COMPONENT_VARIABLE}`;
|
||||
const HOST_ATTR_EXPR = `'_nghost-'+${COMPONENT_VARIABLE}`;
|
||||
const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`;
|
||||
const CONTENT_ATTR_EXPR = `'_ngcontent-'+${COMPONENT_VARIABLE}`;
|
||||
import {COMPONENT_VARIABLE, HOST_ATTR, CONTENT_ATTR} from 'angular2/src/core/render/view_factory';
|
||||
|
||||
@Injectable()
|
||||
export class StyleCompiler {
|
||||
@ -32,28 +23,16 @@ export class StyleCompiler {
|
||||
|
||||
constructor(private _xhr: XHR, private _urlResolver: UrlResolver) {}
|
||||
|
||||
compileComponentRuntime(appId: string, templateId: number,
|
||||
template: CompileTemplateMetadata): Promise<string[]> {
|
||||
compileComponentRuntime(template: CompileTemplateMetadata): Promise<Array<string | any[]>> {
|
||||
var styles = template.styles;
|
||||
var styleAbsUrls = template.styleUrls;
|
||||
return this._loadStyles(styles, styleAbsUrls,
|
||||
template.encapsulation === ViewEncapsulation.Emulated)
|
||||
.then(styles => styles.map(style => StringWrapper.replaceAll(
|
||||
style, COMPONENT_REGEX, componentId(appId, templateId))));
|
||||
template.encapsulation === ViewEncapsulation.Emulated);
|
||||
}
|
||||
|
||||
compileComponentCodeGen(appIdExpression: string, templateIdExpression: string,
|
||||
template: CompileTemplateMetadata): SourceExpression {
|
||||
compileComponentCodeGen(template: CompileTemplateMetadata): SourceExpression {
|
||||
var shim = template.encapsulation === ViewEncapsulation.Emulated;
|
||||
var suffix;
|
||||
if (shim) {
|
||||
suffix = codeGenMapArray(
|
||||
['style'],
|
||||
`style${codeGenReplaceAll(COMPONENT_VARIABLE, componentIdExpression(appIdExpression, templateIdExpression))}`);
|
||||
} else {
|
||||
suffix = '';
|
||||
}
|
||||
return this._styleCodeGen(template.styles, template.styleUrls, shim, suffix);
|
||||
return this._styleCodeGen(template.styles, template.styleUrls, shim);
|
||||
}
|
||||
|
||||
compileStylesheetCodeGen(stylesheetUrl: string, cssText: string): SourceModule[] {
|
||||
@ -61,17 +40,16 @@ export class StyleCompiler {
|
||||
return [
|
||||
this._styleModule(
|
||||
stylesheetUrl, false,
|
||||
this._styleCodeGen([styleWithImports.style], styleWithImports.styleUrls, false, '')),
|
||||
this._styleModule(
|
||||
stylesheetUrl, true,
|
||||
this._styleCodeGen([styleWithImports.style], styleWithImports.styleUrls, true, ''))
|
||||
this._styleCodeGen([styleWithImports.style], styleWithImports.styleUrls, false)),
|
||||
this._styleModule(stylesheetUrl, true, this._styleCodeGen([styleWithImports.style],
|
||||
styleWithImports.styleUrls, true))
|
||||
];
|
||||
}
|
||||
|
||||
clearCache() { this._styleCache.clear(); }
|
||||
|
||||
private _loadStyles(plainStyles: string[], absUrls: string[],
|
||||
encapsulate: boolean): Promise<string[]> {
|
||||
encapsulate: boolean): Promise<Array<string | any[]>> {
|
||||
var promises = absUrls.map((absUrl) => {
|
||||
var cacheKey = `${absUrl}${encapsulate ? '.shim' : ''}`;
|
||||
var result = this._styleCache.get(cacheKey);
|
||||
@ -86,22 +64,23 @@ export class StyleCompiler {
|
||||
return result;
|
||||
});
|
||||
return PromiseWrapper.all(promises).then((nestedStyles: string[][]) => {
|
||||
var result = plainStyles.map(plainStyle => this._shimIfNeeded(plainStyle, encapsulate));
|
||||
nestedStyles.forEach(styles => styles.forEach(style => result.push(style)));
|
||||
var result: Array<string | any[]> =
|
||||
plainStyles.map(plainStyle => this._shimIfNeeded(plainStyle, encapsulate));
|
||||
nestedStyles.forEach(styles => result.push(styles));
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
private _styleCodeGen(plainStyles: string[], absUrls: string[], shim: boolean,
|
||||
suffix: string): SourceExpression {
|
||||
var expressionSource = `(`;
|
||||
expressionSource +=
|
||||
`[${plainStyles.map( plainStyle => escapeSingleQuoteString(this._shimIfNeeded(plainStyle, shim)) ).join(',')}]`;
|
||||
private _styleCodeGen(plainStyles: string[], absUrls: string[], shim: boolean): SourceExpression {
|
||||
var arrayPrefix = IS_DART ? `const` : '';
|
||||
var styleExpressions = plainStyles.map(
|
||||
plainStyle => escapeSingleQuoteString(this._shimIfNeeded(plainStyle, shim)));
|
||||
|
||||
for (var i = 0; i < absUrls.length; i++) {
|
||||
var moduleUrl = this._createModuleUrl(absUrls[i], shim);
|
||||
expressionSource += codeGenConcatArray(`${moduleRef(moduleUrl)}STYLES`);
|
||||
styleExpressions.push(`${moduleRef(moduleUrl)}STYLES`);
|
||||
}
|
||||
expressionSource += `)${suffix}`;
|
||||
var expressionSource = `${arrayPrefix} [${styleExpressions.join(',')}]`;
|
||||
return new SourceExpression([], expressionSource);
|
||||
}
|
||||
|
||||
@ -122,29 +101,3 @@ export class StyleCompiler {
|
||||
return shim ? `${stylesheetUrl}.shim${MODULE_SUFFIX}` : `${stylesheetUrl}${MODULE_SUFFIX}`;
|
||||
}
|
||||
}
|
||||
|
||||
export function shimContentAttribute(appId: string, templateId: number): string {
|
||||
return StringWrapper.replaceAll(CONTENT_ATTR, COMPONENT_REGEX, componentId(appId, templateId));
|
||||
}
|
||||
|
||||
export function shimContentAttributeExpr(appIdExpr: string, templateIdExpr: string): string {
|
||||
return StringWrapper.replaceAll(CONTENT_ATTR_EXPR, COMPONENT_REGEX,
|
||||
componentIdExpression(appIdExpr, templateIdExpr));
|
||||
}
|
||||
|
||||
export function shimHostAttribute(appId: string, templateId: number): string {
|
||||
return StringWrapper.replaceAll(HOST_ATTR, COMPONENT_REGEX, componentId(appId, templateId));
|
||||
}
|
||||
|
||||
export function shimHostAttributeExpr(appIdExpr: string, templateIdExpr: string): string {
|
||||
return StringWrapper.replaceAll(HOST_ATTR_EXPR, COMPONENT_REGEX,
|
||||
componentIdExpression(appIdExpr, templateIdExpr));
|
||||
}
|
||||
|
||||
function componentId(appId: string, templateId: number): string {
|
||||
return `${appId}-${templateId}`;
|
||||
}
|
||||
|
||||
function componentIdExpression(appIdExpression: string, templateIdExpression: string): string {
|
||||
return `${appIdExpression}+'-'+${codeGenToString(templateIdExpression)}`;
|
||||
}
|
||||
|
@ -3,10 +3,10 @@ import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
import {ListWrapper, SetWrapper} from 'angular2/src/facade/collection';
|
||||
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
||||
import {
|
||||
CompiledTemplate,
|
||||
CompiledComponentTemplate,
|
||||
TemplateCmd,
|
||||
nextTemplateId,
|
||||
CompiledHostTemplate
|
||||
CompiledHostTemplate,
|
||||
BeginComponentCmd
|
||||
} from 'angular2/src/core/linker/template_commands';
|
||||
import {
|
||||
createHostComponentMeta,
|
||||
@ -23,7 +23,6 @@ import {CommandCompiler} from './command_compiler';
|
||||
import {TemplateParser} from './template_parser';
|
||||
import {TemplateNormalizer} from './template_normalizer';
|
||||
import {RuntimeMetadataResolver} from './runtime_metadata';
|
||||
import {APP_ID} from 'angular2/src/core/application_tokens';
|
||||
|
||||
import {TEMPLATE_COMMANDS_MODULE_REF} from './command_compiler';
|
||||
import {
|
||||
@ -32,22 +31,19 @@ import {
|
||||
codeGenValueFn,
|
||||
MODULE_SUFFIX
|
||||
} from './util';
|
||||
import {Inject} from 'angular2/src/core/di';
|
||||
|
||||
@Injectable()
|
||||
export class TemplateCompiler {
|
||||
private _hostCacheKeys = new Map<Type, any>();
|
||||
private _compiledTemplateCache = new Map<any, CompiledTemplate>();
|
||||
private _compiledTemplateDone = new Map<any, Promise<CompiledTemplate>>();
|
||||
private _appId: string;
|
||||
private _compiledTemplateCache = new Map<any, CompiledComponentTemplate>();
|
||||
private _compiledTemplateDone = new Map<any, Promise<CompiledComponentTemplate>>();
|
||||
private _nextTemplateId: number = 0;
|
||||
|
||||
constructor(private _runtimeMetadataResolver: RuntimeMetadataResolver,
|
||||
private _templateNormalizer: TemplateNormalizer,
|
||||
private _templateParser: TemplateParser, private _styleCompiler: StyleCompiler,
|
||||
private _commandCompiler: CommandCompiler,
|
||||
private _cdCompiler: ChangeDetectionCompiler, @Inject(APP_ID) appId: string) {
|
||||
this._appId = appId;
|
||||
}
|
||||
private _cdCompiler: ChangeDetectionCompiler) {}
|
||||
|
||||
normalizeDirectiveMetadata(directive: CompileDirectiveMetadata):
|
||||
Promise<CompileDirectiveMetadata> {
|
||||
@ -87,7 +83,7 @@ export class TemplateCompiler {
|
||||
this._compileComponentRuntime(hostCacheKey, hostMeta, [compMeta], new Set());
|
||||
}
|
||||
return this._compiledTemplateDone.get(hostCacheKey)
|
||||
.then(compiledTemplate => new CompiledHostTemplate(() => compiledTemplate));
|
||||
.then(compiledTemplate => new CompiledHostTemplate(compiledTemplate));
|
||||
}
|
||||
|
||||
clearCache() {
|
||||
@ -97,57 +93,55 @@ export class TemplateCompiler {
|
||||
this._compiledTemplateDone.clear();
|
||||
}
|
||||
|
||||
private _compileComponentRuntime(cacheKey: any, compMeta: CompileDirectiveMetadata,
|
||||
viewDirectives: CompileDirectiveMetadata[],
|
||||
compilingComponentCacheKeys: Set<any>): CompiledTemplate {
|
||||
private _compileComponentRuntime(
|
||||
cacheKey: any, compMeta: CompileDirectiveMetadata, viewDirectives: CompileDirectiveMetadata[],
|
||||
compilingComponentCacheKeys: Set<any>): CompiledComponentTemplate {
|
||||
var compiledTemplate = this._compiledTemplateCache.get(cacheKey);
|
||||
var done = this._compiledTemplateDone.get(cacheKey);
|
||||
if (isBlank(compiledTemplate)) {
|
||||
var styles;
|
||||
var styles = [];
|
||||
var changeDetectorFactory;
|
||||
var commands;
|
||||
var templateId = nextTemplateId();
|
||||
compiledTemplate =
|
||||
new CompiledTemplate(templateId, (_a, _b) => [changeDetectorFactory, commands, styles]);
|
||||
var commands = [];
|
||||
var templateId = `${stringify(compMeta.type.runtime)}Template${this._nextTemplateId++}`;
|
||||
compiledTemplate = new CompiledComponentTemplate(
|
||||
templateId, (dispatcher) => changeDetectorFactory(dispatcher), commands, styles);
|
||||
this._compiledTemplateCache.set(cacheKey, compiledTemplate);
|
||||
compilingComponentCacheKeys.add(cacheKey);
|
||||
done =
|
||||
PromiseWrapper.all([
|
||||
<any>this._styleCompiler.compileComponentRuntime(this._appId, templateId,
|
||||
compMeta.template)
|
||||
].concat(viewDirectives.map(dirMeta =>
|
||||
this.normalizeDirectiveMetadata(dirMeta))))
|
||||
.then((stylesAndNormalizedViewDirMetas: any[]) => {
|
||||
var childPromises = [];
|
||||
var normalizedViewDirMetas = stylesAndNormalizedViewDirMetas.slice(1);
|
||||
var parsedTemplate = this._templateParser.parse(
|
||||
compMeta.template.template, normalizedViewDirMetas, compMeta.type.name);
|
||||
done = PromiseWrapper
|
||||
.all([<any>this._styleCompiler.compileComponentRuntime(compMeta.template)].concat(
|
||||
viewDirectives.map(dirMeta => this.normalizeDirectiveMetadata(dirMeta))))
|
||||
.then((stylesAndNormalizedViewDirMetas: any[]) => {
|
||||
var childPromises = [];
|
||||
var normalizedViewDirMetas = stylesAndNormalizedViewDirMetas.slice(1);
|
||||
var parsedTemplate = this._templateParser.parse(
|
||||
compMeta.template.template, normalizedViewDirMetas, compMeta.type.name);
|
||||
|
||||
var changeDetectorFactories = this._cdCompiler.compileComponentRuntime(
|
||||
compMeta.type, compMeta.changeDetection, parsedTemplate);
|
||||
changeDetectorFactory = changeDetectorFactories[0];
|
||||
styles = stylesAndNormalizedViewDirMetas[0];
|
||||
commands = this._compileCommandsRuntime(compMeta, templateId, parsedTemplate,
|
||||
changeDetectorFactories,
|
||||
compilingComponentCacheKeys, childPromises);
|
||||
return PromiseWrapper.all(childPromises);
|
||||
})
|
||||
.then((_) => {
|
||||
SetWrapper.delete(compilingComponentCacheKeys, cacheKey);
|
||||
return compiledTemplate;
|
||||
});
|
||||
var changeDetectorFactories = this._cdCompiler.compileComponentRuntime(
|
||||
compMeta.type, compMeta.changeDetection, parsedTemplate);
|
||||
changeDetectorFactory = changeDetectorFactories[0];
|
||||
var tmpStyles: string[] = stylesAndNormalizedViewDirMetas[0];
|
||||
tmpStyles.forEach(style => styles.push(style));
|
||||
var tmpCommands: TemplateCmd[] = this._compileCommandsRuntime(
|
||||
compMeta, parsedTemplate, changeDetectorFactories,
|
||||
compilingComponentCacheKeys, childPromises);
|
||||
tmpCommands.forEach(cmd => commands.push(cmd));
|
||||
return PromiseWrapper.all(childPromises);
|
||||
})
|
||||
.then((_) => {
|
||||
SetWrapper.delete(compilingComponentCacheKeys, cacheKey);
|
||||
return compiledTemplate;
|
||||
});
|
||||
this._compiledTemplateDone.set(cacheKey, done);
|
||||
}
|
||||
return compiledTemplate;
|
||||
}
|
||||
|
||||
private _compileCommandsRuntime(compMeta: CompileDirectiveMetadata, templateId: number,
|
||||
parsedTemplate: TemplateAst[],
|
||||
private _compileCommandsRuntime(compMeta: CompileDirectiveMetadata, parsedTemplate: TemplateAst[],
|
||||
changeDetectorFactories: Function[],
|
||||
compilingComponentCacheKeys: Set<Type>,
|
||||
childPromises: Promise<any>[]): TemplateCmd[] {
|
||||
return this._commandCompiler.compileComponentRuntime(
|
||||
compMeta, this._appId, templateId, parsedTemplate, changeDetectorFactories,
|
||||
var cmds: TemplateCmd[] = this._commandCompiler.compileComponentRuntime(
|
||||
compMeta, parsedTemplate, changeDetectorFactories,
|
||||
(childComponentDir: CompileDirectiveMetadata) => {
|
||||
var childCacheKey = childComponentDir.type.runtime;
|
||||
var childViewDirectives: CompileDirectiveMetadata[] =
|
||||
@ -160,8 +154,14 @@ export class TemplateCompiler {
|
||||
// Only wait for a child if it is not a cycle
|
||||
childPromises.push(this._compiledTemplateDone.get(childCacheKey));
|
||||
}
|
||||
return childTemplate;
|
||||
return () => childTemplate;
|
||||
});
|
||||
cmds.forEach(cmd => {
|
||||
if (cmd instanceof BeginComponentCmd) {
|
||||
cmd.templateGetter();
|
||||
}
|
||||
});
|
||||
return cmds;
|
||||
}
|
||||
|
||||
compileTemplatesCodeGen(components: NormalizedComponentWithViewDirectives[]): SourceModule {
|
||||
@ -171,40 +171,35 @@ export class TemplateCompiler {
|
||||
var declarations = [];
|
||||
var templateArguments = [];
|
||||
var componentMetas: CompileDirectiveMetadata[] = [];
|
||||
var templateIdVariable = 'templateId';
|
||||
var appIdVariable = 'appId';
|
||||
components.forEach(componentWithDirs => {
|
||||
var compMeta = <CompileDirectiveMetadata>componentWithDirs.component;
|
||||
assertComponent(compMeta);
|
||||
componentMetas.push(compMeta);
|
||||
this._processTemplateCodeGen(compMeta, appIdVariable, templateIdVariable,
|
||||
this._processTemplateCodeGen(compMeta,
|
||||
<CompileDirectiveMetadata[]>componentWithDirs.directives,
|
||||
declarations, templateArguments);
|
||||
if (compMeta.dynamicLoadable) {
|
||||
var hostMeta = createHostComponentMeta(compMeta.type, compMeta.selector);
|
||||
componentMetas.push(hostMeta);
|
||||
this._processTemplateCodeGen(hostMeta, appIdVariable, templateIdVariable, [compMeta],
|
||||
declarations, templateArguments);
|
||||
this._processTemplateCodeGen(hostMeta, [compMeta], declarations, templateArguments);
|
||||
}
|
||||
});
|
||||
ListWrapper.forEachWithIndex(componentMetas, (compMeta: CompileDirectiveMetadata,
|
||||
index: number) => {
|
||||
var templateDataFn = codeGenValueFn([appIdVariable, templateIdVariable],
|
||||
`[${(<any[]>templateArguments[index]).join(',')}]`);
|
||||
var templateId = `${compMeta.type.moduleUrl}|${compMeta.type.name}`;
|
||||
var constructionKeyword = IS_DART ? 'const' : 'new';
|
||||
var compiledTemplateExpr =
|
||||
`new ${TEMPLATE_COMMANDS_MODULE_REF}CompiledTemplate(${TEMPLATE_COMMANDS_MODULE_REF}nextTemplateId(),${templateDataFn})`;
|
||||
`${constructionKeyword} ${TEMPLATE_COMMANDS_MODULE_REF}CompiledComponentTemplate('${templateId}',${(<any[]>templateArguments[index]).join(',')})`;
|
||||
var variableValueExpr;
|
||||
if (compMeta.type.isHost) {
|
||||
var factoryName = `_hostTemplateFactory${index}`;
|
||||
declarations.push(`${codeGenValueFn([], compiledTemplateExpr, factoryName)};`);
|
||||
var constructionKeyword = IS_DART ? 'const' : 'new';
|
||||
variableValueExpr =
|
||||
`${constructionKeyword} ${TEMPLATE_COMMANDS_MODULE_REF}CompiledHostTemplate(${factoryName})`;
|
||||
`${constructionKeyword} ${TEMPLATE_COMMANDS_MODULE_REF}CompiledHostTemplate(${compiledTemplateExpr})`;
|
||||
} else {
|
||||
variableValueExpr = compiledTemplateExpr;
|
||||
}
|
||||
declarations.push(
|
||||
`${codeGenExportVariable(templateVariableName(compMeta.type), compMeta.type.isHost)}${variableValueExpr};`);
|
||||
var varName = templateVariableName(compMeta.type);
|
||||
declarations.push(`${codeGenExportVariable(varName)}${variableValueExpr};`);
|
||||
declarations.push(`${codeGenValueFn([], varName, templateGetterName(compMeta.type))};`);
|
||||
});
|
||||
var moduleUrl = components[0].component.type.moduleUrl;
|
||||
return new SourceModule(`${templateModuleUrl(moduleUrl)}`, declarations.join('\n'));
|
||||
@ -214,17 +209,16 @@ export class TemplateCompiler {
|
||||
return this._styleCompiler.compileStylesheetCodeGen(stylesheetUrl, cssText);
|
||||
}
|
||||
|
||||
private _processTemplateCodeGen(compMeta: CompileDirectiveMetadata, appIdExpr: string,
|
||||
templateIdExpr: string, directives: CompileDirectiveMetadata[],
|
||||
private _processTemplateCodeGen(compMeta: CompileDirectiveMetadata,
|
||||
directives: CompileDirectiveMetadata[],
|
||||
targetDeclarations: string[], targetTemplateArguments: any[][]) {
|
||||
var styleExpr =
|
||||
this._styleCompiler.compileComponentCodeGen(appIdExpr, templateIdExpr, compMeta.template);
|
||||
var styleExpr = this._styleCompiler.compileComponentCodeGen(compMeta.template);
|
||||
var parsedTemplate =
|
||||
this._templateParser.parse(compMeta.template.template, directives, compMeta.type.name);
|
||||
var changeDetectorsExprs = this._cdCompiler.compileComponentCodeGen(
|
||||
compMeta.type, compMeta.changeDetection, parsedTemplate);
|
||||
var commandsExpr = this._commandCompiler.compileComponentCodeGen(
|
||||
compMeta, appIdExpr, templateIdExpr, parsedTemplate, changeDetectorsExprs.expressions,
|
||||
compMeta, parsedTemplate, changeDetectorsExprs.expressions,
|
||||
codeGenComponentTemplateFactory);
|
||||
|
||||
addAll(styleExpr.declarations, targetDeclarations);
|
||||
@ -251,6 +245,10 @@ function templateVariableName(type: CompileTypeMetadata): string {
|
||||
return `${type.name}Template`;
|
||||
}
|
||||
|
||||
function templateGetterName(type: CompileTypeMetadata): string {
|
||||
return `${templateVariableName(type)}Getter`;
|
||||
}
|
||||
|
||||
function templateModuleUrl(moduleUrl: string): string {
|
||||
var urlWithoutSuffix = moduleUrl.substring(0, moduleUrl.length - MODULE_SUFFIX.length);
|
||||
return `${urlWithoutSuffix}.template${MODULE_SUFFIX}`;
|
||||
@ -263,5 +261,5 @@ function addAll(source: any[], target: any[]) {
|
||||
}
|
||||
|
||||
function codeGenComponentTemplateFactory(nestedCompType: CompileDirectiveMetadata): string {
|
||||
return `${moduleRef(templateModuleUrl(nestedCompType.type.moduleUrl))}${templateVariableName(nestedCompType.type)}`;
|
||||
return `${moduleRef(templateModuleUrl(nestedCompType.type.moduleUrl))}${templateGetterName(nestedCompType.type)}`;
|
||||
}
|
||||
|
@ -43,31 +43,19 @@ function escapeString(input: string, re: RegExp): string {
|
||||
});
|
||||
}
|
||||
|
||||
export function codeGenExportVariable(name: string, isConst: boolean = false): string {
|
||||
export function codeGenExportVariable(name: string): string {
|
||||
if (IS_DART) {
|
||||
return isConst ? `const ${name} = ` : `final ${name} = `;
|
||||
return `const ${name} = `;
|
||||
} else {
|
||||
return `var ${name} = exports['${name}'] = `;
|
||||
}
|
||||
}
|
||||
|
||||
export function codeGenConcatArray(expression: string): string {
|
||||
return `${IS_DART ? '..addAll' : '.concat'}(${expression})`;
|
||||
}
|
||||
|
||||
export function codeGenMapArray(argNames: string[], callback: string): string {
|
||||
export function codeGenConstConstructorCall(name: string): string {
|
||||
if (IS_DART) {
|
||||
return `.map( (${argNames.join(',')}) => ${callback} ).toList()`;
|
||||
return `const ${name}`;
|
||||
} else {
|
||||
return `.map(function(${argNames.join(',')}) { return ${callback}; })`;
|
||||
}
|
||||
}
|
||||
|
||||
export function codeGenReplaceAll(pattern: string, expression: string): string {
|
||||
if (IS_DART) {
|
||||
return `.replaceAll('${pattern}', ${expression})`;
|
||||
} else {
|
||||
return `.replace(/${pattern}/g, ${expression})`;
|
||||
return `new ${name}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {isPresent, isBlank, Type, isArray, isNumber} from 'angular2/src/facade/lang';
|
||||
|
||||
import {RenderProtoViewRef} from 'angular2/src/core/render/api';
|
||||
import {RenderProtoViewRef, RenderComponentTemplate} from 'angular2/src/core/render/api';
|
||||
|
||||
import {Optional, Injectable, Provider, resolveForwardRef, Inject} from 'angular2/src/core/di';
|
||||
|
||||
@ -13,12 +13,12 @@ import {ProtoElementInjector, DirectiveProvider} from './element_injector';
|
||||
import {DirectiveResolver} from './directive_resolver';
|
||||
import {ViewResolver} from './view_resolver';
|
||||
import {PipeResolver} from './pipe_resolver';
|
||||
import {ViewMetadata} from '../metadata/view';
|
||||
import {ViewMetadata, ViewEncapsulation} from '../metadata/view';
|
||||
import {AMBIENT_PIPES} from 'angular2/src/core/ambient';
|
||||
|
||||
import {
|
||||
visitAllCommands,
|
||||
CompiledTemplate,
|
||||
CompiledComponentTemplate,
|
||||
CompiledHostTemplate,
|
||||
TemplateCmd,
|
||||
CommandVisitor,
|
||||
@ -36,7 +36,9 @@ import {APP_ID} from 'angular2/src/core/application_tokens';
|
||||
|
||||
@Injectable()
|
||||
export class ProtoViewFactory {
|
||||
private _cache: Map<number, AppProtoView> = new Map<number, AppProtoView>();
|
||||
private _cache: Map<string, AppProtoView> = new Map<string, AppProtoView>();
|
||||
private _nextTemplateId: number = 0;
|
||||
|
||||
constructor(private _renderer: Renderer,
|
||||
@Optional() @Inject(AMBIENT_PIPES) private _ambientPipes: Array<Type | any[]>,
|
||||
private _directiveResolver: DirectiveResolver, private _viewResolver: ViewResolver,
|
||||
@ -45,13 +47,16 @@ export class ProtoViewFactory {
|
||||
clearCache() { this._cache.clear(); }
|
||||
|
||||
createHost(compiledHostTemplate: CompiledHostTemplate): AppProtoView {
|
||||
var compiledTemplate = compiledHostTemplate.getTemplate();
|
||||
var compiledTemplate = compiledHostTemplate.template;
|
||||
var result = this._cache.get(compiledTemplate.id);
|
||||
if (isBlank(result)) {
|
||||
var templateData = compiledTemplate.getData(this._appId);
|
||||
var emptyMap: {[key: string]: PipeProvider} = {};
|
||||
result = new AppProtoView(templateData.commands, ViewType.HOST, true,
|
||||
templateData.changeDetectorFactory, null, new ProtoPipes(emptyMap));
|
||||
var shortId = `${this._appId}-${this._nextTemplateId++}`;
|
||||
this._renderer.registerComponentTemplate(new RenderComponentTemplate(
|
||||
compiledTemplate.id, shortId, ViewEncapsulation.None, compiledTemplate.commands, []));
|
||||
result =
|
||||
new AppProtoView(compiledTemplate.id, compiledTemplate.commands, ViewType.HOST, true,
|
||||
compiledTemplate.changeDetectorFactory, null, new ProtoPipes(emptyMap));
|
||||
this._cache.set(compiledTemplate.id, result);
|
||||
}
|
||||
return result;
|
||||
@ -62,18 +67,19 @@ export class ProtoViewFactory {
|
||||
if (isBlank(nestedProtoView)) {
|
||||
var component = cmd.directives[0];
|
||||
var view = this._viewResolver.resolve(component);
|
||||
var compiledTemplateData = cmd.template.getData(this._appId);
|
||||
|
||||
this._renderer.registerComponentTemplate(cmd.templateId, compiledTemplateData.commands,
|
||||
compiledTemplateData.styles, cmd.nativeShadow);
|
||||
var compiledTemplate = cmd.templateGetter();
|
||||
var styles = _flattenStyleArr(compiledTemplate.styles, []);
|
||||
var shortId = `${this._appId}-${this._nextTemplateId++}`;
|
||||
this._renderer.registerComponentTemplate(new RenderComponentTemplate(
|
||||
compiledTemplate.id, shortId, cmd.encapsulation, compiledTemplate.commands, styles));
|
||||
var boundPipes = this._flattenPipes(view).map(pipe => this._bindPipe(pipe));
|
||||
|
||||
nestedProtoView = new AppProtoView(compiledTemplateData.commands, ViewType.COMPONENT, true,
|
||||
compiledTemplateData.changeDetectorFactory, null,
|
||||
ProtoPipes.fromProviders(boundPipes));
|
||||
nestedProtoView = new AppProtoView(
|
||||
compiledTemplate.id, compiledTemplate.commands, ViewType.COMPONENT, true,
|
||||
compiledTemplate.changeDetectorFactory, null, ProtoPipes.fromProviders(boundPipes));
|
||||
// Note: The cache is updated before recursing
|
||||
// to be able to resolve cycles
|
||||
this._cache.set(cmd.template.id, nestedProtoView);
|
||||
this._cache.set(compiledTemplate.id, nestedProtoView);
|
||||
this._initializeProtoView(nestedProtoView, null);
|
||||
}
|
||||
return nestedProtoView;
|
||||
@ -81,7 +87,7 @@ export class ProtoViewFactory {
|
||||
|
||||
private _createEmbeddedTemplate(cmd: EmbeddedTemplateCmd, parent: AppProtoView): AppProtoView {
|
||||
var nestedProtoView = new AppProtoView(
|
||||
cmd.children, ViewType.EMBEDDED, cmd.isMerged, cmd.changeDetectorFactory,
|
||||
parent.templateId, cmd.children, ViewType.EMBEDDED, cmd.isMerged, cmd.changeDetectorFactory,
|
||||
arrayToMap(cmd.variableNameAndValues, true), new ProtoPipes(parent.pipes.config));
|
||||
if (cmd.isMerged) {
|
||||
this.initializeProtoViewIfNeeded(nestedProtoView);
|
||||
@ -91,7 +97,7 @@ export class ProtoViewFactory {
|
||||
|
||||
initializeProtoViewIfNeeded(protoView: AppProtoView) {
|
||||
if (!protoView.isInitialized()) {
|
||||
var render = this._renderer.createProtoView(protoView.templateCmds);
|
||||
var render = this._renderer.createProtoView(protoView.templateId, protoView.templateCmds);
|
||||
this._initializeProtoView(protoView, render);
|
||||
}
|
||||
}
|
||||
@ -321,3 +327,15 @@ function _flattenArray(tree: any[], out: Array<Type | Provider | any[]>): void {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _flattenStyleArr(arr: Array<string | any[]>, out: string[]): string[] {
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
var entry = arr[i];
|
||||
if (isArray(entry)) {
|
||||
_flattenStyleArr(<any[]>entry, out);
|
||||
} else {
|
||||
out.push(<string>entry);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import {Type, CONST_EXPR, CONST, isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
import {unimplemented} from 'angular2/src/facade/exceptions';
|
||||
import {
|
||||
RenderTemplateCmd,
|
||||
RenderCommandVisitor,
|
||||
@ -8,12 +9,10 @@ import {
|
||||
RenderBeginComponentCmd,
|
||||
RenderEmbeddedTemplateCmd
|
||||
} from 'angular2/src/core/render/render';
|
||||
|
||||
var _nextTemplateId: number = 0;
|
||||
|
||||
export function nextTemplateId(): number {
|
||||
return _nextTemplateId++;
|
||||
}
|
||||
import {ViewEncapsulation} from 'angular2/src/core/metadata';
|
||||
// Export ViewEncapsulation so that compiled templates only need to depend
|
||||
// on template_commands.
|
||||
export {ViewEncapsulation} from 'angular2/src/core/metadata';
|
||||
|
||||
/**
|
||||
* A compiled host template.
|
||||
@ -23,34 +22,16 @@ export function nextTemplateId(): number {
|
||||
*/
|
||||
@CONST()
|
||||
export class CompiledHostTemplate {
|
||||
// Note: _templateGetter is a function so that CompiledHostTemplate can be
|
||||
// a const!
|
||||
constructor(private _templateGetter: Function) {}
|
||||
|
||||
getTemplate(): CompiledTemplate { return this._templateGetter(); }
|
||||
constructor(public template: CompiledComponentTemplate) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* A compiled template.
|
||||
*/
|
||||
export class CompiledTemplate {
|
||||
// Note: paramGetter is a function so that we can have cycles between templates!
|
||||
// paramGetter returns a tuple with:
|
||||
// - ChangeDetector factory function
|
||||
// - TemplateCmd[]
|
||||
// - styles
|
||||
constructor(public id: number,
|
||||
private _dataGetter: /*()=>Array<Function, TemplateCmd[], string[]>*/ Function) {}
|
||||
|
||||
getData(appId: string): CompiledTemplateData {
|
||||
var data = this._dataGetter(appId, this.id);
|
||||
return new CompiledTemplateData(data[0], data[1], data[2]);
|
||||
}
|
||||
}
|
||||
|
||||
export class CompiledTemplateData {
|
||||
constructor(public changeDetectorFactory: Function, public commands: TemplateCmd[],
|
||||
public styles: string[]) {}
|
||||
@CONST()
|
||||
export class CompiledComponentTemplate {
|
||||
constructor(public id: string, public changeDetectorFactory: Function,
|
||||
public commands: TemplateCmd[], public styles: string[]) {}
|
||||
}
|
||||
|
||||
const EMPTY_ARR = CONST_EXPR([]);
|
||||
@ -59,6 +40,7 @@ export interface TemplateCmd extends RenderTemplateCmd {
|
||||
visit(visitor: RenderCommandVisitor, context: any): any;
|
||||
}
|
||||
|
||||
@CONST()
|
||||
export class TextCmd implements TemplateCmd, RenderTextCmd {
|
||||
constructor(public value: string, public isBound: boolean, public ngContentIndex: number) {}
|
||||
visit(visitor: RenderCommandVisitor, context: any): any {
|
||||
@ -66,10 +48,7 @@ export class TextCmd implements TemplateCmd, RenderTextCmd {
|
||||
}
|
||||
}
|
||||
|
||||
export function text(value: string, isBound: boolean, ngContentIndex: number): TextCmd {
|
||||
return new TextCmd(value, isBound, ngContentIndex);
|
||||
}
|
||||
|
||||
@CONST()
|
||||
export class NgContentCmd implements TemplateCmd, RenderNgContentCmd {
|
||||
isBound: boolean = false;
|
||||
constructor(public index: number, public ngContentIndex: number) {}
|
||||
@ -78,17 +57,14 @@ export class NgContentCmd implements TemplateCmd, RenderNgContentCmd {
|
||||
}
|
||||
}
|
||||
|
||||
export function ngContent(index: number, ngContentIndex: number): NgContentCmd {
|
||||
return new NgContentCmd(index, ngContentIndex);
|
||||
}
|
||||
|
||||
export interface IBeginElementCmd extends TemplateCmd, RenderBeginElementCmd {
|
||||
variableNameAndValues: Array<string | number>;
|
||||
eventTargetAndNames: string[];
|
||||
directives: Type[];
|
||||
visit(visitor: RenderCommandVisitor, context: any): any;
|
||||
export abstract class IBeginElementCmd extends RenderBeginElementCmd implements TemplateCmd {
|
||||
get variableNameAndValues(): Array<string | number> { return unimplemented(); }
|
||||
get eventTargetAndNames(): string[] { return unimplemented(); }
|
||||
get directives(): Type[] { return unimplemented(); }
|
||||
abstract visit(visitor: RenderCommandVisitor, context: any): any;
|
||||
}
|
||||
|
||||
@CONST()
|
||||
export class BeginElementCmd implements TemplateCmd, IBeginElementCmd, RenderBeginElementCmd {
|
||||
constructor(public name: string, public attrNameAndValues: string[],
|
||||
public eventTargetAndNames: string[],
|
||||
@ -99,57 +75,40 @@ export class BeginElementCmd implements TemplateCmd, IBeginElementCmd, RenderBeg
|
||||
}
|
||||
}
|
||||
|
||||
export function beginElement(name: string, attrNameAndValues: string[],
|
||||
eventTargetAndNames: string[],
|
||||
variableNameAndValues: Array<string | number>, directives: Type[],
|
||||
isBound: boolean, ngContentIndex: number): BeginElementCmd {
|
||||
return new BeginElementCmd(name, attrNameAndValues, eventTargetAndNames, variableNameAndValues,
|
||||
directives, isBound, ngContentIndex);
|
||||
}
|
||||
|
||||
@CONST()
|
||||
export class EndElementCmd implements TemplateCmd {
|
||||
visit(visitor: RenderCommandVisitor, context: any): any {
|
||||
return visitor.visitEndElement(context);
|
||||
}
|
||||
}
|
||||
|
||||
export function endElement(): TemplateCmd {
|
||||
return new EndElementCmd();
|
||||
}
|
||||
|
||||
@CONST()
|
||||
export class BeginComponentCmd implements TemplateCmd, IBeginElementCmd, RenderBeginComponentCmd {
|
||||
isBound: boolean = true;
|
||||
templateId: number;
|
||||
constructor(public name: string, public attrNameAndValues: string[],
|
||||
public eventTargetAndNames: string[],
|
||||
public variableNameAndValues: Array<string | number>, public directives: Type[],
|
||||
public nativeShadow: boolean, public ngContentIndex: number,
|
||||
public template: CompiledTemplate) {
|
||||
this.templateId = template.id;
|
||||
}
|
||||
public encapsulation: ViewEncapsulation, public ngContentIndex: number,
|
||||
// Note: the template needs to be stored as a function
|
||||
// so that we can resolve cycles
|
||||
public templateGetter: Function /*() => CompiledComponentTemplate*/) {}
|
||||
|
||||
get templateId(): string { return this.templateGetter().id; }
|
||||
|
||||
visit(visitor: RenderCommandVisitor, context: any): any {
|
||||
return visitor.visitBeginComponent(this, context);
|
||||
}
|
||||
}
|
||||
|
||||
export function beginComponent(
|
||||
name: string, attrNameAnsValues: string[], eventTargetAndNames: string[],
|
||||
variableNameAndValues: Array<string | number>, directives: Type[], nativeShadow: boolean,
|
||||
ngContentIndex: number, template: CompiledTemplate): BeginComponentCmd {
|
||||
return new BeginComponentCmd(name, attrNameAnsValues, eventTargetAndNames, variableNameAndValues,
|
||||
directives, nativeShadow, ngContentIndex, template);
|
||||
}
|
||||
|
||||
@CONST()
|
||||
export class EndComponentCmd implements TemplateCmd {
|
||||
visit(visitor: RenderCommandVisitor, context: any): any {
|
||||
return visitor.visitEndComponent(context);
|
||||
}
|
||||
}
|
||||
|
||||
export function endComponent(): TemplateCmd {
|
||||
return new EndComponentCmd();
|
||||
}
|
||||
|
||||
@CONST()
|
||||
export class EmbeddedTemplateCmd implements TemplateCmd, IBeginElementCmd,
|
||||
RenderEmbeddedTemplateCmd {
|
||||
isBound: boolean = true;
|
||||
@ -163,13 +122,6 @@ export class EmbeddedTemplateCmd implements TemplateCmd, IBeginElementCmd,
|
||||
}
|
||||
}
|
||||
|
||||
export function embeddedTemplate(attrNameAndValues: string[], variableNameAndValues: string[],
|
||||
directives: Type[], isMerged: boolean, ngContentIndex: number,
|
||||
changeDetectorFactory: Function,
|
||||
children: TemplateCmd[]): EmbeddedTemplateCmd {
|
||||
return new EmbeddedTemplateCmd(attrNameAndValues, variableNameAndValues, directives, isMerged,
|
||||
ngContentIndex, changeDetectorFactory, children);
|
||||
}
|
||||
|
||||
export interface CommandVisitor extends RenderCommandVisitor {
|
||||
visitText(cmd: TextCmd, context: any): any;
|
||||
|
@ -318,8 +318,8 @@ export class AppProtoView {
|
||||
textBindingCount = null;
|
||||
render: renderApi.RenderProtoViewRef = null;
|
||||
|
||||
constructor(public templateCmds: TemplateCmd[], public type: ViewType, public isMergable: boolean,
|
||||
public changeDetectorFactory: Function,
|
||||
constructor(public templateId: string, public templateCmds: TemplateCmd[], public type: ViewType,
|
||||
public isMergable: boolean, public changeDetectorFactory: Function,
|
||||
public templateVariableBindings: Map<string, string>, public pipes: ProtoPipes) {
|
||||
this.ref = new ProtoViewRef_(this);
|
||||
}
|
||||
|
@ -1,4 +1,6 @@
|
||||
import {unimplemented} from 'angular2/src/facade/exceptions';
|
||||
import {Map} from 'angular2/src/facade/collection';
|
||||
import {ViewEncapsulation} from 'angular2/src/core/metadata';
|
||||
|
||||
/**
|
||||
* Represents an Angular ProtoView in the Rendering Context.
|
||||
@ -69,37 +71,40 @@ export class RenderFragmentRef {}
|
||||
// TODO(i): refactor into an interface
|
||||
export class RenderViewRef {}
|
||||
|
||||
export interface RenderTemplateCmd { visit(visitor: RenderCommandVisitor, context: any): any; }
|
||||
|
||||
export interface RenderBeginCmd extends RenderTemplateCmd {
|
||||
ngContentIndex: number;
|
||||
isBound: boolean;
|
||||
export abstract class RenderTemplateCmd {
|
||||
abstract visit(visitor: RenderCommandVisitor, context: any): any;
|
||||
}
|
||||
|
||||
export interface RenderTextCmd extends RenderBeginCmd { value: string; }
|
||||
export abstract class RenderBeginCmd extends RenderTemplateCmd {
|
||||
get ngContentIndex(): number { return unimplemented(); };
|
||||
get isBound(): boolean { return unimplemented(); };
|
||||
}
|
||||
|
||||
export interface RenderNgContentCmd {
|
||||
export abstract class RenderTextCmd extends RenderBeginCmd {
|
||||
get value(): string { return unimplemented(); };
|
||||
}
|
||||
|
||||
export abstract class RenderNgContentCmd extends RenderTemplateCmd {
|
||||
// The index of this NgContent element
|
||||
index: number;
|
||||
get index(): number { return unimplemented(); };
|
||||
// The index of the NgContent element into which this
|
||||
// NgContent element should be projected (if any)
|
||||
ngContentIndex: number;
|
||||
get ngContentIndex(): number { return unimplemented(); };
|
||||
}
|
||||
|
||||
export interface RenderBeginElementCmd extends RenderBeginCmd {
|
||||
name: string;
|
||||
attrNameAndValues: string[];
|
||||
eventTargetAndNames: string[];
|
||||
export abstract class RenderBeginElementCmd extends RenderBeginCmd {
|
||||
get name(): string { return unimplemented(); };
|
||||
get attrNameAndValues(): string[] { return unimplemented(); };
|
||||
get eventTargetAndNames(): string[] { return unimplemented(); };
|
||||
}
|
||||
|
||||
export interface RenderBeginComponentCmd extends RenderBeginElementCmd {
|
||||
nativeShadow: boolean;
|
||||
templateId: number;
|
||||
export abstract class RenderBeginComponentCmd extends RenderBeginElementCmd {
|
||||
get templateId(): string { return unimplemented(); };
|
||||
}
|
||||
|
||||
export interface RenderEmbeddedTemplateCmd extends RenderBeginElementCmd {
|
||||
isMerged: boolean;
|
||||
children: RenderTemplateCmd[];
|
||||
export abstract class RenderEmbeddedTemplateCmd extends RenderBeginElementCmd {
|
||||
get isMerged(): boolean { return unimplemented(); };
|
||||
get children(): RenderTemplateCmd[] { return unimplemented(); };
|
||||
}
|
||||
|
||||
export interface RenderCommandVisitor {
|
||||
@ -156,6 +161,10 @@ export interface RenderElementRef {
|
||||
boundElementIndex: number;
|
||||
}
|
||||
|
||||
export class RenderComponentTemplate {
|
||||
constructor(public id: string, public shortId: string, public encapsulation: ViewEncapsulation,
|
||||
public commands: RenderTemplateCmd[], public styles: string[]) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Injectable service that provides a low-level interface for modifying the UI.
|
||||
@ -177,13 +186,13 @@ export abstract class Renderer {
|
||||
* Once a template is registered it can be referenced via {@link RenderBeginComponentCmd} when
|
||||
* {@link #createProtoView creating Render ProtoView}.
|
||||
*/
|
||||
abstract registerComponentTemplate(templateId: number, commands: RenderTemplateCmd[],
|
||||
styles: string[], nativeShadow: boolean);
|
||||
abstract registerComponentTemplate(template: RenderComponentTemplate);
|
||||
|
||||
/**
|
||||
* Creates a {@link RenderProtoViewRef} from an array of {@link RenderTemplateCmd}`s.
|
||||
*/
|
||||
abstract createProtoView(cmds: RenderTemplateCmd[]): RenderProtoViewRef;
|
||||
abstract createProtoView(componentTemplateId: string,
|
||||
cmds: RenderTemplateCmd[]): RenderProtoViewRef;
|
||||
|
||||
/**
|
||||
* Creates a Root Host View based on the provided `hostProtoViewRef`.
|
||||
|
@ -1,6 +1,13 @@
|
||||
import {Inject, Injectable, OpaqueToken} from 'angular2/src/core/di';
|
||||
import {AnimationBuilder} from 'angular2/src/animate/animation_builder';
|
||||
import {isPresent, isBlank, RegExpWrapper, CONST_EXPR, stringify} from 'angular2/src/facade/lang';
|
||||
import {
|
||||
isPresent,
|
||||
isBlank,
|
||||
RegExpWrapper,
|
||||
CONST_EXPR,
|
||||
stringify,
|
||||
StringWrapper
|
||||
} from 'angular2/src/facade/lang';
|
||||
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
|
||||
|
||||
import {DOM} from 'angular2/src/core/dom/dom_adapter';
|
||||
@ -18,13 +25,15 @@ import {
|
||||
RenderFragmentRef,
|
||||
RenderViewWithFragments,
|
||||
RenderTemplateCmd,
|
||||
RenderEventDispatcher
|
||||
RenderEventDispatcher,
|
||||
RenderComponentTemplate
|
||||
} from '../api';
|
||||
|
||||
import {DOCUMENT} from './dom_tokens';
|
||||
import {createRenderView, NodeFactory} from '../view_factory';
|
||||
import {createRenderView, NodeFactory, encapsulateStyles} from '../view_factory';
|
||||
import {DefaultRenderView, DefaultRenderFragmentRef, DefaultProtoViewRef} from '../view';
|
||||
import {camelCaseToDashCase} from './util';
|
||||
import {ViewEncapsulation} from 'angular2/src/core/metadata';
|
||||
|
||||
// TODO(tbosch): solve SVG properly once https://github.com/angular/angular/issues/4417 is done
|
||||
const XLINK_NAMESPACE = 'http://www.w3.org/1999/xlink';
|
||||
@ -113,14 +122,12 @@ const SVG_ELEMENT_NAMES = CONST_EXPR({
|
||||
const SVG_ATTR_NAMESPACES = CONST_EXPR({'href': XLINK_NAMESPACE, 'xlink:href': XLINK_NAMESPACE});
|
||||
|
||||
export abstract class DomRenderer extends Renderer implements NodeFactory<Node> {
|
||||
abstract registerComponentTemplate(templateId: number, commands: RenderTemplateCmd[],
|
||||
styles: string[], nativeShadow: boolean);
|
||||
abstract registerComponentTemplate(template: RenderComponentTemplate);
|
||||
|
||||
abstract resolveComponentTemplate(templateId: number): RenderTemplateCmd[];
|
||||
abstract resolveComponentTemplate(templateId: string): RenderComponentTemplate;
|
||||
|
||||
createProtoView(cmds: RenderTemplateCmd[]): RenderProtoViewRef {
|
||||
return new DefaultProtoViewRef(cmds);
|
||||
}
|
||||
abstract createProtoView(componentTemplateId: string,
|
||||
cmds: RenderTemplateCmd[]): RenderProtoViewRef;
|
||||
|
||||
abstract createRootHostView(hostProtoViewRef: RenderProtoViewRef, fragmentCount: number,
|
||||
hostElementSelector: string): RenderViewWithFragments;
|
||||
@ -189,7 +196,7 @@ export abstract class DomRenderer extends Renderer implements NodeFactory<Node>
|
||||
}
|
||||
abstract createElement(name: string, attrNameAndValues: string[]): Node;
|
||||
abstract mergeElement(existing: Node, attrNameAndValues: string[]);
|
||||
abstract createShadowRoot(host: Node, templateId: number): Node;
|
||||
abstract createShadowRoot(host: Node, templateId: string): Node;
|
||||
createText(value: string): Node { return DOM.createTextNode(isPresent(value) ? value : ''); }
|
||||
appendChild(parent: Node, child: Node) { DOM.appendChild(parent, child); }
|
||||
abstract on(element: Node, eventName: string, callback: Function);
|
||||
@ -252,8 +259,8 @@ export abstract class DomRenderer extends Renderer implements NodeFactory<Node>
|
||||
|
||||
@Injectable()
|
||||
export class DomRenderer_ extends DomRenderer {
|
||||
private _componentCmds: Map<number, RenderTemplateCmd[]> = new Map<number, RenderTemplateCmd[]>();
|
||||
private _nativeShadowStyles: Map<number, string[]> = new Map<number, string[]>();
|
||||
private _componentTpls: Map<string, RenderComponentTemplate> =
|
||||
new Map<string, RenderComponentTemplate>();
|
||||
private _document;
|
||||
|
||||
constructor(private _eventManager: EventManager,
|
||||
@ -263,18 +270,20 @@ export class DomRenderer_ extends DomRenderer {
|
||||
this._document = document;
|
||||
}
|
||||
|
||||
registerComponentTemplate(templateId: number, commands: RenderTemplateCmd[], styles: string[],
|
||||
nativeShadow: boolean) {
|
||||
this._componentCmds.set(templateId, commands);
|
||||
if (nativeShadow) {
|
||||
this._nativeShadowStyles.set(templateId, styles);
|
||||
} else {
|
||||
this._domSharedStylesHost.addStyles(styles);
|
||||
registerComponentTemplate(template: RenderComponentTemplate) {
|
||||
this._componentTpls.set(template.id, template);
|
||||
if (template.encapsulation !== ViewEncapsulation.Native) {
|
||||
var encapsulatedStyles = encapsulateStyles(template);
|
||||
this._domSharedStylesHost.addStyles(encapsulatedStyles);
|
||||
}
|
||||
}
|
||||
|
||||
resolveComponentTemplate(templateId: number): RenderTemplateCmd[] {
|
||||
return this._componentCmds.get(templateId);
|
||||
createProtoView(componentTemplateId: string, cmds: RenderTemplateCmd[]): RenderProtoViewRef {
|
||||
return new DefaultProtoViewRef(this._componentTpls.get(componentTemplateId), cmds);
|
||||
}
|
||||
|
||||
resolveComponentTemplate(templateId: string): RenderComponentTemplate {
|
||||
return this._componentTpls.get(templateId);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
@ -299,7 +308,8 @@ export class DomRenderer_ extends DomRenderer {
|
||||
|
||||
private _createView(protoViewRef: RenderProtoViewRef,
|
||||
inplaceElement: HTMLElement): RenderViewWithFragments {
|
||||
var view = createRenderView((<DefaultProtoViewRef>protoViewRef).cmds, inplaceElement, this);
|
||||
var dpvr = <DefaultProtoViewRef>protoViewRef;
|
||||
var view = createRenderView(dpvr.template, dpvr.cmds, inplaceElement, this);
|
||||
var sdRoots = view.nativeShadowRoots;
|
||||
for (var i = 0; i < sdRoots.length; i++) {
|
||||
this._domSharedStylesHost.addHost(sdRoots[i]);
|
||||
@ -375,11 +385,11 @@ export class DomRenderer_ extends DomRenderer {
|
||||
createRootContentInsertionPoint(): Node {
|
||||
return DOM.createComment('root-content-insertion-point');
|
||||
}
|
||||
createShadowRoot(host: Node, templateId: number): Node {
|
||||
createShadowRoot(host: Node, templateId: string): Node {
|
||||
var sr = DOM.createShadowRoot(host);
|
||||
var styles = this._nativeShadowStyles.get(templateId);
|
||||
for (var i = 0; i < styles.length; i++) {
|
||||
DOM.appendChild(sr, DOM.createStyleElement(styles[i]));
|
||||
var tpl = this._componentTpls.get(templateId);
|
||||
for (var i = 0; i < tpl.styles.length; i++) {
|
||||
DOM.appendChild(sr, DOM.createStyleElement(tpl.styles[i]));
|
||||
}
|
||||
return sr;
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ import {ListWrapper, MapWrapper, Map, StringMapWrapper} from 'angular2/src/facad
|
||||
import {isPresent, isBlank, stringify} from 'angular2/src/facade/lang';
|
||||
|
||||
import {
|
||||
RenderComponentTemplate,
|
||||
RenderViewRef,
|
||||
RenderEventDispatcher,
|
||||
RenderTemplateCmd,
|
||||
@ -11,7 +12,9 @@ import {
|
||||
} from './api';
|
||||
|
||||
export class DefaultProtoViewRef extends RenderProtoViewRef {
|
||||
constructor(public cmds: RenderTemplateCmd[]) { super(); }
|
||||
constructor(public template: RenderComponentTemplate, public cmds: RenderTemplateCmd[]) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
export class DefaultRenderFragmentRef<N> extends RenderFragmentRef {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {isBlank, isPresent} from 'angular2/src/facade/lang';
|
||||
import {isBlank, isPresent, StringWrapper} from 'angular2/src/facade/lang';
|
||||
import {
|
||||
RenderEventDispatcher,
|
||||
RenderTemplateCmd,
|
||||
@ -7,17 +7,34 @@ import {
|
||||
RenderBeginComponentCmd,
|
||||
RenderNgContentCmd,
|
||||
RenderTextCmd,
|
||||
RenderEmbeddedTemplateCmd
|
||||
RenderEmbeddedTemplateCmd,
|
||||
RenderComponentTemplate
|
||||
} from './api';
|
||||
import {DefaultRenderView, DefaultRenderFragmentRef} from './view';
|
||||
import {ViewEncapsulation} from 'angular2/src/core/metadata';
|
||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
export function createRenderView(fragmentCmds: RenderTemplateCmd[], inplaceElement: any,
|
||||
|
||||
export function encapsulateStyles(componentTemplate: RenderComponentTemplate): string[] {
|
||||
var processedStyles = componentTemplate.styles;
|
||||
if (componentTemplate.encapsulation === ViewEncapsulation.Emulated) {
|
||||
processedStyles = ListWrapper.createFixedSize(componentTemplate.styles.length);
|
||||
for (var i = 0; i < componentTemplate.styles.length; i++) {
|
||||
processedStyles[i] = StringWrapper.replaceAll(componentTemplate.styles[i], COMPONENT_REGEX,
|
||||
componentTemplate.shortId);
|
||||
}
|
||||
}
|
||||
return processedStyles;
|
||||
}
|
||||
|
||||
export function createRenderView(componentTemplate: RenderComponentTemplate,
|
||||
cmds: RenderTemplateCmd[], inplaceElement: any,
|
||||
nodeFactory: NodeFactory<any>): DefaultRenderView<any> {
|
||||
var view: DefaultRenderView<any>;
|
||||
var eventDispatcher = (boundElementIndex: number, eventName: string, event: any) =>
|
||||
view.dispatchRenderEvent(boundElementIndex, eventName, event);
|
||||
var context = new BuildContext(eventDispatcher, nodeFactory, inplaceElement);
|
||||
context.build(fragmentCmds);
|
||||
context.build(componentTemplate, cmds);
|
||||
var fragments: DefaultRenderFragmentRef<any>[] = [];
|
||||
for (var i = 0; i < context.fragments.length; i++) {
|
||||
fragments.push(new DefaultRenderFragmentRef(context.fragments[i]));
|
||||
@ -29,12 +46,12 @@ export function createRenderView(fragmentCmds: RenderTemplateCmd[], inplaceEleme
|
||||
}
|
||||
|
||||
export interface NodeFactory<N> {
|
||||
resolveComponentTemplate(templateId: number): RenderTemplateCmd[];
|
||||
resolveComponentTemplate(templateId: string): RenderComponentTemplate;
|
||||
createTemplateAnchor(attrNameAndValues: string[]): N;
|
||||
createElement(name: string, attrNameAndValues: string[]): N;
|
||||
createRootContentInsertionPoint(): N;
|
||||
mergeElement(existing: N, attrNameAndValues: string[]);
|
||||
createShadowRoot(host: N, templateId: number): N;
|
||||
createShadowRoot(host: N, templateId: string): N;
|
||||
createText(value: string): N;
|
||||
appendChild(parent: N, child: N);
|
||||
on(element: N, eventName: string, callback: Function);
|
||||
@ -57,8 +74,8 @@ class BuildContext<N> {
|
||||
componentCount: number = 0;
|
||||
isHost: boolean;
|
||||
|
||||
build(fragmentCmds: RenderTemplateCmd[]) {
|
||||
this.enqueueFragmentBuilder(null, fragmentCmds);
|
||||
build(template: RenderComponentTemplate, cmds: RenderTemplateCmd[]) {
|
||||
this.enqueueRootBuilder(template, cmds);
|
||||
this._build(this._builders[0]);
|
||||
}
|
||||
|
||||
@ -73,14 +90,22 @@ class BuildContext<N> {
|
||||
|
||||
enqueueComponentBuilder(component: Component<N>) {
|
||||
this.componentCount++;
|
||||
this._builders.push(new RenderViewBuilder<N>(
|
||||
component, null, this.factory.resolveComponentTemplate(component.cmd.templateId)));
|
||||
this._builders.push(
|
||||
new RenderViewBuilder<N>(component, null, component.template, component.template.commands));
|
||||
}
|
||||
|
||||
enqueueFragmentBuilder(parentComponent: Component<N>, commands: RenderTemplateCmd[]) {
|
||||
enqueueFragmentBuilder(parentComponent: Component<N>, parentTemplate: RenderComponentTemplate,
|
||||
commands: RenderTemplateCmd[]) {
|
||||
var rootNodes = [];
|
||||
this.fragments.push(rootNodes);
|
||||
this._builders.push(new RenderViewBuilder<N>(parentComponent, rootNodes, commands));
|
||||
this._builders.push(
|
||||
new RenderViewBuilder<N>(parentComponent, rootNodes, parentTemplate, commands));
|
||||
}
|
||||
|
||||
enqueueRootBuilder(template: RenderComponentTemplate, cmds: RenderTemplateCmd[]) {
|
||||
var rootNodes = [];
|
||||
this.fragments.push(rootNodes);
|
||||
this._builders.push(new RenderViewBuilder<N>(null, rootNodes, template, cmds));
|
||||
}
|
||||
|
||||
consumeInplaceElement(): N {
|
||||
@ -116,14 +141,15 @@ class RenderViewBuilder<N> implements RenderCommandVisitor {
|
||||
parentStack: Array<N | Component<N>>;
|
||||
|
||||
constructor(public parentComponent: Component<N>, public fragmentRootNodes: N[],
|
||||
public commands: RenderTemplateCmd[]) {
|
||||
public template: RenderComponentTemplate, public cmds: RenderTemplateCmd[]) {
|
||||
var rootNodesParent = isPresent(fragmentRootNodes) ? null : parentComponent.shadowRoot;
|
||||
this.parentStack = [rootNodesParent];
|
||||
}
|
||||
|
||||
build(context: BuildContext<N>) {
|
||||
for (var i = 0; i < this.commands.length; i++) {
|
||||
this.commands[i].visit(this, context);
|
||||
var cmds = this.cmds;
|
||||
for (var i = 0; i < cmds.length; i++) {
|
||||
cmds[i].visit(this, context);
|
||||
}
|
||||
}
|
||||
|
||||
@ -158,7 +184,7 @@ class RenderViewBuilder<N> implements RenderCommandVisitor {
|
||||
return null;
|
||||
}
|
||||
visitBeginElement(cmd: RenderBeginElementCmd, context: BuildContext<N>): any {
|
||||
this.parentStack.push(this._beginElement(cmd, context));
|
||||
this.parentStack.push(this._beginElement(cmd, context, null));
|
||||
return null;
|
||||
}
|
||||
visitEndElement(context: BuildContext<N>): any {
|
||||
@ -166,14 +192,17 @@ class RenderViewBuilder<N> implements RenderCommandVisitor {
|
||||
return null;
|
||||
}
|
||||
visitBeginComponent(cmd: RenderBeginComponentCmd, context: BuildContext<N>): any {
|
||||
var el = this._beginElement(cmd, context);
|
||||
var templateId = cmd.templateId;
|
||||
var tpl = context.factory.resolveComponentTemplate(templateId);
|
||||
var el = this._beginElement(cmd, context, tpl);
|
||||
var root = el;
|
||||
if (cmd.nativeShadow) {
|
||||
root = context.factory.createShadowRoot(el, cmd.templateId);
|
||||
|
||||
if (tpl.encapsulation === ViewEncapsulation.Native) {
|
||||
root = context.factory.createShadowRoot(el, templateId);
|
||||
context.nativeShadowRoots.push(root);
|
||||
}
|
||||
var isRoot = context.componentCount === 0 && context.isHost;
|
||||
var component = new Component(el, root, cmd, isRoot);
|
||||
var component = new Component(el, root, isRoot, tpl);
|
||||
context.enqueueComponentBuilder(component);
|
||||
this.parentStack.push(component);
|
||||
return null;
|
||||
@ -187,18 +216,34 @@ class RenderViewBuilder<N> implements RenderCommandVisitor {
|
||||
this._addChild(el, cmd.ngContentIndex, context);
|
||||
context.boundElements.push(el);
|
||||
if (cmd.isMerged) {
|
||||
context.enqueueFragmentBuilder(this.parentComponent, cmd.children);
|
||||
context.enqueueFragmentBuilder(this.parentComponent, this.template, cmd.children);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private _beginElement(cmd: RenderBeginElementCmd, context: BuildContext<N>): N {
|
||||
private _beginElement(cmd: RenderBeginElementCmd, context: BuildContext<N>,
|
||||
componentTemplate: RenderComponentTemplate): N {
|
||||
var el: N = context.consumeInplaceElement();
|
||||
var attrNameAndValues = cmd.attrNameAndValues;
|
||||
if (this.template.encapsulation === ViewEncapsulation.Emulated) {
|
||||
// Note: Need to clone attrNameAndValues to make it writable!
|
||||
if (isPresent(componentTemplate)) {
|
||||
attrNameAndValues = attrNameAndValues.concat([
|
||||
_shimContentAttribute(this.template.shortId),
|
||||
'',
|
||||
_shimHostAttribute(componentTemplate.shortId),
|
||||
''
|
||||
]);
|
||||
} else {
|
||||
attrNameAndValues =
|
||||
attrNameAndValues.concat([_shimContentAttribute(this.template.shortId), '']);
|
||||
}
|
||||
}
|
||||
if (isPresent(el)) {
|
||||
context.factory.mergeElement(el, cmd.attrNameAndValues);
|
||||
context.factory.mergeElement(el, attrNameAndValues);
|
||||
this.fragmentRootNodes.push(el);
|
||||
} else {
|
||||
el = context.factory.createElement(cmd.name, cmd.attrNameAndValues);
|
||||
el = context.factory.createElement(cmd.name, attrNameAndValues);
|
||||
this._addChild(el, cmd.ngContentIndex, context);
|
||||
}
|
||||
if (cmd.isBound) {
|
||||
@ -232,11 +277,11 @@ class RenderViewBuilder<N> implements RenderCommandVisitor {
|
||||
class Component<N> {
|
||||
private contentNodesByNgContentIndex: N[][] = [];
|
||||
|
||||
constructor(public hostElement: N, public shadowRoot: N, public cmd: RenderBeginComponentCmd,
|
||||
public isRoot: boolean) {}
|
||||
constructor(public hostElement: N, public shadowRoot: N, public isRoot: boolean,
|
||||
public template: RenderComponentTemplate) {}
|
||||
addContentNode(ngContentIndex: number, node: N, context: BuildContext<N>) {
|
||||
if (isBlank(ngContentIndex)) {
|
||||
if (this.cmd.nativeShadow) {
|
||||
if (this.template.encapsulation === ViewEncapsulation.Native) {
|
||||
context.factory.appendChild(this.hostElement, node);
|
||||
}
|
||||
} else {
|
||||
@ -252,3 +297,16 @@ class Component<N> {
|
||||
[];
|
||||
}
|
||||
}
|
||||
|
||||
var COMPONENT_REGEX = /%COMP%/g;
|
||||
export const COMPONENT_VARIABLE = '%COMP%';
|
||||
export const HOST_ATTR = `_nghost-${COMPONENT_VARIABLE}`;
|
||||
export const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`;
|
||||
|
||||
function _shimContentAttribute(componentShortId: string): string {
|
||||
return StringWrapper.replaceAll(CONTENT_ATTR, COMPONENT_REGEX, componentShortId);
|
||||
}
|
||||
|
||||
function _shimHostAttribute(componentShortId: string): string {
|
||||
return StringWrapper.replaceAll(HOST_ATTR, COMPONENT_REGEX, componentShortId);
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ export class WebWorkerEndElementCmd implements RenderTemplateCmd {
|
||||
export class WebWorkerBeginComponentCmd implements RenderBeginComponentCmd {
|
||||
constructor(public isBound: boolean, public ngContentIndex: number, public name: string,
|
||||
public attrNameAndValues: string[], public eventTargetAndNames: string[],
|
||||
public nativeShadow: boolean, public templateId: number) {}
|
||||
public templateId: string) {}
|
||||
visit(visitor: RenderCommandVisitor, context: any): any {
|
||||
return visitor.visitBeginComponent(this, context);
|
||||
}
|
||||
|
@ -13,7 +13,8 @@ import {
|
||||
RenderNgContentCmd,
|
||||
RenderBeginElementCmd,
|
||||
RenderBeginComponentCmd,
|
||||
RenderEmbeddedTemplateCmd
|
||||
RenderEmbeddedTemplateCmd,
|
||||
RenderComponentTemplate
|
||||
} from "angular2/src/core/render/api";
|
||||
import {
|
||||
WebWorkerElementRef,
|
||||
@ -31,6 +32,7 @@ import {RenderProtoViewRefStore} from 'angular2/src/web_workers/shared/render_pr
|
||||
import {
|
||||
RenderViewWithFragmentsStore
|
||||
} from 'angular2/src/web_workers/shared/render_view_with_fragments_store';
|
||||
import {ViewEncapsulation, VIEW_ENCAPSULATION_VALUES} from 'angular2/src/core/metadata/view';
|
||||
|
||||
// PRIMITIVE is any type that does not need to be serialized (string, number, boolean)
|
||||
// We set it to String so that it is considered a Type.
|
||||
@ -41,7 +43,7 @@ export class Serializer {
|
||||
constructor(private _protoViewStore: RenderProtoViewRefStore,
|
||||
private _renderViewStore: RenderViewWithFragmentsStore) {}
|
||||
|
||||
serialize(obj: any, type: Type): Object {
|
||||
serialize(obj: any, type: any): Object {
|
||||
if (!isPresent(obj)) {
|
||||
return null;
|
||||
}
|
||||
@ -61,12 +63,16 @@ export class Serializer {
|
||||
return this._serializeWorkerElementRef(obj);
|
||||
} else if (type == WebWorkerTemplateCmd) {
|
||||
return serializeTemplateCmd(obj);
|
||||
} else if (type === RenderComponentTemplate) {
|
||||
return this._serializeRenderTemplate(obj);
|
||||
} else if (type === ViewEncapsulation) {
|
||||
return serializeEnum(obj);
|
||||
} else {
|
||||
throw new BaseException("No serializer for " + type.toString());
|
||||
}
|
||||
}
|
||||
|
||||
deserialize(map: any, type: Type, data?: any): any {
|
||||
deserialize(map: any, type: any, data?: any): any {
|
||||
if (!isPresent(map)) {
|
||||
return null;
|
||||
}
|
||||
@ -89,6 +95,10 @@ export class Serializer {
|
||||
return this._deserializeWorkerElementRef(map);
|
||||
} else if (type == WebWorkerTemplateCmd) {
|
||||
return deserializeTemplateCmd(map);
|
||||
} else if (type === RenderComponentTemplate) {
|
||||
return this._deserializeRenderTemplate(map);
|
||||
} else if (type === ViewEncapsulation) {
|
||||
return VIEW_ENCAPSULATION_VALUES[map];
|
||||
} else {
|
||||
throw new BaseException("No deserializer for " + type.toString());
|
||||
}
|
||||
@ -137,8 +147,27 @@ export class Serializer {
|
||||
return new WebWorkerElementRef(this.deserialize(map['renderView'], RenderViewRef),
|
||||
map['boundElementIndex']);
|
||||
}
|
||||
|
||||
|
||||
private _serializeRenderTemplate(obj: RenderComponentTemplate): Object {
|
||||
return {
|
||||
'id': obj.id,
|
||||
'shortId': obj.shortId,
|
||||
'encapsulation': this.serialize(obj.encapsulation, ViewEncapsulation),
|
||||
'commands': this.serialize(obj.commands, WebWorkerTemplateCmd),
|
||||
'styles': this.serialize(obj.styles, PRIMITIVE)
|
||||
};
|
||||
}
|
||||
|
||||
private _deserializeRenderTemplate(map: {[key: string]: any}): RenderComponentTemplate {
|
||||
return new RenderComponentTemplate(map['id'], map['shortId'],
|
||||
this.deserialize(map['encapsulation'], ViewEncapsulation),
|
||||
this.deserialize(map['commands'], WebWorkerTemplateCmd),
|
||||
this.deserialize(map['styles'], PRIMITIVE));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function serializeTemplateCmd(cmd: RenderTemplateCmd): Object {
|
||||
return cmd.visit(RENDER_TEMPLATE_CMD_SERIALIZER, null);
|
||||
}
|
||||
@ -178,7 +207,6 @@ class RenderTemplateCmdSerializer implements RenderCommandVisitor {
|
||||
'name': cmd.name,
|
||||
'attrNameAndValues': cmd.attrNameAndValues,
|
||||
'eventTargetAndNames': cmd.eventTargetAndNames,
|
||||
'nativeShadow': cmd.nativeShadow,
|
||||
'templateId': cmd.templateId
|
||||
};
|
||||
}
|
||||
@ -210,7 +238,7 @@ var RENDER_TEMPLATE_CMD_DESERIALIZERS = [
|
||||
(data: {[key: string]: any}) => new WebWorkerEndElementCmd(),
|
||||
(data: {[key: string]: any}) => new WebWorkerBeginComponentCmd(
|
||||
data['isBound'], data['ngContentIndex'], data['name'], data['attrNameAndValues'],
|
||||
data['eventTargetAndNames'], data['nativeShadow'], data['templateId']),
|
||||
data['eventTargetAndNames'], data['templateId']),
|
||||
(data: {[key: string]: any}) => new WebWorkerEndComponentCmd(),
|
||||
(data: {[key: string]: any}) => new WebWorkerEmbeddedTemplateCmd(
|
||||
data['isBound'], data['ngContentIndex'], data['name'], data['attrNameAndValues'],
|
||||
|
@ -6,7 +6,8 @@ import {
|
||||
RenderFragmentRef,
|
||||
RenderProtoViewRef,
|
||||
Renderer,
|
||||
RenderTemplateCmd
|
||||
RenderTemplateCmd,
|
||||
RenderComponentTemplate
|
||||
} from 'angular2/src/core/render/api';
|
||||
import {WebWorkerElementRef, WebWorkerTemplateCmd} from 'angular2/src/web_workers/shared/api';
|
||||
import {EVENT_CHANNEL, RENDERER_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api';
|
||||
@ -31,10 +32,9 @@ export class MessageBasedRenderer {
|
||||
var broker = this._brokerFactory.createMessageBroker(RENDERER_CHANNEL);
|
||||
this._bus.initChannel(EVENT_CHANNEL);
|
||||
|
||||
broker.registerMethod("registerComponentTemplate",
|
||||
[PRIMITIVE, WebWorkerTemplateCmd, PRIMITIVE, PRIMITIVE],
|
||||
broker.registerMethod("registerComponentTemplate", [RenderComponentTemplate],
|
||||
bind(this._renderer.registerComponentTemplate, this._renderer));
|
||||
broker.registerMethod("createProtoView", [WebWorkerTemplateCmd, PRIMITIVE],
|
||||
broker.registerMethod("createProtoView", [PRIMITIVE, WebWorkerTemplateCmd, PRIMITIVE],
|
||||
bind(this._createProtoView, this));
|
||||
broker.registerMethod("createRootHostView",
|
||||
[RenderProtoViewRef, PRIMITIVE, PRIMITIVE, PRIMITIVE],
|
||||
@ -73,8 +73,9 @@ export class MessageBasedRenderer {
|
||||
this._renderViewWithFragmentsStore.remove(viewRef);
|
||||
}
|
||||
|
||||
private _createProtoView(cmds: RenderTemplateCmd[], refIndex: number) {
|
||||
var protoViewRef = this._renderer.createProtoView(cmds);
|
||||
private _createProtoView(componentTemplateId: string, cmds: RenderTemplateCmd[],
|
||||
refIndex: number) {
|
||||
var protoViewRef = this._renderer.createProtoView(componentTemplateId, cmds);
|
||||
this._renderProtoViewRefStore.store(protoViewRef, refIndex);
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,8 @@ import {
|
||||
RenderEventDispatcher,
|
||||
RenderViewWithFragments,
|
||||
RenderFragmentRef,
|
||||
RenderTemplateCmd
|
||||
RenderTemplateCmd,
|
||||
RenderComponentTemplate
|
||||
} from 'angular2/src/core/render/api';
|
||||
import {
|
||||
ClientMessageBroker,
|
||||
@ -35,23 +36,20 @@ export class WebWorkerRenderer implements Renderer {
|
||||
this._messageBroker = messageBrokerFactory.createMessageBroker(RENDERER_CHANNEL);
|
||||
}
|
||||
|
||||
registerComponentTemplate(templateId: number, commands: RenderTemplateCmd[], styles: string[],
|
||||
nativeShadow: boolean) {
|
||||
var fnArgs = [
|
||||
new FnArg(templateId, null),
|
||||
new FnArg(commands, WebWorkerTemplateCmd),
|
||||
new FnArg(styles, null),
|
||||
new FnArg(nativeShadow, null)
|
||||
];
|
||||
registerComponentTemplate(template: RenderComponentTemplate) {
|
||||
var fnArgs = [new FnArg(template, RenderComponentTemplate)];
|
||||
var args = new UiArguments("registerComponentTemplate", fnArgs);
|
||||
this._messageBroker.runOnService(args, null);
|
||||
}
|
||||
|
||||
createProtoView(cmds: RenderTemplateCmd[]): RenderProtoViewRef {
|
||||
createProtoView(componentTemplateId: string, cmds: RenderTemplateCmd[]): RenderProtoViewRef {
|
||||
var renderProtoViewRef = this._renderProtoViewRefStore.allocate();
|
||||
|
||||
var fnArgs: FnArg[] =
|
||||
[new FnArg(cmds, WebWorkerTemplateCmd), new FnArg(renderProtoViewRef, RenderProtoViewRef)];
|
||||
var fnArgs: FnArg[] = [
|
||||
new FnArg(componentTemplateId, null),
|
||||
new FnArg(cmds, WebWorkerTemplateCmd),
|
||||
new FnArg(renderProtoViewRef, RenderProtoViewRef)
|
||||
];
|
||||
var args: UiArguments = new UiArguments("createProtoView", fnArgs);
|
||||
this._messageBroker.runOnService(args, null);
|
||||
return renderProtoViewRef;
|
||||
|
Reference in New Issue
Block a user