I'm working on DOM-based editing tools in my spare time, and have been for
several years. I use DOM mutation events to build "transactions" - objects
which let me undo DOM mutations and redo them.
There's a strong suggestion that DOM mutation observers should replace DOM
mutation events altogether, for performance reasons. I believe a recent
discussion on execCommand brought this to light again. Also, Mozilla has a
bug on file (https://bugzilla.mozilla.org/show_bug.cgi?id=641821 ) to
implement mutation observers which only receive a node (
http://lists.w3.org/Archives/Public/public-webapps/2009AprJun/0745.html ).
For me, this presents a problem: In order to build undoable and redoable
DOM transactions, I need some way of extracting a "before" and an "after"
state for each individual mutation that happens. For all the ballyhoo about
mutation events being evil, this is one thing that they do very well, with
"prevValue" and "newValue" properties for attribute and data nodes, and with
a "relatedNode" property for node insertions and removals. The proposed
mutation observer model does not provide that information.
I hope there may be some room to negotiate (either with WHATWG or Mozilla)
on the interfaces. Without "before" and "after" states exposed through the
DOM somehow, I can't make my editor project work, period.
---
Now, I was until today unaware of HTML5's UndoManager in section 8.8 (
http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html#undo).
Maybe that gets me part of the way there, since it potentially tracks
changes to a document. In particular, I note there's no tests or sample JS
code for it written yet.
I'd like to take a closer look at the interfaces there. For comparison, I'd
like to mention Mozilla's nsITransaction interfaces, including
nsITransactionManager - one of the nicest ideas for an editor, and one
that's worked well for Mozilla's editors, including HTML inputs and
textareas, for over ten years. (They're good enough that they've largely
escaped attention - very few people think about the interfaces, because the
editor's undo mechanism Just Works!) See
http://mxr.mozilla.org/mozilla-central/find?text=&string=nsITransaction for
references.
A few thoughts on UndoManager:
* First, I really like the add() method. That sounds really useful,
especially if it's scriptable, because I see myself having custom
transactions which I may want to track through a document's UndoManager.
* The title is even more important - it would give people a way to see a
localized description of what the transaction is. Though I personally think
it should not be a separate value - I think it should be a required property
of the undo object. (This is one thing I actually hate about Mozilla's
transaction interfaces: they didn't make it easy to label transactions.)
* I'm not so sure about the remove() method. Why do we have it? If we
allow removing items by index, would we want to have a splice() method that
lets scripts insert items at a given index?
* Naturally, UndoManager sounds a lot like nsITransactionManager.
* I wonder about configuring UndoManager by script. For instance, I really
want to turn UndoManager on or off by script. (Most of the time, users
won't care about UndoManager, so I would think it would be disabled by
default, not actively recording transactions.) Or, I may want to track
attribute changes, but not node value changes.
* I think a diagram clarifying the flow of actions through UndoManager would
be helpful. Is it UndoManager.add(data) -> data.doSomething() (which
changes the document), or the other way around? How would this affect
custom actions being added through UndoManager.add()?
* For undo objects added by script, how would the UndoManager tell the
scripted undo object to, well, undo something? Or redo something? (In
particular, in such a way that the UndoManager doesn't record changes
happening while the undo object changes the document in an operation.)
* Is it feasible to have an UndoManagerObserver which is notified of undo
objects being passed through the UndoManager (either in enabled or disabled
state)? That would make it possible to manage undo objects in another
context besides the UndoManager (example: I want to export undo objects to a
Mozilla transaction manager, so that I drive it from there).
* I'm surprised that UndoManager doesn't have a maxlength. Do we really
intend to let it undo an infinite number of actions?
I hope to open a debate on the UndoManager section, and on undo/redo
operations in general, with this e-mail.
Alex Vincent
San Leandro, CA
author, Verbosio XML editor project (in development hell for six years and
counting!)
--
"The first step in confirming there is a bug in someone else's work is
confirming there are no bugs in your own."
-- Alexander J. Vincent, June 30, 2001