/js/skynet.bundle.js

https://github.com/SYCCON/skynet · JavaScript · 8844 lines · 5522 code · 1322 blank · 2000 comment · 1433 complexity · a72b3b54dea417156436f69a63e3f0ce MD5 · raw file

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