lib/command/transaction_history.js

Summary

Maintainability
C
7 hrs
Test Coverage
'use strict';


var ActionsHistory = require('./actions_history')
    , Transaction = require('./transaction')
    , miloCore = require('milo-core')
    , Messenger = miloCore.Messenger
    , _ = miloCore.proto;


module.exports = TransactionHistory;


var SCHEDULED = '_scheduled';


function TransactionHistory(maxLength) {
    this.transactions = new ActionsHistory(maxLength);
    this.currentBatch = undefined;
    this.currentTransaction = undefined;
    this[SCHEDULED] = false;
}


_.extendProto(TransactionHistory, {
    storeCommand: TransactionHistory$storeCommand,
    endTransaction: TransactionHistory$endTransaction,
    storeTransaction: TransactionHistory$storeTransaction,
    deleteLastTransaction: TransactionHistory$deleteLastTransaction,
    undo: TransactionHistory$undo,
    redo: TransactionHistory$redo,
    inTransaction: TransactionHistory$inTransaction,

    getDescription: TransactionHistory$getDescription,
    useMessenger: TransactionHistory$useMessenger,
    destroy: TransactionHistory$destroy
});


/**
 * Stores command in the history. 
 * @param {Command} command           
 * @param {Boolean} appendTransaction If `true`, appends to the current or previous transaction if there is no current transaction.
 */
function TransactionHistory$storeCommand(command, appendTransaction) {
    if (appendTransaction && !(this.currentTransaction || this.currentBatch)) {
        var transaction = this.transactions.getLastAction();
        transaction.storeCommand(command);
        _postTransactionMessage.call(this, 'appended', transaction);
        return transaction;
    }

    if (! this.currentBatch) this.currentBatch = new Transaction;
    this.currentBatch.storeCommand(command);
    if (! this[SCHEDULED]) {
        this[SCHEDULED] = true;
        _.deferMethod(this, _storeTransaction);
    }
    return this.currentBatch;
}


function TransactionHistory$deleteLastTransaction() {
    if (this.currentBatch || this.currentTransaction) {
        this.currentBatch = undefined;
        this.currentTransaction = undefined;
    } else {
        this.transactions.deleteLast();
    }
}


function _storeTransaction() {
    if (this.currentBatch) {
        _addBatchToTransaction.call(this);
        _.deferMethod(this, _storeTransaction);
    } else {
        _storeCurrentTransaction.call(this);
        this[SCHEDULED] = false;
    }
}


function TransactionHistory$endTransaction() {
    _addBatchToTransaction.call(this);
    _storeCurrentTransaction.call(this);
}


function _addBatchToTransaction() {
    if (this.currentBatch) {
        if (! this.currentTransaction) this.currentTransaction = new Transaction;
        this.currentTransaction.merge(this.currentBatch);
        this.currentBatch = undefined;
    } 
}


function _storeCurrentTransaction() {
    if (this.currentTransaction) {
        var t = this.currentTransaction;
        this.transactions.store(t);
        _postTransactionMessage.call(this, 'stored', t);

        this.currentTransaction = undefined;
    }
}


function TransactionHistory$storeTransaction(transaction) {
    this.endTransaction();

    this.transactions.store(transaction);
    _postTransactionMessage.call(this, 'stored', transaction);
}


function _postTransactionMessage(msg, transaction) {
    if (this._messenger)
        this._messenger.postMessage(msg, { transaction: transaction });
}


function _postTransactionMessageSync(msg, transaction) {
    if (this._messenger)
        this._messenger.postMessageSync(msg, { transaction: transaction });
}


function TransactionHistory$undo(cb) {
    var t = this.transactions.nextUndoAction();
    if (!t) return;
    _postTransactionMessageSync.call(this, 'undoing', t);
    var self = this;
    this.transactions.undo(function() {
        _postTransactionMessage.call(self, 'undone', t);
        cb && cb();
    });
    return t;
}


function TransactionHistory$redo(cb) {
    var t = this.transactions.nextRedoAction();
    if (!t) return;
    _postTransactionMessageSync.call(this, 'redoing', t);
    var self = this;
    this.transactions.redo(function() {
        _postTransactionMessage.call(self, 'redone', t);
        cb && cb();
    });
    return t;
}


function TransactionHistory$inTransaction() {
    return this[SCHEDULED];
}


function TransactionHistory$getDescription() {
    return this.transactions.getDescription();
}


function TransactionHistory$useMessenger() {
    this._messenger = new Messenger(this, Messenger.defaultMethods);
    return this._messenger;
}


function TransactionHistory$destroy() {
    if (this._messenger) this._messenger.destroy();
    delete this.transactions;
}