Undoable - Observable Array
An example of using the Undoable extension with an observable array.
by Areson
HTML
<script src="https://raw.github.com/SteveSanderson/knockout/master/build/output/knockout-latest.debug.js"></script>
<script src="https://raw.github.com/SteveSanderson/knockout.mapping/master/build/output/knockout.mapping-latest.debug.js"></script>
<label>Value: <input id="valueBox" type="text" data-bind="value: observe().value"/></label>
<button id="addButton">Add Value</button>
<button data-bind="click: function(){observe.undo()}">Undo</button>
<hr/>
<h3>Array Data</h3>
<table data-bind="foreach: observe">
<tr><td data-bind="text: $data"></td></tr>
</table>
<hr/>
<h3>Available Snapshots</h3>
<table data-bind="foreach: observe.undoStack()">
<tr data-bind="click: $parent.observe.undo, style: {color: isCurrentUndoLevel()?'green':'grey'}">
<td data-bind="text: name"></td>
</tr>
</table>
JavaScript
ko.extenders.undoable= function(target, options) {
var _trackChanges = ko.observable(true);
var _stackSize = options.stackSize || 0;
var _undoData = {};
_undoData.undoStack = ko.observableArray([]);
//Setup our stack items
var StackItem = function(index, data) {
var item = {
data: data,
index: index
};
item.isCurrentUndoLevel = ko.computed(function() {
var undoStack = _undoData.undoStack();
var stackLength = undoStack.length;
var firstIndex = (stackLength > 0)?undoStack[0].index:0;
var temp = (stackLength > 0 && this.index - firstIndex == undoStack[stackLength - 1].index);
return(stackLength > 0 && this.index - firstIndex == undoStack[stackLength - 1].index);
}, item);
return(item);
}
_undoData.isDirty = ko.computed(function() {
if(_trackChanges())
{
var undoStack = _undoData.undoStack;
var stackLength = undoStack().length;
var firstIndex = (stackLength > 0)?undoStack()[0].index:0;
undoStack.push(new StackItem(stackLength + firstIndex, ko.toJSON(target)));
if(_stackSize > 0 && stackLength >= _stackSize)
undoStack.splice(0, stackLength - _stackSize + 1);
}
});
//Setup a dependent observable to track
//the list of snapshots
target.undoStack = ko.computed({
read: function() {
return _undoData.undoStack();
},
write: function() {}
});
//This is the function that controls how the
//snapshots get mapped back to the observable.
//The data that is passed is JSON.
var _undoFunction = function(snapshot, target) {
if($.isPlainObject(target()))
ko.mapping.fromJSON(snapshot.data, null, target);
else
target($.parseJSON(snapshot.data));
...