-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathds-undo.js
More file actions
112 lines (102 loc) · 3.1 KB
/
ds-undo.js
File metadata and controls
112 lines (102 loc) · 3.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
(function(root, Backbone, _) {
var UndoManager = Backbone.Model.extend({
initialize: function () {
this._undoStack = [];
this._redoStack = [];
this.registeredObjects = [];
this.eventToUndoMap = {
"add": "remove",
"change": "set",
"remove": "add"
};
this.undoRedoToEventMap = {
"remove": "add",
"set": "change",
"add": "remove"
};
this.undoRedoMap = {
"add": "remove",
"set": "set",
"remove": "add",
}
},
_addUndo: function (event, obj, now, before) {
var undoStackLength = this._undoStack.length, lastUndo = this._undoStack[--undoStackLength], redoEvent, undoEvent, undoFn, redoFn;
if (!lastUndo || lastUndo.obj !== obj || !_.isEqual(lastUndo.data, before)) {
// Ok, this is not a bubbling Event, but an actual new one
this._redoStack = []; // This is also not an undone redo, so empty the redo stack
undoFn = this.eventToUndoMap[event];
redoFn = this.undoRedoMap[undoFn];
redoEvent = this.undoRedoToEventMap[redoFn];
undoEvent = this.undoRedoToEventMap[undoFn];
this._undoStack.push({
obj: obj,
undo: obj[undoFn],
redo: obj[redoFn],
data: before,
ignoreNextEvent: {
undo: undoEvent,
redo: redoEvent
}
});
}
},
redoIsPossible: function () {
return !!this._redoStack.length;
},
undoIsPossible: function () {
return !!this._undoStack.length;
},
undo: function () {
if (this.undoIsPossible()) {
var undo = this._undoStack.pop();
this._redoStack.unshift(undo);
this.ignoreNextEvent = undo.ignoreNextEvent.undo;
undo.undo.call(undo.obj, undo.data);
}
},
redo: function () {
if (this.redoIsPossible()) {
var redo = this._redoStack.shift();
this._undoStack.push(redo);
this.ignoreNextEvent = redo.ignoreNextEvent.redo;
redo.redo.call(redo.obj, redo.data);
}
},
_addListeners: function (obj) {
obj.on("all", function (event, m, c) {
if (event === this.ignoreNextEvent) {
return this.ignoreNextEvent = undefined;
} else {
var args = [event];
switch (event) {
case "change": args.push(m, _.clone(m.attributes), m.previousAttributes()); break;
case "add": args.push(c,m,m); break;
case "remove": args.push(c,m,m); break;
default: return;
}
this._addUndo.apply(this, args);
}
}, this);
},
_stackToString: function (stack) {
var arr = [], obj;
_.each(stack, function(ur) {
obj = ur.data.get ? ur.data : ur.obj;
arr.push(ur.ignoreNextEvent.undo + ":" + (obj.get("itemId") || obj.get("id")).substr(10));
});
return "[" + arr.join(", ") + "]";
},
toString: function() {
return _.map([this._undoStack, this._redoStack], this._stackToString, this).join(" ");
},
register: function(obj) {
var isModel = obj instanceof Backbone.Model, isCollection = obj instanceof Backbone.Collection;
if (!isModel && !isCollection || _.indexOf(this.registeredObjects, obj) > -1) return;
// is valid and not yet registered
this.registeredObjects.push(obj); // register
this._addListeners(obj);
}
});
root.UndoManager = new UndoManager();
})(this, Backbone, _);