188 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			188 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
if (typeof ot === 'undefined') {
 | 
						|
  // Export for browsers
 | 
						|
  var ot = {};
 | 
						|
}
 | 
						|
 | 
						|
ot.SimpleTextOperation = (function (global) {
 | 
						|
 | 
						|
  var TextOperation = global.ot ? global.ot.TextOperation : require('./text-operation');
 | 
						|
 | 
						|
  function SimpleTextOperation () {}
 | 
						|
 | 
						|
 | 
						|
  // Insert the string `str` at the zero-based `position` in the document.
 | 
						|
  function Insert (str, position) {
 | 
						|
    if (!this || this.constructor !== SimpleTextOperation) {
 | 
						|
      // => function was called without 'new'
 | 
						|
      return new Insert(str, position);
 | 
						|
    }
 | 
						|
    this.str = str;
 | 
						|
    this.position = position;
 | 
						|
  }
 | 
						|
 | 
						|
  Insert.prototype = new SimpleTextOperation();
 | 
						|
  SimpleTextOperation.Insert = Insert;
 | 
						|
 | 
						|
  Insert.prototype.toString = function () {
 | 
						|
    return 'Insert(' + JSON.stringify(this.str) + ', ' + this.position + ')';
 | 
						|
  };
 | 
						|
 | 
						|
  Insert.prototype.equals = function (other) {
 | 
						|
    return other instanceof Insert &&
 | 
						|
      this.str === other.str &&
 | 
						|
      this.position === other.position;
 | 
						|
  };
 | 
						|
 | 
						|
  Insert.prototype.apply = function (doc) {
 | 
						|
    return doc.slice(0, this.position) + this.str + doc.slice(this.position);
 | 
						|
  };
 | 
						|
 | 
						|
 | 
						|
  // Delete `count` many characters at the zero-based `position` in the document.
 | 
						|
  function Delete (count, position) {
 | 
						|
    if (!this || this.constructor !== SimpleTextOperation) {
 | 
						|
      return new Delete(count, position);
 | 
						|
    }
 | 
						|
    this.count = count;
 | 
						|
    this.position = position;
 | 
						|
  }
 | 
						|
 | 
						|
  Delete.prototype = new SimpleTextOperation();
 | 
						|
  SimpleTextOperation.Delete = Delete;
 | 
						|
 | 
						|
  Delete.prototype.toString = function () {
 | 
						|
    return 'Delete(' + this.count + ', ' + this.position + ')';
 | 
						|
  };
 | 
						|
 | 
						|
  Delete.prototype.equals = function (other) {
 | 
						|
    return other instanceof Delete &&
 | 
						|
      this.count === other.count &&
 | 
						|
      this.position === other.position;
 | 
						|
  };
 | 
						|
 | 
						|
  Delete.prototype.apply = function (doc) {
 | 
						|
    return doc.slice(0, this.position) + doc.slice(this.position + this.count);
 | 
						|
  };
 | 
						|
 | 
						|
 | 
						|
  // An operation that does nothing. This is needed for the result of the
 | 
						|
  // transformation of two deletions of the same character.
 | 
						|
  function Noop () {
 | 
						|
    if (!this || this.constructor !== SimpleTextOperation) { return new Noop(); }
 | 
						|
  }
 | 
						|
 | 
						|
  Noop.prototype = new SimpleTextOperation();
 | 
						|
  SimpleTextOperation.Noop = Noop;
 | 
						|
 | 
						|
  Noop.prototype.toString = function () {
 | 
						|
    return 'Noop()';
 | 
						|
  };
 | 
						|
 | 
						|
  Noop.prototype.equals = function (other) { return other instanceof Noop; };
 | 
						|
 | 
						|
  Noop.prototype.apply = function (doc) { return doc; };
 | 
						|
 | 
						|
  var noop = new Noop();
 | 
						|
 | 
						|
 | 
						|
  SimpleTextOperation.transform = function (a, b) {
 | 
						|
    if (a instanceof Noop || b instanceof Noop) { return [a, b]; }
 | 
						|
 | 
						|
    if (a instanceof Insert && b instanceof Insert) {
 | 
						|
      if (a.position < b.position || (a.position === b.position && a.str < b.str)) {
 | 
						|
        return [a, new Insert(b.str, b.position + a.str.length)];
 | 
						|
      }
 | 
						|
      if (a.position > b.position || (a.position === b.position && a.str > b.str)) {
 | 
						|
        return [new Insert(a.str, a.position + b.str.length), b];
 | 
						|
      }
 | 
						|
      return [noop, noop];
 | 
						|
    }
 | 
						|
 | 
						|
    if (a instanceof Insert && b instanceof Delete) {
 | 
						|
      if (a.position <= b.position) {
 | 
						|
        return [a, new Delete(b.count, b.position + a.str.length)];
 | 
						|
      }
 | 
						|
      if (a.position >= b.position + b.count) {
 | 
						|
        return [new Insert(a.str, a.position - b.count), b];
 | 
						|
      }
 | 
						|
      // Here, we have to delete the inserted string of operation a.
 | 
						|
      // That doesn't preserve the intention of operation a, but it's the only
 | 
						|
      // thing we can do to get a valid transform function.
 | 
						|
      return [noop, new Delete(b.count + a.str.length, b.position)];
 | 
						|
    }
 | 
						|
 | 
						|
    if (a instanceof Delete && b instanceof Insert) {
 | 
						|
      if (a.position >= b.position) {
 | 
						|
        return [new Delete(a.count, a.position + b.str.length), b];
 | 
						|
      }
 | 
						|
      if (a.position + a.count <= b.position) {
 | 
						|
        return [a, new Insert(b.str, b.position - a.count)];
 | 
						|
      }
 | 
						|
      // Same problem as above. We have to delete the string that was inserted
 | 
						|
      // in operation b.
 | 
						|
      return [new Delete(a.count + b.str.length, a.position), noop];
 | 
						|
    }
 | 
						|
 | 
						|
    if (a instanceof Delete && b instanceof Delete) {
 | 
						|
      if (a.position === b.position) {
 | 
						|
        if (a.count === b.count) {
 | 
						|
          return [noop, noop];
 | 
						|
        } else if (a.count < b.count) {
 | 
						|
          return [noop, new Delete(b.count - a.count, b.position)];
 | 
						|
        }
 | 
						|
        return [new Delete(a.count - b.count, a.position), noop];
 | 
						|
      }
 | 
						|
      if (a.position < b.position) {
 | 
						|
        if (a.position + a.count <= b.position) {
 | 
						|
          return [a, new Delete(b.count, b.position - a.count)];
 | 
						|
        }
 | 
						|
        if (a.position + a.count >= b.position + b.count) {
 | 
						|
          return [new Delete(a.count - b.count, a.position), noop];
 | 
						|
        }
 | 
						|
        return [
 | 
						|
          new Delete(b.position - a.position, a.position),
 | 
						|
          new Delete(b.position + b.count - (a.position + a.count), a.position)
 | 
						|
        ];
 | 
						|
      }
 | 
						|
      if (a.position > b.position) {
 | 
						|
        if (a.position >= b.position + b.count) {
 | 
						|
          return [new Delete(a.count, a.position - b.count), b];
 | 
						|
        }
 | 
						|
        if (a.position + a.count <= b.position + b.count) {
 | 
						|
          return [noop, new Delete(b.count - a.count, b.position)];
 | 
						|
        }
 | 
						|
        return [
 | 
						|
          new Delete(a.position + a.count - (b.position + b.count), b.position),
 | 
						|
          new Delete(a.position - b.position, b.position)
 | 
						|
        ];
 | 
						|
      }
 | 
						|
    }
 | 
						|
  };
 | 
						|
 | 
						|
  // Convert a normal, composable `TextOperation` into an array of
 | 
						|
  // `SimpleTextOperation`s.
 | 
						|
  SimpleTextOperation.fromTextOperation = function (operation) {
 | 
						|
    var simpleOperations = [];
 | 
						|
    var index = 0;
 | 
						|
    for (var i = 0; i < operation.ops.length; i++) {
 | 
						|
      var op = operation.ops[i];
 | 
						|
      if (TextOperation.isRetain(op)) {
 | 
						|
        index += op;
 | 
						|
      } else if (TextOperation.isInsert(op)) {
 | 
						|
        simpleOperations.push(new Insert(op, index));
 | 
						|
        index += op.length;
 | 
						|
      } else {
 | 
						|
        simpleOperations.push(new Delete(Math.abs(op), index));
 | 
						|
      }
 | 
						|
    }
 | 
						|
    return simpleOperations;
 | 
						|
  };
 | 
						|
 | 
						|
 | 
						|
  return SimpleTextOperation;
 | 
						|
})(this);
 | 
						|
 | 
						|
// Export for CommonJS
 | 
						|
if (typeof module === 'object') {
 | 
						|
  module.exports = ot.SimpleTextOperation;
 | 
						|
} |