CyTube/www/js/ws.js

148 lines
4 KiB
JavaScript
Raw Normal View History

2018-06-15 04:33:40 +00:00
(function () {
2018-06-19 06:12:00 +00:00
var TYPE_FRAME = 0;
var TYPE_ACK = 1;
2018-06-23 23:54:26 +00:00
function WSShim(url) {
this._url = url;
2018-06-15 04:33:40 +00:00
this._listeners = Object.create(null);
2018-06-22 05:05:20 +00:00
this._connected = false;
2018-06-15 04:33:40 +00:00
2018-06-19 06:12:00 +00:00
this._ackId = 0;
this._pendingAcks = Object.create(null);
2018-06-23 23:54:26 +00:00
this._openWS();
2018-06-15 04:33:40 +00:00
}
WSShim.prototype.listeners = function listeners(frame) {
if (!Object.prototype.hasOwnProperty.call(this._listeners, frame)) {
this._listeners[frame] = [];
}
return this._listeners[frame];
};
WSShim.prototype.on = function on(frame, callback) {
this.listeners(frame).push(callback);
};
2018-06-23 23:39:57 +00:00
WSShim.prototype.once = function on(frame, callback) {
callback._once = true;
this.listeners(frame).push(callback);
};
2018-06-19 06:12:00 +00:00
WSShim.prototype.emit = function emit(frame, payload, ack) {
var message = {
type: TYPE_FRAME,
frame: frame,
payload: payload
};
2018-06-15 04:33:40 +00:00
2018-06-19 06:12:00 +00:00
if (ack && typeof ack === 'function') {
message.ackId = ++this._ackId;
this._pendingAcks[message.ackId] = ack;
}
2018-06-15 04:33:40 +00:00
2018-06-19 06:12:00 +00:00
this._ws.send(JSON.stringify(message));
};
2018-06-15 04:33:40 +00:00
2018-06-19 06:12:00 +00:00
WSShim.prototype._emit = function _emit(frame, payload) {
2018-06-23 23:39:57 +00:00
var hasOnce = false;
2018-06-15 04:33:40 +00:00
this.listeners(frame).forEach(function (cb) {
2018-06-19 06:12:00 +00:00
try {
2018-06-23 23:39:57 +00:00
if (cb._once) {
hasOnce = true;
}
2018-06-19 06:12:00 +00:00
cb(payload);
} catch (error) {
console.error('Error in callback for ' + frame + ': ' + error);
}
2018-06-15 04:33:40 +00:00
});
2018-06-23 23:39:57 +00:00
if (hasOnce) {
this._listeners[frame] = this._listeners[frame].filter(function (cb) {
return !cb._once;
});
}
2018-06-15 04:33:40 +00:00
};
2018-06-22 05:05:20 +00:00
WSShim.prototype._onopen = function _onopen() {
this._connected = true;
};
2018-06-15 04:33:40 +00:00
WSShim.prototype._onclose = function _onclose() {
2018-06-22 05:05:20 +00:00
if (!this._connected) {
return;
}
2018-06-23 23:54:26 +00:00
this._connected = false;
2018-06-15 04:33:40 +00:00
this._emit('disconnect');
2018-06-22 05:05:20 +00:00
2018-06-23 23:54:26 +00:00
// TODO: checking for KICKED here is insufficient;
// need to have some sort of explicit disconnect vs. connection loss
// check
2018-06-22 05:05:20 +00:00
if (!KICKED) {
2018-06-23 23:54:26 +00:00
var self = this;
2018-06-22 05:05:20 +00:00
function reconnectAsync(cb) {
2018-06-23 23:54:26 +00:00
self._openWS();
2018-06-22 05:05:20 +00:00
2018-06-23 23:54:26 +00:00
self._ws.addEventListener('open', function () {
2018-06-22 05:05:20 +00:00
cb(null);
});
2018-06-23 23:54:26 +00:00
self._ws.addEventListener('error', function (error) {
2018-06-22 05:05:20 +00:00
cb(error);
});
}
var retryOpts = {
delay: 1000,
jitter: 1000,
factor: 2,
maxDelay: 20000
};
setTimeout(function () {
backoffRetry(reconnectAsync, function(){}, retryOpts);
}, 1000);
}
2018-06-15 04:33:40 +00:00
};
WSShim.prototype._onmessage = function _onmessage(message) {
try {
2018-06-19 06:12:00 +00:00
var parsed = JSON.parse(message.data);
var type = parsed.type;
var frame = parsed.frame;
var payload = parsed.payload;
var ackId = parsed.ackId;
if (type === TYPE_ACK && ackId in this._pendingAcks) {
this._pendingAcks[ackId](payload);
delete this._pendingAcks[ackId];
} else if (type === TYPE_FRAME) {
this._emit(frame, payload);
}
2018-06-15 04:33:40 +00:00
} catch (error) {
console.error('Unparseable message from server: ' + message);
console.error(error.stack);
return;
}
};
2018-06-23 23:54:26 +00:00
WSShim.prototype._openWS = function _openWS() {
if (this._connected) {
throw new Error('Cannot _openWS() when already connected');
}
this._ws = new WebSocket(this._url);
this._ws.onopen = this._onopen.bind(this);
this._ws.onclose = this._onclose.bind(this);
this._ws.onmessage = this._onmessage.bind(this);
this._connected = false;
};
2018-06-15 04:33:40 +00:00
window.WSShim = WSShim;
})();