Class: SC.StatechartManager

The startchart manager mixin allows an object to be a statechart. By becoming a statechart, the object can then be manage a set of its own states.

This implementation of the statechart manager closely follows the concepts stated in D. Harel's original paper "Statecharts: A Visual Formalism For Complex Systems" (www.wisdom.weizmann.ac.il/~harel/papers/Statecharts.pdf).

The statechart allows for complex state heircharies by nesting states within states, and allows for state orthogonality based on the use of concurrent states.

At minimum, a statechart must have one state: The root state. All other states in the statechart are a decendents (substates) of the root state.

The following example shows how states are nested within a statechart:

MyApp.Statechart = SC.Object.extend(SC.StatechartManager, {
  rootState: SC.State.design({
    initialSubstate: 'stateA',

    stateA: SC.State.design({
      // ... can continue to nest further states
    }),

    stateB: SC.State.design({
      // ... can continue to nest further states
    })
  })
});

Note how in the example above, the root state as an explicit initial substate to enter into. If no initial substate is provided, then the statechart will default to the the state's first substate.

You can also defined states without explicitly defining the root state. To do so, simply create properties on your object that represents states. Upon initialization, a root state will be constructed automatically by the mixin and make the states on the object substates of the root state. As an example:

MyApp.Statechart = SC.Object.extend(SC.StatechartManager, {
  initialState: 'stateA',

  stateA: SC.State.design({
    // ... can continue to nest further states
  }),

  stateB: SC.State.design({
    // ... can continue to nest further states
  })
});

If you liked to specify a class that should be used as the root state but using the above method to defined states, you can set the rootStateExample property with a class that extends from SC.State. If the rootStateExample property is not explicitly assigned the then default class used will be SC.State.

To provide your statechart with orthogonality, you use concurrent states. If you use concurrent states, then your statechart will have multiple current states. That is because each concurrent state represents an independent state structure from other concurrent states. The following example shows how to provide your statechart with concurrent states:

MyApp.Statechart = SC.Object.extend(SC.StatechartManager, {
  rootState: SC.State.design({
    substatesAreConcurrent: YES,

    stateA: SC.State.design({
      // ... can continue to nest further states
    }),

    stateB: SC.State.design({
      // ... can continue to nest further states
    })
  })
});

Above, to indicate that a state's substates are concurrent, you just have to set the substatesAreConcurrent to YES. Once done, then stateA and stateB will be independent of each other and each will manage their own current substates. The root state will then have more then one current substate.

To define concurrent states directly on the object without explicitly defining a root, you can do the following:

MyApp.Statechart = SC.Object.extend(SC.StatechartManager, {
  statesAreConcurrent: YES,

  stateA: SC.State.design({
    // ... can continue to nest further states
  }),

  stateB: SC.State.design({
    // ... can continue to nest further states
  })
});

Remember that a startchart can have a mixture of nested and concurrent states in order for you to create as complex of statecharts that suite your needs. Here is an example of a mixed state structure:

MyApp.Statechart = SC.Object.extend(SC.StatechartManager, {
  rootState: SC.State.design({
    initialSubstate: 'stateA',

    stateA: SC.State.design({
      substatesAreConcurrent: YES,

      stateM: SC.State.design({ ... })
      stateN: SC.State.design({ ... })
      stateO: SC.State.design({ ... })
    }),

    stateB: SC.State.design({
      initialSubstate: 'stateX',

      stateX: SC.State.design({ ... })
      stateY: SC.State.design({ ... })
    })
  })
});

Depending on your needs, a statechart can have lots of states, which can become hard to manage all within one file. To modularize your states and make them easier to manage and maintain, you can plug-in states into other states. Let's say we are using the statechart in the last example above, and all the code is within one file. We could update the code and split the logic across two or more files like so:

// state_a.js

MyApp.StateA = SC.State.extend({
  substatesAreConcurrent: YES,

  stateM: SC.State.design({ ... })
  stateN: SC.State.design({ ... })
  stateO: SC.State.design({ ... })
});

// state_b.js

MyApp.StateB = SC.State.extend({
  substatesAreConcurrent: YES,

  stateM: SC.State.design({ ... })
  stateN: SC.State.design({ ... })
  stateO: SC.State.design({ ... })
});

// statechart.js

MyApp.Statechart = SC.Object.extend(SC.StatechartManager, {
  rootState: SC.State.design({
    initialSubstate: 'stateA',
    stateA: SC.State.plugin('MyApp.StateA'),
    stateB: SC.State.plugin('MyApp.StateB')
  })
});

Using state plug-in functionality is optional. If you use the plug-in feature you can break up your statechart into as many files as you see fit.

Defined in: statechart.js

Field Summary

Instance Methods

Field Detail

autoInitStatechart Boolean

Indicates if the statechart should be automatically initialized by this object after it has been created. If YES then initStatechart will be called automatically, otherwise it will not.

delegate SC.Object

A statechart delegate used by the statechart and the states that the statechart manages. The value assigned must adhere to the {@link SC.StatechartDelegate} mixin.

See:
SC.StatechartDelegate
initialState String

Indicates what state should be the initial state of this statechart. The value assigned must be the name of a property on this object that represents a state. As well, the statesAreConcurrent must be set to NO.

This property will only be used if the rootState property is not assigned.

See:
#rootState
monitor

A statechart monitor that can be used to monitor this statechart. Useful for debugging purposes. A monitor will only be used if monitorIsActive is true.

NOTE: This is only available in debug mode!

monitorIsActive Boolean

Indicates whether to use a monitor to monitor that statechart's activities. If true then the monitor will be active, otherwise the monitor will not be used. Useful for debugging purposes.

NOTE: This is only available in debug mode!

name

Optional name you can provide the statechart with. If set this will be included in tracing and error output as well as detail output. Useful for debugging/diagnostic purposes

owner SC.Object

Sets who the owner is of this statechart. If null then the owner is this object otherwise the owner is the assigned object.

See:
#statechartOwnerKey
rootState

The root state of this statechart. All statecharts must have a root state.

If this property is left unassigned then when the statechart is initialized it will used the rootStateExample, initialState, and statesAreConcurrent properties to construct a root state.

See:
#rootStateExample
#initialState
#statesAreConcurrent
rootStateExample

Represents the class used to construct a class that will be the root state for this statechart. The class assigned must derive from SC.State.

This property will only be used if the rootState property is not assigned.

See:
#rootState
statechartIsInitialized Boolean
Indicates if this statechart has been initialized
statechartOwnerKey String

Used to specify what property (key) on the statechart should be used as the owner property. By default the property is 'owner'.

statechartTraceKey String

Used to specify what property (key) on the statechart should be used as the trace property. By default the property is 'trace'.

NOTE: This is only available in debug mode!

statesAreConcurrent Boolean

Indicates if properties on this object representing states are concurrent to each other. If YES then they are concurrent, otherwise they are not. If the YES, then the initialState property must not be assigned.

This property will only be used if the rootState property is not assigned.

See:
#rootState
suppressStatechartWarnings Boolean

If yes, any warning messages produced by the statechart or any of its states will not be logged, otherwise all warning messages will be logged.

While designing and debugging your statechart, it's best to keep this value false. In production you can then suppress the warning messages.

trace Boolean

Indicates whether to trace the statecharts activities. If true then the statechart will output its activites to the browser's JS console. Useful for debugging purposes.

NOTE: This is only available in debug mode!

See:
#statechartTraceKey

Instance Method Detail

createRootState(state, attrs)
Will create a root state for the statechart
Parameters:
state
attrs
currentStateCount()
Returns the count of the current states for this statechart
Returns:
Number
the count
currentStates()
Returns an array of all the current states for this statechart
Returns:
Array
the current states
destroyMixin()
details()
Returns:
Hash
doesContainState(value)
Checks if the given value represents a state is this statechart
Parameters:
value
{State|String} either a state object or the name of a state
Returns:
Boolean
true if the state does belong ot the statechart, otherwise false is returned
enteredStates()

Returns an array of all the states that are currently entered for this statechart.

Returns:
Array
the currently entered states
enterState(state, context)

What will actually invoke a state's enterState method.

Called during the state transition process whenever the gotoState method is invoked.

If the context provided is a state route context object ({@link SC.StateRouteContext}), then if the given state has a enterStateByRoute method, that method will be invoked, otherwise the state's enterState method will be invoked by default. The state route context object will be supplied to both enter methods in either case.

Parameters:
state
{SC.State} the state whose enterState method is to be invoked
context
{Hash} a context hash object to provide the enterState method
exitState(state, context)

What will actually invoke a state's exitState method.

Called during the state transition process whenever the gotoState method is invoked.

Parameters:
state
{SC.State} the state whose enterState method is to be invoked
context
{Hash} a context hash object to provide the enterState method
firstCurrentState()
Returns the first current state for this statechart.
Returns:
SC.State
getState(value)
Gets a state from the statechart that matches the given value
Parameters:
value
{State|String} either a state object of the name of a state
Returns:
State
if a match then the matching state is returned, otherwise null is returned
gotoHistoryState(state, fromCurrentState, recursive, context)

When called, the statechart will proceed to make transitions to the given state then follow that state's history state.

You can either go to a given state's history recursively or non-recursively. To go to a state's history recursively means to following each history state's history state until no more history states can be followed. Non-recursively means to just to the given state's history state but do not recusively follow history states. If the given state does not have a history state, then the statechart will just follow normal procedures when making state transitions.

Because a statechart can have one or more current states, depending on if the statechart has any concurrent states, it is optional to provided current state in which to start the state transition process from. If no current state is provided, then the statechart will default to the first current state that it has; which, depending on the make up of that statechart, can lead to unexpected outcomes. For a statechart with concurrent states, it is best to explicitly supply a current state.

Method can be called in the following ways:

// With one arguments.
gotoHistoryState(<state>)

// With two arguments.
gotoHistoryState(<state>, <state | boolean | hash>)

// With three arguments.
gotoHistoryState(<state>, <state>, <boolean | hash>)
gotoHistoryState(<state>, <boolean>, <hash>)

// With four argumetns
gotoHistoryState(<state>, <state>, <boolean>, <hash>)

where is either a SC.State object or a string and is a regular JS hash object.

Parameters:
state
{SC.State|String} the state to go to and follow it's history state
fromCurrentState
{SC.State|String} Optional. the current state to start the state transition process from
recursive
{Boolean} Optional. whether to follow history states recursively.
context
gotoState(state, fromCurrentState, useHistory, context)

When called, the statechart will proceed with making state transitions in the statechart starting from a current state that meet the statechart conditions. When complete, some or all of the statechart's current states will be changed, and all states that were part of the transition process will either be exited or entered in a specific order.

The state that is given to go to will not necessarily be a current state when the state transition process is complete. The final state or states are dependent on factors such an initial substates, concurrent states, and history states.

Because the statechart can have one or more current states, it may be necessary to indicate what current state to start from. If no current state to start from is provided, then the statechart will default to using the first current state that it has; depending of the make up of the statechart (no concurrent state vs. with concurrent states), the outcome may be unexpected. For a statechart with concurrent states, it is best to provide a current state in which to start from.

When using history states, the statechart will first make transitions to the given state and then use that state's history state and recursively follow each history state's history state until there are no more history states to follow. If the given state does not have a history state, then the statechart will continue following state transition procedures.

Method can be called in the following ways:

// With one argument.
gotoState(<state>)

// With two argument.
gotoState(<state>, <state | boolean | hash>)

// With three argument.
gotoState(<state>, <state>, <boolean | hash>)
gotoState(<state>, <boolean>, <hash>)

// With four argument.
gotoState(<state>, <state>, <boolean>, <hash>)

where is either a SC.State object or a string and is a regular JS hash object.

Parameters:
state
{SC.State|String} the state to go to (may not be the final state in the transition process)
fromCurrentState
{SC.State|String} Optional. The current state to start the transition process from.
useHistory
{Boolean} Optional. Indicates whether to include using history states in the transition process
context
{Hash} Optional. A context object that will be passed to all exited and entered states
gotoStateActive()
Indicates if the statechart is in an active goto state process
gotoStateSuspended()

Indicates if the statechart is in an active goto state process that has been suspended

initMixin()
initStatechart()

Initializes the statechart. By initializing the statechart, it will create all the states and register them with the statechart. Once complete, the statechart can be used to go to states and send events to.

invokeStateMethod(methodName, args, func)

Used to invoke a method on current states. If the method can not be executed on a current state, then the state's parent states will be tried in order of closest ancestry.

A few notes:

  1. Calling this is not the same as calling sendEvent or sendAction. Rather, this should be seen as calling normal methods on a state that will not call gotoState or gotoHistoryState.
  2. A state will only ever be invoked once per call. So if there are two or more current states that have the same parent state, then that parent state will only be invoked once if none of the current states are able to invoke the given method.

When calling this method, you are able to supply zero ore more arguments that can be pass onto the method called on the states. As an example

invokeStateMethod('render', context, firstTime);

The above call will invoke the render method on the current states and supply the context and firstTime arguments to the method.

Because a statechart can have more than one current state and the method invoked may return a value, the addition of a callback function may be provided in order to handle the returned value for each state. As an example, let's say we want to call a calculate method on the current states where the method will return a value when invoked. We can handle the returned values like so:

invokeStateMethod('calculate', value, function (state, result) {
  // .. handle the result returned from calculate that was invoked
  //    on the given state
})

If the method invoked does not return a value and a callback function is supplied, then result value will simply be undefined. In all cases, if a callback function is given, it must be the last value supplied to this method.

invokeStateMethod will return a value if only one state was able to have the given method invoked on it, otherwise no value is returned.

Parameters:
methodName
{String} methodName a method name
args
{Object...} Optional. any additional arguments
func
{Function} Optional. a callback function. Must be the last value supplied if provided.
Returns:

a value if the number of current states is one, otherwise undefined is returned. The value is the result of the method that got invoked on a state.

respondsTo(event)
Parameters:
event
{String} the property name to check
Returns:
Boolean
resumeGotoState()
Resumes an active goto state transition process that has been suspended.
sendEvent(event, arg1, arg2)

Sends a given event to all the statechart's current states.

If a current state does can not respond to the sent event, then the current state's parent state will be tried. This process is recursively done until no more parent state can be tried.

Note that a state will only be checked once if it can respond to an event. Therefore, if there is a state S that handles event foo and S has concurrent substates, then foo will only be invoked once; not as many times as there are substates.

Parameters:
event
{String} name of the event
arg1
{Object} optional argument
arg2
{Object} optional argument
Returns:
SC.Responder
the responder that handled it or null
See:
#stateWillTryToHandleEvent
#stateDidTryToHandleEvent
statechartDelegate()

Computed property that returns an objects that adheres to the {@link SC.StatechartDelegate} mixin. If the #delegate is not assigned then this object is the default value returned.

See:
SC.StatechartDelegate
#delegate
statechartLogError(msg)
Used to log a statechart error message
Parameters:
msg
statechartLogPrefix()
statechartLogTrace(msg, style)

Used to log a statechart trace message

NOTE: This is only available in debug mode!

Parameters:
msg
style
statechartLogWarning(msg)
Used to log a statechart warning message
Parameters:
msg
stateDidTryToHandleEvent(state, event, handler, handled)

Used to notify the statechart that a state did try to handle event that has been passed to it.

Parameters:
state SC.State
the state that did try to handle the event
event String
the event the state did try to handle
handler String
the name of the method on the state that did try to handle the event
handled Boolean
indicates if the handler was able to handle the event
stateIsCurrentState(state)
Checks if a given state is a current state of this statechart.
Parameters:
state
{State|String} the state to check
Returns:
Boolean
true if the state is a current state, otherwise fals is returned
stateIsEntered(state)
Checks if a given state is a currently entered state of this statechart.
Parameters:
state
{State|String} the state to check
Returns:
Boolean
true if the state is a currently entered state, otherwise false is returned
stateWillTryToHandleEvent(state, event, handler)

Used to notify the statechart that a state will try to handle event that has been passed to it.

Parameters:
state SC.State
the state that will try to handle the event
event String
the event the state will try to handle
handler String
the name of the method on the state that will try to handle the event
toStringWithDetails()

Returns a formatted string of detailed information about this statechart. Useful for diagnostic/debugging purposes.

Returns:
String
NOTE: This is only available in debug mode!
See:
#details
tryToPerform(event, arg1, arg2)
Parameters:
event
{String} what to perform
arg1
{Object} Optional
arg2
{Object} Optional
Returns:
Boolean
YES if handled, NO if not handled
Documentation generated by JsDoc Toolkit 2.4.0 on Wed Apr 08 2015 10:02:21 GMT-0600 (CST)