Alex MacCaw

Ruby/JavaScript developer & entrepreneur. O'Reilly writer and open source developer. Working for Stripe.


I'm @maccman on Twitter.

I'm maccman on Github.

I'm oldmanorhouse on Skype.

I'm here on Linked in.

I'm here on Delicious.


Current OS projects:
Books/Sites/App's
I've created/written:
Email me

I am now blogging at blog.alexmaccaw.com

Holla - An Asynchronous JavaScript App

October 24, 2010

I've been developing Holla for a book I'm writing on building JavaScript web applications. I've open sourced it in the hope that it'll be a good example application as it encompasses various best practices (that the book elaborates on).

Holla is a group chat application, a bit like Campfire. You can create channels, then invite friends to those channels, chat and share files.

Check out the live demo.

Holla

Asynchronous

The interface is totally asynchronous, which is one of the most important aspects of Holla. You can CRUD channels, add messages and upload files - all without blocking any of the UI. Any AJAX requests that need to happen, happen in the background. Asynchronous interfaces, I think, are the future of web apps, and what the book I'm writing centres on.

All channel editing is in place, and changes are reflected across the interface immediately. The request to the server is sent in the background.

Async interface

Just drag and drop files onto the interface, they'll upload in the background displaying a realtime progress bar. To do this, I'm using the new XHR file upload API - see jquery.upload.js. What's neat, is that everybody on the channel, not just yourself, can see the file upload progress.

Async uploads

Source management

For the fronted source management I've used Sprockets and Less. Sprockets basically adds 'require' support to JavaScript, is absolutely invaluable for managing JavaScript source files - JS apps can get messy very quickly if you're not careful. Less is very useful if you're dealing with any CSS3, as it provides shortcuts for the different vendor prefixes. For example, take a look at these:

.border-radius(@r: 3px) {
  -moz-border-radius: @r;
  -webkit-border-radius: @r;
  border-radius: @r;
}

/* Vertical Background Gradient */
.vbg-gradient(@fc: #FFF, @tc: #FFF) {
  background: @fc;
  background: -webkit-gradient(linear, left top, left bottom, from(@fc), to(@tc));
  background: -moz-linear-gradient(top, @fc, @tc);
}

Realtime

For the real-time chat, Holla uses Juggernaut 2. Juggernaut open a WebSocket connection with a Node.js backend server. If WebSockets aren't support, Juggernaut falls back to Comet, Polling and Flash Sockets. As well as allowing the interface to be updated in realtime, a socket connection with the server allows us to detect when the user is offline.

var offline = $("<div></div>")
    .html("The connection has been disconnected! <br /> " + 
          "Please go back online to use this service.")
    .dialog({
        autoOpen: false,
        modal:    true,
        width:    330,
        resizable: false,
        closeOnEscape: false,
        title: "Connection"
    });

jug.on("connect", function(){ 
  offline.dialog("close");
});

jug.on("disconnect", function(){ 
  offline.dialog("open");
});

Which looks like this:

Offline

Theme

The theme is inspired by jQuery Mobile's initial designs. They seemed to change the theme somewhat, in their released version, but I'm sticking to their original mockups - as I prefer them. Everything's coded in CSS3, there are very few images and it would be great if some of these styles got back into jQuery. Also, as you can see below, I haven't been very consistent with the pill shaped buttons - I feel they don't work for every situation.

Theme

Framework

The framework behind all of this is SuperApp. At its core, SuperApp is a state machine which splits up your JS logic into separate modules. It's poor practice (but often done) to write your whole application in a concurrent manner to one JavaScript file. SuperApp saves you from falling into this trap, as a state machine is a great way of programming modular UIs.

Admittedly SuperApp is poorly documented at the moment. This is a shortcoming I plan to rectify soon, but for now here's an example. Here we are creating a state, and adding event handlers that are triggered when the state is entered. Some of the variables, such as 'share' and 'input' are automatically populated from elements in the attached view.

(function($){

var state = App.state.add("channel")

state.setup(function(){ 
  this.share.submit(this.proxy(function(e){
    // ...
  }));

  this.input.keydown(function(e){
    // ...
  });  
});

state.beforeEnter(function(channel){
  if ( !channel ) throw "Null Channel";
  this.current = channel;
});

state.hasView = true;

})(jQuery);

//= require <states/channel.asset>
//= require <states/channel.activity>
//= require <states/channel.roster>

Preload

On startup, we preload any channels and messages, so users are never kept waiting behind a spinning loading indicator. Channels and messages are setup as models using SuperModel. This allows us to do some nifty things, like automatically updating the UI when a channel changes, or sending a AJAX request back to the server. SuperModel basically gives you the same API as ActiveModel or ActiveRecord.

var Channel = SuperModel.setup("Channel");
Channel.attributes = ["name"];

Channel.include(SuperModel.GUID);

So, that's a whirlwind tour of Holla. You'll find all the code on Github. I've substantially elaborated on all of this for the book I'm writing (which I'll be blogging more about soon). Any questions, just contact me.