From ce05e8e8b0af2aa00b60d656126fc418d4b8fd9f Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Thu, 21 Apr 2011 16:13:04 -0700 Subject: [PATCH] Moving the ui updating in to the main loop to fix the bug that I mention in the TODO, and fix the client/server communication --- apply.js | 4 +- client.js | 108 ++++++++++++++++++++++++++---------------------------- 2 files changed, 54 insertions(+), 58 deletions(-) diff --git a/apply.js b/apply.js index 153822f..2bfcfa8 100644 --- a/apply.js +++ b/apply.js @@ -27,11 +27,11 @@ define(["./operations"], function (operations) { if ( doc.indexOf(operations.val(op[i])) !== 0 ) { throw new TypeError("Expected '" + operations.val(op[i]) + "' to delete, found '" + doc.slice(0, 10) - + "...'"); + + "'"); } else { doc = doc.slice(operations.val(op[i]).length); - break; } + break; default: throw new TypeError("Unknown operation: " + operations.type(op[i])); diff --git a/client.js b/client.js index 25bfcf9..b3328c6 100644 --- a/client.js +++ b/client.js @@ -29,7 +29,7 @@ define([ msg = outgoing[i]; xform(messages.operation(msg), ops, function (aPrime, bPrime) { messages.operation(msg, aPrime); - messages.document(msg, apply(messages.document(msg), aPrime)); + messages.document(msg, aPrime, apply(aPrime, messages.document(msg))); messages.revision(msg, messages.revision(msg)+1); ops = bPrime; }); @@ -49,7 +49,30 @@ define([ }); } - function init (outgoing, socket, ui, initialData) { + // Might need to start sending client id's back and forth. Don't really want + // to have to do a deep equality test on every check here. + function isOurOutgoing (msg, outgoing) { + var top = outgoing[0], + topOps = messages.operation(top), + msgOps = messages.operation(msg), + i = 0, + len = msgOps.length; + if ( messages.id(msg) !== messages.id(top) ) { + return false; + } + if ( messages.revision(msg) !== messages.revision(top) ) { + return false; + } + if ( len !== topOps.length ) { + return false; + } + if ( topOps.join() !== msgOps.join() ) { + return false; + } + return true; + } + + function init (outgoing, incoming, socket, ui, initialData) { var previousDoc = messages.document(initialData), previousRevision = messages.revision(initialData), id = messages.id(initialData); @@ -60,6 +83,7 @@ define([ var msg, oldOutgoingLength = outgoing.length, uiDoc = ui.getDocument(); + if ( uiDoc !== previousDoc ) { msg = {}; messages.operation(msg, operations.operation(previousDoc, uiDoc)); @@ -69,46 +93,42 @@ define([ outgoing.push(msg); previousDoc = uiDoc; + } - if ( oldOutgoingLength === 0 ) { - socket.send({ - type: "update", - data: outgoing[0] - }); + while ( (msg = incoming.shift()) ) { + if ( outgoing.length && isOurOutgoing(msg, outgoing) ) { + outgoing.shift(); + } else { + // TODO: need to handle cursor selection and index + xformEach(outgoing, messages.operation(msg)); + previousRevision++; + + // TODO: cursor position + if ( outgoing.length ) { + ui.update(previousDoc = messages.document(outgoing[outgoing.length-1])); + } else { + ui.update(previousDoc = messages.document(msg)); + } } } + + if ( outgoing.length ) { + socket.send({ + type: "update", + data: outgoing[0] + }); + } + setTimeout(loop, 1000); } setTimeout(loop, 10); } - // Might need to start sending client id's back and forth. Don't really want - // to have to do a deep equality test on every check here. - function isOurOutgoing (msg, outgoing) { - var top = outgoing[0], - topOps = messages.operation(top), - msgOps = messages.operation(msg), - i = 0, - len = msgOps.length; - if ( messages.id(msg) !== messages.id(top) ) { - return false; - } - if ( messages.revision(msg) !== messages.revision(top) ) { - return false; - } - if ( len !== topOps.length ) { - return false; - } - if ( topOps.join() !== msgOps.join() ) { - return false; - } - return true; - } - return { OTDocument: function (opts) { var outgoing = [], + incoming = [], socket = opts.socket || error("socket is required"), ui = opts.ui || error("ui is required"), docId = opts.id, @@ -116,43 +136,19 @@ define([ connect(socket, docId); - // TODO: what happens if we receive an update message from the - // server and we update the client and they lose what they were just - // typeing because we havent saved the operations in the outgoing - // buffer yet? Do I need to merge the main loop and handling of - // socket messages? Maybe just have the socket's receive handler - // queue events in an inbox and have the main loop process one - // change that the client is creating, then one message from the - // inbox. - socket.onMessage(function (event) { - var msg; - switch ( event.type ) { case "connect": if ( ! initialized ) { - init(outgoing, socket, ui, event.data); + init(outgoing, incoming, socket, ui, event.data); } else { error("Already initialized"); } break; case "update": - msg = event.data; - if ( outgoing.length && isOurOutgoing(msg, outgoing) ) { - outgoing.shift(); - } else { - // TODO: need to handle cursor selection and index - xformEach(outgoing, messages.operation(msg)); - - // TODO: cursor position - if ( outgoing.length ) { - ui.update(messages.document(outgoing[outgoing.length-1])); - } else { - ui.update(messages.document(msg)); - } - } + incoming.push(event.data); break; default: