| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @license | 
					
						
							|  |  |  |  * Copyright Google Inc. All Rights Reserved. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Use of this source code is governed by an MIT-style license that can be | 
					
						
							|  |  |  |  * found in the LICENSE file at https://angular.io/license
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-24 11:34:11 -07:00
										 |  |  | import {flatten, sanitizeIdentifier} from '../../compile_metadata'; | 
					
						
							|  |  |  | import {CompileReflector} from '../../compile_reflector'; | 
					
						
							|  |  |  | import {BindingForm, BuiltinFunctionCall, LocalResolver, convertActionBinding, convertPropertyBinding} from '../../compiler_util/expression_converter'; | 
					
						
							|  |  |  | import {ConstantPool} from '../../constant_pool'; | 
					
						
							|  |  |  | import * as core from '../../core'; | 
					
						
							| 
									
										
										
										
											2018-05-15 21:07:59 +02:00
										 |  |  | import {AST, AstMemoryEfficientTransformer, BindingPipe, BindingType, FunctionCall, ImplicitReceiver, Interpolation, LiteralArray, LiteralMap, LiteralPrimitive, PropertyRead} from '../../expression_parser/ast'; | 
					
						
							| 
									
										
										
										
											2018-05-09 08:35:25 -07:00
										 |  |  | import {Lexer} from '../../expression_parser/lexer'; | 
					
						
							|  |  |  | import {Parser} from '../../expression_parser/parser'; | 
					
						
							|  |  |  | import * as html from '../../ml_parser/ast'; | 
					
						
							|  |  |  | import {HtmlParser} from '../../ml_parser/html_parser'; | 
					
						
							| 
									
										
										
										
											2018-05-21 08:15:19 -07:00
										 |  |  | import {WhitespaceVisitor} from '../../ml_parser/html_whitespaces'; | 
					
						
							| 
									
										
										
										
											2018-05-09 08:35:25 -07:00
										 |  |  | import {DEFAULT_INTERPOLATION_CONFIG} from '../../ml_parser/interpolation_config'; | 
					
						
							| 
									
										
										
										
											2018-06-08 09:00:01 -07:00
										 |  |  | import {splitNsName} from '../../ml_parser/tags'; | 
					
						
							| 
									
										
										
										
											2018-04-24 11:34:11 -07:00
										 |  |  | import * as o from '../../output/output_ast'; | 
					
						
							| 
									
										
										
										
											2018-05-09 08:35:25 -07:00
										 |  |  | import {ParseError, ParseSourceSpan} from '../../parse_util'; | 
					
						
							|  |  |  | import {DomElementSchemaRegistry} from '../../schema/dom_element_schema_registry'; | 
					
						
							| 
									
										
										
										
											2018-04-24 11:34:11 -07:00
										 |  |  | import {CssSelector, SelectorMatcher} from '../../selector'; | 
					
						
							| 
									
										
										
										
											2018-05-09 08:35:25 -07:00
										 |  |  | import {BindingParser} from '../../template_parser/binding_parser'; | 
					
						
							| 
									
										
										
										
											2018-04-24 11:34:11 -07:00
										 |  |  | import {OutputContext, error} from '../../util'; | 
					
						
							|  |  |  | import * as t from '../r3_ast'; | 
					
						
							|  |  |  | import {Identifiers as R3} from '../r3_identifiers'; | 
					
						
							| 
									
										
										
										
											2018-05-09 08:35:25 -07:00
										 |  |  | import {htmlAstToRender3Ast} from '../r3_template_transform'; | 
					
						
							| 
									
										
										
										
											2018-04-24 11:34:11 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | import {R3QueryMetadata} from './api'; | 
					
						
							| 
									
										
										
										
											2018-06-19 12:45:00 -07:00
										 |  |  | import {parseStyle} from './styling'; | 
					
						
							| 
									
										
										
										
											2018-04-24 11:34:11 -07:00
										 |  |  | import {CONTEXT_NAME, I18N_ATTR, I18N_ATTR_PREFIX, ID_SEPARATOR, IMPLICIT_REFERENCE, MEANING_SEPARATOR, REFERENCE_PREFIX, RENDER_FLAGS, TEMPORARY_NAME, asLiteral, getQueryPredicate, invalid, mapToExpression, noop, temporaryAllocator, trimTrailingNulls, unsupported} from './util'; | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-20 16:00:48 -07:00
										 |  |  | function mapBindingToInstruction(type: BindingType): o.ExternalReference|undefined { | 
					
						
							|  |  |  |   switch (type) { | 
					
						
							|  |  |  |     case BindingType.Property: | 
					
						
							|  |  |  |       return R3.elementProperty; | 
					
						
							|  |  |  |     case BindingType.Attribute: | 
					
						
							|  |  |  |       return R3.elementAttribute; | 
					
						
							|  |  |  |     case BindingType.Class: | 
					
						
							|  |  |  |       return R3.elementClassNamed; | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  |       return undefined; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-06 12:52:17 -07:00
										 |  |  | // `className` is used below instead of `class` because the interception
 | 
					
						
							|  |  |  | // code (where this map is used) deals with DOM element property values
 | 
					
						
							|  |  |  | // (like elm.propName) and not component bindining properties (like [propName]).
 | 
					
						
							|  |  |  | const SPECIAL_CASED_PROPERTIES_INSTRUCTION_MAP: {[index: string]: o.ExternalReference} = { | 
					
						
							| 
									
										
										
										
											2018-06-19 12:45:00 -07:00
										 |  |  |   'className': R3.elementClass | 
					
						
							| 
									
										
										
										
											2018-04-06 12:52:17 -07:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-24 11:34:11 -07:00
										 |  |  | export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver { | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  |   private _dataIndex = 0; | 
					
						
							|  |  |  |   private _bindingContext = 0; | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |   private _prefixCode: o.Statement[] = []; | 
					
						
							|  |  |  |   private _creationCode: o.Statement[] = []; | 
					
						
							|  |  |  |   private _variableCode: o.Statement[] = []; | 
					
						
							|  |  |  |   private _bindingCode: o.Statement[] = []; | 
					
						
							|  |  |  |   private _postfixCode: o.Statement[] = []; | 
					
						
							|  |  |  |   private _temporary = temporaryAllocator(this._prefixCode, TEMPORARY_NAME); | 
					
						
							| 
									
										
										
										
											2018-02-14 17:12:05 -08:00
										 |  |  |   private _valueConverter: ValueConverter; | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |   private _unsupported = unsupported; | 
					
						
							|  |  |  |   private _bindingScope: BindingScope; | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-14 10:54:00 -08:00
										 |  |  |   // Whether we are inside a translatable element (`<p i18n>... somewhere here ... </p>)
 | 
					
						
							|  |  |  |   private _inI18nSection: boolean = false; | 
					
						
							|  |  |  |   private _i18nSectionIndex = -1; | 
					
						
							|  |  |  |   // Maps of placeholder to node indexes for each of the i18n section
 | 
					
						
							|  |  |  |   private _phToNodeIdxes: {[phName: string]: number[]}[] = [{}]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-21 15:59:25 -07:00
										 |  |  |   // Number of slots to reserve for pureFunctions
 | 
					
						
							|  |  |  |   private _pureFunctionSlots = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  |   constructor( | 
					
						
							| 
									
										
										
										
											2018-04-24 11:34:11 -07:00
										 |  |  |       private constantPool: ConstantPool, private contextParameter: string, | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |       parentBindingScope: BindingScope, private level = 0, private contextName: string|null, | 
					
						
							| 
									
										
										
										
											2018-04-24 11:34:11 -07:00
										 |  |  |       private templateName: string|null, private viewQueries: R3QueryMetadata[], | 
					
						
							|  |  |  |       private directiveMatcher: SelectorMatcher|null, private directives: Set<o.Expression>, | 
					
						
							| 
									
										
										
										
											2018-06-08 09:00:01 -07:00
										 |  |  |       private pipeTypeByName: Map<string, o.Expression>, private pipes: Set<o.Expression>, | 
					
						
							|  |  |  |       private _namespace: o.ExternalReference) { | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |     this._bindingScope = | 
					
						
							| 
									
										
										
										
											2018-04-05 11:38:06 -07:00
										 |  |  |         parentBindingScope.nestedScope((lhsVar: o.ReadVarExpr, expression: o.Expression) => { | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |           this._bindingCode.push( | 
					
						
							| 
									
										
										
										
											2018-04-05 11:38:06 -07:00
										 |  |  |               lhsVar.set(expression).toDeclStmt(o.INFERRED_TYPE, [o.StmtModifier.Final])); | 
					
						
							|  |  |  |         }); | 
					
						
							| 
									
										
										
										
											2018-02-14 17:12:05 -08:00
										 |  |  |     this._valueConverter = new ValueConverter( | 
					
						
							| 
									
										
										
										
											2018-04-24 11:34:11 -07:00
										 |  |  |         constantPool, () => this.allocateDataSlot(), | 
					
						
							| 
									
										
										
										
											2018-05-21 15:59:25 -07:00
										 |  |  |         (numSlots: number): number => this._pureFunctionSlots += numSlots, | 
					
						
							| 
									
										
										
										
											2018-04-24 11:34:11 -07:00
										 |  |  |         (name, localName, slot, value: o.ReadVarExpr) => { | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |           const pipeType = pipeTypeByName.get(name); | 
					
						
							|  |  |  |           if (pipeType) { | 
					
						
							|  |  |  |             this.pipes.add(pipeType); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           this._bindingScope.set(localName, value); | 
					
						
							|  |  |  |           this._creationCode.push( | 
					
						
							| 
									
										
										
										
											2018-03-30 16:07:37 -07:00
										 |  |  |               o.importExpr(R3.pipe).callFn([o.literal(slot), o.literal(name)]).toStmt()); | 
					
						
							| 
									
										
										
										
											2018-02-05 17:31:12 -08:00
										 |  |  |         }); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |   buildTemplateFunction( | 
					
						
							|  |  |  |       nodes: t.Node[], variables: t.Variable[], hasNgContent: boolean = false, | 
					
						
							|  |  |  |       ngContentSelectors: string[] = []): o.FunctionExpr { | 
					
						
							| 
									
										
										
										
											2018-06-08 09:00:01 -07:00
										 |  |  |     if (this._namespace !== R3.namespaceHTML) { | 
					
						
							|  |  |  |       this.instruction(this._creationCode, null, this._namespace); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-24 11:19:39 -08:00
										 |  |  |     // Create variable bindings
 | 
					
						
							|  |  |  |     for (const variable of variables) { | 
					
						
							|  |  |  |       const variableName = variable.name; | 
					
						
							|  |  |  |       const expression = | 
					
						
							|  |  |  |           o.variable(this.contextParameter).prop(variable.value || IMPLICIT_REFERENCE); | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |       const scopedName = this._bindingScope.freshReferenceName(); | 
					
						
							| 
									
										
										
										
											2018-01-24 11:19:39 -08:00
										 |  |  |       // Add the reference to the local scope.
 | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |       this._bindingScope.set(variableName, o.variable(variableName + scopedName), expression); | 
					
						
							| 
									
										
										
										
											2018-01-24 11:19:39 -08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |     // Output a `ProjectionDef` instruction when some `<ng-content>` are present
 | 
					
						
							|  |  |  |     if (hasNgContent) { | 
					
						
							| 
									
										
										
										
											2018-07-03 20:04:36 -07:00
										 |  |  |       const parameters: o.Expression[] = []; | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |       // Only selectors with a non-default value are generated
 | 
					
						
							|  |  |  |       if (ngContentSelectors.length > 1) { | 
					
						
							| 
									
										
										
										
											2018-04-20 08:52:07 -07:00
										 |  |  |         const r3Selectors = ngContentSelectors.map(s => core.parseSelectorToR3Selector(s)); | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |         // `projectionDef` needs both the parsed and raw value of the selectors
 | 
					
						
							| 
									
										
										
										
											2018-04-24 11:34:11 -07:00
										 |  |  |         const parsed = this.constantPool.getConstLiteral(asLiteral(r3Selectors), true); | 
					
						
							|  |  |  |         const unParsed = this.constantPool.getConstLiteral(asLiteral(ngContentSelectors), true); | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |         parameters.push(parsed, unParsed); | 
					
						
							| 
									
										
										
										
											2018-01-26 17:12:39 -08:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |       this.instruction(this._creationCode, null, R3.projectionDef, ...parameters); | 
					
						
							| 
									
										
										
										
											2018-01-26 17:12:39 -08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-13 10:48:22 -08:00
										 |  |  |     // Define and update any view queries
 | 
					
						
							|  |  |  |     for (let query of this.viewQueries) { | 
					
						
							| 
									
										
										
										
											2018-04-13 15:12:11 -07:00
										 |  |  |       // e.g. r3.Q(0, somePredicate, true);
 | 
					
						
							| 
									
										
										
										
											2018-02-13 10:48:22 -08:00
										 |  |  |       const querySlot = this.allocateDataSlot(); | 
					
						
							| 
									
										
										
										
											2018-04-24 11:34:11 -07:00
										 |  |  |       const predicate = getQueryPredicate(query, this.constantPool); | 
					
						
							|  |  |  |       const args: o.Expression[] = [ | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |         o.literal(querySlot, o.INFERRED_TYPE), | 
					
						
							|  |  |  |         predicate, | 
					
						
							|  |  |  |         o.literal(query.descendants, o.INFERRED_TYPE), | 
					
						
							| 
									
										
										
										
											2018-02-13 10:48:22 -08:00
										 |  |  |       ]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (query.read) { | 
					
						
							| 
									
										
										
										
											2018-04-24 11:34:11 -07:00
										 |  |  |         args.push(query.read); | 
					
						
							| 
									
										
										
										
											2018-02-13 10:48:22 -08:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |       this.instruction(this._creationCode, null, R3.query, ...args); | 
					
						
							| 
									
										
										
										
											2018-02-13 10:48:22 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |       // (r3.qR(tmp = r3.ɵld(0)) && (ctx.someDir = tmp));
 | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |       const temporary = this._temporary(); | 
					
						
							| 
									
										
										
										
											2018-02-13 10:48:22 -08:00
										 |  |  |       const getQueryList = o.importExpr(R3.load).callFn([o.literal(querySlot)]); | 
					
						
							|  |  |  |       const refresh = o.importExpr(R3.queryRefresh).callFn([temporary.set(getQueryList)]); | 
					
						
							|  |  |  |       const updateDirective = o.variable(CONTEXT_NAME) | 
					
						
							|  |  |  |                                   .prop(query.propertyName) | 
					
						
							|  |  |  |                                   .set(query.first ? temporary.prop('first') : temporary); | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |       this._bindingCode.push(refresh.and(updateDirective).toStmt()); | 
					
						
							| 
									
										
										
										
											2018-02-13 10:48:22 -08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |     t.visitAll(this, nodes); | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-21 15:59:25 -07:00
										 |  |  |     if (this._pureFunctionSlots > 0) { | 
					
						
							|  |  |  |       this.instruction( | 
					
						
							|  |  |  |           this._creationCode, null, R3.reserveSlots, o.literal(this._pureFunctionSlots)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |     const creationCode = this._creationCode.length > 0 ? | 
					
						
							| 
									
										
										
										
											2018-04-10 20:57:20 -07:00
										 |  |  |         [o.ifStmt( | 
					
						
							| 
									
										
										
										
											2018-04-24 11:34:11 -07:00
										 |  |  |             o.variable(RENDER_FLAGS).bitwiseAnd(o.literal(core.RenderFlags.Create), null, false), | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |             this._creationCode)] : | 
					
						
							| 
									
										
										
										
											2018-04-10 20:57:20 -07:00
										 |  |  |         []; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |     const updateCode = this._bindingCode.length > 0 ? | 
					
						
							| 
									
										
										
										
											2018-04-10 20:57:20 -07:00
										 |  |  |         [o.ifStmt( | 
					
						
							| 
									
										
										
										
											2018-04-24 11:34:11 -07:00
										 |  |  |             o.variable(RENDER_FLAGS).bitwiseAnd(o.literal(core.RenderFlags.Update), null, false), | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |             this._bindingCode)] : | 
					
						
							| 
									
										
										
										
											2018-01-29 10:36:56 -08:00
										 |  |  |         []; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-14 10:54:00 -08:00
										 |  |  |     // Generate maps of placeholder name to node indexes
 | 
					
						
							|  |  |  |     // TODO(vicb): This is a WIP, not fully supported yet
 | 
					
						
							|  |  |  |     for (const phToNodeIdx of this._phToNodeIdxes) { | 
					
						
							|  |  |  |       if (Object.keys(phToNodeIdx).length > 0) { | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |         const scopedName = this._bindingScope.freshReferenceName(); | 
					
						
							| 
									
										
										
										
											2018-02-14 10:54:00 -08:00
										 |  |  |         const phMap = o.variable(scopedName) | 
					
						
							|  |  |  |                           .set(mapToExpression(phToNodeIdx, true)) | 
					
						
							|  |  |  |                           .toDeclStmt(o.INFERRED_TYPE, [o.StmtModifier.Final]); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |         this._prefixCode.push(phMap); | 
					
						
							| 
									
										
										
										
											2018-02-14 10:54:00 -08:00
										 |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  |     return o.fn( | 
					
						
							| 
									
										
										
										
											2018-04-10 20:57:20 -07:00
										 |  |  |         [new o.FnParam(RENDER_FLAGS, o.NUMBER_TYPE), new o.FnParam(this.contextParameter, null)], | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  |         [ | 
					
						
							| 
									
										
										
										
											2018-04-10 20:57:20 -07:00
										 |  |  |           // Temporary variable declarations for query refresh (i.e. let _t: any;)
 | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |           ...this._prefixCode, | 
					
						
							| 
									
										
										
										
											2018-04-10 20:57:20 -07:00
										 |  |  |           // Creating mode (i.e. if (rf & RenderFlags.Create) { ... })
 | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |           ...creationCode, | 
					
						
							| 
									
										
										
										
											2018-04-10 20:57:20 -07:00
										 |  |  |           // Temporary variable declarations for local refs (i.e. const tmp = ld(1) as any)
 | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |           ...this._variableCode, | 
					
						
							| 
									
										
										
										
											2018-04-10 20:57:20 -07:00
										 |  |  |           // Binding and refresh mode (i.e. if (rf & RenderFlags.Update) {...})
 | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |           ...updateCode, | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  |           // Nested templates (i.e. function CompTemplate() {})
 | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |           ...this._postfixCode | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  |         ], | 
					
						
							| 
									
										
										
										
											2018-01-22 15:35:18 -08:00
										 |  |  |         o.INFERRED_TYPE, null, this.templateName); | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-05 17:31:12 -08:00
										 |  |  |   // LocalResolver
 | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |   getLocal(name: string): o.Expression|null { return this._bindingScope.get(name); } | 
					
						
							| 
									
										
										
										
											2018-01-11 15:37:56 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |   visitContent(ngContent: t.Content) { | 
					
						
							|  |  |  |     const slot = this.allocateDataSlot(); | 
					
						
							|  |  |  |     const selectorIndex = ngContent.selectorIndex; | 
					
						
							| 
									
										
										
										
											2018-07-03 20:04:36 -07:00
										 |  |  |     const parameters: o.Expression[] = [o.literal(slot)]; | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     const attributeAsList: string[] = []; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ngContent.attributes.forEach((attribute) => { | 
					
						
							|  |  |  |       const name = attribute.name; | 
					
						
							|  |  |  |       if (name !== 'select') { | 
					
						
							|  |  |  |         attributeAsList.push(name, attribute.value); | 
					
						
							| 
									
										
										
										
											2018-04-16 11:49:11 -07:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (attributeAsList.length > 0) { | 
					
						
							|  |  |  |       parameters.push(o.literal(selectorIndex), asLiteral(attributeAsList)); | 
					
						
							|  |  |  |     } else if (selectorIndex !== 0) { | 
					
						
							|  |  |  |       parameters.push(o.literal(selectorIndex)); | 
					
						
							| 
									
										
										
										
											2018-01-26 17:12:39 -08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     this.instruction(this._creationCode, ngContent.sourceSpan, R3.projection, ...parameters); | 
					
						
							| 
									
										
										
										
											2018-01-26 17:12:39 -08:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-08 09:00:01 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   getNamespaceInstruction(namespaceKey: string|null) { | 
					
						
							|  |  |  |     switch (namespaceKey) { | 
					
						
							|  |  |  |       case 'math': | 
					
						
							|  |  |  |         return R3.namespaceMathML; | 
					
						
							|  |  |  |       case 'svg': | 
					
						
							|  |  |  |         return R3.namespaceSVG; | 
					
						
							|  |  |  |       default: | 
					
						
							|  |  |  |         return R3.namespaceHTML; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   addNamespaceInstruction(nsInstruction: o.ExternalReference, element: t.Element) { | 
					
						
							|  |  |  |     this._namespace = nsInstruction; | 
					
						
							|  |  |  |     this.instruction(this._creationCode, element.sourceSpan, nsInstruction); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |   visitElement(element: t.Element) { | 
					
						
							| 
									
										
										
										
											2018-01-11 15:37:56 -08:00
										 |  |  |     const elementIndex = this.allocateDataSlot(); | 
					
						
							|  |  |  |     const referenceDataSlots = new Map<string, number>(); | 
					
						
							| 
									
										
										
										
											2018-02-14 10:54:00 -08:00
										 |  |  |     const wasInI18nSection = this._inI18nSection; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const outputAttrs: {[name: string]: string} = {}; | 
					
						
							|  |  |  |     const attrI18nMetas: {[name: string]: string} = {}; | 
					
						
							| 
									
										
										
										
											2018-06-06 13:38:00 -07:00
										 |  |  |     let i18nMeta: string = ''; | 
					
						
							| 
									
										
										
										
											2018-02-14 10:54:00 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-08 09:00:01 -07:00
										 |  |  |     const [namespaceKey, elementName] = splitNsName(element.name); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-14 10:54:00 -08:00
										 |  |  |     // Elements inside i18n sections are replaced with placeholders
 | 
					
						
							|  |  |  |     // TODO(vicb): nested elements are a WIP in this phase
 | 
					
						
							|  |  |  |     if (this._inI18nSection) { | 
					
						
							|  |  |  |       const phName = element.name.toLowerCase(); | 
					
						
							|  |  |  |       if (!this._phToNodeIdxes[this._i18nSectionIndex][phName]) { | 
					
						
							|  |  |  |         this._phToNodeIdxes[this._i18nSectionIndex][phName] = []; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       this._phToNodeIdxes[this._i18nSectionIndex][phName].push(elementIndex); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Handle i18n attributes
 | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |     for (const attr of element.attributes) { | 
					
						
							| 
									
										
										
										
											2018-02-14 10:54:00 -08:00
										 |  |  |       const name = attr.name; | 
					
						
							|  |  |  |       const value = attr.value; | 
					
						
							|  |  |  |       if (name === I18N_ATTR) { | 
					
						
							|  |  |  |         if (this._inI18nSection) { | 
					
						
							|  |  |  |           throw new Error( | 
					
						
							|  |  |  |               `Could not mark an element as translatable inside of a translatable section`); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         this._inI18nSection = true; | 
					
						
							|  |  |  |         this._i18nSectionIndex++; | 
					
						
							|  |  |  |         this._phToNodeIdxes[this._i18nSectionIndex] = {}; | 
					
						
							|  |  |  |         i18nMeta = value; | 
					
						
							|  |  |  |       } else if (name.startsWith(I18N_ATTR_PREFIX)) { | 
					
						
							|  |  |  |         attrI18nMetas[name.slice(I18N_ATTR_PREFIX.length)] = value; | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         outputAttrs[name] = value; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |     // Match directives on non i18n attributes
 | 
					
						
							|  |  |  |     if (this.directiveMatcher) { | 
					
						
							|  |  |  |       const selector = createCssSelector(element.name, outputAttrs); | 
					
						
							|  |  |  |       this.directiveMatcher.match( | 
					
						
							|  |  |  |           selector, (sel: CssSelector, staticType: any) => { this.directives.add(staticType); }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  |     // Element creation mode
 | 
					
						
							| 
									
										
										
										
											2018-04-13 15:12:11 -07:00
										 |  |  |     const parameters: o.Expression[] = [ | 
					
						
							|  |  |  |       o.literal(elementIndex), | 
					
						
							| 
									
										
										
										
											2018-06-08 09:00:01 -07:00
										 |  |  |       o.literal(elementName), | 
					
						
							| 
									
										
										
										
											2018-04-13 15:12:11 -07:00
										 |  |  |     ]; | 
					
						
							| 
									
										
										
										
											2018-01-11 15:37:56 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-14 10:54:00 -08:00
										 |  |  |     // Add the attributes
 | 
					
						
							|  |  |  |     const i18nMessages: o.Statement[] = []; | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  |     const attributes: o.Expression[] = []; | 
					
						
							| 
									
										
										
										
											2018-06-19 12:45:00 -07:00
										 |  |  |     const initialStyleDeclarations: o.Expression[] = []; | 
					
						
							| 
									
										
										
										
											2018-02-14 10:54:00 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-19 12:45:00 -07:00
										 |  |  |     const styleInputs: t.BoundAttribute[] = []; | 
					
						
							|  |  |  |     const allOtherInputs: t.BoundAttribute[] = []; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     element.inputs.forEach((input: t.BoundAttribute) => { | 
					
						
							|  |  |  |       // [attr.style] should not be treated as a styling-based
 | 
					
						
							|  |  |  |       // binding since it is intended to write directly to the attr
 | 
					
						
							|  |  |  |       // and therefore will skip all style resolution that is present
 | 
					
						
							|  |  |  |       // with style="", [style]="" and [style.prop]="" assignments
 | 
					
						
							|  |  |  |       if (input.name == 'style' && input.type == BindingType.Property) { | 
					
						
							|  |  |  |         // this should always go first in the compilation (for [style])
 | 
					
						
							|  |  |  |         styleInputs.splice(0, 0, input); | 
					
						
							|  |  |  |       } else if (input.type == BindingType.Style) { | 
					
						
							|  |  |  |         styleInputs.push(input); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         allOtherInputs.push(input); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     let currStyleIndex = 0; | 
					
						
							|  |  |  |     let staticStylesMap: {[key: string]: any}|null = null; | 
					
						
							|  |  |  |     const stylesIndexMap: {[key: string]: number} = {}; | 
					
						
							| 
									
										
										
										
											2018-02-14 10:54:00 -08:00
										 |  |  |     Object.getOwnPropertyNames(outputAttrs).forEach(name => { | 
					
						
							|  |  |  |       const value = outputAttrs[name]; | 
					
						
							| 
									
										
										
										
											2018-06-19 12:45:00 -07:00
										 |  |  |       if (name == 'style') { | 
					
						
							|  |  |  |         staticStylesMap = parseStyle(value); | 
					
						
							|  |  |  |         Object.keys(staticStylesMap).forEach(prop => { stylesIndexMap[prop] = currStyleIndex++; }); | 
					
						
							| 
									
										
										
										
											2018-02-14 10:54:00 -08:00
										 |  |  |       } else { | 
					
						
							| 
									
										
										
										
											2018-06-19 12:45:00 -07:00
										 |  |  |         attributes.push(o.literal(name)); | 
					
						
							|  |  |  |         if (attrI18nMetas.hasOwnProperty(name)) { | 
					
						
							|  |  |  |           const meta = parseI18nMeta(attrI18nMetas[name]); | 
					
						
							|  |  |  |           const variable = this.constantPool.getTranslation(value, meta); | 
					
						
							|  |  |  |           attributes.push(variable); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           attributes.push(o.literal(value)); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-02-14 10:54:00 -08:00
										 |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-19 12:45:00 -07:00
										 |  |  |     for (let i = 0; i < styleInputs.length; i++) { | 
					
						
							|  |  |  |       const input = styleInputs[i]; | 
					
						
							|  |  |  |       const isMapBasedStyleBinding = i === 0 && input.name === 'style'; | 
					
						
							|  |  |  |       if (!isMapBasedStyleBinding && !stylesIndexMap.hasOwnProperty(input.name)) { | 
					
						
							|  |  |  |         stylesIndexMap[input.name] = currStyleIndex++; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // this will build the instructions so that they fall into the following syntax
 | 
					
						
							|  |  |  |     // => [prop1, prop2, prop3, 0, prop1, value1, prop2, value2]
 | 
					
						
							|  |  |  |     Object.keys(stylesIndexMap).forEach(prop => { | 
					
						
							|  |  |  |       initialStyleDeclarations.push(o.literal(prop)); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (staticStylesMap) { | 
					
						
							|  |  |  |       initialStyleDeclarations.push(o.literal(core.InitialStylingFlags.INITIAL_STYLES)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       Object.keys(staticStylesMap).forEach(prop => { | 
					
						
							|  |  |  |         initialStyleDeclarations.push(o.literal(prop)); | 
					
						
							|  |  |  |         const value = staticStylesMap ![prop]; | 
					
						
							|  |  |  |         initialStyleDeclarations.push(o.literal(value)); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-10 15:58:27 -07:00
										 |  |  |     const attrArg: o.Expression = attributes.length > 0 ? | 
					
						
							|  |  |  |         this.constantPool.getConstLiteral(o.literalArr(attributes), true) : | 
					
						
							|  |  |  |         o.TYPED_NULL_EXPR; | 
					
						
							| 
									
										
										
										
											2018-02-14 10:54:00 -08:00
										 |  |  |     parameters.push(attrArg); | 
					
						
							| 
									
										
										
										
											2018-01-11 15:37:56 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-14 10:54:00 -08:00
										 |  |  |     if (element.references && element.references.length > 0) { | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |       const references = flatten(element.references.map(reference => { | 
					
						
							|  |  |  |         const slot = this.allocateDataSlot(); | 
					
						
							|  |  |  |         referenceDataSlots.set(reference.name, slot); | 
					
						
							|  |  |  |         // Generate the update temporary.
 | 
					
						
							|  |  |  |         const variableName = this._bindingScope.freshReferenceName(); | 
					
						
							|  |  |  |         this._variableCode.push(o.variable(variableName, o.INFERRED_TYPE) | 
					
						
							|  |  |  |                                     .set(o.importExpr(R3.load).callFn([o.literal(slot)])) | 
					
						
							|  |  |  |                                     .toDeclStmt(o.INFERRED_TYPE, [o.StmtModifier.Final])); | 
					
						
							|  |  |  |         this._bindingScope.set(reference.name, o.variable(variableName)); | 
					
						
							|  |  |  |         return [reference.name, reference.value]; | 
					
						
							|  |  |  |       })); | 
					
						
							|  |  |  |       parameters.push(this.constantPool.getConstLiteral(asLiteral(references), true)); | 
					
						
							| 
									
										
										
										
											2018-01-11 15:37:56 -08:00
										 |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2018-04-13 15:12:11 -07:00
										 |  |  |       parameters.push(o.TYPED_NULL_EXPR); | 
					
						
							| 
									
										
										
										
											2018-01-11 15:37:56 -08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-11 15:37:56 -08:00
										 |  |  |     // Generate the instruction create element instruction
 | 
					
						
							| 
									
										
										
										
											2018-02-14 10:54:00 -08:00
										 |  |  |     if (i18nMessages.length > 0) { | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |       this._creationCode.push(...i18nMessages); | 
					
						
							| 
									
										
										
										
											2018-02-14 10:54:00 -08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-06-08 09:00:01 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     const wasInNamespace = this._namespace; | 
					
						
							|  |  |  |     const currentNamespace = this.getNamespaceInstruction(namespaceKey); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // If the namespace is changing now, include an instruction to change it
 | 
					
						
							|  |  |  |     // during element creation.
 | 
					
						
							|  |  |  |     if (currentNamespace !== wasInNamespace) { | 
					
						
							|  |  |  |       this.addNamespaceInstruction(currentNamespace, element); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-05 11:38:06 -07:00
										 |  |  |     const implicit = o.variable(CONTEXT_NAME); | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-19 12:45:00 -07:00
										 |  |  |     const elementStyleIndex = | 
					
						
							|  |  |  |         (initialStyleDeclarations.length || styleInputs.length) ? this.allocateDataSlot() : 0; | 
					
						
							|  |  |  |     const createSelfClosingInstruction = | 
					
						
							|  |  |  |         elementStyleIndex === 0 && element.children.length === 0 && element.outputs.length === 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (createSelfClosingInstruction) { | 
					
						
							| 
									
										
										
										
											2018-05-25 16:23:00 -07:00
										 |  |  |       this.instruction( | 
					
						
							| 
									
										
										
										
											2018-06-08 10:48:27 -07:00
										 |  |  |           this._creationCode, element.sourceSpan, R3.element, ...trimTrailingNulls(parameters)); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       // Generate the instruction create element instruction
 | 
					
						
							|  |  |  |       if (i18nMessages.length > 0) { | 
					
						
							|  |  |  |         this._creationCode.push(...i18nMessages); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       this.instruction( | 
					
						
							|  |  |  |           this._creationCode, element.sourceSpan, R3.elementStart, | 
					
						
							|  |  |  |           ...trimTrailingNulls(parameters)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-19 12:45:00 -07:00
										 |  |  |       // initial styling for static style="..." attributes
 | 
					
						
							|  |  |  |       if (elementStyleIndex > 0) { | 
					
						
							|  |  |  |         let paramsList: (o.Expression)[] = [o.literal(elementStyleIndex)]; | 
					
						
							|  |  |  |         if (initialStyleDeclarations.length) { | 
					
						
							|  |  |  |           // the template compiler handles initial styling (e.g. style="foo") values
 | 
					
						
							|  |  |  |           // in a special command called `elementStyle` so that the initial styles
 | 
					
						
							|  |  |  |           // can be processed during runtime. These initial styles values are bound to
 | 
					
						
							|  |  |  |           // a constant because the inital style values do not change (since they're static).
 | 
					
						
							|  |  |  |           paramsList.push( | 
					
						
							|  |  |  |               this.constantPool.getConstLiteral(o.literalArr(initialStyleDeclarations), true)); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         this._creationCode.push(o.importExpr(R3.elementStyling).callFn(paramsList).toStmt()); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-08 10:48:27 -07:00
										 |  |  |       // Generate Listeners (outputs)
 | 
					
						
							|  |  |  |       element.outputs.forEach((outputAst: t.BoundEvent) => { | 
					
						
							|  |  |  |         const elName = sanitizeIdentifier(element.name); | 
					
						
							|  |  |  |         const evName = sanitizeIdentifier(outputAst.name); | 
					
						
							|  |  |  |         const functionName = `${this.templateName}_${elName}_${evName}_listener`; | 
					
						
							|  |  |  |         const localVars: o.Statement[] = []; | 
					
						
							|  |  |  |         const bindingScope = | 
					
						
							|  |  |  |             this._bindingScope.nestedScope((lhsVar: o.ReadVarExpr, rhsExpression: o.Expression) => { | 
					
						
							|  |  |  |               localVars.push( | 
					
						
							|  |  |  |                   lhsVar.set(rhsExpression).toDeclStmt(o.INFERRED_TYPE, [o.StmtModifier.Final])); | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |         const bindingExpr = convertActionBinding( | 
					
						
							|  |  |  |             bindingScope, implicit, outputAst.handler, 'b', | 
					
						
							|  |  |  |             () => error('Unexpected interpolation')); | 
					
						
							|  |  |  |         const handler = o.fn( | 
					
						
							|  |  |  |             [new o.FnParam('$event', o.DYNAMIC_TYPE)], [...localVars, ...bindingExpr.render3Stmts], | 
					
						
							|  |  |  |             o.INFERRED_TYPE, null, functionName); | 
					
						
							|  |  |  |         this.instruction( | 
					
						
							|  |  |  |             this._creationCode, outputAst.sourceSpan, R3.listener, o.literal(outputAst.name), | 
					
						
							|  |  |  |             handler); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-03-23 10:55:17 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-19 12:45:00 -07:00
										 |  |  |     if (styleInputs.length && elementStyleIndex > 0) { | 
					
						
							|  |  |  |       const indexLiteral = o.literal(elementStyleIndex); | 
					
						
							|  |  |  |       styleInputs.forEach((input, i) => { | 
					
						
							|  |  |  |         const isMapBasedStyleBinding = i == 0 && input.name == 'style'; | 
					
						
							|  |  |  |         const convertedBinding = this.convertPropertyBinding(implicit, input.value, true); | 
					
						
							|  |  |  |         if (isMapBasedStyleBinding) { | 
					
						
							|  |  |  |           this.instruction( | 
					
						
							|  |  |  |               this._bindingCode, input.sourceSpan, R3.elementStyle, indexLiteral, convertedBinding); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           const key = input.name; | 
					
						
							|  |  |  |           let styleIndex: number = stylesIndexMap[key] !; | 
					
						
							|  |  |  |           this.instruction( | 
					
						
							|  |  |  |               this._bindingCode, input.sourceSpan, R3.elementStyleProp, indexLiteral, | 
					
						
							|  |  |  |               o.literal(styleIndex), convertedBinding); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       const spanEnd = styleInputs[styleInputs.length - 1].sourceSpan; | 
					
						
							|  |  |  |       this.instruction(this._bindingCode, spanEnd, R3.elementStylingApply, indexLiteral); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  |     // Generate element input bindings
 | 
					
						
							| 
									
										
										
										
											2018-06-19 12:45:00 -07:00
										 |  |  |     allOtherInputs.forEach((input: t.BoundAttribute) => { | 
					
						
							| 
									
										
										
										
											2018-04-24 14:22:55 -07:00
										 |  |  |       if (input.type === BindingType.Animation) { | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |         this._unsupported('animations'); | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2018-06-19 12:45:00 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-05 17:31:12 -08:00
										 |  |  |       const convertedBinding = this.convertPropertyBinding(implicit, input.value); | 
					
						
							| 
									
										
										
										
											2018-04-06 12:52:17 -07:00
										 |  |  |       const specialInstruction = SPECIAL_CASED_PROPERTIES_INSTRUCTION_MAP[input.name]; | 
					
						
							|  |  |  |       if (specialInstruction) { | 
					
						
							|  |  |  |         // special case for [style] and [class] bindings since they are not handled as
 | 
					
						
							|  |  |  |         // standard properties within this implementation. Instead they are
 | 
					
						
							|  |  |  |         // handed off to special cased instruction handlers which will then
 | 
					
						
							|  |  |  |         // delegate them as animation sequences (or input bindings for dirs/cmps)
 | 
					
						
							|  |  |  |         this.instruction( | 
					
						
							|  |  |  |             this._bindingCode, input.sourceSpan, specialInstruction, o.literal(elementIndex), | 
					
						
							|  |  |  |             convertedBinding); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-20 16:00:48 -07:00
										 |  |  |       const instruction = mapBindingToInstruction(input.type); | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  |       if (instruction) { | 
					
						
							|  |  |  |         // TODO(chuckj): runtime: security context?
 | 
					
						
							|  |  |  |         this.instruction( | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |             this._bindingCode, input.sourceSpan, instruction, o.literal(elementIndex), | 
					
						
							| 
									
										
										
										
											2018-05-15 21:07:59 +02:00
										 |  |  |             o.literal(input.name), convertedBinding); | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  |       } else { | 
					
						
							| 
									
										
										
										
											2018-04-20 11:28:34 -07:00
										 |  |  |         this._unsupported(`binding type ${input.type}`); | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Traverse element child nodes
 | 
					
						
							| 
									
										
										
										
											2018-02-14 10:54:00 -08:00
										 |  |  |     if (this._inI18nSection && element.children.length == 1 && | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |         element.children[0] instanceof t.Text) { | 
					
						
							|  |  |  |       const text = element.children[0] as t.Text; | 
					
						
							| 
									
										
										
										
											2018-02-14 10:54:00 -08:00
										 |  |  |       this.visitSingleI18nTextChild(text, i18nMeta); | 
					
						
							|  |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |       t.visitAll(this, element.children); | 
					
						
							| 
									
										
										
										
											2018-02-14 10:54:00 -08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-19 12:45:00 -07:00
										 |  |  |     if (!createSelfClosingInstruction) { | 
					
						
							| 
									
										
										
										
											2018-06-08 10:48:27 -07:00
										 |  |  |       // Finish element construction mode.
 | 
					
						
							|  |  |  |       this.instruction( | 
					
						
							|  |  |  |           this._creationCode, element.endSourceSpan || element.sourceSpan, R3.elementEnd); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-06-06 13:38:19 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-14 10:54:00 -08:00
										 |  |  |     // Restore the state before exiting this node
 | 
					
						
							|  |  |  |     this._inI18nSection = wasInI18nSection; | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |   visitTemplate(template: t.Template) { | 
					
						
							|  |  |  |     const templateIndex = this.allocateDataSlot(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     let elName = ''; | 
					
						
							|  |  |  |     if (template.children.length === 1 && template.children[0] instanceof t.Element) { | 
					
						
							|  |  |  |       // When the template as a single child, derive the context name from the tag
 | 
					
						
							|  |  |  |       elName = sanitizeIdentifier((template.children[0] as t.Element).name); | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |     const contextName = elName ? `${this.contextName}_${elName}` : ''; | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-22 15:35:18 -08:00
										 |  |  |     const templateName = | 
					
						
							|  |  |  |         contextName ? `${contextName}_Template_${templateIndex}` : `Template_${templateIndex}`; | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  |     const templateContext = `ctx${this.level}`; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |     const parameters: o.Expression[] = [ | 
					
						
							|  |  |  |       o.literal(templateIndex), | 
					
						
							|  |  |  |       o.variable(templateName), | 
					
						
							|  |  |  |       o.TYPED_NULL_EXPR, | 
					
						
							|  |  |  |     ]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-29 12:58:41 -07:00
										 |  |  |     const attributeNames: o.Expression[] = []; | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |     const attributeMap: {[name: string]: string} = {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     template.attributes.forEach(a => { | 
					
						
							|  |  |  |       attributeNames.push(asLiteral(a.name), asLiteral('')); | 
					
						
							|  |  |  |       attributeMap[a.name] = a.value; | 
					
						
							| 
									
										
										
										
											2018-03-29 12:58:41 -07:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Match directives on template attributes
 | 
					
						
							|  |  |  |     if (this.directiveMatcher) { | 
					
						
							|  |  |  |       const selector = createCssSelector('ng-template', attributeMap); | 
					
						
							|  |  |  |       this.directiveMatcher.match( | 
					
						
							|  |  |  |           selector, (cssSelector, staticType) => { this.directives.add(staticType); }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-29 12:58:41 -07:00
										 |  |  |     if (attributeNames.length) { | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |       parameters.push(this.constantPool.getConstLiteral(o.literalArr(attributeNames), true)); | 
					
						
							| 
									
										
										
										
											2018-03-29 12:58:41 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // e.g. C(1, C1Template)
 | 
					
						
							|  |  |  |     this.instruction( | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |         this._creationCode, template.sourceSpan, R3.containerCreate, | 
					
						
							| 
									
										
										
										
											2018-03-29 12:58:41 -07:00
										 |  |  |         ...trimTrailingNulls(parameters)); | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |     // e.g. p(1, 'forOf', ɵb(ctx.items));
 | 
					
						
							|  |  |  |     const context = o.variable(CONTEXT_NAME); | 
					
						
							|  |  |  |     template.inputs.forEach(input => { | 
					
						
							|  |  |  |       const convertedBinding = this.convertPropertyBinding(context, input.value); | 
					
						
							|  |  |  |       this.instruction( | 
					
						
							|  |  |  |           this._bindingCode, template.sourceSpan, R3.elementProperty, o.literal(templateIndex), | 
					
						
							| 
									
										
										
										
											2018-05-15 21:07:59 +02:00
										 |  |  |           o.literal(input.name), convertedBinding); | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2018-01-11 15:37:56 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  |     // Create the template function
 | 
					
						
							|  |  |  |     const templateVisitor = new TemplateDefinitionBuilder( | 
					
						
							| 
									
										
										
										
											2018-04-24 11:34:11 -07:00
										 |  |  |         this.constantPool, templateContext, this._bindingScope, this.level + 1, contextName, | 
					
						
							| 
									
										
										
										
											2018-06-08 09:00:01 -07:00
										 |  |  |         templateName, [], this.directiveMatcher, this.directives, this.pipeTypeByName, this.pipes, | 
					
						
							|  |  |  |         this._namespace); | 
					
						
							| 
									
										
										
										
											2018-04-13 15:12:11 -07:00
										 |  |  |     const templateFunctionExpr = | 
					
						
							|  |  |  |         templateVisitor.buildTemplateFunction(template.children, template.variables); | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |     this._postfixCode.push(templateFunctionExpr.toDeclStmt(templateName, null)); | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // These should be handled in the template or element directly.
 | 
					
						
							|  |  |  |   readonly visitReference = invalid; | 
					
						
							|  |  |  |   readonly visitVariable = invalid; | 
					
						
							| 
									
										
										
										
											2018-04-24 14:22:55 -07:00
										 |  |  |   readonly visitTextAttribute = invalid; | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |   readonly visitBoundAttribute = invalid; | 
					
						
							|  |  |  |   readonly visitBoundEvent = invalid; | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |   visitBoundText(text: t.BoundText) { | 
					
						
							| 
									
										
										
										
											2018-01-11 15:37:56 -08:00
										 |  |  |     const nodeIndex = this.allocateDataSlot(); | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |     this.instruction(this._creationCode, text.sourceSpan, R3.text, o.literal(nodeIndex)); | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     this.instruction( | 
					
						
							| 
									
										
										
										
											2018-05-15 21:07:59 +02:00
										 |  |  |         this._bindingCode, text.sourceSpan, R3.textBinding, o.literal(nodeIndex), | 
					
						
							| 
									
										
										
										
											2018-04-13 15:12:11 -07:00
										 |  |  |         this.convertPropertyBinding(o.variable(CONTEXT_NAME), text.value)); | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |   visitText(text: t.Text) { | 
					
						
							| 
									
										
										
										
											2018-01-11 15:37:56 -08:00
										 |  |  |     this.instruction( | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |         this._creationCode, text.sourceSpan, R3.text, o.literal(this.allocateDataSlot()), | 
					
						
							| 
									
										
										
										
											2018-04-13 15:12:11 -07:00
										 |  |  |         o.literal(text.value)); | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-14 10:54:00 -08:00
										 |  |  |   // When the content of the element is a single text node the translation can be inlined:
 | 
					
						
							|  |  |  |   //
 | 
					
						
							|  |  |  |   // `<p i18n="desc|mean">some content</p>`
 | 
					
						
							|  |  |  |   // compiles to
 | 
					
						
							|  |  |  |   // ```
 | 
					
						
							|  |  |  |   // /**
 | 
					
						
							|  |  |  |   // * @desc desc
 | 
					
						
							|  |  |  |   // * @meaning mean
 | 
					
						
							|  |  |  |   // */
 | 
					
						
							|  |  |  |   // const MSG_XYZ = goog.getMsg('some content');
 | 
					
						
							|  |  |  |   // i0.ɵT(1, MSG_XYZ);
 | 
					
						
							|  |  |  |   // ```
 | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |   visitSingleI18nTextChild(text: t.Text, i18nMeta: string) { | 
					
						
							| 
									
										
										
										
											2018-03-22 15:03:06 -07:00
										 |  |  |     const meta = parseI18nMeta(i18nMeta); | 
					
						
							|  |  |  |     const variable = this.constantPool.getTranslation(text.value, meta); | 
					
						
							| 
									
										
										
										
											2018-02-14 10:54:00 -08:00
										 |  |  |     this.instruction( | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |         this._creationCode, text.sourceSpan, R3.text, o.literal(this.allocateDataSlot()), variable); | 
					
						
							| 
									
										
										
										
											2018-02-14 10:54:00 -08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-11 15:37:56 -08:00
										 |  |  |   private allocateDataSlot() { return this._dataIndex++; } | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  |   private bindingContext() { return `${this._bindingContext++}`; } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   private instruction( | 
					
						
							| 
									
										
										
										
											2018-01-26 17:12:39 -08:00
										 |  |  |       statements: o.Statement[], span: ParseSourceSpan|null, reference: o.ExternalReference, | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  |       ...params: o.Expression[]) { | 
					
						
							|  |  |  |     statements.push(o.importExpr(reference, null, span).callFn(params, span).toStmt()); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-19 12:45:00 -07:00
										 |  |  |   private convertPropertyBinding(implicit: o.Expression, value: AST, skipBindFn?: boolean): | 
					
						
							|  |  |  |       o.Expression { | 
					
						
							| 
									
										
										
										
											2018-02-14 17:12:05 -08:00
										 |  |  |     const pipesConvertedValue = value.visit(this._valueConverter); | 
					
						
							| 
									
										
										
										
											2018-05-15 21:07:59 +02:00
										 |  |  |     if (pipesConvertedValue instanceof Interpolation) { | 
					
						
							|  |  |  |       const convertedPropertyBinding = convertPropertyBinding( | 
					
						
							|  |  |  |           this, implicit, pipesConvertedValue, this.bindingContext(), BindingForm.TrySimple, | 
					
						
							|  |  |  |           interpolate); | 
					
						
							|  |  |  |       this._bindingCode.push(...convertedPropertyBinding.stmts); | 
					
						
							|  |  |  |       return convertedPropertyBinding.currValExpr; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       const convertedPropertyBinding = convertPropertyBinding( | 
					
						
							|  |  |  |           this, implicit, pipesConvertedValue, this.bindingContext(), BindingForm.TrySimple, | 
					
						
							|  |  |  |           () => error('Unexpected interpolation')); | 
					
						
							|  |  |  |       this._bindingCode.push(...convertedPropertyBinding.stmts); | 
					
						
							| 
									
										
										
										
											2018-06-19 12:45:00 -07:00
										 |  |  |       const valExpr = convertedPropertyBinding.currValExpr; | 
					
						
							|  |  |  |       return skipBindFn ? valExpr : o.importExpr(R3.bind).callFn([valExpr]); | 
					
						
							| 
									
										
										
										
											2018-05-15 21:07:59 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-14 17:12:05 -08:00
										 |  |  | class ValueConverter extends AstMemoryEfficientTransformer { | 
					
						
							| 
									
										
										
										
											2018-02-05 17:31:12 -08:00
										 |  |  |   constructor( | 
					
						
							| 
									
										
										
										
											2018-04-24 11:34:11 -07:00
										 |  |  |       private constantPool: ConstantPool, private allocateSlot: () => number, | 
					
						
							| 
									
										
										
										
											2018-05-21 15:59:25 -07:00
										 |  |  |       private allocatePureFunctionSlots: (numSlots: number) => number, | 
					
						
							| 
									
										
										
										
											2018-02-05 17:31:12 -08:00
										 |  |  |       private definePipe: | 
					
						
							|  |  |  |           (name: string, localName: string, slot: number, value: o.Expression) => void) { | 
					
						
							|  |  |  |     super(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // AstMemoryEfficientTransformer
 | 
					
						
							| 
									
										
										
										
											2018-04-13 15:12:11 -07:00
										 |  |  |   visitPipe(pipe: BindingPipe, context: any): AST { | 
					
						
							| 
									
										
										
										
											2018-02-05 17:31:12 -08:00
										 |  |  |     // Allocate a slot to create the pipe
 | 
					
						
							| 
									
										
										
										
											2018-03-30 16:07:37 -07:00
										 |  |  |     const slot = this.allocateSlot(); | 
					
						
							| 
									
										
										
										
											2018-02-05 17:31:12 -08:00
										 |  |  |     const slotPseudoLocal = `PIPE:${slot}`; | 
					
						
							| 
									
										
										
										
											2018-05-21 15:59:25 -07:00
										 |  |  |     // Allocate one slot for the result plus one slot per pipe argument
 | 
					
						
							|  |  |  |     const pureFunctionSlot = this.allocatePureFunctionSlots(2 + pipe.args.length); | 
					
						
							| 
									
										
										
										
											2018-04-13 15:12:11 -07:00
										 |  |  |     const target = new PropertyRead(pipe.span, new ImplicitReceiver(pipe.span), slotPseudoLocal); | 
					
						
							| 
									
										
										
										
											2018-05-22 10:48:11 -07:00
										 |  |  |     const {identifier, isVarLength} = pipeBindingCallInfo(pipe.args); | 
					
						
							|  |  |  |     this.definePipe(pipe.name, slotPseudoLocal, slot, o.importExpr(identifier)); | 
					
						
							|  |  |  |     const args: AST[] = [pipe.exp, ...pipe.args]; | 
					
						
							|  |  |  |     const convertedArgs: AST[] = | 
					
						
							|  |  |  |         isVarLength ? this.visitAll([new LiteralArray(pipe.span, args)]) : this.visitAll(args); | 
					
						
							| 
									
										
										
										
											2018-02-05 17:31:12 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-21 15:59:25 -07:00
										 |  |  |     return new FunctionCall(pipe.span, target, [ | 
					
						
							|  |  |  |       new LiteralPrimitive(pipe.span, slot), | 
					
						
							|  |  |  |       new LiteralPrimitive(pipe.span, pureFunctionSlot), | 
					
						
							| 
									
										
										
										
											2018-05-22 10:48:11 -07:00
										 |  |  |       ...convertedArgs, | 
					
						
							| 
									
										
										
										
											2018-05-21 15:59:25 -07:00
										 |  |  |     ]); | 
					
						
							| 
									
										
										
										
											2018-02-05 17:31:12 -08:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-02-14 17:12:05 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-13 15:12:11 -07:00
										 |  |  |   visitLiteralArray(array: LiteralArray, context: any): AST { | 
					
						
							|  |  |  |     return new BuiltinFunctionCall(array.span, this.visitAll(array.expressions), values => { | 
					
						
							| 
									
										
										
										
											2018-02-14 10:54:00 -08:00
										 |  |  |       // If the literal has calculated (non-literal) elements transform it into
 | 
					
						
							| 
									
										
										
										
											2018-02-14 17:12:05 -08:00
										 |  |  |       // calls to literal factories that compose the literal and will cache intermediate
 | 
					
						
							|  |  |  |       // values. Otherwise, just return an literal array that contains the values.
 | 
					
						
							|  |  |  |       const literal = o.literalArr(values); | 
					
						
							| 
									
										
										
										
											2018-05-21 15:59:25 -07:00
										 |  |  |       return values.every(a => a.isConstant()) ? | 
					
						
							|  |  |  |           this.constantPool.getConstLiteral(literal, true) : | 
					
						
							|  |  |  |           getLiteralFactory(this.constantPool, literal, this.allocatePureFunctionSlots); | 
					
						
							| 
									
										
										
										
											2018-02-14 17:12:05 -08:00
										 |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-13 15:12:11 -07:00
										 |  |  |   visitLiteralMap(map: LiteralMap, context: any): AST { | 
					
						
							|  |  |  |     return new BuiltinFunctionCall(map.span, this.visitAll(map.values), values => { | 
					
						
							| 
									
										
										
										
											2018-02-14 17:12:05 -08:00
										 |  |  |       // If the literal has calculated (non-literal) elements  transform it into
 | 
					
						
							|  |  |  |       // calls to literal factories that compose the literal and will cache intermediate
 | 
					
						
							|  |  |  |       // values. Otherwise, just return an literal array that contains the values.
 | 
					
						
							|  |  |  |       const literal = o.literalMap(values.map( | 
					
						
							| 
									
										
										
										
											2018-04-13 15:12:11 -07:00
										 |  |  |           (value, index) => ({key: map.keys[index].key, value, quoted: map.keys[index].quoted}))); | 
					
						
							| 
									
										
										
										
											2018-05-21 15:59:25 -07:00
										 |  |  |       return values.every(a => a.isConstant()) ? | 
					
						
							|  |  |  |           this.constantPool.getConstLiteral(literal, true) : | 
					
						
							|  |  |  |           getLiteralFactory(this.constantPool, literal, this.allocatePureFunctionSlots); | 
					
						
							| 
									
										
										
										
											2018-02-14 17:12:05 -08:00
										 |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-02-05 17:31:12 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-24 11:34:11 -07:00
										 |  |  | // Pipes always have at least one parameter, the value they operate on
 | 
					
						
							|  |  |  | const pipeBindingIdentifiers = [R3.pipeBind1, R3.pipeBind2, R3.pipeBind3, R3.pipeBind4]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-22 10:48:11 -07:00
										 |  |  | function pipeBindingCallInfo(args: o.Expression[]) { | 
					
						
							|  |  |  |   const identifier = pipeBindingIdentifiers[args.length]; | 
					
						
							|  |  |  |   return { | 
					
						
							|  |  |  |     identifier: identifier || R3.pipeBindV, | 
					
						
							|  |  |  |     isVarLength: !identifier, | 
					
						
							|  |  |  |   }; | 
					
						
							| 
									
										
										
										
											2017-11-20 10:21:17 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-24 11:34:11 -07:00
										 |  |  | const pureFunctionIdentifiers = [ | 
					
						
							|  |  |  |   R3.pureFunction0, R3.pureFunction1, R3.pureFunction2, R3.pureFunction3, R3.pureFunction4, | 
					
						
							|  |  |  |   R3.pureFunction5, R3.pureFunction6, R3.pureFunction7, R3.pureFunction8 | 
					
						
							|  |  |  | ]; | 
					
						
							| 
									
										
										
										
											2018-05-22 10:47:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | function pureFunctionCallInfo(args: o.Expression[]) { | 
					
						
							|  |  |  |   const identifier = pureFunctionIdentifiers[args.length]; | 
					
						
							|  |  |  |   return { | 
					
						
							|  |  |  |     identifier: identifier || R3.pureFunctionV, | 
					
						
							|  |  |  |     isVarLength: !identifier, | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-24 11:34:11 -07:00
										 |  |  | function getLiteralFactory( | 
					
						
							| 
									
										
										
										
											2018-05-21 15:59:25 -07:00
										 |  |  |     constantPool: ConstantPool, literal: o.LiteralArrayExpr | o.LiteralMapExpr, | 
					
						
							|  |  |  |     allocateSlots: (numSlots: number) => number): o.Expression { | 
					
						
							| 
									
										
										
										
											2018-04-24 11:34:11 -07:00
										 |  |  |   const {literalFactory, literalFactoryArguments} = constantPool.getLiteralFactory(literal); | 
					
						
							| 
									
										
										
										
											2018-05-21 15:59:25 -07:00
										 |  |  |   // Allocate 1 slot for the result plus 1 per argument
 | 
					
						
							|  |  |  |   const startSlot = allocateSlots(1 + literalFactoryArguments.length); | 
					
						
							| 
									
										
										
										
											2018-04-24 11:34:11 -07:00
										 |  |  |   literalFactoryArguments.length > 0 || error(`Expected arguments to a literal factory function`); | 
					
						
							| 
									
										
										
										
											2018-05-22 10:47:50 -07:00
										 |  |  |   const {identifier, isVarLength} = pureFunctionCallInfo(literalFactoryArguments); | 
					
						
							| 
									
										
										
										
											2018-04-24 11:34:11 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // Literal factories are pure functions that only need to be re-invoked when the parameters
 | 
					
						
							|  |  |  |   // change.
 | 
					
						
							| 
									
										
										
										
											2018-05-22 10:47:50 -07:00
										 |  |  |   const args = [ | 
					
						
							|  |  |  |     o.literal(startSlot), | 
					
						
							|  |  |  |     literalFactory, | 
					
						
							|  |  |  |   ]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (isVarLength) { | 
					
						
							|  |  |  |     args.push(o.literalArr(literalFactoryArguments)); | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     args.push(...literalFactoryArguments); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return o.importExpr(identifier).callFn(args); | 
					
						
							| 
									
										
										
										
											2018-01-26 17:12:39 -08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-01-29 10:36:56 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-24 11:34:11 -07:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Function which is executed whenever a variable is referenced for the first time in a given | 
					
						
							|  |  |  |  * scope. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * It is expected that the function creates the `const localName = expression`; statement. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | export type DeclareLocalVarCallback = (lhsVar: o.ReadVarExpr, rhsExpression: o.Expression) => void; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export class BindingScope implements LocalResolver { | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Keeps a map from local variables to their expressions. | 
					
						
							|  |  |  |    * | 
					
						
							|  |  |  |    * This is used when one refers to variable such as: 'let abc = a.b.c`.
 | 
					
						
							|  |  |  |    * - key to the map is the string literal `"abc"`. | 
					
						
							|  |  |  |    * - value `lhs` is the left hand side which is an AST representing `abc`. | 
					
						
							|  |  |  |    * - value `rhs` is the right hand side which is an AST representing `a.b.c`. | 
					
						
							|  |  |  |    * - value `declared` is true if the `declareLocalVarCallback` has been called for this scope | 
					
						
							|  |  |  |    * already. | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   private map = new Map < string, { | 
					
						
							|  |  |  |     lhs: o.ReadVarExpr; | 
					
						
							|  |  |  |     rhs: o.Expression|undefined; | 
					
						
							|  |  |  |     declared: boolean; | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-04-24 11:34:11 -07:00
										 |  |  |   > (); | 
					
						
							|  |  |  |   private referenceNameIndex = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   static ROOT_SCOPE = new BindingScope().set('$event', o.variable('$event')); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   private constructor( | 
					
						
							|  |  |  |       private parent: BindingScope|null = null, | 
					
						
							|  |  |  |       private declareLocalVarCallback: DeclareLocalVarCallback = noop) {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   get(name: string): o.Expression|null { | 
					
						
							|  |  |  |     let current: BindingScope|null = this; | 
					
						
							|  |  |  |     while (current) { | 
					
						
							|  |  |  |       let value = current.map.get(name); | 
					
						
							|  |  |  |       if (value != null) { | 
					
						
							|  |  |  |         if (current !== this) { | 
					
						
							|  |  |  |           // make a local copy and reset the `declared` state.
 | 
					
						
							|  |  |  |           value = {lhs: value.lhs, rhs: value.rhs, declared: false}; | 
					
						
							|  |  |  |           // Cache the value locally.
 | 
					
						
							|  |  |  |           this.map.set(name, value); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (value.rhs && !value.declared) { | 
					
						
							|  |  |  |           // if it is first time we are referencing the variable in the scope
 | 
					
						
							|  |  |  |           // than invoke the callback to insert variable declaration.
 | 
					
						
							|  |  |  |           this.declareLocalVarCallback(value.lhs, value.rhs); | 
					
						
							|  |  |  |           value.declared = true; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return value.lhs; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       current = current.parent; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return null; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Create a local variable for later reference. | 
					
						
							|  |  |  |    * | 
					
						
							|  |  |  |    * @param name Name of the variable. | 
					
						
							|  |  |  |    * @param lhs AST representing the left hand side of the `let lhs = rhs;`. | 
					
						
							|  |  |  |    * @param rhs AST representing the right hand side of the `let lhs = rhs;`. The `rhs` can be | 
					
						
							|  |  |  |    * `undefined` for variable that are ambient such as `$event` and which don't have `rhs` | 
					
						
							|  |  |  |    * declaration. | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   set(name: string, lhs: o.ReadVarExpr, rhs?: o.Expression): BindingScope { | 
					
						
							|  |  |  |     !this.map.has(name) || | 
					
						
							|  |  |  |         error(`The name ${name} is already defined in scope to be ${this.map.get(name)}`); | 
					
						
							|  |  |  |     this.map.set(name, {lhs: lhs, rhs: rhs, declared: false}); | 
					
						
							|  |  |  |     return this; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   getLocal(name: string): (o.Expression|null) { return this.get(name); } | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-24 11:34:11 -07:00
										 |  |  |   nestedScope(declareCallback: DeclareLocalVarCallback): BindingScope { | 
					
						
							|  |  |  |     return new BindingScope(this, declareCallback); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   freshReferenceName(): string { | 
					
						
							|  |  |  |     let current: BindingScope = this; | 
					
						
							|  |  |  |     // Find the top scope as it maintains the global reference count
 | 
					
						
							|  |  |  |     while (current.parent) current = current.parent; | 
					
						
							|  |  |  |     const ref = `${REFERENCE_PREFIX}${current.referenceNameIndex++}`; | 
					
						
							|  |  |  |     return ref; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-02-14 10:54:00 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  | /** | 
					
						
							| 
									
										
										
										
											2018-04-24 11:34:11 -07:00
										 |  |  |  * Creates a `CssSelector` given a tag name and a map of attributes | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-04-24 11:34:11 -07:00
										 |  |  | function createCssSelector(tag: string, attributes: {[name: string]: string}): CssSelector { | 
					
						
							|  |  |  |   const cssSelector = new CssSelector(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   cssSelector.setElement(tag); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Object.getOwnPropertyNames(attributes).forEach((name) => { | 
					
						
							|  |  |  |     const value = attributes[name]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     cssSelector.addAttribute(name, value); | 
					
						
							|  |  |  |     if (name.toLowerCase() === 'class') { | 
					
						
							|  |  |  |       const classes = value.trim().split(/\s+/g); | 
					
						
							|  |  |  |       classes.forEach(className => cssSelector.addClassName(className)); | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-04-24 11:34:11 -07:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return cssSelector; | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-14 10:54:00 -08:00
										 |  |  | // Parse i18n metas like:
 | 
					
						
							|  |  |  | // - "@@id",
 | 
					
						
							|  |  |  | // - "description[@@id]",
 | 
					
						
							|  |  |  | // - "meaning|description[@@id]"
 | 
					
						
							|  |  |  | function parseI18nMeta(i18n?: string): {description?: string, id?: string, meaning?: string} { | 
					
						
							|  |  |  |   let meaning: string|undefined; | 
					
						
							|  |  |  |   let description: string|undefined; | 
					
						
							|  |  |  |   let id: string|undefined; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (i18n) { | 
					
						
							|  |  |  |     // TODO(vicb): figure out how to force a message ID with closure ?
 | 
					
						
							|  |  |  |     const idIndex = i18n.indexOf(ID_SEPARATOR); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const descIndex = i18n.indexOf(MEANING_SEPARATOR); | 
					
						
							|  |  |  |     let meaningAndDesc: string; | 
					
						
							|  |  |  |     [meaningAndDesc, id] = | 
					
						
							|  |  |  |         (idIndex > -1) ? [i18n.slice(0, idIndex), i18n.slice(idIndex + 2)] : [i18n, '']; | 
					
						
							|  |  |  |     [meaning, description] = (descIndex > -1) ? | 
					
						
							|  |  |  |         [meaningAndDesc.slice(0, descIndex), meaningAndDesc.slice(descIndex + 1)] : | 
					
						
							|  |  |  |         ['', meaningAndDesc]; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return {description, id, meaning}; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-24 11:34:11 -07:00
										 |  |  | function interpolate(args: o.Expression[]): o.Expression { | 
					
						
							|  |  |  |   args = args.slice(1);  // Ignore the length prefix added for render2
 | 
					
						
							|  |  |  |   switch (args.length) { | 
					
						
							|  |  |  |     case 3: | 
					
						
							|  |  |  |       return o.importExpr(R3.interpolation1).callFn(args); | 
					
						
							|  |  |  |     case 5: | 
					
						
							|  |  |  |       return o.importExpr(R3.interpolation2).callFn(args); | 
					
						
							|  |  |  |     case 7: | 
					
						
							|  |  |  |       return o.importExpr(R3.interpolation3).callFn(args); | 
					
						
							|  |  |  |     case 9: | 
					
						
							|  |  |  |       return o.importExpr(R3.interpolation4).callFn(args); | 
					
						
							|  |  |  |     case 11: | 
					
						
							|  |  |  |       return o.importExpr(R3.interpolation5).callFn(args); | 
					
						
							|  |  |  |     case 13: | 
					
						
							|  |  |  |       return o.importExpr(R3.interpolation6).callFn(args); | 
					
						
							|  |  |  |     case 15: | 
					
						
							|  |  |  |       return o.importExpr(R3.interpolation7).callFn(args); | 
					
						
							|  |  |  |     case 17: | 
					
						
							|  |  |  |       return o.importExpr(R3.interpolation8).callFn(args); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   (args.length >= 19 && args.length % 2 == 1) || | 
					
						
							|  |  |  |       error(`Invalid interpolation argument length ${args.length}`); | 
					
						
							|  |  |  |   return o.importExpr(R3.interpolationV).callFn([o.literalArr(args)]); | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-05-09 08:35:25 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Parse a template into render3 `Node`s and additional metadata, with no other dependencies. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @param template text of the template to parse | 
					
						
							|  |  |  |  * @param templateUrl URL to use for source mapping of the parsed template | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-05-21 08:15:19 -07:00
										 |  |  | export function parseTemplate( | 
					
						
							| 
									
										
										
										
											2018-05-31 15:50:02 -07:00
										 |  |  |     template: string, templateUrl: string, options: {preserveWhitespaces?: boolean} = {}): | 
					
						
							| 
									
										
										
										
											2018-05-09 08:35:25 -07:00
										 |  |  |     {errors?: ParseError[], nodes: t.Node[], hasNgContent: boolean, ngContentSelectors: string[]} { | 
					
						
							|  |  |  |   const bindingParser = makeBindingParser(); | 
					
						
							|  |  |  |   const htmlParser = new HtmlParser(); | 
					
						
							|  |  |  |   const parseResult = htmlParser.parse(template, templateUrl); | 
					
						
							| 
									
										
										
										
											2018-05-21 08:15:19 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-09 08:35:25 -07:00
										 |  |  |   if (parseResult.errors && parseResult.errors.length > 0) { | 
					
						
							|  |  |  |     return {errors: parseResult.errors, nodes: [], hasNgContent: false, ngContentSelectors: []}; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-05-21 08:15:19 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   let rootNodes: html.Node[] = parseResult.rootNodes; | 
					
						
							| 
									
										
										
										
											2018-05-31 15:50:02 -07:00
										 |  |  |   if (!options.preserveWhitespaces) { | 
					
						
							| 
									
										
										
										
											2018-05-21 08:15:19 -07:00
										 |  |  |     rootNodes = html.visitAll(new WhitespaceVisitor(), rootNodes); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-09 08:35:25 -07:00
										 |  |  |   const {nodes, hasNgContent, ngContentSelectors, errors} = | 
					
						
							| 
									
										
										
										
											2018-05-21 08:15:19 -07:00
										 |  |  |       htmlAstToRender3Ast(rootNodes, bindingParser); | 
					
						
							| 
									
										
										
										
											2018-05-09 08:35:25 -07:00
										 |  |  |   if (errors && errors.length > 0) { | 
					
						
							|  |  |  |     return {errors, nodes: [], hasNgContent: false, ngContentSelectors: []}; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return {nodes, hasNgContent, ngContentSelectors}; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Construct a `BindingParser` with a default configuration. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | export function makeBindingParser(): BindingParser { | 
					
						
							|  |  |  |   return new BindingParser( | 
					
						
							| 
									
										
										
										
											2018-06-26 10:44:22 -07:00
										 |  |  |       new Parser(new Lexer()), DEFAULT_INTERPOLATION_CONFIG, new DomElementSchemaRegistry(), null, | 
					
						
							| 
									
										
										
										
											2018-05-09 08:35:25 -07:00
										 |  |  |       []); | 
					
						
							|  |  |  | } |