/js/skynet.bundle.js
JavaScript | 8844 lines | 5505 code | 1357 blank | 1982 comment | 1405 complexity | a72b3b54dea417156436f69a63e3f0ce MD5 | raw file
Large files files are truncated, but you can click here to view the full file
1!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.skynet=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){ 2var Connection = _dereq_('./lib/Connection'); 3 4module.exports = { 5 createConnection: function (opt){ 6 return new Connection(opt); 7 } 8} 9 10 11},{"./lib/Connection":2}],2:[function(_dereq_,module,exports){ 12'use strict'; 13var util = _dereq_('util'); 14var EventEmitter = _dereq_('events').EventEmitter; 15var socketIoClient = _dereq_("socket.io-client"); 16 17var DEFAULT_TIMEOUT = 10000; 18 19function Connection(opt){ 20 21 EventEmitter.call(this); 22 23 this._callbackHandlers = {}; 24 this._ackId = 0; 25 26 this.options = opt || {}; 27 this.options.options = this.options.options || {}; 28 29 this.options.options.transports = this.options.options.transports || ['websocket']; 30 this.options.forceNew = (opt.forceNew != null) ? opt.forceNew : false; 31 32 this.options.server = this.options.server || 'ws://skynet.im'; 33 this.options.port = this.options.port || 80; 34 35 36 37 // if(this.options.server && this.options.port){ 38 if(this.options.server.indexOf("http") === -1 && this.options.server.indexOf("ws") === -1 && this.options.server.indexOf("wss") === -1 ){ 39 this.options.server = "ws://" + this.options.server; 40 } 41 // network = this.options.server + ":" + this.options.port; 42 // } 43 var network = this.options.server + ':' + this.options.port; 44 console.log('trying', network); 45 this.socket = socketIoClient(network, this.options.options); // || "ws://skynet.im"); 46 47 // this.socket = io.connect(this.options.server || "http://skynet.im", { 48 // port: this.options.port || 80, 49 // forceNew: this.options.forceNew, 50 // multiplex: !this.options.forceNew, 51 // 'force new connection': this.options.forceNew 52 // }); 53 54 this.options.protocol = "websocket"; 55 this.setup(); 56} 57 58util.inherits(Connection, EventEmitter); 59 60Connection.prototype.setup = function(){ 61 var self = this; 62 this.socket.once('connect', function(){ 63 this.emit('connect'); 64 65 this.socket.on('messageAck', function(data){ 66 if(self._callbackHandlers[data.ack]){ 67 try{ 68 self._callbackHandlers[data.ack](data.payload); 69 delete self._callbackHandlers[data.ack]; 70 } 71 catch(err){ 72 console.log('error resolving callback', err); 73 } 74 } 75 }); 76 77 this.socket.on('message', function(data){ 78 self._handleAckRequest('message', data); 79 }); 80 81 //this.emit.bind(this, 'message')); 82 this.socket.on('config', function(data){ 83 self._handleAckRequest('config', data); 84 }); 85 86 this.socket.on('disconnect', this.emit.bind(this, 'disconnect')); 87 this.socket.on('identify', this.identify.bind(this)); 88 this.socket.on('ready', this.emit.bind(this, 'ready')); 89 this.socket.on('notReady', this.emit.bind(this, 'notReady')); 90 this.socket.on('tb', this.emit.bind(this, 'tb')); 91 this.socket.on('unboundSocket', this.emit.bind(this, 'unboundSocket')); 92 93 }.bind(this)); 94 95 return this; 96}; 97 98//Provide callback when message with ack requests comes in from another client 99Connection.prototype._handleAckRequest = function(topic, data){ 100 var self = this; 101 if(data){ 102 if(data.ack && data.fromUuid){ 103 //TODO clean these up if not used 104 self.emit(topic, data, function(response){ 105 self.socket.emit('messageAck', { 106 devices: data.fromUuid, 107 ack: data.ack, 108 payload: response 109 }); 110 }); 111 }else{ 112 self.emit(topic, data); 113 } 114 } 115}; 116 117//Allow for making RPC requests to other clients 118Connection.prototype._emitWithAck = function(topic, data, fn){ 119 var self = this; 120 if(data){ 121 if(fn){ 122 var ack = ++this._ackId; 123 data.ack = ack; 124 self._callbackHandlers[ack] = fn; 125 var timeout = data.timeout || DEFAULT_TIMEOUT; 126 //remove handlers 127 setTimeout(function(){ 128 if(self._callbackHandlers[ack]){ 129 self._callbackHandlers[ack]({error: 'timeout ' + timeout}); 130 delete self._callbackHandlers[ack]; 131 } 132 }, timeout); 133 } 134 //console.log('emitting ack', topic, data); 135 this.socket.emit(topic, data); 136 } 137 return this; 138}; 139 140 141Connection.prototype.identify = function(){ 142 this.socket.emit('identity', { 143 uuid: this.options.uuid, 144 token: this.options.token, 145 protocol: this.options.protocol 146 }); 147 return this; 148}; 149 150Connection.prototype.message = function(data, fn) { 151 return this._emitWithAck('message', data, fn); 152}; 153 154Connection.prototype.config = function(data, fn) { 155 return this._emitWithAck('gatewayConfig', data, fn); 156}; 157 158Connection.prototype.gatewayConfig = function(data, fn) { 159 return this._emitWithAck('gatewayConfig', data, fn); 160}; 161 162// send plain text 163Connection.prototype.send = function(text) { 164 165 if(text){ 166 text = text.toString(); 167 this.socket.send(text); 168 } 169 170 return this; 171}; 172 173Connection.prototype.update = function(data, fn) { 174 this.socket.emit('update', data, fn); 175 return this; 176}; 177 178Connection.prototype.register = function(data, fn) { 179 this.socket.emit('register', data, fn); 180 return this; 181}; 182 183Connection.prototype.unregister = function(data, fn) { 184 this.socket.emit('unregister', data, fn); 185 return this; 186}; 187 188Connection.prototype.whoami = function(data, fn) { 189 this.socket.emit('whoami', data, fn); 190 return this; 191}; 192 193Connection.prototype.devices = function(data, fn) { 194 this.socket.emit('devices', data, fn); 195 return this; 196}; 197 198Connection.prototype.mydevices = function(data, fn) { 199 this.socket.emit('mydevices', data, fn); 200 return this; 201}; 202 203Connection.prototype.status = function(data) { 204 this.socket.emit('status', data); 205 return this; 206}; 207 208Connection.prototype.subscribe = function(data, fn) { 209 if(typeof data === 'string'){ 210 data = {uuid: data}; 211 } 212 this.socket.emit('subscribe', data, fn); 213 return this; 214}; 215 216Connection.prototype.unsubscribe = function(data, fn) { 217 if(typeof data === 'string'){ 218 data = {uuid: data}; 219 } 220 this.socket.emit('unsubscribe', data, fn); 221 return this; 222}; 223 224Connection.prototype.authenticate = function(data, fn) { 225 this.socket.emit('authenticate', data, fn); 226 return this; 227}; 228 229Connection.prototype.events = function(data, fn) { 230 this.socket.emit('events', data, fn); 231 return this; 232}; 233 234Connection.prototype.data = function(data, fn) { 235 this.socket.emit('data', data, fn); 236 return this; 237}; 238 239Connection.prototype.getdata = function(data, fn) { 240 this.socket.emit('getdata', data, fn); 241 return this; 242}; 243 244Connection.prototype.localdevices = function(fn) { 245 this.socket.emit('localdevices', {}, fn); 246 return this; 247}; 248 249Connection.prototype.textBroadcast = function(data) { 250 if(typeof data !== 'string'){ 251 data = String(data); 252 } 253 this.socket.emit('tb', data); 254 return this; 255}; 256 257Connection.prototype.directText = function(data) { 258 if(typeof data === 'object' && data.payload && typeof data.payload === 'string' && data.devices){ 259 this.socket.emit('directText', data); 260 } 261 else{ 262 console.log('directText requires an object with a string payload property, and a devices property'); 263 } 264 265 return this; 266}; 267 268Connection.prototype.subscribeText = function(data, fn) { 269 if(typeof data === 'string'){ 270 data = {uuid: data}; 271 } 272 this.socket.emit('subscribeText', data, fn); 273 return this; 274}; 275 276Connection.prototype.unsubscribeText = function(data, fn) { 277 if(typeof data === 'string'){ 278 data = {uuid: data}; 279 } 280 this.socket.emit('unsubscribeText', data, fn); 281 return this; 282}; 283 284 285Connection.prototype.close = function(){ 286 return this; 287}; 288 289 290 291module.exports = Connection; 292 293},{"events":49,"socket.io-client":3,"util":53}],3:[function(_dereq_,module,exports){ 294 295module.exports = _dereq_('./lib/'); 296 297},{"./lib/":4}],4:[function(_dereq_,module,exports){ 298 299/** 300 * Module dependencies. 301 */ 302 303var url = _dereq_('./url'); 304var parser = _dereq_('socket.io-parser'); 305var Manager = _dereq_('./manager'); 306var debug = _dereq_('debug')('socket.io-client'); 307 308/** 309 * Module exports. 310 */ 311 312module.exports = exports = lookup; 313 314/** 315 * Managers cache. 316 */ 317 318var cache = exports.managers = {}; 319 320/** 321 * Looks up an existing `Manager` for multiplexing. 322 * If the user summons: 323 * 324 * `io('http://localhost/a');` 325 * `io('http://localhost/b');` 326 * 327 * We reuse the existing instance based on same scheme/port/host, 328 * and we initialize sockets for each namespace. 329 * 330 * @api public 331 */ 332 333function lookup(uri, opts) { 334 if (typeof uri == 'object') { 335 opts = uri; 336 uri = undefined; 337 } 338 339 opts = opts || {}; 340 341 var parsed = url(uri); 342 var source = parsed.source; 343 var id = parsed.id; 344 var io; 345 346 if (opts.forceNew || opts['force new connection'] || false === opts.multiplex) { 347 debug('ignoring socket cache for %s', source); 348 io = Manager(source, opts); 349 } else { 350 if (!cache[id]) { 351 debug('new io instance for %s', source); 352 cache[id] = Manager(source, opts); 353 } 354 io = cache[id]; 355 } 356 357 return io.socket(parsed.path); 358} 359 360/** 361 * Protocol version. 362 * 363 * @api public 364 */ 365 366exports.protocol = parser.protocol; 367 368/** 369 * `connect`. 370 * 371 * @param {String} uri 372 * @api public 373 */ 374 375exports.connect = lookup; 376 377/** 378 * Expose constructors for standalone build. 379 * 380 * @api public 381 */ 382 383exports.Manager = _dereq_('./manager'); 384exports.Socket = _dereq_('./socket'); 385 386},{"./manager":5,"./socket":7,"./url":8,"debug":11,"socket.io-parser":41}],5:[function(_dereq_,module,exports){ 387 388/** 389 * Module dependencies. 390 */ 391 392var url = _dereq_('./url'); 393var eio = _dereq_('engine.io-client'); 394var Socket = _dereq_('./socket'); 395var Emitter = _dereq_('component-emitter'); 396var parser = _dereq_('socket.io-parser'); 397var on = _dereq_('./on'); 398var bind = _dereq_('component-bind'); 399var object = _dereq_('object-component'); 400var debug = _dereq_('debug')('socket.io-client:manager'); 401 402/** 403 * Module exports 404 */ 405 406module.exports = Manager; 407 408/** 409 * `Manager` constructor. 410 * 411 * @param {String} engine instance or engine uri/opts 412 * @param {Object} options 413 * @api public 414 */ 415 416function Manager(uri, opts){ 417 if (!(this instanceof Manager)) return new Manager(uri, opts); 418 if (uri && ('object' == typeof uri)) { 419 opts = uri; 420 uri = undefined; 421 } 422 opts = opts || {}; 423 424 opts.path = opts.path || '/socket.io'; 425 this.nsps = {}; 426 this.subs = []; 427 this.opts = opts; 428 this.reconnection(opts.reconnection !== false); 429 this.reconnectionAttempts(opts.reconnectionAttempts || Infinity); 430 this.reconnectionDelay(opts.reconnectionDelay || 1000); 431 this.reconnectionDelayMax(opts.reconnectionDelayMax || 5000); 432 this.timeout(null == opts.timeout ? 20000 : opts.timeout); 433 this.readyState = 'closed'; 434 this.uri = uri; 435 this.connected = 0; 436 this.attempts = 0; 437 this.encoding = false; 438 this.packetBuffer = []; 439 this.encoder = new parser.Encoder(); 440 this.decoder = new parser.Decoder(); 441 this.open(); 442} 443 444/** 445 * Propagate given event to sockets and emit on `this` 446 * 447 * @api private 448 */ 449 450Manager.prototype.emitAll = function() { 451 this.emit.apply(this, arguments); 452 for (var nsp in this.nsps) { 453 this.nsps[nsp].emit.apply(this.nsps[nsp], arguments); 454 } 455}; 456 457/** 458 * Mix in `Emitter`. 459 */ 460 461Emitter(Manager.prototype); 462 463/** 464 * Sets the `reconnection` config. 465 * 466 * @param {Boolean} true/false if it should automatically reconnect 467 * @return {Manager} self or value 468 * @api public 469 */ 470 471Manager.prototype.reconnection = function(v){ 472 if (!arguments.length) return this._reconnection; 473 this._reconnection = !!v; 474 return this; 475}; 476 477/** 478 * Sets the reconnection attempts config. 479 * 480 * @param {Number} max reconnection attempts before giving up 481 * @return {Manager} self or value 482 * @api public 483 */ 484 485Manager.prototype.reconnectionAttempts = function(v){ 486 if (!arguments.length) return this._reconnectionAttempts; 487 this._reconnectionAttempts = v; 488 return this; 489}; 490 491/** 492 * Sets the delay between reconnections. 493 * 494 * @param {Number} delay 495 * @return {Manager} self or value 496 * @api public 497 */ 498 499Manager.prototype.reconnectionDelay = function(v){ 500 if (!arguments.length) return this._reconnectionDelay; 501 this._reconnectionDelay = v; 502 return this; 503}; 504 505/** 506 * Sets the maximum delay between reconnections. 507 * 508 * @param {Number} delay 509 * @return {Manager} self or value 510 * @api public 511 */ 512 513Manager.prototype.reconnectionDelayMax = function(v){ 514 if (!arguments.length) return this._reconnectionDelayMax; 515 this._reconnectionDelayMax = v; 516 return this; 517}; 518 519/** 520 * Sets the connection timeout. `false` to disable 521 * 522 * @return {Manager} self or value 523 * @api public 524 */ 525 526Manager.prototype.timeout = function(v){ 527 if (!arguments.length) return this._timeout; 528 this._timeout = v; 529 return this; 530}; 531 532/** 533 * Starts trying to reconnect if reconnection is enabled and we have not 534 * started reconnecting yet 535 * 536 * @api private 537 */ 538 539Manager.prototype.maybeReconnectOnOpen = function() { 540 if (!this.openReconnect && !this.reconnecting && this._reconnection) { 541 // keeps reconnection from firing twice for the same reconnection loop 542 this.openReconnect = true; 543 this.reconnect(); 544 } 545}; 546 547 548/** 549 * Sets the current transport `socket`. 550 * 551 * @param {Function} optional, callback 552 * @return {Manager} self 553 * @api public 554 */ 555 556Manager.prototype.open = 557Manager.prototype.connect = function(fn){ 558 debug('readyState %s', this.readyState); 559 if (~this.readyState.indexOf('open')) return this; 560 561 debug('opening %s', this.uri); 562 this.engine = eio(this.uri, this.opts); 563 var socket = this.engine; 564 var self = this; 565 this.readyState = 'opening'; 566 567 // emit `open` 568 var openSub = on(socket, 'open', function() { 569 self.onopen(); 570 fn && fn(); 571 }); 572 573 // emit `connect_error` 574 var errorSub = on(socket, 'error', function(data){ 575 debug('connect_error'); 576 self.cleanup(); 577 self.readyState = 'closed'; 578 self.emitAll('connect_error', data); 579 if (fn) { 580 var err = new Error('Connection error'); 581 err.data = data; 582 fn(err); 583 } 584 585 self.maybeReconnectOnOpen(); 586 }); 587 588 // emit `connect_timeout` 589 if (false !== this._timeout) { 590 var timeout = this._timeout; 591 debug('connect attempt will timeout after %d', timeout); 592 593 // set timer 594 var timer = setTimeout(function(){ 595 debug('connect attempt timed out after %d', timeout); 596 openSub.destroy(); 597 socket.close(); 598 socket.emit('error', 'timeout'); 599 self.emitAll('connect_timeout', timeout); 600 }, timeout); 601 602 this.subs.push({ 603 destroy: function(){ 604 clearTimeout(timer); 605 } 606 }); 607 } 608 609 this.subs.push(openSub); 610 this.subs.push(errorSub); 611 612 return this; 613}; 614 615/** 616 * Called upon transport open. 617 * 618 * @api private 619 */ 620 621Manager.prototype.onopen = function(){ 622 debug('open'); 623 624 // clear old subs 625 this.cleanup(); 626 627 // mark as open 628 this.readyState = 'open'; 629 this.emit('open'); 630 631 // add new subs 632 var socket = this.engine; 633 this.subs.push(on(socket, 'data', bind(this, 'ondata'))); 634 this.subs.push(on(this.decoder, 'decoded', bind(this, 'ondecoded'))); 635 this.subs.push(on(socket, 'error', bind(this, 'onerror'))); 636 this.subs.push(on(socket, 'close', bind(this, 'onclose'))); 637}; 638 639/** 640 * Called with data. 641 * 642 * @api private 643 */ 644 645Manager.prototype.ondata = function(data){ 646 this.decoder.add(data); 647}; 648 649/** 650 * Called when parser fully decodes a packet. 651 * 652 * @api private 653 */ 654 655Manager.prototype.ondecoded = function(packet) { 656 this.emit('packet', packet); 657}; 658 659/** 660 * Called upon socket error. 661 * 662 * @api private 663 */ 664 665Manager.prototype.onerror = function(err){ 666 debug('error', err); 667 this.emitAll('error', err); 668}; 669 670/** 671 * Creates a new socket for the given `nsp`. 672 * 673 * @return {Socket} 674 * @api public 675 */ 676 677Manager.prototype.socket = function(nsp){ 678 var socket = this.nsps[nsp]; 679 if (!socket) { 680 socket = new Socket(this, nsp); 681 this.nsps[nsp] = socket; 682 var self = this; 683 socket.on('connect', function(){ 684 self.connected++; 685 }); 686 } 687 return socket; 688}; 689 690/** 691 * Called upon a socket close. 692 * 693 * @param {Socket} socket 694 */ 695 696Manager.prototype.destroy = function(socket){ 697 --this.connected || this.close(); 698}; 699 700/** 701 * Writes a packet. 702 * 703 * @param {Object} packet 704 * @api private 705 */ 706 707Manager.prototype.packet = function(packet){ 708 debug('writing packet %j', packet); 709 var self = this; 710 711 if (!self.encoding) { 712 // encode, then write to engine with result 713 self.encoding = true; 714 this.encoder.encode(packet, function(encodedPackets) { 715 for (var i = 0; i < encodedPackets.length; i++) { 716 self.engine.write(encodedPackets[i]); 717 } 718 self.encoding = false; 719 self.processPacketQueue(); 720 }); 721 } else { // add packet to the queue 722 self.packetBuffer.push(packet); 723 } 724}; 725 726/** 727 * If packet buffer is non-empty, begins encoding the 728 * next packet in line. 729 * 730 * @api private 731 */ 732 733Manager.prototype.processPacketQueue = function() { 734 if (this.packetBuffer.length > 0 && !this.encoding) { 735 var pack = this.packetBuffer.shift(); 736 this.packet(pack); 737 } 738}; 739 740/** 741 * Clean up transport subscriptions and packet buffer. 742 * 743 * @api private 744 */ 745 746Manager.prototype.cleanup = function(){ 747 var sub; 748 while (sub = this.subs.shift()) sub.destroy(); 749 750 this.packetBuffer = []; 751 this.encoding = false; 752 753 this.decoder.destroy(); 754}; 755 756/** 757 * Close the current socket. 758 * 759 * @api private 760 */ 761 762Manager.prototype.close = 763Manager.prototype.disconnect = function(){ 764 this.skipReconnect = true; 765 this.engine.close(); 766}; 767 768/** 769 * Called upon engine close. 770 * 771 * @api private 772 */ 773 774Manager.prototype.onclose = function(reason){ 775 debug('close'); 776 this.cleanup(); 777 this.readyState = 'closed'; 778 this.emit('close', reason); 779 if (this._reconnection && !this.skipReconnect) { 780 this.reconnect(); 781 } 782}; 783 784/** 785 * Attempt a reconnection. 786 * 787 * @api private 788 */ 789 790Manager.prototype.reconnect = function(){ 791 if (this.reconnecting) return this; 792 793 var self = this; 794 this.attempts++; 795 796 if (this.attempts > this._reconnectionAttempts) { 797 debug('reconnect failed'); 798 this.emitAll('reconnect_failed'); 799 this.reconnecting = false; 800 } else { 801 var delay = this.attempts * this.reconnectionDelay(); 802 delay = Math.min(delay, this.reconnectionDelayMax()); 803 debug('will wait %dms before reconnect attempt', delay); 804 805 this.reconnecting = true; 806 var timer = setTimeout(function(){ 807 debug('attempting reconnect'); 808 self.emitAll('reconnect_attempt', self.attempts); 809 self.emitAll('reconnecting', self.attempts); 810 self.open(function(err){ 811 if (err) { 812 debug('reconnect attempt error'); 813 self.reconnecting = false; 814 self.reconnect(); 815 self.emitAll('reconnect_error', err.data); 816 } else { 817 debug('reconnect success'); 818 self.onreconnect(); 819 } 820 }); 821 }, delay); 822 823 this.subs.push({ 824 destroy: function(){ 825 clearTimeout(timer); 826 } 827 }); 828 } 829}; 830 831/** 832 * Called upon successful reconnect. 833 * 834 * @api private 835 */ 836 837Manager.prototype.onreconnect = function(){ 838 var attempt = this.attempts; 839 this.attempts = 0; 840 this.reconnecting = false; 841 this.emitAll('reconnect', attempt); 842}; 843 844},{"./on":6,"./socket":7,"./url":8,"component-bind":9,"component-emitter":10,"debug":11,"engine.io-client":12,"object-component":38,"socket.io-parser":41}],6:[function(_dereq_,module,exports){ 845 846/** 847 * Module exports. 848 */ 849 850module.exports = on; 851 852/** 853 * Helper for subscriptions. 854 * 855 * @param {Object|EventEmitter} obj with `Emitter` mixin or `EventEmitter` 856 * @param {String} event name 857 * @param {Function} callback 858 * @api public 859 */ 860 861function on(obj, ev, fn) { 862 obj.on(ev, fn); 863 return { 864 destroy: function(){ 865 obj.removeListener(ev, fn); 866 } 867 }; 868} 869 870},{}],7:[function(_dereq_,module,exports){ 871 872/** 873 * Module dependencies. 874 */ 875 876var parser = _dereq_('socket.io-parser'); 877var Emitter = _dereq_('component-emitter'); 878var toArray = _dereq_('to-array'); 879var on = _dereq_('./on'); 880var bind = _dereq_('component-bind'); 881var debug = _dereq_('debug')('socket.io-client:socket'); 882var hasBin = _dereq_('has-binary-data'); 883var indexOf = _dereq_('indexof'); 884 885/** 886 * Module exports. 887 */ 888 889module.exports = exports = Socket; 890 891/** 892 * Internal events (blacklisted). 893 * These events can't be emitted by the user. 894 * 895 * @api private 896 */ 897 898var events = { 899 connect: 1, 900 connect_error: 1, 901 connect_timeout: 1, 902 disconnect: 1, 903 error: 1, 904 reconnect: 1, 905 reconnect_attempt: 1, 906 reconnect_failed: 1, 907 reconnect_error: 1, 908 reconnecting: 1 909}; 910 911/** 912 * Shortcut to `Emitter#emit`. 913 */ 914 915var emit = Emitter.prototype.emit; 916 917/** 918 * `Socket` constructor. 919 * 920 * @api public 921 */ 922 923function Socket(io, nsp){ 924 this.io = io; 925 this.nsp = nsp; 926 this.json = this; // compat 927 this.ids = 0; 928 this.acks = {}; 929 this.open(); 930 this.receiveBuffer = []; 931 this.sendBuffer = []; 932 this.connected = false; 933 this.disconnected = true; 934 this.subEvents(); 935} 936 937/** 938 * Mix in `Emitter`. 939 */ 940 941Emitter(Socket.prototype); 942 943/** 944 * Subscribe to open, close and packet events 945 * 946 * @api private 947 */ 948 949Socket.prototype.subEvents = function() { 950 var io = this.io; 951 this.subs = [ 952 on(io, 'open', bind(this, 'onopen')), 953 on(io, 'packet', bind(this, 'onpacket')), 954 on(io, 'close', bind(this, 'onclose')) 955 ]; 956}; 957 958/** 959 * Called upon engine `open`. 960 * 961 * @api private 962 */ 963 964Socket.prototype.open = 965Socket.prototype.connect = function(){ 966 if (this.connected) return this; 967 968 this.io.open(); // ensure open 969 if ('open' == this.io.readyState) this.onopen(); 970 return this; 971}; 972 973/** 974 * Sends a `message` event. 975 * 976 * @return {Socket} self 977 * @api public 978 */ 979 980Socket.prototype.send = function(){ 981 var args = toArray(arguments); 982 args.unshift('message'); 983 this.emit.apply(this, args); 984 return this; 985}; 986 987/** 988 * Override `emit`. 989 * If the event is in `events`, it's emitted normally. 990 * 991 * @param {String} event name 992 * @return {Socket} self 993 * @api public 994 */ 995 996Socket.prototype.emit = function(ev){ 997 if (events.hasOwnProperty(ev)) { 998 emit.apply(this, arguments); 999 return this; 1000 } 1001 1002 var args = toArray(arguments); 1003 var parserType = parser.EVENT; // default 1004 if (hasBin(args)) { parserType = parser.BINARY_EVENT; } // binary 1005 var packet = { type: parserType, data: args }; 1006 1007 // event ack callback 1008 if ('function' == typeof args[args.length - 1]) { 1009 debug('emitting packet with ack id %d', this.ids); 1010 this.acks[this.ids] = args.pop(); 1011 packet.id = this.ids++; 1012 } 1013 1014 if (this.connected) { 1015 this.packet(packet); 1016 } else { 1017 this.sendBuffer.push(packet); 1018 } 1019 1020 return this; 1021}; 1022 1023/** 1024 * Sends a packet. 1025 * 1026 * @param {Object} packet 1027 * @api private 1028 */ 1029 1030Socket.prototype.packet = function(packet){ 1031 packet.nsp = this.nsp; 1032 this.io.packet(packet); 1033}; 1034 1035/** 1036 * "Opens" the socket. 1037 * 1038 * @api private 1039 */ 1040 1041Socket.prototype.onopen = function(){ 1042 debug('transport is open - connecting'); 1043 1044 // write connect packet if necessary 1045 if ('/' != this.nsp) { 1046 this.packet({ type: parser.CONNECT }); 1047 } 1048}; 1049 1050/** 1051 * Called upon engine `close`. 1052 * 1053 * @param {String} reason 1054 * @api private 1055 */ 1056 1057Socket.prototype.onclose = function(reason){ 1058 debug('close (%s)', reason); 1059 this.connected = false; 1060 this.disconnected = true; 1061 this.emit('disconnect', reason); 1062}; 1063 1064/** 1065 * Called with socket packet. 1066 * 1067 * @param {Object} packet 1068 * @api private 1069 */ 1070 1071Socket.prototype.onpacket = function(packet){ 1072 if (packet.nsp != this.nsp) return; 1073 1074 switch (packet.type) { 1075 case parser.CONNECT: 1076 this.onconnect(); 1077 break; 1078 1079 case parser.EVENT: 1080 this.onevent(packet); 1081 break; 1082 1083 case parser.BINARY_EVENT: 1084 this.onevent(packet); 1085 break; 1086 1087 case parser.ACK: 1088 this.onack(packet); 1089 break; 1090 1091 case parser.BINARY_ACK: 1092 this.onack(packet); 1093 break; 1094 1095 case parser.DISCONNECT: 1096 this.ondisconnect(); 1097 break; 1098 1099 case parser.ERROR: 1100 this.emit('error', packet.data); 1101 break; 1102 } 1103}; 1104 1105/** 1106 * Called upon a server event. 1107 * 1108 * @param {Object} packet 1109 * @api private 1110 */ 1111 1112Socket.prototype.onevent = function(packet){ 1113 var args = packet.data || []; 1114 debug('emitting event %j', args); 1115 1116 if (null != packet.id) { 1117 debug('attaching ack callback to event'); 1118 args.push(this.ack(packet.id)); 1119 } 1120 1121 if (this.connected) { 1122 emit.apply(this, args); 1123 } else { 1124 this.receiveBuffer.push(args); 1125 } 1126}; 1127 1128/** 1129 * Produces an ack callback to emit with an event. 1130 * 1131 * @api private 1132 */ 1133 1134Socket.prototype.ack = function(id){ 1135 var self = this; 1136 var sent = false; 1137 return function(){ 1138 // prevent double callbacks 1139 if (sent) return; 1140 sent = true; 1141 var args = toArray(arguments); 1142 debug('sending ack %j', args); 1143 1144 var type = hasBin(args) ? parser.BINARY_ACK : parser.ACK; 1145 self.packet({ 1146 type: type, 1147 id: id, 1148 data: args 1149 }); 1150 }; 1151}; 1152 1153/** 1154 * Called upon a server acknowlegement. 1155 * 1156 * @param {Object} packet 1157 * @api private 1158 */ 1159 1160Socket.prototype.onack = function(packet){ 1161 debug('calling ack %s with %j', packet.id, packet.data); 1162 var fn = this.acks[packet.id]; 1163 fn.apply(this, packet.data); 1164 delete this.acks[packet.id]; 1165}; 1166 1167/** 1168 * Called upon server connect. 1169 * 1170 * @api private 1171 */ 1172 1173Socket.prototype.onconnect = function(){ 1174 this.connected = true; 1175 this.disconnected = false; 1176 this.emit('connect'); 1177 this.emitBuffered(); 1178}; 1179 1180/** 1181 * Emit buffered events (received and emitted). 1182 * 1183 * @api private 1184 */ 1185 1186Socket.prototype.emitBuffered = function(){ 1187 var i; 1188 for (i = 0; i < this.receiveBuffer.length; i++) { 1189 emit.apply(this, this.receiveBuffer[i]); 1190 } 1191 this.receiveBuffer = []; 1192 1193 for (i = 0; i < this.sendBuffer.length; i++) { 1194 this.packet(this.sendBuffer[i]); 1195 } 1196 this.sendBuffer = []; 1197}; 1198 1199/** 1200 * Called upon server disconnect. 1201 * 1202 * @api private 1203 */ 1204 1205Socket.prototype.ondisconnect = function(){ 1206 debug('server disconnect (%s)', this.nsp); 1207 this.destroy(); 1208 this.onclose('io server disconnect'); 1209}; 1210 1211/** 1212 * Called upon forced client/server side disconnections, 1213 * this method ensures the manager stops tracking us and 1214 * that reconnections don't get triggered for this. 1215 * 1216 * @api private. 1217 */ 1218 1219Socket.prototype.destroy = function(){ 1220 // clean subscriptions to avoid reconnections 1221 for (var i = 0; i < this.subs.length; i++) { 1222 this.subs[i].destroy(); 1223 } 1224 1225 this.io.destroy(this); 1226}; 1227 1228/** 1229 * Disconnects the socket manually. 1230 * 1231 * @return {Socket} self 1232 * @api public 1233 */ 1234 1235Socket.prototype.close = 1236Socket.prototype.disconnect = function(){ 1237 if (!this.connected) return this; 1238 1239 debug('performing disconnect (%s)', this.nsp); 1240 this.packet({ type: parser.DISCONNECT }); 1241 1242 // remove socket from pool 1243 this.destroy(); 1244 1245 // fire events 1246 this.onclose('io client disconnect'); 1247 return this; 1248}; 1249 1250},{"./on":6,"component-bind":9,"component-emitter":10,"debug":11,"has-binary-data":35,"indexof":37,"socket.io-parser":41,"to-array":45}],8:[function(_dereq_,module,exports){ 1251(function (global){ 1252 1253/** 1254 * Module dependencies. 1255 */ 1256 1257var parseuri = _dereq_('parseuri'); 1258var debug = _dereq_('debug')('socket.io-client:url'); 1259 1260/** 1261 * Module exports. 1262 */ 1263 1264module.exports = url; 1265 1266/** 1267 * URL parser. 1268 * 1269 * @param {String} url 1270 * @param {Object} An object meant to mimic window.location. 1271 * Defaults to window.location. 1272 * @api public 1273 */ 1274 1275function url(uri, loc){ 1276 var obj = uri; 1277 1278 // default to window.location 1279 var loc = loc || global.location; 1280 if (null == uri) uri = loc.protocol + '//' + loc.hostname; 1281 1282 // relative path support 1283 if ('string' == typeof uri) { 1284 if ('/' == uri.charAt(0)) { 1285 if ('undefined' != typeof loc) { 1286 uri = loc.hostname + uri; 1287 } 1288 } 1289 1290 if (!/^(https?|wss?):\/\//.test(uri)) { 1291 debug('protocol-less url %s', uri); 1292 if ('undefined' != typeof loc) { 1293 uri = loc.protocol + '//' + uri; 1294 } else { 1295 uri = 'https://' + uri; 1296 } 1297 } 1298 1299 // parse 1300 debug('parse %s', uri); 1301 obj = parseuri(uri); 1302 } 1303 1304 // make sure we treat `localhost:80` and `localhost` equally 1305 if (!obj.port) { 1306 if (/^(http|ws)$/.test(obj.protocol)) { 1307 obj.port = '80'; 1308 } 1309 else if (/^(http|ws)s$/.test(obj.protocol)) { 1310 obj.port = '443'; 1311 } 1312 } 1313 1314 obj.path = obj.path || '/'; 1315 1316 // define unique id 1317 obj.id = obj.protocol + '://' + obj.host + ':' + obj.port; 1318 // define href 1319 obj.href = obj.protocol + '://' + obj.host + (loc && loc.port == obj.port ? '' : (':' + obj.port)); 1320 1321 return obj; 1322} 1323 1324}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 1325},{"debug":11,"parseuri":39}],9:[function(_dereq_,module,exports){ 1326/** 1327 * Slice reference. 1328 */ 1329 1330var slice = [].slice; 1331 1332/** 1333 * Bind `obj` to `fn`. 1334 * 1335 * @param {Object} obj 1336 * @param {Function|String} fn or string 1337 * @return {Function} 1338 * @api public 1339 */ 1340 1341module.exports = function(obj, fn){ 1342 if ('string' == typeof fn) fn = obj[fn]; 1343 if ('function' != typeof fn) throw new Error('bind() requires a function'); 1344 var args = slice.call(arguments, 2); 1345 return function(){ 1346 return fn.apply(obj, args.concat(slice.call(arguments))); 1347 } 1348}; 1349 1350},{}],10:[function(_dereq_,module,exports){ 1351 1352/** 1353 * Expose `Emitter`. 1354 */ 1355 1356module.exports = Emitter; 1357 1358/** 1359 * Initialize a new `Emitter`. 1360 * 1361 * @api public 1362 */ 1363 1364function Emitter(obj) { 1365 if (obj) return mixin(obj); 1366}; 1367 1368/** 1369 * Mixin the emitter properties. 1370 * 1371 * @param {Object} obj 1372 * @return {Object} 1373 * @api private 1374 */ 1375 1376function mixin(obj) { 1377 for (var key in Emitter.prototype) { 1378 obj[key] = Emitter.prototype[key]; 1379 } 1380 return obj; 1381} 1382 1383/** 1384 * Listen on the given `event` with `fn`. 1385 * 1386 * @param {String} event 1387 * @param {Function} fn 1388 * @return {Emitter} 1389 * @api public 1390 */ 1391 1392Emitter.prototype.on = 1393Emitter.prototype.addEventListener = function(event, fn){ 1394 this._callbacks = this._callbacks || {}; 1395 (this._callbacks[event] = this._callbacks[event] || []) 1396 .push(fn); 1397 return this; 1398}; 1399 1400/** 1401 * Adds an `event` listener that will be invoked a single 1402 * time then automatically removed. 1403 * 1404 * @param {String} event 1405 * @param {Function} fn 1406 * @return {Emitter} 1407 * @api public 1408 */ 1409 1410Emitter.prototype.once = function(event, fn){ 1411 var self = this; 1412 this._callbacks = this._callbacks || {}; 1413 1414 function on() { 1415 self.off(event, on); 1416 fn.apply(this, arguments); 1417 } 1418 1419 on.fn = fn; 1420 this.on(event, on); 1421 return this; 1422}; 1423 1424/** 1425 * Remove the given callback for `event` or all 1426 * registered callbacks. 1427 * 1428 * @param {String} event 1429 * @param {Function} fn 1430 * @return {Emitter} 1431 * @api public 1432 */ 1433 1434Emitter.prototype.off = 1435Emitter.prototype.removeListener = 1436Emitter.prototype.removeAllListeners = 1437Emitter.prototype.removeEventListener = function(event, fn){ 1438 this._callbacks = this._callbacks || {}; 1439 1440 // all 1441 if (0 == arguments.length) { 1442 this._callbacks = {}; 1443 return this; 1444 } 1445 1446 // specific event 1447 var callbacks = this._callbacks[event]; 1448 if (!callbacks) return this; 1449 1450 // remove all handlers 1451 if (1 == arguments.length) { 1452 delete this._callbacks[event]; 1453 return this; 1454 } 1455 1456 // remove specific handler 1457 var cb; 1458 for (var i = 0; i < callbacks.length; i++) { 1459 cb = callbacks[i]; 1460 if (cb === fn || cb.fn === fn) { 1461 callbacks.splice(i, 1); 1462 break; 1463 } 1464 } 1465 return this; 1466}; 1467 1468/** 1469 * Emit `event` with the given args. 1470 * 1471 * @param {String} event 1472 * @param {Mixed} ... 1473 * @return {Emitter} 1474 */ 1475 1476Emitter.prototype.emit = function(event){ 1477 this._callbacks = this._callbacks || {}; 1478 var args = [].slice.call(arguments, 1) 1479 , callbacks = this._callbacks[event]; 1480 1481 if (callbacks) { 1482 callbacks = callbacks.slice(0); 1483 for (var i = 0, len = callbacks.length; i < len; ++i) { 1484 callbacks[i].apply(this, args); 1485 } 1486 } 1487 1488 return this; 1489}; 1490 1491/** 1492 * Return array of callbacks for `event`. 1493 * 1494 * @param {String} event 1495 * @return {Array} 1496 * @api public 1497 */ 1498 1499Emitter.prototype.listeners = function(event){ 1500 this._callbacks = this._callbacks || {}; 1501 return this._callbacks[event] || []; 1502}; 1503 1504/** 1505 * Check if this emitter has `event` handlers. 1506 * 1507 * @param {String} event 1508 * @return {Boolean} 1509 * @api public 1510 */ 1511 1512Emitter.prototype.hasListeners = function(event){ 1513 return !! this.listeners(event).length; 1514}; 1515 1516},{}],11:[function(_dereq_,module,exports){ 1517 1518/** 1519 * Expose `debug()` as the module. 1520 */ 1521 1522module.exports = debug; 1523 1524/** 1525 * Create a debugger with the given `name`. 1526 * 1527 * @param {String} name 1528 * @return {Type} 1529 * @api public 1530 */ 1531 1532function debug(name) { 1533 if (!debug.enabled(name)) return function(){}; 1534 1535 return function(fmt){ 1536 fmt = coerce(fmt); 1537 1538 var curr = new Date; 1539 var ms = curr - (debug[name] || curr); 1540 debug[name] = curr; 1541 1542 fmt = name 1543 + ' ' 1544 + fmt 1545 + ' +' + debug.humanize(ms); 1546 1547 // This hackery is required for IE8 1548 // where `console.log` doesn't have 'apply' 1549 window.console 1550 && console.log 1551 && Function.prototype.apply.call(console.log, console, arguments); 1552 } 1553} 1554 1555/** 1556 * The currently active debug mode names. 1557 */ 1558 1559debug.names = []; 1560debug.skips = []; 1561 1562/** 1563 * Enables a debug mode by name. This can include modes 1564 * separated by a colon and wildcards. 1565 * 1566 * @param {String} name 1567 * @api public 1568 */ 1569 1570debug.enable = function(name) { 1571 try { 1572 localStorage.debug = name; 1573 } catch(e){} 1574 1575 var split = (name || '').split(/[\s,]+/) 1576 , len = split.length; 1577 1578 for (var i = 0; i < len; i++) { 1579 name = split[i].replace('*', '.*?'); 1580 if (name[0] === '-') { 1581 debug.skips.push(new RegExp('^' + name.substr(1) + '$')); 1582 } 1583 else { 1584 debug.names.push(new RegExp('^' + name + '$')); 1585 } 1586 } 1587}; 1588 1589/** 1590 * Disable debug output. 1591 * 1592 * @api public 1593 */ 1594 1595debug.disable = function(){ 1596 debug.enable(''); 1597}; 1598 1599/** 1600 * Humanize the given `ms`. 1601 * 1602 * @param {Number} m 1603 * @return {String} 1604 * @api private 1605 */ 1606 1607debug.humanize = function(ms) { 1608 var sec = 1000 1609 , min = 60 * 1000 1610 , hour = 60 * min; 1611 1612 if (ms >= hour) return (ms / hour).toFixed(1) + 'h'; 1613 if (ms >= min) return (ms / min).toFixed(1) + 'm'; 1614 if (ms >= sec) return (ms / sec | 0) + 's'; 1615 return ms + 'ms'; 1616}; 1617 1618/** 1619 * Returns true if the given mode name is enabled, false otherwise. 1620 * 1621 * @param {String} name 1622 * @return {Boolean} 1623 * @api public 1624 */ 1625 1626debug.enabled = function(name) { 1627 for (var i = 0, len = debug.skips.length; i < len; i++) { 1628 if (debug.skips[i].test(name)) { 1629 return false; 1630 } 1631 } 1632 for (var i = 0, len = debug.names.length; i < len; i++) { 1633 if (debug.names[i].test(name)) { 1634 return true; 1635 } 1636 } 1637 return false; 1638}; 1639 1640/** 1641 * Coerce `val`. 1642 */ 1643 1644function coerce(val) { 1645 if (val instanceof Error) return val.stack || val.message; 1646 return val; 1647} 1648 1649// persist 1650 1651try { 1652 if (window.localStorage) debug.enable(localStorage.debug); 1653} catch(e){} 1654 1655},{}],12:[function(_dereq_,module,exports){ 1656 1657module.exports = _dereq_('./lib/'); 1658 1659},{"./lib/":13}],13:[function(_dereq_,module,exports){ 1660 1661module.exports = _dereq_('./socket'); 1662 1663/** 1664 * Exports parser 1665 * 1666 * @api public 1667 * 1668 */ 1669module.exports.parser = _dereq_('engine.io-parser'); 1670 1671},{"./socket":14,"engine.io-parser":23}],14:[function(_dereq_,module,exports){ 1672(function (global){ 1673/** 1674 * Module dependencies. 1675 */ 1676 1677var transports = _dereq_('./transports'); 1678var Emitter = _dereq_('component-emitter'); 1679var debug = _dereq_('debug')('engine.io-client:socket'); 1680var index = _dereq_('indexof'); 1681var parser = _dereq_('engine.io-parser'); 1682var parseuri = _dereq_('parseuri'); 1683var parsejson = _dereq_('parsejson'); 1684var parseqs = _dereq_('parseqs'); 1685 1686/** 1687 * Module exports. 1688 */ 1689 1690module.exports = Socket; 1691 1692/** 1693 * Noop function. 1694 * 1695 * @api private 1696 */ 1697 1698function noop(){} 1699 1700/** 1701 * Socket constructor. 1702 * 1703 * @param {String|Object} uri or options 1704 * @param {Object} options 1705 * @api public 1706 */ 1707 1708function Socket(uri, opts){ 1709 if (!(this instanceof Socket)) return new Socket(uri, opts); 1710 1711 opts = opts || {}; 1712 1713 if (uri && 'object' == typeof uri) { 1714 opts = uri; 1715 uri = null; 1716 } 1717 1718 if (uri) { 1719 uri = parseuri(uri); 1720 opts.host = uri.host; 1721 opts.secure = uri.protocol == 'https' || uri.protocol == 'wss'; 1722 opts.port = uri.port; 1723 if (uri.query) opts.query = uri.query; 1724 } 1725 1726 this.secure = null != opts.secure ? opts.secure : 1727 (global.location && 'https:' == location.protocol); 1728 1729 if (opts.host) { 1730 var pieces = opts.host.split(':'); 1731 opts.hostname = pieces.shift(); 1732 if (pieces.length) opts.port = pieces.pop(); 1733 } 1734 1735 this.agent = opts.agent || false; 1736 this.hostname = opts.hostname || 1737 (global.location ? location.hostname : 'localhost'); 1738 this.port = opts.port || (global.location && location.port ? 1739 location.port : 1740 (this.secure ? 443 : 80)); 1741 this.query = opts.query || {}; 1742 if ('string' == typeof this.query) this.query = parseqs.decode(this.query); 1743 this.upgrade = false !== opts.upgrade; 1744 this.path = (opts.path || '/engine.io').replace(/\/$/, '') + '/'; 1745 this.forceJSONP = !!opts.forceJSONP; 1746 this.forceBase64 = !!opts.forceBase64; 1747 this.timestampParam = opts.timestampParam || 't'; 1748 this.timestampRequests = opts.timestampRequests; 1749 this.transports = opts.transports || ['polling', 'websocket']; 1750 this.readyState = ''; 1751 this.writeBuffer = []; 1752 this.callbackBuffer = []; 1753 this.policyPort = opts.policyPort || 843; 1754 this.rememberUpgrade = opts.rememberUpgrade || false; 1755 this.open(); 1756 this.binaryType = null; 1757 this.onlyBinaryUpgrades = opts.onlyBinaryUpgrades; 1758} 1759 1760Socket.priorWebsocketSuccess = false; 1761 1762/** 1763 * Mix in `Emitter`. 1764 */ 1765 1766Emitter(Socket.prototype); 1767 1768/** 1769 * Protocol version. 1770 * 1771 * @api public 1772 */ 1773 1774Socket.protocol = parser.protocol; // this is an int 1775 1776/** 1777 * Expose deps for legacy compatibility 1778 * and standalone browser access. 1779 */ 1780 1781Socket.Socket = Socket; 1782Socket.Transport = _dereq_('./transport'); 1783Socket.transports = _dereq_('./transports'); 1784Socket.parser = _dereq_('engine.io-parser'); 1785 1786/** 1787 * Creates transport of the given type. 1788 * 1789 * @param {String} transport name 1790 * @return {Transport} 1791 * @api private 1792 */ 1793 1794Socket.prototype.createTransport = function (name) { 1795 debug('creating transport "%s"', name); 1796 var query = clone(this.query); 1797 1798 // append engine.io protocol identifier 1799 query.EIO = parser.protocol; 1800 1801 // transport name 1802 query.transport = name; 1803 1804 // session id if we already have one 1805 if (this.id) query.sid = this.id; 1806 1807 var transport = new transports[name]({ 1808 agent: this.agent, 1809 hostname: this.hostname, 1810 port: this.port, 1811 secure: this.secure, 1812 path: this.path, 1813 query: query, 1814 forceJSONP: this.forceJSONP, 1815 forceBase64: this.forceBase64, 1816 timestampRequests: this.timestampRequests, 1817 timestampParam: this.timestampParam, 1818 policyPort: this.policyPort, 1819 socket: this 1820 }); 1821 1822 return transport; 1823}; 1824 1825function clone (obj) { 1826 var o = {}; 1827 for (var i in obj) { 1828 if (obj.hasOwnProperty(i)) { 1829 o[i] = obj[i]; 1830 } 1831 } 1832 return o; 1833} 1834 1835/** 1836 * Initializes transport to use and starts probe. 1837 * 1838 * @api private 1839 */ 1840Socket.prototype.open = function () { 1841 var transport; 1842 if (this.rememberUpgrade && Socket.priorWebsocketSuccess && this.transports.indexOf('websocket') != -1) { 1843 transport = 'websocket'; 1844 } else { 1845 transport = this.transports[0]; 1846 } 1847 this.readyState = 'opening'; 1848 var transport = this.createTransport(transport); 1849 transport.open(); 1850 this.setTransport(transport); 1851}; 1852 1853/** 1854 * Sets the current transport. Disables the existing one (if any). 1855 * 1856 * @api private 1857 */ 1858 1859Socket.prototype.setTransport = function(transport){ 1860 debug('setting transport %s', transport.name); 1861 var self = this; 1862 1863 if (this.transport) { 1864 debug('clearing existing transport %s', this.transport.name); 1865 this.transport.removeAllListeners(); 1866 } 1867 1868 // set up transport 1869 this.transport = transport; 1870 1871 // set up transport listeners 1872 transport 1873 .on('drain', function(){ 1874 self.onDrain(); 1875 }) 1876 .on('packet', function(packet){ 1877 self.onPacket(packet); 1878 }) 1879 .on('error', function(e){ 1880 self.onError(e); 1881 }) 1882 .on('close', function(){ 1883 self.onClose('transport close'); 1884 }); 1885}; 1886 1887/** 1888 * Probes a transport. 1889 * 1890 * @param {String} transport name 1891 * @api private 1892 */ 1893 1894Socket.prototype.probe = function (name) { 1895 debug('probing transport "%s"', name); 1896 var transport = this.createTransport(name, { probe: 1 }) 1897 , failed = false 1898 , self = this; 1899 1900 Socket.priorWebsocketSuccess = false; 1901 1902 function onTransportOpen(){ 1903 if (self.onlyBinaryUpgrades) { 1904 var upgradeLosesBinary = !this.supportsBinary && self.transport.supportsBinary; 1905 failed = failed || upgradeLosesBinary; 1906 } 1907 if (failed) return; 1908 1909 debug('probe transport "%s" opened', name); 1910 transport.send([{ type: 'ping', data: 'probe' }]); 1911 transport.once('packet', function (msg) { 1912 if (failed) return; 1913 if ('pong' == msg.type && 'probe' == msg.data) { 1914 debug('probe transport "%s" pong', name); 1915 self.upgrading = true; 1916 self.emit('upgrading', transport); 1917 Socket.priorWebsocketSuccess = 'websocket' == transport.name; 1918 1919 debug('pausing current transport "%s"', self.transport.name); 1920 self.transport.pause(function () { 1921 if (failed) return; 1922 if ('closed' == self.readyState || 'closing' == self.readyState) { 1923 return; 1924 } 1925 debug('changing transport and sending upgrade packet'); 1926 1927 cleanup(); 1928 1929 self.setTransport(transport); 1930 transport.send([{ type: 'upgrade' }]); 1931 self.emit('upgrade', transport); 1932 transport = null; 1933 self.upgrading = false; 1934 self.flush(); 1935 }); 1936 } else { 1937 debug('probe transport "%s" failed', name); 1938 var err = new Error('probe error'); 1939 err.transport = transport.name; 1940 self.emit('upgradeError', err); 1941 } 1942 }); 1943 } 1944 1945 function freezeTransport() { 1946 if (failed) return; 1947 1948 // Any callback called by transport should be ignored since now 1949 failed = true; 1950 1951 cleanup(); 1952 1953 transport.close(); 1954 transport = null; 1955 } 1956 1957 //Handle any error that happens while probing 1958 function onerror(err) { 1959 var error = new Error('probe error: ' + err); 1960 error.transport = transport.name; 1961 1962 freezeTransport(); 1963 1964 debug('probe transport "%s" failed because of error: %s', name, err); 1965 1966 self.emit('upgradeError', error); 1967 } 1968 1969 function onTransportClose(){ 1970 onerror("transport closed"); 1971 } 1972 1973 //When the socket is closed while we're probing 1974 function onclose(){ 1975 onerror("socket closed"); 1976 } 1977 1978 //When the socket is upgraded while we're probing 1979 function onupgrade(to){ 1980 if (transport && to.name != transport.name) { 1981 debug('"%s" works - aborting "%s"', to.name, transport.name); 1982 freezeTransport(); 1983 } 1984 } 1985 1986 //Remove all listeners on the transport and on self 1987 function cleanup(){ 1988 transport.removeListener('open', onTransportOpen); 1989 transport.removeListener('error', onerror); 1990 transport.removeListener('close', onTransportClose); 1991 self.removeListener('close', onclose); 1992 self.removeListener('upgrading', onupgrade); 1993 } 1994 1995 transport.once('open', onTransportOpen); 1996 transport.once('error', onerror); 1997 transport.once('close', onTransportClose); 1998 1999 this.once('close', onclose); 2000 this.once('upgrading', onupgrade); 2001 2002 transport.open(); 2003 2004}; 2005 2006/** 2007 * Called when connection is deemed open. 2008 * 2009 * @api public 2010 */ 2011 2012Socket.prototype.onOpen = function () { 2013 debug('socket open'); 2014 this.readyState = 'open'; 2015 Socket.priorWebsocketSuccess = 'websocket' == this.transport.name; 2016 this.emit('open'); 2017 this.flush(); 2018 2019 // we check for `readyState` in case an `open` 2020 // listener already closed the socket 2021 if ('open' == this.readyState && this.upgrade && this.transport.pause) { 2022 debug('starting upgrade probes'); 2023 for (var i = 0, l = this.upgrades.length; i < l; i++) { 2024 this.probe(this.upgrades[i]); 2025 } 2026 } 2027}; 2028 2029/** 2030 * Handles a packet. 2031 * 2032 * @api private 2033 */ 2034 2035Socket.prototype.onPacket = function (packet) { 2036 if ('opening' == this.readyState || 'open' == this.readyState) { 2037 debug('socket receive: type "%s", data "%s"', packet.type, packet.data); 2038 2039 this.emit('packet', packet); 2040 2041 // Socket is live - any packet counts 2042 this.emit('heartbeat'); 2043 2044 switch (packet.type) { 2045 case 'open': 2046 this.onHandshake(parsejson(packet.data)); 2047 break; 2048 2049 case 'pong': 2050 this.setPing(); 2051 break; 2052 2053 case 'error': 2054 var err = new Error('server error'); 2055 err.code = packet.data; 2056 this.emit('error', err); 2057 break; 2058 2059 case 'message': 2060 this.emit('data', packet.data); 2061 this.emit('message', packet.data); 2062 break; 2063 } 2064 } else { 2065 debug('packet received with socket readyState "%s"', this.readyState); 2066 } 2067}; 2068 2069/** 2070 * Called upon handshake completion. 2071 * 2072 * @param {Object} handshake obj 2073 * @api private 2074 */ 2075 2076Socket.prototype.onHandshake = function (data) { 2077 this.emit('handshake', data); 2078 this.id = data.sid; 2079 this.transport.query.sid = data.sid; 2080 this.upgrades = this.filterUpgrades(data.upgrades); 2081 this.pingInterval = data.pingInterval; 2082 this.pingTimeout = data.pingTimeout; 2083 this.onOpen(); 2084 // In case open handler closes socket 2085 if ('closed' == this.readyState) return; 2086 this.setPing(); 2087 2088 // Prolong liveness of socket on heartbeat 2089 this.removeListener('heartbeat', this.onHeartbeat); 2090 this.on('heartbeat', this.onHeartbeat); 2091}; 2092 2093/** 2094 * Resets ping timeout. 2095 * 2096 * @api private 2097 */ 2098 2099Socket.prototype.onHeartbeat = function (timeout) { 2100 clearTimeout(this.pingTimeoutTimer); 2101 var self = this; 2102 self.pingTimeoutTimer = setTimeout(function () { 2103 if ('closed' == self.readyState) return; 2104 self.onClose('ping timeout'); 2105 }, timeout || (self.pingInterval + self.pingTimeout)); 2106}; 2107 2108/** 2109 * Pings server every `this.pingInterval` and expects response 2110 * within `this.pingTimeout` or closes connection. 2111 * 2112 * @api private 2113 */ 2114 2115Socket.prototype.setPing = function () { 2116 var self = this; 2117 clearTimeout(self.pingIntervalTimer); 2118 self.pingIntervalTimer = setTimeout(function () { 2119 debug('writing ping packet - expecting pong within %sms', self.pingTimeout); 2120 self.ping(); 2121 self.onHeartbeat(self.pingTimeout); 2122 }, self.pingInterval); 2123}; 2124 2125/** 2126* Sends a ping packet. 2127* 2128* @api public 2129*/ 2130 2131Socket.prototype.ping = function () { 2132 this.sendPacket('ping'); 2133}; 2134 2135/** 2136 * Called on `drain` event 2137 * 2138 * @api private 2139 */ 2140 2141Socket.prototype.onDrain = function() { 2142 for (var i = 0; i < this.prevBufferLen; i++) { 2143 if (this.callbackBuffer[i]) { 2144 this.callbackBuffer[i](); 2145 } 2146 } 2147 2148 this.writeBuffer.splice(0, this.prevBufferLen); 2149 this.callbackBuffer.splice(0, this.prevBufferLen); 2150 2151 // setting prevBufferLen = 0 is very important 2152 // for example, when upgrading, upgrade packet is sent over, 2153 // and a nonzero prevBufferLen could cause problems on `drain` 2154 this.prevBufferLen = 0; 2155 2156 if (this.writeBuffer.length == 0) { 2157 this.emit('drain'); 2158 } else { 2159 this.flush(); 2160 } 2161}; 2162 2163/** 2164 * Flush write buffers. 2165 * 2166 * @api private 2167 */ 2168 2169Socket.prototype.flush = function () { 2170 if ('closed' != this.readyState && this.transport.writable && 2171 !this.upgrading && this.writeBuffer.length) { 2172 debug('flushing %d packets in socket', this.writeBuffer.length); 2173 this.transport.send(this.writeBuffer); 2174 // keep track of current length of writeBuffer 2175 // splice writeBuffer and callbackBuffer on `drain` 2176 this.prevBufferLen = this.writeBuffer.length; 2177 this.emit('flush'); 2178 } 2179}; 2180 2181/** 2182 * Sends a message. 2183 * 2184 * @param {String} message. 2185 * @param {Function} callback function. 2186 * @return {Socket} for chaining. 2187 * @api public 2188 */ 2189 2190Socket.prototype.write = 2191Socket.prototype.send = function (msg, fn) { 2192 this.sendPacket('message', msg, fn); 2193 return this; 2194}; 2195 2196/** 2197 * Sends a packet. 2198 * 2199 * @param {String} packet type. 2200 * @param {String} data. 2201 * @param {Function} callback function. 2202 * @api private 2203 */ 2204 2205Socket.prototype.sendPacket = function (type, data, fn) { 2206 var packet = { type: type, data: data }; 2207 this.emit('packetCreate', packet); 2208 this.writeBuffer.push(packet); 2209 this.callbackBuffer.push(fn); 2210 this.flush(); 2211}; 2212 2213/** 2214 * Closes the connection. 2215 * 2216 * @api private 2217 */ 2218 2219Socket.prototype.close = function () { 2220 if ('opening' == this.readyState || 'open' == this.readyState) { 2221 this.onClose('forced close'); 2222 debug('socket closing - telling transport to close'); 2223 this.transport.close(); 2224 } 2225 2226 return this; 2227}; 2228 2229/** 2230 * Called upon transport error 2231 * 2232 * @api private 2233 */ 2234 2235Socket.prototype.onError = function (err) { 2236 debug('socket error %j', err); 2237 Socket.priorWebsocketSuccess = false; 2238 this.emit('error', err); 2239 this.onClose('transport error', err); 2240}; 2241 2242/** 2243 * Called upon transport close. 2244 * 2245 * @api private 2246 */ 2247 2248Socket.prototype.onClose = function (reason, desc) { 2249 if ('opening' == this.readyState || 'open' == this.readyState) { 2250 debug('socket close with reason: "%s"', reason); 2251 var self = this; 2252 2253 // clear timers 2254 clearTimeout(this.pingIntervalTimer); 2255 clearTimeout(this.pingTimeoutTimer); 2256 2257 // clean buffers in next tick, so developers can still 2258 // grab the buffers on `close` event 2259 setTimeout(function() { 2260 self.writeBuffer = []; 2261 self.callbackBuffer = []; 2262 self.prevBufferLen = 0; 2263 }, 0); 2264 2265 // stop event from firing again for transport 2266 this.transport.removeAllListeners('close'); 2267 2268 // ensure transport won't stay open 2269 this.transport.close(); 2270 2271 // ignore further transport communication 2272 this.transport.removeAllListeners(); 2273 2274 // set ready state 2275 this.readyState = 'closed'; 2276 2277 // clear session id 2278 this.id = null; 2279 2280 // emit close event 2281 this.emit('close', reason, desc); 2282 } 2283}; 2284 2285/** 2286 * Filters upgrades, returning only those matching client transports. 2287 * 2288 * @param {Array} server upgrades 2289 * @api private 2290 * 2291 */ 2292 2293Socket.prototype.filterUpgrades = function (upgrades) { 2294 var filteredUpgrades = []; 2295 for (var i = 0, j = upgrades.length; i<j; i++) { 2296 if (~index(this.transports, upgrades[i])) filt…
Large files files are truncated, but you can click here to view the full file