ensl_gathers/public/js/app.js

507 lines
52 KiB
JavaScript
Raw Normal View History

2015-07-22 00:00:05 +00:00
$(function () {
"use strict";
2015-07-22 15:49:20 +00:00
var UserCounter = React.createClass({displayName: "UserCounter",
2015-07-21 14:10:24 +00:00
render: function () {
return (
React.createElement("li", null,
React.createElement("a", {href: "#"},
2015-07-22 15:49:20 +00:00
React.createElement("i", {className: "fa fa-users fa-fw"}), " Online",
2015-07-21 14:10:24 +00:00
React.createElement("span", {className: "badge add-left"}, " ", this.props.count, " ")
)
)
);
}
2015-07-21 00:24:14 +00:00
});
2015-07-22 00:00:05 +00:00
var UserLogin = React.createClass({displayName: "UserLogin",
2015-07-22 15:35:40 +00:00
authorizeId: function (id) {
id = parseInt(id, 10);
socket.emit("users:authorize", {
2015-07-22 15:35:40 +00:00
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;
2015-07-22 00:00:05 +00:00
},
render: function () {
return (
React.createElement("form", {onSubmit: this.handleSubmit},
2015-07-22 15:57:15 +00:00
React.createElement("div", {className: "input-group signin"},
2015-07-22 00:00:05 +00:00
React.createElement("input", {
id: "btn-input",
type: "text",
className: "form-control",
2015-07-22 15:35:40 +00:00
ref: "authorize_id",
2015-07-22 00:00:05 +00:00
placeholder: "Choose an ID..."}),
React.createElement("span", {className: "input-group-btn"},
React.createElement("input", {
type: "submit",
className: "btn btn-primary",
id: "btn-chat",
value: "Login"})
)
2015-07-22 15:57:15 +00:00
),
React.createElement("div", {className: "signin"},
React.createElement("p", {className: "text-center"}, React.createElement("small", null, "Just a temporary measure until genuine authentication is implemented"))
2015-07-22 00:00:05 +00:00
)
)
);
}
})
2015-07-22 15:49:20 +00:00
var UserMenu = React.createClass({displayName: "UserMenu",
2015-07-24 15:01:56 +00:00
getDefaultProps: function () {
return {
count: 0,
users: []
};
},
2015-07-21 14:10:24 +00:00
componentDidMount: function () {
2015-07-22 15:49:20 +00:00
socket.on('userCount', this.updateUsers);
2015-07-21 14:10:24 +00:00
},
2015-07-22 15:49:20 +00:00
updateUsers: function (data) {
2015-07-21 14:10:24 +00:00
this.setProps({
count: data.count,
2015-07-22 15:49:20 +00:00
users: data.users
2015-07-21 14:10:24 +00:00
});
},
render: function () {
2015-07-22 15:49:20 +00:00
var users = this.props.users.map(function (user) {
2015-07-21 14:10:24 +00:00
return (
2015-07-26 11:54:35 +00:00
React.createElement("li", {key: user.id}, React.createElement("a", {href: "#"}, user.username))
2015-07-21 14:10:24 +00:00
);
});
return (
React.createElement("ul", {className: "nav", id: "side-menu"},
2015-07-22 15:49:20 +00:00
React.createElement(UserCounter, React.__spread({}, this.props)),
2015-07-22 15:57:15 +00:00
users,
React.createElement("li", null, React.createElement("br", null), React.createElement(UserLogin, null), React.createElement("br", null))
2015-07-21 14:10:24 +00:00
)
);
}
});
2015-07-21 14:10:24 +00:00
var Chatroom = React.createClass({displayName: "Chatroom",
2015-07-24 15:01:56 +00:00
getDefaultProps: function () {
return {
history: []
};
},
2015-07-21 14:10:24 +00:00
componentDidMount: function () {
var self = this;
var TIMER_INTERVAL = 60000; // Every minute
2015-07-21 00:24:14 +00:00
2015-07-21 14:10:24 +00:00
socket.on("message:new", function (data) {
var history = self.props.history;
history.push(data);
self.setProps({
history: history
});
self.scrollToBottom();
});
2015-07-21 00:24:14 +00:00
2015-07-21 14:10:24 +00:00
// Message History Retrieved
socket.on("message:refresh", function (data) {
self.setProps({
history: data.chatHistory
});
self.scrollToBottom();
});
socket.emit("message:refresh", {});
self.timer = setInterval(function () {
2015-07-26 11:54:35 +00:00
if (self.refs.messages) self.refs.messages.refreshTime();
2015-07-21 14:10:24 +00:00
}, 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 (
React.createElement(ChatMessage, {
avatar: message.author.avatar,
username: message.author.username,
content: message.content,
ref: "messages",
createdAt: message.createdAt})
);
});
return (
React.createElement("div", {className: "panel panel-default"},
React.createElement("div", {className: "panel-heading"}, "Gather Chat"),
React.createElement("div", {className: "panel-body"},
React.createElement("ul", {className: "chat", id: "chatmessages", ref: "messageContainer"},
messages
)
),
React.createElement("div", {className: "panel-footer"},
React.createElement(MessageBar, null)
)
)
);
}
});
var ChatMessage = React.createClass({displayName: "ChatMessage",
getInitialState: function () {
return {
timeAgo: $.timeago(this.props.createdAt)
}
},
refreshTime: function () {
var self = this;
self.setState({
timeAgo: $.timeago(self.props.createdAt)
});
},
render: function () {
return (
React.createElement("li", {className: "left clearfix"},
React.createElement("span", {className: "chat-img pull-left"},
React.createElement("img", {
2015-07-22 23:30:14 +00:00
src: this.props.avatar,
2015-07-21 14:10:24 +00:00
alt: "User Avatar",
height: "40",
2015-07-22 15:20:28 +00:00
width: "40",
2015-07-21 14:10:24 +00:00
className: "img-circle"})
),
React.createElement("div", {className: "chat-body clearfix"},
React.createElement("div", {className: "header"},
React.createElement("strong", {className: "primary-font"}, this.props.username),
React.createElement("small", {className: "pull-right text-muted"},
React.createElement("i", {className: "fa fa-clock-o fa-fw"}), " ", this.state.timeAgo
)
),
React.createElement("p", null, this.props.content)
)
)
);
}
});
2015-07-27 00:09:02 +00:00
var CurrentUser = React.createClass({displayName: "CurrentUser",
getDefaultProps: function () {
return {
username: "",
avatar: ""
}
},
componentDidMount: function () {
},
render: function () {
return (
React.createElement("a", {href: "#"}, this.props.user.username, "  ", React.createElement("img", {src: this.props.user.avatar,
alt: "User Avatar",
height: "20",
width: "20"}))
);
}
});
2015-07-21 14:10:24 +00:00
var MessageBar = React.createClass({displayName: "MessageBar",
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 (
React.createElement("form", {onSubmit: this.handleSubmit},
React.createElement("div", {className: "input-group"},
React.createElement("input", {
id: "btn-input",
type: "text",
className: "form-control",
ref: "content",
placeholder: "Be polite please..."}),
React.createElement("span", {className: "input-group-btn"},
React.createElement("input", {
type: "submit",
className: "btn btn-primary",
id: "btn-chat",
value: "Send"})
)
)
)
);
}
});
2015-07-26 11:54:35 +00:00
var JoinGatherButton = React.createClass({displayName: "JoinGatherButton",
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 (React.createElement("button", {
onClick: this.joinGather,
className: buttonClass}, message))
}
});
var GatherProgress = React.createClass({displayName: "GatherProgress",
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 (
React.createElement("div", {className: "panel-body"},
React.createElement("p", null, "Gather Progress"),
React.createElement("div", {className: "progress"},
React.createElement("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
)
)
)
);
} else {
return false;
}
}
});
2015-07-22 22:34:57 +00:00
var Gather = React.createClass({displayName: "Gather",
2015-07-24 15:01:56 +00:00
getDefaultProps: function () {
return {
gather: {
gatherers: []
}
}
},
2015-07-24 17:05:12 +00:00
joinedGather: function () {
var self = this;
return this.props.gather.gatherers.some(function (gatherer) {
return gatherer.user.id === self.props.user.id;
});
},
2015-07-24 15:01:56 +00:00
componentDidMount: function () {
var self = this;
socket.on("gather:refresh", function (data) {
self.setProps({
2015-07-24 17:05:12 +00:00
gather: data.gather,
user: data.user
2015-07-24 15:01:56 +00:00
});
});
},
2015-07-26 11:54:35 +00:00
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";
}
2015-07-22 22:34:57 +00:00
},
2015-07-24 17:05:12 +00:00
leaveGather: function (e) {
e.preventDefault();
socket.emit("gather:leave", {});
},
2015-07-26 11:54:35 +00:00
inviteToGather: function (e) {
e.preventDefault();
},
2015-07-22 22:34:57 +00:00
render: function () {
2015-07-24 17:05:12 +00:00
var joinButton;
if (this.joinedGather()) {
2015-07-26 11:54:35 +00:00
joinButton = (React.createElement("li", null, React.createElement("button", {
2015-07-24 17:05:12 +00:00
onClick: this.leaveGather,
2015-07-26 11:54:35 +00:00
className: "btn btn-danger"}, "Leave Gather")));
2015-07-24 17:05:12 +00:00
} else {
2015-07-26 11:54:35 +00:00
joinButton = (React.createElement("li", null, React.createElement(JoinGatherButton, null)));
}
var inviteButton;
if (this.props.gather.state === 'gathering') {
inviteButton = (React.createElement("li", null, React.createElement("button", {
onClick: this.inviteToGather,
className: "btn btn-primary"}, "Invite to Gather")));
2015-07-24 17:05:12 +00:00
}
2015-07-22 22:34:57 +00:00
return (
React.createElement("div", {className: "panel panel-default"},
React.createElement("div", {className: "panel-heading"},
2015-07-26 11:54:35 +00:00
React.createElement("strong", null, "NS2 Gather "),
React.createElement("span", {className: "badge add-left"}, this.props.gather.gatherers.length),
React.createElement("br", null),
this.stateDescription()
2015-07-24 15:01:56 +00:00
),
2015-07-24 17:05:12 +00:00
React.createElement(Gatherers, {gatherers: this.props.gather.gatherers}),
2015-07-26 11:54:35 +00:00
React.createElement(GatherProgress, {gather: this.props.gather}),
2015-07-24 17:05:12 +00:00
React.createElement("div", {className: "panel-footer text-right"},
2015-07-26 11:54:35 +00:00
React.createElement("ul", {className: "list-inline"},
inviteButton,
joinButton
)
2015-07-22 22:34:57 +00:00
)
)
);
}
});
2015-07-26 11:54:35 +00:00
var LeaderPoll = React.createClass({displayName: "LeaderPoll",
render: function () {
return (
React.createElement("div", {className: "panel-body"}
)
);
}
});
2015-07-24 17:05:12 +00:00
var Gatherers = React.createClass({displayName: "Gatherers",
2015-07-24 15:01:56 +00:00
render: function () {
2015-07-24 17:05:12 +00:00
var gatherers = this.props.gatherers.map(function (gatherer) {
2015-07-25 12:00:56 +00:00
var lifeforms = (
gatherer.user.ability.lifeforms.map(function (lifeform) {
2015-07-26 11:54:35 +00:00
return (React.createElement("span", {className: "label label-default"}, lifeform));
2015-07-25 12:00:56 +00:00
})
);
2015-07-26 11:54:35 +00:00
var division = (React.createElement("span", {className: "label label-primary"}, gatherer.user.ability.division));
var commBadge;
if (gatherer.user.ability.commander) {
commBadge = (React.createElement("img", {src: "/images/commander.png",
alt: "Commander",
height: "20",
2015-07-27 00:09:02 +00:00
width: "20"}));
2015-07-26 11:54:35 +00:00
}
2015-07-25 12:00:56 +00:00
2015-07-24 17:05:12 +00:00
return (
2015-07-26 11:54:35 +00:00
React.createElement("tr", {key: gatherer.user.id},
React.createElement("td", {className: "col-md-1"}, commBadge),
React.createElement("td", {className: "col-md-5"}, gatherer.user.username),
React.createElement("td", {className: "col-md-3"}, division, " "),
React.createElement("td", {className: "col-md-3"}, lifeforms, " ")
2015-07-24 17:05:12 +00:00
)
);
})
2015-07-26 11:54:35 +00:00
if (this.props.gatherers.length) {
return (
React.createElement("div", {className: "panel-body"},
React.createElement("div", {className: "panel panel-default"},
React.createElement("div", {className: "panel-heading"},
React.createElement("h5", {className: "panel-title"}, "Roster")
),
React.createElement("table", {className: "table roster-table"},
React.createElement("tbody", null,
gatherers
)
)
2015-07-24 17:05:12 +00:00
)
)
2015-07-26 11:54:35 +00:00
);
} else {
return (React.createElement("div", {className: "panel-body text-center"}, React.createElement(JoinGatherButton, {buttonClass: "btn-lg", buttonName: "Start a Gather"})));
}
2015-07-24 15:01:56 +00:00
}
});
2015-07-22 00:00:05 +00:00
var socket;
function initialiseComponents () {
2015-07-22 14:13:08 +00:00
var socketUrl = window.location.protocol + "//" + window.location.host;
socket = io(socketUrl)
2015-07-22 00:00:05 +00:00
.on("connect", function () {
console.log("Connected");
})
.on("reconnect", function () {
console.log("Reconnected");
})
.on("disconnect", function () {
console.log("Disconnected")
});
2015-07-24 15:01:56 +00:00
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'));
2015-07-27 00:09:02 +00:00
React.render(React.createElement(CurrentUser, null), document.getElementById('currentuser'));
2015-07-22 00:00:05 +00:00
};
initialiseComponents();
});
2015-07-22 14:13:08 +00:00
2015-07-27 00:09:02 +00:00
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHJhbnNmb3JtZWQuanMiLCJzb3VyY2VzIjpbbnVsbF0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLENBQUMsQ0FBQyxZQUFZOztBQUVkLFlBQVksQ0FBQzs7QUFFYixJQUFJLGlDQUFpQywyQkFBQTtDQUNwQyxNQUFNLEVBQUUsWUFBWTtFQUNuQjtHQUNDLG9CQUFBLElBQUcsRUFBQSxJQUFDLEVBQUE7SUFDSCxvQkFBQSxHQUFFLEVBQUEsQ0FBQSxDQUFDLElBQUEsRUFBSSxDQUFDLEdBQUksQ0FBQSxFQUFBO0tBQ1gsb0JBQUEsR0FBRSxFQUFBLENBQUEsQ0FBQyxTQUFBLEVBQVMsQ0FBQyxtQkFBb0IsQ0FBSSxDQUFBLEVBQUEsU0FBQSxFQUFBLENBQUE7QUFBQSxLQUNyQyxvQkFBQSxNQUFLLEVBQUEsQ0FBQSxDQUFDLFNBQUEsRUFBUyxDQUFDLGdCQUFpQixDQUFBLEVBQUEsR0FBQSxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUMsS0FBSyxFQUFDLEdBQVEsQ0FBQTtJQUN6RCxDQUFBO0dBQ0EsQ0FBQTtJQUNKO0VBQ0Y7QUFDRixDQUFDLENBQUMsQ0FBQzs7QUFFSCxJQUFJLCtCQUErQix5QkFBQTtDQUNsQyxXQUFXLEVBQUUsVUFBVSxFQUFFLEVBQUU7RUFDMUIsRUFBRSxHQUFHLFFBQVEsQ0FBQyxFQUFFLEVBQUUsRUFBRSxDQUFDLENBQUM7RUFDdEIsTUFBTSxDQUFDLElBQUksQ0FBQyxpQkFBaUIsRUFBRTtHQUM5QixFQUFFLEVBQUUsRUFBRTtHQUNOLENBQUMsQ0FBQztFQUNIO0NBQ0QsWUFBWSxFQUFFLFVBQVUsQ0FBQyxFQUFFO0VBQzFCLENBQUMsQ0FBQyxjQUFjLEVBQUUsQ0FBQztFQUNuQixJQUFJLEVBQUUsR0FBRyxLQUFLLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsWUFBWSxDQUFDLENBQUMsS0FBSyxDQUFDLElBQUksRUFBRSxDQUFDO0VBQ2hFLElBQUksQ0FBQyxFQUFFLEVBQUUsT0FBTztFQUNoQixLQUFLLENB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