Advertisement
Guest User

Untitled

a guest
May 27th, 2016
1,645
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 77.72 KB | None | 0 0
  1. (function UMDish(name, context, definition) {
  2. context[name] = definition.call(context);
  3. if (typeof module !== "undefined" && module.exports) {
  4. module.exports = context[name];
  5. } else if (typeof define === "function" && define.amd) {
  6. define(function reference() { return context[name]; });
  7. }
  8. })("Primus", this, function wrapper() {
  9. var define, module, exports
  10. , Primus = (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);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.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){
  11. 'use strict';
  12.  
  13. /**
  14. * Create a function that will cleanup the instance.
  15. *
  16. * @param {Array|String} keys Properties on the instance that needs to be cleared.
  17. * @param {Object} options Additional configuration.
  18. * @returns {Function} Destroy function
  19. * @api public
  20. */
  21. module.exports = function demolish(keys, options) {
  22. var split = /[, ]+/;
  23.  
  24. options = options || {};
  25. keys = keys || [];
  26.  
  27. if ('string' === typeof keys) keys = keys.split(split);
  28.  
  29. /**
  30. * Run addition cleanup hooks.
  31. *
  32. * @param {String} key Name of the clean up hook to run.
  33. * @param {Mixed} selfie Reference to the instance we're cleaning up.
  34. * @api private
  35. */
  36. function run(key, selfie) {
  37. if (!options[key]) return;
  38. if ('string' === typeof options[key]) options[key] = options[key].split(split);
  39. if ('function' === typeof options[key]) return options[key].call(selfie);
  40.  
  41. for (var i = 0, type, what; i < options[key].length; i++) {
  42. what = options[key][i];
  43. type = typeof what;
  44.  
  45. if ('function' === type) {
  46. what.call(selfie);
  47. } else if ('string' === type && 'function' === typeof selfie[what]) {
  48. selfie[what]();
  49. }
  50. }
  51. }
  52.  
  53. /**
  54. * Destroy the instance completely and clean up all the existing references.
  55. *
  56. * @returns {Boolean}
  57. * @api public
  58. */
  59. return function destroy() {
  60. var selfie = this
  61. , i = 0
  62. , prop;
  63.  
  64. if (selfie[keys[0]] === null) return false;
  65. run('before', selfie);
  66.  
  67. for (; i < keys.length; i++) {
  68. prop = keys[i];
  69.  
  70. if (selfie[prop]) {
  71. if ('function' === typeof selfie[prop].destroy) selfie[prop].destroy();
  72. selfie[prop] = null;
  73. }
  74. }
  75.  
  76. if (selfie.emit) selfie.emit('destroy');
  77. run('after', selfie);
  78.  
  79. return true;
  80. };
  81. };
  82.  
  83. },{}],2:[function(_dereq_,module,exports){
  84. 'use strict';
  85.  
  86. /**
  87. * Returns a function that when invoked executes all the listeners of the
  88. * given event with the given arguments.
  89. *
  90. * @returns {Function} The function that emits all the things.
  91. * @api public
  92. */
  93. module.exports = function emits() {
  94. var self = this
  95. , parser;
  96.  
  97. for (var i = 0, l = arguments.length, args = new Array(l); i < l; i++) {
  98. args[i] = arguments[i];
  99. }
  100.  
  101. //
  102. // If the last argument is a function, assume that it's a parser.
  103. //
  104. if ('function' !== typeof args[args.length - 1]) return function emitter() {
  105. for (var i = 0, l = arguments.length, arg = new Array(l); i < l; i++) {
  106. arg[i] = arguments[i];
  107. }
  108.  
  109. return self.emit.apply(self, args.concat(arg));
  110. };
  111.  
  112. parser = args.pop();
  113.  
  114. /**
  115. * The actual function that emits the given event. It returns a boolean
  116. * indicating if the event was emitted.
  117. *
  118. * @returns {Boolean}
  119. * @api public
  120. */
  121. return function emitter() {
  122. for (var i = 0, l = arguments.length, arg = new Array(l + 1); i < l; i++) {
  123. arg[i + 1] = arguments[i];
  124. }
  125.  
  126. /**
  127. * Async completion method for the parser.
  128. *
  129. * @param {Error} err Optional error when parsing failed.
  130. * @param {Mixed} returned Emit instructions.
  131. * @api private
  132. */
  133. arg[0] = function next(err, returned) {
  134. if (err) return self.emit('error', err);
  135.  
  136. arg = returned === undefined
  137. ? arg.slice(1) : returned === null
  138. ? [] : returned;
  139.  
  140. self.emit.apply(self, args.concat(arg));
  141. };
  142.  
  143. parser.apply(self, arg);
  144. return true;
  145. };
  146. };
  147.  
  148. },{}],3:[function(_dereq_,module,exports){
  149. 'use strict';
  150.  
  151. //
  152. // We store our EE objects in a plain object whose properties are event names.
  153. // If `Object.create(null)` is not supported we prefix the event names with a
  154. // `~` to make sure that the built-in object properties are not overridden or
  155. // used as an attack vector.
  156. // We also assume that `Object.create(null)` is available when the event name
  157. // is an ES6 Symbol.
  158. //
  159. var prefix = typeof Object.create !== 'function' ? '~' : false;
  160.  
  161. /**
  162. * Representation of a single EventEmitter function.
  163. *
  164. * @param {Function} fn Event handler to be called.
  165. * @param {Mixed} context Context for function execution.
  166. * @param {Boolean} once Only emit once
  167. * @api private
  168. */
  169. function EE(fn, context, once) {
  170. this.fn = fn;
  171. this.context = context;
  172. this.once = once || false;
  173. }
  174.  
  175. /**
  176. * Minimal EventEmitter interface that is molded against the Node.js
  177. * EventEmitter interface.
  178. *
  179. * @constructor
  180. * @api public
  181. */
  182. function EventEmitter() { /* Nothing to set */ }
  183.  
  184. /**
  185. * Holds the assigned EventEmitters by name.
  186. *
  187. * @type {Object}
  188. * @private
  189. */
  190. EventEmitter.prototype._events = undefined;
  191.  
  192. /**
  193. * Return a list of assigned event listeners.
  194. *
  195. * @param {String} event The events that should be listed.
  196. * @param {Boolean} exists We only need to know if there are listeners.
  197. * @returns {Array|Boolean}
  198. * @api public
  199. */
  200. EventEmitter.prototype.listeners = function listeners(event, exists) {
  201. var evt = prefix ? prefix + event : event
  202. , available = this._events && this._events[evt];
  203.  
  204. if (exists) return !!available;
  205. if (!available) return [];
  206. if (available.fn) return [available.fn];
  207.  
  208. for (var i = 0, l = available.length, ee = new Array(l); i < l; i++) {
  209. ee[i] = available[i].fn;
  210. }
  211.  
  212. return ee;
  213. };
  214.  
  215. /**
  216. * Emit an event to all registered event listeners.
  217. *
  218. * @param {String} event The name of the event.
  219. * @returns {Boolean} Indication if we've emitted an event.
  220. * @api public
  221. */
  222. EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) {
  223. var evt = prefix ? prefix + event : event;
  224.  
  225. if (!this._events || !this._events[evt]) return false;
  226.  
  227. var listeners = this._events[evt]
  228. , len = arguments.length
  229. , args
  230. , i;
  231.  
  232. if ('function' === typeof listeners.fn) {
  233. if (listeners.once) this.removeListener(event, listeners.fn, undefined, true);
  234.  
  235. switch (len) {
  236. case 1: return listeners.fn.call(listeners.context), true;
  237. case 2: return listeners.fn.call(listeners.context, a1), true;
  238. case 3: return listeners.fn.call(listeners.context, a1, a2), true;
  239. case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true;
  240. case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true;
  241. case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true;
  242. }
  243.  
  244. for (i = 1, args = new Array(len -1); i < len; i++) {
  245. args[i - 1] = arguments[i];
  246. }
  247.  
  248. listeners.fn.apply(listeners.context, args);
  249. } else {
  250. var length = listeners.length
  251. , j;
  252.  
  253. for (i = 0; i < length; i++) {
  254. if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true);
  255.  
  256. switch (len) {
  257. case 1: listeners[i].fn.call(listeners[i].context); break;
  258. case 2: listeners[i].fn.call(listeners[i].context, a1); break;
  259. case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break;
  260. default:
  261. if (!args) for (j = 1, args = new Array(len -1); j < len; j++) {
  262. args[j - 1] = arguments[j];
  263. }
  264.  
  265. listeners[i].fn.apply(listeners[i].context, args);
  266. }
  267. }
  268. }
  269.  
  270. return true;
  271. };
  272.  
  273. /**
  274. * Register a new EventListener for the given event.
  275. *
  276. * @param {String} event Name of the event.
  277. * @param {Functon} fn Callback function.
  278. * @param {Mixed} context The context of the function.
  279. * @api public
  280. */
  281. EventEmitter.prototype.on = function on(event, fn, context) {
  282. var listener = new EE(fn, context || this)
  283. , evt = prefix ? prefix + event : event;
  284.  
  285. if (!this._events) this._events = prefix ? {} : Object.create(null);
  286. if (!this._events[evt]) this._events[evt] = listener;
  287. else {
  288. if (!this._events[evt].fn) this._events[evt].push(listener);
  289. else this._events[evt] = [
  290. this._events[evt], listener
  291. ];
  292. }
  293.  
  294. return this;
  295. };
  296.  
  297. /**
  298. * Add an EventListener that's only called once.
  299. *
  300. * @param {String} event Name of the event.
  301. * @param {Function} fn Callback function.
  302. * @param {Mixed} context The context of the function.
  303. * @api public
  304. */
  305. EventEmitter.prototype.once = function once(event, fn, context) {
  306. var listener = new EE(fn, context || this, true)
  307. , evt = prefix ? prefix + event : event;
  308.  
  309. if (!this._events) this._events = prefix ? {} : Object.create(null);
  310. if (!this._events[evt]) this._events[evt] = listener;
  311. else {
  312. if (!this._events[evt].fn) this._events[evt].push(listener);
  313. else this._events[evt] = [
  314. this._events[evt], listener
  315. ];
  316. }
  317.  
  318. return this;
  319. };
  320.  
  321. /**
  322. * Remove event listeners.
  323. *
  324. * @param {String} event The event we want to remove.
  325. * @param {Function} fn The listener that we need to find.
  326. * @param {Mixed} context Only remove listeners matching this context.
  327. * @param {Boolean} once Only remove once listeners.
  328. * @api public
  329. */
  330. EventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) {
  331. var evt = prefix ? prefix + event : event;
  332.  
  333. if (!this._events || !this._events[evt]) return this;
  334.  
  335. var listeners = this._events[evt]
  336. , events = [];
  337.  
  338. if (fn) {
  339. if (listeners.fn) {
  340. if (
  341. listeners.fn !== fn
  342. || (once && !listeners.once)
  343. || (context && listeners.context !== context)
  344. ) {
  345. events.push(listeners);
  346. }
  347. } else {
  348. for (var i = 0, length = listeners.length; i < length; i++) {
  349. if (
  350. listeners[i].fn !== fn
  351. || (once && !listeners[i].once)
  352. || (context && listeners[i].context !== context)
  353. ) {
  354. events.push(listeners[i]);
  355. }
  356. }
  357. }
  358. }
  359.  
  360. //
  361. // Reset the array, or remove it completely if we have no more listeners.
  362. //
  363. if (events.length) {
  364. this._events[evt] = events.length === 1 ? events[0] : events;
  365. } else {
  366. delete this._events[evt];
  367. }
  368.  
  369. return this;
  370. };
  371.  
  372. /**
  373. * Remove all listeners or only the listeners for the specified event.
  374. *
  375. * @param {String} event The event want to remove all listeners for.
  376. * @api public
  377. */
  378. EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) {
  379. if (!this._events) return this;
  380.  
  381. if (event) delete this._events[prefix ? prefix + event : event];
  382. else this._events = prefix ? {} : Object.create(null);
  383.  
  384. return this;
  385. };
  386.  
  387. //
  388. // Alias methods names because people roll like that.
  389. //
  390. EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
  391. EventEmitter.prototype.addListener = EventEmitter.prototype.on;
  392.  
  393. //
  394. // This function doesn't apply anymore.
  395. //
  396. EventEmitter.prototype.setMaxListeners = function setMaxListeners() {
  397. return this;
  398. };
  399.  
  400. //
  401. // Expose the prefix.
  402. //
  403. EventEmitter.prefixed = prefix;
  404.  
  405. //
  406. // Expose the module.
  407. //
  408. if ('undefined' !== typeof module) {
  409. module.exports = EventEmitter;
  410. }
  411.  
  412. },{}],4:[function(_dereq_,module,exports){
  413. 'use strict';
  414.  
  415. var has = Object.prototype.hasOwnProperty;
  416.  
  417. /**
  418. * Simple query string parser.
  419. *
  420. * @param {String} query The query string that needs to be parsed.
  421. * @returns {Object}
  422. * @api public
  423. */
  424. function querystring(query) {
  425. var parser = /([^=?&]+)=([^&]*)/g
  426. , result = {}
  427. , part;
  428.  
  429. //
  430. // Little nifty parsing hack, leverage the fact that RegExp.exec increments
  431. // the lastIndex property so we can continue executing this loop until we've
  432. // parsed all results.
  433. //
  434. for (;
  435. part = parser.exec(query);
  436. result[decodeURIComponent(part[1])] = decodeURIComponent(part[2])
  437. );
  438.  
  439. return result;
  440. }
  441.  
  442. /**
  443. * Transform a query string to an object.
  444. *
  445. * @param {Object} obj Object that should be transformed.
  446. * @param {String} prefix Optional prefix.
  447. * @returns {String}
  448. * @api public
  449. */
  450. function querystringify(obj, prefix) {
  451. prefix = prefix || '';
  452.  
  453. var pairs = [];
  454.  
  455. //
  456. // Optionally prefix with a '?' if needed
  457. //
  458. if ('string' !== typeof prefix) prefix = '?';
  459.  
  460. for (var key in obj) {
  461. if (has.call(obj, key)) {
  462. pairs.push(encodeURIComponent(key) +'='+ encodeURIComponent(obj[key]));
  463. }
  464. }
  465.  
  466. return pairs.length ? prefix + pairs.join('&') : '';
  467. }
  468.  
  469. //
  470. // Expose the module.
  471. //
  472. exports.stringify = querystringify;
  473. exports.parse = querystring;
  474.  
  475. },{}],5:[function(_dereq_,module,exports){
  476. 'use strict';
  477.  
  478. var EventEmitter = _dereq_('eventemitter3')
  479. , millisecond = _dereq_('millisecond')
  480. , destroy = _dereq_('demolish')
  481. , Tick = _dereq_('tick-tock')
  482. , one = _dereq_('one-time');
  483.  
  484. /**
  485. * Returns sane defaults about a given value.
  486. *
  487. * @param {String} name Name of property we want.
  488. * @param {Recovery} selfie Recovery instance that got created.
  489. * @param {Object} opts User supplied options we want to check.
  490. * @returns {Number} Some default value.
  491. * @api private
  492. */
  493. function defaults(name, selfie, opts) {
  494. return millisecond(
  495. name in opts ? opts[name] : (name in selfie ? selfie[name] : Recovery[name])
  496. );
  497. }
  498.  
  499. /**
  500. * Attempt to recover your connection with reconnection attempt.
  501. *
  502. * @constructor
  503. * @param {Object} options Configuration
  504. * @api public
  505. */
  506. function Recovery(options) {
  507. var recovery = this;
  508.  
  509. if (!(recovery instanceof Recovery)) return new Recovery(options);
  510.  
  511. options = options || {};
  512.  
  513. recovery.attempt = null; // Stores the current reconnect attempt.
  514. recovery._fn = null; // Stores the callback.
  515.  
  516. recovery['reconnect timeout'] = defaults('reconnect timeout', recovery, options);
  517. recovery.retries = defaults('retries', recovery, options);
  518. recovery.factor = defaults('factor', recovery, options);
  519. recovery.max = defaults('max', recovery, options);
  520. recovery.min = defaults('min', recovery, options);
  521. recovery.timers = new Tick(recovery);
  522. }
  523.  
  524. Recovery.prototype = new EventEmitter();
  525. Recovery.prototype.constructor = Recovery;
  526.  
  527. Recovery['reconnect timeout'] = '30 seconds'; // Maximum time to wait for an answer.
  528. Recovery.max = Infinity; // Maximum delay.
  529. Recovery.min = '500 ms'; // Minimum delay.
  530. Recovery.retries = 10; // Maximum amount of retries.
  531. Recovery.factor = 2; // Exponential back off factor.
  532.  
  533. /**
  534. * Start a new reconnect procedure.
  535. *
  536. * @returns {Recovery}
  537. * @api public
  538. */
  539. Recovery.prototype.reconnect = function reconnect() {
  540. var recovery = this;
  541.  
  542. return recovery.backoff(function backedoff(err, opts) {
  543. opts.duration = (+new Date()) - opts.start;
  544.  
  545. if (err) return recovery.emit('reconnect failed', err, opts);
  546.  
  547. recovery.emit('reconnected', opts);
  548. }, recovery.attempt);
  549. };
  550.  
  551. /**
  552. * Exponential back off algorithm for retry operations. It uses a randomized
  553. * retry so we don't DDOS our server when it goes down under pressure.
  554. *
  555. * @param {Function} fn Callback to be called after the timeout.
  556. * @param {Object} opts Options for configuring the timeout.
  557. * @returns {Recovery}
  558. * @api private
  559. */
  560. Recovery.prototype.backoff = function backoff(fn, opts) {
  561. var recovery = this;
  562.  
  563. opts = opts || recovery.attempt || {};
  564.  
  565. //
  566. // Bailout when we already have a back off process running. We shouldn't call
  567. // the callback then.
  568. //
  569. if (opts.backoff) return recovery;
  570.  
  571. opts['reconnect timeout'] = defaults('reconnect timeout', recovery, opts);
  572. opts.retries = defaults('retries', recovery, opts);
  573. opts.factor = defaults('factor', recovery, opts);
  574. opts.max = defaults('max', recovery, opts);
  575. opts.min = defaults('min', recovery, opts);
  576.  
  577. opts.start = +opts.start || +new Date();
  578. opts.duration = +opts.duration || 0;
  579. opts.attempt = +opts.attempt || 0;
  580.  
  581. //
  582. // Bailout if we are about to make too much attempts.
  583. //
  584. if (opts.attempt === opts.retries) {
  585. fn.call(recovery, new Error('Unable to recover'), opts);
  586. return recovery;
  587. }
  588.  
  589. //
  590. // Prevent duplicate back off attempts using the same options object and
  591. // increment our attempt as we're about to have another go at this thing.
  592. //
  593. opts.backoff = true;
  594. opts.attempt++;
  595.  
  596. recovery.attempt = opts;
  597.  
  598. //
  599. // Calculate the timeout, but make it randomly so we don't retry connections
  600. // at the same interval and defeat the purpose. This exponential back off is
  601. // based on the work of:
  602. //
  603. // http://dthain.blogspot.nl/2009/02/exponential-backoff-in-distributed.html
  604. //
  605. opts.scheduled = opts.attempt !== 1
  606. ? Math.min(Math.round(
  607. (Math.random() + 1) * opts.min * Math.pow(opts.factor, opts.attempt - 1)
  608. ), opts.max)
  609. : opts.min;
  610.  
  611. recovery.timers.setTimeout('reconnect', function delay() {
  612. opts.duration = (+new Date()) - opts.start;
  613. opts.backoff = false;
  614. recovery.timers.clear('reconnect, timeout');
  615.  
  616. //
  617. // Create a `one` function which can only be called once. So we can use the
  618. // same function for different types of invocations to create a much better
  619. // and usable API.
  620. //
  621. var connect = recovery._fn = one(function connect(err) {
  622. recovery.reset();
  623.  
  624. if (err) return recovery.backoff(fn, opts);
  625.  
  626. fn.call(recovery, undefined, opts);
  627. });
  628.  
  629. recovery.emit('reconnect', opts, connect);
  630. recovery.timers.setTimeout('timeout', function timeout() {
  631. var err = new Error('Failed to reconnect in a timely manner');
  632. opts.duration = (+new Date()) - opts.start;
  633.  
  634. recovery.emit('reconnect timeout', err, opts);
  635. connect(err);
  636. }, opts['reconnect timeout']);
  637. }, opts.scheduled);
  638.  
  639. //
  640. // Emit a `reconnecting` event with current reconnect options. This allows
  641. // them to update the UI and provide their users with feedback.
  642. //
  643. recovery.emit('reconnect scheduled', opts);
  644.  
  645. return recovery;
  646. };
  647.  
  648. /**
  649. * Check if the reconnection process is currently reconnecting.
  650. *
  651. * @returns {Boolean}
  652. * @api public
  653. */
  654. Recovery.prototype.reconnecting = function reconnecting() {
  655. return !!this.attempt;
  656. };
  657.  
  658. /**
  659. * Tell our reconnection procedure that we're passed.
  660. *
  661. * @param {Error} err Reconnection failed.
  662. * @returns {Recovery}
  663. * @api public
  664. */
  665. Recovery.prototype.reconnected = function reconnected(err) {
  666. if (this._fn) this._fn(err);
  667. return this;
  668. };
  669.  
  670. /**
  671. * Reset the reconnection attempt so it can be re-used again.
  672. *
  673. * @returns {Recovery}
  674. * @api public
  675. */
  676. Recovery.prototype.reset = function reset() {
  677. this._fn = this.attempt = null;
  678. this.timers.clear('reconnect, timeout');
  679.  
  680. return this;
  681. };
  682.  
  683. /**
  684. * Clean up the instance.
  685. *
  686. * @type {Function}
  687. * @returns {Boolean}
  688. * @api public
  689. */
  690. Recovery.prototype.destroy = destroy('timers attempt _fn');
  691.  
  692. //
  693. // Expose the module.
  694. //
  695. module.exports = Recovery;
  696.  
  697. },{"demolish":1,"eventemitter3":3,"millisecond":6,"one-time":7,"tick-tock":8}],6:[function(_dereq_,module,exports){
  698. 'use strict';
  699.  
  700. var regex = new RegExp('^((?:\\d+)?\\.?\\d+) *('+ [
  701. 'milliseconds?',
  702. 'msecs?',
  703. 'ms',
  704. 'seconds?',
  705. 'secs?',
  706. 's',
  707. 'minutes?',
  708. 'mins?',
  709. 'm',
  710. 'hours?',
  711. 'hrs?',
  712. 'h',
  713. 'days?',
  714. 'd',
  715. 'weeks?',
  716. 'wks?',
  717. 'w',
  718. 'years?',
  719. 'yrs?',
  720. 'y'
  721. ].join('|') +')?$', 'i');
  722.  
  723. var second = 1000
  724. , minute = second * 60
  725. , hour = minute * 60
  726. , day = hour * 24
  727. , week = day * 7
  728. , year = day * 365;
  729.  
  730. /**
  731. * Parse a time string and return the number value of it.
  732. *
  733. * @param {String} ms Time string.
  734. * @returns {Number}
  735. * @api private
  736. */
  737. module.exports = function millisecond(ms) {
  738. if ('string' !== typeof ms || '0' === ms || +ms) return +ms;
  739.  
  740. var match = regex.exec(ms)
  741. , amount;
  742.  
  743. if (!match) return 0;
  744.  
  745. amount = parseFloat(match[1]);
  746.  
  747. switch (match[2].toLowerCase()) {
  748. case 'years':
  749. case 'year':
  750. case 'yrs':
  751. case 'yr':
  752. case 'y':
  753. return amount * year;
  754.  
  755. case 'weeks':
  756. case 'week':
  757. case 'wks':
  758. case 'wk':
  759. case 'w':
  760. return amount * week;
  761.  
  762. case 'days':
  763. case 'day':
  764. case 'd':
  765. return amount * day;
  766.  
  767. case 'hours':
  768. case 'hour':
  769. case 'hrs':
  770. case 'hr':
  771. case 'h':
  772. return amount * hour;
  773.  
  774. case 'minutes':
  775. case 'minute':
  776. case 'mins':
  777. case 'min':
  778. case 'm':
  779. return amount * minute;
  780.  
  781. case 'seconds':
  782. case 'second':
  783. case 'secs':
  784. case 'sec':
  785. case 's':
  786. return amount * second;
  787.  
  788. default:
  789. return amount;
  790. }
  791. };
  792.  
  793. },{}],7:[function(_dereq_,module,exports){
  794. 'use strict';
  795.  
  796. /**
  797. * Wrap callbacks to prevent double execution.
  798. *
  799. * @param {Function} fn Function that should only be called once.
  800. * @returns {Function} A wrapped callback which prevents execution.
  801. * @api public
  802. */
  803. module.exports = function one(fn) {
  804. var called = 0
  805. , value;
  806.  
  807. /**
  808. * The function that prevents double execution.
  809. *
  810. * @api private
  811. */
  812. function onetime() {
  813. if (called) return value;
  814.  
  815. called = 1;
  816. value = fn.apply(this, arguments);
  817. fn = null;
  818.  
  819. return value;
  820. }
  821.  
  822. //
  823. // To make debugging more easy we want to use the name of the supplied
  824. // function. So when you look at the functions that are assigned to event
  825. // listeners you don't see a load of `onetime` functions but actually the
  826. // names of the functions that this module will call.
  827. //
  828. onetime.displayName = fn.displayName || fn.name || onetime.displayName || onetime.name;
  829. return onetime;
  830. };
  831.  
  832. },{}],8:[function(_dereq_,module,exports){
  833. 'use strict';
  834.  
  835. var has = Object.prototype.hasOwnProperty
  836. , ms = _dereq_('millisecond');
  837.  
  838. /**
  839. * Timer instance.
  840. *
  841. * @constructor
  842. * @param {Object} timer New timer instance.
  843. * @param {Function} clear Clears the timer instance.
  844. * @param {Function} duration Duration of the timer.
  845. * @param {Function} fn The functions that need to be executed.
  846. * @api private
  847. */
  848. function Timer(timer, clear, duration, fn) {
  849. this.start = +(new Date());
  850. this.duration = duration;
  851. this.clear = clear;
  852. this.timer = timer;
  853. this.fns = [fn];
  854. }
  855.  
  856. /**
  857. * Calculate the time left for a given timer.
  858. *
  859. * @returns {Number} Time in milliseconds.
  860. * @api public
  861. */
  862. Timer.prototype.remaining = function remaining() {
  863. return this.duration - this.taken();
  864. };
  865.  
  866. /**
  867. * Calculate the amount of time it has taken since we've set the timer.
  868. *
  869. * @returns {Number}
  870. * @api public
  871. */
  872. Timer.prototype.taken = function taken() {
  873. return +(new Date()) - this.start;
  874. };
  875.  
  876. /**
  877. * Custom wrappers for the various of clear{whatever} functions. We cannot
  878. * invoke them directly as this will cause thrown errors in Google Chrome with
  879. * an Illegal Invocation Error
  880. *
  881. * @see #2
  882. * @type {Function}
  883. * @api private
  884. */
  885. function unsetTimeout(id) { clearTimeout(id); }
  886. function unsetInterval(id) { clearInterval(id); }
  887. function unsetImmediate(id) { clearImmediate(id); }
  888.  
  889. /**
  890. * Simple timer management.
  891. *
  892. * @constructor
  893. * @param {Mixed} context Context of the callbacks that we execute.
  894. * @api public
  895. */
  896. function Tick(context) {
  897. if (!(this instanceof Tick)) return new Tick(context);
  898.  
  899. this.timers = {};
  900. this.context = context || this;
  901. }
  902.  
  903. /**
  904. * Return a function which will just iterate over all assigned callbacks and
  905. * optionally clear the timers from memory if needed.
  906. *
  907. * @param {String} name Name of the timer we need to execute.
  908. * @param {Boolean} clear Also clear from memory.
  909. * @returns {Function}
  910. * @api private
  911. */
  912. Tick.prototype.tock = function ticktock(name, clear) {
  913. var tock = this;
  914.  
  915. return function tickedtock() {
  916. if (!(name in tock.timers)) return;
  917.  
  918. var timer = tock.timers[name]
  919. , fns = timer.fns.slice()
  920. , l = fns.length
  921. , i = 0;
  922.  
  923. if (clear) tock.clear(name);
  924. else tock.start = +new Date();
  925.  
  926. for (; i < l; i++) {
  927. fns[i].call(tock.context);
  928. }
  929. };
  930. };
  931.  
  932. /**
  933. * Add a new timeout.
  934. *
  935. * @param {String} name Name of the timer.
  936. * @param {Function} fn Completion callback.
  937. * @param {Mixed} time Duration of the timer.
  938. * @returns {Tick}
  939. * @api public
  940. */
  941. Tick.prototype.setTimeout = function timeout(name, fn, time) {
  942. var tick = this
  943. , tock;
  944.  
  945. if (tick.timers[name]) {
  946. tick.timers[name].fns.push(fn);
  947. return tick;
  948. }
  949.  
  950. tock = ms(time);
  951. tick.timers[name] = new Timer(
  952. setTimeout(tick.tock(name, true), ms(time)),
  953. unsetTimeout,
  954. tock,
  955. fn
  956. );
  957.  
  958. return tick;
  959. };
  960.  
  961. /**
  962. * Add a new interval.
  963. *
  964. * @param {String} name Name of the timer.
  965. * @param {Function} fn Completion callback.
  966. * @param {Mixed} time Interval of the timer.
  967. * @returns {Tick}
  968. * @api public
  969. */
  970. Tick.prototype.setInterval = function interval(name, fn, time) {
  971. var tick = this
  972. , tock;
  973.  
  974. if (tick.timers[name]) {
  975. tick.timers[name].fns.push(fn);
  976. return tick;
  977. }
  978.  
  979. tock = ms(time);
  980. tick.timers[name] = new Timer(
  981. setInterval(tick.tock(name), ms(time)),
  982. unsetInterval,
  983. tock,
  984. fn
  985. );
  986.  
  987. return tick;
  988. };
  989.  
  990. /**
  991. * Add a new setImmediate.
  992. *
  993. * @param {String} name Name of the timer.
  994. * @param {Function} fn Completion callback.
  995. * @returns {Tick}
  996. * @api public
  997. */
  998. Tick.prototype.setImmediate = function immediate(name, fn) {
  999. var tick = this;
  1000.  
  1001. if ('function' !== typeof setImmediate) return tick.setTimeout(name, fn, 0);
  1002.  
  1003. if (tick.timers[name]) {
  1004. tick.timers[name].fns.push(fn);
  1005. return tick;
  1006. }
  1007.  
  1008. tick.timers[name] = new Timer(
  1009. setImmediate(tick.tock(name, true)),
  1010. unsetImmediate,
  1011. 0,
  1012. fn
  1013. );
  1014.  
  1015. return tick;
  1016. };
  1017.  
  1018. /**
  1019. * Check if we have a timer set.
  1020. *
  1021. * @param {String} name
  1022. * @returns {Boolean}
  1023. * @api public
  1024. */
  1025. Tick.prototype.active = function active(name) {
  1026. return name in this.timers;
  1027. };
  1028.  
  1029. /**
  1030. * Properly clean up all timeout references. If no arguments are supplied we
  1031. * will attempt to clear every single timer that is present.
  1032. *
  1033. * @param {Arguments} ..args.. The names of the timeouts we need to clear
  1034. * @returns {Tick}
  1035. * @api public
  1036. */
  1037. Tick.prototype.clear = function clear() {
  1038. var args = arguments.length ? arguments : []
  1039. , tick = this
  1040. , timer, i, l;
  1041.  
  1042. if (args.length === 1 && 'string' === typeof args[0]) {
  1043. args = args[0].split(/[, ]+/);
  1044. }
  1045.  
  1046. if (!args.length) {
  1047. for (timer in tick.timers) {
  1048. if (has.call(tick.timers, timer)) args.push(timer);
  1049. }
  1050. }
  1051.  
  1052. for (i = 0, l = args.length; i < l; i++) {
  1053. timer = tick.timers[args[i]];
  1054.  
  1055. if (!timer) continue;
  1056. timer.clear(timer.timer);
  1057.  
  1058. timer.fns = timer.timer = timer.clear = null;
  1059. delete tick.timers[args[i]];
  1060. }
  1061.  
  1062. return tick;
  1063. };
  1064.  
  1065. /**
  1066. * Adjust a timeout or interval to a new duration.
  1067. *
  1068. * @returns {Tick}
  1069. * @api public
  1070. */
  1071. Tick.prototype.adjust = function adjust(name, time) {
  1072. var interval
  1073. , tick = this
  1074. , tock = ms(time)
  1075. , timer = tick.timers[name];
  1076.  
  1077. if (!timer) return tick;
  1078.  
  1079. interval = timer.clear === unsetInterval;
  1080. timer.clear(timer.timer);
  1081. timer.start = +(new Date());
  1082. timer.duration = tock;
  1083. timer.timer = (interval ? setInterval : setTimeout)(tick.tock(name, !interval), tock);
  1084.  
  1085. return tick;
  1086. };
  1087.  
  1088. /**
  1089. * We will no longer use this module, prepare your self for global cleanups.
  1090. *
  1091. * @returns {Boolean}
  1092. * @api public
  1093. */
  1094. Tick.prototype.end = Tick.prototype.destroy = function end() {
  1095. if (!this.context) return false;
  1096.  
  1097. this.clear();
  1098. this.context = this.timers = null;
  1099.  
  1100. return true;
  1101. };
  1102.  
  1103. //
  1104. // Expose the timer factory.
  1105. //
  1106. Tick.Timer = Timer;
  1107. module.exports = Tick;
  1108.  
  1109. },{"millisecond":9}],9:[function(_dereq_,module,exports){
  1110. arguments[4][6][0].apply(exports,arguments)
  1111. },{"dup":6}],10:[function(_dereq_,module,exports){
  1112. 'use strict';
  1113.  
  1114. var required = _dereq_('requires-port')
  1115. , lolcation = _dereq_('./lolcation')
  1116. , qs = _dereq_('querystringify')
  1117. , relativere = /^\/(?!\/)/;
  1118.  
  1119. /**
  1120. * These are the parse instructions for the URL parsers, it informs the parser
  1121. * about:
  1122. *
  1123. * 0. The char it Needs to parse, if it's a string it should be done using
  1124. * indexOf, RegExp using exec and NaN means set as current value.
  1125. * 1. The property we should set when parsing this value.
  1126. * 2. Indication if it's backwards or forward parsing, when set as number it's
  1127. * the value of extra chars that should be split off.
  1128. * 3. Inherit from location if non existing in the parser.
  1129. * 4. `toLowerCase` the resulting value.
  1130. */
  1131. var instructions = [
  1132. ['#', 'hash'], // Extract from the back.
  1133. ['?', 'query'], // Extract from the back.
  1134. ['//', 'protocol', 2, 1, 1], // Extract from the front.
  1135. ['/', 'pathname'], // Extract from the back.
  1136. ['@', 'auth', 1], // Extract from the front.
  1137. [NaN, 'host', undefined, 1, 1], // Set left over value.
  1138. [/\:(\d+)$/, 'port'], // RegExp the back.
  1139. [NaN, 'hostname', undefined, 1, 1] // Set left over.
  1140. ];
  1141.  
  1142. /**
  1143. * The actual URL instance. Instead of returning an object we've opted-in to
  1144. * create an actual constructor as it's much more memory efficient and
  1145. * faster and it pleases my CDO.
  1146. *
  1147. * @constructor
  1148. * @param {String} address URL we want to parse.
  1149. * @param {Boolean|function} parser Parser for the query string.
  1150. * @param {Object} location Location defaults for relative paths.
  1151. * @api public
  1152. */
  1153. function URL(address, location, parser) {
  1154. if (!(this instanceof URL)) {
  1155. return new URL(address, location, parser);
  1156. }
  1157.  
  1158. var relative = relativere.test(address)
  1159. , parse, instruction, index, key
  1160. , type = typeof location
  1161. , url = this
  1162. , i = 0;
  1163.  
  1164. //
  1165. // The following if statements allows this module two have compatibility with
  1166. // 2 different API:
  1167. //
  1168. // 1. Node.js's `url.parse` api which accepts a URL, boolean as arguments
  1169. // where the boolean indicates that the query string should also be parsed.
  1170. //
  1171. // 2. The `URL` interface of the browser which accepts a URL, object as
  1172. // arguments. The supplied object will be used as default values / fall-back
  1173. // for relative paths.
  1174. //
  1175. if ('object' !== type && 'string' !== type) {
  1176. parser = location;
  1177. location = null;
  1178. }
  1179.  
  1180. if (parser && 'function' !== typeof parser) {
  1181. parser = qs.parse;
  1182. }
  1183.  
  1184. location = lolcation(location);
  1185.  
  1186. for (; i < instructions.length; i++) {
  1187. instruction = instructions[i];
  1188. parse = instruction[0];
  1189. key = instruction[1];
  1190.  
  1191. if (parse !== parse) {
  1192. url[key] = address;
  1193. } else if ('string' === typeof parse) {
  1194. if (~(index = address.indexOf(parse))) {
  1195. if ('number' === typeof instruction[2]) {
  1196. url[key] = address.slice(0, index);
  1197. address = address.slice(index + instruction[2]);
  1198. } else {
  1199. url[key] = address.slice(index);
  1200. address = address.slice(0, index);
  1201. }
  1202. }
  1203. } else if (index = parse.exec(address)) {
  1204. url[key] = index[1];
  1205. address = address.slice(0, address.length - index[0].length);
  1206. }
  1207.  
  1208. url[key] = url[key] || (instruction[3] || ('port' === key && relative) ? location[key] || '' : '');
  1209.  
  1210. //
  1211. // Hostname, host and protocol should be lowercased so they can be used to
  1212. // create a proper `origin`.
  1213. //
  1214. if (instruction[4]) {
  1215. url[key] = url[key].toLowerCase();
  1216. }
  1217. }
  1218.  
  1219. //
  1220. // Also parse the supplied query string in to an object. If we're supplied
  1221. // with a custom parser as function use that instead of the default build-in
  1222. // parser.
  1223. //
  1224. if (parser) url.query = parser(url.query);
  1225.  
  1226. //
  1227. // We should not add port numbers if they are already the default port number
  1228. // for a given protocol. As the host also contains the port number we're going
  1229. // override it with the hostname which contains no port number.
  1230. //
  1231. if (!required(url.port, url.protocol)) {
  1232. url.host = url.hostname;
  1233. url.port = '';
  1234. }
  1235.  
  1236. //
  1237. // Parse down the `auth` for the username and password.
  1238. //
  1239. url.username = url.password = '';
  1240. if (url.auth) {
  1241. instruction = url.auth.split(':');
  1242. url.username = instruction[0] || '';
  1243. url.password = instruction[1] || '';
  1244. }
  1245.  
  1246. //
  1247. // The href is just the compiled result.
  1248. //
  1249. url.href = url.toString();
  1250. }
  1251.  
  1252. /**
  1253. * This is convenience method for changing properties in the URL instance to
  1254. * insure that they all propagate correctly.
  1255. *
  1256. * @param {String} prop Property we need to adjust.
  1257. * @param {Mixed} value The newly assigned value.
  1258. * @returns {URL}
  1259. * @api public
  1260. */
  1261. URL.prototype.set = function set(part, value, fn) {
  1262. var url = this;
  1263.  
  1264. if ('query' === part) {
  1265. if ('string' === typeof value && value.length) {
  1266. value = (fn || qs.parse)(value);
  1267. }
  1268.  
  1269. url[part] = value;
  1270. } else if ('port' === part) {
  1271. url[part] = value;
  1272.  
  1273. if (!required(value, url.protocol)) {
  1274. url.host = url.hostname;
  1275. url[part] = '';
  1276. } else if (value) {
  1277. url.host = url.hostname +':'+ value;
  1278. }
  1279. } else if ('hostname' === part) {
  1280. url[part] = value;
  1281.  
  1282. if (url.port) value += ':'+ url.port;
  1283. url.host = value;
  1284. } else if ('host' === part) {
  1285. url[part] = value;
  1286.  
  1287. if (/\:\d+/.test(value)) {
  1288. value = value.split(':');
  1289. url.hostname = value[0];
  1290. url.port = value[1];
  1291. }
  1292. } else {
  1293. url[part] = value;
  1294. }
  1295.  
  1296. url.href = url.toString();
  1297. return url;
  1298. };
  1299.  
  1300. /**
  1301. * Transform the properties back in to a valid and full URL string.
  1302. *
  1303. * @param {Function} stringify Optional query stringify function.
  1304. * @returns {String}
  1305. * @api public
  1306. */
  1307. URL.prototype.toString = function toString(stringify) {
  1308. if (!stringify || 'function' !== typeof stringify) stringify = qs.stringify;
  1309.  
  1310. var query
  1311. , url = this
  1312. , result = url.protocol +'//';
  1313.  
  1314. if (url.username) {
  1315. result += url.username;
  1316. if (url.password) result += ':'+ url.password;
  1317. result += '@';
  1318. }
  1319.  
  1320. result += url.hostname;
  1321. if (url.port) result += ':'+ url.port;
  1322.  
  1323. result += url.pathname;
  1324.  
  1325. query = 'object' === typeof url.query ? stringify(url.query) : url.query;
  1326. if (query) result += '?' !== query.charAt(0) ? '?'+ query : query;
  1327.  
  1328. if (url.hash) result += url.hash;
  1329.  
  1330. return result;
  1331. };
  1332.  
  1333. //
  1334. // Expose the URL parser and some additional properties that might be useful for
  1335. // others.
  1336. //
  1337. URL.qs = qs;
  1338. URL.location = lolcation;
  1339. module.exports = URL;
  1340.  
  1341. },{"./lolcation":11,"querystringify":4,"requires-port":12}],11:[function(_dereq_,module,exports){
  1342. (function (global){
  1343. 'use strict';
  1344.  
  1345. /**
  1346. * These properties should not be copied or inherited from. This is only needed
  1347. * for all non blob URL's as the a blob URL does not include a hash, only the
  1348. * origin.
  1349. *
  1350. * @type {Object}
  1351. * @private
  1352. */
  1353. var ignore = { hash: 1, query: 1 }
  1354. , URL;
  1355.  
  1356. /**
  1357. * The location object differs when your code is loaded through a normal page,
  1358. * Worker or through a worker using a blob. And with the blobble begins the
  1359. * trouble as the location object will contain the URL of the blob, not the
  1360. * location of the page where our code is loaded in. The actual origin is
  1361. * encoded in the `pathname` so we can thankfully generate a good "default"
  1362. * location from it so we can generate proper relative URL's again.
  1363. *
  1364. * @param {Object} loc Optional default location object.
  1365. * @returns {Object} lolcation object.
  1366. * @api public
  1367. */
  1368. module.exports = function lolcation(loc) {
  1369. loc = loc || global.location || {};
  1370. URL = URL || _dereq_('./');
  1371.  
  1372. var finaldestination = {}
  1373. , type = typeof loc
  1374. , key;
  1375.  
  1376. if ('blob:' === loc.protocol) {
  1377. finaldestination = new URL(unescape(loc.pathname), {});
  1378. } else if ('string' === type) {
  1379. finaldestination = new URL(loc, {});
  1380. for (key in ignore) delete finaldestination[key];
  1381. } else if ('object' === type) for (key in loc) {
  1382. if (key in ignore) continue;
  1383. finaldestination[key] = loc[key];
  1384. }
  1385.  
  1386. return finaldestination;
  1387. };
  1388.  
  1389. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  1390. },{"./":10}],12:[function(_dereq_,module,exports){
  1391. 'use strict';
  1392.  
  1393. /**
  1394. * Check if we're required to add a port number.
  1395. *
  1396. * @see https://url.spec.whatwg.org/#default-port
  1397. * @param {Number|String} port Port number we need to check
  1398. * @param {String} protocol Protocol we need to check against.
  1399. * @returns {Boolean} Is it a default port for the given protocol
  1400. * @api private
  1401. */
  1402. module.exports = function required(port, protocol) {
  1403. protocol = protocol.split(':')[0];
  1404. port = +port;
  1405.  
  1406. if (!port) return false;
  1407.  
  1408. switch (protocol) {
  1409. case 'http':
  1410. case 'ws':
  1411. return port !== 80;
  1412.  
  1413. case 'https':
  1414. case 'wss':
  1415. return port !== 443;
  1416.  
  1417. case 'ftp':
  1418. return port !== 22;
  1419.  
  1420. case 'gopher':
  1421. return port !== 70;
  1422.  
  1423. case 'file':
  1424. return false;
  1425. }
  1426.  
  1427. return port !== 0;
  1428. };
  1429.  
  1430. },{}],13:[function(_dereq_,module,exports){
  1431. 'use strict';
  1432.  
  1433. var alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_'.split('')
  1434. , length = 64
  1435. , map = {}
  1436. , seed = 0
  1437. , i = 0
  1438. , prev;
  1439.  
  1440. /**
  1441. * Return a string representing the specified number.
  1442. *
  1443. * @param {Number} num The number to convert.
  1444. * @returns {String} The string representation of the number.
  1445. * @api public
  1446. */
  1447. function encode(num) {
  1448. var encoded = '';
  1449.  
  1450. do {
  1451. encoded = alphabet[num % length] + encoded;
  1452. num = Math.floor(num / length);
  1453. } while (num > 0);
  1454.  
  1455. return encoded;
  1456. }
  1457.  
  1458. /**
  1459. * Return the integer value specified by the given string.
  1460. *
  1461. * @param {String} str The string to convert.
  1462. * @returns {Number} The integer value represented by the string.
  1463. * @api public
  1464. */
  1465. function decode(str) {
  1466. var decoded = 0;
  1467.  
  1468. for (i = 0; i < str.length; i++) {
  1469. decoded = decoded * length + map[str.charAt(i)];
  1470. }
  1471.  
  1472. return decoded;
  1473. }
  1474.  
  1475. /**
  1476. * Yeast: A tiny growing id generator.
  1477. *
  1478. * @returns {String} A unique id.
  1479. * @api public
  1480. */
  1481. function yeast() {
  1482. var now = encode(+new Date());
  1483.  
  1484. if (now !== prev) return seed = 0, prev = now;
  1485. return now +'.'+ encode(seed++);
  1486. }
  1487.  
  1488. //
  1489. // Map each character to its index.
  1490. //
  1491. for (; i < length; i++) map[alphabet[i]] = i;
  1492.  
  1493. //
  1494. // Expose the `yeast`, `encode` and `decode` functions.
  1495. //
  1496. yeast.encode = encode;
  1497. yeast.decode = decode;
  1498. module.exports = yeast;
  1499.  
  1500. },{}],14:[function(_dereq_,module,exports){
  1501. /*globals require, define */
  1502. 'use strict';
  1503.  
  1504. var EventEmitter = _dereq_('eventemitter3')
  1505. , TickTock = _dereq_('tick-tock')
  1506. , Recovery = _dereq_('recovery')
  1507. , qs = _dereq_('querystringify')
  1508. , destroy = _dereq_('demolish')
  1509. , yeast = _dereq_('yeast')
  1510. , u2028 = /\u2028/g
  1511. , u2029 = /\u2029/g;
  1512.  
  1513. /**
  1514. * Context assertion, ensure that some of our public Primus methods are called
  1515. * with the correct context to ensure that
  1516. *
  1517. * @param {Primus} self The context of the function.
  1518. * @param {String} method The method name.
  1519. * @api private
  1520. */
  1521. function context(self, method) {
  1522. if (self instanceof Primus) return;
  1523.  
  1524. var failure = new Error('Primus#'+ method + '\'s context should called with a Primus instance');
  1525.  
  1526. if ('function' !== typeof self.listeners || !self.listeners('error').length) {
  1527. throw failure;
  1528. }
  1529.  
  1530. self.emit('error', failure);
  1531. }
  1532.  
  1533. //
  1534. // Sets the default connection URL, it uses the default origin of the browser
  1535. // when supported but degrades for older browsers. In Node.js, we cannot guess
  1536. // where the user wants to connect to, so we just default to localhost.
  1537. //
  1538. var defaultUrl;
  1539.  
  1540. try {
  1541. if (location.origin) {
  1542. defaultUrl = location.origin;
  1543. } else {
  1544. defaultUrl = location.protocol +'//'+ location.hostname + (location.port ? ':'+ location.port : '');
  1545. }
  1546. } catch (e) {
  1547. defaultUrl = 'http://127.0.0.1';
  1548. }
  1549.  
  1550. /**
  1551. * Primus is a real-time library agnostic framework for establishing real-time
  1552. * connections with servers.
  1553. *
  1554. * Options:
  1555. * - reconnect, configuration for the reconnect process.
  1556. * - manual, don't automatically call `.open` to start the connection.
  1557. * - websockets, force the use of WebSockets, even when you should avoid them.
  1558. * - timeout, connect timeout, server didn't respond in a timely manner.
  1559. * - ping, The heartbeat interval for sending a ping packet to the server.
  1560. * - pong, The heartbeat timeout for receiving a response to the ping.
  1561. * - network, Use network events as leading method for network connection drops.
  1562. * - strategy, Reconnection strategies.
  1563. * - transport, Transport options.
  1564. * - url, uri, The URL to use connect with the server.
  1565. *
  1566. * @constructor
  1567. * @param {String} url The URL of your server.
  1568. * @param {Object} options The configuration.
  1569. * @api public
  1570. */
  1571. function Primus(url, options) {
  1572. if (!(this instanceof Primus)) return new Primus(url, options);
  1573. if ('function' !== typeof this.client) {
  1574. var message = 'The client library has not been compiled correctly, ' +
  1575. 'see https://github.com/primus/primus#client-library for more details';
  1576. return this.critical(new Error(message));
  1577. }
  1578.  
  1579. if ('object' === typeof url) {
  1580. options = url;
  1581. url = options.url || options.uri || defaultUrl;
  1582. } else {
  1583. options = options || {};
  1584. }
  1585.  
  1586. var primus = this;
  1587.  
  1588. // The maximum number of messages that can be placed in queue.
  1589. options.queueSize = 'queueSize' in options ? options.queueSize : Infinity;
  1590.  
  1591. // Connection timeout duration.
  1592. options.timeout = 'timeout' in options ? options.timeout : 10e3;
  1593.  
  1594. // Stores the back off configuration.
  1595. options.reconnect = 'reconnect' in options ? options.reconnect : {};
  1596.  
  1597. // Heartbeat ping interval.
  1598. options.ping = 'ping' in options ? options.ping : 25000;
  1599.  
  1600. // Heartbeat pong response timeout.
  1601. options.pong = 'pong' in options ? options.pong : 10e3;
  1602.  
  1603. // Reconnect strategies.
  1604. options.strategy = 'strategy' in options ? options.strategy : [];
  1605.  
  1606. // Custom transport options.
  1607. options.transport = 'transport' in options ? options.transport : {};
  1608.  
  1609. primus.buffer = []; // Stores premature send data.
  1610. primus.writable = true; // Silly stream compatibility.
  1611. primus.readable = true; // Silly stream compatibility.
  1612. primus.url = primus.parse(url || defaultUrl); // Parse the URL to a readable format.
  1613. primus.readyState = Primus.CLOSED; // The readyState of the connection.
  1614. primus.options = options; // Reference to the supplied options.
  1615. primus.timers = new TickTock(this); // Contains all our timers.
  1616. primus.socket = null; // Reference to the internal connection.
  1617. primus.latency = 0; // Latency between messages.
  1618. primus.disconnect = false; // Did we receive a disconnect packet?
  1619. primus.transport = options.transport; // Transport options.
  1620. primus.transformers = { // Message transformers.
  1621. outgoing: [],
  1622. incoming: []
  1623. };
  1624.  
  1625. //
  1626. // Create our reconnection instance.
  1627. //
  1628. primus.recovery = new Recovery(options.reconnect);
  1629.  
  1630. //
  1631. // Parse the reconnection strategy. It can have the following strategies:
  1632. //
  1633. // - timeout: Reconnect when we have a network timeout.
  1634. // - disconnect: Reconnect when we have an unexpected disconnect.
  1635. // - online: Reconnect when we're back online.
  1636. //
  1637. if ('string' === typeof options.strategy) {
  1638. options.strategy = options.strategy.split(/\s?\,\s?/g);
  1639. }
  1640.  
  1641. if (false === options.strategy) {
  1642. //
  1643. // Strategies are disabled, but we still need an empty array to join it in
  1644. // to nothing.
  1645. //
  1646. options.strategy = [];
  1647. } else if (!options.strategy.length) {
  1648. options.strategy.push('disconnect', 'online');
  1649.  
  1650. //
  1651. // Timeout based reconnection should only be enabled conditionally. When
  1652. // authorization is enabled it could trigger.
  1653. //
  1654. if (!this.authorization) options.strategy.push('timeout');
  1655. }
  1656.  
  1657. options.strategy = options.strategy.join(',').toLowerCase();
  1658.  
  1659. //
  1660. // Force the use of WebSockets, even when we've detected some potential
  1661. // broken WebSocket implementation.
  1662. //
  1663. if ('websockets' in options) {
  1664. primus.AVOID_WEBSOCKETS = !options.websockets;
  1665. }
  1666.  
  1667. //
  1668. // Force or disable the use of NETWORK events as leading client side
  1669. // disconnection detection.
  1670. //
  1671. if ('network' in options) {
  1672. primus.NETWORK_EVENTS = options.network;
  1673. }
  1674.  
  1675. //
  1676. // Check if the user wants to manually initialise a connection. If they don't,
  1677. // we want to do it after a really small timeout so we give the users enough
  1678. // time to listen for `error` events etc.
  1679. //
  1680. if (!options.manual) primus.timers.setTimeout('open', function open() {
  1681. primus.timers.clear('open');
  1682. primus.open();
  1683. }, 0);
  1684.  
  1685. primus.initialise(options);
  1686. }
  1687.  
  1688. /**
  1689. * Simple require wrapper to make browserify, node and require.js play nice.
  1690. *
  1691. * @param {String} name The module to require.
  1692. * @returns {Object|Undefined} The module that we required.
  1693. * @api private
  1694. */
  1695. Primus.require = function requires(name) {
  1696. if ('function' !== typeof _dereq_) return undefined;
  1697.  
  1698. return !('function' === typeof define && define.amd)
  1699. ? _dereq_(name)
  1700. : undefined;
  1701. };
  1702.  
  1703. //
  1704. // It's possible that we're running in Node.js or in a Node.js compatible
  1705. // environment. In this cases we inherit from the Stream base class.
  1706. //
  1707. var Stream;
  1708.  
  1709. try {
  1710. Primus.Stream = Stream = Primus.require('stream');
  1711.  
  1712. //
  1713. // Normally inheritance is done in the same way as we do in our catch
  1714. // statement. But due to changes to the EventEmitter interface in Node 0.10
  1715. // this will trigger annoying memory leak warnings and other potential issues
  1716. // outlined in the issue linked below.
  1717. //
  1718. // @see https://github.com/joyent/node/issues/4971
  1719. //
  1720. Primus.require('util').inherits(Primus, Stream);
  1721. } catch (e) {
  1722. Primus.Stream = EventEmitter;
  1723. Primus.prototype = new EventEmitter();
  1724. }
  1725.  
  1726. /**
  1727. * Primus readyStates, used internally to set the correct ready state.
  1728. *
  1729. * @type {Number}
  1730. * @private
  1731. */
  1732. Primus.OPENING = 1; // We're opening the connection.
  1733. Primus.CLOSED = 2; // No active connection.
  1734. Primus.OPEN = 3; // The connection is open.
  1735.  
  1736. /**
  1737. * Are we working with a potentially broken WebSockets implementation? This
  1738. * boolean can be used by transformers to remove `WebSockets` from their
  1739. * supported transports.
  1740. *
  1741. * @type {Boolean}
  1742. * @private
  1743. */
  1744. Primus.prototype.AVOID_WEBSOCKETS = false;
  1745.  
  1746. /**
  1747. * Some browsers support registering emitting `online` and `offline` events when
  1748. * the connection has been dropped on the client. We're going to detect it in
  1749. * a simple `try {} catch (e) {}` statement so we don't have to do complicated
  1750. * feature detection.
  1751. *
  1752. * @type {Boolean}
  1753. * @private
  1754. */
  1755. Primus.prototype.NETWORK_EVENTS = false;
  1756. Primus.prototype.online = true;
  1757.  
  1758. try {
  1759. if (
  1760. Primus.prototype.NETWORK_EVENTS = 'onLine' in navigator
  1761. && (window.addEventListener || document.body.attachEvent)
  1762. ) {
  1763. if (!navigator.onLine) {
  1764. Primus.prototype.online = false;
  1765. }
  1766. }
  1767. } catch (e) { }
  1768.  
  1769. /**
  1770. * The Ark contains all our plugins definitions. It's namespaced by
  1771. * name => plugin.
  1772. *
  1773. * @type {Object}
  1774. * @private
  1775. */
  1776. Primus.prototype.ark = {};
  1777.  
  1778. /**
  1779. * Simple emit wrapper that returns a function that emits an event once it's
  1780. * called. This makes it easier for transports to emit specific events.
  1781. *
  1782. * @returns {Function} A function that will emit the event when called.
  1783. * @api public
  1784. */
  1785. Primus.prototype.emits = _dereq_('emits');
  1786.  
  1787. /**
  1788. * A small wrapper around `emits` to add a default parser when one is not
  1789. * supplied. The default parser will defer the emission of the event to make
  1790. * sure that the event is emitted at the correct time.
  1791. *
  1792. * @returns {Function} A function that will emit the event when called.
  1793. * @api private
  1794. */
  1795. Primus.prototype.trigger = function trigger() {
  1796. for (var i = 0, l = arguments.length, args = new Array(l); i < l; i++) {
  1797. args[i] = arguments[i];
  1798. }
  1799.  
  1800. if ('function' !== typeof args[l - 1]) args.push(function defer(next) {
  1801. setTimeout(next, 0);
  1802. });
  1803.  
  1804. return this.emits.apply(this, args);
  1805. };
  1806.  
  1807. /**
  1808. * Return the given plugin.
  1809. *
  1810. * @param {String} name The name of the plugin.
  1811. * @returns {Object|undefined} The plugin or undefined.
  1812. * @api public
  1813. */
  1814. Primus.prototype.plugin = function plugin(name) {
  1815. context(this, 'plugin');
  1816.  
  1817. if (name) return this.ark[name];
  1818.  
  1819. var plugins = {};
  1820.  
  1821. for (name in this.ark) {
  1822. plugins[name] = this.ark[name];
  1823. }
  1824.  
  1825. return plugins;
  1826. };
  1827.  
  1828. /**
  1829. * Checks if the given event is an emitted event by Primus.
  1830. *
  1831. * @param {String} evt The event name.
  1832. * @returns {Boolean} Indication of the event is reserved for internal use.
  1833. * @api public
  1834. */
  1835. Primus.prototype.reserved = function reserved(evt) {
  1836. return (/^(incoming|outgoing)::/).test(evt)
  1837. || evt in this.reserved.events;
  1838. };
  1839.  
  1840. /**
  1841. * The actual events that are used by the client.
  1842. *
  1843. * @type {Object}
  1844. * @public
  1845. */
  1846. Primus.prototype.reserved.events = {
  1847. 'reconnect scheduled': 1,
  1848. 'reconnect timeout': 1,
  1849. 'readyStateChange': 1,
  1850. 'reconnect failed': 1,
  1851. 'reconnected': 1,
  1852. 'reconnect': 1,
  1853. 'offline': 1,
  1854. 'timeout': 1,
  1855. 'online': 1,
  1856. 'error': 1,
  1857. 'close': 1,
  1858. 'open': 1,
  1859. 'data': 1,
  1860. 'end': 1
  1861. };
  1862.  
  1863. /**
  1864. * Initialise the Primus and setup all parsers and internal listeners.
  1865. *
  1866. * @param {Object} options The original options object.
  1867. * @returns {Primus}
  1868. * @api private
  1869. */
  1870. Primus.prototype.initialise = function initialise(options) {
  1871. var primus = this
  1872. , start;
  1873.  
  1874. primus.recovery
  1875. .on('reconnected', primus.emits('reconnected'))
  1876. .on('reconnect failed', primus.emits('reconnect failed', function failed(next) {
  1877. primus.emit('end');
  1878. next();
  1879. }))
  1880. .on('reconnect timeout', primus.emits('reconnect timeout'))
  1881. .on('reconnect scheduled', primus.emits('reconnect scheduled'))
  1882. .on('reconnect', primus.emits('reconnect', function reconnect(next) {
  1883. primus.emit('outgoing::reconnect');
  1884. next();
  1885. }));
  1886.  
  1887. primus.on('outgoing::open', function opening() {
  1888. var readyState = primus.readyState;
  1889.  
  1890. primus.readyState = Primus.OPENING;
  1891. if (readyState !== primus.readyState) {
  1892. primus.emit('readyStateChange', 'opening');
  1893. }
  1894.  
  1895. start = +new Date();
  1896. });
  1897.  
  1898. primus.on('incoming::open', function opened() {
  1899. var readyState = primus.readyState;
  1900.  
  1901. if (primus.recovery.reconnecting()) {
  1902. primus.recovery.reconnected();
  1903. }
  1904.  
  1905. //
  1906. // The connection has been opened so we should set our state to
  1907. // (writ|read)able so our stream compatibility works as intended.
  1908. //
  1909. primus.writable = true;
  1910. primus.readable = true;
  1911.  
  1912. //
  1913. // Make sure we are flagged as `online` as we've successfully opened the
  1914. // connection.
  1915. //
  1916. if (!primus.online) {
  1917. primus.online = true;
  1918. primus.emit('online');
  1919. }
  1920.  
  1921. primus.readyState = Primus.OPEN;
  1922. if (readyState !== primus.readyState) {
  1923. primus.emit('readyStateChange', 'open');
  1924. }
  1925.  
  1926. primus.latency = +new Date() - start;
  1927. primus.timers.clear('ping', 'pong');
  1928. primus.heartbeat();
  1929.  
  1930. if (primus.buffer.length) {
  1931. var data = primus.buffer.slice()
  1932. , length = data.length
  1933. , i = 0;
  1934.  
  1935. primus.buffer.length = 0;
  1936.  
  1937. for (; i < length; i++) {
  1938. primus._write(data[i]);
  1939. }
  1940. }
  1941.  
  1942. primus.emit('open');
  1943. });
  1944.  
  1945. primus.on('incoming::pong', function pong(time) {
  1946. primus.online = true;
  1947. primus.timers.clear('pong');
  1948. primus.heartbeat();
  1949.  
  1950. primus.latency = (+new Date()) - time;
  1951. });
  1952.  
  1953. primus.on('incoming::error', function error(e) {
  1954. var connect = primus.timers.active('connect')
  1955. , err = e;
  1956.  
  1957. //
  1958. // When the error is not an Error instance we try to normalize it.
  1959. //
  1960. if ('string' === typeof e) {
  1961. err = new Error(e);
  1962. } else if (!(e instanceof Error) && 'object' === typeof e) {
  1963. //
  1964. // BrowserChannel and SockJS returns an object which contains some
  1965. // details of the error. In order to have a proper error we "copy" the
  1966. // details in an Error instance.
  1967. //
  1968. err = new Error(e.message || e.reason);
  1969. for (var key in e) {
  1970. if (Object.prototype.hasOwnProperty.call(e, key))
  1971. err[key] = e[key];
  1972. }
  1973. }
  1974. //
  1975. // We're still doing a reconnect attempt, it could be that we failed to
  1976. // connect because the server was down. Failing connect attempts should
  1977. // always emit an `error` event instead of a `open` event.
  1978. //
  1979. //
  1980. if (primus.recovery.reconnecting()) return primus.recovery.reconnected(err);
  1981. if (primus.listeners('error').length) primus.emit('error', err);
  1982.  
  1983. //
  1984. // We received an error while connecting, this most likely the result of an
  1985. // unauthorized access to the server.
  1986. //
  1987. if (connect) {
  1988. if (~primus.options.strategy.indexOf('timeout')) {
  1989. primus.recovery.reconnect();
  1990. } else {
  1991. primus.end();
  1992. }
  1993. }
  1994. });
  1995.  
  1996. primus.on('incoming::data', function message(raw) {
  1997. primus.decoder(raw, function decoding(err, data) {
  1998. //
  1999. // Do a "save" emit('error') when we fail to parse a message. We don't
  2000. // want to throw here as listening to errors should be optional.
  2001. //
  2002. if (err) return primus.listeners('error').length && primus.emit('error', err);
  2003.  
  2004. //
  2005. // Handle all "primus::" prefixed protocol messages.
  2006. //
  2007. if (primus.protocol(data)) return;
  2008. primus.transforms(primus, primus, 'incoming', data, raw);
  2009. });
  2010. });
  2011.  
  2012. primus.on('incoming::end', function end() {
  2013. var readyState = primus.readyState;
  2014.  
  2015. //
  2016. // This `end` started with the receiving of a primus::server::close packet
  2017. // which indicated that the user/developer on the server closed the
  2018. // connection and it was not a result of a network disruption. So we should
  2019. // kill the connection without doing a reconnect.
  2020. //
  2021. if (primus.disconnect) {
  2022. primus.disconnect = false;
  2023.  
  2024. return primus.end();
  2025. }
  2026.  
  2027. //
  2028. // Always set the readyState to closed, and if we're still connecting, close
  2029. // the connection so we're sure that everything after this if statement block
  2030. // is only executed because our readyState is set to `open`.
  2031. //
  2032. primus.readyState = Primus.CLOSED;
  2033. if (readyState !== primus.readyState) {
  2034. primus.emit('readyStateChange', 'end');
  2035. }
  2036.  
  2037. if (primus.timers.active('connect')) primus.end();
  2038. if (readyState !== Primus.OPEN) {
  2039. return primus.recovery.reconnecting()
  2040. ? primus.recovery.reconnect()
  2041. : false;
  2042. }
  2043.  
  2044. this.writable = false;
  2045. this.readable = false;
  2046.  
  2047. //
  2048. // Clear all timers in case we're not going to reconnect.
  2049. //
  2050. this.timers.clear();
  2051.  
  2052. //
  2053. // Fire the `close` event as an indication of connection disruption.
  2054. // This is also fired by `primus#end` so it is emitted in all cases.
  2055. //
  2056. primus.emit('close');
  2057.  
  2058. //
  2059. // The disconnect was unintentional, probably because the server has
  2060. // shutdown, so if the reconnection is enabled start a reconnect procedure.
  2061. //
  2062. if (~primus.options.strategy.indexOf('disconnect')) {
  2063. return primus.recovery.reconnect();
  2064. }
  2065.  
  2066. primus.emit('outgoing::end');
  2067. primus.emit('end');
  2068. });
  2069.  
  2070. //
  2071. // Setup the real-time client.
  2072. //
  2073. primus.client();
  2074.  
  2075. //
  2076. // Process the potential plugins.
  2077. //
  2078. for (var plugin in primus.ark) {
  2079. primus.ark[plugin].call(primus, primus, options);
  2080. }
  2081.  
  2082. //
  2083. // NOTE: The following code is only required if we're supporting network
  2084. // events as it requires access to browser globals.
  2085. //
  2086. if (!primus.NETWORK_EVENTS) return primus;
  2087.  
  2088. /**
  2089. * Handler for offline notifications.
  2090. *
  2091. * @api private
  2092. */
  2093. primus.offlineHandler = function offline() {
  2094. if (!primus.online) return; // Already or still offline, bailout.
  2095.  
  2096. primus.online = false;
  2097. primus.emit('offline');
  2098. primus.end();
  2099.  
  2100. //
  2101. // It is certainly possible that we're in a reconnection loop and that the
  2102. // user goes offline. In this case we want to kill the existing attempt so
  2103. // when the user goes online, it will attempt to reconnect freshly again.
  2104. //
  2105. primus.recovery.reset();
  2106. };
  2107.  
  2108. /**
  2109. * Handler for online notifications.
  2110. *
  2111. * @api private
  2112. */
  2113. primus.onlineHandler = function online() {
  2114. if (primus.online) return; // Already or still online, bailout.
  2115.  
  2116. primus.online = true;
  2117. primus.emit('online');
  2118.  
  2119. if (~primus.options.strategy.indexOf('online')) {
  2120. primus.recovery.reconnect();
  2121. }
  2122. };
  2123.  
  2124. if (window.addEventListener) {
  2125. window.addEventListener('offline', primus.offlineHandler, false);
  2126. window.addEventListener('online', primus.onlineHandler, false);
  2127. } else if (document.body.attachEvent){
  2128. document.body.attachEvent('onoffline', primus.offlineHandler);
  2129. document.body.attachEvent('ononline', primus.onlineHandler);
  2130. }
  2131.  
  2132. return primus;
  2133. };
  2134.  
  2135. /**
  2136. * Really dead simple protocol parser. We simply assume that every message that
  2137. * is prefixed with `primus::` could be used as some sort of protocol definition
  2138. * for Primus.
  2139. *
  2140. * @param {String} msg The data.
  2141. * @returns {Boolean} Is a protocol message.
  2142. * @api private
  2143. */
  2144. Primus.prototype.protocol = function protocol(msg) {
  2145. if (
  2146. 'string' !== typeof msg
  2147. || msg.indexOf('primus::') !== 0
  2148. ) return false;
  2149.  
  2150. var last = msg.indexOf(':', 8)
  2151. , value = msg.slice(last + 2);
  2152.  
  2153. switch (msg.slice(8, last)) {
  2154. case 'pong':
  2155. this.emit('incoming::pong', +value);
  2156. break;
  2157.  
  2158. case 'server':
  2159. //
  2160. // The server is closing the connection, forcefully disconnect so we don't
  2161. // reconnect again.
  2162. //
  2163. if ('close' === value) {
  2164. this.disconnect = true;
  2165. }
  2166. break;
  2167.  
  2168. case 'id':
  2169. this.emit('incoming::id', value);
  2170. break;
  2171.  
  2172. //
  2173. // Unknown protocol, somebody is probably sending `primus::` prefixed
  2174. // messages.
  2175. //
  2176. default:
  2177. return false;
  2178. }
  2179.  
  2180. return true;
  2181. };
  2182.  
  2183. /**
  2184. * Execute the set of message transformers from Primus on the incoming or
  2185. * outgoing message.
  2186. * This function and it's content should be in sync with Spark#transforms in
  2187. * spark.js.
  2188. *
  2189. * @param {Primus} primus Reference to the Primus instance with message transformers.
  2190. * @param {Spark|Primus} connection Connection that receives or sends data.
  2191. * @param {String} type The type of message, 'incoming' or 'outgoing'.
  2192. * @param {Mixed} data The data to send or that has been received.
  2193. * @param {String} raw The raw encoded data.
  2194. * @returns {Primus}
  2195. * @api public
  2196. */
  2197. Primus.prototype.transforms = function transforms(primus, connection, type, data, raw) {
  2198. var packet = { data: data }
  2199. , fns = primus.transformers[type];
  2200.  
  2201. //
  2202. // Iterate in series over the message transformers so we can allow optional
  2203. // asynchronous execution of message transformers which could for example
  2204. // retrieve additional data from the server, do extra decoding or even
  2205. // message validation.
  2206. //
  2207. (function transform(index, done) {
  2208. var transformer = fns[index++];
  2209.  
  2210. if (!transformer) return done();
  2211.  
  2212. if (1 === transformer.length) {
  2213. if (false === transformer.call(connection, packet)) {
  2214. //
  2215. // When false is returned by an incoming transformer it means that's
  2216. // being handled by the transformer and we should not emit the `data`
  2217. // event.
  2218. //
  2219. return;
  2220. }
  2221.  
  2222. return transform(index, done);
  2223. }
  2224.  
  2225. transformer.call(connection, packet, function finished(err, arg) {
  2226. if (err) return connection.emit('error', err);
  2227. if (false === arg) return;
  2228.  
  2229. transform(index, done);
  2230. });
  2231. }(0, function done() {
  2232. //
  2233. // We always emit 2 arguments for the data event, the first argument is the
  2234. // parsed data and the second argument is the raw string that we received.
  2235. // This allows you, for example, to do some validation on the parsed data
  2236. // and then save the raw string in your database without the stringify
  2237. // overhead.
  2238. //
  2239. if ('incoming' === type) return connection.emit('data', packet.data, raw);
  2240.  
  2241. connection._write(packet.data);
  2242. }));
  2243.  
  2244. return this;
  2245. };
  2246.  
  2247. /**
  2248. * Retrieve the current id from the server.
  2249. *
  2250. * @param {Function} fn Callback function.
  2251. * @returns {Primus}
  2252. * @api public
  2253. */
  2254. Primus.prototype.id = function id(fn) {
  2255. if (this.socket && this.socket.id) return fn(this.socket.id);
  2256.  
  2257. this._write('primus::id::');
  2258. return this.once('incoming::id', fn);
  2259. };
  2260.  
  2261. /**
  2262. * Establish a connection with the server. When this function is called we
  2263. * assume that we don't have any open connections. If you do call it when you
  2264. * have a connection open, it could cause duplicate connections.
  2265. *
  2266. * @returns {Primus}
  2267. * @api public
  2268. */
  2269. Primus.prototype.open = function open() {
  2270. context(this, 'open');
  2271.  
  2272. //
  2273. // Only start a `connection timeout` procedure if we're not reconnecting as
  2274. // that shouldn't count as an initial connection. This should be started
  2275. // before the connection is opened to capture failing connections and kill the
  2276. // timeout.
  2277. //
  2278. if (!this.recovery.reconnecting() && this.options.timeout) this.timeout();
  2279.  
  2280. this.emit('outgoing::open');
  2281. return this;
  2282. };
  2283.  
  2284. /**
  2285. * Send a new message.
  2286. *
  2287. * @param {Mixed} data The data that needs to be written.
  2288. * @returns {Boolean} Always returns true as we don't support back pressure.
  2289. * @api public
  2290. */
  2291. Primus.prototype.write = function write(data) {
  2292. context(this, 'write');
  2293. this.transforms(this, this, 'outgoing', data);
  2294.  
  2295. return true;
  2296. };
  2297.  
  2298. /**
  2299. * The actual message writer.
  2300. *
  2301. * @param {Mixed} data The message that needs to be written.
  2302. * @returns {Boolean} Successful write to the underlaying transport.
  2303. * @api private
  2304. */
  2305. Primus.prototype._write = function write(data) {
  2306. var primus = this;
  2307.  
  2308. //
  2309. // The connection is closed, normally this would already be done in the
  2310. // `spark.write` method, but as `_write` is used internally, we should also
  2311. // add the same check here to prevent potential crashes by writing to a dead
  2312. // socket.
  2313. //
  2314. if (Primus.OPEN !== primus.readyState) {
  2315. //
  2316. // If the buffer is at capacity, remove the first item.
  2317. //
  2318. if (this.buffer.length === this.options.queueSize) {
  2319. this.buffer.splice(0, 1);
  2320. }
  2321.  
  2322. this.buffer.push(data);
  2323. return false;
  2324. }
  2325.  
  2326. primus.encoder(data, function encoded(err, packet) {
  2327. //
  2328. // Do a "safe" emit('error') when we fail to parse a message. We don't
  2329. // want to throw here as listening to errors should be optional.
  2330. //
  2331. if (err) return primus.listeners('error').length && primus.emit('error', err);
  2332.  
  2333. //
  2334. // Hack 1: \u2028 and \u2029 are allowed inside a JSON string, but JavaScript
  2335. // defines them as newline separators. Unescaped control characters are not
  2336. // allowed inside JSON strings, so this causes an error at parse time. We
  2337. // work around this issue by escaping these characters. This can cause
  2338. // errors with JSONP requests or if the string is just evaluated.
  2339. //
  2340. if ('string' === typeof packet) {
  2341. if (~packet.indexOf('\u2028')) packet = packet.replace(u2028, '\\u2028');
  2342. if (~packet.indexOf('\u2029')) packet = packet.replace(u2029, '\\u2029');
  2343. }
  2344.  
  2345. primus.emit('outgoing::data', packet);
  2346. });
  2347.  
  2348. return true;
  2349. };
  2350.  
  2351. /**
  2352. * Send a new heartbeat over the connection to ensure that we're still
  2353. * connected and our internet connection didn't drop. We cannot use server side
  2354. * heartbeats for this unfortunately.
  2355. *
  2356. * @returns {Primus}
  2357. * @api private
  2358. */
  2359. Primus.prototype.heartbeat = function heartbeat() {
  2360. var primus = this;
  2361.  
  2362. if (!primus.options.ping) return primus;
  2363.  
  2364. /**
  2365. * Exterminate the connection as we've timed out.
  2366. *
  2367. * @api private
  2368. */
  2369. function pong() {
  2370. primus.timers.clear('pong');
  2371.  
  2372. //
  2373. // The network events already captured the offline event.
  2374. //
  2375. if (!primus.online) return;
  2376.  
  2377. primus.online = false;
  2378. primus.emit('offline');
  2379. primus.emit('incoming::end');
  2380. }
  2381.  
  2382. /**
  2383. * We should send a ping message to the server.
  2384. *
  2385. * @api private
  2386. */
  2387. function ping() {
  2388. var value = +new Date();
  2389.  
  2390. primus.timers.clear('ping');
  2391. primus._write('primus::ping::'+ value);
  2392. primus.emit('outgoing::ping', value);
  2393. primus.timers.setTimeout('pong', pong, primus.options.pong);
  2394. }
  2395.  
  2396. primus.timers.setTimeout('ping', ping, primus.options.ping);
  2397. return this;
  2398. };
  2399.  
  2400. /**
  2401. * Start a connection timeout.
  2402. *
  2403. * @returns {Primus}
  2404. * @api private
  2405. */
  2406. Primus.prototype.timeout = function timeout() {
  2407. var primus = this;
  2408.  
  2409. /**
  2410. * Remove all references to the timeout listener as we've received an event
  2411. * that can be used to determine state.
  2412. *
  2413. * @api private
  2414. */
  2415. function remove() {
  2416. primus.removeListener('error', remove)
  2417. .removeListener('open', remove)
  2418. .removeListener('end', remove)
  2419. .timers.clear('connect');
  2420. }
  2421.  
  2422. primus.timers.setTimeout('connect', function expired() {
  2423. remove(); // Clean up old references.
  2424.  
  2425. if (primus.readyState === Primus.OPEN || primus.recovery.reconnecting()) {
  2426. return;
  2427. }
  2428.  
  2429. primus.emit('timeout');
  2430.  
  2431. //
  2432. // We failed to connect to the server.
  2433. //
  2434. if (~primus.options.strategy.indexOf('timeout')) {
  2435. primus.recovery.reconnect();
  2436. } else {
  2437. primus.end();
  2438. }
  2439. }, primus.options.timeout);
  2440.  
  2441. return primus.on('error', remove)
  2442. .on('open', remove)
  2443. .on('end', remove);
  2444. };
  2445.  
  2446. /**
  2447. * Close the connection completely.
  2448. *
  2449. * @param {Mixed} data last packet of data.
  2450. * @returns {Primus}
  2451. * @api public
  2452. */
  2453. Primus.prototype.end = function end(data) {
  2454. context(this, 'end');
  2455.  
  2456. if (
  2457. this.readyState === Primus.CLOSED
  2458. && !this.timers.active('connect')
  2459. && !this.timers.active('open')
  2460. ) {
  2461. //
  2462. // If we are reconnecting stop the reconnection procedure.
  2463. //
  2464. if (this.recovery.reconnecting()) {
  2465. this.recovery.reset();
  2466. this.emit('end');
  2467. }
  2468.  
  2469. return this;
  2470. }
  2471.  
  2472. if (data !== undefined) this.write(data);
  2473.  
  2474. this.writable = false;
  2475. this.readable = false;
  2476.  
  2477. var readyState = this.readyState;
  2478. this.readyState = Primus.CLOSED;
  2479.  
  2480. if (readyState !== this.readyState) {
  2481. this.emit('readyStateChange', 'end');
  2482. }
  2483.  
  2484. this.timers.clear();
  2485. this.emit('outgoing::end');
  2486. this.emit('close');
  2487. this.emit('end');
  2488.  
  2489. return this;
  2490. };
  2491.  
  2492. /**
  2493. * Completely demolish the Primus instance and forcefully nuke all references.
  2494. *
  2495. * @returns {Boolean}
  2496. * @api public
  2497. */
  2498. Primus.prototype.destroy = destroy('url timers options recovery socket transport transformers', {
  2499. before: 'end',
  2500. after: ['removeAllListeners', function detach() {
  2501. if (!this.NETWORK_EVENTS) return;
  2502.  
  2503. if (window.addEventListener) {
  2504. window.removeEventListener('offline', this.offlineHandler);
  2505. window.removeEventListener('online', this.onlineHandler);
  2506. } else if (document.body.attachEvent){
  2507. document.body.detachEvent('onoffline', this.offlineHandler);
  2508. document.body.detachEvent('ononline', this.onlineHandler);
  2509. }
  2510. }]
  2511. });
  2512.  
  2513. /**
  2514. * Create a shallow clone of a given object.
  2515. *
  2516. * @param {Object} obj The object that needs to be cloned.
  2517. * @returns {Object} Copy.
  2518. * @api private
  2519. */
  2520. Primus.prototype.clone = function clone(obj) {
  2521. return this.merge({}, obj);
  2522. };
  2523.  
  2524. /**
  2525. * Merge different objects in to one target object.
  2526. *
  2527. * @param {Object} target The object where everything should be merged in.
  2528. * @returns {Object} Original target with all merged objects.
  2529. * @api private
  2530. */
  2531. Primus.prototype.merge = function merge(target) {
  2532. var args = Array.prototype.slice.call(arguments, 1);
  2533.  
  2534. for (var i = 0, l = args.length, key, obj; i < l; i++) {
  2535. obj = args[i];
  2536.  
  2537. for (key in obj) {
  2538. if (obj.hasOwnProperty(key)) target[key] = obj[key];
  2539. }
  2540. }
  2541.  
  2542. return target;
  2543. };
  2544.  
  2545. /**
  2546. * Parse the connection string.
  2547. *
  2548. * @type {Function}
  2549. * @param {String} url Connection URL.
  2550. * @returns {Object} Parsed connection.
  2551. * @api private
  2552. */
  2553. Primus.prototype.parse = _dereq_('url-parse');
  2554.  
  2555. /**
  2556. * Parse a query string.
  2557. *
  2558. * @param {String} query The query string that needs to be parsed.
  2559. * @returns {Object} Parsed query string.
  2560. * @api private
  2561. */
  2562. Primus.prototype.querystring = qs.parse;
  2563. /**
  2564. * Transform a query string object back into string equiv.
  2565. *
  2566. * @param {Object} obj The query string object.
  2567. * @returns {String}
  2568. * @api private
  2569. */
  2570. Primus.prototype.querystringify = qs.stringify;
  2571.  
  2572. /**
  2573. * Generates a connection URI.
  2574. *
  2575. * @param {String} protocol The protocol that should used to crate the URI.
  2576. * @returns {String|options} The URL.
  2577. * @api private
  2578. */
  2579. Primus.prototype.uri = function uri(options) {
  2580. var url = this.url
  2581. , server = []
  2582. , qsa = false;
  2583.  
  2584. //
  2585. // Query strings are only allowed when we've received clearance for it.
  2586. //
  2587. if (options.query) qsa = true;
  2588.  
  2589. options = options || {};
  2590. options.protocol = 'protocol' in options
  2591. ? options.protocol
  2592. : 'http:';
  2593. options.query = url.query && qsa
  2594. ? url.query.slice(1)
  2595. : false;
  2596. options.secure = 'secure' in options
  2597. ? options.secure
  2598. : url.protocol === 'https:' || url.protocol === 'wss:';
  2599. options.auth = 'auth' in options
  2600. ? options.auth
  2601. : url.auth;
  2602. options.pathname = 'pathname' in options
  2603. ? options.pathname
  2604. : this.pathname;
  2605. options.port = 'port' in options
  2606. ? +options.port
  2607. : +url.port || (options.secure ? 443 : 80);
  2608.  
  2609. //
  2610. // Allow transformation of the options before we construct a full URL from it.
  2611. //
  2612. this.emit('outgoing::url', options);
  2613.  
  2614. //
  2615. // We need to make sure that we create a unique connection URL every time to
  2616. // prevent back forward cache from becoming an issue. We're doing this by
  2617. // forcing an cache busting query string in to the URL.
  2618. //
  2619. var querystring = this.querystring(options.query || '');
  2620. querystring._primuscb = yeast();
  2621. options.query = this.querystringify(querystring);
  2622.  
  2623. //
  2624. // Automatically suffix the protocol so we can supply `ws:` and `http:` and
  2625. // it gets transformed correctly.
  2626. //
  2627. server.push(options.secure ? options.protocol.replace(':', 's:') : options.protocol, '');
  2628.  
  2629. server.push(options.auth ? options.auth +'@'+ url.host : url.host);
  2630.  
  2631. //
  2632. // Pathnames are optional as some Transformers would just use the pathname
  2633. // directly.
  2634. //
  2635. if (options.pathname) server.push(options.pathname.slice(1));
  2636.  
  2637. //
  2638. // Optionally add a search query.
  2639. //
  2640. if (qsa) server.push('?'+ options.query);
  2641. else delete options.query;
  2642.  
  2643. if (options.object) return options;
  2644. return server.join('/');
  2645. };
  2646.  
  2647. /**
  2648. * Register a new message transformer. This allows you to easily manipulate incoming
  2649. * and outgoing data which is particularity handy for plugins that want to send
  2650. * meta data together with the messages.
  2651. *
  2652. * @param {String} type Incoming or outgoing
  2653. * @param {Function} fn A new message transformer.
  2654. * @returns {Primus}
  2655. * @api public
  2656. */
  2657. Primus.prototype.transform = function transform(type, fn) {
  2658. context(this, 'transform');
  2659.  
  2660. if (!(type in this.transformers)) {
  2661. return this.critical(new Error('Invalid transformer type'));
  2662. }
  2663.  
  2664. this.transformers[type].push(fn);
  2665. return this;
  2666. };
  2667.  
  2668. /**
  2669. * A critical error has occurred, if we have an `error` listener, emit it there.
  2670. * If not, throw it, so we get a stack trace + proper error message.
  2671. *
  2672. * @param {Error} err The critical error.
  2673. * @returns {Primus}
  2674. * @api private
  2675. */
  2676. Primus.prototype.critical = function critical(err) {
  2677. if (this.listeners('error').length) {
  2678. this.emit('error', err);
  2679. return this;
  2680. }
  2681.  
  2682. throw err;
  2683. };
  2684.  
  2685. /**
  2686. * Syntax sugar, adopt a Socket.IO like API.
  2687. *
  2688. * @param {String} url The URL we want to connect to.
  2689. * @param {Object} options Connection options.
  2690. * @returns {Primus}
  2691. * @api public
  2692. */
  2693. Primus.connect = function connect(url, options) {
  2694. return new Primus(url, options);
  2695. };
  2696.  
  2697. //
  2698. // Expose the EventEmitter so it can be re-used by wrapping libraries we're also
  2699. // exposing the Stream interface.
  2700. //
  2701. Primus.EventEmitter = EventEmitter;
  2702.  
  2703. //
  2704. // These libraries are automatically are automatically inserted at the
  2705. // server-side using the Primus#library method.
  2706. //
  2707. Primus.prototype.client = function client() {
  2708. var primus = this
  2709. , socket;
  2710.  
  2711. //
  2712. // Select an available WebSocket factory.
  2713. //
  2714. var Factory = (function factory() {
  2715. if ('undefined' !== typeof WebSocket) return WebSocket;
  2716. if ('undefined' !== typeof MozWebSocket) return MozWebSocket;
  2717.  
  2718. try { return Primus.require('ws'); }
  2719. catch (e) {}
  2720.  
  2721. return undefined;
  2722. })();
  2723.  
  2724. if (!Factory) return primus.critical(new Error(
  2725. 'Missing required `ws` module. Please run `npm install --save ws`'
  2726. ));
  2727.  
  2728.  
  2729. //
  2730. // Connect to the given URL.
  2731. //
  2732. primus.on('outgoing::open', function opening() {
  2733. primus.emit('outgoing::end');
  2734.  
  2735. //
  2736. // FireFox will throw an error when we try to establish a connection from
  2737. // a secure page to an unsecured WebSocket connection. This is inconsistent
  2738. // behaviour between different browsers. This should ideally be solved in
  2739. // Primus when we connect.
  2740. //
  2741. try {
  2742. var prot = primus.url.protocol === 'ws+unix:' ? 'ws+unix:' : 'ws:'
  2743. , qsa = prot === 'ws:';
  2744.  
  2745. //
  2746. // Only allow primus.transport object in Node.js, it will throw in
  2747. // browsers with a TypeError if we supply to much arguments.
  2748. //
  2749. if (Factory.length === 3) {
  2750. primus.socket = socket = new Factory(
  2751. primus.uri({ protocol: prot, query: qsa }), // URL
  2752. [], // Sub protocols
  2753. primus.transport // options.
  2754. );
  2755. } else {
  2756. primus.socket = socket = new Factory(primus.uri({
  2757. protocol: prot,
  2758. query: qsa
  2759. }));
  2760. }
  2761. } catch (e) { return primus.emit('error', e); }
  2762.  
  2763. //
  2764. // Setup the Event handlers.
  2765. //
  2766. socket.binaryType = 'arraybuffer';
  2767. socket.onopen = primus.trigger('incoming::open');
  2768. socket.onerror = primus.trigger('incoming::error');
  2769. socket.onclose = primus.trigger('incoming::end');
  2770. socket.onmessage = primus.trigger('incoming::data', function parse(next, evt) {
  2771. setTimeout(function defer() {
  2772. next(undefined, evt.data);
  2773. }, 0);
  2774. });
  2775. });
  2776.  
  2777. //
  2778. // We need to write a new message to the socket.
  2779. //
  2780. primus.on('outgoing::data', function write(message) {
  2781. if (!socket || socket.readyState !== Factory.OPEN) return;
  2782.  
  2783. try { socket.send(message); }
  2784. catch (e) { primus.emit('incoming::error', e); }
  2785. });
  2786.  
  2787. //
  2788. // Attempt to reconnect the socket.
  2789. //
  2790. primus.on('outgoing::reconnect', function reconnect() {
  2791. primus.emit('outgoing::open');
  2792. });
  2793.  
  2794. //
  2795. // We need to close the socket.
  2796. //
  2797. primus.on('outgoing::end', function close() {
  2798. if (!socket) return;
  2799.  
  2800. socket.onerror = socket.onopen = socket.onclose = socket.onmessage = function () {};
  2801. socket.close();
  2802. socket = null;
  2803. });
  2804. };
  2805. Primus.prototype.authorization = true;
  2806. Primus.prototype.pathname = "/live";
  2807. Primus.prototype.encoder = function encoder(data, fn) {
  2808. var err;
  2809.  
  2810. try { data = JSON.stringify(data); }
  2811. catch (e) { err = e; }
  2812.  
  2813. fn(err, data);
  2814. };
  2815. Primus.prototype.decoder = function decoder(data, fn) {
  2816. var err;
  2817.  
  2818. if ('string' !== typeof data) return fn(err, data);
  2819.  
  2820. try { data = JSON.parse(data); }
  2821. catch (e) { err = e; }
  2822.  
  2823. fn(err, data);
  2824. };
  2825. Primus.prototype.version = "4.0.1";
  2826.  
  2827. if (
  2828. 'undefined' !== typeof document
  2829. && 'undefined' !== typeof navigator
  2830. ) {
  2831. //
  2832. // Hack 2: If you press ESC in FireFox it will close all active connections.
  2833. // Normally this makes sense, when your page is still loading. But versions
  2834. // before FireFox 22 will close all connections including WebSocket connections
  2835. // after page load. One way to prevent this is to do a `preventDefault()` and
  2836. // cancel the operation before it bubbles up to the browsers default handler.
  2837. // It needs to be added as `keydown` event, if it's added keyup it will not be
  2838. // able to prevent the connection from being closed.
  2839. //
  2840. if (document.addEventListener) {
  2841. document.addEventListener('keydown', function keydown(e) {
  2842. if (e.keyCode !== 27 || !e.preventDefault) return;
  2843.  
  2844. e.preventDefault();
  2845. }, false);
  2846. }
  2847.  
  2848. //
  2849. // Hack 3: This is a Mac/Apple bug only, when you're behind a reverse proxy or
  2850. // have you network settings set to `automatic proxy discovery` the safari
  2851. // browser will crash when the WebSocket constructor is initialised. There is
  2852. // no way to detect the usage of these proxies available in JavaScript so we
  2853. // need to do some nasty browser sniffing. This only affects Safari versions
  2854. // lower then 5.1.4
  2855. //
  2856. var ua = (navigator.userAgent || '').toLowerCase()
  2857. , parsed = ua.match(/.+(?:rv|it|ra|ie)[\/: ](\d+)\.(\d+)(?:\.(\d+))?/) || []
  2858. , version = +[parsed[1], parsed[2]].join('.');
  2859.  
  2860. if (
  2861. !~ua.indexOf('chrome')
  2862. && ~ua.indexOf('safari')
  2863. && version < 534.54
  2864. ) {
  2865. Primus.prototype.AVOID_WEBSOCKETS = true;
  2866. }
  2867. }
  2868.  
  2869. //
  2870. // Expose the library.
  2871. //
  2872. module.exports = Primus;
  2873.  
  2874. },{"demolish":1,"emits":2,"eventemitter3":3,"querystringify":4,"recovery":5,"tick-tock":8,"url-parse":10,"yeast":13}]},{},[14])(14);
  2875. return Primus;
  2876. });
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement