https://github.com/jamesplease/hsm.js
A minimal hierarchal state machine for Javascript.
https://github.com/jamesplease/hsm.js
Last synced: 4 months ago
JSON representation
A minimal hierarchal state machine for Javascript.
- Host: GitHub
- URL: https://github.com/jamesplease/hsm.js
- Owner: jamesplease
- License: mit
- Created: 2015-02-06T23:30:23.000Z (over 10 years ago)
- Default Branch: master
- Last Pushed: 2015-05-09T20:37:36.000Z (about 10 years ago)
- Last Synced: 2025-03-10T03:04:52.022Z (4 months ago)
- Language: JavaScript
- Homepage:
- Size: 258 KB
- Stars: 12
- Watchers: 2
- Forks: 0
- Open Issues: 8
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# hsm.js
[](https://travis-ci.org/jmeas/hsm.js)
[](https://codeclimate.com/github/jmeas/hsm.js)
[](https://codeclimate.com/github/jmeas/hsm.js)
[](https://david-dm.org/jmeas/hsm.js)
[](https://david-dm.org/jmeas/hsm.js#info=devDependencies)A minimal hierarchal state machine for Javascript.
### What is a state machine?
A state machine is a thing that consists of a finite number of states. At any time, it can
only be in exactly one of those states. The name 'state machine' is an abbreviation of 'finite
state machine.'### What is a hierarchal state machine?
A state machine with a notion of nested states is called a hierarchal state machine. When
a nested state is active, each of its parent states are also active.### What is this library?
This library provides a barebones implementation of the above system. It remains
unopinionated about two things:1. What a state is
2. How to transition between two statesIt has two opinions of importance:
1. Child states cannot be defined before parent states
2. Transitions are always asynchronous### API
##### `Hsm.getParentStateName( stateName )`
Given a state, get the value of the parent state.
```js
Hsm.getParentStateName('cheese.is.good');
// => 'cheese.is'
```##### `constructor( options )`
Create a new instance of Hsm. Pass the `states` option to populate the state machine with those states.
```js
var hsm = new Hsm({
states: {
'': IndexState,
'books': BooksState,
'books.book': BookState
}
});
```Hsm instances are created in an `undefined` state. You must transition
to the initial state manually.##### `setState( stateName, stateDefinition )`
Set a new state. `stateDefinition` can be anything – this library
does nothing with the states. `stateName` can be anything as well,
but an Error will be thrown if the parent state does not exist.```js
hsm.setState('food', FoodState);
hsm.setState('food.breakfast', BreakfastState);
```##### `getState( stateName )`
Access the object that represents `stateName`.
##### `hasState( stateName )`
Returns a Boolean representing whether or not `stateName` has
been set.##### `currentStateName()`
Return the name of the current state.
##### `currentState()`
Return the object that represents the current state.
##### `transitionTo( newState )`
Asynchronously transition to `newState` by delegating to `transition`. This
method is not intended to be overridden. To customize the transition behavior,
use the `transition` hook described below.##### `transition( stateDiff, cancel )`
Define an asynchronous transition. `stateDiff` is an object representing the
difference between the current state and the new state.An example diff between `books.book.author` and `books.comments` is:
```js
{
outStates: ['books.book.author', 'books.book'],
inStates: ['books.comments']
}
```Call `cancel` to cancel the transition.
###### A note on transition to `index`
Defining an `index` state at any other state gives you an opportunity
to enter a unique state that is only triggered when you land **on** that state,
but not on a child state.To get a better understanding of what I mean, consider these states:
```js
{
'': RootState,
'books': BooksRoute,
'books.book': BookRoute
}
```When you transition to `books.book`, both `books` and `book` will be entered. If
instead you transition to just `books`, only `books` is triggered, as you might
have guessed. This is fine in some situations, but what if you want something special
to happen only when you land on `books`, but not when you enter a child of `books`? That's
what the `index` route is for.Taking those same routes from above:
```js
{
'': RootState,
'books': BooksRoute,
'books.index': BookIndexRoute,
'books.book': BookRoute
}
```Transitioning to `books.book` remains the same. But if you transition to `books`,
both `books` and `index` will be activated, giving you a unique state for `books` by
itself. Transitioning to `books` and `books.index` behave identically.