Change ListManager implementation
This commit is contained in:
parent
2723fae12c
commit
9252c9f78a
File diff suppressed because one or more lines are too long
@ -519,9 +519,9 @@ module.exports = function() {
|
|||||||
o = this.prev_cl.next_cl;
|
o = this.prev_cl.next_cl;
|
||||||
i = distance_to_origin;
|
i = distance_to_origin;
|
||||||
while (true) {
|
while (true) {
|
||||||
oDistance = o.getDistanceToOrigin();
|
|
||||||
if (o !== this.next_cl) {
|
if (o !== this.next_cl) {
|
||||||
if (o.getDistanceToOrigin() === i) {
|
oDistance = o.getDistanceToOrigin();
|
||||||
|
if (oDistance === i) {
|
||||||
if (o.uid.creator < this.uid.creator) {
|
if (o.uid.creator < this.uid.creator) {
|
||||||
this.prev_cl = o;
|
this.prev_cl = o;
|
||||||
distance_to_origin = i + 1;
|
distance_to_origin = i + 1;
|
||||||
|
@ -124,6 +124,7 @@ module.exports = function() {
|
|||||||
this.beginning.next_cl = this.end;
|
this.beginning.next_cl = this.end;
|
||||||
this.beginning.execute();
|
this.beginning.execute();
|
||||||
this.end.execute();
|
this.end.execute();
|
||||||
|
this.shortTree = new RBTreeByIndex();
|
||||||
ListManager.__super__.constructor.call(this, custom_type, uid, content, content_operations);
|
ListManager.__super__.constructor.call(this, custom_type, uid, content, content_operations);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,107 +184,72 @@ module.exports = function() {
|
|||||||
return this.beginning.next_cl;
|
return this.beginning.next_cl;
|
||||||
};
|
};
|
||||||
|
|
||||||
ListManager.prototype.getNext = function(start) {
|
ListManager.prototype.getNextNonDeleted = function(start) {
|
||||||
var o;
|
var operation;
|
||||||
o = start.next_cl;
|
if (start.isDeleted()) {
|
||||||
while (!(o instanceof ops.Delimiter)) {
|
operation = start.next_cl;
|
||||||
if (o.is_deleted) {
|
while (!(operation instanceof ops.Delimiter)) {
|
||||||
o = o.next_cl;
|
if (operation.is_deleted) {
|
||||||
} else if (o instanceof ops.Delimiter) {
|
operation = operation.next_cl;
|
||||||
|
} else if (operation instanceof ops.Delimiter) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
operation = start.node.next().node;
|
||||||
|
if (!operation) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return o;
|
return operation;
|
||||||
};
|
};
|
||||||
|
|
||||||
ListManager.prototype.toArray = function() {
|
ListManager.prototype.toArray = function() {
|
||||||
var o, result;
|
return this.shortTree.map(function(operation) {
|
||||||
o = this.beginning.next_cl;
|
return operation.val();
|
||||||
result = [];
|
});
|
||||||
while (o !== this.end) {
|
|
||||||
if (!o.is_deleted) {
|
|
||||||
result.push(o.val());
|
|
||||||
}
|
|
||||||
o = o.next_cl;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ListManager.prototype.map = function(f) {
|
ListManager.prototype.map = function(fun) {
|
||||||
var o, result;
|
return this.shortTree.map(fun);
|
||||||
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.fold = function(init, f) {
|
ListManager.prototype.fold = function(init, fun) {
|
||||||
var o;
|
return this.shortTree.map(function(operation) {
|
||||||
o = this.beginning.next_cl;
|
return init = fun(init, operation);
|
||||||
while (o !== this.end) {
|
});
|
||||||
if (!o.is_deleted) {
|
|
||||||
init = f(init, o);
|
|
||||||
}
|
|
||||||
o = o.next_cl;
|
|
||||||
}
|
|
||||||
return init;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ListManager.prototype.val = function(pos) {
|
ListManager.prototype.val = function(pos) {
|
||||||
var o;
|
var ref;
|
||||||
if (pos != null) {
|
if ((0 <= (ref = pos != null) && ref < this.shortTree.size)) {
|
||||||
o = this.getOperationByPosition(pos + 1);
|
return this.shortTree.find(pos).val();
|
||||||
if (!(o instanceof ops.Delimiter)) {
|
|
||||||
return o.val();
|
|
||||||
} else {
|
|
||||||
throw new Error("this position does not exist");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return this.toArray();
|
return this.toArray();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ListManager.prototype.ref = function(pos) {
|
ListManager.prototype.ref = function(pos) {
|
||||||
var o;
|
var ref;
|
||||||
if (pos != null) {
|
if ((0 <= (ref = pos != null) && ref < this.shortTree.size)) {
|
||||||
o = this.getOperationByPosition(pos + 1);
|
return this.shortTree.find(pos);
|
||||||
if (!(o instanceof ops.Delimiter)) {
|
|
||||||
return o;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error("you must specify a position parameter");
|
return this.shortTree.map(function(operation) {
|
||||||
|
return operation;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ListManager.prototype.getOperationByPosition = function(position) {
|
ListManager.prototype.getOperationByPosition = function(position) {
|
||||||
var o;
|
if (position === 0) {
|
||||||
o = this.beginning;
|
return this.beginning;
|
||||||
while (true) {
|
} else if (position === this.shortTree.size + 1) {
|
||||||
if (o instanceof ops.Delimiter && (o.prev_cl != null)) {
|
return this.end;
|
||||||
o = o.prev_cl;
|
} else {
|
||||||
while (o.isDeleted() && (o.prev_cl != null)) {
|
return this.shortTree.find(position - 1);
|
||||||
o = o.prev_cl;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (position <= 0 && !o.isDeleted()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
o = o.next_cl;
|
|
||||||
if (!o.isDeleted()) {
|
|
||||||
position -= 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return o;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ListManager.prototype.push = function(content) {
|
ListManager.prototype.push = function(content) {
|
||||||
@ -301,14 +267,20 @@ module.exports = function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
ListManager.prototype.insertAfter = function(left, contents) {
|
ListManager.prototype.insertAfter = function(left, contents) {
|
||||||
var c, j, len, right, tmp;
|
var c, j, leftNode, len, right, rightNode, tmp;
|
||||||
right = left.next_cl;
|
if (left === this.beginning) {
|
||||||
while (right.isDeleted()) {
|
leftNode = null;
|
||||||
right = right.next_cl;
|
rightNode = this.shortTree.findNode(0);
|
||||||
|
right = rightNode.data;
|
||||||
|
} else {
|
||||||
|
rightNode = left.node.nextNode();
|
||||||
|
leftNode = left.node;
|
||||||
|
right = rightNode.data;
|
||||||
}
|
}
|
||||||
left = right.prev_cl;
|
left = right.prev_cl;
|
||||||
if (contents instanceof ops.Operation) {
|
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 {
|
} else {
|
||||||
for (j = 0, len = contents.length; j < len; j++) {
|
for (j = 0, len = contents.length; j < len; j++) {
|
||||||
c = contents[j];
|
c = contents[j];
|
||||||
@ -316,6 +288,7 @@ module.exports = function() {
|
|||||||
c = c._getModel(this.custom_types, this.operations);
|
c = c._getModel(this.custom_types, this.operations);
|
||||||
}
|
}
|
||||||
tmp = (new ops.Insert(null, c, null, void 0, void 0, left, right)).execute();
|
tmp = (new ops.Insert(null, c, null, void 0, void 0, left, right)).execute();
|
||||||
|
leftNode = this.shortTree.insert_between(leftNode, rightNode, tmp);
|
||||||
left = tmp;
|
left = tmp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -328,36 +301,35 @@ module.exports = function() {
|
|||||||
return this.insertAfter(ith, contents);
|
return this.insertAfter(ith, contents);
|
||||||
};
|
};
|
||||||
|
|
||||||
ListManager.prototype.deleteRef = function(o, length) {
|
ListManager.prototype.deleteRef = function(operation, length) {
|
||||||
var d, delete_ops, i, j, ref;
|
var deleteOperation, i, j, ref;
|
||||||
if (length == null) {
|
if (length == null) {
|
||||||
length = 1;
|
length = 1;
|
||||||
}
|
}
|
||||||
delete_ops = [];
|
|
||||||
for (i = j = 0, ref = length; 0 <= ref ? j < ref : j > ref; i = 0 <= ref ? ++j : --j) {
|
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;
|
break;
|
||||||
}
|
}
|
||||||
d = (new ops.Delete(null, void 0, o)).execute();
|
deleteOperation = (new ops.Delete(null, void 0, operation)).execute();
|
||||||
o = o.next_cl;
|
operation.node.traverse_up(function(node, parent) {
|
||||||
while ((!(o instanceof ops.Delimiter)) && o.isDeleted()) {
|
return parent.deleted_weight = (parent.deleted_weight || 0) + 1;
|
||||||
o = o.next_cl;
|
});
|
||||||
}
|
this.shortTree.remove_node(operation.node);
|
||||||
delete_ops.push(d._encode());
|
operation = getNextNonDeleted(operation);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
ListManager.prototype["delete"] = function(position, length) {
|
ListManager.prototype["delete"] = function(position, length) {
|
||||||
var o;
|
var operation;
|
||||||
if (length == null) {
|
if (length == null) {
|
||||||
length = 1;
|
length = 1;
|
||||||
}
|
}
|
||||||
o = this.getOperationByPosition(position + 1);
|
operation = this.getOperationByPosition(position + 1);
|
||||||
return this.deleteRef(o, length);
|
return this.deleteRef(operation, length);
|
||||||
};
|
};
|
||||||
|
|
||||||
ListManager.prototype.callOperationSpecificInsertEvents = function(op) {
|
ListManager.prototype.callOperationSpecificInsertEvents = function(operation) {
|
||||||
var getContentType;
|
var getContentType;
|
||||||
getContentType = function(content) {
|
getContentType = function(content) {
|
||||||
if (content instanceof ops.Operation) {
|
if (content instanceof ops.Operation) {
|
||||||
@ -369,25 +341,25 @@ module.exports = function() {
|
|||||||
return this.callEvent([
|
return this.callEvent([
|
||||||
{
|
{
|
||||||
type: "insert",
|
type: "insert",
|
||||||
reference: op,
|
reference: operation,
|
||||||
position: op.getPosition(),
|
position: operation.getPosition(),
|
||||||
object: this.getCustomType(),
|
object: this.getCustomType(),
|
||||||
changedBy: op.uid.creator,
|
changedBy: operation.uid.creator,
|
||||||
value: getContentType(op.val())
|
value: getContentType(operation.val())
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
ListManager.prototype.callOperationSpecificDeleteEvents = function(op, del_op) {
|
ListManager.prototype.callOperationSpecificDeleteEvents = function(operation, del_op) {
|
||||||
return this.callEvent([
|
return this.callEvent([
|
||||||
{
|
{
|
||||||
type: "delete",
|
type: "delete",
|
||||||
reference: op,
|
reference: operation,
|
||||||
position: op.getPosition(),
|
position: operation.getPosition(),
|
||||||
object: this.getCustomType(),
|
object: this.getCustomType(),
|
||||||
length: 1,
|
length: 1,
|
||||||
changedBy: del_op.uid.creator,
|
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;
|
var o;
|
||||||
if (this.tmp_composition_ref != null) {
|
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) {
|
if (operation.uid.creator === this.tmp_composition_ref.creator && operation.uid.op_number === this.tmp_composition_ref.op_number) {
|
||||||
this.composition_ref = op;
|
this.composition_ref = operation;
|
||||||
delete this.tmp_composition_ref;
|
delete this.tmp_composition_ref;
|
||||||
op = op.next_cl;
|
operation = operation.next_cl;
|
||||||
if (op === this.end) {
|
if (operation === this.end) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -456,7 +428,7 @@ module.exports = function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
o = this.end.prev_cl;
|
o = this.end.prev_cl;
|
||||||
while (o !== op) {
|
while (o !== operation) {
|
||||||
this.getCustomType()._unapply(o.undo_delta);
|
this.getCustomType()._unapply(o.undo_delta);
|
||||||
o = o.prev_cl;
|
o = o.prev_cl;
|
||||||
}
|
}
|
||||||
@ -468,13 +440,13 @@ module.exports = function() {
|
|||||||
return this.callEvent([
|
return this.callEvent([
|
||||||
{
|
{
|
||||||
type: "update",
|
type: "update",
|
||||||
changedBy: op.uid.creator,
|
changedBy: operation.uid.creator,
|
||||||
newValue: this.val()
|
newValue: this.val()
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
Composition.prototype.callOperationSpecificDeleteEvents = function(op, del_op) {};
|
Composition.prototype.callOperationSpecificDeleteEvents = function(operation, del_op) {};
|
||||||
|
|
||||||
Composition.prototype.applyDelta = function(delta, operations) {
|
Composition.prototype.applyDelta = function(delta, operations) {
|
||||||
(new ops.Insert(null, delta, operations, this, null, this.end.prev_cl, this.end)).execute();
|
(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;
|
return void 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
ReplaceManager.prototype.callOperationSpecificInsertEvents = function(op) {
|
ReplaceManager.prototype.callOperationSpecificInsertEvents = function(operation) {
|
||||||
var old_value;
|
var old_value;
|
||||||
if (op.next_cl.type === "Delimiter" && op.prev_cl.type !== "Delimiter") {
|
if (operation.next_cl.type === "Delimiter" && operation.prev_cl.type !== "Delimiter") {
|
||||||
if (!op.is_deleted) {
|
if (!operation.is_deleted) {
|
||||||
old_value = op.prev_cl.val();
|
old_value = operation.prev_cl.val();
|
||||||
this.callEventDecorator([
|
this.callEventDecorator([
|
||||||
{
|
{
|
||||||
type: "update",
|
type: "update",
|
||||||
changedBy: op.uid.creator,
|
changedBy: operation.uid.creator,
|
||||||
oldValue: old_value
|
oldValue: old_value
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
op.prev_cl.applyDelete();
|
operation.prev_cl.applyDelete();
|
||||||
} else if (op.next_cl.type !== "Delimiter") {
|
} else if (operation.next_cl.type !== "Delimiter") {
|
||||||
op.applyDelete();
|
operation.applyDelete();
|
||||||
} else {
|
} else {
|
||||||
this.callEventDecorator([
|
this.callEventDecorator([
|
||||||
{
|
{
|
||||||
type: "add",
|
type: "add",
|
||||||
changedBy: op.uid.creator
|
changedBy: operation.uid.creator
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
return void 0;
|
return void 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
ReplaceManager.prototype.callOperationSpecificDeleteEvents = function(op, del_op) {
|
ReplaceManager.prototype.callOperationSpecificDeleteEvents = function(operation, del_op) {
|
||||||
if (op.next_cl.type === "Delimiter") {
|
if (operation.next_cl.type === "Delimiter") {
|
||||||
return this.callEventDecorator([
|
return this.callEventDecorator([
|
||||||
{
|
{
|
||||||
type: "delete",
|
type: "delete",
|
||||||
changedBy: del_op.uid.creator,
|
changedBy: del_op.uid.creator,
|
||||||
oldValue: op.val()
|
oldValue: operation.val()
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@ -609,121 +581,6 @@ module.exports = function() {
|
|||||||
|
|
||||||
return ReplaceManager;
|
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);
|
})(ops.ListManager);
|
||||||
return basic_ops;
|
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
|
# case 3: $origin > $o.origin
|
||||||
# $this insert_position is to the left of $o (forever!)
|
# $this insert_position is to the left of $o (forever!)
|
||||||
while true
|
while true
|
||||||
oDistance = o.getDistanceToOrigin()
|
|
||||||
if o isnt @next_cl
|
if o isnt @next_cl
|
||||||
|
oDistance = o.getDistanceToOrigin()
|
||||||
# $o happened concurrently
|
# $o happened concurrently
|
||||||
if o.getDistanceToOrigin() is i
|
if oDistance is i
|
||||||
# case 1
|
# case 1
|
||||||
if o.uid.creator < @uid.creator
|
if o.uid.creator < @uid.creator
|
||||||
@prev_cl = o
|
@prev_cl = o
|
||||||
|
@ -108,6 +108,8 @@ module.exports = ()->
|
|||||||
@beginning.next_cl = @end
|
@beginning.next_cl = @end
|
||||||
@beginning.execute()
|
@beginning.execute()
|
||||||
@end.execute()
|
@end.execute()
|
||||||
|
|
||||||
|
@shortTree = new RBTreeByIndex()
|
||||||
super custom_type, uid, content, content_operations
|
super custom_type, uid, content, content_operations
|
||||||
|
|
||||||
type: "ListManager"
|
type: "ListManager"
|
||||||
@ -157,66 +159,47 @@ module.exports = ()->
|
|||||||
@beginning.next_cl
|
@beginning.next_cl
|
||||||
|
|
||||||
# get the next non-deleted operation
|
# get the next non-deleted operation
|
||||||
getNext: (start)->
|
getNextNonDeleted: (start)->
|
||||||
o = start.next_cl
|
if start.isDeleted()
|
||||||
while not ((o instanceof ops.Delimiter))
|
operation = start.next_cl
|
||||||
if o.is_deleted
|
while not ((operation instanceof ops.Delimiter))
|
||||||
o = o.next_cl
|
if operation.is_deleted
|
||||||
else if o instanceof ops.Delimiter
|
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
|
return false
|
||||||
else break
|
|
||||||
|
|
||||||
o
|
operation
|
||||||
|
|
||||||
|
# Transforms the list to an array
|
||||||
# Transforms the the list to an array
|
|
||||||
# Doesn't return left-right delimiter.
|
# Doesn't return left-right delimiter.
|
||||||
toArray: ()->
|
toArray: ()->
|
||||||
o = @beginning.next_cl
|
@shortTree.map (operation) ->
|
||||||
result = []
|
operation.val()
|
||||||
while o isnt @end
|
|
||||||
if not o.is_deleted
|
|
||||||
result.push o.val()
|
|
||||||
o = o.next_cl
|
|
||||||
result
|
|
||||||
|
|
||||||
map: (f)->
|
map: (fun)->
|
||||||
o = @beginning.next_cl
|
@shortTree.map fun
|
||||||
result = []
|
|
||||||
while o isnt @end
|
|
||||||
if not o.is_deleted
|
|
||||||
result.push f(o)
|
|
||||||
o = o.next_cl
|
|
||||||
result
|
|
||||||
|
|
||||||
fold: (init, f)->
|
fold: (init, fun)->
|
||||||
o = @beginning.next_cl
|
@shortTree.map (operation) ->
|
||||||
while o isnt @end
|
init = fun(init, operation)
|
||||||
if not o.is_deleted
|
|
||||||
init = f(init, o)
|
|
||||||
o = o.next_cl
|
|
||||||
init
|
|
||||||
|
|
||||||
val: (pos)->
|
val: (pos)->
|
||||||
if pos?
|
if 0 <= pos? < @shortTree.size
|
||||||
o = @getOperationByPosition(pos+1)
|
@shortTree.find(pos).val()
|
||||||
if not (o instanceof ops.Delimiter)
|
|
||||||
o.val()
|
|
||||||
else
|
|
||||||
throw new Error "this position does not exist"
|
|
||||||
else
|
else
|
||||||
@toArray()
|
@toArray()
|
||||||
|
|
||||||
ref: (pos)->
|
ref: (pos)->
|
||||||
if pos?
|
if 0 <= pos? < @shortTree.size
|
||||||
o = @getOperationByPosition(pos+1)
|
@shortTree.find(pos)
|
||||||
if not (o instanceof ops.Delimiter)
|
|
||||||
o
|
|
||||||
else
|
|
||||||
null
|
|
||||||
# throw new Error "this position does not exist"
|
|
||||||
else
|
else
|
||||||
throw new Error "you must specify a position parameter"
|
@shortTree.map (operation) ->
|
||||||
|
operation
|
||||||
|
|
||||||
#
|
#
|
||||||
# Retrieves the x-th not deleted element.
|
# Retrieves the x-th not deleted element.
|
||||||
@ -224,24 +207,12 @@ module.exports = ()->
|
|||||||
# the 0th character is the left Delimiter
|
# the 0th character is the left Delimiter
|
||||||
#
|
#
|
||||||
getOperationByPosition: (position)->
|
getOperationByPosition: (position)->
|
||||||
o = @beginning
|
if position == 0
|
||||||
while true
|
@beginning
|
||||||
# find the i-th op
|
else if position == @shortTree.size + 1
|
||||||
if o instanceof ops.Delimiter and o.prev_cl?
|
@end
|
||||||
# the user or you gave a position parameter that is to big
|
else
|
||||||
# for the current array. Therefore we reach a Delimiter.
|
@shortTree.find (position-1)
|
||||||
# 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
|
|
||||||
|
|
||||||
push: (content)->
|
push: (content)->
|
||||||
@insertAfter @end.prev_cl, [content]
|
@insertAfter @end.prev_cl, [content]
|
||||||
@ -255,19 +226,28 @@ module.exports = ()->
|
|||||||
|
|
||||||
|
|
||||||
insertAfter: (left, contents)->
|
insertAfter: (left, contents)->
|
||||||
right = left.next_cl
|
if left is @beginning
|
||||||
while right.isDeleted()
|
leftNode = null
|
||||||
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.
|
rightNode = @shortTree.findNode 0
|
||||||
|
right = rightNode.data
|
||||||
|
else
|
||||||
|
rightNode = left.node.nextNode()
|
||||||
|
leftNode = left.node
|
||||||
|
right = rightNode.data
|
||||||
|
|
||||||
left = right.prev_cl
|
left = right.prev_cl
|
||||||
|
|
||||||
# TODO: always expect an array as content. Then you can combine this with the other option (else)
|
# TODO: always expect an array as content. Then you can combine this with the other option (else)
|
||||||
if contents instanceof ops.Operation
|
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
|
else
|
||||||
for c in contents
|
for c in contents
|
||||||
if c? and c._name? and c._getModel?
|
if c? and c._name? and c._getModel?
|
||||||
c = c._getModel(@custom_types, @operations)
|
c = c._getModel(@custom_types, @operations)
|
||||||
tmp = (new ops.Insert null, c, null, undefined, undefined, left, right).execute()
|
tmp = (new ops.Insert null, c, null, undefined, undefined, left, right).execute()
|
||||||
|
leftNode = @shortTree.insert_between leftNode, rightNode, tmp
|
||||||
|
|
||||||
left = tmp
|
left = tmp
|
||||||
@
|
@
|
||||||
|
|
||||||
@ -288,26 +268,25 @@ module.exports = ()->
|
|||||||
#
|
#
|
||||||
# @return {ListManager Type} This String object
|
# @return {ListManager Type} This String object
|
||||||
#
|
#
|
||||||
|
deleteRef: (operation, length = 1) ->
|
||||||
deleteRef: (o, length = 1) ->
|
|
||||||
delete_ops = []
|
|
||||||
for i in [0...length]
|
for i in [0...length]
|
||||||
if o instanceof ops.Delimiter
|
if operation instanceof ops.Delimiter
|
||||||
break
|
break
|
||||||
d = (new ops.Delete null, undefined, o).execute()
|
deleteOperation = (new ops.Delete null, undefined, operation).execute()
|
||||||
o = o.next_cl
|
operation.node.traverse_up (node, parent) ->
|
||||||
while (not (o instanceof ops.Delimiter)) and o.isDeleted()
|
parent.deleted_weight = (parent.deleted_weight or 0) + 1
|
||||||
o = o.next_cl
|
@shortTree.remove_node operation.node
|
||||||
delete_ops.push d._encode()
|
|
||||||
|
operation = getNextNonDeleted operation
|
||||||
@
|
@
|
||||||
|
|
||||||
delete: (position, length = 1)->
|
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)->
|
getContentType = (content)->
|
||||||
if content instanceof ops.Operation
|
if content instanceof ops.Operation
|
||||||
content.getCustomType()
|
content.getCustomType()
|
||||||
@ -315,22 +294,22 @@ module.exports = ()->
|
|||||||
content
|
content
|
||||||
@callEvent [
|
@callEvent [
|
||||||
type: "insert"
|
type: "insert"
|
||||||
reference: op
|
reference: operation
|
||||||
position: op.getPosition()
|
position: operation.getPosition()
|
||||||
object: @getCustomType()
|
object: @getCustomType()
|
||||||
changedBy: op.uid.creator
|
changedBy: operation.uid.creator
|
||||||
value: getContentType op.val()
|
value: getContentType operation.val()
|
||||||
]
|
]
|
||||||
|
|
||||||
callOperationSpecificDeleteEvents: (op, del_op)->
|
callOperationSpecificDeleteEvents: (operation, del_op)->
|
||||||
@callEvent [
|
@callEvent [
|
||||||
type: "delete"
|
type: "delete"
|
||||||
reference: op
|
reference: operation
|
||||||
position: op.getPosition()
|
position: operation.getPosition()
|
||||||
object: @getCustomType() # TODO: You can combine getPosition + getParent in a more efficient manner! (only left Delimiter will hold @parent)
|
object: @getCustomType() # TODO: You can combine getPosition + getParent in a more efficient manner! (only left Delimiter will hold @parent)
|
||||||
length: 1
|
length: 1
|
||||||
changedBy: del_op.uid.creator
|
changedBy: del_op.uid.creator
|
||||||
oldValue: op.val()
|
oldValue: operation.val()
|
||||||
]
|
]
|
||||||
|
|
||||||
ops.ListManager.parse = (json)->
|
ops.ListManager.parse = (json)->
|
||||||
@ -381,19 +360,19 @@ module.exports = ()->
|
|||||||
#
|
#
|
||||||
# This is called, when the Insert-operation was successfully executed.
|
# This is called, when the Insert-operation was successfully executed.
|
||||||
#
|
#
|
||||||
callOperationSpecificInsertEvents: (op)->
|
callOperationSpecificInsertEvents: (operation)->
|
||||||
if @tmp_composition_ref?
|
if @tmp_composition_ref?
|
||||||
if op.uid.creator is @tmp_composition_ref.creator and op.uid.op_number is @tmp_composition_ref.op_number
|
if operation.uid.creator is @tmp_composition_ref.creator and operation.uid.op_number is @tmp_composition_ref.op_number
|
||||||
@composition_ref = op
|
@composition_ref = operation
|
||||||
delete @tmp_composition_ref
|
delete @tmp_composition_ref
|
||||||
op = op.next_cl
|
operation = operation.next_cl
|
||||||
if op is @end
|
if operation is @end
|
||||||
return
|
return
|
||||||
else
|
else
|
||||||
return
|
return
|
||||||
|
|
||||||
o = @end.prev_cl
|
o = @end.prev_cl
|
||||||
while o isnt op
|
while o isnt operation
|
||||||
@getCustomType()._unapply o.undo_delta
|
@getCustomType()._unapply o.undo_delta
|
||||||
o = o.prev_cl
|
o = o.prev_cl
|
||||||
while o isnt @end
|
while o isnt @end
|
||||||
@ -403,11 +382,11 @@ module.exports = ()->
|
|||||||
|
|
||||||
@callEvent [
|
@callEvent [
|
||||||
type: "update"
|
type: "update"
|
||||||
changedBy: op.uid.creator
|
changedBy: operation.uid.creator
|
||||||
newValue: @val()
|
newValue: @val()
|
||||||
]
|
]
|
||||||
|
|
||||||
callOperationSpecificDeleteEvents: (op, del_op)->
|
callOperationSpecificDeleteEvents: (operation, del_op)->
|
||||||
return
|
return
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -492,34 +471,34 @@ module.exports = ()->
|
|||||||
# TODO: consider doing this in a more consistent manner. This could also be
|
# 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.
|
# done with execute. But currently, there are no specital Insert-ops for ListManager.
|
||||||
#
|
#
|
||||||
callOperationSpecificInsertEvents: (op)->
|
callOperationSpecificInsertEvents: (operation)->
|
||||||
if op.next_cl.type is "Delimiter" and op.prev_cl.type isnt "Delimiter"
|
if operation.next_cl.type is "Delimiter" and operation.prev_cl.type isnt "Delimiter"
|
||||||
# this replaces another Replaceable
|
# this replaces another Replaceable
|
||||||
if not op.is_deleted # When this is received from the HB, this could already be deleted!
|
if not operation.is_deleted # When this is received from the HB, this could already be deleted!
|
||||||
old_value = op.prev_cl.val()
|
old_value = operation.prev_cl.val()
|
||||||
@callEventDecorator [
|
@callEventDecorator [
|
||||||
type: "update"
|
type: "update"
|
||||||
changedBy: op.uid.creator
|
changedBy: operation.uid.creator
|
||||||
oldValue: old_value
|
oldValue: old_value
|
||||||
]
|
]
|
||||||
op.prev_cl.applyDelete()
|
operation.prev_cl.applyDelete()
|
||||||
else if op.next_cl.type isnt "Delimiter"
|
else if operation.next_cl.type isnt "Delimiter"
|
||||||
# This won't be recognized by the user, because another
|
# This won't be recognized by the user, because another
|
||||||
# concurrent operation is set as the current value of the RM
|
# 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
|
else # prev _and_ next are Delimiters. This is the first created Replaceable in the RM
|
||||||
@callEventDecorator [
|
@callEventDecorator [
|
||||||
type: "add"
|
type: "add"
|
||||||
changedBy: op.uid.creator
|
changedBy: operation.uid.creator
|
||||||
]
|
]
|
||||||
undefined
|
undefined
|
||||||
|
|
||||||
callOperationSpecificDeleteEvents: (op, del_op)->
|
callOperationSpecificDeleteEvents: (operation, del_op)->
|
||||||
if op.next_cl.type is "Delimiter"
|
if operation.next_cl.type is "Delimiter"
|
||||||
@callEventDecorator [
|
@callEventDecorator [
|
||||||
type: "delete"
|
type: "delete"
|
||||||
changedBy: del_op.uid.creator
|
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."
|
# 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)
|
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
|
basic_ops
|
||||||
|
Loading…
x
Reference in New Issue
Block a user