1+ import {
2+ TemplateAstVisitor ,
3+ ElementAst ,
4+ BoundDirectivePropertyAst ,
5+ DirectiveAst ,
6+ BoundElementPropertyAst
7+ } from 'angular2/compiler' ;
8+ import {
9+ AstTransformer ,
10+ Quote ,
11+ AST ,
12+ EmptyExpr ,
13+ LiteralArray ,
14+ LiteralPrimitive ,
15+ ASTWithSource
16+ } from 'angular2/src/core/change_detection/parser/ast' ;
17+ import { BaseException } from 'angular2/src/facade/exceptions' ;
18+ import { Injectable } from 'angular2/core' ;
19+ import { Parser } from 'angular2/src/core/change_detection/parser/parser' ;
20+
21+ /**
22+ * e.g., './User', 'Modal' in ./User[Modal(param: value)]
23+ */
24+ class FixedPart {
25+ constructor ( public value : string ) { }
26+ }
27+
28+ /**
29+ * The square bracket
30+ */
31+ class AuxiliaryStart {
32+ constructor ( ) { }
33+ }
34+
35+ /**
36+ * The square bracket
37+ */
38+ class AuxiliaryEnd {
39+ constructor ( ) { }
40+ }
41+
42+ /**
43+ * e.g., param:value in ./User[Modal(param: value)]
44+ */
45+ class Params {
46+ constructor ( public ast : AST ) { }
47+ }
48+
49+ class RouterLinkLexer {
50+ index : number = 0 ;
51+
52+ constructor ( private parser : Parser , private exp : string ) { }
53+
54+ tokenize ( ) : Array < FixedPart | AuxiliaryStart | AuxiliaryEnd | Params > {
55+ let tokens = [ ] ;
56+ while ( this . index < this . exp . length ) {
57+ tokens . push ( this . _parseToken ( ) ) ;
58+ }
59+ return tokens ;
60+ }
61+
62+ private _parseToken ( ) {
63+ let c = this . exp [ this . index ] ;
64+ if ( c == '[' ) {
65+ this . index ++ ;
66+ return new AuxiliaryStart ( ) ;
67+
68+ } else if ( c == ']' ) {
69+ this . index ++ ;
70+ return new AuxiliaryEnd ( ) ;
71+
72+ } else if ( c == '(' ) {
73+ return this . _parseParams ( ) ;
74+
75+ } else if ( c == '/' && this . index !== 0 ) {
76+ this . index ++ ;
77+ return this . _parseFixedPart ( ) ;
78+
79+ } else {
80+ return this . _parseFixedPart ( ) ;
81+ }
82+ }
83+
84+ private _parseParams ( ) {
85+ let start = this . index ;
86+ for ( ; this . index < this . exp . length ; ++ this . index ) {
87+ let c = this . exp [ this . index ] ;
88+ if ( c == ')' ) {
89+ let paramsContent = this . exp . substring ( start + 1 , this . index ) ;
90+ this . index ++ ;
91+ return new Params ( this . parser . parseBinding ( `{${ paramsContent } }` , null ) . ast ) ;
92+ }
93+ }
94+ throw new BaseException ( "Cannot find ')'" ) ;
95+ }
96+
97+ private _parseFixedPart ( ) {
98+ let start = this . index ;
99+ let sawNonSlash = false ;
100+
101+
102+ for ( ; this . index < this . exp . length ; ++ this . index ) {
103+ let c = this . exp [ this . index ] ;
104+
105+ if ( c == '(' || c == '[' || c == ']' || ( c == '/' && sawNonSlash ) ) {
106+ break ;
107+ }
108+
109+ if ( c != '.' && c != '/' ) {
110+ sawNonSlash = true ;
111+ }
112+ }
113+
114+ let fixed = this . exp . substring ( start , this . index ) ;
115+
116+ if ( start === this . index || ! sawNonSlash || fixed . startsWith ( '//' ) ) {
117+ throw new BaseException ( "Invalid router link" ) ;
118+ }
119+
120+ return new FixedPart ( fixed ) ;
121+ }
122+ }
123+
124+ class RouterLinkAstGenerator {
125+ index : number = 0 ;
126+ constructor ( private tokens : any [ ] ) { }
127+
128+ generate ( ) : AST { return this . _genAuxiliary ( ) ; }
129+
130+ private _genAuxiliary ( ) : AST {
131+ let arr = [ ] ;
132+ for ( ; this . index < this . tokens . length ; this . index ++ ) {
133+ let r = this . tokens [ this . index ] ;
134+
135+ if ( r instanceof FixedPart ) {
136+ arr . push ( new LiteralPrimitive ( r . value ) ) ;
137+
138+ } else if ( r instanceof Params ) {
139+ arr . push ( r . ast ) ;
140+
141+ } else if ( r instanceof AuxiliaryEnd ) {
142+ break ;
143+
144+ } else if ( r instanceof AuxiliaryStart ) {
145+ this . index ++ ;
146+ arr . push ( this . _genAuxiliary ( ) ) ;
147+ }
148+ }
149+
150+ return new LiteralArray ( arr ) ;
151+ }
152+ }
153+
154+ class RouterLinkAstTransformer extends AstTransformer {
155+ constructor ( private parser : Parser ) { super ( ) ; }
156+
157+ visitQuote ( ast : Quote ) : AST {
158+ if ( ast . prefix == "route" ) {
159+ return parseRouterLinkExpression ( this . parser , ast . uninterpretedExpression ) ;
160+ } else {
161+ return super . visitQuote ( ast ) ;
162+ }
163+ }
164+ }
165+
166+ export function parseRouterLinkExpression ( parser : Parser , exp : string ) : AST {
167+ let tokens = new RouterLinkLexer ( parser , exp . trim ( ) ) . tokenize ( ) ;
168+ return new RouterLinkAstGenerator ( tokens ) . generate ( ) ;
169+ }
170+
171+ /**
172+ * A compiler plugin that implements the router link DSL.
173+ */
174+ @Injectable ( )
175+ export class RouterLinkTransform implements TemplateAstVisitor {
176+ private astTransformer ;
177+
178+ constructor ( parser : Parser ) { this . astTransformer = new RouterLinkAstTransformer ( parser ) ; }
179+
180+ visitNgContent ( ast : any , context : any ) : any { return ast ; }
181+
182+ visitEmbeddedTemplate ( ast : any , context : any ) : any { return ast ; }
183+
184+ visitElement ( ast : ElementAst , context : any ) : any {
185+ let updatedChildren = ast . children . map ( c => c . visit ( this , context ) ) ;
186+ let updatedInputs = ast . inputs . map ( c => c . visit ( this , context ) ) ;
187+ let updatedDirectives = ast . directives . map ( c => c . visit ( this , context ) ) ;
188+ return new ElementAst ( ast . name , ast . attrs , updatedInputs , ast . outputs , ast . exportAsVars ,
189+ updatedDirectives , updatedChildren , ast . ngContentIndex , ast . sourceSpan ) ;
190+ }
191+
192+ visitVariable ( ast : any , context : any ) : any { return ast ; }
193+
194+ visitEvent ( ast : any , context : any ) : any { return ast ; }
195+
196+ visitElementProperty ( ast : any , context : any ) : any { return ast ; }
197+
198+ visitAttr ( ast : any , context : any ) : any { return ast ; }
199+
200+ visitBoundText ( ast : any , context : any ) : any { return ast ; }
201+
202+ visitText ( ast : any , context : any ) : any { return ast ; }
203+
204+ visitDirective ( ast : DirectiveAst , context : any ) : any {
205+ let updatedInputs = ast . inputs . map ( c => c . visit ( this , context ) ) ;
206+ return new DirectiveAst ( ast . directive , updatedInputs , ast . hostProperties , ast . hostEvents ,
207+ ast . exportAsVars , ast . sourceSpan ) ;
208+ }
209+
210+ visitDirectiveProperty ( ast : BoundDirectivePropertyAst , context : any ) : any {
211+ let transformedValue = ast . value . visit ( this . astTransformer ) ;
212+ return new BoundDirectivePropertyAst ( ast . directiveName , ast . templateName , transformedValue ,
213+ ast . sourceSpan ) ;
214+ }
215+ }
0 commit comments