summaryrefslogtreecommitdiff
path: root/public/vendor/ot/undo-manager.js
diff options
context:
space:
mode:
Diffstat (limited to 'public/vendor/ot/undo-manager.js')
-rwxr-xr-xpublic/vendor/ot/undo-manager.js111
1 files changed, 111 insertions, 0 deletions
diff --git a/public/vendor/ot/undo-manager.js b/public/vendor/ot/undo-manager.js
new file mode 100755
index 00000000..19e89f18
--- /dev/null
+++ b/public/vendor/ot/undo-manager.js
@@ -0,0 +1,111 @@
+if (typeof ot === 'undefined') {
+ // Export for browsers
+ var ot = {};
+}
+
+ot.UndoManager = (function () {
+ 'use strict';
+
+ var NORMAL_STATE = 'normal';
+ var UNDOING_STATE = 'undoing';
+ var REDOING_STATE = 'redoing';
+
+ // Create a new UndoManager with an optional maximum history size.
+ function UndoManager (maxItems) {
+ this.maxItems = maxItems || 50;
+ this.state = NORMAL_STATE;
+ this.dontCompose = false;
+ this.undoStack = [];
+ this.redoStack = [];
+ }
+
+ // Add an operation to the undo or redo stack, depending on the current state
+ // of the UndoManager. The operation added must be the inverse of the last
+ // edit. When `compose` is true, compose the operation with the last operation
+ // unless the last operation was alread pushed on the redo stack or was hidden
+ // by a newer operation on the undo stack.
+ UndoManager.prototype.add = function (operation, compose) {
+ if (this.state === UNDOING_STATE) {
+ this.redoStack.push(operation);
+ this.dontCompose = true;
+ } else if (this.state === REDOING_STATE) {
+ this.undoStack.push(operation);
+ this.dontCompose = true;
+ } else {
+ var undoStack = this.undoStack;
+ if (!this.dontCompose && compose && undoStack.length > 0) {
+ undoStack.push(operation.compose(undoStack.pop()));
+ } else {
+ undoStack.push(operation);
+ if (undoStack.length > this.maxItems) { undoStack.shift(); }
+ }
+ this.dontCompose = false;
+ this.redoStack = [];
+ }
+ };
+
+ function transformStack (stack, operation) {
+ var newStack = [];
+ var Operation = operation.constructor;
+ for (var i = stack.length - 1; i >= 0; i--) {
+ var pair = Operation.transform(stack[i], operation);
+ if (typeof pair[0].isNoop !== 'function' || !pair[0].isNoop()) {
+ newStack.push(pair[0]);
+ }
+ operation = pair[1];
+ }
+ return newStack.reverse();
+ }
+
+ // Transform the undo and redo stacks against a operation by another client.
+ UndoManager.prototype.transform = function (operation) {
+ this.undoStack = transformStack(this.undoStack, operation);
+ this.redoStack = transformStack(this.redoStack, operation);
+ };
+
+ // Perform an undo by calling a function with the latest operation on the undo
+ // stack. The function is expected to call the `add` method with the inverse
+ // of the operation, which pushes the inverse on the redo stack.
+ UndoManager.prototype.performUndo = function (fn) {
+ this.state = UNDOING_STATE;
+ if (this.undoStack.length === 0) { throw new Error("undo not possible"); }
+ fn(this.undoStack.pop());
+ this.state = NORMAL_STATE;
+ };
+
+ // The inverse of `performUndo`.
+ UndoManager.prototype.performRedo = function (fn) {
+ this.state = REDOING_STATE;
+ if (this.redoStack.length === 0) { throw new Error("redo not possible"); }
+ fn(this.redoStack.pop());
+ this.state = NORMAL_STATE;
+ };
+
+ // Is the undo stack not empty?
+ UndoManager.prototype.canUndo = function () {
+ return this.undoStack.length !== 0;
+ };
+
+ // Is the redo stack not empty?
+ UndoManager.prototype.canRedo = function () {
+ return this.redoStack.length !== 0;
+ };
+
+ // Whether the UndoManager is currently performing an undo.
+ UndoManager.prototype.isUndoing = function () {
+ return this.state === UNDOING_STATE;
+ };
+
+ // Whether the UndoManager is currently performing a redo.
+ UndoManager.prototype.isRedoing = function () {
+ return this.state === REDOING_STATE;
+ };
+
+ return UndoManager;
+
+}());
+
+// Export for CommonJS
+if (typeof module === 'object') {
+ module.exports = ot.UndoManager;
+}