diff --git a/config/config.js b/config/config.js new file mode 100644 index 0000000..f638013 --- /dev/null +++ b/config/config.js @@ -0,0 +1,10 @@ +"use strict"; + +var env = process.env.NODE_ENV || "development"; + +var fs = require("fs"); +var path = require("path"); + +var baseConfig = require(path.join(__dirname, path.join("environments/" + env))); + +module.exports = baseConfig; \ No newline at end of file diff --git a/config/environments/development.js b/config/environments/development.js new file mode 100644 index 0000000..92caa70 --- /dev/null +++ b/config/environments/development.js @@ -0,0 +1,7 @@ +"use strict"; + +var config = { + port: 8000 +}; + +module.exports = config; \ No newline at end of file diff --git a/config/environments/production.js b/config/environments/production.js new file mode 100644 index 0000000..b53e048 --- /dev/null +++ b/config/environments/production.js @@ -0,0 +1,7 @@ +"use strict"; + +var config = { + port: 8080 +}; + +module.exports = config; \ No newline at end of file diff --git a/config/environments/test.js b/config/environments/test.js new file mode 100644 index 0000000..ea5ac53 --- /dev/null +++ b/config/environments/test.js @@ -0,0 +1,7 @@ +"use strict"; + +var config = { + port: 9000 +}; + +module.exports = config; \ No newline at end of file diff --git a/config/express.js b/config/express.js new file mode 100644 index 0000000..3d1794b --- /dev/null +++ b/config/express.js @@ -0,0 +1,41 @@ +"use strict"; + +var fs = require("fs"); +var path = require("path"); +var morgan = require("morgan"); +var express = require("express"); +var winston = require("winston"); +var config = require("./config.js"); +var exphbs = require('express-handlebars'); +var env = process.env.NODE_ENV || "development"; +var pkg = JSON.parse(fs.readFileSync(path.join(__dirname, "../package.json"))); + +module.exports = function (app) { + + app.use(express.static(path.join(__dirname, '../public'))); + + // Use winston on production + var log; + if (env !== 'development') { + log = { + stream: { + write: function (message, encoding) { + winston.info(message); + } + } + }; + } else { + log = 'dev'; + } + + if (env !== 'test') app.use(morgan(log)); + + var hbs = exphbs({ + defaultLayout: 'main', + extname: '.hbs' + }); + + app.engine('.hbs', hbs); + app.set('view engine', '.hbs'); + +}; \ No newline at end of file diff --git a/config/routes.js b/config/routes.js new file mode 100644 index 0000000..aba6238 --- /dev/null +++ b/config/routes.js @@ -0,0 +1,13 @@ +"use strict"; + +var config = require("./config.js"); + +module.exports = function (app) { + app.get("/", function (request, response) { + response.render("index.hbs"); + }); + + app.get("*", function (request, response) { + response.status(404).render("404.hbs"); + }); +}; \ No newline at end of file diff --git a/config/socketio.js b/config/socketio.js new file mode 100644 index 0000000..1b583c7 --- /dev/null +++ b/config/socketio.js @@ -0,0 +1,13 @@ +"use strict"; + +module.exports = function (io) { + io.on('connection', function (socket) { + socket.emit('welcome', { hello: 'world' }); + // socket.on('my other event', function (data) { + // console.log(data); + // }); + socket.on('disconnect', function () { + io.emit('user disconnected'); + }); + }); +}; \ No newline at end of file diff --git a/index.js b/index.js index e69de29..9d7557b 100644 --- a/index.js +++ b/index.js @@ -0,0 +1,22 @@ +"use strict"; + +var express = require("express"); +var app = express(); +var fs = require("fs"); +var path = require("path"); +var config = require(path.join(__dirname, "config/config.js")); +var server = require('http').Server(app); +var io = require('socket.io')(server); + +// Configure express +require(path.join(__dirname, "config/express"))(app); + +// Add routes +require(path.join(__dirname, "config/routes"))(app); + +// Configure socket.io server +require(path.join(__dirname, "config/socketio"))(io); + +console.log("Listening on port", config.port); + +module.exports = app; \ No newline at end of file diff --git a/package.json b/package.json index 3c52a95..4f37ac9 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,13 @@ "version": "1.0.0", "description": "ENSL Gather Service for Skulks with Shotguns", "main": "index.js", + "keywords": [ + "NS2", + "Skulks with Shotguns" + ], "scripts": { - "test": "NODE_ENV=test mocha spec/" + "test": "NODE_ENV=test mocha spec/", + "start": "node index.js" }, "repository": { "type": "git", @@ -17,8 +22,14 @@ }, "homepage": "https://github.com/cblanc/sws_gathers", "dependencies": { - "express": "^4.13.1", - "node-mysql": "^0.4.2", - "socket.io": "^1.3.5" + "chai": "~3.1.0", + "express": "~4.13.1", + "express-handlebars": "~2.0.1", + "mocha": "~2.2.5", + "morgan": "~1.6.1", + "node-mysql": "~0.4.2", + "socket.io": "~1.3.5", + "supertest": "~1.0.1", + "winston": "~1.0.1" } } diff --git a/public/css/app.css b/public/css/app.css new file mode 100644 index 0000000..e69de29 diff --git a/public/css/theme.css b/public/css/theme.css new file mode 100755 index 0000000..e8be396 --- /dev/null +++ b/public/css/theme.css @@ -0,0 +1,354 @@ +/*! + * Start Bootstrap - SB Admin 2 Bootstrap Admin Theme (http://startbootstrap.com) + * Code licensed under the Apache License v2.0. + * For details, see http://www.apache.org/licenses/LICENSE-2.0. + */ + +body { + background-color: #f8f8f8; +} + +#wrapper { + width: 100%; +} + +#page-wrapper { + padding: 0 15px; + min-height: 568px; + background-color: #fff; +} + +@media(min-width:768px) { + #page-wrapper { + position: inherit; + margin: 0 0 0 250px; + padding: 0 30px; + border-left: 1px solid #e7e7e7; + } +} + +.navbar-top-links { + margin-right: 0; +} + +.navbar-top-links li { + display: inline-block; +} + +.navbar-top-links li:last-child { + margin-right: 15px; +} + +.navbar-top-links li a { + padding: 15px; + min-height: 50px; +} + +.navbar-top-links .dropdown-menu li { + display: block; +} + +.navbar-top-links .dropdown-menu li:last-child { + margin-right: 0; +} + +.navbar-top-links .dropdown-menu li a { + padding: 3px 20px; + min-height: 0; +} + +.navbar-top-links .dropdown-menu li a div { + white-space: normal; +} + +.navbar-top-links .dropdown-messages, +.navbar-top-links .dropdown-tasks, +.navbar-top-links .dropdown-alerts { + width: 310px; + min-width: 0; +} + +.navbar-top-links .dropdown-messages { + margin-left: 5px; +} + +.navbar-top-links .dropdown-tasks { + margin-left: -59px; +} + +.navbar-top-links .dropdown-alerts { + margin-left: -123px; +} + +.navbar-top-links .dropdown-user { + right: 0; + left: auto; +} + +.sidebar .sidebar-nav.navbar-collapse { + padding-right: 0; + padding-left: 0; +} + +.sidebar .sidebar-search { + padding: 15px; +} + +.sidebar ul li { + border-bottom: 1px solid #e7e7e7; +} + +.sidebar ul li a.active { + background-color: #eee; +} + +.sidebar .arrow { + float: right; +} + +.sidebar .fa.arrow:before { + content: "\f104"; +} + +.sidebar .active>a>.fa.arrow:before { + content: "\f107"; +} + +.sidebar .nav-second-level li, +.sidebar .nav-third-level li { + border-bottom: 0!important; +} + +.sidebar .nav-second-level li a { + padding-left: 37px; +} + +.sidebar .nav-third-level li a { + padding-left: 52px; +} + +@media(min-width:768px) { + .sidebar { + z-index: 1; + position: absolute; + width: 250px; + margin-top: 51px; + } + + .navbar-top-links .dropdown-messages, + .navbar-top-links .dropdown-tasks, + .navbar-top-links .dropdown-alerts { + margin-left: auto; + } +} + +.btn-outline { + color: inherit; + background-color: transparent; + transition: all .5s; +} + +.btn-primary.btn-outline { + color: #428bca; +} + +.btn-success.btn-outline { + color: #5cb85c; +} + +.btn-info.btn-outline { + color: #5bc0de; +} + +.btn-warning.btn-outline { + color: #f0ad4e; +} + +.btn-danger.btn-outline { + color: #d9534f; +} + +.btn-primary.btn-outline:hover, +.btn-success.btn-outline:hover, +.btn-info.btn-outline:hover, +.btn-warning.btn-outline:hover, +.btn-danger.btn-outline:hover { + color: #fff; +} + +.chat { + margin: 0; + padding: 0; + list-style: none; +} + +.chat li { + margin-bottom: 10px; + padding-bottom: 5px; + border-bottom: 1px dotted #999; +} + +.chat li.left .chat-body { + margin-left: 60px; +} + +.chat li.right .chat-body { + margin-right: 60px; +} + +.chat li .chat-body p { + margin: 0; +} + +.panel .slidedown .glyphicon, +.chat .glyphicon { + margin-right: 5px; +} + +.chat-panel .panel-body { + height: 350px; + overflow-y: scroll; +} + +.login-panel { + margin-top: 25%; +} + +.flot-chart { + display: block; + height: 400px; +} + +.flot-chart-content { + width: 100%; + height: 100%; +} + +.dataTables_wrapper { + position: relative; + clear: both; +} + +table.dataTable thead .sorting, +table.dataTable thead .sorting_asc, +table.dataTable thead .sorting_desc, +table.dataTable thead .sorting_asc_disabled, +table.dataTable thead .sorting_desc_disabled { + background: 0 0; +} + +table.dataTable thead .sorting_asc:after { + content: "\f0de"; + float: right; + font-family: fontawesome; +} + +table.dataTable thead .sorting_desc:after { + content: "\f0dd"; + float: right; + font-family: fontawesome; +} + +table.dataTable thead .sorting:after { + content: "\f0dc"; + float: right; + font-family: fontawesome; + color: rgba(50,50,50,.5); +} + +.btn-circle { + width: 30px; + height: 30px; + padding: 6px 0; + border-radius: 15px; + text-align: center; + font-size: 12px; + line-height: 1.428571429; +} + +.btn-circle.btn-lg { + width: 50px; + height: 50px; + padding: 10px 16px; + border-radius: 25px; + font-size: 18px; + line-height: 1.33; +} + +.btn-circle.btn-xl { + width: 70px; + height: 70px; + padding: 10px 16px; + border-radius: 35px; + font-size: 24px; + line-height: 1.33; +} + +.show-grid [class^=col-] { + padding-top: 10px; + padding-bottom: 10px; + border: 1px solid #ddd; + background-color: #eee!important; +} + +.show-grid { + margin: 15px 0; +} + +.huge { + font-size: 40px; +} + +.panel-green { + border-color: #5cb85c; +} + +.panel-green .panel-heading { + border-color: #5cb85c; + color: #fff; + background-color: #5cb85c; +} + +.panel-green a { + color: #5cb85c; +} + +.panel-green a:hover { + color: #3d8b3d; +} + +.panel-red { + border-color: #d9534f; +} + +.panel-red .panel-heading { + border-color: #d9534f; + color: #fff; + background-color: #d9534f; +} + +.panel-red a { + color: #d9534f; +} + +.panel-red a:hover { + color: #b52b27; +} + +.panel-yellow { + border-color: #f0ad4e; +} + +.panel-yellow .panel-heading { + border-color: #f0ad4e; + color: #fff; + background-color: #f0ad4e; +} + +.panel-yellow a { + color: #f0ad4e; +} + +.panel-yellow a:hover { + color: #df8a13; +} \ No newline at end of file diff --git a/public/css/timeline.css b/public/css/timeline.css new file mode 100755 index 0000000..92161eb --- /dev/null +++ b/public/css/timeline.css @@ -0,0 +1,180 @@ +.timeline { + position: relative; + padding: 20px 0 20px; + list-style: none; +} + +.timeline:before { + content: " "; + position: absolute; + top: 0; + bottom: 0; + left: 50%; + width: 3px; + margin-left: -1.5px; + background-color: #eeeeee; +} + +.timeline > li { + position: relative; + margin-bottom: 20px; +} + +.timeline > li:before, +.timeline > li:after { + content: " "; + display: table; +} + +.timeline > li:after { + clear: both; +} + +.timeline > li:before, +.timeline > li:after { + content: " "; + display: table; +} + +.timeline > li:after { + clear: both; +} + +.timeline > li > .timeline-panel { + float: left; + position: relative; + width: 46%; + padding: 20px; + border: 1px solid #d4d4d4; + border-radius: 2px; + -webkit-box-shadow: 0 1px 6px rgba(0,0,0,0.175); + box-shadow: 0 1px 6px rgba(0,0,0,0.175); +} + +.timeline > li > .timeline-panel:before { + content: " "; + display: inline-block; + position: absolute; + top: 26px; + right: -15px; + border-top: 15px solid transparent; + border-right: 0 solid #ccc; + border-bottom: 15px solid transparent; + border-left: 15px solid #ccc; +} + +.timeline > li > .timeline-panel:after { + content: " "; + display: inline-block; + position: absolute; + top: 27px; + right: -14px; + border-top: 14px solid transparent; + border-right: 0 solid #fff; + border-bottom: 14px solid transparent; + border-left: 14px solid #fff; +} + +.timeline > li > .timeline-badge { + z-index: 100; + position: absolute; + top: 16px; + left: 50%; + width: 50px; + height: 50px; + margin-left: -25px; + border-radius: 50% 50% 50% 50%; + text-align: center; + font-size: 1.4em; + line-height: 50px; + color: #fff; + background-color: #999999; +} + +.timeline > li.timeline-inverted > .timeline-panel { + float: right; +} + +.timeline > li.timeline-inverted > .timeline-panel:before { + right: auto; + left: -15px; + border-right-width: 15px; + border-left-width: 0; +} + +.timeline > li.timeline-inverted > .timeline-panel:after { + right: auto; + left: -14px; + border-right-width: 14px; + border-left-width: 0; +} + +.timeline-badge.primary { + background-color: #2e6da4 !important; +} + +.timeline-badge.success { + background-color: #3f903f !important; +} + +.timeline-badge.warning { + background-color: #f0ad4e !important; +} + +.timeline-badge.danger { + background-color: #d9534f !important; +} + +.timeline-badge.info { + background-color: #5bc0de !important; +} + +.timeline-title { + margin-top: 0; + color: inherit; +} + +.timeline-body > p, +.timeline-body > ul { + margin-bottom: 0; +} + +.timeline-body > p + p { + margin-top: 5px; +} + +@media(max-width:767px) { + ul.timeline:before { + left: 40px; + } + + ul.timeline > li > .timeline-panel { + width: calc(100% - 90px); + width: -moz-calc(100% - 90px); + width: -webkit-calc(100% - 90px); + } + + ul.timeline > li > .timeline-badge { + top: 16px; + left: 15px; + margin-left: 0; + } + + ul.timeline > li > .timeline-panel { + float: right; + } + + ul.timeline > li > .timeline-panel:before { + right: auto; + left: -15px; + border-right-width: 15px; + border-left-width: 0; + } + + ul.timeline > li > .timeline-panel:after { + right: auto; + left: -14px; + border-right-width: 14px; + border-left-width: 0; + } +} \ No newline at end of file diff --git a/public/js/app.js b/public/js/app.js new file mode 100644 index 0000000..e69de29 diff --git a/public/js/socketio.js b/public/js/socketio.js new file mode 100644 index 0000000..405a7cc --- /dev/null +++ b/public/js/socketio.js @@ -0,0 +1,3 @@ +$(function () { + +}); \ No newline at end of file diff --git a/public/js/theme.js b/public/js/theme.js new file mode 100755 index 0000000..5be2c88 --- /dev/null +++ b/public/js/theme.js @@ -0,0 +1,36 @@ +$(function() { + + $('#side-menu').metisMenu(); + +}); + +//Loads the correct sidebar on window load, +//collapses the sidebar on window resize. +// Sets the min-height of #page-wrapper to window size +$(function() { + $(window).bind("load resize", function() { + topOffset = 50; + width = (this.window.innerWidth > 0) ? this.window.innerWidth : this.screen.width; + if (width < 768) { + $('div.navbar-collapse').addClass('collapse'); + topOffset = 100; // 2-row-menu + } else { + $('div.navbar-collapse').removeClass('collapse'); + } + + height = ((this.window.innerHeight > 0) ? this.window.innerHeight : this.screen.height) - 1; + height = height - topOffset; + if (height < 1) height = 1; + if (height > topOffset) { + $("#page-wrapper").css("min-height", (height) + "px"); + } + }); + + var url = window.location; + var element = $('ul.nav a').filter(function() { + return this.href == url || url.href.indexOf(this.href) == 0; + }).addClass('active').parent().parent().addClass('in').parent(); + if (element.is('li')) { + element.addClass('active'); + } +}); diff --git a/spec/helpers/index.js b/spec/helpers/index.js new file mode 100644 index 0000000..2f7e3f1 --- /dev/null +++ b/spec/helpers/index.js @@ -0,0 +1,10 @@ +"use strict"; + +var fs = require("fs"); +var path = require("path"); + +var helpers = {} + +helpers.app = require(path.join(__dirname, "../../index.js")); + +module.exports = helpers; \ No newline at end of file diff --git a/spec/integration.js b/spec/integration.js new file mode 100644 index 0000000..a787c10 --- /dev/null +++ b/spec/integration.js @@ -0,0 +1,21 @@ +"use strict"; + +var helper = require("./helpers/index.js"); +var request = require("supertest"); +var app = helper.app; + +describe("Basic Spec", function () { + it ("returns 200", function (done) { + request(app) + .get("/") + .expect(200) + .end(done); + }); + + it ("returns 404 if page not found", function (done) { + request(app) + .get("/foo") + .expect(404) + .end(done); + }); +}); \ No newline at end of file diff --git a/views/404.hbs b/views/404.hbs new file mode 100644 index 0000000..76f55c1 --- /dev/null +++ b/views/404.hbs @@ -0,0 +1,10 @@ +
+
+
+
+

Four Oh Four

+

Page not found

+
+
+
+
\ No newline at end of file diff --git a/views/index.hbs b/views/index.hbs new file mode 100644 index 0000000..6ac2186 --- /dev/null +++ b/views/index.hbs @@ -0,0 +1,14 @@ +
+
+
+
+
+
+ +
+
+ +
+
+
+
\ No newline at end of file diff --git a/views/layouts/main.hbs b/views/layouts/main.hbs new file mode 100644 index 0000000..eb7cc5a --- /dev/null +++ b/views/layouts/main.hbs @@ -0,0 +1,9 @@ + + {{>head}} + +
+ {{>menu}} + {{{ body }}} +
+ + \ No newline at end of file diff --git a/views/partials/head.hbs b/views/partials/head.hbs new file mode 100644 index 0000000..1006671 --- /dev/null +++ b/views/partials/head.hbs @@ -0,0 +1,20 @@ + + + + + + Skulks with Shotguns + + + + + + + + + + + + + + \ No newline at end of file diff --git a/views/partials/menu.hbs b/views/partials/menu.hbs new file mode 100644 index 0000000..5f6b688 --- /dev/null +++ b/views/partials/menu.hbs @@ -0,0 +1,36 @@ + \ No newline at end of file