text: /*!
   * SkyWay Copyright(c) 2019 NTT Communications Corporation
   * peerjs Copyright(c) 2013 Michelle Bu <michelle@michellebu.com>
   */
  (function webpackUniversalModuleDefinition(root, factory) {
  	if(typeof exports === 'object' && typeof module === 'object')
  		module.exports = factory();
  	else if(typeof define === 'function' && define.amd)
  		define([], factory);
  	else if(typeof exports === 'object')
  		exports["Peer"] = factory();
  	else
  		root["Peer"] = factory();
  })(window, function() {
  return /******/ (function(modules) { // webpackBootstrap
  /******/ 	// The module cache
  /******/ 	var installedModules = {};
  /******/
  /******/ 	// The require function
  /******/ 	function __webpack_require__(moduleId) {
  /******/
  /******/ 		// Check if module is in cache
  /******/ 		if(installedModules[moduleId]) {
  /******/ 			return installedModules[moduleId].exports;
  /******/ 		}
  /******/ 		// Create a new module (and put it into the cache)
  /******/ 		var module = installedModules[moduleId] = {
  /******/ 			i: moduleId,
  /******/ 			l: false,
  /******/ 			exports: {}
  /******/ 		};
  /******/
  /******/ 		// Execute the module function
  /******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
  /******/
  /******/ 		// Flag the module as loaded
  /******/ 		module.l = true;
  /******/
  /******/ 		// Return the exports of the module
  /******/ 		return module.exports;
  /******/ 	}
  /******/
  /******/
  /******/ 	// expose the modules object (__webpack_modules__)
  /******/ 	__webpack_require__.m = modules;
  /******/
  /******/ 	// expose the module cache
  /******/ 	__webpack_require__.c = installedModules;
  /******/
  /******/ 	// define getter function for harmony exports
  /******/ 	__webpack_require__.d = function(exports, name, getter) {
  /******/ 		if(!__webpack_require__.o(exports, name)) {
  /******/ 			Object.defineProperty(exports, name, { enumerable: true, get: getter });
  /******/ 		}
  /******/ 	};
  /******/
  /******/ 	// define __esModule on exports
  /******/ 	__webpack_require__.r = function(exports) {
  /******/ 		if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
  /******/ 			Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
  /******/ 		}
  /******/ 		Object.defineProperty(exports, '__esModule', { value: true });
  /******/ 	};
  /******/
  /******/ 	// create a fake namespace object
  /******/ 	// mode & 1: value is a module id, require it
  /******/ 	// mode & 2: merge all properties of value into the ns
  /******/ 	// mode & 4: return value when already ns object
  /******/ 	// mode & 8|1: behave like require
  /******/ 	__webpack_require__.t = function(value, mode) {
  /******/ 		if(mode & 1) value = __webpack_require__(value);
  /******/ 		if(mode & 8) return value;
  /******/ 		if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
  /******/ 		var ns = Object.create(null);
  /******/ 		__webpack_require__.r(ns);
  /******/ 		Object.defineProperty(ns, 'default', { enumerable: true, value: value });
  /******/ 		if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
  /******/ 		return ns;
  /******/ 	};
  /******/
  /******/ 	// getDefaultExport function for compatibility with non-harmony modules
  /******/ 	__webpack_require__.n = function(module) {
  /******/ 		var getter = module && module.__esModule ?
  /******/ 			function getDefault() { return module['default']; } :
  /******/ 			function getModuleExports() { return module; };
  /******/ 		__webpack_require__.d(getter, 'a', getter);
  /******/ 		return getter;
  /******/ 	};
  /******/
  /******/ 	// Object.prototype.hasOwnProperty.call
  /******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
  /******/
  /******/ 	// __webpack_public_path__
  /******/ 	__webpack_require__.p = "";
  /******/
  /******/
  /******/ 	// Load entry module and return exports
  /******/ 	return __webpack_require__(__webpack_require__.s = "./src/peer.js");
  /******/ })
  /************************************************************************/
  /******/ ({
  
  /***/ "./node_modules/after/index.js":
  /*!*************************************!*\
    !*** ./node_modules/after/index.js ***!
    \*************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports) {
  
  module.exports = after
  
  function after(count, callback, err_cb) {
      var bail = false
      err_cb = err_cb || noop
      proxy.count = count
  
      return (count === 0) ? callback() : proxy
  
      function proxy(err, result) {
          if (proxy.count <= 0) {
              throw new Error('after called too many times')
          }
          --proxy.count
  
          // after first error, rest are passed to err_cb
          if (err) {
              bail = true
              callback(err)
              // future error callbacks will go to error handler
              callback = err_cb
          } else if (proxy.count === 0 && !bail) {
              callback(null, result)
          }
      }
  }
  
  function noop() {}
  
  
  /***/ }),
  
  /***/ "./node_modules/arraybuffer.slice/index.js":
  /*!*************************************************!*\
    !*** ./node_modules/arraybuffer.slice/index.js ***!
    \*************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports) {
  
  /**
   * An abstraction for slicing an arraybuffer even when
   * ArrayBuffer.prototype.slice is not supported
   *
   * @api public
   */
  
  module.exports = function(arraybuffer, start, end) {
    var bytes = arraybuffer.byteLength;
    start = start || 0;
    end = end || bytes;
  
    if (arraybuffer.slice) { return arraybuffer.slice(start, end); }
  
    if (start < 0) { start += bytes; }
    if (end < 0) { end += bytes; }
    if (end > bytes) { end = bytes; }
  
    if (start >= bytes || start >= end || bytes === 0) {
      return new ArrayBuffer(0);
    }
  
    var abv = new Uint8Array(arraybuffer);
    var result = new Uint8Array(end - start);
    for (var i = start, ii = 0; i < end; i++, ii++) {
      result[ii] = abv[i];
    }
    return result.buffer;
  };
  
  
  /***/ }),
  
  /***/ "./node_modules/backo2/index.js":
  /*!**************************************!*\
    !*** ./node_modules/backo2/index.js ***!
    \**************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports) {
  
  
  /**
   * Expose `Backoff`.
   */
  
  module.exports = Backoff;
  
  /**
   * Initialize backoff timer with `opts`.
   *
   * - `min` initial timeout in milliseconds [100]
   * - `max` max timeout [10000]
   * - `jitter` [0]
   * - `factor` [2]
   *
   * @param {Object} opts
   * @api public
   */
  
  function Backoff(opts) {
    opts = opts || {};
    this.ms = opts.min || 100;
    this.max = opts.max || 10000;
    this.factor = opts.factor || 2;
    this.jitter = opts.jitter > 0 && opts.jitter <= 1 ? opts.jitter : 0;
    this.attempts = 0;
  }
  
  /**
   * Return the backoff duration.
   *
   * @return {Number}
   * @api public
   */
  
  Backoff.prototype.duration = function(){
    var ms = this.ms * Math.pow(this.factor, this.attempts++);
    if (this.jitter) {
      var rand =  Math.random();
      var deviation = Math.floor(rand * this.jitter * ms);
      ms = (Math.floor(rand * 10) & 1) == 0  ? ms - deviation : ms + deviation;
    }
    return Math.min(ms, this.max) | 0;
  };
  
  /**
   * Reset the number of attempts.
   *
   * @api public
   */
  
  Backoff.prototype.reset = function(){
    this.attempts = 0;
  };
  
  /**
   * Set the minimum duration
   *
   * @api public
   */
  
  Backoff.prototype.setMin = function(min){
    this.ms = min;
  };
  
  /**
   * Set the maximum duration
   *
   * @api public
   */
  
  Backoff.prototype.setMax = function(max){
    this.max = max;
  };
  
  /**
   * Set the jitter
   *
   * @api public
   */
  
  Backoff.prototype.setJitter = function(jitter){
    this.jitter = jitter;
  };
  
  
  
  /***/ }),
  
  /***/ "./node_modules/base64-arraybuffer/lib/base64-arraybuffer.js":
  /*!*******************************************************************!*\
    !*** ./node_modules/base64-arraybuffer/lib/base64-arraybuffer.js ***!
    \*******************************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports) {
  
  /*
   * base64-arraybuffer
   * https://github.com/niklasvh/base64-arraybuffer
   *
   * Copyright (c) 2012 Niklas von Hertzen
   * Licensed under the MIT license.
   */
  (function(){
    "use strict";
  
    var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  
    // Use a lookup table to find the index.
    var lookup = new Uint8Array(256);
    for (var i = 0; i < chars.length; i++) {
      lookup[chars.charCodeAt(i)] = i;
    }
  
    exports.encode = function(arraybuffer) {
      var bytes = new Uint8Array(arraybuffer),
      i, len = bytes.length, base64 = "";
  
      for (i = 0; i < len; i+=3) {
        base64 += chars[bytes[i] >> 2];
        base64 += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)];
        base64 += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)];
        base64 += chars[bytes[i + 2] & 63];
      }
  
      if ((len % 3) === 2) {
        base64 = base64.substring(0, base64.length - 1) + "=";
      } else if (len % 3 === 1) {
        base64 = base64.substring(0, base64.length - 2) + "==";
      }
  
      return base64;
    };
  
    exports.decode =  function(base64) {
      var bufferLength = base64.length * 0.75,
      len = base64.length, i, p = 0,
      encoded1, encoded2, encoded3, encoded4;
  
      if (base64[base64.length - 1] === "=") {
        bufferLength--;
        if (base64[base64.length - 2] === "=") {
          bufferLength--;
        }
      }
  
      var arraybuffer = new ArrayBuffer(bufferLength),
      bytes = new Uint8Array(arraybuffer);
  
      for (i = 0; i < len; i+=4) {
        encoded1 = lookup[base64.charCodeAt(i)];
        encoded2 = lookup[base64.charCodeAt(i+1)];
        encoded3 = lookup[base64.charCodeAt(i+2)];
        encoded4 = lookup[base64.charCodeAt(i+3)];
  
        bytes[p++] = (encoded1 << 2) | (encoded2 >> 4);
        bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2);
        bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63);
      }
  
      return arraybuffer;
    };
  })();
  
  
  /***/ }),
  
  /***/ "./node_modules/base64-js/index.js":
  /*!*****************************************!*\
    !*** ./node_modules/base64-js/index.js ***!
    \*****************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  "use strict";
  
  
  exports.byteLength = byteLength
  exports.toByteArray = toByteArray
  exports.fromByteArray = fromByteArray
  
  var lookup = []
  var revLookup = []
  var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array
  
  var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
  for (var i = 0, len = code.length; i < len; ++i) {
    lookup[i] = code[i]
    revLookup[code.charCodeAt(i)] = i
  }
  
  revLookup['-'.charCodeAt(0)] = 62
  revLookup['_'.charCodeAt(0)] = 63
  
  function placeHoldersCount (b64) {
    var len = b64.length
    if (len % 4 > 0) {
      throw new Error('Invalid string. Length must be a multiple of 4')
    }
  
    // the number of equal signs (place holders)
    // if there are two placeholders, than the two characters before it
    // represent one byte
    // if there is only one, then the three characters before it represent 2 bytes
    // this is just a cheap hack to not do indexOf twice
    return b64[len - 2] === '=' ? 2 : b64[len - 1] === '=' ? 1 : 0
  }
  
  function byteLength (b64) {
    // base64 is 4/3 + up to two characters of the original data
    return (b64.length * 3 / 4) - placeHoldersCount(b64)
  }
  
  function toByteArray (b64) {
    var i, l, tmp, placeHolders, arr
    var len = b64.length
    placeHolders = placeHoldersCount(b64)
  
    arr = new Arr((len * 3 / 4) - placeHolders)
  
    // if there are placeholders, only get up to the last complete 4 chars
    l = placeHolders > 0 ? len - 4 : len
  
    var L = 0
  
    for (i = 0; i < l; i += 4) {
      tmp = (revLookup[b64.charCodeAt(i)] << 18) | (revLookup[b64.charCodeAt(i + 1)] << 12) | (revLookup[b64.charCodeAt(i + 2)] << 6) | revLookup[b64.charCodeAt(i + 3)]
      arr[L++] = (tmp >> 16) & 0xFF
      arr[L++] = (tmp >> 8) & 0xFF
      arr[L++] = tmp & 0xFF
    }
  
    if (placeHolders === 2) {
      tmp = (revLookup[b64.charCodeAt(i)] << 2) | (revLookup[b64.charCodeAt(i + 1)] >> 4)
      arr[L++] = tmp & 0xFF
    } else if (placeHolders === 1) {
      tmp = (revLookup[b64.charCodeAt(i)] << 10) | (revLookup[b64.charCodeAt(i + 1)] << 4) | (revLookup[b64.charCodeAt(i + 2)] >> 2)
      arr[L++] = (tmp >> 8) & 0xFF
      arr[L++] = tmp & 0xFF
    }
  
    return arr
  }
  
  function tripletToBase64 (num) {
    return lookup[num >> 18 & 0x3F] + lookup[num >> 12 & 0x3F] + lookup[num >> 6 & 0x3F] + lookup[num & 0x3F]
  }
  
  function encodeChunk (uint8, start, end) {
    var tmp
    var output = []
    for (var i = start; i < end; i += 3) {
      tmp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2])
      output.push(tripletToBase64(tmp))
    }
    return output.join('')
  }
  
  function fromByteArray (uint8) {
    var tmp
    var len = uint8.length
    var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes
    var output = ''
    var parts = []
    var maxChunkLength = 16383 // must be multiple of 3
  
    // go through the array every three bytes, we'll deal with trailing stuff later
    for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
      parts.push(encodeChunk(uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength)))
    }
  
    // pad the end with zeros, but make sure to not forget the extra bytes
    if (extraBytes === 1) {
      tmp = uint8[len - 1]
      output += lookup[tmp >> 2]
      output += lookup[(tmp << 4) & 0x3F]
      output += '=='
    } else if (extraBytes === 2) {
      tmp = (uint8[len - 2] << 8) + (uint8[len - 1])
      output += lookup[tmp >> 10]
      output += lookup[(tmp >> 4) & 0x3F]
      output += lookup[(tmp << 2) & 0x3F]
      output += '='
    }
  
    parts.push(output)
  
    return parts.join('')
  }
  
  
  /***/ }),
  
  /***/ "./node_modules/blob/index.js":
  /*!************************************!*\
    !*** ./node_modules/blob/index.js ***!
    \************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  /* WEBPACK VAR INJECTION */(function(global) {/**
   * Create a blob builder even when vendor prefixes exist
   */
  
  var BlobBuilder = global.BlobBuilder
    || global.WebKitBlobBuilder
    || global.MSBlobBuilder
    || global.MozBlobBuilder;
  
  /**
   * Check if Blob constructor is supported
   */
  
  var blobSupported = (function() {
    try {
      var a = new Blob(['hi']);
      return a.size === 2;
    } catch(e) {
      return false;
    }
  })();
  
  /**
   * Check if Blob constructor supports ArrayBufferViews
   * Fails in Safari 6, so we need to map to ArrayBuffers there.
   */
  
  var blobSupportsArrayBufferView = blobSupported && (function() {
    try {
      var b = new Blob([new Uint8Array([1,2])]);
      return b.size === 2;
    } catch(e) {
      return false;
    }
  })();
  
  /**
   * Check if BlobBuilder is supported
   */
  
  var blobBuilderSupported = BlobBuilder
    && BlobBuilder.prototype.append
    && BlobBuilder.prototype.getBlob;
  
  /**
   * Helper function that maps ArrayBufferViews to ArrayBuffers
   * Used by BlobBuilder constructor and old browsers that didn't
   * support it in the Blob constructor.
   */
  
  function mapArrayBufferViews(ary) {
    for (var i = 0; i < ary.length; i++) {
      var chunk = ary[i];
      if (chunk.buffer instanceof ArrayBuffer) {
        var buf = chunk.buffer;
  
        // if this is a subarray, make a copy so we only
        // include the subarray region from the underlying buffer
        if (chunk.byteLength !== buf.byteLength) {
          var copy = new Uint8Array(chunk.byteLength);
          copy.set(new Uint8Array(buf, chunk.byteOffset, chunk.byteLength));
          buf = copy.buffer;
        }
  
        ary[i] = buf;
      }
    }
  }
  
  function BlobBuilderConstructor(ary, options) {
    options = options || {};
  
    var bb = new BlobBuilder();
    mapArrayBufferViews(ary);
  
    for (var i = 0; i < ary.length; i++) {
      bb.append(ary[i]);
    }
  
    return (options.type) ? bb.getBlob(options.type) : bb.getBlob();
  };
  
  function BlobConstructor(ary, options) {
    mapArrayBufferViews(ary);
    return new Blob(ary, options || {});
  };
  
  module.exports = (function() {
    if (blobSupported) {
      return blobSupportsArrayBufferView ? global.Blob : BlobConstructor;
    } else if (blobBuilderSupported) {
      return BlobBuilderConstructor;
    } else {
      return undefined;
    }
  })();
  
  /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js")))
  
  /***/ }),
  
  /***/ "./node_modules/component-bind/index.js":
  /*!**********************************************!*\
    !*** ./node_modules/component-bind/index.js ***!
    \**********************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports) {
  
  /**
   * Slice reference.
   */
  
  var slice = [].slice;
  
  /**
   * Bind `obj` to `fn`.
   *
   * @param {Object} obj
   * @param {Function|String} fn or string
   * @return {Function}
   * @api public
   */
  
  module.exports = function(obj, fn){
    if ('string' == typeof fn) fn = obj[fn];
    if ('function' != typeof fn) throw new Error('bind() requires a function');
    var args = slice.call(arguments, 2);
    return function(){
      return fn.apply(obj, args.concat(slice.call(arguments)));
    }
  };
  
  
  /***/ }),
  
  /***/ "./node_modules/component-emitter/index.js":
  /*!*************************************************!*\
    !*** ./node_modules/component-emitter/index.js ***!
    \*************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  
  /**
   * Expose `Emitter`.
   */
  
  if (true) {
    module.exports = Emitter;
  }
  
  /**
   * Initialize a new `Emitter`.
   *
   * @api public
   */
  
  function Emitter(obj) {
    if (obj) return mixin(obj);
  };
  
  /**
   * Mixin the emitter properties.
   *
   * @param {Object} obj
   * @return {Object}
   * @api private
   */
  
  function mixin(obj) {
    for (var key in Emitter.prototype) {
      obj[key] = Emitter.prototype[key];
    }
    return obj;
  }
  
  /**
   * Listen on the given `event` with `fn`.
   *
   * @param {String} event
   * @param {Function} fn
   * @return {Emitter}
   * @api public
   */
  
  Emitter.prototype.on =
  Emitter.prototype.addEventListener = function(event, fn){
    this._callbacks = this._callbacks || {};
    (this._callbacks['$' + event] = this._callbacks['$' + event] || [])
      .push(fn);
    return this;
  };
  
  /**
   * Adds an `event` listener that will be invoked a single
   * time then automatically removed.
   *
   * @param {String} event
   * @param {Function} fn
   * @return {Emitter}
   * @api public
   */
  
  Emitter.prototype.once = function(event, fn){
    function on() {
      this.off(event, on);
      fn.apply(this, arguments);
    }
  
    on.fn = fn;
    this.on(event, on);
    return this;
  };
  
  /**
   * Remove the given callback for `event` or all
   * registered callbacks.
   *
   * @param {String} event
   * @param {Function} fn
   * @return {Emitter}
   * @api public
   */
  
  Emitter.prototype.off =
  Emitter.prototype.removeListener =
  Emitter.prototype.removeAllListeners =
  Emitter.prototype.removeEventListener = function(event, fn){
    this._callbacks = this._callbacks || {};
  
    // all
    if (0 == arguments.length) {
      this._callbacks = {};
      return this;
    }
  
    // specific event
    var callbacks = this._callbacks['$' + event];
    if (!callbacks) return this;
  
    // remove all handlers
    if (1 == arguments.length) {
      delete this._callbacks['$' + event];
      return this;
    }
  
    // remove specific handler
    var cb;
    for (var i = 0; i < callbacks.length; i++) {
      cb = callbacks[i];
      if (cb === fn || cb.fn === fn) {
        callbacks.splice(i, 1);
        break;
      }
    }
    return this;
  };
  
  /**
   * Emit `event` with the given args.
   *
   * @param {String} event
   * @param {Mixed} ...
   * @return {Emitter}
   */
  
  Emitter.prototype.emit = function(event){
    this._callbacks = this._callbacks || {};
    var args = [].slice.call(arguments, 1)
      , callbacks = this._callbacks['$' + event];
  
    if (callbacks) {
      callbacks = callbacks.slice(0);
      for (var i = 0, len = callbacks.length; i < len; ++i) {
        callbacks[i].apply(this, args);
      }
    }
  
    return this;
  };
  
  /**
   * Return array of callbacks for `event`.
   *
   * @param {String} event
   * @return {Array}
   * @api public
   */
  
  Emitter.prototype.listeners = function(event){
    this._callbacks = this._callbacks || {};
    return this._callbacks['$' + event] || [];
  };
  
  /**
   * Check if this emitter has `event` handlers.
   *
   * @param {String} event
   * @return {Boolean}
   * @api public
   */
  
  Emitter.prototype.hasListeners = function(event){
    return !! this.listeners(event).length;
  };
  
  
  /***/ }),
  
  /***/ "./node_modules/component-inherit/index.js":
  /*!*************************************************!*\
    !*** ./node_modules/component-inherit/index.js ***!
    \*************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports) {
  
  
  module.exports = function(a, b){
    var fn = function(){};
    fn.prototype = b.prototype;
    a.prototype = new fn;
    a.prototype.constructor = a;
  };
  
  /***/ }),
  
  /***/ "./node_modules/decode-uri-component/index.js":
  /*!****************************************************!*\
    !*** ./node_modules/decode-uri-component/index.js ***!
    \****************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  "use strict";
  
  var token = '%[a-f0-9]{2}';
  var singleMatcher = new RegExp(token, 'gi');
  var multiMatcher = new RegExp('(' + token + ')+', 'gi');
  
  function decodeComponents(components, split) {
  	try {
  		// Try to decode the entire string first
  		return decodeURIComponent(components.join(''));
  	} catch (err) {
  		// Do nothing
  	}
  
  	if (components.length === 1) {
  		return components;
  	}
  
  	split = split || 1;
  
  	// Split the array in 2 parts
  	var left = components.slice(0, split);
  	var right = components.slice(split);
  
  	return Array.prototype.concat.call([], decodeComponents(left), decodeComponents(right));
  }
  
  function decode(input) {
  	try {
  		return decodeURIComponent(input);
  	} catch (err) {
  		var tokens = input.match(singleMatcher);
  
  		for (var i = 1; i < tokens.length; i++) {
  			input = decodeComponents(tokens, i).join('');
  
  			tokens = input.match(singleMatcher);
  		}
  
  		return input;
  	}
  }
  
  function customDecodeURIComponent(input) {
  	// Keep track of all the replacements and prefill the map with the `BOM`
  	var replaceMap = {
  		'%FE%FF': '\uFFFD\uFFFD',
  		'%FF%FE': '\uFFFD\uFFFD'
  	};
  
  	var match = multiMatcher.exec(input);
  	while (match) {
  		try {
  			// Decode as big chunks as possible
  			replaceMap[match[0]] = decodeURIComponent(match[0]);
  		} catch (err) {
  			var result = decode(match[0]);
  
  			if (result !== match[0]) {
  				replaceMap[match[0]] = result;
  			}
  		}
  
  		match = multiMatcher.exec(input);
  	}
  
  	// Add `%C2` at the end of the map to make sure it does not replace the combinator before everything else
  	replaceMap['%C2'] = '\uFFFD';
  
  	var entries = Object.keys(replaceMap);
  
  	for (var i = 0; i < entries.length; i++) {
  		// Replace all decoded components
  		var key = entries[i];
  		input = input.replace(new RegExp(key, 'g'), replaceMap[key]);
  	}
  
  	return input;
  }
  
  module.exports = function (encodedURI) {
  	if (typeof encodedURI !== 'string') {
  		throw new TypeError('Expected `encodedURI` to be of type `string`, got `' + typeof encodedURI + '`');
  	}
  
  	try {
  		encodedURI = encodedURI.replace(/\+/g, ' ');
  
  		// Try the built in decoder first
  		return decodeURIComponent(encodedURI);
  	} catch (err) {
  		// Fallback to a more advanced decoder
  		return customDecodeURIComponent(encodedURI);
  	}
  };
  
  
  /***/ }),
  
  /***/ "./node_modules/detect-browser/index.js":
  /*!**********************************************!*\
    !*** ./node_modules/detect-browser/index.js ***!
    \**********************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  "use strict";
  /* WEBPACK VAR INJECTION */(function(process) {
  Object.defineProperty(exports, "__esModule", { value: true });
  var BrowserInfo = /** @class */ (function () {
      function BrowserInfo(name, version, os) {
          this.name = name;
          this.version = version;
          this.os = os;
      }
      return BrowserInfo;
  }());
  exports.BrowserInfo = BrowserInfo;
  var NodeInfo = /** @class */ (function () {
      function NodeInfo(version) {
          this.version = version;
          this.name = 'node';
          this.os = process.platform;
      }
      return NodeInfo;
  }());
  exports.NodeInfo = NodeInfo;
  var BotInfo = /** @class */ (function () {
      function BotInfo() {
          this.bot = true; // NOTE: deprecated test name instead
          this.name = 'bot';
          this.version = null;
          this.os = null;
      }
      return BotInfo;
  }());
  exports.BotInfo = BotInfo;
  // tslint:disable-next-line:max-line-length
  var SEARCHBOX_UA_REGEX = /alexa|bot|crawl(er|ing)|facebookexternalhit|feedburner|google web preview|nagios|postrank|pingdom|slurp|spider|yahoo!|yandex/;
  var SEARCHBOT_OS_REGEX = /(nuhk)|(Googlebot)|(Yammybot)|(Openbot)|(Slurp)|(MSNBot)|(Ask Jeeves\/Teoma)|(ia_archiver)/;
  var REQUIRED_VERSION_PARTS = 3;
  var userAgentRules = [
      ['aol', /AOLShield\/([0-9\._]+)/],
      ['edge', /Edge\/([0-9\._]+)/],
      ['yandexbrowser', /YaBrowser\/([0-9\._]+)/],
      ['vivaldi', /Vivaldi\/([0-9\.]+)/],
      ['kakaotalk', /KAKAOTALK\s([0-9\.]+)/],
      ['samsung', /SamsungBrowser\/([0-9\.]+)/],
      ['silk', /\bSilk\/([0-9._-]+)\b/],
      ['chrome', /(?!Chrom.*OPR)Chrom(?:e|ium)\/([0-9\.]+)(:?\s|$)/],
      ['phantomjs', /PhantomJS\/([0-9\.]+)(:?\s|$)/],
      ['crios', /CriOS\/([0-9\.]+)(:?\s|$)/],
      ['firefox', /Firefox\/([0-9\.]+)(?:\s|$)/],
      ['fxios', /FxiOS\/([0-9\.]+)/],
      ['opera-mini', /Opera Mini.*Version\/([0-9\.]+)/],
      ['opera', /Opera\/([0-9\.]+)(?:\s|$)/],
      ['opera', /OPR\/([0-9\.]+)(:?\s|$)$/],
      ['ie', /Trident\/7\.0.*rv\:([0-9\.]+).*\).*Gecko$/],
      ['ie', /MSIE\s([0-9\.]+);.*Trident\/[4-7].0/],
      ['ie', /MSIE\s(7\.0)/],
      ['bb10', /BB10;\sTouch.*Version\/([0-9\.]+)/],
      ['android', /Android\s([0-9\.]+)/],
      ['ios', /Version\/([0-9\._]+).*Mobile.*Safari.*/],
      ['safari', /Version\/([0-9\._]+).*Safari/],
      ['facebook', /FBAV\/([0-9\.]+)/],
      ['instagram', /Instagram\s([0-9\.]+)/],
      ['ios-webview', /AppleWebKit\/([0-9\.]+).*Mobile/],
      ['ios-webview', /AppleWebKit\/([0-9\.]+).*Gecko\)$/],
      ['searchbot', SEARCHBOX_UA_REGEX],
  ];
  var operatingSystemRules = [
      ['iOS', /iP(hone|od|ad)/],
      ['Android OS', /Android/],
      ['BlackBerry OS', /BlackBerry|BB10/],
      ['Windows Mobile', /IEMobile/],
      ['Amazon OS', /Kindle/],
      ['Windows 3.11', /Win16/],
      ['Windows 95', /(Windows 95)|(Win95)|(Windows_95)/],
      ['Windows 98', /(Windows 98)|(Win98)/],
      ['Windows 2000', /(Windows NT 5.0)|(Windows 2000)/],
      ['Windows XP', /(Windows NT 5.1)|(Windows XP)/],
      ['Windows Server 2003', /(Windows NT 5.2)/],
      ['Windows Vista', /(Windows NT 6.0)/],
      ['Windows 7', /(Windows NT 6.1)/],
      ['Windows 8', /(Windows NT 6.2)/],
      ['Windows 8.1', /(Windows NT 6.3)/],
      ['Windows 10', /(Windows NT 10.0)/],
      ['Windows ME', /Windows ME/],
      ['Open BSD', /OpenBSD/],
      ['Sun OS', /SunOS/],
      ['Chrome OS', /CrOS/],
      ['Linux', /(Linux)|(X11)/],
      ['Mac OS', /(Mac_PowerPC)|(Macintosh)/],
      ['QNX', /QNX/],
      ['BeOS', /BeOS/],
      ['OS/2', /OS\/2/],
      ['Search Bot', SEARCHBOT_OS_REGEX],
  ];
  function detect() {
      if (typeof navigator !== 'undefined') {
          return parseUserAgent(navigator.userAgent);
      }
      return getNodeVersion();
  }
  exports.detect = detect;
  function parseUserAgent(ua) {
      // opted for using reduce here rather than Array#first with a regex.test call
      // this is primarily because using the reduce we only perform the regex
      // execution once rather than once for the test and for the exec again below
      // probably something that needs to be benchmarked though
      var matchedRule = ua !== '' &&
          userAgentRules.reduce(function (matched, _a) {
              var browser = _a[0], regex = _a[1];
              if (matched) {
                  return matched;
              }
              var uaMatch = regex.exec(ua);
              return !!uaMatch && [browser, uaMatch];
          }, false);
      if (!matchedRule) {
          return null;
      }
      var name = matchedRule[0], match = matchedRule[1];
      if (name === 'searchbot') {
          return new BotInfo();
      }
      var versionParts = match[1] && match[1].split(/[._]/).slice(0, 3);
      if (versionParts) {
          if (versionParts.length < REQUIRED_VERSION_PARTS) {
              versionParts = versionParts.concat(createVersionParts(REQUIRED_VERSION_PARTS - versionParts.length));
          }
      }
      else {
          versionParts = [];
      }
      return new BrowserInfo(name, versionParts.join('.'), detectOS(ua));
  }
  exports.parseUserAgent = parseUserAgent;
  function detectOS(ua) {
      for (var ii = 0, count = operatingSystemRules.length; ii < count; ii++) {
          var _a = operatingSystemRules[ii], os = _a[0], regex = _a[1];
          var match = regex.test(ua);
          if (match) {
              return os;
          }
      }
      return null;
  }
  exports.detectOS = detectOS;
  function getNodeVersion() {
      var isNode = typeof process !== 'undefined' && process.version;
      return isNode ? new NodeInfo(process.version.slice(1)) : null;
  }
  exports.getNodeVersion = getNodeVersion;
  function createVersionParts(count) {
      var output = [];
      for (var ii = 0; ii < count; ii++) {
          output.push('0');
      }
      return output;
  }
  
  /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../process/browser.js */ "./node_modules/process/browser.js")))
  
  /***/ }),
  
  /***/ "./node_modules/engine.io-parser/lib/browser.js":
  /*!******************************************************!*\
    !*** ./node_modules/engine.io-parser/lib/browser.js ***!
    \******************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  /* WEBPACK VAR INJECTION */(function(global) {/**
   * Module dependencies.
   */
  
  var keys = __webpack_require__(/*! ./keys */ "./node_modules/engine.io-parser/lib/keys.js");
  var hasBinary = __webpack_require__(/*! has-binary2 */ "./node_modules/has-binary2/index.js");
  var sliceBuffer = __webpack_require__(/*! arraybuffer.slice */ "./node_modules/arraybuffer.slice/index.js");
  var after = __webpack_require__(/*! after */ "./node_modules/after/index.js");
  var utf8 = __webpack_require__(/*! ./utf8 */ "./node_modules/engine.io-parser/lib/utf8.js");
  
  var base64encoder;
  if (global && global.ArrayBuffer) {
    base64encoder = __webpack_require__(/*! base64-arraybuffer */ "./node_modules/base64-arraybuffer/lib/base64-arraybuffer.js");
  }
  
  /**
   * Check if we are running an android browser. That requires us to use
   * ArrayBuffer with polling transports...
   *
   * http://ghinda.net/jpeg-blob-ajax-android/
   */
  
  var isAndroid = typeof navigator !== 'undefined' && /Android/i.test(navigator.userAgent);
  
  /**
   * Check if we are running in PhantomJS.
   * Uploading a Blob with PhantomJS does not work correctly, as reported here:
   * https://github.com/ariya/phantomjs/issues/11395
   * @type boolean
   */
  var isPhantomJS = typeof navigator !== 'undefined' && /PhantomJS/i.test(navigator.userAgent);
  
  /**
   * When true, avoids using Blobs to encode payloads.
   * @type boolean
   */
  var dontSendBlobs = isAndroid || isPhantomJS;
  
  /**
   * Current protocol version.
   */
  
  exports.protocol = 3;
  
  /**
   * Packet types.
   */
  
  var packets = exports.packets = {
      open:     0    // non-ws
    , close:    1    // non-ws
    , ping:     2
    , pong:     3
    , message:  4
    , upgrade:  5
    , noop:     6
  };
  
  var packetslist = keys(packets);
  
  /**
   * Premade error packet.
   */
  
  var err = { type: 'error', data: 'parser error' };
  
  /**
   * Create a blob api even for blob builder when vendor prefixes exist
   */
  
  var Blob = __webpack_require__(/*! blob */ "./node_modules/blob/index.js");
  
  /**
   * Encodes a packet.
   *
   *     <packet type id> [ <data> ]
   *
   * Example:
   *
   *     5hello world
   *     3
   *     4
   *
   * Binary is encoded in an identical principle
   *
   * @api private
   */
  
  exports.encodePacket = function (packet, supportsBinary, utf8encode, callback) {
    if (typeof supportsBinary === 'function') {
      callback = supportsBinary;
      supportsBinary = false;
    }
  
    if (typeof utf8encode === 'function') {
      callback = utf8encode;
      utf8encode = null;
    }
  
    var data = (packet.data === undefined)
      ? undefined
      : packet.data.buffer || packet.data;
  
    if (global.ArrayBuffer && data instanceof ArrayBuffer) {
      return encodeArrayBuffer(packet, supportsBinary, callback);
    } else if (Blob && data instanceof global.Blob) {
      return encodeBlob(packet, supportsBinary, callback);
    }
  
    // might be an object with { base64: true, data: dataAsBase64String }
    if (data && data.base64) {
      return encodeBase64Object(packet, callback);
    }
  
    // Sending data as a utf-8 string
    var encoded = packets[packet.type];
  
    // data fragment is optional
    if (undefined !== packet.data) {
      encoded += utf8encode ? utf8.encode(String(packet.data), { strict: false }) : String(packet.data);
    }
  
    return callback('' + encoded);
  
  };
  
  function encodeBase64Object(packet, callback) {
    // packet data is an object { base64: true, data: dataAsBase64String }
    var message = 'b' + exports.packets[packet.type] + packet.data.data;
    return callback(message);
  }
  
  /**
   * Encode packet helpers for binary types
   */
  
  function encodeArrayBuffer(packet, supportsBinary, callback) {
    if (!supportsBinary) {
      return exports.encodeBase64Packet(packet, callback);
    }
  
    var data = packet.data;
    var contentArray = new Uint8Array(data);
    var resultBuffer = new Uint8Array(1 + data.byteLength);
  
    resultBuffer[0] = packets[packet.type];
    for (var i = 0; i < contentArray.length; i++) {
      resultBuffer[i+1] = contentArray[i];
    }
  
    return callback(resultBuffer.buffer);
  }
  
  function encodeBlobAsArrayBuffer(packet, supportsBinary, callback) {
    if (!supportsBinary) {
      return exports.encodeBase64Packet(packet, callback);
    }
  
    var fr = new FileReader();
    fr.onload = function() {
      packet.data = fr.result;
      exports.encodePacket(packet, supportsBinary, true, callback);
    };
    return fr.readAsArrayBuffer(packet.data);
  }
  
  function encodeBlob(packet, supportsBinary, callback) {
    if (!supportsBinary) {
      return exports.encodeBase64Packet(packet, callback);
    }
  
    if (dontSendBlobs) {
      return encodeBlobAsArrayBuffer(packet, supportsBinary, callback);
    }
  
    var length = new Uint8Array(1);
    length[0] = packets[packet.type];
    var blob = new Blob([length.buffer, packet.data]);
  
    return callback(blob);
  }
  
  /**
   * Encodes a packet with binary data in a base64 string
   *
   * @param {Object} packet, has `type` and `data`
   * @return {String} base64 encoded message
   */
  
  exports.encodeBase64Packet = function(packet, callback) {
    var message = 'b' + exports.packets[packet.type];
    if (Blob && packet.data instanceof global.Blob) {
      var fr = new FileReader();
      fr.onload = function() {
        var b64 = fr.result.split(',')[1];
        callback(message + b64);
      };
      return fr.readAsDataURL(packet.data);
    }
  
    var b64data;
    try {
      b64data = String.fromCharCode.apply(null, new Uint8Array(packet.data));
    } catch (e) {
      // iPhone Safari doesn't let you apply with typed arrays
      var typed = new Uint8Array(packet.data);
      var basic = new Array(typed.length);
      for (var i = 0; i < typed.length; i++) {
        basic[i] = typed[i];
      }
      b64data = String.fromCharCode.apply(null, basic);
    }
    message += global.btoa(b64data);
    return callback(message);
  };
  
  /**
   * Decodes a packet. Changes format to Blob if requested.
   *
   * @return {Object} with `type` and `data` (if any)
   * @api private
   */
  
  exports.decodePacket = function (data, binaryType, utf8decode) {
    if (data === undefined) {
      return err;
    }
    // String data
    if (typeof data === 'string') {
      if (data.charAt(0) === 'b') {
        return exports.decodeBase64Packet(data.substr(1), binaryType);
      }
  
      if (utf8decode) {
        data = tryDecode(data);
        if (data === false) {
          return err;
        }
      }
      var type = data.charAt(0);
  
      if (Number(type) != type || !packetslist[type]) {
        return err;
      }
  
      if (data.length > 1) {
        return { type: packetslist[type], data: data.substring(1) };
      } else {
        return { type: packetslist[type] };
      }
    }
  
    var asArray = new Uint8Array(data);
    var type = asArray[0];
    var rest = sliceBuffer(data, 1);
    if (Blob && binaryType === 'blob') {
      rest = new Blob([rest]);
    }
    return { type: packetslist[type], data: rest };
  };
  
  function tryDecode(data) {
    try {
      data = utf8.decode(data, { strict: false });
    } catch (e) {
      return false;
    }
    return data;
  }
  
  /**
   * Decodes a packet encoded in a base64 string
   *
   * @param {String} base64 encoded message
   * @return {Object} with `type` and `data` (if any)
   */
  
  exports.decodeBase64Packet = function(msg, binaryType) {
    var type = packetslist[msg.charAt(0)];
    if (!base64encoder) {
      return { type: type, data: { base64: true, data: msg.substr(1) } };
    }
  
    var data = base64encoder.decode(msg.substr(1));
  
    if (binaryType === 'blob' && Blob) {
      data = new Blob([data]);
    }
  
    return { type: type, data: data };
  };
  
  /**
   * Encodes multiple messages (payload).
   *
   *     <length>:data
   *
   * Example:
   *
   *     11:hello world2:hi
   *
   * If any contents are binary, they will be encoded as base64 strings. Base64
   * encoded strings are marked with a b before the length specifier
   *
   * @param {Array} packets
   * @api private
   */
  
  exports.encodePayload = function (packets, supportsBinary, callback) {
    if (typeof supportsBinary === 'function') {
      callback = supportsBinary;
      supportsBinary = null;
    }
  
    var isBinary = hasBinary(packets);
  
    if (supportsBinary && isBinary) {
      if (Blob && !dontSendBlobs) {
        return exports.encodePayloadAsBlob(packets, callback);
      }
  
      return exports.encodePayloadAsArrayBuffer(packets, callback);
    }
  
    if (!packets.length) {
      return callback('0:');
    }
  
    function setLengthHeader(message) {
      return message.length + ':' + message;
    }
  
    function encodeOne(packet, doneCallback) {
      exports.encodePacket(packet, !isBinary ? false : supportsBinary, false, function(message) {
        doneCallback(null, setLengthHeader(message));
      });
    }
  
    map(packets, encodeOne, function(err, results) {
      return callback(results.join(''));
    });
  };
  
  /**
   * Async array map using after
   */
  
  function map(ary, each, done) {
    var result = new Array(ary.length);
    var next = after(ary.length, done);
  
    var eachWithIndex = function(i, el, cb) {
      each(el, function(error, msg) {
        result[i] = msg;
        cb(error, result);
      });
    };
  
    for (var i = 0; i < ary.length; i++) {
      eachWithIndex(i, ary[i], next);
    }
  }
  
  /*
   * Decodes data when a payload is maybe expected. Possible binary contents are
   * decoded from their base64 representation
   *
   * @param {String} data, callback method
   * @api public
   */
  
  exports.decodePayload = function (data, binaryType, callback) {
    if (typeof data !== 'string') {
      return exports.decodePayloadAsBinary(data, binaryType, callback);
    }
  
    if (typeof binaryType === 'function') {
      callback = binaryType;
      binaryType = null;
    }
  
    var packet;
    if (data === '') {
      // parser error - ignoring payload
      return callback(err, 0, 1);
    }
  
    var length = '', n, msg;
  
    for (var i = 0, l = data.length; i < l; i++) {
      var chr = data.charAt(i);
  
      if (chr !== ':') {
        length += chr;
        continue;
      }
  
      if (length === '' || (length != (n = Number(length)))) {
        // parser error - ignoring payload
        return callback(err, 0, 1);
      }
  
      msg = data.substr(i + 1, n);
  
      if (length != msg.length) {
        // parser error - ignoring payload
        return callback(err, 0, 1);
      }
  
      if (msg.length) {
        packet = exports.decodePacket(msg, binaryType, false);
  
        if (err.type === packet.type && err.data === packet.data) {
          // parser error in individual packet - ignoring payload
          return callback(err, 0, 1);
        }
  
        var ret = callback(packet, i + n, l);
        if (false === ret) return;
      }
  
      // advance cursor
      i += n;
      length = '';
    }
  
    if (length !== '') {
      // parser error - ignoring payload
      return callback(err, 0, 1);
    }
  
  };
  
  /**
   * Encodes multiple messages (payload) as binary.
   *
   * <1 = binary, 0 = string><number from 0-9><number from 0-9>[...]<number
   * 255><data>
   *
   * Example:
   * 1 3 255 1 2 3, if the binary contents are interpreted as 8 bit integers
   *
   * @param {Array} packets
   * @return {ArrayBuffer} encoded payload
   * @api private
   */
  
  exports.encodePayloadAsArrayBuffer = function(packets, callback) {
    if (!packets.length) {
      return callback(new ArrayBuffer(0));
    }
  
    function encodeOne(packet, doneCallback) {
      exports.encodePacket(packet, true, true, function(data) {
        return doneCallback(null, data);
      });
    }
  
    map(packets, encodeOne, function(err, encodedPackets) {
      var totalLength = encodedPackets.reduce(function(acc, p) {
        var len;
        if (typeof p === 'string'){
          len = p.length;
        } else {
          len = p.byteLength;
        }
        return acc + len.toString().length + len + 2; // string/binary identifier + separator = 2
      }, 0);
  
      var resultArray = new Uint8Array(totalLength);
  
      var bufferIndex = 0;
      encodedPackets.forEach(function(p) {
        var isString = typeof p === 'string';
        var ab = p;
        if (isString) {
          var view = new Uint8Array(p.length);
          for (var i = 0; i < p.length; i++) {
            view[i] = p.charCodeAt(i);
          }
          ab = view.buffer;
        }
  
        if (isString) { // not true binary
          resultArray[bufferIndex++] = 0;
        } else { // true binary
          resultArray[bufferIndex++] = 1;
        }
  
        var lenStr = ab.byteLength.toString();
        for (var i = 0; i < lenStr.length; i++) {
          resultArray[bufferIndex++] = parseInt(lenStr[i]);
        }
        resultArray[bufferIndex++] = 255;
  
        var view = new Uint8Array(ab);
        for (var i = 0; i < view.length; i++) {
          resultArray[bufferIndex++] = view[i];
        }
      });
  
      return callback(resultArray.buffer);
    });
  };
  
  /**
   * Encode as Blob
   */
  
  exports.encodePayloadAsBlob = function(packets, callback) {
    function encodeOne(packet, doneCallback) {
      exports.encodePacket(packet, true, true, function(encoded) {
        var binaryIdentifier = new Uint8Array(1);
        binaryIdentifier[0] = 1;
        if (typeof encoded === 'string') {
          var view = new Uint8Array(encoded.length);
          for (var i = 0; i < encoded.length; i++) {
            view[i] = encoded.charCodeAt(i);
          }
          encoded = view.buffer;
          binaryIdentifier[0] = 0;
        }
  
        var len = (encoded instanceof ArrayBuffer)
          ? encoded.byteLength
          : encoded.size;
  
        var lenStr = len.toString();
        var lengthAry = new Uint8Array(lenStr.length + 1);
        for (var i = 0; i < lenStr.length; i++) {
          lengthAry[i] = parseInt(lenStr[i]);
        }
        lengthAry[lenStr.length] = 255;
  
        if (Blob) {
          var blob = new Blob([binaryIdentifier.buffer, lengthAry.buffer, encoded]);
          doneCallback(null, blob);
        }
      });
    }
  
    map(packets, encodeOne, function(err, results) {
      return callback(new Blob(results));
    });
  };
  
  /*
   * Decodes data when a payload is maybe expected. Strings are decoded by
   * interpreting each byte as a key code for entries marked to start with 0. See
   * description of encodePayloadAsBinary
   *
   * @param {ArrayBuffer} data, callback method
   * @api public
   */
  
  exports.decodePayloadAsBinary = function (data, binaryType, callback) {
    if (typeof binaryType === 'function') {
      callback = binaryType;
      binaryType = null;
    }
  
    var bufferTail = data;
    var buffers = [];
  
    while (bufferTail.byteLength > 0) {
      var tailArray = new Uint8Array(bufferTail);
      var isString = tailArray[0] === 0;
      var msgLength = '';
  
      for (var i = 1; ; i++) {
        if (tailArray[i] === 255) break;
  
        // 310 = char length of Number.MAX_VALUE
        if (msgLength.length > 310) {
          return callback(err, 0, 1);
        }
  
        msgLength += tailArray[i];
      }
  
      bufferTail = sliceBuffer(bufferTail, 2 + msgLength.length);
      msgLength = parseInt(msgLength);
  
      var msg = sliceBuffer(bufferTail, 0, msgLength);
      if (isString) {
        try {
          msg = String.fromCharCode.apply(null, new Uint8Array(msg));
        } catch (e) {
          // iPhone Safari doesn't let you apply to typed arrays
          var typed = new Uint8Array(msg);
          msg = '';
          for (var i = 0; i < typed.length; i++) {
            msg += String.fromCharCode(typed[i]);
          }
        }
      }
  
      buffers.push(msg);
      bufferTail = sliceBuffer(bufferTail, msgLength);
    }
  
    var total = buffers.length;
    buffers.forEach(function(buffer, i) {
      callback(exports.decodePacket(buffer, binaryType, true), i, total);
    });
  };
  
  /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js")))
  
  /***/ }),
  
  /***/ "./node_modules/engine.io-parser/lib/keys.js":
  /*!***************************************************!*\
    !*** ./node_modules/engine.io-parser/lib/keys.js ***!
    \***************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports) {
  
  
  /**
   * Gets the keys for an object.
   *
   * @return {Array} keys
   * @api private
   */
  
  module.exports = Object.keys || function keys (obj){
    var arr = [];
    var has = Object.prototype.hasOwnProperty;
  
    for (var i in obj) {
      if (has.call(obj, i)) {
        arr.push(i);
      }
    }
    return arr;
  };
  
  
  /***/ }),
  
  /***/ "./node_modules/engine.io-parser/lib/utf8.js":
  /*!***************************************************!*\
    !*** ./node_modules/engine.io-parser/lib/utf8.js ***!
    \***************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  /* WEBPACK VAR INJECTION */(function(module, global) {var __WEBPACK_AMD_DEFINE_RESULT__;/*! https://mths.be/utf8js v2.1.2 by @mathias */
  ;(function(root) {
  
  	// Detect free variables `exports`
  	var freeExports = typeof exports == 'object' && exports;
  
  	// Detect free variable `module`
  	var freeModule = typeof module == 'object' && module &&
  		module.exports == freeExports && module;
  
  	// Detect free variable `global`, from Node.js or Browserified code,
  	// and use it as `root`
  	var freeGlobal = typeof global == 'object' && global;
  	if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) {
  		root = freeGlobal;
  	}
  
  	/*--------------------------------------------------------------------------*/
  
  	var stringFromCharCode = String.fromCharCode;
  
  	// Taken from https://mths.be/punycode
  	function ucs2decode(string) {
  		var output = [];
  		var counter = 0;
  		var length = string.length;
  		var value;
  		var extra;
  		while (counter < length) {
  			value = string.charCodeAt(counter++);
  			if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
  				// high surrogate, and there is a next character
  				extra = string.charCodeAt(counter++);
  				if ((extra & 0xFC00) == 0xDC00) { // low surrogate
  					output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
  				} else {
  					// unmatched surrogate; only append this code unit, in case the next
  					// code unit is the high surrogate of a surrogate pair
  					output.push(value);
  					counter--;
  				}
  			} else {
  				output.push(value);
  			}
  		}
  		return output;
  	}
  
  	// Taken from https://mths.be/punycode
  	function ucs2encode(array) {
  		var length = array.length;
  		var index = -1;
  		var value;
  		var output = '';
  		while (++index < length) {
  			value = array[index];
  			if (value > 0xFFFF) {
  				value -= 0x10000;
  				output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800);
  				value = 0xDC00 | value & 0x3FF;
  			}
  			output += stringFromCharCode(value);
  		}
  		return output;
  	}
  
  	function checkScalarValue(codePoint, strict) {
  		if (codePoint >= 0xD800 && codePoint <= 0xDFFF) {
  			if (strict) {
  				throw Error(
  					'Lone surrogate U+' + codePoint.toString(16).toUpperCase() +
  					' is not a scalar value'
  				);
  			}
  			return false;
  		}
  		return true;
  	}
  	/*--------------------------------------------------------------------------*/
  
  	function createByte(codePoint, shift) {
  		return stringFromCharCode(((codePoint >> shift) & 0x3F) | 0x80);
  	}
  
  	function encodeCodePoint(codePoint, strict) {
  		if ((codePoint & 0xFFFFFF80) == 0) { // 1-byte sequence
  			return stringFromCharCode(codePoint);
  		}
  		var symbol = '';
  		if ((codePoint & 0xFFFFF800) == 0) { // 2-byte sequence
  			symbol = stringFromCharCode(((codePoint >> 6) & 0x1F) | 0xC0);
  		}
  		else if ((codePoint & 0xFFFF0000) == 0) { // 3-byte sequence
  			if (!checkScalarValue(codePoint, strict)) {
  				codePoint = 0xFFFD;
  			}
  			symbol = stringFromCharCode(((codePoint >> 12) & 0x0F) | 0xE0);
  			symbol += createByte(codePoint, 6);
  		}
  		else if ((codePoint & 0xFFE00000) == 0) { // 4-byte sequence
  			symbol = stringFromCharCode(((codePoint >> 18) & 0x07) | 0xF0);
  			symbol += createByte(codePoint, 12);
  			symbol += createByte(codePoint, 6);
  		}
  		symbol += stringFromCharCode((codePoint & 0x3F) | 0x80);
  		return symbol;
  	}
  
  	function utf8encode(string, opts) {
  		opts = opts || {};
  		var strict = false !== opts.strict;
  
  		var codePoints = ucs2decode(string);
  		var length = codePoints.length;
  		var index = -1;
  		var codePoint;
  		var byteString = '';
  		while (++index < length) {
  			codePoint = codePoints[index];
  			byteString += encodeCodePoint(codePoint, strict);
  		}
  		return byteString;
  	}
  
  	/*--------------------------------------------------------------------------*/
  
  	function readContinuationByte() {
  		if (byteIndex >= byteCount) {
  			throw Error('Invalid byte index');
  		}
  
  		var continuationByte = byteArray[byteIndex] & 0xFF;
  		byteIndex++;
  
  		if ((continuationByte & 0xC0) == 0x80) {
  			return continuationByte & 0x3F;
  		}
  
  		// If we end up here, it’s not a continuation byte
  		throw Error('Invalid continuation byte');
  	}
  
  	function decodeSymbol(strict) {
  		var byte1;
  		var byte2;
  		var byte3;
  		var byte4;
  		var codePoint;
  
  		if (byteIndex > byteCount) {
  			throw Error('Invalid byte index');
  		}
  
  		if (byteIndex == byteCount) {
  			return false;
  		}
  
  		// Read first byte
  		byte1 = byteArray[byteIndex] & 0xFF;
  		byteIndex++;
  
  		// 1-byte sequence (no continuation bytes)
  		if ((byte1 & 0x80) == 0) {
  			return byte1;
  		}
  
  		// 2-byte sequence
  		if ((byte1 & 0xE0) == 0xC0) {
  			byte2 = readContinuationByte();
  			codePoint = ((byte1 & 0x1F) << 6) | byte2;
  			if (codePoint >= 0x80) {
  				return codePoint;
  			} else {
  				throw Error('Invalid continuation byte');
  			}
  		}
  
  		// 3-byte sequence (may include unpaired surrogates)
  		if ((byte1 & 0xF0) == 0xE0) {
  			byte2 = readContinuationByte();
  			byte3 = readContinuationByte();
  			codePoint = ((byte1 & 0x0F) << 12) | (byte2 << 6) | byte3;
  			if (codePoint >= 0x0800) {
  				return checkScalarValue(codePoint, strict) ? codePoint : 0xFFFD;
  			} else {
  				throw Error('Invalid continuation byte');
  			}
  		}
  
  		// 4-byte sequence
  		if ((byte1 & 0xF8) == 0xF0) {
  			byte2 = readContinuationByte();
  			byte3 = readContinuationByte();
  			byte4 = readContinuationByte();
  			codePoint = ((byte1 & 0x07) << 0x12) | (byte2 << 0x0C) |
  				(byte3 << 0x06) | byte4;
  			if (codePoint >= 0x010000 && codePoint <= 0x10FFFF) {
  				return codePoint;
  			}
  		}
  
  		throw Error('Invalid UTF-8 detected');
  	}
  
  	var byteArray;
  	var byteCount;
  	var byteIndex;
  	function utf8decode(byteString, opts) {
  		opts = opts || {};
  		var strict = false !== opts.strict;
  
  		byteArray = ucs2decode(byteString);
  		byteCount = byteArray.length;
  		byteIndex = 0;
  		var codePoints = [];
  		var tmp;
  		while ((tmp = decodeSymbol(strict)) !== false) {
  			codePoints.push(tmp);
  		}
  		return ucs2encode(codePoints);
  	}
  
  	/*--------------------------------------------------------------------------*/
  
  	var utf8 = {
  		'version': '2.1.2',
  		'encode': utf8encode,
  		'decode': utf8decode
  	};
  
  	// Some AMD build optimizers, like r.js, check for specific condition patterns
  	// like the following:
  	if (
  		true
  	) {
  		!(__WEBPACK_AMD_DEFINE_RESULT__ = (function() {
  			return utf8;
  		}).call(exports, __webpack_require__, exports, module),
  				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
  	}	else { var key, hasOwnProperty, object; }
  
  }(this));
  
  /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../webpack/buildin/module.js */ "./node_modules/webpack/buildin/module.js")(module), __webpack_require__(/*! ./../../webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js")))
  
  /***/ }),
  
  /***/ "./node_modules/enum/dist/enum.js":
  /*!****************************************!*\
    !*** ./node_modules/enum/dist/enum.js ***!
    \****************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  "use strict";
  /* WEBPACK VAR INJECTION */(function(global) {
  
  var _interopRequire = function (obj) { return obj && obj.__esModule ? obj["default"] : obj; };
  
  var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } };
  
  var EnumItem = _interopRequire(__webpack_require__(/*! ./enumItem */ "./node_modules/enum/dist/enumItem.js"));
  
  var isString = __webpack_require__(/*! ./isType */ "./node_modules/enum/dist/isType.js").isString;
  
  var indexOf = __webpack_require__(/*! ./indexOf */ "./node_modules/enum/dist/indexOf.js").indexOf;
  
  var isBuffer = _interopRequire(__webpack_require__(/*! is-buffer */ "./node_modules/is-buffer/index.js"));
  
  var endianness = "LE"; // for react-native
  
  /**
   * Represents an Enum with enum items.
   * @param {Array || Object}  map     This are the enum items.
   * @param {String || Object} options This are options. [optional]
   */
  
  var Enum = (function () {
    function Enum(map, options) {
      var _this = this;
  
      _classCallCheck(this, Enum);
  
      /* implement the "ref type interface", so that Enum types can
       * be used in `node-ffi` function declarations and invokations.
       * In C, these Enums act as `uint32_t` types.
       *
       * https://github.com/TooTallNate/ref#the-type-interface
       */
      this.size = 4;
      this.indirection = 1;
  
      if (options && isString(options)) {
        options = { name: options };
      }
  
      this._options = options || {};
      this._options.separator = this._options.separator || " | ";
      this._options.endianness = this._options.endianness || endianness;
      this._options.ignoreCase = this._options.ignoreCase || false;
      this._options.freez = this._options.freez || false;
  
      this.enums = [];
  
      if (map.length) {
        this._enumLastIndex = map.length;
        var array = map;
        map = {};
  
        for (var i = 0; i < array.length; i++) {
          map[array[i]] = Math.pow(2, i);
        }
      }
  
      for (var member in map) {
        guardReservedKeys(this._options.name, member);
        this[member] = new EnumItem(member, map[member], { ignoreCase: this._options.ignoreCase });
        this.enums.push(this[member]);
      }
      this._enumMap = map;
  
      if (this._options.ignoreCase) {
        this.getLowerCaseEnums = function () {
          var res = {};
          for (var i = 0, len = this.enums.length; i < len; i++) {
            res[this.enums[i].key.toLowerCase()] = this.enums[i];
          }
          return res;
        };
      }
  
      if (this._options.name) {
        this.name = this._options.name;
      }
  
      var isFlaggable = function () {
        for (var i = 0, len = _this.enums.length; i < len; i++) {
          var e = _this.enums[i];
  
          if (!(e.value !== 0 && !(e.value & e.value - 1))) {
            return false;
          }
        }
        return true;
      };
  
      this.isFlaggable = isFlaggable();
      if (this._options.freez) {
        this.freezeEnums(); //this will make instances of Enum non-extensible
      }
    }
  
    /**
     * Returns the appropriate EnumItem key.
     * @param  {EnumItem || String || Number} key The object to get with.
     * @return {String}                           The get result.
     */
  
    Enum.prototype.getKey = function getKey(value) {
      var item = this.get(value);
      if (item) {
        return item.key;
      }
    };
  
    /**
     * Returns the appropriate EnumItem value.
     * @param  {EnumItem || String || Number} key The object to get with.
     * @return {Number}                           The get result.
     */
  
    Enum.prototype.getValue = function getValue(key) {
      var item = this.get(key);
      if (item) {
        return item.value;
      }
    };
  
    /**
     * Returns the appropriate EnumItem.
     * @param  {EnumItem || String || Number} key The object to get with.
     * @return {EnumItem}                         The get result.
     */
  
    Enum.prototype.get = function get(key, offset) {
      if (key === null || key === undefined) {
        return;
      } // Buffer instance support, part of the ref Type interface
      if (isBuffer(key)) {
        key = key["readUInt32" + this._options.endianness](offset || 0);
      }
  
      if (EnumItem.isEnumItem(key)) {
        var foundIndex = indexOf.call(this.enums, key);
        if (foundIndex >= 0) {
          return key;
        }
        if (!this.isFlaggable || this.isFlaggable && key.key.indexOf(this._options.separator) < 0) {
          return;
        }
        return this.get(key.key);
      } else if (isString(key)) {
  
        var enums = this;
        if (this._options.ignoreCase) {
          enums = this.getLowerCaseEnums();
          key = key.toLowerCase();
        }
  
        if (key.indexOf(this._options.separator) > 0) {
          var parts = key.split(this._options.separator);
  
          var value = 0;
          for (var i = 0; i < parts.length; i++) {
            var part = parts[i];
  
            value |= enums[part].value;
          }
  
          return new EnumItem(key, value);
        } else {
          return enums[key];
        }
      } else {
        for (var m in this) {
          if (this.hasOwnProperty(m)) {
            if (this[m].value === key) {
              return this[m];
            }
          }
        }
  
        var result = null;
  
        if (this.isFlaggable) {
          for (var n in this) {
            if (this.hasOwnProperty(n)) {
              if ((key & this[n].value) !== 0) {
                if (result) {
                  result += this._options.separator;
                } else {
                  result = "";
                }
                result += n;
              }
            }
          }
        }
  
        return this.get(result || null);
      }
    };
  
    /**
     * Sets the Enum "value" onto the give `buffer` at the specified `offset`.
     * Part of the ref "Type interface".
     *
     * @param  {Buffer} buffer The Buffer instance to write to.
     * @param  {Number} offset The offset in the buffer to write to. Default 0.
     * @param  {EnumItem || String || Number} value The EnumItem to write.
     */
  
    Enum.prototype.set = function set(buffer, offset, value) {
      var item = this.get(value);
      if (item) {
        return buffer["writeUInt32" + this._options.endianness](item.value, offset || 0);
      }
    };
  
    /**
     * Define freezeEnums() as a property of the prototype.
     * make enumerable items nonconfigurable and deep freeze the properties. Throw Error on property setter.
     */
  
    Enum.prototype.freezeEnums = function freezeEnums() {
      function envSupportsFreezing() {
        return Object.isFrozen && Object.isSealed && Object.getOwnPropertyNames && Object.getOwnPropertyDescriptor && Object.defineProperties && Object.__defineGetter__ && Object.__defineSetter__;
      }
  
      function freezer(o) {
        var props = Object.getOwnPropertyNames(o);
        props.forEach(function (p) {
          if (!Object.getOwnPropertyDescriptor(o, p).configurable) {
            return;
          }
  
          Object.defineProperties(o, p, { writable: false, configurable: false });
        });
        return o;
      }
  
      function getPropertyValue(value) {
        return value;
      }
  
      function deepFreezeEnums(o) {
        if (typeof o !== "object" || o === null || Object.isFrozen(o) || Object.isSealed(o)) {
          return;
        }
        for (var key in o) {
          if (o.hasOwnProperty(key)) {
            o.__defineGetter__(key, getPropertyValue.bind(null, o[key]));
            o.__defineSetter__(key, function throwPropertySetError(value) {
              throw TypeError("Cannot redefine property; Enum Type is not extensible.");
            });
            deepFreezeEnums(o[key]);
          }
        }
        if (Object.freeze) {
          Object.freeze(o);
        } else {
          freezer(o);
        }
      }
  
      if (envSupportsFreezing()) {
        deepFreezeEnums(this);
      }
  
      return this;
    };
  
    /**
     * Returns JSON object representation of this Enum.
     * @return {String} JSON object representation of this Enum.
     */
  
    Enum.prototype.toJSON = function toJSON() {
      return this._enumMap;
    };
  
    /**
     * Extends the existing Enum with a New Map.
     * @param  {Array}  map  Map to extend from
     */
  
    Enum.prototype.extend = function extend(map) {
      if (map.length) {
        var array = map;
        map = {};
  
        for (var i = 0; i < array.length; i++) {
          var exponent = this._enumLastIndex + i;
          map[array[i]] = Math.pow(2, exponent);
        }
  
        for (var member in map) {
          guardReservedKeys(this._options.name, member);
          this[member] = new EnumItem(member, map[member], { ignoreCase: this._options.ignoreCase });
          this.enums.push(this[member]);
        }
  
        for (var key in this._enumMap) {
          map[key] = this._enumMap[key];
        }
  
        this._enumLastIndex += map.length;
        this._enumMap = map;
  
        if (this._options.freez) {
          this.freezeEnums(); //this will make instances of new Enum non-extensible
        }
      }
    };
  
    /**
     * Registers the Enum Type globally in node.js.
     * @param  {String} key Global variable. [optional]
     */
  
    Enum.register = function register() {
      var key = arguments[0] === undefined ? "Enum" : arguments[0];
  
      if (!global[key]) {
        global[key] = Enum;
      }
    };
  
    return Enum;
  })();
  
  module.exports = Enum;
  
  // private
  
  var reservedKeys = ["_options", "get", "getKey", "getValue", "enums", "isFlaggable", "_enumMap", "toJSON", "_enumLastIndex"];
  
  function guardReservedKeys(customName, key) {
    if (customName && key === "name" || indexOf.call(reservedKeys, key) >= 0) {
      throw new Error("Enum key " + key + " is a reserved word!");
    }
  }
  /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js")))
  
  /***/ }),
  
  /***/ "./node_modules/enum/dist/enumItem.js":
  /*!********************************************!*\
    !*** ./node_modules/enum/dist/enumItem.js ***!
    \********************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  "use strict";
  
  
  var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } };
  
  var _isType = __webpack_require__(/*! ./isType */ "./node_modules/enum/dist/isType.js");
  
  var isObject = _isType.isObject;
  var isString = _isType.isString;
  
  /**
   * Represents an Item of an Enum.
   * @param {String} key   The Enum key.
   * @param {Number} value The Enum value.
   */
  
  var EnumItem = (function () {
  
    /*constructor reference so that, this.constructor===EnumItem//=>true */
  
    function EnumItem(key, value) {
      var options = arguments[2] === undefined ? {} : arguments[2];
  
      _classCallCheck(this, EnumItem);
  
      this.key = key;
      this.value = value;
  
      this._options = options;
      this._options.ignoreCase = this._options.ignoreCase || false;
    }
  
    /**
     * Checks if the flagged EnumItem has the passing object.
     * @param  {EnumItem || String || Number} value The object to check with.
     * @return {Boolean}                            The check result.
     */
  
    EnumItem.prototype.has = function has(value) {
      if (EnumItem.isEnumItem(value)) {
        return (this.value & value.value) !== 0;
      } else if (isString(value)) {
        if (this._options.ignoreCase) {
          return this.key.toLowerCase().indexOf(value.toLowerCase()) >= 0;
        }
        return this.key.indexOf(value) >= 0;
      } else {
        return (this.value & value) !== 0;
      }
    };
  
    /**
     * Checks if the EnumItem is the same as the passing object.
     * @param  {EnumItem || String || Number} key The object to check with.
     * @return {Boolean}                          The check result.
     */
  
    EnumItem.prototype.is = function is(key) {
      if (EnumItem.isEnumItem(key)) {
        return this.key === key.key;
      } else if (isString(key)) {
        if (this._options.ignoreCase) {
          return this.key.toLowerCase() === key.toLowerCase();
        }
        return this.key === key;
      } else {
        return this.value === key;
      }
    };
  
    /**
     * Returns String representation of this EnumItem.
     * @return {String} String representation of this EnumItem.
     */
  
    EnumItem.prototype.toString = function toString() {
      return this.key;
    };
  
    /**
     * Returns JSON object representation of this EnumItem.
     * @return {String} JSON object representation of this EnumItem.
     */
  
    EnumItem.prototype.toJSON = function toJSON() {
      return this.key;
    };
  
    /**
     * Returns the value to compare with.
     * @return {String} The value to compare with.
     */
  
    EnumItem.prototype.valueOf = function valueOf() {
      return this.value;
    };
  
    EnumItem.isEnumItem = function isEnumItem(value) {
      return value instanceof EnumItem || isObject(value) && value.key !== undefined && value.value !== undefined;
    };
  
    return EnumItem;
  })();
  
  module.exports = EnumItem;
  
  /***/ }),
  
  /***/ "./node_modules/enum/dist/indexOf.js":
  /*!*******************************************!*\
    !*** ./node_modules/enum/dist/indexOf.js ***!
    \*******************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  "use strict";
  
  
  exports.__esModule = true;
  var indexOf = Array.prototype.indexOf || function (find, i /*opt*/) {
    if (i === undefined) i = 0;
    if (i < 0) i += this.length;
    if (i < 0) i = 0;
    for (var n = this.length; i < n; i++) if (i in this && this[i] === find) return i;
    return -1;
  };
  exports.indexOf = indexOf;
  
  /***/ }),
  
  /***/ "./node_modules/enum/dist/isType.js":
  /*!******************************************!*\
    !*** ./node_modules/enum/dist/isType.js ***!
    \******************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  "use strict";
  
  
  exports.__esModule = true;
  var isType = function (type, value) {
    return typeof value === type;
  };
  exports.isType = isType;
  var isObject = function (value) {
    return isType("object", value);
  };
  exports.isObject = isObject;
  var isString = function (value) {
    return isType("string", value);
  };
  exports.isString = isString;
  
  /***/ }),
  
  /***/ "./node_modules/enum/index.js":
  /*!************************************!*\
    !*** ./node_modules/enum/index.js ***!
    \************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  module.exports = __webpack_require__(/*! ./dist/enum */ "./node_modules/enum/dist/enum.js");
  
  
  /***/ }),
  
  /***/ "./node_modules/has-binary2/index.js":
  /*!*******************************************!*\
    !*** ./node_modules/has-binary2/index.js ***!
    \*******************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  /* WEBPACK VAR INJECTION */(function(global) {/* global Blob File */
  
  /*
   * Module requirements.
   */
  
  var isArray = __webpack_require__(/*! isarray */ "./node_modules/has-binary2/node_modules/isarray/index.js");
  
  var toString = Object.prototype.toString;
  var withNativeBlob = typeof global.Blob === 'function' || toString.call(global.Blob) === '[object BlobConstructor]';
  var withNativeFile = typeof global.File === 'function' || toString.call(global.File) === '[object FileConstructor]';
  
  /**
   * Module exports.
   */
  
  module.exports = hasBinary;
  
  /**
   * Checks for binary data.
   *
   * Supports Buffer, ArrayBuffer, Blob and File.
   *
   * @param {Object} anything
   * @api public
   */
  
  function hasBinary (obj) {
    if (!obj || typeof obj !== 'object') {
      return false;
    }
  
    if (isArray(obj)) {
      for (var i = 0, l = obj.length; i < l; i++) {
        if (hasBinary(obj[i])) {
          return true;
        }
      }
      return false;
    }
  
    if ((typeof global.Buffer === 'function' && global.Buffer.isBuffer && global.Buffer.isBuffer(obj)) ||
       (typeof global.ArrayBuffer === 'function' && obj instanceof ArrayBuffer) ||
       (withNativeBlob && obj instanceof Blob) ||
       (withNativeFile && obj instanceof File)
      ) {
      return true;
    }
  
    // see: https://github.com/Automattic/has-binary/pull/4
    if (obj.toJSON && typeof obj.toJSON === 'function' && arguments.length === 1) {
      return hasBinary(obj.toJSON(), true);
    }
  
    for (var key in obj) {
      if (Object.prototype.hasOwnProperty.call(obj, key) && hasBinary(obj[key])) {
        return true;
      }
    }
  
    return false;
  }
  
  /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js")))
  
  /***/ }),
  
  /***/ "./node_modules/has-binary2/node_modules/isarray/index.js":
  /*!****************************************************************!*\
    !*** ./node_modules/has-binary2/node_modules/isarray/index.js ***!
    \****************************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports) {
  
  var toString = {}.toString;
  
  module.exports = Array.isArray || function (arr) {
    return toString.call(arr) == '[object Array]';
  };
  
  
  /***/ }),
  
  /***/ "./node_modules/has-cors/index.js":
  /*!****************************************!*\
    !*** ./node_modules/has-cors/index.js ***!
    \****************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports) {
  
  
  /**
   * Module exports.
   *
   * Logic borrowed from Modernizr:
   *
   *   - https://github.com/Modernizr/Modernizr/blob/master/feature-detects/cors.js
   */
  
  try {
    module.exports = typeof XMLHttpRequest !== 'undefined' &&
      'withCredentials' in new XMLHttpRequest();
  } catch (err) {
    // if XMLHttp support is disabled in IE then it will throw
    // when trying to create
    module.exports = false;
  }
  
  
  /***/ }),
  
  /***/ "./node_modules/ieee754/index.js":
  /*!***************************************!*\
    !*** ./node_modules/ieee754/index.js ***!
    \***************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports) {
  
  exports.read = function (buffer, offset, isLE, mLen, nBytes) {
    var e, m
    var eLen = nBytes * 8 - mLen - 1
    var eMax = (1 << eLen) - 1
    var eBias = eMax >> 1
    var nBits = -7
    var i = isLE ? (nBytes - 1) : 0
    var d = isLE ? -1 : 1
    var s = buffer[offset + i]
  
    i += d
  
    e = s & ((1 << (-nBits)) - 1)
    s >>= (-nBits)
    nBits += eLen
    for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8) {}
  
    m = e & ((1 << (-nBits)) - 1)
    e >>= (-nBits)
    nBits += mLen
    for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8) {}
  
    if (e === 0) {
      e = 1 - eBias
    } else if (e === eMax) {
      return m ? NaN : ((s ? -1 : 1) * Infinity)
    } else {
      m = m + Math.pow(2, mLen)
      e = e - eBias
    }
    return (s ? -1 : 1) * m * Math.pow(2, e - mLen)
  }
  
  exports.write = function (buffer, value, offset, isLE, mLen, nBytes) {
    var e, m, c
    var eLen = nBytes * 8 - mLen - 1
    var eMax = (1 << eLen) - 1
    var eBias = eMax >> 1
    var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0)
    var i = isLE ? 0 : (nBytes - 1)
    var d = isLE ? 1 : -1
    var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0
  
    value = Math.abs(value)
  
    if (isNaN(value) || value === Infinity) {
      m = isNaN(value) ? 1 : 0
      e = eMax
    } else {
      e = Math.floor(Math.log(value) / Math.LN2)
      if (value * (c = Math.pow(2, -e)) < 1) {
        e--
        c *= 2
      }
      if (e + eBias >= 1) {
        value += rt / c
      } else {
        value += rt * Math.pow(2, 1 - eBias)
      }
      if (value * c >= 2) {
        e++
        c /= 2
      }
  
      if (e + eBias >= eMax) {
        m = 0
        e = eMax
      } else if (e + eBias >= 1) {
        m = (value * c - 1) * Math.pow(2, mLen)
        e = e + eBias
      } else {
        m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen)
        e = 0
      }
    }
  
    for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {}
  
    e = (e << mLen) | m
    eLen += mLen
    for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {}
  
    buffer[offset + i - d] |= s * 128
  }
  
  
  /***/ }),
  
  /***/ "./node_modules/indexof/index.js":
  /*!***************************************!*\
    !*** ./node_modules/indexof/index.js ***!
    \***************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports) {
  
  
  var indexOf = [].indexOf;
  
  module.exports = function(arr, obj){
    if (indexOf) return arr.indexOf(obj);
    for (var i = 0; i < arr.length; ++i) {
      if (arr[i] === obj) return i;
    }
    return -1;
  };
  
  /***/ }),
  
  /***/ "./node_modules/is-buffer/index.js":
  /*!*****************************************!*\
    !*** ./node_modules/is-buffer/index.js ***!
    \*****************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports) {
  
  /*!
   * Determine if an object is a Buffer
   *
   * @author   Feross Aboukhadijeh <feross@feross.org> <http://feross.org>
   * @license  MIT
   */
  
  // The _isBuffer check is for Safari 5-7 support, because it's missing
  // Object.prototype.constructor. Remove this eventually
  module.exports = function (obj) {
    return obj != null && (isBuffer(obj) || isSlowBuffer(obj) || !!obj._isBuffer)
  }
  
  function isBuffer (obj) {
    return !!obj.constructor && typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj)
  }
  
  // For Node v0.10 support. Remove this eventually.
  function isSlowBuffer (obj) {
    return typeof obj.readFloatLE === 'function' && typeof obj.slice === 'function' && isBuffer(obj.slice(0, 0))
  }
  
  
  /***/ }),
  
  /***/ "./node_modules/isarray/index.js":
  /*!***************************************!*\
    !*** ./node_modules/isarray/index.js ***!
    \***************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports) {
  
  var toString = {}.toString;
  
  module.exports = Array.isArray || function (arr) {
    return toString.call(arr) == '[object Array]';
  };
  
  
  /***/ }),
  
  /***/ "./node_modules/js-binarypack/lib/binarypack.js":
  /*!******************************************************!*\
    !*** ./node_modules/js-binarypack/lib/binarypack.js ***!
    \******************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  var BufferBuilder = __webpack_require__(/*! ./bufferbuilder */ "./node_modules/js-binarypack/lib/bufferbuilder.js").BufferBuilder;
  var binaryFeatures = __webpack_require__(/*! ./bufferbuilder */ "./node_modules/js-binarypack/lib/bufferbuilder.js").binaryFeatures;
  
  var BinaryPack = {
    unpack: function(data){
      var unpacker = new Unpacker(data);
      return unpacker.unpack();
    },
    pack: function(data){
      var packer = new Packer();
      packer.pack(data);
      var buffer = packer.getBuffer();
      return buffer;
    }
  };
  
  module.exports = BinaryPack;
  
  function Unpacker (data){
    // Data is ArrayBuffer
    this.index = 0;
    this.dataBuffer = data;
    this.dataView = new Uint8Array(this.dataBuffer);
    this.length = this.dataBuffer.byteLength;
  }
  
  Unpacker.prototype.unpack = function(){
    var type = this.unpack_uint8();
    if (type < 0x80){
      var positive_fixnum = type;
      return positive_fixnum;
    } else if ((type ^ 0xe0) < 0x20){
      var negative_fixnum = (type ^ 0xe0) - 0x20;
      return negative_fixnum;
    }
    var size;
    if ((size = type ^ 0xa0) <= 0x0f){
      return this.unpack_raw(size);
    } else if ((size = type ^ 0xb0) <= 0x0f){
      return this.unpack_string(size);
    } else if ((size = type ^ 0x90) <= 0x0f){
      return this.unpack_array(size);
    } else if ((size = type ^ 0x80) <= 0x0f){
      return this.unpack_map(size);
    }
    switch(type){
      case 0xc0:
        return null;
      case 0xc1:
        return undefined;
      case 0xc2:
        return false;
      case 0xc3:
        return true;
      case 0xca:
        return this.unpack_float();
      case 0xcb:
        return this.unpack_double();
      case 0xcc:
        return this.unpack_uint8();
      case 0xcd:
        return this.unpack_uint16();
      case 0xce:
        return this.unpack_uint32();
      case 0xcf:
        return this.unpack_uint64();
      case 0xd0:
        return this.unpack_int8();
      case 0xd1:
        return this.unpack_int16();
      case 0xd2:
        return this.unpack_int32();
      case 0xd3:
        return this.unpack_int64();
      case 0xd4:
        return undefined;
      case 0xd5:
        return undefined;
      case 0xd6:
        return undefined;
      case 0xd7:
        return undefined;
      case 0xd8:
        size = this.unpack_uint16();
        return this.unpack_string(size);
      case 0xd9:
        size = this.unpack_uint32();
        return this.unpack_string(size);
      case 0xda:
        size = this.unpack_uint16();
        return this.unpack_raw(size);
      case 0xdb:
        size = this.unpack_uint32();
        return this.unpack_raw(size);
      case 0xdc:
        size = this.unpack_uint16();
        return this.unpack_array(size);
      case 0xdd:
        size = this.unpack_uint32();
        return this.unpack_array(size);
      case 0xde:
        size = this.unpack_uint16();
        return this.unpack_map(size);
      case 0xdf:
        size = this.unpack_uint32();
        return this.unpack_map(size);
    }
  }
  
  Unpacker.prototype.unpack_uint8 = function(){
    var byte = this.dataView[this.index] & 0xff;
    this.index++;
    return byte;
  };
  
  Unpacker.prototype.unpack_uint16 = function(){
    var bytes = this.read(2);
    var uint16 =
      ((bytes[0] & 0xff) * 256) + (bytes[1] & 0xff);
    this.index += 2;
    return uint16;
  }
  
  Unpacker.prototype.unpack_uint32 = function(){
    var bytes = this.read(4);
    var uint32 =
       ((bytes[0]  * 256 +
         bytes[1]) * 256 +
         bytes[2]) * 256 +
         bytes[3];
    this.index += 4;
    return uint32;
  }
  
  Unpacker.prototype.unpack_uint64 = function(){
    var bytes = this.read(8);
    var uint64 =
     ((((((bytes[0]  * 256 +
         bytes[1]) * 256 +
         bytes[2]) * 256 +
         bytes[3]) * 256 +
         bytes[4]) * 256 +
         bytes[5]) * 256 +
         bytes[6]) * 256 +
         bytes[7];
    this.index += 8;
    return uint64;
  }
  
  
  Unpacker.prototype.unpack_int8 = function(){
    var uint8 = this.unpack_uint8();
    return (uint8 < 0x80 ) ? uint8 : uint8 - (1 << 8);
  };
  
  Unpacker.prototype.unpack_int16 = function(){
    var uint16 = this.unpack_uint16();
    return (uint16 < 0x8000 ) ? uint16 : uint16 - (1 << 16);
  }
  
  Unpacker.prototype.unpack_int32 = function(){
    var uint32 = this.unpack_uint32();
    return (uint32 < Math.pow(2, 31) ) ? uint32 :
      uint32 - Math.pow(2, 32);
  }
  
  Unpacker.prototype.unpack_int64 = function(){
    var uint64 = this.unpack_uint64();
    return (uint64 < Math.pow(2, 63) ) ? uint64 :
      uint64 - Math.pow(2, 64);
  }
  
  Unpacker.prototype.unpack_raw = function(size){
    if ( this.length < this.index + size){
      throw new Error('BinaryPackFailure: index is out of range'
        + ' ' + this.index + ' ' + size + ' ' + this.length);
    }
    var buf = this.dataBuffer.slice(this.index, this.index + size);
    this.index += size;
  
      //buf = util.bufferToString(buf);
  
    return buf;
  }
  
  Unpacker.prototype.unpack_string = function(size){
    var bytes = this.read(size);
    var i = 0, str = '', c, code;
    while(i < size){
      c = bytes[i];
      if ( c < 128){
        str += String.fromCharCode(c);
        i++;
      } else if ((c ^ 0xc0) < 32){
        code = ((c ^ 0xc0) << 6) | (bytes[i+1] & 63);
        str += String.fromCharCode(code);
        i += 2;
      } else {
        code = ((c & 15) << 12) | ((bytes[i+1] & 63) << 6) |
          (bytes[i+2] & 63);
        str += String.fromCharCode(code);
        i += 3;
      }
    }
    this.index += size;
    return str;
  }
  
  Unpacker.prototype.unpack_array = function(size){
    var objects = new Array(size);
    for(var i = 0; i < size ; i++){
      objects[i] = this.unpack();
    }
    return objects;
  }
  
  Unpacker.prototype.unpack_map = function(size){
    var map = {};
    for(var i = 0; i < size ; i++){
      var key  = this.unpack();
      var value = this.unpack();
      map[key] = value;
    }
    return map;
  }
  
  Unpacker.prototype.unpack_float = function(){
    var uint32 = this.unpack_uint32();
    var sign = uint32 >> 31;
    var exp  = ((uint32 >> 23) & 0xff) - 127;
    var fraction = ( uint32 & 0x7fffff ) | 0x800000;
    return (sign == 0 ? 1 : -1) *
      fraction * Math.pow(2, exp - 23);
  }
  
  Unpacker.prototype.unpack_double = function(){
    var h32 = this.unpack_uint32();
    var l32 = this.unpack_uint32();
    var sign = h32 >> 31;
    var exp  = ((h32 >> 20) & 0x7ff) - 1023;
    var hfrac = ( h32 & 0xfffff ) | 0x100000;
    var frac = hfrac * Math.pow(2, exp - 20) +
      l32   * Math.pow(2, exp - 52);
    return (sign == 0 ? 1 : -1) * frac;
  }
  
  Unpacker.prototype.read = function(length){
    var j = this.index;
    if (j + length <= this.length) {
      return this.dataView.subarray(j, j + length);
    } else {
      throw new Error('BinaryPackFailure: read index out of range');
    }
  }
  
  function Packer(){
    this.bufferBuilder = new BufferBuilder();
  }
  
  Packer.prototype.getBuffer = function(){
    return this.bufferBuilder.getBuffer();
  }
  
  Packer.prototype.pack = function(value){
    var type = typeof(value);
    if (type == 'string'){
      this.pack_string(value);
    } else if (type == 'number'){
      if (Math.floor(value) === value){
        this.pack_integer(value);
      } else{
        this.pack_double(value);
      }
    } else if (type == 'boolean'){
      if (value === true){
        this.bufferBuilder.append(0xc3);
      } else if (value === false){
        this.bufferBuilder.append(0xc2);
      }
    } else if (type == 'undefined'){
      this.bufferBuilder.append(0xc0);
    } else if (type == 'object'){
      if (value === null){
        this.bufferBuilder.append(0xc0);
      } else {
        var constructor = value.constructor;
        if (constructor == Array){
          this.pack_array(value);
        } else if (constructor == Blob || constructor == File) {
          this.pack_bin(value);
        } else if (constructor == ArrayBuffer) {
          if(binaryFeatures.useArrayBufferView) {
            this.pack_bin(new Uint8Array(value));
          } else {
            this.pack_bin(value);
          }
        } else if ('BYTES_PER_ELEMENT' in value){
          if(binaryFeatures.useArrayBufferView) {
            this.pack_bin(new Uint8Array(value.buffer));
          } else {
            this.pack_bin(value.buffer);
          }
        } else if (constructor == Object){
          this.pack_object(value);
        } else if (constructor == Date){
          this.pack_string(value.toString());
        } else if (typeof value.toBinaryPack == 'function'){
          this.bufferBuilder.append(value.toBinaryPack());
        } else {
          throw new Error('Type "' + constructor.toString() + '" not yet supported');
        }
      }
    } else {
      throw new Error('Type "' + type + '" not yet supported');
    }
    this.bufferBuilder.flush();
  }
  
  
  Packer.prototype.pack_bin = function(blob){
    var length = blob.length || blob.byteLength || blob.size;
    if (length <= 0x0f){
      this.pack_uint8(0xa0 + length);
    } else if (length <= 0xffff){
      this.bufferBuilder.append(0xda) ;
      this.pack_uint16(length);
    } else if (length <= 0xffffffff){
      this.bufferBuilder.append(0xdb);
      this.pack_uint32(length);
    } else{
      throw new Error('Invalid length');
    }
    this.bufferBuilder.append(blob);
  }
  
  Packer.prototype.pack_string = function(str){
    var length = utf8Length(str);
  
    if (length <= 0x0f){
      this.pack_uint8(0xb0 + length);
    } else if (length <= 0xffff){
      this.bufferBuilder.append(0xd8) ;
      this.pack_uint16(length);
    } else if (length <= 0xffffffff){
      this.bufferBuilder.append(0xd9);
      this.pack_uint32(length);
    } else{
      throw new Error('Invalid length');
    }
    this.bufferBuilder.append(str);
  }
  
  Packer.prototype.pack_array = function(ary){
    var length = ary.length;
    if (length <= 0x0f){
      this.pack_uint8(0x90 + length);
    } else if (length <= 0xffff){
      this.bufferBuilder.append(0xdc)
      this.pack_uint16(length);
    } else if (length <= 0xffffffff){
      this.bufferBuilder.append(0xdd);
      this.pack_uint32(length);
    } else{
      throw new Error('Invalid length');
    }
    for(var i = 0; i < length ; i++){
      this.pack(ary[i]);
    }
  }
  
  Packer.prototype.pack_integer = function(num){
    if ( -0x20 <= num && num <= 0x7f){
      this.bufferBuilder.append(num & 0xff);
    } else if (0x00 <= num && num <= 0xff){
      this.bufferBuilder.append(0xcc);
      this.pack_uint8(num);
    } else if (-0x80 <= num && num <= 0x7f){
      this.bufferBuilder.append(0xd0);
      this.pack_int8(num);
    } else if ( 0x0000 <= num && num <= 0xffff){
      this.bufferBuilder.append(0xcd);
      this.pack_uint16(num);
    } else if (-0x8000 <= num && num <= 0x7fff){
      this.bufferBuilder.append(0xd1);
      this.pack_int16(num);
    } else if ( 0x00000000 <= num && num <= 0xffffffff){
      this.bufferBuilder.append(0xce);
      this.pack_uint32(num);
    } else if (-0x80000000 <= num && num <= 0x7fffffff){
      this.bufferBuilder.append(0xd2);
      this.pack_int32(num);
    } else if (-0x8000000000000000 <= num && num <= 0x7FFFFFFFFFFFFFFF){
      this.bufferBuilder.append(0xd3);
      this.pack_int64(num);
    } else if (0x0000000000000000 <= num && num <= 0xFFFFFFFFFFFFFFFF){
      this.bufferBuilder.append(0xcf);
      this.pack_uint64(num);
    } else{
      throw new Error('Invalid integer');
    }
  }
  
  Packer.prototype.pack_double = function(num){
    var sign = 0;
    if (num < 0){
      sign = 1;
      num = -num;
    }
    var exp  = Math.floor(Math.log(num) / Math.LN2);
    var frac0 = num / Math.pow(2, exp) - 1;
    var frac1 = Math.floor(frac0 * Math.pow(2, 52));
    var b32   = Math.pow(2, 32);
    var h32 = (sign << 31) | ((exp+1023) << 20) |
        (frac1 / b32) & 0x0fffff;
    var l32 = frac1 % b32;
    this.bufferBuilder.append(0xcb);
    this.pack_int32(h32);
    this.pack_int32(l32);
  }
  
  Packer.prototype.pack_object = function(obj){
    var keys = Object.keys(obj);
    var length = keys.length;
    if (length <= 0x0f){
      this.pack_uint8(0x80 + length);
    } else if (length <= 0xffff){
      this.bufferBuilder.append(0xde);
      this.pack_uint16(length);
    } else if (length <= 0xffffffff){
      this.bufferBuilder.append(0xdf);
      this.pack_uint32(length);
    } else{
      throw new Error('Invalid length');
    }
    for(var prop in obj){
      if (obj.hasOwnProperty(prop)){
        this.pack(prop);
        this.pack(obj[prop]);
      }
    }
  }
  
  Packer.prototype.pack_uint8 = function(num){
    this.bufferBuilder.append(num);
  }
  
  Packer.prototype.pack_uint16 = function(num){
    this.bufferBuilder.append(num >> 8);
    this.bufferBuilder.append(num & 0xff);
  }
  
  Packer.prototype.pack_uint32 = function(num){
    var n = num & 0xffffffff;
    this.bufferBuilder.append((n & 0xff000000) >>> 24);
    this.bufferBuilder.append((n & 0x00ff0000) >>> 16);
    this.bufferBuilder.append((n & 0x0000ff00) >>>  8);
    this.bufferBuilder.append((n & 0x000000ff));
  }
  
  Packer.prototype.pack_uint64 = function(num){
    var high = num / Math.pow(2, 32);
    var low  = num % Math.pow(2, 32);
    this.bufferBuilder.append((high & 0xff000000) >>> 24);
    this.bufferBuilder.append((high & 0x00ff0000) >>> 16);
    this.bufferBuilder.append((high & 0x0000ff00) >>>  8);
    this.bufferBuilder.append((high & 0x000000ff));
    this.bufferBuilder.append((low  & 0xff000000) >>> 24);
    this.bufferBuilder.append((low  & 0x00ff0000) >>> 16);
    this.bufferBuilder.append((low  & 0x0000ff00) >>>  8);
    this.bufferBuilder.append((low  & 0x000000ff));
  }
  
  Packer.prototype.pack_int8 = function(num){
    this.bufferBuilder.append(num & 0xff);
  }
  
  Packer.prototype.pack_int16 = function(num){
    this.bufferBuilder.append((num & 0xff00) >> 8);
    this.bufferBuilder.append(num & 0xff);
  }
  
  Packer.prototype.pack_int32 = function(num){
    this.bufferBuilder.append((num >>> 24) & 0xff);
    this.bufferBuilder.append((num & 0x00ff0000) >>> 16);
    this.bufferBuilder.append((num & 0x0000ff00) >>> 8);
    this.bufferBuilder.append((num & 0x000000ff));
  }
  
  Packer.prototype.pack_int64 = function(num){
    var high = Math.floor(num / Math.pow(2, 32));
    var low  = num % Math.pow(2, 32);
    this.bufferBuilder.append((high & 0xff000000) >>> 24);
    this.bufferBuilder.append((high & 0x00ff0000) >>> 16);
    this.bufferBuilder.append((high & 0x0000ff00) >>>  8);
    this.bufferBuilder.append((high & 0x000000ff));
    this.bufferBuilder.append((low  & 0xff000000) >>> 24);
    this.bufferBuilder.append((low  & 0x00ff0000) >>> 16);
    this.bufferBuilder.append((low  & 0x0000ff00) >>>  8);
    this.bufferBuilder.append((low  & 0x000000ff));
  }
  
  function _utf8Replace(m){
    var code = m.charCodeAt(0);
  
    if(code <= 0x7ff) return '00';
    if(code <= 0xffff) return '000';
    if(code <= 0x1fffff) return '0000';
    if(code <= 0x3ffffff) return '00000';
    return '000000';
  }
  
  function utf8Length(str){
    if (str.length > 600) {
      // Blob method faster for large strings
      return (new Blob([str])).size;
    } else {
      return str.replace(/[^\u0000-\u007F]/g, _utf8Replace).length;
    }
  }
  
  
  /***/ }),
  
  /***/ "./node_modules/js-binarypack/lib/bufferbuilder.js":
  /*!*********************************************************!*\
    !*** ./node_modules/js-binarypack/lib/bufferbuilder.js ***!
    \*********************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports) {
  
  var binaryFeatures = {};
  binaryFeatures.useBlobBuilder = (function(){
    try {
      new Blob([]);
      return false;
    } catch (e) {
      return true;
    }
  })();
  
  binaryFeatures.useArrayBufferView = !binaryFeatures.useBlobBuilder && (function(){
    try {
      return (new Blob([new Uint8Array([])])).size === 0;
    } catch (e) {
      return true;
    }
  })();
  
  module.exports.binaryFeatures = binaryFeatures;
  var BlobBuilder = module.exports.BlobBuilder;
  if (typeof window != 'undefined') {
    BlobBuilder = module.exports.BlobBuilder = window.WebKitBlobBuilder ||
      window.MozBlobBuilder || window.MSBlobBuilder || window.BlobBuilder;
  }
  
  function BufferBuilder(){
    this._pieces = [];
    this._parts = [];
  }
  
  BufferBuilder.prototype.append = function(data) {
    if(typeof data === 'number') {
      this._pieces.push(data);
    } else {
      this.flush();
      this._parts.push(data);
    }
  };
  
  BufferBuilder.prototype.flush = function() {
    if (this._pieces.length > 0) {
      var buf = new Uint8Array(this._pieces);
      if(!binaryFeatures.useArrayBufferView) {
        buf = buf.buffer;
      }
      this._parts.push(buf);
      this._pieces = [];
    }
  };
  
  BufferBuilder.prototype.getBuffer = function() {
    this.flush();
    if(binaryFeatures.useBlobBuilder) {
      var builder = new BlobBuilder();
      for(var i = 0, ii = this._parts.length; i < ii; i++) {
        builder.append(this._parts[i]);
      }
      return builder.getBlob();
    } else {
      return new Blob(this._parts);
    }
  };
  
  module.exports.BufferBuilder = BufferBuilder;
  
  
  /***/ }),
  
  /***/ "./node_modules/ms/index.js":
  /*!**********************************!*\
    !*** ./node_modules/ms/index.js ***!
    \**********************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports) {
  
  /**
   * Helpers.
   */
  
  var s = 1000;
  var m = s * 60;
  var h = m * 60;
  var d = h * 24;
  var y = d * 365.25;
  
  /**
   * Parse or format the given `val`.
   *
   * Options:
   *
   *  - `long` verbose formatting [false]
   *
   * @param {String|Number} val
   * @param {Object} [options]
   * @throws {Error} throw an error if val is not a non-empty string or a number
   * @return {String|Number}
   * @api public
   */
  
  module.exports = function(val, options) {
    options = options || {};
    var type = typeof val;
    if (type === 'string' && val.length > 0) {
      return parse(val);
    } else if (type === 'number' && isNaN(val) === false) {
      return options.long ? fmtLong(val) : fmtShort(val);
    }
    throw new Error(
      'val is not a non-empty string or a valid number. val=' +
        JSON.stringify(val)
    );
  };
  
  /**
   * Parse the given `str` and return milliseconds.
   *
   * @param {String} str
   * @return {Number}
   * @api private
   */
  
  function parse(str) {
    str = String(str);
    if (str.length > 100) {
      return;
    }
    var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(
      str
    );
    if (!match) {
      return;
    }
    var n = parseFloat(match[1]);
    var type = (match[2] || 'ms').toLowerCase();
    switch (type) {
      case 'years':
      case 'year':
      case 'yrs':
      case 'yr':
      case 'y':
        return n * y;
      case 'days':
      case 'day':
      case 'd':
        return n * d;
      case 'hours':
      case 'hour':
      case 'hrs':
      case 'hr':
      case 'h':
        return n * h;
      case 'minutes':
      case 'minute':
      case 'mins':
      case 'min':
      case 'm':
        return n * m;
      case 'seconds':
      case 'second':
      case 'secs':
      case 'sec':
      case 's':
        return n * s;
      case 'milliseconds':
      case 'millisecond':
      case 'msecs':
      case 'msec':
      case 'ms':
        return n;
      default:
        return undefined;
    }
  }
  
  /**
   * Short format for `ms`.
   *
   * @param {Number} ms
   * @return {String}
   * @api private
   */
  
  function fmtShort(ms) {
    if (ms >= d) {
      return Math.round(ms / d) + 'd';
    }
    if (ms >= h) {
      return Math.round(ms / h) + 'h';
    }
    if (ms >= m) {
      return Math.round(ms / m) + 'm';
    }
    if (ms >= s) {
      return Math.round(ms / s) + 's';
    }
    return ms + 'ms';
  }
  
  /**
   * Long format for `ms`.
   *
   * @param {Number} ms
   * @return {String}
   * @api private
   */
  
  function fmtLong(ms) {
    return plural(ms, d, 'day') ||
      plural(ms, h, 'hour') ||
      plural(ms, m, 'minute') ||
      plural(ms, s, 'second') ||
      ms + ' ms';
  }
  
  /**
   * Pluralization helper.
   */
  
  function plural(ms, n, name) {
    if (ms < n) {
      return;
    }
    if (ms < n * 1.5) {
      return Math.floor(ms / n) + ' ' + name;
    }
    return Math.ceil(ms / n) + ' ' + name + 's';
  }
  
  
  /***/ }),
  
  /***/ "./node_modules/node-libs-browser/node_modules/buffer/index.js":
  /*!*********************************************************************!*\
    !*** ./node_modules/node-libs-browser/node_modules/buffer/index.js ***!
    \*********************************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  "use strict";
  /* WEBPACK VAR INJECTION */(function(global) {/*!
   * The buffer module from node.js, for the browser.
   *
   * @author   Feross Aboukhadijeh <feross@feross.org> <http://feross.org>
   * @license  MIT
   */
  /* eslint-disable no-proto */
  
  
  
  var base64 = __webpack_require__(/*! base64-js */ "./node_modules/base64-js/index.js")
  var ieee754 = __webpack_require__(/*! ieee754 */ "./node_modules/ieee754/index.js")
  var isArray = __webpack_require__(/*! isarray */ "./node_modules/isarray/index.js")
  
  exports.Buffer = Buffer
  exports.SlowBuffer = SlowBuffer
  exports.INSPECT_MAX_BYTES = 50
  
  /**
   * If `Buffer.TYPED_ARRAY_SUPPORT`:
   *   === true    Use Uint8Array implementation (fastest)
   *   === false   Use Object implementation (most compatible, even IE6)
   *
   * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+,
   * Opera 11.6+, iOS 4.2+.
   *
   * Due to various browser bugs, sometimes the Object implementation will be used even
   * when the browser supports typed arrays.
   *
   * Note:
   *
   *   - Firefox 4-29 lacks support for adding new properties to `Uint8Array` instances,
   *     See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438.
   *
   *   - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function.
   *
   *   - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of
   *     incorrect length in some situations.
  
   * We detect these buggy browsers and set `Buffer.TYPED_ARRAY_SUPPORT` to `false` so they
   * get the Object implementation, which is slower but behaves correctly.
   */
  Buffer.TYPED_ARRAY_SUPPORT = global.TYPED_ARRAY_SUPPORT !== undefined
    ? global.TYPED_ARRAY_SUPPORT
    : typedArraySupport()
  
  /*
   * Export kMaxLength after typed array support is determined.
   */
  exports.kMaxLength = kMaxLength()
  
  function typedArraySupport () {
    try {
      var arr = new Uint8Array(1)
      arr.__proto__ = {__proto__: Uint8Array.prototype, foo: function () { return 42 }}
      return arr.foo() === 42 && // typed array instances can be augmented
          typeof arr.subarray === 'function' && // chrome 9-10 lack `subarray`
          arr.subarray(1, 1).byteLength === 0 // ie10 has broken `subarray`
    } catch (e) {
      return false
    }
  }
  
  function kMaxLength () {
    return Buffer.TYPED_ARRAY_SUPPORT
      ? 0x7fffffff
      : 0x3fffffff
  }
  
  function createBuffer (that, length) {
    if (kMaxLength() < length) {
      throw new RangeError('Invalid typed array length')
    }
    if (Buffer.TYPED_ARRAY_SUPPORT) {
      // Return an augmented `Uint8Array` instance, for best performance
      that = new Uint8Array(length)
      that.__proto__ = Buffer.prototype
    } else {
      // Fallback: Return an object instance of the Buffer class
      if (that === null) {
        that = new Buffer(length)
      }
      that.length = length
    }
  
    return that
  }
  
  /**
   * The Buffer constructor returns instances of `Uint8Array` that have their
   * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of
   * `Uint8Array`, so the returned instances will have all the node `Buffer` methods
   * and the `Uint8Array` methods. Square bracket notation works as expected -- it
   * returns a single octet.
   *
   * The `Uint8Array` prototype remains unmodified.
   */
  
  function Buffer (arg, encodingOrOffset, length) {
    if (!Buffer.TYPED_ARRAY_SUPPORT && !(this instanceof Buffer)) {
      return new Buffer(arg, encodingOrOffset, length)
    }
  
    // Common case.
    if (typeof arg === 'number') {
      if (typeof encodingOrOffset === 'string') {
        throw new Error(
          'If encoding is specified then the first argument must be a string'
        )
      }
      return allocUnsafe(this, arg)
    }
    return from(this, arg, encodingOrOffset, length)
  }
  
  Buffer.poolSize = 8192 // not used by this implementation
  
  // TODO: Legacy, not needed anymore. Remove in next major version.
  Buffer._augment = function (arr) {
    arr.__proto__ = Buffer.prototype
    return arr
  }
  
  function from (that, value, encodingOrOffset, length) {
    if (typeof value === 'number') {
      throw new TypeError('"value" argument must not be a number')
    }
  
    if (typeof ArrayBuffer !== 'undefined' && value instanceof ArrayBuffer) {
      return fromArrayBuffer(that, value, encodingOrOffset, length)
    }
  
    if (typeof value === 'string') {
      return fromString(that, value, encodingOrOffset)
    }
  
    return fromObject(that, value)
  }
  
  /**
   * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError
   * if value is a number.
   * Buffer.from(str[, encoding])
   * Buffer.from(array)
   * Buffer.from(buffer)
   * Buffer.from(arrayBuffer[, byteOffset[, length]])
   **/
  Buffer.from = function (value, encodingOrOffset, length) {
    return from(null, value, encodingOrOffset, length)
  }
  
  if (Buffer.TYPED_ARRAY_SUPPORT) {
    Buffer.prototype.__proto__ = Uint8Array.prototype
    Buffer.__proto__ = Uint8Array
    if (typeof Symbol !== 'undefined' && Symbol.species &&
        Buffer[Symbol.species] === Buffer) {
      // Fix subarray() in ES2016. See: https://github.com/feross/buffer/pull/97
      Object.defineProperty(Buffer, Symbol.species, {
        value: null,
        configurable: true
      })
    }
  }
  
  function assertSize (size) {
    if (typeof size !== 'number') {
      throw new TypeError('"size" argument must be a number')
    } else if (size < 0) {
      throw new RangeError('"size" argument must not be negative')
    }
  }
  
  function alloc (that, size, fill, encoding) {
    assertSize(size)
    if (size <= 0) {
      return createBuffer(that, size)
    }
    if (fill !== undefined) {
      // Only pay attention to encoding if it's a string. This
      // prevents accidentally sending in a number that would
      // be interpretted as a start offset.
      return typeof encoding === 'string'
        ? createBuffer(that, size).fill(fill, encoding)
        : createBuffer(that, size).fill(fill)
    }
    return createBuffer(that, size)
  }
  
  /**
   * Creates a new filled Buffer instance.
   * alloc(size[, fill[, encoding]])
   **/
  Buffer.alloc = function (size, fill, encoding) {
    return alloc(null, size, fill, encoding)
  }
  
  function allocUnsafe (that, size) {
    assertSize(size)
    that = createBuffer(that, size < 0 ? 0 : checked(size) | 0)
    if (!Buffer.TYPED_ARRAY_SUPPORT) {
      for (var i = 0; i < size; ++i) {
        that[i] = 0
      }
    }
    return that
  }
  
  /**
   * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance.
   * */
  Buffer.allocUnsafe = function (size) {
    return allocUnsafe(null, size)
  }
  /**
   * Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance.
   */
  Buffer.allocUnsafeSlow = function (size) {
    return allocUnsafe(null, size)
  }
  
  function fromString (that, string, encoding) {
    if (typeof encoding !== 'string' || encoding === '') {
      encoding = 'utf8'
    }
  
    if (!Buffer.isEncoding(encoding)) {
      throw new TypeError('"encoding" must be a valid string encoding')
    }
  
    var length = byteLength(string, encoding) | 0
    that = createBuffer(that, length)
  
    var actual = that.write(string, encoding)
  
    if (actual !== length) {
      // Writing a hex string, for example, that contains invalid characters will
      // cause everything after the first invalid character to be ignored. (e.g.
      // 'abxxcd' will be treated as 'ab')
      that = that.slice(0, actual)
    }
  
    return that
  }
  
  function fromArrayLike (that, array) {
    var length = array.length < 0 ? 0 : checked(array.length) | 0
    that = createBuffer(that, length)
    for (var i = 0; i < length; i += 1) {
      that[i] = array[i] & 255
    }
    return that
  }
  
  function fromArrayBuffer (that, array, byteOffset, length) {
    array.byteLength // this throws if `array` is not a valid ArrayBuffer
  
    if (byteOffset < 0 || array.byteLength < byteOffset) {
      throw new RangeError('\'offset\' is out of bounds')
    }
  
    if (array.byteLength < byteOffset + (length || 0)) {
      throw new RangeError('\'length\' is out of bounds')
    }
  
    if (byteOffset === undefined && length === undefined) {
      array = new Uint8Array(array)
    } else if (length === undefined) {
      array = new Uint8Array(array, byteOffset)
    } else {
      array = new Uint8Array(array, byteOffset, length)
    }
  
    if (Buffer.TYPED_ARRAY_SUPPORT) {
      // Return an augmented `Uint8Array` instance, for best performance
      that = array
      that.__proto__ = Buffer.prototype
    } else {
      // Fallback: Return an object instance of the Buffer class
      that = fromArrayLike(that, array)
    }
    return that
  }
  
  function fromObject (that, obj) {
    if (Buffer.isBuffer(obj)) {
      var len = checked(obj.length) | 0
      that = createBuffer(that, len)
  
      if (that.length === 0) {
        return that
      }
  
      obj.copy(that, 0, 0, len)
      return that
    }
  
    if (obj) {
      if ((typeof ArrayBuffer !== 'undefined' &&
          obj.buffer instanceof ArrayBuffer) || 'length' in obj) {
        if (typeof obj.length !== 'number' || isnan(obj.length)) {
          return createBuffer(that, 0)
        }
        return fromArrayLike(that, obj)
      }
  
      if (obj.type === 'Buffer' && isArray(obj.data)) {
        return fromArrayLike(that, obj.data)
      }
    }
  
    throw new TypeError('First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.')
  }
  
  function checked (length) {
    // Note: cannot use `length < kMaxLength()` here because that fails when
    // length is NaN (which is otherwise coerced to zero.)
    if (length >= kMaxLength()) {
      throw new RangeError('Attempt to allocate Buffer larger than maximum ' +
                           'size: 0x' + kMaxLength().toString(16) + ' bytes')
    }
    return length | 0
  }
  
  function SlowBuffer (length) {
    if (+length != length) { // eslint-disable-line eqeqeq
      length = 0
    }
    return Buffer.alloc(+length)
  }
  
  Buffer.isBuffer = function isBuffer (b) {
    return !!(b != null && b._isBuffer)
  }
  
  Buffer.compare = function compare (a, b) {
    if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) {
      throw new TypeError('Arguments must be Buffers')
    }
  
    if (a === b) return 0
  
    var x = a.length
    var y = b.length
  
    for (var i = 0, len = Math.min(x, y); i < len; ++i) {
      if (a[i] !== b[i]) {
        x = a[i]
        y = b[i]
        break
      }
    }
  
    if (x < y) return -1
    if (y < x) return 1
    return 0
  }
  
  Buffer.isEncoding = function isEncoding (encoding) {
    switch (String(encoding).toLowerCase()) {
      case 'hex':
      case 'utf8':
      case 'utf-8':
      case 'ascii':
      case 'latin1':
      case 'binary':
      case 'base64':
      case 'ucs2':
      case 'ucs-2':
      case 'utf16le':
      case 'utf-16le':
        return true
      default:
        return false
    }
  }
  
  Buffer.concat = function concat (list, length) {
    if (!isArray(list)) {
      throw new TypeError('"list" argument must be an Array of Buffers')
    }
  
    if (list.length === 0) {
      return Buffer.alloc(0)
    }
  
    var i
    if (length === undefined) {
      length = 0
      for (i = 0; i < list.length; ++i) {
        length += list[i].length
      }
    }
  
    var buffer = Buffer.allocUnsafe(length)
    var pos = 0
    for (i = 0; i < list.length; ++i) {
      var buf = list[i]
      if (!Buffer.isBuffer(buf)) {
        throw new TypeError('"list" argument must be an Array of Buffers')
      }
      buf.copy(buffer, pos)
      pos += buf.length
    }
    return buffer
  }
  
  function byteLength (string, encoding) {
    if (Buffer.isBuffer(string)) {
      return string.length
    }
    if (typeof ArrayBuffer !== 'undefined' && typeof ArrayBuffer.isView === 'function' &&
        (ArrayBuffer.isView(string) || string instanceof ArrayBuffer)) {
      return string.byteLength
    }
    if (typeof string !== 'string') {
      string = '' + string
    }
  
    var len = string.length
    if (len === 0) return 0
  
    // Use a for loop to avoid recursion
    var loweredCase = false
    for (;;) {
      switch (encoding) {
        case 'ascii':
        case 'latin1':
        case 'binary':
          return len
        case 'utf8':
        case 'utf-8':
        case undefined:
          return utf8ToBytes(string).length
        case 'ucs2':
        case 'ucs-2':
        case 'utf16le':
        case 'utf-16le':
          return len * 2
        case 'hex':
          return len >>> 1
        case 'base64':
          return base64ToBytes(string).length
        default:
          if (loweredCase) return utf8ToBytes(string).length // assume utf8
          encoding = ('' + encoding).toLowerCase()
          loweredCase = true
      }
    }
  }
  Buffer.byteLength = byteLength
  
  function slowToString (encoding, start, end) {
    var loweredCase = false
  
    // No need to verify that "this.length <= MAX_UINT32" since it's a read-only
    // property of a typed array.
  
    // This behaves neither like String nor Uint8Array in that we set start/end
    // to their upper/lower bounds if the value passed is out of range.
    // undefined is handled specially as per ECMA-262 6th Edition,
    // Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization.
    if (start === undefined || start < 0) {
      start = 0
    }
    // Return early if start > this.length. Done here to prevent potential uint32
    // coercion fail below.
    if (start > this.length) {
      return ''
    }
  
    if (end === undefined || end > this.length) {
      end = this.length
    }
  
    if (end <= 0) {
      return ''
    }
  
    // Force coersion to uint32. This will also coerce falsey/NaN values to 0.
    end >>>= 0
    start >>>= 0
  
    if (end <= start) {
      return ''
    }
  
    if (!encoding) encoding = 'utf8'
  
    while (true) {
      switch (encoding) {
        case 'hex':
          return hexSlice(this, start, end)
  
        case 'utf8':
        case 'utf-8':
          return utf8Slice(this, start, end)
  
        case 'ascii':
          return asciiSlice(this, start, end)
  
        case 'latin1':
        case 'binary':
          return latin1Slice(this, start, end)
  
        case 'base64':
          return base64Slice(this, start, end)
  
        case 'ucs2':
        case 'ucs-2':
        case 'utf16le':
        case 'utf-16le':
          return utf16leSlice(this, start, end)
  
        default:
          if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)
          encoding = (encoding + '').toLowerCase()
          loweredCase = true
      }
    }
  }
  
  // The property is used by `Buffer.isBuffer` and `is-buffer` (in Safari 5-7) to detect
  // Buffer instances.
  Buffer.prototype._isBuffer = true
  
  function swap (b, n, m) {
    var i = b[n]
    b[n] = b[m]
    b[m] = i
  }
  
  Buffer.prototype.swap16 = function swap16 () {
    var len = this.length
    if (len % 2 !== 0) {
      throw new RangeError('Buffer size must be a multiple of 16-bits')
    }
    for (var i = 0; i < len; i += 2) {
      swap(this, i, i + 1)
    }
    return this
  }
  
  Buffer.prototype.swap32 = function swap32 () {
    var len = this.length
    if (len % 4 !== 0) {
      throw new RangeError('Buffer size must be a multiple of 32-bits')
    }
    for (var i = 0; i < len; i += 4) {
      swap(this, i, i + 3)
      swap(this, i + 1, i + 2)
    }
    return this
  }
  
  Buffer.prototype.swap64 = function swap64 () {
    var len = this.length
    if (len % 8 !== 0) {
      throw new RangeError('Buffer size must be a multiple of 64-bits')
    }
    for (var i = 0; i < len; i += 8) {
      swap(this, i, i + 7)
      swap(this, i + 1, i + 6)
      swap(this, i + 2, i + 5)
      swap(this, i + 3, i + 4)
    }
    return this
  }
  
  Buffer.prototype.toString = function toString () {
    var length = this.length | 0
    if (length === 0) return ''
    if (arguments.length === 0) return utf8Slice(this, 0, length)
    return slowToString.apply(this, arguments)
  }
  
  Buffer.prototype.equals = function equals (b) {
    if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer')
    if (this === b) return true
    return Buffer.compare(this, b) === 0
  }
  
  Buffer.prototype.inspect = function inspect () {
    var str = ''
    var max = exports.INSPECT_MAX_BYTES
    if (this.length > 0) {
      str = this.toString('hex', 0, max).match(/.{2}/g).join(' ')
      if (this.length > max) str += ' ... '
    }
    return '<Buffer ' + str + '>'
  }
  
  Buffer.prototype.compare = function compare (target, start, end, thisStart, thisEnd) {
    if (!Buffer.isBuffer(target)) {
      throw new TypeError('Argument must be a Buffer')
    }
  
    if (start === undefined) {
      start = 0
    }
    if (end === undefined) {
      end = target ? target.length : 0
    }
    if (thisStart === undefined) {
      thisStart = 0
    }
    if (thisEnd === undefined) {
      thisEnd = this.length
    }
  
    if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) {
      throw new RangeError('out of range index')
    }
  
    if (thisStart >= thisEnd && start >= end) {
      return 0
    }
    if (thisStart >= thisEnd) {
      return -1
    }
    if (start >= end) {
      return 1
    }
  
    start >>>= 0
    end >>>= 0
    thisStart >>>= 0
    thisEnd >>>= 0
  
    if (this === target) return 0
  
    var x = thisEnd - thisStart
    var y = end - start
    var len = Math.min(x, y)
  
    var thisCopy = this.slice(thisStart, thisEnd)
    var targetCopy = target.slice(start, end)
  
    for (var i = 0; i < len; ++i) {
      if (thisCopy[i] !== targetCopy[i]) {
        x = thisCopy[i]
        y = targetCopy[i]
        break
      }
    }
  
    if (x < y) return -1
    if (y < x) return 1
    return 0
  }
  
  // Finds either the first index of `val` in `buffer` at offset >= `byteOffset`,
  // OR the last index of `val` in `buffer` at offset <= `byteOffset`.
  //
  // Arguments:
  // - buffer - a Buffer to search
  // - val - a string, Buffer, or number
  // - byteOffset - an index into `buffer`; will be clamped to an int32
  // - encoding - an optional encoding, relevant is val is a string
  // - dir - true for indexOf, false for lastIndexOf
  function bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) {
    // Empty buffer means no match
    if (buffer.length === 0) return -1
  
    // Normalize byteOffset
    if (typeof byteOffset === 'string') {
      encoding = byteOffset
      byteOffset = 0
    } else if (byteOffset > 0x7fffffff) {
      byteOffset = 0x7fffffff
    } else if (byteOffset < -0x80000000) {
      byteOffset = -0x80000000
    }
    byteOffset = +byteOffset  // Coerce to Number.
    if (isNaN(byteOffset)) {
      // byteOffset: it it's undefined, null, NaN, "foo", etc, search whole buffer
      byteOffset = dir ? 0 : (buffer.length - 1)
    }
  
    // Normalize byteOffset: negative offsets start from the end of the buffer
    if (byteOffset < 0) byteOffset = buffer.length + byteOffset
    if (byteOffset >= buffer.length) {
      if (dir) return -1
      else byteOffset = buffer.length - 1
    } else if (byteOffset < 0) {
      if (dir) byteOffset = 0
      else return -1
    }
  
    // Normalize val
    if (typeof val === 'string') {
      val = Buffer.from(val, encoding)
    }
  
    // Finally, search either indexOf (if dir is true) or lastIndexOf
    if (Buffer.isBuffer(val)) {
      // Special case: looking for empty string/buffer always fails
      if (val.length === 0) {
        return -1
      }
      return arrayIndexOf(buffer, val, byteOffset, encoding, dir)
    } else if (typeof val === 'number') {
      val = val & 0xFF // Search for a byte value [0-255]
      if (Buffer.TYPED_ARRAY_SUPPORT &&
          typeof Uint8Array.prototype.indexOf === 'function') {
        if (dir) {
          return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset)
        } else {
          return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset)
        }
      }
      return arrayIndexOf(buffer, [ val ], byteOffset, encoding, dir)
    }
  
    throw new TypeError('val must be string, number or Buffer')
  }
  
  function arrayIndexOf (arr, val, byteOffset, encoding, dir) {
    var indexSize = 1
    var arrLength = arr.length
    var valLength = val.length
  
    if (encoding !== undefined) {
      encoding = String(encoding).toLowerCase()
      if (encoding === 'ucs2' || encoding === 'ucs-2' ||
          encoding === 'utf16le' || encoding === 'utf-16le') {
        if (arr.length < 2 || val.length < 2) {
          return -1
        }
        indexSize = 2
        arrLength /= 2
        valLength /= 2
        byteOffset /= 2
      }
    }
  
    function read (buf, i) {
      if (indexSize === 1) {
        return buf[i]
      } else {
        return buf.readUInt16BE(i * indexSize)
      }
    }
  
    var i
    if (dir) {
      var foundIndex = -1
      for (i = byteOffset; i < arrLength; i++) {
        if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) {
          if (foundIndex === -1) foundIndex = i
          if (i - foundIndex + 1 === valLength) return foundIndex * indexSize
        } else {
          if (foundIndex !== -1) i -= i - foundIndex
          foundIndex = -1
        }
      }
    } else {
      if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength
      for (i = byteOffset; i >= 0; i--) {
        var found = true
        for (var j = 0; j < valLength; j++) {
          if (read(arr, i + j) !== read(val, j)) {
            found = false
            break
          }
        }
        if (found) return i
      }
    }
  
    return -1
  }
  
  Buffer.prototype.includes = function includes (val, byteOffset, encoding) {
    return this.indexOf(val, byteOffset, encoding) !== -1
  }
  
  Buffer.prototype.indexOf = function indexOf (val, byteOffset, encoding) {
    return bidirectionalIndexOf(this, val, byteOffset, encoding, true)
  }
  
  Buffer.prototype.lastIndexOf = function lastIndexOf (val, byteOffset, encoding) {
    return bidirectionalIndexOf(this, val, byteOffset, encoding, false)
  }
  
  function hexWrite (buf, string, offset, length) {
    offset = Number(offset) || 0
    var remaining = buf.length - offset
    if (!length) {
      length = remaining
    } else {
      length = Number(length)
      if (length > remaining) {
        length = remaining
      }
    }
  
    // must be an even number of digits
    var strLen = string.length
    if (strLen % 2 !== 0) throw new TypeError('Invalid hex string')
  
    if (length > strLen / 2) {
      length = strLen / 2
    }
    for (var i = 0; i < length; ++i) {
      var parsed = parseInt(string.substr(i * 2, 2), 16)
      if (isNaN(parsed)) return i
      buf[offset + i] = parsed
    }
    return i
  }
  
  function utf8Write (buf, string, offset, length) {
    return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length)
  }
  
  function asciiWrite (buf, string, offset, length) {
    return blitBuffer(asciiToBytes(string), buf, offset, length)
  }
  
  function latin1Write (buf, string, offset, length) {
    return asciiWrite(buf, string, offset, length)
  }
  
  function base64Write (buf, string, offset, length) {
    return blitBuffer(base64ToBytes(string), buf, offset, length)
  }
  
  function ucs2Write (buf, string, offset, length) {
    return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length)
  }
  
  Buffer.prototype.write = function write (string, offset, length, encoding) {
    // Buffer#write(string)
    if (offset === undefined) {
      encoding = 'utf8'
      length = this.length
      offset = 0
    // Buffer#write(string, encoding)
    } else if (length === undefined && typeof offset === 'string') {
      encoding = offset
      length = this.length
      offset = 0
    // Buffer#write(string, offset[, length][, encoding])
    } else if (isFinite(offset)) {
      offset = offset | 0
      if (isFinite(length)) {
        length = length | 0
        if (encoding === undefined) encoding = 'utf8'
      } else {
        encoding = length
        length = undefined
      }
    // legacy write(string, encoding, offset, length) - remove in v0.13
    } else {
      throw new Error(
        'Buffer.write(string, encoding, offset[, length]) is no longer supported'
      )
    }
  
    var remaining = this.length - offset
    if (length === undefined || length > remaining) length = remaining
  
    if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) {
      throw new RangeError('Attempt to write outside buffer bounds')
    }
  
    if (!encoding) encoding = 'utf8'
  
    var loweredCase = false
    for (;;) {
      switch (encoding) {
        case 'hex':
          return hexWrite(this, string, offset, length)
  
        case 'utf8':
        case 'utf-8':
          return utf8Write(this, string, offset, length)
  
        case 'ascii':
          return asciiWrite(this, string, offset, length)
  
        case 'latin1':
        case 'binary':
          return latin1Write(this, string, offset, length)
  
        case 'base64':
          // Warning: maxLength not taken into account in base64Write
          return base64Write(this, string, offset, length)
  
        case 'ucs2':
        case 'ucs-2':
        case 'utf16le':
        case 'utf-16le':
          return ucs2Write(this, string, offset, length)
  
        default:
          if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)
          encoding = ('' + encoding).toLowerCase()
          loweredCase = true
      }
    }
  }
  
  Buffer.prototype.toJSON = function toJSON () {
    return {
      type: 'Buffer',
      data: Array.prototype.slice.call(this._arr || this, 0)
    }
  }
  
  function base64Slice (buf, start, end) {
    if (start === 0 && end === buf.length) {
      return base64.fromByteArray(buf)
    } else {
      return base64.fromByteArray(buf.slice(start, end))
    }
  }
  
  function utf8Slice (buf, start, end) {
    end = Math.min(buf.length, end)
    var res = []
  
    var i = start
    while (i < end) {
      var firstByte = buf[i]
      var codePoint = null
      var bytesPerSequence = (firstByte > 0xEF) ? 4
        : (firstByte > 0xDF) ? 3
        : (firstByte > 0xBF) ? 2
        : 1
  
      if (i + bytesPerSequence <= end) {
        var secondByte, thirdByte, fourthByte, tempCodePoint
  
        switch (bytesPerSequence) {
          case 1:
            if (firstByte < 0x80) {
              codePoint = firstByte
            }
            break
          case 2:
            secondByte = buf[i + 1]
            if ((secondByte & 0xC0) === 0x80) {
              tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F)
              if (tempCodePoint > 0x7F) {
                codePoint = tempCodePoint
              }
            }
            break
          case 3:
            secondByte = buf[i + 1]
            thirdByte = buf[i + 2]
            if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) {
              tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F)
              if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) {
                codePoint = tempCodePoint
              }
            }
            break
          case 4:
            secondByte = buf[i + 1]
            thirdByte = buf[i + 2]
            fourthByte = buf[i + 3]
            if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) {
              tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F)
              if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) {
                codePoint = tempCodePoint
              }
            }
        }
      }
  
      if (codePoint === null) {
        // we did not generate a valid codePoint so insert a
        // replacement char (U+FFFD) and advance only 1 byte
        codePoint = 0xFFFD
        bytesPerSequence = 1
      } else if (codePoint > 0xFFFF) {
        // encode to utf16 (surrogate pair dance)
        codePoint -= 0x10000
        res.push(codePoint >>> 10 & 0x3FF | 0xD800)
        codePoint = 0xDC00 | codePoint & 0x3FF
      }
  
      res.push(codePoint)
      i += bytesPerSequence
    }
  
    return decodeCodePointsArray(res)
  }
  
  // Based on http://stackoverflow.com/a/22747272/680742, the browser with
  // the lowest limit is Chrome, with 0x10000 args.
  // We go 1 magnitude less, for safety
  var MAX_ARGUMENTS_LENGTH = 0x1000
  
  function decodeCodePointsArray (codePoints) {
    var len = codePoints.length
    if (len <= MAX_ARGUMENTS_LENGTH) {
      return String.fromCharCode.apply(String, codePoints) // avoid extra slice()
    }
  
    // Decode in chunks to avoid "call stack size exceeded".
    var res = ''
    var i = 0
    while (i < len) {
      res += String.fromCharCode.apply(
        String,
        codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH)
      )
    }
    return res
  }
  
  function asciiSlice (buf, start, end) {
    var ret = ''
    end = Math.min(buf.length, end)
  
    for (var i = start; i < end; ++i) {
      ret += String.fromCharCode(buf[i] & 0x7F)
    }
    return ret
  }
  
  function latin1Slice (buf, start, end) {
    var ret = ''
    end = Math.min(buf.length, end)
  
    for (var i = start; i < end; ++i) {
      ret += String.fromCharCode(buf[i])
    }
    return ret
  }
  
  function hexSlice (buf, start, end) {
    var len = buf.length
  
    if (!start || start < 0) start = 0
    if (!end || end < 0 || end > len) end = len
  
    var out = ''
    for (var i = start; i < end; ++i) {
      out += toHex(buf[i])
    }
    return out
  }
  
  function utf16leSlice (buf, start, end) {
    var bytes = buf.slice(start, end)
    var res = ''
    for (var i = 0; i < bytes.length; i += 2) {
      res += String.fromCharCode(bytes[i] + bytes[i + 1] * 256)
    }
    return res
  }
  
  Buffer.prototype.slice = function slice (start, end) {
    var len = this.length
    start = ~~start
    end = end === undefined ? len : ~~end
  
    if (start < 0) {
      start += len
      if (start < 0) start = 0
    } else if (start > len) {
      start = len
    }
  
    if (end < 0) {
      end += len
      if (end < 0) end = 0
    } else if (end > len) {
      end = len
    }
  
    if (end < start) end = start
  
    var newBuf
    if (Buffer.TYPED_ARRAY_SUPPORT) {
      newBuf = this.subarray(start, end)
      newBuf.__proto__ = Buffer.prototype
    } else {
      var sliceLen = end - start
      newBuf = new Buffer(sliceLen, undefined)
      for (var i = 0; i < sliceLen; ++i) {
        newBuf[i] = this[i + start]
      }
    }
  
    return newBuf
  }
  
  /*
   * Need to make sure that buffer isn't trying to write out of bounds.
   */
  function checkOffset (offset, ext, length) {
    if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint')
    if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length')
  }
  
  Buffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) {
    offset = offset | 0
    byteLength = byteLength | 0
    if (!noAssert) checkOffset(offset, byteLength, this.length)
  
    var val = this[offset]
    var mul = 1
    var i = 0
    while (++i < byteLength && (mul *= 0x100)) {
      val += this[offset + i] * mul
    }
  
    return val
  }
  
  Buffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) {
    offset = offset | 0
    byteLength = byteLength | 0
    if (!noAssert) {
      checkOffset(offset, byteLength, this.length)
    }
  
    var val = this[offset + --byteLength]
    var mul = 1
    while (byteLength > 0 && (mul *= 0x100)) {
      val += this[offset + --byteLength] * mul
    }
  
    return val
  }
  
  Buffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) {
    if (!noAssert) checkOffset(offset, 1, this.length)
    return this[offset]
  }
  
  Buffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) {
    if (!noAssert) checkOffset(offset, 2, this.length)
    return this[offset] | (this[offset + 1] << 8)
  }
  
  Buffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) {
    if (!noAssert) checkOffset(offset, 2, this.length)
    return (this[offset] << 8) | this[offset + 1]
  }
  
  Buffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) {
    if (!noAssert) checkOffset(offset, 4, this.length)
  
    return ((this[offset]) |
        (this[offset + 1] << 8) |
        (this[offset + 2] << 16)) +
        (this[offset + 3] * 0x1000000)
  }
  
  Buffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) {
    if (!noAssert) checkOffset(offset, 4, this.length)
  
    return (this[offset] * 0x1000000) +
      ((this[offset + 1] << 16) |
      (this[offset + 2] << 8) |
      this[offset + 3])
  }
  
  Buffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) {
    offset = offset | 0
    byteLength = byteLength | 0
    if (!noAssert) checkOffset(offset, byteLength, this.length)
  
    var val = this[offset]
    var mul = 1
    var i = 0
    while (++i < byteLength && (mul *= 0x100)) {
      val += this[offset + i] * mul
    }
    mul *= 0x80
  
    if (val >= mul) val -= Math.pow(2, 8 * byteLength)
  
    return val
  }
  
  Buffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) {
    offset = offset | 0
    byteLength = byteLength | 0
    if (!noAssert) checkOffset(offset, byteLength, this.length)
  
    var i = byteLength
    var mul = 1
    var val = this[offset + --i]
    while (i > 0 && (mul *= 0x100)) {
      val += this[offset + --i] * mul
    }
    mul *= 0x80
  
    if (val >= mul) val -= Math.pow(2, 8 * byteLength)
  
    return val
  }
  
  Buffer.prototype.readInt8 = function readInt8 (offset, noAssert) {
    if (!noAssert) checkOffset(offset, 1, this.length)
    if (!(this[offset] & 0x80)) return (this[offset])
    return ((0xff - this[offset] + 1) * -1)
  }
  
  Buffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) {
    if (!noAssert) checkOffset(offset, 2, this.length)
    var val = this[offset] | (this[offset + 1] << 8)
    return (val & 0x8000) ? val | 0xFFFF0000 : val
  }
  
  Buffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) {
    if (!noAssert) checkOffset(offset, 2, this.length)
    var val = this[offset + 1] | (this[offset] << 8)
    return (val & 0x8000) ? val | 0xFFFF0000 : val
  }
  
  Buffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) {
    if (!noAssert) checkOffset(offset, 4, this.length)
  
    return (this[offset]) |
      (this[offset + 1] << 8) |
      (this[offset + 2] << 16) |
      (this[offset + 3] << 24)
  }
  
  Buffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) {
    if (!noAssert) checkOffset(offset, 4, this.length)
  
    return (this[offset] << 24) |
      (this[offset + 1] << 16) |
      (this[offset + 2] << 8) |
      (this[offset + 3])
  }
  
  Buffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) {
    if (!noAssert) checkOffset(offset, 4, this.length)
    return ieee754.read(this, offset, true, 23, 4)
  }
  
  Buffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) {
    if (!noAssert) checkOffset(offset, 4, this.length)
    return ieee754.read(this, offset, false, 23, 4)
  }
  
  Buffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) {
    if (!noAssert) checkOffset(offset, 8, this.length)
    return ieee754.read(this, offset, true, 52, 8)
  }
  
  Buffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) {
    if (!noAssert) checkOffset(offset, 8, this.length)
    return ieee754.read(this, offset, false, 52, 8)
  }
  
  function checkInt (buf, value, offset, ext, max, min) {
    if (!Buffer.isBuffer(buf)) throw new TypeError('"buffer" argument must be a Buffer instance')
    if (value > max || value < min) throw new RangeError('"value" argument is out of bounds')
    if (offset + ext > buf.length) throw new RangeError('Index out of range')
  }
  
  Buffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) {
    value = +value
    offset = offset | 0
    byteLength = byteLength | 0
    if (!noAssert) {
      var maxBytes = Math.pow(2, 8 * byteLength) - 1
      checkInt(this, value, offset, byteLength, maxBytes, 0)
    }
  
    var mul = 1
    var i = 0
    this[offset] = value & 0xFF
    while (++i < byteLength && (mul *= 0x100)) {
      this[offset + i] = (value / mul) & 0xFF
    }
  
    return offset + byteLength
  }
  
  Buffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) {
    value = +value
    offset = offset | 0
    byteLength = byteLength | 0
    if (!noAssert) {
      var maxBytes = Math.pow(2, 8 * byteLength) - 1
      checkInt(this, value, offset, byteLength, maxBytes, 0)
    }
  
    var i = byteLength - 1
    var mul = 1
    this[offset + i] = value & 0xFF
    while (--i >= 0 && (mul *= 0x100)) {
      this[offset + i] = (value / mul) & 0xFF
    }
  
    return offset + byteLength
  }
  
  Buffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) {
    value = +value
    offset = offset | 0
    if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0)
    if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value)
    this[offset] = (value & 0xff)
    return offset + 1
  }
  
  function objectWriteUInt16 (buf, value, offset, littleEndian) {
    if (value < 0) value = 0xffff + value + 1
    for (var i = 0, j = Math.min(buf.length - offset, 2); i < j; ++i) {
      buf[offset + i] = (value & (0xff << (8 * (littleEndian ? i : 1 - i)))) >>>
        (littleEndian ? i : 1 - i) * 8
    }
  }
  
  Buffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) {
    value = +value
    offset = offset | 0
    if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)
    if (Buffer.TYPED_ARRAY_SUPPORT) {
      this[offset] = (value & 0xff)
      this[offset + 1] = (value >>> 8)
    } else {
      objectWriteUInt16(this, value, offset, true)
    }
    return offset + 2
  }
  
  Buffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) {
    value = +value
    offset = offset | 0
    if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)
    if (Buffer.TYPED_ARRAY_SUPPORT) {
      this[offset] = (value >>> 8)
      this[offset + 1] = (value & 0xff)
    } else {
      objectWriteUInt16(this, value, offset, false)
    }
    return offset + 2
  }
  
  function objectWriteUInt32 (buf, value, offset, littleEndian) {
    if (value < 0) value = 0xffffffff + value + 1
    for (var i = 0, j = Math.min(buf.length - offset, 4); i < j; ++i) {
      buf[offset + i] = (value >>> (littleEndian ? i : 3 - i) * 8) & 0xff
    }
  }
  
  Buffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) {
    value = +value
    offset = offset | 0
    if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)
    if (Buffer.TYPED_ARRAY_SUPPORT) {
      this[offset + 3] = (value >>> 24)
      this[offset + 2] = (value >>> 16)
      this[offset + 1] = (value >>> 8)
      this[offset] = (value & 0xff)
    } else {
      objectWriteUInt32(this, value, offset, true)
    }
    return offset + 4
  }
  
  Buffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) {
    value = +value
    offset = offset | 0
    if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)
    if (Buffer.TYPED_ARRAY_SUPPORT) {
      this[offset] = (value >>> 24)
      this[offset + 1] = (value >>> 16)
      this[offset + 2] = (value >>> 8)
      this[offset + 3] = (value & 0xff)
    } else {
      objectWriteUInt32(this, value, offset, false)
    }
    return offset + 4
  }
  
  Buffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) {
    value = +value
    offset = offset | 0
    if (!noAssert) {
      var limit = Math.pow(2, 8 * byteLength - 1)
  
      checkInt(this, value, offset, byteLength, limit - 1, -limit)
    }
  
    var i = 0
    var mul = 1
    var sub = 0
    this[offset] = value & 0xFF
    while (++i < byteLength && (mul *= 0x100)) {
      if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) {
        sub = 1
      }
      this[offset + i] = ((value / mul) >> 0) - sub & 0xFF
    }
  
    return offset + byteLength
  }
  
  Buffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) {
    value = +value
    offset = offset | 0
    if (!noAssert) {
      var limit = Math.pow(2, 8 * byteLength - 1)
  
      checkInt(this, value, offset, byteLength, limit - 1, -limit)
    }
  
    var i = byteLength - 1
    var mul = 1
    var sub = 0
    this[offset + i] = value & 0xFF
    while (--i >= 0 && (mul *= 0x100)) {
      if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) {
        sub = 1
      }
      this[offset + i] = ((value / mul) >> 0) - sub & 0xFF
    }
  
    return offset + byteLength
  }
  
  Buffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) {
    value = +value
    offset = offset | 0
    if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80)
    if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value)
    if (value < 0) value = 0xff + value + 1
    this[offset] = (value & 0xff)
    return offset + 1
  }
  
  Buffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) {
    value = +value
    offset = offset | 0
    if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)
    if (Buffer.TYPED_ARRAY_SUPPORT) {
      this[offset] = (value & 0xff)
      this[offset + 1] = (value >>> 8)
    } else {
      objectWriteUInt16(this, value, offset, true)
    }
    return offset + 2
  }
  
  Buffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) {
    value = +value
    offset = offset | 0
    if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)
    if (Buffer.TYPED_ARRAY_SUPPORT) {
      this[offset] = (value >>> 8)
      this[offset + 1] = (value & 0xff)
    } else {
      objectWriteUInt16(this, value, offset, false)
    }
    return offset + 2
  }
  
  Buffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) {
    value = +value
    offset = offset | 0
    if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)
    if (Buffer.TYPED_ARRAY_SUPPORT) {
      this[offset] = (value & 0xff)
      this[offset + 1] = (value >>> 8)
      this[offset + 2] = (value >>> 16)
      this[offset + 3] = (value >>> 24)
    } else {
      objectWriteUInt32(this, value, offset, true)
    }
    return offset + 4
  }
  
  Buffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) {
    value = +value
    offset = offset | 0
    if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)
    if (value < 0) value = 0xffffffff + value + 1
    if (Buffer.TYPED_ARRAY_SUPPORT) {
      this[offset] = (value >>> 24)
      this[offset + 1] = (value >>> 16)
      this[offset + 2] = (value >>> 8)
      this[offset + 3] = (value & 0xff)
    } else {
      objectWriteUInt32(this, value, offset, false)
    }
    return offset + 4
  }
  
  function checkIEEE754 (buf, value, offset, ext, max, min) {
    if (offset + ext > buf.length) throw new RangeError('Index out of range')
    if (offset < 0) throw new RangeError('Index out of range')
  }
  
  function writeFloat (buf, value, offset, littleEndian, noAssert) {
    if (!noAssert) {
      checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38)
    }
    ieee754.write(buf, value, offset, littleEndian, 23, 4)
    return offset + 4
  }
  
  Buffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) {
    return writeFloat(this, value, offset, true, noAssert)
  }
  
  Buffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) {
    return writeFloat(this, value, offset, false, noAssert)
  }
  
  function writeDouble (buf, value, offset, littleEndian, noAssert) {
    if (!noAssert) {
      checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308)
    }
    ieee754.write(buf, value, offset, littleEndian, 52, 8)
    return offset + 8
  }
  
  Buffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) {
    return writeDouble(this, value, offset, true, noAssert)
  }
  
  Buffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) {
    return writeDouble(this, value, offset, false, noAssert)
  }
  
  // copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length)
  Buffer.prototype.copy = function copy (target, targetStart, start, end) {
    if (!start) start = 0
    if (!end && end !== 0) end = this.length
    if (targetStart >= target.length) targetStart = target.length
    if (!targetStart) targetStart = 0
    if (end > 0 && end < start) end = start
  
    // Copy 0 bytes; we're done
    if (end === start) return 0
    if (target.length === 0 || this.length === 0) return 0
  
    // Fatal error conditions
    if (targetStart < 0) {
      throw new RangeError('targetStart out of bounds')
    }
    if (start < 0 || start >= this.length) throw new RangeError('sourceStart out of bounds')
    if (end < 0) throw new RangeError('sourceEnd out of bounds')
  
    // Are we oob?
    if (end > this.length) end = this.length
    if (target.length - targetStart < end - start) {
      end = target.length - targetStart + start
    }
  
    var len = end - start
    var i
  
    if (this === target && start < targetStart && targetStart < end) {
      // descending copy from end
      for (i = len - 1; i >= 0; --i) {
        target[i + targetStart] = this[i + start]
      }
    } else if (len < 1000 || !Buffer.TYPED_ARRAY_SUPPORT) {
      // ascending copy from start
      for (i = 0; i < len; ++i) {
        target[i + targetStart] = this[i + start]
      }
    } else {
      Uint8Array.prototype.set.call(
        target,
        this.subarray(start, start + len),
        targetStart
      )
    }
  
    return len
  }
  
  // Usage:
  //    buffer.fill(number[, offset[, end]])
  //    buffer.fill(buffer[, offset[, end]])
  //    buffer.fill(string[, offset[, end]][, encoding])
  Buffer.prototype.fill = function fill (val, start, end, encoding) {
    // Handle string cases:
    if (typeof val === 'string') {
      if (typeof start === 'string') {
        encoding = start
        start = 0
        end = this.length
      } else if (typeof end === 'string') {
        encoding = end
        end = this.length
      }
      if (val.length === 1) {
        var code = val.charCodeAt(0)
        if (code < 256) {
          val = code
        }
      }
      if (encoding !== undefined && typeof encoding !== 'string') {
        throw new TypeError('encoding must be a string')
      }
      if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) {
        throw new TypeError('Unknown encoding: ' + encoding)
      }
    } else if (typeof val === 'number') {
      val = val & 255
    }
  
    // Invalid ranges are not set to a default, so can range check early.
    if (start < 0 || this.length < start || this.length < end) {
      throw new RangeError('Out of range index')
    }
  
    if (end <= start) {
      return this
    }
  
    start = start >>> 0
    end = end === undefined ? this.length : end >>> 0
  
    if (!val) val = 0
  
    var i
    if (typeof val === 'number') {
      for (i = start; i < end; ++i) {
        this[i] = val
      }
    } else {
      var bytes = Buffer.isBuffer(val)
        ? val
        : utf8ToBytes(new Buffer(val, encoding).toString())
      var len = bytes.length
      for (i = 0; i < end - start; ++i) {
        this[i + start] = bytes[i % len]
      }
    }
  
    return this
  }
  
  // HELPER FUNCTIONS
  // ================
  
  var INVALID_BASE64_RE = /[^+\/0-9A-Za-z-_]/g
  
  function base64clean (str) {
    // Node strips out invalid characters like \n and \t from the string, base64-js does not
    str = stringtrim(str).replace(INVALID_BASE64_RE, '')
    // Node converts strings with length < 2 to ''
    if (str.length < 2) return ''
    // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not
    while (str.length % 4 !== 0) {
      str = str + '='
    }
    return str
  }
  
  function stringtrim (str) {
    if (str.trim) return str.trim()
    return str.replace(/^\s+|\s+$/g, '')
  }
  
  function toHex (n) {
    if (n < 16) return '0' + n.toString(16)
    return n.toString(16)
  }
  
  function utf8ToBytes (string, units) {
    units = units || Infinity
    var codePoint
    var length = string.length
    var leadSurrogate = null
    var bytes = []
  
    for (var i = 0; i < length; ++i) {
      codePoint = string.charCodeAt(i)
  
      // is surrogate component
      if (codePoint > 0xD7FF && codePoint < 0xE000) {
        // last char was a lead
        if (!leadSurrogate) {
          // no lead yet
          if (codePoint > 0xDBFF) {
            // unexpected trail
            if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
            continue
          } else if (i + 1 === length) {
            // unpaired lead
            if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
            continue
          }
  
          // valid lead
          leadSurrogate = codePoint
  
          continue
        }
  
        // 2 leads in a row
        if (codePoint < 0xDC00) {
          if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
          leadSurrogate = codePoint
          continue
        }
  
        // valid surrogate pair
        codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000
      } else if (leadSurrogate) {
        // valid bmp char, but last char was a lead
        if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
      }
  
      leadSurrogate = null
  
      // encode utf8
      if (codePoint < 0x80) {
        if ((units -= 1) < 0) break
        bytes.push(codePoint)
      } else if (codePoint < 0x800) {
        if ((units -= 2) < 0) break
        bytes.push(
          codePoint >> 0x6 | 0xC0,
          codePoint & 0x3F | 0x80
        )
      } else if (codePoint < 0x10000) {
        if ((units -= 3) < 0) break
        bytes.push(
          codePoint >> 0xC | 0xE0,
          codePoint >> 0x6 & 0x3F | 0x80,
          codePoint & 0x3F | 0x80
        )
      } else if (codePoint < 0x110000) {
        if ((units -= 4) < 0) break
        bytes.push(
          codePoint >> 0x12 | 0xF0,
          codePoint >> 0xC & 0x3F | 0x80,
          codePoint >> 0x6 & 0x3F | 0x80,
          codePoint & 0x3F | 0x80
        )
      } else {
        throw new Error('Invalid code point')
      }
    }
  
    return bytes
  }
  
  function asciiToBytes (str) {
    var byteArray = []
    for (var i = 0; i < str.length; ++i) {
      // Node's code seems to be doing this and not & 0x7F..
      byteArray.push(str.charCodeAt(i) & 0xFF)
    }
    return byteArray
  }
  
  function utf16leToBytes (str, units) {
    var c, hi, lo
    var byteArray = []
    for (var i = 0; i < str.length; ++i) {
      if ((units -= 2) < 0) break
  
      c = str.charCodeAt(i)
      hi = c >> 8
      lo = c % 256
      byteArray.push(lo)
      byteArray.push(hi)
    }
  
    return byteArray
  }
  
  function base64ToBytes (str) {
    return base64.toByteArray(base64clean(str))
  }
  
  function blitBuffer (src, dst, offset, length) {
    for (var i = 0; i < length; ++i) {
      if ((i + offset >= dst.length) || (i >= src.length)) break
      dst[i + offset] = src[i]
    }
    return i
  }
  
  function isnan (val) {
    return val !== val // eslint-disable-line no-self-compare
  }
  
  /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../../webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js")))
  
  /***/ }),
  
  /***/ "./node_modules/node-libs-browser/node_modules/events/events.js":
  /*!**********************************************************************!*\
    !*** ./node_modules/node-libs-browser/node_modules/events/events.js ***!
    \**********************************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports) {
  
  // Copyright Joyent, Inc. and other Node contributors.
  //
  // Permission is hereby granted, free of charge, to any person obtaining a
  // copy of this software and associated documentation files (the
  // "Software"), to deal in the Software without restriction, including
  // without limitation the rights to use, copy, modify, merge, publish,
  // distribute, sublicense, and/or sell copies of the Software, and to permit
  // persons to whom the Software is furnished to do so, subject to the
  // following conditions:
  //
  // The above copyright notice and this permission notice shall be included
  // in all copies or substantial portions of the Software.
  //
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
  // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
  // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
  // USE OR OTHER DEALINGS IN THE SOFTWARE.
  
  function EventEmitter() {
    this._events = this._events || {};
    this._maxListeners = this._maxListeners || undefined;
  }
  module.exports = EventEmitter;
  
  // Backwards-compat with node 0.10.x
  EventEmitter.EventEmitter = EventEmitter;
  
  EventEmitter.prototype._events = undefined;
  EventEmitter.prototype._maxListeners = undefined;
  
  // By default EventEmitters will print a warning if more than 10 listeners are
  // added to it. This is a useful default which helps finding memory leaks.
  EventEmitter.defaultMaxListeners = 10;
  
  // Obviously not all Emitters should be limited to 10. This function allows
  // that to be increased. Set to zero for unlimited.
  EventEmitter.prototype.setMaxListeners = function(n) {
    if (!isNumber(n) || n < 0 || isNaN(n))
      throw TypeError('n must be a positive number');
    this._maxListeners = n;
    return this;
  };
  
  EventEmitter.prototype.emit = function(type) {
    var er, handler, len, args, i, listeners;
  
    if (!this._events)
      this._events = {};
  
    // If there is no 'error' event listener then throw.
    if (type === 'error') {
      if (!this._events.error ||
          (isObject(this._events.error) && !this._events.error.length)) {
        er = arguments[1];
        if (er instanceof Error) {
          throw er; // Unhandled 'error' event
        } else {
          // At least give some kind of context to the user
          var err = new Error('Uncaught, unspecified "error" event. (' + er + ')');
          err.context = er;
          throw err;
        }
      }
    }
  
    handler = this._events[type];
  
    if (isUndefined(handler))
      return false;
  
    if (isFunction(handler)) {
      switch (arguments.length) {
        // fast cases
        case 1:
          handler.call(this);
          break;
        case 2:
          handler.call(this, arguments[1]);
          break;
        case 3:
          handler.call(this, arguments[1], arguments[2]);
          break;
        // slower
        default:
          args = Array.prototype.slice.call(arguments, 1);
          handler.apply(this, args);
      }
    } else if (isObject(handler)) {
      args = Array.prototype.slice.call(arguments, 1);
      listeners = handler.slice();
      len = listeners.length;
      for (i = 0; i < len; i++)
        listeners[i].apply(this, args);
    }
  
    return true;
  };
  
  EventEmitter.prototype.addListener = function(type, listener) {
    var m;
  
    if (!isFunction(listener))
      throw TypeError('listener must be a function');
  
    if (!this._events)
      this._events = {};
  
    // To avoid recursion in the case that type === "newListener"! Before
    // adding it to the listeners, first emit "newListener".
    if (this._events.newListener)
      this.emit('newListener', type,
                isFunction(listener.listener) ?
                listener.listener : listener);
  
    if (!this._events[type])
      // Optimize the case of one listener. Don't need the extra array object.
      this._events[type] = listener;
    else if (isObject(this._events[type]))
      // If we've already got an array, just append.
      this._events[type].push(listener);
    else
      // Adding the second element, need to change to array.
      this._events[type] = [this._events[type], listener];
  
    // Check for listener leak
    if (isObject(this._events[type]) && !this._events[type].warned) {
      if (!isUndefined(this._maxListeners)) {
        m = this._maxListeners;
      } else {
        m = EventEmitter.defaultMaxListeners;
      }
  
      if (m && m > 0 && this._events[type].length > m) {
        this._events[type].warned = true;
        console.error('(node) warning: possible EventEmitter memory ' +
                      'leak detected. %d listeners added. ' +
                      'Use emitter.setMaxListeners() to increase limit.',
                      this._events[type].length);
        if (typeof console.trace === 'function') {
          // not supported in IE 10
          console.trace();
        }
      }
    }
  
    return this;
  };
  
  EventEmitter.prototype.on = EventEmitter.prototype.addListener;
  
  EventEmitter.prototype.once = function(type, listener) {
    if (!isFunction(listener))
      throw TypeError('listener must be a function');
  
    var fired = false;
  
    function g() {
      this.removeListener(type, g);
  
      if (!fired) {
        fired = true;
        listener.apply(this, arguments);
      }
    }
  
    g.listener = listener;
    this.on(type, g);
  
    return this;
  };
  
  // emits a 'removeListener' event iff the listener was removed
  EventEmitter.prototype.removeListener = function(type, listener) {
    var list, position, length, i;
  
    if (!isFunction(listener))
      throw TypeError('listener must be a function');
  
    if (!this._events || !this._events[type])
      return this;
  
    list = this._events[type];
    length = list.length;
    position = -1;
  
    if (list === listener ||
        (isFunction(list.listener) && list.listener === listener)) {
      delete this._events[type];
      if (this._events.removeListener)
        this.emit('removeListener', type, listener);
  
    } else if (isObject(list)) {
      for (i = length; i-- > 0;) {
        if (list[i] === listener ||
            (list[i].listener && list[i].listener === listener)) {
          position = i;
          break;
        }
      }
  
      if (position < 0)
        return this;
  
      if (list.length === 1) {
        list.length = 0;
        delete this._events[type];
      } else {
        list.splice(position, 1);
      }
  
      if (this._events.removeListener)
        this.emit('removeListener', type, listener);
    }
  
    return this;
  };
  
  EventEmitter.prototype.removeAllListeners = function(type) {
    var key, listeners;
  
    if (!this._events)
      return this;
  
    // not listening for removeListener, no need to emit
    if (!this._events.removeListener) {
      if (arguments.length === 0)
        this._events = {};
      else if (this._events[type])
        delete this._events[type];
      return this;
    }
  
    // emit removeListener for all listeners on all events
    if (arguments.length === 0) {
      for (key in this._events) {
        if (key === 'removeListener') continue;
        this.removeAllListeners(key);
      }
      this.removeAllListeners('removeListener');
      this._events = {};
      return this;
    }
  
    listeners = this._events[type];
  
    if (isFunction(listeners)) {
      this.removeListener(type, listeners);
    } else if (listeners) {
      // LIFO order
      while (listeners.length)
        this.removeListener(type, listeners[listeners.length - 1]);
    }
    delete this._events[type];
  
    return this;
  };
  
  EventEmitter.prototype.listeners = function(type) {
    var ret;
    if (!this._events || !this._events[type])
      ret = [];
    else if (isFunction(this._events[type]))
      ret = [this._events[type]];
    else
      ret = this._events[type].slice();
    return ret;
  };
  
  EventEmitter.prototype.listenerCount = function(type) {
    if (this._events) {
      var evlistener = this._events[type];
  
      if (isFunction(evlistener))
        return 1;
      else if (evlistener)
        return evlistener.length;
    }
    return 0;
  };
  
  EventEmitter.listenerCount = function(emitter, type) {
    return emitter.listenerCount(type);
  };
  
  function isFunction(arg) {
    return typeof arg === 'function';
  }
  
  function isNumber(arg) {
    return typeof arg === 'number';
  }
  
  function isObject(arg) {
    return typeof arg === 'object' && arg !== null;
  }
  
  function isUndefined(arg) {
    return arg === void 0;
  }
  
  
  /***/ }),
  
  /***/ "./node_modules/object-sizeof/byte_size.js":
  /*!*************************************************!*\
    !*** ./node_modules/object-sizeof/byte_size.js ***!
    \*************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports) {
  
  /**
   * Byte sizes are taken from ECMAScript Language Specification
   * http://www.ecma-international.org/ecma-262/5.1/
   * http://bclary.com/2004/11/07/#a-4.3.16
   */
  
  module.exports = {
      STRING: 2,
      BOOLEAN: 4,
      NUMBER: 8
  };
  
  /***/ }),
  
  /***/ "./node_modules/object-sizeof/index.js":
  /*!*********************************************!*\
    !*** ./node_modules/object-sizeof/index.js ***!
    \*********************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  "use strict";
  // Copyright 2014 Andrei Karpushonak
  
  
  
  var ECMA_SIZES  = __webpack_require__(/*! ./byte_size */ "./node_modules/object-sizeof/byte_size.js");
  var Buffer = __webpack_require__(/*! buffer */ "./node_modules/node-libs-browser/node_modules/buffer/index.js").Buffer;
  
  /**
   * Main module's entry point
   * Calculates Bytes for the provided parameter
   * @param object - handles object/string/boolean/buffer
   * @returns {*}
   */
  function sizeof(object) {
      if (object !== null && typeof (object) === 'object') {
        if (Buffer.isBuffer(object)) {
          return object.length;
        }
        else {
          var bytes = 0;
          for (var key in object) {
  
            if(!Object.hasOwnProperty.call(object, key)) {
              continue;
            }
  
            bytes += sizeof(key);
            try {
              bytes += sizeof(object[key]);
            } catch (ex) {
              if(ex instanceof RangeError) {
                // circular reference detected, final result might be incorrect
                // let's be nice and not throw an exception
                bytes = 0;
              }
            }
          }
          return bytes;
        }
      } else if (typeof (object) === 'string') {
        return object.length * ECMA_SIZES.STRING;
      } else if (typeof (object) === 'boolean') {
        return ECMA_SIZES.BOOLEAN;
      } else if (typeof (object) === 'number') {
        return ECMA_SIZES.NUMBER;
      } else {
        return 0;
      }
  }
  
  module.exports = sizeof;
  
  
  /***/ }),
  
  /***/ "./node_modules/parseqs/index.js":
  /*!***************************************!*\
    !*** ./node_modules/parseqs/index.js ***!
    \***************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports) {
  
  /**
   * Compiles a querystring
   * Returns string representation of the object
   *
   * @param {Object}
   * @api private
   */
  
  exports.encode = function (obj) {
    var str = '';
  
    for (var i in obj) {
      if (obj.hasOwnProperty(i)) {
        if (str.length) str += '&';
        str += encodeURIComponent(i) + '=' + encodeURIComponent(obj[i]);
      }
    }
  
    return str;
  };
  
  /**
   * Parses a simple querystring into an object
   *
   * @param {String} qs
   * @api private
   */
  
  exports.decode = function(qs){
    var qry = {};
    var pairs = qs.split('&');
    for (var i = 0, l = pairs.length; i < l; i++) {
      var pair = pairs[i].split('=');
      qry[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
    }
    return qry;
  };
  
  
  /***/ }),
  
  /***/ "./node_modules/parseuri/index.js":
  /*!****************************************!*\
    !*** ./node_modules/parseuri/index.js ***!
    \****************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports) {
  
  /**
   * Parses an URI
   *
   * @author Steven Levithan <stevenlevithan.com> (MIT license)
   * @api private
   */
  
  var re = /^(?:(?![^:@]+:[^:@\/]*@)(http|https|ws|wss):\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/;
  
  var parts = [
      'source', 'protocol', 'authority', 'userInfo', 'user', 'password', 'host', 'port', 'relative', 'path', 'directory', 'file', 'query', 'anchor'
  ];
  
  module.exports = function parseuri(str) {
      var src = str,
          b = str.indexOf('['),
          e = str.indexOf(']');
  
      if (b != -1 && e != -1) {
          str = str.substring(0, b) + str.substring(b, e).replace(/:/g, ';') + str.substring(e, str.length);
      }
  
      var m = re.exec(str || ''),
          uri = {},
          i = 14;
  
      while (i--) {
          uri[parts[i]] = m[i] || '';
      }
  
      if (b != -1 && e != -1) {
          uri.source = src;
          uri.host = uri.host.substring(1, uri.host.length - 1).replace(/;/g, ':');
          uri.authority = uri.authority.replace('[', '').replace(']', '').replace(/;/g, ':');
          uri.ipv6uri = true;
      }
  
      return uri;
  };
  
  
  /***/ }),
  
  /***/ "./node_modules/process/browser.js":
  /*!*****************************************!*\
    !*** ./node_modules/process/browser.js ***!
    \*****************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports) {
  
  // shim for using process in browser
  var process = module.exports = {};
  
  // cached from whatever global is present so that test runners that stub it
  // don't break things.  But we need to wrap it in a try catch in case it is
  // wrapped in strict mode code which doesn't define any globals.  It's inside a
  // function because try/catches deoptimize in certain engines.
  
  var cachedSetTimeout;
  var cachedClearTimeout;
  
  function defaultSetTimout() {
      throw new Error('setTimeout has not been defined');
  }
  function defaultClearTimeout () {
      throw new Error('clearTimeout has not been defined');
  }
  (function () {
      try {
          if (typeof setTimeout === 'function') {
              cachedSetTimeout = setTimeout;
          } else {
              cachedSetTimeout = defaultSetTimout;
          }
      } catch (e) {
          cachedSetTimeout = defaultSetTimout;
      }
      try {
          if (typeof clearTimeout === 'function') {
              cachedClearTimeout = clearTimeout;
          } else {
              cachedClearTimeout = defaultClearTimeout;
          }
      } catch (e) {
          cachedClearTimeout = defaultClearTimeout;
      }
  } ())
  function runTimeout(fun) {
      if (cachedSetTimeout === setTimeout) {
          //normal enviroments in sane situations
          return setTimeout(fun, 0);
      }
      // if setTimeout wasn't available but was latter defined
      if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
          cachedSetTimeout = setTimeout;
          return setTimeout(fun, 0);
      }
      try {
          // when when somebody has screwed with setTimeout but no I.E. maddness
          return cachedSetTimeout(fun, 0);
      } catch(e){
          try {
              // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
              return cachedSetTimeout.call(null, fun, 0);
          } catch(e){
              // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
              return cachedSetTimeout.call(this, fun, 0);
          }
      }
  
  
  }
  function runClearTimeout(marker) {
      if (cachedClearTimeout === clearTimeout) {
          //normal enviroments in sane situations
          return clearTimeout(marker);
      }
      // if clearTimeout wasn't available but was latter defined
      if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
          cachedClearTimeout = clearTimeout;
          return clearTimeout(marker);
      }
      try {
          // when when somebody has screwed with setTimeout but no I.E. maddness
          return cachedClearTimeout(marker);
      } catch (e){
          try {
              // When we are in I.E. but the script has been evaled so I.E. doesn't  trust the global object when called normally
              return cachedClearTimeout.call(null, marker);
          } catch (e){
              // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
              // Some versions of I.E. have different rules for clearTimeout vs setTimeout
              return cachedClearTimeout.call(this, marker);
          }
      }
  
  
  
  }
  var queue = [];
  var draining = false;
  var currentQueue;
  var queueIndex = -1;
  
  function cleanUpNextTick() {
      if (!draining || !currentQueue) {
          return;
      }
      draining = false;
      if (currentQueue.length) {
          queue = currentQueue.concat(queue);
      } else {
          queueIndex = -1;
      }
      if (queue.length) {
          drainQueue();
      }
  }
  
  function drainQueue() {
      if (draining) {
          return;
      }
      var timeout = runTimeout(cleanUpNextTick);
      draining = true;
  
      var len = queue.length;
      while(len) {
          currentQueue = queue;
          queue = [];
          while (++queueIndex < len) {
              if (currentQueue) {
                  currentQueue[queueIndex].run();
              }
          }
          queueIndex = -1;
          len = queue.length;
      }
      currentQueue = null;
      draining = false;
      runClearTimeout(timeout);
  }
  
  process.nextTick = function (fun) {
      var args = new Array(arguments.length - 1);
      if (arguments.length > 1) {
          for (var i = 1; i < arguments.length; i++) {
              args[i - 1] = arguments[i];
          }
      }
      queue.push(new Item(fun, args));
      if (queue.length === 1 && !draining) {
          runTimeout(drainQueue);
      }
  };
  
  // v8 likes predictible objects
  function Item(fun, array) {
      this.fun = fun;
      this.array = array;
  }
  Item.prototype.run = function () {
      this.fun.apply(null, this.array);
  };
  process.title = 'browser';
  process.browser = true;
  process.env = {};
  process.argv = [];
  process.version = ''; // empty string to avoid regexp issues
  process.versions = {};
  
  function noop() {}
  
  process.on = noop;
  process.addListener = noop;
  process.once = noop;
  process.off = noop;
  process.removeListener = noop;
  process.removeAllListeners = noop;
  process.emit = noop;
  process.prependListener = noop;
  process.prependOnceListener = noop;
  
  process.listeners = function (name) { return [] }
  
  process.binding = function (name) {
      throw new Error('process.binding is not supported');
  };
  
  process.cwd = function () { return '/' };
  process.chdir = function (dir) {
      throw new Error('process.chdir is not supported');
  };
  process.umask = function() { return 0; };
  
  
  /***/ }),
  
  /***/ "./node_modules/query-string/index.js":
  /*!********************************************!*\
    !*** ./node_modules/query-string/index.js ***!
    \********************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  "use strict";
  
  const strictUriEncode = __webpack_require__(/*! strict-uri-encode */ "./node_modules/strict-uri-encode/index.js");
  const decodeComponent = __webpack_require__(/*! decode-uri-component */ "./node_modules/decode-uri-component/index.js");
  
  function encoderForArrayFormat(options) {
  	switch (options.arrayFormat) {
  		case 'index':
  			return key => (result, value) => {
  				const index = result.length;
  				if (value === undefined) {
  					return result;
  				}
  
  				if (value === null) {
  					return [...result, [encode(key, options), '[', index, ']'].join('')];
  				}
  
  				return [
  					...result,
  					[encode(key, options), '[', encode(index, options), ']=', encode(value, options)].join('')
  				];
  			};
  
  		case 'bracket':
  			return key => (result, value) => {
  				if (value === undefined) {
  					return result;
  				}
  
  				if (value === null) {
  					return [...result, [encode(key, options), '[]'].join('')];
  				}
  
  				return [...result, [encode(key, options), '[]=', encode(value, options)].join('')];
  			};
  
  		case 'comma':
  			return key => (result, value, index) => {
  				if (!value) {
  					return result;
  				}
  
  				if (index === 0) {
  					return [[encode(key, options), '=', encode(value, options)].join('')];
  				}
  
  				return [[result, encode(value, options)].join(',')];
  			};
  
  		default:
  			return key => (result, value) => {
  				if (value === undefined) {
  					return result;
  				}
  
  				if (value === null) {
  					return [...result, encode(key, options)];
  				}
  
  				return [...result, [encode(key, options), '=', encode(value, options)].join('')];
  			};
  	}
  }
  
  function parserForArrayFormat(options) {
  	let result;
  
  	switch (options.arrayFormat) {
  		case 'index':
  			return (key, value, accumulator) => {
  				result = /\[(\d*)\]$/.exec(key);
  
  				key = key.replace(/\[\d*\]$/, '');
  
  				if (!result) {
  					accumulator[key] = value;
  					return;
  				}
  
  				if (accumulator[key] === undefined) {
  					accumulator[key] = {};
  				}
  
  				accumulator[key][result[1]] = value;
  			};
  
  		case 'bracket':
  			return (key, value, accumulator) => {
  				result = /(\[\])$/.exec(key);
  				key = key.replace(/\[\]$/, '');
  
  				if (!result) {
  					accumulator[key] = value;
  					return;
  				}
  
  				if (accumulator[key] === undefined) {
  					accumulator[key] = [value];
  					return;
  				}
  
  				accumulator[key] = [].concat(accumulator[key], value);
  			};
  
  		case 'comma':
  			return (key, value, accumulator) => {
  				const isArray = typeof value === 'string' && value.split('').indexOf(',') > -1;
  				const newValue = isArray ? value.split(',') : value;
  				accumulator[key] = newValue;
  			};
  
  		default:
  			return (key, value, accumulator) => {
  				if (accumulator[key] === undefined) {
  					accumulator[key] = value;
  					return;
  				}
  
  				accumulator[key] = [].concat(accumulator[key], value);
  			};
  	}
  }
  
  function encode(value, options) {
  	if (options.encode) {
  		return options.strict ? strictUriEncode(value) : encodeURIComponent(value);
  	}
  
  	return value;
  }
  
  function decode(value, options) {
  	if (options.decode) {
  		return decodeComponent(value);
  	}
  
  	return value;
  }
  
  function keysSorter(input) {
  	if (Array.isArray(input)) {
  		return input.sort();
  	}
  
  	if (typeof input === 'object') {
  		return keysSorter(Object.keys(input))
  			.sort((a, b) => Number(a) - Number(b))
  			.map(key => input[key]);
  	}
  
  	return input;
  }
  
  function extract(input) {
  	const queryStart = input.indexOf('?');
  	if (queryStart === -1) {
  		return '';
  	}
  
  	return input.slice(queryStart + 1);
  }
  
  function parse(input, options) {
  	options = Object.assign({
  		decode: true,
  		arrayFormat: 'none'
  	}, options);
  
  	const formatter = parserForArrayFormat(options);
  
  	// Create an object with no prototype
  	const ret = Object.create(null);
  
  	if (typeof input !== 'string') {
  		return ret;
  	}
  
  	input = input.trim().replace(/^[?#&]/, '');
  
  	if (!input) {
  		return ret;
  	}
  
  	for (const param of input.split('&')) {
  		let [key, value] = param.replace(/\+/g, ' ').split('=');
  
  		// Missing `=` should be `null`:
  		// http://w3.org/TR/2012/WD-url-20120524/#collect-url-parameters
  		value = value === undefined ? null : decode(value, options);
  
  		formatter(decode(key, options), value, ret);
  	}
  
  	return Object.keys(ret).sort().reduce((result, key) => {
  		const value = ret[key];
  		if (Boolean(value) && typeof value === 'object' && !Array.isArray(value)) {
  			// Sort object keys, not values
  			result[key] = keysSorter(value);
  		} else {
  			result[key] = value;
  		}
  
  		return result;
  	}, Object.create(null));
  }
  
  exports.extract = extract;
  exports.parse = parse;
  
  exports.stringify = (object, options) => {
  	if (!object) {
  		return '';
  	}
  
  	options = Object.assign({
  		encode: true,
  		strict: true,
  		arrayFormat: 'none'
  	}, options);
  
  	const formatter = encoderForArrayFormat(options);
  	const keys = Object.keys(object);
  
  	if (options.sort !== false) {
  		keys.sort(options.sort);
  	}
  
  	return keys.map(key => {
  		const value = object[key];
  
  		if (value === undefined) {
  			return '';
  		}
  
  		if (value === null) {
  			return encode(key, options);
  		}
  
  		if (Array.isArray(value)) {
  			return value
  				.reduce(formatter(key), [])
  				.join('&');
  		}
  
  		return encode(key, options) + '=' + encode(value, options);
  	}).filter(x => x.length > 0).join('&');
  };
  
  exports.parseUrl = (input, options) => {
  	const hashStart = input.indexOf('#');
  	if (hashStart !== -1) {
  		input = input.slice(0, hashStart);
  	}
  
  	return {
  		url: input.split('?')[0] || '',
  		query: parse(extract(input), options)
  	};
  };
  
  
  /***/ }),
  
  /***/ "./node_modules/sdp-interop/lib/array-equals.js":
  /*!******************************************************!*\
    !*** ./node_modules/sdp-interop/lib/array-equals.js ***!
    \******************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports) {
  
  /* Copyright @ 2015 Atlassian Pty Ltd
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  module.exports = function arrayEquals(array) {
      // if the other array is a falsy value, return
      if (!array)
          return false;
  
      // compare lengths - can save a lot of time
      if (this.length != array.length)
          return false;
  
      for (var i = 0, l = this.length; i < l; i++) {
          // Check if we have nested arrays
          if (this[i] instanceof Array && array[i] instanceof Array) {
              // recurse into the nested arrays
              if (!arrayEquals.apply(this[i], [array[i]]))
                  return false;
          } else if (this[i] != array[i]) {
              // Warning - two different object instances will never be equal:
              // {x:20} != {x:20}
              return false;
          }
      }
      return true;
  };
  
  
  
  /***/ }),
  
  /***/ "./node_modules/sdp-interop/lib/index.js":
  /*!***********************************************!*\
    !*** ./node_modules/sdp-interop/lib/index.js ***!
    \***********************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  /* Copyright @ 2015 Atlassian Pty Ltd
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  exports.Interop = __webpack_require__(/*! ./interop */ "./node_modules/sdp-interop/lib/interop.js");
  
  
  /***/ }),
  
  /***/ "./node_modules/sdp-interop/lib/interop.js":
  /*!*************************************************!*\
    !*** ./node_modules/sdp-interop/lib/interop.js ***!
    \*************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  "use strict";
  /* Copyright @ 2015 Atlassian Pty Ltd
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  /* global RTCSessionDescription */
  /* global RTCIceCandidate */
  /* jshint -W097 */
  
  
  var transform = __webpack_require__(/*! ./transform */ "./node_modules/sdp-interop/lib/transform.js");
  var arrayEquals = __webpack_require__(/*! ./array-equals */ "./node_modules/sdp-interop/lib/array-equals.js");
  
  function Interop() {
  
      /**
       * This map holds the most recent Unified Plan offer/answer SDP that was
       * converted to Plan B, with the SDP type ('offer' or 'answer') as keys and
       * the SDP string as values.
       *
       * @type {{}}
       */
      this.cache = {
          mlB2UMap : {},
          mlU2BMap : {}
      };
  }
  
  module.exports = Interop;
  
  /**
   * Changes the candidate args to match with the related Unified Plan
   */
  Interop.prototype.candidateToUnifiedPlan = function(candidate) {
      var cand = new RTCIceCandidate(candidate);
  
      cand.sdpMLineIndex = this.cache.mlB2UMap[cand.sdpMLineIndex];
      /* TODO: change sdpMid to (audio|video)-SSRC */
  
      return cand;
  };
  
  /**
   * Changes the candidate args to match with the related Plan B
   */
  Interop.prototype.candidateToPlanB = function(candidate) {
      var cand = new RTCIceCandidate(candidate);
  
      if (cand.sdpMid.indexOf('audio') === 0) {
        cand.sdpMid = 'audio';
      } else if (cand.sdpMid.indexOf('video') === 0) {
        cand.sdpMid = 'video';
      } else {
        throw new Error('candidate with ' + cand.sdpMid + ' not allowed');
      }
  
      cand.sdpMLineIndex = this.cache.mlU2BMap[cand.sdpMLineIndex];
  
      return cand;
  };
  
  /**
   * Returns the index of the first m-line with the given media type and with a
   * direction which allows sending, in the last Unified Plan description with
   * type "answer" converted to Plan B. Returns {null} if there is no saved
   * answer, or if none of its m-lines with the given type allow sending.
   * @param type the media type ("audio" or "video").
   * @returns {*}
   */
  Interop.prototype.getFirstSendingIndexFromAnswer = function(type) {
      if (!this.cache.answer) {
          return null;
      }
  
      var session = transform.parse(this.cache.answer);
      if (session && session.media && Array.isArray(session.media)){
          for (var i = 0; i < session.media.length; i++) {
              if (session.media[i].type == type &&
                  (!session.media[i].direction /* default to sendrecv */ ||
                      session.media[i].direction === 'sendrecv' ||
                      session.media[i].direction === 'sendonly')){
                  return i;
              }
          }
      }
  
      return null;
  };
  
  /**
   * This method transforms a Unified Plan SDP to an equivalent Plan B SDP. A
   * PeerConnection wrapper transforms the SDP to Plan B before passing it to the
   * application.
   *
   * @param desc
   * @returns {*}
   */
  Interop.prototype.toPlanB = function(desc) {
      var self = this;
      //#region Preliminary input validation.
  
      if (typeof desc !== 'object' || desc === null ||
          typeof desc.sdp !== 'string') {
          console.warn('An empty description was passed as an argument.');
          return desc;
      }
  
      // Objectify the SDP for easier manipulation.
      var session = transform.parse(desc.sdp);
  
      // If the SDP contains no media, there's nothing to transform.
      if (typeof session.media === 'undefined' ||
          !Array.isArray(session.media) || session.media.length === 0) {
          console.warn('The description has no media.');
          return desc;
      }
  
      // Try some heuristics to "make sure" this is a Unified Plan SDP. Plan B
      // SDP has a video, an audio and a data "channel" at most.
      if (session.media.length <= 3 && session.media.every(function(m) {
              return ['video', 'audio', 'data'].indexOf(m.mid) !== -1;
          })) {
          console.warn('This description does not look like Unified Plan.');
          return desc;
      }
  
      //#endregion
  
      // HACK https://bugzilla.mozilla.org/show_bug.cgi?id=1113443
      var sdp = desc.sdp;
      var rewrite = false;
      for (var i = 0; i < session.media.length; i++) {
          var uLine = session.media[i];
          uLine.rtp.forEach(function(rtp) {
              if (rtp.codec === 'NULL')
              {
                  rewrite = true;
                  var offer = transform.parse(self.cache.offer);
                  rtp.codec = offer.media[i].rtp[0].codec;
              }
          });
      }
      if (rewrite) {
          sdp = transform.write(session);
      }
  
      // Unified Plan SDP is our "precious". Cache it for later use in the Plan B
      // -> Unified Plan transformation.
      this.cache[desc.type] = sdp;
  
      //#region Convert from Unified Plan to Plan B.
  
      // We rebuild the session.media array.
      var media = session.media;
      session.media = [];
  
      // Associative array that maps channel types to channel objects for fast
      // access to channel objects by their type, e.g. type2bl['audio']->channel
      // obj.
      var type2bl = {};
  
      // Used to build the group:BUNDLE value after the channels construction
      // loop.
      var types = [];
  
      // Used to aggregate the directions of the m-lines.
      var directionResult = {};
  
      media.forEach(function(uLine) {
          // rtcp-mux is required in the Plan B SDP.
          if ((typeof uLine.rtcpMux !== 'string' ||
              uLine.rtcpMux !== 'rtcp-mux') &&
              uLine.direction !== 'inactive') {
              throw new Error('Cannot convert to Plan B because m-lines ' +
                  'without the rtcp-mux attribute were found.');
          }
  
          // If we don't have a channel for this uLine.type OR the selected is
          // inactive, then select this uLine as the channel basis.
          if (typeof type2bl[uLine.type] === 'undefined' ||
              type2bl[uLine.type].direction === 'inactive') {
              type2bl[uLine.type] = uLine;
          }
      });
  
      // Implode the Unified Plan m-lines/tracks into Plan B channels.
      media.forEach(function(uLine) {
          var type = uLine.type;
  
          if (type === 'application') {
              session.media.push(uLine);
              types.push(uLine.mid);
              return;
          }
  
          // Add sources to the channel and handle a=msid.
          if (typeof uLine.sources === 'object') {
              Object.keys(uLine.sources).forEach(function(ssrc) {
                  if (typeof type2bl[type].sources !== 'object')
                      type2bl[type].sources = {};
  
                  // Assign the sources to the channel.
                  type2bl[type].sources[ssrc] = uLine.sources[ssrc];
  
                  if (typeof uLine.msid !== 'undefined') {
                      // In Plan B the msid is an SSRC attribute. Also, we don't
                      // care about the obsolete label and mslabel attributes.
                      //
                      // Note that it is not guaranteed that the uLine will
                      // have an msid. recvonly channels in particular don't have
                      // one.
                      type2bl[type].sources[ssrc].msid = uLine.msid;
                  }
                  // NOTE ssrcs in ssrc groups will share msids, as
                  // draft-uberti-rtcweb-plan-00 mandates.
              });
          }
  
          // Add ssrc groups to the channel.
          if (typeof uLine.ssrcGroups !== 'undefined' &&
                  Array.isArray(uLine.ssrcGroups)) {
  
              // Create the ssrcGroups array, if it's not defined.
              if (typeof type2bl[type].ssrcGroups === 'undefined' ||
                      !Array.isArray(type2bl[type].ssrcGroups)) {
                  type2bl[type].ssrcGroups = [];
              }
  
              type2bl[type].ssrcGroups
                  = type2bl[type].ssrcGroups.concat(uLine.ssrcGroups);
          }
  
          var direction = uLine.direction;
  
          directionResult[type]
              = (directionResult[type] || 0 /* inactive */)
                  | directionMasks[direction || 'inactive'];
  
          if (type2bl[type] === uLine) {
              // Plan B mids are in ['audio', 'video', 'data']
              uLine.mid = type;
  
              // Plan B doesn't support/need the bundle-only attribute.
              delete uLine.bundleOnly;
  
              // In Plan B the msid is an SSRC attribute.
              delete uLine.msid;
  
              if (direction !== 'inactive') {
                // Used to build the group:BUNDLE value after this loop.
                types.push(type);
              }
  
              // Add the channel to the new media array.
              session.media.push(uLine);
          }
      });
  
      // We regenerate the BUNDLE group with the new mids.
      session.groups.some(function(group) {
          if (group.type === 'BUNDLE') {
              group.mids = types.join(' ');
              return true;
          }
      });
  
      // msid semantic
      session.msidSemantic = {
          semantic: 'WMS',
          token: '*'
      };
  
      var resStr = transform.write(session);
  
      return new RTCSessionDescription({
          type: desc.type,
          sdp: resStr
      });
  
      //#endregion
  };
  
  /**
   * This method transforms a Plan B SDP to an equivalent Unified Plan SDP. A
   * PeerConnection wrapper transforms the SDP to Unified Plan before passing it
   * to FF.
   *
   * @param desc
   * @returns {*}
   */
  Interop.prototype.toUnifiedPlan = function(desc) {
      var self = this;
      //#region Preliminary input validation.
  
      if (typeof desc !== 'object' || desc === null ||
          typeof desc.sdp !== 'string') {
          console.warn('An empty description was passed as an argument.');
          return desc;
      }
  
      var session = transform.parse(desc.sdp);
  
      // If the SDP contains no media, there's nothing to transform.
      if (typeof session.media === 'undefined' ||
          !Array.isArray(session.media) || session.media.length === 0) {
          console.warn('The description has no media.');
          return desc;
      }
  
      // Try some heuristics to "make sure" this is a Plan B SDP. Plan B SDP has
      // a video, an audio and a data "channel" at most.
      if (session.media.length > 3 || !session.media.every(function(m) {
              return ['video', 'audio', 'data'].indexOf(m.mid) !== -1;
          })) {
          console.warn('This description does not look like Plan B.');
          return desc;
      }
  
      // Make sure this Plan B SDP can be converted to a Unified Plan SDP.
      var mids = [];
      session.media.forEach(function(m) {
          mids.push(m.mid);
      });
  
      var hasBundle = false;
      if (typeof session.groups !== 'undefined' &&
          Array.isArray(session.groups)) {
          hasBundle = session.groups.every(function(g) {
              return g.type !== 'BUNDLE' ||
                  arrayEquals.apply(g.mids.sort(), [mids.sort()]);
          });
      }
  
      if (!hasBundle) {
          throw new Error("Cannot convert to Unified Plan because m-lines that" +
              " are not bundled were found.");
      }
  
      //#endregion
  
  
      //#region Convert from Plan B to Unified Plan.
  
      // Unfortunately, a Plan B offer/answer doesn't have enough information to
      // rebuild an equivalent Unified Plan offer/answer.
      //
      // For example, if this is a local answer (in Unified Plan style) that we
      // convert to Plan B prior to handing it over to the application (the
      // PeerConnection wrapper called us, for instance, after a successful
      // createAnswer), we want to remember the m-line at which we've seen the
      // (local) SSRC. That's because when the application wants to do call the
      // SLD method, forcing us to do the inverse transformation (from Plan B to
      // Unified Plan), we need to know to which m-line to assign the (local)
      // SSRC. We also need to know all the other m-lines that the original
      // answer had and include them in the transformed answer as well.
      //
      // Another example is if this is a remote offer that we convert to Plan B
      // prior to giving it to the application, we want to remember the mid at
      // which we've seen the (remote) SSRC.
      //
      // In the iteration that follows, we use the cached Unified Plan (if it
      // exists) to assign mids to ssrcs.
  
      var cached;
      if (typeof this.cache[desc.type] !== 'undefined') {
          cached = transform.parse(this.cache[desc.type]);
      }
  
      var recvonlySsrcs = {
          audio: {},
          video: {}
      };
  
      // A helper map that sends mids to m-line objects. We use it later to
      // rebuild the Unified Plan style session.media array.
      var mid2ul = {};
      var bIdx = 0;
      var uIdx = 0;
  
      session.media.forEach(function(bLine) {
          if ((typeof bLine.rtcpMux !== 'string' ||
              bLine.rtcpMux !== 'rtcp-mux') &&
              bLine.direction !== 'inactive') {
              throw new Error("Cannot convert to Unified Plan because m-lines " +
                  "without the rtcp-mux attribute were found.");
          }
  
          if (bLine.type === 'application') {
              mid2ul[bLine.mid] = bLine;
              return;
          }
  
          // With rtcp-mux and bundle all the channels should have the same ICE
          // stuff.
          var sources = bLine.sources;
          var ssrcGroups = bLine.ssrcGroups;
          var candidates = bLine.candidates;
          var iceUfrag = bLine.iceUfrag;
          var icePwd = bLine.icePwd;
          var fingerprint = bLine.fingerprint;
          var port = bLine.port;
  
          // We'll use the "bLine" object as a prototype for each new "mLine"
          // that we create, but first we need to clean it up a bit.
          delete bLine.sources;
          delete bLine.ssrcGroups;
          delete bLine.candidates;
          delete bLine.iceUfrag;
          delete bLine.icePwd;
          delete bLine.fingerprint;
          delete bLine.port;
          delete bLine.mid;
  
          // inverted ssrc group map
          var ssrc2group = {};
          if (typeof ssrcGroups !== 'undefined' && Array.isArray(ssrcGroups)) {
              ssrcGroups.forEach(function (ssrcGroup) {
  
                  // TODO(gp) find out how to receive simulcast with FF. For the
                  // time being, hide it.
                  if (ssrcGroup.semantics === 'SIM') {
                      return;
                  }
  
                  // XXX This might brake if an SSRC is in more than one group
                  // for some reason.
                  if (typeof ssrcGroup.ssrcs !== 'undefined' &&
                      Array.isArray(ssrcGroup.ssrcs)) {
                      ssrcGroup.ssrcs.forEach(function (ssrc) {
                          if (typeof ssrc2group[ssrc] === 'undefined') {
                              ssrc2group[ssrc] = [];
                          }
  
                          ssrc2group[ssrc].push(ssrcGroup);
                      });
                  }
              });
          }
  
          // ssrc to m-line index.
          var ssrc2ml = {};
  
          if (typeof sources === 'object') {
  
              // Explode the Plan B channel sources with one m-line per source.
              Object.keys(sources).forEach(function(ssrc) {
  
                  // The (unified) m-line for this SSRC. We either create it from
                  // scratch or, if it's a grouped SSRC, we re-use a related
                  // mline. In other words, if the source is grouped with another
                  // source, put the two together in the same m-line.
                  var uLine;
  
                  // We assume here that we are the answerer in the O/A, so any
                  // offers which we translate come from the remote side, while
                  // answers are local. So the check below is to make that we
                  // handle receive-only SSRCs in a special way only if they come
                  // from the remote side.
                  if (desc.type==='offer') {
                      // We want to detect SSRCs which are used by a remote peer
                      // in an m-line with direction=recvonly (i.e. they are
                      // being used for RTCP only).
                      // This information would have gotten lost if the remote
                      // peer used Unified Plan and their local description was
                      // translated to Plan B. So we use the lack of an MSID
                      // attribute to deduce a "receive only" SSRC.
                      if (!sources[ssrc].msid) {
                          recvonlySsrcs[bLine.type][ssrc] = sources[ssrc];
                          // Receive-only SSRCs must not create new m-lines. We
                          // will assign them to an existing m-line later.
                          return;
                      }
                  }
  
                  if (typeof ssrc2group[ssrc] !== 'undefined' &&
                      Array.isArray(ssrc2group[ssrc])) {
                      ssrc2group[ssrc].some(function (ssrcGroup) {
                          // ssrcGroup.ssrcs *is* an Array, no need to check
                          // again here.
                          return ssrcGroup.ssrcs.some(function (related) {
                              if (typeof ssrc2ml[related] === 'object') {
                                  uLine = ssrc2ml[related];
                                  return true;
                              }
                          });
                      });
                  }
  
                  if (typeof uLine === 'object') {
                      // the m-line already exists. Just add the source.
                      uLine.sources[ssrc] = sources[ssrc];
                      delete sources[ssrc].msid;
                  } else {
                      // Use the "bLine" as a prototype for the "uLine".
                      uLine = Object.create(bLine);
                      ssrc2ml[ssrc] = uLine;
  
                      if (typeof sources[ssrc].msid !== 'undefined') {
                          // Assign the msid of the source to the m-line. Note
                          // that it is not guaranteed that the source will have
                          // msid. In particular "recvonly" sources don't have an
                          // msid. Note that "recvonly" is a term only defined
                          // for m-lines.
                          uLine.msid = sources[ssrc].msid;
                          delete sources[ssrc].msid;
                      }
  
                      // We assign one SSRC per media line.
                      uLine.sources = {};
                      uLine.sources[ssrc] = sources[ssrc];
                      uLine.ssrcGroups = ssrc2group[ssrc];
  
                      // Use the cached Unified Plan SDP (if it exists) to assign
                      // SSRCs to mids.
                      if (typeof cached !== 'undefined' &&
                          typeof cached.media !== 'undefined' &&
                          Array.isArray(cached.media)) {
  
                          cached.media.forEach(function (m) {
                              if (typeof m.sources === 'object') {
                                  Object.keys(m.sources).forEach(function (s) {
                                      if (s === ssrc) {
                                          uLine.mid = m.mid;
                                      }
                                  });
                              }
                          });
                      }
  
                      if (typeof uLine.mid === 'undefined') {
  
                          // If this is an SSRC that we see for the first time
                          // assign it a new mid. This is typically the case when
                          // this method is called to transform a remote
                          // description for the first time or when there is a
                          // new SSRC in the remote description because a new
                          // peer has joined the conference. Local SSRCs should
                          // have already been added to the map in the toPlanB
                          // method.
                          //
                          // Because FF generates answers in Unified Plan style,
                          // we MUST already have a cached answer with all the
                          // local SSRCs mapped to some m-line/mid.
  
                          if (desc.type === 'answer') {
                              throw new Error("An unmapped SSRC was found.");
                          }
  
                          uLine.mid = [bLine.type, '-', ssrc].join('');
                      }
  
                      // Include the candidates in the 1st media line.
                      uLine.candidates = candidates;
                      uLine.iceUfrag = iceUfrag;
                      uLine.icePwd = icePwd;
                      uLine.fingerprint = fingerprint;
                      uLine.port = port;
  
                      mid2ul[uLine.mid] = uLine;
  
                      self.cache.mlU2BMap[uIdx] = bIdx;
                      if (typeof self.cache.mlB2UMap[bIdx] === 'undefined') {
                        self.cache.mlB2UMap[bIdx] = uIdx;
                      }
                      uIdx++;
                  }
              });
          }
  
          bIdx++;
      });
  
      // Rebuild the media array in the right order and add the missing mLines
      // (missing from the Plan B SDP).
      session.media = [];
      mids = []; // reuse
  
      if (desc.type === 'answer') {
  
          // The media lines in the answer must match the media lines in the
          // offer. The order is important too. Here we assume that Firefox is
          // the answerer, so we merely have to use the reconstructed (unified)
          // answer to update the cached (unified) answer accordingly.
          //
          // In the general case, one would have to use the cached (unified)
          // offer to find the m-lines that are missing from the reconstructed
          // answer, potentially grabbing them from the cached (unified) answer.
          // One has to be careful with this approach because inactive m-lines do
          // not always have an mid, making it tricky (impossible?) to find where
          // exactly and which m-lines are missing from the reconstructed answer.
  
          for (var i = 0; i < cached.media.length; i++) {
              var uLine = cached.media[i];
  
              if (typeof mid2ul[uLine.mid] === 'undefined') {
  
                  // The mid isn't in the reconstructed (unified) answer.
                  // This is either a (unified) m-line containing a remote
                  // track only, or a (unified) m-line containing a remote
                  // track and a local track that has been removed.
                  // In either case, it MUST exist in the cached
                  // (unified) answer.
                  //
                  // In case this is a removed local track, clean-up
                  // the (unified) m-line and make sure it's 'recvonly' or
                  // 'inactive'.
  
                  delete uLine.msid;
                  delete uLine.sources;
                  delete uLine.ssrcGroups;
                  if (!uLine.direction
                      || uLine.direction === 'sendrecv')
                      uLine.direction = 'recvonly';
                  else if (uLine.direction === 'sendonly')
                      uLine.direction = 'inactive';
              } else {
                  // This is an (unified) m-line/channel that contains a local
                  // track (sendrecv or sendonly channel) or it's a unified
                  // recvonly m-line/channel. In either case, since we're
                  // going from PlanB -> Unified Plan this m-line MUST
                  // exist in the cached answer.
              }
  
              session.media.push(uLine);
  
              if (typeof uLine.mid === 'string') {
                  // inactive lines don't/may not have an mid.
                  mids.push(uLine.mid);
              }
          }
      } else {
  
          // SDP offer/answer (and the JSEP spec) forbids removing an m-section
          // under any circumstances. If we are no longer interested in sending a
          // track, we just remove the msid and ssrc attributes and set it to
          // either a=recvonly (as the reofferer, we must use recvonly if the
          // other side was previously sending on the m-section, but we can also
          // leave the possibility open if it wasn't previously in use), or
          // a=inactive.
  
          if (typeof cached !== 'undefined' &&
              typeof cached.media !== 'undefined' &&
              Array.isArray(cached.media)) {
              cached.media.forEach(function(uLine) {
                  mids.push(uLine.mid);
                  if (typeof mid2ul[uLine.mid] !== 'undefined') {
                      session.media.push(mid2ul[uLine.mid]);
                  } else {
                      delete uLine.msid;
                      delete uLine.sources;
                      delete uLine.ssrcGroups;
                      if (!uLine.direction
                          || uLine.direction === 'sendrecv')
                          uLine.direction = 'recvonly';
                      if (!uLine.direction
                          || uLine.direction === 'sendonly')
                          uLine.direction = 'inactive';
                      session.media.push(uLine);
                  }
              });
          }
  
          // Add all the remaining (new) m-lines of the transformed SDP.
          Object.keys(mid2ul).forEach(function(mid) {
              if (mids.indexOf(mid) === -1) {
                  mids.push(mid);
                  if (mid2ul[mid].direction === 'recvonly') {
                      // This is a remote recvonly channel. Add its SSRC to the
                      // appropriate sendrecv or sendonly channel.
                      // TODO(gp) what if we don't have sendrecv/sendonly
                      // channel?
  
                      session.media.some(function (uLine) {
                          if ((uLine.direction === 'sendrecv' ||
                              uLine.direction === 'sendonly') &&
                              uLine.type === mid2ul[mid].type) {
  
                              // mid2ul[mid] shouldn't have any ssrc-groups
                              Object.keys(mid2ul[mid].sources).forEach(
                                  function (ssrc) {
                                  uLine.sources[ssrc] =
                                      mid2ul[mid].sources[ssrc];
                              });
  
                              return true;
                          }
                      });
                  } else {
                      session.media.push(mid2ul[mid]);
                  }
              }
          });
      }
  
      // After we have constructed the Plan Unified m-lines we can figure out
      // where (in which m-line) to place the 'recvonly SSRCs'.
      // Note: we assume here that we are the answerer in the O/A, so any offers
      // which we translate come from the remote side, while answers are local
      // (and so our last local description is cached as an 'answer').
      ["audio", "video"].forEach(function (type) {
          if (!session || !session.media || !Array.isArray(session.media))
              return;
  
          var idx = null;
          if (Object.keys(recvonlySsrcs[type]).length > 0) {
              idx = self.getFirstSendingIndexFromAnswer(type);
              if (idx === null){
                  // If this is the first offer we receive, we don't have a
                  // cached answer. Assume that we will be sending media using
                  // the first m-line for each media type.
  
                  for (var i = 0; i < session.media.length; i++) {
                      if (session.media[i].type === type) {
                          idx = i;
                          break;
                      }
                  }
              }
          }
  
          if (idx && session.media.length > idx) {
              var mLine = session.media[idx];
              Object.keys(recvonlySsrcs[type]).forEach(function(ssrc) {
                  if (mLine.sources && mLine.sources[ssrc]) {
                      console.warn("Replacing an existing SSRC.");
                  }
                  if (!mLine.sources) {
                      mLine.sources = {};
                  }
  
                  mLine.sources[ssrc] = recvonlySsrcs[type][ssrc];
              });
          }
      });
  
      // We regenerate the BUNDLE group (since we regenerated the mids)
      session.groups.some(function(group) {
          if (group.type === 'BUNDLE') {
              group.mids = mids.join(' ');
              return true;
          }
      });
  
      // msid semantic
      session.msidSemantic = {
          semantic: 'WMS',
          token: '*'
      };
  
      var resStr = transform.write(session);
  
      // Cache the transformed SDP (Unified Plan) for later re-use in this
      // function.
      this.cache[desc.type] = resStr;
  
      return new RTCSessionDescription({
          type: desc.type,
          sdp: resStr
      });
  
      //#endregion
  };
  
  /**
   * Maps the direction strings to their binary representation. The binary
   * representation of the directions will contain only 2 bits. The least
   * significant bit will indicate the receiving direction and the other bit will
   * indicate the sending direction.
   *
   * @type {Map<string, number>}
   */
  var directionMasks = {
      'inactive': 0, // 00
      'recvonly': 1, // 01
      'sendonly': 2, // 10
      'sendrecv': 3  // 11
  }
  
  /**
   * Parses a number into direction string.
   *
   * @param {number} direction - The number to be parsed.
   * @returns {string} - The parsed direction string.
   */
  function parseDirection(direction) {
      // Filter all other bits except the 2 less significant.
      var directionMask = direction & 3;
  
      switch (directionMask) {
      case 0:
          return 'inactive';
      case 1:
          return 'recvonly';
      case 2:
          return 'sendonly';
      case 3:
          return 'sendrecv';
      }
  }
  
  
  /***/ }),
  
  /***/ "./node_modules/sdp-interop/lib/transform.js":
  /*!***************************************************!*\
    !*** ./node_modules/sdp-interop/lib/transform.js ***!
    \***************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  /* Copyright @ 2015 Atlassian Pty Ltd
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  var transform = __webpack_require__(/*! sdp-transform */ "./node_modules/sdp-interop/node_modules/sdp-transform/lib/index.js");
  
  exports.write = function(session, opts) {
  
    if (typeof session !== 'undefined' &&
        typeof session.media !== 'undefined' &&
        Array.isArray(session.media)) {
  
      session.media.forEach(function (mLine) {
        // expand sources to ssrcs
        if (typeof mLine.sources !== 'undefined' &&
          Object.keys(mLine.sources).length !== 0) {
            mLine.ssrcs = [];
            Object.keys(mLine.sources).forEach(function (ssrc) {
              var source = mLine.sources[ssrc];
              Object.keys(source).forEach(function (attribute) {
                mLine.ssrcs.push({
                  id: ssrc,
                  attribute: attribute,
                  value: source[attribute]
                });
              });
            });
            delete mLine.sources;
          }
  
        // join ssrcs in ssrc groups
        if (typeof mLine.ssrcGroups !== 'undefined' &&
          Array.isArray(mLine.ssrcGroups)) {
            mLine.ssrcGroups.forEach(function (ssrcGroup) {
              if (typeof ssrcGroup.ssrcs !== 'undefined' &&
                  Array.isArray(ssrcGroup.ssrcs)) {
                ssrcGroup.ssrcs = ssrcGroup.ssrcs.join(' ');
              }
            });
          }
      });
    }
  
    // join group mids
    if (typeof session !== 'undefined' &&
        typeof session.groups !== 'undefined' && Array.isArray(session.groups)) {
  
      session.groups.forEach(function (g) {
        if (typeof g.mids !== 'undefined' && Array.isArray(g.mids)) {
          g.mids = g.mids.join(' ');
        }
      });
    }
  
    return transform.write(session, opts);
  };
  
  exports.parse = function(sdp) {
    var session = transform.parse(sdp);
  
    if (typeof session !== 'undefined' && typeof session.media !== 'undefined' &&
        Array.isArray(session.media)) {
  
      session.media.forEach(function (mLine) {
        // group sources attributes by ssrc
        if (typeof mLine.ssrcs !== 'undefined' && Array.isArray(mLine.ssrcs)) {
          mLine.sources = {};
          mLine.ssrcs.forEach(function (ssrc) {
            if (!mLine.sources[ssrc.id])
            mLine.sources[ssrc.id] = {};
          mLine.sources[ssrc.id][ssrc.attribute] = ssrc.value;
          });
  
          delete mLine.ssrcs;
        }
  
        // split ssrcs in ssrc groups
        if (typeof mLine.ssrcGroups !== 'undefined' &&
          Array.isArray(mLine.ssrcGroups)) {
            mLine.ssrcGroups.forEach(function (ssrcGroup) {
              if (typeof ssrcGroup.ssrcs === 'string') {
                ssrcGroup.ssrcs = ssrcGroup.ssrcs.split(' ');
              }
            });
          }
      });
    }
    // split group mids
    if (typeof session !== 'undefined' &&
        typeof session.groups !== 'undefined' && Array.isArray(session.groups)) {
  
      session.groups.forEach(function (g) {
        if (typeof g.mids === 'string') {
          g.mids = g.mids.split(' ');
        }
      });
    }
  
    return session;
  };
  
  
  
  /***/ }),
  
  /***/ "./node_modules/sdp-interop/node_modules/sdp-transform/lib/grammar.js":
  /*!****************************************************************************!*\
    !*** ./node_modules/sdp-interop/node_modules/sdp-transform/lib/grammar.js ***!
    \****************************************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports) {
  
  var grammar = module.exports = {
    v: [{
      name: 'version',
      reg: /^(\d*)$/
    }],
    o: [{ //o=- 20518 0 IN IP4 203.0.113.1
      // NB: sessionId will be a String in most cases because it is huge
      name: 'origin',
      reg: /^(\S*) (\d*) (\d*) (\S*) IP(\d) (\S*)/,
      names: ['username', 'sessionId', 'sessionVersion', 'netType', 'ipVer', 'address'],
      format: '%s %s %d %s IP%d %s'
    }],
    // default parsing of these only (though some of these feel outdated)
    s: [{ name: 'name' }],
    i: [{ name: 'description' }],
    u: [{ name: 'uri' }],
    e: [{ name: 'email' }],
    p: [{ name: 'phone' }],
    z: [{ name: 'timezones' }], // TODO: this one can actually be parsed properly..
    r: [{ name: 'repeats' }],   // TODO: this one can also be parsed properly
    //k: [{}], // outdated thing ignored
    t: [{ //t=0 0
      name: 'timing',
      reg: /^(\d*) (\d*)/,
      names: ['start', 'stop'],
      format: '%d %d'
    }],
    c: [{ //c=IN IP4 10.47.197.26
      name: 'connection',
      reg: /^IN IP(\d) (\S*)/,
      names: ['version', 'ip'],
      format: 'IN IP%d %s'
    }],
    b: [{ //b=AS:4000
      push: 'bandwidth',
      reg: /^(TIAS|AS|CT|RR|RS):(\d*)/,
      names: ['type', 'limit'],
      format: '%s:%s'
    }],
    m: [{ //m=video 51744 RTP/AVP 126 97 98 34 31
      // NB: special - pushes to session
      // TODO: rtp/fmtp should be filtered by the payloads found here?
      reg: /^(\w*) (\d*) ([\w\/]*)(?: (.*))?/,
      names: ['type', 'port', 'protocol', 'payloads'],
      format: '%s %d %s %s'
    }],
    a: [
      { //a=rtpmap:110 opus/48000/2
        push: 'rtp',
        reg: /^rtpmap:(\d*) ([\w\-\.]*)(?:\s*\/(\d*)(?:\s*\/(\S*))?)?/,
        names: ['payload', 'codec', 'rate', 'encoding'],
        format: function (o) {
          return (o.encoding) ?
            'rtpmap:%d %s/%s/%s':
            o.rate ?
            'rtpmap:%d %s/%s':
            'rtpmap:%d %s';
        }
      },
      { //a=fmtp:108 profile-level-id=24;object=23;bitrate=64000
        //a=fmtp:111 minptime=10; useinbandfec=1
        push: 'fmtp',
        reg: /^fmtp:(\d*) ([\S| ]*)/,
        names: ['payload', 'config'],
        format: 'fmtp:%d %s'
      },
      { //a=control:streamid=0
        name: 'control',
        reg: /^control:(.*)/,
        format: 'control:%s'
      },
      { //a=rtcp:65179 IN IP4 193.84.77.194
        name: 'rtcp',
        reg: /^rtcp:(\d*)(?: (\S*) IP(\d) (\S*))?/,
        names: ['port', 'netType', 'ipVer', 'address'],
        format: function (o) {
          return (o.address != null) ?
            'rtcp:%d %s IP%d %s':
            'rtcp:%d';
        }
      },
      { //a=rtcp-fb:98 trr-int 100
        push: 'rtcpFbTrrInt',
        reg: /^rtcp-fb:(\*|\d*) trr-int (\d*)/,
        names: ['payload', 'value'],
        format: 'rtcp-fb:%d trr-int %d'
      },
      { //a=rtcp-fb:98 nack rpsi
        push: 'rtcpFb',
        reg: /^rtcp-fb:(\*|\d*) ([\w-_]*)(?: ([\w-_]*))?/,
        names: ['payload', 'type', 'subtype'],
        format: function (o) {
          return (o.subtype != null) ?
            'rtcp-fb:%s %s %s':
            'rtcp-fb:%s %s';
        }
      },
      { //a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
        //a=extmap:1/recvonly URI-gps-string
        push: 'ext',
        reg: /^extmap:(\d+)(?:\/(\w+))? (\S*)(?: (\S*))?/,
        names: ['value', 'direction', 'uri', 'config'],
        format: function (o) {
          return 'extmap:%d' + (o.direction ? '/%s' : '%v') + ' %s' + (o.config ? ' %s' : '');
        }
      },
      { //a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:PS1uQCVeeCFCanVmcjkpPywjNWhcYD0mXXtxaVBR|2^20|1:32
        push: 'crypto',
        reg: /^crypto:(\d*) ([\w_]*) (\S*)(?: (\S*))?/,
        names: ['id', 'suite', 'config', 'sessionConfig'],
        format: function (o) {
          return (o.sessionConfig != null) ?
            'crypto:%d %s %s %s':
            'crypto:%d %s %s';
        }
      },
      { //a=setup:actpass
        name: 'setup',
        reg: /^setup:(\w*)/,
        format: 'setup:%s'
      },
      { //a=mid:1
        name: 'mid',
        reg: /^mid:([^\s]*)/,
        format: 'mid:%s'
      },
      { //a=msid:0c8b064d-d807-43b4-b434-f92a889d8587 98178685-d409-46e0-8e16-7ef0db0db64a
        name: 'msid',
        reg: /^msid:(.*)/,
        format: 'msid:%s'
      },
      { //a=ptime:20
        name: 'ptime',
        reg: /^ptime:(\d*)/,
        format: 'ptime:%d'
      },
      { //a=maxptime:60
        name: 'maxptime',
        reg: /^maxptime:(\d*)/,
        format: 'maxptime:%d'
      },
      { //a=sendrecv
        name: 'direction',
        reg: /^(sendrecv|recvonly|sendonly|inactive)/
      },
      { //a=ice-lite
        name: 'icelite',
        reg: /^(ice-lite)/
      },
      { //a=ice-ufrag:F7gI
        name: 'iceUfrag',
        reg: /^ice-ufrag:(\S*)/,
        format: 'ice-ufrag:%s'
      },
      { //a=ice-pwd:x9cml/YzichV2+XlhiMu8g
        name: 'icePwd',
        reg: /^ice-pwd:(\S*)/,
        format: 'ice-pwd:%s'
      },
      { //a=fingerprint:SHA-1 00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33
        name: 'fingerprint',
        reg: /^fingerprint:(\S*) (\S*)/,
        names: ['type', 'hash'],
        format: 'fingerprint:%s %s'
      },
      { //a=candidate:0 1 UDP 2113667327 203.0.113.1 54400 typ host
        //a=candidate:1162875081 1 udp 2113937151 192.168.34.75 60017 typ host generation 0 network-id 3 network-cost 10
        //a=candidate:3289912957 2 udp 1845501695 193.84.77.194 60017 typ srflx raddr 192.168.34.75 rport 60017 generation 0 network-id 3 network-cost 10
        //a=candidate:229815620 1 tcp 1518280447 192.168.150.19 60017 typ host tcptype active generation 0 network-id 3 network-cost 10
        //a=candidate:3289912957 2 tcp 1845501695 193.84.77.194 60017 typ srflx raddr 192.168.34.75 rport 60017 tcptype passive generation 0 network-id 3 network-cost 10
        push:'candidates',
        reg: /^candidate:(\S*) (\d*) (\S*) (\d*) (\S*) (\d*) typ (\S*)(?: raddr (\S*) rport (\d*))?(?: tcptype (\S*))?(?: generation (\d*))?(?: network-id (\d*))?(?: network-cost (\d*))?/,
        names: ['foundation', 'component', 'transport', 'priority', 'ip', 'port', 'type', 'raddr', 'rport', 'tcptype', 'generation', 'network-id', 'network-cost'],
        format: function (o) {
          var str = 'candidate:%s %d %s %d %s %d typ %s';
  
          str += (o.raddr != null) ? ' raddr %s rport %d' : '%v%v';
  
          // NB: candidate has three optional chunks, so %void middles one if it's missing
          str += (o.tcptype != null) ? ' tcptype %s' : '%v';
  
          if (o.generation != null) {
            str += ' generation %d';
          }
  
          str += (o['network-id'] != null) ? ' network-id %d' : '%v';
          str += (o['network-cost'] != null) ? ' network-cost %d' : '%v';
          return str;
        }
      },
      { //a=end-of-candidates (keep after the candidates line for readability)
        name: 'endOfCandidates',
        reg: /^(end-of-candidates)/
      },
      { //a=remote-candidates:1 203.0.113.1 54400 2 203.0.113.1 54401 ...
        name: 'remoteCandidates',
        reg: /^remote-candidates:(.*)/,
        format: 'remote-candidates:%s'
      },
      { //a=ice-options:google-ice
        name: 'iceOptions',
        reg: /^ice-options:(\S*)/,
        format: 'ice-options:%s'
      },
      { //a=ssrc:2566107569 cname:t9YU8M1UxTF8Y1A1
        push: 'ssrcs',
        reg: /^ssrc:(\d*) ([\w_]*)(?::(.*))?/,
        names: ['id', 'attribute', 'value'],
        format: function (o) {
          var str = 'ssrc:%d';
          if (o.attribute != null) {
            str += ' %s';
            if (o.value != null) {
              str += ':%s';
            }
          }
          return str;
        }
      },
      { //a=ssrc-group:FEC 1 2
        //a=ssrc-group:FEC-FR 3004364195 1080772241
        push: 'ssrcGroups',
        // token-char = %x21 / %x23-27 / %x2A-2B / %x2D-2E / %x30-39 / %x41-5A / %x5E-7E
        reg: /^ssrc-group:([\x21\x23\x24\x25\x26\x27\x2A\x2B\x2D\x2E\w]*) (.*)/,
        names: ['semantics', 'ssrcs'],
        format: 'ssrc-group:%s %s'
      },
      { //a=msid-semantic: WMS Jvlam5X3SX1OP6pn20zWogvaKJz5Hjf9OnlV
        name: 'msidSemantic',
        reg: /^msid-semantic:\s?(\w*) (\S*)/,
        names: ['semantic', 'token'],
        format: 'msid-semantic: %s %s' // space after ':' is not accidental
      },
      { //a=group:BUNDLE audio video
        push: 'groups',
        reg: /^group:(\w*) (.*)/,
        names: ['type', 'mids'],
        format: 'group:%s %s'
      },
      { //a=rtcp-mux
        name: 'rtcpMux',
        reg: /^(rtcp-mux)/
      },
      { //a=rtcp-rsize
        name: 'rtcpRsize',
        reg: /^(rtcp-rsize)/
      },
      { //a=sctpmap:5000 webrtc-datachannel 1024
        name: 'sctpmap',
        reg: /^sctpmap:([\w_\/]*) (\S*)(?: (\S*))?/,
        names: ['sctpmapNumber', 'app', 'maxMessageSize'],
        format: function (o) {
          return (o.maxMessageSize != null) ?
            'sctpmap:%s %s %s' :
            'sctpmap:%s %s';
        }
      },
      { //a=x-google-flag:conference
        name: 'xGoogleFlag',
        reg: /^x-google-flag:([^\s]*)/,
        format: 'x-google-flag:%s'
      },
      { //a=rid:1 send max-width=1280;max-height=720;max-fps=30;depend=0
        push: 'rids',
        reg: /^rid:([\d\w]+) (\w+)(?: ([\S| ]*))?/,
        names: ['id', 'direction', 'params'],
        format: function (o) {
          return (o.params) ? 'rid:%s %s %s' : 'rid:%s %s';
        }
      },
      { //a=imageattr:97 send [x=800,y=640,sar=1.1,q=0.6] [x=480,y=320] recv [x=330,y=250]
        //a=imageattr:* send [x=800,y=640] recv *
        //a=imageattr:100 recv [x=320,y=240]
        push: 'imageattrs',
        reg: new RegExp(
          //a=imageattr:97
          '^imageattr:(\\d+|\\*)' +
          //send [x=800,y=640,sar=1.1,q=0.6] [x=480,y=320]
          '[\\s\\t]+(send|recv)[\\s\\t]+(\\*|\\[\\S+\\](?:[\\s\\t]+\\[\\S+\\])*)' +
          //recv [x=330,y=250]
          '(?:[\\s\\t]+(recv|send)[\\s\\t]+(\\*|\\[\\S+\\](?:[\\s\\t]+\\[\\S+\\])*))?'
        ),
        names: ['pt', 'dir1', 'attrs1', 'dir2', 'attrs2'],
        format: function (o) {
          return 'imageattr:%s %s %s' + (o.dir2 ? ' %s %s' : '');
        }
      },
      { //a=simulcast:send 1,2,3;~4,~5 recv 6;~7,~8
        //a=simulcast:recv 1;4,5 send 6;7
        name: 'simulcast',
        reg: new RegExp(
          //a=simulcast:
          '^simulcast:' +
          //send 1,2,3;~4,~5
          '(send|recv) ([a-zA-Z0-9\\-_~;,]+)' +
          //space + recv 6;~7,~8
          '(?:\\s?(send|recv) ([a-zA-Z0-9\\-_~;,]+))?' +
          //end
          '$'
        ),
        names: ['dir1', 'list1', 'dir2', 'list2'],
        format: function (o) {
          return 'simulcast:%s %s' + (o.dir2 ? ' %s %s' : '');
        }
      },
      { //Old simulcast draft 03 (implemented by Firefox)
        //  https://tools.ietf.org/html/draft-ietf-mmusic-sdp-simulcast-03
        //a=simulcast: recv pt=97;98 send pt=97
        //a=simulcast: send rid=5;6;7 paused=6,7
        name: 'simulcast_03',
        reg: /^simulcast:[\s\t]+([\S+\s\t]+)$/,
        names: ['value'],
        format: 'simulcast: %s'
      },
      {
        //a=framerate:25
        //a=framerate:29.97
        name: 'framerate',
        reg: /^framerate:(\d+(?:$|\.\d+))/,
        format: 'framerate:%s'
      },
      { // any a= that we don't understand is kepts verbatim on media.invalid
        push: 'invalid',
        names: ['value']
      }
    ]
  };
  
  // set sensible defaults to avoid polluting the grammar with boring details
  Object.keys(grammar).forEach(function (key) {
    var objs = grammar[key];
    objs.forEach(function (obj) {
      if (!obj.reg) {
        obj.reg = /(.*)/;
      }
      if (!obj.format) {
        obj.format = '%s';
      }
    });
  });
  
  
  /***/ }),
  
  /***/ "./node_modules/sdp-interop/node_modules/sdp-transform/lib/index.js":
  /*!**************************************************************************!*\
    !*** ./node_modules/sdp-interop/node_modules/sdp-transform/lib/index.js ***!
    \**************************************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  var parser = __webpack_require__(/*! ./parser */ "./node_modules/sdp-interop/node_modules/sdp-transform/lib/parser.js");
  var writer = __webpack_require__(/*! ./writer */ "./node_modules/sdp-interop/node_modules/sdp-transform/lib/writer.js");
  
  exports.write = writer;
  exports.parse = parser.parse;
  exports.parseFmtpConfig = parser.parseFmtpConfig;
  exports.parseParams = parser.parseParams;
  exports.parsePayloads = parser.parsePayloads;
  exports.parseRemoteCandidates = parser.parseRemoteCandidates;
  exports.parseImageAttributes = parser.parseImageAttributes;
  exports.parseSimulcastStreamList = parser.parseSimulcastStreamList;
  
  
  /***/ }),
  
  /***/ "./node_modules/sdp-interop/node_modules/sdp-transform/lib/parser.js":
  /*!***************************************************************************!*\
    !*** ./node_modules/sdp-interop/node_modules/sdp-transform/lib/parser.js ***!
    \***************************************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  var toIntIfInt = function (v) {
    return String(Number(v)) === v ? Number(v) : v;
  };
  
  var attachProperties = function (match, location, names, rawName) {
    if (rawName && !names) {
      location[rawName] = toIntIfInt(match[1]);
    }
    else {
      for (var i = 0; i < names.length; i += 1) {
        if (match[i+1] != null) {
          location[names[i]] = toIntIfInt(match[i+1]);
        }
      }
    }
  };
  
  var parseReg = function (obj, location, content) {
    var needsBlank = obj.name && obj.names;
    if (obj.push && !location[obj.push]) {
      location[obj.push] = [];
    }
    else if (needsBlank && !location[obj.name]) {
      location[obj.name] = {};
    }
    var keyLocation = obj.push ?
      {} :  // blank object that will be pushed
      needsBlank ? location[obj.name] : location; // otherwise, named location or root
  
    attachProperties(content.match(obj.reg), keyLocation, obj.names, obj.name);
  
    if (obj.push) {
      location[obj.push].push(keyLocation);
    }
  };
  
  var grammar = __webpack_require__(/*! ./grammar */ "./node_modules/sdp-interop/node_modules/sdp-transform/lib/grammar.js");
  var validLine = RegExp.prototype.test.bind(/^([a-z])=(.*)/);
  
  exports.parse = function (sdp) {
    var session = {}
      , media = []
      , location = session; // points at where properties go under (one of the above)
  
    // parse lines we understand
    sdp.split(/(\r\n|\r|\n)/).filter(validLine).forEach(function (l) {
      var type = l[0];
      var content = l.slice(2);
      if (type === 'm') {
        media.push({rtp: [], fmtp: []});
        location = media[media.length-1]; // point at latest media line
      }
  
      for (var j = 0; j < (grammar[type] || []).length; j += 1) {
        var obj = grammar[type][j];
        if (obj.reg.test(content)) {
          return parseReg(obj, location, content);
        }
      }
    });
  
    session.media = media; // link it up
    return session;
  };
  
  var paramReducer = function (acc, expr) {
    var s = expr.split(/=(.+)/, 2);
    if (s.length === 2) {
      acc[s[0]] = toIntIfInt(s[1]);
    }
    return acc;
  };
  
  exports.parseParams = function (str) {
    return str.split(/\;\s?/).reduce(paramReducer, {});
  };
  
  // For backward compatibility - alias will be removed in 3.0.0
  exports.parseFmtpConfig = exports.parseParams;
  
  exports.parsePayloads = function (str) {
    return str.split(' ').map(Number);
  };
  
  exports.parseRemoteCandidates = function (str) {
    var candidates = [];
    var parts = str.split(' ').map(toIntIfInt);
    for (var i = 0; i < parts.length; i += 3) {
      candidates.push({
        component: parts[i],
        ip: parts[i + 1],
        port: parts[i + 2]
      });
    }
    return candidates;
  };
  
  exports.parseImageAttributes = function (str) {
    return str.split(' ').map(function (item) {
      return item.substring(1, item.length-1).split(',').reduce(paramReducer, {});
    });
  };
  
  exports.parseSimulcastStreamList = function (str) {
    return str.split(';').map(function (stream) {
      return stream.split(',').map(function (format) {
        var scid, paused = false;
  
        if (format[0] !== '~') {
          scid = toIntIfInt(format);
        } else {
          scid = toIntIfInt(format.substring(1, format.length));
          paused = true;
        }
  
        return {
          scid: scid,
          paused: paused
        };
      });
    });
  };
  
  
  /***/ }),
  
  /***/ "./node_modules/sdp-interop/node_modules/sdp-transform/lib/writer.js":
  /*!***************************************************************************!*\
    !*** ./node_modules/sdp-interop/node_modules/sdp-transform/lib/writer.js ***!
    \***************************************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  var grammar = __webpack_require__(/*! ./grammar */ "./node_modules/sdp-interop/node_modules/sdp-transform/lib/grammar.js");
  
  // customized util.format - discards excess arguments and can void middle ones
  var formatRegExp = /%[sdv%]/g;
  var format = function (formatStr) {
    var i = 1;
    var args = arguments;
    var len = args.length;
    return formatStr.replace(formatRegExp, function (x) {
      if (i >= len) {
        return x; // missing argument
      }
      var arg = args[i];
      i += 1;
      switch (x) {
      case '%%':
        return '%';
      case '%s':
        return String(arg);
      case '%d':
        return Number(arg);
      case '%v':
        return '';
      }
    });
    // NB: we discard excess arguments - they are typically undefined from makeLine
  };
  
  var makeLine = function (type, obj, location) {
    var str = obj.format instanceof Function ?
      (obj.format(obj.push ? location : location[obj.name])) :
      obj.format;
  
    var args = [type + '=' + str];
    if (obj.names) {
      for (var i = 0; i < obj.names.length; i += 1) {
        var n = obj.names[i];
        if (obj.name) {
          args.push(location[obj.name][n]);
        }
        else { // for mLine and push attributes
          args.push(location[obj.names[i]]);
        }
      }
    }
    else {
      args.push(location[obj.name]);
    }
    return format.apply(null, args);
  };
  
  // RFC specified order
  // TODO: extend this with all the rest
  var defaultOuterOrder = [
    'v', 'o', 's', 'i',
    'u', 'e', 'p', 'c',
    'b', 't', 'r', 'z', 'a'
  ];
  var defaultInnerOrder = ['i', 'c', 'b', 'a'];
  
  
  module.exports = function (session, opts) {
    opts = opts || {};
    // ensure certain properties exist
    if (session.version == null) {
      session.version = 0; // 'v=0' must be there (only defined version atm)
    }
    if (session.name == null) {
      session.name = ' '; // 's= ' must be there if no meaningful name set
    }
    session.media.forEach(function (mLine) {
      if (mLine.payloads == null) {
        mLine.payloads = '';
      }
    });
  
    var outerOrder = opts.outerOrder || defaultOuterOrder;
    var innerOrder = opts.innerOrder || defaultInnerOrder;
    var sdp = [];
  
    // loop through outerOrder for matching properties on session
    outerOrder.forEach(function (type) {
      grammar[type].forEach(function (obj) {
        if (obj.name in session && session[obj.name] != null) {
          sdp.push(makeLine(type, obj, session));
        }
        else if (obj.push in session && session[obj.push] != null) {
          session[obj.push].forEach(function (el) {
            sdp.push(makeLine(type, obj, el));
          });
        }
      });
    });
  
    // then for each media line, follow the innerOrder
    session.media.forEach(function (mLine) {
      sdp.push(makeLine('m', grammar.m[0], mLine));
  
      innerOrder.forEach(function (type) {
        grammar[type].forEach(function (obj) {
          if (obj.name in mLine && mLine[obj.name] != null) {
            sdp.push(makeLine(type, obj, mLine));
          }
          else if (obj.push in mLine && mLine[obj.push] != null) {
            mLine[obj.push].forEach(function (el) {
              sdp.push(makeLine(type, obj, el));
            });
          }
        });
      });
    });
  
    return sdp.join('\r\n') + '\r\n';
  };
  
  
  /***/ }),
  
  /***/ "./node_modules/sdp-transform/lib/grammar.js":
  /*!***************************************************!*\
    !*** ./node_modules/sdp-transform/lib/grammar.js ***!
    \***************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports) {
  
  var grammar = module.exports = {
    v: [{
      name: 'version',
      reg: /^(\d*)$/
    }],
    o: [{ //o=- 20518 0 IN IP4 203.0.113.1
      // NB: sessionId will be a String in most cases because it is huge
      name: 'origin',
      reg: /^(\S*) (\d*) (\d*) (\S*) IP(\d) (\S*)/,
      names: ['username', 'sessionId', 'sessionVersion', 'netType', 'ipVer', 'address'],
      format: '%s %s %d %s IP%d %s'
    }],
    // default parsing of these only (though some of these feel outdated)
    s: [{ name: 'name' }],
    i: [{ name: 'description' }],
    u: [{ name: 'uri' }],
    e: [{ name: 'email' }],
    p: [{ name: 'phone' }],
    z: [{ name: 'timezones' }], // TODO: this one can actually be parsed properly..
    r: [{ name: 'repeats' }],   // TODO: this one can also be parsed properly
    //k: [{}], // outdated thing ignored
    t: [{ //t=0 0
      name: 'timing',
      reg: /^(\d*) (\d*)/,
      names: ['start', 'stop'],
      format: '%d %d'
    }],
    c: [{ //c=IN IP4 10.47.197.26
      name: 'connection',
      reg: /^IN IP(\d) (\S*)/,
      names: ['version', 'ip'],
      format: 'IN IP%d %s'
    }],
    b: [{ //b=AS:4000
      push: 'bandwidth',
      reg: /^(TIAS|AS|CT|RR|RS):(\d*)/,
      names: ['type', 'limit'],
      format: '%s:%s'
    }],
    m: [{ //m=video 51744 RTP/AVP 126 97 98 34 31
      // NB: special - pushes to session
      // TODO: rtp/fmtp should be filtered by the payloads found here?
      reg: /^(\w*) (\d*) ([\w\/]*)(?: (.*))?/,
      names: ['type', 'port', 'protocol', 'payloads'],
      format: '%s %d %s %s'
    }],
    a: [
      { //a=rtpmap:110 opus/48000/2
        push: 'rtp',
        reg: /^rtpmap:(\d*) ([\w\-\.]*)(?:\s*\/(\d*)(?:\s*\/(\S*))?)?/,
        names: ['payload', 'codec', 'rate', 'encoding'],
        format: function (o) {
          return (o.encoding) ?
            'rtpmap:%d %s/%s/%s':
            o.rate ?
            'rtpmap:%d %s/%s':
            'rtpmap:%d %s';
        }
      },
      { //a=fmtp:108 profile-level-id=24;object=23;bitrate=64000
        //a=fmtp:111 minptime=10; useinbandfec=1
        push: 'fmtp',
        reg: /^fmtp:(\d*) ([\S| ]*)/,
        names: ['payload', 'config'],
        format: 'fmtp:%d %s'
      },
      { //a=control:streamid=0
        name: 'control',
        reg: /^control:(.*)/,
        format: 'control:%s'
      },
      { //a=rtcp:65179 IN IP4 193.84.77.194
        name: 'rtcp',
        reg: /^rtcp:(\d*)(?: (\S*) IP(\d) (\S*))?/,
        names: ['port', 'netType', 'ipVer', 'address'],
        format: function (o) {
          return (o.address != null) ?
            'rtcp:%d %s IP%d %s':
            'rtcp:%d';
        }
      },
      { //a=rtcp-fb:98 trr-int 100
        push: 'rtcpFbTrrInt',
        reg: /^rtcp-fb:(\*|\d*) trr-int (\d*)/,
        names: ['payload', 'value'],
        format: 'rtcp-fb:%d trr-int %d'
      },
      { //a=rtcp-fb:98 nack rpsi
        push: 'rtcpFb',
        reg: /^rtcp-fb:(\*|\d*) ([\w-_]*)(?: ([\w-_]*))?/,
        names: ['payload', 'type', 'subtype'],
        format: function (o) {
          return (o.subtype != null) ?
            'rtcp-fb:%s %s %s':
            'rtcp-fb:%s %s';
        }
      },
      { //a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
        //a=extmap:1/recvonly URI-gps-string
        push: 'ext',
        reg: /^extmap:(\d+)(?:\/(\w+))? (\S*)(?: (\S*))?/,
        names: ['value', 'direction', 'uri', 'config'],
        format: function (o) {
          return 'extmap:%d' + (o.direction ? '/%s' : '%v') + ' %s' + (o.config ? ' %s' : '');
        }
      },
      { //a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:PS1uQCVeeCFCanVmcjkpPywjNWhcYD0mXXtxaVBR|2^20|1:32
        push: 'crypto',
        reg: /^crypto:(\d*) ([\w_]*) (\S*)(?: (\S*))?/,
        names: ['id', 'suite', 'config', 'sessionConfig'],
        format: function (o) {
          return (o.sessionConfig != null) ?
            'crypto:%d %s %s %s':
            'crypto:%d %s %s';
        }
      },
      { //a=setup:actpass
        name: 'setup',
        reg: /^setup:(\w*)/,
        format: 'setup:%s'
      },
      { //a=mid:1
        name: 'mid',
        reg: /^mid:([^\s]*)/,
        format: 'mid:%s'
      },
      { //a=msid:0c8b064d-d807-43b4-b434-f92a889d8587 98178685-d409-46e0-8e16-7ef0db0db64a
        name: 'msid',
        reg: /^msid:(.*)/,
        format: 'msid:%s'
      },
      { //a=ptime:20
        name: 'ptime',
        reg: /^ptime:(\d*)/,
        format: 'ptime:%d'
      },
      { //a=maxptime:60
        name: 'maxptime',
        reg: /^maxptime:(\d*)/,
        format: 'maxptime:%d'
      },
      { //a=sendrecv
        name: 'direction',
        reg: /^(sendrecv|recvonly|sendonly|inactive)/
      },
      { //a=ice-lite
        name: 'icelite',
        reg: /^(ice-lite)/
      },
      { //a=ice-ufrag:F7gI
        name: 'iceUfrag',
        reg: /^ice-ufrag:(\S*)/,
        format: 'ice-ufrag:%s'
      },
      { //a=ice-pwd:x9cml/YzichV2+XlhiMu8g
        name: 'icePwd',
        reg: /^ice-pwd:(\S*)/,
        format: 'ice-pwd:%s'
      },
      { //a=fingerprint:SHA-1 00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33
        name: 'fingerprint',
        reg: /^fingerprint:(\S*) (\S*)/,
        names: ['type', 'hash'],
        format: 'fingerprint:%s %s'
      },
      { //a=candidate:0 1 UDP 2113667327 203.0.113.1 54400 typ host
        //a=candidate:1162875081 1 udp 2113937151 192.168.34.75 60017 typ host generation 0 network-id 3 network-cost 10
        //a=candidate:3289912957 2 udp 1845501695 193.84.77.194 60017 typ srflx raddr 192.168.34.75 rport 60017 generation 0 network-id 3 network-cost 10
        //a=candidate:229815620 1 tcp 1518280447 192.168.150.19 60017 typ host tcptype active generation 0 network-id 3 network-cost 10
        //a=candidate:3289912957 2 tcp 1845501695 193.84.77.194 60017 typ srflx raddr 192.168.34.75 rport 60017 tcptype passive generation 0 network-id 3 network-cost 10
        push:'candidates',
        reg: /^candidate:(\S*) (\d*) (\S*) (\d*) (\S*) (\d*) typ (\S*)(?: raddr (\S*) rport (\d*))?(?: tcptype (\S*))?(?: generation (\d*))?(?: network-id (\d*))?(?: network-cost (\d*))?/,
        names: ['foundation', 'component', 'transport', 'priority', 'ip', 'port', 'type', 'raddr', 'rport', 'tcptype', 'generation', 'network-id', 'network-cost'],
        format: function (o) {
          var str = 'candidate:%s %d %s %d %s %d typ %s';
  
          str += (o.raddr != null) ? ' raddr %s rport %d' : '%v%v';
  
          // NB: candidate has three optional chunks, so %void middles one if it's missing
          str += (o.tcptype != null) ? ' tcptype %s' : '%v';
  
          if (o.generation != null) {
            str += ' generation %d';
          }
  
          str += (o['network-id'] != null) ? ' network-id %d' : '%v';
          str += (o['network-cost'] != null) ? ' network-cost %d' : '%v';
          return str;
        }
      },
      { //a=end-of-candidates (keep after the candidates line for readability)
        name: 'endOfCandidates',
        reg: /^(end-of-candidates)/
      },
      { //a=remote-candidates:1 203.0.113.1 54400 2 203.0.113.1 54401 ...
        name: 'remoteCandidates',
        reg: /^remote-candidates:(.*)/,
        format: 'remote-candidates:%s'
      },
      { //a=ice-options:google-ice
        name: 'iceOptions',
        reg: /^ice-options:(\S*)/,
        format: 'ice-options:%s'
      },
      { //a=ssrc:2566107569 cname:t9YU8M1UxTF8Y1A1
        push: 'ssrcs',
        reg: /^ssrc:(\d*) ([\w_-]*)(?::(.*))?/,
        names: ['id', 'attribute', 'value'],
        format: function (o) {
          var str = 'ssrc:%d';
          if (o.attribute != null) {
            str += ' %s';
            if (o.value != null) {
              str += ':%s';
            }
          }
          return str;
        }
      },
      { //a=ssrc-group:FEC 1 2
        //a=ssrc-group:FEC-FR 3004364195 1080772241
        push: 'ssrcGroups',
        // token-char = %x21 / %x23-27 / %x2A-2B / %x2D-2E / %x30-39 / %x41-5A / %x5E-7E
        reg: /^ssrc-group:([\x21\x23\x24\x25\x26\x27\x2A\x2B\x2D\x2E\w]*) (.*)/,
        names: ['semantics', 'ssrcs'],
        format: 'ssrc-group:%s %s'
      },
      { //a=msid-semantic: WMS Jvlam5X3SX1OP6pn20zWogvaKJz5Hjf9OnlV
        name: 'msidSemantic',
        reg: /^msid-semantic:\s?(\w*) (\S*)/,
        names: ['semantic', 'token'],
        format: 'msid-semantic: %s %s' // space after ':' is not accidental
      },
      { //a=group:BUNDLE audio video
        push: 'groups',
        reg: /^group:(\w*) (.*)/,
        names: ['type', 'mids'],
        format: 'group:%s %s'
      },
      { //a=rtcp-mux
        name: 'rtcpMux',
        reg: /^(rtcp-mux)/
      },
      { //a=rtcp-rsize
        name: 'rtcpRsize',
        reg: /^(rtcp-rsize)/
      },
      { //a=sctpmap:5000 webrtc-datachannel 1024
        name: 'sctpmap',
        reg: /^sctpmap:([\w_\/]*) (\S*)(?: (\S*))?/,
        names: ['sctpmapNumber', 'app', 'maxMessageSize'],
        format: function (o) {
          return (o.maxMessageSize != null) ?
            'sctpmap:%s %s %s' :
            'sctpmap:%s %s';
        }
      },
      { //a=x-google-flag:conference
        name: 'xGoogleFlag',
        reg: /^x-google-flag:([^\s]*)/,
        format: 'x-google-flag:%s'
      },
      { //a=rid:1 send max-width=1280;max-height=720;max-fps=30;depend=0
        push: 'rids',
        reg: /^rid:([\d\w]+) (\w+)(?: ([\S| ]*))?/,
        names: ['id', 'direction', 'params'],
        format: function (o) {
          return (o.params) ? 'rid:%s %s %s' : 'rid:%s %s';
        }
      },
      { //a=imageattr:97 send [x=800,y=640,sar=1.1,q=0.6] [x=480,y=320] recv [x=330,y=250]
        //a=imageattr:* send [x=800,y=640] recv *
        //a=imageattr:100 recv [x=320,y=240]
        push: 'imageattrs',
        reg: new RegExp(
          //a=imageattr:97
          '^imageattr:(\\d+|\\*)' +
          //send [x=800,y=640,sar=1.1,q=0.6] [x=480,y=320]
          '[\\s\\t]+(send|recv)[\\s\\t]+(\\*|\\[\\S+\\](?:[\\s\\t]+\\[\\S+\\])*)' +
          //recv [x=330,y=250]
          '(?:[\\s\\t]+(recv|send)[\\s\\t]+(\\*|\\[\\S+\\](?:[\\s\\t]+\\[\\S+\\])*))?'
        ),
        names: ['pt', 'dir1', 'attrs1', 'dir2', 'attrs2'],
        format: function (o) {
          return 'imageattr:%s %s %s' + (o.dir2 ? ' %s %s' : '');
        }
      },
      { //a=simulcast:send 1,2,3;~4,~5 recv 6;~7,~8
        //a=simulcast:recv 1;4,5 send 6;7
        name: 'simulcast',
        reg: new RegExp(
          //a=simulcast:
          '^simulcast:' +
          //send 1,2,3;~4,~5
          '(send|recv) ([a-zA-Z0-9\\-_~;,]+)' +
          //space + recv 6;~7,~8
          '(?:\\s?(send|recv) ([a-zA-Z0-9\\-_~;,]+))?' +
          //end
          '$'
        ),
        names: ['dir1', 'list1', 'dir2', 'list2'],
        format: function (o) {
          return 'simulcast:%s %s' + (o.dir2 ? ' %s %s' : '');
        }
      },
      { //Old simulcast draft 03 (implemented by Firefox)
        //  https://tools.ietf.org/html/draft-ietf-mmusic-sdp-simulcast-03
        //a=simulcast: recv pt=97;98 send pt=97
        //a=simulcast: send rid=5;6;7 paused=6,7
        name: 'simulcast_03',
        reg: /^simulcast:[\s\t]+([\S+\s\t]+)$/,
        names: ['value'],
        format: 'simulcast: %s'
      },
      {
        //a=framerate:25
        //a=framerate:29.97
        name: 'framerate',
        reg: /^framerate:(\d+(?:$|\.\d+))/,
        format: 'framerate:%s'
      },
      { // RFC4570
        //a=source-filter: incl IN IP4 239.5.2.31 10.1.15.5
        name: 'sourceFilter',
        reg: /^source-filter: *(excl|incl) (\S*) (IP4|IP6|\*) (\S*) (.*)/,
        names: ['filterMode', 'netType', 'addressTypes', 'destAddress', 'srcList'],
        format: 'source-filter: %s %s %s %s %s'
      },
      { //a=bundle-only
        name: 'bundleOnly',
        reg: /^(bundle-only)/
      },
      { //a=label:1
        name: 'label',
        reg: /^label:(.+)/,
        format: 'label:%s'
      },
      {
        // RFC version 26 for SCTP over DTLS
        // https://tools.ietf.org/html/draft-ietf-mmusic-sctp-sdp-26#section-5
        name:'sctpPort',
        reg: /^sctp-port:(\d+)$/,
        format: 'sctp-port:%s'
      },
      {
        // RFC version 26 for SCTP over DTLS
        // https://tools.ietf.org/html/draft-ietf-mmusic-sctp-sdp-26#section-6
        name:'maxMessageSize',
        reg: /^max-message-size:(\d+)$/,
        format: 'max-message-size:%s'
      },
      { // any a= that we don't understand is kepts verbatim on media.invalid
        push: 'invalid',
        names: ['value']
      }
    ]
  };
  
  // set sensible defaults to avoid polluting the grammar with boring details
  Object.keys(grammar).forEach(function (key) {
    var objs = grammar[key];
    objs.forEach(function (obj) {
      if (!obj.reg) {
        obj.reg = /(.*)/;
      }
      if (!obj.format) {
        obj.format = '%s';
      }
    });
  });
  
  
  /***/ }),
  
  /***/ "./node_modules/sdp-transform/lib/index.js":
  /*!*************************************************!*\
    !*** ./node_modules/sdp-transform/lib/index.js ***!
    \*************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  var parser = __webpack_require__(/*! ./parser */ "./node_modules/sdp-transform/lib/parser.js");
  var writer = __webpack_require__(/*! ./writer */ "./node_modules/sdp-transform/lib/writer.js");
  
  exports.write = writer;
  exports.parse = parser.parse;
  exports.parseFmtpConfig = parser.parseFmtpConfig;
  exports.parseParams = parser.parseParams;
  exports.parsePayloads = parser.parsePayloads;
  exports.parseRemoteCandidates = parser.parseRemoteCandidates;
  exports.parseImageAttributes = parser.parseImageAttributes;
  exports.parseSimulcastStreamList = parser.parseSimulcastStreamList;
  
  
  /***/ }),
  
  /***/ "./node_modules/sdp-transform/lib/parser.js":
  /*!**************************************************!*\
    !*** ./node_modules/sdp-transform/lib/parser.js ***!
    \**************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  var toIntIfInt = function (v) {
    return String(Number(v)) === v ? Number(v) : v;
  };
  
  var attachProperties = function (match, location, names, rawName) {
    if (rawName && !names) {
      location[rawName] = toIntIfInt(match[1]);
    }
    else {
      for (var i = 0; i < names.length; i += 1) {
        if (match[i+1] != null) {
          location[names[i]] = toIntIfInt(match[i+1]);
        }
      }
    }
  };
  
  var parseReg = function (obj, location, content) {
    var needsBlank = obj.name && obj.names;
    if (obj.push && !location[obj.push]) {
      location[obj.push] = [];
    }
    else if (needsBlank && !location[obj.name]) {
      location[obj.name] = {};
    }
    var keyLocation = obj.push ?
      {} :  // blank object that will be pushed
      needsBlank ? location[obj.name] : location; // otherwise, named location or root
  
    attachProperties(content.match(obj.reg), keyLocation, obj.names, obj.name);
  
    if (obj.push) {
      location[obj.push].push(keyLocation);
    }
  };
  
  var grammar = __webpack_require__(/*! ./grammar */ "./node_modules/sdp-transform/lib/grammar.js");
  var validLine = RegExp.prototype.test.bind(/^([a-z])=(.*)/);
  
  exports.parse = function (sdp) {
    var session = {}
      , media = []
      , location = session; // points at where properties go under (one of the above)
  
    // parse lines we understand
    sdp.split(/(\r\n|\r|\n)/).filter(validLine).forEach(function (l) {
      var type = l[0];
      var content = l.slice(2);
      if (type === 'm') {
        media.push({rtp: [], fmtp: []});
        location = media[media.length-1]; // point at latest media line
      }
  
      for (var j = 0; j < (grammar[type] || []).length; j += 1) {
        var obj = grammar[type][j];
        if (obj.reg.test(content)) {
          return parseReg(obj, location, content);
        }
      }
    });
  
    session.media = media; // link it up
    return session;
  };
  
  var paramReducer = function (acc, expr) {
    var s = expr.split(/=(.+)/, 2);
    if (s.length === 2) {
      acc[s[0]] = toIntIfInt(s[1]);
    } else if (s.length === 1 && expr.length > 1) {
      acc[s[0]] = undefined;
    }
    return acc;
  };
  
  exports.parseParams = function (str) {
    return str.split(/\;\s?/).reduce(paramReducer, {});
  };
  
  // For backward compatibility - alias will be removed in 3.0.0
  exports.parseFmtpConfig = exports.parseParams;
  
  exports.parsePayloads = function (str) {
    return str.split(' ').map(Number);
  };
  
  exports.parseRemoteCandidates = function (str) {
    var candidates = [];
    var parts = str.split(' ').map(toIntIfInt);
    for (var i = 0; i < parts.length; i += 3) {
      candidates.push({
        component: parts[i],
        ip: parts[i + 1],
        port: parts[i + 2]
      });
    }
    return candidates;
  };
  
  exports.parseImageAttributes = function (str) {
    return str.split(' ').map(function (item) {
      return item.substring(1, item.length-1).split(',').reduce(paramReducer, {});
    });
  };
  
  exports.parseSimulcastStreamList = function (str) {
    return str.split(';').map(function (stream) {
      return stream.split(',').map(function (format) {
        var scid, paused = false;
  
        if (format[0] !== '~') {
          scid = toIntIfInt(format);
        } else {
          scid = toIntIfInt(format.substring(1, format.length));
          paused = true;
        }
  
        return {
          scid: scid,
          paused: paused
        };
      });
    });
  };
  
  
  /***/ }),
  
  /***/ "./node_modules/sdp-transform/lib/writer.js":
  /*!**************************************************!*\
    !*** ./node_modules/sdp-transform/lib/writer.js ***!
    \**************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  var grammar = __webpack_require__(/*! ./grammar */ "./node_modules/sdp-transform/lib/grammar.js");
  
  // customized util.format - discards excess arguments and can void middle ones
  var formatRegExp = /%[sdv%]/g;
  var format = function (formatStr) {
    var i = 1;
    var args = arguments;
    var len = args.length;
    return formatStr.replace(formatRegExp, function (x) {
      if (i >= len) {
        return x; // missing argument
      }
      var arg = args[i];
      i += 1;
      switch (x) {
      case '%%':
        return '%';
      case '%s':
        return String(arg);
      case '%d':
        return Number(arg);
      case '%v':
        return '';
      }
    });
    // NB: we discard excess arguments - they are typically undefined from makeLine
  };
  
  var makeLine = function (type, obj, location) {
    var str = obj.format instanceof Function ?
      (obj.format(obj.push ? location : location[obj.name])) :
      obj.format;
  
    var args = [type + '=' + str];
    if (obj.names) {
      for (var i = 0; i < obj.names.length; i += 1) {
        var n = obj.names[i];
        if (obj.name) {
          args.push(location[obj.name][n]);
        }
        else { // for mLine and push attributes
          args.push(location[obj.names[i]]);
        }
      }
    }
    else {
      args.push(location[obj.name]);
    }
    return format.apply(null, args);
  };
  
  // RFC specified order
  // TODO: extend this with all the rest
  var defaultOuterOrder = [
    'v', 'o', 's', 'i',
    'u', 'e', 'p', 'c',
    'b', 't', 'r', 'z', 'a'
  ];
  var defaultInnerOrder = ['i', 'c', 'b', 'a'];
  
  
  module.exports = function (session, opts) {
    opts = opts || {};
    // ensure certain properties exist
    if (session.version == null) {
      session.version = 0; // 'v=0' must be there (only defined version atm)
    }
    if (session.name == null) {
      session.name = ' '; // 's= ' must be there if no meaningful name set
    }
    session.media.forEach(function (mLine) {
      if (mLine.payloads == null) {
        mLine.payloads = '';
      }
    });
  
    var outerOrder = opts.outerOrder || defaultOuterOrder;
    var innerOrder = opts.innerOrder || defaultInnerOrder;
    var sdp = [];
  
    // loop through outerOrder for matching properties on session
    outerOrder.forEach(function (type) {
      grammar[type].forEach(function (obj) {
        if (obj.name in session && session[obj.name] != null) {
          sdp.push(makeLine(type, obj, session));
        }
        else if (obj.push in session && session[obj.push] != null) {
          session[obj.push].forEach(function (el) {
            sdp.push(makeLine(type, obj, el));
          });
        }
      });
    });
  
    // then for each media line, follow the innerOrder
    session.media.forEach(function (mLine) {
      sdp.push(makeLine('m', grammar.m[0], mLine));
  
      innerOrder.forEach(function (type) {
        grammar[type].forEach(function (obj) {
          if (obj.name in mLine && mLine[obj.name] != null) {
            sdp.push(makeLine(type, obj, mLine));
          }
          else if (obj.push in mLine && mLine[obj.push] != null) {
            mLine[obj.push].forEach(function (el) {
              sdp.push(makeLine(type, obj, el));
            });
          }
        });
      });
    });
  
    return sdp.join('\r\n') + '\r\n';
  };
  
  
  /***/ }),
  
  /***/ "./node_modules/socket.io-client/lib/index.js":
  /*!****************************************************!*\
    !*** ./node_modules/socket.io-client/lib/index.js ***!
    \****************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  
  /**
   * Module dependencies.
   */
  
  var url = __webpack_require__(/*! ./url */ "./node_modules/socket.io-client/lib/url.js");
  var parser = __webpack_require__(/*! socket.io-parser */ "./node_modules/socket.io-client/node_modules/socket.io-parser/index.js");
  var Manager = __webpack_require__(/*! ./manager */ "./node_modules/socket.io-client/lib/manager.js");
  var debug = __webpack_require__(/*! debug */ "./node_modules/socket.io-client/node_modules/debug/src/browser.js")('socket.io-client');
  
  /**
   * Module exports.
   */
  
  module.exports = exports = lookup;
  
  /**
   * Managers cache.
   */
  
  var cache = exports.managers = {};
  
  /**
   * Looks up an existing `Manager` for multiplexing.
   * If the user summons:
   *
   *   `io('http://localhost/a');`
   *   `io('http://localhost/b');`
   *
   * We reuse the existing instance based on same scheme/port/host,
   * and we initialize sockets for each namespace.
   *
   * @api public
   */
  
  function lookup (uri, opts) {
    if (typeof uri === 'object') {
      opts = uri;
      uri = undefined;
    }
  
    opts = opts || {};
  
    var parsed = url(uri);
    var source = parsed.source;
    var id = parsed.id;
    var path = parsed.path;
    var sameNamespace = cache[id] && path in cache[id].nsps;
    var newConnection = opts.forceNew || opts['force new connection'] ||
                        false === opts.multiplex || sameNamespace;
  
    var io;
  
    if (newConnection) {
      debug('ignoring socket cache for %s', source);
      io = Manager(source, opts);
    } else {
      if (!cache[id]) {
        debug('new io instance for %s', source);
        cache[id] = Manager(source, opts);
      }
      io = cache[id];
    }
    if (parsed.query && !opts.query) {
      opts.query = parsed.query;
    }
    return io.socket(parsed.path, opts);
  }
  
  /**
   * Protocol version.
   *
   * @api public
   */
  
  exports.protocol = parser.protocol;
  
  /**
   * `connect`.
   *
   * @param {String} uri
   * @api public
   */
  
  exports.connect = lookup;
  
  /**
   * Expose constructors for standalone build.
   *
   * @api public
   */
  
  exports.Manager = __webpack_require__(/*! ./manager */ "./node_modules/socket.io-client/lib/manager.js");
  exports.Socket = __webpack_require__(/*! ./socket */ "./node_modules/socket.io-client/lib/socket.js");
  
  
  /***/ }),
  
  /***/ "./node_modules/socket.io-client/lib/manager.js":
  /*!******************************************************!*\
    !*** ./node_modules/socket.io-client/lib/manager.js ***!
    \******************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  
  /**
   * Module dependencies.
   */
  
  var eio = __webpack_require__(/*! engine.io-client */ "./node_modules/socket.io-client/node_modules/engine.io-client/lib/index.js");
  var Socket = __webpack_require__(/*! ./socket */ "./node_modules/socket.io-client/lib/socket.js");
  var Emitter = __webpack_require__(/*! component-emitter */ "./node_modules/component-emitter/index.js");
  var parser = __webpack_require__(/*! socket.io-parser */ "./node_modules/socket.io-client/node_modules/socket.io-parser/index.js");
  var on = __webpack_require__(/*! ./on */ "./node_modules/socket.io-client/lib/on.js");
  var bind = __webpack_require__(/*! component-bind */ "./node_modules/component-bind/index.js");
  var debug = __webpack_require__(/*! debug */ "./node_modules/socket.io-client/node_modules/debug/src/browser.js")('socket.io-client:manager');
  var indexOf = __webpack_require__(/*! indexof */ "./node_modules/indexof/index.js");
  var Backoff = __webpack_require__(/*! backo2 */ "./node_modules/backo2/index.js");
  
  /**
   * IE6+ hasOwnProperty
   */
  
  var has = Object.prototype.hasOwnProperty;
  
  /**
   * Module exports
   */
  
  module.exports = Manager;
  
  /**
   * `Manager` constructor.
   *
   * @param {String} engine instance or engine uri/opts
   * @param {Object} options
   * @api public
   */
  
  function Manager (uri, opts) {
    if (!(this instanceof Manager)) return new Manager(uri, opts);
    if (uri && ('object' === typeof uri)) {
      opts = uri;
      uri = undefined;
    }
    opts = opts || {};
  
    opts.path = opts.path || '/socket.io';
    this.nsps = {};
    this.subs = [];
    this.opts = opts;
    this.reconnection(opts.reconnection !== false);
    this.reconnectionAttempts(opts.reconnectionAttempts || Infinity);
    this.reconnectionDelay(opts.reconnectionDelay || 1000);
    this.reconnectionDelayMax(opts.reconnectionDelayMax || 5000);
    this.randomizationFactor(opts.randomizationFactor || 0.5);
    this.backoff = new Backoff({
      min: this.reconnectionDelay(),
      max: this.reconnectionDelayMax(),
      jitter: this.randomizationFactor()
    });
    this.timeout(null == opts.timeout ? 20000 : opts.timeout);
    this.readyState = 'closed';
    this.uri = uri;
    this.connecting = [];
    this.lastPing = null;
    this.encoding = false;
    this.packetBuffer = [];
    var _parser = opts.parser || parser;
    this.encoder = new _parser.Encoder();
    this.decoder = new _parser.Decoder();
    this.autoConnect = opts.autoConnect !== false;
    if (this.autoConnect) this.open();
  }
  
  /**
   * Propagate given event to sockets and emit on `this`
   *
   * @api private
   */
  
  Manager.prototype.emitAll = function () {
    this.emit.apply(this, arguments);
    for (var nsp in this.nsps) {
      if (has.call(this.nsps, nsp)) {
        this.nsps[nsp].emit.apply(this.nsps[nsp], arguments);
      }
    }
  };
  
  /**
   * Update `socket.id` of all sockets
   *
   * @api private
   */
  
  Manager.prototype.updateSocketIds = function () {
    for (var nsp in this.nsps) {
      if (has.call(this.nsps, nsp)) {
        this.nsps[nsp].id = this.generateId(nsp);
      }
    }
  };
  
  /**
   * generate `socket.id` for the given `nsp`
   *
   * @param {String} nsp
   * @return {String}
   * @api private
   */
  
  Manager.prototype.generateId = function (nsp) {
    return (nsp === '/' ? '' : (nsp + '#')) + this.engine.id;
  };
  
  /**
   * Mix in `Emitter`.
   */
  
  Emitter(Manager.prototype);
  
  /**
   * Sets the `reconnection` config.
   *
   * @param {Boolean} true/false if it should automatically reconnect
   * @return {Manager} self or value
   * @api public
   */
  
  Manager.prototype.reconnection = function (v) {
    if (!arguments.length) return this._reconnection;
    this._reconnection = !!v;
    return this;
  };
  
  /**
   * Sets the reconnection attempts config.
   *
   * @param {Number} max reconnection attempts before giving up
   * @return {Manager} self or value
   * @api public
   */
  
  Manager.prototype.reconnectionAttempts = function (v) {
    if (!arguments.length) return this._reconnectionAttempts;
    this._reconnectionAttempts = v;
    return this;
  };
  
  /**
   * Sets the delay between reconnections.
   *
   * @param {Number} delay
   * @return {Manager} self or value
   * @api public
   */
  
  Manager.prototype.reconnectionDelay = function (v) {
    if (!arguments.length) return this._reconnectionDelay;
    this._reconnectionDelay = v;
    this.backoff && this.backoff.setMin(v);
    return this;
  };
  
  Manager.prototype.randomizationFactor = function (v) {
    if (!arguments.length) return this._randomizationFactor;
    this._randomizationFactor = v;
    this.backoff && this.backoff.setJitter(v);
    return this;
  };
  
  /**
   * Sets the maximum delay between reconnections.
   *
   * @param {Number} delay
   * @return {Manager} self or value
   * @api public
   */
  
  Manager.prototype.reconnectionDelayMax = function (v) {
    if (!arguments.length) return this._reconnectionDelayMax;
    this._reconnectionDelayMax = v;
    this.backoff && this.backoff.setMax(v);
    return this;
  };
  
  /**
   * Sets the connection timeout. `false` to disable
   *
   * @return {Manager} self or value
   * @api public
   */
  
  Manager.prototype.timeout = function (v) {
    if (!arguments.length) return this._timeout;
    this._timeout = v;
    return this;
  };
  
  /**
   * Starts trying to reconnect if reconnection is enabled and we have not
   * started reconnecting yet
   *
   * @api private
   */
  
  Manager.prototype.maybeReconnectOnOpen = function () {
    // Only try to reconnect if it's the first time we're connecting
    if (!this.reconnecting && this._reconnection && this.backoff.attempts === 0) {
      // keeps reconnection from firing twice for the same reconnection loop
      this.reconnect();
    }
  };
  
  /**
   * Sets the current transport `socket`.
   *
   * @param {Function} optional, callback
   * @return {Manager} self
   * @api public
   */
  
  Manager.prototype.open =
  Manager.prototype.connect = function (fn, opts) {
    debug('readyState %s', this.readyState);
    if (~this.readyState.indexOf('open')) return this;
  
    debug('opening %s', this.uri);
    this.engine = eio(this.uri, this.opts);
    var socket = this.engine;
    var self = this;
    this.readyState = 'opening';
    this.skipReconnect = false;
  
    // emit `open`
    var openSub = on(socket, 'open', function () {
      self.onopen();
      fn && fn();
    });
  
    // emit `connect_error`
    var errorSub = on(socket, 'error', function (data) {
      debug('connect_error');
      self.cleanup();
      self.readyState = 'closed';
      self.emitAll('connect_error', data);
      if (fn) {
        var err = new Error('Connection error');
        err.data = data;
        fn(err);
      } else {
        // Only do this if there is no fn to handle the error
        self.maybeReconnectOnOpen();
      }
    });
  
    // emit `connect_timeout`
    if (false !== this._timeout) {
      var timeout = this._timeout;
      debug('connect attempt will timeout after %d', timeout);
  
      // set timer
      var timer = setTimeout(function () {
        debug('connect attempt timed out after %d', timeout);
        openSub.destroy();
        socket.close();
        socket.emit('error', 'timeout');
        self.emitAll('connect_timeout', timeout);
      }, timeout);
  
      this.subs.push({
        destroy: function () {
          clearTimeout(timer);
        }
      });
    }
  
    this.subs.push(openSub);
    this.subs.push(errorSub);
  
    return this;
  };
  
  /**
   * Called upon transport open.
   *
   * @api private
   */
  
  Manager.prototype.onopen = function () {
    debug('open');
  
    // clear old subs
    this.cleanup();
  
    // mark as open
    this.readyState = 'open';
    this.emit('open');
  
    // add new subs
    var socket = this.engine;
    this.subs.push(on(socket, 'data', bind(this, 'ondata')));
    this.subs.push(on(socket, 'ping', bind(this, 'onping')));
    this.subs.push(on(socket, 'pong', bind(this, 'onpong')));
    this.subs.push(on(socket, 'error', bind(this, 'onerror')));
    this.subs.push(on(socket, 'close', bind(this, 'onclose')));
    this.subs.push(on(this.decoder, 'decoded', bind(this, 'ondecoded')));
  };
  
  /**
   * Called upon a ping.
   *
   * @api private
   */
  
  Manager.prototype.onping = function () {
    this.lastPing = new Date();
    this.emitAll('ping');
  };
  
  /**
   * Called upon a packet.
   *
   * @api private
   */
  
  Manager.prototype.onpong = function () {
    this.emitAll('pong', new Date() - this.lastPing);
  };
  
  /**
   * Called with data.
   *
   * @api private
   */
  
  Manager.prototype.ondata = function (data) {
    this.decoder.add(data);
  };
  
  /**
   * Called when parser fully decodes a packet.
   *
   * @api private
   */
  
  Manager.prototype.ondecoded = function (packet) {
    this.emit('packet', packet);
  };
  
  /**
   * Called upon socket error.
   *
   * @api private
   */
  
  Manager.prototype.onerror = function (err) {
    debug('error', err);
    this.emitAll('error', err);
  };
  
  /**
   * Creates a new socket for the given `nsp`.
   *
   * @return {Socket}
   * @api public
   */
  
  Manager.prototype.socket = function (nsp, opts) {
    var socket = this.nsps[nsp];
    if (!socket) {
      socket = new Socket(this, nsp, opts);
      this.nsps[nsp] = socket;
      var self = this;
      socket.on('connecting', onConnecting);
      socket.on('connect', function () {
        socket.id = self.generateId(nsp);
      });
  
      if (this.autoConnect) {
        // manually call here since connecting event is fired before listening
        onConnecting();
      }
    }
  
    function onConnecting () {
      if (!~indexOf(self.connecting, socket)) {
        self.connecting.push(socket);
      }
    }
  
    return socket;
  };
  
  /**
   * Called upon a socket close.
   *
   * @param {Socket} socket
   */
  
  Manager.prototype.destroy = function (socket) {
    var index = indexOf(this.connecting, socket);
    if (~index) this.connecting.splice(index, 1);
    if (this.connecting.length) return;
  
    this.close();
  };
  
  /**
   * Writes a packet.
   *
   * @param {Object} packet
   * @api private
   */
  
  Manager.prototype.packet = function (packet) {
    debug('writing packet %j', packet);
    var self = this;
    if (packet.query && packet.type === 0) packet.nsp += '?' + packet.query;
  
    if (!self.encoding) {
      // encode, then write to engine with result
      self.encoding = true;
      this.encoder.encode(packet, function (encodedPackets) {
        for (var i = 0; i < encodedPackets.length; i++) {
          self.engine.write(encodedPackets[i], packet.options);
        }
        self.encoding = false;
        self.processPacketQueue();
      });
    } else { // add packet to the queue
      self.packetBuffer.push(packet);
    }
  };
  
  /**
   * If packet buffer is non-empty, begins encoding the
   * next packet in line.
   *
   * @api private
   */
  
  Manager.prototype.processPacketQueue = function () {
    if (this.packetBuffer.length > 0 && !this.encoding) {
      var pack = this.packetBuffer.shift();
      this.packet(pack);
    }
  };
  
  /**
   * Clean up transport subscriptions and packet buffer.
   *
   * @api private
   */
  
  Manager.prototype.cleanup = function () {
    debug('cleanup');
  
    var subsLength = this.subs.length;
    for (var i = 0; i < subsLength; i++) {
      var sub = this.subs.shift();
      sub.destroy();
    }
  
    this.packetBuffer = [];
    this.encoding = false;
    this.lastPing = null;
  
    this.decoder.destroy();
  };
  
  /**
   * Close the current socket.
   *
   * @api private
   */
  
  Manager.prototype.close =
  Manager.prototype.disconnect = function () {
    debug('disconnect');
    this.skipReconnect = true;
    this.reconnecting = false;
    if ('opening' === this.readyState) {
      // `onclose` will not fire because
      // an open event never happened
      this.cleanup();
    }
    this.backoff.reset();
    this.readyState = 'closed';
    if (this.engine) this.engine.close();
  };
  
  /**
   * Called upon engine close.
   *
   * @api private
   */
  
  Manager.prototype.onclose = function (reason) {
    debug('onclose');
  
    this.cleanup();
    this.backoff.reset();
    this.readyState = 'closed';
    this.emit('close', reason);
  
    if (this._reconnection && !this.skipReconnect) {
      this.reconnect();
    }
  };
  
  /**
   * Attempt a reconnection.
   *
   * @api private
   */
  
  Manager.prototype.reconnect = function () {
    if (this.reconnecting || this.skipReconnect) return this;
  
    var self = this;
  
    if (this.backoff.attempts >= this._reconnectionAttempts) {
      debug('reconnect failed');
      this.backoff.reset();
      this.emitAll('reconnect_failed');
      this.reconnecting = false;
    } else {
      var delay = this.backoff.duration();
      debug('will wait %dms before reconnect attempt', delay);
  
      this.reconnecting = true;
      var timer = setTimeout(function () {
        if (self.skipReconnect) return;
  
        debug('attempting reconnect');
        self.emitAll('reconnect_attempt', self.backoff.attempts);
        self.emitAll('reconnecting', self.backoff.attempts);
  
        // check again for the case socket closed in above events
        if (self.skipReconnect) return;
  
        self.open(function (err) {
          if (err) {
            debug('reconnect attempt error');
            self.reconnecting = false;
            self.reconnect();
            self.emitAll('reconnect_error', err.data);
          } else {
            debug('reconnect success');
            self.onreconnect();
          }
        });
      }, delay);
  
      this.subs.push({
        destroy: function () {
          clearTimeout(timer);
        }
      });
    }
  };
  
  /**
   * Called upon successful reconnect.
   *
   * @api private
   */
  
  Manager.prototype.onreconnect = function () {
    var attempt = this.backoff.attempts;
    this.reconnecting = false;
    this.backoff.reset();
    this.updateSocketIds();
    this.emitAll('reconnect', attempt);
  };
  
  
  /***/ }),
  
  /***/ "./node_modules/socket.io-client/lib/on.js":
  /*!*************************************************!*\
    !*** ./node_modules/socket.io-client/lib/on.js ***!
    \*************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports) {
  
  
  /**
   * Module exports.
   */
  
  module.exports = on;
  
  /**
   * Helper for subscriptions.
   *
   * @param {Object|EventEmitter} obj with `Emitter` mixin or `EventEmitter`
   * @param {String} event name
   * @param {Function} callback
   * @api public
   */
  
  function on (obj, ev, fn) {
    obj.on(ev, fn);
    return {
      destroy: function () {
        obj.removeListener(ev, fn);
      }
    };
  }
  
  
  /***/ }),
  
  /***/ "./node_modules/socket.io-client/lib/socket.js":
  /*!*****************************************************!*\
    !*** ./node_modules/socket.io-client/lib/socket.js ***!
    \*****************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  
  /**
   * Module dependencies.
   */
  
  var parser = __webpack_require__(/*! socket.io-parser */ "./node_modules/socket.io-client/node_modules/socket.io-parser/index.js");
  var Emitter = __webpack_require__(/*! component-emitter */ "./node_modules/component-emitter/index.js");
  var toArray = __webpack_require__(/*! to-array */ "./node_modules/to-array/index.js");
  var on = __webpack_require__(/*! ./on */ "./node_modules/socket.io-client/lib/on.js");
  var bind = __webpack_require__(/*! component-bind */ "./node_modules/component-bind/index.js");
  var debug = __webpack_require__(/*! debug */ "./node_modules/socket.io-client/node_modules/debug/src/browser.js")('socket.io-client:socket');
  var parseqs = __webpack_require__(/*! parseqs */ "./node_modules/parseqs/index.js");
  var hasBin = __webpack_require__(/*! has-binary2 */ "./node_modules/has-binary2/index.js");
  
  /**
   * Module exports.
   */
  
  module.exports = exports = Socket;
  
  /**
   * Internal events (blacklisted).
   * These events can't be emitted by the user.
   *
   * @api private
   */
  
  var events = {
    connect: 1,
    connect_error: 1,
    connect_timeout: 1,
    connecting: 1,
    disconnect: 1,
    error: 1,
    reconnect: 1,
    reconnect_attempt: 1,
    reconnect_failed: 1,
    reconnect_error: 1,
    reconnecting: 1,
    ping: 1,
    pong: 1
  };
  
  /**
   * Shortcut to `Emitter#emit`.
   */
  
  var emit = Emitter.prototype.emit;
  
  /**
   * `Socket` constructor.
   *
   * @api public
   */
  
  function Socket (io, nsp, opts) {
    this.io = io;
    this.nsp = nsp;
    this.json = this; // compat
    this.ids = 0;
    this.acks = {};
    this.receiveBuffer = [];
    this.sendBuffer = [];
    this.connected = false;
    this.disconnected = true;
    this.flags = {};
    if (opts && opts.query) {
      this.query = opts.query;
    }
    if (this.io.autoConnect) this.open();
  }
  
  /**
   * Mix in `Emitter`.
   */
  
  Emitter(Socket.prototype);
  
  /**
   * Subscribe to open, close and packet events
   *
   * @api private
   */
  
  Socket.prototype.subEvents = function () {
    if (this.subs) return;
  
    var io = this.io;
    this.subs = [
      on(io, 'open', bind(this, 'onopen')),
      on(io, 'packet', bind(this, 'onpacket')),
      on(io, 'close', bind(this, 'onclose'))
    ];
  };
  
  /**
   * "Opens" the socket.
   *
   * @api public
   */
  
  Socket.prototype.open =
  Socket.prototype.connect = function () {
    if (this.connected) return this;
  
    this.subEvents();
    this.io.open(); // ensure open
    if ('open' === this.io.readyState) this.onopen();
    this.emit('connecting');
    return this;
  };
  
  /**
   * Sends a `message` event.
   *
   * @return {Socket} self
   * @api public
   */
  
  Socket.prototype.send = function () {
    var args = toArray(arguments);
    args.unshift('message');
    this.emit.apply(this, args);
    return this;
  };
  
  /**
   * Override `emit`.
   * If the event is in `events`, it's emitted normally.
   *
   * @param {String} event name
   * @return {Socket} self
   * @api public
   */
  
  Socket.prototype.emit = function (ev) {
    if (events.hasOwnProperty(ev)) {
      emit.apply(this, arguments);
      return this;
    }
  
    var args = toArray(arguments);
    var packet = {
      type: (this.flags.binary !== undefined ? this.flags.binary : hasBin(args)) ? parser.BINARY_EVENT : parser.EVENT,
      data: args
    };
  
    packet.options = {};
    packet.options.compress = !this.flags || false !== this.flags.compress;
  
    // event ack callback
    if ('function' === typeof args[args.length - 1]) {
      debug('emitting packet with ack id %d', this.ids);
      this.acks[this.ids] = args.pop();
      packet.id = this.ids++;
    }
  
    if (this.connected) {
      this.packet(packet);
    } else {
      this.sendBuffer.push(packet);
    }
  
    this.flags = {};
  
    return this;
  };
  
  /**
   * Sends a packet.
   *
   * @param {Object} packet
   * @api private
   */
  
  Socket.prototype.packet = function (packet) {
    packet.nsp = this.nsp;
    this.io.packet(packet);
  };
  
  /**
   * Called upon engine `open`.
   *
   * @api private
   */
  
  Socket.prototype.onopen = function () {
    debug('transport is open - connecting');
  
    // write connect packet if necessary
    if ('/' !== this.nsp) {
      if (this.query) {
        var query = typeof this.query === 'object' ? parseqs.encode(this.query) : this.query;
        debug('sending connect packet with query %s', query);
        this.packet({type: parser.CONNECT, query: query});
      } else {
        this.packet({type: parser.CONNECT});
      }
    }
  };
  
  /**
   * Called upon engine `close`.
   *
   * @param {String} reason
   * @api private
   */
  
  Socket.prototype.onclose = function (reason) {
    debug('close (%s)', reason);
    this.connected = false;
    this.disconnected = true;
    delete this.id;
    this.emit('disconnect', reason);
  };
  
  /**
   * Called with socket packet.
   *
   * @param {Object} packet
   * @api private
   */
  
  Socket.prototype.onpacket = function (packet) {
    var sameNamespace = packet.nsp === this.nsp;
    var rootNamespaceError = packet.type === parser.ERROR && packet.nsp === '/';
  
    if (!sameNamespace && !rootNamespaceError) return;
  
    switch (packet.type) {
      case parser.CONNECT:
        this.onconnect();
        break;
  
      case parser.EVENT:
        this.onevent(packet);
        break;
  
      case parser.BINARY_EVENT:
        this.onevent(packet);
        break;
  
      case parser.ACK:
        this.onack(packet);
        break;
  
      case parser.BINARY_ACK:
        this.onack(packet);
        break;
  
      case parser.DISCONNECT:
        this.ondisconnect();
        break;
  
      case parser.ERROR:
        this.emit('error', packet.data);
        break;
    }
  };
  
  /**
   * Called upon a server event.
   *
   * @param {Object} packet
   * @api private
   */
  
  Socket.prototype.onevent = function (packet) {
    var args = packet.data || [];
    debug('emitting event %j', args);
  
    if (null != packet.id) {
      debug('attaching ack callback to event');
      args.push(this.ack(packet.id));
    }
  
    if (this.connected) {
      emit.apply(this, args);
    } else {
      this.receiveBuffer.push(args);
    }
  };
  
  /**
   * Produces an ack callback to emit with an event.
   *
   * @api private
   */
  
  Socket.prototype.ack = function (id) {
    var self = this;
    var sent = false;
    return function () {
      // prevent double callbacks
      if (sent) return;
      sent = true;
      var args = toArray(arguments);
      debug('sending ack %j', args);
  
      self.packet({
        type: hasBin(args) ? parser.BINARY_ACK : parser.ACK,
        id: id,
        data: args
      });
    };
  };
  
  /**
   * Called upon a server acknowlegement.
   *
   * @param {Object} packet
   * @api private
   */
  
  Socket.prototype.onack = function (packet) {
    var ack = this.acks[packet.id];
    if ('function' === typeof ack) {
      debug('calling ack %s with %j', packet.id, packet.data);
      ack.apply(this, packet.data);
      delete this.acks[packet.id];
    } else {
      debug('bad ack %s', packet.id);
    }
  };
  
  /**
   * Called upon server connect.
   *
   * @api private
   */
  
  Socket.prototype.onconnect = function () {
    this.connected = true;
    this.disconnected = false;
    this.emit('connect');
    this.emitBuffered();
  };
  
  /**
   * Emit buffered events (received and emitted).
   *
   * @api private
   */
  
  Socket.prototype.emitBuffered = function () {
    var i;
    for (i = 0; i < this.receiveBuffer.length; i++) {
      emit.apply(this, this.receiveBuffer[i]);
    }
    this.receiveBuffer = [];
  
    for (i = 0; i < this.sendBuffer.length; i++) {
      this.packet(this.sendBuffer[i]);
    }
    this.sendBuffer = [];
  };
  
  /**
   * Called upon server disconnect.
   *
   * @api private
   */
  
  Socket.prototype.ondisconnect = function () {
    debug('server disconnect (%s)', this.nsp);
    this.destroy();
    this.onclose('io server disconnect');
  };
  
  /**
   * Called upon forced client/server side disconnections,
   * this method ensures the manager stops tracking us and
   * that reconnections don't get triggered for this.
   *
   * @api private.
   */
  
  Socket.prototype.destroy = function () {
    if (this.subs) {
      // clean subscriptions to avoid reconnections
      for (var i = 0; i < this.subs.length; i++) {
        this.subs[i].destroy();
      }
      this.subs = null;
    }
  
    this.io.destroy(this);
  };
  
  /**
   * Disconnects the socket manually.
   *
   * @return {Socket} self
   * @api public
   */
  
  Socket.prototype.close =
  Socket.prototype.disconnect = function () {
    if (this.connected) {
      debug('performing disconnect (%s)', this.nsp);
      this.packet({ type: parser.DISCONNECT });
    }
  
    // remove socket from pool
    this.destroy();
  
    if (this.connected) {
      // fire events
      this.onclose('io client disconnect');
    }
    return this;
  };
  
  /**
   * Sets the compress flag.
   *
   * @param {Boolean} if `true`, compresses the sending data
   * @return {Socket} self
   * @api public
   */
  
  Socket.prototype.compress = function (compress) {
    this.flags.compress = compress;
    return this;
  };
  
  /**
   * Sets the binary flag
   *
   * @param {Boolean} whether the emitted data contains binary
   * @return {Socket} self
   * @api public
   */
  
  Socket.prototype.binary = function (binary) {
    this.flags.binary = binary;
    return this;
  };
  
  
  /***/ }),
  
  /***/ "./node_modules/socket.io-client/lib/url.js":
  /*!**************************************************!*\
    !*** ./node_modules/socket.io-client/lib/url.js ***!
    \**************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  
  /**
   * Module dependencies.
   */
  
  var parseuri = __webpack_require__(/*! parseuri */ "./node_modules/parseuri/index.js");
  var debug = __webpack_require__(/*! debug */ "./node_modules/socket.io-client/node_modules/debug/src/browser.js")('socket.io-client:url');
  
  /**
   * Module exports.
   */
  
  module.exports = url;
  
  /**
   * URL parser.
   *
   * @param {String} url
   * @param {Object} An object meant to mimic window.location.
   *                 Defaults to window.location.
   * @api public
   */
  
  function url (uri, loc) {
    var obj = uri;
  
    // default to window.location
    loc = loc || (typeof location !== 'undefined' && location);
    if (null == uri) uri = loc.protocol + '//' + loc.host;
  
    // relative path support
    if ('string' === typeof uri) {
      if ('/' === uri.charAt(0)) {
        if ('/' === uri.charAt(1)) {
          uri = loc.protocol + uri;
        } else {
          uri = loc.host + uri;
        }
      }
  
      if (!/^(https?|wss?):\/\//.test(uri)) {
        debug('protocol-less url %s', uri);
        if ('undefined' !== typeof loc) {
          uri = loc.protocol + '//' + uri;
        } else {
          uri = 'https://' + uri;
        }
      }
  
      // parse
      debug('parse %s', uri);
      obj = parseuri(uri);
    }
  
    // make sure we treat `localhost:80` and `localhost` equally
    if (!obj.port) {
      if (/^(http|ws)$/.test(obj.protocol)) {
        obj.port = '80';
      } else if (/^(http|ws)s$/.test(obj.protocol)) {
        obj.port = '443';
      }
    }
  
    obj.path = obj.path || '/';
  
    var ipv6 = obj.host.indexOf(':') !== -1;
    var host = ipv6 ? '[' + obj.host + ']' : obj.host;
  
    // define unique id
    obj.id = obj.protocol + '://' + host + ':' + obj.port;
    // define href
    obj.href = obj.protocol + '://' + host + (loc && loc.port === obj.port ? '' : (':' + obj.port));
  
    return obj;
  }
  
  
  /***/ }),
  
  /***/ "./node_modules/socket.io-client/node_modules/debug/src/browser.js":
  /*!*************************************************************************!*\
    !*** ./node_modules/socket.io-client/node_modules/debug/src/browser.js ***!
    \*************************************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  /* WEBPACK VAR INJECTION */(function(process) {/**
   * This is the web browser implementation of `debug()`.
   *
   * Expose `debug()` as the module.
   */
  
  exports = module.exports = __webpack_require__(/*! ./debug */ "./node_modules/socket.io-client/node_modules/debug/src/debug.js");
  exports.log = log;
  exports.formatArgs = formatArgs;
  exports.save = save;
  exports.load = load;
  exports.useColors = useColors;
  exports.storage = 'undefined' != typeof chrome
                 && 'undefined' != typeof chrome.storage
                    ? chrome.storage.local
                    : localstorage();
  
  /**
   * Colors.
   */
  
  exports.colors = [
    '#0000CC', '#0000FF', '#0033CC', '#0033FF', '#0066CC', '#0066FF', '#0099CC',
    '#0099FF', '#00CC00', '#00CC33', '#00CC66', '#00CC99', '#00CCCC', '#00CCFF',
    '#3300CC', '#3300FF', '#3333CC', '#3333FF', '#3366CC', '#3366FF', '#3399CC',
    '#3399FF', '#33CC00', '#33CC33', '#33CC66', '#33CC99', '#33CCCC', '#33CCFF',
    '#6600CC', '#6600FF', '#6633CC', '#6633FF', '#66CC00', '#66CC33', '#9900CC',
    '#9900FF', '#9933CC', '#9933FF', '#99CC00', '#99CC33', '#CC0000', '#CC0033',
    '#CC0066', '#CC0099', '#CC00CC', '#CC00FF', '#CC3300', '#CC3333', '#CC3366',
    '#CC3399', '#CC33CC', '#CC33FF', '#CC6600', '#CC6633', '#CC9900', '#CC9933',
    '#CCCC00', '#CCCC33', '#FF0000', '#FF0033', '#FF0066', '#FF0099', '#FF00CC',
    '#FF00FF', '#FF3300', '#FF3333', '#FF3366', '#FF3399', '#FF33CC', '#FF33FF',
    '#FF6600', '#FF6633', '#FF9900', '#FF9933', '#FFCC00', '#FFCC33'
  ];
  
  /**
   * Currently only WebKit-based Web Inspectors, Firefox >= v31,
   * and the Firebug extension (any Firefox version) are known
   * to support "%c" CSS customizations.
   *
   * TODO: add a `localStorage` variable to explicitly enable/disable colors
   */
  
  function useColors() {
    // NB: In an Electron preload script, document will be defined but not fully
    // initialized. Since we know we're in Chrome, we'll just detect this case
    // explicitly
    if (typeof window !== 'undefined' && window.process && window.process.type === 'renderer') {
      return true;
    }
  
    // Internet Explorer and Edge do not support colors.
    if (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) {
      return false;
    }
  
    // is webkit? http://stackoverflow.com/a/16459606/376773
    // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632
    return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) ||
      // is firebug? http://stackoverflow.com/a/398120/376773
      (typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) ||
      // is firefox >= v31?
      // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
      (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) ||
      // double check webkit in userAgent just in case we are in a worker
      (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/));
  }
  
  /**
   * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.
   */
  
  exports.formatters.j = function(v) {
    try {
      return JSON.stringify(v);
    } catch (err) {
      return '[UnexpectedJSONParseError]: ' + err.message;
    }
  };
  
  
  /**
   * Colorize log arguments if enabled.
   *
   * @api public
   */
  
  function formatArgs(args) {
    var useColors = this.useColors;
  
    args[0] = (useColors ? '%c' : '')
      + this.namespace
      + (useColors ? ' %c' : ' ')
      + args[0]
      + (useColors ? '%c ' : ' ')
      + '+' + exports.humanize(this.diff);
  
    if (!useColors) return;
  
    var c = 'color: ' + this.color;
    args.splice(1, 0, c, 'color: inherit')
  
    // the final "%c" is somewhat tricky, because there could be other
    // arguments passed either before or after the %c, so we need to
    // figure out the correct index to insert the CSS into
    var index = 0;
    var lastC = 0;
    args[0].replace(/%[a-zA-Z%]/g, function(match) {
      if ('%%' === match) return;
      index++;
      if ('%c' === match) {
        // we only are interested in the *last* %c
        // (the user may have provided their own)
        lastC = index;
      }
    });
  
    args.splice(lastC, 0, c);
  }
  
  /**
   * Invokes `console.log()` when available.
   * No-op when `console.log` is not a "function".
   *
   * @api public
   */
  
  function log() {
    // this hackery is required for IE8/9, where
    // the `console.log` function doesn't have 'apply'
    return 'object' === typeof console
      && console.log
      && Function.prototype.apply.call(console.log, console, arguments);
  }
  
  /**
   * Save `namespaces`.
   *
   * @param {String} namespaces
   * @api private
   */
  
  function save(namespaces) {
    try {
      if (null == namespaces) {
        exports.storage.removeItem('debug');
      } else {
        exports.storage.debug = namespaces;
      }
    } catch(e) {}
  }
  
  /**
   * Load `namespaces`.
   *
   * @return {String} returns the previously persisted debug modes
   * @api private
   */
  
  function load() {
    var r;
    try {
      r = exports.storage.debug;
    } catch(e) {}
  
    // If debug isn't set in LS, and we're in Electron, try to load $DEBUG
    if (!r && typeof process !== 'undefined' && 'env' in process) {
      r = process.env.DEBUG;
    }
  
    return r;
  }
  
  /**
   * Enable namespaces listed in `localStorage.debug` initially.
   */
  
  exports.enable(load());
  
  /**
   * Localstorage attempts to return the localstorage.
   *
   * This is necessary because safari throws
   * when a user disables cookies/localstorage
   * and you attempt to access it.
   *
   * @return {LocalStorage}
   * @api private
   */
  
  function localstorage() {
    try {
      return window.localStorage;
    } catch (e) {}
  }
  
  /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../../../process/browser.js */ "./node_modules/process/browser.js")))
  
  /***/ }),
  
  /***/ "./node_modules/socket.io-client/node_modules/debug/src/debug.js":
  /*!***********************************************************************!*\
    !*** ./node_modules/socket.io-client/node_modules/debug/src/debug.js ***!
    \***********************************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  
  /**
   * This is the common logic for both the Node.js and web browser
   * implementations of `debug()`.
   *
   * Expose `debug()` as the module.
   */
  
  exports = module.exports = createDebug.debug = createDebug['default'] = createDebug;
  exports.coerce = coerce;
  exports.disable = disable;
  exports.enable = enable;
  exports.enabled = enabled;
  exports.humanize = __webpack_require__(/*! ms */ "./node_modules/ms/index.js");
  
  /**
   * Active `debug` instances.
   */
  exports.instances = [];
  
  /**
   * The currently active debug mode names, and names to skip.
   */
  
  exports.names = [];
  exports.skips = [];
  
  /**
   * Map of special "%n" handling functions, for the debug "format" argument.
   *
   * Valid key names are a single, lower or upper-case letter, i.e. "n" and "N".
   */
  
  exports.formatters = {};
  
  /**
   * Select a color.
   * @param {String} namespace
   * @return {Number}
   * @api private
   */
  
  function selectColor(namespace) {
    var hash = 0, i;
  
    for (i in namespace) {
      hash  = ((hash << 5) - hash) + namespace.charCodeAt(i);
      hash |= 0; // Convert to 32bit integer
    }
  
    return exports.colors[Math.abs(hash) % exports.colors.length];
  }
  
  /**
   * Create a debugger with the given `namespace`.
   *
   * @param {String} namespace
   * @return {Function}
   * @api public
   */
  
  function createDebug(namespace) {
  
    var prevTime;
  
    function debug() {
      // disabled?
      if (!debug.enabled) return;
  
      var self = debug;
  
      // set `diff` timestamp
      var curr = +new Date();
      var ms = curr - (prevTime || curr);
      self.diff = ms;
      self.prev = prevTime;
      self.curr = curr;
      prevTime = curr;
  
      // turn the `arguments` into a proper Array
      var args = new Array(arguments.length);
      for (var i = 0; i < args.length; i++) {
        args[i] = arguments[i];
      }
  
      args[0] = exports.coerce(args[0]);
  
      if ('string' !== typeof args[0]) {
        // anything else let's inspect with %O
        args.unshift('%O');
      }
  
      // apply any `formatters` transformations
      var index = 0;
      args[0] = args[0].replace(/%([a-zA-Z%])/g, function(match, format) {
        // if we encounter an escaped % then don't increase the array index
        if (match === '%%') return match;
        index++;
        var formatter = exports.formatters[format];
        if ('function' === typeof formatter) {
          var val = args[index];
          match = formatter.call(self, val);
  
          // now we need to remove `args[index]` since it's inlined in the `format`
          args.splice(index, 1);
          index--;
        }
        return match;
      });
  
      // apply env-specific formatting (colors, etc.)
      exports.formatArgs.call(self, args);
  
      var logFn = debug.log || exports.log || console.log.bind(console);
      logFn.apply(self, args);
    }
  
    debug.namespace = namespace;
    debug.enabled = exports.enabled(namespace);
    debug.useColors = exports.useColors();
    debug.color = selectColor(namespace);
    debug.destroy = destroy;
  
    // env-specific initialization logic for debug instances
    if ('function' === typeof exports.init) {
      exports.init(debug);
    }
  
    exports.instances.push(debug);
  
    return debug;
  }
  
  function destroy () {
    var index = exports.instances.indexOf(this);
    if (index !== -1) {
      exports.instances.splice(index, 1);
      return true;
    } else {
      return false;
    }
  }
  
  /**
   * Enables a debug mode by namespaces. This can include modes
   * separated by a colon and wildcards.
   *
   * @param {String} namespaces
   * @api public
   */
  
  function enable(namespaces) {
    exports.save(namespaces);
  
    exports.names = [];
    exports.skips = [];
  
    var i;
    var split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/);
    var len = split.length;
  
    for (i = 0; i < len; i++) {
      if (!split[i]) continue; // ignore empty strings
      namespaces = split[i].replace(/\*/g, '.*?');
      if (namespaces[0] === '-') {
        exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$'));
      } else {
        exports.names.push(new RegExp('^' + namespaces + '$'));
      }
    }
  
    for (i = 0; i < exports.instances.length; i++) {
      var instance = exports.instances[i];
      instance.enabled = exports.enabled(instance.namespace);
    }
  }
  
  /**
   * Disable debug output.
   *
   * @api public
   */
  
  function disable() {
    exports.enable('');
  }
  
  /**
   * Returns true if the given mode name is enabled, false otherwise.
   *
   * @param {String} name
   * @return {Boolean}
   * @api public
   */
  
  function enabled(name) {
    if (name[name.length - 1] === '*') {
      return true;
    }
    var i, len;
    for (i = 0, len = exports.skips.length; i < len; i++) {
      if (exports.skips[i].test(name)) {
        return false;
      }
    }
    for (i = 0, len = exports.names.length; i < len; i++) {
      if (exports.names[i].test(name)) {
        return true;
      }
    }
    return false;
  }
  
  /**
   * Coerce `val`.
   *
   * @param {Mixed} val
   * @return {Mixed}
   * @api private
   */
  
  function coerce(val) {
    if (val instanceof Error) return val.stack || val.message;
    return val;
  }
  
  
  /***/ }),
  
  /***/ "./node_modules/socket.io-client/node_modules/engine.io-client/lib/index.js":
  /*!**********************************************************************************!*\
    !*** ./node_modules/socket.io-client/node_modules/engine.io-client/lib/index.js ***!
    \**********************************************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  
  module.exports = __webpack_require__(/*! ./socket */ "./node_modules/socket.io-client/node_modules/engine.io-client/lib/socket.js");
  
  /**
   * Exports parser
   *
   * @api public
   *
   */
  module.exports.parser = __webpack_require__(/*! engine.io-parser */ "./node_modules/engine.io-parser/lib/browser.js");
  
  
  /***/ }),
  
  /***/ "./node_modules/socket.io-client/node_modules/engine.io-client/lib/socket.js":
  /*!***********************************************************************************!*\
    !*** ./node_modules/socket.io-client/node_modules/engine.io-client/lib/socket.js ***!
    \***********************************************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  /**
   * Module dependencies.
   */
  
  var transports = __webpack_require__(/*! ./transports/index */ "./node_modules/socket.io-client/node_modules/engine.io-client/lib/transports/index.js");
  var Emitter = __webpack_require__(/*! component-emitter */ "./node_modules/component-emitter/index.js");
  var debug = __webpack_require__(/*! debug */ "./node_modules/socket.io-client/node_modules/debug/src/browser.js")('engine.io-client:socket');
  var index = __webpack_require__(/*! indexof */ "./node_modules/indexof/index.js");
  var parser = __webpack_require__(/*! engine.io-parser */ "./node_modules/engine.io-parser/lib/browser.js");
  var parseuri = __webpack_require__(/*! parseuri */ "./node_modules/parseuri/index.js");
  var parseqs = __webpack_require__(/*! parseqs */ "./node_modules/parseqs/index.js");
  
  /**
   * Module exports.
   */
  
  module.exports = Socket;
  
  /**
   * Socket constructor.
   *
   * @param {String|Object} uri or options
   * @param {Object} options
   * @api public
   */
  
  function Socket (uri, opts) {
    if (!(this instanceof Socket)) return new Socket(uri, opts);
  
    opts = opts || {};
  
    if (uri && 'object' === typeof uri) {
      opts = uri;
      uri = null;
    }
  
    if (uri) {
      uri = parseuri(uri);
      opts.hostname = uri.host;
      opts.secure = uri.protocol === 'https' || uri.protocol === 'wss';
      opts.port = uri.port;
      if (uri.query) opts.query = uri.query;
    } else if (opts.host) {
      opts.hostname = parseuri(opts.host).host;
    }
  
    this.secure = null != opts.secure ? opts.secure
      : (typeof location !== 'undefined' && 'https:' === location.protocol);
  
    if (opts.hostname && !opts.port) {
      // if no port is specified manually, use the protocol default
      opts.port = this.secure ? '443' : '80';
    }
  
    this.agent = opts.agent || false;
    this.hostname = opts.hostname ||
      (typeof location !== 'undefined' ? location.hostname : 'localhost');
    this.port = opts.port || (typeof location !== 'undefined' && location.port
        ? location.port
        : (this.secure ? 443 : 80));
    this.query = opts.query || {};
    if ('string' === typeof this.query) this.query = parseqs.decode(this.query);
    this.upgrade = false !== opts.upgrade;
    this.path = (opts.path || '/engine.io').replace(/\/$/, '') + '/';
    this.forceJSONP = !!opts.forceJSONP;
    this.jsonp = false !== opts.jsonp;
    this.forceBase64 = !!opts.forceBase64;
    this.enablesXDR = !!opts.enablesXDR;
    this.timestampParam = opts.timestampParam || 't';
    this.timestampRequests = opts.timestampRequests;
    this.transports = opts.transports || ['polling', 'websocket'];
    this.transportOptions = opts.transportOptions || {};
    this.readyState = '';
    this.writeBuffer = [];
    this.prevBufferLen = 0;
    this.policyPort = opts.policyPort || 843;
    this.rememberUpgrade = opts.rememberUpgrade || false;
    this.binaryType = null;
    this.onlyBinaryUpgrades = opts.onlyBinaryUpgrades;
    this.perMessageDeflate = false !== opts.perMessageDeflate ? (opts.perMessageDeflate || {}) : false;
  
    if (true === this.perMessageDeflate) this.perMessageDeflate = {};
    if (this.perMessageDeflate && null == this.perMessageDeflate.threshold) {
      this.perMessageDeflate.threshold = 1024;
    }
  
    // SSL options for Node.js client
    this.pfx = opts.pfx || null;
    this.key = opts.key || null;
    this.passphrase = opts.passphrase || null;
    this.cert = opts.cert || null;
    this.ca = opts.ca || null;
    this.ciphers = opts.ciphers || null;
    this.rejectUnauthorized = opts.rejectUnauthorized === undefined ? true : opts.rejectUnauthorized;
    this.forceNode = !!opts.forceNode;
  
    // detect ReactNative environment
    this.isReactNative = (typeof navigator !== 'undefined' && typeof navigator.product === 'string' && navigator.product.toLowerCase() === 'reactnative');
  
    // other options for Node.js or ReactNative client
    if (typeof self === 'undefined' || this.isReactNative) {
      if (opts.extraHeaders && Object.keys(opts.extraHeaders).length > 0) {
        this.extraHeaders = opts.extraHeaders;
      }
  
      if (opts.localAddress) {
        this.localAddress = opts.localAddress;
      }
    }
  
    // set on handshake
    this.id = null;
    this.upgrades = null;
    this.pingInterval = null;
    this.pingTimeout = null;
  
    // set on heartbeat
    this.pingIntervalTimer = null;
    this.pingTimeoutTimer = null;
  
    this.open();
  }
  
  Socket.priorWebsocketSuccess = false;
  
  /**
   * Mix in `Emitter`.
   */
  
  Emitter(Socket.prototype);
  
  /**
   * Protocol version.
   *
   * @api public
   */
  
  Socket.protocol = parser.protocol; // this is an int
  
  /**
   * Expose deps for legacy compatibility
   * and standalone browser access.
   */
  
  Socket.Socket = Socket;
  Socket.Transport = __webpack_require__(/*! ./transport */ "./node_modules/socket.io-client/node_modules/engine.io-client/lib/transport.js");
  Socket.transports = __webpack_require__(/*! ./transports/index */ "./node_modules/socket.io-client/node_modules/engine.io-client/lib/transports/index.js");
  Socket.parser = __webpack_require__(/*! engine.io-parser */ "./node_modules/engine.io-parser/lib/browser.js");
  
  /**
   * Creates transport of the given type.
   *
   * @param {String} transport name
   * @return {Transport}
   * @api private
   */
  
  Socket.prototype.createTransport = function (name) {
    debug('creating transport "%s"', name);
    var query = clone(this.query);
  
    // append engine.io protocol identifier
    query.EIO = parser.protocol;
  
    // transport name
    query.transport = name;
  
    // per-transport options
    var options = this.transportOptions[name] || {};
  
    // session id if we already have one
    if (this.id) query.sid = this.id;
  
    var transport = new transports[name]({
      query: query,
      socket: this,
      agent: options.agent || this.agent,
      hostname: options.hostname || this.hostname,
      port: options.port || this.port,
      secure: options.secure || this.secure,
      path: options.path || this.path,
      forceJSONP: options.forceJSONP || this.forceJSONP,
      jsonp: options.jsonp || this.jsonp,
      forceBase64: options.forceBase64 || this.forceBase64,
      enablesXDR: options.enablesXDR || this.enablesXDR,
      timestampRequests: options.timestampRequests || this.timestampRequests,
      timestampParam: options.timestampParam || this.timestampParam,
      policyPort: options.policyPort || this.policyPort,
      pfx: options.pfx || this.pfx,
      key: options.key || this.key,
      passphrase: options.passphrase || this.passphrase,
      cert: options.cert || this.cert,
      ca: options.ca || this.ca,
      ciphers: options.ciphers || this.ciphers,
      rejectUnauthorized: options.rejectUnauthorized || this.rejectUnauthorized,
      perMessageDeflate: options.perMessageDeflate || this.perMessageDeflate,
      extraHeaders: options.extraHeaders || this.extraHeaders,
      forceNode: options.forceNode || this.forceNode,
      localAddress: options.localAddress || this.localAddress,
      requestTimeout: options.requestTimeout || this.requestTimeout,
      protocols: options.protocols || void (0),
      isReactNative: this.isReactNative
    });
  
    return transport;
  };
  
  function clone (obj) {
    var o = {};
    for (var i in obj) {
      if (obj.hasOwnProperty(i)) {
        o[i] = obj[i];
      }
    }
    return o;
  }
  
  /**
   * Initializes transport to use and starts probe.
   *
   * @api private
   */
  Socket.prototype.open = function () {
    var transport;
    if (this.rememberUpgrade && Socket.priorWebsocketSuccess && this.transports.indexOf('websocket') !== -1) {
      transport = 'websocket';
    } else if (0 === this.transports.length) {
      // Emit error on next tick so it can be listened to
      var self = this;
      setTimeout(function () {
        self.emit('error', 'No transports available');
      }, 0);
      return;
    } else {
      transport = this.transports[0];
    }
    this.readyState = 'opening';
  
    // Retry with the next transport if the transport is disabled (jsonp: false)
    try {
      transport = this.createTransport(transport);
    } catch (e) {
      this.transports.shift();
      this.open();
      return;
    }
  
    transport.open();
    this.setTransport(transport);
  };
  
  /**
   * Sets the current transport. Disables the existing one (if any).
   *
   * @api private
   */
  
  Socket.prototype.setTransport = function (transport) {
    debug('setting transport %s', transport.name);
    var self = this;
  
    if (this.transport) {
      debug('clearing existing transport %s', this.transport.name);
      this.transport.removeAllListeners();
    }
  
    // set up transport
    this.transport = transport;
  
    // set up transport listeners
    transport
    .on('drain', function () {
      self.onDrain();
    })
    .on('packet', function (packet) {
      self.onPacket(packet);
    })
    .on('error', function (e) {
      self.onError(e);
    })
    .on('close', function () {
      self.onClose('transport close');
    });
  };
  
  /**
   * Probes a transport.
   *
   * @param {String} transport name
   * @api private
   */
  
  Socket.prototype.probe = function (name) {
    debug('probing transport "%s"', name);
    var transport = this.createTransport(name, { probe: 1 });
    var failed = false;
    var self = this;
  
    Socket.priorWebsocketSuccess = false;
  
    function onTransportOpen () {
      if (self.onlyBinaryUpgrades) {
        var upgradeLosesBinary = !this.supportsBinary && self.transport.supportsBinary;
        failed = failed || upgradeLosesBinary;
      }
      if (failed) return;
  
      debug('probe transport "%s" opened', name);
      transport.send([{ type: 'ping', data: 'probe' }]);
      transport.once('packet', function (msg) {
        if (failed) return;
        if ('pong' === msg.type && 'probe' === msg.data) {
          debug('probe transport "%s" pong', name);
          self.upgrading = true;
          self.emit('upgrading', transport);
          if (!transport) return;
          Socket.priorWebsocketSuccess = 'websocket' === transport.name;
  
          debug('pausing current transport "%s"', self.transport.name);
          self.transport.pause(function () {
            if (failed) return;
            if ('closed' === self.readyState) return;
            debug('changing transport and sending upgrade packet');
  
            cleanup();
  
            self.setTransport(transport);
            transport.send([{ type: 'upgrade' }]);
            self.emit('upgrade', transport);
            transport = null;
            self.upgrading = false;
            self.flush();
          });
        } else {
          debug('probe transport "%s" failed', name);
          var err = new Error('probe error');
          err.transport = transport.name;
          self.emit('upgradeError', err);
        }
      });
    }
  
    function freezeTransport () {
      if (failed) return;
  
      // Any callback called by transport should be ignored since now
      failed = true;
  
      cleanup();
  
      transport.close();
      transport = null;
    }
  
    // Handle any error that happens while probing
    function onerror (err) {
      var error = new Error('probe error: ' + err);
      error.transport = transport.name;
  
      freezeTransport();
  
      debug('probe transport "%s" failed because of error: %s', name, err);
  
      self.emit('upgradeError', error);
    }
  
    function onTransportClose () {
      onerror('transport closed');
    }
  
    // When the socket is closed while we're probing
    function onclose () {
      onerror('socket closed');
    }
  
    // When the socket is upgraded while we're probing
    function onupgrade (to) {
      if (transport && to.name !== transport.name) {
        debug('"%s" works - aborting "%s"', to.name, transport.name);
        freezeTransport();
      }
    }
  
    // Remove all listeners on the transport and on self
    function cleanup () {
      transport.removeListener('open', onTransportOpen);
      transport.removeListener('error', onerror);
      transport.removeListener('close', onTransportClose);
      self.removeListener('close', onclose);
      self.removeListener('upgrading', onupgrade);
    }
  
    transport.once('open', onTransportOpen);
    transport.once('error', onerror);
    transport.once('close', onTransportClose);
  
    this.once('close', onclose);
    this.once('upgrading', onupgrade);
  
    transport.open();
  };
  
  /**
   * Called when connection is deemed open.
   *
   * @api public
   */
  
  Socket.prototype.onOpen = function () {
    debug('socket open');
    this.readyState = 'open';
    Socket.priorWebsocketSuccess = 'websocket' === this.transport.name;
    this.emit('open');
    this.flush();
  
    // we check for `readyState` in case an `open`
    // listener already closed the socket
    if ('open' === this.readyState && this.upgrade && this.transport.pause) {
      debug('starting upgrade probes');
      for (var i = 0, l = this.upgrades.length; i < l; i++) {
        this.probe(this.upgrades[i]);
      }
    }
  };
  
  /**
   * Handles a packet.
   *
   * @api private
   */
  
  Socket.prototype.onPacket = function (packet) {
    if ('opening' === this.readyState || 'open' === this.readyState ||
        'closing' === this.readyState) {
      debug('socket receive: type "%s", data "%s"', packet.type, packet.data);
  
      this.emit('packet', packet);
  
      // Socket is live - any packet counts
      this.emit('heartbeat');
  
      switch (packet.type) {
        case 'open':
          this.onHandshake(JSON.parse(packet.data));
          break;
  
        case 'pong':
          this.setPing();
          this.emit('pong');
          break;
  
        case 'error':
          var err = new Error('server error');
          err.code = packet.data;
          this.onError(err);
          break;
  
        case 'message':
          this.emit('data', packet.data);
          this.emit('message', packet.data);
          break;
      }
    } else {
      debug('packet received with socket readyState "%s"', this.readyState);
    }
  };
  
  /**
   * Called upon handshake completion.
   *
   * @param {Object} handshake obj
   * @api private
   */
  
  Socket.prototype.onHandshake = function (data) {
    this.emit('handshake', data);
    this.id = data.sid;
    this.transport.query.sid = data.sid;
    this.upgrades = this.filterUpgrades(data.upgrades);
    this.pingInterval = data.pingInterval;
    this.pingTimeout = data.pingTimeout;
    this.onOpen();
    // In case open handler closes socket
    if ('closed' === this.readyState) return;
    this.setPing();
  
    // Prolong liveness of socket on heartbeat
    this.removeListener('heartbeat', this.onHeartbeat);
    this.on('heartbeat', this.onHeartbeat);
  };
  
  /**
   * Resets ping timeout.
   *
   * @api private
   */
  
  Socket.prototype.onHeartbeat = function (timeout) {
    clearTimeout(this.pingTimeoutTimer);
    var self = this;
    self.pingTimeoutTimer = setTimeout(function () {
      if ('closed' === self.readyState) return;
      self.onClose('ping timeout');
    }, timeout || (self.pingInterval + self.pingTimeout));
  };
  
  /**
   * Pings server every `this.pingInterval` and expects response
   * within `this.pingTimeout` or closes connection.
   *
   * @api private
   */
  
  Socket.prototype.setPing = function () {
    var self = this;
    clearTimeout(self.pingIntervalTimer);
    self.pingIntervalTimer = setTimeout(function () {
      debug('writing ping packet - expecting pong within %sms', self.pingTimeout);
      self.ping();
      self.onHeartbeat(self.pingTimeout);
    }, self.pingInterval);
  };
  
  /**
  * Sends a ping packet.
  *
  * @api private
  */
  
  Socket.prototype.ping = function () {
    var self = this;
    this.sendPacket('ping', function () {
      self.emit('ping');
    });
  };
  
  /**
   * Called on `drain` event
   *
   * @api private
   */
  
  Socket.prototype.onDrain = function () {
    this.writeBuffer.splice(0, this.prevBufferLen);
  
    // setting prevBufferLen = 0 is very important
    // for example, when upgrading, upgrade packet is sent over,
    // and a nonzero prevBufferLen could cause problems on `drain`
    this.prevBufferLen = 0;
  
    if (0 === this.writeBuffer.length) {
      this.emit('drain');
    } else {
      this.flush();
    }
  };
  
  /**
   * Flush write buffers.
   *
   * @api private
   */
  
  Socket.prototype.flush = function () {
    if ('closed' !== this.readyState && this.transport.writable &&
      !this.upgrading && this.writeBuffer.length) {
      debug('flushing %d packets in socket', this.writeBuffer.length);
      this.transport.send(this.writeBuffer);
      // keep track of current length of writeBuffer
      // splice writeBuffer and callbackBuffer on `drain`
      this.prevBufferLen = this.writeBuffer.length;
      this.emit('flush');
    }
  };
  
  /**
   * Sends a message.
   *
   * @param {String} message.
   * @param {Function} callback function.
   * @param {Object} options.
   * @return {Socket} for chaining.
   * @api public
   */
  
  Socket.prototype.write =
  Socket.prototype.send = function (msg, options, fn) {
    this.sendPacket('message', msg, options, fn);
    return this;
  };
  
  /**
   * Sends a packet.
   *
   * @param {String} packet type.
   * @param {String} data.
   * @param {Object} options.
   * @param {Function} callback function.
   * @api private
   */
  
  Socket.prototype.sendPacket = function (type, data, options, fn) {
    if ('function' === typeof data) {
      fn = data;
      data = undefined;
    }
  
    if ('function' === typeof options) {
      fn = options;
      options = null;
    }
  
    if ('closing' === this.readyState || 'closed' === this.readyState) {
      return;
    }
  
    options = options || {};
    options.compress = false !== options.compress;
  
    var packet = {
      type: type,
      data: data,
      options: options
    };
    this.emit('packetCreate', packet);
    this.writeBuffer.push(packet);
    if (fn) this.once('flush', fn);
    this.flush();
  };
  
  /**
   * Closes the connection.
   *
   * @api private
   */
  
  Socket.prototype.close = function () {
    if ('opening' === this.readyState || 'open' === this.readyState) {
      this.readyState = 'closing';
  
      var self = this;
  
      if (this.writeBuffer.length) {
        this.once('drain', function () {
          if (this.upgrading) {
            waitForUpgrade();
          } else {
            close();
          }
        });
      } else if (this.upgrading) {
        waitForUpgrade();
      } else {
        close();
      }
    }
  
    function close () {
      self.onClose('forced close');
      debug('socket closing - telling transport to close');
      self.transport.close();
    }
  
    function cleanupAndClose () {
      self.removeListener('upgrade', cleanupAndClose);
      self.removeListener('upgradeError', cleanupAndClose);
      close();
    }
  
    function waitForUpgrade () {
      // wait for upgrade to finish since we can't send packets while pausing a transport
      self.once('upgrade', cleanupAndClose);
      self.once('upgradeError', cleanupAndClose);
    }
  
    return this;
  };
  
  /**
   * Called upon transport error
   *
   * @api private
   */
  
  Socket.prototype.onError = function (err) {
    debug('socket error %j', err);
    Socket.priorWebsocketSuccess = false;
    this.emit('error', err);
    this.onClose('transport error', err);
  };
  
  /**
   * Called upon transport close.
   *
   * @api private
   */
  
  Socket.prototype.onClose = function (reason, desc) {
    if ('opening' === this.readyState || 'open' === this.readyState || 'closing' === this.readyState) {
      debug('socket close with reason: "%s"', reason);
      var self = this;
  
      // clear timers
      clearTimeout(this.pingIntervalTimer);
      clearTimeout(this.pingTimeoutTimer);
  
      // stop event from firing again for transport
      this.transport.removeAllListeners('close');
  
      // ensure transport won't stay open
      this.transport.close();
  
      // ignore further transport communication
      this.transport.removeAllListeners();
  
      // set ready state
      this.readyState = 'closed';
  
      // clear session id
      this.id = null;
  
      // emit close event
      this.emit('close', reason, desc);
  
      // clean buffers after, so users can still
      // grab the buffers on `close` event
      self.writeBuffer = [];
      self.prevBufferLen = 0;
    }
  };
  
  /**
   * Filters upgrades, returning only those matching client transports.
   *
   * @param {Array} server upgrades
   * @api private
   *
   */
  
  Socket.prototype.filterUpgrades = function (upgrades) {
    var filteredUpgrades = [];
    for (var i = 0, j = upgrades.length; i < j; i++) {
      if (~index(this.transports, upgrades[i])) filteredUpgrades.push(upgrades[i]);
    }
    return filteredUpgrades;
  };
  
  
  /***/ }),
  
  /***/ "./node_modules/socket.io-client/node_modules/engine.io-client/lib/transport.js":
  /*!**************************************************************************************!*\
    !*** ./node_modules/socket.io-client/node_modules/engine.io-client/lib/transport.js ***!
    \**************************************************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  /**
   * Module dependencies.
   */
  
  var parser = __webpack_require__(/*! engine.io-parser */ "./node_modules/engine.io-parser/lib/browser.js");
  var Emitter = __webpack_require__(/*! component-emitter */ "./node_modules/component-emitter/index.js");
  
  /**
   * Module exports.
   */
  
  module.exports = Transport;
  
  /**
   * Transport abstract constructor.
   *
   * @param {Object} options.
   * @api private
   */
  
  function Transport (opts) {
    this.path = opts.path;
    this.hostname = opts.hostname;
    this.port = opts.port;
    this.secure = opts.secure;
    this.query = opts.query;
    this.timestampParam = opts.timestampParam;
    this.timestampRequests = opts.timestampRequests;
    this.readyState = '';
    this.agent = opts.agent || false;
    this.socket = opts.socket;
    this.enablesXDR = opts.enablesXDR;
  
    // SSL options for Node.js client
    this.pfx = opts.pfx;
    this.key = opts.key;
    this.passphrase = opts.passphrase;
    this.cert = opts.cert;
    this.ca = opts.ca;
    this.ciphers = opts.ciphers;
    this.rejectUnauthorized = opts.rejectUnauthorized;
    this.forceNode = opts.forceNode;
  
    // results of ReactNative environment detection
    this.isReactNative = opts.isReactNative;
  
    // other options for Node.js client
    this.extraHeaders = opts.extraHeaders;
    this.localAddress = opts.localAddress;
  }
  
  /**
   * Mix in `Emitter`.
   */
  
  Emitter(Transport.prototype);
  
  /**
   * Emits an error.
   *
   * @param {String} str
   * @return {Transport} for chaining
   * @api public
   */
  
  Transport.prototype.onError = function (msg, desc) {
    var err = new Error(msg);
    err.type = 'TransportError';
    err.description = desc;
    this.emit('error', err);
    return this;
  };
  
  /**
   * Opens the transport.
   *
   * @api public
   */
  
  Transport.prototype.open = function () {
    if ('closed' === this.readyState || '' === this.readyState) {
      this.readyState = 'opening';
      this.doOpen();
    }
  
    return this;
  };
  
  /**
   * Closes the transport.
   *
   * @api private
   */
  
  Transport.prototype.close = function () {
    if ('opening' === this.readyState || 'open' === this.readyState) {
      this.doClose();
      this.onClose();
    }
  
    return this;
  };
  
  /**
   * Sends multiple packets.
   *
   * @param {Array} packets
   * @api private
   */
  
  Transport.prototype.send = function (packets) {
    if ('open' === this.readyState) {
      this.write(packets);
    } else {
      throw new Error('Transport not open');
    }
  };
  
  /**
   * Called upon open
   *
   * @api private
   */
  
  Transport.prototype.onOpen = function () {
    this.readyState = 'open';
    this.writable = true;
    this.emit('open');
  };
  
  /**
   * Called with data.
   *
   * @param {String} data
   * @api private
   */
  
  Transport.prototype.onData = function (data) {
    var packet = parser.decodePacket(data, this.socket.binaryType);
    this.onPacket(packet);
  };
  
  /**
   * Called with a decoded packet.
   */
  
  Transport.prototype.onPacket = function (packet) {
    this.emit('packet', packet);
  };
  
  /**
   * Called upon close.
   *
   * @api private
   */
  
  Transport.prototype.onClose = function () {
    this.readyState = 'closed';
    this.emit('close');
  };
  
  
  /***/ }),
  
  /***/ "./node_modules/socket.io-client/node_modules/engine.io-client/lib/transports/index.js":
  /*!*********************************************************************************************!*\
    !*** ./node_modules/socket.io-client/node_modules/engine.io-client/lib/transports/index.js ***!
    \*********************************************************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  /**
   * Module dependencies
   */
  
  var XMLHttpRequest = __webpack_require__(/*! xmlhttprequest-ssl */ "./node_modules/socket.io-client/node_modules/engine.io-client/lib/xmlhttprequest.js");
  var XHR = __webpack_require__(/*! ./polling-xhr */ "./node_modules/socket.io-client/node_modules/engine.io-client/lib/transports/polling-xhr.js");
  var JSONP = __webpack_require__(/*! ./polling-jsonp */ "./node_modules/socket.io-client/node_modules/engine.io-client/lib/transports/polling-jsonp.js");
  var websocket = __webpack_require__(/*! ./websocket */ "./node_modules/socket.io-client/node_modules/engine.io-client/lib/transports/websocket.js");
  
  /**
   * Export transports.
   */
  
  exports.polling = polling;
  exports.websocket = websocket;
  
  /**
   * Polling transport polymorphic constructor.
   * Decides on xhr vs jsonp based on feature detection.
   *
   * @api private
   */
  
  function polling (opts) {
    var xhr;
    var xd = false;
    var xs = false;
    var jsonp = false !== opts.jsonp;
  
    if (typeof location !== 'undefined') {
      var isSSL = 'https:' === location.protocol;
      var port = location.port;
  
      // some user agents have empty `location.port`
      if (!port) {
        port = isSSL ? 443 : 80;
      }
  
      xd = opts.hostname !== location.hostname || port !== opts.port;
      xs = opts.secure !== isSSL;
    }
  
    opts.xdomain = xd;
    opts.xscheme = xs;
    xhr = new XMLHttpRequest(opts);
  
    if ('open' in xhr && !opts.forceJSONP) {
      return new XHR(opts);
    } else {
      if (!jsonp) throw new Error('JSONP disabled');
      return new JSONP(opts);
    }
  }
  
  
  /***/ }),
  
  /***/ "./node_modules/socket.io-client/node_modules/engine.io-client/lib/transports/polling-jsonp.js":
  /*!*****************************************************************************************************!*\
    !*** ./node_modules/socket.io-client/node_modules/engine.io-client/lib/transports/polling-jsonp.js ***!
    \*****************************************************************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  /* WEBPACK VAR INJECTION */(function(global) {/**
   * Module requirements.
   */
  
  var Polling = __webpack_require__(/*! ./polling */ "./node_modules/socket.io-client/node_modules/engine.io-client/lib/transports/polling.js");
  var inherit = __webpack_require__(/*! component-inherit */ "./node_modules/component-inherit/index.js");
  
  /**
   * Module exports.
   */
  
  module.exports = JSONPPolling;
  
  /**
   * Cached regular expressions.
   */
  
  var rNewline = /\n/g;
  var rEscapedNewline = /\\n/g;
  
  /**
   * Global JSONP callbacks.
   */
  
  var callbacks;
  
  /**
   * Noop.
   */
  
  function empty () { }
  
  /**
   * Until https://github.com/tc39/proposal-global is shipped.
   */
  function glob () {
    return typeof self !== 'undefined' ? self
        : typeof window !== 'undefined' ? window
        : typeof global !== 'undefined' ? global : {};
  }
  
  /**
   * JSONP Polling constructor.
   *
   * @param {Object} opts.
   * @api public
   */
  
  function JSONPPolling (opts) {
    Polling.call(this, opts);
  
    this.query = this.query || {};
  
    // define global callbacks array if not present
    // we do this here (lazily) to avoid unneeded global pollution
    if (!callbacks) {
      // we need to consider multiple engines in the same page
      var global = glob();
      callbacks = global.___eio = (global.___eio || []);
    }
  
    // callback identifier
    this.index = callbacks.length;
  
    // add callback to jsonp global
    var self = this;
    callbacks.push(function (msg) {
      self.onData(msg);
    });
  
    // append to query string
    this.query.j = this.index;
  
    // prevent spurious errors from being emitted when the window is unloaded
    if (typeof addEventListener === 'function') {
      addEventListener('beforeunload', function () {
        if (self.script) self.script.onerror = empty;
      }, false);
    }
  }
  
  /**
   * Inherits from Polling.
   */
  
  inherit(JSONPPolling, Polling);
  
  /*
   * JSONP only supports binary as base64 encoded strings
   */
  
  JSONPPolling.prototype.supportsBinary = false;
  
  /**
   * Closes the socket.
   *
   * @api private
   */
  
  JSONPPolling.prototype.doClose = function () {
    if (this.script) {
      this.script.parentNode.removeChild(this.script);
      this.script = null;
    }
  
    if (this.form) {
      this.form.parentNode.removeChild(this.form);
      this.form = null;
      this.iframe = null;
    }
  
    Polling.prototype.doClose.call(this);
  };
  
  /**
   * Starts a poll cycle.
   *
   * @api private
   */
  
  JSONPPolling.prototype.doPoll = function () {
    var self = this;
    var script = document.createElement('script');
  
    if (this.script) {
      this.script.parentNode.removeChild(this.script);
      this.script = null;
    }
  
    script.async = true;
    script.src = this.uri();
    script.onerror = function (e) {
      self.onError('jsonp poll error', e);
    };
  
    var insertAt = document.getElementsByTagName('script')[0];
    if (insertAt) {
      insertAt.parentNode.insertBefore(script, insertAt);
    } else {
      (document.head || document.body).appendChild(script);
    }
    this.script = script;
  
    var isUAgecko = 'undefined' !== typeof navigator && /gecko/i.test(navigator.userAgent);
  
    if (isUAgecko) {
      setTimeout(function () {
        var iframe = document.createElement('iframe');
        document.body.appendChild(iframe);
        document.body.removeChild(iframe);
      }, 100);
    }
  };
  
  /**
   * Writes with a hidden iframe.
   *
   * @param {String} data to send
   * @param {Function} called upon flush.
   * @api private
   */
  
  JSONPPolling.prototype.doWrite = function (data, fn) {
    var self = this;
  
    if (!this.form) {
      var form = document.createElement('form');
      var area = document.createElement('textarea');
      var id = this.iframeId = 'eio_iframe_' + this.index;
      var iframe;
  
      form.className = 'socketio';
      form.style.position = 'absolute';
      form.style.top = '-1000px';
      form.style.left = '-1000px';
      form.target = id;
      form.method = 'POST';
      form.setAttribute('accept-charset', 'utf-8');
      area.name = 'd';
      form.appendChild(area);
      document.body.appendChild(form);
  
      this.form = form;
      this.area = area;
    }
  
    this.form.action = this.uri();
  
    function complete () {
      initIframe();
      fn();
    }
  
    function initIframe () {
      if (self.iframe) {
        try {
          self.form.removeChild(self.iframe);
        } catch (e) {
          self.onError('jsonp polling iframe removal error', e);
        }
      }
  
      try {
        // ie6 dynamic iframes with target="" support (thanks Chris Lambacher)
        var html = '<iframe src="javascript:0" name="' + self.iframeId + '">';
        iframe = document.createElement(html);
      } catch (e) {
        iframe = document.createElement('iframe');
        iframe.name = self.iframeId;
        iframe.src = 'javascript:0';
      }
  
      iframe.id = self.iframeId;
  
      self.form.appendChild(iframe);
      self.iframe = iframe;
    }
  
    initIframe();
  
    // escape \n to prevent it from being converted into \r\n by some UAs
    // double escaping is required for escaped new lines because unescaping of new lines can be done safely on server-side
    data = data.replace(rEscapedNewline, '\\\n');
    this.area.value = data.replace(rNewline, '\\n');
  
    try {
      this.form.submit();
    } catch (e) {}
  
    if (this.iframe.attachEvent) {
      this.iframe.onreadystatechange = function () {
        if (self.iframe.readyState === 'complete') {
          complete();
        }
      };
    } else {
      this.iframe.onload = complete;
    }
  };
  
  /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../../../../webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js")))
  
  /***/ }),
  
  /***/ "./node_modules/socket.io-client/node_modules/engine.io-client/lib/transports/polling-xhr.js":
  /*!***************************************************************************************************!*\
    !*** ./node_modules/socket.io-client/node_modules/engine.io-client/lib/transports/polling-xhr.js ***!
    \***************************************************************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  /* global attachEvent */
  
  /**
   * Module requirements.
   */
  
  var XMLHttpRequest = __webpack_require__(/*! xmlhttprequest-ssl */ "./node_modules/socket.io-client/node_modules/engine.io-client/lib/xmlhttprequest.js");
  var Polling = __webpack_require__(/*! ./polling */ "./node_modules/socket.io-client/node_modules/engine.io-client/lib/transports/polling.js");
  var Emitter = __webpack_require__(/*! component-emitter */ "./node_modules/component-emitter/index.js");
  var inherit = __webpack_require__(/*! component-inherit */ "./node_modules/component-inherit/index.js");
  var debug = __webpack_require__(/*! debug */ "./node_modules/socket.io-client/node_modules/debug/src/browser.js")('engine.io-client:polling-xhr');
  
  /**
   * Module exports.
   */
  
  module.exports = XHR;
  module.exports.Request = Request;
  
  /**
   * Empty function
   */
  
  function empty () {}
  
  /**
   * XHR Polling constructor.
   *
   * @param {Object} opts
   * @api public
   */
  
  function XHR (opts) {
    Polling.call(this, opts);
    this.requestTimeout = opts.requestTimeout;
    this.extraHeaders = opts.extraHeaders;
  
    if (typeof location !== 'undefined') {
      var isSSL = 'https:' === location.protocol;
      var port = location.port;
  
      // some user agents have empty `location.port`
      if (!port) {
        port = isSSL ? 443 : 80;
      }
  
      this.xd = (typeof location !== 'undefined' && opts.hostname !== location.hostname) ||
        port !== opts.port;
      this.xs = opts.secure !== isSSL;
    }
  }
  
  /**
   * Inherits from Polling.
   */
  
  inherit(XHR, Polling);
  
  /**
   * XHR supports binary
   */
  
  XHR.prototype.supportsBinary = true;
  
  /**
   * Creates a request.
   *
   * @param {String} method
   * @api private
   */
  
  XHR.prototype.request = function (opts) {
    opts = opts || {};
    opts.uri = this.uri();
    opts.xd = this.xd;
    opts.xs = this.xs;
    opts.agent = this.agent || false;
    opts.supportsBinary = this.supportsBinary;
    opts.enablesXDR = this.enablesXDR;
  
    // SSL options for Node.js client
    opts.pfx = this.pfx;
    opts.key = this.key;
    opts.passphrase = this.passphrase;
    opts.cert = this.cert;
    opts.ca = this.ca;
    opts.ciphers = this.ciphers;
    opts.rejectUnauthorized = this.rejectUnauthorized;
    opts.requestTimeout = this.requestTimeout;
  
    // other options for Node.js client
    opts.extraHeaders = this.extraHeaders;
  
    return new Request(opts);
  };
  
  /**
   * Sends data.
   *
   * @param {String} data to send.
   * @param {Function} called upon flush.
   * @api private
   */
  
  XHR.prototype.doWrite = function (data, fn) {
    var isBinary = typeof data !== 'string' && data !== undefined;
    var req = this.request({ method: 'POST', data: data, isBinary: isBinary });
    var self = this;
    req.on('success', fn);
    req.on('error', function (err) {
      self.onError('xhr post error', err);
    });
    this.sendXhr = req;
  };
  
  /**
   * Starts a poll cycle.
   *
   * @api private
   */
  
  XHR.prototype.doPoll = function () {
    debug('xhr poll');
    var req = this.request();
    var self = this;
    req.on('data', function (data) {
      self.onData(data);
    });
    req.on('error', function (err) {
      self.onError('xhr poll error', err);
    });
    this.pollXhr = req;
  };
  
  /**
   * Request constructor
   *
   * @param {Object} options
   * @api public
   */
  
  function Request (opts) {
    this.method = opts.method || 'GET';
    this.uri = opts.uri;
    this.xd = !!opts.xd;
    this.xs = !!opts.xs;
    this.async = false !== opts.async;
    this.data = undefined !== opts.data ? opts.data : null;
    this.agent = opts.agent;
    this.isBinary = opts.isBinary;
    this.supportsBinary = opts.supportsBinary;
    this.enablesXDR = opts.enablesXDR;
    this.requestTimeout = opts.requestTimeout;
  
    // SSL options for Node.js client
    this.pfx = opts.pfx;
    this.key = opts.key;
    this.passphrase = opts.passphrase;
    this.cert = opts.cert;
    this.ca = opts.ca;
    this.ciphers = opts.ciphers;
    this.rejectUnauthorized = opts.rejectUnauthorized;
  
    // other options for Node.js client
    this.extraHeaders = opts.extraHeaders;
  
    this.create();
  }
  
  /**
   * Mix in `Emitter`.
   */
  
  Emitter(Request.prototype);
  
  /**
   * Creates the XHR object and sends the request.
   *
   * @api private
   */
  
  Request.prototype.create = function () {
    var opts = { agent: this.agent, xdomain: this.xd, xscheme: this.xs, enablesXDR: this.enablesXDR };
  
    // SSL options for Node.js client
    opts.pfx = this.pfx;
    opts.key = this.key;
    opts.passphrase = this.passphrase;
    opts.cert = this.cert;
    opts.ca = this.ca;
    opts.ciphers = this.ciphers;
    opts.rejectUnauthorized = this.rejectUnauthorized;
  
    var xhr = this.xhr = new XMLHttpRequest(opts);
    var self = this;
  
    try {
      debug('xhr open %s: %s', this.method, this.uri);
      xhr.open(this.method, this.uri, this.async);
      try {
        if (this.extraHeaders) {
          xhr.setDisableHeaderCheck && xhr.setDisableHeaderCheck(true);
          for (var i in this.extraHeaders) {
            if (this.extraHeaders.hasOwnProperty(i)) {
              xhr.setRequestHeader(i, this.extraHeaders[i]);
            }
          }
        }
      } catch (e) {}
  
      if ('POST' === this.method) {
        try {
          if (this.isBinary) {
            xhr.setRequestHeader('Content-type', 'application/octet-stream');
          } else {
            xhr.setRequestHeader('Content-type', 'text/plain;charset=UTF-8');
          }
        } catch (e) {}
      }
  
      try {
        xhr.setRequestHeader('Accept', '*/*');
      } catch (e) {}
  
      // ie6 check
      if ('withCredentials' in xhr) {
        xhr.withCredentials = true;
      }
  
      if (this.requestTimeout) {
        xhr.timeout = this.requestTimeout;
      }
  
      if (this.hasXDR()) {
        xhr.onload = function () {
          self.onLoad();
        };
        xhr.onerror = function () {
          self.onError(xhr.responseText);
        };
      } else {
        xhr.onreadystatechange = function () {
          if (xhr.readyState === 2) {
            try {
              var contentType = xhr.getResponseHeader('Content-Type');
              if (self.supportsBinary && contentType === 'application/octet-stream') {
                xhr.responseType = 'arraybuffer';
              }
            } catch (e) {}
          }
          if (4 !== xhr.readyState) return;
          if (200 === xhr.status || 1223 === xhr.status) {
            self.onLoad();
          } else {
            // make sure the `error` event handler that's user-set
            // does not throw in the same tick and gets caught here
            setTimeout(function () {
              self.onError(xhr.status);
            }, 0);
          }
        };
      }
  
      debug('xhr data %s', this.data);
      xhr.send(this.data);
    } catch (e) {
      // Need to defer since .create() is called directly fhrom the constructor
      // and thus the 'error' event can only be only bound *after* this exception
      // occurs.  Therefore, also, we cannot throw here at all.
      setTimeout(function () {
        self.onError(e);
      }, 0);
      return;
    }
  
    if (typeof document !== 'undefined') {
      this.index = Request.requestsCount++;
      Request.requests[this.index] = this;
    }
  };
  
  /**
   * Called upon successful response.
   *
   * @api private
   */
  
  Request.prototype.onSuccess = function () {
    this.emit('success');
    this.cleanup();
  };
  
  /**
   * Called if we have data.
   *
   * @api private
   */
  
  Request.prototype.onData = function (data) {
    this.emit('data', data);
    this.onSuccess();
  };
  
  /**
   * Called upon error.
   *
   * @api private
   */
  
  Request.prototype.onError = function (err) {
    this.emit('error', err);
    this.cleanup(true);
  };
  
  /**
   * Cleans up house.
   *
   * @api private
   */
  
  Request.prototype.cleanup = function (fromError) {
    if ('undefined' === typeof this.xhr || null === this.xhr) {
      return;
    }
    // xmlhttprequest
    if (this.hasXDR()) {
      this.xhr.onload = this.xhr.onerror = empty;
    } else {
      this.xhr.onreadystatechange = empty;
    }
  
    if (fromError) {
      try {
        this.xhr.abort();
      } catch (e) {}
    }
  
    if (typeof document !== 'undefined') {
      delete Request.requests[this.index];
    }
  
    this.xhr = null;
  };
  
  /**
   * Called upon load.
   *
   * @api private
   */
  
  Request.prototype.onLoad = function () {
    var data;
    try {
      var contentType;
      try {
        contentType = this.xhr.getResponseHeader('Content-Type');
      } catch (e) {}
      if (contentType === 'application/octet-stream') {
        data = this.xhr.response || this.xhr.responseText;
      } else {
        data = this.xhr.responseText;
      }
    } catch (e) {
      this.onError(e);
    }
    if (null != data) {
      this.onData(data);
    }
  };
  
  /**
   * Check if it has XDomainRequest.
   *
   * @api private
   */
  
  Request.prototype.hasXDR = function () {
    return typeof XDomainRequest !== 'undefined' && !this.xs && this.enablesXDR;
  };
  
  /**
   * Aborts the request.
   *
   * @api public
   */
  
  Request.prototype.abort = function () {
    this.cleanup();
  };
  
  /**
   * Aborts pending requests when unloading the window. This is needed to prevent
   * memory leaks (e.g. when using IE) and to ensure that no spurious error is
   * emitted.
   */
  
  Request.requestsCount = 0;
  Request.requests = {};
  
  if (typeof document !== 'undefined') {
    if (typeof attachEvent === 'function') {
      attachEvent('onunload', unloadHandler);
    } else if (typeof addEventListener === 'function') {
      var terminationEvent = 'onpagehide' in self ? 'pagehide' : 'unload';
      addEventListener(terminationEvent, unloadHandler, false);
    }
  }
  
  function unloadHandler () {
    for (var i in Request.requests) {
      if (Request.requests.hasOwnProperty(i)) {
        Request.requests[i].abort();
      }
    }
  }
  
  
  /***/ }),
  
  /***/ "./node_modules/socket.io-client/node_modules/engine.io-client/lib/transports/polling.js":
  /*!***********************************************************************************************!*\
    !*** ./node_modules/socket.io-client/node_modules/engine.io-client/lib/transports/polling.js ***!
    \***********************************************************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  /**
   * Module dependencies.
   */
  
  var Transport = __webpack_require__(/*! ../transport */ "./node_modules/socket.io-client/node_modules/engine.io-client/lib/transport.js");
  var parseqs = __webpack_require__(/*! parseqs */ "./node_modules/parseqs/index.js");
  var parser = __webpack_require__(/*! engine.io-parser */ "./node_modules/engine.io-parser/lib/browser.js");
  var inherit = __webpack_require__(/*! component-inherit */ "./node_modules/component-inherit/index.js");
  var yeast = __webpack_require__(/*! yeast */ "./node_modules/yeast/index.js");
  var debug = __webpack_require__(/*! debug */ "./node_modules/socket.io-client/node_modules/debug/src/browser.js")('engine.io-client:polling');
  
  /**
   * Module exports.
   */
  
  module.exports = Polling;
  
  /**
   * Is XHR2 supported?
   */
  
  var hasXHR2 = (function () {
    var XMLHttpRequest = __webpack_require__(/*! xmlhttprequest-ssl */ "./node_modules/socket.io-client/node_modules/engine.io-client/lib/xmlhttprequest.js");
    var xhr = new XMLHttpRequest({ xdomain: false });
    return null != xhr.responseType;
  })();
  
  /**
   * Polling interface.
   *
   * @param {Object} opts
   * @api private
   */
  
  function Polling (opts) {
    var forceBase64 = (opts && opts.forceBase64);
    if (!hasXHR2 || forceBase64) {
      this.supportsBinary = false;
    }
    Transport.call(this, opts);
  }
  
  /**
   * Inherits from Transport.
   */
  
  inherit(Polling, Transport);
  
  /**
   * Transport name.
   */
  
  Polling.prototype.name = 'polling';
  
  /**
   * Opens the socket (triggers polling). We write a PING message to determine
   * when the transport is open.
   *
   * @api private
   */
  
  Polling.prototype.doOpen = function () {
    this.poll();
  };
  
  /**
   * Pauses polling.
   *
   * @param {Function} callback upon buffers are flushed and transport is paused
   * @api private
   */
  
  Polling.prototype.pause = function (onPause) {
    var self = this;
  
    this.readyState = 'pausing';
  
    function pause () {
      debug('paused');
      self.readyState = 'paused';
      onPause();
    }
  
    if (this.polling || !this.writable) {
      var total = 0;
  
      if (this.polling) {
        debug('we are currently polling - waiting to pause');
        total++;
        this.once('pollComplete', function () {
          debug('pre-pause polling complete');
          --total || pause();
        });
      }
  
      if (!this.writable) {
        debug('we are currently writing - waiting to pause');
        total++;
        this.once('drain', function () {
          debug('pre-pause writing complete');
          --total || pause();
        });
      }
    } else {
      pause();
    }
  };
  
  /**
   * Starts polling cycle.
   *
   * @api public
   */
  
  Polling.prototype.poll = function () {
    debug('polling');
    this.polling = true;
    this.doPoll();
    this.emit('poll');
  };
  
  /**
   * Overloads onData to detect payloads.
   *
   * @api private
   */
  
  Polling.prototype.onData = function (data) {
    var self = this;
    debug('polling got data %s', data);
    var callback = function (packet, index, total) {
      // if its the first message we consider the transport open
      if ('opening' === self.readyState) {
        self.onOpen();
      }
  
      // if its a close packet, we close the ongoing requests
      if ('close' === packet.type) {
        self.onClose();
        return false;
      }
  
      // otherwise bypass onData and handle the message
      self.onPacket(packet);
    };
  
    // decode payload
    parser.decodePayload(data, this.socket.binaryType, callback);
  
    // if an event did not trigger closing
    if ('closed' !== this.readyState) {
      // if we got data we're not polling
      this.polling = false;
      this.emit('pollComplete');
  
      if ('open' === this.readyState) {
        this.poll();
      } else {
        debug('ignoring poll - transport state "%s"', this.readyState);
      }
    }
  };
  
  /**
   * For polling, send a close packet.
   *
   * @api private
   */
  
  Polling.prototype.doClose = function () {
    var self = this;
  
    function close () {
      debug('writing close packet');
      self.write([{ type: 'close' }]);
    }
  
    if ('open' === this.readyState) {
      debug('transport open - closing');
      close();
    } else {
      // in case we're trying to close while
      // handshaking is in progress (GH-164)
      debug('transport not open - deferring close');
      this.once('open', close);
    }
  };
  
  /**
   * Writes a packets payload.
   *
   * @param {Array} data packets
   * @param {Function} drain callback
   * @api private
   */
  
  Polling.prototype.write = function (packets) {
    var self = this;
    this.writable = false;
    var callbackfn = function () {
      self.writable = true;
      self.emit('drain');
    };
  
    parser.encodePayload(packets, this.supportsBinary, function (data) {
      self.doWrite(data, callbackfn);
    });
  };
  
  /**
   * Generates uri for connection.
   *
   * @api private
   */
  
  Polling.prototype.uri = function () {
    var query = this.query || {};
    var schema = this.secure ? 'https' : 'http';
    var port = '';
  
    // cache busting is forced
    if (false !== this.timestampRequests) {
      query[this.timestampParam] = yeast();
    }
  
    if (!this.supportsBinary && !query.sid) {
      query.b64 = 1;
    }
  
    query = parseqs.encode(query);
  
    // avoid port if default for schema
    if (this.port && (('https' === schema && Number(this.port) !== 443) ||
       ('http' === schema && Number(this.port) !== 80))) {
      port = ':' + this.port;
    }
  
    // prepend ? to query
    if (query.length) {
      query = '?' + query;
    }
  
    var ipv6 = this.hostname.indexOf(':') !== -1;
    return schema + '://' + (ipv6 ? '[' + this.hostname + ']' : this.hostname) + port + this.path + query;
  };
  
  
  /***/ }),
  
  /***/ "./node_modules/socket.io-client/node_modules/engine.io-client/lib/transports/websocket.js":
  /*!*************************************************************************************************!*\
    !*** ./node_modules/socket.io-client/node_modules/engine.io-client/lib/transports/websocket.js ***!
    \*************************************************************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  /* WEBPACK VAR INJECTION */(function(Buffer) {/**
   * Module dependencies.
   */
  
  var Transport = __webpack_require__(/*! ../transport */ "./node_modules/socket.io-client/node_modules/engine.io-client/lib/transport.js");
  var parser = __webpack_require__(/*! engine.io-parser */ "./node_modules/engine.io-parser/lib/browser.js");
  var parseqs = __webpack_require__(/*! parseqs */ "./node_modules/parseqs/index.js");
  var inherit = __webpack_require__(/*! component-inherit */ "./node_modules/component-inherit/index.js");
  var yeast = __webpack_require__(/*! yeast */ "./node_modules/yeast/index.js");
  var debug = __webpack_require__(/*! debug */ "./node_modules/socket.io-client/node_modules/debug/src/browser.js")('engine.io-client:websocket');
  
  var BrowserWebSocket, NodeWebSocket;
  
  if (typeof WebSocket !== 'undefined') {
    BrowserWebSocket = WebSocket;
  } else if (typeof self !== 'undefined') {
    BrowserWebSocket = self.WebSocket || self.MozWebSocket;
  } else {
    try {
      NodeWebSocket = __webpack_require__(/*! ws */ 0);
    } catch (e) { }
  }
  
  /**
   * Get either the `WebSocket` or `MozWebSocket` globals
   * in the browser or try to resolve WebSocket-compatible
   * interface exposed by `ws` for Node-like environment.
   */
  
  var WebSocketImpl = BrowserWebSocket || NodeWebSocket;
  
  /**
   * Module exports.
   */
  
  module.exports = WS;
  
  /**
   * WebSocket transport constructor.
   *
   * @api {Object} connection options
   * @api public
   */
  
  function WS (opts) {
    var forceBase64 = (opts && opts.forceBase64);
    if (forceBase64) {
      this.supportsBinary = false;
    }
    this.perMessageDeflate = opts.perMessageDeflate;
    this.usingBrowserWebSocket = BrowserWebSocket && !opts.forceNode;
    this.protocols = opts.protocols;
    if (!this.usingBrowserWebSocket) {
      WebSocketImpl = NodeWebSocket;
    }
    Transport.call(this, opts);
  }
  
  /**
   * Inherits from Transport.
   */
  
  inherit(WS, Transport);
  
  /**
   * Transport name.
   *
   * @api public
   */
  
  WS.prototype.name = 'websocket';
  
  /*
   * WebSockets support binary
   */
  
  WS.prototype.supportsBinary = true;
  
  /**
   * Opens socket.
   *
   * @api private
   */
  
  WS.prototype.doOpen = function () {
    if (!this.check()) {
      // let probe timeout
      return;
    }
  
    var uri = this.uri();
    var protocols = this.protocols;
    var opts = {
      agent: this.agent,
      perMessageDeflate: this.perMessageDeflate
    };
  
    // SSL options for Node.js client
    opts.pfx = this.pfx;
    opts.key = this.key;
    opts.passphrase = this.passphrase;
    opts.cert = this.cert;
    opts.ca = this.ca;
    opts.ciphers = this.ciphers;
    opts.rejectUnauthorized = this.rejectUnauthorized;
    if (this.extraHeaders) {
      opts.headers = this.extraHeaders;
    }
    if (this.localAddress) {
      opts.localAddress = this.localAddress;
    }
  
    try {
      this.ws =
        this.usingBrowserWebSocket && !this.isReactNative
          ? protocols
            ? new WebSocketImpl(uri, protocols)
            : new WebSocketImpl(uri)
          : new WebSocketImpl(uri, protocols, opts);
    } catch (err) {
      return this.emit('error', err);
    }
  
    if (this.ws.binaryType === undefined) {
      this.supportsBinary = false;
    }
  
    if (this.ws.supports && this.ws.supports.binary) {
      this.supportsBinary = true;
      this.ws.binaryType = 'nodebuffer';
    } else {
      this.ws.binaryType = 'arraybuffer';
    }
  
    this.addEventListeners();
  };
  
  /**
   * Adds event listeners to the socket
   *
   * @api private
   */
  
  WS.prototype.addEventListeners = function () {
    var self = this;
  
    this.ws.onopen = function () {
      self.onOpen();
    };
    this.ws.onclose = function () {
      self.onClose();
    };
    this.ws.onmessage = function (ev) {
      self.onData(ev.data);
    };
    this.ws.onerror = function (e) {
      self.onError('websocket error', e);
    };
  };
  
  /**
   * Writes data to socket.
   *
   * @param {Array} array of packets.
   * @api private
   */
  
  WS.prototype.write = function (packets) {
    var self = this;
    this.writable = false;
  
    // encodePacket efficient as it uses WS framing
    // no need for encodePayload
    var total = packets.length;
    for (var i = 0, l = total; i < l; i++) {
      (function (packet) {
        parser.encodePacket(packet, self.supportsBinary, function (data) {
          if (!self.usingBrowserWebSocket) {
            // always create a new object (GH-437)
            var opts = {};
            if (packet.options) {
              opts.compress = packet.options.compress;
            }
  
            if (self.perMessageDeflate) {
              var len = 'string' === typeof data ? Buffer.byteLength(data) : data.length;
              if (len < self.perMessageDeflate.threshold) {
                opts.compress = false;
              }
            }
          }
  
          // Sometimes the websocket has already been closed but the browser didn't
          // have a chance of informing us about it yet, in that case send will
          // throw an error
          try {
            if (self.usingBrowserWebSocket) {
              // TypeError is thrown when passing the second argument on Safari
              self.ws.send(data);
            } else {
              self.ws.send(data, opts);
            }
          } catch (e) {
            debug('websocket closed before onclose event');
          }
  
          --total || done();
        });
      })(packets[i]);
    }
  
    function done () {
      self.emit('flush');
  
      // fake drain
      // defer to next tick to allow Socket to clear writeBuffer
      setTimeout(function () {
        self.writable = true;
        self.emit('drain');
      }, 0);
    }
  };
  
  /**
   * Called upon close
   *
   * @api private
   */
  
  WS.prototype.onClose = function () {
    Transport.prototype.onClose.call(this);
  };
  
  /**
   * Closes socket.
   *
   * @api private
   */
  
  WS.prototype.doClose = function () {
    if (typeof this.ws !== 'undefined') {
      this.ws.close();
    }
  };
  
  /**
   * Generates uri for connection.
   *
   * @api private
   */
  
  WS.prototype.uri = function () {
    var query = this.query || {};
    var schema = this.secure ? 'wss' : 'ws';
    var port = '';
  
    // avoid port if default for schema
    if (this.port && (('wss' === schema && Number(this.port) !== 443) ||
      ('ws' === schema && Number(this.port) !== 80))) {
      port = ':' + this.port;
    }
  
    // append timestamp to URI
    if (this.timestampRequests) {
      query[this.timestampParam] = yeast();
    }
  
    // communicate binary support capabilities
    if (!this.supportsBinary) {
      query.b64 = 1;
    }
  
    query = parseqs.encode(query);
  
    // prepend ? to query
    if (query.length) {
      query = '?' + query;
    }
  
    var ipv6 = this.hostname.indexOf(':') !== -1;
    return schema + '://' + (ipv6 ? '[' + this.hostname + ']' : this.hostname) + port + this.path + query;
  };
  
  /**
   * Feature detection for WebSocket.
   *
   * @return {Boolean} whether this transport is available.
   * @api public
   */
  
  WS.prototype.check = function () {
    return !!WebSocketImpl && !('__initialize' in WebSocketImpl && this.name === WS.prototype.name);
  };
  
  /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../../../../node-libs-browser/node_modules/buffer/index.js */ "./node_modules/node-libs-browser/node_modules/buffer/index.js").Buffer))
  
  /***/ }),
  
  /***/ "./node_modules/socket.io-client/node_modules/engine.io-client/lib/xmlhttprequest.js":
  /*!*******************************************************************************************!*\
    !*** ./node_modules/socket.io-client/node_modules/engine.io-client/lib/xmlhttprequest.js ***!
    \*******************************************************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  // browser shim for xmlhttprequest module
  
  var hasCORS = __webpack_require__(/*! has-cors */ "./node_modules/has-cors/index.js");
  
  module.exports = function (opts) {
    var xdomain = opts.xdomain;
  
    // scheme must be same when usign XDomainRequest
    // http://blogs.msdn.com/b/ieinternals/archive/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds.aspx
    var xscheme = opts.xscheme;
  
    // XDomainRequest has a flow of not sending cookie, therefore it should be disabled as a default.
    // https://github.com/Automattic/engine.io-client/pull/217
    var enablesXDR = opts.enablesXDR;
  
    // XMLHttpRequest can be disabled on IE
    try {
      if ('undefined' !== typeof XMLHttpRequest && (!xdomain || hasCORS)) {
        return new XMLHttpRequest();
      }
    } catch (e) { }
  
    // Use XDomainRequest for IE8 if enablesXDR is true
    // because loading bar keeps flashing when using jsonp-polling
    // https://github.com/yujiosaka/socke.io-ie8-loading-example
    try {
      if ('undefined' !== typeof XDomainRequest && !xscheme && enablesXDR) {
        return new XDomainRequest();
      }
    } catch (e) { }
  
    if (!xdomain) {
      try {
        return new self[['Active'].concat('Object').join('X')]('Microsoft.XMLHTTP');
      } catch (e) { }
    }
  };
  
  
  /***/ }),
  
  /***/ "./node_modules/socket.io-client/node_modules/isarray/index.js":
  /*!*********************************************************************!*\
    !*** ./node_modules/socket.io-client/node_modules/isarray/index.js ***!
    \*********************************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports) {
  
  var toString = {}.toString;
  
  module.exports = Array.isArray || function (arr) {
    return toString.call(arr) == '[object Array]';
  };
  
  
  /***/ }),
  
  /***/ "./node_modules/socket.io-client/node_modules/socket.io-parser/binary.js":
  /*!*******************************************************************************!*\
    !*** ./node_modules/socket.io-client/node_modules/socket.io-parser/binary.js ***!
    \*******************************************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  /*global Blob,File*/
  
  /**
   * Module requirements
   */
  
  var isArray = __webpack_require__(/*! isarray */ "./node_modules/socket.io-client/node_modules/isarray/index.js");
  var isBuf = __webpack_require__(/*! ./is-buffer */ "./node_modules/socket.io-client/node_modules/socket.io-parser/is-buffer.js");
  var toString = Object.prototype.toString;
  var withNativeBlob = typeof Blob === 'function' || (typeof Blob !== 'undefined' && toString.call(Blob) === '[object BlobConstructor]');
  var withNativeFile = typeof File === 'function' || (typeof File !== 'undefined' && toString.call(File) === '[object FileConstructor]');
  
  /**
   * Replaces every Buffer | ArrayBuffer in packet with a numbered placeholder.
   * Anything with blobs or files should be fed through removeBlobs before coming
   * here.
   *
   * @param {Object} packet - socket.io event packet
   * @return {Object} with deconstructed packet and list of buffers
   * @api public
   */
  
  exports.deconstructPacket = function(packet) {
    var buffers = [];
    var packetData = packet.data;
    var pack = packet;
    pack.data = _deconstructPacket(packetData, buffers);
    pack.attachments = buffers.length; // number of binary 'attachments'
    return {packet: pack, buffers: buffers};
  };
  
  function _deconstructPacket(data, buffers) {
    if (!data) return data;
  
    if (isBuf(data)) {
      var placeholder = { _placeholder: true, num: buffers.length };
      buffers.push(data);
      return placeholder;
    } else if (isArray(data)) {
      var newData = new Array(data.length);
      for (var i = 0; i < data.length; i++) {
        newData[i] = _deconstructPacket(data[i], buffers);
      }
      return newData;
    } else if (typeof data === 'object' && !(data instanceof Date)) {
      var newData = {};
      for (var key in data) {
        newData[key] = _deconstructPacket(data[key], buffers);
      }
      return newData;
    }
    return data;
  }
  
  /**
   * Reconstructs a binary packet from its placeholder packet and buffers
   *
   * @param {Object} packet - event packet with placeholders
   * @param {Array} buffers - binary buffers to put in placeholder positions
   * @return {Object} reconstructed packet
   * @api public
   */
  
  exports.reconstructPacket = function(packet, buffers) {
    packet.data = _reconstructPacket(packet.data, buffers);
    packet.attachments = undefined; // no longer useful
    return packet;
  };
  
  function _reconstructPacket(data, buffers) {
    if (!data) return data;
  
    if (data && data._placeholder) {
      return buffers[data.num]; // appropriate buffer (should be natural order anyway)
    } else if (isArray(data)) {
      for (var i = 0; i < data.length; i++) {
        data[i] = _reconstructPacket(data[i], buffers);
      }
    } else if (typeof data === 'object') {
      for (var key in data) {
        data[key] = _reconstructPacket(data[key], buffers);
      }
    }
  
    return data;
  }
  
  /**
   * Asynchronously removes Blobs or Files from data via
   * FileReader's readAsArrayBuffer method. Used before encoding
   * data as msgpack. Calls callback with the blobless data.
   *
   * @param {Object} data
   * @param {Function} callback
   * @api private
   */
  
  exports.removeBlobs = function(data, callback) {
    function _removeBlobs(obj, curKey, containingObject) {
      if (!obj) return obj;
  
      // convert any blob
      if ((withNativeBlob && obj instanceof Blob) ||
          (withNativeFile && obj instanceof File)) {
        pendingBlobs++;
  
        // async filereader
        var fileReader = new FileReader();
        fileReader.onload = function() { // this.result == arraybuffer
          if (containingObject) {
            containingObject[curKey] = this.result;
          }
          else {
            bloblessData = this.result;
          }
  
          // if nothing pending its callback time
          if(! --pendingBlobs) {
            callback(bloblessData);
          }
        };
  
        fileReader.readAsArrayBuffer(obj); // blob -> arraybuffer
      } else if (isArray(obj)) { // handle array
        for (var i = 0; i < obj.length; i++) {
          _removeBlobs(obj[i], i, obj);
        }
      } else if (typeof obj === 'object' && !isBuf(obj)) { // and object
        for (var key in obj) {
          _removeBlobs(obj[key], key, obj);
        }
      }
    }
  
    var pendingBlobs = 0;
    var bloblessData = data;
    _removeBlobs(bloblessData);
    if (!pendingBlobs) {
      callback(bloblessData);
    }
  };
  
  
  /***/ }),
  
  /***/ "./node_modules/socket.io-client/node_modules/socket.io-parser/index.js":
  /*!******************************************************************************!*\
    !*** ./node_modules/socket.io-client/node_modules/socket.io-parser/index.js ***!
    \******************************************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  
  /**
   * Module dependencies.
   */
  
  var debug = __webpack_require__(/*! debug */ "./node_modules/socket.io-client/node_modules/debug/src/browser.js")('socket.io-parser');
  var Emitter = __webpack_require__(/*! component-emitter */ "./node_modules/component-emitter/index.js");
  var binary = __webpack_require__(/*! ./binary */ "./node_modules/socket.io-client/node_modules/socket.io-parser/binary.js");
  var isArray = __webpack_require__(/*! isarray */ "./node_modules/socket.io-client/node_modules/isarray/index.js");
  var isBuf = __webpack_require__(/*! ./is-buffer */ "./node_modules/socket.io-client/node_modules/socket.io-parser/is-buffer.js");
  
  /**
   * Protocol version.
   *
   * @api public
   */
  
  exports.protocol = 4;
  
  /**
   * Packet types.
   *
   * @api public
   */
  
  exports.types = [
    'CONNECT',
    'DISCONNECT',
    'EVENT',
    'ACK',
    'ERROR',
    'BINARY_EVENT',
    'BINARY_ACK'
  ];
  
  /**
   * Packet type `connect`.
   *
   * @api public
   */
  
  exports.CONNECT = 0;
  
  /**
   * Packet type `disconnect`.
   *
   * @api public
   */
  
  exports.DISCONNECT = 1;
  
  /**
   * Packet type `event`.
   *
   * @api public
   */
  
  exports.EVENT = 2;
  
  /**
   * Packet type `ack`.
   *
   * @api public
   */
  
  exports.ACK = 3;
  
  /**
   * Packet type `error`.
   *
   * @api public
   */
  
  exports.ERROR = 4;
  
  /**
   * Packet type 'binary event'
   *
   * @api public
   */
  
  exports.BINARY_EVENT = 5;
  
  /**
   * Packet type `binary ack`. For acks with binary arguments.
   *
   * @api public
   */
  
  exports.BINARY_ACK = 6;
  
  /**
   * Encoder constructor.
   *
   * @api public
   */
  
  exports.Encoder = Encoder;
  
  /**
   * Decoder constructor.
   *
   * @api public
   */
  
  exports.Decoder = Decoder;
  
  /**
   * A socket.io Encoder instance
   *
   * @api public
   */
  
  function Encoder() {}
  
  var ERROR_PACKET = exports.ERROR + '"encode error"';
  
  /**
   * Encode a packet as a single string if non-binary, or as a
   * buffer sequence, depending on packet type.
   *
   * @param {Object} obj - packet object
   * @param {Function} callback - function to handle encodings (likely engine.write)
   * @return Calls callback with Array of encodings
   * @api public
   */
  
  Encoder.prototype.encode = function(obj, callback){
    debug('encoding packet %j', obj);
  
    if (exports.BINARY_EVENT === obj.type || exports.BINARY_ACK === obj.type) {
      encodeAsBinary(obj, callback);
    } else {
      var encoding = encodeAsString(obj);
      callback([encoding]);
    }
  };
  
  /**
   * Encode packet as string.
   *
   * @param {Object} packet
   * @return {String} encoded
   * @api private
   */
  
  function encodeAsString(obj) {
  
    // first is type
    var str = '' + obj.type;
  
    // attachments if we have them
    if (exports.BINARY_EVENT === obj.type || exports.BINARY_ACK === obj.type) {
      str += obj.attachments + '-';
    }
  
    // if we have a namespace other than `/`
    // we append it followed by a comma `,`
    if (obj.nsp && '/' !== obj.nsp) {
      str += obj.nsp + ',';
    }
  
    // immediately followed by the id
    if (null != obj.id) {
      str += obj.id;
    }
  
    // json data
    if (null != obj.data) {
      var payload = tryStringify(obj.data);
      if (payload !== false) {
        str += payload;
      } else {
        return ERROR_PACKET;
      }
    }
  
    debug('encoded %j as %s', obj, str);
    return str;
  }
  
  function tryStringify(str) {
    try {
      return JSON.stringify(str);
    } catch(e){
      return false;
    }
  }
  
  /**
   * Encode packet as 'buffer sequence' by removing blobs, and
   * deconstructing packet into object with placeholders and
   * a list of buffers.
   *
   * @param {Object} packet
   * @return {Buffer} encoded
   * @api private
   */
  
  function encodeAsBinary(obj, callback) {
  
    function writeEncoding(bloblessData) {
      var deconstruction = binary.deconstructPacket(bloblessData);
      var pack = encodeAsString(deconstruction.packet);
      var buffers = deconstruction.buffers;
  
      buffers.unshift(pack); // add packet info to beginning of data list
      callback(buffers); // write all the buffers
    }
  
    binary.removeBlobs(obj, writeEncoding);
  }
  
  /**
   * A socket.io Decoder instance
   *
   * @return {Object} decoder
   * @api public
   */
  
  function Decoder() {
    this.reconstructor = null;
  }
  
  /**
   * Mix in `Emitter` with Decoder.
   */
  
  Emitter(Decoder.prototype);
  
  /**
   * Decodes an encoded packet string into packet JSON.
   *
   * @param {String} obj - encoded packet
   * @return {Object} packet
   * @api public
   */
  
  Decoder.prototype.add = function(obj) {
    var packet;
    if (typeof obj === 'string') {
      packet = decodeString(obj);
      if (exports.BINARY_EVENT === packet.type || exports.BINARY_ACK === packet.type) { // binary packet's json
        this.reconstructor = new BinaryReconstructor(packet);
  
        // no attachments, labeled binary but no binary data to follow
        if (this.reconstructor.reconPack.attachments === 0) {
          this.emit('decoded', packet);
        }
      } else { // non-binary full packet
        this.emit('decoded', packet);
      }
    } else if (isBuf(obj) || obj.base64) { // raw binary data
      if (!this.reconstructor) {
        throw new Error('got binary data when not reconstructing a packet');
      } else {
        packet = this.reconstructor.takeBinaryData(obj);
        if (packet) { // received final buffer
          this.reconstructor = null;
          this.emit('decoded', packet);
        }
      }
    } else {
      throw new Error('Unknown type: ' + obj);
    }
  };
  
  /**
   * Decode a packet String (JSON data)
   *
   * @param {String} str
   * @return {Object} packet
   * @api private
   */
  
  function decodeString(str) {
    var i = 0;
    // look up type
    var p = {
      type: Number(str.charAt(0))
    };
  
    if (null == exports.types[p.type]) {
      return error('unknown packet type ' + p.type);
    }
  
    // look up attachments if type binary
    if (exports.BINARY_EVENT === p.type || exports.BINARY_ACK === p.type) {
      var buf = '';
      while (str.charAt(++i) !== '-') {
        buf += str.charAt(i);
        if (i == str.length) break;
      }
      if (buf != Number(buf) || str.charAt(i) !== '-') {
        throw new Error('Illegal attachments');
      }
      p.attachments = Number(buf);
    }
  
    // look up namespace (if any)
    if ('/' === str.charAt(i + 1)) {
      p.nsp = '';
      while (++i) {
        var c = str.charAt(i);
        if (',' === c) break;
        p.nsp += c;
        if (i === str.length) break;
      }
    } else {
      p.nsp = '/';
    }
  
    // look up id
    var next = str.charAt(i + 1);
    if ('' !== next && Number(next) == next) {
      p.id = '';
      while (++i) {
        var c = str.charAt(i);
        if (null == c || Number(c) != c) {
          --i;
          break;
        }
        p.id += str.charAt(i);
        if (i === str.length) break;
      }
      p.id = Number(p.id);
    }
  
    // look up json data
    if (str.charAt(++i)) {
      var payload = tryParse(str.substr(i));
      var isPayloadValid = payload !== false && (p.type === exports.ERROR || isArray(payload));
      if (isPayloadValid) {
        p.data = payload;
      } else {
        return error('invalid payload');
      }
    }
  
    debug('decoded %s as %j', str, p);
    return p;
  }
  
  function tryParse(str) {
    try {
      return JSON.parse(str);
    } catch(e){
      return false;
    }
  }
  
  /**
   * Deallocates a parser's resources
   *
   * @api public
   */
  
  Decoder.prototype.destroy = function() {
    if (this.reconstructor) {
      this.reconstructor.finishedReconstruction();
    }
  };
  
  /**
   * A manager of a binary event's 'buffer sequence'. Should
   * be constructed whenever a packet of type BINARY_EVENT is
   * decoded.
   *
   * @param {Object} packet
   * @return {BinaryReconstructor} initialized reconstructor
   * @api private
   */
  
  function BinaryReconstructor(packet) {
    this.reconPack = packet;
    this.buffers = [];
  }
  
  /**
   * Method to be called when binary data received from connection
   * after a BINARY_EVENT packet.
   *
   * @param {Buffer | ArrayBuffer} binData - the raw binary data received
   * @return {null | Object} returns null if more binary data is expected or
   *   a reconstructed packet object if all buffers have been received.
   * @api private
   */
  
  BinaryReconstructor.prototype.takeBinaryData = function(binData) {
    this.buffers.push(binData);
    if (this.buffers.length === this.reconPack.attachments) { // done with buffer list
      var packet = binary.reconstructPacket(this.reconPack, this.buffers);
      this.finishedReconstruction();
      return packet;
    }
    return null;
  };
  
  /**
   * Cleans up binary packet reconstruction variables.
   *
   * @api private
   */
  
  BinaryReconstructor.prototype.finishedReconstruction = function() {
    this.reconPack = null;
    this.buffers = [];
  };
  
  function error(msg) {
    return {
      type: exports.ERROR,
      data: 'parser error: ' + msg
    };
  }
  
  
  /***/ }),
  
  /***/ "./node_modules/socket.io-client/node_modules/socket.io-parser/is-buffer.js":
  /*!**********************************************************************************!*\
    !*** ./node_modules/socket.io-client/node_modules/socket.io-parser/is-buffer.js ***!
    \**********************************************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  /* WEBPACK VAR INJECTION */(function(Buffer) {
  module.exports = isBuf;
  
  var withNativeBuffer = typeof Buffer === 'function' && typeof Buffer.isBuffer === 'function';
  var withNativeArrayBuffer = typeof ArrayBuffer === 'function';
  
  var isView = function (obj) {
    return typeof ArrayBuffer.isView === 'function' ? ArrayBuffer.isView(obj) : (obj.buffer instanceof ArrayBuffer);
  };
  
  /**
   * Returns true if obj is a buffer or an arraybuffer.
   *
   * @api private
   */
  
  function isBuf(obj) {
    return (withNativeBuffer && Buffer.isBuffer(obj)) ||
            (withNativeArrayBuffer && (obj instanceof ArrayBuffer || isView(obj)));
  }
  
  /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../../node-libs-browser/node_modules/buffer/index.js */ "./node_modules/node-libs-browser/node_modules/buffer/index.js").Buffer))
  
  /***/ }),
  
  /***/ "./node_modules/strict-uri-encode/index.js":
  /*!*************************************************!*\
    !*** ./node_modules/strict-uri-encode/index.js ***!
    \*************************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  "use strict";
  
  module.exports = str => encodeURIComponent(str).replace(/[!'()*]/g, x => `%${x.charCodeAt(0).toString(16).toUpperCase()}`);
  
  
  /***/ }),
  
  /***/ "./node_modules/to-array/index.js":
  /*!****************************************!*\
    !*** ./node_modules/to-array/index.js ***!
    \****************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports) {
  
  module.exports = toArray
  
  function toArray(list, index) {
      var array = []
  
      index = index || 0
  
      for (var i = index || 0; i < list.length; i++) {
          array[i - index] = list[i]
      }
  
      return array
  }
  
  
  /***/ }),
  
  /***/ "./node_modules/webpack/buildin/global.js":
  /*!***********************************!*\
    !*** (webpack)/buildin/global.js ***!
    \***********************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports) {
  
  var g;
  
  // This works in non-strict mode
  g = (function() {
  	return this;
  })();
  
  try {
  	// This works if eval is allowed (see CSP)
  	g = g || Function("return this")() || (1, eval)("this");
  } catch (e) {
  	// This works if the window reference is available
  	if (typeof window === "object") g = window;
  }
  
  // g can still be undefined, but nothing to do about it...
  // We return undefined, instead of nothing here, so it's
  // easier to handle this case. if(!global) { ...}
  
  module.exports = g;
  
  
  /***/ }),
  
  /***/ "./node_modules/webpack/buildin/module.js":
  /*!***********************************!*\
    !*** (webpack)/buildin/module.js ***!
    \***********************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports) {
  
  module.exports = function(module) {
  	if (!module.webpackPolyfill) {
  		module.deprecate = function() {};
  		module.paths = [];
  		// module.parent = undefined by default
  		if (!module.children) module.children = [];
  		Object.defineProperty(module, "loaded", {
  			enumerable: true,
  			get: function() {
  				return module.l;
  			}
  		});
  		Object.defineProperty(module, "id", {
  			enumerable: true,
  			get: function() {
  				return module.i;
  			}
  		});
  		module.webpackPolyfill = 1;
  	}
  	return module;
  };
  
  
  /***/ }),
  
  /***/ "./node_modules/yeast/index.js":
  /*!*************************************!*\
    !*** ./node_modules/yeast/index.js ***!
    \*************************************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports, __webpack_require__) {
  
  "use strict";
  
  
  var alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_'.split('')
    , length = 64
    , map = {}
    , seed = 0
    , i = 0
    , prev;
  
  /**
   * Return a string representing the specified number.
   *
   * @param {Number} num The number to convert.
   * @returns {String} The string representation of the number.
   * @api public
   */
  function encode(num) {
    var encoded = '';
  
    do {
      encoded = alphabet[num % length] + encoded;
      num = Math.floor(num / length);
    } while (num > 0);
  
    return encoded;
  }
  
  /**
   * Return the integer value specified by the given string.
   *
   * @param {String} str The string to convert.
   * @returns {Number} The integer value represented by the string.
   * @api public
   */
  function decode(str) {
    var decoded = 0;
  
    for (i = 0; i < str.length; i++) {
      decoded = decoded * length + map[str.charAt(i)];
    }
  
    return decoded;
  }
  
  /**
   * Yeast: A tiny growing id generator.
   *
   * @returns {String} A unique id.
   * @api public
   */
  function yeast() {
    var now = encode(+new Date());
  
    if (now !== prev) return seed = 0, prev = now;
    return now +'.'+ encode(seed++);
  }
  
  //
  // Map each character to its index.
  //
  for (; i < length; i++) map[alphabet[i]] = i;
  
  //
  // Expose the `yeast`, `encode` and `decode` functions.
  //
  yeast.encode = encode;
  yeast.decode = decode;
  module.exports = yeast;
  
  
  /***/ }),
  
  /***/ "./package.json":
  /*!**********************!*\
    !*** ./package.json ***!
    \**********************/
  /*! exports provided: name, version, description, main, module, scripts, repository, files, author, license, devDependencies, dependencies, default */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module) {
  
  module.exports = {"name":"skyway-js","version":"1.4.0","description":"The official JavaScript SDK for SkyWay","main":"dist/skyway.js","module":"src/peer.js","scripts":{"test":"karma start ./karma.conf.js","clean":"del ./dist","lint":"eslint .","build":"NODE_ENV=production webpack","dev":"webpack -w"},"repository":{"type":"git","url":"git+https://github.com/skyway/skyway-js-sdk"},"files":["dist/skyway.js","LICENSE","CHANGELOG.md","README.md"],"author":"NTT Communications Corp.","license":"MIT","devDependencies":{"babel-loader":"^7.1.4","babel-plugin-espower":"^2.4.0","babel-plugin-istanbul":"^4.1.6","babel-preset-es2015":"^6.24.1","del-cli":"^1.1.0","eslint":"^5.0.0","eslint-config-prettier":"^2.9.0","eslint-plugin-prettier":"^2.4.0","inject-loader":"^4.0.1","karma":"^2.0.0","karma-chrome-launcher":"^2.2.0","karma-coverage":"^1.1.2","karma-mocha":"^1.3.0","karma-mocha-reporter":"^2.2.5","karma-sourcemap-loader":"^0.3.7","karma-webpack":"^3.0.0","mocha":"^5.2.0","power-assert":"^1.4.4","prettier":"^1.9.2","sinon":"^6.0.1","webpack":"^4.12.1","webpack-cli":"^3.0.8"},"dependencies":{"detect-browser":"^4.2.0","enum":"git+https://github.com/eastandwest/enum.git#react-native","events":"^3.0.0","js-binarypack":"0.0.9","object-sizeof":"^1.3.0","query-string":"^6.4.0","sdp-interop":"^0.1.13","sdp-transform":"^2.7.0","socket.io-client":"^2.2.0"}};
  
  /***/ }),
  
  /***/ "./src/peer.js":
  /*!**********************************!*\
    !*** ./src/peer.js + 12 modules ***!
    \**********************************/
  /*! exports provided: default */
  /*! ModuleConcatenation bailout: Cannot concat with ./node_modules/detect-browser/index.js (<- Module is not an ECMAScript module) */
  /*! ModuleConcatenation bailout: Cannot concat with ./node_modules/enum/index.js (<- Module is not an ECMAScript module) */
  /*! ModuleConcatenation bailout: Cannot concat with ./node_modules/js-binarypack/lib/binarypack.js (<- Module is not an ECMAScript module) */
  /*! ModuleConcatenation bailout: Cannot concat with ./node_modules/node-libs-browser/node_modules/events/events.js (<- Module is not an ECMAScript module) */
  /*! ModuleConcatenation bailout: Cannot concat with ./node_modules/object-sizeof/index.js (<- Module is not an ECMAScript module) */
  /*! ModuleConcatenation bailout: Cannot concat with ./node_modules/query-string/index.js (<- Module is not an ECMAScript module) */
  /*! ModuleConcatenation bailout: Cannot concat with ./node_modules/sdp-interop/lib/index.js (<- Module is not an ECMAScript module) */
  /*! ModuleConcatenation bailout: Cannot concat with ./node_modules/sdp-transform/lib/index.js (<- Module is not an ECMAScript module) */
  /*! ModuleConcatenation bailout: Cannot concat with ./node_modules/socket.io-client/lib/index.js (<- Module is not an ECMAScript module) */
  /*! ModuleConcatenation bailout: Cannot concat with ./package.json (<- Module is not an ECMAScript module) */
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
  
  "use strict";
  
  // EXTERNAL MODULE: ./node_modules/node-libs-browser/node_modules/events/events.js
  var events = __webpack_require__("./node_modules/node-libs-browser/node_modules/events/events.js");
  var events_default = /*#__PURE__*/__webpack_require__.n(events);
  
  // EXTERNAL MODULE: ./node_modules/enum/index.js
  var node_modules_enum = __webpack_require__("./node_modules/enum/index.js");
  var enum_default = /*#__PURE__*/__webpack_require__.n(node_modules_enum);
  
  // EXTERNAL MODULE: ./node_modules/socket.io-client/lib/index.js
  var lib = __webpack_require__("./node_modules/socket.io-client/lib/index.js");
  var lib_default = /*#__PURE__*/__webpack_require__.n(lib);
  
  // EXTERNAL MODULE: ./node_modules/query-string/index.js
  var query_string = __webpack_require__("./node_modules/query-string/index.js");
  var query_string_default = /*#__PURE__*/__webpack_require__.n(query_string);
  
  // CONCATENATED MODULE: ./src/shared/config.js
  
  
  const DISPATCHER_HOST = 'dispatcher.webrtc.ecl.ntt.com';
  const DISPATCHER_PORT = 443;
  const DISPATCHER_SECURE = true;
  const DISPATCHER_TIMEOUT = 10000;
  
  const TURN_HOST = 'turn.webrtc.ecl.ntt.com';
  const TURN_PORT = 443;
  
  const MESSAGE_TYPES = {
    CLIENT: new enum_default.a([
      'SEND_OFFER',
      'SEND_ANSWER',
      'SEND_CANDIDATE',
      'SEND_LEAVE',
      'ROOM_JOIN',
      'ROOM_LEAVE',
      'ROOM_GET_LOGS',
      'ROOM_GET_USERS',
      'ROOM_SEND_DATA',
      'SFU_GET_OFFER',
      'SFU_ANSWER',
      'SFU_CANDIDATE',
      'PING',
      'UPDATE_CREDENTIAL',
      'SEND_FORCE_CLOSE',
    ]),
    SERVER: new enum_default.a([
      'OPEN',
      'ERROR',
      'OFFER',
      'ANSWER',
      'CANDIDATE',
      'LEAVE',
      'AUTH_EXPIRES_IN',
      'ROOM_LOGS',
      'ROOM_USERS',
      'ROOM_DATA',
      'ROOM_USER_JOIN',
      'ROOM_USER_LEAVE',
      'SFU_OFFER',
      'FORCE_CLOSE',
    ]),
  };
  
  // Current recommended maximum chunksize is 16KB (DataChannel spec)
  // https://tools.ietf.org/html/draft-ietf-rtcweb-data-channel-13
  // The actual chunk size is adjusted in dataChannel to accomodate metaData
  const maxChunkSize = 16300;
  
  // Number of reconnection attempts to the same server before giving up
  const reconnectionAttempts = 2;
  
  // Number of times to try changing servers before giving up
  const numberServersToTry = 3;
  
  // Send loop interval in milliseconds
  const sendInterval = 1;
  
  // Ping interval in milliseconds
  const pingInterval = 25000;
  
  const defaultConfig = {
    iceServers: [
      {
        urls: 'stun:stun.webrtc.ecl.ntt.com:3478',
        url: 'stun:stun.webrtc.ecl.ntt.com:3478',
      },
    ],
    iceTransportPolicy: 'all',
  };
  
  /* harmony default export */ var config = ({
    DISPATCHER_HOST,
    DISPATCHER_PORT,
    DISPATCHER_SECURE,
    DISPATCHER_TIMEOUT,
    TURN_HOST,
    TURN_PORT,
    MESSAGE_TYPES,
    maxChunkSize,
    reconnectionAttempts,
    numberServersToTry,
    sendInterval,
    pingInterval,
    defaultConfig,
  });
  
  // CONCATENATED MODULE: ./src/shared/logger.js
  
  
  const LOG_PREFIX = 'SkyWay: ';
  const LogLevel = new enum_default.a({
    NONE: 0,
    ERROR: 1,
    WARN: 2,
    FULL: 3,
  });
  
  /**
   * Class for logging
   * This class exports only one instance(a.k.a. Singleton)
   */
  class Logger {
    /**
     * Create a Logger instance.
     *
     */
    constructor() {
      this._logLevel = LogLevel.NONE.value;
      this.LOG_LEVELS = LogLevel;
    }
  
    /**
     * Set the level of log.
     * @param {number} [level=0] The log level. 0: NONE, 1: ERROR, 2: WARN, 3:FULL.
     */
    setLogLevel(level) {
      if (level.value) {
        level = level.value;
      }
  
      const debugLevel = parseInt(level, 10);
  
      switch (debugLevel) {
        case 0:
          this._logLevel = LogLevel.NONE.value;
          break;
        case 1:
          this._logLevel = LogLevel.ERROR.value;
          break;
        case 2:
          this._logLevel = LogLevel.WARN.value;
          break;
        case 3:
          this._logLevel = LogLevel.FULL.value;
          break;
        default:
          this._logLevel = LogLevel.NONE.value;
          break;
      }
    }
  
    /**
     * Output a warning message to the Web Console.
     * @param {...*} args - arguments to warn.
     */
    warn(...args) {
      if (this._logLevel >= LogLevel.WARN.value) {
        console.warn(LOG_PREFIX, ...args);
      }
    }
  
    /**
     * Output an error message to the Web Console.
     * @param {...*} args - arguments to error.
     */
    error(...args) {
      if (this._logLevel >= LogLevel.ERROR.value) {
        console.error(LOG_PREFIX, ...args);
      }
    }
  
    /**
     * Output a log message to the Web Console.
     * @param {...*} args - arguments to log.
     */
    log(...args) {
      if (this._logLevel >= LogLevel.FULL.value) {
        console.log(LOG_PREFIX, ...args);
      }
    }
  }
  
  /* harmony default export */ var logger = (new Logger());
  
  // EXTERNAL MODULE: ./package.json
  var package_0 = __webpack_require__("./package.json");
  
  // CONCATENATED MODULE: ./src/peer/socket.js
  
  
  
  
  
  
  
  
  
  /**
   * Class to handle WS/HTTP communication with the signalling server
   * @extends EventEmitter
   */
  class socket_Socket extends events_default.a {
    /**
     * Creates an instance of Socket.
     * @param {string} key - The apiKey to connect using.
     * @param {Object} options - Socket connection options.
     * @param {boolean} options.secure - True if signalling server supports HTTPS/WSS.
     * @param {string} options.host - The signalling server host.
     * @param {number | string} options.port - The port the signalling server is listening to.
     * @param {boolean} options.dispatcherSecure - True if dispatcher server supports HTTPS/WSS.
     * @param {string} options.dispatcherHost - The signalling server host.
     * @param {number | string} options.dispatcherPort - The port the signalling server is listening to.
     */
    constructor(key, options) {
      super();
  
      this._isOpen = false;
      this._isPeerIdSet = false;
      this._queue = [];
  
      this._io = null;
      this._key = key;
      this._reconnectAttempts = 0;
  
      if (options.host && options.port) {
        const httpProtocol = options.secure ? 'https://' : 'http://';
        this.signalingServerUrl = `${httpProtocol}${options.host}:${
          options.port
        }`;
      } else {
        const dispatcherHost = options.dispatcherHost || config.DISPATCHER_HOST;
        const dispatcherPort = options.dispatcherPort || config.DISPATCHER_PORT;
        const dispatcherSecure =
          options.dispatcherSecure || config.DISPATCHER_SECURE;
  
        const httpProtocol = dispatcherSecure ? 'https://' : 'http://';
        this._dispatcherUrl = `${httpProtocol}${dispatcherHost}:${dispatcherPort}/signaling`;
      }
    }
  
    /**
     * Whether the socket is connecting to the signalling server or not.
     * @type {boolean}
     */
    get isOpen() {
      return Boolean(this._io && this._io.connected && this._isOpen);
    }
  
    /**
     * Connect to the signalling server.
     * @param {string} id - Unique peerId to identify the client.
     * @param {string} token - Token to identify the session.
     * @param {object} credential - The credential used to authenticate peer.
     * @param {number} [credential.timestamp] - Current UNIX timestamp.
     + @param {number} [credential.ttl] - Time to live; The credential expires at timestamp + ttl.
     + @param {string} [credential.authToken] - Credential token calculated with HMAC.
     * @return {Promise<void>} Promise that resolves when starting is done.
     * @fires Socket#error
     */
    async start(id, token, credential) {
      let query =
        `apiKey=${this._key}&token=${token}` +
        `&platform=javascript&sdk_version=${package_0["version"]}`;
  
      if (id) {
        query += `&peerId=${id}`;
        this._isPeerIdSet = true;
      }
  
      if (credential) {
        const encodedCredentialStr = encodeURIComponent(
          JSON.stringify(credential)
        );
        query += `&credential=${encodedCredentialStr}`;
      }
  
      if (this._dispatcherUrl) {
        let serverInfo;
        try {
          serverInfo = await this._getSignalingServer();
        } catch (err) {
          this.emit('error', err);
          return;
        }
        const httpProtocol = serverInfo.secure ? 'https://' : 'http://';
        this.signalingServerUrl = `${httpProtocol}${serverInfo.host}:${
          serverInfo.port
        }`;
      }
  
      this._io = lib_default()(this.signalingServerUrl, {
        'force new connection': true,
        query: query,
        reconnectionAttempts: config.reconnectionAttempts,
      });
  
      this._io.on('reconnect_failed', () => {
        this._stopPings();
        this._connectToNewServer();
      });
  
      this._io.on('error', e => {
        logger.error(e);
      });
  
      this._setupMessageHandlers();
    }
  
    /**
     * Connect to "new" signaling server. Attempts up to 10 times before giving up and emitting an error on the socket.
     * @param {number} [numAttempts=0] - Current number of attempts.
     * @return {Promise<void>} A promise that resolves with new connection has done.
     * @private
     */
    async _connectToNewServer(numAttempts = 0) {
      // max number of attempts to get a new server from the dispatcher.
      const maxNumberOfAttempts = 10;
      if (
        numAttempts >= maxNumberOfAttempts ||
        this._reconnectAttempts >= config.numberServersToTry
      ) {
        this.emit('error', 'Could not connect to server.');
        return;
      }
  
      // Keep trying until we connect to a new server because consul can take some time to remove from the active list.
      let serverInfo;
      try {
        serverInfo = await this._getSignalingServer();
      } catch (err) {
        this.emit('error', err);
        return;
      }
  
      if (this.signalingServerUrl.indexOf(serverInfo.host) === -1) {
        const httpProtocol = serverInfo.secure ? 'https://' : 'http://';
        this.signalingServerUrl = `${httpProtocol}${serverInfo.host}:${
          serverInfo.port
        }`;
  
        this._io.io.uri = this.signalingServerUrl;
        this._io.connect();
        this._reconnectAttempts++;
      } else {
        this._connectToNewServer(++numAttempts);
      }
    }
  
    /**
     * Return object including signaling server info.
     * @return {Promise<Object>} A promise that resolves with signaling server info
     and rejects if there's no response or status code isn't 200.
     */
    _getSignalingServer() {
      return new Promise((resolve, reject) => {
        const http = new XMLHttpRequest();
  
        http.timeout = config.DISPATCHER_TIMEOUT;
        http.open('GET', this._dispatcherUrl, true);
  
        /* istanbul ignore next */
        http.onerror = () => {
          reject(new Error('There was a problem with the dispatcher.'));
        };
  
        http.onabort = () => {
          reject(new Error('The request was aborted.'));
        };
  
        http.ontimeout = () => {
          reject(new Error('The request for the dispather timed out.'));
        };
  
        http.onreadystatechange = () => {
          if (http.readyState !== 4) {
            return;
          }
  
          // maybe aborted or something
          if (http.status === 0) {
            reject(new Error('There was a problem with the dispatcher.'));
            return;
          }
  
          let res = null;
          try {
            res = JSON.parse(http.responseText);
          } catch (err) {
            reject(
              new Error(
                'The dispatcher server returned an invalid JSON response.'
              )
            );
            return;
          }
  
          if (http.status === 200) {
            if (res && res.domain) {
              resolve({ host: res.domain, port: 443, secure: true });
              return;
            }
          }
  
          if (res.error && res.error.message) {
            const message = res.error.message;
            reject(new Error(message));
          } else {
            reject(new Error('There was a problem with the dispatcher.'));
          }
        };
  
        http.send(null);
      });
    }
  
    /**
     * Send a message to the signalling server. Queue the messages if not connected yet.
     * @param {string} type - The signalling message type. Message types are defined in config.MESSAGE_TYPES.
     * @param {string | object} message - The message to send to the server.
     */
    send(type, message) {
      if (!type) {
        this._io.emit('error', 'Invalid message');
        return;
      }
  
      // If we are not connected yet, queue the message
      if (!this.isOpen) {
        this._queue.push({ type: type, message: message });
        return;
      }
  
      if (this._io.connected === true) {
        this._io.emit(type, message);
      }
    }
  
    /**
     * Disconnect from the signalling server.
     */
    close() {
      if (this.isOpen) {
        this._stopPings();
        this._io.disconnect();
        this._isOpen = false;
      }
    }
  
    /**
     * Reconnect to the signaling server.
     */
    reconnect() {
      this._io.connect();
    }
  
    /**
     * Update Credential by sending the new credential to the signaling server.
     * Also set the new one to the Socket.io.opts's query string for reconnection.
     * @param {object} newCredential - The new credential generated by user.
     * @param {number} [newCredential.timestamp] - Current UNIX timestamp.
     + @param {number} [newCredential.ttl] - Time to live; The credential expires at timestamp + ttl.
     + @param {string} [newCredential.authToken] - Credential token calculated with HMAC.
     */
    updateCredential(newCredential) {
      // Parse the current queryString and replace the new credential with old one
      const parseQuery = query_string_default.a.parse(this._io.io.opts.query);
      if (parseQuery.credential) {
        parseQuery.credential = encodeURIComponent(JSON.stringify(newCredential));
      } else {
        // For future development; here we can tell the the developer
        // which connection(p2p/turn/sfu) should be authenticated.
        logger.warn("Adding a credential when one wasn't specified before.");
      }
      this._io.io.opts.query = query_string_default.a.stringify(parseQuery);
  
      this.send(config.MESSAGE_TYPES.CLIENT.UPDATE_CREDENTIAL.key, newCredential);
    }
  
    /**
     * Set up the signalling message handlers.
     * @private
     * @fires Socket#OPEN
     * @fires Socket#OFFER
     * @fires Socket#ANSWER
     * @fires Socket#CANDIDATE
     * @fires Socket#LEAVE
     * @fires Socket#AUTH_EXPIRES_IN
     * @fires Socket#ROOM_OFFER
     * @fires Socket#ROOM_USER_JOIN
     * @fires Socket#ROOM_USER_LEAVE
     * @fires Socket#ROOM_DATA
     * @fires Socket#FORCE_CLOSE
     */
    _setupMessageHandlers() {
      config.MESSAGE_TYPES.SERVER.enums.forEach(type => {
        if (type.key === config.MESSAGE_TYPES.SERVER.OPEN.key) {
          this._io.on(type.key, openMessage => {
            if (!openMessage || !openMessage.peerId) {
              return;
            }
            if (!this._isPeerIdSet) {
              // set peerId for when reconnecting to the server
              this._io.io.opts.query += `&peerId=${openMessage.peerId}`;
              this._isPeerIdSet = true;
            }
            this._reconnectAttempts = 0;
  
            this._startPings();
            this._sendQueuedMessages();
  
            if (!this._isOpen) {
              this._isOpen = true;
  
              // To inform the peer that the socket successfully connected
              this.emit(type.key, openMessage);
            }
          });
        } else {
          this._io.on(type.key, message => {
            this.emit(type.key, message);
          });
        }
      });
    }
  
    /**
     * Send messages that were queued when the client wasn't connected to the signalling server yet.
     * @private
     */
    _sendQueuedMessages() {
      for (const data of this._queue) {
        this.send(data.type, data.message);
      }
      this._queue = [];
    }
  
    /**
     * Start sending ping messages if they aren't already
     * @private
     */
    _startPings() {
      if (!this._pingIntervalId) {
        this._pingIntervalId = setInterval(() => {
          this.send(config.MESSAGE_TYPES.CLIENT.PING.key);
        }, config.pingInterval);
      }
    }
  
    /**
     * Stop sending ping messages
     * @private
     */
    _stopPings() {
      clearInterval(this._pingIntervalId);
      this._pingIntervalId = undefined;
    }
  
    /**
     * Error occurred.
     *
     * @event Connection#error
     * @type {Error}
     */
  
    /**
     * Socket opened.
     *
     * @event Socket#OPEN
     * @type {object}
     * @property {string} peerId - The peerId of the client.
     * @property {string} [turnCredential] - The turn credentials for this client.
     */
  
    /**
     * Signalling server error.
     *
     * @event Socket#ERROR
     * @type {string}
     */
  
    /**
     * ICE candidate received from peer.
     *
     * @event Socket#CANDIDATE
     * @type {object}
     * @property {RTCIceCandidate} candidate - The ice candidate.
     * @property {string} src - Sender peerId.
     * @property {string} dst - Recipient peerId.
     * @property {string} connectionId - The connection id.
     * @property {string} connectionType - The connection type.
     */
  
    /**
     * Offer received from peer.
     *
     * @event Socket#OFFER
     * @type {object}
     * @property {RTCSessionDescription} offer - The remote peer's offer.
     * @property {string} src - Sender peerId.
     * @property {string} dst - Recipient peerId.
     * @property {string} connectionId - The connection id.
     * @property {string} connectionType - The connection type.
     * @property {object} metadata - Any extra data sent with the connection.
     */
  
    /**
     * Answer received from peer.
     *
     * @event Socket#ANSWER
     * @type {object}
     * @property {RTCSessionDescription} answer - The remote peer's answer.
     * @property {string} src - Sender peerId.
     * @property {string} dst - Recipient peerId.
     * @property {string} connectionId - The connection id.
     * @property {string} connectionType - The connection type.
     */
  
    /**
     * Peer has left.
     *
     * @event Socket#LEAVE
     * @type {string}
     */
  
    /**
     * Message sent to peer has failed.
     *
     * @event Socket#EXPIRE
     * @type {string}
     */
  
    /**
     * Room offer sdp received.
     *
     * @event Socket#ROOM_OFFER
     * @type {object}
     * @property {string} roomName - The name of the room the offer is for.
     * @property {RTCSessionDescription} offer - The offer object.
     */
  
    /**
     * User has joined the room.
     *
     * @event Socket#ROOM_USER_JOIN
     * @type {object}
     * @property {string} src - The peerId of the user who joined the room.
     * @property {string} roomName - The name of the room joined.
     */
  
    /**
     * User has left the room.
     *
     * @event Socket#ROOM_USER_LEAVE
     * @type {object}
     * @property {string} src - The peerId of the user who left the room.
     * @property {string} roomName - The name of the room left.
     */
  
    /**
     * Received a data message from a user in a room.
     *
     * @event Socket#ROOM_DATA
     * @type {object}
     * @property {string} src - The peerId of the user who sent the message.
     * @property {string} roomName - The name of the room left.
     * @property {*} data - The data that was sent.
     */
  
    /**
     * Remote Peer requested to close a connection.
     *
     * @event Socket#FORCE_CLOSE
     * @type {object}
     * @property {string} src - Sender peerId.
     * @property {string} dst - Recipient peerId.
     * @property {string} connectionId - The connection id.
     */
  }
  
  /* harmony default export */ var socket = (socket_Socket);
  
  // EXTERNAL MODULE: ./node_modules/sdp-transform/lib/index.js
  var sdp_transform_lib = __webpack_require__("./node_modules/sdp-transform/lib/index.js");
  var sdp_transform_lib_default = /*#__PURE__*/__webpack_require__.n(sdp_transform_lib);
  
  // EXTERNAL MODULE: ./node_modules/sdp-interop/lib/index.js
  var sdp_interop_lib = __webpack_require__("./node_modules/sdp-interop/lib/index.js");
  
  // CONCATENATED MODULE: ./src/shared/sdpUtil.js
  
  
  
  /**
   * Class that contains utility functions for SDP munging.
   */
  class sdpUtil_SdpUtil {
    /**
     * Convert unified plan SDP to Plan B SDP
     * @param {RTCSessionDescriptionInit} offer unified plan SDP
     * @return {RTCSessionDescription} Plan B SDP
     */
    unifiedToPlanB(offer) {
      const interop = new sdp_interop_lib["Interop"]();
      const oldSdp = interop.toPlanB(offer).sdp;
  
      // use a set to avoid duplicates
      const msids = new Set();
      // extract msids from the offer sdp
      const msidRegexp = /a=ssrc:\d+ msid:(\w+)/g;
      let matches;
      // loop while matches is truthy
      // double parentheses for explicit conditional assignment (lint)
      while ((matches = msidRegexp.exec(oldSdp))) {
        msids.add(matches[1]);
      }
  
      // replace msid-semantic line with planB version
      const newSdp = oldSdp.replace(
        'a=msid-semantic:WMS *',
        `a=msid-semantic:WMS ${Array.from(msids).join(' ')}`
      );
  
      return new RTCSessionDescription({
        type: 'offer',
        sdp: newSdp,
      });
    }
  
    /**
     * Add b=AS to m=video section and return the SDP.
     * @param {string} sdp - A SDP.
     * @param {number} bandwidth - video Bandwidth (kbps)
     * @return {string} A SDP which include b=AS in m=video section
     */
    addVideoBandwidth(sdp, bandwidth) {
      this._validateBandwidth(bandwidth);
      return this._addBandwidth(sdp, bandwidth, 'video');
    }
  
    /**
     * Add b=AS to m=audio section and return the SDP
     * @param {string} sdp - A SDP.
     * @param {number} bandwidth - audio Bandwidth (kbps)
     * @return {string} A SDP which include b=AS in m=audio section
     */
    addAudioBandwidth(sdp, bandwidth) {
      this._validateBandwidth(bandwidth);
      return this._addBandwidth(sdp, bandwidth, 'audio');
    }
  
    /**
     * Remove video codecs in SDP except argument's codec.
     * If the codec doesn't exist, throw error.
     * @param {string} sdp - A SDP.
     * @param {string} codec - Video codec name (e.g. H264)
     * @return {string} A SDP which contains the codecs except argument's codec
     */
    filterVideoCodec(sdp, codec) {
      return this._filterCodec(sdp, codec, 'video');
    }
  
    /**
     * Remove audio codecs in SDP except argument's codec.
     * If the codec doesn't exist, throw error.
     * @param {string} sdp - A SDP.
     * @param {string} codec - Audio codec name (e.g. PCMU)
     * @return {string} A SDP which contains the codecs except argument's codec
     */
    filterAudioCodec(sdp, codec) {
      return this._filterCodec(sdp, codec, 'audio');
    }
  
    /**
     * Our signaling server determines client's SDP semantics
     * by checking answer SDP includes `a=msid-semantic:WMS *` or NOT.
     *
     * Currenly, Firefox prints exact string,
     * but Chrome does not. even using `unified-plan`.
     * Therefore Chrome needs to pretend Firefox to join SFU rooms.
     *
     * At a glance, using `sdp-transform` is better choice to munge SDP,
     * but if you do so, it prints `a=msid-semantic: WMS *`.
     * The problem is the space before the word `WMS`,
     * our signaling server can not handle this as `unified-plan` SDP...
     *
     * @param {string} sdp - A SDP.
     * @return {string} A SDP which has `a=msid-semantic:WMS *`.
     */
    pretendUnifiedPlan(sdp) {
      const delimiter = '\r\n';
      return sdp
        .split(delimiter)
        .map(line => {
          return line.startsWith('a=msid-semantic')
            ? 'a=msid-semantic:WMS *'
            : line;
        })
        .join(delimiter);
    }
  
    /**
     * Remove codecs except the codec passed as argument and return the SDP
     *
     * @param {string} sdp - A SDP.
     * @param {string} codec - The codec name, case sensitive.
     * @param {string} mediaType - 'audio' or 'video'
     * @return {string} A SDP which contains the codecs except argument's codec
     * @private
     */
    _filterCodec(sdp, codec, mediaType) {
      if (codec === undefined) {
        throw new Error('codec is not passed');
      }
  
      const sdpObject = sdp_transform_lib_default.a.parse(sdp);
  
      sdpObject.media = sdpObject.media.map(media => {
        if (media.type === mediaType) {
          media.rtp = media.rtp.filter(rtp => {
            return rtp.codec === codec;
          });
  
          // Extract the payload number into Array, like [126, 97];
          // Note, there are cases the length of Array is more than 2.
          //   e.g. Firefox generates two 'H264' video codecs: 126, 97;
          //   e.g. Chrome generates three 'CN' audio codecs:  106, 105, 13;
          const payloadNumbers = media.rtp.reduce((prev, curr) => {
            return [...prev, curr.payload];
          }, []);
  
          // At this point, 0 means there's no codec, so let's throw Error.
          if (media.rtp.length === 0) {
            throw new Error(`${codec} does not exist`);
          }
  
          // fmtp is optional though most codecs have this parameter.
          if (media.fmtp) {
            media.fmtp = media.fmtp.filter(fmtp => {
              return payloadNumbers.includes(fmtp.payload);
            });
          }
  
          // rtcpFb is optional. Especially, m=audio doesn't have rtcpFb.
          if (media.rtcpFb) {
            media.rtcpFb = media.rtcpFb.filter(rtcpFb => {
              return payloadNumbers.includes(rtcpFb.payload);
            });
          }
  
          media.payloads = payloadNumbers.join(' ');
        }
        return media;
      });
  
      return sdp_transform_lib_default.a.write(sdpObject);
    }
  
    /**
     * Add b=AS to 'm=audio' or 'm=video' section and return the SDP
     *
     * @param {string} sdp - A SDP.
     * @param {number} bandwidth - bandidth of 'audio' or 'video'
     * @param {string} mediaType - 'audio' or 'video'
     * @return {string} A SDP which include b=AS in m=audio or m=video section
     * @private
     */
    _addBandwidth(sdp, bandwidth, mediaType) {
      const sdpObject = sdp_transform_lib_default.a.parse(sdp);
      sdpObject.media = sdpObject.media.map(media => {
        if (media.type === mediaType) {
          media.bandwidth = [
            {
              // Chrome supports only 'AS'
              type: 'AS',
              limit: bandwidth.toString(),
            },
            {
              // Firefox Supports only 'TIAS' from M49
              type: 'TIAS',
              limit: (bandwidth * 1000).toString(),
            },
          ];
        }
        return media;
      });
      return sdp_transform_lib_default.a.write(sdpObject);
    }
  
    /**
     * Check bandwidth is valid or not. If invalid, throw Error
     * @param {number} bandwidth - bandwidth of 'audio' or 'video'
     * @private
     */
    _validateBandwidth(bandwidth) {
      if (bandwidth === undefined) {
        throw new Error('bandwidth is not passed');
      }
  
      if (!/^\d+$/.test(bandwidth)) {
        throw new Error(`${bandwidth} is not a number`);
      }
    }
  }
  
  /* harmony default export */ var sdpUtil = (new sdpUtil_SdpUtil());
  
  // EXTERNAL MODULE: ./node_modules/detect-browser/index.js
  var detect_browser = __webpack_require__("./node_modules/detect-browser/index.js");
  
  // CONCATENATED MODULE: ./src/shared/util.js
  
  
  /**
   * Validate the Peer ID format.
   * @param {string} [id] - A Peer ID.
   * @return {boolean} True if the peerId format is valid. False if not.
   */
  function validateId(id) {
    // Allow empty ids
    return !id || /^[A-Za-z0-9_-]+(?:[ _-][A-Za-z0-9]+)*$/.exec(id);
  }
  
  /**
   * Validate the API key.
   * @param {string} [key] A SkyWay API key.
   * @return {boolean} True if the API key format is valid. False if not.
   */
  function validateKey(key) {
    // Allow empty keys
    return !key || /^[a-z0-9]{8}(-[a-z0-9]{4}){3}-[a-z0-9]{12}$/.exec(key);
  }
  
  /**
   * Return random ID.
   * @return {string} A text consisting of 16 chars.
   */
  function randomId() {
    const keyLength = 16;
    // '36' means that we want to convert the number to a string using chars in
    // the range of '0-9a-z'. The concatenated 0's are for padding the key,
    // as Math.random() may produce a key shorter than 16 chars in length
    const randString = Math.random().toString(36) + '0000000000000000000';
    return randString.substr(2, keyLength);
  }
  
  /**
   * Generate random token.
   * @return {string} A token consisting of random alphabet and integer.
   */
  function randomToken() {
    return Math.random()
      .toString(36)
      .substr(2);
  }
  
  /**
   * Combine the sliced ArrayBuffers.
   * @param {Array} buffers - An Array of ArrayBuffer.
   * @return {ArrayBuffer} The combined ArrayBuffer.
   */
  function joinArrayBuffers(buffers) {
    const size = buffers.reduce((sum, buffer) => {
      return sum + buffer.byteLength;
    }, 0);
    const tmpArray = new Uint8Array(size);
    let currPos = 0;
    for (const buffer of buffers) {
      tmpArray.set(new Uint8Array(buffer), currPos);
      currPos += buffer.byteLength;
    }
    return tmpArray.buffer;
  }
  
  /**
   * Convert Blob to ArrayBuffer.
   * @param {Blob} blob - The Blob to be read as ArrayBuffer.
   * @param {Function} cb - Callback function that called after load event fired.
   */
  function blobToArrayBuffer(blob, cb) {
    const fr = new FileReader();
    fr.onload = event => {
      cb(event.target.result);
    };
    fr.readAsArrayBuffer(blob);
  }
  
  /**
   * Whether the protocol is https or not.
   * @return {boolean} Whether the protocol is https or not.
   */
  function isSecure() {
    return location.protocol === 'https:';
  }
  
  /**
   * Detect browser name and version.
   * @return {Object} Browser name and major, minor and patch versions. Object is empty if info can't be obtained.
   */
  function detectBrowser() {
    const { name, version } = Object(detect_browser["detect"])();
    const [major, minor, patch] = version.split('.').map(i => parseInt(i));
    return {
      name,
      major,
      minor,
      patch,
    };
  }
  
  /**
   * Safari 12.1 may use plan-b sdp and also unified-plan sdp.
   * It depends on user settings.
   * See https://webkit.org/blog/8672/on-the-road-to-webrtc-1-0-including-vp8/
   *
   * @return {boolean} Browser is Safari 12.1 or later and enables unified-plan OR NOT
   */
  function isUnifiedPlanSafari() {
    const { name, major, minor } = detectBrowser();
  
    if (
      (name === 'safari' || name === 'ios') &&
      (major >= 12 && minor >= 1) &&
      RTCRtpTransceiver.prototype.hasOwnProperty('currentDirection')
    ) {
      return true;
    }
  
    return false;
  }
  
  /* harmony default export */ var util = ({
    validateId,
    validateKey,
    randomId,
    randomToken,
    joinArrayBuffers,
    blobToArrayBuffer,
    isSecure,
    detectBrowser,
    isUnifiedPlanSafari,
  });
  
  // CONCATENATED MODULE: ./src/peer/negotiator.js
  
  
  
  
  
  
  
  const NegotiatorEvents = new enum_default.a([
    'addStream',
    'removeStream',
    'dcCreated',
    'offerCreated',
    'answerCreated',
    'iceCandidate',
    'iceCandidatesComplete',
    'iceConnectionFailed',
    'negotiationNeeded',
    'error',
  ]);
  
  /**
   * Class that manages RTCPeerConnection and SDP exchange.
   * @extends EventEmitter
   */
  class negotiator_Negotiator extends events_default.a {
    /**
     * Create a negotiator
     * @param {string} name - Room name.
     */
    constructor() {
      super();
      this._offerQueue = [];
      this._isExpectingAnswer = false;
      this._replaceStreamCalled = false;
      this._isNegotiationAllowed = true;
      this.hasRemoteDescription = false;
    }
  
    /**
     * Class that manages RTCPeerConnection and SDP exchange.
     * @param {object} [options] - Optional arguments for starting connection.
     * @param {string} [options.type] - Type of connection. One of 'media' or 'data'.
     * @param {MediaStream} [options._stream] - The MediaStream to be sent to the remote peer.
     * @param {string} [options.label] - Label to easily identify the connection on either peer.
     * @param {Object} [options.dcInit] - Options passed to createDataChannel() as a RTCDataChannelInit.
     *                  See https://www.w3.org/TR/webrtc/#dom-rtcdatachannelinit
     * @param {boolean} [options.originator] - true means the peer is the originator of the connection.
     * @param {RTCSessionDescription} [options.offer]
     *        - The local description. If the peer is originator, handleOffer is called with it.
     * @param {object} [options.pcConfig] - A RTCConfiguration dictionary for the RTCPeerConnection.
     * @param {number} [options.videoBandwidth] - A max video bandwidth(kbps)
     * @param {number} [options.audioBandwidth] - A max audio bandwidth(kbps)
     * @param {string} [options.videoCodec] - A video codec like 'H264'
     * @param {string} [options.audioCodec] - A video codec like 'PCMU'
     * @param {boolean} [options.videoReceiveEnabled] - A flag to set video recvonly
     * @param {boolean} [options.audioReceiveEnabled] - A flag to set audio recvonly
     * @return {Promise<void>} Promise that resolves when starting is done.
     */
    async startConnection(options = {}) {
      this._pc = this._createPeerConnection(options.pcConfig);
      this._setupPCListeners();
      this.originator = options.originator;
      this._audioBandwidth = options.audioBandwidth;
      this._videoBandwidth = options.videoBandwidth;
      this._audioCodec = options.audioCodec;
      this._videoCodec = options.videoCodec;
      this._type = options.type;
      this._recvonlyState = this._getReceiveOnlyState(options);
  
      if (this._type === 'media') {
        if (options.stream) {
          options.stream.getTracks().forEach(track => {
            this._pc.addTrack(track, options.stream);
          });
        } else if (this.originator) {
          // This means the peer wants to create offer SDP with `recvonly`
          const offer = await this._makeOfferSdp();
          await this._setLocalDescription(offer);
        }
      }
  
      if (this.originator) {
        if (this._type === 'data') {
          const label = options.label || '';
          const dcInit = options.dcInit || {};
          const dc = this._pc.createDataChannel(label, dcInit);
          this.emit(negotiator_Negotiator.EVENTS.dcCreated.key, dc);
        }
      } else {
        await this.handleOffer(options.offer);
      }
    }
  
    /**
     * Replace the stream being sent with a new one.
     * Video and audio tracks are updated per track by using `{add|replace|remove}Track` methods.
     * We assume that there is at most 1 audio and at most 1 video in local stream.
     * @param {MediaStream} newStream - The stream to replace the old stream with.
     * @private
     */
    replaceStream(newStream) {
      // If negotiator is null
      // or replaceStream was called but `onnegotiationneeded` event has not finished yet.
      if (!this._pc) {
        return;
      }
  
      this._isNegotiationAllowed = true;
  
      const _this = this;
      const vTracks = newStream.getVideoTracks();
      const aTracks = newStream.getAudioTracks();
  
      const senders = this._pc.getSenders();
      const vSender = senders.find(
        sender => sender.track && sender.track.kind === 'video'
      );
      const aSender = senders.find(
        sender => sender.track && sender.track.kind === 'audio'
      );
  
      _updateSenderWithTrack(vSender, vTracks[0], newStream);
      _updateSenderWithTrack(aSender, aTracks[0], newStream);
  
      /**
       * Replace a track being sent with a new one.
       * @param {RTCRtpSender} sender - The sender which type is video or audio.
       * @param {MediaStreamTrack} track - The track of new stream.
       * @param {MediaStream} stream - The stream which contains the track.
       * @private
       */
      function _updateSenderWithTrack(sender, track, stream) {
        if (track === undefined && sender === undefined) {
          return;
        }
        // remove video or audio sender if not passed
        if (track === undefined) {
          _this._pc.removeTrack(sender);
          return;
        }
        // if passed, replace track or create sender
        if (sender === undefined) {
          _this._pc.addTrack(track, stream);
          return;
        }
        // if track was not replaced, do nothing
        if (sender.track.id === track.id) {
          return;
        }
        sender.replaceTrack(track);
      }
    }
  
    /**
     * Set remote description with remote Offer SDP, then create Answer SDP and emit it.
     * @param {object} [offerSdp] - An object containing Offer SDP.
     * @return {Promise<void>} Promise that resolves when handling offer is done.
     */
    async handleOffer(offerSdp) {
      // Avoid unnecessary processing by short circuiting the code if nothing has changed in the sdp.
      if (this._lastOffer && offerSdp && this._lastOffer.sdp === offerSdp.sdp) {
        return;
      }
  
      this._isNegotiationAllowed = true;
  
      if (!offerSdp) {
        offerSdp = this._lastOffer;
      }
  
      this._lastOffer = offerSdp;
  
      // Enqueue and skip while signalingState is wrong state.
      // (when room is SFU and there are multiple conns in a same time, it happens)
      if (this._pc.signalingState === 'have-remote-offer') {
        this._offerQueue.push(offerSdp);
        return;
      }
  
      await this._setRemoteDescription(offerSdp);
      const answer = await this._makeAnswerSdp().catch(err => logger.error(err));
      this.emit(negotiator_Negotiator.EVENTS.answerCreated.key, answer);
    }
  
    /**
     * Set remote description with Answer SDP.
     * @param {object} answerSdp - An object containing Answer SDP.
     */
    async handleAnswer(answerSdp) {
      this._isNegotiationAllowed = true;
  
      if (this._isExpectingAnswer) {
        await this._setRemoteDescription(answerSdp);
        this._isExpectingAnswer = false;
      } else if (this._pc.onnegotiationneeded) {
        // manually trigger negotiation
        this._pc.onnegotiationneeded();
      }
    }
  
    /**
     * Set ice candidate with Candidate SDP.
     * @param {object} candidate - An object containing Candidate SDP.
     * @return {Promise<void>} Promise that resolves when handling candidate is done.
     */
    async handleCandidate(candidate) {
      await this._pc
        .addIceCandidate(new RTCIceCandidate(candidate))
        .then(() => logger.log('Successfully added ICE candidate'))
        .catch(err => logger.error('Failed to add ICE candidate', err));
    }
  
    /**
     * Close a PeerConnection.
     */
    cleanup() {
      logger.log('Cleaning up PeerConnection');
  
      if (
        this._pc &&
        (this._pc.readyState !== 'closed' || this._pc.signalingState !== 'closed')
      ) {
        this._pc.close();
      }
      this._pc = null;
    }
  
    /**
     * Create new RTCPeerConnection.
     * @param {object} pcConfig - A RTCConfiguration dictionary for the RTCPeerConnection.
     * @return {RTCPeerConnection} An instance of RTCPeerConnection.
     * @private
     */
    _createPeerConnection(pcConfig = {}) {
      logger.log('Creating RTCPeerConnection');
  
      const browserInfo = util.detectBrowser();
  
      // If browser is Chrome and over 69, it has addTransceiver but does not work without unified-plan option.
      // SkyWay has not supported unified-plan.
      this._isAddTransceiverAvailable =
        typeof RTCPeerConnection.prototype.addTransceiver === 'function' &&
        browserInfo.name !== 'chrome';
  
      // Force plan-b for SFU, until we finish unified-plan support.
      pcConfig.sdpSemantics = 'plan-b';
      return new RTCPeerConnection(pcConfig);
    }
  
    /**
     * Set up event handlers of RTCPeerConnection events.
     * @private
     */
    _setupPCListeners() {
      const pc = this._pc;
  
      pc.ontrack = evt => {
        logger.log('Received remote media stream track');
        evt.streams.forEach(stream => {
          this.emit(negotiator_Negotiator.EVENTS.addStream.key, stream);
        });
      };
  
      pc.ondatachannel = evt => {
        logger.log('Received data channel');
        const dc = evt.channel;
        this.emit(negotiator_Negotiator.EVENTS.dcCreated.key, dc);
      };
  
      pc.onicecandidate = evt => {
        const candidate = evt.candidate;
        if (candidate) {
          logger.log('Generated ICE candidate for:', candidate);
          this.emit(negotiator_Negotiator.EVENTS.iceCandidate.key, candidate);
        } else {
          logger.log('ICE candidates gathering complete');
  
          this.emit(
            negotiator_Negotiator.EVENTS.iceCandidatesComplete.key,
            pc.localDescription
          );
        }
      };
  
      pc.oniceconnectionstatechange = () => {
        switch (pc.iceConnectionState) {
          case 'completed':
            logger.log('iceConnectionState is completed');
            // istanbul ignore next
            pc.onicecandidate = () => {};
            break;
          case 'disconnected':
            /**
             * Browsers(Chrome/Safari/Firefox) implement iceRestart with createOffer(),
             * but it seems buggy at 2017/08, so we don't use iceRestart to reconnect intensionally.
             * Ref: https://github.com/nttcom-webcore/ECLRTC-JS-SDK/pull/37
             */
            logger.log(
              'iceConnectionState is disconnected, trying reconnect by browser'
            );
            break;
          case 'failed':
            logger.log('iceConnectionState is failed, closing connection');
            this.emit(negotiator_Negotiator.EVENTS.iceConnectionFailed.key);
            break;
          default:
            logger.log(`iceConnectionState is ${pc.iceConnectionState}`);
            break;
        }
      };
  
      pc.onnegotiationneeded = async () => {
        logger.log('`negotiationneeded` triggered');
  
        // Don't make a new offer if it's not stable or if onnegotiationneeded is called consecutively.
        // Chrome 65+ called onnegotiationneeded once per addTrack so force it to run only once.
        if (pc.signalingState === 'stable' && this._isNegotiationAllowed) {
          this._isNegotiationAllowed = false;
          // Emit negotiationNeeded event in case additional handling is needed.
          if (this.originator) {
            const offer = await this._makeOfferSdp();
            this._setLocalDescription(offer);
            this.emit(negotiator_Negotiator.EVENTS.negotiationNeeded.key);
          } else if (this._replaceStreamCalled) {
            this.handleOffer();
          }
  
          this._replaceStreamCalled = false;
        }
      };
  
      pc.onremovestream = evt => {
        logger.log('`removestream` triggered');
        this.emit(negotiator_Negotiator.EVENTS.removeStream.key, evt.stream);
      };
  
      pc.onsignalingstatechange = () => {
        logger.log(`signalingState is ${pc.signalingState}`);
  
        // After signaling state is getting back to 'stable',
        // apply pended remote offer, which was stored when simultaneous multiple conns happened in SFU room,
        // Note that this code very rarely applies the old remote offer.
        // E.g. "Offer A -> Offer B" should be the right order but for some reason like NW unstablity,
        //      offerQueue might keep "Offer B" first and handle "Offer A" later.
        if (pc.signalingState === 'stable') {
          const offer = this._offerQueue.shift();
          if (offer) {
            this.handleOffer(offer);
          }
        }
      };
    }
  
    /**
     * Create Offer SDP.
     * @return {Promise<Object>} A promise that resolves with Offer SDP.
     * @private
     */
    async _makeOfferSdp() {
      let offer;
  
      try {
        // DataConnection
        if (this._type !== 'media') {
          offer = await this._pc.createOffer();
          // MediaConnection
        } else {
          if (this._isAddTransceiverAvailable) {
            this._recvonlyState.audio &&
              this._pc.addTransceiver('audio', { direction: 'recvonly' });
            this._recvonlyState.video &&
              this._pc.addTransceiver('video', { direction: 'recvonly' });
            offer = await this._pc.createOffer();
          } else {
            const offerOptions = {};
            // the offerToReceiveXXX options are defined in the specs as boolean but `undefined` acts differently from false
            this._recvonlyState.audio &&
              (offerOptions.offerToReceiveAudio = true);
            this._recvonlyState.video &&
              (offerOptions.offerToReceiveVideo = true);
            offer = await this._pc.createOffer(offerOptions);
          }
        }
      } catch (err) {
        err.type = 'webrtc';
        logger.error(err);
        this.emit(negotiator_Negotiator.EVENTS.error.key, err);
  
        logger.log('Failed to createOffer, ', err);
        throw err;
      }
  
      logger.log('Created offer.');
  
      if (this._audioBandwidth) {
        offer.sdp = sdpUtil.addAudioBandwidth(offer.sdp, this._audioBandwidth);
      }
      if (this._videoBandwidth) {
        offer.sdp = sdpUtil.addVideoBandwidth(offer.sdp, this._videoBandwidth);
      }
      if (this._audioCodec) {
        offer.sdp = sdpUtil.filterAudioCodec(offer.sdp, this._audioCodec);
      }
      if (this._videoCodec) {
        offer.sdp = sdpUtil.filterVideoCodec(offer.sdp, this._videoCodec);
      }
  
      return offer;
    }
  
    /**
     * Make Answer SDP and set it as local description.
     * @return {Promise<Object>} A promise that is resolved with answer when setting local SDP is completed.
     * @private
     */
    async _makeAnswerSdp() {
      let answer;
      try {
        answer = await this._pc.createAnswer();
      } catch (err) {
        err.type = 'webrtc';
        logger.error(err);
        this.emit(negotiator_Negotiator.EVENTS.error.key, err);
  
        logger.log('Failed to createAnswer, ', err);
        throw err;
      }
  
      logger.log('Created answer.');
  
      if (this._audioBandwidth) {
        answer.sdp = sdpUtil.addAudioBandwidth(answer.sdp, this._audioBandwidth);
      }
      if (this._videoBandwidth) {
        answer.sdp = sdpUtil.addVideoBandwidth(answer.sdp, this._videoBandwidth);
      }
      if (this._audioCodec) {
        answer.sdp = sdpUtil.filterAudioCodec(answer.sdp, this._audioCodec);
      }
      if (this._videoCodec) {
        answer.sdp = sdpUtil.filterVideoCodec(answer.sdp, this._videoCodec);
      }
  
      try {
        await this._pc.setLocalDescription(answer);
      } catch (err) {
        err.type = 'webrtc';
        logger.error(err);
        this.emit(negotiator_Negotiator.EVENTS.error.key, err);
  
        logger.log('Failed to setLocalDescription, ', err);
        throw err;
      }
  
      logger.log('Set localDescription: answer');
      logger.log(`Setting local description ${JSON.stringify(answer.sdp)}`);
  
      return answer;
    }
  
    /**
     * Set local description with Offer SDP and emit offerCreated event.
     * @param {RTCSessionDescription} offer - Offer SDP.
     * @return {Promise<void>} A promise that is resolved when setting local SDP is completed.
     * @private
     */
    async _setLocalDescription(offer) {
      logger.log(`Setting local description ${JSON.stringify(offer.sdp)}`);
  
      try {
        await this._pc.setLocalDescription(offer);
      } catch (err) {
        err.type = 'webrtc';
        logger.error(err);
        this.emit(negotiator_Negotiator.EVENTS.error.key, err);
  
        logger.log('Failed to setLocalDescription, ', err);
        throw err;
      }
  
      logger.log('Set localDescription: offer');
      this._isExpectingAnswer = true;
      this.emit(negotiator_Negotiator.EVENTS.offerCreated.key, offer);
    }
  
    /**
     * Set remote SDP.
     * @param {object} sdp - An object containing remote SDP.
     * @return {Promise<void>} A promise that is resolved when setting remote SDP is completed.
     * @private
     */
    async _setRemoteDescription(sdp) {
      logger.log(`Setting remote description ${JSON.stringify(sdp)}`);
  
      try {
        await this._pc.setRemoteDescription(new RTCSessionDescription(sdp));
        this.hasRemoteDescription = true;
      } catch (err) {
        err.type = 'webrtc';
        logger.error(err);
        this.emit(negotiator_Negotiator.EVENTS.error.key, err);
  
        logger.log('Failed to setRemoteDescription: ', err);
        throw err;
      }
  
      logger.log('Set remoteDescription:', sdp.type);
    }
  
    /**
     * Get map object describes which kinds of tracks should be marked as recvonly
     * @param {Object} options - Options of peer.call()
     * @return {Object} Map object which streamTrack will be recvonly or not
     */
    _getReceiveOnlyState(options = {}) {
      const state = {
        audio: false,
        video: false,
      };
  
      const hasStream = options.stream instanceof MediaStream;
      const hasAudioTrack = hasStream
        ? options.stream.getAudioTracks().length !== 0
        : false;
      const hasVideoTrack = hasStream
        ? options.stream.getVideoTracks().length !== 0
        : false;
  
      // force true if stream not passed(backward compatibility)
      if (
        hasStream === false &&
        options.audioReceiveEnabled === undefined &&
        options.videoReceiveEnabled === undefined
      ) {
        state.audio = true;
        state.video = true;
        return state;
      }
  
      // Set recvonly to true if `stream does not have track` and `option is true` case only
      if (options.audioReceiveEnabled && hasAudioTrack === false) {
        state.audio = true;
      }
      if (options.videoReceiveEnabled && hasVideoTrack === false) {
        state.video = true;
      }
  
      // If stream has track, ignore options, which results in setting sendrecv internally.
      if (options.audioReceiveEnabled === false && hasAudioTrack) {
        logger.warn(
          'Option audioReceiveEnabled will be treated as true, because passed stream has MediaStreamTrack(kind = audio)'
        );
      }
      if (options.videoReceiveEnabled === false && hasVideoTrack) {
        logger.warn(
          'Option videoReceiveEnabled will be treated as true, because passed stream has MediaStreamTrack(kind = video)'
        );
      }
  
      return state;
    }
  
    /**
     * Events the Negotiator class can emit.
     * @type {Enum}
     */
    static get EVENTS() {
      return NegotiatorEvents;
    }
  
    /**
     * Remote media stream received.
     *
     * @event Negotiator#addStream
     * @type {MediaStream}
     */
  
    /**
     * DataConnection is ready.
     *
     * @event Negotiator#dcCreated
     * @type {DataConnection}
     */
  
    /**
     * Offer SDP created.
     *
     * @event Negotiator#offerCreated
     * @type {RTCSessionDescription}
     */
  
    /**
     * Answer SDP created.
     *
     * @event Negotiator#answerCreated
     * @type {RTCSessionDescription}
     */
  
    /**
     * Ice Candidate created.
     *
     * @event Negotiator#iceCandidate
     * @type {RTCIceCandidate}
     */
  
    /**
     * Ice Candidate collection finished. Emits localDescription.
     *
     * @event Negotiator#iceCandidatesComplete
     * @type {RTCSessionDescription}
     */
  
    /**
     * Ice connection failed.
     *
     * @event Negotiator#iceConnectionFailed
     */
  
    /**
     * Session needs negotiation.
     *
     * @event Negotiator#negotiationNeeded
     */
  
    /**
     * Error occurred.
     *
     * @event Negotiator#error
     * @type {Error}
     */
  }
  
  /* harmony default export */ var negotiator = (negotiator_Negotiator);
  
  // CONCATENATED MODULE: ./src/peer/connection.js
  
  
  
  
  
  
  
  
  const ConnectionEvents = new enum_default.a([
    'candidate',
    'offer',
    'answer',
    'close',
    'forceClose',
  ]);
  
  /**
   * Class that manages connections to other peers.
   * @extends EventEmitter
   */
  class connection_Connection extends events_default.a {
    /**
     * Create a connection to another peer. Cannot be called directly. Must be called by a subclass.
     * @param {string} remoteId - The peerId of the peer you are connecting to.
     * @param {object} [options] - Optional arguments for the connection.
     * @param {string} [options.connectionId] - An ID to uniquely identify the connection.
     *                                          Defaults to random string if not specified.
     */
    constructor(remoteId, options) {
      super();
  
      options = options || {};
  
      // Abstract class
      if (this.constructor === connection_Connection) {
        throw new TypeError('Cannot construct Connection instances directly');
      }
  
      this._options = options;
  
      /**
       * Whether the Connection has been opened or not.
       * @type {boolean}
       */
      this.open = false;
  
      /**
       * The connection type. Either 'media' or 'data'.
       * @type {string}
       */
      this.type = undefined;
  
      /**
       * Any additional information to send to the peer.
       * @type {object}
       */
      this.metadata = this._options.metadata;
  
      /**
       * PeerId of the peer this connection is connected to.
       * @type {string}
       */
      this.remoteId = remoteId;
  
      this._negotiator = new negotiator();
  
      this._idPrefix = 'c_';
      this._randomIdSuffix = util.randomToken();
  
      this._setupNegotiatorMessageHandlers();
    }
  
    /**
     * An id to uniquely identify the connection.
     */
    get id() {
      return this._options.connectionId || this._idPrefix + this._randomIdSuffix;
    }
  
    /**
     * Handle an sdp answer message from the remote peer.
     * @param {object} answerMessage - Message object containing sdp answer.
     */
    async handleAnswer(answerMessage) {
      if (this._pcAvailable) {
        await this._negotiator.handleAnswer(answerMessage.answer);
        this.open = true;
        this._handleQueuedMessages();
      } else {
        logger.log(`Queuing ANSWER message in ${this.id} from ${this.remoteId}`);
        this._queuedMessages.push({
          type: config.MESSAGE_TYPES.SERVER.ANSWER.key,
          payload: answerMessage,
        });
      }
    }
  
    /**
     * Handle a candidate message from the remote peer.
     * @param {object} candidateMessage - Message object containing a candidate.
     */
    handleCandidate(candidateMessage) {
      // The orginator(caller) should wait for the remote ANSWER arrival and
      // setRemoteDescription(ANSWER) before handleCandidate(addIceCandidate).
      if (this._negotiator.originator && !this._negotiator.hasRemoteDescription) {
        this._queuedMessages.push({
          type: config.MESSAGE_TYPES.SERVER.CANDIDATE.key,
          payload: candidateMessage,
        });
        return;
      }
  
      if (this._pcAvailable) {
        this._negotiator.handleCandidate(candidateMessage.candidate);
      } else {
        logger.log(
          `Queuing CANDIDATE message in ${this.id} from ${this.remoteId}`
        );
        this._queuedMessages.push({
          type: config.MESSAGE_TYPES.SERVER.CANDIDATE.key,
          payload: candidateMessage,
        });
      }
    }
  
    /**
     * Handle an offer message from the remote peer. Allows an offer to be updated.
     * @param {object} offerMessage - Message object containing an offer.
     */
    updateOffer(offerMessage) {
      if (this.open) {
        this._negotiator.handleOffer(offerMessage.offer);
      } else {
        this._options.payload = offerMessage;
      }
    }
  
    /**
     * Gives a RTCPeerConnection.
     */
    getPeerConnection() {
      if (!this.open) {
        return null;
      }
      return this._negotiator._pc;
    }
  
    /**
     * Process messages received before the RTCPeerConnection is ready.
     * @private
     */
    _handleQueuedMessages() {
      for (const message of this._queuedMessages) {
        switch (message.type) {
          // Should we remove this ANSWER block
          // because ANSWER should be handled immediately?
          case config.MESSAGE_TYPES.SERVER.ANSWER.key:
            this.handleAnswer(message.payload);
            break;
          case config.MESSAGE_TYPES.SERVER.CANDIDATE.key:
            this.handleCandidate(message.payload);
            break;
          default:
            logger.warn(
              'Unrecognized message type:',
              message.type,
              'from peer:',
              this.remoteId
            );
            break;
        }
      }
      this._queuedMessages = [];
    }
  
    /**
     * Disconnect from remote peer.
     * @fires Connection#close
     */
    close(forceClose = false) {
      if (!this.open) {
        return;
      }
  
      this.open = false;
      this._negotiator.cleanup();
      this.emit(connection_Connection.EVENTS.close.key);
  
      if (forceClose) {
        this.emit(connection_Connection.EVENTS.forceClose.key);
      }
    }
  
    /**
     * Handle messages from the negotiator.
     * @private
     */
    _setupNegotiatorMessageHandlers() {
      this._negotiator.on(negotiator.EVENTS.answerCreated.key, answer => {
        const connectionAnswer = {
          answer: answer,
          dst: this.remoteId,
          connectionId: this.id,
          connectionType: this.type,
        };
        this.emit(connection_Connection.EVENTS.answer.key, connectionAnswer);
      });
  
      this._negotiator.on(negotiator.EVENTS.offerCreated.key, offer => {
        const connectionOffer = {
          offer: offer,
          dst: this.remoteId,
          connectionId: this.id,
          connectionType: this.type,
          metadata: this.metadata,
        };
        if (this.serialization) {
          connectionOffer.serialization = this.serialization;
        }
        if (this.label) {
          connectionOffer.label = this.label;
        }
        if (this.dcInit) {
          connectionOffer.dcInit = this.dcInit;
        }
        this.emit(connection_Connection.EVENTS.offer.key, connectionOffer);
      });
  
      this._negotiator.on(negotiator.EVENTS.iceCandidate.key, candidate => {
        const connectionCandidate = {
          candidate: candidate,
          dst: this.remoteId,
          connectionId: this.id,
          connectionType: this.type,
        };
        this.emit(connection_Connection.EVENTS.candidate.key, connectionCandidate);
      });
  
      this._negotiator.on(negotiator.EVENTS.iceConnectionFailed.key, () => {
        this.close();
      });
    }
  
    /**
     * The remote peerId.
     * @type {string}
     * @deprecated Use remoteId instead.
     */
    get peer() {
      logger.warn(
        `${
          this.constructor.name
        }.peer is deprecated and may be removed from a future version.` +
          ` Please use ${this.constructor.name}.remoteId instead.`
      );
      return this.remoteId;
    }
  
    /**
     * Events the Connection can emit.
     * @type {Enum}
     */
    static get EVENTS() {
      return ConnectionEvents;
    }
  
    /**
     * ICE candidate created event.
     *
     * @event Connection#candidate
     * @type {object}
     * @property {RTCIceCandidate} candidate - The ice candidate.
     * @property {string} dst - Destination peerId
     * @property {string} connectionId - This connection's id.
     * @property {string} connectionType - This connection's type.
     */
  
    /**
     * Offer created event.
     *
     * @event Connection#offer
     * @type {object}
     * @property {RTCSessionDescription} offer - The local offer to send to the peer.
     * @property {string} dst - Destination peerId
     * @property {string} connectionId - This connection's id.
     * @property {string} connectionType - This connection's type.
     * @property {object} metadata - Any extra data to send with the connection.
     */
  
    /**
     * Answer created event.
     *
     * @event Connection#answer
     * @type {object}
     * @property {RTCSessionDescription} answer - The local answer to send to the peer.
     * @property {string} dst - Destination peerId
     * @property {string} connectionId - This connection's id.
     * @property {string} connectionType - This connection's type.
     */
  
    /**
     * Connection closed event.
     *
     * @event Connection#close
     */
  
    /**
     * Requested to close the connection.
     *
     * @event Connection#forceClose
     */
  }
  
  /* harmony default export */ var peer_connection = (connection_Connection);
  
  // EXTERNAL MODULE: ./node_modules/js-binarypack/lib/binarypack.js
  var binarypack = __webpack_require__("./node_modules/js-binarypack/lib/binarypack.js");
  var binarypack_default = /*#__PURE__*/__webpack_require__.n(binarypack);
  
  // EXTERNAL MODULE: ./node_modules/object-sizeof/index.js
  var object_sizeof = __webpack_require__("./node_modules/object-sizeof/index.js");
  var object_sizeof_default = /*#__PURE__*/__webpack_require__.n(object_sizeof);
  
  // CONCATENATED MODULE: ./src/peer/dataConnection.js
  
  
  
  
  
  
  
  
  
  
  const DCEvents = new enum_default.a(['open', 'data', 'error']);
  
  DCEvents.extend(peer_connection.EVENTS.enums);
  
  const DCSerializations = new enum_default.a(['binary', 'binary-utf8', 'json', 'none']);
  
  /**
   * Class that manages data connections to other peers.
   * @extends Connection
   */
  class dataConnection_DataConnection extends peer_connection {
    /**
     * Create a data connection to another peer.
     * @param {string} remoteId - The peerId of the peer you are connecting to.
     * @param {object} [options] - Optional arguments for the connection.
     * @param {string} [options.connectionId] - An ID to uniquely identify the connection. Defaults to random string if not specified.
     * @param {string} [options.serialization] - How to serialize data when sending. One of 'binary', 'json' or 'none'.
     * @param {string} [options.label] - Label to easily identify the connection on either peer.
     * @param {Object} [options.dcInit] - Options passed to createDataChannel() as a RTCDataChannelInit.
     *                  See https://www.w3.org/TR/webrtc/#dom-rtcdatachannelinit
     * @param {string} [options.queuedMessages] - An array of messages that were already received before the connection was created.
     * @param {string} [options.payload] - An offer message that triggered creating this object.
     */
    constructor(remoteId, options) {
      super(remoteId, options);
  
      this._idPrefix = 'dc_';
      this.type = 'data';
  
      this._isOnOpenCalled = false;
  
      /**
       * Label to easily identify the DataConnection on either peer.
       * @type {string}
       */
      this.label = this._options.label || this.id;
  
      // Use reliable mode by default
      this.dcInit = this._options.dcInit || {};
  
      // Serialization is binary by default
      this.serialization = dataConnection_DataConnection.SERIALIZATIONS.binary.key;
      if (this._options.serialization) {
        if (!dataConnection_DataConnection.SERIALIZATIONS.get(this._options.serialization)) {
          // Can't emit error as there hasn't been a chance to set up listeners
          throw new Error('Invalid serialization');
        }
        this.serialization = this._options.serialization;
  
        if (this._isUnreliableDCInit(this.dcInit)) {
          logger.warn(
            'You can not specify serialization with unreliable mode enabled.'
          );
          this.serialization = dataConnection_DataConnection.SERIALIZATIONS.binary.key;
        }
      }
  
      // New send code properties
      this._sendBuffer = [];
      this._receivedData = {};
      // Messages stored by peer because DC was not ready yet
      this._queuedMessages = this._options.queuedMessages || [];
  
      // This replaces the PeerJS 'initialize' method
      this._negotiator.on(negotiator.EVENTS.dcCreated.key, dc => {
        this._dc = dc;
        this._dc.binaryType = 'arraybuffer';
        this._setupMessageHandlers();
  
        // Manually call dataChannel.onopen() if the dataChannel opened before the event handler was set.
        // This can happen if the tab is in the background in Chrome as the event loop is handled differently.
        if (!this._isOnOpenCalled && this._dc.readyState === 'open') {
          this._dc.onopen();
        }
      });
  
      // If this is not the originator, we need to set the pcConfig
      if (this._options.payload) {
        this._options.payload.pcConfig = this._options.pcConfig;
      }
    }
  
    /**
     * Start connection via negotiator and handle queued messages.
     * @return {Promise<void>} Promise that resolves when starting is done.
     */
    async startConnection() {
      await this._negotiator.startConnection(
        this._options.payload || {
          originator: true,
          type: 'data',
          label: this.label,
          dcInit: this.dcInit,
          pcConfig: this._options.pcConfig,
        }
      );
  
      this._pcAvailable = true;
      this._handleQueuedMessages();
    }
  
    /**
     * Set up data channel event and message handlers.
     * @private
     */
    _setupMessageHandlers() {
      this._dc.onopen = () => {
        if (this._isOnOpenCalled) {
          return;
        }
  
        logger.log('Data channel connection success');
        this.open = true;
        this._isOnOpenCalled = true;
        this.emit(dataConnection_DataConnection.EVENTS.open.key);
      };
  
      // We no longer need the reliable shim here
      this._dc.onmessage = msg => {
        this._handleDataMessage(msg);
      };
  
      this._dc.onclose = () => {
        logger.log('DataChannel closed for:', this.id);
        this.close();
      };
  
      this._dc.onerror = err => {
        logger.error(err);
      };
    }
  
    /**
     * Handle a data message from the peer.
     * @param {object} msg - The data message to handle.
     * @private
     */
    _handleDataMessage(msg) {
      if (this.serialization === dataConnection_DataConnection.SERIALIZATIONS.none.key) {
        this.emit(dataConnection_DataConnection.EVENTS.data.key, msg.data);
        return;
      } else if (this.serialization === dataConnection_DataConnection.SERIALIZATIONS.json.key) {
        this.emit(dataConnection_DataConnection.EVENTS.data.key, JSON.parse(msg.data));
        return;
      }
  
      // Everything below is for serialization binary or binary-utf8
  
      const dataMeta = binarypack_default.a.unpack(msg.data);
  
      // If we haven't started receiving pieces of data with a given id, this will be undefined
      // In that case, we need to initialise receivedData[id] to hold incoming file chunks
      let currData = this._receivedData[dataMeta.id];
      if (!currData) {
        currData = this._receivedData[dataMeta.id] = {
          size: dataMeta.size,
          type: dataMeta.type,
          name: dataMeta.name,
          mimeType: dataMeta.mimeType,
          totalParts: dataMeta.totalParts,
          parts: new Array(dataMeta.totalParts),
          receivedParts: 0,
        };
      }
      currData.receivedParts++;
      currData.parts[dataMeta.index] = dataMeta.data;
  
      if (currData.receivedParts === currData.totalParts) {
        delete this._receivedData[dataMeta.id];
  
        // recombine the sliced arraybuffers
        const ab = util.joinArrayBuffers(currData.parts);
        const unpackedData = binarypack_default.a.unpack(ab);
  
        let finalData;
        switch (currData.type) {
          case 'Blob':
            finalData = new Blob([new Uint8Array(unpackedData)], {
              type: currData.mimeType,
            });
            break;
          case 'File':
            finalData = new File([new Uint8Array(unpackedData)], currData.name, {
              type: currData.mimeType,
            });
            break;
          default:
            finalData = unpackedData;
        }
  
        this.emit(dataConnection_DataConnection.EVENTS.data.key, finalData);
      }
    }
  
    /**
     * Send data to peer. If serialization is 'binary', it will chunk it before sending.
     * @param {*} data - The data to send to the peer.
     */
    send(data) {
      if (!this.open) {
        this.emit(
          dataConnection_DataConnection.EVENTS.error.key,
          new Error(
            'Connection is not open. You should listen for the `open` event before sending messages.'
          )
        );
        return;
      }
  
      if (data === undefined || data === null) {
        return;
      }
  
      if (this.serialization === dataConnection_DataConnection.SERIALIZATIONS.none.key) {
        this._sendBuffer.push(data);
        this._startSendLoop();
        return;
      } else if (this.serialization === dataConnection_DataConnection.SERIALIZATIONS.json.key) {
        this._sendBuffer.push(JSON.stringify(data));
        this._startSendLoop();
        return;
      }
  
      // Everything below is for serialization binary or binary-utf8
  
      const packedData = binarypack_default.a.pack(data);
      const size = packedData.size;
      const type = data.constructor.name;
  
      const dataMeta = {
        id: util.randomId(),
        type: type,
        size: size,
        totalParts: 0,
      };
  
      if (type === 'File') {
        dataMeta.name = data.name;
      }
      if (data instanceof Blob) {
        dataMeta.mimeType = data.type;
      }
  
      // dataMeta contains all possible parameters by now.
      // Adjust the chunk size to avoid issues with sending
      const chunkSize = config.maxChunkSize - object_sizeof_default()(dataMeta);
      const numSlices = Math.ceil(size / chunkSize);
      dataMeta.totalParts = numSlices;
  
      // Perform any required slicing
      for (let sliceIndex = 0; sliceIndex < numSlices; sliceIndex++) {
        const slice = packedData.slice(
          sliceIndex * chunkSize,
          (sliceIndex + 1) * chunkSize
        );
        dataMeta.index = sliceIndex;
        dataMeta.data = slice;
  
        // Add all chunks to our buffer and start the send loop (if we haven't already)
        util.blobToArrayBuffer(binarypack_default.a.pack(dataMeta), ab => {
          this._sendBuffer.push(ab);
          this._startSendLoop();
        });
      }
    }
  
    /**
     * Disconnect from remote peer.
     * @fires DataConnection#close
     */
    close(forceClose) {
      super.close(forceClose);
  
      this._isOnOpenCalled = false;
    }
  
    /**
     * Start sending messages at intervals to allow other threads to run.
     * @private
     */
    _startSendLoop() {
      if (!this.sendInterval) {
        // Define send interval
        // Try sending a new chunk with every callback
        this.sendInterval = setInterval(() => {
          // Might need more extensive buffering than this:
          const currMsg = this._sendBuffer.shift();
          try {
            this._dc.send(currMsg);
          } catch (error) {
            this._sendBuffer.push(currMsg);
          }
  
          if (this._sendBuffer.length === 0) {
            clearInterval(this.sendInterval);
            this.sendInterval = undefined;
          }
        }, config.sendInterval);
      }
    }
  
    /**
     * Check dcInit argument is valid to enable unreliable mode.
     * See https://www.w3.org/TR/webrtc/#dom-rtcdatachannelinit
     * @param {Object} dcInit - Options passed to createDataChannel() as a RTCDataChannelInit.
     *                  See https://www.w3.org/TR/webrtc/#dom-rtcdatachannelinit
     * @return {boolean} Returns this dcInit has valid properties to enable unreliable mode.
     */
    _isUnreliableDCInit(dcInit) {
      if (!dcInit) {
        return false;
      }
  
      // Either of these props are passed, works on unreliable mode.
      if ('maxRetransmits' in dcInit || 'maxPacketLifeTime' in dcInit) {
        return true;
      }
  
      return false;
    }
  
    /**
     * Possible serializations for the DataConnection.
     * @type {Enum}
     */
    static get SERIALIZATIONS() {
      return DCSerializations;
    }
  
    /**
     * Events the DataConnection class can emit.
     * @type {Enum}
     */
    static get EVENTS() {
      return DCEvents;
    }
  
    /**
     * DataConnection created event.
     *
     * @event DataConnection#open
     */
  
    /**
     * Data received from peer.
     *
     * @event DataConnection#data
     * @type {*}
     */
  
    /**
     * Error occurred.
     *
     * @event DataConnection#error
     * @type {Error}
     */
  }
  
  /* harmony default export */ var dataConnection = (dataConnection_DataConnection);
  
  // CONCATENATED MODULE: ./src/peer/mediaConnection.js
  
  
  
  
  
  
  const MCEvents = new enum_default.a(['stream', 'removeStream']);
  
  MCEvents.extend(peer_connection.EVENTS.enums);
  
  /**
   * Class that manages data connections to other peers.
   * @extends Connection
   */
  class mediaConnection_MediaConnection extends peer_connection {
    /**
     * Create a data connection to another peer.
     * @param {string} remoteId - The peerId of the peer you are connecting to.
     * @param {object} [options] - Optional arguments for the connection.
     * @param {string} [options.connectionId] - An ID to uniquely identify the connection. Defaults to random string if not specified.
     * @param {string} [options.label] - Label to easily identify the connection on either peer.
     * @param {object} [options.pcConfig] - A RTCConfiguration dictionary for the RTCPeerConnection.
     * @param {object} [options.stream] - The MediaStream to send to the remote peer. Set only when on the caller side.
     * @param {boolean} [options.originator] - true means the peer is the originator of the connection.
     * @param {string} [options.queuedMessages] - An array of messages that were already received before the connection was created.
     * @param {string} [options.payload] - An offer message that triggered creating this object.
     * @param {number} [options.videoBandwidth] - A max video bandwidth(kbps)
     * @param {number} [options.audioBandwidth] - A max audio bandwidth(kbps)
     * @param {string} [options.videoCodec] - A video codec like 'H264'
     * @param {string} [options.audioCodec] - A video codec like 'PCMU'
     * @param {boolean} [options.videoReceiveEnabled] - A flag to set video recvonly
     * @param {boolean} [options.audioReceiveEnabled] - A flag to set audio recvonly
     */
    constructor(remoteId, options) {
      super(remoteId, options);
  
      this._idPrefix = 'mc_';
      this.type = 'media';
  
      /**
       * The local MediaStream.
       * @type {MediaStream}
       */
      this.localStream = this._options.stream;
  
      // Messages stored by peer because MC was not ready yet
      this._queuedMessages = this._options.queuedMessages || [];
      this._pcAvailable = false;
    }
  
    /**
     * Start connection via negotiator and handle queued messages.
     * @return {Promise<void>} Promise that resolves when starting is done.
     */
    async startConnection() {
      if (!this._options.originator) {
        return;
      }
  
      await this._negotiator.startConnection({
        type: 'media',
        stream: this.localStream,
        originator: this._options.originator,
        pcConfig: this._options.pcConfig,
        videoBandwidth: this._options.videoBandwidth,
        audioBandwidth: this._options.audioBandwidth,
        videoCodec: this._options.videoCodec,
        audioCodec: this._options.audioCodec,
        videoReceiveEnabled: this._options.videoReceiveEnabled,
        audioReceiveEnabled: this._options.audioReceiveEnabled,
      });
  
      this._pcAvailable = true;
      this._handleQueuedMessages();
    }
  
    /**
     * Create and send an answer message.
     * @param {MediaStream} stream - The stream to send to the peer.
     * @param {object} [options] - Optional arguments for the connection.
     * @param {number} [options.videoBandwidth] - A max video bandwidth(kbps)
     * @param {number} [options.audioBandwidth] - A max audio bandwidth(kbps)
     * @param {string} [options.videoCodec] - A video codec like 'H264'
     * @param {string} [options.audioCodec] - A video codec like 'PCMU'
     * @param {boolean} [options.videoReceiveEnabled] - A flag to set video recvonly
     * @param {boolean} [options.audioReceiveEnabled] - A flag to set audio recvonly
     */
    answer(stream, options = {}) {
      if (this.localStream) {
        logger.warn(
          'localStream already exists on this MediaConnection. Are you answering a call twice?'
        );
        return;
      }
  
      this._options.payload.stream = stream;
  
      this.localStream = stream;
      this._negotiator.startConnection({
        type: 'media',
        stream: this.localStream,
        originator: false,
        offer: this._options.payload.offer,
        pcConfig: this._options.pcConfig,
        audioBandwidth: options.audioBandwidth,
        videoBandwidth: options.videoBandwidth,
        videoCodec: options.videoCodec,
        audioCodec: options.audioCodec,
        videoReceiveEnabled: options.videoReceiveEnabled,
        audioReceiveEnabled: options.audioReceiveEnabled,
      });
      this._pcAvailable = true;
  
      this._handleQueuedMessages();
  
      this.open = true;
    }
  
    /**
     * Replace the stream being sent with a new one.
     * @param {MediaStream} newStream - The stream to replace the old stream with.
     */
    replaceStream(newStream) {
      this._negotiator.replaceStream(newStream);
      this.localStream = newStream;
    }
  
    /**
     * Set up negotiator message handlers.
     * @private
     */
    _setupNegotiatorMessageHandlers() {
      super._setupNegotiatorMessageHandlers();
  
      this._negotiator.on(negotiator.EVENTS.addStream.key, remoteStream => {
        logger.log('Receiving stream', remoteStream);
  
        // return if the remoteStream which we will add already exists
        if (this.remoteStream && this.remoteStream.id === remoteStream.id) {
          return;
        }
        this.remoteStream = remoteStream;
  
        this.emit(mediaConnection_MediaConnection.EVENTS.stream.key, remoteStream);
      });
  
      this._negotiator.on(negotiator.EVENTS.removeStream.key, remoteStream => {
        logger.log('Stream removed', remoteStream);
  
        // Don't unset if a new stream has already replaced the old one
        if (this.remoteStream === remoteStream) {
          this.remoteStream = null;
        }
        this.emit(mediaConnection_MediaConnection.EVENTS.removeStream.key, remoteStream);
      });
    }
  
    /**
     * Events the MediaConnection class can emit.
     * @type {Enum}
     */
    static get EVENTS() {
      return MCEvents;
    }
  
    /**
     * MediaStream received from peer.
     *
     * @event MediaConnection#stream
     * @type {MediaStream}
     */
  
    /**
     * MediaStream from peer was removed.
     *
     * @event MediaConnection#removeStream
     * @type {MediaStream}
     */
  }
  
  /* harmony default export */ var mediaConnection = (mediaConnection_MediaConnection);
  
  // CONCATENATED MODULE: ./src/peer/room.js
  
  
  
  const Events = [
    'stream',
    'removeStream',
    'open',
    'close',
    'peerJoin',
    'peerLeave',
    'error',
    'data',
    'log',
  ];
  
  const MessageEvents = [
    'offer',
    'answer',
    'candidate',
    'leave',
    'close',
    'getLog',
    'broadcast',
  ];
  
  const RoomEvents = new enum_default.a(Events);
  const RoomMessageEvents = new enum_default.a(MessageEvents);
  
  /**
   * Class to manage rooms where one or more users can participate
   * @extends EventEmitter
   */
  class room_Room extends events_default.a {
    /**
     * Creates a Room instance.
     * @param {string} name - Room name.
     * @param {string} peerId - User's peerId.
     * @param {object} [options] - Optional arguments for the connection.
     * @param {object} [options.stream] - User's medias stream to send other participants.
     * @param {object} [options.pcConfig] - A RTCConfiguration dictionary for the RTCPeerConnection.
     * @param {number} [options.videoBandwidth] - A max video bandwidth(kbps)
     * @param {number} [options.audioBandwidth] - A max audio bandwidth(kbps)
     * @param {string} [options.videoCodec] - A video codec like 'H264'
     * @param {string} [options.audioCodec] - A video codec like 'PCMU'
     * @param {boolean} [options.videoReceiveEnabled] - A flag to set video recvonly
     * @param {boolean} [options.audioReceiveEnabled] - A flag to set audio recvonly
     */
    constructor(name, peerId, options = {}) {
      super();
  
      // Abstract class
      if (this.constructor === room_Room) {
        throw new TypeError('Cannot construct Room instances directly');
      }
  
      this.name = name;
      this._options = options;
      this._peerId = peerId;
      this._localStream = this._options.stream;
  
      this._pcConfig = this._options.pcConfig;
    }
  
    /**
     * Handle received data message from other paricipants in the room.
     * It emits data event.
     * @param {object} dataMessage - The data message to handle.
     * @param {ArrayBuffer} dataMessage.data - The data that a peer sent in the room.
     * @param {string} dataMessage.src -  The peerId of the peer who sent the data.
     * @param {string} [dataMessage.roomName] -  The name of the room user is joining.
     */
    handleData(dataMessage) {
      const message = {
        data: dataMessage.data,
        src: dataMessage.src,
      };
      this.emit(room_Room.EVENTS.data.key, message);
    }
  
    /**
     * Handle received log message.
     * It emits log event with room's logs.
     * @param {Array} logs - An array containing JSON text.
     */
    handleLog(logs) {
      this.emit(room_Room.EVENTS.log.key, logs);
    }
  
    /**
     * Start getting room's logs from SkyWay server.
     */
    getLog() {
      const message = {
        roomName: this.name,
      };
      this.emit(room_Room.MESSAGE_EVENTS.getLog.key, message);
    }
  
    /**
     * Events the Room class can emit.
     * @type {Enum}
     */
    static get EVENTS() {
      return RoomEvents;
    }
  
    /**
     * MediaStream received from peer in the room.
     *
     * @event Room#stream
     * @type {MediaStream}
     */
  
    /**
     * Room is ready.
     *
     * @event Room#open
     */
  
    /**
     * All connections in the room has closed.
     *
     * @event Room#close
     */
  
    /**
     * New peer has joined.
     *
     * @event Room#peerJoin
     * @type {string}
     */
  
    /**
     * A peer has left.
     *
     * @event Room#peerLeave
     * @type {string}
     */
  
    /**
     * Error occured
     *
     * @event Room#error
     */
  
    /**
     * Data received from peer.
     *
     * @event Room#data
     * @type {object}
     * @property {string} src - The peerId of the peer who sent the data.
     * @property {*} data - The data that a peer sent in the room.
     */
  
    /**
     * Room's log received.
     *
     * @event Room#log
     * @type {Array}
     */
  
    /**
     * Connection closed event.
     *
     * @event Connection#close
     */
  
    /**
     * Events the Room class can emit.
     * @type {Enum}
     */
    static get MESSAGE_EVENTS() {
      return RoomMessageEvents;
    }
  
    /**
     * Offer created event.
     *
     * @event Room#offer
     * @type {object}
     * @property {RTCSessionDescription} offer - The local offer to send to the peer.
     * @property {string} dst - Destination peerId
     * @property {string} connectionId - This connection's id.
     * @property {string} connectionType - This connection's type.
     * @property {object} metadata - Any extra data to send with the connection.
     */
  
    /**
     * Answer created event.
     *
     * @event Room#answer
     * @type {object}
     * @property {RTCSessionDescription} answer - The local answer to send to the peer.
     * @property {string} dst - Destination peerId
     * @property {string} connectionId - This connection's id.
     * @property {string} connectionType - This connection's type.
     */
  
    /**
     * ICE candidate created event.
     *
     * @event Room#candidate
     * @type {object}
     * @property {RTCIceCandidate} candidate - The ice candidate.
     * @property {string} dst - Destination peerId
     * @property {string} connectionId - This connection's id.
     * @property {string} connectionType - This connection's type.
     */
  
    /**
     * Left the room.
     *
     * @event Room#peerLeave
     * @type {object}
     * @property {string} roomName - The room name.
     */
  
    /**
     * Get room log from SkyWay server.
     *
     * @event Room#log
     * @type {object}
     * @property {string} roomName - The room name.
     */
  }
  
  /* harmony default export */ var peer_room = (room_Room);
  
  // CONCATENATED MODULE: ./src/peer/sfuRoom.js
  
  
  
  
  
  
  
  
  const sfuRoom_MessageEvents = ['offerRequest', 'candidate'];
  
  const SFUEvents = new enum_default.a([]);
  SFUEvents.extend(peer_room.EVENTS.enums);
  const SFUMessageEvents = new enum_default.a(sfuRoom_MessageEvents);
  SFUMessageEvents.extend(peer_room.MESSAGE_EVENTS.enums);
  
  /**
   * Class that manages SFU type room.
   * @extends Room
   */
  class sfuRoom_SFURoom extends peer_room {
    /**
     * Creates a SFU type room.
     * @param {string} name - Room name.
     * @param {string} peerId - peerId - User's peerId.
     * @param {object} [options] - Optional arguments for the connection.
     * @param {MediaStream} [options.stream] - The MediaStream to send to the remote peer.
     * @param {object} [options.pcConfig] - A RTCConfiguration dictionary for the RTCPeerConnection.
     * @param {number} [options.videoBandwidth] - A max video bandwidth(kbps)
     * @param {number} [options.audioBandwidth] - A max audio bandwidth(kbps)
     * @param {string} [options.videoCodec] - A video codec like 'H264'
     * @param {string} [options.audioCodec] - A video codec like 'PCMU'
     * @param {boolean} [options.videoReceiveEnabled] - A flag to set video recvonly
     * @param {boolean} [options.audioReceiveEnabled] - A flag to set audio recvonly
     */
    constructor(name, peerId, options) {
      super(name, peerId, options);
  
      this.remoteStreams = {};
      this.members = [];
  
      this._open = false;
      this._msidMap = {};
      this._unknownStreams = {};
  
      this._negotiator = new negotiator();
    }
  
    /**
     * Send Offer request message to SFU server.
     * @param {MediaStream} [stream] - A media stream to send.
     */
    call(stream) {
      if (stream) {
        this._localStream = stream;
      }
  
      const data = {
        roomName: this.name,
      };
  
      this.emit(sfuRoom_SFURoom.MESSAGE_EVENTS.offerRequest.key, data);
    }
  
    /**
     * Handles Offer message from SFU server.
     * It create new RTCPeerConnection object.
     * @param {object} offerMessage - Message object containing Offer SDP.
     * @param {object} offerMessage.offer - Object containing Offer SDP text.
     */
    handleOffer(offerMessage) {
      let offer = offerMessage.offer;
  
      /**
       * Our SFU returns unified-plan SDP.
       * Our support browsers and sdp semantics relations are
       *
       * Chrome: plan-b(controlled by us)
       * Firefox: unified-plan
       * Safari 12.1~: plan-b
       * Safari 12.1~: unified-plan(if user enables)
       *
       * So we need to convert server offer SDP if it in case.
       * We don't need to convert the answer back to Unified Plan because the server can handle Plan B.
       */
      const browserInfo = util.detectBrowser();
      // Means Chrome or Safari(plan-b)
      if (!(browserInfo.name === 'firefox' || util.isUnifiedPlanSafari())) {
        offer = sdpUtil.unifiedToPlanB(offer);
      }
  
      // Handle SFU Offer and send Answer to Server
      if (this._connectionStarted) {
        this._negotiator.handleOffer(offer);
      } else {
        this._negotiator.startConnection({
          type: 'media',
          stream: this._localStream,
          pcConfig: this._options.pcConfig,
          offer: offer,
        });
        this._setupNegotiatorMessageHandlers();
        this._connectionStarted = true;
      }
    }
  
    /**
     * Handle messages from the negotiator.
     * @private
     */
    _setupNegotiatorMessageHandlers() {
      this._negotiator.on(negotiator.EVENTS.addStream.key, stream => {
        const remoteStream = stream;
  
        if (this._msidMap[remoteStream.id]) {
          remoteStream.peerId = this._msidMap[remoteStream.id];
  
          // return if the remoteStream's peerID is my peerID
          if (remoteStream.peerId === this._peerId) {
            return;
          }
  
          // return if the cachedStream which we will add already exists
          const cachedStream = this.remoteStreams[remoteStream.id];
          if (cachedStream && cachedStream.id === remoteStream.id) {
            return;
          }
          this.remoteStreams[remoteStream.id] = remoteStream;
          this.emit(sfuRoom_SFURoom.EVENTS.stream.key, remoteStream);
  
          logger.log(
            `Received remote media stream for ${remoteStream.peerId} in ${
              this.name
            }`
          );
        } else {
          this._unknownStreams[remoteStream.id] = remoteStream;
        }
      });
  
      this._negotiator.on(negotiator.EVENTS.removeStream.key, stream => {
        delete this.remoteStreams[stream.id];
        delete this._msidMap[stream.id];
        delete this._unknownStreams[stream.id];
  
        this.emit(sfuRoom_SFURoom.EVENTS.removeStream.key, stream);
      });
  
      this._negotiator.on(negotiator.EVENTS.negotiationNeeded.key, () => {
        // Renegotiate by requesting an offer then sending an answer when one is created.
        const offerRequestMessage = {
          roomName: this.name,
        };
        this.emit(sfuRoom_SFURoom.MESSAGE_EVENTS.offerRequest.key, offerRequestMessage);
      });
  
      this._negotiator.on(negotiator.EVENTS.answerCreated.key, answer => {
        // If we specify sdpSemantics: unified-plan in Chrome, need to add Chrome here
        // or fix some lines to ensure SDP to be recognized by SFU.
        if (util.isUnifiedPlanSafari()) {
          answer.sdp = sdpUtil.pretendUnifiedPlan(answer.sdp);
        }
  
        const answerMessage = {
          roomName: this.name,
          answer: answer,
        };
        this.emit(sfuRoom_SFURoom.MESSAGE_EVENTS.answer.key, answerMessage);
      });
  
      this._negotiator.on(negotiator.EVENTS.iceConnectionFailed.key, () => {
        this.close();
      });
  
      this._negotiator.on(negotiator.EVENTS.iceCandidate.key, candidate => {
        const candidateMessage = {
          roomName: this.name,
          candidate: candidate,
        };
        this.emit(sfuRoom_SFURoom.MESSAGE_EVENTS.candidate.key, candidateMessage);
      });
    }
  
    /**
     * Handles Join message from SFU server.
     * It emits peerJoin event and if the message contains user's peerId, also emits open event.
     * @param {Object} joinMessage - Message object.
     * @param {string} joinMessage.src - The peerId of the peer that joined.
     * @param {string} joinMessage.roomName - The name of the joined room.
     */
    handleJoin(joinMessage) {
      const src = joinMessage.src;
  
      if (src === this._peerId) {
        this._open = true;
  
        this.call(this._localStream);
        this.emit(sfuRoom_SFURoom.EVENTS.open.key);
  
        // At this stage the Server has acknowledged us joining a room
        return;
      }
  
      this.members.push(src);
      this.emit(sfuRoom_SFURoom.EVENTS.peerJoin.key, src);
    }
  
    /**
     * Handles Leave message from SFU server.
     * It emits peerLeave message.
     * @param {Object} leaveMessage - Message from SFU server.
     */
    handleLeave(leaveMessage) {
      if (!this._open) {
        return;
      }
  
      const src = leaveMessage.src;
  
      const index = this.members.indexOf(src);
      if (index >= 0) {
        this.members.splice(index, 1);
      }
  
      // Delete leaving peer's streams
      for (const msid in this.remoteStreams) {
        if (this.remoteStreams[msid].peerId === src) {
          delete this.remoteStreams[msid];
        }
      }
  
      this.emit(sfuRoom_SFURoom.EVENTS.peerLeave.key, src);
    }
  
    /**
     * Send data to all participants in the room with WebSocket.
     * It emits broadcast event.
     * @param {*} data - The data to send.
     */
    send(data) {
      if (!this._open) {
        return;
      }
  
      const message = {
        roomName: this.name,
        data: data,
      };
      this.emit(sfuRoom_SFURoom.MESSAGE_EVENTS.broadcast.key, message);
    }
  
    /**
     * Close PeerConnection and emit leave and close event.
     */
    close() {
      if (!this._open) {
        return;
      }
  
      if (this._negotiator) {
        this._negotiator.cleanup();
      }
  
      this._open = false;
  
      const message = {
        roomName: this.name,
      };
      this.emit(sfuRoom_SFURoom.MESSAGE_EVENTS.leave.key, message);
      this.emit(sfuRoom_SFURoom.EVENTS.close.key);
    }
  
    /**
     * Replace the stream being sent with a new one.
     * @param {MediaStream} newStream - The stream to replace the old stream with.
     */
    replaceStream(newStream) {
      this._localStream = newStream;
      this._negotiator.replaceStream(newStream);
    }
  
    /**
     * Update the entries in the msid to peerId map.
     * @param {Object} msids - Object with msids as the key and peerIds as the values.
     */
    updateMsidMap(msids = {}) {
      this._msidMap = msids;
  
      for (const msid of Object.keys(this._unknownStreams)) {
        if (this._msidMap[msid]) {
          const remoteStream = this._unknownStreams[msid];
          remoteStream.peerId = this._msidMap[remoteStream.id];
  
          delete this._unknownStreams[msid];
  
          if (remoteStream.peerId === this._peerId) {
            return;
          }
  
          this.remoteStreams[remoteStream.id] = remoteStream;
          this.emit(sfuRoom_SFURoom.EVENTS.stream.key, remoteStream);
        }
      }
    }
  
    /**
     * Events the SFURoom class can emit.
     * @type {Enum}
     */
    static get EVENTS() {
      return SFUEvents;
    }
  
    /**
     * Message events the MeshRoom class can emit.
     * @type {Enum}
     */
    static get MESSAGE_EVENTS() {
      return SFUMessageEvents;
    }
  
    /**
     * Send offer request to SkyWay server.
     *
     * @event SFURoom#offerRequest
     * @type {object}
     * @property {string} roomName - The Room name.
  
     */
  
    /**
     * Send data to all peers in the room by WebSocket.
     *
     * @event SFURoom#broadcast
     * @type {object}
     * @property {string} roomName - The Room name.
     * @property {*} data - The data to send.
     */
  }
  
  /* harmony default export */ var peer_sfuRoom = (sfuRoom_SFURoom);
  
  // CONCATENATED MODULE: ./src/peer/meshRoom.js
  
  
  
  
  
  
  
  
  
  const meshRoom_MessageEvents = ['broadcastByDC', 'getPeers'];
  
  const MeshEvents = new enum_default.a([]);
  MeshEvents.extend(peer_room.EVENTS.enums);
  const MeshMessageEvents = new enum_default.a(meshRoom_MessageEvents);
  MeshMessageEvents.extend(peer_room.MESSAGE_EVENTS.enums);
  
  /**
   * Class that manages fullmesh type room.
   * @extends Room
   */
  class meshRoom_MeshRoom extends peer_room {
    /**
     * Create a fullmesh room.
     * @param {string} name - Room name.
     * @param {string} peerId - User's peerId.
     * @param {object} [options] - Optional arguments for the connection.
     * @param {MediaStream} [options.stream] - The MediaStream to send to the remote peer.
     * @param {object} [options.pcConfig] - A RTCConfiguration dictionary for the RTCPeerConnection.
     * @param {number} [options.videoBandwidth] - A max video bandwidth(kbps)
     * @param {number} [options.audioBandwidth] - A max audio bandwidth(kbps)
     * @param {string} [options.videoCodec] - A video codec like 'H264'
     * @param {string} [options.audioCodec] - A video codec like 'PCMU'
     * @param {boolean} [options.videoReceiveEnabled] - A flag to set video recvonly
     * @param {boolean} [options.audioReceiveEnabled] - A flag to set audio recvonly
     */
    constructor(name, peerId, options) {
      super(name, peerId, options);
  
      this.connections = {};
  
      // messages(candidates) received before connection is ready
      this._queuedMessages = {};
    }
  
    /**
     * Called by client app to create MediaConnections.
     * It emit getPeers event for getting peerIds of all of room participant.
     * After getting peerIds, makeMCs is called.
     * @param {MediaStream} [stream] - The MediaStream to send to the remote peer.
     */
    call(stream) {
      if (stream) {
        this._localStream = stream;
      }
  
      const data = {
        roomName: this.name,
        type: 'media',
      };
  
      this.emit(meshRoom_MeshRoom.MESSAGE_EVENTS.getPeers.key, data);
    }
  
    /**
     * Called by client app to create DataConnections.
     * It emit getPeers event for getting peerIds of all of room participant.
     * After getting peerIds, makeDCs is called.
     */
    connect() {
      const data = {
        roomName: this.name,
        type: 'data',
      };
  
      this.emit(meshRoom_MeshRoom.MESSAGE_EVENTS.getPeers.key, data);
    }
  
    /**
     * Start video call to all participants in the room.
     * @param {Array} peerIds - Array of peerIds you are calling to.
     */
    makeMediaConnections(peerIds) {
      const options = {
        stream: this._localStream,
        pcConfig: this._pcConfig,
        originator: true,
        videoBandwidth: this._options.videoBandwidth,
        audioBandwidth: this._options.audioBandwidth,
        videoCodec: this._options.videoCodec,
        audioCodec: this._options.audioCodec,
        videoReceiveEnabled: this._options.videoReceiveEnabled,
        audioReceiveEnabled: this._options.audioReceiveEnabled,
      };
  
      this._makeConnections(peerIds, 'media', options);
    }
  
    /**
     * Start data connection to all participants in the room.
     * @param {Array} peerIds - Array of peerIds you are connecting to.
     */
    makeDataConnections(peerIds) {
      const options = {
        pcConfig: this._pcConfig,
      };
  
      this._makeConnections(peerIds, 'data', options);
    }
  
    /**
     * Handle join message from new participant in the room.
     * It emits peerJoin event and if the message contains user's peerId, also emits open event.
     * @param {Object} joinMessage - Message object.
     * @param {string} joinMessage.src - The peerId of the peer that joined.
     * @param {string} joinMessage.roomName - The name of the joined room.
     */
    handleJoin(joinMessage) {
      const src = joinMessage.src;
      if (src === this._peerId) {
        this.call(this._localStream);
        this.emit(meshRoom_MeshRoom.EVENTS.open.key);
  
        // At this stage the Server has acknowledged us joining a room
        return;
      }
  
      this.emit(meshRoom_MeshRoom.EVENTS.peerJoin.key, src);
    }
  
    /**
     * Handle leave message from other participant in the room.
     * It deletes connection from room's connections property and emits peerLeave event.
     * @param {Object} leaveMessage - Message object.
     * @param {string} leaveMessage.src - The peerId of the peer that left.
     * @param {string} leaveMessage.roomName - The name of the left room.
     */
    handleLeave(leaveMessage) {
      const src = leaveMessage.src;
      this._deleteConnections(src);
      this.emit(meshRoom_MeshRoom.EVENTS.peerLeave.key, src);
    }
  
    /**
     * Handle Offer message from new participant and create a Connection instance.
     * @param {object} offerMessage - Message object containing Offer SDP.
     * @param {object} offerMessage.offer - Object containing Offer SDP text.
     * @param {string} offerMessage.connectionId - An ID to uniquely identify the connection.
     * @param {string} offerMessage.connectionType - One of 'media' or 'data'.
     * @param {string} offerMessage.dst - The peerId of the peer who receiving the Offer.
     * @param {string} offerMessage.roomName - The name of the room user is joining.
     * @param {string} offerMessage.src - The peerId of the peer who sent the Offer.
     */
    handleOffer(offerMessage) {
      const connectionId = offerMessage.connectionId;
      let connection = this._getConnection(offerMessage.src, connectionId);
  
      if (connection) {
        connection.updateOffer(offerMessage);
        return;
      }
  
      if (offerMessage.connectionType === 'media') {
        connection = new mediaConnection(offerMessage.src, {
          connectionId: connectionId,
          payload: offerMessage,
          metadata: offerMessage.metadata,
          queuedMessages: this._queuedMessages[connectionId],
          pcConfig: this._pcConfig,
        });
        connection.startConnection();
  
        logger.log('MediaConnection created in OFFER');
        this._addConnection(offerMessage.src, connection);
        this._setupMessageHandlers(connection);
  
        connection.answer(this._localStream, {
          videoBandwidth: this._options.videoBandwidth,
          audioBandwidth: this._options.audioBandwidth,
          videoCodec: this._options.videoCodec,
          audioCodec: this._options.audioCodec,
          videoReceiveEnabled: this._options.videoReceiveEnabled,
          audioReceiveEnabled: this._options.audioReceiveEnabled,
        });
      } else {
        logger.warn(
          `Received malformed connection type: ${offerMessage.connectionType}`
        );
      }
    }
  
    /**
     * Handle Answer message from participant in the room.
     * @param {object} answerMessage - Message object containing Answer SDP.
     * @param {object} answerMessage.answer - Object containing Answer SDP text.
     * @param {string} answerMessage.connectionId - An ID to uniquely identify the connection.
     * @param {string} answerMessage.connectionType - One of 'media' or 'data'.
     * @param {string} answerMessage.dst - The peerId of the peer who receiving the Answer.
     * @param {string} answerMessage.roomName - The name of the room user is joining.
     * @param {string} answerMessage.src - The peerId of the peer who sent the Answer.
     */
    handleAnswer(answerMessage) {
      const connection = this._getConnection(
        answerMessage.src,
        answerMessage.connectionId
      );
  
      if (connection) {
        connection.handleAnswer(answerMessage);
      }
    }
  
    /**
     * Handles Candidate message from participant in the room.
     * @param {object} candidateMessage - Message object containing Candidate SDP.
     * @param {object} candidateMessage.candidate - Object containing Candidate SDP text.
     * @param {string} candidateMessage.connectionId - An ID to uniquely identify the connection.
     * @param {string} candidateMessage.connectionType - One of 'media' or 'data'.
     * @param {string} candidateMessage.dst - The peerId of the peer who receiving the Candidate.
     * @param {string} candidateMessage.roomName - The name of the room user is joining.
     * @param {string} candidateMessage.src - The peerId of the peer who sent the Candidate.
     */
    handleCandidate(candidateMessage) {
      const connection = this._getConnection(
        candidateMessage.src,
        candidateMessage.connectionId
      );
  
      if (connection) {
        connection.handleCandidate(candidateMessage);
      } else {
        // Looks like PeerConnection hasn't completed setRemoteDescription
        if (this._queuedMessages[candidateMessage.connectionId] === undefined) {
          this._queuedMessages[candidateMessage.connectionId] = [];
        }
        this._queuedMessages[candidateMessage.connectionId].push({
          type: config.MESSAGE_TYPES.SERVER.CANDIDATE.key,
          payload: candidateMessage,
        });
      }
    }
  
    /**
     * Send data to all participants in the room with WebSocket.
     * It emits broadcast event.
     * @param {*} data - The data to send.
     */
    send(data) {
      const message = {
        roomName: this.name,
        data: data,
      };
      this.emit(meshRoom_MeshRoom.MESSAGE_EVENTS.broadcast.key, message);
    }
  
    /**
     * Close all connections in the room.
     */
    close() {
      for (const peerId in this.connections) {
        if (this.connections.hasOwnProperty(peerId)) {
          this.connections[peerId].forEach(connection => {
            connection.close();
          });
        }
      }
      const message = {
        roomName: this.name,
      };
      this.emit(meshRoom_MeshRoom.MESSAGE_EVENTS.leave.key, message);
      this.emit(meshRoom_MeshRoom.EVENTS.close.key);
    }
  
    /**
     * Replace the stream being sent on all MediaConnections   with a new one.
     * @param {MediaStream} newStream - The stream to replace the old stream with.
     */
    replaceStream(newStream) {
      this._localStream = newStream;
      for (const peerId in this.connections) {
        if (this.connections.hasOwnProperty(peerId)) {
          this.connections[peerId].forEach(connection => {
            if (connection.type === 'media') {
              connection.replaceStream(newStream);
            }
          });
        }
      }
    }
  
    /**
     * Append a connection to peer's array of connections, stored in room.connections.
     * @param {string} peerId - User's peerID.
     * @param {MediaConnection|DataConnection} connection - An instance of MediaConnection or DataConnection.
     * @private
     */
    _addConnection(peerId, connection) {
      if (!this.connections[peerId]) {
        this.connections[peerId] = [];
      }
      this.connections[peerId].push(connection);
    }
  
    /**
     * Start connections and add handlers.
     * @param {Array} peerIds - Array of peerIds you are creating connections for.
     * @param {string} type - Either 'data' or 'media'.
     * @param {Object} options - Options to pass to the connection constructor.
     * @private
     */
    _makeConnections(peerIds, type, options) {
      peerIds
        .filter(peerId => {
          return peerId !== this._peerId;
        })
        .forEach(peerId => {
          let connection;
  
          switch (type) {
            case 'data':
              connection = new dataConnection(peerId, options);
              break;
            case 'media':
              connection = new mediaConnection(peerId, options);
              break;
            default:
              return;
          }
  
          connection.startConnection();
          this._addConnection(peerId, connection);
          this._setupMessageHandlers(connection);
  
          logger.log(`${type} connection to ${peerId} created in ${this.name}`);
        });
    }
  
    /**
     * Delete a connection according to given peerId.
     * @param {string} peerId - The id of the peer that will be deleted.
     * @private
     */
    _deleteConnections(peerId) {
      if (this.connections[peerId]) {
        delete this.connections[peerId];
      }
    }
  
    /**
     * Return a connection according to given peerId and connectionId.
     * @param {string} peerId - User's PeerId.
     * @param {string} connectionId - An ID to uniquely identify the connection.
     * @return {Connection} A connection according to given peerId and connectionId.
     * @private
     */
    _getConnection(peerId, connectionId) {
      if (this.connections && this.connections[peerId]) {
        const conn = this.connections[peerId].filter(connection => {
          return connection.id === connectionId;
        });
        return conn[0];
      }
      return null;
    }
  
    /**
     * Set up connection event and message handlers.
     * @param {MediaConnection|DataConnection} connection - An instance of MediaConnection or DataConnection.
     * @private
     */
    _setupMessageHandlers(connection) {
      connection.on(peer_connection.EVENTS.offer.key, offerMessage => {
        offerMessage.roomName = this.name;
        this.emit(meshRoom_MeshRoom.MESSAGE_EVENTS.offer.key, offerMessage);
      });
  
      connection.on(peer_connection.EVENTS.answer.key, answerMessage => {
        answerMessage.roomName = this.name;
        this.emit(meshRoom_MeshRoom.MESSAGE_EVENTS.answer.key, answerMessage);
      });
  
      connection.on(peer_connection.EVENTS.candidate.key, candidateMessage => {
        candidateMessage.roomName = this.name;
        this.emit(meshRoom_MeshRoom.MESSAGE_EVENTS.candidate.key, candidateMessage);
      });
  
      if (connection.type === 'media') {
        connection.on(mediaConnection.EVENTS.stream.key, remoteStream => {
          remoteStream.peerId = connection.remoteId;
          this.emit(meshRoom_MeshRoom.EVENTS.stream.key, remoteStream);
        });
  
        connection.on(mediaConnection.EVENTS.removeStream.key, remoteStream => {
          this.emit(meshRoom_MeshRoom.EVENTS.removeStream.key, remoteStream);
        });
      }
    }
  
    /**
     * Events the MeshRoom class can emit.
     * @type {Enum}
     */
    static get EVENTS() {
      return MeshEvents;
    }
  
    /**
     * Message events the MeshRoom class can emit.
     * @type {Enum}
     */
    static get MESSAGE_EVENTS() {
      return MeshMessageEvents;
    }
  
    /**
     * Get all peer's peerId joining in the room.
     * @event MeshRoom#getPeers
     * @type {object}
     * @property {string} roomName - The Room name.
     * @property {string} type - One of 'media' or 'data'.
  
     */
  
    /**
     * Send data to all peers in the room by WebSocket.
     *
     * @event MeshRoom#broadcastByWS
     * @type {object}
     * @property {string} roomName - The Room name.
     * @property {*} data - The data to send.
     */
  
    /**
     * Send data to all peers in the room by DataChannel.
     *
     * @event MeshRoom#broadcastByDC
     * @type {object}
     * @property {string} roomName - The Room name.
     * @property {*} data - The data to send.
     */
  }
  
  /* harmony default export */ var peer_meshRoom = (meshRoom_MeshRoom);
  
  // CONCATENATED MODULE: ./src/peer.js
  
  
  
  
  
  
  
  
  
  
  
  
  
  const PeerEvents = new enum_default.a([
    'open',
    'error',
    'call',
    'connection',
    'expiresin',
    'close',
    'disconnected',
  ]);
  
  /**
   * Class that manages all p2p connections and rooms.
   * This class contains socket.io message handlers.
   * @extends EventEmitter
   */
  class peer_Peer extends events_default.a {
    /**
     * Create new Peer instance. This is called by user application.
     * @param {string} [id] - User's peerId.
     * @param {Object} options - Optional arguments for the connection.
     * @param {string} options.key - SkyWay API key.
     * @param {number} [options.debug=0] - Log level. NONE:0, ERROR:1, WARN:2, FULL:3.
     * @param {string} [options.host] - The host name of signaling server.
     * @param {number} [options.port] - The port number of signaling server.
     * @param {string} [options.dispatcherPort=dispatcher.webrtc.ecl.ntt.com] - The host name of the dispatcher server.
     * @param {number} [options.dispatcherPort=443] - The port number of dispatcher server.
     * @param {boolean} [options.dispatcherSecure=true] - True if the dispatcher server supports https.
     * @param {object} [options.config=config.defaultConfig] - A RTCConfiguration dictionary for the RTCPeerConnection.
     * @param {boolean} [options.turn=true] - Whether using TURN or not.
     * @param {object} [options.credential] - The credential used to authenticate peer.
     + @param {number} [options.credential.timestamp] - Current UNIX timestamp.
     + @param {number} [options.credential.ttl] - Time to live; The credential expires at timestamp + ttl.
     + @param {string} [options.credential.authToken] - Credential token calculated with HMAC.
     */
    constructor(id, options) {
      super();
  
      this.connections = {};
      this.rooms = {};
  
      // messages received before connection is ready
      this._queuedMessages = {};
  
      if (id && id.constructor === Object) {
        options = id;
        id = undefined;
      } else if (id) {
        id = id.toString();
      }
  
      const defaultOptions = {
        debug: logger.LOG_LEVELS.NONE,
        secure: true,
        token: util.randomToken(),
        config: config.defaultConfig,
        turn: true,
  
        dispatcherSecure: config.DISPATCHER_SECURE,
        dispatcherHost: config.DISPATCHER_HOST,
        dispatcherPort: config.DISPATCHER_PORT,
      };
  
      this.options = Object.assign({}, defaultOptions, options);
  
      logger.setLogLevel(this.options.debug);
  
      if (!util.validateId(id)) {
        this._abort('invalid-id', `ID "${id}" is invalid`);
        return;
      }
  
      if (!util.validateKey(options.key)) {
        this._abort('invalid-key', `API KEY "${this.options.key}" is invalid`);
        return;
      }
  
      if (this.options.host === '/') {
        this.options.host = window.location.hostname;
      }
      if (options.secure === undefined && this.options.port !== 443) {
        this.options.secure = undefined;
      }
      this._initializeServerConnection(id);
    }
  
    /**
     * Creates new MediaConnection.
     * @param {string} peerId - The peerId of the peer you are connecting to.
     * @param {MediaStream} [stream] - The MediaStream to send to the remote peer.
     *                               If not set, the caller creates offer SDP with `sendonly` attribute.
     * @param {object} [options] - Optional arguments for the connection.
     * @param {string} [options.connectionId] - An ID to uniquely identify the connection.
     * @param {string} [options.label] - Label to easily identify the connection on either peer.
     * @param {number} [options.videoBandwidth] - A max video bandwidth(kbps)
     * @param {number} [options.audioBandwidth] - A max audio bandwidth(kbps)
     * @param {string} [options.videoCodec] - A video codec like 'H264'
     * @param {string} [options.audioCodec] - A video codec like 'PCMU'
     * @param {boolean} [options.videoReceiveEnabled] - A flag to set video recvonly
     * @param {boolean} [options.audioReceiveEnabled] - A flag to set audio recvonly
     * @return {MediaConnection} An instance of MediaConnection.
     */
    call(peerId, stream, options = {}) {
      if (!this._checkOpenStatus()) {
        return;
      }
  
      options.originator = true;
      options.stream = stream;
      options.pcConfig = this._pcConfig;
      const mc = new mediaConnection(peerId, options);
      mc.startConnection();
      logger.log('MediaConnection created in call method');
      this._addConnection(peerId, mc);
      return mc;
    }
  
    /**
     * Creates new DataConnection.
     * @param {string} peerId - User's peerId.
     * @param {Object} [options] - Optional arguments for DataConnection.
     * @param {string} [options.connectionId] - An ID to uniquely identify the connection.
     * @param {string} [options.label] - Label to easily identify the connection on either peer.
     * @param {Object} [options.dcInit] - Options passed to createDataChannel() as a RTCDataChannelInit.
     *                  See https://www.w3.org/TR/webrtc/#dom-rtcdatachannelinit
     * @param {string} [options.serialization] - How to serialize data when sending.
     *                  One of 'binary', 'json' or 'none'.
     * @return {DataConnection} An instance of DataConnection.
     */
    connect(peerId, options = {}) {
      if (!this._checkOpenStatus()) {
        return;
      }
  
      options.pcConfig = this._pcConfig;
      const connection = new dataConnection(peerId, options);
      connection.startConnection();
      logger.log('DataConnection created in connect method');
      this._addConnection(peerId, connection);
      return connection;
    }
  
    /**
     * Join fullmesh type or SFU type room that two or more users can join.
     * @param {string} roomName - The name of the room user is joining to.
     * @param {object} [roomOptions]- Optional arguments for the RTCPeerConnection.
     * @param {string} [roomOptions.mode='mesh'] - One of 'sfu' or 'mesh'.
     * @param {MediaStream} [roomOptions.stream] - Media stream user wants to emit.
     * @param {number} [roomOptions.videoBandwidth] - A max video bandwidth(kbps)
     * @param {number} [roomOptions.audioBandwidth] - A max audio bandwidth(kbps)
     * @param {string} [roomOptions.videoCodec] - A video codec like 'H264'
     * @param {string} [roomOptions.audioCodec] - A video codec like 'PCMU'
     * @param {boolean} [options.videoReceiveEnabled] - A flag to set video recvonly
     * @param {boolean} [options.audioReceiveEnabled] - A flag to set audio recvonly
     * @return {SFURoom|MeshRoom} - An instance of SFURoom or MeshRoom.
     */
    joinRoom(roomName, roomOptions = {}) {
      if (!this._checkOpenStatus()) {
        return;
      }
  
      if (!roomName) {
        const err = new Error('Room name must be defined.');
        err.type = 'room-error';
        logger.error(err);
        this.emit(peer_Peer.EVENTS.error.key, err);
        return null;
      }
  
      roomOptions.pcConfig = this._pcConfig;
      roomOptions.peerId = this.id;
  
      if (roomOptions.mode === 'sfu') {
        return this._initializeSfuRoom(roomName, roomOptions);
      }
  
      // mode is blank or 'mesh'
      return this._initializeFullMeshRoom(roomName, roomOptions);
    }
  
    /**
     * Returns a connection according to given peerId and connectionId.
     * @param {string} peerId - The peerId of the connection to be searched.
     * @param {Object} connectionId - An ID to uniquely identify the connection.
     * @return {MediaConnection|DataConnection} Search result.
     */
    getConnection(peerId, connectionId) {
      if (!this._checkOpenStatus()) {
        return;
      }
  
      if (this.connections[peerId]) {
        for (const connection of this.connections[peerId]) {
          if (connection.id === connectionId) {
            return connection;
          }
        }
      }
      return null;
    }
  
    /**
     * Whether the socket is connecting to the signalling server or not.
     * @type {boolean} The open status.
     */
    get open() {
      return this.socket && this.socket.isOpen;
    }
  
    /**
     * Close all connections and disconnect socket.
     */
    destroy() {
      this._cleanup();
      this.disconnect();
    }
  
    /**
     * Close socket and clean up some properties, then emit disconnect event.
     */
    disconnect() {
      if (this.open) {
        this.socket.close();
        this.emit(peer_Peer.EVENTS.disconnected.key, this.id);
      }
    }
  
    /**
     * Reconnect to SkyWay server. Does not work after a peer.destroy().
     */
    reconnect() {
      if (!this.open) {
        this.socket.reconnect();
      }
    }
  
    /**
     * Update server-side credential by sending a request in order to extend TTL.
     * @param {object} newCredential - The new credential generated by user.
     * @param {number} [newCredential.timestamp] - Current UNIX timestamp.
     + @param {number} [newCredential.ttl] - Time to live; The credential expires at timestamp + ttl.
     + @param {string} [newCredential.authToken] - Credential token calculated with HMAC.
     */
    updateCredential(newCredential) {
      this.socket.updateCredential(newCredential);
    }
  
    /**
     * Call Rest API and get the list of peerIds assciated with API key.
     * @param {function} cb - The callback function that is called after XHR.
     */
    listAllPeers(cb) {
      if (!this._checkOpenStatus()) {
        return;
      }
  
      cb = cb || function() {};
      const self = this;
      const http = new XMLHttpRequest();
  
      const url = `${this.socket.signalingServerUrl}/api/apikeys/${
        this.options.key
      }/clients/`;
  
      // If there's no ID we need to wait for one before trying to init socket.
      http.open('get', url, true);
  
      /* istanbul ignore next */
      http.onerror = function() {
        self._abort('server-error', 'Could not get peers from the server.');
        cb([]);
      };
      http.onreadystatechange = function() {
        if (http.readyState !== 4) {
          return;
        }
        if (http.status === 401) {
          cb([]);
          const err = new Error(
            "It doesn't look like you have permission to list peers IDs. " +
              'Please enable the SkyWay REST API on dashboard'
          );
          err.type = 'list-error';
          logger.error(err);
          self.emit(peer_Peer.EVENTS.error.key, err);
        } else if (http.status === 200) {
          cb(JSON.parse(http.responseText));
        } else {
          cb([]);
        }
      };
      http.send(null);
    }
  
    /**
     * Return socket open status and emit error when it's not open.
     * @return {boolean} - The socket status.
     */
    _checkOpenStatus() {
      if (!this.open) {
        this._emitNotConnectedError();
      }
      return this.open;
    }
  
    /**
     * Emit not connected error.
     */
    _emitNotConnectedError() {
      logger.warn(
        'You cannot connect to a new Peer because you are not connecting to SkyWay server now.' +
          'You can create a new Peer to reconnect, or call reconnect() ' +
          'on this peer if you believe its ID to still be available.'
      );
  
      const err = new Error(
        'Cannot connect to new Peer before connecting to SkyWay server or after disconnecting from the server.'
      );
      err.type = 'disconnected';
      logger.error(err);
      this.emit(peer_Peer.EVENTS.error.key, err);
    }
  
    /**
     * Creates new Socket and initalize its message handlers.
     * @param {string} id - User's peerId.
     * @private
     */
    _initializeServerConnection(id) {
      this.socket = new socket(this.options.key, {
        secure: this.options.secure,
        host: this.options.host,
        port: this.options.port,
  
        dispatcherSecure: this.options.dispatcherSecure,
        dispatcherHost: this.options.dispatcherHost,
        dispatcherPort: this.options.dispatcherPort,
      });
  
      this._setupMessageHandlers();
  
      this.socket.on('error', error => {
        this._abort('socket-error', error);
      });
  
      this.socket.on('disconnect', () => {
        // If we haven't explicitly disconnected, emit error and disconnect.
        this.disconnect();
  
        const err = new Error('Lost connection to server.');
        err.type = 'socket-error';
        logger.error(err);
        this.emit(peer_Peer.EVENTS.error.key, err);
      });
  
      this.socket.start(id, this.options.token, this.options.credential);
    }
  
    /**
     * Create and setup a SFURoom instance and emit SFU_JOIN message to SkyWay server.
     * @param {string} roomName - The name of the room user is joining to.
     * @param {object} [roomOptions] - Optional arguments for the RTCPeerConnection.
     * @param {object} [roomOptions.pcConfig] -  A RTCConfiguration dictionary for the RTCPeerConnection.
     * @param {string} [roomOptions.peerId] - User's peerId.
     * @param {string} [roomOptions.mode='mesh'] - One of 'sfu' or 'mesh'.
     * @param {MediaStream} [roomOptions.stream] - Media stream user wants to emit.
     * @param {number} [roomOptions.videoBandwidth] - A max video bandwidth(kbps)
     * @param {number} [roomOptions.audioBandwidth] - A max audio bandwidth(kbps)
     * @param {string} [roomOptions.videoCodec] - A video codec like 'H264'
     * @param {string} [roomOptions.audioCodec] - A video codec like 'PCMU'
     * @param {boolean} [roomOptions.videoReceiveEnabled] - A flag to set video recvonly
     * @param {boolean} [roomOptions.audioReceiveEnabled] - A flag to set audio recvonly
     * @return {SFURoom} - An instance of SFURoom.
     */
    _initializeSfuRoom(roomName, roomOptions = {}) {
      if (this.rooms[roomName]) {
        return this.rooms[roomName];
      }
      const sfuRoom = new peer_sfuRoom(roomName, this.id, roomOptions);
      this.rooms[roomName] = sfuRoom;
      this._setupSFURoomMessageHandlers(sfuRoom);
  
      const data = {
        roomName: roomName,
        roomType: 'sfu',
      };
  
      this.socket.send(config.MESSAGE_TYPES.CLIENT.ROOM_JOIN.key, data);
  
      return sfuRoom;
    }
  
    /**
     * Create and setup a MeshRoom instance and emit MESH_JOIN message to SkyWay server.
     * @param {string} roomName - The name of the room user is joining to.
     * @param {object} roomOptions - Optional arguments for the RTCPeerConnection.
     * @param {string} roomOptions.pcConfig -  A RTCConfiguration dictionary for the RTCPeerConnection.
     * @param {string} roomOptions.peerId - User's peerId.
     * @param {string} [roomOptions.mode='mesh'] - One of 'sfu' or 'mesh'.
     * @param {MediaStream} [roomOptions.stream] - Media stream user wants to emit.
     * @param {number} [roomOptions.videoBandwidth] - A max video bandwidth(kbps)
     * @param {number} [roomOptions.audioBandwidth] - A max audio bandwidth(kbps)
     * @param {string} [roomOptions.videoCodec] - A video codec like 'H264'
     * @param {string} [roomOptions.audioCodec] - A video codec like 'PCMU'
     * @param {boolean} [roomOptions.videoReceiveEnabled] - A flag to set video recvonly
     * @param {boolean} [roomOptions.audioReceiveEnabled] - A flag to set audio recvonly
     * @return {SFURoom} - An instance of MeshRoom.
     */
    _initializeFullMeshRoom(roomName, roomOptions = {}) {
      if (this.rooms[roomName]) {
        return this.rooms[roomName];
      }
      const meshRoom = new peer_meshRoom(roomName, this.id, roomOptions);
      this.rooms[roomName] = meshRoom;
      this._setupMeshRoomMessageHandlers(meshRoom);
  
      const data = {
        roomName: roomName,
        roomType: 'mesh',
      };
  
      this.socket.send(config.MESSAGE_TYPES.CLIENT.ROOM_JOIN.key, data);
  
      return meshRoom;
    }
  
    /**
     * Set up socket's message handlers.
     * @private
     */
    _setupMessageHandlers() {
      this.socket.on(config.MESSAGE_TYPES.SERVER.OPEN.key, openMessage => {
        this.id = openMessage.peerId;
        this._pcConfig = Object.assign({}, this.options.config);
  
        // make a copy of iceServers as Object.assign still retains the reference
        const iceServers = this._pcConfig.iceServers;
        this._pcConfig.iceServers = iceServers ? iceServers.slice() : [];
  
        // Set up turn credentials
        const turnCredential = openMessage.turnCredential;
        let turnUserName;
        let turnPassword;
        if (typeof turnCredential === 'object') {
          turnUserName = turnCredential.username;
          turnPassword = turnCredential.credential;
        } else if (typeof turnCredential === 'string') {
          // Handle older server versions that don't send the username
          turnUserName = `${this.options.key}$${this.id}`;
          turnPassword = turnCredential;
        }
        if (this.options.turn === true && turnUserName && turnPassword) {
          // possible turn types are turn-tcp, turns-tcp, turn-udp
          const turnCombinations = [
            { protocol: 'turn', transport: 'tcp' },
            { protocol: 'turn', transport: 'udp' },
          ];
  
          // Edge can not handle turns-tcp
          const browser = util.detectBrowser();
          if (browser.name !== 'edge') {
            turnCombinations.push({ protocol: 'turns', transport: 'tcp' });
          }
  
          for (const turnType of turnCombinations) {
            const protocol = turnType.protocol;
            const transport = turnType.transport;
  
            const iceServer = {
              urls: `${protocol}:${config.TURN_HOST}:${
                config.TURN_PORT
              }?transport=${transport}`,
              url: `${protocol}:${config.TURN_HOST}:${
                config.TURN_PORT
              }?transport=${transport}`,
  
              username: turnUserName,
              credential: turnPassword,
            };
  
            this._pcConfig.iceServers.push(iceServer);
          }
  
          logger.log('SkyWay TURN Server is available');
        } else {
          logger.log('SkyWay TURN Server is unavailable');
        }
  
        this.emit(peer_Peer.EVENTS.open.key, this.id);
      });
  
      this.socket.on(config.MESSAGE_TYPES.SERVER.ERROR.key, error => {
        const err = new Error(error.message);
        err.type = error.type;
        logger.error(err);
        this.emit(peer_Peer.EVENTS.error.key, err);
      });
  
      this.socket.on(config.MESSAGE_TYPES.SERVER.LEAVE.key, peerId => {
        logger.log(`Received leave message from ${peerId}`);
        this._cleanupPeer(peerId);
      });
  
      this.socket.on(
        config.MESSAGE_TYPES.SERVER.FORCE_CLOSE.key,
        ({ src: remoteId, connectionId }) => {
          // select a force closing connection and Close it.
          const connection = this.getConnection(remoteId, connectionId);
          if (connection) {
            // close the connection without sending FORCE_CLOSE
            connection.close(false);
          }
        }
      );
  
      this.socket.on(
        config.MESSAGE_TYPES.SERVER.AUTH_EXPIRES_IN.key,
        remainingSec => {
          logger.log(`Credential expires in ${remainingSec}`);
          this.emit(peer_Peer.EVENTS.expiresin.key, remainingSec);
        }
      );
  
      this.socket.on(config.MESSAGE_TYPES.SERVER.OFFER.key, offerMessage => {
        // handle mesh room offers
        const roomName = offerMessage.roomName;
        if (roomName) {
          const room = this.rooms[roomName];
  
          if (room) {
            room.handleOffer(offerMessage);
          }
          return;
        }
  
        // handle p2p offers
        const connectionId = offerMessage.connectionId;
        let connection = this.getConnection(offerMessage.src, connectionId);
  
        if (connection) {
          connection.updateOffer(offerMessage);
          return;
        }
  
        if (offerMessage.connectionType === 'media') {
          connection = new mediaConnection(offerMessage.src, {
            connectionId: connectionId,
            payload: offerMessage,
            metadata: offerMessage.metadata,
            originator: false,
            queuedMessages: this._queuedMessages[connectionId],
            pcConfig: this._pcConfig,
          });
          connection.startConnection();
  
          logger.log('MediaConnection created in OFFER');
  
          this._addConnection(offerMessage.src, connection);
          this.emit(peer_Peer.EVENTS.call.key, connection);
        } else if (offerMessage.connectionType === 'data') {
          connection = new dataConnection(offerMessage.src, {
            connectionId: connectionId,
            payload: offerMessage,
            metadata: offerMessage.metadata,
            label: offerMessage.label,
            dcInit: offerMessage.dcInit,
            serialization: offerMessage.serialization,
            queuedMessages: this._queuedMessages[connectionId],
            pcConfig: this._pcConfig,
          });
          connection.startConnection();
  
          logger.log('DataConnection created in OFFER');
  
          this._addConnection(offerMessage.src, connection);
          this.emit(peer_Peer.EVENTS.connection.key, connection);
        } else {
          logger.warn(
            'Received malformed connection type: ',
            offerMessage.connectionType
          );
        }
  
        delete this._queuedMessages[connectionId];
      });
  
      this.socket.on(config.MESSAGE_TYPES.SERVER.ANSWER.key, answerMessage => {
        // handle mesh room answers
        const roomName = answerMessage.roomName;
        if (roomName) {
          const room = this.rooms[roomName];
  
          if (room) {
            room.handleAnswer(answerMessage);
          }
          return;
        }
  
        // handle p2p answers
        const connection = this.getConnection(
          answerMessage.src,
          answerMessage.connectionId
        );
  
        if (connection) {
          connection.handleAnswer(answerMessage);
        } else {
          // Should we remove this storing
          // because answer should be handled immediately after its arrival?
          this._storeMessage(
            config.MESSAGE_TYPES.SERVER.ANSWER.key,
            answerMessage
          );
        }
      });
  
      this.socket.on(
        config.MESSAGE_TYPES.SERVER.CANDIDATE.key,
        candidateMessage => {
          // handle mesh room candidates
          const roomName = candidateMessage.roomName;
          if (roomName) {
            const room = this.rooms[roomName];
  
            if (room) {
              room.handleCandidate(candidateMessage);
            }
            return;
          }
  
          // handle p2p candidates
          const connection = this.getConnection(
            candidateMessage.src,
            candidateMessage.connectionId
          );
  
          if (connection) {
            connection.handleCandidate(candidateMessage);
          } else {
            // Store candidate in the queue so that the candidate can be added
            // after setRemoteDescription completed.
            this._storeMessage(
              config.MESSAGE_TYPES.SERVER.CANDIDATE.key,
              candidateMessage
            );
          }
        }
      );
  
      this.socket.on(
        config.MESSAGE_TYPES.SERVER.ROOM_USER_JOIN.key,
        roomUserJoinMessage => {
          const room = this.rooms[roomUserJoinMessage.roomName];
          if (room) {
            room.handleJoin(roomUserJoinMessage);
          }
        }
      );
  
      this.socket.on(
        config.MESSAGE_TYPES.SERVER.ROOM_USER_LEAVE.key,
        roomUserLeaveMessage => {
          const room = this.rooms[roomUserLeaveMessage.roomName];
          if (room) {
            room.handleLeave(roomUserLeaveMessage);
          }
        }
      );
  
      this.socket.on(
        config.MESSAGE_TYPES.SERVER.ROOM_DATA.key,
        roomDataMessage => {
          const room = this.rooms[roomDataMessage.roomName];
          if (room) {
            room.handleData(roomDataMessage);
          }
        }
      );
  
      this.socket.on(
        config.MESSAGE_TYPES.SERVER.ROOM_LOGS.key,
        roomLogMessage => {
          const room = this.rooms[roomLogMessage.roomName];
          if (room) {
            room.handleLog(roomLogMessage.log);
          }
        }
      );
  
      this.socket.on(
        config.MESSAGE_TYPES.SERVER.ROOM_USERS.key,
        roomUserListMessage => {
          const room = this.rooms[roomUserListMessage.roomName];
          if (room) {
            if (roomUserListMessage.type === 'media') {
              room.makeMediaConnections(roomUserListMessage.userList);
            } else {
              room.makeDataConnections(roomUserListMessage.userList);
            }
          }
        }
      );
  
      this.socket.on(config.MESSAGE_TYPES.SERVER.SFU_OFFER.key, offerMessage => {
        const room = this.rooms[offerMessage.roomName];
        if (room) {
          room.updateMsidMap(offerMessage.msids);
          room.handleOffer(offerMessage);
        }
      });
    }
  
    /**
     * Set up connection's event handlers.
     * @param {MediaConnection|DataConnection} connection - The connection to be set up.
     * @private
     */
    _setupConnectionMessageHandlers(connection) {
      connection.on(peer_connection.EVENTS.candidate.key, candidateMessage => {
        this.socket.send(
          config.MESSAGE_TYPES.CLIENT.SEND_CANDIDATE.key,
          candidateMessage
        );
      });
      connection.on(peer_connection.EVENTS.answer.key, answerMessage => {
        this.socket.send(
          config.MESSAGE_TYPES.CLIENT.SEND_ANSWER.key,
          answerMessage
        );
      });
      connection.on(peer_connection.EVENTS.offer.key, offerMessage => {
        this.socket.send(
          config.MESSAGE_TYPES.CLIENT.SEND_OFFER.key,
          offerMessage
        );
      });
      connection.on(peer_connection.EVENTS.forceClose.key, () => {
        const forceCloseMessage = {
          dst: connection.remoteId,
          connectionId: connection.id,
        };
  
        this.socket.send(
          config.MESSAGE_TYPES.CLIENT.SEND_FORCE_CLOSE.key,
          forceCloseMessage
        );
      });
    }
  
    /**
     * Set up the message event handlers for a Room
     * @param {Room} room - The room to be set up.
     * @private
     */
    _setupRoomMessageHandlers(room) {
      room.on(peer_sfuRoom.MESSAGE_EVENTS.broadcast.key, sendMessage => {
        this.socket.send(
          config.MESSAGE_TYPES.CLIENT.ROOM_SEND_DATA.key,
          sendMessage
        );
      });
      room.on(peer_sfuRoom.MESSAGE_EVENTS.getLog.key, getLogMessage => {
        this.socket.send(
          config.MESSAGE_TYPES.CLIENT.ROOM_GET_LOGS.key,
          getLogMessage
        );
      });
      room.on(peer_sfuRoom.MESSAGE_EVENTS.leave.key, leaveMessage => {
        delete this.rooms[room.name];
        this.socket.send(
          config.MESSAGE_TYPES.CLIENT.ROOM_LEAVE.key,
          leaveMessage
        );
      });
    }
  
    /**
     * Set up the message event handlers for an SFURoom
     * @param {SFURoom} room - The room to be set up.
     * @private
     */
    _setupSFURoomMessageHandlers(room) {
      this._setupRoomMessageHandlers(room);
  
      room.on(peer_sfuRoom.MESSAGE_EVENTS.offerRequest.key, sendMessage => {
        this.socket.send(
          config.MESSAGE_TYPES.CLIENT.SFU_GET_OFFER.key,
          sendMessage
        );
      });
      room.on(peer_sfuRoom.MESSAGE_EVENTS.answer.key, answerMessage => {
        this.socket.send(
          config.MESSAGE_TYPES.CLIENT.SFU_ANSWER.key,
          answerMessage
        );
      });
      room.on(peer_sfuRoom.MESSAGE_EVENTS.candidate.key, candidateMessage => {
        this.socket.send(
          config.MESSAGE_TYPES.CLIENT.SFU_CANDIDATE.key,
          candidateMessage
        );
      });
    }
  
    /**
     * Set up the message event handlers for a MeshRoom
     * @param {MeshRoom} room - The room to be set up.
     * @private
     */
    _setupMeshRoomMessageHandlers(room) {
      this._setupRoomMessageHandlers(room);
  
      room.on(peer_meshRoom.MESSAGE_EVENTS.offer.key, offerMessage => {
        this.socket.send(
          config.MESSAGE_TYPES.CLIENT.SEND_OFFER.key,
          offerMessage
        );
      });
      room.on(peer_meshRoom.MESSAGE_EVENTS.answer.key, answerMessage => {
        this.socket.send(
          config.MESSAGE_TYPES.CLIENT.SEND_ANSWER.key,
          answerMessage
        );
      });
      room.on(peer_meshRoom.MESSAGE_EVENTS.candidate.key, candidateMessage => {
        this.socket.send(
          config.MESSAGE_TYPES.CLIENT.SEND_CANDIDATE.key,
          candidateMessage
        );
      });
      room.on(peer_meshRoom.MESSAGE_EVENTS.getPeers.key, requestMessage => {
        this.socket.send(
          config.MESSAGE_TYPES.CLIENT.ROOM_GET_USERS.key,
          requestMessage
        );
      });
    }
  
    /**
     * Disconnect the socket and emit error.
     * @param {string} type - The type of error.
     * @param {string} message - Error description.
     * @private
     */
    _abort(type, message) {
      logger.error('Aborting!');
      this.disconnect();
  
      const err = new Error(message);
      err.type = type;
      logger.error(err);
      this.emit(peer_Peer.EVENTS.error.key, err);
    }
  
    /**
     * Add connection to connections property and set up message handlers.
     * @param {string} peerId - User's peerId.
     * @param {MediaConnection|DataConnection} connection - The connection to be added.
     * @private
     */
    _addConnection(peerId, connection) {
      if (!this.connections[peerId]) {
        this.connections[peerId] = [];
      }
      this.connections[peerId].push(connection);
  
      this._setupConnectionMessageHandlers(connection);
    }
  
    /**
     * Store a message until the connection is ready.
     * @param {string} type - The type of message. One of 'ANSWER' or 'CANDIDATE'.
     * @param {object} message - The object containing the message from remote peer.
     * @private
     */
    _storeMessage(type, message) {
      if (!this._queuedMessages[message.connectionId]) {
        this._queuedMessages[message.connectionId] = [];
      }
      this._queuedMessages[message.connectionId].push({
        type: type,
        payload: message,
      });
    }
  
    /**
     * Close all connections and emit close event.
     * @private
     */
    _cleanup() {
      if (this.connections) {
        for (const peer of Object.keys(this.connections)) {
          this._cleanupPeer(peer);
        }
      }
      this.emit(peer_Peer.EVENTS.close.key);
    }
  
    /**
     * Close the connection.
     * @param {string} peer - The peerId of the peer to be closed.
     * @private
     */
    _cleanupPeer(peer) {
      if (this.connections[peer]) {
        for (const connection of this.connections[peer]) {
          connection.close();
        }
      }
    }
  
    /**
     * Events the Peer class can emit.
     * @type {Enum}
     */
    static get EVENTS() {
      return PeerEvents;
    }
  
    /**
     * Successfully connected to signaling server.
     *
     * @event Peer#open
     * @type {string}
     */
  
    /**
     * Error occurred.
     *
     * @event Peer#error
     * @type {MediaStream}
     */
  
    /**
     * Received a call from peer.
     *
     * @event Peer#call
     * @type {MediaConnection}
     */
  
    /**
     * Received a connection from peer.
     *
     * @event Peer#connection
     * @type {DataConnection}
     */
  
    /**
     * Finished closing all connections to peers.
     *
     * @event Peer#close
     */
  
    /**
     * Disconnected from the signalling server.
     *
     * @event Peer#disconnected
     * @type {string}
     */
  }
  
  /* harmony default export */ var peer = __webpack_exports__["default"] = (peer_Peer);
  
  
  /***/ }),
  
  /***/ 0:
  /*!********************!*\
    !*** ws (ignored) ***!
    \********************/
  /*! no static exports found */
  /*! ModuleConcatenation bailout: Module is not an ECMAScript module */
  /***/ (function(module, exports) {
  
  /* (ignored) */
  
  /***/ })
  
  /******/ })["default"];
  });