@@ -70,12 +70,14 @@ function UrlMatcher(pattern, config) {
70
70
// The regular expression is somewhat complicated due to the need to allow curly braces
71
71
// inside the regular expression. The placeholder regexp breaks down as follows:
72
72
// ([:*])(\w+) classic placeholder ($1 / $2)
73
- // \{(\w+)(?:\:( ... ))?\} curly brace placeholder ($3) with optional regexp ... ($4)
73
+ // ([:]?)([\w-]+) classic search placeholder (supports snake-case-params) ($1 / $2)
74
+ // \{(\w+)(?:\:( ... ))?\} curly brace placeholder ($3) with optional regexp/type ... ($4)
74
75
// (?: ... | ... | ... )+ the regexp consists of any number of atoms, an atom being either
75
76
// [^{}\\]+ - anything other than curly braces or backslash
76
77
// \\. - a backslash escape
77
78
// \{(?:[^{}\\]+|\\.)*\} - a matched set of curly braces containing other atoms
78
79
var placeholder = / ( [: * ] ) ( \w + ) | \{ ( \w + ) (?: \: ( (?: [ ^ { } \\ ] + | \\ .| \{ (?: [ ^ { } \\ ] + | \\ .) * \} ) + ) ) ? \} / g,
80
+ searchPlaceholder = / ( [: ] ? ) ( [ \w - ] + ) | \{ ( \w + ) (?: \: ( (?: [ ^ { } \\ ] + | \\ .| \{ (?: [ ^ { } \\ ] + | \\ .) * \} ) + ) ) ? \} / g,
79
81
compiled = '^' , last = 0 , m ,
80
82
segments = this . segments = [ ] ,
81
83
params = this . params = new $$UrlMatcherFactoryProvider . ParamSet ( ) ;
@@ -94,32 +96,33 @@ function UrlMatcher(pattern, config) {
94
96
return result + flag + '(' + pattern + ')' + flag ;
95
97
}
96
98
97
- function regexpType ( regexp ) {
98
- var type = new Type ( {
99
- pattern : new RegExp ( regexp ) ,
100
- is : function ( value ) { return type . pattern . exec ( type . encode ( value ) ) ; }
101
- } ) ;
102
- return type ;
103
- }
104
-
105
99
this . source = pattern ;
106
100
107
101
// Split into static segments separated by path parameter placeholders.
108
102
// The number of segments is always 1 more than the number of parameters.
109
- var id , regexp , segment , type , cfg ;
110
-
111
- while ( ( m = placeholder . exec ( pattern ) ) ) {
103
+ function matchDetails ( m , isSearch ) {
104
+ var id , regexp , segment , type , typeId , cfg ;
105
+ var $types = UrlMatcher . prototype . $types ;
106
+ var defaultTypeId = ( isSearch ? "searchParam" : "pathParam" ) ;
112
107
id = m [ 2 ] || m [ 3 ] ; // IE[78] returns '' for unmatched groups instead of null
113
- regexp = m [ 4 ] || ( m [ 1 ] == '*' ? '.*' : '[^/]*' ) ;
114
108
segment = pattern . substring ( last , m . index ) ;
115
- type = this . $types [ regexp ] || regexpType ( regexp ) ;
109
+ regexp = isSearch ? m [ 4 ] : m [ 4 ] || ( m [ 1 ] == '*' ? '.*' : null ) ;
110
+ typeId = regexp || defaultTypeId ;
111
+ type = $types [ typeId ] || extend ( { } , $types [ defaultTypeId ] , { pattern : new RegExp ( regexp ) } ) ;
116
112
cfg = config . params [ id ] ;
113
+ return {
114
+ id : id , regexp : regexp , segment : segment , type : type , cfg : cfg
115
+ } ;
116
+ }
117
117
118
- if ( segment . indexOf ( '?' ) >= 0 ) break ; // we're into the search part
118
+ var p , param , segment ;
119
+ while ( ( m = placeholder . exec ( pattern ) ) ) {
120
+ p = matchDetails ( m , false ) ;
121
+ if ( p . segment . indexOf ( '?' ) >= 0 ) break ; // we're into the search part
119
122
120
- var param = addParameter ( id , type , cfg ) ;
121
- compiled += quoteRegExp ( segment , type . $subPattern ( ) , param . isOptional ) ;
122
- segments . push ( segment ) ;
123
+ param = addParameter ( p . id , p . type , p . cfg ) ;
124
+ compiled += quoteRegExp ( p . segment , param . type . pattern . source , param . isOptional ) ;
125
+ segments . push ( p . segment ) ;
123
126
last = placeholder . lastIndex ;
124
127
}
125
128
segment = pattern . substring ( last ) ;
@@ -132,10 +135,15 @@ function UrlMatcher(pattern, config) {
132
135
segment = segment . substring ( 0 , i ) ;
133
136
this . sourcePath = pattern . substring ( 0 , last + i ) ;
134
137
135
- // Allow parameters to be separated by '?' as well as '&' to make concat() easier
136
- forEach ( search . substring ( 1 ) . split ( / [ & ? ] / ) , function ( key ) {
137
- addParameter ( key , null , config . params [ key ] ) ;
138
- } ) ;
138
+ if ( search . length > 0 ) {
139
+ last = 0 ;
140
+ while ( ( m = searchPlaceholder . exec ( search ) ) ) {
141
+ p = matchDetails ( m , true ) ;
142
+ param = addParameter ( p . id , p . type , p . cfg ) ;
143
+ last = placeholder . lastIndex ;
144
+ // check if ?&
145
+ }
146
+ }
139
147
} else {
140
148
this . sourcePath = pattern ;
141
149
this . sourceSearch = '' ;
@@ -385,7 +393,7 @@ Type.prototype.encode = function(val, key) {
385
393
* @methodOf ui.router.util.type:Type
386
394
*
387
395
* @description
388
- * Converts a string URL parameter value to a custom/native value.
396
+ * Converts a parameter value (from URL string or transition param) to a custom/native value.
389
397
*
390
398
* @param {string } val The URL parameter value to decode.
391
399
* @param {string } key The name of the parameter in which `val` is stored. Can be used for
@@ -433,7 +441,36 @@ function $UrlMatcherFactory() {
433
441
434
442
var isCaseInsensitive = false , isStrictMode = true ;
435
443
444
+ function safeString ( val ) { return isDefined ( val ) ? val . toString ( ) : val ; }
445
+ function coerceEquals ( left , right ) { return left == right ; }
446
+ function angularEquals ( left , right ) { return angular . equals ( left , right ) ; }
447
+ // TODO: function regexpMatches(val) { return isDefined(val) && this.pattern.test(val); }
448
+ function regexpMatches ( val ) { /*jshint validthis:true */ return this . pattern . test ( val ) ; }
449
+ function normalizeStringOrArray ( val ) {
450
+ if ( isArray ( val ) ) {
451
+ var encoded = [ ] ;
452
+ forEach ( val , function ( item ) { encoded . push ( safeString ( item ) ) ; } ) ;
453
+ return encoded ;
454
+ } else {
455
+ return safeString ( val ) ;
456
+ }
457
+ }
458
+
436
459
var enqueue = true , typeQueue = [ ] , injector , defaultTypes = {
460
+ "searchParam" : {
461
+ encode : normalizeStringOrArray ,
462
+ decode : normalizeStringOrArray ,
463
+ equals : angularEquals ,
464
+ is : regexpMatches ,
465
+ pattern : / [ ^ & ? ] * /
466
+ } ,
467
+ "pathParam" : {
468
+ encode : safeString ,
469
+ decode : safeString ,
470
+ equals : coerceEquals ,
471
+ is : regexpMatches ,
472
+ pattern : / [ ^ / ] * /
473
+ } ,
437
474
int : {
438
475
decode : function ( val ) {
439
476
return parseInt ( val , 10 ) ;
@@ -449,7 +486,7 @@ function $UrlMatcherFactory() {
449
486
return val ? 1 : 0 ;
450
487
} ,
451
488
decode : function ( val ) {
452
- return parseInt ( val , 10 ) === 0 ? false : true ;
489
+ return parseInt ( val , 10 ) !== 0 ;
453
490
} ,
454
491
is : function ( val ) {
455
492
return val === true || val === false ;
@@ -720,8 +757,9 @@ function $UrlMatcherFactory() {
720
757
721
758
function getType ( config , urlType ) {
722
759
if ( config . type && urlType ) throw new Error ( "Param '" + id + "' has two type configurations." ) ;
723
- if ( urlType && ! config . type ) return urlType ;
724
- return config . type instanceof Type ? config . type : new Type ( config . type || { } ) ;
760
+ if ( urlType ) return urlType ;
761
+ if ( ! config . type ) return UrlMatcher . prototype . $types . pathParam ;
762
+ return config . type instanceof Type ? config . type : new Type ( config . type ) ;
725
763
}
726
764
727
765
/**
0 commit comments