A lot of changes.....

This commit is contained in:
Chris Blanchard 2015-07-28 16:54:29 +01:00
parent cf2b9643fb
commit 9515c8c6f7
19 changed files with 2512 additions and 961 deletions

21
gulpfile.js Normal file
View file

@ -0,0 +1,21 @@
"use strict";
var gulp = require("gulp");
var react = require("gulp-react");
var concat = require("gulp-concat");
var watch = require("gulp-watch");
var plumber = require("gulp-plumber");
gulp.task('default', ['compile']);
gulp.task('compile', function () {
return gulp.src('lib/react/**')
.pipe(plumber())
.pipe(concat('app.js'))
.pipe(react())
.pipe(gulp.dest('public/js/'));
});
gulp.task('watch', function () {
gulp.watch('lib/react/**', ['compile']);
});

View file

@ -53,7 +53,11 @@ async.parallel(instructions, function (error) {
console.log("Error while adding gatherers", error);
} else {
console.log("Loaded gatherers");
gather.gatherers.forEach(function (gatherer, index, array) {
var candidate = Math.floor(Math.random() * array.length);
array[index].leaderVote = array[candidate].id;
});
console.log("Assigned vote for each gatherer");
}
});
@ -65,7 +69,7 @@ module.exports = function (namespace) {
namespace.sockets.forEach(function (socket) {
socket.emit("gather:refresh", {
gather: gather.toJson(),
currentUser: socket._user
currentGatherer: gather.getGatherer(socket._user)
});
});
};

View file

@ -164,6 +164,14 @@ Gather.prototype.voteForLeader = function (voter, candidate) {
});
};
Gather.prototype.getGatherer = function (user) {
var matchingGatherer = null;
this.gatherers.forEach(function (gatherer) {
if (gatherer.id === user.id) matchingGatherer = gatherer;
});
return matchingGatherer;
};
// Gather States
// - Gathering
// - Election

View file

@ -1,573 +0,0 @@
$(function () {
"use strict";
var UserCounter = React.createClass({
render: function () {
return (
<li>
<a href="#">
<i className="fa fa-users fa-fw"></i> Online
<span className="badge add-left"> {this.props.count} </span>
</a>
</li>
);
}
});
var UserLogin = React.createClass({
authorizeId: function (id) {
id = parseInt(id, 10);
socket.emit("users:authorize", {
id: id
});
},
handleSubmit: function (e) {
e.preventDefault();
var id = React.findDOMNode(this.refs.authorize_id).value.trim();
if (!id) return;
React.findDOMNode(this.refs.authorize_id).value = '';
this.authorizeId(id);
return;
},
render: function () {
return (
<form onSubmit={this.handleSubmit} >
<div className="input-group signin">
<input
id="btn-input"
type="text"
className="form-control"
ref="authorize_id"
placeholder="Choose an ID..." />
<span className="input-group-btn">
<input
type="submit"
className="btn btn-primary"
id="btn-chat"
value="Login" />
</span>
</div>
<div className="signin">
<p className="text-center"><small>Just a temporary measure until genuine authentication is implemented</small></p>
</div>
</form>
);
}
})
var UserMenu = React.createClass({
getDefaultProps: function () {
return {
count: 0,
users: []
};
},
componentDidMount: function () {
socket.on('users:update', this.updateUsers);
},
updateUsers: function (data) {
this.setProps({
count: data.count,
users: data.users
});
},
render: function () {
var users = this.props.users.map(function (user) {
return (
<li key={user.id}><a href="#">{user.username}</a></li>
);
});
return (
<ul className="nav" id="side-menu">
<UserCounter {...this.props} />
{users}
<li><br /><UserLogin /><br /></li>
</ul>
);
}
});
var Chatroom = React.createClass({
getDefaultProps: function () {
return {
history: []
};
},
componentDidMount: function () {
var self = this;
var TIMER_INTERVAL = 60000; // Every minute
socket.on("message:new", function (data) {
var history = self.props.history;
history.push(data);
self.setProps({
history: history
});
self.scrollToBottom();
});
// Message History Retrieved
socket.on("message:refresh", function (data) {
self.setProps({
history: data.chatHistory
});
self.scrollToBottom();
});
socket.emit("message:refresh", {});
self.timer = setInterval(function () {
if (self.refs.messages) self.refs.messages.refreshTime();
}, TIMER_INTERVAL);
},
componentDidUnmount: function () {
clearInterval(this.timer);
},
sendMessage: function (message) {
socket.emit("newMessage", {message: message});
},
scrollToBottom: function () {
var node = React.findDOMNode(this.refs.messageContainer);
node.scrollTop = node.scrollHeight;
},
render: function () {
var messages = this.props.history.map(function (message) {
return (
<ChatMessage
avatar={message.author.avatar}
username={message.author.username}
content={message.content}
ref="messages"
createdAt={message.createdAt} />
);
});
return (
<div className="panel panel-default">
<div className="panel-heading">Gather Chat</div>
<div className="panel-body">
<ul className="chat" id="chatmessages" ref="messageContainer">
{messages}
</ul>
</div>
<div className="panel-footer">
<MessageBar />
</div>
</div>
);
}
});
var ChatMessage = React.createClass({
getInitialState: function () {
return {
timeAgo: $.timeago(this.props.createdAt)
}
},
refreshTime: function () {
var self = this;
self.setState({
timeAgo: $.timeago(self.props.createdAt)
});
},
render: function () {
return (
<li className="left clearfix">
<span className="chat-img pull-left">
<img
src={this.props.avatar}
alt="User Avatar"
height="40"
width="40"
className="img-circle" />
</span>
<div className="chat-body clearfix">
<div className="header">
<strong className="primary-font">{this.props.username}</strong>
<small className="pull-right text-muted">
<i className="fa fa-clock-o fa-fw"></i> {this.state.timeAgo}
</small>
</div>
<p>{this.props.content}</p>
</div>
</li>
);
}
});
var CurrentUser = React.createClass({
componentDidMount: function () {
var self = this;
socket.on("users:update", function (data) {
self.setProps({
user: data.currentUser
});
});
socket.emit("users:refresh", {});
},
render: function () {
if (this.props.user) {
return (
<a href="#">{this.props.user.username} &nbsp;<img src={this.props.user.avatar}
alt="User Avatar"
height="20"
width="20" /></a>
);
} else {
return false;
}
}
});
var VoteButton = React.createClass({
cancelVote: function (e) {
socket.emit("gather:vote", {
leader: {
candidate: null
}
});
},
vote: function (e) {
e.preventDefault();
socket.emit("gather:vote", {
leader: {
candidate: parseInt(e.target.value, 10)
}
});
},
render: function () {
if (this.props.currentGatherer === null) {
return false;
}
if (this.props.currentGatherer.leaderVote === this.props.candidate.id) {
return (
<button
onClick={this.cancelVote}
className="btn btn-xs btn-success">Voted
</button>
);
} else {
return (
<button
onClick={this.vote}
className="btn btn-xs btn-default"
value={this.props.candidate.id}>Vote
</button>
);
}
}
});
var MessageBar = React.createClass({
sendMessage: function (content) {
socket.emit("message:new", {
content: content
});
},
handleSubmit: function (e) {
e.preventDefault();
var content = React.findDOMNode(this.refs.content).value.trim();
if (!content) return;
React.findDOMNode(this.refs.content).value = '';
this.sendMessage(content);
return;
},
render: function () {
return (
<form onSubmit={this.handleSubmit} >
<div className="input-group">
<input
id="btn-input"
type="text"
className="form-control"
ref="content"
placeholder="Be polite please..." />
<span className="input-group-btn">
<input
type="submit"
className="btn btn-primary"
id="btn-chat"
value="Send" />
</span>
</div>
</form>
);
}
});
var JoinGatherButton = React.createClass({
joinGather: function (e) {
e.preventDefault();
socket.emit("gather:join", {});
},
render: function () {
var message = this.props.buttonName || "Join Gather";
var buttonClass = "btn btn-primary";
if (this.props.buttonClass) {
buttonClass += " " + this.props.buttonClass;
}
return (<button
onClick={this.joinGather}
className={buttonClass}>{message}</button>)
}
});
var GatherProgress = React.createClass({
gatheringProgress: function () {
var num = this.props.gather.gatherers.length;
var den = 12;
return {
num: num,
den: den,
message: num + " / " + den
};
},
electionProgress: function () {
var num = this.props.gather.gatherers.reduce(function (acc, gatherer) {
if (gatherer.leaderVote) acc++;
return acc;
}, 0);
var den = 12;
return {
num: num,
den: den,
message: den - num + " more votes required"
};
},
selectionProgress: function () {
var num = this.props.gather.gatherers.reduce(function (acc, gatherer) {
if (gatherer.team !== "lobby") acc++;
return acc;
}, 0);
var den = 12;
return {
num: num,
den: den,
message: num + " out of " + den + " players assigned"
};
},
render: function () {
var progress;
var gatherState = this.props.gather.state;
if (gatherState === 'gathering' && this.props.gather.gatherers.length) {
progress = this.gatheringProgress();
} else if (gatherState === 'election') {
progress = this.electionProgress();
} else if (gatherState === 'selection') {
progress = this.selectionProgress();
}
if (progress) {
var style = {
width: Math.round((progress.num / progress.den * 100)) + "%"
};
return (
<div className="panel-body">
<p>Gather Progress</p>
<div className="progress">
<div className="progress-bar progress-bar-striped active"
data-role="progressbar"
data-aria-valuenow={progress.num}
data-aria-valuemin="0"
data-aria-valuemax={progress.den}
style={style}>
{progress.message}
</div>
</div>
</div>
);
} else {
return false;
}
}
});
var Gather = React.createClass({
getDefaultProps: function () {
return {
gather: {
gatherers: []
}
}
},
joinedGather: function () {
var self = this;
return this.props.gather.gatherers.some(function (gatherer) {
return gatherer.user.id === self.props.currentUser.id;
});
},
componentDidMount: function () {
var self = this;
socket.on("gather:refresh", function (data) {
self.setProps({
gather: data.gather,
currentUser: data.currentUser
});
});
},
stateDescription: function () {
switch(this.props.gather.state) {
case "gathering":
return "Waiting for more gatherers";
case "election":
return "Currently voting for team leaders";
case "selection":
return "Waiting for leaders to picking teams";
case "done":
return "Gather completed";
default:
return "Initialising gather";
}
},
leaveGather: function (e) {
e.preventDefault();
socket.emit("gather:leave", {});
},
inviteToGather: function (e) {
e.preventDefault();
},
currentGatherer: function () {
var current = null;
var self = this;
this.props.gather.gatherers.forEach(function (gatherer) {
if (gatherer.id === self.props.currentUser.id) current = gatherer;
});
return current;
},
render: function () {
var joinButton;
if (this.joinedGather()) {
joinButton = (<li><button
onClick={this.leaveGather}
className="btn btn-danger">Leave Gather</button></li>);
} else {
joinButton = (<li><JoinGatherButton /></li>);
}
var inviteButton;
if (this.props.gather.state === 'gathering') {
inviteButton = (<li><button
onClick={this.inviteToGather}
className="btn btn-primary">Invite to Gather</button></li>);
}
return (
<div className="panel panel-default">
<div className="panel-heading">
<strong>NS2 Gather </strong>
<span className="badge add-left">{this.props.gather.gatherers.length}</span>
<br />
{this.stateDescription()}
</div>
<Gatherers gather={this.props.gather} currentGatherer={this.currentGatherer()} />
<GatherProgress gather={this.props.gather} />
<div className="panel-footer text-right">
<ul className="list-inline">
{inviteButton}
{joinButton}
</ul>
</div>
</div>
);
}
});
var LeaderPoll = React.createClass({
render: function () {
return (
<div className="panel-body">
</div>
);
}
});
var Gatherers = React.createClass({
render: function () {
var self = this;
var gatherers = this.props.gather.gatherers.map(function (gatherer) {
var lifeforms = (
gatherer.user.ability.lifeforms.map(function (lifeform) {
return (<span className="label label-default">{lifeform}</span>);
})
);
var commBadge;
if (gatherer.user.ability.commander) {
commBadge = (<img src="/images/commander.png"
alt="Commander"
height="20"
width="20" />);
}
var division = (<span className="label label-primary">{gatherer.user.ability.division}</span>);
var action = lifeforms;
if (self.props.gather.state === "election") {
var votes = self.props.gather.gatherers.reduce(function (acc, voter) {
if (voter.leaderVote === gatherer.id) acc++;
return acc;
}, 0)
action = (
<div className="text-right">
<small>{votes + " votes"} &nbsp;</small>
<VoteButton currentGatherer={self.props.currentGatherer} candidate={gatherer} />
</div>
);
}
return (
<tr key={gatherer.user.id}>
<td className="col-md-1">{commBadge}</td>
<td className="col-md-5">{gatherer.user.username}</td>
<td className="col-md-3">{division}&nbsp;</td>
<td className="col-md-2">{action}&nbsp;</td>
</tr>
);
})
if (this.props.gather.gatherers.length) {
return (
<div className="panel-body">
<div className="panel panel-default">
<div className="panel-heading">
<h5 className="panel-title">Roster</h5>
</div>
<table className="table roster-table">
<tbody>
{gatherers}
</tbody>
</table>
</div>
</div>
);
} else {
return (<div className="panel-body text-center"><JoinGatherButton buttonClass="btn-lg" buttonName="Start a Gather" /></div>);
}
}
});
var socket;
function initialiseComponents () {
var socketUrl = window.location.protocol + "//" + window.location.host;
socket = io(socketUrl)
.on("connect", function () {
console.log("Connected");
})
.on("reconnect", function () {
console.log("Reconnected");
})
.on("disconnect", function () {
console.log("Disconnected")
});
React.render(<UserMenu />, document.getElementById('side-menu'));
React.render(<Chatroom />, document.getElementById('chatroom'));
React.render(<Gather />, document.getElementById('gathers'));
React.render(<CurrentUser />, document.getElementById('currentuser'));
};
initialiseComponents();
});

354
lib/react/gather.jsx Normal file
View file

@ -0,0 +1,354 @@
"use strict";
var VoteButton = React.createClass({
cancelVote: function (e) {
socket.emit("gather:vote", {
leader: {
candidate: null
}
});
},
vote: function (e) {
e.preventDefault();
socket.emit("gather:vote", {
leader: {
candidate: parseInt(e.target.value, 10)
}
});
},
render: function () {
if (this.props.currentGatherer === null) {
return false;
}
if (this.props.currentGatherer.leaderVote === this.props.candidate.id) {
return (
<button
onClick={this.cancelVote}
className="btn btn-xs btn-success">Voted
</button>
);
} else {
return (
<button
onClick={this.vote}
className="btn btn-xs btn-default"
value={this.props.candidate.id}>Vote
</button>
);
}
}
});
var JoinGatherButton = React.createClass({
joinGather: function (e) {
e.preventDefault();
socket.emit("gather:join", {});
},
render: function () {
var message = this.props.buttonName || "Join Gather";
var buttonClass = "btn btn-primary";
if (this.props.buttonClass) {
buttonClass += " " + this.props.buttonClass;
}
return (<button
onClick={this.joinGather}
className={buttonClass}>{message}</button>)
}
});
var SelectPlayerButton = React.createClass({
selectPlayer: function (e) {
e.preventDefault();
},
render: function () {
if (!this.props.currentGatherer.leader) {
return false;
} else {
return (<button
onClick={this.selectPlayer}
className="btn btn-xs btn-primary"> Select
</button>
);
}
}
})
var GatherTeams = React.createClass({
alienGatherers: function () {
return this.props.gather.gatherers.filter(function (gatherer) {
return gatherer.team === "alien";
}).sort(function (gatherer) {
return (gatherer.leader) ? 1 : 0;
});
},
marineGatherers: function () {
return this.props.gather.gatherers.filter(function (gatherer) {
return gatherer.team === "marine";
}).sort(function (gatherer) {
return (gatherer.leader) ? 1 : 0;
});
},
render: function () {
var extractGatherer = function (gatherer) {
var image;
if (gatherer.leader) {
image = (<img src="/images/commander.png"
alt="Commander"
height="20"
width="20" />);
}
return (
<tr key={gatherer.id}>
<td className="col-md-1">{image}</td>
<td className="col-md-11">{gatherer.user.username}</td>
</tr>
);
}
var marines = this.marineGatherers().map(extractGatherer);
var aliens = this.alienGatherers().map(extractGatherer);
return (
<div className="panel-body">
<div className="row">
<div className="col-md-6">
<div className="panel panel-default">
<div className="panel-heading">
Aliens
</div>
<table className="table">
<tbody>
{aliens}
</tbody>
</table>
</div>
</div>
<div className="col-md-6">
<div className="panel panel-default">
<div className="panel-heading">
Marines
</div>
<table className="table">
<tbody>
{marines}
</tbody>
</table>
</div>
</div>
</div>
</div>
);
}
})
var GatherProgress = React.createClass({
stateDescription: function () {
switch(this.props.gather.state) {
case "gathering":
return "Waiting for more gatherers.";
case "election":
return "Currently voting for team leaders.";
case "selection":
return "Waiting for leaders to picking teams.";
case "done":
return "Gather completed.";
default:
return "Initialising gather.";
}
},
gatheringProgress: function () {
var num = this.props.gather.gatherers.length;
var den = 12;
var remaining = den - num;
var message = (remaining === 1) ? "Waiting for last player" : "Waiting for " + remaining + " more players";
return {
num: num,
den: den,
message: message
};
},
electionProgress: function () {
var num = this.props.gather.gatherers.reduce(function (acc, gatherer) {
if (gatherer.leaderVote) acc++;
return acc;
}, 0);
var den = 12;
return {
num: num,
den: den,
message: den - num + " more votes required"
};
},
selectionProgress: function () {
var num = this.props.gather.gatherers.reduce(function (acc, gatherer) {
if (gatherer.team !== "lobby") acc++;
return acc;
}, 0);
var den = 12;
return {
num: num,
den: den,
message: num + " out of " + den + " players assigned"
};
},
render: function () {
var progress;
var gatherState = this.props.gather.state;
if (gatherState === 'gathering' && this.props.gather.gatherers.length) {
progress = this.gatheringProgress();
} else if (gatherState === 'election') {
progress = this.electionProgress();
} else if (gatherState === 'selection') {
progress = this.selectionProgress();
}
if (progress) {
var style = {
width: Math.round((progress.num / progress.den * 100)) + "%"
};
return (
<div className="panel-body">
<p><strong>{this.stateDescription()}</strong> {progress.message}</p>
<div className="progress">
<div className="progress-bar progress-bar-striped active"
data-role="progressbar"
data-aria-valuenow={progress.num}
data-aria-valuemin="0"
data-aria-valuemax={progress.den}
style={style}>
</div>
</div>
</div>
);
} else {
return false;
}
}
});
var Gather = React.createClass({
getDefaultProps: function () {
return {
gather: {
gatherers: []
}
}
},
joinedGather: function () {
return this.props.currentGatherer !== null;
},
componentDidMount: function () {
var self = this;
socket.on("gather:refresh", function (data) {
self.setProps({
gather: data.gather,
currentGatherer: data.currentGatherer
});
});
},
leaveGather: function (e) {
e.preventDefault();
socket.emit("gather:leave", {});
},
inviteToGather: function (e) {
e.preventDefault();
},
render: function () {
var joinButton;
if (this.joinedGather()) {
joinButton = (<li><button
onClick={this.leaveGather}
className="btn btn-danger">Leave Gather</button></li>);
} else {
joinButton = (<li><JoinGatherButton /></li>);
}
var inviteButton;
if (this.props.gather.state === 'gathering') {
inviteButton = (<li><button
onClick={this.inviteToGather}
className="btn btn-primary">Invite to Gather</button></li>);
}
var gatherTeams;
if (this.props.gather.state === 'selection') {
gatherTeams = <GatherTeams gather={this.props.gather} />
}
return (
<div className="panel panel-default">
<div className="panel-heading">
<strong>NS2 Gather </strong>
<span className="badge add-left">{this.props.gather.gatherers.length}</span>
</div>
<Gatherers gather={this.props.gather} currentGatherer={this.props.currentGatherer} />
{gatherTeams}
<GatherProgress gather={this.props.gather} />
<div className="panel-footer text-right">
<ul className="list-inline">
{inviteButton}
{joinButton}
</ul>
</div>
</div>
);
}
});
var Gatherers = React.createClass({
render: function () {
var self = this;
var gatherers = this.props.gather.gatherers.map(function (gatherer) {
var lifeforms = (
gatherer.user.ability.lifeforms.map(function (lifeform) {
return (<span className="label label-default">{lifeform}</span>);
})
);
// Switch this to online status
var online= (<span src="/images/commander.png"
alt="online"
className="user-online">&nbsp;</span>);
var division = (<span className="label label-primary">{gatherer.user.ability.division}</span>);
var action = lifeforms;
if (self.props.gather.state === "election") {
var votes = self.props.gather.gatherers.reduce(function (acc, voter) {
if (voter.leaderVote === gatherer.id) acc++;
return acc;
}, 0)
action = (
<span>
<small>{votes + " votes"} &nbsp;</small>
<VoteButton currentGatherer={self.props.currentGatherer} candidate={gatherer} />
</span>
);
}
return (
<tr key={gatherer.user.id}>
<td className="col-md-1">{online}</td>
<td className="col-md-5">{gatherer.user.username}</td>
<td className="col-md-3">{division}&nbsp;</td>
<td className="col-md-2 text-right">{action}&nbsp;</td>
</tr>
);
})
if (this.props.gather.gatherers.length) {
return (
<div className="panel-body">
<div className="panel panel-default">
<div className="panel-heading">
<h5 className="panel-title">Roster</h5>
</div>
<table className="table roster-table">
<tbody>
{gatherers}
</tbody>
</table>
</div>
</div>
);
} else {
return (<div className="panel-body text-center"><JoinGatherButton buttonClass="btn-lg" buttonName="Start a Gather" /></div>);
}
}
});

22
lib/react/init.jsx Normal file
View file

@ -0,0 +1,22 @@
"use strict";
var socket;
function initialiseComponents () {
var socketUrl = window.location.protocol + "//" + window.location.host;
socket = io(socketUrl)
.on("connect", function () {
console.log("Connected");
})
.on("reconnect", function () {
console.log("Reconnected");
})
.on("disconnect", function () {
console.log("Disconnected")
});
React.render(<UserMenu />, document.getElementById('side-menu'));
React.render(<Chatroom />, document.getElementById('chatroom'));
React.render(<Gather />, document.getElementById('gathers'));
React.render(<CurrentUser />, document.getElementById('currentuser'));
};

146
lib/react/message.jsx Normal file
View file

@ -0,0 +1,146 @@
"use strict";
var Chatroom = React.createClass({
getDefaultProps: function () {
return {
history: []
};
},
componentDidMount: function () {
var self = this;
var TIMER_INTERVAL = 60000; // Every minute
socket.on("message:new", function (data) {
var history = self.props.history;
history.push(data);
self.setProps({
history: history
});
self.scrollToBottom();
});
// Message History Retrieved
socket.on("message:refresh", function (data) {
self.setProps({
history: data.chatHistory
});
self.scrollToBottom();
});
socket.emit("message:refresh", {});
self.timer = setInterval(function () {
if (self.refs.messages) self.refs.messages.refreshTime();
}, TIMER_INTERVAL);
},
componentDidUnmount: function () {
clearInterval(this.timer);
},
sendMessage: function (message) {
socket.emit("newMessage", {message: message});
},
scrollToBottom: function () {
var node = React.findDOMNode(this.refs.messageContainer);
node.scrollTop = node.scrollHeight;
},
render: function () {
var messages = this.props.history.map(function (message) {
return (
<ChatMessage
avatar={message.author.avatar}
username={message.author.username}
content={message.content}
ref="messages"
createdAt={message.createdAt} />
);
});
return (
<div className="panel panel-default">
<div className="panel-heading">Gather Chat</div>
<div className="panel-body">
<ul className="chat" id="chatmessages" ref="messageContainer">
{messages}
</ul>
</div>
<div className="panel-footer">
<MessageBar />
</div>
</div>
);
}
});
var ChatMessage = React.createClass({
getInitialState: function () {
return {
timeAgo: $.timeago(this.props.createdAt)
}
},
refreshTime: function () {
var self = this;
self.setState({
timeAgo: $.timeago(self.props.createdAt)
});
},
render: function () {
return (
<li className="left clearfix">
<span className="chat-img pull-left">
<img
src={this.props.avatar}
alt="User Avatar"
height="40"
width="40"
className="img-circle" />
</span>
<div className="chat-body clearfix">
<div className="header">
<strong className="primary-font">{this.props.username}</strong>
<small className="pull-right text-muted">
<i className="fa fa-clock-o fa-fw"></i> {this.state.timeAgo}
</small>
</div>
<p>{this.props.content}</p>
</div>
</li>
);
}
});
var MessageBar = React.createClass({
sendMessage: function (content) {
socket.emit("message:new", {
content: content
});
},
handleSubmit: function (e) {
e.preventDefault();
var content = React.findDOMNode(this.refs.content).value.trim();
if (!content) return;
React.findDOMNode(this.refs.content).value = '';
this.sendMessage(content);
return;
},
render: function () {
return (
<form onSubmit={this.handleSubmit} >
<div className="input-group">
<input
id="btn-input"
type="text"
className="form-control"
ref="content"
placeholder="Be polite please..." />
<span className="input-group-btn">
<input
type="submit"
className="btn btn-primary"
id="btn-chat"
value="Send" />
</span>
</div>
</form>
);
}
});

111
lib/react/user.jsx Normal file
View file

@ -0,0 +1,111 @@
"use strict";
var UserCounter = React.createClass({
render: function () {
return (
<li>
<a href="#">
<i className="fa fa-users fa-fw"></i> Online
<span className="badge add-left"> {this.props.count} </span>
</a>
</li>
);
}
});
var UserLogin = React.createClass({
authorizeId: function (id) {
id = parseInt(id, 10);
socket.emit("users:authorize", {
id: id
});
},
handleSubmit: function (e) {
e.preventDefault();
var id = React.findDOMNode(this.refs.authorize_id).value.trim();
if (!id) return;
React.findDOMNode(this.refs.authorize_id).value = '';
this.authorizeId(id);
return;
},
render: function () {
return (
<form onSubmit={this.handleSubmit} >
<div className="input-group signin">
<input
id="btn-input"
type="text"
className="form-control"
ref="authorize_id"
placeholder="Choose an ID..." />
<span className="input-group-btn">
<input
type="submit"
className="btn btn-primary"
id="btn-chat"
value="Login" />
</span>
</div>
<div className="signin">
<p className="text-center"><small>Just a temporary measure until genuine authentication is implemented</small></p>
</div>
</form>
);
}
})
var UserMenu = React.createClass({
getDefaultProps: function () {
return {
count: 0,
users: []
};
},
componentDidMount: function () {
socket.on('users:update', this.updateUsers);
},
updateUsers: function (data) {
this.setProps({
count: data.count,
users: data.users
});
},
render: function () {
var users = this.props.users.map(function (user) {
return (
<li key={user.id}><a href="#">{user.username}</a></li>
);
});
return (
<ul className="nav" id="side-menu">
<UserCounter {...this.props} />
{users}
<li><br /><UserLogin /><br /></li>
</ul>
);
}
});
var CurrentUser = React.createClass({
componentDidMount: function () {
var self = this;
socket.on("users:update", function (data) {
self.setProps({
user: data.currentUser
});
});
socket.emit("users:refresh", {});
},
render: function () {
if (this.props.user) {
return (
<a href="#">{this.props.user.username} &nbsp;<img src={this.props.user.avatar}
alt="User Avatar"
height="20"
width="20" /></a>
);
} else {
return false;
}
}
});

780
npm-shrinkwrap.json generated
View file

@ -2,6 +2,11 @@
"name": "sws_gathers",
"version": "1.0.0",
"dependencies": {
"async": {
"version": "1.4.0",
"from": "async@",
"resolved": "https://registry.npmjs.org/async/-/async-1.4.0.tgz"
},
"express": {
"version": "4.13.1",
"from": "express@",
@ -405,6 +410,781 @@
"from": "extend@",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz"
},
"gulp": {
"version": "3.9.0",
"from": "gulp@",
"resolved": "https://registry.npmjs.org/gulp/-/gulp-3.9.0.tgz",
"dependencies": {
"archy": {
"version": "1.0.0",
"from": "archy@^1.0.0",
"resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz"
},
"chalk": {
"version": "1.1.0",
"from": "chalk@^1.0.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.0.tgz",
"dependencies": {
"ansi-styles": {
"version": "2.1.0",
"from": "ansi-styles@^2.1.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.1.0.tgz"
},
"escape-string-regexp": {
"version": "1.0.3",
"from": "escape-string-regexp@^1.0.2",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.3.tgz"
},
"has-ansi": {
"version": "2.0.0",
"from": "has-ansi@^2.0.0",
"resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
"dependencies": {
"ansi-regex": {
"version": "2.0.0",
"from": "ansi-regex@^2.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz"
}
}
},
"strip-ansi": {
"version": "3.0.0",
"from": "strip-ansi@^3.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.0.tgz",
"dependencies": {
"ansi-regex": {
"version": "2.0.0",
"from": "ansi-regex@^2.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz"
}
}
},
"supports-color": {
"version": "2.0.0",
"from": "supports-color@^2.0.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz"
}
}
},
"deprecated": {
"version": "0.0.1",
"from": "deprecated@^0.0.1",
"resolved": "https://registry.npmjs.org/deprecated/-/deprecated-0.0.1.tgz"
},
"gulp-util": {
"version": "3.0.6",
"from": "gulp-util@^3.0.0",
"resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.6.tgz",
"dependencies": {
"array-differ": {
"version": "1.0.0",
"from": "array-differ@^1.0.0",
"resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz"
},
"array-uniq": {
"version": "1.0.2",
"from": "array-uniq@^1.0.2",
"resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.2.tgz"
},
"beeper": {
"version": "1.1.0",
"from": "beeper@^1.0.0",
"resolved": "https://registry.npmjs.org/beeper/-/beeper-1.1.0.tgz"
},
"dateformat": {
"version": "1.0.11",
"from": "dateformat@^1.0.11",
"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.11.tgz",
"dependencies": {
"get-stdin": {
"version": "4.0.1",
"from": "get-stdin@*",
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz"
},
"meow": {
"version": "3.3.0",
"from": "meow@*",
"resolved": "https://registry.npmjs.org/meow/-/meow-3.3.0.tgz",
"dependencies": {
"camelcase-keys": {
"version": "1.0.0",
"from": "camelcase-keys@^1.0.0",
"resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-1.0.0.tgz",
"dependencies": {
"camelcase": {
"version": "1.1.0",
"from": "camelcase@^1.0.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.1.0.tgz"
},
"map-obj": {
"version": "1.0.1",
"from": "map-obj@^1.0.0",
"resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz"
}
}
},
"indent-string": {
"version": "1.2.2",
"from": "indent-string@^1.1.0",
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-1.2.2.tgz",
"dependencies": {
"repeating": {
"version": "1.1.3",
"from": "repeating@^1.1.0",
"resolved": "https://registry.npmjs.org/repeating/-/repeating-1.1.3.tgz",
"dependencies": {
"is-finite": {
"version": "1.0.1",
"from": "is-finite@^1.0.0",
"resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.1.tgz",
"dependencies": {
"number-is-nan": {
"version": "1.0.0",
"from": "number-is-nan@^1.0.0",
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz"
}
}
}
}
}
}
}
}
}
}
},
"lodash._reescape": {
"version": "3.0.0",
"from": "lodash._reescape@^3.0.0",
"resolved": "https://registry.npmjs.org/lodash._reescape/-/lodash._reescape-3.0.0.tgz"
},
"lodash._reevaluate": {
"version": "3.0.0",
"from": "lodash._reevaluate@^3.0.0",
"resolved": "https://registry.npmjs.org/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz"
},
"lodash._reinterpolate": {
"version": "3.0.0",
"from": "lodash._reinterpolate@^3.0.0",
"resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz"
},
"lodash.template": {
"version": "3.6.2",
"from": "lodash.template@^3.0.0",
"resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz",
"dependencies": {
"lodash._basecopy": {
"version": "3.0.1",
"from": "lodash._basecopy@^3.0.0",
"resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz"
},
"lodash._basetostring": {
"version": "3.0.1",
"from": "lodash._basetostring@^3.0.0",
"resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz"
},
"lodash._basevalues": {
"version": "3.0.0",
"from": "lodash._basevalues@^3.0.0",
"resolved": "https://registry.npmjs.org/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz"
},
"lodash._isiterateecall": {
"version": "3.0.9",
"from": "lodash._isiterateecall@^3.0.0",
"resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz"
},
"lodash.escape": {
"version": "3.0.0",
"from": "lodash.escape@^3.0.0",
"resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.0.0.tgz"
},
"lodash.keys": {
"version": "3.1.2",
"from": "lodash.keys@^3.0.0",
"resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz",
"dependencies": {
"lodash._getnative": {
"version": "3.9.1",
"from": "lodash._getnative@^3.0.0",
"resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz"
},
"lodash.isarguments": {
"version": "3.0.4",
"from": "lodash.isarguments@^3.0.0",
"resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.0.4.tgz"
},
"lodash.isarray": {
"version": "3.0.4",
"from": "lodash.isarray@^3.0.0",
"resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz"
}
}
},
"lodash.restparam": {
"version": "3.6.1",
"from": "lodash.restparam@^3.0.0",
"resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz"
},
"lodash.templatesettings": {
"version": "3.1.0",
"from": "lodash.templatesettings@^3.0.0",
"resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.0.tgz"
}
}
},
"multipipe": {
"version": "0.1.2",
"from": "multipipe@^0.1.2",
"resolved": "https://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz",
"dependencies": {
"duplexer2": {
"version": "0.0.2",
"from": "duplexer2@0.0.2",
"resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz",
"dependencies": {
"readable-stream": {
"version": "1.1.13",
"from": "readable-stream@~1.1.9",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.13.tgz",
"dependencies": {
"core-util-is": {
"version": "1.0.1",
"from": "core-util-is@~1.0.0",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz"
},
"isarray": {
"version": "0.0.1",
"from": "isarray@0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
},
"string_decoder": {
"version": "0.10.31",
"from": "string_decoder@~0.10.x",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz"
},
"inherits": {
"version": "2.0.1",
"from": "inherits@~2.0.1",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
}
}
}
}
}
}
},
"object-assign": {
"version": "3.0.0",
"from": "object-assign@^3.0.0",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz"
},
"replace-ext": {
"version": "0.0.1",
"from": "replace-ext@0.0.1",
"resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz"
},
"through2": {
"version": "2.0.0",
"from": "through2@^2.0.0",
"resolved": "https://registry.npmjs.org/through2/-/through2-2.0.0.tgz",
"dependencies": {
"readable-stream": {
"version": "2.0.2",
"from": "readable-stream@~2.0.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.2.tgz",
"dependencies": {
"core-util-is": {
"version": "1.0.1",
"from": "core-util-is@~1.0.0",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz"
},
"inherits": {
"version": "2.0.1",
"from": "inherits@~2.0.1",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
},
"isarray": {
"version": "0.0.1",
"from": "isarray@0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
},
"process-nextick-args": {
"version": "1.0.2",
"from": "process-nextick-args@~1.0.0",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.2.tgz"
},
"string_decoder": {
"version": "0.10.31",
"from": "string_decoder@~0.10.x",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz"
},
"util-deprecate": {
"version": "1.0.1",
"from": "util-deprecate@~1.0.1",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.1.tgz"
}
}
},
"xtend": {
"version": "4.0.0",
"from": "xtend@>=4.0.0 <4.1.0-0",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.0.tgz"
}
}
},
"vinyl": {
"version": "0.5.0",
"from": "vinyl@^0.5.0",
"resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.0.tgz",
"dependencies": {
"clone": {
"version": "1.0.2",
"from": "clone@^1.0.0",
"resolved": "https://registry.npmjs.org/clone/-/clone-1.0.2.tgz"
},
"clone-stats": {
"version": "0.0.1",
"from": "clone-stats@^0.0.1",
"resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz"
}
}
}
}
},
"interpret": {
"version": "0.6.5",
"from": "interpret@^0.6.2",
"resolved": "https://registry.npmjs.org/interpret/-/interpret-0.6.5.tgz"
},
"liftoff": {
"version": "2.1.0",
"from": "liftoff@^2.1.0",
"resolved": "https://registry.npmjs.org/liftoff/-/liftoff-2.1.0.tgz",
"dependencies": {
"extend": {
"version": "2.0.1",
"from": "extend@^2.0.1",
"resolved": "https://registry.npmjs.org/extend/-/extend-2.0.1.tgz"
},
"findup-sync": {
"version": "0.2.1",
"from": "findup-sync@^0.2.1",
"resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.2.1.tgz",
"dependencies": {
"glob": {
"version": "4.3.5",
"from": "glob@~4.3.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-4.3.5.tgz",
"dependencies": {
"inflight": {
"version": "1.0.4",
"from": "inflight@^1.0.4",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz",
"dependencies": {
"wrappy": {
"version": "1.0.1",
"from": "wrappy@1",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz"
}
}
},
"inherits": {
"version": "2.0.1",
"from": "inherits@2",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
},
"minimatch": {
"version": "2.0.10",
"from": "minimatch@^2.0.1",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz",
"dependencies": {
"brace-expansion": {
"version": "1.1.0",
"from": "brace-expansion@^1.0.0",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.0.tgz",
"dependencies": {
"balanced-match": {
"version": "0.2.0",
"from": "balanced-match@^0.2.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.2.0.tgz"
},
"concat-map": {
"version": "0.0.1",
"from": "concat-map@0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
}
}
}
}
},
"once": {
"version": "1.3.2",
"from": "once@^1.3.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz",
"dependencies": {
"wrappy": {
"version": "1.0.1",
"from": "wrappy@1",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz"
}
}
}
}
}
}
},
"flagged-respawn": {
"version": "0.3.1",
"from": "flagged-respawn@^0.3.1",
"resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-0.3.1.tgz"
},
"rechoir": {
"version": "0.6.2",
"from": "rechoir@^0.6.0",
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz"
},
"resolve": {
"version": "1.1.6",
"from": "resolve@^1.1.6",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.6.tgz"
}
}
},
"minimist": {
"version": "1.1.2",
"from": "minimist@^1.1.0",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.1.2.tgz"
},
"orchestrator": {
"version": "0.3.7",
"from": "orchestrator@^0.3.0",
"resolved": "https://registry.npmjs.org/orchestrator/-/orchestrator-0.3.7.tgz",
"dependencies": {
"end-of-stream": {
"version": "0.1.5",
"from": "end-of-stream@~0.1.5",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-0.1.5.tgz",
"dependencies": {
"once": {
"version": "1.3.2",
"from": "once@~1.3.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz",
"dependencies": {
"wrappy": {
"version": "1.0.1",
"from": "wrappy@1",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz"
}
}
}
}
},
"sequencify": {
"version": "0.0.7",
"from": "sequencify@~0.0.7",
"resolved": "https://registry.npmjs.org/sequencify/-/sequencify-0.0.7.tgz"
},
"stream-consume": {
"version": "0.1.0",
"from": "stream-consume@~0.1.0",
"resolved": "https://registry.npmjs.org/stream-consume/-/stream-consume-0.1.0.tgz"
}
}
},
"pretty-hrtime": {
"version": "1.0.0",
"from": "pretty-hrtime@^1.0.0",
"resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.0.tgz"
},
"semver": {
"version": "4.3.6",
"from": "semver@^4.1.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz"
},
"tildify": {
"version": "1.1.0",
"from": "tildify@^1.0.0",
"resolved": "https://registry.npmjs.org/tildify/-/tildify-1.1.0.tgz",
"dependencies": {
"os-homedir": {
"version": "1.0.1",
"from": "os-homedir@^1.0.0",
"resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.1.tgz"
}
}
},
"v8flags": {
"version": "2.0.9",
"from": "v8flags@^2.0.2",
"resolved": "https://registry.npmjs.org/v8flags/-/v8flags-2.0.9.tgz",
"dependencies": {
"user-home": {
"version": "1.1.1",
"from": "user-home@^1.1.1",
"resolved": "https://registry.npmjs.org/user-home/-/user-home-1.1.1.tgz"
}
}
},
"vinyl-fs": {
"version": "0.3.13",
"from": "vinyl-fs@^0.3.0",
"resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-0.3.13.tgz",
"dependencies": {
"defaults": {
"version": "1.0.2",
"from": "defaults@^1.0.0",
"resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.2.tgz",
"dependencies": {
"clone": {
"version": "0.1.19",
"from": "clone@~0.1.5",
"resolved": "https://registry.npmjs.org/clone/-/clone-0.1.19.tgz"
}
}
},
"glob-stream": {
"version": "3.1.18",
"from": "glob-stream@^3.1.5",
"resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-3.1.18.tgz",
"dependencies": {
"glob": {
"version": "4.5.3",
"from": "glob@^4.3.1",
"resolved": "https://registry.npmjs.org/glob/-/glob-4.5.3.tgz",
"dependencies": {
"inflight": {
"version": "1.0.4",
"from": "inflight@^1.0.4",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz",
"dependencies": {
"wrappy": {
"version": "1.0.1",
"from": "wrappy@1",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz"
}
}
},
"inherits": {
"version": "2.0.1",
"from": "inherits@2",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
},
"once": {
"version": "1.3.2",
"from": "once@^1.3.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz",
"dependencies": {
"wrappy": {
"version": "1.0.1",
"from": "wrappy@1",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz"
}
}
}
}
},
"minimatch": {
"version": "2.0.10",
"from": "minimatch@^2.0.1",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz",
"dependencies": {
"brace-expansion": {
"version": "1.1.0",
"from": "brace-expansion@^1.0.0",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.0.tgz",
"dependencies": {
"balanced-match": {
"version": "0.2.0",
"from": "balanced-match@^0.2.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.2.0.tgz"
},
"concat-map": {
"version": "0.0.1",
"from": "concat-map@0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
}
}
}
}
},
"ordered-read-streams": {
"version": "0.1.0",
"from": "ordered-read-streams@^0.1.0",
"resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.1.0.tgz"
},
"glob2base": {
"version": "0.0.12",
"from": "glob2base@^0.0.12",
"resolved": "https://registry.npmjs.org/glob2base/-/glob2base-0.0.12.tgz",
"dependencies": {
"find-index": {
"version": "0.1.1",
"from": "find-index@^0.1.1",
"resolved": "https://registry.npmjs.org/find-index/-/find-index-0.1.1.tgz"
}
}
},
"unique-stream": {
"version": "1.0.0",
"from": "unique-stream@^1.0.0",
"resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-1.0.0.tgz"
}
}
},
"glob-watcher": {
"version": "0.0.6",
"from": "glob-watcher@^0.0.6",
"resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-0.0.6.tgz",
"dependencies": {
"gaze": {
"version": "0.5.1",
"from": "gaze@^0.5.1",
"resolved": "https://registry.npmjs.org/gaze/-/gaze-0.5.1.tgz",
"dependencies": {
"globule": {
"version": "0.1.0",
"from": "globule@~0.1.0",
"resolved": "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz",
"dependencies": {
"lodash": {
"version": "1.0.2",
"from": "lodash@~1.0.1",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz"
},
"glob": {
"version": "3.1.21",
"from": "glob@~3.1.21",
"resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz",
"dependencies": {
"graceful-fs": {
"version": "1.2.3",
"from": "graceful-fs@~1.2.0",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz"
},
"inherits": {
"version": "1.0.0",
"from": "inherits@1",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.0.tgz"
}
}
},
"minimatch": {
"version": "0.2.14",
"from": "minimatch@~0.2.11",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz",
"dependencies": {
"lru-cache": {
"version": "2.6.5",
"from": "lru-cache@2",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.5.tgz"
},
"sigmund": {
"version": "1.0.1",
"from": "sigmund@~1.0.0",
"resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz"
}
}
}
}
}
}
}
}
},
"graceful-fs": {
"version": "3.0.8",
"from": "graceful-fs@^3.0.0",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.8.tgz"
},
"mkdirp": {
"version": "0.5.1",
"from": "mkdirp@^0.5.0",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"dependencies": {
"minimist": {
"version": "0.0.8",
"from": "minimist@0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz"
}
}
},
"strip-bom": {
"version": "1.0.0",
"from": "strip-bom@^1.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-1.0.0.tgz",
"dependencies": {
"first-chunk-stream": {
"version": "1.0.0",
"from": "first-chunk-stream@^1.0.0",
"resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz"
},
"is-utf8": {
"version": "0.2.0",
"from": "is-utf8@^0.2.0",
"resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.0.tgz"
}
}
},
"through2": {
"version": "0.6.5",
"from": "through2@^0.6.1",
"resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz",
"dependencies": {
"readable-stream": {
"version": "1.0.33",
"from": "readable-stream@>=1.0.33-1 <1.1.0-0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz",
"dependencies": {
"core-util-is": {
"version": "1.0.1",
"from": "core-util-is@~1.0.0",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz"
},
"isarray": {
"version": "0.0.1",
"from": "isarray@0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
},
"string_decoder": {
"version": "0.10.31",
"from": "string_decoder@~0.10.x",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz"
},
"inherits": {
"version": "2.0.1",
"from": "inherits@~2.0.1",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
}
}
},
"xtend": {
"version": "4.0.0",
"from": "xtend@>=4.0.0 <4.1.0-0",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.0.tgz"
}
}
},
"vinyl": {
"version": "0.4.6",
"from": "vinyl@^0.4.0",
"resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz",
"dependencies": {
"clone": {
"version": "0.2.0",
"from": "clone@^0.2.0",
"resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz"
},
"clone-stats": {
"version": "0.0.1",
"from": "clone-stats@^0.0.1",
"resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz"
}
}
}
}
}
}
},
"javascript-state-machine": {
"version": "2.3.5",
"from": "javascript-state-machine@",

View file

@ -10,8 +10,8 @@
"scripts": {
"test": "NODE_ENV=test mocha spec/",
"start": "npm run compile && node index.js",
"compile:dev": "node_modules/react-tools/bin/jsx --watch --no-cache-dir --source-map-inline -x jsx lib/react/ public/js/",
"compile": "node_modules/react-tools/bin/jsx -x jsx lib/react/ public/js/",
"watch": "node_modules/gulp/bin/gulp.js watch",
"compile": "node_modules/gulp/bin/gulp.js",
"dev": "nodemon index.js"
},
"repository": {
@ -29,16 +29,22 @@
"express": "~4.13.1",
"express-handlebars": "~2.0.1",
"extend": "^3.0.0",
"gulp": "^3.9.0",
"gulp-concat": "^2.6.0",
"gulp-react": "^3.0.1",
"javascript-state-machine": "^2.3.5",
"morgan": "~1.6.1",
"node-mysql": "~0.4.2",
"react-tools": "~0.13.3",
"request": "~2.60.0",
"socket.io": "~1.3.5",
"gulp-watch": "^4.3.4",
"winston": "~1.0.1"
},
"devDependencies": {
"chai": "~3.1.0",
"gulp": "^3.9.0",
"gulp-plumber": "^1.0.1",
"mocha": "~2.2.5",
"nodemon": "~1.3.7",
"supertest": "~1.0.1"

View file

@ -19,4 +19,14 @@
.signin {
margin: 0px 10px;
}
.user-online {
border-radius: 50%;
width: 10px;
height: 10px;
background-image: -moz-radial-gradient(45px 45px 45deg, circle cover, yellow 0%, orange 100%, red 95%);
background-image: -webkit-radial-gradient(45px 45px, circle cover, yellow, orange);
background-image: radial-gradient(45px 45px 45deg, circle cover, yellow 0%, orange 100%, red 95%);
}

File diff suppressed because one or more lines are too long

4
public/js/client.js Normal file
View file

@ -0,0 +1,4 @@
$(function () {
initialiseComponents();
});

282
public/js/gather.js Normal file

File diff suppressed because one or more lines are too long

23
public/js/init.js Normal file
View file

@ -0,0 +1,23 @@
"use strict";
var socket;
function initialiseComponents () {
var socketUrl = window.location.protocol + "//" + window.location.host;
socket = io(socketUrl)
.on("connect", function () {
console.log("Connected");
})
.on("reconnect", function () {
console.log("Reconnected");
})
.on("disconnect", function () {
console.log("Disconnected")
});
React.render(React.createElement(UserMenu, null), document.getElementById('side-menu'));
React.render(React.createElement(Chatroom, null), document.getElementById('chatroom'));
React.render(React.createElement(Gather, null), document.getElementById('gathers'));
React.render(React.createElement(CurrentUser, null), document.getElementById('currentuser'));
};
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHJhbnNmb3JtZWQuanMiLCJzb3VyY2VzIjpbbnVsbF0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLFlBQVksQ0FBQzs7QUFFYixJQUFJLE1BQU0sQ0FBQzs7QUFFWCxTQUFTLG9CQUFvQixJQUFJO0NBQ2hDLElBQUksU0FBUyxHQUFHLE1BQU0sQ0FBQyxRQUFRLENBQUMsUUFBUSxHQUFHLElBQUksR0FBRyxNQUFNLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQztDQUN2RSxNQUFNLEdBQUcsRUFBRSxDQUFDLFNBQVMsQ0FBQztHQUNwQixFQUFFLENBQUMsU0FBUyxFQUFFLFlBQVk7R0FDMUIsT0FBTyxDQUFDLEdBQUcsQ0FBQyxXQUFXLENBQUMsQ0FBQztHQUN6QixDQUFDO0dBQ0QsRUFBRSxDQUFDLFdBQVcsRUFBRSxZQUFZO0dBQzVCLE9BQU8sQ0FBQyxHQUFHLENBQUMsYUFBYSxDQUFDLENBQUM7R0FDM0IsQ0FBQztHQUNELEVBQUUsQ0FBQyxZQUFZLEVBQUUsWUFBWTtHQUM3QixPQUFPLENBQUMsR0FBRyxDQUFDLGNBQWMsQ0FBQztBQUM5QixHQUFHLENBQUMsQ0FBQzs7Q0FFSixLQUFLLENBQUMsTUFBTSxDQUFDLG9CQUFDLFFBQVEsRUFBQSxJQUFBLENBQUcsQ0FBQSxFQUFFLFFBQVEsQ0FBQyxjQUFjLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQztDQUNqRSxLQUFLLENBQUMsTUFBTSxDQUFDLG9CQUFDLFFBQVEsRUFBQSxJQUFBLENBQUcsQ0FBQSxFQUFFLFFBQVEsQ0FBQyxjQUFjLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQztDQUNoRSxLQUFLLENBQUMsTUFBTSxDQUFDLG9CQUFDLE1BQU0sRUFBQSxJQUFBLENBQUcsQ0FBQSxFQUFFLFFBQVEsQ0FBQyxjQUFjLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQztDQUM3RCxLQUFLLENBQUMsTUFBTSxDQUFDLG9CQUFDLFdBQVcsRUFBQSxJQUFBLENBQUcsQ0FBQSxFQUFFLFFBQVEsQ0FBQyxjQUFjLENBQUMsYUFBYSxDQUFDLENBQUMsQ0FBQztDQUN0RSIsInNvdXJjZXNDb250ZW50IjpbIlwidXNlIHN0cmljdFwiO1xuXG52YXIgc29ja2V0O1xuXG5mdW5jdGlvbiBpbml0aWFsaXNlQ29tcG9uZW50cyAoKSB7XG5cdHZhciBzb2NrZXRVcmwgPSB3aW5kb3cubG9jYXRpb24ucHJvdG9jb2wgKyBcIi8vXCIgKyB3aW5kb3cubG9jYXRpb24uaG9zdDtcblx0c29ja2V0ID0gaW8oc29ja2V0VXJsKVxuXHRcdC5vbihcImNvbm5lY3RcIiwgZnVuY3Rpb24gKCkge1xuXHRcdFx0Y29uc29sZS5sb2coXCJDb25uZWN0ZWRcIik7XG5cdFx0fSlcblx0XHQub24oXCJyZWNvbm5lY3RcIiwgZnVuY3Rpb24gKCkge1xuXHRcdFx0Y29uc29sZS5sb2coXCJSZWNvbm5lY3RlZFwiKTtcblx0XHR9KVxuXHRcdC5vbihcImRpc2Nvbm5lY3RcIiwgZnVuY3Rpb24gKCkge1xuXHRcdFx0Y29uc29sZS5sb2coXCJEaXNjb25uZWN0ZWRcIilcblx0XHR9KTtcblxuXHRSZWFjdC5yZW5kZXIoPFVzZXJNZW51IC8+LCBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnc2lkZS1tZW51JykpO1xuXHRSZWFjdC5yZW5kZXIoPENoYXRyb29tIC8+LCBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnY2hhdHJvb20nKSk7XG5cdFJlYWN0LnJlbmRlcig8R2F0aGVyIC8+LCBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnZ2F0aGVycycpKTtcblx0UmVhY3QucmVuZGVyKDxDdXJyZW50VXNlciAvPiwgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2N1cnJlbnR1c2VyJykpO1xufTsiXX0=

148
public/js/message.js Normal file

File diff suppressed because one or more lines are too long

114
public/js/user.js Normal file

File diff suppressed because one or more lines are too long

View file

@ -147,6 +147,7 @@ describe("Gather Model:", function () {
});
});
});
describe("addUser", function () {
it ("adds gatherer to lobby", function () {
gather.addUser(user);
@ -159,6 +160,7 @@ describe("Gather Model:", function () {
assert.equal(gather.gatherers.length, 1);
});
});
describe("removeUser", function () {
it ("removes gatherer altogether", function () {
gather.addUser(user);
@ -168,6 +170,7 @@ describe("Gather Model:", function () {
assert.equal(gather.gatherers.length, 0);
});
});
describe("moveToMarine", function () {
it ("moves a player to marine", function () {
gather.addUser(user);
@ -183,6 +186,7 @@ describe("Gather Model:", function () {
});
});
});
describe("moveToAlien", function () {
it ("moves a player to alien", function () {
gather.addUser(user);
@ -198,6 +202,7 @@ describe("Gather Model:", function () {
});
});
});
describe("moveToLobby", function () {
it ("moves a player to lobby", function () {
gather.addUser(user);
@ -208,6 +213,7 @@ describe("Gather Model:", function () {
assert.equal(gather.lobby()[0].id, user.id);
});
});
describe("aliens", function () {
it ("returns all gatherers in aliens", function () {
gather.addUser(user);
@ -215,6 +221,7 @@ describe("Gather Model:", function () {
assert.equal(gather.aliens().length, 1);
});
});
describe("marines", function () {
it ("returns all gatherers in marines", function () {
gather.addUser(user);
@ -222,12 +229,14 @@ describe("Gather Model:", function () {
assert.equal(gather.marines().length, 1);
});
});
describe("lobby", function () {
it ("returns all gatherers in lobby", function () {
gather.addUser(user);
assert.equal(gather.lobby().length, 1);
});
});
describe("toJson", function () {
it ("returns a json representation of the gather instance", function () {
var output = gather.toJson();
@ -235,6 +244,7 @@ describe("Gather Model:", function () {
assert.isString(output.state);
});
});
describe("leaderVotes", function () {
beforeEach(function () {
gatherers.forEach(function (user) {
@ -261,6 +271,7 @@ describe("Gather Model:", function () {
assert.equal(gather.leaderVotes().length, 0);
});
});
describe("voteForLeader", function () {
beforeEach(function () {
gatherers.forEach(function (user) {
@ -289,6 +300,7 @@ describe("Gather Model:", function () {
assert.equal(votes[0], secondCandidate.id);
});
});
describe("alienLeader", function () {
beforeEach(function () {
gatherers.forEach(function (gatherer) {
@ -304,6 +316,7 @@ describe("Gather Model:", function () {
assert.isUndefined(gather.alienLeader());
});
});
describe("marineLeader", function () {
beforeEach(function () {
gatherers.forEach(function (gatherer) {
@ -319,6 +332,7 @@ describe("Gather Model:", function () {
assert.isUndefined(gather.marineLeader());
});
});
describe("assignMarineLeader", function () {
it ("assigns a marine leader", function () {
gather.addUser(user);
@ -327,6 +341,7 @@ describe("Gather Model:", function () {
assert.equal(leader.id, user.id);
});
});
describe("assignAlienLeader", function () {
it ("assigns an alien leader", function () {
gather.addUser(user);
@ -335,6 +350,21 @@ describe("Gather Model:", function () {
assert.equal(leader.id, user.id);
});
});
describe("getGatherer", function () {
beforeEach(function () {
gather.addGatherer(user);
});
it ("returns a gatherer given a user", function () {
var gatherer = gather.getGatherer(user);
assert.equal(gatherer.id, user.id);
});
it ("returns null if user is not a gatherer", function () {
var gatherer = gather.getGatherer(gatherers[0]);
assert.isNull(gatherer);
});
});
describe("confirmTeam", function () {
var leader;
beforeEach(function () {

View file

@ -1 +1,2 @@
<script src="/js/app.js"></script>
<script src="/js/app.js"></script>
<script src="/js/client.js"></script>