yjs/src/Types/Array.js
2015-07-15 21:24:05 +02:00

178 lines
5.4 KiB
JavaScript

(function(){
class YArray {
constructor (os, _model, idArray, valArray) {
this.os = os;
this._model = _model;
// Array of all the operation id's
this.idArray = idArray;
// Array of all the values
this.valArray = valArray;
this.eventHandler = new EventHandler( ops =>{
var userEvents = [];
for (var i in ops) {
var op = ops[i];
if (op.struct === "Insert") {
let pos;
// we check op.left only!,
// because op.right might not be defined when this is called
if (op.left === null) {
pos = 0;
} else {
var sid = JSON.stringify(op.left);
pos = this.idArray.indexOf(sid) + 1;
if (pos <= 0) {
throw new Error("Unexpected operation!");
}
}
this.idArray.splice(pos, 0, JSON.stringify(op.id));
this.valArray.splice(pos, 0, op.content);
userEvents.push({
type: "insert",
object: this,
index: pos,
length: 1
});
} else if (op.struct === "Delete") {
let pos = this.idArray.indexOf(JSON.stringify(op.target));
this.idArray.splice(pos, 1);
this.valArray.splice(pos, 1);
userEvents.push({
type: "delete",
object: this,
index: pos,
length: 1
});
} else {
throw new Error("Unexpected struct!");
}
}
this.eventHandler.callUserEventListeners(userEvents);
});
}
get (pos) {
if (pos == null || typeof pos !== "number") {
throw new Error("pos must be a number!");
}
return this.valArray[pos];
}
toArray() {
return this.valArray.slice();
}
insert (pos, contents) {
if (typeof pos !== "number") {
throw new Error("pos must be a number!");
}
if (!(contents instanceof Array)) {
throw new Error("contents must be an Array of objects!");
}
if (contents.length === 0) {
return;
}
if (pos > this.idArray.length || pos < 0) {
throw new Error("This position exceeds the range of the array!");
}
var mostLeft = pos === 0 ? null : JSON.parse(this.idArray[pos - 1]);
var ops = [];
var prevId = mostLeft;
for (var i = 0; i < contents.length; i++) {
var op = {
left: prevId,
origin: prevId,
// right: mostRight,
// NOTE: I intentionally do not define right here, because it could be deleted
// at the time of creating this operation, and is therefore not defined in idArray
parent: this._model,
content: contents[i],
struct: "Insert",
id: this.os.getNextOpId()
};
ops.push(op);
prevId = op.id;
}
var eventHandler = this.eventHandler;
eventHandler.awaitAndPrematurelyCall(ops);
this.os.requestTransaction(function*(){
// now we can set the right reference.
var mostRight;
if (mostLeft != null) {
mostRight = (yield* this.getOperation(mostLeft)).right;
} else {
mostRight = (yield* this.getOperation(ops[0].parent)).start;
}
for (var j in ops) {
ops[j].right = mostRight;
}
yield* this.applyCreatedOperations(ops);
eventHandler.awaitedLastInserts(ops.length);
});
}
delete (pos, length = 1) {
if (typeof length !== "number") {
throw new Error("pos must be a number!");
}
if (typeof pos !== "number") {
throw new Error("pos must be a number!");
}
if (pos + length > this.idArray.length || pos < 0 || length < 0) {
throw new Error("The deletion range exceeds the range of the array!");
}
var eventHandler = this.eventHandler;
var newLeft = pos > 0 ? JSON.parse(this.idArray[pos - 1]) : null;
var dels = [];
for (var i = 0; i < length; i++) {
dels.push({
target: JSON.parse(this.idArray[pos + i]),
struct: "Delete"
});
}
eventHandler.awaitAndPrematurelyCall(dels);
this.os.requestTransaction(function*(){
yield* this.applyCreatedOperations(dels);
eventHandler.awaitedLastDeletes(dels.length, newLeft);
});
}
observe (f) {
this.eventHandler.addUserEventListener(f);
}
*_changed (transaction, op) {
if (op.struct === "Insert") {
var l = op.left;
var left;
while (l != null) {
left = yield* transaction.getOperation(l);
if (!left.deleted) {
break;
}
l = left.left;
}
op.left = l;
}
this.eventHandler.receivedOp(op);
}
}
Y.Array = function* _YArray(){
var model = {
start: null,
end: null,
struct: "List",
type: "Array",
id: this.store.getNextOpId()
};
yield* this.applyCreatedOperations([model]);
return yield* this.createType(model);
};
Y.Array.create = function* YArrayCreate(os, model){
var valArray = [];
var idArray = yield* Y.Struct.List.map.call(this, model, function(c){
valArray.push(c.content);
return JSON.stringify(c.id);
});
return new YArray(os, model.id, idArray, valArray);
};
})();