Adam Ahmed avatar Adam Ahmed committed 8d237c3

Make JSHint pass. Convert indentation to spaces. Add evil:true exception for configHanlder when evaling the config JSONish.

Comments (0)

Files changed (18)

 /node_modules/
-/lib/gapps/data/

etc/googleRequest.js

 var clientSecret = 'Pj1DAnZGNn8mfHMllmyrcKex';
 
 tokenHandler.getTokenData(clientId, clientSecret, function(err, tokenData) {
-	var calApi = gcal(tokenData, require('winston'));
+    var calApi = gcal(tokenData, require('winston'));
 /*
-	calApi.getRooms(function(err, res) {
-		console.log(res);
-	});*/
+    calApi.getRooms(function(err, res) {
+        console.log(res);
+    });*/
 
-	var zoolander = 'atlassian.com_2d31363935383530352d343335@resource.calendar.google.com';
-	calApi.getUpcomingEvents(zoolander,
-		new Date(2012, 8, 26),
-		function(err, res) {
-		res.items.forEach(function(item) {
-			console.dir(item);
-		});
-	});
-});
+    var zoolander = 'atlassian.com_2d31363935383530352d343335@resource.calendar.google.com';
+    calApi.getUpcomingEvents(zoolander,
+        new Date(2012, 8, 26),
+        function(err, res) {
+        res.items.forEach(function(item) {
+            console.dir(item);
+        });
+    });
+});
 var logger, clientLogger, datasource;
 
 function getDatasource(config, logger) {
-	var datasourceName = config.options.datasource;
-	var datasourceConfig = extend(true, {
-		logger : logger,
-		dataDirectory :  path.join(config.directories.data, datasourceName),
-		roomFilter : config.options.rooms.filter
-	}, config.options[datasourceName]);
+    var datasourceName = config.options.datasource;
+    var datasourceConfig = extend(true, {
+        logger : logger,
+        dataDirectory :  path.join(config.directories.data, datasourceName),
+        roomFilter : config.options.rooms.filter
+    }, config.options[datasourceName]);
 
-	if (datasourceName === 'gapps') {
-		return require('./' + datasourceName + '/datasource')(datasourceConfig);	
-	} else {
-		throw new Error('Google Apps (gapps) is currently the only supported datasource.');
-	}	
+    if (datasourceName === 'gapps') {
+        return require('./' + datasourceName + '/datasource')(datasourceConfig);    
+    } else {
+        throw new Error('Google Apps (gapps) is currently the only supported datasource.');
+    }
 }
 
 logger = logHandler.getLogger(config);
 datasource = getDatasource(config, logger);
 
 function logError(e) {
-	var stack;
-	if (e && e.stack) {
-		// remove the message from the stack
-		stack = e.stack.substring(e.stack.indexOf('\n') + 1);
-	} else {
-		// remove this function and the message from the stack.
-		stack = (new Error(e).stack);
-		stack = stack.substring(stack.indexOf('\n', stack.indexOf('\n') + 1) + 1);	
-	}
+    var stack;
+    if (e && e.stack) {
+        // remove the message from the stack
+        stack = e.stack.substring(e.stack.indexOf('\n') + 1);
+    } else {
+        // remove this function and the message from the stack.
+        stack = (new Error(e).stack);
+        stack = stack.substring(stack.indexOf('\n', stack.indexOf('\n') + 1) + 1);  
+    }
 
-	logger.error('Datasource error:\n' +
-		util.inspect(e, false, 3, false) + '\n' +
-		stack);
+    logger.error('Datasource error:\n' +
+        util.inspect(e, false, 3, false) + '\n' +
+        stack);
 }
 
 datasource.on('error', logError);
 
 var app = require('./app')({
-		secret : config.options.deviceSecret,
-		datasource : datasource,
-		logger : clientLogger,
-		clientConfig : extend(true, {
-			bookingParameters : config.options.booking,
-			removeRegex : config.options.rooms.noDisplayRegex
-		}, config.options.client)
-	});
+        secret : config.options.deviceSecret,
+        datasource : datasource,
+        logger : clientLogger,
+        clientConfig : extend(true, {
+            bookingParameters : config.options.booking,
+            removeRegex : config.options.rooms.noDisplayRegex
+        }, config.options.client)
+    });
 
 app.listen(config.options.server.port || process.env.C9_PORT || 80);

lib/configHandler.js

 
 // the conf isn't exactly JSON since we want regexs, so we eval it as JS.
 function getJSONish(path) {
-	try {
-		return new Function('return ' + fs.readFileSync(path, 'utf8'))();	
-	} catch(err) {
-		throw new Error('Could not parse ' + path + '\n' + err);
-	}
+    /*jshint evil:true */
+    try {
+        return new Function('return ' + fs.readFileSync(path, 'utf8'))();   
+    } catch(err) {
+        throw new Error('Could not parse ' + path + '\n' + err);
+    }
 }
 
 function extractToRoomFilter(roomFilterOption, filterProperty) {
-	if (!roomFilterOption) {
-		return function() { return true; };
-	}
+    if (!roomFilterOption) {
+        return function() { return true; };
+    }
 
-	filterProperty = filterProperty || 'name';
+    filterProperty = filterProperty || 'name';
 
-	var propFunc;
-	switch(filterProperty) {
-		case 'key':
-			propFunc = 'getKey'; break;
-		case 'name':
-			propFunc = 'getName'; break;
-		default:
-			throw new Error('Invalid room filter property "' + filterProperty +
-				'". Only "name" and "key" are allowed.');
-	}
+    var propFunc;
+    switch(filterProperty) {
+        case 'key':
+            propFunc = 'getKey'; break;
+        case 'name':
+            propFunc = 'getName'; break;
+        default:
+            throw new Error('Invalid room filter property "' + filterProperty +
+                '". Only "name" and "key" are allowed.');
+    }
 
-	if (roomFilterOption instanceof Array) {
-		return function(room) {
-			var roomProp = room[propFunc]();
-			return roomFilterOption.some(function(includedRoomProp) {
-				return roomProp === includedRoomProp;
-			});
-		};
-	}
+    if (roomFilterOption instanceof Array) {
+        return function(room) {
+            var roomProp = room[propFunc]();
+            return roomFilterOption.some(function(includedRoomProp) {
+                return roomProp === includedRoomProp;
+            });
+        };
+    }
 
-	if (typeof roomFilterOption.test === 'function') {
-		return function(room) {
-			var roomProp = room[propFunc]();
-			roomFilterOption.lastIndex = 0;
-			return roomFilterOption.test(roomProp);
-		};
-	}
+    if (typeof roomFilterOption.test === 'function') {
+        return function(room) {
+            var roomProp = room[propFunc]();
+            roomFilterOption.lastIndex = 0;
+            return roomFilterOption.test(roomProp);
+        };
+    }
 
-	throw new Error('Invalid room filter value "' + roomFilterOption +
-		'". Only Array or RegExp are allowed.');
+    throw new Error('Invalid room filter value "' + roomFilterOption +
+        '". Only Array or RegExp are allowed.');
 }
 
 exports.getConfiguration = function() {
-	var home_dir = process.env.MEAT_HOME;
-	if (!home_dir) {
-		console.log(
-			'It is required that you set a MEAT_HOME environment\n' +
-			'variable containing a path to a directory for storing\n' +
-			'data and configuration. Exiting...');
-		process.exit(1);
-	}
+    var home_dir = process.env.MEAT_HOME;
+    if (!home_dir) {
+        console.log(
+            'It is required that you set a MEAT_HOME environment\n' +
+            'variable containing a path to a directory for storing\n' +
+            'data and configuration. Exiting...');
+        process.exit(1);
+    }
 
-	if (!existsSync(home_dir)) {
-		console.log('Directory ' + home_dir + ' doesn\'t exist. Creating it.');
-		mkdirp.sync(home_dir);
-	}
+    if (!existsSync(home_dir)) {
+        console.log('Directory ' + home_dir + ' doesn\'t exist. Creating it.');
+        mkdirp.sync(home_dir);
+    }
 
-	var data_dir = path.join(home_dir, 'data');
-	var config_dir = path.join(home_dir, 'config');
-	var logs_dir = path.join(home_dir, 'logs');
+    var data_dir = path.join(home_dir, 'data');
+    var config_dir = path.join(home_dir, 'config');
+    var logs_dir = path.join(home_dir, 'logs');
 
-	[ data_dir, config_dir, logs_dir ].forEach(function(dir) {
-		if (!existsSync(dir)) {
-			fs.mkdirSync(dir);
-		}
-	});
+    [ data_dir, config_dir, logs_dir ].forEach(function(dir) {
+        if (!existsSync(dir)) {
+            fs.mkdirSync(dir);
+        }
+    });
 
-	var coreConfig = path.join(config_dir, 'core.js');
-	var configTemplate = path.join(__dirname, 'configTemplate.js');
-	if (!existsSync(coreConfig)) {
-		console.log(
-			'Required configuration file ' + coreConfig + ' does\n' +
-			'not exist. Creating from template...');
-		fs.writeFileSync(coreConfig, fs.readFileSync(configTemplate));
-		console.log(
-			'Created file. Please add your configuration to it. Exiting...');
-		process.exit(1);
-	}
+    var coreConfig = path.join(config_dir, 'core.js');
+    var configTemplate = path.join(__dirname, 'configTemplate.js');
+    if (!existsSync(coreConfig)) {
+        console.log(
+            'Required configuration file ' + coreConfig + ' does\n' +
+            'not exist. Creating from template...');
+        fs.writeFileSync(coreConfig, fs.readFileSync(configTemplate));
+        console.log(
+            'Created file. Please add your configuration to it. Exiting...');
+        process.exit(1);
+    }
 
-	var options = extend(true, getJSONish(configTemplate), getJSONish(coreConfig));
+    var options = extend(true, getJSONish(configTemplate), getJSONish(coreConfig));
 
-	options.rooms.filter = extractToRoomFilter(options.rooms.filter, options.rooms.filterBy);
+    options.rooms.filter = extractToRoomFilter(options.rooms.filter, options.rooms.filterBy);
 
-	return {
-		directories : {
-			data : data_dir,
-			configuration : config_dir,
-			logs : logs_dir	
-		},
-		options : options
-	};
+    return {
+        directories : {
+            data : data_dir,
+            configuration : config_dir,
+            logs : logs_dir 
+        },
+        options : options
+    };
 };

lib/configTemplate.js

 {
-	"rooms" : {
-		/*
-			Either:
-				- null/undefined/falsy to list ALL rooms,
-				- an array of all room names to display, or
-				- a regular expression to check against each room
-			
-			Examples:
-				regular expression to match all rooms with names that start with "Syd - ":
-					/^Syd - /
-				array to match only two specific rooms: 
-					['Syd - Coding Corner', 'Syd - Neo - Kent St']
-		*/
-		"filter" : null
-		/*
-			Which property to run the above filter on ('name' or 'key')
-		*/
-	,	"filterBy" : "name"
-		
-		/*
-			A regex that defines parts of the room name NOT to display, or falsy to display the full name.
-			
-			Examples:
-				regex that would cause 'Syd - Coding Corner' to display as 'Coding Corner'
-					/^Syd - /g
-		*/
-	,	"noDisplayRegex" : null
-	}
-,	"booking" : {
-		// the maximum number of minutes that a user is allowed to book
-		"maxBookableMinutes" : 60,
-		// the minimum number of minutes that a user is allowed to book
-		"minBookableMinutes" : 5,
-		// your definition of "room is available soon" in minutes. 0 will disable.
-		"maxStatusSoonMinutes" : 0,
-		// your definition of "room has enough time available for a meeting" in minutes.  0 will disable.
-		"minFreeTimeAdequateMinutes" : 0,
-		// The default meeting duration in minutes
-		"defaultBookingMinutes" : 30,
-		// The number of minutes to add or subtract from the booking duration per button press
-		"bookingIntervalMinutes" : 15
-	}
-	/* data comes from google apps. no other options available at this time */
-,	"datasource" : "gapps"
+    "rooms" : {
+        /*
+            Either:
+                - null/undefined/falsy to list ALL rooms,
+                - an array of all room names to display, or
+                - a regular expression to check against each room
+            
+            Examples:
+                regular expression to match all rooms with names that start with "Syd - ":
+                    /^Syd - /
+                array to match only two specific rooms: 
+                    ['Syd - Coding Corner', 'Syd - Neo - Kent St']
+        */
+        "filter" : null
+        /*
+            Which property to run the above filter on ('name' or 'key')
+        */
+    ,   "filterBy" : "name"
+        
+        /*
+            A regex that defines parts of the room name NOT to display, or falsy to display the full name.
+            
+            Examples:
+                regex that would cause 'Syd - Coding Corner' to display as 'Coding Corner'
+                    /^Syd - /g
+        */
+    ,   "noDisplayRegex" : null
+    }
+,   "booking" : {
+        // the maximum number of minutes that a user is allowed to book
+        "maxBookableMinutes" : 60,
+        // the minimum number of minutes that a user is allowed to book
+        "minBookableMinutes" : 5,
+        // your definition of "room is available soon" in minutes. 0 will disable.
+        "maxStatusSoonMinutes" : 0,
+        // your definition of "room has enough time available for a meeting" in minutes.  0 will disable.
+        "minFreeTimeAdequateMinutes" : 0,
+        // The default meeting duration in minutes
+        "defaultBookingMinutes" : 30,
+        // The number of minutes to add or subtract from the booking duration per button press
+        "bookingIntervalMinutes" : 15
+    }
+    /* data comes from google apps. no other options available at this time */
+,   "datasource" : "gapps"
 
-	/* Configuration specific to a gapps datasource. */
-,	"gapps" : {
-		/* A clientId and clientSecret from Google Apps API Console when registering for
-		   "Installed apps" API access. This default is a universal MEAT id/secret pair
-		   which is only allowed 10k requests per day, so will probably run out fast... */
-		"clientId" : "23835704985.apps.googleusercontent.com"
-	,	"clientSecret" : "Pj1DAnZGNn8mfHMllmyrcKex"
+    /* Configuration specific to a gapps datasource. */
+,   "gapps" : {
+        /* A clientId and clientSecret from Google Apps API Console when registering for
+           "Installed apps" API access. This default is a universal MEAT id/secret pair
+           which is only allowed 10k requests per day, so will probably run out fast... */
+        "clientId" : "23835704985.apps.googleusercontent.com"
+    ,   "clientSecret" : "Pj1DAnZGNn8mfHMllmyrcKex"
 
-		/*
-			Whether to include only calendars that are labeled as resources. Resources will have ids
-			like {resource}@resource.calendar.google.com
-		*/
-	,	"resourcesOnly" : true
-	}
+        /*
+            Whether to include only calendars that are labeled as resources. Resources will have ids
+            like {resource}@resource.calendar.google.com
+        */
+    ,   "resourcesOnly" : true
+    }
 
-	/* A secret used to identify legitimate users. This is not secure, it is only obscuring.
-	   A more robust solution is required. It is recommended you only run MEATier on secured networks.
+    /* A secret used to identify legitimate users. This is not secure, it is only obscuring.
+       A more robust solution is required. It is recommended you only run MEATier on secured networks.
 
        If specified, any device wanting to read or write to the datasource will need to
-	   include a secret="{deviceSecret}" query string variable.
-	 */
-,	"deviceSecret" : null
-,	"server" : {
-		/* The port on which to run MEAT. Defaults to the value of $C9_PORT or 80 */
-		"port" : null
-	}
-,	"client" : {	
-		/*
-			How long to wait, in seconds, for user input before reverting to the initial screen.
-		*/
-		"idleTimeoutSeconds" : 30
+       include a secret="{deviceSecret}" query string variable.
+     */
+,   "deviceSecret" : null
+,   "server" : {
+        /* The port on which to run MEAT. Defaults to the value of $C9_PORT or 80 */
+        "port" : null
+    }
+,   "client" : {    
+        /*
+            How long to wait, in seconds, for user input before reverting to the initial screen.
+        */
+        "idleTimeoutSeconds" : 30
 
-		/*
-			Use this setting to "disable" MEAT iPads during non-office hours. The MEAT will still work,
-			but the display will be black when left idle for 30s or more. By default, MEAT will display at all times.
+        /*
+            Use this setting to "disable" MEAT iPads during non-office hours. The MEAT will still work,
+            but the display will be black when left idle for 30s or more. By default, MEAT will display at all times.
 
-			"days" should be an array of 3-letter weekday abbreviations
-			MEAT will be enabled between "start" and "end" times on those days.
+            "days" should be an array of 3-letter weekday abbreviations
+            MEAT will be enabled between "start" and "end" times on those days.
 
-			The format of these settings is very strict - only 3-letter weekday abbreviations and 4-digit 24-hour time
-			are supported.
-		*/
-	,	"enabledPeriod" : null/*{
-			days : [ "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" ],
-			timeRange : {
-				start : "07:00",
-				end : "19:00"
-			}
-		}*/
-	}
-}
+            The format of these settings is very strict - only 3-letter weekday abbreviations and 4-digit 24-hour time
+            are supported.
+        */
+    ,   "enabledPeriod" : null/*{
+            days : [ "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" ],
+            timeRange : {
+                start : "07:00",
+                end : "19:00"
+            }
+        }*/
+    }
+}

lib/gapps/datasource.js

     );
 
     return ds;
-};
+};

lib/gapps/gauth.js

         json.refresh_token = tokenData.refresh_token;
         return json;
     });
-};
+};

lib/gapps/gcal.js

 
 module.exports = function(tokenData, logger) {
     return new GCal(tokenData, logger);
-};
+};

lib/gapps/storage.js

 var storage_dir;
 
 exports.setStorageDirectory = function(dirpath) {
-	storage_dir = dirpath;
+    storage_dir = dirpath;
 
-	try { fs.mkdirSync(storage_dir); }
-	catch (e) { /* chuck it */ }	
+    try { fs.mkdirSync(storage_dir); }
+    catch (e) { /* chuck it */ }    
 };
 
 function initDir() {
-	if (!storage_dir) {
-		exports.setStorageDirectory(path.join(__dirname, 'data'));
-	}
+    if (!storage_dir) {
+        exports.setStorageDirectory(path.join(__dirname, 'data'));
+    }
 }
 
 exports.put = function(key, value) {
-	initDir();
+    initDir();
     try {
         fs.writeFileSync(path.join(storage_dir, key + ""), JSON.stringify(value), 'utf8');
     }
 };
 
 exports.get = function(key) {
-	initDir();
+    initDir();
     try {
         return JSON.parse(fs.readFileSync(path.join(storage_dir, key + "")), 'utf8');
     }
     catch(e) { /* chuck it */ }
-};
+};

lib/logHandler.js

 var Logger = winston.Logger;
 
 function getLogger(config) {
-	return new Logger({
-	    transports: [
-	        new winston.transports.Console(),
-	        new winston.transports.File({
-	      	    filename: path.join(config.directories.logs, 'meat.log'),
-	      	    maxsize: 10 * 1024 * 1024,
-	      	    maxFiles: 3
-	        })
-	    ],
-	    exceptionHandlers: [
-	        new winston.transports.Console(),
-	        new winston.transports.File({
-	        	filename: path.join(config.directories.logs, 'meat-exception.log')
-	        })
-	    ]
-	});
+    return new Logger({
+        transports: [
+            new winston.transports.Console(),
+            new winston.transports.File({
+                filename: path.join(config.directories.logs, 'meat.log'),
+                maxsize: 10 * 1024 * 1024,
+                maxFiles: 3
+            })
+        ],
+        exceptionHandlers: [
+            new winston.transports.Console(),
+            new winston.transports.File({
+                filename: path.join(config.directories.logs, 'meat-exception.log')
+            })
+        ]
+    });
 }
 function getClientLogger(config) {
-	return new Logger({
+    return new Logger({
         transports: [
             new winston.transports.File({
                 filename: path.join(config.directories.logs, 'meat-web.log'),
 }
 
 exports.getLogger = getLogger;
-exports.getClientLogger = getClientLogger;
+exports.getClientLogger = getClientLogger;
 function noop() {}
 
 module.exports = function(logger, level) {
-	logger = logger || require('winston');
-	level = level || 'info';
+    logger = logger || require('winston');
+    level = level || 'info';
 
-	function loggerWrite(msg, enc) {
-	    if (Buffer.isBuffer(msg)) {
-	        msg = msg.toString(enc);
-	    }
+    function loggerWrite(msg, enc) {
+        if (Buffer.isBuffer(msg)) {
+            msg = msg.toString(enc);
+        }
 
-	    var n;
-	    while(~(n = msg.indexOf('\n'))) {
-	        logger[level](msg.substring(0, n));
-	        msg = msg.substring(n + 1);
-	    }
-	    if (msg) {
-	        logger[level](msg);
-	    }
-	    loggerStream.emit('drain');
-	    return false;
-	}
-	var loggerStream = Object.create(EventEmitter.prototype);
-	loggerStream.writeable = true;
-	loggerStream.write = loggerStream.end = loggerWrite;
-	loggerStream.destroy = loggerStream.destroySoon = noop;
+        var n;
+        while(~(n = msg.indexOf('\n'))) {
+            logger[level](msg.substring(0, n));
+            msg = msg.substring(n + 1);
+        }
+        if (msg) {
+            logger[level](msg);
+        }
+        loggerStream.emit('drain');
+        return false;
+    }
+    var loggerStream = Object.create(EventEmitter.prototype);
+    loggerStream.writeable = true;
+    loggerStream.write = loggerStream.end = loggerWrite;
+    loggerStream.destroy = loggerStream.destroySoon = noop;
 
-	return loggerStream;
-};
+    return loggerStream;
+};

lib/models/event.js

 function Event(title, organizer, start, end) {
-	this._title = title;
-	this._organizer = organizer;
-	this._start = start;
-	this._end = end;
+    this._title = title;
+    this._organizer = organizer;
+    this._start = start;
+    this._end = end;
 }
 
 Event.prototype.toJSON = function() {
-	return {
-		title : this._title,
-		organizer : this._organizer,
-		start : this._start,
-		end : this._end
-	};
+    return {
+        title : this._title,
+        organizer : this._organizer,
+        start : this._start,
+        end : this._end
+    };
 };
 
-module.exports = Event;
+module.exports = Event;

lib/models/room.js

 function Room(key, name) {
-	this._key = key;
-	this._name = name;
-	this._events = [];
+    this._key = key;
+    this._name = name;
+    this._events = [];
 }
 Room.prototype.getKey = function() { return this._key; };
 Room.prototype.getName = function() { return this._name; };
 Room.prototype.getEvents = function() { return this._events; };
 
 Room.prototype.setEvents = function(events) {
-	this._events = events;
+    this._events = events;
 };
 Room.prototype.addEvent = function(event) {
-	this._events.push(event);
-	this._events.sort(function(a, b) {
-		return (a._start - b._start) || (a._end - b._end);
-	});
+    this._events.push(event);
+    this._events.sort(function(a, b) {
+        return (a._start - b._start) || (a._end - b._end);
+    });
 };
 Room.prototype.toJSON = function(expand) {
-	return expand ? {
-		key : this._key,
-		name : this._name,
-		events : this._events.map(function(event) { return event.toJSON(); })
-	} : {
-		key : this._key,
-		name : this._name
-	};
+    return expand ? {
+        key : this._key,
+        name : this._name,
+        events : this._events.map(function(event) { return event.toJSON(); })
+    } : {
+        key : this._key,
+        name : this._name
+    };
 };
 
 Room.hasKey = function(key, room) {
-	if (room) {
-		return key === room.getKey();
-	}
-	return function (room) {
-		return key === room.getKey();
-	};
+    if (room) {
+        return key === room.getKey();
+    }
+    return function (room) {
+        return key === room.getKey();
+    };
 };
 
-module.exports = Room;
+module.exports = Room;

public/js/eventManager.js

 var EventManager = (function() {
-	var TimeRange = function(start, end) {
-		this.start = start;
-		this.end = end;
-	};
-	TimeRange.prototype = {
-		toString : function() {
-			if (this.start.getDay() == this.end.getDay())
-				return this.start.toDateString() + '(' + 
-					this.start.getHours() + ':' + this.start.getMinutes() + ' - ' + 
-					this.end.getHours() + ':' + this.end.getMinutes() + ')';
-			else
-				return '(' + this.start.toString() + ' - ' + this.end.toString() + ')';
-		},
-		intersects : function(dateOrRange) {
-			return (dateOrRange instanceof Date) ?
-						dateOrRange >= this.start && dateOrRange < this.end :
-						dateOrRange.end > this.start && dateOrRange.start < this.end;
-		},
-		isBefore : function(date) {
-			return this.end <= date;
-		},
-		isAfter : function(date) {
-			return this.start > date;
-		}
-	};
-	TimeRange.sorter = function(a, b) {
-		return a.start < b.start ? -1 :
-				a.start > b.start ? 1 :
-				a.end < b.end ? -1 :
-				a.end > b.end ? 1 :
-				0;
-	};
+    var TimeRange = function(start, end) {
+        this.start = start;
+        this.end = end;
+    };
+    TimeRange.prototype = {
+        toString : function() {
+            if (this.start.getDay() == this.end.getDay())
+                return this.start.toDateString() + '(' + 
+                    this.start.getHours() + ':' + this.start.getMinutes() + ' - ' + 
+                    this.end.getHours() + ':' + this.end.getMinutes() + ')';
+            else
+                return '(' + this.start.toString() + ' - ' + this.end.toString() + ')';
+        },
+        intersects : function(dateOrRange) {
+            return (dateOrRange instanceof Date) ?
+                        dateOrRange >= this.start && dateOrRange < this.end :
+                        dateOrRange.end > this.start && dateOrRange.start < this.end;
+        },
+        isBefore : function(date) {
+            return this.end <= date;
+        },
+        isAfter : function(date) {
+            return this.start > date;
+        }
+    };
+    TimeRange.sorter = function(a, b) {
+        return a.start < b.start ? -1 :
+                a.start > b.start ? 1 :
+                a.end < b.end ? -1 :
+                a.end > b.end ? 1 :
+                0;
+    };
 
-	function CalendarEvent(jsonOrEvent) {
-		TimeRange.call(this, new Date(jsonOrEvent.start), new Date(jsonOrEvent.end));
+    function CalendarEvent(jsonOrEvent) {
+        TimeRange.call(this, new Date(jsonOrEvent.start), new Date(jsonOrEvent.end));
 
-		this._title = jsonOrEvent._title || jsonOrEvent.title || 'Unnamed Event';
-		this._organizer = jsonOrEvent._organizer || jsonOrEvent.organizer;
-	}
-	$.extend(CalendarEvent.prototype, TimeRange.prototype, {
-		conflictsWith : function (date) {
-			return this.intersects(date);
-		},
-		nextNonConflict : function (afterDate) {
-			if (this.intersects(afterDate)) {
-				return this.end;
-			}
-			return afterDate;
-		},
-		toString : function() {
-			return this._title + "(" + TimeRange.prototype.toString.call(this) + ")";
-		},
-		title : function() { return this._title; },
-		organizer : function() { return this._organizer; }
-	});
-	CalendarEvent.getEvents = function (eventJsonArr) {
-		var events = [];
-		for (var i = 0; i < eventJsonArr.length; i++) {
-			events.push(new CalendarEvent(eventJsonArr[i]));
-		}
-		return events;
-	};
-	CalendarEvent.sorter = TimeRange.sorter;
+        this._title = jsonOrEvent._title || jsonOrEvent.title || 'Unnamed Event';
+        this._organizer = jsonOrEvent._organizer || jsonOrEvent.organizer;
+    }
+    $.extend(CalendarEvent.prototype, TimeRange.prototype, {
+        conflictsWith : function (date) {
+            return this.intersects(date);
+        },
+        nextNonConflict : function (afterDate) {
+            if (this.intersects(afterDate)) {
+                return this.end;
+            }
+            return afterDate;
+        },
+        toString : function() {
+            return this._title + "(" + TimeRange.prototype.toString.call(this) + ")";
+        },
+        title : function() { return this._title; },
+        organizer : function() { return this._organizer; }
+    });
+    CalendarEvent.getEvents = function (eventJsonArr) {
+        var events = [];
+        for (var i = 0; i < eventJsonArr.length; i++) {
+            events.push(new CalendarEvent(eventJsonArr[i]));
+        }
+        return events;
+    };
+    CalendarEvent.sorter = TimeRange.sorter;
 
-	var Room;
-	(function() {
-		Room = function (roomJson) {
-			var _events = CalendarEvent.getEvents(roomJson.events);
+    var Room;
+    (function() {
+        Room = function (roomJson) {
+            var _events = CalendarEvent.getEvents(roomJson.events);
 
-			delete roomJson.events;
-			
-			this._meta = roomJson;
+            delete roomJson.events;
+            
+            this._meta = roomJson;
 
-			var self = this;
-			this.events = function(events) {
-				if (events) {
-					_events = CalendarEvent.getEvents(events);
+            var self = this;
+            this.events = function(events) {
+                if (events) {
+                    _events = CalendarEvent.getEvents(events);
 
-					self._nextFreeTimeCache = {};
-					self._nextEventCache = {};
-				} else {
-					return _events;
-				}
-			};
+                    self._nextFreeTimeCache = {};
+                    self._nextEventCache = {};
+                } else {
+                    return _events;
+                }
+            };
 
-			this._nextFreeTimeCache = {};
-			this._nextEventCache = {};
-		};
-		function getNextEventCacheObj(afterDate, room) {
-			var dateObj = afterDate || DebugSettings.now() || new Date();
+            this._nextFreeTimeCache = {};
+            this._nextEventCache = {};
+        };
+        function getNextEventCacheObj(afterDate, room) {
+            var dateObj = afterDate || DebugSettings.now() || new Date();
 
-			if (room._nextEventCache[dateObj]) {
-				return room._nextEventCache[dateObj];
-			}
+            if (room._nextEventCache[dateObj]) {
+                return room._nextEventCache[dateObj];
+            }
 
-			var minDate = null, minDateEvent = null;
-			for (var i = 0, events = room.events(), l = events.length; i < l; i++) {
-				var event = events[i];
-				if (event.isBefore(afterDate)) {
-					continue;
-				}
+            var minDate = null, minDateEvent = null;
+            for (var i = 0, events = room.events(), l = events.length; i < l; i++) {
+                var event = events[i];
+                if (event.isBefore(afterDate)) {
+                    continue;
+                }
 
-				minDateEvent = event;
-				minDate = event.isAfter(afterDate) ? event.start : afterDate;
-				break;
-			}
+                minDateEvent = event;
+                minDate = event.isAfter(afterDate) ? event.start : afterDate;
+                break;
+            }
 
-			room._nextEventCache[dateObj] = { event: minDateEvent, date: minDate };
-			return room._nextEventCache[dateObj];
-		}
+            room._nextEventCache[dateObj] = { event: minDateEvent, date: minDate };
+            return room._nextEventCache[dateObj];
+        }
 
-		Room.prototype = {
-			id : function() { return this._meta.key; },
-			name : function() { return this._meta.name; },
-			simpleName : EventManagerConfig.removeRegex && EventManagerConfig.removeRegex.test ?
-				function() { return this.name().replace(EventManagerConfig.removeRegex, ''); } :
-				function() { return this.name(); },
-			isBooked : function (dateToCheck) {
-				var dateObj = dateToCheck || DebugSettings.now() || new Date();
+        Room.prototype = {
+            id : function() { return this._meta.key; },
+            name : function() { return this._meta.name; },
+            simpleName : EventManagerConfig.removeRegex && EventManagerConfig.removeRegex.test ?
+                function() { return this.name().replace(EventManagerConfig.removeRegex, ''); } :
+                function() { return this.name(); },
+            isBooked : function (dateToCheck) {
+                var dateObj = dateToCheck || DebugSettings.now() || new Date();
 
-				for (var i = 0; i < this.events().length; i++) {
-					if(this.events()[i].conflictsWith(dateObj)) {
-						return true;
-					}
-				}
-				return false;
-			},
-			upcomingBookings : function(afterDate) {
-				var dateObj = afterDate || DebugSettings.now() || new Date();
-				var events = this.events().slice();
-				while(events.length && events[0].isBefore(dateObj)) {
-					events.shift();
-				}
-				
-				return events;
-			},
-			nextEvent : function (afterDate) {
-				return getNextEventCacheObj(afterDate, this).event;
-			},
-			nextEventTime : function (afterDate) {
-				return getNextEventCacheObj(afterDate, this).date;
-			},
-			nextFreeTime : function (afterDate) {
-				var dateObj = afterDate || DebugSettings.now() || new Date();
+                for (var i = 0; i < this.events().length; i++) {
+                    if(this.events()[i].conflictsWith(dateObj)) {
+                        return true;
+                    }
+                }
+                return false;
+            },
+            upcomingBookings : function(afterDate) {
+                var dateObj = afterDate || DebugSettings.now() || new Date();
+                var events = this.events().slice();
+                while(events.length && events[0].isBefore(dateObj)) {
+                    events.shift();
+                }
+                
+                return events;
+            },
+            nextEvent : function (afterDate) {
+                return getNextEventCacheObj(afterDate, this).event;
+            },
+            nextEventTime : function (afterDate) {
+                return getNextEventCacheObj(afterDate, this).date;
+            },
+            nextFreeTime : function (afterDate) {
+                var dateObj = afterDate || DebugSettings.now() || new Date();
 
-				if (this._nextFreeTimeCache[dateObj]) {
-					return this._nextFreeTimeCache[dateObj];
-				}
+                if (this._nextFreeTimeCache[dateObj]) {
+                    return this._nextFreeTimeCache[dateObj];
+                }
 
-				var maxofMinFreeDates = new Date(dateObj);
-				for (var i = 0; i < this.events().length; i++) {
-					var minFreeDate = this.events()[i].nextNonConflict(dateObj);
-					if(minFreeDate > maxofMinFreeDates) {
-						maxofMinFreeDates = minFreeDate;
-					}
-				}
+                var maxofMinFreeDates = new Date(dateObj);
+                for (var i = 0; i < this.events().length; i++) {
+                    var minFreeDate = this.events()[i].nextNonConflict(dateObj);
+                    if(minFreeDate > maxofMinFreeDates) {
+                        maxofMinFreeDates = minFreeDate;
+                    }
+                }
 
-				this._nextFreeTimeCache[dateObj] = maxofMinFreeDates;
-				return maxofMinFreeDates;
-			},
-			toAllEventsString : function() {
-				return this.events().join(', ');
-			},
-			toBookedTimesString : function() {
-				return this.events().join(', ');
-			},
-			getBookedTimesToday : function() {
-				var now = DebugSettings.now() || new Date(),
-					today = new TimeRange(
-								new Date(now.getFullYear(), now.getMonth(), now.getDate()),
-								new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1));
-				return $.map(
-							this.events(),
-							function(timeRange) {
-								return timeRange.intersects(today) ? timeRange : null;
-							}
-						);
-			}
-		};
-	})();
+                this._nextFreeTimeCache[dateObj] = maxofMinFreeDates;
+                return maxofMinFreeDates;
+            },
+            toAllEventsString : function() {
+                return this.events().join(', ');
+            },
+            toBookedTimesString : function() {
+                return this.events().join(', ');
+            },
+            getBookedTimesToday : function() {
+                var now = DebugSettings.now() || new Date(),
+                    today = new TimeRange(
+                                new Date(now.getFullYear(), now.getMonth(), now.getDate()),
+                                new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1));
+                return $.map(
+                            this.events(),
+                            function(timeRange) {
+                                return timeRange.intersects(today) ? timeRange : null;
+                            }
+                        );
+            }
+        };
+    })();
 
-	var shouldShowRoom = 
-		!EventManagerConfig.roomsToShow ?
-			function(name) {
-				return true;
-			} :
-		$.isArray(EventManagerConfig.roomsToShow) ?
-			function(name) {
-				for(var i = 0, l = EventManagerConfig.roomsToShow.length; i < l; i++) {
-					if (EventManagerConfig.roomsToShow[i] === name) {
-						return true;
-					}
-				}
-				return false;
-			} :
-		EventManagerConfig.roomsToShow.test ?
-			function(name) {
-				EventManagerConfig.roomsToShow.lastIndex = 0;
-				return EventManagerConfig.roomsToShow.test(name);
-			} :
-		Logger.log('EventManagerConfig.roomsToShow must be falsy, an Array of strings, or a RegExp object.');
-	
-	Room.getRooms = function(roomJsonArr) {
-		var rooms = [];
-		for (var i = 0; i < roomJsonArr.length; i++) {
-			var roomJson = roomJsonArr[i];
-			if (shouldShowRoom(roomJson.name)) {
-				rooms.push(new Room(roomJson));
-			}
-		}
-		return rooms;
-	};
+    var shouldShowRoom = 
+        !EventManagerConfig.roomsToShow ?
+            function(name) {
+                return true;
+            } :
+        $.isArray(EventManagerConfig.roomsToShow) ?
+            function(name) {
+                for(var i = 0, l = EventManagerConfig.roomsToShow.length; i < l; i++) {
+                    if (EventManagerConfig.roomsToShow[i] === name) {
+                        return true;
+                    }
+                }
+                return false;
+            } :
+        EventManagerConfig.roomsToShow.test ?
+            function(name) {
+                EventManagerConfig.roomsToShow.lastIndex = 0;
+                return EventManagerConfig.roomsToShow.test(name);
+            } :
+        Logger.log('EventManagerConfig.roomsToShow must be falsy, an Array of strings, or a RegExp object.');
+    
+    Room.getRooms = function(roomJsonArr) {
+        var rooms = [];
+        for (var i = 0; i < roomJsonArr.length; i++) {
+            var roomJson = roomJsonArr[i];
+            if (shouldShowRoom(roomJson.name)) {
+                rooms.push(new Room(roomJson));
+            }
+        }
+        return rooms;
+    };
 
-	return new (function () {
-		var url;
-		var secretKey;
+    return new (function () {
+        var url;
+        var secretKey;
 
-		var roomsByName;
-		var roomsById;
-		
-		function addNewRoom(room) {
-			EventManager.rooms.push(room);
-			roomsByName[room.name()] = room;
-			roomsById[room.id()] = room;
-		}
+        var roomsByName;
+        var roomsById;
+        
+        function addNewRoom(room) {
+            EventManager.rooms.push(room);
+            roomsByName[room.name()] = room;
+            roomsById[room.id()] = room;
+        }
 
-		this.init = function(rootUrl, secret, callback) {
-			url = rootUrl;
-			secretKey = secret;
-			this.rooms = [];
-			roomsByName = {};
-			roomsById = {};
+        this.init = function(rootUrl, secret, callback) {
+            url = rootUrl;
+            secretKey = secret;
+            this.rooms = [];
+            roomsByName = {};
+            roomsById = {};
 
-			this.update(callback);
-		};
+            this.update(callback);
+        };
 
-		this.update = function(callback) {
-			getRooms(function(res) {
-				var newRooms = [];
-				_.each(Room.getRooms(res.rooms), function(room) {
-					if (room.id() in roomsById) {
-						roomsById[room.id()].events(room.events());
-					} else {
-						newRooms.push(room);
-						addNewRoom(room);
-					}
-				});
-				
-				callback(newRooms);
-			}, function(err) { Logger.log('GetAvailableCalendars error', err); });
-		};
+        this.update = function(callback) {
+            getRooms(function(res) {
+                var newRooms = [];
+                _.each(Room.getRooms(res.rooms), function(room) {
+                    if (room.id() in roomsById) {
+                        roomsById[room.id()].events(room.events());
+                    } else {
+                        newRooms.push(room);
+                        addNewRoom(room);
+                    }
+                });
+                
+                callback(newRooms);
+            }, function(err) { Logger.log('GetAvailableCalendars error', err); });
+        };
 
-		function isForbidden(xhr, next) {
-			if (xhr.status === 403) {
-				window.location.href = url + 'forbidden';
-				return;
-			}
-			return next();
-		}
+        function isForbidden(xhr, next) {
+            if (xhr.status === 403) {
+                window.location.href = url + 'forbidden';
+                return;
+            }
+            return next();
+        }
 
-		function errorHandler(onError) {
-			return function(xhr) {
-				var self = this, args = arguments;
-				return isForbidden(xhr, function() { return onError && onError.apply(self, args); });
-			};
-		}
-		
-		function getRooms(onSuccess, onError) {
-			return $.ajax({
-				url : url + 'data/rooms?expand' + (secretKey ? '&secret=' + secretKey : ''),
-				dataType : 'json',
-				success : onSuccess,
-				error : errorHandler(onError)
-			});
-		}
+        function errorHandler(onError) {
+            return function(xhr) {
+                var self = this, args = arguments;
+                return isForbidden(xhr, function() { return onError && onError.apply(self, args); });
+            };
+        }
+        
+        function getRooms(onSuccess, onError) {
+            return $.ajax({
+                url : url + 'data/rooms?expand' + (secretKey ? '&secret=' + secretKey : ''),
+                dataType : 'json',
+                success : onSuccess,
+                error : errorHandler(onError)
+            });
+        }
 
-		function bookRoom(roomKey, startTimeISO, endTimeISO, onSuccess, onError) {
-			return $.ajax({
-				type: 'POST',
-				url : url + 'data/events?room=' + roomKey +
-					'&start=' + startTimeISO + '&end=' + endTimeISO +
-					(secretKey ? '&secret=' + secretKey : ''),
-				dataType : 'json',
-				success : onSuccess,
-				error : errorHandler(onError)
-			});
-		}
-		
-		this.getRoom = function (roomName) {
-			return roomsByName[roomName];
-		};
-		this.getRoomById = function (roomId) {
-			return roomsById[roomId];
-		};
-		
-		this.bookRoom = function(room, startTime, durationMinutes, onSuccess, onError) {
-			var endTime = new Date(startTime);
-			endTime.setMinutes(startTime.getMinutes() + durationMinutes);
-			
-			return bookRoom(room.id(), startTime.toISOString(), endTime.toISOString(), onSuccess, onError);
-		};
-	})();
-})();
+        function bookRoom(roomKey, startTimeISO, endTimeISO, onSuccess, onError) {
+            return $.ajax({
+                type: 'POST',
+                url : url + 'data/events?room=' + roomKey +
+                    '&start=' + startTimeISO + '&end=' + endTimeISO +
+                    (secretKey ? '&secret=' + secretKey : ''),
+                dataType : 'json',
+                success : onSuccess,
+                error : errorHandler(onError)
+            });
+        }
+        
+        this.getRoom = function (roomName) {
+            return roomsByName[roomName];
+        };
+        this.getRoomById = function (roomId) {
+            return roomsById[roomId];
+        };
+        
+        this.bookRoom = function(room, startTime, durationMinutes, onSuccess, onError) {
+            var endTime = new Date(startTime);
+            endTime.setMinutes(startTime.getMinutes() + durationMinutes);
+            
+            return bookRoom(room.id(), startTime.toISOString(), endTime.toISOString(), onSuccess, onError);
+        };
+    })();
+})();

public/js/main.js

 (function() {
-	var params = ParameterParser.parse();
-	var roomName = params.room;
-	
-	function runAtMidnight(func) {
-		var oneDay = 1000 * 60 * 60 * 24;
-		
-		var midnight = new Date(new Date().getTime() + oneDay);
-		midnight.setSeconds(0); midnight.setMinutes(0);midnight.setHours(0);
-		
-		// paranoia
-		while (midnight < new Date()) midnight = new Date(midnight.getTime() + oneDay);
-		
-		setTimeout(func, midnight - new Date());
-	}
-	function runEveryNMinutesOnTheMthSecond(n, m, func) {
-		var firstDelaySec = m - (DebugSettings.now() || new Date()).getSeconds();
-		if (firstDelaySec <= 0) {
-			firstDelaySec +=  60;
-		}
+    var params = ParameterParser.parse();
+    var roomName = params.room;
+    
+    function runAtMidnight(func) {
+        var oneDay = 1000 * 60 * 60 * 24;
+        
+        var midnight = new Date(new Date().getTime() + oneDay);
+        midnight.setSeconds(0); midnight.setMinutes(0);midnight.setHours(0);
+        
+        // paranoia
+        while (midnight < new Date()) midnight = new Date(midnight.getTime() + oneDay);
+        
+        setTimeout(func, midnight - new Date());
+    }
+    function runEveryNMinutesOnTheMthSecond(n, m, func) {
+        var firstDelaySec = m - (DebugSettings.now() || new Date()).getSeconds();
+        if (firstDelaySec <= 0) {
+            firstDelaySec +=  60;
+        }
 
-		setTimeout(function() {
+        setTimeout(function() {
             try {
                 func();
                 setInterval(function() {
             } catch (err) {
                 Logger.log("Error in repeated function.", err);
             }
-		}, firstDelaySec * 1000);
-	}
-	
-	function getRootUrl() {
-		var qsStart = window.location.href.indexOf('?');
-		var hashStart = window.location.href.indexOf('#');
-		var url = window.location.href.substring(0,
-			Math.min(~qsStart ? qsStart : Infinity, ~hashStart ? hashStart : Infinity));
-		
-		if (url.lastIndexOf('/') !== url.length - 1) {
-			url += '/';
-		}
-		return url;
-	}
+        }, firstDelaySec * 1000);
+    }
+    
+    function getRootUrl() {
+        var qsStart = window.location.href.indexOf('?');
+        var hashStart = window.location.href.indexOf('#');
+        var url = window.location.href.substring(0,
+            Math.min(~qsStart ? qsStart : Infinity, ~hashStart ? hashStart : Infinity));
+        
+        if (url.lastIndexOf('/') !== url.length - 1) {
+            url += '/';
+        }
+        return url;
+    }
 
-	function updateRooms() {
-		EventManager.update(function success(newRooms) {
-			_.each(newRooms, function(room) {
-				GlobalEvents.trigger('roomLoaded', room);
-			});
-			_.each(EventManager.rooms, function(room) {
-				GlobalEvents.trigger('roomUpdatedByServer', room);
-			});
-		}, function error() {
-			console.log('Error while updating rooms.');
-		});
-	}
+    function updateRooms() {
+        EventManager.update(function success(newRooms) {
+            _.each(newRooms, function(room) {
+                GlobalEvents.trigger('roomLoaded', room);
+            });
+            _.each(EventManager.rooms, function(room) {
+                GlobalEvents.trigger('roomUpdatedByServer', room);
+            });
+        }, function error() {
+            console.log('Error while updating rooms.');
+        });
+    }
 
-	DebugSettings.init(getRootUrl());
-	EventManager.init(getRootUrl(), params.secret, function(newRooms) {
-		var thisRoom = roomName ? EventManager.getRoom(roomName) : undefined;
-		
-		if (roomName && !thisRoom) {
-			$('#errormsg')
-				.css('font-size','18px')
-				.text('You entered an invalid room name.  The room could not be found.')
-				.removeClass('hidden');
-			$('#container').addClass('hidden');
-			return;
-		}
-			
-		initUi(thisRoom);
-		
-		_.each(newRooms, function(room) {
-			GlobalEvents.trigger('roomLoaded', room);
-		});
+    DebugSettings.init(getRootUrl());
+    EventManager.init(getRootUrl(), params.secret, function(newRooms) {
+        var thisRoom = roomName ? EventManager.getRoom(roomName) : undefined;
+        
+        if (roomName && !thisRoom) {
+            $('#errormsg')
+                .css('font-size','18px')
+                .text('You entered an invalid room name.  The room could not be found.')
+                .removeClass('hidden');
+            $('#container').addClass('hidden');
+            return;
+        }
+            
+        initUi(thisRoom);
+        
+        _.each(newRooms, function(room) {
+            GlobalEvents.trigger('roomLoaded', room);
+        });
 
-		runEveryNMinutesOnTheMthSecond(1, 31, updateRooms);
-		
-		//update UI when the minute ticks over.
-		runEveryNMinutesOnTheMthSecond(1, 1, function() { GlobalEvents.trigger('minuteChanged'); });
-	});
-	
-	GlobalEvents.bind('bookingAddedByUser', function(event, booking) {
-		EventManager.bookRoom(booking.room, booking.time, booking.duration,
-			function success() {			
-				updateRooms();
-			},
-			function failure() {
-				GlobalEvents.trigger('bookingFailure', booking);
-			}
-		);
-	});
-	
-	// update MEAT from server everyday at midnight.
-	runAtMidnight(function() { window.location.reload(); });
-})();
+        runEveryNMinutesOnTheMthSecond(1, 31, updateRooms);
+        
+        //update UI when the minute ticks over.
+        runEveryNMinutesOnTheMthSecond(1, 1, function() { GlobalEvents.trigger('minuteChanged'); });
+    });
+    
+    GlobalEvents.bind('bookingAddedByUser', function(event, booking) {
+        EventManager.bookRoom(booking.room, booking.time, booking.duration,
+            function success() {            
+                updateRooms();
+            },
+            function failure() {
+                GlobalEvents.trigger('bookingFailure', booking);
+            }
+        );
+    });
+    
+    // update MEAT from server everyday at midnight.
+    runAtMidnight(function() { window.location.reload(); });
+})();
 
-function initUi(thisRoom) {	
-	
-	function coalesce(a,b) { return a == null ? b : a; }
-	var bookingParams = EventManagerConfig.bookingParameters || {};
-	var enabledPeriods = EventManagerConfig.enabledPeriod || {};
+function initUi(thisRoom) { 
+    
+    function coalesce(a,b) { return a == null ? b : a; }
+    var bookingParams = EventManagerConfig.bookingParameters || {};
+    var enabledPeriods = EventManagerConfig.enabledPeriod || {};
 
-	var maxBookableMinutes =          coalesce(bookingParams.maxBookableMinutes, 60),
-		minBookableMinutes =          coalesce(bookingParams.minBookableMinutes, 5),
-		maxStatusSoonMinutes =        coalesce(bookingParams.maxStatusSoonMinutes, 0),
-		minFreeTimeAdequateMinutes =  coalesce(bookingParams.minFreeTimeAdequateMinutes, 0),
-		defaultTimeBlock =            coalesce(bookingParams.defaultBookingMinutes, 30),
-		timeInterval =                coalesce(bookingParams.bookingIntervalMinutes, 15);
+    var maxBookableMinutes =          coalesce(bookingParams.maxBookableMinutes, 60),
+        minBookableMinutes =          coalesce(bookingParams.minBookableMinutes, 5),
+        maxStatusSoonMinutes =        coalesce(bookingParams.maxStatusSoonMinutes, 0),
+        minFreeTimeAdequateMinutes =  coalesce(bookingParams.minFreeTimeAdequateMinutes, 0),
+        defaultTimeBlock =            coalesce(bookingParams.defaultBookingMinutes, 30),
+        timeInterval =                coalesce(bookingParams.bookingIntervalMinutes, 15);
 
-	var enabledDays =                 coalesce(enabledPeriods.days, [ 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun' ]),
-		enabledTimeRange =            coalesce(enabledPeriods.timeRange, { start : '00:00', end : '24:00'  });
+    var enabledDays =                 coalesce(enabledPeriods.days, [ 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun' ]),
+        enabledTimeRange =            coalesce(enabledPeriods.timeRange, { start : '00:00', end : '24:00'  });
 
-	var idleTimeoutSec =              coalesce(EventManagerConfig.idleTimeoutSeconds, 30);
-	
-	var ViewModels = (function() {
+    var idleTimeoutSec =              coalesce(EventManagerConfig.idleTimeoutSeconds, 30);
+    
+    var ViewModels = (function() {
 
-		function pluralize(num, one, many) {
-			return num === 1 ? one : many;
-		}
+        function pluralize(num, one, many) {
+            return num === 1 ? one : many;
+        }
 
-		function minutesBetween(a, b) {
-			return Math.ceil((b.getTime() - a.getTime()) / 60000);
-		}
-	
-		function timeBetweenString(a, b, prefix) {
-			if (!a || !b) {
-				return "";
-			}
-			
-			var minutes = minutesBetween(a, b);
-			
-			if (minutes < 1) {
-				return "";
-			} else if (minutes < 60) {
-				return prefix + minutes + pluralize(minutes, " minute", " minutes");
-			} else {
-				var hours = Math.floor(minutes / 60);
-				if (hours < 24) {
-					return prefix + hours + pluralize(hours, " hour", " hours");
-				} else {
-					return prefix + "a long time";
-				}
-			}
-		}
+        function minutesBetween(a, b) {
+            return Math.ceil((b.getTime() - a.getTime()) / 60000);
+        }
+    
+        function timeBetweenString(a, b, prefix) {
+            if (!a || !b) {
+                return "";
+            }
+            
+            var minutes = minutesBetween(a, b);
+            
+            if (minutes < 1) {
+                return "";
+            } else if (minutes < 60) {
+                return prefix + minutes + pluralize(minutes, " minute", " minutes");
+            } else {
+                var hours = Math.floor(minutes / 60);
+                if (hours < 24) {
+                    return prefix + hours + pluralize(hours, " hour", " hours");
+                } else {
+                    return prefix + "a long time";
+                }
+            }
+        }
 
-		function minutesLeftToday() {
-			var now = DebugSettings.now() || new Date();
-			var end = new Date(new Date().getTime() + (1000 * 60 * 60 * 24));
-			end.setHours(0);
-			end.setMinutes(0);
-			end.setSeconds(0);
+        function minutesLeftToday() {
+            var now = DebugSettings.now() || new Date();
+            var end = new Date(new Date().getTime() + (1000 * 60 * 60 * 24));
+            end.setHours(0);
+            end.setMinutes(0);
+            end.setSeconds(0);
 
-			return (end - now) / 1000 / 60;
-		}
+            return (end - now) / 1000 / 60;
+        }
 
-		function minutesToDurationString(mins, isOngoing) {
-			if (mins < 60) {
-				return isOngoing ?
-					'for ' + mins + pluralize(mins, ' more min', ' more mins') :
-					'in ' + mins + pluralize(mins, ' min', ' mins');
-			}
-			if (mins > minutesLeftToday()) {
-				return isOngoing ? 'for a long time' : 'tomorrow';
-			} else {
-				var hours = Math.floor(mins / 60);
-				return isOngoing ?
-					'for ' + hours + pluralize(hours, '+ more hour', '+ more hours') :
-					'in ' + hours + pluralize(hours, ' hour', ' hours');
-			}
-		}
-		
-		function getRoomAvailability(room) {
-			var now = DebugSettings.now() || new Date(),
-				bookings = room.upcomingBookings(now),
-				availability = {
-					currentBooking : null,
-					nextBooking : null,
-					minutesTilFree : 0,
-					freeAt : now,
-					minutesFreeFor : Infinity
-				};
-				
-			if (bookings.length) {
-				var bIndex = 0;
-				var next = bookings[bIndex];
-				if (next.start < now) {
-					availability.currentBooking = {
-						title : next.title(),
-						organizer : next.organizer(),
-						minutesTilStart : 0,
-						minutesTilEnd : minutesBetween(now, next.end)
-					};
-					bIndex++;
-				}
-				next = bookings[bIndex];
-				if (next) {
-					availability.nextBooking = {
-						title : next.title(),
-						organizer : next.organizer(),
-						minutesTilStart : minutesBetween(now, next.start),
-						minutesTilEnd : minutesBetween(now, next.end)
-					};
-				}
-				
-				var freeTime = now, freeMinutes;
-				next = bookings.shift();
-				while(next && minutesBetween(freeTime, next.start) < minBookableMinutes) {
-					freeTime = next.end;
-					next = bookings.shift();
-				}
-				availability.freeAt = freeTime;
-				availability.minutesTilFree = minutesBetween(now, freeTime);
-				if (next) {
-					availability.minutesFreeFor = minutesBetween(freeTime, next.start);
-				}
-			}
-			
-			return availability;
-		}
-	
-		function getStatusClassString(minutesFreeIn, minutesFreeFor) {
-			return (
-					minutesFreeIn <= 0 ?
-						'status-free' :
-					minutesFreeIn <= maxStatusSoonMinutes ?
-						'status-soon' :
-						'status-busy'
-				) + ' ' + (
-					minutesFreeFor < minFreeTimeAdequateMinutes ?
-						'freetime-inadequate' :
-					minutesFreeFor <= maxBookableMinutes ?
-						'freetime-adequate' :
-						'freetime-long'
-				);
-		}
-			
-		return {
-			thisRoom : (function() {
-				var room;
-				return {
-					getRoom : function() { return room; },
-					getRoomStatusClassString : function() {
-						var availability = getRoomAvailability(room);
-						return getStatusClassString(availability.minutesTilFree, availability.minutesFreeFor);
-					},
-					getCurrentBooking : function() {
-						var currentBooking = getRoomAvailability(room).currentBooking;
-						if (currentBooking) {
-							currentBooking.when =
-								minutesToDurationString(currentBooking.minutesTilEnd, true);
-						}
-						return currentBooking;
-					},
-					getNextBooking : function() {
-						var nextBooking = getRoomAvailability(room).nextBooking;
-						if (nextBooking && nextBooking.minutesTilStart) {
-							nextBooking.when =
-								minutesToDurationString(nextBooking.minutesTilStart, false);
-						}
-						return nextBooking;
-					},
-					setRoom : function(theRoom) {
-						room = theRoom;
-					},
-					getDisplayedBookingCount : function() {
-						var availability = getRoomAvailability(room);
-						var bookings = 0;
-						availability.currentBooking && bookings++;
-						availability.nextBooking && bookings++;
-						return bookings;
-					},
-					sync : function() {}
-				};
-			})(),
-			otherRooms : (function() {
-				var rows = {};
-				
-				function htmlEscape(str) {
-					return str ? str.replace(/'"`<>/g, function(c) {
-						switch(c) {
-							case "'": return "&apos;";
-							case '"': return "&quot;";
-							case "`": return "&apos;";
-							case "<": return "&lt;";
-							case ">": return "&gt;";
-							default: return "&#" + c.charCodeAt(0) + ";";
-						}
-					}) : str;
-				}
-				
-				function rowCompareTo(otherRow) {
-					var now = DebugSettings.now() || new Date(),
-						aRoom = this.getRoom(),
-						bRoom = otherRow.getRoom(),
-						aNextFree = aRoom.nextFreeTime(now),
-						bNextFree = bRoom.nextFreeTime(now),
-						aMinutesToFree = minutesBetween(now, aNextFree),
-						bMinutesToFree = minutesBetween(now, bNextFree);
-					
-					//free at the same time
-					if (aMinutesToFree == bMinutesToFree) {
-						//how long for?
-						var aBookedAt = aRoom.nextEventTime(aNextFree),
-							bBookedAt = bRoom.nextEventTime(bNextFree),
-							aFreeMinutes = aBookedAt ? minutesBetween(aNextFree, aBookedAt) : Infinity,
-							bFreeMinutes = bBookedAt ? minutesBetween(bNextFree, bBookedAt) : Infinity;
-					
-						if (aFreeMinutes == bFreeMinutes || (aFreeMinutes > maxBookableMinutes && bFreeMinutes > maxBookableMinutes)) {
-							return aRoom.name() < bRoom.name() ? -1 : 1;
-						} else {
-							return aFreeMinutes > bFreeMinutes ? -1 : 1;
-						}
-					} else {
-						//one is free first
-						return aMinutesToFree < bMinutesToFree ? -1 : 1;
-					}
-					
-				}
-				
-				return {
-					createRoomRowViewModel : function(room) {
-						if (rows.hasOwnProperty(room.id())) {
-							throw new Error("A row has already been created for room " + room.simpleName() + " (ID: " + room.id() + ")");
-						}
-						
-						return rows[room.id()] = {
-								sync : function() {},
-								getHtmlId : function() { return htmlEscape(room.id()); },
-								getDisplayName : function() { return room.simpleName(); },
-								getRoom : function() { return room; },
-								getCssClass : function() {
-									var availability = getRoomAvailability(room);
-									return getStatusClassString(availability.minutesTilFree, availability.minutesFreeFor);
-								},
-								compareTo : rowCompareTo
-							};
-					},
-					getRow : function(room) {
-						return rows.hasOwnProperty(room.id()) ? rows[room.id()] : undefined;
-					}
-				};
-			})(),
-			bookingData : (function() {
-				var bookingRoom,
-					availability,
-					bookingDuration;
-				return {
-					getBookingRoom : function() { return bookingRoom; },
-					getBookingRoomName : function() { return bookingRoom.simpleName(); },
-					getTimeFreeAtString : function() {
-						return timeBetweenString(DebugSettings.now() || new Date(), availability.freeAt, "in ");
-					},
-					getTimeAvailableString : function() {
-						return availability.minutesFreeFor >= maxBookableMinutes ? maxBookableMinutes + '+' : availability.minutesFreeFor;
-					},
-					addTimeInterval : function() {
-						bookingDuration += timeInterval;
-						if (bookingDuration > maxBookableMinutes || bookingDuration > availability.minutesFreeFor)
-							bookingDuration = Math.min(maxBookableMinutes, availability.minutesFreeFor);
-						return bookingDuration;
-					},
-					subtractTimeInterval : function() {
-						if (bookingDuration % timeInterval == 0 ) {
-							bookingDuration -= timeInterval;
-						} else {
-							bookingDuration -= bookingDuration % timeInterval;
-						}
-						if (bookingDuration < 0)
-							bookingDuration = Math.max(minBookableMinutes, Math.min(timeInterval, availability.minutesFreeFor));
-						return bookingDuration;
-					},
-					canAddTime : function() {
-						return bookingDuration < Math.min(availability.minutesFreeFor, maxBookableMinutes);
-					},
-					canSubtractTime : function() {
-						return bookingDuration > timeInterval;
-					},
-					getBookingDuration : function () {
-						return bookingDuration;
-					},
-					getBookingTime : function () {
-						var date = availability.freeAt || DebugSettings.now() || new Date();
-						date.setSeconds(0, 0);
-						return date;
-					},
-					canBook : function() {
-						return bookingDuration >= minBookableMinutes && bookingDuration <= Math.min(availability.minutesFreeFor, maxBookableMinutes);
-					},
-					setRoom : function(room) {
-						bookingRoom = room;
-						availability = getRoomAvailability(bookingRoom);
-						bookingDuration = availability.minutesFreeFor < defaultTimeBlock ?
-							(availability.minutesFreeFor < timeInterval ?
-								availability.minutesFreeFor :
-								Math.floor(availability.minutesFreeFor/timeInterval) * timeInterval
-							) :
-							defaultTimeBlock;
-					},
-					updateTimes : function() {
-						availability = getRoomAvailability(bookingRoom);
-						bookingDuration = Math.min(bookingDuration, Math.min(maxBookableMinutes, availability.minutesFreeFor));
-					}
-				}; 
-			})(),
-		};
-	})();
-		
-	var Stages = (function() {
-		var $body = $('body'),
-			$close = $('#close').click(function (e) {
-				revertToPreviousStage();
-				e.stopPropagation();
-			});
-		
-		var currStage,
-			prevStages = [ ];
-		
-		function switchTo(newStage, asRevert) {
-			if (newStage && currStage !== Switching && currStage !== newStage) {
-				var prevStage = currStage;
-				currStage = Switching;
-				
-				if (prevStage) {
-					// If we're switching "backwards", don't push the current stage onto the stack
-					asRevert || prevStages.push(prevStage);
-					$body.queue(prevStage.exit);
-				}
-				
-				$body.queue(newStage.enter).queue(function() {
-					currStage = newStage;
-					$body.dequeue();
-				});
-			}
-		}
-		function revertToPreviousStage() {
-			if (currStage !== Switching) {
-				var newStage = prevStages.pop();
-				if (newStage) {
-					switchTo(newStage, true);
-				}
-			}
-		}
-		function revertToInitial() {
-			prevStages.length && (prevStages = [ prevStages[0] ]);
-			revertToPreviousStage();
-		}
-	
-		var Status = (function() {
-				var self,
-					model,
-					idleTimeout,
-					$container,
-					$status,
-					$statusMinutes,
-					$events,
-					$currentEvent,
-					$nextEvent;
-				return self = {
-					name : 'status',
-					enter : function() {
-						$body.removeClass().addClass("show-status");	
-						
-						if (idleTimeout) {
-							ActivityMonitor.clearIdleHandler(idleTimeout);
-							idleTimeout = null;
-						}
-						
-						self.update();
-						$status.fadeIn('slow', function() {
-							$status.css('display', '');
-							$body.dequeue();
-						});
-					},
-					exit : function() {
-						$status.fadeOut('fast', function() {
-							$body.removeClass();
-							
-							if (!idleTimeout) {
-								idleTimeout = ActivityMonitor.setIdleHandler(idleTimeoutSec * 1000, revertToInitial);
-							}
-							
-							$body.dequeue();
-						});
-					},
-					init : function($theContainer, thisRoom) {
-						model = ViewModels.thisRoom;
-						model.setRoom(thisRoom);
-						
-						$container = $theContainer;
-						$status = $('#status', $container).click(function(e) {
-							if (!model.getCurrentBooking()) {
-								Book.setRoom(model.getRoom());
-								switchTo(Book);
-							} else {
-								switchTo(RoomList);
-							}
-							e.stopPropagation();
-						});
-						$statusMinutes = $('#minutes-free', $status);
-						$events = $('.events', $status);
-						$currentEvent = $('#current-event', $events);
-						$nextEvent = $('#next-event', $events);
-						
-						GlobalEvents.bind('minuteChanged', self.update);
-						GlobalEvents.bind('roomUpdatedByServer', function(event, room) {
-							room === model.getRoom() && self.update();
-						});
-					},
-					update : (function() {
-						function updateEventDOM($eventDOM, event) {
-							if (event) {
-								$eventDOM.removeClass('hidden');
-								
-								var title = event.title || '',
-									organizer = event.organizer || '',
-									when = event.when;
-								$eventDOM.children('.title').text(title);
-								$eventDOM.children('.organizer').text(organizer);
-								$eventDOM.children('.when').text(when);
-								$eventDOM.appendTo($events);
-							} else {
-								$eventDOM.detach();
-							}
-						}
-						return function() {
-							$container
-								.removeClass()
-								.addClass(model.getRoomStatusClassString());
-							
-							updateEventDOM($currentEvent, model.getCurrentBooking());
-							updateEventDOM($nextEvent, model.getNextBooking());
-							
-							$status.removeClass().addClass("events-upcoming-" + model.getDisplayedBookingCount());
-						};
-					})()
-				};
-			})(),
-			RoomList = (function() {
-				var self,
-					model,
-					$rooms,
-					$roomsList;
-				
-				function sortRoomList() {
-				
-					var $rooms = $roomsList.children();
-					var roomArray = $.makeArray($rooms.detach());
-						roomArray.sort(function(a, b) {
-							return $(a).data('model').compareTo($(b).data('model'));
-						});
-					$(roomArray).appendTo($roomsList);
-				}
-				
-				return self = {
-					name : 'rooms',
-					enter : function() {
-						$body.removeClass().addClass("show-rooms");				
-						$rooms.fadeIn('slow',function(){
-							$close.toggleClass('hidden', !thisRoom);
-							$rooms.css('display', '');
-							$body.dequeue();
-						});
-					},
-					exit : function() {
-						$rooms.fadeOut('fast',function() {
-							$body.removeClass('show-rooms');
-							$close.addClass('hidden');
-							$body.dequeue();
-						});
-					},
-					init : function($root) {
-						model = ViewModels.otherRooms;
-						$rooms = $root;
-						$roomsList = $rooms.children('ul');
-						GlobalEvents.bind('roomLoaded', function(event, room) {
-							self.createRow(model.createRoomRowViewModel(room));
-						});
-						GlobalEvents.bind('roomUpdatedByServer', function(event, room) {
-							self.updateRow(room);
-							sortRoomList();
-						});
-						GlobalEvents.bind('minuteChanged', self.updateAllRows);
-						self.reset();
-					},
-					createRow : function(rowModel) {
-						var $row = $('<li><strong></strong></li>');
-						$row
-							.attr('id', rowModel.getHtmlId())
-							.data('model', rowModel)
-							.find('strong')
-								.text(rowModel.getDisplayName());
-						$row.click(function(e) {
-							Book.setRoom(rowModel.getRoom());
-							switchTo(Book);
-							e.stopPropagation();
-						});
-						$roomsList.append($row);
-						self.updateRow(rowModel);
-						sortRoomList();
-					},
-					updateRow : function(roomOrRow) {
-						var row = roomOrRow.getCssClass && roomOrRow.getHtmlId ?
-								roomOrRow :
-								model.getRow(roomOrRow);
-						$(document.getElementById(row.getHtmlId()))
-							.removeClass()
-							.addClass(row.getCssClass());
-					},
-					updateAllRows : function() {
-						$roomsList.children().each(function() {
-							self.updateRow($(this).data('model'));
-						});
-						sortRoomList();
-					},
-					reset : function() {
-						$roomsList.children().remove();
-					}
-				};
-			})(),
-			Book = (function() {
-				var self,
-					model,
-					$booking,
-					$roomName,
-					$freeIn,
-					$timeAvailable,
-					$timeRequired,
-					$timeMore,
-					$timeLess,
-					$freeAt;
-					
-					function onTimeRequiredClicked(e) {
-						if (!$timeRequired.hasClass('disabled')) {
-							var bookingRoom = model.getBookingRoom(),
-								onComplete = function() {
-								};
-							$timeRequired
-								.text("Booked")
-								.siblings()
-									.addClass('hidden')
-								.end()
-								.queue(function() {
-									var onComplete = function() {
-										GlobalEvents.unbind('roomUpdatedByServer', onSuccess);
-										GlobalEvents.unbind('bookingFailure', onFailure);
-										
-										switchTo(thisRoom ? Status : RoomList);
-										$timeRequired.dequeue();
-									}, onSuccess = function (event, room) {
-										if (room === bookingRoom) {
-											onComplete();
-										}
-									}, onFailure = function(event, booking) {
-										if (booking.room === bookingRoom) {
-											$timeRequired.text('ERROR');
-											setTimeout(onComplete, 2000);
-										}
-									};
-									GlobalEvents.bind('roomUpdatedByServer', onSuccess);
-									GlobalEvents.bind('bookingFailure', onFailure);
-									GlobalEvents.trigger('bookingAddedByUser', {
-										room : bookingRoom,
-										title : 'Impromptu Meeting',
-										time : model.getBookingTime(),
-										duration : model.getBookingDuration()
-									});
-								});
-						}
-						return false;
-					}
-					function onMoreTimeClicked(e) {
-						if (!$timeMore.hasClass('disabled')) {
-							$timeRequired.text(model.addTimeInterval());
-							$timeMore.toggleClass('disabled', !model.canAddTime());
-							$timeLess.toggleClass('disabled', !model.canSubtractTime());
-						}
-						
-						return false;
-					}
-					function onLessTimeClicked(e) {
-						if (!$timeLess.hasClass('disabled')) {
-							$timeRequired.text(model.subtractTimeInterval());
-							$timeMore.toggleClass('disabled', !model.canAddTime());
-							$timeLess.toggleClass('disabled', !model.canSubtractTime());
-						}
-						
-						return false;
-					}
-					
-				return self = {
-					name : 'book',
-					enter : function() {
-						self.reset();
-						$body.removeClass().addClass("show-booking");
-						$booking.fadeIn('slow',function(){
-							$booking.css('display', '');
-							$close.removeClass('hidden');
-							$body.dequeue();
-						});
-					},
-					exit : function() {
-						$booking.fadeOut('fast',function(){
-							$body.removeClass('show-booking');
-							$close.addClass('hidden');
-							$body.dequeue();
-						});
-					},
-					init : function($root) {
-						model = ViewModels.bookingData;
-						
-						GlobalEvents.bind('minuteChanged', function() {
-							if (model.getBookingRoom()) {
-								model.updateTimes();
-								$timeAvailable.text(model.getTimeAvailableString());
-								$timeRequired.text(model.getBookingDuration()).toggleClass('disabled', !model.canBook());
-								$freeAt.text(model.getTimeFreeAtString());
-							}
-						});
-						
-						$booking = $root;
-						
-						$timeAvailable = $('#info .time-available', $root);
-						$timeRequired = $("#time-required", $root).click(onTimeRequiredClicked);
-						$timeMore = $("#time-more", $root).click(onMoreTimeClicked);
-						$timeLess = $("#time-less", $root).click(onLessTimeClicked);
-						$roomName = $('#room-name', $root);
-						$freeAt = $('.free-at', $root);
-					},
-					setRoom : function(room) {
-						model.setRoom(room);
-					},
-					reset : function() {
-						$roomName.text(model.getBookingRoomName());
-						$timeAvailable.text(model.getTimeAvailableString());
-						$timeRequired.removeClass('disabled').text(model.getBookingDuration());
-						$freeAt.text(model.getTimeFreeAtString());
-						$timeMore.removeClass('hidden').toggleClass('disabled', !model.canAddTime());
-						$timeLess.removeClass('hidden').toggleClass('disabled', !model.canSubtractTime());
-					}
-				};
-			})(),
-			Switching = {};
-		
-		return {
-			init : function(thisRoom) {
-				Book.init($('#booking'));
-				RoomList.init($('#rooms'));
-				if (thisRoom) {
-					Status.init($('#container'), thisRoom);
-					switchTo(Status);
-				} else {
-					switchTo(RoomList);
-				}
-			}
-		};
-	})();
-	
+        function minutesToDurationString(mins, isOngoing) {
+            if (mins < 60) {
+                return isOngoing ?
+                    'for ' + mins + pluralize(mins, ' more min', ' more mins') :
+                    'in ' + mins + pluralize(mins, ' min', ' mins');
+            }
+            if (mins > minutesLeftToday()) {
+                return isOngoing ? 'for a long time' : 'tomorrow';
+            } else {
+                var hours = Math.floor(mins / 60);
+                return isOngoing ?
+                    'for ' + hours + pluralize(hours, '+ more hour', '+ more hours') :
+                    'in ' + hours + pluralize(hours, ' hour', ' hours');
+            }
+        }
+        
+        function getRoomAvailability(room) {
+            var now = DebugSettings.now() || new Date(),
+                bookings = room.upcomingBookings(now),
+                availability = {
+                    currentBooking : null,
+                    nextBooking : null,
+                    minutesTilFree : 0,
+                    freeAt : now,
+                    minutesFreeFor : Infinity
+                };
+                
+            if (bookings.length) {
+                var bIndex = 0;
+                var next = bookings[bIndex];
+                if (next.start < now) {
+                    availability.currentBooking = {
+                        title : next.title(),
+                        organizer : next.organizer(),
+                        minutesTilStart : 0,
+                        minutesTilEnd : minutesBetween(now, next.end)
+                    };
+                    bIndex++;
+                }