Commit 10bc055b authored by Hubert Denkmair's avatar Hubert Denkmair
Browse files

use reconnecting-websocket.js for auto-reconnect

parent 40e3ade0
......@@ -63,6 +63,10 @@ function setupToolbar()
});
});
$('#bt_save').click(function() {
save('save', snake_title);
});
$('#bt_save_as').click(function() {
showModal($('#safe_as_dialog'), function() {
save('save', $('#save_as_title').val());
......
......@@ -18,9 +18,10 @@
<div id="log"></div>
<div id="tools">
<div id="toolbar">
<button id="bt_run">save & run</button>
<button id="bt_run">run</button>
<button id="bt_restart">restart</button>
<button id="bt_stop">stop</button>
<button id="bt_save">save</button>
<button id="bt_save_as">save as</button>
<button>load</button>
<button>preferences</button>
......
......@@ -30,7 +30,7 @@ Game.prototype.Run = function()
Game.prototype.ConnectWebsocket = function()
{
this.ws = new WebSocket(this.GetWebsocketURL());
this.ws = new ReconnectingWebSocket(this.GetWebsocketURL());
this.ws.binaryType = 'arraybuffer';
let self = this;
this.ws.addEventListener('open', function(event) {
......
MIT License:
Copyright (c) 2010-2012, Joe Walnes
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
ReconnectingWebSocket
=====================
A small JavaScript library that decorates the WebSocket API to provide a WebSocket connection that will automatically reconnect if the connection is dropped.
It is API compatible, so when you have:
```javascript
var ws = new WebSocket('ws://....');
```
you can replace with:
```javascript
var ws = new ReconnectingWebSocket('ws://....');
```
Minified library with gzip compression is less than 600 bytes.
How reconnections occur
-----------------------
With the standard `WebSocket` API, the events you receive from the WebSocket instance are typically:
onopen
onmessage
onmessage
onmessage
onclose // At this point the WebSocket instance is dead.
With a `ReconnectingWebSocket`, after an `onclose` event is called it will automatically attempt to reconnect. In addition, a connection is attempted repeatedly (with a small pause) until it succeeds. So the events you receive may look something more like:
onopen
onmessage
onmessage
onmessage
onclose
// ReconnectingWebSocket attempts to reconnect
onopen
onmessage
onmessage
onmessage
onclose
// ReconnectingWebSocket attempts to reconnect
onopen
onmessage
onmessage
onmessage
onclose
This is all handled automatically for you by the library.
## Parameters
```javascript
var socket = new ReconnectingWebSocket(url, protocols, options);
```
#### `url`
- The URL you are connecting to.
- https://html.spec.whatwg.org/multipage/comms.html#network
#### `protocols`
- Optional string or array of protocols per the WebSocket spec.
- https://tools.ietf.org/html/rfc6455
#### `options`
- Options (see below)
## Options
Options can either be passed as the 3rd parameter upon instantiation or set directly on the object after instantiation:
```javascript
var socket = new ReconnectingWebSocket(url, null, {debug: true, reconnectInterval: 3000});
```
or
```javascript
var socket = new ReconnectingWebSocket(url);
socket.debug = true;
socket.timeoutInterval = 5400;
```
#### `debug`
- Whether this instance should log debug messages or not. Debug messages are printed to `console.debug()`.
- Accepts `true` or `false`
- Default value: `false`
#### `automaticOpen`
- Whether or not the websocket should attempt to connect immediately upon instantiation. The socket can be manually opened or closed at any time using ws.open() and ws.close().
- Accepts `true` or `false`
- Default value: `true`
#### `reconnectInterval`
- The number of milliseconds to delay before attempting to reconnect.
- Accepts `integer`
- Default: `1000`
#### `maxReconnectInterval`
- The maximum number of milliseconds to delay a reconnection attempt.
- Accepts `integer`
- Default: `30000`
####`reconnectDecay`
- The rate of increase of the reconnect delay. Allows reconnect attempts to back off when problems persist.
- Accepts `integer` or `float`
- Default: `1.5`
#### `timeoutInterval`
- The maximum time in milliseconds to wait for a connection to succeed before closing and retrying.
- Accepts `integer`
- Default: `2000`
#### `maxReconnectAttempts`
- The maximum number of reconnection attempts that will be made before giving up. If null, reconnection attempts will be continue to be made forever.
- Accepts `integer` or `null`.
- Default: `null`
#### `binaryType`
- The binary type is required by some applications.
- Accepts strings `'blob'` or `'arraybuffer'`.
- Default: `'blob'`
---
## Methods
#### `ws.open()`
- Open the Reconnecting Websocket
#### `ws.close(code, reason)`
- Closes the WebSocket connection or connection attempt, if any. If the connection is already CLOSED, this method does nothing.
- `code` is optional the closing code (default value 1000). [https://tools.ietf.org/html/rfc6455#section-7.4.1](https://tools.ietf.org/html/rfc6455#section-7.4.1)
- `reason` is the optional reason that the socket is being closed. [https://tools.ietf.org/html/rfc6455#section-7.1.6](https://tools.ietf.org/html/rfc6455#section-7.1.6)
#### `ws.refresh()`
- Refresh the connection if still open (close and then re-open it).
#### `ws.send(data)`
- Transmits data to the server over the WebSocket connection.
- Accepts @param data a text string, ArrayBuffer or Blob
Like this? Check out [websocketd](https://github.com/joewalnes/websocketd) for the simplest way to create WebSocket backends from any programming language.
[Follow @joewalnes](https://twitter.com/joewalnes)
[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/joewalnes/reconnecting-websocket/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
{
"name": "reconnectingWebsocket",
"main": "reconnecting-websocket.js",
"version": "1.0.1",
"homepage": "https://github.com/joewalnes/reconnecting-websocket",
"authors": [
"Joe Walnes",
"headlessme <contact@coggle.it>"
],
"description": "A small decorator for the JavaScript WebSocket API that automatically reconnects",
"moduleType": [
"amd",
"globals",
"node"
],
"license": "MIT",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
]
}
{
"name": "ReconnectingWebSocket",
"version": "1.0.1",
"description": "A small JavaScript library that decorates the WebSocket API to provide a WebSocket connection that will automatically reconnect if the connection is dropped.",
"main": "reconnecting-websocket.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git://github.com/joewalnes/reconnecting-websocket.git"
},
"author": "Joe Walnes",
"license": "MIT",
"bugs": {
"url": "https://github.com/joewalnes/reconnecting-websocket/issues"
},
"homepage": "https://github.com/joewalnes/reconnecting-websocket"
}
// MIT License:
//
// Copyright (c) 2010-2012, Joe Walnes
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
/**
* This behaves like a WebSocket in every way, except if it fails to connect,
* or it gets disconnected, it will repeatedly poll until it successfully connects
* again.
*
* It is API compatible, so when you have:
* ws = new WebSocket('ws://....');
* you can replace with:
* ws = new ReconnectingWebSocket('ws://....');
*
* The event stream will typically look like:
* onconnecting
* onopen
* onmessage
* onmessage
* onclose // lost connection
* onconnecting
* onopen // sometime later...
* onmessage
* onmessage
* etc...
*
* It is API compatible with the standard WebSocket API, apart from the following members:
*
* - `bufferedAmount`
* - `extensions`
* - `binaryType`
*
* Latest version: https://github.com/joewalnes/reconnecting-websocket/
* - Joe Walnes
*
* Syntax
* ======
* var socket = new ReconnectingWebSocket(url, protocols, options);
*
* Parameters
* ==========
* url - The url you are connecting to.
* protocols - Optional string or array of protocols.
* options - See below
*
* Options
* =======
* Options can either be passed upon instantiation or set after instantiation:
*
* var socket = new ReconnectingWebSocket(url, null, { debug: true, reconnectInterval: 4000 });
*
* or
*
* var socket = new ReconnectingWebSocket(url);
* socket.debug = true;
* socket.reconnectInterval = 4000;
*
* debug
* - Whether this instance should log debug messages. Accepts true or false. Default: false.
*
* automaticOpen
* - Whether or not the websocket should attempt to connect immediately upon instantiation. The socket can be manually opened or closed at any time using ws.open() and ws.close().
*
* reconnectInterval
* - The number of milliseconds to delay before attempting to reconnect. Accepts integer. Default: 1000.
*
* maxReconnectInterval
* - The maximum number of milliseconds to delay a reconnection attempt. Accepts integer. Default: 30000.
*
* reconnectDecay
* - The rate of increase of the reconnect delay. Allows reconnect attempts to back off when problems persist. Accepts integer or float. Default: 1.5.
*
* timeoutInterval
* - The maximum time in milliseconds to wait for a connection to succeed before closing and retrying. Accepts integer. Default: 2000.
*
*/
(function (global, factory) {
if (typeof define === 'function' && define.amd) {
define([], factory);
} else if (typeof module !== 'undefined' && module.exports){
module.exports = factory();
} else {
global.ReconnectingWebSocket = factory();
}
})(this, function () {
if (!('WebSocket' in window)) {
return;
}
function ReconnectingWebSocket(url, protocols, options) {
// Default settings
var settings = {
/** Whether this instance should log debug messages. */
debug: false,
/** Whether or not the websocket should attempt to connect immediately upon instantiation. */
automaticOpen: true,
/** The number of milliseconds to delay before attempting to reconnect. */
reconnectInterval: 1000,
/** The maximum number of milliseconds to delay a reconnection attempt. */
maxReconnectInterval: 30000,
/** The rate of increase of the reconnect delay. Allows reconnect attempts to back off when problems persist. */
reconnectDecay: 1.5,
/** The maximum time in milliseconds to wait for a connection to succeed before closing and retrying. */
timeoutInterval: 2000,
/** The maximum number of reconnection attempts to make. Unlimited if null. */
maxReconnectAttempts: null,
/** The binary type, possible values 'blob' or 'arraybuffer', default 'blob'. */
binaryType: 'blob'
}
if (!options) { options = {}; }
// Overwrite and define settings with options if they exist.
for (var key in settings) {
if (typeof options[key] !== 'undefined') {
this[key] = options[key];
} else {
this[key] = settings[key];
}
}
// These should be treated as read-only properties
/** The URL as resolved by the constructor. This is always an absolute URL. Read only. */
this.url = url;
/** The number of attempted reconnects since starting, or the last successful connection. Read only. */
this.reconnectAttempts = 0;
/**
* The current state of the connection.
* Can be one of: WebSocket.CONNECTING, WebSocket.OPEN, WebSocket.CLOSING, WebSocket.CLOSED
* Read only.
*/
this.readyState = WebSocket.CONNECTING;
/**
* A string indicating the name of the sub-protocol the server selected; this will be one of
* the strings specified in the protocols parameter when creating the WebSocket object.
* Read only.
*/
this.protocol = null;
// Private state variables
var self = this;
var ws;
var forcedClose = false;
var timedOut = false;
var eventTarget = document.createElement('div');
// Wire up "on*" properties as event handlers
eventTarget.addEventListener('open', function(event) { self.onopen(event); });
eventTarget.addEventListener('close', function(event) { self.onclose(event); });
eventTarget.addEventListener('connecting', function(event) { self.onconnecting(event); });
eventTarget.addEventListener('message', function(event) { self.onmessage(event); });
eventTarget.addEventListener('error', function(event) { self.onerror(event); });
// Expose the API required by EventTarget
this.addEventListener = eventTarget.addEventListener.bind(eventTarget);
this.removeEventListener = eventTarget.removeEventListener.bind(eventTarget);
this.dispatchEvent = eventTarget.dispatchEvent.bind(eventTarget);
/**
* This function generates an event that is compatible with standard
* compliant browsers and IE9 - IE11
*
* This will prevent the error:
* Object doesn't support this action
*
* http://stackoverflow.com/questions/19345392/why-arent-my-parameters-getting-passed-through-to-a-dispatched-event/19345563#19345563
* @param s String The name that the event should use
* @param args Object an optional object that the event will use
*/
function generateEvent(s, args) {
var evt = document.createEvent("CustomEvent");
evt.initCustomEvent(s, false, false, args);
return evt;
};
this.open = function (reconnectAttempt) {
ws = new WebSocket(self.url, protocols || []);
ws.binaryType = this.binaryType;
if (reconnectAttempt) {
if (this.maxReconnectAttempts && this.reconnectAttempts > this.maxReconnectAttempts) {
return;
}
} else {
eventTarget.dispatchEvent(generateEvent('connecting'));
this.reconnectAttempts = 0;
}
if (self.debug || ReconnectingWebSocket.debugAll) {
console.debug('ReconnectingWebSocket', 'attempt-connect', self.url);
}
var localWs = ws;
var timeout = setTimeout(function() {
if (self.debug || ReconnectingWebSocket.debugAll) {
console.debug('ReconnectingWebSocket', 'connection-timeout', self.url);
}
timedOut = true;
localWs.close();
timedOut = false;
}, self.timeoutInterval);
ws.onopen = function(event) {
clearTimeout(timeout);
if (self.debug || ReconnectingWebSocket.debugAll) {
console.debug('ReconnectingWebSocket', 'onopen', self.url);
}
self.protocol = ws.protocol;
self.readyState = WebSocket.OPEN;
self.reconnectAttempts = 0;
var e = generateEvent('open');
e.isReconnect = reconnectAttempt;
reconnectAttempt = false;
eventTarget.dispatchEvent(e);
};
ws.onclose = function(event) {
clearTimeout(timeout);
ws = null;
if (forcedClose) {
self.readyState = WebSocket.CLOSED;
eventTarget.dispatchEvent(generateEvent('close'));
} else {
self.readyState = WebSocket.CONNECTING;
var e = generateEvent('connecting');
e.code = event.code;
e.reason = event.reason;
e.wasClean = event.wasClean;
eventTarget.dispatchEvent(e);
if (!reconnectAttempt && !timedOut) {
if (self.debug || ReconnectingWebSocket.debugAll) {
console.debug('ReconnectingWebSocket', 'onclose', self.url);
}
eventTarget.dispatchEvent(generateEvent('close'));
}
var timeout = self.reconnectInterval * Math.pow(self.reconnectDecay, self.reconnectAttempts);
setTimeout(function() {
self.reconnectAttempts++;
self.open(true);
}, timeout > self.maxReconnectInterval ? self.maxReconnectInterval : timeout);
}
};
ws.onmessage = function(event) {
if (self.debug || ReconnectingWebSocket.debugAll) {
console.debug('ReconnectingWebSocket', 'onmessage', self.url, event.data);
}
var e = generateEvent('message');
e.data = event.data;
eventTarget.dispatchEvent(e);
};
ws.onerror = function(event) {
if (self.debug || ReconnectingWebSocket.debugAll) {
console.debug('ReconnectingWebSocket', 'onerror', self.url, event);
}
eventTarget.dispatchEvent(generateEvent('error'));
};
}
// Whether or not to create a websocket upon instantiation
if (this.automaticOpen == true) {
this.open(false);
}
/**
* Transmits data to the server over the WebSocket connection.
*
* @param data a text string, ArrayBuffer or Blob to send to the server.
*/
this.send = function(data) {
if (ws) {
if (self.debug || ReconnectingWebSocket.debugAll) {
console.debug('ReconnectingWebSocket', 'send', self.url, data);
}
return ws.send(data);
} else {
throw 'INVALID_STATE_ERR : Pausing to reconnect websocket';
}
};
/**
* Closes the WebSocket connection or connection attempt, if any.
* If the connection is already CLOSED, this method does nothing.
*/
this.close = function(code, reason) {
// Default CLOSE_NORMAL code
if (typeof code == 'undefined') {
code = 1000;
}
forcedClose = true;
if (ws) {
ws.close(code, reason);
}
};
/**
* Additional public API method to refresh the connection if still open (close, re-open).
* For example, if the app suspects bad data / missed heart beats, it can try to refresh.
*/
this.refresh = function() {
if (ws) {