112 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			112 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
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;
 | 
						|
}
 |