Thursday, October 30, 2014

ShareJS 0.7.3 working example

I’m experimenting with ShareJS library, which is intended to allow live concurrent editing like in Google Docs. The demo on their website seems incredibly easy, even though later on the page they are so cruel: “ShareJS is mostly working, but it’s still a bit shit.”. I wouldn’t be so harsh as I was able to have it up and running in less than few hours. But the fact is it wasn’t as easy as it seemed.

It looks like the main problem with current state of ShareJS is what is pretty common in wild and uncontrolled open source world - lack of proper documentation. Here the problem is even worse. There are some docs and examples, but most of it is either incomplete or outdated. ShareJS.org website runs on ShareJS 0.5, while the most recent release is 0.7.3, with no backward compatibility between those releases. I think it will be less harmful if there was no examples at all - right now they are more misleading than helpful. It was a bit frustrating when even the shortest and simplest snippet from their website didn’t work, failing on non-existing functions being called.

Anyway, I was able to figure out what I need to change to have the simple demo running, both server- and client-side. Here it is, in case you have the same struggle, too.

On server-side, I’m running CoffeeScript WebSocket server, almost like in the original sample. I just needed few changes in order to have it running with Connect 3 - logging and static serving middlewares are no longer included in Connect out of the box, so I used morgan and serve-static, respectively. Here is the only changed part around Connect middlewares initialization:

app = connect()
app.use morgan()
app.use '/srv', serveStatic sharejs.scriptsDir
app.use serveStatic "#{__dirname}/app”

Go here for full Gist: ShareJS 0.7.3 server-side code.

I’m exposing client JavaScript libraries provided with ShareJS under /srv path and the client-facing web application files, physically located in /app on my filesystem, are exposed directly in the root path.

Client-side was a bit harder. Running the original code from the main ShareJS.org website wasn’t successful.

sharejs.open('blag', 'text', function(error, doc) {
  var elem = document.getElementById('pad');
  doc.attach_textarea(elem);
});

It tries to call sharejs.open function, which yields “TypeError: undefined is not a function” error for a simple reason - there is no longer “open” function on sharejs global variable. Fiddling around, I found an example that is using more verbose call like this:

var ws = new WebSocket('ws://127.0.0.1:7007');
var share = new sharejs.Connection(ws);
var doc = share.get('blag', 'doc');

if (!doc.type) {
    doc.create('text');
}
       
doc.whenReady(function () {
    var elem = document.getElementById('pad');
    doc.attachTextarea(elem);
});

Seemed legitimate and didn’t fail immediately, but I was getting "Operation was rejected (Document already exists). Trying to rollback change locally.” error message anytime except the first time. The code was calling doc.create('text') every time and that was clearly wrong, I should get doc.type pre-populated somehow. The solution is to subscribe to the document first and move checking the type and creating when needed to the function called after the document is ready - like this:

var ws = new WebSocket('ws://127.0.0.1:7007');
var share = new sharejs.Connection(ws);
var doc = share.get('blag', 'doc');
doc.subscribe();

doc.whenReady(function () {
    if (!doc.type) {
        doc.create('text');
    }

    var elem = document.getElementById('pad');
    doc.attachTextarea(elem);
});

See the full Gist: ShareJS 0.7.3 client-side code.

This post is cross-posted with my company blog.

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.