2013-05-27 23:57:31 +08:00
|
|
|
Backbone.Undo.js
|
|
|
|
================
|
|
|
|
|
2013-05-28 02:23:25 +08:00
|
|
|
An extremely simple Undo-Manager for Backbone.js
|
|
|
|
|
|
|
|
### Advantages of Backbone.Undo.js
|
|
|
|
|
2013-05-28 04:58:14 +08:00
|
|
|
* Easy to include and exclude
|
2013-05-28 02:23:25 +08:00
|
|
|
|
2013-05-28 04:57:27 +08:00
|
|
|
In comparison to other Backbone-based undo-managers like *memento*, you don't have to modify your models
|
|
|
|
or collections to use Backbone.Undo.js. You can have your whole application already set up with all the
|
|
|
|
models and collections and then add the undo-manager. That makes it easy to not only include
|
2013-05-28 04:58:14 +08:00
|
|
|
Backbone.Undo.js, but also to remove it again if you don't longer want to use it at some point.
|
2013-05-28 02:23:25 +08:00
|
|
|
|
|
|
|
* Uses Backbone-Events
|
|
|
|
|
2013-05-28 04:57:27 +08:00
|
|
|
To detect an action, Backbone.Undo.js listens to the events Backbone triggeres automatically. You don't have
|
|
|
|
to do anything. You don't have to `store()` or `restore()` certain states. Nothing.
|
2013-05-28 02:23:25 +08:00
|
|
|
|
|
|
|
* Memory-friendly
|
|
|
|
|
2013-05-28 02:29:23 +08:00
|
|
|
Backbone.Undo.js only stores changes, instead of snapshots (clones of models/collections).
|
2013-05-28 02:23:25 +08:00
|
|
|
|
2013-05-28 04:57:27 +08:00
|
|
|
* Optimized for Usability
|
2013-05-28 02:23:25 +08:00
|
|
|
|
2013-05-28 04:57:27 +08:00
|
|
|
In a sophisticated webapp one click of the user might trigger several Backbone-Events which are stored as
|
|
|
|
several Undo-Actions within the Undo-Stack. If the user then calls `undo()` it shouldn't just undo the latest
|
|
|
|
action, it should undo all the actions which were triggered by the user's click. Backbone.Undo.js has a way to
|
|
|
|
figure out which actions belong together and then undoes/redoes all of them.
|
|
|
|
|
2013-05-28 02:23:25 +08:00
|
|
|
## Getting started
|
|
|
|
|
2013-05-28 04:57:27 +08:00
|
|
|
Like with all the other JavaScript-Libraries you only need to include Backbone.Undo.js into your webpage or webapp
|
|
|
|
to make it available.
|
|
|
|
As Backbone.Undo.js depends on Backbone you need Backbone, which again depends on underscore.js (or lowdash.js) and
|
|
|
|
jQuery (or zepto). Make sure to include all these files before Backbone.Undo.js as it relies on these libraries:
|
2013-05-28 02:23:25 +08:00
|
|
|
|
|
|
|
<script src="jquery.js"></script>
|
|
|
|
<script src="underscore.js"></script>
|
|
|
|
<script src="backbone.js"></script>
|
|
|
|
<!-- Backbone.Undo.js is included *after* those other libs -->
|
|
|
|
<script src="Backbone.Undo.js"></script>
|
|
|
|
|
|
|
|
|
|
|
|
### Backbone Version
|
|
|
|
|
|
|
|
Backbone.Undo.js was developed for Backbone 1.0.0 or higher.
|
|
|
|
|
|
|
|
### Underscore Version
|
|
|
|
|
|
|
|
Backbone.Undo.js was developed for Underscore 1.4.4 or higher.
|
|
|
|
|
|
|
|
# Setting up your UndoManager
|
|
|
|
|
|
|
|
In order to set up you UndoManager you have to do the following steps:
|
|
|
|
|
|
|
|
// 1. Instantiate your UndoManager
|
|
|
|
var myUndoManager = new Backbone.UndoManager({
|
|
|
|
maximumStackLength: 100 // maximumStackLength determines how many actions
|
|
|
|
// are stored to be undone. Default is Infinity aka no limit at all. This
|
|
|
|
// attribute is optional. You don't need to pass anything to the constructor.
|
|
|
|
});
|
|
|
|
|
|
|
|
// 2. Register the models and collections you want to observe
|
|
|
|
var model = new Backbone.Model,
|
|
|
|
collection = new Backbone.Collection;
|
|
|
|
myUndoManager.register(model, collection); // You can pass several objects as arguments
|
|
|
|
|
|
|
|
// You can setting up your objects here. Changes won't be tracked yet.
|
|
|
|
model.set("foo", "bar");
|
|
|
|
collection.add([{"something": "blue"}]);
|
|
|
|
// These changes can't be undone.
|
|
|
|
|
|
|
|
// 3. Start tracking the changes
|
|
|
|
myUndoManager.startTracking(); // Everything that happens from now on, can be undone
|
|
|
|
|
2013-05-28 04:57:27 +08:00
|
|
|
# Undo or Redo Actions
|
|
|
|
|
|
|
|
To undo the last set of actions, just call `undo()`
|
|
|
|
|
|
|
|
myUndoManager.undo();
|
|
|
|
|
|
|
|
To redo undone actions, call `redo()`
|
2013-05-28 02:23:25 +08:00
|
|
|
|
2013-05-28 04:57:27 +08:00
|
|
|
myUndoManager.redo();
|
2013-05-28 02:23:25 +08:00
|
|
|
|
2013-05-28 04:57:27 +08:00
|
|
|
### Problems that may occur
|
|
|
|
|
|
|
|
Backbone.Undo.js is not made to be called within your code. It has an internal mechanism which figures out
|
|
|
|
which Undo-Actions were generated in the same call cycle.
|
|
|
|
This mechanism is great for usability (see above, *Advantages of Backbone.Undo.js*). However this mechanism
|
|
|
|
makes it impossible to call `undo()` or `redo()` within a codeblock. Imagine this:
|
|
|
|
|
|
|
|
model.get("foo"); // "bar"
|
|
|
|
|
|
|
|
// Several changes:
|
|
|
|
model.set("foo", "baz");
|
|
|
|
model.set("foo", "qux");
|
|
|
|
model.set("foo", 42);
|
|
|
|
model.set("foo", {})
|
|
|
|
|
|
|
|
// One call to `undo`:
|
|
|
|
myUndoManager.undo();
|
|
|
|
model.get("foo"); // Is "bar" instead of 42
|
|
|
|
|
|
|
|
Calling `undo()` resets `"foo"` to `"bar"` instead of `42`, because it had figured out that the four `set`s happened in
|
|
|
|
one call cycle.
|
|
|
|
If you want to call `undo()` within your code and each time only want to undo the latest change you have to call the
|
|
|
|
changes to the model asynchronously.
|
|
|
|
|
|
|
|
model.get("foo");
|
|
|
|
|
|
|
|
// Several changes:
|
|
|
|
_.defer(function () {
|
|
|
|
model.set("foo", "baz");
|
|
|
|
|
|
|
|
_.defer(function () {
|
|
|
|
model.set("foo", "qux");
|
|
|
|
|
|
|
|
_.defer(function () {
|
|
|
|
model.set("foo", 42);
|
|
|
|
|
|
|
|
_.defer(function () {
|
|
|
|
model.set("foo", {});
|
|
|
|
|
|
|
|
myUndoManager.undo();
|
|
|
|
model.get("foo") // 42
|
|
|
|
|
|
|
|
myUndoManager.undo();
|
|
|
|
model.get("foo") // "qux"
|
|
|
|
|
|
|
|
myUndoManager.undo();
|
|
|
|
model.get("foo") // "baz"
|
|
|
|
|
|
|
|
myUndoManager.undo();
|
|
|
|
model.get("foo") // "bar"
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
2013-05-28 02:23:25 +08:00
|
|
|
|
2013-05-28 04:57:27 +08:00
|
|
|
Obviously noone would ever do that. In fact you also shouldn't do that: Your webapp shouldn't have any reference to the
|
|
|
|
undo-manager within your code. Try to develop it independently from the undo-manager.
|