function StateMachine(options) { options = options || {}; if (!(this instanceof StateMachine)) { return new StateMachine(options); } var states = this.states = (options.states || this.states), initialState = this.initialState = (options.initialState || this.initialState || Object.keys(states).shift()); this.transitionTo(initialState); } StateMachine.prototype = { constructor: StateMachine, initialState: null, state: null, states: {}, get currentState() { return this.states[this.state]; }, transitionTo: function(state) { var currentState = this.currentState, newState = this.states[state]; if (typeof(newState) !== 'object') { throw "Invalid state '" + state + "'"; } if (currentState && typeof(currentState.exit) === 'function') { currentState.exit(this); } this.state = state; if (typeof(newState.enter) === 'function') { newState.enter(this); } }, send: function(method) { var args = [].slice.call(arguments, 1), currentState = this.currentState, func = currentState[method]; if (typeof(func) !== 'function') { throw "Cannot call '" + method + "' on current state '" + this.state + "'"; } args.unshift(this); func.apply(currentState, args); } };