recoder-code
Version:
Complete AI-powered development platform with ML model training, plugin registry, real-time collaboration, monitoring, infrastructure automation, and enterprise deployment capabilities
151 lines (124 loc) • 4.75 kB
JavaScript
var RemotePresence = require('./remote-presence');
var ot = require('../../ot');
module.exports = RemoteDocPresence;
function RemoteDocPresence(presence, presenceId) {
RemotePresence.call(this, presence, presenceId);
this.collection = this.presence.collection;
this.id = this.presence.id;
this.src = null;
this.presenceVersion = null;
this._doc = this.connection.get(this.collection, this.id);
this._emitter = this.connection._docPresenceEmitter;
this._pending = null;
this._opCache = null;
this._pendingSetPending = false;
this._opHandler = this._handleOp.bind(this);
this._createDelHandler = this._handleCreateDel.bind(this);
this._loadHandler = this._handleLoad.bind(this);
this._registerWithDoc();
}
RemoteDocPresence.prototype = Object.create(RemotePresence.prototype);
RemoteDocPresence.prototype.receiveUpdate = function(message) {
if (this._pending && message.pv < this._pending.pv) return;
this.src = message.src;
this._pending = message;
this._setPendingPresence();
};
RemoteDocPresence.prototype.destroy = function(callback) {
this._emitter.removeEventListener(this._doc, 'op', this._opHandler);
this._emitter.removeEventListener(this._doc, 'create', this._createDelHandler);
this._emitter.removeEventListener(this._doc, 'del', this._createDelHandler);
this._emitter.removeEventListener(this._doc, 'load', this._loadHandler);
RemotePresence.prototype.destroy.call(this, callback);
};
RemoteDocPresence.prototype._registerWithDoc = function() {
this._emitter.addEventListener(this._doc, 'op', this._opHandler);
this._emitter.addEventListener(this._doc, 'create', this._createDelHandler);
this._emitter.addEventListener(this._doc, 'del', this._createDelHandler);
this._emitter.addEventListener(this._doc, 'load', this._loadHandler);
};
RemoteDocPresence.prototype._setPendingPresence = function() {
if (this._pendingSetPending) return;
this._pendingSetPending = true;
var presence = this;
this._doc.whenNothingPending(function() {
presence._pendingSetPending = false;
if (!presence._pending) return;
if (presence._pending.pv < presence.presenceVersion) return presence._pending = null;
if (presence._pending.v > presence._doc.version) {
return presence._doc.fetch();
}
if (!presence._catchUpStalePresence()) return;
presence.value = presence._pending.p;
presence.presenceVersion = presence._pending.pv;
presence._pending = null;
presence.presence._updateRemotePresence(presence);
});
};
RemoteDocPresence.prototype._handleOp = function(op, source, connectionId) {
var isOwnOp = connectionId === this.src;
this._transformAgainstOp(op, isOwnOp);
this._cacheOp(op, isOwnOp);
this._setPendingPresence();
};
RemotePresence.prototype._handleCreateDel = function() {
this._cacheOp(null);
this._setPendingPresence();
};
RemotePresence.prototype._handleLoad = function() {
this.value = null;
this._pending = null;
this._opCache = null;
this.presence._updateRemotePresence(this);
};
RemoteDocPresence.prototype._transformAgainstOp = function(op, isOwnOp) {
if (!this.value) return;
try {
this.value = this._doc.type.transformPresence(this.value, op, isOwnOp);
} catch (error) {
return this.presence.emit('error', error);
}
this.presence._updateRemotePresence(this);
};
RemoteDocPresence.prototype._catchUpStalePresence = function() {
if (this._pending.v >= this._doc.version) return true;
if (!this._opCache) {
this._startCachingOps();
this._doc.fetch();
this.presence._requestRemotePresence();
return false;
}
while (this._opCache[this._pending.v]) {
var item = this._opCache[this._pending.v];
var op = item.op;
var isOwnOp = item.isOwnOp;
// We use a null op to signify a create or a delete operation. In both
// cases we just want to reset the presence (which doesn't make sense
// in a new document), so just set the presence to null.
if (op === null) {
this._pending.p = null;
this._pending.v++;
} else {
ot.transformPresence(this._pending, op, isOwnOp);
}
}
var hasCaughtUp = this._pending.v >= this._doc.version;
if (hasCaughtUp) {
this._stopCachingOps();
}
return hasCaughtUp;
};
RemoteDocPresence.prototype._startCachingOps = function() {
this._opCache = [];
};
RemoteDocPresence.prototype._stopCachingOps = function() {
this._opCache = null;
};
RemoteDocPresence.prototype._cacheOp = function(op, isOwnOp) {
if (this._opCache) {
op = op ? {op: op} : null;
// Subtract 1 from the current doc version, because an op with v3
// should be read as the op that takes a doc from v3 -> v4
this._opCache[this._doc.version - 1] = {op: op, isOwnOp: isOwnOp};
}
};