For an online app I was looking for an existing undo manager in Javascript. My first place to shop was at jQuery, and although it looks like one had been created as plugin for jQuery, only an info page is online.
Other resources where either tied to a larger library (Ruby on Rails, tinymce) or too complicated (like jsUndoable).
So that left me to write one myself. And although the principle is not too difficult, it did take me a couple of trial and error rounds before I got it right. Hopefully this code will save others some time.
My Undo Manager registers undo and redo actions simultaneously and stores them as commands (see command pattern) to store set and unset actions.
With each new/create action you call UndoManager.register(...) with these parameters:
param undoObj: caller of the undo function
param undoFunc: function to be called at myUndoManager.undo
param undoParamsList: (array) parameter list
param undoMsg: message to be used
redo params: you can figure these out...
So in an example create function would look like this:
function createPerson (name, id) {
if (id == undefined) {
// create id...
}
// store name and id...
this.undoManager.register(
this, this.removePerson, [id], 'Remove person',
this, this.createPerson, [name, id], 'Create person'
);
}
To undo this action you write:
myUndoManager.undo()
To redo this undo-ed action:
myUndoManager.redo()
Test if UndoManager has any undo actions:
var hasUndo = myUndoManager.hasUndo()
Test if UndoManager has any redo actions:
var hasRedo = myUndoManager.hasRedo()
To update your interface I recommend to use backbone.js that makes it easy to tie update functions to model updates.
A backbone version of the undo manager is also provided. Create an instance of UndoManager with:
this.undoManager = new UndoManager()
var onUndoChanged = function(undoManager) {
$('.btnUndo').attr('disabled', !undoManager.hasUndo());
$('.btnRedo').attr('disabled', !undoManager.hasRedo());
};
this.undoManager.bind('change', onUndoChanged);
If you don't use backbone.js, pass a function to setCallback: