@@ -91,11 +91,15 @@ function UrlMatcher(pattern, config) {
91
91
return params [ id ] ;
92
92
}
93
93
94
- function quoteRegExp ( string , pattern , isOptional ) {
95
- var result = string . replace ( / [ \\ \[ \] \^ $ * + ? . ( ) | { } ] / g, "\\$&" ) ;
94
+ function quoteRegExp ( string , pattern , squashPolicy ) {
95
+ var flags = [ '' , '' ] , result = string . replace ( / [ \\ \[ \] \^ $ * + ? . ( ) | { } ] / g, "\\$&" ) ;
96
96
if ( ! pattern ) return result ;
97
- var flag = isOptional ? '?' : '' ;
98
- return result + flag + '(' + pattern + ')' + flag ;
97
+ switch ( squashPolicy ) {
98
+ case "nosquash" : flags = [ '' , '' ] ; break ;
99
+ case "value" : flags = [ '' , '?' ] ; break ;
100
+ case "slash" : flags = [ '?' , '?' ] ; break ;
101
+ }
102
+ return result + flags [ 0 ] + '(' + pattern + ')' + flags [ 1 ] ;
99
103
}
100
104
101
105
this . source = pattern ;
@@ -122,7 +126,7 @@ function UrlMatcher(pattern, config) {
122
126
if ( p . segment . indexOf ( '?' ) >= 0 ) break ; // we're into the search part
123
127
124
128
param = addParameter ( p . id , p . type , p . cfg ) ;
125
- compiled += quoteRegExp ( p . segment , param . type . pattern . source , param . isOptional ) ;
129
+ compiled += quoteRegExp ( p . segment , param . type . pattern . source , param . squash ) ;
126
130
segments . push ( p . segment ) ;
127
131
last = placeholder . lastIndex ;
128
132
}
@@ -290,37 +294,47 @@ UrlMatcher.prototype.validates = function (params) {
290
294
*/
291
295
UrlMatcher . prototype . format = function ( values ) {
292
296
var segments = this . segments , params = this . parameters ( ) ;
293
-
294
- if ( ! values ) return segments . join ( '' ) . replace ( '//' , '/' ) ;
297
+ var paramset = this . params ;
298
+ values = values || { } ;
295
299
296
300
var nPath = segments . length - 1 , nTotal = params . length ,
297
- result = segments [ 0 ] , i , search , value , param , cfg , array ;
301
+ result = segments [ 0 ] , i , search , value , name , param , array , isDefaultValue ;
298
302
299
303
if ( ! this . validates ( values ) ) return null ;
300
304
301
305
for ( i = 0 ; i < nPath ; i ++ ) {
302
- param = params [ i ] ;
303
- value = values [ param ] ;
304
- cfg = this . params [ param ] ;
305
-
306
- if ( ! isDefined ( value ) && ( segments [ i ] === '/' && segments [ i + 1 ] === '/' ) ) continue ;
307
- if ( value != null ) result += encodeURIComponent ( cfg . type . encode ( value ) ) ;
308
- result += segments [ i + 1 ] ;
306
+ name = params [ i ] ;
307
+ param = paramset [ name ] ;
308
+ value = param . value ( values [ name ] ) ;
309
+ isDefaultValue = param . isOptional && param . type . equals ( param . value ( ) , value ) ;
310
+ var squash = isDefaultValue ? param . squash : "nosquash" ;
311
+ var encoded = param . type . encode ( value ) ;
312
+
313
+ var nextSegment = segments [ i + 1 ] ;
314
+ if ( squash === "nosquash" ) {
315
+ if ( encoded != null ) result += encodeURIComponent ( encoded ) ;
316
+ result += nextSegment ;
317
+ } else if ( squash === "value" ) {
318
+ result += nextSegment ;
319
+ } else if ( squash === "slash" ) {
320
+ var capture = result . match ( / \/ $ / ) ? / \/ ? ( .* ) / : / ( .* ) / ;
321
+ result += nextSegment . match ( capture ) [ 1 ] ;
322
+ }
309
323
}
310
324
311
325
for ( /**/ ; i < nTotal ; i ++ ) {
312
- param = params [ i ] ;
313
- value = values [ param ] ;
326
+ name = params [ i ] ;
327
+ value = values [ name ] ;
314
328
if ( value == null ) continue ;
315
329
array = isArray ( value ) ;
316
330
317
331
if ( array ) {
318
- value = value . map ( encodeURIComponent ) . join ( '&' + param + '=' ) ;
332
+ value = value . map ( encodeURIComponent ) . join ( '&' + name + '=' ) ;
319
333
}
320
- result += ( search ? '&' : '?' ) + param + '=' + ( array ? value : encodeURIComponent ( value ) ) ;
334
+ result += ( search ? '&' : '?' ) + name + '=' + ( array ? value : encodeURIComponent ( value ) ) ;
321
335
search = true ;
322
336
}
323
- return result . replace ( '//' , '/' ) ;
337
+ return result ;
324
338
} ;
325
339
326
340
/**
@@ -446,9 +460,9 @@ Type.prototype.pattern = /.*/;
446
460
function $UrlMatcherFactory ( ) {
447
461
$$UMFP = this ;
448
462
449
- var isCaseInsensitive = false , isStrictMode = true ;
463
+ var isCaseInsensitive = false , isStrictMode = true , defaultSquashPolicy = "nosquash" ;
450
464
451
- function safeString ( val ) { return isDefined ( val ) ? val . toString ( ) : val ; }
465
+ function safeString ( val ) { return val != null ? val . toString ( ) : val ; }
452
466
function coerceEquals ( left , right ) { return left == right ; }
453
467
function angularEquals ( left , right ) { return angular . equals ( left , right ) ; }
454
468
// TODO: function regexpMatches(val) { return isDefined(val) && this.pattern.test(val); }
@@ -569,6 +583,28 @@ function $UrlMatcherFactory() {
569
583
isStrictMode = value ;
570
584
} ;
571
585
586
+ /**
587
+ * @ngdoc function
588
+ * @name ui.router.util.$urlMatcherFactory#defaultSquashPolicy
589
+ * @methodOf ui.router.util.$urlMatcherFactory
590
+ *
591
+ * @description
592
+ * Sets the default behavior when generating or matching URLs with default parameter values.
593
+ *
594
+ * @param {string } value A string that defines the default parameter URL squashing behavior.
595
+ * `nosquash`: When generating an href with a default parameter value, do not squash the parameter value from the URL
596
+ * `value`: When generating an href with a default parameter value, squash (remove) the parameter value from the URL
597
+ * `slash`: When generating an href with a default parameter value, squash (remove) the parameter value, and, if the
598
+ * parameter is surrounded by slashes, squash (remove) one slash from the URL
599
+ */
600
+ this . defaultSquashPolicy = function ( value ) {
601
+ if ( ! value ) return defaultSquashPolicy ;
602
+ if ( value !== "nosquash" && value !== "value" && value !== "slash" )
603
+ throw new Error ( "Invalid squash policy: " + value + ". Valid policies: 'nosquash', 'value', 'slash'" ) ;
604
+ defaultSquashPolicy = value ;
605
+ return value ;
606
+ } ;
607
+
572
608
/**
573
609
* @ngdoc function
574
610
* @name ui.router.util.$urlMatcherFactory#compile
@@ -758,10 +794,12 @@ function $UrlMatcherFactory() {
758
794
var defaultValueConfig = getDefaultValueConfig ( config ) ;
759
795
config = config || { } ;
760
796
type = getType ( config , type ) ;
797
+ var isOptional = defaultValueConfig . value !== undefined ;
798
+ var squash = getSquashPolicy ( config , isOptional ) ;
761
799
762
800
function getDefaultValueConfig ( config ) {
763
801
var keys = isObject ( config ) ? objectKeys ( config ) : [ ] ;
764
- var isShorthand = keys . indexOf ( "value" ) === - 1 && keys . indexOf ( "type" ) === - 1 ;
802
+ var isShorthand = keys . indexOf ( "value" ) === - 1 && keys . indexOf ( "type" ) === - 1 && keys . indexOf ( "squash" ) === - 1 ;
765
803
var configValue = isShorthand ? config : config . value ;
766
804
return {
767
805
fn : isInjectable ( configValue ) ? configValue : function ( ) { return configValue ; } ,
@@ -776,6 +814,19 @@ function $UrlMatcherFactory() {
776
814
return config . type instanceof Type ? config . type : new Type ( config . type ) ;
777
815
}
778
816
817
+ /**
818
+ * returns "nosquash", "value", "slash" to indicate the "default parameter url squash policy".
819
+ * undefined aliases to urlMatcherFactory default. `false` aliases to "nosquash". `true` aliases to "slash".
820
+ */
821
+ function getSquashPolicy ( config , isOptional ) {
822
+ var squash = config . squash ;
823
+ if ( ! isOptional || squash === false ) return "nosquash" ;
824
+ if ( ! isDefined ( squash ) ) return defaultSquashPolicy ;
825
+ if ( squash === true ) return "slash" ;
826
+ if ( squash === "nosquash" || squash === "value" || squash === "slash" ) return squash ;
827
+ throw new Error ( "Invalid squash policy: '" + squash + "'. Valid policies: 'nosquash' (false), 'value', 'slash' (true)" ) ;
828
+ }
829
+
779
830
/**
780
831
* [Internal] Get the default value of a parameter, which may be an injectable function.
781
832
*/
@@ -796,8 +847,9 @@ function $UrlMatcherFactory() {
796
847
id : id ,
797
848
type : type ,
798
849
config : config ,
850
+ squash : squash ,
799
851
dynamic : undefined ,
800
- isOptional : defaultValueConfig . value !== undefined ,
852
+ isOptional : isOptional ,
801
853
value : $value
802
854
} ) ;
803
855
} ;
0 commit comments