@@ -3,6 +3,99 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
3
3
4
4
var root , states = { } , $state ;
5
5
6
+ // Builds state properties from definition passed to registerState()
7
+ var stateBuilder = {
8
+ // Derive parent state from a hierarchical name only if 'parent' is not explicitly defined.
9
+ // state.children = [];
10
+ // if (parent) parent.children.push(state);
11
+ parent : function ( state ) {
12
+ if ( isDefined ( state . parent ) && state . parent ) return findState ( state . parent ) ;
13
+ // regex matches any valid composite state name
14
+ // would match "contact.list" but not "contacts"
15
+ var compositeName = / ^ ( .+ ) \. [ ^ . ] + $ / . exec ( state . name ) ;
16
+ return compositeName ? findState ( compositeName [ 1 ] ) : root ;
17
+ } ,
18
+
19
+ // Build a URLMatcher if necessary, either via a relative or absolute URL
20
+ url : function ( state ) {
21
+ var url = state . url ;
22
+
23
+ if ( isString ( url ) ) {
24
+ if ( url . charAt ( 0 ) == '^' ) {
25
+ return $urlMatcherFactory . compile ( url . substring ( 1 ) ) ;
26
+ }
27
+ return ( state . parent . navigable || root ) . url . concat ( url ) ;
28
+ }
29
+ var isMatcher = (
30
+ isObject ( url ) && isFunction ( url . exec ) && isFunction ( url . format ) && isFunction ( url . concat )
31
+ ) ;
32
+
33
+ if ( isMatcher || url == null ) {
34
+ return url ;
35
+ }
36
+ throw new Error ( "Invalid url '" + url + "' in state '" + state + "'" ) ;
37
+ } ,
38
+
39
+ // Keep track of the closest ancestor state that has a URL (i.e. is navigable)
40
+ navigable : function ( state ) {
41
+ return state . url ? state : ( state . parent ? state . parent . navigable : null ) ;
42
+ } ,
43
+
44
+ // Derive parameters for this state and ensure they're a super-set of parent's parameters
45
+ params : function ( state ) {
46
+ if ( ! state . params ) {
47
+ return state . url ? state . url . parameters ( ) : state . parent . params ;
48
+ }
49
+ if ( ! isArray ( state . params ) ) throw new Error ( "Invalid params in state '" + state + "'" ) ;
50
+ if ( state . url ) throw new Error ( "Both params and url specicified in state '" + state + "'" ) ;
51
+ return state . params ;
52
+ } ,
53
+
54
+ // If there is no explicit multi-view configuration, make one up so we don't have
55
+ // to handle both cases in the view directive later. Note that having an explicit
56
+ // 'views' property will mean the default unnamed view properties are ignored. This
57
+ // is also a good time to resolve view names to absolute names, so everything is a
58
+ // straight lookup at link time.
59
+ views : function ( state ) {
60
+ var views = { } ;
61
+
62
+ forEach ( isDefined ( state . views ) ? state . views : { '' : state } , function ( view , name ) {
63
+ if ( name . indexOf ( '@' ) < 0 ) name += '@' + state . parent . name ;
64
+ views [ name ] = view ;
65
+ } ) ;
66
+ return views ;
67
+ } ,
68
+
69
+ ownParams : function ( state ) {
70
+ if ( ! state . parent ) {
71
+ return state . params ;
72
+ }
73
+ var paramNames = { } ; forEach ( state . params , function ( p ) { paramNames [ p ] = true ; } ) ;
74
+
75
+ forEach ( state . parent . params , function ( p ) {
76
+ if ( ! paramNames [ p ] ) {
77
+ throw new Error ( "Missing required parameter '" + p + "' in state '" + state . name + "'" ) ;
78
+ }
79
+ paramNames [ p ] = false ;
80
+ } ) ;
81
+ var ownParams = [ ] ;
82
+
83
+ forEach ( paramNames , function ( own , p ) {
84
+ if ( own ) ownParams . push ( p ) ;
85
+ } ) ;
86
+ return ownParams ;
87
+ } ,
88
+
89
+ data : function ( state ) {
90
+ // inherit 'data' from parent and override by own values (if any)
91
+ if ( state . parent && state . parent . data ) {
92
+ state . data = angular . extend ( { } , state . parent . data , state . data ) ;
93
+ state . self . data = state . data ;
94
+ }
95
+ return state . data ;
96
+ }
97
+ } ;
98
+
6
99
function findState ( stateOrName ) {
7
100
var state ;
8
101
if ( isString ( stateOrName ) ) {
@@ -20,102 +113,28 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
20
113
// Wrap a new object around the state so we can store our private details easily.
21
114
state = inherit ( state , {
22
115
self : state ,
116
+ resolve : state . resolve || { } ,
23
117
toString : function ( ) { return this . name ; }
24
118
} ) ;
25
119
26
120
var name = state . name ;
27
121
if ( ! isString ( name ) || name . indexOf ( '@' ) >= 0 ) throw new Error ( "State must have a valid name" ) ;
28
122
if ( states [ name ] ) throw new Error ( "State '" + name + "'' is already defined" ) ;
29
123
30
- // Derive parent state from a hierarchical name only if 'parent' is not explicitly defined.
31
- var parent = root ;
32
- if ( ! isDefined ( state . parent ) ) {
33
- // regex matches any valid composite state name
34
- // would match "contact.list" but not "contacts"
35
- var compositeName = / ^ ( .+ ) \. [ ^ . ] + $ / . exec ( name ) ;
36
- if ( compositeName != null ) {
37
- parent = findState ( compositeName [ 1 ] ) ;
38
- }
39
- } else if ( state . parent != null ) {
40
- parent = findState ( state . parent ) ;
124
+ for ( var key in stateBuilder ) {
125
+ state [ key ] = stateBuilder [ key ] ( state ) ;
41
126
}
42
- state . parent = parent ;
43
- // inherit 'data' from parent and override by own values (if any)
44
- if ( state . parent && state . parent . data ) {
45
- state . data = angular . extend ( { } , state . parent . data , state . data ) ;
46
- state . self . data = state . data ;
47
- }
48
- // state.children = [];
49
- // if (parent) parent.children.push(state);
50
-
51
- // Build a URLMatcher if necessary, either via a relative or absolute URL
52
- var url = state . url ;
53
- if ( isString ( url ) ) {
54
- if ( url . charAt ( 0 ) == '^' ) {
55
- url = state . url = $urlMatcherFactory . compile ( url . substring ( 1 ) ) ;
56
- } else {
57
- url = state . url = ( parent . navigable || root ) . url . concat ( url ) ;
58
- }
59
- } else if ( isObject ( url ) &&
60
- isFunction ( url . exec ) && isFunction ( url . format ) && isFunction ( url . concat ) ) {
61
- /* use UrlMatcher (or compatible object) as is */
62
- } else if ( url != null ) {
63
- throw new Error ( "Invalid url '" + url + "' in state '" + state + "'" ) ;
64
- }
65
-
66
- // Keep track of the closest ancestor state that has a URL (i.e. is navigable)
67
- state . navigable = url ? state : parent ? parent . navigable : null ;
68
-
69
- // Derive parameters for this state and ensure they're a super-set of parent's parameters
70
- var params = state . params ;
71
- if ( params ) {
72
- if ( ! isArray ( params ) ) throw new Error ( "Invalid params in state '" + state + "'" ) ;
73
- if ( url ) throw new Error ( "Both params and url specicified in state '" + state + "'" ) ;
74
- } else {
75
- params = state . params = url ? url . parameters ( ) : state . parent . params ;
76
- }
77
-
78
- var paramNames = { } ; forEach ( params , function ( p ) { paramNames [ p ] = true ; } ) ;
79
- if ( parent ) {
80
- forEach ( parent . params , function ( p ) {
81
- if ( ! paramNames [ p ] ) {
82
- throw new Error ( "Missing required parameter '" + p + "' in state '" + name + "'" ) ;
83
- }
84
- paramNames [ p ] = false ;
85
- } ) ;
86
-
87
- var ownParams = state . ownParams = [ ] ;
88
- forEach ( paramNames , function ( own , p ) {
89
- if ( own ) ownParams . push ( p ) ;
90
- } ) ;
91
- } else {
92
- state . ownParams = params ;
93
- }
94
-
95
- // If there is no explicit multi-view configuration, make one up so we don't have
96
- // to handle both cases in the view directive later. Note that having an explicit
97
- // 'views' property will mean the default unnamed view properties are ignored. This
98
- // is also a good time to resolve view names to absolute names, so everything is a
99
- // straight lookup at link time.
100
- var views = { } ;
101
- forEach ( isDefined ( state . views ) ? state . views : { '' : state } , function ( view , name ) {
102
- if ( name . indexOf ( '@' ) < 0 ) name = name + '@' + state . parent . name ;
103
- views [ name ] = view ;
104
- } ) ;
105
- state . views = views ;
106
127
107
128
// Keep a full path from the root down to this state as this is needed for state activation.
108
- state . path = parent ? parent . path . concat ( state ) : [ ] ; // exclude root from path
129
+ state . path = state . parent ? state . parent . path . concat ( state ) : [ ] ; // exclude root from path
109
130
110
131
// Speed up $state.contains() as it's used a lot
111
- var includes = state . includes = parent ? extend ( { } , parent . includes ) : { } ;
132
+ var includes = state . includes = state . parent ? extend ( { } , state . parent . includes ) : { } ;
112
133
includes [ name ] = true ;
113
134
114
- if ( ! state . resolve ) state . resolve = { } ; // prevent null checks later
115
-
116
135
// Register the state in the global state list and with $urlRouter if necessary.
117
- if ( ! state [ 'abstract' ] && url ) {
118
- $urlRouterProvider . when ( url , [ '$match' , '$stateParams' , function ( $match , $stateParams ) {
136
+ if ( ! state [ 'abstract' ] && state . url ) {
137
+ $urlRouterProvider . when ( state . url , [ '$match' , '$stateParams' , function ( $match , $stateParams ) {
119
138
if ( $state . $current . navigable != state || ! equalForKeys ( $match , $stateParams ) ) {
120
139
$state . transitionTo ( state , $match , false ) ;
121
140
}
@@ -194,8 +213,8 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
194
213
toParams = normalize ( to . params , toParams || { } ) ;
195
214
196
215
// Broadcast start event and cancel the transition if requested
197
- if ( $rootScope . $broadcast ( '$stateChangeStart' , to . self , toParams , from . self , fromParams )
198
- . defaultPrevented ) return TransitionPrevented ;
216
+ var evt = $rootScope . $broadcast ( '$stateChangeStart' , to . self , toParams , from . self , fromParams ) ;
217
+ if ( evt . defaultPrevented ) return TransitionPrevented ;
199
218
200
219
// Resolve locals for the remaining states, but don't update any global state just
201
220
// yet -- if anything fails to resolve the current state needs to remain untouched.
0 commit comments