Javascript History Operations Manager Undo/Redo
by ne on 2021-09-29 under Javascript
This article will demonstrate a way of creating a javascript utility which can help in maintaining history operations.
For instance,f you want to undo, redo a series of operations, then you can use the following utility. A demonstration of how every operation takes on, is just after the code below.
var historyManager = function (undoCallBack, redoCallback, maxStackLimit) {
var undoCommands = [], redoCommands = [], limitForUndoRedo = 20;
//set the max items for undo redo list
if (maxStackLimit) {
limitForUndoRedo = maxStackLimit;
}
return {
//this function trigger undo
executeUndo: function (obj) {
var index, decompressedObj, compressedObj;
this.addRedo(obj);
//trigger undo
if (undoCallBack) {
undoCallBack(obj);
}
//trigger event for enable/disbale undo button
},
//this function trigger redo
executeRedo: function (obj) {
var index, decompressedObj, compressedObj;
this.addUndo(obj, true);
//trigger redo
if (redoCallback) {
redoCallback(obj);
}
//trigger event for enable/disbale redo button
},
//this function undo the last change and if index of undo list is passed then it will undo change till that parameter
undo: function (undoCommandIndex) {
var commandsForRedo, startIndex;
if (!this.canUndo()) {
return false;
}
if (!undoCommandIndex) {
this.executeUndo(undoCommands.pop());
}
else {
startIndex = undoCommands.length - (undoCommandIndex + 1);
commandsForRedo = undoCommands.splice(startIndex, (undoCommandIndex + 1));
commandsForRedo.reverse();
this.executeUndo(commandsForRedo);
}
},
//this function redo the last change
redo: function (redoCommandIndex) {
var startIndex, commandsForUndo;
if (!this.canRedo()) {
return false;
}
if (!redoCommandIndex) {
this.executeRedo(redoCommands.pop());
} else {
startIndex = redoCommands.length - (redoCommandIndex + 1);
commandsForUndo = redoCommands.splice(startIndex, (redoCommandIndex + 1));
commandsForUndo.reverse();
this.executeRedo(commandsForUndo);
}
},
//this function adds a command to undo commands list
addUndo: function (obj, calledFromRedo) {
var compressedObj, index;
if (limitForUndoRedo === undoCommands.length) {
undoCommands.splice(0, 1);
}
//push the change into the undo command
if (obj && obj.length) {
undoCommands = undoCommands.concat(obj);
} else {
undoCommands.push(obj);
}
if (!calledFromRedo) {
this.clearRedo();
}
},
//this function adds a command to redo commands list
addRedo: function (obj) {
if (limitForUndoRedo === redoCommands.length) {
redoCommands.splice(0, 1);
}
if (obj && obj.length) {
redoCommands = redoCommands.concat(obj);
} else {
redoCommands.push(obj);
}
},
//it returns the list of the undo commands
getUndoCommandsList: function () {
return undoCommands;
},
//it returns the list of the redo commands
getRedoCommandsList: function () {
return redoCommands;
},
//it clears the unedo commands lists
clearUndo: function () {
undoCommands = [];
//trigger event for enable/disbale undo button
},
//it clears the redo commands lists
clearRedo: function () {
redoCommands = [];
//trigger event for enable/disbale redo button
},
//this functions returns true if we can perform the undo action otherwise it will return false
canUndo: function () {
return undoCommands.length > 0;
},
//this functions returns true if we can perform the redo action otherwise it will return false
canRedo: function () {
return redoCommands.length > 0;
},
//resets the undo-redo lists
reset: function () {
undoCommands = [];
redoCommands = [];
//trigger event for enable/disbale redo button
//trigger event for enable/disbale undo button
}
};
};
// creating an instance, with callback operations.
// here in this example, we are simply consoling the undo and redo commands.
// and max history we are using here is 5
const historyMan=new historyManager(function(){console.log("called undo");},function(){console.log("called redo");},5);
// adding some undo operations
historyMan.addUndo("dev");
historyMan.addUndo("melly");
// displaying the possible undo operations pending
manager.getUndoCommandsList();
// returns => ?["dev", "melly"]
// trying an undo
manager.undo();
// console output:
// called undo
// displaying the possible undo operations pending now
manager.getUndoCommandsList()
// returns => ["dev"]
// displaying the possible redo operations
manager.getRedoCommandsList()
// returns => ["melly"]
// trying a redo operations
manager.redo()
// consoles => called redo
// displaying undo operations now
manager.getUndoCommandsList()
// returns =>?["dev", "melly"]
// you can also call clear operations, or a reset operation to reset the pending queue of operations.
manager.reset()
Happy Coding !