6060 * @returns {Object } New `UrlMatcher` object
6161 */
6262function UrlMatcher ( pattern , config ) {
63- config = angular . isObject ( config ) ? config : { } ;
63+ config = extend ( { params : { } } , isObject ( config ) ? config : { } ) ;
6464
6565 // Find all placeholders and create a compiled pattern, using either classic or curly syntax:
6666 // '*' name
@@ -78,21 +78,13 @@ function UrlMatcher(pattern, config) {
7878 var placeholder = / ( [: * ] ) ( \w + ) | \{ ( \w + ) (?: \: ( (?: [ ^ { } \\ ] + | \\ .| \{ (?: [ ^ { } \\ ] + | \\ .) * \} ) + ) ) ? \} / g,
7979 compiled = '^' , last = 0 , m ,
8080 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 ( ) ;
9182
9283 function addParameter ( id , type , config ) {
9384 if ( ! / ^ \w + ( - + \w + ) * $ / . test ( id ) ) throw new Error ( "Invalid parameter name '" + id + "' in pattern '" + pattern + "'" ) ;
9485 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 ] ;
9688 }
9789
9890 function quoteRegExp ( string , pattern , isOptional ) {
@@ -102,12 +94,6 @@ function UrlMatcher(pattern, config) {
10294 return result + flag + '(' + pattern + ')' + flag ;
10395 }
10496
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-
11197 this . source = pattern ;
11298
11399 // Split into static segments separated by path parameter placeholders.
@@ -119,12 +105,12 @@ function UrlMatcher(pattern, config) {
119105 regexp = m [ 4 ] || ( m [ 1 ] == '*' ? '.*' : '[^/]*' ) ;
120106 segment = pattern . substring ( last , m . index ) ;
121107 type = this . $types [ regexp ] || new Type ( { pattern : new RegExp ( regexp ) } ) ;
122- cfg = paramConfig ( id ) ;
108+ cfg = config . params [ id ] ;
123109
124110 if ( segment . indexOf ( '?' ) >= 0 ) break ; // we're into the search part
125111
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 ) ;
128114 segments . push ( segment ) ;
129115 last = placeholder . lastIndex ;
130116 }
@@ -140,7 +126,7 @@ function UrlMatcher(pattern, config) {
140126
141127 // Allow parameters to be separated by '?' as well as '&' to make concat() easier
142128 forEach ( search . substring ( 1 ) . split ( / [ & ? ] / ) , function ( key ) {
143- addParameter ( key , null , paramConfig ( key ) ) ;
129+ addParameter ( key , null , config . params [ key ] ) ;
144130 } ) ;
145131 } else {
146132 this . sourcePath = pattern ;
@@ -180,7 +166,7 @@ UrlMatcher.prototype.concat = function (pattern, config) {
180166 // Because order of search parameters is irrelevant, we can add our own search
181167 // parameters to the end of the new pattern. Parse the new pattern by itself
182168 // 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 ) ;
184170} ;
185171
186172UrlMatcher . prototype . toString = function ( ) {
@@ -216,21 +202,19 @@ UrlMatcher.prototype.exec = function (path, searchParams) {
216202 if ( ! m ) return null ;
217203 searchParams = searchParams || { } ;
218204
219- var params = this . parameters ( ) , nTotal = params . length ,
205+ var paramNames = this . parameters ( ) , nTotal = paramNames . length ,
220206 nPath = this . segments . length - 1 ,
221- values = { } , i , cfg , param ;
207+ values = { } , i , cfg , paramName ;
222208
223209 if ( nPath !== m . length - 1 ) throw new Error ( "Unbalanced capture group in route '" + this . source + "'" ) ;
224210
225211 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 ] ) ;
229214 }
230215 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 ] ) ;
234218 }
235219
236220 return values ;
@@ -265,15 +249,7 @@ UrlMatcher.prototype.parameters = function (param) {
265249 * @returns {boolean } Returns `true` if `params` validates, otherwise `false`.
266250 */
267251UrlMatcher . 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 ) ;
277253} ;
278254
279255/**
@@ -717,7 +693,94 @@ function $UrlMatcherFactory() {
717693 UrlMatcher . prototype . $types [ type . name ] = def ;
718694 } ) ;
719695 }
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 ;
720782}
721783
722784// Register as a provider so it's available to other providers
723785angular . module ( 'ui.router.util' ) . provider ( '$urlMatcherFactory' , $UrlMatcherFactory ) ;
786+ angular . module ( 'ui.router.util' ) . run ( [ '$urlMatcherFactory' , function ( $urlMatcherFactory ) { } ] ) ;
0 commit comments