Source: singletons/history.js

/**
 * @class
 * @classdesc Manages and stores [Actions]{@link Syntree.Action}
 */
Syntree.History = {
    /**
     * Actions is stored as a queue with the most recent action at the end (index length-1).
     * Note that it is returned as an array with the most recent action at the front (index 0).
     *
     * @see Syntree.History.getAll()
     */
    actions: [],

    /**
     * While this is set to true, no [Actions]{@link Syntree.Action} are added to the history.
     * This is used so that we don't have to worry about creating new [Actions]{@link Syntree.Action} while undoing old ones.
     */
    silent: false,

    /**
     * Add an action to the history.
     *
     * @see Syntree.History.actions
     */
    addAction: function(action) {
        action = Syntree.Lib.checkArg(action, 'action');

        if (!this.silent) {
            this.actions.push(action);
        }
    },

    /**
     * Get the most recent [Action]{@link Syntree.Action}.
     *
     * @see Syntree.History.actions
     * @returns {Syntree.Action} - the most recent action.
     */
    getLast: function() {
        return this.actions[this.actions.length-1];
    },

    /**
     * Get all [Actions]{@link Syntree.Action}, reversed so that the most recent is at the front.
     *
     * @returns {Syntree.Action[]} - all actions in the history.
     *
     * @see Syntree.History.actions
     */
    getAll: function() {
        return this.actions.slice().reverse();
    },

    /**
     * Get all [Actions]{@link Syntree.Action}, reversed so that the most recent is at the front.
     * Filter by given type.
     *
     * @param {string} - type of action to filter by, e.g. 'select'
     * @returns {Syntree.Action[]} - all actions matching the given type
     *
     * @see Syntree.History.getAll
     * @see Syntree.History.actions
     */
    getAllByType: function(type) {
        type = Syntree.Lib.checkArg(type, 'string');

        var filtered = this.actions.filter(function(value) {
            return value.type === type;
        });
        return filtered.slice().reverse();
    },

    /**
     * Get the most recent [Action]{@link Syntree.Action} of given type.
     *
     * @param {string} - type of action to filter by, e.g. 'select'
     * @returns {Syntree.Action} - most recent action of given type
     *
     * @see Syntree.History.getLast
     * @see Syntree.History.actions
     */
    getLastOfType: function(type) {
        type = Syntree.Lib.checkArg(type, 'string');

        var actions = this.getByType(type);
        return actions[actions.length-1];
    },

    /**
     * Undo the most recent undoable [Action]{@link Syntree.Action}.
     * During this function, History is silenced so that it does not collect eroneously produced [Actions]{@link Syntree.Action}.
     *
     * @see Syntree.History.silent
     */
    undo: function() {
        this.silent = true;
        var all = this.getAll();
        for (i=0; i<all.length; i++) {
            if (typeof all[i].undo !== 'undefined') {
                this.removeAction(all[i]);
                all[i].undo();
                this.silent = false;
                return;
            }
        }
        this.silent = false;
    },

    /**
     * Remove the given [Action]{@link Syntree.Action} from the history.
     *
     * @param {Syntree.Action} - action to remove
     *
     * @see Syntree.History.actions
     */
    removeAction: function(a) {
        a = Syntree.Lib.checkArg(a, 'action');

        this.actions.splice(this.actions.indexOf(a),1);
    },

    /**
     * Get all [Actions]{@link Syntree.Action} representing a Node selection.
     * This is a convenience function, so that we don't have to filter for selected element type inline.
     *
     * @returns {Syntree.Action[]} - all Node select actions, most recent at index 0
     *
     * @see Syntree.History.actions
     */
    getNodeSelects: function() {
        var res = this.getAllByType('select');
        res = res.filter(function(x) {
            return Syntree.Lib.checkType(x.selected_obj, 'node');
        });
        return res;
    },

    toString: function() {
        return '[object History]';
    }
}