Rehacer – Deshacer en javascript

En este tutorial intentaré explicar la forma de implementar la funcionalidad de deshacer y rehacer, una característica muy usada en los programas de escritorio y que muchas aplicaciones web no tienen (y hace mucha falta ya que errar es de humanos).
Descargar código
La idea consiste en tener objetos que representen las acciones (mover, eliminar, resaltar…), los cuales deben guardar el estado anterior del objeto afectado.
ChangeColorCommand = function(obj, color) {

this.obj = obj;
this.new_color = color;

// guardar estado anterior
this.prev_color = obj.style.backgroundColor;
}

ChangeColorCommand.prototype.execute = function() {
this.obj.style.backgroundColor = this.new_color;
}

ChangeColorCommand.prototype.undo = function() {
this.obj.style.backgroundColor = this.prev_color;
}
Cada objeto cuenta con dos métodos
  • execute: ejecuta la acción (dah)
  • undo: deshace la acción valiéndose de los datos guardados previamente en el constructor.
Las acciones son administradas por un objeto que mantiene dos pilas de acciones: las que se pueden deshacer y las que se pueden rehacer:
  • cuando ejecuta una acción, la guarda en undo_stack y borra toda acción que se podía rehacer
  • cuando se deshace algo, saca el ultimo objeto de la pila undo_stack, ejecuta el método undo y guarda la acción en la pila redo_stack.
CommandManager = function(max_undo) {

// máxima cantidad de acciones guardadas
max_undo = max_undo || 30;

// pilas de acciones
this.undo_stack = [];
this.redo_stack = [];

// ejecutar comando cmd
this.executeCommand = function(cmd){
cmd.execute();

// si se sobrepasa cantidad de acciones
// eliminar primer elemento
if (this.undo_stack.length >= max_undo) {
this.undo_stack.shift();
}
this.undo_stack.push(cmd);
this.redo_stack = [];
}

// deshacer acción
this.undoCommand = function() {
var cmd = this.undo_stack.pop();

// si existe acción
if ( cmd ) {
cmd.undo();
this.redo_stack.push(cmd);
}
}
}
Para realizar una acción creamos una instancia del objeto apropiado y la pasamos al CommandManager (previamente creado).
var UndoRedo = new CommandManager(),

box = document.getElementById("box");

// cambiar color a rojo
UndoRedo.executeCommand(new ChangeColorCommand(box, "red"));

// cambiar color a verde
UndoRedo.executeCommand(new ChangeColorCommand(box, "green"));

// cambiar de verde a rojo
UndoRedo.undoCommand();

// cambiar de rojo a blanco
UndoRedo.undoCommand();
Ahora solo resta crear mas acciones (mover, negrita, itálica…) dependiendo de la aplicación que estemos desarrollando.

Eso es todo, espero les haya servido; si tienen alguna duda o critica no duden en dejarla en los comentarios.