TogetherJS
The other day I started looking at TogetherJS. It's a JavaScript library developed by Mozilla which enables collaboration between users browsing the same website. Take a look at the demo they have recorded, it's really cool.

It uses WebSockets to connect to a server which will echo back messages to other connected browsers. This way you can chat with others and see what they do on a page. It synchronizes the different input fields of the page, tells you where other users go and if you want to follow them, etc... The default public server is hosted by Mozilla, which means they'll see everything that goes through it. But you can host your own server if you want to.

It also uses WebRTC which enables P2P communications between browsers. Right now it only works on Firefox, Chrome and Opera. It allows TogetherJS to support audio communication, so you can start a conversation with users browsing the same website that you do, providing you invited them.

I've recorded a quick demo of TogetherJS in Nuxeo to give you an idea:


And if you want to try it yourself, it's available on the Marketplace. You can take a look at the full code on Github.

In theory it's really easy to add TogetherJS to any website. Actually all you have to do is add this code to your page:

[xml]
<button onclick="TogetherJS(this); return false;">Start TogetherJS</button>
<script src="https://togetherjs.com/togetherjs-min.js"></script&gt;
[/xml]

This will load TogetherJS when you click on the 'Start TogetherJS' button. I said in theory because I had to do a couple of changes to it.

Any action you do on a page, like simply moving your mouse cursor, will trigger events that will be sent to the Hub. The Hub is the server hosted by Mozilla that relays all the messages to the other connected clients. This event contains the id of the DOM object where the event occurred. When the element cannot be identified by an id, the CSS selector is used (like ':nth-child(1)'). In Nuxeo, most of the DOM objects have an id also containing the colon character. This makes default TogetherJS behavior erratic to say the least, because it handles what's after the first colon like a CSS selector. I had to fix this and package it, making the deployment a little more complicated.

Here's what I did once I had my Nuxeo-compliant version of TogetherJS. First I took Nuxeo IDE and generated a new plugin project. There I created the src/main/resources/web/nuxeo.war/ folder. In this folder I put the togetherjs-min.js file and the associated togetherjs folder. This is what you get when building your own version. To make sure all of this gets deployed, I created a src/main/resources/OSGI-INF/deployment-fragment.xml file containing the following code:

[xml]
<?xml version="1.0"?>
<fragment version="1">

<require>org.nuxeo.ecm.platform.lang</require>
<require>org.nuxeo.ecm.webapp.core</require>
<require>org.nuxeo.ecm.webapp.ui</require>

<install>
<!-- unzip the war template -->
<unzip from="${bundle.fileName}" to="/" prefix="web">
<include>web/nuxeo.war/**</include>
</unzip>
</install>

</fragment>
[/xml]

Instead of adding a hard coded button like in the official example, I added an action. Notice that it's in the FOOTER category so it will be available anywhere in Nuxeo.

[xml]
<extension point="actions" target="org.nuxeo.ecm.platform.actions.ActionService">

<action accessKey="i" enabled="true" id="togetherJS" label=""
link="javascript:TogetherJS()" order="100" icon="/icons/togetherjs.png" type="bare_link">
<category>FOOTER</category>
<properties>
<property name="ajaxSupport">true</property>
<property name="labelStyleClass">footerLabel</property>
</properties>
</action>

</extension>
[/xml]

Eventually I added the script to the footer template. I did not go with the Theme solution as usual because TogetherJS infers the base URL. So it would not have worked with a URL like "/nuxeo/nxthemes-lib/prototype.js,togetherjs.js....".

Here's what I added at the end of the src/main/resources/web/nuxeo.war/incl/nuxeo_footer_template.xhtml file:
[xml]
<script src="#{contextPath}/togetherjs-min.js"></script>
[/xml]

And this is pretty much all I had to do. Now I can enjoy real-time collaboration and chat when editing a document on the intranet or simply browsing it.