-
Notifications
You must be signed in to change notification settings - Fork 0
/
jquery.gracefulWebSocket.js
165 lines (144 loc) · 4.88 KB
/
jquery.gracefulWebSocket.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/**
* WebSocket with graceful degradation - jQuery plugin
* @author David Lindkvist
* @version 0.1
*
* Returns an object implementing the WebSocket API.
*
* If browser supports WebSockets a native WebSocket instance is returned.
* If not, a simulated half-duplex implementation is returned which uses polling
* over HTTP to retrieve new messages
*
* OPTIONS
* -----------------------------------------------------------------------------
*
* {Number} fallbackOpenDelay number of ms to delay simulated open
* event for fallback
* {Number} fallbackPollInterval number of ms between requests for
* fallback polling
* {Object} fallbackPollParams optional params to pass with each poll
* requests
*
* EXAMPLES
* -----------------------------------------------------------------------------
*
* var websocket = $.gracefulWebSocket("ws://127.0.0.1:8080/");
*
* var websocket = $.gracefulWebSocket({
* fallbackPollParams: {
* "latestMessageID": function () {
* return latestMessageID;
* }
* }
* });
*
*/
(function ($) {
$.extend({
gracefulWebSocket: function (url, options) {
// Default properties
this.defaults = {
keepAlive: false, // not implemented - should ping server to keep socket open
autoReconnect: false, // not implemented - should try to reconnect silently if socket is closed
fallback: true, // not implemented - always use HTTP fallback if native browser support is missing
fallbackSendURL: url.replace('ws:', 'http:').replace('wss:', 'https:'),
fallbackSendMethod: 'POST',
fallbackPollURL: url.replace('ws:', 'http:').replace('wss:', 'https:'),
fallbackPollMethod: 'GET',
fallbackOpenDelay: 100, // number of ms to delay simulated open event
fallbackPollInterval: 3000, // number of ms between poll requests
fallbackPollParams: {} // optional params to pass with poll requests
};
// Override defaults with user properties
var opts = $.extend({}, this.defaults, options);
/**
* Creates a fallback object implementing the WebSocket interface
*/
function FallbackSocket() {
// WebSocket interface constants
const CONNECTING = 0;
const OPEN = 1;
const CLOSING = 2;
const CLOSED = 3;
var pollInterval;
var openTimout;
// create WebSocket object
var fws = {
// ready state
readyState: CONNECTING,
bufferedAmount: 0,
send: function (data) {
var success = true;
$.ajax({
async: false, // send synchronously
type: opts.fallbackSendMethod,
url: opts.fallbackSendURL + '?' + $.param( getFallbackParams() ),
data: data,
dataType: 'text',
contentType : "application/x-www-form-urlencoded; charset=utf-8",
success: pollSuccess,
error: function (xhr) {
success = false;
$(fws).triggerHandler('error');
}
});
return success;
},
close: function () {
clearTimeout(openTimout);
clearInterval(pollInterval);
this.readyState = CLOSED;
$(fws).triggerHandler('close');
},
onopen: function () {},
onmessage: function () {},
onerror: function () {},
onclose: function () {},
previousRequest: null,
currentRequest: null
};
function getFallbackParams() {
// update timestamp of previous and current poll request
fws.previousRequest = fws.currentRequest;
fws.currentRequest = new Date().getTime();
// extend default params with plugin options
return $.extend({"previousRequest": fws.previousRequest, "currentRequest": fws.currentRequest}, opts.fallbackPollParams);
}
/**
* @param {Object} data
*/
function pollSuccess(data) {
// trigger onmessage
var messageEvent = {"data" : data};
fws.onmessage(messageEvent);
}
function poll() {
$.ajax({
type: opts.fallbackPollMethod,
url: opts.fallbackPollURL,
dataType: 'text',
data: getFallbackParams(),
success: pollSuccess,
error: function (xhr) {
$(fws).triggerHandler('error');
}
});
}
// simulate open event and start polling
openTimout = setTimeout(function () {
fws.readyState = OPEN;
//fws.currentRequest = new Date().getTime();
$(fws).triggerHandler('open');
poll();
pollInterval = setInterval(poll, opts.fallbackPollInterval);
}, opts.fallbackOpenDelay);
// return socket impl
return fws;
}
// create a new websocket or fallback
var ws = window.WebSocket ? new WebSocket(url) : new FallbackSocket();
$(window).unload(function () { ws.close(); ws = null });
return ws;
}
});
})(jQuery);