@@ -5,6 +5,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
55
66 // Builds state properties from definition passed to registerState()
77 var stateBuilder = {
8+
89 // Derive parent state from a hierarchical name only if 'parent' is not explicitly defined.
910 // state.children = [];
1011 // if (parent) parent.children.push(state);
@@ -92,28 +93,52 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
9293 if ( own ) ownParams . push ( p ) ;
9394 } ) ;
9495 return ownParams ;
96+ } ,
97+
98+ // Keep a full path from the root down to this state as this is needed for state activation.
99+ path : function ( state ) {
100+ return state . parent ? state . parent . path . concat ( state ) : [ ] ; // exclude root from path
101+ } ,
102+
103+ // Speed up $state.contains() as it's used a lot
104+ includes : function ( state ) {
105+ var includes = state . parent ? extend ( { } , state . parent . includes ) : { } ;
106+ includes [ state . name ] = true ;
107+ return includes ;
95108 }
96109 } ;
97110
98- function findState ( stateOrName ) {
99- var state ;
100- if ( isString ( stateOrName ) ) {
101- state = states [ stateOrName ] ;
102- if ( ! state ) throw new Error ( "No such state '" + stateOrName + "'" ) ;
103- } else {
104- state = states [ stateOrName . name ] ;
105- if ( ! state || state !== stateOrName && state . self !== stateOrName )
106- throw new Error ( "Invalid or unregistered state" ) ;
111+
112+ function findState ( stateOrName , base ) {
113+ var isStr = isString ( stateOrName ) ,
114+ name = isStr ? stateOrName : stateOrName . name ,
115+ path = isStr ? name . match ( / ^ ( (?: (?: \^ ) (?: \. ) ? ) { 1 , } ) ( .+ ) / ) : null ;
116+
117+ if ( path && path . length ) {
118+ if ( ! base ) throw new Error ( "No reference point given for path '" + stateOrName + "'" ) ;
119+ var rel = path [ 1 ] . split ( "." ) , i = 0 , pathLength = rel . length - 1 , current = base ;
120+
121+ for ( ; i < pathLength ; i ++ ) {
122+ if ( rel [ i ] === "^" ) current = current . parent ;
123+ if ( ! current ) throw new Error ( "Path '" + name + "' not valid for state '" + base . name + "'" ) ;
124+ }
125+ name = current . name + "." + path [ 2 ] ;
126+ }
127+ var state = states [ name ] ;
128+
129+ if ( state && ( isStr || ( ! isStr && ( state === stateOrName || state . self === stateOrName ) ) ) ) {
130+ return state ;
107131 }
108- return state ;
132+ throw new Error ( isStr ? "No such state '" + name + "'" : "Invalid or unregistered state" ) ;
109133 }
110134
135+
111136 function registerState ( state ) {
112137 // Wrap a new object around the state so we can store our private details easily.
113138 state = inherit ( state , {
114139 self : state ,
115140 resolve : state . resolve || { } ,
116- toString : function ( ) { return this . name ; }
141+ toString : function ( ) { return this . name ; }
117142 } ) ;
118143
119144 var name = state . name ;
@@ -123,13 +148,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
123148 for ( var key in stateBuilder ) {
124149 state [ key ] = stateBuilder [ key ] ( state ) ;
125150 }
126-
127- // Keep a full path from the root down to this state as this is needed for state activation.
128- state . path = state . parent ? state . parent . path . concat ( state ) : [ ] ; // exclude root from path
129-
130- // Speed up $state.contains() as it's used a lot
131- var includes = state . includes = state . parent ? extend ( { } , state . parent . includes ) : { } ;
132- includes [ name ] = true ;
151+ states [ name ] = state ;
133152
134153 // Register the state in the global state list and with $urlRouter if necessary.
135154 if ( ! state [ 'abstract' ] && state . url ) {
@@ -139,10 +158,10 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
139158 }
140159 } ] ) ;
141160 }
142- states [ name ] = state ;
143161 return state ;
144162 }
145163
164+
146165 // Implicit root state that is always active
147166 root = registerState ( {
148167 name : '' ,
0 commit comments