1- import { UrlSegment , Tree } from './segments' ;
1+ import { UrlSegment , Tree , TreeNode } from './segments' ;
22import { BaseException } from 'angular2/src/facade/exceptions' ;
3+ import { isBlank , isPresent , RegExpWrapper } from 'angular2/src/facade/lang' ;
4+ import { DEFAULT_OUTLET_NAME } from './constants' ;
35
46export abstract class RouterUrlParser { abstract parse ( url : string ) : Tree < UrlSegment > ; }
57
@@ -8,20 +10,157 @@ export class DefaultRouterUrlParser extends RouterUrlParser {
810 if ( url . length === 0 ) {
911 throw new BaseException ( `Invalid url '${ url } '` ) ;
1012 }
11- return new Tree < UrlSegment > ( this . _parseNodes ( url ) ) ;
13+ let root = new _UrlParser ( ) . parse ( url ) ;
14+ return new Tree < UrlSegment > ( root ) ;
1215 }
16+ }
1317
14- private _parseNodes ( url : string ) : UrlSegment [ ] {
15- let index = url . indexOf ( "/" , 1 ) ;
16- let children : UrlSegment [ ] ;
17- let currentUrl ;
18- if ( index > - 1 ) {
19- children = this . _parseNodes ( url . substring ( index + 1 ) ) ;
20- currentUrl = url . substring ( 0 , index ) ;
18+ var SEGMENT_RE = RegExpWrapper . create ( '^[^\\/\\(\\)\\?;=&#]+' ) ;
19+ function matchUrlSegment ( str : string ) : string {
20+ var match = RegExpWrapper . firstMatch ( SEGMENT_RE , str ) ;
21+ return isPresent ( match ) ? match [ 0 ] : '' ;
22+ }
23+ var QUERY_PARAM_VALUE_RE = RegExpWrapper . create ( '^[^\\(\\)\\?;&#]+' ) ;
24+ function matchUrlQueryParamValue ( str : string ) : string {
25+ var match = RegExpWrapper . firstMatch ( QUERY_PARAM_VALUE_RE , str ) ;
26+ return isPresent ( match ) ? match [ 0 ] : '' ;
27+ }
28+
29+ class _UrlParser {
30+ private _remaining : string ;
31+
32+ peekStartsWith ( str : string ) : boolean { return this . _remaining . startsWith ( str ) ; }
33+
34+ capture ( str : string ) : void {
35+ if ( ! this . _remaining . startsWith ( str ) ) {
36+ throw new BaseException ( `Expected "${ str } ".` ) ;
37+ }
38+ this . _remaining = this . _remaining . substring ( str . length ) ;
39+ }
40+
41+ parse ( url : string ) : TreeNode < UrlSegment > {
42+ this . _remaining = url ;
43+ if ( url == '' || url == '/' ) {
44+ return new TreeNode < UrlSegment > ( new UrlSegment ( '' , { } , DEFAULT_OUTLET_NAME ) , [ ] ) ;
2145 } else {
22- children = [ ] ;
23- currentUrl = url ;
46+ return this . parseRoot ( ) ;
47+ }
48+ }
49+
50+ parseRoot ( ) : TreeNode < UrlSegment > {
51+ let segments = this . parseSegments ( DEFAULT_OUTLET_NAME ) ;
52+ let queryParams = this . peekStartsWith ( '?' ) ? this . parseQueryParams ( ) : { } ;
53+ return new TreeNode < UrlSegment > ( new UrlSegment ( '' , queryParams , DEFAULT_OUTLET_NAME ) , segments ) ;
54+ }
55+
56+ parseSegments ( outletName : string ) : TreeNode < UrlSegment > [ ] {
57+ if ( this . _remaining . length == 0 ) {
58+ return [ ] ;
2459 }
25- return [ new UrlSegment ( currentUrl , { } , "" ) ] . concat ( children ) ;
60+ if ( this . peekStartsWith ( '/' ) ) {
61+ this . capture ( '/' ) ;
62+ }
63+ var path = matchUrlSegment ( this . _remaining ) ;
64+ this . capture ( path ) ;
65+
66+
67+ if ( path . indexOf ( ":" ) > - 1 ) {
68+ let parts = path . split ( ":" ) ;
69+ outletName = parts [ 0 ] ;
70+ path = parts [ 1 ] ;
71+ }
72+
73+ var matrixParams : { [ key : string ] : any } = { } ;
74+ if ( this . peekStartsWith ( ';' ) ) {
75+ matrixParams = this . parseMatrixParams ( ) ;
76+ }
77+
78+ var aux = [ ] ;
79+ if ( this . peekStartsWith ( '(' ) ) {
80+ aux = this . parseAuxiliaryRoutes ( ) ;
81+ }
82+
83+ var children : TreeNode < UrlSegment > [ ] = [ ] ;
84+ if ( this . peekStartsWith ( '/' ) && ! this . peekStartsWith ( '//' ) ) {
85+ this . capture ( '/' ) ;
86+ children = this . parseSegments ( DEFAULT_OUTLET_NAME ) ;
87+ }
88+
89+ let segment = new UrlSegment ( path , matrixParams , outletName ) ;
90+ let node = new TreeNode < UrlSegment > ( segment , children ) ;
91+ return [ node ] . concat ( aux ) ;
92+ }
93+
94+ parseQueryParams ( ) : { [ key : string ] : any } {
95+ var params : { [ key : string ] : any } = { } ;
96+ this . capture ( '?' ) ;
97+ this . parseQueryParam ( params ) ;
98+ while ( this . _remaining . length > 0 && this . peekStartsWith ( '&' ) ) {
99+ this . capture ( '&' ) ;
100+ this . parseQueryParam ( params ) ;
101+ }
102+ return params ;
103+ }
104+
105+ parseMatrixParams ( ) : { [ key : string ] : any } {
106+ var params : { [ key : string ] : any } = { } ;
107+ while ( this . _remaining . length > 0 && this . peekStartsWith ( ';' ) ) {
108+ this . capture ( ';' ) ;
109+ this . parseParam ( params ) ;
110+ }
111+ return params ;
112+ }
113+
114+ parseParam ( params : { [ key : string ] : any } ) : void {
115+ var key = matchUrlSegment ( this . _remaining ) ;
116+ if ( isBlank ( key ) ) {
117+ return ;
118+ }
119+ this . capture ( key ) ;
120+ var value : any = "true" ;
121+ if ( this . peekStartsWith ( '=' ) ) {
122+ this . capture ( '=' ) ;
123+ var valueMatch = matchUrlSegment ( this . _remaining ) ;
124+ if ( isPresent ( valueMatch ) ) {
125+ value = valueMatch ;
126+ this . capture ( value ) ;
127+ }
128+ }
129+
130+ params [ key ] = value ;
131+ }
132+
133+ parseQueryParam ( params : { [ key : string ] : any } ) : void {
134+ var key = matchUrlSegment ( this . _remaining ) ;
135+ if ( isBlank ( key ) ) {
136+ return ;
137+ }
138+ this . capture ( key ) ;
139+ var value : any = "true" ;
140+ if ( this . peekStartsWith ( '=' ) ) {
141+ this . capture ( '=' ) ;
142+ var valueMatch = matchUrlQueryParamValue ( this . _remaining ) ;
143+ if ( isPresent ( valueMatch ) ) {
144+ value = valueMatch ;
145+ this . capture ( value ) ;
146+ }
147+ }
148+
149+ params [ key ] = value ;
150+ }
151+
152+ parseAuxiliaryRoutes ( ) : TreeNode < UrlSegment > [ ] {
153+ var segments = [ ] ;
154+ this . capture ( '(' ) ;
155+
156+ while ( ! this . peekStartsWith ( ')' ) && this . _remaining . length > 0 ) {
157+ segments = segments . concat ( this . parseSegments ( "aux" ) ) ;
158+ if ( this . peekStartsWith ( '//' ) ) {
159+ this . capture ( '//' ) ;
160+ }
161+ }
162+ this . capture ( ')' ) ;
163+
164+ return segments ;
26165 }
27- }
166+ }
0 commit comments