178 lines
5.4 KiB
JavaScript
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);
|
|
};
|
|
})();
|