diff --git a/public/css/default.css b/public/css/default.css
new file mode 100644
index 0000000..0b178a7
--- /dev/null
+++ b/public/css/default.css
@@ -0,0 +1,80 @@
+/*------------------------------------*\
+	GENERIC
+\*------------------------------------*/
+html {
+	-webkit-box-sizing: border-box; /* Safari/Chrome, other WebKit */
+	-moz-box-sizing: border-box;    /* Firefox, other Gecko */
+	box-sizing: border-box;         /* Opera/IE 8+ */
+}
+
+*, *:before, *:after {
+	box-sizing: inherit;
+
+	font-family: monospace, 'Courier New';
+	font-size: 12px;
+}
+
+body, html {
+	min-height: 100%;
+	background-color: #000000;
+	overflow: hidden;
+	padding: 0px;
+	margin:0px;
+	color: #00DD00;
+}
+
+/*------------------------------------*\
+	CHAT
+\*------------------------------------*/
+#chat {
+	left: 0;
+	right: 0;
+	bottom: 40px;
+	position: absolute;
+	list-style-type: none;
+	padding:0;
+	margin:0;
+}
+
+/* Messages */
+#chat li {
+	white-space: pre;
+	padding: 2px 10px;
+}
+
+/* Message types */
+/* the `i` element hold the actual message */
+#chat i {
+	font-style: normal;
+}
+#chat i.info	{ color: #7777ff; }
+#chat i.server	{ color: #999999; }
+#chat i.error	{ color: #ff7777; }
+#chat i.message	{ color: #eeeeee; }
+
+/*------------------------------------*\
+	INPUT
+\*------------------------------------*/
+#input_wrapper {
+	right:0;
+	bottom:0;
+	left:0;
+	position: absolute;
+
+	height:40px;
+}
+
+#input {
+	top: 0;
+	bottom: 0;
+	width: 100%;
+	position: absolute;
+
+	border: 0;
+	outline: 0;
+
+	padding: 5px 5px 5px 15px;
+
+	color: #00dd00;
+	background-color:#141414;
+}
\ No newline at end of file
diff --git a/public/index.html b/public/index.html
new file mode 100644
index 0000000..b286b2c
--- /dev/null
+++ b/public/index.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<head>
+	<meta charset="utf-8">
+	<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+	<title>Cryptalk</title>
+
+	<link rel="stylesheet" type="text/css" href="css/default.css">
+</head>
+<body>
+
+	<!-- Messages -->
+	<!-- Each message is contained within an li element -->
+	<ul id="chat">
+		<li>SRV> Booting ...</li>
+	</ul>
+
+	<!-- Message input -->
+	<div id="input_wrapper">
+		<input type="text" id="input" />
+	</div>
+	
+	<!-- Only include the script needed for loading the app -->
+	<script src="js/vendor/fandango.v20140918.min.js"></script>
+	<script src="js/bootstrap.js"></script>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/public/js/bootstrap.js b/public/js/bootstrap.js
new file mode 100644
index 0000000..ec63521
--- /dev/null
+++ b/public/js/bootstrap.js
@@ -0,0 +1,30 @@
+// This Javascript file is the only file besides fandango.js that will be fetched through DOM.
+
+// Setup fandango
+fandango.defaults({
+	baseUrl: 'js/cryptalk_modules/',
+	paths: {
+		websocket: 'https://cdnjs.cloudflare.com/ajax/libs/socket.io/0.9.16/socket.io.min.js',
+		aes: 'https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js',
+		domReady: 'https://cdnjs.cloudflare.com/ajax/libs/require-domReady/2.0.1/domReady.min.js'
+	},
+
+	// CryptoJs AES does not support AMD modules. We'll have to create a shim.
+	shim: {
+		aes: {
+			exports: function () { // String (the global variable name) or a function; returning the desired variable.
+				return CryptoJS.AES;
+			}
+		}
+	}
+});
+
+// Fetch our modules asynchronously, when the DOM is finished loading.
+//define('bootstrap_module', ['domReady'], function (domReady) {
+//	domReady(function () {
+//		require(['cryptalk']);
+//	});
+//});
+
+// No need to wait for DOM - the Javascript is at the bottom
+require(['cryptalk']);
\ No newline at end of file
diff --git a/public/js/cryptalk_modules/$.js b/public/js/cryptalk_modules/$.js
new file mode 100644
index 0000000..8683209
--- /dev/null
+++ b/public/js/cryptalk_modules/$.js
@@ -0,0 +1,129 @@
+define('$', ['fandango', 'websocket', 'aes'], function (fandango, websocket, aes) {
+	var exports = {
+			selector: 0,
+			utilities: {},
+			prototype: {}
+		},
+
+		// Shortcuts
+		utils = exports.utilities,
+		proto = exports.prototype,
+
+		each = fandango.each;
+
+	// The DOM selector engine
+	exports.selector = function (selector) {
+		var match,
+			matches = [];
+
+		if (selector === document) {
+			matches.push(document);
+		} else {
+			selector = selector.slice(1);
+
+			if (match = document.getElementById(selector)) {
+				matches.push(match);
+			}
+		}
+
+		// Must ALWAYS return a native array: []
+		return matches;
+	};
+
+	// Namespace AES
+	utils.AES = {
+		decrypt: aes.decrypt,
+		encrypt: aes.encrypt
+	};
+
+	// Namespace encode
+	utils.AES = {
+		decrypt: function (string, fgh) {
+			return aes.decrypt(string, fgh).toString(CryptoJS.enc.Utf8);
+		},
+		encrypt: function (string, fgh) {
+			return aes.encrypt(string, fgh).toString();
+		},
+	};
+
+	// Namespace websocket
+	utils.Websocket = {
+		connect: websocket.connect,
+		on: websocket.on
+	};
+
+	utils.ssplit = function (string, seperator) {
+		var components = string.split(seperator);
+		return [components.shift(), components.join(seperator)];
+	};
+
+	utils.activeElement = function () {
+		try { return document.activeElement; } catch (e) {}
+	}
+
+	/**
+	 * A very simple implementation of sprintf()
+	 * @param  {} str [description]
+	 * @param  {[type]} map [description]
+	 * @return {[type]}     [description]
+	 */
+	utils.template = function (str, map) {
+		return str && str.replace(/{(\w+)}/gi, function(outer, inner) {
+			return map.hasOwnProperty(inner) ? map[inner] : outer /* '' */;
+		});
+	};
+
+	// Originating from mustasche.js
+	// https://github.com/janl/mustache.js/blob/master/mustache.js#L43
+	utils.escapeHtml = (function () {
+		var pattern = /[&<>"'\/]/g,
+			entities = {
+				'&': '&amp;',
+				'<': '&lt;',
+				'>': '&gt;',
+				'"': '&quot;',
+				"'": '&#39;',
+				'/': '&#x2F;'
+			};
+
+		return function (string) {
+			return String(string).replace(pattern, function (s) {
+				return entities[s];
+			});
+		};
+	}());
+
+	// Extremely naive implementations of .html() and .append()
+	proto.html = function (string) {
+		each(this, function (element) {
+			element.innerHTML = string;
+		});
+
+		return this;
+	};
+
+	proto.append = function (string) {
+		each(this, function (element) {
+			element.innerHTML += string;
+		});
+
+		return this;
+	};
+
+	// Extremely naive implementations of .on()
+	proto.on = function (eventName, callback) {
+		each(this, function (element) {
+			element.addEventListener(eventName, callback);
+		});
+
+		return this;
+	};
+
+	proto.focus = function () {
+		this[0].focus();
+
+		return this;
+	};
+
+	return exports;
+})
\ No newline at end of file
diff --git a/public/js/cryptalk_modules/cryptalk.js b/public/js/cryptalk_modules/cryptalk.js
new file mode 100644
index 0000000..75cc3f4
--- /dev/null
+++ b/public/js/cryptalk_modules/cryptalk.js
@@ -0,0 +1,187 @@
+// Main cryptalk module. Will be called by bootstrap.js when the DOM is ready to interact with.
+define('cryptalk', {
+	data: {
+		// If no host is given it will default to localhost.
+		host: 'http://www.huset.nu:8080'
+	},
+	compiles: ['$'],
+	requires: ['templates']
+}, function ($, requires, data) {
+	var socket,
+		key,
+		room,
+		hash,
+
+		// Collection of DOM components
+		components = {
+			chat: 	$('#chat'),
+			input: 	$('#input')
+		},
+
+		// Shortcut
+		templates = requires.templates,
+
+		// Adds a new message to the DOM
+		post = function (type, text, clearChat, clearBuffer) {
+			var tpl = templates.post[type],
+				post = $.template(tpl, text && {
+					text: text
+				});
+
+			// Always clear the input after a post
+			if (clearBuffer) {
+				components.input[0].value = '';
+			}
+
+			// Append the post to the chat DOM element
+			components.chat[clearChat ? 'html' : 'append'](post);
+		},
+
+		// Chat related commands
+		commands = {
+			help: function () {
+				post('info', templates.help);
+			},
+
+			clear: function () {
+				components.chat.html('');
+				components.input[0].value = '';
+			},
+
+			leave: function () {
+				socket.emit('room:leave', room);
+			},
+
+			key: function (payload) {
+				// Make sure the key meets the length requirements
+				if (payload.length < 8) {
+					return post('info', templates.messages.key_weak);
+				}
+
+				// Set key
+				key = payload;
+
+				// Inform that the key has been set
+				post('info', (room ? templates.messages.key_ok_ready : templates.messages.key_ok_but_no_room));
+			},
+
+			join: function (payload) {
+				return (
+					room
+						? post('info', $.template(templates.messages.already_in_room, { roomName: room}))
+						: socket.emit('room:join', payload)
+				);
+			},
+
+			create: function (payload) {
+				socket.emit('room:create');
+			}
+		},
+
+		// Handler for the document`s keyDown-event.
+		onKeyDown = function (e) {
+			var buffer,
+				parts,
+				payload,
+				command;
+
+			// The Document object is bound to this element.
+			// If the active element is not the input, focus on it and exit the function.
+			if (components.input[0] !== $.activeElement()) {
+				return components.input.focus();
+			}
+
+			// Return immediatly if the buffer is empty or if the hit key was not <enter>
+			if (e.keyCode !== 13 || !(buffer = components.input[0].value)) {
+				return;
+			}
+
+			// Handle command
+			if (buffer[0] === '/') {
+				parts = $.ssplit(buffer.slice(1), ' ');
+				command = parts[0];
+				payload = parts[1];
+
+				// Check that there is an handler for this command
+				if (!commands[command]) {
+					return post('error', $.template(templates.messages.unrecognized_command, { commandName: command }));
+				}
+
+				// Execute command handler
+				commands[command](payload);
+			} else /* Handle ordinary message */ {
+				// Make sure that the users has joined a room
+				if (!room) {
+					return post('error', templates.messages.msg_no_room);
+				}
+
+				// And that a valid key is set
+				if (!key) {
+					return post('error', templates.messages.msg_no_key);
+				}
+
+				// Before sending the message.
+				// Encrypt message using room UUID as salt and key as pepper.
+				socket.emit('message:send', {
+					room: room,
+					msg: $.AES.encrypt(buffer, room + key)
+				});
+
+				// Adn the the buffer
+				components.input[0].value = '';
+			}
+		};
+
+	// Connect to server
+	socket = $.Websocket.connect(data.host);
+
+	// Bind socket events
+	socket
+		.on('connect', function () {
+			$(document).on('keydown', onKeyDown);
+			components.input.focus();
+		})
+
+		.on('room:created', function (data) {
+			socket.emit('room:join', data);
+		})
+
+		.on('room:joined', function (data) {
+			room = data;
+			post('info', $.template(templates.messages.joined_room, { roomName: room }));
+		})
+
+		.on('room:left', function () {
+			post('info', $.template(templates.messages.left_room, { roomName: room }));
+			room = false;
+		})
+
+		.on('message:send', function (data) {
+			var decrypted = $.AES.decrypt(data, room + key),
+				sanitized = $.escapeHtml(decrypted);
+
+			if (!decrypted) {
+				post('info', templates.messages.unable_to_decrypt);
+			} else {
+				// Post the message, but do not clear either the chat nor the buffer.
+				post('message', sanitized, false, false);
+			}
+		})
+
+		.on('message:server', function (data) {
+			post('server', data);
+		});
+
+	// Post the help/welcome message
+	post('info', templates.help, true);
+
+	// It's possible to provide room and key using the hashtag.
+	// The room and key is then seperated by semicolon (room:key).
+	// If there is no semicolon present, the complete hash will be treated as the room name and the key has to be set manually.
+	if (hash = window.location.hash) {
+		parts = hash.slice(1).split(':');
+
+		parts[0] && commands.join(parts[0]);
+		parts[1] && commands.key(parts[1]);
+	}
+});
\ No newline at end of file
diff --git a/public/js/cryptalk_modules/templates.js b/public/js/cryptalk_modules/templates.js
new file mode 100644
index 0000000..1f87fea
--- /dev/null
+++ b/public/js/cryptalk_modules/templates.js
@@ -0,0 +1,50 @@
+// The templating function only supports variables.
+// Define a variable as so: {variable_name}
+define({
+	help: '' +
+		'<li>																				\n' +
+		'Cryptalk, encrypted instant chat.													\n' +
+		' 																					\n' +
+		'----------------------------------------------------------------------------------	\n' +
+		' 																					\n' +
+		'Available commands: 																\n' +
+		' 	/create 					Creates a room 										\n' +
+		'	/join		RoomId				Joins a room									\n' +
+		'	/leave		RoomId				Leaves a room									\n' +
+		'	/key		OurStrongPassphrase		Sets the password used for 					\n' +
+		'	 						encryption/decryption 									\n' +
+		'	/clear						Clears on-screen buffer 							\n' +
+		'	/help						This 												\n' +
+		'	 																				\n' +
+		'	Besides that, it\'s just to talk! 												\n' +
+		'	 																				\n' +
+		'Code available for review at https://www.github.com/hexagon/cryptalk 				\n' +
+		'																					\n' +
+		'---------------------------------------------------------------------------------	\n' +
+		'</li>																				',
+
+	post: {
+		info: 		'<li>INF> <i class="info">{text}</i></li>',
+		server: 	'<li>SRV> <i class="server">{text}</i></li>',
+		error: 		'<li>ERR> <i class="error">{text}</i></li>',
+		message: 	'<li>MSG> <i class="message">{text}</i></li>'
+	},
+
+	messages: {
+		key_weak: 				'Hmm, that\'s a weak key, try again...',
+		key_ok_ready: 			'Key set, you can now start communicating.',
+		key_ok_but_no_room: 	'Key set, you can now join a room and start communicating.',
+		msg_no_room: 			'You have to join a room before sending messages. See /help.',
+		msg_no_key: 			'You have to set an encryption key before sending a message. See /help.',
+
+		// Available variables: 'commandName'
+		unrecognized_command: 	'Unrecognized command: "{commandName}"',
+
+		// Available variables: 'roomName'
+		joined_room: 			'Joined room {roomName}',
+		left_room: 				'Left room {roomName}',
+		already_in_room: 		'You are already in room {roomName}, stoopid.',
+
+		unable_to_decrypt: 		'Unabled to decrypt received message, keys does not match.'
+	}
+});
\ No newline at end of file
diff --git a/public/js/vendor/aes.v3.1.2.min.js b/public/js/vendor/aes.v3.1.2.min.js
new file mode 100644
index 0000000..6834e42
--- /dev/null
+++ b/public/js/vendor/aes.v3.1.2.min.js
@@ -0,0 +1,10 @@
+/*
+CryptoJS v3.1.2
+code.google.com/p/crypto-js
+(c) 2009-2013 by Jeff Mott. All rights reserved.
+code.google.com/p/crypto-js/wiki/License
+*/
+(function(){for(var q=CryptoJS,x=q.lib.BlockCipher,r=q.algo,j=[],y=[],z=[],A=[],B=[],C=[],s=[],u=[],v=[],w=[],g=[],k=0;256>k;k++)g[k]=128>k?k<<1:k<<1^283;for(var n=0,l=0,k=0;256>k;k++){var f=l^l<<1^l<<2^l<<3^l<<4,f=f>>>8^f&255^99;j[n]=f;y[f]=n;var t=g[n],D=g[t],E=g[D],b=257*g[f]^16843008*f;z[n]=b<<24|b>>>8;A[n]=b<<16|b>>>16;B[n]=b<<8|b>>>24;C[n]=b;b=16843009*E^65537*D^257*t^16843008*n;s[f]=b<<24|b>>>8;u[f]=b<<16|b>>>16;v[f]=b<<8|b>>>24;w[f]=b;n?(n=t^g[g[g[E^t]]],l^=g[g[l]]):n=l=1}var F=[0,1,2,4,8,
+16,32,64,128,27,54],r=r.AES=x.extend({_doReset:function(){for(var c=this._key,e=c.words,a=c.sigBytes/4,c=4*((this._nRounds=a+6)+1),b=this._keySchedule=[],h=0;h<c;h++)if(h<a)b[h]=e[h];else{var d=b[h-1];h%a?6<a&&4==h%a&&(d=j[d>>>24]<<24|j[d>>>16&255]<<16|j[d>>>8&255]<<8|j[d&255]):(d=d<<8|d>>>24,d=j[d>>>24]<<24|j[d>>>16&255]<<16|j[d>>>8&255]<<8|j[d&255],d^=F[h/a|0]<<24);b[h]=b[h-a]^d}e=this._invKeySchedule=[];for(a=0;a<c;a++)h=c-a,d=a%4?b[h]:b[h-4],e[a]=4>a||4>=h?d:s[j[d>>>24]]^u[j[d>>>16&255]]^v[j[d>>>
+8&255]]^w[j[d&255]]},encryptBlock:function(c,e){this._doCryptBlock(c,e,this._keySchedule,z,A,B,C,j)},decryptBlock:function(c,e){var a=c[e+1];c[e+1]=c[e+3];c[e+3]=a;this._doCryptBlock(c,e,this._invKeySchedule,s,u,v,w,y);a=c[e+1];c[e+1]=c[e+3];c[e+3]=a},_doCryptBlock:function(c,e,a,b,h,d,j,m){for(var n=this._nRounds,f=c[e]^a[0],g=c[e+1]^a[1],k=c[e+2]^a[2],p=c[e+3]^a[3],l=4,t=1;t<n;t++)var q=b[f>>>24]^h[g>>>16&255]^d[k>>>8&255]^j[p&255]^a[l++],r=b[g>>>24]^h[k>>>16&255]^d[p>>>8&255]^j[f&255]^a[l++],s=
+b[k>>>24]^h[p>>>16&255]^d[f>>>8&255]^j[g&255]^a[l++],p=b[p>>>24]^h[f>>>16&255]^d[g>>>8&255]^j[k&255]^a[l++],f=q,g=r,k=s;q=(m[f>>>24]<<24|m[g>>>16&255]<<16|m[k>>>8&255]<<8|m[p&255])^a[l++];r=(m[g>>>24]<<24|m[k>>>16&255]<<16|m[p>>>8&255]<<8|m[f&255])^a[l++];s=(m[k>>>24]<<24|m[p>>>16&255]<<16|m[f>>>8&255]<<8|m[g&255])^a[l++];p=(m[p>>>24]<<24|m[f>>>16&255]<<16|m[g>>>8&255]<<8|m[k&255])^a[l++];c[e]=q;c[e+1]=r;c[e+2]=s;c[e+3]=p},keySize:8});q.AES=x._createHelper(r)})();
\ No newline at end of file
diff --git a/public/js/vendor/domReady.v2.0.1.min.js b/public/js/vendor/domReady.v2.0.1.min.js
new file mode 100644
index 0000000..f61f2b9
--- /dev/null
+++ b/public/js/vendor/domReady.v2.0.1.min.js
@@ -0,0 +1,26 @@
+/*
+
+MIT License
+-----------
+
+Copyright (c) 2010-2011, The Dojo Foundation
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+define(function(){"use strict";function e(e){var t;for(t=0;e.length>t;t+=1)e[t](h)}function t(){var t=c;l&&t.length&&(c=[],e(t))}function i(){l||(l=!0,o&&clearInterval(o),t())}function n(e){return l?e(h):c.push(e),n}var r,s,o,a="undefined"!=typeof window&&window.document,l=!a,h=a?document:null,c=[];if(a){if(document.addEventListener)document.addEventListener("DOMContentLoaded",i,!1),window.addEventListener("load",i,!1);else if(window.attachEvent){window.attachEvent("onload",i),s=document.createElement("div");try{r=null===window.frameElement}catch(u){}s.doScroll&&r&&window.external&&(o=setInterval(function(){try{s.doScroll(),i()}catch(e){}},30))}"complete"===document.readyState&&i()}return n.version="2.0.1",n.load=function(e,t,i,r){r.isBuild?i(null):n(i)},n});
\ No newline at end of file
diff --git a/public/js/vendor/fandango.v20140918.min.js b/public/js/vendor/fandango.v20140918.min.js
new file mode 100644
index 0000000..fe61dd3
--- /dev/null
+++ b/public/js/vendor/fandango.v20140918.min.js
@@ -0,0 +1,23 @@
+(function(l){var y={"http://":1,"https://":1,"file://":1},u={deepCopy:!0,baseUrl:"",namespace:"default",timeout:1E3,paths:{}},g={};(function(){this.is=function(){var f=Object.prototype.hasOwnProperty,b=Object.prototype.toString,d={array:Array.isArray||function(a){return"[object Array]"==b.call(a)},arraylike:function(a){if(!a||!a.length&&0!==a.length||d.window(a))return!1;var c=a.length;return 1===a.nodeType||d.array(a)||!d["function"](a)&&(0===c||"number"===typeof c&&0<c&&!g.is(a,"primitive")&&c-
+1 in a)},"boolean":function(a){return!0===a||!1===a},element:function(a){return!(!a||1!==a.nodeType)},finite:function(a){return isFinite(a)&&!isNaN(parseFloat(a))},integer:Number.isInteger||function(a){return"number"===typeof a&&d.finite(a)&&-9007199254740992<a&&9007199254740992>a&&Math.floor(a)===a},iterable:function(a){try{1 in obj}catch(c){return!1}return!0},nan:function(a){return d.number(a)&&a!=+a},number:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},object:function(a){return a===Object(a)},
+primitive:function(a){return!0===a||!1===a||null==a||!!{string:1,number:1}[typeof a]},string:function(a){return"string"==typeof a||a instanceof String},undefined:function(a){return void 0===a},untyped:function(a){if(!a||a.nodeType||"[object Object]"!==b.call(a)||d.window(a))return!1;try{if(a.constructor&&!f.call(a,"constructor")&&!f.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}for(var e in a);return void 0===e||f.call(a,e)},window:function(a){return null!=a&&a==a.window},
+empty:function(a){if(a){if(g.is(a,"array"))return 0===a.length;for(var c in a)if(f.call(a,c))return!1}return!0}},e=0,c=["Arguments","Date","Function","RegExp"];for(;4>e;e++)d[c[e].toLowerCase()]=function(a){a="[object "+a+"]";return function(c){return b.call(c)==a}}(c[e]);d.args=d.arguments;d.bool=d["boolean"];d.plain=d.untyped;return function(a,c){if(d["function"](c))return a===c(a);if(!d.string(c))return a===c;if((c=c.toLowerCase())&&d[c])return d[c](a);throw'Unknown type "'+c+'"';}}();this.each=
+function(){var f=Array.prototype.some;return function(b,d,e){var c,a;if(void 0===b)return obj;e=e||g;if(f&&b.some===f)return b.some(d,e),b;if(g.is(b,"array")||g.is(b,"arraylike")){c=0;for(a=b.length;c<a&&(void 0===b[c]||!d.call(e,b[c],c,b));c++);return b}for(c in b)if(b.hasOwnProperty(c)&&d.call(e,b[c],c,b))break;return b}}();this.merge=function(f){var b,d,e,c,a;b=arguments.length;var C=!1;f=arguments[0];var w=1,z,l,A=0;if(!0===f||!1===f)C=f,f=arguments[w],w++;g.is(arguments[b-1],"function")&&(a=
+arguments[b-1],b--);if(void 0===f)f={};else if(g.is(f,"primitive"))throw new TypeError("Cannot merge primitive data types: merge("+Array.prototype.join.call(arguments,", ")+")");l=g.is(f,"array");if(w<b)for(;c=arguments[w++];){l=l&&g.is(c,"array");for(e in c)d=f[e],b=c[e],void 0===b||b===f?l&&A--:(C&&b&&((z=g.is(b,"array"))||g.is(b,"untyped"))&&(b=g.merge(C,d&&(z?g.is(d,"array")?d:[]:g.is(d,"untyped")?d:{}),b,a)),a&&a(e,f,c)||(A&&(e=+e+A),f[e]=b));A=0}return f};this.subordinate=function(){function f(a){if(!a||
+a.source===window&&a.data===e){var b=c.shift();a=b[0];var b=b[1],d=b.length;d?1===d?a(b[0]):2===d?a(b[0],b[1]):3===d?a(b[0],b[1],b[2]):4===d?a(b[0],b[1],b[2],b[3]):a.apply(void 0,b):a()}}var b=Array.prototype.slice,d=window.postMessage,e="__fandango@"+Math.random().toString(36),c=[],a=function(){return d?function(){d(e,"*")}:function(){setTimeout(f)}}();d&&window.addEventListener("message",f,!1);return function(e){a(c.push([arguments[arguments.length-1],b.call(arguments).slice(0,-1)])-1)}}();this.noop=
+function(){};this.coalesce=function(){for(var f=0,b=arguments.length;f<b;f++)if(void 0!==arguments[f])return arguments[f]};this.hasOwn=function(f){var b=1,d=arguments.length,e,c=!0,a=!0;if(1===d)return!0;if(2===d)return f.hasOwnProperty(arguments[1]);for(;b<d;b++)if(e=arguments[b],!0===e||!1===e)a=e;else if(!f.hasOwnProperty(e)){if(a)return!1;c=!1}else if(!a)return!0;return c};this.freezeObject=Object.freeze||function(f){return object}}).call(g);var D=function(){var f={};headElement=document.getElementsByTagName("head")[0];
+nativeSlice=[].slice;isOpera="undefined"!==typeof opera&&"[object Opera]"===opera.toString();createScriptNode=function(b){var d=document.createElement("script");d.setAttribute("id","script::"+b);d.type="text/javascript";d.charset="utf-8";d.async=!0;return d};useAttachEvent=!(!((useAttachEvent=createScriptNode())&&useAttachEvent.attachEvent&&useAttachEvent.attachEvent.toString&&0<useAttachEvent.attachEvent.toString().indexOf("[native code"))||isOpera);onError=function(b,d,e){return function(c){f[d]=
+0;b.error=c||!0;for(e.parentElement.removeChild(e);func=b.onRejected.shift();)func(c,d)}};onProgress=function(b,d,e,c){return function(){var a,g;if(f[d]){f[d]=2;for(g=0;a=b.onProgress[g++];)a(d,e);c?e.onreadystatechange=null:(e.removeEventListener("load",b.loadListener),e.removeEventListener("error",b.errorListener));if(b.loaded.push(d)===b.load.length)for(g=0;a=b.onResolved[g++];)a(b.load)}}};isLoaded=function(b,d){for(var e=0,c;c=d[e++];)if(f[c])return!0;return!1};load=function(b){var d=0,e,c;if(0<
+b.onResolved.length&&0>=b.load.length){for(;d=b.onResolved.shift();)d([]);return b.shared}for(;!b.error&&(e=b.load[d++]);)e=b.prefix+e+b.suffix,f[e]?onProgress(b,e,document.getElementById("script::"+e))():(f[e]=1,c=createScriptNode(e),useAttachEvent?c.attachEvent("onreadystatechange",onProgress(b,e,c,!0)):(b.loadListener=onProgress(b,e,c),b.errorListener=onError(b,e,c),c.addEventListener("load",b.loadListener,!1),c.addEventListener("error",b.errorListener,!1)),c.src=e,headElement.appendChild(c))};
+unload=function(b){for(var d=0,e,c;e=b[d++];)(c=document.getElementById("script::"+e))&&c.parentElement.removeChild(c),f[e]=0};return function(b,d){var e,c={load:b,loaded:[],prefix:"",suffix:"",onResolved:[],onRejected:[],onProgress:[],onAlways:[],error:null,shared:e};void 0!==d&&!0!==d||load(c);e={prefix:function(a){c.prefix=a;return e},suffix:function(a){c.suffix=a;return e},now:function(){load(c);return e},and:function(a){c.load=c.load.concat(a);return e},unload:function(a){unload(a);return e},
+isLoaded:function(a){return isLoaded(c,a)},done:function(){c.onResolved=c.onResolved.concat(nativeSlice.call(arguments));return e},fail:function(){c.onRejected=c.onRejected.concat(nativeSlice.call(arguments));return e},progress:function(){c.onProgress=c.onProgress.concat(nativeSlice.call(arguments));return e},always:function(){c.onAlways=c.onAlways.concat(nativeSlice.call(arguments));return e}};e["try"]=e.done;e["catch"]=e.fail;e["finally"]=e.always;e.add=e.and;return e}}(),E=function(){function f(a){var c=
+function(){return[]},b={},e={},d,f;for(f=0;d=a[f++];)d.selector&&(c=d.selector),d.prototype&&("function"===typeof d.prototype?d.prototype.call(e):g.merge(e,d.prototype)),d.utilities&&("function"===typeof d.utilities?d.utilities.call(b):g.merge(b,d.utilities));var k=function(a,b,e){var d=0;a=c(a,b);this.context=b||document;this.identify=e;for(this.length=a.length;d<this.length;d++)this[d]=a[d];return this};k.prototype=e;k.prototype.length=0;k.prototype.selector="";k.prototype.splice=Array.prototype.slice;
+return function(a){return g.merge(function(c,b){return new k(c,b,a)},b)}}function b(b){var e,d,h=c[b],q=g.is(h.factory,"function"),m=q?h.factory.length:0,k=h.amdStyle,r=h.compiles&&[],n=h.requires&&(k?[]:{}),l=h.inherits&&{},p,t,s;void 0===(e=h.deepCopy)&&(e=u.deepCopy);h=c[b]=g.merge(e,{},u,h);if(m&&h.requires)for(t=0;s=h.requires[t++];)k?n.push(c[s].exports):g.is(c[s].exports,"function")?n[s]=c[s].exports:g.merge(e,n[s]={},c[s].exports);if(m&&h.compiles)for(t=0;s=h.compiles[t++];)r.push(c[s].exports);
+if(h.inherits)for(t=0;s=h.inherits[t++];)g.merge(e,l,c[s].exports);r&&(d=f(r));for(s in h.instances)p=h.instances[s],p.context=void 0===p.context?h.context:p.context,p.deepCopy=void 0===p.deepCopy?h.deepCopy:p.deepCopy,p.namespace=void 0===p.namespace?h.namespace:p.namespace,r=[],k?r=n:(d&&(e=function(){var a={namespace:p.namespace,module:b,instance:s};a.UID=[a.namespace,a.module,a.instance].join("/");return function(){return a}}(),d=d(e),d.identify=e,r.push(d)),n&&r.push(n),m>r.length&&r.push(p.data?
+g.merge(p.deepCopy,{},p.data,h.data):h.data),m>r.length&&r.push(g.merge(p.deepCopy,{},p,h))),p.exports=q?h.factory.apply(p.context,r)||{}:h.factory,void 0===h.exports&&(h.exports=l?p.exports?g.merge(!0,{},l,p.exports):l:p.exports);h.state=3;if(a[b])for(;a[b].length;)a[b].pop()(b)}function d(d,f){var l,h=f.length,q,m,k=[],r={},n,x=[],p,t=void 0!==c[d].timeout?c[d].timeout:u.timeout,s=function(a){1===c[d].state&&(k.push(a),--h||(t&&clearTimeout(p),b(d)))};for(l=0;n=f[l++];)if(q=c[n],!q||1===q.state)(a[n]=
+a[n]||[]).push(s),q||((m=u.paths[n])?g.is(m,"array")&&(m=m[0]):m=n,m=v(m),r[m]=n,x.push(m));else if(3===q.state)k.push(n);else if(2===q.state)throw Error('Could not instantiate "'+d+'"; dependency "'+n+'" has been rejected.');if(!((h=f.length)-k.length))return b(d);0<x.length&&D(x).fail(function(a,b){c[d].state=2;t&&clearTimeout(p);throw Error('Could not instantiate "'+d+'"; an error occured while trying to load "'+b+'".');}).progress(function(a){(a=r[a])&&!c[a]&&e(w,a)});t&&(p=setTimeout(function(){var a,
+b,e=0,g=0;for(c[d].state=2;a=f[e++];)for(g=0;b=k[g++];)a===b&&(k.splice(--g,1),f.splice(--e,1));throw Error("Load timeout of "+t+'ms exceeded for module "'+d+'". Missing "'+f.join('", "')+'".');},t))}function e(){var a=0,f,B,h,q,m=!1,k,r,n,x;if(arguments[0]!==w){for(;(n=arguments[a++])&&(_type=(typeof n)[0]);)if("s"===_type?k?x=1:k=n:"f"===_type?q?r?x=1:r=n:q=n:"o"===_type&&(g.is(n,"array")?(f={requires:n},m=!0):f?q=n:f=n),x)throw new TypeError("define called with unrecognized signature; `"+Array.prototype.join.call(arguments,
+", ")+"`.");f=f||{};k=k||f.UID;q=q||f.factory;r=r||f.onRejected}else{if(!(a=l.pop()))throw Error("Inconsistent naming queue");k=arguments[1];f=a[0];q=a[1];r=a[3]}if(!q)if(f)q=f,f={};else throw Error('Missing factory for module "'+k+'"');if(c[k])throw Error('Duplicate entry for module "'+k+'"');if(k)if(a=c[k]=f,a.UID=k,a.amdStyle=m,a.factory=q,a.state=1,a.requires=g.is(a.requires,"array")&&a.requires,a.inherits=g.is(a.inherits,"array")&&a.inherits,a.compiles=g.is(a.compiles,"array")&&a.compiles,a.instances||
+(a.instances={instance1:{}}),a.requires||a.compiles||a.inherits){m=[a.requires,a.inherits,a.compiles];m=[];a.requires&&z.apply(m,a.requires);a.inherits&&z.apply(m,a.inherits);a.compiles&&z.apply(m,a.compiles);for(a=0;f=m[a++];)if(c[f]&&3===c[f].state)m.splice(--a,1);else if(B=u.shim[f])h=v(u.paths[f]||h),q=g.is(B.exports,"function")?B.exports:function(){return window[B.exports]},D([h]).done(function(a,b){return function(){e(a,b)}}(f,q));m.length?g.subordinate(k,m,d):b(k)}else b(k);else l.unshift([f,
+q,r])}var c={},a={},l=[],w={sentinel:1},z=Array.prototype.push,v=function(a){var b=a.toLowerCase(),c=b[0];"/"===c||"\\"===c||"h"===c&&(y[b.slice(0,7)]||y[b.slice(0,8)])||"f"===c&&y[b.slice(0,7)]||(a=u.baseUrl+a);".js"!==a.slice(-3)&&(a+=".js");return a};e.amd={};return e}();E("fandango",function(){return g});var F=l.fandango,G=l.define,v={fn:g,define:E,require:function(f){var b,d,e,c,a=[];for(b=0;d=f[b++];)e=d.toLowerCase(),c=e.slice(0,1),(isAbsolutePath=!!("/"===c||"\\"===c||"h"===c&&(y[e.slice(0,
+7)]||y[e.slice(0,8)])||"f"===c&&y[e.slice(0,7)]))||(d=u.baseUrl+d),e.slice(-3)&&(d+=".js"),a.push(d);return D(a)},noConflict:function(f){l.define===E&&(l.define=G);f&&l.fandango===v&&(l.fandango=F);return v},defaults:function(f){return f?g.merge(u,f):u}};l.define||(l.define=v.define);l.require||(l.require=v.require);l.fandango=v})(this);
\ No newline at end of file
diff --git a/public/js/vendor/socket.io.v0.9.16.min.js b/public/js/vendor/socket.io.v0.9.16.min.js
new file mode 100644
index 0000000..d5067f0
--- /dev/null
+++ b/public/js/vendor/socket.io.v0.9.16.min.js
@@ -0,0 +1,2 @@
+/*! Socket.IO.min.js build:0.9.16, production. Copyright(c) 2011 LearnBoost <dev@learnboost.com> MIT Licensed */
+var io="undefined"==typeof module?{}:module.exports;(function(){(function(a,b){var c=a;c.version="0.9.16",c.protocol=1,c.transports=[],c.j=[],c.sockets={},c.connect=function(a,d){var e=c.util.parseUri(a),f,g;b&&b.location&&(e.protocol=e.protocol||b.location.protocol.slice(0,-1),e.host=e.host||(b.document?b.document.domain:b.location.hostname),e.port=e.port||b.location.port),f=c.util.uniqueUri(e);var h={host:e.host,secure:"https"==e.protocol,port:e.port||("https"==e.protocol?443:80),query:e.query||""};c.util.merge(h,d);if(h["force new connection"]||!c.sockets[f])g=new c.Socket(h);return!h["force new connection"]&&g&&(c.sockets[f]=g),g=g||c.sockets[f],g.of(e.path.length>1?e.path:"")}})("object"==typeof module?module.exports:this.io={},this),function(a,b){var c=a.util={},d=/^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/,e=["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"];c.parseUri=function(a){var b=d.exec(a||""),c={},f=14;while(f--)c[e[f]]=b[f]||"";return c},c.uniqueUri=function(a){var c=a.protocol,d=a.host,e=a.port;return"document"in b?(d=d||document.domain,e=e||(c=="https"&&document.location.protocol!=="https:"?443:document.location.port)):(d=d||"localhost",!e&&c=="https"&&(e=443)),(c||"http")+"://"+d+":"+(e||80)},c.query=function(a,b){var d=c.chunkQuery(a||""),e=[];c.merge(d,c.chunkQuery(b||""));for(var f in d)d.hasOwnProperty(f)&&e.push(f+"="+d[f]);return e.length?"?"+e.join("&"):""},c.chunkQuery=function(a){var b={},c=a.split("&"),d=0,e=c.length,f;for(;d<e;++d)f=c[d].split("="),f[0]&&(b[f[0]]=f[1]);return b};var f=!1;c.load=function(a){if("document"in b&&document.readyState==="complete"||f)return a();c.on(b,"load",a,!1)},c.on=function(a,b,c,d){a.attachEvent?a.attachEvent("on"+b,c):a.addEventListener&&a.addEventListener(b,c,d)},c.request=function(a){if(a&&"undefined"!=typeof XDomainRequest&&!c.ua.hasCORS)return new XDomainRequest;if("undefined"!=typeof XMLHttpRequest&&(!a||c.ua.hasCORS))return new XMLHttpRequest;if(!a)try{return new(window[["Active"].concat("Object").join("X")])("Microsoft.XMLHTTP")}catch(b){}return null},"undefined"!=typeof window&&c.load(function(){f=!0}),c.defer=function(a){if(!c.ua.webkit||"undefined"!=typeof importScripts)return a();c.load(function(){setTimeout(a,100)})},c.merge=function(b,d,e,f){var g=f||[],h=typeof e=="undefined"?2:e,i;for(i in d)d.hasOwnProperty(i)&&c.indexOf(g,i)<0&&(typeof b[i]!="object"||!h?(b[i]=d[i],g.push(d[i])):c.merge(b[i],d[i],h-1,g));return b},c.mixin=function(a,b){c.merge(a.prototype,b.prototype)},c.inherit=function(a,b){function c(){}c.prototype=b.prototype,a.prototype=new c},c.isArray=Array.isArray||function(a){return Object.prototype.toString.call(a)==="[object Array]"},c.intersect=function(a,b){var d=[],e=a.length>b.length?a:b,f=a.length>b.length?b:a;for(var g=0,h=f.length;g<h;g++)~c.indexOf(e,f[g])&&d.push(f[g]);return d},c.indexOf=function(a,b,c){for(var d=a.length,c=c<0?c+d<0?0:c+d:c||0;c<d&&a[c]!==b;c++);return d<=c?-1:c},c.toArray=function(a){var b=[];for(var c=0,d=a.length;c<d;c++)b.push(a[c]);return b},c.ua={},c.ua.hasCORS="undefined"!=typeof XMLHttpRequest&&function(){try{var a=new XMLHttpRequest}catch(b){return!1}return a.withCredentials!=undefined}(),c.ua.webkit="undefined"!=typeof navigator&&/webkit/i.test(navigator.userAgent),c.ua.iDevice="undefined"!=typeof navigator&&/iPad|iPhone|iPod/i.test(navigator.userAgent)}("undefined"!=typeof io?io:module.exports,this),function(a,b){function c(){}a.EventEmitter=c,c.prototype.on=function(a,c){return this.$events||(this.$events={}),this.$events[a]?b.util.isArray(this.$events[a])?this.$events[a].push(c):this.$events[a]=[this.$events[a],c]:this.$events[a]=c,this},c.prototype.addListener=c.prototype.on,c.prototype.once=function(a,b){function d(){c.removeListener(a,d),b.apply(this,arguments)}var c=this;return d.listener=b,this.on(a,d),this},c.prototype.removeListener=function(a,c){if(this.$events&&this.$events[a]){var d=this.$events[a];if(b.util.isArray(d)){var e=-1;for(var f=0,g=d.length;f<g;f++)if(d[f]===c||d[f].listener&&d[f].listener===c){e=f;break}if(e<0)return this;d.splice(e,1),d.length||delete this.$events[a]}else(d===c||d.listener&&d.listener===c)&&delete this.$events[a]}return this},c.prototype.removeAllListeners=function(a){return a===undefined?(this.$events={},this):(this.$events&&this.$events[a]&&(this.$events[a]=null),this)},c.prototype.listeners=function(a){return this.$events||(this.$events={}),this.$events[a]||(this.$events[a]=[]),b.util.isArray(this.$events[a])||(this.$events[a]=[this.$events[a]]),this.$events[a]},c.prototype.emit=function(a){if(!this.$events)return!1;var c=this.$events[a];if(!c)return!1;var d=Array.prototype.slice.call(arguments,1);if("function"==typeof c)c.apply(this,d);else{if(!b.util.isArray(c))return!1;var e=c.slice();for(var f=0,g=e.length;f<g;f++)e[f].apply(this,d)}return!0}}("undefined"!=typeof io?io:module.exports,"undefined"!=typeof io?io:module.parent.exports),function(exports,nativeJSON){function f(a){return a<10?"0"+a:a}function date(a,b){return isFinite(a.valueOf())?a.getUTCFullYear()+"-"+f(a.getUTCMonth()+1)+"-"+f(a.getUTCDate())+"T"+f(a.getUTCHours())+":"+f(a.getUTCMinutes())+":"+f(a.getUTCSeconds())+"Z":null}function quote(a){return escapable.lastIndex=0,escapable.test(a)?'"'+a.replace(escapable,function(a){var b=meta[a];return typeof b=="string"?b:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+a+'"'}function str(a,b){var c,d,e,f,g=gap,h,i=b[a];i instanceof Date&&(i=date(a)),typeof rep=="function"&&(i=rep.call(b,a,i));switch(typeof i){case"string":return quote(i);case"number":return isFinite(i)?String(i):"null";case"boolean":case"null":return String(i);case"object":if(!i)return"null";gap+=indent,h=[];if(Object.prototype.toString.apply(i)==="[object Array]"){f=i.length;for(c=0;c<f;c+=1)h[c]=str(c,i)||"null";return e=h.length===0?"[]":gap?"[\n"+gap+h.join(",\n"+gap)+"\n"+g+"]":"["+h.join(",")+"]",gap=g,e}if(rep&&typeof rep=="object"){f=rep.length;for(c=0;c<f;c+=1)typeof rep[c]=="string"&&(d=rep[c],e=str(d,i),e&&h.push(quote(d)+(gap?": ":":")+e))}else for(d in i)Object.prototype.hasOwnProperty.call(i,d)&&(e=str(d,i),e&&h.push(quote(d)+(gap?": ":":")+e));return e=h.length===0?"{}":gap?"{\n"+gap+h.join(",\n"+gap)+"\n"+g+"}":"{"+h.join(",")+"}",gap=g,e}}"use strict";if(nativeJSON&&nativeJSON.parse)return exports.JSON={parse:nativeJSON.parse,stringify:nativeJSON.stringify};var JSON=exports.JSON={},cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},rep;JSON.stringify=function(a,b,c){var d;gap="",indent="";if(typeof c=="number")for(d=0;d<c;d+=1)indent+=" ";else typeof c=="string"&&(indent=c);rep=b;if(!b||typeof b=="function"||typeof b=="object"&&typeof b.length=="number")return str("",{"":a});throw new Error("JSON.stringify")},JSON.parse=function(text,reviver){function walk(a,b){var c,d,e=a[b];if(e&&typeof e=="object")for(c in e)Object.prototype.hasOwnProperty.call(e,c)&&(d=walk(e,c),d!==undefined?e[c]=d:delete e[c]);return reviver.call(a,b,e)}var j;text=String(text),cx.lastIndex=0,cx.test(text)&&(text=text.replace(cx,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)}));if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return j=eval("("+text+")"),typeof reviver=="function"?walk({"":j},""):j;throw new SyntaxError("JSON.parse")}}("undefined"!=typeof io?io:module.exports,typeof JSON!="undefined"?JSON:undefined),function(a,b){var c=a.parser={},d=c.packets=["disconnect","connect","heartbeat","message","json","event","ack","error","noop"],e=c.reasons=["transport not supported","client not handshaken","unauthorized"],f=c.advice=["reconnect"],g=b.JSON,h=b.util.indexOf;c.encodePacket=function(a){var b=h(d,a.type),c=a.id||"",i=a.endpoint||"",j=a.ack,k=null;switch(a.type){case"error":var l=a.reason?h(e,a.reason):"",m=a.advice?h(f,a.advice):"";if(l!==""||m!=="")k=l+(m!==""?"+"+m:"");break;case"message":a.data!==""&&(k=a.data);break;case"event":var n={name:a.name};a.args&&a.args.length&&(n.args=a.args),k=g.stringify(n);break;case"json":k=g.stringify(a.data);break;case"connect":a.qs&&(k=a.qs);break;case"ack":k=a.ackId+(a.args&&a.args.length?"+"+g.stringify(a.args):"")}var o=[b,c+(j=="data"?"+":""),i];return k!==null&&k!==undefined&&o.push(k),o.join(":")},c.encodePayload=function(a){var b="";if(a.length==1)return a[0];for(var c=0,d=a.length;c<d;c++){var e=a[c];b+="\ufffd"+e.length+"\ufffd"+a[c]}return b};var i=/([^:]+):([0-9]+)?(\+)?:([^:]+)?:?([\s\S]*)?/;c.decodePacket=function(a){var b=a.match(i);if(!b)return{};var c=b[2]||"",a=b[5]||"",h={type:d[b[1]],endpoint:b[4]||""};c&&(h.id=c,b[3]?h.ack="data":h.ack=!0);switch(h.type){case"error":var b=a.split("+");h.reason=e[b[0]]||"",h.advice=f[b[1]]||"";break;case"message":h.data=a||"";break;case"event":try{var j=g.parse(a);h.name=j.name,h.args=j.args}catch(k){}h.args=h.args||[];break;case"json":try{h.data=g.parse(a)}catch(k){}break;case"connect":h.qs=a||"";break;case"ack":var b=a.match(/^([0-9]+)(\+)?(.*)/);if(b){h.ackId=b[1],h.args=[];if(b[3])try{h.args=b[3]?g.parse(b[3]):[]}catch(k){}}break;case"disconnect":case"heartbeat":}return h},c.decodePayload=function(a){if(a.charAt(0)=="\ufffd"){var b=[];for(var d=1,e="";d<a.length;d++)a.charAt(d)=="\ufffd"?(b.push(c.decodePacket(a.substr(d+1).substr(0,e))),d+=Number(e)+1,e=""):e+=a.charAt(d);return b}return[c.decodePacket(a)]}}("undefined"!=typeof io?io:module.exports,"undefined"!=typeof io?io:module.parent.exports),function(a,b){function c(a,b){this.socket=a,this.sessid=b}a.Transport=c,b.util.mixin(c,b.EventEmitter),c.prototype.heartbeats=function(){return!0},c.prototype.onData=function(a){this.clearCloseTimeout(),(this.socket.connected||this.socket.connecting||this.socket.reconnecting)&&this.setCloseTimeout();if(a!==""){var c=b.parser.decodePayload(a);if(c&&c.length)for(var d=0,e=c.length;d<e;d++)this.onPacket(c[d])}return this},c.prototype.onPacket=function(a){return this.socket.setHeartbeatTimeout(),a.type=="heartbeat"?this.onHeartbeat():(a.type=="connect"&&a.endpoint==""&&this.onConnect(),a.type=="error"&&a.advice=="reconnect"&&(this.isOpen=!1),this.socket.onPacket(a),this)},c.prototype.setCloseTimeout=function(){if(!this.closeTimeout){var a=this;this.closeTimeout=setTimeout(function(){a.onDisconnect()},this.socket.closeTimeout)}},c.prototype.onDisconnect=function(){return this.isOpen&&this.close(),this.clearTimeouts(),this.socket.onDisconnect(),this},c.prototype.onConnect=function(){return this.socket.onConnect(),this},c.prototype.clearCloseTimeout=function(){this.closeTimeout&&(clearTimeout(this.closeTimeout),this.closeTimeout=null)},c.prototype.clearTimeouts=function(){this.clearCloseTimeout(),this.reopenTimeout&&clearTimeout(this.reopenTimeout)},c.prototype.packet=function(a){this.send(b.parser.encodePacket(a))},c.prototype.onHeartbeat=function(a){this.packet({type:"heartbeat"})},c.prototype.onOpen=function(){this.isOpen=!0,this.clearCloseTimeout(),this.socket.onOpen()},c.prototype.onClose=function(){var a=this;this.isOpen=!1,this.socket.onClose(),this.onDisconnect()},c.prototype.prepareUrl=function(){var a=this.socket.options;return this.scheme()+"://"+a.host+":"+a.port+"/"+a.resource+"/"+b.protocol+"/"+this.name+"/"+this.sessid},c.prototype.ready=function(a,b){b.call(this)}}("undefined"!=typeof io?io:module.exports,"undefined"!=typeof io?io:module.parent.exports),function(a,b,c){function d(a){this.options={port:80,secure:!1,document:"document"in c?document:!1,resource:"socket.io",transports:b.transports,"connect timeout":1e4,"try multiple transports":!0,reconnect:!0,"reconnection delay":500,"reconnection limit":Infinity,"reopen delay":3e3,"max reconnection attempts":10,"sync disconnect on unload":!1,"auto connect":!0,"flash policy port":10843,manualFlush:!1},b.util.merge(this.options,a),this.connected=!1,this.open=!1,this.connecting=!1,this.reconnecting=!1,this.namespaces={},this.buffer=[],this.doBuffer=!1;if(this.options["sync disconnect on unload"]&&(!this.isXDomain()||b.util.ua.hasCORS)){var d=this;b.util.on(c,"beforeunload",function(){d.disconnectSync()},!1)}this.options["auto connect"]&&this.connect()}function e(){}a.Socket=d,b.util.mixin(d,b.EventEmitter),d.prototype.of=function(a){return this.namespaces[a]||(this.namespaces[a]=new b.SocketNamespace(this,a),a!==""&&this.namespaces[a].packet({type:"connect"})),this.namespaces[a]},d.prototype.publish=function(){this.emit.apply(this,arguments);var a;for(var b in this.namespaces)this.namespaces.hasOwnProperty(b)&&(a=this.of(b),a.$emit.apply(a,arguments))},d.prototype.handshake=function(a){function f(b){b instanceof Error?(c.connecting=!1,c.onError(b.message)):a.apply(null,b.split(":"))}var c=this,d=this.options,g=["http"+(d.secure?"s":"")+":/",d.host+":"+d.port,d.resource,b.protocol,b.util.query(this.options.query,"t="+ +(new Date))].join("/");if(this.isXDomain()&&!b.util.ua.hasCORS){var h=document.getElementsByTagName("script")[0],i=document.createElement("script");i.src=g+"&jsonp="+b.j.length,h.parentNode.insertBefore(i,h),b.j.push(function(a){f(a),i.parentNode.removeChild(i)})}else{var j=b.util.request();j.open("GET",g,!0),this.isXDomain()&&(j.withCredentials=!0),j.onreadystatechange=function(){j.readyState==4&&(j.onreadystatechange=e,j.status==200?f(j.responseText):j.status==403?c.onError(j.responseText):(c.connecting=!1,!c.reconnecting&&c.onError(j.responseText)))},j.send(null)}},d.prototype.getTransport=function(a){var c=a||this.transports,d;for(var e=0,f;f=c[e];e++)if(b.Transport[f]&&b.Transport[f].check(this)&&(!this.isXDomain()||b.Transport[f].xdomainCheck(this)))return new b.Transport[f](this,this.sessionid);return null},d.prototype.connect=function(a){if(this.connecting)return this;var c=this;return c.connecting=!0,this.handshake(function(d,e,f,g){function h(a){c.transport&&c.transport.clearTimeouts(),c.transport=c.getTransport(a);if(!c.transport)return c.publish("connect_failed");c.transport.ready(c,function(){c.connecting=!0,c.publish("connecting",c.transport.name),c.transport.open(),c.options["connect timeout"]&&(c.connectTimeoutTimer=setTimeout(function(){if(!c.connected){c.connecting=!1;if(c.options["try multiple transports"]){var a=c.transports;while(a.length>0&&a.splice(0,1)[0]!=c.transport.name);a.length?h(a):c.publish("connect_failed")}}},c.options["connect timeout"]))})}c.sessionid=d,c.closeTimeout=f*1e3,c.heartbeatTimeout=e*1e3,c.transports||(c.transports=c.origTransports=g?b.util.intersect(g.split(","),c.options.transports):c.options.transports),c.setHeartbeatTimeout(),h(c.transports),c.once("connect",function(){clearTimeout(c.connectTimeoutTimer),a&&typeof a=="function"&&a()})}),this},d.prototype.setHeartbeatTimeout=function(){clearTimeout(this.heartbeatTimeoutTimer);if(this.transport&&!this.transport.heartbeats())return;var a=this;this.heartbeatTimeoutTimer=setTimeout(function(){a.transport.onClose()},this.heartbeatTimeout)},d.prototype.packet=function(a){return this.connected&&!this.doBuffer?this.transport.packet(a):this.buffer.push(a),this},d.prototype.setBuffer=function(a){this.doBuffer=a,!a&&this.connected&&this.buffer.length&&(this.options.manualFlush||this.flushBuffer())},d.prototype.flushBuffer=function(){this.transport.payload(this.buffer),this.buffer=[]},d.prototype.disconnect=function(){if(this.connected||this.connecting)this.open&&this.of("").packet({type:"disconnect"}),this.onDisconnect("booted");return this},d.prototype.disconnectSync=function(){var a=b.util.request(),c=["http"+(this.options.secure?"s":"")+":/",this.options.host+":"+this.options.port,this.options.resource,b.protocol,"",this.sessionid].join("/")+"/?disconnect=1";a.open("GET",c,!1),a.send(null),this.onDisconnect("booted")},d.prototype.isXDomain=function(){var a=c.location.port||("https:"==c.location.protocol?443:80);return this.options.host!==c.location.hostname||this.options.port!=a},d.prototype.onConnect=function(){this.connected||(this.connected=!0,this.connecting=!1,this.doBuffer||this.setBuffer(!1),this.emit("connect"))},d.prototype.onOpen=function(){this.open=!0},d.prototype.onClose=function(){this.open=!1,clearTimeout(this.heartbeatTimeoutTimer)},d.prototype.onPacket=function(a){this.of(a.endpoint).onPacket(a)},d.prototype.onError=function(a){a&&a.advice&&a.advice==="reconnect"&&(this.connected||this.connecting)&&(this.disconnect(),this.options.reconnect&&this.reconnect()),this.publish("error",a&&a.reason?a.reason:a)},d.prototype.onDisconnect=function(a){var b=this.connected,c=this.connecting;this.connected=!1,this.connecting=!1,this.open=!1;if(b||c)this.transport.close(),this.transport.clearTimeouts(),b&&(this.publish("disconnect",a),"booted"!=a&&this.options.reconnect&&!this.reconnecting&&this.reconnect())},d.prototype.reconnect=function(){function e(){if(a.connected){for(var b in a.namespaces)a.namespaces.hasOwnProperty(b)&&""!==b&&a.namespaces[b].packet({type:"connect"});a.publish("reconnect",a.transport.name,a.reconnectionAttempts)}clearTimeout(a.reconnectionTimer),a.removeListener("connect_failed",f),a.removeListener("connect",f),a.reconnecting=!1,delete a.reconnectionAttempts,delete a.reconnectionDelay,delete a.reconnectionTimer,delete a.redoTransports,a.options["try multiple transports"]=c}function f(){if(!a.reconnecting)return;if(a.connected)return e();if(a.connecting&&a.reconnecting)return a.reconnectionTimer=setTimeout(f,1e3);a.reconnectionAttempts++>=b?a.redoTransports?(a.publish("reconnect_failed"),e()):(a.on("connect_failed",f),a.options["try multiple transports"]=!0,a.transports=a.origTransports,a.transport=a.getTransport(),a.redoTransports=!0,a.connect()):(a.reconnectionDelay<d&&(a.reconnectionDelay*=2),a.connect(),a.publish("reconnecting",a.reconnectionDelay,a.reconnectionAttempts),a.reconnectionTimer=setTimeout(f,a.reconnectionDelay))}this.reconnecting=!0,this.reconnectionAttempts=0,this.reconnectionDelay=this.options["reconnection delay"];var a=this,b=this.options["max reconnection attempts"],c=this.options["try multiple transports"],d=this.options["reconnection limit"];this.options["try multiple transports"]=!1,this.reconnectionTimer=setTimeout(f,this.reconnectionDelay),this.on("connect",f)}}("undefined"!=typeof io?io:module.exports,"undefined"!=typeof io?io:module.parent.exports,this),function(a,b){function c(a,b){this.socket=a,this.name=b||"",this.flags={},this.json=new d(this,"json"),this.ackPackets=0,this.acks={}}function d(a,b){this.namespace=a,this.name=b}a.SocketNamespace=c,b.util.mixin(c,b.EventEmitter),c.prototype.$emit=b.EventEmitter.prototype.emit,c.prototype.of=function(){return this.socket.of.apply(this.socket,arguments)},c.prototype.packet=function(a){return a.endpoint=this.name,this.socket.packet(a),this.flags={},this},c.prototype.send=function(a,b){var c={type:this.flags.json?"json":"message",data:a};return"function"==typeof b&&(c.id=++this.ackPackets,c.ack=!0,this.acks[c.id]=b),this.packet(c)},c.prototype.emit=function(a){var b=Array.prototype.slice.call(arguments,1),c=b[b.length-1],d={type:"event",name:a};return"function"==typeof c&&(d.id=++this.ackPackets,d.ack="data",this.acks[d.id]=c,b=b.slice(0,b.length-1)),d.args=b,this.packet(d)},c.prototype.disconnect=function(){return this.name===""?this.socket.disconnect():(this.packet({type:"disconnect"}),this.$emit("disconnect")),this},c.prototype.onPacket=function(a){function d(){c.packet({type:"ack",args:b.util.toArray(arguments),ackId:a.id})}var c=this;switch(a.type){case"connect":this.$emit("connect");break;case"disconnect":this.name===""?this.socket.onDisconnect(a.reason||"booted"):this.$emit("disconnect",a.reason);break;case"message":case"json":var e=["message",a.data];a.ack=="data"?e.push(d):a.ack&&this.packet({type:"ack",ackId:a.id}),this.$emit.apply(this,e);break;case"event":var e=[a.name].concat(a.args);a.ack=="data"&&e.push(d),this.$emit.apply(this,e);break;case"ack":this.acks[a.ackId]&&(this.acks[a.ackId].apply(this,a.args),delete this.acks[a.ackId]);break;case"error":a.advice?this.socket.onError(a):a.reason=="unauthorized"?this.$emit("connect_failed",a.reason):this.$emit("error",a.reason)}},d.prototype.send=function(){this.namespace.flags[this.name]=!0,this.namespace.send.apply(this.namespace,arguments)},d.prototype.emit=function(){this.namespace.flags[this.name]=!0,this.namespace.emit.apply(this.namespace,arguments)}}("undefined"!=typeof io?io:module.exports,"undefined"!=typeof io?io:module.parent.exports),function(a,b,c){function d(a){b.Transport.apply(this,arguments)}a.websocket=d,b.util.inherit(d,b.Transport),d.prototype.name="websocket",d.prototype.open=function(){var a=b.util.query(this.socket.options.query),d=this,e;return e||(e=c.MozWebSocket||c.WebSocket),this.websocket=new e(this.prepareUrl()+a),this.websocket.onopen=function(){d.onOpen(),d.socket.setBuffer(!1)},this.websocket.onmessage=function(a){d.onData(a.data)},this.websocket.onclose=function(){d.onClose(),d.socket.setBuffer(!0)},this.websocket.onerror=function(a){d.onError(a)},this},b.util.ua.iDevice?d.prototype.send=function(a){var b=this;return setTimeout(function(){b.websocket.send(a)},0),this}:d.prototype.send=function(a){return this.websocket.send(a),this},d.prototype.payload=function(a){for(var b=0,c=a.length;b<c;b++)this.packet(a[b]);return this},d.prototype.close=function(){return this.websocket.close(),this},d.prototype.onError=function(a){this.socket.onError(a)},d.prototype.scheme=function(){return this.socket.options.secure?"wss":"ws"},d.check=function(){return"WebSocket"in c&&!("__addTask"in WebSocket)||"MozWebSocket"in c},d.xdomainCheck=function(){return!0},b.transports.push("websocket")}("undefined"!=typeof io?io.Transport:module.exports,"undefined"!=typeof io?io:module.parent.exports,this),function(a,b){function c(){b.Transport.websocket.apply(this,arguments)}a.flashsocket=c,b.util.inherit(c,b.Transport.websocket),c.prototype.name="flashsocket",c.prototype.open=function(){var a=this,c=arguments;return WebSocket.__addTask(function(){b.Transport.websocket.prototype.open.apply(a,c)}),this},c.prototype.send=function(){var a=this,c=arguments;return WebSocket.__addTask(function(){b.Transport.websocket.prototype.send.apply(a,c)}),this},c.prototype.close=function(){return WebSocket.__tasks.length=0,b.Transport.websocket.prototype.close.call(this),this},c.prototype.ready=function(a,d){function e(){var b=a.options,e=b["flash policy port"],g=["http"+(b.secure?"s":"")+":/",b.host+":"+b.port,b.resource,"static/flashsocket","WebSocketMain"+(a.isXDomain()?"Insecure":"")+".swf"];c.loaded||(typeof WEB_SOCKET_SWF_LOCATION=="undefined"&&(WEB_SOCKET_SWF_LOCATION=g.join("/")),e!==843&&WebSocket.loadFlashPolicyFile("xmlsocket://"+b.host+":"+e),WebSocket.__initialize(),c.loaded=!0),d.call(f)}var f=this;if(document.body)return e();b.util.load(e)},c.check=function(){return typeof WebSocket!="undefined"&&"__initialize"in WebSocket&&!!swfobject?swfobject.getFlashPlayerVersion().major>=10:!1},c.xdomainCheck=function(){return!0},typeof window!="undefined"&&(WEB_SOCKET_DISABLE_AUTO_INITIALIZATION=!0),b.transports.push("flashsocket")}("undefined"!=typeof io?io.Transport:module.exports,"undefined"!=typeof io?io:module.parent.exports);if("undefined"!=typeof window)var swfobject=function(){function A(){if(t)return;try{var a=i.getElementsByTagName("body")[0].appendChild(Q("span"));a.parentNode.removeChild(a)}catch(b){return}t=!0;var c=l.length;for(var d=0;d<c;d++)l[d]()}function B(a){t?a():l[l.length]=a}function C(b){if(typeof h.addEventListener!=a)h.addEventListener("load",b,!1);else if(typeof i.addEventListener!=a)i.addEventListener("load",b,!1);else if(typeof h.attachEvent!=a)R(h,"onload",b);else if(typeof h.onload=="function"){var c=h.onload;h.onload=function(){c(),b()}}else h.onload=b}function D(){k?E():F()}function E(){var c=i.getElementsByTagName("body")[0],d=Q(b);d.setAttribute("type",e);var f=c.appendChild(d);if(f){var g=0;(function(){if(typeof f.GetVariable!=a){var b=f.GetVariable("$version");b&&(b=b.split(" ")[1].split(","),y.pv=[parseInt(b[0],10),parseInt(b[1],10),parseInt(b[2],10)])}else if(g<10){g++,setTimeout(arguments.callee,10);return}c.removeChild(d),f=null,F()})()}else F()}function F(){var b=m.length;if(b>0)for(var c=0;c<b;c++){var d=m[c].id,e=m[c].callbackFn,f={success:!1,id:d};if(y.pv[0]>0){var g=P(d);if(g)if(S(m[c].swfVersion)&&!(y.wk&&y.wk<312))U(d,!0),e&&(f.success=!0,f.ref=G(d),e(f));else if(m[c].expressInstall&&H()){var h={};h.data=m[c].expressInstall,h.width=g.getAttribute("width")||"0",h.height=g.getAttribute("height")||"0",g.getAttribute("class")&&(h.styleclass=g.getAttribute("class")),g.getAttribute("align")&&(h.align=g.getAttribute("align"));var i={},j=g.getElementsByTagName("param"),k=j.length;for(var l=0;l<k;l++)j[l].getAttribute("name").toLowerCase()!="movie"&&(i[j[l].getAttribute("name")]=j[l].getAttribute("value"));I(h,i,d,e)}else J(g),e&&e(f)}else{U(d,!0);if(e){var n=G(d);n&&typeof n.SetVariable!=a&&(f.success=!0,f.ref=n),e(f)}}}}function G(c){var d=null,e=P(c);if(e&&e.nodeName=="OBJECT")if(typeof e.SetVariable!=a)d=e;else{var f=e.getElementsByTagName(b)[0];f&&(d=f)}return d}function H(){return!u&&S("6.0.65")&&(y.win||y.mac)&&!(y.wk&&y.wk<312)}function I(b,c,d,e){u=!0,r=e||null,s={success:!1,id:d};var g=P(d);if(g){g.nodeName=="OBJECT"?(p=K(g),q=null):(p=g,q=d),b.id=f;if(typeof b.width==a||!/%$/.test(b.width)&&parseInt(b.width,10)<310)b.width="310";if(typeof b.height==a||!/%$/.test(b.height)&&parseInt(b.height,10)<137)b.height="137";i.title=i.title.slice(0,47)+" - Flash Player Installation";var j=y.ie&&y.win?["Active"].concat("").join("X"):"PlugIn",k="MMredirectURL="+h.location.toString().replace(/&/g,"%26")+"&MMplayerType="+j+"&MMdoctitle="+i.title;typeof c.flashvars!=a?c.flashvars+="&"+k:c.flashvars=k;if(y.ie&&y.win&&g.readyState!=4){var l=Q("div");d+="SWFObjectNew",l.setAttribute("id",d),g.parentNode.insertBefore(l,g),g.style.display="none",function(){g.readyState==4?g.parentNode.removeChild(g):setTimeout(arguments.callee,10)}()}L(b,c,d)}}function J(a){if(y.ie&&y.win&&a.readyState!=4){var b=Q("div");a.parentNode.insertBefore(b,a),b.parentNode.replaceChild(K(a),b),a.style.display="none",function(){a.readyState==4?a.parentNode.removeChild(a):setTimeout(arguments.callee,10)}()}else a.parentNode.replaceChild(K(a),a)}function K(a){var c=Q("div");if(y.win&&y.ie)c.innerHTML=a.innerHTML;else{var d=a.getElementsByTagName(b)[0];if(d){var e=d.childNodes;if(e){var f=e.length;for(var g=0;g<f;g++)(e[g].nodeType!=1||e[g].nodeName!="PARAM")&&e[g].nodeType!=8&&c.appendChild(e[g].cloneNode(!0))}}}return c}function L(c,d,f){var g,h=P(f);if(y.wk&&y.wk<312)return g;if(h){typeof c.id==a&&(c.id=f);if(y.ie&&y.win){var i="";for(var j in c)c[j]!=Object.prototype[j]&&(j.toLowerCase()=="data"?d.movie=c[j]:j.toLowerCase()=="styleclass"?i+=' class="'+c[j]+'"':j.toLowerCase()!="classid"&&(i+=" "+j+'="'+c[j]+'"'));var k="";for(var l in d)d[l]!=Object.prototype[l]&&(k+='<param name="'+l+'" value="'+d[l]+'" />');h.outerHTML='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'+i+">"+k+"</object>",n[n.length]=c.id,g=P(c.id)}else{var m=Q(b);m.setAttribute("type",e);for(var o in c)c[o]!=Object.prototype[o]&&(o.toLowerCase()=="styleclass"?m.setAttribute("class",c[o]):o.toLowerCase()!="classid"&&m.setAttribute(o,c[o]));for(var p in d)d[p]!=Object.prototype[p]&&p.toLowerCase()!="movie"&&M(m,p,d[p]);h.parentNode.replaceChild(m,h),g=m}}return g}function M(a,b,c){var d=Q("param");d.setAttribute("name",b),d.setAttribute("value",c),a.appendChild(d)}function N(a){var b=P(a);b&&b.nodeName=="OBJECT"&&(y.ie&&y.win?(b.style.display="none",function(){b.readyState==4?O(a):setTimeout(arguments.callee,10)}()):b.parentNode.removeChild(b))}function O(a){var b=P(a);if(b){for(var c in b)typeof b[c]=="function"&&(b[c]=null);b.parentNode.removeChild(b)}}function P(a){var b=null;try{b=i.getElementById(a)}catch(c){}return b}function Q(a){return i.createElement(a)}function R(a,b,c){a.attachEvent(b,c),o[o.length]=[a,b,c]}function S(a){var b=y.pv,c=a.split(".");return c[0]=parseInt(c[0],10),c[1]=parseInt(c[1],10)||0,c[2]=parseInt(c[2],10)||0,b[0]>c[0]||b[0]==c[0]&&b[1]>c[1]||b[0]==c[0]&&b[1]==c[1]&&b[2]>=c[2]?!0:!1}function T(c,d,e,f){if(y.ie&&y.mac)return;var g=i.getElementsByTagName("head")[0];if(!g)return;var h=e&&typeof e=="string"?e:"screen";f&&(v=null,w=null);if(!v||w!=h){var j=Q("style");j.setAttribute("type","text/css"),j.setAttribute("media",h),v=g.appendChild(j),y.ie&&y.win&&typeof i.styleSheets!=a&&i.styleSheets.length>0&&(v=i.styleSheets[i.styleSheets.length-1]),w=h}y.ie&&y.win?v&&typeof v.addRule==b&&v.addRule(c,d):v&&typeof i.createTextNode!=a&&v.appendChild(i.createTextNode(c+" {"+d+"}"))}function U(a,b){if(!x)return;var c=b?"visible":"hidden";t&&P(a)?P(a).style.visibility=c:T("#"+a,"visibility:"+c)}function V(b){var c=/[\\\"<>\.;]/,d=c.exec(b)!=null;return d&&typeof encodeURIComponent!=a?encodeURIComponent(b):b}var a="undefined",b="object",c="Shockwave Flash",d="ShockwaveFlash.ShockwaveFlash",e="application/x-shockwave-flash",f="SWFObjectExprInst",g="onreadystatechange",h=window,i=document,j=navigator,k=!1,l=[D],m=[],n=[],o=[],p,q,r,s,t=!1,u=!1,v,w,x=!0,y=function(){var f=typeof i.getElementById!=a&&typeof i.getElementsByTagName!=a&&typeof i.createElement!=a,g=j.userAgent.toLowerCase(),l=j.platform.toLowerCase(),m=l?/win/.test(l):/win/.test(g),n=l?/mac/.test(l):/mac/.test(g),o=/webkit/.test(g)?parseFloat(g.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):!1,p=!1,q=[0,0,0],r=null;if(typeof j.plugins!=a&&typeof j.plugins[c]==b)r=j.plugins[c].description,r&&(typeof j.mimeTypes==a||!j.mimeTypes[e]||!!j.mimeTypes[e].enabledPlugin)&&(k=!0,p=!1,r=r.replace(/^.*\s+(\S+\s+\S+$)/,"$1"),q[0]=parseInt(r.replace(/^(.*)\..*$/,"$1"),10),q[1]=parseInt(r.replace(/^.*\.(.*)\s.*$/,"$1"),10),q[2]=/[a-zA-Z]/.test(r)?parseInt(r.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0);else if(typeof h[["Active"].concat("Object").join("X")]!=a)try{var s=new(window[["Active"].concat("Object").join("X")])(d);s&&(r=s.GetVariable("$version"),r&&(p=!0,r=r.split(" ")[1].split(","),q=[parseInt(r[0],10),parseInt(r[1],10),parseInt(r[2],10)]))}catch(t){}return{w3:f,pv:q,wk:o,ie:p,win:m,mac:n}}(),z=function(){if(!y.w3)return;(typeof i.readyState!=a&&i.readyState=="complete"||typeof i.readyState==a&&(i.getElementsByTagName("body")[0]||i.body))&&A(),t||(typeof i.addEventListener!=a&&i.addEventListener("DOMContentLoaded",A,!1),y.ie&&y.win&&(i.attachEvent(g,function(){i.readyState=="complete"&&(i.detachEvent(g,arguments.callee),A())}),h==top&&function(){if(t)return;try{i.documentElement.doScroll("left")}catch(a){setTimeout(arguments.callee,0);return}A()}()),y.wk&&function(){if(t)return;if(!/loaded|complete/.test(i.readyState)){setTimeout(arguments.callee,0);return}A()}(),C(A))}(),W=function(){y.ie&&y.win&&window.attachEvent("onunload",function(){var a=o.length;for(var b=0;b<a;b++)o[b][0].detachEvent(o[b][1],o[b][2]);var c=n.length;for(var d=0;d<c;d++)N(n[d]);for(var e in y)y[e]=null;y=null;for(var f in swfobject)swfobject[f]=null;swfobject=null})}();return{registerObject:function(a,b,c,d){if(y.w3&&a&&b){var e={};e.id=a,e.swfVersion=b,e.expressInstall=c,e.callbackFn=d,m[m.length]=e,U(a,!1)}else d&&d({success:!1,id:a})},getObjectById:function(a){if(y.w3)return G(a)},embedSWF:function(c,d,e,f,g,h,i,j,k,l){var m={success:!1,id:d};y.w3&&!(y.wk&&y.wk<312)&&c&&d&&e&&f&&g?(U(d,!1),B(function(){e+="",f+="";var n={};if(k&&typeof k===b)for(var o in k)n[o]=k[o];n.data=c,n.width=e,n.height=f;var p={};if(j&&typeof j===b)for(var q in j)p[q]=j[q];if(i&&typeof i===b)for(var r in i)typeof p.flashvars!=a?p.flashvars+="&"+r+"="+i[r]:p.flashvars=r+"="+i[r];if(S(g)){var s=L(n,p,d);n.id==d&&U(d,!0),m.success=!0,m.ref=s}else{if(h&&H()){n.data=h,I(n,p,d,l);return}U(d,!0)}l&&l(m)})):l&&l(m)},switchOffAutoHideShow:function(){x=!1},ua:y,getFlashPlayerVersion:function(){return{major:y.pv[0],minor:y.pv[1],release:y.pv[2]}},hasFlashPlayerVersion:S,createSWF:function(a,b,c){return y.w3?L(a,b,c):undefined},showExpressInstall:function(a,b,c,d){y.w3&&H()&&I(a,b,c,d)},removeSWF:function(a){y.w3&&N(a)},createCSS:function(a,b,c,d){y.w3&&T(a,b,c,d)},addDomLoadEvent:B,addLoadEvent:C,getQueryParamValue:function(a){var b=i.location.search||i.location.hash;if(b){/\?/.test(b)&&(b=b.split("?")[1]);if(a==null)return V(b);var c=b.split("&");for(var d=0;d<c.length;d++)if(c[d].substring(0,c[d].indexOf("="))==a)return V(c[d].substring(c[d].indexOf("=")+1))}return""},expressInstallCallback:function(){if(u){var a=P(f);a&&p&&(a.parentNode.replaceChild(p,a),q&&(U(q,!0),y.ie&&y.win&&(p.style.display="block")),r&&r(s)),u=!1}}}}();(function(){if("undefined"==typeof window||window.WebSocket)return;var a=window.console;if(!a||!a.log||!a.error)a={log:function(){},error:function(){}};if(!swfobject.hasFlashPlayerVersion("10.0.0")){a.error("Flash Player >= 10.0.0 is required.");return}location.protocol=="file:"&&a.error("WARNING: web-socket-js doesn't work in file:///... URL unless you set Flash Security Settings properly. Open the page via Web server i.e. http://..."),WebSocket=function(a,b,c,d,e){var f=this;f.__id=WebSocket.__nextId++,WebSocket.__instances[f.__id]=f,f.readyState=WebSocket.CONNECTING,f.bufferedAmount=0,f.__events={},b?typeof b=="string"&&(b=[b]):b=[],setTimeout(function(){WebSocket.__addTask(function(){WebSocket.__flash.create(f.__id,a,b,c||null,d||0,e||null)})},0)},WebSocket.prototype.send=function(a){if(this.readyState==WebSocket.CONNECTING)throw"INVALID_STATE_ERR: Web Socket connection has not been established";var b=WebSocket.__flash.send(this.__id,encodeURIComponent(a));return b<0?!0:(this.bufferedAmount+=b,!1)},WebSocket.prototype.close=function(){if(this.readyState==WebSocket.CLOSED||this.readyState==WebSocket.CLOSING)return;this.readyState=WebSocket.CLOSING,WebSocket.__flash.close(this.__id)},WebSocket.prototype.addEventListener=function(a,b,c){a in this.__events||(this.__events[a]=[]),this.__events[a].push(b)},WebSocket.prototype.removeEventListener=function(a,b,c){if(!(a in this.__events))return;var d=this.__events[a];for(var e=d.length-1;e>=0;--e)if(d[e]===b){d.splice(e,1);break}},WebSocket.prototype.dispatchEvent=function(a){var b=this.__events[a.type]||[];for(var c=0;c<b.length;++c)b[c](a);var d=this["on"+a.type];d&&d(a)},WebSocket.prototype.__handleEvent=function(a){"readyState"in a&&(this.readyState=a.readyState),"protocol"in a&&(this.protocol=a.protocol);var b;if(a.type=="open"||a.type=="error")b=this.__createSimpleEvent(a.type);else if(a.type=="close")b=this.__createSimpleEvent("close");else{if(a.type!="message")throw"unknown event type: "+a.type;var c=decodeURIComponent(a.message);b=this.__createMessageEvent("message",c)}this.dispatchEvent(b)},WebSocket.prototype.__createSimpleEvent=function(a){if(document.createEvent&&window.Event){var b=document.createEvent("Event");return b.initEvent(a,!1,!1),b}return{type:a,bubbles:!1,cancelable:!1}},WebSocket.prototype.__createMessageEvent=function(a,b){if(document.createEvent&&window.MessageEvent&&!window.opera){var c=document.createEvent("MessageEvent");return c.initMessageEvent("message",!1,!1,b,null,null,window,null),c}return{type:a,data:b,bubbles:!1,cancelable:!1}},WebSocket.CONNECTING=0,WebSocket.OPEN=1,WebSocket.CLOSING=2,WebSocket.CLOSED=3,WebSocket.__flash=null,WebSocket.__instances={},WebSocket.__tasks=[],WebSocket.__nextId=0,WebSocket.loadFlashPolicyFile=function(a){WebSocket.__addTask(function(){WebSocket.__flash.loadManualPolicyFile(a)})},WebSocket.__initialize=function(){if(WebSocket.__flash)return;WebSocket.__swfLocation&&(window.WEB_SOCKET_SWF_LOCATION=WebSocket.__swfLocation);if(!window.WEB_SOCKET_SWF_LOCATION){a.error("[WebSocket] set WEB_SOCKET_SWF_LOCATION to location of WebSocketMain.swf");return}var b=document.createElement("div");b.id="webSocketContainer",b.style.position="absolute",WebSocket.__isFlashLite()?(b.style.left="0px",b.style.top="0px"):(b.style.left="-100px",b.style.top="-100px");var c=document.createElement("div");c.id="webSocketFlash",b.appendChild(c),document.body.appendChild(b),swfobject.embedSWF(WEB_SOCKET_SWF_LOCATION,"webSocketFlash","1","1","10.0.0",null,null,{hasPriority:!0,swliveconnect:!0,allowScriptAccess:"always"},null,function(b){b.success||a.error("[WebSocket] swfobject.embedSWF failed")})},WebSocket.__onFlashInitialized=function(){setTimeout(function(){WebSocket.__flash=document.getElementById("webSocketFlash"),WebSocket.__flash.setCallerUrl(location.href),WebSocket.__flash.setDebug(!!window.WEB_SOCKET_DEBUG);for(var a=0;a<WebSocket.__tasks.length;++a)WebSocket.__tasks[a]();WebSocket.__tasks=[]},0)},WebSocket.__onFlashEvent=function(){return setTimeout(function(){try{var b=WebSocket.__flash.receiveEvents();for(var c=0;c<b.length;++c)WebSocket.__instances[b[c].webSocketId].__handleEvent(b[c])}catch(d){a.error(d)}},0),!0},WebSocket.__log=function(b){a.log(decodeURIComponent(b))},WebSocket.__error=function(b){a.error(decodeURIComponent(b))},WebSocket.__addTask=function(a){WebSocket.__flash?a():WebSocket.__tasks.push(a)},WebSocket.__isFlashLite=function(){if(!window.navigator||!window.navigator.mimeTypes)return!1;var a=window.navigator.mimeTypes["application/x-shockwave-flash"];return!a||!a.enabledPlugin||!a.enabledPlugin.filename?!1:a.enabledPlugin.filename.match(/flashlite/i)?!0:!1},window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION||(window.addEventListener?window.addEventListener("load",function(){WebSocket.__initialize()},!1):window.attachEvent("onload",function(){WebSocket.__initialize()}))})(),function(a,b,c){function d(a){if(!a)return;b.Transport.apply(this,arguments),this.sendBuffer=[]}function e(){}a.XHR=d,b.util.inherit(d,b.Transport),d.prototype.open=function(){return this.socket.setBuffer(!1),this.onOpen(),this.get(),this.setCloseTimeout(),this},d.prototype.payload=function(a){var c=[];for(var d=0,e=a.length;d<e;d++)c.push(b.parser.encodePacket(a[d]));this.send(b.parser.encodePayload(c))},d.prototype.send=function(a){return this.post(a),this},d.prototype.post=function(a){function d(){this.readyState==4&&(this.onreadystatechange=e,b.posting=!1,this.status==200?b.socket.setBuffer(!1):b.onClose())}function f(){this.onload=e,b.socket.setBuffer(!1)}var b=this;this.socket.setBuffer(!0),this.sendXHR=this.request("POST"),c.XDomainRequest&&this.sendXHR instanceof XDomainRequest?this.sendXHR.onload=this.sendXHR.onerror=f:this.sendXHR.onreadystatechange=d,this.sendXHR.send(a)},d.prototype.close=function(){return this.onClose(),this},d.prototype.request=function(a){var c=b.util.request(this.socket.isXDomain()),d=b.util.query(this.socket.options.query,"t="+ +(new Date));c.open(a||"GET",this.prepareUrl()+d,!0);if(a=="POST")try{c.setRequestHeader?c.setRequestHeader("Content-type","text/plain;charset=UTF-8"):c.contentType="text/plain"}catch(e){}return c},d.prototype.scheme=function(){return this.socket.options.secure?"https":"http"},d.check=function(a,d){try{var e=b.util.request(d),f=c.XDomainRequest&&e instanceof XDomainRequest,g=a&&a.options&&a.options.secure?"https:":"http:",h=c.location&&g!=c.location.protocol;if(e&&(!f||!h))return!0}catch(i){}return!1},d.xdomainCheck=function(a){return d.check(a,!0)}}("undefined"!=typeof io?io.Transport:module.exports,"undefined"!=typeof io?io:module.parent.exports,this),function(a,b){function c(a){b.Transport.XHR.apply(this,arguments)}a.htmlfile=c,b.util.inherit(c,b.Transport.XHR),c.prototype.name="htmlfile",c.prototype.get=function(){this.doc=new(window[["Active"].concat("Object").join("X")])("htmlfile"),this.doc.open(),this.doc.write("<html></html>"),this.doc.close(),this.doc.parentWindow.s=this;var a=this.doc.createElement("div");a.className="socketio",this.doc.body.appendChild(a),this.iframe=this.doc.createElement("iframe"),a.appendChild(this.iframe);var c=this,d=b.util.query(this.socket.options.query,"t="+ +(new Date));this.iframe.src=this.prepareUrl()+d,b.util.on(window,"unload",function(){c.destroy()})},c.prototype._=function(a,b){a=a.replace(/\\\//g,"/"),this.onData(a);try{var c=b.getElementsByTagName("script")[0];c.parentNode.removeChild(c)}catch(d){}},c.prototype.destroy=function(){if(this.iframe){try{this.iframe.src="about:blank"}catch(a){}this.doc=null,this.iframe.parentNode.removeChild(this.iframe),this.iframe=null,CollectGarbage()}},c.prototype.close=function(){return this.destroy(),b.Transport.XHR.prototype.close.call(this)},c.check=function(a){if(typeof window!="undefined"&&["Active"].concat("Object").join("X")in window)try{var c=new(window[["Active"].concat("Object").join("X")])("htmlfile");return c&&b.Transport.XHR.check(a)}catch(d){}return!1},c.xdomainCheck=function(){return!1},b.transports.push("htmlfile")}("undefined"!=typeof io?io.Transport:module.exports,"undefined"!=typeof io?io:module.parent.exports),function(a,b,c){function d(){b.Transport.XHR.apply(this,arguments)}function e(){}a["xhr-polling"]=d,b.util.inherit(d,b.Transport.XHR),b.util.merge(d,b.Transport.XHR),d.prototype.name="xhr-polling",d.prototype.heartbeats=function(){return!1},d.prototype.open=function(){var a=this;return b.Transport.XHR.prototype.open.call(a),!1},d.prototype.get=function(){function b(){this.readyState==4&&(this.onreadystatechange=e,this.status==200?(a.onData(this.responseText),a.get()):a.onClose())}function d(){this.onload=e,this.onerror=e,a.retryCounter=1,a.onData(this.responseText),a.get()}function f(){a.retryCounter++,!a.retryCounter||a.retryCounter>3?a.onClose():a.get()}if(!this.isOpen)return;var a=this;this.xhr=this.request(),c.XDomainRequest&&this.xhr instanceof XDomainRequest?(this.xhr.onload=d,this.xhr.onerror=f):this.xhr.onreadystatechange=b,this.xhr.send(null)},d.prototype.onClose=function(){b.Transport.XHR.prototype.onClose.call(this);if(this.xhr){this.xhr.onreadystatechange=this.xhr.onload=this.xhr.onerror=e;try{this.xhr.abort()}catch(a){}this.xhr=null}},d.prototype.ready=function(a,c){var d=this;b.util.defer(function(){c.call(d)})},b.transports.push("xhr-polling")}("undefined"!=typeof io?io.Transport:module.exports,"undefined"!=typeof io?io:module.parent.exports,this),function(a,b,c){function e(a){b.Transport["xhr-polling"].apply(this,arguments),this.index=b.j.length;var c=this;b.j.push(function(a){c._(a)})}var d=c.document&&"MozAppearance"in c.document.documentElement.style;a["jsonp-polling"]=e,b.util.inherit(e,b.Transport["xhr-polling"]),e.prototype.name="jsonp-polling",e.prototype.post=function(a){function i(){j(),c.socket.setBuffer(!1)}function j(){c.iframe&&c.form.removeChild(c.iframe);try{h=document.createElement('<iframe name="'+c.iframeId+'">')}catch(a){h=document.createElement("iframe"),h.name=c.iframeId}h.id=c.iframeId,c.form.appendChild(h),c.iframe=h}var c=this,d=b.util.query(this.socket.options.query,"t="+ +(new Date)+"&i="+this.index);if(!this.form){var e=document.createElement("form"),f=document.createElement("textarea"),g=this.iframeId="socketio_iframe_"+this.index,h;e.className="socketio",e.style.position="absolute",e.style.top="0px",e.style.left="0px",e.style.display="none",e.target=g,e.method="POST",e.setAttribute("accept-charset","utf-8"),f.name="d",e.appendChild(f),document.body.appendChild(e),this.form=e,this.area=f}this.form.action=this.prepareUrl()+d,j(),this.area.value=b.JSON.stringify(a);try{this.form.submit()}catch(k){}this.iframe.attachEvent?h.onreadystatechange=function(){c.iframe.readyState=="complete"&&i()}:this.iframe.onload=i,this.socket.setBuffer(!0)},e.prototype.get=function(){var a=this,c=document.createElement("script"),e=b.util.query(this.socket.options.query,"t="+ +(new Date)+"&i="+this.index);this.script&&(this.script.parentNode.removeChild(this.script),this.script=null),c.async=!0,c.src=this.prepareUrl()+e,c.onerror=function(){a.onClose()};var f=document.getElementsByTagName("script")[0];f.parentNode.insertBefore(c,f),this.script=c,d&&setTimeout(function(){var a=document.createElement("iframe");document.body.appendChild(a),document.body.removeChild(a)},100)},e.prototype._=function(a){return this.onData(a),this.isOpen&&this.get(),this},e.prototype.ready=function(a,c){var e=this;if(!d)return c.call(this);b.util.load(function(){c.call(e)})},e.check=function(){return"document"in c},e.xdomainCheck=function(){return!0},b.transports.push("jsonp-polling")}("undefined"!=typeof io?io.Transport:module.exports,"undefined"!=typeof io?io:module.parent.exports,this),typeof define=="function"&&define.amd&&define([],function(){return io})})()
\ No newline at end of file
diff --git a/server.js b/server.js
new file mode 100644
index 0000000..52f8889
--- /dev/null
+++ b/server.js
@@ -0,0 +1,39 @@
+var express = require('express.io'),
+    uuid = require('node-uuid');
+
+app = express();app.http().io();
+
+app.use(express.static(__dirname + '/public'));
+
+app.io.route('room', {
+    create: function(req) {
+      var room = uuid.v4();
+      req.socket.emit('message:server', 'Room ' + room + ' created');
+      req.socket.emit('room:created',room);
+    },
+    join: function(req) {
+      if(req.data) {
+        req.socket.emit('room:joined',req.data);
+        req.socket.join(req.data);
+        req.socket.broadcast.to(req.data).emit('message:server', 'A person joined this room');
+      }
+    },
+    leave: function(req) {
+      if(req.data) {
+        req.socket.emit('room:left');
+        req.socket.leave(req.data);
+        req.socket.broadcast.to(req.data).emit('message:server', 'A person left this room');
+      }
+    } 
+});
+
+app.io.route('message', {
+    send: function(req) {
+      if(req.data && req.data.room) req.socket.broadcast.to(req.data.room).emit('message:send', req.data.msg);
+      req.socket.emit('message:send', req.data.msg);
+    }
+});
+
+app.listen(8080, function(){
+  console.log('listening on *:8080');
+});
\ No newline at end of file