| 
									
										
										
										
											2016-07-21 13:56:58 -07: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
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-09 16:16:50 -07:00
										 |  |  | import {AstPath} from '../ast_path'; | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  | import {ParseSourceSpan} from '../parse_util'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export interface Node { | 
					
						
							| 
									
										
										
										
											2017-03-24 09:57:32 -07:00
										 |  |  |   sourceSpan: ParseSourceSpan; | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  |   visit(visitor: Visitor, context: any): any; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export class Text implements Node { | 
					
						
							| 
									
										
										
										
											2017-03-24 09:57:32 -07:00
										 |  |  |   constructor(public value: string, public sourceSpan: ParseSourceSpan) {} | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  |   visit(visitor: Visitor, context: any): any { return visitor.visitText(this, context); } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export class Expansion implements Node { | 
					
						
							|  |  |  |   constructor( | 
					
						
							|  |  |  |       public switchValue: string, public type: string, public cases: ExpansionCase[], | 
					
						
							|  |  |  |       public sourceSpan: ParseSourceSpan, public switchValueSourceSpan: ParseSourceSpan) {} | 
					
						
							|  |  |  |   visit(visitor: Visitor, context: any): any { return visitor.visitExpansion(this, context); } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export class ExpansionCase implements Node { | 
					
						
							|  |  |  |   constructor( | 
					
						
							|  |  |  |       public value: string, public expression: Node[], public sourceSpan: ParseSourceSpan, | 
					
						
							|  |  |  |       public valueSourceSpan: ParseSourceSpan, public expSourceSpan: ParseSourceSpan) {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   visit(visitor: Visitor, context: any): any { return visitor.visitExpansionCase(this, context); } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export class Attribute implements Node { | 
					
						
							| 
									
										
										
										
											2016-10-07 13:53:29 -07:00
										 |  |  |   constructor( | 
					
						
							|  |  |  |       public name: string, public value: string, public sourceSpan: ParseSourceSpan, | 
					
						
							|  |  |  |       public valueSpan?: ParseSourceSpan) {} | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  |   visit(visitor: Visitor, context: any): any { return visitor.visitAttribute(this, context); } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export class Element implements Node { | 
					
						
							|  |  |  |   constructor( | 
					
						
							|  |  |  |       public name: string, public attrs: Attribute[], public children: Node[], | 
					
						
							| 
									
										
										
										
											2017-03-24 09:57:32 -07:00
										 |  |  |       public sourceSpan: ParseSourceSpan, public startSourceSpan: ParseSourceSpan|null = null, | 
					
						
							| 
									
										
										
										
											2017-03-24 09:59:58 -07:00
										 |  |  |       public endSourceSpan: ParseSourceSpan|null = null) {} | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  |   visit(visitor: Visitor, context: any): any { return visitor.visitElement(this, context); } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export class Comment implements Node { | 
					
						
							| 
									
										
										
										
											2017-03-24 09:59:58 -07:00
										 |  |  |   constructor(public value: string|null, public sourceSpan: ParseSourceSpan) {} | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  |   visit(visitor: Visitor, context: any): any { return visitor.visitComment(this, context); } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export interface Visitor { | 
					
						
							| 
									
										
										
										
											2016-10-10 09:13:50 -07:00
										 |  |  |   // Returning a truthy value from `visit()` will prevent `visitAll()` from the call to the typed
 | 
					
						
							|  |  |  |   // method and result returned will become the result included in `visitAll()`s result array.
 | 
					
						
							|  |  |  |   visit?(node: Node, context: any): any; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  |   visitElement(element: Element, context: any): any; | 
					
						
							|  |  |  |   visitAttribute(attribute: Attribute, context: any): any; | 
					
						
							|  |  |  |   visitText(text: Text, context: any): any; | 
					
						
							|  |  |  |   visitComment(comment: Comment, context: any): any; | 
					
						
							|  |  |  |   visitExpansion(expansion: Expansion, context: any): any; | 
					
						
							|  |  |  |   visitExpansionCase(expansionCase: ExpansionCase, context: any): any; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export function visitAll(visitor: Visitor, nodes: Node[], context: any = null): any[] { | 
					
						
							| 
									
										
										
										
											2016-11-12 14:08:58 +01:00
										 |  |  |   const result: any[] = []; | 
					
						
							| 
									
										
										
										
											2016-10-10 09:13:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-12 14:08:58 +01:00
										 |  |  |   const visit = visitor.visit ? | 
					
						
							| 
									
										
										
										
											2017-03-24 09:59:58 -07:00
										 |  |  |       (ast: Node) => visitor.visit !(ast, context) || ast.visit(visitor, context) : | 
					
						
							| 
									
										
										
										
											2016-10-10 09:13:50 -07:00
										 |  |  |       (ast: Node) => ast.visit(visitor, context); | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  |   nodes.forEach(ast => { | 
					
						
							| 
									
										
										
										
											2016-10-10 09:13:50 -07:00
										 |  |  |     const astResult = visit(ast); | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  |     if (astResult) { | 
					
						
							|  |  |  |       result.push(astResult); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  |   return result; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-05-09 16:16:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | export class RecursiveVisitor implements Visitor { | 
					
						
							|  |  |  |   constructor() {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   visitElement(ast: Element, context: any): any { | 
					
						
							|  |  |  |     this.visitChildren(context, visit => { | 
					
						
							|  |  |  |       visit(ast.attrs); | 
					
						
							|  |  |  |       visit(ast.children); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   visitAttribute(ast: Attribute, context: any): any {} | 
					
						
							|  |  |  |   visitText(ast: Text, context: any): any {} | 
					
						
							|  |  |  |   visitComment(ast: Comment, context: any): any {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   visitExpansion(ast: Expansion, context: any): any { | 
					
						
							|  |  |  |     return this.visitChildren(context, visit => { visit(ast.cases); }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   visitExpansionCase(ast: ExpansionCase, context: any): any {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   private visitChildren<T extends Node>( | 
					
						
							|  |  |  |       context: any, cb: (visit: (<V extends Node>(children: V[]|undefined) => void)) => void) { | 
					
						
							|  |  |  |     let results: any[][] = []; | 
					
						
							|  |  |  |     let t = this; | 
					
						
							|  |  |  |     function visit<T extends Node>(children: T[] | undefined) { | 
					
						
							|  |  |  |       if (children) results.push(visitAll(t, children, context)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     cb(visit); | 
					
						
							|  |  |  |     return [].concat.apply([], results); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export type HtmlAstPath = AstPath<Node>; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function spanOf(ast: Node) { | 
					
						
							|  |  |  |   const start = ast.sourceSpan.start.offset; | 
					
						
							|  |  |  |   let end = ast.sourceSpan.end.offset; | 
					
						
							|  |  |  |   if (ast instanceof Element) { | 
					
						
							|  |  |  |     if (ast.endSourceSpan) { | 
					
						
							|  |  |  |       end = ast.endSourceSpan.end.offset; | 
					
						
							|  |  |  |     } else if (ast.children && ast.children.length) { | 
					
						
							|  |  |  |       end = spanOf(ast.children[ast.children.length - 1]).end; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return {start, end}; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export function findNode(nodes: Node[], position: number): HtmlAstPath { | 
					
						
							|  |  |  |   const path: Node[] = []; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const visitor = new class extends RecursiveVisitor { | 
					
						
							|  |  |  |     visit(ast: Node, context: any): any { | 
					
						
							|  |  |  |       const span = spanOf(ast); | 
					
						
							|  |  |  |       if (span.start <= position && position < span.end) { | 
					
						
							|  |  |  |         path.push(ast); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         // Returning a value here will result in the children being skipped.
 | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-07-27 16:13:16 -07:00
										 |  |  |   }; | 
					
						
							| 
									
										
										
										
											2017-05-09 16:16:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   visitAll(visitor, nodes); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return new AstPath<Node>(path, position); | 
					
						
							|  |  |  | } |