mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-06-04 08:49:59 -04:00
Added support of operational transformation
This commit is contained in:
parent
4702b83adc
commit
556338a9c6
23 changed files with 3668 additions and 256 deletions
111
public/vendor/ot/undo-manager.js
vendored
Executable file
111
public/vendor/ot/undo-manager.js
vendored
Executable file
|
@ -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;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue