edit/undo
May 26, 2005, 10:11pm EDT
Aaron asks for favorite javascript idioms. Well, this isn’t an idiom, but I do like how closures make it very easy to implement undo/redo functionality [1] in a web application.
For example, in an drawing application, I may want to enable the user to undo/redo the creation of a line. To do this, I would create an object that holds the undo and redo state:
var objId = DrawLine(startPoint, endPoint); var state = { ‘undo’: function() { DeleteObject(objId); }, ‘redo’: function() { DrawLineWithId(startPoint, endPoint, objId); } };
Then I would have two stacks that hold state objects, one for undo and one for redo. New state objects get pushed onto the undo stack. When the user presses the undo button we do three things:
- pop a state from the undo stack
- call the undo method of the state object
- push the state onto the redo stack
Through the magic of closures, the internal state is encapsulated (that is, the startPoint, endPoint, and objId are not accessible to those who can access the state object).
[1] Or if you like talking in the language of design patterns, you may know this as the memento pattern.
BR at May 31, 2005, 12:59pm EDT
So, state object is your memento here, and the web app is the originator, right?
Having two stacks and moving around the obj is a nifty idea. Is it possible to maintain a single stack with two stack pointers: current (undo will decrement, redo will increment, can never redo a nonexistant op) and overall (absolute ptr, always increments). This way, you can reduce the space reqmt (not much though)…or am I missing something here?
…just pondering what you are pondering.
~BR
tony at May 31, 2005, 11:08pm EDT
Sure, you could use a single stack. The memory and speed are the same (only different by a constant factor) so it doesn’t matter how you implement it.
I prefer two stacks because that’s the mental model I have (I think of back and forward as two different objects). It doesn’t really matter.