-# Binb #
+# binb #
-Binb is a simple, realtime, multiplayer, competitive music listening game.
+binb is a simple, realtime, multiplayer, competitive music listening game.
To play the game: [http://binb.nodejitsu.com](http://binb.nodejitsu.com)
Ideal setup is a browser with websocket support and able to decode .m4a format natively.
-For this reason Binb is optimized for Google Chrome but also works in all major browsers (if version is recently enough).
+For this reason binb is optimized for Google Chrome but also works in all major browsers (if version is recently enough).
## Shout-Outs ##
## Copyright and license ##
-Binb is released under the MIT license. See LICENSE for details.
+binb is released under the MIT license. See LICENSE for details.
exports.configure = function() {
this.port = 80;
- this.redisurl = '';
+ this.songsdburl = '';
+ this.usersdburl = '';
+ this.sessionsecret = '';
this.songsinarun = 15;
this.threshold = 2; // Edit distance threshold
this.rooms = ["pop", "rock", "mixed"];
"name": "binb",
"dependencies": {
"async": "latest",
+ "canvas": "latest",
+ "connect": "latest",
+ "connect-redis": "latest",
"express": "latest",
+ "express-form": "latest",
"jade": "latest",
"redis-url": "latest",
"socket.io": "latest"
"engines": {
"node": "0.6.x"
},
- "version": "0.2.0-14"
+ "version": "0.3.0-1"
}
\ No newline at end of file
.label-info:hover{background-color:#2d6987;}
.label-inverse{background-color:#333333;}
.label-inverse:hover{background-color:#1a1a1a;}
+table{max-width:100%;border-collapse:collapse;border-spacing:0;background-color:transparent;}
+.table{width:100%;margin-bottom:18px;}.table th,.table td{padding:8px;line-height:18px;text-align:left;vertical-align:top;border-top:1px solid #dddddd;}
+.table th{font-weight:bold;}
+.table thead th{vertical-align:bottom;}
+.table colgroup+thead tr:first-child th,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child th,.table thead:first-child tr:first-child td{border-top:0;}
+.table tbody+tbody{border-top:2px solid #dddddd;}
+.table-condensed th,.table-condensed td{padding:4px 5px;}
+.table-bordered{border:1px solid #dddddd;border-left:0;border-collapse:separate;*border-collapse:collapsed;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.table-bordered th,.table-bordered td{border-left:1px solid #dddddd;}
+.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0;}
+.table-bordered thead:first-child tr:first-child th:first-child,.table-bordered tbody:first-child tr:first-child td:first-child{-webkit-border-radius:4px 0 0 0;-moz-border-radius:4px 0 0 0;border-radius:4px 0 0 0;}
+.table-bordered thead:first-child tr:first-child th:last-child,.table-bordered tbody:first-child tr:first-child td:last-child{-webkit-border-radius:0 4px 0 0;-moz-border-radius:0 4px 0 0;border-radius:0 4px 0 0;}
+.table-bordered thead:last-child tr:last-child th:first-child,.table-bordered tbody:last-child tr:last-child td:first-child{-webkit-border-radius:0 0 0 4px;-moz-border-radius:0 0 0 4px;border-radius:0 0 0 4px;}
+.table-bordered thead:last-child tr:last-child th:last-child,.table-bordered tbody:last-child tr:last-child td:last-child{-webkit-border-radius:0 0 4px 0;-moz-border-radius:0 0 4px 0;border-radius:0 0 4px 0;}
+.table-striped tbody tr:nth-child(odd) td,.table-striped tbody tr:nth-child(odd) th{background-color:#f9f9f9;}
+.table tbody tr:hover td,.table tbody tr:hover th{background-color:#f5f5f5;}
+table .span1{float:none;width:24px;margin-left:0;}
+table .span2{float:none;width:84px;margin-left:0;}
+table .span3{float:none;width:144px;margin-left:0;}
+table .span4{float:none;width:204px;margin-left:0;}
+table .span5{float:none;width:264px;margin-left:0;}
+table .span6{float:none;width:324px;margin-left:0;}
+table .span7{float:none;width:384px;margin-left:0;}
+table .span8{float:none;width:444px;margin-left:0;}
+table .span9{float:none;width:504px;margin-left:0;}
+table .span10{float:none;width:564px;margin-left:0;}
+table .span11{float:none;width:624px;margin-left:0;}
+table .span12{float:none;width:684px;margin-left:0;}
+table .span13{float:none;width:744px;margin-left:0;}
+table .span14{float:none;width:804px;margin-left:0;}
+table .span15{float:none;width:864px;margin-left:0;}
+table .span16{float:none;width:924px;margin-left:0;}
+table .span17{float:none;width:984px;margin-left:0;}
+table .span18{float:none;width:1044px;margin-left:0;}
+table .span19{float:none;width:1104px;margin-left:0;}
+table .span20{float:none;width:1164px;margin-left:0;}
+table .span21{float:none;width:1224px;margin-left:0;}
+table .span22{float:none;width:1284px;margin-left:0;}
+table .span23{float:none;width:1344px;margin-left:0;}
+table .span24{float:none;width:1404px;margin-left:0;}
form{margin:0 0 18px;}
fieldset{padding:0;margin:0;border:0;}
legend{display:block;width:100%;padding:0;margin-bottom:27px;font-size:19.5px;line-height:36px;color:#333333;border:0;border-bottom:1px solid #eee;}legend small{font-size:13.5px;color:#999999;}
.btn-small .caret{margin-top:6px;}
.btn-large .caret{margin-top:6px;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000000;}
.btn-primary .caret,.btn-warning .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;opacity:0.75;filter:alpha(opacity=75);}
+.nav{margin-left:0;margin-bottom:18px;list-style:none;}
+.nav>li>a{display:block;}
+.nav>li>a:hover{text-decoration:none;background-color:#eeeeee;}
+.nav .nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:18px;color:#999999;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);text-transform:uppercase;}
+.nav li+.nav-header{margin-top:9px;}
+.nav-list{padding-left:15px;padding-right:15px;margin-bottom:0;}
+.nav-list>li>a,.nav-list .nav-header{margin-left:-15px;margin-right:-15px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);}
+.nav-list>li>a{padding:3px 15px;}
+.nav-list>.active>a,.nav-list>.active>a:hover{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.2);background-color:#0088cc;}
+.nav-list [class^="icon-"]{margin-right:2px;}
+.nav-list .divider{height:1px;margin:8px 1px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #ffffff;*width:100%;*margin:-5px 0 5px;}
+.nav-tabs,.nav-pills{*zoom:1;}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;content:"";}
+.nav-tabs:after,.nav-pills:after{clear:both;}
+.nav-tabs>li,.nav-pills>li{float:left;}
+.nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px;}
+.nav-tabs{border-bottom:1px solid #ddd;}
+.nav-tabs>li{margin-bottom:-1px;}
+.nav-tabs>li>a{padding-top:8px;padding-bottom:8px;line-height:18px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;}.nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #dddddd;}
+.nav-tabs>.active>a,.nav-tabs>.active>a:hover{color:#555555;background-color:#ffffff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default;}
+.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;}
+.nav-pills>.active>a,.nav-pills>.active>a:hover{color:#ffffff;background-color:#0088cc;}
+.nav-stacked>li{float:none;}
+.nav-stacked>li>a{margin-right:0;}
+.nav-tabs.nav-stacked{border-bottom:0;}
+.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
+.nav-tabs.nav-stacked>li:first-child>a{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;}
+.nav-tabs.nav-stacked>li:last-child>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;}
+.nav-tabs.nav-stacked>li>a:hover{border-color:#ddd;z-index:2;}
+.nav-pills.nav-stacked>li>a{margin-bottom:3px;}
+.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px;}
+.nav-tabs .dropdown-menu,.nav-pills .dropdown-menu{margin-top:1px;border-width:1px;}
+.nav-pills .dropdown-menu{-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
+.nav-tabs .dropdown-toggle .caret,.nav-pills .dropdown-toggle .caret{border-top-color:#0088cc;border-bottom-color:#0088cc;margin-top:6px;}
+.nav-tabs .dropdown-toggle:hover .caret,.nav-pills .dropdown-toggle:hover .caret{border-top-color:#005580;border-bottom-color:#005580;}
+.nav-tabs .active .dropdown-toggle .caret,.nav-pills .active .dropdown-toggle .caret{border-top-color:#333333;border-bottom-color:#333333;}
+.nav>.dropdown.active>a:hover{color:#000000;cursor:pointer;}
+.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>.open.active>a:hover{color:#ffffff;background-color:#999999;border-color:#999999;}
+.nav .open .caret,.nav .open.active .caret,.nav .open a:hover .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;opacity:1;filter:alpha(opacity=100);}
+.tabs-stacked .open>a:hover{border-color:#999999;}
+.tabbable{*zoom:1;}.tabbable:before,.tabbable:after{display:table;content:"";}
+.tabbable:after{clear:both;}
+.tab-content{display:table;width:100%;}
+.tabs-below .nav-tabs,.tabs-right .nav-tabs,.tabs-left .nav-tabs{border-bottom:0;}
+.tab-content>.tab-pane,.pill-content>.pill-pane{display:none;}
+.tab-content>.active,.pill-content>.active{display:block;}
+.tabs-below .nav-tabs{border-top:1px solid #ddd;}
+.tabs-below .nav-tabs>li{margin-top:-1px;margin-bottom:0;}
+.tabs-below .nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;}.tabs-below .nav-tabs>li>a:hover{border-bottom-color:transparent;border-top-color:#ddd;}
+.tabs-below .nav-tabs .active>a,.tabs-below .nav-tabs .active>a:hover{border-color:transparent #ddd #ddd #ddd;}
+.tabs-left .nav-tabs>li,.tabs-right .nav-tabs>li{float:none;}
+.tabs-left .nav-tabs>li>a,.tabs-right .nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px;}
+.tabs-left .nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd;}
+.tabs-left .nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px;}
+.tabs-left .nav-tabs>li>a:hover{border-color:#eeeeee #dddddd #eeeeee #eeeeee;}
+.tabs-left .nav-tabs .active>a,.tabs-left .nav-tabs .active>a:hover{border-color:#ddd transparent #ddd #ddd;*border-right-color:#ffffff;}
+.tabs-right .nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd;}
+.tabs-right .nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0;}
+.tabs-right .nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #eeeeee #dddddd;}
+.tabs-right .nav-tabs .active>a,.tabs-right .nav-tabs .active>a:hover{border-color:#ddd #ddd #ddd transparent;*border-left-color:#ffffff;}
+.navbar{*position:relative;*z-index:2;overflow:visible;margin-bottom:18px;}
+.navbar-inner{padding-left:20px;padding-right:20px;background-color:#2c2c2c;background-image:-moz-linear-gradient(top, #333333, #222222);background-image:-ms-linear-gradient(top, #333333, #222222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222));background-image:-webkit-linear-gradient(top, #333333, #222222);background-image:-o-linear-gradient(top, #333333, #222222);background-image:linear-gradient(top, #333333, #222222);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);}
+.navbar .container{width:auto;}
+.btn-navbar{display:none;float:right;padding:7px 10px;margin-left:5px;margin-right:5px;background-color:#2c2c2c;background-image:-moz-linear-gradient(top, #333333, #222222);background-image:-ms-linear-gradient(top, #333333, #222222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222));background-image:-webkit-linear-gradient(top, #333333, #222222);background-image:-o-linear-gradient(top, #333333, #222222);background-image:linear-gradient(top, #333333, #222222);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);border-color:#222222 #222222 #000000;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);}.btn-navbar:hover,.btn-navbar:active,.btn-navbar.active,.btn-navbar.disabled,.btn-navbar[disabled]{background-color:#222222;}
+.btn-navbar:active,.btn-navbar.active{background-color:#080808 \9;}
+.btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);-moz-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);}
+.btn-navbar .icon-bar+.icon-bar{margin-top:3px;}
+.nav-collapse.collapse{height:auto;}
+.navbar{color:#999999;}.navbar .brand:hover{text-decoration:none;}
+.navbar .brand{float:left;display:block;padding:8px 20px 12px;margin-left:-20px;font-size:20px;font-weight:200;line-height:1;color:#ffffff;}
+.navbar .navbar-text{margin-bottom:0;line-height:40px;}
+.navbar .btn,.navbar .btn-group{margin-top:5px;}
+.navbar .btn-group .btn{margin-top:0;}
+.navbar-form{margin-bottom:0;*zoom:1;}.navbar-form:before,.navbar-form:after{display:table;content:"";}
+.navbar-form:after{clear:both;}
+.navbar-form input,.navbar-form select,.navbar-form .radio,.navbar-form .checkbox{margin-top:5px;}
+.navbar-form input,.navbar-form select{display:inline-block;margin-bottom:0;}
+.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px;}
+.navbar-form .input-append,.navbar-form .input-prepend{margin-top:6px;white-space:nowrap;}.navbar-form .input-append input,.navbar-form .input-prepend input{margin-top:0;}
+.navbar-search{position:relative;float:left;margin-top:6px;margin-bottom:0;}.navbar-search .search-query{padding:4px 9px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;color:#ffffff;background-color:#626262;border:1px solid #151515;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15);-webkit-transition:none;-moz-transition:none;-ms-transition:none;-o-transition:none;transition:none;}.navbar-search .search-query:-moz-placeholder{color:#cccccc;}
+.navbar-search .search-query::-webkit-input-placeholder{color:#cccccc;}
+.navbar-search .search-query:focus,.navbar-search .search-query.focused{padding:5px 10px;color:#333333;text-shadow:0 1px 0 #ffffff;background-color:#ffffff;border:0;-webkit-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);-moz-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);box-shadow:0 0 3px rgba(0, 0, 0, 0.15);outline:0;}
+.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;margin-bottom:0;}
+.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding-left:0;padding-right:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
+.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px;}
+.navbar-fixed-top{top:0;}
+.navbar-fixed-bottom{bottom:0;}
+.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0;}
+.navbar .nav.pull-right{float:right;}
+.navbar .nav>li{display:block;float:left;}
+.navbar .nav>li>a{float:none;padding:10px 10px 11px;line-height:19px;color:#999999;text-decoration:none;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);}
+.navbar .nav>li>a:hover{background-color:transparent;color:#ffffff;text-decoration:none;}
+.navbar .nav .active>a,.navbar .nav .active>a:hover{color:#ffffff;text-decoration:none;background-color:#222222;}
+.navbar .divider-vertical{height:40px;width:1px;margin:0 9px;overflow:hidden;background-color:#222222;border-right:1px solid #333333;}
+.navbar .nav.pull-right{margin-left:10px;margin-right:0;}
+.navbar .dropdown-menu{margin-top:1px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.navbar .dropdown-menu:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-bottom-color:rgba(0, 0, 0, 0.2);position:absolute;top:-7px;left:9px;}
+.navbar .dropdown-menu:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #ffffff;position:absolute;top:-6px;left:10px;}
+.navbar-fixed-bottom .dropdown-menu:before{border-top:7px solid #ccc;border-top-color:rgba(0, 0, 0, 0.2);border-bottom:0;bottom:-7px;top:auto;}
+.navbar-fixed-bottom .dropdown-menu:after{border-top:6px solid #ffffff;border-bottom:0;bottom:-6px;top:auto;}
+.navbar .nav .dropdown-toggle .caret,.navbar .nav .open.dropdown .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;}
+.navbar .nav .active .caret{opacity:1;filter:alpha(opacity=100);}
+.navbar .nav .open>.dropdown-toggle,.navbar .nav .active>.dropdown-toggle,.navbar .nav .open.active>.dropdown-toggle{background-color:transparent;}
+.navbar .nav .active>.dropdown-toggle:hover{color:#ffffff;}
+.navbar .nav.pull-right .dropdown-menu,.navbar .nav .dropdown-menu.pull-right{left:auto;right:0;}.navbar .nav.pull-right .dropdown-menu:before,.navbar .nav .dropdown-menu.pull-right:before{left:auto;right:12px;}
+.navbar .nav.pull-right .dropdown-menu:after,.navbar .nav .dropdown-menu.pull-right:after{left:auto;right:13px;}
.thumbnails{margin-left:-20px;list-style:none;*zoom:1;}.thumbnails:before,.thumbnails:after{display:table;content:"";}
.thumbnails:after{clear:both;}
.thumbnails>li{float:left;margin:0 0 18px 20px;}
a.thumbnail:hover{border-color:#0088cc;-webkit-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);-moz-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);}
.thumbnail>img{display:block;max-width:100%;margin-left:auto;margin-right:auto;}
.thumbnail .caption{padding:9px;}
+.alert{padding:8px 35px 8px 14px;margin-bottom:18px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;color:#c09853;}
+.alert-heading{color:inherit;}
+.alert .close{position:relative;top:-2px;right:-21px;line-height:18px;}
+.alert-success{background-color:#dff0d8;border-color:#d6e9c6;color:#468847;}
+.alert-danger,.alert-error{background-color:#f2dede;border-color:#eed3d7;color:#b94a48;}
+.alert-info{background-color:#d9edf7;border-color:#bce8f1;color:#3a87ad;}
+.alert-block{padding-top:14px;padding-bottom:14px;}
+.alert-block>p,.alert-block>ul{margin-bottom:0;}
+.alert-block p+p{margin-top:5px;}
.modal-open .dropdown-menu{z-index:2050;}
.modal-open .dropdown.open{*z-index:2050;}
.modal-open .popover{z-index:2060;}
.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000000;content:"\2191";}
.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px;}
.typeahead{margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
+.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #eee;border:1px solid rgba(0, 0, 0, 0.05);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);}.well blockquote{border-color:#ddd;border-color:rgba(0, 0, 0, 0.15);}
+.well-large{padding:24px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}
+.well-small{padding:9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
+.close{float:right;font-size:20px;font-weight:bold;line-height:18px;color:#000000;text-shadow:0 1px 0 #ffffff;opacity:0.2;filter:alpha(opacity=20);}.close:hover{color:#000000;text-decoration:none;opacity:0.4;filter:alpha(opacity=40);cursor:pointer;}
+.pull-right{float:right;}
+.pull-left{float:left;}
+.hide{display:none;}
+.show{display:block;}
+.invisible{visibility:hidden;}
.fade{-webkit-transition:opacity 0.15s linear;-moz-transition:opacity 0.15s linear;-ms-transition:opacity 0.15s linear;-o-transition:opacity 0.15s linear;transition:opacity 0.15s linear;opacity:0;}.fade.in{opacity:1;}
.collapse{-webkit-transition:height 0.35s ease;-moz-transition:height 0.35s ease;-ms-transition:height 0.35s ease;-o-transition:height 0.35s ease;transition:height 0.35s ease;position:relative;overflow:hidden;height:0;}.collapse.in{height:auto;}
body {
background: url('/static/images/bg.jpg') repeat-x scroll 0 0 #F5F6F7;
+ padding-top:45px;
}
section {
margin-top:30px;
}
+.motto {
+ color: #999999;
+ font-size:13px;
+ text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);
+}
+.navbar .navbar-text {
+ line-height:19px;
+ padding: 10px 10px 11px;
+}
+.form-horizontal .control-group {
+ margin-bottom: 10px;
+}
+.form-horizontal .control-label {
+ width: 100px;
+}
+.form-horizontal .controls {
+ margin-left: 120px;
+}
+form .clearfix {
+ margin-bottom: 10px;
+}
+.well {
+ background-color: #DDDDDD;
+ margin-bottom: 18px;
+}
+.alert {
+ margin-bottom: 9px;
+}
+#signup-button {
+ margin-left: 120px;
+ margin-top: 9px;
+}
+#captcha-input {
+ width: 126px;
+}
+#captcha {
+ margin-right:20px;
+}
.page-header {
padding: 0;
margin: 0 0 17px 0;
.modal-footer {
text-align: left;
}
-#join {
+.modal-footer .btn {
float: right;
}
.thumbnails {
width: 70px;
height: 70px;
}
+.profile {
+ font-size: 24px;
+ font-weight: bold;
+ line-height: 32px;
+}
+.profile .img {
+ width: 32px;
+ height: 32px;
+ float: left;
+ margin-right: 5px;
+ background: url('/static/images/sprites.png') no-repeat 0px -56px;
+}
+.stats {
+ border: 1px solid #ccc;
+ border-left: 0;
+ margin-top:8px;
+}
+.stats td {
+ border-left: 1px solid #ccc;
+ border-top: 1px solid #ccc;
+ vertical-align: middle;
+}
+.stats tbody tr:nth-child(odd) td {
+ background-color: #ddd;
+}
+.stats tbody tr:hover td {
+ background-color: #dadada;
+}
.room {
height: 25px;
line-height: 25px;
text-align: center;
text-transform: capitalize;
}
-.btn-group {
- float: right;
- margin: 21px 10px 0 0;
-}
.dropdown-menu {
min-width: 140px;
}
.matched {
color: #f3a22f;
}
-.gameover {
- padding-left:32px;
- height:32px;
- line-height:32px;
+.cups, .medals {
+ margin-left: auto;
+ margin-right: auto;
}
-.gameover .name {
+.cups {
+ width: 16px;
+ height: 16px;
+}
+.medals {
+ width: 32px;
+ height: 32px;
+}
+.rank1 {
+ background: url('/static/images/sprites.png') no-repeat -32px -16px;
+}
+.rank2 {
+ background: url('/static/images/sprites.png') no-repeat -32px -48px;
+}
+.rank3 {
+ background: url('/static/images/sprites.png') no-repeat -32px -80px;
+}
+.scoreboard th, .scoreboard td {
+ vertical-align: middle;
+ text-align: center;
+}
+.scoreboard .name {
font-weight:bold;
}
.relative {
width: 24px;
height:24px;
top: 49px;
- background: url('/static/images/wheel.png') no-repeat 0 0;
+ background: url('/static/images/sprites.png') no-repeat 0px -32px;
}
#wheel-left {
left:51px;
text-align: center;
margin-bottom:18px;
}
+#users-wrapper {
+ margin-left: 120px;
+ width: 300px;
+}
#users {
+ padding-left:20px;
margin-bottom: 90px;
max-height: 380px;
overflow: auto;
}
#users .private {
display: none;
+ font-size: 9.75px;
+ padding: 2px 4px;
+ position: absolute;
+ left: -19px;
}
#users .private, #users .name, .gameover .name {
margin-right: 4px;
}
-#users .label {
- font-size:9.75px;
- padding: 2px 4px;
- line-height: 18px;
- vertical-align: top;
+.registered, #users .round-rank {
+ display: inline-block;
+ vertical-align: middle;
+}
+.registered, .round-rank {
+ height: 16px;
+ width: 16px;
+ margin-right:2px;
}
-#users .name {
+.registered {
+ background: url('/static/images/sprites.png') no-repeat 0px -16px;
+}
+.registered:hover {
+ background: url('/static/images/sprites.png') no-repeat -16px -16px;
+}
+#users .name, .registered {
cursor: pointer;
}
#users .you {
#users .points, #users .round-points {
margin-right: 10px;
}
-.round-rank {
- height:16px;
- width:16px;
- margin-right:2px;
-}
.stand1 {
background: url('/static/images/sprites.png') no-repeat 0 0;
}
.stand3 {
background: url('/static/images/sprites.png') no-repeat -32px 0;
}
-#users .round-rank {
- display:inline-block;
- vertical-align: middle;
-}
#users .guess-time {
font-size: 11px;
+ line-height: 1px;
}
#toggle-chat {
position: absolute;
/**
* Bootstrap.js by @fat & @mdo
-* plugins: bootstrap-transition.js, bootstrap-modal.js, bootstrap-dropdown.js
+* plugins: bootstrap-transition.js, bootstrap-modal.js, bootstrap-dropdown.js, bootstrap-alert.js
* Copyright 2012 Twitter, Inc.
* http://www.apache.org/licenses/LICENSE-2.0.txt
*/
-!function(a){a(function(){a.support.transition=function(){var b=document.body||document.documentElement,c=b.style,d=c.transition!==undefined||c.WebkitTransition!==undefined||c.MozTransition!==undefined||c.MsTransition!==undefined||c.OTransition!==undefined;return d&&{end:function(){var b="TransitionEnd";return a.browser.webkit?b="webkitTransitionEnd":a.browser.mozilla?b="transitionend":a.browser.opera&&(b="oTransitionEnd"),b}()}}()})}(window.jQuery),!function(a){function c(){var b=this,c=setTimeout(function(){b.$element.off(a.support.transition.end),d.call(b)},500);this.$element.one(a.support.transition.end,function(){clearTimeout(c),d.call(b)})}function d(a){this.$element.hide().trigger("hidden"),e.call(this)}function e(b){var c=this,d=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var e=a.support.transition&&d;this.$backdrop=a('<div class="modal-backdrop '+d+'" />').appendTo(document.body),this.options.backdrop!="static"&&this.$backdrop.click(a.proxy(this.hide,this)),e&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),e?this.$backdrop.one(a.support.transition.end,b):b()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(a.support.transition.end,a.proxy(f,this)):f.call(this)):b&&b()}function f(){this.$backdrop.remove(),this.$backdrop=null}function g(){var b=this;this.isShown&&this.options.keyboard?a(document).on("keyup.dismiss.modal",function(a){a.which==27&&b.hide()}):this.isShown||a(document).off("keyup.dismiss.modal")}var b=function(b,c){this.options=c,this.$element=a(b).delegate('[data-dismiss="modal"]',"click.dismiss.modal",a.proxy(this.hide,this))};b.prototype={constructor:b,toggle:function(){return this[this.isShown?"hide":"show"]()},show:function(){var b=this;if(this.isShown)return;a("body").addClass("modal-open"),this.isShown=!0,this.$element.trigger("show"),g.call(this),e.call(this,function(){var c=a.support.transition&&b.$element.hasClass("fade");!b.$element.parent().length&&b.$element.appendTo(document.body),b.$element.show(),c&&b.$element[0].offsetWidth,b.$element.addClass("in"),c?b.$element.one(a.support.transition.end,function(){b.$element.trigger("shown")}):b.$element.trigger("shown")})},hide:function(b){b&&b.preventDefault();if(!this.isShown)return;var e=this;this.isShown=!1,a("body").removeClass("modal-open"),g.call(this),this.$element.trigger("hide").removeClass("in"),a.support.transition&&this.$element.hasClass("fade")?c.call(this):d.call(this)}},a.fn.modal=function(c){return this.each(function(){var d=a(this),e=d.data("modal"),f=a.extend({},a.fn.modal.defaults,d.data(),typeof c=="object"&&c);e||d.data("modal",e=new b(this,f)),typeof c=="string"?e[c]():f.show&&e.show()})},a.fn.modal.defaults={backdrop:!0,keyboard:!0,show:!0},a.fn.modal.Constructor=b,a(function(){a("body").on("click.modal.data-api",'[data-toggle="modal"]',function(b){var c=a(this),d,e=a(c.attr("data-target")||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,"")),f=e.data("modal")?"toggle":a.extend({},e.data(),c.data());b.preventDefault(),e.modal(f)})})}(window.jQuery),!function(a){function d(){a(b).parent().removeClass("open")}var b='[data-toggle="dropdown"]',c=function(b){var c=a(b).on("click.dropdown.data-api",this.toggle);a("html").on("click.dropdown.data-api",function(){c.parent().removeClass("open")})};c.prototype={constructor:c,toggle:function(b){var c=a(this),e=c.attr("data-target"),f,g;return e||(e=c.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,"")),f=a(e),f.length||(f=c.parent()),g=f.hasClass("open"),d(),!g&&f.toggleClass("open"),!1}},a.fn.dropdown=function(b){return this.each(function(){var d=a(this),e=d.data("dropdown");e||d.data("dropdown",e=new c(this)),typeof b=="string"&&e[b].call(d)})},a.fn.dropdown.Constructor=c,a(function(){a("html").on("click.dropdown.data-api",d),a("body").on("click.dropdown.data-api",b,c.prototype.toggle)})}(window.jQuery)
\ No newline at end of file
+!function(a){a(function(){a.support.transition=function(){var b=document.body||document.documentElement,c=b.style,d=c.transition!==undefined||c.WebkitTransition!==undefined||c.MozTransition!==undefined||c.MsTransition!==undefined||c.OTransition!==undefined;return d&&{end:function(){var b="TransitionEnd";return a.browser.webkit?b="webkitTransitionEnd":a.browser.mozilla?b="transitionend":a.browser.opera&&(b="oTransitionEnd"),b}()}}()})}(window.jQuery),!function(a){function c(){var b=this,c=setTimeout(function(){b.$element.off(a.support.transition.end),d.call(b)},500);this.$element.one(a.support.transition.end,function(){clearTimeout(c),d.call(b)})}function d(a){this.$element.hide().trigger("hidden"),e.call(this)}function e(b){var c=this,d=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var e=a.support.transition&&d;this.$backdrop=a('<div class="modal-backdrop '+d+'" />').appendTo(document.body),this.options.backdrop!="static"&&this.$backdrop.click(a.proxy(this.hide,this)),e&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),e?this.$backdrop.one(a.support.transition.end,b):b()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(a.support.transition.end,a.proxy(f,this)):f.call(this)):b&&b()}function f(){this.$backdrop.remove(),this.$backdrop=null}function g(){var b=this;this.isShown&&this.options.keyboard?a(document).on("keyup.dismiss.modal",function(a){a.which==27&&b.hide()}):this.isShown||a(document).off("keyup.dismiss.modal")}var b=function(b,c){this.options=c,this.$element=a(b).delegate('[data-dismiss="modal"]',"click.dismiss.modal",a.proxy(this.hide,this))};b.prototype={constructor:b,toggle:function(){return this[this.isShown?"hide":"show"]()},show:function(){var b=this;if(this.isShown)return;a("body").addClass("modal-open"),this.isShown=!0,this.$element.trigger("show"),g.call(this),e.call(this,function(){var c=a.support.transition&&b.$element.hasClass("fade");!b.$element.parent().length&&b.$element.appendTo(document.body),b.$element.show(),c&&b.$element[0].offsetWidth,b.$element.addClass("in"),c?b.$element.one(a.support.transition.end,function(){b.$element.trigger("shown")}):b.$element.trigger("shown")})},hide:function(b){b&&b.preventDefault();if(!this.isShown)return;var e=this;this.isShown=!1,a("body").removeClass("modal-open"),g.call(this),this.$element.trigger("hide").removeClass("in"),a.support.transition&&this.$element.hasClass("fade")?c.call(this):d.call(this)}},a.fn.modal=function(c){return this.each(function(){var d=a(this),e=d.data("modal"),f=a.extend({},a.fn.modal.defaults,d.data(),typeof c=="object"&&c);e||d.data("modal",e=new b(this,f)),typeof c=="string"?e[c]():f.show&&e.show()})},a.fn.modal.defaults={backdrop:!0,keyboard:!0,show:!0},a.fn.modal.Constructor=b,a(function(){a("body").on("click.modal.data-api",'[data-toggle="modal"]',function(b){var c=a(this),d,e=a(c.attr("data-target")||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,"")),f=e.data("modal")?"toggle":a.extend({},e.data(),c.data());b.preventDefault(),e.modal(f)})})}(window.jQuery),!function(a){function d(){a(b).parent().removeClass("open")}var b='[data-toggle="dropdown"]',c=function(b){var c=a(b).on("click.dropdown.data-api",this.toggle);a("html").on("click.dropdown.data-api",function(){c.parent().removeClass("open")})};c.prototype={constructor:c,toggle:function(b){var c=a(this),e=c.attr("data-target"),f,g;return e||(e=c.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,"")),f=a(e),f.length||(f=c.parent()),g=f.hasClass("open"),d(),!g&&f.toggleClass("open"),!1}},a.fn.dropdown=function(b){return this.each(function(){var d=a(this),e=d.data("dropdown");e||d.data("dropdown",e=new c(this)),typeof b=="string"&&e[b].call(d)})},a.fn.dropdown.Constructor=c,a(function(){a("html").on("click.dropdown.data-api",d),a("body").on("click.dropdown.data-api",b,c.prototype.toggle)})}(window.jQuery),!function(a){var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype={constructor:c,close:function(b){function f(){e.trigger("closed").remove()}var c=a(this),d=c.attr("data-target"),e;d||(d=c.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),e=a(d),e.trigger("close"),b&&b.preventDefault(),e.length||(e=c.hasClass("alert")?c:c.parent()),e.trigger("close").removeClass("in"),a.support.transition&&e.hasClass("fade")?e.on(a.support.transition.end,f):f()}},a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("alert");e||d.data("alert",e=new c(this)),typeof b=="string"&&e[b].call(d)})},a.fn.alert.Constructor=c,a(function(){a("body").on("click.alert.data-api",b,c.prototype.close)})}(window.jQuery)
\ No newline at end of file
}
});
}
- var mottos = ['guess the song.', 'name that tune.', 'i know this track.'];
- var motto = mottos[Math.floor(Math.random()*mottos.length)];
- $('#app-name small').text(motto);
$.get("/artworks", function(data) {
$(".thumbnail").each(function(index) {
var i = index * 6;
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 mottos = ['guess the song.', 'name that tune.', 'i know this track.'];
-
var DOM = {};
// Exact match version of jQuery :contains selector
};
// Prompt for name and send it.
- var joinRoom = function(msg) {
+ var joinAnonymously = function(msg) {
if (/nickname\s*\=/.test(document.cookie) && !msg) {
nickname = unescape(document.cookie.replace(/.*nickname\s*\=\s*([^;]*);?.*/, "$1"));
- socket.emit('joinroom', {nickname:nickname,roomname:roomname});
+ socket.emit('joinanonymously', {nickname:nickname,roomname:roomname});
}
else {
if (!$('body').hasClass('modal-open')) {
var val = $.trim(login.val());
if (val !== "") {
nickname = val;
- socket.emit('joinroom', {nickname:nickname,roomname:roomname});
+ socket.emit('joinanonymously', {nickname:nickname,roomname:roomname});
}
else {
var txt = "Nickname can't be empty.";
// Your submitted name was invalid
var invalidNickName = function(feedback) {
- joinRoom(feedback+"<br/>Try with another one:");
+ joinAnonymously(feedback+"<br/>Try with another one:");
+ };
+
+ /* 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');
};
- // You joined the game
+ // You joined the room
var ready = function(data) {
- if (!/nickname\s*\=/.test(document.cookie)) {
+ if (!data.loggedin && !/nickname\s*\=/.test(document.cookie)) {
document.cookie = "nickname="+escape(nickname)+";path=/;";
}
DOM.modal.modal('hide').empty();
var found = false;
for (var i=0; i<users.length; i++) {
var user = users[i];
- var li = $('<li></li>');
+ var li = $('<li class="relative"></li>');
var pvt = $('<span class="private label label-info">P</span>');
var username = $('<span class="name"></span>').text(user.nickname);
var points = $('<span class="points">('+user.points+')</span>');
var roundpointsel = $('<span class="round-points"></span>');
var guesstime = $('<span class="guess-time"></span>');
li.append(pvt, username, points, roundrank, roundpointsel, guesstime);
+ if (user.registered) {
+ var href = 'href="/user/'+username.text().replace(/"/g, """)+'"';
+ pvt.after('<a class="registered" target="_blank" '+href+'></a>');
+ }
DOM.users.append(li);
if (pvtmsgto === user.nickname) {
pvt.show();
DOM.recipient.hide();
DOM.messagebox.animate({'width':'-='+width+'px'}, "fast", function() {DOM.recipient.show();});
var el = $("span.name:econtains("+usrname+")");
- el.prev().show();
+ el.prevAll(".private").show();
el.unbind('click');
el.click(clearPrivate);
pvtmsgto = usrname;
DOM.recipient.text("");
DOM.messagebox.animate({'width':'+='+width+'px'}, "fast");
var el = $("span.name:econtains("+pvtmsgto+")");
- el.prev().hide();
+ el.prevAll(".private").hide();
el.unbind("click");
el.click(function() {
addPrivate($(this).text());
};
var gameOver = function(data) {
- var users = [];
- for (var key in data.users) {
- users.push(data.users[key]);
- }
- users.sort(function(a, b) {return b.points - a.points;});
var html = '<div class="modal-header"><h3>Game Over</h3></div>';
- html += '<div class="modal-body">';
+ 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="cups stand1"></div></th><th><div class="cups stand2"></div></th>';
+ html += '<th><div class="cups stand3"></div></th><th>Guessed</th><th>Best time</th>';
+ html += '</thead><tbody>';
for(var i=0;i<3;i++) {
- if (users[i]) {
- var rank = i+1;
- var offset = -16 + (-32 * i);
- var style = ' style="background:url(/static/images/sprites.png)';
- style += ' no-repeat 0px '+offset+'px;"';
- html += '<div class="gameover"'+style+'>'+rank+')';
- html += ' <span class="name">'+users[i].nickname;
- html += '</span>('+users[i].points+')</div>';
+ if (data.users[i]) {
+ var playername = data.users[i].nickname.replace(/</g, "<")
+ .replace(/>/g, ">").replace(/"/g, """);
+ html += '<tr><td><div class="medals rank'+(i+1)+'"></div></td>';
+ html += '<td class="name">'+playername+'</td>';
+ html += '<td>'+data.users[i].points+'</td>';
+ html += '<td>'+data.users[i].golds+'</td><td>'+data.users[i].silvers+'</td>';
+ html += '<td>'+data.users[i].bronzes+'</td><td>'+data.users[i].guessed+'</td>';
+ var besttime = "N/A";
+ if (data.users[i].bestguesstime !== 30000) {
+ besttime = (data.users[i].bestguesstime/1000).toFixed(1)+" s";
+ }
+ html += '<td>'+besttime+'</td></tr>';
}
}
- html +='</div>';
+ html +='</tbody></table></div>';
html += '<div class="modal-footer">A new game will start in <span></span> second/s</div>';
DOM.modal.append($(html));
DOM.modal.modal('show');
// Set up the room.
$(function() {
- var motto = mottos[Math.floor(Math.random()*mottos.length)];
- $('#app-name small').text(motto);
setVariables();
DOM.modal.modal({keyboard:false,show:false,backdrop:"static"});
DOM.togglechat.click(hideChat);
socket.on("connect", function() {
jplayer = $("#player").jPlayer({
ready: function() {
- joinRoom();
+ socket.emit('loggedin', function(data) {
+ if (data) {
+ nickname = data;
+ socket.emit('joinroom', roomname);
+ }
+ else {
+ joinAnonymously();
+ }
+ });
if (!$.jPlayer.platform.mobile && !$.jPlayer.platform.tablet) {
addVolumeControl();
}
volume: 1
});
});
+ socket.on('alreadyinaroom', alreadyInARoom);
socket.on('invalidnickname', invalidNickName);
socket.on('ready', ready);
socket.on("disconnect", disconnect);
var async = require("async");
+var crypto = require("crypto");
+var canvas = require("canvas");
var express = require("express");
+var form = require("express-form");
+var parseCookie = require('connect').utils.parseCookie;
+var redisstore = require('connect-redis')(express);
+var config = require("./config.js").configure();
+
+// Setting up Redis
+var songsdb = require("redis-url").createClient(config.songsdburl);
+var usersdb = require("redis-url").createClient(config.usersdburl);
+
+songsdb.on('error', function(err) {
+ console.log("Error: "+err);
+});
+
+usersdb.on('error', function(err) {
+ console.log("Error: "+err);
+});
+
+// Setting up Express
+var sessionstore = new redisstore({client:usersdb});
var http = express.createServer();
// Configuration
-var config = require("./config.js").configure();
http.use(express.static(__dirname + '/public'));
+http.use(express.bodyParser());
+http.use(express.cookieParser());
+http.use(express.session({secret:config.sessionsecret,store:sessionstore}));
http.set("view options", {layout:false});
http.set('view engine', 'jade');
// Routes
http.get("/", function(req, res) {
+ if (req.session.user) {
+ res.local('loggedin', req.session.user);
+ }
res.render("index", {rooms:config.rooms});
});
+// Captcha generator
+const CHARACTERS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+
+var Captcha = function() {
+ var code = "";
+ while (code.length < 4) {
+ code += CHARACTERS[Math.floor(Math.random() * CHARACTERS.length)];
+ }
+ var _canvas = new canvas(64, 26);
+ var ctx = _canvas.getContext('2d');
+ 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.toDataURL = function() {
+ return _canvas.toDataURL();
+ }
+};
+
+http.get("/signup", function(req, res) {
+ var captcha = new Captcha();
+ req.session.captchacode = captcha.getCode();
+ res.render("signup", {captchaurl:captcha.toDataURL()});
+});
+
+http.post("/signup",
+ form(
+ form.filter("username").trim().required().not(/binb/, "is reserved")
+ .is(/^[^\x00-\x1F\x7F]{1,15}$/, "1 to 15 characters required"),
+ form.filter("email").required().isEmail("is not an email address"),
+ form.filter("password").required()
+ .is(/^[A-Za-z0-9]{6,15}$/, "6 to 15 alphanumeric characters required"),
+ form.filter("captcha").required()
+ ),
+ function(req, res) {
+ if (req.form.isValid) {
+ if (req.session.captchacode !== req.form.captcha) {
+ var errors = {captcha:['no match']};
+ var captcha = new Captcha();
+ req.session.captchacode = captcha.getCode();
+ return res.render("signup", {errors:errors,captchaurl:captcha.toDataURL()});
+ }
+ var userkey = "user:"+req.form.username;
+ usersdb.exists(userkey, function(err, data) {
+ if (data === 1) { // User already exists
+ var errors = {alert: "A user with name "+req.form.username+" already exists."};
+ var captcha = new Captcha();
+ req.session.captchacode = captcha.getCode();
+ return res.render("signup", {errors:errors,captchaurl:captcha.toDataURL()});
+ }
+ var mailkey = "email:"+req.form.email;
+ usersdb.exists(mailkey, function(e, d) {
+ if (d === 1) { // Email already exists
+ var errors = {alert: "A user with that email already exists."};
+ var captcha = new Captcha();
+ req.session.captchacode = captcha.getCode();
+ return res.render("signup", {errors:errors,captchaurl:captcha.toDataURL()});
+ }
+ var salt = "";
+ while (salt.length < 8) {
+ salt += CHARACTERS[Math.floor(Math.random() * CHARACTERS.length)];
+ }
+ var hash = crypto.createHash('sha256')
+ .update(salt+req.form.password).digest('hex');
+ var date = new Date();
+ var joindate = date.getDate()+"/"+(date.getMonth()+1)+"/"+date.getFullYear();
+ usersdb.hmset(userkey, "username", req.form.username,
+ "email", req.form.email,
+ "password", hash,
+ "salt", salt,
+ "joindate", joindate,
+ "totpoints", 0,
+ "bestscore", 0,
+ "golds", 0,
+ "silvers", 0,
+ "bronzes", 0,
+ "bestguesstime", 30000,
+ "worstguesstime", 0,
+ "totguesstime", 0,
+ "guessed", 0,
+ "victories", 0,
+ "secondplaces", 0,
+ "thirdplaces", 0);
+ usersdb.set(mailkey, userkey);
+ usersdb.sadd("users", userkey);
+ usersdb.sadd("emails", mailkey);
+ var msg = "You successfully created your account. You are now ready to login.";
+ res.render("login", {success:msg});
+ });
+ });
+ }
+ else {
+ var captcha = new Captcha();
+ req.session.captchacode = captcha.getCode();
+ res.render("signup", {errors:req.form.getErrors(),captchaurl:captcha.toDataURL()});
+ }
+ }
+);
+
+http.get("/login", function(req, res) {
+ res.render("login");
+});
+
+http.post("/login",
+ form(
+ form.filter("username").trim().required(),
+ form.filter("password").trim().required()
+ ),
+ function(req, res) {
+ if (req.form.isValid) {
+ var errors = {alert: "The username and/or password you specified are not correct."};
+ var key = "user:"+req.form.username;
+ usersdb.exists(key, function(err, data) {
+ if (data === 1) { // User exists
+ usersdb.hmget(key, "salt", "password", function(e, resp) {
+ var hash = crypto.createHash('sha256')
+ .update(resp[0]+req.body.password).digest('hex');
+ if (hash === resp[1]) {
+ req.session.regenerate(function() {
+ req.session.cookie.maxAge = 604800000; // One week
+ req.session.user = req.form.username;
+ res.redirect('/');
+ });
+ }
+ else {
+ res.render("login", {errors:errors});
+ }
+ });
+ }
+ else {
+ res.render("login", {errors:errors});
+ }
+ });
+ }
+ else {
+ res.render("login", {errors:req.form.getErrors()});
+ }
+ }
+);
+
+http.get("/logout", function(req, res) {
+ req.session.destroy(function() {
+ res.redirect("/");
+ });
+});
+
+var makeCallBack = function(genre) {
+ return function(callback) {
+ songsdb.srandmember(genre, function(err, res) {
+ songsdb.hget(res, "artworkUrl100", callback);
+ });
+ };
+};
+
http.get("/artworks", function(req, res) {
var callitems = [];
for (var i=0; i<config.rooms.length; i++) {
});
});
-http.get("/:room", function(req, res, next) {
+http.get("/:room", function(req, res) {
if (config.rooms.indexOf(req.params.room) !== -1) {
+ if (req.session.user) {
+ res.local('loggedin', req.session.user);
+ }
res.render("room", {roomname:req.params.room,rooms:config.rooms});
}
else {
- next();
+ res.send(404);
}
});
+http.get("/user/*", function(req, res) {
+ var key = "user:"+req.params[0];
+ usersdb.exists(key, function(err, data) {
+ if (data === 1) {
+ usersdb.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.render("user", obj);
+ });
+ }
+ else {
+ res.send(404);
+ }
+ });
+});
+
// Starting HTTP server
http.listen(config.port);
-// Setting up Redis
-var redis = require("redis-url").createClient(config.redisurl);
-
-redis.on('error', function(err) {
- console.log("Error: "+err);
-});
-
// Setting up Socket.IO
var io = require("socket.io").listen(http);
// enable transports
io.set('transports', ['websocket', 'htmlfile', 'xhr-polling', 'jsonp-polling']);
-var makeCallBack = function(genre) {
- return function(callback) {
- redis.srandmember(genre, function(err, res) {
- redis.hget(res, "artworkUrl100", callback);
+io.set('authorization', function(data, accept) {
+ if(data.headers.cookie) {
+ var cookie = parseCookie(data.headers.cookie);
+ sessionstore.get(cookie['connect.sid'], function(err, session) {
+ if (err || !session) {
+ accept('Error', false);
+ }
+ else {
+ data.session = session;
+ accept(null, true);
+ }
});
- };
+ }
+ else {
+ return accept('No cookie transmitted.', false);
+ }
+});
+
+io.sockets.on('connection', function(socket) {
+ var session = socket.handshake.session;
+ socket.on('getoverview', function() {
+ var data = Object.create(null);
+ for (var prop in Rooms) {
+ data[prop] = Rooms[prop].getPopulation();
+ }
+ socket.join('home');
+ socket.emit('overview', data);
+ });
+ socket.on('loggedin', function(fn) {
+ return (session.user) ? fn(session.user) : fn(false);
+ });
+ socket.on('joinroom', function(data) {
+ if (session.user && typeof data === "string" && config.rooms.indexOf(data) !== -1) {
+ if (getUserSocket(session.user)) { // User already in a room
+ socket.emit('alreadyinaroom');
+ return;
+ }
+ socket.nickname = session.user;
+ Rooms[data].joinRoom(socket);
+ }
+ });
+ socket.on('joinanonymously', function(data) {
+ if (!socket.nickname && typeof data === "object" && typeof data.nickname === "string" &&
+ data.nickname !== "" && typeof data.roomname === "string" &&
+ config.rooms.indexOf(data.roomname) !== -1) {
+ Rooms[data.roomname].setNickName(socket, data);
+ }
+ });
+ socket.on('getstatus', function() {
+ if (socket.roomname) {
+ Rooms[socket.roomname].sendStatus(socket);
+ }
+ });
+ socket.on('sendchatmsg', function(data) {
+ if (socket.roomname) {
+ Rooms[socket.roomname].sendChatMessage(socket, data);
+ }
+ });
+ socket.on('guess', function(data) {
+ if (socket.roomname && typeof data === "string") {
+ Rooms[socket.roomname].guess(socket, data);
+ }
+ });
+ socket.on("disconnect", function() {
+ if (socket.roomname && socket.nickname) {
+ Rooms[socket.roomname].userLeft(socket);
+ }
+ });
+});
+
+// Sockets of all rooms
+var sockets = Object.create(null);
+
+// Get the socket of a player
+var getUserSocket = function(nickname) {
+ return sockets[nickname];
};
/*
return false;
};
-var sockets = Object.create(null);
-
-var getUserSocket = function(nickname) {
- return sockets[nickname];
+var collectStats = function(username, stats) {
+ var key = "user:"+username;
+ if (stats.points) {
+ usersdb.hincrby(key, "totpoints", stats.points);
+ }
+ if (stats.userscore) {
+ // Set personal best
+ usersdb.hget(key, "bestscore", function(err, res) {
+ if (res < stats.userscore) {
+ usersdb.hset(key, "bestscore", stats.userscore);
+ }
+ });
+ }
+ if (stats.gold) {
+ usersdb.hincrby(key, "golds", 1);
+ }
+ if (stats.silver) {
+ usersdb.hincrby(key, "silvers", 1);
+ }
+ if (stats.bronze) {
+ usersdb.hincrby(key, "bronzes", 1);
+ }
+ if (stats.guesstime) {
+ usersdb.hincrby(key, "guessed", 1);
+ usersdb.hincrby(key, "totguesstime", stats.guesstime);
+ usersdb.hget(key, "bestguesstime", function(err, res) {
+ if (stats.guesstime < res) {
+ usersdb.hset(key, "bestguesstime", stats.guesstime);
+ }
+ });
+ usersdb.hget(key, "worstguesstime", function(err, res) {
+ if (stats.guesstime > res) {
+ usersdb.hset(key, "worstguesstime", stats.guesstime);
+ }
+ });
+ }
+ if (stats.firstplace) {
+ usersdb.hincrby(key, "victories", 1);
+ }
+ if (stats.secondplace) {
+ usersdb.hincrby(key, "secondplaces", 1);
+ }
+ if (stats.thirdplace) {
+ usersdb.hincrby(key, "thirdplaces", 1);
+ }
};
function Room(name) {
var totusers = 0;
var usersData = Object.create(null);
- var playedtracks = []; // Used to prevent the same song from playing twice in one game
+ var playedtracks = Object.create(null); // Used to prevent the same song from playing twice in one game
var artistName = null;
var artistlcase = null;
return totusers;
};
- var addUser = function(socket) {
+ var addUser = function(socket, loggedin) {
sockets[socket.nickname] = socket;
usersData[socket.nickname] = {
nickname: socket.nickname,
+ registered: loggedin,
points: 0,
roundpoints: 0,
matched: null,
- guesstime: null
+ guessed: 0,
+ guesstime: null,
+ bestguesstime: 30000,
+ golds: 0,
+ silvers: 0,
+ bronzes: 0
};
totusers = totusers + 1;
io.sockets.in('home').emit('update', {room:roomname,players:totusers});
+ // Broadcast new user event
+ socket.emit('ready', {users:usersData,trackscount:trackscount,loggedin:loggedin});
+ socket.broadcast.to(roomname).emit('newuser', {nickname:socket.nickname,users:usersData});
};
var removeUser = function(socket) {
return false;
};
+ this.joinRoom = function(socket) {
+ socket.roomname = roomname;
+ socket.join(roomname);
+ addUser(socket, true);
+ }
+
// A user requested an invalid name
var invalidNickName = function(socket, feedback) {
socket.emit('invalidnickname', feedback);
if (data.nickname.length > 15) {
feedback = '<span class="label label-important">That name is too long.</span>';
}
- else if (data.nickname === "Binb") {
+ else if (data.nickname === "binb") {
feedback = '<span class="label label-important">That name is reserved.</span>';
}
else if (getUserSocket(data.nickname)) {
if (feedback) {
return invalidNickName(socket, feedback);
}
-
- socket.nickname = data.nickname;
- socket.roomname = roomname;
- socket.join(roomname);
- // Add user to the list of active users and broadcast the event
- addUser(socket);
- socket.emit('ready', {users:usersData,trackscount:trackscount});
- socket.broadcast.to(roomname).emit('newuser', {nickname:socket.nickname,users:usersData});
+ var key = "user:"+data.nickname;
+ usersdb.exists(key, function(err, resp) {
+ if (resp === 1) { // User already exists
+ feedback = '<span class="label label-important">That name belongs '
+ feedback += 'to a registered user.</span>';
+ return invalidNickName(socket, feedback);
+ }
+ else {
+ socket.nickname = data.nickname;
+ socket.roomname = roomname;
+ socket.join(roomname);
+ // Add user to the list of active users
+ addUser(socket, false);
+ }
+ });
};
// A user has left (DCed, etc.)
if (allowedguess && (amatch(artistlcase, datalcase, true) ||
amatch(tracklcase, datalcase))) {
var msg = "You are probably right, but you have to use the box above.";
- socket.emit('chatmsg', {from:"Binb",to:socket.nickname,chatmsg:msg});
+ socket.emit('chatmsg', {from:"binb",to:socket.nickname,chatmsg:msg});
return;
}
io.sockets.in(roomname).emit('chatmsg', {from:socket.nickname,chatmsg:data});
};
var addPoints = function(socket, allinone) {
- usersData[socket.nickname].matched = 'both';
+ usersData[socket.nickname].guesstime = 30000 - songtimeleft;
+ var stats = {};
switch (finishline) {
case 1:
finishline++;
- usersData[socket.nickname].guesstime = 30000 - songtimeleft;
usersData[socket.nickname].roundpoints = 6;
- usersData[socket.nickname].points += (allinone) ? 6 : 5;
+ if (allinone) {
+ usersData[socket.nickname].points += 6;
+ stats.points = 6;
+ }
+ else {
+ usersData[socket.nickname].points += 5;
+ stats.points = 5;
+ }
+ usersData[socket.nickname].golds++;
+ stats.gold = true;
break;
case 2:
finishline++;
- usersData[socket.nickname].guesstime = 30000 - songtimeleft;
usersData[socket.nickname].roundpoints = 5;
- usersData[socket.nickname].points += (allinone) ? 5 : 4;
+ if (allinone) {
+ usersData[socket.nickname].points += 5;
+ stats.points = 5;
+ }
+ else {
+ usersData[socket.nickname].points += 4;
+ stats.points = 4;
+ }
+ usersData[socket.nickname].silvers++;
+ stats.silver = true;
break;
case 3:
finishline++;
- usersData[socket.nickname].guesstime = 30000 - songtimeleft;
usersData[socket.nickname].roundpoints = 4;
- usersData[socket.nickname].points += (allinone) ? 4 : 3;
+ if (allinone) {
+ usersData[socket.nickname].points += 4;
+ stats.points = 4;
+ }
+ else {
+ usersData[socket.nickname].points += 3;
+ stats.points = 3;
+ }
+ usersData[socket.nickname].bronzes++;
+ stats.bronze = true;
break;
default:
usersData[socket.nickname].roundpoints = 3;
- usersData[socket.nickname].points += (allinone) ? 3 : 2;
+ if (allinone) {
+ usersData[socket.nickname].points += 3;
+ stats.points = 3;
+ }
+ else {
+ usersData[socket.nickname].points += 2;
+ stats.points = 2;
+ }
+ }
+ usersData[socket.nickname].matched = 'both';
+ usersData[socket.nickname].guessed++;
+ if (usersData[socket.nickname].guesstime < usersData[socket.nickname].bestguesstime) {
+ usersData[socket.nickname].bestguesstime = usersData[socket.nickname].guesstime;
+ }
+ if (usersData[socket.nickname].registered) {
+ stats.userscore = usersData[socket.nickname].points;
+ stats.guesstime = usersData[socket.nickname].guesstime;
+ collectStats(socket.nickname, stats);
}
};
usersData[socket.nickname].matched = 'artist';
socket.emit('artistmatched');
io.sockets.in(roomname).emit('updateusers', {users:usersData});
+ if (usersData[socket.nickname].registered) {
+ var stats = {points:1,userscore:usersData[socket.nickname].points};
+ collectStats(socket.nickname, stats);
+ }
}
else if (amatch(tracklcase, guess)) {
usersData[socket.nickname].roundpoints++;
usersData[socket.nickname].matched = 'title';
socket.emit('titlematched');
io.sockets.in(roomname).emit('updateusers', {users:usersData});
+ if (usersData[socket.nickname].registered) {
+ var stats = {points:1,userscore:usersData[socket.nickname].points};
+ collectStats(socket.nickname, stats);
+ }
}
else {
socket.emit('nomatch');
for (var key in usersData) {
if (!roundonly) {
usersData[key].points = 0;
+ usersData[key].guessed = 0;
+ usersData[key].bestguesstime = 30000;
+ usersData[key].golds = 0;
+ usersData[key].silvers = 0;
+ usersData[key].bronzes = 0;
}
usersData[key].roundpoints = 0;
usersData[key].matched = null;
};
var sendLoadTrack = function() {
- redis.srandmember(roomname, function(err, res) {
- redis.hmget(res, "trackId", "artistName", "trackName", "collectionName", "previewUrl",
+ songsdb.srandmember(roomname, function(err, res) {
+ songsdb.hmget(res, "artistName", "trackName", "collectionName", "previewUrl",
"artworkUrl60", "trackViewUrl", function(e, replies) {
- if (playedtracks[replies[0]]) {
+ if (playedtracks[res]) {
return sendLoadTrack();
}
- playedtracks[replies[0]] = true;
- artistName = replies[1];
+ playedtracks[res] = true;
+ artistName = replies[0];
artistlcase = artistName.toLowerCase();
- trackName = replies[2];
+ trackName = replies[1];
tracklcase = trackName.toLowerCase();
- collectionName = replies[3];
- previewUrl = replies[4];
- artworkUrl = replies[5];
- trackViewUrl = replies[6];
+ collectionName = replies[2];
+ previewUrl = replies[3];
+ artworkUrl = replies[4];
+ trackViewUrl = replies[5];
io.sockets.in(roomname).emit('loadtrack', {previewUrl:previewUrl});
setTimeout(sendPlayTrack, 5000);
});
var gameOver = function() {
status = 3; // Game over
- io.sockets.in(roomname).emit('gameover', {users:usersData});
+ 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', {users:podium});
+ 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);
setTimeout(reset, 5000);
};
var reset = function() {
songcounter = 0;
- playedtracks = [];
+ playedtracks = Object.create(null);
sendLoadTrack();
};
// Start the room
this.start = function() {
- redis.scard(roomname, function(err, res) {
+ songsdb.scard(roomname, function(err, res) {
trackscount = res;
});
sendLoadTrack();
Rooms[config.rooms[i]].start();
}
-console.log("Bimb started and listening on port "+config.port);
-
-io.sockets.on('connection', function(socket) {
- socket.on('getoverview', function() {
- var data = Object.create(null);
- for (var prop in Rooms) {
- data[prop] = Rooms[prop].getPopulation();
- }
- socket.join('home');
- socket.emit('overview', data);
- });
- socket.on('joinroom', function(data) {
- if (!socket.nickname && typeof data === "object" && typeof data.nickname === "string" &&
- data.nickname !== "" && typeof data.roomname === "string" &&
- config.rooms.indexOf(data.roomname) !== -1) {
- Rooms[data.roomname].setNickName(socket, data);
- }
- });
- socket.on('getstatus', function() {
- Rooms[socket.roomname].sendStatus(socket);
- });
- socket.on('sendchatmsg', function(data) {
- Rooms[socket.roomname].sendChatMessage(socket, data);
- });
- socket.on('guess', function(data) {
- if (typeof data === "string") {
- Rooms[socket.roomname].guess(socket, data);
- }
- });
- socket.on("disconnect", function() {
- if (socket.nickname !== undefined) {
- Rooms[socket.roomname].userLeft(socket);
- }
- });
-});
+console.log("binb started and listening on port "+config.port);
-doctype html
-html
- head
- link(href="/static/css/bootstrap.min.css", rel="stylesheet")
- link(href="/static/css/style.css", rel="stylesheet")
- meta(charset="UTF-8")
- meta(name="keywords", content="iTunes, music, quiz, binb, beatquest, realtime, multiplayer, listening, game")
- meta(name="description", content="Simple, realtime, multiplayer, competitive music listening game. Guess the song and prove your music knowledge!")
- meta(name="author", content="Luigi Pinca")
- script(src="/static/js/jquery.min.js")
- script(src="/socket.io/socket.io.js")
- script(type="text/javascript")
- 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);
- })();
+mottos = ['guess the song.', 'name that tune.', 'i know this track.']
+motto = mottos[Math.floor(Math.random()*mottos.length)]
+head
+ link(href="/static/css/bootstrap.min.css", rel="stylesheet")
+ link(href="/static/css/style.css", rel="stylesheet")
+ meta(charset="UTF-8")
+ meta(name="keywords", content="iTunes, music, quiz, binb, beatquest, realtime, multiplayer, listening, game")
+ meta(name="description", content="Simple, realtime, multiplayer, competitive music listening game. Guess the song and prove your music knowledge!")
+ meta(name="author", content="Luigi Pinca")
+ script(src="/static/js/jquery.min.js")
+ script(type="text/javascript")
+ 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);
+ })();
-include header
- title Binb
+doctype html
+html
+ include header
+ title binb
+ script(src="/socket.io/socket.io.js")
+ script(src="/static/js/bootstrap.min.js")
script(src="/static/js/home.js")
body
- script(type="text/javascript")
- 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);
- })();
+ include uv.jade
+ .navbar.navbar-fixed-top
+ .navbar-inner
+ .container
+ a.brand(href="/") binb,
+ span.motto #{motto}
+ ul.nav.pull-right
+ li.active
+ a(href="/") Home
+ if (typeof(loggedin) !== "undefined")
+ li
+ p.navbar-text Logged in as
+ a#loggedin(href="/user/#{loggedin}",target="_blank") #{loggedin}
+ li
+ a(href="/logout") Logout
+ else
+ li
+ a(href="/signup") Sign up
+ li
+ a(href="/login") Login
.container
section
- .page-header
- h1#app-name Binb,
- small
.row
.span7
h3 What's this?
- p Binb is a realtime, multiplayer, competitive music listening game.
+ 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
--- /dev/null
+doctype html
+html
+ include header
+ title binb :: login
+ script(src="/static/js/bootstrap.min.js")
+ body
+ include uv.jade
+ .navbar.navbar-fixed-top
+ .navbar-inner
+ .container
+ a.brand(href="/") binb,
+ span.motto #{motto}
+ ul.nav.pull-right
+ li
+ a(href="/") Home
+ li
+ a(href="/signup") Sign up
+ li.active
+ a(href="/login") Login
+ .container
+ section
+ .row
+ .span3
+ h3 New user?
+ a(href="/signup") Click here to create an account.
+ .span13
+ if ((typeof(errors) !== "undefined") && (typeof(errors.alert) !== "undefined"))
+ .alert.alert-error
+ a.close(data-dismiss="alert") ×
+ strong Oh snap!
+ | #{errors.alert}
+ else if (typeof(success) !== "undefined")
+ .alert.alert-success
+ a.close(data-dismiss="alert") ×
+ strong Well done!
+ | #{success}
+ form.form-horizontal.well(method="post",action="/login")
+ fieldset
+ if (typeof(errors) !== "undefined")
+ if (typeof(errors.username) !== "undefined")
+ .control-group.error
+ label.control-label(for="username") Name
+ .controls
+ input#username(type="text",name="username",
+ value="#{username}")
+ span.help-inline #{errors.username[0]}
+ else
+ .control-group
+ label.control-label(for="username") Name
+ .controls
+ input#username(type="text",name="username",
+ value="#{username}")
+ if (typeof(errors.password) !== 'undefined')
+ .control-group.error
+ label.control-label(for="password") Password
+ .controls
+ input#password(type="password",name="password")
+ span.help-inline #{errors.password[0]}
+ else
+ .control-group
+ label.control-label(for="password") Password
+ .controls
+ input#password(type="password",name="password")
+ else
+ .control-group
+ label.control-label(for="username") Name
+ .controls
+ input#username(type="text",name="username",
+ placeholder="enter your nickname...")
+ .control-group
+ label.control-label(for="password") Password
+ .controls
+ input#password(type="password",name="password",
+ placeholder="enter your password...")
+ button#signup-button.btn.btn-primary(type="submit")
+ i.icon-user.icon-white
+ | Login
+ include footer
-include header
- title Binb - #{roomname}
+doctype html
+html
+ include header
+ title binb :: #{roomname}
+ script(src="/socket.io/socket.io.js")
script(src="/static/js/bootstrap.min.js")
script(src="/static/js/jquery.jplayer.min.js")
script(type='text/javascript')
var roomname = "#{roomname}";
script(src="/static/js/room.js")
body
- script(type="text/javascript")
- 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);
- })();
+ include uv.jade
+ .navbar.navbar-fixed-top
+ .navbar-inner
+ .container
+ ul.nav.pull-right
+ li
+ a(href="/") Home
+ 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}") #{item}
+ if (typeof(loggedin) !== "undefined")
+ li
+ p.navbar-text Logged in as
+ a#loggedin(href="/user/#{loggedin}",target="_blank") #{loggedin}
+ li
+ a(href="/logout") Logout
+ else
+ li
+ a(href="/signup") Sign up
+ li
+ a(href="/login") Login
#player
#modal.modal.fade
.container
#volume.relative
.span8
.page-header
- h1#app-name Binb,
- small
+ h1#app-name binb,
+ small #{motto}
#total-tracks <span></span> tracks.
- .btn-group
- button.btn.btn-mini.dropdown-toggle(data-toggle="dropdown")
- | #{roomname}
- span.caret
- ul.dropdown-menu
- each item in rooms
- if item != roomname
- li
- a(href="/#{item}") #{item}
- li.divider
- li
- a(href="/") home
#summary.row
.span2
.title Rank
.title Track
.track
p#feedback Waiting for connection...
- input#guess.span8(type="text")
+ input#guess.span8(type="text",placeholder="guess the artist and/or title here")
section.relative
.row
- .span5.offset2
+ #users-wrapper.span5.offset2
ul#users.unstyled
.span8
a#toggle-chat Hide chat
--- /dev/null
+doctype html
+html
+ include header
+ title binb :: sign up
+ script(src="/static/js/bootstrap.min.js")
+ body
+ include uv.jade
+ .navbar.navbar-fixed-top
+ .navbar-inner
+ .container
+ a.brand(href="/") binb,
+ span.motto #{motto}
+ ul.nav.pull-right
+ li
+ a(href="/") Home
+ li.active
+ a(href="/signup") Sign up
+ li
+ a(href="/login") Login
+ .container
+ section
+ .row
+ .span3
+ h3 Not a new user?
+ a(href="/login") 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 ((typeof(errors) !== "undefined") && (typeof(errors.alert) !== "undefined"))
+ .alert.alert-error
+ a.close(data-dismiss="alert") ×
+ strong Oh snap!
+ | #{errors.alert}
+ form.form-horizontal.well(method="post",action="/signup")
+ fieldset
+ if (typeof(errors) !== "undefined")
+ if (typeof(errors.username) !== "undefined")
+ .control-group.error
+ label.control-label(for="username") Name
+ .controls
+ input#username(type="text",name="username",
+ value="#{username}")
+ span.help-inline #{errors.username[0]}
+ else
+ .control-group
+ label.control-label(for="username") Name
+ .controls
+ input#username(type="text",name="username",
+ value="#{username}")
+ if (typeof(errors.email) !== 'undefined')
+ .control-group.error
+ label.control-label(for="email") Email
+ .controls
+ input#email(type="text",name="email",
+ value="#{email}")
+ span.help-inline #{errors.email[0]}
+ else
+ .control-group
+ label.control-label(for="email") Email
+ .controls
+ input#email(type="text",name="email",
+ value="#{email}")
+ if (typeof(errors.password) !== 'undefined')
+ .control-group.error
+ label.control-label(for="password") Password
+ .controls
+ input#password(type="password",name="password")
+ span.help-inline #{errors.password[0]}
+ else
+ .control-group
+ label.control-label(for="password") Password
+ .controls
+ input#password(type="password",name="password")
+ if (typeof(errors.captcha) !== 'undefined')
+ .control-group.error
+ label.control-label(for="captcha-input") Are you human?
+ .controls
+ img#captcha(src="#{captchaurl}")
+ input#captcha-input(type="text",name="captcha")
+ span.help-inline #{errors.captcha[0]}
+ else
+ .control-group
+ label.control-label(for="captcha-input") Are you human?
+ .controls
+ img#captcha(src="#{captchaurl}")
+ input#captcha-input(type="text",name="captcha")
+ else
+ .control-group
+ label.control-label(for="username") Name
+ .controls
+ input#username(type="text",name="username",
+ 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",
+ placeholder="type what you see...")
+ button#signup-button.btn.btn-success(type="submit")
+ i.icon-user.icon-white
+ | Sign up!
+ include footer
--- /dev/null
+doctype html
+html
+ include header
+ title binb :: #{username} info
+ body
+ include uv.jade
+ .navbar.navbar-fixed-top
+ .navbar-inner
+ .container
+ a.brand(href="#") binb,
+ span.motto #{motto}
+ .container
+ section
+ .row
+ .span7.offset1
+ .profile #{username}
+ .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
+ .cups.stand1
+ td #{golds}
+ tr
+ td Silver cups
+ td
+ .cups.stand2
+ td #{silvers}
+ tr
+ td Bronze cups
+ td
+ .cups.stand3
+ td #{bronzes}
+ tr
+ td Victories
+ td
+ .medals.rank1
+ td #{victories}
+ tr
+ td Second places
+ td
+ .medals.rank2
+ td #{secondplaces}
+ tr
+ td Third places
+ td
+ .medals.rank3
+ td #{thirdplaces}
+ include footer
--- /dev/null
+script(type="text/javascript")
+ 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);
+ })();