diff --git a/index.js b/index.js
index bf29f61..dc5011b 100644
--- a/index.js
+++ b/index.js
@@ -100,9 +100,15 @@ function WebSocketConnection(label, wsurl, shouldReconnect) {
this.deduplicator = new Deduplicator();
}
+WebSocketConnection.prototype.statusRoute = function (status) {
+ return pub([this.label + "_state", status]);
+};
+
WebSocketConnection.prototype.relayRoutes = function () {
// fresh copy each time, suitable for in-place extension/mutation
- return [pub([this.label, __, __], 0, 1000), sub([this.label, __, __], 0, 1000)];
+ return [this.statusRoute(this.isConnected() ? "connected" : "disconnected"),
+ pub([this.label, __, __], 0, 1000),
+ sub([this.label, __, __], 0, 1000)];
};
WebSocketConnection.prototype.aggregateRoutes = function () {
@@ -119,7 +125,6 @@ WebSocketConnection.prototype.aggregateRoutes = function () {
};
WebSocketConnection.prototype.boot = function () {
- World.updateRoutes(this.aggregateRoutes());
this.reconnect();
};
@@ -224,6 +229,10 @@ WebSocketConnection.prototype.onmessage = function (wse) {
WebSocketConnection.prototype.onclose = function (e) {
var self = this;
// console.log("onclose", e);
+
+ // Update routes to give clients some indication of the discontinuity
+ World.updateRoutes(this.aggregateRoutes());
+
if (this.shouldReconnect) {
console.log("reconnecting to " + this.wsurl + " in " + this.reconnectDelay + "ms");
setTimeout(World.wrap(function () { self.reconnect(); }), this.reconnectDelay);
@@ -238,34 +247,55 @@ WebSocketConnection.prototype.onclose = function (e) {
///////////////////////////////////////////////////////////////////////////
// Main
+function outputItem(item) {
+ var stamp = $("
").text((new Date()).toGMTString()).addClass("timestamp");
+ var item = $("
").append([stamp].concat(item));
+ var o = $("#chat_output");
+ o.append(item);
+ o[0].scrollTop = o[0].scrollHeight;
+ return item;
+}
+
function updateNymList(rs) {
var nyms = [];
+ var statuses = {};
for (var i = 0; i < rs.length; i++) {
var p = rs[i].pattern;
- if (p[0] === "broker"
- && p[1] === 0
- && p[2][1] === "says")
- {
+ if (p[0] === "broker" && p[1] === 0 && p[2][1] === "says") {
nyms.push(p[2][0]);
}
+ if (p[0] === "broker" && p[1] === 0 && p[2][1] === "status") {
+ statuses[p[2][0]] = p[2][2];
+ }
}
- console.log(nyms);
+
+ var container = $("#nymlist");
+ container[0].innerHTML = ""; // remove all children
+ for (var i = 0; i < nyms.length; i++) {
+ var n = $("
").text(nyms[i]).addClass("nym");
+ var s = statuses[nyms[i]];
+ if (s) {
+ container.append($("
").append([n, $("
").text(s).addClass("nym_status")]));
+ } else {
+ container.append($("
").append(n));
+ }
+ }
+}
+
+function outputState(state) {
+ outputItem([$("
").text(state).addClass(state).addClass("state")])
+ .addClass("state_" + state);
}
function outputUtterance(who, what) {
- var stamp = $("
").text((new Date()).toGMTString()).addClass("timestamp");
- var nymLabel = $("
").text(who).addClass("nym");
- var utterance = $("
").text(what).addClass("utterance");
- var o = $("#chat_output");
- o.append($("
")
- .append([stamp, nymLabel, utterance])
- .addClass("utterance"));
- o[0].scrollTop = o[0].scrollHeight;
+ outputItem([$("
").text(who).addClass("nym"),
+ $("
").text(what).addClass("utterance")]).addClass("utterance");
}
$(document).ready(function () {
$("#chat_form").submit(function (e) { e.preventDefault(); return false; });
$("#nym_form").submit(function (e) { e.preventDefault(); return false; });
+ $("#nym").val("nym" + Math.floor(Math.random() * 65536));
var g = new Ground(function () {
console.log('starting ground boot');
@@ -273,20 +303,43 @@ $(document).ready(function () {
spawnJQueryDriver();
World.spawn(new WebSocketConnection("broker", "ws://localhost:8000/", true));
World.spawn({
- // step: function () { console.log('dummy step'); },
+ // Monitor connection, notifying connectivity changes
+ state: null,
+ boot: function () {
+ World.updateRoutes([sub(["broker_state", __], 0, 1)]);
+ },
+ handleEvent: function (e) {
+ if (e.type === "routes") {
+ if (e.routes.length > 0) {
+ var newState = e.routes[0].pattern[1];
+ if (this.state != newState) {
+ outputState(newState);
+ this.state = newState;
+ }
+ }
+ }
+ }
+ });
+ World.spawn({
+ // Actual chat functionality
+ peers: new PresenceDetector(),
+ peerMap: {},
boot: function () {
World.updateRoutes(this.subscriptions());
},
- nym: function () {
- return $("#nym").val();
- },
+ nym: function () { return $("#nym").val(); },
+ currentStatus: function () { return $("#status").val(); },
subscriptions: function () {
return [sub(["jQuery", "#send_chat", "click", __]),
sub(["jQuery", "#nym", "change", __]),
+ sub(["jQuery", "#status", "change", __]),
pub(["broker", 0, [this.nym(), "says", __]]),
- sub(["broker", 0, [__, "says", __]], 0, 1)];
+ pub(["broker", 0, [this.nym(), "status", this.currentStatus()]]),
+ sub(["broker", 0, [__, "says", __]], 0, 1),
+ sub(["broker", 0, [__, "status", __]], 0, 1)];
},
handleEvent: function (e) {
+ var self = this;
switch (e.type) {
case "routes":
updateNymList(e.routes);
@@ -304,6 +357,7 @@ $(document).ready(function () {
}
break;
case "#nym":
+ case "#status":
World.updateRoutes(this.subscriptions());
break;
default:
diff --git a/style.css b/style.css
index fc50d76..7d581ee 100644
--- a/style.css
+++ b/style.css
@@ -1,17 +1,41 @@
-div.utterance span.timestamp {
+span.timestamp {
color: #d0d0d0;
}
-div.utterance span.timestamp:after {
+span.timestamp:after {
content: " ";
}
-div.utterance span.nym {
+.utterance span.nym:after {
+ content: ": ";
+}
+
+span.arrived:after {
+ content: " arrived";
+}
+
+span.departed:after {
+ content: " departed";
+}
+
+div.notification {
+ background-color: #eeeeff;
+}
+
+span.state.connected, span.arrived {
color: #00c000;
}
-div.utterance span.nym:after {
- content: ": ";
+span.state.disconnected, span.departed {
+ color: #c00000;
+}
+
+div.state_disconnected {
+ background-color: #ffeeee;
+}
+
+div.state_connected {
+ background-color: #eeffee;
}
#chat_output {
@@ -22,3 +46,19 @@ div.utterance span.nym:after {
#chat_input {
width: 80%;
}
+
+.nym {
+ color: #00c000;
+}
+
+.nym_status:before {
+ content: " (";
+}
+
+.nym_status:after {
+ content: ")";
+}
+
+.nym_status {
+ font-size: smaller;
+}
\ No newline at end of file