define(["util"], function (util) {
var ot = util.Module("ot");
var assert = util.assert;
var StringSet = util.Class({
constructor: function () {
this._items = {};
this._count = 0;
},
contains: function (k) {
assert(typeof k == "string");
return this._items.hasOwnProperty(k);
},
add: function (k) {
assert(typeof k == "string");
if (this.contains(k)) {
return;
}
this._items[k] = null;
this._count++;
},
remove: function (k) {
assert(typeof k == "string");
if (! this.contains(k)) {
return;
}
delete this._items[k];
this._count++;
},
isEmpty: function () {
return ! this._count;
}
});
var Queue = util.Class({
constructor: function (size) {
this._q = [];
this._size = size;
this._deleted = 0;
},
_trim: function () {
if (this._size) {
if (this._q.length > this._size) {
this._q.splice(0, this._q.length - this._size);
this._deleted += this._q.length - this._size;
}
}
},
push: function (item) {
this._q.push(item);
this._trim();
},
last: function () {
return this._q[this._q.length-1];
},
walkBack: function (callback, context) {
var result = true;
for (var i=this._q.length-1; i >= 0; i--) {
var item = this._q[i];
result = callback.call(context, item, i + this._deleted);
if (result === false) {
return result;
} else if (! result) {
result = true;
}
}
return result;
},
walkForward: function (index, callback, context) {
var result = true;
for (var i=index; i<this._q.length; i++) {
var item = this._q[i-this._deleted];
result = callback.call(context, item, i);
if (result === false) {
return result;
} else if (! result) {
result = true;
}
}
return result;
},
insert: function (index, item) {
this._q.splice(index-this._deleted, 0, item);
}
});
var Change = util.Class({
constructor: function (version, clientId, delta, known, outOfOrder) {
this.version = version;
this.clientId = clientId;
this.delta = delta;
this.known = known;
this.outOfOrder = !! outOfOrder;
assert(typeof version == "number" && typeof clientId == "string",
"Bad Change():", version, clientId);
},
toString: function () {
var s = "[Change " + this.version + "." + this.clientId + ": ";
s += this.delta + " ";
if (this.outOfOrder) {
s += "(out of order) ";
}
var cids = [];
for (var a in this.known) {
if (this.known.hasOwnProperty(a)) {
cids.push(a);
}
}
cids.sort();
s += "{";
if (! cids.length) {
s += "nothing known";
} else {
cids.forEach(function (a, index) {
if (index) {
s += ";";
}
s += a + ":" + this.known[a];
}, this);
}
return s + "}]";
},
clone: function () {
return Change(this.version, this.clientId, this.delta.clone(), util.extend(this.known), this.outOfOrder);
},
isBefore: function (otherChange) {
assert(otherChange !== this, "Tried to compare a change to itself", this);
return otherChange.version > this.version ||
(otherChange.version == this.version && otherChange.clientId > this.clientId);
},
knowsAboutAll: function (versions) {
for (var clientId in versions) {
if (! versions.hasOwnProperty(clientId)) {
continue;
}
if (! versions[clientId]) {
continue;
}
if ((! this.known[clientId]) || this.known[clientId] < versions[clientId]) {
return false;
}
}
return true;
},
knowsAboutChange: function (change) {
return change.clientId == this.clientId ||
(this.known[change.clientId] && this.known[change.clientId] >= change.version);
},
knowsAboutVersion: function (version, clientId) {
if ((! version) || clientId == this.clientId) {
return true;
}
return this.known[clientId] && this.known[clientId] >= version;
},
maybeMissingChanges: function (mostRecentVersion, clientId) {
if (! mostRecentVersion) {