Knockout N-Level Undo

by gurkavcu

HTML

<script src="https://github.com/downloads/SteveSanderson/knockout/knockout-1.3.0beta.js"></script>
<script src="https://raw.github.com/SteveSanderson/knockout.mapping/master/build/output/knockout.mapping-latest.js"></script>
<div>
    <button data-bind="click: function() { undo(); }">Undo</button>
    <input data-bind="value: firstName" />
    <input data-bind="value: lastName" />
    <textarea data-bind="value: text"></textarea>
</div>

JavaScript

(function() {
    window.undoStack = [];
    
    var currentState = JSON.stringify({
        firstName: 'Paul',
        lastName: 'Tyng',
        text: 'Text' 
    })
        , performingUndo = false
        , viewModel = ko.mapping.fromJSON(currentState);
        
    ko.dependentObservable(function() {
        ko.toJS(viewModel); //subscribe to all properties    
    }, viewModel).subscribe(function() {
        if(!performingUndo) {
        undoStack.push(currentState);
        currentState = ko.mapping.toJSON(viewModel);
    }
    });
        
        window.undo = function() {
            performingUndo = true;
            if(undoStack.length > 1)
            {
                currentState = undoStack.pop();
                ko.mapping.fromJSON(currentState, {}, viewModel);
            }
            else {
                currentState = undoStack[0];
                ko.mapping.fromJSON(undoStack[0], {}, viewModel);
            }
            performingUndo = false;
    };
    
    ko.applyBindings(viewModel);
})();