@@ -126,6 +126,30 @@ function $StickyStateProvider($stateProvider, uirextras_coreProvider) {
126126 return true ;
127127 }
128128
129+ function calcTreeChanges ( transition ) {
130+ var fromPath = transition . fromState . path ;
131+ var toPath = transition . toState . path ;
132+ var toParams = transition . toParams ;
133+ var keep = 0 , state = toPath [ keep ] ;
134+
135+ if ( transition . options . inherit ) {
136+ toParams = inheritParams ( $stateParams , toParams || { } , $state . $current , transition . toState ) ;
137+ }
138+
139+ while ( state && state === fromPath [ keep ] && paramsEqualForState ( state . ownParams , toParams , transition . fromParams ) ) {
140+ // We're "keeping" this state. bump keep var and get the next state in toPath for the next iteration.
141+ state = toPath [ ++ keep ] ;
142+ }
143+
144+ return {
145+ keep : keep ,
146+ retained : fromPath . slice ( 0 , keep ) ,
147+ exiting : fromPath . slice ( keep ) ,
148+ entering : toPath . slice ( keep )
149+ } ;
150+ }
151+
152+
129153 var stickySupport = {
130154 getInactiveStates : function ( ) {
131155 return map ( inactiveStates , angular . identity ) ;
@@ -139,96 +163,63 @@ function $StickyStateProvider($stateProvider, uirextras_coreProvider) {
139163 // keep: The number of states being "kept"
140164 // inactives: Array of all states which will be inactive if the transition is completed.
141165 // reactivatingStates: Array of all states which will be reactivated if the transition is completed.
142- // inactiveOrphans : Array of previously inactive states, which are being orphaned by the transition
166+ // orphans : Array of previously inactive states, which are being orphaned by the transition
143167 // Note: Transitioning directly to an inactive state with inactive children will reactivate the state, but exit all the inactive children.
144- // Note: Each inactiveOrphans is potentially the root of an orphaned subtree. The substates of the orphaned node are not in the list.
145168 // enter: Enter transition type for all added states. This is a parallel array to "toStates" array in $state.transitionTo.
146169 // exit: Exit transition type for all removed states. This is a parallel array to "fromStates" array in $state.transitionTo.
147170 // }
148171 processTransition : function ( transition ) {
172+ var treeChanges = calcTreeChanges ( transition ) ;
173+ var currentInactives = map ( inactiveStates , angular . identity ) ;
174+ var futureInactives , exitingTypes , enteringTypes ;
175+ var keep = treeChanges . keep ;
176+
177+
149178 /////////////////////////////////////////
150179 // helper functions
180+ function notIn ( array ) { return function ( elem ) { return array . indexOf ( elem ) === - 1 ; } ; }
151181 function flattenReduce ( memo , list ) { return memo . concat ( list ) ; }
152- function uniqReduce ( memo , orphan ) { if ( memo . indexOf ( orphan ) === - 1 ) memo . push ( orphan ) ; return memo ; }
153- function pushAll ( dest , src ) { dest . push . apply ( dest , src ) ; return dest ; }
154- function val ( x ) { return function ( ) { return x ; } ; }
182+ function uniqReduce ( memo , orphan ) { if ( notIn ( memo ) ( orphan ) ) memo . push ( orphan ) ; return memo ; }
155183 function prop ( attr ) { return function ( obj ) { return obj [ attr ] ; } }
156-
157184 function typeIs ( type ) { return function ( obj ) { return obj . type === type ; } }
158185 function isChildOf ( state ) { return function ( other ) { return other . parent === state ; } ; }
159- function notEntered ( state ) { return enteringStates . indexOf ( state ) === - 1 ; }
186+ var notEntering = notIn ( treeChanges . entering ) ;
160187 function notSticky ( state ) { return ! state . sticky ; }
161188 ////////////////////////////////////
162189
163190
164- // This object is returned
165- var result = { inactives : [ ] , enter : [ ] , exit : [ ] , keep : 0 } ;
166- var fromPath = transition . fromState . path ;
167- var toPath = transition . toState . path ;
168- var toParams = transition . toParams ;
169- var keep = 0 , state = toPath [ keep ] ;
170-
171- if ( transition . options . inherit ) {
172- toParams = inheritParams ( $stateParams , toParams || { } , $state . $current , transition . toState ) ;
173- }
174-
175- while ( state && state === fromPath [ keep ] && paramsEqualForState ( state . ownParams , toParams , transition . fromParams ) ) {
176- // We're "keeping" this state. bump keep var and get the next state in toPath for the next iteration.
177- state = toPath [ ++ keep ] ;
178- }
179-
180- result . keep = keep ;
181- var treeChanges = {
182- retained : fromPath . slice ( 0 , keep ) ,
183- exiting : fromPath . slice ( keep ) ,
184- entering : toPath . slice ( keep )
185- } ;
186-
187- result . exit = treeChanges . retained . map ( val ( undefined ) ) ;
188- result . enter = treeChanges . retained . map ( val ( undefined ) ) ;
189-
190- var inactives = [ ] , reactivatingStates = [ ] , enteringStates = [ ] , exitingStates = [ ] ;
191- var allInactivesByParents = mapInactivesByImmediateParent ( ) ;
192-
193- // Two things must be satisfied in order to inactivate "exiting" states (instead of exit them):
191+ // Calculate the "exit" transition types for states being exited in fromPath
192+ // Exit types will be either "inactivate" or "exit"
193+ // Two things must be satisfied in order to inactivate the "exiting" states (instead of exit them):
194194 // - The first element of the exiting path must be sticky
195195 // - We must be entering any sibling state of the sticky (we can check this using entering.length)
196- //if (treeChanges.exiting[0] && treeChanges.exiting[0].sticky && treeChanges.entering.length > 0) {
197- var isFromSticky = treeChanges . exiting [ 0 ] && treeChanges . exiting [ 0 ] . sticky && treeChanges . entering . length > 0 ;
198-
199- var exiting = treeChanges . exiting . map ( function ( state ) {
200- var type = isFromSticky ? "inactivate" : "exit" ;
196+ var shouldInactivate = treeChanges . exiting [ 0 ] && treeChanges . exiting [ 0 ] . sticky && treeChanges . entering . length > 0 ;
197+ exitingTypes = treeChanges . exiting . map ( function ( state ) {
198+ var type = shouldInactivate ? "inactivate" : "exit" ;
201199 return { type : type , state : state } ;
202200 } ) ;
203201
204- pushAll ( inactives , exiting . filter ( typeIs ( "inactivate" ) ) . map ( prop ( "state" ) ) ) ;
205- pushAll ( exitingStates , exiting . filter ( typeIs ( "exit" ) ) . map ( prop ( "state" ) ) ) ;
206- pushAll ( result . exit , exiting . map ( prop ( "type" ) ) ) ;
207202
208- // Calculate the "enter" transitions for new states in toPath
209- // Enter transitions will be either "enter", "reactivate", or "reload" where
203+ // Calculate the "enter" transition types for states being entered in toPath
204+ // Enter types will be either "enter", "reactivate", or "reload" where:
210205 // enter: full resolve, no special logic
211206 // reactivate: use previous locals
212207 // reload: like 'enter', except exit the inactive state before entering it.
213208 var reloaded = ! ! transition . options . reload ;
214- var entering = treeChanges . entering . map ( function ( state ) {
215- var type = getEnterTransition ( state , toParams , transition . reloadStateTree , reloaded ) ;
209+ enteringTypes = treeChanges . entering . map ( function ( state ) {
210+ var type = getEnterTransition ( state , transition . toParams , transition . reloadStateTree , reloaded ) ;
216211 reloaded = reloaded || type === 'reload' ;
217212 return { type : type , state : state } ;
218213 } ) ;
219214
220- pushAll ( enteringStates , entering . map ( prop ( "state" ) ) ) ;
221- pushAll ( reactivatingStates , entering . filter ( typeIs ( "reactivate" ) ) . map ( prop ( "state" ) ) ) ;
222- pushAll ( result . enter , entering . map ( prop ( "type" ) ) ) ;
223-
224- var allInactives = map ( inactiveStates , angular . identity ) ;
225-
226215 // Find all the "orphaned" states. those states that are :
216+ // - are siblings of the entering states
227217 // - previously inactive
228- // - are children or siblings of the entering states
229- // - are not being reactivated
218+ // - are not being reactivated (entered)
230219 // - are not sticky
231- // These states should now be exited because of the sticky rules
220+ // unioned with:
221+ // - children of the toState
222+ // - previously inactive
232223 //
233224 // Given:
234225 // - states A (sticky: true), B, A.foo, A.bar
@@ -246,35 +237,50 @@ function $StickyStateProvider($stateProvider, uirextras_coreProvider) {
246237 // Orphan case 3)
247238 // - Transition directly to A orphans the inactive sticky state A.foo; it should be exited
248239 // Note: transition from B to A.bar does not orphan A.foo
249- var orphanedRoots = enteringStates
250- // For each entering state in the path, find any sibling states which are currently inactive
251- . map ( function ( entering ) { return allInactives . filter ( isChildOf ( entering . parent ) ) ; } )
240+ // Note 2: each orphaned state might be the parent of a larger inactive subtree.
241+ var orphanedRoots = treeChanges . entering
242+ // For each entering state in the path, find all sibling states which are currently inactive
243+ . map ( function ( entering ) { return currentInactives . filter ( isChildOf ( entering . parent ) ) ; } )
252244 // Flatten nested arrays. Now we have an array of inactive states that are children of the ones being entered.
253245 . reduce ( flattenReduce , [ ] )
254- . reduce ( uniqReduce , [ ] )
255246 // Consider "orphaned": only those children that are themselves not currently being entered
256- . filter ( notEntered )
247+ . filter ( notEntering )
257248 // Consider "orphaned": only those children that are not themselves sticky states.
258- . filter ( notSticky ) ;
259- var orphanedChildrenOfToState = allInactives . filter ( isChildOf ( transition . toState ) ) ;
249+ . filter ( notSticky )
250+ // Finally, union that set with any inactive children of the "to state"
251+ . concat ( currentInactives . filter ( isChildOf ( transition . toState ) ) ) ;
252+
253+ var currentInactivesByParent = mapInactivesByImmediateParent ( ) ;
254+ var allOrphans = orphanedRoots
255+ . map ( function ( root ) { return currentInactivesByParent [ root . name ] } )
256+ . filter ( angular . isDefined )
257+ . reduce ( flattenReduce , [ ] )
258+ . concat ( orphanedRoots )
259+ // Sort by depth to exit orphans in proper order
260+ . sort ( function ( a , b ) { return a . name . split ( "." ) . length - b . name . split ( "." ) . length ; } ) ;
260261
261262 // Add them to the list of states being exited.
262- exitingStates = exitingStates . concat ( orphanedRoots ) . concat ( orphanedChildrenOfToState ) ;
263+ var exitOrOrphaned = exitingTypes
264+ . filter ( typeIs ( "exit" ) )
265+ . map ( prop ( "state" ) )
266+ . concat ( allOrphans ) ;
263267
264268 // Now calculate the states that will be inactive if this transition succeeds.
265269 // We have already pushed the transitionType == "inactivate" states to 'inactives'.
266270 // Second, add all the existing inactive states
267- inactives = inactives . concat ( map ( inactiveStates , angular . identity ) ) ;
268- // Finally, remove any states that are scheduled for "exit" or "enter", "reactivate", or "reload"
269- inactives = inactives . filter ( function ( state ) {
270- return exitingStates . indexOf ( state ) === - 1 && enteringStates . indexOf ( state ) === - 1 ;
271- } ) ;
272-
273- result . inactives = inactives ;
274- result . reactivatingStates = reactivatingStates ;
275- result . inactiveOrphans = orphanedRoots . concat ( orphanedChildrenOfToState ) ;
276-
277- return result ;
271+ futureInactives = currentInactives
272+ . filter ( notIn ( exitOrOrphaned ) )
273+ . filter ( notIn ( treeChanges . entering ) )
274+ . concat ( exitingTypes . filter ( typeIs ( "inactivate" ) ) . map ( prop ( "state" ) ) ) ;
275+
276+ return {
277+ keep : keep ,
278+ enter : new Array ( keep ) . concat ( enteringTypes . map ( prop ( "type" ) ) ) ,
279+ exit : new Array ( keep ) . concat ( exitingTypes . map ( prop ( "type" ) ) ) ,
280+ inactives : futureInactives ,
281+ reactivatingStates : enteringTypes . filter ( typeIs ( "reactivate" ) ) . map ( prop ( "state" ) ) ,
282+ orphans : allOrphans
283+ } ;
278284 } ,
279285
280286 // Adds a state to the inactivated sticky state registry.
0 commit comments