Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/third_party/closure/goog/editor/plugins/undoredomanager.js
2868 views
1
// Copyright 2008 The Closure Library Authors. All Rights Reserved.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS-IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
/**
16
* @fileoverview Code for managing series of undo-redo actions in the form of
17
* {@link goog.editor.plugins.UndoRedoState}s.
18
*
19
*/
20
21
22
goog.provide('goog.editor.plugins.UndoRedoManager');
23
goog.provide('goog.editor.plugins.UndoRedoManager.EventType');
24
25
goog.require('goog.editor.plugins.UndoRedoState');
26
goog.require('goog.events');
27
goog.require('goog.events.EventTarget');
28
29
30
31
/**
32
* Manages undo and redo operations through a series of {@code UndoRedoState}s
33
* maintained on undo and redo stacks.
34
*
35
* @constructor
36
* @extends {goog.events.EventTarget}
37
*/
38
goog.editor.plugins.UndoRedoManager = function() {
39
goog.events.EventTarget.call(this);
40
41
/**
42
* The maximum number of states on the undo stack at any time. Used to limit
43
* the memory footprint of the undo-redo stack.
44
* TODO(user) have a separate memory size based limit.
45
* @type {number}
46
* @private
47
*/
48
this.maxUndoDepth_ = 100;
49
50
/**
51
* The undo stack.
52
* @type {Array<goog.editor.plugins.UndoRedoState>}
53
* @private
54
*/
55
this.undoStack_ = [];
56
57
/**
58
* The redo stack.
59
* @type {Array<goog.editor.plugins.UndoRedoState>}
60
* @private
61
*/
62
this.redoStack_ = [];
63
64
/**
65
* A queue of pending undo or redo actions. Stored as objects with two
66
* properties: func and state. The func property stores the undo or redo
67
* function to be called, the state property stores the state that method
68
* came from.
69
* @type {Array<Object>}
70
* @private
71
*/
72
this.pendingActions_ = [];
73
};
74
goog.inherits(goog.editor.plugins.UndoRedoManager, goog.events.EventTarget);
75
76
77
/**
78
* Event types for the events dispatched by undo-redo manager.
79
* @enum {string}
80
*/
81
goog.editor.plugins.UndoRedoManager.EventType = {
82
/**
83
* Signifies that he undo or redo stack transitioned between 0 and 1 states,
84
* meaning that the ability to peform undo or redo operations has changed.
85
*/
86
STATE_CHANGE: 'state_change',
87
88
/**
89
* Signifies that a state was just added to the undo stack. Events of this
90
* type will have a {@code state} property whose value is the state that
91
* was just added.
92
*/
93
STATE_ADDED: 'state_added',
94
95
/**
96
* Signifies that the undo method of a state is about to be called.
97
* Events of this type will have a {@code state} property whose value is the
98
* state whose undo action is about to be performed. If the event is cancelled
99
* the action does not proceed, but the state will still transition between
100
* stacks.
101
*/
102
BEFORE_UNDO: 'before_undo',
103
104
/**
105
* Signifies that the redo method of a state is about to be called.
106
* Events of this type will have a {@code state} property whose value is the
107
* state whose redo action is about to be performed. If the event is cancelled
108
* the action does not proceed, but the state will still transition between
109
* stacks.
110
*/
111
BEFORE_REDO: 'before_redo'
112
};
113
114
115
/**
116
* The key for the listener for the completion of the asynchronous state whose
117
* undo or redo action is in progress. Null if no action is in progress.
118
* @type {goog.events.Key}
119
* @private
120
*/
121
goog.editor.plugins.UndoRedoManager.prototype.inProgressActionKey_ = null;
122
123
124
/**
125
* Set the max undo stack depth (not the real memory usage).
126
* @param {number} depth Depth of the stack.
127
*/
128
goog.editor.plugins.UndoRedoManager.prototype.setMaxUndoDepth = function(
129
depth) {
130
this.maxUndoDepth_ = depth;
131
};
132
133
134
/**
135
* Add state to the undo stack. This clears the redo stack.
136
*
137
* @param {goog.editor.plugins.UndoRedoState} state The state to add to the undo
138
* stack.
139
*/
140
goog.editor.plugins.UndoRedoManager.prototype.addState = function(state) {
141
// TODO: is the state.equals check necessary?
142
if (this.undoStack_.length == 0 ||
143
!state.equals(this.undoStack_[this.undoStack_.length - 1])) {
144
this.undoStack_.push(state);
145
if (this.undoStack_.length > this.maxUndoDepth_) {
146
this.undoStack_.shift();
147
}
148
// Clobber the redo stack.
149
var redoLength = this.redoStack_.length;
150
this.redoStack_.length = 0;
151
152
this.dispatchEvent({
153
type: goog.editor.plugins.UndoRedoManager.EventType.STATE_ADDED,
154
state: state
155
});
156
157
// If the redo state had states on it, then clobbering the redo stack above
158
// has caused a state change.
159
if (this.undoStack_.length == 1 || redoLength) {
160
this.dispatchStateChange_();
161
}
162
}
163
};
164
165
166
/**
167
* Dispatches a STATE_CHANGE event with this manager as the target.
168
* @private
169
*/
170
goog.editor.plugins.UndoRedoManager.prototype.dispatchStateChange_ =
171
function() {
172
this.dispatchEvent(
173
goog.editor.plugins.UndoRedoManager.EventType.STATE_CHANGE);
174
};
175
176
177
/**
178
* Performs the undo operation of the state at the top of the undo stack, moving
179
* that state to the top of the redo stack. If the undo stack is empty, does
180
* nothing.
181
*/
182
goog.editor.plugins.UndoRedoManager.prototype.undo = function() {
183
this.shiftState_(this.undoStack_, this.redoStack_);
184
};
185
186
187
/**
188
* Performs the redo operation of the state at the top of the redo stack, moving
189
* that state to the top of the undo stack. If redo undo stack is empty, does
190
* nothing.
191
*/
192
goog.editor.plugins.UndoRedoManager.prototype.redo = function() {
193
this.shiftState_(this.redoStack_, this.undoStack_);
194
};
195
196
197
/**
198
* @return {boolean} Wether the undo stack has items on it, i.e., if it is
199
* possible to perform an undo operation.
200
*/
201
goog.editor.plugins.UndoRedoManager.prototype.hasUndoState = function() {
202
return this.undoStack_.length > 0;
203
};
204
205
206
/**
207
* @return {boolean} Wether the redo stack has items on it, i.e., if it is
208
* possible to perform a redo operation.
209
*/
210
goog.editor.plugins.UndoRedoManager.prototype.hasRedoState = function() {
211
return this.redoStack_.length > 0;
212
};
213
214
215
/**
216
* Move a state from one stack to the other, performing the appropriate undo
217
* or redo action.
218
*
219
* @param {Array<goog.editor.plugins.UndoRedoState>} fromStack Stack to move
220
* the state from.
221
* @param {Array<goog.editor.plugins.UndoRedoState>} toStack Stack to move
222
* the state to.
223
* @private
224
*/
225
goog.editor.plugins.UndoRedoManager.prototype.shiftState_ = function(
226
fromStack, toStack) {
227
if (fromStack.length) {
228
var state = fromStack.pop();
229
230
// Push the current state into the redo stack.
231
toStack.push(state);
232
233
this.addAction_({
234
type: fromStack == this.undoStack_ ?
235
goog.editor.plugins.UndoRedoManager.EventType.BEFORE_UNDO :
236
goog.editor.plugins.UndoRedoManager.EventType.BEFORE_REDO,
237
func: fromStack == this.undoStack_ ? state.undo : state.redo,
238
state: state
239
});
240
241
// If either stack transitioned between 0 and 1 in size then the ability
242
// to do an undo or redo has changed and we must dispatch a state change.
243
if (fromStack.length == 0 || toStack.length == 1) {
244
this.dispatchStateChange_();
245
}
246
}
247
};
248
249
250
/**
251
* Adds an action to the queue of pending undo or redo actions. If no actions
252
* are pending, immediately performs the action.
253
*
254
* @param {Object} action An undo or redo action. Stored as an object with two
255
* properties: func and state. The func property stores the undo or redo
256
* function to be called, the state property stores the state that method
257
* came from.
258
* @private
259
*/
260
goog.editor.plugins.UndoRedoManager.prototype.addAction_ = function(action) {
261
this.pendingActions_.push(action);
262
if (this.pendingActions_.length == 1) {
263
this.doAction_();
264
}
265
};
266
267
268
/**
269
* Executes the action at the front of the pending actions queue. If an action
270
* is already in progress or the queue is empty, does nothing.
271
* @private
272
*/
273
goog.editor.plugins.UndoRedoManager.prototype.doAction_ = function() {
274
if (this.inProgressActionKey_ || this.pendingActions_.length == 0) {
275
return;
276
}
277
278
var action = this.pendingActions_.shift();
279
280
var e = {type: action.type, state: action.state};
281
282
if (this.dispatchEvent(e)) {
283
if (action.state.isAsynchronous()) {
284
this.inProgressActionKey_ = goog.events.listen(
285
action.state, goog.editor.plugins.UndoRedoState.ACTION_COMPLETED,
286
this.finishAction_, false, this);
287
action.func.call(action.state);
288
} else {
289
action.func.call(action.state);
290
this.doAction_();
291
}
292
}
293
};
294
295
296
/**
297
* Finishes processing the current in progress action, starting the next queued
298
* action if one exists.
299
* @private
300
*/
301
goog.editor.plugins.UndoRedoManager.prototype.finishAction_ = function() {
302
goog.events.unlistenByKey(/** @type {number} */ (this.inProgressActionKey_));
303
this.inProgressActionKey_ = null;
304
this.doAction_();
305
};
306
307
308
/**
309
* Clears the undo and redo stacks.
310
*/
311
goog.editor.plugins.UndoRedoManager.prototype.clearHistory = function() {
312
if (this.undoStack_.length > 0 || this.redoStack_.length > 0) {
313
this.undoStack_.length = 0;
314
this.redoStack_.length = 0;
315
this.dispatchStateChange_();
316
}
317
};
318
319
320
/**
321
* @return {goog.editor.plugins.UndoRedoState|undefined} The state at the top of
322
* the undo stack without removing it from the stack.
323
*/
324
goog.editor.plugins.UndoRedoManager.prototype.undoPeek = function() {
325
return this.undoStack_[this.undoStack_.length - 1];
326
};
327
328
329
/**
330
* @return {goog.editor.plugins.UndoRedoState|undefined} The state at the top of
331
* the redo stack without removing it from the stack.
332
*/
333
goog.editor.plugins.UndoRedoManager.prototype.redoPeek = function() {
334
return this.redoStack_[this.redoStack_.length - 1];
335
};
336
337