From 556338a9c6964d110c1351a402b425c71c2571fa Mon Sep 17 00:00:00 2001 From: Wu Cheng-Han Date: Sat, 11 Jul 2015 12:43:08 +0800 Subject: Added support of operational transformation --- public/vendor/ot/undo-manager.js | 111 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100755 public/vendor/ot/undo-manager.js (limited to 'public/vendor/ot/undo-manager.js') 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; +} -- cgit v1.2.3