git @ Cat's Eye Technologies yoob.js / master src / yoob / state-machine.js
master

Tree @master (Download .tar.gz)

state-machine.js @masterraw · history · blame

/*
 * This file is part of yoob.js version 0.12
 * Available from https://github.com/catseye/yoob.js/
 * This file is in the public domain.  See http://unlicense.org/ for details.
 */
if (window.yoob === undefined) yoob = {};

/*
 * A simple, generic state machine.
 */
yoob.StateMachine = function() {
    this.init = function(cfg) {
        this.currentState = null;
        this.state = {};
        for (var nm in cfg.stateClasses) {
            var stateClass = cfg.stateClasses[nm];
            this.state[nm] = new stateClass();
            this.state[nm].machine = this;
            if (this.state[nm].init) {
                this.state[nm].init();
            } else {
                throw new Error('"' + nm + '" must implement .init');
            }
        }
        return this;
    };

    /*
     * When called, calls .onleave() on the current state (if there is one)
     * automatically, then calls .onenter() on the target state.
     */
    this.enter = function(nm, args) {
        if (this.currentState) {
            if (this.currentState.onleave) {
                this.currentState.onleave();
            } else {
                throw new Error('"' + nm + '" must implement .onleave');
            }
        }
        var state = this.state[nm];
        this.currentState = state;
        if (state.onenter) {
            state.onenter(args);
        } else {
            throw new Error('"' + nm + '" must implement .onenter');
        }
        return this;
    };
};

/*
 * You don't have to subclass this.  But you should follow its rules.
 */
yoob.State = function() {
    this.init = function() {
        throw new Error("You must implement .init()");
    };

    /*
     * When called, set up the things for the current state:
     *   Create/reveal any DOM elements
     *   Link up their events
     *   Set up timers/animations.  They can unilaterally transition
     *     to another state when done (so this is a "transition state")
     *   onenter itself can enter another state immediately, but it
     *     should only do so as the last thing in the handler.
     */
    this.onenter = function(args) {
        throw new Error("You must implement .onenter()");
    };

    /*
     * When called,
     *   Hide/destroy DOM elements relevant to this state
     *   Unlink events, if necessary
     *   Cancel timers/animations
     */
    this.onleave = function() {
        throw new Error("You must implement .onleave()");
    };
};