]> git.example.dev Git - binbsis50.git/commitdiff
each entry in the dropdown menu of rooms now also shows the number of players
authorLuigi Pinca <luigipinca@gmail.com>
Sat, 25 Aug 2012 12:56:37 +0000 (14:56 +0200)
committerLuigi Pinca <luigipinca@gmail.com>
Sat, 25 Aug 2012 12:56:37 +0000 (14:56 +0200)
app.js
package.json
public/static/css/style.css
public/static/js/home.js
public/static/js/room.js
views/room.jade

diff --git a/app.js b/app.js
index a0baa234a637c2285f115340cb64eede4eb380a3..b0f700a14e5724cdff387192608188e2cd67c7a5 100644 (file)
--- a/app.js
+++ b/app.js
@@ -111,7 +111,7 @@ io.set('authorization', function(data, accept) {
         }
         else if (!session) {
             var debuginfos = {
-                address: data.address,
+                address: data.headers['x-forwarded-for'],
                 ua: data.headers['user-agent'],
                 cookie: data.headers.cookie
             };
@@ -197,5 +197,6 @@ for (var i=0; i<config.rooms.length; i++) {
 }
 
 // Begin accepting connections
-server.listen(config.port);
-console.log('   binb started and listening on port ' + config.port);
+server.listen(config.port, function() {
+    console.log('binb server listening on port ' + config.port);
+});
index 7e5e533136b5b2a15971c3f960ac63adf97df655..3f4b31b2e429f94c659a5988ff7c6884c8e35aed 100644 (file)
@@ -21,5 +21,5 @@
     "start": "app.js"
   },
   "subdomain": "binb",
-  "version": "0.3.3-2"
+  "version": "0.3.3-6"
 }
\ No newline at end of file
index dc82543fc0601f2d2de042d5bbeaeb992c84974b..2cb59ebe99c0393a183b3e475704c6e799f62a98 100644 (file)
@@ -120,7 +120,7 @@ input {
 .modal-footer {
     text-align: left;
 }
-.modal-footer .btn, #total-tracks, #tracks a, #nodejitsu-logo, .footer-info {
+.modal-footer .btn, .users-counter, #total-tracks, #tracks a, #nodejitsu-logo, .footer-info {
     float: right;
 }
 .thumbnails {
@@ -207,14 +207,13 @@ input {
     text-align: center;
     text-transform: capitalize;
 }
-.dropdown-menu {
-    min-width: 140px;
-}
 .dropdown-menu a {
     padding: 2px 10px;
+    white-space: normal;
 }
-.dropdown-menu .divider {
-    margin: 4px 1px;
+.users-counter {
+    font-size: 12px;
+    margin-right: 4px;
 }
 .matched {
     color: #f3a22f;
index 23b060b3929dfbdceffbecd200efe6c92736a056..fe72972dd4a89c2572da76ecc8140424035880ea 100644 (file)
@@ -7,8 +7,8 @@ $(function() {
             }
         });
     }
-    $.get("/artworks", function(data) {
-        $(".thumbnail").each(function(index) {
+    $.get('/artworks', function(data) {
+        $('.thumbnail').each(function(index) {
             var i = index * 6;
             var j = i + 6;
             for(i; i < j; i++) {
@@ -16,16 +16,17 @@ $(function() {
             }
         });
     });
-    var socket = io.connect('http://'+window.location.host, {'reconnect':false});
-    socket.on("connect", function() {
-        socket.emit("getoverview");
-        socket.on("overview", function(data) {
+    var uri = window.location.protocol+'//'+window.location.host;
+    var socket = io.connect(uri, {'reconnect':false});
+    socket.on('connect', function() {
+        socket.emit('getoverview');
+        socket.on('overview', function(data) {
             for (var prop in data) {
-                $("#"+prop).text(data[prop]);
+                $('#'+prop).text(data[prop]);
             }
         });
-        socket.on("update", function(room, players) {
-            $("#"+room).text(players);
+        socket.on('update', function(room, players) {
+            $('#'+room).text(players);
         });
     });
 });
index c00609953a69d84899a8f21e6606cf0b2aeee3eb..396e24f28c77214c572d1f06adce6b4206db9011 100644 (file)
         , 'Try again'
     ];
 
-    var tmstrings = [
-        'Yes, you guessed the title. Who is the artist?'
-        , 'Now tell me the artist!'
-        , 'Correct, do you also know the artist?'
-    ];
-
     var states = [
         'A song is already playing, please wait for the next one...'
         , 'Game is about to start...'
         , '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;');
     };
 
-    // Prompt for name and send it.
-    var joinAnonymously = function(msg) {
-        if (/nickname\s*\=/.test(document.cookie) && !msg) {
-            var encodednickname = document.cookie.replace(/.*nickname\s*\=\s*([^;]*);?.*/, '$1');
-            nickname = decodeURIComponent(encodednickname);
-            socket.emit('joinanonymously', nickname, roomname);
-        }
-        else {
-            if (!$('body').hasClass('modal-open')) {
-                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">';
-                html += '<input id="login" class="" type="text" name="nickname" />';
-                html += '<button id="join" class="btn btn-primary">';
-                html += '<i class="icon-user icon-white"></i> Join the game</button></div>';
-
-                $(html).appendTo(DOM.modal);
-                var login = $('#login');
-                var button = $('#join');
-
-                button.click(function() {
-                    var val = $.trim(login.val());
-                    if (val !== '') {
-                        nickname = val;
-                        socket.emit('joinanonymously', nickname, roomname);
-                    }
-                    else {
-                        var txt = 'Nickname can\'t be empty.';
-                        invalidNickName('<span class="label label-important">'+txt+'</span>');
-                    }
-                    login.val('');
-                });
-
-                login.keyup(function(event) {
-                    if (event.keyCode === 13) {
-                        button.click();
-                    }
-                });
-
-                DOM.modal.modal('show');
-                DOM.modal.on('shown', function() {
-                    login.focus();
-                });
-            }
-            else {
-                $('.modal-body p').html(msg);
-                $('#login').focus();
-            }
-        }
-    };
-
-    // Submitted name was invalid
-    var invalidNickName = function(feedback) {
-        joinAnonymously(feedback+'<br/>Try with another one:');
+    // 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;
     };
 
-    /* Triggered when a logged user tries to join a room from another tab or another browser
-        and he is already in a 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');
+    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);
     };
 
-    // Successfully joined the room
-    var ready = function(usersData, trackscount, loggedin) {
-        if (!loggedin && !/nickname\s*\=/.test(document.cookie)) {
-            document.cookie = 'nickname='+encodeURIComponent(nickname)+';path=/;';
+    var addPrivate = function(usrname) {
+        if (pvtmsgto) {
+            clearPrivate();
         }
-        
-        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 {
-                        socket.emit('sendchatmsg', val);
-                    }
-                }
-                DOM.messagebox.val('');
-            }
+        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();
         });
-
-        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('');
-                    }
-            }
+        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();
+    };
 
-        DOM.guessbox.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);
 
-        socket.on('newuser', userJoin);
-        socket.on('userleft', userLeft);
-        socket.on('updateusers', updateUsers);
-        socket.on('chatmsg', getChatMessage);
-        socket.on('loadtrack', loadTrack);
-        socket.on('playtrack', playTrack);
-        socket.on('trackinfo', addTrackInfo);
-        socket.on('artistmatched', function() {
-            var feedback = amstrings[Math.floor(Math.random()*amstrings.length)];
-            addFeedback(feedback, 'correct');
-        });
-        socket.on('titlematched', function() {
-            var feedback = tmstrings[Math.floor(Math.random()*tmstrings.length)];
-            addFeedback(feedback, 'correct');
-        });
-        socket.on('bothmatched', function() {
-            var feedback = bmstrings[Math.floor(Math.random()*bmstrings.length)];
-            addFeedback(feedback, 'correct');
-        });
-        socket.on('nomatch', function() {
-            var feedback = nmstrings[Math.floor(Math.random()*nmstrings.length)];
-            addFeedback(feedback, 'wrong');
-        });
-        socket.on('stoptrying', function() {
-            addFeedback('You guessed both artist and title. Please wait...');
-        });
-        socket.on('noguesstime', function() {
-            addFeedback('You have to wait the next song...');
-        });
-        socket.on('gameover', gameOver);
-        socket.on('status', setStatus);
-        socket.emit('getstatus');
-    };
+        var html = '<li class="bordered"><img class="artwork" src="'+data.artworkUrl+'"/>';
+        html += '<div class="info"><div class="artist">'+data.artistName+'</div>';
 
-    var setStatus = function(data) {
-        if (data.status === 0) {
-            cassetteAnimation(Date.now()+data.timeleft, true);
+        var titleattr = ''
+            , trackname = data.trackName
+            , attrs = ''
+            , rp = '';
+
+        if (data.trackName.length > 45) {
+            titleattr = data.trackName.replace(/"/g, '&quot;');
+            trackname = data.trackName.substring(0, 42) + '...';
         }
-        else if (data.status === 1) {
-            loadTrack(data.previewUrl);
+        html += '<div class="title" title="'+titleattr+'">'+trackname+'</div></div>';
+
+        if (roundpoints > 0) {
+            rp = '+'+roundpoints;
+            if (roundpoints > 3) {
+                var stand = 7 - roundpoints;
+                attrs += 'class="round-rank stand'+stand+'"';
+            }
         }
-        addFeedback(states[data.status]);
-    };
+        html += '<div '+attrs+'></div><div class="round-points">'+rp+'</div>';
+        html += '<a target="_blank" href="'+data.trackViewUrl+'">';
+        html += '<img src="/static/images/itunes.png"/></a></li>';
 
-    // A new player has joined the game
-    var userJoin = function(username, usersData) {
-        var msg = username+' joined the game';
-        var joinspan = $('<span class="join"></span>');
-        joinspan.text(msg);
-        addChatEntry(joinspan);
-        updateUsers(usersData);
+        DOM.tracks.prepend($(html));
     };
 
-    // 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 addVolumeControl = function() {
+        var volumebutton = $('<div id="volume-button">'+
+            '<a class="button"><div id="icon" class="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');
 
-    // 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>');
+        var icon = volumebutton.find('#icon')
+            , volumeslider = volumebutton.find('#volume-slider')
+            , volumetotal = volumebutton.find('#volume-total')
+            , volumecurrent = volumebutton.find('#volume-current')
+            , volumehandle = volumebutton.find('#volume-handle')
+            , mouseisdown = false
+            , mouseisover = false
+            , oldvalue = 1
+            , clicked = false;
 
-            li.append(pvt, username, points, roundrank, roundpointsel, guesstime);
-            if (user.registered) {
-                var href = 'href="/user/'+encodeURIComponent(user.nickname)+'"';
-                pvt.after('<a class="registered" target="_blank" '+href+'></a>');
+        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);
+            volumehandle.css('top', totalposition.top + newtop - (volumehandle.height() / 2));
+            volumecurrent.height(totalheight - newtop );
+            volumecurrent.css('top', totalposition.top + newtop);
+        };
+
+        var handleIcon = function (volume) {
+            if (volume === 0) {
+                icon.removeClass().addClass('volume-none');
+            }
+            else if (volume <= 0.33) {
+                icon.removeClass().addClass('volume-low');
             }
-            DOM.users.append(li);
-            
-            if (pvtmsgto === user.nickname) {
-                pvt.show();
-                username.click(clearPrivate);
-                found = true;
+            else if (volume <= 0.66) {
+                icon.removeClass().addClass('volume-medium');
             }
             else {
-                username.click(function() {
-                    addPrivate($(this).text());
-                });
+                icon.removeClass().addClass('volume-high');
             }
-            
-            if (nickname === user.nickname) {
-                username.addClass('you');
-                roundpoints = user.roundpoints;
-                DOM.rank.text(i+1);
-                DOM.points.text(user.points);
+        };
+
+        var setVolume = function(volume) {
+            handleIcon(volume);
+            oldvalue = volume;
+            jplayer.jPlayer('volume', volume);
+        };
+
+        var handleVolumeMove = function(e) {
+            var totaloffset = volumetotal.offset()
+                , newy = e.pageY - totaloffset.top
+                , railheight = volumetotal.height()
+                , totalTop = parseInt(volumetotal.css('top').replace(/px/, ''), 10)
+                , volume = (railheight - newy) / railheight;
+
+            if (newy < 0) {
+                newy = 0;
             }
-            
-            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('round-rank stand'+stand);
-                        var gtime = (user.guesstime / 1000).toFixed(1);
-                        guesstime.text(gtime+' s');
-                    }
-                    username.addClass('correct');
-                }
+            else if (newy > railheight) {
+                newy = railheight;
             }
-        }
-        
-        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();
-        }
-    };
 
-    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;
+            volumehandle.css('top', totalTop + newy - (volumehandle.height() / 2));
+            volumecurrent.height(railheight - newy);
+            volumecurrent.css('top', newy + totalTop);
+
+            volume = Math.max(0, volume);
+            volume = Math.min(volume, 1);
+
+            setVolume(volume);
+
+            var d = new Date();
+            d.setTime(d.getTime() + 31536000000); // One year in milliseconds
+            document.cookie = 'volume='+volume+';path=/;expires='+d.toGMTString()+';';
+        };
+
+        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);
+        };
+
+        volumebutton.hover(function() {
+            volumeslider.show();
+            mouseisover = true;
+        }, function() {
+            mouseisover = false;
+            if (!mouseisdown) {
+                volumeslider.hide();
+            }
         });
-        el.prevAll('.private').show();
-        el.unbind('click');
-        el.click(clearPrivate);
-        pvtmsgto = usrname;
-        DOM.messagebox.focus();
-    };
 
-    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;
+        volumeslider.on('mouseover', function() {
+            mouseisover = true;
+        }).on('mousedown', function (e) {
+            handleVolumeMove(e);
+            mouseisdown = true;
+            return false;
         });
-        el.prevAll('.private').hide();
-        el.unbind('click');
-        el.click(function() {
-            addPrivate($(this).text());
+
+        $(document).on('mouseup', function (e) {
+            mouseisdown = false;
+            if (!mouseisover) {
+                volumeslider.hide();
+            }
+        }).on('mousemove', function (e) {
+            if (mouseisdown) {
+                handleVolumeMove(e);
+            }
         });
-        pvtmsgto = null;
-        DOM.messagebox.focus();
-    };
 
-    // Convert any URLs in text into clickable links.
-    var urlize = function(text) {
-        if (text.match(urlregex)) {
-            var html = '';
-            var splits = text.split(urlregex);
-            for (var i=0; i<splits.length; i++) {
-                var escapedsplit = splits[i].encodeEntities();
-                if (splits[i].match(urlregex)) {
-                    html += '<a target="_blank" href="'+escapedsplit+'">'+escapedsplit+'</a>';
-                    continue;
+        volumebutton.find('.button').click(function() {
+            if (!clicked) {
+                clicked = true;
+                if (oldvalue !== 0) {
+                    jplayer.jPlayer('volume', 0);
+                    positionVolumeHandle(0);
+                    handleIcon(0);
                 }
-                html += escapedsplit;
             }
-            return html;
-        }
-        return text.encodeEntities();
-    };
-
-    // Receive a chat message
-    var getChatMessage = function(chatmsg, from, to) {
-        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);
-    };
+            else {
+                clicked = false;
+                if (oldvalue !== 0) {
+                    jplayer.jPlayer('volume', oldvalue);
+                    positionVolumeHandle(oldvalue);
+                    handleIcon(oldvalue);
+                }
+            }
+        });
 
-    var loadTrack = function(previewUrl) {
-        jplayer.jPlayer('mute');
-        jplayer.jPlayer('setMedia', {m4a: previewUrl});
+        loadFromCookie();
     };
 
-    // 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?');
+    /* Triggered when a logged user tries to join a room from another tab or another browser
+        and he is already in a 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
             return;
         }
 
-        setTimeout(function() {cassetteAnimation(endtime, forward);}, 50);
-    };
-
-    // 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 html = '<li class="bordered"><img class="artwork" src="'+data.artworkUrl+'"/>';
-        html += '<div class="info"><div class="artist">'+data.artistName+'</div>';
-
-        var titleattr = ''
-            , trackname = data.trackName
-            , attrs = ''
-            , rp = '';
-
-        if (data.trackName.length > 45) {
-            titleattr = data.trackName.replace(/"/g, '&quot;');
-            trackname = data.trackName.substring(0, 42) + '...';
-        }
-        html += '<div class="title" title="'+titleattr+'">'+trackname+'</div></div>';
-
-        if (roundpoints > 0) {
-            rp = '+'+roundpoints;
-            if (roundpoints > 3) {
-                var stand = 7 - roundpoints;
-                attrs += 'class="round-rank stand'+stand+'"';
-            }
-        }
-        html += '<div '+attrs+'></div><div class="round-points">'+rp+'</div>';
-        html += '<a target="_blank" href="'+data.trackViewUrl+'">';
-        html += '<img src="/static/images/itunes.png"/></a></li>';
-
-        DOM.tracks.prepend($(html));
+        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
         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">';
         countDown(Date.now()+10000);
     };
 
-    // 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();
-    };
-
-    // 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;
+    // Receive a chat message
+    var getChatMessage = function(chatmsg, from, to) {
+        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.click(showChat);
     };
 
-    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);
+    // Submitted name was invalid
+    var invalidNickName = function(feedback) {
+        joinAnonymously(feedback+'<br/>Try with another one:');
     };
 
-    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;
+    // Prompt for name and send it.
+    var joinAnonymously = function(msg) {
+        if (/nickname\s*\=/.test(document.cookie) && !msg) {
+            var encodednickname = document.cookie.replace(/.*nickname\s*\=\s*([^;]*);?.*/, '$1');
+            nickname = decodeURIComponent(encodednickname);
+            socket.emit('joinanonymously', nickname, roomname);
         }
-        DOM.feedback.text(txt);
-    };
+        else {
+            if (!$('body').hasClass('modal-open')) {
+                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">';
+                html += '<input id="login" class="" type="text" name="nickname" />';
+                html += '<button id="join" class="btn btn-primary">';
+                html += '<i class="icon-user icon-white"></i> Join the game</button></div>';
 
-    var addVolumeControl = function() {
-        var volumebutton = $('<div id="volume-button">'+
-            '<a class="button"><div id="icon" class="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');
+                $(html).appendTo(DOM.modal);
+                var login = $('#login');
+                var button = $('#join');
 
-        var icon = volumebutton.find('#icon')
-            , volumeslider = volumebutton.find('#volume-slider')
-            , volumetotal = volumebutton.find('#volume-total')
-            , volumecurrent = volumebutton.find('#volume-current')
-            , volumehandle = volumebutton.find('#volume-handle')
-            , mouseisdown = false
-            , mouseisover = false
-            , oldvalue = 1
-            , clicked = false;
+                button.click(function() {
+                    var val = $.trim(login.val());
+                    if (val !== '') {
+                        nickname = 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 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);
-            volumehandle.css('top', totalposition.top + newtop - (volumehandle.height() / 2));
-            volumecurrent.height(totalheight - newtop );
-            volumecurrent.css('top', totalposition.top + newtop);
-        };
+                login.keyup(function(event) {
+                    if (event.keyCode === 13) {
+                        button.click();
+                    }
+                });
 
-        var handleIcon = function (volume) {
-            if (volume === 0) {
-                icon.removeClass().addClass('volume-none');
+                DOM.modal.modal('show');
+                DOM.modal.on('shown', function() {
+                    login.focus();
+                });
             }
-            else if (volume <= 0.33) {
-                icon.removeClass().addClass('volume-low');
+            else {
+                $('.modal-body p').html(msg);
+                $('#login').focus();
             }
-            else if (volume <= 0.66) {
-                icon.removeClass().addClass('volume-medium');
+        }
+    };
+
+    var jplayerReady = function() {
+        socket.emit('loggedin', function(data) {
+            if (data) {
+                nickname = data;
+                return socket.emit('joinroom', roomname);
             }
-            else {
-                icon.removeClass().addClass('volume-high');
+            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 setVolume = function(volume) {
-            handleIcon(volume);
-            oldvalue = volume;
-            jplayer.jPlayer('volume', volume);
-        };
+    var loadTrack = function(previewUrl) {
+        jplayer.jPlayer('mute');
+        jplayer.jPlayer('setMedia', {m4a: previewUrl});
+    };
 
-        var handleVolumeMove = function(e) {
-            var totaloffset = volumetotal.offset()
-                , newy = e.pageY - totaloffset.top
-                , railheight = volumetotal.height()
-                , totalTop = parseInt(volumetotal.css('top').replace(/px/, ''), 10)
-                , volume = (railheight - newy) / railheight;
+    // 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?');
+    };
 
-            if (newy < 0) {
-                newy = 0;
+    // Successfully joined the room
+    var ready = function(usersData, trackscount, loggedin) {
+        if (!loggedin && !/nickname\s*\=/.test(document.cookie)) {
+            document.cookie = 'nickname='+encodeURIComponent(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 {
+                        socket.emit('sendchatmsg', val);
+                    }
+                }
+                DOM.messagebox.val('');
             }
-            else if (newy > railheight) {
-                newy = railheight;
+        });
+
+        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('');
+                    }
             }
+        });
 
-            volumehandle.css('top', totalTop + newy - (volumehandle.height() / 2));
-            volumecurrent.height(railheight - newy);
-            volumecurrent.css('top', newy + totalTop);
-
-            volume = Math.max(0, volume);
-            volume = Math.min(volume, 1);
-
-            setVolume(volume);
+        DOM.guessbox.focus();
 
-            var d = new Date();
-            d.setTime(d.getTime() + 31536000000); // One year in milliseconds
-            document.cookie = 'volume='+volume+';path=/;expires='+d.toGMTString()+';';
-        };
+        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('status', setStatus);
+        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');
+    };
 
-        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;
+    // 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]);
             }
-            positionVolumeHandle(1);
-        };
+        }
+    };
 
-        volumebutton.hover(function() {
-            volumeslider.show();
-            mouseisover = true;
-        }, function() {
-            mouseisover = false;
-            if (!mouseisdown) {
-                volumeslider.hide();
-            }
+    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);
         });
+    };
 
-        volumeslider.on('mouseover', function() {
-            mouseisover = true;
-        }).on('mousedown', function (e) {
-            handleVolumeMove(e);
-            mouseisdown = true;
-            return false;
+    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);
+    };
 
-        $(document).on('mouseup', function (e) {
-            mouseisdown = false;
-            if (!mouseisover) {
-                volumeslider.hide();
+    // 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/'+encodeURIComponent(user.nickname)+'"';
+                pvt.after('<a class="registered" target="_blank" '+href+'></a>');
             }
-        }).on('mousemove', function (e) {
-            if (mouseisdown) {
-                handleVolumeMove(e);
+            DOM.users.append(li);
+            
+            if (pvtmsgto === user.nickname) {
+                pvt.show();
+                username.click(clearPrivate);
+                found = true;
             }
-        });
-
-        volumebutton.find('.button').click(function() {
-            if (!clicked) {
-                clicked = true;
-                if (oldvalue !== 0) {
-                    jplayer.jPlayer('volume', 0);
-                    positionVolumeHandle(0);
-                    handleIcon(0);
+            else {
+                username.click(function() {
+                    addPrivate($(this).text());
+                });
+            }
+            
+            if (nickname === user.nickname) {
+                username.addClass('you');
+                roundpoints = user.roundpoints;
+                DOM.rank.text(i+1);
+                DOM.points.text(user.points);
+            }
+            
+            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('round-rank stand'+stand);
+                        var gtime = (user.guesstime / 1000).toFixed(1);
+                        guesstime.text(gtime+' s');
+                    }
+                    username.addClass('correct');
                 }
             }
-            else {
-                clicked = false;
-                if (oldvalue !== 0) {
-                    jplayer.jPlayer('volume', oldvalue);
-                    positionVolumeHandle(oldvalue);
-                    handleIcon(oldvalue);
+        }
+        
+        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();
+        }
+    };
+
+    var updateRoomsOverview = function(room, players) {
+        if (room !== roomname) {
+            DOM.userscounters[room].text(players);
+        }
+    };
+
+    // Convert any URLs in text into clickable links.
+    var urlize = function(text) {
+        if (text.match(urlregex)) {
+            var html = '';
+            var splits = text.split(urlregex);
+            for (var i=0; i<splits.length; i++) {
+                var escapedsplit = splits[i].encodeEntities();
+                if (splits[i].match(urlregex)) {
+                    html += '<a target="_blank" href="'+escapedsplit+'">'+escapedsplit+'</a>';
+                    continue;
                 }
+                html += escapedsplit;
             }
-        });
+            return html;
+        }
+        return text.encodeEntities();
+    };
 
-        loadFromCookie();
+    // A new player has joined the game
+    var userJoin = function(username, usersData) {
+        var msg = username+' joined the game';
+        var joinspan = $('<span class="join"></span>');
+        joinspan.text(msg);
+        addChatEntry(joinspan);
+        updateUsers(usersData);
     };
 
-    var setVariables = function() {
-        DOM.modal = $('#modal');
-        DOM.guessbox = $('#guess');
-        DOM.users = $('#users');
-        DOM.rank = $('#summary .rank');
-        DOM.points = $('#summary .points');
-        DOM.messagebox = $('#message');
-        DOM.recipient = $('#recipient');
-        DOM.tracks = $('#tracks');
-        DOM.track = $('#summary .track');
-        DOM.progress = $('#progress');
-        DOM.cassettewheels = $('#cassette .wheel');
-        DOM.tapeleft = $('#tape-left');
-        DOM.taperight = $('#tape-right');
-        DOM.countdown = $('#countdown');
-        DOM.chat = $('#chat');
-        DOM.feedback = $('#feedback');
-        DOM.togglechat = $('#toggle-chat');
-        DOM.chatwrapper = $('#chat-outer-wrapper');
+    // 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);
     };
 
     // Set up the room.
                 }
             });
         }
-        socket = io.connect('http://'+window.location.host, {'reconnect':false});
+        var uri = window.location.protocol+'//'+window.location.host;
+        socket = io.connect(uri, {'reconnect':false});
         socket.on('connect', function() {
             jplayer = $('#player').jPlayer({
-                ready: function() {
-                    socket.emit('loggedin', function(data) {
-                        if (data) {
-                            nickname = data;
-                            return socket.emit('joinroom', roomname);
-                        }
-                        joinAnonymously();
-                    });
-                    if (!$.jPlayer.platform.mobile && !$.jPlayer.platform.tablet) {
-                        addVolumeControl();
-                    }
-                    else {
-                        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();
-                            }
-                        });
-                    }
-                },
+                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('overview', roomsOverview);
+            socket.on('ready', ready);
+            socket.on('update', updateRoomsOverview);
+            socket.emit('getoverview');
         });
-        socket.on('alreadyinaroom', alreadyInARoom);
-        socket.on('invalidnickname', invalidNickName);
-        socket.on('ready', ready);
-        socket.on('disconnect', disconnect);
     });
+
 })();
index 73917cca776ad320734cf6f3a2079e46c653c9c3..62f56443bd998b89938ad2d2c8a2ebc8c3e98fca 100644 (file)
@@ -27,7 +27,10 @@ html
                                 each item in rooms
                                     if item != roomname
                                         li
-                                            a(href=item) #{item}
+                                            a(href=item)
+                                                span.room-name #{item}
+                                                i.icon-user.pull-right
+                                                span.users-counter
                         if (typeof(loggedin) !== "undefined")
                             li.dropdown
                                 a.dropdown-toggle(data-toggle="dropdown",