summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFreeyorp <TheFreeYorp@NOSPAM.G.m.a.i.l.replace>2013-05-14 21:08:11 +1200
committerFreeyorp <TheFreeYorp@NOSPAM.G.m.a.i.l.replace>2013-05-14 21:09:06 +1200
commit4e83d0b4c7e2b303689ac1b8df6373a987ab1944 (patch)
tree46974728f66f38112922d9d3105aa6faad481fb3
parent8edde7d254089d9ae7b4745d43117b02c9d4b09d (diff)
downloadmanavis-4e83d0b4c7e2b303689ac1b8df6373a987ab1944.tar.gz
manavis-4e83d0b4c7e2b303689ac1b8df6373a987ab1944.tar.bz2
manavis-4e83d0b4c7e2b303689ac1b8df6373a987ab1944.tar.xz
manavis-4e83d0b4c7e2b303689ac1b8df6373a987ab1944.zip
Add channels
Active filters will only be applied from users in the same channel. This closes #14.
-rw-r--r--index.js74
-rw-r--r--public/css/style.css13
-rw-r--r--public/index.html2
-rw-r--r--public/js/mv/connect.js193
-rw-r--r--public/js/mv/main.js2
-rw-r--r--public/js/mv/parse.js3
6 files changed, 243 insertions, 44 deletions
diff --git a/index.js b/index.js
index 5a2c7c2..8116dfb 100644
--- a/index.js
+++ b/index.js
@@ -33,12 +33,16 @@ logger.format = function(level, date, message) {
/* Number of sessions we've seen. */
var count = 0;
-/* nid -> { nick, filters, following } */
+/* nid -> { nick, filters, channel } */
var users = {};
/* nid -> socket */
var sockets = {};
/* FIXME: Workaround to prevent logout propagating during ghosting */
var ghosting = false;
+/* Auto-incrementing room count */
+var channelCount = 0;
+/* Channel ID -> { usernum, filters } */
+var channels = {};
sessionSockets.on('connection', function (err, socket, session) {
/*
@@ -86,13 +90,73 @@ sessionSockets.on('connection', function (err, socket, session) {
nick: d.nick
});
});
+ socket.on('join', function(d) {
+ var channel;
+ if (d != null) {
+ if (!(typeof(d) == "number" && d <= channelCount)) {
+ return;
+ }
+ /* Join an existing channel */
+ channel = d;
+ } else {
+ /* Automagically create a new channel */
+ channel = ++channelCount;
+ }
+ logAction("JOIN", users[session.nid].channel);
+ if ("channel" in users[session.nid]) {
+ /* Leave any channel we're in */
+ socket.leave(users[session.nid].channel);
+ }
+ /* Inform socket.io about the channel join */
+ socket.join(channel);
+ /* Let everyone know about the channel join */
+ users[session.nid].channel = channel;
+ io.sockets.emit('join', {
+ id: session.nid,
+ channel: channel
+ });
+ /* Update the channel information */
+ if (channel in channels) {
+ /* This channel already exists. Inform the joining user of the current filters. */
+ socket.emit('filterset', {
+ id: 0, /* Server */
+ filters: channels[channel].filters
+ });
+ ++channels[channel].usernum;
+ } else {
+ /* This channel didn't already exist, so create it and set the filters. */
+ channels[channel] = {
+ usernum: 1,
+ filters: users[session.nid].filters
+ };
+ }
+ });
+ socket.on('part', function() {
+ if (!users[session.nid].channel) {
+ return;
+ }
+ logAction("PART");
+ socket.leave(users[session.nid].channel);
+ if (!--channels[users[session.nid].channel].usernum) {
+ delete channels[users[session.nid].channel];
+ }
+ delete users[session.nid].channel;
+ io.sockets.emit('part', {
+ id: session.nid
+ });
+ });
socket.on('filter', function(d) {
if (!(typeof(d) == "object" && "filters" in d)) {
return;
}
users[session.nid].filters = d.filters;
logAction("FILTER", d.filters);
- socket.broadcast.emit('filterset', {
+ var channel = users[session.nid].channel;
+ if (!channel) {
+ return;
+ }
+ channels[channel].filters = d.filters;
+ socket.broadcast.to(channel).emit('filterset', {
id: session.nid,
filters: d.filters
});
@@ -111,7 +175,11 @@ sessionSockets.on('connection', function (err, socket, session) {
});
});
function logAction(action, content) {
- logger.info(session.nid, action, content);
+ if (arguments.length > 1) {
+ logger.info(session.nid, action, content);
+ } else {
+ logger.info(session.nid, action);
+ }
}
});
diff --git a/public/css/style.css b/public/css/style.css
index d223226..0e1e244 100644
--- a/public/css/style.css
+++ b/public/css/style.css
@@ -151,9 +151,20 @@ h3 {
border-left: 1px black solid;
border-bottom: 1px black solid;
display: none;
+ height: 200px;
+ overflow-y: scroll;
}
-#users-status {
+#users-status .channel {
+ border: 1px gray solid;
+ margin: 0 2em;
+}
+
+#users-status a {
+ margin-left: 1em;
+}
+
+#users-status ul {
padding-left: 20px;
}
diff --git a/public/index.html b/public/index.html
index 0ab91a1..a016fb1 100644
--- a/public/index.html
+++ b/public/index.html
@@ -9,7 +9,7 @@
<div class="side med col3">
<div id="connect-status">
<h3>Manavis users</h3>
- <ul id="users-status"></ul>
+ <div id="users-status"></div>
</div>
<div id="blvl-chart">
<h3>Character base level <a class="reset" style="display: none;" href="javascript:mv.charts.blvl.filterAll();dc.redrawAll();">clear</a></h3>
diff --git a/public/js/mv/connect.js b/public/js/mv/connect.js
index 0fee5e7..564690d 100644
--- a/public/js/mv/connect.js
+++ b/public/js/mv/connect.js
@@ -1,7 +1,9 @@
"use strict";
var mv = function(mv) {
- mv.socket = {
- connect: connect
+ mv.connect = {
+ connect: connect,
+ join: join,
+ part: part
};
/* If we're updating due to something we received, then don't broadcast back. */
var netrendering = false;
@@ -19,12 +21,17 @@ var mv = function(mv) {
socket.on("disconnect", function() { console.log("DISCONNECT", arguments); });
d3.select("#connect-status").style("display", "block");
socket.emit('login');
+ /* Tell the server our starting filters */
+ socket.emit("filter", { filters: mv.charter.filters() });
/*
* Protocol:
* selflogin -> id (I)
* login -> id (I), nick (S)
* nickset -> id (I), nick (S)
- * users -> { id -> {nick (S), filters ({dim (S) -> filter (*)}) } }
+ * join -> id (I), channel (I)
+ * part -> id (I)
+ * filterset -> id (I), filters ({dim (S) -> filter (*)})
+ * users -> { id -> { nick (S), channel (I), filters ({dim (S) -> filter (*)}) } }
* logout -> id (I)
*/
socket.on('selflogin', function(d) {
@@ -48,9 +55,20 @@ var mv = function(mv) {
users[d.id].nick = d.nick;
updateUsersStatus();
});
+ socket.on('join', function(d) {
+ users[d.id].channel = d.channel;
+ updateUsersStatus();
+ });
+ socket.on('part', function(d) {
+ delete users[d.id].channel;
+ updateUsersStatus();
+ });
socket.on('filterset', function(d) {
/* Someone changed their filter */
- users[d.id].filters = d.filters;
+ /* Ignore server sourced (ID: 0) changes */
+ if (d.id) {
+ users[d.id].filters = d.filters;
+ }
/* Use the variable netrendering to denote that we're rendering due to a change received from the network. */
netrendering = true;
setOwnFilters(d.filters);
@@ -83,6 +101,13 @@ var mv = function(mv) {
var key;
/* Check for keys in the filters to apply which are not in our charts. */
for (key in filters) {
+ if (key == "date") {
+ /*
+ * Special case! FIXME: Find a more elegant way to handle this
+ */
+ filters[key][0] = new Date(filters[key][0]);
+ filters[key][1] = new Date(filters[key][1]);
+ }
if (!(key in mv.charts))
continue;
var filter = mv.charts[key].filter();
@@ -115,49 +140,145 @@ var mv = function(mv) {
dc.redrawAll();
}
}
+ function join(channel) {
+ if (channel != null) {
+ socket.emit("join", channel);
+ } else {
+ socket.emit("join");
+ }
+ }
+ function part() {
+ socket.emit("part");
+ }
function updateUsersStatus() {
/* Convert the user list to a form suitable for a d3 selection */
- var data = [];
- for (var uid in users) { users[uid].id = uid; data.push(users[uid]); }
- /* Data join */
- var userlist = usersStatus.selectAll(".user")
- .data(data, function(d) { return d.id; });
- /* Enter */
- userlist
- .enter().append("li").attr("class", "user")
+ var groups = [];
+ /* Track the groupless people separately. They should come at the end. */
+ var unchannelled = [];
+ var channelById = {};
+ var channelNames = [];
+ for (var uid in users) {
+ users[uid].id = uid;
+ if ("channel" in users[uid]) {
+ if (!(users[uid].channel in channelById)) {
+ groups.push(channelById[users[uid].channel] = []);
+ channelNames.push(users[uid].channel);
+ }
+ channelById[users[uid].channel].push(users[uid]);
+ } else {
+ unchannelled.push(users[uid]);
+ }
+ }
+ console.log("Channels:", groups, "Users without channels:", unchannelled);
+ groups.push(unchannelled);
+ var createpart = usersStatus.select(".createpart");
+ /* Link to part a channel we're in, or create a channel if we're not in one */
+ if (createpart.empty()) {
+ createpart = usersStatus.append("a")
+ .attr("class", "createpart")
+ ;
+ }
+ if ("channel" in users[id]) {
+ createpart
+ .attr("href", "javascript:mv.connect.part();")
+ .text("Part channel")
+ ;
+ } else {
+ createpart
+ .attr("href", "javascript:mv.connect.join();")
+ .text("Create channel")
+ ;
+ }
+ /* List of groups, with unchanneled users at the end */
+ var grouplist = usersStatus.selectAll(".group")
+ .data(groups, function(d, i) { return channelNames[i]; })
+ ;
+ grouplist
+ .enter().append("div").attr("class", "group")
;
/* Update */
- userlist
- .each(function(d,i) {
- var elm = d3.select(this);
- console.log("Userlist para appending", d,i);
- var name = elm.select(".name");
- var nick = d.nick == null ? "Anonymous User " + d.id : d.nick;
- if (d.id == id) {
- /* This is us! We can edit our name. */
- if (name.empty()) {
- console.log("Found our entry. id:", id, "datum", d);
- name = elm.append("input").attr("class", "name")
- .attr("type", "text")
- .attr("placeholder", "Enter name here")
- .on("change", function () {
- /* A d3 select must be dereferenced twice to access the DOM entity */
- socket.emit("nick", { nick: name[0][0].value });
- })
+ grouplist
+ .each(function(d, i) {
+ var group = d3.select(this);
+ var ul = group.select("ul");
+ if (ul.empty()) {
+ ul = group.append("ul");
+ }
+ /*
+ * Hacky way to check if these are the users without a channel.
+ * Feel free to FIXME!
+ */
+ if (i != groups.length - 1) {
+ console.log("Group is a channel", i, groups.length);
+ group
+ .attr("class", "group channel")
+ ;
+ var join = group.select(".join");
+ if (join.empty()) {
+ join = group.append("a")
+ .attr("class", "join")
+ .attr("href", "javascript:mv.connect.join(" + channelNames[i] + ");")
+ .text("Join channel")
;
}
- name.attr("value", nick);
+ if (users[id].channel == channelNames[i]) {
+ /* We're in this channel */
+ join.style("display", "none");
+ } else {
+ join.style("display", "inline");
+ }
} else {
- /* This is someone else. We can't edit their name. */
- if (name.empty()) {
- name = elm.append("p").attr("class", "name");
+ console.log("Group is not a channel", i, groups.length);
+ var join = group.select(".join");
+ group
+ .attr("class", "group")
+ ;
+ if (join.empty()) {
+ join.remove();
}
- name.text(nick);
}
+ /* Update the list of users in this group */
+ var userlist = ul.selectAll(".user")
+ .data(d, function(d) { return d.id; })
+ ;
+ userlist
+ .enter().append("li").attr("class", "user")
+ ;
+ userlist
+ .each(function(d,i) {
+ var elm = d3.select(this);
+ console.log("Userlist para appending", d,i);
+ var name = elm.select(".name");
+ var nick = d.nick == null ? "Anonymous User " + d.id : d.nick;
+ if (d.id == id) {
+ /* This is us! We can edit our name. */
+ if (name.empty()) {
+ console.log("Found our entry. id:", id, "datum", d);
+ name = elm.append("input").attr("class", "name")
+ .attr("type", "text")
+ .attr("placeholder", "Enter name here")
+ .on("change", function () {
+ /* A d3 select must be dereferenced twice to access the DOM entity */
+ socket.emit("nick", { nick: name[0][0].value });
+ })
+ ;
+ }
+ name.attr("value", nick);
+ } else {
+ /* This is someone else. We can't edit their name. */
+ if (name.empty()) {
+ name = elm.append("p").attr("class", "name");
+ }
+ name.text(nick);
+ }
+ })
+ ;
+ userlist
+ .exit().remove()
+ ;
})
;
- /* Remove */
- userlist
+ grouplist
.exit().remove()
;
}
diff --git a/public/js/mv/main.js b/public/js/mv/main.js
index eb802e0..d7ebdf9 100644
--- a/public/js/mv/main.js
+++ b/public/js/mv/main.js
@@ -58,7 +58,7 @@ var mv = function(mv) {
mv.charter.init();
console.log(document.getElementById("connect-option").checked);
if (document.getElementById("connect-option").checked) {
- mv.socket.connect();
+ mv.connect.connect();
}
}, 2000);
}
diff --git a/public/js/mv/parse.js b/public/js/mv/parse.js
index b30a19a..2c8b916 100644
--- a/public/js/mv/parse.js
+++ b/public/js/mv/parse.js
@@ -218,7 +218,6 @@ var mv = function(mv) {
}
function parseScrubbed(scrubbedRecords) {
scrubbedRecords = JSON.parse(scrubbedRecords);
- console.log(scrubbedRecords, scrubbedRecords.length);
/*
* The work is mostly all done for us. Just scan through to see if there
* are any undefined records, and update the pointer if so.
@@ -236,7 +235,7 @@ var mv = function(mv) {
}
}
/* It's simple when everything's already been done. */
- parser.records = parser.records.concat(scrubbedRecords);
+ mv.parser.records = mv.parser.records.concat(scrubbedRecords);
}
return mv;
}(mv || {});