@@ -26,6 +26,33 @@ import {RouteHandler} from './route_handler';
2626export class Segment {
2727 name : string ;
2828 regex : string ;
29+ generate ( params : TouchMap ) : string { return '' ; }
30+ }
31+
32+ class TouchMap {
33+ map : StringMap < string , string > = StringMapWrapper . create ( ) ;
34+ keys : StringMap < string , boolean > = StringMapWrapper . create ( ) ;
35+
36+ constructor ( map : StringMap < string , any > ) {
37+ if ( isPresent ( map ) ) {
38+ StringMapWrapper . forEach ( map , ( value , key ) => {
39+ this . map [ key ] = isPresent ( value ) ? value . toString ( ) : null ;
40+ this . keys [ key ] = true ;
41+ } ) ;
42+ }
43+ }
44+
45+ get ( key : string ) : string {
46+ StringMapWrapper . delete ( this . keys , key ) ;
47+ return this . map [ key ] ;
48+ }
49+
50+ getUnused ( ) : StringMap < string , any > {
51+ var unused : StringMap < string , any > = StringMapWrapper . create ( ) ;
52+ var keys = StringMapWrapper . keys ( this . keys ) ;
53+ ListWrapper . forEach ( keys , ( key ) => { unused [ key ] = StringMapWrapper . get ( this . map , key ) ; } ) ;
54+ return unused ;
55+ }
2956}
3057
3158function normalizeString ( obj : any ) : string {
@@ -36,20 +63,36 @@ function normalizeString(obj: any): string {
3663 }
3764}
3865
39- class ContinuationSegment extends Segment {
40- generate ( params ) : string { return '' ; }
66+ function parseAndAssignMatrixParams ( keyValueMap , matrixString ) {
67+ if ( matrixString [ 0 ] == ';' ) {
68+ matrixString = matrixString . substring ( 1 ) ;
69+ }
70+
71+ matrixString . split ( ';' ) . forEach ( ( entry ) => {
72+ var tuple = entry . split ( '=' ) ;
73+ var key = tuple [ 0 ] ;
74+ var value = tuple . length > 1 ? tuple [ 1 ] : true ;
75+ keyValueMap [ key ] = value ;
76+ } ) ;
4177}
4278
79+ class ContinuationSegment extends Segment { }
80+
4381class StaticSegment extends Segment {
4482 regex : string ;
4583 name : string = '' ;
4684
4785 constructor ( public string : string ) {
4886 super ( ) ;
4987 this . regex = escapeRegex ( string ) ;
88+
89+ // we add this property so that the route matcher still sees
90+ // this segment as a valid path even if do not use the matrix
91+ // parameters
92+ this . regex += '(;[^\/]+)?' ;
5093 }
5194
52- generate ( params ) : string { return this . string ; }
95+ generate ( params : TouchMap ) : string { return this . string ; }
5396}
5497
5598@IMPLEMENTS ( Segment )
@@ -58,23 +101,22 @@ class DynamicSegment {
58101
59102 constructor ( public name : string ) { }
60103
61- generate ( params : StringMap < string , string > ) : string {
62- if ( ! StringMapWrapper . contains ( params , this . name ) ) {
104+ generate ( params : TouchMap ) : string {
105+ if ( ! StringMapWrapper . contains ( params . map , this . name ) ) {
63106 throw new BaseException (
64107 `Route generator for '${ this . name } ' was not included in parameters passed.` ) ;
65108 }
66- return normalizeString ( StringMapWrapper . get ( params , this . name ) ) ;
109+ return normalizeString ( params . get ( this . name ) ) ;
67110 }
68111}
69112
70113
71114class StarSegment {
72115 regex : string = "(.+)" ;
116+
73117 constructor ( public name : string ) { }
74118
75- generate ( params : StringMap < string , string > ) : string {
76- return normalizeString ( StringMapWrapper . get ( params , this . name ) ) ;
77- }
119+ generate ( params : TouchMap ) : string { return normalizeString ( params . get ( this . name ) ) ; }
78120}
79121
80122
@@ -168,9 +210,27 @@ export class PathRecognizer {
168210 }
169211
170212 parseParams ( url : string ) : StringMap < string , string > {
213+ // the last segment is always the star one since it's terminal
214+ var segmentsLimit = this . segments . length - 1 ;
215+ var containsStarSegment =
216+ segmentsLimit >= 0 && this . segments [ segmentsLimit ] instanceof StarSegment ;
217+
218+ var matrixString ;
219+ if ( ! containsStarSegment ) {
220+ var matches =
221+ RegExpWrapper . firstMatch ( RegExpWrapper . create ( '^(.*\/[^\/]+?)(;[^\/]+)?\/?$' ) , url ) ;
222+ if ( isPresent ( matches ) ) {
223+ url = matches [ 1 ] ;
224+ matrixString = matches [ 2 ] ;
225+ }
226+
227+ url = StringWrapper . replaceAll ( url , / ( ; [ ^ \/ ] + ) (? = ( \/ | \Z ) ) / g, '' ) ;
228+ }
229+
171230 var params = StringMapWrapper . create ( ) ;
172231 var urlPart = url ;
173- for ( var i = 0 ; i < this . segments . length ; i ++ ) {
232+
233+ for ( var i = 0 ; i <= segmentsLimit ; i ++ ) {
174234 var segment = this . segments [ i ] ;
175235 if ( segment instanceof ContinuationSegment ) {
176236 continue ;
@@ -179,16 +239,45 @@ export class PathRecognizer {
179239 var match = RegExpWrapper . firstMatch ( RegExpWrapper . create ( '/' + segment . regex ) , urlPart ) ;
180240 urlPart = StringWrapper . substring ( urlPart , match [ 0 ] . length ) ;
181241 if ( segment . name . length > 0 ) {
182- StringMapWrapper . set ( params , segment . name , match [ 1 ] ) ;
242+ params [ segment . name ] = match [ 1 ] ;
183243 }
184244 }
185245
246+ if ( isPresent ( matrixString ) && matrixString . length > 0 && matrixString [ 0 ] == ';' ) {
247+ parseAndAssignMatrixParams ( params , matrixString ) ;
248+ }
249+
186250 return params ;
187251 }
188252
189- generate ( params : StringMap < string , string > ) : string {
190- return ListWrapper . join ( ListWrapper . map ( this . segments , ( segment ) => segment . generate ( params ) ) ,
191- '/' ) ;
253+ generate ( params : StringMap < string , any > ) : string {
254+ var paramTokens = new TouchMap ( params ) ;
255+ var applyLeadingSlash = false ;
256+
257+ var url = '' ;
258+ for ( var i = 0 ; i < this . segments . length ; i ++ ) {
259+ let segment = this . segments [ i ] ;
260+ let s = segment . generate ( paramTokens ) ;
261+ applyLeadingSlash = applyLeadingSlash || ( segment instanceof ContinuationSegment ) ;
262+
263+ if ( s . length > 0 ) {
264+ url += ( i > 0 ? '/' : '' ) + s ;
265+ }
266+ }
267+
268+ var unusedParams = paramTokens . getUnused ( ) ;
269+ StringMapWrapper . forEach ( unusedParams , ( value , key ) => {
270+ url += ';' + key ;
271+ if ( isPresent ( value ) ) {
272+ url += '=' + value ;
273+ }
274+ } ) ;
275+
276+ if ( applyLeadingSlash ) {
277+ url += '/' ;
278+ }
279+
280+ return url ;
192281 }
193282
194283 resolveComponentType ( ) : Promise < any > { return this . handler . resolveComponentType ( ) ; }
0 commit comments