1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 | (function (exports, io) {
|
9 |
|
10 | |
11 |
|
12 |
|
13 |
|
14 | exports.Socket = Socket;
|
15 |
|
16 | |
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 | function Socket (options) {
|
24 | this.options = {
|
25 | port: 80
|
26 | , secure: false
|
27 | , document: document
|
28 | , resource: 'socket.io'
|
29 | , transports: io.transports
|
30 | , 'connect timeout': 10000
|
31 | , 'try multiple transports': true
|
32 | , 'reconnect': true
|
33 | , 'reconnection delay': 500
|
34 | , 'reopen delay': 3000
|
35 | , 'max reconnection attempts': 10
|
36 | , 'sync disconnect on unload': true
|
37 | , 'auto connect': true
|
38 | };
|
39 |
|
40 | io.util.merge(this.options, options);
|
41 |
|
42 | this.connected = false;
|
43 | this.open = false;
|
44 | this.connecting = false;
|
45 | this.reconnecting = false;
|
46 | this.namespaces = {};
|
47 | this.buffer = [];
|
48 | this.doBuffer = false;
|
49 |
|
50 | if (this.options['sync disconnect on unload'] &&
|
51 | (!this.isXDomain() || io.util.ua.hasCORS)) {
|
52 | var self = this;
|
53 |
|
54 | io.util.on(window, 'beforeunload', function () {
|
55 | self.disconnectSync();
|
56 | }, false);
|
57 | }
|
58 |
|
59 | if (this.options['auto connect']) {
|
60 | this.connect();
|
61 | }
|
62 | };
|
63 |
|
64 | |
65 |
|
66 |
|
67 |
|
68 | io.util.mixin(Socket, io.EventEmitter);
|
69 |
|
70 | |
71 |
|
72 |
|
73 |
|
74 |
|
75 |
|
76 | Socket.prototype.of = function (name) {
|
77 | if (!this.namespaces[name]) {
|
78 | this.namespaces[name] = new io.SocketNamespace(this, name);
|
79 |
|
80 | if (name !== '') {
|
81 | this.namespaces[name].packet({ type: 'connect' });
|
82 | }
|
83 | }
|
84 |
|
85 | return this.namespaces[name];
|
86 | };
|
87 |
|
88 | |
89 |
|
90 |
|
91 |
|
92 |
|
93 |
|
94 | Socket.prototype.publish = function () {
|
95 | this.emit.apply(this, arguments);
|
96 |
|
97 | var nsp;
|
98 |
|
99 | for (var i in this.namespaces) {
|
100 | if (this.namespaces.hasOwnProperty(i)) {
|
101 | nsp = this.of(i);
|
102 | nsp.$emit.apply(nsp, arguments);
|
103 | }
|
104 | }
|
105 | };
|
106 |
|
107 | |
108 |
|
109 |
|
110 |
|
111 |
|
112 |
|
113 | function empty () { };
|
114 |
|
115 | Socket.prototype.handshake = function (fn) {
|
116 | var self = this
|
117 | , options = this.options;
|
118 |
|
119 | function complete (data) {
|
120 | if (data instanceof Error) {
|
121 | self.onError(data.message);
|
122 | } else {
|
123 | fn.apply(null, data.split(':'));
|
124 | }
|
125 | };
|
126 |
|
127 | var url = [
|
128 | 'http' + (options.secure ? 's' : '') + ':/'
|
129 | , options.host + ':' + options.port
|
130 | , this.options.resource
|
131 | , io.protocol
|
132 | , '?t=' + + new Date
|
133 | ].join('/');
|
134 |
|
135 | if (this.isXDomain()) {
|
136 | var insertAt = document.getElementsByTagName('script')[0]
|
137 | , script = document.createElement('SCRIPT');
|
138 |
|
139 | script.src = url + '&jsonp=' + io.j.length;
|
140 | insertAt.parentNode.insertBefore(script, insertAt);
|
141 |
|
142 | io.j.push(function (data) {
|
143 | complete(data);
|
144 | script.parentNode.removeChild(script);
|
145 | });
|
146 | } else {
|
147 | var xhr = io.util.request();
|
148 |
|
149 | xhr.open('GET', url);
|
150 | xhr.onreadystatechange = function () {
|
151 | if (xhr.readyState == 4) {
|
152 | xhr.onreadystatechange = empty;
|
153 |
|
154 | if (xhr.status == 200) {
|
155 | complete(xhr.responseText);
|
156 | } else {
|
157 | !self.reconnecting && self.onError(xhr.responseText);
|
158 | }
|
159 | }
|
160 | };
|
161 | xhr.send(null);
|
162 | }
|
163 | };
|
164 |
|
165 | |
166 |
|
167 |
|
168 |
|
169 |
|
170 |
|
171 | Socket.prototype.getTransport = function (override) {
|
172 | var transports = override || this.transports, match;
|
173 |
|
174 | for (var i = 0, transport; transport = transports[i]; i++) {
|
175 | if (io.Transport[transport]
|
176 | && io.Transport[transport].check(this)
|
177 | && (!this.isXDomain() || io.Transport[transport].xdomainCheck())) {
|
178 | return new io.Transport[transport](this, this.sessionid);
|
179 | }
|
180 | }
|
181 |
|
182 | return null;
|
183 | };
|
184 |
|
185 | |
186 |
|
187 |
|
188 |
|
189 |
|
190 |
|
191 |
|
192 |
|
193 | Socket.prototype.connect = function (fn) {
|
194 | if (this.connecting) {
|
195 | return this;
|
196 | }
|
197 |
|
198 | var self = this;
|
199 |
|
200 | this.handshake(function (sid, heartbeat, close, transports) {
|
201 | self.sessionid = sid;
|
202 | self.closeTimeout = close * 1000;
|
203 | self.heartbeatTimeout = heartbeat * 1000;
|
204 | self.transports = io.util.intersect(
|
205 | transports.split(',')
|
206 | , self.options.transports
|
207 | );
|
208 | self.transport = self.getTransport();
|
209 |
|
210 | if (!self.transport) {
|
211 | return;
|
212 | }
|
213 |
|
214 | self.connecting = true;
|
215 | self.publish('connecting', self.transport.name);
|
216 |
|
217 | self.transport.open();
|
218 |
|
219 | if (self.options.connectTimeout) {
|
220 | self.connectTimeoutTimer = setTimeout(function () {
|
221 | if (!self.connected) {
|
222 | if (self.options['try multiple transports']) {
|
223 | if (!self.remainingTransports) {
|
224 | self.remainingTransports = self.transports.slice(0);
|
225 | }
|
226 |
|
227 | var transports = self.remainingTransports;
|
228 |
|
229 | while (transports.length > 0 && transports.splice(0,1)[0] !=
|
230 | self.transport.name) {}
|
231 |
|
232 | if (transports.length) {
|
233 | self.transport = self.getTransport(transports);
|
234 | self.connect();
|
235 | }
|
236 | }
|
237 |
|
238 | if (!self.remainingTransports || self.remainingTransports.length == 0) {
|
239 | self.publish('connect_failed');
|
240 | }
|
241 | }
|
242 |
|
243 | if(self.remainingTransports && self.remainingTransports.length == 0) {
|
244 | delete self.remainingTransports;
|
245 | }
|
246 | }, self.options['connect timeout']);
|
247 | }
|
248 |
|
249 | if (fn && typeof fn == 'function') {
|
250 | self.once('connect', fn);
|
251 | }
|
252 | });
|
253 |
|
254 | return this;
|
255 | };
|
256 |
|
257 | |
258 |
|
259 |
|
260 |
|
261 |
|
262 |
|
263 |
|
264 |
|
265 | Socket.prototype.packet = function (data) {
|
266 | if (this.connected && !this.doBuffer) {
|
267 | this.transport.packet(data);
|
268 | } else {
|
269 | this.buffer.push(data);
|
270 | }
|
271 |
|
272 | return this;
|
273 | };
|
274 |
|
275 | |
276 |
|
277 |
|
278 |
|
279 |
|
280 |
|
281 | Socket.prototype.setBuffer = function (v) {
|
282 | this.doBuffer = v;
|
283 |
|
284 | if (!v && this.connected && this.buffer.length) {
|
285 | this.transport.payload(this.buffer);
|
286 | this.buffer = [];
|
287 | }
|
288 | };
|
289 |
|
290 | |
291 |
|
292 |
|
293 |
|
294 |
|
295 |
|
296 |
|
297 | Socket.prototype.disconnect = function () {
|
298 | if (this.connected) {
|
299 | if (this.open) {
|
300 | this.of('').packet({ type: 'disconnect' });
|
301 | }
|
302 |
|
303 |
|
304 | this.onDisconnect('booted');
|
305 | }
|
306 |
|
307 | return this;
|
308 | };
|
309 |
|
310 | |
311 |
|
312 |
|
313 |
|
314 |
|
315 |
|
316 | Socket.prototype.disconnectSync = function () {
|
317 |
|
318 | var xhr = io.util.request()
|
319 | , uri = this.resource + '/' + io.protocol + '/' + this.sessionid;
|
320 |
|
321 | xhr.open('GET', uri, true);
|
322 |
|
323 |
|
324 | this.onDisconnect('booted');
|
325 | };
|
326 |
|
327 | |
328 |
|
329 |
|
330 |
|
331 |
|
332 |
|
333 |
|
334 |
|
335 | Socket.prototype.isXDomain = function () {
|
336 | var locPort = window.location.port || 80;
|
337 | return this.options.host !== document.domain || this.options.port != locPort;
|
338 | };
|
339 |
|
340 | |
341 |
|
342 |
|
343 |
|
344 |
|
345 |
|
346 | Socket.prototype.onConnect = function () {
|
347 | this.connected = true;
|
348 | this.connecting = false;
|
349 | if (!this.doBuffer) {
|
350 |
|
351 | this.setBuffer(false);
|
352 | }
|
353 | this.emit('connect');
|
354 | };
|
355 |
|
356 | |
357 |
|
358 |
|
359 |
|
360 |
|
361 |
|
362 | Socket.prototype.onOpen = function () {
|
363 | this.open = true;
|
364 | };
|
365 |
|
366 | |
367 |
|
368 |
|
369 |
|
370 |
|
371 |
|
372 | Socket.prototype.onClose = function () {
|
373 | this.open = false;
|
374 | };
|
375 |
|
376 | |
377 |
|
378 |
|
379 |
|
380 |
|
381 |
|
382 | Socket.prototype.onPacket = function (packet) {
|
383 | this.of(packet.endpoint).onPacket(packet);
|
384 | };
|
385 |
|
386 | |
387 |
|
388 |
|
389 |
|
390 |
|
391 |
|
392 | Socket.prototype.onError = function (err) {
|
393 | if (err && err.advice) {
|
394 | if (err.advice === 'reconnect') {
|
395 | this.disconnect();
|
396 | this.reconnect();
|
397 | }
|
398 | }
|
399 |
|
400 | this.publish('error', err && err.reason ? err.reason : err);
|
401 | };
|
402 |
|
403 | |
404 |
|
405 |
|
406 |
|
407 |
|
408 |
|
409 | Socket.prototype.onDisconnect = function (reason) {
|
410 | var wasConnected = this.connected;
|
411 |
|
412 | this.connected = false;
|
413 | this.connecting = false;
|
414 | this.open = false;
|
415 |
|
416 | if (wasConnected) {
|
417 | this.transport.close();
|
418 | this.transport.clearTimeouts();
|
419 | this.publish('disconnect', reason);
|
420 |
|
421 | if ('booted' != reason && this.options.reconnect && !this.reconnecting) {
|
422 | this.reconnect();
|
423 | }
|
424 | }
|
425 | };
|
426 |
|
427 | |
428 |
|
429 |
|
430 |
|
431 |
|
432 |
|
433 | Socket.prototype.reconnect = function () {
|
434 | this.reconnecting = true;
|
435 | this.reconnectionAttempts = 0;
|
436 | this.reconnectionDelay = this.options['reconnection delay'];
|
437 |
|
438 | var self = this
|
439 | , maxAttempts = this.options['max reconnection attempts']
|
440 | , tryMultiple = this.options['try multiple transports']
|
441 |
|
442 | function reset () {
|
443 | if (self.connected) {
|
444 | self.publish('reconnect', self.transport.name, self.reconnectionAttempts);
|
445 | }
|
446 |
|
447 | self.removeListener('connect_failed', maybeReconnect);
|
448 | self.removeListener('connect', maybeReconnect);
|
449 |
|
450 | self.reconnecting = false;
|
451 |
|
452 | delete self.reconnectionAttempts;
|
453 | delete self.reconnectionDelay;
|
454 | delete self.reconnectionTimer;
|
455 | delete self.redoTransports;
|
456 |
|
457 | self.options['try multiple transports'] = tryMultiple;
|
458 | };
|
459 |
|
460 | function maybeReconnect () {
|
461 | if (!self.reconnecting) {
|
462 | return;
|
463 | }
|
464 |
|
465 | if (self.connected) {
|
466 | return reset();
|
467 | };
|
468 |
|
469 | if (self.connecting && self.reconnecting) {
|
470 | return self.reconnectionTimer = setTimeout(maybeReconnect, 1000);
|
471 | }
|
472 |
|
473 | if (self.reconnectionAttempts++ >= maxAttempts) {
|
474 | if (!self.redoTransports) {
|
475 | self.on('connect_failed', maybeReconnect);
|
476 | self.options['try multiple transports'] = true;
|
477 | self.transport = self.getTransport();
|
478 | self.redoTransports = true;
|
479 | self.connect();
|
480 | } else {
|
481 | self.publish('reconnect_failed');
|
482 | reset();
|
483 | }
|
484 | } else {
|
485 | self.reconnectionDelay *= 2;
|
486 | self.connect();
|
487 | self.publish('reconnecting', self.reconnectionDelay, self.reconnectionAttempts);
|
488 | self.reconnectionTimer = setTimeout(maybeReconnect, self.reconnectionDelay);
|
489 | }
|
490 | };
|
491 |
|
492 | this.options['try multiple transports'] = false;
|
493 | this.reconnectionTimer = setTimeout(maybeReconnect, this.reconnectionDelay);
|
494 |
|
495 | this.on('connect', maybeReconnect);
|
496 | };
|
497 |
|
498 | })(
|
499 | 'undefined' != typeof io ? io : module.exports
|
500 | , 'undefined' != typeof io ? io : module.parent.exports
|
501 | );
|