Automating Light Switches with Node.js

I was originally planning on creating a roguelike for my fun Christmas project, but I decided to create an app to control the lights in my house instead. Home Automation, it’s the future! ūüôā


The app can turn lights on and off, dim to a percentage value, and update UI values in realtime.

If you plan to do something similar, you’ll need a Fibaro Dimmer Module in each light switch, a (local!) server running Node.js, a Z-Wave controller (mine is an Aeotec Z-Stick), the Open Z-Wave library and Socket.IO.

If enough people are interested I’d be happy to write a tutorial, but I think it’s a pretty niche topic – I’d hate to be writing for an audience of one. Let me know what you think in the comments below or hit me up on Twitter.

Twitter Sentiment Analysis with Node.js

Hey folks! This tutorial will demonstrate how to use Node.js – together with Twit (a Twitter API client) and Sentiment (a sentiment analysis library) – to conduct sentiment analysis on the Twitter zeitgeist.

Why? Because it’s interesting! Twitter – despite it’s slow descent into a toxic cesspit – is how you find out what’s happening right now; it represents a global stream of consciousness by way of millions of one-hundred-and-forty character long text snippets.

Using the Twitter API, we can query the Twitter hosepipe for material relevant to our interests. We can then aggregate and interrogate the results and, with the aid of natural language processing tools, divine how people generally feel about something. “Something” could be an event, a product, a brand – anything really. Suffice it to say, marketers love this stuff.

I love indie games, and I’m based in Guildford. Being an avid sci-fi fan, No Man’s Sky is definitely on my radar. So I’m going to use sentiment analysis to find out how people feel about procedural generation in computer games. Let’s get started!


Firstly, you’ll need to create an app in Twitter. This will give you the credentials you need to access and query the Twitter API. Log in to Twitter Application Management, and click the button entitled “Create New App”.

Twitter Application Management Page - Create a New Application
Twitter Application Management Page – Create a New Application

Once you’re all set up, you’ll be directed to your app’s credentials page. You’ll be needing these later.

Using Terminal, create a new folder and create a new JS file in it.

mkdir twitter-app && cd twitter-app
touch app.js

Install Twit and Sentiment;

npm install twit sentiment

Now let’s edit the JS file.

subl app.js

Require Twit and Sentiment, and set up your access credentials.

var twit = require('twit'),
    sentiment = require('sentiment');

var t = new twit({
  consumer_key : 'YOUR_CONSUMER_KEY',
  consumer_secret : 'YOUR_CONSUMER_SECRET',
  access_token : 'YOUR_ACCESS_TOKEN',
  access_token_secret : 'YOUR_ACCESS_TOKEN_SECRET'
});

Okay, now we can query the Twitter API! Let’s search for the phrase (a phrase is indicated by double quotes) “procedural generation.” (N.B: The Twitter Search API only extends to around the past seven days.)

t.get('search/tweets', { q : '"procedural generation" since:2015-10-08', count: 100 }, function(err, data, response) {
  console.log(data);
});

If you run the app – 

node app.js

– you’ll get a JSON object back containing the data for 100 tweets. This is the sanitised output for -one- of those tweets.

{ statuses: 
   [ { created_at: 'Thur Oct 15 14:06:44 +0000 2015',
       id: 000000000000000000,
       id_str: '000000000000000000',
       text: 'text',
       truncated: false,
       entities: [Object],
       metadata: [Object],
       source: '<a href="#" rel="nofollow">@Twitter</a>',
       in_reply_to_status_id: null,
       in_reply_to_status_id_str: null,
       in_reply_to_user_id: null,
       in_reply_to_user_id_str: null,
       in_reply_to_screen_name: null,
       user: [Object],
       geo: null,
       coordinates: null,
       place: null,
       contributors: null,
       retweeted_status: [Object],
       is_quote_status: false,
       retweet_count: 10,
       favorite_count: 0,
       favorited: false,
       retweeted: false,
       possibly_sensitive: false,
       lang: 'en' } ],
  search_metadata: 
   { completed_in: 0.045,
     max_id: 000000000000000000,
     max_id_str: '000000000000000000',
     next_results: '?max_id=000000000000000000&q=%22procedural+generation%22&count=100&include_entities=1',
     query: '%22procedural+generation%22',
     refresh_url: '?since_id=000000000000000000&q=%22procedural+generation%22&include_entities=1',
     count: 100,
     since_id: 0,
     since_id_str: '0' } 
}

“Hosepipe” sounds about right. Let’s whittle this down a bit.

t.get('search/tweets', { q : '"procedural generation" since:2015-10-08', count: 100 }, function(err, data, response) {
  for (var i in data.statuses) {
    if (data.statuses[i].lang === 'en') {
      var s = sentiment(data.statuses[i].text);
      console.log(s.score + ' ' + data.statuses[i].text);
    }
  }
});

This will return the text of 100 tweets, prepended with a sentiment score. A negative score indicates a negative sentiment, a positive one indicates a positive sentiment.


Update July 2017

When I originally wrote this tutorial, the Sentiment was still in it’s infancy (the developers hadn’t yet written the code to handle negation in statements) so I figured I’d re-write this section to reflect the changes. I’m grateful (as always) to FOSS contributors that make libraries like Sentiment happen.

Let’s run the query above again, removing the date modifier, limiting the number results to 10, and filtering out retweets and replies (which can often be taken out of context).

t.get('search/tweets', { q : '"procedural generation" -filter:retweets -filter:replies', count: 100 }, function(err, data, response) {
  for (var i in data.statuses) {
    if (data.statuses[i].lang === 'en') {
      var s = sentiment(data.statuses[i].text);
      console.log(s.score + ' ' + data.statuses[i].text);
    }
  }
});

This is what we get back:

4 like take the procedural generation shit and work on it, when it's good make a good game out of it
3 This GDC talk gives a really good overview on procedural generation ūüôā https://t.co/y16em13WcV
-4 Screw the procedural generation, the core problem here is the none-orbiting planets. You don't know my pain. #NoMansSky
0 Almost there with the procedural generation... #GameDev #IndieDev https://t.co/nXekRxYYgx
-4 Sure, it has procedural generation, I'm fighting back tears because of the shallow gameplay. I'm calling Anonymous. #NoMansSky
0 Got my copy of https://t.co/gWrr7vLPPA today. Should keep me busy for a while …
-6 Ugh... finally got the core of this procedural generation algorithm of mine working... holy shit wasn't that exhausting...
0 GameMaker Studio 2 Action RPG Tutorial - Part 1 - Procedural Generation: https://t.co/1RxY4rws3f via @YouTube
0 I added a video to a @YouTube playlist https://t.co/1RxY4rws3f GameMaker Studio 2 Action RPG Tutorial - Part 1 - Procedural Generation
3 The procedural generation in Dwarf Fortress is really darn impressive. https://t.co/Lz8EWQSLhb

Remember, the first number in each line is the sentiment score; so line 2 reveals a positive sentiment about a GDC talk, and line 3 reveals a negative sentiment regarding procedural generation in No Man’s Sky.

Now, if you’ve been paying attention, you’ll have noticed a problem with my original choice of search phrase. “Procedural generation” is a bit of a broad concept, and we can’t yet determine context from a tweet. Do I mean procedural generation pertaining to a specific game? A specific algorithm? A tool in the Unity Asset Store? A GDC talk? I *could* use a ton of filters to pare down the results – but I think, for now, it’s better to limit searches to proper nouns. Events, brands, products – concrete “things”. Let’s try something else.

I’m trying to keep things positive here, so instead of looking at No Man’s Sky, I’m going to examine sentiment regarding my newest favourite indie game, Caves of Qud:

0 Average Gunslinger On The Roll in Caves of Qud Weekly Run #21 Part 1: https://t.co/ZLdFVZmyfB via @YouTube
3 I think today is going to be a nice chill day with some Caves of Qud and some RimWorld ūüėÄ
-4 the real caves of qud lore question is where are these cannibals finding all these fucking missile launchers
0 When I get addicted in to a game I get really in to them. 96 hours in Caves of Qud. But I think I'm burned out on it now.
0 my dude @Craigoryham is making some VERY spooky tracks for Caves of Qud https://t.co/ZNas6x1Htg
0 I can think of a billion other things I'd rather be doing rn than cleaning this basement.... all of them just happen to be Caves of Qud...
1 caves of qud status juicing cannibal gary: don't shoot a missile in a tiny room juicing cannibal steve: why not
1 Caves of qud is. Oh god not my contacts.
4 Had a lot of fun with Caves of Qud tonight. Definitely need to do more with a beguile build.
5 Caves of Qud is fascinating but so long and with so little narrative variation that I think this'll let me enjoy it… https://t.co/edy2OwVuVI

(Caves of Qud is Dune-inspired roguelike awesomeness. Seriously, go buy it.)

Ten tweets is far too small a sample to be statistically significant. Let’s average the sentiment score over the past week.

t.get('search/tweets', { q : '"Caves of Qud" -filter:retweets -filter:replies since:2017-07-04 until:2017-07-11', count: 100 }, function(err, data, response) {
  var aggregate = 0,
      numScores = data.statuses.length;

  for (var i in data.statuses) {
    if (data.statuses[i].lang === 'en') {
      var s = sentiment(data.statuses[i].text);
      aggregate += s.score;

      // remove any neutral scores - they bring down the average
      if (s.score === 0) numScores -= 1;
    }
  }

  console.log(aggregate / numScores);
});

This returns a score of 1.173913043478261 (remember, any score greater than zero reveals a positive sentiment). So we can say that, over the past week, Twitter users have been generally favourable in their discussion of Caves of Qud. Let’s try another – how is Stardew Valley doing this week? Computer says 0.6052631578947368 – so still generally positive, but not as much so as Caves of Qud. And No Man’s Sky? -0.2625 ūüôĀ

It’s not 100% accurate, as there are a lot of edge cases, and the tools are still being developed. But hopefully you can see how it’s useful to determine sentiment concerning “a thing.” Do you have a product that you update on a regular basis? Now you can gauge whether your latest update was a hit or a miss. Sentiment can be applied to any text – not just tweets. You could use it ascertain average sentiment on user reviews or customer surveys. Why not take my example and extend it? I’d love to see someone do some data visualisation (using D3.js?) with this. Just treat it as a jumping off point. Let me know what you make of it in the comments or on Twitter.


Note: The Twitter Search API has also seen some updates since my original post – it now supports filtering by positive and negative sentiment.


So whilst you can’t use it (without some wrangling) to get an average, you could use it to reveal (and hopefully recify) negative user sentiment about your product. Or perhaps you’ve just shipped a unicorn and want to bask in adulation? :] Perhaps Twitter should just apply this filter globally – though I’m not sure the world needs rose-tinted goggles right now.

Attribution: Google Images can’t find the original creator of the header image used for this post. The closest I could find is from @bskipper27. If you created this image, please let me know so I can correctly attribute your work. Thanks!

Web Audio API Tutorial

Welcome to this tutorial on the Web Audio API. I’ll show you how to load and play sounds, how to adjust the volume, how to loop sounds, and how to crossfade tracks in and out – everything you need to get started implementing audio into your HTML5 games.

Web Audio – a brief history

Before the release of the Web Audio API in 2011, the only cross-platform way of playing audio in the browser (without using Flash) was with the <audio> element. The <audio> element has a very basic feature set – there isn’t much to it beyond loading, playing, pausing and stopping a single track.

For the game developers taking advantage of the new and improved graphics APIs (WebGL, Canvas), audio support – or lack thereof – was a source of constant frustration. As graphics advanced, the paucity of the <audio> feature set became more pronounced. Worse still, <audio> was plagued by bugs across the different browser implementations, thwarting developers’ attempts to use the API for even the most basic of it’s intended purposes.

Ingenious hacks had to be devised – the ‚Äėaudio sprite‚Äô* was invented simply to get audio to work correctly in iOS. Developers clamoured for a better audio API to complement the rich, engaging visual experiences they were creating with the far-superior graphics APIs.

Enter, the Web Audio API.

The Web Audio API

The Web Audio API enables developers to create vibrant, immersive audio experiences in the browser. It provides a high-level abstraction for manipulating and controlling audio.

The API has a node-based architecture: a sound can be routed through several different types of nodes before reaching it’s end-point. Each node has it’s own unique purpose; there are nodes for generating, modifying, analysing and outputting sounds.

Where is it supported?

web-audio-browser-support
The Web Audio API is currently supported in all good browsers.

Test for API Support

Before you can load a sound, you first need to check whether the API is supported in your target browser. This snippet of code attempts to create an AudioContext.

var context;

try {
  // still needed for Safari
  window.AudioContext = window.AudioContext || window.webkitAudioContext;

  // create an AudioContext
  context = new AudioContext();
} catch(e) {
  // API not supported
  throw new Error('Web Audio API not supported.');
}

View demo

If the test fails, you have one of three options: a) ignore it (user gets no audio); b) use an <audio> sprite; c) use a Flash fallback. Personally, I’m in favour of option a) – as mentioned in the footnotes, audio sprites are a real pain in the backside to create, and Flash is a no-go in HTML5 mobile games.

Load a sound

Next, we’ll load a sound. The binary audio data is loaded into an ArrayBuffer via Ajax. In the onload callback, it’s decoded using the AudioContext’s decodeAudioData method. The decoded audio is then assigned to our sound variable.

var sound;

/**
 * Example 1: Load a sound
 * @param {String} src Url of the sound to be loaded.
 */

function loadSound(url) {
  var request = new XMLHttpRequest();
  request.open('GET', url, true);
  request.responseType = 'arraybuffer';

  request.onload = function() {
    // request.response is encoded... so decode it now
    context.decodeAudioData(request.response, function(buffer) {
      sound = buffer;
    }, function(err) {
      throw new Error(err);
    });
  }

  request.send();
}
// loadSound('audio/BaseUnderAttack.mp3');

View demo

Testing file format support

To ensure that our audio is playable wherever the Web Audio API is supported, we’ll need to provide the browser with two variants of our audio source, in MP3 and Ogg format. This code snippet checks whether the browser can play Ogg format audio, and helps us to fall back to the MP3 where it’s not supported.

var format = '.' + (new Audio().canPlayType('audio/ogg') !== '' ? 'ogg' : 'mp3');
// loadSound('audio/baseUnderAttack' + format);

View demo

Play a sound

To play a sound, we need to take the AudioBuffer containing our sound, and use it to create an AudioBufferSourceNode. We then connect the AudioBufferSourceNode to the AudioContext’s destination and call the start() method to play it.

/**
 * Example 2: Play a sound
 * @param {Object} buffer AudioBuffer object - a loaded sound.
 */

function playSound(buffer) {
  var source = context.createBufferSource();
  source.buffer = buffer;
  source.connect(context.destination);
  source.start(0);
}
// playSound(sound);

View demo

Load multiple sounds

To load more than one sound, reference your sounds in a format that can be iterated over (like an array or an object).

var sounds = {
  laser : {
    src : 'audio/laser'
  },
  coin : {
    src : 'audio/coin'
  },
  explosion : {
    src : 'audio/explosion'
  }
};


/**
 * Example 3a: Modify loadSound fn to accept changed params
 * @param {Object} obj Object containing url of sound to be loaded.
 */

function loadSoundObj(obj) {
  var request = new XMLHttpRequest();
  request.open('GET', obj.src + format, true);
  request.responseType = 'arraybuffer';

  request.onload = function() {
    // request.response is encoded... so decode it now
    context.decodeAudioData(request.response, function(buffer) {
      obj.buffer = buffer;
    }, function(err) {
      throw new Error(err);
    });
  }

  request.send();
}
// loadSoundObj({ src : 'audio/baseUnderAttack' });


/**
 * Example 3b: Function to loop through and load all sounds
 * @param {Object} obj List of sounds to loop through.
 */

function loadSounds(obj) {
  var len = obj.length, i;

  // iterate over sounds obj
  for (i in obj) {
    if (obj.hasOwnProperty(i)) {
      // load sound
      loadSoundObj(obj[i]);
    }
  }
}
// loadSounds(sounds);

View demo

Adjusting the volume

In the ‘play’ example, we created an AudioBufferSourceNode, and then connected it to a destination. To change the volume of an audio source, we need to create an AudioBufferSourceNode as before, but then we create a GainNode, and connect the AudioBufferSourceNode to that, before connecting the GainNode to the destination. Then we can use the GainNode to alter the volume.

sounds = {
  laser : {
    src : 'audio/laser',
    volume : 2
  },
  coin : {
    src : 'audio/coin',
    volume : 1.5
  },
  explosion : {
    src : 'audio/explosion',
    volume : 0.5
  }
};


/**
 * Example 4: Modify the playSoundObj function to accept volume property
 * @param {Object} obj Object containing url of sound to be loaded.
 */

function playSoundObj(obj) {
  var source = context.createBufferSource();
  source.buffer = obj.buffer;

  // create a gain node
  obj.gainNode = context.createGain();

  // connect the source to the gain node
  source.connect(obj.gainNode);

  // set the gain (volume)
  obj.gainNode.gain.value = obj.volume;

  // connect gain node to destination
  obj.gainNode.connect(context.destination);

  // play sound
  source.start(0);
}
// loadSounds(sounds);

View demo

Muting a sound

To mute a sound, we simply need to set the value of the gain on the GainNode to zero.

var nyan = {
  src : 'audio/nyan',
  volume : 1
};
loadSoundObj(nyan);


/**
 * Example 5: Muting a sound
 * @param  {object} obj Object containing a loaded sound buffer.
 */

function muteSoundObj(obj) {
  obj.gainNode.gain.value = 0;
}
// muteSoundObj(nyan);

View demo

Looping sounds

Whenever you’re creating any game, you should always be mindful of optimising file sizes. There’s no point making your player download a 10Mb audio file, when the same effect is achievable with 0.5Mb of looped audio. This is especially the case if you’re creating games for HTML5 mobile game portals.

To create a looping sound, set the loop attribute of the AudioBufferSourceNode’s to true just before connecting it to the GainNode.

sounds = {
  laser : {
    src : 'audio/laser',
    volume : 1,
    loop: true
  },
  coin : {
    src : 'audio/coin',
    volume : 1,
    loop: true
  },
  explosion : {
    src : 'audio/explosion',
    volume : 1,
    loop: true
  }
};


/**
 * Example 6: Modify the playSoundObj function again to accept a loop property
 * @param {Object} obj Object containing url of sound to be loaded.
 */

function playSoundObj(obj) {
  var source = context.createBufferSource();
  source.buffer = obj.buffer;

  // loop the audio?
  source.loop = obj.loop;

  // create a gain node
  obj.gainNode = context.createGain();

  // connect the source to the gain node
  source.connect(obj.gainNode);

  // set the gain (volume)
  obj.gainNode.gain.value = obj.volume;

  // connect gain node to destination
  obj.gainNode.connect(context.destination);

  // play sound
  source.start(0);
}
// loadSounds(sounds);

View demo

Crossfading sounds

Making use of multiple audio tracks is a great way to aurally demarcate the different areas of your game. In Brickout, for example, I crossfade between the title music and the game music when the game starts, and back again when it ends.

To crossfade between two tracks, you’ll need to schedule a transition between the gain volume ‘now’ and a time fixed in the future (e.g. ‘now’ plus 3 seconds). ‘Now’ in Web Audio terms is the AudioContext’s currentTime property – the time that has elapsed since the AudioContext was created.

var crossfade = {
  battle : {
    src : 'audio/the-last-encounter',
    volume : 1,
    loop : true
  },
  eclipse : {
    src : 'audio/red-eclipse',
    volume : 0,
    loop : true
  }
};


/**
 * Example 7: Crossfading between two sounds
 * @param  {Object} a Sound object to fade out.
 * @param  {Object} b Sound object to fade in.
 */

function crossFadeSounds(a, b) {
  var currentTime = context.currentTime,
      fadeTime = 3; // 3 seconds fade time

  // fade out
  a.gainNode.gain.linearRampToValueAtTime(1, currentTime);
  a.gainNode.gain.linearRampToValueAtTime(0, currentTime + fadeTime);

  // fade in
  b.gainNode.gain.linearRampToValueAtTime(0, currentTime);
  b.gainNode.gain.linearRampToValueAtTime(1, currentTime + fadeTime);
}
// crossFadeSounds(crossfade.battle, crossfade.eclipse);

View demo

LinearRampToValueAtTime has a catch that isn’t immediately apparent – if you try to change the gain after using it, nothing will happen. You need to cancel any scheduled effects you’ve applied before you can set the gain, even if your schedule has long since expired. You can do this with the cancelScheduledValues() method.

Final Tip

If you’re struggling to get a sense of what each of the vast array of audio nodes does, head over to the spec. Under each node’s subheading you’ll find the following:

numberOfInputs: n
numberOfOutputs: n

From this you can get a rough idea of what the node does. If the node has no inputs and one output, it will load or synthesise audio. If it has one input and n outputs, an audio source can be connected to it and modified or analysed in some respect. If the node has inputs but no output, it will be an end-point – the final destination that connects your audio to your user’s headphones or speakers.

Further Reading

https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API

*The process of creating an audio sprite was painstaking – all of your game’s audio had to be composited into one file, and once loaded the ‘playhead’ had to be jogged back and forth between each of the ‘sprites’. This workaround still had it’s downsides – sounds were often clipped if a new sound was triggered before the previous sound had finished playing.

Migrating to Express.js v3

I’ve been working on a Node.js web application for a couple of months now. ¬†A few days ago I ran npm update¬†on my project without really thinking about it (d’oh), and my Express.js install was upgraded to v3. ¬†There are a lot of changes in v3, and it took the best part of a day for me to work through them all. ¬†I’ve outlined some of the issues I encountered below. Some of them aren’t particularly easy to find fixes for, so hopefully this will help someone out.

Getting started

Your first port of call should be the migration guide:
https://github.com/visionmedia/express/wiki/Migrating-from-2.x-to-3.x. ¬†There is also a wiki article on the new features (https://github.com/visionmedia/express/wiki/New-features-in-3.x), but there’s no need to read through this until you’ve got everything fixed and working again.

‘Layout’ removed

The first thing that will be immediately apparent is that none of your views are rendering properly.  The default way to render views in Express v2.x was to have a master layout template that all other views inherited from. If you were using Jade as your template engine, a layout template used to look something like this:

!!! 5
html
  head
    title Title
    link(rel='stylesheet', '/style.css')
  body!= body

This template would be applied to all other views – all other views inheriting from this template would be rendered as != body.

Both ‘layout’ and partials have been removed in Express v3, so when you first load your site or application after the upgrade, you’ll see that only the body of your view template is being rendered to the page. ¬†The fix for this is dependent upon which template engine you use. ¬†I’m using Jade, so I’ve outlined the fix below, but if you’re using EJS or similar, you’ll need to look it up.

Layouts in Jade

In the latest version of Jade, the way in which views are rendered has been ‘reversed’ as it were – rather than views inheriting from layouts, a view will specify the layout it wants to extend. This gives you greater flexibility with your views, as you can have different layouts for different purposes.

To get your views to render again, you’ll firstly need to go through all your views and specify the layout you want to extend at the top of each template file, using the keyword¬†extends. The path to layout is relative, so if your views are nested in folders, don’t forget¬†../. ¬†One thing I haven’t figured out yet is if you can set a fixed path to layout, so that any deeply nested views can inherit from extends layout rather than extends ../../../layout. Answers on a postcard please :p

The second thing you’ll have to do is designate blocks in your layout – chunks of code to be replaced with the content in the view template:

// layout.jade
!!! 5
html
  head
    title Title
    link(rel='stylesheet', '/style.css')
  body
    block content
    block scripts

 

// view.jade
extends layout

block content
  h1 Content
  p Some content here 
block scripts
  script(src='/js/lib/jquery.min.js')

This should fix your view rendering problems.

req.flash removed

Req.flash was removed in favour of attaching messages to req.session.messages. ¬†I’ve made extensive use of req.flash in my application, so it would have taken me a long time to rewrite all my message code. ¬†Luckily, the migration guide points us towards¬†connect-flash, a middleware that implements req.flash in Express v3 – very handy if you don’t want to do a big refactor.

Dynamic helpers

Dynamic helpers have been removed in favour of app.locals.use(callback).  Now, instead of assigning view helpers to an object, you assign them directly to res.locals:

Express v2.x

app.dynamicHelpers({
  session: function(req, res){
    return req.session;
  }
});

Express v.3

app.locals.use(function(req, res) {
  res.locals.session = req.session;
});

Socket.io not serving client

There are a number of reasons why socket.io might not be serving the client, and just as many proffered solutions on stackoverflow to try.  After a bit of digging, I discovered that the bug in my application was related an issue with Connect:
https://github.com/senchalabs/connect/issues/500#issuecomment-4620773

Long story short, you have to call app.listen() before¬†io.listen() – this wasn’t the case before. It’s annoying how the simplest bugs can sometimes take the longest time to track down ūüôā

Socket.io and sessions

In my application, socket.io has access to session data. ¬†When I was initially developing this functionality, I leaned heavily on the code from¬†Daniel Baulig’s excellent blog post on passing session information to socket.io via the handshake/authorisation method (http://www.danielbaulig.de/socket-ioexpress/). ¬†As Daniel has now noted at the top of his guide, it requires a few tweaks to work with Express v3.

The first thing that you’ll need to change is where you put your application’s session secret. Formerly, you specified this in express.session – you’ll need to move it to reside in express.cookieparser:

app.configure(function() {
  app.use(express.cookieParser('somesuperspecialsecrethere'));
  ...
});

If you make the above change and run your application, you’ll notice that the session cookie is now being transmitted, but we get another 500 error instead. If we take a look at Daniel’s code:

sio.set('authorization', function (data, accept) {
    if (data.headers.cookie) {
        data.cookie = parseCookie(data.headers.cookie);
        data.sessionID = data.cookie['express.sid'];
        // save the session store to the data object
        // (as required by the Session constructor)
        data.sessionStore = sessionStore;
        sessionStore.get(data.sessionID, function (err, session) {
            if (err || !session) {
                accept('Error', false);
            } else {
                // create a session object, passing data as request and our
                // just acquired session data
                data.session = new Session(data, session);
                accept(null, true);
            }
        });
    } else {
       return accept('No cookie transmitted.', false);
    }
});

With a bit of debugging, you’ll find that the line that starts sessionStore.get¬†is failing and throwing an error on the next line. ¬†This is because the way that cookie signing is implemented in Connect has changed, and sessionStore.get¬†is being passed the long session id from the cookie, rather than the short (24 character) session id it’s expecting (for more information see this thread). ¬†In order to get the short session id, you need to split the long session id at the dot:

data.sessionID = data.cookie['express.sid'].split('.')[0];

Data.sessionID should now contain the short SID Рthis will be evaluated against the SID contained in the sessionStore as normal.  No more pesky 500 errors!

If you have any comments or suggestions, please feel free to comment below.  Thanks!

Book Review: MongoDB in Action

Happy New Year everyone! ¬†I’ve been a bit quiet lately, owing to the holidays and work obligations, but I’m going to make a concerted effort this year to be a bit more active on my blog. ¬†I don’t, however, count writing skills amongst my talents – I can do tutorials, and how-to’s, but I don’t think I’ll ever be able to write informative,¬†expository articles like some members of the JavaScript community do. ¬†I’d rather get my own hands dirty in the code than write about it!

So, in lieu of anything particularly insightful or original, I’ve decided to write some book reviews. ¬†I spend a fortune on books, half of which I never get around to reading. ¬†I’m hoping that creating a self imposed obligation to write book reviews will help me clear the backlog, and that the reviews might be useful to anyone thinking about making a purchase. And if they’re not, well, at least my blog gets some SEO fodder.

Enjoy ūüėČ

Book Review: MongoDB in Action

Author:

Kyle Banker

Publisher:

Manning

Publisher Summary:

MongoDB in Action is a comprehensive guide to MongoDB for application developers. The book begins by explaining what makes MongoDB unique and describing its ideal use cases. A series of tutorials designed for MongoDB mastery then leads into detailed examples for leveraging MongoDB in e-commerce, social networking, analytics, and other common applications.

Review:

I was tinkering with a Node.js application over Christmas, and arrived at a point where I needed to implement a database into my app.  MongoDB seems to be the DBMS of choice for Node developers, so I bought MongoDB in Action to learn a bit more about it.

I wanted a book that would get me through the introductory content fast, and would enable me to be productive in as short a time as possible.  Thankfully, the author had similar goals in mind when writing the book, and split the book up into three parts: Getting Started; Application Development in MongoDB; and MongoDB Mastery.

Part One begins by discussing the origins, history and evolution of the project; and then goes on to discuss why you would choose to use MongoDB over, for example, a key/value store, or SQL. ¬†This is followed by an introduction to the JavaScript shell and some of the core CRUD (create, read, update, delete) operations. ¬†The explanation of the core concepts is accompanied by the development of a simple application with Ruby. ¬†I personally don’t know a lot of Ruby, but the examples were easy to follow, and helped me understand how a similar implementation would work in Node.

Part Two delves deeper into MongoDB’s document data model and query language. ¬†The author explains how to design, create and manipulate documents and collections using the JavaScript shell, and then illustrates how you would implement this in practice by building an e-commerce application. ¬†The example application is quite extensive – it covers¬†products, product categories, users, orders and product reviews. ¬†Any potential issues are highlighted, and any workarounds and solutions are discussed.

Part Three covers advanced topics; it is aimed at anyone who might have a hand in database administration. ¬†This section covers replication, sharding, and hardware setup, in addition to indexing and query performance optimisation techniques. ¬†As a NoSQL novice building relatively small web applications, scale doesn’t feature highly on my list of priorities. ¬†However, I enjoyed learning about the problems that normalised data models face when scaling (it seems like the best you can do is stick Memcached in front of the database and upgrade your hardware), and the advantages of a denormalised data model (horizontal scaling) in this regard. ¬†I also enjoyed the section on indexing and performance optimisation; the discussion of the need for efficient indices, and how redundant indices¬†can significantly hamper query performance was particularly instructive.

Overall:

First off, I wouldn’t recommend this book to anyone without a reasonable knowledge of JavaScript, or at least reasonable proficiency in object-oriented programming. ¬†A passing familiarity with Ruby is also helpful, but not required. ¬†The application examples created by the author really help you to grasp the fundamentals, it would be a more difficult read if you couldn’t follow them.

The book is well organised – I didn’t feel the need to read parts out of order to make sense of things. ¬†Chapters Five and Six were my favourite chapters: they explain the nuts and bolts of MongoDB, and quickly get you building a real-world, practical application. ¬†They were particularly instructive on how to implement CRUD functionality, and the design patterns provided continue to be useful.

The author set out to create a book that would take a developer from being a MongoDB novice, to being a “master” of MongoDB. ¬†After reading the book, I wouldn’t exactly call myself a master,¬†but I’m certainly confident enough now to use MongoDB now in my applications, without worrying whether I might’ve been better off using MySQL.

Check out MongoDB in Action on Amazon

Terminal Productivity Tips for Web Developers

When I’m working, the three applications I’m guaranteed to have open are Chrome, Textmate and Terminal. Terminal can look pretty intimidating at first, but once you’ve picked up some of the core commands (cd, ls, mv, cp, mkdir, touch, grep, etc.) you begin to wonder how you ever lived without it. Today I’m going to share with you a few of my favourite Terminal commands and shortcuts.

I’ve split this article into two parts: the first section is an introduction to aliases and functions. It also contains instructions on how to find and edit your bash profile. The second discusses a few of the commands I use on a daily basis, in addition to some commands I use less frequently, but are otherwise useful to know. If you’re already comfortable with aliases and functions, feel free to skip ahead to part two.

Part One: Aliases, Functions, and your Bash Profile

Aliases

Aliases are fantastic time-savers Рthey are shortcuts used to avoid typing lengthy commands. To create an alias, you need to edit your bash profile.

What’s a Bash Profile?

A bash profile is a file that runs when you start up Terminal – it loads user-created aliases and functions. It’s located in your home directory, and is usually called¬†.bash_profile or .profile. The filename starts with a dot, so it’s hidden by default. ¬†To find hidden files in Terminal, use the ls command with the -a option (ls -a). ¬†Once you’ve found your profile, open .profile will open the file in your default editor. ¬†mate .profile will open the file in Textmate if you have it installed.

Example: Directory Aliases

My development server is on a NAS box. If I had to navigate to it from my home directory each time I wanted to work on a project, that would involve a reasonable amount of typing. This is the alias I use to navigate to my web server’s directory:

alias ws='cd /Volumes/Nikki/WebServer/'

Any time I need to get to my web server, all I have to type is ws Рthis saves me a huge amount of time over the course of the day.  You can use this as a template for your own aliases.

Functions

Aliases are great for simple string substitution, but if you need something a bit more complex – if you need to use conditional logic, or pass arguments, for example – you can use a function.

Example: Start Web Browser/Run Local HTML Files in Browser

It can be a bit of a pain to navigate to my applications directory and open my browser, so I created a shortcut with a couple of options.  I can either open the application using wb, or I can pass in the name of a file and open that in the browser (wb file.html).

function wb() {
    if [ -z '$1' ]
    then
        open /Applications/Google Chrome.app/
    else
        open -a /Applications/Google Chrome.app/ $1
    fi
}

(Just using open on file.html would open the file your default program – in my case, Textmate).

Part Two: Tips and Tricks

Quitting Applications

Quitting an application from Terminal is slightly more long-winded than opening one:

osascript -e 'tell app "Firefox" to quit'

That’s a fair amount to type every time you want to quit an application. Creating a function in your ~/.bash_profile will make this quicker, and you can pass in the name of the application you want to quit as a variable.

function quit() { osascript -e "tell app "$1" to quit"; }

Pop that pre into your bash profile, and you can close any open application by typing

quit appname

into Terminal. If the name of the application you need to close has a space in it, you need to remember to escape that space with a backslash, otherwise it won’t work.

Quick Web Server

Sometimes when I’m out and about with my MBP, I might temporarily need the use of a http server. Type the following command into terminal, and the contents of your current working directory will be served over http at http://localhost:8000.

python -m SimpleHTTPServer 8000

Even better, create an alias –

alias pyserve='python -m SimpleHTTPServer 8000'

Note: you need Python installed on your machine for this to work. OSX should have Python installed by default.

Zipping files

To zip a single file:

zip filename.zip file.txt

Using the -r flag

zip -r filename.zip directory

will zip an entire directory.

Concatenating files

Concatenating several files together is as simple as –

cat file1 file2 file3 > newfilename

Minifying files

Grab a copy of YUI Compressor. The only file you need is the .jar file located in the build directory. Rename this file to yui.jar and put it into your main development directory (not your project folders). To minify a JavaScript file, use:

java -jar yui.jar filenamein.js -o filenameout.min.js

A new, minified version of your file will be created. This will also work for css files – obviously it won’t tokenize anything, but it will strip out all the comments and whitespace.

Exporting a MySQL database

This is useful when you need to migrate a local WordPress installation to a live website:

mysqldump -h remotehostip -u username -p databasename > db_name.sql

New Project Template

Most web developers have some sort of base template that they use for each new project. For front-end developer like myself, this template will usually consist of some HTML boilerplate, CSS/SASS templates, a CSS reset, a directory for JavaScript files, maybe a JavaScript library and/or a framework, a directory for images, and a directory for project assets.

Browsing to the template folder, copying it to a new location on my web server, and renaming it to the name of the new project can at best be considered a minor inconvenience; but it’s an inconvenience we can optimise:

function new() {
    cp -R /Volumes/Nikki/WebServer/Template `pwd`;
    mv Template $1;
}

Finally…

One final tip – not strictly a Terminal tip, but it certainly helps me in terms of productivity. On my work machines, I block access to Twitter, Hacker News, Google News – anything I catch myself reading when I’m procrastinating rather than working. I do this by using the well known trick of redirecting any outgoing requests for these sites to my local machine. ¬†If you want to do the same, open your hosts file:

sudo mate /etc/hosts

And add a new entry underneath the entry for localhost:

127.0.0.1 twitter.com

It’s a minor annoyance to unblock it, but it’s enough of an annoyance that I don’t use Twitter on my work machines any more ūüôā

What are your favourite Terminal productivity tips?

Quick Tip: CSS3 Transition End Event

If you’re working with CSS3 transforms and transitions, you detect the completion of an animation with the vendorTransitionEnd event.

So in Chrome and Safari:

document.addEventListener('webkitTransitionEnd', function(e) {
    console.log(e);
}, false);

will log the event object to the console. Opera uses oTransitionEnd, and Firefox just uses transitionend (note the lack of camel case for this one).

If you have multiple animations on multiple elements, you need to be able to tell which element has finished animating, and at what time.  You could either bind your event listener to each element respectively (which might get a bit messy), or you could rely on event delegation. Bind your event to the document as above, and e.target will reference the dom element that just finished animating.  e.target.className and e.target.id are very handy!

Optimising HTML5 Canvas games

In this blog post I’m going to share a few tips and tricks I’ve picked up from the process of refactoring HTML5 Breakout. Some of these will be common sense performance tips; others might leave you scratching your head a bit. ¬†I’ll start with a few general tips, and then move on to the canvas specific ones. (The best stuff is at the bottom).

Don’t prematurely optimise your code

This can cause all sorts of bugs, like breaking your collision detection. ¬†You’ll end up having to backtrack, and it can become a bit of a nightmare, so it’s best not to do it!

Profile your code

Once you’ve finished your game, you need to profile your code. ¬†It’s important to benchmark your code on all of the browsers you’re targeting. ¬†Modern browsers compile and optimise your code, but the different JS engines do it in slightly different ways, so the function that runs lightning fast in one browser may perform sluggishly on another (for example, see ‘clear methods’ below). ¬†David Mandelin at Mozilla did a great presentation on JS engine internals at VelocityConf, it’s well worth going through the slide deck if you’re interested in the nitty gritty. Go for the low hanging fruit here and investigate any code that is reported as running slow across all your profiles.

Minimise code in loops

This is a common sense best practice, but it becomes especially important when you’re doing something as CPU intensive as repeatedly drawing images to canvas. ¬†When I was profiling Breakout, I discovered one function that was taking a particularly long time to execute. ¬†It turned out that I¬†had a call to fillRect in one of the loops in the drawBricks function. Simply moving this one line out of the loop shaved 50% of the execution time of that function. ¬†Little fixes that result in huge performance increases are always the most satisfying ūüôā

Minimise draw calls

As mentioned above, constantly drawing and redrawing images is hard work, so don’t draw if you don’t have to! ¬†It can be tempting to draw a frame and then clear the whole canvas repeatedly in your game loop, as it simplifies development. However, it doesn’t do much good for your game’s performance. ¬†If you can figure out which parts of your canvas have changed since the last frame, and only redraw those parts (by using ‘dirty rectangles‘), your game should see a huge performance boost.

Use more than one canvas

If you draw too many pixels to the same canvas at the same time, your frame rate will fall through the floor. ¬†In these circumstances, it’s better to use multiple canvasses layered on top of one another. Take Breakout for example. Trying to draw the bricks, the ball, the paddle, any power-ups or weapons, and then each star in the background – this simply won’t work, it takes too long to execute each of these instructions in turn. ¬†By splitting the starfield and the rest of the game onto separate canvasses, I was able to ensure a decent framerate. ¬†This is a useful technique for animation-heavy games.

Pixel snapping

If you’re a front-end developer used to working with the DOM, the concept of sub-pixel rendering will be a little odd – we’re just not used to seeing anything with units of measurement less than a pixel. ¬†Canvas can render your images at positions less than a pixel, anti-aliasing them in the process. ¬†Anti-aliasing canvas is currently super slow on some platforms, so it’s best to avoid it if you can¬†by rounding off the positions of your game entities just before each frame is drawn. ¬†I’ve been using | 0 (bitwise OR) – it’s faster than Math.round() as it doesn’t have the function call overhead, but it seems like the best method changes with each incremental browser version. ¬†Check with JSPerf and pick the best method for your use case. And read Seb Lee Delisle’s HTML5 canvas sprite optimisation post, he explains this much better than I do ūüôā

Clear methods

There are three ways to clear your canvas: fillRect using your background colour, clearRect, and resetting the canvas’ width (or height) property. ¬†Resetting the width of the canvas is supposedly the fastest method, but if you look at the JSPerf tests – particularly the differences between Chrome 14 and Firefox 4 – the execution speeds documented are wildly at odds with each other. ¬†From this data, it appears that width = width performs at the same speed or faster (much faster in some instances) than the other two methods on mobile, but when it comes to desktop browsers, all bets are off.

Another issue worth thinking about – if you’re doing a lot of transforms, width = width resets the transform stack, whereas clearRect and fillRect don’t. ¬†So if you’re doing a lots of transforms and don’t want to reset state, clearRect is probably the best way to go. ¬†Simon Sarris did a good write up of this called How you clear your HTML5 Canvas matters.

From JSPerf:

UserAgent clearing non transformed canvas clearing transformed canvas setting width # Tests
Chrome 14.0.786 14,779 14,495 417,562 1
Chrome 14.0.789 15,842 15,258 407,201 3
Firefox 4.0.1 188,673 143,651 1,741 27

Use requestAnimationFrame instead of setInterval/setTimeout

SetInterval and setTimeout were never intended to be used as animation timers, they’re just generic methods for calling functions after a time delay. ¬†If you set an interval for 20ms in the future, but your queue of functions takes longer than that to execute, your timer won’t fire until after these functions have completed. ¬†That could be a while, which isn’t ideal where animation is concerned. ¬†RequestAnimationFrame is a method¬†invented by Robert O’Callahan at Mozilla – it specifically tells the browser that an animation is taking place, so it can optimise repaints accordingly. ¬†It also throttles the animation for inactive tabs, so it won’t sap your mobile device’s battery if you leave it open in the background.

Nicholas Zakas wrote a hugely detailed and informative article about requestAnimationFrame on his blog which is well worth reading. ¬†If you want some hard and fast implementation instructions, then Paul Irish has written a requestAnimationFrame shim – this is what I’m currently using in my games.

Canvas has a back-reference

And finally, a useful tip I picked up at Remy Sharp’s¬†HTML5 Workshop – your 2d context has a back reference to it’s associated DOM element:

var ctx = doc.getElementById('canvas').getContext('2d');
console.log(ctx.canvas);    // HTMLCanvasElement

This can come in pretty handy!

I hope you’ve enjoyed my whistle-stop tour of canvas performance optimisation techniques. If you spot any errors or glaring omissions, please leave a comment below ūüôā

Cheers!

Curved and sloped tiles in JavaScript platform games

Demo: http://nicolahibbert.com/demo/javascript-tile-map-editor/

If you’ve been reading along recently, you’ll know that HTML5 Breakout is at a stage where I’m not entirely embarrassed by it, and I’m making a start on my next JavaScript game.

I’ve had an idea for a 2d sidescrolling platformer for a while, but I specifically want the player character to be able to move up and down slopes and curves (ala Sonic the Hedgehog). JavaScript game engines are still generally quite young and aren’t particularly full featured – I don’t know of any that implement this functionality out of the box, so I’m going to show you how I’m doing it. It involves separating out the alpha channel using the HTML5 Canvas API, and using this data to build a pixel-level map of where the character’s position should be at any single point on the tile.

Please note that this is a discussion of static per-pixel collision detection only, i.e., character vs. background tile map. I’m assuming some knowledge of tile based collision maps and of the Canvas API, but I’ll do a quick overview just in case.

Static collision detection

Your standard collision map in JavaScript is just a multidimensional array:

var map = [
    [1,0,0,0],
    [1,1,0,0],
    [1,1,1,0],
    [1,1,1,1]
];

The zeros represent an empty space on the map, and the ones represent where a collision will take place. Each ‘one’ is a rectangle: the dimensions of the tile, multiplied by it’s position in the map, drawn to the stage at it’s respective x and y co-ordinate. This map will usually be stored in a JSON format with some other information – tile dimensions, image file locations and the like.

This is great if you want flat platforms, but not so great if you want your character to be able to traverse curved and sloped surfaces.

Using the Canvas API to extract alpha channel data

Creating a sloped or curved tile is easy – you just have to ensure that your tile image has an alpha transparent component representing the path that you want your character to follow. This tile is saved with a bunch of other tiles to a sprite sheet. You can then use Canvas to extract the alpha channel data, and include this data as a part of your collision map.

buildMap : function(e) {
    var alpha = [], // used to store alpha data
        numTiles = 10, // size of tilemap (10 tiles high, 10 tiles wide)
        tileSize = 32, // size of tile in pixels
        pixels, // store pixel data array
        len, // cache length of pixel data array
        x, y, z;

    for (x = 0; x < numTiles; x++) { // tiles across
        alpha[x] = [];

        for (y = 0; y < numTiles; y++) { // tiles down
            pixels = map.getImageData(y * tileSize, x * tileSize, tileSize, tileSize),
            len = pixels.data.length;

            alpha[x][y] = [];

            for (z = 0; z < len; z += 4) {
                alpha[x][y][z / 4] = pixels.data[z + 3]; // store alpha data only
            }

            if (alpha[x][y].indexOf(0) === -1) { // solid tile
                alpha[x][y] = 1;
            } else if (alpha[x][y].indexOf(255) === -1) { // transparent tile
                alpha[x][y] = 0;
            } else { // partial alpha, build pixel map
                alpha[x][y] = pixels;
            }
        }
    }
}

This bit of code retrieves image data from a canvas, breaks it down into tile sized chunks, and then stores the image data for each of these tiles into an array. It then checks the alpha data for each tile: if the tile is comprised entirely of alpha data, then the tile is transparent – it’s value is set to zero. If there isn’t any alpha on the tile, then the tile is completely solid – it’s value is set to one. If there is partial alpha data, then the pixel data for the alpha channel is stored so we can use it to resolve collisions at a per-pixel level later on.

The array containing the alpha channel for each tile can become pretty large. For these particular tiles, there are 1024 bits of alpha channel data (32 * 32). If you were to JSON.stringify() this, just one tile would take up a huge chunk of space in your map file. Thankfully, we can reduce this information down into a much more manageable state.

sortPartial : function(arr) {
    var len = arr.length,
        temp = [],
        i, j;

    for (i = 0; i < tileSize; i++) {
        temp[i] = [];
        for (j = 0; j < len; j++) {
            if (j % tileSize === j) {
                temp[i][j] = arr[j * tileSize + i];
            }
        }
        temp[i] = temp[i].indexOf(255);
    }
    return temp;
}

This function will filter the array of part-alpha tiles, and return an array containing the position of each first solid pixel on the y axis. Just call this function on the part-alpha array above, like so –

... else { // partial alpha, build pixel map
    alpha[x][y] = app.sortPartial(alpha[x][y]);
}

Now for each tile we’ll get back an array 32 items, rather than 1024 items long –

[28,28,26,26,24,24,22,22,20,20,18,18,16,16,14,14,12,12,10,10,8,8,6,6,4,4,2,2,0,0,0,0]

We can use this information to resolve the x/y position of a character in a 2d platform game when it’s interacting with curved or sloped terrain. Neat eh? ūüôā

A minor problem…

Half way through writing this tutorial, I realised it would be kind of lame not to have a working demonstration of this technique in action. Unfortunately to do that, I needed a tile map editor. I couldn’t find an open source JavaScript based tile map editor, so I started writing one. So much for not reinventing the wheel! It’s definitely a minimum viable product at this early stage – it only outputs collision maps, and it has very limited functionality.

What it does have though, is a lot of potential. If there are any JavaScript game engine developers out there reading this, and you’re in need of a tile map editor (and your project is open source), I’d be happy to contribute to your project and spend a bit more time on this; otherwise it’s just going to be developed as and when I get the time.

For a demonstration of per-pixel collision detection on sloped and curved tiles, check out the demo. Create and build a map, and hit the test button to see it in action. Works in Chrome, Safari and Firefox.

Check out the demo here: JavaScript tile map editor
Get the source on Github

Form Widgets with jQuery UI

Hello!¬† In this tutorial we’re going to (finally!) finish the HTML5 contact form we’ve been developing.¬† Just a quick recap – in part one we went over the HTML markup and discussed the new types of input element;¬† in part two we prettied the form up a little using CSS3; in part three we wrote a JavaScript function to detect whether the user’s browser supports any of the new UI widgets; and in part four we used the jQuery Validate plugin to enable client side form validation.¬† In this tutorial, we’re going to use jQuery UI to enable form widgets if they aren’t supported natively.

Check out where we’re at thus far: Demo
You can view the finished product here

Before we start, I’m going to make a quick change to the form – I’m going to change the input type of the ‘project-budget’ field from ‘number’ to ‘range’, and remove validation for this input.¬† Now we have an extra UI widget to work with.

For this tutorial, we don’t need all of jQuery UI – we only need the core and a couple of the widgets.¬† So do we serve a custom jQuery UI file, which will be considerably smaller in size, or load the whole of jQuery UI from the Google CDN?¬† From a performance perspective, both options have pros and cons.¬† On the one hand, you have the smaller file size of a custom build, but on the other you have content being downloaded concurrently from a CDN, with the possibility that it’s already cached in the user’s browser. If the custom build was very small I would go for that option, but for the purposes of this tutorial, the simplicity of loading jQuery UI from Google’s CDN is just easier for me.¬† You should always consider each case on it’s own merit to get the best performance possible; but if you decide to use files on an external CDN, make sure that you always put a fallback in place in case the CDN is unavailable.

We need to include a reference to the jQuery UI base theme in the head of our page.

<link rel="stylesheet" href="screen.css">
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.9/themes/base/jquery-ui.css">

We already have some script files referenced in our html –

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.0/jquery.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.9/jquery-ui.min.js"></script>
<script src="http://ajax.microsoft.com/ajax/jquery.validate/1.7/jquery.validate.min.js"></script>
<script src="global.js"></script>

Lets change the version number on the jQuery core to 1.5, and import jQuery UI. Paste the following lines of JavaScript code into the bottom of the form HTML, just before the last body tag.

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.0/jquery.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.9/jquery-ui.min.js"></script>
<script src="http://ajax.microsoft.com/ajax/jquery.validate/1.7/jquery.validate.min.js"></script>
<script src="global.js"></script>

Why do we put them at the bottom of the page?¬† We put them here so that they don’t block the loading and rendering of page content.¬† If you place a script in the head of a page, especially if it’s a large one, the browser retrieves and parses this script before flowing the content into the page.¬† So your user will be faced with a big fat blank page until the script has been fetched and parsed, which could be a considerable amount of time depending on how large your script is.¬† Not very user friendly!¬† Notice also that we’re linking to a specific version number, not the shorthand ‘latest’.¬† If you link to the ‘latest’ version, you don’t get the benefits of caching mentioned above, and run the (small) risk of backwards compatibility issues with your code when the next version of jQuery is released.

Below is the (refactored) support detection script that we wrote in part three.

(function() {
    var inputs = ['url', 'email', 'datetime', 'date', 'month', 'week', 'time', 'datetime-local', 'number', 'color', 'range'],
        input = document.createElement('input')
        len = inputs.length,
        uiSupport = {},
        i = 0;

    for (; i < len; i++) {
        input.setAttribute('type', inputs[i]);

        if (input.type === 'text') {
            uiSupport[inputs[i]] = false;
        } else {
            input.value = 'testing';
            (input.value === 'testing') ? uiSupport[inputs[i]] = false : uiSupport[inputs[i]] = true;
        }
    }
})();

After this function has executed (i.e. immediately), the uiSupport object will contain the name of each input type, and a boolean value (true or false) describing whether it is supported in the browser.¬† Next we’re going to create an object to contain all of our widget methods.¬† We’re going to use the slider and the datepicker from jQuery UI.

...
        i = 0,
        widgets = {
            date : function(elem) {
                elem.datepicker({
                    beforeShow : function(input, inst) {
                        inst.dpDiv.css({
                            fontSize : '14px',
                            marginLeft : 215,
                            marginTop : -22
                        });
                    }
                });
            },
            range : function(elem) {
                elem
                    .after('<div></div><span class="slider-val">1500</span>')
                    .next()
                    .slider({
                        value : 1500,
                        min : 500,
                        max : 4000,
                        step : 500,
                        slide: function(event, ui) {
                            $(this).next().text(ui.value);
                        }
                    })
                    .end()
                    .remove();
            },
            altrange : function(elem) {
                elem
                    .addClass('ui-slider')
                    .after('<span class="slider-val">1500</span>')
                    .change(function() {
                        $(this).next().text($(this).val());
                    });
            }
        };

Then after the for loop, we’re going to use for…in to iterate over the uiSupport object to check support and call the relevant method:

    for (var prop in uiSupport) {
        if (prop === 'range') {
            widgets[(uiSupport[prop] ? 'alt' : '') + prop]($('input[type=' + prop + ']', 'form'));
        }
        if (prop === 'date' && !uiSupport[prop]) {
            widgets[prop]($('input[type=' + prop + ']', 'form'));
        }
    }

The last thing we need to do is to disable the ‘contact time’ field, unless the ‘phone’ radio button has been clicked.¬† Our complete function looks like this:

$.contactForm = (function() {

    var inputs = ['url', 'email', 'datetime', 'date', 'month', 'week', 'time', 'datetime-local', 'number', 'color', 'range'],
        input = document.createElement('input')
        len = inputs.length,
        uiSupport = {},
        i = 0,
        widgets = {
            date : function(elem) {
                elem.datepicker({
                    beforeShow : function(input, inst) {
                        inst.dpDiv.css({
                            fontSize : '14px',
                            marginLeft : 215,
                            marginTop : -22
                        });
                    }
                });
            },
            range : function(elem) {
                elem
                    .after('<div></div><span class="slider-val">1500</span>')
                    .next()
                    .slider({
                        value : 1500,
                        min : 500,
                        max : 4000,
                        step : 500,
                        slide: function(event, ui) {
                            $(this).next().text(ui.value);
                        }
                    })
                    .end()
                    .remove();
            },
            altrange : function(elem) {
                elem
                    .addClass('ui-slider')
                    .after('<span class="slider-val">1500</span>')
                    .change(function() {
                        $(this).next().text($(this).val());
                    });
            }
        };

    for (; i < len; i++) {
        input.setAttribute('type', inputs[i]);

        if (input.type === 'text') {
            uiSupport[inputs[i]] = false;
        } else {
            input.value = 'testing';
            (input.value === 'testing') ? uiSupport[inputs[i]] = false : uiSupport[inputs[i]] = true;
        }
    }

    for (var prop in uiSupport) {
        if (prop === 'range') {
            widgets[(uiSupport[prop] ? 'alt' : '') + prop]($('input[type=' + prop + ']', 'form'));
        }
        if (prop === 'date' && !uiSupport[prop]) {
            widgets[prop]($('input[type=' + prop + ']', 'form'));
        }
    }

    $('input[type=time]').attr('disabled','disabled');
    $('input[type=radio]').click(function() {
        if ($(this).val() === 'Phone') {
            $('input[type=time]').removeAttr('disabled');
        } else {
            $('input[type=time]').attr('disabled','disabled');
        }
    });

})();

If we have a look at our form now, we can see that in Firefox, where none of the UI widgets are currently supported, we have the jQuery UI slider and datepicker:

In Chrome, where the range slider is supported but the datepicker isn’t, we see this:

And in Opera, where both the range slider and datepicker are supported natively, we see this:
You can view the final product at the link below.
Demo

I hope you’ve enjoyed this series of tutorials.¬† Please leave a comment below if you have any questions or suggestions.