1
1
/**
2
2
* State-based routing for AngularJS
3
- * @version v0.2.12
3
+ * @version v0.2.13
4
4
* @link http://angular-ui.github.com/
5
5
* @license MIT License, http://www.opensource.org/licenses/MIT
6
6
*/
@@ -187,7 +187,7 @@ function omit(obj) {
187
187
var copy = { } ;
188
188
var keys = Array . prototype . concat . apply ( Array . prototype , Array . prototype . slice . call ( arguments , 1 ) ) ;
189
189
for ( var key in obj ) {
190
- if ( keys . indexOf ( key ) == - 1 ) copy [ key ] = obj [ key ] ;
190
+ if ( indexOf ( keys , key ) == - 1 ) copy [ key ] = obj [ key ] ;
191
191
}
192
192
return copy ;
193
193
}
@@ -202,10 +202,12 @@ function pluck(collection, key) {
202
202
}
203
203
204
204
function filter ( collection , callback ) {
205
- var result = isArray ( collection ) ? [ ] : { } ;
205
+ var array = isArray ( collection ) ;
206
+ var result = array ? [ ] : { } ;
206
207
forEach ( collection , function ( val , i ) {
207
- if ( callback ( val , i ) )
208
- result [ i ] = val ;
208
+ if ( callback ( val , i ) ) {
209
+ result [ array ? result . length : i ] = val ;
210
+ }
209
211
} ) ;
210
212
return result ;
211
213
}
@@ -754,9 +756,11 @@ function UrlMatcher(pattern, config, parentMatcher) {
754
756
compiled = '^' , last = 0 , m ,
755
757
segments = this . segments = [ ] ,
756
758
parentParams = parentMatcher ? parentMatcher . params : { } ,
757
- params = this . params = parentMatcher ? parentMatcher . params . $$new ( ) : new $$UMFP . ParamSet ( ) ;
759
+ params = this . params = parentMatcher ? parentMatcher . params . $$new ( ) : new $$UMFP . ParamSet ( ) ,
760
+ paramNames = [ ] ;
758
761
759
762
function addParameter ( id , type , config , location ) {
763
+ paramNames . push ( id ) ;
760
764
if ( parentParams [ id ] ) return parentParams [ id ] ;
761
765
if ( ! / ^ \w + ( - + \w + ) * (?: \[ \] ) ? $ / . test ( id ) ) throw new Error ( "Invalid parameter name '" + id + "' in pattern '" + pattern + "'" ) ;
762
766
if ( params [ id ] ) throw new Error ( "Duplicate parameter name '" + id + "' in pattern '" + pattern + "'" ) ;
@@ -830,6 +834,7 @@ function UrlMatcher(pattern, config, parentMatcher) {
830
834
831
835
this . regexp = new RegExp ( compiled , config . caseInsensitive ? 'i' : undefined ) ;
832
836
this . prefix = segments [ 0 ] ;
837
+ this . $$paramNames = paramNames ;
833
838
}
834
839
835
840
/**
@@ -945,7 +950,7 @@ UrlMatcher.prototype.exec = function (path, searchParams) {
945
950
* pattern has no parameters, an empty array is returned.
946
951
*/
947
952
UrlMatcher . prototype . parameters = function ( param ) {
948
- if ( ! isDefined ( param ) ) return this . params . $$keys ( ) ;
953
+ if ( ! isDefined ( param ) ) return this . $$paramNames ;
949
954
return this . params [ param ] || null ;
950
955
} ;
951
956
@@ -1161,28 +1166,51 @@ Type.prototype.$asArray = function(mode, isSearch) {
1161
1166
return new ArrayType ( this , mode ) ;
1162
1167
1163
1168
function ArrayType ( type , mode ) {
1164
- function bindTo ( thisObj , callback ) {
1169
+ function bindTo ( type , callbackName ) {
1165
1170
return function ( ) {
1166
- return callback . apply ( thisObj , arguments ) ;
1171
+ return type [ callbackName ] . apply ( type , arguments ) ;
1167
1172
} ;
1168
1173
}
1169
1174
1170
- function arrayHandler ( callback , reducefn ) {
1171
- // Wraps type functions to operate on each value of an array
1175
+ // Wrap non-array value as array
1176
+ function arrayWrap ( val ) { return isArray ( val ) ? val : ( isDefined ( val ) ? [ val ] : [ ] ) ; }
1177
+ // Unwrap array value for "auto" mode. Return undefined for empty array.
1178
+ function arrayUnwrap ( val ) {
1179
+ switch ( val . length ) {
1180
+ case 0 : return undefined ;
1181
+ case 1 : return mode === "auto" ? val [ 0 ] : val ;
1182
+ default : return val ;
1183
+ }
1184
+ }
1185
+ function falsey ( val ) { return ! val ; }
1186
+
1187
+ // Wraps type (.is/.encode/.decode) functions to operate on each value of an array
1188
+ function arrayHandler ( callback , allTruthyMode ) {
1172
1189
return function handleArray ( val ) {
1173
- if ( ! isArray ( val ) ) val = [ val ] ;
1190
+ val = arrayWrap ( val ) ;
1174
1191
var result = map ( val , callback ) ;
1175
- if ( reducefn )
1176
- return result . reduce ( reducefn , true ) ;
1177
- return ( result && result . length == 1 && mode === "auto" ) ? result [ 0 ] : result ;
1192
+ if ( allTruthyMode === true )
1193
+ return filter ( result , falsey ) . length === 0 ;
1194
+ return arrayUnwrap ( result ) ;
1195
+ } ;
1196
+ }
1197
+
1198
+ // Wraps type (.equals) functions to operate on each value of an array
1199
+ function arrayEqualsHandler ( callback ) {
1200
+ return function handleArray ( val1 , val2 ) {
1201
+ var left = arrayWrap ( val1 ) , right = arrayWrap ( val2 ) ;
1202
+ if ( left . length !== right . length ) return false ;
1203
+ for ( var i = 0 ; i < left . length ; i ++ ) {
1204
+ if ( ! callback ( left [ i ] , right [ i ] ) ) return false ;
1205
+ }
1206
+ return true ;
1178
1207
} ;
1179
1208
}
1180
1209
1181
- function alltruthy ( val , memo ) { return val && memo ; }
1182
- this . encode = arrayHandler ( bindTo ( this , type . encode ) ) ;
1183
- this . decode = arrayHandler ( bindTo ( this , type . decode ) ) ;
1184
- this . equals = arrayHandler ( bindTo ( this , type . equals ) , alltruthy ) ;
1185
- this . is = arrayHandler ( bindTo ( this , type . is ) , alltruthy ) ;
1210
+ this . encode = arrayHandler ( bindTo ( type , 'encode' ) ) ;
1211
+ this . decode = arrayHandler ( bindTo ( type , 'decode' ) ) ;
1212
+ this . is = arrayHandler ( bindTo ( type , 'is' ) , true ) ;
1213
+ this . equals = arrayEqualsHandler ( bindTo ( type , 'equals' ) ) ;
1186
1214
this . pattern = type . pattern ;
1187
1215
this . $arrayMode = mode ;
1188
1216
}
@@ -1203,9 +1231,8 @@ function $UrlMatcherFactory() {
1203
1231
1204
1232
var isCaseInsensitive = false , isStrictMode = true , defaultSquashPolicy = false ;
1205
1233
1206
- function valToString ( val ) { return val != null ? val . toString ( ) . replace ( "/" , "%2F" ) : val ; }
1207
- function valFromString ( val ) { return val != null ? val . toString ( ) . replace ( "%2F" , "/" ) : val ; }
1208
- function angularEquals ( left , right ) { return angular . equals ( left , right ) ; }
1234
+ function valToString ( val ) { return val != null ? val . toString ( ) . replace ( / \/ / g, "%2F" ) : val ; }
1235
+ function valFromString ( val ) { return val != null ? val . toString ( ) . replace ( / % 2 F / g, "/" ) : val ; }
1209
1236
// TODO: in 1.0, make string .is() return false if value is undefined by default.
1210
1237
// function regexpMatches(val) { /*jshint validthis:true */ return isDefined(val) && this.pattern.test(val); }
1211
1238
function regexpMatches ( val ) { /*jshint validthis:true */ return this . pattern . test ( val ) ; }
@@ -1230,16 +1257,37 @@ function $UrlMatcherFactory() {
1230
1257
pattern : / 0 | 1 /
1231
1258
} ,
1232
1259
date : {
1233
- encode : function ( val ) { return [
1234
- val . getFullYear ( ) ,
1260
+ encode : function ( val ) {
1261
+ if ( ! this . is ( val ) )
1262
+ return undefined ;
1263
+ return [ val . getFullYear ( ) ,
1235
1264
( '0' + ( val . getMonth ( ) + 1 ) ) . slice ( - 2 ) ,
1236
1265
( '0' + val . getDate ( ) ) . slice ( - 2 )
1237
1266
] . join ( "-" ) ;
1238
1267
} ,
1239
- decode : function ( val ) { return new Date ( val ) ; } ,
1268
+ decode : function ( val ) {
1269
+ if ( this . is ( val ) ) return val ;
1270
+ var match = this . capture . exec ( val ) ;
1271
+ return match ? new Date ( match [ 1 ] , match [ 2 ] - 1 , match [ 3 ] ) : undefined ;
1272
+ } ,
1240
1273
is : function ( val ) { return val instanceof Date && ! isNaN ( val . valueOf ( ) ) ; } ,
1241
- equals : function ( a , b ) { return a . toISOString ( ) === b . toISOString ( ) ; } ,
1242
- pattern : / [ 0 - 9 ] { 4 } - (?: 0 [ 1 - 9 ] | 1 [ 0 - 2 ] ) - (?: 0 [ 1 - 9 ] | [ 1 - 2 ] [ 0 - 9 ] | 3 [ 0 - 1 ] ) /
1274
+ equals : function ( a , b ) { return this . is ( a ) && this . is ( b ) && a . toISOString ( ) === b . toISOString ( ) ; } ,
1275
+ pattern : / [ 0 - 9 ] { 4 } - (?: 0 [ 1 - 9 ] | 1 [ 0 - 2 ] ) - (?: 0 [ 1 - 9 ] | [ 1 - 2 ] [ 0 - 9 ] | 3 [ 0 - 1 ] ) / ,
1276
+ capture : / ( [ 0 - 9 ] { 4 } ) - ( 0 [ 1 - 9 ] | 1 [ 0 - 2 ] ) - ( 0 [ 1 - 9 ] | [ 1 - 2 ] [ 0 - 9 ] | 3 [ 0 - 1 ] ) /
1277
+ } ,
1278
+ json : {
1279
+ encode : angular . toJson ,
1280
+ decode : angular . fromJson ,
1281
+ is : angular . isObject ,
1282
+ equals : angular . equals ,
1283
+ pattern : / [ ^ / ] * /
1284
+ } ,
1285
+ any : { // does not encode/decode
1286
+ encode : angular . identity ,
1287
+ decode : angular . identity ,
1288
+ is : angular . identity ,
1289
+ equals : angular . equals ,
1290
+ pattern : / .* /
1243
1291
}
1244
1292
} ;
1245
1293
@@ -1506,33 +1554,29 @@ function $UrlMatcherFactory() {
1506
1554
1507
1555
this . Param = function Param ( id , type , config , location ) {
1508
1556
var self = this ;
1509
- var defaultValueConfig = getDefaultValueConfig ( config ) ;
1510
- config = config || { } ;
1511
- type = getType ( config , type ) ;
1557
+ config = unwrapShorthand ( config ) ;
1558
+ type = getType ( config , type , location ) ;
1512
1559
var arrayMode = getArrayMode ( ) ;
1513
1560
type = arrayMode ? type . $asArray ( arrayMode , location === "search" ) : type ;
1514
- if ( type . name === "string" && ! arrayMode && location === "path" && defaultValueConfig . value === undefined )
1515
- defaultValueConfig . value = "" ; // for 0.2.x; in 0.3.0+ do not automatically default to ""
1516
- var isOptional = defaultValueConfig . value !== undefined ;
1561
+ if ( type . name === "string" && ! arrayMode && location === "path" && config . value === undefined )
1562
+ config . value = "" ; // for 0.2.x; in 0.3.0+ do not automatically default to ""
1563
+ var isOptional = config . value !== undefined ;
1517
1564
var squash = getSquashPolicy ( config , isOptional ) ;
1518
1565
var replace = getReplace ( config , arrayMode , isOptional , squash ) ;
1519
1566
1520
- function getDefaultValueConfig ( config ) {
1567
+ function unwrapShorthand ( config ) {
1521
1568
var keys = isObject ( config ) ? objectKeys ( config ) : [ ] ;
1522
1569
var isShorthand = indexOf ( keys , "value" ) === - 1 && indexOf ( keys , "type" ) === - 1 &&
1523
1570
indexOf ( keys , "squash" ) === - 1 && indexOf ( keys , "array" ) === - 1 ;
1524
- var configValue = isShorthand ? config : config . value ;
1525
- var result = {
1526
- fn : isInjectable ( configValue ) ? configValue : function ( ) { return result . value ; } ,
1527
- value : configValue
1528
- } ;
1529
- return result ;
1571
+ if ( isShorthand ) config = { value : config } ;
1572
+ config . $$fn = isInjectable ( config . value ) ? config . value : function ( ) { return config . value ; } ;
1573
+ return config ;
1530
1574
}
1531
1575
1532
- function getType ( config , urlType ) {
1576
+ function getType ( config , urlType , location ) {
1533
1577
if ( config . type && urlType ) throw new Error ( "Param '" + id + "' has two type configurations." ) ;
1534
1578
if ( urlType ) return urlType ;
1535
- if ( ! config . type ) return $types . string ;
1579
+ if ( ! config . type ) return ( location === "config" ? $types . any : $types . string ) ;
1536
1580
return config . type instanceof Type ? config . type : new Type ( config . type ) ;
1537
1581
}
1538
1582
@@ -1571,7 +1615,7 @@ function $UrlMatcherFactory() {
1571
1615
*/
1572
1616
function $$getDefaultValue ( ) {
1573
1617
if ( ! injector ) throw new Error ( "Injectable functions cannot be called at configuration time" ) ;
1574
- return injector . invoke ( defaultValueConfig . fn ) ;
1618
+ return injector . invoke ( config . $$ fn) ;
1575
1619
}
1576
1620
1577
1621
/**
@@ -1593,13 +1637,14 @@ function $UrlMatcherFactory() {
1593
1637
extend ( this , {
1594
1638
id : id ,
1595
1639
type : type ,
1640
+ location : location ,
1596
1641
array : arrayMode ,
1597
- config : config ,
1598
1642
squash : squash ,
1599
1643
replace : replace ,
1600
1644
isOptional : isOptional ,
1601
- dynamic : undefined ,
1602
1645
value : $value ,
1646
+ dynamic : undefined ,
1647
+ config : config ,
1603
1648
toString : toString
1604
1649
} ) ;
1605
1650
} ;
@@ -1646,7 +1691,7 @@ function $UrlMatcherFactory() {
1646
1691
param = self [ key ] ;
1647
1692
val = paramValues [ key ] ;
1648
1693
isOptional = ! val && param . isOptional ;
1649
- result = result && ( isOptional || param . type . is ( val ) ) ;
1694
+ result = result && ( isOptional || ! ! param . type . is ( val ) ) ;
1650
1695
} ) ;
1651
1696
return result ;
1652
1697
} ,
@@ -1927,7 +1972,7 @@ function $UrlRouterProvider( $locationProvider, $urlMatcherFactory) {
1927
1972
$get . $inject = [ '$location' , '$rootScope' , '$injector' , '$browser' ] ;
1928
1973
function $get ( $location , $rootScope , $injector , $browser ) {
1929
1974
1930
- var baseHref = $browser . baseHref ( ) , location = $location . url ( ) ;
1975
+ var baseHref = $browser . baseHref ( ) , location = $location . url ( ) , lastPushedUrl ;
1931
1976
1932
1977
function appendBasePath ( url , isHtml5 , absolute ) {
1933
1978
if ( baseHref === '/' ) return url ;
@@ -1939,6 +1984,9 @@ function $UrlRouterProvider( $locationProvider, $urlMatcherFactory) {
1939
1984
// TODO: Optimize groups of rules with non-empty prefix into some sort of decision tree
1940
1985
function update ( evt ) {
1941
1986
if ( evt && evt . defaultPrevented ) return ;
1987
+ var ignoreUpdate = lastPushedUrl && $location . url ( ) === lastPushedUrl ;
1988
+ lastPushedUrl = undefined ;
1989
+ if ( ignoreUpdate ) return true ;
1942
1990
1943
1991
function check ( rule ) {
1944
1992
var handled = rule ( $injector , $location ) ;
@@ -2011,6 +2059,7 @@ function $UrlRouterProvider( $locationProvider, $urlMatcherFactory) {
2011
2059
2012
2060
push : function ( urlMatcher , params , options ) {
2013
2061
$location . url ( urlMatcher . format ( params || { } ) ) ;
2062
+ lastPushedUrl = options && options . $$avoidResync ? $location . url ( ) : undefined ;
2014
2063
if ( options && options . replace ) $location . replace ( ) ;
2015
2064
} ,
2016
2065
@@ -2140,7 +2189,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
2140
2189
ownParams : function ( state ) {
2141
2190
var params = state . url && state . url . params || new $$UMFP . ParamSet ( ) ;
2142
2191
forEach ( state . params || { } , function ( config , id ) {
2143
- if ( ! params [ id ] ) params [ id ] = new $$UMFP . Param ( id , null , config ) ;
2192
+ if ( ! params [ id ] ) params [ id ] = new $$UMFP . Param ( id , null , config , "config" ) ;
2144
2193
} ) ;
2145
2194
return params ;
2146
2195
} ,
@@ -2266,7 +2315,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
2266
2315
if ( ! state [ abstractKey ] && state . url ) {
2267
2316
$urlRouterProvider . when ( state . url , [ '$match' , '$stateParams' , function ( $match , $stateParams ) {
2268
2317
if ( $state . $current . navigable != state || ! equalForKeys ( $match , $stateParams ) ) {
2269
- $state . transitionTo ( state , $match , { location : false } ) ;
2318
+ $state . transitionTo ( state , $match , { inherit : true , location : false } ) ;
2270
2319
}
2271
2320
} ] ) ;
2272
2321
}
@@ -3147,7 +3196,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
3147
3196
3148
3197
if ( options . location && to . navigable ) {
3149
3198
$urlRouter . push ( to . navigable . url , to . navigable . locals . globals . $stateParams , {
3150
- replace : options . location === 'replace'
3199
+ $$avoidResync : true , replace : options . location === 'replace'
3151
3200
} ) ;
3152
3201
}
3153
3202
@@ -3243,15 +3292,9 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
3243
3292
options = extend ( { relative : $state . $current } , options || { } ) ;
3244
3293
var state = findState ( stateOrName , options . relative ) ;
3245
3294
3246
- if ( ! isDefined ( state ) ) {
3247
- return undefined ;
3248
- }
3249
-
3250
- if ( $state . $current !== state ) {
3251
- return false ;
3252
- }
3253
-
3254
- return isDefined ( params ) && params !== null ? angular . equals ( $stateParams , params ) : true ;
3295
+ if ( ! isDefined ( state ) ) { return undefined ; }
3296
+ if ( $state . $current !== state ) { return false ; }
3297
+ return params ? equalForKeys ( state . params . $$values ( params ) , $stateParams ) : true ;
3255
3298
} ;
3256
3299
3257
3300
/**
@@ -3315,13 +3358,9 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
3315
3358
}
3316
3359
3317
3360
var state = findState ( stateOrName , options . relative ) ;
3318
- if ( ! isDefined ( state ) ) {
3319
- return undefined ;
3320
- }
3321
- if ( ! isDefined ( $state . $current . includes [ state . name ] ) ) {
3322
- return false ;
3323
- }
3324
- return equalForKeys ( params , $stateParams ) ;
3361
+ if ( ! isDefined ( state ) ) { return undefined ; }
3362
+ if ( ! isDefined ( $state . $current . includes [ state . name ] ) ) { return false ; }
3363
+ return params ? equalForKeys ( state . params . $$values ( params ) , $stateParams , objectKeys ( params ) ) : true ;
3325
3364
} ;
3326
3365
3327
3366
@@ -4137,15 +4176,11 @@ function $StateRefActiveDirective($state, $stateParams, $interpolate) {
4137
4176
4138
4177
function isMatch ( ) {
4139
4178
if ( typeof $attrs . uiSrefActiveEq !== 'undefined' ) {
4140
- return $ state. $current . self === state && matchesParams ( ) ;
4179
+ return state && $state . is ( state . name , params ) ;
4141
4180
} else {
4142
- return state && $state . includes ( state . name ) && matchesParams ( ) ;
4181
+ return state && $state . includes ( state . name , params ) ;
4143
4182
}
4144
4183
}
4145
-
4146
- function matchesParams ( ) {
4147
- return ! params || equalForKeys ( params , $stateParams ) ;
4148
- }
4149
4184
} ]
4150
4185
} ;
4151
4186
}
0 commit comments