summaryrefslogtreecommitdiff
path: root/lib/ot/editor-socketio-server.js
diff options
context:
space:
mode:
authorWu Cheng-Han2015-07-11 12:43:08 +0800
committerWu Cheng-Han2015-07-11 12:43:08 +0800
commit556338a9c6964d110c1351a402b425c71c2571fa (patch)
treed5b6d2071e554e65c7bfaa4f2c84ddb034598e01 /lib/ot/editor-socketio-server.js
parent4702b83adc35f384e214a2a6e9199d08e4494093 (diff)
Added support of operational transformation
Diffstat (limited to 'lib/ot/editor-socketio-server.js')
-rwxr-xr-xlib/ot/editor-socketio-server.js146
1 files changed, 146 insertions, 0 deletions
diff --git a/lib/ot/editor-socketio-server.js b/lib/ot/editor-socketio-server.js
new file mode 100755
index 00000000..aae156fc
--- /dev/null
+++ b/lib/ot/editor-socketio-server.js
@@ -0,0 +1,146 @@
+'use strict';
+
+var EventEmitter = require('events').EventEmitter;
+var TextOperation = require('./text-operation');
+var WrappedOperation = require('./wrapped-operation');
+var Server = require('./server');
+var Selection = require('./selection');
+var util = require('util');
+
+var LZString = require('lz-string');
+
+function EditorSocketIOServer(document, operations, docId, mayWrite) {
+ EventEmitter.call(this);
+ Server.call(this, document, operations);
+ this.users = {};
+ this.docId = docId;
+ this.mayWrite = mayWrite || function (_, cb) {
+ cb(true);
+ };
+}
+
+util.inherits(EditorSocketIOServer, Server);
+extend(EditorSocketIOServer.prototype, EventEmitter.prototype);
+
+function extend(target, source) {
+ for (var key in source) {
+ if (source.hasOwnProperty(key)) {
+ target[key] = source[key];
+ }
+ }
+}
+
+EditorSocketIOServer.prototype.addClient = function (socket) {
+ var self = this;
+ socket.join(this.docId);
+ var docOut = {
+ str: this.document,
+ revision: this.operations.length,
+ clients: this.users
+ };
+ socket.emit('doc', LZString.compressToUTF16(JSON.stringify(docOut)));
+ socket.on('operation', function (revision, operation, selection) {
+ operation = LZString.decompressFromUTF16(operation);
+ operation = JSON.parse(operation);
+ self.mayWrite(socket, function (mayWrite) {
+ if (!mayWrite) {
+ console.log("User doesn't have the right to edit.");
+ return;
+ }
+ self.onOperation(socket, revision, operation, selection);
+ });
+ });
+ socket.on('get_operations', function (base, head) {
+ self.onGetOperations(socket, base, head);
+ });
+ socket.on('selection', function (obj) {
+ self.mayWrite(socket, function (mayWrite) {
+ if (!mayWrite) {
+ console.log("User doesn't have the right to edit.");
+ return;
+ }
+ self.updateSelection(socket, obj && Selection.fromJSON(obj));
+ });
+ });
+ socket.on('disconnect', function () {
+ //console.log("Disconnect");
+ socket.leave(self.docId);
+ self.onDisconnect(socket);
+ /*
+ if (socket.manager && socket.manager.sockets.clients(self.docId).length === 0) {
+ self.emit('empty-room');
+ }
+ */
+ });
+};
+
+EditorSocketIOServer.prototype.onOperation = function (socket, revision, operation, selection) {
+ var wrapped;
+ try {
+ wrapped = new WrappedOperation(
+ TextOperation.fromJSON(operation),
+ selection && Selection.fromJSON(selection)
+ );
+ } catch (exc) {
+ console.error("Invalid operation received: " + exc);
+ return;
+ }
+
+ try {
+ var clientId = socket.id;
+ var wrappedPrime = this.receiveOperation(revision, wrapped);
+ //console.log("new operation: " + JSON.stringify(wrapped));
+ this.getClient(clientId).selection = wrappedPrime.meta;
+ revision = this.operations.length;
+ socket.emit('ack', revision);
+ socket.broadcast.in(this.docId).emit(
+ 'operation', clientId, revision,
+ wrappedPrime.wrapped.toJSON(), wrappedPrime.meta
+ );
+ this.isDirty = true;
+ } catch (exc) {
+ console.error(exc);
+ }
+};
+
+EditorSocketIOServer.prototype.onGetOperations = function (socket, base, head) {
+ var operations = this.operations.slice(base, head).map(function (op) {
+ return op.wrapped.toJSON();
+ });
+ operations = LZString.compressToUTF16(JSON.stringify(operations));
+ socket.emit('operations', head, operations);
+};
+
+EditorSocketIOServer.prototype.updateSelection = function (socket, selection) {
+ var clientId = socket.id;
+ if (selection) {
+ this.getClient(clientId).selection = selection;
+ } else {
+ delete this.getClient(clientId).selection;
+ }
+ socket.broadcast.to(this.docId).emit('selection', clientId, selection);
+};
+
+EditorSocketIOServer.prototype.setName = function (socket, name) {
+ var clientId = socket.id;
+ this.getClient(clientId).name = name;
+ socket.broadcast.to(this.docId).emit('set_name', clientId, name);
+};
+
+EditorSocketIOServer.prototype.setColor = function (socket, color) {
+ var clientId = socket.id;
+ this.getClient(clientId).color = color;
+ socket.broadcast.to(this.docId).emit('set_color', clientId, color);
+};
+
+EditorSocketIOServer.prototype.getClient = function (clientId) {
+ return this.users[clientId] || (this.users[clientId] = {});
+};
+
+EditorSocketIOServer.prototype.onDisconnect = function (socket) {
+ var clientId = socket.id;
+ delete this.users[clientId];
+ socket.broadcast.to(this.docId).emit('client_left', clientId);
+};
+
+module.exports = EditorSocketIOServer; \ No newline at end of file