Change ListManager implementation

This commit is contained in:
Corentin STG_CADIOU 2015-07-10 15:37:16 +02:00
parent 2723fae12c
commit 9252c9f78a
12 changed files with 950 additions and 1956 deletions

File diff suppressed because one or more lines are too long

@ -519,9 +519,9 @@ module.exports = function() {
o = this.prev_cl.next_cl;
i = distance_to_origin;
while (true) {
oDistance = o.getDistanceToOrigin();
if (o !== this.next_cl) {
if (o.getDistanceToOrigin() === i) {
oDistance = o.getDistanceToOrigin();
if (oDistance === i) {
if (o.uid.creator < this.uid.creator) {
this.prev_cl = o;
distance_to_origin = i + 1;

@ -124,6 +124,7 @@ module.exports = function() {
this.beginning.next_cl = this.end;
this.beginning.execute();
this.end.execute();
this.shortTree = new RBTreeByIndex();
ListManager.__super__.constructor.call(this, custom_type, uid, content, content_operations);
}
@ -183,107 +184,72 @@ module.exports = function() {
return this.beginning.next_cl;
};
ListManager.prototype.getNext = function(start) {
var o;
o = start.next_cl;
while (!(o instanceof ops.Delimiter)) {
if (o.is_deleted) {
o = o.next_cl;
} else if (o instanceof ops.Delimiter) {
ListManager.prototype.getNextNonDeleted = function(start) {
var operation;
if (start.isDeleted()) {
operation = start.next_cl;
while (!(operation instanceof ops.Delimiter)) {
if (operation.is_deleted) {
operation = operation.next_cl;
} else if (operation instanceof ops.Delimiter) {
return false;
} else {
break;
}
}
} else {
operation = start.node.next().node;
if (!operation) {
return false;
} else {
break;
}
}
return o;
return operation;
};
ListManager.prototype.toArray = function() {
var o, result;
o = this.beginning.next_cl;
result = [];
while (o !== this.end) {
if (!o.is_deleted) {
result.push(o.val());
}
o = o.next_cl;
}
return result;
return this.shortTree.map(function(operation) {
return operation.val();
});
};
ListManager.prototype.map = function(f) {
var o, result;
o = this.beginning.next_cl;
result = [];
while (o !== this.end) {
if (!o.is_deleted) {
result.push(f(o));
}
o = o.next_cl;
}
return result;
ListManager.prototype.map = function(fun) {
return this.shortTree.map(fun);
};
ListManager.prototype.fold = function(init, f) {
var o;
o = this.beginning.next_cl;
while (o !== this.end) {
if (!o.is_deleted) {
init = f(init, o);
}
o = o.next_cl;
}
return init;
ListManager.prototype.fold = function(init, fun) {
return this.shortTree.map(function(operation) {
return init = fun(init, operation);
});
};
ListManager.prototype.val = function(pos) {
var o;
if (pos != null) {
o = this.getOperationByPosition(pos + 1);
if (!(o instanceof ops.Delimiter)) {
return o.val();
} else {
throw new Error("this position does not exist");
}
var ref;
if ((0 <= (ref = pos != null) && ref < this.shortTree.size)) {
return this.shortTree.find(pos).val();
} else {
return this.toArray();
}
};
ListManager.prototype.ref = function(pos) {
var o;
if (pos != null) {
o = this.getOperationByPosition(pos + 1);
if (!(o instanceof ops.Delimiter)) {
return o;
} else {
return null;
}
var ref;
if ((0 <= (ref = pos != null) && ref < this.shortTree.size)) {
return this.shortTree.find(pos);
} else {
throw new Error("you must specify a position parameter");
return this.shortTree.map(function(operation) {
return operation;
});
}
};
ListManager.prototype.getOperationByPosition = function(position) {
var o;
o = this.beginning;
while (true) {
if (o instanceof ops.Delimiter && (o.prev_cl != null)) {
o = o.prev_cl;
while (o.isDeleted() && (o.prev_cl != null)) {
o = o.prev_cl;
}
break;
}
if (position <= 0 && !o.isDeleted()) {
break;
}
o = o.next_cl;
if (!o.isDeleted()) {
position -= 1;
}
if (position === 0) {
return this.beginning;
} else if (position === this.shortTree.size + 1) {
return this.end;
} else {
return this.shortTree.find(position - 1);
}
return o;
};
ListManager.prototype.push = function(content) {
@ -301,14 +267,20 @@ module.exports = function() {
};
ListManager.prototype.insertAfter = function(left, contents) {
var c, j, len, right, tmp;
right = left.next_cl;
while (right.isDeleted()) {
right = right.next_cl;
var c, j, leftNode, len, right, rightNode, tmp;
if (left === this.beginning) {
leftNode = null;
rightNode = this.shortTree.findNode(0);
right = rightNode.data;
} else {
rightNode = left.node.nextNode();
leftNode = left.node;
right = rightNode.data;
}
left = right.prev_cl;
if (contents instanceof ops.Operation) {
(new ops.Insert(null, content, null, void 0, void 0, left, right)).execute();
tmp = (new ops.Insert(null, content, null, void 0, void 0, left, right)).execute();
this.shortTree.insert_between(leftNode, rightNode, tmp);
} else {
for (j = 0, len = contents.length; j < len; j++) {
c = contents[j];
@ -316,6 +288,7 @@ module.exports = function() {
c = c._getModel(this.custom_types, this.operations);
}
tmp = (new ops.Insert(null, c, null, void 0, void 0, left, right)).execute();
leftNode = this.shortTree.insert_between(leftNode, rightNode, tmp);
left = tmp;
}
}
@ -328,36 +301,35 @@ module.exports = function() {
return this.insertAfter(ith, contents);
};
ListManager.prototype.deleteRef = function(o, length) {
var d, delete_ops, i, j, ref;
ListManager.prototype.deleteRef = function(operation, length) {
var deleteOperation, i, j, ref;
if (length == null) {
length = 1;
}
delete_ops = [];
for (i = j = 0, ref = length; 0 <= ref ? j < ref : j > ref; i = 0 <= ref ? ++j : --j) {
if (o instanceof ops.Delimiter) {
if (operation instanceof ops.Delimiter) {
break;
}
d = (new ops.Delete(null, void 0, o)).execute();
o = o.next_cl;
while ((!(o instanceof ops.Delimiter)) && o.isDeleted()) {
o = o.next_cl;
}
delete_ops.push(d._encode());
deleteOperation = (new ops.Delete(null, void 0, operation)).execute();
operation.node.traverse_up(function(node, parent) {
return parent.deleted_weight = (parent.deleted_weight || 0) + 1;
});
this.shortTree.remove_node(operation.node);
operation = getNextNonDeleted(operation);
}
return this;
};
ListManager.prototype["delete"] = function(position, length) {
var o;
var operation;
if (length == null) {
length = 1;
}
o = this.getOperationByPosition(position + 1);
return this.deleteRef(o, length);
operation = this.getOperationByPosition(position + 1);
return this.deleteRef(operation, length);
};
ListManager.prototype.callOperationSpecificInsertEvents = function(op) {
ListManager.prototype.callOperationSpecificInsertEvents = function(operation) {
var getContentType;
getContentType = function(content) {
if (content instanceof ops.Operation) {
@ -369,25 +341,25 @@ module.exports = function() {
return this.callEvent([
{
type: "insert",
reference: op,
position: op.getPosition(),
reference: operation,
position: operation.getPosition(),
object: this.getCustomType(),
changedBy: op.uid.creator,
value: getContentType(op.val())
changedBy: operation.uid.creator,
value: getContentType(operation.val())
}
]);
};
ListManager.prototype.callOperationSpecificDeleteEvents = function(op, del_op) {
ListManager.prototype.callOperationSpecificDeleteEvents = function(operation, del_op) {
return this.callEvent([
{
type: "delete",
reference: op,
position: op.getPosition(),
reference: operation,
position: operation.getPosition(),
object: this.getCustomType(),
length: 1,
changedBy: del_op.uid.creator,
oldValue: op.val()
oldValue: operation.val()
}
]);
};
@ -441,14 +413,14 @@ module.exports = function() {
}
};
Composition.prototype.callOperationSpecificInsertEvents = function(op) {
Composition.prototype.callOperationSpecificInsertEvents = function(operation) {
var o;
if (this.tmp_composition_ref != null) {
if (op.uid.creator === this.tmp_composition_ref.creator && op.uid.op_number === this.tmp_composition_ref.op_number) {
this.composition_ref = op;
if (operation.uid.creator === this.tmp_composition_ref.creator && operation.uid.op_number === this.tmp_composition_ref.op_number) {
this.composition_ref = operation;
delete this.tmp_composition_ref;
op = op.next_cl;
if (op === this.end) {
operation = operation.next_cl;
if (operation === this.end) {
return;
}
} else {
@ -456,7 +428,7 @@ module.exports = function() {
}
}
o = this.end.prev_cl;
while (o !== op) {
while (o !== operation) {
this.getCustomType()._unapply(o.undo_delta);
o = o.prev_cl;
}
@ -468,13 +440,13 @@ module.exports = function() {
return this.callEvent([
{
type: "update",
changedBy: op.uid.creator,
changedBy: operation.uid.creator,
newValue: this.val()
}
]);
};
Composition.prototype.callOperationSpecificDeleteEvents = function(op, del_op) {};
Composition.prototype.callOperationSpecificDeleteEvents = function(operation, del_op) {};
Composition.prototype.applyDelta = function(delta, operations) {
(new ops.Insert(null, delta, operations, this, null, this.end.prev_cl, this.end)).execute();
@ -542,40 +514,40 @@ module.exports = function() {
return void 0;
};
ReplaceManager.prototype.callOperationSpecificInsertEvents = function(op) {
ReplaceManager.prototype.callOperationSpecificInsertEvents = function(operation) {
var old_value;
if (op.next_cl.type === "Delimiter" && op.prev_cl.type !== "Delimiter") {
if (!op.is_deleted) {
old_value = op.prev_cl.val();
if (operation.next_cl.type === "Delimiter" && operation.prev_cl.type !== "Delimiter") {
if (!operation.is_deleted) {
old_value = operation.prev_cl.val();
this.callEventDecorator([
{
type: "update",
changedBy: op.uid.creator,
changedBy: operation.uid.creator,
oldValue: old_value
}
]);
}
op.prev_cl.applyDelete();
} else if (op.next_cl.type !== "Delimiter") {
op.applyDelete();
operation.prev_cl.applyDelete();
} else if (operation.next_cl.type !== "Delimiter") {
operation.applyDelete();
} else {
this.callEventDecorator([
{
type: "add",
changedBy: op.uid.creator
changedBy: operation.uid.creator
}
]);
}
return void 0;
};
ReplaceManager.prototype.callOperationSpecificDeleteEvents = function(op, del_op) {
if (op.next_cl.type === "Delimiter") {
ReplaceManager.prototype.callOperationSpecificDeleteEvents = function(operation, del_op) {
if (operation.next_cl.type === "Delimiter") {
return this.callEventDecorator([
{
type: "delete",
changedBy: del_op.uid.creator,
oldValue: op.val()
oldValue: operation.val()
}
]);
}
@ -609,121 +581,6 @@ module.exports = function() {
return ReplaceManager;
})(ops.ListManager);
ops.FastListManager = (function(superClass) {
extend(FastListManager, superClass);
function FastListManager() {
this.shortTree = new RBTreeByIndex();
FastListManager.__super__.constructor.apply(this, arguments);
}
FastListManager.prototype.type = 'FastListManager';
FastListManager.prototype.getNext = function(start) {
return start.node.next().data;
};
FastListManager.prototype.getPrev = function(start) {
return start.node.prev().data;
};
FastListManager.prototype.map = function(fun) {
return this.shortTree.map(function(operation) {
return fun(operation);
});
};
FastListManager.prototype.fold = function(init, f) {
this.shortTree.each(function(operation) {
return init = f(init, operation);
});
return init;
};
FastListManager.prototype.val = function(position) {
if (position != null) {
return (this.shortTree.find(position)).val();
} else {
return this.shortTree.map(function(operation) {
return operation.val();
});
}
};
FastListManager.prototype.ref = function(position) {
if (position != null) {
return this.shortTree.find(position);
} else {
return this.shortTree.map(function(operation) {
return operation;
});
}
};
FastListManager.prototype.push = function(content) {
return this.insertAfter(this.end.prev_cl, [content]);
};
FastListManager.prototype.insertAfter = function(left, contents) {
var nodeOnLeft, nodeOnRight, operation, right;
nodeOnLeft = left === this.beginning ? null : left.node;
nodeOnRight = nodeOnLeft ? nodeOnLeft.next() : this.shortTree.find(0);
right = nodeOnRight ? nodeOnRight.data : this.end;
left = right.prev_cl;
if (contents instanceof ops.Operation) {
operation = new ops.Insert(null, content, null, void 0, void 0, left, right);
operation.node = this.shortTree.insertAfter(nodeOnLeft, operation);
operation.execute();
} else {
contents.forEach(function(content) {
if ((content != null) && (content._name != null) && (content._getModel != null)) {
content = content._getModel(this.custom_types, this.operations);
}
operation = new ops.Insert(null, c, null, void 0, void 0, left, right);
operation.node = this.shortTree.insertAfter(nodeOnLeft, operation);
operation.execute();
left = operation;
return nodeOnLeft = operation.node;
});
}
return this;
};
FastListManager.prototype.insert = function(position, contents) {
var left;
left = (this.shortTree.find(position - 1)) || this.beginning;
return this.insertAfter(left, contents);
};
FastListManager.prototype["delete"] = function(position, length) {
var deleteOp, delete_ops, i, j, nextNode, operation, ref;
if (length == null) {
length = 1;
}
delete_ops = [];
operation = this.shortTree.find(position);
for (i = j = 0, ref = length; 0 <= ref ? j < ref : j > ref; i = 0 <= ref ? ++j : --j) {
if (operation instanceof ops.Delimiter) {
break;
}
deleteOp = new ops.Delete(null, void 0, operation);
deleteOp.execute();
nextNode = operation.node.next();
operation = nextNode ? nextNode.data : this.end;
this.shortTree.remove_node(operation.node);
operation.node = null;
delete_ops.push(d._encode());
}
return this;
};
FastListManager.prototype.getLength = function() {
return this.tree.size;
};
return FastListManager;
})(ops.ListManager);
return basic_ops;
};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -520,10 +520,10 @@ module.exports = ()->
# case 3: $origin > $o.origin
# $this insert_position is to the left of $o (forever!)
while true
oDistance = o.getDistanceToOrigin()
if o isnt @next_cl
oDistance = o.getDistanceToOrigin()
# $o happened concurrently
if o.getDistanceToOrigin() is i
if oDistance is i
# case 1
if o.uid.creator < @uid.creator
@prev_cl = o

@ -108,6 +108,8 @@ module.exports = ()->
@beginning.next_cl = @end
@beginning.execute()
@end.execute()
@shortTree = new RBTreeByIndex()
super custom_type, uid, content, content_operations
type: "ListManager"
@ -157,66 +159,47 @@ module.exports = ()->
@beginning.next_cl
# get the next non-deleted operation
getNext: (start)->
o = start.next_cl
while not ((o instanceof ops.Delimiter))
if o.is_deleted
o = o.next_cl
else if o instanceof ops.Delimiter
getNextNonDeleted: (start)->
if start.isDeleted()
operation = start.next_cl
while not ((operation instanceof ops.Delimiter))
if operation.is_deleted
operation = operation.next_cl
else if operation instanceof ops.Delimiter
return false
else break
else
operation = start.node.next().node
if not operation
return false
else break
o
operation
# Transforms the the list to an array
# Transforms the list to an array
# Doesn't return left-right delimiter.
toArray: ()->
o = @beginning.next_cl
result = []
while o isnt @end
if not o.is_deleted
result.push o.val()
o = o.next_cl
result
@shortTree.map (operation) ->
operation.val()
map: (f)->
o = @beginning.next_cl
result = []
while o isnt @end
if not o.is_deleted
result.push f(o)
o = o.next_cl
result
map: (fun)->
@shortTree.map fun
fold: (init, f)->
o = @beginning.next_cl
while o isnt @end
if not o.is_deleted
init = f(init, o)
o = o.next_cl
init
fold: (init, fun)->
@shortTree.map (operation) ->
init = fun(init, operation)
val: (pos)->
if pos?
o = @getOperationByPosition(pos+1)
if not (o instanceof ops.Delimiter)
o.val()
else
throw new Error "this position does not exist"
if 0 <= pos? < @shortTree.size
@shortTree.find(pos).val()
else
@toArray()
ref: (pos)->
if pos?
o = @getOperationByPosition(pos+1)
if not (o instanceof ops.Delimiter)
o
else
null
# throw new Error "this position does not exist"
if 0 <= pos? < @shortTree.size
@shortTree.find(pos)
else
throw new Error "you must specify a position parameter"
@shortTree.map (operation) ->
operation
#
# Retrieves the x-th not deleted element.
@ -224,24 +207,12 @@ module.exports = ()->
# the 0th character is the left Delimiter
#
getOperationByPosition: (position)->
o = @beginning
while true
# find the i-th op
if o instanceof ops.Delimiter and o.prev_cl?
# the user or you gave a position parameter that is to big
# for the current array. Therefore we reach a Delimiter.
# Then, we'll just return the last character.
o = o.prev_cl
while o.isDeleted() and o.prev_cl?
o = o.prev_cl
break
if position <= 0 and not o.isDeleted()
break
o = o.next_cl
if not o.isDeleted()
position -= 1
o
if position == 0
@beginning
else if position == @shortTree.size + 1
@end
else
@shortTree.find (position-1)
push: (content)->
@insertAfter @end.prev_cl, [content]
@ -255,19 +226,28 @@ module.exports = ()->
insertAfter: (left, contents)->
right = left.next_cl
while right.isDeleted()
right = right.next_cl # find the first character to the right, that is not deleted. In the case that position is 0, its the Delimiter.
if left is @beginning
leftNode = null
rightNode = @shortTree.findNode 0
right = rightNode.data
else
rightNode = left.node.nextNode()
leftNode = left.node
right = rightNode.data
left = right.prev_cl
# TODO: always expect an array as content. Then you can combine this with the other option (else)
if contents instanceof ops.Operation
(new ops.Insert null, content, null, undefined, undefined, left, right).execute()
tmp = (new ops.Insert null, content, null, undefined, undefined, left, right).execute()
@shortTree.insert_between leftNode, rightNode, tmp
else
for c in contents
if c? and c._name? and c._getModel?
c = c._getModel(@custom_types, @operations)
tmp = (new ops.Insert null, c, null, undefined, undefined, left, right).execute()
leftNode = @shortTree.insert_between leftNode, rightNode, tmp
left = tmp
@
@ -288,26 +268,25 @@ module.exports = ()->
#
# @return {ListManager Type} This String object
#
deleteRef: (o, length = 1) ->
delete_ops = []
deleteRef: (operation, length = 1) ->
for i in [0...length]
if o instanceof ops.Delimiter
if operation instanceof ops.Delimiter
break
d = (new ops.Delete null, undefined, o).execute()
o = o.next_cl
while (not (o instanceof ops.Delimiter)) and o.isDeleted()
o = o.next_cl
delete_ops.push d._encode()
deleteOperation = (new ops.Delete null, undefined, operation).execute()
operation.node.traverse_up (node, parent) ->
parent.deleted_weight = (parent.deleted_weight or 0) + 1
@shortTree.remove_node operation.node
operation = getNextNonDeleted operation
@
delete: (position, length = 1)->
o = @getOperationByPosition(position+1) # position 0 in this case is the deletion of the first character
operation = @getOperationByPosition(position+1) # position 0 in this case is the deletion of the first character
@deleteRef o, length
@deleteRef operation, length
callOperationSpecificInsertEvents: (op)->
callOperationSpecificInsertEvents: (operation)->
getContentType = (content)->
if content instanceof ops.Operation
content.getCustomType()
@ -315,22 +294,22 @@ module.exports = ()->
content
@callEvent [
type: "insert"
reference: op
position: op.getPosition()
reference: operation
position: operation.getPosition()
object: @getCustomType()
changedBy: op.uid.creator
value: getContentType op.val()
changedBy: operation.uid.creator
value: getContentType operation.val()
]
callOperationSpecificDeleteEvents: (op, del_op)->
callOperationSpecificDeleteEvents: (operation, del_op)->
@callEvent [
type: "delete"
reference: op
position: op.getPosition()
reference: operation
position: operation.getPosition()
object: @getCustomType() # TODO: You can combine getPosition + getParent in a more efficient manner! (only left Delimiter will hold @parent)
length: 1
changedBy: del_op.uid.creator
oldValue: op.val()
oldValue: operation.val()
]
ops.ListManager.parse = (json)->
@ -381,19 +360,19 @@ module.exports = ()->
#
# This is called, when the Insert-operation was successfully executed.
#
callOperationSpecificInsertEvents: (op)->
callOperationSpecificInsertEvents: (operation)->
if @tmp_composition_ref?
if op.uid.creator is @tmp_composition_ref.creator and op.uid.op_number is @tmp_composition_ref.op_number
@composition_ref = op
if operation.uid.creator is @tmp_composition_ref.creator and operation.uid.op_number is @tmp_composition_ref.op_number
@composition_ref = operation
delete @tmp_composition_ref
op = op.next_cl
if op is @end
operation = operation.next_cl
if operation is @end
return
else
return
o = @end.prev_cl
while o isnt op
while o isnt operation
@getCustomType()._unapply o.undo_delta
o = o.prev_cl
while o isnt @end
@ -403,11 +382,11 @@ module.exports = ()->
@callEvent [
type: "update"
changedBy: op.uid.creator
changedBy: operation.uid.creator
newValue: @val()
]
callOperationSpecificDeleteEvents: (op, del_op)->
callOperationSpecificDeleteEvents: (operation, del_op)->
return
#
@ -492,34 +471,34 @@ module.exports = ()->
# TODO: consider doing this in a more consistent manner. This could also be
# done with execute. But currently, there are no specital Insert-ops for ListManager.
#
callOperationSpecificInsertEvents: (op)->
if op.next_cl.type is "Delimiter" and op.prev_cl.type isnt "Delimiter"
callOperationSpecificInsertEvents: (operation)->
if operation.next_cl.type is "Delimiter" and operation.prev_cl.type isnt "Delimiter"
# this replaces another Replaceable
if not op.is_deleted # When this is received from the HB, this could already be deleted!
old_value = op.prev_cl.val()
if not operation.is_deleted # When this is received from the HB, this could already be deleted!
old_value = operation.prev_cl.val()
@callEventDecorator [
type: "update"
changedBy: op.uid.creator
changedBy: operation.uid.creator
oldValue: old_value
]
op.prev_cl.applyDelete()
else if op.next_cl.type isnt "Delimiter"
operation.prev_cl.applyDelete()
else if operation.next_cl.type isnt "Delimiter"
# This won't be recognized by the user, because another
# concurrent operation is set as the current value of the RM
op.applyDelete()
operation.applyDelete()
else # prev _and_ next are Delimiters. This is the first created Replaceable in the RM
@callEventDecorator [
type: "add"
changedBy: op.uid.creator
changedBy: operation.uid.creator
]
undefined
callOperationSpecificDeleteEvents: (op, del_op)->
if op.next_cl.type is "Delimiter"
callOperationSpecificDeleteEvents: (operation, del_op)->
if operation.next_cl.type is "Delimiter"
@callEventDecorator [
type: "delete"
changedBy: del_op.uid.creator
oldValue: op.val()
oldValue: operation.val()
]
@ -554,103 +533,4 @@ module.exports = ()->
# throw new Error "Replace Manager doesn't contain anything."
o.val?() # ? - for the case that (currently) the RM does not contain anything (then o is a Delimiter)
class ops.FastListManager extends ops.ListManager
constructor: () ->
@shortTree = new RBTreeByIndex()
super arguments...
type: 'FastListManager'
getNext: (start)->
start.node.next().data
getPrev: (start)->
start.node.prev().data
map: (fun)->
@shortTree.map (operation) ->
fun operation
fold: (init, f) ->
@shortTree.each (operation) ->
init = f(init, operation)
init
val: (position)->
if position?
(@shortTree.find position).val()
else
@shortTree.map (operation) ->
operation.val()
ref: (position)->
if position?
@shortTree.find position
else
@shortTree.map (operation) ->
operation
push: (content) ->
@insertAfter @end.prev_cl, [content]
insertAfter: (left, contents) ->
# the operation is inserted just before the next non deleted insertion
# the operation is stored in the tree just after left
nodeOnLeft = if left is @beginning then null else left.node
nodeOnRight = if nodeOnLeft then nodeOnLeft.next() else @shortTree.find 0
right = if nodeOnRight then nodeOnRight.data else @end
left = right.prev_cl
if contents instanceof ops.Operation
operation = new ops.Insert null, content, null, undefined, undefined, left, right
operation.node = @shortTree.insertAfter nodeOnLeft, operation
operation.execute()
else
contents.forEach (content) ->
if content? and content._name? and content._getModel?
content = content._getModel(@custom_types, @operations)
#TODO: override Insert.prototype.getDistanceToOrigin (in Insert.prototype.execute)
operation = new ops.Insert null, c, null, undefined, undefined, left, right
operation.node = @shortTree.insertAfter nodeOnLeft, operation
operation.execute()
left = operation
nodeOnLeft = operation.node
@
insert: (position, contents) ->
left = (@shortTree.find (position-1)) or @beginning
@insertAfter left, contents
delete: (position, length = 1) ->
delete_ops = []
operation = @shortTree.find position
for i in [0...length]
if operation instanceof ops.Delimiter
break
deleteOp = new ops.Delete null, undefined, operation
# execute the delete operation
deleteOp.execute()
# get the next operation from the shortTree
nextNode = operation.node.next()
operation = if nextNode then nextNode.data else @end
# remove the operation from the shortTree
@shortTree.remove_node operation.node
operation.node = null
delete_ops.push d._encode()
@
getLength: () ->
@tree.size
basic_ops

4
y.js

File diff suppressed because one or more lines are too long