API Docs for:
Show:

File: development/js/mixins/fsm.js

import {Mixin} from '../utils/classes';
import FSM from '../utils/fsm';



/**
 * @module Mixins
 */


/**
 * The FSM utility as a mixin, provides a finite state machine
 * @class Mixins.FSM
 */

const STATES = Symbol('__finiteStateMaschine');



const FSMMixin = Mixin({
    /**
     * Allows to add a new state (see example for more info)
     *
     * @method addState
     * @for Mixins.FSM
     * @param {object} state The new state
     * @return {this} The instance
     *
     * @example
     *     import FSM from './mixins/fsm';
     *
     *     const MyClass = FSM(class {
     *         constructor() {
     *             // New states MyClass can be in are added
     *             this.addState({
     *                     name: 'init',
     *                     events: {next: 'after'}
     *                 }).
     *                 addState({
     *                     name: 'after',
     *                     events: {next: 'last', prev: 'init'}
     *                 }).
     *                 addState({
     *                     name: 'last',
     *                     events: {prev: 'after'}
     *                 });
     *         }
     *     });
     */
    addState(state) {
        if (!this[STATES]) {
            this[STATES] = FSM();
        }
        this[STATES].addState(state);
        return this;
    },
    /**
     * Allows to add an array of new states all at once
     *
     * @method addStates
     * @for Mixins.FSM
     * @param {array} states The new states
     * @return {this} The instance
     *
     * @example
     *     import FSM from './mixins/fsm';
     *
     *     const MyStates = [
     *         {name: 'init', events: {next: 'after'}},
     *         {name: 'after', events: {next: 'last', prev: 'init'}},
     *         {name: 'last', events: {prev: 'after'}}
     *     ];
     *     
     *     const MyClass = FSM(class {
     *         constructor() {
     *             // New states MyClass can be in are added
     *             this.addStates(MyStates);
     *         }
     *     });
     */
    addStates(states) {
        states.forEach(this.addState.bind(this));
        return this;
    },
    /**
     * Returns the name of the current state or null if no state is set
     *
     * @method getState
     * @for Mixins.FSM
     * @return {string|null} Name of the current state or null
     *
     * @example
     *     import FSM from './mixins/fsm';
     *
     *     const MyStates = [
     *         {name: 'init', events: {next: 'after'}},
     *         {name: 'after', events: {next: 'last', prev: 'init'}},
     *         {name: 'last', events: {prev: 'after'}}
     *     ];
     *     
     *     const MyClass = FSM(class {
     *         constructor() {
     *             // New states MyClass can be in are added
     *             this.addStates(MyStates);
     *         }
     *     });
     *
     *     let myInstance = new MyClass();
     *     myInstance.getState(); // -> 'init'
     */
    getState() {
        return this[STATES] ? this[STATES].getState() : null;
    },
    /**
     * Removes a state from the FSM, if it is not the active one. Please note
     *     that you have to take care to preserve a valid path of events to
     *     consume, or otherwise the FSM will be trapped in a single state
     *     (which may be fine if that is your intention)
     *
     * @method removeState
     * @for Mixins.FSM
     * @param {object} state The state to remove
     * @return {this} The instance
     *
     * @example
     *     import FSM from './mixins/fsm';
     *
     *     const MyStates = [
     *         {name: 'init', events: {next: 'after'}},
     *         {name: 'after', events: {next: 'last', prev: 'init'}},
     *         {name: 'last', events: {prev: 'after'}}
     *     ];
     *     
     *     const MyClass = FSM(class {
     *         constructor() {
     *             // New states MyClass can be in are added
     *             this.addStates(MyStates);
     *             this.removeState(MyStates[2]); // remove the 'last' state
     *         }
     *     });
     */
    removeState(state) {
        if (this[STATES]) {
            this[STATES].removeState(state);
        }
        return this;
    },
    /**
     * Transitions from the current state to the state specified by the given
     *     name of event to consume.
     *
     * @method consume
     * @for Mixins.FSM
     * @param {string} eventName Name of the event to consume
     * @return {this} The instance
     *
     * @example
     *     import FSM from './mixins/fsm';
     *
     *     const MyStates = [
     *         {name: 'init', events: {next: 'after'}},
     *         {name: 'after', events: {next: 'last', prev: 'init'}},
     *         {name: 'last', events: {prev: 'after'}}
     *     ];
     *     
     *     const MyClass = FSM(class {
     *         constructor() {
     *             // New states MyClass can be in are added
     *             this.addStates(MyStates);
     *         }
     *     });
     *
     *     let myInstance = new MyClass();
     *     myInstance.getState(); // -> 'init'
     *
     *     myInstance.consume('next').consume('next').getState(); // -> 'last'
     */
    consume(eventName) {
        if (this[STATES]) {
            this[STATES].consume(eventName);
        }
        return this;
    },
    /**
     * Destroys all states associated with the FSM
     *
     * @method destroyStates
     * @for Mixins.FSM
     * @return {undefined} Nothing
     *
     * @example
     *     import FSM from './mixins/fsm';
     *
     *     const MyStates = [
     *         {name: 'init', events: {next: 'after'}},
     *         {name: 'after', events: {next: 'last', prev: 'init'}},
     *         {name: 'last', events: {prev: 'after'}}
     *     ];
     *     
     *     const MyClass = FSM(class {
     *         constructor() {
     *             // New states MyClass can be in are added
     *             this.addStates(MyStates);
     *         }
     *     });
     *
     *     let myInstance = new MyClass();
     *     myInstance.destroyStates();
     */
    destroyStates() {
        if (this[STATES]) {
            this[STATES].destroyStates();
            this[STATES] = undefined;
        }
    }
});



export default FSMMixin;