Finalize functionality.
Add start/stop/reset buttons. Tidy up the interactions between the view and the stopwatch model. Add CSS styles.
This commit is contained in:
parent
31c251f6de
commit
4b5b869c14
43
app.js
43
app.js
|
@ -1,6 +1,7 @@
|
||||||
var express = require('express'),
|
var express = require('express'),
|
||||||
app = express.createServer(express.logger()),
|
app = module.exports = express.createServer(express.logger()),
|
||||||
io = require('socket.io').listen(app),
|
io = require('socket.io').listen(app);
|
||||||
|
Stopwatch = require('./models/stopwatch'),
|
||||||
routes = require('./routes');
|
routes = require('./routes');
|
||||||
|
|
||||||
// Configuration
|
// Configuration
|
||||||
|
@ -25,26 +26,44 @@ app.configure('production', function() {
|
||||||
// Heroku won't actually allow us to use WebSockets
|
// Heroku won't actually allow us to use WebSockets
|
||||||
// so we have to setup polling instead.
|
// so we have to setup polling instead.
|
||||||
// https://devcenter.heroku.com/articles/using-socket-io-with-node-js-on-heroku
|
// https://devcenter.heroku.com/articles/using-socket-io-with-node-js-on-heroku
|
||||||
// io.configure(function () {
|
io.configure(function () {
|
||||||
// io.set("transports", ["xhr-polling"]);
|
io.set("transports", ["xhr-polling"]);
|
||||||
// io.set("polling duration", 10);
|
io.set("polling duration", 10);
|
||||||
// });
|
});
|
||||||
|
|
||||||
// Routes
|
// Routes
|
||||||
|
|
||||||
var port = process.env.PORT || 5000; // Use the port that Heroku provides or default to 5000
|
// Use the port that Heroku provides or default to 5000
|
||||||
|
var port = process.env.PORT || 5000;
|
||||||
app.listen(port, function() {
|
app.listen(port, function() {
|
||||||
console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);
|
console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('/', routes.index);
|
app.get('/', routes.index);
|
||||||
|
|
||||||
var status = "All is well.";
|
var stopwatch = new Stopwatch();
|
||||||
|
stopwatch.on('tick:stopwatch', function(time) {
|
||||||
|
io.sockets.emit('time', { time: time });
|
||||||
|
});
|
||||||
|
|
||||||
|
stopwatch.on('reset:stopwatch', function(time) {
|
||||||
|
io.sockets.emit('time', { time: time });
|
||||||
|
});
|
||||||
|
|
||||||
|
stopwatch.start();
|
||||||
|
|
||||||
io.sockets.on('connection', function (socket) {
|
io.sockets.on('connection', function (socket) {
|
||||||
io.sockets.emit('status', { status: status }); // note the use of io.sockets to emit but socket.on to listen
|
io.sockets.emit('time', { time: stopwatch.getTime() });
|
||||||
socket.on('reset', function (data) {
|
|
||||||
status = "War is imminent!";
|
socket.on('click:start', function () {
|
||||||
io.sockets.emit('status', { status: status });
|
stopwatch.start();
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('click:stop', function () {
|
||||||
|
stopwatch.stop();
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('click:reset', function () {
|
||||||
|
stopwatch.reset();
|
||||||
});
|
});
|
||||||
});
|
});
|
|
@ -0,0 +1,105 @@
|
||||||
|
var util = require('util'),
|
||||||
|
events = require('events')
|
||||||
|
_ = require('underscore');
|
||||||
|
|
||||||
|
// ---------------------------------------------
|
||||||
|
// Constructor
|
||||||
|
// ---------------------------------------------
|
||||||
|
function Stopwatch() {
|
||||||
|
if(false === (this instanceof Stopwatch)) {
|
||||||
|
return new Stopwatch();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.hour = 3600000;
|
||||||
|
this.minute = 60000;
|
||||||
|
this.second = 1000;
|
||||||
|
this.time = this.hour;
|
||||||
|
this.interval = undefined;
|
||||||
|
|
||||||
|
events.EventEmitter.call(this);
|
||||||
|
|
||||||
|
// Use Underscore to bind all of our methods
|
||||||
|
// to the proper context
|
||||||
|
_.bindAll(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---------------------------------------------
|
||||||
|
// Inherit from EventEmitter
|
||||||
|
// ---------------------------------------------
|
||||||
|
util.inherits(Stopwatch, events.EventEmitter);
|
||||||
|
|
||||||
|
// ---------------------------------------------
|
||||||
|
// Methods
|
||||||
|
// ---------------------------------------------
|
||||||
|
Stopwatch.prototype.start = function() {
|
||||||
|
if (this.interval) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Starting Stopwatch!');
|
||||||
|
// note the use of _.bindAll in the constructor
|
||||||
|
// with bindAll we can pass one of our methods to
|
||||||
|
// setInterval and have it called with the proper 'this' value
|
||||||
|
this.interval = setInterval(this.onTick, this.second);
|
||||||
|
this.emit('start:stopwatch');
|
||||||
|
};
|
||||||
|
|
||||||
|
Stopwatch.prototype.stop = function() {
|
||||||
|
console.log('Stopping Stopwatch!');
|
||||||
|
if (this.interval) {
|
||||||
|
clearInterval(this.interval);
|
||||||
|
this.interval = undefined;
|
||||||
|
this.emit('stop:stopwatch');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Stopwatch.prototype.reset = function() {
|
||||||
|
console.log('Resetting Stopwatch!');
|
||||||
|
this.time = this.hour;
|
||||||
|
this.emit('reset:stopwatch', this.formatTime(this.time));
|
||||||
|
};
|
||||||
|
|
||||||
|
Stopwatch.prototype.onTick = function() {
|
||||||
|
this.time -= this.second;
|
||||||
|
|
||||||
|
var formattedTime = this.formatTime(this.time);
|
||||||
|
this.emit('tick:stopwatch', formattedTime);
|
||||||
|
|
||||||
|
if (this.time === 0) {
|
||||||
|
this.stop();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Stopwatch.prototype.formatTime = function(time) {
|
||||||
|
var remainder = time,
|
||||||
|
numHours,
|
||||||
|
numMinutes,
|
||||||
|
numSeconds,
|
||||||
|
output = "";
|
||||||
|
|
||||||
|
numHours = String(parseInt(remainder / this.hour, 10));
|
||||||
|
remainder -= this.hour * numHours;
|
||||||
|
|
||||||
|
numMinutes = String(parseInt(remainder / this.minute, 10));
|
||||||
|
remainder -= this.minute * numMinutes;
|
||||||
|
|
||||||
|
numSeconds = String(parseInt(remainder / this.second, 10));
|
||||||
|
|
||||||
|
output = _.map([numHours, numMinutes, numSeconds], function(str) {
|
||||||
|
if (str.length === 1) {
|
||||||
|
str = "0" + str;
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}).join(":");
|
||||||
|
|
||||||
|
return output;
|
||||||
|
};
|
||||||
|
|
||||||
|
Stopwatch.prototype.getTime = function() {
|
||||||
|
return this.formatTime(this.time);
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---------------------------------------------
|
||||||
|
// Export
|
||||||
|
// ---------------------------------------------
|
||||||
|
module.exports = Stopwatch;
|
|
@ -5,7 +5,8 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"express": "~2.5.8",
|
"express": "~2.5.8",
|
||||||
"ejs": "~0.7.1",
|
"ejs": "~0.7.1",
|
||||||
"socket.io": "~0.9.6"
|
"socket.io": "~0.9.6",
|
||||||
|
"underscore": "~1.3.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "0.6.x"
|
"node": "0.6.x"
|
||||||
|
|
|
@ -1,8 +1,58 @@
|
||||||
body {
|
#wrapper {
|
||||||
padding: 50px;
|
width: 475px;
|
||||||
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
|
height: 171px;
|
||||||
|
margin: 100px auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
#countdown {
|
||||||
color: #00B7FF;
|
font-family: 'Black Ops One', cursive;
|
||||||
|
font-size: 90px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.thoughtbot {
|
||||||
|
background-color: #ee432e;
|
||||||
|
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ee432e), color-stop(50%, #c63929), color-stop(50%, #b51700), color-stop(100%, #891100));
|
||||||
|
background-image: -webkit-linear-gradient(top, #ee432e 0%, #c63929 50%, #b51700 50%, #891100 100%);
|
||||||
|
background-image: -moz-linear-gradient(top, #ee432e 0%, #c63929 50%, #b51700 50%, #891100 100%);
|
||||||
|
background-image: -ms-linear-gradient(top, #ee432e 0%, #c63929 50%, #b51700 50%, #891100 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #ee432e 0%, #c63929 50%, #b51700 50%, #891100 100%);
|
||||||
|
background-image: linear-gradient(top, #ee432e 0%, #c63929 50%, #b51700 50%, #891100 100%);
|
||||||
|
border: 1px solid #951100;
|
||||||
|
-webkit-border-radius: 5px;
|
||||||
|
-moz-border-radius: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
-webkit-box-shadow: inset 0px 0px 0px 1px rgba(255, 115, 100, 0.4), 0 1px 3px #333333;
|
||||||
|
-moz-box-shadow: inset 0px 0px 0px 1px rgba(255, 115, 100, 0.4), 0 1px 3px #333333;
|
||||||
|
box-shadow: inset 0px 0px 0px 1px rgba(255, 115, 100, 0.4), 0 1px 3px #333333;
|
||||||
|
color: #fff;
|
||||||
|
font: bold 20px "helvetica neue", helvetica, arial, sans-serif;
|
||||||
|
line-height: 1;
|
||||||
|
padding: 12px 0 14px 0;
|
||||||
|
text-align: center;
|
||||||
|
text-shadow: 0px -1px 1px rgba(0, 0, 0, 0.8);
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.thoughtbot:hover {
|
||||||
|
background-color: #f37873;
|
||||||
|
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #f37873), color-stop(50%, #db504d), color-stop(50%, #cb0500), color-stop(100%, #a20601));
|
||||||
|
background-image: -webkit-linear-gradient(top, #f37873 0%, #db504d 50%, #cb0500 50%, #a20601 100%);
|
||||||
|
background-image: -moz-linear-gradient(top, #f37873 0%, #db504d 50%, #cb0500 50%, #a20601 100%);
|
||||||
|
background-image: -ms-linear-gradient(top, #f37873 0%, #db504d 50%, #cb0500 50%, #a20601 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #f37873 0%, #db504d 50%, #cb0500 50%, #a20601 100%);
|
||||||
|
background-image: linear-gradient(top, #f37873 0%, #db504d 50%, #cb0500 50%, #a20601 100%);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.thoughtbot:active {
|
||||||
|
background-color: #d43c28;
|
||||||
|
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #d43c28), color-stop(50%, #ad3224), color-stop(50%, #9c1500), color-stop(100%, #700d00));
|
||||||
|
background-image: -webkit-linear-gradient(top, #d43c28 0%, #ad3224 50%, #9c1500 50%, #700d00 100%);
|
||||||
|
background-image: -moz-linear-gradient(top, #d43c28 0%, #ad3224 50%, #9c1500 50%, #700d00 100%);
|
||||||
|
background-image: -ms-linear-gradient(top, #d43c28 0%, #ad3224 50%, #9c1500 50%, #700d00 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #d43c28 0%, #ad3224 50%, #9c1500 50%, #700d00 100%);
|
||||||
|
background-image: linear-gradient(top, #d43c28 0%, #ad3224 50%, #9c1500 50%, #700d00 100%);
|
||||||
|
-webkit-box-shadow: inset 0px 0px 0px 1px rgba(255, 115, 100, 0.4);
|
||||||
|
-moz-box-shadow: inset 0px 0px 0px 1px rgba(255, 115, 100, 0.4);
|
||||||
|
box-shadow: inset 0px 0px 0px 1px rgba(255, 115, 100, 0.4);
|
||||||
}
|
}
|
|
@ -1,9 +1,17 @@
|
||||||
var socket = io.connect(window.location.hostname);
|
var socket = io.connect(window.location.hostname);
|
||||||
|
|
||||||
socket.on('status', function (data) {
|
socket.on('time', function (data) {
|
||||||
$('#status').html(data.status);
|
$('#countdown').html(data.time);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#start').click(function() {
|
||||||
|
socket.emit('click:start');
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#stop').click(function() {
|
||||||
|
socket.emit('click:stop');
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#reset').click(function() {
|
$('#reset').click(function() {
|
||||||
socket.emit('reset');
|
socket.emit('click:reset');
|
||||||
});
|
});
|
|
@ -1,3 +1,5 @@
|
||||||
|
var app = require('../app');
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* GET home page.
|
* GET home page.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,2 +1,6 @@
|
||||||
<div id="status"></div>
|
<div id="wrapper">
|
||||||
<button id="reset">Reset!</button>
|
<div id="countdown"></div>
|
||||||
|
<button id="start" class="thoughtbot">Start</button>
|
||||||
|
<button id="stop" class="thoughtbot">Stop</button>
|
||||||
|
<button id="reset" class="thoughtbot">Reset</button>
|
||||||
|
</div>
|
|
@ -2,7 +2,7 @@
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>Title</title>
|
<title>DEFCON</title>
|
||||||
<meta name="description" content="">
|
<meta name="description" content="">
|
||||||
<meta name="author" content="">
|
<meta name="author" content="">
|
||||||
|
|
||||||
|
@ -14,6 +14,9 @@
|
||||||
<!-- styles -->
|
<!-- styles -->
|
||||||
<link href="/css/main.css" rel="stylesheet">
|
<link href="/css/main.css" rel="stylesheet">
|
||||||
|
|
||||||
|
<!-- fonts -->
|
||||||
|
<link href='http://fonts.googleapis.com/css?family=Black+Ops+One' rel='stylesheet' type='text/css'>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<%- body %>
|
<%- body %>
|
||||||
|
|
Loading…
Reference in New Issue