]> git.example.dev Git - binbsis50.git/commitdiff
reformatted files to have 2 space indentations
authorLuigi Pinca <luigipinca@gmail.com>
Sat, 23 Feb 2013 11:48:16 +0000 (12:48 +0100)
committerLuigi Pinca <luigipinca@gmail.com>
Sat, 23 Feb 2013 14:14:25 +0000 (15:14 +0100)
26 files changed:
app.js
lib/captcha.js
lib/email/mailer.js
lib/email/template.jade
lib/match.js
lib/redis-clients.js
lib/room.js
lib/stats.js
lib/user.js
lib/utils.js
public/css/style.css
public/js/app.js
public/js/home.js
public/js/leaderboards.js
routes/site.js
routes/user.js
views/changepasswd.jade
views/home.jade
views/layout.jade
views/leaderboards.jade
views/login.jade
views/recoverpasswd.jade
views/resetpasswd.jade
views/room.jade
views/signup.jade
views/user.jade

diff --git a/app.js b/app.js
index 9c3cc6c3a5b0f2aa31e1c1623cf5956714754f85..129416a18c3b0d2e52d44ace20ba9dc1abc3e6cd 100644 (file)
--- a/app.js
+++ b/app.js
@@ -3,22 +3,22 @@
  */
 
 var config = require('./config')
-    , express = require('express')
-    , http = require('http')
-    , parseCookie = require('express/node_modules/cookie').parse
-    , parseSignedCookies = require('express/node_modules/connect').utils.parseSignedCookies
-    , redisstore = require('connect-redis')(express)
-    , site = require('./routes/site')
-    , user = require('./routes/user')
-    , usersdb = require('./lib/redis-clients').users;
+  , express = require('express')
+  , http = require('http')
+  , parseCookie = require('express/node_modules/cookie').parse
+  , parseSignedCookies = require('express/node_modules/connect').utils.parseSignedCookies
+  , redisstore = require('connect-redis')(express)
+  , site = require('./routes/site')
+  , user = require('./routes/user')
+  , usersdb = require('./lib/redis-clients').users;
 
 /**
  * Setting up Express.
  */
 
 var app = express()
-    , pub = __dirname + '/public' // Path to public directory
-    , sessionstore = new redisstore({client: usersdb});
+  , pub = __dirname + '/public' // Path to public directory
+  , sessionstore = new redisstore({client: usersdb});
 
 // Configuration
 app.set('view engine', 'jade');
@@ -55,7 +55,7 @@ var server = http.createServer(app);
  */
 
 var io = require('socket.io').listen(server)
-    , sockets = Object.create(null); // Sockets of all rooms
+  , sockets = Object.create(null); // Sockets of all rooms
 
 // Configuration
 io.enable('browser client minification');
@@ -63,106 +63,106 @@ io.enable('browser client etag');
 io.enable('browser client gzip');
 io.set('log level', 1);
 io.set('transports', [
-    'websocket'
-    , 'htmlfile'
-    , 'xhr-polling'
-    , 'jsonp-polling'
+  'websocket'
+  , 'htmlfile'
+  , 'xhr-polling'
+  , 'jsonp-polling'
 ]);
 
 // Authorization
 io.set('authorization', function(data, accept) {
-    if(!data.headers.cookie) {
-        return accept('no cookie transmitted', false);
+  if(!data.headers.cookie) {
+    return accept('no cookie transmitted', false);
+  }
+  var signedcookie = parseCookie(data.headers.cookie);
+  var cookie = parseSignedCookies(signedcookie, process.env.SITE_SECRET);
+  sessionstore.get(cookie['connect.sid'], function(err, session) {
+    if (err) {
+      return accept(err.message, false);
     }
-    var signedcookie = parseCookie(data.headers.cookie);
-    var cookie = parseSignedCookies(signedcookie, process.env.SITE_SECRET);
-    sessionstore.get(cookie['connect.sid'], function(err, session) {
-        if (err) {
-            return accept(err.message, false);
-        }
-        else if (!session) {
-            var debuginfos = {
-                address: data.headers['x-forwarded-for'],
-                ua: data.headers['user-agent'],
-                cookie: data.headers.cookie
-            };
-            console.log(debuginfos);
-            return accept('session not found', false);
-        }
-        data.session = session;
-        accept(null, true);
-    });
+    else if (!session) {
+      var debuginfos = {
+        address: data.headers['x-forwarded-for'],
+        ua: data.headers['user-agent'],
+        cookie: data.headers.cookie
+      };
+      console.log(debuginfos);
+      return accept('session not found', false);
+    }
+    data.session = session;
+    accept(null, true);
+  });
 });
 
 io.sockets.on('connection', function(socket) {
-    var session = socket.handshake.session;
-    socket.on('disconnect', function() {
-        if (socket.roomname) {
-            rooms[socket.roomname].removeUser(socket.nickname);
-        }
-    });
-    socket.on('getoverview', function(callback) {
-        if (typeof callback !== 'function') {
-            return;
-        }
-        var data = Object.create(null);
-        for (var prop in rooms) {
-            data[prop] = rooms[prop].getPopulation();
-        }
-        callback(data);
-    });
-    socket.on('getstatus', function(callback) {
-        if (socket.roomname && typeof callback === 'function') {
-            rooms[socket.roomname].sendStatus(callback);
-        }
-    });
-    socket.on('guess', function(guess) {
-        if (socket.roomname && typeof guess === 'string') {
-            rooms[socket.roomname].guess(socket, guess);
-        }
-    });
-    socket.on('ignore', function(who, callback) {
-        if (socket.roomname && typeof who === 'string' && typeof callback === 'function') {
-            rooms[socket.roomname].ignore(who, socket.nickname, callback);
-        }
-    });
-    socket.on('joinanonymously', function(nickname, roomname) {
-        if (!socket.nickname && typeof nickname === 'string' && ~config.rooms.indexOf(roomname)) {
-            rooms[roomname].setNickName(socket, nickname);
-        }
-    });
-    socket.on('joinroom', function(room) {
-        if (session.user && ~config.rooms.indexOf(room)) {
-            if (sockets[session.user]) { // User already in a room
-                socket.emit('alreadyinaroom');
-                return;
-            }
-            socket.nickname = session.user;
-            rooms[room].joinRoom(socket);
-        }
-    });
-    socket.on('kick', function(who, why, callback) {
-        if (socket.roomname && typeof who === 'string' && typeof why === 'string' &&
-            typeof callback === 'function') {
-            rooms[socket.roomname].kick(who, why, socket.nickname, callback);
-        }
-    });
-    socket.on('loggedin', function(callback) {
-        if (typeof callback !== 'function') {
-            return;
-        }
-        return (session.user) ? callback(session.user) : callback(false);
-    });
-    socket.on('sendchatmsg', function(msg, to) {
-        if (socket.roomname && typeof msg === 'string') {
-            rooms[socket.roomname].sendChatMessage(msg, socket, to);
-        }
-    });
-    socket.on('unignore', function(who) {
-        if (socket.roomname && typeof who === 'string') {
-            rooms[socket.roomname].unignore(who, socket.nickname);
-        }
-    });
+  var session = socket.handshake.session;
+  socket.on('disconnect', function() {
+    if (socket.roomname) {
+      rooms[socket.roomname].removeUser(socket.nickname);
+    }
+  });
+  socket.on('getoverview', function(callback) {
+    if (typeof callback !== 'function') {
+      return;
+    }
+    var data = Object.create(null);
+    for (var prop in rooms) {
+      data[prop] = rooms[prop].getPopulation();
+    }
+    callback(data);
+  });
+  socket.on('getstatus', function(callback) {
+    if (socket.roomname && typeof callback === 'function') {
+      rooms[socket.roomname].sendStatus(callback);
+    }
+  });
+  socket.on('guess', function(guess) {
+    if (socket.roomname && typeof guess === 'string') {
+      rooms[socket.roomname].guess(socket, guess);
+    }
+  });
+  socket.on('ignore', function(who, callback) {
+    if (socket.roomname && typeof who === 'string' && typeof callback === 'function') {
+      rooms[socket.roomname].ignore(who, socket.nickname, callback);
+    }
+  });
+  socket.on('joinanonymously', function(nickname, roomname) {
+    if (!socket.nickname && typeof nickname === 'string' && ~config.rooms.indexOf(roomname)) {
+      rooms[roomname].setNickName(socket, nickname);
+    }
+  });
+  socket.on('joinroom', function(room) {
+    if (session.user && ~config.rooms.indexOf(room)) {
+      if (sockets[session.user]) { // User already in a room
+        socket.emit('alreadyinaroom');
+        return;
+      }
+      socket.nickname = session.user;
+      rooms[room].joinRoom(socket);
+    }
+  });
+  socket.on('kick', function(who, why, callback) {
+    if (socket.roomname && typeof who === 'string' && typeof why === 'string' &&
+      typeof callback === 'function') {
+      rooms[socket.roomname].kick(who, why, socket.nickname, callback);
+    }
+  });
+  socket.on('loggedin', function(callback) {
+    if (typeof callback !== 'function') {
+      return;
+    }
+    return (session.user) ? callback(session.user) : callback(false);
+  });
+  socket.on('sendchatmsg', function(msg, to) {
+    if (socket.roomname && typeof msg === 'string') {
+      rooms[socket.roomname].sendChatMessage(msg, socket, to);
+    }
+  });
+  socket.on('unignore', function(who) {
+    if (socket.roomname && typeof who === 'string') {
+      rooms[socket.roomname].unignore(who, socket.nickname);
+    }
+  });
 });
 
 /**
@@ -170,14 +170,14 @@ io.sockets.on('connection', function(socket) {
  */
 
 var Room = require('./lib/room')({io: io, sockets: sockets})
-    , rooms = Object.create(null); // The Object that contains all the room instances
+  , rooms = Object.create(null); // The Object that contains all the room instances
 
 for (var i=0; i<config.rooms.length; i++) {
-    rooms[config.rooms[i]] = new Room(config.rooms[i]);
-    rooms[config.rooms[i]].start();
+  rooms[config.rooms[i]] = new Room(config.rooms[i]);
+  rooms[config.rooms[i]].start();
 }
 
 // Begin accepting connections
 server.listen(config.port, function() {
-    console.log('binb server listening on port ' + config.port);
+  console.log('binb server listening on port ' + config.port);
 });
index 08065e236f7760625404744f234bdb8ef0855e3f..5c2b7c92b55616dd83753a7fcf59ea54a7b6b8ef 100644 (file)
@@ -3,35 +3,35 @@
  */
 
 var canvas = require('canvas')
-    , characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+  , characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
 
 /**
  * Expose the constructor.
  */
 
 module.exports = function() {
-    var code = ''
-        , _canvas = new canvas(64, 26)
-        , ctx = _canvas.getContext('2d');
+  var code = ''
+    , _canvas = new canvas(64, 26)
+    , ctx = _canvas.getContext('2d');
 
-    while (code.length < 4) {
-        code += characters[Math.floor(Math.random() * characters.length)];
-    }
+  while (code.length < 4) {
+    code += characters[Math.floor(Math.random() * characters.length)];
+  }
 
-    ctx.fillStyle = '#DDDDDD';
-    ctx.fillRect(0, 0, 64, 26);
-    ctx.font = 'bold 20px Helvetica';
-    ctx.lineWidth = 1;
-    ctx.textAlign = "center";
-    ctx.strokeStyle = '#080';
-    ctx.strokeText(code, 31, 20);
-    ctx.save();
+  ctx.fillStyle = '#DDDDDD';
+  ctx.fillRect(0, 0, 64, 26);
+  ctx.font = 'bold 20px Helvetica';
+  ctx.lineWidth = 1;
+  ctx.textAlign = "center";
+  ctx.strokeStyle = '#080';
+  ctx.strokeText(code, 31, 20);
+  ctx.save();
 
-    this.getCode = function() {
-        return code;
-    };
+  this.getCode = function() {
+    return code;
+  };
 
-    this.toDataURL = function() {
-        return _canvas.toDataURL();
-    };
+  this.toDataURL = function() {
+    return _canvas.toDataURL();
+  };
 };
index a1e67b6682cd4cb112d54dbe32931ece008bd0df..e6508e6440eb47de600d8aa00745bdf72bb3ef4a 100644 (file)
@@ -3,10 +3,10 @@
  */
 
 var fs = require('fs')
-    , jade = require('jade')
-    , nodemailer = require('nodemailer')
-    , jadetemplate = fs.readFileSync(__dirname + '/template.jade')
-    , texttemplate = fs.readFileSync(__dirname + '/template.txt', 'utf-8');
+  , jade = require('jade')
+  , nodemailer = require('nodemailer')
+  , jadetemplate = fs.readFileSync(__dirname + '/template.jade')
+  , texttemplate = fs.readFileSync(__dirname + '/template.txt', 'utf-8');
 
 /**
  * Generate the HTML version of the message.
@@ -19,7 +19,7 @@ var HTMLMessage = jade.compile(jadetemplate);
  */
 
 var plaintextMessage = function(token) {
-    return texttemplate.replace(/<token>/, token);
+  return texttemplate.replace(/<token>/, token);
 };
 
 /**
@@ -27,11 +27,11 @@ var plaintextMessage = function(token) {
  */
 
 var transport = nodemailer.createTransport('SMTP', {
-    service: 'SendGrid',
-    auth: {
-        user: process.env.SENDGRID_USER,
-        pass: process.env.SENDGRID_PASS
-    }
+  service: 'SendGrid',
+  auth: {
+    user: process.env.SENDGRID_USER,
+    pass: process.env.SENDGRID_PASS
+  }
 });
 
 /**
@@ -39,16 +39,16 @@ var transport = nodemailer.createTransport('SMTP', {
  */
 
 exports.sendEmail = function(to, token, callback) {
-    transport.sendMail({
-        from: 'binb <no-reply@binb.nodejitsu.com>',
-        to: to,
-        subject: 'binb password recovery',
-        html: HTMLMessage({token:token}),
-        text: plaintextMessage(token)
-    }, function(err, response) {
-        if(err) {
-            return callback(err);
-        }
-        callback(null, response);
-    });
+  transport.sendMail({
+    from: 'binb <no-reply@binb.nodejitsu.com>',
+    to: to,
+    subject: 'binb password recovery',
+    html: HTMLMessage({token:token}),
+    text: plaintextMessage(token)
+  }, function(err, response) {
+    if(err) {
+      return callback(err);
+    }
+    callback(null, response);
+  });
 };
index 99041e0353d1c8846b3326c4e462b357eb1fff8e..624bee03ec7a1ccd063074a1ef6c808037ed3014 100644 (file)
@@ -1,22 +1,22 @@
 doctype html
 html
-    head
-        meta(charset="UTF-8")
-        title binb password recovery
-    body(style="font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;")
-        div(style="width:600px;margin:0 auto;border:3px solid #CCC;border-radius:5px;background-color:#F0F0F0;")
-            div(style="padding:16px 16px 0;font-size:15px;color:#505050;")
-                img(alt="logo",src="https://dl.dropbox.com/u/58444696/binb-logo.png")
-                h2 Password recovery
-                p To initiate the password reset process for your binb account,
-                    | click the link below:
-                a(style="color:#0088CC;text-decoration:none;",
-                    href="http://binb.nodejitsu.com/resetpasswd?token=#{token}")
-                    | http://bind.nodejitsu.com/resetpasswd?token=#{token}
-                p If you can't click it, please copy and paste the URL in a new tab/window.
-                p If you haven't requested a password reset, you can disregard this message, 
-                    | because it's  likely that another user entered your email address 
-                    | by mistake while trying to reset a password.
-                p.note(style="font-size:13px;")
-                    b Note:
-                    |  This email cannot accept replies.
+  head
+    meta(charset="UTF-8")
+    title binb password recovery
+  body(style="font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;")
+    div(style="width:600px;margin:0 auto;border:3px solid #CCC;border-radius:5px;background-color:#F0F0F0;")
+      div(style="padding:16px 16px 0;font-size:15px;color:#505050;")
+        img(alt="logo",src="https://dl.dropbox.com/u/58444696/binb-logo.png")
+        h2 Password recovery
+        p To initiate the password reset process for your binb account,
+          | click the link below:
+        a(style="color:#0088CC;text-decoration:none;",
+          href="http://binb.nodejitsu.com/resetpasswd?token=#{token}")
+          | http://bind.nodejitsu.com/resetpasswd?token=#{token}
+        p If you can't click it, please copy and paste the URL in a new tab/window.
+        p If you haven't requested a password reset, you can disregard this message, 
+          | because it's  likely that another user entered your email address 
+          | by mistake while trying to reset a password.
+        p.note(style="font-size:13px;")
+          b Note:
+          |  This email cannot accept replies.
index 8fef54a9080b37d33b47d2dd6ab10409e7fe3254..36c1776c9428c565f10078c5ea9c79b061991c64 100644 (file)
@@ -14,41 +14,41 @@ var threshold = require('../config').allowederrors;
  */
 
 var checkDistance = function(s1, s2, k) {
-    if (k === 0) {
-        return s1 === s2;
-    }
-    if (Math.abs(s1.length - s2.length) > k) {
-        return false;
-    }
-    var d = [];
-    for (var i=0; i <= s1.length; i++) {
-        d[i] = []; // Now d is a matrix with s1.length + 1 rows
-        d[i][0] = i;
-    }
-    for (var j=1; j <= s2.length; j++) {
-        d[0][j] = j;
-    }
-    for (i=1; i <= s1.length; i++) {
-        var l = ((i-k) < 1) ? 1 : i-k;
-        var m = ((i+k) > s2.length) ? s2.length : i+k;
-        for (j=l; j<=m; j++) {
-            if (s1.charAt(i-1) === s2.charAt(j-1)) {
-                d[i][j] = d[i-1][j-1];
-            }
-            else {
-                if ((j === l) && (d[i][j-1] === undefined)) {
-                    d[i][j] = Math.min(d[i-1][j-1]+1, d[i-1][j]+1);
-                }
-                else if ((j === m) && (d[i-1][j] === undefined)) {
-                    d[i][j] = Math.min(d[i][j-1]+1, d[i-1][j-1]+1);
-                }
-                else {
-                    d[i][j] = Math.min(d[i][j-1]+1, d[i-1][j-1]+1, d[i-1][j]+1);
-                }
-            }
+  if (k === 0) {
+    return s1 === s2;
+  }
+  if (Math.abs(s1.length - s2.length) > k) {
+    return false;
+  }
+  var d = [];
+  for (var i=0; i <= s1.length; i++) {
+    d[i] = []; // Now d is a matrix with s1.length + 1 rows
+    d[i][0] = i;
+  }
+  for (var j=1; j <= s2.length; j++) {
+    d[0][j] = j;
+  }
+  for (i=1; i <= s1.length; i++) {
+    var l = ((i-k) < 1) ? 1 : i-k;
+    var m = ((i+k) > s2.length) ? s2.length : i+k;
+    for (j=l; j<=m; j++) {
+      if (s1.charAt(i-1) === s2.charAt(j-1)) {
+        d[i][j] = d[i-1][j-1];
+      }
+      else {
+        if ((j === l) && (d[i][j-1] === undefined)) {
+          d[i][j] = Math.min(d[i-1][j-1]+1, d[i-1][j]+1);
+        }
+        else if ((j === m) && (d[i-1][j] === undefined)) {
+          d[i][j] = Math.min(d[i][j-1]+1, d[i-1][j-1]+1);
         }
+        else {
+          d[i][j] = Math.min(d[i][j-1]+1, d[i-1][j-1]+1, d[i-1][j]+1);
+        }
+      }
     }
-    return d[s1.length][s2.length] <= k;
+  }
+  return d[s1.length][s2.length] <= k;
 };
 
 /**
@@ -56,79 +56,79 @@ var checkDistance = function(s1, s2, k) {
  */
 
 module.exports = function(subject, guess, enableartistrules) {
-    if (checkDistance(subject, guess, threshold)) {
-        return true;
-    }
+  if (checkDistance(subject, guess, threshold)) {
+    return true;
+  }
 
-    // Ignore dots
-    if (/\./.test(subject) && 
-        checkDistance(subject.replace(/\./g, ''), guess, threshold)) {
-        return true;
-    }
-    // Ignore dashes
-    if (/\-/.test(subject) && 
-        checkDistance(subject.replace(/\-/g, ''), guess, threshold)) {
-        return true;
-    }
-    // Allow to write "and" in place of "+"
-    if (/\+/.test(subject) && 
-        checkDistance(subject.replace(/\+/, 'and'), guess, threshold)) {
+  // Ignore dots
+  if (/\./.test(subject) && 
+    checkDistance(subject.replace(/\./g, ''), guess, threshold)) {
+    return true;
+  }
+  // Ignore dashes
+  if (/\-/.test(subject) && 
+    checkDistance(subject.replace(/\-/g, ''), guess, threshold)) {
+    return true;
+  }
+  // Allow to write "and" in place of "+"
+  if (/\+/.test(subject) && 
+    checkDistance(subject.replace(/\+/, 'and'), guess, threshold)) {
+    return true;
+  }
+  // Allow to write "and" in place of " & "
+  if (/ & /.test(subject) && !/\(/.test(subject) &&
+    checkDistance(subject.replace(/ & /, ' and '), guess, threshold)) {
+    return true;
+  }
+
+  if (enableartistrules) {
+    // Ignore "the" at the beginning of artist name
+    if (/^the /.test(subject)) {
+      var nothe = subject.replace(/^the /, '');
+      if (checkDistance(nothe, guess, threshold)) {
         return true;
-    }
-    // Allow to write "and" in place of " & "
-    if (/ & /.test(subject) && !/\(/.test(subject) &&
-        checkDistance(subject.replace(/ & /, ' and '), guess, threshold)) {
+      }
+      if (/jimi hendrix experience/.test(nothe) && 
+        checkDistance(nothe.replace(/ experience/, ''), guess, threshold)) {
         return true;
+      }
     }
-
-    if (enableartistrules) {
-        // Ignore "the" at the beginning of artist name
-        if (/^the /.test(subject)) {
-            var nothe = subject.replace(/^the /, '');
-            if (checkDistance(nothe, guess, threshold)) {
-                return true;
-            }
-            if (/jimi hendrix experience/.test(nothe) && 
-                checkDistance(nothe.replace(/ experience/, ''), guess, threshold)) {
-                return true;
-            }
+    // Split artist name on " & " and ", " (artist name can be composed by more names)
+    var splitted = subject.split(/ & |, /);
+    if (splitted.length !== 1) {
+      for (var i=0; i<splitted.length; i++) {
+        if (checkDistance(splitted[i], guess, threshold)) {
+          return true;
         }
-        // Split artist name on " & " and ", " (artist name can be composed by more names)
-        var splitted = subject.split(/ & |, /);
-        if (splitted.length !== 1) {
-            for (var i=0; i<splitted.length; i++) {
-                if (checkDistance(splitted[i], guess, threshold)) {
-                    return true;
-                }
-                if (/^the /.test(splitted[i]) && 
-                    checkDistance(splitted[i].replace(/^the /, ''), guess, threshold)) {
-                    return true;
-                }
-            }
+        if (/^the /.test(splitted[i]) && 
+          checkDistance(splitted[i].replace(/^the /, ''), guess, threshold)) {
+          return true;
         }
+      }
     }
-    else {
-        // Ignore commas
-        if (/,/.test(subject) && 
-            checkDistance(subject.replace(/,/g, ''), guess, threshold)) {
-            return true;
-        }
-        // Ignore additional info e.g. "(Love Theme from Titanic)"
-        if (/\(.+\)\??(?: \[.+\])?/.test(subject)) {
-            var normalized = subject.replace(/\(.+\)\??(?: \[.+\])?/, '').trim();
-            if (checkDistance(normalized, guess, threshold)) {
-                return true;
-            }
-            if (/ & /.test(normalized) && 
-                checkDistance(normalized.replace(/ & /, ' and '), guess, threshold)) {
-                return true;
-            }
-        }
-        if (/, [pP]t\. [0-9]$/.test(subject) && 
-            checkDistance(subject.replace(/, [pP]t\. [0-9]$/, ''), guess, threshold)) {
-            return true;
-        }
+  }
+  else {
+    // Ignore commas
+    if (/,/.test(subject) && 
+      checkDistance(subject.replace(/,/g, ''), guess, threshold)) {
+      return true;
+    }
+    // Ignore additional info e.g. "(Love Theme from Titanic)"
+    if (/\(.+\)\??(?: \[.+\])?/.test(subject)) {
+      var normalized = subject.replace(/\(.+\)\??(?: \[.+\])?/, '').trim();
+      if (checkDistance(normalized, guess, threshold)) {
+        return true;
+      }
+      if (/ & /.test(normalized) && 
+        checkDistance(normalized.replace(/ & /, ' and '), guess, threshold)) {
+        return true;
+      }
+    }
+    if (/, [pP]t\. [0-9]$/.test(subject) && 
+      checkDistance(subject.replace(/, [pP]t\. [0-9]$/, ''), guess, threshold)) {
+      return true;
     }
+  }
 
-    return false;
+  return false;
 };
index 15fa78f85a1e8265a27015a36bf8b9092573e731..8f4838e7484ea111ed2d95dd0897a40ead8ebc06 100644 (file)
@@ -9,19 +9,19 @@ var redis = require('redis');
  */
 
 var songsclient = redis.createClient(process.env.SONGS_DB_PORT, process.env.SONGS_DB_HOST)
-    , usersclient = redis.createClient(process.env.USERS_DB_PORT, process.env.USERS_DB_HOST);
+  , usersclient = redis.createClient(process.env.USERS_DB_PORT, process.env.USERS_DB_HOST);
 
 if (process.env.NODE_ENV === 'production') {
-    songsclient.auth(process.env.SONGS_DB_AUTH);
-    usersclient.auth(process.env.USERS_DB_AUTH);
+  songsclient.auth(process.env.SONGS_DB_AUTH);
+  usersclient.auth(process.env.USERS_DB_AUTH);
 }
 
 songsclient.on('error', function(err) {
-    console.log(err.message);
+  console.log(err.message);
 });
 
 usersclient.on('error', function(err) {
-    console.log(err.message);
+  console.log(err.message);
 });
 
 /**
index 7b4c0ea69f21e522efaf11d558a1f55c4f6fafa0..9ea7d88e1d8ccce7fc5b9053aa61bc95ab6111c0 100644 (file)
@@ -3,24 +3,24 @@
  */
 
 var amatch = require('./match')
-    , clients = require('./redis-clients')
-    , collectStats = require('./stats')
-    , config = require('../config')
-    , fifolength = config.songsinarun * config.gameswithnorepeats
-    , io
-    , isUsername = require('./utils').isUsername
-    , sockets
-    , songsdb = clients.songs
-    , usersdb = clients.users;
+  , clients = require('./redis-clients')
+  , collectStats = require('./stats')
+  , config = require('../config')
+  , fifolength = config.songsinarun * config.gameswithnorepeats
+  , io
+  , isUsername = require('./utils').isUsername
+  , sockets
+  , songsdb = clients.songs
+  , usersdb = clients.users;
 
 /**
  * Expose the constructor.
  */
 
 module.exports = function(options) {
-    io = options.io;
-    sockets = options.sockets;
-    return Room;
+  io = options.io;
+  sockets = options.sockets;
+  return Room;
 };
 
 /**
@@ -29,440 +29,440 @@ module.exports = function(options) {
 
 function Room(roomname) {
 
-    var allowedguess = false
-        , artist // Artists in lowercase
-        , artistName
-        , artworkUrl
-        , feat // Featured artists
-        , finishline = 1
-        , playedtracks = [] // The list of already played songs
-        , previewUrl
-        , songcounter = 0
-        , songtimeleft // Milliseconds
-        , status
-        , title // Title in lowercase
-        , trackName
-        , trackscount = 0
-        , trackViewUrl
-        , totusers = 0
-        , usersData = Object.create(null);
-
-    // User points and statistics
-    var addPointsAndStats = function(nickname, allinone) {
-        usersData[nickname].guesstime = 30000 - songtimeleft;
-        var stats = {};
-        switch (finishline) {
-            case 1:
-                finishline++;
-                usersData[nickname].roundpoints = 6;
-                if (allinone) {
-                    usersData[nickname].points += 6;
-                    stats.points = 6;
-                }
-                else {
-                    usersData[nickname].points += 5;
-                    stats.points = 5;
-                }
-                usersData[nickname].golds++;
-                stats.gold = true;
-                break;
-            case 2:
-                finishline++;
-                usersData[nickname].roundpoints = 5;
-                if (allinone) {
-                    usersData[nickname].points += 5;
-                    stats.points = 5;
-                }
-                else {
-                    usersData[nickname].points += 4;
-                    stats.points = 4;
-                }
-                usersData[nickname].silvers++;
-                stats.silver = true;
-                break;
-            case 3:
-                finishline++;
-                usersData[nickname].roundpoints = 4;
-                if (allinone) {
-                    usersData[nickname].points += 4;
-                    stats.points = 4;
-                }
-                else {
-                    usersData[nickname].points += 3;
-                    stats.points = 3;
-                }
-                usersData[nickname].bronzes++;
-                stats.bronze = true;
-                break;
-            default:
-                usersData[nickname].roundpoints = 3;
-                if (allinone) {
-                    usersData[nickname].points += 3;
-                    stats.points = 3;
-                }
-                else {
-                    usersData[nickname].points += 2;
-                    stats.points = 2;
-                }
+  var allowedguess = false
+    , artist // Artists in lowercase
+    , artistName
+    , artworkUrl
+    , feat // Featured artists
+    , finishline = 1
+    , playedtracks = [] // The list of already played songs
+    , previewUrl
+    , songcounter = 0
+    , songtimeleft // Milliseconds
+    , status
+    , title // Title in lowercase
+    , trackName
+    , trackscount = 0
+    , trackViewUrl
+    , totusers = 0
+    , usersData = Object.create(null);
+
+  // User points and statistics
+  var addPointsAndStats = function(nickname, allinone) {
+    usersData[nickname].guesstime = 30000 - songtimeleft;
+    var stats = {};
+    switch (finishline) {
+      case 1:
+        finishline++;
+        usersData[nickname].roundpoints = 6;
+        if (allinone) {
+          usersData[nickname].points += 6;
+          stats.points = 6;
         }
-        usersData[nickname].matched = 'both';
-        usersData[nickname].guessed++;
-        usersData[nickname].totguesstime += usersData[nickname].guesstime;
-
-        if (usersData[nickname].registered) {
-            stats.userscore = usersData[nickname].points;
-            stats.guesstime = usersData[nickname].guesstime;
-            collectStats(nickname, stats);
-        }
-    };
-
-    // Add a new user in the room
-    var addUser = function(socket, loggedin) {
-        sockets[socket.nickname] = socket;
-        usersData[socket.nickname] = {
-            nickname: socket.nickname,
-            registered: loggedin,
-            points: 0,
-            roundpoints: 0,
-            matched: null,
-            guessed: 0,
-            guesstime: null,
-            totguesstime: 0,
-            golds: 0,
-            silvers: 0,
-            bronzes: 0
-        };
-        totusers++;
-        // Broadcast new user event
-        io.sockets.emit('updateoverview', roomname, totusers);
-        socket.emit('ready', usersData, trackscount, loggedin);
-        socket.broadcast.to(roomname).emit('newuser', socket.nickname, usersData);
-    };
-
-    var gameOver = function() {
-        status = 3; // Game over
-
-        // Build podium
-        var users = [];
-        for (var key in usersData) {
-            users.push(usersData[key]);
-        }
-        users.sort(function(a, b) {return b.points - a.points;});
-        var podium = users.slice(0,3);
-        io.sockets.in(roomname).emit('gameover', podium);
-
-        // Collect podium stats
-        if (podium[0] && podium[0].registered) {
-            collectStats(podium[0].nickname, {firstplace:true});
+        else {
+          usersData[nickname].points += 5;
+          stats.points = 5;
         }
-        if (podium[1] && podium[1].registered) {
-            collectStats(podium[1].nickname, {secondplace:true});
+        usersData[nickname].golds++;
+        stats.gold = true;
+        break;
+      case 2:
+        finishline++;
+        usersData[nickname].roundpoints = 5;
+        if (allinone) {
+          usersData[nickname].points += 5;
+          stats.points = 5;
         }
-        if (podium[2] && podium[2].registered) {
-            collectStats(podium[2].nickname, {thirdplace:true});
+        else {
+          usersData[nickname].points += 4;
+          stats.points = 4;
         }
-
-        resetPoints(false);
-        songcounter = 0;
-        // Check if FIFO is full
-        if (playedtracks.length === fifolength) {
-            playedtracks.splice(0, config.songsinarun);
-        }
-
-        // Start a new game
-        setTimeout(sendLoadTrack, 5000);
-    };
-
-    // Return the number of users in the room
-    this.getPopulation = function() {
-        return totusers;
-    };
-
-    // A user is sending a guess
-    this.guess = function(socket, guess) {
-        if (allowedguess) {
-            if (!usersData[socket.nickname].matched) { // No track no artist
-                if ((artist === title) && amatch(title, guess, true)) {
-                    addPointsAndStats(socket.nickname, true);
-                    socket.emit('bothmatched');
-                    io.sockets.in(roomname).emit('updateusers', usersData);
-                }
-                else if (amatch(artist, guess, true) || (feat && amatch(feat, guess, true))) {
-                    usersData[socket.nickname].roundpoints++;
-                    usersData[socket.nickname].points++;
-                    usersData[socket.nickname].matched = 'artist';
-                    socket.emit('artistmatched');
-                    io.sockets.in(roomname).emit('updateusers', usersData);
-                    if (usersData[socket.nickname].registered) {
-                        var stats = {points:1,userscore:usersData[socket.nickname].points};
-                        collectStats(socket.nickname, stats);
-                    }
-                }
-                else if (amatch(title, guess)) {
-                    usersData[socket.nickname].roundpoints++;
-                    usersData[socket.nickname].points++;
-                    usersData[socket.nickname].matched = 'title';
-                    socket.emit('titlematched');
-                    io.sockets.in(roomname).emit('updateusers', usersData);
-                    if (usersData[socket.nickname].registered) {
-                        var stats = {points:1,userscore:usersData[socket.nickname].points};
-                        collectStats(socket.nickname, stats);
-                    }
-                }
-                else {
-                    socket.emit('nomatch');
-                }
-            }
-            else if (usersData[socket.nickname].matched !== 'both') { // Track or artist
-                if (usersData[socket.nickname].matched === 'artist') {
-                    if (amatch(title, guess)) {
-                        addPointsAndStats(socket.nickname, false);
-                        socket.emit('bothmatched');
-                        io.sockets.in(roomname).emit('updateusers', usersData);
-                    }
-                    else {
-                        socket.emit('nomatch');
-                    }
-                }
-                else {
-                    if (amatch(artist, guess, true) || (feat && amatch(feat, guess, true))) {
-                        addPointsAndStats(socket.nickname, false);
-                        socket.emit('bothmatched');
-                        io.sockets.in(roomname).emit('updateusers', usersData);
-                    }
-                    else {
-                        socket.emit('nomatch');
-                    }
-                }
-            }
-            else { // The user has guessed both track and artist
-                socket.emit('stoptrying');
-            }
+        usersData[nickname].silvers++;
+        stats.silver = true;
+        break;
+      case 3:
+        finishline++;
+        usersData[nickname].roundpoints = 4;
+        if (allinone) {
+          usersData[nickname].points += 4;
+          stats.points = 4;
         }
         else {
-            socket.emit('noguesstime');
+          usersData[nickname].points += 3;
+          stats.points = 3;
         }
-    };
-
-    this.ignore = function(who, executor, callback) {
-        // Check if the player to be ignored is in the room
-        if (usersData[who]) {
-            // Inform the bad player that he/she is being ignored
-            var recipient = sockets[who];
-            recipient.emit('chatmsg', executor+' is ignoring you.', 'binb', who);
-            return callback(who);
+        usersData[nickname].bronzes++;
+        stats.bronze = true;
+        break;
+      default:
+        usersData[nickname].roundpoints = 3;
+        if (allinone) {
+          usersData[nickname].points += 3;
+          stats.points = 3;
         }
-        callback(false);
-    };
-
-    this.joinRoom = function(socket) {
-        socket.roomname = roomname;
-        socket.join(roomname);
-        addUser(socket, true);
-    };
-
-    // Kick a user
-    this.kick = function(who, why, executor, callback) {
-        usersdb.hget('user:'+executor, 'role', function (err, role) {
-            if (role > 0) { // Check role
-                if (usersData[who]) {
-                    if (why) {
-                        why = ' ('+why+')';
-                    }
-                    var notice = 'you have been kicked by '+executor+why+'.';
-                    var recipient = sockets[who];
-                    recipient.emit('chatmsg', notice, 'binb', who);
-                    recipient.disconnect();
-                }
-                return;
-            }
-            callback();
-        });
-    };
-
-    // A user has left (DCed, etc.)
-    this.removeUser = function(nickname) {
-        // Delete the references
-        delete sockets[nickname];
-        delete usersData[nickname];
-        totusers--;
-        // Broadcast the event
-        io.sockets.emit('updateoverview', roomname, totusers);
-        io.sockets.in(roomname).emit('userleft', nickname, usersData);
-    };
-
-    var resetPoints = function(roundonly) {
-        for (var key in usersData) {
-            if (!roundonly) {
-                usersData[key].points = 0;
-                usersData[key].guessed = 0;
-                usersData[key].totguesstime = 0;
-                usersData[key].golds = 0;
-                usersData[key].silvers = 0;
-                usersData[key].bronzes = 0;
-            }
-            usersData[key].roundpoints = 0;
-            usersData[key].matched = null;
-            usersData[key].guesstime = null;
+        else {
+          usersData[nickname].points += 2;
+          stats.points = 2;
         }
+    }
+    usersData[nickname].matched = 'both';
+    usersData[nickname].guessed++;
+    usersData[nickname].totguesstime += usersData[nickname].guesstime;
+
+    if (usersData[nickname].registered) {
+      stats.userscore = usersData[nickname].points;
+      stats.guesstime = usersData[nickname].guesstime;
+      collectStats(nickname, stats);
+    }
+  };
+
+  // Add a new user in the room
+  var addUser = function(socket, loggedin) {
+    sockets[socket.nickname] = socket;
+    usersData[socket.nickname] = {
+      nickname: socket.nickname,
+      registered: loggedin,
+      points: 0,
+      roundpoints: 0,
+      matched: null,
+      guessed: 0,
+      guesstime: null,
+      totguesstime: 0,
+      golds: 0,
+      silvers: 0,
+      bronzes: 0
     };
-
-    // A user is sending a chat message
-    this.sendChatMessage = function(msg, socket, to) {
-        if (typeof to === 'string') {
-            // Check if the recipient is in the room
-            if (usersData[to]) {
-                socket.emit('chatmsg', msg, socket.nickname, to);
-                var recipient = sockets[to];
-                recipient.emit('chatmsg', msg, socket.nickname, to);
-            }
-            return;
+    totusers++;
+    // Broadcast new user event
+    io.sockets.emit('updateoverview', roomname, totusers);
+    socket.emit('ready', usersData, trackscount, loggedin);
+    socket.broadcast.to(roomname).emit('newuser', socket.nickname, usersData);
+  };
+
+  var gameOver = function() {
+    status = 3; // Game over
+
+    // Build podium
+    var users = [];
+    for (var key in usersData) {
+      users.push(usersData[key]);
+    }
+    users.sort(function(a, b) {return b.points - a.points;});
+    var podium = users.slice(0,3);
+    io.sockets.in(roomname).emit('gameover', podium);
+
+    // Collect podium stats
+    if (podium[0] && podium[0].registered) {
+      collectStats(podium[0].nickname, {firstplace:true});
+    }
+    if (podium[1] && podium[1].registered) {
+      collectStats(podium[1].nickname, {secondplace:true});
+    }
+    if (podium[2] && podium[2].registered) {
+      collectStats(podium[2].nickname, {thirdplace:true});
+    }
+
+    resetPoints(false);
+    songcounter = 0;
+    // Check if FIFO is full
+    if (playedtracks.length === fifolength) {
+      playedtracks.splice(0, config.songsinarun);
+    }
+
+    // Start a new game
+    setTimeout(sendLoadTrack, 5000);
+  };
+
+  // Return the number of users in the room
+  this.getPopulation = function() {
+    return totusers;
+  };
+
+  // A user is sending a guess
+  this.guess = function(socket, guess) {
+    if (allowedguess) {
+      if (!usersData[socket.nickname].matched) { // No track no artist
+        if ((artist === title) && amatch(title, guess, true)) {
+          addPointsAndStats(socket.nickname, true);
+          socket.emit('bothmatched');
+          io.sockets.in(roomname).emit('updateusers', usersData);
         }
-        // Censor answers from chat
-        var msglcase = msg.toLowerCase();
-        if (allowedguess && (amatch(artist, msglcase, true) ||
-                (feat && amatch(feat, msglcase, true)) || amatch(title, msglcase))) {
-            var notice = 'You are probably right, but you have to use the box above.';
-            socket.emit('chatmsg', notice, 'binb', socket.nickname);
-            return;
+        else if (amatch(artist, guess, true) || (feat && amatch(feat, guess, true))) {
+          usersData[socket.nickname].roundpoints++;
+          usersData[socket.nickname].points++;
+          usersData[socket.nickname].matched = 'artist';
+          socket.emit('artistmatched');
+          io.sockets.in(roomname).emit('updateusers', usersData);
+          if (usersData[socket.nickname].registered) {
+            var stats = {points:1,userscore:usersData[socket.nickname].points};
+            collectStats(socket.nickname, stats);
+          }
         }
-        io.sockets.in(roomname).emit('chatmsg', msg, socket.nickname);
-    };
-
-    // Extract a random track from the database and send the load event
-    var sendLoadTrack = function() {
-        songsdb.srandmember(roomname, function(err, res) {
-            // Check if extracted track is in the list of already played tracks
-            if (~playedtracks.indexOf(res)) {
-                return sendLoadTrack();
-            }
-            playedtracks.push(res);
-            var args = [
-                'song:'+res
-                , 'artistName'
-                , 'trackName'
-                , 'previewUrl'
-                , 'artworkUrl60'
-                , 'trackViewUrl'
-            ];
-            songsdb.hmget(args, function(e, replies) {
-                artistName = replies[0];
-                artist = artistName.toLowerCase();
-                trackName = replies[1];
-                title = trackName.toLowerCase();
-                feat = /feat\. (.+?)[)\]]/.test(title) ? RegExp.$1 : null;
-                previewUrl = replies[2];
-                artworkUrl = replies[3];
-                trackViewUrl = replies[4];
-                io.sockets.in(roomname).emit('loadtrack', previewUrl);
-                setTimeout(sendPlayTrack, 5000);
-            });
-        });
-        status = 1; // Loading next song
-    };
-
-    var sendPlayTrack = function() {
-        songcounter++;
-        status = 0; // Playing track
-        var data = {
-            counter: songcounter,
-            tot: config.songsinarun,
-            users: usersData
-        };
-        io.sockets.in(roomname).emit('playtrack', data);
-        songTimeLeft(Date.now() + 30000, 50);
-        allowedguess = true;
-        setTimeout(sendTrackInfo, 30000);
-    };
-
-    // Send the room status
-    this.sendStatus = function(callback) {
-        var data = {
-            status: status,
-            timeleft: songtimeleft,
-            previewUrl: previewUrl
-        };
-        callback(data);
-    };
-
-    var sendTrackInfo = function() {
-        var trackinfo = {
-            artworkUrl: artworkUrl,
-            artistName: artistName,
-            trackName: trackName,
-            trackViewUrl: trackViewUrl,
-        };
-        io.sockets.in(roomname).emit('trackinfo', trackinfo);
-        finishline = 1;
-        allowedguess = false;
-
-        if (songcounter < config.songsinarun) {
-            resetPoints(true);
-            sendLoadTrack();
-            return;
+        else if (amatch(title, guess)) {
+          usersData[socket.nickname].roundpoints++;
+          usersData[socket.nickname].points++;
+          usersData[socket.nickname].matched = 'title';
+          socket.emit('titlematched');
+          io.sockets.in(roomname).emit('updateusers', usersData);
+          if (usersData[socket.nickname].registered) {
+            var stats = {points:1,userscore:usersData[socket.nickname].points};
+            collectStats(socket.nickname, stats);
+          }
         }
-
-        status = 2; // Sending last track info
-        setTimeout(gameOver, 5000);
-    };
-
-    // A user is submitting a name
-    this.setNickName = function(socket, nickname) {
-        var feedback = null;
-
-        if (nickname === 'binb') {
-            feedback = '<span class="label label-important">That name is reserved.</span>';
-        }
-        else if (!isUsername(nickname)) {
-            feedback = '<span class="label label-important">Name must contain only ';
-            feedback += 'alphanumeric characters.</span>';
+        else {
+          socket.emit('nomatch');
         }
-        else if (sockets[nickname]) {
-            feedback = '<span class="label label-important">Name already taken.</span>';
+      }
+      else if (usersData[socket.nickname].matched !== 'both') { // Track or artist
+        if (usersData[socket.nickname].matched === 'artist') {
+          if (amatch(title, guess)) {
+            addPointsAndStats(socket.nickname, false);
+            socket.emit('bothmatched');
+            io.sockets.in(roomname).emit('updateusers', usersData);
+          }
+          else {
+            socket.emit('nomatch');
+          }
         }
-
-        if (feedback) {
-            return socket.emit('invalidnickname', feedback);
+        else {
+          if (amatch(artist, guess, true) || (feat && amatch(feat, guess, true))) {
+            addPointsAndStats(socket.nickname, false);
+            socket.emit('bothmatched');
+            io.sockets.in(roomname).emit('updateusers', usersData);
+          }
+          else {
+            socket.emit('nomatch');
+          }
         }
-
-        // Check if requested nickname belong to a registered user
-        var key = 'user:'+nickname;
-        usersdb.exists(key, function(err, resp) {
-            if (resp === 1) {
-                feedback = '<span class="label label-important">That name belongs ';
-                feedback += 'to a registered user.</span>';
-                return socket.emit('invalidnickname', feedback);
-            }
-            socket.nickname = nickname;
-            socket.roomname = roomname;
-            socket.join(roomname);
-            addUser(socket, false);
-        });
-    };
-
-    // Timer for the playing song
-    var songTimeLeft = function(end, delay) {
-        songtimeleft = end - Date.now();
-        if (songtimeleft < delay) {
-            return;
+      }
+      else { // The user has guessed both track and artist
+        socket.emit('stoptrying');
+      }
+    }
+    else {
+      socket.emit('noguesstime');
+    }
+  };
+
+  this.ignore = function(who, executor, callback) {
+    // Check if the player to be ignored is in the room
+    if (usersData[who]) {
+      // Inform the bad player that he/she is being ignored
+      var recipient = sockets[who];
+      recipient.emit('chatmsg', executor+' is ignoring you.', 'binb', who);
+      return callback(who);
+    }
+    callback(false);
+  };
+
+  this.joinRoom = function(socket) {
+    socket.roomname = roomname;
+    socket.join(roomname);
+    addUser(socket, true);
+  };
+
+  // Kick a user
+  this.kick = function(who, why, executor, callback) {
+    usersdb.hget('user:'+executor, 'role', function (err, role) {
+      if (role > 0) { // Check role
+        if (usersData[who]) {
+          if (why) {
+            why = ' ('+why+')';
+          }
+          var notice = 'you have been kicked by '+executor+why+'.';
+          var recipient = sockets[who];
+          recipient.emit('chatmsg', notice, 'binb', who);
+          recipient.disconnect();
         }
-        setTimeout(songTimeLeft, delay, end, delay);
+        return;
+      }
+      callback();
+    });
+  };
+
+  // A user has left (DCed, etc.)
+  this.removeUser = function(nickname) {
+    // Delete the references
+    delete sockets[nickname];
+    delete usersData[nickname];
+    totusers--;
+    // Broadcast the event
+    io.sockets.emit('updateoverview', roomname, totusers);
+    io.sockets.in(roomname).emit('userleft', nickname, usersData);
+  };
+
+  var resetPoints = function(roundonly) {
+    for (var key in usersData) {
+      if (!roundonly) {
+        usersData[key].points = 0;
+        usersData[key].guessed = 0;
+        usersData[key].totguesstime = 0;
+        usersData[key].golds = 0;
+        usersData[key].silvers = 0;
+        usersData[key].bronzes = 0;
+      }
+      usersData[key].roundpoints = 0;
+      usersData[key].matched = null;
+      usersData[key].guesstime = null;
+    }
+  };
+
+  // A user is sending a chat message
+  this.sendChatMessage = function(msg, socket, to) {
+    if (typeof to === 'string') {
+      // Check if the recipient is in the room
+      if (usersData[to]) {
+        socket.emit('chatmsg', msg, socket.nickname, to);
+        var recipient = sockets[to];
+        recipient.emit('chatmsg', msg, socket.nickname, to);
+      }
+      return;
+    }
+    // Censor answers from chat
+    var msglcase = msg.toLowerCase();
+    if (allowedguess && (amatch(artist, msglcase, true) ||
+        (feat && amatch(feat, msglcase, true)) || amatch(title, msglcase))) {
+      var notice = 'You are probably right, but you have to use the box above.';
+      socket.emit('chatmsg', notice, 'binb', socket.nickname);
+      return;
+    }
+    io.sockets.in(roomname).emit('chatmsg', msg, socket.nickname);
+  };
+
+  // Extract a random track from the database and send the load event
+  var sendLoadTrack = function() {
+    songsdb.srandmember(roomname, function(err, res) {
+      // Check if extracted track is in the list of already played tracks
+      if (~playedtracks.indexOf(res)) {
+        return sendLoadTrack();
+      }
+      playedtracks.push(res);
+      var args = [
+        'song:'+res
+        , 'artistName'
+        , 'trackName'
+        , 'previewUrl'
+        , 'artworkUrl60'
+        , 'trackViewUrl'
+      ];
+      songsdb.hmget(args, function(e, replies) {
+        artistName = replies[0];
+        artist = artistName.toLowerCase();
+        trackName = replies[1];
+        title = trackName.toLowerCase();
+        feat = /feat\. (.+?)[)\]]/.test(title) ? RegExp.$1 : null;
+        previewUrl = replies[2];
+        artworkUrl = replies[3];
+        trackViewUrl = replies[4];
+        io.sockets.in(roomname).emit('loadtrack', previewUrl);
+        setTimeout(sendPlayTrack, 5000);
+      });
+    });
+    status = 1; // Loading next song
+  };
+
+  var sendPlayTrack = function() {
+    songcounter++;
+    status = 0; // Playing track
+    var data = {
+      counter: songcounter,
+      tot: config.songsinarun,
+      users: usersData
     };
-
-    // Start the room
-    this.start = function() {
-        songsdb.scard(roomname, function(err, res) {
-            trackscount = res;
-        });
-        sendLoadTrack();
+    io.sockets.in(roomname).emit('playtrack', data);
+    songTimeLeft(Date.now() + 30000, 50);
+    allowedguess = true;
+    setTimeout(sendTrackInfo, 30000);
+  };
+
+  // Send the room status
+  this.sendStatus = function(callback) {
+    var data = {
+      status: status,
+      timeleft: songtimeleft,
+      previewUrl: previewUrl
     };
-
-    this.unignore = function(who, executor) {
-        if (usersData[who]) {
-            // Inform the bad player that he/she is no longer ignored
-            var notice = executor+' has stopped ignoring you.';
-            var recipient = sockets[who];
-            recipient.emit('chatmsg', notice, 'binb', who);
-        }
+    callback(data);
+  };
+
+  var sendTrackInfo = function() {
+    var trackinfo = {
+      artworkUrl: artworkUrl,
+      artistName: artistName,
+      trackName: trackName,
+      trackViewUrl: trackViewUrl,
     };
+    io.sockets.in(roomname).emit('trackinfo', trackinfo);
+    finishline = 1;
+    allowedguess = false;
+
+    if (songcounter < config.songsinarun) {
+      resetPoints(true);
+      sendLoadTrack();
+      return;
+    }
+
+    status = 2; // Sending last track info
+    setTimeout(gameOver, 5000);
+  };
+
+  // A user is submitting a name
+  this.setNickName = function(socket, nickname) {
+    var feedback = null;
+
+    if (nickname === 'binb') {
+      feedback = '<span class="label label-important">That name is reserved.</span>';
+    }
+    else if (!isUsername(nickname)) {
+      feedback = '<span class="label label-important">Name must contain only ';
+      feedback += 'alphanumeric characters.</span>';
+    }
+    else if (sockets[nickname]) {
+      feedback = '<span class="label label-important">Name already taken.</span>';
+    }
+
+    if (feedback) {
+      return socket.emit('invalidnickname', feedback);
+    }
+
+    // Check if requested nickname belong to a registered user
+    var key = 'user:'+nickname;
+    usersdb.exists(key, function(err, resp) {
+      if (resp === 1) {
+        feedback = '<span class="label label-important">That name belongs ';
+        feedback += 'to a registered user.</span>';
+        return socket.emit('invalidnickname', feedback);
+      }
+      socket.nickname = nickname;
+      socket.roomname = roomname;
+      socket.join(roomname);
+      addUser(socket, false);
+    });
+  };
+
+  // Timer for the playing song
+  var songTimeLeft = function(end, delay) {
+    songtimeleft = end - Date.now();
+    if (songtimeleft < delay) {
+      return;
+    }
+    setTimeout(songTimeLeft, delay, end, delay);
+  };
+
+  // Start the room
+  this.start = function() {
+    songsdb.scard(roomname, function(err, res) {
+      trackscount = res;
+    });
+    sendLoadTrack();
+  };
+
+  this.unignore = function(who, executor) {
+    if (usersData[who]) {
+      // Inform the bad player that he/she is no longer ignored
+      var notice = executor+' has stopped ignoring you.';
+      var recipient = sockets[who];
+      recipient.emit('chatmsg', notice, 'binb', who);
+    }
+  };
 }
index b4c4ee85784bd7f3c32a9f89be5602c9d4f38afd..7e309396bf5e31d983faa87dc8500af30cae2758 100644 (file)
@@ -9,57 +9,57 @@ var db = require('./redis-clients').users;
  */
 
 module.exports = function(username, stats) {
-    var key = 'user:'+username;
-    if (stats.points) {
-        // Update total points
-        db.hincrby(key, 'totpoints', stats.points);
-        // Update the score of the member in the sorted set
-        db.zincrby('users', stats.points, username);
-    }
-    if (stats.userscore) {
-        // Set personal best
-        db.hget(key, 'bestscore', function(err, res) {
-            if (res < stats.userscore) {
-                db.hset(key, 'bestscore', stats.userscore);
-            }
-        });
-    }
-    if (stats.gold) {
-        // Update the number of golds
-        db.hincrby(key, 'golds', 1);
-    }
-    if (stats.silver) {
-        db.hincrby(key, 'silvers', 1);
-    }
-    if (stats.bronze) {
-        db.hincrby(key, 'bronzes', 1);
-    }
-    if (stats.guesstime) {
-        // Update the number of guessed tracks
-        db.hincrby(key, 'guessed', 1);
-        // Update total guess time
-        db.hincrby(key, 'totguesstime', stats.guesstime);
-        // Set best answer time
-        db.hget(key, 'bestguesstime', function(err, res) {
-            if (stats.guesstime < res) {
-                db.hset(key, 'bestguesstime', stats.guesstime);
-            }
-        });
-        // Set worst answer time
-        db.hget(key, 'worstguesstime', function(err, res) {
-            if (stats.guesstime > res) {
-                db.hset(key, 'worstguesstime', stats.guesstime);
-            }
-        });
-    }
-    if (stats.firstplace) {
-        // Update the number of first places
-        db.hincrby(key, 'victories', 1);
-    }
-    if (stats.secondplace) {
-        db.hincrby(key, 'secondplaces', 1);
-    }
-    if (stats.thirdplace) {
-        db.hincrby(key, 'thirdplaces', 1);
-    }
+  var key = 'user:'+username;
+  if (stats.points) {
+    // Update total points
+    db.hincrby(key, 'totpoints', stats.points);
+    // Update the score of the member in the sorted set
+    db.zincrby('users', stats.points, username);
+  }
+  if (stats.userscore) {
+    // Set personal best
+    db.hget(key, 'bestscore', function(err, res) {
+      if (res < stats.userscore) {
+        db.hset(key, 'bestscore', stats.userscore);
+      }
+    });
+  }
+  if (stats.gold) {
+    // Update the number of golds
+    db.hincrby(key, 'golds', 1);
+  }
+  if (stats.silver) {
+    db.hincrby(key, 'silvers', 1);
+  }
+  if (stats.bronze) {
+    db.hincrby(key, 'bronzes', 1);
+  }
+  if (stats.guesstime) {
+    // Update the number of guessed tracks
+    db.hincrby(key, 'guessed', 1);
+    // Update total guess time
+    db.hincrby(key, 'totguesstime', stats.guesstime);
+    // Set best answer time
+    db.hget(key, 'bestguesstime', function(err, res) {
+      if (stats.guesstime < res) {
+        db.hset(key, 'bestguesstime', stats.guesstime);
+      }
+    });
+    // Set worst answer time
+    db.hget(key, 'worstguesstime', function(err, res) {
+      if (stats.guesstime > res) {
+        db.hset(key, 'worstguesstime', stats.guesstime);
+      }
+    });
+  }
+  if (stats.firstplace) {
+    // Update the number of first places
+    db.hincrby(key, 'victories', 1);
+  }
+  if (stats.secondplace) {
+    db.hincrby(key, 'secondplaces', 1);
+  }
+  if (stats.thirdplace) {
+    db.hincrby(key, 'thirdplaces', 1);
+  }
 };
index 675c76e6ae93213cb70a5aa17b5fdb923c74bd0f..0f792182ea7a3be04e91e0e1f248f422a13a1fe0 100644 (file)
@@ -3,21 +3,21 @@
  */
 
 module.exports = function(username, email, salt, hash, joindate) {
-    this.username = username;
-    this.email = email;
-    this.salt = salt;
-    this.password = hash;
-    this.joindate = joindate;
-    this.totpoints = 0;
-    this.bestscore = 0;
-    this.golds = 0;
-    this.silvers = 0;
-    this.bronzes = 0;
-    this.bestguesstime = 30000;
-    this.worstguesstime = 0;
-    this.totguesstime = 0;
-    this.guessed = 0;
-    this.victories = 0;
-    this.secondplaces = 0;
-    this.thirdplaces = 0;
+  this.username = username;
+  this.email = email;
+  this.salt = salt;
+  this.password = hash;
+  this.joindate = joindate;
+  this.totpoints = 0;
+  this.bestscore = 0;
+  this.golds = 0;
+  this.silvers = 0;
+  this.bronzes = 0;
+  this.bestguesstime = 30000;
+  this.worstguesstime = 0;
+  this.totguesstime = 0;
+  this.guessed = 0;
+  this.victories = 0;
+  this.secondplaces = 0;
+  this.thirdplaces = 0;
 };
index d5b81cab11a6bcc38f4ac66db192161a0ddfceab..2c74f35b0a54570486e3329b602d19f32614120e 100644 (file)
@@ -4,21 +4,21 @@
  */
 
 exports.buildLeaderboards = function(pointsresults, timesresults) {
-    var obj = {
-        pointsleaderboard: [],
-        timesleaderboard: []
-    };
-    for (var i=0; i<pointsresults.length; i+=2) {
-        obj.pointsleaderboard.push({
-            username: pointsresults[i],
-            totpoints: pointsresults[i+1]
-        });
-        obj.timesleaderboard.push({
-            username: timesresults[i],
-            bestguesstime: (timesresults[i+1] / 1000).toFixed(2)
-        });
-    }
-    return obj;
+  var obj = {
+    pointsleaderboard: [],
+    timesleaderboard: []
+  };
+  for (var i=0; i<pointsresults.length; i+=2) {
+    obj.pointsleaderboard.push({
+      username: pointsresults[i],
+      totpoints: pointsresults[i+1]
+    });
+    obj.timesleaderboard.push({
+      username: timesresults[i],
+      bestguesstime: (timesresults[i+1] / 1000).toFixed(2)
+    });
+  }
+  return obj;
 };
 
 /**
@@ -26,9 +26,9 @@ exports.buildLeaderboards = function(pointsresults, timesresults) {
  */
 
 exports.isEmail = function(str) {
-    // Simple filter, but it covers most of the use cases.
-    var filter = /^[+a-zA-Z0-9_.\-]+@([a-zA-Z0-9\-]+\.)+[a-zA-Z0-9]{2,6}$/;
-    return filter.test(str);
+  // Simple filter, but it covers most of the use cases.
+  var filter = /^[+a-zA-Z0-9_.\-]+@([a-zA-Z0-9\-]+\.)+[a-zA-Z0-9]{2,6}$/;
+  return filter.test(str);
 };
 
 /**
@@ -36,8 +36,8 @@ exports.isEmail = function(str) {
  */
 
 exports.isUsername = function(str) {
-    var filter = /^[a-zA-Z0-9\-_]{1,15}$/;
-    return filter.test(str);
+  var filter = /^[a-zA-Z0-9\-_]{1,15}$/;
+  return filter.test(str);
 };
 
 /**
@@ -45,12 +45,12 @@ exports.isUsername = function(str) {
  */
 
 exports.randomSlogan = function() {
-       var slogans = [
-        'guess the song.'
-        , 'name that tune.'
-        , 'i know this track.'
-    ];
-       return slogans[Math.floor(Math.random() * slogans.length)];
+  var slogans = [
+    'guess the song.'
+    , 'name that tune.'
+    , 'i know this track.'
+  ];
+  return slogans[Math.floor(Math.random() * slogans.length)];
 };
 
 /**
@@ -58,17 +58,17 @@ exports.randomSlogan = function() {
  */
 
 exports.sortParams = function(offset) {
-    var params = [
-        'users'
-        , 'by'
-        , 'user:*->bestguesstime'
-        , 'get'
-        , '#'
-        , 'get'
-        , 'user:*->bestguesstime'
-        , 'limit'
-        , offset
-        , '30'
-    ];
-    return params;
+  var params = [
+    'users'
+    , 'by'
+    , 'user:*->bestguesstime'
+    , 'get'
+    , '#'
+    , 'get'
+    , 'user:*->bestguesstime'
+    , 'limit'
+    , offset
+    , '30'
+  ];
+  return params;
 };
index 1200e10852838e8378a4963c0270f17c5c2725a2..1453f58b9c942b46df6bb50c40c48548c0305e6f 100644 (file)
 @font-face {
-    font-family: 'ChristopherhandRegular';
-    src: url('/static/fonts/comesinhandy-webfont.eot');
-    src: url('/static/fonts/comesinhandy-webfont.eot?#iefix') format('embedded-opentype'),
-         url('/static/fonts/comesinhandy-webfont.woff') format('woff'),
-         url('/static/fonts/comesinhandy-webfont.ttf') format('truetype');
-    font-weight: normal;
-    font-style: normal;
+  font-family: 'ChristopherhandRegular';
+  src: url('/static/fonts/comesinhandy-webfont.eot');
+  src: url('/static/fonts/comesinhandy-webfont.eot?#iefix') format('embedded-opentype'),
+     url('/static/fonts/comesinhandy-webfont.woff') format('woff'),
+     url('/static/fonts/comesinhandy-webfont.ttf') format('truetype');
+  font-weight: normal;
+  font-style: normal;
 }
 body {
-    background: url('/static/img/bg.jpg') repeat-x scroll 0 0 #F5F6F7;
-    padding-top: 45px;
+  background: url('/static/img/bg.jpg') repeat-x scroll 0 0 #F5F6F7;
+  padding-top: 45px;
 }
 section {
-    margin-top: 30px;
+  margin-top: 30px;
 }
 h1, h2, h3, h4, h5, h6 {
-    margin: 0;
+  margin: 0;
 }
 h1 {
-    font-size: 30px;
-    line-height: 36px;
+  font-size: 30px;
+  line-height: 36px;
 }
 h2 {
-    font-size: 24px;
-    line-height: 36px;
+  font-size: 24px;
+  line-height: 36px;
 }
 h3 {
-    font-size: 18px;
-    line-height: 27px;
+  font-size: 18px;
+  line-height: 27px;
 }
 h4, h5, h6 {
-    line-height: 18px;
+  line-height: 18px;
 }
 h4 {
-    font-size: 14px;
+  font-size: 14px;
 }
 h5 {
-    font-size: 12px;
+  font-size: 12px;
 }
 h6 {
-    font-size: 11px;
-    color: #999999;
-    text-transform: uppercase;
+  font-size: 11px;
+  color: #999999;
+  text-transform: uppercase;
 }
 .icons {
-    background: url('/static/img/sprites.png') no-repeat;
+  background: url('/static/img/sprites.png') no-repeat;
 }
 .align-left {
-    text-align: left;
+  text-align: left;
 }
 .navbar .brand {
-    padding: 2px 20px 4px;
+  padding: 2px 20px 4px;
 }
 .logo {
-    font-family: 'ChristopherhandRegular';
+  font-family: 'ChristopherhandRegular';
 }
 .navbar .brand .logo {
-    height: 34px;
-    padding-left: 68px;
-    line-height: 40px;
-    background-position: -36px -174px;
-    font-size: 25px;
+  height: 34px;
+  padding-left: 68px;
+  line-height: 40px;
+  background-position: -36px -174px;
+  font-size: 25px;
 }
 .navbar .nav > li > a {
-    padding: 11px 10px;
+  padding: 11px 10px;
 }
 .navbar .nav.pull-right {
-    margin-left: 10px;
-    margin-right: 0;
+  margin-left: 10px;
+  margin-right: 0;
 }
 .page-header .logo {
-    display: inline-block;
-    height: 54px;
-    padding-left: 109px;
-    line-height: 68px;
-    background-position: 0 -120px;
-    font-size: 34px;
-    color: #0088CC;
+  display: inline-block;
+  height: 54px;
+  padding-left: 109px;
+  line-height: 68px;
+  background-position: 0 -120px;
+  font-size: 34px;
+  color: #0088CC;
 }
 .form-horizontal .control-group {
-    margin-bottom: 10px;
+  margin-bottom: 10px;
 }
 .form-horizontal .control-label {
-    width: 100px;
+  width: 100px;
 }
 .form-horizontal .controls {
-    margin-left: 120px;
+  margin-left: 120px;
 }
 form .clearfix {
-    margin-bottom: 10px;
+  margin-bottom: 10px;
 }
 .well {
-    background-color: #DDDDDD;
-    margin-bottom: 18px;
+  background-color: #DDDDDD;
+  margin-bottom: 18px;
 }
 .alert {
-    margin-bottom: 9px;
+  margin-bottom: 9px;
 }
 .submit-button {
-    margin-left: 120px;
-    margin-top: 9px;
+  margin-left: 120px;
+  margin-top: 9px;
 }
 .forgot-passwd {
-    display: inline-block;
-    vertical-align: top;
-    margin: 14px 0 0 15px;
+  display: inline-block;
+  vertical-align: top;
+  margin: 14px 0 0 15px;
 }
 #captcha-input {
-    width: 122px;
+  width: 122px;
 }
 #captcha {
-    margin-right: 20px;
+  margin-right: 20px;
 }
 .page-header {
-    padding: 0;
-    margin: 0 0 17px 0;
-    border-bottom: 1px solid #ddd;
-    -webkit-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
-    -moz-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
-    box-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
+  padding: 0;
+  margin: 0 0 17px 0;
+  border-bottom: 1px solid #ddd;
+  -webkit-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
+  -moz-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
+  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
 }
 #login, #guess, #message {
-    margin-bottom: 0;
+  margin-bottom: 0;
 }
 #login {
-    float: left;
+  float: left;
 }
 #join {
-    margin-right: 45px;
+  margin-right: 45px;
 }
 .modal-footer .divider, .modal-footer .divider span {
-    position: absolute;
+  position: absolute;
 }
 .modal-footer .divider {
-    border-left: 1px solid #ddd;
-    height: 38px;
-    top: 9px;
-    left: 442px;
+  border-left: 1px solid #ddd;
+  height: 38px;
+  top: 9px;
+  left: 442px;
 }
 .modal-footer .divider span {
-    color: #aaa;
-    background-color: #F5F5F5;
-    top: 10px;
-    left: -6px;
+  color: #aaa;
+  background-color: #F5F5F5;
+  top: 10px;
+  left: -6px;
 }
 .users-counter, #total-tracks, #tracks a, #nodejitsu-logo, .footer-info {
-    float: right;
+  float: right;
 }
 .thumbnails {
-    margin-bottom: 0;
+  margin-bottom: 0;
 }
 .thumbnails > li {
-    margin: 0 0 18px 80px;
+  margin: 0 0 18px 80px;
 }
 .thumbnail {
-    border: 1px solid #ccc;
-    height: 140px;
-    opacity: 0.7;
+  border: 1px solid #ccc;
+  height: 140px;
+  opacity: 0.7;
 }
 .thumbnail:hover {
-    opacity: 1;
+  opacity: 1;
 }
 .thumbnail img {
-    float: left;
-    width: 70px;
-    height: 70px;
+  float: left;
+  width: 70px;
+  height: 70px;
 }
 .highscores, .profile {
-    font-weight: bold;
-    font-size: 22px;
-    line-height: 32px;
+  font-weight: bold;
+  font-size: 22px;
+  line-height: 32px;
 }
 .highscores .img, .profile .img, .medals {
-    width: 32px;
-    height: 32px;
+  width: 32px;
+  height: 32px;
 }
 .profile .img {
-    margin-right: 5px;
-    background-position: 0 -56px;
+  margin-right: 5px;
+  background-position: 0 -56px;
 }
 .highscores .img {
-    margin-right: 7px;
-    background-position: -64px 0;
+  margin-right: 7px;
+  background-position: -64px 0;
 }
 .leaderboard-wrapper, .stats {
-    border: 1px solid #ccc;
-    border-left: 0;
-    margin-top: 8px;
+  border: 1px solid #ccc;
+  border-left: 0;
+  margin-top: 8px;
 }
 .leaderboard-wrapper {
-    height: 349px;
-    overflow: auto;
-    margin-bottom: 18px;
-    -webkit-border-radius: 4px;
-    -moz-border-radius: 4px;
-    border-radius: 4px;
+  height: 349px;
+  overflow: auto;
+  margin-bottom: 18px;
+  -webkit-border-radius: 4px;
+  -moz-border-radius: 4px;
+  border-radius: 4px;
 }
 .leaderboard {
-    margin: 0;
-    border: 0;
+  margin: 0;
+  border: 0;
 }
 .leaderboard td, .stats td {
-    border-left: 1px solid #ccc;
-    border-top: 1px solid #ccc;
-    vertical-align: middle;
+  border-left: 1px solid #ccc;
+  border-top: 1px solid #ccc;
+  vertical-align: middle;
 }
 .leaderboard td [class^="icon-"] {
-    vertical-align: top;
-    margin-top: 2px;
+  vertical-align: top;
+  margin-top: 2px;
 }
 .leaderboard tr:first-child td {
-    border-top: 0;
+  border-top: 0;
 }
 .leaderboard tbody tr:nth-child(odd) td, .stats tbody tr:nth-child(odd) td {
-    background-color: #ddd;
+  background-color: #ddd;
 }
 .leaderboard tbody tr:hover td, .stats tbody tr:hover td {
-    background-color: #dadada;
+  background-color: #dadada;
 }
 .loading {
-    width: 18px;
-    position: absolute;
-    top: 360px;
-    left: 191px;
-    display: none;
+  width: 18px;
+  position: absolute;
+  top: 360px;
+  left: 191px;
+  display: none;
 }
 .loading-block {
-    background-color: #6184B7;
-    border: 1px solid #466085;
-    float: left;
-    height: 12px;
-    margin-left: 1px;
-    width: 3px;
-    opacity: 0.1;
-    -o-animation-name: bounce;
-    -o-animation-duration: 1s;
-    -o-animation-iteration-count: infinite;
-    -o-transform: scale(0.7);
-    -moz-animation-name: bounce;
-    -moz-animation-duration: 1s;
-    -moz-animation-iteration-count: infinite;
-    -moz-transform: scale(0.7);
-    -ms-transform: scale(0.7);
-    -webkit-animation-name: bounce;
-    -webkit-animation-duration: 1s;
-    -webkit-animation-iteration-count: infinite;
-    -webkit-transform: scale(0.7);
-    animation-name: bounce;
-    animation-duration: 1s;
-    animation-iteration-count: infinite;
-    transform: scale(0.7);
+  background-color: #6184B7;
+  border: 1px solid #466085;
+  float: left;
+  height: 12px;
+  margin-left: 1px;
+  width: 3px;
+  opacity: 0.1;
+  -o-animation-name: bounce;
+  -o-animation-duration: 1s;
+  -o-animation-iteration-count: infinite;
+  -o-transform: scale(0.7);
+  -moz-animation-name: bounce;
+  -moz-animation-duration: 1s;
+  -moz-animation-iteration-count: infinite;
+  -moz-transform: scale(0.7);
+  -ms-transform: scale(0.7);
+  -webkit-animation-name: bounce;
+  -webkit-animation-duration: 1s;
+  -webkit-animation-iteration-count: infinite;
+  -webkit-transform: scale(0.7);
+  animation-name: bounce;
+  animation-duration: 1s;
+  animation-iteration-count: infinite;
+  transform: scale(0.7);
 }
 .loading-block:first-child {
-    -o-animation-delay: 0.4s;
-    -moz-animation-delay: 0.4s;
-    -webkit-animation-delay: 0.4s;
-    animation-delay: 0.4s;
+  -o-animation-delay: 0.4s;
+  -moz-animation-delay: 0.4s;
+  -webkit-animation-delay: 0.4s;
+  animation-delay: 0.4s;
 }
 .loading-block:nth-child(2) {
-    -o-animation-delay: 0.5s;
-    -moz-animation-delay: 0.5s;
-    -webkit-animation-delay: 0.5s;
-    animation-delay: 0.5s;
+  -o-animation-delay: 0.5s;
+  -moz-animation-delay: 0.5s;
+  -webkit-animation-delay: 0.5s;
+  animation-delay: 0.5s;
 }
 .loading-block:last-child {
-    -o-animation-delay: 0.6s;
-    -moz-animation-delay: 0.6s;
-    -webkit-animation-delay: 0.6s;
-    animation-delay: 0.6s;
+  -o-animation-delay: 0.6s;
+  -moz-animation-delay: 0.6s;
+  -webkit-animation-delay: 0.6s;
+  animation-delay: 0.6s;
 }
 @-o-keyframes bounce {
-    0% {
-        -o-transform: scale(1);
-        opacity: 1;
-    }
-    100% {
-        -o-transform: scale(0.7);
-        opacity: 0.1; 
-    }
+  0% {
+    -o-transform: scale(1);
+    opacity: 1;
+  }
+  100% {
+    -o-transform: scale(0.7);
+    opacity: 0.1; 
+  }
 }
 @-moz-keyframes bounce {
-    0% {
-        -moz-transform: scale(1);
-        opacity: 1;
-    }
-    100% {
-        -moz-transform: scale(0.7);
-        opacity: 0.1;
-    }
+  0% {
+    -moz-transform: scale(1);
+    opacity: 1;
+  }
+  100% {
+    -moz-transform: scale(0.7);
+    opacity: 0.1;
+  }
 }
 @-webkit-keyframes bounce {
-    0% {
-        -webkit-transform: scale(1);
-        opacity: 1;
-    }
-    100% {
-        -webkit-transform: scale(0.7);
-        opacity: 0.1;
-    }
+  0% {
+    -webkit-transform: scale(1);
+    opacity: 1;
+  }
+  100% {
+    -webkit-transform: scale(0.7);
+    opacity: 0.1;
+  }
 }
 @keyframes bounce {
-    0% {
-        transform: scale(1);
-        opacity: 1;
-    }
-    100% {
-        transform:s cale(0.7);
-        opacity: 0.1;
-    }
+  0% {
+    transform: scale(1);
+    opacity: 1;
+  }
+  100% {
+    transform:s cale(0.7);
+    opacity: 0.1;
+  }
 }
 .room {
-    height: 25px;
-    line-height: 25px;
-    position: absolute;
-    top: 61px;
-    background-color: rgba(51,51,51, 0.7);
-    color: white;
-    font-weight: bold;
-    font-size: 14px;
-    width: 210px;
-    text-align: center;
-    text-transform: capitalize;
+  height: 25px;
+  line-height: 25px;
+  position: absolute;
+  top: 61px;
+  background-color: rgba(51,51,51, 0.7);
+  color: white;
+  font-weight: bold;
+  font-size: 14px;
+  width: 210px;
+  text-align: center;
+  text-transform: capitalize;
 }
 .dropdown-menu li > a {
-    padding: 2px 10px;
-    white-space: normal;
+  padding: 2px 10px;
+  white-space: normal;
 }
 .users-counter {
-    font-size: 12px;
-    margin-right: 4px;
+  font-size: 12px;
+  margin-right: 4px;
 }
 .matched {
-    color: #f3a22f;
+  color: #f3a22f;
 }
 .cups, .medals {
-    margin-left: auto;
-    margin-right: auto;
+  margin-left: auto;
+  margin-right: auto;
 }
 .cups {
-    width: 16px;
-    height: 16px;
+  width: 16px;
+  height: 16px;
 }
 .rank1 {
-    background-position: -32px -16px;
+  background-position: -32px -16px;
 }
 .rank2 {
-    background-position: -32px -48px;
+  background-position: -32px -48px;
 }
 .rank3 {
-    background-position: -32px -80px;
+  background-position: -32px -80px;
 }
 .scoreboard th, .scoreboard td {
-    vertical-align: middle;
-    text-align: center;
+  vertical-align: middle;
+  text-align: center;
 }
 .scoreboard .name {
-    font-weight: bold;
+  font-weight: bold;
 }
 .relative {
-    position: relative;
+  position: relative;
 }
 #app-name {
-    display: inline-block;
+  display: inline-block;
 }
 #total-tracks {
-    color: #BFBFBF;
-    margin-top: 34px;
+  color: #BFBFBF;
+  margin-top: 34px;
 }
 #cassette {
-    margin-top: 22px;
-    height: 137px;
-    background: url('/static/img/cassette.png') no-repeat 0 0;
+  margin-top: 22px;
+  height: 137px;
+  background: url('/static/img/cassette.png') no-repeat 0 0;
 }
 #countdown {
-    position: absolute;
-    width: 26px;
-    text-align: center;
-    top: 80px;
-    left: 175px;
+  position: absolute;
+  width: 26px;
+  text-align: center;
+  top: 80px;
+  left: 175px;
 }
 #wheel-left, #wheel-right {
-    position: absolute;
-    width: 24px;
-    height: 24px;
-    top: 49px;
-    background-position: 0 -32px;
+  position: absolute;
+  width: 24px;
+  height: 24px;
+  top: 49px;
+  background-position: 0 -32px;
 }
 #wheel-left {
-    left: 51px;
+  left: 51px;
 }
 #wheel-right {
-    left: 145px;
+  left: 145px;
 }
 #tape-left, #tape-right {
-    height: 70px;
-    width: 70px;
-    position: absolute;
-    top: 25px;
-    background-color: black;
-    -webkit-border-radius: 70px;
-    -moz-border-radius: 70px;
-    border-radius: 70px;
-    z-index: -1;
+  height: 70px;
+  width: 70px;
+  position: absolute;
+  top: 25px;
+  background-color: black;
+  -webkit-border-radius: 70px;
+  -moz-border-radius: 70px;
+  border-radius: 70px;
+  z-index: -1;
 }
 #tape-left {
-    left: 20px;
+  left: 20px;
 }
 #tape-right {
-    left: 106px;
+  left: 106px;
 }
 #progress-bar, #progress {
-    height: 7px;
-    -webkit-border-radius: 4px;
-    -moz-border-radius: 4px;
-    border-radius: 4px;
-    -webkit-box-shadow: inset 0 1px 2px 0 #333;
-     -moz-box-shadow: inset 0 1px 2px 0 #333;
-    box-shadow: inset 0 1px 2px 0 #333;
+  height: 7px;
+  -webkit-border-radius: 4px;
+  -moz-border-radius: 4px;
+  border-radius: 4px;
+  -webkit-box-shadow: inset 0 1px 2px 0 #333;
+   -moz-box-shadow: inset 0 1px 2px 0 #333;
+  box-shadow: inset 0 1px 2px 0 #333;
 }
 #progress-bar {
-    position: absolute;
-    width: 148px;
-    top: 85px;
-    left: 20px;
-    border: 1px solid #404040;
-    border: 1px solid rgba(0, 0, 0, 0.5);
+  position: absolute;
+  width: 148px;
+  top: 85px;
+  left: 20px;
+  border: 1px solid #404040;
+  border: 1px solid rgba(0, 0, 0, 0.5);
 }
 #progress {
-    background-color: #6184b7;
-    width: 0;
+  background-color: #6184b7;
+  width: 0;
 }
 #touch-backdrop {
-    height: 137px;
-    -webkit-border-radius: 5px;
-    -moz-border-radius: 5px;
-    border-radius: 5px;
-    background: rgba(50,50,50,0.8);
+  height: 137px;
+  -webkit-border-radius: 5px;
+  -moz-border-radius: 5px;
+  border-radius: 5px;
+  background: rgba(50,50,50,0.8);
 }
 #touch-play {
-    position: absolute;
-    top: 54px;
-    left: 73px;
+  position: absolute;
+  top: 54px;
+  left: 73px;
 }
 #volume {
-    height: 159px;
+  height: 159px;
 }
 #volume-button, #volume-slider, #volume-total, #volume-current, #volume-handle {
-    position: absolute;
+  position: absolute;
 }
 #volume-button {
-    bottom: 0;
-    height: 20px;
-    width: 20px;
+  bottom: 0;
+  height: 20px;
+  width: 20px;
 }
 #volume-button .button {
-    display: block;
-    height: 18px;
-    width: 18px;
-    border: 1px solid;
-    border-color: #CCCCCC #CCCCCC #AAAAAA;
-    -webkit-border-radius: 3px;
-    -moz-border-radius: 3px;
-    border-radius: 3px;
-    cursor: pointer;
-    filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=#ffffffff,EndColorStr=#ffe0e0e0);
-    background-image: -moz-linear-gradient(top,#fff 0,#e0e0e0 100%);
-    background-image: -ms-linear-gradient(top,#fff 0,#e0e0e0 100%);
-    background-image: -o-linear-gradient(top,#fff 0,#e0e0e0 100%);
-    background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),color-stop(100%,#e0e0e0));
-    background-image: -webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);
-    background-image: linear-gradient(to bottom,#fff 0,#e0e0e0 100%);
+  display: block;
+  height: 18px;
+  width: 18px;
+  border: 1px solid;
+  border-color: #CCCCCC #CCCCCC #AAAAAA;
+  -webkit-border-radius: 3px;
+  -moz-border-radius: 3px;
+  border-radius: 3px;
+  cursor: pointer;
+  filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=#ffffffff,EndColorStr=#ffe0e0e0);
+  background-image: -moz-linear-gradient(top,#fff 0,#e0e0e0 100%);
+  background-image: -ms-linear-gradient(top,#fff 0,#e0e0e0 100%);
+  background-image: -o-linear-gradient(top,#fff 0,#e0e0e0 100%);
+  background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),color-stop(100%,#e0e0e0));
+  background-image: -webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);
+  background-image: linear-gradient(to bottom,#fff 0,#e0e0e0 100%);
 }
 #volume-button .button:hover {
-    border-color: #999999;
-    -webkit-box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.25);
-    -moz-box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.25);
-    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.25);
+  border-color: #999999;
+  -webkit-box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.25);
+  -moz-box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.25);
+  box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.25);
 }
 #volume-button .button:active {
-    border-color: #999999 #AAAAAA #CCCCCC;
-    -webkit-box-shadow: 0 1px 2px 0 #aaa inset;
-    -moz-box-shadow: 0 1px 2px 0 #aaa inset;
-    box-shadow: 0 1px 2px 0 #aaa inset;
-    filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=#e6e6e6,EndColorStr=#dcdcdc);
-    background-image: -moz-linear-gradient(top,#e6e6e6 0,#dcdcdc 100%);
-    background-image: -ms-linear-gradient(top,#e6e6e6 0,#dcdcdc 100%);
-    background-image: -o-linear-gradient(top,#e6e6e6 0,#dcdcdc 100%);
-    background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0,#e6e6e6),color-stop(100%,#dcdcdc));
-    background-image: -webkit-linear-gradient(top,#e6e6e6 0,#dcdcdc 100%);
-    background-image: linear-gradient(to bottom,#e6e6e6 0,#dcdcdc 100%);
+  border-color: #999999 #AAAAAA #CCCCCC;
+  -webkit-box-shadow: 0 1px 2px 0 #aaa inset;
+  -moz-box-shadow: 0 1px 2px 0 #aaa inset;
+  box-shadow: 0 1px 2px 0 #aaa inset;
+  filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=#e6e6e6,EndColorStr=#dcdcdc);
+  background-image: -moz-linear-gradient(top,#e6e6e6 0,#dcdcdc 100%);
+  background-image: -ms-linear-gradient(top,#e6e6e6 0,#dcdcdc 100%);
+  background-image: -o-linear-gradient(top,#e6e6e6 0,#dcdcdc 100%);
+  background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0,#e6e6e6),color-stop(100%,#dcdcdc));
+  background-image: -webkit-linear-gradient(top,#e6e6e6 0,#dcdcdc 100%);
+  background-image: linear-gradient(to bottom,#e6e6e6 0,#dcdcdc 100%);
 }
 #volume-button .button #icon {
-    margin: 1px;
-    width: 16px;
-    height: 16px;
+  margin: 1px;
+  width: 16px;
+  height: 16px;
 }
 #volume-button .button .volume-none {
-    background-position: 0 -88px;
+  background-position: 0 -88px;
 }
 #volume-button .button .volume-low {
-    background-position: -16px -88px;
+  background-position: -16px -88px;
 }
 #volume-button .button .volume-medium {
-    background-position: 0 -104px;
+  background-position: 0 -104px;
 }
 #volume-button .button .volume-high {
-    background-position: -16px -104px;
+  background-position: -16px -104px;
 }
 #volume-slider {
-    display: none;
-    height: 116px;
-    width: 20px;
-    top: -116px;
-    -webkit-border-radius: 3px;
-    -moz-border-radius: 3px;
-    border-radius: 3px;
-    background: rgba(50, 50, 50, 0.1);
-    z-index: 1;
+  display: none;
+  height: 116px;
+  width: 20px;
+  top: -116px;
+  -webkit-border-radius: 3px;
+  -moz-border-radius: 3px;
+  border-radius: 3px;
+  background: rgba(50, 50, 50, 0.1);
+  z-index: 1;
 }
 #volume-total, #volume-current {
-    left: 8px;
-    top: 8px;
-    width: 4px;
-    height: 100px;
+  left: 8px;
+  top: 8px;
+  width: 4px;
+  height: 100px;
 }
 #volume-total, #volume-current, #volume-handle {
-    -webkit-border-radius: 2px;
-    -moz-border-radius: 2px;
-    border-radius: 2px;
+  -webkit-border-radius: 2px;
+  -moz-border-radius: 2px;
+  border-radius: 2px;
 }
 #volume-total {
-    -webkit-box-shadow: inset 0 0 3px 0 #333;
-    -moz-box-shadow: inset 0 0 3px 0 #333;
-    box-shadow: inset 0 0 3px 0 #333;
+  -webkit-box-shadow: inset 0 0 3px 0 #333;
+  -moz-box-shadow: inset 0 0 3px 0 #333;
+  box-shadow: inset 0 0 3px 0 #333;
 }
 #volume-current {
-    -webkit-box-shadow: inset 0 0 1px 0 #000;
-    -moz-box-shadow: inset 0 0 1px 0 #000;
-    box-shadow: inset 0 0 1px 0 #000;
-    background-color: #6184b7;
+  -webkit-box-shadow: inset 0 0 1px 0 #000;
+  -moz-box-shadow: inset 0 0 1px 0 #000;
+  box-shadow: inset 0 0 1px 0 #000;
+  background-color: #6184b7;
 }
 #volume-handle {
-    left: 1px;
-    width: 16px;
-    height: 4px;
-    background: #ddd;
-    background: rgba(255, 255, 255, 0.9);
-    cursor: N-resize;
-    border: 1px solid #999;
+  left: 1px;
+  width: 16px;
+  height: 4px;
+  background: #ddd;
+  background: rgba(255, 255, 255, 0.9);
+  cursor: N-resize;
+  border: 1px solid #999;
 }
 #summary {
-    text-align: center;
-    margin-bottom: 18px;
+  text-align: center;
+  margin-bottom: 18px;
 }
 #users-wrapper {
-    margin-left: 120px;
-    width: 300px;
+  margin-left: 120px;
+  width: 300px;
 }
 #users {
-    padding-left: 20px;
-    margin-bottom: 72px;
-    max-height: 398px;
-    overflow: auto;
+  padding-left: 20px;
+  margin-bottom: 72px;
+  max-height: 398px;
+  overflow: auto;
 }
 #users .name, #tracks .artist {
-    font-weight: bold;
+  font-weight: bold;
 }
 #message-wrapper {
-    text-align: right;
+  text-align: right;
 }
 #users li, #tracks li, #chat li {
-    color: #404040;
+  color: #404040;
 }
 #users li {
-    height: 18px;
-    position: relative;
+  height: 18px;
+  position: relative;
 }
 #users .private {
-    display: none;
-    font-size: 9.75px;
-    padding: 2px 4px;
-    position: absolute;
-    left: -19px;
+  display: none;
+  font-size: 9.75px;
+  padding: 2px 4px;
+  position: absolute;
+  left: -19px;
 }
 .registered, .round-rank {
-    height: 16px;
-    width: 16px;
-    margin: 1px 2px 0 0;
+  height: 16px;
+  width: 16px;
+  margin: 1px 2px 0 0;
 }
 .registered {
-    background-position: 0 -16px;
+  background-position: 0 -16px;
 }
 .registered:hover {
-    background-position: -16px -16px;
+  background-position: -16px -16px;
 }
 #users .name {
-    margin-right: 4px;
+  margin-right: 4px;
 }
 #users .name, .registered {
-    cursor: pointer;
+  cursor: pointer;
 }
 #users .you {
-    cursor: auto;
+  cursor: auto;
 }
 #users .points, #users .round-points {
-    margin-right: 10px;
+  margin-right: 10px;
 }
 .stand1 {
-    background-position: 0 0;
+  background-position: 0 0;
 }
 .stand2 {
-    background-position: -16px 0;
+  background-position: -16px 0;
 }
 .stand3 {
-    background-position: -32px 0;
+  background-position: -32px 0;
 }
 #users .guess-time {
-    font-size: 11px;
-    line-height: 18px;
+  font-size: 11px;
+  line-height: 18px;
 }
 #toggle-chat {
-    position: absolute;
-    top: -17px;
-    left: 805px;
-    color: #BFBFBF;
-    height: 16px;
-    line-height: 16px;
-    font-size: 11px;
-    border: 1px solid #DDDDDD;
-    -moz-border-radius: 4px 4px 0 0;
-    -webkit-border-radius: 4px 4px 0 0;
-    border-radius: 4px 4px 0 0;
-    cursor: pointer;
-    padding: 0 10px;
-    text-decoration: none;
+  position: absolute;
+  top: -17px;
+  left: 805px;
+  color: #BFBFBF;
+  height: 16px;
+  line-height: 16px;
+  font-size: 11px;
+  border: 1px solid #DDDDDD;
+  -moz-border-radius: 4px 4px 0 0;
+  -webkit-border-radius: 4px 4px 0 0;
+  border-radius: 4px 4px 0 0;
+  cursor: pointer;
+  padding: 0 10px;
+  text-decoration: none;
 }
 #toggle-chat:hover {
-    border-color: #CCCCCC;
-    background-color: #f5f5f5;
-    color: #333333;
-    background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6);
-    background-image: -ms-linear-gradient(top, #ffffff, #e6e6e6);
-    background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6));
-    background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6);
-    background-image: -o-linear-gradient(top, #ffffff, #e6e6e6);
-    background-image: linear-gradient(top, #ffffff, #e6e6e6);
-    filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);
-    -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);
-    -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);
-    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);
+  border-color: #CCCCCC;
+  background-color: #f5f5f5;
+  color: #333333;
+  background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6);
+  background-image: -ms-linear-gradient(top, #ffffff, #e6e6e6);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6));
+  background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6);
+  background-image: -o-linear-gradient(top, #ffffff, #e6e6e6);
+  background-image: linear-gradient(top, #ffffff, #e6e6e6);
+  filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);
+  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);
+  -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);
+  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);
 }
 #toggle-chat:active {
-    background-image: none;
-    -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);
-    -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);
-    box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);
-    background-color: #e6e6e6;
-    background-color: #d9d9d9 \9;
-    outline: 0;
+  background-image: none;
+  -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);
+  -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);
+  box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);
+  background-color: #e6e6e6;
+  background-color: #d9d9d9 \9;
+  outline: 0;
 }
 #chat-wrapper {
-    margin-bottom: 4px;
-    height: 160px;
-    background-color: white;
+  margin-bottom: 4px;
+  height: 160px;
+  background-color: white;
 }
 #chat {
-    height: 152px;
-    width: 446px;
-    margin: 4px 6px;
-    overflow: auto;
+  height: 152px;
+  width: 446px;
+  margin: 4px 6px;
+  overflow: auto;
 }
 .bordered {
-    border: 1px solid #ccc;
-    -webkit-border-radius: 6px;
-    -moz-border-radius: 6px;
-    border-radius: 6px;
-    -webkit-box-shadow: 0 1px 2px rgba(0,0,0,.075);
-    -moz-box-shadow: 0 1px 2px rgba(0,0,0,.075);
-    box-shadow: 0 1px 2px rgba(0,0,0,.075);
+  border: 1px solid #ccc;
+  -webkit-border-radius: 6px;
+  -moz-border-radius: 6px;
+  border-radius: 6px;
+  -webkit-box-shadow: 0 1px 2px rgba(0,0,0,.075);
+  -moz-box-shadow: 0 1px 2px rgba(0,0,0,.075);
+  box-shadow: 0 1px 2px rgba(0,0,0,.075);
 }
 #chat .join, #chat .left {
-    font-style: italic;
+  font-style: italic;
 }
 #chat .private {
-    color: #a65fc3;
+  color: #a65fc3;
 }
 #feedback {
-    text-align: center;
+  text-align: center;
 }
 #feedback .correct, #users .correct{
-    color: #46A546;
+  color: #46A546;
 }
 #feedback .wrong, #chat .error {
-    color: #C43C35;
+  color: #C43C35;
 }
 #guess.correct {
-    border-color: #46A546;
+  border-color: #46A546;
 }
 #guess.correct:focus {
-    -webkit-box-shadow: 0 0 6px #7aba7b;
-    -moz-box-shadow: 0 0 6px #7aba7b;
-    box-shadow: 0 0 6px #7aba7b;
+  -webkit-box-shadow: 0 0 6px #7aba7b;
+  -moz-box-shadow: 0 0 6px #7aba7b;
+  box-shadow: 0 0 6px #7aba7b;
 }
 #guess.wrong {
-    border-color: #C43C35;
+  border-color: #C43C35;
 }
 #guess.wrong:focus {
-    -webkit-box-shadow: 0 0 6px #d59392;
-    -moz-box-shadow: 0 0 6px #d59392;
-    box-shadow: 0 0 6px #d59392;
+  -webkit-box-shadow: 0 0 6px #d59392;
+  -moz-box-shadow: 0 0 6px #d59392;
+  box-shadow: 0 0 6px #d59392;
 }
 #tracks {
-    margin: 18px 0;
-    max-height: 240px;
-    overflow: auto;
+  margin: 18px 0;
+  max-height: 240px;
+  overflow: auto;
 }
 #tracks li {
-    margin: 0 2px 2px 0;
-    padding: 8px;
-    min-height: 40px;
-    background: -moz-linear-gradient(center top , #FBFBFB, #F5F5F5);
-    background: -webkit-gradient(linear, center top, center bottom, from(#FBFBFB), to(#F5F5F5));
-    background: -webkit-linear-gradient(center top , #FBFBFB, #F5F5F5);
-    background: -o-linear-gradient(center top , #FBFBFB, #F5F5F5);
-    background: -ms-linear-gradient(center top , #FBFBFB, #F5F5F5);
-    background: linear-gradient(center top , #FBFBFB, #F5F5F5);
+  margin: 0 2px 2px 0;
+  padding: 8px;
+  min-height: 40px;
+  background: -moz-linear-gradient(center top , #FBFBFB, #F5F5F5);
+  background: -webkit-gradient(linear, center top, center bottom, from(#FBFBFB), to(#F5F5F5));
+  background: -webkit-linear-gradient(center top , #FBFBFB, #F5F5F5);
+  background: -o-linear-gradient(center top , #FBFBFB, #F5F5F5);
+  background: -ms-linear-gradient(center top , #FBFBFB, #F5F5F5);
+  background: linear-gradient(center top , #FBFBFB, #F5F5F5);
 }
 .registered, #users .name, #users .points, .round-rank, .round-points, #users .guess-time, #tracks img.artwork, #tracks .info, .highscores .img, .profile .img, #copy, #facebook-button, #twitter-button, #github-button {
-    float: left;
+  float: left;
 }
 #tracks img.artwork {
-    width: 40px;
-    height: 40px;
-    margin-right: 10px;
+  width: 40px;
+  height: 40px;
+  margin-right: 10px;
 }
 #tracks .info {
-    margin-right: 15px;
+  margin-right: 15px;
 }
 #tracks .artist, #tracks .title {
-    white-space: nowrap;
-    max-width: 270px;
-    overflow: hidden;
-    -o-text-overflow: ellipsis;
-    -ms-text-overflow: ellipsis;
-    text-overflow: ellipsis;
+  white-space: nowrap;
+  max-width: 270px;
+  overflow: hidden;
+  -o-text-overflow: ellipsis;
+  -ms-text-overflow: ellipsis;
+  text-overflow: ellipsis;
 }
 #tracks .artist {
-    margin-top: 1px;
-    font-size: 14px;
+  margin-top: 1px;
+  font-size: 14px;
 }
 #tracks .round-rank {
-    margin-top: 12px;
+  margin-top: 12px;
 }
 #tracks .round-points {
-    margin-top: 11px;
+  margin-top: 11px;
 }
 #tracks a {
-    display: block;
-    width: 44px;
-    height: 15px;
-    margin-top: 13px;
-    background-position: 0 -248px;
+  display: block;
+  width: 44px;
+  height: 15px;
+  margin-top: 13px;
+  background-position: 0 -248px;
 }
 #tracks a:hover {
-    background-position: -44px -248px;
+  background-position: -44px -248px;
 }
 #tracks a:active {
-    background-position: 0 -263px;
+  background-position: 0 -263px;
 }
 #disclaimer {
-    position: absolute;
-    left: 60px;
-    bottom: 18px;
-    color: #BFBFBF;
-    width: 340px;
+  position: absolute;
+  left: 60px;
+  bottom: 18px;
+  color: #BFBFBF;
+  width: 340px;
 }
 footer {
-    padding: 0;
-    margin-top: 17px;
-    border-top: 1px solid #DDDDDD;
-    color: #BFBFBF;
-    text-shadow: 0 1px 0 #FFFFFF;
+  padding: 0;
+  margin-top: 17px;
+  border-top: 1px solid #DDDDDD;
+  color: #BFBFBF;
+  text-shadow: 0 1px 0 #FFFFFF;
 }
 #footer-inner, #twitter-button, #github-button {
-    height: 20px;
+  height: 20px;
 }
 #footer-inner {
-    border-top: 1px solid white;
-    padding: 5px 20px 0 20px;
-    margin-bottom: 30px;
+  border-top: 1px solid white;
+  padding: 5px 20px 0 20px;
+  margin-bottom: 30px;
 }
 #copy {
-    margin-right: 20px;
+  margin-right: 20px;
 }
 #facebook-button {
-    width: 90px;
-    height: 21px
+  width: 90px;
+  height: 21px
 }
 #twitter-button, #github-button {
-    width: 100px;
+  width: 100px;
 }
 #copy, .footer-info {
-    line-height: 20px;
+  line-height: 20px;
 }
 .footer-info {
-    white-space: pre-wrap;
+  white-space: pre-wrap;
 }
 #nodejitsu-logo {
-    display: block;
-    width: 84px;
-    height: 20px;
-    background-position: 0 -208px;
+  display: block;
+  width: 84px;
+  height: 20px;
+  background-position: 0 -208px;
 }
 #nodejitsu-logo:hover {
-    background-position: 0 -228px;
+  background-position: 0 -228px;
 }
index e899f8315223959233e9e8e98cde47ff8364cc83..9fb8fe5e593e1e1822b45f37d6c60c0347a854e0 100644 (file)
 (function() {
 
-    var elapsedtime = 0
-        , DOM = {}
-        , historycursor = 0
-        , historyvalues = []
-        , ignoredplayers = {}
-        , jplayer
-        , nickname
-        , pvtmsgto
-        , subscriber = false
-        , roundpoints = 0
-        , roomname = window.location.pathname.replace('/', '')
-        , socket
-        , stopanimation = false
-        , touchplay
-        , urlregex = /(https?:\/\/[\-A-Za-z0-9+&@#\/%?=~_()|!:,.;]*[\-A-Za-z0-9+&@#\/%=~_()|])/
-        , uri = window.location.protocol+'//'+window.location.host; // Socket.IO server URI
-
-    var amstrings = [
-        'Yes, that\'s the artist. What about the title?'
-        , 'Exactly, now tell me the title!'
-        , 'Do you also know the title?'
-    ];
-
-    var bmstrings = [
-        'Yeah true! do you like this track?'
-        , 'Good job!'
-        , 'Great!'
-        , 'Very well done!'
-        , 'Exactly!'
-        , 'Excellent!'
-        , 'Woohoo!'
-    ];
-
-    var nmstrings = [
-        'Nope, sorry!'
-        , 'No way!'
-        , 'Fail'
-        , 'Nope'
-        , 'No'
-        , 'That\'s wrong'
-        , 'What?!'
-        , 'Wrong', 'Haha, what?!'
-        , 'You kidding?'
-        , 'Don\'t make me laugh'
-        , 'You mad?'
-        , 'Try again'
-    ];
-
-    var states = [
-        'A song is already playing, please wait for the next one...'
-        , 'Game is about to start...'
-        , 'Game is over'
-        , 'New game will start soon...'
-    ];
-
-    var tmstrings = [
-        'Yes, you guessed the title. Who is the artist?'
-        , 'Now tell me the artist!'
-        , 'Correct, do you also know the artist?'
-    ];
-
-    String.prototype.encodeEntities = function() {
-        return this.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
-    };
-
-    // Add a chat entry, whether message, notification, etc.
-    var addChatEntry = function(childNode) {
-        var li = $('<li class="entry"></li>');
-        li.append(childNode);
-        DOM.chat.append(li);
-        DOM.chat[0].scrollTop = DOM.chat[0].scrollHeight;
-    };
-
-    var addFeedback = function(txt, style) {
-        if (typeof style === 'string') {
-            var fbspan = $('<span class="'+style+'"></span>');
-            fbspan.text(txt);
-            DOM.feedback.html(fbspan);
-            DOM.guessbox.addClass(style);
-            setTimeout(function() {DOM.guessbox.removeClass(style);}, 350);
-            return;
-        }
-        DOM.feedback.text(txt);
-    };
-
-    var addPrivate = function(usrname) {
-        if (pvtmsgto) {
-            clearPrivate();
-        }
-        if (nickname === usrname) {
-            return;
-        }
-        DOM.recipient.css('margin-right', '4px');
-        DOM.recipient.text('To '+usrname+':');
-        var width = DOM.recipient.outerWidth(true) + 1;
-        DOM.recipient.hide();
-        DOM.messagebox.animate({'width':'-='+width+'px'}, 'fast', function() {
-            DOM.recipient.show();
-        });
-        var el = $('.name').filter(function(index) {
-            return $(this).text() === usrname;
-        });
-        el.prevAll('.private').show();
-        el.unbind('click');
-        el.click(clearPrivate);
-        pvtmsgto = usrname;
-        DOM.messagebox.focus();
-    };
-
-    // Add track info
-    var addTrackInfo = function(data) {
-        if (touchplay) {
-            touchplay.removeClass('btn-success').addClass('btn-danger disabled');
-            touchplay.html('<i class="icon-play icon-white"></i> Wait');
-        }
-        cassetteAnimation(Date.now()+5000, false);
-
-        var artistName = data.artistName.replace(/"/g, '&quot;')
-            , trackName = data.trackName.replace(/"/g, '&quot;')
-            , attrs = ''
-            , rp = '';
-
-        var html = '<li class="bordered"><img class="artwork" src="'+data.artworkUrl+'"/>';
-        html += '<div class="info"><div class="artist" title="'+artistName+'">'+artistName+'</div>';
-        html += '<div class="title" title="'+trackName+'">'+trackName+'</div></div>';
-
-        if (roundpoints > 0) {
-            rp = '+'+roundpoints;
-            if (roundpoints > 3) {
-                var stand = 7 - roundpoints;
-                attrs += 'class="icons round-rank stand'+stand+'"';
-            }
-        }
-        html += '<div '+attrs+'></div><div class="round-points">'+rp+'</div>';
-        html += '<a class="icons" target="itunes_store" href="'+data.trackViewUrl+'"></a></li>';
-
-        DOM.tracks.prepend($(html));
-    };
-
-    var addVolumeControl = function() {
-        var volumebutton = $('<div id="volume-button">'+
-            '<a class="button"><div id="icon" class="icons volume-high"></div></a>'+
-            '<div id="volume-slider">'+ // Outer background
-                '<div id="volume-total"></div>'+ // Rail
-                '<div id="volume-current"></div>'+ // Current volume
-                '<div id="volume-handle"></div>'+ // Handle
-            '</div></div>').appendTo('#volume');
-
-        var clicked = false
-            , icon = volumebutton.find('#icon')
-            , mouseisdown = false
-            , mouseisover = false
-            , oldvalue = 1
-            , volumecurrent = volumebutton.find('#volume-current')
-            , volumehandle = volumebutton.find('#volume-handle')
-            , volumeslider = volumebutton.find('#volume-slider')
-            , volumetotal = volumebutton.find('#volume-total');
-
-        var handleIcon = function (volume) {
-            if (volume === 0) {
-                icon.removeClass().addClass('icons volume-none');
-            }
-            else if (volume <= 0.33) {
-                icon.removeClass().addClass('icons volume-low');
-            }
-            else if (volume <= 0.66) {
-                icon.removeClass().addClass('icons volume-medium');
-            }
-            else {
-                icon.removeClass().addClass('icons volume-high');
-            }
-        };
-
-        var handleVolumeMove = function(e) {
-            var railheight = volumetotal.height()
-                , totaloffset = volumetotal.offset()
-                , totalTop = parseInt(volumetotal.css('top').replace(/px/, ''), 10)
-                , newy = e.pageY - totaloffset.top
-                , volume = (railheight - newy) / railheight;
-
-            clicked = false;
-
-            if (newy < 0) {
-                newy = 0;
-            }
-            else if (newy > railheight) {
-                newy = railheight;
-            }
-
-            volumecurrent.height(railheight - newy);
-            volumecurrent.css('top', newy + totalTop);
-            volumehandle.css('top', totalTop + newy - (volumehandle.height() / 2));
-
-            volume = Math.max(0, volume);
-            volume = Math.min(volume, 1);
-
-            setVolume(volume);
-        };
-
-        var loadFromCookie = function() {
-            if (/volume\s*\=/.test(document.cookie)) {
-                var value = document.cookie.replace(/.*volume\s*\=\s*([^;]*);?.*/, '$1');
-                value = parseFloat(value);
-                positionVolumeHandle(value);
-                setVolume(value);
-                return;
-            }
-            positionVolumeHandle(1);
-        };
-
-        var positionVolumeHandle = function(volume) {
-            if (!volumeslider.is(':visible')) {
-                volumeslider.show();
-                positionVolumeHandle(volume);
-                volumeslider.hide();
-                return;
-            }
-            var totalheight = volumetotal.height();
-            var totalposition = volumetotal.position();
-            var newtop = totalheight - (totalheight * volume);
-            volumecurrent.height(totalheight - newtop );
-            volumecurrent.css('top', totalposition.top + newtop);
-            volumehandle.css('top', totalposition.top + newtop - (volumehandle.height() / 2));
-        };
-
-        var setCookie = function(volume) {
-            var d = new Date();
-            d.setTime(d.getTime() + 31536000000); // One year in milliseconds
-            document.cookie = 'volume='+volume+';path=/;expires='+d.toGMTString()+';';
-        };
-
-        var setVolume = function(volume) {
-            handleIcon(volume);
-            jplayer.jPlayer('volume', volume);
-            oldvalue = volume;
-            setCookie(volume);
-        };
-
-        volumebutton.find('.button').click(function() {
-            if (!clicked) {
-                clicked = true;
-                if (oldvalue !== 0) {
-                    handleIcon(0);
-                    jplayer.jPlayer('volume', 0);
-                    positionVolumeHandle(0);
-                }
-            }
-            else {
-                clicked = false;
-                if (oldvalue !== 0) {
-                    handleIcon(oldvalue);
-                    jplayer.jPlayer('volume', oldvalue);
-                    positionVolumeHandle(oldvalue);
-                }
-            }
-        });
-
-        volumebutton.hover(function() {
-            mouseisover = true;
-            volumeslider.show();
-        }, function() {
-            mouseisover = false;
-            if (!mouseisdown) {
-                volumeslider.hide();
-            }
-        });
-
-        volumeslider.on('mouseover', function() {
-            mouseisover = true;
-        }).on('mousedown', function(e) {
-            handleVolumeMove(e);
-            mouseisdown = true;
-            return false;
-        });
-
-        $(document).on('mouseup', function(e) {
-            mouseisdown = false;
-            if (!mouseisover) {
-                volumeslider.hide();
-            }
-        }).on('mousemove', function(e) {
-            if (mouseisdown) {
-                handleVolumeMove(e);
-            }
-        });
-
-        loadFromCookie();
-    };
-
-    // Called when a registered user already in a room, tries to enter in another room
-    var alreadyInARoom = function() {
-        var html = '<div class="modal-header"><h3>Already in a room</h3></div>';
-        html += '<div class="modal-body"><div class="alert alert-error alert-block">';
-        html += '<h4 class="alert-heading">Warning!</h4>You are already in a room.<br/>';
-        html += 'Leave the other room and refresh this page or close this one.</div></div>';
-        $(html).appendTo(DOM.modal);
-        DOM.modal.modal('show');
-    };
-
-    // Start cassette animation
-    var cassetteAnimation = function(endtime, forward) {
-        var millisleft = endtime - Date.now()
-            , secleft = millisleft / 1000
-            , width
-            , deg
-            , offsetleft
-            , offsetright
-            , css;
-
-        if (forward) {
-            width = 148 - (148*secleft/30);
-            deg = 360 - (360*secleft/30);
-            offsetleft = 44 - 24*secleft/30;
-            offsetright = 130 - 24*secleft/30;
-            DOM.progress.width(width);
-            DOM.cassettewheels.css('transform', 'rotate('+deg+'deg)');
-            DOM.tapeleft.css('left', offsetleft+'px');
-            DOM.taperight.css('left', offsetright+'px');
-        }
-        else {
-            width = 148*secleft/5;
-            deg = 360*secleft/5;
-            offsetleft = 20 + 24*secleft/5;
-            offsetright = 106 + 24*secleft/5;
-            DOM.progress.width(width);
-            DOM.cassettewheels.css('transform', 'rotate('+deg+'deg)');
-            DOM.tapeleft.css('left', offsetleft+'px');
-            DOM.taperight.css('left', offsetright+'px');
-        }
-
-        if (forward) {
-            DOM.countdown.text(secleft.toFixed(1));
-            if (touchplay) {elapsedtime = 30 - Math.round(secleft);}
-        }
-        else {
-            DOM.countdown.text(Math.round(secleft));
-        }
-
-        if (stopanimation || millisleft < 50) {
-            return;
-        }
-
-        setTimeout(function() {cassetteAnimation(endtime, forward);}, 50);
-    };
-
-    var clearPrivate = function() {
-        var width = DOM.recipient.outerWidth(true) + 1;
-        DOM.recipient.css('margin-right', '0');
-        DOM.recipient.text('');
-        DOM.messagebox.animate({'width':'+='+width+'px'}, 'fast');
-        var el = $('.name').filter(function(index) {
-            return $(this).text() === pvtmsgto;
-        });
-        el.prevAll('.private').hide();
-        el.unbind('click');
-        el.click(function() {
-            addPrivate($(this).text());
-        });
-        pvtmsgto = null;
-        DOM.messagebox.focus();
-    };
-
-    // Game over countdown
-    var countDown = function(endtime) {
-        var millisleft = endtime - Date.now();
-        var secleft = millisleft / 1000;
-        $('.modal-footer span').text(Math.round(secleft));
-        if (millisleft < 200) {
-            return;
-        }
-        setTimeout(function() {countDown(endtime);}, 200);
-    };
-
-    // Let the user know when he/she has disconnected
-    var disconnect = function() {
-        stopanimation = true;
-        jplayer.jPlayer('stop');
-        var errorspan = $('<span class="error">ERROR: You have disconnected.</span>');
-        addChatEntry(errorspan);
-        addFeedback('Something wrong happened');
-        DOM.users.empty();
-    };
+  var elapsedtime = 0
+    , DOM = {}
+    , historycursor = 0
+    , historyvalues = []
+    , ignoredplayers = {}
+    , jplayer
+    , nickname
+    , pvtmsgto
+    , subscriber = false
+    , roundpoints = 0
+    , roomname = window.location.pathname.replace('/', '')
+    , socket
+    , stopanimation = false
+    , touchplay
+    , urlregex = /(https?:\/\/[\-A-Za-z0-9+&@#\/%?=~_()|!:,.;]*[\-A-Za-z0-9+&@#\/%=~_()|])/
+    , uri = window.location.protocol+'//'+window.location.host; // Socket.IO server URI
+
+  var amstrings = [
+    'Yes, that\'s the artist. What about the title?'
+    , 'Exactly, now tell me the title!'
+    , 'Do you also know the title?'
+  ];
+
+  var bmstrings = [
+    'Yeah true! do you like this track?'
+    , 'Good job!'
+    , 'Great!'
+    , 'Very well done!'
+    , 'Exactly!'
+    , 'Excellent!'
+    , 'Woohoo!'
+  ];
+
+  var nmstrings = [
+    'Nope, sorry!'
+    , 'No way!'
+    , 'Fail'
+    , 'Nope'
+    , 'No'
+    , 'That\'s wrong'
+    , 'What?!'
+    , 'Wrong', 'Haha, what?!'
+    , 'You kidding?'
+    , 'Don\'t make me laugh'
+    , 'You mad?'
+    , 'Try again'
+  ];
+
+  var states = [
+    'A song is already playing, please wait for the next one...'
+    , 'Game is about to start...'
+    , 'Game is over'
+    , 'New game will start soon...'
+  ];
+
+  var tmstrings = [
+    'Yes, you guessed the title. Who is the artist?'
+    , 'Now tell me the artist!'
+    , 'Correct, do you also know the artist?'
+  ];
+
+  String.prototype.encodeEntities = function() {
+    return this.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
+  };
+
+  // Add a chat entry, whether message, notification, etc.
+  var addChatEntry = function(childNode) {
+    var li = $('<li class="entry"></li>');
+    li.append(childNode);
+    DOM.chat.append(li);
+    DOM.chat[0].scrollTop = DOM.chat[0].scrollHeight;
+  };
+
+  var addFeedback = function(txt, style) {
+    if (typeof style === 'string') {
+      var fbspan = $('<span class="'+style+'"></span>');
+      fbspan.text(txt);
+      DOM.feedback.html(fbspan);
+      DOM.guessbox.addClass(style);
+      setTimeout(function() {DOM.guessbox.removeClass(style);}, 350);
+      return;
+    }
+    DOM.feedback.text(txt);
+  };
 
-    var gameOver = function(podium) {
-        var html = '<div class="modal-header"><h3>Game Over</h3></div>';
-        html += '<div class="modal-body"><table class="table table-striped scoreboard">';
-        html += '<thead><tr><th>#</th><th>Name</th><th>Points</th>';
-        html += '<th><div class="icons cups stand1"></div></th>';
-        html += '<th><div class="icons cups stand2"></div></th>';
-        html += '<th><div class="icons cups stand3"></div></th><th>Guessed</th><th>Mean time</th>';
-        html += '</thead><tbody>';
-
-        for(var i=0;i<3;i++) {
-            if (podium[i]) {
-                html += '<tr><td><div class="icons medals rank'+(i+1)+'"></div></td>';
-                html += '<td class="name">'+podium[i].nickname+'</td>';
-                html += '<td>'+podium[i].points+'</td>';
-                html += '<td>'+podium[i].golds+'</td><td>'+podium[i].silvers+'</td>';
-                html += '<td>'+podium[i].bronzes+'</td><td>'+podium[i].guessed+'</td>';
-                var meantime = "N/A";
-                if (podium[i].guessed !== 0) {
-                    meantime = podium[i].totguesstime / podium[i].guessed;
-                    meantime = (meantime / 1000).toFixed(1)+' s';
-                }
-                html += '<td>'+meantime+'</td></tr>';
-            }
-        }
+  var addPrivate = function(usrname) {
+    if (pvtmsgto) {
+      clearPrivate();
+    }
+    if (nickname === usrname) {
+      return;
+    }
+    DOM.recipient.css('margin-right', '4px');
+    DOM.recipient.text('To '+usrname+':');
+    var width = DOM.recipient.outerWidth(true) + 1;
+    DOM.recipient.hide();
+    DOM.messagebox.animate({'width':'-='+width+'px'}, 'fast', function() {
+      DOM.recipient.show();
+    });
+    var el = $('.name').filter(function(index) {
+      return $(this).text() === usrname;
+    });
+    el.prevAll('.private').show();
+    el.unbind('click');
+    el.click(clearPrivate);
+    pvtmsgto = usrname;
+    DOM.messagebox.focus();
+  };
+
+  // Add track info
+  var addTrackInfo = function(data) {
+    if (touchplay) {
+      touchplay.removeClass('btn-success').addClass('btn-danger disabled');
+      touchplay.html('<i class="icon-play icon-white"></i> Wait');
+    }
+    cassetteAnimation(Date.now()+5000, false);
+
+    var artistName = data.artistName.replace(/"/g, '&quot;')
+      , trackName = data.trackName.replace(/"/g, '&quot;')
+      , attrs = ''
+      , rp = '';
+
+    var html = '<li class="bordered"><img class="artwork" src="'+data.artworkUrl+'"/>';
+    html += '<div class="info"><div class="artist" title="'+artistName+'">'+artistName+'</div>';
+    html += '<div class="title" title="'+trackName+'">'+trackName+'</div></div>';
+
+    if (roundpoints > 0) {
+      rp = '+'+roundpoints;
+      if (roundpoints > 3) {
+        var stand = 7 - roundpoints;
+        attrs += 'class="icons round-rank stand'+stand+'"';
+      }
+    }
+    html += '<div '+attrs+'></div><div class="round-points">'+rp+'</div>';
+    html += '<a class="icons" target="itunes_store" href="'+data.trackViewUrl+'"></a></li>';
+
+    DOM.tracks.prepend($(html));
+  };
+
+  var addVolumeControl = function() {
+    var volumebutton = $('<div id="volume-button">'+
+      '<a class="button"><div id="icon" class="icons volume-high"></div></a>'+
+      '<div id="volume-slider">'+ // Outer background
+        '<div id="volume-total"></div>'+ // Rail
+        '<div id="volume-current"></div>'+ // Current volume
+        '<div id="volume-handle"></div>'+ // Handle
+      '</div></div>').appendTo('#volume');
+
+    var clicked = false
+      , icon = volumebutton.find('#icon')
+      , mouseisdown = false
+      , mouseisover = false
+      , oldvalue = 1
+      , volumecurrent = volumebutton.find('#volume-current')
+      , volumehandle = volumebutton.find('#volume-handle')
+      , volumeslider = volumebutton.find('#volume-slider')
+      , volumetotal = volumebutton.find('#volume-total');
+
+    var handleIcon = function (volume) {
+      if (volume === 0) {
+        icon.removeClass().addClass('icons volume-none');
+      }
+      else if (volume <= 0.33) {
+        icon.removeClass().addClass('icons volume-low');
+      }
+      else if (volume <= 0.66) {
+        icon.removeClass().addClass('icons volume-medium');
+      }
+      else {
+        icon.removeClass().addClass('icons volume-high');
+      }
+    };
+
+    var handleVolumeMove = function(e) {
+      var railheight = volumetotal.height()
+        , totaloffset = volumetotal.offset()
+        , totalTop = parseInt(volumetotal.css('top').replace(/px/, ''), 10)
+        , newy = e.pageY - totaloffset.top
+        , volume = (railheight - newy) / railheight;
+
+      clicked = false;
+
+      if (newy < 0) {
+        newy = 0;
+      }
+      else if (newy > railheight) {
+        newy = railheight;
+      }
+
+      volumecurrent.height(railheight - newy);
+      volumecurrent.css('top', newy + totalTop);
+      volumehandle.css('top', totalTop + newy - (volumehandle.height() / 2));
+
+      volume = Math.max(0, volume);
+      volume = Math.min(volume, 1);
+
+      setVolume(volume);
+    };
+
+    var loadFromCookie = function() {
+      if (/volume\s*\=/.test(document.cookie)) {
+        var value = document.cookie.replace(/.*volume\s*\=\s*([^;]*);?.*/, '$1');
+        value = parseFloat(value);
+        positionVolumeHandle(value);
+        setVolume(value);
+        return;
+      }
+      positionVolumeHandle(1);
+    };
+
+    var positionVolumeHandle = function(volume) {
+      if (!volumeslider.is(':visible')) {
+        volumeslider.show();
+        positionVolumeHandle(volume);
+        volumeslider.hide();
+        return;
+      }
+      var totalheight = volumetotal.height();
+      var totalposition = volumetotal.position();
+      var newtop = totalheight - (totalheight * volume);
+      volumecurrent.height(totalheight - newtop );
+      volumecurrent.css('top', totalposition.top + newtop);
+      volumehandle.css('top', totalposition.top + newtop - (volumehandle.height() / 2));
+    };
+
+    var setCookie = function(volume) {
+      var d = new Date();
+      d.setTime(d.getTime() + 31536000000); // One year in milliseconds
+      document.cookie = 'volume='+volume+';path=/;expires='+d.toGMTString()+';';
+    };
+
+    var setVolume = function(volume) {
+      handleIcon(volume);
+      jplayer.jPlayer('volume', volume);
+      oldvalue = volume;
+      setCookie(volume);
+    };
+
+    volumebutton.find('.button').click(function() {
+      if (!clicked) {
+        clicked = true;
+        if (oldvalue !== 0) {
+          handleIcon(0);
+          jplayer.jPlayer('volume', 0);
+          positionVolumeHandle(0);
+        }
+      }
+      else {
+        clicked = false;
+        if (oldvalue !== 0) {
+          handleIcon(oldvalue);
+          jplayer.jPlayer('volume', oldvalue);
+          positionVolumeHandle(oldvalue);
+        }
+      }
+    });
 
-        html +='</tbody></table></div>';
-        html += '<div class="modal-footer align-left">A new game will start in ';
-        html += '<span></span> second/s</div>';
-        DOM.modal.append($(html));
-        DOM.modal.modal('show');
-        countDown(Date.now()+10000);
-    };
+    volumebutton.hover(function() {
+      mouseisover = true;
+      volumeslider.show();
+    }, function() {
+      mouseisover = false;
+      if (!mouseisdown) {
+        volumeslider.hide();
+      }
+    });
 
-    // Receive a chat message
-    var getChatMessage = function(chatmsg, from, to) {
-        if (ignoredplayers[from]) {
-            return;
-        }
-        var prefix = from;
-        var msgspan = $('<span class="message"></span>');
-        if (to) {
-            // Private Message
-            prefix = (nickname === from) ? '(To '+to+')' : '(From '+prefix+')';
-            msgspan.addClass('private');
-        }
-        var msg = prefix+': '+chatmsg;
-        msgspan.html(urlize(msg));
-        addChatEntry(msgspan);
-    };
+    volumeslider.on('mouseover', function() {
+      mouseisover = true;
+    }).on('mousedown', function(e) {
+      handleVolumeMove(e);
+      mouseisdown = true;
+      return false;
+    });
 
-    var hideChat = function() {
-        DOM.togglechat.text('Show chat').unbind('click');
-        DOM.chatwrapper.toggle(300);
-        DOM.tracks.animate({maxHeight:'434px'}, 300);
-        DOM.togglechat.click(showChat);
-    };
+    $(document).on('mouseup', function(e) {
+      mouseisdown = false;
+      if (!mouseisover) {
+        volumeslider.hide();
+      }
+    }).on('mousemove', function(e) {
+      if (mouseisdown) {
+        handleVolumeMove(e);
+      }
+    });
 
-    // Put a player in the ignore list
-    var ignorePlayer = function(args, outcome) {
-        if (ignoredplayers[args[0]]) {
-            outcome.text('(From binb): '+args[0]+' is already ignored.');
-            return addChatEntry(outcome);
-        }
-        socket.emit('ignore', args[0], function(player) {
-            if (player) {
-                ignoredplayers[player] = true;
-                outcome.text('(From binb): '+player+' is now ignored.');
-                return addChatEntry(outcome);
-            }
-            outcome.append('player not found.');
-            addChatEntry(outcome);
-        });
-    };
+    loadFromCookie();
+  };
+
+  // Called when a registered user already in a room, tries to enter in another room
+  var alreadyInARoom = function() {
+    var html = '<div class="modal-header"><h3>Already in a room</h3></div>';
+    html += '<div class="modal-body"><div class="alert alert-error alert-block">';
+    html += '<h4 class="alert-heading">Warning!</h4>You are already in a room.<br/>';
+    html += 'Leave the other room and refresh this page or close this one.</div></div>';
+    $(html).appendTo(DOM.modal);
+    DOM.modal.modal('show');
+  };
+
+  // Start cassette animation
+  var cassetteAnimation = function(endtime, forward) {
+    var millisleft = endtime - Date.now()
+      , secleft = millisleft / 1000
+      , width
+      , deg
+      , offsetleft
+      , offsetright
+      , css;
+
+    if (forward) {
+      width = 148 - (148*secleft/30);
+      deg = 360 - (360*secleft/30);
+      offsetleft = 44 - 24*secleft/30;
+      offsetright = 130 - 24*secleft/30;
+      DOM.progress.width(width);
+      DOM.cassettewheels.css('transform', 'rotate('+deg+'deg)');
+      DOM.tapeleft.css('left', offsetleft+'px');
+      DOM.taperight.css('left', offsetright+'px');
+    }
+    else {
+      width = 148*secleft/5;
+      deg = 360*secleft/5;
+      offsetleft = 20 + 24*secleft/5;
+      offsetright = 106 + 24*secleft/5;
+      DOM.progress.width(width);
+      DOM.cassettewheels.css('transform', 'rotate('+deg+'deg)');
+      DOM.tapeleft.css('left', offsetleft+'px');
+      DOM.taperight.css('left', offsetright+'px');
+    }
 
-    // Submitted name was invalid
-    var invalidNickName = function(feedback) {
-        joinAnonymously(feedback+'<br/>Try with another one:');
-    };
+    if (forward) {
+      DOM.countdown.text(secleft.toFixed(1));
+      if (touchplay) {elapsedtime = 30 - Math.round(secleft);}
+    }
+    else {
+      DOM.countdown.text(Math.round(secleft));
+    }
 
-    // Prompt for name and send it
-    var joinAnonymously = function(msg) {
-        if (/nickname\s*\=/.test(document.cookie) && !msg) {
-            var nickname = document.cookie.replace(/.*nickname\s*\=\s*([^;]*);?.*/, '$1');
-            return socket.emit('joinanonymously', nickname, roomname);
-        }
+    if (stopanimation || millisleft < 50) {
+      return;
+    }
 
-        if (DOM.modal.hasClass('in')) {
-            $('.modal-body p').html(msg);
-            return $('#login').focus();
-        }
+    setTimeout(function() {cassetteAnimation(endtime, forward);}, 50);
+  };
 
-        var html = '<div class="modal-header">';
-        html += '<h3>You are joining the '+roomname+' room</h3></div>';
-        html += '<div class="modal-body"><p>'+(msg || "What's your name?")+'</p></div>';
-        html += '<div class="modal-footer relative">';
-        html += '<input id="login" maxlength="15" type="text" name="nickname" />';
-        html += '<button id="join" class="btn btn-success">';
-        html += '<i class="icon-user icon-white"></i> Join the game</button>';
-        html += '<span class="divider"><span>or</span></span>';
-        html += '<a class="btn btn-primary" href="/login?followup=/'+roomname+'">';
-        html += '<i class="icon-lock icon-white"></i> Login</a></div>';
-
-        $(html).appendTo(DOM.modal);
-        var login = $('#login');
-        var button = $('#join');
-
-        button.click(function() {
-            if ($.trim(login.val()) !== '') {
-                nickname = login.val();
-                socket.emit('joinanonymously', nickname, roomname);
-            }
-            else {
-                var txt = 'Nickname can\'t be empty.';
-                invalidNickName('<span class="label label-important">'+txt+'</span>');
-            }
-            login.val('');
-        });
+  var clearPrivate = function() {
+    var width = DOM.recipient.outerWidth(true) + 1;
+    DOM.recipient.css('margin-right', '0');
+    DOM.recipient.text('');
+    DOM.messagebox.animate({'width':'+='+width+'px'}, 'fast');
+    var el = $('.name').filter(function(index) {
+      return $(this).text() === pvtmsgto;
+    });
+    el.prevAll('.private').hide();
+    el.unbind('click');
+    el.click(function() {
+      addPrivate($(this).text());
+    });
+    pvtmsgto = null;
+    DOM.messagebox.focus();
+  };
+
+  // Game over countdown
+  var countDown = function(endtime) {
+    var millisleft = endtime - Date.now();
+    var secleft = millisleft / 1000;
+    $('.modal-footer span').text(Math.round(secleft));
+    if (millisleft < 200) {
+      return;
+    }
+    setTimeout(function() {countDown(endtime);}, 200);
+  };
+
+  // Let the user know when he/she has disconnected
+  var disconnect = function() {
+    stopanimation = true;
+    jplayer.jPlayer('stop');
+    var errorspan = $('<span class="error">ERROR: You have disconnected.</span>');
+    addChatEntry(errorspan);
+    addFeedback('Something wrong happened');
+    DOM.users.empty();
+  };
+
+  var gameOver = function(podium) {
+    var html = '<div class="modal-header"><h3>Game Over</h3></div>';
+    html += '<div class="modal-body"><table class="table table-striped scoreboard">';
+    html += '<thead><tr><th>#</th><th>Name</th><th>Points</th>';
+    html += '<th><div class="icons cups stand1"></div></th>';
+    html += '<th><div class="icons cups stand2"></div></th>';
+    html += '<th><div class="icons cups stand3"></div></th><th>Guessed</th><th>Mean time</th>';
+    html += '</thead><tbody>';
+
+    for(var i=0;i<3;i++) {
+      if (podium[i]) {
+        html += '<tr><td><div class="icons medals rank'+(i+1)+'"></div></td>';
+        html += '<td class="name">'+podium[i].nickname+'</td>';
+        html += '<td>'+podium[i].points+'</td>';
+        html += '<td>'+podium[i].golds+'</td><td>'+podium[i].silvers+'</td>';
+        html += '<td>'+podium[i].bronzes+'</td><td>'+podium[i].guessed+'</td>';
+        var meantime = "N/A";
+        if (podium[i].guessed !== 0) {
+          meantime = podium[i].totguesstime / podium[i].guessed;
+          meantime = (meantime / 1000).toFixed(1)+' s';
+        }
+        html += '<td>'+meantime+'</td></tr>';
+      }
+    }
 
-        login.keyup(function(event) {
-            if (event.keyCode === 13) {
-                button.click();
-            }
-        });
+    html +='</tbody></table></div>';
+    html += '<div class="modal-footer align-left">A new game will start in ';
+    html += '<span></span> second/s</div>';
+    DOM.modal.append($(html));
+    DOM.modal.modal('show');
+    countDown(Date.now()+10000);
+  };
+
+  // Receive a chat message
+  var getChatMessage = function(chatmsg, from, to) {
+    if (ignoredplayers[from]) {
+      return;
+    }
+    var prefix = from;
+    var msgspan = $('<span class="message"></span>');
+    if (to) {
+      // Private Message
+      prefix = (nickname === from) ? '(To '+to+')' : '(From '+prefix+')';
+      msgspan.addClass('private');
+    }
+    var msg = prefix+': '+chatmsg;
+    msgspan.html(urlize(msg));
+    addChatEntry(msgspan);
+  };
+
+  var hideChat = function() {
+    DOM.togglechat.text('Show chat').unbind('click');
+    DOM.chatwrapper.toggle(300);
+    DOM.tracks.animate({maxHeight:'434px'}, 300);
+    DOM.togglechat.click(showChat);
+  };
+
+  // Put a player in the ignore list
+  var ignorePlayer = function(args, outcome) {
+    if (ignoredplayers[args[0]]) {
+      outcome.text('(From binb): '+args[0]+' is already ignored.');
+      return addChatEntry(outcome);
+    }
+    socket.emit('ignore', args[0], function(player) {
+      if (player) {
+        ignoredplayers[player] = true;
+        outcome.text('(From binb): '+player+' is now ignored.');
+        return addChatEntry(outcome);
+      }
+      outcome.append('player not found.');
+      addChatEntry(outcome);
+    });
+  };
+
+  // Submitted name was invalid
+  var invalidNickName = function(feedback) {
+    joinAnonymously(feedback+'<br/>Try with another one:');
+  };
+
+  // Prompt for name and send it
+  var joinAnonymously = function(msg) {
+    if (/nickname\s*\=/.test(document.cookie) && !msg) {
+      nickname = document.cookie.replace(/.*nickname\s*\=\s*([^;]*);?.*/, '$1');
+      return socket.emit('joinanonymously', nickname, roomname);
+    }
 
-        DOM.modal.modal('show');
-        DOM.modal.on('shown', function() {
-            login.focus();
-        });
-    };
+    if (DOM.modal.hasClass('in')) {
+      $('.modal-body p').html(msg);
+      return $('#login').focus();
+    }
 
-    var jplayerReady = function() {
-        socket.emit('loggedin', function(data) {
-            if (data) {
-                nickname = data;
-                subscriber = true;
-                return socket.emit('joinroom', roomname);
-            }
-            joinAnonymously();
-        });
-        if (!$.jPlayer.platform.mobile && !$.jPlayer.platform.tablet) {
-            return addVolumeControl();
-        }
-        var touchbackdrop = $('<div id="touch-backdrop">'+
-            '<button id="touch-play" class="btn btn-danger disabled">'+
-                '<i class="icon-play icon-white"></i> Wait'+
-            '</button></div>').appendTo('#cassette');
-        touchplay = $('#touch-play');
-        touchplay.click(function() {
-            if (!$(this).hasClass('btn-danger')) {
-                touchplay = null;
-                jplayer.jPlayer('play', elapsedtime);
-                touchbackdrop.remove();
-            }
-        });
-    };
+    var html = '<div class="modal-header">';
+    html += '<h3>You are joining the '+roomname+' room</h3></div>';
+    html += '<div class="modal-body"><p>'+(msg || "What's your name?")+'</p></div>';
+    html += '<div class="modal-footer relative">';
+    html += '<input id="login" maxlength="15" type="text" name="nickname" />';
+    html += '<button id="join" class="btn btn-success">';
+    html += '<i class="icon-user icon-white"></i> Join the game</button>';
+    html += '<span class="divider"><span>or</span></span>';
+    html += '<a class="btn btn-primary" href="/login?followup=/'+roomname+'">';
+    html += '<i class="icon-lock icon-white"></i> Login</a></div>';
+
+    $(html).appendTo(DOM.modal);
+    var login = $('#login');
+    var button = $('#join');
+
+    button.click(function() {
+      if ($.trim(login.val()) !== '') {
+        nickname = login.val();
+        socket.emit('joinanonymously', nickname, roomname);
+      }
+      else {
+        var txt = 'Nickname can\'t be empty.';
+        invalidNickName('<span class="label label-important">'+txt+'</span>');
+      }
+      login.val('');
+    });
 
-    // Kick a player
-    var kickPlayer = function(args, outcome) {
-        outcome.append('you are not allowed to kick a player.');
-        if (!subscriber) {
-            return addChatEntry(outcome);
-        }
-        var why = args[1] || '';
-        socket.emit('kick', args[0], why, function() {
-            addChatEntry(outcome);
-        });
-    };
+    login.keyup(function(event) {
+      if (event.keyCode === 13) {
+        button.click();
+      }
+    });
 
-    var loadTrack = function(previewUrl) {
-        jplayer.jPlayer('mute');
-        jplayer.jPlayer('setMedia', {m4a: previewUrl});
-    };
+    DOM.modal.modal('show');
+    DOM.modal.on('shown', function() {
+      login.focus();
+    });
+  };
+
+  var jplayerReady = function() {
+    socket.emit('loggedin', function(data) {
+      if (data) {
+        nickname = data;
+        subscriber = true;
+        return socket.emit('joinroom', roomname);
+      }
+      joinAnonymously();
+    });
+    if (!$.jPlayer.platform.mobile && !$.jPlayer.platform.tablet) {
+      return addVolumeControl();
+    }
+    var touchbackdrop = $('<div id="touch-backdrop">'+
+      '<button id="touch-play" class="btn btn-danger disabled">'+
+        '<i class="icon-play icon-white"></i> Wait'+
+      '</button></div>').appendTo('#cassette');
+    touchplay = $('#touch-play');
+    touchplay.click(function() {
+      if (!$(this).hasClass('btn-danger')) {
+        touchplay = null;
+        jplayer.jPlayer('play', elapsedtime);
+        touchbackdrop.remove();
+      }
+    });
+  };
 
-    /**
-     * Given a string, parse the string extracting fields separated by whitespace
-     * and optionally enclosed within double quotes (which are stripped off), and
-     * build an array of copies of the string for each field.
-     */
-
-    var parseCommand = function(input) {
-        var inquotes = false
-            , token = ''
-            , tokens = [];
-        for (var i = 0; i < input.length; i++) {
-            if (input[i] === '\\') {
-                if (++i === input.length) {
-                    throw new Error('SyntaxError: Unexpected end of input');
-                }
-                if (input[i] === '\\' || input[i] === '"' || !inquotes) {
-                    token += input[i];
-                    continue;
-                }
-                token += '\\'+input[i];
-                continue;
-            }
-            if (input[i] === '"') {
-                inquotes = !inquotes;
-                var j = i + 1;
-                if (!inquotes && (input[j] === ' ' || j === input.length)) {
-                    tokens.push(token);
-                    token = '';
-                    i = j;
-                }
-                continue;
-            }
-            if (input[i] === ' ') {
-                if (inquotes) {
-                    token += ' ';
-                }
-                else if (token.length) {
-                    tokens.push(token);
-                    token = '';
-                }
-                continue;
-            }
-            token += input[i];
-        }
+  // Kick a player
+  var kickPlayer = function(args, outcome) {
+    outcome.append('you are not allowed to kick a player.');
+    if (!subscriber) {
+      return addChatEntry(outcome);
+    }
+    var why = args[1] || '';
+    socket.emit('kick', args[0], why, function() {
+      addChatEntry(outcome);
+    });
+  };
+
+  var loadTrack = function(previewUrl) {
+    jplayer.jPlayer('mute');
+    jplayer.jPlayer('setMedia', {m4a: previewUrl});
+  };
+
+  /**
+   * Given a string, parse the string extracting fields separated by whitespace
+   * and optionally enclosed within double quotes (which are stripped off), and
+   * build an array of copies of the string for each field.
+   */
+
+  var parseCommand = function(input) {
+    var inquotes = false
+      , token = ''
+      , tokens = [];
+    for (var i = 0; i < input.length; i++) {
+      if (input[i] === '\\') {
+        if (++i === input.length) {
+          throw new Error('SyntaxError: Unexpected end of input');
+        }
+        if (input[i] === '\\' || input[i] === '"' || !inquotes) {
+          token += input[i];
+          continue;
+        }
+        token += '\\'+input[i];
+        continue;
+      }
+      if (input[i] === '"') {
+        inquotes = !inquotes;
+        var j = i + 1;
+        if (!inquotes && (input[j] === ' ' || j === input.length)) {
+          tokens.push(token);
+          token = '';
+          i = j;
+        }
+        continue;
+      }
+      if (input[i] === ' ') {
         if (inquotes) {
-            throw new Error('SyntaxError: Unexpected end of input');
-        }
-        if (token.length) {
-            tokens.push(token);
-        }
-        return tokens;
-    };
-
-    // Play a track
-    var playTrack = function(data) {
-        if (touchplay) {
-            touchplay.removeClass('btn-danger disabled').addClass('btn-success');
-            touchplay.html('<i class="icon-play icon-white"></i> Play');
-        }
-        jplayer.jPlayer('unmute');
-        jplayer.jPlayer('play');
-        updateUsers(data.users);
-        cassetteAnimation(Date.now()+30000, true);
-        if (data.counter === 1) {
-            DOM.modal.modal('hide').empty();
-            DOM.tracks.empty();
-        }
-        DOM.track.text(data.counter+'/'+data.tot);
-        addFeedback('What is this song?');
-    };
-
-    // Successfully joined the room
-    var ready = function(usersData, trackscount, loggedin) {
-        if (!loggedin && !/nickname\s*\=/.test(document.cookie)) {
-            document.cookie = 'nickname='+nickname+';path=/;';
-        }
-
-        DOM.modal.modal('hide').empty();
-        $('#total-tracks span').text(trackscount);
-        var msg = nickname+' joined the game';
-        var joinspan = $('<span class="join"></span>');
-        joinspan.text(msg);
-        addChatEntry(joinspan);
-        updateUsers(usersData);
-
-        DOM.messagebox.keydown(function(event) {
-            if (event.keyCode === 13) {
-                var val = $.trim(DOM.messagebox.val());
-                if (val !== '') {
-                    if (pvtmsgto) {
-                        socket.emit('sendchatmsg', val, pvtmsgto);
-                    }
-                    else if (/^\/[^ ]/.test(val)) {
-                        slashCommandHandler(val);
-                    }
-                    else {
-                        socket.emit('sendchatmsg', val);
-                    }
-                }
-                DOM.messagebox.val('');
-            }
-        });
-
-        DOM.guessbox.keydown(function(event) {
-            switch (event.keyCode) {
-                case 13: // return
-                    var guess = $.trim(DOM.guessbox.val());
-                    if (guess !== '') {
-                        socket.emit('guess', guess.toLowerCase());
-                        historyvalues.push(guess);
-                        if (historyvalues.length > 20) {
-                            historyvalues.splice(0, 1);
-                        }
-                        historycursor = historyvalues.length;
-                    }
-                    DOM.guessbox.val('');
-                    break;
-                case 38: // up-arrow
-                    if (historycursor > 0) {
-                        DOM.guessbox.val(historyvalues[--historycursor]);
-                    }
-                    break;
-                case 40: // down-arrow
-                    if (historycursor < historyvalues.length - 1) {
-                        DOM.guessbox.val(historyvalues[++historycursor]);
-                    }
-                    else {
-                        historycursor = historyvalues.length;
-                        DOM.guessbox.val('');
-                    }
-            }
-        });
-
-        DOM.guessbox.focus();
-
-        socket.on('artistmatched', function() {
-            addFeedback(amstrings[Math.floor(Math.random()*amstrings.length)], 'correct');
-        });
-        socket.on('bothmatched', function() {
-            addFeedback(bmstrings[Math.floor(Math.random()*bmstrings.length)], 'correct');
-        });
-        socket.on('chatmsg', getChatMessage);
-        socket.on('gameover', gameOver);
-        socket.on('loadtrack', loadTrack);
-        socket.on('newuser', userJoin);
-        socket.on('noguesstime', function() {
-            addFeedback('You have to wait the next song...');
-        });
-        socket.on('nomatch', function() {
-            addFeedback(nmstrings[Math.floor(Math.random()*nmstrings.length)], 'wrong');
-        });
-        socket.on('playtrack', playTrack);
-        socket.on('stoptrying', function() {
-            addFeedback('You guessed both artist and title. Please wait...');
-        });
-        socket.on('titlematched', function() {
-            addFeedback(tmstrings[Math.floor(Math.random()*tmstrings.length)], 'correct');
-        });
-        socket.on('trackinfo', addTrackInfo);
-        socket.on('updateusers', updateUsers);
-        socket.on('userleft', userLeft);
-        socket.emit('getstatus', setStatus);
-    };
-
-    // Show the number of players inside each room
-    var roomsOverview = function(data) {
-        for (var prop in data) {
-            if (prop !== roomname) {
-                DOM.userscounters[prop].text(data[prop]);
-            }
-        }
-    };
-
-    var setStatus = function(data) {
-        if (data.status === 0) {
-            cassetteAnimation(Date.now()+data.timeleft, true);
+          token += ' ';
         }
-        else if (data.status === 1) {
-            loadTrack(data.previewUrl);
+        else if (token.length) {
+          tokens.push(token);
+          token = '';
         }
-        addFeedback(states[data.status]);
-    };
-
-    var setVariables = function() {
-        DOM.cassettewheels = $('#cassette .wheel');
-        DOM.chat = $('#chat');
-        DOM.chatwrapper = $('#chat-outer-wrapper');
-        DOM.countdown = $('#countdown');
-        DOM.feedback = $('#feedback');
-        DOM.guessbox = $('#guess');
-        DOM.messagebox = $('#message');
-        DOM.modal = $('#modal');
-        DOM.points = $('#summary .points');
-        DOM.progress = $('#progress');
-        DOM.rank = $('#summary .rank');
-        DOM.recipient = $('#recipient');
-        DOM.tapeleft = $('#tape-left');
-        DOM.taperight = $('#tape-right');
-        DOM.togglechat = $('#toggle-chat');
-        DOM.track = $('#summary .track');
-        DOM.tracks = $('#tracks');
-        DOM.users = $('#users');
-        DOM.userscounters = {};
-        $('.users-counter').each(function() {
-            DOM.userscounters[$(this).prevAll('.room-name').text()] = $(this);
-        });
-    };
-
-    var showChat = function() {
-        DOM.togglechat.text('Hide chat').unbind('click');
-        DOM.chatwrapper.toggle(300);
-        DOM.tracks.animate({maxHeight:'240px'}, 300, function() {
-            DOM.chat[0].scrollTop = DOM.chat[0].scrollHeight;
-        });
-        DOM.togglechat.click(hideChat);
-    };
-
-    var slashCommandHandler = function(line) {
-        var args;
-        var outcome = $('<span class="message private">(From binb): </span>');
+        continue;
+      }
+      token += input[i];
+    }
+    if (inquotes) {
+      throw new Error('SyntaxError: Unexpected end of input');
+    }
+    if (token.length) {
+      tokens.push(token);
+    }
+    return tokens;
+  };
+
+  // Play a track
+  var playTrack = function(data) {
+    if (touchplay) {
+      touchplay.removeClass('btn-danger disabled').addClass('btn-success');
+      touchplay.html('<i class="icon-play icon-white"></i> Play');
+    }
+    jplayer.jPlayer('unmute');
+    jplayer.jPlayer('play');
+    updateUsers(data.users);
+    cassetteAnimation(Date.now()+30000, true);
+    if (data.counter === 1) {
+      DOM.modal.modal('hide').empty();
+      DOM.tracks.empty();
+    }
+    DOM.track.text(data.counter+'/'+data.tot);
+    addFeedback('What is this song?');
+  };
+
+  // Successfully joined the room
+  var ready = function(usersData, trackscount, loggedin) {
+    if (!loggedin && !/nickname\s*\=/.test(document.cookie)) {
+      document.cookie = 'nickname='+nickname+';path=/;';
+    }
 
-        try {
-            args = parseCommand(line);
-        }
-        catch (err) {
-            outcome.append(err.message);
-            return addChatEntry(outcome);
-        }
+    DOM.modal.modal('hide').empty();
+    $('#total-tracks span').text(trackscount);
+    var msg = nickname+' joined the game';
+    var joinspan = $('<span class="join"></span>');
+    joinspan.text(msg);
+    addChatEntry(joinspan);
+    updateUsers(usersData);
+
+    DOM.messagebox.keydown(function(event) {
+      if (event.keyCode === 13) {
+        var val = $.trim(DOM.messagebox.val());
+        if (val !== '') {
+          if (pvtmsgto) {
+            socket.emit('sendchatmsg', val, pvtmsgto);
+          }
+          else if (/^\/[^ ]/.test(val)) {
+            slashCommandHandler(val);
+          }
+          else {
+            socket.emit('sendchatmsg', val);
+          }
+        }
+        DOM.messagebox.val('');
+      }
+    });
 
-        var cmdname = args.shift();
-        var command = slashcommands[cmdname.substr(1)];
+    DOM.guessbox.keydown(function(event) {
+      switch (event.keyCode) {
+        case 13: // return
+          var guess = $.trim(DOM.guessbox.val());
+          if (guess !== '') {
+            socket.emit('guess', guess.toLowerCase());
+            historyvalues.push(guess);
+            if (historyvalues.length > 20) {
+              historyvalues.splice(0, 1);
+            }
+            historycursor = historyvalues.length;
+          }
+          DOM.guessbox.val('');
+          break;
+        case 38: // up-arrow
+          if (historycursor > 0) {
+            DOM.guessbox.val(historyvalues[--historycursor]);
+          }
+          break;
+        case 40: // down-arrow
+          if (historycursor < historyvalues.length - 1) {
+            DOM.guessbox.val(historyvalues[++historycursor]);
+          }
+          else {
+            historycursor = historyvalues.length;
+            DOM.guessbox.val('');
+          }
+      }
+    });
 
-        if (command) {
-            if (args.length < command.minargs) {
-                outcome.append(command.usage);
-                return addChatEntry(outcome);
-            }
-            if (command.checkrecipient && (!args[0] || args[0] === nickname)) {
-                outcome.append('invalid argument.');
-                return addChatEntry(outcome);
-            }
-            return command.fn(args, outcome);
-        }
+    DOM.guessbox.focus();
 
-        outcome.text('(From binb): unknown command '+cmdname+'.');
-        addChatEntry(outcome);
-    };
+    socket.on('artistmatched', function() {
+      addFeedback(amstrings[Math.floor(Math.random()*amstrings.length)], 'correct');
+    });
+    socket.on('bothmatched', function() {
+      addFeedback(bmstrings[Math.floor(Math.random()*bmstrings.length)], 'correct');
+    });
+    socket.on('chatmsg', getChatMessage);
+    socket.on('gameover', gameOver);
+    socket.on('loadtrack', loadTrack);
+    socket.on('newuser', userJoin);
+    socket.on('noguesstime', function() {
+      addFeedback('You have to wait the next song...');
+    });
+    socket.on('nomatch', function() {
+      addFeedback(nmstrings[Math.floor(Math.random()*nmstrings.length)], 'wrong');
+    });
+    socket.on('playtrack', playTrack);
+    socket.on('stoptrying', function() {
+      addFeedback('You guessed both artist and title. Please wait...');
+    });
+    socket.on('titlematched', function() {
+      addFeedback(tmstrings[Math.floor(Math.random()*tmstrings.length)], 'correct');
+    });
+    socket.on('trackinfo', addTrackInfo);
+    socket.on('updateusers', updateUsers);
+    socket.on('userleft', userLeft);
+    socket.emit('getstatus', setStatus);
+  };
+
+  // Show the number of players inside each room
+  var roomsOverview = function(data) {
+    for (var prop in data) {
+      if (prop !== roomname) {
+        DOM.userscounters[prop].text(data[prop]);
+      }
+    }
+  };
 
-    // Remove a player from the ignore list
-    var unignorePlayer = function(args, outcome) {
-        if (!ignoredplayers[args[0]]) {
-            outcome.text('(From binb): you have not ignored '+args[0]+'.');
-            return addChatEntry(outcome);
-        }
-        delete ignoredplayers[args[0]];
-        socket.emit('unignore', args[0]);
-        outcome.text('(From binb): '+args[0]+' is no longer ignored.');
-        addChatEntry(outcome);
-    };
+  var setStatus = function(data) {
+    if (data.status === 0) {
+      cassetteAnimation(Date.now()+data.timeleft, true);
+    }
+    else if (data.status === 1) {
+      loadTrack(data.previewUrl);
+    }
+    addFeedback(states[data.status]);
+  };
+
+  var setVariables = function() {
+    DOM.cassettewheels = $('#cassette .wheel');
+    DOM.chat = $('#chat');
+    DOM.chatwrapper = $('#chat-outer-wrapper');
+    DOM.countdown = $('#countdown');
+    DOM.feedback = $('#feedback');
+    DOM.guessbox = $('#guess');
+    DOM.messagebox = $('#message');
+    DOM.modal = $('#modal');
+    DOM.points = $('#summary .points');
+    DOM.progress = $('#progress');
+    DOM.rank = $('#summary .rank');
+    DOM.recipient = $('#recipient');
+    DOM.tapeleft = $('#tape-left');
+    DOM.taperight = $('#tape-right');
+    DOM.togglechat = $('#toggle-chat');
+    DOM.track = $('#summary .track');
+    DOM.tracks = $('#tracks');
+    DOM.users = $('#users');
+    DOM.userscounters = {};
+    $('.users-counter').each(function() {
+      DOM.userscounters[$(this).prevAll('.room-name').text()] = $(this);
+    });
+  };
 
-    // Update the list of players
-    var updateUsers = function(usersData) {
-        DOM.users.empty();
+  var showChat = function() {
+    DOM.togglechat.text('Hide chat').unbind('click');
+    DOM.chatwrapper.toggle(300);
+    DOM.tracks.animate({maxHeight:'240px'}, 300, function() {
+      DOM.chat[0].scrollTop = DOM.chat[0].scrollHeight;
+    });
+    DOM.togglechat.click(hideChat);
+  };
 
-        var users = [];
-        for (var key in usersData) {
-            users.push(usersData[key]);
-        }
-        users.sort(function(a, b) {return b.points - a.points;});
-
-        // Flag to test if our private recipient is in the list of active users
-        var found = false;
-        for (var i=0; i<users.length; i++) {
-            var user = users[i]
-                , li = $('<li></li>')
-                , pvt = $('<span class="private label label-info">P</span>')
-                , username = $('<span class="name"></span>').text(user.nickname)
-                , points = $('<span class="points">('+user.points+')</span>')
-                , roundrank = $('<span></span>')
-                , roundpointsel = $('<span class="round-points"></span>')
-                , guesstime = $('<span class="guess-time"></span>');
-
-            li.append(pvt, username, points, roundrank, roundpointsel, guesstime);
-            if (user.registered) {
-                var href = 'href="/user/'+user.nickname+'"';
-                pvt.after('<a class="icons registered" target="_blank" '+href+'></a>');
-            }
-            DOM.users.append(li);
+  var slashCommandHandler = function(line) {
+    var args;
+    var outcome = $('<span class="message private">(From binb): </span>');
 
-            if (pvtmsgto === user.nickname) {
-                pvt.show();
-                username.click(clearPrivate);
-                found = true;
-            }
-            else {
-                username.click(function() {
-                    addPrivate($(this).text());
-                });
-            }
+    try {
+      args = parseCommand(line);
+    }
+    catch (err) {
+      outcome.append(err.message);
+      return addChatEntry(outcome);
+    }
 
-            if (nickname === user.nickname) {
-                username.addClass('you');
-                roundpoints = user.roundpoints;
-                DOM.rank.text(i+1);
-                DOM.points.text(user.points);
-            }
+    var cmdname = args.shift();
+    var command = slashcommands[cmdname.substr(1)];
+
+    if (command) {
+      if (args.length < command.minargs) {
+        outcome.append(command.usage);
+        return addChatEntry(outcome);
+      }
+      if (command.checkrecipient && (!args[0] || args[0] === nickname)) {
+        outcome.append('invalid argument.');
+        return addChatEntry(outcome);
+      }
+      return command.fn(args, outcome);
+    }
 
-            if (user.roundpoints > 0) {
-                roundpointsel.text('+'+user.roundpoints);
-                if (user.roundpoints === 1) {
-                    username.addClass('matched');
-                }
-                else {
-                    if (user.roundpoints > 3) {
-                        var stand = 7 - user.roundpoints;
-                        roundrank.addClass('icons round-rank stand'+stand);
-                        var gtime = (user.guesstime / 1000).toFixed(1);
-                        guesstime.text(gtime+' s');
-                    }
-                    username.addClass('correct');
-                }
-            }
-        }
+    outcome.text('(From binb): unknown command '+cmdname+'.');
+    addChatEntry(outcome);
+  };
 
-        if (!found && pvtmsgto) {
-            var width = DOM.recipient.outerWidth(true) + 1;
-            DOM.recipient.css('margin-right', '0');
-            DOM.recipient.text('');
-            DOM.messagebox.animate({'width':'+='+width+'px'}, 'fast');
-            pvtmsgto = null;
-            DOM.messagebox.focus();
-        }
-    };
+  // Remove a player from the ignore list
+  var unignorePlayer = function(args, outcome) {
+    if (!ignoredplayers[args[0]]) {
+      outcome.text('(From binb): you have not ignored '+args[0]+'.');
+      return addChatEntry(outcome);
+    }
+    delete ignoredplayers[args[0]];
+    socket.emit('unignore', args[0]);
+    outcome.text('(From binb): '+args[0]+' is no longer ignored.');
+    addChatEntry(outcome);
+  };
+
+  // Update the list of players
+  var updateUsers = function(usersData) {
+    DOM.users.empty();
+
+    var users = [];
+    for (var key in usersData) {
+      users.push(usersData[key]);
+    }
+    users.sort(function(a, b) {return b.points - a.points;});
+
+    // Flag to test if our private recipient is in the list of active users
+    var found = false;
+    for (var i=0; i<users.length; i++) {
+      var user = users[i]
+        , li = $('<li></li>')
+        , pvt = $('<span class="private label label-info">P</span>')
+        , username = $('<span class="name"></span>').text(user.nickname)
+        , points = $('<span class="points">('+user.points+')</span>')
+        , roundrank = $('<span></span>')
+        , roundpointsel = $('<span class="round-points"></span>')
+        , guesstime = $('<span class="guess-time"></span>');
+
+      li.append(pvt, username, points, roundrank, roundpointsel, guesstime);
+      if (user.registered) {
+        var href = 'href="/user/'+user.nickname+'"';
+        pvt.after('<a class="icons registered" target="_blank" '+href+'></a>');
+      }
+      DOM.users.append(li);
+
+      if (pvtmsgto === user.nickname) {
+        pvt.show();
+        username.click(clearPrivate);
+        found = true;
+      }
+      else {
+        username.click(function() {
+          addPrivate($(this).text());
+        });
+      }
 
-    var updateRoomsOverview = function(room, players) {
-        if (room !== roomname) {
-            DOM.userscounters[room].text(players);
-        }
-    };
+      if (nickname === user.nickname) {
+        username.addClass('you');
+        roundpoints = user.roundpoints;
+        DOM.rank.text(i+1);
+        DOM.points.text(user.points);
+      }
 
-    // Convert any URLs in text into clickable links
-    var urlize = function(text) {
-        if (urlregex.test(text)) {
-            var html = '';
-            var splits = text.split(urlregex);
-            for (var i=0; i<splits.length; i++) {
-                var escapedsplit = splits[i].encodeEntities();
-                if (urlregex.test(splits[i])) {
-                    html += '<a target="_blank" href="'+escapedsplit+'">'+escapedsplit+'</a>';
-                    continue;
-                }
-                html += escapedsplit;
-            }
-            return html;
+      if (user.roundpoints > 0) {
+        roundpointsel.text('+'+user.roundpoints);
+        if (user.roundpoints === 1) {
+          username.addClass('matched');
         }
-        return text.encodeEntities();
-    };
-
-    // A new player has joined the game
-    var userJoin = function(username, usersData) {
-        var joinmsg = username+' joined the game';
-        var joinspan = $('<span class="join"></span>');
-        joinspan.text(joinmsg);
-        addChatEntry(joinspan);
-        updateUsers(usersData);
-    };
-
-    // A player has left the game
-    var userLeft = function(username, usersData) {
-        var leftmsg = username+' left the game';
-        var leftspan = $('<span class="left"></span>');
-        leftspan.text(leftmsg);
-        addChatEntry(leftspan);
-        updateUsers(usersData);
-    };
+        else {
+          if (user.roundpoints > 3) {
+            var stand = 7 - user.roundpoints;
+            roundrank.addClass('icons round-rank stand'+stand);
+            var gtime = (user.guesstime / 1000).toFixed(1);
+            guesstime.text(gtime+' s');
+          }
+          username.addClass('correct');
+        }
+      }
+    }
 
-    var slashcommands = {
-        ignore: {
-            checkrecipient: true, // Assume that the first argument (argv[0]) is the recipient
-            fn: ignorePlayer,
-            minargs: 1,
-            usage: 'usage: /ignore &lt;player name&gt;'
-        },
-        kick: {
-            checkrecipient: true,
-            fn: kickPlayer,
-            minargs: 1,
-            usage: 'usage: /kick &lt;player name&gt; [message]'
-        },
-        unignore: {
-            checkrecipient: true,
-            fn: unignorePlayer,
-            minargs: 1,
-            usage: 'usage: /unignore &lt;player name&gt;'
-        }
-    };
+    if (!found && pvtmsgto) {
+      var width = DOM.recipient.outerWidth(true) + 1;
+      DOM.recipient.css('margin-right', '0');
+      DOM.recipient.text('');
+      DOM.messagebox.animate({'width':'+='+width+'px'}, 'fast');
+      pvtmsgto = null;
+      DOM.messagebox.focus();
+    }
+  };
 
-    // Set up the app
-    setVariables();
-    DOM.modal.modal({keyboard:false, show:false, backdrop:'static'});
-    DOM.togglechat.click(hideChat);
-    if ($.browser.mozilla) {
-        // Block ESC button in firefox (breaks socket connections)
-        $(document).keypress(function(event) {
-            if(event.keyCode === 27) {
-                return false;
-            }
-        });
+  var updateRoomsOverview = function(room, players) {
+    if (room !== roomname) {
+      DOM.userscounters[room].text(players);
     }
-    socket = io.connect(uri, {'reconnect':false});
-    socket.on('connect', function() {
-        jplayer = $('#player').jPlayer({
-            ready: jplayerReady,
-            swfPath: '/static/swf/',
-            supplied: 'm4a',
-            preload: 'auto',
-            volume: 1
-        });
-        socket.on('alreadyinaroom', alreadyInARoom);
-        socket.on('disconnect', disconnect);
-        socket.on('invalidnickname', invalidNickName);
-        socket.on('ready', ready);
-        socket.on('updateoverview', updateRoomsOverview);
-        socket.emit('getoverview', roomsOverview);
+  };
+
+  // Convert any URLs in text into clickable links
+  var urlize = function(text) {
+    if (urlregex.test(text)) {
+      var html = '';
+      var splits = text.split(urlregex);
+      for (var i=0; i<splits.length; i++) {
+        var escapedsplit = splits[i].encodeEntities();
+        if (urlregex.test(splits[i])) {
+          html += '<a target="_blank" href="'+escapedsplit+'">'+escapedsplit+'</a>';
+          continue;
+        }
+        html += escapedsplit;
+      }
+      return html;
+    }
+    return text.encodeEntities();
+  };
+
+  // A new player has joined the game
+  var userJoin = function(username, usersData) {
+    var joinmsg = username+' joined the game';
+    var joinspan = $('<span class="join"></span>');
+    joinspan.text(joinmsg);
+    addChatEntry(joinspan);
+    updateUsers(usersData);
+  };
+
+  // A player has left the game
+  var userLeft = function(username, usersData) {
+    var leftmsg = username+' left the game';
+    var leftspan = $('<span class="left"></span>');
+    leftspan.text(leftmsg);
+    addChatEntry(leftspan);
+    updateUsers(usersData);
+  };
+
+  var slashcommands = {
+    ignore: {
+      checkrecipient: true, // Assume that the first argument (argv[0]) is the recipient
+      fn: ignorePlayer,
+      minargs: 1,
+      usage: 'usage: /ignore &lt;player name&gt;'
+    },
+    kick: {
+      checkrecipient: true,
+      fn: kickPlayer,
+      minargs: 1,
+      usage: 'usage: /kick &lt;player name&gt; [message]'
+    },
+    unignore: {
+      checkrecipient: true,
+      fn: unignorePlayer,
+      minargs: 1,
+      usage: 'usage: /unignore &lt;player name&gt;'
+    }
+  };
+
+  // Set up the app
+  setVariables();
+  DOM.modal.modal({keyboard:false, show:false, backdrop:'static'});
+  DOM.togglechat.click(hideChat);
+  if ($.browser.mozilla) {
+    // Block ESC button in firefox (breaks socket connections)
+    $(document).keypress(function(event) {
+      if(event.keyCode === 27) {
+        return false;
+      }
+    });
+  }
+  socket = io.connect(uri, {'reconnect':false});
+  socket.on('connect', function() {
+    jplayer = $('#player').jPlayer({
+      ready: jplayerReady,
+      swfPath: '/static/swf/',
+      supplied: 'm4a',
+      preload: 'auto',
+      volume: 1
     });
+    socket.on('alreadyinaroom', alreadyInARoom);
+    socket.on('disconnect', disconnect);
+    socket.on('invalidnickname', invalidNickName);
+    socket.on('ready', ready);
+    socket.on('updateoverview', updateRoomsOverview);
+    socket.emit('getoverview', roomsOverview);
+  });
 
 })();
index 5c0ec62ea0332b2fed57cc35fc2a3e6eeb496e7e..e1c9083f394839f0b3446728077bc11bbe9690f6 100644 (file)
@@ -1,31 +1,31 @@
 (function() {
-    if ($.browser.mozilla) {
-        // Block ESC button in firefox (breaks socket connections).
-        $(document).keypress(function(event) {
-            if(event.keyCode === 27) {
-                return false;
-            }
-        });
-    }
-    $.get('/artworks', function(data) {
-        $('.thumbnail').each(function(index) {
-            var i = index * 6;
-            var j = i + 6;
-            for(i; i < j; i++) {
-                $('<img src="'+data.results[i]+'" />').appendTo($(this));
-            }
-        });
+  if ($.browser.mozilla) {
+    // Block ESC button in firefox (breaks socket connections).
+    $(document).keypress(function(event) {
+      if(event.keyCode === 27) {
+        return false;
+      }
     });
-    var uri = window.location.protocol+'//'+window.location.host;
-    var socket = io.connect(uri, {'reconnect':false});
-    socket.on('connect', function() {
-        socket.emit('getoverview', function(data) {
-            for (var prop in data) {
-                $('#'+prop).text(data[prop]);
-            }
-        });
-        socket.on('updateoverview', function(room, players) {
-            $('#'+room).text(players);
-        });
+  }
+  $.get('/artworks', function(data) {
+    $('.thumbnail').each(function(index) {
+      var i = index * 6;
+      var j = i + 6;
+      for(i; i < j; i++) {
+        $('<img src="'+data.results[i]+'" />').appendTo($(this));
+      }
     });
+  });
+  var uri = window.location.protocol+'//'+window.location.host;
+  var socket = io.connect(uri, {'reconnect':false});
+  socket.on('connect', function() {
+    socket.emit('getoverview', function(data) {
+      for (var prop in data) {
+        $('#'+prop).text(data[prop]);
+      }
+    });
+    socket.on('updateoverview', function(room, players) {
+      $('#'+room).text(players);
+    });
+  });
 })();
index 81d84b67275c9f46ce297422f66e3ea54469c627..eb4e9c220a8d621dd534227ec5dc268adfb2b0f7 100644 (file)
@@ -1,33 +1,33 @@
 (function() {
-    var appendResults = function(data, leaderboard, offset, type) {
-        for (var i=0; i<data.length; i+=2) {
-            var link = $('<a href="/user/'+data[i]+'"></a>').text(data[i])
-                , col1 = '<td>'+(++offset)+'</td>'
-                , col2 = $('<td></td>').append(link)
-                , col3 = (type === 'points')
-                    ? '<td>'+data[i+1]+'</td>'
-                    : '<td><i class="icon-time"></i> '+(data[i+1] / 1000).toFixed(2)+' sec</td>';
-            var row = $('<tr></tr>').append(col1, col2, col3);
-            leaderboard.append(row);
-        }
-    };
+  var appendResults = function(data, leaderboard, offset, type) {
+    for (var i=0; i<data.length; i+=2) {
+      var link = $('<a href="/user/'+data[i]+'"></a>').text(data[i])
+        , col1 = '<td>'+(++offset)+'</td>'
+        , col2 = $('<td></td>').append(link)
+        , col3 = (type === 'points')
+          ? '<td>'+data[i+1]+'</td>'
+          : '<td><i class="icon-time"></i> '+(data[i+1] / 1000).toFixed(2)+' sec</td>';
+      var row = $('<tr></tr>').append(col1, col2, col3);
+      leaderboard.append(row);
+    }
+  };
 
-    $('.leaderboard-wrapper').each(function(index) {
-        var leaderboard = $(this).find('tbody')
-            , loading = $(this).find('.loading')
-            , offset = 0
-            , type = (index === 0) ? 'points' : 'times';
+  $('.leaderboard-wrapper').each(function(index) {
+    var leaderboard = $(this).find('tbody')
+      , loading = $(this).find('.loading')
+      , offset = 0
+      , type = (index === 0) ? 'points' : 'times';
 
-        $(this).scroll(function() {
-            var diff = $(this).prop('scrollHeight') - $(this).scrollTop();
-            if (diff === $(this).height() && offset < 180) {
-                offset += 30;
-                loading.show();
-                $.get('/sliceleaderboard', {begin: offset, by: type}, function(data) {
-                    loading.hide();
-                    appendResults(data, leaderboard, offset, type);
-                });
-            }
+    $(this).scroll(function() {
+      var diff = $(this).prop('scrollHeight') - $(this).scrollTop();
+      if (diff === $(this).height() && offset < 180) {
+        offset += 30;
+        loading.show();
+        $.get('/sliceleaderboard', {begin: offset, by: type}, function(data) {
+          loading.hide();
+          appendResults(data, leaderboard, offset, type);
         });
+      }
     });
+  });
 })();
index d690964a6065e885fe4a65de0f48776c5c40f202..6e913590653e7177e2677f9cdba7ca94eda357d2 100644 (file)
@@ -3,21 +3,21 @@
  */
 
 var async = require('async')
-    , Captcha = require('../lib/captcha')
-    , db = require('../lib/redis-clients').songs
-    , randomSlogan = require('../lib/utils').randomSlogan
-    , rooms = require('../config').rooms;
+  , Captcha = require('../lib/captcha')
+  , db = require('../lib/redis-clients').songs
+  , randomSlogan = require('../lib/utils').randomSlogan
+  , rooms = require('../config').rooms;
 
 /**
  * Generate a task.
  */
 
 var task = function(genre) {
-    return function(callback) {
-        db.srandmember(genre, function(err, res) {
-            db.hget('song:'+res, 'artworkUrl100', callback);
-        });
-    };
+  return function(callback) {
+    db.srandmember(genre, function(err, res) {
+      db.hget('song:'+res, 'artworkUrl100', callback);
+    });
+  };
 };
 
 /**
@@ -25,84 +25,84 @@ var task = function(genre) {
  */
 
 exports.artworks = function(req, res) {
-    var tasks = [];
-    for (var i=0; i<rooms.length; i++) {
-        for (var j=0; j<6; j++) {
-            tasks.push(task(rooms[i]));
-        }
+  var tasks = [];
+  for (var i=0; i<rooms.length; i++) {
+    for (var j=0; j<6; j++) {
+      tasks.push(task(rooms[i]));
     }
-    async.parallel(tasks, function(err, results) {
-        var obj = {
-            resultCount: results.length,
-            results: results
-        };
-        res.json(obj);
-    });
+  }
+  async.parallel(tasks, function(err, results) {
+    var obj = {
+      resultCount: results.length,
+      results: results
+    };
+    res.json(obj);
+  });
 };
 
 exports.changePasswd = function(req, res) {
-    if (!req.session.user) {
-        return res.redirect('/login?followup=/changepasswd');
-    }
-    res.render('changepasswd', {
-        followup: req.query.followup || '/',
-        loggedin: req.session.user,
-        slogan: randomSlogan()
-    });
+  if (!req.session.user) {
+    return res.redirect('/login?followup=/changepasswd');
+  }
+  res.render('changepasswd', {
+    followup: req.query.followup || '/',
+    loggedin: req.session.user,
+    slogan: randomSlogan()
+  });
 };
 
 exports.home = function(req, res) {
-    res.render('home', {
-        loggedin: req.session.user,
-        rooms: rooms,
-        slogan: randomSlogan()
-    });
+  res.render('home', {
+    loggedin: req.session.user,
+    rooms: rooms,
+    slogan: randomSlogan()
+  });
 };
 
 exports.login = function(req, res) {
-    res.render('login', {
-        followup: req.query.followup || '/',
-        slogan: randomSlogan()
-    });
+  res.render('login', {
+    followup: req.query.followup || '/',
+    slogan: randomSlogan()
+  });
 };
 
 exports.recoverPasswd = function(req, res) {
-    var captcha = new Captcha();
-    req.session.captchacode = captcha.getCode();
-    res.render('recoverpasswd', {
-        captchaurl: captcha.toDataURL(),
-        followup: req.query.followup || '/',
-        slogan: randomSlogan()
-    });
+  var captcha = new Captcha();
+  req.session.captchacode = captcha.getCode();
+  res.render('recoverpasswd', {
+    captchaurl: captcha.toDataURL(),
+    followup: req.query.followup || '/',
+    slogan: randomSlogan()
+  });
 };
 
 exports.resetPasswd = function(req, res) {
-    res.render('resetpasswd', {
-        slogan: randomSlogan(),
-        token: req.query.token || ''
-    });
+  res.render('resetpasswd', {
+    slogan: randomSlogan(),
+    token: req.query.token || ''
+  });
 };
 
 exports.room = function(req, res) {
-    if (~rooms.indexOf(req.params.room)) {
-        return res.render('room', {
-            loggedin: req.session.user,
-            roomname: req.params.room,
-            rooms: rooms,
-            slogan: randomSlogan()
-        });
-    }
-    res.send(404);
+  if (~rooms.indexOf(req.params.room)) {
+    return res.render('room', {
+      loggedin: req.session.user,
+      roomname: req.params.room,
+      rooms: rooms,
+      slogan: randomSlogan()
+    });
+  }
+  res.send(404);
 };
 
 exports.signup = function(req, res) {
-    var captcha = new Captcha();
-    req.session.captchacode = captcha.getCode();
-    res.render('signup', {
-        captchaurl: captcha.toDataURL(),
-        followup: req.query.followup || '/',
-        slogan: randomSlogan()
-    });
+  var captcha = new Captcha();
+  req.session.captchacode = captcha.getCode();
+  res.render('signup', {
+    captchaurl: captcha.toDataURL(),
+    followup: req.query.followup || '/',
+    slogan: randomSlogan()
+  });
 };
 
 /**
@@ -110,9 +110,9 @@ exports.signup = function(req, res) {
  */
 
 exports.validationErrors = function(req, res, next) {
-    res.locals.errors = req.session.errors;
-    res.locals.oldvalues = req.session.oldvalues;
-    delete req.session.errors;
-    delete req.session.oldvalues;
-    next();
+  res.locals.errors = req.session.errors;
+  res.locals.oldvalues = req.session.oldvalues;
+  delete req.session.errors;
+  delete req.session.oldvalues;
+  next();
 };
index c30ec04b71f5f1d34b793e508c369b93615515eb..4e8812b3776f94678a606765267596a6fe0c3b19 100644 (file)
@@ -3,11 +3,11 @@
  */
 
 var crypto = require('crypto')
-    , db = require('../lib/redis-clients').users
-    , mailer = require('../lib/email/mailer')
-    , rooms = require('../config').rooms
-    , User = require('../lib/user')
-    , utils = require('../lib/utils');
+  , db = require('../lib/redis-clients').users
+  , mailer = require('../lib/email/mailer')
+  , rooms = require('../config').rooms
+  , User = require('../lib/user')
+  , utils = require('../lib/utils');
 
 /**
  * Populate the whitelist of follow-up URLs.
@@ -15,7 +15,7 @@ var crypto = require('crypto')
 
 var safeurls = ['/', '/changepasswd'];
 for (var i=0; i<rooms.length; i++) {
-    safeurls.push('/'+rooms[i]);
+  safeurls.push('/'+rooms[i]);
 }
 
 /**
@@ -23,13 +23,13 @@ for (var i=0; i<rooms.length; i++) {
  */
 
 exports.leaderboards = function(req, res) {
-    db.zrevrange('users', 0, 29, 'withscores', function(err, pointsresults) {
-        db.sort(utils.sortParams(0), function(e, timesresults) {
-            var leaderboards = utils.buildLeaderboards(pointsresults, timesresults);
-            res.locals.slogan = utils.randomSlogan();
-            res.render('leaderboards', leaderboards);
-        });
+  db.zrevrange('users', 0, 29, 'withscores', function(err, pointsresults) {
+    db.sort(utils.sortParams(0), function(e, timesresults) {
+      var leaderboards = utils.buildLeaderboards(pointsresults, timesresults);
+      res.locals.slogan = utils.randomSlogan();
+      res.render('leaderboards', leaderboards);
     });
+  });
 };
 
 /**
@@ -37,21 +37,21 @@ exports.leaderboards = function(req, res) {
  */
 
 exports.sliceLeaderboard = function(req, res) {
-    var begin = parseInt(req.query.begin, 10)
-        , by = req.query.by;
-    if (isNaN(begin) || begin > 180 || (by !== 'points' && by !== 'times')) {
-        return res.send(412);
-    }
-    var end = begin + 29;
-    if (by === 'points') {
-        db.zrevrange('users', begin, end, 'withscores', function(err, results) {
-            res.json(results);
-        });
-        return;
-    }
-    db.sort(utils.sortParams(begin), function(err, results) {
-        res.json(results);
+  var begin = parseInt(req.query.begin, 10)
+    , by = req.query.by;
+  if (isNaN(begin) || begin > 180 || (by !== 'points' && by !== 'times')) {
+    return res.send(412);
+  }
+  var end = begin + 29;
+  if (by === 'points') {
+    db.zrevrange('users', begin, end, 'withscores', function(err, results) {
+      res.json(results);
     });
+    return;
+  }
+  db.sort(utils.sortParams(begin), function(err, results) {
+    res.json(results);
+  });
 };
 
 /**
@@ -59,57 +59,57 @@ exports.sliceLeaderboard = function(req, res) {
  */
  
 exports.validateChangePasswd = function(req, res, next) {
-    if (!req.session.user || req.body.oldpassword === undefined ||
-        req.body.newpassword === undefined) {
-        return res.send(412);
-    }
-    
-    var errors = {};
-
-    if (req.body.oldpassword.trim() === '') {
-        errors.oldpassword = "can't be empty";
-    }
-    if (req.body.newpassword.length < 6) {
-        errors.newpassword = 'must be at least 6 characters long';
-    }
-    else if(req.body.newpassword === req.body.oldpassword) {
-        errors.newpassword = "can't be changed to the old one";
-    }
-    
-    if (errors.oldpassword || errors.newpassword) {
-        req.session.errors = errors;
-        return res.redirect(req.url);
-    }
-    
-    next();
+  if (!req.session.user || req.body.oldpassword === undefined ||
+    req.body.newpassword === undefined) {
+    return res.send(412);
+  }
+  
+  var errors = {};
+
+  if (req.body.oldpassword.trim() === '') {
+    errors.oldpassword = "can't be empty";
+  }
+  if (req.body.newpassword.length < 6) {
+    errors.newpassword = 'must be at least 6 characters long';
+  }
+  else if(req.body.newpassword === req.body.oldpassword) {
+    errors.newpassword = "can't be changed to the old one";
+  }
+  
+  if (errors.oldpassword || errors.newpassword) {
+    req.session.errors = errors;
+    return res.redirect(req.url);
+  }
+  
+  next();
 };
 
 exports.checkOldPasswd = function(req, res, next) {
-    var key = 'user:'+req.session.user;
-    db.hmget(key, 'salt', 'password', function(err, data) {
-        var hash = crypto.createHash('sha256').update(data[0]+req.body.oldpassword).digest('hex');
-        if (hash !== data[1]) {
-            req.session.errors = {oldpassword: 'is incorrect'};
-            return res.redirect(req.url);
-        }
-        next();
-    });
+  var key = 'user:'+req.session.user;
+  db.hmget(key, 'salt', 'password', function(err, data) {
+    var hash = crypto.createHash('sha256').update(data[0]+req.body.oldpassword).digest('hex');
+    if (hash !== data[1]) {
+      req.session.errors = {oldpassword: 'is incorrect'};
+      return res.redirect(req.url);
+    }
+    next();
+  });
 };
 
 exports.changePasswd = function(req, res) {
-    var followup = ~safeurls.indexOf(req.query.followup) ? req.query.followup : '/'
-        , user = req.session.user
-        , key = 'user:'+user
-        , salt = crypto.randomBytes(6).toString('base64')
-        , password = crypto.createHash('sha256').update(salt+req.body.newpassword).digest('hex');
-    db.hmset(key, 'salt', salt, 'password', password, function(err, data) {
-        // Regenerate the session
-        req.session.regenerate(function() {
-            req.session.cookie.maxAge = 604800000; // One week
-            req.session.user = user;
-            res.redirect(followup);
-        });
+  var followup = ~safeurls.indexOf(req.query.followup) ? req.query.followup : '/'
+    , user = req.session.user
+    , key = 'user:'+user
+    , salt = crypto.randomBytes(6).toString('base64')
+    , password = crypto.createHash('sha256').update(salt+req.body.newpassword).digest('hex');
+  db.hmset(key, 'salt', salt, 'password', password, function(err, data) {
+    // Regenerate the session
+    req.session.regenerate(function() {
+      req.session.cookie.maxAge = 604800000; // One week
+      req.session.user = user;
+      res.redirect(followup);
     });
+  });
 };
 
 /**
@@ -117,56 +117,56 @@ exports.changePasswd = function(req, res) {
  */
 
 exports.validateLogin = function(req, res, next) {
-    if (req.body.username === undefined || req.body.password === undefined) {
-        return res.send(412);
-    }
-
-    var errors = {};
-
-    if (req.body.username.trim() === '') {
-        errors.username = "can't be empty";
-    }
-    if (req.body.password.trim() === '') {
-        errors.password = "can't be empty";
-    }
-    
-    req.session.oldvalues = {username: req.body.username};
-    if (errors.username || errors.password) {
-        req.session.errors = errors;
-        return res.redirect(req.url);
-    }
-    next();
+  if (req.body.username === undefined || req.body.password === undefined) {
+    return res.send(412);
+  }
+
+  var errors = {};
+
+  if (req.body.username.trim() === '') {
+    errors.username = "can't be empty";
+  }
+  if (req.body.password.trim() === '') {
+    errors.password = "can't be empty";
+  }
+  
+  req.session.oldvalues = {username: req.body.username};
+  if (errors.username || errors.password) {
+    req.session.errors = errors;
+    return res.redirect(req.url);
+  }
+  next();
 };
 
 exports.checkUser = function(req, res, next) {
-    var key = 'user:'+req.body.username;
-    db.exists(key, function(err, data) {
-        if (data === 1) {
-            // User exists, proceed with authentication
-            return next();
-        }
-        req.session.errors = {alert: 'The username you specified does not exists.'};
-        res.redirect(req.url);
-    });
+  var key = 'user:'+req.body.username;
+  db.exists(key, function(err, data) {
+    if (data === 1) {
+      // User exists, proceed with authentication
+      return next();
+    }
+    req.session.errors = {alert: 'The username you specified does not exists.'};
+    res.redirect(req.url);
+  });
 };
 
 exports.authenticate = function(req, res) {
-    var key = 'user:'+req.body.username;
-    db.hmget(key, 'salt', 'password', function(err, data) {
-        var hash = crypto.createHash('sha256').update(data[0]+req.body.password).digest('hex');
-        if (hash === data[1]) {
-            var followup = ~safeurls.indexOf(req.query.followup) ? req.query.followup : '/';
-            // Authentication succeeded, regenerate the session
-            req.session.regenerate(function() {
-                req.session.cookie.maxAge = 604800000; // One week
-                req.session.user = req.body.username;
-                res.redirect(followup);
-            });
-            return;
-        }
-        req.session.errors = {alert: 'The password you specified is not correct.'};
-        res.redirect(req.url);
-    });
+  var key = 'user:'+req.body.username;
+  db.hmget(key, 'salt', 'password', function(err, data) {
+    var hash = crypto.createHash('sha256').update(data[0]+req.body.password).digest('hex');
+    if (hash === data[1]) {
+      var followup = ~safeurls.indexOf(req.query.followup) ? req.query.followup : '/';
+      // Authentication succeeded, regenerate the session
+      req.session.regenerate(function() {
+        req.session.cookie.maxAge = 604800000; // One week
+        req.session.user = req.body.username;
+        res.redirect(followup);
+      });
+      return;
+    }
+    req.session.errors = {alert: 'The password you specified is not correct.'};
+    res.redirect(req.url);
+  });
 };
 
 /**
@@ -174,10 +174,10 @@ exports.authenticate = function(req, res) {
  */
 
 exports.logout = function(req, res) {
-    // Destroy the session
-    req.session.destroy(function() {
-        res.redirect('/');
-    });
+  // Destroy the session
+  req.session.destroy(function() {
+    res.redirect('/');
+  });
 };
 
 /**
@@ -185,97 +185,97 @@ exports.logout = function(req, res) {
  */
 
 exports.validateSignUp = function(req, res, next) {
-    if (req.body.username === undefined || req.body.email === undefined ||
-        req.body.password === undefined || req.body.captcha === undefined) {
-        return res.send(412);
-    }
-
-    var errors = {};
-
-    if (req.body.username === 'binb') {
-        errors.username = 'is reserved';
-    }
-    else if (!utils.isUsername(req.body.username)) {
-        errors.username = 'must contain only alphanumeric characters';
-    }
-    if (!utils.isEmail(req.body.email)) {
-        errors.email = 'is not an email address';
-    }
-    if (req.body.password.length < 6) {
-        errors.password = 'must be at least 6 characters long';
-    }
-    if (req.body.captcha !== req.session.captchacode) {
-        errors.captcha = 'no match';
-    }
-    
-    // Save old values to repopulate the fields in case of future errors
-    req.session.oldvalues = {
-        username: req.body.username,
-        email: req.body.email
-    };
-    
-    if (errors.username || errors.email || errors.password || errors.captcha) {
-        req.session.errors = errors;
-        return res.redirect(req.url);
-    }
-    
-    next();
+  if (req.body.username === undefined || req.body.email === undefined ||
+    req.body.password === undefined || req.body.captcha === undefined) {
+    return res.send(412);
+  }
+
+  var errors = {};
+
+  if (req.body.username === 'binb') {
+    errors.username = 'is reserved';
+  }
+  else if (!utils.isUsername(req.body.username)) {
+    errors.username = 'must contain only alphanumeric characters';
+  }
+  if (!utils.isEmail(req.body.email)) {
+    errors.email = 'is not an email address';
+  }
+  if (req.body.password.length < 6) {
+    errors.password = 'must be at least 6 characters long';
+  }
+  if (req.body.captcha !== req.session.captchacode) {
+    errors.captcha = 'no match';
+  }
+  
+  // Save old values to repopulate the fields in case of future errors
+  req.session.oldvalues = {
+    username: req.body.username,
+    email: req.body.email
+  };
+  
+  if (errors.username || errors.email || errors.password || errors.captcha) {
+    req.session.errors = errors;
+    return res.redirect(req.url);
+  }
+  
+  next();
 };
 
 exports.userExists = function(req, res, next) {
-    var key = 'user:'+req.body.username;
-    db.exists(key, function(err, data) {
-        if (data === 1) {
-            // User already exists
-            req.session.errors = {alert: 'A user with that name already exists.'};
-            return res.redirect(req.url);
-        }
-        next();
-    });
+  var key = 'user:'+req.body.username;
+  db.exists(key, function(err, data) {
+    if (data === 1) {
+      // User already exists
+      req.session.errors = {alert: 'A user with that name already exists.'};
+      return res.redirect(req.url);
+    }
+    next();
+  });
 };
 
 exports.emailExists = function(req, res, next) {
-    var key = 'email:'+req.body.email;
-    db.exists(key, function(err, data) {
-        if (data === 1) {
-            // Email already exists
-            req.session.errors = {alert: 'A user with that email already exists.'};
-            return res.redirect(req.url);
-        }
-        next();
-    });
+  var key = 'email:'+req.body.email;
+  db.exists(key, function(err, data) {
+    if (data === 1) {
+      // Email already exists
+      req.session.errors = {alert: 'A user with that email already exists.'};
+      return res.redirect(req.url);
+    }
+    next();
+  });
 };
 
 exports.createAccount = function(req, res) {
-    var userkey = 'user:'+req.body.username
-        , mailkey = 'email:'+req.body.email
-        , salt = crypto.randomBytes(6).toString('base64')
-        , hash = crypto.createHash('sha256').update(salt+req.body.password).digest('hex')
-        , date = new Date()
-        , day = date.getDate()
-        , month = date.getMonth() + 1
-        , year = date.getFullYear();
-
-    if (day < 10) {
-        day = '0' + day;
-    }
-    if (month < 10) {
-        month = '0' + month;
-    }
-    var joindate = day+'/'+month+'/'+year;
-    var user = new User(req.body.username, req.body.email, salt, hash, joindate);
-    // Add new user in the db
-    db.hmset(userkey, user);
-    db.set(mailkey, userkey);
-    db.zadd('users', 0, req.body.username);
-    db.sadd('emails', req.body.email);
-    // Delete old fields values
-    delete req.session.oldvalues;
-    res.render('login', {
-        followup: req.query.followup || '/',
-        slogan: utils.randomSlogan(),
-        success: 'You successfully created your account. You are now ready to login.'
-    });
+  var userkey = 'user:'+req.body.username
+    , mailkey = 'email:'+req.body.email
+    , salt = crypto.randomBytes(6).toString('base64')
+    , hash = crypto.createHash('sha256').update(salt+req.body.password).digest('hex')
+    , date = new Date()
+    , day = date.getDate()
+    , month = date.getMonth() + 1
+    , year = date.getFullYear();
+
+  if (day < 10) {
+    day = '0' + day;
+  }
+  if (month < 10) {
+    month = '0' + month;
+  }
+  var joindate = day+'/'+month+'/'+year;
+  var user = new User(req.body.username, req.body.email, salt, hash, joindate);
+  // Add new user in the db
+  db.hmset(userkey, user);
+  db.set(mailkey, userkey);
+  db.zadd('users', 0, req.body.username);
+  db.sadd('emails', req.body.email);
+  // Delete old fields values
+  delete req.session.oldvalues;
+  res.render('login', {
+    followup: req.query.followup || '/',
+    slogan: utils.randomSlogan(),
+    success: 'You successfully created your account. You are now ready to login.'
+  });
 };
 
 /**
@@ -283,54 +283,54 @@ exports.createAccount = function(req, res) {
  */
  
 exports.validateRecoverPasswd = function(req, res, next) {
-    if (req.body.email === undefined || req.body.captcha === undefined) {
-        return res.send(412);
-    }
-
-    var errors = {};
-    
-    if (!utils.isEmail(req.body.email)) {
-        errors.email = 'is not an email address';
-    }
-    if (req.body.captcha !== req.session.captchacode) {
-        errors.captcha = 'no match';
-    }
-    
-    req.session.oldvalues = {email: req.body.email};
-    
-    if (errors.email || errors.captcha) {
-        req.session.errors = errors;
-        return res.redirect(req.url);
-    }
-    
-    next();
+  if (req.body.email === undefined || req.body.captcha === undefined) {
+    return res.send(412);
+  }
+
+  var errors = {};
+  
+  if (!utils.isEmail(req.body.email)) {
+    errors.email = 'is not an email address';
+  }
+  if (req.body.captcha !== req.session.captchacode) {
+    errors.captcha = 'no match';
+  }
+  
+  req.session.oldvalues = {email: req.body.email};
+  
+  if (errors.email || errors.captcha) {
+    req.session.errors = errors;
+    return res.redirect(req.url);
+  }
+  
+  next();
 };
 
 exports.sendEmail = function(req, res) {
-    var key = 'email:'+req.body.email;
-    db.get(key, function(err, data) {
-        if (data !== null) {
-            // Email exists, generate a secure random token
-            delete req.session.captchacode;
-            var token = crypto.randomBytes(48).toString('hex');
-            // Token expires after 4 hours
-            db.setex('token:'+token, 14400, data, function(err, reply) {
-                mailer.sendEmail(req.body.email, token, function(err, response) {
-                    if (err) {
-                        console.log('error sending email: '+err.message);
-                    }
-                });
-            });
-            delete req.session.oldvalues;
-            return res.render('recoverpasswd', {
-                followup: req.query.followup || '/',
-                slogan: utils.randomSlogan(),
-                success: true
-            });
-        }
-        req.session.errors = {alert: 'The email address you specified could not be found'};
-        res.redirect(req.url);
-    });
+  var key = 'email:'+req.body.email;
+  db.get(key, function(err, data) {
+    if (data !== null) {
+      // Email exists, generate a secure random token
+      delete req.session.captchacode;
+      var token = crypto.randomBytes(48).toString('hex');
+      // Token expires after 4 hours
+      db.setex('token:'+token, 14400, data, function(err, reply) {
+        mailer.sendEmail(req.body.email, token, function(err, response) {
+          if (err) {
+            console.log('error sending email: '+err.message);
+          }
+        });
+      });
+      delete req.session.oldvalues;
+      return res.render('recoverpasswd', {
+        followup: req.query.followup || '/',
+        slogan: utils.randomSlogan(),
+        success: true
+      });
+    }
+    req.session.errors = {alert: 'The email address you specified could not be found'};
+    res.redirect(req.url);
+  });
 };
 
 /**
@@ -338,46 +338,46 @@ exports.sendEmail = function(req, res) {
  */
 
 exports.resetPasswd = function(req, res) {
-    if (req.body.password === undefined) {
-        return res.send(412);
-    }
-    
-    var errors = {};
-    
-    // Validate new password
-    if (req.body.password.length < 6) {
-        errors.password = 'must be at least 6 characters long';
-    }
-    // Check token availability
-    if (!req.query.token) {
-        errors.alert = 'Missing token.';
-    }
-    
-    if (errors.password || errors.alert) {
-        req.session.errors = errors;
-        return res.redirect(req.url);
+  if (req.body.password === undefined) {
+    return res.send(412);
+  }
+  
+  var errors = {};
+  
+  // Validate new password
+  if (req.body.password.length < 6) {
+    errors.password = 'must be at least 6 characters long';
+  }
+  // Check token availability
+  if (!req.query.token) {
+    errors.alert = 'Missing token.';
+  }
+  
+  if (errors.password || errors.alert) {
+    req.session.errors = errors;
+    return res.redirect(req.url);
+  }
+  
+  var key = 'token:'+req.query.token;
+  db.get(key, function(err, user) {
+    if (user !== null) {
+      // Delete the token
+      db.del(key);
+      // Update password
+      var salt = crypto.randomBytes(6).toString('base64');
+      var password = crypto.createHash('sha256').update(salt+req.body.password).digest('hex');
+      db.hmset(user, 'salt', salt, 'password', password, function(err, data) {
+        res.render('login', {
+          followup: '/',
+          slogan: utils.randomSlogan(),
+          success: 'You can now login with your new password.'
+        });
+      });
+      return;
     }
-    
-    var key = 'token:'+req.query.token;
-    db.get(key, function(err, user) {
-        if (user !== null) {
-            // Delete the token
-            db.del(key);
-            // Update password
-            var salt = crypto.randomBytes(6).toString('base64');
-            var password = crypto.createHash('sha256').update(salt+req.body.password).digest('hex');
-            db.hmset(user, 'salt', salt, 'password', password, function(err, data) {
-                res.render('login', {
-                    followup: '/',
-                    slogan: utils.randomSlogan(),
-                    success: 'You can now login with your new password.'
-                });
-            });
-            return;
-        }
-        req.session.errors = {alert: 'Invalid or expired token.'};
-        res.redirect(req.url);
-    });
+    req.session.errors = {alert: 'Invalid or expired token.'};
+    res.redirect(req.url);
+  });
 };
 
 /**
@@ -385,24 +385,24 @@ exports.resetPasswd = function(req, res) {
  */
 
 exports.profile = function(req, res) {
-    var key = 'user:'+req.params[0];
-    db.exists(key, function(err, data) {
-        if (data === 1) {
-            db.hgetall(key, function(e, obj) {
-                obj.bestguesstime = (obj.bestguesstime/1000).toFixed(1);
-                obj.worstguesstime = (obj.worstguesstime/1000).toFixed(1);
-                if (obj.guessed !== '0') {
-                    obj.meanguesstime = ((obj.totguesstime/obj.guessed)/1000).toFixed(1);
-                }
-                delete obj.email;
-                delete obj.password;
-                delete obj.salt;
-                delete obj.totguesstime;
-                res.locals.slogan = utils.randomSlogan();
-                res.render('user', obj);
-            });
-            return;
+  var key = 'user:'+req.params[0];
+  db.exists(key, function(err, data) {
+    if (data === 1) {
+      db.hgetall(key, function(e, obj) {
+        obj.bestguesstime = (obj.bestguesstime/1000).toFixed(1);
+        obj.worstguesstime = (obj.worstguesstime/1000).toFixed(1);
+        if (obj.guessed !== '0') {
+          obj.meanguesstime = ((obj.totguesstime/obj.guessed)/1000).toFixed(1);
         }
-        res.send(404);
-    });
+        delete obj.email;
+        delete obj.password;
+        delete obj.salt;
+        delete obj.totguesstime;
+        res.locals.slogan = utils.randomSlogan();
+        res.render('user', obj);
+      });
+      return;
+    }
+    res.send(404);
+  });
 };
index 01c714c7ac95853e7d26504425d2d5514199c00c..5af4d7dfc0774a31ec281b8bb5257a0f2f644bc2 100644 (file)
@@ -1,65 +1,64 @@
 extends layout
 
 block title
-    title binb :: Change password
+  title binb :: Change password
 
 block nav
-    ul.nav.pull-right
+  ul.nav.pull-right
+    li
+      a(href="/") Home
+    li.dropdown
+      a.dropdown-toggle(data-toggle="dropdown", href="#") Logged in as #{loggedin} 
+        span.caret
+      ul.dropdown-menu
         li
-            a(href="/") Home
-        li.dropdown
-            a.dropdown-toggle(data-toggle="dropdown", href="#")
-                | Logged in as #{loggedin} 
-                span.caret
-            ul.dropdown-menu
-                li
-                    a(href="/user/#{loggedin}", target="_blank") Profile
-                li
-                    a(href="/logout") Logout
+          a(href="/user/#{loggedin}", target="_blank") Profile
+        li
+          a(href="/logout") Logout
 
 block sections
-    section
-        .row
-            .span12.offset2
-                form.form-horizontal.well(method="post",
-                    action="/changepasswd?followup=#{followup}")
-                    fieldset
-                        - if (locals.errors)
-                            - if (errors.oldpassword)
-                                .control-group.error
-                                    label.control-label(for="oldpassword") Old password
-                                    .controls
-                                        input#oldpassword(type="password", name="oldpassword")
-                                        span.help-inline #{errors.oldpassword}
-                            - else
-                                .control-group
-                                    label.control-label(for="oldpassword") Old password
-                                    .controls
-                                        input#oldpassword(type="password", name="oldpassword",
-                                            placeholder="enter your current password...")
-                            - if (errors.newpassword)
-                                .control-group.error
-                                    label.control-label(for="newpassword") New password
-                                    .controls
-                                        input#newpassword(type="password", name="newpassword")
-                                        span.help-inline #{errors.newpassword}
-                            - else
-                                .control-group
-                                    label.control-label(for="newpassword") New password
-                                    .controls
-                                        input#newpassword(type="password", name="newpassword",
-                                            placeholder="enter your new password...")
-                        - else
-                            .control-group
-                                label.control-label(for="oldpassword") Old password
-                                .controls
-                                    input#oldpassword(type="password", name="oldpassword",
-                                        placeholder="enter your current password...")
-                            .control-group
-                                label.control-label(for="password") New password
-                                .controls
-                                    input#newpassword(type="password", name="newpassword",
-                                        placeholder="enter your new password...")
-                        button.submit-button.btn.btn-primary(type="submit")
-                            i.icon-edit.icon-white
-                            |  Update
+  section
+    .row
+      .span12.offset2
+        form.form-horizontal.well(method="post",
+          action="/changepasswd?followup=#{followup}")
+          fieldset
+            - if (locals.errors)
+              - if (errors.oldpassword)
+                .control-group.error
+                  label.control-label(for="oldpassword") Old password
+                  .controls
+                    input#oldpassword(type="password", name="oldpassword")
+                    span.help-inline #{errors.oldpassword}
+              - else
+                .control-group
+                  label.control-label(for="oldpassword") Old password
+                  .controls
+                    input#oldpassword(type="password", name="oldpassword",
+                      placeholder="enter your current password...")
+              - if (errors.newpassword)
+                .control-group.error
+                  label.control-label(for="newpassword") New password
+                  .controls
+                    input#newpassword(type="password", name="newpassword")
+                    span.help-inline #{errors.newpassword}
+              - else
+                .control-group
+                  label.control-label(for="newpassword") New password
+                  .controls
+                    input#newpassword(type="password", name="newpassword",
+                      placeholder="enter your new password...")
+            - else
+              .control-group
+                label.control-label(for="oldpassword") Old password
+                .controls
+                  input#oldpassword(type="password", name="oldpassword",
+                    placeholder="enter your current password...")
+              .control-group
+                label.control-label(for="password") New password
+                .controls
+                  input#newpassword(type="password", name="newpassword",
+                    placeholder="enter your new password...")
+            button.submit-button.btn.btn-primary(type="submit")
+              i.icon-edit.icon-white
+              |  Update
index cbbf765950a6b9d8c504d7668005dda00e77de31..5bc0cc384029d54773a33acd965d7d9bdbcde018 100644 (file)
@@ -1,61 +1,60 @@
 extends layout
 
 block title
-    title binb
+  title binb
 
 block nav
-    ul.nav.pull-right
-        li.active
-            a(href="/") Home
-        li
-            a(target="_blank", href="/leaderboards")
-                i.icon-list-alt.icon-white
-                |  Leaderboards
-        - if (locals.loggedin)
-            li.dropdown
-                a.dropdown-toggle(data-toggle="dropdown", href="#")
-                    | Logged in as #{loggedin} 
-                    span.caret
-                ul.dropdown-menu
-                    li
-                        a(href="/user/#{loggedin}", target="_blank") Profile
-                    li
-                        a(href="/changepasswd") Change password
-                    li
-                        a(href="/logout") Logout
-        - else
-            li
-                a(href="/signup") Sign up
-            li
-                a(href="/login") Login
+  ul.nav.pull-right
+    li.active
+      a(href="/") Home
+    li
+      a(target="_blank", href="/leaderboards")
+        i.icon-list-alt.icon-white
+        |  Leaderboards
+    - if (locals.loggedin)
+      li.dropdown
+        a.dropdown-toggle(data-toggle="dropdown", href="#") Logged in as #{loggedin} 
+          span.caret
+        ul.dropdown-menu
+          li
+            a(href="/user/#{loggedin}", target="_blank") Profile
+          li
+            a(href="/changepasswd") Change password
+          li
+            a(href="/logout") Logout
+    - else
+      li
+        a(href="/signup") Sign up
+      li
+        a(href="/login") Login
 
 block sections
-    section
-        .row
-            .span7
-                h3 What's this?
-                p binb is a realtime, multiplayer, competitive music listening game.
-            .span9
-                h3 How to play?
-                p All you have to do is to guess the song that is playing. A fixed number 
-                    | of songs will run and for each one correctly guessed you will earn an 
-                    | amount of points. That amount depends on the number of correct guesses 
-                    | (artist and/or title of the song) and on how fast you will be on 
-                    | answering compared to other players. At the end a scoreboard will 
-                    | report the best three players of the match. If you have read this 
-                    | far, what are you waiting? Click on a room below and prove your 
-                    | music knowledge!
-    section
-        .row
-            .span16
-                ul.thumbnails
-                    - each item in rooms
-                        li.span4
-                            a.thumbnail.relative(href=item)
-                                .room #{item} - 
-                                    span(id=item)
-                                    |  Players
+  section
+    .row
+      .span7
+        h3 What's this?
+        p binb is a realtime, multiplayer, competitive music listening game.
+      .span9
+        h3 How to play?
+        p All you have to do is to guess the song that is playing. A fixed number 
+          | of songs will run and for each one correctly guessed you will earn an 
+          | amount of points. That amount depends on the number of correct guesses 
+          | (artist and/or title of the song) and on how fast you will be on 
+          | answering compared to other players. At the end a scoreboard will 
+          | report the best three players of the match. If you have read this 
+          | far, what are you waiting? Click on a room below and prove your 
+          | music knowledge!
+  section
+    .row
+      .span16
+        ul.thumbnails
+          - each item in rooms
+            li.span4
+              a.thumbnail.relative(href=item)
+                .room #{item} - 
+                  span(id=item)
+                  |  Players
 
 append scripts    
-    script(src="/socket.io/socket.io.js")
-    script(src="/static/js/home.js")
+  script(src="/socket.io/socket.io.js")
+  script(src="/static/js/home.js")
index 967d81c749b39a6b969a44856400ed5243cdfb39..e1edb603356696697ab23cbae882aa699c743738 100644 (file)
@@ -1,62 +1,62 @@
 doctype html
 html
-    head
-        meta(charset="utf-8")
-        block title
-        meta(name="author", content="Luigi Pinca")
-        meta(name="description", content="Simple, realtime, multiplayer, competitive music listening game. Guess the song and prove your music knowledge!")
-        meta(property="og:description", content="Simple, realtime, multiplayer, competitive music listening game. Guess the song and prove your music knowledge!")
-        meta(property="og:image", content="https://dl.dropbox.com/u/58444696/binb-logo-200.png")
-        meta(property="og:title", content="binb")
-        meta(property="og:type", content="game")
-        meta(property="og:url", content="http://binb.nodejitsu.com/")
-        link(href="/static/css/bootstrap.min.css", rel="stylesheet")
-        link(href="/static/css/style.css", rel="stylesheet")
-        script
-            var _gaq = _gaq || [];
-            _gaq.push(['_setAccount', 'UA-29865853-1']);
-            _gaq.push(['_trackPageview']);
-            (function() {
-                var ga = document.createElement('script');
-                ga.type = 'text/javascript'; ga.async = true;
-                ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') +
-                    '.google-analytics.com/ga.js';
-                var s = document.getElementsByTagName('script')[0];
-                s.parentNode.insertBefore(ga, s);
-            })();
-    body
-        .navbar.navbar-inverse.navbar-fixed-top
-            .navbar-inner
-                .container
-                    block brand
-                        a.brand(href="/")
-                            .icons.logo #{slogan}
-                    block nav
+  head
+    meta(charset="utf-8")
+    block title
+    meta(name="author", content="Luigi Pinca")
+    meta(name="description", content="Simple, realtime, multiplayer, competitive music listening game. Guess the song and prove your music knowledge!")
+    meta(property="og:description", content="Simple, realtime, multiplayer, competitive music listening game. Guess the song and prove your music knowledge!")
+    meta(property="og:image", content="https://dl.dropbox.com/u/58444696/binb-logo-200.png")
+    meta(property="og:title", content="binb")
+    meta(property="og:type", content="game")
+    meta(property="og:url", content="http://binb.nodejitsu.com/")
+    link(href="/static/css/bootstrap.min.css", rel="stylesheet")
+    link(href="/static/css/style.css", rel="stylesheet")
+    script
+      var _gaq = _gaq || [];
+      _gaq.push(['_setAccount', 'UA-29865853-1']);
+      _gaq.push(['_trackPageview']);
+      (function() {
+        var ga = document.createElement('script');
+        ga.type = 'text/javascript'; ga.async = true;
+        ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') +
+          '.google-analytics.com/ga.js';
+        var s = document.getElementsByTagName('script')[0];
+        s.parentNode.insertBefore(ga, s);
+      })();
+  body
+    .navbar.navbar-inverse.navbar-fixed-top
+      .navbar-inner
         .container
-            block sections
-            footer
-                #footer-inner
-                    #copy &copy; 2012 Luigi Pinca
-                    iframe#facebook-button(allowTransparency="true", frameborder="0", scrolling="no",
-                        src="//www.facebook.com/plugins/like.php?href=http%3A%2F%2Fbinb.nodejitsu.com&amp;send=false&amp;layout=button_count&amp;show_faces=false&amp;action=like&amp;colorscheme=light&amp;locale=en_US")
-                    iframe#twitter-button(allowtransparency="true", frameborder="0", scrolling="no",
-                        src="//platform.twitter.com/widgets/tweet_button.html?url=http%3A%2F%2Fbinb.nodejitsu.com")
-                    iframe#github-button(allowtransparency="true", frameborder="0", scrolling="0", 
-                        src="http://ghbtns.com/github-btn.html?user=lpinca&repo=binb&type=watch&count=true")
-                    span.footer-info . Optimized for Google Chrome.
-                    a#nodejitsu-logo.icons(target="_blank", href="http://nodejitsu.com/")
-                    span.footer-info Powered by 
-        block media
-        block scripts
-            script(src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js")
-            script(src="/static/js/bootstrap.min.js")
-        script
-            var uvOptions = {};
-            (function() {
-                var uv = document.createElement('script');
-                uv.type = 'text/javascript'; uv.async = true;
-                uv.src = ('https:' == document.location.protocol ? 'https://' : 'http://') + 
-                    'widget.uservoice.com/LSMjFAQRifhD6BjOG2KWw.js';
-                var s = document.getElementsByTagName('script')[0];
-                s.parentNode.insertBefore(uv, s);
-            })();
+          block brand
+            a.brand(href="/")
+              .icons.logo #{slogan}
+          block nav
+    .container
+      block sections
+      footer
+        #footer-inner
+          #copy &copy; 2012 Luigi Pinca
+          iframe#facebook-button(allowTransparency="true", frameborder="0", scrolling="no",
+            src="//www.facebook.com/plugins/like.php?href=http%3A%2F%2Fbinb.nodejitsu.com&amp;send=false&amp;layout=button_count&amp;show_faces=false&amp;action=like&amp;colorscheme=light&amp;locale=en_US")
+          iframe#twitter-button(allowtransparency="true", frameborder="0", scrolling="no",
+            src="//platform.twitter.com/widgets/tweet_button.html?url=http%3A%2F%2Fbinb.nodejitsu.com")
+          iframe#github-button(allowtransparency="true", frameborder="0", scrolling="0", 
+            src="http://ghbtns.com/github-btn.html?user=lpinca&repo=binb&type=watch&count=true")
+          span.footer-info . Optimized for Google Chrome.
+          a#nodejitsu-logo.icons(target="_blank", href="http://nodejitsu.com/")
+          span.footer-info Powered by 
+    block media
+    block scripts
+      script(src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js")
+      script(src="/static/js/bootstrap.min.js")
+    script
+      var uvOptions = {};
+      (function() {
+        var uv = document.createElement('script');
+        uv.type = 'text/javascript'; uv.async = true;
+        uv.src = ('https:' == document.location.protocol ? 'https://' : 'http://') + 
+          'widget.uservoice.com/LSMjFAQRifhD6BjOG2KWw.js';
+        var s = document.getElementsByTagName('script')[0];
+        s.parentNode.insertBefore(uv, s);
+      })();
index d8e24d83b461269c76d446f3d51fce0463a3eaaa..53b78999568838c72331c3677ca19fa1fbfae5b7 100644 (file)
@@ -1,53 +1,53 @@
 extends layout
 
 block title
-    title binb :: Leaderboards
+  title binb :: Leaderboards
 
 block brand
-    a.brand(href="#")
-        .icons.logo #{slogan}
+  a.brand(href="#")
+    .icons.logo #{slogan}
 
 block sections
-    section
-        .row
-            .span7.offset1
-                .highscores High Scores
-                    .icons.img
-    section
-        .row
-            .span7.offset1.relative
-                h4 Points
-                .leaderboard-wrapper
-                    table.table.table-striped.table-bordered.leaderboard
-                        tbody
-                            - each user, i in pointsleaderboard
-                                tr
-                                    td #{i+1}
-                                    td
-                                        a(href="/user/#{user.username}") #{user.username}
-                                    td #{user.totpoints}
-                    .loading
-                        .loading-block
-                        .loading-block
-                        .loading-block
-            .span7.relative
-                h4 Times
-                .leaderboard-wrapper
-                    table.table.table-striped.table-bordered.leaderboard
-                        tbody
-                            - each user, i in timesleaderboard
-                                tr
-                                    td #{i+1}
-                                    td
-                                        a(href="/user/#{user.username}") #{user.username}
-                                    td 
-                                        i.icon-time
-                                        |  #{user.bestguesstime} sec
-                    .loading
-                        .loading-block
-                        .loading-block
-                        .loading-block
+  section
+    .row
+      .span7.offset1
+        .highscores High Scores
+          .icons.img
+  section
+    .row
+      .span7.offset1.relative
+        h4 Points
+        .leaderboard-wrapper
+          table.table.table-striped.table-bordered.leaderboard
+            tbody
+              - each user, i in pointsleaderboard
+                tr
+                  td #{i+1}
+                  td
+                    a(href="/user/#{user.username}") #{user.username}
+                  td #{user.totpoints}
+          .loading
+            .loading-block
+            .loading-block
+            .loading-block
+      .span7.relative
+        h4 Times
+        .leaderboard-wrapper
+          table.table.table-striped.table-bordered.leaderboard
+            tbody
+              - each user, i in timesleaderboard
+                tr
+                  td #{i+1}
+                  td
+                    a(href="/user/#{user.username}") #{user.username}
+                  td 
+                    i.icon-time
+                    |  #{user.bestguesstime} sec
+          .loading
+            .loading-block
+            .loading-block
+            .loading-block
 
 block scripts
-    script(src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js")
-    script(src="/static/js/leaderboards.js")
+  script(src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js")
+  script(src="/static/js/leaderboards.js")
index 382df575e7ff491a1d3604e65d9a2bdc62c7b4df..dfe1d122f4f0e0a2c6e409b0cf7fb90280d3124a 100644 (file)
@@ -1,75 +1,75 @@
 extends layout
 
 block title
-    title binb :: login
-        
+  title binb :: login
+    
 block nav
-    ul.nav.pull-right
-        li
-            a(href="/") Home
-        li
-            a(href="/signup?followup=#{followup}") Sign up
-        li.active
-            a(href="/login?followup=#{followup}") Login
+  ul.nav.pull-right
+    li
+      a(href="/") Home
+    li
+      a(href="/signup?followup=#{followup}") Sign up
+    li.active
+      a(href="/login?followup=#{followup}") Login
 
 block sections
-    section
-        .row
-            .span3
-                h3 New user?
-                a(href="/signup?followup=#{followup}") Click here to create an account.
-            .span13
-                - if (locals.errors && errors.alert)
-                    .alert.alert-error
-                        a.close(data-dismiss="alert") &times;
-                        strong Oh snap!
-                        |  #{errors.alert}
-                - else if (locals.success)
-                    .alert.alert-success
-                        a.close(data-dismiss="alert") &times;
-                        strong Well done!
-                        |  #{success}
-                form.form-horizontal.well(method="post", action="/login?followup=#{followup}")
-                    fieldset
-                        - if (locals.errors)
-                            - if (errors.username)
-                                .control-group.error
-                                    label.control-label(for="username") Name
-                                    .controls
-                                        input#username(type="text", name="username",
-                                            maxlength="15", value=oldvalues.username)
-                                        span.help-inline #{errors.username}
-                            - else
-                                .control-group
-                                    label.control-label(for="username") Name
-                                    .controls
-                                        input#username(type="text", name="username",
-                                            maxlength="15", value=oldvalues.username)
-                            - if (errors.password)
-                                .control-group.error
-                                    label.control-label(for="password") Password
-                                    .controls
-                                        input#password(type="password", name="password")
-                                        span.help-inline #{errors.password}
-                            - else
-                                .control-group
-                                    label.control-label(for="password") Password
-                                    .controls
-                                        input#password(type="password", name="password",
-                                            placeholder="enter your password...")
-                        - else
-                            .control-group
-                                label.control-label(for="username") Name
-                                .controls
-                                    input#username(type="text", name="username",
-                                        maxlength="15", placeholder="enter your nickname...")
-                            .control-group
-                                label.control-label(for="password") Password
-                                .controls
-                                    input#password(type="password", name="password",
-                                        placeholder="enter your password...")
-                        button.submit-button.btn.btn-primary(type="submit")
-                            i.icon-lock.icon-white
-                            |  Login
-                        a.forgot-passwd(href="/recoverpasswd?followup=#{followup}")
-                            | Forgot your password?
+  section
+    .row
+      .span3
+        h3 New user?
+        a(href="/signup?followup=#{followup}") Click here to create an account.
+      .span13
+        - if (locals.errors && errors.alert)
+          .alert.alert-error
+            a.close(data-dismiss="alert") &times;
+            strong Oh snap!
+            |  #{errors.alert}
+        - else if (locals.success)
+          .alert.alert-success
+            a.close(data-dismiss="alert") &times;
+            strong Well done!
+            |  #{success}
+        form.form-horizontal.well(method="post", action="/login?followup=#{followup}")
+          fieldset
+            - if (locals.errors)
+              - if (errors.username)
+                .control-group.error
+                  label.control-label(for="username") Name
+                  .controls
+                    input#username(type="text", name="username",
+                      maxlength="15", value=oldvalues.username)
+                    span.help-inline #{errors.username}
+              - else
+                .control-group
+                  label.control-label(for="username") Name
+                  .controls
+                    input#username(type="text", name="username",
+                      maxlength="15", value=oldvalues.username)
+              - if (errors.password)
+                .control-group.error
+                  label.control-label(for="password") Password
+                  .controls
+                    input#password(type="password", name="password")
+                    span.help-inline #{errors.password}
+              - else
+                .control-group
+                  label.control-label(for="password") Password
+                  .controls
+                    input#password(type="password", name="password",
+                      placeholder="enter your password...")
+            - else
+              .control-group
+                label.control-label(for="username") Name
+                .controls
+                  input#username(type="text", name="username",
+                    maxlength="15", placeholder="enter your nickname...")
+              .control-group
+                label.control-label(for="password") Password
+                .controls
+                  input#password(type="password", name="password",
+                    placeholder="enter your password...")
+            button.submit-button.btn.btn-primary(type="submit")
+              i.icon-lock.icon-white
+              |  Login
+            a.forgot-passwd(href="/recoverpasswd?followup=#{followup}")
+              | Forgot your password?
index c83ab09250cb5444ac4c0ade501180c2c095992e..f9d577e12a476a442d7d84627ccfdb470c0e3c81 100644 (file)
@@ -1,78 +1,78 @@
 extends layout
 
 block title
-    title binb :: Recover password
+  title binb :: Recover password
 
 block nav
-    ul.nav.pull-right
-        li
-            a(href="/") Home
-        li
-            a(href="/signup?followup=#{followup}") Sign up
-        li
-            a(href="/login?followup=#{followup}") Login
+  ul.nav.pull-right
+    li
+      a(href="/") Home
+    li
+      a(href="/signup?followup=#{followup}") Sign up
+    li
+      a(href="/login?followup=#{followup}") Login
 
 block sections
-    section
-        .row
-            .span12.offset2
-                - if (locals.success)
-                    .alert.alert-block.alert-success
-                        h4.alert-heading Success!
-                        | An email has been sent to you.<br>To start the password reset 
-                        | process, open this email and follow the given instructions.<br>
-                        | If you don't receive it in a reasonable amount of time, please 
-                        | use the support form on the left.
+  section
+    .row
+      .span12.offset2
+        - if (locals.success)
+          .alert.alert-block.alert-success
+            h4.alert-heading Success!
+            | An email has been sent to you.<br>To start the password reset 
+            | process, open this email and follow the given instructions.<br>
+            | If you don't receive it in a reasonable amount of time, please 
+            | use the support form on the left.
+        - else
+          - if (locals.errors && errors.alert)
+            .alert.alert-error
+              a.close(data-dismiss="alert") &times;
+              strong Oh snap!
+              |  #{errors.alert}
+          form.form-horizontal.well(method="post",
+            action="/recoverpasswd?followup=#{followup}")
+            fieldset
+              - if (locals.errors)
+                - if (errors.email)
+                  .control-group.error
+                    label.control-label(for="email") Email
+                    .controls
+                      input#email(type="text", name="email",
+                        value=oldvalues.email)
+                      span.help-inline #{errors.email}
                 - else
-                    - if (locals.errors && errors.alert)
-                        .alert.alert-error
-                            a.close(data-dismiss="alert") &times;
-                            strong Oh snap!
-                            |  #{errors.alert}
-                    form.form-horizontal.well(method="post",
-                        action="/recoverpasswd?followup=#{followup}")
-                        fieldset
-                            - if (locals.errors)
-                                - if (errors.email)
-                                    .control-group.error
-                                        label.control-label(for="email") Email
-                                        .controls
-                                            input#email(type="text", name="email",
-                                                value=oldvalues.email)
-                                            span.help-inline #{errors.email}
-                                - else
-                                    .control-group
-                                        label.control-label(for="email") Email
-                                        .controls
-                                            input#email(type="text", name="email",
-                                                value=oldvalues.email)
-                                - if (errors.captcha)
-                                    .control-group.error
-                                        label.control-label(for="captcha-input") Are you human?
-                                        .controls
-                                            img#captcha(src=captchaurl)
-                                            input#captcha-input(type="text", name="captcha",
-                                                maxlength="4")
-                                            span.help-inline #{errors.captcha}
-                                - else
-                                    .control-group
-                                        label.control-label(for="captcha-input") Are you human?
-                                        .controls
-                                            img#captcha(src=captchaurl)
-                                            input#captcha-input(type="text", name="captcha",
-                                                maxlength="4", placeholder="type what you see...")
-                            - else
-                                .control-group
-                                    label.control-label(for="email") Email
-                                    .controls
-                                        input#email(type="text", name="email",
-                                            placeholder="type the email you used to sign up...")
-                                .control-group
-                                    label.control-label(for="captcha-input") Are you human?
-                                    .controls
-                                        img#captcha(src=captchaurl)
-                                        input#captcha-input(type="text", name="captcha",
-                                            maxlength="4", placeholder="type what you see...")
-                            button.submit-button.btn.btn-primary(type="submit")
-                                i.icon-envelope.icon-white
-                                |  Send password reset link
+                  .control-group
+                    label.control-label(for="email") Email
+                    .controls
+                      input#email(type="text", name="email",
+                        value=oldvalues.email)
+                - if (errors.captcha)
+                  .control-group.error
+                    label.control-label(for="captcha-input") Are you human?
+                    .controls
+                      img#captcha(src=captchaurl)
+                      input#captcha-input(type="text", name="captcha",
+                        maxlength="4")
+                      span.help-inline #{errors.captcha}
+                - else
+                  .control-group
+                    label.control-label(for="captcha-input") Are you human?
+                    .controls
+                      img#captcha(src=captchaurl)
+                      input#captcha-input(type="text", name="captcha",
+                        maxlength="4", placeholder="type what you see...")
+              - else
+                .control-group
+                  label.control-label(for="email") Email
+                  .controls
+                    input#email(type="text", name="email",
+                      placeholder="type the email you used to sign up...")
+                .control-group
+                  label.control-label(for="captcha-input") Are you human?
+                  .controls
+                    img#captcha(src=captchaurl)
+                    input#captcha-input(type="text", name="captcha",
+                      maxlength="4", placeholder="type what you see...")
+              button.submit-button.btn.btn-primary(type="submit")
+                i.icon-envelope.icon-white
+                |  Send password reset link
index cee041e9f1361ce4137da55bc2115acd1e54707f..c4f96df5d512d4bd792fc26ff2c05c694b4cc417 100644 (file)
@@ -1,35 +1,35 @@
 extends layout
 
 block title
-    title binb :: Reset password
+  title binb :: Reset password
 
 block brand
-    a.brand(href="#")
-        .icons.logo #{slogan}
+  a.brand(href="#")
+    .icons.logo #{slogan}
 
 block sections
-    section
-        .row
-            .span12.offset2
-                - if (locals.errors && errors.alert)
-                    .alert.alert-error
-                        a.close(data-dismiss="alert") &times;
-                        strong Oh snap!
-                        |  #{errors.alert}
-                form.form-horizontal.well(method="post", action="/resetpasswd?token=#{token}")
-                    fieldset
-                        - if (locals.errors && errors.password)
-                            .control-group.error
-                                label.control-label(for="password") New password
-                                .controls
-                                    input#password(type="password", name="password")
-                                    span.help-inline #{errors.password}
-                        - else
-                            .control-group
-                                label.control-label(for="password") New password
-                                .controls
-                                    input#password(type="password", name="password",
-                                        placeholder="enter your new password...")
-                        button.submit-button.btn.btn-primary(type="submit")
-                            i.icon-edit.icon-white
-                            |  Update
+  section
+    .row
+      .span12.offset2
+        - if (locals.errors && errors.alert)
+          .alert.alert-error
+            a.close(data-dismiss="alert") &times;
+            strong Oh snap!
+            |  #{errors.alert}
+        form.form-horizontal.well(method="post", action="/resetpasswd?token=#{token}")
+          fieldset
+            - if (locals.errors && errors.password)
+              .control-group.error
+                label.control-label(for="password") New password
+                .controls
+                  input#password(type="password", name="password")
+                  span.help-inline #{errors.password}
+            - else
+              .control-group
+                label.control-label(for="password") New password
+                .controls
+                  input#password(type="password", name="password",
+                    placeholder="enter your new password...")
+            button.submit-button.btn.btn-primary(type="submit")
+              i.icon-edit.icon-white
+              |  Update
index 7a558d6eb113544b7ee572b0c74f8f8741a9175d..94ab232eb4e5165d2ead0aa315985752931fe52d 100644 (file)
 extends layout
 
 block title
-    title binb :: #{roomname}
+  title binb :: #{roomname}
 
 block brand
 
 block nav
-    ul.nav.pull-right
-        li
-            a(href="/") Home
-        li
-            a(target="_blank", href="/leaderboards")
-                i.icon-list-alt.icon-white
-                |  Leaderboards
-        li.active.dropdown
-            a.dropdown-toggle(data-toggle="dropdown",href="#") #{roomname} 
-                b.caret
-            ul.dropdown-menu
-                - each item in rooms
-                    - if (item !== roomname)
-                        li
-                            a(href=item)
-                                span.room-name #{item}
-                                i.icon-user.pull-right
-                                span.users-counter
-        - if (locals.loggedin)
-            li.dropdown
-                a.dropdown-toggle(data-toggle="dropdown", href="#")
-                    | Logged in as #{loggedin} 
-                    span.caret
-                ul.dropdown-menu
-                    li
-                        a(href="/user/#{loggedin}", target="_blank") Profile
-                    li
-                        a(href="/changepasswd?followup=/#{roomname}") Change password
-                    li
-                        a(href="/logout") Logout
-        - else
+  ul.nav.pull-right
+    li
+      a(href="/") Home
+    li
+      a(target="_blank", href="/leaderboards")
+        i.icon-list-alt.icon-white
+        |  Leaderboards
+    li.active.dropdown
+      a.dropdown-toggle(data-toggle="dropdown",href="#") #{roomname} 
+        b.caret
+      ul.dropdown-menu
+        - each item in rooms
+          - if (item !== roomname)
             li
-                a(href="/signup?followup=/#{roomname}") Sign up
-            li
-                a(href="/login?followup=/#{roomname}") Login
+              a(href=item)
+                span.room-name #{item}
+                i.icon-user.pull-right
+                span.users-counter
+    - if (locals.loggedin)
+      li.dropdown
+        a.dropdown-toggle(data-toggle="dropdown", href="#") Logged in as #{loggedin} 
+          span.caret
+        ul.dropdown-menu
+          li
+            a(href="/user/#{loggedin}", target="_blank") Profile
+          li
+            a(href="/changepasswd?followup=/#{roomname}") Change password
+          li
+            a(href="/logout") Logout
+    - else
+      li
+        a(href="/signup?followup=/#{roomname}") Sign up
+      li
+        a(href="/login?followup=/#{roomname}") Login
 
 block sections
-    section
-        .row
-            .span4.offset1
-                #cassette.relative
-                    #wheel-left.icons.wheel
-                    #tape-left
-                    #tape-right
-                    #wheel-right.icons.wheel
-                    #progress-bar
-                        #progress
-                    #countdown
-            .span2
-                #volume.relative
-            .span8
-                .page-header
-                    .icons.logo #{slogan}
-                    #total-tracks
-                        span
-                        |  tracks.
-                #summary.row
-                    .span2
-                        .title Rank
-                        .rank
-                    .span4
-                        .title Points
-                        .points
-                    .span2
-                        .title Track
-                        .track
-                p#feedback Waiting for connection...
-                input#guess.span8(type="text", tabindex="1",
-                    placeholder="guess the artist and/or title here")
-    section.relative
-        .row
-            #users-wrapper.span5.offset2
-                ul#users.unstyled
-            .span8
-                a#toggle-chat Hide chat
-                #chat-outer-wrapper
-                    #chat-wrapper.bordered
-                        ul#chat.unstyled
-                    #message-wrapper
-                        span#recipient
-                        input#message.span8(type="text", tabindex="2")
-                ul#tracks.unstyled
-        #disclaimer
-            div I do not own any right on the songs that are played here.
-            div Tracks are played using iTunes api preview.
+  section
+    .row
+      .span4.offset1
+        #cassette.relative
+          #wheel-left.icons.wheel
+          #tape-left
+          #tape-right
+          #wheel-right.icons.wheel
+          #progress-bar
+            #progress
+          #countdown
+      .span2
+        #volume.relative
+      .span8
+        .page-header
+          .icons.logo #{slogan}
+          #total-tracks
+            span
+            |  tracks.
+        #summary.row
+          .span2
+            .title Rank
+            .rank
+          .span4
+            .title Points
+            .points
+          .span2
+            .title Track
+            .track
+        p#feedback Waiting for connection...
+        input#guess.span8(type="text", tabindex="1",
+          placeholder="guess the artist and/or title here")
+  section.relative
+    .row
+      #users-wrapper.span5.offset2
+        ul#users.unstyled
+      .span8
+        a#toggle-chat Hide chat
+        #chat-outer-wrapper
+          #chat-wrapper.bordered
+            ul#chat.unstyled
+          #message-wrapper
+            span#recipient
+            input#message.span8(type="text", tabindex="2")
+        ul#tracks.unstyled
+    #disclaimer
+      div I do not own any right on the songs that are played here.
+      div Tracks are played using iTunes api preview.
 
 block media
-    #modal.modal.fade
-    #player
+  #modal.modal.fade
+  #player
 
 append scripts
-    script(src="/static/js/jquery.jplayer.min.js")
-    script(src="/socket.io/socket.io.js")
-    script(src="/static/js/app.js")
+  script(src="/static/js/jquery.jplayer.min.js")
+  script(src="/socket.io/socket.io.js")
+  script(src="/static/js/app.js")
index ee10d0c5f271a032f744b369692fbe92f2945aa5..8e0c262a168ca07a9ac7f539d7d59906edd6db95 100644 (file)
 extends layout
 
 block title
-    title binb :: sign up
+  title binb :: sign up
 
 block nav
-    ul.nav.pull-right
-        li
-            a(href="/") Home
-        li.active
-            a(href="/signup?followup=#{followup}") Sign up
-        li
-            a(href="/login?followup=#{followup}") Login
+  ul.nav.pull-right
+    li
+      a(href="/") Home
+    li.active
+      a(href="/signup?followup=#{followup}") Sign up
+    li
+      a(href="/login?followup=#{followup}") Login
 
 block sections
-    section
-        .row
-            .span3
-                h3 Not a new user?
-                a(href="/login?followup=#{followup}") Click here to log in.
-            .span13
-                h3 Why sign up?
-                p Registration is optional, but if you are a regular user consider creating 
-                    | an account. This will provide the following advantages:
-                ul
-                    li You will be the one and only owner of your nickname (no one can use 
-                        | your nickname in your place).
-                    li Some simple stats related to your account will be collected.
-                - if (locals.errors && errors.alert)
-                    .alert.alert-error
-                        a.close(data-dismiss="alert") &times;
-                        strong Oh snap!
-                        |  #{errors.alert}
-                form.form-horizontal.well(method="post",action="/signup?followup=#{followup}")
-                    fieldset
-                        - if (locals.errors)
-                            - if (errors.username)
-                                .control-group.error
-                                    label.control-label(for="username") Name
-                                    .controls
-                                        input#username(type="text", name="username",
-                                            maxlength="15", value=oldvalues.username)
-                                        span.help-inline #{errors.username}
-                            - else
-                                .control-group
-                                    label.control-label(for="username") Name
-                                    .controls
-                                        input#username(type="text", name="username",
-                                            maxlength="15", value=oldvalues.username)
-                            - if (errors.email)
-                                .control-group.error
-                                    label.control-label(for="email") Email
-                                    .controls
-                                        input#email(type="text", name="email",
-                                            value=oldvalues.email)
-                                        span.help-inline #{errors.email}
-                            - else
-                                .control-group
-                                    label.control-label(for="email") Email
-                                    .controls
-                                        input#email(type="text", name="email",
-                                            value=oldvalues.email)
-                            - if (errors.password)
-                                .control-group.error
-                                    label.control-label(for="password") Password
-                                    .controls
-                                        input#password(type="password", name="password")
-                                        span.help-inline #{errors.password}
-                            - else
-                                .control-group
-                                    label.control-label(for="password") Password
-                                    .controls
-                                        input#password(type="password", name="password",
-                                            placeholder="enter a password...")
-                            - if (errors.captcha)
-                                .control-group.error
-                                    label.control-label(for="captcha-input") Are you human?
-                                    .controls
-                                        img#captcha(src=captchaurl)
-                                        input#captcha-input(type="text", name="captcha",
-                                            maxlength="4")
-                                        span.help-inline #{errors.captcha}
-                            - else
-                                .control-group
-                                    label.control-label(for="captcha-input") Are you human?
-                                    .controls
-                                        img#captcha(src=captchaurl)
-                                        input#captcha-input(type="text", name="captcha",
-                                            maxlength="4", placeholder="type what you see...")
-                        - else
-                            .control-group
-                                label.control-label(for="username") Name
-                                .controls
-                                    input#username(type="text", name="username",
-                                        maxlength="15", placeholder="enter a nickname...")
-                            .control-group
-                                label.control-label(for="email") Email
-                                .controls
-                                    input#email(type="text", name="email",
-                                        placeholder="enter a valid email...")
-                            .control-group
-                                label.control-label(for="password") Password
-                                .controls
-                                    input#password(type="password", name="password",
-                                        placeholder="enter a password...")
-                            .control-group
-                                label.control-label(for="captcha-input") Are you human?
-                                .controls
-                                    img#captcha(src=captchaurl)
-                                    input#captcha-input(type="text", name="captcha",
-                                        maxlength="4", placeholder="type what you see...")
-                        button.submit-button.btn.btn-success(type="submit")
-                            i.icon-user.icon-white
-                            |  Sign up!
+  section
+    .row
+      .span3
+        h3 Not a new user?
+        a(href="/login?followup=#{followup}") Click here to log in.
+      .span13
+        h3 Why sign up?
+        p Registration is optional, but if you are a regular user consider creating 
+          | an account. This will provide the following advantages:
+        ul
+          li You will be the one and only owner of your nickname (no one can use 
+            | your nickname in your place).
+          li Some simple stats related to your account will be collected.
+        - if (locals.errors && errors.alert)
+          .alert.alert-error
+            a.close(data-dismiss="alert") &times;
+            strong Oh snap!
+            |  #{errors.alert}
+        form.form-horizontal.well(method="post",action="/signup?followup=#{followup}")
+          fieldset
+            - if (locals.errors)
+              - if (errors.username)
+                .control-group.error
+                  label.control-label(for="username") Name
+                  .controls
+                    input#username(type="text", name="username",
+                      maxlength="15", value=oldvalues.username)
+                    span.help-inline #{errors.username}
+              - else
+                .control-group
+                  label.control-label(for="username") Name
+                  .controls
+                    input#username(type="text", name="username",
+                      maxlength="15", value=oldvalues.username)
+              - if (errors.email)
+                .control-group.error
+                  label.control-label(for="email") Email
+                  .controls
+                    input#email(type="text", name="email",
+                      value=oldvalues.email)
+                    span.help-inline #{errors.email}
+              - else
+                .control-group
+                  label.control-label(for="email") Email
+                  .controls
+                    input#email(type="text", name="email",
+                      value=oldvalues.email)
+              - if (errors.password)
+                .control-group.error
+                  label.control-label(for="password") Password
+                  .controls
+                    input#password(type="password", name="password")
+                    span.help-inline #{errors.password}
+              - else
+                .control-group
+                  label.control-label(for="password") Password
+                  .controls
+                    input#password(type="password", name="password",
+                      placeholder="enter a password...")
+              - if (errors.captcha)
+                .control-group.error
+                  label.control-label(for="captcha-input") Are you human?
+                  .controls
+                    img#captcha(src=captchaurl)
+                    input#captcha-input(type="text", name="captcha",
+                      maxlength="4")
+                    span.help-inline #{errors.captcha}
+              - else
+                .control-group
+                  label.control-label(for="captcha-input") Are you human?
+                  .controls
+                    img#captcha(src=captchaurl)
+                    input#captcha-input(type="text", name="captcha",
+                      maxlength="4", placeholder="type what you see...")
+            - else
+              .control-group
+                label.control-label(for="username") Name
+                .controls
+                  input#username(type="text", name="username",
+                    maxlength="15", placeholder="enter a nickname...")
+              .control-group
+                label.control-label(for="email") Email
+                .controls
+                  input#email(type="text", name="email",
+                    placeholder="enter a valid email...")
+              .control-group
+                label.control-label(for="password") Password
+                .controls
+                  input#password(type="password", name="password",
+                    placeholder="enter a password...")
+              .control-group
+                label.control-label(for="captcha-input") Are you human?
+                .controls
+                  img#captcha(src=captchaurl)
+                  input#captcha-input(type="text", name="captcha",
+                    maxlength="4", placeholder="type what you see...")
+            button.submit-button.btn.btn-success(type="submit")
+              i.icon-user.icon-white
+              |  Sign up!
index 01cb36ff73f378c536eb7fd6de3470ded1a983c5..e5a3f963401aa0002f59b28e8940c9efa4331638 100644 (file)
@@ -1,88 +1,88 @@
 extends layout
 
 block title
-    title binb :: #{username} info
-    
+  title binb :: #{username} info
+  
 block brand
-    a.brand(href="#")
-        .icons.logo #{slogan}
+  a.brand(href="#")
+    .icons.logo #{slogan}
 
 block sections
-    section
-        .row
-            .span7.offset1
-                .profile #{username}
-                    .icons.img
-                div member since #{joindate}
-    section
-        .row
-            .span7.offset1
-                h4 Points
-                table.table.table-striped.table-bordered.stats
-                    tbody
-                        tr
-                            td Total
-                            td #{totpoints}
-                        tr
-                            td Best score
-                            td #{bestscore}
-                        tr
-                            td Guessed songs
-                            td #{guessed}
-                h4 Times
-                table.table.table-striped.table-bordered.stats
-                    tbody
-                        tr
-                            td Best guess time
-                            - if (bestguesstime !== "30.0")
-                                td #{bestguesstime} sec
-                            - else
-                                td N/A
-                        tr
-                            td Worst guess time
-                            - if (worstguesstime !== "0.0")
-                                td #{worstguesstime} sec
-                            - else
-                                td N/A
-                        tr
-                            td Mean guess time
-                            - if (typeof meanguesstime !== 'undefined')
-                                td #{meanguesstime} sec
-                            - else
-                                td N/A
-            .span7
-                h4 Awards
-                table.table.table-striped.table-bordered.stats
-                    tbody
-                        tr
-                            td Gold cups
-                            td
-                                .icons.cups.stand1
-                            td #{golds}
-                        tr
-                            td Silver cups
-                            td
-                                .icons.cups.stand2
-                            td #{silvers}
-                        tr
-                            td Bronze cups
-                            td
-                                .icons.cups.stand3
-                            td #{bronzes}
-                        tr
-                            td Victories
-                            td
-                                .icons.medals.rank1
-                            td #{victories}
-                        tr
-                            td Second places
-                            td
-                                .icons.medals.rank2
-                            td #{secondplaces}
-                        tr
-                            td Third places
-                            td
-                                .icons.medals.rank3
-                            td #{thirdplaces}
+  section
+    .row
+      .span7.offset1
+        .profile #{username}
+          .icons.img
+        div member since #{joindate}
+  section
+    .row
+      .span7.offset1
+        h4 Points
+        table.table.table-striped.table-bordered.stats
+          tbody
+            tr
+              td Total
+              td #{totpoints}
+            tr
+              td Best score
+              td #{bestscore}
+            tr
+              td Guessed songs
+              td #{guessed}
+        h4 Times
+        table.table.table-striped.table-bordered.stats
+          tbody
+            tr
+              td Best guess time
+              - if (bestguesstime !== "30.0")
+                td #{bestguesstime} sec
+              - else
+                td N/A
+            tr
+              td Worst guess time
+              - if (worstguesstime !== "0.0")
+                td #{worstguesstime} sec
+              - else
+                td N/A
+            tr
+              td Mean guess time
+              - if (typeof meanguesstime !== 'undefined')
+                td #{meanguesstime} sec
+              - else
+                td N/A
+      .span7
+        h4 Awards
+        table.table.table-striped.table-bordered.stats
+          tbody
+            tr
+              td Gold cups
+              td
+                .icons.cups.stand1
+              td #{golds}
+            tr
+              td Silver cups
+              td
+                .icons.cups.stand2
+              td #{silvers}
+            tr
+              td Bronze cups
+              td
+                .icons.cups.stand3
+              td #{bronzes}
+            tr
+              td Victories
+              td
+                .icons.medals.rank1
+              td #{victories}
+            tr
+              td Second places
+              td
+                .icons.medals.rank2
+              td #{secondplaces}
+            tr
+              td Third places
+              td
+                .icons.medals.rank3
+              td #{thirdplaces}
 
 block scripts