diff --git a/www/canvas/main.js b/www/canvas/main.js
index 1a52213ff..212f12bfe 100644
--- a/www/canvas/main.js
+++ b/www/canvas/main.js
@@ -67,6 +67,7 @@ define([
userName: Crypto.rand64(8),
channel: channel,
cryptKey: key,
+ crypto: Crypto,
};
var onInit = config.onInit = function (info) {
diff --git a/www/common/json-ot.js b/www/common/json-ot.js
index a785d524c..f04cf3b75 100644
--- a/www/common/json-ot.js
+++ b/www/common/json-ot.js
@@ -4,10 +4,9 @@ define([
var ChainPad = window.ChainPad;
var JsonOT = {};
-/* FIXME
- resultOp after transform0() might be null, in which case you should return null
- because it is simply a transformation which yields a "do nothing" operation */
var validate = JsonOT.validate = function (text, toTransform, transformBy) {
+ var DEBUG = window.REALTIME_DEBUG = window.REALTIME_DEBUG || {};
+
var resultOp, text2, text3;
try {
// text = O (mutual common ancestor)
@@ -28,7 +27,7 @@ define([
return resultOp;
} catch (e) {
console.error(e);
- var info = window.REALTIME_MODULE.ot_parseError = {
+ var info = DEBUG.ot_parseError = {
type: 'resultParseError',
resultOp: resultOp,
@@ -40,11 +39,11 @@ define([
text3: text3,
error: e
};
- console.log('Debugging info available at `window.REALTIME_MODULE.ot_parseError`');
+ console.log('Debugging info available at `window.REALTIME_DEBUG.ot_parseError`');
}
} catch (x) {
console.error(x);
- window.REALTIME_MODULE.ot_applyError = {
+ window.DEBUG.ot_applyError = {
type: 'resultParseError',
resultOp: resultOp,
@@ -56,7 +55,7 @@ define([
text3: text3,
error: x
};
- console.log('Debugging info available at `window.REALTIME_MODULE.ot_applyError`');
+ console.log('Debugging info available at `window.REALTIME_DEBUG.ot_applyError`');
}
// returning **null** breaks out of the loop
diff --git a/www/common/netflux-client.js b/www/common/netflux-client.js
index e6567229a..83dadcf5c 100644
--- a/www/common/netflux-client.js
+++ b/www/common/netflux-client.js
@@ -1,225 +1,291 @@
/*global: WebSocket */
-define(() => {
-'use strict';
-const MAX_LAG_BEFORE_PING = 15000;
-const MAX_LAG_BEFORE_DISCONNECT = 30000;
-const PING_CYCLE = 5000;
-const REQUEST_TIMEOUT = 30000;
-
-const now = () => new Date().getTime();
-
-const networkSendTo = (ctx, peerId, content) => {
- const seq = ctx.seq++;
- ctx.ws.send(JSON.stringify([seq, 'MSG', peerId, content]));
- return new Promise((res, rej) => {
- ctx.requests[seq] = { reject: rej, resolve: res, time: now() };
- });
-};
-
-const channelBcast = (ctx, chanId, content) => {
- const chan = ctx.channels[chanId];
- if (!chan) { throw new Error("no such channel " + chanId); }
- const seq = ctx.seq++;
- ctx.ws.send(JSON.stringify([seq, 'MSG', chanId, content]));
- return new Promise((res, rej) => {
- ctx.requests[seq] = { reject: rej, resolve: res, time: now() };
- });
-};
-
-const channelLeave = (ctx, chanId, reason) => {
- const chan = ctx.channels[chanId];
- if (!chan) { throw new Error("no such channel " + chanId); }
- delete ctx.channels[chanId];
- ctx.ws.send(JSON.stringify([ctx.seq++, 'LEAVE', chanId, reason]));
-};
-
-const makeEventHandlers = (ctx, mappings) => {
- return (name, handler) => {
- const handlers = mappings[name];
- if (!handlers) { throw new Error("no such event " + name); }
- handlers.push(handler);
- };
-};
-
-const mkChannel = (ctx, id) => {
- const internal = {
- onMessage: [],
- onJoin: [],
- onLeave: [],
- members: [],
- jSeq: ctx.seq++
+define(function () {
+ 'use strict';
+
+ var MAX_LAG_BEFORE_PING = 15000;
+ var MAX_LAG_BEFORE_DISCONNECT = 30000;
+ var PING_CYCLE = 5000;
+ var REQUEST_TIMEOUT = 30000;
+
+ var now = function now() {
+ return new Date().getTime();
};
- const chan = {
- _: internal,
- id: id,
- members: internal.members,
- bcast: (msg) => channelBcast(ctx, chan.id, msg),
- leave: (reason) => channelLeave(ctx, chan.id, reason),
- on: makeEventHandlers(ctx, { message:
- internal.onMessage, join: internal.onJoin, leave: internal.onLeave })
+
+ var networkSendTo = function networkSendTo(ctx, peerId, content) {
+ var seq = ctx.seq++;
+ ctx.ws.send(JSON.stringify([seq, 'MSG', peerId, content]));
+ return new Promise(function (res, rej) {
+ ctx.requests[seq] = { reject: rej, resolve: res, time: now() };
+ });
};
- ctx.requests[internal.jSeq] = chan;
- ctx.ws.send(JSON.stringify([internal.jSeq, 'JOIN', id]));
-
- return new Promise((res, rej) => {
- chan._.resolve = res;
- chan._.reject = rej;
- })
-};
-
-const mkNetwork = (ctx) => {
- const network = {
- webChannels: ctx.channels,
- getLag: () => (ctx.lag),
- sendto: (peerId, content) => (networkSendTo(ctx, peerId, content)),
- join: (chanId) => (mkChannel(ctx, chanId)),
- on: makeEventHandlers(ctx, { message: ctx.onMessage, disconnect: ctx.onDisconnect })
+
+ var channelBcast = function channelBcast(ctx, chanId, content) {
+ var chan = ctx.channels[chanId];
+ if (!chan) {
+ throw new Error("no such channel " + chanId);
+ }
+ var seq = ctx.seq++;
+ ctx.ws.send(JSON.stringify([seq, 'MSG', chanId, content]));
+ return new Promise(function (res, rej) {
+ ctx.requests[seq] = { reject: rej, resolve: res, time: now() };
+ });
};
- network.__defineGetter__("webChannels", () => {
- return Object.keys(ctx.channels).map((k) => (ctx.channels[k]));
- });
- return network;
-};
-
-const onMessage = (ctx, evt) => {
- let msg;
- try { msg = JSON.parse(evt.data); } catch (e) { console.log(e.stack); return; }
- if (msg[0] !== 0) {
- const req = ctx.requests[msg[0]];
- if (!req) {
- console.log("error: " + JSON.stringify(msg));
- return;
+
+ var channelLeave = function channelLeave(ctx, chanId, reason) {
+ var chan = ctx.channels[chanId];
+ if (!chan) {
+ throw new Error("no such channel " + chanId);
}
- delete ctx.requests[msg[0]];
- if (msg[1] === 'ACK') {
- if (req.ping) { // ACK of a PING
- ctx.lag = now() - Number(req.ping);
- return;
- }
- req.resolve();
- } else if (msg[1] === 'JACK') {
- if (req._) {
- // Channel join request...
- if (!msg[2]) { throw new Error("wrong type of ACK for channel join"); }
- req.id = msg[2];
- ctx.channels[req.id] = req;
- return;
+ delete ctx.channels[chanId];
+ ctx.ws.send(JSON.stringify([ctx.seq++, 'LEAVE', chanId, reason]));
+ };
+
+ var makeEventHandlers = function makeEventHandlers(ctx, mappings) {
+ return function (name, handler) {
+ var handlers = mappings[name];
+ if (!handlers) {
+ throw new Error("no such event " + name);
}
- req.resolve();
- } else if (msg[1] === 'ERROR') {
- req.reject({ type: msg[2], message: msg[3] });
- } else {
- req.reject({ type: 'UNKNOWN', message: JSON.stringify(msg) });
+ handlers.push(handler);
+ };
+ };
+
+ var mkChannel = function mkChannel(ctx, id) {
+ var internal = {
+ onMessage: [],
+ onJoin: [],
+ onLeave: [],
+ members: [],
+ jSeq: ctx.seq++
+ };
+ var chan = {
+ _: internal,
+ time: now(),
+ id: id,
+ members: internal.members,
+ bcast: function bcast(msg) {
+ return channelBcast(ctx, chan.id, msg);
+ },
+ leave: function leave(reason) {
+ return channelLeave(ctx, chan.id, reason);
+ },
+ on: makeEventHandlers(ctx, { message: internal.onMessage, join: internal.onJoin, leave: internal.onLeave })
+ };
+ ctx.requests[internal.jSeq] = chan;
+ ctx.ws.send(JSON.stringify([internal.jSeq, 'JOIN', id]));
+
+ return new Promise(function (res, rej) {
+ chan._.resolve = res;
+ chan._.reject = rej;
+ });
+ };
+
+ var mkNetwork = function mkNetwork(ctx) {
+ var network = {
+ webChannels: ctx.channels,
+ getLag: function getLag() {
+ return ctx.lag;
+ },
+ sendto: function sendto(peerId, content) {
+ return networkSendTo(ctx, peerId, content);
+ },
+ join: function join(chanId) {
+ return mkChannel(ctx, chanId);
+ },
+ on: makeEventHandlers(ctx, { message: ctx.onMessage, disconnect: ctx.onDisconnect })
+ };
+ network.__defineGetter__("webChannels", function () {
+ return Object.keys(ctx.channels).map(function (k) {
+ return ctx.channels[k];
+ });
+ });
+ return network;
+ };
+
+ var onMessage = function onMessage(ctx, evt) {
+ var msg = void 0;
+ try {
+ msg = JSON.parse(evt.data);
+ } catch (e) {
+ console.log(e.stack);return;
}
- return;
- }
- if (msg[2] === 'IDENT') {
- ctx.uid = msg[3];
-
- setInterval(() => {
- if (now() - ctx.timeOfLastMessage < MAX_LAG_BEFORE_PING) { return; }
- let seq = ctx.seq++;
- let currentDate = now();
- ctx.requests[seq] = {time: now(), ping: currentDate};
- ctx.ws.send(JSON.stringify([seq, 'PING', currentDate]));
- if (now() - ctx.timeOfLastMessage > MAX_LAG_BEFORE_DISCONNECT) {
- ctx.ws.close();
- }
- }, PING_CYCLE);
-
- return;
- } else if (!ctx.uid) {
- // extranious message, waiting for an ident.
- return;
- }
- if (msg[2] === 'PING') {
- msg[2] = 'PONG';
- ctx.ws.send(JSON.stringify(msg));
- return;
- }
-
- if (msg[2] === 'MSG') {
- let handlers;
- if (msg[3] === ctx.uid) {
- handlers = ctx.onMessage;
- } else {
- const chan = ctx.channels[msg[3]];
- if (!chan) {
- console.log("message to non-existant chan " + JSON.stringify(msg));
+ if (msg[0] !== 0) {
+ var req = ctx.requests[msg[0]];
+ if (!req) {
+ console.log("error: " + JSON.stringify(msg));
return;
}
- handlers = chan._.onMessage;
+ delete ctx.requests[msg[0]];
+ if (msg[1] === 'ACK') {
+ if (req.ping) {
+ // ACK of a PING
+ ctx.lag = now() - Number(req.ping);
+ return;
+ }
+ req.resolve();
+ } else if (msg[1] === 'JACK') {
+ if (req._) {
+ // Channel join request...
+ if (!msg[2]) {
+ throw new Error("wrong type of ACK for channel join");
+ }
+ req.id = msg[2];
+ ctx.channels[req.id] = req;
+ return;
+ }
+ req.resolve();
+ } else if (msg[1] === 'ERROR') {
+ req.reject({ type: msg[2], message: msg[3] });
+ } else {
+ req.reject({ type: 'UNKNOWN', message: JSON.stringify(msg) });
+ }
+ return;
}
- handlers.forEach((h) => {
- try { h(msg[4], msg[1]); } catch (e) { console.error(e); }
- });
- }
+
+ if (msg[2] === 'IDENT') {
+ ctx.uid = msg[3];
+
+ setInterval(function () {
+ if (now() - ctx.timeOfLastMessage < MAX_LAG_BEFORE_PING) {
+ return;
+ }
+ var seq = ctx.seq++;
+ var currentDate = now();
+ ctx.requests[seq] = { time: now(), ping: currentDate };
+ ctx.ws.send(JSON.stringify([seq, 'PING', currentDate]));
+ if (now() - ctx.timeOfLastMessage > MAX_LAG_BEFORE_DISCONNECT) {
+ ctx.ws.close();
+ }
+ }, PING_CYCLE);
- if (msg[2] === 'LEAVE') {
- const chan = ctx.channels[msg[3]];
- if (!chan) {
- console.log("leaving non-existant chan " + JSON.stringify(msg));
+ return;
+ } else if (!ctx.uid) {
+ // extranious message, waiting for an ident.
return;
}
- chan._.onLeave.forEach((h) => {
- try { h(msg[1], msg[4]); } catch (e) { console.log(e.stack); }
- });
- }
-
- if (msg[2] === 'JOIN') {
- const chan = ctx.channels[msg[3]];
- if (!chan) {
- console.log("ERROR: join to non-existant chan " + JSON.stringify(msg));
+ if (msg[2] === 'PING') {
+ msg[2] = 'PONG';
+ ctx.ws.send(JSON.stringify(msg));
return;
}
- // have we yet fully joined the chan?
- const synced = (chan._.members.indexOf(ctx.uid) !== -1);
- chan._.members.push(msg[1]);
- if (!synced && msg[1] === ctx.uid) {
- // sync the channel join event
- chan.myID = ctx.uid;
- chan._.resolve(chan);
+
+ if (msg[2] === 'MSG') {
+ var handlers = void 0;
+ if (msg[3] === ctx.uid) {
+ handlers = ctx.onMessage;
+ } else {
+ var chan = ctx.channels[msg[3]];
+ if (!chan) {
+ console.log("message to non-existant chan " + JSON.stringify(msg));
+ return;
+ }
+ handlers = chan._.onMessage;
+ }
+ handlers.forEach(function (h) {
+ try {
+ h(msg[4], msg[1]);
+ } catch (e) {
+ console.error(e);
+ }
+ });
}
- if (synced) {
- chan._.onJoin.forEach((h) => {
- try { h(msg[1]); } catch (e) { console.log(e.stack); }
+
+ if (msg[2] === 'LEAVE') {
+ var _chan = ctx.channels[msg[3]];
+ if (!_chan) {
+ console.log("leaving non-existant chan " + JSON.stringify(msg));
+ return;
+ }
+ _chan._.onLeave.forEach(function (h) {
+ try {
+ h(msg[1], msg[4]);
+ } catch (e) {
+ console.log(e.stack);
+ }
});
}
- }
-};
-
-const connect = (websocketURL) => {
- let ctx = {
- ws: new WebSocket(websocketURL),
- seq: 1,
- lag: 0,
- uid: null,
- network: null,
- channels: {},
- onMessage: [],
- onDisconnect: [],
- requests: {}
- };
- setInterval(() => {
- for (let id in ctx.requests) {
- const req = ctx.requests[id];
- if (now() - req.time > REQUEST_TIMEOUT) {
- delete ctx.requests[id];
- if(typeof req.reject === "function") { req.reject({ type: 'TIMEOUT', message: 'waited ' + now() - req.time + 'ms' }); }
+
+ if (msg[2] === 'JOIN') {
+ var _chan2 = ctx.channels[msg[3]];
+ if (!_chan2) {
+ console.log("ERROR: join to non-existant chan " + JSON.stringify(msg));
+ return;
+ }
+ // have we yet fully joined the chan?
+ var synced = _chan2._.members.indexOf(ctx.uid) !== -1;
+ _chan2._.members.push(msg[1]);
+ if (!synced && msg[1] === ctx.uid) {
+ // sync the channel join event
+ _chan2.myID = ctx.uid;
+ _chan2._.resolve(_chan2);
+ }
+ if (synced) {
+ _chan2._.onJoin.forEach(function (h) {
+ try {
+ h(msg[1]);
+ } catch (e) {
+ console.log(e.stack);
+ }
+ });
}
}
- }, 5000);
- ctx.network = mkNetwork(ctx);
- ctx.ws.onmessage = (msg) => (onMessage(ctx, msg));
- ctx.ws.onclose = (evt) => {
- ctx.onDisconnect.forEach((h) => {
- try { h(evt.reason); } catch (e) { console.log(e.stack); }
+ };
+
+ var connect = function connect(websocketURL) {
+ var ctx = {
+ ws: new WebSocket(websocketURL),
+ seq: 1,
+ lag: 0,
+ uid: null,
+ network: null,
+ channels: {},
+ onMessage: [],
+ onDisconnect: [],
+ requests: {}
+ };
+ setInterval(function () {
+ for (var id in ctx.requests) {
+ var req = ctx.requests[id];
+ if (now() - req.time > REQUEST_TIMEOUT) {
+ delete ctx.requests[id];
+ if (typeof req.reject === "function") {
+ req.reject({ type: 'TIMEOUT', message: 'waited ' + (now() - req.time) + 'ms' });
+ }
+ }
+ }
+ }, 5000);
+ ctx.network = mkNetwork(ctx);
+ ctx.ws.onmessage = function (msg) {
+ return onMessage(ctx, msg);
+ };
+ ctx.ws.onclose = function (evt) {
+ ctx.onDisconnect.forEach(function (h) {
+ try {
+ h(evt.reason);
+ } catch (e) {
+ console.log(e.stack);
+ }
+ });
+ };
+ return new Promise(function (resolve, reject) {
+ ctx.ws.onopen = function () {
+ var count = 0;
+ var interval = 100;
+ var checkIdent = function() {
+ if(ctx.uid !== null) {
+ return resolve(ctx.network);
+ }
+ else {
+ if(count * interval > REQUEST_TIMEOUT) {
+ return reject({ type: 'TIMEOUT', message: 'waited ' + (count * interval) + 'ms' });
+ }
+ setTimeout(checkIdent, 100);
+ }
+ }
+ checkIdent();
+ };
});
};
- return new Promise((resolve, reject) => {
- ctx.ws.onopen = () => resolve(ctx.network);
- });
-};
-return { connect: connect };
-});
+ return { connect: connect };
+});
\ No newline at end of file
diff --git a/www/common/realtime-input.js b/www/common/realtime-input.js
index 6a958e406..5a8726e35 100644
--- a/www/common/realtime-input.js
+++ b/www/common/realtime-input.js
@@ -15,16 +15,15 @@
* along with this program. If not, see