aboutsummaryrefslogtreecommitdiff
path: root/node_modules/montage/core/undo-manager.js
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/montage/core/undo-manager.js')
-rw-r--r--node_modules/montage/core/undo-manager.js240
1 files changed, 240 insertions, 0 deletions
diff --git a/node_modules/montage/core/undo-manager.js b/node_modules/montage/core/undo-manager.js
new file mode 100644
index 00000000..cd97ee59
--- /dev/null
+++ b/node_modules/montage/core/undo-manager.js
@@ -0,0 +1,240 @@
1/* <copyright>
2 This file contains proprietary software owned by Motorola Mobility, Inc.<br/>
3 No rights, expressed or implied, whatsoever to this software are provided by Motorola Mobility, Inc. hereunder.<br/>
4 (c) Copyright 2011 Motorola Mobility, Inc. All Rights Reserved.
5 </copyright> */
6
7// Consider proposal at https://rniwa.com/editing/undomanager.html
8
9var Montage = require("montage").Montage;
10
11exports.UndoManager = Montage.create(Montage, {
12
13 enabled: {
14 value: true
15 },
16
17 _maxUndoCount: {
18 enumerable: false,
19 value: null
20 },
21
22 maxUndoCount: {
23 get: function() {
24 return this._maxUndoCount;
25 },
26 set: function(value) {
27 if (value === this._maxUndoCount) {
28 return;
29 }
30
31 this._maxUndoCount = value;
32
33 if (this._maxUndoCount != null) {
34 this._trimStacks();
35 }
36 }
37
38 },
39
40 _trimStacks: {
41 enumerable: false,
42 value: function() {
43
44 var undoRemoveCount = this._maxUndoCount - this.undoStack.length,
45 redoRemoveCount = this._maxUndoCount - this.redoStack.length;
46
47 if (undoRemoveCount > 0) {
48 this.undoStack.splice(0, undoRemoveCount);
49 }
50
51 if (redoRemoveCount > 0) {
52 this.redoStack.splice(0, redoRemoveCount);
53 }
54 }
55 },
56
57 _undoStack: {
58 enumerable: false,
59 value: null
60 },
61
62 undoStack: {
63 get: function() {
64 if (!this._undoStack) {
65 this._undoStack = [];
66 }
67 return this._undoStack
68 }
69 },
70
71 redoStack: {
72 value: [],
73 distinct: true
74 },
75
76 add: {
77 value: function(label, undoFunction, context) {
78
79 if (0 === this._maxUndoCount) {
80 return;
81 }
82
83 var undoEntry = {
84 label: label,
85 undoFunction: undoFunction,
86 context: context,
87 args: Array.prototype.slice.call(arguments, 3)
88 };
89
90 // seeing as you can only ever add one entry at a time to either stack, we should never need to make room
91 // for more than a single entry at this point; there's no need for an expensive trim
92 if (this.isUndoing) {
93
94 // preserve the label of the current action being undone to be the name of the redo
95 undoEntry.label = this.undoEntry.label;
96
97 if (this.redoStack.length === this._maxUndoCount) {
98 this.redoStack.shift();
99 }
100
101 this.redoStack.push(undoEntry);
102 } else {
103
104 if (this.undoStack.length === this._maxUndoCount) {
105 this.undoStack.shift();
106 }
107
108 this.undoStack.push(undoEntry);
109
110 if (!this.isRedoing && this.redoStack.length > 0) {
111 this.clearRedo();
112 }
113 }
114 }
115 },
116
117 clearUndo: {
118 value: function() {
119 this.undoStack.splice(0, this.undoStack.length);
120 }
121 },
122
123 clearRedo: {
124 value: function() {
125 this.redoStack.splice(0, this.redoStack.length);
126 }
127 },
128
129 isUndoing: {
130 dependencies: ["undoEntry"],
131 get: function() {
132 return !!this.undoEntry;
133 }
134 },
135
136 isRedoing: {
137 dependencies: ["redoEntry"],
138 get: function() {
139 return !!this.redoEntry;
140 }
141 },
142
143 undoEntry: {
144 enumerable: false,
145 value: null
146 },
147
148 redoEntry: {
149 enumerable: false,
150 value: null
151 },
152
153 undo: {
154 value: function() {
155
156 if (this.isUndoing || this.isRedoing) {
157 throw "UndoManager cannot initiate an undo or redo while undoing.";
158 }
159
160 if (this.undoStack.length === 0) {
161 return;
162 }
163
164 var entry = this.undoEntry = this.undoStack.pop();
165 entry.undoFunction.apply(entry.context, entry.args);
166 this.undoEntry = null;
167 }
168 },
169
170 redo: {
171 value: function() {
172 if (this.isUndoing || this.isRedoing) {
173 throw "UndoManager cannot initiate an undo or redo while redoing.";
174 }
175
176 if (this.redoStack.length === 0) {
177 return;
178 }
179
180 var entry = this.redoEntry = this.redoStack.pop();
181 entry.undoFunction.apply(entry.context, entry.args);
182 this.redoEntry = null;
183 }
184 },
185
186 canUndo: {
187 dependencies: ["undoStack.count()"],
188 get: function() {
189 return !!this.undoStack.length;
190 }
191 },
192
193 canRedo: {
194 dependencies: ["redoStack.count()"],
195 get: function() {
196 return !!this.redoStack.length;
197 }
198 },
199
200 undoLabel: {
201 dependencies: ["undoStack.count()"],
202 get: function() {
203 var undoCount = this.undoStack.length,
204 label;
205
206 if (undoCount) {
207 label = this.undoStack[undoCount - 1].label;
208 }
209
210 return label ? "Undo " + label : "Undo";
211 },
212 set: function(value) {
213 var undoCount = this.redoStack.length;
214 if (undoCount) {
215 this.undoStack[undoCount - 1].label = value;
216 }
217 }
218 },
219
220 redoLabel: {
221 dependencies: ["redoStack.count()"],
222 get: function() {
223 var redoCount = this.redoStack.length,
224 label;
225
226 if (redoCount) {
227 label = this.redoStack[redoCount - 1].label
228 }
229
230 return label ? "Redo " + label : "Redo";
231 },
232 set: function(value) {
233 var redoCount = this.redoStack.length;
234 if (redoCount) {
235 this.redoStack[redoCount - 1].label = value;
236 }
237