1
1
/**
2
2
* State-based routing for AngularJS
3
- * @version v0.2.13
3
+ * @version v0.2.13-dev-2015-03-22
4
4
* @link http://angular-ui.github.com/
5
5
* @license MIT License, http://www.opensource.org/licenses/MIT
6
6
*/
@@ -68,7 +68,7 @@ function objectKeys(object) {
68
68
}
69
69
var result = [ ] ;
70
70
71
- angular . forEach ( object , function ( val , key ) {
71
+ forEach ( object , function ( val , key ) {
72
72
result . push ( key ) ;
73
73
} ) ;
74
74
return result ;
@@ -768,13 +768,13 @@ function UrlMatcher(pattern, config, parentMatcher) {
768
768
return params [ id ] ;
769
769
}
770
770
771
- function quoteRegExp ( string , pattern , squash ) {
771
+ function quoteRegExp ( string , pattern , squash , optional ) {
772
772
var surroundPattern = [ '' , '' ] , result = string . replace ( / [ \\ \[ \] \^ $ * + ? . ( ) | { } ] / g, "\\$&" ) ;
773
773
if ( ! pattern ) return result ;
774
774
switch ( squash ) {
775
- case false : surroundPattern = [ '(' , ')' ] ; break ;
775
+ case false : surroundPattern = [ '(' , ')' + ( optional ? "?" : "" ) ] ; break ;
776
776
case true : surroundPattern = [ '?(' , ')?' ] ; break ;
777
- default : surroundPattern = [ '(' + squash + "|" , ')?' ] ; break ;
777
+ default : surroundPattern = [ '(' + squash + "|" , ')?' ] ; break ;
778
778
}
779
779
return result + surroundPattern [ 0 ] + pattern + surroundPattern [ 1 ] ;
780
780
}
@@ -801,7 +801,7 @@ function UrlMatcher(pattern, config, parentMatcher) {
801
801
if ( p . segment . indexOf ( '?' ) >= 0 ) break ; // we're into the search part
802
802
803
803
param = addParameter ( p . id , p . type , p . cfg , "path" ) ;
804
- compiled += quoteRegExp ( p . segment , param . type . pattern . source , param . squash ) ;
804
+ compiled += quoteRegExp ( p . segment , param . type . pattern . source , param . squash , param . isOptional ) ;
805
805
segments . push ( p . segment ) ;
806
806
last = placeholder . lastIndex ;
807
807
}
@@ -912,7 +912,7 @@ UrlMatcher.prototype.exec = function (path, searchParams) {
912
912
913
913
function decodePathArray ( string ) {
914
914
function reverseString ( str ) { return str . split ( "" ) . reverse ( ) . join ( "" ) ; }
915
- function unquoteDashes ( str ) { return str . replace ( / \\ - / , "-" ) ; }
915
+ function unquoteDashes ( str ) { return str . replace ( / \\ - / g , "-" ) ; }
916
916
917
917
var split = reverseString ( string ) . split ( / - (? ! \\ ) / ) ;
918
918
var allReversed = map ( split , reverseString ) ;
@@ -1150,6 +1150,11 @@ Type.prototype.pattern = /.*/;
1150
1150
1151
1151
Type . prototype . toString = function ( ) { return "{Type:" + this . name + "}" ; } ;
1152
1152
1153
+ /** Given an encoded string, or a decoded object, returns a decoded object */
1154
+ Type . prototype . $normalize = function ( val ) {
1155
+ return this . is ( val ) ? val : this . decode ( val ) ;
1156
+ } ;
1157
+
1153
1158
/*
1154
1159
* Wraps an existing custom Type as an array of Type, depending on 'mode'.
1155
1160
* e.g.:
@@ -1163,7 +1168,6 @@ Type.prototype.toString = function() { return "{Type:" + this.name + "}"; };
1163
1168
Type . prototype . $asArray = function ( mode , isSearch ) {
1164
1169
if ( ! mode ) return this ;
1165
1170
if ( mode === "auto" && ! isSearch ) throw new Error ( "'auto' array mode is for query parameters only" ) ;
1166
- return new ArrayType ( this , mode ) ;
1167
1171
1168
1172
function ArrayType ( type , mode ) {
1169
1173
function bindTo ( type , callbackName ) {
@@ -1212,8 +1216,12 @@ Type.prototype.$asArray = function(mode, isSearch) {
1212
1216
this . is = arrayHandler ( bindTo ( type , 'is' ) , true ) ;
1213
1217
this . equals = arrayEqualsHandler ( bindTo ( type , 'equals' ) ) ;
1214
1218
this . pattern = type . pattern ;
1219
+ this . $normalize = arrayHandler ( bindTo ( type , '$normalize' ) ) ;
1220
+ this . name = type . name ;
1215
1221
this . $arrayMode = mode ;
1216
1222
}
1223
+
1224
+ return new ArrayType ( this , mode ) ;
1217
1225
} ;
1218
1226
1219
1227
@@ -1241,7 +1249,7 @@ function $UrlMatcherFactory() {
1241
1249
string : {
1242
1250
encode : valToString ,
1243
1251
decode : valFromString ,
1244
- is : regexpMatches ,
1252
+ is : function ( val ) { return typeof val === "string" ; } ,
1245
1253
pattern : / [ ^ / ] * /
1246
1254
} ,
1247
1255
int : {
@@ -1615,7 +1623,10 @@ function $UrlMatcherFactory() {
1615
1623
*/
1616
1624
function $$getDefaultValue ( ) {
1617
1625
if ( ! injector ) throw new Error ( "Injectable functions cannot be called at configuration time" ) ;
1618
- return injector . invoke ( config . $$fn ) ;
1626
+ var defaultValue = injector . invoke ( config . $$fn ) ;
1627
+ if ( defaultValue !== null && defaultValue !== undefined && ! self . type . is ( defaultValue ) )
1628
+ throw new Error ( "Default value (" + defaultValue + ") for parameter '" + self . id + "' is not an instance of Type (" + self . type . name + ")" ) ;
1629
+ return defaultValue ;
1619
1630
}
1620
1631
1621
1632
/**
@@ -1629,7 +1640,7 @@ function $UrlMatcherFactory() {
1629
1640
return replacement . length ? replacement [ 0 ] : value ;
1630
1641
}
1631
1642
value = $replace ( value ) ;
1632
- return isDefined ( value ) ? self . type . decode ( value ) : $$getDefaultValue ( ) ;
1643
+ return ! isDefined ( value ) ? $$getDefaultValue ( ) : self . type . $normalize ( value ) ;
1633
1644
}
1634
1645
1635
1646
function toString ( ) { return "{Param:" + id + " " + type + " squash: '" + squash + "' optional: " + isOptional + "}" ; }
@@ -1685,15 +1696,20 @@ function $UrlMatcherFactory() {
1685
1696
return equal ;
1686
1697
} ,
1687
1698
$$validates : function $$validate ( paramValues ) {
1688
- var result = true , isOptional , val , param , self = this ;
1689
-
1690
- forEach ( this . $$keys ( ) , function ( key ) {
1691
- param = self [ key ] ;
1692
- val = paramValues [ key ] ;
1693
- isOptional = ! val && param . isOptional ;
1694
- result = result && ( isOptional || ! ! param . type . is ( val ) ) ;
1695
- } ) ;
1696
- return result ;
1699
+ var keys = this . $$keys ( ) , i , param , rawVal , normalized , encoded ;
1700
+ for ( i = 0 ; i < keys . length ; i ++ ) {
1701
+ param = this [ keys [ i ] ] ;
1702
+ rawVal = paramValues [ keys [ i ] ] ;
1703
+ if ( ( rawVal === undefined || rawVal === null ) && param . isOptional )
1704
+ break ; // There was no parameter value, but the param is optional
1705
+ normalized = param . type . $normalize ( rawVal ) ;
1706
+ if ( ! param . type . is ( normalized ) )
1707
+ return false ; // The value was not of the correct Type, and could not be decoded to the correct Type
1708
+ encoded = param . type . encode ( normalized ) ;
1709
+ if ( angular . isString ( encoded ) && ! param . type . pattern . exec ( encoded ) )
1710
+ return false ; // The value was of the correct type, but when encoded, did not match the Type's regexp
1711
+ }
1712
+ return true ;
1697
1713
} ,
1698
1714
$$parent : undefined
1699
1715
} ;
@@ -2336,6 +2352,13 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
2336
2352
var globSegments = glob . split ( '.' ) ,
2337
2353
segments = $state . $current . name . split ( '.' ) ;
2338
2354
2355
+ //match single stars
2356
+ for ( var i = 0 , l = globSegments . length ; i < l ; i ++ ) {
2357
+ if ( globSegments [ i ] === '*' ) {
2358
+ segments [ i ] = '*' ;
2359
+ }
2360
+ }
2361
+
2339
2362
//match greedy starts
2340
2363
if ( globSegments [ 0 ] === '**' ) {
2341
2364
segments = segments . slice ( indexOf ( segments , globSegments [ 1 ] ) ) ;
@@ -2351,13 +2374,6 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
2351
2374
return false ;
2352
2375
}
2353
2376
2354
- //match single stars
2355
- for ( var i = 0 , l = globSegments . length ; i < l ; i ++ ) {
2356
- if ( globSegments [ i ] === '*' ) {
2357
- segments [ i ] = '*' ;
2358
- }
2359
- }
2360
-
2361
2377
return segments . join ( '' ) === globSegments . join ( '' ) ;
2362
2378
}
2363
2379
@@ -2566,6 +2582,13 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
2566
2582
* published to scope under the controllerAs name.
2567
2583
* <pre>controllerAs: "myCtrl"</pre>
2568
2584
*
2585
+ * @param {string|object= } stateConfig.parent
2586
+ * <a id='parent'></a>
2587
+ * Optionally specifies the parent state of this state.
2588
+ *
2589
+ * <pre>parent: 'parentState'</pre>
2590
+ * <pre>parent: parentState // JS variable</pre>
2591
+ *
2569
2592
* @param {object= } stateConfig.resolve
2570
2593
* <a id='resolve'></a>
2571
2594
*
@@ -2597,15 +2620,19 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
2597
2620
* transitioned to, the `$stateParams` service will be populated with any
2598
2621
* parameters that were passed.
2599
2622
*
2623
+ * (See {@link ui.router.util.type:UrlMatcher UrlMatcher} `UrlMatcher`} for
2624
+ * more details on acceptable patterns )
2625
+ *
2600
2626
* examples:
2601
2627
* <pre>url: "/home"
2602
2628
* url: "/users/:userid"
2603
2629
* url: "/books/{bookid:[a-zA-Z_-]}"
2604
2630
* url: "/books/{categoryid:int}"
2605
2631
* url: "/books/{publishername:string}/{categoryid:int}"
2606
2632
* url: "/messages?before&after"
2607
- * url: "/messages?{before:date}&{after:date}"</pre>
2633
+ * url: "/messages?{before:date}&{after:date}"
2608
2634
* url: "/messages/:mailboxid?{before:date}&{after:date}"
2635
+ * </pre>
2609
2636
*
2610
2637
* @param {object= } stateConfig.views
2611
2638
* <a id='views'></a>
@@ -2909,8 +2936,8 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
2909
2936
* @methodOf ui.router.state.$state
2910
2937
*
2911
2938
* @description
2912
- * A method that force reloads the current state. All resolves are re-resolved, events are not re-fired,
2913
- * and controllers reinstantiated (bug with controllers reinstantiating right now, fixing soon) .
2939
+ * A method that force reloads the current state. All resolves are re-resolved,
2940
+ * controllers reinstantiated, and events re-fired .
2914
2941
*
2915
2942
* @example
2916
2943
* <pre>
@@ -3609,7 +3636,7 @@ function $ViewScrollProvider() {
3609
3636
}
3610
3637
3611
3638
return function ( $element ) {
3612
- $timeout ( function ( ) {
3639
+ return $timeout ( function ( ) {
3613
3640
$element [ 0 ] . scrollIntoView ( ) ;
3614
3641
} , 0 , false ) ;
3615
3642
} ;
@@ -3807,24 +3834,36 @@ function $ViewDirective( $state, $injector, $uiViewScroll, $interpolate)
3807
3834
updateView ( true ) ;
3808
3835
3809
3836
function cleanupLastView ( ) {
3810
- if ( previousEl ) {
3811
- previousEl . remove ( ) ;
3812
- previousEl = null ;
3813
- }
3837
+ if ( previousEl ) {
3838
+ previousEl . remove ( ) ;
3839
+ previousEl = null ;
3840
+ }
3814
3841
3815
- if ( currentScope ) {
3816
- currentScope . $destroy ( ) ;
3817
- currentScope = null ;
3818
- }
3842
+ if ( previousScope ) {
3843
+ previousScope . $destroy ( ) ;
3844
+ previousScope = null ;
3845
+ }
3819
3846
3820
- if ( currentEl ) {
3821
- renderer . leave ( currentEl , function ( ) {
3822
- previousEl = null ;
3823
- } ) ;
3847
+ var previousScope = currentScope ;
3848
+ currentScope = null ;
3824
3849
3825
- previousEl = currentEl ;
3826
- currentEl = null ;
3827
- }
3850
+ if ( currentEl ) {
3851
+ renderer . leave ( currentEl , function ( ) {
3852
+ previousEl = null ;
3853
+
3854
+ if ( previousScope ) {
3855
+ previousScope . $destroy ( ) ;
3856
+ previousScope = null ;
3857
+ }
3858
+ } ) ;
3859
+
3860
+ previousEl = currentEl ;
3861
+ currentEl = null ;
3862
+ }
3863
+ else if ( previousScope ) {
3864
+ previousScope . $destroy ( ) ;
3865
+ previousScope = null ;
3866
+ }
3828
3867
}
3829
3868
3830
3869
function updateView ( firstTime ) {
@@ -3894,6 +3933,7 @@ function $ViewDirectiveFill ( $compile, $controller, $state, $interpolate
3894
3933
3895
3934
if ( locals . $$controller ) {
3896
3935
locals . $scope = scope ;
3936
+ locals . $element = $element ;
3897
3937
var controller = $controller ( locals . $$controller , locals ) ;
3898
3938
if ( locals . $$controllerAs ) {
3899
3939
scope [ locals . $$controllerAs ] = controller ;
@@ -4001,17 +4041,20 @@ function stateContext(el) {
4001
4041
*/
4002
4042
$StateRefDirective . $inject = [ '$state' , '$timeout' ] ;
4003
4043
function $StateRefDirective ( $state , $timeout ) {
4004
- var allowedOptions = [ 'location' , 'inherit' , 'reload' ] ;
4044
+ var allowedOptions = [ 'location' , 'inherit' , 'reload' , 'absolute' ] ;
4005
4045
4006
4046
return {
4007
4047
restrict : 'A' ,
4008
4048
require : [ '?^uiSrefActive' , '?^uiSrefActiveEq' ] ,
4009
4049
link : function ( scope , element , attrs , uiSrefActive ) {
4010
4050
var ref = parseStateRef ( attrs . uiSref , $state . current . name ) ;
4011
4051
var params = null , url = null , base = stateContext ( element ) || $state . $current ;
4012
- var newHref = null , isAnchor = element . prop ( "tagName" ) === "A" ;
4052
+ // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
4053
+ var hrefKind = toString . call ( element . prop ( 'href' ) ) === '[object SVGAnimatedString]' ?
4054
+ 'xlink:href' : 'href' ;
4055
+ var newHref = null , isAnchor = element . prop ( "tagName" ) . toUpperCase ( ) === "A" ;
4013
4056
var isForm = element [ 0 ] . nodeName === "FORM" ;
4014
- var attr = isForm ? "action" : "href" , nav = true ;
4057
+ var attr = isForm ? "action" : hrefKind , nav = true ;
4015
4058
4016
4059
var options = { relative : base , inherit : true } ;
4017
4060
var optionsOverride = scope . $eval ( attrs . uiSrefOpts ) || { } ;
0 commit comments