60
60
* @returns {Object } New `UrlMatcher` object
61
61
*/
62
62
function UrlMatcher ( pattern , config ) {
63
- config = angular . isObject ( config ) ? config : { } ;
63
+ config = extend ( { params : { } } , isObject ( config ) ? config : { } ) ;
64
64
65
65
// Find all placeholders and create a compiled pattern, using either classic or curly syntax:
66
66
// '*' name
@@ -78,21 +78,13 @@ function UrlMatcher(pattern, config) {
78
78
var placeholder = / ( [: * ] ) ( \w + ) | \{ ( \w + ) (?: \: ( (?: [ ^ { } \\ ] + | \\ .| \{ (?: [ ^ { } \\ ] + | \\ .) * \} ) + ) ) ? \} / g,
79
79
compiled = '^' , last = 0 , m ,
80
80
segments = this . segments = [ ] ,
81
- params = this . params = { } ;
82
-
83
- /**
84
- * [Internal] Gets the decoded representation of a value if the value is defined, otherwise, returns the
85
- * default value, which may be the result of an injectable function.
86
- */
87
- function $value ( value ) {
88
- /*jshint validthis: true */
89
- return isDefined ( value ) ? this . type . decode ( value ) : $UrlMatcherFactory . $$getDefaultValue ( this ) ;
90
- }
81
+ params = this . params = new $$UrlMatcherFactoryProvider . ParamSet ( ) ;
91
82
92
83
function addParameter ( id , type , config ) {
93
84
if ( ! / ^ \w + ( - + \w + ) * $ / . test ( id ) ) throw new Error ( "Invalid parameter name '" + id + "' in pattern '" + pattern + "'" ) ;
94
85
if ( params [ id ] ) throw new Error ( "Duplicate parameter name '" + id + "' in pattern '" + pattern + "'" ) ;
95
- params [ id ] = extend ( { type : type || new Type ( ) , $value : $value } , config ) ;
86
+ params [ id ] = new $$UrlMatcherFactoryProvider . Param ( id , type , config ) ;
87
+ return params [ id ] ;
96
88
}
97
89
98
90
function quoteRegExp ( string , pattern , isOptional ) {
@@ -102,12 +94,6 @@ function UrlMatcher(pattern, config) {
102
94
return result + flag + '(' + pattern + ')' + flag ;
103
95
}
104
96
105
- function paramConfig ( param ) {
106
- if ( ! config . params || ! config . params [ param ] ) return { } ;
107
- var cfg = config . params [ param ] ;
108
- return isObject ( cfg ) ? cfg : { value : cfg } ;
109
- }
110
-
111
97
this . source = pattern ;
112
98
113
99
// Split into static segments separated by path parameter placeholders.
@@ -119,12 +105,12 @@ function UrlMatcher(pattern, config) {
119
105
regexp = m [ 4 ] || ( m [ 1 ] == '*' ? '.*' : '[^/]*' ) ;
120
106
segment = pattern . substring ( last , m . index ) ;
121
107
type = this . $types [ regexp ] || new Type ( { pattern : new RegExp ( regexp ) } ) ;
122
- cfg = paramConfig ( id ) ;
108
+ cfg = config . params [ id ] ;
123
109
124
110
if ( segment . indexOf ( '?' ) >= 0 ) break ; // we're into the search part
125
111
126
- compiled += quoteRegExp ( segment , type . $subPattern ( ) , isDefined ( cfg . value ) ) ;
127
- addParameter ( id , type , cfg ) ;
112
+ var param = addParameter ( id , type , cfg ) ;
113
+ compiled += quoteRegExp ( segment , type . $subPattern ( ) , param . isOptional ) ;
128
114
segments . push ( segment ) ;
129
115
last = placeholder . lastIndex ;
130
116
}
@@ -140,7 +126,7 @@ function UrlMatcher(pattern, config) {
140
126
141
127
// Allow parameters to be separated by '?' as well as '&' to make concat() easier
142
128
forEach ( search . substring ( 1 ) . split ( / [ & ? ] / ) , function ( key ) {
143
- addParameter ( key , null , paramConfig ( key ) ) ;
129
+ addParameter ( key , null , config . params [ key ] ) ;
144
130
} ) ;
145
131
} else {
146
132
this . sourcePath = pattern ;
@@ -180,7 +166,7 @@ UrlMatcher.prototype.concat = function (pattern, config) {
180
166
// Because order of search parameters is irrelevant, we can add our own search
181
167
// parameters to the end of the new pattern. Parse the new pattern by itself
182
168
// and then join the bits together, but it's much easier to do this on a string level.
183
- return new $$UrlMatcherFactoryProvider . compile ( this . sourcePath + pattern + this . sourceSearch , config ) ;
169
+ return $$UrlMatcherFactoryProvider . compile ( this . sourcePath + pattern + this . sourceSearch , config ) ;
184
170
} ;
185
171
186
172
UrlMatcher . prototype . toString = function ( ) {
@@ -216,21 +202,19 @@ UrlMatcher.prototype.exec = function (path, searchParams) {
216
202
if ( ! m ) return null ;
217
203
searchParams = searchParams || { } ;
218
204
219
- var params = this . parameters ( ) , nTotal = params . length ,
205
+ var paramNames = this . parameters ( ) , nTotal = paramNames . length ,
220
206
nPath = this . segments . length - 1 ,
221
- values = { } , i , cfg , param ;
207
+ values = { } , i , cfg , paramName ;
222
208
223
209
if ( nPath !== m . length - 1 ) throw new Error ( "Unbalanced capture group in route '" + this . source + "'" ) ;
224
210
225
211
for ( i = 0 ; i < nPath ; i ++ ) {
226
- param = params [ i ] ;
227
- cfg = this . params [ param ] ;
228
- values [ param ] = cfg . $value ( m [ i + 1 ] ) ;
212
+ paramName = paramNames [ i ] ;
213
+ values [ paramName ] = this . params [ paramName ] . value ( m [ i + 1 ] ) ;
229
214
}
230
215
for ( /**/ ; i < nTotal ; i ++ ) {
231
- param = params [ i ] ;
232
- cfg = this . params [ param ] ;
233
- values [ param ] = cfg . $value ( searchParams [ param ] ) ;
216
+ paramName = paramNames [ i ] ;
217
+ values [ paramName ] = this . params [ paramName ] . value ( searchParams [ paramName ] ) ;
234
218
}
235
219
236
220
return values ;
@@ -265,15 +249,7 @@ UrlMatcher.prototype.parameters = function (param) {
265
249
* @returns {boolean } Returns `true` if `params` validates, otherwise `false`.
266
250
*/
267
251
UrlMatcher . prototype . validates = function ( params ) {
268
- var result = true , isOptional , cfg , self = this ;
269
-
270
- forEach ( params , function ( val , key ) {
271
- if ( ! self . params [ key ] ) return ;
272
- cfg = self . params [ key ] ;
273
- isOptional = ! val && isDefined ( cfg . value ) ;
274
- result = result && ( isOptional || cfg . type . is ( val ) ) ;
275
- } ) ;
276
- return result ;
252
+ return this . params . $$validates ( params ) ;
277
253
} ;
278
254
279
255
/**
@@ -717,7 +693,94 @@ function $UrlMatcherFactory() {
717
693
UrlMatcher . prototype . $types [ type . name ] = def ;
718
694
} ) ;
719
695
}
696
+
697
+ this . Param = function Param ( id , type , config ) {
698
+ var self = this ;
699
+ var defaultValueConfig = getDefaultValueConfig ( config ) ;
700
+ config = config || { } ;
701
+ type = getType ( config , type ) ;
702
+
703
+ function getDefaultValueConfig ( config ) {
704
+ var keys = isObject ( config ) ? objectKeys ( config ) : [ ] ;
705
+ var isShorthand = keys . indexOf ( "value" ) === - 1 && keys . indexOf ( "type" ) === - 1 ;
706
+ var configValue = isShorthand ? config : config . value ;
707
+ return {
708
+ fn : isInjectable ( configValue ) ? configValue : function ( ) { return configValue ; } ,
709
+ value : configValue
710
+ } ;
711
+ }
712
+
713
+ function getType ( config , urlType ) {
714
+ if ( config . type && urlType ) throw new Error ( "Param '" + id + "' has two type configurations." ) ;
715
+ if ( urlType && ! config . type ) return urlType ;
716
+ return config . type instanceof Type ? config . type : new Type ( config . type || { } ) ;
717
+ }
718
+
719
+ /**
720
+ * [Internal] Get the default value of a parameter, which may be an injectable function.
721
+ */
722
+ function $$getDefaultValue ( ) {
723
+ if ( ! injector ) throw new Error ( "Injectable functions cannot be called at configuration time" ) ;
724
+ return injector . invoke ( defaultValueConfig . fn ) ;
725
+ }
726
+
727
+ /**
728
+ * [Internal] Gets the decoded representation of a value if the value is defined, otherwise, returns the
729
+ * default value, which may be the result of an injectable function.
730
+ */
731
+ function $value ( value ) {
732
+ return isDefined ( value ) ? self . type . decode ( value ) : $$getDefaultValue ( ) ;
733
+ }
734
+
735
+ extend ( this , {
736
+ id : id ,
737
+ type : type ,
738
+ config : config ,
739
+ dynamic : undefined ,
740
+ isOptional : defaultValueConfig . value !== undefined ,
741
+ value : $value
742
+ } ) ;
743
+ } ;
744
+
745
+ function ParamSet ( params ) {
746
+ extend ( this , params || { } ) ;
747
+ }
748
+
749
+ ParamSet . prototype = {
750
+ $$keys : function ( ) {
751
+ return protoKeys ( this , [ "$$keys" , "$$values" , "$$equals" , "$$validates" ] ) ;
752
+ } ,
753
+ $$values : function ( paramValues ) {
754
+ var values = { } , self = this ;
755
+ forEach ( self . $$keys ( ) , function ( key ) {
756
+ values [ key ] = self [ key ] . value ( paramValues && paramValues [ key ] ) ;
757
+ } ) ;
758
+ return values ;
759
+ } ,
760
+ $$equals : function ( paramValues1 , paramValues2 ) {
761
+ var equal = true ; self = this ;
762
+ forEach ( self . $$keys ( ) , function ( key ) {
763
+ var left = paramValues1 && paramValues1 [ key ] , right = paramValues2 && paramValues2 [ key ] ;
764
+ if ( ! self [ key ] . type . equals ( left , right ) ) equal = false ;
765
+ } ) ;
766
+ return equal ;
767
+ } ,
768
+ $$validates : function $$validate ( paramValues ) {
769
+ var result = true , isOptional , val , param , self = this ;
770
+
771
+ forEach ( this . $$keys ( ) , function ( key ) {
772
+ param = self [ key ] ;
773
+ val = paramValues [ key ] ;
774
+ isOptional = ! val && param . isOptional ;
775
+ result = result && ( isOptional || param . type . is ( val ) ) ;
776
+ } ) ;
777
+ return result ;
778
+ }
779
+ } ;
780
+
781
+ this . ParamSet = ParamSet ;
720
782
}
721
783
722
784
// Register as a provider so it's available to other providers
723
785
angular . module ( 'ui.router.util' ) . provider ( '$urlMatcherFactory' , $UrlMatcherFactory ) ;
786
+ angular . module ( 'ui.router.util' ) . run ( [ '$urlMatcherFactory' , function ( $urlMatcherFactory ) { } ] ) ;
0 commit comments